diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index fee35c00cc4ed6fd59fdff3fd14cac396e9336a4..0406076e44059be2eadf6b622a5a8c2d506e3f0c 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -32,7 +32,7 @@ Description: 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-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index db7aab1516dee24c41c70598d87a299d798ac654..b8d0a30f164459dba4debf95a4ef5f05c434f1fb 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -192,3 +192,14 @@ Date: November 2017 Contact: "Sheng Yong" Description: Controls readahead inode block in readdir. + +What: /sys/fs/f2fs//extension_list +Date: Feburary 2018 +Contact: "Chao Yu" +Description: + Used to control configure extension list: + - Query: cat /sys/fs/f2fs//extension_list + - Add: echo '[h/c]extension' > /sys/fs/f2fs//extension_list + - Del: echo '[h/c]!extension' > /sys/fs/f2fs//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 89fd8f9a259f69b9c9423da9bb16771ed0596cad..b3d2e4a422559a571008a9f85c43f01c79e41aa1 100644 --- a/Documentation/device-mapper/verity.txt +++ b/Documentation/device-mapper/verity.txt @@ -109,6 +109,17 @@ fec_start This is the offset, in 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 bda03f0863e122efc5c1c11875f62d6183a61a70..3a966104a258e3ca1d211fb8afb6228dcf5036ae 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-base" - physical base address of cti registers - - for coresight hardware event devices: - compatible : should be "qcom,coresight-hwevent" - reg-names : should be: - "" - physical base address of hardware event mux - control registers where 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: phandle to the regulator device tree node. The required - is "vdd" for SD card and "vdd-io" for SD - I/O supply. Used for tpiu component -- qcom,-voltage-level : specifies voltage level for vdd supply. Should - be specified in pairs (min, max) with units - being uV. Here can be "vdd" for SD card - vdd supply or "vdd-io" for SD I/O vdd supply. -- qcom,-current-level : specifies current load levels for vdd supply. - Should be specified in pairs (lpm, hpm) with - units being uA. Here 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 : 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-: 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: diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-workarounds.txt b/Documentation/devicetree/bindings/arm/msm/lpm-workarounds.txt deleted file mode 100644 index 0304035492a17a48a03d97cafa18e8d577beb978..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/arm/msm/lpm-workarounds.txt +++ /dev/null @@ -1,55 +0,0 @@ -* LPM Workarounds - -The required properties are: - -- compatible: "qcom,lpm-workarounds" - -The optional properties are: -- reg: The physical address and the size of the l1_l2_gcc and l2_pwr_sts - regitsters of performance cluster. - -- reg-names: "l2_pwr_sts" - string to identify l2_pwr_sts physical address. - "l1_l2_gcc" - string to identify l1_l2_gcc physical address. - -- qcom,lpm-wa-cx-turbo-unvote: Indicates the workaround to unvote CX turbo - vote when system is coming out of rpm assisted power collaspe. - lpm-cx-supply is required if this is present. - -- lpm-cx-supply: will hold handle for CX regulator supply which is used - to unvote. - -- qcom,lpm-wa-skip-l2-spm: Due to a hardware bug on 8939 and 8909, secure - world needs to disable and enable L2 SPM to get the proper context - in secure watchdog bite cases. With this workaround there is a race - in programming L2 SPM between HLOS and secure world. This leads to - stability issues. To avoid this program L2 SPM only in secure world - based on the L2 mode flag passed. Set lpm-wa-skip-l2-spm node if this - is required. - -- qcom,lpm-wa-dynamic-clock-gating: Due to a hardware bug on 8952, L1/L2 dynamic - clock gating needs to be enabled by software for performance cluster - cores and L2. Set lpm-wa-dynamic-clock-gating node if this workaround is - required. - -- qcom,cpu-offline-mask: Dynamic clock gating should be enabled when cluster is - in L2 PC. Each bit of cpu-offline-mask lists the cpu no. to hotplug by KTM - driver. - -- qcom,non-boot-cpu-index: will hold index of non boot cluster cpu. - -- qcom,l1-l2-gcc-secure: indicates L1/L2 clock enabling register is secure. - -Example: - -qcom,lpm-workarounds { - compatible = "qcom,lpm-workarounds"; - reg = <0x0B011018 0x4>, - <0x0B011088 0x4>; - reg-names = "l2-pwr-sts", "l1-l2-gcc"; - lpm-cx-supply = <&pm8916_s2_corner>; - qcom,lpm-wa-cx-turbo-unvote; - qcom,lpm-wa-skip-l2-spm; - qcom,lpm-wa-dynamic-clock-gating; - qcom,cpu-offline-mask = "0xF"; - qcom,non-boot-cpu-index = <4>; -} diff --git a/Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt b/Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt index 2b7b3fa588d7e57b666ae13d1587bd218558e01f..606da38c095951f3559fb89ab763f0b6d748ccad 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/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 7175dc3740acf12ddddd673a13d543eecb9c6440..ed34253d9fb129916f25db38440faf13645721c3 100644 --- a/Documentation/devicetree/bindings/display/panel/toppoly,td028ttec1.txt +++ b/Documentation/devicetree/bindings/display/panel/tpo,td028ttec1.txt @@ -2,7 +2,7 @@ Toppoly TD028TTEC1 Panel ======================== Required properties: -- compatible: "toppoly,td028ttec1" +- compatible: "tpo,td028ttec1" Optional properties: - label: a symbolic name for the panel @@ -14,7 +14,7 @@ Example ------- 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 4f7ae75557582c23c5add209e64b53442389699c..bda9d6fab6b4fbe4c1d044ea4ec756d6f95df22c 100644 --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt @@ -47,10 +47,13 @@ Required properties: 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 806c45823205d9e5ec9b75995bb8d8d4ea281f51..24290c86302c2ec216f2305aac4d53b232646833 100644 --- a/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt +++ b/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt @@ -519,6 +519,8 @@ Optional properties: 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. Required properties for sub-nodes: None Optional properties: @@ -771,5 +773,6 @@ Example: 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 b8cb9036262fa6018b2b2ca7d0ed300d7d050bae..73a6dbcca27302b180d2ec8d57d5840648723dca 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt @@ -195,6 +195,10 @@ Optional properties: "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 @@ Optional properties: - 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 @@ Example: 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 8b593a97ef0d80dc4fb9785bd96c89cc2f1dd8ba..1934bc5c9f3464353dc9eb2a230a52075e424e63 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dsi.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dsi.txt @@ -110,6 +110,9 @@ Optional properties: 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. - 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 +256,8 @@ Example: 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 6b9238c4bf302d0dc9824b79b23a5596741d02b9..5c0bb2a9526b8cb2b2920f0958d74de8e89e4be3 100644 --- a/Documentation/devicetree/bindings/fb/mdss-pll.txt +++ b/Documentation/devicetree/bindings/fb/mdss-pll.txt @@ -14,7 +14,7 @@ Required properties: "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/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt index 4fb47d6f90fe545a50142314fa676d7202a8b8c9..a4e9ba7b057f40b80f4c3aadc33641a2ecffcdbb 100644 --- a/Documentation/devicetree/bindings/gpu/adreno.txt +++ b/Documentation/devicetree/bindings/gpu/adreno.txt @@ -127,6 +127,19 @@ Optional Properties: 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 - 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 - 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 d9d3470ceff72af516e65767c32e48769596349b..432c482e0b9c8c0fe4dcc1ffdc06e5caea0ca796 100644 --- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt +++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt @@ -10,6 +10,7 @@ VADC node 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 0000000000000000000000000000000000000000..27c304ef517e88034f7617e47947b95c9f49e80e --- /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/misc/stmvl53l0x.txt b/Documentation/devicetree/bindings/input/misc/stmvl53l0x.txt new file mode 100644 index 0000000000000000000000000000000000000000..bf10122de8a861853da27fe2e79b0b114e3fbc1a --- /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 258ffec27b8fdb275bd03e49d00c0135b54ab078..b54c85971927dcf0c0d48c840374ed4032089024 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/himax.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/himax.txt @@ -3,3 +3,20 @@ Himax touch controller 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/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt index 78aa1d78062c11155c8b7072108eff6698b8441c..4839df482cbe37d7e3c755249f05ae1237a8c212 100644 --- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt +++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt @@ -77,6 +77,21 @@ conditions. - 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,hibernation-support: + A boolean, indicates that hibernation should be supported and + all secure usecases should be disabled, since they cannot be + restored properly. + - 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/mcd/mcd.txt b/Documentation/devicetree/bindings/mcd/mcd.txt deleted file mode 100644 index 4077ee2250b2efc95ecce35ff6de08f2f9897b83..0000000000000000000000000000000000000000 --- 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 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/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt index 8f3ad9ab46372bd36631db5701589efb04d88e87..b41d2601c6ba026f29a0a6606026a56bd39cc192 100644 --- a/Documentation/devicetree/bindings/mfd/axp20x.txt +++ b/Documentation/devicetree/bindings/mfd/axp20x.txt @@ -28,6 +28,9 @@ Optional properties: 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. + - -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/net/qcom,emac-dwc-eqos.txt b/Documentation/devicetree/bindings/net/qcom,emac-dwc-eqos.txt index eff3d82e212403e251e7e6656dae6da1448a14f9..6f0cbfed5d0804db6d3adb9e8e19229823d5a6df 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 @@ emac_hw node: - 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 @@ Optional: 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: 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: 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 @@ soc { 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 caf297bee1fb4c34ef5151912656f9636fe1565e..c28d4eb83b7687fbb630af467c7b49fb5e7e25f7 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-palmas.txt +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-palmas.txt @@ -35,6 +35,15 @@ Optional properties: - 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/power/supply/qcom/qpnp-linear-charger.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-linear-charger.txt new file mode 100644 index 0000000000000000000000000000000000000000..6dccdd3d4748519960d3310d536b3f794cf205f8 --- /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 efa67f52654773fd88436bb7d5ff2599a2ff180f..afeb65dc86d0b585f9d0d43dce87cc590b5b2ac6 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt +++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt @@ -191,6 +191,77 @@ First Level Node - QGAUGE device temperature specific configuration as applied. If not specified, the default value is 0 degree centigrade. +- qcom,cl-disable + Usage: optional + Value type: + Definition: A boolean property to disable the battery capacity + learning when charging. + +- qcom,cl-feedback-on + Usage: optional + Value type: + 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: + 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: + 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: + 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: + 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: + 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: + 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: + 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: + 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. + ========================================================== Second Level Nodes - Peripherals managed by QGAUGE driver ========================================================== diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt index de273cdda4dcf699187b97f7b5fd07c8058a34d0..8ee2749aea35e01f5c8e76c908abc6c2cb9e211b 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 @@ Charger specific properties: This is only applicable to certain PMICs like PMI632 which has SCHGM_FLASH peripheral. +- qcom,chg-vadc + Usage: optional + Value type: + 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 0000000000000000000000000000000000000000..690a0473dcd7ed271ca1e65ee8922c42d0910496 --- /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 + . +- 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/regulator/cpr3-regulator.txt b/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt index 846bd22e5fcb1c453aaf05879402c96837bef8d9..b02d759b1cf73b97cf6450cd7d6c95d6a35cfa99 100644 --- a/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt @@ -241,6 +241,15 @@ Platform independent properties: 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: + 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 7b8ae6f2df3f85f17f45fe63fbba551d7c077605..1e4d2c12d5599c199b9ceb3ba27adee1cf638bee 100644 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -434,6 +434,9 @@ Required properties: 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 @@ -722,6 +725,7 @@ Example: wcd_dsp_glink { compatible = "qcom,wcd-dsp-glink"; + qcom,msm-codec-glink-edge = "bg"; }; msm_ext_disp_audio_codec_rx { @@ -1180,6 +1184,18 @@ Required properties: 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 97b71a738fc00f2c18da14d28214bec318f50b8e..d3f765ca462489425f182f92e4943e186b7689bb 100644 --- a/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt +++ b/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt @@ -13,6 +13,7 @@ VADC_TM node 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/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt index 881f9ca12a34bf61f14b0b983a2a252dffd3f6fb..0cc8b00bc47fb232b9007aa5fd847cdc7bd179c5 100644 --- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt +++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt @@ -82,6 +82,7 @@ Optional properties : - 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/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index e7fb61ee65835fdc151d395e0d22477df9f94ddc..e85f9e1848d106799f3b2cd3ec1e216d1201d636 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -171,6 +171,23 @@ offgrpjquota Turn off group journelled quota. 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/Makefile b/Makefile index 7c1b91b50d3f38dc12aa45b28900c9f18551bb9d..bf5ea1127631ec6db950ec7113f6329b4d41592d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 4 PATCHLEVEL = 9 -SUBLEVEL = 84 +SUBLEVEL = 96 EXTRAVERSION = NAME = Roaring Lionus @@ -349,6 +349,7 @@ include scripts/Kbuild.include 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_GCOV := -fprofile-arcs -ftest-coverage -fno-tree-loop-im $(call cc-disabl 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,-ffunction-sections,) 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 @@ KBUILD_CFLAGS += $(call cc-disable-warning, pointer-sign) # 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 @@ prepare-objtool: $(objtool_target) # 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)),) @@ -1100,6 +1187,11 @@ ifdef stackp-check @echo Cannot use CONFIG_CC_STACKPROTECTOR_$(stackp-name): \ $(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 @: @@ -1575,7 +1667,8 @@ clean: $(clean-dirs) -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 a364ecec958e34d9cc56628e8b780bc2e6a46684..4fa799bbbe140c1d45239b8e4b703c887f8324d2 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -503,6 +503,74 @@ config LD_DEAD_CODE_DATA_ELIMINATION 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/kernel/console.c b/arch/alpha/kernel/console.c index 6a61deed4a853ce8fdbb6b753070f2ca29aef08f..ab228ed45945acc2378e48db39942a996d073ea2 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/arm/Kconfig b/arch/arm/Kconfig index 9dcf7a2db3ce646b50de48835630b01646dc9f09..f252182186c3ae9207c3f2992bb6336494ddab0c 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -574,6 +574,7 @@ config ARCH_QCOM 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 @@ -1938,6 +1939,26 @@ config BUILD_ARM_APPENDED_DTB_IMAGE 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 f42fcd4561ddfa63bb01998a1f01b6db42df64f0..f66f377db2e4311dd866842d519e9005d36cdb64 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 @@ libs-y := arch/arm/lib/ $(libs-y) 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 @@ endif 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 ad7a0253ea961a405fb13bb284bd1c5942e28675..271e1241be95d5fd3ce36b51f28fca830616e31f 100644 --- a/arch/arm/boot/.gitignore +++ b/arch/arm/boot/.gitignore @@ -4,4 +4,6 @@ xipImage 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 4175dfe4322531831877fdfaf57908a38776de34..52e8c50aeee004b6618a227ac50b3cb268844839 100644 --- a/arch/arm/boot/Makefile +++ b/arch/arm/boot/Makefile @@ -34,10 +34,10 @@ targets := Image zImage xipImage bootpImage uImage DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES)) ifneq ($(DTB_NAMES),) DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES)) +DTB_OBJS := $(addprefix $(obj)/dts/,$(DTB_LIST)) else -DTB_LIST := $(dtb-y) +DTB_OBJS := $(shell find $(obj)/dts/ -name \*.dtb) endif -DTB_OBJS := $(addprefix $(obj)/dts/,$(DTB_LIST)) ifeq ($(CONFIG_XIP_KERNEL),y) @@ -59,6 +59,14 @@ $(obj)/xipImage: FORCE $(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 6df7829a2c15dcb6ee79e71eebeda2b9f6720c3a..78bee26361f15847ccc0a950a61d201de9e3feb2 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 db858fff4e180627c1549e16c0eed5218dd41a88..1cc62727e43a5d058ecc5a316dbbaa6153057843 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 1b7a5ff0e533f6b19f66bc806f621b1fe9421ce9..d7774d35e466e148b0f49283928aaf3c1c1771dc 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 a7da0dd0c98fa0af8d05ad545660aba426962c30..0898213f3bb22e41df4612a51b71ac77034fede6 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 12c981e5113489ea785d8f4d8b0a5b9f99d61beb..9a0599f711ff3382a7b908bb72e9f0392db5e1bf 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 3f0a56ebcf1f64692cbdd311752ce8d1fd36a5f1..dc7ae776db5fc0ca54e91ef6369fbaee71162d22 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 132f2be10889fbfc7a94d5048b9ee096a0bf848c..56311fd34f8118d8ea21a5d7e5fd3ac03d8c5b2e 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 41ecd6d465a77b8f3ae6ae4ee3e5ae03c6247de6..75a60633efffef6c4caecedffc1fcfc246f579a6 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 f7357d99b47cea3a88a4c8f91662ec33d1239302..64de33d067c9b94ff210dad5da5853f56a21c371 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 96d7eede412e1343d5e4e0a982044cec3ac347d3..036c9bd9bf7551ce39edcc7061480f34bc57128d 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/imx6qdl-wandboard.dtsi b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi index 2b9c2be436f944d9e10265351a62bbaac2180ffc..47c955458a77f7f03d47a95b93cc2fb9efd9cb3d 100644 --- a/arch/arm/boot/dts/imx6qdl-wandboard.dtsi +++ b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi @@ -88,6 +88,7 @@ clocks = <&clks IMX6QDL_CLK_CKO>; VDDA-supply = <®_2p5v>; VDDIO-supply = <®_3p3v>; + lrclk-strength = <3>; }; }; diff --git a/arch/arm/boot/dts/logicpd-som-lv.dtsi b/arch/arm/boot/dts/logicpd-som-lv.dtsi index 4f2c5ec7571450a59aca89293f3a6e71e30db1da..e262fa9ef3346207940f62af3aa626738a637491 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 efe53998c961244fc0cd1ff32a5e53c885b6322f..08f0a35dc0d1eab74908a011c3bc1408420b0501 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 940875316d0f3926b39ea0717e560288f8594084..67b4de0e343921fda48d7e94b228732fe2d0131d 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 a8b148ad1dd2c37076a6a89d7c02498064fabc15..44715c8ef756b9f44bdc7a7c69b0cdc12c26d93d 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 368e2193428547382edb01b1b3e645fa2921f302..825f6eae3d1c1ce08cab50e991a075beb5537b8d 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 = ; clock-frequency = <0>; diff --git a/arch/arm/boot/dts/moxart-uc7112lx.dts b/arch/arm/boot/dts/moxart-uc7112lx.dts index 10d088df0c35177a573bd695337ac1010a2dc138..4a962a26482df2796b1627e304ea743f5ae32680 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 1fd27ed65a01fc868d918a2a593534153bd45415..64f2f44235d02272dc1739f4fb03e40c8a706c98 100644 --- a/arch/arm/boot/dts/moxart.dtsi +++ b/arch/arm/boot/dts/moxart.dtsi @@ -6,6 +6,7 @@ */ /include/ "skeleton.dtsi" +#include / { 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 b7a24af8f47b388e3bb9b35968495ae941399e16..4b7d97275c621af5a219856d38f489a3e704b2e7 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 = ; 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 4642d0d67ac3d5b361adc7141e9de93261d00c7f..e6af69d030b90793ffada2a5c0dc83486c0932d9 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -21,14 +21,18 @@ dtb-$(CONFIG_ARCH_MDM9607) += mdm9607-mtp.dtb \ mdm9607-ttp.dtb targets += dtbs -targets += $(addprefix ../, $(dtb-y)) -$(obj)/../%.dtb: $(src)/%.dts FORCE +include $(srctree)/arch/arm64/boot/dts/qcom/Makefile +$(obj)/%.dtb: $(src)/../../../../arm64/boot/dts/qcom/%.dts FORCE $(call if_changed_dep,dtc) -include $(srctree)/arch/arm64/boot/dts/qcom/Makefile -$(obj)/../%.dtb: $(src)/../../../../arm64/boot/dts/qcom/%.dts FORCE +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)) +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 0fd3b3473c612dfccdd62335ddf5ce43025f53c6..6a3210ca0ca09138840093eae53ce81f2cae957a 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,10 @@ compatible = "qcom,msm-pcm-hostless"; }; + audio_apr: qcom,msm-audio-apr { + compatible = "qcom,msm-audio-apr"; + }; + 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 6a3b39110262552bde7393ceafcab2142723ffbe..519367c0ace53b1b049a6cedfa2e0d2a83bac69f 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-pcie-ep-cdp-256.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp-256.dtsi index 518d8a1e22712486d541b60bd26b6927991c469e..dafd0b8dc87770ba2a686fab03234c3802f33b6b 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 @@ -25,6 +25,10 @@ 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 518d8a1e22712486d541b60bd26b6927991c469e..1428f3778f1ef66ac4dbd838d11ba9c290708c46 100644 --- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.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-256.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp-256.dtsi index eb544e6fe6f85e78cdc97a1cbc1514a217ee24da..6c5f3c3795ae31714cb45206442fcffb53e19cef 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 @@ -25,6 +25,10 @@ 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 eb544e6fe6f85e78cdc97a1cbc1514a217ee24da..d7c0d139c342e902f9fa4e9ec59c3101df65a241 100644 --- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.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-pm.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pm.dtsi index 77fc5330602274e142f5c2c0258224432b3b1eb6..505f242a28ae8431f7a225c11d57d81f611fd962 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 38ba7fc7c3e1c9a3880c1dfb4ee117f5524b39d2..50bbebfa9e20c585b46e3580d2c38ee2c54d549d 100644 --- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi +++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi @@ -19,6 +19,8 @@ #include #include +#define MHZ_TO_MBPS(mhz, w) ((mhz * 1000000 * w) / (1024 * 1024)) + / { model = "Qualcomm Technologies, Inc. SDX POORWILLS"; compatible = "qcom,sdxpoorwills"; @@ -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>; }; @@ -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 fb9ef9ca120e9a67fe9855c88e798c77d99b101d..959e3edf367b6f24364fd5deeff98ac9b65a6bc8 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 = ; clock-output-names = "ether"; }; diff --git a/arch/arm/boot/dts/r8a7740-armadillo800eva.dts b/arch/arm/boot/dts/r8a7740-armadillo800eva.dts index 7885075428bb03a2c1c230578adcdb3e7b21fd88..1788e186a51241c043487d9699d02eb4c1987c40 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 b6c6410ca384f8ea1c87c788bd3d32d53e908620..262a51205aee908f44c74dccd50ede079726df4e 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 f8a7d090fd017deb0d673dedf19f1104c47f5bb2..12841e9bab982ff584b0149d9683a1876d8090b8 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 162b55c665a3519ea9f6895b93c7ddd6b6cfc909..59405ebdce01b1ad5ae27402651f8dba9d202d62 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 713141d38b3ea960fcee7dfdbd9317bbc0527536..0b50c67668670301dde99ad54117550939c9b40c 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 8d02aacf2892627155ea82c3f6414c614b33ebb5..e9625cb3bbaa271681613bf2c37b56d306e83fb2 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 cf880ac06f4b795f6b6458f597ffed4bf35983c4..8874451fb91459601c3f45d4890943b086512b34 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 7e860d3737ff00ca599ccc2dadb030e30e9b5b72..d8f4ca85ed3f693dff3ea76870074b12259dc290 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 = , ; 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 9e6bf0e311bb69e2857121be784c3e85060072b0..2679dc80f8316409d82be820f336377c1d2d4f8f 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 65e725fb567986b3db1687056c4c74df2f9bd003..de0e189711f6e12c12788701d68ef675f0ff3cda 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 79de828e49addc2c6a6efec37bbc154a354f4cd8..e32b0550a3384502b997474440028e7f8b18e5aa 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_PREEMPT_VOLUNTARY=y 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_MAC80211=y 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_USB_NET_SMSC95XX=y 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 0000000000000000000000000000000000000000..9f5001f79216e11052898f94db0be46e0cc39916 --- /dev/null +++ b/arch/arm/configs/msm8909-perf_defconfig @@ -0,0 +1,503 @@ +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_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_CC_STACKPROTECTOR_STRONG=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_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_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_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_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_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_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_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_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 +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_CI13XXX_MSM=y +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_SERIAL=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_NEW_LEDS=y +CONFIG_LEDS_CLASS=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_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_PM=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_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_EVENT_TIMER=y +CONFIG_QTI_RPM_STATS_LOG=y +CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=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_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_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_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_MD4=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=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 0000000000000000000000000000000000000000..c8087adad853f4c7079458ce8767c3917e9f09a0 --- /dev/null +++ b/arch/arm/configs/msm8909_defconfig @@ -0,0 +1,528 @@ +# 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_KALLSYMS_ALL=y +CONFIG_BPF_SYSCALL=y +CONFIG_EMBEDDED=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=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_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_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_HS=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=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_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_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_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_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 +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_CI13XXX_MSM=y +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_SERIAL=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_NEW_LEDS=y +CONFIG_LEDS_CLASS=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_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_PM=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_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_EVENT_TIMER=y +CONFIG_QTI_RPM_STATS_LOG=y +CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=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_NLS_CODEPAGE_437=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_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_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_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_MD4=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=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 dfe5fd8d5bc5039d0f370016b68adc4bea9671d6..5a56d638d3c330bab835aa26b5c093e6892ac2ad 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_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 @@ -34,7 +34,7 @@ CONFIG_BPF_SYSCALL=y 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_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 @@ -108,14 +108,12 @@ 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_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_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 @@ -139,6 +136,7 @@ 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 @@ -146,6 +144,8 @@ CONFIG_NETFILTER_XT_MATCH_STRING=y 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_ARPTABLES=y 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_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 @@ -257,7 +257,6 @@ CONFIG_WCNSS_MEM_PRE_ALLOC=y 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_TOUCHSCREEN_FTS=y 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_CAMERA_SUPPORT=y 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_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 @@ -361,6 +360,7 @@ CONFIG_USB_CONFIGFS_SERIAL=y 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_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 @@ -381,7 +381,6 @@ 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 @@ -400,10 +399,13 @@ 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_MSM_RMNET_BAM=y CONFIG_MSM_MDSS_PLL=y CONFIG_REMOTE_SPINLOCK_MSM=y CONFIG_MAILBOX=y @@ -440,7 +442,6 @@ 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_MSM_GLINK_BGCOM_XPRT=y @@ -460,16 +461,15 @@ 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 @@ -491,21 +491,12 @@ 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_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 @@ -514,13 +505,14 @@ CONFIG_LSM_MMAP_MIN_ADDR=4096 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 df7f1c909c3a1d67276c20a00fa2f0e927d1bc7a..af47269436b822ea44870c704cee45fc510c6b78 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 @@ -34,7 +35,7 @@ CONFIG_BPF_SYSCALL=y 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 @@ -141,6 +142,7 @@ 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 @@ -269,6 +271,8 @@ CONFIG_TOUCHSCREEN_FTS=y 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 +326,8 @@ CONFIG_MEDIA_CAMERA_SUPPORT=y 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 @@ -366,6 +372,7 @@ CONFIG_USB_CONFIGFS_SERIAL=y 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 @@ -374,6 +381,7 @@ 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 @@ -405,10 +413,13 @@ 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_MSM_RMNET_BAM=y CONFIG_MSM_MDSS_PLL=y CONFIG_REMOTE_SPINLOCK_MSM=y CONFIG_MAILBOX=y @@ -473,6 +484,7 @@ 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 diff --git a/arch/arm/configs/msm8937-perf_defconfig b/arch/arm/configs/msm8937-perf_defconfig index 971f4e1f05512abc48f42bacefeb3624d3d7355b..b113ebd5a7115b74b3f54ae1e3c55e3592219f45 100644 --- a/arch/arm/configs/msm8937-perf_defconfig +++ b/arch/arm/configs/msm8937-perf_defconfig @@ -17,7 +17,6 @@ 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 @@ -68,6 +67,7 @@ CONFIG_HIGHMEM=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 @@ -207,8 +207,6 @@ 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 @@ -220,13 +218,12 @@ 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_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 @@ -245,7 +242,6 @@ 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 @@ -257,10 +253,8 @@ 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_REQ_CRYPT=y CONFIG_DM_UEVENT=y @@ -269,6 +263,14 @@ CONFIG_DM_VERITY_FEC=y 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 @@ -282,6 +284,21 @@ CONFIG_PPPOPNS=y 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 @@ -322,7 +339,6 @@ CONFIG_PINCTRL_MSM8917=y 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 @@ -392,7 +408,6 @@ CONFIG_OV12830=y 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 @@ -405,7 +420,7 @@ CONFIG_FB_MSM_MDSS_WRITEBACK=y 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 @@ -421,12 +436,9 @@ CONFIG_USB_HIDDEV=y 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 @@ -440,14 +452,10 @@ 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 @@ -456,7 +464,6 @@ CONFIG_USB_CI13XXX_MSM=y 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 @@ -474,11 +481,12 @@ CONFIG_USB_CONFIGFS_F_CCID=y 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 @@ -551,6 +559,7 @@ 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 @@ -569,9 +578,7 @@ 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 @@ -582,8 +589,6 @@ 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_NETWORK_FILESYSTEMS is not set CONFIG_NLS_CODEPAGE_437=y @@ -599,8 +604,12 @@ CONFIG_SCHED_STACK_END_CHECK=y CONFIG_IPC_LOGGING=y CONFIG_CPU_FREQ_SWITCH_PROFILER=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 diff --git a/arch/arm/configs/msm8937_defconfig b/arch/arm/configs/msm8937_defconfig index 2629d2c58f6a05bcfd2d4649b9f7f69e826b30d6..1cccfd3da463f391ee62ac74b53e453e2303e16a 100644 --- a/arch/arm/configs/msm8937_defconfig +++ b/arch/arm/configs/msm8937_defconfig @@ -18,7 +18,6 @@ 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 @@ -71,6 +70,7 @@ CONFIG_HIGHMEM=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 @@ -211,8 +211,6 @@ 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 @@ -224,14 +222,13 @@ 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_BT_BREDR is not set +# CONFIG_BT_LE is not set CONFIG_MSM_BT_POWER=y CONFIG_CFG80211=y CONFIG_CFG80211_INTERNAL_REGDB=y @@ -250,7 +247,6 @@ 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 @@ -262,10 +258,8 @@ 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_REQ_CRYPT=y CONFIG_DM_UEVENT=y @@ -274,6 +268,14 @@ CONFIG_DM_VERITY_FEC=y 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 @@ -287,6 +289,21 @@ CONFIG_PPPOPNS=y 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 @@ -329,7 +346,6 @@ CONFIG_PINCTRL_MSM8917=y 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 @@ -399,7 +415,6 @@ CONFIG_OV12830=y 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 @@ -413,7 +428,7 @@ CONFIG_FB_MSM_MDSS_WRITEBACK=y 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 @@ -429,12 +444,9 @@ CONFIG_USB_HIDDEV=y 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 @@ -448,14 +460,10 @@ 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 @@ -464,7 +472,6 @@ CONFIG_USB_CI13XXX_MSM=y 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 @@ -482,12 +489,13 @@ CONFIG_USB_CONFIGFS_F_CCID=y 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 @@ -567,6 +575,7 @@ 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 @@ -585,9 +594,7 @@ 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 @@ -598,8 +605,6 @@ 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_NETWORK_FILESYSTEMS is not set CONFIG_NLS_CODEPAGE_437=y @@ -658,8 +663,12 @@ 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_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 diff --git a/arch/arm/configs/msm8953-batcam-perf_defconfig b/arch/arm/configs/msm8953-batcam-perf_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..1610d29ec0097fef6ba12201180deeecd1b303b3 --- /dev/null +++ b/arch/arm/configs/msm8953-batcam-perf_defconfig @@ -0,0 +1,271 @@ +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_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_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_QSEECOM=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_NETDEVICES=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_FB_MSM=y +CONFIG_FB_MSM_MDSS=y +CONFIG_FB_MSM_MDSS_WRITEBACK=y +CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS=y +CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_SOC=y +CONFIG_UHID=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_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_MSM_MDSS_PLL=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_MSM_PIL_MSS_QDSP6V5=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_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 0000000000000000000000000000000000000000..1ba9d96ca93b5012ab9b706f8a754afcabda2ff1 --- /dev/null +++ b/arch/arm/configs/msm8953-batcam_defconfig @@ -0,0 +1,272 @@ +# 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_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_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_QSEECOM=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_NETDEVICES=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_FB_MSM=y +CONFIG_FB_MSM_MDSS=y +CONFIG_FB_MSM_MDSS_WRITEBACK=y +CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS=y +CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_SOC=y +CONFIG_UHID=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_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_MSM_MDSS_PLL=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_MSM_PIL_MSS_QDSP6V5=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_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 bfa6cc7d078a3037a79a49cde8a44143174e2b18..aa557b0aeb735aafcce8f0c8fccd6e9439b5bd6e 100644 --- a/arch/arm/configs/msm8953-perf_defconfig +++ b/arch/arm/configs/msm8953-perf_defconfig @@ -17,7 +17,6 @@ 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 @@ -222,6 +221,8 @@ 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 @@ -240,7 +241,6 @@ 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 @@ -252,10 +252,8 @@ 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_REQ_CRYPT=y CONFIG_DM_UEVENT=y @@ -309,6 +307,10 @@ 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 @@ -339,7 +341,6 @@ CONFIG_PINCTRL_MSM8953=y 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 @@ -355,11 +356,19 @@ CONFIG_QPNP_QG=y 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_SPMI_PMIC=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y @@ -409,7 +418,6 @@ CONFIG_OV12830=y 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 @@ -422,7 +430,7 @@ CONFIG_FB_MSM_MDSS_WRITEBACK=y 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 @@ -489,11 +497,12 @@ CONFIG_USB_CONFIGFS_F_CCID=y 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 @@ -592,8 +601,6 @@ 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_NETWORK_FILESYSTEMS is not set CONFIG_NLS_CODEPAGE_437=y diff --git a/arch/arm/configs/msm8953_defconfig b/arch/arm/configs/msm8953_defconfig index b7dc23ddeeecd24f0c8649c284d68cd3ed1d01de..f38341ddfb334cd69ab419aa907459d801dfec3e 100644 --- a/arch/arm/configs/msm8953_defconfig +++ b/arch/arm/configs/msm8953_defconfig @@ -18,7 +18,6 @@ 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 @@ -227,6 +226,8 @@ 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 @@ -245,7 +246,6 @@ 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 @@ -257,10 +257,8 @@ 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_REQ_CRYPT=y CONFIG_DM_UEVENT=y @@ -314,6 +312,10 @@ 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 @@ -346,7 +348,6 @@ CONFIG_PINCTRL_MSM8953=y 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 @@ -362,11 +363,19 @@ CONFIG_QPNP_QG=y 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_SPMI_PMIC=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y @@ -416,7 +425,6 @@ CONFIG_OV12830=y 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 @@ -430,7 +438,7 @@ CONFIG_FB_MSM_MDSS_WRITEBACK=y 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 @@ -497,12 +505,13 @@ CONFIG_USB_CONFIGFS_F_CCID=y 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 @@ -608,8 +617,6 @@ 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_NETWORK_FILESYSTEMS is not set CONFIG_NLS_CODEPAGE_437=y diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig index e3c1c1aa817b0b2a0f987d337ed9f120e515c5ed..14c2a7c2f679117ba47deae63adba62aa1885cc1 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 @@ -60,6 +61,7 @@ CONFIG_IPV6_PIMSM_V2=y 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 +79,7 @@ CONFIG_NF_CT_NETLINK=y 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 +87,7 @@ CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y 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 +119,7 @@ CONFIG_IP_NF_MANGLE=y 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 +222,7 @@ CONFIG_INPUT_UINPUT=y 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 +259,7 @@ CONFIG_QTI_QMI_COOLING_DEVICE=y 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 +381,10 @@ CONFIG_MSM_PM=y 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,6 +403,7 @@ CONFIG_NLS_ISO8859_1=y 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 @@ -407,6 +419,10 @@ CONFIG_CORESIGHT_TPDM=y CONFIG_CORESIGHT_CTI=y CONFIG_CORESIGHT_EVENT=y CONFIG_CORESIGHT_HWEVENT=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=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 77ae3e3bc1c5d3c2ddf1e0f6da199d25eece2520..b259dc7ae54bedc9f71622e17b3c8a8915f9a7f7 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 @@ -62,6 +63,7 @@ CONFIG_IPV6_PIMSM_V2=y 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 +81,7 @@ CONFIG_NF_CT_NETLINK=y 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 +89,7 @@ CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y 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 +121,7 @@ CONFIG_IP_NF_MANGLE=y 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 @@ -380,6 +385,9 @@ CONFIG_MSM_PM=y 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,6 +413,7 @@ CONFIG_DEBUG_KMEMLEAK=y 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_DEBUG_SPINLOCK=y @@ -435,6 +444,10 @@ CONFIG_CORESIGHT_EVENT=y CONFIG_CORESIGHT_TGU=y CONFIG_CORESIGHT_HWEVENT=y CONFIG_CORESIGHT_DUMMY=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_SELINUX=y +# CONFIG_SECURITY_SELINUX_AVC_STATS is not set CONFIG_CRYPTO_CMAC=y CONFIG_CRYPTO_SHA256=y CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y diff --git a/arch/arm/include/asm/dma-contiguous.h b/arch/arm/include/asm/dma-contiguous.h index 4f8e9e5514b14486c8b9a6fdcc652af966bceeda..2dbb8c668e611b0416f8697e66d3fbd813cb898f 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 void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size); -#endif #endif #endif diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index d5423ab15ed5be1c705817e13d4a7d7fe35b465b..9fe1043e72d247dcdfa31dda79cacd9bf9ac3288 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h @@ -318,4 +318,10 @@ static inline int kvm_arm_vcpu_arch_has_attr(struct kvm_vcpu *vcpu, 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_psci.h b/arch/arm/include/asm/kvm_psci.h deleted file mode 100644 index 6bda945d31fa8effe1b9d51c589733c5f9f66183..0000000000000000000000000000000000000000 --- a/arch/arm/include/asm/kvm_psci.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2012 - ARM Ltd - * Author: Marc Zyngier - * - * 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 . - */ - -#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 71e473d05fcce8a2b869b84272dfcd498c3847dc..620dc75362e5972adb5933ce9f8875fe56072c0a 100644 --- a/arch/arm/include/asm/xen/events.h +++ b/arch/arm/include/asm/xen/events.h @@ -16,7 +16,7 @@ static inline int xen_irqs_disabled(struct pt_regs *regs) 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/kernel/ftrace.c b/arch/arm/kernel/ftrace.c index 3f1759411d51bec32175992a2332ab07ca598a8a..414e60ed02573d06af2a94ac576cdd75df027d11 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 @@ void arch_ftrace_update_code(int command) 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/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index 4f9e2b54bb8e20fa8feaf49c9a159353eb88b279..5b6cb335cf544180e503336ac41138377528180c 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -1066,8 +1066,6 @@ static int armv7pmu_set_event_filter(struct hw_perf_event *event, { 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,11 +1182,68 @@ static void armv7_read_num_pmnc_events(void *info) *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) { - return smp_call_function_any(&arm_pmu->supported_cpus, + 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) +{ + int ret; + struct armv7_pmu_idle_nb *pmu_idle_nb; + + pmu_idle_nb = devm_kzalloc(&arm_pmu->plat_device->dev, + sizeof(*pmu_idle_nb), GFP_KERNEL); + if (!pmu_idle_nb) + return -ENOMEM; + + ret = smp_call_function_any(&arm_pmu->supported_cpus, armv7_read_num_pmnc_events, &arm_pmu->num_events, 1); + if (ret) + return ret; + + pmu_idle_nb->cpu_pmu = arm_pmu; + 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 int armv7_a8_pmu_init(struct arm_pmu *cpu_pmu) @@ -1200,7 +1255,7 @@ static int armv7_a8_pmu_init(struct arm_pmu *cpu_pmu) &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 +1267,7 @@ static int armv7_a9_pmu_init(struct arm_pmu *cpu_pmu) &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 +1279,7 @@ static int armv7_a5_pmu_init(struct arm_pmu *cpu_pmu) &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 +1292,7 @@ static int armv7_a15_pmu_init(struct arm_pmu *cpu_pmu) &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 +1305,7 @@ static int armv7_a7_pmu_init(struct arm_pmu *cpu_pmu) &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 +1318,7 @@ static int armv7_a12_pmu_init(struct arm_pmu *cpu_pmu) &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 +1715,7 @@ static int krait_pmu_init(struct arm_pmu *cpu_pmu) 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 +2038,7 @@ static int scorpion_pmu_init(struct arm_pmu *cpu_pmu) 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 +2051,7 @@ static int scorpion_mp_pmu_init(struct arm_pmu *cpu_pmu) 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[] = { diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index c6324b534b9df21b11c01543fad8b048cf989f23..38ad8b956c6becb5ef8e0e6d6d0170bfe8397c8a 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -129,13 +129,13 @@ static void show_data(unsigned long addr, int nbytes, const char *name) 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/smp.c b/arch/arm/kernel/smp.c index ab509d6cc6ca0fbed7bd07720c69043b9503c3fb..a03a99ae7ac601fd3a88e55eec76ed8a492c6f63 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -672,6 +672,8 @@ void handle_IPI(int ipinr, struct pt_regs *regs) 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/kvm/arm.c b/arch/arm/kvm/arm.c index c38bfbeec306e5019b0498733c726bffaf1763a7..ef6595c7d69709236ca568203c2dd1610e6ae312 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -29,6 +29,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include "trace.h" @@ -44,7 +45,6 @@ #include #include #include -#include #include #ifdef REQUIRES_VIRT @@ -1088,7 +1088,7 @@ static void cpu_init_hyp_mode(void *dummy) 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 @@ static int init_hyp_mode(void) 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/handle_exit.c b/arch/arm/kvm/handle_exit.c index 4e57ebca6e6919eb496178f8e39bfbaa954b1526..de1aedce2a8b29ce3e0011d2ce48c0c4770cd020 100644 --- a/arch/arm/kvm/handle_exit.c +++ b/arch/arm/kvm/handle_exit.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include "trace.h" @@ -36,7 +36,7 @@ static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run) 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 92eab1d51785b9078cce5084270251a1ab517809..61049216e4d5b4db683cafffa2e3ed61124bb715 100644 --- a/arch/arm/kvm/hyp/Makefile +++ b/arch/arm/kvm/hyp/Makefile @@ -6,6 +6,8 @@ ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING 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) += tlb.o 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 111bda8cdebdc7e59789103838087920aedf0efe..be4b8b0a40ade5c8e412bbc75f2b311c389aa979 100644 --- a/arch/arm/kvm/hyp/banked-sr.c +++ b/arch/arm/kvm/hyp/banked-sr.c @@ -20,6 +20,10 @@ #include +/* + * 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 624a510d31df4095701387da09b4b9a27bd09a17..ebd2dd46adf7f991d7131eb55873a5e916010a8d 100644 --- a/arch/arm/kvm/hyp/switch.c +++ b/arch/arm/kvm/hyp/switch.c @@ -237,8 +237,10 @@ void __hyp_text __noreturn __hyp_panic(int cause) 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 2a35c1963f6d8d22571a973c11a94aba1e8ccb92..7f868d9bb5ed744420daea0a6407865111a7ced2 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c @@ -1284,7 +1284,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, 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 a08d7a93aebbece7777f042ec3881ad088104a4d..3d962257c166fcb4501ed0bc3041433012059370 100644 --- a/arch/arm/kvm/psci.c +++ b/arch/arm/kvm/psci.c @@ -15,16 +15,16 @@ * along with this program. If not, see . */ +#include #include #include #include #include #include -#include #include -#include +#include /* * This is an implementation of the Power State Coordination Interface @@ -33,6 +33,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 +107,7 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu) 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 +120,14 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu) 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 +146,7 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu) * 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 +166,8 @@ static unsigned long kvm_psci_vcpu_affinity_info(struct kvm_vcpu *vcpu) 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 +230,10 @@ static void kvm_psci_system_reset(struct kvm_vcpu *vcpu) 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 +243,7 @@ static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu) * 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 +300,56 @@ static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu) 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 +367,7 @@ static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu) break; } - vcpu_set_reg(vcpu, 0, val); + smccc_set_retval(vcpu, val, 0, 0, 0); return 1; } @@ -319,9 +385,11 @@ static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu) * 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 +398,30 @@ int kvm_psci_call(struct kvm_vcpu *vcpu) 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; +} diff --git a/arch/arm/lib/csumpartialcopyuser.S b/arch/arm/lib/csumpartialcopyuser.S index 1712f132b80d2402d94d72ea974a0c3326fa2f52..b83fdc06286a64ece150fb7e419bc587e47c3e34 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/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig index a0e66d8200c5cf8f2ea592abf870a32800213fa2..403db76e34973140f1d002c36b9d563802f5d7b3 100644 --- a/arch/arm/mach-bcm/Kconfig +++ b/arch/arm/mach-bcm/Kconfig @@ -199,6 +199,7 @@ config ARCH_BRCMSTB 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 add3771d38f640196a32b5be1deaf329de4dc329..9a22d40602aab365d813e72c8a672b7c6f810ad4 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -821,6 +821,8 @@ static struct platform_device da8xx_dsp = { .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 @@ void __init da8xx_rproc_reserve_cma(void) 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 __init da8xx_register_rproc(void) { 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 b3347d32349f6adc947bcacc4d9349ebb75889d2..94906ed4939236b4a3d9a1a4896e57a418b23b98 100644 --- a/arch/arm/mach-imx/cpu.c +++ b/arch/arm/mach-imx/cpu.c @@ -131,6 +131,9 @@ struct device * __init imx_soc_device_init(void) 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 34f2ff62583c6ebc1e93dd1285d6e2b3c83d5e65..e00d6260c3dfb96c25b7a5ab337bec1e108c8e1f 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 @@ static inline bool cpu_is_imx6ul(void) 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 541647f5719255cfd755a22b0435f97426e92612..895c0746fe5032e484efa2303e59a4c785e06ba4 100644 --- a/arch/arm/mach-mvebu/Kconfig +++ b/arch/arm/mach-mvebu/Kconfig @@ -42,7 +42,7 @@ config MACH_ARMADA_375 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 @@ config MACH_ARMADA_38X 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 ef9ed36e8a612154c5e3adbff9439b9a5871f85f..e3416b40e82baf5bc7cbd57e28d280d8a039f2b5 100644 --- a/arch/arm/mach-omap2/clockdomains7xx_data.c +++ b/arch/arm/mach-omap2/clockdomains7xx_data.c @@ -524,7 +524,7 @@ static struct clockdomain pcie_7xx_clkdm = { .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 9ff92050053cbb2939c751564b21ab7bce01b9e5..fa7f308c90279eb20ef89fdcdaafef31a1f8ec2c 100644 --- a/arch/arm/mach-omap2/omap-secure.c +++ b/arch/arm/mach-omap2/omap-secure.c @@ -73,6 +73,7 @@ phys_addr_t omap_secure_ram_mempool_base(void) 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 @@ u32 omap3_save_secure_ram(void __iomem *addr, int size) 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 1ab1fbbe2721853a0edbd23a15805e6a8cc0f232..b055b60ef9e3c2576ff2050582c0fb9141d9a8f5 100644 --- a/arch/arm/mach-qcom/Kconfig +++ b/arch/arm/mach-qcom/Kconfig @@ -65,6 +65,17 @@ config ARCH_MSM8953 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 diff --git a/arch/arm/mach-qcom/board-msm8953.c b/arch/arm/mach-qcom/board-msm8953.c index 04b0bcc4460344641eaba50e2e8da23fed0285c2..de4538fb0e711cb2fba4af7370beb7d593c30693 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 #include +#include static const char *msm8953_dt_match[] __initconst = { "qcom,msm8953", @@ -23,9 +24,25 @@ static const char *msm8953_dt_match[] __initconst = { 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 + */ + 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 c648eafcc68516bc17918e8fa5cb0cdc1852b043..6bdf0f4eb9572d23a0451f9bb055752cf0a90209 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 312f3a5c5bc382816641ba97163af474d6a09ff7..7b9aa0fa8676b88addc7ed0864591968b9c9e020 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/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index da0b33deba6d3c2906eef271f253ab7a30a92680..5629d7580973bf65b845cd6f91ee0c27d8ccb360 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c @@ -648,7 +648,7 @@ int vfp_restore_user_hwstate(struct user_vfp __user *ufp, */ 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 b42422e3e8e4d9dffc3ebe347f2f4e9651ef43fa..9c01a3131f79497797c0ca96a26f1f890b2e7c2b 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -16,6 +16,7 @@ config ARM64 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_845719 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 @@ config HARDEN_BRANCH_PREDICTOR 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_BASE 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 diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index f08cfd07851302ae1e467782acaf3bf97def42b2..2b265a7ad330be93c6e480542ae139943c846477 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -26,8 +26,17 @@ ifeq ($(CONFIG_ARM64_ERRATUM_843419),y) 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 @@ CHECKFLAGS += -D__aarch64__ 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) diff --git a/arch/arm64/boot/dts/qcom/8909-pm8916.dtsi b/arch/arm64/boot/dts/qcom/8909-pm8916.dtsi index af56793e27b67b85c08b537ddfa0dce74158e81c..3247d0d1e07a4145994e37ccd7be6f8366edefea 100644 --- a/arch/arm64/boot/dts/qcom/8909-pm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/8909-pm8916.dtsi @@ -296,7 +296,6 @@ qcom,scale-function = <2>; qcom,hw-settle-time = <2>; qcom,fast-avg-setup = <0>; - qcom,vadc-thermal-node; }; chan@32 { @@ -308,7 +307,6 @@ qcom,scale-function = <4>; qcom,hw-settle-time = <2>; qcom,fast-avg-setup = <0>; - qcom,vadc-thermal-node; }; chan@3c { @@ -320,7 +318,6 @@ qcom,scale-function = <4>; qcom,hw-settle-time = <2>; qcom,fast-avg-setup = <0>; - qcom,vadc-thermal-node; }; }; diff --git a/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi b/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi index 394740610c8da3d83c532c0a128fb1b6e4eaef4f..c8330bd689d60e4f2bc26eab7b7041162008a69f 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 31d4b0d5bc0feb2542f1e6a64254698529793db2..98238d9a79a7f6138a7c19c38032bbc4730c3888 100644 --- a/arch/arm64/boot/dts/qcom/Makefile +++ b/arch/arm64/boot/dts/qcom/Makefile @@ -308,7 +308,8 @@ dtbo-$(CONFIG_ARCH_SDM632) += sdm632-rumi-overlay.dtbo \ 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 dtbo-$(CONFIG_ARCH_SDM429) += sdm429-mtp-overlay.dtbo \ sdm429-cdp-overlay.dtbo \ @@ -368,14 +369,19 @@ sdm632-qrd-overlay.dtbo-base := sdm632.dtb \ 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 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 +404,10 @@ dtb-$(CONFIG_ARCH_MSM8953) += msm8953-cdp.dtb \ apq8053-iot-mtp.dtb \ apq8053-lite-dragon-v1.0.dtb \ apq8053-lite-dragon-v2.0.dtb \ + apq8053-lite-lenovo-v1.0.dtb \ + apq8053-lite-lenovo-v1.1.dtb \ + apq8053-lite-harman-v1.0.dtb \ + apq8053-lite-lge-v1.0.dtb \ msm8953-pmi8940-cdp.dtb \ msm8953-pmi8940-mtp.dtb \ msm8953-pmi8937-cdp.dtb \ @@ -414,14 +424,29 @@ dtb-$(CONFIG_ARCH_MSM8937) += msm8937-pmi8950-mtp.dtb \ 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-robot-som-refboard.dtb \ apq8009-dragon.dtb dtb-$(CONFIG_ARCH_SDM450) += sdm450-rcm.dtb \ @@ -459,7 +484,8 @@ dtb-$(CONFIG_ARCH_SDM439) += sdm439-mtp.dtb \ sdm439-cdp.dtb \ sdm439-qrd.dtb \ sda439-mtp.dtb \ - sda439-cdp.dtb + sda439-cdp.dtb \ + sdm439-external-codec-mtp.dtb dtb-$(CONFIG_ARCH_SDM429) += sdm429-mtp.dtb \ sdm429-cdp.dtb \ diff --git a/arch/arm64/boot/dts/qcom/apq8009-dragon.dts b/arch/arm64/boot/dts/qcom/apq8009-dragon.dts index 1446c9c8b3a59ed67d7aa4b97a5188542c2e1b2b..12a4363c3115b9c4a290ee4d7ed0247eee7014e1 100644 --- a/arch/arm64/boot/dts/qcom/apq8009-dragon.dts +++ b/arch/arm64/boot/dts/qcom/apq8009-dragon.dts @@ -66,6 +66,11 @@ mdss_mdp: qcom,mdss_mdp@1a00000 { status = "disabled"; }; + + bluetooth: bt_qca9379 { + compatible = "qca,qca9379"; + qca,bt-reset-gpio = <&msm_gpio 47 0>; /* BT_EN */ + }; }; &sdhc_2 { @@ -73,11 +78,12 @@ }; &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 +117,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-mtp-wcd9326-refboard.dts b/arch/arm64/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts index 9efd8083b01b59996b13245e86310101bab8a6bd..a0a9c5457a0047921d9775fa67ebe78ebb13d1e0 100644 --- a/arch/arm64/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts +++ b/arch/arm64/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts @@ -268,6 +268,7 @@ }; &i2c_4 { + status= "okay"; smb1360_otg_supply: smb1360-chg-fg@14 { compatible = "qcom,smb1360-chg-fg"; reg = <0x14>; @@ -283,15 +284,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 { 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 0000000000000000000000000000000000000000..958f7c86082ccfc014bdf9192020a696b46629c4 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/apq8009-robot-som-refboard.dts @@ -0,0 +1,188 @@ +/* + * 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 0x87a00000 0x0 0x0600000>; +}; + +&modem_adsp_mem { + reg = <0x0 0x88000000 0x0 0x01e00000>; +}; + +&peripheral_mem { + reg = <0x0 0x89e00000 0x0 0x0700000>; +}; + +&pm8916_chg { + status = "ok"; +}; + +&pm8916_bms { + status = "ok"; +}; + +&blsp1_uart2_hs { + 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 57a28d015ccfe4fdbf4dd86644330f49f84a6d0b..1fe7b1551da37089fff870ff66b9e192ba6f3233 100644 --- a/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts +++ b/arch/arm64/boot/dts/qcom/apq8009w-bg-alpha.dts @@ -296,3 +296,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 454ba81684a3fb6aafbd6a4f2be558e3fd04751e..81136703dd4731cbd29901a595fd9d2b19775b84 100644 --- a/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts +++ b/arch/arm64/boot/dts/qcom/apq8009w-bg-wtp-v2.dts @@ -313,3 +313,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 0000000000000000000000000000000000000000..56f69ad663200aff1d1c9087ad942b4de3642857 --- /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 0000000000000000000000000000000000000000..19c24f8e3086cfc1632069405b2008da6fdd5a7c --- /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 0000000000000000000000000000000000000000..6faa41317cdf7cefdc007e2ece58cb51d7bcf5da --- /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 0000000000000000000000000000000000000000..01305e6bfa49c2e745c3b85a9dc20a93798de083 --- /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 0000000000000000000000000000000000000000..8ddb1fcc2a0e67ca3f998bb034aacb258a798dc3 --- /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 0000000000000000000000000000000000000000..3337add3fae7302b09de1ade910c2e929153c3ea --- /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 0000000000000000000000000000000000000000..5c8ef83f5ae6a987819c6a706a6280819d8ab6df --- /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 0000000000000000000000000000000000000000..b5b766769fc77c305727bfc945d39e66ab94c3c8 --- /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 0000000000000000000000000000000000000000..7dfa9527118446842820c8d1dbf50cf0379f2f7c --- /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 0000000000000000000000000000000000000000..fecc7ce8d08f1b5a50d8fdf0d27cb25d37469056 --- /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 8f7e3261e3b17500591f005a45b879f3ab5053d6..9f0edda5e3ed3a19c3db9833037144a0235deae5 100644 --- a/arch/arm64/boot/dts/qcom/apq8053-iot-mtp.dts +++ b/arch/arm64/boot/dts/qcom/apq8053-iot-mtp.dts @@ -26,3 +26,63 @@ 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"; +}; 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 d7836e5485bb2ee507587641c2646e9e4e59ab45..55d8b7b13e6e66a8de4fedc5d353e88cd7f34cc7 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 947af4d1fa13539fdae7d8cd903effdb0a5925b8..bb40018e1737a40c6e95d47a8ff80ec845f5bb5c 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"; +&pm8953_l4 { + status = "okay"; + regulator-always-on; }; -&labibb { - status = "okay"; - qpnp,qpnp-labibb-mode = "lcd"; +&camera0 { + qcom,mount-angle = <90>; }; -&wled { - qcom,cons-sync-write-delay-us = <1000>; - qcom,led-strings-list = [00 01 02 03]; +&camera1 { + qcom,mount-angle = <90>; }; -&pm8953_l4 { - status = "okay"; - regulator-always-on; +&camera2{ + qcom,mount-angle = <90>; }; diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi index 5e3ddce4f481829287b4401304d2eae91ddb06f1..bd48f090165486da77ac40f22eecaf35dde3a039 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 { @@ -290,6 +339,7 @@ &sdhc_2 { /* 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>; @@ -301,7 +351,7 @@ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &sdc2_wlan_gpio_off>; - 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/apq8053-lite-harman-v1.0.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dts new file mode 100644 index 0000000000000000000000000000000000000000..203b6b89ef6a8dfa578c4952fe36e52b3cbe0318 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.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-harman-v1.0.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. APQ8053 Lite Harman v1.0 Board"; + 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-harman-v1.0.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..5cf8ac06ff439cdab9abfd6967363d913abe149a --- /dev/null +++ b/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.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-lenovo-v1.0.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dts new file mode 100644 index 0000000000000000000000000000000000000000..325accffdf5c267b923770fcf68761b3f100e528 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.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-lenovo-v1.0.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. APQ8053 Lite Lenovo v1.0 Board"; + 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-lenovo-v1.0.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..4d9c40c2d07071fd7c31c3bde43f8243206eba5c --- /dev/null +++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.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-lenovo-v1.1.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dts new file mode 100644 index 0000000000000000000000000000000000000000..0c7b5577ff4dc13068f3eb904393cc29948e187c --- /dev/null +++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.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-lenovo-v1.1.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. APQ8053 Lite Lenovo v1.1 Board"; + 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-lenovo-v1.1.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..396fd55b7c3a4c28efeb8aeeb3e935962295e8eb --- /dev/null +++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dtsi @@ -0,0 +1,62 @@ +/* + * 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; +}; + +&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-lge-v1.0.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dts new file mode 100644 index 0000000000000000000000000000000000000000..70952dc6d835aa2ab4f83157ab3c802b70a06b7d --- /dev/null +++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.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-lge-v1.0.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. APQ8053 Lite LGE v1.0 Board"; + 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-lge-v1.0.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..db0331e18ce63f90540749f64c51a5c112e7e669 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.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/dsi-panel-390p-auo-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..b4ac287f8badd374fe2b5fcb5217cefd9d09e670 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/dsi-panel-390p-auo-cmd.dtsi @@ -0,0 +1,98 @@ +/* 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 + ]; + 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-nt35597-truly-dsc-wqxga-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi index 2c54504dc8cdbb761119c899ffc17258454743fc..b2a541d9384e18dda1cfe7a7f9663f482e7125df 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 ab088b89359e7351bd2dc066872121d1ff78a837..707875baf50e64ab3011a6978d5a9dd950170ab8 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 = , , - , - , - , - , - , - , - , - ; + , + , + , + , + , + , + , + ; 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 e4fe2e392ea2fe76a26c47d1760875da7ffbd80e..0ac9c2a1dc3f34c881ad568bf35da80a3192a877 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 = , , - , - , - , - , - , - , - , - ; + , + , + , + , + , + , + , + ; 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 0000000000000000000000000000000000000000..fe9094fb006874f0a0dd6f703e6b2e14f7d23287 --- /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 46069560f8c8a2b9ccfabe70009b46374ddc8af1..94672972531cbe7fd6ccf4900ab21051b2b711be 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,10 @@ compatible = "qcom,msm-pcm-hostless"; }; + audio_apr: qcom,msm-audio-apr { + compatible = "qcom,msm-audio-apr"; + }; + 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 f2cea322ecc56df805e0879adbfb1136c66a0d21..fa6498edcd7df7e91c5dc9faba4fe4328d5c80e6 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 4e7d6f8b955a8152a3e5cad6ce934ce30a8e8f2d..9d35b0628db650882175c675dedb25f9ad98c3ed 100644 --- a/arch/arm64/boot/dts/qcom/msm8909-coresight.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8909-coresight.dtsi @@ -1,623 +1,747 @@ -/* 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>; + 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"; + port { + tmc_etr_in_replicator: endpoint { + slave-mode; + remote-endpoint = <&replicator_out_tmc_etr>; + }; + }; }; - 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>; + tmc_etf: tmc@825000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b961>; - interrupts = <0 82 0>; - interrupt-names = "nidnt-irq"; + reg = <0x825000 0x1000>; + reg-names = "tmc-base"; - vdd-supply = <&pm8909_l11>; - qcom,vdd-voltage-level = <2950000 2950000>; - qcom,vdd-current-level = <15000 400000>; + coresight-name = "coresight-tmc-etf"; - vdd-io-supply = <&pm8909_l12>; - qcom,vdd-io-voltage-level = <2950000 2950000>; - qcom,vdd-io-current-level = <200 50000>; + 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 = "qcom,coresight-replicator"; + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b909>; + 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"; - tmc_etf: tmc@825000 { - compatible = "arm,coresight-tmc"; - 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; - coresight-ctis = <&cti0 &cti8>; + ports { + #address-cells = <1>; + #size-cells = <0>; - clocks = <&clock_rpm clk_qdss_clk>, - <&clock_rpm clk_qdss_a_clk>; - clock-names = "core_clk", "core_a_clk"; + 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_center: funnel@869000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; - funnel_in2: funnel@869000 { - compatible = "arm,coresight-funnel"; 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"; - cti0: cti@810000 { - compatible = "arm,coresight-cti"; - reg = <0x810000 0x1000>; - reg-names = "cti-base"; + ports { + #address-cells = <1>; + #size-cells = <0>; - coresight-id = <7>; - coresight-name = "coresight-cti0"; - coresight-nr-inports = <0>; - clocks = <&clock_rpm clk_qdss_clk>, - <&clock_rpm clk_qdss_a_clk>; - clock-names = "core_clk", "core_a_clk"; - }; + port@0 { + funnel_right_out_funnel_in0: endpoint { + remote-endpoint = + <&funnel_in0_in_funnel_right>; + }; + }; - cti1: cti@811000 { - compatible = "arm,coresight-cti"; - reg = <0x811000 0x1000>; - reg-names = "cti-base"; + port@2 { + reg = <2>; + funnel_right_in_funnel_apss: endpoint { + slave-mode; + remote-endpoint = + <&funnel_apss_out_funnel_right>; + }; + }; - coresight-id = <8>; - coresight-name = "coresight-cti1"; - coresight-nr-inports = <0>; - clocks = <&clock_rpm clk_qdss_clk>, - <&clock_rpm clk_qdss_a_clk>; - clock-names = "core_clk", "core_a_clk"; + port@3 { + reg = <0>; + funnel_right_in_wcn_etm0: endpoint { + slave-mode; + remote-endpoint = + <&wcn_etm0_out_funnel_mm>; + }; + }; + }; }; - cti2: cti@812000 { - compatible = "arm,coresight-cti"; - reg = <0x812000 0x1000>; - reg-names = "cti-base"; + funnel_apss: funnel@855000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b908>; - coresight-id = <9>; - coresight-name = "coresight-cti2"; - coresight-nr-inports = <0>; - clocks = <&clock_rpm clk_qdss_clk>, - <&clock_rpm clk_qdss_a_clk>; - clock-names = "core_clk", "core_a_clk"; - }; + reg = <0x855000 0x1000>; + reg-names = "funnel-base"; - cti3: cti@813000 { - compatible = "arm,coresight-cti"; - reg = <0x813000 0x1000>; - reg-names = "cti-base"; + coresight-name = "coresight-funnel-apss"; - coresight-id = <10>; - coresight-name = "coresight-cti3"; - coresight-nr-inports = <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_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>; + }; + }; + + }; + }; - cti4: cti@814000 { - compatible = "arm,coresight-cti"; - reg = <0x814000 0x1000>; - reg-names = "cti-base"; + tpiu: tpiu@820000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b912>; + coresight-name = "coresight-tpiu"; - coresight-id = <11>; - coresight-name = "coresight-cti4"; - 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"; }; - cti5: cti@815000 { - compatible = "arm,coresight-cti"; - reg = <0x815000 0x1000>; - reg-names = "cti-base"; + etm0: etm@84c000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b955>; + + reg = <0x84c000 0x1000>; + cpu = <&CPU0>; + coresight-name = "coresight-etm0"; - 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"; + clock-names = "apb_pclk"; + + port { + etm0_out_funnel_apss0: endpoint { + remote-endpoint = <&funnel_apss0_in_etm0>; + }; + }; }; - cti6: cti@816000 { - compatible = "arm,coresight-cti"; - reg = <0x816000 0x1000>; - reg-names = "cti-base"; + etm1: etm@84d000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b955>; + + reg = <0x84d000 0x1000>; + cpu = <&CPU1>; + coresight-name = "coresight-etm1"; - 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"; + clock-names = "apb_pclk"; - qcom,cti-gpio-trigout = <2>; - pinctrl-names = "cti-trigout-pctrl"; - pinctrl-0 = <&trigout_a0>; + port { + etm1_out_funnel_apss0: endpoint { + remote-endpoint = <&funnel_apss0_in_etm1>; + }; + }; }; - cti7: cti@817000 { - compatible = "arm,coresight-cti"; - reg = <0x817000 0x1000>; - reg-names = "cti-base"; + etm2: etm@84e000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b955>; + + reg = <0x84e000 0x1000>; + cpu = <&CPU2>; + coresight-name = "coresight-etm2"; - 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"; + clock-names = "apb_pclk"; + + port { + etm2_out_funnel_apss0: endpoint { + remote-endpoint = <&funnel_apss0_in_etm2>; + }; + }; }; - cti8: cti@818000 { - compatible = "arm,coresight-cti"; - reg = <0x818000 0x1000>; - reg-names = "cti-base"; + etm3: etm@84f000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b955>; + + reg = <0x84f000 0x1000>; + coresight-name = "coresight-etm3"; + cpu = <&CPU3>; - 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"; + clock-names = "apb_pclk"; + + port { + etm3_out_funnel_apss0: endpoint { + remote-endpoint = <&funnel_apss0_in_etm3>; + }; + }; }; - cti_cpu0: cti@851000 { - compatible = "arm,coresight-cti"; - reg = <0x851000 0x1000>; - reg-names = "cti-base"; + stm: stm@802000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b962>; - coresight-id = <16>; - coresight-name = "coresight-cti-cpu0"; - coresight-nr-inports = <0>; - coresight-cti-cpu = <&CPU0>; + reg = <0x802000 0x1000>, + <0x9280000 0x180000>; + reg-names = "stm-base", "stm-stimulus-base"; - qcom,cti-save; + coresight-name = "coresight-stm"; 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"; + port { + stm_out_funnel_in0: endpoint { + remote-endpoint = <&funnel_in0_in_stm>; + }; + }; + }; - coresight-id = <17>; - coresight-name = "coresight-cti-cpu1"; - coresight-nr-inports = <0>; - coresight-cti-cpu = <&CPU1>; + cti0: cti@810000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; - qcom,cti-save; + 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 = "core_clk", "core_a_clk"; + clock-names = "apb_pclk"; }; - 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>; + cti1: cti@811000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; - qcom,cti-save; + 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 = "core_clk", "core_a_clk"; + clock-names = "apb_pclk"; }; - 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>; + cti2: cti@812000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; - qcom,cti-save; + 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 = "core_clk", "core_a_clk"; + clock-names = "apb_pclk"; }; - cti_rpm_cpu0: cti@83c000 { - compatible = "arm,coresight-cti"; - reg = <0x83c000 0x1000>; - reg-names = "cti-base"; + cti3: cti@813000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; - coresight-id = <20>; - coresight-name = "coresight-cti-rpm-cpu0"; - coresight-nr-inports = <0>; + 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 = "core_clk", "core_a_clk"; + clock-names = "apb_pclk"; }; - cti_modem_cpu0: cti@838000 { - compatible = "arm,coresight-cti"; - reg = <0x838000 0x1000>; - reg-names = "cti-base"; + cti4: cti@814000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; - coresight-id = <21>; - coresight-name = "coresight-cti-modem-cpu0"; - coresight-nr-inports = <0>; + 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 = "core_clk", "core_a_clk"; + clock-names = "apb_pclk"; }; - cti_wcn_cpu0: cti@835000 { - compatible = "arm,coresight-cti"; - reg = <0x835000 0x1000>; - reg-names = "cti-base"; + cti5: cti@815000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; - coresight-id = <22>; - coresight-name = "coresight-cti-wcn-cpu0"; - coresight-nr-inports = <0>; + 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 = "core_clk", "core_a_clk"; + clock-names = "apb_pclk"; }; - cti_video_cpu0: cti@830000 { - compatible = "arm,coresight-cti"; - reg = <0x830000 0x1000>; - reg-names = "cti-base"; + cti6: cti@816000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; - coresight-id = <23>; - coresight-name = "coresight-cti-video-cpu0"; - coresight-nr-inports = <0>; + 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 = "core_clk", "core_a_clk"; + clock-names = "apb_pclk"; }; - stm: stm@802000 { - compatible = "arm,coresight-stm"; - reg = <0x802000 0x1000>, - <0x9280000 0x180000>; - reg-names = "stm-base", "stm-data-base"; + cti7: cti@817000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; + + reg = <0x817000 0x1000>; + reg-names = "cti-base"; + coresight-name = "coresight-cti7"; - 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"; }; - csr: csr@801000 { - compatible = "qcom,coresight-csr"; - reg = <0x801000 0x1000>; - reg-names = "csr-base"; + cti8: cti@818000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; - coresight-id = <25>; - coresight-name = "coresight-csr"; - coresight-nr-inports = <0>; + reg = <0x818000 0x1000>; + reg-names = "cti-base"; + coresight-name = "coresight-cti8"; - qcom,blk-size = <1>; clocks = <&clock_rpm clk_qdss_clk>, <&clock_rpm clk_qdss_a_clk>; - clock-names = "core_clk", "core_a_clk"; + clock-names = "apb_pclk"; }; - funnel_apss: funnel@855000 { - compatible = "arm,coresight-funnel"; - reg = <0x855000 0x1000>; - reg-names = "funnel-base"; + cti_cpu0: cti@851000{ + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; - coresight-id = <26>; - coresight-name = "coresight-funnel-apss"; - coresight-nr-inports = <4>; - coresight-outports = <0>; - coresight-child-list = <&funnel_in0>; - coresight-child-ports = <4>; + 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 = "core_clk", "core_a_clk"; + <&clock_rpm clk_qdss_a_clk>; + clock-names = "apb_pclk"; }; - etm0: etm@84c000 { - compatible = "arm,coresight-etm"; - reg = <0x84c000 0x1000>; - reg-names = "etm-base"; + cti_cpu1: cti@852000{ + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; - 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>; + 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 = "core_clk", "core_a_clk"; + clock-names = "apb_pclk"; }; - etm1: etm@84d000 { - compatible = "arm,coresight-etm"; - reg = <0x84d000 0x1000>; - reg-names = "etm-base"; + cti_cpu2: cti@853000{ + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; - 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>; + 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 = "core_clk", "core_a_clk"; + clock-names = "apb_pclk"; }; - etm2: etm@84e000 { - compatible = "arm,coresight-etm"; - reg = <0x84e000 0x1000>; - reg-names = "etm-base"; + cti_cpu3: cti@854000{ + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; - 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>; + 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 = "core_clk", "core_a_clk"; + clock-names = "apb_pclk"; }; - etm3: etm@84f000 { - compatible = "arm,coresight-etm"; - reg = <0x84f000 0x1000>; - reg-names = "etm-base"; + cti_modem_cpu0: cti@838000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; - 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>; + 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 = "core_clk", "core_a_clk"; + clock-names = "apb_pclk"; }; - 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>; + /* 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 = "core_clk", "core_a_clk"; + clock-names = "apb_pclk"; }; - rpm_etm0 { - compatible = "qcom,coresight-remote-etm"; + /* Pronto CTI */ + cti_wcn_cpu0: cti@835000 { + compatible = "arm,primecell"; + arm,primecell-periphid = <0x0003b966>; - coresight-id = <32>; - coresight-name = "coresight-rpm-etm0"; - coresight-nr-inports = <0>; - coresight-outports = <0>; - coresight-child-list = <&funnel_in0>; - coresight-child-ports = <0>; + reg = <0x835000 0x1000>; + reg-names = "cti-base"; + coresight-name = "coresight-cti-wcn-cpu0"; - qcom,inst-id = <4>; + clocks = <&clock_rpm clk_qdss_clk>, + <&clock_rpm clk_qdss_a_clk>; + clock-names = "apb_pclk"; }; 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>; + + port { + wcn_etm0_out_funnel_mm: endpoint { + remote-endpoint = <&funnel_right_in_wcn_etm0>; + }; + }; }; + /* MSS_SCL */ 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 = <11>; - qcom,inst-id = <2>; + port { + modem_etm0_out_funnel_right: endpoint { + remote-endpoint = <&funnel_in0_in_modem_etm0>; + }; + }; }; - 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"; + rpm_etm0 { + compatible = "qcom,coresight-remote-etm"; + coresight-name = "coresight-rpm-etm0"; + qcom,inst-id = <4>; - coresight-id = <35>; - coresight-name = "coresight-fuse"; - coresight-nr-inports = <0>; + port { + rpm_etm0_out_funnel_center: endpoint { + remote-endpoint = <&funnel_in0_in_rpm_etm0>; + }; + }; }; - 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>; + csr: csr@801000 { + compatible = "qcom,coresight-csr"; + reg = <0x801000 0x1000>; + reg-names = "csr-base"; - vdd-supply = <&pm8909_l11>; - qcom,vdd-voltage-level = <2800000 2950000>; - qcom,vdd-current-level = <15000 400000>; + coresight-name = "coresight-csr"; - vdd-io-supply = <&pm8909_l12>; - qcom,vdd-io-voltage-level = <1800000 2950000>; - qcom,vdd-io-current-level = <200 50000>; + qcom,usb-bam-support; + qcom,hwctrl-set-support; + qcom,set-byte-cntr-support; + qcom,blk-size = <1>; }; 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 = "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 = "core_clk", "core_a_clk"; + clock-names = "apb_pclk"; }; + }; diff --git a/arch/arm64/boot/dts/qcom/msm8909-ion.dtsi b/arch/arm64/boot/dts/qcom/msm8909-ion.dtsi index 858429bc2d95560487c88d73137a5e0e09f8b3b9..cd21e6ea9eb22e9363b64e32fe2fc2a7f8b169cb 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 0000000000000000000000000000000000000000..0a657daa20aa41be83e49991d66c5aaa7cd0b9cd --- /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 0d824e089d93b80ec166c8b395f7a72c060943e7..67c6d0670b0069133fc3bd08425c28c41fa3ee09 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 18fc575851d115e1aee40b4cb81206c1b0765ddc..e2fbc9efc82b0c1c8019a0f818935f6bff485572 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-vidc.dtsi b/arch/arm64/boot/dts/qcom/msm8909-vidc.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..5eeaa210be6264d7dfad3612150c17da991befc4 --- /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 = ; + qcom,bus-slave = ; + 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 = ; + qcom,bus-slave = ; + 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 90a4c8336e0de0c29ac415262267250723b7cc5a..a72fdbf1786152ef06ebd0e445ba6e760d83d93a 100644 --- a/arch/arm64/boot/dts/qcom/msm8909.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8909.dtsi @@ -239,6 +239,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" @@ -700,6 +701,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"; @@ -1937,19 +1948,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 d9fa6fb5a411fffca6d2a2389b4881b2053319e9..9dd80f085c4ed0e9f7db7a0ffcd64fab38a33933 100644 --- a/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts +++ b/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts @@ -316,3 +316,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 707b56ef82ded28d1bdd7a39e7adc54244abad73..c2e28d1477303fead85919ae7f723d0c1f0129b5 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 0000000000000000000000000000000000000000..465859aeab5f67c7eae16d22d7bb6d0fa9720321 --- /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-cdp-mirror-lake-touch.dtsi b/arch/arm64/boot/dts/qcom/msm8917-cdp-mirror-lake-touch.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..d6d85fa7fdea3d99f4ac194e94890619d0ff1ecd --- /dev/null +++ b/arch/arm64/boot/dts/qcom/msm8917-cdp-mirror-lake-touch.dtsi @@ -0,0 +1,250 @@ +/* + * 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>; +}; + +&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 0000000000000000000000000000000000000000..fde4847f2a1fd3889cdbcea172f74b1d13c6d1a1 --- /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 0000000000000000000000000000000000000000..87303c5ad4c72cb282e02c2a93c3039c0a8c1a07 --- /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 792d5d123848146c076d6506bf0fd3e9e86741e4..5a242db555eaf2e04f9cabd61fa6fc7f0a842127 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 800ea1cd6c691181dc740ab6ecd9bad78eef308c..164b7816234e1f72802c0f2d5879bc8453d9c8a6 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-pm.dtsi b/arch/arm64/boot/dts/qcom/msm8917-pm.dtsi index 6200b4ee7129b10dee6755322f6b983fa283e18b..575d1b5d684fa6932c505e37e2896b9e6df01bf4 100644 --- a/arch/arm64/boot/dts/qcom/msm8917-pm.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8917-pm.dtsi @@ -125,7 +125,211 @@ }; }; + qcom,mpm@601d0 { + compatible = "qcom,mpm-v2"; + reg = <0x601d0 0x1000>, /* MSM_RPM_MPM_BASE 4K */ + <0xb011008 0x4>; + reg-names = "vmpm", "ipc"; + interrupts = <0 171 1>; + clocks = <&clock_gcc clk_xo_lpm_clk>; + clock-names = "xo"; + qcom,ipc-bit-offset = <1>; + qcom,gic-parent = <&intc>; + qcom,gic-map = <2 216>, /* tsens_upper_lower_int */ + <49 172>, /* usb1_hs_async_wakeup_irq */ + <58 166>, /* usb_hs_irq */ + <53 104>, /* mdss_irq */ + <62 222>, /* ee0_krait_hlos_spmi_periph_irq */ + <0xff 18>, /* APC_qgicQTmrSecPhysIrptReq */ + <0xff 19>, /* APC_qgicQTmrNonSecPhysIrptReq */ + <0xff 20>, /* qgicQTmrVirtIrptReq */ + <0xff 35>, /* WDT_barkInt */ + <0xff 39>, /* arch_mem_timer */ + <0xff 40>, /* qtmr_phy_irq[0] */ + <0xff 47>, /* rbif_irq[0] */ + <0xff 56>, /* q6_wdog_expired_irq */ + <0xff 57>, /* mss_to_apps_irq(0) */ + <0xff 58>, /* mss_to_apps_irq(1) */ + <0xff 59>, /* mss_to_apps_irq(2) */ + <0xff 60>, /* mss_to_apps_irq(3) */ + <0xff 61>, /* mss_a2_bam_irq */ + <0xff 65>, /* o_gc_sys_irq[0] */ + <0xff 69>, /* vbif_irpt */ + <0xff 73>, /* smmu_intr_bus[1] */ + <0xff 74>, /* smmu_bus_intr[2] */ + <0xff 75>, /* smmu_bus_intr[3] */ + <0xff 76>, /* venus_irq */ + <0xff 78>, /* smmu_bus_intr[5] */ + <0xff 79>, /* smmu_bus_intr[6] */ + <0xff 85>, /* smmu_bus_intr[31] */ + <0xff 86>, /* smmu_bus_intr[32] */ + <0xff 90>, /* smmu_bus_intr[33] */ + <0xff 92>, /* smmu_bus_intr[34] */ + <0xff 93>, /* smmu_bus_intr[35] */ + <0xff 97>, /* smmu_bus_intr[10] */ + <0xff 102>, /* smmu_bus_intr[14] */ + <0xff 108>, /* smmu_bus_intr[36] */ + <0xff 109>, /* smmu_bus_intr[37] */ + <0xff 112>, /* smmu_bus_intr[38] */ + <0xff 114>, /* qdsd_intr_out */ + <0xff 126>, /* smmu_bus_intr[39] */ + <0xff 128>, /* blsp1_peripheral_irq[3] */ + <0xff 129>, /* blsp1_peripheral_irq[4] */ + <0xff 131>, /* qup_irq */ + <0xff 136>, /* smmu_bus_intr[43] */ + <0xff 137>, /* smmu_intr_bus[44] */ + <0xff 138>, /* smmu_intr_bus[45] */ + <0xff 140>, /* uart_dm_intr */ + <0xff 141>, /* smmu_bus_intr[46] */ + <0xff 142>, /* smmu_bus_intr[47] */ + <0xff 143>, /* smmu_bus_intr[48] */ + <0xff 144>, /* smmu_bus_intr[49] */ + <0xff 145>, /* smmu_bus_intr[50] */ + <0xff 146>, /* smmu_bus_intr[51] */ + <0xff 147>, /* smmu_bus_intr[52] */ + <0xff 148>, /* smmu_bus_intr[53] */ + <0xff 149>, /* smmu_bus_intr[54] */ + <0xff 150>, /* smmu_bus_intr[55] */ + <0xff 151>, /* smmu_bus_intr[56] */ + <0xff 152>, /* smmu_bus_intr[57] */ + <0xff 153>, /* smmu_bus_intr[58] */ + <0xff 155>, /* sdc1_irq(0) */ + <0xff 157>, /* sdc2_irq(0) */ + <0xff 167>, /* bam_irq(0) */ + <0xff 170>, /* sdc1_pwr_cmd_irq */ + <0xff 173>, /* o_wcss_apss_smd_hi */ + <0xff 174>, /* o_wcss_apss_smd_med */ + <0xff 175>, /* o_wcss_apss_smd_low */ + <0xff 176>, /* o_wcss_apss_smsm_irq */ + <0xff 177>, /* o_wcss_apss_wlan_data_xfer_done */ + <0xff 178>, /* o_wcss_apss_wlan_rx_data_avail */ + <0xff 179>, /* o_wcss_apss_asic_intr */ + <0xff 181>, /* o_wcss_apss_wdog_bite_and_reset_rdy */ + <0xff 188>, /* lpass_irq_out_apcs(0) */ + <0xff 189>, /* lpass_irq_out_apcs(1) */ + <0xff 190>, /* lpass_irq_out_apcs(2) */ + <0xff 191>, /* lpass_irq_out_apcs(3) */ + <0xff 192>, /* lpass_irq_out_apcs(4) */ + <0xff 193>, /* lpass_irq_out_apcs(5) */ + <0xff 194>, /* lpass_irq_out_apcs(6) */ + <0xff 195>, /* lpass_irq_out_apcs(7) */ + <0xff 196>, /* lpass_irq_out_apcs(8) */ + <0xff 197>, /* lpass_irq_out_apcs(9) */ + <0xff 198>, /* coresight-tmc-etr interrupt */ + <0xff 200>, /* rpm_ipc(4) */ + <0xff 201>, /* rpm_ipc(5) */ + <0xff 202>, /* rpm_ipc(6) */ + <0xff 203>, /* rpm_ipc(7) */ + <0xff 204>, /* rpm_ipc(24) */ + <0xff 205>, /* rpm_ipc(25) */ + <0xff 206>, /* rpm_ipc(26) */ + <0xff 207>, /* rpm_ipc(27) */ + <0xff 215>, /* o_bimc_intr[0] */ + <0xff 224>, /* SPDM interrupt */ + <0xff 239>, /* crypto_bam_irq[1]*/ + <0xff 240>, /* summary_irq_kpss */ + <0xff 253>, /* sdcc_pwr_cmd_irq */ + <0xff 260>, /* ipa_irq[0] */ + <0xff 261>, /* ipa_irq[2] */ + <0xff 262>, /* ipa_bam_irq[0] */ + <0xff 263>, /* ipa_bam_irq[2] */ + <0xff 269>, /* rpm_wdog_expired_irq */ + <0xff 270>, /* blsp1_bam_irq[0] */ + <0xff 272>, /* smmu_intr_bus[17] */ + <0xff 273>, /* smmu_bus_intr[18] */ + <0xff 274>, /* smmu_bus_intr[19] */ + <0xff 275>, /* rpm_ipc(30) */ + <0xff 276>, /* rpm_ipc(31) */ + <0xff 277>, /* smmu_intr_bus[20] */ + <0xff 285>, /* smmu_bus_intr[28] */ + <0xff 286>, /* smmu_bus_intr[29] */ + <0xff 287>, /* smmu_bus_intr[30] */ + <0xff 321>, /* q6ss_irq_out(4) */ + <0xff 322>, /* q6ss_irq_out(5) */ + <0xff 323>, /* q6ss_irq_out(6) */ + <0xff 325>, /* q6ss_wdog_exp_irq */ + <0xff 344>; /* sdcc1ice */ + + qcom,gpio-parent = <&tlmm>; + qcom,gpio-map = <3 38 >, + <4 1 >, + <5 5 >, + <6 9 >, + <8 37>, + <9 36>, + <10 13>, + <11 35>, + <12 17>, + <13 21>, + <14 54>, + <15 34>, + <16 31>, + <17 58>, + <18 28>, + <19 42>, + <20 25>, + <21 12>, + <22 43>, + <23 44>, + <24 45>, + <25 46>, + <26 48>, + <27 65>, + <28 93>, + <29 97>, + <30 63>, + <31 70>, + <32 71>, + <33 72>, + <34 81>, + <35 126>, + <36 90>, + <37 128>, + <38 91>, + <39 41>, + <40 127>, + <41 86>, + <50 67>, + <51 73>, + <52 74>, + <53 62>, + <54 124>, + <55 61>, + <56 130>, + <57 59>, + <59 50>; + }; + qcom,cpu-sleep-status { compatible = "qcom,cpu-sleep-status"; }; + + qcom,rpm-log@29dc00 { + compatible = "qcom,rpm-log"; + reg = <0x29dc00 0x4000>; + qcom,rpm-addr-phys = <0x200000>; + qcom,offset-version = <4>; + qcom,offset-page-buffer-addr = <36>; + qcom,offset-log-len = <40>; + qcom,offset-log-len-mask = <44>; + qcom,offset-page-indices = <56>; + }; + + qcom,rpm-stats@29dba0 { + compatible = "qcom,rpm-stats"; + reg = <0x200000 0x1000>, + <0x290014 0x4>, + <0x29001c 0x4>; + reg-names = "phys_addr_base", "offset_addr", + "heap_phys_addrbase"; + qcom,sleep-stats-version = <2>; + }; + + 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 0000000000000000000000000000000000000000..9d8a2ebce6cbff5e42460113212a606b14e3d465 --- /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-rcm.dts b/arch/arm64/boot/dts/qcom/msm8917-pmi8937-rcm.dts new file mode 100644 index 0000000000000000000000000000000000000000..c7fe1151dbb0d859449f4784e6987e7de9430414 --- /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 0000000000000000000000000000000000000000..9785a4f8e7f49d6e1081aedf583ecb76a34645ec --- /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 0000000000000000000000000000000000000000..2cab7160b5e7dcd308f7a58e926bc62978035bc5 --- /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 0000000000000000000000000000000000000000..ea6b24a51bef225da381426311f255daef761280 --- /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 0000000000000000000000000000000000000000..47011013e0f088065afa0d23645d799b9e2922c5 --- /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 0000000000000000000000000000000000000000..a32e2b3ceb8a80ed4bb0bde1caca10a2b7b1f9da --- /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 0000000000000000000000000000000000000000..91c4f3ac6b91021c8a9272e79a592ea87ab573e3 --- /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 3fe60a302fea3c081264ce3b81ccc7998195ba08..5b458b22541db35a0fd197d3483026921857e1df 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 0000000000000000000000000000000000000000..a91bea1cf95ff075306c970ac2b80ea8e6c7de66 --- /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 0000000000000000000000000000000000000000..9543133096eea0616118763ad47d1e612903dc79 --- /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 f897b5941023d45c5845f6949e2f1e05062dd224..fae258d07bf93a279920b7ca2bb6f14c05522291 100644 --- a/arch/arm64/boot/dts/qcom/msm8917-qrd.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8917-qrd.dtsi @@ -78,6 +78,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.dtsi b/arch/arm64/boot/dts/qcom/msm8917.dtsi index 36db486b8de8d6010eae9c366b3d31e08d071d10..a28511085c83ca59f64929b22d1818d147a442de 100644 --- a/arch/arm64/boot/dts/qcom/msm8917.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8917.dtsi @@ -161,6 +161,7 @@ #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" @@ -183,6 +184,18 @@ <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"; interrupts = ; @@ -482,6 +495,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>; @@ -713,6 +743,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>; @@ -724,6 +772,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>, @@ -782,6 +856,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>; @@ -1141,6 +1273,10 @@ qcom,ce-opp-freq = <100000000>; }; + qcom,iris-fm { + compatible = "qcom,iris_fm"; + }; + qcom,mss@4080000 { compatible = "qcom,pil-q6v55-mss"; reg = <0x04080000 0x100>, diff --git a/arch/arm64/boot/dts/qcom/msm8937-audio.dtsi b/arch/arm64/boot/dts/qcom/msm8937-audio.dtsi index 2e4d38ecaf293b15b7948ea82ae42ed146b91530..ffc266fb626ce0c67c841814cbda3e77bc92282a 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-interposer-sdm429.dtsi b/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm429.dtsi index 5ae93333d05407331a881e78d5d3ed1d61006088..433ed7c73f79aa204212badf9421fbdc850d5269 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-mdss-panels.dtsi b/arch/arm64/boot/dts/qcom/msm8937-mdss-panels.dtsi index 436b8a751cfb5507fdfc326d76e57dd8d57e2f78..ef4f4b09c451e0516a20b6768fc52edf048431c5 100644 --- a/arch/arm64/boot/dts/qcom/msm8937-mdss-panels.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8937-mdss-panels.dtsi @@ -23,6 +23,8 @@ #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" &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 42f1b5c5b47253fa8434e66c229a12a14df1bf2d..57823b8c48f41e87d6e879bffebe5c8212b490f2 100644 --- a/arch/arm64/boot/dts/qcom/msm8937-mtp.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8937-mtp.dtsi @@ -152,5 +152,12 @@ linux,can-disable; gpio-key,wakeup; }; + + }; +}; + +&thermal_zones { + quiet-therm-step { + status = "disabled"; }; }; diff --git a/arch/arm64/boot/dts/qcom/msm8937-vidc.dtsi b/arch/arm64/boot/dts/qcom/msm8937-vidc.dtsi index c4d5b90371af5f3eec4b303fe86e1c293d23015b..ce6692da688b62b05f79fae51f28f44ec1e0c830 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 240cc04fe244bacc0924c9e255a339746b3561cf..66c7e7cd987f04e5a5f07eab7ea7d5ace7a7801e 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"; }; @@ -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>; @@ -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-iot-mtp.dts b/arch/arm64/boot/dts/qcom/msm8953-iot-mtp.dts index 39c76cc4da5252c29a8e357ac8e43b1e72dadb6f..c3d4cc585fc43875fee2a3f956bc6d1f83785268 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.dtsi b/arch/arm64/boot/dts/qcom/msm8953.dtsi index 64e76645221e2ba6b340c806851e6a77e121c67c..67fd75ff7bc3260f1272badfb4b3cf62cfd7750c 100644 --- a/arch/arm64/boot/dts/qcom/msm8953.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8953.dtsi @@ -17,6 +17,7 @@ #include #include #include +#include / { model = "Qualcomm Technologies, Inc. MSM8953"; @@ -68,7 +69,7 @@ modem_mem: modem_region@0 { compatible = "removed-dma-pool"; - no-map-fixup; + no-map; reg = <0x0 0x86c00000 0x0 0x6a00000>; }; @@ -168,7 +169,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 +362,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>; @@ -1322,6 +1329,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 7d38151c9efcbb69f197fba4342857eb6f23fff1..af290809edd3fadad87bbd81501b6612abcce071 100644 --- a/arch/arm64/boot/dts/qcom/pm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/pm8916.dtsi @@ -169,6 +169,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 b8f8b6834b74a3b51c2b2a2839d81ffebf4c537f..0b1a50ad2d1752f1dde610e2a74057abf06694c1 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,7 @@ qcom,pmic-revid = <&pmi632_revid>; dpdm-supply = <&qusb_phy>; qcom,auto-recharge-soc = <98>; + qcom,chg-vadc = <&pmi632_vadc>; qcom,thermal-mitigation = <3000000 2500000 2000000 1500000 @@ -454,6 +464,7 @@ "ilim1-s1", "ilim2-s2", "vreg-ok"; + qcom,flash-disable-soc = <10>; }; smb5_vbus: qcom,smb5-vbus { diff --git a/arch/arm64/boot/dts/qcom/qcs605-lc.dtsi b/arch/arm64/boot/dts/qcom/qcs605-lc.dtsi index 15b23ded8c183f8d0c68ca026fff352f664c61b5..f8dde3965b8840a6ebb6421cd04fa2b913f0caae 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 1e1d82c38954639d30be6d03c0a38b13d3d34665..747593f87f88502bd52ab4d8feeecfbe6ef1e2b9 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 { diff --git a/arch/arm64/boot/dts/qcom/sda429.dts b/arch/arm64/boot/dts/qcom/sda429.dts new file mode 100644 index 0000000000000000000000000000000000000000..6a26f23f3d251875254fe95e3fbf2a4098f46285 --- /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 0000000000000000000000000000000000000000..a124c759b6fef5f01c3e46be80b97001d69ef659 --- /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/sda845-sdxpoorwills.dtsi b/arch/arm64/boot/dts/qcom/sda845-sdxpoorwills.dtsi index 0b678a86f3e7652f94f0a59fd136bdaf91d7b6e5..c4b4a5030c43efe915d8401907e5b9b8fa1e0385 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/sdm429-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm429-cdp-overlay.dts index 93a9ae99af6b4ee1916db8b1cbe568d020b2e98b..c55c2a508bae739e2410d4dd11027f60fa9987e2 100644 --- a/arch/arm64/boot/dts/qcom/sdm429-cdp-overlay.dts +++ b/arch/arm64/boot/dts/qcom/sdm429-cdp-overlay.dts @@ -22,5 +22,4 @@ / { 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 ab874c9d94db7428b77e5691b91c4913653af298..6c4ea9f2a73e708ad94a287debb97af2a52d6d60 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 3a339daa99d62e409a78910865e1eccbb79fc4d8..7735b35ced882a1ed7d218f86dcf06d057fe4dd3 100644 --- a/arch/arm64/boot/dts/qcom/sdm429-mtp-overlay.dts +++ b/arch/arm64/boot/dts/qcom/sdm429-mtp-overlay.dts @@ -22,5 +22,4 @@ / { 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 8abccb7aa1b9dd56f76863370ce289241fc0febb..fae68c994356e7a78046b8e6e18caafb7ff2da66 100644 --- a/arch/arm64/boot/dts/qcom/sdm429-qrd-overlay.dts +++ b/arch/arm64/boot/dts/qcom/sdm429-qrd-overlay.dts @@ -22,5 +22,4 @@ / { 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 7116662f29c613333df8db25e6f916a6f68f78d7..dde9c5616cc2c1eddc81f21a4e77864d5c546a95 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 33b352d8f92282bbbe84091d14d3806caaad49c0..19df05491c49f42d32c054fcbbb4d4ebd2bca8da 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 { @@ -178,3 +196,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 f6751d2445aab3f7cda6644656f103805465548c..a6b2371b2722728ec4f0f70fc9a8ccfa0c34532b 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-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm439-cdp-overlay.dts index 6d6f99bb56aa1678ad412ee690d7c3fad4cc77b2..5e866721dffd326d2e37b037067764be94238ec0 100644 --- a/arch/arm64/boot/dts/qcom/sdm439-cdp-overlay.dts +++ b/arch/arm64/boot/dts/qcom/sdm439-cdp-overlay.dts @@ -22,5 +22,4 @@ / { 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 f0e550ea28427d0ab0d3e066e6c5641bd6a51d29..fd66f4b2e8583b2ce6e56e0998ac71b3df4f0545 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"; }; @@ -200,3 +240,318 @@ qcom,mdss-dsi-bl-pmic-bank-select = <0>; qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>; }; + +&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 0000000000000000000000000000000000000000..a74fb7588f1dc98a7e3258c0ba668312cb577d25 --- /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 0000000000000000000000000000000000000000..468f514f6cfbbb5a8b1117e6dfc62053c3a66312 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/sdm439-external-codec-mtp-overlay.dts @@ -0,0 +1,28 @@ +/* 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 +#include +#include + +#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 0000000000000000000000000000000000000000..e2a86bb496361b8d2036a1265fc78471485c1553 --- /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 0000000000000000000000000000000000000000..83864ef31939210e74d989ca7cd9ab48e1b1ffb8 --- /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 df8a0d72a8cd81ad2f8af30152a7db44a6e1c0b7..8b6c6fb7f152f1330ac818889bd9b4cac5f3b295 100644 --- a/arch/arm64/boot/dts/qcom/sdm439-mtp-overlay.dts +++ b/arch/arm64/boot/dts/qcom/sdm439-mtp-overlay.dts @@ -22,5 +22,4 @@ / { 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 c6cec5360ed0c09d4344175e87973a0efcbf2cce..29e0d722d816c25e70f2e4804fb4b166259eb96d 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 { diff --git a/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi b/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi index 300c83aebf03c52c05e17dc2eaafc738224b36c5..bc2ba9f04d171c83c9d86a677ea1d88ac0a63ba6 100644 --- a/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi @@ -105,4 +105,83 @@ }; }; }; + + 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>; + }; + }; + }; }; diff --git a/arch/arm64/boot/dts/qcom/sdm439-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sdm439-qrd-overlay.dts index ac059c4da5498f78e651c66a21bed839e7045612..ed6b2ade25ea3fc1e804c5ced5dd629c26213b89 100644 --- a/arch/arm64/boot/dts/qcom/sdm439-qrd-overlay.dts +++ b/arch/arm64/boot/dts/qcom/sdm439-qrd-overlay.dts @@ -22,5 +22,4 @@ / { model = "QRD"; qcom,board-id = <0xb 2>; - qcom,msm-id = <353 0x0>; }; diff --git a/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi index 6d197869404bc407075c3ec74333997cf3511ad3..1979f4eaacd58584cf0fdce9377244fc6d9d6f03 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 { @@ -283,4 +288,27 @@ 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>; }; diff --git a/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi index f325925cdfa349f77544a2018f8ab6a6f57ee567..e2f2dea3d6986eaeeda375570ea730ea9e191180 100644 --- a/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi @@ -468,5 +468,26 @@ 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 = + /* */ + <(-1) (-1) ( 0) (-1) (-1) (-1)>, + <(-1) (-1) (-1) (-1) (-1) (-1)>; + + qcom,cpr-quotient-adjustment = + <66 77 66>, /* SVSP_30mV, NOM_35mV, TUR_30mV */ + <0 0 0>; + + qcom,cpr-voltage-ceiling-override = + <(-1) (-1) 795000 795000 835000 910000 910000>; + + 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 6cfd2d7ab4da4d4b735e126b69043e0cb894e3f1..0e4f6666462fbdba5c1ce34c537fd14947845880 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 = @@ -48,30 +53,87 @@ < 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 +142,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"; - - 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 { +/{ + /delete-node/ energy-costs; + energy_costs: energy-costs { + compatible = "sched-energy"; + + 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 + >; + }; }; }; @@ -258,3 +324,301 @@ vdd_hf_dig-supply = <&pm8953_s2_level_ao>; vdd_hf_pll-supply = <&pm8953_l7_ao>; }; + +&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"; + /delete-property/ qcom,dsi-pll-ssc-en; + /delete-property/ qcom,dsi-pll-ssc-mode; + /delete-property/ qcom,ssc-frequency-hz; + /delete-property/ qcom,ssc-ppm; +}; + +&mdss_dsi1_pll { + compatible = "qcom,mdss_dsi_pll_sdm439"; + reg = <0x001a96400 0x400>, + <0x0184d074 0x8>; + reg-names = "pll_base", "gdsc_base"; + /delete-property/ qcom,dsi-pll-ssc-en; + /delete-property/ qcom,dsi-pll-ssc-mode; + /delete-property/ qcom,ssc-frequency-hz; + /delete-property/ qcom,ssc-ppm; +}; + +&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>; + }; + }; + }; +}; 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 e12ad512bd5f530b8415bef6f22f45e0aa6e7aa2..97c938991567a19c14ddc5ab3b2c8bb4aa69845c 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 ae522a55ff8a242b22af9aa7b8dce3161899598b..3dd7200cc343eb51491e3029497188c0d94c9b83 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.dtsi b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi index 5c127bc57518e2ac69d22322be7b5393a1fe94c6..e09d637ce51359f8a70ef3472e8961efad81641f 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>; }; 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 558c3c65201630e3ba2417c978711b8f5dbdb5fe..e8dca18d73b5450f2df15a29d4e4c97d53523362 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/sdm632-coresight.dtsi b/arch/arm64/boot/dts/qcom/sdm632-coresight.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..62eeb65ea968e37f7057da47ff75969301d0e0d8 --- /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 de1bd1f1d8d88b1cdc264664cefa3b57083ca6ea..c37750a3ee1f8aab67c983ba2c2cf72a529e35e9 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 0000000000000000000000000000000000000000..b34e3d8db2ab72807f78c7fcb050f2a2b2df4898 --- /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 eb5afbd526a6ae40f0e054a4369f1f1ab8395ceb..c3e0ba9208c7db5d75546d70946630785729cd47 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-mtp-s4-overlay.dts b/arch/arm64/boot/dts/qcom/sdm632-ext-codec-mtp-s4-overlay.dts index 5656fd00fc53b60e8ab0d62ec7969cc0b89570e6..63b51ab85425b449810bf6763276f6522919da7e 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 d8326ff5d7c47d3feaec553bd6fc737a90c3479a..fd1eb3dd9b5e85cd40278bc402eb4dc9826ca4a8 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-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sdm632-qrd-overlay.dts index e6217e536fb05a18c9c0d81cec43ab36b0f9afd7..4b7f6b25bc6ab561e55acbd0d0f106747d02a437 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 09077c4244238620076f17c5635e39f80c55cb20..cefc0783a1c18f85237d55f49018277a3f9fe04a 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.dts b/arch/arm64/boot/dts/qcom/sdm632-rcm.dts index 68f0ea0ca3e5317361bab6febdaece40716ded4b..fe7ab38edd9ec5599806a26e95e83237af6397d6 100644 --- a/arch/arm64/boot/dts/qcom/sdm632-rcm.dts +++ b/arch/arm64/boot/dts/qcom/sdm632-rcm.dts @@ -24,3 +24,32 @@ qcom,pmic-id = <0x010016 0x010011 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-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm632-regulator.dtsi index b5373a22c5d2e3b2d129bcc93cdbb0309ec3b90d..33ac930e81c3918eba546f93aa79f6466aac0c48 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 = @@ -170,57 +179,232 @@ 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>, <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,cpr-floor-to-ceiling-max-range = + <50000 50000 50000 50000 50000 + 50000 50000>; - qcom,mem-acc-voltage = <2 2 2 2 3>; + 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 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>, <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 62689f597e868614497e508f418c4698b95ffa8b..67efe0f85d962dad2340f137833a02cca2721214 100644 --- a/arch/arm64/boot/dts/qcom/sdm632.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm632.dtsi @@ -40,6 +40,17 @@ &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 +835,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 +861,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 +887,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 +931,8 @@ < 1804800 >; qcom,cpufreq-table-4 = + < 633600 >, + < 902400 >, < 1094400 >, < 1401600 >, < 1555200 >, @@ -961,6 +980,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 +1004,4 @@ /delete-node/ case-therm-step; }; +#include "sdm632-coresight.dtsi" diff --git a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi index bd88087d94fec230c20235d1d893cd9e9ddf6e2d..da4d27da8d66a863a00aad9fbe7f6e79766d6546 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 @@ -143,7 +143,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 +153,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; diff --git a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi index cc5512790cf684c9cac4c62f1f2e0362100d1007..7764837cb7dbf8f1153a9b731f3a8860ab9db41c 100644 --- a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi @@ -202,7 +202,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 +212,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; diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pm.dtsi index bdcd039c145f3ff59596380cba2becbe4a9f98ae..8ed821affe94a54a07ca30dfb1e271532e1239b1 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.dtsi b/arch/arm64/boot/dts/qcom/sdm670.dtsi index 3ca33b2d2583945090dfb16d82157997993ce5b7..4b39207aae8efe4c11e27d65b360441226324138 100644 --- a/arch/arm64/boot/dts/qcom/sdm670.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm670.dtsi @@ -1096,7 +1096,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 +1389,7 @@ }; }; - mem_dump { + mem_dump: mem_dump { compatible = "qcom,mem-dump"; memory-region = <&dump_mem>; 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 9903d19c3562b428b5b028157b14d31fc159ae3f..0c37bf1345313a1832d90ea85e6e45907d9269a9 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 a5c6d84ccdbe49ab5a42af59b6711c5fe90b5d56..944c1ddc15c9f964c98c191d15b046b210776fa2 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-v2.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi index 575cf124a8031d148a0fb29c5bfa83da62268fdf..ba76273d507eeef534e41a436f8069fd9836a8f1 100644 --- a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi @@ -469,6 +469,7 @@ 2764800 30000 2784000 35000 2803200 40000 + 2841600 50000 >; idle-cost-data = < 100 80 60 40 @@ -535,6 +536,7 @@ 2764800 160 2784000 165 2803200 170 + 2841600 180 >; 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 22b4b9075505f8423257f15e45cfd96247f0773f..e9a913fb37b6ef0e097a442607785f97579f7b70 100644 --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi @@ -1276,7 +1276,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/renesas/r8a7796.dtsi b/arch/arm64/boot/dts/renesas/r8a7796.dtsi index 9217da9835256573477667cce41bc20355b7759c..53d03cb144e443da7259998764d89977d679b5a5 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 ff7ba4a38009f9695f1740fa719047de6114dfe1..1a31ee3769ffcf8d4e66e2e46db82d400511a7bb 100644 --- a/arch/arm64/configs/msm8937-perf_defconfig +++ b/arch/arm64/configs/msm8937-perf_defconfig @@ -18,7 +18,6 @@ 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 @@ -66,8 +65,8 @@ CONFIG_PREEMPT=y 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 @@ -147,6 +146,7 @@ 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_BPF=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y CONFIG_NETFILTER_XT_MATCH_CONNMARK=y @@ -208,8 +208,6 @@ 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 @@ -221,13 +219,12 @@ 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_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 @@ -246,7 +243,6 @@ 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 @@ -258,10 +254,8 @@ 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_REQ_CRYPT=y CONFIG_DM_UEVENT=y @@ -270,6 +264,14 @@ CONFIG_DM_VERITY_FEC=y 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 @@ -283,6 +285,21 @@ CONFIG_PPPOPNS=y 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 @@ -322,7 +339,6 @@ CONFIG_PINCTRL_QCOM_SPMI_PMIC=y 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 @@ -395,11 +411,9 @@ CONFIG_OV12830=y 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 @@ -410,7 +424,7 @@ CONFIG_FB_MSM_MDSS_WRITEBACK=y 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 @@ -426,12 +440,9 @@ CONFIG_USB_HIDDEV=y 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 @@ -445,14 +456,10 @@ 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 @@ -461,7 +468,6 @@ CONFIG_USB_CI13XXX_MSM=y 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 @@ -479,11 +485,12 @@ CONFIG_USB_CONFIGFS_F_CCID=y 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 @@ -577,9 +584,7 @@ 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 @@ -590,8 +595,6 @@ CONFIG_FUSE_FS=y 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 a394a4b42579bb613d0895567715003cd2771172..2bcdc2c0ebd11ccc404519d3ed931a5ed58a6ef5 100644 --- a/arch/arm64/configs/msm8937_defconfig +++ b/arch/arm64/configs/msm8937_defconfig @@ -19,7 +19,6 @@ 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 @@ -69,8 +68,8 @@ CONFIG_CLEANCACHE=y 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 @@ -151,6 +150,7 @@ 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_BPF=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y CONFIG_NETFILTER_XT_MATCH_CONNMARK=y @@ -213,8 +213,6 @@ 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 @@ -226,14 +224,13 @@ 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_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 +249,6 @@ 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 @@ -264,10 +260,8 @@ 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_REQ_CRYPT=y CONFIG_DM_UEVENT=y @@ -276,6 +270,14 @@ CONFIG_DM_VERITY_FEC=y 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 +291,21 @@ CONFIG_PPPOPNS=y 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 @@ -330,7 +347,6 @@ CONFIG_PINCTRL_QCOM_SPMI_PMIC=y 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 @@ -403,11 +419,9 @@ CONFIG_OV12830=y 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 +433,7 @@ CONFIG_FB_MSM_MDSS_WRITEBACK=y 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 +449,9 @@ CONFIG_USB_HIDDEV=y 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 +465,10 @@ 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 @@ -470,7 +477,6 @@ CONFIG_USB_CI13XXX_MSM=y 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 +494,13 @@ CONFIG_USB_CONFIGFS_F_CCID=y 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 @@ -595,9 +602,7 @@ 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 @@ -609,8 +614,6 @@ CONFIG_MSDOS_FS=y 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 46b34b30c470a888284cf7fc77e1bed97ed5543d..8fece0eebc737698d2a80999baf7d2f465b5047e 100644 --- a/arch/arm64/configs/msm8953-perf_defconfig +++ b/arch/arm64/configs/msm8953-perf_defconfig @@ -18,7 +18,6 @@ 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 @@ -68,6 +67,7 @@ CONFIG_ZSMALLOC=y 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 @@ -147,6 +147,7 @@ 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_BPF=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y CONFIG_NETFILTER_XT_MATCH_CONNMARK=y @@ -223,8 +224,12 @@ 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_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 +246,6 @@ 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 @@ -253,10 +257,8 @@ 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_REQ_CRYPT=y CONFIG_DM_UEVENT=y @@ -338,7 +340,6 @@ CONFIG_PINCTRL_QCOM_SPMI_PMIC=y 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 @@ -381,6 +382,7 @@ CONFIG_REGULATOR_SPM=y 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 +413,12 @@ CONFIG_OV12830=y 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 +429,7 @@ CONFIG_FB_MSM_MDSS_WRITEBACK=y 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 +496,12 @@ CONFIG_USB_CONFIGFS_F_CCID=y 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 @@ -598,8 +603,6 @@ CONFIG_FUSE_FS=y 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 dc58fc369cd9bf635c1cd4f0822d9cde1296d102..0eb9df44bc3d5dca339ac428ed87429489bae69d 100644 --- a/arch/arm64/configs/msm8953_defconfig +++ b/arch/arm64/configs/msm8953_defconfig @@ -19,7 +19,6 @@ 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 @@ -71,6 +70,7 @@ CONFIG_ZSMALLOC=y 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 @@ -151,6 +151,7 @@ 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_BPF=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y CONFIG_NETFILTER_XT_MATCH_CONNMARK=y @@ -229,8 +230,12 @@ 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_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 +252,6 @@ 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 @@ -259,10 +263,8 @@ 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_REQ_CRYPT=y CONFIG_DM_UEVENT=y @@ -347,7 +349,6 @@ CONFIG_PINCTRL_QCOM_SPMI_PMIC=y 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 @@ -390,6 +391,7 @@ CONFIG_REGULATOR_SPM=y 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 +422,12 @@ CONFIG_OV12830=y 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 +439,7 @@ CONFIG_FB_MSM_MDSS_WRITEBACK=y 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 +506,13 @@ CONFIG_USB_CONFIGFS_F_CCID=y 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 @@ -619,8 +624,6 @@ CONFIG_MSDOS_FS=y 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 f82678ed7691c73ba5f50949a3b50cae976d45f6..8e7c3699c80d3b7bf226641cbbeec3d81d4d87a8 100644 --- a/arch/arm64/configs/sdm670-perf_defconfig +++ b/arch/arm64/configs/sdm670-perf_defconfig @@ -67,6 +67,7 @@ CONFIG_BALANCE_ANON_FILE_RECLAIM=y 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 @@ -146,6 +147,7 @@ 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_BPF=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y CONFIG_NETFILTER_XT_MATCH_CONNMARK=y @@ -229,12 +231,15 @@ CONFIG_RMNET_DATA_DEBUG_PKT=y 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 +288,7 @@ CONFIG_PPPOLAC=y 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 diff --git a/arch/arm64/configs/sdm670_defconfig b/arch/arm64/configs/sdm670_defconfig index ee3418bd8c5f9935c0cbd39d2dbe506a06515e85..5fb8fdb4c8e56c67ca1f0f57b852655dee639276 100644 --- a/arch/arm64/configs/sdm670_defconfig +++ b/arch/arm64/configs/sdm670_defconfig @@ -72,6 +72,7 @@ CONFIG_BALANCE_ANON_FILE_RECLAIM=y 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 @@ -151,6 +152,7 @@ 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_BPF=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y CONFIG_NETFILTER_XT_MATCH_CONNMARK=y @@ -237,6 +239,8 @@ CONFIG_RMNET_DATA_DEBUG_PKT=y 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 +294,7 @@ CONFIG_PPPOLAC=y 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 diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig index f5dc2a8f4043306ab63ebc96f49056c5fe67935b..fe5b5b565f8ed1d497b6e9290cb00314b62a8001 100644 --- a/arch/arm64/configs/sdm845-perf_defconfig +++ b/arch/arm64/configs/sdm845-perf_defconfig @@ -65,6 +65,7 @@ CONFIG_ZSMALLOC=y 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 @@ -145,6 +146,7 @@ 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_BPF=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y CONFIG_NETFILTER_XT_MATCH_CONNMARK=y @@ -228,6 +230,8 @@ CONFIG_SOCKEV_NLMCAST=y 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 @@ -565,9 +569,7 @@ 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_EXT4_ENCRYPTION=y CONFIG_F2FS_FS=y diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig index 8dc560d8d0eaac1d796163f118891a352b0c74ec..666f350b632f5dbe8563d782684199fb63a73e12 100644 --- a/arch/arm64/configs/sdm845_defconfig +++ b/arch/arm64/configs/sdm845_defconfig @@ -69,6 +69,7 @@ CONFIG_ZSMALLOC=y 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 @@ -148,6 +149,7 @@ 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_BPF=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y CONFIG_NETFILTER_XT_MATCH_CONNMARK=y @@ -233,6 +235,8 @@ CONFIG_SOCKEV_NLMCAST=y 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 @@ -586,9 +590,7 @@ 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_EXT4_ENCRYPTION=y CONFIG_F2FS_FS=y diff --git a/arch/arm64/crypto/Kconfig b/arch/arm64/crypto/Kconfig index 2cf32e9887e1b292a0ec9102761ce3a200b8fa0d..2b10984ee58c3f71290c4c83ad186b34ff1b57c3 100644 --- a/arch/arm64/crypto/Kconfig +++ b/arch/arm64/crypto/Kconfig @@ -53,4 +53,11 @@ config CRYPTO_CRC32_ARM64 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 abb79b3cfcfea158cdcaa8ac1ffcbd32699da9b0..9908765a2f452990653eef41042614b0db739fd0 100644 --- a/arch/arm64/crypto/Makefile +++ b/arch/arm64/crypto/Makefile @@ -18,7 +18,8 @@ obj-$(CONFIG_CRYPTO_GHASH_ARM64_CE) += ghash-ce.o 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 @@ aes-ce-blk-y := aes-glue-ce.o aes-ce.o 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 50d9fe11d0c862bbd9cdd75855bfbc0fbffcfea7..9f4191774b35000cc54998dc90a268626e2d28d2 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 * @@ -10,16 +10,10 @@ #include #include -#include #include -#include #include "aes-ce-setkey.h" -MODULE_DESCRIPTION("Synchronous AES cipher using ARMv8 Crypto Extensions"); -MODULE_AUTHOR("Ard Biesheuvel "); -MODULE_LICENSE("GPL v2"); - struct aes_block { u8 b[AES_BLOCK_SIZE]; }; @@ -36,7 +30,7 @@ static int num_rounds(struct crypto_aes_ctx *ctx) 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 @@ static void aes_cipher_encrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[]) 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 @@ int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key, 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 0000000000000000000000000000000000000000..442949e7e849f17e8911def1e6ae55285043d654 --- /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 + * + * 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 +#include +#include +#include + +#include "aes-ce-setkey.h" + +MODULE_DESCRIPTION("Synchronous AES cipher using ARMv8 Crypto Extensions"); +MODULE_AUTHOR("Ard Biesheuvel "); +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 ea319c055f5dfbee35a31c68ceb005501a8f26b7..f08ecf431dcf8ce195099acc0c142a84e2bb29d2 100644 --- a/arch/arm64/crypto/sha1-ce-glue.c +++ b/arch/arm64/crypto/sha1-ce-glue.c @@ -28,6 +28,14 @@ struct sha1_ce_state { 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 0ed9486f75dd928568c86d1fdd3ad9bca3cde47a..c2432210f5e2031c5fe72299a282e638d388d2fd 100644 --- a/arch/arm64/crypto/sha2-ce-glue.c +++ b/arch/arm64/crypto/sha2-ce-glue.c @@ -28,6 +28,14 @@ struct sha256_ce_state { 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 0000000000000000000000000000000000000000..3ce82cc860bcf69ba169da3989636b92094d1254 --- /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 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 " +.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 0000000000000000000000000000000000000000..bd0f59f06c9d39460eb3c52022999d6d05b76e52 --- /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 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 " +.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 0000000000000000000000000000000000000000..b14463438b0966b6bc37f2f7784b0285c51ce290 --- /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 + */ + +#include + + .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 0000000000000000000000000000000000000000..c7d18569d408efee989dcf21dc3dc19c712cffb6 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 "); +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 f1ace59c545bd8406ecf7f0a75caa53da4156b56..c4cc771c891fcb2fe0988fad8c2ddb87e34b2356 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 @@ static inline u64 gic_read_iar_common(void) { 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 @@ static inline u64 gic_read_iar_cavium_thunderx(void) 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 u64 gic_read_iar_cavium_thunderx(void) 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,19 +203,28 @@ static inline u32 gic_read_sre(void) { 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) diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h index 88a4d1e8f6380dc70413d35837cd4661fc03b98c..c7749d8058ca9beb03b00bf1d581f15640faea72 100644 --- a/arch/arm64/include/asm/assembler.h +++ b/arch/arm64/include/asm/assembler.h @@ -108,6 +108,24 @@ dmb \opt .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 */ @@ -492,4 +510,8 @@ alternative_endif .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 0fe7e43b7fbc26bf2f6f2a47354a21943ae56ab9..0b0755c961ac13130607ce333e1c22cf707547dd 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 847bfe6a8503626249e2c8c34a4bb5ce6f5cf20d..9a6b81adc68d5d4fe870f67897542e79879326a1 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 f7e2c32ff9284e6444a8f2b3cc410ce6c4c60e18..50f70a8aac946e5c556db98a3853df4cd2be3faa 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 @@ -23,5 +22,3 @@ void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size); #endif #endif - -#endif diff --git a/arch/arm64/include/asm/futex.h b/arch/arm64/include/asm/futex.h index 85c4a8981d4703e854d23aa98d257d694d7ccb72..c5bc52e47f6ac31b6eb5554b8b762c14cc8f8ba5 100644 --- a/arch/arm64/include/asm/futex.h +++ b/arch/arm64/include/asm/futex.h @@ -48,16 +48,17 @@ do { \ } while (0) static inline int -futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) +futex_atomic_op_inuser(unsigned int encoded_op, 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 oparg = (int)(encoded_op << 8) >> 20; + int cmparg = (int)(encoded_op << 20) >> 20; int oldval = 0, ret, tmp; + u32 __user *uaddr = __uaccess_mask_ptr(_uaddr); if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) - oparg = 1 << oparg; + oparg = 1U << (oparg & 0x1f); if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; @@ -106,15 +107,17 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) } 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/kernel-pgtable.h b/arch/arm64/include/asm/kernel-pgtable.h index 77a27af013710de1d4bed56655ee587c5e6ef349..7803343e5881fbd7b2f635b25082d3e91d2583f8 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 e5050388e062209868bac64cab1740ece15b3e13..37d56e85036ea4060b990f154e293f739d2c0589 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -393,4 +393,9 @@ static inline void __cpu_init_stage2(void) "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 b18e852d27e85728db336fe3b7af10a1f97286e0..36d6758d580bb5b3c8155f26ce2f903605e2226d 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 35ea9c1206f0e6c0b4db1d90d850c450a4c89a7a..36d2aba5ba18757dd76b04e55b59c21d7957425c 100644 --- a/arch/arm64/include/asm/kvm_mmu.h +++ b/arch/arm64/include/asm/kvm_mmu.h @@ -325,7 +325,7 @@ static inline void *kvm_get_hyp_vector(void) 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 bc39e557c56c83e163e04c560ed45a3852e2d34f..0000000000000000000000000000000000000000 --- a/arch/arm64/include/asm/kvm_psci.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2012,2013 - ARM Ltd - * Author: Marc Zyngier - * - * 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 . - */ - -#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 8fe5ffc1029775e51ce4ba8a79a40deeea576090..708028e54153cdd1bcf50edd79e5d7ae5fc84268 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 f543df3a5921897b9f55c4f40f8227bf8b30545b..24c780d4f1c6efccf0b6642f054e3d6f7c3abd7c 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -21,7 +21,7 @@ #ifndef __ASSEMBLY__ -#include +#include typedef struct { atomic64_t id; @@ -55,7 +55,7 @@ DECLARE_PER_CPU_READ_MOSTLY(struct bp_hardening_data, bp_hardening_data); 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 a0fac5ca9cf86e103afa65dc3ece47522cc2b0c3..f2ea8a27eea3b0f358e510909ce350c8ae8732f5 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -134,7 +134,7 @@ static inline void cpu_install_idmap(void) * 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 84b5283d2e7feb0392af2a4f168396616e31623b..f705d96a76f2e099eb35325fc7026fee12f4234d 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 a4af9f0f8b812624ba4ce1e69b50cdc4abfbc6e9..d8039a1d46da98aba5ade2c4799050666dc762cd 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 #include +/* + * 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_pan(void *__unused); 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 88bbe364b6ae005cbec57a7394b6198e6c2df19c..8546afc831c1134035c2f40beee1c745c720ed7b 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -246,20 +246,39 @@ #include -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 @@ asm( * 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 9311547f939d5d3fd04a1dafb014c5bdc117b4f6..6c35d2151123d43ca9c944be7d64c961ad86c3dc 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -65,16 +66,20 @@ struct exception_table_entry 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) { 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 @@ static inline void set_fs(mm_segment_t fs) * 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 @@ static inline void set_fs(mm_segment_t fs) */ #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) \ @@ -235,6 +250,26 @@ static inline void uaccess_enable_not_uao(void) __uaccess_enable(ARM64_ALT_PAN_NOT_UAO); } +/* + * 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()" @@ -287,30 +322,35 @@ do { \ (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_unaligned __get_user - -#define get_user(x, ptr) \ +#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); \ + int __gu_err = 0; \ + __get_user_check((x), (ptr), __gu_err); \ + __gu_err; \ }) +#define __get_user_unaligned __get_user + +#define get_user __get_user + #define __put_user_asm(instr, alt_instr, reg, x, addr, err, feature) \ asm volatile( \ "1:"ALTERNATIVE(instr " " reg "1, [%2]\n", \ @@ -353,47 +393,51 @@ do { \ 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_unaligned __put_user - -#define put_user(x, ptr) \ +#define __put_user(x, ptr) \ ({ \ - __typeof__(*(ptr)) __user *__p = (ptr); \ - might_fault(); \ - access_ok(VERIFY_WRITE, __p, sizeof(*__p)) ? \ - __put_user((x), __p) : \ - -EFAULT; \ + int __pu_err = 0; \ + __put_user_check((x), (ptr), __pu_err); \ + __pu_err; \ }) +#define __put_user_unaligned __put_user + +#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 @@ static inline unsigned long __must_check copy_to_user(void __user *to, const voi 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/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c index dd918d0791d476ddefdde1a6f14e696b5b34ea3e..ca1cf2d2b4931fd4bbe7c19b906338d453f61871 100644 --- a/arch/arm64/kernel/arm64ksyms.c +++ b/arch/arm64/kernel/arm64ksyms.c @@ -38,8 +38,8 @@ EXPORT_SYMBOL(clear_page); /* 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 dec95bd82e319e9fe3e701d602d532f1b2508227..746a2032d473934f5afe459eddb86d74cd1d8461 100644 --- a/arch/arm64/kernel/bpi.S +++ b/arch/arm64/kernel/bpi.S @@ -17,6 +17,7 @@ */ #include +#include .macro ventry target .rept 31 @@ -53,6 +54,7 @@ ENTRY(__bp_harden_hyp_vecs_start) 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 @@ ENTRY(__psci_hyp_bp_inval_start) 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 65f42d2574142d4b37bebf49ed1fc3cdccbb56ae..f736a6f81ecdfab99efad58f3e6f9845851183fc 100644 --- a/arch/arm64/kernel/cpu-reset.S +++ b/arch/arm64/kernel/cpu-reset.S @@ -16,7 +16,7 @@ #include .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 7c8e1855f2c8d78dd881f3977c0cb626d024f08d..49e548fd4b1952a942725eacfc52689bd6af111c 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -54,11 +54,15 @@ DEFINE_PER_CPU_READ_MOSTLY(struct bp_hardening_data, bp_hardening_data); #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 @@ static void __install_bp_hardening_cb(bp_hardening_cb_t fn, #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 @@ static void install_bp_hardening_cb(const struct arm64_cpu_capabilities *entry, __install_bp_hardening_cb(fn, hyp_vecs_start, hyp_vecs_end); } +#include +#include #include +#ifdef CONFIG_PSCI_BP_HARDENING static int enable_psci_bp_hardening(void *data) { const struct arm64_cpu_capabilities *entry = data; @@ -135,6 +146,59 @@ static int enable_psci_bp_hardening(void *data) (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 @@ const struct arm64_cpu_capabilities arm64_errata[] = { { .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 @@ void verify_local_cpu_errata_workarounds(void) { 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 5cf4c649d67ebe7499fe7a9ab29efd06b397512a..675bf45fc0f94b688be5667f96401d70afa8e202 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_aa64isar0[] = { }; 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 int __kpti_forced; /* 0: not forced, >0: forced on, <0: forced off */ 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 @@ static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry, 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 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .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 @@ static void __init setup_elf_hwcaps(const struct arm64_cpu_capabilities *hwcaps) 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 @@ verify_local_elf_hwcaps(const struct arm64_cpu_capabilities *caps) } 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 @@ verify_local_cpu_features(const struct arm64_cpu_capabilities *caps) * 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 @@ static void __init setup_feature_capabilities(void) 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 f8ba35d6baa0aa82695b5e7ee663af98b17c1c0c..7613ed1256971a6f07d3161562736b88c791258b 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -30,11 +30,13 @@ #include #include #include +#include #include #include #include #include #include +#include /* * Context tracking subsystem. Used to instrument transitions @@ -125,10 +127,10 @@ alternative_else_nop_endif .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 @@ el0_ia: * 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 @@ el0_sp_pc: * 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 @@ el0_irq_naked: #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 @@ el0_svc_naked: // compat entry point 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 @@ __ni_sys_trace: 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 @@ __ni_sys_trace: .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 c18658690993610a76f4f74917e1b3007f6bebce..d479185d29c96faca5ee7e51d1e68aee3811d89e 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S @@ -474,7 +474,7 @@ ENDPROC(__primary_switched) * 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.lds b/arch/arm64/kernel/module.lds index f7c9781a9d48b48396e61375da2e10f31c0b6296..eacb5c67f61e97d318631d0a129bb7150fa021a9 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 409abc45bdb62f2b8dc69d82814ce7e6f00c8db7..1b3eb67edefbacb5d317c23ea6303da6e15d89da 100644 --- a/arch/arm64/kernel/pci.c +++ b/arch/arm64/kernel/pci.c @@ -175,8 +175,10 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) 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 52710f11d473ad0d4c5924c959ab2a48c4bca506..e0b731e70f66c6dda6046cefacf7ec55ad46842f 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c @@ -869,15 +869,23 @@ static int armv8pmu_set_event_filter(struct hw_perf_event *event, { 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 ee0ea179df1dbbf7b6daf273b572a358f4fbc3ce..08ca9dc390e134fd10596586d6baf0038e16b4f4 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -213,7 +213,7 @@ static void show_data(unsigned long addr, int nbytes, const char *name) 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 df67652e46f0de3fcfcfa18dbf5d69c7a97815a1..5a4fbcc744d2c88d642ab2277c0471f9d73533b8 100644 --- a/arch/arm64/kernel/sleep.S +++ b/arch/arm64/kernel/sleep.S @@ -95,7 +95,7 @@ ENTRY(__cpu_suspend_enter) 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/traps.c b/arch/arm64/kernel/traps.c index fcd2b33e00cfc6ef7600bb2eaad41b337e54a4b5..bfaead19708343fb056ea18a4f56b90408199841 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -53,7 +53,7 @@ static const char *handler[]= { "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 62c84f7cb01b99e0105abef1d3bd3252249fc74c..88fef38678862f08aa3fe11b110e9fe61edb00dd 100644 --- a/arch/arm64/kernel/vdso/Makefile +++ b/arch/arm64/kernel/vdso/Makefile @@ -14,6 +14,7 @@ obj-vdso := $(addprefix $(obj)/, $(obj-vdso)) 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/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S index 34d3ed64fe8ed91a5e2955c7d404e9e170089bac..402a62165d40bc433b1fec2ff664ddd898f39ccf 100644 --- a/arch/arm64/kernel/vmlinux.lds.S +++ b/arch/arm64/kernel/vmlinux.lds.S @@ -60,7 +60,7 @@ jiffies = jiffies_64; #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 @@ SECTIONS . = 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/handle_exit.c b/arch/arm64/kvm/handle_exit.c index 2e6e9e99977b87ff578f6309e5e04dc40954d0ef..efe43c5f2dc1387a14827054d0bb7317b237fc6e 100644 --- a/arch/arm64/kvm/handle_exit.c +++ b/arch/arm64/kvm/handle_exit.c @@ -22,12 +22,15 @@ #include #include +#include + #include #include #include #include #include -#include +#include +#include #define CREATE_TRACE_POINTS #include "trace.h" @@ -42,7 +45,7 @@ static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run) 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_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run) 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 48b03547a9693614d58f3e1575d69330c80763fd..c470a7c5f05bfed54a4fa90ff0c569cf5ca94582 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 4e92399f71054347e064cab9f9610dc64bf4f86f..4e9d50c3e658b46ae2b0b6f4aacd717ed895b5b5 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 . */ +#include #include #include @@ -79,10 +80,11 @@ alternative_endif 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 @@ alternative_endif 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 3eab6ac18d7d33c1f841fa8474391bf46db5e904..849ee8a1752546c6d3bd07241f833e2608c7789e 100644 --- a/arch/arm64/kvm/hyp/switch.c +++ b/arch/arm64/kvm/hyp/switch.c @@ -19,6 +19,8 @@ #include #include +#include + #include #include #include @@ -311,14 +313,16 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu) 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; - - vcpu_set_reg(vcpu, 0, val); - goto again; + kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_HVC32)) { + u32 val = vcpu_get_reg(vcpu, 0); + + 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 @@ void __hyp_text __noreturn __hyp_panic(void) 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 07c7ad97ee281b1edb2a320d031a174bc65627a7..b581e16320dd8adfc638b05766a5af5a91e0d53d 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 @@ uao_user_alternative 9f, strb, sttrb, wzr, x0, 0 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 e8bfaf19f778f85644df8a3b39bd59aca0e3648d..800779eb3079dfafea890f272f64492e801c8986 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 2b8950ed5e720c8efc26ac5eba11979894b48eda..6befc9cc95a827ffd084558784d578535c823719 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -343,7 +343,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr, 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 @@ asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr, 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 @@ asmlinkage void __exception do_sp_pc_abort(unsigned long addr, 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 @@ asmlinkage int __exception do_debug_exception(unsigned long addr, 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 01c171723bb33ba9660246778e703d66a82e1641..caf75abf6ae746377130537ae38a6aeed1d78259 100644 --- a/arch/arm64/mm/mmap.c +++ b/arch/arm64/mm/mmap.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -102,12 +103,18 @@ void arch_pick_mmap_layout(struct mm_struct *mm) */ 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 d8cdb169892d8d0ca393546607341c60f9c0ba97..6e989ebdb9cae9099def5cf59f2d2ba434370b21 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -466,7 +466,7 @@ static int __init map_entry_trampoline(void) { 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 @@ int pmd_clear_huge(pmd_t *pmd) 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 2e69a14f65c23f1e3cf20031e33f31a96aad76ae..3a9af60a426d2a49cc10ac858d23a45ae705b9ae 100644 --- a/arch/arm64/mm/proc.S +++ b/arch/arm64/mm/proc.S @@ -132,7 +132,7 @@ ENDPROC(cpu_do_suspend) * * 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 @@ ENTRY(cpu_do_switch_mm) 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 @@ ENTRY(idmap_cpu_replace_ttbr1) 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 @@ ENTRY(idmap_cpu_replace_ttbr1) 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 d8199e12fb6ef906087a0c071c8669553b3ee6fa..b47a26f4290cfc804e97f15d90d98b709cde2551 100644 --- a/arch/arm64/net/bpf_jit_comp.c +++ b/arch/arm64/net/bpf_jit_comp.c @@ -234,8 +234,9 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx) 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 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx) */ 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/timex.h b/arch/frv/include/asm/timex.h index a89bddefdacf9194373a201c6c6c4cf8c4ac87c0..139093fab3260debefb4da2fbd33e37778784298 100644 --- a/arch/frv/include/asm/timex.h +++ b/arch/frv/include/asm/timex.h @@ -16,5 +16,11 @@ static inline cycles_t get_cycles(void) #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/ia64/kernel/module.c b/arch/ia64/kernel/module.c index 6ab0ae7d6535db4d9bf8950eda5c4c980f65bb82..d1d945c6bd05f05d567be65357210210bfaae48e 100644 --- a/arch/ia64/kernel/module.c +++ b/arch/ia64/kernel/module.c @@ -153,7 +153,7 @@ slot (const struct insn *insn) 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 @@ apply_imm64 (struct module *mod, struct insn *insn, uint64_t val) 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/mips/ath25/board.c b/arch/mips/ath25/board.c index 9ab48ff80c1c8de3a5de0050e2d4d5f22dc0c773..6d11ae581ea775bc2e919e16bdd63cdb6b06d86f 100644 --- a/arch/mips/ath25/board.c +++ b/arch/mips/ath25/board.c @@ -135,6 +135,8 @@ int __init ath25_find_config(phys_addr_t base, unsigned long size) } 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 c1eb1ff7c80017f53e545229fdd6acbb1c91969d..6ed1ded87b8f644ba87eadf5e7a2e8e29976d9e6 100644 --- a/arch/mips/cavium-octeon/octeon-irq.c +++ b/arch/mips/cavium-octeon/octeon-irq.c @@ -2277,6 +2277,8 @@ static int __init octeon_irq_init_cib(struct device_node *ciu_node, } 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/kprobes.h b/arch/mips/include/asm/kprobes.h index daba1f9a4f7939070dd0a75c00ac67979055737e..174aedce3167ae783816a230c0487c725f04c757 100644 --- a/arch/mips/include/asm/kprobes.h +++ b/arch/mips/include/asm/kprobes.h @@ -40,7 +40,8 @@ typedef union mips_instruction kprobe_opcode_t; #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 d21f3da7bdb619402a438b923fda454b7525d204..c0be540e83cb32e4d24ebaa448d2bdab2a6215c4 100644 --- a/arch/mips/include/asm/pgtable-32.h +++ b/arch/mips/include/asm/pgtable-32.h @@ -18,6 +18,10 @@ #include +#ifdef CONFIG_HIGHMEM +#include +#endif + extern int temp_tlb_entry; /* @@ -61,7 +65,8 @@ extern int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1, #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 89fa5c0b1579cf63ecdaf7bdf097214183a979a5..c92f4c28db7fe591baff834b2c3c072f55b6b6a3 100644 --- a/arch/mips/include/asm/uaccess.h +++ b/arch/mips/include/asm/uaccess.h @@ -1257,6 +1257,13 @@ __clear_user(void __user *addr, __kernel_size_t size) { __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 @@ __clear_user(void __user *addr, __kernel_size_t size) "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 @@ __clear_user(void __user *addr, __kernel_size_t size) "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 d8227f289d7f64cfaf117f0ce6bb3af52b6f02c4..9a4aed652736d53847d1a16c311257744ff268bf 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 @@ int mipsr2_decoder(struct pt_regs *regs, u32 inst, unsigned long *fcr31) } 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 @@ static int mipsr2_stats_clear_show(struct seq_file *s, void *unused) __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 47c9646f93b32049093e49e41c21537a9455c78d..d4a293b68249b3cfefe4466b606e3c7847980e89 100644 --- a/arch/mips/kernel/smp-bmips.c +++ b/arch/mips/kernel/smp-bmips.c @@ -166,11 +166,11 @@ static void bmips_prepare_cpus(unsigned int max_cpus) 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 0344e575f522982dc4725479d66544d79837b0b4..fba4ca56e46a39a88d610e504bdb126388e0a2b0 100644 --- a/arch/mips/lib/Makefile +++ b/arch/mips/lib/Makefile @@ -15,4 +15,5 @@ obj-$(CONFIG_CPU_R3000) += r3k_dump_tlb.o 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 05909d58e2fe1c278e3f30a3d196e1c214724194..56ea0df60a443c378bfe6744593f178955baec57 100644 --- a/arch/mips/lib/libgcc.h +++ b/arch/mips/lib/libgcc.h @@ -9,10 +9,18 @@ typedef int word_type __attribute__ ((mode (__word__))); 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 @@ typedef union { 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 18a1ccd4d1348c0134ed9d755512b66d1f54779d..2b1bf93b5c80aeab2c5f766069f2f66b566f2aa1 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 0000000000000000000000000000000000000000..111ad475aa0cdd111ebe362af928414d9499f666 --- /dev/null +++ b/arch/mips/lib/multi3.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +#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 adc6911ba748915bda5b2575fc03b76891f1fb21..b19a3c506b1e9d203cbacb0da71513d8f21868b1 100644 --- a/arch/mips/mm/pgtable-32.c +++ b/arch/mips/mm/pgtable-32.c @@ -51,15 +51,15 @@ void __init pagetable_init(void) /* * 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 49a2e2226fee84f2a284373a427b5fe155bf261d..2486037391980738895587141c539996ada69f43 100644 --- a/arch/mips/net/bpf_jit.c +++ b/arch/mips/net/bpf_jit.c @@ -526,7 +526,8 @@ static void save_bpf_jit_regs(struct jit_ctx *ctx, unsigned offset) 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 @@ static void restore_bpf_jit_regs(struct jit_ctx *ctx, 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 @@ static void build_prologue(struct jit_ctx *ctx) 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 5d2e0c8d29c0bd0003bae3f7337edbcab5a403c4..88a2075305d1c1d71d09c7e3860367609b81ca04 100644 --- a/arch/mips/net/bpf_jit_asm.S +++ b/arch/mips/net/bpf_jit_asm.S @@ -90,18 +90,14 @@ FEXPORT(sk_load_half_positive) 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 @@ FEXPORT(sk_load_byte_positive) 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 @@ FEXPORT(sk_load_byte_positive) * (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 @@ FEXPORT(sk_load_byte_positive) 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 64543d66e76b50ccace33bf942e0b3c61c7e7fa4..e9531fea23a297adf2aac05d68ffd04dede6e0d4 100644 --- a/arch/mips/ralink/reset.c +++ b/arch/mips/ralink/reset.c @@ -96,16 +96,9 @@ static void ralink_restart(char *command) 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 1d8c24dc04d4357b7c59e151da11d578fff2fe90..88290d32b956aa45d519087913aa2c031a0db8d8 100644 --- a/arch/parisc/include/asm/cacheflush.h +++ b/arch/parisc/include/asm/cacheflush.h @@ -25,6 +25,7 @@ void flush_user_icache_range_asm(unsigned long, unsigned long); 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/kernel/cache.c b/arch/parisc/kernel/cache.c index df757c9675e670c0e6e158788189f002cbae6261..8a0822125f8b44fb10294d02d04246b425fa781c 100644 --- a/arch/parisc/kernel/cache.c +++ b/arch/parisc/kernel/cache.c @@ -464,10 +464,10 @@ EXPORT_SYMBOL(copy_user_page); 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 @@ void flush_cache_mm(struct mm_struct *mm) 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 @@ void flush_cache_mm(struct mm_struct *mm) 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 @@ void flush_cache_mm(struct mm_struct *mm) 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 @@ flush_user_icache_range(unsigned long start, unsigned long end) void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { - BUG_ON(!vma->vm_mm->context); - - /* Flush the TLB to avoid speculation if coherency is required. */ - if (parisc_requires_coherency()) - flush_tlb_range(vma, start, end); + pgd_t *pgd; + unsigned long addr; - 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 @@ flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long 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 700e2d2da0969cdfeb872071fe16b5f9c32e82cf..2e68ca1fe0dbce9ab7f9dbb596fb10a2beb0ad8f 100644 --- a/arch/parisc/kernel/drivers.c +++ b/arch/parisc/kernel/drivers.c @@ -648,6 +648,10 @@ static int match_pci_device(struct device *dev, int index, (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 2d40c4ff3f6918ae9b2e2c6af71e20658a9850e1..67b0f7532e835f4db1214c6ccecf62183eb84e50 100644 --- a/arch/parisc/kernel/pacache.S +++ b/arch/parisc/kernel/pacache.S @@ -1110,6 +1110,28 @@ ENTRY_CFI(flush_kernel_dcache_range_asm) .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 c0deafc212b8b9856bcb923a1bf07c439452d1e9..798ab37c9930019a80050a7fc2525109c0d2a651 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 2015b072422cb1d7c63e04f22631ebc48578179f..b4ab1f497335aefc1e3e00609ac78758ccbdbd02 100644 --- a/arch/powerpc/include/asm/code-patching.h +++ b/arch/powerpc/include/asm/code-patching.h @@ -30,6 +30,7 @@ int patch_branch(unsigned int *addr, unsigned long target, int flags); 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 ab68d0ee7725861d827d34c4f05ed08001e13274..4e54282c29b4459409402a38fd70d191bcc4bf9b 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -474,7 +474,8 @@ enum { 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/module.h b/arch/powerpc/include/asm/module.h index cd4ffd86765f35eeac01f0103307aa5ef174d0e5..bb9073a2b2ae711fa670072fda46debc9c7a31ca 100644 --- a/arch/powerpc/include/asm/module.h +++ b/arch/powerpc/include/asm/module.h @@ -14,6 +14,10 @@ #include +#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 e958b7096f19c5ea703816e935bd4ba434a370ad..9e5e0d910b91e89302013f2226c341e86528d3a4 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 56398e7e61004d7766f8e77627df54e73f7e75bf..71c69883125afeb65251fb92ecb648e23c48ccf0 100644 --- a/arch/powerpc/include/asm/page.h +++ b/arch/powerpc/include/asm/page.h @@ -132,7 +132,19 @@ extern long long virt_phys_offset; #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 78efe8d5d77574dbb75263bc31e826af08291f32..30f2d6d4c640d40105caede785364d75118a702e 100644 --- a/arch/powerpc/include/asm/synch.h +++ b/arch/powerpc/include/asm/synch.h @@ -5,10 +5,6 @@ #include #include -#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_pe.c b/arch/powerpc/kernel/eeh_pe.c index de7d091c4c31d4a76429ff0d58f20c73d31464e0..1abd8dd77ec134ff3d238c28a2251fed3a1396ae 100644 --- a/arch/powerpc/kernel/eeh_pe.c +++ b/arch/powerpc/kernel/eeh_pe.c @@ -795,7 +795,8 @@ static void eeh_restore_bridge_bars(struct eeh_dev *edev) 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 7614d1dd2c0b2143a3d7c7ba5502419e83bcc3f4..94b5dfb087e9e806ac5296a3f10a992c504e4a54 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -723,7 +723,7 @@ EXC_COMMON_BEGIN(bad_addr_slb) 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 028a22bfa90c2e6f5685b887e89e5ff099ef4a42..ad713f741ca8500721013c1d06e4159d0114a96e 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -372,6 +372,14 @@ void force_external_irq_replay(void) */ 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 183368e008cf362a3925b0b4e1c308e67a9cb2d7..99407cf12ad5b51925e423ca0056e6d7a04833d8 100644 --- a/arch/powerpc/kernel/module_64.c +++ b/arch/powerpc/kernel/module_64.c @@ -494,7 +494,17 @@ static bool is_early_mcount_callsite(u32 *instruction) 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/time.c b/arch/powerpc/kernel/time.c index f1d7e996e67313c315a3acb980a92dc7178a19a0..ab7b661b6da3a3aa1bb48e2873cf1c2c14f7d682 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -719,12 +719,20 @@ static int __init get_freq(char *name, int cells, unsigned long *val) 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 a587e8f4fd2648caf61535176ef5a1d03a2c79ae..4b4e927c4822c3cac691a8d84dadf843a1b5efba 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_host.c +++ b/arch/powerpc/kvm/book3s_64_mmu_host.c @@ -177,12 +177,15 @@ int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte, 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_pr.c b/arch/powerpc/kvm/book3s_pr.c index 826c541a12af12e4bb76a723103d3121769e1015..e0d88d0890aaa66c354f93f37d930044ad9ca9dc 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -627,7 +627,11 @@ int kvmppc_handle_pagefault(struct kvm_run *run, struct kvm_vcpu *vcpu, 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 02176fd52f84b85187f8a57a5e434bd56299a151..286a3b051ff6e261c2e40b3cea196724cdc959ed 100644 --- a/arch/powerpc/kvm/book3s_pr_papr.c +++ b/arch/powerpc/kvm/book3s_pr_papr.c @@ -50,7 +50,9 @@ static int kvmppc_h_pr_enter(struct kvm_vcpu *vcpu) 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 @@ static int kvmppc_h_pr_enter(struct kvm_vcpu *vcpu) 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 @@ static int kvmppc_h_pr_remove(struct kvm_vcpu *vcpu) 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 @@ static int kvmppc_h_pr_remove(struct kvm_vcpu *vcpu) ((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 @@ static int kvmppc_h_pr_bulk_remove(struct kvm_vcpu *vcpu) } 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 @@ static int kvmppc_h_pr_bulk_remove(struct kvm_vcpu *vcpu) 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 @@ static int kvmppc_h_pr_protect(struct kvm_vcpu *vcpu) 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 @@ static int kvmppc_h_pr_protect(struct kvm_vcpu *vcpu) 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 d5edbeb8eb8209e61a9d6524acfa565e3f30782f..753d591f1b52343cf40023bde72542a30fa87d2d 100644 --- a/arch/powerpc/lib/code-patching.c +++ b/arch/powerpc/lib/code-patching.c @@ -95,6 +95,11 @@ int instr_is_relative_branch(unsigned int instr) 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 e86bfa111f3c9774350bbdce4c08a3d530efa1d9..46c8338a61bc81187f33307a7ef6ef5b805ecdfb 100644 --- a/arch/powerpc/lib/feature-fixups.c +++ b/arch/powerpc/lib/feature-fixups.c @@ -55,7 +55,7 @@ static int patch_alt_instruction(unsigned int *src, unsigned int *dest, 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 d0b137d96df19e0e6f1b1258555f9ff359cd3fbd..9376e8e53bfaefbad4223e27f2d8018ad09d9b3e 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c @@ -294,7 +294,7 @@ int do_page_fault(struct pt_regs *regs, unsigned long address, * 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 a5d3ecdabc44464e995c9a785e515359d43d4217..035dfb65df4ba36e44ab7da54f68141fdda7c387 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -765,6 +765,24 @@ static int __init add_huge_page_size(unsigned long long size) 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 050badc0ebd3446f22677aea4d9e516d9b3801bd..0b50019505a5d7cd41d733b042c9e6121a3d9432 100644 --- a/arch/powerpc/mm/tlb_nohash.c +++ b/arch/powerpc/mm/tlb_nohash.c @@ -751,7 +751,7 @@ void setup_initial_memory_limit(phys_addr_t first_memblock_base, * 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 0fe98a567125aa7c339f5dfa537d278cda223868..be9d968244ad9ef1ad3894435dd8aa71b6189ffe 100644 --- a/arch/powerpc/net/bpf_jit_comp64.c +++ b/arch/powerpc/net/bpf_jit_comp64.c @@ -245,6 +245,7 @@ static void bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 * 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 85c85eb3e245d7a7a9600a8fce95a592789f38bc..b4abf9d5d9e15ea45e152297ed5d58b7aba10eb2 100644 --- a/arch/powerpc/platforms/cell/spufs/coredump.c +++ b/arch/powerpc/platforms/cell/spufs/coredump.c @@ -175,6 +175,8 @@ static int spufs_arch_write_note(struct spu_context *ctx, int i, 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 9db4398ded5de1f6c8e9a672a51008df1fbc85d7..1bceb95f422d0f828017128580695c0d4c87ba47 100644 --- a/arch/powerpc/platforms/powernv/opal-nvram.c +++ b/arch/powerpc/platforms/powernv/opal-nvram.c @@ -11,6 +11,7 @@ #define DEBUG +#include #include #include #include @@ -56,9 +57,17 @@ static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index) 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) { + msleep(OPAL_BUSY_DELAY_MS); opal_poll_events(NULL); + } else if (rc == OPAL_BUSY) { + msleep(OPAL_BUSY_DELAY_MS); + } } + + if (rc) + return -EIO; + *index += count; return count; } diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c index 66e7227469b8c279c4ff392be4e9a0ec37c66e8c..85018a14217a37a4331af0018f2c10e7b8ab651f 100644 --- a/arch/powerpc/platforms/pseries/cmm.c +++ b/arch/powerpc/platforms/pseries/cmm.c @@ -708,7 +708,7 @@ static void cmm_exit(void) * 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 3e828b20c21eb8d9d3fb813e1e84370a1163ad5a..2842f9d63d215b036b9f4588d7ae1656d3ce95c6 100644 --- a/arch/powerpc/sysdev/mpc8xx_pic.c +++ b/arch/powerpc/sysdev/mpc8xx_pic.c @@ -79,7 +79,7 @@ unsigned int mpc8xx_get_irq(void) 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/hypfs/inode.c b/arch/s390/hypfs/inode.c index 09bccb224d03c7fcf91a80fd44a6564e65297b4d..2a17123130d304eb7316cfc9e07306316f8d7b2b 100644 --- a/arch/s390/hypfs/inode.c +++ b/arch/s390/hypfs/inode.c @@ -318,7 +318,7 @@ static void hypfs_kill_super(struct super_block *sb) 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/kernel/early.c b/arch/s390/kernel/early.c index 29d87444a655ec82756339e8f1fb94ea27ac2adf..62578989c74d3c8ef0067a84a31c901e8e0e90a5 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -372,7 +372,7 @@ static int __init topology_setup(char *str) 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/ipl.c b/arch/s390/kernel/ipl.c index 295bfb7124bcac7a55b71fedb0e9f55a96b7e1ad..39127b691b7829813bda1a713d00e31cee2af441 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -798,6 +798,7 @@ static ssize_t reipl_generic_loadparm_store(struct ipl_parameter_block *ipb, /* 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/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index 3667d20e997f3ccac943438ad2e03588795afb33..115bda280d50f89cd9512b57c79fca46b6f25f4a 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S @@ -31,8 +31,14 @@ SECTIONS { . = 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 diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 5ba494ed18c1b874b899b8cde676e8a4ad5ecf2c..a70ff09b498259b8884547904b740453f05804f9 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -1601,6 +1601,7 @@ static void sca_add_vcpu(struct kvm_vcpu *vcpu) /* 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) { diff --git a/arch/sh/boards/mach-se/770x/setup.c b/arch/sh/boards/mach-se/770x/setup.c index 658326f44df819eaba9d7d20dbe888d28b796d1f..5e0267624d8dfcf369ea3614b60b58cbf8ab5476 100644 --- a/arch/sh/boards/mach-se/770x/setup.c +++ b/arch/sh/boards/mach-se/770x/setup.c @@ -8,6 +8,7 @@ */ #include #include +#include #include #include #include @@ -114,6 +115,11 @@ static struct platform_device heartbeat_device = { #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 @@ static struct platform_device sh_eth0_device = { .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 @@ static struct platform_device sh_eth1_device = { .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/sparc/kernel/ldc.c b/arch/sparc/kernel/ldc.c index 59d5038664317d962a73ea2ee9d6133bb5d44904..9cc600b2d68ced3bf7817f0edf6c498f8eb1e67f 100644 --- a/arch/sparc/kernel/ldc.c +++ b/arch/sparc/kernel/ldc.c @@ -1733,9 +1733,14 @@ static int read_nonraw(struct ldc_channel *lp, void *buf, unsigned int size) 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/um/os-Linux/file.c b/arch/um/os-Linux/file.c index 2db18cbbb0eaba83aba45bd809ba4ed0834e80b1..c0197097c86e5075c146ad17142895e70b9fe25c 100644 --- a/arch/um/os-Linux/file.c +++ b/arch/um/os-Linux/file.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index a86d7cc2c2d82fa7d0e5c13d1f3188efcd660fba..bf0acb8aad8b20e31a4591ec5f1889d38d68d010 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c @@ -16,6 +16,7 @@ #include #include #include +#include void (*sig_info[NSIG])(int, struct siginfo *, struct uml_pt_regs *) = { [SIGTRAP] = relay_signal, @@ -159,7 +160,7 @@ static void (*handlers[_NSIG])(int sig, struct siginfo *si, mcontext_t *mc) = { 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 ea24114a92ae83814c64d953705148972537a9ed..93b170ea2480522b7e1fbc640b2347ff36e5fd19 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -193,6 +193,15 @@ KBUILD_CFLAGS += $(cfi) $(cfi-sigframe) $(cfi-sections) $(asinstr) $(avx_instr) 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 @@ KBUILD_AFLAGS += $(mflags-y) # 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/error.h b/arch/x86/boot/compressed/error.h index 2e59dac07f9e051ad34e1061c3ae7b9a8e111480..d732e608e3af63e8f9f73210377509c24a56b38b 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 + 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 6de58f1bd7ec27e9dfcba04d062e524d4c85052b..eb5ff66b4d7a60371316c89068d9506770fb6243 100644 --- a/arch/x86/boot/compressed/kaslr.c +++ b/arch/x86/boot/compressed/kaslr.c @@ -460,10 +460,17 @@ void choose_random_location(unsigned long input, 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 59559542f3b7b05341e411a60aa8e92367bbed4c..488409875ea7a7278024439d4b81fc24cb7a3548 100644 --- a/arch/x86/boot/compressed/misc.c +++ b/arch/x86/boot/compressed/misc.c @@ -299,6 +299,10 @@ static void parse_elf(void *output) 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 0000000000000000000000000000000000000000..43c95758a9146fd7013a3227d7cc6ac3d972c9cb --- /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" +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 8648158f39166f1cf1603fc7f139af629e163bde..f8fe11d24cde5448b0fda463f9a70d5645027804 100644 --- a/arch/x86/crypto/cast5_avx_glue.c +++ b/arch/x86/crypto/cast5_avx_glue.c @@ -66,8 +66,6 @@ static int ecb_crypt(struct blkcipher_desc *desc, struct blkcipher_walk *walk, 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 @@ static int ecb_crypt(struct blkcipher_desc *desc, struct blkcipher_walk *walk, /* 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 f5434b4670c1182b9c877f1adab60e42bcee933d..a76dc738ec614b91e821fd307a3ace45b9fdce28 100644 --- a/arch/x86/entry/entry_32.S +++ b/arch/x86/entry/entry_32.S @@ -237,8 +237,7 @@ ENTRY(__switch_to_asm) * 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 8be2aaa5b8ec609ad683b62afe041e68962d66b4..3a31fd40058ac9ac550dc51cdff8fd8f528c07a1 100644 --- a/arch/x86/entry/entry_64.S +++ b/arch/x86/entry/entry_64.S @@ -176,13 +176,26 @@ GLOBAL(entry_SYSCALL_64_after_swapgs) 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 @@ ENTRY(__switch_to_asm) * 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 @@ apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \ #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/events/intel/core.c b/arch/x86/events/intel/core.c index 0bd0c1cc322893503a03b05616e309c5e1d4e865..6f353a87417816fb0b35c78b377f0fa4dc041c1a 100644 --- a/arch/x86/events/intel/core.c +++ b/arch/x86/events/intel/core.c @@ -3025,7 +3025,7 @@ static unsigned bdw_limit_period(struct perf_event *event, unsigned left) X86_CONFIG(.event=0xc0, .umask=0x01)) { if (left < 128) left = 128; - left &= ~0x3fu; + left &= ~0x3fULL; } return left; } diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c index afe8024e9e95e6a2326bcfcf4c5e6aa964fd7e48..6bc36944a8c1641566334f806cfe56a0a7b0cdd8 100644 --- a/arch/x86/events/intel/uncore_snbep.c +++ b/arch/x86/events/intel/uncore_snbep.c @@ -3522,24 +3522,27 @@ static struct intel_uncore_type *skx_msr_uncores[] = { 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 intel_uncore_type skx_uncore_imc = { }; 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/include/asm/apm.h b/arch/x86/include/asm/apm.h index 93eebc636c7616395ffa5b55f81c33e9b35dbb71..46e40aeae44637ddcb3c487546cbaee97adce880 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 + #ifdef APM_ZERO_SEGS # define APM_DO_ZERO_SEGS \ "pushl %%ds\n\t" \ @@ -31,6 +33,7 @@ static inline void apm_bios_call_asm(u32 func, u32 ebx_in, u32 ecx_in, * 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 @@ static inline void apm_bios_call_asm(u32 func, u32 ebx_in, u32 ecx_in, "=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 @@ static inline bool apm_bios_call_simple_asm(u32 func, u32 ebx_in, * 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 @@ static inline bool apm_bios_call_simple_asm(u32 func, u32 ebx_in, "=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 166654218329e0f69ba0ffd6df83adc5de3f72c9..5a25ada75aeb6803e37533b6097ebc24b1c29530 100644 --- a/arch/x86/include/asm/asm-prototypes.h +++ b/arch/x86/include/asm/asm-prototypes.h @@ -37,7 +37,4 @@ INDIRECT_THUNK(dx) 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 08684b3595d7c544c2565981d0b80289cbc3f85b..8d8c24f3a963d0889a89b5f182bfd3c1e900fcdf 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 8eb23f5cf7f4efb1dcdf991ac30dd666e866f8ca..a2485311164b3b5d859b42c6ab0e785f8e3c96aa 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -203,6 +203,7 @@ #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 */ /* Virtualization flags: Linux defined, word 8 */ #define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */ @@ -301,6 +302,7 @@ /* 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) */ diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 389d700b961e48028a1a7b1797c41aa2fb339bf7..9df22bb07f7fd7450e25fbad417a049ddff35e57 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -5,6 +5,7 @@ #include #include #include +#include /* * 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 @@ struct efi_scratch { 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 @@ struct efi_scratch { __flush_tlb_all(); \ } \ \ + firmware_restrict_branch_speculation_end(); \ __kernel_fpu_end(); \ preempt_enable(); \ }) diff --git a/arch/x86/include/asm/mmu.h b/arch/x86/include/asm/mmu.h index 8b272a08d1a845a48796b24dd82c8e93ce9bc549..e2e09347ee3c9f1edc000a54b56d4969f50ce09c 100644 --- a/arch/x86/include/asm/mmu.h +++ b/arch/x86/include/asm/mmu.h @@ -3,12 +3,18 @@ #include #include +#include /* - * 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 @@ typedef struct { #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 d23e35584f157f3fc748fbe3e01e33f8ae6650a1..5a295bb97103439f74d8fa49b20a07a4f1abfc0e 100644 --- a/arch/x86/include/asm/mmu_context.h +++ b/arch/x86/include/asm/mmu_context.h @@ -12,6 +12,9 @@ #include #include #include + +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,6 +109,8 @@ static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) 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 */ diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h index 76b058533e473b10d99e7a1ee4b661b5b2b2b14d..f928ad9b143fedea1085dedc508658fa745b4ceb 100644 --- a/arch/x86/include/asm/nospec-branch.h +++ b/arch/x86/include/asm/nospec-branch.h @@ -8,6 +8,50 @@ #include #include +/* + * 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 ") 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__ /* @@ -23,6 +67,18 @@ .popsection .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 @@ -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" \ @@ -156,25 +229,90 @@ extern char __indirect_thunk_end[]; 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 } +#define alternative_msr_write(_msr, _val, _feature) \ + asm volatile(ALTERNATIVE("", \ + "movl %[msr], %%ecx\n\t" \ + "movl %[val], %%eax\n\t" \ + "movl $0, %%edx\n\t" \ + "wrmsr", \ + _feature) \ + : : [msr] "i" (_msr), [val] "i" (_val) \ + : "eax", "ecx", "edx", "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"); + alternative_msr_write(MSR_IA32_PRED_CMD, PRED_CMD_IBPB, + X86_FEATURE_USE_IBPB); } +/* + * 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 { \ + preempt_disable(); \ + alternative_msr_write(MSR_IA32_SPEC_CTRL, SPEC_CTRL_IBRS, \ + X86_FEATURE_USE_IBRS_FW); \ +} while (0) + +#define firmware_restrict_branch_speculation_end() \ +do { \ + alternative_msr_write(MSR_IA32_SPEC_CTRL, 0, \ + 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 ce932812f142a731cc4b9e082899b59b07b7a850..24af8b1de4386c3d37bb13a42c8256b9f0093c5c 100644 --- a/arch/x86/include/asm/paravirt.h +++ b/arch/x86/include/asm/paravirt.h @@ -6,6 +6,7 @@ #ifdef CONFIG_PARAVIRT #include #include +#include #include @@ -869,23 +870,27 @@ extern void default_banner(void); #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 @@ extern void default_banner(void); */ #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 @@ extern void default_banner(void); #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 bc5902c9240b3b6001242bd36ee933de8797fde1..98dbad6bd2f44e25a25bbe44bace158ae5dc5ea4 100644 --- a/arch/x86/include/asm/paravirt_types.h +++ b/arch/x86/include/asm/paravirt_types.h @@ -42,6 +42,7 @@ #include #include #include +#include struct page; struct thread_struct; @@ -391,7 +392,9 @@ int paravirt_disable_iospace(void); * 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/reboot.h b/arch/x86/include/asm/reboot.h index 2cb1cc253d51ebb5828e89f63b63462396050970..fc62ba8dce933d3bb1ec0262b7d4851bcc5fee26 100644 --- a/arch/x86/include/asm/reboot.h +++ b/arch/x86/include/asm/reboot.h @@ -15,6 +15,7 @@ struct machine_ops { }; 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/tlbflush.h b/arch/x86/include/asm/tlbflush.h index 94146f665a3ce9f251425c6b6c859393ce365653..99185a0649783c12c09ef69c1505dadcf9df36cc 100644 --- a/arch/x86/include/asm/tlbflush.h +++ b/arch/x86/include/asm/tlbflush.h @@ -68,6 +68,8 @@ static inline void invpcid_flush_all_nonglobals(void) 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 diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h index 6899cf187ba275fb61b3c504a007decc0c0494de..9cbfbef6a115766181222244e12f8311694399f6 100644 --- a/arch/x86/include/asm/vmx.h +++ b/arch/x86/include/asm/vmx.h @@ -309,6 +309,7 @@ enum vmcs_field { #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/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c index b5229abd1629f163f14bbf30ef9e871691d505aa..4922ab66fd290c72dfe05774e9b8729e25d1776d 100644 --- a/arch/x86/kernel/apic/vector.c +++ b/arch/x86/kernel/apic/vector.c @@ -93,8 +93,12 @@ static struct apic_chip_data *alloc_apic_chip_data(int node) 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 @@ static void x86_vector_free_irqs(struct irq_domain *domain, 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 @@ static int x86_vector_alloc_irqs(struct irq_domain *domain, unsigned int virq, 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/bugs.c b/arch/x86/kernel/cpu/bugs.c index baddc9ed345477515d4c34e77565176338710e78..b8b0b6e7837125fe01aa470c0ee14331835bbb16 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c @@ -299,6 +299,15 @@ static void __init spectre_v2_select_mitigation(void) 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 @@ -325,8 +334,9 @@ ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, c if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2)) return sprintf(buf, "Not affected\n"); - return sprintf(buf, "%s%s%s\n", spectre_v2_strings[spectre_v2_enabled], + 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()); } #endif diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index 6ed206bd9071177a353223810a968ebdb160e1b7..8fb1d6522f8e531687e411a2758ce4095ceb042a 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -64,7 +64,7 @@ void check_mpx_erratum(struct cpuinfo_x86 *c) /* * 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 @@ static const struct sku_microcode spectre_bad_microcodes[] = { { 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 @@ static bool bad_spectre_microcode(struct cpuinfo_x86 *c) { 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) diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index fe5cd6ea1f0e9638d93feff57992ee1941534b7d..7bbd50fa72ad55b0a5a6b56cdd1ddb87d86f8f22 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -48,6 +48,7 @@ #include #include #include +#include #include "mce-internal.h" @@ -61,6 +62,9 @@ static DEFINE_MUTEX(mce_chrdev_read_mutex); smp_load_acquire(&(p)); \ }) +/* sysfs synchronization */ +static DEFINE_MUTEX(mce_sysfs_mutex); + #define CREATE_TRACE_POINTS #include @@ -1078,9 +1082,22 @@ void do_machine_check(struct pt_regs *regs, long error_code) * 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,30 +1695,35 @@ static int __mcheck_cpu_ancient_init(struct cpuinfo_x86 *c) return 0; } -static void __mcheck_cpu_init_vendor(struct cpuinfo_x86 *c) +/* + * Init basic CPU features needed for early decoding of MCEs. + */ +static void __mcheck_cpu_init_early(struct cpuinfo_x86 *c) { - switch (c->x86_vendor) { - case X86_VENDOR_INTEL: - mce_intel_feature_init(c); - mce_adjust_timer = cmci_intel_adjust_timer; - break; - - case X86_VENDOR_AMD: { + 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); - /* - * 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); + } +} + +static void __mcheck_cpu_init_vendor(struct cpuinfo_x86 *c) +{ + switch (c->x86_vendor) { + case X86_VENDOR_INTEL: + mce_intel_feature_init(c); + mce_adjust_timer = cmci_intel_adjust_timer; + break; + case X86_VENDOR_AMD: { + mce_amd_feature_init(c); break; } @@ -1787,6 +1809,7 @@ void mcheck_cpu_init(struct cpuinfo_x86 *c) 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 @@ static ssize_t set_ignore_ce(struct device *s, 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 @@ static ssize_t set_ignore_ce(struct device *s, on_each_cpu(mce_enable_ce, (void *)1, 1); } } + mutex_unlock(&mce_sysfs_mutex); + return size; } @@ -2332,6 +2358,7 @@ static ssize_t set_cmci_disabled(struct device *s, 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 @@ static ssize_t set_cmci_disabled(struct device *s, on_each_cpu(mce_enable_ce, NULL, 1); } } + mutex_unlock(&mce_sysfs_mutex); + return size; } @@ -2350,8 +2379,19 @@ static ssize_t store_int_with_restart(struct device *s, 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/head_64.S b/arch/x86/kernel/head_64.S index 67cd7c1b99daac6bbe3a7cc03673e610a5720fd8..9d72cf547c8888ec9fe3f48cb330632f40b6bad5 100644 --- a/arch/x86/kernel/head_64.S +++ b/arch/x86/kernel/head_64.S @@ -22,6 +22,7 @@ #include #include "../entry/calling.h" #include +#include #ifdef CONFIG_PARAVIRT #include @@ -200,6 +201,7 @@ ENTRY(secondary_startup_64) /* 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 be22f5a2192e358153610ebdf95a823da1ea8e5f..4e3b8a587c882ee8a0e2c39678fde12738c7752c 100644 --- a/arch/x86/kernel/i8259.c +++ b/arch/x86/kernel/i8259.c @@ -418,6 +418,7 @@ struct legacy_pic default_legacy_pic = { }; 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 b55d07b9d5305ff2ba6bd9824237009969fe694e..91c48cdfe81f36679d38f63c1e50395d4cd71d0e 100644 --- a/arch/x86/kernel/kprobes/core.c +++ b/arch/x86/kernel/kprobes/core.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -199,6 +200,8 @@ int can_boost(kprobe_opcode_t *opcodes, void *addr) 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 @@ int __copy_instruction(u8 *dest, u8 *src) 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 @@ static int arch_copy_kprobe(struct kprobe *p) 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 dc20da1c78f09588ec8462217b8b7b818f9c75c5..fa671b90c37457db89e7644cf3f62150b01a0d9d 100644 --- a/arch/x86/kernel/kprobes/opt.c +++ b/arch/x86/kernel/kprobes/opt.c @@ -371,6 +371,7 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, } 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 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, 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_64.c b/arch/x86/kernel/machine_kexec_64.c index 8c1f218926d783ecb8d288f9fc340379695089a7..a5784a14f8d1bb3fb23ead6826b80b3b96ecafc9 100644 --- a/arch/x86/kernel/machine_kexec_64.c +++ b/arch/x86/kernel/machine_kexec_64.c @@ -524,6 +524,7 @@ int arch_kexec_apply_relocations_add(const Elf64_Ehdr *ehdr, 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 477ae806c2fa71f425ff56a8b75b1306dfcf7535..19977d2f97fb7ab2c94b9a0a5fcfe80de14a78b4 100644 --- a/arch/x86/kernel/module.c +++ b/arch/x86/kernel/module.c @@ -171,19 +171,28 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, 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 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, } 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/reboot.c b/arch/x86/kernel/reboot.c index ce020a69bba951774fff825b7e1042eb991148fa..03f21dbfaa9d871f45ebcd11e505ea443ae48362 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -769,10 +769,11 @@ void machine_crash_shutdown(struct pt_regs *regs) #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 2bbd27f8980217bc443ac4d202d6658328061a81..f9da471a77075eea47da67f17538e7a9e6996b78 100644 --- a/arch/x86/kernel/setup_percpu.c +++ b/arch/x86/kernel/setup_percpu.c @@ -287,4 +287,25 @@ void __init setup_per_cpu_areas(void) /* 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 ca699677e288d52063b73f6b342eeb6727109170..ea217caa731cf355d89f7c02a7ca37724ca8798c 100644 --- a/arch/x86/kernel/smp.c +++ b/arch/x86/kernel/smp.c @@ -33,6 +33,7 @@ #include #include #include +#include /* * Some notes on x86 processor bugs affecting SMP operation: @@ -162,6 +163,7 @@ static int smp_stop_nmi_callback(unsigned int val, struct pt_regs *regs) 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 @@ static int smp_stop_nmi_callback(unsigned int val, struct pt_regs *regs) 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/sys_x86_64.c b/arch/x86/kernel/sys_x86_64.c index 1119414ab419a6ec67f310d03b12502f24bc1503..1d4e7fd3e66dc673adf07fcc13e39ee82b30f97b 100644 --- a/arch/x86/kernel/sys_x86_64.c +++ b/arch/x86/kernel/sys_x86_64.c @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -100,7 +101,7 @@ SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len, 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 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, 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 322f433fbc76b362050e1f962aff6237cf2dcb46..f2142932ff0bbeebf16395453a13f3ac10b66357 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -526,7 +526,6 @@ do_general_protection(struct pt_regs *regs, long error_code) } 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 @@ dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code) 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 @@ dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code) 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 @@ void __init trap_init(void) 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 d07a9390023e0194f05e313a899df6f5a6cf0629..bbfb03eccb7f16bb23da347fc526b99eb260b4a0 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c @@ -366,6 +366,8 @@ static int __init tsc_setup(char *str) 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; } diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c index 8a1d635913995a9d879b6e5d3c82cc80355ec42c..961831bf74b123635d6b80962f08524cb43e7d30 100644 --- a/arch/x86/kernel/vm86_32.c +++ b/arch/x86/kernel/vm86_32.c @@ -719,7 +719,8 @@ void handle_vm86_fault(struct kernel_vm86_regs *regs, long error_code) 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/lapic.c b/arch/x86/kvm/lapic.c index b24b3c6d686ea6426dcef858167ae9ec0090aa88..5c3d416fff17d668b5efd1270d10321c95027e4f 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -1363,8 +1363,10 @@ EXPORT_SYMBOL_GPL(kvm_lapic_hv_timer_in_use); 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 be644afab1bbd55feccdbde5163a77596b8898e6..aaa93b4b0380413112912f557201bd256fe41079 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -1878,6 +1879,7 @@ static void svm_get_segment(struct kvm_vcpu *vcpu, */ if (var->unusable) var->db = 0; + /* This is symmetric with svm_set_segment() */ var->dpl = to_svm(vcpu)->vmcb->save.cpl; break; } @@ -2023,18 +2025,14 @@ static void svm_set_segment(struct kvm_vcpu *vcpu, 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 +2041,8 @@ static void svm_set_segment(struct kvm_vcpu *vcpu, * 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); } @@ -4919,7 +4918,7 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu) * being speculatively taken. */ if (svm->spec_ctrl) - wrmsrl(MSR_IA32_SPEC_CTRL, svm->spec_ctrl); + native_wrmsrl(MSR_IA32_SPEC_CTRL, svm->spec_ctrl); asm volatile ( "push %%" _ASM_BP "; \n\t" @@ -5028,11 +5027,11 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu) * 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); + native_wrmsrl(MSR_IA32_SPEC_CTRL, 0); /* Eliminate branch target predictions from guest mode */ vmexit_fill_RSB(); @@ -5448,6 +5447,12 @@ static inline void avic_post_state_restore(struct kvm_vcpu *vcpu) 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, @@ -5563,6 +5568,7 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = { .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 f2cd6bd69ae0b5876355c7a6191d0d66b95fed89..ff2030ffcb7f151ec6df94cc21516a072c87ec3d 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include "trace.h" @@ -1052,6 +1053,13 @@ static inline bool is_machine_check(u32 intr_info) (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; @@ -5732,7 +5740,7 @@ static int handle_exception(struct kvm_vcpu *vcpu) (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 @@ static bool nested_vmx_exit_handled_cr(struct kvm_vcpu *vcpu, { 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 @@ static bool nested_vmx_exit_handled_cr(struct kvm_vcpu *vcpu, * 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; @@ -8905,7 +8916,7 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu) * being speculatively taken. */ if (vmx->spec_ctrl) - wrmsrl(MSR_IA32_SPEC_CTRL, vmx->spec_ctrl); + native_wrmsrl(MSR_IA32_SPEC_CTRL, vmx->spec_ctrl); vmx->__launched = vmx->loaded_vmcs->launched; asm( @@ -9040,11 +9051,11 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu) * 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); + native_wrmsrl(MSR_IA32_SPEC_CTRL, 0); /* Eliminate branch target predictions from guest mode */ vmexit_fill_RSB(); @@ -10652,8 +10663,7 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, 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); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 4b19ec1da22df9f914d4338629cf6e73b392093b..3aaaf305420d9da42abbeb1a11406aa0069f744e 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -3070,7 +3070,8 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, 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 */ diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index 4ad7c4dd311c90e466a6cb63d687c4d855f4a395..6bf1898ddf49077618e14504da84b794f910415b 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -26,7 +26,6 @@ lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o 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 7e48807b2fa198c4e2b8e7022df2218308a41b7c..45a53dfe1859b1955fa91bad9bf1543903f1c1a8 100644 --- a/arch/x86/lib/csum-copy_64.S +++ b/arch/x86/lib/csum-copy_64.S @@ -55,7 +55,7 @@ ENTRY(csum_partial_copy_generic) 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 @@ ENTRY(csum_partial_copy_generic) /* 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 @@ ENTRY(csum_partial_copy_generic) source movq 32(%rdi), %r10 source - movq 40(%rdi), %rbp + movq 40(%rdi), %r15 source movq 48(%rdi), %r14 source @@ -103,7 +103,7 @@ ENTRY(csum_partial_copy_generic) 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 @@ ENTRY(csum_partial_copy_generic) dest movq %r10, 32(%rsi) dest - movq %rbp, 40(%rsi) + movq %r15, 40(%rsi) dest movq %r14, 48(%rsi) dest @@ -203,7 +203,7 @@ ENTRY(csum_partial_copy_generic) 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 480edc3a5e03002dd6f0a0316477cbd7b0971cc8..c909961e678a594bd3812cb14936bdf035af2bb9 100644 --- a/arch/x86/lib/retpoline.S +++ b/arch/x86/lib/retpoline.S @@ -7,7 +7,6 @@ #include #include #include -#include .macro THUNK reg .section .text.__x86.indirect_thunk @@ -47,58 +46,3 @@ GENERATE_THUNK(r13) 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 3d7f60fdb836c3a22aad1c64bbc57f179e3040ff..54efa85acab6ce8ead06bf9c55cee6f3ac9636c6 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -343,7 +343,7 @@ static noinline int vmalloc_fault(unsigned long address) 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 @@ static noinline int vmalloc_fault(unsigned long address) 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 @@ static noinline int vmalloc_fault(unsigned long address) 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 209b9465e97a9ad4583087afbe3cd0b508b1990d..b97ef29c940f3925c53de973b20adc0c0832b25f 100644 --- a/arch/x86/mm/pgtable.c +++ b/arch/x86/mm/pgtable.c @@ -643,4 +643,52 @@ int pmd_clear_huge(pmd_t *pmd) 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/tlb.c b/arch/x86/mm/tlb.c index 578973ade71b016c3537dc7a864a60753b5e36a7..eac92e2d171bd4b979389c672e8bd593c166e9b8 100644 --- a/arch/x86/mm/tlb.c +++ b/arch/x86/mm/tlb.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -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 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next, 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 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next, 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 7840331d30568665413d0a3319a98e26401ffd6f..cd97645208515e5ba8c685d374eca6d0adf8f0e0 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -12,6 +12,7 @@ #include #include #include +#include #include int bpf_jit_enable __read_mostly; @@ -281,7 +282,7 @@ static void emit_bpf_tail_call(u8 **pprog) 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 @@ static void emit_bpf_tail_call(u8 **pprog) */ 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 @@ static void emit_bpf_tail_call(u8 **pprog) * 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 @@ static void emit_bpf_tail_call(u8 **pprog) * 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 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) * 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 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) } } 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 28c04123b6ddaebce73e967644cf589a0b524ac2..18f9dad8dc793c07ba2f39c805b21fd9912d9322 100644 --- a/arch/x86/oprofile/nmi_int.c +++ b/arch/x86/oprofile/nmi_int.c @@ -472,7 +472,7 @@ static int nmi_setup(void) 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 @@ enum __force_cpu_type { 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 274dfc48184977db435a9c5c78607cd8a182067c..a0e85f2aff7d871bf443d202cb53a2a54b448efb 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -832,9 +832,11 @@ static void __init kexec_enter_virtual_mode(void) /* * 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 7850128f0026f2a045fa135173fe95963a927f14..834783bc6752602533570e3d819409ea2579bb21 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_power_off(void) 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 73eb7fd4aec48d02e367b87ec1326da6c4022bde..5b6c8486a0bece83fe9af6248537675d41f9a3ac 100644 --- a/arch/x86/tools/relocs.c +++ b/arch/x86/tools/relocs.c @@ -769,9 +769,12 @@ static int do_reloc64(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym, 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 1518d2805ae81733eb0629989f4d39ae595502c2..27361cbb7ca9bed47194fe4127e231a8d3c39c0d 100644 --- a/arch/x86/um/stub_segv.c +++ b/arch/x86/um/stub_segv.c @@ -6,11 +6,12 @@ #include #include #include +#include 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/smp.c b/arch/x86/xen/smp.c index 137afbbd05903baa700d9cce5d9707fac07f0062..a11540e51f62d3a32e20374258ae7714a5c7d229 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c @@ -299,35 +299,46 @@ static void __init xen_filter_cpu_maps(void) } -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 irqreturn_t xen_irq_work_interrupt(int irq, void *dev_id) } 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 @@ void __init xen_hvm_smp_init(void) 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 7f664c416faf55672f604c5b688f6b6ef32102a8..4ecd0de08557e8ae820f07b0f00dee59e82d1d4d 100644 --- a/arch/x86/xen/suspend.c +++ b/arch/x86/xen/suspend.c @@ -1,11 +1,14 @@ #include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -68,6 +71,8 @@ static void xen_pv_post_suspend(int suspend_cancelled) 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 @@ void xen_arch_post_suspend(int cancelled) 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_restore(void *data) 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/mm/init.c b/arch/xtensa/mm/init.c index 80e4cfb2471ad5af6a99433d6e8af76e16865558..d5abdf8878fecc9006bc5f68f0d683706befd808 100644 --- a/arch/xtensa/mm/init.c +++ b/arch/xtensa/mm/init.c @@ -77,19 +77,75 @@ void __init zones_init(void) 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 63f72f00c72eef45640d68fad4579fca271235ed..80dedde0de73df093123835d99dd37562a1dbba4 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -175,6 +175,9 @@ bool bio_integrity_enabled(struct bio *bio) 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 07f287b14cff42e330b491122ef6e660bc3b1b5f..4f93345c6a824eb18bf858b216f825dad1424c3b 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 diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index d673a69b61b41cf64c43522f035713d6cc8c78f0..a28ce591b28e6cf75871069742d59a54793f0d01 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -1079,10 +1079,8 @@ int blkcg_init_queue(struct request_queue *q) 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-mq.c b/block/blk-mq.c index 74ff73fa7b61b10cea4ec7621712a93425df4085..0668b66efb332e85af5fc91fd50b3f4329fad490 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -1265,13 +1265,13 @@ static blk_qc_t blk_mq_make_request(struct request_queue *q, struct bio *bio) 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 @@ static void blk_mq_exit_hctx(struct request_queue *q, { 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 @@ static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set, 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 @@ static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set, } } 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 a3ea8260c94c89236f938fb255158be8cb880a73..3a4c9a3c1427fc7802c5dee58d131ed6aa638b91 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -499,6 +499,17 @@ static void throtl_dequeue_tg(struct throtl_grp *tg) 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/partition-generic.c b/block/partition-generic.c index a2437c0066402786762c5636ec928c2cca112d95..298c05f8b5e38f11f0ee989c0a6cd13c101fa60b 100644 --- a/block/partition-generic.c +++ b/block/partition-generic.c @@ -321,8 +321,10 @@ struct hd_struct *add_partition(struct gendisk *disk, int partno, 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 5610cd537da78812e2633d76ca90e5c3fb66e7cc..7d8d50c11ce779b55d55984523cf1c2ae35cc5e1 100644 --- a/block/partitions/msdos.c +++ b/block/partitions/msdos.c @@ -300,7 +300,9 @@ static void parse_bsd(struct parsed_partitions *state, 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 0000000000000000000000000000000000000000..5a9656359f0bb1be70563719c099c3a7e4dd61af --- /dev/null +++ b/build.config.cuttlefish.x86_64 @@ -0,0 +1,15 @@ +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/clang/host/linux-x86/clang-4630689/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 +" diff --git a/crypto/ahash.c b/crypto/ahash.c index 14402ef6d8269cc8db32f87dd63009da4b9cb7a4..90d73a22f12917d44504b137ea52a3e26d30dabe 100644 --- a/crypto/ahash.c +++ b/crypto/ahash.c @@ -91,13 +91,14 @@ int crypto_hash_walk_done(struct crypto_hash_walk *walk, int err) 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 5a37962d21994c2f4ee55f31a40970cda1c4d439..df1bde273bba02ae7a68e1b37a4d5575b68c6913 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -261,7 +261,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, 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 4955eb66e361f45594a70c738b62fd6060ccdcc5..8525fe474abd6fbd60109d670b129114349491f6 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -93,9 +93,11 @@ int public_key_verify_signature(const struct public_key *pkey, 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 19d1afb9890f660e43ee95261cf0a703e44f92c6..09b1374dc6191b04b4c892b1cc0529c1e7a444ce 100644 --- a/crypto/asymmetric_keys/restrict.c +++ b/crypto/asymmetric_keys/restrict.c @@ -66,8 +66,9 @@ __setup("ca_keys=", ca_keys_setup); * * 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 @@ int restrict_link_by_signature(struct key *trust_keyring, 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 029f7051f2beae25286f4601948303408ed75849..ce2df8c9c583970be0177d2a50de85316fb1c2b2 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -102,6 +102,7 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) } } + 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 f83de99d7d71cf356d35a9efef65768f85f3f769..56bd612927ab1688d324fb056e9ff6d2a2f3e037 100644 --- a/crypto/async_tx/async_pq.c +++ b/crypto/async_tx/async_pq.c @@ -62,9 +62,6 @@ do_async_gen_syndrome(struct dma_chan *chan, 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 @@ do_async_gen_syndrome(struct dma_chan *chan, 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/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index c5557d070954c6a7bbf8799b0f2cc4ee593a14d4..94e04c9de12b4f8b485d744934666228dff6e518 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -87,8 +87,8 @@ MODULE_PARM_DESC(report_key_events, 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); @@ -2082,6 +2082,16 @@ int acpi_video_register(void) 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) + only_lcd = acpi_osi_is_win8(); + 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 13caebd679f5b07503941f1023cf468901aeec36..ce8fc680785b590cabaf4e63c285aced99e69007 100644 --- a/drivers/acpi/acpi_watchdog.c +++ b/drivers/acpi/acpi_watchdog.c @@ -74,10 +74,10 @@ void __init acpi_watchdog_init(void) 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 9179e9abe3db2e4a12f0596450985699c513836c..35a2d9ea065457dc7b200ca0c45906fe5f99b62c 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c @@ -180,6 +180,12 @@ acpi_status acpi_enable_event(u32 event, u32 flags) 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_status acpi_disable_event(u32 event, u32 flags) 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_status acpi_clear_event(u32 event) 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 db0e90342e82f5cb498b875ac62247a9d5ea6d02..ac2e8dfdf74eeec897e6b16a0cf1f5b99c0a81a8 100644 --- a/drivers/acpi/acpica/psobject.c +++ b/drivers/acpi/acpica/psobject.c @@ -121,6 +121,9 @@ static acpi_status acpi_ps_get_aml_opcode(struct acpi_walk_state *walk_state) (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 @@ static acpi_status acpi_ps_get_aml_opcode(struct acpi_walk_state *walk_state) 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 @@ acpi_ps_create_op(struct acpi_walk_state *walk_state, 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 e19f530f1083a13732328516925e3bbeb6493e14..f7c4301f7fa3f961c6cf7c92643268a350563e5d 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -556,7 +556,8 @@ static int acpi_button_remove(struct acpi_device *device) 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 @@ static int param_set_lid_init_state(const char *val, struct kernel_param *kp) 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 c3bcb7f5986e704c4b0bdc67956ae5b999702b17..7b665aaa6a570205c2ff18c1c0137bbb7553483d 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1518,7 +1518,7 @@ static int acpi_ec_setup(struct acpi_ec *ec, bool handle_events) } 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 @@ static const struct dev_pm_ops acpi_ec_pm = { 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 @@ static int param_set_event_clearing(const char *val, struct kernel_param *kp) 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 6c7dd7af789e453ce3d16ae80b849ef38bc4a3c0..dd70d6c2bca03eb227fe834fa14e33636f842d48 100644 --- a/drivers/acpi/ec_sys.c +++ b/drivers/acpi/ec_sys.c @@ -128,7 +128,7 @@ static int acpi_ec_add_debugfs(struct acpi_ec *ec, unsigned int ec_device_count) 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 08b3ca0ead69f3dc9f8139a1b2dff658fe74510c..b012e94b7d9f9787f008ad1208b88eb9fe2e488a 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -158,7 +158,7 @@ static inline void acpi_early_processor_osc(void) {} -------------------------------------------------------------------------- */ 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 b1815b20a99c0dcda0b385d9fef0c4321cf83125..3874eec972cdf898dbb0585173c3f0def1b3eaa5 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -967,8 +967,11 @@ static ssize_t scrub_show(struct device *dev, 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 void acpi_nfit_scrub(struct work_struct *work) 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 ce3a7a16f03fcc8f8bff97ec137b03df2f7986f1..17b518cb787c33c55c554c69a2724612cd9b1d96 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -103,25 +103,27 @@ int acpi_map_pxm_to_node(int pxm) */ 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 e6e991ac20f3e56e803183e3f6238b3158affa1b..0c6874e6a9b638ea6b414130b542e9cf08fb7c18 100644 --- a/drivers/acpi/pmic/intel_pmic_xpower.c +++ b/drivers/acpi/pmic/intel_pmic_xpower.c @@ -28,97 +28,97 @@ static struct pmic_table power_table[] = { .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 = 0x38, + .address = 0x34, .reg = 0x10, .bit = 0x03, - }, + }, /* BUC1 */ { - .address = 0x3c, + .address = 0x38, .reg = 0x10, .bit = 0x06, - }, + }, /* BUC2 */ { - .address = 0x40, + .address = 0x3c, .reg = 0x10, .bit = 0x05, - }, + }, /* BUC3 */ { - .address = 0x44, + .address = 0x40, .reg = 0x10, .bit = 0x04, - }, + }, /* BUC4 */ { - .address = 0x48, + .address = 0x44, .reg = 0x10, .bit = 0x01, - }, + }, /* BUC5 */ { - .address = 0x4c, + .address = 0x48, .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 1c2b846c577604d0b471e87d19cecea6caa286f9..3a6c9b741b233cd81304961e43482d003347397d 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -864,6 +864,16 @@ void acpi_resume_power_resources(void) 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 9d5f0c7ed3f7bf657736eeeb57ec803856449459..8697a82bd4659bba3084d1e5ae9c0951eb8c6c4a 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -251,6 +251,9 @@ static int __acpi_processor_start(struct acpi_device *device) 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 acpi_device *device) 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 d51ca1c05619567798281c2733fdf3dd28909d5d..207e9bbb9490b78366fca787dfb8283beadf8294 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -62,8 +62,8 @@ struct acpi_processor_throttling_arg { #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 @@ static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr) 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 @@ static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr) return 0; } -static int acpi_processor_get_throttling(struct acpi_processor *pr) +static long __acpi_processor_get_throttling(void *data) { - cpumask_var_t saved_mask; - int ret; + struct acpi_processor *pr = data; + + return pr->throttling.acpi_processor_get_throttling(pr); +} +static int acpi_processor_get_throttling(struct acpi_processor *pr) +{ 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 @@ static long acpi_processor_throttling_fn(void *data) 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 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, 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 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, 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 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, 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 a4327af676fe81948cdb76c3b15786256929ffa8..097d630ab8867267326121f9f4db2525cf06ef4b 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -474,6 +474,7 @@ static void acpi_pm_start(u32 acpi_state) */ 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 a9cc34e663f9cf15d356c90d6f36f1d3a78435ed..a82ff74faf7a2ae94c8b16ce7a60cdc4c78f70fc 100644 --- a/drivers/acpi/sleep.h +++ b/drivers/acpi/sleep.h @@ -6,6 +6,7 @@ extern struct list_head acpi_wakeup_device_list; 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 cf05ae973381178cc066e1c235c38afb419e1f01..34328e1b52d0a75961d757e9ef309cb946bf9ee9 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -227,7 +227,8 @@ module_param_cb(trace_method_name, ¶m_ops_trace_method, &trace_method_name, 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 @@ static int param_set_trace_state(const char *val, struct kernel_param *kp) 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 @@ MODULE_PARM_DESC(aml_debug_output, "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 02ded25c82e4a06e1e79bf2f0a4855aa933b3df1..cdc47375178e7eaf5f06f348d76f88df0b890bb5 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -213,6 +213,15 @@ static const struct dmi_system_id video_detect_dmi_table[] = { "3570R/370R/470R/450R/510R/4450RV"), }, }, + { + /* 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, diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index a56fa2a1e9aaf74b772a19fc8a00538933b71974..7394aac91d9e584d1a187287e52948cf7b07dea6 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -83,7 +83,8 @@ static ssize_t driver_override_store(struct device *_dev, struct amba_device *dev = to_amba_device(_dev); char *driver_override, *old = dev->driver_override, *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); diff --git a/drivers/android/binder.c b/drivers/android/binder.c index ff367b8faece821cf3db8db896769220dd6549cf..e7e4560e4c6e4f0a0721f192f461fbb535e4f45c 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -151,7 +151,7 @@ static DECLARE_WAIT_QUEUE_HEAD(binder_user_error_wait); 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 @@ static void binder_send_failed_reply(struct binder_transaction *t, &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); diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 9b46ef4c851e1114b16eede6e5f924aad6804dc9..4d4b5f607b81844c22015b0df6cd9d30579f21c8 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -539,7 +539,9 @@ static const struct pci_device_id ahci_pci_tbl[] = { .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 aaa761b9081cc02a75792302c741f7b54ebd9823..cd2eab6aa92ea245e1a3dab839be7fe8aa938cdb 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -514,8 +514,9 @@ int ahci_platform_init_host(struct platform_device *pdev, 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 aee39524375c0316054933b2a1579afe11c1b41a..e08c09fa5da061f80f77e585c3b9b58c9ecfe8f2 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4403,6 +4403,25 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "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, }, + /* devices that don't properly handle queued TRIM commands */ { "Micron_M500_*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | ATA_HORKAGE_ZERO_AFTER_TRIM, }, @@ -4414,7 +4433,9 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { 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 +5286,7 @@ void ata_qc_issue(struct ata_queued_cmd *qc) * 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 e3e10e8f6f6aaa9e3be6e4397d69d4ab182e17ba..9babbc8457503ea2529f3b0ee10064275c9a8ed1 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3226,6 +3226,12 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) 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 @@ static inline int __ata_scsi_queuecmd(struct scsi_cmnd *scmd, 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/base/power/opp/core.c b/drivers/base/power/opp/core.c index a7c5b79371a7c015211328e123cccd9e706c8e2f..94001aafc6d0cb8142b14377a780b22eb6e01b8e 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c @@ -708,7 +708,7 @@ static void _remove_opp_dev(struct opp_device *opp_dev, 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 3331ed806e3a672bd9c9d7490777616a06e562d7..f74f3ca08ae0eec696f79fbdf53d952f96950d71 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1736,7 +1736,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, 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 402254d262474aecf93098e1c596619d169abc6c..ff1c4d7aa025b87813f40c175ce7e42c6d67aacb 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -263,7 +263,7 @@ static int lo_write_bvec(struct file *file, struct bio_vec *bvec, loff_t *ppos) 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_switch(struct loop_device *lo, struct file *file) */ 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 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) 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 f92775650754688c9c5d2ef6b4ec90940ab4bad4..7f7c9421c8c301766f2212cf00b8d253b23d7456 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 08c2c93887c1b3e4a38d19272f61850403f8f405..3aac78a5091b6bd1d58055f05d861ae53dfafaae 100644 --- a/drivers/bluetooth/btqcomsmd.c +++ b/drivers/bluetooth/btqcomsmd.c @@ -85,7 +85,8 @@ static int btqcomsmd_send(struct hci_dev *hdev, struct sk_buff *skb) break; } - kfree_skb(skb); + if (!ret) + kfree_skb(skb); return ret; } diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 3257647d4f74cb67efb90b81f361c07254604c70..f8ba5c714df5aac544be76749c7a8542a3fd8c53 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -217,7 +217,6 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x0036), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x311e), .driver_info = BTUSB_ATH3012 }, @@ -250,6 +249,7 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 }, /* QCA ROME chipset */ + { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x0cf3, 0xe007), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x0cf3, 0xe009), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME }, diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 9497c469efd225cddd8cb53214d18c7310a14539..2230f9368c215f88791cc1192a0aca769e3a62d7 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -113,16 +113,21 @@ static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu) { 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 6c867fbc56a7a82d4ceddf83b79c15083e224c03..74b2f4a146430486d2597bb217524901388e2d9d 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -936,6 +936,9 @@ static int qca_setup(struct hci_uart *hu) 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 72fe0a5a8bf3ff2ce8cb7da6820dfdba526be3f6..017c37b9c7c145df2bad7139aba1514a09cb35c6 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 @@ enum { 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 @@ static const int gisb_offsets_bcm7038[] = { [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 @@ static const int gisb_offsets_bcm7400[] = { [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 @@ static const int gisb_offsets_bcm7435[] = { [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 @@ static const int gisb_offsets_bcm7445[] = { [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 @@ static u32 gisb_read(struct brcmstb_gisb_arb_device *gdev, int reg) { 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 @@ static u32 gisb_read(struct brcmstb_gisb_arb_device *gdev, int reg) 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 @@ static void gisb_write(struct brcmstb_gisb_arb_device *gdev, u32 val, int reg) 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 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev, 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 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev, 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 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev, 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/char/adsprpc.c b/drivers/char/adsprpc.c index 7f8a158a33d023c495b5608c3be1cd8f9c9c70ed..4d734bfe20182ee8331eb773f6a1ba4355f123a1 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -1236,6 +1236,25 @@ static void fastrpc_notify_users(struct fastrpc_file *me) } + +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 @@ static void fastrpc_notify_pdr_drivers(struct fastrpc_apps *me, char *spdname) 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); diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 0f7d28a98b9a0ad926a99d82221b5cff4b031508..a7cc5b7be598451da9e07f400dd9c78036600301 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -871,6 +871,8 @@ void intel_gtt_insert_sg_entries(struct sg_table *st, } } 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_masks.c b/drivers/char/diag/diag_masks.c index b7d56a99684dca53307590d6c7d10aeedafebf67..c20dd2e12160fa1736ea0ddeb50aa96c503aef6a 100644 --- a/drivers/char/diag/diag_masks.c +++ b/drivers/char/diag/diag_masks.c @@ -2189,9 +2189,12 @@ void diag_send_updates_peripheral(uint8_t peripheral) 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 @@ int diag_process_apps_masks(unsigned char *buf, int len, int pid) 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 @@ int diag_process_apps_masks(unsigned char *buf, int len, int pid) 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 ce0c7bb2fbef3eba662770520f8779a50d7a5108..55b1b4961eeb5ca32d270b9df02839812ae8924c 100644 --- a/drivers/char/diag/diag_memorydevice.c +++ b/drivers/char/diag/diag_memorydevice.c @@ -384,7 +384,7 @@ int diag_md_init(void) 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 * @@ -408,12 +408,53 @@ int diag_md_init(void) 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)); + } + + 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 35a1ee35a956c6b2fcbb9113a432366fe9e3223a..9b4aa392233df9893a7af5dce3130fdb00e3b225 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 @@ -46,7 +46,9 @@ struct diag_md_info { 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 fc187767405896a8c23fdbbe8f8c44678c1b6f6c..9bbfb82fec13bfeedc679725710c274dc64d1228 100644 --- a/drivers/char/diag/diagchar.h +++ b/drivers/char/diag/diagchar.h @@ -673,6 +673,7 @@ struct diagchar_dev { 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 0d3389a82bca17b91df38b1e0e2b1b88bfbcf4ba..54a4d98271b33f780af762f587720bdc7ed108b0 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -969,6 +969,7 @@ static int diag_remote_init(void) poolsize_mdm_dci_write); diagmem_setsize(POOL_TYPE_QSC_MUX, itemsize_qsc_usb, poolsize_qsc_usb); + diag_md_mdm_init(); 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_mhi.c b/drivers/char/diag/diagfwd_mhi.c index f8c3fdeb505c7759d0c5619f17eb922bb8162ffc..6f418681c8ff8a5cd7241c8a3412dbe69f3992ca 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 @@ static void mhi_read_work_fn(struct work_struct *work) 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 @@ void diag_mhi_exit(void) diagmem_exit(driver, mhi_info->mempool); } } - diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index 9f2e3be2c5b8b37d059d9aca5d2f4677753ce034..676c910e990fb753218f91aebb699d6fff577438 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -66,7 +66,7 @@ static void (*specific_poweroff_func)(ipmi_user_t user); /* 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 e0a53156b782f086e944ac333652873faf71cad1..89adeb4905f1c16307c89fda25f1c315eeae44c6 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1344,7 +1344,7 @@ static unsigned int num_slave_addrs; #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 @@ static struct smi_info *smi_info_alloc(void) 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 510fc104bcdc25992323774f1fbd26ef94d9c79e..f11c1c7e84c6e595d311973f0eb82dad71f7fde6 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -409,6 +409,7 @@ static void start_event_fetch(struct ssif_info *ssif_info, unsigned long *flags) 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 @@ static void start_recv_msg_fetch(struct ssif_info *ssif_info, 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 9093110161083f7e031b9fb1f802b12488ebacc9..055d2ce378a78f61ddb81fb9021c9e5aca9f4588 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -515,7 +515,7 @@ static void panic_halt_ipmi_heartbeat(void) 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 @@ static void panic_halt_ipmi_heartbeat(void) &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 @@ static void panic_halt_ipmi_set_timeout(void) /* 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 ee737ef41ce92e05240e4218579ed1ff70a0b543..8a167a6eb23938ea96f97420a6cd7dac76cf3c96 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -434,8 +434,9 @@ struct crng_state primary_crng = { * 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]); @@ -741,7 +742,7 @@ static void credit_entropy_bits(struct entropy_store *r, int nbits) 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 +801,7 @@ static int crng_fast_load(const char *cp, size_t len) 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; } @@ -836,7 +837,7 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r) _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) && @@ -852,7 +853,7 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r) wake_up_interruptible(&crng_init_wait); pr_notice("random: crng init done\n"); } - spin_unlock_irqrestore(&primary_crng.lock, flags); + spin_unlock_irqrestore(&crng->lock, flags); } static inline void maybe_reseed_primary_crng(void) @@ -872,8 +873,9 @@ static void _extract_crng(struct crng_state *crng, { 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 +1117,16 @@ static void add_interrupt_bench(cycles_t start) 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 +1155,7 @@ void add_interrupt_randomness(int irq, int irq_flags) 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))) { @@ -1664,6 +1670,7 @@ static int rand_initialize(void) init_std_data(&input_pool); init_std_data(&blocking_pool); crng_initialize(&primary_crng); + crng_global_init_time = jiffies; #ifdef CONFIG_NUMA pool = kcalloc(nr_node_ids, sizeof(*pool), GFP_KERNEL|__GFP_NOFAIL); @@ -1850,6 +1857,14 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg) 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 +2158,7 @@ void add_hwgenerator_randomness(const char *buffer, size_t count, { 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 6f060c76217b9a7ea02ab19b8909e7d708578bd5..7205e6da16cdb833d2ac48c1532e5eb1b9f05dc1 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.c +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -458,7 +458,7 @@ static int st33zp24_recv(struct tpm_chip *chip, unsigned char *buf, size_t count) { int size = 0; - int expected; + u32 expected; if (!chip) return -EBUSY; @@ -475,7 +475,7 @@ static int st33zp24_recv(struct tpm_chip *chip, unsigned char *buf, } 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 912ad30be5852f8a2145d0c1e56c03257bbbb211..65b824954bdc4ce8def1b227cb114fc54b605047 100644 --- a/drivers/char/tpm/tpm-dev.c +++ b/drivers/char/tpm/tpm-dev.c @@ -136,6 +136,12 @@ static ssize_t tpm_write(struct file *file, const char __user *buf, 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 d0ac2d56520f3c6050bcca97285d3b2a78285ac3..830d7e30e508ae68203d8ab3f929744a6cafe6e2 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -1078,6 +1078,11 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) 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 17896d65403343dcf7fc6a4665ec51e65b07bbc8..a5780ebe15efb2bbdfbe94f5f2c8e15e69be13c7 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -668,6 +668,11 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, 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 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, 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 62ee44e57ddc4478d41d9b2082d0e4e27df57b04..da69ddea56cf607765396470a32681b91d996e09 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -437,7 +437,8 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) 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 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count) } 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 c6428771841f814a891719fab16e92d7e0723fc7..caa86b19c76dd7007a3975756dec8fe069a31db3 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -281,7 +281,11 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) 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 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) * 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 8022bea27fed4f43fbfa4298ff47d41730f1cf82..06173d2e316fa6efa1c6286a4e25ef9a95c103ef 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -98,7 +98,7 @@ static int tpm_tcg_read_bytes(struct tpm_tis_data *data, u32 addr, u16 len, } 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 4d24ec3d7cd67fb4ede78deddeda3451ba2acfba..f9aa47ec7af79567dc5700630852f83193c50e4d 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -208,7 +208,8 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) { 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 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) } 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 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) * 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 @@ static void disable_interrupts(struct tpm_chip *chip) * 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 9191aabbf9c2d9031fae874d720c7510480a59fb..e1c2193f2ed3ed17cb77810b8af6472a2aab1e25 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -98,7 +98,7 @@ struct tpm_tis_phy_ops { 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_read32(struct tpm_tis_data *data, u32 addr, } 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 3b97b14c3417e085d917126d86a437192e73d251..01eccb193b5a2990fe4072d5532d9af38976ad4e 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 inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *da } 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 @@ static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, 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 @@ static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, 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 @@ static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, 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 @@ static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, 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 @@ static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, 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_transfer(struct tpm_tis_data *data, u32 addr, u16 len, 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 @@ static int tpm_tis_spi_probe(struct spi_device *dev) 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/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c index 4e1cd5aa69d8aa6c884a5a51198c7ce499b8ee93..07c8f701e51c6f3d032f3aaf1971a9dc1ae6b736 100644 --- a/drivers/clk/at91/clk-generated.c +++ b/drivers/clk/at91/clk-generated.c @@ -260,13 +260,13 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, 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 2acaa77ad482a99f28ea64ea43c891501125304c..73aab6e984cd727f0c45d8df63940eab84c60805 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -401,17 +401,17 @@ struct bcm2835_pll_ana_bits { 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 @@ static void bcm2835_pll_off(struct clk_hw *hw) 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 @@ static int bcm2835_pll_on(struct clk_hw *hw) ~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 @@ static int bcm2835_pll_on(struct clk_hw *hw) 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 @@ static int bcm2835_pll_set_rate(struct clk_hw *hw, } /* 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 a564e9248814c19129b0f824f36e1511a98938cc..adc14145861a4f1309d1613ce9583fcbdddcb89e 100644 --- a/drivers/clk/bcm/clk-ns2.c +++ b/drivers/clk/bcm/clk-ns2.c @@ -103,7 +103,7 @@ CLK_OF_DECLARE(ns2_genpll_src_clk, "brcm,ns2-genpll-scr", 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 5e918e7afaba8d82b2f823c3c07602f8fb68f0d7..95a6e98343920a5f08137f46bbaf9583af846c49 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 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, 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 674785d968a3e1c507637ebbeb3865e17fcb5def..f02900922bbecd188c54f15e14bef216c78fb46f 100644 --- a/drivers/clk/clk-conf.c +++ b/drivers/clk/clk-conf.c @@ -106,7 +106,7 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier) 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 96d37175d0ad59f47abd218db271f558c5921a4d..8ad458b5ad6e7c83d4b9d54d2ebfd16861c49da1 100644 --- a/drivers/clk/clk-scpi.c +++ b/drivers/clk/clk-scpi.c @@ -71,15 +71,15 @@ static const struct clk_ops scpi_clk_ops = { }; /* 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 b051db43fae1995f536bbf732b3a1729a6d214b0..136a86094c531849fdd9eee68b94f628999a82bd 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -72,7 +72,7 @@ static const char * const si5351_input_names[] = { "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 929aef0bd0a00bc919f90dbb4f60c9ae79cf7bef..f41307d38fed3a1ee489beab7c12c8302c2e4da1 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -3262,6 +3262,21 @@ static int __clk_core_init(struct clk_core *core) rate = 0; 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 +3285,13 @@ static int __clk_core_init(struct clk_core *core) 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 +3310,6 @@ static int __clk_core_init(struct clk_core *core) 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 2f29ee1a4d005422a087de38efc3b5274b8f7a04..5588f75a8414d5b7cefa9529cba44ec734d52b91 100644 --- a/drivers/clk/meson/Kconfig +++ b/drivers/clk/meson/Kconfig @@ -7,9 +7,9 @@ config COMMON_CLK_MESON8B 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 9d9af446bafc942db897159f2e077eb5d2440693..37e05d6e010a8aa722dc78b9622a3eff3891de3f 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -572,7 +572,7 @@ static MESON_GATE(gxbb_pl301, HHI_GCLK_MPEG0, 6); 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_usb0_ddr_bridge, HHI_GCLK_MPEG2, 9); 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 3f1be46cbb33749aa1adbf9f073cfa957348f10c..70567958b86a5ddf786a6b6f044417ad7b6d3043 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 @@ -661,7 +662,9 @@ static int meson8b_clkc_probe(struct platform_device *pdev) } 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-8953.c b/drivers/clk/msm/clock-cpu-8953.c index 4ba25431b3920670b395b88d2ae3b94d4fa2d11e..fb393c9b246f99f9f500f93ac5803ac669402e29 100644 --- a/drivers/clk/msm/clock-cpu-8953.c +++ b/drivers/clk/msm/clock-cpu-8953.c @@ -796,11 +796,13 @@ static struct notifier_block clock_panic_notifier = { .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 @@ arch_initcall(clock_cpu_init); #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 @@ static int __init cpu_clock_pwr_init(void) 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-8953.c b/drivers/clk/msm/clock-gcc-8953.c index 57adebf4d4b8027a33d024c19ca29107dfda80f8..0e8665c1f5d441c527801ac2a7d124fc7f7355d9 100644 --- a/drivers/clk/msm/clock-gcc-8953.c +++ b/drivers/clk/msm/clock-gcc-8953.c @@ -411,6 +411,27 @@ static struct clk_freq_tbl ftbl_gfx3d_clk_src_sdm450[] = { 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 @@ static int msm_gcc_gfx_probe(struct platform_device *pdev) 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/mdss/Makefile b/drivers/clk/msm/mdss/Makefile index 6285714ddbc167ae3f6054c4218d2b2ff1dc1e70..3b884dc72951b84a401596c36c9434c879f12964 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-28lpm.o 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 0000000000000000000000000000000000000000..f2ed36ca1d6c48f15b6644bf53c4ff40d15f5c26 --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm-util.c @@ -0,0 +1,820 @@ +/* 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 +#include +#include +#include +#include + +#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) +{ + 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)) { + 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)) { + 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)) { + 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 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); + + 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)) { + 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; + } + + data = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_SYS_CTRL); + if (data & BIT(7)) { /* DSI PHY in LP-11 or ULPS */ + rc = dsi_pll_relock(pll); + if (rc) + goto error; + else + goto end; + } + } + + mdss_dsi_pll_12nm_calc_reg(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; + + 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; + } + + MDSS_PLL_REG_W(pll->pll_base, DSIPHY_SSC0, 0x40); + 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 0000000000000000000000000000000000000000..210742b93373531051be69fff203aad5b06baa74 --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.c @@ -0,0 +1,738 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include + +#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_byte_clk_src), + CLK_LIST(dsi0pll_pixel_clk_src), +}; + +static struct clk_lookup mdss_dsi_pllcc_12nm_1[] = { + 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; + + 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; + + /* 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 0000000000000000000000000000000000000000..6912ff4ff5438b52dc917bafdd5638c6548b4262 --- /dev/null +++ b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.h @@ -0,0 +1,89 @@ +/* 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_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; +}; + +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 4a9bb64ba5b4bf8af2655f639edd53675207c5bf..84982592579ab86d832adad4432d76cfdf00aaae 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 @@ int dsi_pll_clock_register_lpm(struct platform_device *pdev, 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 49f3d7be25cae4cac9b6a9c0aaa7693bc7035e41..b8b6883678c1f0563d138f3da7b7e6b4034b8296 100644 --- a/drivers/clk/msm/mdss/mdss-pll.c +++ b/drivers/clk/msm/mdss/mdss-pll.c @@ -136,6 +136,9 @@ static int mdss_pll_resource_parse(struct platform_device *pdev, } 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 @@ static int mdss_pll_clock_register(struct platform_device *pdev, 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 @@ static const struct of_device_id mdss_pll_dt_match[] = { {.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 1fa5cffab6c2923c9ea252112af7bf766f77fdc2..026ac8e53115dab8abe8244417e0cb8e6257ac7d 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 @@ enum { 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 8bccf4ecdab641bf0a24d2db15ccf419023d7357..9ff4ea63932d507c9a5b7289e34eddf125193652 100644 --- a/drivers/clk/mvebu/armada-38x.c +++ b/drivers/clk/mvebu/armada-38x.c @@ -46,10 +46,11 @@ static u32 __init armada_38x_get_tclk_freq(void __iomem *sar) } 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 struct coreclk_ratio armada_38x_coreclk_ratios[] __initconst = { }; static const int armada_38x_cpu_l2_ratios[32][2] __initconst = { - {0, 1}, {0, 1}, {0, 1}, {0, 1}, - {1, 2}, {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}, - {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 @@ static const int armada_38x_cpu_ddr_ratios[32][2] __initconst = { {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/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index aaf2324c0e05858e3d5b8fbdeff3c696fbf5139c..f7599785c08b2c8443ecd9702796273d8dff680d 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_byte2_ops; 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 35bcf5aec32630eba579dbb15246afe840f2b51d..057f0e11c4fc0a7ddb53ca7b6deda2b7f548e518 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 @@ static unsigned long 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 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 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 @@ const struct clk_ops clk_gfx3d_ops = { }; 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/dispcc-sdm845.c b/drivers/clk/qcom/dispcc-sdm845.c index d4f27d79f638a3962123c082c80840e65f528e1b..0c49fa4bd1379c443aab4d86004cc36dd578d894 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 @@ static struct clk_rcg2 disp_cc_mdss_esc0_clk_src = { .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 @@ static struct clk_rcg2 disp_cc_mdss_esc1_clk_src = { .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 5c4e193164d4d7b9d3ec729ef724b2b1293e560d..8dd71345b5d0214215264c4b0509f948b90e347f 100644 --- a/drivers/clk/qcom/gcc-msm8916.c +++ b/drivers/clk/qcom/gcc-msm8916.c @@ -1437,6 +1437,7 @@ static const struct freq_tbl ftbl_codec_clk[] = { 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/mdss/mdss-dsi-pll-10nm.c b/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c index 874c229910cf24f26ba271bb3a86eb44c7a36505..7b23db46c121a8d9c30074475cd8fc79c83343e5 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 @@ static inline int pll_reg_read(void *context, unsigned int reg, 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 @@ static inline int pll_reg_read(void *context, unsigned int reg, 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 ca97e11517976cbe81e895f8586d540283441591..3b171bef913a770caed9bf3878562c9167c6f683 100644 --- a/drivers/clk/qcom/mmcc-msm8996.c +++ b/drivers/clk/qcom/mmcc-msm8996.c @@ -2984,7 +2984,7 @@ static struct gdsc vfe1_gdsc = { .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 00e6aba4b9c095960e7bd635d62eefdd33033da5..c55d5fe116d6ca748b757fc7db886c9019b0a8d7 100644 --- a/drivers/clk/renesas/clk-rcar-gen2.c +++ b/drivers/clk/renesas/clk-rcar-gen2.c @@ -271,11 +271,14 @@ struct cpg_pll_config { 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 const struct clk_div_table cpg_sd01_div_table[] = { 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 @@ rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg, * 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 eea38f6ea77e995d287831a5d6b34bd7663883c0..3892346c4fcc177b811d386d8df41b53503d1f99 100644 --- a/drivers/clk/renesas/clk-sh73a0.c +++ b/drivers/clk/renesas/clk-sh73a0.c @@ -46,7 +46,7 @@ struct div4_clk { 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 @@ sh73a0_cpg_register_clock(struct device_node *np, struct sh73a0_cpg *cpg, { 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 @@ sh73a0_cpg_register_clock(struct device_node *np, struct sh73a0_cpg *cpg, 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/sunxi-ng/ccu-sun6i-a31.c b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c index 9fe0939c1273ea64eeb13a9e9082e9bcc859960a..6ea5401e688141c931f2a9881ce1dc40b3511128 100644 --- a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c +++ b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c @@ -750,7 +750,7 @@ static struct ccu_mp out_a_clk = { .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 @@ static struct ccu_mp out_b_clk = { .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 @@ static struct ccu_mp out_c_clk = { .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/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 597aa574c36636fa586bbea954431c3e63adb59d..379080c63277005ff80727d3add2f536ec07be30 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -45,6 +45,15 @@ config CPU_FREQ_STAT_DETAILS 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 bf98b28f9ecbedee4a25a6d07fa430abe52b8ce2..ba9f9405258ce0b1658389f2a89c0a8eb60fb8bc 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -4,7 +4,10 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq.o freq_table.o # 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 8059ef9c74283bc81f62a8cb6d6060493a6bd16a..d82ce73c7f7c935dd5f4fa75571c165b82398dff 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -444,6 +445,7 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy, (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 @@ static int cpufreq_online(unsigned int cpu) 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); diff --git a/drivers/cpufreq/cpufreq_times.c b/drivers/cpufreq/cpufreq_times.c new file mode 100644 index 0000000000000000000000000000000000000000..6254f45ca9077cb4522316d86235d4025fc83a2d --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c index 7b596fa38ad2de4b7d9cf075ce69a876b4f2e35f..6bebc1f9f55aa65dc1718e47ddea4c0439b42cb0 100644 --- a/drivers/cpufreq/s3c24xx-cpufreq.c +++ b/drivers/cpufreq/s3c24xx-cpufreq.c @@ -351,7 +351,13 @@ struct clk *s3c_cpufreq_clk_get(struct device *dev, const char *name) 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 86628e22b2a36827d37f35d552f0624bc9dc387c..719c3d9f07fb1adbb0e4e528d00c3f49bb39be33 100644 --- a/drivers/cpufreq/sh-cpufreq.c +++ b/drivers/cpufreq/sh-cpufreq.c @@ -30,54 +30,63 @@ 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; } -/* - * Here we notify other drivers of the proposed change and the final change. - */ -static int sh_cpufreq_target(struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation) +static long __sh_cpufreq_target(void *arg) { - unsigned int cpu = policy->cpu; + struct cpufreq_target *target = arg; + struct cpufreq_policy *policy = target->policy; + 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; - cpus_allowed = current->cpus_allowed; - set_cpus_allowed_ptr(current, cpumask_of(cpu)); - - BUG_ON(smp_processor_id() != cpu); + 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); + 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); + 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); + cpufreq_freq_transition_begin(target->policy, &freqs); clk_set_rate(cpuclk, freq); - cpufreq_freq_transition_end(policy, &freqs, 0); + 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. + */ +static int sh_cpufreq_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + struct cpufreq_target data = { .policy = policy, .freq = target_freq }; + + return work_on_cpu(policy->cpu, __sh_cpufreq_target, &data); +} + static int sh_cpufreq_verify(struct cpufreq_policy *policy) { struct clk *cpuclk = &per_cpu(sh_cpuclk, policy->cpu); diff --git a/drivers/cpuidle/dt_idle_states.c b/drivers/cpuidle/dt_idle_states.c index a5c111b67f377e2a1b8e008c9fa09a92bfcea051..ea11a33e7fff4383e6f333e52393e0b984c3549d 100644 --- a/drivers/cpuidle/dt_idle_states.c +++ b/drivers/cpuidle/dt_idle_states.c @@ -174,8 +174,10 @@ int dt_init_idle_driver(struct cpuidle_driver *drv, 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-of.c b/drivers/cpuidle/lpm-levels-of.c index ad0be45c2dcd58567dc4b7c808102feee21e2c2d..7a653c6c2035fea12a9044237e798bbc4ac00457 100644 --- a/drivers/cpuidle/lpm-levels-of.c +++ b/drivers/cpuidle/lpm-levels-of.c @@ -474,7 +474,7 @@ static int parse_cluster_params(struct device_node *node, /* 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); @@ -708,7 +708,7 @@ static int parse_cpu(struct device_node *node, struct lpm_cpu *cpu) 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 +724,12 @@ static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c) 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 +757,14 @@ static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c) 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 +779,8 @@ void free_cluster_node(struct lpm_cluster *cluster) 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; - } - } } /* @@ -886,7 +873,6 @@ struct lpm_cluster *parse_cluster(struct device_node *node, list_del(&c->list); free_cluster_node(c); failed_parse_params: - c->parent = NULL; pr_err("Failed parse params\n"); return NULL; } diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 9ae640da4df8c6bbbd8ace1ce3d1ce9cf9a4a17c..9694225f8ea8aa0a9e4675899f97b550c59a8315 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -1783,12 +1783,10 @@ static int __init lpm_levels_module_init(void) #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/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index d0b16e5e4ee56acb3e5a4b02191198798f7ff918..d8305ddf87d0787c57ebc539eaf9088337c5fe32 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -750,7 +750,10 @@ static int omap_sham_align_sgs(struct scatterlist *sg, 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 @@ static int omap_sham_prepare_request(struct ahash_request *req, bool update) 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 @@ static int omap_sham_prepare_request(struct ahash_request *req, bool update) } 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 @@ static int omap_sham_handle_queue(struct omap_sham_dev *dd, 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 @@ static int omap_sham_update(struct ahash_request *req) 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/dax/dax.c b/drivers/dax/dax.c index 40be3747724d9e86bbbc2271ed24acde1888cfed..473b44c008dd3c1a5997f2b6d348bb50aa5b1a33 100644 --- a/drivers/dax/dax.c +++ b/drivers/dax/dax.c @@ -453,9 +453,21 @@ static int dax_dev_pmd_fault(struct vm_area_struct *vma, unsigned long addr, 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 1dca479fd8ba9802ae4ff4c6394047ea182536d4..81f7c162b069834a5df1a8d6d8ff8a1f93758e0d 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 @@ static unsigned long compute_freq(struct cpu_pmu_stats *cpustats, { 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)); diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 3357aafc8c75c33c97d45514b098f3407d43d7d9..2298de2a24fdb1af19354a84d610c90c6137b771 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -969,7 +969,8 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, 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/at_xdmac.c b/drivers/dma/at_xdmac.c index b7d7f2d443a140f6e9f1783a89b2723ca02c4bed..ee7b48d5243cf19a6e442319d0f228f884afea2c 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -1473,10 +1473,10 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie, 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 6775f2c74e25b7269417bbe001adfb03698dea97..c7568869284e17d4b63379b236a0f30391640820 100644 --- a/drivers/dma/fsl-edma.c +++ b/drivers/dma/fsl-edma.c @@ -863,11 +863,11 @@ static void fsl_edma_irq_exit( } } -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 @@ static int fsl_edma_probe(struct platform_device *pdev) 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 @@ static int fsl_edma_probe(struct platform_device *pdev) 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 @@ static int fsl_edma_probe(struct platform_device *pdev) 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 @@ static int fsl_edma_remove(struct platform_device *pdev) 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 d1651a50c3491e5ddd6a0ed908453713733bcc62..b9c29720aeb1078d63c3560b4b7160309de1511e 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -937,6 +937,21 @@ static int sdma_disable_channel(struct dma_chan *chan) 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 @@ static int sdma_probe(struct platform_device *pdev) 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 @@ static int sdma_probe(struct platform_device *pdev) 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 @@ static int sdma_probe(struct platform_device *pdev) 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 @@ static int sdma_remove(struct platform_device *pdev) 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 43e88d85129e4fb6de951f8bbd55581f6a5a5fcf..8c3c588834d2e0960f44f96a269c9daf4ad3360b 100644 --- a/drivers/dma/ti-dma-crossbar.c +++ b/drivers/dma/ti-dma-crossbar.c @@ -54,7 +54,15 @@ struct ti_am335x_xbar_map { 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 6d221e5c72ee116511a4c2d7459e066ab1a6ba30..22658057fe27ba501561ce09fb1810ba98257cb2 100644 --- a/drivers/dma/xilinx/zynqmp_dma.c +++ b/drivers/dma/xilinx/zynqmp_dma.c @@ -933,7 +933,8 @@ static void zynqmp_dma_chan_remove(struct zynqmp_dma_chan *chan) 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 58d3e2b39b5b56c6a0594ab10a7dcce864ea5293..61262a7a5c3af1b0c248a3feff5c791799aaf8e0 100644 --- a/drivers/edac/altera_edac.c +++ b/drivers/edac/altera_edac.c @@ -1020,13 +1020,23 @@ altr_init_a10_ecc_block(struct device_node *np, u32 irq_mask, 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 const struct edac_device_prv_data a10_sdmmceccb_data = { 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 4e0f8e720ad9a8bf37280d8063330508bee141d2..bea71fb006a6501c19cc73ed98e2e390f2eadfc0 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -50,7 +50,7 @@ int edac_mc_get_poll_msec(void) 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 5f8543be995ab6584ffe479558133de016649194..b0d32843d58b343ceaf58df8a264e17573eb9d63 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 cb9b8577acbcc04cd5880f2cc13b398db2814a4a..61c19b81ed81233b9770803e4f33c3fc62755ba0 100644 --- a/drivers/edac/mv64x60_edac.c +++ b/drivers/edac/mv64x60_edac.c @@ -759,7 +759,7 @@ static int mv64x60_mc_err_probe(struct platform_device *pdev) /* 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/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 219556c3463d0a47ebe942e992d9d617e141151e..cf2ed6c5ad5736e0b0714ce24db760a207709669 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -19,7 +19,8 @@ cflags-$(CONFIG_EFI_ARMSTUB) += -I$(srctree)/scripts/dtc/libfdt 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 3d50bae8694eef965b62e1b0daa0d4aebea9fd32..1f81e63561fef25f722856685e0135160fd331dd 100644 --- a/drivers/firmware/psci.c +++ b/drivers/firmware/psci.c @@ -59,7 +59,10 @@ bool psci_tos_resident_on(int cpu) 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 @@ static unsigned long psci_migrate_info_up_cpu(void) 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 @@ static int get_set_conduit_method(struct device_node *np) } 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 @@ static void __init psci_init_migrate(void) 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 @@ static int __init psci_probe(void) psci_init_migrate(); if (PSCI_VERSION_MAJOR(ver) >= 1) { + psci_init_smccc(); psci_init_cpu_suspend(); psci_init_system_suspend(); } @@ -656,9 +701,9 @@ int __init psci_acpi_init(void) 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-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 7c446d118cd68f543f63114cd95bf334a11ed67f..1d87b0718d3a4c781046aa9bd4aad134c2624884 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -90,8 +90,18 @@ static inline int to_reg(int gpio, enum ctrl_register reg_type) { 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 void crystalcove_update_irq_ctrl(struct crystalcove_gpio *cg, int gpio) 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 @@ static void crystalcove_gpio_set(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; 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 @@ static int crystalcove_irq_type(struct irq_data *data, unsigned type) 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 @@ static void crystalcove_irq_unmask(struct irq_data *data) 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 @@ static void crystalcove_irq_mask(struct irq_data *data) 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 d0ddba7a9d08a8c41a8f650eeba11af76d63660f..5ef4980f3f143e7ecfb1985004626ff6dc17291c 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 @@ static irqreturn_t wcove_gpio_irq_handler(int irq, void *data) 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 @@ static irqreturn_t wcove_gpio_irq_handler(int irq, void *data) 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 @@ static irqreturn_t wcove_gpio_irq_handler(int irq, void *data) 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 f3c3680963b94ba2ec52f0b9e9bab208c84a2f68..4f54ff45e09e5e3df0030d6e1ada08e516a999bf 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3231,7 +3231,8 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, 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 5796539a0bcb9c50a20871a0b822817fd4dc5265..648ecf69bad55e864004ae8e9253a4f6a8f1c43b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c @@ -540,6 +540,9 @@ int amdgpu_acpi_pcie_performance_request(struct amdgpu_device *adev, 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 6c343a9331825452a572a876649dc3b67eba28c4..0217f5d6ecb9285b58c250a1d45399673ca8560c 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 @@ struct amdgpu_atpx { 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 @@ static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx) 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 @@ static const struct vga_switcheroo_handler amdgpu_atpx_handler = { .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 @@ static bool amdgpu_atpx_detect(void) 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 @@ static bool amdgpu_atpx_detect(void) 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 c02db01f6583e620d885542f37a0da6f1780152e..fe011c7ec70a683c13c514ecd96c9f8ea282aa7f 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 @@ void amdgpu_bo_list_get_list(struct amdgpu_bo_list *list, 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 086aa5c9c6348c45888d0b732f9417c0ec964cbe..e9311eb7b8d9f9654d07069f0bb3344c945e8990 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c @@ -69,25 +69,18 @@ void amdgpu_connector_hotplug(struct drm_connector *connector) /* 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; - - /* 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; + 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; + + /* 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 @@ amdgpu_connector_lvds_detect(struct drm_connector *connector, bool force) 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 @@ amdgpu_connector_lvds_detect(struct drm_connector *connector, bool force) /* 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 @@ amdgpu_connector_vga_detect(struct drm_connector *connector, bool force) 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_vga_detect(struct drm_connector *connector, bool force) 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 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force) 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_dvi_detect(struct drm_connector *connector, bool force) 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 @@ amdgpu_connector_dp_detect(struct drm_connector *connector, bool force) 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_dp_detect(struct drm_connector *connector, bool force) 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 cb505f66d3aab7f2cd7757bfd4a67100dc161686..c801624f33bd91796da8907074d4b217f15b7baa 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -519,7 +519,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, 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 ce9797b6f9c75091e099062a3ef18c69aacb6a20..d0c3e56ece7404973407866b04201a14a4467036 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1678,8 +1678,6 @@ int amdgpu_device_init(struct amdgpu_device *adev, * 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 @@ int amdgpu_gpu_reset(struct amdgpu_device *adev) 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 @@ int amdgpu_gpu_reset(struct amdgpu_device *adev) } 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 @@ int amdgpu_gpu_reset(struct amdgpu_device *adev) } 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 083e2b42987272d96f57fa091f92426651751a61..15a2d8f3725d57d6e1008dfdc51d0243bcf3edbd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c @@ -533,6 +533,12 @@ amdgpu_user_framebuffer_create(struct drm_device *dev, 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 a7ea9a3b454e6dcb897edc28a11c988aba9dec48..d5e4748e3300145af8abad85d93a7d98206f4d3b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -36,8 +36,6 @@ void amdgpu_gem_object_free(struct drm_gem_object *gobj) 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 f3efb1c5dae96469ecdb6944e991f832b43e26dd..5afe72778518f109adaaef3577af2e8080fb6d17 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -94,6 +94,8 @@ static void amdgpu_ttm_bo_destroy(struct ttm_buffer_object *tbo) 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 e3281cacc586020843886593531e5f160b7952fc..5caf517eec9f258bca7f0a7991140ce14e833e5c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -273,12 +273,15 @@ int amdgpu_uvd_suspend(struct amdgpu_device *adev) 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 71116da9e782d52597837b2dfa95a17b2007998d..e040a896179c2d29ad9088426fd31ae8618eb7a9 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 @@ static void gfx_v7_0_gpu_early_init(struct amdgpu_device *adev) 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/si_dpm.c b/drivers/gpu/drm/amd/amdgpu/si_dpm.c index 4cb347e88cf07639e779fa658b80b91a90dc7387..3fa8320e49c175743fc9e51d2234ec1d431e8f2d 100644 --- a/drivers/gpu/drm/amd/amdgpu/si_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/si_dpm.c @@ -3507,6 +3507,11 @@ static void si_apply_state_adjust_rules(struct amdgpu_device *adev, 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 @@ static void si_set_pcie_lane_width_in_smc(struct amdgpu_device *adev, { 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 ef7c8de7060e2dab64b7f7739b8c8286ea741711..171480bb95d0ffba492298b41aa9ef4d299aa672 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -317,7 +317,8 @@ static struct kfd_process *create_process(const struct task_struct *thread) /* 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 1e50647499593927f6238602ec7ba0efce216eb4..8c6e47c5507fbf3d02c74c98749088c8be6b93ec 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c @@ -519,11 +519,17 @@ static ssize_t sysprops_show(struct kobject *kobj, struct attribute *attr, 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 const struct sysfs_ops iolink_ops = { }; static struct kobj_type iolink_type = { + .release = kfd_topology_kobj_release, .sysfs_ops = &iolink_ops, }; @@ -586,6 +593,7 @@ static const struct sysfs_ops mem_ops = { }; static struct kobj_type mem_type = { + .release = kfd_topology_kobj_release, .sysfs_ops = &mem_ops, }; @@ -625,6 +633,7 @@ static const struct sysfs_ops cache_ops = { }; static struct kobj_type cache_type = { + .release = kfd_topology_kobj_release, .sysfs_ops = &cache_ops, }; @@ -747,6 +756,7 @@ static const struct sysfs_ops node_ops = { }; static struct kobj_type node_type = { + .release = kfd_topology_kobj_release, .sysfs_ops = &node_ops, }; diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 4e5ba7eed5421274b9c90ec0908119861626fff7..03a9f208c06b0d2c77bb41cbeca33ff3dd705190 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -115,6 +115,9 @@ static const struct edid_quirk { /* 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 @@ EXPORT_SYMBOL(drm_edid_get_monitor_name); * @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 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid) } 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 48a6167f5e7ba51674ffc17c26cedf5be5b20f22..00c815a7c414f320c457e57ca4ac0041612490fe 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -1202,9 +1202,9 @@ static void drm_vblank_put(struct drm_device *dev, unsigned int pipe) 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 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe) 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 276474d13763b97eeaf52a0eea5a8bc69c94a7ba..d7822bef1986983a53925ad563f9303c6820f1b8 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -460,6 +460,26 @@ static void output_poll_execute(struct work_struct *work) schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD); } +/** + * 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 7513e7678263c182bc85293acf71421632aadcde..bae62cf934cff2229b1efad2dfc7b588dcccf9d5 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1703,6 +1703,8 @@ static int i915_drm_resume_early(struct drm_device *dev) 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/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 13c306173f27b9be7dde7e831d1dca942b59a4e0..0c935dede9f49447b9b4eaa49915972304940983 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1452,12 +1452,20 @@ intel_hdmi_set_edid(struct drm_connector *connector) 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/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c index 5e73dfac1e753a908c3a08072ac9ae8092d7fd62..bfe98b505cf6964fec8f8d339a05981ce265b57d 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog.c +++ b/drivers/gpu/drm/msm/dp/dp_catalog.c @@ -772,8 +772,6 @@ static void dp_catalog_ctrl_config_misc(struct dp_catalog_ctrl *ctrl, 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/dsi-staging/dsi_catalog.c b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c index d083e72cbaa72b2747e81254e368d2ad04d7cd9c..a457070137c46ec40aae2b69a3585974c305e444 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 @@ static void dsi_catalog_phy_3_0_init(struct dsi_phy_hw *phy) 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 0e2db4304a0d20e39a310d6b35537e2be60e0109..144ccd909cabfa318d7c3671c69a3724eb7883e1 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h @@ -102,6 +102,7 @@ u32 dsi_phy_hw_v3_0_get_lanes_in_ulps(struct dsi_phy_hw *phy); 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); diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h b/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h index d89760ec00493b1b25958b515d8be66475d04b70..bdc60d243c553876ff8d7ca1990b801c0d2dca10 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 @@ enum dsi_link_clk_type { 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 @@ enum dsi_clk_type { 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_core_clk_info { }; /** - * 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 @@ struct link_clk_freq { * 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 @@ typedef int (*pre_clockon_cb)(void *priv, * 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 @@ typedef int (*pre_clockon_cb)(void *priv, 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_info { /** * 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 @@ int dsi_clk_set_link_frequencies(void *client, struct link_clk_freq freq, /** - * 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 434d38306df51358eddf371d235653318eaa9651..2e3828ef11e2f0eb805a48b917d9f00463a81741 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_core_clks { }; 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 @@ int dsi_clk_set_pixel_clk_rate(void *client, u64 pixel_clk, u32 index) 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 @@ int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index) 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 @@ int dsi_core_clk_stop(struct dsi_core_clks *c_clks) 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 @@ static int dsi_link_clk_set_rate(struct dsi_link_clks *l_clks, int index) * 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 @@ static int dsi_link_clk_set_rate(struct dsi_link_clks *l_clks, int index) 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 @@ static int dsi_link_clk_prepare(struct dsi_link_clks *l_clks) 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 @@ static int dsi_link_clk_enable(struct dsi_link_clks *l_clks) 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 @@ static int dsi_link_clk_start(struct dsi_link_clks *clks, int index) 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 @@ static int dsi_link_clk_start(struct dsi_link_clks *clks, int index) /** * 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) +{ + struct dsi_link_clks *l_clks; + + 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) { - dsi_link_clk_disable(clks); - dsi_link_clk_unprepare(clks); + struct dsi_link_clks *l_clks; + + l_clks = container_of(link_lp_clks, struct dsi_link_clks, lp_clks); - pr_debug("Link clocks disabled\n"); + 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_core_clk_enable(struct dsi_core_clks *clks, } 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 @@ static int dsi_display_link_clk_enable(struct dsi_link_clks *clks, 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; + } + } + + 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; + } } - /* Turn on rest of the core clocks */ 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_core_clk_disable(struct dsi_core_clks *clks, } 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,18 +743,103 @@ static int dsi_display_link_clk_disable(struct dsi_link_clks *clks, if (!clk || (clk == m_clks)) continue; - rc = dsi_link_clk_stop(clk); + 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); + } + } + + 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 clocks, rc=%d\n", rc); + pr_err("failed to turn off master lp 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_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; +} + static int dsi_update_clk_state(struct dsi_core_clks *c_clks, u32 c_state, struct dsi_link_clks *l_clks, u32 l_state) { @@ -710,6 +871,7 @@ static int dsi_update_clk_state(struct dsi_core_clks *c_clks, u32 c_state, 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 @@ static int dsi_update_clk_state(struct dsi_core_clks *c_clks, u32 c_state, 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 @@ static int dsi_update_clk_state(struct dsi_core_clks *c_clks, u32 c_state, 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; + + rc = dsi_clk_update_link_clk_state(l_clks, + DSI_LINK_HS_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"); - } } else { /* * Two conditions that need to be checked for Link @@ -784,36 +937,26 @@ static int dsi_update_clk_state(struct dsi_core_clks *c_clks, u32 c_state, } 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_clk_update_link_clk_state(l_clks, + DSI_LINK_HS_CLK, l_state, false); + if (rc) + goto error; - 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_LP_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"); - } /* * This check is to save unnecessary clock state * change when going from EARLY_GATE to OFF. In the @@ -872,6 +1015,7 @@ static int dsi_update_clk_state(struct dsi_core_clks *c_clks, u32 c_state, 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 @@ static int dsi_update_clk_state(struct dsi_core_clks *c_clks, u32 c_state, 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 @@ static int dsi_display_link_clk_force_update(void *client) } 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 @@ static int dsi_display_link_clk_force_update(void *client) } 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 @@ void *dsi_display_clk_mngr_register(struct dsi_clk_info *info) 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 93e364de236259e07d2980bfc0b6f826586e6950..32bc3eb5b6557b44e5fce7413ac1f52c0b28754e 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c @@ -498,7 +498,8 @@ static int dsi_ctrl_init_regmap(struct platform_device *pdev, 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 +515,17 @@ static int dsi_ctrl_clocks_deinit(struct dsi_ctrl *ctrl) 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 +542,8 @@ static int dsi_ctrl_clocks_init(struct platform_device *pdev, { 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 +576,30 @@ static int dsi_ctrl_clocks_init(struct platform_device *pdev, 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 +1136,11 @@ static int dsi_message_tx(struct dsi_ctrl *dsi_ctrl, } 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 + @@ -1333,9 +1341,6 @@ static int dsi_message_rx(struct dsi_ctrl *dsi_ctrl, u32 dlen, diff, rlen = msg->rx_len; unsigned char *buff; char cmd; - struct dsi_cmd_desc *of_cmd; - - of_cmd = container_of(msg, struct dsi_cmd_desc, msg); if (msg->rx_len <= 2) { short_resp = true; @@ -1373,9 +1378,9 @@ static int dsi_message_rx(struct dsi_ctrl *dsi_ctrl, * 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, diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h index 4f85cdaf5ddbc992f91ad8b2e5f7eeaf0687207d..b059fc550a3af6b85974928c7eb8e9e7902db36a 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. @@ -97,7 +100,8 @@ struct dsi_ctrl_power_info { /** * 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 +115,8 @@ struct dsi_ctrl_power_info { 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_defs.h b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h index d45f8493d29d446da3ffa9a47079e4de51ad881a..6540182c529c1b0bcedeb0c5b23be7683063eb99 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 @@ struct dsi_host_common_cfg { * @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 @@ struct dsi_video_engine_cfg { 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 199833d132646da831f9200f16813a57fb539b46..7194f1aee24f07af695efa6e34fd87e01f96453a 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 @@ static int dsi_display_read_status(struct dsi_display_ctrl *ctrl, 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 @@ static int dsi_display_read_status(struct dsi_display_ctrl *ctrl, 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 @@ static int dsi_display_read_status(struct dsi_display_ctrl *ctrl, start += lenp[i]; } -error: - /* release panel_lock */ - dsi_panel_release_panel_lock(panel); return rc; } @@ -708,15 +703,16 @@ int dsi_display_check_status(void *display, bool te_check_override) 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; } @@ -741,7 +737,7 @@ int dsi_display_check_status(void *display, bool te_check_override) 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); return rc; } @@ -757,7 +753,7 @@ static int dsi_display_cmd_prepare(const char *cmd_buf, u32 cmd_buf_len, 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 +1577,19 @@ static int dsi_display_set_clamp(struct dsi_display *display, bool enable) 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 +1603,18 @@ static int dsi_display_set_clamp(struct dsi_display *display, bool enable) 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 +3019,14 @@ static void dsi_display_ctrl_isr_configure(struct dsi_display *display, bool en) 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 +3069,8 @@ int dsi_pre_clkoff_cb(void *priv, 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 +3078,14 @@ int dsi_pre_clkoff_cb(void *priv, 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 +3094,13 @@ int dsi_post_clkon_cb(void *priv, 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 +3139,11 @@ int dsi_post_clkon_cb(void *priv, 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 +3168,7 @@ int dsi_post_clkon_cb(void *priv, 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 +3181,6 @@ int dsi_post_clkoff_cb(void *priv, 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 +3196,7 @@ int dsi_post_clkoff_cb(void *priv, 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; @@ -4391,10 +4413,16 @@ static int dsi_display_bind(struct device *dev, 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; diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h index 6b1c0292becf147666e9d79ab9c68a0f509bf72c..56120160cb579392ac5bffd85ee43e146faea3ec 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 @@ int dsi_display_disable(struct dsi_display *display); * 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 @@ int dsi_display_update_pps(char *pps_cmd, void *display); * 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 5b4786596d331590bfa77ff36a7f48ea9956f7d8..6b5bfb4a5cb19d99cd17145154c30a2c11c10487 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 @@ static void dsi_bridge_enable(struct drm_bridge *bridge) { 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 @@ static void dsi_bridge_enable(struct drm_bridge *bridge) 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 e62c65ebe2ffba4341ae78a1837fc66b27ea196c..cb9c1fae5aee6a0edb3e427a5f548dc53fc736fc 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 @@ static int dsi_panel_parse_video_host_config(struct dsi_video_engine_cfg *cfg, 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 @@ static int dsi_panel_parse_video_host_config(struct dsi_video_engine_cfg *cfg, 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 @@ static int dsi_panel_create_cmd_packets(const char *data, 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 @@ static int dsi_panel_parse_misc_features(struct dsi_panel *panel, 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; } @@ -3499,6 +3514,7 @@ static int dsi_panel_roi_prepare_dcs_cmds(struct dsi_panel_cmd_set *set, 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 +3526,7 @@ static int dsi_panel_roi_prepare_dcs_cmds(struct dsi_panel_cmd_set *set, 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 +3751,6 @@ int dsi_panel_unprepare(struct dsi_panel *panel) 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 +3770,11 @@ int dsi_panel_post_unprepare(struct dsi_panel *panel) 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_phy.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c index 6a7a84c0858c94c351863c11c4889b377fb6f560..2e2d0d81d6437b49f8b683c11de402d9e33c936e 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c @@ -901,6 +901,26 @@ int dsi_phy_disable(struct msm_dsi_phy *phy) return rc; } +/** + * 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 diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h index 56d5ee3bd5d43614183748fbcabc5c6bbd993e18..41634116f897433ff485f60fdb2d20582ff42939 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h @@ -217,6 +217,15 @@ int dsi_phy_clk_cb_register(struct msm_dsi_phy *phy, */ 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 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 e31899d4f664dea0f06394980b1ad58bd4da0496..d24a61380cfe2757417852d03b99345ec833754f 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h @@ -233,6 +233,14 @@ struct dsi_phy_hw_ops { int (*phy_timing_val)(struct dsi_phy_per_lane_cfgs *timing_val, 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. 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 b078231e70e36433de154624a151118fb569c3fd..5015806c17e5ded1583aaa17dbffeb0c9b5f4182 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 @@ static void dsi_phy_hw_v3_0_lane_settings(struct dsi_phy_hw *phy, DSI_W32(phy, DSIPHY_LNX_OFFSET_BOT_CTRL(i), 0x0); DSI_W32(phy, DSIPHY_LNX_TX_DCTRL(i), tx_dctrl[i]); } +} + +void dsi_phy_hw_v3_0_clamp_ctrl(struct dsi_phy_hw *phy, bool enable) +{ + u32 reg; - /* 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); + 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/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 19df8be48760af8b7b3f893df6a53307d97d2053..0697db831867be4158c912b86042c3673aa23f20 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -947,6 +947,14 @@ static void msm_lastclose(struct drm_device *dev) 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. */ diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index ce4197bef697bead67395d31e30f3082985d915d..fcdddb34f92597ec6b0b060c2d46c2a5b5df66c4 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -155,6 +155,7 @@ enum msm_mdp_crtc_property { 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_gem.c b/drivers/gpu/drm/msm/msm_gem.c index d9aad883dda3ef2758860a7128cab6619106c28d..277b42162e7cf5ec12e07fc19ff6a904d1c1dd8f 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -96,14 +96,17 @@ static struct page **get_pages(struct drm_gem_object *obj) return p; } + msm_obj->pages = p; + msm_obj->sgt = drm_prime_pages_to_sg(p, npages); if (IS_ERR(msm_obj->sgt)) { + void *ptr = ERR_CAST(msm_obj->sgt); + dev_err(dev->dev, "failed to allocate sgt\n"); - return ERR_CAST(msm_obj->sgt); + msm_obj->sgt = NULL; + return ptr; } - msm_obj->pages = p; - /* * Make sure to flush the CPU cache for newly allocated memory * so we don't get ourselves into trouble with a dirty cache @@ -121,7 +124,9 @@ static void put_pages(struct drm_gem_object *obj) 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)) diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h index db9e7ee45f1a62210b9a32ad64d0f7ca46afc610..e99ff9c53f6f316d62f31e569debac060523d517 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 * @@ -106,6 +106,8 @@ struct msm_kms_funcs { 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 bf08360e986299225534d3790480575cb07886f6..b254d7dc981eb01e0b9453e0be48237b2b86f78c 100644 --- a/drivers/gpu/drm/msm/sde/sde_ad4.h +++ b/drivers/gpu/drm/msm/sde/sde_ad4.h @@ -52,6 +52,7 @@ enum ad_property { 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 0f55b198ce994465938bd721b2750f75fcdebfbc..47ff02431851f2b078914c452bdfe75f71e60d4e 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 @@ static void sde_cp_ad_set_prop(struct sde_crtc *sde_crtc, 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 @@ enum { 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 @@ void sde_cp_crtc_init(struct drm_crtc *crtc) 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 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node, 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 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node, 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 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node, 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 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node, 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 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node, 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 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node, 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 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node, 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 @@ void sde_cp_crtc_apply_properties(struct drm_crtc *crtc) 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 @@ static void dspp_ad_install_property(struct drm_crtc *crtc) "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 @@ static void sde_cp_ad_set_prop(struct sde_crtc *sde_crtc, 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 @@ int sde_cp_hist_interrupt(struct drm_crtc *crtc_drm, bool en, 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 7eb1738526117267637e495088820f983d34c15e..620db26775a91d4e04bd81193fda0a74e9f8d67d 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 @@ void sde_cp_crtc_post_ipc(struct drm_crtc *crtc); */ 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 d2f8d12cdaa6df9a52864a9d36798b022b13a5c3..07d1ad7394c02e2b8cbda84ee7c5683e45a96196 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.c +++ b/drivers/gpu/drm/msm/sde/sde_connector.c @@ -623,6 +623,7 @@ int sde_connector_pre_kickoff(struct drm_connector *connector) 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 @@ void sde_connector_helper_bridge_disable(struct drm_connector *connector) 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_prepare_fence(struct drm_connector *connector) } 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 @@ void sde_connector_complete_commit(struct drm_connector *connector, } /* 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 @@ void sde_connector_commit_reset(struct drm_connector *connector, ktime_t ts) } /* 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,15 @@ sde_connector_best_encoder(struct drm_connector *connector) static void _sde_connector_report_panel_dead(struct sde_connector *conn) { struct drm_event event; - bool panel_dead = true; if (!conn) 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 c6f348ec80eff83417faf5987a5001b1514294ee..51dc92ded875bb6383c9d21914ed29a8b67c9acb 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.h +++ b/drivers/gpu/drm/msm/sde/sde_connector.h @@ -324,6 +324,8 @@ struct sde_connector_evt { * @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 sde_connector { 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 @@ void sde_connector_prepare_fence(struct drm_connector *connector); * 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 @@ -762,6 +766,12 @@ void sde_conn_timeline_status(struct drm_connector *conn); */ 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 diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index ce2997d6486003c98b84bfcb47271ae5b38c115e..cefa5139e29bd1f8994a2bbe9ba9766e4e06f58c 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -936,7 +936,9 @@ static int _sde_crtc_set_crtc_roi(struct drm_crtc *crtc, 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 +960,14 @@ static int _sde_crtc_set_crtc_roi(struct drm_crtc *crtc, 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 +1288,6 @@ static int _sde_crtc_check_rois(struct drm_crtc *crtc, 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,41 +1303,40 @@ static int _sde_crtc_check_rois(struct drm_crtc *crtc, SDE_ERROR("failed to get mode info\n"); return -EINVAL; } - break; - } - - if (!mode_info.roi_caps.enabled) - return 0; - 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 (!mode_info.roi_caps.enabled) + continue; - rc = _sde_crtc_set_crtc_roi(crtc, state); - if (rc) - return rc; + 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_check_autorefresh(crtc, state); - if (rc) - return rc; + rc = _sde_crtc_set_crtc_roi(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); + rc = _sde_crtc_check_autorefresh(crtc, state); if (rc) return rc; - } - rc = _sde_crtc_check_rois_centered_and_symmetric(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); + 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 +2111,62 @@ static void _sde_crtc_dest_scaler_setup(struct drm_crtc *crtc) } } +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 +2183,17 @@ void sde_crtc_prepare_commit(struct drm_crtc *crtc, 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 +2244,16 @@ enum sde_intf_mode sde_crtc_get_intf_mode(struct drm_crtc *crtc) 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 +2278,16 @@ static void sde_crtc_vblank_cb(void *data) 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"); } @@ -2287,6 +2300,7 @@ static void sde_crtc_frame_event_work(struct kthread_work *work) 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 +2329,11 @@ static void sde_crtc_frame_event_work(struct kthread_work *work) 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", @@ -2347,13 +2362,17 @@ static void sde_crtc_frame_event_work(struct kthread_work *work) 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", @@ -2368,46 +2387,6 @@ static void sde_crtc_frame_event_work(struct kthread_work *work) 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) { @@ -2439,6 +2418,23 @@ static void _sde_crtc_set_input_fence_timeout(struct sde_crtc_state *cstate) cstate->input_fence_timeout_ns *= NSEC_PER_MSEC; } +/** + * _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 @@ -2459,6 +2455,8 @@ static void _sde_crtc_set_dim_layer_v1(struct sde_crtc_state *cstate, 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 +2956,10 @@ static void _sde_crtc_setup_mixers(struct drm_crtc *crtc) 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 +3101,7 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc, * 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); /* @@ -4125,6 +4127,7 @@ static void sde_crtc_disable(struct drm_crtc *crtc) 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); @@ -4191,7 +4194,8 @@ static void sde_crtc_disable(struct drm_crtc *crtc) * 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 +4260,7 @@ static void sde_crtc_enable(struct drm_crtc *crtc) 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 && @@ -4915,6 +4919,11 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, {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 +5003,12 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, "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); @@ -5867,10 +5882,6 @@ static int _sde_crtc_init_events(struct sde_crtc *sde_crtc) 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; } diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h index 64f38211631d994ab221fe6f39472bd2f4d94417..53b8df94c15185d8aebdb91dd372563a49eccee2 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 @@ -47,6 +48,16 @@ enum sde_crtc_client_type { RT_RSC_CLIENT, }; +/** + * 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 @@ -80,10 +91,21 @@ struct sde_crtc_mixer { u32 pipe_mask; }; +/** + * 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_mixer { 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; @@ -155,13 +178,12 @@ struct sde_crtc_event { * @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 @@ -224,6 +246,7 @@ struct sde_crtc { 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,8 +255,6 @@ struct sde_crtc { 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 */ diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c index 30214967efde6d9dc36042a08e286cd46d121350..2ce8b2ece5574301e88cba258328771f5b2f889f 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 @@ enum sde_enc_rc_states { 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 sde_encoder_virt { 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 @@ bool sde_encoder_is_dsc_merge(struct drm_encoder *drm_enc) 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) { @@ -1586,11 +1600,16 @@ static int _sde_encoder_update_rsc_client( * 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 +2320,17 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc, _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; } @@ -2648,7 +2674,6 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc) 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,10 +2702,6 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc) 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); @@ -2838,8 +2859,8 @@ void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc, } 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 +2877,7 @@ void sde_encoder_register_frame_event_callback(struct drm_encoder *drm_enc, 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 +2888,9 @@ static void sde_encoder_frame_done_callback( 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 +2919,13 @@ static void sde_encoder_frame_done_callback( 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 +3034,10 @@ static inline void _sde_encoder_trigger_start(struct sde_encoder_phys *phys) 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 +3430,55 @@ void sde_encoder_trigger_kickoff_pending(struct drm_encoder *drm_enc) 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, @@ -4368,6 +4429,7 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc, 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 +4572,7 @@ struct drm_encoder *sde_encoder_init( 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); @@ -4838,10 +4901,14 @@ int sde_encoder_display_failure_notification(struct drm_encoder *enc) SDE_EVT32_VERBOSE(DRMID(enc)); disp_thread = &priv->disp_thread[sde_enc->crtc->index]; - - kthread_queue_work(&disp_thread->worker, - &sde_enc->esd_trigger_work); - kthread_flush_work(&sde_enc->esd_trigger_work); + if (current->tgid == disp_thread->thread->tgid) { + sde_encoder_resource_control(&sde_enc->base, + SDE_ENC_RC_EVENT_KICKOFF); + } else { + kthread_queue_work(&disp_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 0e8e9dd2b8e3acc882f847fb35f9f0aa89748f49..42b9e5880e05154faadb6f06cdb98acad0d65a22 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 @@ void sde_encoder_register_vblank_callback(struct drm_encoder *encoder, * 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 @@ int sde_encoder_update_caps_for_cont_splash(struct drm_encoder *encoder); * 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 9557d41dd8c9aa3acf302ae98c8a37203a54919b..4e9430e0d8f8e10960635c205499e5390d824fd6 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 @@ struct sde_encoder_phys_ops { * @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 @@ enum sde_intr_idx { 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 @@ struct sde_encoder_irq { * @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 @@ struct sde_encoder_phys { 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_phys { 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 @@ struct sde_encoder_phys_cmd { * 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_cmd { 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 @@ struct sde_enc_phys_init_params { 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 828d771acedbea833b409904eacea7219426b904..f06ceb7e5f5c5e3aa155474dbae44e5ccfd79662 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 @@ static int sde_encoder_phys_cmd_control_vblank_irq( 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 @@ static int sde_encoder_phys_cmd_control_vblank_irq( enable, refcount, SDE_EVTLOG_ERROR); } + mutex_unlock(phys_enc->vblank_ctl_lock); return ret; } @@ -1398,6 +1400,7 @@ struct sde_encoder_phys *sde_encoder_phys_cmd_init( 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 862a8b36a4282ccdb4f48867a04102038fd7d652..d363d62746f09d05e940083ca9a0005ab30c07b2 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 @@ static int sde_encoder_phys_vid_control_vblank_irq( 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 @@ static int sde_encoder_phys_vid_control_vblank_irq( 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 @@ static int sde_encoder_phys_vid_control_vblank_irq( 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 @@ struct sde_encoder_phys *sde_encoder_phys_vid_init( 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 9a90075d8993e27d0cb8312d0620bf30aa41252d..bad608de977281d1bdcdea1e31c390709df9e82d 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_fb(struct sde_encoder_phys *phys_enc, } } +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 @@ static void sde_encoder_phys_wb_setup_cdp(struct sde_encoder_phys *phys_enc) 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 @@ static void sde_encoder_phys_wb_setup_cdp(struct sde_encoder_phys *phys_enc) 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 & 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 @@ static int sde_encoder_phys_wb_atomic_check( 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 @@ static int sde_encoder_phys_wb_atomic_check( } } - 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 int sde_encoder_phys_wb_atomic_check( */ 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 @@ static void _sde_encoder_phys_wb_update_flush(struct sde_encoder_phys *phys_enc) 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 @@ static void sde_encoder_phys_wb_setup( sde_encoder_phys_wb_setup_fb(phys_enc, fb, wb_roi); sde_encoder_phys_wb_setup_cdp(phys_enc); -} - -/** - * 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_setup_cwb(phys_enc, 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) +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; + + 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); + 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 @@ static void sde_encoder_phys_wb_done_irq(void *arg, int irq_idx) } /** - * 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); +} - if (wb_enc->bypass_irqreg) - 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) +{ + _sde_encoder_phys_wb_frame_done_helper(arg, false); +} - 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; - } +/** + * 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) +{ - 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; - } + struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys); + int index = 0; - 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; - } + if (!wb_enc) + return; - SDE_DEBUG("registered IRQ for wb %d, irq_idx=%d\n", - hw_wb->idx - WB_0, - wb_enc->irq_idx); + if (wb_enc->bypass_irqreg) + return; - return ret; + 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); + } + } } /** @@ -846,6 +1007,7 @@ static int sde_encoder_phys_wb_wait_for_commit_done( 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 @@ static int sde_encoder_phys_wb_wait_for_commit_done( /* 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 @@ static int sde_encoder_phys_wb_wait_for_commit_done( 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 @@ static int sde_encoder_phys_wb_wait_for_commit_done( } } - sde_encoder_phys_wb_unregister_irq(phys_enc); - if (!rc) wb_enc->end_time = ktime_get(); @@ -937,19 +1097,12 @@ static int sde_encoder_phys_wb_prepare_for_kickoff( 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 @@ static int sde_encoder_phys_wb_prepare_for_kickoff( _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 @@ static void sde_encoder_phys_wb_trigger_flush(struct sde_encoder_phys *phys_enc) 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 @@ static void sde_encoder_phys_wb_disable(struct sde_encoder_phys *phys_enc) 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 @@ static void sde_encoder_phys_wb_init_ops(struct sde_encoder_phys_ops *ops) 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 *sde_encoder_phys_wb_init( 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 @@ struct sde_encoder_phys *sde_encoder_phys_wb_init( 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 @@ struct sde_encoder_phys *sde_encoder_phys_wb_init( 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 686c6403fcf6e68ccbaf5ab50d80383a1b81fa6b..1f30a5c85abc2d0cfde5569ae66da3c16c9244cb 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 @@ void sde_fence_prepare(struct sde_fence_context *ctx) } } -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 @@ static void _sde_fence_trigger(struct sde_fence_context *ctx, ktime_t ts) 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 @@ int sde_fence_create(struct sde_fence_context *ctx, uint64_t *val, 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 @@ int sde_fence_create(struct sde_fence_context *ctx, uint64_t *val, } 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 @@ void sde_fence_signal(struct sde_fence_context *ctx, ktime_t ts, } 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 @@ void sde_fence_signal(struct sde_fence_context *ctx, ktime_t ts, 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 @@ void sde_fence_signal(struct sde_fence_context *ctx, ktime_t ts, 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 @@ void sde_fence_signal(struct sde_fence_context *ctx, ktime_t ts, 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, diff --git a/drivers/gpu/drm/msm/sde/sde_fence.h b/drivers/gpu/drm/msm/sde/sde_fence.h index 29d2ec740e4f5dd280ed5617cea87436a08043cb..7891be45f13825d0d8a6b6f2cad70c75885eaf68 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 @@ struct sde_fence_context { 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 @@ int sde_fence_create(struct sde_fence_context *fence, uint64_t *val, * 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 diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ad4.c b/drivers/gpu/drm/msm/sde/sde_hw_ad4.c index 66445da368fc06dc81f02006991eb7643c4dbab4..e60defd2c35b35a05b7ea569c97cae779f4d5fab 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_ipc_reset_setup_ipcr(struct sde_hw_dspp *dspp, 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 @@ static ad4_prop_setup prop_set_func[ad4_state_max][AD_PROPMAX] = { [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 @@ static ad4_prop_setup prop_set_func[ad4_state_max][AD_PROPMAX] = { [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 @@ static ad4_prop_setup prop_set_func[ad4_state_max][AD_PROPMAX] = { [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 @@ static ad4_prop_setup prop_set_func[ad4_state_max][AD_PROPMAX] = { [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 @@ static ad4_prop_setup prop_set_func[ad4_state_max][AD_PROPMAX] = { [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 @@ static ad4_prop_setup prop_set_func[ad4_state_max][AD_PROPMAX] = { [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 @@ struct ad4_info { 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 @@ static int ad4_cfg_setup(struct sde_hw_dspp *dspp, struct sde_ad_hw_cfg *cfg) 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 @@ static int ad4_strength_setup_idle(struct sde_hw_dspp *dspp, 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 eacafe6045e1b4ef8cf64b8e28bb8bde134ae035..475f8d08cc414af27916d4bf11c33bc6c633c0e0 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c @@ -1694,6 +1694,9 @@ static int sde_wb_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) 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, @@ -3248,6 +3251,7 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) } 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 963b48fd552e56fa84e818ecd6cdb26a19946729..65919e9bfbf27a2345919dc74deb67806f675fc1 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 @@ enum { * @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 @@ enum { SDE_WB_QOS, SDE_WB_QOS_8LVL, SDE_WB_CDP, + SDE_WB_HAS_CWB, SDE_WB_MAX }; @@ -922,6 +924,7 @@ struct sde_perf_cfg { * @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 +962,7 @@ struct sde_mdss_cfg { 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_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c index 303d96e3625cb47c603467c361352194c92bc546..2146611b66d71a7b70dce3871cd4e7d7354a6db0 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 @@ static void sde_hw_ctl_intf_cfg(struct sde_hw_ctl *ctx, 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 @@ static void _setup_ctl_ops(struct sde_hw_ctl_ops *ops, 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 9eb31f17adc5030b19ffbc5f3323258c1d43acb0..75631dd8074ed093b30b0f2ecdb43e4937c22a46 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 @@ struct sde_hw_ctl_ops { 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 4e677c2a326ee6a83ebb9ff45ea9b78a1a46e9bb..97a3ea4489aa49fcf494c58327d50e83bfa32f94 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 @@ static void sde_hw_lm_clear_dim_layer(struct sde_hw_mixer *ctx) 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 8022f234048112cbf48728454a42efb24f9babc2..9b4e03d65098dd63f2899b40022ccbfd84e66d10 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 ctl_top { /** * 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 @@ struct ctl_top { * @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 8b89ebbbb6aca2f3b902938503d11853454da4de..cf646bdbee40107c661c8c49189b6f45a6595d4a 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 @@ static void sde_hw_intf_audio_select(struct sde_hw_mdp *mdp) 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 @@ static void _setup_mdp_ops(struct sde_hw_mdp_ops *ops, 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 950a62cee8ef1683cc2394a0df68b8206adc6216..210ea5379e3ba17175542a3df803b497af50fc64 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 @@ struct sde_hw_mdp_ops { * @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 cc020d993def901f5150bb850f0109ded240798c..a59222350a11f03cdfa2a38379ea82e5ccf06223 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 33bdec9fa86fe54cc2e39b0ae2e9f248db432cf9..b0a52a705cb518b82cd30e31bc8142ecddc0680b 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.c @@ -936,9 +936,6 @@ static void sde_kms_prepare_commit(struct msm_kms *kms, } } - 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_commit(struct msm_kms *kms, } } +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 @@ static void sde_kms_complete_commit(struct msm_kms *kms, sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false); - 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_kms_release_splash_resource(sde_kms, old_state); - 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; - } + SDE_EVT32_VERBOSE(SDE_EVTLOG_FUNC_EXIT); } static void sde_kms_wait_for_commit_done(struct msm_kms *kms, @@ -2704,6 +2727,19 @@ static int sde_kms_cont_splash_config(struct msm_kms *kms) 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 +2940,7 @@ static const struct msm_kms_funcs kms_funcs = { .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 +2993,7 @@ static int _sde_kms_mmu_init(struct sde_kms *sde_kms) * 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 +3022,27 @@ static int _sde_kms_mmu_init(struct sde_kms *sde_kms) } 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; } } @@ -3285,12 +3321,7 @@ static int sde_kms_hw_init(struct msm_kms *kms) &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_dbg.c b/drivers/gpu/drm/msm/sde_dbg.c index 3a67a2288af2bbb3d60c981f02f0a320bbcb07c3..582909580be9094948cd734f7888cb999c100c2a 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 @@ -2785,6 +2785,11 @@ static ssize_t sde_evtlog_dump_read(struct file *file, char __user *buff, 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; diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index c1084088f9e4e048ae386a1fdf53b393e407e650..56c288f78d8a89632a3d26170665bdc0a5375069 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -271,9 +271,15 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) 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 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) 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 2c2b86d6812992f07f16f5a5fe190c74f40fca9e..6526a33660874e7b8d38522a0f0db0e46346be76 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -106,7 +106,7 @@ nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos, }; 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 a4cb82495cee3e1d4910cdb295bcd25509baa8a1..245c946ea6612e993191892211bbedbeaaa2d017 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 @@ nvkm_pci_init(struct nvkm_subdev *subdev) 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 e859b3f893f70a67c956615894c87f8b8f9fc171..6112c32b994f7122526e6a62300aadc54a7e1e13 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 int td028ttec1_panel_remove(struct spi_device *spi) } 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 @@ static struct spi_driver td028ttec1_spi_driver = { module_spi_driver(td028ttec1_spi_driver); +MODULE_ALIAS("spi:tpo,td028ttec1"); MODULE_ALIAS("spi:toppoly,td028ttec1"); MODULE_AUTHOR("H. Nikolaus Schaller "); 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 4b83e9eeab06df1027e330407d350ace99a1536b..7def04049498c99bd424ed0212ff2c8c8de814e7 100644 --- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c +++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c @@ -298,7 +298,12 @@ static int dmm_txn_commit(struct dmm_txn *txn, bool wait) 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 505dee0db973da8f13918dff36f4791adee52427..ca91651be3d4dce704f3eb0ffe6df87a1ff0c59d 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -195,7 +195,7 @@ static void evict_entry(struct drm_gem_object *obj, 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 @@ static int fault_2d(struct drm_gem_object *obj, * 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 2cd879a4ae15643cd7a1f72e1644d87a7a50c6d6..cdbb6e625f0532b6492212970758d61c2e52ddaa 100644 --- a/drivers/gpu/drm/qxl/qxl_fb.c +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -387,9 +387,11 @@ static const struct drm_fb_helper_funcs qxl_fb_helper_funcs = { 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 @@ int qxl_fbdev_init(struct qxl_device *qdev) drm_fb_helper_fini(&qfbdev->helper); free: kfree(qfbdev); +#endif + return ret; } @@ -438,6 +442,9 @@ void qxl_fbdev_fini(struct qxl_device *qdev) 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 edee6a5f4da968492139f8b8e3e7329e3c980bf0..b99f3e59011c1500b120d3980f25d3aff05302d3 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -3244,35 +3244,8 @@ static void cik_gpu_init(struct radeon_device *rdev) 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 27affbde058c626eec6ae5b5b77cd9a2d4601ba9..f416f5c2e8e9a6cd3a77d94e94629abfaa077d8d 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -90,25 +90,18 @@ void radeon_connector_hotplug(struct drm_connector *connector) /* 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; - - /* 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; + 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; + + /* 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 @@ radeon_lvds_detect(struct drm_connector *connector, bool force) 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 @@ radeon_lvds_detect(struct drm_connector *connector, bool force) /* 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 @@ radeon_vga_detect(struct drm_connector *connector, bool force) 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_vga_detect(struct drm_connector *connector, bool force) 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 @@ radeon_tv_detect(struct drm_connector *connector, bool force) 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 @@ radeon_tv_detect(struct drm_connector *connector, bool force) 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 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) 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 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) } 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 @@ radeon_dp_detect(struct drm_connector *connector, bool force) 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 @@ radeon_dp_detect(struct drm_connector *connector, bool force) } 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 cdb8cb568c15310589039b2f0c56faf5cbf11b9c..ca1caf4058327f07328a4b7182bb9256e9ad71cc 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -1352,6 +1352,12 @@ radeon_user_framebuffer_create(struct drm_device *dev, 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 41b72ce6613febc033c0e37fe68655faeca65554..83e1345db9e231fa8c04b2fca6328fcb1cd112df 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -238,9 +238,10 @@ int radeon_bo_create(struct radeon_device *rdev, * 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 574ab0016a57fb927ee840afa75ff3e2bfaa9226..b82ef5ed727ca81ce518af207be037014bbacde6 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -5969,9 +5969,9 @@ static void si_set_pcie_lane_width_in_smc(struct radeon_device *rdev, { 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 3322b157106dde518df47c4edcd20d4465904879..1c4d95dea8873f9033db1c4157f3f9003ba10565 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 @@ static void rcar_du_crtc_disable(struct drm_crtc *crtc) 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 c7eba305c48830b225d3927185ad849dac193d07..32d87c6035c9104e441528ea7ab091f3c6e400c7 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -503,7 +503,7 @@ static int vop_enable(struct drm_crtc *crtc) 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 @@ static int vop_initial(struct vop *vop) 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 @@ static int vop_initial(struct vop *vop) 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 @@ static int vop_initial(struct vop *vop) vop->is_enabled = false; + pm_runtime_put_sync(vop->dev); + return 0; err_disable_aclk: @@ -1419,6 +1430,8 @@ static int vop_initial(struct vop *vop) 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 @@ static int vop_bind(struct device *dev, struct device *master, void *data) 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 @@ static int vop_bind(struct device *dev, struct device *master, void *data) mutex_init(&vop->vsync_mutex); - ret = devm_request_irq(dev, vop->irq, vop_isr, - IRQF_SHARED, dev_name(dev), vop); + ret = vop_create_crtc(vop); if (ret) return ret; - /* IRQ is initially disabled; it gets enabled in power_on */ - disable_irq(vop->irq); + pm_runtime_enable(&pdev->dev); - ret = vop_create_crtc(vop); + 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) - goto err_enable_irq; + goto err_disable_pm_runtime; - pm_runtime_enable(&pdev->dev); + /* IRQ is initially disabled; it gets enabled in power_on */ + disable_irq(vop->irq); 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 4a192210574f5ed442d09e33babb8697eacd4bad..caba0311c86cef3b96f6532f0ac204756e6725e7 100644 --- a/drivers/gpu/drm/sun4i/sun4i_crtc.c +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -136,5 +137,9 @@ struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm) 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 1feec34ca9dd881e2c9eac02ca467d9882c20a46..aad2f4a2a0ef1ede385d00bf3bbdbde82f2e4b0a 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -145,7 +145,7 @@ static int sun4i_drv_bind(struct device *dev) 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 @@ static int sun4i_drv_bind(struct device *dev) 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 @@ static int sun4i_drv_bind(struct device *dev) 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 @@ static int sun4i_drv_bind(struct device *dev) 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 @@ static int sun4i_drv_bind(struct device *dev) 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 @@ static const struct component_master_ops sun4i_drv_master_ops = { .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 @@ static int sun4i_drv_add_endpoints(struct device *dev, !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 c6afb24486552d27b1c2fa5ebb0c6c130ded980f..f2975a1525bee962b4227dd08ad02532573bf950 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -336,12 +336,11 @@ static int sun4i_tcon_init_clocks(struct device *dev, } } - 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 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, return ret; } + 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_assert_reset; + goto err_free_clocks; } - ret = sun4i_tcon_init_clocks(dev, tcon); + ret = sun4i_dclk_create(dev, tcon); if (ret) { - dev_err(dev, "Couldn't init our TCON clocks\n"); - goto err_assert_reset; + 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 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, return 0; +err_free_dotclock: + sun4i_dclk_free(tcon); err_free_clocks: sun4i_tcon_free_clocks(tcon); err_assert_reset: @@ -542,6 +549,7 @@ static void sun4i_tcon_unbind(struct device *dev, struct device *master, { 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 f57c0d62c76a23896d172bd1bbc54a60e7871172..19d8dc3cdd4020ba5a801fd57bdc2c9f36faf508 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_regs.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_regs.h @@ -124,7 +124,7 @@ static inline void tilcdc_write64(struct drm_device *dev, u32 reg, u64 data) 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 94983e86ea25ba95bce3b627413f8bbae9469db7..5a184089dfa9594f6c18456a600e1a1934111bfb 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -1209,18 +1209,20 @@ int ttm_bo_init(struct ttm_bo_device *bdev, 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 ddd6badd0eee3d63ce4e8e4f79b9c2450cb6431c..c756b2b7f5dc495f630fed1edd93cda2329c4006 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c @@ -818,6 +818,8 @@ int ttm_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages) 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 611b6b9bb3cb02adaa16767e47965b92ae5d5a93..67ea2ce03a23f1e4feb0c36ed1fd40ac31d7394f 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -158,10 +158,15 @@ static int udl_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) { 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_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index ab3016982466c3ca35ba479050ee107d26eb50ac..b608cd463d4e6c01930faed1c0e135704160e7ea 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -110,8 +110,8 @@ vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, &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 @@ vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, 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/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index d2d93959b1198ce41470a1a76a8341553c1ddc6c..aec6e9eef48999c244cf2cd75642965477de6816 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -433,7 +433,7 @@ static int vmw_fb_kms_detach(struct vmw_fb_par *par, 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 @@ int vmw_fb_off(struct vmw_private *vmw_priv) 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 bf28ccc150dfe081924fc08cf248d898003846f7..87086af4211489fa536dd6f194cc739a40a6c024 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 @@ void vmw_kms_helper_buffer_finish(struct vmw_private *dev_priv, * 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 @@ void vmw_kms_helper_resource_revert(struct vmw_resource *res) * 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 @@ int vmw_kms_helper_resource_prepare(struct vmw_resource *res, 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 @@ int vmw_kms_helper_resource_prepare(struct vmw_resource *res, 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,11 +2005,13 @@ int vmw_kms_helper_resource_prepare(struct vmw_resource *res, * @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_resource_unreserve(res, false, NULL, 0); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index ff4803c107bcdbff9f1b6f4e8cf3cfbbc66cba32..2dd05395e98bda3c5f53bffed6d6a74fe58116ed 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -183,6 +183,11 @@ struct vmw_display_unit { 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 @@ void vmw_kms_helper_buffer_finish(struct vmw_private *dev_priv, 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 f42359084adc4e9890427c7d8a1ac5fcfce6507f..a6ca2185f5b0bb396c13b212a83f96f3846c359a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -753,12 +753,13 @@ int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv, 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 @@ int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv, 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 94ad8d2acf9a2106cafdc9349c4f1518d5d85503..8b914504b85783e602f68ec34aae2c260c26c81d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -977,12 +977,13 @@ int vmw_kms_stdu_surface_dirty(struct vmw_private *dev_priv, 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 @@ int vmw_kms_stdu_surface_dirty(struct vmw_private *dev_priv, 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 f7e9eb33891315cd252d95425d4c0450af5e8f7a..614b1c7697ef8f75c053ce002dcdf063ad7ee947 100644 --- a/drivers/gpu/msm/adreno-gpulist.h +++ b/drivers/gpu/msm/adreno-gpulist.h @@ -207,9 +207,11 @@ static const struct adreno_gpu_core adreno_gpulist[] = { .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 @@ static const struct adreno_gpu_core adreno_gpulist[] = { .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 cb916ae82bfe652ebe6f2ebf0b21bf2bfd04251f..757f9d736d6a2c3fd75a9ecbe4b828cf8cb98afa 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -279,6 +279,59 @@ int adreno_efuse_read_u32(struct adreno_device *adreno_dev, unsigned int offset, 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 @@ static irqreturn_t adreno_irq_handler(struct kgsl_device *device) 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 @@ static irqreturn_t adreno_irq_handler(struct kgsl_device *device) * 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 686ed34d629eb9f2c2aa9517178705521355e7be..8e219583ed011b2e55bd9842e7b45b3a56f64b50 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 @@ enum adreno_regs { 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_map(struct adreno_device *adreno_dev); 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 @@ static inline bool is_power_counter_overflow(struct adreno_device *adreno_dev, 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 @@ static inline unsigned int counter_delta(struct kgsl_device *device, 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 @@ static inline unsigned int counter_delta(struct kgsl_device *device, * 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_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index d37753e24e4a84390d3888f0b89b704f68151b29..876b7c96a551d3b0c53b3c29c65b75ec569570b7 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.c @@ -122,14 +122,34 @@ static void a530_efuse_speed_bin(struct adreno_device *adreno_dev) 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 }, }; @@ -2191,6 +2211,7 @@ static int a5xx_microcode_load(struct adreno_device *adreno_dev) 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, @@ -2204,6 +2225,13 @@ static int a5xx_microcode_load(struct adreno_device *adreno_dev) kgsl_regwrite(device, A5XX_CP_PFP_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; + /* * Resume call to write the zap shader base address into the * appropriate register, @@ -2211,7 +2239,6 @@ static int a5xx_microcode_load(struct adreno_device *adreno_dev) */ 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 @@ static int a5xx_microcode_load(struct adreno_device *adreno_dev) /* 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); - - /* 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; + /* + * 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)) { + 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 @@ static void a5xx_gpmu_int_callback(struct adreno_device *adreno_dev, int bit) } /* - * 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 @@ void a5x_gpc_err_int_callback(struct adreno_device *adreno_dev, int bit) * 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 @@ static struct adreno_irq_funcs a5xx_irq_funcs[32] = { 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_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c index 7096d7ca0280795e96440128f253498c74fc746c..37330cb0a501bc080eea6efede12b17b0c65d725 100644 --- a/drivers/gpu/msm/adreno_a6xx.c +++ b/drivers/gpu/msm/adreno_a6xx.c @@ -845,7 +845,7 @@ static int a6xx_microcode_load(struct adreno_device *adreno_dev) 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 @@ static int a6xx_microcode_load(struct adreno_device *adreno_dev) 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); - - /* 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; + /* + * 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; + } while ((ret == -EAGAIN) && (zap_retry++ < ZAP_RETRY_MAX)); } return ret; @@ -1708,6 +1723,10 @@ static int a6xx_rpmh_power_on_gpu(struct kgsl_device *device) 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 @@ static int a6xx_rpmh_power_on_gpu(struct kgsl_device *device) 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 @@ static int a6xx_rpmh_power_off_gpu(struct kgsl_device *device) 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 @@ static int a6xx_rpmh_power_off_gpu(struct kgsl_device *device) 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 @@ static int a6xx_gmu_fw_start(struct kgsl_device *device, 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 @@ static void a6xx_cp_callback(struct adreno_device *adreno_dev, int bit) 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 @@ static struct adreno_irq_funcs a6xx_irq_funcs[32] = { 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 @@ static unsigned int a6xx_register_offsets[ADRENO_REG_REGISTER_MAX] = { 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 b8c282db189a051504b120952112acb16931db87..a634d9871992d19ef1c57bbf7cd2c3cfa85a58fb 100644 --- a/drivers/gpu/msm/adreno_dispatch.c +++ b/drivers/gpu/msm/adreno_dispatch.c @@ -208,8 +208,8 @@ static inline bool _isidle(struct adreno_device *adreno_dev) 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 bb9f9ff1b4c13b2260772ffff141fdbd9da542d6..a2d60712ea7c6298b7c946217fa494c21661924f 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -458,6 +458,10 @@ static void kgsl_mem_entry_detach_process(struct kgsl_mem_entry *entry) 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); @@ -4143,13 +4147,18 @@ static int 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 c737801bdb0b21334738369fcd17176ede735070..c8c6456343e5d4c752523e275110b162d16fd8e1 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -446,6 +446,7 @@ struct kgsl_context { * @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 @@ struct kgsl_process_private { 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 dbf517a8627bcdb30458631077fa539bd3493470..dbc3d53e08cd0a53706a8eafe9bb8210f25c7cf7 100644 --- a/drivers/gpu/msm/kgsl_gmu.c +++ b/drivers/gpu/msm/kgsl_gmu.c @@ -131,6 +131,10 @@ static int _gmu_iommu_fault_handler(struct device *dev, 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 @@ static int gmu_disable_gdsc(struct gmu_device *gmu) 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 @@ int gmu_start(struct kgsl_device *device) 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 @@ int gmu_start(struct kgsl_device *device) 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 @@ int gmu_start(struct kgsl_device *device) 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 @@ int gmu_start(struct kgsl_device *device) 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 @@ int gmu_start(struct kgsl_device *device) 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 19fa9728ca673a0cd90a471d682db229d717eaf6..130d4004bf77f0ac5fa66537d7d8d18d825b3cbd 100644 --- a/drivers/gpu/msm/kgsl_gmu.h +++ b/drivers/gpu/msm/kgsl_gmu.h @@ -102,6 +102,7 @@ enum gmu_flags { GMU_HFI_ON = 2, GMU_FAULT = 3, GMU_DCVS_REPLAY = 4, + GMU_RSCC_SLEEP_SEQ_DONE = 5, }; /** @@ -139,13 +140,11 @@ struct rpmh_votes_t { /* * 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 @@ enum gpu_idle_level { * @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 @@ struct gmu_device { unsigned int ccl; unsigned int idle_level; unsigned int fault_count; + bool unrecovered; }; void gmu_snapshot(struct kgsl_device *device); @@ -256,6 +257,7 @@ void gmu_remove(struct kgsl_device *device); 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 0ce72f636d6ee4f196ab48e8015324d2a4c15c1f..325d44ad89133c6844805b5036463484cd84d13d 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -789,6 +789,10 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain, 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++; diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index ee9e5df06d0facc5b7a76a8e7bc93b86d8ca6604..2a149ac87ed16f84326acc3ff8879f32a806a872 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -2798,6 +2798,25 @@ _aware(struct kgsl_device *device) 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 3702893566b5f98639cce42318f987ae40a68bf5..14653eab318613c9e6450b1f5e5077818475b129 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -97,6 +97,73 @@ struct mem_entry_stats { 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 @@ void kgsl_process_init_sysfs(struct kgsl_device *device, &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); } } diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index b5888dbd60e0d8ff89091f8df50703d7f8f69496..aea626757e56c87493e8c6875e800e908e160761 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1370,7 +1370,7 @@ u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags) * 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 @@ void __hid_request(struct hid_device *hid, struct hid_report *report, { char *buf; int ret; - int len; + u32 len; buf = hid_alloc_report_buf(report, GFP_KERNEL); if (!buf) @@ -1461,14 +1461,14 @@ void __hid_request(struct hid_device *hid, struct hid_report *report, } 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 @@ EXPORT_SYMBOL_GPL(hid_report_raw_event); * * 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 @@ static const struct hid_device_id hid_ignore_list[] = { { 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 0cd4f72162394b5652fc529681cfbc8774b50a36..5eea6fe0d7bd8181082599a4ed0f05eda3268d5c 100644 --- a/drivers/hid/hid-elo.c +++ b/drivers/hid/hid-elo.c @@ -42,6 +42,12 @@ static int elo_input_configured(struct hid_device *hdev, { 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 244b97c1b74ee48554c8b1ef01d6db1e4ada448c..9347b37a1303f83b33dd05c8c1ceaf34b65d329a 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 fb9ace1cef8b50cbcb52955eb41f4490cb7aff7f..5ff6dd8147b66a6e6339a86830f9b244bac00a5e 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1149,18 +1149,26 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct /* * 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 @@ static void hidinput_led_worker(struct work_struct *work) 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 20b40ad2632503754685b84cc07d8787a4a44515..42ed887ba0be5d79d67738bcc1520eb8fb8a0190 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -34,7 +34,8 @@ module_param(emulate_scroll_wheel, bool, 0644); 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 89e9032ab1e7446c37b5d4d19c6dfc3ec6498bfd..fba655d639af11a0ce9640382274c2afbd0e2eee 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -315,7 +315,8 @@ static struct attribute_group mt_attribute_group = { 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 @@ static void mt_set_input_mode(struct hid_device *hdev) 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 be89bcbf6a71b23f266c8bb0b38b0aa66cca1ae0..276d12d4b5767bbed94a0042e59fc6d3a778134e 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -110,8 +110,8 @@ struct rmi_data { 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 b0bb99a821bd777692cc845ec2992d95834ffeb6..1b1dccd37fbd20e7774799689591200c9623480e 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -1056,7 +1056,6 @@ struct sony_sc { 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 @@ static int sony_leds_init(struct sony_sc *sc) 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 void sony_remove(struct hid_device *hdev) 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) { - 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)); + /* On suspend stop any running force-feedback events */ + if (SONY_FF_SUPPORT) { + struct sony_sc *sc = hid_get_drvdata(hdev); + sc->left = sc->right = 0; 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); - - 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; - } + struct sony_sc *sc = hid_get_drvdata(hdev); - 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 f0e2757cb9094dee3bab158d2c368c739ec91c20..216f0338a1f7746bbc1fcc2e0abd67fe94e59bcf 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -192,6 +192,11 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t 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 865e7c262322e1ed760718ff346ac7d281964ae5..2548c5dbdc75ff558f96b04dcc99e5c109002ca3 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -142,10 +142,10 @@ struct i2c_hid { * 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 int i2c_hid_hwreset(struct i2c_client *client) 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 @@ static void i2c_hid_get_input(struct i2c_hid *ihid) 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 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client, 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 @@ static inline int i2c_hid_acpi_pdata(struct i2c_client *client, { return -ENODEV; } + +static inline void i2c_hid_acpi_fix_up_power(struct device *dev) {} #endif #ifdef CONFIG_OF @@ -1082,6 +1094,8 @@ static int i2c_hid_probe(struct i2c_client *client, 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 7a4d39ce51d914b8eecfa101f7d13249c02f1fb2..e8b90b534f08e88d38ba54f6bd8fe8119ad38a30 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -351,7 +351,7 @@ static int wacom_set_device_mode(struct hid_device *hdev, 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 6031cd1465565b35d8832d445ad15fc60b4c37ee..802afc98e8bdeb6ebd7d33437b4a70d087a30235 100644 --- a/drivers/hsi/clients/ssi_protocol.c +++ b/drivers/hsi/clients/ssi_protocol.c @@ -989,7 +989,7 @@ static int ssip_pn_xmit(struct sk_buff *skb, struct net_device *dev) 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 @@ static int ssip_pn_xmit(struct sk_buff *skb, struct net_device *dev) 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 d8bc4b91019225f5c83d79e68a67f858cfa9b56e..9360cdce740e8591f007dcf21033dfa1d25f3146 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -70,7 +70,7 @@ static const struct vmbus_device vmbus_devs[] = { /* 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 b24f1d3045f04386f7dc3a1c5af4b7ee7724e891..ac63e562071fea7d9186c5e374c8d2fd2c3cb67a 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -94,18 +94,20 @@ enum ina2xx_ids { ina219, ina226 }; 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 @@ struct ina2xx_data { 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 @@ static u16 ina226_interval_to_reg(int interval) 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 @@ static int ina2xx_init(struct ina2xx_data *data) 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 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, 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 @@ static ssize_t ina2xx_show_value(struct device *dev, 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 @@ static ssize_t ina2xx_set_shunt(struct device *dev, 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 @@ static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ina2xx_show_value, NULL, /* 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 @@ static int ina2xx_probe(struct i2c_client *client, /* 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 @@ static int ina2xx_probe(struct i2c_client *client, 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 @@ static int ina2xx_probe(struct i2c_client *client, 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 3baa4f4a8c5e8bfd23d2fa79cb132b0f495ca49a..d659a02647d4ed2eb0f0db82af822daeccad6c6b 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -101,8 +101,8 @@ static const struct coefficients adm1075_coefficients[] = { [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 62ad4f4639f7ddd5c4b7303c4867ed4372cef9a0..b900f76b9b553f70c6312fb4b0fff57a18007ba6 100644 --- a/drivers/hwmon/qpnp-adc-common.c +++ b/drivers/hwmon/qpnp-adc-common.c @@ -751,6 +751,154 @@ static const struct qpnp_vadc_map_pt adcmap_batt_therm_qrd[] = { {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 @@ int32_t qpnp_adc_batt_therm_qrd(struct qpnp_vadc_chip *chip, } 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 @@ int32_t qpnp_adc_get_devicetree_data(struct platform_device *pdev, } } + 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 1f38574dc4de16e379363be70a9083bfaba41f85..7e6af659a649dead91d02c34cfa26274dce426d8 100644 --- a/drivers/hwmon/qpnp-adc-voltage.c +++ b/drivers/hwmon/qpnp-adc-voltage.c @@ -35,8 +35,6 @@ #include #include -#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 @@ static struct qpnp_vadc_scale_fn vadc_scale_fn[] = { [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 @@ int32_t qpnp_vadc_hc_read(struct qpnp_vadc_chip *vadc, 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 @@ static const struct of_device_id qpnp_vadc_match_table[] = { }, { .compatible = "qcom,qpnp-vadc-hc", }, + { .compatible = "qcom,qpnp-adc-hc-pm5", + }, {} }; diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 95c887caf596e0742fd43bc6361d296cc87f1689..2d4da74c58f929e779870ecb5bd266bfa76e2dd6 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -63,7 +63,6 @@ config CORESIGHT_SOURCE_ETM3X 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 d7325c6534ad9d96e899fce16ddb6fa2e6148c4b..7e041d9cdbec378cfc664e9334f175974bec3728 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 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id) 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 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id) 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 f57ad2e0d32e005f3cf68115ebcf60ce55a8ea55..3bbcc957740c6aebccb60a135cf2e0acb73da7cf 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 @@ -711,6 +712,10 @@ static void etm_init_arch_data(void *info) CS_UNLOCK(drvdata->base); + /* check the state of the fuse */ + if (!coresight_authstatus_enabled(drvdata->base)) + goto out; + /* First dummy read */ (void)etm_readl(drvdata, ETMPDSR); /* Provide power to ETM: ETMPDCR[3] == 1 */ @@ -742,6 +747,7 @@ static void etm_init_arch_data(void *info) 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 a4bf109c401eb4119adf36c34a6d3fa93a45d083..5db74f0e9ec361a558c1ba5ef1a9305f8d76af3d 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 @@ -38,6 +38,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); @@ -436,6 +437,9 @@ static void etm4_init_arch_data(void *info) 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 +586,8 @@ static void etm4_init_arch_data(void *info) 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); } diff --git a/drivers/hwtracing/coresight/coresight-event.c b/drivers/hwtracing/coresight/coresight-event.c index d5f304f670601da5854e36942e354e47bc756ac2..86e2b29fdd06ac7e563e82a9c2471eb77f65b183 100644 --- a/drivers/hwtracing/coresight/coresight-event.c +++ b/drivers/hwtracing/coresight/coresight-event.c @@ -20,12 +20,13 @@ #include 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 @@ static void event_abort_unregister(void) 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 @@ static int event_abort_set(const char *val, struct kernel_param *kp) 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-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-replicator-qcom.c index 98547a926a132327b57e94f600d5bbfa1586e30e..09901643b0413b98c1ff71f28b005383744439bb 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 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id) 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 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id) 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-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 3948a50248f1e73f54592f4ef68f307cf5fbaf9e..287f9012b39096674d710b19948a30edb9a81db7 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -689,8 +689,6 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) 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 +733,13 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) 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 +759,10 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) 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 5d2d0879bb5578e9c490b3a89184993d844b7f53..261b8c8fc15739d39076a9e8048481465c0578e7 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 @@ static int tpda_probe(struct amba_device *adev, const struct amba_id *id) 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 @@ static int tpda_probe(struct amba_device *adev, const struct amba_id *id) 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 7cdb45989a9d371e9ebabf7da336433f353bc4f9..6463a4703e374ecab4901a0e42482530e26e4034 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.c +++ b/drivers/hwtracing/coresight/coresight-tpdm.c @@ -4029,8 +4029,6 @@ static int tpdm_probe(struct amba_device *adev, const struct amba_id *id) 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 @@ static int tpdm_probe(struct amba_device *adev, const struct amba_id *id) 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 1cf60f3126aaa7329d4ede411d713192a07f923d..85a16b13c4e1371eb658e1330e421ea130c4c984 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -589,6 +589,9 @@ int coresight_enable(struct coresight_device *csdev) 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 +599,16 @@ int coresight_enable(struct coresight_device *csdev) 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 be810fea6971b569c5fb8dd07291f4f15927fe46..5e2f89a2e32605949780b9d6467f75cb19e01ae3 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -152,7 +152,7 @@ struct coresight_platform_data *of_get_coresight_platform_data( 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/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index 809f4d4e93a0a70c7fe211b200afa7fd6fc15b14..340e037b3224ffb87e4e9004794c35c32fa759d0 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -507,7 +507,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) i2c_dw_disable_int(dev); /* Enable the adapter */ - __i2c_dw_enable(dev, true); + __i2c_dw_enable_and_wait(dev, true); /* Clear and enable interrupts */ dw_readl(dev, DW_IC_CLR_INTR); diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c index dfc98df7b1b6a72ccc96e1e1b242bcab9770002e..7aa7b9cb6203f07a705b245ef6fd4a935bf74cab 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 smbus_methods_t ibm_smbus_methods = { 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 c6a90b4a9c626dcf4fdf65e51e2f01b5b6ac4968..91b10afc8c3490ed5722efb70785f7b4fcaeb1fc 100644 --- a/drivers/i2c/muxes/i2c-mux-reg.c +++ b/drivers/i2c/muxes/i2c-mux-reg.c @@ -196,20 +196,25 @@ static int i2c_mux_reg_probe(struct platform_device *pdev) 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 @@ static int i2c_mux_reg_probe(struct platform_device *pdev) 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 d127ace6aa5757a2e91a67d74b7c18353e1dd689..6ee866fcc5dd035655cc8649101e8b3df1e578ea 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -244,7 +244,7 @@ struct chs_geom { 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 void ide_dev_apply_params(ide_drive_t *drive, u8 unit) 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 3a557e3181eac58b8f2a3972ca50404277aad687..32cd64c2ee0673c427efca09de3ee2451bda849e 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -827,6 +827,8 @@ static const struct iio_trigger_ops st_accel_trigger_ops = { 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 @@ int st_accel_common_probe(struct iio_dev *indio_dev) &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 678e8c7ea7633afb0dacbdca89863fb009a8847c..fec696ec3fd1996701fa876dc2d636eb5c9761cf 100644 --- a/drivers/iio/adc/hi8435.c +++ b/drivers/iio/adc/hi8435.c @@ -121,10 +121,21 @@ static int hi8435_write_event_config(struct iio_dev *idev, enum iio_event_direction dir, int state) { struct hi8435_priv *priv = iio_priv(idev); + int ret; + u32 tmp; + + 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); - if (state) priv->event_scan_mask |= BIT(chan->channel); + } else + priv->event_scan_mask &= ~BIT(chan->channel); return 0; } @@ -442,13 +453,15 @@ static int hi8435_probe(struct spi_device *spi) 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 ab646a90e3da113859474458e3071058c91bf8af..af85db50412ea567f117e965923c9baea3f2084e 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 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st, 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 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, 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 1f1ad41ef881faa468b8a290b441dcf980dd6519..0a8b76326d79d027d52f07d0953100f667dc0d16 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -39,6 +39,7 @@ config KMX61 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 c71bcd30dc38ae487f895d288df56d775c846341..fab6a5e2c733fba7200e6b9beb9e9a68c8644143 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile @@ -15,5 +15,6 @@ obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o 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 f53e9a803a0e1ec1589a5b0ad25d7aa1f8d54e70..93b99bd93738fab6cf2e9c2f8f0d14bf752bb31d 100644 --- a/drivers/iio/imu/adis_trigger.c +++ b/drivers/iio/imu/adis_trigger.c @@ -47,6 +47,10 @@ int adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev) 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 @@ int adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev) 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 0000000000000000000000000000000000000000..fa8868915fba812e980891b97dc81c3ab962c3b4 --- /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 0000000000000000000000000000000000000000..d60c10ac178e190cbf878e8d0d7ba8d5dfe212f5 --- /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 0000000000000000000000000000000000000000..bfff285457c55677ad363dfc029250a3360868ba --- /dev/null +++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.c @@ -0,0 +1,932 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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, char *buf, int size) +{ + int result = MPU_SUCCESS; + char tx_buf[2] = {0x0, 0x0}; + int tmp_size = size; + int 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; + while (tmp_size > 0) { +#ifdef ICM20602_I2C_SMBUS + result += i2c_smbus_read_i2c_block_data(st->client, + reg, (tmp_size < 32)?tmp_size:32, tmp_buf); + tmp_size -= 32; + tmp_buf += tmp_size; +#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 = (tmp_size < 32)?tmp_size:32; + msg[1].buf = tmp_buf; + i2c_transfer(st->client->adapter, msg, ARRAY_SIZE(msg)); + tmp_size -= 32; + tmp_buf += tmp_size; +#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; +} + +static 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; +} + +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) +{ + uint8_t raw_data[6] = {0, 0, 0, 0, 0, 0}; + uint8_t selfTest[6]; + float factory_trim[6]; + int result = 0; + int ii; + + 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); + struct icm20602_user_config *config = st->config; + int i, j; + + 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, j; + + 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 = (st_otp.X != 0) ? mpu_st_tb[gyro_ST_code.X - 1] : 0; + st_otp.Y = (st_otp.Y != 0) ? mpu_st_tb[gyro_ST_code.Y - 1] : 0; + st_otp.Z = (st_otp.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; + int sample_rate; + uint8_t fchoice_b; + + 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; + + 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(sizeof(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; + + return result; +} + +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; + uint8_t usr_ctrl = 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 0000000000000000000000000000000000000000..9c4dbd03c0f7a5f957b978d39b231eaeacf415b6 --- /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 0000000000000000000000000000000000000000..0f7fc9200424c234a4c27ce498b484d13ae4035e --- /dev/null +++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_core.c @@ -0,0 +1,547 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "inv_icm20602_iio.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 = 10; + 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_100HZ; + 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) +{ + return 0; +} + +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) +{ + return 0; +} + +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 gyro_self_test */ +static ssize_t inv_gyro_self_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return 0; +} + +static ssize_t inv_gyro_self_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return 0; +} +static IIO_DEVICE_ATTR( + inv_icm20602_gyro_self_test, + 0644, + inv_gyro_self_test_show, + inv_gyro_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) +{ + return 0; +} + +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) +{ + return 0; +} + +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 acc_self_test */ +static ssize_t inv_acc_self_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return 0; +} + +static ssize_t inv_acc_self_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return 0; +} +static IIO_DEVICE_ATTR( + inv_icm20602_acc_self_test, + 0644, + inv_acc_self_test_show, + inv_acc_self_test_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) +{ + return 0; +} + +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) +{ + return 0; +} + +static ssize_t inv_sampling_frequency_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return 0; +} +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_self_test.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_acc_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 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; + } + + 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); +out_free: + 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) +{ + return 0; +} + +static int inv_icm20602_resume(struct device *dev) +{ + 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 0000000000000000000000000000000000000000..943fc1e6ee28444ba098b1334d5743fdcdb2786a --- /dev/null +++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_iio.h @@ -0,0 +1,282 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 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; + uint32_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; + 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; + struct struct_icm20602_raw_data raw_data; + struct struct_icm20602_real_data real_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_reset_fifo(struct iio_dev *indio_dev); + +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_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); +#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 0000000000000000000000000000000000000000..b0f93be3334019319f576b5638f2a61e04498b2c --- /dev/null +++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_ring.c @@ -0,0 +1,131 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "inv_icm20602_iio.h" +#include + +#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); +} + +int inv_icm20602_reset_fifo(struct iio_dev *indio_dev) +{ + int result; + //u8 d; + //struct inv_icm20602_state *st = iio_priv(indio_dev); + + return result; +} + +/* + * 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; +} + +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; + //struct struct_icm20602_data *data_push = st->data_push; + s64 timestamp; + int i; + + if (!st) + return -MPU_FAIL; + package_count = config->fifo_waterlevel / ICM20602_PACKAGE_SIZE; + mutex_lock(&indio_dev->mlock); + if (config->fifo_enabled) { + result = icm20602_read_fifo(st, + st->buf, config->fifo_waterlevel); + if (result != config->fifo_waterlevel) { + pr_err("icm20602 read fifo failed, result = %d\n", + result); + goto flush_fifo; + } + + for (i = 0; i < package_count; i++) { + memcpy((char *)(&st->data_push[i].raw_data), + st->buf, ICM20602_PACKAGE_SIZE); + result = kfifo_out(&st->timestamps, + ×tamp, 1); + /* when there is no timestamp, put it as 0 */ + if (result == 0) + timestamp = 0; + st->data_push[i].timestamps = timestamp; + iio_push_to_buffers(indio_dev, st->data_push+i); + st->buf += ICM20602_PACKAGE_SIZE; + } + } +//end_session: + mutex_unlock(&indio_dev->mlock); + iio_trigger_notify_done(indio_dev->trig); + return MPU_SUCCESS; + +flush_fifo: + /* Flush HW and SW FIFOs. */ + inv_clear_kfifo(st); + inv_icm20602_reset_fifo(indio_dev); + 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 0000000000000000000000000000000000000000..f05a6318fb6aa33c46e5ab920597963bfb2f9186 --- /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 +#include +#include +#include "inv_icm20602_iio.h" +#include + +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 158aaf44dd951047f7d6212b522dd8b921395b21..5d05c38c4ba9c83c062f333bcd96970a8cf085ce 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -174,7 +174,7 @@ unsigned int iio_buffer_poll(struct file *filp, 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 7de0f397194b0b858bfe229cb88d47a45d87b979..d23cf7759ee7a4528e9dfbb73177bd5652075e62 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -510,13 +510,26 @@ static int rpr0521_probe(struct i2c_client *client, 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 6325e7dc8e03e2cfce529cbab75e38204a46f61b..f3cb4dc0539144e84a5b61c4f213596aec514ca7 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -48,8 +48,6 @@ static int st_magn_spi_remove(struct spi_device *spi) } 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 44e46c159a7e30fda4a4fbd17d1ba11305436bfb..3458418d88bc4aa5942029d70168c3d4a41f7ad3 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -638,6 +638,8 @@ static const struct iio_trigger_ops st_press_trigger_ops = { 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 @@ int st_press_common_probe(struct iio_dev *indio_dev) 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 19d2eb46fda6106f586ca173dee8915842735361..2a4a62ebfd8d11616c9e96421f48429f843c7c34 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -871,12 +871,13 @@ static int zpa2326_wait_oneshot_completion(const struct iio_dev *indio_dev, { 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 @@ static int zpa2326_wait_oneshot_completion(const struct iio_dev *indio_dev, /* 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 fb4ce0394ac704ed941b7b6b0e5587e056773fc2..978b8d94f9a4bf4e1697656cdc7433d54c1cbd54 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -209,6 +209,22 @@ int rdma_addr_size(struct sockaddr *addr) } 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 30f01613b518a8c854dd1971fff03b649d19e0b7..cbe5324c4331373472f1889bed5e693d677ac3df 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -4039,6 +4039,9 @@ int rdma_join_multicast(struct rdma_cm_id *id, struct sockaddr *addr, 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 @@ static int cma_get_id_stats(struct sk_buff *skb, struct netlink_callback *cb) 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/iwpm_util.c b/drivers/infiniband/core/iwpm_util.c index ade71e7f01313b7634a074885a3900dea087425b..2fe4c2c921de6f0bfcbaa438f226ce04c2f18465 100644 --- a/drivers/infiniband/core/iwpm_util.c +++ b/drivers/infiniband/core/iwpm_util.c @@ -664,6 +664,7 @@ int iwpm_send_mapinfo(u8 nl_client, int iwpm_pid) } 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 9520154f1d7c96ddb521ad4e146e8ef251dcd49a..cb79d171d1e4d0f84504f19d9293bccb0cd636ff 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -132,7 +132,7 @@ static inline struct ucma_context *_ucma_find_context(int id, 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 @@ static ssize_t ucma_create_id(struct ucma_file *file, const char __user *inbuf, 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 @@ static ssize_t ucma_create_id(struct ucma_file *file, const char __user *inbuf, 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 @@ static ssize_t ucma_create_id(struct ucma_file *file, const char __user *inbuf, 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 @@ static ssize_t ucma_bind_ip(struct ucma_file *file, const char __user *inbuf, 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 @@ static ssize_t ucma_bind(struct ucma_file *file, const char __user *inbuf, 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 @@ static ssize_t ucma_resolve_ip(struct ucma_file *file, if (copy_from_user(&cmd, inbuf, sizeof(cmd))) return -EFAULT; + if (!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 @@ static ssize_t ucma_resolve_addr(struct ucma_file *file, 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 @@ static ssize_t ucma_init_qp_attr(struct ucma_file *file, 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 @@ static int ucma_set_ib_path(struct ucma_context *ctx, 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 @@ static ssize_t ucma_set_option(struct ucma_file *file, const char __user *inbuf, 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 @@ static ssize_t ucma_notify(struct ucma_file *file, const char __user *inbuf, { 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 @@ static ssize_t ucma_notify(struct ucma_file *file, const char __user *inbuf, 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 @@ static ssize_t ucma_process_join(struct ucma_file *file, 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 @@ static ssize_t ucma_join_ip_multicast(struct ucma_file *file, 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 @@ static ssize_t ucma_join_multicast(struct ucma_file *file, 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 c22fde6207d141bdaec17dcda3d8b585a1140255..e74aa1d60fdba507183a9614a77fa9408a9ff135 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -193,7 +193,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, 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 @@ int ib_umem_copy_from(void *dst, struct ib_umem *umem, size_t offset, 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 d118ffe0bfb6edbd46aa634dc3750edb0fd235bf..4b717cf50d27ea168fd797ee0b82e59f17174fbe 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -2491,9 +2491,13 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file, 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 @@ static struct ib_recv_wr *ib_uverbs_unmarshall_recv(const char __user *buf, 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 44b1104eb168e50b7d598a2c5f756c5681d1e416..c5e921bf9130216eee09c1c47faeea1c8101b82c 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -735,12 +735,21 @@ static int verify_command_mask(struct ib_device *ib_dev, __u32 command) 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 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf, } 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 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf, 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 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf, 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 6512a555f7f8139be08767a62d5639bb273a5a9a..dd18b74cd01dde556d77ba3e45d6f753392d8fec 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -488,6 +488,7 @@ static int _put_ep_safe(struct c4iw_dev *dev, struct sk_buff *skb) ep = *((struct c4iw_ep **)(skb->cb + 2 * sizeof(void *))); release_ep_resources(ep); + kfree_skb(skb); return 0; } @@ -498,6 +499,7 @@ static int _put_pass_ep_safe(struct c4iw_dev *dev, struct sk_buff *skb) 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 @@ static void abort_arp_failure(void *handle, struct sk_buff *skb) 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/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c index 4682909b021b2f182e5b86273dd378b8bdfef011..7853b0caad328114c88561d62f9576b41fa20c15 100644 --- a/drivers/infiniband/hw/hfi1/chip.c +++ b/drivers/infiniband/hw/hfi1/chip.c @@ -6379,18 +6379,17 @@ static void lcb_shutdown(struct hfi1_devdata *dd, int abort) * * 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 @@ static void dc_shutdown(struct hfi1_devdata *dd) 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 @@ static int do_8051_command( { 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 @@ static int do_8051_command( 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 @@ static int do_8051_command( 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 @@ static int test_qsfp_read(struct hfi1_pportdata *ppd) 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 @@ static void free_cntrs(struct hfi1_devdata *dd) 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 @@ u64 write_port_cntr(struct hfi1_pportdata *ppd, int index, int vl, u64 data) 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 @@ static void update_synth_timer(unsigned long opaque) 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 @@ static void update_synth_timer(unsigned long opaque) } 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 @@ static int init_cntrs(struct hfi1_devdata *dd) 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/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h index cc87fd4e534bbefd1235be4c010c5df4be9c065c..a3279f3d2578fd57afc8c9bcd2c9b4f295a390b0 100644 --- a/drivers/infiniband/hw/hfi1/hfi.h +++ b/drivers/infiniband/hw/hfi1/hfi.h @@ -475,7 +475,7 @@ struct rvt_sge_state; #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 @@ struct hfi1_devdata { 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 a3dd27b1305d6cbb1172755c0946d4e6b7ad8cb2..84a97f3f92997dbf288dbb1d3afef88f6f1efa8b 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -1078,11 +1078,11 @@ struct hfi1_devdata *hfi1_alloc_devdata(struct pci_dev *pdev, size_t extra) 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/sysfs.c b/drivers/infiniband/hw/hfi1/sysfs.c index 919a5474e6512a237fa0b6059f3f77f4a671103e..621b60ab74eefb11de5a7196ee45b6c5c85e5c3e 100644 --- a/drivers/infiniband/hw/hfi1/sysfs.c +++ b/drivers/infiniband/hw/hfi1/sysfs.c @@ -196,7 +196,8 @@ static const struct sysfs_ops port_cc_sysfs_ops = { }; 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 2c4b4d072d6ae230fb862fe89886ea820381562d..3b973cba8975024e5b6349f9e67b683ed399ca1e 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_ctrl.c +++ b/drivers/infiniband/hw/i40iw/i40iw_ctrl.c @@ -3644,8 +3644,10 @@ enum i40iw_status_code i40iw_config_fpm_values(struct i40iw_sc_dev *dev, u32 qp_ 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 d1328a6977509cc3cc689a7cc83d329c7c232f91..8ce599e1639c35644aa6129a02ebb625fb0fb6a8 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 c62d354f78102c6e31ff8b65aae4a1be9c021948..3ed49146d73c0423f5159cda37fe362e9ae8eb15 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_puda.c +++ b/drivers/infiniband/hw/i40iw/i40iw_puda.c @@ -1320,7 +1320,7 @@ static void i40iw_ieq_handle_exception(struct i40iw_puda_rsrc *ieq, 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 c41c8d0a4ac08bfc4b31e964aa8e2cfbac5d0e08..19bc1c2186ff64ac30bac71261eb30167533540a 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -1168,7 +1168,7 @@ static void mlx4_ib_disassociate_ucontext(struct ib_ucontext *ibcontext) /* 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 @@ static void mlx4_ib_disassociate_ucontext(struct ib_ucontext *ibcontext) 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 5d73989d977135d9d560add27e074417ceaca258..ae41623e0f135884376575869d61a390b6637010 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -406,7 +406,6 @@ struct ib_mr *mlx4_ib_alloc_mr(struct ib_pd *pd, 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 @@ struct ib_mr *mlx4_ib_alloc_mr(struct ib_pd *pd, 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 fcd04b881ec1924eb679827e18c0a2f8f6f3fd39..fc62a7ded73446b2384bc847f82f2ad23d2b094e 100644 --- a/drivers/infiniband/hw/mlx5/cq.c +++ b/drivers/infiniband/hw/mlx5/cq.c @@ -172,6 +172,8 @@ static void handle_responder(struct ib_wc *wc, struct mlx5_cqe64 *cqe, 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 @@ static void handle_responder(struct ib_wc *wc, struct mlx5_cqe64 *cqe, 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 @@ static void handle_responder(struct ib_wc *wc, struct mlx5_cqe64 *cqe, 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; + } + + 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 (wc->sl & 0x3) { + switch (roce_packet_type) { case MLX5_CQE_ROCE_L3_HEADER_TYPE_GRH: wc->network_hdr_type = RDMA_NETWORK_IB; break; @@ -1117,7 +1130,12 @@ static int resize_user(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq, 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 5e29fbd3a5a0bc929f1439a2fce6a2d970895b41..d7da1dca765f9613035db8471678017952741122 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -1313,7 +1313,7 @@ static void mlx5_ib_disassociate_ucontext(struct ib_ucontext *ibcontext) /* 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 @@ static void mlx5_ib_disassociate_ucontext(struct ib_ucontext *ibcontext) /* 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 0a260a06876de9e82df62947c7d2acc1ca0a376f..7e466f031e30d5629fb980757db12ddfd30d285b 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1648,6 +1648,7 @@ struct ib_mr *mlx5_ib_alloc_mr(struct ib_pd *pd, 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 @@ mlx5_ib_sg_to_klms(struct mlx5_ib_mr *mr, 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 @@ mlx5_ib_sg_to_klms(struct mlx5_ib_mr *mr, 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 fdd156101a72af0e15583bc107ef2b4aadb0a6ce..403df3591d2992afd0cc073eda4b4d0f6c44df17 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -1130,7 +1130,7 @@ static void destroy_raw_packet_qp_sq(struct mlx5_ib_dev *dev, 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 +1146,8 @@ static int get_rq_pas_size(void *qpc) } 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 +1156,12 @@ static int create_raw_packet_qp_rq(struct mlx5_ib_dev *dev, 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 +1239,7 @@ static void destroy_raw_packet_qp_tir(struct mlx5_ib_dev *dev, } 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 +1266,7 @@ static int create_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, 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 +1757,15 @@ static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd, 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 +1805,7 @@ static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd, else if (qp->create_type == MLX5_QP_KERNEL) destroy_qp_kernel(dev, qp); +err: kvfree(in); return err; } diff --git a/drivers/infiniband/hw/mlx5/srq.c b/drivers/infiniband/hw/mlx5/srq.c index d61fd2c727c0fe523ef804c9bac4cc96153816cd..5c1dbe2f8757509cff6d11f78750d280391795df 100644 --- a/drivers/infiniband/hw/mlx5/srq.c +++ b/drivers/infiniband/hw/mlx5/srq.c @@ -243,8 +243,8 @@ struct ib_srq *mlx5_ib_create_srq(struct ib_pd *pd, { 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 @@ struct ib_srq *mlx5_ib_create_srq(struct ib_pd *pd, 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 8bef09a8c49ff3195ac8c9df9dd01a100a93c6ac..265943069b35ae5e9d0374cff7f361c83dbf0a87 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_stats.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_stats.c @@ -836,7 +836,7 @@ void ocrdma_add_port_stats(struct ocrdma_dev *dev) 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 cedb447d0162aeb9bba3c800ceaac75ee205b195..228cb4c70363c3226332651a468891ef360b17a6 100644 --- a/drivers/infiniband/hw/qib/qib_iba7322.c +++ b/drivers/infiniband/hw/qib/qib_iba7322.c @@ -150,7 +150,7 @@ static struct kparam_string kp_txselect = { .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 @@ static void set_no_qsfp_atten(struct qib_devdata *dd, int change) } /* 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 16c446142c2aeb75a87e93991491f25cae96c203..b0f09fb45c7225c8fc988c27f5f423601d832bd8 100644 --- a/drivers/infiniband/sw/rdmavt/ah.c +++ b/drivers/infiniband/sw/rdmavt/ah.c @@ -119,7 +119,7 @@ struct ib_ah *rvt_create_ah(struct ib_pd *pd, 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 6d9904a4a0abe7efb9e882942cd76f96dc920806..29dc5278d7be7be2d5ebf3c359981f1b91371257 100644 --- a/drivers/infiniband/sw/rdmavt/cq.c +++ b/drivers/infiniband/sw/rdmavt/cq.c @@ -197,7 +197,7 @@ struct ib_cq *rvt_create_cq(struct ib_device *ibdev, 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 @@ struct ib_cq *rvt_create_cq(struct ib_device *ibdev, 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 @@ int rvt_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata) 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 39e31b2263c69a3f8c58467ce967e24e177faa39..2152c71a99d345d330633f39bc9c21382d232cbe 100644 --- a/drivers/infiniband/sw/rxe/rxe_resp.c +++ b/drivers/infiniband/sw/rxe/rxe_resp.c @@ -471,8 +471,6 @@ static enum resp_states check_rkey(struct rxe_qp *qp, 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 59f37f412a7fa958d341bd1d0517d84125fac704..ced416f5dffbe366422e69e9a8d8c48dec698eca 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -747,9 +747,8 @@ static int init_send_wqe(struct rxe_qp *qp, struct ib_send_wr *ibwr, 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 335bd2c9e16ec6c53c8fb00f92c2d1abe23b879e..34122c96522b828b71dfb23d6739235d4ca5c468 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -974,6 +974,19 @@ static inline int update_parent_pkey(struct ipoib_dev_priv *priv) */ 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 183db0cd849e64624a7ca87c46417c957c1e560d..0df7d4504c0691aba341fd8fad01cae4fc03c3c8 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -799,6 +799,22 @@ static void path_rec_completion(int status, 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 @@ static int path_rec_start(struct net_device *dev, 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 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr, 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 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr, 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 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr, spin_unlock_irqrestore(&priv->lock, flags); ipoib_neigh_put(neigh); - return; + return NULL; err_path: ipoib_neigh_free(neigh); @@ -998,6 +1022,8 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr, 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 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) 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 fddff403d5d21b0c8a3216de6f7ec0170afa3278..8f79caedf9050548f535f07d83c872c01ee85f80 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -487,6 +487,9 @@ static int ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast) !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 @@ void ipoib_mcast_join_task(struct work_struct *work) 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 @@ void ipoib_mcast_join_task(struct work_struct *work) 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 @@ void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb) 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 0983470929bda8a1f29549e0ff8ad24c8f44aeed..b879d21b75481f14a03529862f3c32b9bbb92144 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -2098,6 +2098,9 @@ isert_rdma_rw_ctx_post(struct isert_cmd *cmd, struct isert_conn *conn, 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 @@ isert_rdma_rw_ctx_post(struct isert_cmd *cmd, struct isert_conn *conn, 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 c02ada57d7f5c4fa5b765a6401ac8e7bbd6096bb..85bd19753d55d69f92c3d97e4265293869337aac 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.h +++ b/drivers/infiniband/ulp/isert/ib_isert.h @@ -124,6 +124,7 @@ struct isert_cmd { 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 84f91858b5e6efc342722c59d022ad3579776e12..463ea592a42abdceea2c3693d530b5ea82d70639 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -2626,9 +2626,11 @@ static int srp_abort(struct scsi_cmnd *scmnd) 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 @@ static ssize_t srp_create_target(struct device *dev, 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 29ab814693fc3ee917aa58013ca6d3a8d95062cb..9888c9b531bbd3b94e13e1bb811ce1f68f3d729c 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -80,7 +80,7 @@ module_param(srpt_srq_size, int, 0444); 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 @@ static void srpt_queue_response(struct se_cmd *cmd) } 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 @@ static void srpt_release_cmd(struct se_cmd *se_cmd) 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/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index 7f12b6579f822d48add2f6f42ab7f8a1ad068d11..795fa353de7c01699c11c76f72743965fc243eb3 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -216,8 +216,10 @@ static void matrix_keypad_stop(struct input_dev *dev) { 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 5a5778729e3706e3b38016f27ee26bf9e3f7bf9f..76bb51309a781f60f4589888c2a72b01dfb96401 100644 --- a/drivers/input/keyboard/qt1070.c +++ b/drivers/input/keyboard/qt1070.c @@ -274,9 +274,18 @@ static const struct i2c_device_id qt1070_id[] = { }; 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 3048ef3e3e1639e2f7d24b9e60c6cf45cfe01c8a..a5e8998047fec543031acec6195f5f483a452fd1 100644 --- a/drivers/input/keyboard/tca8418_keypad.c +++ b/drivers/input/keyboard/tca8418_keypad.c @@ -189,8 +189,6 @@ static void tca8418_read_keypad(struct tca8418_keypad *keypad_data) 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 3026c0624bb4d73b4c50494fb4a4b72ce49e8e59..f8a987a61bd7db898ee28261f2ad02a497d79657 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -398,6 +398,18 @@ config INPUT_KEYCHORD 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 c3af7c28d1dd1ea7ea174918ea53029fc3b7feab..149273c9f2c485bbc7b35414c323e43f11db3bab 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -82,3 +82,5 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o 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/keychord.c b/drivers/input/misc/keychord.c index fdcc14653b64c7bafd4d4fb52ccd2068291dfdd0..82fefdff366a236df578318e5345343850459666 100644 --- a/drivers/input/misc/keychord.c +++ b/drivers/input/misc/keychord.c @@ -276,7 +276,7 @@ static ssize_t keychord_write(struct file *file, const char __user *buffer, 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 603fc2fadf057d9bb08f9a373e0bf6b20440dc18..12b20840fb74f2c2c0f773fa8ec07aeb1d39cb0c 100644 --- a/drivers/input/misc/twl4030-pwrbutton.c +++ b/drivers/input/misc/twl4030-pwrbutton.c @@ -70,7 +70,7 @@ static int twl4030_pwrbutton_probe(struct platform_device *pdev) 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 0000000000000000000000000000000000000000..af00414d93f5065c349b1ed52640504a63f5391b --- /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 0000000000000000000000000000000000000000..a0e0e791613ed2d6e20fe76fecb4f809bcb1ee9f --- /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 0000000000000000000000000000000000000000..5964e0b0ed414352f77c71c4e453705dcd6eb02d --- /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 0000000000000000000000000000000000000000..ac368cf8c59831983b3e2de9b9e16220c2030aee --- /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 0000000000000000000000000000000000000000..e6b48fc59c41a7b0b23c25e7839bd0284cb8d6df --- /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 0000000000000000000000000000000000000000..78a2dcd4e79404d3bcc88a5e7f31fb876773cd2c --- /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 0000000000000000000000000000000000000000..50495a0dc7d2848d2a7dab897bd21bce4ece66a1 --- /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) +/*!>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 0000000000000000000000000000000000000000..b296ff8d93c7d7335b9f980ecd3327eb39fe6f96 --- /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 0000000000000000000000000000000000000000..3cd5d691c52eabb4de7db0baac4ffd59928fd8c9 --- /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 0000000000000000000000000000000000000000..ecfc99453d8e148041afaf4c2231ee4d6eadc546 --- /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 0000000000000000000000000000000000000000..d896fe800f8b1573fd412f032056be862895bf3d --- /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 +#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 0000000000000000000000000000000000000000..a8f22c5b2cef1e802de7d01b53ca610893c620b8 --- /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 +/* 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 + + +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 0000000000000000000000000000000000000000..ba2998ac7b0197da31f3b31e1f683a3b22714a28 --- /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 0000000000000000000000000000000000000000..be40e83d701300d28c655e7475a202c931b99b64 --- /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 + +#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 0000000000000000000000000000000000000000..888947f2d8341e095f9fffd4c1c5e26c765ee4b7 --- /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 +#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 0000000000000000000000000000000000000000..08b5ce23fe79b9502c286fed400de3a65765ae8a --- /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 +#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 0000000000000000000000000000000000000000..ceb3585b20e7025f427340c9c267b64af76a0118 --- /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 +#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 0000000000000000000000000000000000000000..a1f4683d67870b4d089bfbf1d0bdcab681207a2f --- /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 +#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 0000000000000000000000000000000000000000..71fec10d1afd1d0cd12d434870d8e0b94cab78dc --- /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 +#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 0000000000000000000000000000000000000000..edc4b5c8c3eff30f017c284e757d5f15e0790fdd --- /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 +#include +#include +#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 0000000000000000000000000000000000000000..79df3db288f3ec112beff0ba52fc5d7bd0d4977f --- /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 0000000000000000000000000000000000000000..9e14f7915b16e895fc137b7048735c34d200bbd4 --- /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 +#include +#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 0000000000000000000000000000000000000000..5476ef9241a3ce6cbf9696a5c9f541ddc8af7d90 --- /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 + +#ifdef CAMERA_CCI +#include +#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 0000000000000000000000000000000000000000..68f7d37349ea6520ee05450fdcd1ef64f7bcdd82 --- /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 + +#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 0000000000000000000000000000000000000000..1f15278d5418b28d60315cee15b30bbe87187d6e --- /dev/null +++ b/drivers/input/misc/vl53l0x/stmvl53l0x.h @@ -0,0 +1,186 @@ +/* + * 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 +#include +#include +#include +#include + + +#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 */ +#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, +}; + +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; /* ! +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * power specific includes + */ +#include +#include +#include +#include +#include +/* + * 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 0000000000000000000000000000000000000000..7e0cc152a67a494b4d8b33c22c8cec2fa9fd45c8 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * power specific includes + */ +#include +#include +#include +#include +#include +/* + * 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 0000000000000000000000000000000000000000..ece245f24111a255653d6c2ec9bd690d23cd659a --- /dev/null +++ b/drivers/input/misc/vl53l0x/stmvl53l0x_module.c @@ -0,0 +1,1893 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * 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); + + unsigned int val; + + kstrtoint(buf, 10, &val); + if ((val != 0) && (val != 1)) { + err("store unvalid value=%ld\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 ( %ld)\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; + + kstrtoint(buf, 10, &on); + if ((on != 0) && (on != 1)) { + err("set debug=%ld\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; + + kstrtoint(buf, 10, &delay_ms); + if (delay_ms == 0) { + err("set delay_ms=%ld\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; + + kstrtoint(buf, 10, &timingBudget); + if (timingBudget == 0) { + err("set timingBudget=%ld\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; + + kstrtoint(buf, 10, &useLongRange); + if ((useLongRange != 0) && (useLongRange != 1)) { + err("set useLongRange=%ld\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); + dbg("Measure = %d\n", Measure.RangeMilliMeter); + 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) +{ + struct vl_data *data = dev_get_drvdata(dev); + struct VL_RangingMeasurementData_t Measure; + + dbg("Measure = %d\n", Measure.RangeMilliMeter); + return snprintf(buf, 4, "%d\n", Measure.RangeMilliMeter); +} + +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; + + 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); + dbg("Measure = %d\n", Measure.RangeMilliMeter); + 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; + + 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 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, + 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 XTalkCompensationRateMegaCps; + /*caltarget distance : 100mm and convert to */ + /* fixed point 16 16 format */ + papi_func_tbl->PerformXTalkCalibration(vl53l0x_dev, + (data->xtalkCalDistance<<16), + &XTalkCompensationRateMegaCps); + dbg("Xtalk calibration:%u\n", XTalkCompensationRateMegaCps); + 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 af83d2e349131588305f167b7364ef8ab3e9a9c6..a8a96def0ba21addc2fe9c115c8f3cd496c895ad 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -2538,13 +2538,31 @@ static int alps_update_btn_info_ss4_v2(unsigned char otp[][4], } 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 @@ static int alps_set_defaults_ss4_v2(struct psmouse *psmouse, 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 c9d491bc85e06a32d7cdd19068dc2422b9b42ebb..3851d5715772f8894698f22166a98d65e16db616 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -1082,6 +1082,13 @@ static int elan_probe(struct i2c_client *client, 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 a679e56c44cd49ddea4361aebdb97d4fe7f1e12e..765879dcaf854481402952bc2af08be74d6f915f 100644 --- a/drivers/input/mouse/elan_i2c_i2c.c +++ b/drivers/input/mouse/elan_i2c_i2c.c @@ -557,7 +557,14 @@ static int elan_i2c_finish_fw_update(struct i2c_client *client, 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 59603a5728f7b78553c5702d10105700f07d868e..c519c0b09568e952388c0df4485455821314cb7e 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1711,6 +1711,17 @@ int elantech_init(struct psmouse *psmouse) 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 b604564dec5c9a5d8d154ac18a61c02341f77e5d..30328e57fddae69aea599b7636bf00f9e862902c 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -15,6 +15,7 @@ #define MOUSEDEV_MINORS 31 #define MOUSEDEV_MIX 63 +#include #include #include #include @@ -103,7 +104,7 @@ struct mousedev_client { 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 @@ static void mousedev_notify_readers(struct mousedev *mousedev, } 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 @@ static int mousedev_open(struct inode *inode, struct file *file) 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; + + dx = clamp_val(p->dx, -127, 127); + p->dx -= dx; + + dy = clamp_val(p->dy, -127, 127); + p->dy -= dy; - 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]; + 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 @@ static ssize_t mousedev_read(struct file *file, char __user *buffer, { 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 d1051e3ce819fff642eeb47e7ccb65d21b5ea927..e484ea2dc787f6aa7ccd3f122ff7e53fd8bacf3a 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 __initconst i8042_dmi_nomux_table[] = { { } }; +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. */ @@ -692,6 +706,13 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "20046"), }, }, + { + /* 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 = { @@ -1223,6 +1244,9 @@ static int __init i8042_platform_init(void) 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 25791c2a153377156576ff81a7135702e80ccd6b..6c4156a5fe67837bca9c422e2547603890513c58 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1279,4 +1279,16 @@ config FT_SECURE_TOUCH 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 34e88236feff0c7bbe8c984947ac4084c48dac6e..a5952cab1e752d6eff114a92a2c35dc5248d8876 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -104,3 +104,4 @@ obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o 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 71b5a634cf6d56509f49563ed7454af9f8ae1b88..e7bb155911d057add3929ce5f10ea5e919cf9e98 100644 --- a/drivers/input/touchscreen/ar1021_i2c.c +++ b/drivers/input/touchscreen/ar1021_i2c.c @@ -152,7 +152,7 @@ static int __maybe_unused ar1021_i2c_resume(struct device *dev) 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/goodix.c b/drivers/input/touchscreen/goodix.c index 240b16f3ee9797c4961709ef8f9a50b874febb3f..5907fddcc966ca54faf4c4cb537fe6f19238a2e4 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -778,8 +778,10 @@ static int __maybe_unused goodix_suspend(struct device *dev) 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 @@ static int __maybe_unused goodix_resume(struct device *dev) 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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/drivers/input/touchscreen/hxchipset/Kconfig b/drivers/input/touchscreen/hxchipset/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..ebf3aa478af51d0991a760a0d871988846c83e49 --- /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 0000000000000000000000000000000000000000..509d4913bc39f4577ba64fa4b8e8fefd5bd1c53a --- /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 0000000000000000000000000000000000000000..417b0c08e3615ee256fba5f4c9f9d1da8bd78f91 --- /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;igest_pt_x[i]) + tmp_min_x=gest_pt_x[i]; + if(tmp_max_ygest_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 0000000000000000000000000000000000000000..27ce9aafd959cc3bdbcaa7ca9166374398033bae --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "himax_platform.h" + +#if defined(CONFIG_FB) +#include +#include +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#endif + +#ifdef CONFIG_OF +#include +#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 0000000000000000000000000000000000000000..f8bee11b43515e3bb3c84124aaaf00766d312741 --- /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='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 0000000000000000000000000000000000000000..91a7ae2eb7abad857bcc254389074a39239fb4f1 --- /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 0000000000000000000000000000000000000000..6ad8dc03149b4b339504ee87e4abeba67dc7d28b --- /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 0000000000000000000000000000000000000000..18cd12b8b36fe3d387537adcbaf1b886c1eca0ed --- /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 + + +#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 0000000000000000000000000000000000000000..7e8a1d6572c5121d42a030d3ff7a8d0cb39e8108 --- /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 0000000000000000000000000000000000000000..1223685683aa081af63e3a4cc4f44b7f8995436b --- /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 +#include +#include +#include +#include +#include +#if defined(CONFIG_HMX_DB) +#include +#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/tsc2007.c b/drivers/input/touchscreen/tsc2007.c index 5d0cd51c6f41d126beef0e6e4bc7580288129cf6..a4b7b4c3d27b29f0aae7286415ec6e3e9b5e44a7 100644 --- a/drivers/input/touchscreen/tsc2007.c +++ b/drivers/input/touchscreen/tsc2007.c @@ -455,6 +455,14 @@ static int tsc2007_probe(struct i2c_client *client, 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 2ee2028bdcefe0db83678944b8e876fc21284127..e1cb1a435358dcd60884763764c701842f90bb88 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 @@ struct page *arm_smmu_errata_get_guard_page(int vmid) 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 8005ab877792a4de56495d7a794820695da6c4af..17193366d28f828f03e09daa6c856bd38146368a 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -292,6 +292,7 @@ enum arm_smmu_s2cr_privcfg { #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 @@ struct arm_smmu_smr { 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 @@ struct arm_smmu_device { 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; @@ -568,6 +579,7 @@ static struct arm_smmu_option_prop arm_smmu_options[] = { { ARM_SMMU_OPT_MMU500_ERRATA1, "qcom,mmu500-errata-1" }, { ARM_SMMU_OPT_STATIC_CB, "qcom,enable-static-cb"}, { ARM_SMMU_OPT_HALT, "qcom,enable-smmu-halt"}, + { ARM_SMMU_OPT_HIBERNATION, "qcom,hibernation-support"}, { 0, NULL}, }; @@ -693,6 +705,11 @@ static void arm_smmu_secure_domain_unlock(struct arm_smmu_domain *smmu_domain) mutex_unlock(&smmu_domain->assign_lock); } +static bool arm_smmu_opt_hibernation(struct arm_smmu_device *smmu) +{ + return smmu->options & ARM_SMMU_OPT_HIBERNATION; +} + /* * init() * Hook for additional device tree parsing at probe time. @@ -1625,17 +1642,76 @@ static int arm_smmu_set_pt_format(struct arm_smmu_domain *smmu_domain, 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 +1721,7 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, 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 +1740,57 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, /* 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)); - /* 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); - } - } else { - reg64 = pgtbl_cfg->arm_lpae_s2_cfg.vttbr; - writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR0); - } + /* + * 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); - /* 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); + /* TTBRs */ + 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 { - reg = pgtbl_cfg->arm_lpae_s2_cfg.vtcr; + writeq_relaxed(cb->ttbr[0], cb_base + ARM_SMMU_CB_TTBR0); + if (stage1) + writeq_relaxed(cb->ttbr[1], cb_base + ARM_SMMU_CB_TTBR1); } - 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 +1865,14 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, 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 +2048,9 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, 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 +2059,6 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, 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 +2106,6 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain) 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 +2137,8 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain) * 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 +3561,16 @@ static int arm_smmu_enable_s1_translations(struct arm_smmu_domain *smmu_domain) { 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 +3757,17 @@ static void qsmmuv2_device_reset(struct arm_smmu_device *smmu) 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 +3832,6 @@ static void arm_smmu_context_bank_reset(struct arm_smmu_device *smmu) 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 +3848,10 @@ static void arm_smmu_context_bank_reset(struct arm_smmu_device *smmu) /* 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 +4008,33 @@ static int arm_smmu_alloc_cb(struct iommu_domain *domain, 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 +4508,10 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) &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 +4734,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) } parse_driver_options(smmu); + parse_static_cb_cfg(smmu); smmu->pwr = arm_smmu_init_power_resources(pdev); if (IS_ERR(smmu->pwr)) @@ -4753,8 +4841,9 @@ static int arm_smmu_device_remove(struct platform_device *pdev) 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 +4857,43 @@ static int arm_smmu_device_remove(struct platform_device *pdev) 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 +4904,7 @@ static int __init arm_smmu_init(void) { static bool registered; int ret = 0; + struct device_node *node; ktime_t cur; if (registered) @@ -4793,9 +4916,12 @@ static int __init arm_smmu_init(void) 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 +5511,14 @@ static void qsmmuv500_init_cb(struct arm_smmu_domain *smmu_domain, 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 +5526,6 @@ static void qsmmuv500_init_cb(struct arm_smmu_domain *smmu_domain, 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 cb72e0011310d1fbd04cb1560a861c8e5ec2fd55..f846f0140a9deadbc2bb7781d280225d60c92a8e 100644 --- a/drivers/iommu/intel-svm.c +++ b/drivers/iommu/intel-svm.c @@ -127,6 +127,7 @@ int intel_svm_enable_prq(struct intel_iommu *iommu) 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 @@ int intel_svm_finish_prq(struct intel_iommu *iommu) 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 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_ 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 d0a8a792050b0580e16e6078de8771bf002bd160..e0314f9ebc3bcd8f7d6dec5676f9d74ad66039c1 100644 --- a/drivers/iommu/io-pgtable-msm-secure.c +++ b/drivers/iommu/io-pgtable-msm-secure.c @@ -47,6 +47,11 @@ struct msm_secure_io_pgtable { 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 @@ int msm_iommu_sec_pgtbl_init(void) 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 @@ int msm_iommu_sec_pgtbl_init(void) 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 @@ int msm_iommu_sec_pgtbl_init(void) 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}; - - desc.args[0] = paddr; - desc.args[1] = psize[0]; - desc.args[2] = 0; - desc.arginfo = SCM_ARGS(3, SCM_RW, SCM_VAL, SCM_VAL); + 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); + 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 e2583cce2cc12bc59c4b08ee8769ee4f423c660b..54556713c8d161cdd2a11c1e5a0955d97e92fa38 100644 --- a/drivers/iommu/omap-iommu.c +++ b/drivers/iommu/omap-iommu.c @@ -1299,6 +1299,7 @@ static int __init omap_iommu_init(void) 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 @@ static int __init omap_iommu_init(void) 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 9ae71804b5dd67bf27322f88b7066f9e646fbdb5..1c2ca8d51a708c9dee4213d671dfca073344069d 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 @@ int gic_configure_irq(unsigned int irq, unsigned int type, 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 @@ int gic_configure_irq(unsigned int irq, unsigned int type, 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 @@ int gic_configure_irq(unsigned int irq, unsigned int type, 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 acb9d250a905993e88c65f43c77d4281f005d7b7..ac15e5d5d9b20e7ef2467128fc6c00b79d6973a6 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -684,7 +684,7 @@ static struct irq_chip its_irq_chip = { * 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 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, 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 2519c9227f31ab35355757a0d2f51be898eb8a8d..2f0f44860dc556d14be7ac01c29883e896649543 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -721,7 +721,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) * 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; @@ -1359,6 +1359,10 @@ gic_acpi_parse_madt_gicc(struct acpi_subtable_header *header, 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 +1412,13 @@ static int __init gic_acpi_match_gicc(struct acpi_subtable_header *header, 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-mbigen.c b/drivers/irqchip/irq-mbigen.c index 03b79b061d245bc40bd4b9b243d3ef79ac8549b2..05d87f60d9290934ea35a45e3d14cb688dd105d9 100644 --- a/drivers/irqchip/irq-mbigen.c +++ b/drivers/irqchip/irq-mbigen.c @@ -105,10 +105,7 @@ static inline void get_mbigen_type_reg(irq_hw_number_t hwirq, 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 d74374f25392237c90a32dee3c05dec64ffd2e29..abf696b49dd73bea04826e0229241cbd36578907 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -55,6 +55,7 @@ static unsigned int gic_cpu_pin; 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 @@ static int gic_irq_domain_alloc(struct irq_domain *d, unsigned int virq, 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 @@ static int gic_irq_domain_alloc(struct irq_domain *d, unsigned int virq, 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 @@ void gic_irq_domain_free(struct irq_domain *d, unsigned int virq, 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 @@ static void __init __gic_init(unsigned long gic_base_addr, 2 * gic_vpes); } + bitmap_copy(ipi_available, ipi_resrv, GIC_MAX_INTRS); gic_basic_init(); } diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c index e3fa1cd64470ce48ba32ab797f4f063c62293f4b..a57b04f749fe32f611baf3ce983dee92281474fb 100644 --- a/drivers/isdn/hardware/mISDN/avmfritz.c +++ b/drivers/isdn/hardware/mISDN/avmfritz.c @@ -156,7 +156,7 @@ _set_debug(struct fritzcard *card) } 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 d5bdbaf93a1afe2d905a21ecad5cca09d637a731..1fc290659e945a5ffdf2efcda8cf687c4bedd42d 100644 --- a/drivers/isdn/hardware/mISDN/mISDNinfineon.c +++ b/drivers/isdn/hardware/mISDN/mISDNinfineon.c @@ -244,7 +244,7 @@ _set_debug(struct inf_hw *card) } 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 afde4edef9ae895fa009fb5ad7305cc13af2c22e..e9fcae4569afc88277ce415b8b2de6775f6c6874 100644 --- a/drivers/isdn/hardware/mISDN/netjet.c +++ b/drivers/isdn/hardware/mISDN/netjet.c @@ -111,7 +111,7 @@ _set_debug(struct tiger_hw *card) } 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 9815bb4eec9c747a6f285377622ca004b2ea0373..1f1446ed8d5f328506e71bce312862a8473c15c9 100644 --- a/drivers/isdn/hardware/mISDN/speedfax.c +++ b/drivers/isdn/hardware/mISDN/speedfax.c @@ -94,7 +94,7 @@ _set_debug(struct sfax_hw *card) } 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 3b067ea656bd9260d0172d7e2387d602d459d170..0db6783a139facf818b979c2deca3cd8bc416895 100644 --- a/drivers/isdn/hardware/mISDN/w6692.c +++ b/drivers/isdn/hardware/mISDN/w6692.c @@ -101,7 +101,7 @@ _set_debug(struct w6692_hw *card) } 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 9cb4b621fbc3cffe0bceb770bde7dbdac292dd95..b92a19a594a108f6ba71126fa05c76ed47b49e01 100644 --- a/drivers/isdn/mISDN/stack.c +++ b/drivers/isdn/mISDN/stack.c @@ -72,7 +72,7 @@ send_socklist(struct mISDN_sock_list *sl, struct sk_buff *skb) 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 787bda3549dbe842f277b84c793a3a748e3f19ba..f931a2cdf214475d9a002ca4d04e358c7d59f320 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -723,6 +723,16 @@ config LEDS_QPNP_VIBRATOR_LDO 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 e9eaa50d0b613bc68b62b4c5894241b2d5637da3..31d29f0e26c78bea58579e330b4678917543060d 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_LEDS_QPNP_FLASH_V2) += leds-qpnp-flash-v2.o leds-qpnp-flash-common. 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-core.c b/drivers/leds/led-core.c index 3bce44893021365a999724a9aff0cf17d462a95f..454ed4dc6ec1d2b93f2c0142c24b696e87f386d0 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -188,6 +188,7 @@ void led_blink_set(struct led_classdev *led_cdev, { 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 840401ae9a4ea210d02d02b2efa7bbdfc015b000..f6726484a8a17c5e4f827480745eb70cf1ed7d22 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -266,7 +266,7 @@ static int pca955x_probe(struct i2c_client *client, "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 a52674327857bcbf01a7a1c9437e0e6878f73f5a..8988ba3b2d655179eb86d771d87e0a0a40ea0921 100644 --- a/drivers/leds/leds-pm8058.c +++ b/drivers/leds/leds-pm8058.c @@ -106,7 +106,7 @@ static int pm8058_led_probe(struct platform_device *pdev) 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 d4b64ba800e602e15c21ad7b1eaf2a1c7713b605..759d8531114c69a9bb27d03a30d99c3861ca8376 100644 --- a/drivers/leds/leds-qpnp-flash-v2.c +++ b/drivers/leds/leds-qpnp-flash-v2.c @@ -353,7 +353,7 @@ static inline int get_current_reg_code(int target_curr_ma, int ires_ua) 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-vibrator.c b/drivers/leds/leds-qpnp-vibrator.c new file mode 100644 index 0000000000000000000000000000000000000000..cc2615daf1c10afcdb603b02572466a5f65772c3 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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/md/bcache/alloc.c b/drivers/md/bcache/alloc.c index 537903bf9add0f9b3c797e7e138254bfb7a906a6..d23337e8c4ee4f79846a37f2b14251f8cef7ea9d 100644 --- a/drivers/md/bcache/alloc.c +++ b/drivers/md/bcache/alloc.c @@ -512,15 +512,21 @@ struct open_bucket { /* * 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 @@ static struct open_bucket *pick_data_bucket(struct cache_set *c, 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 e44816ff13866eb58bc60fe6d732880189b15c6e..e4c2d5da7208025da6f06e83fefb9949d88dba47 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -892,6 +892,12 @@ static void cached_dev_detach_finish(struct work_struct *w) 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 @@ int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c) 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 @@ int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c) 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 @@ static void register_bdev(struct cache_sb *sb, struct page *sb_page, 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 @@ static int register_cache(struct cache_sb *sb, struct page *sb_page, 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 @@ static int register_cache(struct cache_sb *sb, struct page *sb_page, 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 @@ static int register_cache(struct cache_sb *sb, struct page *sb_page, 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 @@ static ssize_t register_bcache(struct kobject *k, struct kobj_attribute *attr, 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 @@ static ssize_t register_bcache(struct kobject *k, struct kobj_attribute *attr, 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 @@ static ssize_t register_bcache(struct kobject *k, struct kobj_attribute *attr, 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-io.c b/drivers/md/dm-io.c index 0bf1a12e35fee2371051afe6e332edf44004963b..ee6045d6c0bb3717baca5317b3695b2a246b9f2d 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -302,6 +302,7 @@ static void do_region(int op, int op_flags, unsigned region, 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 be13ebf94f286a73fd42c4222986a9e01073e7e3..f688bfe36b7265a9d059e13b565dc59587ac4781 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1777,12 +1777,12 @@ static int validate_params(uint cmd, struct dm_ioctl *param) 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 5d0a9963b10855b4165aee50b10c11053b2b0078..d96aa84cacd23f992c7666aa4fc6fadba259071c 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -19,6 +19,7 @@ #include #include +#include #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) @@ -394,6 +396,18 @@ static int verity_bv_zero(struct dm_verity *v, struct dm_verity_io *io, return 0; } +/* + * 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. */ @@ -406,9 +420,16 @@ static int verity_verify_io(struct dm_verity_io *io) 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 @@ static int verity_verify_io(struct dm_verity_io *io) 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 @@ void verity_status(struct dm_target *ti, status_type_t type, 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 @@ void verity_status(struct dm_target *ti, status_type_t type, } 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 @@ void verity_dtr(struct dm_target *ti) 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 @@ void verity_dtr(struct dm_target *ti) } 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 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v) } 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 @@ EXPORT_SYMBOL_GPL(verity_ctr); 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 75effca400a3b170ae436334f0c961e335bd9096..6d6d8df5b58c1988b3600edc15197f1e0e8bfe47 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -63,6 +63,7 @@ struct dm_verity { 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 ba7edcdd09cec7f607d7b9f203ba5417fe20f10d..fcc2b5746a9f25c63be2b9ad2fb55336f925c4dd 100644 --- a/drivers/md/md-cluster.c +++ b/drivers/md/md-cluster.c @@ -1122,8 +1122,10 @@ static int add_new_disk(struct mddev *mddev, struct md_rdev *rdev) 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 df7d6063929f5e89f4b4406940b72b6e9118f94d..93059dd74737825baa684f00a34cb48001b8a385 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -4826,8 +4826,10 @@ array_size_store(struct mddev *mddev, const char *buf, size_t len) 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 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data) 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 @@ static int remove_and_add_spares(struct mddev *mddev, 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 @@ static __exit void md_exit(void) 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 8d402382b53c77287bc692a3e9a1eccbd8eb5612..2b04c720677041f901ce6db937dc37e6b9053692 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -2704,6 +2704,11 @@ static void handle_write_completed(struct r10conf *conf, struct r10bio *r10_bio) 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 @@ static int raid10_run(struct mddev *mddev) if (blk_queue_discard(bdev_get_queue(rdev->bdev))) discard_supported = true; + first = 0; } if (mddev->queue) { @@ -4084,6 +4090,7 @@ static int raid10_start_reshape(struct mddev *mddev) 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 90d863eea0be94fda2a2dec99b560c78f00dce0e..a7549a4b9b4d542f34704441338c114b41fdd76f 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -110,8 +110,7 @@ static inline void unlock_device_hash_lock(struct r5conf *conf, int hash) 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 @@ static inline void unlock_all_device_hash_locks_irq(struct r5conf *conf) { 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 bool is_full_stripe_write(struct stripe_head *sh) 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 lock_two_stripes(struct stripe_head *sh1, struct stripe_head *sh2) 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 @@ static int fetch_block(struct stripe_head *sh, struct stripe_head_state *s, 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 b5b5b195ea7f04d1feb40b2967b43cb174b1bf7e..42ce88ceac66d914cee62a1a68b67f33db139181 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -779,6 +779,29 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * b 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 e0fe5bc9dbce358a9dd5b07faa554d17d10290b3..31f16105184c036828dea3c99166493cd82886f6 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -1262,11 +1262,12 @@ static int m88ds3103_select(struct i2c_mux_core *muxc, u32 chan) * 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 @@ static int m88ds3103_probe(struct i2c_client *client, 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 20b4a659e2e459dd9f536566f60c8fdda300eaa4..0fe69f79a24d64cca23eb2c322d3d43066081e5d 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 + #include "si2168_priv.h" static const struct dvb_frontend_ops si2168_ops; @@ -378,6 +380,7 @@ static int si2168_init(struct dvb_frontend *fe) 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 142ae28803bb859d5d375976867f9c2badd5ad9c..d558ed3e59c61b3f4a3fc105d5721a47d1d56ed1 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -420,11 +420,13 @@ static void cx25840_initialize(struct i2c_client *client) 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 @@ static void cx23885_initialize(struct i2c_client *client) 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 @@ static void cx231xx_initialize(struct i2c_client *client) 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 4bf2995e1cb802e8c0fd6ed59a41aa6253e30d01..8f85910eda5df9ddb430e37b5a2b6828c8648b0a 100644 --- a/drivers/media/i2c/soc_camera/ov6650.c +++ b/drivers/media/i2c/soc_camera/ov6650.c @@ -1033,7 +1033,7 @@ static int ov6650_probe(struct i2c_client *client, 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 26d999c812c9ad7d838d17c684a70f5ca1a0f700..0f572bff64f5f48a9395683ee4e7eda59c00cc50 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -222,7 +222,7 @@ static void i2c_wr8(struct v4l2_subdev *sd, u16 reg, u8 val) 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 4462d8c69d575f0bb70b8651111199bebbaa743e..cc8de56682275bad35cbf8a7c75eedef46083e51 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -58,9 +58,10 @@ static int media_device_close(struct file *filp) 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 @@ static struct media_entity *find_entity(struct media_device *mdev, u32 id) 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 @@ static void media_device_kpad_to_upad(const struct media_pad *kpad, 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 @@ static long media_device_enum_links(struct media_device *mdev, 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 @@ static long media_device_setup_link(struct media_device *mdev, 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 8aa72665163010cd5bc2c230a6636bc1d8afe463..90fcccc05b5626f0932af2a3541b40ad36e98136 100644 --- a/drivers/media/pci/bt8xx/bt878.c +++ b/drivers/media/pci/bt8xx/bt878.c @@ -422,8 +422,7 @@ static int bt878_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) 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/solo6x10/solo6x10-v4l2.c b/drivers/media/pci/solo6x10/solo6x10-v4l2.c index b4be47969b6b56913454c2441a6bf91c28cfeeb5..e17d6b945c07223904f465d70d7d7949adca761a 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2.c @@ -341,6 +341,17 @@ static void solo_stop_streaming(struct vb2_queue *q) 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 71a0453b1af15dabea389e82fd22111d3f269dce..279d4478c2a1560ba07614600a95b9d9f1336fb7 100644 --- a/drivers/media/pci/tw686x/tw686x-core.c +++ b/drivers/media/pci/tw686x/tw686x-core.c @@ -72,12 +72,12 @@ static const char *dma_mode_name(unsigned int mode) } } -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_hw_mgr_intf.h b/drivers/media/platform/msm/camera/cam_core/cam_hw_mgr_intf.h index 4168ce62ce91863e07dfd59268c4ccc691439343..f7990b6d5d4a4f2b77539a794641d1f6d982b3d6 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 @@ struct cam_hw_config_args { 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 a943680215e2c3fc19882b25a6bc46aca92a4bbb..0a9fabcf8d5e3101703567c6955382a2d8196972 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 @@ static int __cam_node_handle_acquire_dev(struct cam_node *node, 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 @@ static int __cam_node_handle_acquire_dev(struct cam_node *node, 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 @@ static int __cam_node_handle_release_dev(struct cam_node *node, 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_icp/fw_inc/hfi_intf.h b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_intf.h index f5567801d95ff03ca5486a688c0bd2a80669edc9..2c364e0181e6a3709c66e997dad58d1a7aed4c9b 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 @@ -120,6 +120,12 @@ void cam_hfi_deinit(void __iomem *icp_base); */ 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 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 91190b6fa420f3bd411cba33cecad4be14b68779..311886ffd6da1bb372c651921ba55dcc9e1fba79 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 b75719b78c827607bef4146e2bb1f26c4adcdfe0..a0752f596c9647396baf18e483f6ee9fc9b91097 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 @@ int hfi_set_debug_level(u64 a5_dbg_type, uint32_t lvl) 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 ba4385f5ca0213e50c3e482ecff7a4e686c87a47..16e97ea1f8133adbaa6dbbe55ada288f3963c9ca 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 @@ static int cam_icp_get_a5_dbg_type(void *data, u64 *val) 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 @@ static int cam_icp_hw_mgr_create_debugfs_entry(void) 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 @@ static int cam_icp_hw_mgr_create_debugfs_entry(void) 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 @@ static int cam_icp_mgr_abort_handle( 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 @@ static int cam_icp_mgr_abort_handle( 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 @@ static int cam_icp_mgr_abort_handle( CAM_ERR(CAM_ICP, "FW timeout/err in abort handle command"); } + kfree(abort_cmd); return rc; } @@ -2322,13 +2336,7 @@ static int cam_icp_mgr_destroy_handle( 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 @@ static int cam_icp_mgr_destroy_handle( 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 @@ static int cam_icp_mgr_destroy_handle( HFI_DEBUG_MODE_QUEUE) cam_icp_mgr_process_dbg_buf(); } + kfree(destroy_cmd); return rc; } @@ -3866,6 +3869,8 @@ static int cam_icp_mgr_acquire_hw(void *hw_mgr_priv, void *acquire_hw_args) 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 c94550d770b180cb5c1bc8b2faba1c6d2be182e5..8746ee264f31f9d66eaddbb7702f8f9ffd4dd378 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 @@ struct cam_icp_clk_info { * @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 @@ struct cam_icp_hw_mgr { 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 97e977d0c83af864715347817ed45c196367bdf0..f9985eb882bc73a6bd8bed36dc1a3f3a494ab58d 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 @@ -1244,6 +1244,7 @@ static int __cam_isp_ctx_apply_req_in_activated_state( 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) { @@ -2374,6 +2375,7 @@ static int __cam_isp_ctx_start_dev_in_ready(struct cam_context *ctx, 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; 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 0a127eff35a71243d003faf6c0be2c2abd6a4b02..38a449796a18febd22ef07f35ca7c2a8218ef2d8 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 @@ -1611,7 +1611,7 @@ static int cam_ife_mgr_config_hw(void *hw_mgr_priv, 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 +1621,7 @@ static int cam_ife_mgr_config_hw(void *hw_mgr_priv, 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 1d38f3bb1cb6f81ccba71db8dd9f39bd1f425211..e869e2bdc9184551541f275ad5c990640aea02fe 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 @@ int cam_isp_add_io_buffers( 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 8863275b632312e1d3c2a52917ed1ab25195249d..3a897328eeb3f1ea912dcf354dd42d09b901b273 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 @@ static void cam_tasklet_flush(void *tasklet_info) 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 @@ int cam_tasklet_start(void *tasklet_info) } 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 053eb00afe4c3937410664486a250e7e3dee13b9..d20450c38511238bf048b51c0da16a57ff8a5111 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 @@ -989,10 +989,6 @@ static int cam_ife_csid_enable_hw(struct cam_ife_csid_hw *csid_hw) 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", @@ -2171,25 +2167,38 @@ static int cam_ife_csid_reset_retain_sw_reg( 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; + + 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); - init_completion(&csid_hw->csid_top_complete); 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 +2538,6 @@ irqreturn_t cam_ife_csid_irq(int irq_num, void *data) 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 +2558,6 @@ irqreturn_t cam_ife_csid_irq(int irq_num, void *data) 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); 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 be4db8a9d39d458efac04fe8737e2e3825596854..3c37b83a22561e9e3dcc16c5294ef1b47732c50c 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_ver2_common_data { 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 @@ static int cam_vfe_bus_get_evt_payload( 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 @@ static int cam_vfe_bus_put_evt_payload(void *core_info, 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 @@ static int cam_vfe_bus_put_evt_payload(void *core_info, } 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 @@ int cam_vfe_bus_ver2_init( } } + 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_req_mgr/cam_req_mgr_core.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c index aaba48112821f819f161d2e15037a5176e2d33b0..adfac57d367898e96ade57a7e5a6f70636e28444 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 @@ -183,6 +183,7 @@ static int __cam_req_mgr_traverse(struct cam_req_mgr_traverse *traverse_data) 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,17 +194,18 @@ static int __cam_req_mgr_traverse(struct cam_req_mgr_traverse *traverse_data) 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) && + if ((slot->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--; + slot->inject_delay--; /* This pd table is not ready to proceed with asked idx */ SET_FAILURE_BIT(traverse_data->result, tbl->pd); return -EAGAIN; @@ -230,7 +232,7 @@ static int __cam_req_mgr_traverse(struct cam_req_mgr_traverse *traverse_data) 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 +250,11 @@ static int __cam_req_mgr_traverse(struct cam_req_mgr_traverse *traverse_data) } } 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 +457,9 @@ static int __cam_req_mgr_send_req(struct cam_req_mgr_core_link *link, rc = dev->ops->apply_req(&apply_req); if (rc < 0) break; + + if (pd == link->max_delay) + link->open_req_cnt--; } } } @@ -460,6 +470,7 @@ static int __cam_req_mgr_send_req(struct cam_req_mgr_core_link *link, 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 +520,7 @@ static int __cam_req_mgr_check_link_is_ready(struct cam_req_mgr_core_link *link, traverse_data.result = 0; traverse_data.validate_only = validate_only; traverse_data.self_link = self_link; + traverse_data.open_req_cnt = link->open_req_cnt; /* * Traverse through all pd tables, if result is success, * apply the settings @@ -1513,6 +1525,7 @@ int cam_req_mgr_process_sched_req(void *priv, void *data) 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 +1595,13 @@ int cam_req_mgr_process_add_req(void *priv, void *data) 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 e15b3b0294bcf5f349cac0854832148caee7a6ed..73ffb81dd9534afc4acf9f2e5780d7cad78ed9ab 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 @@ -134,6 +134,7 @@ enum cam_req_mgr_link_state { * @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 + * @open_req_cnt : Count of open requests yet to be serviced in the kernel. */ struct cam_req_mgr_traverse { int32_t idx; @@ -143,6 +144,7 @@ struct cam_req_mgr_traverse { struct cam_req_mgr_req_queue *in_q; bool validate_only; bool self_link; + int32_t open_req_cnt; }; /** @@ -164,11 +166,13 @@ struct cam_req_mgr_apply { * @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 +187,6 @@ struct cam_req_mgr_tbl_slot { * @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 +198,6 @@ struct cam_req_mgr_req_tbl { int32_t pd_delta; int32_t num_slots; struct cam_req_mgr_tbl_slot slot[MAX_REQ_SLOTS]; - uint32_t inject_delay; }; /** @@ -301,6 +303,8 @@ struct cam_req_mgr_connected_device { * @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 +328,7 @@ struct cam_req_mgr_core_link { 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_sensor_module/cam_actuator/cam_actuator_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c index 56cb49a1baa35c54c523d99a65c296842dfc364d..2f74765f1437fa4d6852d87db41d220bbed3365f 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 @@ -636,6 +636,12 @@ int32_t cam_actuator_driver_cmd(struct cam_actuator_ctrl_t *a_ctrl, 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 +775,7 @@ int32_t cam_actuator_driver_cmd(struct cam_actuator_ctrl_t *a_ctrl, 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_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c index fb37526fb0d289d0a73f59070f06eaa575099485..da08bc7ab037e02938e4f7579f20b830df0032cc 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; 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 d25964e4def1e2451da5d3bafa588b6e056d1d31..7cde6190f9db179a86d346abb79030ab2c9347aa 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 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 e978a40bdddd248dbce681cd7b12ea10e1a9f64a..dbbac08a5879770b044cd662825bfd567b27681d 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 @@ int32_t cam_csiphy_core_cfg(void *phy_dev, 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) { 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 b0fbead43a434dc3711236cc52caede7bfff4263..30b9d967c4058bb61124a05be28d43ebf6e8a784 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 @@ -707,11 +707,6 @@ static int32_t cam_eeprom_pkt_parse(struct cam_eeprom_ctrl_t *e_ctrl, void *arg) 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; @@ -852,8 +847,14 @@ int32_t cam_eeprom_driver_cmd(struct cam_eeprom_ctrl_t *e_ctrl, void *arg) 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 6120e028d124bf61592dc58af6f6c07aa3cdd5f4..f9411fcfa4b35e1a17c8fc300231cf0ef83eac7f 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 @@ static int32_t cam_flash_driver_cmd(struct cam_flash_ctrl *fctrl, 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 c792be49b6e17a0aa32c969bc230a5c6240b897e..196df084d40b83a6f6f24fbe02d720aac4cfa931 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 @@ -672,8 +672,14 @@ int cam_ois_driver_cmd(struct cam_ois_ctrl_t *o_ctrl, void *arg) 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 e9dd1ade51077f886f263bc719950fdb2aa5a33e..d58834c3d719b49d22f9c1fd735abf07b4049a98 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 @@ -568,6 +568,14 @@ int32_t cam_sensor_driver_cmd(struct cam_sensor_ctrl_t *s_ctrl, 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: { @@ -607,6 +615,7 @@ int32_t cam_sensor_driver_cmd(struct cam_sensor_ctrl_t *s_ctrl, } else { CAM_ERR(CAM_SENSOR, "Invalid Command Type: %d", cmd->handle_type); + return -EINVAL; } /* Parse and fill vreg params for powerup settings */ @@ -721,8 +730,9 @@ int32_t cam_sensor_driver_cmd(struct cam_sensor_ctrl_t *s_ctrl, 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 +771,9 @@ int32_t cam_sensor_driver_cmd(struct cam_sensor_ctrl_t *s_ctrl, 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 +812,9 @@ int32_t cam_sensor_driver_cmd(struct cam_sensor_ctrl_t *s_ctrl, } 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 +839,9 @@ int32_t cam_sensor_driver_cmd(struct cam_sensor_ctrl_t *s_ctrl, 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_utils/cam_sensor_util.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c index a3999631185419a993b98e12684a843e818a9935..10d29c91f03e65d0f5cdb12fe187838d26543d77 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 @@ -1303,6 +1303,11 @@ int cam_sensor_core_power_up(struct cam_sensor_power_ctrl_t *ctrl, 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"); + return -EINVAL; + } + CAM_DBG(CAM_SENSOR, "seq_type %d", power_setting->seq_type); switch (power_setting->seq_type) { @@ -1589,6 +1594,11 @@ static int cam_config_mclk_reg(struct cam_sensor_power_ctrl_t *ctrl, pd = &ctrl->power_down_setting[index]; + if (!pd) { + CAM_ERR(CAM_SENSOR, "Invalid power down setting"); + return -EINVAL; + } + for (j = 0; j < num_vreg; j++) { if (!strcmp(soc_info->rgltr_name[j], "cam_clk")) { 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 58a43904006eccbc483cabe440d1e0d3b5abd8fd..6fc8e1e87af288cc669ed73b0654db8c384025ff 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 #include #include +#include #include "msm_csiphy.h" #include "msm_sd.h" #include "include/msm_csiphy_2_0_hwreg.h" @@ -179,12 +180,13 @@ static int msm_csiphy_snps_2_lane_config( 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 +194,14 @@ static int msm_csiphy_snps_2_lane_config( 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,7 +211,7 @@ static int msm_csiphy_snps_2_lane_config( 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; @@ -290,10 +293,18 @@ static int msm_csiphy_snps_lane_config( 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 @@ -315,10 +326,13 @@ static int msm_csiphy_snps_lane_config( return -EINVAL; } 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) { + if (csiphy_dev->snps_state == NOT_CONFIGURED) { mode = TWO_LANE_PHY_A; csiphy_dev->snps_state = CONFIGURED_TWO_LANE_PHY_A; } else if (csiphy_dev->snps_state == @@ -330,10 +344,12 @@ static int msm_csiphy_snps_lane_config( pr_err("%s: invalid request\n", __func__); return -EINVAL; } + 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) { + if (csiphy_dev->snps_state == NOT_CONFIGURED) { mode = TWO_LANE_PHY_B; csiphy_dev->snps_state = CONFIGURED_TWO_LANE_PHY_B; } else if (csiphy_dev->snps_state == @@ -345,11 +361,17 @@ static int msm_csiphy_snps_lane_config( pr_err("%s: invalid request\n", __func__); return -EINVAL; } + 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); @@ -1233,7 +1255,7 @@ static int msm_csiphy_lane_config(struct csiphy_device *csiphy_dev, 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); 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 f3814e0cb9c82f11e9ffd330500db1ae872fc9ac..ce50dcda961d7cd216dfb876a3dff1f4b08a5668 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_resume(struct platform_device *dev) */ 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 8421873a929743cf74abb84a782571d4580eae88..f6fdb9072503c835b03cc933c9d867fd6344e44c 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_queue { 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 sde_rot_entry { 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 sde_rot_file_private { 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 @@ void sde_rotator_core_dump(struct sde_rot_mgr *mgr); */ 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 a46194f62ada6844f931c6d98437d57f6709954e..48ef46c3be04008421dac614f0f23c6adc3fdc7c 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 @@ struct sde_rotator_ctx *sde_rotator_ctx_open( 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 @@ struct sde_rotator_ctx *sde_rotator_ctx_open( 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 @@ struct sde_rotator_ctx *sde_rotator_ctx_open( 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; + } + } + + if (ctx->kthread_id < 0) { + SDEDEV_ERR(ctx->rot_dev->dev, + "fail to acquire the kthread\n"); + ret = -EINVAL; + goto error_alloc_kthread; } - SDEDEV_DBG(ctx->rot_dev->dev, "kthread name=%s\n", name); snprintf(name, sizeof(name), "%d_%d", rot_dev->dev->id, ctx->session_id); @@ -1069,13 +1075,17 @@ struct sde_rotator_ctx *sde_rotator_ctx_open( 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 @@ struct sde_rotator_ctx *sde_rotator_ctx_open( mutex_unlock(&rot_dev->lock); error_lock: kfree(ctx); + ctx = NULL; return ERR_PTR(ret); } @@ -1096,10 +1107,18 @@ struct sde_rotator_ctx *sde_rotator_ctx_open( 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 @@ static int sde_rotator_ctx_release(struct sde_rotator_ctx *ctx, 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 @@ static int sde_rotator_ctx_release(struct sde_rotator_ctx *ctx, 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 @@ int sde_rotator_inline_commit(void *handle, struct sde_rotator_inline_cmd *cmd, } 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 @@ static int sde_rotator_process_buffers(struct sde_rotator_ctx *ctx, 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 @@ static int sde_rotator_job_ready(void *priv) 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 @@ static int sde_rotator_probe(struct platform_device *pdev) { 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 @@ static int sde_rotator_probe(struct platform_device *pdev) 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_probe(struct platform_device *pdev) 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 @@ static int sde_rotator_remove(struct platform_device *pdev) } 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 ab27043ce0db8978673dec73093cf582a4da3c7b..f81801675c83a6b7fa4f9a896101a3893b91e210 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 @@ struct sde_rotator_request { * @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_ctx { 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 sde_rotator_ctx { 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 @@ struct sde_rotator_statistics { * @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 @@ struct sde_rotator_device { 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 e3a5cc723765a984afaa9f6aab0f106b58dbfe92..9ef9d05d92f43d1df23110c1a46f3e12f4a1be8d 100644 --- a/drivers/media/platform/msm/vidc/hfi_packetization.c +++ b/drivers/media/platform/msm/vidc/hfi_packetization.c @@ -1239,6 +1239,51 @@ int create_pkt_cmd_session_set_property( 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 @@ int create_pkt_cmd_session_set_property( 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 97f5ca2ac1e1682ca44067445edf61734f1547bf..0cdcbd6e0df1b383d1958db29933a082568dd41e 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 @@ enum hal_capability get_hal_cap_type(u32 capability_type) 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 @@ static inline void copy_cap_prop( 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 be24f8df68e33d091e7d26b8180a37fdf611520c..9a7d272bd629d1256ae0f8bb0499fec86925a4c1 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 @@ static const char *const mpeg_video_rate_control[] = { "CBR CFR", "MBR CFR", "MBR VFR", + "CQ", NULL }; @@ -358,7 +367,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { .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,10 +377,34 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { (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", @@ -1383,6 +1416,8 @@ int msm_venc_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) 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 @@ int msm_venc_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) 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,67 @@ int msm_venc_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) 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: + { + int i = 0, j = 0; + u32 width = 0, height = 0; + u32 trows, tcols; + + 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; + + /* Update tile info table */ + 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 exceeds maximum\n"); + rc = -ENOTSUPP; + break; + } + + 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; + } + } + break; + } case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: { struct v4l2_ctrl *avg_bitrate = TRY_GET_CTRL( diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c index f077e3bc7c7af56b6eb37c607b4278ace43918d9..8e3250465e8fbfcf97fc47b80cf32b9cd24bb589 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc.c +++ b/drivers/media/platform/msm/vidc/msm_vidc.c @@ -1480,7 +1480,9 @@ static int try_get_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) 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 4a0aa581bd3cf413ddda00f8544337e05ba3853d..6ae030f8ef271b6436ccb3e722f1bf92134ef39d 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 @@ static int fill_dynamic_stats(struct msm_vidc_inst *inst, { 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); diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 7d9bbed94599f18bdafd57c90e94d48972900418..f9bc79c877906fcdd2185ca09e7c59d6d73328be 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 @@ static void handle_session_init_done(enum hal_command_response cmd, void *data) 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 @@ static void handle_session_flush(enum hal_command_response cmd, void *data) 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 @@ static void handle_session_flush(enum hal_command_response cmd, void *data) 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); @@ -2397,6 +2406,14 @@ static void handle_ebd(enum hal_command_response cmd, void *data) } empty_buf_done = (struct vidc_hal_ebd *)&response->input_done; + if ((get_hal_codec(inst->fmts[CAPTURE_PORT].fourcc) == + HAL_VIDEO_CODEC_HEVC) && + (inst->img_grid_dimension > 0) && + (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", @@ -4148,6 +4165,58 @@ enum hal_buffer get_hal_buffer_type(unsigned int type, } } +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 +4424,26 @@ int msm_comm_qbuf(struct msm_vidc_inst *inst, struct msm_vidc_buffer *mbuf) 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 (get_hal_codec(inst->fmts[CAPTURE_PORT].fourcc) == + HAL_VIDEO_CODEC_HEVC && + (inst->img_grid_dimension > 0)) { + 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, diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h index 60cdd183f3a41d5be2b847eacea7dbe0d4ac6846..e107609ea41d2ab5e4219f2cb30b25fff7a4d15f 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 @@ struct session_crop { 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 @@ struct msm_vidc_inst { 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/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c index 7e7ed4713485237fabc428c40a26d1697e6bca5a..ade04e32c32015351301b2f144e279c707584c46 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 @@ struct tzbsp_resp { /* 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, @@ -2850,12 +2853,52 @@ static void venus_hfi_pm_handler(struct work_struct *work) 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); diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h index e899af363d2aa8b0985124f3d1cc631cb2ff66f2..0cfe6934520ad94199cd067bce11af6b690a40a1 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 @@ struct hfi_hybrid_hierp { #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 9290c487137b1ef5ce9958babe05f416dbb4135e..f69b98e828b6d5c6c4df707fe9ccf72042170429 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 @@ enum hal_property { 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 @@ enum hal_rate_control { 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 @@ struct hal_idr_period { 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 @@ enum hal_capability { 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 @@ struct hal_aspect_ratio { 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 msm_vidc_capability { 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 7615736e78bfe4360df7fc601933f3a8f9662725..f4ea86b6e654dccb6de5da1698dea6d6f95bf568 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 @@ struct hfi_buffer_info { (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 @@ struct hfi_colour_space { 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 @@ struct hfi_colour_space { #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,14 @@ struct hfi_frame_rate { u32 frame_rate; }; +struct hfi_heic_frame_quality { + u32 frame_quality; +}; + +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 343348395c5400bbdf082f0b15ad716f7a41545f..3ddd6336acd0a532f19de17ac8bda38b4ab69efe 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/msm_venc.c b/drivers/media/platform/msm/vidc_3x/msm_venc.c index f9760cfa4040c5c8a0e593e687f3ba7adea58f37..d129dc24cb1a649146e58087125c84353ff0dd58 100644 --- a/drivers/media/platform/msm/vidc_3x/msm_venc.c +++ b/drivers/media/platform/msm/vidc_3x/msm_venc.c @@ -2278,6 +2278,21 @@ static inline int venc_v4l2_to_hal(int id, int value) 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 +3153,10 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) } 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 +3175,9 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) 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 +3196,9 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) 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 +3217,9 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) 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 4dcb3b133a008b2f8008748ef7b638b3ff23f672..bd581175008262a248e37e7a396199c5888c747d 100644 --- a/drivers/media/platform/msm/vidc_3x/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc_3x/msm_vidc_common.c @@ -2474,7 +2474,6 @@ static int msm_comm_session_abort(struct msm_vidc_inst *inst) } 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 +2631,6 @@ static int msm_comm_init_core(struct msm_vidc_inst *inst) __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 +2734,6 @@ static int msm_comm_session_init(int flipped_state, 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 +2872,6 @@ static int msm_vidc_start(int flipped_state, struct msm_vidc_inst *inst) 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 +2901,6 @@ static int msm_vidc_stop(int flipped_state, struct msm_vidc_inst *inst) 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 +2930,6 @@ static int msm_vidc_release_res(int flipped_state, struct msm_vidc_inst *inst) } 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 +2960,6 @@ static int msm_comm_session_close(int flipped_state, } 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 +4008,6 @@ int msm_comm_try_get_prop(struct msm_vidc_inst *inst, enum hal_property ptype, } 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 +4233,6 @@ int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst, 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 +4304,6 @@ int msm_comm_release_persist_buffers(struct msm_vidc_inst *inst) 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) { diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index c12209c701d38093b01cdeb05526fa77795ed654..390d708c807a0e1ad60acea862c146f45daeb0b8 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -2169,6 +2169,12 @@ static void pxa_camera_sensor_unbind(struct v4l2_async_notifier *notifier, 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 @@ static int pxa_camera_remove(struct platform_device *pdev) 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/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c index 30c148b9d65e8c74eb7e8acfd8f9a070d5ef0750..06e2cfd09855fa4d8e5d98996fb4960ae2e92d18 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 c8sectpfe_timer_interrupt(unsigned long ac8sectpfei) 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 @@ static void channel_swdemux_tsklet(unsigned long data) 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/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index cd209dccff1bd0f8f9679b124f619a59b831a5e3..8e2aa3f8e52f50631f6e9dcf3bec2c42c6476d32 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -596,6 +596,7 @@ int vsp1_drm_init(struct vsp1_device *vsp1) 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 57c713a4e1df7ed10722b6d1fb91eb1651e8a6ff..4ac1ff482a0b35146d76d1b233a3d7bdaf7df890 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -509,7 +509,13 @@ static int __maybe_unused vsp1_pm_suspend(struct device *dev) { 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 @@ static int __maybe_unused vsp1_pm_resume(struct device *dev) 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 d351b9c768d2c2c4623db129b82ec3602083dac3..743aa0febc0992338bd7fa54fd3ba97343ddbd4f 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -792,6 +792,7 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) { 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 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) 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 98afd19fe33867b94f768e751a666141b5cba081..b03cb27599e05ad65d8fb04e1723d75b2441955e 100644 --- a/drivers/media/radio/radio-iris-transport.c +++ b/drivers/media/radio/radio-iris-transport.c @@ -40,7 +40,7 @@ struct radio_data hs; 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 @@ static void radio_hci_smd_exit(void) 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 db525cdfac88f56479e186538db1f85e97aa481f..d9f88a4a96bd1677c5f891bf14be52184bf8d460 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -1381,8 +1381,13 @@ static int mceusb_dev_probe(struct usb_interface *intf, 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 9caea8344547a790db13b04690d5d5c14aaf9451..d793c630f1dde32010c0b48963919ca0fe5dce7f 100644 --- a/drivers/media/usb/cpia2/cpia2_v4l.c +++ b/drivers/media/usb/cpia2/cpia2_v4l.c @@ -812,7 +812,7 @@ static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) 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 @@ static int cpia2_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) 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/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c index 0324633ede42e80c11c1e8be1a260ec4546bd20a..e56a49a5e8b1aa5dfd30d006b479b54b7ab0ba8a 100644 --- a/drivers/media/usb/usbtv/usbtv-core.c +++ b/drivers/media/usb/usbtv/usbtv-core.c @@ -109,6 +109,8 @@ static int usbtv_probe(struct usb_interface *intf, 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 cde43b63c3dabe402b85753ccafa2432c2e29641..8412dfc02b5804fba75e89b54fc7c18a9aa1caef 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2184,7 +2184,7 @@ static int uvc_reset_resume(struct usb_interface *intf) * 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 @@ static int uvc_clock_param_get(char *buffer, struct kernel_param *kp) 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 9eaab9852b994e38a50172ad96972bc90bd7d020..d4e93f1501f1ebf8ab79421d111b70722ab17136 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 get_v4l2_window32(struct v4l2_window __user *kp, 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 @@ static int put_v4l2_window32(struct v4l2_window __user *kp, 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 d4ee1408bc50d4ca1a5856cca86896875a305635..9ae687bf5e67cde3d3acb5e144780924f1db7435 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2548,11 +2548,8 @@ struct v4l2_ioctl_info { 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 @@ struct v4l2_ioctl_info { #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 @@ struct v4l2_ioctl_info { .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 @@ static long __video_do_ioctl(struct file *file, } 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 1db0af6c7f94810bf9270b0ebb5961de6b1802a5..b6189a4958c5ec2d40cfd8486e69794ba9be9bee 100644 --- a/drivers/media/v4l2-core/videobuf-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf-dma-sg.c @@ -185,12 +185,13 @@ static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma, 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 9ccf7f5e0e2e15ba162d152c9b64295f533167e0..4299ce06c25b17ff5b5a0e404ba53167de4afcc6 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -334,6 +334,10 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, 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/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index 89c7ed16b4df13057fcadb712ee369316c514ac6..1cdab6d00b43493c36c151e4bb94b82b1d281f10 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -99,7 +99,7 @@ module_param(mpt_channel_mapping, int, 0); 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_enable_io_access(struct pci_dev *pdev) 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/mfd/palmas.c b/drivers/mfd/palmas.c index 8f8bacb67a15a4608de39efa80f30a0f7a550d02..a6b5259ffbddbfd97608f2b3f12e23ed79b1fd1c 100644 --- a/drivers/mfd/palmas.c +++ b/drivers/mfd/palmas.c @@ -430,6 +430,20 @@ static void palmas_power_off(void) { 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/Makefile b/drivers/misc/Makefile index fb07ce4c44dff2bba62c3f0d9baa6fde0bcf2aa5..f1c94677a97e98c529150af3eabf54dd5667d09a 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -69,6 +69,9 @@ lkdtm-$(CONFIG_LKDTM) += lkdtm_perms.o 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 c63d61e17d565cbecef9dba8c5ad9e9e434e37f2..381a9a166f9338a33c81c9ed6bd61386bb0a923f 100644 --- a/drivers/misc/cxl/flash.c +++ b/drivers/misc/cxl/flash.c @@ -401,8 +401,10 @@ static int device_open(struct inode *inode, struct file *file) 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 @@ static int device_open(struct inode *inode, struct file *file) 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 cc91f7b3d90ca76ef0193e573d25389e93d7e42c..eb29113e0bac2437f1714af1e4189b74fe992b6f 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -148,7 +148,7 @@ enclosure_register(struct device *dev, const char *name, int components, 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 @@ static ssize_t get_component_power_status(struct device *cdev, 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/kgdbts.c b/drivers/misc/kgdbts.c index 99635dd9dbac7b716ec9a889a34e17817bcb7bfb..2290845eaba552083efdc50d4008181a0bb6f6ee 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c @@ -1130,7 +1130,8 @@ static void kgdbts_put_char(u8 chr) 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 41f318631c6d808ee8f3e85ffd4c76c52d9e2368..60f5a8ded8dd938c4951b882c43bf234ed8ad999 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -551,7 +551,6 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) 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 c0143db8ca9d03518359581a311d726f46efb65b..2b31ed3f981d99f7d8de752d2944cc0dd248c21c 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -4510,6 +4510,7 @@ int qseecom_start_app(struct qseecom_handle **handle, 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; diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 728c65ab9a4dc2f6ff691c55a81f0bf0ad5d8504..a940f977fe31ce2eb1944a54dc5d8cdfeeb0ce71 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -14,6 +14,7 @@ */ #include +#include #include #include #include @@ -422,6 +423,10 @@ static ssize_t uid_remove_write(struct file *file, 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 f84a4275ca294af9fb2ca8b6ac15bcb9b20bf69f..f735ab4ba84e7e80528cd74974fb66cc368693a8 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c @@ -298,8 +298,11 @@ static void *qp_alloc_queue(u64 size, u32 flags) 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 @@ static struct vmci_queue *qp_host_alloc_queue(u64 size) { 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/core/core.c b/drivers/mmc/core/core.c index 1b961a975ddb6c18a2023a63c3a8404abf09886a..8700e72b3bb8a9b81d2b993fe43c8c51a5e80913 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -4706,9 +4706,11 @@ static int mmc_pm_notify(struct notifier_block *notify_block, int err = 0; switch (mode) { + 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: - case PM_RESTORE_PREPARE: spin_lock_irqsave(&host->lock, flags); host->rescan_disable = 1; spin_unlock_irqrestore(&host->lock, flags); @@ -4723,6 +4725,14 @@ static int mmc_pm_notify(struct notifier_block *notify_block, 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 +4742,11 @@ static int mmc_pm_notify(struct notifier_block *notify_block, 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 ff4f84fdab38468a797ff55e4659111f19a57298..99165f7ac961d9c4ef53c20d86bca4d44582f810 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -3058,6 +3058,73 @@ static int mmc_shutdown(struct mmc_host *host) 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 @@ static const struct mmc_bus_ops mmc_ops = { .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/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index f81f4175f49ab686f4d8fb452af3c7a23a9bd2c8..d382dbd446359b76618fc1acbb1780825863f18c 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -490,6 +490,7 @@ static int dw_mci_idmac_init(struct dw_mci *host) (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 @@ static int dw_mci_idmac_init(struct dw_mci *host) 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 @@ static bool dw_mci_reset(struct dw_mci *host) } 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 684087db170b218c45dfc0bb45e9479b7e61014f..1752007397f91bda1e7a70987cec29fdbf4b947a 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -368,9 +368,9 @@ static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host, 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 5f2f24a7360d87988981372e5ba7eea438d22a78..a082aa330f4707088e0f78ee595b55c67df3fe3f 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1762,8 +1762,8 @@ static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host) */ 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-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 3c27401cf7fe5c918388c095bb069ea48d362dd7..a51d636c23121621a0879ab73b5cacf84919b4f6 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -432,6 +432,20 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) 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 b0b9ceb0ab01a7f13dba2749ef6cb93890cb6b25..cfb794766fead2823f0e5d3ba36259d97b9b952a 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -492,6 +492,8 @@ static int intel_mrfld_mmc_probe_slot(struct sdhci_pci_slot *slot) 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 b674b387832f7251457cfb4a2526e5388c45ad7e..36aecd2f8228c99015d67709f9bbaf6f8d5058b4 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1575,6 +1575,13 @@ void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode, 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/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 7c0b27d132b1bca8aa546cedac726cf5e92c6613..b479bd81120b3432e015b76496c3f3ea1d32580c 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -1889,6 +1889,8 @@ static inline u32 jedec_read_mfr(struct map_info *map, uint32_t base, 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 7c887f111a7d03eaeb41dd4bdb6bce67bf81609b..62fd6905c648c6eb5ef167e019831729bb294a85 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -431,7 +431,7 @@ static int block2mtd_setup2(const char *val) } -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 8b66e52ca3ccb811e818dce496ca6e0fcc7c9ecb..7287696a21f91df6a875dc32a112e6206c855de1 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -266,7 +266,7 @@ static int phram_setup(const char *val) 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 2a47a3f0e7308ea13b31391d77e8dab83f536dfe..b4092eab53ac9c4f0315c04c8545836b11554153 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -487,7 +487,7 @@ static int shrink_ecclayout(struct mtd_info *mtd, 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 @@ static int get_oobinfo(struct mtd_info *mtd, struct nand_oobinfo *to) 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 1a4a790054e4d5c89b71dc7e6559dac281fb5574..ef9a6b22c9faf493a6d379cfe9da9d1c094d16fe 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c @@ -1763,7 +1763,7 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, 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 d1570f512f0bbad5c07c9903528c125e412c029a..2f6b55229d5b818d99965faf3d36bf20862e2a1b 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -201,14 +201,9 @@ static int is_blank(struct mtd_info *mtd, unsigned int bufnum) /* 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 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) 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 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) 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 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) 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 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) 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 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) 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 6c062b8251d238ac8155c2a7590eaa2c9ffd2c42..d9dab42758590f903ce382edaf3df20a91157086 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1059,9 +1059,6 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, 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 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, 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 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) 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 21c03086bb7f610c82aee1b26ac9515d5a2e6082..5fb45161789ce012e928c8ec05f589465a33ca93 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -715,7 +715,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command, 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 @@ static void nand_command(struct mtd_info *mtd, unsigned int command, */ switch (command) { + case NAND_CMD_NONE: case NAND_CMD_PAGEPROG: case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: @@ -806,7 +808,9 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, } /* 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 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, */ switch (command) { + case NAND_CMD_NONE: case NAND_CMD_CACHEDPROG: case NAND_CMD_PAGEPROG: case NAND_CMD_ERASE1: @@ -4780,6 +4785,11 @@ int nand_scan_tail(struct mtd_info *mtd) 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 1cb3f7758fb60d8b084c27cc74d2098d5b7a691f..766b2c38568240ceb8505c52ea049f4543a581fc 100644 --- a/drivers/mtd/tests/oobtest.c +++ b/drivers/mtd/tests/oobtest.c @@ -193,6 +193,9 @@ static int verify_eraseblock(int ebnum) 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 @@ static int verify_eraseblock(int ebnum) 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 @@ static int verify_eraseblock_in_one_go(int ebnum) /* 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 @@ static int __init mtd_oobtest_init(void) 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 @@ static int __init mtd_oobtest_init(void) 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 @@ static int __init mtd_oobtest_init(void) 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 @@ static int __init mtd_oobtest_init(void) 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 46913ef25bc02127d3437e272164eb58144242da..479a5f02d10be64f2a4c5f2178004a2fff8cb7bd 100644 --- a/drivers/mtd/ubi/block.c +++ b/drivers/mtd/ubi/block.c @@ -244,7 +244,7 @@ static int ubiblock_open(struct block_device *bdev, fmode_t mode) * 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 85d54f37e28ff25d88fe055838593413170659ad..68902b88415c01fa2eaefbd1468ae65ea882be69 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -894,6 +894,17 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, 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 @@ static int __init bytes_str_to_int(const char *str) * 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 4f0bd6b4422adc9bfa5c7bfd4f1afdd8abcdb4fc..69dd21679a30c21fcb58779f83098034747d4037 100644 --- a/drivers/mtd/ubi/fastmap-wl.c +++ b/drivers/mtd/ubi/fastmap-wl.c @@ -362,7 +362,6 @@ static void ubi_fastmap_close(struct ubi_device *ubi) { 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 c1f5c29e458ef86305376fa7b404f9c5b8e05681..b44c8d348e78ec0b19c884178e54c83a0967dc90 100644 --- a/drivers/mtd/ubi/fastmap.c +++ b/drivers/mtd/ubi/fastmap.c @@ -828,6 +828,24 @@ static int find_fm_anchor(struct ubi_attach_info *ai) 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 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, 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 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, 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 7ac78c13dd1c961df89493d62b701cb594dc62f5..1bcb25bc35eff45ec69d039dc3553da46a9ec222 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -265,6 +265,12 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) 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 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) 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 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) 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_main.c b/drivers/net/bonding/bond_main.c index 63d61c084815934eae4a6c5144f9bd90e99f1e5e..513457a2a7bfd2b0fd876063ede1f0957daca60a 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -371,9 +371,10 @@ int bond_set_carrier(struct bonding *bond) /* 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 @@ static void bond_update_speed_duplex(struct slave *slave) 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 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) 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", @@ -1715,6 +1686,40 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) 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); @@ -1738,6 +1743,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) 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 +1753,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) 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 +2068,7 @@ static int bond_miimon_inspect(struct bonding *bond) (bond->params.downdelay - slave->delay) * bond->params.miimon, slave->dev->name); + commit++; continue; } @@ -2100,7 +2106,7 @@ static int bond_miimon_inspect(struct bonding *bond) (bond->params.updelay - slave->delay) * bond->params.miimon, slave->dev->name); - + commit++; continue; } @@ -2600,11 +2606,13 @@ static void bond_loadbalance_arp_mon(struct work_struct *work) 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 +2639,7 @@ static void bond_loadbalance_arp_mon(struct work_struct *work) 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 +2670,11 @@ static void bond_loadbalance_arp_mon(struct work_struct *work) 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 +3340,17 @@ static void bond_fold_stats(struct rtnl_link_stats64 *_res, 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 1e37313054f3950ee30e6c6fccad874d9262013a..6da69af103e60d9e26ed30815fc946be56aee224 100644 --- a/drivers/net/can/cc770/cc770.c +++ b/drivers/net/can/cc770/cc770.c @@ -390,37 +390,23 @@ static int cc770_get_berr_counter(const struct net_device *dev, 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 @@ static netdev_tx_t cc770_start_xmit(struct sk_buff *skb, struct net_device *dev) 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); + RMTPND_UNC | TXRQST_SET | CPUUPD_RES | NEWDAT_UNC); + cc770_write_reg(priv, msgobj[mo].ctrl0, + MSGVAL_SET | TXIE_SET | RXIE_SET | INTPND_UNC); +} - stats->tx_bytes += dlc; +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; - /* - * 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 - */ - cc770_write_reg(priv, msgobj[mo].ctrl0, - MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES); + 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 @@ static void cc770_tx_interrupt(struct net_device *dev, unsigned int o) 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; + + ctrl1 = cc770_read_reg(priv, msgobj[mo].ctrl1); - /* Nothing more to send, switch off interrupts */ 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].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. */ - cc770_write_reg(priv, msgobj[mo].ctrl0, - MSGVAL_UNC | TXIE_UNC | RXIE_UNC | INTPND_RES); + 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 @@ struct net_device *alloc_cc770dev(int sizeof_priv) 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 a1739db98d911f006f82a44682f0be1b8694a01c..95752e1d128397260968ee100b0b3035923547c9 100644 --- a/drivers/net/can/cc770/cc770.h +++ b/drivers/net/can/cc770/cc770.h @@ -193,6 +193,8 @@ struct cc770_priv { 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 16f7cadda5c32b430c0d25aea9746a93a063bd39..47f43bdecd51bf18844993c148856bbeb6f6b3e2 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -493,7 +493,7 @@ static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev) 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 c06ef438f23f1083040a318bd877ad17d40cd746..6c676403f82305cd4c46a35042b184dbb8558ca1 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 @@ static void ifi_canfd_irq_enable(struct net_device *ndev, bool enable) 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 @@ static int ifi_canfd_handle_lost_msg(struct net_device *ndev) 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 @@ static int ifi_canfd_handle_state_change(struct net_device *ndev, 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 @@ static int ifi_canfd_handle_state_change(struct net_device *ndev, 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 @@ static int ifi_canfd_handle_state_change(struct net_device *ndev, 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 @@ static int ifi_canfd_poll(struct napi_struct *napi, int quota) { 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; - 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); + int work_done = 0; /* 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 @@ static int ifi_canfd_poll(struct napi_struct *napi, int quota) /* 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 @@ static irqreturn_t ifi_canfd_isr(int irq, void *dev_id) 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 @@ static int ifi_canfd_plat_probe(struct platform_device *pdev) 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 @@ static int ifi_canfd_plat_probe(struct platform_device *pdev) 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/ethernet/amazon/ena/ena_com.c b/drivers/net/ethernet/amazon/ena/ena_com.c index e2512ab411681360a251161ab06cb26a9b9a0ace..e13c9cd45dc011ef7cd21c75cc6736d2448843ef 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 @@ static struct ena_comp_ctx *__ena_com_submit_admin_cmd(struct ena_com_admin_queu 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_comp_status_to_errno(u8 comp_status) 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 (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; - while (comp_ctx->status == ENA_CMD_SUBMITTED) { - if ((((u32)jiffies_to_usecs(jiffies)) - start_time) > - ADMIN_CMD_TIMEOUT_US) { + 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 @@ static int ena_com_wait_and_process_admin_cq_polling(struct ena_comp_ctx *comp_c 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_admin_destroy(struct ena_com_dev *ena_dev) 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 bfeaec5bd7b90098076286f4b8763cfbefd6db56..0d9ce08ee3a900dbff4e5dda61bf9232a04dc6d2 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -1542,6 +1542,7 @@ static int ena_create_io_tx_queue(struct ena_adapter *adapter, int qid) "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 @@ static int ena_create_io_rx_queue(struct ena_adapter *adapter, int qid) "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 @@ static void ena_release_bars(struct ena_com_dev *ena_dev, struct pci_dev *pdev) { 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 @@ static int ena_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 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 @@ static int ena_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 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 5390ae89136c6b7870c5d915655854707192b0d5..71611bd6384baef3a6c79451f5811087ff47ad79 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 @@ static void xgene_enet_cle_bypass(struct xgene_enet_pdata *pdata, 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 06e598c8bc16e5618c110fcfbd5b183b464f86c3..c82faf1a88b817f5008febd7643d4a607a768c4f 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 @@ enum xgene_enet_rm { #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 8158d4698734dd76be6475d38d7f17800517362b..fca2e428cd86844342be25177416a010a542888d 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 @@ static netdev_tx_t xgene_enet_start_xmit(struct sk_buff *skb, 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 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, 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 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, 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 @@ static void xgene_enet_napi_add(struct xgene_enet_pdata *pdata) } } +#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 @@ static int xgene_enet_probe(struct platform_device *pdev) 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 @@ static void xgene_enet_shutdown(struct platform_device *pdev) 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 be865b4dada2c65e7089ca4147ef47758698dc51..aba4853e6b70dd49069fbd651141b951f1c92021 100644 --- a/drivers/net/ethernet/arc/emac_main.c +++ b/drivers/net/ethernet/arc/emac_main.c @@ -210,39 +210,48 @@ static int arc_emac_rx(struct net_device *ndev, int budget) 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 c6163874e4e7e136fb953aff74eab4fff401cb75..c770ca37c9b21eb77af38e404489653109aae711 100644 --- a/drivers/net/ethernet/arc/emac_rockchip.c +++ b/drivers/net/ethernet/arc/emac_rockchip.c @@ -169,8 +169,10 @@ static int emac_rockchip_probe(struct platform_device *pdev) /* 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 744ed6ddaf373964a2b3526a2b73613932c73c87..91fbba58d033d5b31281170360efa6b7896197f7 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -707,37 +707,33 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, 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 @@ static int bcm_sysport_init_tx_ring(struct bcm_sysport_priv *priv, 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 1c82e3da69a7bf6134389d982e15bc17697327a6..07b0aaa98de04a5c10b5dab3cc96e1b197b416a6 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.h +++ b/drivers/net/ethernet/broadcom/bcmsysport.h @@ -638,7 +638,7 @@ struct bcm_sysport_tx_ring { 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 c16ec3a51876aacee21ad81dc714a343afb75ba7..1ee11b600645ddb42d0571c84decddf84d7692d7 100644 --- a/drivers/net/ethernet/broadcom/bgmac-bcma.c +++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "bgmac.h" static inline bool bgmac_is_bcm4707_family(struct bcma_device *core) @@ -96,7 +97,7 @@ static int bgmac_probe(struct bcma_device *core) 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 @@ static int bgmac_probe(struct bcma_device *core) 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 0a5ee1d973acf4e0654988e868fcaef7d725975d..31287cec6e3a9a650c02cc4103a5434da0547033 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -2026,6 +2026,7 @@ static void bnx2x_set_rx_buf_size(struct bnx2x *bp) 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 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link) 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 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link) 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 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) /* 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 5d958b5bb8b124ed62824a2f7bedd26848e142f1..554c4086b3c6e007ee2cfc15c0175b08cbee549d 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -9578,6 +9578,15 @@ static int bnx2x_init_shmem(struct bnx2x *bp) 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 @@ static pci_ers_result_t bnx2x_io_slot_reset(struct pci_dev *pdev) 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 bbb3641eddcb699b67172c530c5721e35ec3eb40..3aa993bbafd9cd6d0f7ef9a0d8c0f5a2d38c21f6 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1498,12 +1498,16 @@ static int bnxt_async_event_process(struct bnxt *bp, 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 60e2af8678bdc2bcd71f5015d35ca8599268b8c5..393cce3bf2fc6a62ca9e7cfa561589a19eea6d01 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c @@ -68,7 +68,7 @@ static int bnxt_vf_ndo_prep(struct bnxt *bp, int vf_id) 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 bb22d325e9656c50630df4829d7ab9f1b37a25c8..795a133fb07447ddfe21181fc96fb3d23df8914a 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -10049,6 +10049,16 @@ static int tg3_reset_hw(struct tg3 *tp, bool reset_phy) 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; @@ -14228,7 +14238,8 @@ static int tg3_change_mtu(struct net_device *dev, int new_mtu) */ 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 3b5e98ecba00bb21ad4a9592aad8ef1d10f5c4ef..6e51b793615c18a37c6394d5da5dfef5121921d8 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 0f6811860ad51de9b871e806f3f254a1abfcf2eb..a36e3867664003dcc54a63b5f5749406b083bd74 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_ioc.c +++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.c @@ -2845,7 +2845,7 @@ bfa_ioc_get_adapter_optrom_ver(struct bfa_ioc *ioc, char *optrom_ver) 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 8a37012c9c89637560fc3ae7554718b0f96163e4..c75d4ea9342b3ed9df6c36ff0ae8d48fb54c6f4c 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -1576,6 +1576,11 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 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 0c2a32a305bc90a797ff0773c694acacbcf19bbc..3ec32d7c58668bf1e4b9ac747d10fd2b3ea58477 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2742,6 +2742,16 @@ static int cxgb_setup_tc(struct net_device *dev, u32 handle, __be16 proto, 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 @@ static const struct net_device_ops cxgb4_netdev_ops = { #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 9e073fb6870ac0066a1834a031c4ce63a9830a2f..ebeeb3581b9c5b81214ef6c318d40af62764af5e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -2596,7 +2596,6 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) } #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 @@ int t4_get_raw_vpd_params(struct adapter *adapter, struct vpd_params *p) 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 @@ int t4_fw_upgrade(struct adapter *adap, unsigned int mbox, 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 @@ int t4_fw_upgrade(struct adapter *adap, unsigned int mbox, * 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 @@ int t4_cim_read_la(struct adapter *adap, u32 *la_buf, unsigned int *wrptr) 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 f3ed9ce99e5e7023a93aa22fc85398f4057148dd..9d64e8e7c41712aba9dfeba68309e346260c6aaf 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c @@ -2616,8 +2616,8 @@ void t4vf_sge_stop(struct adapter *adapter) 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 @@ int t4vf_sge_init(struct adapter *adapter) * 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 @@ int t4vf_sge_init(struct adapter *adapter) /* * 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 262587240c86e561a95fd142708ce031524f04ab..0437149f5939236ef96d04c948dd5b846d0d6128 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 91709187125953d8c85590eb31efb0d5a0774301..fe00f71bc6b4e6d9be30f30908c9696948f72131 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -2371,6 +2371,10 @@ static int fec_enet_get_sset_count(struct net_device *dev, int sset) 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 @@ static int fec_enet_init(struct net_device *ndev) } #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 @@ static void fec_reset_phy(struct platform_device *pdev) 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 @@ static void fec_reset_phy(struct platform_device *pdev) 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 @@ static void fec_reset_phy(struct platform_device *pdev) "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 @@ static void fec_reset_phy(struct platform_device *pdev) 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 @@ fec_probe(struct platform_device *pdev) 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 @@ fec_probe(struct platform_device *pdev) 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_probe(struct platform_device *pdev) 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_drv_remove(struct platform_device *pdev) 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 446c7b374ff5c36712d5813cf94b4e9b4ca0b01e..a10de1e9c157d2590eb19122f27fd5dda1a4816b 100644 --- a/drivers/net/ethernet/freescale/fsl_pq_mdio.c +++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.c @@ -381,7 +381,7 @@ static int fsl_pq_mdio_probe(struct platform_device *pdev) { 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 @@ static int fsl_pq_mdio_probe(struct platform_device *pdev) 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 57798814160dca157d9576448a04e06bfc183255..ec4b6997f24a1a4ba18da1fcd7dfb4a6be16ec54 100644 --- a/drivers/net/ethernet/freescale/gianfar_ptp.c +++ b/drivers/net/ethernet/freescale/gianfar_ptp.c @@ -314,11 +314,10 @@ static int ptp_gianfar_adjtime(struct ptp_clock_info *ptp, s64 delta) 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 f76d3327945465660508af9acdaaac4b5e90a51d..ef9bc26ebc1ad8ce59c41f10b276d09dbc2b94ce 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -2594,11 +2594,10 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) } 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 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) } 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 2d0cb609adc33415ab0c724ff266e1f4a145d694..b7c8433a7a37803d332f7efa2166a0a3099fbe20 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 @@ static int hns_ae_get_rss(struct hnae_handle *handle, u32 *indir, u8 *key, 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 @@ static int hns_ae_set_rss(struct hnae_handle *handle, const u32 *indir, 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 1e1eb92998fb3d66f497f88b890817e22c4a8d3d..02a03bccde7b243e7ae4c1b43307006077904ac3 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 @@ static void hns_gmac_disable(void *mac_drv, enum mac_commom_mode mode) 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 c494fc52be7496382b092d77ea29f26992c1c6bd..62a12991ce9a85c2657f892099b2a7ff13defaf8 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 @@ enum dsaf_roce_qos_sl { }; #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 f0ed80d6ef9cd45a8408c987ab4315646098f438..f3be9ac47bfbb19005b7cd2a485111dbd9c4ed07 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 void hns_rcb_ring_pair_get_cfg(struct ring_pair_cb *ring_pair_cb) 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 8f4f0e8da984d37fa84c156dbba185efb8c6c9a9..d1c868c800f99f0326dddfd28f1922b84440adca 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 void hns_xgmac_get_strings(u32 stringset, u8 *data) */ 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 c06845b7b666900d180f7be5054e2f135042b6ca..111e1aab7d83e6982656a3cbadd6ae6d571b116c 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -299,9 +299,9 @@ static void fill_tso_desc(struct hnae_ring *ring, void *priv, 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 @@ int hns_nic_net_xmit_hw(struct net_device *ndev, 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 @@ static void hns_nic_reuse_page(struct sk_buff *skb, int i, 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 @@ static netdev_tx_t hns_nic_net_xmit(struct sk_buff *skb, 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_reset_subtask(struct hns_nic_priv *priv) 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 5b412de350aa28e9099ee251e0824ed66f37b2b1..7bc6a6ecd666694533bf230f5df6543bc807d88d 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_ethtool_set_ops(struct net_device *ndev); 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 87d5c94b2810230b334ddef0501644cd291b8bbf..6be0cae44e9bf52743f223d29ecfacd98f5b6791 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -1017,8 +1017,10 @@ int hns_get_sset_count(struct net_device *netdev, int stringset) 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 @@ hns_set_rss(struct net_device *netdev, const u32 *indir, const u8 *key, 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 8f139197f1aae7409934742d630d90b6bf998afd..5977b695d0fa3f1eefb6fbc70a93f069c774726f 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -342,6 +342,7 @@ static int emac_reset(struct emac_instance *dev) { 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 @@ static int emac_reset(struct emac_instance *dev) } #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 @@ static int emac_reset(struct emac_instance *dev) * 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 @@ static int emac_reset(struct emac_instance *dev) #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 7c6c1468628bfbd9029cde2233c0aa2083c7b5c3..49094c96569721cc3a7c90f12a1bbf9442d5b4ba 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -511,6 +511,23 @@ static int ibmvnic_open(struct net_device *netdev) 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 @@ static int ibmvnic_close(struct net_device *netdev) 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 d7bdea79e9fa755f8a9c6ad63b0ed1eb1e3c63f5..8fd2458060a088d6475622e4ae68cd40df23c116 100644 --- a/drivers/net/ethernet/intel/e1000/e1000.h +++ b/drivers/net/ethernet/intel/e1000/e1000.h @@ -331,7 +331,8 @@ struct e1000_adapter { 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 f42129d09e2c23ba9fdb5cde890d50ecb7166a42..dd112aa5cebbe1097a777235447bcf08e9305b9f 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_init_hw_struct(struct e1000_adapter *adapter, 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 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 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 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 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 @@ static void e1000_remove(struct pci_dev *pdev) 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 @@ static void e1000_remove(struct pci_dev *pdev) 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 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) 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 @@ static int e1000_resume(struct pci_dev *pdev) 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 @@ static pci_ers_result_t e1000_io_error_detected(struct pci_dev *pdev, 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 @@ static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev) 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 0feddf3393f9b5d6d9d36dffa3f9580d0bad86b2..825ec8f710e7a92bfe13eec226ef83bebd19297e 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -1182,6 +1182,7 @@ static void e1000e_tx_hwtstamp_work(struct work_struct *work) 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 @@ static void e1000e_tx_hwtstamp_work(struct work_struct *work) 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 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca) 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_thaw(struct device *dev) 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 5241e0873397692d6365a907ffe95f16e8739b11..7041d83d48bf3bda2a09f7e5174877613a8fbd25 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c @@ -942,7 +942,7 @@ static void fm10k_self_test(struct net_device *dev, 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 92bc8846f1ba991abea8de574efefb340d121e95..f4569461dcb82e62957a48a09a167f9bb7a7b25f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1135,6 +1135,11 @@ static int i40e_get_eeprom_len(struct net_device *netdev) 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 becffd15c092609afb0c12be2a851d177f2d7674..57c7456a5751d6bc22b6d9a5077f79e3852de9e2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -11142,10 +11142,12 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 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 @@ static void i40e_remove(struct pci_dev *pdev) 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 954efe3118dbb15fcf436e5a56f0a98499d165a4..abe290bfc638736d342556837bdc6087c5d42d79 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -292,14 +292,14 @@ i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, { 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 c5430394fac9ea7a90b2a21885385551029e7759..2e12ccf73dba050d8c39f26d9027b8e389671d50 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1820,6 +1820,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) */ 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 @@ bool __i40e_chk_linearize(struct sk_buff *skb) /* 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 @@ bool __i40e_chk_linearize(struct sk_buff *skb) 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 c03800d1000ac19a2b3969ecb2076b96f797ff64..7bfed441c466c615c168ed8c872b3bd2fadefc98 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1262,6 +1262,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) */ 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 @@ bool __i40evf_chk_linearize(struct sk_buff *skb) /* 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 @@ bool __i40evf_chk_linearize(struct sk_buff *skb) 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 ddf478d6322b31e84f92d5c8184c7c1627d34d8d..614f93e01500ed4bfb497995d75f067022a3c82c 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -154,6 +154,7 @@ int i40evf_send_vf_config_msg(struct i40evf_adapter *adapter) 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 a7895c4cbcc3e09023e8901a33d420427548071b..9eb9b68f8935ea12f106109b404cd58e3ec4930a 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -721,6 +721,7 @@ void igb_ptp_rx_hang(struct igb_adapter *adapter) **/ 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 @@ static void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter) 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 508e72c5f1c21b44d9176020993542dd6dfb4b6b..d665de8849a2aa86d91bff41b05c5cea0ca35669 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c +++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c @@ -80,7 +80,7 @@ static struct ixgbe_stats ixgbevf_gstrings_stats[] = { #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 941c8e2c944e11d349937c89cc143eb94cb78fc7..93ab0b3ad39308a855b3c9477fb36b36d3bd46c9 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -5079,7 +5079,7 @@ static int sky2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 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 4832223f150056989d3f4ecb66ccc20b3d0110f7..20de37a414fe0c8a18326744b01cf12afad81fca 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -1842,11 +1842,12 @@ static int mtk_hw_init(struct mtk_eth *eth) /* 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 b04760a5034b9c84038c0bbb44fe80035bc1e0f7..af2e6ea36eac7e24eedb0d47d73d4060c0f3f2f1 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 int mlx4_en_dcbnl_getnumtcs(struct net_device *netdev, int tcid, u8 *num) 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 @@ static int mlx4_en_ets_validate(struct mlx4_en_priv *priv, struct ieee_ets *ets) } 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 @@ static int mlx4_en_config_port_scheduler(struct mlx4_en_priv *priv, /* 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 @@ static int mlx4_en_dcbnl_ieee_setpfc(struct net_device *dev, 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 @@ static int mlx4_en_dcbnl_ieee_setpfc(struct net_device *dev, 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 bdda17d2ea0f9b9e6d8efd37f2637ef22b873cc3..24977cc881d2936081394c1358fe25ddefc792a2 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -1003,27 +1003,32 @@ static int mlx4_en_set_pauseparam(struct net_device *dev, { 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 bf7628db098acd24f9a510ca07862736f0475cfe..22c3fdd5482ab01b28e60cffe36450d68c84a33b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c @@ -163,9 +163,9 @@ static int mlx4_en_get_profile(struct mlx4_en_dev *mdev) 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 d223e7cb68ba316630f53e0279c0a52953ca0701..0160c93de6d3d091ed9673b9518cb130dd499026 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -3125,6 +3125,13 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, 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/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index 1a670b68155550fe9f61fb2bc2c7a3688391249c..0710b367746468f1d4faeb5b8a8f3266ca941674 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -35,6 +35,7 @@ #include #include +#include #include #include "mlx4.h" @@ -985,16 +986,21 @@ int mlx4_flow_attach(struct mlx4_dev *dev, 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 @@ int mlx4_flow_attach(struct mlx4_dev *dev, } } +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 df0f39611c5e7ee4b3f34176e9f94fbd1190ae6c..18f221d8a04de735a5dc9b138f868c211e7c98a8 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -472,6 +472,7 @@ struct mlx4_en_frag_info { #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 { diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 6143113a7fef7c6dfa3395cda7014e099fc631fb..474ff36b97558aebc749c0ce09b4c8fc89be2176 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -387,6 +387,19 @@ static void mlx4_qp_free_icm(struct mlx4_dev *dev, int qpn) __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 @@ int mlx4_update_qp(struct mlx4_dev *dev, u32 qpn, } 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 1822382212eed5d77fb290598fbe2d0834480aa9..d6b06bef1b69a9f46e8d38234a48b370c759755d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -5048,6 +5048,7 @@ static void rem_slave_fs_rule(struct mlx4_dev *dev, int slave) &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 @@ void mlx4_delete_all_resources_for_slave(struct mlx4_dev *dev, int slave) 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 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work) 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 38981db43bc356bfec2c4f84a8c577632a34cb2a..2d235e8433bec27772cd39844a2f5c18054f5cbb 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 @@ static int set_feature_lro(struct net_device *netdev, bool enable) mutex_unlock(&priv->state_lock); + if (mlx5e_vxlan_allowed(priv->mdev)) + udp_tunnel_get_rx_info(netdev); + return err; } @@ -3785,13 +3788,6 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv) 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/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 981cd1d84a5b5e62a3160b362c7a1f39f9824fed..3c183b8c083a3925c5dc109e8e2ddf60ade0949c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -548,7 +548,6 @@ static int mlx5_irq_set_affinity_hint(struct mlx5_core_dev *mdev, int i) 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 @@ static int mlx5_irq_set_affinity_hint(struct mlx5_core_dev *mdev, int i) 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 1e2c8eca3af1e6a7463a5f7bbb74edbca3db4540..60e1edcbe5734d4d0ebab29778ad146a5ef9b9ef 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -809,6 +809,7 @@ static int __mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u8 local_port, bool dynamic) { char *sfd_pl; + u8 num_rec; int err; sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); @@ -818,9 +819,16 @@ static int __mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u8 local_port, 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 @@ static int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id, 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 @@ static int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id, 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 @@ static int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr, 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 @@ static int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr, 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 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, 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 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, 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/qlogic/netxen/netxen_nic_ctx.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ctx.c index b8d5270359cd8a7109226c0be1731cbafa6d1423..e306765155290a31e390f5ddcf88b4f0aa8f973c 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 @@ nx_fw_cmd_set_mtu(struct netxen_adapter *adapter, int mtu) 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 ed014bdbbabde4a6b529f8f0f8e7707bb72fd6f4..457e304275357cb53a4904e4fa0f14c6ea9fd90b 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c +++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c @@ -271,16 +271,34 @@ struct qed_tm_iids { 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; - - for (i = 0; i < MAX_CONN_TYPES; i++) { + bool tm_vf_required = false; + bool tm_required = false; + int i, j; + + /* 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 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) /* 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 @@ static void qed_tm_init_pf(struct qed_hwfn *p_hwfn) 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 afe5e57d9acbd67b7fd7adb3d858bede8a392cda..d02313770fc21c278773561af235b2b167d50a47 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -850,7 +850,7 @@ qed_hw_init_pf_doorbell_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) 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 @@ static int qed_hw_get_nvm_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) 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 333c7442e48af451f28c37e14826d9782fa2d76a..0b949c6d83fc2a039da0ca5cb8ccde5153067745 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -711,7 +711,8 @@ static int qed_slowpath_setup_int(struct qed_dev *cdev, 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 @@ static void qed_fill_link(struct qed_hwfn *hwfn, /* 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 @@ static void qed_fill_link(struct qed_hwfn *hwfn, 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 dff520ed069bf61d951076bee5b82adf89f95c72..7b7a84d2c839a95171bcbe67251244ccd546520c 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_params { 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 d2d6621fe0e591047912dfc5bef20307f8012bbb..48bc5c1513361d213e0d3f18b2601f325d6d0f7b 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -3573,6 +3573,7 @@ static int qed_get_vf_config(struct qed_dev *cdev, 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 @@ void qed_inform_vf_link_state(struct qed_hwfn *hwfn) 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 abf5bf11f865669c33c9428f98776bc7858e3abe..0645124a887b29de2e302d5e8fdf7d3e2233b732 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.c +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.c @@ -204,7 +204,7 @@ static int qed_vf_pf_acquire(struct qed_hwfn *p_hwfn) /* 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 509b596cf1e8612436af6886dc0e2a6e78acdc1d..bd1ec70fb7361d3ea19780fe3a68f0053c29c2bf 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c @@ -341,7 +341,7 @@ qlcnic_pcie_sem_lock(struct qlcnic_adapter *adapter, int sem, u32 id_reg) } 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 d7107055ec6035bf206380d98bbd4670d06660cd..2f656f395f39699e4cec53f4ff25ea7e745d1041 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 @@ static int qlcnic_sriov_virtid_fn(struct qlcnic_adapter *adapter, int vf_id) 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 be258d90de9e90120df3146e12f331b8e87072e2..e3223f2fe2ffc9d4b186a42e0cac87fc37021afd 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c @@ -765,7 +765,7 @@ int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump) 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 @@ static void ql_gen_reg_dump(struct ql_adapter *qdev, 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 6e2add97947128804668a9cdb08bac22f1b888d2..8bbb55f319092fa622b762785a92862d160cef79 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.c +++ b/drivers/net/ethernet/qualcomm/qca_spi.c @@ -296,8 +296,9 @@ qcaspi_receive(struct qcaspi *qca) /* 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 @@ qcaspi_receive(struct qcaspi *qca) 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 @@ qcaspi_netdev_init(struct net_device *dev) 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/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 18e68c91e65130023f277fa136159de2320b7984..dbb63640bc6eb73a5a3de5aed12022ad67446afd 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -8446,12 +8446,12 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 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 b6816ae00b7aa307242ff68b20f773519ee8f4f9..c8fd99b3ca299a6fa6f67f6b0b689eab03c57632 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -3133,7 +3133,7 @@ static int sh_eth_drv_probe(struct platform_device *pdev) /* 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 10d6059b2f26555af9963812f847b68109b9c959..f4074e25fb71c138bcc21e3062831b5cbc8981f4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c @@ -38,6 +38,7 @@ static u32 stmmac_config_sub_second_increment(void __iomem *ioaddr, { 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 @@ static u32 stmmac_config_sub_second_increment(void __iomem *ioaddr, 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 98bbb91336e4dd8b67cd51c04ff79e31b68a6945..c212d1dd8bfde4d032f24a88e76458fa2a017f15 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -478,7 +478,10 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) /* 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 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) 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 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) 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 c06938c47af5549658c19e9c64a568595d80510a..174777cd888e0706f8a6b369af724b16b0f5c347 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/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 3f1971d485f3eed567178451038666420e39658e..552de9c490c637b850edce5a497dfbcd1e1307bf 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -282,6 +282,10 @@ struct cpsw_ss_regs { /* 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 @@ static void _cpsw_adjust_link(struct cpsw_slave *slave, /* 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) @@ -1159,11 +1164,23 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) 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 3c1f89ab0110c88c9b85fd75f6d46cdd97a11da0..92ad43e53c722e3a6cef8feb17cc9a6d078e21b1 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -209,6 +209,7 @@ static void geneve_rx(struct geneve_dev *geneve, struct geneve_sock *gs, 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 @@ static void geneve_rx(struct geneve_dev *geneve, struct geneve_sock *gs, 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 @@ static void geneve_rx(struct geneve_dev *geneve, struct geneve_sock *gs, /* 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 @@ static void geneve_rx(struct geneve_dev *geneve, struct geneve_sock *gs, 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 @@ static void geneve_rx(struct geneve_dev *geneve, struct geneve_sock *gs, } } - 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 @@ static int geneve_udp_encap_recv(struct sock *sk, struct sk_buff *skb) 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 @@ static int geneve_udp_encap_recv(struct sock *sk, struct sk_buff *skb) 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 4bad0b894e9c9fccbe89ae0427a01a4d07f226e4..27160d1870e1c146cd69ca61be9aaea84c8b1da5 100644 --- a/drivers/net/hamradio/hdlcdrv.c +++ b/drivers/net/hamradio/hdlcdrv.c @@ -576,6 +576,8 @@ static int hdlcdrv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 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 c2ac39a940f719b80e735fba17f9424abd1348e5..14f58b60d1b58763724729955e62765aeb8b477d 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -151,6 +151,13 @@ static void netvsc_destroy_buf(struct hv_device *device) 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 @@ static void netvsc_destroy_buf(struct hv_device *device) 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 f355df7cf84a819ef2a27a3956d10df96b98cb12..1b980f12663afc259da33422b6c4abb6057ab4ec 100644 --- a/drivers/net/ieee802154/adf7242.c +++ b/drivers/net/ieee802154/adf7242.c @@ -888,7 +888,7 @@ static struct ieee802154_ops adf7242_ops = { .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 @@ static irqreturn_t adf7242_isr(int irq, void *data) 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 627eb825eb74c81a2769e3c0c0eb602fb8f1cbdf..c747ab65266559fdb6dff77eb8078a4885c9a7ef 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -299,6 +299,10 @@ static int ipvlan_rcv_frame(struct ipvl_addr *addr, struct sk_buff **pskb, 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 2caac0c37059de5f146595f9f672910105744532..365a48cfcbbf03c1be7cacdfd73c739376871507 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -742,7 +742,12 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb, 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 @@ static struct sk_buff *macsec_decrypt(struct sk_buff *skb, 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 6d55049cd3dcb1c9cf4a2031d50dd6bb4278f178..e8ad4d060da736ea86b1218c232a36d1554c9ef5 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -1377,9 +1377,14 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, 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 963838d4fac12428e0c01f88537a72b4a2fd8703..599ce24c514f1b0eb1e8a6df1e3a0362f53bee26 100644 --- a/drivers/net/phy/mdio-mux.c +++ b/drivers/net/phy/mdio-mux.c @@ -122,10 +122,9 @@ int mdio_mux_init(struct device *dev, 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 @@ int mdio_mux_init(struct device *dev, 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 @@ int mdio_mux_init(struct device *dev, 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 @@ int mdio_mux_init(struct device *dev, 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 135296508a7ed70661f7e8f9aa9c0eb7fadf3133..6425ce04d3f95d6ca7a7b2ec7a22b7f860358a1e 100644 --- a/drivers/net/phy/mdio-sun4i.c +++ b/drivers/net/phy/mdio-sun4i.c @@ -118,8 +118,10 @@ static int sun4i_mdio_probe(struct platform_device *pdev) 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 92af182951bec5cc0c6374242ec2a59570c6e40b..20fbcc9c4687cc057fa730cd6087445a0d10e354 100644 --- a/drivers/net/phy/mdio-xgene.c +++ b/drivers/net/phy/mdio-xgene.c @@ -197,8 +197,11 @@ static int xgene_mdio_reset(struct xgene_mdio_pdata *pdata) } 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 @@ static int xgene_xfi_mdio_write(struct mii_bus *bus, int phy_id, 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 @@ static acpi_status acpi_register_phy(acpi_handle handle, u32 lvl, } #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 @@ static int xgene_mdio_probe(struct platform_device *pdev) 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 @@ static int xgene_mdio_probe(struct platform_device *pdev) 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 @@ static int xgene_mdio_probe(struct platform_device *pdev) } 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 @@ static int xgene_mdio_remove(struct platform_device *pdev) 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 354241b53c1d00fa6d6b8698744be32770dc210f..594a11d42401d2462a2e0746b96bc88a4503440f 100644 --- a/drivers/net/phy/mdio-xgene.h +++ b/drivers/net/phy/mdio-xgene.h @@ -132,10 +132,6 @@ static inline u64 xgene_enet_get_field_value(int pos, int len, u64 src) #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 6e12401b510268921be6335c47d25d36f06ac63e..4d217649c8b109d66e4e3ad279e8f74f7e0d7eda 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -148,6 +148,12 @@ static inline int phy_aneg_done(struct phy_device *phydev) 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 @@ void phy_start(struct phy_device *phydev) 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 fc4c2ccc3d22508e8b3b217ec511368204478b72..1e4969d90f1a66c20dd43beb517c47bd94af46c6 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -255,7 +255,7 @@ struct ppp_net { /* 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 @@ static ssize_t ppp_write(struct file *file, const char __user *buf, 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 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev) 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 @@ static void ppp_setup(struct net_device *dev) */ /* 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 @@ static void __ppp_xmit_process(struct ppp *ppp) 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 @@ static void ppp_xmit_process(struct ppp *ppp) 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 @@ static void ppp_xmit_process(struct ppp *ppp) err: local_bh_enable(); + kfree_skb(skb); + if (net_ratelimit()) netdev_err(ppp->dev, "recursion detected\n"); } @@ -1938,7 +1940,7 @@ static void __ppp_channel_push(struct channel *pch) 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 @@ ppp_connect_channel(struct channel *pch, int unit) 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/pptp.c b/drivers/net/ppp/pptp.c index 1951b1085cb8943d85af68a26d0e22dc35ce3a16..3045c9662ed681c2b1c9f17681f59840f0d498e0 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -465,7 +465,6 @@ static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr, 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 27ed25252aac502838527ec48ba48eda754ed69c..cfd81eb1b532ad229fd637cfb13fbde749980a1c 100644 --- a/drivers/net/slip/slhc.c +++ b/drivers/net/slip/slhc.c @@ -509,6 +509,10 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) 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 @@ slhc_remember(struct slcompress *comp, unsigned char *icp, int isize) 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 26681707fc7af0fc8ad053f04610a9adc5848796..8673ef3c9cdc13a5f5b1f0b7e77ed3c3f99b907d 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1203,11 +1203,6 @@ static int team_port_add(struct team *team, struct net_device *port_dev) 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", @@ -1247,6 +1242,11 @@ static int team_port_add(struct team *team, struct net_device *port_dev) 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 +1271,6 @@ static int team_port_add(struct team *team, struct net_device *port_dev) 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: @@ -2403,7 +2401,7 @@ static int team_nl_send_options_get(struct team *team, u32 portid, u32 seq, if (!nlh) { err = __send_and_alloc_skb(&skb, team, portid, send_func); if (err) - goto errout; + return err; goto send_done; } @@ -2688,7 +2686,7 @@ static int team_nl_send_port_list_get(struct team *team, u32 portid, u32 seq, 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 1fca0024f29467c3e896556c0b1fd3f2d32bf6cc..4fb468666b192085ab1dd64241a1400afbd75324 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -773,6 +773,12 @@ static const struct usb_device_id products[] = { USB_CDC_SUBCLASS_ETHERNET, 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, diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index dc6d3b0a0be8b9878c9d29fe2060781caa2320d8..feb61eaffe3273821c9a6b1db23c349ef89a7c70 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -1118,6 +1118,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) 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 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) * 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 c53385a0052f1229b6d8b8ad64d179816e915fc7..f5a96678494be0ee1239026f01bf6673c4fd86ab 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -873,7 +873,8 @@ static int lan78xx_read_otp(struct lan78xx_net *dev, u32 offset, 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 e1e5e84384573f77d9a8881f4350c49abbc5eaac..973e90fb4a24eda0a72f768a4ba2a88f50a7c9b2 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -531,7 +531,7 @@ static int qmi_wwan_resume(struct usb_interface *intf) 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 = { 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, @@ -805,6 +805,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x05c6, 0x9084, 4)}, {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 */ @@ -914,6 +915,7 @@ static const struct usb_device_id products[] = { {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 */ diff --git a/drivers/net/veth.c b/drivers/net/veth.c index fbc853e645318537e41eee115e17961607e394ff..ee7460ee3d050c943ed7cb524e55415beca329a4 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -425,6 +425,9 @@ static int veth_newlink(struct net *src_net, struct net_device *dev, 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 1568aedddfc9448a462641e198cc538e67b77808..472ed6df22215798810f6817f724349e2e6db1db 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -529,7 +529,12 @@ static int add_recvbuf_small(struct virtnet_info *vi, struct receive_queue *rq, 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 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb) 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 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb) 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 4afba17e24031119563c88b0076b7037dd76165b..f809eed0343c62fd0aebeab4d5ccfb7b85fe4e36 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -2962,6 +2962,11 @@ vmxnet3_force_close(struct vmxnet3_adapter *adapter) /* 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); } diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 346e48698555a9fadb307e22d914958febfa78bf..42c9480acdc70b597c277398cea7021684679052 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -585,13 +585,15 @@ static int vrf_finish_output(struct net *net, struct sock *sk, struct sk_buff *s 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 983e941bdf29e6239ab2524396d49022c679a1a0..28afdf22b88fd43be56a3c2f60a65497988ed4b0 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -930,7 +930,7 @@ static bool vxlan_snoop(struct net_device *dev, 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) 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 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, 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 65647533b4014844f1c0b0e7d5ab25b71e0b94ed..a8bd68f252e9f2ad002b5f82335bcfdeb7dc04ea 100644 --- a/drivers/net/wan/fsl_ucc_hdlc.c +++ b/drivers/net/wan/fsl_ucc_hdlc.c @@ -137,7 +137,7 @@ static int uhdlc_init(struct ucc_hdlc_private *priv) 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 @@ static int uhdlc_init(struct ucc_hdlc_private *priv) /* 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 @@ static int uhdlc_init(struct ucc_hdlc_private *priv) } /* 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 @@ static int uhdlc_init(struct ucc_hdlc_private *priv) 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_tx_done(struct ucc_hdlc_private *priv) 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 @@ static void uhdlc_memclean(struct ucc_hdlc_private *priv) 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 @@ static void uhdlc_memclean(struct ucc_hdlc_private *priv) 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 @@ static int ucc_hdlc_probe(struct platform_device *pdev) 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 47fdb87d3567ce8ecb1f6dfc04d7f97884c57d8a..8a9aced850be34d161e3289828efae40e496b03a 100644 --- a/drivers/net/wan/hdlc_ppp.c +++ b/drivers/net/wan/hdlc_ppp.c @@ -574,7 +574,10 @@ static void ppp_timer(unsigned long arg) 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 db363856e0b52aff1a4f2f6d9ac4416ccd7d031b..2b064998915fccd41a9195289252b7f8e265d8f9 100644 --- a/drivers/net/wan/pc300too.c +++ b/drivers/net/wan/pc300too.c @@ -347,6 +347,7 @@ static int pc300_pci_init_one(struct pci_dev *pdev, 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 7d3231acfb24939bcee74cf9b6a38a3a26a82a67..82bdec744055b9e51b23eefe9565fe065294bf5a 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.h +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -83,6 +83,8 @@ enum bmi_cmd_id { #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 0b4d7965988445d0e9660cafeb1a5fd6e3eb64ad..041ef3be87b9534b725ee510214af208771d5dc7 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -1059,7 +1059,7 @@ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, */ 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 7b3017f55e3d64a23152e6515b6cf15a6ed7c693..65ad7a130ca1d42aa74d2bc5658d0753a88a682c 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -652,7 +652,7 @@ static int ath10k_core_get_board_id_from_otp(struct ath10k *ar) { 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 @@ static int ath10k_core_get_board_id_from_otp(struct ath10k *ar) 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 @@ static int ath10k_download_and_run_otp(struct ath10k *ar) 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 82a4c67f3672ba8e7f05951ee3dd6916ac1ca356..0dadc6044dba50068969f6edc535112ba3e5474f 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -624,17 +624,21 @@ static ssize_t ath10k_write_simulate_fw_crash(struct file *file, 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 @@ static ssize_t ath10k_write_simulate_radar(struct file *file, 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 17ab8efdac3571081531f01f464a909b0d03d1ea..a497bf31953de5541bfeb6009b67dc0a55bdfc69 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2505,7 +2505,7 @@ static void ath10k_peer_assoc_h_qos(struct ath10k *ar, } 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: @@ -6054,6 +6054,16 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, "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 +7080,7 @@ ath10k_mac_update_rx_channel(struct ath10k *ar, 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 54df425bb0fc1e2fd23a28f9052e76f263b277ee..e518b640aad02a8268bff1b0daf31d7882f6341c 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -3638,6 +3638,11 @@ static void ath10k_dfs_radar_report(struct ath10k *ar, 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 1b243c899beffbba5938ca1250c4a76376cba44f..9b8562ff669876f6525e1730ec3e039808c769a6 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -5017,7 +5017,8 @@ enum wmi_10_4_vdev_param { #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 4f8d9ed04f5ebd8995170da9be566819c984db8c..4a1054408f1ab77b80914ae13a1824b098815590 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -939,7 +939,10 @@ static int open_file_eeprom(struct inode *inode, struct file *file) } 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/regd.c b/drivers/net/wireless/ath/regd.c index f8506037736f8b0f5ea0257e3e56cf246d8a7207..404fc306cb2a73cc3a2afcb7081422372f826457 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -254,8 +254,12 @@ bool ath_is_49ghz_allowed(u16 regdomain) 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 @@ __ath_reg_apply_beaconing_flags(struct wiphy *wiphy, 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 @@ ath_reg_apply_ir_flags(struct wiphy *wiphy, } } -/* 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 @@ static void ath_reg_apply_radar_flags(struct wiphy *wiphy) 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 @@ void ath_reg_notifier_apply(struct wiphy *wiphy, 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 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, } 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/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 1875387d0eee75eb31bceff663835891196b77db..0f34c43a3eaab0f84ab3d62d9f9e9a97d3435f47 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -33,10 +33,8 @@ module_param(ftm_mode, bool, 0444); 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 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) } #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 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) * 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 @@ static void wil_pcie_remove(struct pci_dev *pdev) 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 @@ static const struct pci_device_id wil6210_pcie_ids[] = { 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 @@ static int wil6210_pm_resume(struct device *dev) { return wil6210_resume(dev, false); } -#endif /* CONFIG_PM_SLEEP */ static int wil6210_pm_runtime_idle(struct device *dev) { @@ -578,10 +570,12 @@ static int wil6210_pm_runtime_suspend(struct device *dev) #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 14533edc583342e55beb9637c4bf58eab2fca831..0a96518a566f20e6c010b7dc502545f3cc2524b3 100644 --- a/drivers/net/wireless/ath/wil6210/pm.c +++ b/drivers/net/wireless/ath/wil6210/pm.c @@ -33,7 +33,11 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime) 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 dcec343864aa6157430094f615ebbe0dcd252fd4..d0fecd9f8a4cd1aadc7f35d3292cbb21e636ad7d 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -323,6 +323,8 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, 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 9b68663d87cdbe7bdb8956a39e395fa6a962614f..f4476ee4f870ee61cbdcbdb6e304215f4d5b85dc 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -798,9 +798,7 @@ struct wil6210_priv { int fw_calib_result; #ifdef CONFIG_PM -#ifdef CONFIG_PM_SLEEP struct notifier_block pm_notify; -#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM */ bool suspend_resp_rcvd; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index 85d949e03f79f7c9566c7b00a9bbe99853df7f16..f78d91b6928711a2128317d60d7ea054eb09abd7 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -462,25 +462,23 @@ static int brcmf_p2p_set_firmware(struct brcmf_if *ifp, u8 *p2p_mac) * @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/main.c b/drivers/net/wireless/cnss2/main.c index 914275867075331f78f9f3915806e6aa02ba9514..e4efb98aec57722eb68a3c447e08b144e77daa19 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -1675,6 +1675,7 @@ static int cnss_cold_boot_cal_start_hdlr(struct cnss_plat_data *plat_priv) 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); clear_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state); @@ -1799,20 +1800,7 @@ int cnss_register_subsys(struct cnss_plat_data *plat_priv) 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; diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h index c11b2061d93c436f2e127a0aa0cb5e2b347548e6..b62c01446945b7ca5233cc0f56c18f13d247e0e3 100644 --- a/drivers/net/wireless/cnss2/main.h +++ b/drivers/net/wireless/cnss2/main.h @@ -107,6 +107,7 @@ struct cnss_fw_mem { void *va; phys_addr_t pa; bool valid; + u32 type; }; enum cnss_fw_dump_type { @@ -198,7 +199,8 @@ struct cnss_plat_data { 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,6 +212,7 @@ struct cnss_plat_data { 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); diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 9d1fbf9a5e886ab6d47b8a349c751cf55d74befb..4726750a371dc7c0049180cd44120ed89e273ea2 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -732,18 +732,21 @@ int cnss_pm_request_resume(struct cnss_pci_data *pci_priv) 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; - - 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; - - return -ENOMEM; + struct cnss_fw_mem *fw_mem = plat_priv->fw_mem; + int i; + + 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; + } } } @@ -753,17 +756,25 @@ int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv) 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; - - 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; + struct cnss_fw_mem *fw_mem = plat_priv->fw_mem; + int i; + + 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) @@ -1107,7 +1118,7 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic) 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 +1163,20 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic) 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"); - - 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++; + cnss_pr_dbg("Collect remote heap dump segment\n"); + + 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) diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c index f4344aee54eecae94fbb7d75e555fc3e35684bf9..b8777c18d2528c929352b1d3a9eeb042d089270e 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 @@ -159,10 +159,9 @@ static int cnss_wlfw_host_cap_send_sync(struct cnss_plat_data *plat_priv) memset(&req, 0, sizeof(req)); memset(&resp, 0, sizeof(resp)); - req.daemon_support_valid = 1; - req.daemon_support = daemon_support; - - cnss_pr_dbg("daemon_support is %d\n", req.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); req.wake_msi = cnss_get_wake_msi(plat_priv); if (req.wake_msi) { @@ -170,6 +169,19 @@ static int cnss_wlfw_host_cap_send_sync(struct cnss_plat_data *plat_priv) 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 +233,8 @@ static int cnss_wlfw_ind_register_send_sync(struct cnss_plat_data *plat_priv) 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 +272,48 @@ static int cnss_wlfw_request_mem_ind_hdlr(struct cnss_plat_data *plat_priv, 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,29 +350,46 @@ static int cnss_qmi_pin_result_ind_hdlr(struct cnss_plat_data *plat_priv, 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; - cnss_pr_dbg("Memory for FW, va: 0x%pK, pa: %pa, size: 0x%zx\n", - fw_mem->va, &fw_mem->pa, fw_mem->size); + 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; + } - memset(&req, 0, sizeof(req)); - memset(&resp, 0, sizeof(resp)); + 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.addr = fw_mem->pa; - req.size = fw_mem->size; + 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; + } req_desc.max_msg_len = WLFW_RESPOND_MEM_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_WLFW_RESPOND_MEM_REQ_V01; @@ -349,8 +399,8 @@ int cnss_wlfw_respond_mem_send_sync(struct cnss_plat_data *plat_priv) 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 +408,21 @@ int cnss_wlfw_respond_mem_send_sync(struct cnss_plat_data *plat_priv) 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 +963,12 @@ static void cnss_wlfw_clnt_ind(struct qmi_handle *handle, 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 +1029,11 @@ int cnss_wlfw_server_arrive(struct cnss_plat_data *plat_priv) 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 7d6a771bc0d5d1d6b3c674f91d7be69c01812c85..bbf707b869bdefcff00bc342dcad645c5e9c5f6c 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 @@ static struct elem_info wlfw_ce_tgt_pipe_cfg_s_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -97,7 +97,7 @@ static struct elem_info wlfw_ce_svc_pipe_cfg_s_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -123,7 +123,7 @@ static struct elem_info wlfw_shadow_reg_cfg_s_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -140,7 +140,7 @@ static struct elem_info wlfw_shadow_reg_v2_cfg_s_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -175,7 +175,131 @@ static struct elem_info wlfw_memory_region_info_s_v01_ei[] = { { .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 @@ static struct elem_info wlfw_rf_chip_info_s_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -218,7 +342,7 @@ static struct elem_info wlfw_rf_board_info_s_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -235,7 +359,7 @@ static struct elem_info wlfw_soc_info_s_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -261,7 +385,7 @@ static struct elem_info wlfw_fw_version_info_s_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -418,7 +542,7 @@ struct elem_info wlfw_ind_register_req_msg_v01_ei[] = { .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 @@ struct elem_info wlfw_ind_register_req_msg_v01_ei[] = { .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, @@ -447,10 +571,46 @@ struct elem_info wlfw_ind_register_req_msg_v01_ei[] = { .offset = offsetof(struct wlfw_ind_register_req_msg_v01, 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 @@ struct elem_info wlfw_ind_register_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -497,7 +657,7 @@ struct elem_info wlfw_fw_ready_ind_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -505,7 +665,7 @@ struct elem_info wlfw_msa_ready_ind_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -573,7 +733,7 @@ struct elem_info wlfw_pin_connect_result_ind_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -608,7 +768,7 @@ struct elem_info wlfw_wlan_mode_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -626,7 +786,7 @@ struct elem_info wlfw_wlan_mode_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -764,7 +924,7 @@ struct elem_info wlfw_wlan_cfg_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -782,7 +942,7 @@ struct elem_info wlfw_wlan_cfg_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -790,7 +950,7 @@ struct elem_info wlfw_cap_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -920,7 +1080,7 @@ struct elem_info wlfw_cap_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1054,7 +1214,7 @@ struct elem_info wlfw_bdf_download_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1073,7 +1233,7 @@ struct elem_info wlfw_bdf_download_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1117,7 +1277,7 @@ struct elem_info wlfw_cal_report_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1135,7 +1295,7 @@ struct elem_info wlfw_cal_report_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1153,7 +1313,7 @@ struct elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1269,7 +1429,7 @@ struct elem_info wlfw_cal_download_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1288,7 +1448,7 @@ struct elem_info wlfw_cal_download_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1316,7 +1476,7 @@ struct elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1342,7 +1502,7 @@ struct elem_info wlfw_cal_update_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1459,7 +1619,7 @@ struct elem_info wlfw_cal_update_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1485,7 +1645,7 @@ struct elem_info wlfw_msa_info_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1522,7 +1682,7 @@ struct elem_info wlfw_msa_info_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1530,7 +1690,7 @@ struct elem_info wlfw_msa_ready_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1548,7 +1708,7 @@ struct elem_info wlfw_msa_ready_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1574,7 +1734,7 @@ struct elem_info wlfw_ini_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1592,7 +1752,7 @@ struct elem_info wlfw_ini_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1627,7 +1787,7 @@ struct elem_info wlfw_athdiag_read_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1676,7 +1836,7 @@ struct elem_info wlfw_athdiag_read_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1724,7 +1884,7 @@ struct elem_info wlfw_athdiag_write_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1743,7 +1903,7 @@ struct elem_info wlfw_athdiag_write_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1760,7 +1920,7 @@ struct elem_info wlfw_vbatt_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1778,7 +1938,7 @@ struct elem_info wlfw_vbatt_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1804,7 +1964,7 @@ struct elem_info wlfw_mac_addr_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1822,7 +1982,7 @@ struct elem_info wlfw_mac_addr_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1834,16 +1994,16 @@ struct elem_info wlfw_host_cap_req_msg_v01_ei[] = { .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, @@ -1863,10 +2023,217 @@ struct elem_info wlfw_host_cap_req_msg_v01_ei[] = { .offset = offsetof(struct wlfw_host_cap_req_msg_v01, 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 @@ struct elem_info wlfw_host_cap_resp_msg_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_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 @@ struct elem_info wlfw_respond_mem_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -1953,15 +2331,15 @@ struct elem_info wlfw_fw_mem_ready_ind_msg_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_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 @@ struct elem_info wlfw_rejuvenate_ind_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -2049,7 +2427,7 @@ struct elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -2068,7 +2446,7 @@ struct elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -2096,7 +2474,7 @@ struct elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -2155,7 +2533,7 @@ struct elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -2181,7 +2559,7 @@ struct elem_info wlfw_m3_info_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -2199,7 +2577,7 @@ struct elem_info wlfw_m3_info_resp_msg_v01_ei[] = { { .data_type = QMI_EOTI, .is_array = NO_ARRAY, - .is_array = QMI_COMMON_TLV_TYPE, + .tlv_type = QMI_COMMON_TLV_TYPE, }, }; @@ -2216,6 +2594,14 @@ struct elem_info wlfw_xo_cal_ind_msg_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_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 9b56eb0c02fbce1419da6085add915ac1b9b06b6..00a873d11d1408b489b9384738b161488f3ba693 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 @@ enum wlfw_pipedir_enum_v01 { 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 @@ enum wlfw_pipedir_enum_v01 { #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 @@ struct wlfw_memory_region_info_s_v01 { 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 @@ struct wlfw_ind_register_req_msg_v01 { 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 @@ struct wlfw_mac_addr_resp_msg_v01 { 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; -}; - -#define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 11 + 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 189 extern struct elem_info wlfw_host_cap_req_msg_v01_ei[]; struct wlfw_host_cap_resp_msg_v01 { @@ -550,18 +613,19 @@ struct wlfw_host_cap_resp_msg_v01 { 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 @@ struct wlfw_fw_mem_ready_ind_msg_v01 { #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 @@ struct wlfw_xo_cal_ind_msg_v01 { #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 d4b73dedf89b1a9bfdbe02cdd418311a5a062939..b35adbca7a5d2c596db5f9c90d2b621f075c4bc4 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 8d3e53fac1dabc01ed875b6f8c2863bb908f770c..20d08ddb4388e47b52d8a5117d4c84ea00410112 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 ea16185258788a2235d1c7055d8bdae107b0caa1..e267377edbe777633b39a5f9a65cb6487a7f3de8 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 88f260db3744006244509a71b938d0d2d26b4082..68412ff2112e34ec4cba0bc00a5814baec966798 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 @@ void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_wait) } 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 @@ void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_wait, } } 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 0f9995ed71cdef320a518cea5c2178dce4ec635a..368884be4e7c9641f80e402bb94b9e4a92d114fe 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 @@ struct iwl_notif_wait_data { * * 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 @@ struct iwl_notification_wait { /* 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 406ef301b8ab8aedd8a1e8e93e8800d53872a233..da8234b762bfdd5f5f98cbabb5054c1f162f7ec2 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 700d244df34b76acbbf2082e9c92770dc18a20a5..2642d8e477b822b272f9e776ad3140b5af644cd9 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 @@ int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, 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 @@ int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id) /* 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 c60703e0c246731cc3976dbe03f1f6fabe76f028..2b1c691bb4b24083808d098fff0f480a0c4edd28 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1666,8 +1666,11 @@ int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id, u8 minq, u8 maxq); */ 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 @@ void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, 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 4d35deb628bcaae711851a7e4acaf158883b57f0..6d38eec3f9d3cb5e4f0d3909e72224c35f2538d0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1118,21 +1118,37 @@ static void iwl_mvm_fw_error_dump_wk(struct work_struct *work) 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); + 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)); + /* 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); + } + } mutex_unlock(&mvm->mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 227c5ed9cbe6366611682c717d81336df83318d3..0aea476ebf5099aa412fce4d06049ecd6b6c6d5e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -1867,12 +1867,10 @@ static int rs_switch_to_column(struct iwl_mvm *mvm, 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 0e60e38b2acf058aef19954ae6e9ccacb2ed0f3e..b78e60eb600f1c54ad64e15bf3e67d4ea5e9c645 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -104,7 +104,20 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, 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 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, * 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 bec7d9c46087d3c8fed48d5858f4f116797eceed..c5203568a47ac171c6105d1a233cf8e37ed742e9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -790,11 +790,13 @@ static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev, 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 10ef44e8ecd5c3bb0513ff4114a2cd61076eca94..fe32de252e6b2d4197a93facb2c796e3fc19a794 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2824,7 +2824,8 @@ static struct iwl_trans_dump_data #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 int iwl_trans_pcie_suspend(struct iwl_trans *trans) 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 4b462dc21c413a895431bc0cde566be115275003..4182c3775a72df20781d89d208bc6d16569fe66b 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -552,8 +552,6 @@ struct mac80211_hwsim_data { /* 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 @@ static int hwsim_fops_ps_write(void *dat, u64 val) val != PS_MANUAL_POLL) return -EINVAL; - old_ps = data->ps; - data->ps = val; - - local_bh_disable(); 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); - data->ps_poll_pending = true; - } else if (old_ps == PS_DISABLED && val != PS_DISABLED) { + local_bh_enable(); + return 0; + } + old_ps = data->ps; + data->ps = val; + + local_bh_disable(); + 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 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, 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 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) 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 @@ static const char mac80211_hwsim_gstrings_stats[][ETH_GSTRING_LEN] = { "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 @@ static void mac80211_hwsim_get_et_stats(struct ieee80211_hw *hw, 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 @@ static int hwsim_get_radio_nl(struct sk_buff *msg, struct genl_info *info) 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; diff --git a/drivers/net/wireless/marvell/libertas/if_spi.c b/drivers/net/wireless/marvell/libertas/if_spi.c index c3a53cd6988e730ccddd13802672b7b863457e06..7b4955cc38db3d9b19376d0fde19c1ada14139f9 100644 --- a/drivers/net/wireless/marvell/libertas/if_spi.c +++ b/drivers/net/wireless/marvell/libertas/if_spi.c @@ -1181,6 +1181,10 @@ static int if_spi_probe(struct spi_device *spi) /* 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 @@ static int if_spi_probe(struct spi_device *spi) 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 8677a53ef725d449bb4a818e595fad8ef7f165a6..48d51be11f9b1ad619e6dec177bb1165d4206e2d 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -1109,6 +1109,12 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, 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 2478ccd6f2d936c9e962f5cc6cdeeb9a5908219a..1e36f11831a9bad6a87f3a633b9370ac74c1ac85 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -146,7 +146,6 @@ static int mwifiex_unregister(struct mwifiex_adapter *adapter) kfree(adapter->regd); - vfree(adapter->chan_stats); kfree(adapter); return 0; } @@ -636,6 +635,7 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) 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_shutdown_sw(struct mwifiex_adapter *adapter, struct semaphore *sem) mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev); rtnl_unlock(); } + vfree(adapter->chan_stats); up(sem); exit_sem_err: @@ -1729,6 +1730,7 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) 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 8d601dcf294808244914665a9d6abc8d13a296d4..486b8c75cd1f9ec098edb71d1b878aecbc14b27c 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -1458,7 +1458,7 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, } 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 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, } 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 dbdfb3f5c507165fbc2f72d884ff13bdfc5b31c6..a9f5f398b2f860c239db8f6e17899d9adcdc7f33 100644 --- a/drivers/net/wireless/mediatek/mt7601u/mcu.c +++ b/drivers/net/wireless/mediatek/mt7601u/mcu.c @@ -66,8 +66,10 @@ mt7601u_mcu_msg_alloc(struct mt7601u_dev *dev, const void *data, int len) 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 @@ static int mt7601u_mcu_function_select(struct mt7601u_dev *dev, }; 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 @@ mt7601u_mcu_calibrate(struct mt7601u_dev *dev, enum mcu_calibrate cal, u32 val) }; 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 13da95a24cf77bb366481252226f3c4566352d67..987c7c4f43cd60ab693d4b387892d3b6d7668123 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c @@ -142,15 +142,25 @@ void rt2x00mac_tx(struct ieee80211_hw *hw, 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 @@ void rt2x00mac_tx(struct ieee80211_hw *hw, 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 0881ba8535f4e11bec61d029dc58f29a7f0c4500..c78abfc7bd96de89506a4705f58fe97a4ed8797d 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -247,7 +247,10 @@ static const UCHAR b4_default_startup_parms[] = { 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 @@ static void init_startup_params(ray_dev_t *local) * 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 231f84db9ab0691e46eee60c41f2b1ce3ccf9312..6113624ccec3986f01dbc2ebc707ac15b9ee167a 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c @@ -1454,6 +1454,7 @@ static int rtl8187_probe(struct usb_interface *intf, 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 @@ static int rtl8187_probe(struct usb_interface *intf, 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 75ffeaa54ed8397abedc9fe48c586d85cd160f0a..e15b462d096bf30fb6ea5148108d108da766cf77 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -1572,7 +1572,14 @@ int rtl_pci_reset_trx_ring(struct ieee80211_hw *hw) 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 aba60c3145c5f6f60b5e34cdbc7885d3042ae2f8..618e509e75d61df86bba9efef721f76e9cf48abc 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c @@ -1125,7 +1125,8 @@ static void _rtl8723be_enable_aspm_back_door(struct ieee80211_hw *hw) /* 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 280196a7f8b3f0c0e9eb646af3ae114cbc397844..ae87b39bca94bcb3ac3e31ba5b42cd1f20577132 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -3434,6 +3434,10 @@ static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf) /* 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 1c539c83e8cf4ed5b5570e83301bcf4fd3bdcca0..5e41bf04ef618db3ba07a7b8fda6b252281c2b95 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -1200,8 +1200,7 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, 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 c5effd6c6be9beea8481d3c1a68e57f5ace403cf..01ca1d57b3d92074d2953195b9776b368f653ead 100644 --- a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c @@ -1278,6 +1278,9 @@ static int eject_installer(struct usb_interface *intf) 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 1a9dadf7b3cc38b71e27dbb4250f86eade804afe..1b287861e34f1a0fd7bedb4831a1229c65fad9af 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1345,6 +1345,7 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev) netif_carrier_off(netdev); + xenbus_switch_state(dev, XenbusStateInitialising); return netdev; exit: @@ -2037,7 +2038,10 @@ static void netback_changed(struct xenbus_device *dev, case XenbusStateInitialised: case XenbusStateReconfiguring: case XenbusStateReconfigured: + break; + case XenbusStateUnknown: + wake_up_all(&module_unload_q); break; case XenbusStateInitWait: @@ -2168,7 +2172,9 @@ static int xennet_remove(struct xenbus_device *dev) 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 af62c4c854f32b95729c766f89fdc643b6ef637f..b4f31dad40d6ef842916152f502d6202bede35d0 100644 --- a/drivers/nfc/nfcmrvl/fw_dnld.c +++ b/drivers/nfc/nfcmrvl/fw_dnld.c @@ -17,7 +17,7 @@ */ #include -#include +#include #include #include #include diff --git a/drivers/nfc/nfcmrvl/spi.c b/drivers/nfc/nfcmrvl/spi.c index a7faa0bcc01e8a101bfafefff157975bc448709b..fc8e78a29d77a83feacd0cda380f6cd736e96fff 100644 --- a/drivers/nfc/nfcmrvl/spi.c +++ b/drivers/nfc/nfcmrvl/spi.c @@ -96,10 +96,9 @@ static int nfcmrvl_spi_nci_send(struct nfcmrvl_private *priv, /* 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 1dc89248e58e5b2876490fb926c80c350d239cda..11d78b43cf76c658989504d0fad328aca89851f7 100644 --- a/drivers/nfc/pn533/i2c.c +++ b/drivers/nfc/pn533/i2c.c @@ -242,10 +242,10 @@ static int pn533_i2c_remove(struct i2c_client *client) 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 9faaa9694d8741adb64b5aae8754f44d24a01e13..77db9795510f2f765f660391af651c75f8f4a781 100644 --- a/drivers/nvdimm/blk.c +++ b/drivers/nvdimm/blk.c @@ -286,8 +286,6 @@ static int nsblk_attach_disk(struct nd_namespace_blk *nsblk) 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 @@ static int nsblk_attach_disk(struct nd_namespace_blk *nsblk) } 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 7121453ec0475bc264b2bdb337ec61459ae6002a..0c46ada027cf4c4e967631e89ea4b4545538956f 100644 --- a/drivers/nvdimm/btt.c +++ b/drivers/nvdimm/btt.c @@ -1392,8 +1392,6 @@ static int btt_blk_init(struct btt *btt) 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 @@ static int btt_blk_init(struct btt *btt) } } 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 0392eb8a0deae995d1a3d6e5cc3c49207c0d99d6..8311a93cabd8966342f19f091c7518ad88b84320 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -812,16 +812,17 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, 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 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, } 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 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, 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 b8fb1ef1fc1524983a9bc3386a42df16dcb71fcc..74257ac92490f443d4d47830f6ba70a6767ec7d1 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c @@ -1747,7 +1747,7 @@ struct device *create_namespace_pmem(struct nd_region *nd_region, } 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 @@ struct device *create_namespace_pmem(struct nd_region *nd_region, * 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 42abdd2391c9b029525f18e0b2fbbade60d9d50e..d6aa59ca68b9e9ce807986122a9c4423f320721b 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c @@ -563,6 +563,12 @@ static struct vmem_altmap *__nvdimm_setup_pfn(struct nd_pfn *nd_pfn, 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 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) 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 719ee5fb2626670484f9e937f477d9bf3f74893a..c823e9346389b33c6063eb871b5615b10ca53292 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -1204,7 +1204,8 @@ static void nvme_set_queue_limits(struct nvme_ctrl *ctrl, 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 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl) 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 e48ecb9303ca40c92917a68c2a41ae25e79ca040..8cc856ecec95769c21fa0e942ccaef9f39e939e4 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -1263,7 +1263,7 @@ static bool nvme_should_reset(struct nvme_dev *dev, u32 csts) 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 @@ static void nvme_reset_work(struct work_struct *work) 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 @@ static void nvme_reset_work(struct work_struct *work) 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 @@ static int nvme_reset(struct nvme_dev *dev) { 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 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) 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 @@ static void nvme_remove(struct pci_dev *pdev) 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/device.c b/drivers/of/device.c index f7a9701200558e634cfeee35d4b14b4bb4ddc5dd..3cda60c036f90f6e1a1873c79d55f41d06119f18 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -223,7 +223,7 @@ ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len) str[i] = '_'; } - return tsize; + return repend; } EXPORT_SYMBOL_GPL(of_device_get_modalias); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 4aba42f92359ae683257b10a94a3760d0a8031da..8d8e4c5ea5c0dffbf70db39105277df4a928293d 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -502,7 +502,7 @@ int of_platform_default_populate(struct device_node *root, } 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 78530d1714dc7422a597314e098a88513208c169..bdce0679674cc0edc3830afbf2523bd35816612b 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -2646,6 +2646,7 @@ enum parport_pc_pci_cards { netmos_9901, netmos_9865, quatech_sppxp100, + wch_ch382l, }; @@ -2708,6 +2709,7 @@ static struct parport_pc_pci { /* 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 @@ static const struct pci_device_id parport_pc_pci_tbl[] = { /* 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-hyperv.c b/drivers/pci/host/pci-hyperv.c index dafb4cdb2b7f2006200bff1434ec8ebbaa1b8a2c..d392a55ec0a9251ea784b01540bcfbffb4e1216e 100644 --- a/drivers/pci/host/pci-hyperv.c +++ b/drivers/pci/host/pci-hyperv.c @@ -351,6 +351,7 @@ enum hv_pcibus_state { hv_pcibus_init = 0, hv_pcibus_probed, hv_pcibus_installed, + hv_pcibus_removed, hv_pcibus_maximum }; @@ -1205,9 +1206,11 @@ static int create_root_hv_pci_bus(struct hv_pcibus_device *hbus) 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 @@ static void pci_devices_present_work(struct work_struct *work) 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 @@ static void hv_eject_device_work(struct work_struct *work) 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 @@ static int hv_pci_probe(struct hv_device *hdev, 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 @@ static int hv_pci_remove(struct hv_device *hdev) 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 b67a94d133e35403a8a29d9997838a8d870f3755..cc681366d2dbda82d919e08e2a4f11a57e783224 100644 --- a/drivers/pci/host/pci-msm.c +++ b/drivers/pci/host/pci-msm.c @@ -1457,6 +1457,10 @@ static void msm_pcie_sel_debug_testcase(struct msm_pcie_dev_t *dev, 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 @@ static int msm_pcie_pm_suspend(struct pci_dev *dev, 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 @@ static void msm_pcie_fixup_suspend(struct pci_dev *dev) 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 af8f6e92e8851ca84b459e4587508960b225d336..b3a87159e4573ecc3355139590b8fe56a7f5a8ee 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -861,7 +861,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp) /* 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 a46b585fae316f2f987080d7bc8dec976089af6b..d44b55879c673dec27e7f46a66f33516d8472b8d 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -587,6 +587,7 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot) { 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 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot) 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 802997e2ddcceb8a5f39937440a2dc83bc4dc988..d81ad841dc0ce5e1d22f441dace8866e8153c0ab 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -463,8 +463,6 @@ static void pci_device_shutdown(struct device *dev) 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/pcie/aspm.c b/drivers/pci/pcie/aspm.c index b0916b126923ea87759b84d72c6f6bf2d80dcecf..e38bb48d32456de46ce7e718131eb55c83ca9abb 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -526,10 +526,14 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev) /* * 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 @@ void pci_disable_link_state(struct pci_dev *pdev, int state) } 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 @@ static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) 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 a98be6db7e93b53503849ec2ebaec8786d0625ea..56340abe4fc69eb6f455a00e44cd9f007181c3ba 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -231,7 +231,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, 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 98eba9127a0b35c26371f3cb3c85491b0363f65a..fb177dc576d6f73358cf4917edf1dcf23034d33b 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3369,22 +3369,29 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PORT_RIDGE, static void quirk_chelsio_extend_vpd(struct pci_dev *dev) { - pci_set_vpd_size(dev, 8192); -} - -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); + 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, PCI_ANY_ID, + quirk_chelsio_extend_vpd); #ifdef CONFIG_ACPI /* @@ -3870,6 +3877,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9230, 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 @@ static int pci_quirk_cavium_acs(struct pci_dev *dev, u16 acs_flags) */ 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 4bc589ee78d03716c708fa18d56844049e7ac433..85774b7a316aac4b8b2d49f8a448d9539cb333b1 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -63,7 +63,7 @@ static void pci_std_update_resource(struct pci_dev *dev, int resno) 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 ffcd00153ee24ec3bb6ec0258560574bfeb9e7f2..df02f98796ecef12ecabfd97b92f5000323c1d70 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -325,10 +325,16 @@ validate_group(struct perf_event *event) 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 @@ static irqreturn_t armpmu_dispatch_irq(int irq, void *dev) * 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 @@ int arm_pmu_device_probe(struct platform_device *pdev, 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; diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index fb38e208f32dd2199f427207fe6a0e19d3987a5a..735d8f7f9d716a98870160de82bd583cf897c12d 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -992,19 +992,16 @@ struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, 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 @@ int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state) 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 @@ void pinctrl_unregister_map(struct pinctrl_map const *map) 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 @@ EXPORT_SYMBOL_GPL(pinctrl_force_sleep); 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 0a965026b134242f1f6de16af1aac8d8d64af403..fc5b18d3db20eff75041cb4351009fbd0eacd17c 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 @@ static int byt_irq_type(struct irq_data *d, unsigned int type) */ 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/meson/pinctrl-meson-gxbb.c b/drivers/pinctrl/meson/pinctrl-meson-gxbb.c index 7511723c6b05bb417b0289fe5747a529bd191055..257c1c2e588830b5825d96b21e36ee953d3f5ea6 100644 --- a/drivers/pinctrl/meson/pinctrl-meson-gxbb.c +++ b/drivers/pinctrl/meson/pinctrl-meson-gxbb.c @@ -138,7 +138,6 @@ static const struct pinctrl_pin_desc meson_gxbb_periphs_pins[] = { 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 49bf7dcb7ed82c082df8c24011fe69dec1ecada7..f826793e972c533077236aebfdf9b83bc16c8f9f 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -1278,8 +1278,16 @@ static int rockchip_gpio_get_direction(struct gpio_chip *chip, unsigned offset) { 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 196559cf06a18f83b6ce468be94d79341eb99454..e9d7dfc4acc857150748b6458f6665590ad7e041 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdm670.c +++ b/drivers/pinctrl/qcom/pinctrl-sdm670.c @@ -1636,6 +1636,9 @@ static struct msm_dir_conn sdm670_dir_conn[] = { {97, 563}, {101, 564}, {103, 565}, + {108, 567}, + {112, 568}, + {113, 569}, {115, 570}, {116, 571}, {117, 572}, @@ -1647,6 +1650,7 @@ static struct msm_dir_conn sdm670_dir_conn[] = { {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 72b730d2fbe03e1b2d432028857625afc639b7a5..bc8ec394d62053470f53c0d5d6b5f1661818a356 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c +++ b/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c @@ -1738,6 +1738,9 @@ static struct msm_dir_conn sdm845_dir_conn[] = { {97, 563}, {101, 564}, {103, 565}, + {108, 567}, + {112, 568}, + {113, 569}, {104, 566}, {115, 570}, {116, 571}, diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c index 04053fe1e980bf059324ef3917deeeb970bd7bef..cfa3e850c49fd854288c1aeb0fa433563b7c0979 100644 --- a/drivers/platform/chrome/cros_ec_proto.c +++ b/drivers/platform/chrome/cros_ec_proto.c @@ -60,12 +60,14 @@ static int send_command(struct cros_ec_device *ec_dev, 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 @@ static int send_command(struct cros_ec_device *ec_dev, 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 f3baf9973989b8005ed88197e60a1a0319ac8808..24f1630a8b3f16fbbd6a7af6845ccd633233a2c7 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -187,7 +187,7 @@ static ssize_t show_ec_version(struct device *dev, 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/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c index dc445a0c07f67c551204aab055547a30767d4bbb..e729c563ce193c2344ec4918575e6a856d11245a 100644 --- a/drivers/platform/msm/gsi/gsi.c +++ b/drivers/platform/msm/gsi/gsi.c @@ -1572,6 +1572,10 @@ static void gsi_program_chan_ctx(struct gsi_chan_props *props, unsigned int ee, 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)); } diff --git a/drivers/platform/msm/gsi/gsi_reg.h b/drivers/platform/msm/gsi/gsi_reg.h index 7817613ed9f123665bde070e95a5ceb519f46a2c..add38767877d589efe40da63db0c237f693f7d54 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/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c index ebf1d2963f060eb0692c78df6106fb431fefd2b8..d9e3ab9d2fcca90cbbc4cdab64e18b243e5c44aa 100644 --- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c +++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c @@ -1847,12 +1847,15 @@ static int ipa3_usb_xdci_connect_internal( } 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( diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c index f062ed259fd4ca74ee496fb8e008c33097d60612..953469aef7a94ad397919c0c3ada87c987f0b2af 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c @@ -432,6 +432,8 @@ static ssize_t ipa_read_hdr(struct file *file, char __user *ubuf, size_t count, 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 +608,14 @@ static int ipa_attrib_dump_eq(struct ipa_ipfltri_rule_eq *attrib) 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 +624,12 @@ static int ipa_attrib_dump_eq(struct ipa_ipfltri_rule_eq *attrib) 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 +651,12 @@ static int ipa_attrib_dump_eq(struct ipa_ipfltri_rule_eq *attrib) 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 +665,12 @@ static int ipa_attrib_dump_eq(struct ipa_ipfltri_rule_eq *attrib) 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 +840,14 @@ static ssize_t ipa_read_flt(struct file *file, char __user *ubuf, size_t count, 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 +866,14 @@ static ssize_t ipa_read_flt(struct file *file, char __user *ubuf, size_t count, 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 +883,8 @@ static ssize_t ipa_read_flt(struct file *file, char __user *ubuf, size_t count, 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 +904,23 @@ static ssize_t ipa_read_flt(struct file *file, char __user *ubuf, size_t count, 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_qmi_service.h b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h index 1f5d619ef5736d2ddd6f11fd4155dffbe9e3aaba..98f5574b28eab8f04a51ab9a3bdb679118f17793 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 inline void ipa_broadcast_quota_reach_ind { } -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/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index c86b0dfd476eb591369b106fd04317ec295b7eb6..130afc729c2192970e3378cc5224a73b57522cb1 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 @@ static int ipa_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /* 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/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 0fc7b0eabf01408724d3e723ec5d9043d640f242..c523b3d1eae80973781b4c15ca31a7d628c6f525 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -737,6 +737,7 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 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 +747,7 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 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 +1845,28 @@ static long ipa3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } 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; @@ -3923,16 +3947,18 @@ int ipa3_set_clock_plan_from_pm(int idx) { 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; @@ -4694,12 +4720,12 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p, ipa3_ctx->ipa_initialization_complete = true; mutex_unlock(&ipa3_ctx->lock); + ipa3_debugfs_init(); + ipa3_trigger_ipa_ready_cbs(); 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: diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c index ee9c49c20bfbf9c23b28348fb74995f912bd9717..dc5f5e0037aa642e22a62edf81ea9533c381dbd7 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c @@ -372,6 +372,8 @@ static ssize_t ipa3_read_hdr(struct file *file, char __user *ubuf, size_t count, 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 +558,12 @@ static int ipa3_attrib_dump_eq(struct ipa_ipfltri_rule_eq *attrib) 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 +575,12 @@ static int ipa3_attrib_dump_eq(struct ipa_ipfltri_rule_eq *attrib) 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 +588,12 @@ static int ipa3_attrib_dump_eq(struct ipa_ipfltri_rule_eq *attrib) 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 +608,14 @@ static int ipa3_attrib_dump_eq(struct ipa_ipfltri_rule_eq *attrib) 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 +808,11 @@ static ssize_t ipa3_read_rt_hw(struct file *file, char __user *ubuf, 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 +843,11 @@ static ssize_t ipa3_read_rt_hw(struct file *file, char __user *ubuf, 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 +921,7 @@ static ssize_t ipa3_read_flt(struct file *file, char __user *ubuf, size_t count, u32 rt_tbl_idx; u32 bitmap; bool eq; + int res = 0; mutex_lock(&ipa3_ctx->lock); @@ -894,6 +931,8 @@ static ssize_t ipa3_read_flt(struct file *file, char __user *ubuf, size_t count, 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 +958,23 @@ static ssize_t ipa3_read_flt(struct file *file, char __user *ubuf, size_t count, 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 +1029,11 @@ static ssize_t ipa3_read_flt_hw(struct file *file, char __user *ubuf, 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 +1061,11 @@ static ssize_t ipa3_read_flt_hw(struct file *file, char __user *ubuf, 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"); } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index 9a0f44a950c64e5e040ee1ee2df070f2a17d8bed..e73349a2104bb6ff8d4fff402267eb6ac3bf19d4 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -646,7 +646,6 @@ int ipa3_send_cmd_timeout(u16 num_desc, struct ipa3_desc *descr, u32 timeout) 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 +684,6 @@ int ipa3_send_cmd_timeout(u16 num_desc, struct ipa3_desc *descr, u32 timeout) kfree(comp); bail: - IPA_ACTIVE_CLIENTS_DEC_SIMPLE(); return result; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c index 29fd547964999cbda2a0437818cbe9d7c38cc5bd..674277364a31f65abe2db4ecacf574896b6b68b0 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 @@ static int ipa3_generate_flt_hw_rule(enum ipa_ip_type ip, 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; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index 50c1e3feb75ea78fc724a36bf25321f90dc358f5..56b5740da5019eeb2b50b6109d2e88a7932b0280 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -2380,4 +2380,5 @@ void __ipa_gsi_irq_rx_scedule_poll(struct ipa3_sys_context *sys); 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); #endif /* _IPA3_I_H_ */ 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 821abe285fb069637dfce1e30aaa4e0c21ae2bf3..3aa8a01f75afc588bad1a2214767d3962ee32522 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 @@ static int ipa3_qmi_init_modem_send_sync_msg(void) 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_v01.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c index 703acd7d1398087adf7448d41fd46c9c3679ddc9..aa56311bc0c6824a6d6f76869a672895b415bb4b 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 @@ -1093,11 +1093,92 @@ struct elem_info ipa3_init_modem_driver_req_msg_data_v01_ei[] = { struct ipa_init_modem_driver_req_msg_v01, 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_uc.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c index f5ef141caf84aed243342bfd851b493e55786f49..e746229db92e22a7a0f7811671cf37a0412ea96f 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 @@ int ipa3_uc_notify_clk_state(bool enabled) { 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 80155f9e2060a1bcd78e2474302fd9890b74a1a2..5d6d3cd9e911e94193ee4e849635a66ef870e2b1 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -1279,31 +1279,31 @@ static const struct ipa_ep_configuration ipa3_ep_mapping true, IPA_v4_0_GROUP_UL_DL, false, IPA_DPS_HPS_SEQ_TYPE_INVALID, - QMB_MASTER_SELECT_PCIE, + QMB_MASTER_SELECT_DDR, { 11, 6, 9, 9, IPA_EE_AP } }, [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, + QMB_MASTER_SELECT_DDR, { 11, 6, 9, 9, IPA_EE_AP } }, [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, + QMB_MASTER_SELECT_DDR, { 12, 2, 5, 5, IPA_EE_AP } }, [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, + QMB_MASTER_SELECT_DDR, { 19, 12, 9, 9, IPA_EE_AP } }, [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, + QMB_MASTER_SELECT_DDR, { 21, 14, 9, 9, IPA_EE_AP } }, /* Dummy consumer (pipe 31) is used in L2TP rt rule */ [IPA_4_0][IPA_CLIENT_DUMMY_CONS] = { @@ -1363,7 +1363,7 @@ static const struct ipa_ep_configuration ipa3_ep_mapping 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] = { + [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, @@ -4326,7 +4326,7 @@ static void ipa3_set_tag_process_before_gating(bool val) * * 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"); diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index 561c533eebd811131f0273b6ab11bbd9b52e6c02..f1e2e6df037f4570070ac8d7c07fe4d7d2711b02 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 @@ static int ipa3_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /* 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/ipa_pm_ut.c b/drivers/platform/msm/ipa/test/ipa_pm_ut.c index e07040a73e28e4fbc60f1990e65cfb73c61acdfb..83b705d420190f6d89bb16a47faa1bdd523f3c3f 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 @@ struct callback_param { 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 @@ static int ipa_pm_ut_single_registration(void *priv) 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 @@ static int ipa_pm_ut_double_register_activate(void *priv) 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 @@ static int ipa_pm_ut_deferred_deactivate(void *priv) 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 @@ static int ipa_pm_ut_two_clients_activate(void *priv) struct ipa_pm_init_params init_params = { - .threshold_size = IPA_PM_THRESHOLD_MAX, + .threshold_size = 2, .default_threshold = {600, 1000} }; @@ -648,7 +662,7 @@ static int ipa_pm_ut_deactivate_all_deferred(void *priv) 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 @@ static int ipa_pm_ut_deactivate_after_activate(void *priv) 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 @@ static int ipa_pm_ut_atomic_activate(void *priv) 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 @@ static int ipa_pm_ut_deactivate_loop(void *priv) 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 @@ static int ipa_pm_ut_set_perf_profile(void *priv) 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 @@ static int ipa_pm_ut_group_tput(void *priv) 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 @@ static int ipa_pm_ut_skip_clk_vote_tput(void *priv) 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 @@ static int ipa_pm_ut_simple_exception(void *priv) }; 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/qcom-geni-se.c b/drivers/platform/msm/qcom-geni-se.c index 6ce21613d5cec56accc65512cbc0158644f66049..ed4b837017fed0f118a6f84f0403bc60ffe51e24 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_fifo_mode(void __iomem *base) 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 @@ static int geni_se_select_dma_mode(void __iomem *base) 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/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index 6eb2837f6b89049f2349427647ac8f5bd84e223f..687cc5b922eea28fb350426abb75b9132b28454d 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -120,6 +120,10 @@ static struct quirk_entry quirk_asus_x550lb = { .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; @@ -150,6 +154,15 @@ static const struct dmi_system_id asus_quirks[] = { */ .driver_data = &quirk_asus_wapf4, }, + { + .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", @@ -411,6 +424,15 @@ static const struct dmi_system_id asus_quirks[] = { }, .driver_data = &quirk_asus_ux303ub, }, + { + .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", diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 8499d3ae4257b68fcbe276e9467ffe61e5a581b8..8a1bfd489c26865cb5c867e4db1adfc79c12a31e 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1108,6 +1108,15 @@ static void asus_wmi_set_xusb2pr(struct asus_wmi *asus) orig_ports_available, ports_available); } +/* + * 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 */ @@ -2120,6 +2129,9 @@ static int asus_wmi_add(struct platform_device *pdev) 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 fdff626c3b51b039f3b63473a6cf333d04fda819..5db052d1de1e16a8eb6edb7e0208e08357957478 100644 --- a/drivers/platform/x86/asus-wmi.h +++ b/drivers/platform/x86/asus-wmi.h @@ -45,6 +45,7 @@ struct quirk_entry { 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 78080763df51768f04548e1627d3baded9bbac14..a74340dff530effc33658ef21f35ba171a2d5411 100644 --- a/drivers/platform/x86/intel-vbtn.c +++ b/drivers/platform/x86/intel-vbtn.c @@ -37,6 +37,10 @@ static const struct acpi_device_id intel_vbtn_ids[] = { 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 b65ce7519411a87f2d279360df1b8fe252efce0b..60ee94e572423228d0c1710d642fbe6b4fb2091f 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -9526,7 +9526,7 @@ static struct ibm_init_struct ibms_init[] __initdata = { }, }; -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 5b318893727bd0ebcc1a9a32b1056a7122134b58..1a28e566c61a9fdb86dfaf38d56663461f42127e 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c @@ -76,7 +76,7 @@ static void *kaslr_imem_addr; #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 @@ static void enable_emergency_dload_mode(void) 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 5cee9aa87aa338b3e310c136317850e47e62d9cd..48a11fd86a7f2995de10ce0947610403f1d2f581 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -3218,11 +3218,13 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di) } /* 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/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index 50171fd3cc6db6f31ed1ee489af04884cafaa687..8ed2782d1bb1b228390c472a8e022f96522fab54 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -506,6 +506,9 @@ static int bq24190_register_reset(struct bq24190_dev_info *bdi) 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 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data) } } 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 4cd6899b961e780e0623e81951ecc12e1fcd3557..95af5f305838a6508bf1b95988a28854d829a6dc 100644 --- a/drivers/power/supply/isp1704_charger.c +++ b/drivers/power/supply/isp1704_charger.c @@ -418,6 +418,10 @@ static int isp1704_charger_probe(struct platform_device *pdev) 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 dfe1ee89f7c7ad06fc014b3986c240904329e6ba..922a86787c5c4c69901bf2f48933e29653e29bf1 100644 --- a/drivers/power/supply/pda_power.c +++ b/drivers/power/supply/pda_power.c @@ -30,9 +30,9 @@ static inline unsigned int get_irq_flags(struct resource *res) 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 update_charger(void) } } -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 @@ static void psy_changed(void) * 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 @@ static irqreturn_t power_changed_isr(int irq, void *power_supply) * 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 @@ static void polling_timer_func(unsigned long unused) 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 @@ static int otg_handle_notification(struct notifier_block *nb, * 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 @@ static int pda_power_probe(struct platform_device *pdev) 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 @@ static int pda_power_probe(struct platform_device *pdev) 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 @@ static int pda_power_remove(struct platform_device *pdev) 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/qcom/Kconfig b/drivers/power/supply/qcom/Kconfig index e571eb472dac27ead188d9f6c29a52ddab7fcdc5..1c8f1166a7fa33604e6a7407d178858534534b13 100644 --- a/drivers/power/supply/qcom/Kconfig +++ b/drivers/power/supply/qcom/Kconfig @@ -29,6 +29,17 @@ config SMB135X_CHARGER 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 @@ config QPNP_QNOVO 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 de76a5bdeacf77aead14040fed50b9d2214e9cb6..ffc1ce3ecb95aa49a0b52549ca89d75466ec549e 100644 --- a/drivers/power/supply/qcom/Makefile +++ b/drivers/power/supply/qcom/Makefile @@ -2,12 +2,15 @@ obj-$(CONFIG_QPNP_FG) += qpnp-fg.o 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/batterydata-interface.c b/drivers/power/supply/qcom/batterydata-interface.c new file mode 100644 index 0000000000000000000000000000000000000000..01878279b0f56585993a8fd598e73797d3567dc6 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 0000000000000000000000000000000000000000..fae5658067d42418472ce8a5a6e404f0c0963732 --- /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 +#include + +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 3d74f7e4452c9d7d980876150b8198f47fad7ebe..9b9a88028dc9523aaa2b6cf32a2a2114d431bc9b 100644 --- a/drivers/power/supply/qcom/fg-alg.c +++ b/drivers/power/supply/qcom/fg-alg.c @@ -164,13 +164,13 @@ void cycle_count_update(struct cycle_counter *counter, int batt_soc, } /** - * 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; @@ -186,6 +186,68 @@ int get_cycle_count(struct cycle_counter *counter) return count; } +/** + * 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 @@ -327,13 +389,15 @@ static int cap_learning_process_full_data(struct cap_learning *cl) */ 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; } diff --git a/drivers/power/supply/qcom/fg-alg.h b/drivers/power/supply/qcom/fg-alg.h index ff5becee881bb835890ebb95db2b4e78cf1103fe..7c2500720297c1e5750e12f3bb2bbde2fa9e02a9 100644 --- a/drivers/power/supply/qcom/fg-alg.h +++ b/drivers/power/supply/qcom/fg-alg.h @@ -21,6 +21,7 @@ struct cycle_counter { 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 +30,8 @@ struct cycle_counter { }; struct cl_params { - int start_soc; + int min_start_soc; + int max_start_soc; int max_temp; int min_temp; int max_cap_inc; @@ -60,7 +62,8 @@ 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, diff --git a/drivers/power/supply/qcom/qg-battery-profile.c b/drivers/power/supply/qcom/qg-battery-profile.c index 441c759a384a85d943676f234afd44fa4fad30ae..36edd76c642add17d45104a4d08674513789331f 100644 --- a/drivers/power/supply/qcom/qg-battery-profile.c +++ b/drivers/power/supply/qcom/qg-battery-profile.c @@ -22,6 +22,7 @@ #include #include #include +#include "qg-battery-profile.h" #include "qg-profile-lib.h" #include "qg-defs.h" @@ -410,6 +411,26 @@ int lookup_soc_ocv(u32 *soc, u32 ocv_uv, int batt_temp, bool charging) 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) { + pr_err("Battery profile not loaded\n"); + 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 143a4f239cfba84f3dcddc2a60fbf1a3dc5ea5a6..1b0627776ff0ba244d2bd5fdf20c55e9221f00a7 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 6e44f335f884785ff854df9240b4d0de38ad4356..a1aeac2b86ef1e869ed4c2262cc4976f2d945006 100644 --- a/drivers/power/supply/qcom/qg-core.h +++ b/drivers/power/supply/qcom/qg-core.h @@ -12,6 +12,9 @@ #ifndef __QG_CORE_H__ #define __QG_CORE_H__ +#include +#include "fg-alg.h" + struct qg_batt_props { const char *batt_type_str; int float_volt_uv; @@ -49,6 +52,8 @@ struct qg_dt { int cold_temp_threshold; bool hold_soc_while_full; bool linearize_soc; + bool cl_disable; + bool cl_feedback_on; }; struct qpnp_qg { @@ -109,12 +114,18 @@ struct qpnp_qg { int maint_soc; int msoc; int pon_soc; + int batt_soc; + int cc_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; }; enum ocv_type { @@ -135,6 +146,7 @@ enum debug_mask { QG_DEBUG_PM = BIT(7), QG_DEBUG_BUS_READ = BIT(8), QG_DEBUG_BUS_WRITE = BIT(9), + QG_DEBUG_ALG_CL = BIT(10), }; enum qg_irq { diff --git a/drivers/power/supply/qcom/qg-defs.h b/drivers/power/supply/qcom/qg-defs.h index 6ae9aa2c0cabf90ff9ea507b25bcc46d855d20f3..2061208ad55ccf49d8a8754a528c94ce5f58a943 100644 --- a/drivers/power/supply/qcom/qg-defs.h +++ b/drivers/power/supply/qcom/qg-defs.h @@ -47,4 +47,7 @@ #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 96533d4dad43f9d09527c0aedcc42c71999b3a8d..66f9be11a7df9dd1063e312f725eb2cf1050106c 100644 --- a/drivers/power/supply/qcom/qg-reg.h +++ b/drivers/power/supply/qcom/qg-reg.h @@ -87,6 +87,8 @@ #define QG_SDAM_OCV_OFFSET 0x4C #define QG_SDAM_IBAT_OFFSET 0x50 #define QG_SDAM_TIME_OFFSET 0x54 +#define QG_SDAM_CYCLE_COUNT_OFFSET 0x58 +#define QG_SDAM_LEARNED_CAPACITY_OFFSET 0x68 #define QG_SDAM_PON_OCV_OFFSET 0x7C #endif diff --git a/drivers/power/supply/qcom/qg-sdam.c b/drivers/power/supply/qcom/qg-sdam.c index 54eed16fa2c23e4c66418e1f7748a7dba1e5287b..7bc4afac1b447d600df17254fec6ce34f4d45f3e 100644 --- a/drivers/power/supply/qcom/qg-sdam.c +++ b/drivers/power/supply/qcom/qg-sdam.c @@ -130,6 +130,53 @@ int qg_sdam_read(u8 param, u32 *data) 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=%0x4x value=%d\n", + offset, *data); + } else { + for (i = 0; i < length; i++) + pr_debug("QG SDAM write offset=%0x4x 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=%0x4x\n", offset); + } else { + for (i = 0; i < length; i++) + pr_debug("QG SDAM read offset=%0x4x 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 51af04c7b493fb21e70fe082c0eda352022c802b..10e684f8ec403d70e23c1c75caa8541af438e24d 100644 --- a/drivers/power/supply/qcom/qg-sdam.h +++ b/drivers/power/supply/qcom/qg-sdam.h @@ -37,5 +37,7 @@ int qg_sdam_write(u8 param, u32 data); 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 c4d504354554a6155ff9494199ddca3aacd3b7ea..af8b158b7c9575f14e668b05a742a67b2dc40667 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 +#include #include #include #include +#include "fg-alg.h" #include "qg-sdam.h" #include "qg-core.h" #include "qg-reg.h" @@ -98,11 +100,12 @@ static bool is_scaling_required(struct qpnp_qg *chip) 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 @@ static void update_msoc(struct qpnp_qg *chip) 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/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c index 440bf74bd26c61851247334fc18c8416d6838d8d..eaf138c99f2cbc0794f356e2e4b3dd97591012b2 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen3.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c @@ -1417,16 +1417,16 @@ static void fg_cap_learning_post_process(struct fg_chip *chip) 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 @@ static void fg_cap_learning_post_process(struct fg_chip *chip) 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 @@ static void fg_cap_learning_post_process(struct fg_chip *chip) 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 @@ static int fg_rconn_config(struct fg_chip *chip) } 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 @@ static int fg_rconn_config(struct fg_chip *chip) } 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_set_recharge_soc(struct fg_chip *chip, int recharge_soc) 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 @@ static int fg_adjust_recharge_soc(struct fg_chip *chip) 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-linear-charger.c b/drivers/power/supply/qcom/qpnp-linear-charger.c new file mode 100644 index 0000000000000000000000000000000000000000..aedc77a1d6f3aede082ac9356b2a14b688fe1107 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 f7a6b7ab36c0887ff2272ffd64496ab4dac7ceaa..5a0682c3d42125cb4692142fe3beb068f818ae6e 100644 --- a/drivers/power/supply/qcom/qpnp-qg.c +++ b/drivers/power/supply/qcom/qpnp-qg.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include "fg-alg.h" #include "qg-sdam.h" #include "qg-core.h" #include "qg-reg.h" @@ -259,8 +261,13 @@ static int qg_process_fifo(struct qpnp_qg *chip, u32 fifo_length) 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"); @@ -641,6 +648,12 @@ static void process_udata_work(struct work_struct *work) 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_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); @@ -946,6 +959,127 @@ static int qg_good_ocv_irq_disable_cb(struct votable *votable, void *data, 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 -EPERM; + + 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 -EPERM; + + 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 -EPERM; + + 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 -EPERM; + + 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 +1102,36 @@ static const char *qg_get_battery_type(struct qpnp_qg *chip) 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); @@ -1046,6 +1199,32 @@ 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; + default: + break; + } return 0; } @@ -1055,6 +1234,7 @@ static int qg_psy_get_property(struct power_supply *psy, { struct qpnp_qg *chip = power_supply_get_drvdata(psy); int rc = 0; + int64_t temp = 0; pval->intval = 0; @@ -1106,6 +1286,27 @@ static int qg_psy_get_property(struct power_supply *psy, 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; default: pr_debug("Unsupported property %d\n", psp); break; @@ -1117,6 +1318,12 @@ static int qg_psy_get_property(struct power_supply *psy, static int qg_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_FULL: + return 1; + default: + break; + } return 0; } @@ -1136,6 +1343,10 @@ static enum power_supply_property qg_psy_props[] = { 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, }; static const struct power_supply_desc qg_psy_desc = { @@ -1262,7 +1473,7 @@ 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"); @@ -1294,6 +1505,24 @@ static void qg_status_change_work(struct work_struct *work) 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); @@ -2069,6 +2298,64 @@ static int qg_request_irqs(struct qpnp_qg *chip) return 0; } +static int qg_alg_init(struct qpnp_qg *chip) +{ + struct cycle_counter *counter; + struct cap_learning *cl; + 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; + + 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, 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 +2369,14 @@ static int qg_request_irqs(struct qpnp_qg *chip) #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 5 +#define DEFAULT_CL_MAX_DEC_DECIPERC 100 +#define DEFAULT_CL_MIN_LIM_DECIPERC 0 +#define DEFAULT_CL_MAX_LIM_DECIPERC 0 static int qg_parse_dt(struct qpnp_qg *chip) { int rc = 0; @@ -2275,6 +2570,65 @@ static int qg_parse_dt(struct qpnp_qg *chip) else chip->dt.rbat_conn_mohm = temp; + /* 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\n", chip->dt.vbatt_empty_mv, chip->dt.vbatt_low_mv, chip->dt.delta_soc); @@ -2392,7 +2746,9 @@ static int process_resume(struct qpnp_qg *chip) 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); } @@ -2464,7 +2820,7 @@ static const struct dev_pm_ops qpnp_qg_pm_ops = { 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 +2853,14 @@ static int qpnp_qg_probe(struct platform_device *pdev) 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; + + 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 +2898,30 @@ static int qpnp_qg_probe(struct platform_device *pdev) 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; + } + } + rc = qg_determine_pon_soc(chip); if (rc < 0) { pr_err("Failed to determine initial state, rc=%d\n", rc); @@ -2627,6 +3015,22 @@ static int qpnp_qg_remove(struct platform_device *pdev) 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 +3045,7 @@ static struct platform_driver qpnp_qg_driver = { }, .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-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c index 7a3a4dcc39693c61094c7ceab44254454d212f91..4fd659e2b5f44ee54376780b26f0bca98d67f8f7 100644 --- a/drivers/power/supply/qcom/qpnp-smb5.c +++ b/drivers/power/supply/qcom/qpnp-smb5.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "smb5-reg.h" #include "smb5-lib.h" #include "schgm-flash.h" @@ -51,6 +52,13 @@ static struct smb_params smb5_pmi632_params = { .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_pmi632_params = { }, }; -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 @@ static struct smb_params smb5_pmi855_params = { .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 @@ module_param_named( 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 @@ static int smb5_chg_config_init(struct smb5 *chip) 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 @@ static int smb5_chg_config_init(struct smb5 *chip) chg->param = smb5_pmi632_params; chg->use_extcon = true; chg->name = "pmi632_charger"; + /* PMI632 does not support PD */ + __pd_disabled = 1; chg->hw_max_icl_ua = (chip->dt.usb_icl_ua > 0) ? chip->dt.usb_icl_ua : PMI632_MAX_ICL_UA; @@ -361,6 +388,61 @@ static int smb5_parse_dt(struct smb5 *chip) 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 +455,6 @@ static enum power_supply_property smb5_usb_props[] = { 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 +470,8 @@ static enum power_supply_property smb5_usb_props[] = { 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 +535,6 @@ static int smb5_usb_get_prop(struct power_supply *psy, 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 +584,22 @@ static int smb5_usb_get_prop(struct power_supply *psy, : 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 +622,6 @@ static int smb5_usb_set_prop(struct power_supply *psy, 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 +663,6 @@ static int smb5_usb_set_prop(struct power_supply *psy, break; } -unlock: - mutex_unlock(&chg->lock); return rc; } @@ -789,6 +877,7 @@ static int smb5_usb_main_set_prop(struct power_supply *psy, { 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 +891,31 @@ static int smb5_usb_main_set_prop(struct power_supply *psy, 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 +1086,7 @@ static enum power_supply_property smb5_batt_props[] = { 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 +1181,9 @@ static int smb5_batt_get_prop(struct power_supply *psy, 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 +1263,8 @@ static int smb5_batt_set_prop(struct power_supply *psy, 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 +1437,42 @@ static int smb5_init_vconn_regulator(struct smb5 *chip) 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 | @@ -1354,14 +1508,6 @@ static int smb5_configure_micro_usb(struct smb_charger *chg) { 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 +1541,6 @@ static int smb5_init_hw(struct smb5 *chip) &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 +1577,31 @@ static int smb5_init_hw(struct smb5 *chip) 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: + * - Rerun APSD to ensure proper charger detection if device + * boots with charger connected. * - Initialize flash module for PMI632 */ - if (chg->smb_version == PMI632_SUBTYPE) + if (chg->smb_version == PMI632_SUBTYPE) { schgm_flash_init(chg); - - smblib_rerun_apsd_if_required(chg); + smblib_rerun_apsd_if_required(chg); + } /* vote 0mA on usb_icl for non battery platforms */ vote(chg->usb_icl_votable, @@ -1460,12 +1618,6 @@ static int smb5_init_hw(struct smb5 *chip) 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 +1625,15 @@ static int smb5_init_hw(struct smb5 *chip) /* * 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 +1643,6 @@ static int smb5_init_hw(struct smb5 *chip) 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 +1851,21 @@ static int smb5_determine_initial_status(struct smb5 *chip) { 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 +2057,7 @@ static struct smb_irq_info smb5_irqs[] = { }, [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 +2353,7 @@ static int smb5_probe(struct platform_device *pdev) 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; @@ -2347,6 +2503,10 @@ static int smb5_remove(struct platform_device *pdev) 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 c4d10b006d63de11c877e0fbfcb15505039acba6..3bb48104db081b46d5c59dec10f49c0b9b9a14f4 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 smbchg_chip { 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 @@ enum wake_reason { "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 @@ static bool smbchg_is_usbin_active_pwr_src(struct smbchg_chip *chip) && (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 @@ static void smbchg_parallel_usb_taper(struct smbchg_chip *chip) 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 @@ static int dc_suspend_vote_cb(struct votable *votable, 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 @@ struct regulator_ops smbchg_otg_reg_ops = { #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 @@ static int smbchg_external_otg_regulator_enable(struct regulator_dev *rdev) * 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 @@ static int smbchg_external_otg_regulator_disable(struct regulator_dev *rdev) * 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 @@ static int smbchg_change_usb_supply_type(struct smbchg_chip *chip, 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 @@ static void restore_from_hvdcp_detection(struct smbchg_chip *chip) { 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 @@ static void restore_from_hvdcp_detection(struct smbchg_chip *chip) 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 @@ static void restore_from_hvdcp_detection(struct smbchg_chip *chip) 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 @@ static void handle_usb_removal(struct smbchg_chip *chip) 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 @@ static bool is_usbin_uv_high(struct smbchg_chip *chip) #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 @@ static void handle_usb_insertion(struct smbchg_chip *chip) 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 @@ static int smbchg_prepare_for_pulsing(struct smbchg_chip *chip) /* 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 @@ static int smbchg_unprepare_for_pulsing(struct smbchg_chip *chip) /* 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 @@ static int smbchg_prepare_for_pulsing_lite(struct smbchg_chip *chip) { 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 @@ static int smbchg_prepare_for_pulsing_lite(struct smbchg_chip *chip) 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 @@ static irqreturn_t fastchg_handler(int irq, void *_chip) 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 @@ static int smbchg_hw_init(struct smbchg_chip *chip) 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 @@ static int smbchg_check_chg_version(struct smbchg_chip *chip) 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 @@ static int smbchg_probe(struct platform_device *pdev) 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 @@ static int smbchg_probe(struct platform_device *pdev) 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 @@ static int smbchg_probe(struct platform_device *pdev) 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 @@ static int smbchg_probe(struct platform_device *pdev) 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 @@ static void smbchg_shutdown(struct platform_device *pdev) 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 @@ static void smbchg_shutdown(struct platform_device *pdev) /* 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 @@ static void smbchg_shutdown(struct platform_device *pdev) 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 0000000000000000000000000000000000000000..042ce99d6b98d981c3e3cf5b4f893f279dfc3ed2 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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/smb1355-charger.c b/drivers/power/supply/qcom/smb1355-charger.c index 327ae630143e9a210690126a867a141ace727be7..1d99ccb9dd14f6db86a15e7d7aedce20cda5d003 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 @@ struct smb1355 { 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 @@ static int smb1355_read(struct smb1355 *chip, u16 addr, u8 *val) 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 @@ static enum power_supply_property smb1355_parallel_props[] = { 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 @@ static int smb1355_parallel_get_prop(struct power_supply *psy, 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 @@ static int smb1355_set_current_max(struct smb1355 *chip, int curr) 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 @@ static int smb1355_parallel_set_prop(struct power_supply *psy, 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 @@ static int smb1355_init_hw(struct smb1355 *chip) { 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 @@ static void smb1355_shutdown(struct platform_device *pdev) 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 0000000000000000000000000000000000000000..ed9c6101685af878b76154547827fe5b79b490a8 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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/smb1390-charger.c b/drivers/power/supply/qcom/smb1390-charger.c index 636265bdb7aed4f68c721c3cd1d1eb5e311f5d6f..55a1f457faeeb99c11a6d19ddab545775251ecb6 100644 --- a/drivers/power/supply/qcom/smb1390-charger.c +++ b/drivers/power/supply/qcom/smb1390-charger.c @@ -67,6 +67,15 @@ #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" @@ -591,11 +600,30 @@ static void smb1390_destroy_votables(struct smb1390 *chip) 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; } diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c index 3152669ef15b9dd3791dd48d1a57daff41622e9d..a5af817e759644437f7b07304931e77f924e46c5 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) { @@ -508,6 +512,23 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg, /******************** * 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_disabled) + 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 +675,7 @@ int smblib_mapping_cc_delta_from_field_value(struct smb_chg_param *param, return 0; } -#define USBIN_100MA 100000 +#define SDP_100_MA 100000 static void smblib_uusb_removal(struct smb_charger *chg) { int rc; @@ -663,10 +684,6 @@ static void smblib_uusb_removal(struct smb_charger *chg) 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 +700,7 @@ static void smblib_uusb_removal(struct smb_charger *chg) /* 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 +723,6 @@ static void smblib_uusb_removal(struct smb_charger *chg) 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 +788,7 @@ static int smblib_get_pulse_cnt(struct smb_charger *chg, int *count) } #define USBIN_25MA 25000 +#define USBIN_100MA 100000 #define USBIN_150MA 150000 #define USBIN_500MA 500000 #define USBIN_900MA 900000 @@ -885,6 +902,10 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) 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; + } /* unsuspend after configuring current and override */ rc = smblib_set_usb_suspend(chg, false); @@ -925,7 +946,8 @@ int smblib_get_icl_current(struct smb_charger *chg, int *icl_ua) 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 +976,6 @@ static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data, 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 +1036,25 @@ int smblib_vconn_regulator_enable(struct regulator_dev *rdev) { struct smb_charger *chg = rdev_get_drvdata(rdev); int rc = 0; + u8 stat, orientation; smblib_dbg(chg, PR_OTG, "enabling VCONN\n"); + 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 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_VALUE_BIT); + VCONN_EN_VALUE_BIT | VCONN_EN_ORIENTATION_BIT, + VCONN_EN_VALUE_BIT | orientation); if (rc < 0) { - smblib_err(chg, "Couldn't enable vconn setting rc=%d\n", rc); + smblib_err(chg, "Couldn't read TYPE_C_CCOUT_CONTROL_REG rc=%d\n", + rc); return rc; } @@ -1447,6 +1469,19 @@ int smblib_get_prop_batt_charge_counter(struct smb_charger *chg, 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 +1625,7 @@ static int smblib_dm_pulse(struct smb_charger *chg) 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 +2012,6 @@ int smblib_get_prop_typec_power_role(struct smb_charger *chg, 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 +2055,7 @@ int smblib_get_prop_pd_in_hard_reset(struct smb_charger *chg, 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 +2142,40 @@ static int smblib_handle_usb_current(struct smb_charger *chg, 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 +2184,13 @@ static int smblib_handle_usb_current(struct smb_charger *chg, */ 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 +2200,12 @@ static int smblib_handle_usb_current(struct smb_charger *chg, 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 +2325,14 @@ int smblib_set_prop_pd_voltage_max(struct smb_charger *chg, 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 +2340,24 @@ static int __smblib_set_prop_pd_active(struct smb_charger *chg, bool pd_active) * 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 +2365,6 @@ static int __smblib_set_prop_pd_active(struct smb_charger *chg, bool pd_active) 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 +2675,7 @@ irqreturn_t icl_change_irq_handler(int irq, void *data) 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); @@ -2752,12 +2778,10 @@ irqreturn_t usb_plugin_irq_handler(int irq, void *data) 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 +2870,6 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, 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 +2882,18 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, 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 +2908,69 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg, 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 +2980,18 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) 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 +3008,14 @@ irqreturn_t usb_source_change_irq_handler(int irq, void *data) 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 +3031,7 @@ irqreturn_t usb_source_change_irq_handler(int irq, void *data) * charger-mis-detection. */ chg->uusb_apsd_rerun_done = true; - smblib_rerun_apsd(chg); + smblib_rerun_apsd_if_required(chg); return IRQ_HANDLED; } @@ -2999,32 +3070,69 @@ irqreturn_t usb_source_change_irq_handler(int irq, void *data) 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; +} + +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; + 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 +3148,7 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) 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 +3156,6 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) 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 +3168,10 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) 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 +3180,6 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) 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 +3187,35 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) 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); - - /* 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) + smblib_notify_device_mode(chg, false); - 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. - */ - if (apsd->pst == POWER_SUPPLY_TYPE_USB_FLOAT && - (get_client_vote(chg->usb_icl_votable, DEFAULT_100MA_VOTER) - <= USBIN_100MA)) - return; - - /* - * handle Rp change for DCP/FLOAT/OCP. - * Update the current only if the Rp is different from - * the last Rp value. + * pre-existing valid vote or FLOAT is configured for + * SDP current. */ - 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) + 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; - - 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); + update_sw_icl_max(chg, apsd->pst); - 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); + smblib_dbg(chg, PR_MISC, "CC change old_mode=%d new_mode=%d\n", + chg->typec_mode, typec_mode); } irqreturn_t typec_or_rid_detection_change_irq_handler(int irq, void *data) @@ -3276,17 +3239,81 @@ irqreturn_t typec_state_change_irq_handler(int irq, void *data) { 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 +3448,55 @@ int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg, 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 +3519,9 @@ static void smblib_uusb_otg_work(struct work_struct *work) 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 +3742,6 @@ static int smblib_create_votables(struct smb_charger *chg) 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 +3779,6 @@ static void smblib_destroy_votables(struct smb_charger *chg) 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 +3790,6 @@ int smblib_init(struct smb_charger *chg) 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 +3802,7 @@ int smblib_init(struct smb_charger *chg) 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 668ce5ffaed5ec07a00d1bb1cc91c8e2a39e10bc..14eba8c8fcb2f74bfb32855b86e48d19369dd246 100644 --- a/drivers/power/supply/qcom/smb5-lib.h +++ b/drivers/power/supply/qcom/smb5-lib.h @@ -56,6 +56,7 @@ enum print_reason { #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 @@ enum print_reason { #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 @@ enum smb_mode { NUM_MODES, }; +enum sink_src_mode { + SINK_MODE, + SRC_MODE, + UNATTACHED_MODE, +}; + enum { BOOST_BACK_WA = BIT(0), }; @@ -228,6 +233,7 @@ struct smb_params { 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,17 @@ struct smb_charger { 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; /* locks */ struct mutex lock; struct mutex ps_change_lock; - struct mutex otg_oc_lock; /* power supplies */ struct power_supply *batt_psy; @@ -295,8 +302,6 @@ struct smb_charger { 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 +319,17 @@ struct smb_charger { 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 +345,10 @@ struct smb_charger { 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 +358,7 @@ struct smb_charger { 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 +427,7 @@ irqreturn_t usb_plugin_irq_handler(int irq, void *data); 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 +462,8 @@ 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_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 +495,6 @@ int smblib_get_prop_typec_cc_orientation(struct smb_charger *chg, 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 +537,8 @@ 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); 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_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 bb2842332e452cb962c509f1f3eead8816f3f10b..79a8bd0c61cb9d22b5b4f2158affb18d13d4e625 100644 --- a/drivers/power/supply/qcom/smb5-reg.h +++ b/drivers/power/supply/qcom/smb5-reg.h @@ -105,6 +105,8 @@ enum { /******************************** * 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 @@ enum { #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 @@ enum { #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 @@ enum { #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 @@ enum { #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 @@ enum { #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 @@ enum { #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 14bde0db8c245680fd010bf2f0aa39093af5a4c2..5b10b50f8686f953a5fe476f3131b0d8a42307bb 100644 --- a/drivers/powercap/powercap_sys.c +++ b/drivers/powercap/powercap_sys.c @@ -538,6 +538,7 @@ struct powercap_zone *powercap_register_zone( 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 86280b7e41f3f79db793c0545c97bb0c1e36e96e..2aa5b37cc6d24d9282cf9c4cfed3d0e4223ac9ce 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -97,30 +97,26 @@ static s32 scaled_ppm_to_ppb(long ppm) /* 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 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct timex *tx) ops = ptp->info; if (tx->modes & ADJ_SETOFFSET) { - struct timespec ts; + struct timespec64 ts; ktime_t kt; s64 delta; @@ -146,7 +142,7 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct timex *tx) 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-rcar.c b/drivers/pwm/pwm-rcar.c index 1c85ecc9e7ac076e7ad6e13333a1a6f4b15b07b6..0fcf94ffad321d8bb10911c6cdb65eebd8c8161b 100644 --- a/drivers/pwm/pwm-rcar.c +++ b/drivers/pwm/pwm-rcar.c @@ -156,8 +156,12 @@ static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 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 e464582a390a58ef68064a1cc925822d2ec95354..3439f1e902cb0e5dbfba93f1bbd88d0a1785c4fa 100644 --- a/drivers/pwm/pwm-stmpe.c +++ b/drivers/pwm/pwm-stmpe.c @@ -145,7 +145,7 @@ static int stmpe_24xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 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 e4647840cd6e3129fb94da036e30470714852f14..7e8906d6ab7a93721a1271486599a1b27101d8d6 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -76,6 +76,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 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 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * 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 3a6d0290c54c0fbd0f1c82ffcd5329c2facc56ae..c5e272ea4372838f589d9d9d2dc0599c9733f400 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -296,6 +296,11 @@ static int anatop_regulator_probe(struct platform_device *pdev) 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 1f6063560e5989f8dbbe4ba215a6ef038924d214..1a2549995bca42f9cdd7783542ffbae4dd1f66cf 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2498,7 +2498,7 @@ static int _regulator_list_voltage(struct regulator *regulator, 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 @@ int regulator_count_voltages(struct regulator *regulator) 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 @@ regulator_register(const struct regulator_desc *regulator_desc, 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 95100168f391022078b9fee332a73eea1636ad80..2eff0cff234e4b66d7274e19a0e2a4a1be631f8c 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 @@ static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl) 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 778f4824bdf614bedaf5d671ad27bdac0fbf92d5..39d3f820c153207b7d76ea68d24267358b385361 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 @@ struct cpr3_panic_regs_info { * 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 cpr3_controller { 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 39ee3c551e025639385bd5eec467cefff43dd831..9e138ce289441768eb1787322fbac1f157144ea8 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 @@ -1240,6 +1240,16 @@ int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl) = of_property_read_bool(ctrl->dev->of_node, "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 diff --git a/drivers/regulator/cpr4-apss-regulator.c b/drivers/regulator/cpr4-apss-regulator.c index 1f8f8be8e9c6c99bb73a498bd87b8872fad38d46..725fc585cabe220aae950350ee100189cf1ebaf7 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 @@ static const char * const cpr4_msm8953_apss_fuse_corner_name[] = { 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 @@ static const struct cpr3_fuse_param 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 @@ static const struct cpr3_fuse_param 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 @@ static const struct cpr3_fuse_param 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 @@ static const struct cpr3_fuse_param 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 @@ static const int 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 @@ static int cpr4_apss_calculate_target_quotients(struct cpr3_regulator *vreg) } 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 @@ static int cpr4_apss_init_controller(struct cpr3_controller *ctrl) 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 @@ static struct platform_driver cpr4_apss_regulator_driver = { .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/rtc/interface.c b/drivers/rtc/interface.c index 99b5e359f62883e19ff8c5811a7c3f8ddf5af5d1..162afccac3964120ee79f5b15e53ae7b090c2b9c 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -227,6 +227,13 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) 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 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) 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 7030d7cd38610f47e9e4bbecc0394068fcfd18da..c554e529fc4e0af49ad9ae2339ff808357c1d27e 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -41,6 +41,9 @@ #include #include #include +#ifdef CONFIG_X86 +#include +#endif /* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */ #include @@ -1117,17 +1120,23 @@ static int cmos_pnp_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) { 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 3b3049c8c9e04ddb8ebfb3c391eba41b320286f3..c0eb113588ff9313b167314c00c4f6cbbfe0c6aa 100644 --- a/drivers/rtc/rtc-ds1374.c +++ b/drivers/rtc/rtc-ds1374.c @@ -527,6 +527,10 @@ static long ds1374_wdt_ioctl(struct file *file, unsigned int cmd, 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 @@ static long ds1374_wdt_ioctl(struct file *file, unsigned int cmd, 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 @@ static long ds1374_wdt_ioctl(struct file *file, unsigned int cmd, 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 58698d21c2c3d37878deedf442a34331659a5c7a..c4ca6a385790b5fa36bd9cfddb54fb30e5e82cd9 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -168,6 +168,7 @@ static int m41t80_get_datetime(struct i2c_client *client, /* 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 @@ static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm) 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 e4324dcf9508ea0b3374c118cbf168adc67664c0..aa53fceaa5e09014e35e6c18c500e53d17d6c790 100644 --- a/drivers/rtc/rtc-opal.c +++ b/drivers/rtc/rtc-opal.c @@ -150,6 +150,16 @@ static int opal_get_tpo_time(struct device *dev, struct rtc_wkalrm *alarm) 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 0f11c2a228e35a02033e1b64dcddc6f5599927cd..a753ef9c14595ca61e234ab2b8b4ff633401c4b3 100644 --- a/drivers/rtc/rtc-snvs.c +++ b/drivers/rtc/rtc-snvs.c @@ -257,7 +257,7 @@ static int snvs_rtc_probe(struct platform_device *pdev) 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; } diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 5ecd40884f015dc61fd591728fcacdfb78449581..0da246505f70f721341dd7f95cc0b9bc5206883f 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1950,8 +1950,12 @@ static int __dasd_device_is_unusable(struct dasd_device *device, { 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 8305ab688d5766b5eaf9b694bd10b52cfc7ec150..a20dc2940d2d552ea3e4ab75e3a0d440aa290904 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -2755,6 +2755,16 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) 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/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 71bf9bded48519c72a5d540a6ee8c1f89efc7bf2..66e9bb053629472db6b78fe80f904555303628a7 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -126,7 +126,7 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq) 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 @@ static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state, 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 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start, 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 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr, 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/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 9b5fc502f6a1f17996f51c2fc48a81f7a3d1b1d7..403712bf1ddf3652ff6b3e9ae59ea80cb494f07c 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -592,6 +592,11 @@ struct qeth_cmd_buffer { 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 @@ struct qeth_trap_id { */ 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 df8f74cb14067738ebb598421d4a851a35e924c7..283416aefa564af20cfb0b9987302a080e0b308e 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -522,8 +522,7 @@ static inline int qeth_is_cq(struct qeth_card *card, unsigned int queue) 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 @@ static int qeth_issue_next_read(struct qeth_card *card) 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 @@ void qeth_clear_thread_running_bit(struct qeth_card *card, unsigned long thread) 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 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, } 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 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, 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 @@ int qeth_send_control_data(struct qeth_card *card, int len, 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 @@ int qeth_send_control_data(struct qeth_card *card, int len, } 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)) ; - qeth_prepare_control_data(card, len, iob); - if (IS_IPA(iob->data)) + 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 + } else { + reply->seqno = QETH_IDX_COMMAND_SEQNO; event_timeout = QETH_TIMEOUT; + } + qeth_prepare_control_data(card, len, iob); + + 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 @@ int qeth_send_control_data(struct qeth_card *card, int len, /* 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 @@ static void qeth_fill_ipacmd_header(struct qeth_card *card, 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 @@ EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags); 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 @@ static void qeth_core_free_card(struct qeth_card *card) 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 5082dfeacb95a15333a63655bdc231fb749dafe8..e94e9579914ee254cde84d485f2013405f10163f 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -1057,8 +1057,8 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev) 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 eedf9b01a496a4ff623ad1d94ac5e7313df2442b..573569474e44174629d057b1a22f6612729eb5d5 100644 --- a/drivers/s390/net/qeth_l3.h +++ b/drivers/s390/net/qeth_l3.h @@ -39,8 +39,40 @@ struct qeth_ipaddr { 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 1487f8a0c575e5a256c3b75ca117bdc50c401412..4ca161bdc696bf28bc855632ff5c0820202ecaa1 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -154,6 +154,24 @@ int qeth_l3_string_to_ipaddr(const char *buf, enum qeth_prot_versions proto, 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 @@ static bool qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card, 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 @@ int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr) 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 @@ int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr) 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 qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr) { int rc = 0; struct qeth_ipaddr *addr; + char buf[40]; QETH_CARD_TEXT(card, 4, "addip"); @@ -286,8 +273,20 @@ int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr) 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 @@ int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr) (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 @@ static void qeth_l3_recover_ip(struct qeth_card *card) 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 @@ int qeth_l3_add_vipa(struct qeth_card *card, enum qeth_prot_versions proto, 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 @@ int qeth_l3_add_rxip(struct qeth_card *card, enum qeth_prot_versions proto, 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 @@ qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev) 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 @@ qeth_l3_add_mc6_to_hash(struct qeth_card *card, struct inet6_dev *in6_dev) 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 void qeth_tso_fill_header(struct qeth_card *card, 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 @@ static void qeth_l3_remove_device(struct ccwgroup_device *cgdev) 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/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c index be65da2988fbca99d3926f30a6ff4f03619d1336..838edbba8aecda0d37f3fa0b09ea16010e6fac98 100644 --- a/drivers/scsi/be2iscsi/be_cmds.c +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -246,6 +246,12 @@ int beiscsi_mccq_compl_wait(struct beiscsi_hba *phba, { 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 fdd4eb4e41b21cf3688fb453959feadef6fa9b72..e58786f797d96e9d50f59f277d1d33ec4568b323 100644 --- a/drivers/scsi/bnx2fc/bnx2fc.h +++ b/drivers/scsi/bnx2fc/bnx2fc.h @@ -191,6 +191,7 @@ struct bnx2fc_hba { 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 f9ddb6156f1499cf2f5b11a00d4bd688b3dce32e..bee7d37367ca0c5fd840d3d4542eb74d84028ea4 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -670,15 +670,17 @@ static struct fc_host_statistics *bnx2fc_get_host_stats(struct Scsi_Host *shost) 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 @@ static struct fc_host_statistics *bnx2fc_get_host_stats(struct Scsi_Host *shost) 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 @@ static struct bnx2fc_hba *bnx2fc_hba_create(struct cnic_dev *cnic) } 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/csiostor/csio_hw.c b/drivers/scsi/csiostor/csio_hw.c index 622bdabc88941430f18ed65b0d70fd9fb0478b9b..dab195f04da78f46921f4eaf044c3f7e953c9a65 100644 --- a/drivers/scsi/csiostor/csio_hw.c +++ b/drivers/scsi/csiostor/csio_hw.c @@ -1769,7 +1769,6 @@ csio_hw_use_fwconfig(struct csio_hw *hw, int reset, u32 *fw_cfg_param) 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 @@ csio_hw_use_fwconfig(struct csio_hw *hw, int reset, u32 *fw_cfg_param) 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 375c536cbc688f0e5b9467ca65e9885b96ea130d..c5eb0c468f0b9c5f2004f02f6f87939756105bd7 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -32,13 +32,13 @@ MODULE_AUTHOR("Open-FCoE.org"); 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 @@ EXPORT_SYMBOL(fcoe_ctlr_destroy_store); * * 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 @@ static int fcoe_transport_create(const char *buffer, struct kernel_param *kp) * * 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 @@ static int fcoe_transport_destroy(const char *buffer, struct kernel_param *kp) * * 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 @@ static int fcoe_transport_disable(const char *buffer, struct kernel_param *kp) * * 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 44dd372aa7d3cd57323afbf99ce069c2112aeb45..c056b8111ad2705f71147813e0ad51b1a0b2ca68 100644 --- a/drivers/scsi/fnic/fnic_scsi.c +++ b/drivers/scsi/fnic/fnic_scsi.c @@ -1127,12 +1127,6 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, 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 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, (((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 @@ int fnic_abort_cmd(struct scsi_cmnd *sc) /* 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 9a0696f68f37153a5b7647c0e5c22ac69574f583..b81a53c4a9a8b1020a96a85fd5e84cde2adb9900 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.h +++ b/drivers/scsi/ibmvscsi/ibmvfc.h @@ -367,7 +367,7 @@ enum ibmvfc_fcp_rsp_info_codes { }; 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 532474109624d9cd9c6a52c040a1027120819886..c5bc41d97f84bb885c730bb0d8c80be38ef7c3ca 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -836,8 +836,10 @@ static void ipr_sata_eh_done(struct ipr_cmnd *ipr_cmd) 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 @@ static void ipr_erp_done(struct ipr_cmnd *ipr_cmd) 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 @@ static void ipr_erp_start(struct ipr_ioa_cfg *ioa_cfg, } 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 @@ static void ipr_scsi_done(struct ipr_cmnd *ipr_cmd) 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 4abd3fce5ab6c73c85ba361bd6665119dd4919d6..c2b682916337991799aa016f5f0dc9b5f34d4626 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -1695,6 +1695,15 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc) */ 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; @@ -1979,6 +1988,19 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) } 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 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) 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 022bb6e10d985b69839753ac9521f9b5b1562320..12886f96b2860c33b7e07a50dc0b2fbaa0df2cf1 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -282,6 +282,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) 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 @@ int sas_smp_get_phy_events(struct sas_phy *phy) 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 @@ int sas_smp_get_phy_events(struct sas_phy *phy) phy->phy_reset_problem_count = scsi_to_u32(&resp[24]); out: + kfree(req); kfree(resp); return res; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index f7e3f27bb5c5590961f55bba385a4157f1302736..e9ea8f4ea2c9200719093d9945a00552e4a23508 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -11312,6 +11312,7 @@ int 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 @@ lpfc_fof_queue_create(struct lpfc_hba *phba) 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 8f1df76a77b6aad6bff35b3ce83c7c2e97f83213..0902ed204ba87b64df97022e2234dcb5d9f543fe 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -13696,6 +13696,9 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq, 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 14c0334f41e4525b89c1e7d43894c553eef28586..26c67c42985c8e1e7f127d0ae84417f5149abea7 100644 --- a/drivers/scsi/mac_esp.c +++ b/drivers/scsi/mac_esp.c @@ -55,6 +55,7 @@ struct mac_esp_priv { 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 @@ static int esp_mac_probe(struct platform_device *dev) } 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 @@ static int esp_mac_probe(struct platform_device *dev) 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 @@ static int esp_mac_remove(struct platform_device *dev) 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 a1a5ceb42ce6d3280dadd5a521e24e4af9a2aff8..8e83e346e0b3473f62ea576c7e25c9c28a9c9aa4 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -105,7 +105,7 @@ _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc); * */ 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 468acab04d3dca9ebbeca8aaf892ed9f024dad69..44da9d8a02d846223e9de09d426159afdd139226 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -281,7 +281,7 @@ struct _scsi_io_transfer { * 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 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) 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 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) 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 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) 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 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) 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 diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 8f12f6baa6b8758167e2b3f699723e1c27505922..4441a559f1391ed88373a15a284a1879375c4038 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -369,6 +369,7 @@ qla24xx_abort_sp_done(void *data, void *ptr, int res) 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_os.c b/drivers/scsi/qla2xxx/qla_os.c index 94630d4738e66c57ababaa6f782b7a098ba274a6..baccd116f864847aa8adaee914dd5a59de932cfc 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -1443,7 +1443,7 @@ qla2x00_loop_reset(scsi_qla_host_t *vha) 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 @@ qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res) */ 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 59059ffbb98c06abc061d1485db2fb0624d27899..11f45cb998927b1fe655c382b0a160d8a57fd32a 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -5789,7 +5789,7 @@ static fc_port_t *qlt_get_port_database(struct scsi_qla_host *vha, 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/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 26e6b05d05fc9bad52f61eb0275bb5d99f196621..43d4b30cbf656b7cf6ff3b936c7e4e61297ed4d0 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -180,7 +180,7 @@ static struct { {"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 scsi_get_device_flags_keyed(struct scsi_device *sdev, 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 84addee05be67a93fdc6a5445ec7618fa449b371..a5e30e9449efed76883e62cc2cd2a77b38feeb3d 100644 --- a/drivers/scsi/scsi_dh.c +++ b/drivers/scsi/scsi_dh.c @@ -56,10 +56,13 @@ static const struct scsi_dh_blist scsi_dh_blist[] = { {"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/ses.c b/drivers/scsi/ses.c index 50adabbb5808902aea6abfd64a39c183678a6a6f..69046d342bc5d553b8d220996a8b2da4621618fe 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -548,7 +548,6 @@ static void ses_enclosure_data_process(struct enclosure_device *edev, 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_enclosure_data_process(struct enclosure_device *edev, } 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 @@ static int ses_intf_add(struct device *cdev, 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 @@ static int ses_intf_add(struct device *cdev, 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 3a7a86d1e3def90df612864e5d1d78d18567e0d7..3d9ad4c08fb36e2e9313df2952c0c64f5d39ee86 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -524,6 +524,7 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) } 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 @@ sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp) } err_out: err2 = sg_finish_rem_req(srp); + sg_remove_request(sfp, srp); return err ? : err2 ? : count; } @@ -663,18 +665,14 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) * 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(Sg_fd * sfp, Sg_request * srp, "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_common_write(Sg_fd * sfp, Sg_request * srp, } sg_finish_rem_req(srp); + sg_remove_request(sfp, srp); return -ENODEV; } @@ -1287,6 +1290,7 @@ sg_rq_end_io_usercontext(struct work_struct *work) 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 @@ sg_finish_rem_req(Sg_request *srp) else sg_remove_scat(sfp, req_schp); - sg_remove_request(sfp, srp); - return ret; } @@ -2066,11 +2068,12 @@ sg_get_rq_mark(Sg_fd * sfp, int pack_id) 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 @@ sg_remove_sfp_usercontext(struct work_struct *work) 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 2bf96d33428abf54293f4afb23756443973e85cc..0dd1984e381e71717a921f6949e4a95b15bf8eaa 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -915,10 +915,11 @@ static void storvsc_handle_error(struct vmscsi_request *vm_srb, 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; diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index c680d76413116c00b80193f5e7db9de2e13441b1..cbc8e9388268819a7447d30bb85293a5966902d0 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #define VIRTIO_SCSI_MEMPOOL_SZ 64 @@ -705,6 +706,28 @@ static int virtscsi_device_reset(struct scsi_cmnd *sc) 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 @@ static struct scsi_host_template virtscsi_host_template_single = { .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 @@ static struct scsi_host_template virtscsi_host_template_multi = { .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/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c index 119054bc922bfc0032d0db52476e4b53985e0d5c..2caacd9d252656006ae0b19d08fa11535baeb2e2 100644 --- a/drivers/soc/fsl/qbman/qman.c +++ b/drivers/soc/fsl/qbman/qman.c @@ -2429,39 +2429,21 @@ struct cgr_comp { 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 2707a827261b10378ef45460082463cf80890dd1..5482302d3d9e1bc34ffde6c3a5630f2c1af7fe38 100644 --- a/drivers/soc/fsl/qe/qe.c +++ b/drivers/soc/fsl/qe/qe.c @@ -163,11 +163,15 @@ EXPORT_SYMBOL(qe_issue_cmd); */ 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 @@ unsigned int qe_get_brg_clk(void) 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/dcc.c b/drivers/soc/qcom/dcc.c index a68728626f0a19238d0096503d4ee331baa4cded..bef0757e112c4bfb468509f0f75b1e7f84491c66 100644 --- a/drivers/soc/qcom/dcc.c +++ b/drivers/soc/qcom/dcc.c @@ -731,7 +731,8 @@ static int dcc_config_add(struct dcc_drvdata *drvdata, unsigned int addr, 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_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c index dda2178b037006e9e71540ba20f5f8e676ea22fa..ca9953a9467e95040ed46a1d9203ed30a430f46f 100644 --- a/drivers/soc/qcom/glink_smem_native_xprt.c +++ b/drivers/soc/qcom/glink_smem_native_xprt.c @@ -175,8 +175,6 @@ struct mailbox_config_info { * @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 edge_info { 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; @@ -1253,18 +1250,6 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx) srcu_read_unlock(&einfo->use_ref, rcu_id); } -/** - * 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 +1269,7 @@ irqreturn_t irq_handler(int irq, void *priv) 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; @@ -2479,7 +2464,6 @@ static int glink_smem_native_probe(struct platform_device *pdev) 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 +2591,6 @@ static int glink_smem_native_probe(struct platform_device *pdev) 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 +2676,6 @@ static int glink_rpm_native_probe(struct platform_device *pdev) 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 +2846,6 @@ static int glink_rpm_native_probe(struct platform_device *pdev) 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 +2975,6 @@ static int glink_mailbox_probe(struct platform_device *pdev) 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 +3106,6 @@ static int glink_mailbox_probe(struct platform_device *pdev) 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 683b074a862e45b91d59c74cee34d5424826b05e..c2542996eb9159c8f9eac6906008431811f39f2c 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -2453,6 +2453,9 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state)) 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", event_data->crashed, priv->state); @@ -2467,9 +2470,6 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, goto out; } - if (priv->force_err_fatal) - ICNSS_ASSERT(0); - icnss_fw_crashed(priv, event_data); out: diff --git a/drivers/soc/qcom/microdump_collector.c b/drivers/soc/qcom/microdump_collector.c index 4a22b4dc6c4eee2e36d9bf4448a6c73350118f20..6e9da60ed1180aaf0e86f11b9240e98de6456fa0 100644 --- a/drivers/soc/qcom/microdump_collector.c +++ b/drivers/soc/qcom/microdump_collector.c @@ -48,9 +48,9 @@ static int microdump_modem_notifier_nb(struct notifier_block *nb, 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 @@ static int microdump_modem_notifier_nb(struct notifier_block *nb, 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 @@ static int microdump_modem_notifier_nb(struct notifier_block *nb, 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/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c index 360dbcf9ee89bdc348682077dfb854dfe1a4fbc4..8776379273193178ee8869d240c4ada6e2dff37d 100644 --- a/drivers/soc/qcom/peripheral-loader.c +++ b/drivers/soc/qcom/peripheral-loader.c @@ -263,6 +263,7 @@ int pil_do_ramdump(struct pil_desc *desc, 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 @@ int pil_do_ramdump(struct pil_desc *desc, * 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 6283477a7952aab0f4d323a5e8135875a51349ee..2a46d0b6a76349575f7556bfc3fddfe14bf7ff29 100644 --- a/drivers/soc/qcom/pil-msa.c +++ b/drivers/soc/qcom/pil-msa.c @@ -797,14 +797,18 @@ int pil_mss_reset_load_mba(struct pil_desc *pil) 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 @@ int pil_mss_debug_reset(struct pil_desc *pil) * 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/ramdump.c b/drivers/soc/qcom/ramdump.c index 7758c64fb9abc5c0a00e27eea3470f0651f80b0f..789eee75e0cbb788c222167c758f001d5756bff5 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 int _do_ramdump(void *handle, struct ramdump_segment *segments, } 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 @@ static int _do_minidump(void *handle, struct ramdump_segment *segments, 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 @@ static int _do_minidump(void *handle, struct ramdump_segment *segments, 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 9dfe281812fe9e22ca2053752a87f7f4c672a814..5abc093003d67d938121c032ff7fd3b2a0c08b13 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 service_locator_send_msg(struct pd_qmi_client_data *pd) 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 @@ static int init_service_locator(void) 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/spcom.c b/drivers/soc/qcom/spcom.c index 0b037d48d49b25920aec4be3e53bdff99c9bcbb5..d568014b52c488ac13cd76064ce2911388ed5ab9 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 @@ static void spcom_link_state_notif_cb(struct glink_link_state_cb_info *cb_info, 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 @@ struct spcom_client *spcom_register_client(struct spcom_client_info *info) 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 @@ static int spcom_handle_write(struct spcom_channel *ch, 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 @@ static ssize_t spcom_device_write(struct file *filp, 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 @@ static int spcom_probe(struct platform_device *pdev) goto fail_ion_client; } - pr_info("Driver Initialization ok.\n"); + pr_debug("Driver Initialization ok.\n"); return 0; @@ -2821,7 +2830,7 @@ static int __init spcom_init(void) { 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/wcnss/wcnss_vreg.c b/drivers/soc/qcom/wcnss/wcnss_vreg.c index 9d24ce114a0bcb5441f4cf4adbecb9ef34ac06e7..1e61250f6ddbfe843e0254c852e856b63c9a2f15 100644 --- a/drivers/soc/qcom/wcnss/wcnss_vreg.c +++ b/drivers/soc/qcom/wcnss/wcnss_vreg.c @@ -206,7 +206,7 @@ wcnss_dt_parse_vreg_level(struct device *dev, int index, 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 @@ wcnss_dt_parse_vreg_level(struct device *dev, int index, 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 @@ wcnss_parse_voltage_regulator(struct wcnss_wlan_config *wlan_config, 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 @@ wcnss_parse_voltage_regulator(struct wcnss_wlan_config *wlan_config, 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 @@ wcnss_parse_voltage_regulator(struct wcnss_wlan_config *wlan_config, 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 @@ wcnss_parse_voltage_regulator(struct wcnss_wlan_config *wlan_config, 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 @@ configure_iris_xo(struct device *dev, 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 @@ configure_iris_xo(struct device *dev, 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 @@ configure_iris_xo(struct device *dev, 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 @@ configure_iris_xo(struct device *dev, 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 @@ configure_iris_xo(struct device *dev, 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 @@ configure_iris_xo(struct device *dev, 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 @@ configure_iris_xo(struct device *dev, 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 @@ static void wcnss_vregs_off(struct vregs_info regulators[], uint size, 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 @@ static void wcnss_vregs_off(struct vregs_info regulators[], uint size, 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 @@ static void wcnss_vregs_off(struct vregs_info regulators[], uint size, 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 @@ static int wcnss_vregs_on(struct device *dev, 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 @@ static int wcnss_vregs_on(struct device *dev, 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 @@ static int wcnss_vregs_on(struct device *dev, 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 @@ static int wcnss_vregs_on(struct device *dev, /* 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 @@ static void wcnss_iris_vregs_off(enum wcnss_hw_type hw_type, 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 @@ static int wcnss_iris_vregs_on(struct device *dev, 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 @@ static void wcnss_core_vregs_off(enum wcnss_hw_type hw_type, 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 @@ static int wcnss_core_vregs_on(struct device *dev, 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 5e94b66081172c85bc701be7f8754f1e9afe1b69..08bd78b55364cfbd207af6eac0d37beb955e0297 100644 --- a/drivers/soc/qcom/wcnss/wcnss_wlan.c +++ b/drivers/soc/qcom/wcnss/wcnss_wlan.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -452,6 +453,53 @@ static struct { 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 @@ static ssize_t wcnss_wlan_macaddr_store(struct device *dev, 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 @@ static ssize_t wcnss_wlan_macaddr_store(struct device *dev, (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 @@ void wcnss_riva_log_debug_regs(void) 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 @@ void wcnss_pronto_is_a2xb_bus_stall(void *tst_addr, u32 fifo_mask, char *type) 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 @@ void wcnss_pronto_log_debug_regs(void) 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 @@ void wcnss_pronto_log_debug_regs(void) 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 @@ void wcnss_pronto_log_debug_regs(void) 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 @@ void wcnss_pronto_log_debug_regs(void) 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 @@ void wcnss_pronto_log_debug_regs(void) 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 @@ void wcnss_pronto_log_debug_regs(void) 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 @@ void wcnss_pronto_log_debug_regs(void) 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 @@ void wcnss_pronto_log_debug_regs(void) 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 @@ void wcnss_pronto_log_debug_regs(void) 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 @@ void wcnss_pronto_log_debug_regs(void) 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 @@ void wcnss_pronto_log_debug_regs(void) (!(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 @@ static int wcnss_gpio_set_state(bool is_enable) 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 @@ static int wcnss_gpio_set_state(bool is_enable) 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 @@ static int wcnss_gpio_set_state(bool is_enable) 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 @@ static void wcnss_log_iris_regs(void) 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 @@ void wcnss_log_debug_regs_on_bite(void) 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 @@ void wcnss_reset_fiq(bool clk_chk_en) 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_remove_sysfs(struct device *dev) 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 @@ static void wcnss_smd_notify_event(void *data, unsigned int event) 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 @@ static void wcnss_smd_notify_event(void *data, unsigned int event) 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 @@ wcnss_pinctrl_set_state(bool active) 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 @@ wcnss_pinctrl_set_state(bool active) 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 @@ wcnss_pinctrl_init(struct platform_device *pdev) 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_init(struct platform_device *pdev) 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_init(struct platform_device *pdev) 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 @@ wcnss_pronto_gpios_config(struct platform_device *pdev, bool enable) /* 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 @@ wcnss_pronto_gpios_config(struct platform_device *pdev, bool enable) 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 @@ wcnss_gpios_config(struct resource *gpios_5wire, bool enable) 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 @@ wcnss_wlan_ctrl_probe(struct platform_device *pdev) 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 @@ wcnss_wlan_ctrl_remove(struct platform_device *pdev) 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 @@ wcnss_ctrl_probe(struct platform_device *pdev) &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 @@ void wcnss_wlan_unregister_pm_ops(struct device *dev, { 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 @@ void wcnss_unregister_thermal_mitigation( { 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 @@ unsigned int wcnss_get_serial_number(void) { 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 @@ int wcnss_get_wlan_mac_address(char mac_addr[WLAN_MAC_ADDR_SIZE]) 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 @@ EXPORT_SYMBOL(wcnss_get_wlan_mac_address); 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 @@ static int enable_wcnss_suspend_notify_set(const char *val, 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; } @@ -1875,12 +1928,12 @@ static int wcnss_smd_tx(void *data, int len) 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; @@ -1892,19 +1945,19 @@ static int wcnss_get_battery_volt(int *result_uv) 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; @@ -1918,7 +1971,7 @@ static void wcnss_notify_vbat(enum qpnp_tm_state state, void *ctx) 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 + @@ -1930,14 +1983,14 @@ static void wcnss_notify_vbat(enum qpnp_tm_state state, void *ctx) 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); @@ -1945,7 +1998,7 @@ static void wcnss_notify_vbat(enum qpnp_tm_state state, void *ctx) &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)); @@ -1958,7 +2011,7 @@ static int wcnss_setup_vbat_monitoring(void) 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; @@ -1973,14 +2026,14 @@ static int wcnss_setup_vbat_monitoring(void) 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; } @@ -1997,12 +2050,12 @@ static void wcnss_send_vbatt_indication(struct work_struct *work) 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) @@ -2020,13 +2073,13 @@ static void wcnss_update_vbatt(struct work_struct *work) 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; @@ -2034,7 +2087,7 @@ static void wcnss_update_vbatt(struct work_struct *work) 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) @@ -2046,13 +2099,13 @@ static unsigned char wcnss_fw_status(void) 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; @@ -2075,7 +2128,7 @@ static void wcnss_send_cal_rsp(unsigned char fw_status) 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); } @@ -2088,7 +2141,7 @@ void extract_cal_data(int len) 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; } @@ -2096,18 +2149,18 @@ void extract_cal_data(int len) 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; } @@ -2125,7 +2178,7 @@ void extract_cal_data(int len) 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; } @@ -2141,7 +2194,7 @@ void extract_cal_data(int len) 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; @@ -2160,7 +2213,7 @@ void extract_cal_data(int len) 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); @@ -2190,18 +2243,18 @@ static void wcnssctrl_rx_handler(struct work_struct *worker) 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); @@ -2212,14 +2265,14 @@ static void wcnssctrl_rx_handler(struct work_struct *worker) 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; @@ -2228,14 +2281,15 @@ static void wcnssctrl_rx_handler(struct work_struct *worker) 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; @@ -2245,41 +2299,43 @@ static void wcnssctrl_rx_handler(struct work_struct *worker) 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; @@ -2289,12 +2345,12 @@ static void wcnssctrl_rx_handler(struct work_struct *worker) 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: @@ -2302,7 +2358,7 @@ static void wcnssctrl_rx_handler(struct work_struct *worker) break; default: - pr_err("wcnss: invalid message type %d\n", phdr->msg_type); + wcnss_log(ERR, "invalid message type %d\n", phdr->msg_type); } } @@ -2315,7 +2371,7 @@ static void wcnss_send_version_req(struct work_struct *worker) 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) @@ -2340,12 +2396,12 @@ static void wcnss_send_pm_config(struct work_struct *worker) 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)); @@ -2355,7 +2411,7 @@ static void wcnss_send_pm_config(struct work_struct *worker) 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); } @@ -2386,7 +2442,8 @@ static void wcnss_nvbin_dnld(void) 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; } @@ -2399,7 +2456,7 @@ static void wcnss_nvbin_dnld(void) 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 */ @@ -2447,11 +2504,12 @@ static void wcnss_nvbin_dnld(void) 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); @@ -2461,10 +2519,11 @@ static void wcnss_nvbin_dnld(void) } 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; } } @@ -2538,11 +2597,12 @@ static void wcnss_caldata_dnld(const void *cal_data, 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); @@ -2552,10 +2612,10 @@ static void wcnss_caldata_dnld(const void *cal_data, } 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; } } @@ -2578,17 +2638,17 @@ static void wcnss_nvbin_dnld_main(struct work_struct *worker) 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(); } @@ -2631,20 +2691,20 @@ void process_usr_ctrl_cmd(u8 *buf, size_t len) 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; } } @@ -2703,7 +2763,7 @@ wcnss_trigger_config(struct platform_device *pdev) 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; } @@ -2742,7 +2802,7 @@ wcnss_trigger_config(struct platform_device *pdev) /* 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; } @@ -2752,7 +2812,7 @@ wcnss_trigger_config(struct platform_device *pdev) } if (ret) { - dev_err(&pdev->dev, "WCNSS gpios config failed.\n"); + wcnss_log(ERR, "gpios config failed.\n"); goto fail_gpio_res; } @@ -2765,7 +2825,7 @@ wcnss_trigger_config(struct platform_device *pdev) "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; } @@ -2785,7 +2845,7 @@ wcnss_trigger_config(struct platform_device *pdev) "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; } @@ -2797,7 +2857,7 @@ wcnss_trigger_config(struct platform_device *pdev) "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; } @@ -2807,7 +2867,7 @@ wcnss_trigger_config(struct platform_device *pdev) 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; } @@ -2818,7 +2878,7 @@ wcnss_trigger_config(struct platform_device *pdev) "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; } @@ -2827,7 +2887,7 @@ wcnss_trigger_config(struct platform_device *pdev) 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; } @@ -2837,7 +2897,7 @@ wcnss_trigger_config(struct platform_device *pdev) "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; } @@ -2846,8 +2906,8 @@ wcnss_trigger_config(struct platform_device *pdev) 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; } @@ -2856,7 +2916,7 @@ wcnss_trigger_config(struct platform_device *pdev) "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; } @@ -2865,8 +2925,8 @@ wcnss_trigger_config(struct platform_device *pdev) 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; } @@ -2874,14 +2934,15 @@ wcnss_trigger_config(struct platform_device *pdev) 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; } @@ -2891,7 +2952,7 @@ wcnss_trigger_config(struct platform_device *pdev) "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; } @@ -2899,8 +2960,8 @@ wcnss_trigger_config(struct platform_device *pdev) 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; } @@ -2908,8 +2969,8 @@ wcnss_trigger_config(struct platform_device *pdev) 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; } @@ -2919,8 +2980,8 @@ wcnss_trigger_config(struct platform_device *pdev) "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 = @@ -2928,7 +2989,8 @@ wcnss_trigger_config(struct platform_device *pdev) 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; } @@ -2937,8 +2999,8 @@ wcnss_trigger_config(struct platform_device *pdev) "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 = @@ -2946,7 +3008,8 @@ wcnss_trigger_config(struct platform_device *pdev) 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; } @@ -2955,7 +3018,7 @@ wcnss_trigger_config(struct platform_device *pdev) "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; } @@ -2964,7 +3027,8 @@ wcnss_trigger_config(struct platform_device *pdev) 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; } @@ -2973,8 +3037,8 @@ wcnss_trigger_config(struct platform_device *pdev) "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 = @@ -2982,7 +3046,8 @@ wcnss_trigger_config(struct platform_device *pdev) 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; } @@ -2991,8 +3056,8 @@ wcnss_trigger_config(struct platform_device *pdev) "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 = @@ -3000,7 +3065,8 @@ wcnss_trigger_config(struct platform_device *pdev) 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; } @@ -3009,8 +3075,8 @@ wcnss_trigger_config(struct platform_device *pdev) "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 = @@ -3018,8 +3084,8 @@ wcnss_trigger_config(struct platform_device *pdev) 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; } @@ -3027,8 +3093,8 @@ wcnss_trigger_config(struct platform_device *pdev) "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; } } @@ -3036,7 +3102,7 @@ wcnss_trigger_config(struct platform_device *pdev) 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); @@ -3045,14 +3111,14 @@ wcnss_trigger_config(struct platform_device *pdev) 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; } @@ -3062,7 +3128,7 @@ wcnss_trigger_config(struct platform_device *pdev) 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); @@ -3070,8 +3136,8 @@ wcnss_trigger_config(struct platform_device *pdev) 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); } } @@ -3079,7 +3145,7 @@ wcnss_trigger_config(struct platform_device *pdev) /* 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(); @@ -3129,7 +3195,7 @@ void wcnss_snoc_vote(bool clk_chk_en) 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; } @@ -3137,13 +3203,15 @@ void wcnss_snoc_vote(bool clk_chk_en) 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 { @@ -3219,7 +3287,7 @@ static int wcnss_node_open(struct inode *inode, struct file *file) 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) @@ -3282,8 +3350,8 @@ static ssize_t wcnss_wlan_write(struct file *fp, const char __user 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; @@ -3297,8 +3365,8 @@ static ssize_t wcnss_wlan_write(struct file *fp, const char __user 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; } @@ -3320,7 +3388,7 @@ static ssize_t wcnss_wlan_write(struct file *fp, const char __user 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); @@ -3342,12 +3410,12 @@ static int wcnss_notif_cb(struct notifier_block *this, unsigned long code, 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) { @@ -3356,7 +3424,7 @@ static int wcnss_notif_cb(struct notifier_block *this, unsigned long code, 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) { @@ -3407,30 +3475,30 @@ static int wcnss_cdev_register(struct platform_device *pdev) 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; } @@ -3438,11 +3506,11 @@ static int wcnss_cdev_register(struct platform_device *pdev) 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; } @@ -3466,7 +3534,7 @@ static int wcnss_cdev_register(struct platform_device *pdev) 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); @@ -3483,7 +3551,7 @@ wcnss_wlan_probe(struct platform_device *pdev) /* 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; } @@ -3497,7 +3565,7 @@ wcnss_wlan_probe(struct platform_device *pdev) 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; } @@ -3511,7 +3579,7 @@ wcnss_wlan_probe(struct platform_device *pdev) /* 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); } @@ -3534,7 +3602,7 @@ wcnss_wlan_probe(struct platform_device *pdev) * 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); } @@ -3579,6 +3647,11 @@ static struct platform_driver wcnss_wlan_driver = { 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); @@ -3599,6 +3672,8 @@ static void __exit wcnss_wlan_exit(void) 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 6ee1da02af399b833503a0885d6485303a2b00e0..fc96f62a4838baa720830c585f15eee34265ff1a 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -156,7 +156,6 @@ config SPI_BCM63XX_HSSPI 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 8feac599e9ab4bfe886b8da89960f04ed32c1aba..44be6b593b30dc65a1ae955716c2e11b0bca806a 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1669,12 +1669,12 @@ static int atmel_spi_remove(struct platform_device *pdev) 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-davinci.c b/drivers/spi/spi-davinci.c index 02fb96797ac8b9ec52f41c8a13f93b290db0fc1c..0d8f43a17edbafe2c7d7663976219b481a0daa78 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -646,7 +646,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) 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 447497e9124c922a4e3ecc20af8ab7ee91062e99..d25cc4037e239d0df386a06253049dd25a79b2b2 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -115,8 +115,8 @@ static int dw_spi_mmio_remove(struct platform_device *pdev) { 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 a074763baaf210313121e55dc92609016d9908ab..dfa387c24fedbe2166f209dcdee93990a4bf15e9 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -356,9 +356,6 @@ static struct msm_gpi_tre *setup_config0_tre(struct spi_transfer *xfer, 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 @@ static int setup_gsi_xfer(struct spi_transfer *xfer, } 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); @@ -940,8 +940,6 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, 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 +948,12 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, 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) { @@ -966,8 +968,9 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, 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); + "%s: trans_len %d xferlen%d tx_cfg 0x%x cmd 0x%x cs %d\n", + __func__, trans_len, xfer->len, spi_tx_cfg, m_cmd, + xfer->cs_change); if (m_cmd & SPI_TX_ONLY) geni_write_reg(mas->tx_wm, mas->base, SE_GENI_TX_WATERMARK_REG); /* Ensure all writes are done before the WM interrupt */ diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index d5157b2222ce1d8837ed204598107f4968218aef..a47cf638460a6bf7e2665d410192552cce6b2ad0 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -454,6 +454,8 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, 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 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, 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 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, 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-sun6i.c b/drivers/spi/spi-sun6i.c index 9918a57a6a6e643456d95dfaf53a6d85e428cbac..7e7da97982aafc24172a5c2fceef5ae186f02523 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -464,7 +464,7 @@ static int sun6i_spi_probe(struct platform_device *pdev) 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 6db80635ace81f02d07edc1fc2e6119338de3e6f..c2e85e23d538e504f27ed793b2648464cde4ccf3 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -743,8 +743,14 @@ static int spi_map_buf(struct spi_master *master, struct device *dev, 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/staging/android/Kconfig b/drivers/staging/android/Kconfig index 7bd27a42b9e8bec1f31f9d00b615341ce4f3269d..a17c483f906e98e983dc391a8b5b7d88ab27d79e 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -33,6 +33,15 @@ config ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES /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 21b0ff4a158aa9a0a41f9ab423335c5bd933ac59..93c5f5a7390ad758199c8f6b945513eaf432c198 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger/ 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 64d8c87209605cbfebbed27378adf9fd9a244bcb..dd64148f158705a0ffdc107c57649a21cd9870ca 100644 --- a/drivers/staging/android/TODO +++ b/drivers/staging/android/TODO @@ -33,5 +33,15 @@ sync framework: - 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. + - Use ioremap_wc instead of ioremap_nocache. + - 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 and Cc: Arve Hjønnevåg and Riley Andrews diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index 35697e602a7f8bedcc93ad7f31b139495e9a06cd..f4ffac437d9333e4a5556461e9ba6c6e42804d4d 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -343,24 +343,23 @@ static loff_t ashmem_llseek(struct file *file, loff_t offset, int origin) 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 @@ static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd, 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_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c index 800c167c8412325719bfc1462cb379d6fe9d04fc..3ac71ed62619c70d6cd19d1650b50db2c3b415f1 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 high_order_gfp_flags = (GFP_HIGHUSER | __GFP_NOWARN | 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/uapi/vsoc_shm.h b/drivers/staging/android/uapi/vsoc_shm.h new file mode 100644 index 0000000000000000000000000000000000000000..741b1387c25b730a96a00ec8b881749f7272097e --- /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 + +/** + * 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 0000000000000000000000000000000000000000..587c66d709b97e759901aa6909d1ecdb1c178224 --- /dev/null +++ b/drivers/staging/android/vsoc.c @@ -0,0 +1,1169 @@ +/* + * 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 + * + * Based on cirrusfb.c and 8139cp.c: + * Copyright 1999-2001 Jeff Garzik + * Copyright 2001-2004 Jeff Garzik + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; + int irq_requested; + int 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 *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; + /* + * Flags that indicate what we've initialzied. These are used to do an + * orderly cleanup of the device. + */ + char enabled_device; + char requested_regions; + char cdev_added; + char class_added; + char msix_enabled; + /* Mutex that protectes the permission list */ + struct mutex mtx; + /* Major number assigned by the kernel */ + int major; + + struct cdev cdev; + struct class *class; +}; + +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 *, size_t, loff_t *); +static ssize_t vsoc_write(struct file *, const char *, 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 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 *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_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 *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 = 1; + 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 = 1; + /* 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 ioremap 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 %p size=0x%zx\n", + (void *)vsoc_dev.shm_phys_start, vsoc_dev.shm_size); + /* TODO(ghartman): ioremap_wc should work here */ + vsoc_dev.kernel_mapped_shm = ioremap_nocache( + vsoc_dev.shm_phys_start, vsoc_dev.shm_size); + 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 *)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 = 1; + 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 = 1; + vsoc_dev.regions = (struct vsoc_device_region *) + (vsoc_dev.kernel_mapped_shm + + 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 = 1; + 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 = + vsoc_dev.kernel_mapped_shm + + region->region_begin_offset + + h_to_g_signal_table->interrupt_signalled_offset; + vsoc_dev.regions_data[i].outgoing_signalled = + vsoc_dev.kernel_mapped_shm + + 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 = 1; + 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 = 1; + } + 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 = 0; + } + if (vsoc_dev.regions_data[i].irq_requested) + free_irq(vsoc_dev.msix_entries[i].vector, NULL); + vsoc_dev.regions_data[i].irq_requested = 0; + } + kfree(vsoc_dev.regions_data); + vsoc_dev.regions_data = 0; + } + if (vsoc_dev.msix_enabled) { + pci_disable_msix(pdev); + vsoc_dev.msix_enabled = 0; + } + kfree(vsoc_dev.msix_entries); + vsoc_dev.msix_entries = 0; + vsoc_dev.regions = 0; + if (vsoc_dev.class_added) { + class_destroy(vsoc_dev.class); + vsoc_dev.class_added = 0; + } + if (vsoc_dev.cdev_added) { + cdev_del(&vsoc_dev.cdev); + vsoc_dev.cdev_added = 0; + } + 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 = 0; + if (vsoc_dev.kernel_mapped_shm) { + pci_iounmap(pdev, vsoc_dev.kernel_mapped_shm); + vsoc_dev.kernel_mapped_shm = 0; + } + if (vsoc_dev.regs) { + pci_iounmap(pdev, vsoc_dev.regs); + vsoc_dev.regs = 0; + } + if (vsoc_dev.requested_regions) { + pci_release_regions(pdev); + vsoc_dev.requested_regions = 0; + } + if (vsoc_dev.enabled_device) { + pci_disable_device(pdev); + vsoc_dev.enabled_device = 0; + } + /* 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 "); +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 a5bf2cc165c01e6c9a452ed56e6b02a5e901b4fe..1736248bc5b83496c2a70ca8fdd19e0faacc5542 100644 --- a/drivers/staging/comedi/drivers.c +++ b/drivers/staging/comedi/drivers.c @@ -484,8 +484,7 @@ unsigned int comedi_nsamples_left(struct comedi_subdevice *s, 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 a574885ffba9f5937b1514719f10428052537436..18c5312f7886f1067da57d37bcb1d4757a9bb5f1 100644 --- a/drivers/staging/comedi/drivers/ni_mio_common.c +++ b/drivers/staging/comedi/drivers/ni_mio_common.c @@ -1284,6 +1284,8 @@ static void ack_a_interrupt(struct comedi_device *dev, unsigned short a_status) 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/mdc/mdc_request.c b/drivers/staging/lustre/lustre/mdc/mdc_request.c index f56ea643f9bf906d437cc180993ff97f79c8789c..6a146f45bedb514baeef80eb966b41f2d3e659e9 100644 --- a/drivers/staging/lustre/lustre/mdc/mdc_request.c +++ b/drivers/staging/lustre/lustre/mdc/mdc_request.c @@ -1219,9 +1219,9 @@ struct readpage_param { * 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/ptlrpc/sec.c b/drivers/staging/lustre/lustre/ptlrpc/sec.c index a7416cd9ac71777424aee2101c5993b2447435d2..7b0587d8b1761e0c5f7fd5205cc3e87d6955a9a8 100644 --- a/drivers/staging/lustre/lustre/ptlrpc/sec.c +++ b/drivers/staging/lustre/lustre/ptlrpc/sec.c @@ -838,7 +838,7 @@ void sptlrpc_request_out_callback(struct ptlrpc_request *req) 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/speakup/kobjects.c b/drivers/staging/speakup/kobjects.c index e744aa9730ffd2385f9f96af27769a50be168c57..dea018cba0944ded848c048586f3e33880d053d6 100644 --- a/drivers/staging/speakup/kobjects.c +++ b/drivers/staging/speakup/kobjects.c @@ -834,7 +834,9 @@ static ssize_t message_show(struct kobject *kobj, 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 @@ static ssize_t message_store(struct kobject *kobj, struct kobj_attribute *attr, { 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 5a7a87efed27d3a9eab19ea3c0358b324c787672..28b5392153a8b356d6abe3f5a383f795c517a981 100644 --- a/drivers/staging/unisys/visorhba/visorhba_main.c +++ b/drivers/staging/unisys/visorhba/visorhba_main.c @@ -842,7 +842,7 @@ static void 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 @@ do_scsi_nolinuxstat(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd) 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 @@ do_scsi_nolinuxstat(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd) if (scsi_sg_count(scsicmd) == 0) { memcpy(scsi_sglist(scsicmd), buf, cmdrsp->scsi.bufflen); + kfree(buf); return; } @@ -879,6 +884,7 @@ do_scsi_nolinuxstat(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd) 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 6ab7443eabdefa2bdf7c4a4653433e10250b9bf2..6326375b76ab2cba6cc259fe73fd1330a86abea2 100644 --- a/drivers/staging/wilc1000/host_interface.c +++ b/drivers/staging/wilc1000/host_interface.c @@ -1930,6 +1930,8 @@ static s32 Handle_Get_InActiveTime(struct wilc_vif *vif, 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 242f82f4d24f296faabcb7c719f72d82852e39f6..d8ab4cb112894c76917f8519d49835ac6dc7185e 100644 --- a/drivers/staging/wilc1000/linux_mon.c +++ b/drivers/staging/wilc1000/linux_mon.c @@ -197,6 +197,8 @@ static netdev_tx_t WILC_WFI_mon_xmit(struct sk_buff *skb, 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 170de1c9eac4d9251211143939d67a0eb494e856..f815f9d5045f342049dfa3a9b4b510ec5191692a 100644 --- a/drivers/staging/wlan-ng/prism2mgmt.c +++ b/drivers/staging/wlan-ng/prism2mgmt.c @@ -169,7 +169,7 @@ int prism2mgmt_scan(struct wlandevice *wlandev, void *msgp) 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 97928b42ad6253062cc048fee5436625f2e29343..57a3d39363645dd24e8d4f2e2b80955683a20203 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -276,12 +276,11 @@ static int fd_do_rw(struct se_cmd *cmd, struct file *fd, 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 @@ static int fd_do_rw(struct se_cmd *cmd, struct file *fd, 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/imx_thermal.c b/drivers/thermal/imx_thermal.c index 06912f0602b75ba1a7d801babc686f5305838b59..b7cb49afa0560746cb615315f9a1a40eda74f739 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -587,6 +587,9 @@ static int imx_thermal_probe(struct platform_device *pdev) 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 @@ static int imx_thermal_probe(struct platform_device *pdev) return ret; } - data->irq_enabled = true; - data->mode = THERMAL_DEVICE_ENABLED; - return 0; } diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c index b4d3116cfdafe81767b2b1c91fcab4a034f29041..3055f9a12a17087cfee105400b2cca3e68cbb166 100644 --- a/drivers/thermal/power_allocator.c +++ b/drivers/thermal/power_allocator.c @@ -523,6 +523,7 @@ static void allow_maximum_power(struct thermal_zone_device *tz) 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 @@ static void allow_maximum_power(struct thermal_zone_device *tz) 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 5c0022e2a2e9241e70c2ef08010e4a307a7b8e9f..5d345cceb7028504288656bd9d51ad37975049cf 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 @@ static int32_t qpnp_adc_tm_timer_interval_select( 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 @@ static int32_t qpnp_adc_tm_timer_interval_select( 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 @@ static int32_t qpnp_adc_tm_timer_interval_select( 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 @@ static int32_t qpnp_adc_tm_timer_interval_select( 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 @@ static int32_t qpnp_adc_tm_timer_interval_select( 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 @@ static int32_t qpnp_adc_tm_timer_interval_select( 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 int qpnp_adc_tm_initial_setup(struct qpnp_adc_tm_chip *chip) 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/tsens.h b/drivers/thermal/tsens.h index 730d1243bf0350705f86df4e8950f1df386c7f92..d35b867d92fdf849b399772bd44389e04a95ed02 100644 --- a/drivers/thermal/tsens.h +++ b/drivers/thermal/tsens.h @@ -153,8 +153,8 @@ struct tsens_device { 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; diff --git a/drivers/thermal/tsens1xxx.c b/drivers/thermal/tsens1xxx.c index d63fcd1e74d25e5aee98db0f08686909c315c7f2..19e2b5a91b5c730ee57c22a3ab78325e5a619bf2 100644 --- a/drivers/thermal/tsens1xxx.c +++ b/drivers/thermal/tsens1xxx.c @@ -517,8 +517,8 @@ static irqreturn_t tsens_irq_thread(int irq, void *data) 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 +539,8 @@ static irqreturn_t tsens_irq_thread(int irq, void *data) 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], diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index a8c20413dbda9711e1828a064f8a940900442e31..cba6bc6ab9ed7624a404e577d2e8dde8059fa8ab 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -628,6 +628,7 @@ static const struct dev_pm_ops nhi_pm_ops = { * 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 54cab59e20edf9e34220354c4c2e870f3c35d027..fe2291795d2fbe9c979501217204f4b107caea98 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -1467,6 +1467,10 @@ static void gsm_dlci_open(struct gsm_dlci *dlci) * 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 +1484,15 @@ static void gsm_dlci_t1(unsigned long data) 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); + gsm_dlci_open(dlci); + } else { gsm_dlci_close(dlci); + } + break; case DLCI_CLOSING: dlci->retries--; @@ -1499,8 +1510,8 @@ static void gsm_dlci_t1(unsigned long data) * @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. */ diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index faf50df816224feb643d75f79c75aa2800297474..1c70541a146724fcfcea8bda926d9679e7c64d10 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -2182,6 +2182,12 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, } 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 459d726f9d59bc709a889163964f775866e7adb4..3eb01a719d2295dbb589f8d574c669906cb2a1f4 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -464,7 +464,8 @@ static int dw8250_probe(struct platform_device *pdev) /* 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 da31159a03ecfc0c88a2a6edf9c58acf5dfe4964..e8b34f16ba2c95fc7420a20a1eca181ca62c4901 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -613,6 +613,10 @@ static int omap_8250_startup(struct uart_port *port) 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 b80ea872b039e77cac8b56e10763ef2cf0adea1b..e82b3473b6b8d59ca014aa1137796627693709ff 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -5099,6 +5099,17 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS400, 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 */ diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index e2c33b9528d82ed7a2c27d083d7b1d222da68178..b42d7f1c9089bd6b3a1c6d96d56d823b78694d64 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1302,14 +1302,15 @@ static void pl011_stop_tx(struct uart_port *port) 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 @@ static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c, 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 @@ static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq) 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/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 4d079cdaa7a36738ad099a00b85808331159bedf..addb287cacea9910708f9abf76464c53f85f0d75 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1780,6 +1780,7 @@ static void atmel_get_ip_name(struct uart_port *port) 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 c3651540e1ba1aefd89c2772b0ab849a939d8c13..3b31fd8863eb51ddbcbe28bd149371166e48366c 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -253,11 +253,12 @@ int __init of_setup_earlycon(const struct earlycon_id *match, } 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/imx.c b/drivers/tty/serial/imx.c index 521a6e4507559461ef9de2ab651e7576146c7acb..f575a33974faa52b7625338ad512e86d3d6a52f0 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1316,19 +1316,10 @@ static int imx_startup(struct uart_port *port) 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 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, 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); @@ -2149,6 +2138,27 @@ static int serial_imx_probe(struct platform_device *pdev) 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. + */ + writel(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 { + writel(0, sport->port.membase + UFCR); + } + clk_disable_unprepare(sport->clk_ipg); /* diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index a260cde743e272657673474d9612c0bf663d3995..5532c440bf61be00193101ecc2e15e5e63e17400 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -245,7 +245,8 @@ static void kgdboc_put_char(u8 chr) 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 f76b3dba0bc0cc9164859acf42b2e92483b520ab..8e5d1000ff12ffb43aea7b8b34b73abcfb98ea78 100644 --- a/drivers/tty/serial/msm_geni_serial.c +++ b/drivers/tty/serial/msm_geni_serial.c @@ -1660,7 +1660,7 @@ static int msm_geni_serial_port_setup(struct uart_port *uport) 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; diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index fcf803ffad1931c45ee297d862209f3b09ae76d2..cdd2f942317c59fe4ec04c022a004d871f551fde 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -884,14 +884,19 @@ static int sccnxp_probe(struct platform_device *pdev) 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 { + clk_prepare_enable(clk); + 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)) { diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index dce39de6e3d5742f68953fc9fd3f8d7f6402ef2d..839cee47dc27d3abbff3265987dffaad624fd565 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1138,6 +1138,8 @@ static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state) 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 15eaea53b3df81ebbf1b2fe2be8ebc8e5280fcff..107f0d194ac5fcfd8fd34d0feae0a6cd46941051 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -935,6 +935,8 @@ static void sci_receive_chars(struct uart_port *port) /* 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 @@ static void sci_free_dma(struct uart_port *port) 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_request_dma(struct uart_port *port) 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 @@ static const struct uart_ops sci_uart_ops = { .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/tty_io.c b/drivers/tty/tty_io.c index 8d9f9a803b42987f3f4cdd994ede69f7bba129a6..4ee0a9de75566b2c6aaba3465d958b135ea4d762 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -709,6 +709,14 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) 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 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) * 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 @@ static void release_tty(struct tty_struct *tty, int idx) 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); diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index ce2c3c6349d454faba658a3694a86f9ff607f62a..9e1ac58e269e138a3e258e216c2f10830137c0aa 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1354,6 +1354,11 @@ static void csi_m(struct vc_data *vc) 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 @@ static void csi_m(struct vc_data *vc) vc->vc_disp_ctrl = 1; vc->vc_toggle_meta = 1; break; - case 21: case 22: vc->vc_intensity = 1; break; @@ -1727,7 +1731,7 @@ static void reset_terminal(struct vc_data *vc, int do_clear) 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 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) 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 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) 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 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) 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 fba021f5736afdee41fd914da4648e8bf71874b6..208bc52fc84da7e495a7818ce26fc6a73890b44d 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -279,7 +279,7 @@ static int uio_dev_add_attributes(struct uio_device *idev) 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 @@ static int uio_dev_add_attributes(struct uio_device *idev) 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 @@ static int uio_dev_add_attributes(struct uio_device *idev) 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 @@ static int uio_dev_add_attributes(struct uio_device *idev) 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 6e0d614a807554b997691fd1a8489de79a2ac2c9..64c6af2c85593ab41ffa3922606352e9bce7fb87 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -839,7 +839,7 @@ static inline void ci_role_destroy(struct ci_hdrc *ci) { 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 @@ static int ci_hdrc_probe(struct platform_device *pdev) /* 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 @@ static int ci_hdrc_probe(struct platform_device *pdev) 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/core/generic.c b/drivers/usb/core/generic.c index 0f10ff2ca0c7d90e5b552f7956584a6f0724feec..8df85f68fb481b3454f42714d9989e9ec51709ba 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -240,8 +240,13 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg) 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/message.c b/drivers/usb/core/message.c index 4f8221e3a0ffcef56333dff9b65e3e14d14100de..9016a9b21ee42ccfeb0deef37ef857fc6c38b014 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -148,6 +148,10 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, 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 c05c4f87775043e271574458019759b92b74d1df..4f1c6f8d4352b7ee1c99106471bcbb1f33d3894c 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -225,8 +225,12 @@ static const struct usb_device_id usb_quirk_list[] = { { 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/hcd.c b/drivers/usb/dwc2/hcd.c index df5a065780054f21841ca9f08b8ab118922c530b..919a321530601db9dd43af0a271cbe9e0b05a1f7 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3237,8 +3237,12 @@ static void dwc2_conn_id_status_change(struct work_struct *work) 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 2095579800e2888bf0eeeb0bebad3eb1b62fad97..ef3f542cd59a643fb87f608d35b4e43db7928f3a 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -252,7 +252,7 @@ static int dwc3_core_reset(struct dwc3 *dwc) /* 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); @@ -570,6 +570,12 @@ static int dwc3_phy_setup(struct dwc3 *dwc) 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 @@ -913,6 +919,17 @@ int dwc3_core_init(struct dwc3 *dwc) 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: diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 4407a83076e674b95e4f342710d45e0b4afd355d..e5fe7a472aab711cfc75e1bb9d7891612c98ca3f 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) diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c index 72664700b8a25c7d9bcee89c16c68fa6b5792565..12ee23f53cdde17ea924344ff7e84d88cf9881cb 100644 --- a/drivers/usb/dwc3/dwc3-keystone.c +++ b/drivers/usb/dwc3/dwc3-keystone.c @@ -107,6 +107,10 @@ static int kdwc3_probe(struct platform_device *pdev) 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 989956d6f7171ec5c9d1c98cd6a40eee5f11c864..810546a23518f18dc5b463b343dc070f2730af40 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -3593,6 +3593,8 @@ static int dwc3_msm_probe(struct platform_device *pdev) 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) { diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 427291a19e6deeda040f830087e77d1e217740a4..d6493abcf6bc28f7ec0b20a9312c383cc6ddd9d8 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -173,7 +173,7 @@ static int dwc3_pci_probe(struct pci_dev *pci, 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/gadget.c b/drivers/usb/dwc3/gadget.c index 243c0786443e7225d05b70d2e4efee0678be9c9c..f0e4d5e05b68d9838ebb7f57a3867a38504a5233 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1307,7 +1307,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) 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 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) 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/function/Makefile b/drivers/usb/gadget/function/Makefile index a9344a57b7f80c460d808d7044159ed0c2cd5ed2..b8fa93e6c224d4fce9e43cb148291ef57fe79e92 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -64,7 +64,7 @@ usb_f_gsi-y := f_gsi.o rndis.o 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_fs.c b/drivers/usb/gadget/function/f_fs.c index 7897f68ce199c964b7a507a390da70866a4406bb..9974332315883e46439fc8d39d9a8e33ea06f3ea 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1097,7 +1097,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) 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 @@ static int _ffs_func_bind(struct usb_configuration *c, 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; diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index b6d4b484c51aeefb154a653897391fecdca19ae6..5815120c04021695d2453dece631bdbd43c51885 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -284,6 +284,7 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, 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 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, 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 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, } 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 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, 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 @@ static void hidg_disable(struct usb_function *f) 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 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 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 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 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 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 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 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) 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 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) 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 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) 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 1979156e4cfd4c1cb948bd19a65a8b48edd67ca8..4b4ce0e0dac1a2f946e29175ef8d8f4343202ee6 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -413,7 +413,8 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 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_qdss.c b/drivers/usb/gadget/function/f_qdss.c index d1c37410184d2894716bfaec0d70bd8d8575830d..0ce2c4078248ab8ebe86ff947dc902f868522ced 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 @@ static struct usb_ss_ep_comp_descriptor qdss_ctrl_out_ep_comp_desc = { .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 @@ static struct usb_descriptor_header *qdss_ss_desc[] = { 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 @@ static void clear_desc(struct usb_gadget *gadget, struct usb_function *f) 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 @@ static int qdss_bind(struct usb_configuration *c, struct usb_function *f) 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 @@ static int qdss_bind(struct usb_configuration *c, struct usb_function *f) 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 @@ static int qdss_set_alt(struct usb_function *f, unsigned int intf, 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 @@ static struct usb_function *qdss_alloc(struct usb_function_instance *fi) 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; diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 6153c54e9fc4ed2eb24d0595c299e72023ceeae0..814b4a3e4a5e4c6febe3e6ab3c9305a52f99b45a 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -766,18 +766,31 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) * 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; @@ -894,7 +907,9 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) 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; @@ -957,8 +972,6 @@ static void rndis_free_inst(struct usb_function_instance *f) 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 +993,6 @@ static struct usb_function_instance *rndis_alloc_inst(void) 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; @@ -1025,6 +1032,8 @@ static void rndis_free(struct usb_function *f) 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 +1041,8 @@ static void rndis_unbind(struct usb_configuration *c, struct usb_function *f) 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 +1062,9 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) 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/u_f.h b/drivers/usb/gadget/u_f.h index 7d53a4773d1af5a303e292234239769b97878d2f..2f03334c68741fc1bedcb4590fc61ff2d456db62 100644 --- a/drivers/usb/gadget/u_f.h +++ b/drivers/usb/gadget/u_f.h @@ -64,7 +64,9 @@ struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len); /* 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 ccb9c213cc9f7e5b1ba958b9451df218fe3334bc..e9bd8d4abca00df87aa35672728870d15ba3ba74 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -475,7 +475,7 @@ static int bdc_probe(struct platform_device *pdev) 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 02968842b359eb140497a94f2fe4e34167326b6a..708e36f530d8dd0b8de1bccee945a919683df52f 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_pci.c +++ b/drivers/usb/gadget/udc/bdc/bdc_pci.c @@ -82,6 +82,7 @@ static int bdc_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) 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 69590717e89d06c33cf0c7cb880fcd95eea9322b..0c71938d837a58d2147278c0d84d8f04234a529b 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -139,10 +139,8 @@ int usb_ep_disable(struct usb_ep *ep) goto out; ret = ep->ops->disable(ep); - if (ret) { - ret = ret; + if (ret) goto out; - } ep->enabled = false; @@ -250,6 +248,9 @@ EXPORT_SYMBOL_GPL(usb_ep_free_request); * 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 b62a3de6507509f77146491a91a9e641ec02950f..ff4d6cac7ac0cde492a2a4355b6005ed9affb04b 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -2103,16 +2103,13 @@ static int dummy_hub_control( } 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 @@ static int dummy_hub_control( 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/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 142fe5139ca327859fd7bdb96a24467b916d7b68..e79ac2a45e59d4fcd2a3bbb74b5004b08df56849 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -377,6 +377,9 @@ static void ehci_shutdown(struct usb_hcd *hcd) 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 5635a33ebadf7b7a1301bd1dcfa8d652e95fbea9..2c39571520de1c7a5f845e3da8955b422c31d6c8 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -73,6 +73,7 @@ static const char hcd_name [] = "ohci_hcd"; #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 @@ static int ohci_urb_enqueue ( } /* 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); @@ -501,6 +502,7 @@ static int ohci_init (struct ohci_hcd *ohci) 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 +732,7 @@ static void io_watchdog_func(unsigned long _ohci) 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 +837,7 @@ static void io_watchdog_func(unsigned long _ohci) } } 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 +847,7 @@ static void io_watchdog_func(unsigned long _ohci) } done: + ohci->prev_frame_no = prev_frame_no; spin_unlock_irqrestore(&ohci->lock, flags); } @@ -973,6 +976,7 @@ static void ohci_stop (struct usb_hcd *hcd) 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 ed678c17c4eaadd4f4ceee99a6161da3e00b3d76..798b2d25dda9b21380250ed574e9dd6774d8f662 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -310,8 +310,10 @@ static int ohci_bus_suspend (struct usb_hcd *hcd) 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 4365dc36be8d93d00fe65977b04e0cb889cf6ecd..48200a89f7aa91047d8485c8d41892cb1c21150d 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -1018,6 +1018,8 @@ static void finish_unlinks(struct ohci_hcd *ohci) * 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 @@ static void finish_unlinks(struct ohci_hcd *ohci) 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-plat.c b/drivers/usb/host/xhci-plat.c index a1dedf0ffae9f0bfada8e8231395f05b2e3013dc..1ce079d134f2151dddae220a464d57eb05d197db 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -489,7 +489,6 @@ MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match); 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/misc/ldusb.c b/drivers/usb/misc/ldusb.c index 9ca595632f171c2ac705c3c465769afe80c1e6f7..c5e3032a4d6b4c53b86ce3eff4d51f8f98b9c236 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 @@ static const struct usb_device_id ld_usb_table[] = { { 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 934e10fc1536a205b4212fe9ca8114e6db26068e..baa57bc2919224f254728423c6c4e7862a440237 100644 --- a/drivers/usb/misc/lvstest.c +++ b/drivers/usb/misc/lvstest.c @@ -481,6 +481,7 @@ static void lvs_rh_disconnect(struct usb_interface *intf) 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 57e038d3cf067720fbdb35a250eef8172c520dc4..794c55367324012a67f97cf45e74df02d021ec3d 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -83,6 +83,8 @@ struct mon_reader_text { 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 @@ static int mon_text_open(struct inode *inode, struct file *file) 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_copy_to_user(struct mon_reader_text *rp, + char __user * const buf, const size_t nbytes) +{ + const size_t togo = min(nbytes, rp->printf_togo); + + if (copy_to_user(buf, &rp->printf_buf[rp->printf_offset], togo)) + return -EFAULT; + rp->printf_togo -= togo; + rp->printf_offset += togo; + return togo; +} + +/* 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) + 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_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; + + 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); + 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); - 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) + 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') { - 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); + 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); } - 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; } static struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp, diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 844a309fe895f04fc3ffa2c0a4748b738906c9bc..e85b9c2a4910808405544e6df27a25fb52bd4d82 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -114,15 +114,19 @@ static int service_tx_status_request( } is_in = epnum & USB_DIR_IN; - if (is_in) { - epnum &= 0x0f; + epnum &= 0x0f; + if (epnum >= MUSB_C_NUM_EPS) { + handled = -EINVAL; + break; + } + + if (is_in) ep = &musb->endpoints[epnum].ep_in; - } else { + 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 55c624f2a8c029cd919dd8af7504b1eec6113a47..43033895e8f632cf1d737270067e6f571570b65a 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -418,13 +418,7 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb, } } - /* - * 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); diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 6111d399aecceb5b0303d7b23bebccda64327452..d21823bac262b91bfab3191d2a0dfc5325a8e088 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -942,7 +942,7 @@ static struct rx_msg *pd_ext_msg_received(struct usbpd *pd, u16 header, u8 *buf, /* 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); } diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 6c6a3a8df07a84e490f184a4b29662173de4451b..968ade5a35f5d031dac33b7d4d29fe98f544ac3f 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -1001,6 +1001,10 @@ static int usbhsf_dma_prepare_pop_with_usb_dmac(struct usbhs_pkt *pkt, 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 @@ static int usbhsf_dma_pop_done_with_usb_dmac(struct usbhs_pkt *pkt, 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/cp210x.c b/drivers/usb/serial/cp210x.c index 3178d8afb3e6798533e8bb6260b98ebfe1c826fe..cab80acace4e3c181a10eee78531e0609b16b8ce 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -152,6 +152,7 @@ static const struct usb_device_id id_table[] = { { 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 */ diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 0c743e4cca1eedee378c4f824e24e692eb5d6c2f..71cbc6890ac46eb1da4395ce1f5b55e84611cf67 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -773,6 +773,7 @@ static const struct usb_device_id id_table_combined[] = { .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 @@ static const struct usb_device_id id_table_combined[] = { { 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 }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 543d2801632b4907daab18b8aed682c338b729f3..76a10b222ff9222f8b41c245ed651b427b9b0128 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 */ @@ -1440,6 +1443,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/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index 4340b4925daa78d8d148956ffaccb65e0eec0e9e..4d6eb48b2c45de764091fdab628ea8e89be816fb 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -1942,6 +1942,8 @@ static int ene_load_bincode(struct us_data *us, unsigned char flag) 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 ms_scsi_irp(struct us_data *us, struct scsi_cmnd *srb) 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 6891e909277573603dd993ecf13bc71cd301a859..a96dcc660d0f07c49cb58e297ef9d999d9caa3ae 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -1076,7 +1076,7 @@ static int uas_post_reset(struct usb_interface *intf) 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 b605115eb47a600c105f77248fb9d5c713335d07..ca3a5d430ae1c45cf344129e254fe91ccb60336c 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -2137,6 +2137,13 @@ UNUSUAL_DEV( 0x152d, 0x2566, 0x0114, 0x0114, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_BROKEN_FUA ), +/* Reported by Teijo Kinnunen */ +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 */ UNUSUAL_DEV(0x152d, 0x9561, 0x0000, 0x9999, "JMicron", diff --git a/drivers/usb/usbip/vudc_sysfs.c b/drivers/usb/usbip/vudc_sysfs.c index 0f98f2c7475f5d95a0eff25b3bf8e5db5408f4cf..7efa374a49703f51911ff02afe736616dae7cc66 100644 --- a/drivers/usb/usbip/vudc_sysfs.c +++ b/drivers/usb/usbip/vudc_sysfs.c @@ -117,10 +117,14 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr, 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 9f1ec43922094927b979682f6dca01405f52f253..7b8a957b008d23cc9addec2380e36fd967bae5b2 100644 --- a/drivers/vfio/pci/vfio_pci_config.c +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -810,6 +810,7 @@ static int vfio_exp_config_write(struct vfio_pci_device *vdev, int pos, { __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 @@ static int vfio_exp_config_write(struct vfio_pci_device *vdev, int pos, 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 @@ static int __init init_pci_cap_exp_perm(struct perm_bits *perm) * 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 59b3f62a2d64ebd85be990630e09a48c7c7f2f1c..70c748a5fbcc29fe1a285e0416e86852a2e5bc90 100644 --- a/drivers/vfio/vfio_iommu_spapr_tce.c +++ b/drivers/vfio/vfio_iommu_spapr_tce.c @@ -195,6 +195,11 @@ static long tce_iommu_register_pages(struct tce_container *container, 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 @@ static int tce_iommu_attach_group(void *iommu_data, 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 e5b7652234fcf7efa89e9595383470df9da28166..487586e2d8b9007b09f53f3e6e370791088ab993 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -524,7 +524,7 @@ static int vhost_net_rx_peek_head_len(struct vhost_net *net, struct sock *sk) 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 @@ static void handle_rx(struct vhost_net *net) 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 cd38f5add254046c107e81afea15f519b94c6e25..fce49ebc575dd143eb69129c314f8458ad3eca3b 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -211,8 +211,7 @@ int vhost_poll_start(struct vhost_poll *poll, struct file *file) 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 @@ static int vq_log_access_ok(struct vhost_virtqueue *vq, /* 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 16c8fcf0a1afa019a2121ea85245b8a0441edd23..8c835f16af8227fe6c7c3119dff2ecc9f56fa486 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -134,7 +134,7 @@ static ssize_t bl_power_store(struct device *dev, struct device_attribute *attr, { 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 @@ static ssize_t bl_power_store(struct device *dev, struct device_attribute *attr, 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 @@ int backlight_device_set_brightness(struct backlight_device *bd, 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 d7c239ea3d09f5f47aefca8cd4d9157608f250f3..f5574060f9c82dcb1802059c8bd229aa5f3f122c 100644 --- a/drivers/video/backlight/corgi_lcd.c +++ b/drivers/video/backlight/corgi_lcd.c @@ -177,7 +177,7 @@ static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int adrs, uint8_t data) 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 eab1f842f9c01c1fa74d944e72ec7c6547da12cc..e4bd63e9db6bda265fe9fea5a7f3073bc5661478 100644 --- a/drivers/video/backlight/tdo24m.c +++ b/drivers/video/backlight/tdo24m.c @@ -369,7 +369,7 @@ static int tdo24m_probe(struct spi_device *spi) 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 6a41ea92737a30590ac1bb5d9bd7e93f683bb4d8..4dc5ee8debeba2fee5f02807f0b8b454f7363ffd 100644 --- a/drivers/video/backlight/tosa_lcd.c +++ b/drivers/video/backlight/tosa_lcd.c @@ -49,7 +49,7 @@ static int tosa_tg_send(struct spi_device *spi, int adrs, uint8_t data) 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 b90ef96e43d65a3ea18f74fdc6c9c9d702214595..25d5bc3dcdb5eb15c71e92a7eff5c90491047efa 100644 --- a/drivers/video/console/dummycon.c +++ b/drivers/video/console/dummycon.c @@ -41,12 +41,55 @@ static void dummycon_init(struct vc_data *vc, int init) 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; +} + +static int dummycon_switch(struct vc_data *vc) { return 0; } -#define DUMMY (void *)dummycon_dummy +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 @@ const struct consw dummy_con = { .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 11576611a974bae39a356f21ae7ed3b5d5e0a613..dda1c4b3a2294dfbe8d26284c3173ae5eeac1dca 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -405,7 +405,10 @@ static const char *vgacon_startup(void) 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 @@ static const char *vgacon_startup(void) &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 @@ static const char *vgacon_startup(void) 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 @@ static const char *vgacon_startup(void) } } 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 ec2671d98abcccb5f3afa6bfd68158a14557ce6e..89880b70cc2880a57b07108e93904da22d540040 100644 --- a/drivers/video/fbdev/amba-clcd.c +++ b/drivers/video/fbdev/amba-clcd.c @@ -892,8 +892,8 @@ static int clcdfb_of_dma_setup(struct clcd_fb *fb) 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/dsi_host_v2.c b/drivers/video/fbdev/msm/dsi_host_v2.c index 33775eca7c71e2471859e8a631bc8ccff8699b3d..2cca4691b93fa769a83719ed2791be5c7fb25aac 100644 --- a/drivers/video/fbdev/msm/dsi_host_v2.c +++ b/drivers/video/fbdev/msm/dsi_host_v2.c @@ -753,8 +753,8 @@ static int msm_dsi_cmds_tx(struct mdss_dsi_ctrl_pdata *ctrl, } 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_v2.c b/drivers/video/fbdev/msm/dsi_v2.c index 74c072602b1003f2e3ed45d57b6ed5e785194ca6..11c7144c605b3d7cf4fff2614de6cf2e9679de82 100644 --- a/drivers/video/fbdev/msm/dsi_v2.c +++ b/drivers/video/fbdev/msm/dsi_v2.c @@ -233,6 +233,10 @@ static int dsi_parse_gpio(struct platform_device *pdev, 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 591c240dc6ff873b82ff24b10df7612b313bd543..c9db88e07706b2bb9de18fb5032ea7256ac4cd6b 100644 --- a/drivers/video/fbdev/msm/mdp3.c +++ b/drivers/video/fbdev/msm/mdp3.c @@ -2111,9 +2111,41 @@ static ssize_t mdp3_show_smart_blit(struct device *dev, 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 +2410,7 @@ static int mdp3_probe(struct platform_device *pdev) 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; diff --git a/drivers/video/fbdev/msm/mdp3.h b/drivers/video/fbdev/msm/mdp3.h index 6fb39a73649dd5bc011829beda2bda836017fb89..7132dee09e15ad8b96e1055ae096a21e28dbd823 100644 --- a/drivers/video/fbdev/msm/mdp3.h +++ b/drivers/video/fbdev/msm/mdp3.h @@ -208,7 +208,7 @@ struct mdp3_hw_resource { bool solid_fill_vote_en; struct list_head reg_bus_clist; struct mutex reg_bus_lock; - + bool twm_en; u32 max_bw; u8 ppp_formats[BITS_TO_BYTES(MDP_IMGTYPE_LIMIT1)]; diff --git a/drivers/video/fbdev/msm/mdp3_ctrl.c b/drivers/video/fbdev/msm/mdp3_ctrl.c index 589f2df1842fddeff09e083d3aa85ba479512886..2d132102148a40090545b0f620d5ec310bdd5330 100644 --- a/drivers/video/fbdev/msm/mdp3_ctrl.c +++ b/drivers/video/fbdev/msm/mdp3_ctrl.c @@ -983,6 +983,11 @@ static int mdp3_ctrl_on(struct msm_fb_data_type *mfd) 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; @@ -1025,8 +1030,10 @@ static int mdp3_ctrl_off(struct msm_fb_data_type *mfd) 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 +1041,13 @@ static int mdp3_ctrl_off(struct msm_fb_data_type *mfd) * 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 +1073,27 @@ static int mdp3_ctrl_off(struct msm_fb_data_type *mfd) 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); @@ -2870,6 +2896,7 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd) 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) diff --git a/drivers/video/fbdev/msm/mdp3_ppp.c b/drivers/video/fbdev/msm/mdp3_ppp.c index 612cccc961f9d0d9d21bfd287b9dc8b09b735b8c..6095073cd1a2efb13f9741f3d3e7ee677547fbc7 100644 --- a/drivers/video/fbdev/msm/mdp3_ppp.c +++ b/drivers/video/fbdev/msm/mdp3_ppp.c @@ -22,6 +22,7 @@ #include #include "linux/proc_fs.h" #include +#include #include "mdss_fb.h" #include "mdp3_ppp.h" @@ -195,12 +196,20 @@ int mdp3_ppp_verify_res(struct mdp_blit_req *req) 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; } @@ -1645,6 +1654,7 @@ int mdp3_ppp_parse_req(void __user *p, } } 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/mdss.h b/drivers/video/fbdev/msm/mdss.h index 12a317149aae44a0f7d5ae8a2cf200a16dfa4b2c..e6d55b5ec7a00a9eb89c713244e149c33a10ad20 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -42,6 +42,7 @@ enum mdss_mdp_clk_type { 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 f38d40c8aa1817a4765be59435c04fe0e5f4b00d..7d07040d515cb2e484e641e1f508387cbfecf28c 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 @@ static inline struct mdss_mdp_misr_map *mdss_misr_get_map(u32 block_id, } } 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 @@ static inline struct mdss_mdp_misr_map *mdss_misr_get_map(u32 block_id, /* * 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_misr_crc_collect(struct mdss_data_type *mdata, int block_id, } +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 0d482c033d0fee15ae6627b691772c2750fe1561..64df339b44e7ffe98e6010244efede059dbaf077 100644 --- a/drivers/video/fbdev/msm/mdss_debug.h +++ b/drivers/video/fbdev/msm/mdss_debug.h @@ -78,8 +78,8 @@ struct vbif_debug_bus { #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 @@ u32 get_dump_range(struct dump_offset *range_node, size_t max_offset); 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 a651b5586e906b8f6e2a3e77c9a3ca8cf7c43f82..7bf4f1100e2ca1fc591d405d4078194b3fe6e904 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 @@ struct mdss_dbg_xlog { 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 @@ struct mdss_debug_base *get_dump_blk_addr(const char *blk_name) 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 @@ static void mdss_xlog_dump_array(struct mdss_debug_base *blk_arr[], &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 @@ static void xlog_debug_work(struct work_struct *work) 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 @@ void mdss_xlog_tout_handler_default(bool queue, const char *name, ...) 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 @@ void mdss_xlog_tout_handler_default(bool queue, const char *name, ...) 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 @@ void mdss_xlog_tout_handler_default(bool queue, const char *name, ...) 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 @@ int mdss_create_xlog_debug(struct mdss_debug_data *mdd) 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 cad1387a8d73af5517b0070c32025124c8e9a665..d3e52698d3722f860cc765f7b35cab12c1525d31 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "mdss.h" #include "mdss_panel.h" @@ -46,6 +47,97 @@ static struct mdss_dsi_data *mdss_dsi_res; 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; @@ -1704,7 +1796,8 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata, int power_state) 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"); @@ -3340,6 +3433,8 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev) else ctrl_pdata->shared_data->dsi1_active = true; + mdss_dsi_debug_bus_init(mdss_dsi_res); + return 0; error_shadow_clk_deinit: @@ -4069,6 +4164,7 @@ static int mdss_dsi_parse_gpio_params(struct platform_device *ctrl_pdev, 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 +4180,10 @@ static int mdss_dsi_parse_gpio_params(struct platform_device *ctrl_pdev, 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->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 058c27a6113064e55e80850eb6cdab6896eb078a..5e921ff515cef46e5c0b1fd1c90216ee51ad283c 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 @@ struct mdss_dsi_data { * mutex, clocks, regulator information, setup information */ struct dsi_shared_data *shared_data; + u32 *dbg_bus; + int dbg_bus_size; }; /* @@ -407,7 +409,7 @@ struct mdss_dsi_ctrl_pdata { 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; @@ -435,7 +437,10 @@ struct mdss_dsi_ctrl_pdata { int bklt_en_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; @@ -681,6 +686,8 @@ void mdss_dsi_set_reg(struct mdss_dsi_ctrl_pdata *ctrl, int off, 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_debug_bus_init(struct mdss_dsi_data *sdata); + static inline const char *__mdss_dsi_pm_name(enum dsi_pm_type module) { switch (module) { diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c index d76be70b570486233280574641d677ffd4821e58..f425620efefaa914e20cccea69b2166641a99e60 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_host.c +++ b/drivers/video/fbdev/msm/mdss_dsi_host.c @@ -161,7 +161,14 @@ void mdss_dsi_clk_req(struct mdss_dsi_ctrl_pdata *ctrl, 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); @@ -1134,6 +1141,11 @@ static int mdss_dsi_read_status(struct mdss_dsi_ctrl_pdata *ctrl) 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 +1162,8 @@ static int mdss_dsi_read_status(struct mdss_dsi_ctrl_pdata *ctrl) 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 +1251,15 @@ void mdss_dsi_dsc_config(struct mdss_dsi_ctrl_pdata *ctrl, struct dsc_desc *dsc) { 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 +1448,7 @@ static void mdss_dsi_mode_setup(struct mdss_panel_data *pdata) 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,6 +1505,8 @@ int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata) /* 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 */ @@ -1684,8 +1708,8 @@ static int mdss_dsi_cmds2buf_tx(struct mdss_dsi_ctrl_pdata *ctrl, __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 +2127,7 @@ static int mdss_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl, 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 */ @@ -2181,6 +2206,7 @@ static int mdss_dsi_cmd_dma_rx(struct mdss_dsi_ctrl_pdata *ctrl, 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 +2267,11 @@ static int mdss_dsi_cmd_dma_rx(struct mdss_dsi_ctrl_pdata *ctrl, 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 +2999,10 @@ bool mdss_dsi_ack_err_status(struct mdss_dsi_ctrl_pdata *ctrl) * 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); @@ -3122,7 +3155,7 @@ static void __dsi_error_counter(struct dsi_err_container *err_container) 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 +3233,10 @@ irqreturn_t mdss_dsi_isr(int irq, void *ptr) * 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 +3245,7 @@ irqreturn_t mdss_dsi_isr(int irq, void *ptr) } 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 2ef1695b15f13a7463a8248744f3329d1c40f1ee..e586667a932c89de439c8d7f88bf18adc4f1152c 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -27,8 +27,9 @@ #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 +239,11 @@ static void mdss_dsi_panel_bklt_dcs(struct mdss_dsi_ctrl_pdata *ctrl, int level) 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); } @@ -421,6 +427,8 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) __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 +445,19 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) 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 +489,17 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) } } 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 +1590,9 @@ static int mdss_dsi_parse_topology_config(struct device_node *np, 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 +1704,7 @@ static int mdss_dsi_parse_reset_seq(struct device_node *np, 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 +1713,15 @@ static bool mdss_dsi_cmp_panel_reg_v2(struct mdss_dsi_ctrl_pdata *ctrl) 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 +1853,12 @@ static void mdss_dsi_parse_dms_config(struct device_node *np, 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 +2178,10 @@ static int mdss_dsi_set_refresh_rate_range(struct device_node *pan_node, __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 +2303,13 @@ int mdss_panel_parse_bl_settings(struct device_node *np, } } 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_status.c b/drivers/video/fbdev/msm/mdss_dsi_status.c index 992d687edc191ca7d06c16d9be0cf6f3b994e6d9..c5af962141140a9eace83847d290a8fdcb4e99c7 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_status.c +++ b/drivers/video/fbdev/msm/mdss_dsi_status.c @@ -133,14 +133,16 @@ static int fb_event_callback(struct notifier_block *self, 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; @@ -189,7 +191,8 @@ static int fb_event_callback(struct notifier_block *self, 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 +207,7 @@ static int param_dsi_status_disable(const char *val, struct kernel_param *kp) 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 27299d1075e8c3ad63668e2e7d029f27452d6834..72f651e83e4b3e6dfffc4b9ac1fb296bd141943c 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -1161,11 +1161,24 @@ static int mdss_fb_init_panel_modes(struct msm_fb_data_type *mfd, 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 @@ -1653,6 +1666,7 @@ void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl) 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 +1701,17 @@ void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl) 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 +4752,9 @@ static int mdss_fb_mode_switch(struct msm_fb_data_type *mfd, u32 mode) 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 c85f033cff85b6fdd564f098e435ffe97de9b4ff..5f2baef3f058dc62845fca5768e6c93d703d7d98 100644 --- a/drivers/video/fbdev/msm/mdss_fb.h +++ b/drivers/video/fbdev/msm/mdss_fb.h @@ -236,6 +236,7 @@ struct msm_mdp_interface { 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 311779347e086c0325581d61711423eac35674a3..2f70ad37e2271b70711ce692e19bdb8076a430bb 100644 --- a/drivers/video/fbdev/msm/mdss_io_util.c +++ b/drivers/video/fbdev/msm/mdss_io_util.c @@ -275,8 +275,8 @@ int msm_mdss_enable_vreg(struct mdss_vreg *in_vreg, int num_vreg, int enable) } 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 @@ int msm_mdss_enable_vreg(struct mdss_vreg *in_vreg, int num_vreg, int enable) } 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 @@ int msm_mdss_enable_vreg(struct mdss_vreg *in_vreg, int num_vreg, int enable) } 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 @@ int msm_mdss_enable_vreg(struct mdss_vreg *in_vreg, int num_vreg, int enable) 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 @@ int msm_mdss_enable_vreg(struct mdss_vreg *in_vreg, int num_vreg, int enable) 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 d07650ba3b35f1b3a71f83aea09435e2ac434c2f..a6d43a6cd57b2971f6461ee4c8ff6e322ee9e0ac 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -1270,6 +1270,7 @@ static inline void __mdss_mdp_reg_access_clk_enable( 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 @@ static inline void __mdss_mdp_reg_access_clk_enable( 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); } @@ -1453,6 +1455,35 @@ static int mdss_mdp_idle_pc_restore(void) return rc; } +/** + * 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 @@ void mdss_mdp_clk_ctrl(int enable) 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 @@ static int mdss_mdp_irq_clk_setup(struct mdss_data_type *mdata) /* 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 @@ static int mdss_mdp_probe(struct platform_device *pdev) 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 344d6efc2f370dd14264b6d3fb42cdc6ba49e1be..9de02d9976518dd8f79200c41712c4ea4d2a13df 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -571,11 +571,12 @@ static u32 __calc_qseed3_mdp_clk_rate(struct mdss_mdp_pipe *pipe, 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 @@ static u32 __calc_qseed3_mdp_clk_rate(struct mdss_mdp_pipe *pipe, 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 @@ void mdss_mdp_check_ctl_reset_status(struct mdss_mdp_ctl *ctl) 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_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c index 4442f194d486364b545ad9da11bcd47e4c3d2bd6..b49e9545acadfdbdb2ad2e84a0a6c7d1493d1429 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 @@ int mdss_mdp_resource_control(struct mdss_mdp_ctl *ctl, u32 sw_event) } /* 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) { @@ -1973,12 +1975,14 @@ static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg) 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 7b69aa398425e62d935f58ea3ea0c788844d8248..f18987ce4da773a46d7e8c47bf88838575424cc2 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -1252,7 +1252,7 @@ static int mdss_mdp_video_wait4vsync(struct mdss_mdp_ctl *ctl) 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 80708aa627f4c8916f1dbfc696d345cad01f3454..672b634c7155811828b46e41d0e38db483b3daeb 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -6603,6 +6603,7 @@ int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) 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_trace.h b/drivers/video/fbdev/msm/mdss_mdp_trace.h index f8a6baf79dd7d82dee9215dfccbcb791a5c85945..c100e9cc8c18e39e87feee70003074d72aa015dd 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_trace.h +++ b/drivers/video/fbdev/msm/mdss_mdp_trace.h @@ -376,7 +376,7 @@ TRACE_EVENT(mdp_cmd_wait_pingpong, __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 29f6c1ad80d1aaf08328ffe770e3298e400be794..c0301097517f188098557d13bc14d960f2c58c29 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 @@ static int mdss_mdp_get_ubwc_plane_size(struct mdss_mdp_format_params *fmt, } /* 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 a3f9349eb7224265ebdc512e15de1977f73bb9d6..c9e7e619679c22250db5bc85795676934b8fb37c 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -821,6 +821,7 @@ struct mdss_panel_data { struct mdss_panel_data *next; int panel_te_gpio; + int panel_en_gpio; struct completion te_done; }; diff --git a/drivers/video/fbdev/msm/mdss_qpic.c b/drivers/video/fbdev/msm/mdss_qpic.c index aa8d783f972ebca059ba8121c904216e66823357..2f170cce32f1ba2eab277533eb3a4d63c6b43778 100644 --- a/drivers/video/fbdev/msm/mdss_qpic.c +++ b/drivers/video/fbdev/msm/mdss_qpic.c @@ -231,6 +231,7 @@ int mdss_qpic_overlay_init(struct msm_fb_data_type *mfd) 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 f51a1b8d9e3cc413cf60025339a6a63e231de856..4952e7ec13b91fcf885834c6b58c16c0ee0e31fd 100644 --- a/drivers/video/fbdev/msm/mdss_rotator.c +++ b/drivers/video/fbdev/msm/mdss_rotator.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "mdss_rotator_internal.h" #include "mdss_mdp.h" @@ -374,6 +375,24 @@ static bool mdss_rotator_is_work_pending(struct mdss_rot_mgr *mgr, 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 @@ static int mdss_rotator_create_fence(struct mdss_rot_entry *entry) 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 @@ static int mdss_rotator_handle_request(struct mdss_rot_mgr *mgr, 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 @@ static int mdss_rotator_handle_request32(struct mdss_rot_mgr *mgr, 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 2d8b5d5ea55ae6750183b3852ae396e754127510..972c8dec933684665c0e4e1f7318c73f5248441a 100644 --- a/drivers/video/fbdev/msm/mdss_smmu.c +++ b/drivers/video/fbdev/msm/mdss_smmu.c @@ -693,13 +693,13 @@ int mdss_smmu_init(struct mdss_data_type *mdata, struct device *dev) } 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_128K, (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_128K, (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_128K, (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_128K, (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/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c index f0e46f77a0ad8ca5c488af353308c78f52ccf57f..153b39f04b17ff9879392ba742261f8fe8b75f97 100644 --- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c +++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c @@ -2280,7 +2280,8 @@ int mdss_dsi_post_clkon_cb(void *priv, 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 +2312,12 @@ int mdss_dsi_post_clkon_cb(void *priv, 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 +2433,26 @@ int mdss_dsi_pre_clkon_cb(void *priv, 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 b529a8c2b652cfa327ad0435024870e4b60c7ddc..3984716096aa0f3180e2817aec620473830dfb91 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 int td028ttec1_panel_remove(struct spi_device *spi) } 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 @@ static struct spi_driver td028ttec1_spi_driver = { module_spi_driver(td028ttec1_spi_driver); +MODULE_ALIAS("spi:tpo,td028ttec1"); MODULE_ALIAS("spi:toppoly,td028ttec1"); MODULE_AUTHOR("H. Nikolaus Schaller "); 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 47d7f69ad9ad858a75595922f032341d319f442d..48c6500c24e1f4b05a93d4a274c4a366cb8c0d6f 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dss.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dss.c @@ -941,11 +941,13 @@ static int dss_init_features(struct platform_device *pdev) 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 @@ static int dss_init_ports(struct platform_device *pdev) 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 d0a4e2f79a5703c3b0ed1b968fcac40f43052968..d215faacce040c0e8390fbb725cc9376805f2a08 100644 --- a/drivers/video/fbdev/sm501fb.c +++ b/drivers/video/fbdev/sm501fb.c @@ -1600,6 +1600,7 @@ static int sm501fb_start(struct sm501fb_info *info, 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 53326badfb615cd6310a8e1d8310d3e59c5cbec1..2add8def83bead37e48ba7ebbaec57c55353bec7 100644 --- a/drivers/video/fbdev/udlfb.c +++ b/drivers/video/fbdev/udlfb.c @@ -1487,15 +1487,25 @@ static struct device_attribute fb_device_attrs[] = { 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 a042329062d295fb894a947fdb57d57b3ff1dd5f..026df9ae30d8ed95e7c3c7a46a6974f56078f455 100644 --- a/drivers/video/fbdev/vfb.c +++ b/drivers/video/fbdev/vfb.c @@ -241,8 +241,23 @@ static int vfb_check_var(struct fb_var_screeninfo *var, */ 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 @@ static int vfb_probe(struct platform_device *dev) 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 1cf907ecded4eb72e191dfe727858f4ec433b08f..111a0ab6280a06ebb719f8dd13e4f57df1ba7447 100644 --- a/drivers/video/hdmi.c +++ b/drivers/video/hdmi.c @@ -321,6 +321,17 @@ int hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame) } 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 @@ ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, 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 @@ ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, 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 @@ hdmi_vendor_any_infoframe_unpack(union hdmi_vendor_any_infoframe *frame, 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 @@ hdmi_vendor_any_infoframe_unpack(union hdmi_vendor_any_infoframe *frame, 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 489bfc61cf30001626307f4e2f936d2e65cdeb73..8977f40ea4417b2f8d2a5ef9c5eb883ba8a54f7f 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -423,8 +423,6 @@ static inline int virtqueue_add(struct virtqueue *_vq, 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 3eb58cb51e5648f87f5d732e33cde8c6cd49569d..8f8909a668d786a393dc329961deb0e375169643 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -799,11 +799,12 @@ config EBC_C384_WDT 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 1b7e9169072f21b99335a97fa219d30eaa903393..e682bf046e5040b2620ebeda51b3d74e11dac60c 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 @@ MODULE_PARM_DESC(timeout, 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_param(start_withtimeout, uint, 0); 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_timeout(int timeout) 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 @@ static int watchdog_start(void) goto exit_superio; break; + case f71868: case f71869: /* GPIO14 --> WDTRST# */ superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 4); @@ -486,7 +496,7 @@ static bool watchdog_is_running(void) 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 @@ static int __init f71808e_find(int sioaddr) 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 70c7194e2810dff05ddc0535dc5d8d7579ab7e77..b0a158073abd55b4dcb961ef7d241dcecfe225f7 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -28,16 +28,7 @@ #include #include #include -#ifdef CONFIG_HPWDT_NMI_DECODING -#include -#include -#include -#include -#include -#include -#endif /* CONFIG_HPWDT_NMI_DECODING */ #include -#include #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 @@ static const struct pci_device_id hpwdt_devices[] = { }; 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 @@ static int hpwdt_time_left(void) } #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 @@ static struct miscdevice hpwdt_miscdev = { * 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,44 +316,25 @@ static int hpwdt_init_nmi_decoding(struct pci_dev *dev) dev_warn(&dev->dev, "Unable to register a die notifier (err=%d).\n", retval); - if (cru_rom_addr) - iounmap(cru_rom_addr); return retval; +#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"); - 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) -{ - return 0; +#endif } -static void hpwdt_exit_nmi_decoding(void) -{ -} -#endif /* CONFIG_HPWDT_NMI_DECODING */ - static int hpwdt_init_one(struct pci_dev *dev, const struct pci_device_id *ent) { 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. @@ -842,6 +368,7 @@ static int hpwdt_init_one(struct pci_dev *dev, 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 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" #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 32930a073a12744b6d6cf1bcc0b154effea431ab..977fe74e5abe25f20e93417161d9551237b0bf4d 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -760,6 +760,7 @@ static int watchdog_open(struct inode *inode, struct file *file) { struct watchdog_core_data *wd_data; struct watchdog_device *wdd; + bool hw_running; int err; /* Get the corresponding watchdog device */ @@ -779,7 +780,8 @@ static int watchdog_open(struct inode *inode, struct file *file) * 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 @@ static int watchdog_open(struct inode *inode, struct file *file) 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 6d1fbda0f461ca2d2304eaeb43aef35adfd77cf4..0da9943d405f8ff89efc87a0cf6df5ff43df9d6f 100644 --- a/drivers/watchdog/wdat_wdt.c +++ b/drivers/watchdog/wdat_wdt.c @@ -392,7 +392,7 @@ static int wdat_wdt_probe(struct platform_device *pdev) 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 8feab810aed9222b97bd5959b42e6f09ffaaf83d..7bd882d5f025bfb55837dc0471e3a9715c86c601 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -7,8 +7,10 @@ obj-y += xenbus/ 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 79b8ab4c666390944d814af822cfb32fb6697635..910b5d40c6e9f7e349ea2aee06a939a7ba9d2fdb 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -378,10 +378,8 @@ static int unmap_grant_pages(struct grant_map *map, int offset, int pages) } 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 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma) 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 72372970725b56d35d76a0c840bfb3a04f4e7f3c..37f97bab78abc390221fc103fab426bc4b57049b 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -123,11 +123,10 @@ static void afs_file_readpage_read_complete(struct page *page, /* * 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 @@ int afs_page_filler(void *data, struct page *page) 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 @@ static int afs_readpage(struct file *file, struct page *page) 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 dd98dcda6a3fb3666eec84f04eef1b75a71ed9df..b4165cce985dcc2d8e403612d2915cabc74c76bc 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -497,7 +497,7 @@ extern const struct file_operations afs_file_operations; 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 0fcb49ad67d400d05c3faf35f3c72e9ca4e14969..0606f033cd9b54cd5424c51c23cc211629096dfa 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -68,9 +68,9 @@ struct aio_ring { #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 kioctx { 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 @@ static int aio_ring_mremap(struct vm_area_struct *vma) 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 @@ static int kiocb_cancel(struct aio_kiocb *kiocb) 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 @@ static void free_ioctx(struct work_struct *work) 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 @@ static void free_ioctx_reqs(struct percpu_ref *ref) 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 @@ static int ioctx_add_table(struct kioctx *ctx, struct mm_struct *mm) 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 @@ static int kill_ioctx(struct mm_struct *mm, struct kioctx *ctx, } 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 @@ void exit_aio(struct mm_struct *mm) 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 @@ static struct kioctx *lookup_ioctx(unsigned long ctx_id) 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 a11f7317487773617dd585197e7482f5e22a3045..6182d693cf43f6edd6099c7fc439ecaee6698bb5 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -746,7 +746,7 @@ static int autofs4_dir_mkdir(struct inode *dir, 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 8d8370ddb6b29655f05ffe2855bd78f9937c7363..1ba49ebe67da39f6daf0a9a6f0ff63fff8554935 100644 --- a/fs/btrfs/acl.c +++ b/fs/btrfs/acl.c @@ -114,13 +114,17 @@ static int __btrfs_set_acl(struct btrfs_trans_handle *trans, 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/extent_io.c b/fs/btrfs/extent_io.c index 8ed05d95584a30c2c0cdc4feb1bd2036e93d3276..03ac3ab4b3b4a7b77c27f4034e9ee84c9f0f10ff 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -2453,7 +2453,7 @@ void end_extent_writepage(struct page *page, int err, u64 start, u64 end) 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 3286a6e47ff014ed236ef5103e7e27db330a2725..c95ff096cd244702683e594ae94ed78278fba8a3 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -2817,8 +2817,10 @@ static long btrfs_fallocate(struct file *file, int mode, } 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 d196ce4be31cb5c1453276e1f662e9dea9db7116..ffd5831ca15c0b167bcace0297b882624f100d41 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -567,8 +567,10 @@ static noinline void compress_file_range(struct inode *inode, 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 9a47b5598df764f862ac0b7e726d48959b48e208..d040afc966fe67bfbd7c399cc683f4d6cbfb57ed 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -5156,13 +5156,19 @@ static int is_extent_unchanged(struct send_ctx *sctx, 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 @@ static int is_extent_unchanged(struct send_ctx *sctx, 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 5539f0b95efa8fad0f95393d6ed33f9c10966119..52401732cddc5604c62b818af2b32656840cf4b7 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -3664,7 +3664,7 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, 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) { diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index fad7b37f910c56d1f1f6b52c076017a1c17353d6..39b917b7874cbaea0b10ca7d6f2e4105058ee16e 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -583,6 +583,7 @@ void btrfs_free_stale_device(struct btrfs_device *cur_dev) 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 @@ int btrfs_balance(struct btrfs_balance_control *bctl, 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 @@ int btrfs_balance(struct btrfs_balance_control *bctl, } } 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) { @@ -4748,10 +4755,13 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans, 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 +4801,6 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans, 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/ceph/file.c b/fs/ceph/file.c index ca3f630db90f22a0cefe4bdb7355033704b4154c..e7ddb23d9bb73611f4f4aebb7d2f149672837dd9 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -598,7 +598,8 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *i, 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 @@ static void ceph_aio_complete_req(struct ceph_osd_request *req) } } - 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 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter, 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 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter, 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 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter, 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 7b496a4e650e16eb643a1f387f201d3ef661aa04..4ed4736b5bc68e0b96d11e169a79bf5e530a8a7d 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1412,6 +1412,7 @@ struct dfs_info3_param { #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/file.c b/fs/cifs/file.c index 02e403af9518f353623cc5f9dbf56cf3add32798..49eeed25f200942cf43ba1c8c48501b8de58e18c 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -589,7 +589,7 @@ cifs_relock_file(struct cifsFileInfo *cfile) 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 7ab5be7944aa8436f35e1e6d1ac6f277a07e14af..24c19eb94fa3fd815cbda9d2aa2afc0120d8b622 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -701,6 +701,18 @@ cifs_get_file_info(struct file *filp) 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 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, 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 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, &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 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, } 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 abae6dd2c6b998816db830f40934c86ea8c33fff..cc88f4f0325ef4003bd248c36ca96865ff1b4ddd 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -980,10 +980,10 @@ struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset) 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 538d9b55699a10f1185b8df25a1f36f9a24ae75c..c3db2a882aee6f7aedc78dd36d5fd12fdb744502 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -344,13 +344,12 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, /* 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 @@ int build_ntlmssp_auth_blob(unsigned char **pbuffer, 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 94c4c19012226d97916d84057215761b83518586..44b7ccbe4b082f82f0d800555e8b890f6114cc05 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -707,15 +707,13 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data) 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 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data) 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 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, 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 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, } 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 f2d7402abe02a59c95c4ef3ee1e556f9eea638b2..93c8e4a4bbd31e1e7b30427691e3ba935a5ebbec 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -833,7 +833,7 @@ static int compat_ioctl_preallocate(struct file *file, */ #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/bio.c b/fs/crypto/bio.c index 2596c9ad6d776984ccb15281ecc6438d8645c205..d7b4c480e39ec6536c3884265f27069b61cff597 100644 --- a/fs/crypto/bio.c +++ b/fs/crypto/bio.c @@ -25,15 +25,8 @@ #include #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; @@ -45,22 +38,38 @@ static void completion_pages(struct work_struct *work) if (ret) { WARN_ON_ONCE(1); SetPageError(page); - } else { + } 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 732a786cce9deabe490410ee6dfb15c72fc8f048..0758d32ad01bc284bb7aeb1ae4ced74000cb6195 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "fscrypt_private.h" static unsigned int num_prealloc_crypto_pages = 32; @@ -44,12 +45,18 @@ static mempool_t *fscrypt_bounce_page_pool = NULL; 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 6eb434363ff2b6b2b7c61d44e6f8dd45feaf8833..b18fa323d1d9131fdb94661a078e8bf1b494470d 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -12,42 +12,46 @@ #include #include +#include #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 @@ static int fname_encrypt(struct inode *inode, 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 @@ static int fname_encrypt(struct inode *inode, return res; } - oname->len = cryptlen; return 0; } @@ -187,50 +190,52 @@ static int digest_decode(const char *src, int len, char *dst) 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; - - 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); + int padding = 4 << (inode->i_crypt_info->ci_flags & + FS_POLICY_FLAGS_PAD_MASK); + u32 encrypted_len; + + 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 + * + * Allocate a buffer that is large enough to hold any decrypted or encoded + * filename (null-terminated), for the given maximum encrypted filename length. * - * Allocates an output buffer that is sufficient for the crypto operation - * specified by the context and the direction. + * 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) { @@ -296,35 +301,6 @@ int fscrypt_fname_disk_to_usr(struct inode *inode, } 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 @@ -368,11 +344,17 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, 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 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, 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_private.h b/fs/crypto/fscrypt_private.h index 4688a645cd6311476c37a93f9ac8c7f815314f77..d36a648cb2bbe8b5e78248ddda48d53ffcfef288 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -49,6 +49,15 @@ struct fscrypt_context { #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; + /* * A pointer to this structure is stored in the file system's in-core * representation of an inode. @@ -70,9 +79,23 @@ typedef enum { #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; + + return false; +} + /* 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 +105,13 @@ extern int fscrypt_do_page_crypto(const struct inode *inode, 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 9f5fb2eb9cf7dcd19f47c96295f9a33ac3021192..bec06490fb13be7802cc9c9e830dd7dec27774ab 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -110,3 +110,161 @@ int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry) 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 c891d2ea9d2484d242c87a5d775bd1c6467c5276..aae68c0924f15a4574278adf95a2b89a56c86728 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "fscrypt_private.h" static struct crypto_shash *essiv_hash_tfm; @@ -353,19 +354,9 @@ int fscrypt_get_encryption_info(struct inode *inode) } 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 800748f10b3d7068073b82f56616584c53dfdc1b..71f87d74afe126dc86c76e99b4961881eb468459 100644 --- a/fs/dax.c +++ b/fs/dax.c @@ -785,6 +785,7 @@ int dax_writeback_mapping_range(struct address_space *mapping, 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 227a4f938d438a000b3fa16712dfae9a6ec4dfe9..885f74e8e435dbc6e3e8408946fa2bbe72728e84 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -461,9 +461,11 @@ static void dentry_lru_add(struct dentry *dentry) * 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 @@ void __d_drop(struct dentry *dentry) 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 @@ static inline struct dentry *lock_parent(struct dentry *dentry) 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 @@ EXPORT_SYMBOL(d_delete); 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 @@ static void __d_move(struct dentry *dentry, struct dentry *target, 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 @@ static void __d_move(struct dentry *dentry, struct dentry *target, __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/exofs/inode.c b/fs/exofs/inode.c index d8072bc074a44f74c09c47c2905eb0d1c7ac87dc..7f5b73528663fb77d3645ca520eabe18e9cc602b 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -377,9 +377,8 @@ static int read_exec(struct page_collect *pcol) * 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 @@ static int readpage_strip(void *data, struct page *page) 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 @@ static int _readpage(struct page *page, bool read_4_write) _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/ext4/balloc.c b/fs/ext4/balloc.c index e04ec868e37e71d3e82529eeb68f449c284b47c6..176b4b27a27a0edb71cd9f137d52bed0a2fd4b0c 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -242,8 +242,6 @@ static int ext4_init_block_bitmap(struct super_block *sb, */ 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; } @@ -447,6 +445,7 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group) 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/file.c b/fs/ext4/file.c index 510e66422f045bd92b3631671ed3b72d9b39ec7f..08fca4add1e2b1be8efd1a02ed687b996197c78d 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -429,7 +429,7 @@ static int ext4_find_unwritten_pgoff(struct inode *inode, 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 2cec6059e8864046e062c04bb5a63931c508b93b..ef76b8351dcd385b51ca0eb5ff9df95f8c377d51 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -63,44 +63,6 @@ void ext4_mark_bitmap_end(int start_bit, int end_bit, char *bitmap) 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) { @@ -184,17 +146,14 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group) 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 21c06707f175b88b08b31368b35d49fb652f1808..2bf83d0cb363d48f767189fd9628162b243b23cc 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3421,7 +3421,6 @@ static ssize_t ext4_direct_IO_write(struct kiocb *iocb, struct iov_iter *iter) { 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 +3444,7 @@ static ssize_t ext4_direct_IO_write(struct kiocb *iocb, struct iov_iter *iter) goto out; } orphan = 1; - ei->i_disksize = inode->i_size; + ext4_update_i_disksize(inode, inode->i_size); ext4_journal_stop(handle); } @@ -3573,7 +3572,7 @@ static ssize_t ext4_direct_IO_write(struct kiocb *iocb, struct iov_iter *iter) 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' @@ -4561,6 +4560,12 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) 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/mballoc.c b/fs/ext4/mballoc.c index e5e99a7f101f9f6bd9b0e1e2bd978dece58ccf87..4beca0683920f9bbd44ecd1052eecb95f09ed6f1 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -3878,7 +3878,8 @@ ext4_mb_discard_group_preallocations(struct super_block *sb, 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 @@ void ext4_discard_preallocations(struct inode *inode) 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 @@ ext4_mb_discard_lg_preallocations(struct super_block *sb, 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 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group, 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 8338cd4f19c3b7940e29450c305b8a4445445ed9..e3183e83e2a8dc4f9d120ff6fdd1236c3d7d92b1 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -3027,36 +3027,16 @@ static int ext4_symlink(struct inode *dir, 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 @@ static int ext4_symlink(struct inode *dir, 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 @@ static int ext4_symlink(struct inode *dir, } 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 @@ static int ext4_symlink(struct inode *dir, 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/readpage.c b/fs/ext4/readpage.c index 2531cc1df4bdcd45528c6ff77ae5e5dcd79d82b2..c39a12d6d2d6c5d319beb0a198d54e13b4169557 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -91,7 +91,7 @@ static void mpage_end_io(struct bio *bio) 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 43ce5fce47bfa6adc463d1d4b101734ab8a5c539..e95b6e117477e7e4111cda5d22f0e5fd24b9433f 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1028,9 +1028,7 @@ void ext4_clear_inode(struct inode *inode) 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, @@ -2272,6 +2270,8 @@ static int ext4_check_descriptors(struct super_block *sb, 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 +2284,8 @@ static int ext4_check_descriptors(struct super_block *sb, 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 +2298,8 @@ static int ext4_check_descriptors(struct super_block *sb, 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 557b3b0d668c3f12d458a194d819ecb1da407645..d4ce3af418a019986b41279b38e576b440c8768b 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c @@ -27,59 +27,28 @@ static const char *ext4_encrypted_get_link(struct dentry *dentry, 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 3eeed8f0aa06136a0b733279a0732feb5051e55b..3fadfabcac39cbd97fe7ca5df47c65b348157339 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -837,8 +837,6 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, 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 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, } 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 701781a372f3ec34f089bdb79c82628d4d6baaa1..91b2d0056470ac2aaa794902546b43065e5864d9 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -68,6 +68,7 @@ static struct page *__get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index, .old_blkaddr = index, .new_blkaddr = index, .encrypted_page = NULL, + .is_meta = is_meta, }; if (unlikely(!is_meta)) @@ -162,6 +163,7 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, .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 @@ static int f2fs_set_meta_page_dirty(struct page *page) 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 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) 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 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) 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 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) /* 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 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc) 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 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc) 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 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) } } - /* 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 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) 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); + + /* 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 node/meta pages writeback */ + /* 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); - - /* 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); + /* flush all device cache */ + err = f2fs_flush_device_cache(sbi); + if (err) + return err; - /* 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 6c13ee3a80efb9ef28dfe689ebf204d9e6eeb105..b1fd4e2f63a5da24965f1f97e24e091f4a529b5d 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -19,8 +19,6 @@ #include #include #include -#include -#include #include #include "f2fs.h" @@ -30,6 +28,11 @@ #include #include +#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 @@ static bool __is_cp_guaranteed(struct page *page) 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 @@ static void f2fs_read_end_io(struct bio *bio) } #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; - 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); + ctx->cur_step = STEP_INITIAL; + bio_post_read_processing(ctx); + return; } - bio_put(bio); + + __read_end_io(bio); } static void f2fs_write_end_io(struct bio *bio) @@ -174,15 +230,22 @@ static bool __same_bdev(struct f2fs_sb_info *sbi, */ 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 @@ static inline void __submit_bio(struct f2fs_sb_info *sbi, 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); @@ -376,12 +438,13 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio) struct page *page = fio->encrypted_page ? fio->encrypted_page : fio->page; + 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 (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { bio_put(bio); @@ -421,8 +484,8 @@ int f2fs_submit_page_write(struct f2fs_io_info *fio) } 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; @@ -444,7 +507,8 @@ int f2fs_submit_page_write(struct f2fs_io_info *fio) 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); io->fio = *fio; } @@ -472,29 +536,33 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr, unsigned nr_pages) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct fscrypt_ctx *ctx = NULL; struct bio *bio; - - if (f2fs_encrypted_file(inode)) { - ctx = fscrypt_get_ctx(inode, GFP_NOFS); - if (IS_ERR(ctx)) - return ERR_CAST(ctx); - - /* wait the page to be moved by cleaning */ - f2fs_wait_on_block_writeback(sbi, blkaddr); - } + struct bio_post_read_ctx *ctx; + unsigned int post_read_steps = 0; bio = f2fs_bio_alloc(sbi, min_t(int, nr_pages, BIO_MAX_PAGES), false); - if (!bio) { - if (ctx) - fscrypt_release_ctx(ctx); + if (!bio) 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); + 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); + } + return bio; } @@ -831,13 +899,6 @@ static int __allocate_data_block(struct dnode_of_data *dn, int seg_type) 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 +929,8 @@ int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *from) 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 +1174,31 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, 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 +1236,7 @@ static int get_data_block_dio(struct inode *inode, sector_t iblock, 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, @@ -1500,7 +1584,7 @@ static int encrypt_one_page(struct f2fs_io_info *fio) 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: @@ -1650,6 +1734,7 @@ int do_write_data_page(struct f2fs_io_info *fio) 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 +1757,7 @@ int do_write_data_page(struct f2fs_io_info *fio) goto out_writepage; set_page_writeback(page); + ClearPageError(page); /* LFS mode write path */ write_data_page(&dn, fio); @@ -2212,8 +2298,8 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, 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 +2390,19 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) { 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 +2429,24 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) current->pid, path, current->comm); } + if (rw == WRITE && whint_mode == WHINT_MODE_OFF) + iocb->ki_hint = WRITE_LIFE_NOT_SET; + + 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]); + } - 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 +2455,7 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) 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 +2512,6 @@ int f2fs_release_page(struct page *page, gfp_t wait) 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 +2535,7 @@ static int f2fs_set_data_page_dirty(struct page *page) } if (!PageDirty(page)) { - f2fs_set_page_dirty_nobuffers(page); + __set_page_dirty_nobuffers(page); update_dirty_page(inode, page); return 1; } @@ -2556,3 +2628,27 @@ const struct address_space_operations f2fs_dblock_aops = { .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 6ef3c8337f2d3849a736c6f085a1e41d529bef4b..f9a1e182bf554e4421e60285e1c451aca9eae294 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -94,14 +94,12 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page, 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 @@ ino_t f2fs_inode_by_name(struct inode *dir, const struct qstr *qstr, 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 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, 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 @@ static int make_empty_dir(struct inode *inode, 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 *init_inode_metadata(struct inode *inode, struct inode *dir, 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 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir, 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 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir, 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 @@ int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name, 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 @@ int f2fs_add_regular_entry(struct inode *dir, const struct qstr *new_name, if (inode) up_write(&F2FS_I(inode)->i_sem); - kunmap(dentry_page); f2fs_put_page(dentry_page, 1); return err; @@ -642,7 +634,6 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, 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 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, 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 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, 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 @@ bool f2fs_empty_dir(struct inode *dir) 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 @@ bool f2fs_empty_dir(struct inode *dir) 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 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) } } - 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 ff2352a0ed157c21b14a3b55d880e927827cb2a2..d5a861bf2b42ad1a931ca2de3bd26586e1843e90 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -460,7 +460,7 @@ static struct extent_node *__insert_extent_tree(struct inode *inode, 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 @@ void f2fs_drop_extent_tree(struct inode *inode) 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 a8434afa3549f45759386758559b489d24ef6da3..84be1e7db9b6714cfc57b44e53515fe1cb95bc60 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -99,9 +99,10 @@ extern char *fault_name[FAULT_MAX]; #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 block_t; /* 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 @@ struct f2fs_mount_info { #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 @@ static inline void make_dentry_ptr_block(struct inode *inode, 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 @@ enum { #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 @@ enum { #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 @@ struct f2fs_inode_info { 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 @@ struct f2fs_nm_info { 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 @@ struct f2fs_io_info { 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 @@ enum { 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_sb_info { 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 @@ struct f2fs_sb_info { 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 @@ struct f2fs_sb_info { 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 @@ struct f2fs_sb_info { /* 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 @@ struct f2fs_sb_info { __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 f2fs_has_xattr_block(unsigned int ofs) } 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 @@ static inline bool __allow_reserved_blocks(struct f2fs_sb_info *sbi, 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 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi, 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 @@ static inline void *__bitmap_ptr(struct f2fs_sb_info *sbi, int flag) 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 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi, 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 @@ static inline int f2fs_has_inline_dentry(struct inode *inode) 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 @@ static inline bool f2fs_skip_inode_update(struct inode *inode, int dsync) } 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 @@ static inline bool f2fs_skip_inode_update(struct inode *inode, int dsync) 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 @@ static inline void *kvzalloc(size_t size, gfp_t flags) 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 @@ void handle_failed_inode(struct inode *inode); /* * 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 @@ void destroy_segment_manager(struct f2fs_sb_info *sbi); 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 @@ void destroy_checkpoint_caches(void); /* * 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 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, 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_release_page(struct page *page, gfp_t wait); 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 @@ static inline void f2fs_set_encrypted_inode(struct inode *inode) #endif } -static inline bool f2fs_bio_encrypted(struct bio *bio) -{ - return bio->bi_private != NULL; -} - -static inline int f2fs_sb_has_crypto(struct super_block *sb) -{ - return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_ENCRYPT); -} - -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) +/* + * 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 F2FS_HAS_FEATURE(sb, F2FS_FEATURE_FLEXIBLE_INLINE_XATTR); + return f2fs_encrypted_file(inode); } -static inline int f2fs_sb_has_quota_ino(struct super_block *sb) -{ - return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_QUOTA_INO); +#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_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 @@ static inline bool f2fs_discard_en(struct f2fs_sb_info *sbi) { 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,11 @@ static inline bool f2fs_may_encrypt(struct inode *inode) #endif } +static inline bool f2fs_force_buffered_io(struct inode *inode, int rw) +{ + return (f2fs_post_read_required(inode) || + (rw == WRITE && test_opt(F2FS_I_SB(inode), LFS)) || + F2FS_I_SB(inode)->s_ndevs); +} + #endif diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index b926df73d75d0870ad11e577a8f026636cfc4658..0e39c7768a8812618a80bb3f334db076afa101d3 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -112,8 +112,8 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, /* 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 @@ static inline enum cp_reason_type need_do_checkpoint(struct inode *inode) 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 @@ static int f2fs_file_open(struct inode *inode, struct file *filp) if (err) return err; + + filp->f_mode |= FMODE_NOWAIT; + return dquot_file_open(inode, filp); } @@ -570,7 +574,6 @@ static int truncate_partial_data_page(struct inode *inode, u64 from, 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 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock) 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 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, } 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 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) inode_lock(inode); + down_write(&F2FS_I(inode)->dio_rwsem[WRITE]); + if (f2fs_is_volatile_file(inode)) goto err_out; @@ -1723,6 +1732,7 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) 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 @@ static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg) { 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_set_encryption_policy(struct file *filp, unsigned long arg) 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 @@ static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg) 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 @@ static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg) 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 @@ static int f2fs_ioc_gc_range(struct file *filp, unsigned long arg) 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 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) 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 3b26aa19430b86436f424a9039a930078d7addc1..604d3fcb89572ed2ad2ddbf2d9d5ab071217afdc 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -76,14 +76,15 @@ static int gc_thread_func(void *data) * 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 @@ static int select_gc_type(struct f2fs_gc_kthread *gc_th, int gc_type) { 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 @@ static void select_policy(struct f2fs_sb_info *sbi, int gc_type, } /* 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]; @@ -841,8 +850,8 @@ static void gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, 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 +899,7 @@ static void gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, 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 364114ad21477fa7dccdbd219d66a0e6edd8ecde..26832e7782336d5861ee0754821a3a96d607a9d6 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -26,7 +26,7 @@ bool f2fs_may_inline_data(struct inode *inode) 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; @@ -387,7 +387,7 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, 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 +404,6 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage, 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 89c838bfb06789a8c7ba339b1968b5e8adb0bcca..e0d9e8f27ed2b1ea3bf2f92f883d07caa7fde7a8 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -284,6 +284,10 @@ static int do_read_inode(struct inode *inode) 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 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) 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 @@ void update_inode(struct inode *inode, struct page *node_page) } __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 @@ void f2fs_evict_inode(struct inode *inode) !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 070b3a71daa8a5ac8a0546d7c6c6c568bddf43eb..392d1ed99b56035f0f17862d66f15d86efedb673 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -78,7 +78,8 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) 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 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) 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 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) 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 @@ static int is_multimedia_file(const unsigned char *s, const char *sub) /* * 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; + + down_read(&sbi->sb_lock); + + cold_count = le32_to_cpu(sbi->raw_super->extension_count); + hot_count = sbi->raw_super->hot_ext_count; - int count = le32_to_cpu(sbi->raw_super->extension_count); - for (i = 0; i < count; i++) { - if (is_multimedia_file(name, extlist[i])) { + 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 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, 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 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, 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 @@ static int __recover_dot_dentries(struct inode *dir, nid_t pino) 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 @@ static int __recover_dot_dentries(struct inode *dir, nid_t pino) } 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 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, } 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 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) 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 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, 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 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, 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 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, 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 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, 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 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) 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 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) 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 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry, 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, 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 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, } 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 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, 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 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, } 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 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, 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 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry, 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); - - 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'; + page = read_mapping_page(inode->i_mapping, 0, NULL); + if (IS_ERR(page)) + return ERR_CAST(page); - 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 7cded843cf1833c82de053b8e9ef240d8a39c3ab..ccf410af91929b8a01a621c4c9ba5d51ab5464e7 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -193,8 +193,8 @@ static void __del_from_nat_cache(struct f2fs_nm_info *nm_i, struct nat_entry *e) __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 @@ static void __set_nat_cache_dirty(struct f2fs_nm_info *nm_i, 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 @@ struct page *new_node_page(struct dnode_of_data *dn, unsigned int ofs) 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)) @@ -1751,7 +1772,7 @@ static int f2fs_set_node_page_dirty(struct page *page) 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 +2331,7 @@ int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page) 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 +2621,7 @@ static int __get_nat_bitmaps(struct f2fs_sb_info *sbi) 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 +2747,20 @@ static int init_node_manager(struct f2fs_sb_info *sbi) 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 +2851,13 @@ void destroy_node_manager(struct f2fs_sb_info *sbi) 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 081ef0d672bf5cf457a3bb1f4c20328bf24b1a12..b95e49e4a928a77c421f53175a8b8cde36c2ef6b 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -44,6 +44,7 @@ enum { 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 @@ static inline void clear_inline_node(struct page *page) 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 b6d1ec620a8cb319816b3ba77546117f9e5d662a..4ddc2262baf10591fc0b24d74e49b8d33e0f1915 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -144,7 +144,7 @@ static int recover_dentry(struct inode *inode, struct page *ipage, 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 @@ static int recover_dentry(struct inode *inode, struct page *ipage, 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 @@ static int recover_dentry(struct inode *inode, struct page *ipage, 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 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, 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 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, 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 cc34d885243871dba2cb962928e1e99f7b33b8b6..fc4ee3835f1dc983d9ab351db64ff7b778d7dd50 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1411,12 +1411,11 @@ static int issue_discard_thread(void *data) 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 @@ static int __issue_discard_async(struct f2fs_sb_info *sbi, 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 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) 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 @@ void init_discard_policy(struct discard_policy *dpolicy, } 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 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) 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 @@ static unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type) 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 @@ int rw_hint_to_seg_type(enum rw_hint hint) } } +/* 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 @@ static int __get_segment_type_6(struct f2fs_io_info *fio) 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 @@ static int __get_segment_type(struct f2fs_io_info *fio) { 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 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page, 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, @@ -2689,8 +2791,15 @@ void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio) 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 f11c4bc82c7863f7f64dfa67750c846b690078e9..3325d076972326d5f11cd7776fceb07a3fec5a14 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 @@ static inline int utilization(struct f2fs_sb_info *sbi) #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 @@ static inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno) 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 78b763c3e132c311cda2bc051f0c5de62be4cea2..7d4621a8a5e762f1e148de870433691301013cf0 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -60,7 +60,7 @@ char *fault_name[FAULT_MAX] = { 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 @@ enum { 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 @@ static match_table_t f2fs_tokens = { {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 @@ static inline void limit_reserve_root(struct f2fs_sb_info *sbi) 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 @@ static int f2fs_set_qf_name(struct super_block *sb, int qtype, 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 @@ static int f2fs_set_qf_name(struct super_block *sb, int qtype, "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 @@ static int f2fs_set_qf_name(struct super_block *sb, int qtype, "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 @@ static int f2fs_clear_qf_name(struct super_block *sb, int qtype) { 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 @@ static int f2fs_check_quota_options(struct f2fs_sb_info *sbi) "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 @@ static int f2fs_check_quota_options(struct f2fs_sb_info *sbi) 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 @@ static int parse_options(struct super_block *sb, char *options) 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 @@ static int parse_options(struct super_block *sb, char *options) 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 @@ static int parse_options(struct super_block *sb, char *options) 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 @@ static int parse_options(struct super_block *sb, char *options) 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 @@ static int parse_options(struct super_block *sb, char *options) "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 @@ static int parse_options(struct super_block *sb, char *options) "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 @@ static int parse_options(struct super_block *sb, char *options) 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 @@ static int parse_options(struct super_block *sb, char *options) 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 @@ static int parse_options(struct super_block *sb, char *options) 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 @@ static int parse_options(struct super_block *sb, char *options) "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 @@ static int parse_options(struct super_block *sb, char *options) } 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 @@ static int parse_options(struct super_block *sb, char *options) 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 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) /* 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 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) 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 @@ static void f2fs_put_super(struct super_block *sb) 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 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) 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 @@ static inline void f2fs_show_quota_options(struct seq_file *seq, #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 @@ static inline void f2fs_show_quota_options(struct seq_file *seq, 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 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) 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 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) 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 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) 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 @@ static void default_options(struct f2fs_sb_info *sbi) 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 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) 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 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) */ 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 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) 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 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) #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 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) } 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 qsize_t *f2fs_get_reserved_space(struct inode *inode) 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 @@ int f2fs_enable_quota_files(struct f2fs_sb_info *sbi, bool rdonly) } 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,11 +1908,28 @@ static int f2fs_get_context(struct inode *inode, void *ctx, size_t len) 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) ? @@ -1812,6 +1940,7 @@ 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, }; @@ -1894,7 +2023,6 @@ static int __f2fs_commit_super(struct buffer_head *bh, 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 +2309,8 @@ static void init_sb_info(struct f2fs_sb_info *sbi) 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 +2336,7 @@ static int init_blkz_info(struct f2fs_sb_info *sbi, int devi) 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 +2464,7 @@ int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) } /* 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 +2475,7 @@ int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) 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 +2547,7 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi) #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 +2581,18 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi) 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 +2640,8 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) 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 +2654,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) * 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 +2870,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) * 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 +2945,8 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) 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 +2955,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) 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 +2999,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) 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 +3096,13 @@ static int __init init_f2fs_fs(void) 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 +3125,7 @@ static int __init init_f2fs_fs(void) 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 d978c7b6ea04a6c6062ec4c55c1b74adb53274a1..f33a56d6e6dd7916feee0206bfadce16f873dc78 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -58,7 +58,7 @@ static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) #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 @@ static ssize_t features_show(struct f2fs_attr *a, 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 @@ static ssize_t features_show(struct f2fs_attr *a, 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 @@ static ssize_t f2fs_sbi_show(struct f2fs_attr *a, 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 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, 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 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, 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 @@ enum feat_id { 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 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a, 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, idle_interval, interval_time[REQ_TIME]); 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(inode_checksum, FEAT_INODE_CHECKSUM); 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 @@ static struct attribute *f2fs_attrs[] = { 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 @@ static struct attribute *f2fs_feat_attrs[] = { 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 ad2e55d3ed787f1fa5784fd2aebc5e2c9847e6d8..5af226fffbbf38c79f9985ab4c1e235e59aaf145 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -745,11 +745,12 @@ int inode_congested(struct inode *inode, int cong_bits) */ 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; } diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 658fa9e7bc10735fbdf54eb64e9c97a0350e593d..a0b0683add7c64dd826fba25570fd66376b17796 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1891,8 +1891,10 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, 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 21d829b1a8f1fbc852e69037576b3b319c89e9e1..7adf8712692a6329685b527e4b3d7aca742fbfee 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -857,9 +857,9 @@ struct fuse_fill_data { 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 f1512c81f25af6fe4faaf40d4a3f367ca4865f33..8aa98b185d7ba73c1d4e004424262ee00afd62f8 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -30,7 +30,7 @@ static struct kmem_cache *fuse_inode_cachep; 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 @@ static void sanitize_global_limit(unsigned *limit) *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 5a6f52ea272282b0c695372f11c8f8512c7ace12..c6c507c75eb1a0acc571b0f6f37d177edda2a06c 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -511,7 +511,7 @@ static int stuffed_readpage(struct gfs2_inode *ip, struct page *page) * */ -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/jbd2/journal.c b/fs/jbd2/journal.c index 6cb3a8c5b17068fa4a840b4caba4d6ab25f4fd90..d10bb2c30bf8a220d4402b45f8d590af5f708b8b 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -691,8 +691,21 @@ int jbd2_log_wait_commit(journal_t *journal, tid_t tid) { 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 @@ int __jbd2_update_log_tail(journal_t *journal, tid_t tid, unsigned long block) } /* - * 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 @@ int jbd2_journal_update_sb_log_tail(journal_t *journal, tid_t tail_tid, 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/jffs2/super.c b/fs/jffs2/super.c index 5ef21f4c4c77d12aa401eea175235cc85e0d6aa6..59c019a148f6b493f07de16c3e1567ff8bb2b9a9 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -342,7 +342,7 @@ static void jffs2_put_super (struct super_block *sb) 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 9d373247222c7d716b8dd359b0d7987edd742aa6..4d51259edee7b25932483a73ba65d06909038c73 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -132,6 +132,8 @@ lockd(void *vrqstp) { 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 @@ lockd(void *vrqstp) 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; } @@ -270,8 +274,6 @@ static void lockd_down_net(struct svc_serv *serv, struct net *net) if (ln->nlmsvc_users) { if (--ln->nlmsvc_users == 0) { nlm_shutdown_hosts_net(net); - cancel_delayed_work_sync(&ln->grace_period_end); - locks_end_grace(&ln->lockd_manager); svc_shutdown_net(serv, net); dprintk("lockd_down_net: per-net data destroyed; net=%p\n", net); } @@ -596,7 +598,7 @@ static struct ctl_table nlm_sysctl_root[] = { */ #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 a8329cc47decd627ca26cf608e6f15276697a4f6..fdf844f6d9f7284a5cc6732057aabd13ef43d18a 100644 --- a/fs/logfs/dev_bdev.c +++ b/fs/logfs/dev_bdev.c @@ -34,9 +34,9 @@ static int sync_request(struct page *page, struct block_device *bdev, int op) 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 b76a62b1978fd699bcf139ef1500720ae7e19cfa..9ec8e8f9d1c97beb5a9cb55de787f1ae4b4bbf55 100644 --- a/fs/logfs/dev_mtd.c +++ b/fs/logfs/dev_mtd.c @@ -122,9 +122,9 @@ static void logfs_mtd_sync(struct super_block *sb) 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 c87ea52de3d9d202d02dd6d66735c4d8f89724a8..8ddacc124804c9e6fdfc627f3ba96cd4657e7125 100644 --- a/fs/logfs/dir.c +++ b/fs/logfs/dir.c @@ -174,7 +174,7 @@ static struct page *logfs_get_dd_page(struct inode *dir, struct dentry *dentry) 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 @@ static int logfs_readdir(struct file *file, struct dir_context *ctx) 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 27d040e35faa9d01b57649aa498869960e270d02..1a7c0b02efbca94ac4845d63ebf43138bd5505eb 100644 --- a/fs/logfs/logfs.h +++ b/fs/logfs/logfs.h @@ -151,7 +151,7 @@ struct logfs_device_ops { 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 339f7c741ed11b4dd1054a90146107fdb296f74f..a5a05d3faa63f3e80b2bdcd46e2e52f79866213a 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -221,9 +221,10 @@ getname_kernel(const char * filename) 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 int __nd_alloc_stack(struct nameidata *nd) 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 @@ static int follow_automount(struct path *path, struct nameidata *nd, 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; diff --git a/fs/namespace.c b/fs/namespace.c index 2160bb929894e089daf4d8e0d20488f6e1c11682..4628d08c7fa51d179d918ee0278b4b7c8f1ce579 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1051,7 +1051,8 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, 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 88dbbc9fcf4d6a5eb76a95f162481c7b88190a9b..f571570a2e72cae881b32846cddb0eecac440d1c 100644 --- a/fs/ncpfs/ncplib_kernel.c +++ b/fs/ncpfs/ncplib_kernel.c @@ -980,6 +980,10 @@ ncp_read_kernel(struct ncp_server *server, const char *file_id, 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 1ac1593aded30fc978eb31e29a42d6656c6ecdf8..1ab91124a93e9f380b95dd7eea754dbd8864f619 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -86,10 +86,10 @@ struct nfs_direct_req { 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 13abd608af0f658ea7d1fb559c8e79d276a64390..4539008502ce65c09cf2a5e0e16d4e29e59827c2 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -475,6 +475,7 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh, 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 4638654e26f3f99c8b0ab205c43ee2f598ab2ef3..1b1b616a61712db6ef8674d182ea970a49b0cb7a 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3300,6 +3300,7 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f .rpc_resp = &res, }; int status; + int i; bitmask[0] = FATTR4_WORD0_SUPPORTED_ATTRS | FATTR4_WORD0_FH_EXPIRE_TYPE | @@ -3365,8 +3366,13 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f 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 @@ static int nfs41_reclaim_complete_handle_errors(struct rpc_task *task, struct nf /* 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 @@ static int nfs41_proc_reclaim_complete(struct nfs_client *clp, 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 71deeae6eefdc4de2a3160b4507f41b9adfb7c1b..0bb0e620cf427b4e162f525412c48b5d4ccc5f57 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1637,13 +1637,14 @@ static void nfs4_state_start_reclaim_reboot(struct nfs_client *clp) 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 @@ static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp) { 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 3d17fc82b9fece1fb87d8cc98b5834e53cd48489..892c88542ebde9ab41d587fdc161fd7b711d6d1b 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -1262,8 +1262,10 @@ void nfs_pageio_cond_complete(struct nfs_pageio_descriptor *desc, pgoff_t index) 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 b8e44746f761b82cc33a1003a548c340bc1c557e..0e008db16b169c998875cdbebc08385cdb0ff4d3 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1953,8 +1953,6 @@ void pnfs_error_mark_layout_for_return(struct inode *inode, 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 @@ pnfs_do_read(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr) 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 defc9233e9858c43a9438b2cf918a899b7f56afa..67b80004d3f1cf9376cdcffb2f45012afb290fbc 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -346,7 +346,7 @@ struct nfs_readdesc { }; 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 51bf1f9ab287fdf111a384bd050d5bd94b9923f9..2fdb8f5a7b69ddf12fb635f1bb8dc8e8ae7a9a26 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2613,6 +2613,8 @@ struct dentry *nfs_fs_mount_common(struct nfs_server *server, /* 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 9a3b3820306d4d6aedc94423be148a52fc908eb2..a8b786a648cdeae45ed5df62478e2056af252e1d 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1847,40 +1847,43 @@ int nfs_generic_commit_list(struct inode *inode, struct list_head *head, 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 nfs_write_inode(struct inode *inode, struct writeback_control *wbc) 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 @@ int nfs_write_inode(struct inode *inode, struct writeback_control *wbc) 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 022d95886d6685479dea0307928b275a5c291e4f..eef0caf6e67d0c07bf4fcdc1eebe10be700a6ccb 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1338,14 +1338,14 @@ nfsd4_layoutget(struct svc_rqst *rqstp, 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 f463c4e0b2ea597de61522a78d2628639adb0158..12d780718b486b0a5c5eddb7d58950fdd86a768f 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -263,6 +263,35 @@ free_blocked_lock(struct nfsd4_blocked_lock *nbl) 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 __be32 mark_client_expired_locked(struct nfs4_client *clp) 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 @@ __destroy_client(struct nfs4_client *clp) 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 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, } 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 @@ nfs4_state_destroy_net(struct net *net) } } + 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 @@ nfs4_state_shutdown_net(struct net *net) 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_state_shutdown_net(struct net *net) 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 b829cc9a9b3933f2822c21e8bb983c6584a758e5..8f0b19a3ca810993d02eb20fb160b2da017dcd59 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -94,6 +94,12 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, 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 @@ static int nfsd_lookup_parent(struct svc_rqst *rqstp, struct dentry *dparent, st /* * 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 e0e5f7c3c99fe076d11dc5d907b543d5e5376671..8a459b17918348f686b03d8cb8a6697224de7bd4 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -92,7 +92,7 @@ static bool fanotify_should_send_event(struct fsnotify_mark *inode_mark, 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 @@ static bool fanotify_should_send_event(struct fsnotify_mark *inode_mark, !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 1079fae5aa12dfb00f2d256bf2a5233ba489a7f6..47e08e4f6223e9cae6adb0bc0b062e41b5a269d1 100644 --- a/fs/ocfs2/dlmfs/dlmfs.c +++ b/fs/ocfs2/dlmfs/dlmfs.c @@ -88,13 +88,13 @@ struct workqueue_struct *user_dlm_worker; */ #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 629d8c917fa679886715360fcfe8f6cee1b5a37f..6e35ef6521b4e7857157701e3f1dbad972ff64ae 100644 --- a/fs/orangefs/super.c +++ b/fs/orangefs/super.c @@ -559,6 +559,11 @@ void orangefs_kill_sb(struct super_block *sb) /* 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 f61b00887481f7e0417b528133e614b405d8f9e9..cbca58ba008a37424afc1d3e5963aad2ff03999a 100644 --- a/fs/orangefs/waitqueue.c +++ b/fs/orangefs/waitqueue.c @@ -124,7 +124,14 @@ int service_operation(struct orangefs_kernel_op_s *op, 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 306b6c16184081f26579b25f830b1b6f5eb0c094..8546384a5fdfd36577fddac11164223a410d9f05 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -180,6 +180,9 @@ static void ovl_instantiate(struct dentry *dentry, struct inode *inode, 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 7fb53d05553780ac4bc9f3909afdb81df723af32..16f6db88c8e5c067515d8a39f186414fe49d153c 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -227,6 +227,16 @@ int ovl_xattr_get(struct dentry *dentry, const char *name, 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 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) 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 1ade1206bb896f50ad326d281c42ec56e66d8372..08dce22afec13b22751a18431a9f0352078f2758 100644 --- a/fs/proc/Kconfig +++ b/fs/proc/Kconfig @@ -81,3 +81,10 @@ config PROC_CHILDREN 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 12c6922c913c4519255c54f5cb1f92fc3f5d8901..dea53bac412eddc859cbea1682ceebcef7a9725c 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile @@ -25,6 +25,7 @@ proc-y += softirqs.o 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/base.c b/fs/proc/base.c index 1370a4e2b43a9471a1ce1c791578d19db7de46a5..abe157a57b5658723b73408082cc9a9b6f4b1717 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -87,6 +87,7 @@ #include #include #include +#include #ifdef CONFIG_HARDWALL #include #endif @@ -3166,8 +3167,8 @@ static const struct pid_entry tgid_base_stuff[] = { 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 @@ static const struct pid_entry tgid_base_stuff[] = { 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 @@ static const struct pid_entry tid_base_stuff[] = { 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 @@ static const struct pid_entry tid_base_stuff[] = { 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 6dfb41413d88b7ac8ea45d35c4d62951ccf7c34d..d8105cd8b01c1fc80f456c281ae09967302be90c 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -256,6 +256,15 @@ static inline void proc_sys_init(void) { } static inline void sysctl_head_put(struct ctl_table_header *head) { } #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 */ diff --git a/fs/proc/root.c b/fs/proc/root.c index 8d3e484055a6b3529982135289d693decc4dcb3c..c2f5014d642d22f6d4baa4cfc1ee49981047ef44 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -131,7 +131,7 @@ void __init proc_root_init(void) 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 0000000000000000000000000000000000000000..3fd7b9fe974e1bd562d1c5f83e45f3b0707dbe03 --- /dev/null +++ b/fs/proc/uid.c @@ -0,0 +1,295 @@ +/* + * /proc/uid support + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 - 1; 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 ba280596ec7825d4747e38088272aceb8637733c..2f1027d708d27d3311f94ee43c2123a086b7ecbf 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -23,9 +23,6 @@ #include #include -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 @@ unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to) 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 @@ static ssize_t do_iter_readv_writev(struct file *filp, struct iov_iter *iter, 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 @@ static ssize_t do_iter_readv_writev(struct file *filp, struct iov_iter *iter, /* 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 @@ static ssize_t do_loop_readv_writev(struct file *filp, struct iov_iter *iter, 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 @@ static ssize_t do_readv_writev(int type, struct file *file, 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 @@ static ssize_t do_readv_writev(int type, struct file *file, 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 @@ static ssize_t compat_do_readv_writev(int type, struct file *file, 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 @@ static ssize_t compat_do_readv_writev(int type, struct file *file, 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 bc2dde2423c2eb3fdb464031d18c22e4586e81cb..2a5c4813c47d33db93faf2c5fea05fb0d275722e 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -1959,7 +1959,7 @@ static int do_journal_release(struct reiserfs_transaction_handle *th, * 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 @@ static int journal_init_dev(struct super_block *super, 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 5dcf3ab838861c4d2c9c3f9bf763bfb99fb77a59..6ca00471afbf40353b76c414a1611b0bbedd10e3 100644 --- a/fs/reiserfs/reiserfs.h +++ b/fs/reiserfs/reiserfs.h @@ -2948,6 +2948,7 @@ int reiserfs_allocate_list_bitmaps(struct super_block *s, 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 e101d70d2327bc822fb44a5632697f2b2d10c90e..dec6c93044fa3bcfc3b1b740aa3a599d19592170 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -90,7 +90,9 @@ static void flush_old_commits(struct work_struct *work) 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 @@ void reiserfs_schedule_old_flush(struct super_block *s) 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_freeze(struct super_block *s) 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 @@ static int reiserfs_fill_super(struct super_block *s, void *data, int silent) 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/file.c b/fs/sdcardfs/file.c index 2879d1291a11cb21ff66f97d2799ff475c7a708f..1461254f301dcf81192dab8044fa36f93b8a6546 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -414,7 +414,7 @@ ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter) 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/packagelist.c b/fs/sdcardfs/packagelist.c index 6da0c2186d39cffb74596e9990c623709d3798ce..4b9a5635f1e0450693e6cbc1a4fee7c5df3023da 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -659,6 +659,7 @@ static struct config_item *extension_details_make_item(struct config_group *grou 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 847d82dfc7a7883c419ebb22349de499c75bc1b9..ce404a29d913112bc5a3b9f48bde7acc3de42913 100644 --- a/fs/super.c +++ b/fs/super.c @@ -519,7 +519,11 @@ struct super_block *sget_userns(struct file_system_type *type, 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 4ec051089186ea2aef6bb4f96e22fefa24a10777..03dda1cbe485a9f8eae3ed04d57cf635eac3e7dd 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1728,8 +1728,11 @@ static void ubifs_remount_ro(struct ubifs_info *c) 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 @@ static void ubifs_put_super(struct super_block *sb) 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 695389a4fc239f245cfacfa5a1e2fde9eae5b4a7..3a3be23689b352be9d19c7f34172c00645f5a83b 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 @@ static int udf_uni2char_utf8(wchar_t uni, 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_qm.c b/fs/xfs/xfs_qm.c index 1fdd3face2d90eea18b9ae19b91b3ba2584b1017..e3edaa5472169b4b81fd97181dc8c06b21b5c233 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 @@ xfs_qm_init_quotainfo( 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 @@ xfs_qm_init_quotainfo( return error; } - /* * Gets called when unmounting a filesystem or when all quotas get * turned off. @@ -722,19 +729,8 @@ xfs_qm_destroy_quotainfo( 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; @@ -1619,6 +1615,24 @@ xfs_qm_init_quotainos( return error; } +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 c93dbadfc71d34836f085d872c2b625b8bce1ad6..db7d15b6a5807cbf14b8c0bde5ae7b71dd4a95f6 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -783,6 +783,15 @@ struct acpi_iort_smmu { #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 8f66aaabadf74dc252fde96f30784c4537ed0b03..9e3f7618593f6c1aa969b452fe61be2dd9d0fbc4 100644 --- a/include/acpi/platform/acgcc.h +++ b/include/acpi/platform/acgcc.h @@ -48,7 +48,17 @@ * Use compiler specific 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 +#endif +#endif #define ACPI_INLINE __inline__ diff --git a/include/acpi/platform/acintel.h b/include/acpi/platform/acintel.h index 17bd3b7b4e5a9204e8c3392f0928534aadcad28a..bdb6858e245824536c4b7d65a81163b8cbfc36b9 100644 --- a/include/acpi/platform/acintel.h +++ b/include/acpi/platform/acintel.h @@ -48,7 +48,9 @@ * Use compiler specific is a good practice for even when * -nostdinc is specified (i.e., ACPI_USE_STANDARD_HEADERS undefined. */ +#ifndef va_arg #include +#endif /* Configuration specific to Intel 64-bit C compiler */ diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index c4f8fd2fd3842431ba5140503870e538a1c06295..f6ea0f3c03f87a718004eb239615ef167b2d9da7 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h @@ -764,6 +764,8 @@ int pud_set_huge(pud_t *pud, phys_addr_t addr, pgprot_t prot); 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 @@ static inline int pmd_clear_huge(pmd_t *pmd) { 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 3a396a9cb1768dd7c219fca5ae3ec0dc3bd8ab9f..8236dbd48fa39a5bff4514f469171a2a7868111a 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() @@ -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 b02a4403a302188054b8801f2c4da96551aa480b..133041ea9ec8a13a37d2a0d42ae0f86f3d349e30 100644 --- a/include/crypto/ice.h +++ b/include/crypto/ice.h @@ -53,16 +53,22 @@ typedef void (*ice_error_cb)(void *, u32 error); 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 982c299e435a09703de12d32b4deec08a54d44e0..642d0597bb73020ffa262f4fc8ff68e595572784 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -74,5 +74,6 @@ extern void drm_kms_helper_hotplug_event(struct drm_device *dev); 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 3c2024d8e6f5f35989d39d798a686861d3dd3433..328f232f33eef1cb5bd4c8c68c5f1fe9f8aa5bc9 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -32,6 +32,7 @@ struct mipi_dsi_device; * @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 @@ struct mipi_dsi_msg { u8 type; u16 flags; u32 ctrl; + u32 wait_ms; size_t tx_len; const void *tx_buf; diff --git a/include/dt-bindings/arm/arm-smmu.h b/include/dt-bindings/arm/arm-smmu.h index 3a1dbd333e2a33ac671ea620179ac911e52527c2..1de45a92f1e6eb9cc7035d13410a9665188c1353 100644 --- a/include/dt-bindings/arm/arm-smmu.h +++ b/include/dt-bindings/arm/arm-smmu.h @@ -23,5 +23,6 @@ #define ARM_SMMU_OPT_MMU500_ERRATA1 (1 << 7) #define ARM_SMMU_OPT_STATIC_CB (1 << 8) #define ARM_SMMU_OPT_HALT (1 << 9) +#define ARM_SMMU_OPT_HIBERNATION (1 << 10) #endif diff --git a/include/dt-bindings/clock/msm-clocks-8952.h b/include/dt-bindings/clock/msm-clocks-8952.h index 4547751bbd41dedbf514ea91b3872f29b7cb6089..3190d4f7495d1a250707b27b1e79c9073f837712 100644 --- a/include/dt-bindings/clock/msm-clocks-8952.h +++ b/include/dt-bindings/clock/msm-clocks-8952.h @@ -235,6 +235,11 @@ #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_dsi1pll_byte_clk_src 0x63930a8f +#define clk_dsi1pll_pixel_clk_src 0x0e4c9b56 + #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 2062c67e2e515845a1f2bbff6fbb6f934e8a00ca..a72db8d23ed6512f2393237e2e465fa3b81f1062 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/r8a7794-clock.h b/include/dt-bindings/clock/r8a7794-clock.h index 88e64846cf370ff4a32c8423b5d00833c8cb5a81..cdeafd9cab07c60460885297f530cf9c965aeb8c 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/kvm/arm_psci.h b/include/kvm/arm_psci.h new file mode 100644 index 0000000000000000000000000000000000000000..e518e4e3dfb50a83520a3b8100c3c27899c15bdf --- /dev/null +++ b/include/kvm/arm_psci.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * 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 . + */ + +#ifndef __KVM_ARM_PSCI_H__ +#define __KVM_ARM_PSCI_H__ + +#include +#include + +#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. + */ + if (test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features)) + return KVM_ARM_PSCI_LATEST; + + return KVM_ARM_PSCI_0_1; +} + + +int kvm_hvc_call_handler(struct kvm_vcpu *vcpu); + +#endif /* __KVM_ARM_PSCI_H__ */ diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 4c5bca38c6533ea56c5fa2f50841d3c09e31ff11..43690f501712a427c551e3248f02ae7a26ce0038 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 + /* * 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 @@ -130,5 +150,148 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1, #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 +#include + +#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 0691068ac030c23e211040cc856e7fffad930a70..002f87c0517dc44f114553d0846d54bbf1ccf088 100644 --- a/include/linux/backing-dev-defs.h +++ b/include/linux/backing-dev-defs.h @@ -195,6 +195,11 @@ static inline void set_bdi_congested(struct backing_dev_info *bdi, int sync) 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 c52a48cb9a66379971be13008bd8cdfba087807e..8272fcf78c270382aa4335711d0e3d779df9b592 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -374,7 +374,7 @@ static inline struct bdi_writeback *inode_to_wb(struct inode *inode) /** * 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 @@ static inline struct bdi_writeback *inode_to_wb(struct inode *inode) * 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 @@ unlocked_inode_to_wb_begin(struct inode *inode, bool *lockedp) * 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_begin(struct inode *inode, bool *lockedp) /** * 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 *inode_to_wb(struct inode *inode) } 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 0000000000000000000000000000000000000000..4318eb9eac5a1380f0e76b30e0c65ef50c133116 --- /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 + +int config_battery_data(struct bms_battery_data *profile); diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index 2b8b6e0d7745ca6b5620fb921f4a629d828dfff7..e4d84d327a4b286e22fc70d9ff487d780003eb49 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -25,6 +25,7 @@ typedef void (bio_end_io_t) (struct bio *); 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 diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 193c40e296fcd9c1459f417491206d427fec4bfd..cd6aaf08324d0ab9c68f79493b42c4a96c01dd9a 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -306,6 +306,9 @@ static inline void bpf_long_memcpy(void *dst, const void *src, u32 size) /* 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 @@ static inline struct bpf_prog *bpf_prog_inc(struct bpf_prog *prog) { 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/cfi.h b/include/linux/cfi.h new file mode 100644 index 0000000000000000000000000000000000000000..e27033d5dd53265066aef8ba4b3954db9c744c80 --- /dev/null +++ b/include/linux/cfi.h @@ -0,0 +1,38 @@ +#ifndef _LINUX_CFI_H +#define _LINUX_CFI_H + +#include + +#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 c5a8afdb1ef37e0a5982645fef4714d56cbd54d1..34d3f2766b4d7e67fc194aa630a76cff33688801 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -614,13 +614,13 @@ struct sock_cgroup_data { * 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/compiler-clang.h b/include/linux/compiler-clang.h index de179993e039d41d7e9034b5744be40954f81c09..e4f142c5564d2c1fc0be5dae70b51652200cea9b 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 3a8c897f67a3cc7fd9f6d8004339b38601023f52..27101bbfa410364d6c670e0ec155e4ee0cf0898f 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 5ce911db7d884b99ba208cd97839e97f9c17978d..81bcdcaaceb3cd6aff8f27391450e388aa3d0466 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -451,6 +451,14 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s #define __visible #endif +#ifndef __norecordmcount +#define __norecordmcount +#endif + +#ifndef __nocfi +#define __nocfi +#endif + /* * Assume alignment of return value. */ diff --git a/include/linux/cpufreq_times.h b/include/linux/cpufreq_times.h new file mode 100644 index 0000000000000000000000000000000000000000..3fb38750c853fe8d53813bb38f185ca9155f29d4 --- /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 +#include +#include + +#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 b49f8661ffcd02d869f6d8b731e0effed36ac005..bd96bb2e74755f42f20ff397496dc72d351343fa 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -695,6 +695,11 @@ void alloc_bootmem_cpumask_var(cpumask_var_t *mask); 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_cpumask_var(cpumask_var_t mask) 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 add6c4bc568f150cea20c058efee0419f9672fab..ed9cf2f5cd06997db3ba2dc4d0d4bf69621fbd57 100644 --- a/include/linux/dax.h +++ b/include/linux/dax.h @@ -61,11 +61,6 @@ static inline int dax_pmd_fault(struct vm_area_struct *vma, unsigned long addr, 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/diagchar.h b/include/linux/diagchar.h index 95fe2397315f4b33d7241bad323d93b6837035ed..d9912e07b7a988410eb28f3cddb373e458f4aa5d 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 0x0C5B #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 @@ static const uint32_t msg_bld_masks_0[] = { 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 @@ static const uint32_t msg_bld_masks_0[] = { 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 @@ static const uint32_t msg_bld_masks_6[] = { MSG_LVL_LOW, MSG_LVL_LOW, MSG_LVL_LOW, + MSG_LVL_LOW, MSG_LVL_LOW }; @@ -516,7 +517,9 @@ static const uint32_t msg_bld_masks_7[] = { 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 @@ static const uint32_t msg_bld_masks_8[] = { 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 @@ static const uint32_t msg_bld_masks_8[] = { 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 @@ static const uint32_t msg_bld_masks_10[] = { 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_19[] = { }; 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 @@ static const uint32_t msg_bld_masks_25[] = { /* 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 @@ static const uint32_t log_code_last_tbl[] = { 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/f2fs_fs.h b/include/linux/f2fs_fs.h index 58aecb60ea51b90eaf3a447a1f25c21d137a145b..aa5db8b5521a589ae48cebd7c6119e315f45a460 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 @@ struct f2fs_super_block { __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 @@ struct f2fs_super_block { __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 @@ struct f2fs_node { */ #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 4abfa9c71aa444116726c52aea1ce4054901b771..8a3bdadd0ca7231348a3786d2a077a7bd91385af 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -18,8 +18,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -143,6 +145,9 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset, /* 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 page; 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 @@ struct writeback_control; #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 @@ struct kiocb { 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 @@ struct inode { 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 @@ struct file_lock_context { #define OFFT_OFFSET_MAX INT_LIMIT(off_t) #endif -#include - extern void send_sigio(struct fown_struct *fown, int fd, int band); /* @@ -1319,6 +1337,7 @@ struct mm_struct; #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 */ @@ -3059,6 +3078,25 @@ static inline bool io_is_direct(struct file *filp) 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 8641e56b8f8a9107a3cb595b130fe70b12839f79..9e535af579e81b11f5a3528463578947ed6de1db 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -13,42 +13,13 @@ #ifndef _LINUX_FSCRYPT_H #define _LINUX_FSCRYPT_H -#include #include -#include -#include -#include -#include -#include #define FS_CRYPTO_BLOCK_SIZE 16 +struct fscrypt_ctx; struct fscrypt_info; -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. - */ -struct fscrypt_symlink_data { - __le16 len; - char encrypted_path[1]; -} __packed; - struct fscrypt_str { unsigned char *name; u32 len; @@ -67,86 +38,11 @@ struct fscrypt_name { #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 - -#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 -#endif /* __FS_HAS_ENCRYPTION */ +#endif /** * fscrypt_require_key - require an inode's encryption key @@ -287,4 +183,68 @@ static inline int fscrypt_prepare_setattr(struct dentry *dentry, 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 c4c6bf2c390e0550198e58040ae6c9918571adb0..6f97714fbd8838edfc5a663b360b80d0a23d3d19 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 @@ static inline int fscrypt_decrypt_page(const struct inode *inode, 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 @@ static inline int fscrypt_get_encryption_info(struct inode *inode) 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 @@ static inline void fscrypt_free_filename(struct fscrypt_name *fname) 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 @@ static inline int fscrypt_fname_disk_to_usr(struct inode *inode, 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 @@ static inline bool fscrypt_match_name(const struct fscrypt_name *fname, } /* 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) +{ +} + +static inline void fscrypt_enqueue_decrypt_bio(struct fscrypt_ctx *ctx, + struct bio *bio) { - return; } static inline void fscrypt_pullback_bio_page(struct page **page, bool restore) @@ -207,4 +213,28 @@ static inline int __fscrypt_prepare_lookup(struct inode *dir, 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 2db5e9706f60d32f8dcf0dffecf72bc7b39e7247..1ed79eeb24389b8ace1cdc2a52e666a2c40efc71 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -10,8 +10,55 @@ #ifndef _LINUX_FSCRYPT_SUPP_H #define _LINUX_FSCRYPT_SUPP_H +#include +#include + +/* + * 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 *); +}; + +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 +66,12 @@ extern struct page *fscrypt_encrypt_page(const struct inode *, struct page *, 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 +96,7 @@ extern int fscrypt_inherit_context(struct inode *, struct inode *, 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 +107,11 @@ static inline void fscrypt_free_filename(struct fscrypt_name *fname) 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,7 +188,9 @@ static inline bool fscrypt_match_name(const struct fscrypt_name *fname, } /* 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); @@ -152,5 +204,14 @@ extern int __fscrypt_prepare_rename(struct inode *old_dir, 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 c332f0a4560794225a538f2a45ed77f936472c46..3fdfede2f0f3e9d7f66cd498f2ac51d00a012d58 100644 --- a/include/linux/fsl_ifc.h +++ b/include/linux/fsl_ifc.h @@ -734,11 +734,7 @@ struct fsl_ifc_nand { 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/gfp.h b/include/linux/gfp.h index 16ef4077f8b4c62fab96634dcd1b82876313a7c5..51649c97fb818ffccda473af59e3f4221e1e6efe 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -523,7 +523,7 @@ extern void __free_page_frag(void *addr); 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 b2ec82712baac8e5ead67ea20d4f55372a8e74a2..fab65b61d6d4e582f63d7371ff0710d15da404eb 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -801,7 +801,7 @@ extern int hidinput_connect(struct hid_device *hid, unsigned int force); 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 @@ static inline void hid_hw_wait(struct hid_device *hdev) * * @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/init.h b/include/linux/init.h index 8e346d1bd83757519e0cc661c3728e1f7e4d50a9..d4b13a4e04e6d40e7cb7ac4fdd134705b09f5515 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -5,10 +5,10 @@ #include /* 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 prepare_namespace(void); 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 @@ extern bool initcall_debug; #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 @@ extern bool initcall_debug; */ #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/ipa.h b/include/linux/ipa.h index f966e0c0d94620270eace676069cf709c28956da..1a1c68e3a44e90b46811ffd0dc39a9c1348a7f1e 100644 --- a/include/linux/ipa.h +++ b/include/linux/ipa.h @@ -116,19 +116,6 @@ enum hdr_total_len_or_pad_type { IPA_HDR_TOTAL_LEN = 1, }; -/** -* 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 diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h index 589d14e970ad8da596684e818dca5d3b44e514e3..c2a0f0072274329f1966df12b2c3275b537befe1 100644 --- a/include/linux/jiffies.h +++ b/include/linux/jiffies.h @@ -1,6 +1,7 @@ #ifndef _LINUX_JIFFIES_H #define _LINUX_JIFFIES_H +#include #include #include #include @@ -63,19 +64,17 @@ extern int register_refined_jiffies(long clock_tick_rate); /* 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 bc6ed52a39b967898c6e2f2814e23fbd6f2a3332..61054f12be7cf3b173f05480015ff7133c340e60 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/mlx4/qp.h b/include/linux/mlx4/qp.h index b4ee8f62ce8da82720cb79e7a9ea3ec97703b849..8e2828d48d7fcf6a7ba683bf51c8c91a1ad28ec0 100644 --- a/include/linux/mlx4/qp.h +++ b/include/linux/mlx4/qp.h @@ -470,6 +470,7 @@ struct mlx4_update_qp_params { 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 58276144ba81f338ae82f8d3ab960c7dff9d58e3..5f3e62253e2faa0838d185a2905de6b4a8726415 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -750,8 +750,14 @@ enum { }; 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 6a620e01b04090de4a969d8f4c205add0d9cb71d..7751d72a6a40c034e2f1c7cfb5bb9668561797fb 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -380,8 +380,8 @@ struct mlx5_core_srq { 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 932e99cfde37e84491b878641e3a5eaff8c71f76..270f0328f411120af606650fb4a735f572761c31 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1291,6 +1291,19 @@ long __get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm, 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); diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index c5a4a259577ac91ec640688e6e6b33ada57bd03f..48b7281a5e25278dfb22f3d1df4c107a25c6c2c2 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -138,6 +138,8 @@ struct mmc_bus_ops { 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 2530fcce1f5646922d2873f96001f020bb9ed7f8..7228bcd24ad521f1c5e50868814dfe77fb72f061 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -454,6 +454,7 @@ struct mmc_host { #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 */ diff --git a/include/linux/module.h b/include/linux/module.h index fd9e121c7b3f3bbd1bfd66e78e0b324be894d1c4..9d6fd1d17edbc7054725405fc94895206b7b458f 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -20,6 +20,7 @@ #include #include /* only as arch move module.h -> extable.h */ #include +#include #include #include @@ -349,6 +350,10 @@ struct module { 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 52666d90ca9452251c539383ecc7b038ba9afbea..060db5c327be632a80f1f5c89220c7c66442b985 100644 --- a/include/linux/moduleparam.h +++ b/include/linux/moduleparam.h @@ -225,19 +225,11 @@ struct kparam_array 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 6e0b439fcdc8a3ebd8e1f9a019b960d3c3552798..81e7e7527960d60600c64490d4db3591a8ed22d2 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 @@ -219,6 +219,11 @@ enum gsi_max_prefetch { 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 +362,7 @@ struct gsi_chan_props { 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; diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 2ad1a2b289b5a57d25a3835e97302dd0db774f90..69111fa2e5780ad12407864d40730d59d22e9ddd 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -254,6 +254,8 @@ unsigned int *xt_alloc_entry_offsets(unsigned int size); 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 @@ static inline unsigned long ifname_compare_aligned(const char *_a, 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 fbc98e2c8228d0b8ca17e4af9be1cb1cddaccd27..e791ebc65c9c0776325cdc5e72de5d995038d7e0 100644 --- a/include/linux/nospec.h +++ b/include/linux/nospec.h @@ -5,6 +5,7 @@ #ifndef _LINUX_NOSPEC_H #define _LINUX_NOSPEC_H +#include /** * array_index_mask_nospec() - generate a ~0 mask when index < size, 0 otherwise @@ -29,26 +30,6 @@ static inline unsigned long array_index_mask_nospec(unsigned long index, } #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 * @@ -67,12 +48,11 @@ static inline unsigned long array_index_mask_nospec(unsigned long index, ({ \ 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); \ }) #endif /* _LINUX_NOSPEC_H */ diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 7dbe9148b2f8a661257d0aa620cf1f61fa7d5f57..791c54709cd178fcf5cfbe3507162994593eb7d9 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -148,7 +148,7 @@ static inline int page_cache_get_speculative(struct page *page) #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 @@ static inline int page_cache_add_speculative(struct page *page, int count) #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 @@ static inline gfp_t readahead_gfp_mask(struct address_space *x) __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 @@ extern int read_cache_pages(struct address_space *mapping, 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 1b711796d989d95ef0d08fcd21355b855b7c67af..36522905685b59d299e5c9bfb86b7c031adf376e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1348,9 +1348,9 @@ static inline int pci_alloc_irq_vectors(struct pci_dev *dev, 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/platform_data/isl9305.h b/include/linux/platform_data/isl9305.h index 1419133fa69e9be9ad07e55690fb8abeff966e32..4ac1a070af0a1967f59c02e3b18363ba83e7552f 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 34c4498b800fc7b288bedceb375a7788d375f4d4..83b22ae9ae12a27a2855f0559cd227036a2accf6 100644 --- a/include/linux/posix-clock.h +++ b/include/linux/posix-clock.h @@ -59,23 +59,23 @@ struct posix_clock_operations { 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/proc_fs.h b/include/linux/proc_fs.h index b97bf2ef996ef3e9e0cf906280741f20365d1455..b326d0a0cace1c67610ebbaf26fe3f24d8a20c0f 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -74,6 +74,12 @@ static inline int remove_proc_subtree(const char *name, struct proc_dir_entry *p #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 6306ab10af182141ef3568ba0665b2bcc4fea9a7..347077cf19c6928e87a780f38f768677cdcb9810 100644 --- a/include/linux/psci.h +++ b/include/linux/psci.h @@ -25,6 +25,17 @@ bool psci_tos_resident_on(int cpu); 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 @@ struct psci_operations { 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 a083370465b310020af01c70f9e986e7df42fa63..48fe2e981b0cbce40160a2bda060d9bc1de69808 100644 --- a/include/linux/qpnp/qpnp-adc.h +++ b/include/linux/qpnp/qpnp-adc.h @@ -401,6 +401,10 @@ enum qpnp_adc_channel_scaling_param { * %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 @@ enum qpnp_adc_scale_fn_type { 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 @@ struct qpnp_vadc_scaling_ratio { * @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,9 +1473,10 @@ int32_t qpnp_adc_batt_therm_qrd(struct qpnp_vadc_chip *dev, 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 + * 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, @@ -1476,6 +1485,41 @@ int32_t qpnp_adc_batt_therm_qrd(struct qpnp_vadc_chip *dev, * 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 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_scale_batt_therm(struct qpnp_vadc_chip *dev, int32_t adc_code, const struct qpnp_adc_properties *adc_prop, @@ -2091,6 +2135,18 @@ static inline int32_t qpnp_adc_batt_therm_qrd(struct qpnp_vadc_chip *vadc, 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/regulator/driver.h b/include/linux/regulator/driver.h index ee7911363c2155c4ab225f2092dc4ea93648d872..9760b534ac08f3e6beaa145737ffe1c4f18a9793 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -432,6 +432,8 @@ struct regulator_dev { 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 5c132d3188be8f2ead11a6c71c75e8bde6f4d27c..85d1ffc90285f837f14edde2cb0db6b508443252 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -706,8 +706,10 @@ static inline void *__rhashtable_insert_fast( 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 4b2a1bcea5ff4384a1eae7f37ec360bf7749a5cf..0748b7bc1a991a00201755862457eb5019adaa4d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1664,6 +1664,7 @@ struct sched_dl_entity { 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 @@ struct task_struct { 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; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index c73bef9785baffec83f65e4c8c3923fa1e9507e8..3fbfe9615be3d2d262773639c4b47430ddf7ac5c 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -988,10 +988,10 @@ struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, 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/tty.h b/include/linux/tty.h index a41244fe58d022655c0726c7a9db25afa4643d0f..6f1ee8528210e956fbfe283361f4fde32cad08fe 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -355,6 +355,7 @@ struct tty_file_private { #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 */ diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h index de2a722fe3cf7c457352eb02548f0351a669ddf5..ea4f81c2a6d5e04b5567d608f2276c1abfd01c6f 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/wcnss_wlan.h b/include/linux/wcnss_wlan.h index b37f8dfe94cddfb3c45c9b41088126bc0a9f76c7..05fb7b57c9f0ff7d5ccfe2b0dde140cee65abb64 100644 --- a/include/linux/wcnss_wlan.h +++ b/include/linux/wcnss_wlan.h @@ -63,6 +63,13 @@ enum { 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_dump_stack(struct task_struct *task); 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 1061add575d23f22c55f0b6d1fa8f252bff4adc8..1def337b16d4d378f5de4de378c63eedc2fa0be4 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -453,6 +453,7 @@ extern bool cancel_delayed_work_sync(struct delayed_work *dwork); 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/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 554671c81f4a39a8a773a01a318af0b1c70b5617..4931787193c3f0940094dc72c118464789c3907b 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -893,7 +893,7 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst, 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/cfg80211.h b/include/net/cfg80211.h index 520c4c36885d2dd9d823e98148987be1b0757556..f656728cc644558be7f3bfc7d2cb73b329449959 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 @@ enum rate_info_flags { * @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/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 0406073c613bcbddcb9a7619dfd7d31ac811b7bc..bc7a1d9efbc40941fad3ef48b7bf21997ff7e112 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -332,7 +332,7 @@ static inline bool nf_ct_should_gc(const struct nf_conn *ct) 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/sch_generic.h b/include/net/sch_generic.h index f18fc1a0321f8b6b2f810bc15709d9deadf5dbd6..538f3c4458b0425c232de6ac06c9319f95201935 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -675,6 +675,16 @@ static inline void __qdisc_drop(struct sk_buff *skb, struct sk_buff **to_free) *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 @@ static inline int qdisc_drop(struct sk_buff *skb, struct Qdisc *sch, 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 8716d5942b6561dc1cfd037ccfb7d732d26eba07..8fcf8908a694fb63a7316d1b0f760511d189e880 100644 --- a/include/net/slhc_vj.h +++ b/include/net/slhc_vj.h @@ -127,6 +127,7 @@ typedef __u32 int32; */ 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 e7ac1147553862df497872602ecfa0a38ad7ca06..76d7c97355914286d0e68aa82f9b5bd25017066c 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1281,9 +1281,11 @@ void tcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd, 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 80761938b9a78081822a4b82b4bd3fb30b5f6625..8228155b305e55162534f5b225c1ffa4f362e6cd 100644 --- a/include/net/udplite.h +++ b/include/net/udplite.h @@ -62,6 +62,7 @@ static inline int udplite_checksum_init(struct sk_buff *skb, struct udphdr *uh) 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 c383aa4edbf0c27980ef7aad22ff160c72b41bbd..6d30a01d281d4faa824129cb3921dbc3a77e7da3 100644 --- a/include/net/x25.h +++ b/include/net/x25.h @@ -298,10 +298,10 @@ void x25_check_rbuf(struct sock *); /* 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 818a38f9922122167cb11deebe6e87b6643fc522..f888263fd757281aa14b7196ced92416e2f06660 100644 --- a/include/rdma/ib_addr.h +++ b/include/rdma/ib_addr.h @@ -129,6 +129,8 @@ int rdma_copy_addr(struct rdma_dev_addr *dev_addr, struct net_device *dev, 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/soc/fsl/qe/qe.h b/include/soc/fsl/qe/qe.h index 70339d7958c02c2d8690509a5b62ef8a844bb7ff..226f915a68c28e5abf3e902e5b3b38c25f4eb41a 100644 --- a/include/soc/fsl/qe/qe.h +++ b/include/soc/fsl/qe/qe.h @@ -243,6 +243,7 @@ static inline int qe_alive_during_sleep(void) #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 @@ struct ucc_slow_pram { #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/sound/pcm_oss.h b/include/sound/pcm_oss.h index 760c969d885d542997010ad8486662fc35731088..12bbf8c8111209f134387d7380a1b7f8e06050ec 100644 --- a/include/sound/pcm_oss.h +++ b/include/sound/pcm_oss.h @@ -57,6 +57,7 @@ struct snd_pcm_oss_runtime { 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/sched.h b/include/trace/events/sched.h index e06df4da01ef27ce2587a8ab60a9849b74548d85..b355ebfd76973052aadeb54acb392a817b01cc8c 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -713,10 +713,10 @@ TRACE_EVENT(sched_energy_diff, 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 @@ TRACE_EVENT(sched_task_util, __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 @@ TRACE_EVENT(sched_task_util, __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/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 75aa98ffb40fa1d87ac56b4713886889a624a0dd..c9248e5ae2d1a2cb8ea85939d526e058dfb8a5f1 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -65,6 +65,7 @@ header-y += auto_fs.h 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 @@ -490,6 +491,7 @@ header-y += virtio_rng.h 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 0000000000000000000000000000000000000000..498f4e0335e61951d338a5511bd7a646d6916226 --- /dev/null +++ b/include/uapi/linux/batterydata-interface.h @@ -0,0 +1,30 @@ +#ifndef __BATTERYDATA_LIB_H__ +#define __BATTERYDATA_LIB_H__ + +#include + +/** + * 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/fcntl.h b/include/uapi/linux/fcntl.h index beed138bd359382273cd9f9c114f44b7bd559841..f85ed3a5ef4de128eb5a60c8526a5e635fd7b3cc 100644 --- a/include/uapi/linux/fcntl.h +++ b/include/uapi/linux/fcntl.h @@ -42,6 +42,27 @@ #define F_SEAL_WRITE 0x0008 /* prevent writes */ /* (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. */ diff --git a/include/uapi/linux/ipa_qmi_service_v01.h b/include/uapi/linux/ipa_qmi_service_v01.h index 1917c0dd26990b059e1fe766e3c20d848c058c0d..72efc86015cb988cc84347f3256d67683ba25b13 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 @@ struct ipa_init_modem_driver_req_msg_v01 { * 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 @@ struct ipa_configure_ul_firewall_rules_ind_msg_v01 { #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/libc-compat.h b/include/uapi/linux/libc-compat.h index 44b8a6bd5fe1128e8c18061e14b9a63486d26e0d..774cb2db1b89f26460e250faff5b58a571d74fa5 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 062a1b9c3e992486864a4a99159b3c3754ef2a72..1c39dafa68d4fac0626eb1b4d1adb687980a562a 100644 --- a/include/uapi/linux/msm_ipa.h +++ b/include/uapi/linux/msm_ipa.h @@ -97,6 +97,7 @@ #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 /** * max size of the header to be inserted @@ -1847,6 +1848,29 @@ struct ipa_tether_device_info { struct ipa_lan_client lan_client[IPA_MAX_NUM_HW_PATH_CLIENTS]; }; +/** + * 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; +}; + /** * actual IOCTLs supported by IPA driver */ @@ -2029,6 +2053,9 @@ struct ipa_tether_device_info { 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 *) /* * unique magic number of the Tethering bridge ioctls */ diff --git a/include/uapi/linux/netfilter/xt_bpf.h b/include/uapi/linux/netfilter/xt_bpf.h index 1fad2c27ac320d08f6f83ccbf7600b1c2768eb98..da161b56c79e443afacce238d4a120b373673db9 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 +#include #include #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 xt_bpf_info { 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/pci_regs.h b/include/uapi/linux/pci_regs.h index 2c5810a1b76c3c98b7c4ba8113ea90385cfe3d4c..b67cfd8d37051c96b8aabcba256dd8419e75fc5b 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/psci.h b/include/uapi/linux/psci.h index 3d7a0fc021a75a7b52c725c151dee15eb9fc5830..39930ca998cd8b86a00063c9079830ae086717d4 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.h b/include/uapi/linux/qg.h index 54488ff79c8185fcb5ce15ea9e09f9381275d8ff..2c7b49af873dcb4edc8d6bd0d67088c26dfc091f 100644 --- a/include/uapi/linux/qg.h +++ b/include/uapi/linux/qg.h @@ -12,8 +12,8 @@ enum qg { QG_ESR, QG_CHARGE_COUNTER, QG_FIFO_TIME_DELTA, - QG_RESERVED_1, - QG_RESERVED_2, + QG_BATT_SOC, + QG_CC_SOC, QG_RESERVED_3, QG_RESERVED_4, QG_RESERVED_5, @@ -25,6 +25,9 @@ enum qg { QG_MAX, }; +#define QG_BATT_SOC QG_BATT_SOC +#define QG_CC_SOC QG_CC_SOC + struct fifo_data { unsigned int v; unsigned int i; diff --git a/include/uapi/linux/random.h b/include/uapi/linux/random.h index 3f93d1695e7f6b0ba27ea5e563126960ea84c6a4..b455b0d86f26920d0679b9796c3837401db53055 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/usb/audio.h b/include/uapi/linux/usb/audio.h index c6f5b096c594ffb0a02cdeae8d26e556507c0926..6f6d93f6c4063c566a2141acf9b67752ec06ea6b 100644 --- a/include/uapi/linux/usb/audio.h +++ b/include/uapi/linux/usb/audio.h @@ -370,7 +370,7 @@ static inline __u8 uac_processing_unit_bControlSize(struct uac_processing_unit_d { 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 @@ static inline __u8 *uac_processing_unit_bmControls(struct uac_processing_unit_de { 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 0b5ff0fb10e86c0b450f5b29437bc36aafe7955e..d14cd8fabee1c7d12a89f66a31d9b4fef7cf7b49 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -696,6 +696,7 @@ enum v4l2_mpeg_vidc_video_rate_control { 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 @@ enum v4l2_mpeg_vidc_video_divx_format_type { 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 0000000000000000000000000000000000000000..364efc848855dcd95659b3f9085a650fe968b018 --- /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 c58a2734dfa9f81a93fc127605d0db88aaaf8c42..7b2b966ce5c6c83a15dda3baee84c968512c26db 100644 --- a/include/uapi/media/msm_camera.h +++ b/include/uapi/media/msm_camera.h @@ -1392,7 +1392,7 @@ struct msm_camera_csiphy_params { 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 2283933f325ba674c1b3d0e5bfe6f84bbbd5cb33..6d25967d1ca88d89e3505c5ecf19c11725bded81 100644 --- a/include/uapi/media/msm_camsensor_sdk.h +++ b/include/uapi/media/msm_camsensor_sdk.h @@ -367,7 +367,7 @@ struct msm_camera_csiphy_params { 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 4f12e5c534c8c4d7cfc389b6174133449d613183..3fd0c8849a4ec8e3f4a8c4f26f903205b22f9567 100644 --- a/include/uapi/media/msm_media_info.h +++ b/include/uapi/media/msm_media_info.h @@ -1229,6 +1229,7 @@ static inline unsigned int VENUS_BUFFER_SIZE( { 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 @@ static inline unsigned int VENUS_BUFFER_SIZE( 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 @@ static inline unsigned int VENUS_BUFFER_SIZE( 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/init/Kconfig b/init/Kconfig index 2e57658cb7404a12c0e08f9f94493be859e3ad1f..e3929edde2c0b782e10bf1cc2573d2efd793afc1 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -840,6 +840,16 @@ config LOG_BUF_SHIFT 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 @@ endif # MODULES 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 17e439ff46f5d4186cdacb8cda9f4e19e73ac8de..f9cd3f04e2b0a4dc5919ff1ca5ce56cdaa02ff96 100644 --- a/init/main.c +++ b/init/main.c @@ -81,6 +81,7 @@ #include #include #include +#include #include #include @@ -913,14 +914,16 @@ static int try_to_run_init_process(const char *init_filename) 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 e2072ae4f90e2f86082a2d41eba161c64c5bfeba..b626745e771ca3798e7891b7d7d2f1d91143e85f 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -198,6 +198,12 @@ static int __shm_open(struct vm_area_struct *vma) 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 @@ static int shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 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 @@ static int shm_mmap(struct file *file, struct vm_area_struct *vma) 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 @@ static int shm_release(struct inode *ino, struct file *file) 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 @@ static const struct vm_operations_struct shm_vm_ops = { .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 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, 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 314e7d62f5f0a3a4fae40d71a3355c0bdecc1a0e..108f5bee0111dff2ad25edbbd0a84309c6873dca 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -32,6 +32,9 @@ KASAN_SANITIZE_kcov.o := n # 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_TRACEPOINTS) += trace/ 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/bpf/arraymap.c b/kernel/bpf/arraymap.c index 2eb9b7f3c8041f066e155cb9e5f5bed31dcf467f..4db6a67d4c61b6f3395b407a02c1d98a07b04ae4 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -23,8 +23,10 @@ static void bpf_array_free_percpu(struct bpf_array *array) { 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 @@ static int bpf_array_alloc_percpu(struct bpf_array *array) return -ENOMEM; } array->pptrs[i] = ptr; + cond_resched(); } return 0; @@ -51,8 +54,9 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr) 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 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr) 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 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr) 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; } diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 879ca844ba1d3371617ef4a16bf103dad61d69ee..66d64db4f8c7f0f0461f7e77f0bc25e34c31d62d 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -466,7 +466,7 @@ EXPORT_SYMBOL_GPL(__bpf_call_base); * * 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/inode.c b/kernel/bpf/inode.c index 2d23c32e9c4b735b18398fc774aa1e2c19104132..7e48fa609579b473fe1e3b6dd12b191c4372826f 100644 --- a/kernel/bpf/inode.c +++ b/kernel/bpf/inode.c @@ -322,6 +322,42 @@ int bpf_obj_get_user(const char __user *pathname, int flags) 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 230aced8bdc96187f9d888f18adcb2bbe15ccc30..6039db3dcf0269e0280c190968c6ec0abdfd0161 100644 --- a/kernel/bpf/stackmap.c +++ b/kernel/bpf/stackmap.c @@ -91,6 +91,7 @@ static struct bpf_map *stack_map_alloc(union bpf_attr *attr) 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 590ce0acdf179ddb5f23e00b47bf4b7fbc93bede..41aa664124e87bc9969c87d4b1a89a82ee239eba 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -970,7 +970,7 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz 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 0000000000000000000000000000000000000000..87053e2c0acfef136b50d6330fb756346668afbf --- /dev/null +++ b/kernel/cfi.c @@ -0,0 +1,299 @@ +/* + * CFI (Control Flow Integrity) error and slowpath handling + * + * Copyright (C) 2017 Google, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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() +{ +#ifdef CONFIG_CFI_PERMISSIVE + WARN_RATELIMIT(1, "CFI failure:\n"); +#else + pr_err("CFI failure:\n"); + 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(); +} +EXPORT_SYMBOL(cfi_slowpath_handler); +#endif /* CONFIG_MODULES */ + +void cfi_failure_handler(void *data, void *value, void *vtable) +{ + handle_cfi_failure(); +} +EXPORT_SYMBOL(cfi_failure_handler); + +void __cfi_check_fail(void *data, void *value) +{ + handle_cfi_failure(); +} diff --git a/kernel/cpu.c b/kernel/cpu.c index c68d1505a1b6af0e21025418d8ca2c4df41a4064..3f3b7f848efefd64dc47e2735ecc552867a41786 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -64,6 +64,12 @@ struct cpuhp_cpu_state { 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 @@ static void cpuhp_thread_fun(unsigned int cpu) 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 @@ static void cpuhp_thread_fun(unsigned int cpu) 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 @@ cpuhp_invoke_ap_callback(int cpu, enum cpuhp_state state, bool bringup, 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 @@ static int cpuhp_kick_ap_work(unsigned int cpu) 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 e9fdb5203de5c0b99bfb992640e89ffe9d4a8160..411226b26bcaa2be897885f343dd68d14f66a6ab 100644 --- a/kernel/events/callchain.c +++ b/kernel/events/callchain.c @@ -227,12 +227,18 @@ get_perf_callchain(struct pt_regs *regs, u32 init_nr, bool kernel, bool user, } 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 cd941f8541424a6854538eb49542e6b000c5e527..2ea4eb124a8f6123be80cefba2fea48a55e43f11 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4181,6 +4181,9 @@ static void _free_event(struct perf_event *event) 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 @@ static void perf_output_read_one(struct perf_output_handle *handle, __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 @@ static void perf_output_read_group(struct perf_output_handle *handle, #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 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, * 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 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, 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 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, 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_event_alloc(struct perf_event_attr *attr, int cpu, 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); diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c index 3f8cb1e14588fe3258a1c3aa49874e008f5a266a..253ae2da13c38750166299ba265d0b4a6edabf4b 100644 --- a/kernel/events/hw_breakpoint.c +++ b/kernel/events/hw_breakpoint.c @@ -427,16 +427,9 @@ EXPORT_SYMBOL_GPL(register_user_hw_breakpoint); * 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 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att 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; - - err = validate_hw_breakpoint(bp); - if (!err) - perf_event_enable(bp); + if (!attr->disabled) { + int err = validate_hw_breakpoint(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); + if (err) + return err; - return err; + perf_event_enable(bp); + bp->attr.disabled = 0; } -end: - bp->attr.disabled = attr->disabled; - return 0; } EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint); diff --git a/kernel/exit.c b/kernel/exit.c index 35ff283454591e5cf5fe30624f7cec09f3a919d6..4b4f03acffd2fdcecd6dc4fa55cbe2e907bcaefb 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -54,6 +54,7 @@ #include #include #include +#include #include "sched/tune.h" @@ -171,6 +172,9 @@ void release_task(struct task_struct *p) { 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 @@ void __noreturn do_exit(long code) * 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 diff --git a/kernel/irq/debug.h b/kernel/irq/debug.h index e75e29e4434a9073b0d05f39903df2cf7f4c2d10..3514d955af949c2520aaad10307ebff15511a32d 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 c1195eb01734921bbf7e3f6da001e8953925f5e7..c93d4dfd8dc7efddfcaaa7fffa32423fb678ad9a 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -853,7 +853,7 @@ irq_thread_check_affinity(struct irq_desc *desc, struct irqaction *action) * 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 fafd1a3ef0da56c6887fb5edd631e8e4a925a0e4..a0c3365c884b6db8c3a4fddef90418f25468941f 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -306,6 +306,24 @@ int kallsyms_lookup_size_offset(unsigned long addr, unsigned long *symbolsize, 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 @@ const char *kallsyms_lookup(unsigned long addr, 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 @@ int lookup_symbol_name(unsigned long addr, char *symname) /* 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 @@ int lookup_symbol_attrs(unsigned long addr, unsigned long *size, 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 a1a07cf1101fc88a0dfd02433a5517cf45ec2122..69485183af7914121d2576ceebcac26692ae8620 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -125,7 +125,7 @@ static void *alloc_insn_page(void) 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 d3de04b12f8cf32fd0234aa1ff1add582d0c2550..babc67cfed693bf1d8e096fede0d089257061677 100644 --- a/kernel/locking/locktorture.c +++ b/kernel/locking/locktorture.c @@ -641,8 +641,7 @@ static void __torture_print_stats(char *page, { 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 @@ static void lock_torture_cleanup(void) * 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 @@ static int __init lock_torture_init(void) 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 @@ static int __init lock_torture_init(void) #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 @@ static int __init lock_torture_init(void) 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; - } - - for (i = 0; i < cxt.nrealreaders_stress; i++) { - cxt.lrsa[i].n_lock_fail = 0; - cxt.lrsa[i].n_lock_acquired = 0; + 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; + } } } @@ -915,12 +925,14 @@ static int __init lock_torture_init(void) 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 2c49d76f96c3351c2038b630e5875066a907509b..196cc460e38ddcd635b69c30079ce06b346f7e75 100644 --- a/kernel/locking/rtmutex.c +++ b/kernel/locking/rtmutex.c @@ -236,8 +236,7 @@ rt_mutex_waiter_less(struct rt_mutex_waiter *left, * 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 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task, /* [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 rt_mutex_adjust_prio_chain(struct task_struct *task, 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 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock, 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 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock, 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 @@ static void remove_waiter(struct rt_mutex *lock, 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 e317e1cbb3eba80098fa018de8573c1a1c7a86c0..50848b460851be04b94fb9969b4aa444ad0cef7d 100644 --- a/kernel/locking/rtmutex_common.h +++ b/kernel/locking/rtmutex_common.h @@ -33,6 +33,7 @@ struct rt_mutex_waiter { struct rt_mutex *deadlock_lock; #endif int prio; + u64 deadline; }; /* diff --git a/kernel/memremap.c b/kernel/memremap.c index 426547a21a0c9479292ead7048ae3b20c291fca5..f61a8c387c3e05bacb2f0cf2d3454328744573ca 100644 --- a/kernel/memremap.c +++ b/kernel/memremap.c @@ -194,7 +194,7 @@ void put_zone_device_page(struct page *page) } 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 @@ static void pgmap_radix_release(struct resource *res) 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 @@ static void devm_memremap_pages_release(struct device *dev, void *data) 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 @@ struct dev_pagemap *find_dev_pagemap(resource_size_t phys) 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 @@ void *devm_memremap_pages(struct device *dev, struct resource *res, 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 07bfb9971f2fb7b55f52bbe677e5f9afd8b84d53..13ad2f8c44f9b936b445abae246106b1c67c2750 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1911,6 +1911,9 @@ static void frob_writable_data(const struct module_layout *layout, /* 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_disable_ro(const struct module *mod) 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 @@ void set_all_modules_text_rw(void) { 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 @@ void set_all_modules_text_ro(void) { 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 @@ void set_all_modules_text_ro(void) 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 @@ void __weak module_arch_freeing_init(struct module *mod) { } +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 @@ static void free_module(struct module *mod) /* 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 @@ int __weak module_finalize(const Elf_Ehdr *hdr, 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 @@ static int post_relocation(struct module *mod, const struct load_info *info) /* 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 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, } #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 693a64385d59a7e77611896ecb33a3ed3e4f3eba..fa704f88ff8e8b07a3cf42ff2b278a4d59abb499 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -322,8 +322,10 @@ struct pid *alloc_pid(struct pid_namespace *ns) } 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 a29eaee3789d57c293fae66eb061dd0dda5a297e..bf60b37f1df1f91890c1f8f35b4f61cc24dce5f2 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -94,6 +94,16 @@ config HIBERNATION_IMAGE_REUSE 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/swap.c b/kernel/power/swap.c index 95db6b79f6fa55ab2f86699bd064cd3af08ab829..1f6aef2e21312942a87264c12f60ae0adc58f255 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -606,9 +606,10 @@ static int crc32_threadfn(void *data) } 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 @@ static int load_image_lzo(struct swap_map_handle *handle, 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 d5760c42f042f4accfb65de23784bcaa877d9b00..61d41ca418441a15945b376964f6f3ed07ac84b8 100644 --- a/kernel/printk/braille.c +++ b/kernel/printk/braille.c @@ -2,12 +2,13 @@ #include #include +#include #include #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 @@ char *_braille_console_setup(char **str, char **brl_options) } 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 769d771145c881cd78f43c035ea0866adb96b9b9..749a6756843a08e2a5f2a9ae4fa984658b43a65b 100644 --- a/kernel/printk/braille.h +++ b/kernel/printk/braille.h @@ -9,7 +9,14 @@ braille_set_options(struct console_cmdline *c, char *brl_options) 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 @@ braille_set_options(struct console_cmdline *c, char *brl_options) { } -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 20fc294fbd13b193d6cb8b823b239d13fb223dac..396b020963843ba1e84399ff01e58aa88b9d2b81 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -2172,6 +2172,8 @@ void resume_console(void) 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 @@ static int console_cpu_notify(struct notifier_block *self, return NOTIFY_OK; } +#endif + /** * console_lock - lock the console system for exclusive use. * @@ -2350,7 +2354,7 @@ void console_unlock(void) } /* - * 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 @@ void console_unlock(void) * 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 @@ static int __init printk_late_init(void) 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 89778a34631d50566c1c2c2ab0a15d1f61229524..fbea5afe10a862d64e7004667f6fa4ceac50a43b 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -633,7 +633,8 @@ static int __find_resource(struct resource *root, struct resource *old, 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 ef34eb52c4c7760b4544dc8bf91fb12d60793aa0..fb2e56c5fc18f98b4203c84daac8efe21396553c 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -76,6 +76,7 @@ #include #include #include +#include #include #include @@ -526,7 +527,8 @@ void resched_cpu(int cpu) 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 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) 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 @@ static void try_to_wake_up_local(struct task_struct *p, struct pin_cookie cookie 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 @@ void __dl_clear_params(struct task_struct *p) 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 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p) 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 @@ static void __sched notrace __schedule(bool preempt) 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 @@ static void __sched notrace __schedule(bool preempt) 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 @@ __setparam_dl(struct task_struct *p, const struct sched_attr *attr) 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 c0a8a2af9cb6d19e895314ec54aa2c3e8fa35c8e..26c9cf4cc1701720742afe2dc5988a7dc0b0c1da 100644 --- a/kernel/sched/core_ctl.c +++ b/kernel/sched/core_ctl.c @@ -588,7 +588,12 @@ static bool eval_need(struct cluster_data *cluster) 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; diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index e4f48f139106286ca4cb9a43ee45af2a1c0e8f8a..50f2ea890b6a63ea04b461cfd9d2c579ccb86100 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "sched.h" #ifdef CONFIG_PARAVIRT #include @@ -157,6 +158,11 @@ void account_user_time(struct task_struct *p, cputime_t cputime, /* 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 @@ void __account_system_time(struct task_struct *p, cputime_t cputime, /* 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 0a295acde570ac0f33d4a9d97a4e91471d183691..d7c7dd635361a10444d0887770f79fbbb28b2e21 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -485,13 +485,84 @@ static bool dl_entity_overflow(struct sched_dl_entity *dl_se, } /* - * 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 @@ static void update_dl_entity(struct sched_dl_entity *dl_se, 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 @@ static void dequeue_dl_entity(struct sched_dl_entity *dl_se) __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 @@ static void enqueue_task_dl(struct rq *rq, struct task_struct *p, int flags) * 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 04ba6d02736d911ad1a57402e8efd0f7988e99e9..78c433aab1233ca91da4f8a79608677bf8471762 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -2511,7 +2511,8 @@ void task_numa_work(struct callback_head *work) 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); @@ -6871,6 +6872,13 @@ static inline bool skip_sg(struct task_struct *p, struct sched_group *sg, 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 +7365,12 @@ static inline struct cpumask *find_rtg_target(struct task_struct *p) } #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 +7381,7 @@ static int select_energy_cpu_brute(struct task_struct *p, int prev_cpu, int sync 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 +7418,17 @@ static int select_energy_cpu_brute(struct task_struct *p, int prev_cpu, int sync 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 +7515,12 @@ static int select_energy_cpu_brute(struct task_struct *p, int prev_cpu, int sync 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 +8811,12 @@ static void update_cpu_capacity(struct sched_domain *sd, int cpu) 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 e6abbb4725eb39e6751192bd2ed8f9811c3bd885..7a32e5a2a506ec7e88a243bf38b7c21c009a6ad7 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -2492,7 +2492,7 @@ static void switched_to_rt(struct rq *rq, struct task_struct *p) 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 f27ab13d40cee15825df22e33bc2b7d3669849b2..61aa3c78e8d1586d5dd297de5ad5e6ea9606f469 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -798,7 +798,6 @@ struct rq { 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 @@ cpu_util_freq_pelt(int cpu) } #ifdef CONFIG_SCHED_WALT +extern atomic64_t walt_irq_work_lastq_ws; + static inline unsigned long cpu_util_freq_walt(int cpu, struct sched_walt_cpu_load *walt_load) { @@ -1898,7 +1899,7 @@ cpu_util_freq_walt(int cpu, struct sched_walt_cpu_load *walt_load) walt_load->prev_window_util = util; walt_load->nl = nl; walt_load->pl = pl; - walt_load->ws = rq->load_reported_window; + walt_load->ws = atomic64_read(&walt_irq_work_lastq_ws); } return (util >= capacity) ? capacity : util; @@ -2268,22 +2269,8 @@ static inline void cpufreq_update_util(struct rq *rq, unsigned int flags) 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 a9fb3678fdb1d5742c46a779f1f49b60f10f5709..a2debf9dc40546ce58b3df10e1a41c6af1c4ea86 100644 --- a/kernel/sched/walt.c +++ b/kernel/sched/walt.c @@ -44,7 +44,7 @@ const char *migrate_type_names[] = {"GROUP_TO_RQ", "RQ_TO_GROUP", static struct cpu_cycle_counter_cb cpu_cycle_counter_cb; static bool use_cycle_counter; DEFINE_MUTEX(cluster_lock); -static atomic64_t walt_irq_work_lastq_ws; +atomic64_t walt_irq_work_lastq_ws; static struct irq_work walt_cpufreq_irq_work; static struct irq_work walt_migration_irq_work; @@ -847,8 +847,7 @@ void fixup_busy_time(struct task_struct *p, int new_cpu) 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: diff --git a/kernel/smpboot.c b/kernel/smpboot.c index 1650578045fcd05c1a05c961332d5eb34a4d31c7..10c5a3a8d638388a3cce63f3a987a2ee80133560 100644 --- a/kernel/smpboot.c +++ b/kernel/smpboot.c @@ -121,7 +121,45 @@ static int smpboot_thread_fn(void *data) } 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/time/hrtimer.c b/kernel/time/hrtimer.c index 38b008e767145ae3bd9258562808d510b70ef01a..11e4af2fc2eeb6e1d8d7831879026c644c107a1e 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -1113,7 +1113,12 @@ static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id, 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 9cff0ab82b635d33d2936697d909724d07767e69..e24008c098c6b8ca149afe58e255a8c1303086d9 100644 --- a/kernel/time/posix-clock.c +++ b/kernel/time/posix-clock.c @@ -300,14 +300,17 @@ static int pc_clock_adjtime(clockid_t id, struct timex *tx) 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_gettime(clockid_t id, struct timespec *ts) 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_getres(clockid_t id, struct timespec *ts) 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 @@ static int pc_clock_settime(clockid_t id, const struct timespec *ts) } 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 @@ static void pc_timer_gettime(struct k_itimer *kit, struct itimerspec *ts) { 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 bad8a6be2b0754fed40ffc5551fb94cbe433dfd5..4776585d785a63e51df1be3580790eff8903e8a9 100644 --- a/kernel/time/sched_clock.c +++ b/kernel/time/sched_clock.c @@ -208,6 +208,11 @@ sched_clock_register(u64 (*read)(void), int bits, unsigned long rate) 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/timer.c b/kernel/time/timer.c index 41481dc0d678666814d48d0b44ac16d30c849e4a..d397432023be0e38df8898e8867722a2dc9f6614 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1902,6 +1902,12 @@ static void __migrate_timers(unsigned int cpu, bool remove_pinned) 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 83aa1f867b97d247554e3dc71a80371a9326acb9..998d730401b35d452c13551413f1582f13670ae5 100644 --- a/kernel/time/timer_list.c +++ b/kernel/time/timer_list.c @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -86,6 +87,9 @@ print_active_timers(struct seq_file *m, struct hrtimer_clock_base *base, 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 @@ print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu) { 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 2884fe01cb5435be69dc122248220436cb2b137d..063dd229bd36c77c2797349d63aed923227b2fda 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -4874,9 +4874,9 @@ static int ftrace_cmp_ips(const void *a, const void *b) 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; diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c index 79bbaf0002eaacd905d70ca7cc66435ee6a4f402..59b482f7eba7971545c817cd06b69f3ab28359cc 100644 --- a/kernel/trace/trace_irqsoff.c +++ b/kernel/trace/trace_irqsoff.c @@ -478,14 +478,11 @@ static inline void tracer_hardirqs_on(void) #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 @@ static inline void tracer_hardirqs_off(void) #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 5ff45cae4ac47a5243429caf4478403988e68da5..ea3ed03fed7e34d0e34eef82ef78deb66b1d647d 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -607,7 +607,7 @@ static int create_trace_kprobe(int argc, char **argv) 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 @@ static int create_trace_kprobe(int argc, char **argv) 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 8c0553d9afd3f2563756641cfa0e659a4ab7ff99..5ea191b917e90997d52619be56ff8bde096076f9 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -319,7 +319,7 @@ static fetch_func_t get_fetch_size_function(const struct fetch_type *type, } /* 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 @@ int traceprobe_split_symbol_offset(char *symbol, unsigned long *offset) 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 0c0ae54d44c616d5d09876165c2516c3e032af77..2b84c0de92c714dfd46569551aa42db6b6534ea7 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -354,7 +354,7 @@ extern int traceprobe_conflict_field_name(const char *name, 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/user.c b/kernel/user.c index b069ccbfb0b0375b5ab03445472f2679d6981bbf..41e94e45383669b420eb5db36135f1b2b8b9f9a0 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -16,6 +16,7 @@ #include #include #include +#include #include /* @@ -201,6 +202,7 @@ struct user_struct *alloc_uid(kuid_t uid) } spin_unlock_irq(&uidhash_lock); } + proc_register_uid(uid); return up; @@ -222,6 +224,7 @@ static int __init uid_cache_init(void) 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 5ef61bdfb664d9b1175ee4a4203754d2395297af..04c2289164c5adb3642ddf20a9d6607aacff61f9 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -4136,6 +4136,22 @@ void workqueue_set_max_active(struct workqueue_struct *wq, int max_active) } 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? * diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 558be538a4433818b5b703c9cf89a83f84b88d6b..079d91ad4e2f4d41ebfde87460f2513a4e7fd3aa 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -834,6 +834,17 @@ config BOOTPARAM_SOFTLOCKUP_PANIC_VALUE 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 056052dc8e911f31f20bd5aff4824c94dd9f5e83..19572a4bbdca25ccc59d6fb561d137426afc5553 100644 --- a/lib/debugobjects.c +++ b/lib/debugobjects.c @@ -293,10 +293,13 @@ static void debug_object_is_on_stack(void *addr, int onstack) 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 86c8911b0e3a6fff02b9e52faa11816cfe508362..5323b59ca3936d653fa9fbf91ef3c05284de299e 100644 --- a/lib/ioremap.c +++ b/lib/ioremap.c @@ -83,7 +83,8 @@ static inline int ioremap_pmd_range(pud_t *pud, unsigned long addr, 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 @@ static inline int ioremap_pud_range(pgd_t *pgd, unsigned long addr, 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/mpi/longlong.h b/lib/mpi/longlong.h index 93336502af089685ce1f6ccbdb159003f1442d70..0f64fcee4ccd1c7b1482919db040d99968d22b7e 100644 --- a/lib/mpi/longlong.h +++ b/lib/mpi/longlong.h @@ -671,7 +671,23 @@ do { \ ************** 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 32d0ad0583806c98df3380001a107dad053f0c66..895961c5338550423844aacfd079ec099197ae61 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -448,8 +448,10 @@ static void *rhashtable_lookup_one(struct rhashtable *ht, 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 0e70ecc12fe20655b5aa0c4b4d7704706c55ec69..8fcad6191ade9ab0211ad7be229bc3013d6088bf 100644 --- a/lib/test_kasan.c +++ b/lib/test_kasan.c @@ -440,6 +440,26 @@ static noinline void __init use_after_scope_test(void) 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 @@ static int __init kmalloc_tests_init(void) 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/cma.c b/mm/cma.c index 5fc18097fcc5bde025b0c795143b91b00b7dfa3d..e97ad01ef148c2982b49d92dc342bcc1154b5c67 100644 --- a/mm/cma.c +++ b/mm/cma.c @@ -438,6 +438,8 @@ struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align) 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,8 +466,15 @@ struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align) 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. @@ -483,6 +492,8 @@ struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align) 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 b4c09eccd9542bd946ff54edb7ebcb1390d32b35..211df83fef6f858ebbc2b3cbbed01e031046098f 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -618,7 +618,7 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask) 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 @@ static int __add_to_page_cache_locked(struct page *page, 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 @@ struct page *pagecache_get_page(struct address_space *mapping, pgoff_t offset, 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 @@ static int page_cache_read(struct file *file, pgoff_t offset, gfp_t gfp_mask) 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 *wait_on_page_read(struct page *page) 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 @@ static struct page *do_read_cache_page(struct address_space *mapping, */ 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 @@ struct page *read_cache_page_gfp(struct address_space *mapping, 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 db77dcb38afda3d3720a228e14236fb2e324f929..375a103d7a56e8d928a69251d45f0e1bd85f4f33 100644 --- a/mm/frame_vector.c +++ b/mm/frame_vector.c @@ -52,6 +52,18 @@ int get_vaddr_frames(unsigned long start, unsigned int nr_frames, 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 c63a0341ae38238820a0a972782331b468704a42..6c3b4e822946f533529852e8bf06abc007918e62 100644 --- a/mm/gup.c +++ b/mm/gup.c @@ -982,6 +982,70 @@ long get_user_pages(unsigned long start, unsigned long nr_pages, } 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 c234c078693c2fd5e41442eb8bd2fc04b080f271..e2982ea26090e8f21782b6cb5b984a89d93dba96 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -2279,11 +2279,13 @@ static unsigned long deferred_split_scan(struct shrinker *shrink, 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 7a6d1a1f9894cd020d7bd90374719c73f1658446..ab47d93a766ff1a1396ed99c464213e128586089 100644 --- a/mm/kasan/kasan.c +++ b/mm/kasan/kasan.c @@ -794,6 +794,55 @@ void __asan_unpoison_stack_memory(const void *addr, size_t size) } 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 1229298cce646ddc725c4f1ea9d823a0c71e3cd1..d9cf9e2cc78222c9bba01b6da1c2e1d1b420f281 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_reduce(void) { } 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 0b8cf43e6f8a1b3d74f4d1ce4089150e60e5b415..5f2b9ebcdbd84619a05a8fe2298d7ae800f02a45 100644 --- a/mm/kasan/report.c +++ b/mm/kasan/report.c @@ -102,6 +102,10 @@ static const char *get_shadow_bug_type(struct kasan_access_info *info) 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 5d7c006373d30580f97d336c1cbb60edc7927735..898eb26f5dc862ec52f4bd570065db6cfe4635d8 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -528,7 +528,12 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma, 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 43622c652fafc27b0665de5bfeac57b03bf2c283..351d94ae483803d52441c507ebcf6a9d9cb3fce4 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -921,6 +921,7 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn, 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 @@ -984,6 +985,13 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn, pr_err("Memory failure: %#lx: failed to unmap page (mapcount=%d)\n", 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 diff --git a/mm/memory.c b/mm/memory.c index f882280f58f7994bafe80ac464cffba6ce3e0bb0..cc6ab384703cdcc09fbf1d4e879c1f7c7735060e 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2842,6 +2842,17 @@ static int __do_fault(struct fault_env *fe, pgoff_t pgoff, 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 @@ static int pte_alloc_one_map(struct fault_env *fe) 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 @@ static int handle_pte_fault(struct fault_env *fe) 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 e6fbdf4a052b9f64bc395837a584150989fbfeb5..ca18dc0c07c3db0a0fd4a3f3e8d66393dcce213c 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -2506,13 +2506,13 @@ void account_page_redirty(struct page *page) 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 @@ void cancel_dirty_page(struct page *page) 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 @@ int clear_page_dirty_for_io(struct page *page) 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 @@ int clear_page_dirty_for_io(struct page *page) * 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 @@ int clear_page_dirty_for_io(struct page *page) 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 d3ea11fd1a711c0da9668c4bd9e6740e98558371..b74f30e2d3716e1c9ac24ed80e885537f97a187d 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2402,8 +2402,9 @@ static void drain_pages(unsigned int cpu) * 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 @@ void drain_all_pages(struct zone *zone) 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/readahead.c b/mm/readahead.c index 7dc48baeb75e8563216b8b95739b28954b3854ae..8b2a7557bfcc10fc066d60d30b944c1adbf5aab4 100644 --- a/mm/readahead.c +++ b/mm/readahead.c @@ -81,7 +81,7 @@ static void read_cache_pages_invalidate_pages(struct address_space *mapping, * 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 0a6ac6a4378ebf17ee46351c7712c128f5f2573c..61a39aa49c5df07a05af2e57b41f045f8a658b5e 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -466,36 +466,45 @@ static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo, 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 1f82d16a051862b20c4e8d28f76289d289c73610..c59844dbd034d7f641dd7b9ee9df697df43b98ab 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -4096,7 +4096,8 @@ static void cache_reap(struct work_struct *w) 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/vmpressure.c b/mm/vmpressure.c index 1306f326620df09b0778ef67a6440fa7741c24a6..e468da694b478fc3aa9ecaf1bad08c248521ed95 100644 --- a/mm/vmpressure.c +++ b/mm/vmpressure.c @@ -423,7 +423,7 @@ static void vmpressure_global(gfp_t gfp, unsigned long scanned, 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 658d62c470723225ec02ae5dfb8d8fe9fef7f513..c5b94d61bb02752636dbecd0295ffe394fd292fd 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -3023,7 +3023,7 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order, 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 +3038,12 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order, * 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 +3827,15 @@ static int __node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask, unsigned in 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 +3845,7 @@ static int __node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask, unsigned in * 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 26f0e4928c78440a4284382e96da49a0b5cf0841..8bd62ed3eb43cb3b5af17f4940aedc52486b8cd7 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -1356,8 +1356,6 @@ static bool is_zone_first_populated(pg_data_t *pgdat, struct zone *zone) return zone == compare; } - /* The zone must be somewhere! */ - WARN_ON_ONCE(1); return false; } @@ -1390,18 +1388,24 @@ static void zoneinfo_show_print(struct seq_file *m, pg_data_t *pgdat, 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 fbfacd51aa3407f3d0289468a073da04fecd742e..fb3e2a50d76e62d977aa80c002d3a13e567eaef3 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -562,8 +563,7 @@ static int vlan_dev_init(struct net_device *dev) 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 @@ static int vlan_ethtool_get_ts_info(struct net_device *dev, { 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 0b8c2558df6e054adc69613218f8ec101d901cc8..78694c42f6319bc6741a922c6d49e01d9d0c5f01 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -295,6 +295,7 @@ config BPF_JIT 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/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index e7f690b571ea9be8ace25843d6e187a907486b99..5419b1214abd8f01809b9fb91d54cde27e437f7c 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -1964,10 +1964,22 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, /* 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 1904a93f47d50a2bd1c2c1651f48b1ab762ece3c..de7b82ece499ca4c3e5e5a36841f3f6969ea940d 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -755,7 +755,8 @@ static void set_ip_addr_bits(u8 addr_type, u8 *addr) } 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 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, 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 int setup_netdev(struct l2cap_chan *chan, struct lowpan_btle_dev **dev) 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 @@ static inline void chan_ready_cb(struct l2cap_chan *chan) 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 4b325250236fd87b3f647bfc40f2e5bc3cb0e3b4..ec313c9dee6dd5b1792978bf2e1abb88fa075893 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -192,6 +192,9 @@ void bt_accept_enqueue(struct sock *parent, struct sock *sk) } 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 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) 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 dc59eae5471788e42091ba7fc907585d36e99c2d..cc061495f6535264870e00dd4177478b290eee13 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -749,18 +749,31 @@ static bool conn_use_rpa(struct hci_conn *conn) } 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 @@ static void hci_req_directed_advertising(struct hci_request *req, 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 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, 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 3ac89e9ace71556c66804b19e8dde8d397eb308f..4bd72d2fe4150e116c95c4014870b37ac020d4c3 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -548,6 +548,7 @@ static void hci_set_event_mask_page_2(struct hci_request *req) { 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 @@ static void hci_set_event_mask_page_2(struct hci_request *req) 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 @@ static void hci_set_event_mask_page_2(struct hci_request *req) 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 e17aacbc563096d539552e1be3e34299098f49e3..d2f9eb169ba80e757f2b5333904bdafa307c25d5 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4646,7 +4646,8 @@ static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, /* 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 @@ static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev, } 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 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, 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 2bbca23a9d050434743b8385b164115ebea2bde3..1fc23cb4a3e03df23e77322561758f1f6d976e4d 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7148,7 +7148,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, 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 658c900752c67a65d939997f1a713ac015c3f950..ead4d1baeaa6a7826402998972455a6abbd3921b 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -2233,8 +2233,14 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) 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_sysfs_if.c b/net/bridge/br_sysfs_if.c index 8bd569695e76fb76112bd5c17fb1a8027c6590a5..abf71111241846e80cfb0ebd51795b07c133dea9 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -230,6 +230,9 @@ static ssize_t brport_show(struct kobject *kobj, 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 9024283d2bca8206d6976bb7bb72523b514bd68d..9adf16258cab4685dc83736ee0fa9bc213780965 100644 --- a/net/bridge/netfilter/ebt_among.c +++ b/net/bridge/netfilter/ebt_among.c @@ -172,18 +172,69 @@ ebt_among_mt(const struct sk_buff *skb, struct xt_action_param *par) 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 f5c11bbe27db651dfd66f6cb8e7dc5b77b88a511..5a89a4ac86ef7c3e8ea864d4f9633df9c56750f7 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -2031,7 +2031,9 @@ static int ebt_size_mwt(struct compat_ebt_entry_mwt *match32, 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 @@ static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base, * * 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/osdmap.c b/net/ceph/osdmap.c index d3f6c26425b360cf8f82f6d8ea766d5b29f8b669..255c0a075e49cb84454a7eea3ed79604f2f0efa9 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -295,6 +295,7 @@ static struct crush_map *crush_decode(void *pbyval, void *end) 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/core/dev.c b/net/core/dev.c index 72fb37cc266a923e2c1b9a9eacb5dae418dd4357..802b3faf9a71192b44c5c6ccd637bb4934fe363c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -995,7 +995,7 @@ bool dev_valid_name(const char *name) { 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 @@ EXPORT_SYMBOL(netif_set_xps_queue); */ 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 @@ int netif_set_real_num_tx_queues(struct net_device *dev, unsigned int txq) 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 @@ __be16 skb_network_protocol(struct sk_buff *skb, int *depth) 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; } @@ -3228,15 +3235,23 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, #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) + return; + map = rcu_dereference_bh(skb->dev->priomap); + if (!map) + return; + sk = skb_to_full_sk(skb); + if (!sk) + return; - if (!skb->priority && skb->sk && map) { - unsigned int prioidx = - sock_cgroup_prioidx(&skb->sk->sk_cgrp_data); + prioidx = sock_cgroup_prioidx(&sk->sk_cgrp_data); - if (prioidx < map->priomap_len) - skb->priority = map->priomap[prioidx]; - } + if (prioidx < map->priomap_len) + skb->priority = map->priomap[prioidx]; } #else #define skb_update_prio(skb) diff --git a/net/core/neighbour.c b/net/core/neighbour.c index eecad95383be87e71cc14639f18630baec0bc44c..cb9a16bcbf38289307ea8fed8f63cd936abf3049 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1139,10 +1139,6 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, 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 +1160,16 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, } } + /* 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) diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index b7efe2f19f8375cc90f2f2f71a16bcbb9bde8b07..04fd04ccaa043c237ab0434cf401d0d42080db3e 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -312,6 +312,25 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns) 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/skbuff.c b/net/core/skbuff.c index 26ef78ad8945bc8ba129980a37d1afb2c707f1c4..e2136eb2291b3d8c4717f2d0828d37ff0b3619f7 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2621,7 +2621,8 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len) { 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 +3235,8 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, 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 +3482,18 @@ void __init skb_init(void) 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 +3512,8 @@ __skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len) 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 +3528,22 @@ __skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len) } 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 +3554,31 @@ __skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len) 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 +3601,11 @@ __skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len) 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 +3743,7 @@ int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb) 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 +3888,8 @@ void __skb_tstamp_tx(struct sk_buff *orig_skb, 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/sysctl_net_core.c b/net/core/sysctl_net_core.c index 1b4619008c4ee734bb79433ef832680c96580efc..546ba76b35a5ab07631fb02bd2c3d5034eab15c3 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -438,8 +438,6 @@ static __net_init int sysctl_core_net_init(struct net *net) { 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/proto.c b/net/dccp/proto.c index 9d43c1f4027408f3a2176767da0dd425938ba652..ff3b058cf58ca5e5d21a8ea620052750bbe8376e 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -789,6 +789,11 @@ int dccp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) 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/hsr/hsr_forward.c b/net/hsr/hsr_forward.c index 4ebe2aa3e7d3e944295e9d53890e3cb9b7a90139..04b5450c5a5572e875f7900a3676fd80259b9b4b 100644 --- a/net/hsr/hsr_forward.c +++ b/net/hsr/hsr_forward.c @@ -324,8 +324,7 @@ static int hsr_fill_frame_info(struct hsr_frame_info *frame, 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 7ea925816f79d8a0e547a0feb1257fda23097ba4..284a9b820df8db51a0dbee9737db5a8127eeaad7 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -158,9 +158,10 @@ struct hsr_node *hsr_add_node(struct list_head *node_db, unsigned char addr[], /* 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 @@ struct hsr_node *hsr_get_node(struct list_head *node_db, struct sk_buff *skb, */ 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 438b40f98f5a986e50180c351d55c9dbb0b66977..4e04f0e868e95044ac27f77ca11d004d928326cd 100644 --- a/net/hsr/hsr_framereg.h +++ b/net/hsr/hsr_framereg.h @@ -18,7 +18,7 @@ struct hsr_node; 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 d7efbf0dad20f8c4d3c42d039eff528c678bc04c..83af5339e582a969539b61b8980170d01d0b9cf5 100644 --- a/net/ieee802154/6lowpan/core.c +++ b/net/ieee802154/6lowpan/core.c @@ -204,9 +204,13 @@ static inline void lowpan_netlink_fini(void) 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 @@ static int lowpan_device_event(struct notifier_block *unused, * 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 e0bd013a1e5ed9fc8fbd16329a877f0231e13271..bf5d26d83af064ef33c31c0e66887be67335f0c4 100644 --- a/net/ieee802154/socket.c +++ b/net/ieee802154/socket.c @@ -304,12 +304,12 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) 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 @@ static int dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) 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 f3c81c286862f270b3156be93f18b344486313ea..34031088cfd8e76c4902e31936ca3878caa80881 100644 --- a/net/ipc_router/ipc_router_core.c +++ b/net/ipc_router/ipc_router_core.c @@ -223,6 +223,25 @@ void msm_ipc_router_set_ws_allowed(bool flag) 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 @@ static void do_read_data(struct work_struct *work) 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 @@ static void do_read_data(struct work_struct *work) 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 @@ void msm_ipc_router_xprt_notify(struct msm_ipc_router_xprt *xprt, { 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 @@ void msm_ipc_router_xprt_notify(struct msm_ipc_router_xprt *xprt, 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 22377c8ff14b72292caa5c2f72898fc60dcfa576..e8f8623585189943defc832f7f4a0851938334a4 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c @@ -220,7 +220,9 @@ static int ah_output(struct xfrm_state *x, struct sk_buff *skb) 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 @@ static int ah_input(struct xfrm_state *x, struct sk_buff *skb) 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 e60517eb1c3a55afc5c7832a7b1f1b3d69e0d7ad..8cae791a7acedb02a4265e0edac1e9eceb71a985 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -437,7 +437,7 @@ static int arp_filter(__be32 sip, __be32 tip, struct net_device *dev) /*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 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb) 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 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb) break; #endif default: + tha = arp_ptr; arp_ptr += dev->addr_len; } memcpy(&tip, arp_ptr, 4); @@ -847,8 +849,18 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb) 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 20fb25e3027bbbf8b8c2068751caf40df939546d..3d8021d553362dbf9eaaadceae932d03f4529c7e 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -268,10 +268,11 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb) 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 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb) } 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 38c1c979ecb1ffd4b066f0ed956858aa4445429e..e1be24416c0e569989927a07e15a58b3c5554b70 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -640,6 +640,11 @@ int fib_nh_match(struct fib_config *cfg, struct fib_info *fi) 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 @@ void fib_select_multipath(struct fib_result *res, int hash) 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 631c0d0d7cf8ebd3473fbe2409e4ee0fc6094a9e..8effac0f221948742bf5ba35ca49e38dc383b140 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -119,6 +119,9 @@ static void inet_frag_secret_rebuild(struct inet_frags *f) 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/ip_sockglue.c b/net/ipv4/ip_sockglue.c index bf62fa48726208b5474bfd604c19ed33d3366191..5ddd64995e7387ebc00a80f6b8f52a354d5bbdaa 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -242,7 +242,8 @@ int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc, 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 @@ int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc, 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 @@ int ip_getsockopt(struct sock *sk, int level, 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 @@ int compat_ip_getsockopt(struct sock *sk, int level, int optname, 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 96536a0d6e2db193083e3a07647e88b4c18466e7..e1271e75e107177892870728dbd1434cccd11c08 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -253,13 +253,14 @@ static struct net_device *__ip_tunnel_create(struct net *net, struct net_device *dev; char name[IFNAMSIZ]; - if (parms->name[0]) + 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)) { - err = -E2BIG; + } 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 27089f5ebbb1c1c6e13038a94aeee5209e03d532..742a3432c3ead31c838c47b90fbea2506aa6b420 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1929,6 +1929,20 @@ int ip_mr_input(struct sk_buff *skb) 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 @@ int ip_mr_input(struct sk_buff *skb) /* 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 @@ int ip_mr_input(struct sk_buff *skb) } 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 697538464e6e31f237d4a909174fe6a5fb267367..d35815e5967b567356fa5db1f86f1be9ad30ac03 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -261,6 +261,10 @@ unsigned int arpt_do_table(struct sk_buff *skb, } 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 check_target(struct arpt_entry *e, const char *name) } 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 @@ find_check_entry(struct arpt_entry *e, const char *name, unsigned int size) 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 @@ static inline void cleanup_entry(struct arpt_entry *e) 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 inline void cleanup_entry(struct arpt_entry *e) 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 @@ static int translate_table(struct xt_table_info *newinfo, void *entry0, /* 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 7c00ce90adb8496ed9fb97894163f783b158e7cf..e78f6521823f748be76ecd2eaed2b4e5a86c6407 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -345,8 +345,13 @@ ipt_do_table(struct sk_buff *skb, 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 check_target(struct ipt_entry *e, struct net *net, const char *name) 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 @@ find_check_entry(struct ipt_entry *e, struct net *net, const char *name, 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 @@ find_check_entry(struct ipt_entry *e, struct net *net, const char *name, cleanup_match(ematch, net); } - xt_percpu_counter_free(e->counters.pcnt); + xt_percpu_counter_free(&e->counters); return ret; } @@ -674,7 +677,7 @@ cleanup_entry(struct ipt_entry *e, struct net *net) 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 @@ static int 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 @@ translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0, /* 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 574f7ebba0b6238d8e61ffd08dead06a07a619c5..ac8342dcb55eb51680d1f4559d57c2e99509b505 100644 --- a/net/ipv4/netfilter/nf_nat_h323.c +++ b/net/ipv4/netfilter/nf_nat_h323.c @@ -252,16 +252,16 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, 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 @@ static int nat_h245(struct sk_buff *skb, struct nf_conn *ct, /* 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 @@ static int nat_q931(struct sk_buff *skb, struct nf_conn *ct, /* 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 @@ static int nat_callforwarding(struct sk_buff *skb, struct nf_conn *ct, } /* 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/route.c b/net/ipv4/route.c index 03728c65b83d4fa945fdbdfb9d7d3902d055ef32..e83fdf65663ba70c6fd1e49d8c557f9afba1d222 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -126,10 +126,13 @@ static int ip_rt_redirect_silence __read_mostly = ((HZ / 50) << (9 + 1)); 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 @@ static struct ctl_table ipv4_route_table[] = { .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_input.c b/net/ipv4/tcp_input.c index 8ce03d85da1063da4a5c54872710b1fb4459c42b..16d3619bddc99fe62ea4a63ee92ac8e5de7d7b4a 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -116,6 +116,7 @@ int sysctl_tcp_default_init_rwnd __read_mostly = TCP_INIT_CWND * 2; #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 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) 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; @@ -5608,10 +5610,6 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb) 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 +5678,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, 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 +5782,15 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, 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 +5973,17 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb) /* 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 +6053,6 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb) * 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 20634eaccf82332c1c52c408997d9887c1ff4968..16a473aeba4346c9c0456b114981d98b4b7de73d 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1580,7 +1580,7 @@ u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now, */ 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 @@ static u32 tcp_tso_segs(struct sock *sk, unsigned int mss_now) 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 inline void tcp_mtu_check_reprobe(struct sock *sk) } } +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 @@ static int tcp_mtu_probe(struct sock *sk) 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 @@ static int tcp_mtu_probe(struct sock *sk) /* 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 { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 1589e6ae929e30e40c7ab39de6c304baedf41639..5af27b9e71671a61aa7f6772fb059dd707b49c81 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1723,6 +1723,11 @@ static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh, 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 a5c1ff3d43d7b4df8b8bbb062784069d7bbad7e4..9077060f936896469cb7391699fd1448a12b75fa 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -994,7 +994,10 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, 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 189eb10b742d02fa5b39ac7206703e31e30c3cf7..e742c4deb13d79a3b4ca7e9c009b57f978f8efe0 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -423,7 +423,9 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) 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 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) 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 cbcdd5db31f473f75011c2346345dd752c9a7424..44a2010e2076850d45fdaef2889dbe41f0690919 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -248,9 +248,11 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb) 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 @@ static int esp6_input(struct xfrm_state *x, struct sk_buff *skb) } 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 c0cbcb259f5a9acf9157d4626c61f532d8077855..1dc023ca98fd4a9e8ed71f8e7e8c146bc50927a4 100644 --- a/net/ipv6/ip6_checksum.c +++ b/net/ipv6/ip6_checksum.c @@ -72,6 +72,11 @@ int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto) 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 21bd54fd9ba7fe372540ea47aa7f8edd460f98d6..a88aff02b579be88796edc318d413daf12a828ff 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -319,11 +319,13 @@ static struct ip6_tnl *ip6gre_tunnel_locate(struct net *net, 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 55ca65c7d5e5b3f01eab67b32cffb9c0595c35c5..de0188e05dab61dfe7d524b620e6652485b4de5a 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -365,6 +365,11 @@ static int ip6_forward_proxy_check(struct sk_buff *skb) 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 @@ int ip6_forward(struct sk_buff *skb) 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 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork, 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 @@ static int __ip6_append_data(struct sock *sk, 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 @@ static int __ip6_append_data(struct sock *sk, 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 @@ static int __ip6_append_data(struct sock *sk, 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; } diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 4c522367895c3d7b545e9213c121210dfae30bbd..f338848a70bec84ca9f406924fb3487251baeae7 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -298,13 +298,16 @@ static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p) 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 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield, 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 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield, 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 afc30a01cc19f4d0afe639dd4de41948171afe9b..5ae168135e318a61341ff252c454aee6945c4249 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -212,10 +212,13 @@ static struct ip6_tnl *vti6_tnl_create(struct net *net, struct __ip6_tnl_parm *p 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 493a32f6a5f27a94397f125a81a141a8cd97762f..c66b9a87e995501875e565b823553c07439e6aaa 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -1343,10 +1343,7 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, 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 @@ int compat_ipv6_getsockopt(struct sock *sk, int level, int optname, 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 01858acc4ce7691ede4f72420e7168bff357b90e..52236be017d2b4e0dc51d68b66ff8c50cf6e8f39 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1518,7 +1518,8 @@ static void ndisc_fill_redirect_hdr_option(struct sk_buff *skb, *(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 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, 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 1cee1935b089a6bca0a7da5387ce60307983422b..aa828580968a7b7718d8cf4644f59c32f734db9d 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -385,6 +385,10 @@ ip6t_do_table(struct sk_buff *skb, } 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 check_target(struct ip6t_entry *e, struct net *net, const char *name) 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 @@ find_check_entry(struct ip6t_entry *e, struct net *net, const char *name, 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 @@ find_check_entry(struct ip6t_entry *e, struct net *net, const char *name, cleanup_match(ematch, net); } - xt_percpu_counter_free(e->counters.pcnt); + xt_percpu_counter_free(&e->counters); return ret; } @@ -712,8 +714,7 @@ static void cleanup_entry(struct ip6t_entry *e, struct net *net) 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 @@ static int 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 @@ translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0, /* 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 e0be97e636a48f54c1488ca70ae97a9a13e8be61..322c7657165b415f288d8a9505330c116bbf1ce5 100644 --- a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c @@ -99,6 +99,10 @@ static bool nf_nat_ipv6_manip_pkt(struct sk_buff *skb, !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 558d566e96680f1e3477870be240e64f314f93b2..96be019eed008b16a28f62f4343a298fe5cff940 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -847,6 +847,9 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net, 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 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, } 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; diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index db6d437002a6733c93a60e91eb52658c80d612c3..dcb292134c21cb55ac9323e98060bb26752466f2 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -176,7 +176,7 @@ static void ipip6_tunnel_clone_6rd(struct net_device *dev, struct sit_net *sitn) #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 @@ static struct ip_tunnel *ipip6_tunnel_locate(struct net *net, 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 @@ static int ipip6_rcv(struct sk_buff *skb) 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 91cbbf1c3f82daff512009be723ed507bf19c9d2..c2dfc32eb9f21a2b15898f166ab13a297e989dea 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -2418,9 +2418,11 @@ static int afiucv_iucv_init(void) 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 179cd9b1b1f4672a0d7ac57c2870bd7ef0cd5bf4..63e6d08388ab7bf78f56fe4eae58d55894f62d17 100644 --- a/net/kcm/kcmsock.c +++ b/net/kcm/kcmsock.c @@ -1375,24 +1375,32 @@ static int kcm_attach(struct socket *sock, struct socket *csock, 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 @@ static int kcm_attach(struct socket *sock, struct socket *csock, 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); @@ -1418,7 +1426,8 @@ static int kcm_attach(struct socket *sock, struct socket *csock, write_unlock_bh(&csk->sk_callback_lock); 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 +1463,10 @@ static int kcm_attach(struct socket *sock, struct socket *csock, /* 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 +1518,7 @@ static void kcm_unattach(struct kcm_psock *psock) 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 6482b001f19a25355eac827fd6d8ddeae7617d29..15150b412930bb1dc1c8614d8aafc851cdd2f97f 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -3305,7 +3305,7 @@ static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt, 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 cfc4dd8997e54c898ae71ca99030eb507403ad1c..ead98e8e0b1f54c5a70d86fb0a3fa07c396a63bb 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1612,9 +1612,14 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 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 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 } 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_netlink.c b/net/l2tp/l2tp_netlink.c index ee03bc866d1b7141425453dd928e32be18b32527..ce1238492c0fbbff826ebf2238c9eaf9e4484fff 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -750,6 +750,8 @@ static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, int fl if ((session->ifname[0] && nla_put_string(skb, L2TP_ATTR_IFNAME, session->ifname)) || + (session->offset && + nla_put_u16(skb, L2TP_ATTR_OFFSET, session->offset)) || (session->cookie_len && nla_put(skb, L2TP_ATTR_COOKIE, session->cookie_len, &session->cookie[0])) || diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index db916cf51ffeabd3d5246cb09a9e7227d483f574..f7caf0f5d9c81807b68a86903f595244d2148cca 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -309,6 +309,8 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen) 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 +382,7 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen) out_put: llc_sap_put(sap); out: + release_sock(sk); return rc; } diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 07001b6d36cc3771ccc1e75f1cf8f76b23013ec2..d7801f6877af1c4aa9ec5c1e40457fa8e6144e4e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -620,10 +620,11 @@ void sta_set_rate_info_tx(struct sta_info *sta, 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 @@ static int sta_apply_parameters(struct ieee80211_local *local, 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 @@ static int sta_apply_parameters(struct ieee80211_local *local, 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 @@ static int sta_apply_parameters(struct ieee80211_local *local, /* 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 @@ static int ieee80211_change_bss(struct wiphy *wiphy, 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 @@ static int ieee80211_change_bss(struct wiphy *wiphy, } 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 @@ static int ieee80211_change_bss(struct wiphy *wiphy, 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 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy, 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 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy, 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 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon) } 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 09f77e4a8a79d78f5722a85470bfa0862671e43e..49c8a9c9b91f4078fac9b0191122874edef4f28c 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -164,7 +164,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, 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 62d13eabe17f6b917306f893465a324884060348..a5acaf1efaabce9a623a396e4024d56481d3d048 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -427,7 +427,7 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, 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 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, 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 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, 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 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, 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 03dbc6bd859834b93cb332b90550df18a3e74d7c..7fd544d970d91d302c2739b9db9b99b20084261e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -991,21 +991,6 @@ sdata_assert_lock(struct ieee80211_sub_if_data *sdata) 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 @@ IEEE80211_WDEV_TO_SUB_IF(struct wireless_dev *wdev) 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 a7aa54f45e19090bb6eac81b7e89f584d2df7741..fa7d757fef95afa6266f5ceb2ecd6fa331f4df41 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1520,7 +1520,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, 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 b4b3fe07886891618d96bc997775554a5621b68c..b2a27263d6ffd655c6c56f05b22063dea1a93dbd 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -63,6 +63,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, 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 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, (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 @@ static int mesh_add_ds_params_ie(struct ieee80211_sub_if_data *sdata, 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_ht_oper_ie(struct ieee80211_sub_if_data *sdata, 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 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, 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 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, 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 fcba70e57073f372793d69d247e78ea11f8c2597..2d3c4695e75b98aaf34662514fe2938a853eeb7f 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -93,19 +93,23 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta) 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 @@ static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata) 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 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, 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 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, 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 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, 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 b2c706cc5f4bef068a48724332d72bb91025258e..973adf30c41278dfd256ef5a6fc7e5812893ebe9 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1851,11 +1851,16 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, 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 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, } 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 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, 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 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, 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 206698bc93f406939bb5d883b6ab2f04bc1a3bed..e6096dfd02105223e100898bc71d14bc8d774e4f 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -173,9 +173,11 @@ ieee80211_rate_control_ops_get(const char *name) /* 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 @@ int rate_control_set_rates(struct ieee80211_hw *hw, 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 439e597fd3742f070691640641fe7966539fa525..404284a14d75449f0d84d84838033a9faa124702 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3611,6 +3611,8 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) } 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 348700b424eafde9b7651307d2ea0e3ed27fe117..1ecf3d07d1f514842d1959f32c3b5eb167c59ce8 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -395,10 +395,15 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, 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 ad37b4e58c2f864a973eca29bc8b78667a1079ac..72fe9bc7a1f96d429acb7dc4b2a83dbb77a28dcb 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -200,6 +200,7 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) } 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 afca7d103684bbc8c953ae13bb0345934d8f5f35..f20dcf1b1830a2412ed96ebcc9ce3be65ef5d7e8 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -47,8 +47,7 @@ static void ieee80211_tdls_add_ext_capab(struct ieee80211_sub_if_data *sdata, 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 void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb) 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 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, 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 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, * 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 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, 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_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, 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 1ffd1e145c13aa0e227390fd4687f578ccbd6723..84582998f65f4dc51e2f8e83b98bbd3912ee29d6 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -4182,7 +4182,10 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, 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 031273a61d273291886e4db2070e833b480ee8f6..ae91a3ccefc9f4d0cd99668f5daf37f639893db1 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1590,14 +1590,14 @@ u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata, 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 c5a5a6959c1b878cc3935a0bdd470ab71b4f68a8..ffab94d61e1da4c123f3dc437e09bdc4838c75a1 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -756,6 +757,22 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg, 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 @@ static int mpls_route_add(struct mpls_route_config *cfg) 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 @@ static int mpls_route_del(struct mpls_route_config *cfg) 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 @@ static int rtm_to_route_config(struct sk_buff *skb, struct nlmsghdr *nlh, &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 2155c2498aed637c893da6cd3218db2e01e6fb66..74d119512d966de535f6f2d1b6d930d77be4e185 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -3092,6 +3092,17 @@ static int ip_vs_genl_dump_services(struct sk_buff *skb, 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 +3129,7 @@ static int ip_vs_genl_parse_service(struct netns_ipvs *ipvs, 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) { @@ -3624,6 +3631,11 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) 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/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index fa4d1ecd9d35a599a71a6fc9aa5d237000ce8664..18e96a2696c3dbdd93b1bd7b5b801d04915ba3c8 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1621,7 +1621,6 @@ get_next_corpse(struct net *net, int (*iter)(struct nf_conn *i, void *data), 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,24 +1642,40 @@ get_next_corpse(struct net *net, int (*iter)(struct nf_conn *i, void *data), cond_resched(); } + return NULL; +found: + atomic_inc(&ct->ct_general.use); + spin_unlock(lockp); + local_bh_enable(); + return ct; +} + +static void +__nf_ct_unconfirmed_destroy(struct net *net) +{ + int cpu; + for_each_possible_cpu(cpu) { - struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, 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); - if (iter(ct, data)) - set_bit(IPS_DYING_BIT, &ct->status); + + /* 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(); } - return NULL; -found: - atomic_inc(&ct->ct_general.use); - spin_unlock(lockp); - local_bh_enable(); - return ct; } void nf_ct_iterate_cleanup(struct net *net, @@ -1675,6 +1690,10 @@ void nf_ct_iterate_cleanup(struct net *net, 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 @@ int nf_conntrack_hash_resize(unsigned int hashsize) 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 6dc44d9b41900bea12f487e5a044259e92a47f7e..92e69af41656f9c8ec2fda61b497c7341ec0cb1e 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -379,17 +379,33 @@ int nf_conntrack_helper_register(struct nf_conntrack_helper *me) 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 3c3725332ea9e48100af812e0ca3b92845766546..c9ca5293205ef294c2c8afda6dcf101c5ab872e6 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -894,8 +894,13 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb) } 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 const struct nla_policy tuple_nla_policy[CTA_TUPLE_MAX+1] = { 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 struct nfnl_ct_hook ctnetlink_glue_hook = { 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 e84a578dbe351da3122732cd2581a0e9322931a3..d76afafdc6993ba2f8e4a03f377b8cad3a34c145 100644 --- a/net/netfilter/nf_nat_ftp.c +++ b/net/netfilter/nf_nat_ftp.c @@ -134,7 +134,7 @@ static int __init nf_nat_ftp_init(void) } /* 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 1fb2258c35357c8c6f5072ae6593de2d53ab9f03..8039bcd607581c9b047ccd991f155e9a045d5384 100644 --- a/net/netfilter/nf_nat_irc.c +++ b/net/netfilter/nf_nat_irc.c @@ -107,7 +107,7 @@ static int __init nf_nat_irc_init(void) } /* 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 fbce552a796e14a6249e6e9a5d0d2ea16aaf3f09..7d7466dbf66338f817bb6698b9dbd637de26d3ed 100644 --- a/net/netfilter/nf_nat_proto_common.c +++ b/net/netfilter/nf_nat_proto_common.c @@ -41,7 +41,7 @@ void nf_nat_l4proto_unique_tuple(const struct nf_nat_l3proto *l3proto, 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 @@ void nf_nat_l4proto_unique_tuple(const struct nf_nat_l3proto *l3proto, } } 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/nft_dynset.c b/net/netfilter/nft_dynset.c index 31ca94793aa9622f4a77ab852c3a8c1a422ba658..b9dd4e96042616c5e61f797a0b7da09e856180df 100644 --- a/net/netfilter/nft_dynset.c +++ b/net/netfilter/nft_dynset.c @@ -82,8 +82,7 @@ static void nft_dynset_eval(const struct nft_expr *expr, 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 @@ static void nft_dynset_eval(const struct nft_expr *expr, 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 e47ade305a460d43e1ea359d508e550c24862d89..59be89813a29069bdac46acaffd7ae638fdee53c 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -39,6 +39,8 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Harald Welte "); 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 @@ textify_hooks(char *buf, size_t size, unsigned int mask, uint8_t nfproto) 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 @@ struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af, 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 @@ void xt_proto_fini(struct net *net, u_int8_t af) } 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 6669e68d589ee07782c54e9ef36a4a386dc348b1..00ef8243d48d808b000f0065f607ebd2110044d8 100644 --- a/net/netfilter/xt_CT.c +++ b/net/netfilter/xt_CT.c @@ -168,8 +168,10 @@ xt_ct_set_timeout(struct nf_conn *ct, const struct xt_tgchk_param *par, 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 @@ static int xt_ct_tg_check(const struct xt_tgchk_param *par, 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 @@ static int xt_ct_tg_check(const struct xt_tgchk_param *par, 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 @@ static int xt_ct_tg_check(const struct xt_tgchk_param *par, 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 14e3d858e04c1092cfff1865c40294e44da4919c..3d871c4452fe3440055044bd469c9169f220fdab 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -331,11 +331,11 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) 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 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) 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 3ba31c194ccec3101aa0979bb18a693d30d1c68b..0858fe17e14a22a0b4dde95fc8d02aeb6fda8117 100644 --- a/net/netfilter/xt_LED.c +++ b/net/netfilter/xt_LED.c @@ -141,10 +141,11 @@ static int led_tg_check(const struct xt_tgchk_param *par) 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 @@ static void led_tg_destroy(const struct xt_tgdtor_param *par) 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 dffee9d47ec4b3333271ac0dd54a306cf9943e3b..a8df0a9d68e65af3da5cde4138c4c6bc282e234e 100644 --- a/net/netfilter/xt_bpf.c +++ b/net/netfilter/xt_bpf.c @@ -8,8 +8,10 @@ */ #include +#include #include #include +#include #include #include @@ -20,15 +22,18 @@ MODULE_LICENSE("GPL"); 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 @@ static int bpf_mt_check(const struct xt_mtchk_param *par) 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 @@ static bool bpf_mt(const struct sk_buff *skb, struct xt_action_param *par) 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 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, +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 b89b688e9d01a2d14071563bb3e823b62dceeb17..a1a29cdc58fc0c86a05caefd483f4dfeac7bf1c3 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -794,8 +794,9 @@ static int hashlimit_mt_check_v1(const struct xt_mtchk_param *par) 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_v1(const struct xt_mtchk_param *par) 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 e3b7a09b103e490a2c85364d8938b47565b9c66c..79d7ad621a80fad3c3dc4d74df9c78c264472b18 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -361,9 +361,9 @@ static int recent_mt_check(const struct xt_mtchk_param *par, 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 e1c123d4cddaa86086d5ce7ff27679543e9b663b..1e97b8d9a159b43840484566cfff6c9913a60dfd 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1054,6 +1054,9 @@ static int netlink_connect(struct socket *sock, struct sockaddr *addr, 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; @@ -2258,7 +2261,7 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb, if (cb->start) { ret = cb->start(cb); if (ret) - goto error_unlock; + goto error_put; } nlk->cb_running = true; @@ -2278,6 +2281,8 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb, */ 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 b09d4753f66d808be2caf82f7a39fefdf0db48da..a1fae01de6b28654dc3028ecea0e6180239b40b6 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -1103,6 +1103,7 @@ static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group, { struct sk_buff *tmp; struct net *net, *prev = NULL; + bool delivered = false; int err; for_each_net_rcu(net) { @@ -1114,14 +1115,21 @@ static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group, } 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 b28e45b691de934cd2a7cbb8cf3f6e0893b9649a..466393936db9492c99c41748bf0c1aa1a20a35f6 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -396,10 +396,38 @@ ovs_ct_expect_find(struct net *net, const struct nf_conntrack_zone *zone, 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/rds/bind.c b/net/rds/bind.c index 095f6ce583fee33eee431dc88e04a26c01db2d4d..adb53ae97a0253ed9a15cb571d2ca291d8debf67 100644 --- a/net/rds/bind.c +++ b/net/rds/bind.c @@ -114,6 +114,7 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port) 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 ef53d164e146b943e4093f8b17bb2b4747ad2873..50241d30e16de759f32fb8a0adfea0a4e915da50 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 @@ static int rds_send_mprds_hash(struct rds_sock *rs, struct rds_connection *conn) 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/rxrpc/output.c b/net/rxrpc/output.c index 5dab1ff3a6c2d89d7e6c3b6ee53e6c8a97bc785d..59d32860331205a5b12e2c711250f478124bdd1c 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -391,7 +391,7 @@ int rxrpc_send_data_packet(struct rxrpc_call *call, struct sk_buff *skb, (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 4374e7b9c7bff9fdd1d36a5a9fc8f135414ed360..90df95e18ea491b8bc90a15806c2b2b811578639 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -229,7 +229,9 @@ static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call, 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 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, struct sk_buff *trailer; u32 data_size, buf; u16 check; - int nsg; + int nsg, ret; _enter(""); @@ -342,7 +344,9 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, 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 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, struct sk_buff *trailer; u32 data_size, buf; u16 check; - int nsg; + int nsg, ret; _enter(",{%d}", skb->len); @@ -429,7 +433,12 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, } 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 f3117324146aac4bf8c91b8273514bc03fae5f64..67adb4ecded2c3fca60c8960c9dcf890d620f72f 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -95,8 +95,10 @@ static int tcf_dump_walker(struct tcf_hashinfo *hinfo, struct sk_buff *skb, 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 1d3960033f61d0f72d5039ba31ff1c8dc91d7cba..40496f34bdb3fc7ba6ab09149c65181e7c05e4b5 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c @@ -245,10 +245,14 @@ static int tcf_bpf_init_from_efd(struct nlattr **tb, struct tcf_bpf_cfg *cfg) 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 e0defcef376d872c123b7d23f81a3b1eed207c02..24a0c66394a098dee04412545e4f36b84124e9c4 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -180,6 +180,9 @@ static int tcf_csum_ipv4_tcp(struct sk_buff *skb, unsigned int ihl, 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 @@ static int tcf_csum_ipv6_tcp(struct sk_buff *skb, unsigned int ihl, 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 @@ static int tcf_csum_ipv4_udp(struct sk_buff *skb, unsigned int ihl, 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 @@ static int tcf_csum_ipv6_udp(struct sk_buff *skb, unsigned int ihl, 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_skbmod.c b/net/sched/act_skbmod.c index f85313d60a4d9b77ed11bff0cb4043e27d96bdcc..bd8e86cb87432637c03fccf5fb17f8d7d0476117 100644 --- a/net/sched/act_skbmod.c +++ b/net/sched/act_skbmod.c @@ -192,7 +192,8 @@ static void tcf_skbmod_cleanup(struct tc_action *a, int bind) 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 af47bdf2f4831541fce2e2adc3d7b14eab9bcf19..901fb8bb9dcecacef554d3d37c8465effdf05c8c 100644 --- a/net/sched/act_tunnel_key.c +++ b/net/sched/act_tunnel_key.c @@ -141,6 +141,7 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX; break; default: + ret = -EINVAL; goto err_out; } @@ -195,11 +196,12 @@ static void tunnel_key_release(struct tc_action *a, int bind) 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_netem.c b/net/sched/sch_netem.c index 9f7b380cf0a3836aca117b6d9bde05e0fecea32b..e899d9eb76cbbb24505465c2eaa94f5e3d3410e1 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -462,7 +462,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, /* 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 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, } 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 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, now = psched_get_time(); if (q->rate) { - struct sk_buff *last; + struct netem_skb_cb *last = NULL; + + 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 (sch->q.qlen) - last = sch->q.tail; - else - last = netem_rb_to_skb(rb_last(&q->t_root)); 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/ipv6.c b/net/sctp/ipv6.c index 5d015270e454b3c673240902c43aa2c9fe961dd6..355d95a7cd81058bff126ec4895ec760921767a3 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -324,8 +324,10 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, 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 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, } 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); @@ -723,8 +727,10 @@ static int sctp_v6_addr_to_user(struct sctp_sock *sp, union sctp_addr *addr) 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); } diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 7b523e3f551f1761d891b606f293b2b721a87ca8..fb7b7632316a5fc826f6d913a80c0cab4f12f7d5 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -510,22 +510,20 @@ static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr, 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 9e9690b7afe118636eb64751f9c58637be56c5e8..fc67d356b5fa009a81a8ce123b0c20c9baac032f 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1373,9 +1373,14 @@ static struct sctp_chunk *_sctp_make_chunk(const struct sctp_association *asoc, 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/socket.c b/net/sctp/socket.c index fd5b9d573b382e33747bdab39aa47b3f89b674ee..78f38056fca6dc0cb32ad050940ace584735e07f 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -335,11 +335,14 @@ static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt, 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 @@ static void sctp_close(struct sock *sk, long timeout) 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 @@ static void sctp_close(struct sock *sk, long timeout) * 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 @@ static int sctp_getsockopt_autoclose(struct sock *sk, int len, char __user *optv 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 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, 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 @@ static int sctp_getsockopt_maxseg(struct sock *sk, int len, 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 @@ static int sctp_getsockopt_active_key(struct sock *sk, int len, 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 @@ static int sctp_getsockopt_active_key(struct sock *sk, int len, 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 @@ static int sctp_getsockopt_peer_auth_chunks(struct sock *sk, int len, 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 @@ static int sctp_getsockopt_local_auth_chunks(struct sock *sk, int len, 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/strparser/strparser.c b/net/strparser/strparser.c index b5c279b2268017e502f2028355874b62208dc3a1..6cbc935ddd96457247668dad81296dbd5b3c95c7 100644 --- a/net/strparser/strparser.c +++ b/net/strparser/strparser.c @@ -59,7 +59,7 @@ static void strp_abort_rx_strp(struct strparser *strp, int err) strp->rx_stopped = 1; /* Report an error on the lower socket */ - csk->sk_err = err; + csk->sk_err = -err; csk->sk_error_report(csk); } @@ -422,7 +422,7 @@ static void strp_rx_msg_timeout(unsigned long arg) /* 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 79aec90259cdd599c811a3fe5ed6823d02d7d98e..4afd4149a632d357694953111281bb2027e5de9c 100644 --- a/net/sunrpc/auth_gss/gss_krb5_crypto.c +++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c @@ -237,9 +237,6 @@ make_checksum_hmac_md5(struct krb5_ctx *kctx, char *header, int hdrlen, 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 61a504fb1ae2556c02c831b37ba9e3f598530426..34f94052c519dcfbb36ae700319f3a5a107b7598 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -1375,6 +1375,7 @@ rpc_gssd_dummy_depopulate(struct dentry *pipe_dentry) 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 272c345519799fd5f3a572246b198b33fed3dc3f..b43818c720b8793b3f994e143bacaec0a34c027a 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -50,7 +50,7 @@ EXPORT_SYMBOL_GPL(svc_pool_map); 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 @@ param_set_pool_mode(const char *val, struct kernel_param *kp) } 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 69502fa68a3ca8681d58b73d9d903662f21c64cf..1a2b1f61ed26198b0fdab5549c695597ab69ed03 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -1054,6 +1054,7 @@ void 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 d24d14ea8ba417fc979cd9b277418424b3e902cc..1bf9153004cdea1b3454b763ed6fff6017f701ea 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2384,7 +2384,12 @@ static void xs_tcp_setup_socket(struct work_struct *work) 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 52d74760fb68697d508fafb213b56deaed2e8639..ca68db207965142cdd21c35c8508477848c04f7c 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -322,6 +322,7 @@ static int tipc_enable_bearer(struct net *net, const char *name, if (res) { pr_warn("Bearer <%s> rejected, enable failure (%d)\n", name, -res); + kfree(b); return -EINVAL; } @@ -345,8 +346,10 @@ static int tipc_enable_bearer(struct net *net, const char *name, 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 9e109bb1a2071836323d41b77e903644b28c7b73..0fcfb3916dcf2f830f9f8add1bbda33968ca3a19 100644 --- a/net/tipc/monitor.c +++ b/net/tipc/monitor.c @@ -633,9 +633,13 @@ void tipc_mon_delete(struct net *net, int bearer_id) { 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/node.c b/net/tipc/node.c index 5b3e1ea37b6dd64502d2a6cee8eb2f2105139520..db8fbc076e1a5e893a967be5fa27f450a6c69497 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -2094,6 +2094,8 @@ int tipc_nl_node_get_monitor(struct sk_buff *skb, struct genl_info *info) 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/db.txt b/net/wireless/db.txt index eb4cc00e38e0c833610d6eb19c3f1a4cb19c573c..30cc249bb98e966da239e75d154bac0226b77f47 100644 --- a/net/wireless/db.txt +++ b/net/wireless/db.txt @@ -40,6 +40,7 @@ country AL: DFS-ETSI (5150 - 5250 @ 80), (23), AUTO-BW (5250 - 5350 @ 80), (23), DFS, AUTO-BW (5470 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country AM: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -51,6 +52,7 @@ country AN: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country AR: (2402 - 2482 @ 40), (36) @@ -74,15 +76,7 @@ country AT: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -112,6 +106,7 @@ country BA: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country BB: DFS-FCC (2402 - 2482 @ 40), (20) @@ -128,15 +123,7 @@ country BE: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -152,15 +139,7 @@ country BG: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -250,15 +229,7 @@ country CH: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -312,15 +283,7 @@ country CY: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -331,15 +294,7 @@ country CZ: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -354,15 +309,7 @@ country DE: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -371,15 +318,7 @@ country DK: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -415,15 +354,7 @@ country EE: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -437,15 +368,7 @@ country ES: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -460,15 +383,7 @@ country FI: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -484,15 +399,7 @@ country FR: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -501,15 +408,7 @@ country GB: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -530,6 +429,7 @@ country GF: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country GH: DFS-FCC (2402 - 2482 @ 40), (20) @@ -561,15 +461,7 @@ country GR: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -614,15 +506,7 @@ country HR: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -638,15 +522,7 @@ country HU: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -660,15 +536,7 @@ country IE: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -695,15 +563,7 @@ country IS: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -712,15 +572,7 @@ country IT: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -811,15 +663,7 @@ country LI: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -841,15 +685,7 @@ country LT: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -858,15 +694,7 @@ country LU: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -875,15 +703,7 @@ country LV: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -899,18 +719,21 @@ country MC: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 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 + (5725 - 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 + (5725 - 5875 @ 80), (14) country MF: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -930,6 +753,7 @@ country MK: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country MN: DFS-FCC (2402 - 2482 @ 40), (20) @@ -957,6 +781,7 @@ country MQ: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country MR: DFS-ETSI (2402 - 2482 @ 40), (20) @@ -969,15 +794,7 @@ country MT: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -1044,15 +861,7 @@ country NL: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -1061,15 +870,7 @@ country NO: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -1111,6 +912,7 @@ country PF: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country PG: DFS-FCC (2402 - 2482 @ 40), (20) @@ -1137,15 +939,7 @@ country PL: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -1154,6 +948,7 @@ country PM: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country PR: DFS-FCC (2402 - 2472 @ 40), (30) @@ -1174,15 +969,7 @@ country PT: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -1220,15 +1007,7 @@ country RO: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -1239,6 +1018,7 @@ country RS: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -1268,15 +1048,7 @@ country SE: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -1294,15 +1066,7 @@ country SI: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -1311,15 +1075,7 @@ country SK: DFS-ETSI (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) + (5725 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -1379,6 +1135,7 @@ country TR: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country TT: (2402 - 2482 @ 40), (20) @@ -1430,18 +1187,6 @@ country US: DFS-FCC (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) @@ -1465,6 +1210,7 @@ country VC: DFS-ETSI (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5725 - 5875 @ 80), (14) country VE: DFS-FCC (2402 - 2482 @ 40), (20) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index febd5ab38bd4d3afa57983b31360c43bae01b346..976cba3bd67271204e251e16710b98bffca204f5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4133,7 +4133,7 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, 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 @@ static int nl80211_nan_add_func(struct sk_buff *skb, 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; } diff --git a/net/wireless/reg.c b/net/wireless/reg.c index bc0ebd475fc407e9bc75ea91a185bb344d18c8ce..9195f23c0b9b5108c47eef0b157cb55112326dad 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2142,6 +2142,21 @@ static void reg_process_hint(struct regulatory_request *reg_request) 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 @@ static void reg_process_pending_hints(void) 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 @@ EXPORT_SYMBOL(regulatory_set_wiphy_regd_sync_rtnl); 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 2d64e0f45d06e1666e5fec5e03e868c72c3504cb..0dc41fdc27c83ff93338cc4a0d4d85dd9bd77b32 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -1012,6 +1012,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, 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 29c56615a87d4d61c7d05bd23d08e84bc1f43d6a..13ff407f7a5ab3d7b7b659bbfdd794cd4fa6fd97 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -549,7 +549,7 @@ __ieee80211_amsdu_copy_frag(struct sk_buff *skb, struct sk_buff *frame, 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 @@ __ieee80211_amsdu_copy_frag(struct sk_buff *skb, struct sk_buff *frame, 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 @@ __ieee80211_amsdu_copy_frag(struct sk_buff *skb, struct sk_buff *frame, 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 f83b74d3e2acf733864b6a9ddd0f6b7cd41fa4ef..007721632b07086fa78e5e2395d6b75bc76866a6 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -1790,32 +1790,40 @@ void x25_kill_by_neigh(struct x25_neigh *nb) 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; - pr_info("Linux Version 0.2\n"); + rc = x25_register_sysctl(); + if (rc) + goto out_dev; - x25_register_sysctl(); rc = x25_proc_init(); - if (rc != 0) - goto out_dev; + if (rc) + goto out_sysctl; + + pr_info("Linux Version 0.2\n"); + 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 43239527a2058007242bd32ae4bf1891f6622252..703d46aae7a2ebe6753335887c7c0621af7bbe52 100644 --- a/net/x25/sysctl_net_x25.c +++ b/net/x25/sysctl_net_x25.c @@ -73,9 +73,12 @@ static struct ctl_table x25_table[] = { { 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 ccfdc7115a83f709e2a5980c5dc0d65cd5859467..a00ec715aa4681a89e348a058450320bbafe6cfb 100644 --- a/net/xfrm/xfrm_ipcomp.c +++ b/net/xfrm/xfrm_ipcomp.c @@ -283,7 +283,7 @@ static struct crypto_comp * __percpu *ipcomp_alloc_tfms(const char *alg_name) 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 74f2e8f15396cb26aa7b192b873e31f4872790c0..d869b1d927071a329dd9ffee91a402f649778668 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1246,6 +1246,8 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig) 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 +1885,11 @@ int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen 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 3b3455a069a570458925888f66ed0b68cb323d29..35e60d3548a39491b30ad07770853d8a5dd48923 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -121,22 +121,17 @@ static inline int verify_replay(struct xfrm_usersa_info *p, struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL]; struct xfrm_replay_state_esn *rs; - if (p->flags & XFRM_STATE_ESN) { - if (!rt) - return -EINVAL; + if (!rt) + return (p->flags & XFRM_STATE_ESN) ? -EINVAL : 0; - rs = nla_data(rt); + 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 (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8) + return -EINVAL; - if (!rt) - return 0; + 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 140a0fa24f84dd17b731634e9903f9fbe12fac32..0f101f73cf09bb940af1ed908f93f091aa1d270b 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -147,6 +147,37 @@ cc-disable-warning = $(call try-run,\ # 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-version = $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh $(CC)) 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 @@ ld-option = $(call try-run,\ # 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 @@ ld-version = $(shell $(LD) --version | $(srctree)/scripts/ld-version.sh) # 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 3ecec724fc76abc86e0fa13f19ac66d2d081d342..a9c1da505f0226e0020704ba0c632fbd2e934d8a 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -213,6 +213,23 @@ else 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 @@ cmd_modversions_c = \ 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 @@ sub_cmd_record_mcount = \ 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 @@ $(sort $(subdir-obj-y)): $(subdir-ym) ; # 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 @@ ifdef lib-target 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 @@ else 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=-objs))) \ $($(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 37323b0df374b1a89fb256a4077165bc05376421..8c69cd1a7776e38e65547e0dc56ec6d943bca7a7 100644 --- a/scripts/Makefile.kasan +++ b/scripts/Makefile.kasan @@ -9,10 +9,7 @@ KASAN_SHADOW_OFFSET ?= $(CONFIG_KASAN_SHADOW_OFFSET) 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 @@ ifeq ($(call cc-option, $(CFLAGS_KASAN_MINIMAL) -Werror),) -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 6bc33ee37dbce227d71a1956b8b25b337c081742..ea98308b791afb6521e7eb9d6517c2a330f3909b 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -290,11 +290,11 @@ cmd_dt_S_dtb= \ echo '\#include '; \ 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 16923ba4b5b1005158508b4e0c9d2b3d947c119c..b7c50cb48ba4f4682368c526d3158d5e0b49c35a 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -82,12 +82,28 @@ modpost = scripts/mod/modpost \ 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 @@ vmlinux.o: FORCE # 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 @@ quiet_cmd_cc_o_c = CC $@ 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/clang-version.sh b/scripts/clang-version.sh new file mode 100755 index 0000000000000000000000000000000000000000..9780efa56980098ad3d3f9c9d544050655faeb82 --- /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 \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 1b9e67b2d1886bc8121e7f18743697453f89f3a6..7858b309e07f2b02fb5d2a2d4801559305430704 100644 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -53,7 +53,38 @@ archive_builtin() ${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 @@ -70,7 +101,29 @@ modpost_link() ${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 @@ vmlinux_link() 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 @@ vmlinux_link() ${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 @@ vmlinux_link() fi } - # Create ${2} .o file with all symbols from the ${1} object file kallsyms() { @@ -183,6 +242,7 @@ cleanup() 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 @@ case "${KCONFIG_CONFIG}" in . "./${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 @@ else 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 b497d9764dcf02e0dd1abab386faef3af8b65499..247cf1fb5a4ef3660bc4cf92e4ccc3d89a317ee0 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 5423a58d1b06934cb4cd220355deafed24b27f91..e0c508203c9c9a2a963e457d4b7db01ad22b1b31 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -366,7 +366,8 @@ is_mcounted_section_name(char const *const txtname) 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 a2ff3388e5ea396f478870b47e79faadb73cca02..2a61db329adfc72e76d2ce1d48de9775adaca7ea 100755 --- a/scripts/tags.sh +++ b/scripts/tags.sh @@ -106,6 +106,7 @@ all_compiled_sources() case "$i" in *.[cS]) j=${i/\.[cS]/\.o} + j="${j#$tree}" if [ -e $j ]; then echo $i fi diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 935752ce9530d827f86de21e16562a8547e7c4ee..777e1320ee72f6b484ee2a766ec9b835db7a87f3 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -656,11 +656,11 @@ static const struct kernel_param_ops param_ops_aalockpolicy = { .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 @@ module_param_named(logsyscall, aa_g_logsyscall, aabool, S_IRUSR | S_IWUSR); /* 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 @@ static int param_get_aauint(char *buffer, const struct kernel_param *kp) 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 @@ static int param_get_audit(char *buffer, struct kernel_param *kp) 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 @@ static int param_set_audit(const char *val, struct kernel_param *kp) 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 @@ static int param_get_mode(char *buffer, struct kernel_param *kp) 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 6830d2427e47500bae473034b6bac1296cd286d8..7bf8b005a178c4529d5e69c1d3e8ed1bf4cedc94 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -207,7 +207,8 @@ int ima_appraise_measurement(enum ima_hooks func, 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/selinux/hooks.c b/security/selinux/hooks.c index 3b02b82e16ca3acd6cd91192c362b154ea9443ee..8e12ffe4c8323db6f6b0adf4f0da7127b76f1eed 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -407,18 +407,6 @@ static void superblock_free_security(struct super_block *sb) 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 @@ static int sb_finish_set_opts(struct super_block *sb) } } - 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; @@ -2062,8 +2046,9 @@ static inline u32 file_to_av(struct file *file) 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_permission(struct inode *inode, int mask) 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 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) 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 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in 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/ss/services.c b/security/selinux/ss/services.c index 66ea81c43a7db7f090d56c859eaf702cbb2622e0..9b517a42a1af5ffaa6bb193f584f1e1770168ce0 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -154,7 +154,7 @@ static int selinux_set_mapping(struct policydb *pol, } 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++; diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 3321348fd86bdc00836b4a7555fd7d56eae30fa3..cfb8f5896787fd45d95237928ca04715e2a83bb5 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -834,8 +834,25 @@ static int choose_rate(struct snd_pcm_substream *substream, 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 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream, 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 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream, 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 @@ static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_fil 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,14 +1166,35 @@ static int snd_pcm_oss_make_ready(struct snd_pcm_substream *substream) 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); if (err < 0) 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) @@ -1361,19 +1417,21 @@ static ssize_t snd_pcm_oss_write2(struct snd_pcm_substream *substream, const cha 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 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha } 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_read2(struct snd_pcm_substream *substream, char *buf, 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 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use } tmp = 0; } + atomic_dec(&runtime->oss.rw_ref); return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; } @@ -1536,10 +1598,12 @@ static int snd_pcm_oss_reset(struct snd_pcm_oss_file *pcm_oss_file) 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 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) 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 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) 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 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) 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 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) 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 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) 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 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) 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 @@ static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate) 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 @@ static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate) 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 @@ static int snd_pcm_oss_set_channels(struct snd_pcm_oss_file *pcm_oss_file, unsig 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 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) 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 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) 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 @@ static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int for 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 @@ static int snd_pcm_oss_set_subdivide1(struct snd_pcm_substream *substream, int s { struct snd_pcm_runtime *runtime; - if (substream == NULL) - return 0; runtime = substream->runtime; if (subdivide == 0) { subdivide = runtime->oss.subdivision; @@ -1892,9 +1981,17 @@ static int snd_pcm_oss_set_subdivide(struct snd_pcm_oss_file *pcm_oss_file, int 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 @@ static int snd_pcm_oss_set_fragment1(struct snd_pcm_substream *substream, unsign { 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 @@ static int snd_pcm_oss_set_fragment(struct snd_pcm_oss_file *pcm_oss_file, unsig 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 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr } 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 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr 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 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr 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 @@ static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream, 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 48f6aee3680db8f5815334e0c6540010539bb83d..d79a04e703dc75f67f20474b074454dec7f6b583 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -1036,8 +1037,13 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) 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_native.c b/sound/core/pcm_native.c index 8e5649a1d2068732afd8590cc007be2a7baa56ca..019f60b4765c1fb77fa3047afb5817fce6bc0f05 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3460,7 +3460,7 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, 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_compat.c b/sound/core/rawmidi_compat.c index f69764d7cdd7025d3935143883c162372e3c0c7c..e30e30ba6e3984804ec325457d76d8195d69245d 100644 --- a/sound/core/rawmidi_compat.c +++ b/sound/core/rawmidi_compat.c @@ -36,8 +36,6 @@ static int snd_rawmidi_ioctl_params_compat(struct snd_rawmidi_file *rfile, 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 @@ static int snd_rawmidi_ioctl_params_compat(struct snd_rawmidi_file *rfile, 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 @@ static int snd_rawmidi_ioctl_status_compat(struct snd_rawmidi_file *rfile, 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 @@ static int snd_rawmidi_ioctl_status_x32(struct snd_rawmidi_file *rfile, 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/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 0b408617b2c9421b44193d76162a028a28dc373a..ecd1c5fc8db80b4021d897576ec8e47f85213c85 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -255,12 +255,12 @@ static int seq_free_client1(struct snd_seq_client *client) 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 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop) 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 @@ static int snd_seq_client_enqueue_event(struct snd_seq_client *client, 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 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf, 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 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf, /* 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 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf, written += len; } + out: + mutex_unlock(&client->ioctl_mutex); return written ? written : err; } @@ -1835,6 +1838,9 @@ static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client, (! 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 @@ static int kernel_client_enqueue(int client, struct snd_seq_event *ev, 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 3490d21ab9e7f4875dfe58463e90fceca6541f81..9acbed1ac9824de9d118164785ef00dbdeae2984 100644 --- a/sound/core/seq/seq_fifo.c +++ b/sound/core/seq/seq_fifo.c @@ -123,7 +123,7 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f, 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 5847c4475bf31efe707032c9f370df9b245a7bba..4c8cbcd898878abc3c22104f8aa60eeb1ebd3735 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -221,7 +221,8 @@ void snd_seq_cell_free(struct snd_seq_event_cell * cell) */ 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 @@ static int snd_seq_cell_alloc(struct snd_seq_pool *pool, 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 @@ static int snd_seq_cell_alloc(struct snd_seq_pool *pool, */ 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 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event, 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 snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event, 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 32f959c17786d9ac8c071ba0e6fd070dc06da78b..3abe306c394af95c8dbdb99475f7829a17efc33c 100644 --- a/sound/core/seq/seq_memory.h +++ b/sound/core/seq/seq_memory.h @@ -66,7 +66,8 @@ struct snd_seq_pool { 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 bc1c8488fc2a1508d9572617e9030ba180477fd0..2bc6759e4adcf6a794efc22e4707b8f228d6b362 100644 --- a/sound/core/seq/seq_prioq.c +++ b/sound/core/seq/seq_prioq.c @@ -87,7 +87,7 @@ void snd_seq_prioq_delete(struct snd_seq_prioq **fifo) 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 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f, 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 @@ struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f) 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 @@ int snd_seq_prioq_avail(struct snd_seq_prioq * f) 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 d38bb78d934545b56e87f248ac1b6b46f34be8e7..2c315ca10fc4c1a8ef5eddd20e3731705fa8097c 100644 --- a/sound/core/seq/seq_prioq.h +++ b/sound/core/seq/seq_prioq.h @@ -44,14 +44,12 @@ void snd_seq_prioq_delete(struct snd_seq_prioq **fifo); 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 79e0c5604ef806d62eca084293e66222b8fe6828..1a6dc4ff44a69bb53e4b8ab9009b957ea7fd8689 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -277,30 +277,20 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop) __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/drivers/aloop.c b/sound/drivers/aloop.c index cbd20cb8ca1127704b9448952f5d907e4a17432d..dc91002d1e0de0e30b1b3d85d3a5907a916dd63f 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -192,6 +192,11 @@ static inline void loopback_timer_stop(struct loopback_pcm *dpcm) 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) @@ -326,6 +331,8 @@ static int loopback_prepare(struct snd_pcm_substream *substream) 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 +666,9 @@ static void free_cable(struct snd_pcm_substream *substream) 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 +708,6 @@ static int loopback_open(struct snd_pcm_substream *substream) 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 +739,11 @@ static int loopback_open(struct snd_pcm_substream *substream) 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 +758,7 @@ static int loopback_close(struct snd_pcm_substream *substream) 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); diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 9741757436beec2e0c3be573b6da4d1f2ac2fbda..d0ad61f563e26e2c3e1c5fddd2ba5a3c7ce9d162 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -471,8 +471,9 @@ static int handle_in_packet(struct amdtp_stream *s, * 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 f7c054bc9d92b151e111e52b12769fa600741a34..8136bd20c8b1aecfceab02f969aae6e4e8a5f802 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 @@ enum cip_flags { 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/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c index b3cffd01a19f711f3572920a33fa75098f39ffde..a4688545339ccd58ee558a7e35546c5a18b9fb13 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 @@ struct amdtp_dot { 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 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, 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 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, 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 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer, 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 @@ static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer, 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 @@ void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port, { 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 cc4776c6ded313673ef1c767101f2ed7e0c4369f..1f5e1d23f31a7132a5dfb5f3f34468b1517a89d0 100644 --- a/sound/firewire/digi00x/digi00x.c +++ b/sound/firewire/digi00x/digi00x.c @@ -13,7 +13,8 @@ MODULE_AUTHOR("Takashi Sakamoto "); 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 @@ static int snd_dg00x_probe(struct fw_unit *unit, 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 @@ static const struct ieee1394_device_id snd_dg00x_id_table[] = { .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 2cd465c0caae84e1f1e21849f58c12194e2b9f5f..43bcb0ce69c061e8b73fc63176cd87f9608d9ed3 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -60,6 +60,7 @@ struct snd_dg00x { /* 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/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 293f3f21377657c532a834dc598811c9d0a91087..7d3f88d90eec8d7e9dcd1000ca78c663d6ee9d93 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -185,6 +185,10 @@ module_param(power_save, xint, 0644); 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 @@ enum { #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 @@ static void azx_check_snoop_available(struct azx *chip) */ 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,24 @@ static int azx_probe(struct pci_dev *pci, 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.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 +2079,7 @@ static int azx_probe_continue(struct azx *chip) 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 +2155,21 @@ static int azx_probe_continue(struct azx *chip) 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 2c3065c1f3fb160fdcd64d58e7b1404280aec938..b3851b99112037ab63c25b748cabf0ace1f7b345 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -849,6 +849,8 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { 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 89c166b97e81835e6194b4edcc465142daa8ffcd..e2230bed74099678cebf790170c0c2f7a11cdb26 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3261,8 +3261,12 @@ static void alc269_fixup_mic_mute_hook(void *private_data, int enabled) 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 +4484,14 @@ static void alc_fixup_tpt470_dock(struct hda_codec *codec, 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 +4764,16 @@ static void alc298_fixup_speaker_volume(struct hda_codec *codec, } } +/* 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 +4923,7 @@ enum { 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 +5616,10 @@ static const struct hda_fixup alc269_fixups[] = { .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 +5683,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { 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 +5805,11 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { 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), @@ -6761,6 +6784,7 @@ enum { 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 +7036,21 @@ static const struct hda_fixup alc662_fixups[] = { .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/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index f9f2737c4ad230616cd66e47e885b8e2953233c0..a4871c4adcb0df0f6674fb45d6abf496282a315d 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -882,6 +882,7 @@ static int nau8825_adc_event(struct snd_soc_dapm_widget *w, 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 f5d34153e21fc4cf10c3e9d45436ad9e14efa166..f0c9e25624748bd580b319cc23f87bd5d28f7920 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -1736,6 +1736,7 @@ static const struct regmap_config rt5651_regmap = { .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 abc802a5a479013b0c1995a46790ec009f87a111..65ac4518ad06096e1ca8d04c539fc19ddf1b8f15 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -5035,6 +5035,12 @@ static const struct i2c_device_id rt5677_i2c_id[] = { }; 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 int rt5677_i2c_remove(struct i2c_client *i2c) 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 1589325855bc10ca41c1180de3e06b745d32ee19..3dba5550a6659152df5c8feec4e00daa2bbd33e7 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -774,15 +774,26 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream, 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 993bde29ca1bcc6e611bf8d7b7b1f83caa0ce396..7693e63078b19955b06e88917f00c629b30337d6 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -54,10 +54,17 @@ struct ssm2602_priv { * 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 @@ const struct regmap_config ssm2602_regmap_config = { .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/generic/simple-card.c b/sound/soc/generic/simple-card.c index dd88c2cb64703a4b0999261a92fb06f348af2ea7..48804e4ab5301e050c85a23d6451e1bb6b4af369 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -201,7 +201,7 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) 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 0bfa688624604d2a2f1b7e8819d9dae64c5c3456..cd16b431d1bd013814ec0a2a21193799097e30bf 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -420,7 +420,21 @@ static const struct dmi_system_id byt_table[] = { .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 4ccc80e5e8cc0a78aa2cbb805f5ed94c160c5fdf..c798f8d4ae43322db9971360bd69b479c3f5a578 100644 --- a/sound/soc/intel/atom/sst/sst_stream.c +++ b/sound/soc/intel/atom/sst/sst_stream.c @@ -221,7 +221,7 @@ int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, 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 90525614c20aa695a1ed5b3fb4255d81600e2491..b1eb696f33b657b5f6efee7650ac1f9319a5787e 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -111,6 +111,7 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = { 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 @@ static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = { {"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 @@ static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = { {"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 @@ static const struct snd_kcontrol_new cht_mc_controls[] = { 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 805b7f2173f3ec6163d9de9728d0b099d9a3a9a1..78472c908ae95fa858102ef430d0e69ff8dc3283 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -331,7 +331,11 @@ int skl_resume_dsp(struct skl *skl) 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 58c728662600455dcd9d9fbf442687728ea1bf7d..2fd213cd9a409250419fb0785f19ba4c908ba391 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1191,7 +1191,11 @@ static int skl_platform_soc_probe(struct snd_soc_platform *platform) 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 06fa5e85dd0e0177f6e886065e23060a87a958fa..3e526fbd267e618e705b5a3b3b46f61fc0a9add3 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -457,7 +457,7 @@ static int probe_codec(struct hdac_ext_bus *ebus, int addr) 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 b6615affe5718b379df657f401cf34eeddc08dd1..fde974d52bb2be6134085038715e791b7f338350 100644 --- a/sound/soc/nuc900/nuc900-ac97.c +++ b/sound/soc/nuc900/nuc900-ac97.c @@ -67,7 +67,7 @@ static unsigned short nuc900_ac97_read(struct snd_ac97 *ac97, /* 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 @@ static void nuc900_ac97_write(struct snd_ac97 *ac97, unsigned short reg, /* 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/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index 7d92a24b7cfa558afbb8331401c974c59d5f1ae5..98835e9d1d7d13ddc905917a34419e61add7cc83 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -82,6 +82,9 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, [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 a9a43acce30ec573bce012f5c8a6758df08305bf..17e305b71fd9042e218944bdc2153c33b863bb4c 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -232,6 +232,15 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, */ 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 @@ -543,6 +552,13 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, 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 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, * 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/usb/line6/midi.c b/sound/usb/line6/midi.c index d0fb2f205bd94a804b6958cef5fa51e2f58eae85..4f4ebe90f1a8e232d0352ab9b73f04af3981841b 100644 --- a/sound/usb/line6/midi.c +++ b/sound/usb/line6/midi.c @@ -125,7 +125,7 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data, } 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/quirks-table.h b/sound/usb/quirks-table.h index 8a59d4782a0f4d3c33b3e6840cbe265ba8ee4406..69bf5cf1e91ef1576e1c91193036d2b504977129 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3277,4 +3277,51 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), } }, +{ + /* + * 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 1cd7f8b0bf77986a00937aa5495edf595b2113cd..45655b9108e82e24cf257f320754bb4049185202 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1175,6 +1175,7 @@ static bool is_teac_dsd_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; diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index f87996b0cb299636e5803a699710be944d3924e3..9a250c71840e9202b67e78fb6a3d6990b23e1136 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -442,9 +442,9 @@ static int perf_del_probe_events(struct strfilter *filter) } 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 688dea7cb08f7337bc6c620f98aa3e996ce4d581..68861e81f06cfe9a46866fd413f240fefb297b96 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -146,6 +146,7 @@ static aggr_get_id_t aggr_get_id; 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 @@ static int read_counter(struct perf_evsel *counter) 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 int read_counter(struct perf_evsel *counter) 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 @@ static void print_metric_csv(void *ctx, 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 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval, 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 @@ static void print_footer(void) 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 21f8a81797a04e32b9fc496851e9332f58e5e6d9..0fd8bfb77f65278172c304c16346f14041070ace 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -679,6 +679,10 @@ static struct syscall_fmt { { .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 @@ struct syscall { 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 @@ static bool trace__filter_duration(struct trace *trace, double t) 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 void sig_handler(int sig) } 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 @@ static int trace__printf_interrupted_entry(struct trace *trace, struct perf_samp 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 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, 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 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, { 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 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, 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 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, 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 @@ static int trace__pgfault(struct trace *trace, 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 76f41f249944ccaa3af8de50256f8a24faf22f6b..6cd9e5107f7784ab4c340f80a03bc786b7c264b4 100644 --- a/tools/perf/tests/kmod-path.c +++ b/tools/perf/tests/kmod-path.c @@ -61,6 +61,7 @@ int test__kmod_path__parse(int subtest __maybe_unused) 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 @@ int test__kmod_path__parse(int subtest __maybe_unused) 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 430d039d00798e452d9f9996934d72a36eb6224e..a38227eb545054c3ac3a2c4fb59714a9d93b87c7 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1250,6 +1250,7 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil { 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 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil 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 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil __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 e528c40739ccec0b60e392c5d815ccdbf79541db..993ef2762508ed76540900a6adaf9bd400c90149 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -182,13 +182,17 @@ char *build_id_cache__origname(const char *sbuild_id) 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 "../../" */ p = strrchr(buf, '/'); /* Cut off the "/" */ if (p && (p > buf + offs)) { diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index d2c6cdd9d42b72a194d913bdc1840d5ff72bb281..4bc58822416cf9c204f435bab52a0df6889a76b1 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -366,7 +366,23 @@ static int __open_dso(struct dso *dso, struct machine *machine) if (!is_regular_file(name)) return -EINVAL; + if (dso__needs_decompress(dso)) { + char newpath[KMOD_DECOMP_LEN]; + size_t len = sizeof(newpath); + + if (dso__decompress_kmodule_path(dso, name, newpath, len) < 0) { + free(name); + return -dso->load_errno; + } + + strcpy(name, newpath); + } + fd = do_open(name); + + if (dso__needs_decompress(dso)) + unlink(name); + free(name); return fd; } diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 8ab0d7da956bcb3244d98ee27284034d1fe1b00c..6631923957805dee46e5a1051178b88c7c06437a 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -255,8 +255,8 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, 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 8bc271141d9df9051aa93bdf4677ba79ff5473e0..bce80f866dd004b4263ce97362e5d0eee4cda4c6 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1221,7 +1221,7 @@ int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread, 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 @@ int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, 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 perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, 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 28bdb48357f09ba0ad8e8a2109a008cf620d31de..ab36aa5585b4b61654a79121326609b48ba5a4b9 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1454,8 +1454,16 @@ static int __event_process_build_id(struct build_id_event *bev, 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 7e27207d0f452b51e42b0125000a8c121e59d59f..cac39532c0574ae005ba9620987a2b30b7f73e7a 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 @@ static int intel_pt_overflow(struct intel_pt_decoder *decoder) 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 @@ static int intel_pt_walk_fup_tip(struct intel_pt_decoder *decoder) 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 @@ const struct intel_pt_state *intel_pt_decode(struct intel_pt_decoder *decoder) 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 @@ static unsigned char *intel_pt_last_psb(unsigned char *buf, size_t len) * @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 @@ static unsigned char *intel_pt_last_psb(unsigned char *buf, size_t len) * * 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 @@ static bool intel_pt_next_tsc(unsigned char *buf, size_t len, uint64_t *tsc) 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 @@ static int intel_pt_tsc_cmp(uint64_t tsc1, uint64_t tsc2) * @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 int intel_pt_tsc_cmp(uint64_t tsc1, uint64_t tsc2) 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 @@ static unsigned char *intel_pt_find_overlap_tsc(unsigned char *buf_a, * @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 @@ static unsigned char *intel_pt_find_overlap_tsc(unsigned char *buf_a, */ 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 @@ unsigned char *intel_pt_find_overlap(unsigned char *buf_a, size_t len_a, 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 @@ unsigned char *intel_pt_find_overlap(unsigned char *buf_a, size_t len_a, } /* 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 89399985fa4d4bfec664a1630ebc13c37da3b6d0..9ae4df1dcedcf86323f48358425a4a6020b610b6 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 @@ const struct intel_pt_state *intel_pt_decode(struct intel_pt_decoder *decoder); 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 dc041d4368c812d6e932e31e4f56d3a24620ee6c..b1161d725ce998480ac47492087208a05ff200a0 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -131,6 +131,7 @@ struct intel_pt_queue { 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 void intel_pt_dump_event(struct intel_pt *pt, unsigned char *buf, 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 @@ static int intel_pt_setup_queue(struct intel_pt *pt, 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 @@ static int intel_pt_sample(struct intel_pt_queue *ptq) 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 @@ static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip) 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 @@ static int intel_pt_run_decoder(struct intel_pt_queue *ptq, u64 *timestamp) 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 @@ static int intel_pt_run_decoder(struct intel_pt_queue *ptq, u64 *timestamp) 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 @@ static int intel_pt_run_decoder(struct intel_pt_queue *ptq, u64 *timestamp) 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 @@ static int intel_pt_sync_switch(struct intel_pt *pt, int cpu, pid_t tid, 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 fe84df1875aa9e231d63d56ba3417f242ea4e3b7..e70e935b1841967fecd4620bea5c94d2ed62d1e6 100644 --- a/tools/perf/util/ordered-events.c +++ b/tools/perf/util/ordered-events.c @@ -79,7 +79,7 @@ static union perf_event *dup_event(struct ordered_events *oe, 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 @@ void ordered_events__delete(struct ordered_events *oe, struct ordered_event *eve 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 6a6f44dd594bc4c6275694335ebaa02b22118982..c93daccec755b9a347ee0398491a0bbb80374656 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -2609,6 +2609,14 @@ static int get_new_event_name(char *buf, size_t len, const char *base, 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 @@ concat_probe_trace_events(struct probe_trace_event **tevs, int *ntevs, 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 5d61242a6e648a1b65c8b9d16c93e02f8ed655cd..7e0573e55a356b7c9b503d374db18b31edb8c056 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -139,8 +139,14 @@ struct perf_session *perf_session__new(struct perf_data_file *file, 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 @@ struct perf_session *perf_session__new(struct perf_data_file *file, 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 @@ static int __perf_session__process_pipe_events(struct perf_session *session) 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 452e15a10dd28f53951b4ae8aaf08b1928e9a805..031e64ce71564b1994852e4224c4d47fd884cbe2 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -846,6 +846,9 @@ static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf, 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 @@ sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right) 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 e97d7016d771c37ec2bbbab8634c579b0ee64299..ce7e6e5c1cead11290b8f8272d678a15be659c08 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 @@ struct trigger { 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 bool trigger_is_error(struct trigger *t) 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 783a53fb7a4ed41c2fef9e90de26355e6b1bcc7e..b46e1cf347e51c47f108b194b1e306fe19d7e4cd 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -38,6 +38,14 @@ static int __report_module(struct addr_location *al, u64 ip, 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 @@ frame_callback(Dwfl_Frame *state, void *arg) { 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 20c2e5743903872771ae975b340b63aec521c3d3..120383494ff2f7016f1bb9ac21fede25c9f7e087 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -646,6 +646,17 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, 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 85c56800f17a6018eb080db40e9a08b03f9a460c..dfb010bd29f2f689f56057ab0d1054489b86ffcf 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -207,7 +207,7 @@ int copyfile_offset(int ifd, loff_t off_in, int ofd, loff_t off_out, u64 size) 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 71620fa959530211c6c73c25ab71aaaba7cfa50a..d025eafc09c21c433e7f99ab5e5e9271e8f7268c 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c @@ -1908,6 +1908,7 @@ static __init int nfit_test_init(void) 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 @@ static __init int nfit_test_init(void) 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 @@ static __exit void nfit_test_exit(void) { 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 d8ac9ba67688eb8ebe437fdb47cb5afb5621d9ca..17e16fcaa0cc5b73c7eda5ced589f680d3c3be11 100755 --- a/tools/testing/selftests/firmware/fw_filesystem.sh +++ b/tools/testing/selftests/firmware/fw_filesystem.sh @@ -28,7 +28,10 @@ test_finish() 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 + OLD_FWPATH=" " + fi + echo -n "$OLD_FWPATH" >/sys/module/firmware_class/parameters/path 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 d9c49f41515e704994ed1669dbe0a859a6dadd79..e79ccd6aada1a9acf10bb2860e76ddb444415d52 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 @@ int test_body(void) 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 3f81a109520693ec6ebac0abe78c73388b8fc334..50a6371b2b2e473e342072aead44bd1c61972321 100755 --- a/tools/testing/selftests/rcutorture/bin/configinit.sh +++ b/tools/testing/selftests/rcutorture/bin/configinit.sh @@ -51,7 +51,7 @@ then 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 cbb0564c0ec4a0c0ee514376d36c4b0dac535800..f68998149351154ff61900f7e650ee66665a78c4 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -1318,7 +1318,7 @@ void change_syscall(struct __test_metadata *_metadata, 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__) diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index 6eb50152baf0bdbbdbf9f18e8afe4f81dc7e1a5a..e5b459a09cffc95b4f2afe42640bdd35fa96f7ea 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile @@ -17,7 +17,7 @@ TARGETS_C_64BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_64BIT_ONLY) 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 d075ea0e5ca1ac0a42ce0d48e25b30a1a059de48..ade443a8842106e49aabc1f2d36dc9d4f8b75b65 100644 --- a/tools/testing/selftests/x86/entry_from_vm86.c +++ b/tools/testing/selftests/x86/entry_from_vm86.c @@ -95,6 +95,31 @@ asm ( "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 @@ asm ( 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 @@ static bool do_test(struct vm86plus_struct *v86, unsigned long eip, (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 @@ int main(void) 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 @@ int main(void) 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 @@ int main(void) 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 @@ int main(void) 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 79e1d13d1cdae005083eb7bb32433bf1aa7bd4e9..58384189370c3722a4bbaa12e6df6882457895d5 100644 --- a/tools/testing/selftests/x86/mpx-mini-test.c +++ b/tools/testing/selftests/x86/mpx-mini-test.c @@ -419,8 +419,7 @@ void handler(int signum, siginfo_t *si, void *vucontext) 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 2842a5fa22b33c0b1e94cfb9f8971c95de30d68f..85a78eba0a932d4123cec063ca8a1949ce69d1d6 100644 --- a/tools/testing/selftests/x86/protection_keys.c +++ b/tools/testing/selftests/x86/protection_keys.c @@ -188,17 +188,29 @@ void lots_o_noops_around_write(int *write_to_me) #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 @@ void dump_mem(void *dumpme, int len_bytes) } } -#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 @@ void signal_handler(int signum, siginfo_t *si, void *vucontext) 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 @@ void signal_handler(int signum, siginfo_t *si, void *vucontext) 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 @@ void signal_handler(int signum, siginfo_t *si, void *vucontext) 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 eaea9243970840dab196cb1ddf84586e30803f0a..1e3da137a8bb977ee4010a3114bb6391f395383d 100644 --- a/tools/testing/selftests/x86/ptrace_syscall.c +++ b/tools/testing/selftests/x86/ptrace_syscall.c @@ -182,8 +182,10 @@ static void test_ptrace_syscall_restart(void) 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 @@ static void test_restart_under_ptrace(void) 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 a0972dea9e6ce9b886cf70ba4ce219c70664a906..1935565079576b10188ff269034512ec30a77c38 100644 --- a/tools/usb/usbip/src/usbipd.c +++ b/tools/usb/usbip/src/usbipd.c @@ -463,7 +463,7 @@ static void set_signal(void) 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/kvm_main.c b/virt/kvm/kvm_main.c index 1b20768e781df38efca02085c1c205f08dc4ef16..eaae7252f60c202ae6da3a50ff708c0e668ab7b9 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -976,8 +976,7 @@ int __kvm_set_memory_region(struct kvm *kvm, /* 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)))