diff options
654 files changed, 21211 insertions, 7268 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt index 02bf809740c3..781f05879445 100644 --- a/Documentation/devicetree/bindings/arm/msm/msm.txt +++ b/Documentation/devicetree/bindings/arm/msm/msm.txt @@ -48,7 +48,7 @@ SoCs: compatible = "qcom,apqtitanium" - APQ8998 - compatible = "qcom,apq8998" + compatible = "qcom,apq8098" - MDM9630 compatible = "qcom,mdm9630" @@ -195,9 +195,9 @@ compatible = "qcom,apq8037-cdp" compatible = "qcom,apq8037-mtp" compatible = "qcom,apqtitanium-cdp" compatible = "qcom,apqtitanium-mtp" -compatible = "qcom,apq8998-cdp" -compatible = "qcom,apq8998-mtp" -compatible = "qcom,apq8998-qrd" +compatible = "qcom,apq8098-cdp" +compatible = "qcom,apq8098-mtp" +compatible = "qcom,apq8098-qrd" compatible = "qcom,mdm9630-cdp" compatible = "qcom,mdm9630-mtp" compatible = "qcom,mdm9630-sim" @@ -280,6 +280,7 @@ compatible = "qcom,sdm660-rumi" compatible = "qcom,sdm660-cdp" compatible = "qcom,sdm660-mtp" compatible = "qcom,sdm660-qrd" +compatible = "qcom,sda660-qrd" compatible = "qcom,sda658-mtp" compatible = "qcom,sda658-cdp" compatible = "qcom,sda660-mtp" @@ -290,6 +291,7 @@ compatible = "qcom,sdm630-cdp" compatible = "qcom,sda630-mtp" compatible = "qcom,sda630-cdp" compatible = "qcom,sdm630-qrd" +compatible = "qcom,sda630-qrd" compatible = "qcom,msm8952-rumi" compatible = "qcom,msm8952-sim" compatible = "qcom,msm8952-qrd" diff --git a/Documentation/devicetree/bindings/fb/mdss-dp.txt b/Documentation/devicetree/bindings/fb/mdss-dp.txt index aa227c2628da..707e6edb26ea 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dp.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dp.txt @@ -27,7 +27,46 @@ Required properties - qcom,aux-en-gpio: Specifies the aux-channel enable gpio. - qcom,aux-sel-gpio: Specifies the aux-channel select gpio. - qcom,usbplug-cc-gpio: Specifies the usbplug orientation gpio. -- qcom,aux-cfg-settings: An array that specifies the DP AUX configuration settings. +- qcom,aux-cfg0-settings: Specifies the DP AUX configuration 0 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg1-settings: Specifies the DP AUX configuration 1 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg2-settings: Specifies the DP AUX configuration 2 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg3-settings: Specifies the DP AUX configuration 3 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg4-settings: Specifies the DP AUX configuration 4 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg5-settings: Specifies the DP AUX configuration 5 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg6-settings: Specifies the DP AUX configuration 6 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg7-settings: Specifies the DP AUX configuration 7 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg8-settings: Specifies the DP AUX configuration 8 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg9-settings: Specifies the DP AUX configuration 9 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. Optional properties: - qcom,<type>-supply-entries: A node that lists the elements of the supply used by the @@ -87,7 +126,16 @@ Example: "core_aux_clk", "core_cfg_ahb_clk", "ctrl_link_clk", "ctrl_link_iface_clk", "ctrl_crypto_clk", "ctrl_pixel_clk"; - qcom,aux-cfg-settings = [00 13 00 10 0a 26 0a 03 8b 03]; + qcom,aux-cfg0-settings = [1c 00]; + qcom,aux-cfg1-settings = [20 13 23 1d]; + qcom,aux-cfg2-settings = [24 00]; + qcom,aux-cfg3-settings = [28 00]; + qcom,aux-cfg4-settings = [2c 0a]; + qcom,aux-cfg5-settings = [30 26]; + qcom,aux-cfg6-settings = [34 0a]; + qcom,aux-cfg7-settings = [38 03]; + qcom,aux-cfg8-settings = [3c bb]; + qcom,aux-cfg9-settings = [40 03]; qcom,logical2physical-lane-map = [02 03 01 00]; qcom,phy-register-offset = <0x4>; qcom,max-pclk-frequency-khz = <593470>; diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt index 90ccfa7c62e2..ce5ee56ada68 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt @@ -581,6 +581,15 @@ Additional properties added to the second level nodes that represent timings pro commands. "dsi_lp_mode" = DSI low power mode (default) "dsi_hs_mode" = DSI high speed mode +- qcom,sublinks-count: An integer value indicates the number of sublinks in the panel. + Default value is 1. This property is used only if qcom,split-link-enabled + is defined. +- qcom,lanes-per-sublink: An integer value indicates the number of data lanes per sublink in the panel. + Default value is 1. This property is used only if qcom,split-link-enabled + is defined. +- qcom,split-link-enabled: A boolean value to enable/disable the split link feature. If qcom,sublinks-count + or qcom,lanes-per-sublink are not defined, default values are used. + Note, if a given optional qcom,* binding is not present, then the driver will configure the default values specified. @@ -808,6 +817,10 @@ Example: qcom,mdss-dsc-version = <0x11>; qcom,mdss-dsc-scr-version = <0x1>; + qcom,split-link-enabled; + qcom,sublinks-count = <2>; + qcom,lanes-per-sublink = <2>; + dsi_sim_vid_config0: config0 { qcom,lm-split = <360 360>; qcom,mdss-dsc-encoders = <2>; diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi.txt b/Documentation/devicetree/bindings/fb/mdss-dsi.txt index 6c20d22f98b4..0d55389f3790 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dsi.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dsi.txt @@ -50,6 +50,8 @@ Optional properties: -- qcom,supply-post-on-sleep: time to sleep (ms) after turning on -- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off -- qcom,supply-post-off-sleep: time to sleep (ms) after turning off + -- qcom,supply-lp-mode-disable-allowed: supply can be turned off in + low power state. - pll-src-config Specified the source PLL for the DSI link clocks: "PLL0" - Clocks sourced out of DSI PLL0 diff --git a/Documentation/devicetree/bindings/input/qpnp-power-on.txt b/Documentation/devicetree/bindings/input/qpnp-power-on.txt index a596aa1c595d..c2550e6fe26c 100644 --- a/Documentation/devicetree/bindings/input/qpnp-power-on.txt +++ b/Documentation/devicetree/bindings/input/qpnp-power-on.txt @@ -82,6 +82,8 @@ Optional properties: - qcom,shutdown-poweroff-type Same description as qcom,warm-reset-poweroff- type but this applies for the system shutdown case. +- qcom,kpdpwr-sw-debounce Boolean property to enable the debounce logic + on the KPDPWR_N rising edge. All the below properties are in the sub-node section (properties of the child diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt index addb0a6869ac..12d32ec74369 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt +++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt @@ -96,6 +96,14 @@ First Level Node - FG Gen3 device This value has to be specified in negative values for the charging current. +- qcom,fg-chg-term-base-current + Usage: optional + Value type: <u32> + Definition: Battery current (in mA) upper boundary at which the fuel + gauge will issue an end of charge during discharging. If + this property is not specified, then the default value used + will be 75mA. + - qcom,fg-delta-soc-thr Usage: optional Value type: <u32> diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt index 5a2c3ecd3d1e..894c34553a22 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt +++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt @@ -169,6 +169,18 @@ Charger specific properties: Definition: Boolean flag which when present enables intput suspend for debug battery. +- qcom,min-freq-khz + Usage: optional + Value type: <u32> + Definition: Specifies the minimum charger buck/boost switching frequency + in KHz. It overrides the min frequency defined for the charger. + +- qcom,max-freq-khz + Usage: optional + Value type: <u32> + Definition: Specifies the maximum charger buck/boost switching frequency in + KHz. It overrides the max frequency defined for the charger. + ============================================= Second Level Nodes - SMB2 Charger Peripherals ============================================= diff --git a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt index 92ef23c3a290..5529e30811fb 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt +++ b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt @@ -22,7 +22,8 @@ Charger specific properties: Definition: String which indicates the charging mode. Can be one of the following: Standalone/Parallel Master - "qcom,smb138x-charger" - Parallel Slave - "qcom,smb138x-parallel-slave" + smb138x Parallel Slave - "qcom,smb138x-parallel-slave" + smb1355 Parallel Slave - "qcom,smb1355-parallel-slave", - qcom,pmic-revid Usage: required @@ -35,7 +36,8 @@ Charger specific properties: Usage: optional Value type: <u32> Definition: Specifies parallel charging mode. If not specified, MID-MID - option is selected by default. + option is selected by default. Note that smb1355 can only + run in MID-MID configuration. - qcom,suspend-input Usage: optional @@ -125,7 +127,7 @@ Example ======= smb138x_charger: qcom,smb138x-charger { - compatible = "qcom,qpnp-smb138x-charger"; + compatible = "qcom,smb138x-charger"; #address-cells = <1>; #size-cells = <1>; diff --git a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt index c9cfc889faba..0d53b9fa4378 100644 --- a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt @@ -151,6 +151,10 @@ LAB subnode optional properties: any value in the allowed limit. - qcom,notify-lab-vreg-ok-sts: A boolean property which upon set will poll and notify the lab_vreg_ok status. +- qcom,qpnp-lab-sc-wait-time-ms: This property is used to specify the time + (in ms) to poll for the short circuit + detection. If not specified the default time + is 5 sec. Following properties are available only for PM660A: diff --git a/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt index 38f599ba5321..55fde0d4feb6 100644 --- a/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt @@ -14,6 +14,11 @@ Required Node Structure Value type: <string> Definition: should be "qcom,qpnp-oledb-regulator". +- qcom,pmic-revid + Usage: required + Value type: <phandle> + Definition: Used to identify the PMIC subtype. + - reg Usage: required Value type: <prop-encoded-array> @@ -57,13 +62,6 @@ Required Node Structure rail. This property is applicable only if qcom,ext-pin-ctl property is specified and it is specific to PM660A. -- qcom,force-pd-control - Usage: optional - Value type: <bool> - Definition: Used to enable the pull down control forcibly via SPMI by - disabling the pull down configuration done by hardware - automatically through SWIRE pulses. - - qcom,pbs-client Usage: optional Value type: <phandle> @@ -224,6 +222,7 @@ pm660a_oledb: qpnp-oledb@e000 { compatible = "qcom,qpnp-oledb-regulator"; #address-cells = <1>; #size-cells = <1>; + qcom,pmic-revid = <&pm660l_revid>; reg = <0xe000 0x100>; label = "oledb"; diff --git a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt index bef919334574..633abd2b8d08 100644 --- a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt +++ b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt @@ -42,6 +42,9 @@ Required properties: cell 4: interrupt flags indicating level-sense information, as defined in dt-bindings/interrupt-controller/irq.h +Optional properties: +- qcom,reserved-chan : Reserved channel for debug purpose + Example V1 PMIC-Arbiter: spmi { @@ -56,6 +59,7 @@ Example V1 PMIC-Arbiter: qcom,ee = <0>; qcom,channel = <0>; + qcom,reserved-chan = <511>; #address-cells = <2>; #size-cells = <0>; diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 0220f18658e8..e953469cbe5e 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2692,6 +2692,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. we can turn it on. on: enable the feature + page_poison= [KNL] Boot-time parameter changing the state of + poisoning on the buddy allocator. + off: turn off poisoning + on: turn on poisoning + panic= [KNL] Kernel behaviour on panic: delay <timeout> timeout > 0: seconds before rebooting timeout = 0: wait forever diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 2d8fe6499090..f6851d94c1af 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1418,11 +1418,20 @@ accept_ra_pinfo - BOOLEAN Functional default: enabled if accept_ra is enabled. disabled if accept_ra is disabled. +accept_ra_rt_info_min_plen - INTEGER + Minimum prefix length of Route Information in RA. + + Route Information w/ prefix smaller than this variable shall + be ignored. + + Functional default: 0 if accept_ra_rtr_pref is enabled. + -1 if accept_ra_rtr_pref is disabled. + accept_ra_rt_info_max_plen - INTEGER Maximum prefix length of Route Information in RA. - Route Information w/ prefix larger than or equal to this - variable shall be ignored. + Route Information w/ prefix larger than this variable shall + be ignored. Functional default: 0 if accept_ra_rtr_pref is enabled. -1 if accept_ra_rtr_pref is disabled. diff --git a/Documentation/networking/netlink_mmap.txt b/Documentation/networking/netlink_mmap.txt deleted file mode 100644 index 54f10478e8e3..000000000000 --- a/Documentation/networking/netlink_mmap.txt +++ /dev/null @@ -1,332 +0,0 @@ -This file documents how to use memory mapped I/O with netlink. - -Author: Patrick McHardy <kaber@trash.net> - -Overview --------- - -Memory mapped netlink I/O can be used to increase throughput and decrease -overhead of unicast receive and transmit operations. Some netlink subsystems -require high throughput, these are mainly the netfilter subsystems -nfnetlink_queue and nfnetlink_log, but it can also help speed up large -dump operations of f.i. the routing database. - -Memory mapped netlink I/O used two circular ring buffers for RX and TX which -are mapped into the processes address space. - -The RX ring is used by the kernel to directly construct netlink messages into -user-space memory without copying them as done with regular socket I/O, -additionally as long as the ring contains messages no recvmsg() or poll() -syscalls have to be issued by user-space to get more message. - -The TX ring is used to process messages directly from user-space memory, the -kernel processes all messages contained in the ring using a single sendmsg() -call. - -Usage overview --------------- - -In order to use memory mapped netlink I/O, user-space needs three main changes: - -- ring setup -- conversion of the RX path to get messages from the ring instead of recvmsg() -- conversion of the TX path to construct messages into the ring - -Ring setup is done using setsockopt() to provide the ring parameters to the -kernel, then a call to mmap() to map the ring into the processes address space: - -- setsockopt(fd, SOL_NETLINK, NETLINK_RX_RING, ¶ms, sizeof(params)); -- setsockopt(fd, SOL_NETLINK, NETLINK_TX_RING, ¶ms, sizeof(params)); -- ring = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0) - -Usage of either ring is optional, but even if only the RX ring is used the -mapping still needs to be writable in order to update the frame status after -processing. - -Conversion of the reception path involves calling poll() on the file -descriptor, once the socket is readable the frames from the ring are -processed in order until no more messages are available, as indicated by -a status word in the frame header. - -On kernel side, in order to make use of memory mapped I/O on receive, the -originating netlink subsystem needs to support memory mapped I/O, otherwise -it will use an allocated socket buffer as usual and the contents will be - copied to the ring on transmission, nullifying most of the performance gains. -Dumps of kernel databases automatically support memory mapped I/O. - -Conversion of the transmit path involves changing message construction to -use memory from the TX ring instead of (usually) a buffer declared on the -stack and setting up the frame header appropriately. Optionally poll() can -be used to wait for free frames in the TX ring. - -Structured and definitions for using memory mapped I/O are contained in -<linux/netlink.h>. - -RX and TX rings ----------------- - -Each ring contains a number of continuous memory blocks, containing frames of -fixed size dependent on the parameters used for ring setup. - -Ring: [ block 0 ] - [ frame 0 ] - [ frame 1 ] - [ block 1 ] - [ frame 2 ] - [ frame 3 ] - ... - [ block n ] - [ frame 2 * n ] - [ frame 2 * n + 1 ] - -The blocks are only visible to the kernel, from the point of view of user-space -the ring just contains the frames in a continuous memory zone. - -The ring parameters used for setting up the ring are defined as follows: - -struct nl_mmap_req { - unsigned int nm_block_size; - unsigned int nm_block_nr; - unsigned int nm_frame_size; - unsigned int nm_frame_nr; -}; - -Frames are grouped into blocks, where each block is a continuous region of memory -and holds nm_block_size / nm_frame_size frames. The total number of frames in -the ring is nm_frame_nr. The following invariants hold: - -- frames_per_block = nm_block_size / nm_frame_size - -- nm_frame_nr = frames_per_block * nm_block_nr - -Some parameters are constrained, specifically: - -- nm_block_size must be a multiple of the architectures memory page size. - The getpagesize() function can be used to get the page size. - -- nm_frame_size must be equal or larger to NL_MMAP_HDRLEN, IOW a frame must be - able to hold at least the frame header - -- nm_frame_size must be smaller or equal to nm_block_size - -- nm_frame_size must be a multiple of NL_MMAP_MSG_ALIGNMENT - -- nm_frame_nr must equal the actual number of frames as specified above. - -When the kernel can't allocate physically continuous memory for a ring block, -it will fall back to use physically discontinuous memory. This might affect -performance negatively, in order to avoid this the nm_frame_size parameter -should be chosen to be as small as possible for the required frame size and -the number of blocks should be increased instead. - -Ring frames ------------- - -Each frames contain a frame header, consisting of a synchronization word and some -meta-data, and the message itself. - -Frame: [ header message ] - -The frame header is defined as follows: - -struct nl_mmap_hdr { - unsigned int nm_status; - unsigned int nm_len; - __u32 nm_group; - /* credentials */ - __u32 nm_pid; - __u32 nm_uid; - __u32 nm_gid; -}; - -- nm_status is used for synchronizing processing between the kernel and user- - space and specifies ownership of the frame as well as the operation to perform - -- nm_len contains the length of the message contained in the data area - -- nm_group specified the destination multicast group of message - -- nm_pid, nm_uid and nm_gid contain the netlink pid, UID and GID of the sending - process. These values correspond to the data available using SOCK_PASSCRED in - the SCM_CREDENTIALS cmsg. - -The possible values in the status word are: - -- NL_MMAP_STATUS_UNUSED: - RX ring: frame belongs to the kernel and contains no message - for user-space. Approriate action is to invoke poll() - to wait for new messages. - - TX ring: frame belongs to user-space and can be used for - message construction. - -- NL_MMAP_STATUS_RESERVED: - RX ring only: frame is currently used by the kernel for message - construction and contains no valid message yet. - Appropriate action is to invoke poll() to wait for - new messages. - -- NL_MMAP_STATUS_VALID: - RX ring: frame contains a valid message. Approriate action is - to process the message and release the frame back to - the kernel by setting the status to - NL_MMAP_STATUS_UNUSED or queue the frame by setting the - status to NL_MMAP_STATUS_SKIP. - - TX ring: the frame contains a valid message from user-space to - be processed by the kernel. After completing processing - the kernel will release the frame back to user-space by - setting the status to NL_MMAP_STATUS_UNUSED. - -- NL_MMAP_STATUS_COPY: - RX ring only: a message is ready to be processed but could not be - stored in the ring, either because it exceeded the - frame size or because the originating subsystem does - not support memory mapped I/O. Appropriate action is - to invoke recvmsg() to receive the message and release - the frame back to the kernel by setting the status to - NL_MMAP_STATUS_UNUSED. - -- NL_MMAP_STATUS_SKIP: - RX ring only: user-space queued the message for later processing, but - processed some messages following it in the ring. The - kernel should skip this frame when looking for unused - frames. - -The data area of a frame begins at a offset of NL_MMAP_HDRLEN relative to the -frame header. - -TX limitations --------------- - -As of Jan 2015 the message is always copied from the ring frame to an -allocated buffer due to unresolved security concerns. -See commit 4682a0358639b29cf ("netlink: Always copy on mmap TX."). - -Example -------- - -Ring setup: - - unsigned int block_size = 16 * getpagesize(); - struct nl_mmap_req req = { - .nm_block_size = block_size, - .nm_block_nr = 64, - .nm_frame_size = 16384, - .nm_frame_nr = 64 * block_size / 16384, - }; - unsigned int ring_size; - void *rx_ring, *tx_ring; - - /* Configure ring parameters */ - if (setsockopt(fd, SOL_NETLINK, NETLINK_RX_RING, &req, sizeof(req)) < 0) - exit(1); - if (setsockopt(fd, SOL_NETLINK, NETLINK_TX_RING, &req, sizeof(req)) < 0) - exit(1) - - /* Calculate size of each individual ring */ - ring_size = req.nm_block_nr * req.nm_block_size; - - /* Map RX/TX rings. The TX ring is located after the RX ring */ - rx_ring = mmap(NULL, 2 * ring_size, PROT_READ | PROT_WRITE, - MAP_SHARED, fd, 0); - if ((long)rx_ring == -1L) - exit(1); - tx_ring = rx_ring + ring_size: - -Message reception: - -This example assumes some ring parameters of the ring setup are available. - - unsigned int frame_offset = 0; - struct nl_mmap_hdr *hdr; - struct nlmsghdr *nlh; - unsigned char buf[16384]; - ssize_t len; - - while (1) { - struct pollfd pfds[1]; - - pfds[0].fd = fd; - pfds[0].events = POLLIN | POLLERR; - pfds[0].revents = 0; - - if (poll(pfds, 1, -1) < 0 && errno != -EINTR) - exit(1); - - /* Check for errors. Error handling omitted */ - if (pfds[0].revents & POLLERR) - <handle error> - - /* If no new messages, poll again */ - if (!(pfds[0].revents & POLLIN)) - continue; - - /* Process all frames */ - while (1) { - /* Get next frame header */ - hdr = rx_ring + frame_offset; - - if (hdr->nm_status == NL_MMAP_STATUS_VALID) { - /* Regular memory mapped frame */ - nlh = (void *)hdr + NL_MMAP_HDRLEN; - len = hdr->nm_len; - - /* Release empty message immediately. May happen - * on error during message construction. - */ - if (len == 0) - goto release; - } else if (hdr->nm_status == NL_MMAP_STATUS_COPY) { - /* Frame queued to socket receive queue */ - len = recv(fd, buf, sizeof(buf), MSG_DONTWAIT); - if (len <= 0) - break; - nlh = buf; - } else - /* No more messages to process, continue polling */ - break; - - process_msg(nlh); -release: - /* Release frame back to the kernel */ - hdr->nm_status = NL_MMAP_STATUS_UNUSED; - - /* Advance frame offset to next frame */ - frame_offset = (frame_offset + frame_size) % ring_size; - } - } - -Message transmission: - -This example assumes some ring parameters of the ring setup are available. -A single message is constructed and transmitted, to send multiple messages -at once they would be constructed in consecutive frames before a final call -to sendto(). - - unsigned int frame_offset = 0; - struct nl_mmap_hdr *hdr; - struct nlmsghdr *nlh; - struct sockaddr_nl addr = { - .nl_family = AF_NETLINK, - }; - - hdr = tx_ring + frame_offset; - if (hdr->nm_status != NL_MMAP_STATUS_UNUSED) - /* No frame available. Use poll() to avoid. */ - exit(1); - - nlh = (void *)hdr + NL_MMAP_HDRLEN; - - /* Build message */ - build_message(nlh); - - /* Fill frame header: length and status need to be set */ - hdr->nm_len = nlh->nlmsg_len; - hdr->nm_status = NL_MMAP_STATUS_VALID; - - if (sendto(fd, NULL, 0, 0, &addr, sizeof(addr)) < 0) - exit(1); - - /* Advance frame offset to next frame */ - frame_offset = (frame_offset + frame_size) % ring_size; @@ -1,6 +1,6 @@ VERSION = 4 PATCHLEVEL = 4 -SUBLEVEL = 55 +SUBLEVEL = 59 EXTRAVERSION = NAME = Blurry Fish Butt @@ -146,7 +146,7 @@ PHONY += $(MAKECMDGOALS) sub-make $(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make @: -sub-make: FORCE +sub-make: $(Q)$(MAKE) -C $(KBUILD_OUTPUT) KBUILD_SRC=$(CURDIR) \ -f $(CURDIR)/Makefile $(filter-out _all sub-make,$(MAKECMDGOALS)) @@ -1005,7 +1005,7 @@ prepare1: prepare2 $(version_h) include/generated/utsrelease.h \ archprepare: archheaders archscripts prepare1 scripts_basic -prepare0: archprepare FORCE +prepare0: archprepare $(Q)$(MAKE) $(build)=. # All the preparing.. @@ -1050,7 +1050,7 @@ INSTALL_FW_PATH=$(INSTALL_MOD_PATH)/lib/firmware export INSTALL_FW_PATH PHONY += firmware_install -firmware_install: FORCE +firmware_install: @mkdir -p $(objtree)/firmware $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_install @@ -1070,7 +1070,7 @@ PHONY += archscripts archscripts: PHONY += __headers -__headers: $(version_h) scripts_basic asm-generic archheaders archscripts FORCE +__headers: $(version_h) scripts_basic asm-generic archheaders archscripts $(Q)$(MAKE) $(build)=scripts build_unifdef PHONY += headers_install_all diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg index f10371a981b7..67842e7007a8 100644 --- a/android/configs/android-base.cfg +++ b/android/configs/android-base.cfg @@ -1,17 +1,19 @@ # KEEP ALPHABETICALLY SORTED # CONFIG_DEVKMEM is not set # CONFIG_DEVMEM is not set +# CONFIG_FHANDLE is not set # CONFIG_INET_LRO is not set # CONFIG_MODULES is not set # CONFIG_OABI_COMPAT is not set # CONFIG_SYSVIPC is not set +# CONFIG_USELIB is not set CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_BINDER_DEVICES=binder,hwbinder,vndbinder CONFIG_ANDROID_LOW_MEMORY_KILLER=y CONFIG_ARMV8_DEPRECATED=y CONFIG_ASHMEM=y CONFIG_AUDIT=y -CONFIG_BLK_DEV_DM=y CONFIG_BLK_DEV_INITRD=y CONFIG_CGROUPS=y CONFIG_CGROUP_CPUACCT=y @@ -19,14 +21,15 @@ CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_SCHED=y CONFIG_CP15_BARRIER_EMULATION=y -CONFIG_DM_CRYPT=y -CONFIG_DM_VERITY=y -CONFIG_DM_VERITY_FEC=y +CONFIG_DEFAULT_SECURITY_SELINUX=y CONFIG_EMBEDDED=y CONFIG_FB=y CONFIG_HARDENED_USERCOPY=y CONFIG_HIGH_RES_TIMERS=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y CONFIG_INET6_AH=y +CONFIG_INET6_DIAG_DESTROY=y CONFIG_INET6_ESP=y CONFIG_INET6_IPCOMP=y CONFIG_INET=y @@ -42,7 +45,6 @@ CONFIG_IPV6=y CONFIG_IPV6_MIP6=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_OPTIMISTIC_DAD=y -CONFIG_IPV6_PRIVACY=y CONFIG_IPV6_ROUTER_PREF=y CONFIG_IPV6_ROUTE_INFO=y CONFIG_IP_ADVANCED_ROUTER=y @@ -64,6 +66,9 @@ CONFIG_IP_NF_TARGET_MASQUERADE=y CONFIG_IP_NF_TARGET_NETMAP=y CONFIG_IP_NF_TARGET_REDIRECT=y CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y CONFIG_NET=y CONFIG_NETDEVICES=y CONFIG_NETFILTER=y @@ -157,16 +162,16 @@ CONFIG_STAGING=y CONFIG_SWP_EMULATION=y CONFIG_SYNC=y CONFIG_TUN=y -CONFIG_UID_CPUTIME=y +CONFIG_UID_SYS_STATS=y CONFIG_UNIX=y -CONFIG_USB_GADGET=y CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_F_ACC=y +CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_MIDI=y CONFIG_USB_CONFIGFS_F_MTP=y CONFIG_USB_CONFIGFS_F_PTP=y -CONFIG_USB_CONFIGFS_F_ACC=y -CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y CONFIG_USB_CONFIGFS_UEVENT=y -CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_USB_GADGET=y CONFIG_USB_OTG_WAKELOCK=y CONFIG_XFRM_USER=y diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg index 70aaae17ad29..11df0892301b 100644 --- a/android/configs/android-recommended.cfg +++ b/android/configs/android-recommended.cfg @@ -7,16 +7,22 @@ # CONFIG_PM_WAKELOCKS_GC is not set # CONFIG_VT is not set CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ARM64_SW_TTBR0_PAN=y CONFIG_ARM_KERNMEM_PERMS=y CONFIG_ARM64_SW_TTBR0_PAN=y CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_BLK_DEV_DM=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_CC_STACKPROTECTOR_STRONG=y CONFIG_COMPACTION=y +CONFIG_CPU_SW_DOMAIN_PAN=y CONFIG_DEBUG_RODATA=y +CONFIG_DM_CRYPT=y CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y CONFIG_DRAGONRISE_FF=y CONFIG_ENABLE_DEFAULT_TRACERS=y CONFIG_EXT4_FS=y @@ -92,6 +98,7 @@ CONFIG_LOGIRUMBLEPAD2_FF=y CONFIG_LOGITECH_FF=y CONFIG_MD=y CONFIG_MEDIA_SUPPORT=y +CONFIG_MEMORY_STATE_TIME=y CONFIG_MSDOS_FS=y CONFIG_PANIC_TIMEOUT=5 CONFIG_PANTHERLORD_FF=y @@ -121,7 +128,6 @@ CONFIG_TIMER_STATS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_UHID=y -CONFIG_MEMORY_STATE_TIME=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_HIDDEV=y diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 1886a65eee87..588393412271 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -113,7 +113,7 @@ if ARM_DMA_USE_IOMMU config ARM_DMA_IOMMU_ALIGNMENT int "Maximum PAGE_SIZE order of alignment for DMA IOMMU buffers" range 4 9 - default 8 + default 9 help DMA mapping framework by default aligns all buffers to the smallest PAGE_SIZE order which is greater than or equal to the requested buffer diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index 74aefe4e616d..27f79c288c61 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -102,6 +102,28 @@ dtb-$(CONFIG_ARCH_MSM8996) += msm8996-v2-pmi8994-cdp.dtb \ apq8096-v3-pmi8996-mdm9x55-slimbus-mtp.dtb \ apq8096-v3-pmi8996-dragonboard.dtb +ifeq ($(CONFIG_BUILD_ARM64_DT_OVERLAY),y) +dtbo-$(CONFIG_ARCH_MSM8998) += \ + msm8998-cdp-overlay.dtbo \ + msm8998-mtp-overlay.dtbo \ + msm8998-v2-cdp-overlay.dtbo \ + msm8998-v2-mtp-overlay.dtbo \ + msm8998-v2.1-cdp-overlay.dtbo \ + msm8998-v2.1-mtp-overlay.dtbo \ + msm8998-qrd-overlay.dtbo \ + msm8998-qrd-vr1-overlay.dtbo \ + msm8998-qrd-skuk-overlay.dtbo + +msm8998-cdp-overlay.dtbo-base := msm8998.dtb +msm8998-mtp-overlay.dtbo-base := msm8998.dtb +msm8998-v2-cdp-overlay.dtbo-base := msm8998-v2.dtb +msm8998-v2-mtp-overlay.dtbo-base := msm8998-v2.dtb +msm8998-v2.1-cdp-overlay.dtbo-base := msm8998-v2.1.dtb +msm8998-v2.1-mtp-overlay.dtbo-base := msm8998-v2.1.dtb +msm8998-qrd-overlay.dtbo-base := msm8998-qrd.dtb +msm8998-qrd-vr1-overlay.dtbo-base := msm8998-qrd-vr1.dtb +msm8998-qrd-skuk-overlay.dtbo-base := msm8998-qrd-skuk.dtb +else dtb-$(CONFIG_ARCH_MSM8998) += msm8998-sim.dtb \ msm8998-rumi.dtb \ msm8998-cdp.dtb \ @@ -118,22 +140,24 @@ dtb-$(CONFIG_ARCH_MSM8998) += msm8998-sim.dtb \ msm8998-v2-qrd-vr1.dtb \ msm8998-v2-qrd-skuk-evt3.dtb \ msm8998-v2-qrd-skuk-hdk.dtb \ - apq8998-mtp.dtb \ - apq8998-cdp.dtb \ - apq8998-v2-mtp.dtb \ - apq8998-v2-cdp.dtb \ - apq8998-v2-qrd.dtb \ - apq8998-v2-qrd-skuk-hdk.dtb \ + apq8098-mtp.dtb \ + apq8098-cdp.dtb \ + apq8098-v2-mtp.dtb \ + apq8098-v2-cdp.dtb \ + apq8098-v2-qrd.dtb \ + apq8098-v2-qrd-skuk-hdk.dtb \ msm8998-v2.1-mtp.dtb \ + msm8998-v2.1-mtp-4k-display.dtb \ msm8998-v2.1-cdp.dtb \ msm8998-v2.1-qrd.dtb \ - apq8998-v2.1-mtp.dtb \ - apq8998-v2.1-cdp.dtb \ - apq8998-v2.1-qrd.dtb \ - apq8998-v2.1-mediabox.dtb \ + apq8098-v2.1-mtp.dtb \ + apq8098-v2.1-cdp.dtb \ + apq8098-v2.1-qrd.dtb \ + apq8098-v2.1-mediabox.dtb \ msm8998-v2.1-interposer-sdm660-cdp.dtb \ msm8998-v2.1-interposer-sdm660-mtp.dtb \ msm8998-v2.1-interposer-sdm660-qrd.dtb +endif dtb-$(CONFIG_ARCH_MSMHAMSTER) += msmhamster-rumi.dtb @@ -161,6 +185,7 @@ dtb-$(CONFIG_ARCH_SDM660) += sdm660-sim.dtb \ sda660-pm660a-cdp.dtb \ sda660-pm660a-mtp.dtb \ sda660-pm660a-rcm.dtb \ + sda660-pm660a-qrd-hdk.dtb \ sdm660-headset-jacktype-no-cdp.dtb \ sdm660-headset-jacktype-no-rcm.dtb \ sdm660-pm660a-headset-jacktype-no-cdp.dtb \ @@ -211,6 +236,7 @@ dtb-$(CONFIG_ARCH_SDM630) += sdm630-rumi.dtb \ sda630-pm660a-mtp.dtb \ sda630-pm660a-cdp.dtb \ sda630-pm660a-rcm.dtb \ + sda630-pm660a-qrd-hdk.dtb \ sdm630-headset-jacktype-no-cdp.dtb \ sdm630-headset-jacktype-no-rcm.dtb \ sdm630-pm660a-headset-jacktype-no-cdp.dtb \ @@ -218,6 +244,7 @@ dtb-$(CONFIG_ARCH_SDM630) += sdm630-rumi.dtb \ ifeq ($(CONFIG_ARM64),y) always := $(dtb-y) +always += $(dtbo-y) subdir-y := $(dts-dirs) else targets += dtbs @@ -228,4 +255,4 @@ $(obj)/../%.dtb: $(src)/%.dts FORCE dtbs: $(addprefix $(obj)/../,$(dtb-y)) endif -clean-files := *.dtb +clean-files := *.dtbo *.dtb diff --git a/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi b/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi index e74aded8c9e3..57a0467c6fc2 100644 --- a/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi +++ b/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi @@ -633,12 +633,13 @@ <&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>, <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>, <&dai_tert_tdm_rx_2>, <&dai_tert_tdm_rx_3>, - <&dai_tert_tdm_tx_0>, <&dai_tert_tdm_tx_1>, - <&dai_tert_tdm_tx_2>, <&dai_tert_tdm_tx_3>, - <&dai_quat_tdm_rx_0>, <&dai_quat_tdm_rx_1>, - <&dai_quat_tdm_rx_2>, <&dai_quat_tdm_rx_3>, - <&dai_quat_tdm_tx_0>, <&dai_quat_tdm_tx_1>, - <&dai_quat_tdm_tx_2>, <&dai_quat_tdm_tx_3>; + <&dai_tert_tdm_rx_4>, <&dai_tert_tdm_tx_0>, + <&dai_tert_tdm_tx_1>, <&dai_tert_tdm_tx_2>, + <&dai_tert_tdm_tx_3>, <&dai_quat_tdm_rx_0>, + <&dai_quat_tdm_rx_1>, <&dai_quat_tdm_rx_2>, + <&dai_quat_tdm_rx_3>, <&dai_quat_tdm_tx_0>, + <&dai_quat_tdm_tx_1>, <&dai_quat_tdm_tx_2>, + <&dai_quat_tdm_tx_3>; asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2", "msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.1", "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", @@ -650,12 +651,13 @@ "msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887", "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898", "msm-dai-q6-tdm.36900", "msm-dai-q6-tdm.36902", - "msm-dai-q6-tdm.36897", "msm-dai-q6-tdm.36899", - "msm-dai-q6-tdm.36901", "msm-dai-q6-tdm.36903", - "msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36914", - "msm-dai-q6-tdm.36916", "msm-dai-q6-tdm.36918", - "msm-dai-q6-tdm.36913", "msm-dai-q6-tdm.36915", - "msm-dai-q6-tdm.36917", "msm-dai-q6-tdm.36919"; + "msm-dai-q6-tdm.36904", "msm-dai-q6-tdm.36897", + "msm-dai-q6-tdm.36899", "msm-dai-q6-tdm.36901", + "msm-dai-q6-tdm.36903", "msm-dai-q6-tdm.36912", + "msm-dai-q6-tdm.36914", "msm-dai-q6-tdm.36916", + "msm-dai-q6-tdm.36918", "msm-dai-q6-tdm.36913", + "msm-dai-q6-tdm.36915", "msm-dai-q6-tdm.36917", + "msm-dai-q6-tdm.36919"; asoc-codec = <&stub_codec>; asoc-codec-names = "msm-stub-codec.1"; }; @@ -689,6 +691,25 @@ }; }; + qcom,msm-dai-tdm-tert-rx { + qcom,msm-cpudai-tdm-group-num-ports = <5>; + qcom,msm-cpudai-tdm-group-port-id = <36896 36898 36900 + 36902 36904>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&tert_tdm_dout_active>; + pinctrl-1 = <&tert_tdm_dout_sleep>; + dai_tert_tdm_rx_4: qcom,msm-dai-q6-tdm-tert-rx-4 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36904>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + 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>; + }; + }; + qcom,msm-dai-tdm-quat-rx { pinctrl-names = "default", "sleep"; pinctrl-0 = <&quat_tdm_dout_active>; diff --git a/arch/arm/boot/dts/qcom/apq8998-cdp.dts b/arch/arm/boot/dts/qcom/apq8098-cdp.dts index 8acd2dabe18a..c8011ff957c0 100644 --- a/arch/arm/boot/dts/qcom/apq8998-cdp.dts +++ b/arch/arm/boot/dts/qcom/apq8098-cdp.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -12,11 +12,11 @@ /dts-v1/; -#include "apq8998.dtsi" +#include "apq8098.dtsi" #include "msm8998-cdp.dtsi" / { - model = "Qualcomm Technologies, Inc. APQ 8998 CDP"; - compatible = "qcom,apq8998-cdp", "qcom,apq8998", "qcom,cdp"; + model = "Qualcomm Technologies, Inc. APQ 8098 CDP"; + compatible = "qcom,apq8098-cdp", "qcom,apq8098", "qcom,cdp"; qcom,board-id = <1 0>; }; diff --git a/arch/arm/boot/dts/qcom/apq8998-mtp.dts b/arch/arm/boot/dts/qcom/apq8098-mtp.dts index 5bed816f77d6..7af82a91e935 100644 --- a/arch/arm/boot/dts/qcom/apq8998-mtp.dts +++ b/arch/arm/boot/dts/qcom/apq8098-mtp.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -12,11 +12,11 @@ /dts-v1/; -#include "apq8998.dtsi" +#include "apq8098.dtsi" #include "msm8998-mtp.dtsi" / { - model = "Qualcomm Technologies, Inc. APQ 8998 MTP"; - compatible = "qcom,apq8998-mtp", "qcom,apq8998", "qcom,mtp"; + model = "Qualcomm Technologies, Inc. APQ 8098 MTP"; + compatible = "qcom,apq8098-mtp", "qcom,apq8098", "qcom,mtp"; qcom,board-id = <8 0>; }; diff --git a/arch/arm/boot/dts/qcom/apq8998-v2.1-cdp.dts b/arch/arm/boot/dts/qcom/apq8098-v2-cdp.dts index 94c6031854fa..b01c85bdbac6 100644 --- a/arch/arm/boot/dts/qcom/apq8998-v2.1-cdp.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2-cdp.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -12,11 +12,11 @@ /dts-v1/; -#include "apq8998-v2.1.dtsi" +#include "apq8098-v2.dtsi" #include "msm8998-cdp.dtsi" / { - model = "Qualcomm Technologies, Inc. APQ 8998 V2.1 CDP"; - compatible = "qcom,apq8998-cdp", "qcom,apq8998", "qcom,cdp"; + model = "Qualcomm Technologies, Inc. APQ 8098 V2 CDP"; + compatible = "qcom,apq8098-cdp", "qcom,apq8098", "qcom,cdp"; qcom,board-id = <1 0>; }; diff --git a/arch/arm/boot/dts/qcom/apq8998-v2-mtp.dts b/arch/arm/boot/dts/qcom/apq8098-v2-mtp.dts index 4dc735c8d182..a6e6f15bfa34 100644 --- a/arch/arm/boot/dts/qcom/apq8998-v2-mtp.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2-mtp.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -12,11 +12,11 @@ /dts-v1/; -#include "apq8998-v2.dtsi" +#include "apq8098-v2.dtsi" #include "msm8998-mtp.dtsi" / { - model = "Qualcomm Technologies, Inc. APQ 8998 V2 MTP"; - compatible = "qcom,apq8998-mtp", "qcom,apq8998", "qcom,mtp"; + model = "Qualcomm Technologies, Inc. APQ 8098 V2 MTP"; + compatible = "qcom,apq8098-mtp", "qcom,apq8098", "qcom,mtp"; qcom,board-id = <8 0>; }; diff --git a/arch/arm/boot/dts/qcom/apq8998-v2-qrd-skuk-hdk.dts b/arch/arm/boot/dts/qcom/apq8098-v2-qrd-skuk-hdk.dts index 6406fe52242d..7aeb8e36cca2 100644 --- a/arch/arm/boot/dts/qcom/apq8998-v2-qrd-skuk-hdk.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2-qrd-skuk-hdk.dts @@ -13,11 +13,11 @@ /dts-v1/; -#include "apq8998-v2.dtsi" +#include "apq8098-v2.dtsi" #include "msm8998-qrd-skuk-hdk.dtsi" / { - model = "Qualcomm Technologies, Inc. APQ 8998 SKUK HDK"; + model = "Qualcomm Technologies, Inc. APQ 8098 SKUK HDK"; compatible = "qcom,msm8998-qrd", "qcom,msm8998", "qcom,qrd"; qcom,board-id = <0x06000b 0x10>; }; diff --git a/arch/arm/boot/dts/qcom/apq8998-v2-qrd.dts b/arch/arm/boot/dts/qcom/apq8098-v2-qrd.dts index 4f7efa7b4357..0b808e47c915 100644 --- a/arch/arm/boot/dts/qcom/apq8998-v2-qrd.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2-qrd.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -12,11 +12,11 @@ /dts-v1/; -#include "apq8998-v2.dtsi" +#include "apq8098-v2.dtsi" #include "msm8998-qrd.dtsi" / { - model = "Qualcomm Technologies, Inc. APQ 8998 V2 QRD"; - compatible = "qcom,apq8998-qrd", "qcom,apq8998", "qcom,qrd"; + model = "Qualcomm Technologies, Inc. APQ 8098 V2 QRD"; + compatible = "qcom,apq8098-qrd", "qcom,apq8098", "qcom,qrd"; qcom,board-id = <11 0>; }; diff --git a/arch/arm/boot/dts/qcom/apq8998-v2-cdp.dts b/arch/arm/boot/dts/qcom/apq8098-v2.1-cdp.dts index 397892dbb540..e41a66eff103 100644 --- a/arch/arm/boot/dts/qcom/apq8998-v2-cdp.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2.1-cdp.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -12,11 +12,11 @@ /dts-v1/; -#include "apq8998-v2.dtsi" +#include "apq8098-v2.1.dtsi" #include "msm8998-cdp.dtsi" / { - model = "Qualcomm Technologies, Inc. APQ 8998 V2 CDP"; - compatible = "qcom,apq8998-cdp", "qcom,apq8998", "qcom,cdp"; + model = "Qualcomm Technologies, Inc. APQ 8098 V2.1 CDP"; + compatible = "qcom,apq8098-cdp", "qcom,apq8098", "qcom,cdp"; qcom,board-id = <1 0>; }; diff --git a/arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts b/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts index 9d4b1457f990..d9b1cb8741eb 100644 --- a/arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2.1-mediabox.dts @@ -12,12 +12,12 @@ /dts-v1/; -#include "apq8998-v2.1.dtsi" +#include "apq8098-v2.1.dtsi" #include "msm8998-cdp.dtsi" / { - model = "Qualcomm Technologies, Inc. APQ 8998 V2.1 mediabox"; - compatible = "qcom,apq8998-cdp", "qcom,apq8998", "qcom,cdp"; + model = "Qualcomm Technologies, Inc. APQ 8098 V2.1 mediabox"; + compatible = "qcom,apq8098-cdp", "qcom,apq8098", "qcom,cdp"; qcom,board-id = <8 1>; }; diff --git a/arch/arm/boot/dts/qcom/apq8998-v2.1-mtp.dts b/arch/arm/boot/dts/qcom/apq8098-v2.1-mtp.dts index fa4e28e515b5..5684e1849b0d 100644 --- a/arch/arm/boot/dts/qcom/apq8998-v2.1-mtp.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2.1-mtp.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -12,11 +12,11 @@ /dts-v1/; -#include "apq8998-v2.1.dtsi" +#include "apq8098-v2.1.dtsi" #include "msm8998-mtp.dtsi" / { - model = "Qualcomm Technologies, Inc. APQ 8998 V2.1 MTP"; - compatible = "qcom,apq8998-mtp", "qcom,apq8998", "qcom,mtp"; + model = "Qualcomm Technologies, Inc. APQ 8098 V2.1 MTP"; + compatible = "qcom,apq8098-mtp", "qcom,apq8098", "qcom,mtp"; qcom,board-id = <8 0>; }; diff --git a/arch/arm/boot/dts/qcom/apq8998-v2.1-qrd.dts b/arch/arm/boot/dts/qcom/apq8098-v2.1-qrd.dts index 6a91b966a9b7..0f315d7c893d 100644 --- a/arch/arm/boot/dts/qcom/apq8998-v2.1-qrd.dts +++ b/arch/arm/boot/dts/qcom/apq8098-v2.1-qrd.dts @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -12,11 +12,11 @@ /dts-v1/; -#include "apq8998-v2.1.dtsi" +#include "apq8098-v2.1.dtsi" #include "msm8998-qrd.dtsi" / { - model = "Qualcomm Technologies, Inc. APQ 8998 V2.1 QRD"; - compatible = "qcom,apq8998-qrd", "qcom,apq8998", "qcom,qrd"; + model = "Qualcomm Technologies, Inc. APQ 8098 V2.1 QRD"; + compatible = "qcom,apq8098-qrd", "qcom,apq8098", "qcom,qrd"; qcom,board-id = <11 0>; }; diff --git a/arch/arm/boot/dts/qcom/apq8998-v2.1.dtsi b/arch/arm/boot/dts/qcom/apq8098-v2.1.dtsi index c7d44816c7d6..a5fa81b71537 100644 --- a/arch/arm/boot/dts/qcom/apq8998-v2.1.dtsi +++ b/arch/arm/boot/dts/qcom/apq8098-v2.1.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -13,6 +13,6 @@ #include "msm8998-v2.1.dtsi" / { - model = "Qualcomm Technologies, Inc. APQ 8998 V2.1"; + model = "Qualcomm Technologies, Inc. APQ 8098 V2.1"; qcom,msm-id = <319 0x20001>; }; diff --git a/arch/arm/boot/dts/qcom/apq8998-v2.dtsi b/arch/arm/boot/dts/qcom/apq8098-v2.dtsi index 2be3db45bf37..e51e310f7131 100644 --- a/arch/arm/boot/dts/qcom/apq8998-v2.dtsi +++ b/arch/arm/boot/dts/qcom/apq8098-v2.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -13,6 +13,6 @@ #include "msm8998-v2.dtsi" / { - model = "Qualcomm Technologies, Inc. APQ 8998 V2"; + model = "Qualcomm Technologies, Inc. APQ 8098 V2"; qcom,msm-id = <319 0x20000>; }; diff --git a/arch/arm/boot/dts/qcom/apq8998.dtsi b/arch/arm/boot/dts/qcom/apq8098.dtsi index 99d3459e39ce..8cb0c6b17689 100644 --- a/arch/arm/boot/dts/qcom/apq8998.dtsi +++ b/arch/arm/boot/dts/qcom/apq8098.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -13,7 +13,7 @@ #include "msm8998.dtsi" / { - model = "Qualcomm Technologies, Inc. APQ 8998"; + model = "Qualcomm Technologies, Inc. APQ 8098"; qcom,msm-id = <319 0x10000>; }; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi index 62115cf6f98a..0ff02042600d 100644 --- a/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi +++ b/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi @@ -15,6 +15,7 @@ qcom,mdss-dsi-panel-name = "JDI a407 wqhd cmd mode dsi panel"; qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-panel-clockrate = <838600000>; qcom,mdss-dsi-virtual-channel-id = <0>; qcom,mdss-dsi-stream = <0>; qcom,mdss-dsi-panel-width = <720>; @@ -79,6 +80,8 @@ qcom,adjust-timer-wakeup-ms = <1>; qcom,mdss-dsi-reset-sequence = <1 20>, <0 10>, <1 20>; + qcom,dcs-cmd-by-left; + qcom,config-select = <&dsi_dual_jdi_a407_cmd_config0>; dsi_dual_jdi_a407_cmd_config0: config0 { diff --git a/arch/arm/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi index 49ae94e7a975..7d3e3d362946 100644 --- a/arch/arm/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi +++ b/arch/arm/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi @@ -52,8 +52,12 @@ 15 01 00 00 00 00 02 28 40 15 01 00 00 02 00 02 29 4f 15 01 00 00 00 00 02 fe 04 + 15 01 00 00 00 00 02 0a d8 + 15 01 00 00 00 00 02 0c e6 + 15 01 00 00 00 00 02 4e 20 15 01 00 00 00 00 02 4f 1b - 15 01 00 00 02 00 02 50 2f + 15 01 00 00 00 00 02 50 2f + 15 01 00 00 02 00 02 51 08 15 01 00 00 00 00 02 fe 09 15 01 00 00 00 00 02 00 08 15 01 00 00 00 00 02 01 08 diff --git a/arch/arm/boot/dts/qcom/msm-pm660.dtsi b/arch/arm/boot/dts/qcom/msm-pm660.dtsi index 5c0010d26a8a..9bdaedd72585 100644 --- a/arch/arm/boot/dts/qcom/msm-pm660.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pm660.dtsi @@ -41,6 +41,7 @@ interrupt-names = "kpdpwr", "resin", "resin-bark", "kpdpwr-resin-bark"; qcom,pon-dbc-delay = <15625>; + qcom,kpdpwr-sw-debounce; qcom,system-reset; qcom,store-hard-reset-reason; @@ -539,8 +540,10 @@ #address-cells = <1>; #size-cells = <1>; qcom,pmic-revid = <&pm660_revid>; - io-channels = <&pm660_rradc 0>; - io-channel-names = "rradc_batt_id"; + io-channels = <&pm660_rradc 0>, + <&pm660_rradc 7>; + io-channel-names = "rradc_batt_id", + "rradc_die_temp"; qcom,rradc-base = <0x4500>; qcom,fg-esr-timer-awake = <96>; qcom,fg-esr-timer-asleep = <256>; diff --git a/arch/arm/boot/dts/qcom/msm-pm660l.dtsi b/arch/arm/boot/dts/qcom/msm-pm660l.dtsi index fdc04b9726b4..679149a78833 100644 --- a/arch/arm/boot/dts/qcom/msm-pm660l.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pm660l.dtsi @@ -415,7 +415,9 @@ compatible = "qcom,qpnp-oledb-regulator"; #address-cells = <1>; #size-cells = <1>; + qcom,pmic-revid = <&pm660l_revid>; reg = <0xe000 0x100>; + qcom,pbs-client = <&pm660l_pbs>; label = "oledb"; regulator-name = "regulator-oledb"; @@ -463,6 +465,8 @@ qcom,qpnp-lab-slew-rate = <5000>; qcom,qpnp-lab-init-voltage = <4600000>; qcom,qpnp-lab-init-amoled-voltage = <4600000>; + + qcom,notify-lab-vreg-ok-sts; }; }; }; diff --git a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi index d007e8bcfc33..c1fa33f5ca32 100644 --- a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi @@ -616,12 +616,13 @@ <&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>, <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>, <&dai_tert_tdm_rx_2>, <&dai_tert_tdm_rx_3>, - <&dai_tert_tdm_tx_0>, <&dai_tert_tdm_tx_1>, - <&dai_tert_tdm_tx_2>, <&dai_tert_tdm_tx_3>, - <&dai_quat_tdm_rx_0>, <&dai_quat_tdm_rx_1>, - <&dai_quat_tdm_rx_2>, <&dai_quat_tdm_rx_3>, - <&dai_quat_tdm_tx_0>, <&dai_quat_tdm_tx_1>, - <&dai_quat_tdm_tx_2>, <&dai_quat_tdm_tx_3>; + <&dai_tert_tdm_rx_4>, <&dai_tert_tdm_tx_0>, + <&dai_tert_tdm_tx_1>, <&dai_tert_tdm_tx_2>, + <&dai_tert_tdm_tx_3>, <&dai_quat_tdm_rx_0>, + <&dai_quat_tdm_rx_1>, <&dai_quat_tdm_rx_2>, + <&dai_quat_tdm_rx_3>, <&dai_quat_tdm_tx_0>, + <&dai_quat_tdm_tx_1>, <&dai_quat_tdm_tx_2>, + <&dai_quat_tdm_tx_3>; asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2", "msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.1", "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", @@ -633,12 +634,13 @@ "msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887", "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898", "msm-dai-q6-tdm.36900", "msm-dai-q6-tdm.36902", - "msm-dai-q6-tdm.36897", "msm-dai-q6-tdm.36899", - "msm-dai-q6-tdm.36901", "msm-dai-q6-tdm.36903", - "msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36914", - "msm-dai-q6-tdm.36916", "msm-dai-q6-tdm.36918", - "msm-dai-q6-tdm.36913", "msm-dai-q6-tdm.36915", - "msm-dai-q6-tdm.36917", "msm-dai-q6-tdm.36919"; + "msm-dai-q6-tdm.36904", "msm-dai-q6-tdm.36897", + "msm-dai-q6-tdm.36899", "msm-dai-q6-tdm.36901", + "msm-dai-q6-tdm.36903", "msm-dai-q6-tdm.36912", + "msm-dai-q6-tdm.36914", "msm-dai-q6-tdm.36916", + "msm-dai-q6-tdm.36918", "msm-dai-q6-tdm.36913", + "msm-dai-q6-tdm.36915", "msm-dai-q6-tdm.36917", + "msm-dai-q6-tdm.36919"; asoc-codec = <&stub_codec>; asoc-codec-names = "msm-stub-codec.1"; }; @@ -678,9 +680,22 @@ }; qcom,msm-dai-tdm-tert-rx { + qcom,msm-cpudai-tdm-group-num-ports = <5>; + qcom,msm-cpudai-tdm-group-port-id = <36896 36898 36900 + 36902 36904>; pinctrl-names = "default", "sleep"; pinctrl-0 = <&tert_tdm_dout_active>; pinctrl-1 = <&tert_tdm_dout_sleep>; + dai_tert_tdm_rx_4: qcom,msm-dai-q6-tdm-tert-rx-4 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36904>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + 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>; + }; }; qcom,msm-dai-tdm-quat-rx { diff --git a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi index 7fffe81c7614..a5a6bad16176 100644 --- a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi @@ -637,12 +637,13 @@ <&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>, <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>, <&dai_tert_tdm_rx_2>, <&dai_tert_tdm_rx_3>, - <&dai_tert_tdm_tx_0>, <&dai_tert_tdm_tx_1>, - <&dai_tert_tdm_tx_2>, <&dai_tert_tdm_tx_3>, - <&dai_quat_tdm_rx_0>, <&dai_quat_tdm_rx_1>, - <&dai_quat_tdm_rx_2>, <&dai_quat_tdm_rx_3>, - <&dai_quat_tdm_tx_0>, <&dai_quat_tdm_tx_1>, - <&dai_quat_tdm_tx_2>, <&dai_quat_tdm_tx_3>; + <&dai_tert_tdm_rx_4>, <&dai_tert_tdm_tx_0>, + <&dai_tert_tdm_tx_1>, <&dai_tert_tdm_tx_2>, + <&dai_tert_tdm_tx_3>, <&dai_quat_tdm_rx_0>, + <&dai_quat_tdm_rx_1>, <&dai_quat_tdm_rx_2>, + <&dai_quat_tdm_rx_3>, <&dai_quat_tdm_tx_0>, + <&dai_quat_tdm_tx_1>, <&dai_quat_tdm_tx_2>, + <&dai_quat_tdm_tx_3>; asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2", "msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.1", "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", @@ -654,12 +655,13 @@ "msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887", "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898", "msm-dai-q6-tdm.36900", "msm-dai-q6-tdm.36902", - "msm-dai-q6-tdm.36897", "msm-dai-q6-tdm.36899", - "msm-dai-q6-tdm.36901", "msm-dai-q6-tdm.36903", - "msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36914", - "msm-dai-q6-tdm.36916", "msm-dai-q6-tdm.36918", - "msm-dai-q6-tdm.36913", "msm-dai-q6-tdm.36915", - "msm-dai-q6-tdm.36917", "msm-dai-q6-tdm.36919"; + "msm-dai-q6-tdm.36904", "msm-dai-q6-tdm.36897", + "msm-dai-q6-tdm.36899", "msm-dai-q6-tdm.36901", + "msm-dai-q6-tdm.36903", "msm-dai-q6-tdm.36912", + "msm-dai-q6-tdm.36914", "msm-dai-q6-tdm.36916", + "msm-dai-q6-tdm.36918", "msm-dai-q6-tdm.36913", + "msm-dai-q6-tdm.36915", "msm-dai-q6-tdm.36917", + "msm-dai-q6-tdm.36919"; asoc-codec = <&stub_codec>; asoc-codec-names = "msm-stub-codec.1"; }; @@ -709,6 +711,25 @@ }; }; + qcom,msm-dai-tdm-tert-rx { + qcom,msm-cpudai-tdm-group-num-ports = <5>; + qcom,msm-cpudai-tdm-group-port-id = <36896 36898 36900 + 36902 36904>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&tert_tdm_dout_active>; + pinctrl-1 = <&tert_tdm_dout_sleep>; + dai_tert_tdm_rx_4: qcom,msm-dai-q6-tdm-tert-rx-4 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36904>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + 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>; + }; + }; + qcom,msm-dai-tdm-quat-rx { pinctrl-names = "default", "sleep"; pinctrl-0 = <&quat_tdm_dout_active>; diff --git a/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi b/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi index 215608959dc5..27f692bb14af 100644 --- a/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -89,6 +89,13 @@ coresight-child-list = <&funnel_in0>; coresight-child-ports = <4>; + /* DRM settings */ + qcom,gpmu-tsens = <0x00060007>; + qcom,lm-max-power = <5448>; + qcom,gpmu-firmware = "a530v3_gpmu.fw2"; + qcom,gpmu-version = <1 0>; + qcom,zap-shader = "a530_zap"; + clocks = <&clock_gpu clk_gpu_gx_gfx3d_clk>, <&clock_gpu clk_gpu_ahb_clk>, <&clock_gpu clk_gpu_gx_rbbmtimer_clk>, diff --git a/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi index b6e6fb4193b4..c5b6e7d0a3dc 100644 --- a/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi @@ -542,12 +542,13 @@ <&dai_sec_tdm_tx_2>, <&dai_sec_tdm_tx_3>, <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>, <&dai_tert_tdm_rx_2>, <&dai_tert_tdm_rx_3>, - <&dai_tert_tdm_tx_0>, <&dai_tert_tdm_tx_1>, - <&dai_tert_tdm_tx_2>, <&dai_tert_tdm_tx_3>, - <&dai_quat_tdm_rx_0>, <&dai_quat_tdm_rx_1>, - <&dai_quat_tdm_rx_2>, <&dai_quat_tdm_rx_3>, - <&dai_quat_tdm_tx_0>, <&dai_quat_tdm_tx_1>, - <&dai_quat_tdm_tx_2>, <&dai_quat_tdm_tx_3>; + <&dai_tert_tdm_rx_4>, <&dai_tert_tdm_tx_0>, + <&dai_tert_tdm_tx_1>, <&dai_tert_tdm_tx_2>, + <&dai_tert_tdm_tx_3>, <&dai_quat_tdm_rx_0>, + <&dai_quat_tdm_rx_1>, <&dai_quat_tdm_rx_2>, + <&dai_quat_tdm_rx_3>, <&dai_quat_tdm_tx_0>, + <&dai_quat_tdm_tx_1>, <&dai_quat_tdm_tx_2>, + <&dai_quat_tdm_tx_3>; asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2", "msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.1", "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3", @@ -559,12 +560,13 @@ "msm-dai-q6-tdm.36885", "msm-dai-q6-tdm.36887", "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898", "msm-dai-q6-tdm.36900", "msm-dai-q6-tdm.36902", - "msm-dai-q6-tdm.36897", "msm-dai-q6-tdm.36899", - "msm-dai-q6-tdm.36901", "msm-dai-q6-tdm.36903", - "msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36914", - "msm-dai-q6-tdm.36916", "msm-dai-q6-tdm.36918", - "msm-dai-q6-tdm.36913", "msm-dai-q6-tdm.36915", - "msm-dai-q6-tdm.36917", "msm-dai-q6-tdm.36919"; + "msm-dai-q6-tdm.36904", "msm-dai-q6-tdm.36897", + "msm-dai-q6-tdm.36899", "msm-dai-q6-tdm.36901", + "msm-dai-q6-tdm.36903", "msm-dai-q6-tdm.36912", + "msm-dai-q6-tdm.36914", "msm-dai-q6-tdm.36916", + "msm-dai-q6-tdm.36918", "msm-dai-q6-tdm.36913", + "msm-dai-q6-tdm.36915", "msm-dai-q6-tdm.36917", + "msm-dai-q6-tdm.36919"; asoc-codec = <&stub_codec>; asoc-codec-names = "msm-stub-codec.1"; }; @@ -592,6 +594,25 @@ }; }; + qcom,msm-dai-tdm-tert-rx { + qcom,msm-cpudai-tdm-group-num-ports = <5>; + qcom,msm-cpudai-tdm-group-port-id = <36896 36898 36900 + 36902 36904>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&tert_tdm_dout_active>; + pinctrl-1 = <&tert_tdm_dout_sleep>; + dai_tert_tdm_rx_4: qcom,msm-dai-q6-tdm-tert-rx-4 { + compatible = "qcom,msm-dai-q6-tdm"; + qcom,msm-cpudai-tdm-dev-id = <36904>; + qcom,msm-cpudai-tdm-sync-mode = <1>; + 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>; + }; + }; + qcom,msm-dai-tdm-quat-rx { pinctrl-names = "default", "sleep"; pinctrl-0 = <&quat_tdm_dout_active>; diff --git a/arch/arm/boot/dts/qcom/msm8996-sde.dtsi b/arch/arm/boot/dts/qcom/msm8996-sde.dtsi index 8aebac3b0e22..cb33df82da0d 100644 --- a/arch/arm/boot/dts/qcom/msm8996-sde.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-sde.dtsi @@ -51,6 +51,8 @@ #interrupt-cells = <1>; iommus = <&mdp_smmu 0>; + gpus = <&msm_gpu>; + /* hw blocks */ qcom,sde-off = <0x1000>; qcom,sde-ctl-off = <0x2000 0x2200 0x2400 diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi index c73a2ff23369..398aee9d3ab8 100644 --- a/arch/arm/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996.dtsi @@ -1284,50 +1284,50 @@ 39 40 41 42 43>; #interrupt-cells = <1>; interrupt-map-mask = <0x0 0x0 0x0 0xffffffff>; - interrupt-map = <0x0 0x0 0x0 0 &intc 0 405 0 - 0x0 0x0 0x0 1 &intc 0 244 0 - 0x0 0x0 0x0 2 &intc 0 245 0 - 0x0 0x0 0x0 3 &intc 0 247 0 - 0x0 0x0 0x0 4 &intc 0 248 0 - 0x0 0x0 0x0 5 &intc 0 249 0 - 0x0 0x0 0x0 6 &intc 0 250 0 - 0x0 0x0 0x0 7 &intc 0 251 0 - 0x0 0x0 0x0 8 &intc 0 252 0 - 0x0 0x0 0x0 9 &intc 0 253 0 - 0x0 0x0 0x0 10 &intc 0 254 0 - 0x0 0x0 0x0 11 &intc 0 255 0 - 0x0 0x0 0x0 12 &intc 0 512 0 - 0x0 0x0 0x0 13 &intc 0 513 0 - 0x0 0x0 0x0 14 &intc 0 514 0 - 0x0 0x0 0x0 15 &intc 0 515 0 - 0x0 0x0 0x0 16 &intc 0 516 0 - 0x0 0x0 0x0 17 &intc 0 517 0 - 0x0 0x0 0x0 18 &intc 0 518 0 - 0x0 0x0 0x0 19 &intc 0 519 0 - 0x0 0x0 0x0 20 &intc 0 520 0 - 0x0 0x0 0x0 21 &intc 0 521 0 - 0x0 0x0 0x0 22 &intc 0 522 0 - 0x0 0x0 0x0 23 &intc 0 523 0 - 0x0 0x0 0x0 24 &intc 0 524 0 - 0x0 0x0 0x0 25 &intc 0 525 0 - 0x0 0x0 0x0 26 &intc 0 526 0 - 0x0 0x0 0x0 27 &intc 0 527 0 - 0x0 0x0 0x0 28 &intc 0 528 0 - 0x0 0x0 0x0 29 &intc 0 529 0 - 0x0 0x0 0x0 30 &intc 0 530 0 - 0x0 0x0 0x0 31 &intc 0 531 0 - 0x0 0x0 0x0 32 &intc 0 532 0 - 0x0 0x0 0x0 33 &intc 0 533 0 - 0x0 0x0 0x0 34 &intc 0 534 0 - 0x0 0x0 0x0 35 &intc 0 535 0 - 0x0 0x0 0x0 36 &intc 0 536 0 - 0x0 0x0 0x0 37 &intc 0 537 0 - 0x0 0x0 0x0 38 &intc 0 538 0 - 0x0 0x0 0x0 39 &intc 0 539 0 - 0x0 0x0 0x0 40 &intc 0 540 0 - 0x0 0x0 0x0 41 &intc 0 541 0 - 0x0 0x0 0x0 42 &intc 0 542 0 - 0x0 0x0 0x0 43 &intc 0 543 0>; + interrupt-map = <0x0 0x0 0x0 0 &intc 0 0 405 0 + 0x0 0x0 0x0 1 &intc 0 0 244 0 + 0x0 0x0 0x0 2 &intc 0 0 245 0 + 0x0 0x0 0x0 3 &intc 0 0 247 0 + 0x0 0x0 0x0 4 &intc 0 0 248 0 + 0x0 0x0 0x0 5 &intc 0 0 249 0 + 0x0 0x0 0x0 6 &intc 0 0 250 0 + 0x0 0x0 0x0 7 &intc 0 0 251 0 + 0x0 0x0 0x0 8 &intc 0 0 252 0 + 0x0 0x0 0x0 9 &intc 0 0 253 0 + 0x0 0x0 0x0 10 &intc 0 0 254 0 + 0x0 0x0 0x0 11 &intc 0 0 255 0 + 0x0 0x0 0x0 12 &intc 0 0 512 0 + 0x0 0x0 0x0 13 &intc 0 0 513 0 + 0x0 0x0 0x0 14 &intc 0 0 514 0 + 0x0 0x0 0x0 15 &intc 0 0 515 0 + 0x0 0x0 0x0 16 &intc 0 0 516 0 + 0x0 0x0 0x0 17 &intc 0 0 517 0 + 0x0 0x0 0x0 18 &intc 0 0 518 0 + 0x0 0x0 0x0 19 &intc 0 0 519 0 + 0x0 0x0 0x0 20 &intc 0 0 520 0 + 0x0 0x0 0x0 21 &intc 0 0 521 0 + 0x0 0x0 0x0 22 &intc 0 0 522 0 + 0x0 0x0 0x0 23 &intc 0 0 523 0 + 0x0 0x0 0x0 24 &intc 0 0 524 0 + 0x0 0x0 0x0 25 &intc 0 0 525 0 + 0x0 0x0 0x0 26 &intc 0 0 526 0 + 0x0 0x0 0x0 27 &intc 0 0 527 0 + 0x0 0x0 0x0 28 &intc 0 0 528 0 + 0x0 0x0 0x0 29 &intc 0 0 529 0 + 0x0 0x0 0x0 30 &intc 0 0 530 0 + 0x0 0x0 0x0 31 &intc 0 0 531 0 + 0x0 0x0 0x0 32 &intc 0 0 532 0 + 0x0 0x0 0x0 33 &intc 0 0 533 0 + 0x0 0x0 0x0 34 &intc 0 0 534 0 + 0x0 0x0 0x0 35 &intc 0 0 535 0 + 0x0 0x0 0x0 36 &intc 0 0 536 0 + 0x0 0x0 0x0 37 &intc 0 0 537 0 + 0x0 0x0 0x0 38 &intc 0 0 538 0 + 0x0 0x0 0x0 39 &intc 0 0 539 0 + 0x0 0x0 0x0 40 &intc 0 0 540 0 + 0x0 0x0 0x0 41 &intc 0 0 541 0 + 0x0 0x0 0x0 42 &intc 0 0 542 0 + 0x0 0x0 0x0 43 &intc 0 0 543 0>; interrupt-names = "int_msi", "int_a", "int_b", "int_c", "int_d", "int_pls_pme", "int_pme_legacy", "int_pls_err", @@ -1440,50 +1440,50 @@ 39 40 41 42 43>; #interrupt-cells = <1>; interrupt-map-mask = <0x0 0x0 0x0 0xffffffff>; - interrupt-map = <0x0 0x0 0x0 0 &intc 0 413 0 - 0x0 0x0 0x0 1 &intc 0 272 0 - 0x0 0x0 0x0 2 &intc 0 273 0 - 0x0 0x0 0x0 3 &intc 0 274 0 - 0x0 0x0 0x0 4 &intc 0 275 0 - 0x0 0x0 0x0 5 &intc 0 276 0 - 0x0 0x0 0x0 6 &intc 0 277 0 - 0x0 0x0 0x0 7 &intc 0 278 0 - 0x0 0x0 0x0 8 &intc 0 279 0 - 0x0 0x0 0x0 9 &intc 0 280 0 - 0x0 0x0 0x0 10 &intc 0 281 0 - 0x0 0x0 0x0 11 &intc 0 282 0 - 0x0 0x0 0x0 12 &intc 0 544 0 - 0x0 0x0 0x0 13 &intc 0 545 0 - 0x0 0x0 0x0 14 &intc 0 546 0 - 0x0 0x0 0x0 15 &intc 0 547 0 - 0x0 0x0 0x0 16 &intc 0 548 0 - 0x0 0x0 0x0 17 &intc 0 549 0 - 0x0 0x0 0x0 18 &intc 0 550 0 - 0x0 0x0 0x0 19 &intc 0 551 0 - 0x0 0x0 0x0 20 &intc 0 552 0 - 0x0 0x0 0x0 21 &intc 0 553 0 - 0x0 0x0 0x0 22 &intc 0 554 0 - 0x0 0x0 0x0 23 &intc 0 555 0 - 0x0 0x0 0x0 24 &intc 0 556 0 - 0x0 0x0 0x0 25 &intc 0 557 0 - 0x0 0x0 0x0 26 &intc 0 558 0 - 0x0 0x0 0x0 27 &intc 0 559 0 - 0x0 0x0 0x0 28 &intc 0 560 0 - 0x0 0x0 0x0 29 &intc 0 561 0 - 0x0 0x0 0x0 30 &intc 0 562 0 - 0x0 0x0 0x0 31 &intc 0 563 0 - 0x0 0x0 0x0 32 &intc 0 564 0 - 0x0 0x0 0x0 33 &intc 0 565 0 - 0x0 0x0 0x0 34 &intc 0 566 0 - 0x0 0x0 0x0 35 &intc 0 567 0 - 0x0 0x0 0x0 36 &intc 0 568 0 - 0x0 0x0 0x0 37 &intc 0 569 0 - 0x0 0x0 0x0 38 &intc 0 570 0 - 0x0 0x0 0x0 39 &intc 0 571 0 - 0x0 0x0 0x0 40 &intc 0 572 0 - 0x0 0x0 0x0 41 &intc 0 573 0 - 0x0 0x0 0x0 42 &intc 0 574 0 - 0x0 0x0 0x0 43 &intc 0 575 0>; + interrupt-map = <0x0 0x0 0x0 0 &intc 0 0 413 0 + 0x0 0x0 0x0 1 &intc 0 0 272 0 + 0x0 0x0 0x0 2 &intc 0 0 273 0 + 0x0 0x0 0x0 3 &intc 0 0 274 0 + 0x0 0x0 0x0 4 &intc 0 0 275 0 + 0x0 0x0 0x0 5 &intc 0 0 276 0 + 0x0 0x0 0x0 6 &intc 0 0 277 0 + 0x0 0x0 0x0 7 &intc 0 0 278 0 + 0x0 0x0 0x0 8 &intc 0 0 279 0 + 0x0 0x0 0x0 9 &intc 0 0 280 0 + 0x0 0x0 0x0 10 &intc 0 0 281 0 + 0x0 0x0 0x0 11 &intc 0 0 282 0 + 0x0 0x0 0x0 12 &intc 0 0 544 0 + 0x0 0x0 0x0 13 &intc 0 0 545 0 + 0x0 0x0 0x0 14 &intc 0 0 546 0 + 0x0 0x0 0x0 15 &intc 0 0 547 0 + 0x0 0x0 0x0 16 &intc 0 0 548 0 + 0x0 0x0 0x0 17 &intc 0 0 549 0 + 0x0 0x0 0x0 18 &intc 0 0 550 0 + 0x0 0x0 0x0 19 &intc 0 0 551 0 + 0x0 0x0 0x0 20 &intc 0 0 552 0 + 0x0 0x0 0x0 21 &intc 0 0 553 0 + 0x0 0x0 0x0 22 &intc 0 0 554 0 + 0x0 0x0 0x0 23 &intc 0 0 555 0 + 0x0 0x0 0x0 24 &intc 0 0 556 0 + 0x0 0x0 0x0 25 &intc 0 0 557 0 + 0x0 0x0 0x0 26 &intc 0 0 558 0 + 0x0 0x0 0x0 27 &intc 0 0 559 0 + 0x0 0x0 0x0 28 &intc 0 0 560 0 + 0x0 0x0 0x0 29 &intc 0 0 561 0 + 0x0 0x0 0x0 30 &intc 0 0 562 0 + 0x0 0x0 0x0 31 &intc 0 0 563 0 + 0x0 0x0 0x0 32 &intc 0 0 564 0 + 0x0 0x0 0x0 33 &intc 0 0 565 0 + 0x0 0x0 0x0 34 &intc 0 0 566 0 + 0x0 0x0 0x0 35 &intc 0 0 567 0 + 0x0 0x0 0x0 36 &intc 0 0 568 0 + 0x0 0x0 0x0 37 &intc 0 0 569 0 + 0x0 0x0 0x0 38 &intc 0 0 570 0 + 0x0 0x0 0x0 39 &intc 0 0 571 0 + 0x0 0x0 0x0 40 &intc 0 0 572 0 + 0x0 0x0 0x0 41 &intc 0 0 573 0 + 0x0 0x0 0x0 42 &intc 0 0 574 0 + 0x0 0x0 0x0 43 &intc 0 0 575 0>; interrupt-names = "int_msi", "int_a", "int_b", "int_c", "int_d", "int_pls_pme", "int_pme_legacy", "int_pls_err", @@ -1592,50 +1592,50 @@ 39 40 41 42 43>; #interrupt-cells = <1>; interrupt-map-mask = <0x0 0x0 0x0 0xffffffff>; - interrupt-map = <0x0 0x0 0x0 0 &intc 0 421 0 - 0x0 0x0 0x0 1 &intc 0 142 0 - 0x0 0x0 0x0 2 &intc 0 143 0 - 0x0 0x0 0x0 3 &intc 0 144 0 - 0x0 0x0 0x0 4 &intc 0 145 0 - 0x0 0x0 0x0 5 &intc 0 146 0 - 0x0 0x0 0x0 6 &intc 0 147 0 - 0x0 0x0 0x0 7 &intc 0 148 0 - 0x0 0x0 0x0 8 &intc 0 149 0 - 0x0 0x0 0x0 9 &intc 0 260 0 - 0x0 0x0 0x0 10 &intc 0 261 0 - 0x0 0x0 0x0 11 &intc 0 262 0 - 0x0 0x0 0x0 12 &intc 0 576 0 - 0x0 0x0 0x0 13 &intc 0 577 0 - 0x0 0x0 0x0 14 &intc 0 578 0 - 0x0 0x0 0x0 15 &intc 0 579 0 - 0x0 0x0 0x0 16 &intc 0 580 0 - 0x0 0x0 0x0 17 &intc 0 581 0 - 0x0 0x0 0x0 18 &intc 0 582 0 - 0x0 0x0 0x0 19 &intc 0 583 0 - 0x0 0x0 0x0 20 &intc 0 584 0 - 0x0 0x0 0x0 21 &intc 0 585 0 - 0x0 0x0 0x0 22 &intc 0 586 0 - 0x0 0x0 0x0 23 &intc 0 587 0 - 0x0 0x0 0x0 24 &intc 0 588 0 - 0x0 0x0 0x0 25 &intc 0 589 0 - 0x0 0x0 0x0 26 &intc 0 590 0 - 0x0 0x0 0x0 27 &intc 0 591 0 - 0x0 0x0 0x0 28 &intc 0 592 0 - 0x0 0x0 0x0 29 &intc 0 593 0 - 0x0 0x0 0x0 30 &intc 0 594 0 - 0x0 0x0 0x0 31 &intc 0 595 0 - 0x0 0x0 0x0 32 &intc 0 596 0 - 0x0 0x0 0x0 33 &intc 0 597 0 - 0x0 0x0 0x0 34 &intc 0 598 0 - 0x0 0x0 0x0 35 &intc 0 599 0 - 0x0 0x0 0x0 36 &intc 0 600 0 - 0x0 0x0 0x0 37 &intc 0 601 0 - 0x0 0x0 0x0 38 &intc 0 602 0 - 0x0 0x0 0x0 39 &intc 0 603 0 - 0x0 0x0 0x0 40 &intc 0 604 0 - 0x0 0x0 0x0 41 &intc 0 605 0 - 0x0 0x0 0x0 42 &intc 0 606 0 - 0x0 0x0 0x0 43 &intc 0 607 0>; + interrupt-map = <0x0 0x0 0x0 0 &intc 0 0 421 0 + 0x0 0x0 0x0 1 &intc 0 0 142 0 + 0x0 0x0 0x0 2 &intc 0 0 143 0 + 0x0 0x0 0x0 3 &intc 0 0 144 0 + 0x0 0x0 0x0 4 &intc 0 0 145 0 + 0x0 0x0 0x0 5 &intc 0 0 146 0 + 0x0 0x0 0x0 6 &intc 0 0 147 0 + 0x0 0x0 0x0 7 &intc 0 0 148 0 + 0x0 0x0 0x0 8 &intc 0 0 149 0 + 0x0 0x0 0x0 9 &intc 0 0 260 0 + 0x0 0x0 0x0 10 &intc 0 0 261 0 + 0x0 0x0 0x0 11 &intc 0 0 262 0 + 0x0 0x0 0x0 12 &intc 0 0 576 0 + 0x0 0x0 0x0 13 &intc 0 0 577 0 + 0x0 0x0 0x0 14 &intc 0 0 578 0 + 0x0 0x0 0x0 15 &intc 0 0 579 0 + 0x0 0x0 0x0 16 &intc 0 0 580 0 + 0x0 0x0 0x0 17 &intc 0 0 581 0 + 0x0 0x0 0x0 18 &intc 0 0 582 0 + 0x0 0x0 0x0 19 &intc 0 0 583 0 + 0x0 0x0 0x0 20 &intc 0 0 584 0 + 0x0 0x0 0x0 21 &intc 0 0 585 0 + 0x0 0x0 0x0 22 &intc 0 0 586 0 + 0x0 0x0 0x0 23 &intc 0 0 587 0 + 0x0 0x0 0x0 24 &intc 0 0 588 0 + 0x0 0x0 0x0 25 &intc 0 0 589 0 + 0x0 0x0 0x0 26 &intc 0 0 590 0 + 0x0 0x0 0x0 27 &intc 0 0 591 0 + 0x0 0x0 0x0 28 &intc 0 0 592 0 + 0x0 0x0 0x0 29 &intc 0 0 593 0 + 0x0 0x0 0x0 30 &intc 0 0 594 0 + 0x0 0x0 0x0 31 &intc 0 0 595 0 + 0x0 0x0 0x0 32 &intc 0 0 596 0 + 0x0 0x0 0x0 33 &intc 0 0 597 0 + 0x0 0x0 0x0 34 &intc 0 0 598 0 + 0x0 0x0 0x0 35 &intc 0 0 599 0 + 0x0 0x0 0x0 36 &intc 0 0 600 0 + 0x0 0x0 0x0 37 &intc 0 0 601 0 + 0x0 0x0 0x0 38 &intc 0 0 602 0 + 0x0 0x0 0x0 39 &intc 0 0 603 0 + 0x0 0x0 0x0 40 &intc 0 0 604 0 + 0x0 0x0 0x0 41 &intc 0 0 605 0 + 0x0 0x0 0x0 42 &intc 0 0 606 0 + 0x0 0x0 0x0 43 &intc 0 0 607 0>; interrupt-names = "int_msi", "int_a", "int_b", "int_c", "int_d", "int_pls_pme", "int_pme_legacy", "int_pls_err", diff --git a/arch/arm/boot/dts/qcom/msm8998-cdp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-cdp-overlay.dts new file mode 100644 index 000000000000..5fe0d0176618 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-cdp-overlay.dts @@ -0,0 +1,28 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; +/plugin/; + +#include <dt-bindings/clock/msm-clocks-8998.h> +#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h> +#include <dt-bindings/interrupt-controller/arm-gic.h> + +#include "msm8998-cdp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 v1 CDP"; + compatible = "qcom,msm8998-cdp", "qcom,msm8998", "qcom,cdp"; + qcom,msm-id = <292 0x0>; + qcom,board-id = <1 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8998-cdp.dtsi index 2b925250c5c0..9f57fa5127d7 100644 --- a/arch/arm/boot/dts/qcom/msm8998-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-cdp.dtsi @@ -10,9 +10,8 @@ * GNU General Public License for more details. */ -#include "msm8998-pinctrl.dtsi" #include "msm8998-camera-sensor-cdp.dtsi" -/ { +&vendor { bluetooth: bt_wcn3990 { compatible = "qca,wcn3990"; qca,bt-vdd-io-supply = <&pm8998_s3>; diff --git a/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi index d0d13332595a..64f377f1a576 100644 --- a/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-mdss-panels.dtsi @@ -10,8 +10,6 @@ * GNU General Public License for more details. */ -#include "dsi-panel-sim-video.dtsi" -#include "dsi-panel-sim-dualmipi-video.dtsi" #include "dsi-panel-nt35597-dualmipi-wqxga-video.dtsi" #include "dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi" #include "dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi" diff --git a/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi b/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi index 24186aca22be..2b9e13ea24f2 100644 --- a/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi @@ -502,7 +502,16 @@ qcom,msm_ext_disp = <&msm_ext_disp>; - qcom,aux-cfg-settings = [00 13 00 10 0a 26 0a 03 8b 03]; + qcom,aux-cfg0-settings = [1c 00]; + qcom,aux-cfg1-settings = [20 13 23 1d]; + qcom,aux-cfg2-settings = [24 00]; + qcom,aux-cfg3-settings = [28 00]; + qcom,aux-cfg4-settings = [2c 0a]; + qcom,aux-cfg5-settings = [30 26]; + qcom,aux-cfg6-settings = [34 0a]; + qcom,aux-cfg7-settings = [38 03]; + qcom,aux-cfg8-settings = [3c bb]; + qcom,aux-cfg9-settings = [40 03]; qcom,logical2physical-lane-map = [02 03 01 00]; qcom,core-supply-entries { diff --git a/arch/arm/boot/dts/qcom/msm8998-mtp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-mtp-overlay.dts new file mode 100644 index 000000000000..8525d781aae2 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-mtp-overlay.dts @@ -0,0 +1,28 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; +/plugin/; + +#include <dt-bindings/clock/msm-clocks-8998.h> +#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h> +#include <dt-bindings/interrupt-controller/arm-gic.h> + +#include "msm8998-mtp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 v1 MTP"; + compatible = "qcom,msm8998-mtp", "qcom,msm8998", "qcom,mtp"; + qcom,msm-id = <292 0x0>; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi index f7dcfc7c149f..859e77679e44 100644 --- a/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi @@ -11,9 +11,8 @@ */ #include <dt-bindings/interrupt-controller/irq.h> -#include "msm8998-pinctrl.dtsi" #include "msm8998-camera-sensor-mtp.dtsi" -/ { +&vendor { bluetooth: bt_wcn3990 { compatible = "qca,wcn3990"; qca,bt-vdd-io-supply = <&pm8998_s3>; @@ -586,7 +585,7 @@ }; }; -/{ +&vendor { mtp_batterydata: qcom,battery-data { qcom,batt-id-range-pct = <15>; #include "fg-gen3-batterydata-itech-3000mah.dtsi" diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-qrd-overlay.dts new file mode 100644 index 000000000000..55255261a827 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-qrd-overlay.dts @@ -0,0 +1,27 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; +/plugin/; + +#include <dt-bindings/clock/msm-clocks-8998.h> +#include <dt-bindings/interrupt-controller/irq.h> + +#include "msm8998-qrd.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 QRD"; + compatible = "qcom,msm8998-qrd", "qcom,msm8998", "qcom,qrd"; + qcom,msm-id = <292 0x0>; + qcom,board-id = <11 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-skuk-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-qrd-skuk-overlay.dts new file mode 100644 index 000000000000..408a067dbeee --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-qrd-skuk-overlay.dts @@ -0,0 +1,27 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; +/plugin/; + +#include <dt-bindings/clock/msm-clocks-8998.h> +#include <dt-bindings/interrupt-controller/irq.h> + +#include "msm8998-qrd-skuk.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 QRD SKUK"; + compatible = "qcom,msm8998-qrd", "qcom,msm8998", "qcom,qrd"; + qcom,msm-id = <292 0x0>; + qcom,board-id = <0x01000b 0x80>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-skuk.dtsi b/arch/arm/boot/dts/qcom/msm8998-qrd-skuk.dtsi index bc87b375ff50..97c4c5b1d455 100644 --- a/arch/arm/boot/dts/qcom/msm8998-qrd-skuk.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-qrd-skuk.dtsi @@ -289,7 +289,7 @@ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; }; -/{ +&vendor { qrd_batterydata: qcom,battery-data { qcom,batt-id-range-pct = <15>; #include "fg-gen3-batterydata-qrd-skuk-4v4-3000mah.dtsi" diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-vr1-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-qrd-vr1-overlay.dts new file mode 100644 index 000000000000..ff0e24dd0371 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-qrd-vr1-overlay.dts @@ -0,0 +1,27 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; +/plugin/; + +#include <dt-bindings/clock/msm-clocks-8998.h> +#include <dt-bindings/interrupt-controller/irq.h> + +#include "msm8998-qrd-vr1.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 QRD VR1 Board"; + compatible = "qcom,msm8998-qrd", "qcom,msm8998", "qcom,qrd"; + qcom,msm-id = <292 0x0>; + qcom,board-id = <0x02000b 0x80>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-vr1.dtsi b/arch/arm/boot/dts/qcom/msm8998-qrd-vr1.dtsi index aa95872da385..0d0c66d7f26e 100644 --- a/arch/arm/boot/dts/qcom/msm8998-qrd-vr1.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-qrd-vr1.dtsi @@ -219,7 +219,7 @@ }; }; -/{ +&vendor { qrd_batterydata: qcom,battery-data { qcom,batt-id-range-pct = <15>; #include "fg-gen3-batterydata-qrd-skuk-4v4-3000mah.dtsi" diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd.dtsi b/arch/arm/boot/dts/qcom/msm8998-qrd.dtsi index 1c2db6833bde..a3eb3e5ab0d0 100644 --- a/arch/arm/boot/dts/qcom/msm8998-qrd.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-qrd.dtsi @@ -539,7 +539,7 @@ }; }; -/{ +&vendor { qrd_batterydata: qcom,battery-data { qcom,batt-id-range-pct = <15>; #include "fg-gen3-batterydata-itech-3000mah.dtsi" diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-cdp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-v2-cdp-overlay.dts new file mode 100644 index 000000000000..c602459815b2 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2-cdp-overlay.dts @@ -0,0 +1,28 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; +/plugin/; + +#include <dt-bindings/clock/msm-clocks-8998.h> +#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h> +#include <dt-bindings/interrupt-controller/arm-gic.h> + +#include "msm8998-cdp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 v2 CDP"; + compatible = "qcom,msm8998-cdp", "qcom,msm8998", "qcom,cdp"; + qcom,msm-id = <292 0x20000>; + qcom,board-id = <1 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-mtp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-v2-mtp-overlay.dts new file mode 100644 index 000000000000..660fc4d51f9f --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2-mtp-overlay.dts @@ -0,0 +1,28 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; +/plugin/; + +#include <dt-bindings/clock/msm-clocks-8998.h> +#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h> +#include <dt-bindings/interrupt-controller/arm-gic.h> + +#include "msm8998-mtp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 v2 MTP"; + compatible = "qcom,msm8998-mtp", "qcom,msm8998", "qcom,mtp"; + qcom,msm-id = <292 0x20000>; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-cdp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-cdp-overlay.dts new file mode 100644 index 000000000000..47c55a045017 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-cdp-overlay.dts @@ -0,0 +1,28 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; +/plugin/; + +#include <dt-bindings/clock/msm-clocks-8998.h> +#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h> +#include <dt-bindings/interrupt-controller/arm-gic.h> + +#include "msm8998-cdp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 v2.1 CDP"; + compatible = "qcom,msm8998-cdp", "qcom,msm8998", "qcom,cdp"; + qcom,msm-id = <292 0x20001>; + qcom,board-id = <1 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts new file mode 100644 index 000000000000..7d537aa35533 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-4k-display.dts @@ -0,0 +1,51 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This 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 "msm8998-v2.1.dtsi" +#include "msm8998-mtp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 v2.1 MTP, 4k display"; + compatible = "qcom,msm8998-mtp", "qcom,msm8998", "qcom,mtp"; + qcom,board-id = <8 4>; +}; + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dsi { + hw-config = "split_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_sharp_4k_dsc_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-reset-gpio = <&tlmm 94 0>; + qcom,platform-te-gpio = <&tlmm 10 0>; + qcom,panel-mode-gpio = <&tlmm 91 0>; +}; + +&mdss_dsi1 { + qcom,dsi-pref-prim-pan = <&dsi_sharp_4k_dsc_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-reset-gpio = <&tlmm 94 0>; + qcom,platform-te-gpio = <&tlmm 10 0>; + qcom,panel-mode-gpio = <&tlmm 91 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-overlay.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-overlay.dts new file mode 100644 index 000000000000..54a0e98cdce7 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1-mtp-overlay.dts @@ -0,0 +1,28 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; +/plugin/; + +#include <dt-bindings/clock/msm-clocks-8998.h> +#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h> +#include <dt-bindings/interrupt-controller/arm-gic.h> + +#include "msm8998-mtp.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 v2.1 MTP"; + compatible = "qcom,msm8998-mtp", "qcom,msm8998", "qcom,mtp"; + qcom,msm-id = <292 0x20001>; + qcom,board-id = <8 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.1.dts b/arch/arm/boot/dts/qcom/msm8998-v2.1.dts new file mode 100644 index 000000000000..64559c71307f --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2.1.dts @@ -0,0 +1,23 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This 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 <dt-bindings/clock/msm-clocks-8998.h> +#include "msm8998-v2.1.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 v2.1 SoC"; + compatible = "qcom,msm8998"; + qcom,board-id = <0 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.dts b/arch/arm/boot/dts/qcom/msm8998-v2.dts new file mode 100644 index 000000000000..f1b5af9f14c8 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998-v2.dts @@ -0,0 +1,23 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This 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 <dt-bindings/clock/msm-clocks-8998.h> +#include "msm8998-v2.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 v2 SoC"; + compatible = "qcom,msm8998"; + qcom,board-id = <0 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.dtsi b/arch/arm/boot/dts/qcom/msm8998-v2.dtsi index 348faf9b69c3..b2f30de94bbc 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-v2.dtsi @@ -591,53 +591,53 @@ qcom,cpr-open-loop-voltage-adjustment = /* Speed bin 0 */ - <(-4000) (-4000) (-4000) (-4000) (-4000) - (-4000) (-4000) (-4000) (-8000) (-8000) - (-8000) (-12000) (-12000) (-12000) (-12000) + < 0 0 0 0 0 + 0 0 0 0 0 + 0 (-12000) (-12000) (-12000) (-12000) (-12000) (-16000) (-16000) (-20000) (-24000) (-28000) (-28000)>, /* Speed bin 1 */ - <(-4000) (-4000) (-4000) (-4000) (-4000) - (-4000) (-4000) (-4000) (-8000) (-8000) - (-8000) (-12000) (-12000) (-12000) (-12000) + < 0 0 0 0 0 + 0 0 0 0 0 + 0 (-12000) (-12000) (-12000) (-12000) (-12000) (-16000) (-16000) (-20000) (-24000) (-28000) (-28000)>, /* Speed bin 2 */ - <(-4000) (-4000) (-4000) (-4000) (-4000) - (-4000) (-4000) (-4000) (-8000) (-8000) - (-8000) (-12000) (-12000) (-12000) (-12000) + < 0 0 0 0 0 + 0 0 0 0 0 + 0 (-12000) (-12000) (-12000) (-12000) (-12000) (-16000) (-16000) (-20000) (-24000) (-28000) (-28000)>, /* Speed bin 3 */ - <(-4000) (-4000) (-4000) (-4000) (-4000) - (-4000) (-4000) (-4000) (-8000) (-8000) - (-8000) (-12000) (-12000) (-12000) (-12000) + < 0 0 0 0 0 + 0 0 0 0 0 + 0 (-12000) (-12000) (-12000) (-12000) (-12000) (-16000) (-16000) (-20000) (-24000) (-28000) (-28000)>; qcom,cpr-closed-loop-voltage-adjustment = /* Speed bin 0 */ - <(-5000) (-5000) (-5000) (-5000) (-5000) - (-5000) (-5000) (-5000) (-7000) (-8000) - (-10000) (-10000) (-11000) (-12000) (-13000) + < 0 0 0 0 0 + 0 0 0 0 0 + 0 (-10000) (-11000) (-12000) (-13000) (-14000) (-14000) (-15000) (-21000) (-24000) (-26000) (-28000)>, /* Speed bin 1 */ - <(-5000) (-5000) (-5000) (-5000) (-5000) - (-5000) (-5000) (-5000) (-7000) (-8000) - (-10000) (-10000) (-11000) (-12000) (-13000) + < 0 0 0 0 0 + 0 0 0 0 0 + 0 (-10000) (-11000) (-12000) (-13000) (-14000) (-14000) (-15000) (-21000) (-24000) (-26000) (-28000)>, /* Speed bin 2 */ - <(-5000) (-5000) (-5000) (-5000) (-5000) - (-5000) (-5000) (-5000) (-7000) (-8000) - (-10000) (-10000) (-11000) (-12000) (-13000) + < 0 0 0 0 0 + 0 0 0 0 0 + 0 (-10000) (-11000) (-12000) (-13000) (-14000) (-14000) (-15000) (-21000) (-24000) (-26000) (-28000)>, /* Speed bin 3 */ - <(-5000) (-5000) (-5000) (-5000) (-5000) - (-5000) (-5000) (-5000) (-7000) (-8000) - (-10000) (-10000) (-11000) (-12000) (-13000) + < 0 0 0 0 0 + 0 0 0 0 0 + 0 (-10000) (-11000) (-12000) (-13000) (-14000) (-14000) (-15000) (-21000) (-24000) (-26000) (-28000)>; @@ -926,65 +926,65 @@ qcom,cpr-open-loop-voltage-adjustment = /* Speed bin 0 */ - <(-4000) (-4000) (-4000) (-4000) (-4000) - (-4000) (-4000) (-4000) (-8000) (-8000) - (-8000) (-8000) (-12000) (-12000) (-12000) + < 0 0 0 0 0 + 0 0 0 0 0 + 0 (-8000) (-12000) (-12000) (-12000) (-12000) (-12000) (-12000) (-16000) (-16000) - (-20000) (-20000) (-24000) (-24000) (-24000) + (-20000) (-16000) (-16000) (-16000) (-12000) (-28000) (-28000) (-28000) (-28000) (-28000) (-28000) (-28000)>, /* Speed bin 1 */ - <(-4000) (-4000) (-4000) (-4000) (-4000) - (-4000) (-4000) (-4000) (-8000) (-8000) - (-8000) (-8000) (-12000) (-12000) (-12000) + < 0 0 0 0 0 + 0 0 0 0 0 + 0 (-8000) (-12000) (-12000) (-12000) (-12000) (-12000) (-12000) (-16000) (-16000) - (-20000) (-20000) (-24000) (-24000) (-28000) + (-20000) (-16000) (-16000) (-16000) (-16000) (-28000)>, /* Speed bin 2 */ - <(-4000) (-4000) (-4000) (-4000) (-4000) - (-4000) (-4000) (-4000) (-8000) (-8000) - (-8000) (-8000) (-12000) (-12000) (-12000) + < 0 0 0 0 0 + 0 0 0 0 0 + 0 (-8000) (-12000) (-12000) (-12000) (-12000) (-12000) (-12000) (-16000) (-16000) - (-20000) (-20000) (-24000) (-24000) (-24000) + (-20000) (-16000) (-16000) (-16000) (-12000) (-28000) (-28000) (-28000) (-28000) (-28000)>, /* Speed bin 3 */ - <(-4000) (-4000) (-4000) (-4000) (-4000) - (-4000) (-4000) (-4000) (-8000) (-8000) - (-8000) (-8000) (-12000) (-12000) (-12000) + < 0 0 0 0 0 + 0 0 0 0 0 + 0 (-8000) (-12000) (-12000) (-12000) (-12000) (-12000) (-12000) (-16000) (-16000) - (-20000) (-20000) (-24000) (-24000) (-24000) + (-20000) (-16000) (-16000) (-16000) (-12000) (-28000) (-28000) (-28000) (-28000) (-28000) (-28000)>; qcom,cpr-closed-loop-voltage-adjustment = /* Speed bin 0 */ - <(-5000) (-5000) (-5000) (-5000) (-5000) - (-5000) (-5000) (-5000) (-6000) (-7000) - (-9000) (-10000) (-10000) (-11000) (-12000) + < 0 0 0 0 0 + 0 0 0 0 0 + 0 (-10000) (-10000) (-11000) (-12000) (-12000) (-13000) (-14000) (-14000) (-15000) - (-18000) (-21000) (-24000) (-25000) (-25000) + (-16000) (-16000) (-17000) (-15000) (-13000) (-26000) (-26000) (-27000) (-27000) (-28000) (-28000) (-28000)>, /* Speed bin 1 */ - <(-5000) (-5000) (-5000) (-5000) (-5000) - (-5000) (-5000) (-5000) (-6000) (-7000) - (-9000) (-10000) (-10000) (-11000) (-12000) + < 0 0 0 0 0 + 0 0 0 0 0 + 0 (-10000) (-10000) (-11000) (-12000) (-12000) (-13000) (-14000) (-14000) (-15000) - (-18000) (-21000) (-24000) (-26000) (-27000) + (-16000) (-16000) (-17000) (-16000) (-15000) (-28000)>, /* Speed bin 2 */ - <(-5000) (-5000) (-5000) (-5000) (-5000) - (-5000) (-5000) (-5000) (-6000) (-7000) - (-9000) (-10000) (-10000) (-11000) (-12000) + < 0 0 0 0 0 + 0 0 0 0 0 + 0 (-10000) (-10000) (-11000) (-12000) (-12000) (-13000) (-14000) (-14000) (-15000) - (-18000) (-21000) (-24000) (-25000) (-26000) + (-16000) (-16000) (-17000) (-15000) (-14000) (-27000) (-27000) (-28000) (-28000) (-28000)>, /* Speed bin 3 */ - <(-5000) (-5000) (-5000) (-5000) (-5000) - (-5000) (-5000) (-5000) (-6000) (-7000) - (-9000) (-10000) (-10000) (-11000) (-12000) + < 0 0 0 0 0 + 0 0 0 0 0 + 0 (-10000) (-10000) (-11000) (-12000) (-12000) (-13000) (-14000) (-14000) (-15000) - (-18000) (-21000) (-24000) (-25000) (-26000) + (-16000) (-16000) (-17000) (-15000) (-14000) (-26000) (-27000) (-27000) (-28000) (-28000) (-28000)>; diff --git a/arch/arm/boot/dts/qcom/msm8998.dts b/arch/arm/boot/dts/qcom/msm8998.dts new file mode 100644 index 000000000000..b340b6de4c00 --- /dev/null +++ b/arch/arm/boot/dts/qcom/msm8998.dts @@ -0,0 +1,23 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This 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 <dt-bindings/clock/msm-clocks-8998.h> +#include "msm8998.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 v1 SoC"; + compatible = "qcom,msm8998"; + qcom,board-id = <0 0>; +}; diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi index 7a0ed2497a7e..9b5092cf7f14 100644 --- a/arch/arm/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998.dtsi @@ -275,6 +275,13 @@ soc: soc { }; + vendor: vendor { + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0 0 0xffffffff>; + compatible = "simple-bus"; + }; + reserved-memory { #address-cells = <2>; #size-cells = <2>; @@ -434,6 +441,7 @@ interrupts = <GIC_SPI 326 IRQ_TYPE_NONE>; qcom,ee = <0>; qcom,channel = <0>; + qcom,reserved-chan = <511>; #address-cells = <2>; #size-cells = <0>; interrupt-controller; diff --git a/arch/arm/boot/dts/qcom/sda630-pm660a-qrd-hdk.dts b/arch/arm/boot/dts/qcom/sda630-pm660a-qrd-hdk.dts new file mode 100644 index 000000000000..480a69601541 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sda630-pm660a-qrd-hdk.dts @@ -0,0 +1,85 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This 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 "sda630.dtsi" +#include "sdm630-qrd.dtsi" +#include "msm-pm660a.dtsi" +#include "sdm660-internal-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 630 PM660 + PM660A QRD HDK630"; + compatible = "qcom,sda630-qrd", "qcom,sda630", "qcom,qrd"; + qcom,board-id = <0x0006000b 0x00>; + qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; +}; + +&int_codec { + qcom,model = "sdm660-snd-card-skush"; + /delete-property/ qcom,us-euro-gpios; + qcom,audio-routing = + "RX_BIAS", "INT_MCLK0", + "SPK_RX_BIAS", "INT_MCLK0", + "INT_LDO_H", "INT_MCLK0", + "MIC BIAS External2", "Headset Mic", + "AMIC2", "MIC BIAS External2", + "MIC BIAS External", "Digital Mic1", + "DMIC1", "MIC BIAS External", + "MIC BIAS External", "Digital Mic3", + "DMIC3", "MIC BIAS External", + "MIC BIAS External", "Digital Mic4", + "DMIC4", "MIC BIAS External", + "SpkrLeft IN", "SPK1 OUT", + "PDM_IN_RX1", "PDM_OUT_RX1", + "PDM_IN_RX2", "PDM_OUT_RX2", + "PDM_IN_RX3", "PDM_OUT_RX3", + "ADC1_IN", "ADC1_OUT", + "ADC2_IN", "ADC2_OUT", + "ADC3_IN", "ADC3_OUT"; + qcom,wsa-max-devs = <1>; + qcom,wsa-devs = <&wsa881x_211_en>, <&wsa881x_213_en>; + qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrLeft"; +}; + +&pm660a_oledb { + status = "okay"; + qcom,oledb-default-voltage-mv = <6400>; +}; + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dsi { + hw-config = "single_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + oledb-supply = <&pm660a_oledb>; + lab-supply = <&lab_regulator>; + ibb-supply = <&ibb_regulator>; + qcom,platform-reset-gpio = <&tlmm 53 0>; + qcom,platform-te-gpio = <&tlmm 59 0>; +}; + +&dsi_rm67195_amoled_fhd_cmd { + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <255>; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>; +}; diff --git a/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts b/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts new file mode 100644 index 000000000000..a767853cd829 --- /dev/null +++ b/arch/arm/boot/dts/qcom/sda660-pm660a-qrd-hdk.dts @@ -0,0 +1,56 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This 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 "sda660.dtsi" +#include "sdm660-qrd.dtsi" +#include "msm-pm660a.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDA 660 PM660 + PM660A QRD HDK660"; + compatible = "qcom,sda660-qrd", "qcom,sda660", "qcom,qrd"; + qcom,board-id = <0x0006000b 0>; + qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; +}; + +&pm660a_oledb { + status = "okay"; + qcom,oledb-default-voltage-mv = <6400>; +}; + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dsi { + hw-config = "single_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + lab-supply = <&lab_regulator>; + ibb-supply = <&ibb_regulator>; + qcom,platform-reset-gpio = <&tlmm 53 0>; + qcom,platform-te-gpio = <&tlmm 59 0>; +}; + +&dsi_rm67195_amoled_fhd_cmd { + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <255>; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm630-cdp.dtsi b/arch/arm/boot/dts/qcom/sdm630-cdp.dtsi index 55e6943bf327..fd109450ad81 100644 --- a/arch/arm/boot/dts/qcom/sdm630-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-cdp.dtsi @@ -192,3 +192,30 @@ qcom,partial-update-enabled = "single_roi"; qcom,panel-roi-alignment = <2 2 4 2 1080 2>; }; + +&pm660l_pwm_4 { + qcom,dtest-line = <2>; /* DTEST2 */ + qcom,dtest-output = <2>; /* OUTPUT PWM */ +}; + +&pm660l_gpios { + gpio@c500 { + qcom,mode = <1>; /* DIG_OUT */ + qcom,output-type = <0>; /* CMOS */ + qcom,src-sel = <7>; /* DTEST2 */ + qcom,master-en = <1>; /* Enable MPP */ + qcom,invert = <0>; /* Enable MPP */ + }; +}; + +&dsi_sharp_split_link_wuxga_video { + pwms = <&pm660l_pwm_4 0 0>; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm"; + qcom,mdss-dsi-bl-pwm-pmi; + qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>; + qcom,panel-supply-entries = <&dsi_panel_split_link_pwr_supply>; +}; + +&dsi_rm67195_amoled_fhd_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi index dfed9ec80a34..4e3ebd445814 100644 --- a/arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi @@ -16,6 +16,7 @@ #include "dsi-panel-truly-1080p-cmd.dtsi" #include "dsi-panel-truly-1080p-video.dtsi" #include "dsi-panel-rm67195-amoled-fhd-cmd.dtsi" +#include "dsi-panel-sharp-split-link-wuxga-video.dtsi" &soc { dsi_panel_pwr_supply: dsi_panel_pwr_supply { @@ -51,6 +52,48 @@ }; }; + dsi_panel_split_link_pwr_supply: dsi_panel_split_link_pwr_supply { + #address-cells = <1>; + #size-cells = <0>; + + qcom,panel-supply-entry@0 { + reg = <0>; + qcom,supply-name = "wqhd-vddio"; + qcom,supply-min-voltage = <1800000>; + qcom,supply-max-voltage = <1950000>; + qcom,supply-enable-load = <32000>; + qcom,supply-disable-load = <80>; + }; + + qcom,panel-supply-entry@1 { + reg = <1>; + qcom,supply-name = "vdda-3p3"; + qcom,supply-min-voltage = <3300000>; + qcom,supply-max-voltage = <3300000>; + qcom,supply-enable-load = <13900>; + qcom,supply-disable-load = <100>; + }; + + qcom,panel-supply-entry@2 { + reg = <2>; + qcom,supply-name = "lab"; + qcom,supply-min-voltage = <5500000>; + 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 = <5500000>; + qcom,supply-max-voltage = <6000000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + qcom,supply-post-on-sleep = <10>; + }; + }; + dsi_panel_pwr_supply_labibb_amoled: dsi_panel_pwr_supply_labibb_amoled { #address-cells = <1>; @@ -114,7 +157,14 @@ qcom,mdss-dsi-pan-enable-dynamic-fps; qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp"; qcom,esd-check-enabled; - qcom,mdss-dsi-panel-status-check-mode = "bta_check"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <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>; + }; &dsi_nt35695b_truly_fhd_cmd { @@ -124,7 +174,14 @@ 24 1e 08 09 05 03 04 a0 24 1a 08 09 05 03 04 a0]; qcom,esd-check-enabled; - qcom,mdss-dsi-panel-status-check-mode = "bta_check"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <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>; + }; &dsi_truly_1080_vid { @@ -138,7 +195,14 @@ qcom,mdss-dsi-pan-enable-dynamic-fps; qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp"; qcom,esd-check-enabled; - qcom,mdss-dsi-panel-status-check-mode = "bta_check"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <0x1c>; + qcom,mdss-dsi-panel-on-check-value = <0x1c>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-panel-max-error-count = <3>; + }; &dsi_truly_1080_cmd { @@ -148,7 +212,14 @@ 23 1e 08 09 05 03 04 a0 23 1a 08 09 05 03 04 a0]; qcom,esd-check-enabled; - qcom,mdss-dsi-panel-status-check-mode = "bta_check"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <0x1c>; + qcom,mdss-dsi-panel-on-check-value = <0x1c>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-panel-max-error-count = <3>; + }; &dsi_rm67195_amoled_fhd_cmd { @@ -160,3 +231,15 @@ qcom,mdss-dsi-t-clk-post = <0x0d>; qcom,mdss-dsi-t-clk-pre = <0x2f>; }; + +&dsi_sharp_split_link_wuxga_video { + qcom,mdss-dsi-panel-timings-phy-v2 = [25 1f 09 0a 06 03 04 a0 + 25 1f 09 0a 06 03 04 a0 + 25 1f 09 0a 06 03 04 a0 + 25 1f 09 0a 06 03 04 a0 + 25 1f 08 0a 06 03 04 a0]; + 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"; +}; diff --git a/arch/arm/boot/dts/qcom/sdm630-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm630-mtp.dtsi index 0619d62526b1..bb50f23d8126 100644 --- a/arch/arm/boot/dts/qcom/sdm630-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-mtp.dtsi @@ -190,3 +190,19 @@ qcom,mdss-dsi-bl-max-level = <4095>; qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; }; + +&i2c_2 { + status = "ok"; + smb1351-charger@1d { + compatible = "qcom,smb1351-charger"; + reg = <0x1d>; + qcom,parallel-charger; + qcom,float-voltage-mv = <4400>; + qcom,recharge-mv = <100>; + qcom,parallel-en-pin-polarity = <1>; + }; +}; + +&dsi_rm67195_amoled_fhd_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts index d9b6a8ae9d34..7e3e9a0cca59 100644 --- a/arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts +++ b/arch/arm/boot/dts/qcom/sdm630-pm660a-cdp.dts @@ -34,3 +34,10 @@ qcom,msm-mbhc-hphl-swh = <0>; qcom,msm-mbhc-gnd-swh = <0>; }; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>; + oledb-supply = <&pm660a_oledb>; + lab-supply = <&lab_regulator>; + ibb-supply = <&ibb_regulator>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts index 8ebdbc08a00c..a522b7ad1d5f 100644 --- a/arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts +++ b/arch/arm/boot/dts/qcom/sdm630-pm660a-mtp.dts @@ -28,3 +28,10 @@ &tavil_snd { qcom,msm-mbhc-moist-cfg = <0>, <0>, <3>; }; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>; + oledb-supply = <&pm660a_oledb>; + lab-supply = <&lab_regulator>; + ibb-supply = <&ibb_regulator>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi b/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi index fb24f727fb49..c677df529dd7 100644 --- a/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi @@ -22,11 +22,13 @@ #address-cells = <1>; #size-cells = <1>; interrupt-parent = <&tlmm>; - interrupts = <21 0x0>; + interrupts = <21 IRQ_TYPE_LEVEL_LOW>; interrupt_names = "smb138x"; interrupt-controller; #interrupt-cells = <3>; qcom,periph-map = <0x10 0x11 0x12 0x13 0x14 0x16 0x36>; + pinctrl-names = "default"; + pinctrl-0 = <&smb_int_default>; smb138x_revid: qcom,revid@100 { compatible = "qcom,qpnp-revid"; @@ -131,6 +133,21 @@ }; }; +&tlmm { + smb_int_default: smb_int_default { + mux { + pins = "gpio21"; + function = "gpio"; + }; + + config { + pins = "gpio21"; + drive-strength = <2>; + bias-pull-up; + }; + }; +}; + / { qrd_batterydata: qcom,battery-data { qcom,batt-id-range-pct = <15>; diff --git a/arch/arm/boot/dts/qcom/sdm630-regulator.dtsi b/arch/arm/boot/dts/qcom/sdm630-regulator.dtsi index eded8b08528a..c4adcfe1bdfb 100644 --- a/arch/arm/boot/dts/qcom/sdm630-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-regulator.dtsi @@ -28,7 +28,7 @@ rpm-regulator-smpa5 { status = "okay"; pm660_s5: regulator-s5 { - regulator-min-microvolt = <1350000>; + regulator-min-microvolt = <1224000>; regulator-max-microvolt = <1350000>; status = "okay"; }; @@ -657,6 +657,7 @@ qcom,cpr-up-error-step-limit = <1>; qcom,cpr-corner-switch-delay-time = <1042>; qcom,cpr-voltage-settling-time = <1760>; + qcom,cpr-reset-step-quot-loop-en; qcom,apm-threshold-voltage = <872000>; qcom,apm-crossover-voltage = <872000>; @@ -665,6 +666,9 @@ qcom,voltage-base = <400000>; qcom,cpr-saw-use-unit-mV; + qcom,cpr-enable; + qcom,cpr-hw-closed-loop; + qcom,cpr-panic-reg-addr-list = <0x179cbaa4 0x17912c18>; qcom,cpr-panic-reg-name-list = @@ -705,6 +709,24 @@ qcom,allow-voltage-interpolation; qcom,allow-quotient-interpolation; qcom,cpr-scaled-open-loop-voltage-as-ceiling; + + 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>; + + qcom,cpr-closed-loop-voltage-fuse-adjustment = + <(-30000) (-30000) (-30000)>; + + qcom,cpr-floor-to-ceiling-max-range = + <32000 32000 32000 40000 44000 + 40000 40000 40000>; }; }; }; @@ -731,6 +753,7 @@ qcom,cpr-up-error-step-limit = <1>; qcom,cpr-corner-switch-delay-time = <1042>; qcom,cpr-voltage-settling-time = <1760>; + qcom,cpr-reset-step-quot-loop-en; qcom,apm-threshold-voltage = <872000>; qcom,apm-crossover-voltage = <872000>; @@ -739,6 +762,9 @@ qcom,voltage-base = <400000>; qcom,cpr-saw-use-unit-mV; + qcom,cpr-enable; + qcom,cpr-hw-closed-loop; + qcom,cpr-panic-reg-addr-list = <0x179c7aa4 0x17812c18>; qcom,cpr-panic-reg-name-list = @@ -834,6 +860,46 @@ qcom,allow-voltage-interpolation; qcom,allow-quotient-interpolation; qcom,cpr-scaled-open-loop-voltage-as-ceiling; + + qcom,cpr-ro-scaling-factor = + <4040 4230 0000 2210 2560 2450 2230 + 2220 2410 2300 2560 2470 1600 3120 + 2620 2280>, + <4040 4230 0000 2210 2560 2450 2230 + 2220 2410 2300 2560 2470 1600 3120 + 2620 2280>, + <4040 4230 0000 2210 2560 2450 2230 + 2220 2410 2300 2560 2470 1600 3120 + 2620 2280>, + <4040 4230 0000 2210 2560 2450 2230 + 2220 2410 2300 2560 2470 1600 3120 + 2620 2280>, + <4040 4230 0000 2210 2560 2450 2230 + 2220 2410 2300 2560 2470 1600 3120 + 2620 2280>; + + qcom,cpr-open-loop-voltage-fuse-adjustment = + <15000 5000 5000 0 0>; + + qcom,cpr-closed-loop-voltage-fuse-adjustment = + <(-30000) (-30000) (-30000) + (-30000) (-30000)>; + + qcom,cpr-floor-to-ceiling-max-range = + /* Speed bin 0 */ + <40000 40000 40000 40000 + 40000 40000 40000 66000 + 66000 40000>, + + /* Speed bin 1 */ + <40000 40000 40000 40000 + 40000 40000 40000 66000 + 66000 40000>, + + /* Speed bin 2 */ + <40000 40000 40000 40000 + 40000 40000 40000 66000 + 66000 40000 40000>; }; }; }; diff --git a/arch/arm/boot/dts/qcom/sdm630.dtsi b/arch/arm/boot/dts/qcom/sdm630.dtsi index 2ef14d766560..24a935ffebec 100644 --- a/arch/arm/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630.dtsi @@ -1988,12 +1988,12 @@ interrupts = <GIC_SPI 326 IRQ_TYPE_NONE>; qcom,ee = <0>; qcom,channel = <0>; + qcom,reserved-chan = <511>; #address-cells = <2>; #size-cells = <0>; interrupt-controller; #interrupt-cells = <4>; cell-index = <0>; - qcom,not-wakeup; /* Needed until Full-boot-chain enabled */ status = "ok"; }; diff --git a/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi b/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi index 33303f1e2a74..4d05ea75b576 100644 --- a/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi @@ -180,6 +180,10 @@ qcom,panel-roi-alignment = <2 2 4 2 1080 2>; }; +&dsi_rm67195_amoled_fhd_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>; +}; + &mdss_dp_ctrl { pinctrl-names = "mdss_dp_active", "mdss_dp_sleep"; pinctrl-0 = <&mdss_dp_aux_active &mdss_dp_usbplug_cc_active>; diff --git a/arch/arm/boot/dts/qcom/sdm660-common.dtsi b/arch/arm/boot/dts/qcom/sdm660-common.dtsi index 33edebec2f72..f933586183ec 100644 --- a/arch/arm/boot/dts/qcom/sdm660-common.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-common.dtsi @@ -181,6 +181,7 @@ "cfg_ahb_clk", "xo"; qcom,core-clk-rate = <133330000>; + qcom,core-clk-rate-hs = <66666667>; resets = <&clock_gcc GCC_USB_30_BCR>; reset-names = "core_reset"; @@ -192,6 +193,7 @@ interrupts = <0 131 0>; usb-phy = <&qusb_phy0>, <&ssphy>; tx-fifo-resize; + snps,usb3-u1u2-disable; snps,nominal-elastic-buffer; snps,disable-clk-gating; snps,has-lpm-erratum; @@ -375,6 +377,8 @@ 0x8f8 0x77 0x00 0x4fc 0x80 0x00 0x8fc 0x80 0x00 + 0x564 0x00 0x00 + 0x964 0x00 0x00 0x4c0 0x0a 0x00 0x8c0 0x0a 0x00 0x504 0x03 0x00 diff --git a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi index 7d293f8d821c..19862f02aa84 100644 --- a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi @@ -147,7 +147,13 @@ 23 1e 07 08 05 03 04 a0 23 18 07 08 04 03 04 a0]; qcom,esd-check-enabled; - qcom,mdss-dsi-panel-status-check-mode = "bta_check"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <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>; }; &dsi_dual_nt36850_truly_cmd { @@ -195,7 +201,13 @@ 20 12 05 06 03 13 04 a0]; qcom,config-select = <&dsi_nt35597_truly_dsc_cmd_config2>; qcom,esd-check-enabled; - qcom,mdss-dsi-panel-status-check-mode = "bta_check"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <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>; }; &dsi_dual_nt35597_video { @@ -229,7 +241,13 @@ qcom,mdss-dsi-pan-enable-dynamic-fps; qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp"; qcom,esd-check-enabled; - qcom,mdss-dsi-panel-status-check-mode = "bta_check"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <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>; }; &dsi_nt35695b_truly_fhd_cmd { @@ -239,7 +257,13 @@ 24 1e 08 09 05 03 04 a0 24 1a 08 09 05 03 04 a0]; qcom,esd-check-enabled; - qcom,mdss-dsi-panel-status-check-mode = "bta_check"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <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>; }; &dsi_truly_1080_vid { @@ -253,7 +277,14 @@ qcom,mdss-dsi-pan-enable-dynamic-fps; qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp"; qcom,esd-check-enabled; - qcom,mdss-dsi-panel-status-check-mode = "bta_check"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <0x1c>; + qcom,mdss-dsi-panel-on-check-value = <0x1c>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-panel-max-error-count = <3>; + }; &dsi_truly_1080_cmd { @@ -263,7 +294,14 @@ 23 1e 08 09 05 03 04 a0 23 1a 08 09 05 03 04 a0]; qcom,esd-check-enabled; - qcom,mdss-dsi-panel-status-check-mode = "bta_check"; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a]; + qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-status-value = <0x1c>; + qcom,mdss-dsi-panel-on-check-value = <0x1c>; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-panel-max-error-count = <3>; + }; &dsi_rm67195_amoled_fhd_cmd { diff --git a/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi index b263d2a68792..787c4f1e2fb6 100644 --- a/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi @@ -505,7 +505,16 @@ qcom,msm_ext_disp = <&msm_ext_disp>; - qcom,aux-cfg-settings = [00 13 00 00 0a 28 0a 03 b7 03]; + qcom,aux-cfg0-settings = [20 00]; + qcom,aux-cfg1-settings = [24 13 23 1d]; + qcom,aux-cfg2-settings = [28 00]; + qcom,aux-cfg3-settings = [2c 00]; + qcom,aux-cfg4-settings = [30 0a]; + qcom,aux-cfg5-settings = [34 28]; + qcom,aux-cfg6-settings = [38 0a]; + qcom,aux-cfg7-settings = [3c 03]; + qcom,aux-cfg8-settings = [40 b7]; + qcom,aux-cfg9-settings = [44 03]; qcom,logical2physical-lane-map = [00 01 02 03]; qcom,phy-register-offset = <0x4>; qcom,max-pclk-frequency-khz = <300000>; diff --git a/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi index ed3b3d89d392..cb554a639d71 100644 --- a/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi @@ -170,6 +170,10 @@ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; }; +&dsi_rm67195_amoled_fhd_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>; +}; + &sdhc_1 { /* device core power supply */ vdd-supply = <&pm660l_l4>; diff --git a/arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts b/arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts index 7ca31fcc41a2..c27f76d3027b 100644 --- a/arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts +++ b/arch/arm/boot/dts/qcom/sdm660-pm660a-cdp.dts @@ -25,13 +25,19 @@ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; }; +&mdss_dsi { + hw-config = "single_dsi"; +}; + &mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>; oledb-supply = <&pm660a_oledb>; lab-supply = <&lab_regulator>; ibb-supply = <&ibb_regulator>; }; &mdss_dsi1 { + status = "disabled"; oledb-supply = <&pm660a_oledb>; lab-supply = <&lab_regulator>; ibb-supply = <&ibb_regulator>; diff --git a/arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts b/arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts index d6e1f6a32def..eb5e4999fb67 100644 --- a/arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts +++ b/arch/arm/boot/dts/qcom/sdm660-pm660a-mtp.dts @@ -25,13 +25,19 @@ qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>; }; +&mdss_dsi { + hw-config = "single_dsi"; +}; + &mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>; oledb-supply = <&pm660a_oledb>; lab-supply = <&lab_regulator>; ibb-supply = <&ibb_regulator>; }; &mdss_dsi1 { + status = "disabled"; oledb-supply = <&pm660a_oledb>; lab-supply = <&lab_regulator>; ibb-supply = <&ibb_regulator>; diff --git a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi b/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi index a93efdc38f41..8b6bbac171f4 100644 --- a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi @@ -28,7 +28,7 @@ rpm-regulator-smpa5 { status = "okay"; pm660_s5: regulator-s5 { - regulator-min-microvolt = <1350000>; + regulator-min-microvolt = <1224000>; regulator-max-microvolt = <1350000>; status = "okay"; }; diff --git a/arch/arm/boot/dts/qcom/sdm660.dtsi b/arch/arm/boot/dts/qcom/sdm660.dtsi index be200f8dd531..2e576a51677f 100644 --- a/arch/arm/boot/dts/qcom/sdm660.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660.dtsi @@ -466,6 +466,7 @@ interrupts = <GIC_SPI 326 IRQ_TYPE_NONE>; qcom,ee = <0>; qcom,channel = <0>; + qcom,reserved-chan = <511>; #address-cells = <2>; #size-cells = <0>; interrupt-controller; diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi index 4dfca8fc49b3..1bc61ece2589 100644 --- a/arch/arm/boot/dts/sama5d2.dtsi +++ b/arch/arm/boot/dts/sama5d2.dtsi @@ -856,6 +856,13 @@ compatible = "atmel,at91sam9260-usart"; reg = <0xf801c000 0x100>; interrupts = <24 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(35))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(36))>; + dma-names = "tx", "rx"; clocks = <&uart0_clk>; clock-names = "usart"; status = "disabled"; @@ -865,6 +872,13 @@ compatible = "atmel,at91sam9260-usart"; reg = <0xf8020000 0x100>; interrupts = <25 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(37))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(38))>; + dma-names = "tx", "rx"; clocks = <&uart1_clk>; clock-names = "usart"; status = "disabled"; @@ -874,6 +888,13 @@ compatible = "atmel,at91sam9260-usart"; reg = <0xf8024000 0x100>; interrupts = <26 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(39))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(40))>; + dma-names = "tx", "rx"; clocks = <&uart2_clk>; clock-names = "usart"; status = "disabled"; @@ -985,6 +1006,13 @@ compatible = "atmel,at91sam9260-usart"; reg = <0xfc008000 0x100>; interrupts = <27 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(41))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(42))>; + dma-names = "tx", "rx"; clocks = <&uart3_clk>; clock-names = "usart"; status = "disabled"; @@ -993,6 +1021,13 @@ uart4: serial@fc00c000 { compatible = "atmel,at91sam9260-usart"; reg = <0xfc00c000 0x100>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(43))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(44))>; + dma-names = "tx", "rx"; interrupts = <28 IRQ_TYPE_LEVEL_HIGH 7>; clocks = <&uart4_clk>; clock-names = "usart"; diff --git a/arch/arm/configs/sdm660-perf_defconfig b/arch/arm/configs/sdm660-perf_defconfig index 32cf48661c9b..a8844dafe592 100644 --- a/arch/arm/configs/sdm660-perf_defconfig +++ b/arch/arm/configs/sdm660-perf_defconfig @@ -359,6 +359,7 @@ CONFIG_REGULATOR_MSM_GFX_LDO=y CONFIG_REGULATOR_RPM_SMD=y CONFIG_REGULATOR_QPNP=y CONFIG_REGULATOR_QPNP_LABIBB=y +CONFIG_REGULATOR_QPNP_LCDB=y CONFIG_REGULATOR_QPNP_OLEDB=y CONFIG_REGULATOR_SPM=y CONFIG_REGULATOR_CPR3_HMSS=y diff --git a/arch/arm/configs/sdm660_defconfig b/arch/arm/configs/sdm660_defconfig index c4b0eabe2fbf..30a71904747f 100644 --- a/arch/arm/configs/sdm660_defconfig +++ b/arch/arm/configs/sdm660_defconfig @@ -635,6 +635,7 @@ CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_PAGEALLOC=y CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y CONFIG_SLUB_DEBUG_PANIC_ON=y +CONFIG_PAGE_POISONING_ENABLE_DEFAULT=y CONFIG_DEBUG_OBJECTS=y CONFIG_DEBUG_OBJECTS_FREE=y CONFIG_DEBUG_OBJECTS_TIMERS=y diff --git a/arch/arm/crypto/aes-ce-glue.c b/arch/arm/crypto/aes-ce-glue.c index 679c589c4828..1f7b98e1a00d 100644 --- a/arch/arm/crypto/aes-ce-glue.c +++ b/arch/arm/crypto/aes-ce-glue.c @@ -369,7 +369,7 @@ static struct crypto_alg aes_algs[] = { { .cra_blkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, - .ivsize = AES_BLOCK_SIZE, + .ivsize = 0, .setkey = ce_aes_setkey, .encrypt = ecb_encrypt, .decrypt = ecb_decrypt, @@ -446,7 +446,7 @@ static struct crypto_alg aes_algs[] = { { .cra_ablkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, - .ivsize = AES_BLOCK_SIZE, + .ivsize = 0, .setkey = ablk_set_key, .encrypt = ablk_encrypt, .decrypt = ablk_decrypt, diff --git a/arch/arm/kernel/vdso.c b/arch/arm/kernel/vdso.c index 54a5aeab988d..bbbffe946122 100644 --- a/arch/arm/kernel/vdso.c +++ b/arch/arm/kernel/vdso.c @@ -17,6 +17,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <linux/cache.h> #include <linux/elf.h> #include <linux/err.h> #include <linux/kernel.h> @@ -39,7 +40,7 @@ static struct page **vdso_text_pagelist; /* Total number of pages needed for the data and text portions of the VDSO. */ -unsigned int vdso_total_pages __read_mostly; +unsigned int vdso_total_pages __ro_after_init; /* * The VDSO data page. @@ -47,13 +48,13 @@ unsigned int vdso_total_pages __read_mostly; static union vdso_data_store vdso_data_store __page_aligned_data; static struct vdso_data *vdso_data = &vdso_data_store.data; -static struct page *vdso_data_page; -static struct vm_special_mapping vdso_data_mapping = { +static struct page *vdso_data_page __ro_after_init; +static const struct vm_special_mapping vdso_data_mapping = { .name = "[vvar]", .pages = &vdso_data_page, }; -static struct vm_special_mapping vdso_text_mapping = { +static struct vm_special_mapping vdso_text_mapping __ro_after_init = { .name = "[vdso]", }; @@ -67,7 +68,7 @@ struct elfinfo { /* Cached result of boot-time check for whether the arch timer exists, * and if so, whether the virtual counter is useable. */ -static bool cntvct_ok __read_mostly; +static bool cntvct_ok __ro_after_init; static bool __init cntvct_functional(void) { @@ -224,7 +225,7 @@ static int install_vvar(struct mm_struct *mm, unsigned long addr) VM_READ | VM_MAYREAD, &vdso_data_mapping); - return IS_ERR(vma) ? PTR_ERR(vma) : 0; + return PTR_ERR_OR_ZERO(vma); } /* assumes mmap_sem is write-locked */ diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index 23726fb31741..d687f860a2da 100644 --- a/arch/arm/mach-at91/pm.c +++ b/arch/arm/mach-at91/pm.c @@ -286,6 +286,22 @@ static void at91_ddr_standby(void) at91_ramc_write(1, AT91_DDRSDRC_LPR, saved_lpr1); } +static void sama5d3_ddr_standby(void) +{ + u32 lpr0; + u32 saved_lpr0; + + saved_lpr0 = at91_ramc_read(0, AT91_DDRSDRC_LPR); + lpr0 = saved_lpr0 & ~AT91_DDRSDRC_LPCB; + lpr0 |= AT91_DDRSDRC_LPCB_POWER_DOWN; + + at91_ramc_write(0, AT91_DDRSDRC_LPR, lpr0); + + cpu_do_idle(); + + at91_ramc_write(0, AT91_DDRSDRC_LPR, saved_lpr0); +} + /* We manage both DDRAM/SDRAM controllers, we need more than one value to * remember. */ @@ -320,7 +336,7 @@ static const struct of_device_id const ramc_ids[] __initconst = { { .compatible = "atmel,at91rm9200-sdramc", .data = at91rm9200_standby }, { .compatible = "atmel,at91sam9260-sdramc", .data = at91sam9_sdram_standby }, { .compatible = "atmel,at91sam9g45-ddramc", .data = at91_ddr_standby }, - { .compatible = "atmel,sama5d3-ddramc", .data = at91_ddr_standby }, + { .compatible = "atmel,sama5d3-ddramc", .data = sama5d3_ddr_standby }, { /*sentinel*/ } }; diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index b58d8e0d9ed1..a25d6b0e22a4 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -67,7 +67,8 @@ __dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot, static void __dma_free_remap(void *cpu_addr, size_t size, bool no_warn); -static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot); +static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot, + bool coherent); static void *arm_dma_remap(struct device *dev, void *cpu_addr, dma_addr_t handle, size_t size, @@ -76,6 +77,33 @@ static void *arm_dma_remap(struct device *dev, void *cpu_addr, static void arm_dma_unremap(struct device *dev, void *remapped_addr, size_t size); +static bool is_dma_coherent(struct device *dev, struct dma_attrs *attrs) +{ + bool is_coherent; + + if (dma_get_attr(DMA_ATTR_FORCE_COHERENT, attrs)) + is_coherent = true; + else if (dma_get_attr(DMA_ATTR_FORCE_NON_COHERENT, attrs)) + is_coherent = false; + else if (is_device_dma_coherent(dev)) + is_coherent = true; + else + is_coherent = false; + + return is_coherent; +} + +static int __get_iommu_pgprot(struct dma_attrs *attrs, int prot, + bool coherent) +{ + if (!dma_get_attr(DMA_ATTR_EXEC_MAPPING, attrs)) + prot |= IOMMU_NOEXEC; + if (coherent) + prot |= IOMMU_CACHE; + + return prot; +} + /** * arm_dma_map_page - map a portion of a page for streaming DMA * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices @@ -245,7 +273,7 @@ static u64 get_coherent_dma_mask(struct device *dev) } static void __dma_clear_buffer(struct page *page, size_t size, - struct dma_attrs *attrs) + struct dma_attrs *attrs, bool is_coherent) { /* * Ensure that the allocated pages are zeroed, and that any data @@ -258,18 +286,22 @@ static void __dma_clear_buffer(struct page *page, size_t size, void *ptr = kmap_atomic(page); if (!dma_get_attr(DMA_ATTR_SKIP_ZEROING, attrs)) memset(ptr, 0, PAGE_SIZE); - dmac_flush_range(ptr, ptr + PAGE_SIZE); + if (!is_coherent) + dmac_flush_range(ptr, ptr + PAGE_SIZE); kunmap_atomic(ptr); page++; size -= PAGE_SIZE; } - outer_flush_range(base, end); + if (!is_coherent) + outer_flush_range(base, end); } else { void *ptr = page_address(page); if (!dma_get_attr(DMA_ATTR_SKIP_ZEROING, attrs)) memset(ptr, 0, size); - dmac_flush_range(ptr, ptr + size); - outer_flush_range(__pa(ptr), __pa(ptr) + size); + if (!is_coherent) { + dmac_flush_range(ptr, ptr + size); + outer_flush_range(__pa(ptr), __pa(ptr) + size); + } } } @@ -277,7 +309,8 @@ static void __dma_clear_buffer(struct page *page, size_t size, * Allocate a DMA buffer for 'dev' of size 'size' using the * specified gfp mask. Note that 'size' must be page aligned. */ -static struct page *__dma_alloc_buffer(struct device *dev, size_t size, gfp_t gfp) +static struct page *__dma_alloc_buffer(struct device *dev, size_t size, + gfp_t gfp, bool coherent) { unsigned long order = get_order(size); struct page *page, *p, *e; @@ -293,7 +326,7 @@ static struct page *__dma_alloc_buffer(struct device *dev, size_t size, gfp_t gf for (p = page + (size >> PAGE_SHIFT), e = page + (1 << order); p < e; p++) __free_page(p); - __dma_clear_buffer(page, size, NULL); + __dma_clear_buffer(page, size, NULL, coherent); return page; } @@ -527,7 +560,7 @@ static void *__alloc_remap_buffer(struct device *dev, size_t size, gfp_t gfp, { struct page *page; void *ptr = NULL; - page = __dma_alloc_buffer(dev, size, gfp); + page = __dma_alloc_buffer(dev, size, gfp, false); if (!page) return NULL; if (!want_vaddr) @@ -602,7 +635,7 @@ static void *__alloc_from_contiguous(struct device *dev, size_t size, */ if (!(dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs) && dma_get_attr(DMA_ATTR_SKIP_ZEROING, attrs))) - __dma_clear_buffer(page, size, attrs); + __dma_clear_buffer(page, size, attrs, false); if (PageHighMem(page)) { if (!want_vaddr) { @@ -643,15 +676,13 @@ static void __free_from_contiguous(struct device *dev, struct page *page, dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT); } -static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot) +static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot, + bool coherent) { - if (dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs)) - prot = pgprot_writecombine(prot); - else if (dma_get_attr(DMA_ATTR_STRONGLY_ORDERED, attrs)) + if (dma_get_attr(DMA_ATTR_STRONGLY_ORDERED, attrs)) prot = pgprot_stronglyordered(prot); - /* if non-consistent just pass back what was given */ - else if (!dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs)) - prot = pgprot_dmacoherent(prot); + else if (!coherent || dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs)) + prot = pgprot_writecombine(prot); return prot; } @@ -673,10 +704,10 @@ static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot) #endif /* CONFIG_MMU */ static void *__alloc_simple_buffer(struct device *dev, size_t size, gfp_t gfp, - struct page **ret_page) + struct page **ret_page, bool coherent) { struct page *page; - page = __dma_alloc_buffer(dev, size, gfp); + page = __dma_alloc_buffer(dev, size, gfp, coherent); if (!page) return NULL; @@ -724,12 +755,14 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, want_vaddr = !dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs); if (nommu()) - addr = __alloc_simple_buffer(dev, size, gfp, &page); + addr = __alloc_simple_buffer(dev, size, gfp, &page, + is_coherent); else if (dev_get_cma_area(dev) && (gfp & __GFP_DIRECT_RECLAIM)) addr = __alloc_from_contiguous(dev, size, prot, &page, caller, attrs); else if (is_coherent) - addr = __alloc_simple_buffer(dev, size, gfp, &page); + addr = __alloc_simple_buffer(dev, size, gfp, &page, + is_coherent); else if (!gfpflags_allow_blocking(gfp)) addr = __alloc_from_pool(size, &page); else @@ -749,7 +782,7 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, void *arm_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs) { - pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL); + pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, false); return __dma_alloc(dev, size, handle, gfp, prot, false, attrs, __builtin_return_address(0)); @@ -792,8 +825,9 @@ static void *arm_dma_remap(struct device *dev, void *cpu_addr, struct dma_attrs *attrs) { void *ptr; + bool is_coherent = is_dma_coherent(dev, attrs); struct page *page = pfn_to_page(dma_to_pfn(dev, handle)); - pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL); + pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, is_coherent); unsigned long offset = handle & ~PAGE_MASK; size = PAGE_ALIGN(size + offset); @@ -837,7 +871,8 @@ int arm_dma_mmap(struct device *dev, struct vm_area_struct *vma, struct dma_attrs *attrs) { #ifdef CONFIG_MMU - vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot); + vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot, + false); #endif /* CONFIG_MMU */ return __arm_dma_mmap(dev, vma, cpu_addr, dma_addr, size, attrs); } @@ -1238,6 +1273,7 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size, size_t count = size >> PAGE_SHIFT; size_t array_size = count * sizeof(struct page *); int i = 0; + bool is_coherent = is_dma_coherent(dev, attrs); if (array_size <= PAGE_SIZE) pages = kzalloc(array_size, GFP_KERNEL); @@ -1255,7 +1291,7 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size, if (!page) goto error; - __dma_clear_buffer(page, size, NULL); + __dma_clear_buffer(page, size, attrs, is_coherent); for (i = 0; i < count; i++) pages[i] = page + i; @@ -1299,7 +1335,8 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size, pages[i + j] = pages[i] + j; } - __dma_clear_buffer(pages[i], PAGE_SIZE << order, NULL); + __dma_clear_buffer(pages[i], PAGE_SIZE << order, attrs, + is_coherent); i += 1 << order; count -= 1 << order; } @@ -1353,16 +1390,20 @@ __iommu_alloc_remap(struct page **pages, size_t size, gfp_t gfp, pgprot_t prot, * Create a mapping in device IO address space for specified pages */ static dma_addr_t -__iommu_create_mapping(struct device *dev, struct page **pages, size_t size) +__iommu_create_mapping(struct device *dev, struct page **pages, size_t size, + struct dma_attrs *attrs) { struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev); unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT; dma_addr_t dma_addr, iova; int i; + int prot = IOMMU_READ | IOMMU_WRITE; dma_addr = __alloc_iova(mapping, size); if (dma_addr == DMA_ERROR_CODE) return dma_addr; + prot = __get_iommu_pgprot(attrs, prot, + is_dma_coherent(dev, attrs)); iova = dma_addr; for (i = 0; i < count; ) { @@ -1377,8 +1418,7 @@ __iommu_create_mapping(struct device *dev, struct page **pages, size_t size) break; len = (j - i) << PAGE_SHIFT; - ret = iommu_map(mapping->domain, iova, phys, len, - IOMMU_READ|IOMMU_WRITE); + ret = iommu_map(mapping->domain, iova, phys, len, prot); if (ret < 0) goto fail; iova += len; @@ -1435,23 +1475,52 @@ static struct page **__iommu_get_pages(void *cpu_addr, struct dma_attrs *attrs) } static void *__iommu_alloc_atomic(struct device *dev, size_t size, - dma_addr_t *handle) + dma_addr_t *handle, gfp_t gfp, + struct dma_attrs *attrs) { struct page *page; + struct page **pages; + size_t count = size >> PAGE_SHIFT; + size_t array_size = count * sizeof(struct page *); void *addr; + int i; + bool coherent = is_dma_coherent(dev, attrs); - addr = __alloc_from_pool(size, &page); - if (!addr) + if (array_size <= PAGE_SIZE) + pages = kzalloc(array_size, gfp); + else + pages = vzalloc(array_size); + + if (!pages) return NULL; - *handle = __iommu_create_mapping(dev, &page, size); + if (coherent) { + page = alloc_pages(gfp, get_order(size)); + addr = page ? page_address(page) : NULL; + } else { + addr = __alloc_from_pool(size, &page); + } + + if (!addr) + goto err_free; + + for (i = 0; i < count ; i++) + pages[i] = page + i; + + *handle = __iommu_create_mapping(dev, pages, size, attrs); if (*handle == DMA_ERROR_CODE) goto err_mapping; + kvfree(pages); return addr; err_mapping: - __free_from_pool(addr, size); + if (coherent) + __free_pages(page, get_order(size)); + else + __free_from_pool(addr, size); +err_free: + kvfree(pages); return NULL; } @@ -1465,7 +1534,9 @@ static void __iommu_free_atomic(struct device *dev, void *cpu_addr, static void *arm_iommu_alloc_attrs(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs) { - pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL); + + bool coherent = is_dma_coherent(dev, attrs); + pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, coherent); struct page **pages; void *addr = NULL; @@ -1473,7 +1544,7 @@ static void *arm_iommu_alloc_attrs(struct device *dev, size_t size, size = PAGE_ALIGN(size); if (!gfpflags_allow_blocking(gfp)) - return __iommu_alloc_atomic(dev, size, handle); + return __iommu_alloc_atomic(dev, size, handle, gfp, attrs); /* * Following is a work-around (a.k.a. hack) to prevent pages @@ -1488,7 +1559,7 @@ static void *arm_iommu_alloc_attrs(struct device *dev, size_t size, if (!pages) return NULL; - *handle = __iommu_create_mapping(dev, pages, size); + *handle = __iommu_create_mapping(dev, pages, size, attrs); if (*handle == DMA_ERROR_CODE) goto err_buffer; @@ -1518,8 +1589,10 @@ static int arm_iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma, struct page **pages = __iommu_get_pages(cpu_addr, attrs); unsigned long nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; unsigned long off = vma->vm_pgoff; + bool coherent = is_dma_coherent(dev, attrs); - vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot); + vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot, + coherent); if (!pages) return -ENXIO; @@ -1750,6 +1823,8 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, dev_err(dev, "Couldn't allocate iova for sg %p\n", sg); return 0; } + prot = __get_iommu_pgprot(attrs, prot, + is_dma_coherent(dev, attrs)); ret = iommu_map_sg(mapping->domain, iova, sg, nents, prot); if (ret != total_length) { @@ -1836,6 +1911,13 @@ void arm_iommu_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, { struct scatterlist *s; int i; + struct dma_iommu_mapping *mapping = dev->archdata.mapping; + dma_addr_t iova = sg_dma_address(sg); + bool iova_coherent = iommu_is_iova_coherent(mapping->domain, iova); + + if (iova_coherent) + return; + for_each_sg(sg, s, nents, i) __dma_page_dev_to_cpu(sg_page(s), s->offset, s->length, dir); @@ -1854,6 +1936,12 @@ void arm_iommu_sync_sg_for_device(struct device *dev, struct scatterlist *sg, { struct scatterlist *s; int i; + struct dma_iommu_mapping *mapping = dev->archdata.mapping; + dma_addr_t iova = sg_dma_address(sg); + bool iova_coherent = iommu_is_iova_coherent(mapping->domain, iova); + + if (iova_coherent) + return; for_each_sg(sg, s, nents, i) __dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir); @@ -1887,6 +1975,8 @@ static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, struct page *p return dma_addr; prot = __dma_direction_to_prot(dir); + prot = __get_iommu_pgprot(attrs, prot, + is_dma_coherent(dev, attrs)); ret = iommu_map(mapping->domain, dma_addr, page_to_phys(page) + start_offset, len, prot); @@ -1913,7 +2003,8 @@ static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page, unsigned long offset, size_t size, enum dma_data_direction dir, struct dma_attrs *attrs) { - if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) + if (!is_dma_coherent(dev, attrs) && + !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) __dma_page_cpu_to_dev(page, offset, size, dir); return arm_coherent_iommu_map_page(dev, page, offset, size, dir, attrs); @@ -1960,7 +2051,8 @@ static void arm_iommu_unmap_page(struct device *dev, dma_addr_t handle, int offset = handle & ~PAGE_MASK; int len = PAGE_ALIGN(size + offset); - if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) + if (!(is_dma_coherent(dev, attrs) || + dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))) __dma_page_dev_to_cpu(page, offset, size, dir); iommu_unmap(mapping->domain, iova, len); @@ -1974,8 +2066,10 @@ static void arm_iommu_sync_single_for_cpu(struct device *dev, dma_addr_t iova = handle & PAGE_MASK; struct page *page = phys_to_page(iommu_iova_to_phys(mapping->domain, iova)); unsigned int offset = handle & ~PAGE_MASK; + bool iova_coherent = iommu_is_iova_coherent(mapping->domain, handle); - __dma_page_dev_to_cpu(page, offset, size, dir); + if (!iova_coherent) + __dma_page_dev_to_cpu(page, offset, size, dir); } static void arm_iommu_sync_single_for_device(struct device *dev, @@ -1985,10 +2079,31 @@ static void arm_iommu_sync_single_for_device(struct device *dev, dma_addr_t iova = handle & PAGE_MASK; struct page *page = phys_to_page(iommu_iova_to_phys(mapping->domain, iova)); unsigned int offset = handle & ~PAGE_MASK; + bool iova_coherent = iommu_is_iova_coherent(mapping->domain, handle); - __dma_page_cpu_to_dev(page, offset, size, dir); + if (!iova_coherent) + __dma_page_cpu_to_dev(page, offset, size, dir); +} + +static int arm_iommu_dma_supported(struct device *dev, u64 mask) +{ + struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev); + + if (!mapping) { + dev_warn(dev, "No IOMMU mapping for device\n"); + return 0; + } + + return iommu_dma_supported(mapping->domain, dev, mask); +} + +static int arm_iommu_mapping_error(struct device *dev, + dma_addr_t dma_addr) +{ + return dma_addr == DMA_ERROR_CODE; } + const struct dma_map_ops iommu_ops = { .alloc = arm_iommu_alloc_attrs, .free = arm_iommu_free_attrs, @@ -2006,6 +2121,8 @@ const struct dma_map_ops iommu_ops = { .sync_sg_for_device = arm_iommu_sync_sg_for_device, .set_dma_mask = arm_dma_set_mask, + .dma_supported = arm_iommu_dma_supported, + .mapping_error = arm_iommu_mapping_error, }; const struct dma_map_ops iommu_coherent_ops = { @@ -2052,6 +2169,9 @@ arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, u64 size) if (!bitmap_size) return ERR_PTR(-EINVAL); + WARN(!IS_ALIGNED(size, SZ_128M), + "size is not aligned to 128M, alignment enforced"); + if (bitmap_size > PAGE_SIZE) { extensions = bitmap_size / PAGE_SIZE; bitmap_size = PAGE_SIZE; @@ -2074,7 +2194,7 @@ arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, u64 size) mapping->nr_bitmaps = 1; mapping->extensions = extensions; mapping->base = base; - mapping->bits = bits; + mapping->bits = BITS_PER_BYTE * bitmap_size; spin_lock_init(&mapping->lock); diff --git a/arch/arm/vdso/Makefile b/arch/arm/vdso/Makefile index 1160434eece0..59a8fa7b8a3b 100644 --- a/arch/arm/vdso/Makefile +++ b/arch/arm/vdso/Makefile @@ -74,5 +74,5 @@ $(MODLIB)/vdso: FORCE @mkdir -p $(MODLIB)/vdso PHONY += vdso_install -vdso_install: $(obj)/vdso.so.dbg $(MODLIB)/vdso FORCE +vdso_install: $(obj)/vdso.so.dbg $(MODLIB)/vdso $(call cmd,vdso_install) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 3c89828b9af2..69f08519301d 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1023,6 +1023,26 @@ config BUILD_ARM64_APPENDED_DTB_IMAGE DTBs to be built by default (instead of a standalone Image.gz.) The image will built in arch/arm64/boot/Image.gz-dtb +choice + prompt "Appended DTB Kernel Image name" + depends on BUILD_ARM64_APPENDED_DTB_IMAGE + 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/arm64/boot/IMAGE-NAME-dtb + + config IMG_GZ_DTB + bool "Image.gz-dtb" + config IMG_DTB + bool "Image-dtb" +endchoice + +config BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME + string + depends on BUILD_ARM64_APPENDED_DTB_IMAGE + default "Image.gz-dtb" if IMG_GZ_DTB + default "Image-dtb" if IMG_DTB + config BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES string "Default dtb names" depends on BUILD_ARM64_APPENDED_DTB_IMAGE diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index fcfa3c7dedc1..f656c52d7e10 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -85,7 +85,7 @@ core-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a # Default target when executing plain make ifeq ($(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE),y) -KBUILD_IMAGE := Image.gz-dtb +KBUILD_IMAGE := $(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME)) else KBUILD_IMAGE := Image.gz endif @@ -132,6 +132,16 @@ archclean: $(Q)$(MAKE) $(clean)=$(boot) $(Q)$(MAKE) $(clean)=$(boot)/dts +# We need to generate vdso-offsets.h before compiling certain files in kernel/. +# In order to do that, we should use the archprepare target, but we can't since +# asm-offsets.h is included in some files used to generate vdso-offsets.h, and +# asm-offsets.h is built in prepare0, for which archprepare is a dependency. +# Therefore we need to generate the header after prepare0 has been made, hence +# this hack. +prepare: vdso_prepare +vdso_prepare: prepare0 + $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso include/generated/vdso-offsets.h + define archhelp echo '* Image.gz - Compressed kernel image (arch/$(ARCH)/boot/Image.gz)' echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)' diff --git a/arch/arm64/boot/dts/arm/juno-r1.dts b/arch/arm64/boot/dts/arm/juno-r1.dts index 93bc3d7d51c0..29315af22147 100644 --- a/arch/arm64/boot/dts/arm/juno-r1.dts +++ b/arch/arm64/boot/dts/arm/juno-r1.dts @@ -60,6 +60,28 @@ }; }; + idle-states { + entry-method = "arm,psci"; + + CPU_SLEEP_0: cpu-sleep-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x0010000>; + local-timer-stop; + entry-latency-us = <300>; + exit-latency-us = <1200>; + min-residency-us = <2000>; + }; + + CLUSTER_SLEEP_0: cluster-sleep-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x1010000>; + local-timer-stop; + entry-latency-us = <400>; + exit-latency-us = <1200>; + min-residency-us = <2500>; + }; + }; + A57_0: cpu@0 { compatible = "arm,cortex-a57","arm,armv8"; reg = <0x0 0x0>; @@ -67,6 +89,7 @@ enable-method = "psci"; next-level-cache = <&A57_L2>; clocks = <&scpi_dvfs 0>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; }; A57_1: cpu@1 { @@ -76,6 +99,7 @@ enable-method = "psci"; next-level-cache = <&A57_L2>; clocks = <&scpi_dvfs 0>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; }; A53_0: cpu@100 { @@ -85,6 +109,7 @@ enable-method = "psci"; next-level-cache = <&A53_L2>; clocks = <&scpi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; }; A53_1: cpu@101 { @@ -94,6 +119,7 @@ enable-method = "psci"; next-level-cache = <&A53_L2>; clocks = <&scpi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; }; A53_2: cpu@102 { @@ -103,6 +129,7 @@ enable-method = "psci"; next-level-cache = <&A53_L2>; clocks = <&scpi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; }; A53_3: cpu@103 { @@ -112,6 +139,7 @@ enable-method = "psci"; next-level-cache = <&A53_L2>; clocks = <&scpi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; }; A57_L2: l2-cache0 { diff --git a/arch/arm64/boot/dts/arm/juno-sched-energy.dtsi b/arch/arm64/boot/dts/arm/juno-sched-energy.dtsi new file mode 100644 index 000000000000..38207e4391ab --- /dev/null +++ b/arch/arm64/boot/dts/arm/juno-sched-energy.dtsi @@ -0,0 +1,147 @@ +/* + * ARM JUNO specific energy cost model data. There are no unit requirements for + * the data. Data can be normalized to any reference point, but the + * normalization must be consistent. That is, one bogo-joule/watt must be the + * same quantity for all data, but we don't care what it is. + */ + +/* static struct idle_state idle_states_cluster_a53[] = { */ +/* { .power = 56 }, /\* arch_cpu_idle() (active idle) = WFI *\/ */ +/* { .power = 56 }, /\* WFI *\/ */ +/* { .power = 56 }, /\* cpu-sleep-0 *\/ */ +/* { .power = 17 }, /\* cluster-sleep-0 *\/ */ +/* }; */ + +/* static struct idle_state idle_states_cluster_a57[] = { */ +/* { .power = 65 }, /\* arch_cpu_idle() (active idle) = WFI *\/ */ +/* { .power = 65 }, /\* WFI *\/ */ +/* { .power = 65 }, /\* cpu-sleep-0 *\/ */ +/* { .power = 24 }, /\* cluster-sleep-0 *\/ */ +/* }; */ + +/* static struct capacity_state cap_states_cluster_a53[] = { */ +/* /\* Power per cluster *\/ */ +/* { .cap = 235, .power = 26, }, /\* 450 MHz *\/ */ +/* { .cap = 303, .power = 30, }, /\* 575 MHz *\/ */ +/* { .cap = 368, .power = 39, }, /\* 700 MHz *\/ */ +/* { .cap = 406, .power = 47, }, /\* 775 MHz *\/ */ +/* { .cap = 447, .power = 57, }, /\* 850 Mhz *\/ */ +/* }; */ + +/* static struct capacity_state cap_states_cluster_a57[] = { */ +/* /\* Power per cluster *\/ */ +/* { .cap = 417, .power = 24, }, /\* 450 MHz *\/ */ +/* { .cap = 579, .power = 32, }, /\* 625 MHz *\/ */ +/* { .cap = 744, .power = 43, }, /\* 800 MHz *\/ */ +/* { .cap = 883, .power = 49, }, /\* 950 MHz *\/ */ +/* { .cap = 1024, .power = 64, }, /\* 1100 MHz *\/ */ +/* }; */ + +/* static struct sched_group_energy energy_cluster_a53 = { */ +/* .nr_idle_states = ARRAY_SIZE(idle_states_cluster_a53), */ +/* .idle_states = idle_states_cluster_a53, */ +/* .nr_cap_states = ARRAY_SIZE(cap_states_cluster_a53), */ +/* .cap_states = cap_states_cluster_a53, */ +/* }; */ + +/* static struct sched_group_energy energy_cluster_a57 = { */ +/* .nr_idle_states = ARRAY_SIZE(idle_states_cluster_a57), */ +/* .idle_states = idle_states_cluster_a57, */ +/* .nr_cap_states = ARRAY_SIZE(cap_states_cluster_a57), */ +/* .cap_states = cap_states_cluster_a57, */ +/* }; */ + +/* static struct idle_state idle_states_core_a53[] = { */ +/* { .power = 6 }, /\* arch_cpu_idle() (active idle) = WFI *\/ */ +/* { .power = 6 }, /\* WFI *\/ */ +/* { .power = 0 }, /\* cpu-sleep-0 *\/ */ +/* { .power = 0 }, /\* cluster-sleep-0 *\/ */ +/* }; */ + +/* static struct idle_state idle_states_core_a57[] = { */ +/* { .power = 15 }, /\* arch_cpu_idle() (active idle) = WFI *\/ */ +/* { .power = 15 }, /\* WFI *\/ */ +/* { .power = 0 }, /\* cpu-sleep-0 *\/ */ +/* { .power = 0 }, /\* cluster-sleep-0 *\/ */ +/* }; */ + +/* static struct capacity_state cap_states_core_a53[] = { */ +/* /\* Power per cpu *\/ */ +/* { .cap = 235, .power = 33, }, /\* 450 MHz *\/ */ +/* { .cap = 302, .power = 46, }, /\* 575 MHz *\/ */ +/* { .cap = 368, .power = 61, }, /\* 700 MHz *\/ */ +/* { .cap = 406, .power = 76, }, /\* 775 MHz *\/ */ +/* { .cap = 447, .power = 93, }, /\* 850 Mhz *\/ */ +/* }; */ + +/* static struct capacity_state cap_states_core_a57[] = { */ +/* /\* Power per cpu *\/ */ +/* { .cap = 417, .power = 168, }, /\* 450 MHz *\/ */ +/* { .cap = 579, .power = 251, }, /\* 625 MHz *\/ */ +/* { .cap = 744, .power = 359, }, /\* 800 MHz *\/ */ +/* { .cap = 883, .power = 479, }, /\* 950 MHz *\/ */ +/* { .cap = 1024, .power = 616, }, /\* 1100 MHz *\/ */ +/* }; */ + +energy-costs { + CPU_COST_A57: core-cost0 { + busy-cost-data = < + 417 168 + 579 251 + 744 359 + 883 479 + 1023 616 + >; + idle-cost-data = < + 15 + 15 + 0 + 0 + >; + }; + CPU_COST_A53: core-cost1 { + busy-cost-data = < + 235 33 + 302 46 + 368 61 + 406 76 + 447 93 + >; + idle-cost-data = < + 6 + 6 + 0 + 0 + >; + }; + CLUSTER_COST_A57: cluster-cost0 { + busy-cost-data = < + 417 24 + 579 32 + 744 43 + 883 49 + 1024 64 + >; + idle-cost-data = < + 65 + 65 + 65 + 24 + >; + }; + CLUSTER_COST_A53: cluster-cost1 { + busy-cost-data = < + 235 26 + 303 30 + 368 39 + 406 47 + 447 57 + >; + idle-cost-data = < + 56 + 56 + 56 + 17 + >; + }; +}; diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts index 3e1a84b01b50..68816f71fa51 100644 --- a/arch/arm64/boot/dts/arm/juno.dts +++ b/arch/arm64/boot/dts/arm/juno.dts @@ -60,6 +60,28 @@ }; }; + idle-states { + entry-method = "arm,psci"; + + CPU_SLEEP_0: cpu-sleep-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x0010000>; + local-timer-stop; + entry-latency-us = <300>; + exit-latency-us = <1200>; + min-residency-us = <2000>; + }; + + CLUSTER_SLEEP_0: cluster-sleep-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x1010000>; + local-timer-stop; + entry-latency-us = <400>; + exit-latency-us = <1200>; + min-residency-us = <2500>; + }; + }; + A57_0: cpu@0 { compatible = "arm,cortex-a57","arm,armv8"; reg = <0x0 0x0>; @@ -67,6 +89,8 @@ enable-method = "psci"; next-level-cache = <&A57_L2>; clocks = <&scpi_dvfs 0>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + sched-energy-costs = <&CPU_COST_A57 &CLUSTER_COST_A57>; }; A57_1: cpu@1 { @@ -76,6 +100,8 @@ enable-method = "psci"; next-level-cache = <&A57_L2>; clocks = <&scpi_dvfs 0>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + sched-energy-costs = <&CPU_COST_A57 &CLUSTER_COST_A57>; }; A53_0: cpu@100 { @@ -85,6 +111,8 @@ enable-method = "psci"; next-level-cache = <&A53_L2>; clocks = <&scpi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + sched-energy-costs = <&CPU_COST_A53 &CLUSTER_COST_A53>; }; A53_1: cpu@101 { @@ -94,6 +122,8 @@ enable-method = "psci"; next-level-cache = <&A53_L2>; clocks = <&scpi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + sched-energy-costs = <&CPU_COST_A53 &CLUSTER_COST_A53>; }; A53_2: cpu@102 { @@ -103,6 +133,8 @@ enable-method = "psci"; next-level-cache = <&A53_L2>; clocks = <&scpi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + sched-energy-costs = <&CPU_COST_A53 &CLUSTER_COST_A53>; }; A53_3: cpu@103 { @@ -112,6 +144,8 @@ enable-method = "psci"; next-level-cache = <&A53_L2>; clocks = <&scpi_dvfs 1>; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + sched-energy-costs = <&CPU_COST_A53 &CLUSTER_COST_A53>; }; A57_L2: l2-cache0 { @@ -121,6 +155,8 @@ A53_L2: l2-cache1 { compatible = "cache"; }; + + /include/ "juno-sched-energy.dtsi" }; pmu_a57 { diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index 0031c55360c7..a6413f1f8b06 100644 --- a/arch/arm64/configs/msm-perf_defconfig +++ b/arch/arm64/configs/msm-perf_defconfig @@ -5,6 +5,9 @@ CONFIG_AUDIT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y CONFIG_RCU_EXPERT=y CONFIG_RCU_FAST_NO_HZ=y CONFIG_IKCONFIG=y @@ -236,7 +239,7 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y -CONFIG_UID_CPUTIME=y +CONFIG_UID_SYS_STATS=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_SG=y @@ -531,6 +534,7 @@ CONFIG_MSM_PIL_MSS_QDSP6V5=y CONFIG_TRACER_PKT=y CONFIG_MSM_MPM_OF=y CONFIG_MSM_AVTIMER=y +CONFIG_MSM_QBT1000=y CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y CONFIG_MSM_RPM_LOG=y CONFIG_MSM_RPM_STATS_LOG=y diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig index 261a49d7944a..127a9d14128f 100644 --- a/arch/arm64/configs/msm_defconfig +++ b/arch/arm64/configs/msm_defconfig @@ -4,6 +4,9 @@ CONFIG_AUDIT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y CONFIG_RCU_EXPERT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y @@ -228,7 +231,7 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y -CONFIG_UID_CPUTIME=y +CONFIG_UID_SYS_STATS=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_SG=y @@ -535,6 +538,7 @@ CONFIG_MSM_MPM_OF=y CONFIG_MSM_AVTIMER=y CONFIG_QCOM_REMOTEQDSS=y CONFIG_MSM_SERVICE_NOTIFIER=y +CONFIG_MSM_QBT1000=y CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y CONFIG_MSM_RPM_LOG=y CONFIG_MSM_RPM_STATS_LOG=y diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 4f55414454fc..86b114354a2e 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -5,8 +5,13 @@ CONFIG_AUDIT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_IRQ_TIME_ACCOUNTING=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 @@ -186,6 +191,7 @@ CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y CONFIG_NF_CONNTRACK_IPV6=y CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_IPTABLES_128=y CONFIG_IP6_NF_MATCH_RPFILTER=y CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y @@ -240,7 +246,7 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y -CONFIG_UID_CPUTIME=y +CONFIG_UID_SYS_STATS=y CONFIG_QPNP_MISC=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y @@ -565,6 +571,7 @@ CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_AVTIMER=y CONFIG_QCOM_REMOTEQDSS=y CONFIG_MSM_SERVICE_NOTIFIER=y +CONFIG_MSM_QBT1000=y CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y CONFIG_MSM_RPM_LOG=y CONFIG_MSM_RPM_STATS_LOG=y @@ -588,7 +595,6 @@ CONFIG_PWM_QPNP=y CONFIG_ARM_GIC_V3_ACL=y CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y -CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder" CONFIG_MSM_TZ_LOG=y CONFIG_SENSORS_SSC=y CONFIG_EXT2_FS=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index dc50257f5b7a..25b6f6eeb638 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -4,8 +4,13 @@ CONFIG_AUDIT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_IRQ_TIME_ACCOUNTING=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 @@ -82,6 +87,7 @@ CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_BOOST=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y @@ -185,6 +191,7 @@ CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y CONFIG_NF_CONNTRACK_IPV6=y CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_IPTABLES_128=y CONFIG_IP6_NF_MATCH_RPFILTER=y CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y @@ -242,7 +249,7 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y -CONFIG_UID_CPUTIME=y +CONFIG_UID_SYS_STATS=y CONFIG_QPNP_MISC=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y @@ -585,6 +592,7 @@ CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_AVTIMER=y CONFIG_QCOM_REMOTEQDSS=y CONFIG_MSM_SERVICE_NOTIFIER=y +CONFIG_MSM_QBT1000=y CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y CONFIG_MSM_RPM_LOG=y CONFIG_MSM_RPM_STATS_LOG=y @@ -609,7 +617,6 @@ CONFIG_ARM_GIC_V3_ACL=y CONFIG_PHY_XGENE=y CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y -CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder" CONFIG_MSM_TZ_LOG=y CONFIG_SENSORS_SSC=y CONFIG_EXT2_FS=y @@ -638,6 +645,8 @@ CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y CONFIG_SLUB_DEBUG_PANIC_ON=y +CONFIG_PAGE_POISONING=y +CONFIG_PAGE_POISONING_ENABLE_DEFAULT=y CONFIG_DEBUG_OBJECTS=y CONFIG_DEBUG_OBJECTS_FREE=y CONFIG_DEBUG_OBJECTS_TIMERS=y diff --git a/arch/arm64/configs/msmcortex_mediabox_defconfig b/arch/arm64/configs/msmcortex_mediabox_defconfig index ccd653eaec7d..3322f8fa11fc 100644 --- a/arch/arm64/configs/msmcortex_mediabox_defconfig +++ b/arch/arm64/configs/msmcortex_mediabox_defconfig @@ -194,6 +194,7 @@ CONFIG_L2TP_V3=y CONFIG_L2TP_IP=y CONFIG_L2TP_ETH=y CONFIG_BRIDGE=y +CONFIG_VLAN_8021Q=y CONFIG_NET_SCHED=y CONFIG_NET_SCH_HTB=y CONFIG_NET_SCH_PRIO=y @@ -543,7 +544,6 @@ CONFIG_QCOM_WATCHDOG_V2=y CONFIG_QCOM_IRQ_HELPER=y CONFIG_QCOM_MEMORY_DUMP_V2=y CONFIG_ICNSS=y -CONFIG_ICNSS_DEBUG=y CONFIG_MSM_GLADIATOR_ERP_V2=y CONFIG_PANIC_ON_GLADIATOR_ERROR_V2=y CONFIG_MSM_GLADIATOR_HANG_DETECT=y diff --git a/arch/arm64/configs/sdm660-perf_defconfig b/arch/arm64/configs/sdm660-perf_defconfig index ffb983587c31..9a4841db89be 100644 --- a/arch/arm64/configs/sdm660-perf_defconfig +++ b/arch/arm64/configs/sdm660-perf_defconfig @@ -526,6 +526,7 @@ CONFIG_ARM_SMMU=y CONFIG_IOMMU_DEBUG=y CONFIG_IOMMU_DEBUG_TRACKING=y CONFIG_IOMMU_TESTS=y +CONFIG_QCOM_COMMON_LOG=y CONFIG_MSM_SMEM=y CONFIG_QPNP_HAPTIC=y CONFIG_QPNP_PBS=y @@ -556,6 +557,7 @@ CONFIG_QCOM_MEMORY_DUMP_V2=y CONFIG_ICNSS=y CONFIG_MSM_RUN_QUEUE_STATS=y CONFIG_MSM_BOOT_STATS=y +CONFIG_QCOM_CPUSS_DUMP=y CONFIG_MSM_ADSP_LOADER=y CONFIG_MSM_CDSP_LOADER=y CONFIG_MSM_PERFORMANCE=y diff --git a/arch/arm64/configs/sdm660_defconfig b/arch/arm64/configs/sdm660_defconfig index bba52749284a..13ae21bdd562 100644 --- a/arch/arm64/configs/sdm660_defconfig +++ b/arch/arm64/configs/sdm660_defconfig @@ -645,6 +645,8 @@ CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y CONFIG_SLUB_DEBUG_PANIC_ON=y +CONFIG_PAGE_POISONING=y +CONFIG_PAGE_POISONING_ENABLE_DEFAULT=y CONFIG_DEBUG_OBJECTS=y CONFIG_DEBUG_OBJECTS_FREE=y CONFIG_DEBUG_OBJECTS_TIMERS=y diff --git a/arch/arm64/crypto/Kconfig b/arch/arm64/crypto/Kconfig index 2cf32e9887e1..de1aab4b5da8 100644 --- a/arch/arm64/crypto/Kconfig +++ b/arch/arm64/crypto/Kconfig @@ -23,6 +23,11 @@ config CRYPTO_GHASH_ARM64_CE depends on ARM64 && KERNEL_MODE_NEON select CRYPTO_HASH +config CRYPTO_POLY_HASH_ARM64_CE + tristate "poly_hash (for HEH encryption mode) using ARMv8 Crypto Extensions" + depends on ARM64 && KERNEL_MODE_NEON + select CRYPTO_HASH + config CRYPTO_AES_ARM64_CE tristate "AES core cipher using ARMv8 Crypto Extensions" depends on ARM64 && KERNEL_MODE_NEON diff --git a/arch/arm64/crypto/Makefile b/arch/arm64/crypto/Makefile index abb79b3cfcfe..f0a8f2475ea3 100644 --- a/arch/arm64/crypto/Makefile +++ b/arch/arm64/crypto/Makefile @@ -17,6 +17,9 @@ sha2-ce-y := sha2-ce-glue.o sha2-ce-core.o obj-$(CONFIG_CRYPTO_GHASH_ARM64_CE) += ghash-ce.o ghash-ce-y := ghash-ce-glue.o ghash-ce-core.o +obj-$(CONFIG_CRYPTO_POLY_HASH_ARM64_CE) += poly-hash-ce.o +poly-hash-ce-y := poly-hash-ce-glue.o poly-hash-ce-core.o + obj-$(CONFIG_CRYPTO_AES_ARM64_CE) += aes-ce-cipher.o CFLAGS_aes-ce-cipher.o += -march=armv8-a+crypto diff --git a/arch/arm64/crypto/aes-glue.c b/arch/arm64/crypto/aes-glue.c index 6a51dfccfe71..448b874a4826 100644 --- a/arch/arm64/crypto/aes-glue.c +++ b/arch/arm64/crypto/aes-glue.c @@ -294,7 +294,7 @@ static struct crypto_alg aes_algs[] = { { .cra_blkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, - .ivsize = AES_BLOCK_SIZE, + .ivsize = 0, .setkey = aes_setkey, .encrypt = ecb_encrypt, .decrypt = ecb_decrypt, @@ -371,7 +371,7 @@ static struct crypto_alg aes_algs[] = { { .cra_ablkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, - .ivsize = AES_BLOCK_SIZE, + .ivsize = 0, .setkey = ablk_set_key, .encrypt = ablk_encrypt, .decrypt = ablk_decrypt, diff --git a/arch/arm64/crypto/poly-hash-ce-core.S b/arch/arm64/crypto/poly-hash-ce-core.S new file mode 100644 index 000000000000..8ccb544c5526 --- /dev/null +++ b/arch/arm64/crypto/poly-hash-ce-core.S @@ -0,0 +1,163 @@ +/* + * Accelerated poly_hash implementation with ARMv8 PMULL instructions. + * + * Based on ghash-ce-core.S. + * + * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org> + * Copyright (C) 2017 Google, Inc. <ebiggers@google.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> + + KEY .req v0 + KEY2 .req v1 + T1 .req v2 + T2 .req v3 + GSTAR .req v4 + XL .req v5 + XM .req v6 + XH .req v7 + + .text + .arch armv8-a+crypto + + /* 16-byte aligned (2**4 = 16); not required, but might as well */ + .align 4 +.Lgstar: + .quad 0x87, 0x87 + +/* + * void pmull_poly_hash_update(le128 *digest, const le128 *key, + * const u8 *src, unsigned int blocks, + * unsigned int partial); + */ +ENTRY(pmull_poly_hash_update) + + /* Load digest into XL */ + ld1 {XL.16b}, [x0] + + /* Load key into KEY */ + ld1 {KEY.16b}, [x1] + + /* Load g*(x) = g(x) + x^128 = x^7 + x^2 + x + 1 into both halves of + * GSTAR */ + adr x1, .Lgstar + ld1 {GSTAR.2d}, [x1] + + /* Set KEY2 to (KEY[1]+KEY[0]):(KEY[1]+KEY[0]). This is needed for + * Karatsuba multiplication. */ + ext KEY2.16b, KEY.16b, KEY.16b, #8 + eor KEY2.16b, KEY2.16b, KEY.16b + + /* If 'partial' is nonzero, then we're finishing a pending block and + * should go right to the multiplication. */ + cbnz w4, 1f + +0: + /* Add the next block from 'src' to the digest */ + ld1 {T1.16b}, [x2], #16 + eor XL.16b, XL.16b, T1.16b + sub w3, w3, #1 + +1: + /* + * Multiply the current 128-bit digest (a1:a0, in XL) by the 128-bit key + * (b1:b0, in KEY) using Karatsuba multiplication. + */ + + /* T1 = (a1+a0):(a1+a0) */ + ext T1.16b, XL.16b, XL.16b, #8 + eor T1.16b, T1.16b, XL.16b + + /* XH = a1 * b1 */ + pmull2 XH.1q, XL.2d, KEY.2d + + /* XL = a0 * b0 */ + pmull XL.1q, XL.1d, KEY.1d + + /* XM = (a1+a0) * (b1+b0) */ + pmull XM.1q, T1.1d, KEY2.1d + + /* XM += (XH[0]:XL[1]) + XL + XH */ + ext T1.16b, XL.16b, XH.16b, #8 + eor T2.16b, XL.16b, XH.16b + eor XM.16b, XM.16b, T1.16b + eor XM.16b, XM.16b, T2.16b + + /* + * Now the 256-bit product is in XH[1]:XM:XL[0]. It represents a + * polynomial over GF(2) with degree as large as 255. We need to + * compute its remainder modulo g(x) = x^128+x^7+x^2+x+1. For this it + * is sufficient to compute the remainder of the high half 'c(x)x^128' + * add it to the low half. To reduce the high half we use the Barrett + * reduction method. The basic idea is that we can express the + * remainder p(x) as g(x)q(x) mod x^128, where q(x) = (c(x)x^128)/g(x). + * As detailed in [1], to avoid having to divide by g(x) at runtime the + * following equivalent expression can be derived: + * + * p(x) = [ g*(x)((c(x)q+(x))/x^128) ] mod x^128 + * + * where g*(x) = x^128+g(x) = x^7+x^2+x+1, and q+(x) = x^256/g(x) = g(x) + * in this case. This is also equivalent to: + * + * p(x) = [ g*(x)((c(x)(x^128 + g*(x)))/x^128) ] mod x^128 + * = [ g*(x)(c(x) + (c(x)g*(x))/x^128) ] mod x^128 + * + * Since deg g*(x) < 64: + * + * p(x) = [ g*(x)(c(x) + ((c(x)/x^64)g*(x))/x^64) ] mod x^128 + * = [ g*(x)((c(x)/x^64)x^64 + (c(x) mod x^64) + + * ((c(x)/x^64)g*(x))/x^64) ] mod x^128 + * + * Letting t(x) = g*(x)(c(x)/x^64): + * + * p(x) = [ t(x)x^64 + g*(x)((c(x) mod x^64) + t(x)/x^64) ] mod x^128 + * + * Therefore, to do the reduction we only need to issue two 64-bit => + * 128-bit carryless multiplications: g*(x) times c(x)/x^64, and g*(x) + * times ((c(x) mod x^64) + t(x)/x^64). (Multiplication by x^64 doesn't + * count since it is simply a shift or move.) + * + * An alternate reduction method, also based on Barrett reduction and + * described in [1], uses only shifts and XORs --- no multiplications. + * However, the method with multiplications requires fewer instructions + * and is faster on processors with fast carryless multiplication. + * + * [1] "Intel Carry-Less Multiplication Instruction and its Usage for + * Computing the GCM Mode", + * https://software.intel.com/sites/default/files/managed/72/cc/clmul-wp-rev-2.02-2014-04-20.pdf + */ + + /* 256-bit product is XH[1]:XM:XL[0], so c(x) is XH[1]:XM[1] */ + + /* T1 = t(x) = g*(x)(c(x)/x^64) */ + pmull2 T1.1q, GSTAR.2d, XH.2d + + /* T2 = g*(x)((c(x) mod x^64) + t(x)/x^64) */ + eor T2.16b, XM.16b, T1.16b + pmull2 T2.1q, GSTAR.2d, T2.2d + + /* Make XL[0] be the low half of the 128-bit result by adding the low 64 + * bits of the T2 term to what was already there. The 't(x)x^64' term + * makes no difference, so skip it. */ + eor XL.16b, XL.16b, T2.16b + + /* Make XL[1] be the high half of the 128-bit result by adding the high + * 64 bits of the 't(x)x^64' and T2 terms to what was already in XM[0], + * then moving XM[0] to XL[1]. */ + eor XM.16b, XM.16b, T1.16b + ext T2.16b, T2.16b, T2.16b, #8 + eor XM.16b, XM.16b, T2.16b + mov XL.d[1], XM.d[0] + + /* If more blocks remain, then loop back to process the next block; + * else, store the digest and return. */ + cbnz w3, 0b + st1 {XL.16b}, [x0] + ret +ENDPROC(pmull_poly_hash_update) diff --git a/arch/arm64/crypto/poly-hash-ce-glue.c b/arch/arm64/crypto/poly-hash-ce-glue.c new file mode 100644 index 000000000000..e195740c9ecf --- /dev/null +++ b/arch/arm64/crypto/poly-hash-ce-glue.c @@ -0,0 +1,166 @@ +/* + * Accelerated poly_hash implementation with ARMv8 PMULL instructions. + * + * Based on ghash-ce-glue.c. + * + * poly_hash is part of the HEH (Hash-Encrypt-Hash) encryption mode, proposed in + * Internet Draft https://tools.ietf.org/html/draft-cope-heh-01. + * + * poly_hash is very similar to GHASH: both algorithms are keyed hashes which + * interpret their input data as coefficients of a polynomial over GF(2^128), + * then calculate a hash value by evaluating that polynomial at the point given + * by the key, e.g. using Horner's rule. The difference is that poly_hash uses + * the more natural "ble" convention to represent GF(2^128) elements, whereas + * GHASH uses the less natural "lle" convention (see include/crypto/gf128mul.h). + * The ble convention makes it simpler to implement GF(2^128) multiplication. + * + * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org> + * Copyright (C) 2017 Google Inc. <ebiggers@google.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include <asm/neon.h> +#include <crypto/b128ops.h> +#include <crypto/internal/hash.h> +#include <linux/cpufeature.h> +#include <linux/crypto.h> +#include <linux/module.h> + +/* + * Note: in this algorithm we currently use 'le128' to represent GF(2^128) + * elements, even though poly_hash-generic uses 'be128'. Both types are + * actually "wrong" because the elements are actually in 'ble' format, and there + * should be a ble type to represent this --- as well as lle, bbe, and lbe types + * for the other conventions for representing GF(2^128) elements. But + * practically it doesn't matter which type we choose here, so we just use le128 + * since it's arguably more accurate, while poly_hash-generic still has to use + * be128 because the generic GF(2^128) multiplication functions all take be128. + */ + +struct poly_hash_desc_ctx { + le128 digest; + unsigned int count; +}; + +asmlinkage void pmull_poly_hash_update(le128 *digest, const le128 *key, + const u8 *src, unsigned int blocks, + unsigned int partial); + +static int poly_hash_setkey(struct crypto_shash *tfm, + const u8 *key, unsigned int keylen) +{ + if (keylen != sizeof(le128)) { + crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + + memcpy(crypto_shash_ctx(tfm), key, sizeof(le128)); + return 0; +} + +static int poly_hash_init(struct shash_desc *desc) +{ + struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc); + + ctx->digest = (le128) { 0 }; + ctx->count = 0; + return 0; +} + +static int poly_hash_update(struct shash_desc *desc, const u8 *src, + unsigned int len) +{ + struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc); + unsigned int partial = ctx->count % sizeof(le128); + u8 *dst = (u8 *)&ctx->digest + partial; + + ctx->count += len; + + /* Finishing at least one block? */ + if (partial + len >= sizeof(le128)) { + const le128 *key = crypto_shash_ctx(desc->tfm); + + if (partial) { + /* Finish the pending block. */ + unsigned int n = sizeof(le128) - partial; + + len -= n; + do { + *dst++ ^= *src++; + } while (--n); + } + + /* + * Do the real work. If 'partial' is nonzero, this starts by + * multiplying 'digest' by 'key'. Then for each additional full + * block it adds the block to 'digest' and multiplies by 'key'. + */ + kernel_neon_begin_partial(8); + pmull_poly_hash_update(&ctx->digest, key, src, + len / sizeof(le128), partial); + kernel_neon_end(); + + src += len - (len % sizeof(le128)); + len %= sizeof(le128); + dst = (u8 *)&ctx->digest; + } + + /* Continue adding the next block to 'digest'. */ + while (len--) + *dst++ ^= *src++; + return 0; +} + +static int poly_hash_final(struct shash_desc *desc, u8 *out) +{ + struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc); + unsigned int partial = ctx->count % sizeof(le128); + + /* Finish the last block if needed. */ + if (partial) { + const le128 *key = crypto_shash_ctx(desc->tfm); + + kernel_neon_begin_partial(8); + pmull_poly_hash_update(&ctx->digest, key, NULL, 0, partial); + kernel_neon_end(); + } + + memcpy(out, &ctx->digest, sizeof(le128)); + return 0; +} + +static struct shash_alg poly_hash_alg = { + .digestsize = sizeof(le128), + .init = poly_hash_init, + .update = poly_hash_update, + .final = poly_hash_final, + .setkey = poly_hash_setkey, + .descsize = sizeof(struct poly_hash_desc_ctx), + .base = { + .cra_name = "poly_hash", + .cra_driver_name = "poly_hash-ce", + .cra_priority = 300, + .cra_ctxsize = sizeof(le128), + .cra_module = THIS_MODULE, + }, +}; + +static int __init poly_hash_ce_mod_init(void) +{ + return crypto_register_shash(&poly_hash_alg); +} + +static void __exit poly_hash_ce_mod_exit(void) +{ + crypto_unregister_shash(&poly_hash_alg); +} + +MODULE_DESCRIPTION("Polynomial evaluation hash using ARMv8 Crypto Extensions"); +MODULE_AUTHOR("Eric Biggers <ebiggers@google.com>"); +MODULE_LICENSE("GPL v2"); + +module_cpu_feature_match(PMULL, poly_hash_ce_mod_init); +module_exit(poly_hash_ce_mod_exit); diff --git a/arch/arm64/include/asm/cache.h b/arch/arm64/include/asm/cache.h index 5082b30bc2c0..f9359d32fae5 100644 --- a/arch/arm64/include/asm/cache.h +++ b/arch/arm64/include/asm/cache.h @@ -18,17 +18,17 @@ #include <asm/cachetype.h> -#define L1_CACHE_SHIFT 7 +#define L1_CACHE_SHIFT 6 #define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) /* * Memory returned by kmalloc() may be used for DMA, so we must make - * sure that all such allocations are cache aligned. Otherwise, - * unrelated code may cause parts of the buffer to be read into the - * cache before the transfer is done, causing old data to be seen by - * the CPU. + * sure that all such allocations are aligned to the maximum *known* + * cache line size on ARMv8 systems. Otherwise, unrelated code may + * cause parts of the buffer to be read into the cache before the + * transfer is done, causing old data to be seen by the CPU. */ -#define ARCH_DMA_MINALIGN L1_CACHE_BYTES +#define ARCH_DMA_MINALIGN (128) #ifndef __ASSEMBLY__ diff --git a/arch/arm64/include/asm/hw_breakpoint.h b/arch/arm64/include/asm/hw_breakpoint.h index 9732908bfc8a..c72b8e201ab4 100644 --- a/arch/arm64/include/asm/hw_breakpoint.h +++ b/arch/arm64/include/asm/hw_breakpoint.h @@ -68,7 +68,11 @@ static inline void decode_ctrl_reg(u32 reg, /* Lengths */ #define ARM_BREAKPOINT_LEN_1 0x1 #define ARM_BREAKPOINT_LEN_2 0x3 +#define ARM_BREAKPOINT_LEN_3 0x7 #define ARM_BREAKPOINT_LEN_4 0xf +#define ARM_BREAKPOINT_LEN_5 0x1f +#define ARM_BREAKPOINT_LEN_6 0x3f +#define ARM_BREAKPOINT_LEN_7 0x7f #define ARM_BREAKPOINT_LEN_8 0xff /* Kernel stepping */ @@ -110,7 +114,7 @@ struct perf_event; struct pmu; extern int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl, - int *gen_len, int *gen_type); + int *gen_len, int *gen_type, int *offset); extern int arch_check_bp_in_kernelspace(struct perf_event *bp); extern int arch_validate_hwbkpt_settings(struct perf_event *bp); extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused, diff --git a/arch/arm64/include/asm/vdso_datapage.h b/arch/arm64/include/asm/vdso_datapage.h index de66199673d7..2b9a63771eda 100644 --- a/arch/arm64/include/asm/vdso_datapage.h +++ b/arch/arm64/include/asm/vdso_datapage.h @@ -22,6 +22,8 @@ struct vdso_data { __u64 cs_cycle_last; /* Timebase at clocksource init */ + __u64 raw_time_sec; /* Raw time */ + __u64 raw_time_nsec; __u64 xtime_clock_sec; /* Kernel time */ __u64 xtime_clock_nsec; __u64 xtime_coarse_sec; /* Coarse time */ @@ -29,8 +31,10 @@ struct vdso_data { __u64 wtm_clock_sec; /* Wall to monotonic time */ __u64 wtm_clock_nsec; __u32 tb_seq_count; /* Timebase sequence counter */ - __u32 cs_mult; /* Clocksource multiplier */ - __u32 cs_shift; /* Clocksource shift */ + /* cs_* members must be adjacent and in this order (ldp accesses) */ + __u32 cs_mono_mult; /* NTP-adjusted clocksource multiplier */ + __u32 cs_shift; /* Clocksource shift (mono = raw) */ + __u32 cs_raw_mult; /* Raw clocksource multiplier */ __u32 tz_minuteswest; /* Whacky timezone stuff */ __u32 tz_dsttime; __u32 use_syscall; diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index d3cfa681654f..69c203d4d63f 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -52,7 +52,3 @@ obj-y += $(arm64-obj-y) vdso/ probes/ obj-m += $(arm64-obj-m) head-y := head.o extra-y += $(head-y) vmlinux.lds - -# vDSO - this must be built first to generate the symbol offsets -$(call objectify,$(arm64-obj-y)): $(obj)/vdso/vdso-offsets.h -$(obj)/vdso/vdso-offsets.h: $(obj)/vdso diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index c9ea87198789..350c0e99fc6b 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -92,6 +92,7 @@ int main(void) BLANK(); DEFINE(CLOCK_REALTIME, CLOCK_REALTIME); DEFINE(CLOCK_MONOTONIC, CLOCK_MONOTONIC); + DEFINE(CLOCK_MONOTONIC_RAW, CLOCK_MONOTONIC_RAW); DEFINE(CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC); DEFINE(CLOCK_REALTIME_COARSE, CLOCK_REALTIME_COARSE); DEFINE(CLOCK_MONOTONIC_COARSE,CLOCK_MONOTONIC_COARSE); @@ -99,6 +100,8 @@ int main(void) DEFINE(NSEC_PER_SEC, NSEC_PER_SEC); BLANK(); DEFINE(VDSO_CS_CYCLE_LAST, offsetof(struct vdso_data, cs_cycle_last)); + DEFINE(VDSO_RAW_TIME_SEC, offsetof(struct vdso_data, raw_time_sec)); + DEFINE(VDSO_RAW_TIME_NSEC, offsetof(struct vdso_data, raw_time_nsec)); DEFINE(VDSO_XTIME_CLK_SEC, offsetof(struct vdso_data, xtime_clock_sec)); DEFINE(VDSO_XTIME_CLK_NSEC, offsetof(struct vdso_data, xtime_clock_nsec)); DEFINE(VDSO_XTIME_CRS_SEC, offsetof(struct vdso_data, xtime_coarse_sec)); @@ -106,7 +109,8 @@ int main(void) DEFINE(VDSO_WTM_CLK_SEC, offsetof(struct vdso_data, wtm_clock_sec)); DEFINE(VDSO_WTM_CLK_NSEC, offsetof(struct vdso_data, wtm_clock_nsec)); DEFINE(VDSO_TB_SEQ_COUNT, offsetof(struct vdso_data, tb_seq_count)); - DEFINE(VDSO_CS_MULT, offsetof(struct vdso_data, cs_mult)); + DEFINE(VDSO_CS_MONO_MULT, offsetof(struct vdso_data, cs_mono_mult)); + DEFINE(VDSO_CS_RAW_MULT, offsetof(struct vdso_data, cs_raw_mult)); DEFINE(VDSO_CS_SHIFT, offsetof(struct vdso_data, cs_shift)); DEFINE(VDSO_TZ_MINWEST, offsetof(struct vdso_data, tz_minuteswest)); DEFINE(VDSO_TZ_DSTTIME, offsetof(struct vdso_data, tz_dsttime)); diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index cdf1dca64133..f75000996e4c 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -992,9 +992,9 @@ void __init setup_cpu_features(void) if (!cwg) pr_warn("No Cache Writeback Granule information, assuming cache line size %d\n", cls); - if (L1_CACHE_BYTES < cls) - pr_warn("L1_CACHE_BYTES smaller than the Cache Writeback Granule (%d < %d)\n", - L1_CACHE_BYTES, cls); + if (ARCH_DMA_MINALIGN < cls) + pr_warn("ARCH_DMA_MINALIGN smaller than the Cache Writeback Granule (%d < %d)\n", + ARCH_DMA_MINALIGN, cls); } static bool __maybe_unused diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index 367a954f9937..f4dfd8c41e06 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c @@ -317,9 +317,21 @@ static int get_hbp_len(u8 hbp_len) case ARM_BREAKPOINT_LEN_2: len_in_bytes = 2; break; + case ARM_BREAKPOINT_LEN_3: + len_in_bytes = 3; + break; case ARM_BREAKPOINT_LEN_4: len_in_bytes = 4; break; + case ARM_BREAKPOINT_LEN_5: + len_in_bytes = 5; + break; + case ARM_BREAKPOINT_LEN_6: + len_in_bytes = 6; + break; + case ARM_BREAKPOINT_LEN_7: + len_in_bytes = 7; + break; case ARM_BREAKPOINT_LEN_8: len_in_bytes = 8; break; @@ -349,7 +361,7 @@ int arch_check_bp_in_kernelspace(struct perf_event *bp) * to generic breakpoint descriptions. */ int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl, - int *gen_len, int *gen_type) + int *gen_len, int *gen_type, int *offset) { /* Type */ switch (ctrl.type) { @@ -369,17 +381,33 @@ int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl, return -EINVAL; } + if (!ctrl.len) + return -EINVAL; + *offset = __ffs(ctrl.len); + /* Len */ - switch (ctrl.len) { + switch (ctrl.len >> *offset) { case ARM_BREAKPOINT_LEN_1: *gen_len = HW_BREAKPOINT_LEN_1; break; case ARM_BREAKPOINT_LEN_2: *gen_len = HW_BREAKPOINT_LEN_2; break; + case ARM_BREAKPOINT_LEN_3: + *gen_len = HW_BREAKPOINT_LEN_3; + break; case ARM_BREAKPOINT_LEN_4: *gen_len = HW_BREAKPOINT_LEN_4; break; + case ARM_BREAKPOINT_LEN_5: + *gen_len = HW_BREAKPOINT_LEN_5; + break; + case ARM_BREAKPOINT_LEN_6: + *gen_len = HW_BREAKPOINT_LEN_6; + break; + case ARM_BREAKPOINT_LEN_7: + *gen_len = HW_BREAKPOINT_LEN_7; + break; case ARM_BREAKPOINT_LEN_8: *gen_len = HW_BREAKPOINT_LEN_8; break; @@ -423,9 +451,21 @@ static int arch_build_bp_info(struct perf_event *bp) case HW_BREAKPOINT_LEN_2: info->ctrl.len = ARM_BREAKPOINT_LEN_2; break; + case HW_BREAKPOINT_LEN_3: + info->ctrl.len = ARM_BREAKPOINT_LEN_3; + break; case HW_BREAKPOINT_LEN_4: info->ctrl.len = ARM_BREAKPOINT_LEN_4; break; + case HW_BREAKPOINT_LEN_5: + info->ctrl.len = ARM_BREAKPOINT_LEN_5; + break; + case HW_BREAKPOINT_LEN_6: + info->ctrl.len = ARM_BREAKPOINT_LEN_6; + break; + case HW_BREAKPOINT_LEN_7: + info->ctrl.len = ARM_BREAKPOINT_LEN_7; + break; case HW_BREAKPOINT_LEN_8: info->ctrl.len = ARM_BREAKPOINT_LEN_8; break; @@ -517,18 +557,17 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) default: return -EINVAL; } - - info->address &= ~alignment_mask; - info->ctrl.len <<= offset; } else { if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) alignment_mask = 0x3; else alignment_mask = 0x7; - if (info->address & alignment_mask) - return -EINVAL; + offset = info->address & alignment_mask; } + info->address &= ~alignment_mask; + info->ctrl.len <<= offset; + /* * Disallow per-task kernel breakpoints since these would * complicate the stepping code. @@ -661,12 +700,47 @@ unlock: } NOKPROBE_SYMBOL(breakpoint_handler); +/* + * Arm64 hardware does not always report a watchpoint hit address that matches + * one of the watchpoints set. It can also report an address "near" the + * watchpoint if a single instruction access both watched and unwatched + * addresses. There is no straight-forward way, short of disassembling the + * offending instruction, to map that address back to the watchpoint. This + * function computes the distance of the memory access from the watchpoint as a + * heuristic for the likelyhood that a given access triggered the watchpoint. + * + * See Section D2.10.5 "Determining the memory location that caused a Watchpoint + * exception" of ARMv8 Architecture Reference Manual for details. + * + * The function returns the distance of the address from the bytes watched by + * the watchpoint. In case of an exact match, it returns 0. + */ +static u64 get_distance_from_watchpoint(unsigned long addr, u64 val, + struct arch_hw_breakpoint_ctrl *ctrl) +{ + u64 wp_low, wp_high; + u32 lens, lene; + + lens = __ffs(ctrl->len); + lene = __fls(ctrl->len); + + wp_low = val + lens; + wp_high = val + lene; + if (addr < wp_low) + return wp_low - addr; + else if (addr > wp_high) + return addr - wp_high; + else + return 0; +} + static int watchpoint_handler(unsigned long addr, unsigned int esr, struct pt_regs *regs) { - int i, step = 0, *kernel_step, access; + int i, step = 0, *kernel_step, access, closest_match = 0; + u64 min_dist = -1, dist; u32 ctrl_reg; - u64 val, alignment_mask; + u64 val; struct perf_event *wp, **slots; struct debug_info *debug_info; struct arch_hw_breakpoint *info; @@ -675,35 +749,15 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr, slots = this_cpu_ptr(wp_on_reg); debug_info = ¤t->thread.debug; + /* + * Find all watchpoints that match the reported address. If no exact + * match is found. Attribute the hit to the closest watchpoint. + */ + rcu_read_lock(); for (i = 0; i < core_num_wrps; ++i) { - rcu_read_lock(); - wp = slots[i]; - if (wp == NULL) - goto unlock; - - info = counter_arch_bp(wp); - /* AArch32 watchpoints are either 4 or 8 bytes aligned. */ - if (is_compat_task()) { - if (info->ctrl.len == ARM_BREAKPOINT_LEN_8) - alignment_mask = 0x7; - else - alignment_mask = 0x3; - } else { - alignment_mask = 0x7; - } - - /* Check if the watchpoint value matches. */ - val = read_wb_reg(AARCH64_DBG_REG_WVR, i); - if (val != (addr & ~alignment_mask)) - goto unlock; - - /* Possible match, check the byte address select to confirm. */ - ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i); - decode_ctrl_reg(ctrl_reg, &ctrl); - if (!((1 << (addr & alignment_mask)) & ctrl.len)) - goto unlock; + continue; /* * Check that the access type matches. @@ -712,18 +766,41 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr, access = (esr & AARCH64_ESR_ACCESS_MASK) ? HW_BREAKPOINT_W : HW_BREAKPOINT_R; if (!(access & hw_breakpoint_type(wp))) - goto unlock; + continue; + /* Check if the watchpoint value and byte select match. */ + val = read_wb_reg(AARCH64_DBG_REG_WVR, i); + ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i); + decode_ctrl_reg(ctrl_reg, &ctrl); + dist = get_distance_from_watchpoint(addr, val, &ctrl); + if (dist < min_dist) { + min_dist = dist; + closest_match = i; + } + /* Is this an exact match? */ + if (dist != 0) + continue; + + info = counter_arch_bp(wp); info->trigger = addr; perf_bp_event(wp, regs); /* Do we need to handle the stepping? */ if (!wp->overflow_handler) step = 1; + } + if (min_dist > 0 && min_dist != -1) { + /* No exact match found. */ + wp = slots[closest_match]; + info = counter_arch_bp(wp); + info->trigger = addr; + perf_bp_event(wp, regs); -unlock: - rcu_read_unlock(); + /* Do we need to handle the stepping? */ + if (!wp->overflow_handler) + step = 1; } + rcu_read_unlock(); if (!step) return 0; diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index c5ef05959813..6204b7600d1b 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -327,13 +327,13 @@ static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type, struct arch_hw_breakpoint_ctrl ctrl, struct perf_event_attr *attr) { - int err, len, type, disabled = !ctrl.enabled; + int err, len, type, offset, disabled = !ctrl.enabled; attr->disabled = disabled; if (disabled) return 0; - err = arch_bp_generic_fields(ctrl, &len, &type); + err = arch_bp_generic_fields(ctrl, &len, &type, &offset); if (err) return err; @@ -352,6 +352,7 @@ static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type, attr->bp_len = len; attr->bp_type = type; + attr->bp_addr += offset; return 0; } @@ -404,7 +405,7 @@ static int ptrace_hbp_get_addr(unsigned int note_type, if (IS_ERR(bp)) return PTR_ERR(bp); - *addr = bp ? bp->attr.bp_addr : 0; + *addr = bp ? counter_arch_bp(bp)->address : 0; return 0; } diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index 97bc68f4c689..3b8acfae7797 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -55,7 +55,7 @@ struct vdso_data *vdso_data = &vdso_data_store.data; */ static struct page *vectors_page[1]; -static int alloc_vectors_page(void) +static int __init alloc_vectors_page(void) { extern char __kuser_helper_start[], __kuser_helper_end[]; extern char __aarch32_sigret_code_start[], __aarch32_sigret_code_end[]; @@ -88,7 +88,7 @@ int aarch32_setup_vectors_page(struct linux_binprm *bprm, int uses_interp) { struct mm_struct *mm = current->mm; unsigned long addr = AARCH32_VECTORS_BASE; - static struct vm_special_mapping spec = { + static const struct vm_special_mapping spec = { .name = "[vectors]", .pages = vectors_page, @@ -212,10 +212,16 @@ void update_vsyscall(struct timekeeper *tk) vdso_data->wtm_clock_nsec = tk->wall_to_monotonic.tv_nsec; if (!use_syscall) { + /* tkr_mono.cycle_last == tkr_raw.cycle_last */ vdso_data->cs_cycle_last = tk->tkr_mono.cycle_last; + vdso_data->raw_time_sec = tk->raw_time.tv_sec; + vdso_data->raw_time_nsec = tk->raw_time.tv_nsec; vdso_data->xtime_clock_sec = tk->xtime_sec; vdso_data->xtime_clock_nsec = tk->tkr_mono.xtime_nsec; - vdso_data->cs_mult = tk->tkr_mono.mult; + /* tkr_raw.xtime_nsec == 0 */ + vdso_data->cs_mono_mult = tk->tkr_mono.mult; + vdso_data->cs_raw_mult = tk->tkr_raw.mult; + /* tkr_mono.shift == tkr_raw.shift */ vdso_data->cs_shift = tk->tkr_mono.shift; } diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile index b467fd0a384b..62c84f7cb01b 100644 --- a/arch/arm64/kernel/vdso/Makefile +++ b/arch/arm64/kernel/vdso/Makefile @@ -23,7 +23,7 @@ GCOV_PROFILE := n ccflags-y += -Wl,-shared obj-y += vdso.o -extra-y += vdso.lds vdso-offsets.h +extra-y += vdso.lds CPPFLAGS_vdso.lds += -P -C -U$(ARCH) # Force dependency (incbin is bad) @@ -42,11 +42,10 @@ $(obj)/%.so: $(obj)/%.so.dbg FORCE gen-vdsosym := $(srctree)/$(src)/gen_vdso_offsets.sh quiet_cmd_vdsosym = VDSOSYM $@ define cmd_vdsosym - $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@ && \ - cp $@ include/generated/ + $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@ endef -$(obj)/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE +include/generated/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE $(call if_changed,vdsosym) # Assembly rules for the .S files diff --git a/arch/arm64/kernel/vdso/gettimeofday.S b/arch/arm64/kernel/vdso/gettimeofday.S index 9f8eeccae67c..e00b4671bd7c 100644 --- a/arch/arm64/kernel/vdso/gettimeofday.S +++ b/arch/arm64/kernel/vdso/gettimeofday.S @@ -26,24 +26,109 @@ #define NSEC_PER_SEC_HI16 0x3b9a vdso_data .req x6 -use_syscall .req w7 -seqcnt .req w8 +seqcnt .req w7 +w_tmp .req w8 +x_tmp .req x8 + +/* + * Conventions for macro arguments: + * - An argument is write-only if its name starts with "res". + * - All other arguments are read-only, unless otherwise specified. + */ .macro seqcnt_acquire 9999: ldr seqcnt, [vdso_data, #VDSO_TB_SEQ_COUNT] tbnz seqcnt, #0, 9999b dmb ishld - ldr use_syscall, [vdso_data, #VDSO_USE_SYSCALL] .endm - .macro seqcnt_read, cnt + .macro seqcnt_check fail dmb ishld - ldr \cnt, [vdso_data, #VDSO_TB_SEQ_COUNT] + ldr w_tmp, [vdso_data, #VDSO_TB_SEQ_COUNT] + cmp w_tmp, seqcnt + b.ne \fail .endm - .macro seqcnt_check, cnt, fail - cmp \cnt, seqcnt - b.ne \fail + .macro syscall_check fail + ldr w_tmp, [vdso_data, #VDSO_USE_SYSCALL] + cbnz w_tmp, \fail + .endm + + .macro get_nsec_per_sec res + mov \res, #NSEC_PER_SEC_LO16 + movk \res, #NSEC_PER_SEC_HI16, lsl #16 + .endm + + /* + * Returns the clock delta, in nanoseconds left-shifted by the clock + * shift. + */ + .macro get_clock_shifted_nsec res, cycle_last, mult + /* Read the virtual counter. */ + isb + mrs x_tmp, cntvct_el0 + /* Calculate cycle delta and convert to ns. */ + sub \res, x_tmp, \cycle_last + /* We can only guarantee 56 bits of precision. */ + movn x_tmp, #0xff00, lsl #48 + and \res, x_tmp, \res + mul \res, \res, \mult + .endm + + /* + * Returns in res_{sec,nsec} the REALTIME timespec, based on the + * "wall time" (xtime) and the clock_mono delta. + */ + .macro get_ts_realtime res_sec, res_nsec, \ + clock_nsec, xtime_sec, xtime_nsec, nsec_to_sec + add \res_nsec, \clock_nsec, \xtime_nsec + udiv x_tmp, \res_nsec, \nsec_to_sec + add \res_sec, \xtime_sec, x_tmp + msub \res_nsec, x_tmp, \nsec_to_sec, \res_nsec + .endm + + /* + * Returns in res_{sec,nsec} the timespec based on the clock_raw delta, + * used for CLOCK_MONOTONIC_RAW. + */ + .macro get_ts_clock_raw res_sec, res_nsec, clock_nsec, nsec_to_sec + udiv \res_sec, \clock_nsec, \nsec_to_sec + msub \res_nsec, \res_sec, \nsec_to_sec, \clock_nsec + .endm + + /* sec and nsec are modified in place. */ + .macro add_ts sec, nsec, ts_sec, ts_nsec, nsec_to_sec + /* Add timespec. */ + add \sec, \sec, \ts_sec + add \nsec, \nsec, \ts_nsec + + /* Normalise the new timespec. */ + cmp \nsec, \nsec_to_sec + b.lt 9999f + sub \nsec, \nsec, \nsec_to_sec + add \sec, \sec, #1 +9999: + cmp \nsec, #0 + b.ge 9998f + add \nsec, \nsec, \nsec_to_sec + sub \sec, \sec, #1 +9998: + .endm + + .macro clock_gettime_return, shift=0 + .if \shift == 1 + lsr x11, x11, x12 + .endif + stp x10, x11, [x1, #TSPEC_TV_SEC] + mov x0, xzr + ret + .endm + + .macro jump_slot jumptable, index, label + .if (. - \jumptable) != 4 * (\index) + .error "Jump slot index mismatch" + .endif + b \label .endm .text @@ -51,18 +136,25 @@ seqcnt .req w8 /* int __kernel_gettimeofday(struct timeval *tv, struct timezone *tz); */ ENTRY(__kernel_gettimeofday) .cfi_startproc - mov x2, x30 - .cfi_register x30, x2 - - /* Acquire the sequence counter and get the timespec. */ adr vdso_data, _vdso_data -1: seqcnt_acquire - cbnz use_syscall, 4f - /* If tv is NULL, skip to the timezone code. */ cbz x0, 2f - bl __do_get_tspec - seqcnt_check w9, 1b + + /* Compute the time of day. */ +1: seqcnt_acquire + syscall_check fail=4f + ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST] + /* w11 = cs_mono_mult, w12 = cs_shift */ + ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT] + ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC] + seqcnt_check fail=1b + + get_nsec_per_sec res=x9 + lsl x9, x9, x12 + + get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11 + get_ts_realtime res_sec=x10, res_nsec=x11, \ + clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9 /* Convert ns to us. */ mov x13, #1000 @@ -76,95 +168,126 @@ ENTRY(__kernel_gettimeofday) stp w4, w5, [x1, #TZ_MINWEST] 3: mov x0, xzr - ret x2 + ret 4: /* Syscall fallback. */ mov x8, #__NR_gettimeofday svc #0 - ret x2 + ret .cfi_endproc ENDPROC(__kernel_gettimeofday) +#define JUMPSLOT_MAX CLOCK_MONOTONIC_COARSE + /* int __kernel_clock_gettime(clockid_t clock_id, struct timespec *tp); */ ENTRY(__kernel_clock_gettime) .cfi_startproc - cmp w0, #CLOCK_REALTIME - ccmp w0, #CLOCK_MONOTONIC, #0x4, ne - b.ne 2f + cmp w0, #JUMPSLOT_MAX + b.hi syscall + adr vdso_data, _vdso_data + adr x_tmp, jumptable + add x_tmp, x_tmp, w0, uxtw #2 + br x_tmp + + ALIGN +jumptable: + jump_slot jumptable, CLOCK_REALTIME, realtime + jump_slot jumptable, CLOCK_MONOTONIC, monotonic + b syscall + b syscall + jump_slot jumptable, CLOCK_MONOTONIC_RAW, monotonic_raw + jump_slot jumptable, CLOCK_REALTIME_COARSE, realtime_coarse + jump_slot jumptable, CLOCK_MONOTONIC_COARSE, monotonic_coarse + + .if (. - jumptable) != 4 * (JUMPSLOT_MAX + 1) + .error "Wrong jumptable size" + .endif + + ALIGN +realtime: + seqcnt_acquire + syscall_check fail=syscall + ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST] + /* w11 = cs_mono_mult, w12 = cs_shift */ + ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT] + ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC] + seqcnt_check fail=realtime - mov x2, x30 - .cfi_register x30, x2 + /* All computations are done with left-shifted nsecs. */ + get_nsec_per_sec res=x9 + lsl x9, x9, x12 - /* Get kernel timespec. */ - adr vdso_data, _vdso_data -1: seqcnt_acquire - cbnz use_syscall, 7f + get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11 + get_ts_realtime res_sec=x10, res_nsec=x11, \ + clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9 + clock_gettime_return, shift=1 - bl __do_get_tspec - seqcnt_check w9, 1b + ALIGN +monotonic: + seqcnt_acquire + syscall_check fail=syscall + ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST] + /* w11 = cs_mono_mult, w12 = cs_shift */ + ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT] + ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC] + ldp x3, x4, [vdso_data, #VDSO_WTM_CLK_SEC] + seqcnt_check fail=monotonic - mov x30, x2 + /* All computations are done with left-shifted nsecs. */ + lsl x4, x4, x12 + get_nsec_per_sec res=x9 + lsl x9, x9, x12 - cmp w0, #CLOCK_MONOTONIC - b.ne 6f + get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11 + get_ts_realtime res_sec=x10, res_nsec=x11, \ + clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9 - /* Get wtm timespec. */ - ldp x13, x14, [vdso_data, #VDSO_WTM_CLK_SEC] + add_ts sec=x10, nsec=x11, ts_sec=x3, ts_nsec=x4, nsec_to_sec=x9 + clock_gettime_return, shift=1 - /* Check the sequence counter. */ - seqcnt_read w9 - seqcnt_check w9, 1b - b 4f -2: - cmp w0, #CLOCK_REALTIME_COARSE - ccmp w0, #CLOCK_MONOTONIC_COARSE, #0x4, ne - b.ne 8f + ALIGN +monotonic_raw: + seqcnt_acquire + syscall_check fail=syscall + ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST] + /* w11 = cs_raw_mult, w12 = cs_shift */ + ldp w12, w11, [vdso_data, #VDSO_CS_SHIFT] + ldp x13, x14, [vdso_data, #VDSO_RAW_TIME_SEC] + seqcnt_check fail=monotonic_raw - /* xtime_coarse_nsec is already right-shifted */ - mov x12, #0 + /* All computations are done with left-shifted nsecs. */ + lsl x14, x14, x12 + get_nsec_per_sec res=x9 + lsl x9, x9, x12 - /* Get coarse timespec. */ - adr vdso_data, _vdso_data -3: seqcnt_acquire - ldp x10, x11, [vdso_data, #VDSO_XTIME_CRS_SEC] + get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11 + get_ts_clock_raw res_sec=x10, res_nsec=x11, \ + clock_nsec=x15, nsec_to_sec=x9 - /* Get wtm timespec. */ - ldp x13, x14, [vdso_data, #VDSO_WTM_CLK_SEC] + add_ts sec=x10, nsec=x11, ts_sec=x13, ts_nsec=x14, nsec_to_sec=x9 + clock_gettime_return, shift=1 - /* Check the sequence counter. */ - seqcnt_read w9 - seqcnt_check w9, 3b + ALIGN +realtime_coarse: + seqcnt_acquire + ldp x10, x11, [vdso_data, #VDSO_XTIME_CRS_SEC] + seqcnt_check fail=realtime_coarse + clock_gettime_return - cmp w0, #CLOCK_MONOTONIC_COARSE - b.ne 6f -4: - /* Add on wtm timespec. */ - add x10, x10, x13 - lsl x14, x14, x12 - add x11, x11, x14 + ALIGN +monotonic_coarse: + seqcnt_acquire + ldp x10, x11, [vdso_data, #VDSO_XTIME_CRS_SEC] + ldp x13, x14, [vdso_data, #VDSO_WTM_CLK_SEC] + seqcnt_check fail=monotonic_coarse - /* Normalise the new timespec. */ - mov x15, #NSEC_PER_SEC_LO16 - movk x15, #NSEC_PER_SEC_HI16, lsl #16 - lsl x15, x15, x12 - cmp x11, x15 - b.lt 5f - sub x11, x11, x15 - add x10, x10, #1 -5: - cmp x11, #0 - b.ge 6f - add x11, x11, x15 - sub x10, x10, #1 + /* Computations are done in (non-shifted) nsecs. */ + get_nsec_per_sec res=x9 + add_ts sec=x10, nsec=x11, ts_sec=x13, ts_nsec=x14, nsec_to_sec=x9 + clock_gettime_return -6: /* Store to the user timespec. */ - lsr x11, x11, x12 - stp x10, x11, [x1, #TSPEC_TV_SEC] - mov x0, xzr - ret -7: - mov x30, x2 -8: /* Syscall fallback. */ + ALIGN +syscall: /* Syscall fallback. */ mov x8, #__NR_clock_gettime svc #0 ret @@ -176,6 +299,7 @@ ENTRY(__kernel_clock_getres) .cfi_startproc cmp w0, #CLOCK_REALTIME ccmp w0, #CLOCK_MONOTONIC, #0x4, ne + ccmp w0, #CLOCK_MONOTONIC_RAW, #0x4, ne b.ne 1f ldr x2, 5f @@ -203,55 +327,3 @@ ENTRY(__kernel_clock_getres) .quad CLOCK_COARSE_RES .cfi_endproc ENDPROC(__kernel_clock_getres) - -/* - * Read the current time from the architected counter. - * Expects vdso_data to be initialised. - * Clobbers the temporary registers (x9 - x15). - * Returns: - * - w9 = vDSO sequence counter - * - (x10, x11) = (ts->tv_sec, shifted ts->tv_nsec) - * - w12 = cs_shift - */ -ENTRY(__do_get_tspec) - .cfi_startproc - - /* Read the virtual counter. */ - isb -#if IS_ENABLED(CONFIG_MSM_TIMER_LEAP) -#define LEAST_32BITS 0x00000000FFFFFFFF -reread: - mrs x15, cntvct_el0 - and x13, x15, #LEAST_32BITS - eor x13, x13, #LEAST_32BITS - cbz x13, reread -#else - mrs x15, cntvct_el0 -#endif - - /* Read from the vDSO data page. */ - ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST] - ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC] - ldp w11, w12, [vdso_data, #VDSO_CS_MULT] - seqcnt_read w9 - - /* Calculate cycle delta and convert to ns. */ - sub x10, x15, x10 - /* We can only guarantee 56 bits of precision. */ - movn x15, #0xff00, lsl #48 - and x10, x15, x10 - mul x10, x10, x11 - - /* Use the kernel time to calculate the new timespec. */ - mov x11, #NSEC_PER_SEC_LO16 - movk x11, #NSEC_PER_SEC_HI16, lsl #16 - lsl x11, x11, x12 - add x15, x10, x14 - udiv x14, x15, x11 - add x10, x13, x14 - mul x13, x14, x11 - sub x11, x15, x13 - - ret - .cfi_endproc -ENDPROC(__do_get_tspec) diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index a13b9a65322f..1804aea44faa 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -708,5 +708,6 @@ int cpu_enable_pan(void *__unused) int cpu_enable_uao(void *__unused) { asm(SET_PSTATE_UAO(1)); + return 0; } #endif /* CONFIG_ARM64_UAO */ diff --git a/arch/c6x/kernel/ptrace.c b/arch/c6x/kernel/ptrace.c index 3c494e84444d..a511ac16a8e3 100644 --- a/arch/c6x/kernel/ptrace.c +++ b/arch/c6x/kernel/ptrace.c @@ -69,46 +69,6 @@ static int gpr_get(struct task_struct *target, 0, sizeof(*regs)); } -static int gpr_set(struct task_struct *target, - const struct user_regset *regset, - unsigned int pos, unsigned int count, - const void *kbuf, const void __user *ubuf) -{ - int ret; - struct pt_regs *regs = task_pt_regs(target); - - /* Don't copyin TSR or CSR */ - ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, - ®s, - 0, PT_TSR * sizeof(long)); - if (ret) - return ret; - - ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, - PT_TSR * sizeof(long), - (PT_TSR + 1) * sizeof(long)); - if (ret) - return ret; - - ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, - ®s, - (PT_TSR + 1) * sizeof(long), - PT_CSR * sizeof(long)); - if (ret) - return ret; - - ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, - PT_CSR * sizeof(long), - (PT_CSR + 1) * sizeof(long)); - if (ret) - return ret; - - ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, - ®s, - (PT_CSR + 1) * sizeof(long), -1); - return ret; -} - enum c6x_regset { REGSET_GPR, }; @@ -120,7 +80,6 @@ static const struct user_regset c6x_regsets[] = { .size = sizeof(u32), .align = sizeof(u32), .get = gpr_get, - .set = gpr_set }, }; diff --git a/arch/h8300/kernel/ptrace.c b/arch/h8300/kernel/ptrace.c index 92075544a19a..0dc1c8f622bc 100644 --- a/arch/h8300/kernel/ptrace.c +++ b/arch/h8300/kernel/ptrace.c @@ -95,7 +95,8 @@ static int regs_get(struct task_struct *target, long *reg = (long *)®s; /* build user regs in buffer */ - for (r = 0; r < ARRAY_SIZE(register_offset); r++) + BUILD_BUG_ON(sizeof(regs) % sizeof(long) != 0); + for (r = 0; r < sizeof(regs) / sizeof(long); r++) *reg++ = h8300_get_reg(target, r); return user_regset_copyout(&pos, &count, &kbuf, &ubuf, @@ -113,7 +114,8 @@ static int regs_set(struct task_struct *target, long *reg; /* build user regs in buffer */ - for (reg = (long *)®s, r = 0; r < ARRAY_SIZE(register_offset); r++) + BUILD_BUG_ON(sizeof(regs) % sizeof(long) != 0); + for (reg = (long *)®s, r = 0; r < sizeof(regs) / sizeof(long); r++) *reg++ = h8300_get_reg(target, r); ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, @@ -122,7 +124,7 @@ static int regs_set(struct task_struct *target, return ret; /* write back to pt_regs */ - for (reg = (long *)®s, r = 0; r < ARRAY_SIZE(register_offset); r++) + for (reg = (long *)®s, r = 0; r < sizeof(regs) / sizeof(long); r++) h8300_put_reg(target, r, *reg++); return 0; } diff --git a/arch/ia64/Makefile b/arch/ia64/Makefile index 970d0bd99621..648f1cef33fa 100644 --- a/arch/ia64/Makefile +++ b/arch/ia64/Makefile @@ -95,8 +95,8 @@ define archhelp echo '* unwcheck - Check vmlinux for invalid unwind info' endef -archprepare: make_nr_irqs_h FORCE +archprepare: make_nr_irqs_h PHONY += make_nr_irqs_h FORCE -make_nr_irqs_h: FORCE +make_nr_irqs_h: $(Q)$(MAKE) $(build)=arch/ia64/kernel include/generated/nr-irqs.h diff --git a/arch/metag/kernel/ptrace.c b/arch/metag/kernel/ptrace.c index 7563628822bd..5e2dc7defd2c 100644 --- a/arch/metag/kernel/ptrace.c +++ b/arch/metag/kernel/ptrace.c @@ -24,6 +24,16 @@ * user_regset definitions. */ +static unsigned long user_txstatus(const struct pt_regs *regs) +{ + unsigned long data = (unsigned long)regs->ctx.Flags; + + if (regs->ctx.SaveMask & TBICTX_CBUF_BIT) + data |= USER_GP_REGS_STATUS_CATCH_BIT; + + return data; +} + int metag_gp_regs_copyout(const struct pt_regs *regs, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) @@ -62,9 +72,7 @@ int metag_gp_regs_copyout(const struct pt_regs *regs, if (ret) goto out; /* TXSTATUS */ - data = (unsigned long)regs->ctx.Flags; - if (regs->ctx.SaveMask & TBICTX_CBUF_BIT) - data |= USER_GP_REGS_STATUS_CATCH_BIT; + data = user_txstatus(regs); ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &data, 4*25, 4*26); if (ret) @@ -119,6 +127,7 @@ int metag_gp_regs_copyin(struct pt_regs *regs, if (ret) goto out; /* TXSTATUS */ + data = user_txstatus(regs); ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &data, 4*25, 4*26); if (ret) @@ -244,6 +253,8 @@ int metag_rp_state_copyin(struct pt_regs *regs, unsigned long long *ptr; int ret, i; + if (count < 4*13) + return -EINVAL; /* Read the entire pipeline before making any changes */ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &rp, 0, 4*13); @@ -303,7 +314,7 @@ static int metag_tls_set(struct task_struct *target, const void *kbuf, const void __user *ubuf) { int ret; - void __user *tls; + void __user *tls = target->thread.tls_ptr; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1); if (ret) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 74d581569778..c95bf18260f8 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -485,7 +485,8 @@ static int fpr_set(struct task_struct *target, &target->thread.fpu, 0, sizeof(elf_fpregset_t)); - for (i = 0; i < NUM_FPU_REGS; i++) { + BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t)); + for (i = 0; i < NUM_FPU_REGS && count >= sizeof(elf_fpreg_t); i++) { err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &fpr_val, i * sizeof(elf_fpreg_t), (i + 1) * sizeof(elf_fpreg_t)); diff --git a/arch/powerpc/boot/zImage.lds.S b/arch/powerpc/boot/zImage.lds.S index 861e72109df2..f080abfc2f83 100644 --- a/arch/powerpc/boot/zImage.lds.S +++ b/arch/powerpc/boot/zImage.lds.S @@ -68,6 +68,7 @@ SECTIONS } #ifdef CONFIG_PPC64_BOOT_WRAPPER + . = ALIGN(256); .got : { __toc_start = .; diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c index 5cc2e7af3a7b..b379146de55b 100644 --- a/arch/powerpc/kvm/emulate.c +++ b/arch/powerpc/kvm/emulate.c @@ -302,7 +302,6 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) advance = 0; printk(KERN_ERR "Couldn't emulate instruction 0x%08x " "(op %d xop %d)\n", inst, get_op(inst), get_xop(inst)); - kvmppc_core_queue_program(vcpu, 0); } } diff --git a/arch/s390/pci/pci_dma.c b/arch/s390/pci/pci_dma.c index 3a40f718baef..4004e03267cd 100644 --- a/arch/s390/pci/pci_dma.c +++ b/arch/s390/pci/pci_dma.c @@ -455,7 +455,7 @@ int zpci_dma_init_device(struct zpci_dev *zdev) zdev->dma_table = dma_alloc_cpu_table(); if (!zdev->dma_table) { rc = -ENOMEM; - goto out_clean; + goto out; } /* @@ -475,18 +475,22 @@ int zpci_dma_init_device(struct zpci_dev *zdev) zdev->iommu_bitmap = vzalloc(zdev->iommu_pages / 8); if (!zdev->iommu_bitmap) { rc = -ENOMEM; - goto out_reg; + goto free_dma_table; } rc = zpci_register_ioat(zdev, 0, zdev->start_dma, zdev->end_dma, (u64) zdev->dma_table); if (rc) - goto out_reg; - return 0; + goto free_bitmap; -out_reg: + return 0; +free_bitmap: + vfree(zdev->iommu_bitmap); + zdev->iommu_bitmap = NULL; +free_dma_table: dma_free_cpu_table(zdev->dma_table); -out_clean: + zdev->dma_table = NULL; +out: return rc; } diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c index 9ddc4928a089..c1566170964f 100644 --- a/arch/sparc/kernel/ptrace_64.c +++ b/arch/sparc/kernel/ptrace_64.c @@ -311,7 +311,7 @@ static int genregs64_set(struct task_struct *target, } if (!ret) { - unsigned long y; + unsigned long y = regs->y; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &y, diff --git a/arch/x86/crypto/ghash-clmulni-intel_glue.c b/arch/x86/crypto/ghash-clmulni-intel_glue.c index 440df0c7a2ee..a69321a77783 100644 --- a/arch/x86/crypto/ghash-clmulni-intel_glue.c +++ b/arch/x86/crypto/ghash-clmulni-intel_glue.c @@ -219,6 +219,29 @@ static int ghash_async_final(struct ahash_request *req) } } +static int ghash_async_import(struct ahash_request *req, const void *in) +{ + struct ahash_request *cryptd_req = ahash_request_ctx(req); + struct shash_desc *desc = cryptd_shash_desc(cryptd_req); + struct ghash_desc_ctx *dctx = shash_desc_ctx(desc); + + ghash_async_init(req); + memcpy(dctx, in, sizeof(*dctx)); + return 0; + +} + +static int ghash_async_export(struct ahash_request *req, void *out) +{ + struct ahash_request *cryptd_req = ahash_request_ctx(req); + struct shash_desc *desc = cryptd_shash_desc(cryptd_req); + struct ghash_desc_ctx *dctx = shash_desc_ctx(desc); + + memcpy(out, dctx, sizeof(*dctx)); + return 0; + +} + static int ghash_async_digest(struct ahash_request *req) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); @@ -288,8 +311,11 @@ static struct ahash_alg ghash_async_alg = { .final = ghash_async_final, .setkey = ghash_async_setkey, .digest = ghash_async_digest, + .export = ghash_async_export, + .import = ghash_async_import, .halg = { .digestsize = GHASH_DIGEST_SIZE, + .statesize = sizeof(struct ghash_desc_ctx), .base = { .cra_name = "ghash", .cra_driver_name = "ghash-clmulni", diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile index 265c0ed68118..7af017a8958f 100644 --- a/arch/x86/entry/vdso/Makefile +++ b/arch/x86/entry/vdso/Makefile @@ -187,10 +187,10 @@ vdso_img_insttargets := $(vdso_img_sodbg:%.dbg=install_%) $(MODLIB)/vdso: FORCE @mkdir -p $(MODLIB)/vdso -$(vdso_img_insttargets): install_%: $(obj)/%.dbg $(MODLIB)/vdso FORCE +$(vdso_img_insttargets): install_%: $(obj)/%.dbg $(MODLIB)/vdso $(call cmd,vdso_install) PHONY += vdso_install $(vdso_img_insttargets) -vdso_install: $(vdso_img_insttargets) FORCE +vdso_install: $(vdso_img_insttargets) clean-files := vdso32.so vdso32.so.dbg vdso64* vdso-image-*.c vdsox32.so* diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index cfc4a966e2b9..83b5f7a323a9 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -30,6 +30,7 @@ #include <asm/apic.h> #include <asm/timer.h> #include <asm/reboot.h> +#include <asm/nmi.h> struct ms_hyperv_info ms_hyperv; EXPORT_SYMBOL_GPL(ms_hyperv); @@ -157,6 +158,26 @@ static unsigned char hv_get_nmi_reason(void) return 0; } +#ifdef CONFIG_X86_LOCAL_APIC +/* + * Prior to WS2016 Debug-VM sends NMIs to all CPUs which makes + * it dificult to process CHANNELMSG_UNLOAD in case of crash. Handle + * unknown NMI on the first CPU which gets it. + */ +static int hv_nmi_unknown(unsigned int val, struct pt_regs *regs) +{ + static atomic_t nmi_cpu = ATOMIC_INIT(-1); + + if (!unknown_nmi_panic) + return NMI_DONE; + + if (atomic_cmpxchg(&nmi_cpu, -1, raw_smp_processor_id()) != -1) + return NMI_HANDLED; + + return NMI_DONE; +} +#endif + static void __init ms_hyperv_init_platform(void) { /* @@ -182,6 +203,9 @@ static void __init ms_hyperv_init_platform(void) printk(KERN_INFO "HyperV: LAPIC Timer Frequency: %#x\n", lapic_timer_frequency); } + + register_nmi_handler(NMI_UNKNOWN, hv_nmi_unknown, NMI_FLAG_FIRST, + "hv_nmi_unknown"); #endif if (ms_hyperv.features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE) diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 1a8256dd6729..5b2f2306fbcc 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1996,8 +1996,8 @@ static int x86_pmu_event_init(struct perf_event *event) static void refresh_pce(void *ignored) { - if (current->mm) - load_mm_cr4(current->mm); + if (current->active_mm) + load_mm_cr4(current->active_mm); } static void x86_pmu_event_mapped(struct perf_event *event) diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index f129a9af6357..b6b0077da1af 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c @@ -4,6 +4,7 @@ * Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE */ +#define DISABLE_BRANCH_PROFILING #include <linux/init.h> #include <linux/linkage.h> #include <linux/types.h> diff --git a/arch/x86/mm/kasan_init_64.c b/arch/x86/mm/kasan_init_64.c index d470cf219a2d..4e5ac46adc9d 100644 --- a/arch/x86/mm/kasan_init_64.c +++ b/arch/x86/mm/kasan_init_64.c @@ -1,3 +1,4 @@ +#define DISABLE_BRANCH_PROFILING #define pr_fmt(fmt) "kasan: " fmt #include <linux/bootmem.h> #include <linux/kasan.h> diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c index c6d6efed392a..7575f0798194 100644 --- a/arch/x86/pci/xen.c +++ b/arch/x86/pci/xen.c @@ -231,23 +231,14 @@ static int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) return 1; for_each_pci_msi_entry(msidesc, dev) { - __pci_read_msi_msg(msidesc, &msg); - pirq = MSI_ADDR_EXT_DEST_ID(msg.address_hi) | - ((msg.address_lo >> MSI_ADDR_DEST_ID_SHIFT) & 0xff); - if (msg.data != XEN_PIRQ_MSI_DATA || - xen_irq_from_pirq(pirq) < 0) { - pirq = xen_allocate_pirq_msi(dev, msidesc); - if (pirq < 0) { - irq = -ENODEV; - goto error; - } - xen_msi_compose_msg(dev, pirq, &msg); - __pci_write_msi_msg(msidesc, &msg); - dev_dbg(&dev->dev, "xen: msi bound to pirq=%d\n", pirq); - } else { - dev_dbg(&dev->dev, - "xen: msi already bound to pirq=%d\n", pirq); + pirq = xen_allocate_pirq_msi(dev, msidesc); + if (pirq < 0) { + irq = -ENODEV; + goto error; } + xen_msi_compose_msg(dev, pirq, &msg); + __pci_write_msi_msg(msidesc, &msg); + dev_dbg(&dev->dev, "xen: msi bound to pirq=%d\n", pirq); irq = xen_bind_pirq_msi_to_irq(dev, msidesc, pirq, (type == PCI_CAP_ID_MSI) ? nvec : 1, (type == PCI_CAP_ID_MSIX) ? diff --git a/arch/x86/xen/spinlock.c b/arch/x86/xen/spinlock.c index 9e2ba5c6e1dd..f42e78de1e10 100644 --- a/arch/x86/xen/spinlock.c +++ b/arch/x86/xen/spinlock.c @@ -27,6 +27,12 @@ static bool xen_pvspin = true; static void xen_qlock_kick(int cpu) { + int irq = per_cpu(lock_kicker_irq, cpu); + + /* Don't kick if the target's kicker interrupt is not initialized. */ + if (irq == -1) + return; + xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR); } diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index 0774799942e0..c6fee7437be4 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -182,6 +182,9 @@ static void blk_set_cmd_filter_defaults(struct blk_cmd_filter *filter) __set_bit(WRITE_16, filter->write_ok); __set_bit(WRITE_LONG, filter->write_ok); __set_bit(WRITE_LONG_2, filter->write_ok); + __set_bit(WRITE_SAME, filter->write_ok); + __set_bit(WRITE_SAME_16, filter->write_ok); + __set_bit(WRITE_SAME_32, filter->write_ok); __set_bit(ERASE, filter->write_ok); __set_bit(GPCMD_MODE_SELECT_10, filter->write_ok); __set_bit(MODE_SELECT, filter->write_ok); diff --git a/build.config.goldfish.arm b/build.config.goldfish.arm new file mode 100644 index 000000000000..866da9361b71 --- /dev/null +++ b/build.config.goldfish.arm @@ -0,0 +1,12 @@ +ARCH=arm +BRANCH=android-4.4 +CROSS_COMPILE=arm-linux-androidkernel- +DEFCONFIG=ranchu_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin +FILES=" +arch/arm/boot/zImage +vmlinux +System.map +" diff --git a/build.config.goldfish.arm64 b/build.config.goldfish.arm64 new file mode 100644 index 000000000000..9c963cf4a3d8 --- /dev/null +++ b/build.config.goldfish.arm64 @@ -0,0 +1,12 @@ +ARCH=arm64 +BRANCH=android-4.4 +CROSS_COMPILE=aarch64-linux-android- +DEFCONFIG=ranchu64_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin +FILES=" +arch/arm64/boot/Image +vmlinux +System.map +" diff --git a/build.config.goldfish.mips b/build.config.goldfish.mips new file mode 100644 index 000000000000..8af53d2c2940 --- /dev/null +++ b/build.config.goldfish.mips @@ -0,0 +1,11 @@ +ARCH=mips +BRANCH=android-4.4 +CROSS_COMPILE=mips64el-linux-android- +DEFCONFIG=ranchu_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9/bin +FILES=" +vmlinux +System.map +" diff --git a/build.config.goldfish.mips64 b/build.config.goldfish.mips64 new file mode 100644 index 000000000000..2a33d36dc4c8 --- /dev/null +++ b/build.config.goldfish.mips64 @@ -0,0 +1,11 @@ +ARCH=mips +BRANCH=android-4.4 +CROSS_COMPILE=mips64el-linux-android- +DEFCONFIG=ranchu64_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9/bin +FILES=" +vmlinux +System.map +" diff --git a/build.config.goldfish.x86 b/build.config.goldfish.x86 new file mode 100644 index 000000000000..f86253f58d4d --- /dev/null +++ b/build.config.goldfish.x86 @@ -0,0 +1,12 @@ +ARCH=x86 +BRANCH=android-4.4 +CROSS_COMPILE=x86_64-linux-android- +DEFCONFIG=i386_ranchu_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +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/build.config.goldfish.x86_64 b/build.config.goldfish.x86_64 new file mode 100644 index 000000000000..e1738861ec5c --- /dev/null +++ b/build.config.goldfish.x86_64 @@ -0,0 +1,12 @@ +ARCH=x86_64 +BRANCH=android-4.4 +CROSS_COMPILE=x86_64-linux-android- +DEFCONFIG=x86_64_ranchu_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +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/Kconfig b/crypto/Kconfig index 7240821137fd..3240d394426c 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -289,6 +289,24 @@ config CRYPTO_CBC CBC: Cipher Block Chaining mode This block cipher algorithm is required for IPSec. +config CRYPTO_HEH + tristate "HEH support" + select CRYPTO_CMAC + select CRYPTO_ECB + select CRYPTO_GF128MUL + select CRYPTO_MANAGER + select CRYPTO_POLY_HASH_ARM64_CE if ARM64 && KERNEL_MODE_NEON + help + HEH: Hash-Encrypt-Hash mode + HEH is a proposed block cipher mode of operation which extends the + strong pseudo-random permutation (SPRP) property of block ciphers to + arbitrary-length input strings. This provides a stronger notion of + security than existing block cipher modes of operation (e.g. CBC, CTR, + XTS), though it is usually less performant. Applications include disk + encryption and encryption of file names and contents. Currently, this + implementation only provides a symmetric cipher interface, so it can't + yet be used as an AEAD. + config CRYPTO_CTR tristate "CTR support" select CRYPTO_BLKCIPHER diff --git a/crypto/Makefile b/crypto/Makefile index 03e66097eb0c..8507d1fab3ac 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_CRYPTO_TGR192) += tgr192.o obj-$(CONFIG_CRYPTO_GF128MUL) += gf128mul.o obj-$(CONFIG_CRYPTO_ECB) += ecb.o obj-$(CONFIG_CRYPTO_CBC) += cbc.o +obj-$(CONFIG_CRYPTO_HEH) += heh.o obj-$(CONFIG_CRYPTO_PCBC) += pcbc.o obj-$(CONFIG_CRYPTO_CTS) += cts.o obj-$(CONFIG_CRYPTO_LRW) += lrw.o diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c index 68a5ceaa04c8..8d8b3eeba725 100644 --- a/crypto/algif_hash.c +++ b/crypto/algif_hash.c @@ -184,7 +184,7 @@ static int hash_accept(struct socket *sock, struct socket *newsock, int flags) struct alg_sock *ask = alg_sk(sk); struct hash_ctx *ctx = ask->private; struct ahash_request *req = &ctx->req; - char state[crypto_ahash_statesize(crypto_ahash_reqtfm(req))]; + char state[crypto_ahash_statesize(crypto_ahash_reqtfm(req)) ? : 1]; struct sock *sk2; struct alg_sock *ask2; struct hash_ctx *ctx2; diff --git a/crypto/blkcipher.c b/crypto/blkcipher.c index dca7bc87dad9..7bbfadc195a6 100644 --- a/crypto/blkcipher.c +++ b/crypto/blkcipher.c @@ -373,6 +373,27 @@ int blkcipher_aead_walk_virt_block(struct blkcipher_desc *desc, } EXPORT_SYMBOL_GPL(blkcipher_aead_walk_virt_block); +/* + * This function allows ablkcipher algorithms to use the blkcipher_walk API to + * walk over their data. The specified crypto_ablkcipher tfm is used to + * initialize the struct blkcipher_walk, and the crypto_blkcipher specified in + * desc->tfm is never used so it can be left NULL. (Yes, this design is ugly, + * but it parallels blkcipher_aead_walk_virt_block() above. In the 4.10 kernel + * this is starting to be cleaned up...) + */ +int blkcipher_ablkcipher_walk_virt(struct blkcipher_desc *desc, + struct blkcipher_walk *walk, + struct crypto_ablkcipher *tfm) +{ + walk->flags &= ~BLKCIPHER_WALK_PHYS; + walk->walk_blocksize = crypto_ablkcipher_blocksize(tfm); + walk->cipher_blocksize = walk->walk_blocksize; + walk->ivsize = crypto_ablkcipher_ivsize(tfm); + walk->alignmask = crypto_ablkcipher_alignmask(tfm); + return blkcipher_walk_first(desc, walk); +} +EXPORT_SYMBOL_GPL(blkcipher_ablkcipher_walk_virt); + static int setkey_unaligned(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen) { diff --git a/crypto/cryptd.c b/crypto/cryptd.c index e7aa904cb20b..26a504db3f53 100644 --- a/crypto/cryptd.c +++ b/crypto/cryptd.c @@ -642,6 +642,7 @@ static int cryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb, inst->alg.halg.base.cra_flags = type; inst->alg.halg.digestsize = salg->digestsize; + inst->alg.halg.statesize = salg->statesize; inst->alg.halg.base.cra_ctxsize = sizeof(struct cryptd_hash_ctx); inst->alg.halg.base.cra_init = cryptd_hash_init_tfm; diff --git a/crypto/gf128mul.c b/crypto/gf128mul.c index 5276607c72d0..f3d9f6da0767 100644 --- a/crypto/gf128mul.c +++ b/crypto/gf128mul.c @@ -44,7 +44,7 @@ --------------------------------------------------------------------------- Issue 31/01/2006 - This file provides fast multiplication in GF(128) as required by several + This file provides fast multiplication in GF(2^128) as required by several cryptographic authentication modes */ @@ -88,37 +88,52 @@ q(0xf8), q(0xf9), q(0xfa), q(0xfb), q(0xfc), q(0xfd), q(0xfe), q(0xff) \ } -/* Given the value i in 0..255 as the byte overflow when a field element - in GHASH is multiplied by x^8, this function will return the values that - are generated in the lo 16-bit word of the field value by applying the - modular polynomial. The values lo_byte and hi_byte are returned via the - macro xp_fun(lo_byte, hi_byte) so that the values can be assembled into - memory as required by a suitable definition of this macro operating on - the table above -*/ - -#define xx(p, q) 0x##p##q +/* + * Given a value i in 0..255 as the byte overflow when a field element + * in GF(2^128) is multiplied by x^8, the following macro returns the + * 16-bit value that must be XOR-ed into the low-degree end of the + * product to reduce it modulo the irreducible polynomial x^128 + x^7 + + * x^2 + x + 1. + * + * There are two versions of the macro, and hence two tables: one for + * the "be" convention where the highest-order bit is the coefficient of + * the highest-degree polynomial term, and one for the "le" convention + * where the highest-order bit is the coefficient of the lowest-degree + * polynomial term. In both cases the values are stored in CPU byte + * endianness such that the coefficients are ordered consistently across + * bytes, i.e. in the "be" table bits 15..0 of the stored value + * correspond to the coefficients of x^15..x^0, and in the "le" table + * bits 15..0 correspond to the coefficients of x^0..x^15. + * + * Therefore, provided that the appropriate byte endianness conversions + * are done by the multiplication functions (and these must be in place + * anyway to support both little endian and big endian CPUs), the "be" + * table can be used for multiplications of both "bbe" and "ble" + * elements, and the "le" table can be used for multiplications of both + * "lle" and "lbe" elements. + */ -#define xda_bbe(i) ( \ - (i & 0x80 ? xx(43, 80) : 0) ^ (i & 0x40 ? xx(21, c0) : 0) ^ \ - (i & 0x20 ? xx(10, e0) : 0) ^ (i & 0x10 ? xx(08, 70) : 0) ^ \ - (i & 0x08 ? xx(04, 38) : 0) ^ (i & 0x04 ? xx(02, 1c) : 0) ^ \ - (i & 0x02 ? xx(01, 0e) : 0) ^ (i & 0x01 ? xx(00, 87) : 0) \ +#define xda_be(i) ( \ + (i & 0x80 ? 0x4380 : 0) ^ (i & 0x40 ? 0x21c0 : 0) ^ \ + (i & 0x20 ? 0x10e0 : 0) ^ (i & 0x10 ? 0x0870 : 0) ^ \ + (i & 0x08 ? 0x0438 : 0) ^ (i & 0x04 ? 0x021c : 0) ^ \ + (i & 0x02 ? 0x010e : 0) ^ (i & 0x01 ? 0x0087 : 0) \ ) -#define xda_lle(i) ( \ - (i & 0x80 ? xx(e1, 00) : 0) ^ (i & 0x40 ? xx(70, 80) : 0) ^ \ - (i & 0x20 ? xx(38, 40) : 0) ^ (i & 0x10 ? xx(1c, 20) : 0) ^ \ - (i & 0x08 ? xx(0e, 10) : 0) ^ (i & 0x04 ? xx(07, 08) : 0) ^ \ - (i & 0x02 ? xx(03, 84) : 0) ^ (i & 0x01 ? xx(01, c2) : 0) \ +#define xda_le(i) ( \ + (i & 0x80 ? 0xe100 : 0) ^ (i & 0x40 ? 0x7080 : 0) ^ \ + (i & 0x20 ? 0x3840 : 0) ^ (i & 0x10 ? 0x1c20 : 0) ^ \ + (i & 0x08 ? 0x0e10 : 0) ^ (i & 0x04 ? 0x0708 : 0) ^ \ + (i & 0x02 ? 0x0384 : 0) ^ (i & 0x01 ? 0x01c2 : 0) \ ) -static const u16 gf128mul_table_lle[256] = gf128mul_dat(xda_lle); -static const u16 gf128mul_table_bbe[256] = gf128mul_dat(xda_bbe); +static const u16 gf128mul_table_le[256] = gf128mul_dat(xda_le); +static const u16 gf128mul_table_be[256] = gf128mul_dat(xda_be); -/* These functions multiply a field element by x, by x^4 and by x^8 - * in the polynomial field representation. It uses 32-bit word operations - * to gain speed but compensates for machine endianess and hence works +/* + * The following functions multiply a field element by x or by x^8 in + * the polynomial field representation. They use 64-bit word operations + * to gain speed but compensate for machine endianness and hence work * correctly on both styles of machine. */ @@ -126,7 +141,7 @@ static void gf128mul_x_lle(be128 *r, const be128 *x) { u64 a = be64_to_cpu(x->a); u64 b = be64_to_cpu(x->b); - u64 _tt = gf128mul_table_lle[(b << 7) & 0xff]; + u64 _tt = gf128mul_table_le[(b << 7) & 0xff]; r->b = cpu_to_be64((b >> 1) | (a << 63)); r->a = cpu_to_be64((a >> 1) ^ (_tt << 48)); @@ -136,7 +151,7 @@ static void gf128mul_x_bbe(be128 *r, const be128 *x) { u64 a = be64_to_cpu(x->a); u64 b = be64_to_cpu(x->b); - u64 _tt = gf128mul_table_bbe[a >> 63]; + u64 _tt = gf128mul_table_be[a >> 63]; r->a = cpu_to_be64((a << 1) | (b >> 63)); r->b = cpu_to_be64((b << 1) ^ _tt); @@ -146,7 +161,7 @@ void gf128mul_x_ble(be128 *r, const be128 *x) { u64 a = le64_to_cpu(x->a); u64 b = le64_to_cpu(x->b); - u64 _tt = gf128mul_table_bbe[b >> 63]; + u64 _tt = gf128mul_table_be[b >> 63]; r->a = cpu_to_le64((a << 1) ^ _tt); r->b = cpu_to_le64((b << 1) | (a >> 63)); @@ -157,7 +172,7 @@ static void gf128mul_x8_lle(be128 *x) { u64 a = be64_to_cpu(x->a); u64 b = be64_to_cpu(x->b); - u64 _tt = gf128mul_table_lle[b & 0xff]; + u64 _tt = gf128mul_table_le[b & 0xff]; x->b = cpu_to_be64((b >> 8) | (a << 56)); x->a = cpu_to_be64((a >> 8) ^ (_tt << 48)); @@ -167,12 +182,22 @@ static void gf128mul_x8_bbe(be128 *x) { u64 a = be64_to_cpu(x->a); u64 b = be64_to_cpu(x->b); - u64 _tt = gf128mul_table_bbe[a >> 56]; + u64 _tt = gf128mul_table_be[a >> 56]; x->a = cpu_to_be64((a << 8) | (b >> 56)); x->b = cpu_to_be64((b << 8) ^ _tt); } +static void gf128mul_x8_ble(be128 *x) +{ + u64 a = le64_to_cpu(x->b); + u64 b = le64_to_cpu(x->a); + u64 _tt = gf128mul_table_be[a >> 56]; + + x->b = cpu_to_le64((a << 8) | (b >> 56)); + x->a = cpu_to_le64((b << 8) ^ _tt); +} + void gf128mul_lle(be128 *r, const be128 *b) { be128 p[8]; @@ -249,9 +274,48 @@ void gf128mul_bbe(be128 *r, const be128 *b) } EXPORT_SYMBOL(gf128mul_bbe); +void gf128mul_ble(be128 *r, const be128 *b) +{ + be128 p[8]; + int i; + + p[0] = *r; + for (i = 0; i < 7; ++i) + gf128mul_x_ble((be128 *)&p[i + 1], (be128 *)&p[i]); + + memset(r, 0, sizeof(*r)); + for (i = 0;;) { + u8 ch = ((u8 *)b)[15 - i]; + + if (ch & 0x80) + be128_xor(r, r, &p[7]); + if (ch & 0x40) + be128_xor(r, r, &p[6]); + if (ch & 0x20) + be128_xor(r, r, &p[5]); + if (ch & 0x10) + be128_xor(r, r, &p[4]); + if (ch & 0x08) + be128_xor(r, r, &p[3]); + if (ch & 0x04) + be128_xor(r, r, &p[2]); + if (ch & 0x02) + be128_xor(r, r, &p[1]); + if (ch & 0x01) + be128_xor(r, r, &p[0]); + + if (++i >= 16) + break; + + gf128mul_x8_ble(r); + } +} +EXPORT_SYMBOL(gf128mul_ble); + + /* This version uses 64k bytes of table space. A 16 byte buffer has to be multiplied by a 16 byte key - value in GF(128). If we consider a GF(128) value in + value in GF(2^128). If we consider a GF(2^128) value in the buffer's lowest byte, we can construct a table of the 256 16 byte values that result from the 256 values of this byte. This requires 4096 bytes. But we also @@ -352,8 +416,8 @@ void gf128mul_free_64k(struct gf128mul_64k *t) int i; for (i = 0; i < 16; i++) - kfree(t->t[i]); - kfree(t); + kzfree(t->t[i]); + kzfree(t); } EXPORT_SYMBOL(gf128mul_free_64k); @@ -385,7 +449,7 @@ EXPORT_SYMBOL(gf128mul_64k_bbe); /* This version uses 4k bytes of table space. A 16 byte buffer has to be multiplied by a 16 byte key - value in GF(128). If we consider a GF(128) value in a + value in GF(2^128). If we consider a GF(2^128) value in a single byte, we can construct a table of the 256 16 byte values that result from the 256 values of this byte. This requires 4096 bytes. If we take the highest byte in @@ -443,6 +507,28 @@ out: } EXPORT_SYMBOL(gf128mul_init_4k_bbe); +struct gf128mul_4k *gf128mul_init_4k_ble(const be128 *g) +{ + struct gf128mul_4k *t; + int j, k; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + goto out; + + t->t[1] = *g; + for (j = 1; j <= 64; j <<= 1) + gf128mul_x_ble(&t->t[j + j], &t->t[j]); + + for (j = 2; j < 256; j += j) + for (k = 1; k < j; ++k) + be128_xor(&t->t[j + k], &t->t[j], &t->t[k]); + +out: + return t; +} +EXPORT_SYMBOL(gf128mul_init_4k_ble); + void gf128mul_4k_lle(be128 *a, struct gf128mul_4k *t) { u8 *ap = (u8 *)a; @@ -473,5 +559,20 @@ void gf128mul_4k_bbe(be128 *a, struct gf128mul_4k *t) } EXPORT_SYMBOL(gf128mul_4k_bbe); +void gf128mul_4k_ble(be128 *a, struct gf128mul_4k *t) +{ + u8 *ap = (u8 *)a; + be128 r[1]; + int i = 15; + + *r = t->t[ap[15]]; + while (i--) { + gf128mul_x8_ble(r); + be128_xor(r, r, &t->t[ap[i]]); + } + *a = *r; +} +EXPORT_SYMBOL(gf128mul_4k_ble); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Functions for multiplying elements of GF(2^128)"); diff --git a/crypto/heh.c b/crypto/heh.c new file mode 100644 index 000000000000..10c00aaf797e --- /dev/null +++ b/crypto/heh.c @@ -0,0 +1,1033 @@ +/* + * HEH: Hash-Encrypt-Hash mode + * + * Copyright (c) 2016 Google Inc. + * + * Authors: + * Alex Cope <alexcope@google.com> + * Eric Biggers <ebiggers@google.com> + */ + +/* + * Hash-Encrypt-Hash (HEH) is a proposed block cipher mode of operation which + * extends the strong pseudo-random permutation (SPRP) property of block ciphers + * (e.g. AES) to arbitrary length input strings. It uses two keyed invertible + * hash functions with a layer of ECB encryption applied in-between. The + * algorithm is specified by the following Internet Draft: + * + * https://tools.ietf.org/html/draft-cope-heh-01 + * + * Although HEH can be used as either a regular symmetric cipher or as an AEAD, + * currently this module only provides it as a symmetric cipher. Additionally, + * only 16-byte nonces are supported. + */ + +#include <crypto/gf128mul.h> +#include <crypto/internal/hash.h> +#include <crypto/internal/skcipher.h> +#include <crypto/scatterwalk.h> +#include <crypto/skcipher.h> +#include "internal.h" + +/* + * The block size is the size of GF(2^128) elements and also the required block + * size of the underlying block cipher. + */ +#define HEH_BLOCK_SIZE 16 + +struct heh_instance_ctx { + struct crypto_shash_spawn cmac; + struct crypto_shash_spawn poly_hash; + struct crypto_skcipher_spawn ecb; +}; + +struct heh_tfm_ctx { + struct crypto_shash *cmac; + struct crypto_shash *poly_hash; /* keyed with tau_key */ + struct crypto_ablkcipher *ecb; +}; + +struct heh_cmac_data { + u8 nonce[HEH_BLOCK_SIZE]; + __le32 nonce_length; + __le32 aad_length; + __le32 message_length; + __le32 padding; +}; + +struct heh_req_ctx { /* aligned to alignmask */ + be128 beta1_key; + be128 beta2_key; + union { + struct { + struct heh_cmac_data data; + struct shash_desc desc; + /* + crypto_shash_descsize(cmac) */ + } cmac; + struct { + struct shash_desc desc; + /* + crypto_shash_descsize(poly_hash) */ + } poly_hash; + struct { + u8 keystream[HEH_BLOCK_SIZE]; + u8 tmp[HEH_BLOCK_SIZE]; + struct scatterlist tmp_sgl[2]; + struct ablkcipher_request req; + /* + crypto_ablkcipher_reqsize(ecb) */ + } ecb; + } u; +}; + +/* + * Get the offset in bytes to the last full block, or equivalently the length of + * all full blocks excluding the last + */ +static inline unsigned int get_tail_offset(unsigned int len) +{ + len -= len % HEH_BLOCK_SIZE; + return len - HEH_BLOCK_SIZE; +} + +static inline struct heh_req_ctx *heh_req_ctx(struct ablkcipher_request *req) +{ + unsigned int alignmask = crypto_ablkcipher_alignmask( + crypto_ablkcipher_reqtfm(req)); + + return (void *)PTR_ALIGN((u8 *)ablkcipher_request_ctx(req), + alignmask + 1); +} + +static inline void async_done(struct crypto_async_request *areq, int err, + int (*next_step)(struct ablkcipher_request *, + u32)) +{ + struct ablkcipher_request *req = areq->data; + + if (err) + goto out; + + err = next_step(req, req->base.flags & ~CRYPTO_TFM_REQ_MAY_SLEEP); + if (err == -EINPROGRESS || + (err == -EBUSY && (req->base.flags & CRYPTO_TFM_REQ_MAY_BACKLOG))) + return; +out: + ablkcipher_request_complete(req, err); +} + +/* + * Generate the per-message "beta" keys used by the hashing layers of HEH. The + * first beta key is the CMAC of the nonce, the additional authenticated data + * (AAD), and the lengths in bytes of the nonce, AAD, and message. The nonce + * and AAD are each zero-padded to the next 16-byte block boundary, and the + * lengths are serialized as 4-byte little endian integers and zero-padded to + * the next 16-byte block boundary. + * The second beta key is the first one interpreted as an element in GF(2^128) + * and multiplied by x. + * + * Note that because the nonce and AAD may, in general, be variable-length, the + * key generation must be done by a pseudo-random function (PRF) on + * variable-length inputs. CBC-MAC does not satisfy this, as it is only a PRF + * on fixed-length inputs. CMAC remedies this flaw. Including the lengths of + * the nonce, AAD, and message is also critical to avoid collisions. + * + * That being said, this implementation does not yet operate as an AEAD and + * therefore there is never any AAD, nor are variable-length nonces supported. + */ +static int generate_betas(struct ablkcipher_request *req, + be128 *beta1_key, be128 *beta2_key) +{ + struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req); + struct heh_tfm_ctx *ctx = crypto_ablkcipher_ctx(tfm); + struct heh_req_ctx *rctx = heh_req_ctx(req); + struct heh_cmac_data *data = &rctx->u.cmac.data; + struct shash_desc *desc = &rctx->u.cmac.desc; + int err; + + BUILD_BUG_ON(sizeof(*data) != 2 * HEH_BLOCK_SIZE); + memcpy(data->nonce, req->info, HEH_BLOCK_SIZE); + data->nonce_length = cpu_to_le32(HEH_BLOCK_SIZE); + data->aad_length = cpu_to_le32(0); + data->message_length = cpu_to_le32(req->nbytes); + data->padding = cpu_to_le32(0); + + desc->tfm = ctx->cmac; + desc->flags = req->base.flags; + + err = crypto_shash_digest(desc, (const u8 *)data, sizeof(*data), + (u8 *)beta1_key); + if (err) + return err; + + gf128mul_x_ble(beta2_key, beta1_key); + return 0; +} + +/*****************************************************************************/ + +/* + * This is the generic version of poly_hash. It does the GF(2^128) + * multiplication by 'tau_key' using a precomputed table, without using any + * special CPU instructions. On some platforms, an accelerated version (with + * higher cra_priority) may be used instead. + */ + +struct poly_hash_tfm_ctx { + struct gf128mul_4k *tau_key; +}; + +struct poly_hash_desc_ctx { + be128 digest; + unsigned int count; +}; + +static int poly_hash_setkey(struct crypto_shash *tfm, + const u8 *key, unsigned int keylen) +{ + struct poly_hash_tfm_ctx *tctx = crypto_shash_ctx(tfm); + be128 key128; + + if (keylen != HEH_BLOCK_SIZE) { + crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + + if (tctx->tau_key) + gf128mul_free_4k(tctx->tau_key); + memcpy(&key128, key, HEH_BLOCK_SIZE); + tctx->tau_key = gf128mul_init_4k_ble(&key128); + if (!tctx->tau_key) + return -ENOMEM; + return 0; +} + +static int poly_hash_init(struct shash_desc *desc) +{ + struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc); + + ctx->digest = (be128) { 0 }; + ctx->count = 0; + return 0; +} + +static int poly_hash_update(struct shash_desc *desc, const u8 *src, + unsigned int len) +{ + struct poly_hash_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm); + struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc); + unsigned int partial = ctx->count % HEH_BLOCK_SIZE; + u8 *dst = (u8 *)&ctx->digest + partial; + + ctx->count += len; + + /* Finishing at least one block? */ + if (partial + len >= HEH_BLOCK_SIZE) { + + if (partial) { + /* Finish the pending block. */ + unsigned int n = HEH_BLOCK_SIZE - partial; + + len -= n; + do { + *dst++ ^= *src++; + } while (--n); + + gf128mul_4k_ble(&ctx->digest, tctx->tau_key); + } + + /* Process zero or more full blocks. */ + while (len >= HEH_BLOCK_SIZE) { + be128 coeff; + + memcpy(&coeff, src, HEH_BLOCK_SIZE); + be128_xor(&ctx->digest, &ctx->digest, &coeff); + src += HEH_BLOCK_SIZE; + len -= HEH_BLOCK_SIZE; + gf128mul_4k_ble(&ctx->digest, tctx->tau_key); + } + dst = (u8 *)&ctx->digest; + } + + /* Continue adding the next block to 'digest'. */ + while (len--) + *dst++ ^= *src++; + return 0; +} + +static int poly_hash_final(struct shash_desc *desc, u8 *out) +{ + struct poly_hash_desc_ctx *ctx = shash_desc_ctx(desc); + + /* Finish the last block if needed. */ + if (ctx->count % HEH_BLOCK_SIZE) { + struct poly_hash_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm); + + gf128mul_4k_ble(&ctx->digest, tctx->tau_key); + } + + memcpy(out, &ctx->digest, HEH_BLOCK_SIZE); + return 0; +} + +static void poly_hash_exit(struct crypto_tfm *tfm) +{ + struct poly_hash_tfm_ctx *tctx = crypto_tfm_ctx(tfm); + + gf128mul_free_4k(tctx->tau_key); +} + +static struct shash_alg poly_hash_alg = { + .digestsize = HEH_BLOCK_SIZE, + .init = poly_hash_init, + .update = poly_hash_update, + .final = poly_hash_final, + .setkey = poly_hash_setkey, + .descsize = sizeof(struct poly_hash_desc_ctx), + .base = { + .cra_name = "poly_hash", + .cra_driver_name = "poly_hash-generic", + .cra_priority = 100, + .cra_ctxsize = sizeof(struct poly_hash_tfm_ctx), + .cra_exit = poly_hash_exit, + .cra_module = THIS_MODULE, + }, +}; + +/*****************************************************************************/ + +/* + * Split the message into 16 byte blocks, padding out the last block, and use + * the blocks as coefficients in the evaluation of a polynomial over GF(2^128) + * at the secret point 'tau_key'. For ease of implementing the higher-level + * heh_hash_inv() function, the constant and degree-1 coefficients are swapped + * if there is a partial block. + * + * Mathematically, compute: + * if (no partial block) + * k^{N-1} * m_0 + ... + k * m_{N-2} + m_{N-1} + * else if (partial block) + * k^N * m_0 + ... + k^2 * m_{N-2} + k * m_N + m_{N-1} + * + * where: + * t is tau_key + * N is the number of full blocks in the message + * m_i is the i-th full block in the message for i = 0 to N-1 inclusive + * m_N is the partial block of the message zero-padded up to 16 bytes + * + * Note that most of this is now separated out into its own keyed hash + * algorithm, to allow optimized implementations. However, we still handle the + * swapping of the last two coefficients here in the HEH template because this + * simplifies the poly_hash algorithms: they don't have to buffer an extra + * block, don't have to duplicate as much code, and are more similar to GHASH. + */ +static int poly_hash(struct ablkcipher_request *req, struct scatterlist *sgl, + be128 *hash) +{ + struct heh_req_ctx *rctx = heh_req_ctx(req); + struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req); + struct heh_tfm_ctx *ctx = crypto_ablkcipher_ctx(tfm); + struct shash_desc *desc = &rctx->u.poly_hash.desc; + unsigned int tail_offset = get_tail_offset(req->nbytes); + unsigned int tail_len = req->nbytes - tail_offset; + be128 tail[2]; + unsigned int i, n; + struct sg_mapping_iter miter; + int err; + + desc->tfm = ctx->poly_hash; + desc->flags = req->base.flags; + + /* Handle all full blocks except the last */ + err = crypto_shash_init(desc); + sg_miter_start(&miter, sgl, sg_nents(sgl), + SG_MITER_FROM_SG | SG_MITER_ATOMIC); + for (i = 0; i < tail_offset && !err; i += n) { + sg_miter_next(&miter); + n = min_t(unsigned int, miter.length, tail_offset - i); + err = crypto_shash_update(desc, miter.addr, n); + } + sg_miter_stop(&miter); + if (err) + return err; + + /* Handle the last full block and the partial block */ + scatterwalk_map_and_copy(tail, sgl, tail_offset, tail_len, 0); + + if (tail_len != HEH_BLOCK_SIZE) { + /* handle the partial block */ + memset((u8 *)tail + tail_len, 0, sizeof(tail) - tail_len); + err = crypto_shash_update(desc, (u8 *)&tail[1], HEH_BLOCK_SIZE); + if (err) + return err; + } + err = crypto_shash_final(desc, (u8 *)hash); + if (err) + return err; + be128_xor(hash, hash, &tail[0]); + return 0; +} + +/* + * Transform all full blocks except the last. + * This is used by both the hash and inverse hash phases. + */ +static int heh_tfm_blocks(struct ablkcipher_request *req, + struct scatterlist *src_sgl, + struct scatterlist *dst_sgl, unsigned int len, + const be128 *hash, const be128 *beta_key) +{ + struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req); + struct blkcipher_desc desc = { .flags = req->base.flags }; + struct blkcipher_walk walk; + be128 e = *beta_key; + int err; + unsigned int nbytes; + + blkcipher_walk_init(&walk, dst_sgl, src_sgl, len); + + err = blkcipher_ablkcipher_walk_virt(&desc, &walk, tfm); + + while ((nbytes = walk.nbytes)) { + const be128 *src = (be128 *)walk.src.virt.addr; + be128 *dst = (be128 *)walk.dst.virt.addr; + + do { + gf128mul_x_ble(&e, &e); + be128_xor(dst, src, hash); + be128_xor(dst, dst, &e); + src++; + dst++; + } while ((nbytes -= HEH_BLOCK_SIZE) >= HEH_BLOCK_SIZE); + err = blkcipher_walk_done(&desc, &walk, nbytes); + } + return err; +} + +/* + * The hash phase of HEH. Given a message, compute: + * + * (m_0 + H, ..., m_{N-2} + H, H, m_N) + (xb, x^2b, ..., x^{N-1}b, b, 0) + * + * where: + * N is the number of full blocks in the message + * m_i is the i-th full block in the message for i = 0 to N-1 inclusive + * m_N is the unpadded partial block, possibly empty + * H is the poly_hash() of the message, keyed by tau_key + * b is beta_key + * x is the element x in our representation of GF(2^128) + * + * Note that the partial block remains unchanged, but it does affect the result + * of poly_hash() and therefore the transformation of all the full blocks. + */ +static int heh_hash(struct ablkcipher_request *req, const be128 *beta_key) +{ + be128 hash; + unsigned int tail_offset = get_tail_offset(req->nbytes); + unsigned int partial_len = req->nbytes % HEH_BLOCK_SIZE; + int err; + + /* poly_hash() the full message including the partial block */ + err = poly_hash(req, req->src, &hash); + if (err) + return err; + + /* Transform all full blocks except the last */ + err = heh_tfm_blocks(req, req->src, req->dst, tail_offset, &hash, + beta_key); + if (err) + return err; + + /* Set the last full block to hash XOR beta_key */ + be128_xor(&hash, &hash, beta_key); + scatterwalk_map_and_copy(&hash, req->dst, tail_offset, HEH_BLOCK_SIZE, + 1); + + /* Copy the partial block if needed */ + if (partial_len != 0 && req->src != req->dst) { + unsigned int offs = tail_offset + HEH_BLOCK_SIZE; + + scatterwalk_map_and_copy(&hash, req->src, offs, partial_len, 0); + scatterwalk_map_and_copy(&hash, req->dst, offs, partial_len, 1); + } + return 0; +} + +/* + * The inverse hash phase of HEH. This undoes the result of heh_hash(). + */ +static int heh_hash_inv(struct ablkcipher_request *req, const be128 *beta_key) +{ + be128 hash; + be128 tmp; + struct scatterlist tmp_sgl[2]; + struct scatterlist *tail_sgl; + unsigned int tail_offset = get_tail_offset(req->nbytes); + struct scatterlist *sgl = req->dst; + int err; + + /* + * The last full block was computed as hash XOR beta_key, so XOR it with + * beta_key to recover hash. + */ + tail_sgl = scatterwalk_ffwd(tmp_sgl, sgl, tail_offset); + scatterwalk_map_and_copy(&hash, tail_sgl, 0, HEH_BLOCK_SIZE, 0); + be128_xor(&hash, &hash, beta_key); + + /* Transform all full blocks except the last */ + err = heh_tfm_blocks(req, sgl, sgl, tail_offset, &hash, beta_key); + if (err) + return err; + + /* + * Recover the last full block. We know 'hash', i.e. the poly_hash() of + * the the original message. The last full block was the constant term + * of the polynomial. To recover the last full block, temporarily zero + * it, compute the poly_hash(), and take the difference from 'hash'. + */ + memset(&tmp, 0, sizeof(tmp)); + scatterwalk_map_and_copy(&tmp, tail_sgl, 0, HEH_BLOCK_SIZE, 1); + err = poly_hash(req, sgl, &tmp); + if (err) + return err; + be128_xor(&tmp, &tmp, &hash); + scatterwalk_map_and_copy(&tmp, tail_sgl, 0, HEH_BLOCK_SIZE, 1); + return 0; +} + +static int heh_hash_inv_step(struct ablkcipher_request *req, u32 flags) +{ + struct heh_req_ctx *rctx = heh_req_ctx(req); + + return heh_hash_inv(req, &rctx->beta2_key); +} + +static int heh_ecb_step_3(struct ablkcipher_request *req, u32 flags) +{ + struct heh_req_ctx *rctx = heh_req_ctx(req); + u8 partial_block[HEH_BLOCK_SIZE] __aligned(__alignof__(u32)); + unsigned int tail_offset = get_tail_offset(req->nbytes); + unsigned int partial_offset = tail_offset + HEH_BLOCK_SIZE; + unsigned int partial_len = req->nbytes - partial_offset; + + /* + * Extract the pad in req->dst at tail_offset, and xor the partial block + * with it to create encrypted partial block + */ + scatterwalk_map_and_copy(rctx->u.ecb.keystream, req->dst, tail_offset, + HEH_BLOCK_SIZE, 0); + scatterwalk_map_and_copy(partial_block, req->dst, partial_offset, + partial_len, 0); + crypto_xor(partial_block, rctx->u.ecb.keystream, partial_len); + + /* + * Store the encrypted final block and partial block back in dst_sg + */ + scatterwalk_map_and_copy(&rctx->u.ecb.tmp, req->dst, tail_offset, + HEH_BLOCK_SIZE, 1); + scatterwalk_map_and_copy(partial_block, req->dst, partial_offset, + partial_len, 1); + + return heh_hash_inv_step(req, flags); +} + +static void heh_ecb_step_2_done(struct crypto_async_request *areq, int err) +{ + return async_done(areq, err, heh_ecb_step_3); +} + +static int heh_ecb_step_2(struct ablkcipher_request *req, u32 flags) +{ + struct heh_req_ctx *rctx = heh_req_ctx(req); + unsigned int partial_len = req->nbytes % HEH_BLOCK_SIZE; + struct scatterlist *tmp_sgl; + int err; + unsigned int tail_offset = get_tail_offset(req->nbytes); + + if (partial_len == 0) + return heh_hash_inv_step(req, flags); + + /* + * Extract the final full block, store it in tmp, and then xor that with + * the value saved in u.ecb.keystream + */ + scatterwalk_map_and_copy(rctx->u.ecb.tmp, req->dst, tail_offset, + HEH_BLOCK_SIZE, 0); + crypto_xor(rctx->u.ecb.keystream, rctx->u.ecb.tmp, HEH_BLOCK_SIZE); + + /* + * Encrypt the value in rctx->u.ecb.keystream to create the pad for the + * partial block. + * We cannot encrypt stack buffers, so re-use the dst_sg to do this + * encryption to avoid a malloc. The value at tail_offset is stored in + * tmp, and will be restored later. + */ + scatterwalk_map_and_copy(rctx->u.ecb.keystream, req->dst, tail_offset, + HEH_BLOCK_SIZE, 1); + tmp_sgl = scatterwalk_ffwd(rctx->u.ecb.tmp_sgl, req->dst, tail_offset); + ablkcipher_request_set_callback(&rctx->u.ecb.req, flags, + heh_ecb_step_2_done, req); + ablkcipher_request_set_crypt(&rctx->u.ecb.req, tmp_sgl, tmp_sgl, + HEH_BLOCK_SIZE, NULL); + err = crypto_ablkcipher_encrypt(&rctx->u.ecb.req); + if (err) + return err; + return heh_ecb_step_3(req, flags); +} + +static void heh_ecb_full_done(struct crypto_async_request *areq, int err) +{ + return async_done(areq, err, heh_ecb_step_2); +} + +/* + * The encrypt phase of HEH. This uses ECB encryption, with special handling + * for the partial block at the end if any. The source data is already in + * req->dst, so the encryption happens in-place. + * + * After the encrypt phase we continue on to the inverse hash phase. The + * functions calls are chained to support asynchronous ECB algorithms. + */ +static int heh_ecb(struct ablkcipher_request *req, bool decrypt) +{ + struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req); + struct heh_tfm_ctx *ctx = crypto_ablkcipher_ctx(tfm); + struct heh_req_ctx *rctx = heh_req_ctx(req); + struct ablkcipher_request *ecb_req = &rctx->u.ecb.req; + unsigned int tail_offset = get_tail_offset(req->nbytes); + unsigned int full_len = tail_offset + HEH_BLOCK_SIZE; + int err; + + /* + * Save the last full block before it is encrypted/decrypted. This will + * be used later to encrypt/decrypt the partial block + */ + scatterwalk_map_and_copy(rctx->u.ecb.keystream, req->dst, tail_offset, + HEH_BLOCK_SIZE, 0); + + /* Encrypt/decrypt all full blocks */ + ablkcipher_request_set_tfm(ecb_req, ctx->ecb); + ablkcipher_request_set_callback(ecb_req, req->base.flags, + heh_ecb_full_done, req); + ablkcipher_request_set_crypt(ecb_req, req->dst, req->dst, full_len, + NULL); + if (decrypt) + err = crypto_ablkcipher_decrypt(ecb_req); + else + err = crypto_ablkcipher_encrypt(ecb_req); + if (err) + return err; + + return heh_ecb_step_2(req, req->base.flags); +} + +static int heh_crypt(struct ablkcipher_request *req, bool decrypt) +{ + struct heh_req_ctx *rctx = heh_req_ctx(req); + int err; + + /* Inputs must be at least one full block */ + if (req->nbytes < HEH_BLOCK_SIZE) + return -EINVAL; + + err = generate_betas(req, &rctx->beta1_key, &rctx->beta2_key); + if (err) + return err; + + if (decrypt) + swap(rctx->beta1_key, rctx->beta2_key); + + err = heh_hash(req, &rctx->beta1_key); + if (err) + return err; + + return heh_ecb(req, decrypt); +} + +static int heh_encrypt(struct ablkcipher_request *req) +{ + return heh_crypt(req, false); +} + +static int heh_decrypt(struct ablkcipher_request *req) +{ + return heh_crypt(req, true); +} + +static int heh_setkey(struct crypto_ablkcipher *parent, const u8 *key, + unsigned int keylen) +{ + struct heh_tfm_ctx *ctx = crypto_ablkcipher_ctx(parent); + struct crypto_shash *cmac = ctx->cmac; + struct crypto_ablkcipher *ecb = ctx->ecb; + SHASH_DESC_ON_STACK(desc, cmac); + u8 *derived_keys; + u8 digest[HEH_BLOCK_SIZE]; + unsigned int i; + int err; + + /* set prf_key = key */ + crypto_shash_clear_flags(cmac, CRYPTO_TFM_REQ_MASK); + crypto_shash_set_flags(cmac, crypto_ablkcipher_get_flags(parent) & + CRYPTO_TFM_REQ_MASK); + err = crypto_shash_setkey(cmac, key, keylen); + crypto_ablkcipher_set_flags(parent, crypto_shash_get_flags(cmac) & + CRYPTO_TFM_RES_MASK); + if (err) + return err; + + /* + * Generate tau_key and ecb_key as follows: + * tau_key = cmac(prf_key, 0x00...01) + * ecb_key = cmac(prf_key, 0x00...02) || cmac(prf_key, 0x00...03) || ... + * truncated to keylen bytes + */ + derived_keys = kzalloc(round_up(HEH_BLOCK_SIZE + keylen, + HEH_BLOCK_SIZE), GFP_KERNEL); + if (!derived_keys) + return -ENOMEM; + desc->tfm = cmac; + desc->flags = (crypto_shash_get_flags(cmac) & CRYPTO_TFM_REQ_MASK); + for (i = 0; i < keylen + HEH_BLOCK_SIZE; i += HEH_BLOCK_SIZE) { + derived_keys[i + HEH_BLOCK_SIZE - 1] = + 0x01 + i / HEH_BLOCK_SIZE; + err = crypto_shash_digest(desc, derived_keys + i, + HEH_BLOCK_SIZE, digest); + if (err) + goto out; + memcpy(derived_keys + i, digest, HEH_BLOCK_SIZE); + } + + err = crypto_shash_setkey(ctx->poly_hash, derived_keys, HEH_BLOCK_SIZE); + if (err) + goto out; + + crypto_ablkcipher_clear_flags(ecb, CRYPTO_TFM_REQ_MASK); + crypto_ablkcipher_set_flags(ecb, crypto_ablkcipher_get_flags(parent) & + CRYPTO_TFM_REQ_MASK); + err = crypto_ablkcipher_setkey(ecb, derived_keys + HEH_BLOCK_SIZE, + keylen); + crypto_ablkcipher_set_flags(parent, crypto_ablkcipher_get_flags(ecb) & + CRYPTO_TFM_RES_MASK); +out: + kzfree(derived_keys); + return err; +} + +static int heh_init_tfm(struct crypto_tfm *tfm) +{ + struct crypto_instance *inst = crypto_tfm_alg_instance(tfm); + struct heh_instance_ctx *ictx = crypto_instance_ctx(inst); + struct heh_tfm_ctx *ctx = crypto_tfm_ctx(tfm); + struct crypto_shash *cmac; + struct crypto_shash *poly_hash; + struct crypto_ablkcipher *ecb; + unsigned int reqsize; + int err; + + cmac = crypto_spawn_shash(&ictx->cmac); + if (IS_ERR(cmac)) + return PTR_ERR(cmac); + + poly_hash = crypto_spawn_shash(&ictx->poly_hash); + err = PTR_ERR(poly_hash); + if (IS_ERR(poly_hash)) + goto err_free_cmac; + + ecb = crypto_spawn_skcipher(&ictx->ecb); + err = PTR_ERR(ecb); + if (IS_ERR(ecb)) + goto err_free_poly_hash; + + ctx->cmac = cmac; + ctx->poly_hash = poly_hash; + ctx->ecb = ecb; + + reqsize = crypto_tfm_alg_alignmask(tfm) & + ~(crypto_tfm_ctx_alignment() - 1); + reqsize += max3(offsetof(struct heh_req_ctx, u.cmac.desc) + + sizeof(struct shash_desc) + + crypto_shash_descsize(cmac), + offsetof(struct heh_req_ctx, u.poly_hash.desc) + + sizeof(struct shash_desc) + + crypto_shash_descsize(poly_hash), + offsetof(struct heh_req_ctx, u.ecb.req) + + sizeof(struct ablkcipher_request) + + crypto_ablkcipher_reqsize(ecb)); + tfm->crt_ablkcipher.reqsize = reqsize; + + return 0; + +err_free_poly_hash: + crypto_free_shash(poly_hash); +err_free_cmac: + crypto_free_shash(cmac); + return err; +} + +static void heh_exit_tfm(struct crypto_tfm *tfm) +{ + struct heh_tfm_ctx *ctx = crypto_tfm_ctx(tfm); + + crypto_free_shash(ctx->cmac); + crypto_free_shash(ctx->poly_hash); + crypto_free_ablkcipher(ctx->ecb); +} + +static void heh_free_instance(struct crypto_instance *inst) +{ + struct heh_instance_ctx *ctx = crypto_instance_ctx(inst); + + crypto_drop_shash(&ctx->cmac); + crypto_drop_shash(&ctx->poly_hash); + crypto_drop_skcipher(&ctx->ecb); + kfree(inst); +} + +/* + * Create an instance of HEH as a ablkcipher. + * + * This relies on underlying CMAC and ECB algorithms, usually cmac(aes) and + * ecb(aes). For performance reasons we support asynchronous ECB algorithms. + * However, we do not yet support asynchronous CMAC algorithms because CMAC is + * only used on a small fixed amount of data per request, independent of the + * request length. This would change if AEAD or variable-length nonce support + * were to be exposed. + */ +static int heh_create_common(struct crypto_template *tmpl, struct rtattr **tb, + const char *full_name, const char *cmac_name, + const char *poly_hash_name, const char *ecb_name) +{ + struct crypto_attr_type *algt; + struct crypto_instance *inst; + struct heh_instance_ctx *ctx; + struct shash_alg *cmac; + struct shash_alg *poly_hash; + struct crypto_alg *ecb; + int err; + + algt = crypto_get_attr_type(tb); + if (IS_ERR(algt)) + return PTR_ERR(algt); + + /* User must be asking for something compatible with ablkcipher */ + if ((algt->type ^ CRYPTO_ALG_TYPE_ABLKCIPHER) & algt->mask) + return -EINVAL; + + /* Allocate the ablkcipher instance */ + inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + ctx = crypto_instance_ctx(inst); + + /* Set up the cmac spawn */ + ctx->cmac.base.inst = inst; + err = crypto_grab_shash(&ctx->cmac, cmac_name, 0, 0); + if (err) + goto err_free_inst; + cmac = crypto_spawn_shash_alg(&ctx->cmac); + err = -EINVAL; + if (cmac->digestsize != HEH_BLOCK_SIZE) + goto err_drop_cmac; + + /* Set up the poly_hash spawn */ + ctx->poly_hash.base.inst = inst; + err = crypto_grab_shash(&ctx->poly_hash, poly_hash_name, 0, 0); + if (err) + goto err_drop_cmac; + poly_hash = crypto_spawn_shash_alg(&ctx->poly_hash); + err = -EINVAL; + if (poly_hash->digestsize != HEH_BLOCK_SIZE) + goto err_drop_poly_hash; + + /* Set up the ecb spawn */ + ctx->ecb.base.inst = inst; + err = crypto_grab_skcipher(&ctx->ecb, ecb_name, 0, + crypto_requires_sync(algt->type, + algt->mask)); + if (err) + goto err_drop_poly_hash; + ecb = crypto_skcipher_spawn_alg(&ctx->ecb); + + /* HEH only supports block ciphers with 16 byte block size */ + err = -EINVAL; + if (ecb->cra_blocksize != HEH_BLOCK_SIZE) + goto err_drop_ecb; + + /* The underlying "ECB" algorithm must not require an IV */ + err = -EINVAL; + if ((ecb->cra_flags & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_BLKCIPHER) { + if (ecb->cra_blkcipher.ivsize != 0) + goto err_drop_ecb; + } else { + if (ecb->cra_ablkcipher.ivsize != 0) + goto err_drop_ecb; + } + + /* Set the instance names */ + err = -ENAMETOOLONG; + if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME, + "heh_base(%s,%s,%s)", cmac->base.cra_driver_name, + poly_hash->base.cra_driver_name, + ecb->cra_driver_name) >= CRYPTO_MAX_ALG_NAME) + goto err_drop_ecb; + + err = -ENAMETOOLONG; + if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME, + "%s", full_name) >= CRYPTO_MAX_ALG_NAME) + goto err_drop_ecb; + + /* Finish initializing the instance */ + + inst->alg.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | + (ecb->cra_flags & CRYPTO_ALG_ASYNC); + inst->alg.cra_blocksize = HEH_BLOCK_SIZE; + inst->alg.cra_ctxsize = sizeof(struct heh_tfm_ctx); + inst->alg.cra_alignmask = ecb->cra_alignmask | (__alignof__(be128) - 1); + inst->alg.cra_priority = ecb->cra_priority; + inst->alg.cra_type = &crypto_ablkcipher_type; + inst->alg.cra_init = heh_init_tfm; + inst->alg.cra_exit = heh_exit_tfm; + + inst->alg.cra_ablkcipher.setkey = heh_setkey; + inst->alg.cra_ablkcipher.encrypt = heh_encrypt; + inst->alg.cra_ablkcipher.decrypt = heh_decrypt; + if ((ecb->cra_flags & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_BLKCIPHER) { + inst->alg.cra_ablkcipher.min_keysize = ecb->cra_blkcipher.min_keysize; + inst->alg.cra_ablkcipher.max_keysize = ecb->cra_blkcipher.max_keysize; + } else { + inst->alg.cra_ablkcipher.min_keysize = ecb->cra_ablkcipher.min_keysize; + inst->alg.cra_ablkcipher.max_keysize = ecb->cra_ablkcipher.max_keysize; + } + inst->alg.cra_ablkcipher.ivsize = HEH_BLOCK_SIZE; + + /* Register the instance */ + err = crypto_register_instance(tmpl, inst); + if (err) + goto err_drop_ecb; + return 0; + +err_drop_ecb: + crypto_drop_skcipher(&ctx->ecb); +err_drop_poly_hash: + crypto_drop_shash(&ctx->poly_hash); +err_drop_cmac: + crypto_drop_shash(&ctx->cmac); +err_free_inst: + kfree(inst); + return err; +} + +static int heh_create(struct crypto_template *tmpl, struct rtattr **tb) +{ + const char *cipher_name; + char full_name[CRYPTO_MAX_ALG_NAME]; + char cmac_name[CRYPTO_MAX_ALG_NAME]; + char ecb_name[CRYPTO_MAX_ALG_NAME]; + + /* Get the name of the requested block cipher (e.g. aes) */ + cipher_name = crypto_attr_alg_name(tb[1]); + if (IS_ERR(cipher_name)) + return PTR_ERR(cipher_name); + + if (snprintf(full_name, CRYPTO_MAX_ALG_NAME, "heh(%s)", cipher_name) >= + CRYPTO_MAX_ALG_NAME) + return -ENAMETOOLONG; + + if (snprintf(cmac_name, CRYPTO_MAX_ALG_NAME, "cmac(%s)", cipher_name) >= + CRYPTO_MAX_ALG_NAME) + return -ENAMETOOLONG; + + if (snprintf(ecb_name, CRYPTO_MAX_ALG_NAME, "ecb(%s)", cipher_name) >= + CRYPTO_MAX_ALG_NAME) + return -ENAMETOOLONG; + + return heh_create_common(tmpl, tb, full_name, cmac_name, "poly_hash", + ecb_name); +} + +static struct crypto_template heh_tmpl = { + .name = "heh", + .create = heh_create, + .free = heh_free_instance, + .module = THIS_MODULE, +}; + +static int heh_base_create(struct crypto_template *tmpl, struct rtattr **tb) +{ + char full_name[CRYPTO_MAX_ALG_NAME]; + const char *cmac_name; + const char *poly_hash_name; + const char *ecb_name; + + cmac_name = crypto_attr_alg_name(tb[1]); + if (IS_ERR(cmac_name)) + return PTR_ERR(cmac_name); + + poly_hash_name = crypto_attr_alg_name(tb[2]); + if (IS_ERR(poly_hash_name)) + return PTR_ERR(poly_hash_name); + + ecb_name = crypto_attr_alg_name(tb[3]); + if (IS_ERR(ecb_name)) + return PTR_ERR(ecb_name); + + if (snprintf(full_name, CRYPTO_MAX_ALG_NAME, "heh_base(%s,%s,%s)", + cmac_name, poly_hash_name, ecb_name) >= + CRYPTO_MAX_ALG_NAME) + return -ENAMETOOLONG; + + return heh_create_common(tmpl, tb, full_name, cmac_name, poly_hash_name, + ecb_name); +} + +/* + * If HEH is instantiated as "heh_base" instead of "heh", then specific + * implementations of cmac, poly_hash, and ecb can be specified instead of just + * the cipher. + */ +static struct crypto_template heh_base_tmpl = { + .name = "heh_base", + .create = heh_base_create, + .free = heh_free_instance, + .module = THIS_MODULE, +}; + +static int __init heh_module_init(void) +{ + int err; + + err = crypto_register_template(&heh_tmpl); + if (err) + return err; + + err = crypto_register_template(&heh_base_tmpl); + if (err) + goto out_undo_heh; + + err = crypto_register_shash(&poly_hash_alg); + if (err) + goto out_undo_heh_base; + + return 0; + +out_undo_heh_base: + crypto_unregister_template(&heh_base_tmpl); +out_undo_heh: + crypto_unregister_template(&heh_tmpl); + return err; +} + +static void __exit heh_module_exit(void) +{ + crypto_unregister_template(&heh_tmpl); + crypto_unregister_template(&heh_base_tmpl); + crypto_unregister_shash(&poly_hash_alg); +} + +module_init(heh_module_init); +module_exit(heh_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Hash-Encrypt-Hash block cipher mode"); +MODULE_ALIAS_CRYPTO("heh"); +MODULE_ALIAS_CRYPTO("heh_base"); diff --git a/crypto/mcryptd.c b/crypto/mcryptd.c index a0ceb41d5ccc..b4f3930266b1 100644 --- a/crypto/mcryptd.c +++ b/crypto/mcryptd.c @@ -531,6 +531,7 @@ static int mcryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb, inst->alg.halg.base.cra_flags = type; inst->alg.halg.digestsize = salg->digestsize; + inst->alg.halg.statesize = salg->statesize; inst->alg.halg.base.cra_ctxsize = sizeof(struct mcryptd_hash_ctx); inst->alg.halg.base.cra_init = mcryptd_hash_init_tfm; diff --git a/crypto/shash.c b/crypto/shash.c index 359754591653..9ae1e891308d 100644 --- a/crypto/shash.c +++ b/crypto/shash.c @@ -683,6 +683,14 @@ void shash_free_instance(struct crypto_instance *inst) } EXPORT_SYMBOL_GPL(shash_free_instance); +int crypto_grab_shash(struct crypto_shash_spawn *spawn, + const char *name, u32 type, u32 mask) +{ + spawn->base.frontend = &crypto_shash_type; + return crypto_grab_spawn(&spawn->base, name, type, mask); +} +EXPORT_SYMBOL_GPL(crypto_grab_shash); + int crypto_init_shash_spawn(struct crypto_shash_spawn *spawn, struct shash_alg *alg, struct crypto_instance *inst) diff --git a/crypto/testmgr.c b/crypto/testmgr.c index d4944318ca1f..8374ca8b6579 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -3214,6 +3214,21 @@ static const struct alg_test_desc alg_test_descs[] = { } } }, { + .alg = "heh(aes)", + .test = alg_test_skcipher, + .suite = { + .cipher = { + .enc = { + .vecs = aes_heh_enc_tv_template, + .count = AES_HEH_ENC_TEST_VECTORS + }, + .dec = { + .vecs = aes_heh_dec_tv_template, + .count = AES_HEH_DEC_TEST_VECTORS + } + } + } + }, { .alg = "hmac(crc32)", .test = alg_test_hash, .suite = { diff --git a/crypto/testmgr.h b/crypto/testmgr.h index 0e02c60a57b6..ba6530d8ba58 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -14139,6 +14139,8 @@ static struct cipher_testvec cast6_xts_dec_tv_template[] = { #define AES_DEC_TEST_VECTORS 4 #define AES_CBC_ENC_TEST_VECTORS 5 #define AES_CBC_DEC_TEST_VECTORS 5 +#define AES_HEH_ENC_TEST_VECTORS 4 +#define AES_HEH_DEC_TEST_VECTORS 4 #define HMAC_MD5_ECB_CIPHER_NULL_ENC_TEST_VECTORS 2 #define HMAC_MD5_ECB_CIPHER_NULL_DEC_TEST_VECTORS 2 #define HMAC_SHA1_ECB_CIPHER_NULL_ENC_TEST_VEC 2 @@ -14511,6 +14513,198 @@ static struct cipher_testvec aes_dec_tv_template[] = { }, }; +static struct cipher_testvec aes_heh_enc_tv_template[] = { + { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + .klen = 16, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + .ilen = 16, + .result = "\xd8\xbd\x40\xbf\xca\xe5\xee\x81" + "\x0f\x3d\x1f\x1f\xae\x89\x07\x55", + .rlen = 16, + .also_non_np = 1, + .np = 2, + .tap = { 8, 8 }, + }, { + .key = "\xa8\xda\x24\x9b\x5e\xfa\x13\xc2" + "\xc1\x94\xbf\x32\xba\x38\xa3\x77", + .klen = 16, + .iv = "\x4d\x47\x61\x37\x2b\x47\x86\xf0" + "\xd6\x47\xb5\xc2\xe8\xcf\x85\x27", + .input = "\xb8\xee\x29\xe4\xa5\xd1\xe7\x55" + "\xd0\xfd\xe7\x22\x63\x76\x36\xe2" + "\xf8\x0c\xf8\xfe\x65\x76\xe7\xca" + "\xc1\x42\xf5\xca\x5a\xa8\xac\x2a", + .ilen = 32, + .result = "\x59\xf2\x78\x4e\x10\x94\xf9\x5c" + "\x22\x23\x78\x2a\x30\x48\x11\x97" + "\xb1\xfe\x70\xc4\xef\xdf\x04\xef" + "\x16\x39\x04\xcf\xc0\x95\x9a\x98", + .rlen = 32, + .also_non_np = 1, + .np = 3, + .tap = { 16, 13, 3 }, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + .klen = 16, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00", + .ilen = 63, + .result = "\xe0\x40\xeb\xe9\x52\xbe\x65\x60" + "\xe4\x68\x68\xa3\x73\x75\xb8\x52" + "\xef\x38\x6a\x87\x25\x25\xf6\x04" + "\xe5\x8e\xbe\x14\x8b\x02\x14\x1f" + "\xa9\x73\xb7\xad\x15\xbe\x9c\xa0" + "\xd2\x8a\x2c\xdc\xd4\xe3\x05\x55" + "\x0a\xf5\xf8\x51\xee\xe5\x62\xa5" + "\x71\xa7\x7c\x15\x5d\x7a\x9e", + .rlen = 63, + .also_non_np = 1, + .np = 8, + .tap = { 20, 20, 10, 8, 2, 1, 1, 1 }, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + .klen = 16, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x01" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00", + .ilen = 63, + .result = "\x4b\x1a\x15\xa0\xaf\x08\x6d\x70" + "\xf0\xa7\x97\xb5\x31\x4b\x8c\xc3" + "\x4d\xf2\x7a\x9d\xdd\xd4\x15\x99" + "\x57\xad\xc6\xb1\x35\x69\xf5\x6a" + "\x2d\x70\xe4\x97\x49\xb2\x9f\x71" + "\xde\x22\xb5\x70\x8c\x69\x24\xd3" + "\xad\x80\x58\x48\x90\xe4\xed\xba" + "\x76\x3d\x71\x7c\x57\x25\x87", + .rlen = 63, + .also_non_np = 1, + .np = 8, + .tap = { 20, 20, 10, 8, 2, 1, 1, 1 }, + } +}; + +static struct cipher_testvec aes_heh_dec_tv_template[] = { + { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + .klen = 16, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\xd8\xbd\x40\xbf\xca\xe5\xee\x81" + "\x0f\x3d\x1f\x1f\xae\x89\x07\x55", + .ilen = 16, + .result = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + .rlen = 16, + .also_non_np = 1, + .np = 2, + .tap = { 8, 8 }, + }, { + .key = "\xa8\xda\x24\x9b\x5e\xfa\x13\xc2" + "\xc1\x94\xbf\x32\xba\x38\xa3\x77", + .klen = 16, + .iv = "\x4d\x47\x61\x37\x2b\x47\x86\xf0" + "\xd6\x47\xb5\xc2\xe8\xcf\x85\x27", + .input = "\x59\xf2\x78\x4e\x10\x94\xf9\x5c" + "\x22\x23\x78\x2a\x30\x48\x11\x97" + "\xb1\xfe\x70\xc4\xef\xdf\x04\xef" + "\x16\x39\x04\xcf\xc0\x95\x9a\x98", + .ilen = 32, + .result = "\xb8\xee\x29\xe4\xa5\xd1\xe7\x55" + "\xd0\xfd\xe7\x22\x63\x76\x36\xe2" + "\xf8\x0c\xf8\xfe\x65\x76\xe7\xca" + "\xc1\x42\xf5\xca\x5a\xa8\xac\x2a", + .rlen = 32, + .also_non_np = 1, + .np = 3, + .tap = { 16, 13, 3 }, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + .klen = 16, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\xe0\x40\xeb\xe9\x52\xbe\x65\x60" + "\xe4\x68\x68\xa3\x73\x75\xb8\x52" + "\xef\x38\x6a\x87\x25\x25\xf6\x04" + "\xe5\x8e\xbe\x14\x8b\x02\x14\x1f" + "\xa9\x73\xb7\xad\x15\xbe\x9c\xa0" + "\xd2\x8a\x2c\xdc\xd4\xe3\x05\x55" + "\x0a\xf5\xf8\x51\xee\xe5\x62\xa5" + "\x71\xa7\x7c\x15\x5d\x7a\x9e", + .ilen = 63, + .result = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00", + .rlen = 63, + .also_non_np = 1, + .np = 8, + .tap = { 20, 20, 10, 8, 2, 1, 1, 1 }, + }, { + .key = "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + .klen = 16, + .iv = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .input = "\x4b\x1a\x15\xa0\xaf\x08\x6d\x70" + "\xf0\xa7\x97\xb5\x31\x4b\x8c\xc3" + "\x4d\xf2\x7a\x9d\xdd\xd4\x15\x99" + "\x57\xad\xc6\xb1\x35\x69\xf5\x6a" + "\x2d\x70\xe4\x97\x49\xb2\x9f\x71" + "\xde\x22\xb5\x70\x8c\x69\x24\xd3" + "\xad\x80\x58\x48\x90\xe4\xed\xba" + "\x76\x3d\x71\x7c\x57\x25\x87", + .ilen = 63, + .result = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x01" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00", + .rlen = 63, + .also_non_np = 1, + .np = 8, + .tap = { 20, 20, 10, 8, 2, 1, 1, 1 }, + } +}; + static struct cipher_testvec aes_cbc_enc_tv_template[] = { { /* From RFC 3602 */ .key = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b" diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index 5fdac394207a..549cdbed7b0e 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -1211,6 +1211,9 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video) union acpi_object *dod = NULL; union acpi_object *obj; + if (!video->cap._DOD) + return AE_NOT_EXIST; + status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer); if (!ACPI_SUCCESS(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD")); diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index 96809cd99ace..2f24b578bcaf 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -346,6 +346,34 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9343"), }, }, + { + .callback = dmi_enable_rev_override, + .ident = "DELL Precision 5520", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Precision 5520"), + }, + }, + { + .callback = dmi_enable_rev_override, + .ident = "DELL Precision 3520", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Precision 3520"), + }, + }, + /* + * Resolves a quirk with the Dell Latitude 3350 that + * causes the ethernet adapter to not function. + */ + { + .callback = dmi_enable_rev_override, + .ident = "DELL Latitude 3350", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Latitude 3350"), + }, + }, #endif {} }; diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig index a82fc022d34b..4d4cdc1a6e25 100644 --- a/drivers/android/Kconfig +++ b/drivers/android/Kconfig @@ -22,7 +22,7 @@ config ANDROID_BINDER_IPC config ANDROID_BINDER_DEVICES string "Android Binder devices" depends on ANDROID_BINDER_IPC - default "binder" + default "binder,hwbinder,vndbinder" ---help--- Default value for the binder.devices parameter. diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 37b9eecf5c71..d1490be45c67 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -18,6 +18,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <asm/cacheflush.h> +#include <linux/atomic.h> #include <linux/fdtable.h> #include <linux/file.h> #include <linux/freezer.h> @@ -46,19 +47,11 @@ #include <uapi/linux/android/binder.h> #include "binder_trace.h" -static DEFINE_MUTEX(binder_main_lock); -static DEFINE_MUTEX(binder_deferred_lock); -static DEFINE_MUTEX(binder_mmap_lock); - static HLIST_HEAD(binder_devices); -static HLIST_HEAD(binder_procs); -static HLIST_HEAD(binder_deferred_list); -static HLIST_HEAD(binder_dead_nodes); static struct dentry *binder_debugfs_dir_entry_root; static struct dentry *binder_debugfs_dir_entry_proc; -static int binder_last_id; -static struct workqueue_struct *binder_deferred_workqueue; +atomic_t binder_last_id; #define BINDER_DEBUG_ENTRY(name) \ static int binder_##name##_open(struct inode *inode, struct file *file) \ @@ -173,20 +166,24 @@ enum binder_stat_types { struct binder_stats { int br[_IOC_NR(BR_FAILED_REPLY) + 1]; int bc[_IOC_NR(BC_REPLY_SG) + 1]; - int obj_created[BINDER_STAT_COUNT]; - int obj_deleted[BINDER_STAT_COUNT]; }; -static struct binder_stats binder_stats; +/* These are still global, since it's not always easy to get the context */ +struct binder_obj_stats { + atomic_t obj_created[BINDER_STAT_COUNT]; + atomic_t obj_deleted[BINDER_STAT_COUNT]; +}; + +static struct binder_obj_stats binder_obj_stats; static inline void binder_stats_deleted(enum binder_stat_types type) { - binder_stats.obj_deleted[type]++; + atomic_inc(&binder_obj_stats.obj_deleted[type]); } static inline void binder_stats_created(enum binder_stat_types type) { - binder_stats.obj_created[type]++; + atomic_inc(&binder_obj_stats.obj_created[type]); } struct binder_transaction_log_entry { @@ -207,8 +204,6 @@ struct binder_transaction_log { int full; struct binder_transaction_log_entry entry[32]; }; -static struct binder_transaction_log binder_transaction_log; -static struct binder_transaction_log binder_transaction_log_failed; static struct binder_transaction_log_entry *binder_transaction_log_add( struct binder_transaction_log *log) @@ -229,6 +224,21 @@ struct binder_context { struct binder_node *binder_context_mgr_node; kuid_t binder_context_mgr_uid; const char *name; + + struct mutex binder_main_lock; + struct mutex binder_deferred_lock; + struct mutex binder_mmap_lock; + + struct hlist_head binder_procs; + struct hlist_head binder_dead_nodes; + struct hlist_head binder_deferred_list; + + struct work_struct deferred_work; + struct workqueue_struct *binder_deferred_workqueue; + struct binder_transaction_log transaction_log; + struct binder_transaction_log transaction_log_failed; + + struct binder_stats binder_stats; }; struct binder_device { @@ -459,18 +469,19 @@ static long task_close_fd(struct binder_proc *proc, unsigned int fd) return retval; } -static inline void binder_lock(const char *tag) +static inline void binder_lock(struct binder_context *context, const char *tag) { trace_binder_lock(tag); - mutex_lock(&binder_main_lock); + mutex_lock(&context->binder_main_lock); preempt_disable(); trace_binder_locked(tag); } -static inline void binder_unlock(const char *tag) +static inline void binder_unlock(struct binder_context *context, + const char *tag) { trace_binder_unlock(tag); - mutex_unlock(&binder_main_lock); + mutex_unlock(&context->binder_main_lock); preempt_enable(); } @@ -1017,7 +1028,7 @@ static struct binder_node *binder_new_node(struct binder_proc *proc, binder_stats_created(BINDER_STAT_NODE); rb_link_node(&node->rb_node, parent, p); rb_insert_color(&node->rb_node, &proc->nodes); - node->debug_id = ++binder_last_id; + node->debug_id = atomic_inc_return(&binder_last_id); node->proc = proc; node->ptr = ptr; node->cookie = cookie; @@ -1159,7 +1170,7 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, if (new_ref == NULL) return NULL; binder_stats_created(BINDER_STAT_REF); - new_ref->debug_id = ++binder_last_id; + new_ref->debug_id = atomic_inc_return(&binder_last_id); new_ref->proc = proc; new_ref->node = node; rb_link_node(&new_ref->rb_node_node, parent, p); @@ -1920,7 +1931,7 @@ static void binder_transaction(struct binder_proc *proc, binder_size_t last_fixup_min_off = 0; struct binder_context *context = proc->context; - e = binder_transaction_log_add(&binder_transaction_log); + e = binder_transaction_log_add(&context->transaction_log); e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY); e->from_proc = proc->pid; e->from_thread = thread->pid; @@ -2042,7 +2053,7 @@ static void binder_transaction(struct binder_proc *proc, } binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE); - t->debug_id = ++binder_last_id; + t->debug_id = atomic_inc_return(&binder_last_id); e->debug_id = t->debug_id; if (reply) @@ -2315,7 +2326,8 @@ err_no_context_mgr_node: { struct binder_transaction_log_entry *fe; - fe = binder_transaction_log_add(&binder_transaction_log_failed); + fe = binder_transaction_log_add( + &context->transaction_log_failed); *fe = *e; } @@ -2343,8 +2355,8 @@ static int binder_thread_write(struct binder_proc *proc, return -EFAULT; ptr += sizeof(uint32_t); trace_binder_command(cmd); - if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) { - binder_stats.bc[_IOC_NR(cmd)]++; + if (_IOC_NR(cmd) < ARRAY_SIZE(context->binder_stats.bc)) { + context->binder_stats.bc[_IOC_NR(cmd)]++; proc->stats.bc[_IOC_NR(cmd)]++; thread->stats.bc[_IOC_NR(cmd)]++; } @@ -2710,8 +2722,8 @@ static void binder_stat_br(struct binder_proc *proc, struct binder_thread *thread, uint32_t cmd) { trace_binder_return(cmd); - if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.br)) { - binder_stats.br[_IOC_NR(cmd)]++; + if (_IOC_NR(cmd) < ARRAY_SIZE(proc->stats.br)) { + proc->context->binder_stats.br[_IOC_NR(cmd)]++; proc->stats.br[_IOC_NR(cmd)]++; thread->stats.br[_IOC_NR(cmd)]++; } @@ -2775,7 +2787,7 @@ retry: if (wait_for_proc_work) proc->ready_threads++; - binder_unlock(__func__); + binder_unlock(proc->context, __func__); trace_binder_wait_for_work(wait_for_proc_work, !!thread->transaction_stack, @@ -2802,7 +2814,7 @@ retry: ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread)); } - binder_lock(__func__); + binder_lock(proc->context, __func__); if (wait_for_proc_work) proc->ready_threads--; @@ -3188,14 +3200,14 @@ static unsigned int binder_poll(struct file *filp, struct binder_thread *thread = NULL; int wait_for_proc_work; - binder_lock(__func__); + binder_lock(proc->context, __func__); thread = binder_get_thread(proc); wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo) && thread->return_error == BR_OK; - binder_unlock(__func__); + binder_unlock(proc->context, __func__); if (wait_for_proc_work) { if (binder_has_proc_work(proc, thread)) @@ -3322,6 +3334,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int ret; struct binder_proc *proc = filp->private_data; + struct binder_context *context = proc->context; struct binder_thread *thread; unsigned int size = _IOC_SIZE(cmd); void __user *ubuf = (void __user *)arg; @@ -3335,7 +3348,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (ret) goto err_unlocked; - binder_lock(__func__); + binder_lock(context, __func__); thread = binder_get_thread(proc); if (thread == NULL) { ret = -ENOMEM; @@ -3386,7 +3399,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) err: if (thread) thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN; - binder_unlock(__func__); + binder_unlock(context, __func__); wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); if (ret && ret != -ERESTARTSYS) pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret); @@ -3459,7 +3472,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) } vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE; - mutex_lock(&binder_mmap_lock); + mutex_lock(&proc->context->binder_mmap_lock); if (proc->buffer) { ret = -EBUSY; failure_string = "already mapped"; @@ -3474,7 +3487,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) } proc->buffer = area->addr; proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer; - mutex_unlock(&binder_mmap_lock); + mutex_unlock(&proc->context->binder_mmap_lock); #ifdef CONFIG_CPU_CACHE_VIPT if (cache_is_vipt_aliasing()) { @@ -3523,12 +3536,12 @@ err_alloc_small_buf_failed: kfree(proc->pages); proc->pages = NULL; err_alloc_pages_failed: - mutex_lock(&binder_mmap_lock); + mutex_lock(&proc->context->binder_mmap_lock); vfree(proc->buffer); proc->buffer = NULL; err_get_vm_area_failed: err_already_mapped: - mutex_unlock(&binder_mmap_lock); + mutex_unlock(&proc->context->binder_mmap_lock); err_bad_arg: pr_err("binder_mmap: %d %lx-%lx %s failed %d\n", proc->pid, vma->vm_start, vma->vm_end, failure_string, ret); @@ -3555,15 +3568,15 @@ static int binder_open(struct inode *nodp, struct file *filp) miscdev); proc->context = &binder_dev->context; - binder_lock(__func__); + binder_lock(proc->context, __func__); binder_stats_created(BINDER_STAT_PROC); - hlist_add_head(&proc->proc_node, &binder_procs); + hlist_add_head(&proc->proc_node, &proc->context->binder_procs); proc->pid = current->group_leader->pid; INIT_LIST_HEAD(&proc->delivered_death); filp->private_data = proc; - binder_unlock(__func__); + binder_unlock(proc->context, __func__); if (binder_debugfs_dir_entry_proc) { char strbuf[11]; @@ -3628,6 +3641,7 @@ static int binder_release(struct inode *nodp, struct file *filp) static int binder_node_release(struct binder_node *node, int refs) { struct binder_ref *ref; + struct binder_context *context = node->proc->context; int death = 0; list_del_init(&node->work.entry); @@ -3643,7 +3657,7 @@ static int binder_node_release(struct binder_node *node, int refs) node->proc = NULL; node->local_strong_refs = 0; node->local_weak_refs = 0; - hlist_add_head(&node->dead_node, &binder_dead_nodes); + hlist_add_head(&node->dead_node, &context->binder_dead_nodes); hlist_for_each_entry(ref, &node->refs, node_entry) { refs++; @@ -3708,7 +3722,8 @@ static void binder_deferred_release(struct binder_proc *proc) node = rb_entry(n, struct binder_node, rb_node); nodes++; rb_erase(&node->rb_node, &proc->nodes); - incoming_refs = binder_node_release(node, incoming_refs); + incoming_refs = binder_node_release(node, + incoming_refs); } outgoing_refs = 0; @@ -3780,18 +3795,20 @@ static void binder_deferred_func(struct work_struct *work) { struct binder_proc *proc; struct files_struct *files; + struct binder_context *context = + container_of(work, struct binder_context, deferred_work); int defer; do { trace_binder_lock(__func__); - mutex_lock(&binder_main_lock); + mutex_lock(&context->binder_main_lock); trace_binder_locked(__func__); - mutex_lock(&binder_deferred_lock); + mutex_lock(&context->binder_deferred_lock); preempt_disable(); - if (!hlist_empty(&binder_deferred_list)) { - proc = hlist_entry(binder_deferred_list.first, + if (!hlist_empty(&context->binder_deferred_list)) { + proc = hlist_entry(context->binder_deferred_list.first, struct binder_proc, deferred_work_node); hlist_del_init(&proc->deferred_work_node); defer = proc->deferred_work; @@ -3800,7 +3817,7 @@ static void binder_deferred_func(struct work_struct *work) proc = NULL; defer = 0; } - mutex_unlock(&binder_deferred_lock); + mutex_unlock(&context->binder_deferred_lock); files = NULL; if (defer & BINDER_DEFERRED_PUT_FILES) { @@ -3816,25 +3833,25 @@ static void binder_deferred_func(struct work_struct *work) binder_deferred_release(proc); /* frees proc */ trace_binder_unlock(__func__); - mutex_unlock(&binder_main_lock); + mutex_unlock(&context->binder_main_lock); preempt_enable_no_resched(); if (files) put_files_struct(files); } while (proc); } -static DECLARE_WORK(binder_deferred_work, binder_deferred_func); static void binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer) { - mutex_lock(&binder_deferred_lock); + mutex_lock(&proc->context->binder_deferred_lock); proc->deferred_work |= defer; if (hlist_unhashed(&proc->deferred_work_node)) { hlist_add_head(&proc->deferred_work_node, - &binder_deferred_list); - queue_work(binder_deferred_workqueue, &binder_deferred_work); + &proc->context->binder_deferred_list); + queue_work(proc->context->binder_deferred_workqueue, + &proc->context->deferred_work); } - mutex_unlock(&binder_deferred_lock); + mutex_unlock(&proc->context->binder_deferred_lock); } static void print_binder_transaction(struct seq_file *m, const char *prefix, @@ -4065,8 +4082,20 @@ static const char * const binder_objstat_strings[] = { "transaction_complete" }; +static void add_binder_stats(struct binder_stats *from, struct binder_stats *to) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(to->bc); i++) + to->bc[i] += from->bc[i]; + + for (i = 0; i < ARRAY_SIZE(to->br); i++) + to->br[i] += from->br[i]; +} + static void print_binder_stats(struct seq_file *m, const char *prefix, - struct binder_stats *stats) + struct binder_stats *stats, + struct binder_obj_stats *obj_stats) { int i; @@ -4086,16 +4115,21 @@ static void print_binder_stats(struct seq_file *m, const char *prefix, binder_return_strings[i], stats->br[i]); } - BUILD_BUG_ON(ARRAY_SIZE(stats->obj_created) != + if (!obj_stats) + return; + + BUILD_BUG_ON(ARRAY_SIZE(obj_stats->obj_created) != ARRAY_SIZE(binder_objstat_strings)); - BUILD_BUG_ON(ARRAY_SIZE(stats->obj_created) != - ARRAY_SIZE(stats->obj_deleted)); - for (i = 0; i < ARRAY_SIZE(stats->obj_created); i++) { - if (stats->obj_created[i] || stats->obj_deleted[i]) + BUILD_BUG_ON(ARRAY_SIZE(obj_stats->obj_created) != + ARRAY_SIZE(obj_stats->obj_deleted)); + for (i = 0; i < ARRAY_SIZE(obj_stats->obj_created); i++) { + int obj_created = atomic_read(&obj_stats->obj_created[i]); + int obj_deleted = atomic_read(&obj_stats->obj_deleted[i]); + + if (obj_created || obj_deleted) seq_printf(m, "%s%s: active %d total %d\n", prefix, - binder_objstat_strings[i], - stats->obj_created[i] - stats->obj_deleted[i], - stats->obj_created[i]); + binder_objstat_strings[i], + obj_created - obj_deleted, obj_created); } } @@ -4150,85 +4184,131 @@ static void print_binder_proc_stats(struct seq_file *m, } seq_printf(m, " pending transactions: %d\n", count); - print_binder_stats(m, " ", &proc->stats); + print_binder_stats(m, " ", &proc->stats, NULL); } static int binder_state_show(struct seq_file *m, void *unused) { + struct binder_device *device; + struct binder_context *context; struct binder_proc *proc; struct binder_node *node; int do_lock = !binder_debug_no_lock; - - if (do_lock) - binder_lock(__func__); + bool wrote_dead_nodes_header = false; seq_puts(m, "binder state:\n"); - if (!hlist_empty(&binder_dead_nodes)) - seq_puts(m, "dead nodes:\n"); - hlist_for_each_entry(node, &binder_dead_nodes, dead_node) - print_binder_node(m, node); + hlist_for_each_entry(device, &binder_devices, hlist) { + context = &device->context; + if (do_lock) + binder_lock(context, __func__); + if (!wrote_dead_nodes_header && + !hlist_empty(&context->binder_dead_nodes)) { + seq_puts(m, "dead nodes:\n"); + wrote_dead_nodes_header = true; + } + hlist_for_each_entry(node, &context->binder_dead_nodes, + dead_node) + print_binder_node(m, node); + + if (do_lock) + binder_unlock(context, __func__); + } - hlist_for_each_entry(proc, &binder_procs, proc_node) - print_binder_proc(m, proc, 1); - if (do_lock) - binder_unlock(__func__); + hlist_for_each_entry(device, &binder_devices, hlist) { + context = &device->context; + if (do_lock) + binder_lock(context, __func__); + + hlist_for_each_entry(proc, &context->binder_procs, proc_node) + print_binder_proc(m, proc, 1); + if (do_lock) + binder_unlock(context, __func__); + } return 0; } static int binder_stats_show(struct seq_file *m, void *unused) { + struct binder_device *device; + struct binder_context *context; struct binder_proc *proc; + struct binder_stats total_binder_stats; int do_lock = !binder_debug_no_lock; - if (do_lock) - binder_lock(__func__); + memset(&total_binder_stats, 0, sizeof(struct binder_stats)); + + hlist_for_each_entry(device, &binder_devices, hlist) { + context = &device->context; + if (do_lock) + binder_lock(context, __func__); + + add_binder_stats(&context->binder_stats, &total_binder_stats); + + if (do_lock) + binder_unlock(context, __func__); + } seq_puts(m, "binder stats:\n"); + print_binder_stats(m, "", &total_binder_stats, &binder_obj_stats); - print_binder_stats(m, "", &binder_stats); + hlist_for_each_entry(device, &binder_devices, hlist) { + context = &device->context; + if (do_lock) + binder_lock(context, __func__); - hlist_for_each_entry(proc, &binder_procs, proc_node) - print_binder_proc_stats(m, proc); - if (do_lock) - binder_unlock(__func__); + hlist_for_each_entry(proc, &context->binder_procs, proc_node) + print_binder_proc_stats(m, proc); + if (do_lock) + binder_unlock(context, __func__); + } return 0; } static int binder_transactions_show(struct seq_file *m, void *unused) { + struct binder_device *device; + struct binder_context *context; struct binder_proc *proc; int do_lock = !binder_debug_no_lock; - if (do_lock) - binder_lock(__func__); - seq_puts(m, "binder transactions:\n"); - hlist_for_each_entry(proc, &binder_procs, proc_node) - print_binder_proc(m, proc, 0); - if (do_lock) - binder_unlock(__func__); + hlist_for_each_entry(device, &binder_devices, hlist) { + context = &device->context; + if (do_lock) + binder_lock(context, __func__); + + hlist_for_each_entry(proc, &context->binder_procs, proc_node) + print_binder_proc(m, proc, 0); + if (do_lock) + binder_unlock(context, __func__); + } return 0; } static int binder_proc_show(struct seq_file *m, void *unused) { + struct binder_device *device; + struct binder_context *context; struct binder_proc *itr; int pid = (unsigned long)m->private; int do_lock = !binder_debug_no_lock; - if (do_lock) - binder_lock(__func__); + hlist_for_each_entry(device, &binder_devices, hlist) { + context = &device->context; + if (do_lock) + binder_lock(context, __func__); - hlist_for_each_entry(itr, &binder_procs, proc_node) { - if (itr->pid == pid) { - seq_puts(m, "binder proc state:\n"); - print_binder_proc(m, itr, 1); + hlist_for_each_entry(itr, &context->binder_procs, proc_node) { + if (itr->pid == pid) { + seq_puts(m, "binder proc state:\n"); + print_binder_proc(m, itr, 1); + } } + if (do_lock) + binder_unlock(context, __func__); } - if (do_lock) - binder_unlock(__func__); return 0; } @@ -4243,11 +4323,10 @@ static void print_binder_transaction_log_entry(struct seq_file *m, e->to_node, e->target_handle, e->data_size, e->offsets_size); } -static int binder_transaction_log_show(struct seq_file *m, void *unused) +static int print_binder_transaction_log(struct seq_file *m, + struct binder_transaction_log *log) { - struct binder_transaction_log *log = m->private; int i; - if (log->full) { for (i = log->next; i < ARRAY_SIZE(log->entry); i++) print_binder_transaction_log_entry(m, &log->entry[i]); @@ -4257,6 +4336,31 @@ static int binder_transaction_log_show(struct seq_file *m, void *unused) return 0; } +static int binder_transaction_log_show(struct seq_file *m, void *unused) +{ + struct binder_device *device; + struct binder_context *context; + + hlist_for_each_entry(device, &binder_devices, hlist) { + context = &device->context; + print_binder_transaction_log(m, &context->transaction_log); + } + return 0; +} + +static int binder_failed_transaction_log_show(struct seq_file *m, void *unused) +{ + struct binder_device *device; + struct binder_context *context; + + hlist_for_each_entry(device, &binder_devices, hlist) { + context = &device->context; + print_binder_transaction_log(m, + &context->transaction_log_failed); + } + return 0; +} + static const struct file_operations binder_fops = { .owner = THIS_MODULE, .poll = binder_poll, @@ -4272,11 +4376,20 @@ BINDER_DEBUG_ENTRY(state); BINDER_DEBUG_ENTRY(stats); BINDER_DEBUG_ENTRY(transactions); BINDER_DEBUG_ENTRY(transaction_log); +BINDER_DEBUG_ENTRY(failed_transaction_log); + +static void __init free_binder_device(struct binder_device *device) +{ + if (device->context.binder_deferred_workqueue) + destroy_workqueue(device->context.binder_deferred_workqueue); + kfree(device); +} static int __init init_binder_device(const char *name) { int ret; struct binder_device *binder_device; + struct binder_context *context; binder_device = kzalloc(sizeof(*binder_device), GFP_KERNEL); if (!binder_device) @@ -4286,31 +4399,65 @@ static int __init init_binder_device(const char *name) binder_device->miscdev.minor = MISC_DYNAMIC_MINOR; binder_device->miscdev.name = name; - binder_device->context.binder_context_mgr_uid = INVALID_UID; - binder_device->context.name = name; + context = &binder_device->context; + context->binder_context_mgr_uid = INVALID_UID; + context->name = name; + + mutex_init(&context->binder_main_lock); + mutex_init(&context->binder_deferred_lock); + mutex_init(&context->binder_mmap_lock); + + context->binder_deferred_workqueue = + create_singlethread_workqueue(name); + + if (!context->binder_deferred_workqueue) { + ret = -ENOMEM; + goto err_create_singlethread_workqueue_failed; + } + + INIT_HLIST_HEAD(&context->binder_procs); + INIT_HLIST_HEAD(&context->binder_dead_nodes); + INIT_HLIST_HEAD(&context->binder_deferred_list); + INIT_WORK(&context->deferred_work, binder_deferred_func); ret = misc_register(&binder_device->miscdev); if (ret < 0) { - kfree(binder_device); - return ret; + goto err_misc_register_failed; } hlist_add_head(&binder_device->hlist, &binder_devices); + return ret; + +err_create_singlethread_workqueue_failed: +err_misc_register_failed: + free_binder_device(binder_device); return ret; } static int __init binder_init(void) { - int ret; + int ret = 0; char *device_name, *device_names; struct binder_device *device; struct hlist_node *tmp; - binder_deferred_workqueue = create_singlethread_workqueue("binder"); - if (!binder_deferred_workqueue) + /* + * Copy the module_parameter string, because we don't want to + * tokenize it in-place. + */ + device_names = kzalloc(strlen(binder_devices_param) + 1, GFP_KERNEL); + if (!device_names) return -ENOMEM; + strcpy(device_names, binder_devices_param); + + while ((device_name = strsep(&device_names, ","))) { + ret = init_binder_device(device_name); + if (ret) + goto err_init_binder_device_failed; + } + binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL); if (binder_debugfs_dir_entry_root) binder_debugfs_dir_entry_proc = debugfs_create_dir("proc", @@ -4335,30 +4482,13 @@ static int __init binder_init(void) debugfs_create_file("transaction_log", S_IRUGO, binder_debugfs_dir_entry_root, - &binder_transaction_log, + NULL, &binder_transaction_log_fops); debugfs_create_file("failed_transaction_log", S_IRUGO, binder_debugfs_dir_entry_root, - &binder_transaction_log_failed, - &binder_transaction_log_fops); - } - - /* - * Copy the module_parameter string, because we don't want to - * tokenize it in-place. - */ - device_names = kzalloc(strlen(binder_devices_param) + 1, GFP_KERNEL); - if (!device_names) { - ret = -ENOMEM; - goto err_alloc_device_names_failed; - } - strcpy(device_names, binder_devices_param); - - while ((device_name = strsep(&device_names, ","))) { - ret = init_binder_device(device_name); - if (ret) - goto err_init_binder_device_failed; + NULL, + &binder_failed_transaction_log_fops); } return ret; @@ -4367,12 +4497,8 @@ err_init_binder_device_failed: hlist_for_each_entry_safe(device, tmp, &binder_devices, hlist) { misc_deregister(&device->miscdev); hlist_del(&device->hlist); - kfree(device); + free_binder_device(device); } -err_alloc_device_names_failed: - debugfs_remove_recursive(binder_debugfs_dir_entry_root); - - destroy_workqueue(binder_deferred_workqueue); return ret; } diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 09c07f519952..0e494108c20c 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -1042,7 +1042,7 @@ static int print_wakeup_source_stats(struct seq_file *m, active_time = ktime_set(0, 0); } - seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n", + seq_printf(m, "%-32s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n", ws->name, active_count, ws->event_count, ws->wakeup_count, ws->expire_count, ktime_to_ms(active_time), ktime_to_ms(total_time), @@ -1062,7 +1062,7 @@ static int wakeup_sources_stats_show(struct seq_file *m, void *unused) { struct wakeup_source *ws; - seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t" + seq_puts(m, "name\t\t\t\t\tactive_count\tevent_count\twakeup_count\t" "expire_count\tactive_since\ttotal_time\tmax_time\t" "last_change\tprevent_suspend_time\n"); diff --git a/drivers/bluetooth/btfm_slim_wcn3990.c b/drivers/bluetooth/btfm_slim_wcn3990.c index d61454ac6e84..c93b29281e35 100644 --- a/drivers/bluetooth/btfm_slim_wcn3990.c +++ b/drivers/bluetooth/btfm_slim_wcn3990.c @@ -69,6 +69,14 @@ error: return ret; } +static inline int is_fm_port(uint8_t port_num) +{ + if (port_num == CHRK_SB_PGD_PORT_TX1_FM || + port_num == CHRK_SB_PGD_PORT_TX2_FM) + return 1; + else + return 0; +} int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num, uint8_t rxport, uint8_t enable) @@ -88,9 +96,7 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num, goto enable_disable_txport; /* Multiple Channel Setting - only for FM Tx */ - if (port_num == CHRK_SB_PGD_PORT_TX1_FM || - port_num == CHRK_SB_PGD_PORT_TX2_FM) { - + if (is_fm_port(port_num)) { reg_val = (0x1 << CHRK_SB_PGD_PORT_TX1_FM) | (0x1 << CHRK_SB_PGD_PORT_TX2_FM); reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num); @@ -116,10 +122,14 @@ enable_disable_txport: reg = CHRK_SB_PGD_PORT_TX_CFGN(port_num); enable_disable_rxport: - if (enable) - /* Set water mark to 1 and enable the port */ - reg_val = CHRK_SB_PGD_PORT_ENABLE | CHRK_SB_PGD_PORT_WM_LB; - else + if (enable) { + if (is_fm_port(port_num)) + reg_val = CHRK_SB_PGD_PORT_ENABLE | + CHRK_SB_PGD_PORT_WM_L3; + else + reg_val = CHRK_SB_PGD_PORT_ENABLE | + CHRK_SB_PGD_PORT_WM_LB; + } else reg_val = CHRK_SB_PGD_PORT_DISABLE; ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index ebbe31fee7ae..4bbe4e5f9a6d 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -592,6 +592,16 @@ config DEVPORT source "drivers/s390/char/Kconfig" +config MSM_SMD_PKT + bool "Enable device interface for some SMD packet ports" + default n + depends on MSM_SMD + help + smd_pkt driver provides the interface for the userspace clients + to communicate over smd via device nodes. This enable the + usersapce clients to read and write to some smd packets channel + for MSM chipset. + config TILE_SROM bool "Character-device access via hypervisor to the Tilera SPI ROM" depends on TILE @@ -607,7 +617,7 @@ source "drivers/char/xillybus/Kconfig" config MSM_ADSPRPC tristate "QTI ADSP RPC driver" - depends on MSM_GLINK + depends on MSM_SMD help Provides a communication mechanism that allows for clients to make remote method invocations across processor boundary to diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 7b0bd5408324..77697b8c42c0 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o obj-$(CONFIG_RAW_DRIVER) += raw.o obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o +obj-$(CONFIG_MSM_SMD_PKT) += msm_smd_pkt.o obj-$(CONFIG_MSPEC) += mspec.o obj-$(CONFIG_MMTIMER) += mmtimer.o obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 479599473381..10c4d8ce2410 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -25,6 +25,7 @@ #include <linux/hash.h> #include <linux/msm_ion.h> #include <soc/qcom/secure_buffer.h> +#include <soc/qcom/smd.h> #include <soc/qcom/glink.h> #include <soc/qcom/subsystem_notif.h> #include <soc/qcom/subsystem_restart.h> @@ -213,6 +214,7 @@ struct fastrpc_channel_ctx { struct completion work; struct notifier_block nb; struct kref kref; + int channel; int sesscount; int ssrcount; void *handle; @@ -238,6 +240,7 @@ struct fastrpc_apps { spinlock_t hlock; struct ion_client *client; struct device *dev; + bool glink; }; struct fastrpc_mmap { @@ -298,18 +301,21 @@ static struct fastrpc_channel_ctx gcinfo[NUM_CHANNELS] = { { .name = "adsprpc-smd", .subsys = "adsp", + .channel = SMD_APPS_QDSP, .link.link_info.edge = "lpass", .link.link_info.transport = "smem", }, { .name = "mdsprpc-smd", .subsys = "modem", + .channel = SMD_APPS_MODEM, .link.link_info.edge = "mpss", .link.link_info.transport = "smem", }, { .name = "sdsprpc-smd", .subsys = "slpi", + .channel = SMD_APPS_DSPS, .link.link_info.edge = "dsps", .link.link_info.transport = "smem", .vmid = VMID_SSC_Q6, @@ -1382,7 +1388,7 @@ static int fastrpc_invoke_send(struct smq_invoke_ctx *ctx, struct smq_msg *msg = &ctx->msg; struct fastrpc_file *fl = ctx->fl; struct fastrpc_channel_ctx *channel_ctx = &fl->apps->channel[fl->cid]; - int err = 0; + int err = 0, len; VERIFY(err, 0 != channel_ctx->chan); if (err) @@ -1397,21 +1403,64 @@ static int fastrpc_invoke_send(struct smq_invoke_ctx *ctx, msg->invoke.page.addr = ctx->buf ? ctx->buf->phys : 0; msg->invoke.page.size = buf_page_size(ctx->used); - if (fl->ssrcount != channel_ctx->ssrcount) { - err = -ECONNRESET; - goto bail; + if (fl->apps->glink) { + if (fl->ssrcount != channel_ctx->ssrcount) { + err = -ECONNRESET; + goto bail; + } + VERIFY(err, channel_ctx->link.port_state == + FASTRPC_LINK_CONNECTED); + if (err) + goto bail; + err = glink_tx(channel_ctx->chan, + (void *)&fl->apps->channel[fl->cid], msg, sizeof(*msg), + GLINK_TX_REQ_INTENT); + } else { + spin_lock(&fl->apps->hlock); + len = smd_write((smd_channel_t *) + channel_ctx->chan, + msg, sizeof(*msg)); + spin_unlock(&fl->apps->hlock); + VERIFY(err, len == sizeof(*msg)); } - VERIFY(err, channel_ctx->link.port_state == - FASTRPC_LINK_CONNECTED); - if (err) - goto bail; - err = glink_tx(channel_ctx->chan, - (void *)&fl->apps->channel[fl->cid], msg, sizeof(*msg), - GLINK_TX_REQ_INTENT); bail: return err; } +static void fastrpc_smd_read_handler(int cid) +{ + struct fastrpc_apps *me = &gfa; + struct smq_invoke_rsp rsp = {0}; + int ret = 0; + + do { + ret = smd_read_from_cb(me->channel[cid].chan, &rsp, + sizeof(rsp)); + if (ret != sizeof(rsp)) + break; + rsp.ctx = rsp.ctx & ~1; + context_notify_user(uint64_to_ptr(rsp.ctx), rsp.retval); + } while (ret == sizeof(rsp)); +} + +static void smd_event_handler(void *priv, unsigned event) +{ + struct fastrpc_apps *me = &gfa; + int cid = (int)(uintptr_t)priv; + + switch (event) { + case SMD_EVENT_OPEN: + complete(&me->channel[cid].work); + break; + case SMD_EVENT_CLOSE: + fastrpc_notify_drivers(me, cid); + break; + case SMD_EVENT_DATA: + fastrpc_smd_read_handler(cid); + break; + } +} + static void fastrpc_init(struct fastrpc_apps *me) { int i; @@ -1987,10 +2036,12 @@ static void fastrpc_channel_close(struct kref *kref) ctx = container_of(kref, struct fastrpc_channel_ctx, kref); cid = ctx - &gcinfo[0]; - fastrpc_glink_close(ctx->chan, cid); + if (!me->glink) + smd_close(ctx->chan); + else + fastrpc_glink_close(ctx->chan, cid); + ctx->chan = 0; - glink_unregister_link_state_cb(ctx->link.link_notify_handle); - ctx->link.link_notify_handle = 0; mutex_unlock(&me->smd_mutex); pr_info("'closed /dev/%s c %d %d'\n", gcinfo[cid].name, MAJOR(me->dev_no), cid); @@ -2405,8 +2456,16 @@ static int fastrpc_channel_open(struct fastrpc_file *fl) fl->ssrcount = me->channel[cid].ssrcount; if ((kref_get_unless_zero(&me->channel[cid].kref) == 0) || (me->channel[cid].chan == 0)) { - fastrpc_glink_register(cid, me); - VERIFY(err, 0 == fastrpc_glink_open(cid)); + if (me->glink) { + fastrpc_glink_register(cid, me); + VERIFY(err, 0 == fastrpc_glink_open(cid)); + } else { + VERIFY(err, !smd_named_open_on_edge(FASTRPC_SMD_GUID, + gcinfo[cid].channel, + (smd_channel_t **)&me->channel[cid].chan, + (void *)(uintptr_t)cid, + smd_event_handler)); + } if (err) goto bail; @@ -2635,7 +2694,11 @@ static int fastrpc_restart_notifier_cb(struct notifier_block *nb, ctx->ssrcount++; ctx->issubsystemup = 0; if (ctx->chan) { - fastrpc_glink_close(ctx->chan, cid); + if (me->glink) + fastrpc_glink_close(ctx->chan, cid); + else + smd_close(ctx->chan); + ctx->chan = 0; pr_info("'restart notifier: closed /dev/%s c %d %d'\n", gcinfo[cid].name, MAJOR(me->dev_no), cid); @@ -2859,7 +2922,7 @@ static int fastrpc_probe(struct platform_device *pdev) } return 0; } - + me->glink = of_property_read_bool(dev->of_node, "qcom,fastrpc-glink"); VERIFY(err, !of_platform_populate(pdev->dev.of_node, fastrpc_match_table, NULL, &pdev->dev)); diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c index 437077c4d44d..3c10462c2274 100644 --- a/drivers/char/diag/diag_masks.c +++ b/drivers/char/diag/diag_masks.c @@ -457,7 +457,9 @@ static void diag_send_feature_mask_update(uint8_t peripheral) if (driver->supports_apps_hdlc_encoding) DIAG_SET_FEATURE_MASK(F_DIAG_APPS_HDLC_ENCODE); if (driver->supports_apps_header_untagging) { - if (peripheral == PERIPHERAL_MODEM) { + if (peripheral == PERIPHERAL_MODEM || + peripheral == PERIPHERAL_LPASS || + peripheral == PERIPHERAL_CDSP) { DIAG_SET_FEATURE_MASK(F_DIAG_PKT_HEADER_UNTAG); driver->peripheral_untag[peripheral] = ENABLE_PKT_HEADER_UNTAGGING; diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c index dc3029cc459d..bd34e6cceec0 100644 --- a/drivers/char/diag/diag_memorydevice.c +++ b/drivers/char/diag/diag_memorydevice.c @@ -129,6 +129,37 @@ void diag_md_close_all() diag_ws_reset(DIAG_WS_MUX); } +static int diag_md_get_peripheral(int ctxt) +{ + int peripheral; + + if (driver->num_pd_session) { + peripheral = GET_PD_CTXT(ctxt); + switch (peripheral) { + case UPD_WLAN: + case UPD_AUDIO: + case UPD_SENSORS: + break; + case DIAG_ID_MPSS: + case DIAG_ID_LPASS: + case DIAG_ID_CDSP: + default: + peripheral = + GET_BUF_PERIPHERAL(ctxt); + if (peripheral > NUM_PERIPHERALS) + peripheral = -EINVAL; + break; + } + } else { + /* Account for Apps data as well */ + peripheral = GET_BUF_PERIPHERAL(ctxt); + if (peripheral > NUM_PERIPHERALS) + peripheral = -EINVAL; + } + + return peripheral; +} + int diag_md_write(int id, unsigned char *buf, int len, int ctx) { int i; @@ -144,26 +175,13 @@ int diag_md_write(int id, unsigned char *buf, int len, int ctx) if (!buf || len < 0) return -EINVAL; - if (driver->pd_logging_mode) { - peripheral = GET_PD_CTXT(ctx); - switch (peripheral) { - case UPD_WLAN: - break; - case DIAG_ID_MPSS: - default: - peripheral = GET_BUF_PERIPHERAL(ctx); - if (peripheral > NUM_PERIPHERALS) - return -EINVAL; - break; - } - } else { - /* Account for Apps data as well */ - peripheral = GET_BUF_PERIPHERAL(ctx); - if (peripheral > NUM_PERIPHERALS) - return -EINVAL; - } + peripheral = + diag_md_get_peripheral(ctx); + if (peripheral < 0) + return -EINVAL; - session_info = diag_md_session_get_peripheral(peripheral); + session_info = + diag_md_session_get_peripheral(peripheral); if (!session_info) return -EIO; @@ -243,31 +261,15 @@ int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size, entry = &ch->tbl[j]; if (entry->len <= 0) continue; - if (driver->pd_logging_mode) { - peripheral = GET_PD_CTXT(entry->ctx); - switch (peripheral) { - case UPD_WLAN: - break; - case DIAG_ID_MPSS: - default: - peripheral = - GET_BUF_PERIPHERAL(entry->ctx); - if (peripheral > NUM_PERIPHERALS) - goto drop_data; - break; - } - } else { - /* Account for Apps data as well */ - peripheral = GET_BUF_PERIPHERAL(entry->ctx); - if (peripheral > NUM_PERIPHERALS) - goto drop_data; - } + + peripheral = diag_md_get_peripheral(entry->ctx); + if (peripheral < 0) + goto drop_data; session_info = diag_md_session_get_peripheral(peripheral); if (!session_info) { - mutex_unlock(&driver->diagfwd_untag_mutex); - return -EIO; + goto drop_data; } if (session_info && info && @@ -363,9 +365,15 @@ int diag_md_close_peripheral(int id, uint8_t peripheral) spin_lock_irqsave(&ch->lock, flags); for (i = 0; i < ch->num_tbl_entries && !found; i++) { entry = &ch->tbl[i]; - if ((GET_BUF_PERIPHERAL(entry->ctx) != peripheral) || - (GET_PD_CTXT(entry->ctx) != peripheral)) - continue; + + if (peripheral > NUM_PERIPHERALS) { + if (GET_PD_CTXT(entry->ctx) != peripheral) + continue; + } else { + if (GET_BUF_PERIPHERAL(entry->ctx) != + peripheral) + continue; + } found = 1; if (ch->ops && ch->ops->write_done) { ch->ops->write_done(entry->buf, entry->len, diff --git a/drivers/char/diag/diag_mux.c b/drivers/char/diag/diag_mux.c index 55c5de1ea9fc..39f4b08d9b0a 100644 --- a/drivers/char/diag/diag_mux.c +++ b/drivers/char/diag/diag_mux.c @@ -27,7 +27,7 @@ #include "diag_mux.h" #include "diag_usb.h" #include "diag_memorydevice.h" - +#include "diag_ipc_logging.h" struct diag_mux_state_t *diag_mux; static struct diag_logger_t usb_logger; @@ -146,7 +146,15 @@ int diag_mux_write(int proc, unsigned char *buf, int len, int ctx) case DIAG_ID_MPSS: upd = PERIPHERAL_MODEM; break; + case DIAG_ID_LPASS: + upd = PERIPHERAL_LPASS; + break; + case DIAG_ID_CDSP: + upd = PERIPHERAL_CDSP; + break; case UPD_WLAN: + case UPD_AUDIO: + case UPD_SENSORS: break; default: pr_err("diag: invalid pd ctxt= %d\n", upd); diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h index b68a47219132..b17538a10ea9 100644 --- a/drivers/char/diag/diagchar.h +++ b/drivers/char/diag/diagchar.h @@ -76,7 +76,9 @@ | DIAG_CON_LPASS | DIAG_CON_WCNSS \ | DIAG_CON_SENSORS | DIAG_CON_WDSP \ | DIAG_CON_CDSP) -#define DIAG_CON_UPD_ALL (DIAG_CON_UPD_WLAN) +#define DIAG_CON_UPD_ALL (DIAG_CON_UPD_WLAN \ + | DIAG_CON_UPD_AUDIO \ + | DIAG_CON_UPD_SENSORS) #define DIAG_STM_MODEM 0x01 #define DIAG_STM_LPASS 0x02 @@ -222,6 +224,10 @@ #define DIAG_ID_APPS 1 #define DIAG_ID_MPSS 2 #define DIAG_ID_WLAN 3 +#define DIAG_ID_LPASS 4 +#define DIAG_ID_CDSP 5 +#define DIAG_ID_AUDIO 6 +#define DIAG_ID_SENSORS 7 /* Number of sessions possible in Memory Device Mode. +1 for Apps data */ #define NUM_MD_SESSIONS (NUM_PERIPHERALS \ @@ -598,10 +604,15 @@ struct diagchar_dev { int in_busy_dcipktdata; int logging_mode; int logging_mask; - int pd_logging_mode; + int pd_logging_mode[NUM_UPD]; + int pd_session_clear[NUM_UPD]; int num_pd_session; - int cpd_len_1; - int cpd_len_2; + int cpd_len_1[NUM_PERIPHERALS]; + int cpd_len_2[NUM_PERIPHERALS]; + int upd_len_1_a[NUM_PERIPHERALS]; + int upd_len_1_b[NUM_PERIPHERALS]; + int upd_len_2_a; + int upd_len_2_b; int mask_check; uint32_t md_session_mask; uint8_t md_session_mode; diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index 4f56696f52e9..682c035c5bd4 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -397,6 +397,10 @@ static uint32_t diag_translate_kernel_to_user_mask(uint32_t peripheral_mask) ret |= DIAG_CON_CDSP; if (peripheral_mask & MD_PERIPHERAL_MASK(UPD_WLAN)) ret |= DIAG_CON_UPD_WLAN; + if (peripheral_mask & MD_PERIPHERAL_MASK(UPD_AUDIO)) + ret |= DIAG_CON_UPD_AUDIO; + if (peripheral_mask & MD_PERIPHERAL_MASK(UPD_SENSORS)) + ret |= DIAG_CON_UPD_SENSORS; return ret; } int diag_mask_param(void) @@ -426,8 +430,8 @@ void diag_clear_masks(struct diag_md_session_t *info) static void diag_close_logging_process(const int pid) { - int i; - int session_peripheral_mask; + int i, j; + int session_mask; struct diag_md_session_t *session_info = NULL; struct diag_logging_mode_param_t params; @@ -443,27 +447,34 @@ static void diag_close_logging_process(const int pid) mutex_unlock(&driver->diag_maskclear_mutex); mutex_lock(&driver->diagchar_mutex); - session_peripheral_mask = session_info->peripheral_mask; + + session_mask = session_info->peripheral_mask; diag_md_session_close(session_info); - mutex_unlock(&driver->diagchar_mutex); + for (i = 0; i < NUM_MD_SESSIONS; i++) - if (MD_PERIPHERAL_MASK(i) & session_peripheral_mask) + if (MD_PERIPHERAL_MASK(i) & session_mask) diag_mux_close_peripheral(DIAG_LOCAL_PROC, i); params.req_mode = USB_MODE; params.mode_param = 0; params.peripheral_mask = - diag_translate_kernel_to_user_mask(session_peripheral_mask); - if (driver->pd_logging_mode) - params.pd_mask = - diag_translate_kernel_to_user_mask(session_peripheral_mask); - - if (session_peripheral_mask & MD_PERIPHERAL_MASK(UPD_WLAN)) { - driver->pd_logging_mode--; - driver->num_pd_session--; + diag_translate_kernel_to_user_mask(session_mask); + + for (i = UPD_WLAN; i < NUM_MD_SESSIONS; i++) { + if (session_mask & + MD_PERIPHERAL_MASK(i)) { + j = i - UPD_WLAN; + driver->pd_session_clear[j] = 1; + driver->pd_logging_mode[j] = 0; + driver->num_pd_session -= 1; + params.pd_mask = + diag_translate_kernel_to_user_mask(session_mask); + } else + params.pd_mask = 0; } - mutex_lock(&driver->diagchar_mutex); + diag_switch_logging(¶ms); + mutex_unlock(&driver->diagchar_mutex); } @@ -985,6 +996,11 @@ static int diag_send_raw_data_remote(int proc, void *buf, int len, hdlc_disabled = driver->hdlc_disabled; if (hdlc_disabled) { payload = *(uint16_t *)(buf + 2); + if (payload > DIAG_MAX_HDLC_BUF_SIZE) { + pr_err("diag: Dropping packet, payload size is %d\n", + payload); + return -EBADMSG; + } driver->hdlc_encode_buf_len = payload; /* * Adding 4 bytes for start (1 byte), version (1 byte) and @@ -1562,17 +1578,22 @@ static uint32_t diag_translate_mask(uint32_t peripheral_mask) ret |= (1 << PERIPHERAL_CDSP); if (peripheral_mask & DIAG_CON_UPD_WLAN) ret |= (1 << UPD_WLAN); + if (peripheral_mask & DIAG_CON_UPD_AUDIO) + ret |= (1 << UPD_AUDIO); + if (peripheral_mask & DIAG_CON_UPD_SENSORS) + ret |= (1 << UPD_SENSORS); return ret; } static int diag_switch_logging(struct diag_logging_mode_param_t *param) { - int new_mode; + int new_mode, i; int curr_mode; int err = 0; uint8_t do_switch = 1; uint32_t peripheral_mask = 0; + uint8_t peripheral, upd; if (!param) return -EINVAL; @@ -1583,10 +1604,28 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param) return -EINVAL; } - switch (param->pd_mask) { - case DIAG_CON_UPD_WLAN: - if (driver->md_session_map[PERIPHERAL_MODEM] && - (MD_PERIPHERAL_MASK(PERIPHERAL_MODEM) & + if (param->pd_mask) { + switch (param->pd_mask) { + case DIAG_CON_UPD_WLAN: + peripheral = PERIPHERAL_MODEM; + upd = UPD_WLAN; + break; + case DIAG_CON_UPD_AUDIO: + peripheral = PERIPHERAL_LPASS; + upd = UPD_AUDIO; + break; + case DIAG_CON_UPD_SENSORS: + peripheral = PERIPHERAL_LPASS; + upd = UPD_SENSORS; + break; + default: + DIAG_LOG(DIAG_DEBUG_USERSPACE, + "asking for mode switch with no pd mask set\n"); + return -EINVAL; + } + + if (driver->md_session_map[peripheral] && + (MD_PERIPHERAL_MASK(peripheral) & diag_mux->mux_mask)) { DIAG_LOG(DIAG_DEBUG_USERSPACE, "diag_fr: User PD is already logging onto active peripheral logging\n"); @@ -1595,15 +1634,16 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param) peripheral_mask = diag_translate_mask(param->pd_mask); param->peripheral_mask = peripheral_mask; - driver->pd_logging_mode++; - driver->num_pd_session++; - break; - - default: + i = upd - UPD_WLAN; + if (!driver->pd_session_clear[i]) { + driver->pd_logging_mode[i] = 1; + driver->num_pd_session += 1; + driver->pd_session_clear[i] = 0; + } + } else { peripheral_mask = diag_translate_mask(param->peripheral_mask); param->peripheral_mask = peripheral_mask; - break; } switch (param->req_mode) { @@ -1945,9 +1985,36 @@ static int diag_ioctl_hdlc_toggle(unsigned long ioarg) return 0; } -static int diag_ioctl_query_pd_logging(unsigned long ioarg) +static int diag_ioctl_query_pd_logging(struct diag_logging_mode_param_t *param) { int ret = -EINVAL; + int peripheral; + char *p_str = NULL; + + if (!param) + return -EINVAL; + + if (!param->pd_mask) { + DIAG_LOG(DIAG_DEBUG_USERSPACE, + "query with no pd mask set, returning error\n"); + return -EINVAL; + } + + switch (param->pd_mask) { + case DIAG_CON_UPD_WLAN: + peripheral = PERIPHERAL_MODEM; + p_str = "MODEM"; + break; + case DIAG_CON_UPD_AUDIO: + case DIAG_CON_UPD_SENSORS: + peripheral = PERIPHERAL_LPASS; + p_str = "LPASS"; + break; + default: + DIAG_LOG(DIAG_DEBUG_USERSPACE, + "Invalid pd mask, returning EINVAL\n"); + return -EINVAL; + } DIAG_LOG(DIAG_DEBUG_USERSPACE, "diag: %s: Untagging support on APPS is %s\n", __func__, @@ -1955,12 +2022,13 @@ static int diag_ioctl_query_pd_logging(unsigned long ioarg) "present" : "absent")); DIAG_LOG(DIAG_DEBUG_USERSPACE, - "diag: %s: Tagging support on MODEM is %s\n", __func__, - (driver->feature[PERIPHERAL_MODEM].untag_header ? + "diag: %s: Tagging support on %s is %s\n", + __func__, p_str, + (driver->feature[peripheral].untag_header ? "present" : "absent")); if (driver->supports_apps_header_untagging && - driver->feature[PERIPHERAL_MODEM].untag_header) + driver->feature[peripheral].untag_header) ret = 0; return ret; @@ -2206,7 +2274,10 @@ long diagchar_compat_ioctl(struct file *filp, result = diag_ioctl_hdlc_toggle(ioarg); break; case DIAG_IOCTL_QUERY_PD_LOGGING: - result = diag_ioctl_query_pd_logging(ioarg); + if (copy_from_user((void *)&mode_param, (void __user *)ioarg, + sizeof(mode_param))) + return -EFAULT; + result = diag_ioctl_query_pd_logging(&mode_param); break; } return result; @@ -2332,7 +2403,10 @@ long diagchar_ioctl(struct file *filp, result = diag_ioctl_hdlc_toggle(ioarg); break; case DIAG_IOCTL_QUERY_PD_LOGGING: - result = diag_ioctl_query_pd_logging(ioarg); + if (copy_from_user((void *)&mode_param, (void __user *)ioarg, + sizeof(mode_param))) + return -EFAULT; + result = diag_ioctl_query_pd_logging(&mode_param); break; } return result; @@ -3474,7 +3548,10 @@ static int __init diagchar_init(void) poolsize_usb_apps + 1 + (NUM_PERIPHERALS * 6)); driver->num_clients = max_clients; driver->logging_mode = DIAG_USB_MODE; - driver->pd_logging_mode = 0; + for (i = 0; i < NUM_UPD; i++) { + driver->pd_logging_mode[i] = 0; + driver->pd_session_clear[i] = 0; + } driver->num_pd_session = 0; driver->mask_check = 0; driver->in_busy_pktdata = 0; diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c index 07c90b741fa0..8fb724305c03 100644 --- a/drivers/char/diag/diagfwd.c +++ b/drivers/char/diag/diagfwd.c @@ -38,6 +38,7 @@ #include "diag_masks.h" #include "diag_usb.h" #include "diag_mux.h" +#include "diag_ipc_logging.h" #define STM_CMD_VERSION_OFFSET 4 #define STM_CMD_MASK_OFFSET 5 diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c index ae749725f6db..82a67f1f6f47 100644 --- a/drivers/char/diag/diagfwd_cntl.c +++ b/drivers/char/diag/diagfwd_cntl.c @@ -110,6 +110,8 @@ void diag_notify_md_client(uint8_t peripheral, int data) { int stat = 0; struct siginfo info; + struct pid *pid_struct; + struct task_struct *result; if (peripheral > NUM_PERIPHERALS) return; @@ -122,20 +124,38 @@ void diag_notify_md_client(uint8_t peripheral, int data) info.si_code = SI_QUEUE; info.si_int = (PERIPHERAL_MASK(peripheral) | data); info.si_signo = SIGCONT; - if (driver->md_session_map[peripheral] && - driver->md_session_map[peripheral]->task) { - if (driver->md_session_map[peripheral]-> - md_client_thread_info->task != NULL - && driver->md_session_map[peripheral]->pid == - driver->md_session_map[peripheral]->task->tgid) { + + if (!driver->md_session_map[peripheral] || + driver->md_session_map[peripheral]->pid <= 0) { + pr_err("diag: md_session_map[%d] is invalid\n", peripheral); + mutex_unlock(&driver->md_session_lock); + return; + } + + pid_struct = find_get_pid( + driver->md_session_map[peripheral]->pid); + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, + "md_session_map[%d] pid = %d task = %pK\n", + peripheral, + driver->md_session_map[peripheral]->pid, + driver->md_session_map[peripheral]->task); + + if (pid_struct) { + result = get_pid_task(pid_struct, PIDTYPE_PID); + + if (!result) { DIAG_LOG(DIAG_DEBUG_PERIPHERALS, - "md_session %d pid = %d, md_session %d task tgid = %d\n", - peripheral, - driver->md_session_map[peripheral]->pid, + "diag: md_session_map[%d] with pid = %d Exited..\n", peripheral, - driver->md_session_map[peripheral]->task->tgid); - stat = send_sig_info(info.si_signo, &info, - driver->md_session_map[peripheral]->task); + driver->md_session_map[peripheral]->pid); + mutex_unlock(&driver->md_session_lock); + return; + } + + if (driver->md_session_map[peripheral] && + driver->md_session_map[peripheral]->task == result) { + stat = send_sig_info(info.si_signo, + &info, result); if (stat) pr_err("diag: Err sending signal to memory device client, signal data: 0x%x, stat: %d\n", info.si_int, stat); diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c index 154f181d82f9..e86dc8292bf0 100644 --- a/drivers/char/diag/diagfwd_peripheral.c +++ b/drivers/char/diag/diagfwd_peripheral.c @@ -244,9 +244,14 @@ static void diagfwd_data_process_done(struct diagfwd_info *fwd_info, mutex_lock(&driver->hdlc_disable_mutex); mutex_lock(&fwd_info->data_mutex); + peripheral = GET_PD_CTXT(buf->ctxt); if (peripheral == DIAG_ID_MPSS) peripheral = PERIPHERAL_MODEM; + if (peripheral == DIAG_ID_LPASS) + peripheral = PERIPHERAL_LPASS; + if (peripheral == DIAG_ID_CDSP) + peripheral = PERIPHERAL_CDSP; session_info = diag_md_session_get_peripheral(peripheral); @@ -323,15 +328,19 @@ end: static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info, unsigned char *buf, int len) { - int len_cpd = 0, len_upd_1 = 0; - int ctxt_cpd = 0, ctxt_upd_1 = 0; + int len_cpd = 0; + int len_upd_1 = 0, len_upd_2 = 0; + int ctxt_cpd = 0; + int ctxt_upd_1 = 0, ctxt_upd_2 = 0; int buf_len = 0, processed = 0; unsigned char *temp_buf_main = NULL; unsigned char *temp_buf_cpd = NULL; unsigned char *temp_buf_upd_1 = NULL; + unsigned char *temp_buf_upd_2 = NULL; struct diagfwd_buf_t *temp_ptr_upd = NULL; struct diagfwd_buf_t *temp_ptr_cpd = NULL; int flag_buf_1 = 0, flag_buf_2 = 0; + uint8_t peripheral; if (!fwd_info || !buf || len <= 0) { diag_ws_release(); @@ -349,24 +358,42 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info, diag_ws_release(); return; } + peripheral = fwd_info->peripheral; - if (driver->feature[fwd_info->peripheral].encode_hdlc && - driver->feature[fwd_info->peripheral].untag_header && - driver->peripheral_untag[fwd_info->peripheral]) { + if (driver->feature[peripheral].encode_hdlc && + driver->feature[peripheral].untag_header && + driver->peripheral_untag[peripheral]) { mutex_lock(&driver->diagfwd_untag_mutex); temp_buf_cpd = buf; temp_buf_main = buf; if (fwd_info->buf_1 && fwd_info->buf_1->data_raw == buf) { flag_buf_1 = 1; - if (fwd_info->type == TYPE_DATA) + temp_ptr_cpd = fwd_info->buf_1; + if (fwd_info->type == TYPE_DATA) { temp_buf_upd_1 = fwd_info->buf_upd_1_a->data_raw; - } else { + if (peripheral == + PERIPHERAL_LPASS) + temp_buf_upd_2 = + fwd_info->buf_upd_2_a->data_raw; + } + } else if (fwd_info->buf_2 && + fwd_info->buf_2->data_raw == buf) { flag_buf_2 = 1; + temp_ptr_cpd = fwd_info->buf_2; if (fwd_info->type == TYPE_DATA) temp_buf_upd_1 = fwd_info->buf_upd_1_b->data_raw; + if (peripheral == + PERIPHERAL_LPASS) + temp_buf_upd_2 = + fwd_info->buf_upd_2_b->data_raw; + } else { + pr_err("diag: In %s, no match for buffer %pK, peripheral %d, type: %d\n", + __func__, buf, peripheral, + fwd_info->type); + goto end; } while (processed < len) { buf_len = @@ -390,31 +417,97 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info, temp_buf_upd_1 += buf_len; } break; + case DIAG_ID_LPASS: + ctxt_cpd = DIAG_ID_LPASS; + len_cpd += buf_len; + if (temp_buf_cpd) { + memcpy(temp_buf_cpd, + (temp_buf_main + 4), buf_len); + temp_buf_cpd += buf_len; + } + break; + case DIAG_ID_AUDIO: + ctxt_upd_1 = UPD_AUDIO; + len_upd_1 += buf_len; + if (temp_buf_upd_1) { + memcpy(temp_buf_upd_1, + (temp_buf_main + 4), buf_len); + temp_buf_upd_1 += buf_len; + } + break; + case DIAG_ID_SENSORS: + ctxt_upd_2 = UPD_SENSORS; + len_upd_2 += buf_len; + if (temp_buf_upd_2) { + memcpy(temp_buf_upd_2, + (temp_buf_main + 4), buf_len); + temp_buf_upd_2 += buf_len; + } + break; + case DIAG_ID_CDSP: + ctxt_cpd = DIAG_ID_CDSP; + len_cpd += buf_len; + if (temp_buf_cpd) { + memcpy(temp_buf_cpd, + (temp_buf_main + 4), buf_len); + temp_buf_cpd += buf_len; + } + break; + default: + goto end; } len = len - 4; temp_buf_main += (buf_len + 4); processed += buf_len; } - if (fwd_info->type == TYPE_DATA && len_upd_1) { + if (peripheral == PERIPHERAL_LPASS && + fwd_info->type == TYPE_DATA && len_upd_2) { + if (flag_buf_1) { + driver->upd_len_2_a = len_upd_2; + temp_ptr_upd = fwd_info->buf_upd_2_a; + } else { + driver->upd_len_2_b = len_upd_2; + temp_ptr_upd = fwd_info->buf_upd_2_b; + } + temp_ptr_upd->ctxt &= 0x00FFFFFF; + temp_ptr_upd->ctxt |= + (SET_PD_CTXT(ctxt_upd_2)); + atomic_set(&temp_ptr_upd->in_busy, 1); + diagfwd_data_process_done(fwd_info, + temp_ptr_upd, len_upd_2); + } else { if (flag_buf_1) + driver->upd_len_2_a = 0; + if (flag_buf_2) + driver->upd_len_2_b = 0; + } + if (fwd_info->type == TYPE_DATA && len_upd_1) { + if (flag_buf_1) { + driver->upd_len_1_a[peripheral] = + len_upd_1; temp_ptr_upd = fwd_info->buf_upd_1_a; - else + } else { + driver->upd_len_1_b[peripheral] = + len_upd_1; temp_ptr_upd = fwd_info->buf_upd_1_b; + } temp_ptr_upd->ctxt &= 0x00FFFFFF; temp_ptr_upd->ctxt |= (SET_PD_CTXT(ctxt_upd_1)); atomic_set(&temp_ptr_upd->in_busy, 1); diagfwd_data_process_done(fwd_info, temp_ptr_upd, len_upd_1); + } else { + if (flag_buf_1) + driver->upd_len_1_a[peripheral] = 0; + if (flag_buf_2) + driver->upd_len_1_b[peripheral] = 0; } if (len_cpd) { - if (flag_buf_1) { - driver->cpd_len_1 = len_cpd; - temp_ptr_cpd = fwd_info->buf_1; - } else { - driver->cpd_len_2 = len_cpd; - temp_ptr_cpd = fwd_info->buf_2; - } + if (flag_buf_1) + driver->cpd_len_1[peripheral] = len_cpd; + else + driver->cpd_len_2[peripheral] = len_cpd; temp_ptr_cpd->ctxt &= 0x00FFFFFF; temp_ptr_cpd->ctxt |= (SET_PD_CTXT(ctxt_cpd)); @@ -422,14 +515,24 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info, temp_ptr_cpd, len_cpd); } else { if (flag_buf_1) - driver->cpd_len_1 = 0; + driver->cpd_len_1[peripheral] = 0; if (flag_buf_2) - driver->cpd_len_2 = 0; + driver->cpd_len_2[peripheral] = 0; } mutex_unlock(&driver->diagfwd_untag_mutex); + return; } else { diagfwd_data_read_done(fwd_info, buf, len); + return; + } +end: + diag_ws_release(); + mutex_unlock(&driver->diagfwd_untag_mutex); + if (temp_ptr_cpd) { + diagfwd_write_done(fwd_info->peripheral, fwd_info->type, + GET_BUF_NUM(temp_ptr_cpd->ctxt)); } + diagfwd_queue_read(fwd_info); } static void diagfwd_data_read_done(struct diagfwd_info *fwd_info, @@ -991,6 +1094,21 @@ static void __diag_fwd_open(struct diagfwd_info *fwd_info) if (!fwd_info->inited) return; + /* + * Logging mode here is reflecting previous mode + * status and will be updated to new mode later. + * + * Keeping the buffers busy for Memory Device Mode. + */ + + if ((driver->logging_mode != DIAG_USB_MODE) || + driver->usb_connected) { + if (fwd_info->buf_1) + atomic_set(&fwd_info->buf_1->in_busy, 0); + if (fwd_info->buf_2) + atomic_set(&fwd_info->buf_2->in_busy, 0); + } + if (fwd_info->p_ops && fwd_info->p_ops->open) fwd_info->p_ops->open(fwd_info->ctxt); @@ -1151,18 +1269,78 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt) return; fwd_info = &peripheral_info[type][peripheral]; - if (ctxt == 1 && fwd_info->buf_1) + + if (ctxt == 1 && fwd_info->buf_1) { + /* Buffer 1 for core PD is freed */ atomic_set(&fwd_info->buf_1->in_busy, 0); - else if (ctxt == 2 && fwd_info->buf_2) + driver->cpd_len_1[peripheral] = 0; + } else if (ctxt == 2 && fwd_info->buf_2) { + /* Buffer 2 for core PD is freed */ atomic_set(&fwd_info->buf_2->in_busy, 0); - else if (ctxt == 3 && fwd_info->buf_upd_1_a) { + driver->cpd_len_2[peripheral] = 0; + } else if (ctxt == 3 && fwd_info->buf_upd_1_a) { + /* Buffer 1 for user pd 1 is freed */ atomic_set(&fwd_info->buf_upd_1_a->in_busy, 0); - if (driver->cpd_len_1 == 0) - atomic_set(&fwd_info->buf_1->in_busy, 0); + + if (peripheral == PERIPHERAL_LPASS) { + /* if not data in cpd and other user pd + * free the core pd buffer for LPASS + */ + if (!driver->cpd_len_1[PERIPHERAL_LPASS] && + !driver->upd_len_2_a) + atomic_set(&fwd_info->buf_1->in_busy, 0); + } else { + /* if not data in cpd + * free the core pd buffer for MPSS + */ + if (!driver->cpd_len_1[PERIPHERAL_MODEM]) + atomic_set(&fwd_info->buf_1->in_busy, 0); + } + driver->upd_len_1_a[peripheral] = 0; + } else if (ctxt == 4 && fwd_info->buf_upd_1_b) { + /* Buffer 2 for user pd 1 is freed */ atomic_set(&fwd_info->buf_upd_1_b->in_busy, 0); - if (driver->cpd_len_2 == 0) + if (peripheral == PERIPHERAL_LPASS) { + /* if not data in cpd and other user pd + * free the core pd buffer for LPASS + */ + if (!driver->cpd_len_2[peripheral] && + !driver->upd_len_2_b) + atomic_set(&fwd_info->buf_2->in_busy, 0); + } else { + /* if not data in cpd + * free the core pd buffer for MPSS + */ + if (!driver->cpd_len_2[PERIPHERAL_MODEM]) + atomic_set(&fwd_info->buf_2->in_busy, 0); + } + driver->upd_len_1_b[peripheral] = 0; + + } else if (ctxt == 5 && fwd_info->buf_upd_2_a) { + /* Buffer 1 for user pd 2 is freed */ + atomic_set(&fwd_info->buf_upd_2_a->in_busy, 0); + /* if not data in cpd and other user pd + * free the core pd buffer for LPASS + */ + if (!driver->cpd_len_1[PERIPHERAL_LPASS] && + !driver->upd_len_1_a[PERIPHERAL_LPASS]) + atomic_set(&fwd_info->buf_1->in_busy, 0); + + driver->upd_len_2_a = 0; + + } else if (ctxt == 6 && fwd_info->buf_upd_2_b) { + /* Buffer 2 for user pd 2 is freed */ + atomic_set(&fwd_info->buf_upd_2_b->in_busy, 0); + /* if not data in cpd and other user pd + * free the core pd buffer for LPASS + */ + if (!driver->cpd_len_2[PERIPHERAL_LPASS] && + !driver->upd_len_1_b[PERIPHERAL_LPASS]) atomic_set(&fwd_info->buf_2->in_busy, 0); + + driver->upd_len_2_b = 0; + } else pr_err("diag: In %s, invalid ctxt %d\n", __func__, ctxt); @@ -1295,8 +1473,8 @@ static void diagfwd_queue_read(struct diagfwd_info *fwd_info) void diagfwd_buffers_init(struct diagfwd_info *fwd_info) { - unsigned char *temp_buf; - uint8_t peripheral; + struct diagfwd_buf_t *temp_fwd_buf; + unsigned char *temp_char_buf; if (!fwd_info) return; @@ -1309,20 +1487,19 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info) mutex_lock(&fwd_info->buf_mutex); - peripheral = fwd_info->peripheral; - if (!fwd_info->buf_1) { fwd_info->buf_1 = kzalloc(sizeof(struct diagfwd_buf_t), GFP_KERNEL); - if (!fwd_info->buf_1) + if (ZERO_OR_NULL_PTR(fwd_info->buf_1)) goto err; kmemleak_not_leak(fwd_info->buf_1); } + if (!fwd_info->buf_1->data) { fwd_info->buf_1->data = kzalloc(PERIPHERAL_BUF_SZ + APF_DIAG_PADDING, GFP_KERNEL); - if (!fwd_info->buf_1->data) + if (ZERO_OR_NULL_PTR(fwd_info->buf_1->data)) goto err; fwd_info->buf_1->len = PERIPHERAL_BUF_SZ; kmemleak_not_leak(fwd_info->buf_1->data); @@ -1334,7 +1511,7 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info) if (!fwd_info->buf_2) { fwd_info->buf_2 = kzalloc(sizeof(struct diagfwd_buf_t), GFP_KERNEL); - if (!fwd_info->buf_2) + if (ZERO_OR_NULL_PTR(fwd_info->buf_2)) goto err; kmemleak_not_leak(fwd_info->buf_2); } @@ -1343,7 +1520,7 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info) fwd_info->buf_2->data = kzalloc(PERIPHERAL_BUF_SZ + APF_DIAG_PADDING, GFP_KERNEL); - if (!fwd_info->buf_2->data) + if (ZERO_OR_NULL_PTR(fwd_info->buf_2->data)) goto err; fwd_info->buf_2->len = PERIPHERAL_BUF_SZ; kmemleak_not_leak(fwd_info->buf_2->data); @@ -1352,53 +1529,117 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info) fwd_info->type, 2); } - if (driver->feature[peripheral].untag_header && - driver->peripheral_untag[peripheral]) { + if (driver->feature[fwd_info->peripheral].untag_header) { if (!fwd_info->buf_upd_1_a) { fwd_info->buf_upd_1_a = kzalloc(sizeof(struct diagfwd_buf_t), GFP_KERNEL); - if (!fwd_info->buf_upd_1_a) + if (ZERO_OR_NULL_PTR(fwd_info->buf_upd_1_a)) goto err; kmemleak_not_leak(fwd_info->buf_upd_1_a); } - if (!fwd_info->buf_upd_1_a->data) { + if (fwd_info->buf_upd_1_a && + !fwd_info->buf_upd_1_a->data) { fwd_info->buf_upd_1_a->data = kzalloc(PERIPHERAL_BUF_SZ + APF_DIAG_PADDING, GFP_KERNEL); - if (!fwd_info->buf_upd_1_a->data) + temp_char_buf = fwd_info->buf_upd_1_a->data; + if (ZERO_OR_NULL_PTR(temp_char_buf)) goto err; fwd_info->buf_upd_1_a->len = PERIPHERAL_BUF_SZ; - kmemleak_not_leak(fwd_info->buf_upd_1_a->data); + kmemleak_not_leak(temp_char_buf); fwd_info->buf_upd_1_a->ctxt = SET_BUF_CTXT( fwd_info->peripheral, fwd_info->type, 3); } + if (!fwd_info->buf_upd_1_b) { - fwd_info->buf_upd_1_b = + fwd_info->buf_upd_1_b = kzalloc(sizeof(struct diagfwd_buf_t), GFP_KERNEL); - if (!fwd_info->buf_upd_1_b) - goto err; - kmemleak_not_leak(fwd_info->buf_upd_1_b); + if (ZERO_OR_NULL_PTR(fwd_info->buf_upd_1_b)) + goto err; + kmemleak_not_leak(fwd_info->buf_upd_1_b); } - if (!fwd_info->buf_upd_1_b->data) { + if (fwd_info->buf_upd_1_b && + !fwd_info->buf_upd_1_b->data) { fwd_info->buf_upd_1_b->data = kzalloc(PERIPHERAL_BUF_SZ + APF_DIAG_PADDING, GFP_KERNEL); - if (!fwd_info->buf_upd_1_b->data) + temp_char_buf = + fwd_info->buf_upd_1_b->data; + if (ZERO_OR_NULL_PTR(temp_char_buf)) goto err; fwd_info->buf_upd_1_b->len = PERIPHERAL_BUF_SZ; - kmemleak_not_leak(fwd_info->buf_upd_1_b->data); + kmemleak_not_leak(temp_char_buf); fwd_info->buf_upd_1_b->ctxt = SET_BUF_CTXT( fwd_info->peripheral, fwd_info->type, 4); } + if (fwd_info->peripheral == + PERIPHERAL_LPASS) { + if (!fwd_info->buf_upd_2_a) { + fwd_info->buf_upd_2_a = + kzalloc(sizeof(struct diagfwd_buf_t), + GFP_KERNEL); + temp_fwd_buf = + fwd_info->buf_upd_2_a; + if (ZERO_OR_NULL_PTR(temp_fwd_buf)) + goto err; + kmemleak_not_leak(temp_fwd_buf); + } + + if (!fwd_info->buf_upd_2_a->data) { + fwd_info->buf_upd_2_a->data = + kzalloc(PERIPHERAL_BUF_SZ + + APF_DIAG_PADDING, + GFP_KERNEL); + temp_char_buf = + fwd_info->buf_upd_2_a->data; + if (ZERO_OR_NULL_PTR(temp_char_buf)) + goto err; + fwd_info->buf_upd_2_a->len = + PERIPHERAL_BUF_SZ; + kmemleak_not_leak(temp_char_buf); + fwd_info->buf_upd_2_a->ctxt = + SET_BUF_CTXT( + fwd_info->peripheral, + fwd_info->type, 5); + } + if (!fwd_info->buf_upd_2_b) { + fwd_info->buf_upd_2_b = + kzalloc(sizeof(struct diagfwd_buf_t), + GFP_KERNEL); + temp_fwd_buf = + fwd_info->buf_upd_2_b; + if (ZERO_OR_NULL_PTR(temp_fwd_buf)) + goto err; + kmemleak_not_leak(temp_fwd_buf); + } + + if (!fwd_info->buf_upd_2_b->data) { + fwd_info->buf_upd_2_b->data = + kzalloc(PERIPHERAL_BUF_SZ + + APF_DIAG_PADDING, + GFP_KERNEL); + temp_char_buf = + fwd_info->buf_upd_2_b->data; + if (ZERO_OR_NULL_PTR(temp_char_buf)) + goto err; + fwd_info->buf_upd_2_b->len = + PERIPHERAL_BUF_SZ; + kmemleak_not_leak(temp_char_buf); + fwd_info->buf_upd_2_b->ctxt = + SET_BUF_CTXT( + fwd_info->peripheral, + fwd_info->type, 6); + } + } } if (driver->supports_apps_hdlc_encoding) { @@ -1408,52 +1649,87 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info) kzalloc(PERIPHERAL_BUF_SZ + APF_DIAG_PADDING, GFP_KERNEL); - if (!fwd_info->buf_1->data_raw) + temp_char_buf = + fwd_info->buf_1->data_raw; + if (ZERO_OR_NULL_PTR(temp_char_buf)) goto err; fwd_info->buf_1->len_raw = PERIPHERAL_BUF_SZ; - kmemleak_not_leak(fwd_info->buf_1->data_raw); + kmemleak_not_leak(temp_char_buf); } + if (!fwd_info->buf_2->data_raw) { fwd_info->buf_2->data_raw = kzalloc(PERIPHERAL_BUF_SZ + APF_DIAG_PADDING, GFP_KERNEL); - if (!fwd_info->buf_2->data_raw) + temp_char_buf = + fwd_info->buf_2->data_raw; + if (ZERO_OR_NULL_PTR(temp_char_buf)) goto err; fwd_info->buf_2->len_raw = PERIPHERAL_BUF_SZ; - kmemleak_not_leak(fwd_info->buf_2->data_raw); + kmemleak_not_leak(temp_char_buf); } if (driver->feature[fwd_info->peripheral]. - untag_header && - driver->peripheral_untag[peripheral]) { - if (!fwd_info->buf_upd_1_a->data_raw) { + untag_header) { + if (fwd_info->buf_upd_1_a && + !fwd_info->buf_upd_1_a->data_raw) { fwd_info->buf_upd_1_a->data_raw = kzalloc(PERIPHERAL_BUF_SZ + APF_DIAG_PADDING, GFP_KERNEL); - if (!fwd_info->buf_upd_1_a->data_raw) + temp_char_buf = + fwd_info->buf_upd_1_a->data_raw; + if (ZERO_OR_NULL_PTR(temp_char_buf)) goto err; fwd_info->buf_upd_1_a->len_raw = PERIPHERAL_BUF_SZ; - temp_buf = - fwd_info->buf_upd_1_a->data_raw; - kmemleak_not_leak(temp_buf); + kmemleak_not_leak(temp_char_buf); } - if (!fwd_info->buf_upd_1_b->data_raw) { + + if (fwd_info->buf_upd_1_b && + !fwd_info->buf_upd_1_b->data_raw) { fwd_info->buf_upd_1_b->data_raw = kzalloc(PERIPHERAL_BUF_SZ + APF_DIAG_PADDING, GFP_KERNEL); - if (!fwd_info->buf_upd_1_b->data_raw) + temp_char_buf = + fwd_info->buf_upd_1_b->data_raw; + if (ZERO_OR_NULL_PTR(temp_char_buf)) goto err; fwd_info->buf_upd_1_b->len_raw = PERIPHERAL_BUF_SZ; - temp_buf = - fwd_info->buf_upd_1_b->data_raw; - kmemleak_not_leak(temp_buf); + kmemleak_not_leak(temp_char_buf); + } + if (fwd_info->peripheral == PERIPHERAL_LPASS + && !fwd_info->buf_upd_2_a->data_raw) { + fwd_info->buf_upd_2_a->data_raw = + kzalloc(PERIPHERAL_BUF_SZ + + APF_DIAG_PADDING, + GFP_KERNEL); + temp_char_buf = + fwd_info->buf_upd_2_a->data_raw; + if (ZERO_OR_NULL_PTR(temp_char_buf)) + goto err; + fwd_info->buf_upd_2_a->len_raw = + PERIPHERAL_BUF_SZ; + kmemleak_not_leak(temp_char_buf); + } + if (fwd_info->peripheral == PERIPHERAL_LPASS + && !fwd_info->buf_upd_2_b->data_raw) { + fwd_info->buf_upd_2_b->data_raw = + kzalloc(PERIPHERAL_BUF_SZ + + APF_DIAG_PADDING, + GFP_KERNEL); + temp_char_buf = + fwd_info->buf_upd_2_b->data_raw; + if (ZERO_OR_NULL_PTR(temp_char_buf)) + goto err; + fwd_info->buf_upd_2_b->len_raw = + PERIPHERAL_BUF_SZ; + kmemleak_not_leak(temp_char_buf); } } } @@ -1466,10 +1742,12 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info) fwd_info->buf_1->data_raw = kzalloc(PERIPHERAL_BUF_SZ + APF_DIAG_PADDING, GFP_KERNEL); - if (!fwd_info->buf_1->data_raw) + temp_char_buf = + fwd_info->buf_1->data_raw; + if (ZERO_OR_NULL_PTR(temp_char_buf)) goto err; fwd_info->buf_1->len_raw = PERIPHERAL_BUF_SZ; - kmemleak_not_leak(fwd_info->buf_1->data_raw); + kmemleak_not_leak(temp_char_buf); } } @@ -1506,6 +1784,38 @@ static void diagfwd_buffers_exit(struct diagfwd_info *fwd_info) kfree(fwd_info->buf_2); fwd_info->buf_2 = NULL; } + if (fwd_info->buf_upd_1_a) { + kfree(fwd_info->buf_upd_1_a->data); + fwd_info->buf_upd_1_a->data = NULL; + kfree(fwd_info->buf_upd_1_a->data_raw); + fwd_info->buf_upd_1_a->data_raw = NULL; + kfree(fwd_info->buf_upd_1_a); + fwd_info->buf_upd_1_a = NULL; + } + if (fwd_info->buf_upd_1_b) { + kfree(fwd_info->buf_upd_1_b->data); + fwd_info->buf_upd_1_b->data = NULL; + kfree(fwd_info->buf_upd_1_b->data_raw); + fwd_info->buf_upd_1_b->data_raw = NULL; + kfree(fwd_info->buf_upd_1_b); + fwd_info->buf_upd_1_b = NULL; + } + if (fwd_info->buf_upd_2_a) { + kfree(fwd_info->buf_upd_2_a->data); + fwd_info->buf_upd_2_a->data = NULL; + kfree(fwd_info->buf_upd_2_a->data_raw); + fwd_info->buf_upd_2_a->data_raw = NULL; + kfree(fwd_info->buf_upd_2_a); + fwd_info->buf_upd_2_a = NULL; + } + if (fwd_info->buf_upd_2_b) { + kfree(fwd_info->buf_upd_2_b->data); + fwd_info->buf_upd_2_b->data = NULL; + kfree(fwd_info->buf_upd_2_b->data_raw); + fwd_info->buf_upd_2_b->data_raw = NULL; + kfree(fwd_info->buf_upd_2_b); + fwd_info->buf_upd_2_b = NULL; + } mutex_unlock(&fwd_info->buf_mutex); } diff --git a/drivers/char/diag/diagfwd_peripheral.h b/drivers/char/diag/diagfwd_peripheral.h index f483da81cc96..760f139ff428 100644 --- a/drivers/char/diag/diagfwd_peripheral.h +++ b/drivers/char/diag/diagfwd_peripheral.h @@ -80,6 +80,8 @@ struct diagfwd_info { struct diagfwd_buf_t *buf_2; struct diagfwd_buf_t *buf_upd_1_a; struct diagfwd_buf_t *buf_upd_1_b; + struct diagfwd_buf_t *buf_upd_2_a; + struct diagfwd_buf_t *buf_upd_2_b; struct diagfwd_buf_t *buf_ptr[NUM_WRITE_BUFFERS]; struct diag_peripheral_ops *p_ops; struct diag_channel_ops *c_ops; diff --git a/drivers/char/msm_smd_pkt.c b/drivers/char/msm_smd_pkt.c new file mode 100644 index 000000000000..a61d273bfb65 --- /dev/null +++ b/drivers/char/msm_smd_pkt.c @@ -0,0 +1,1397 @@ +/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. + * + * This 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. + * + */ +/* + * SMD Packet Driver -- Provides a binary SMD non-muxed packet port + * interface. + */ + +#include <linux/slab.h> +#include <linux/cdev.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/device.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/uaccess.h> +#include <linux/workqueue.h> +#include <linux/platform_device.h> +#include <linux/completion.h> +#include <linux/msm_smd_pkt.h> +#include <linux/poll.h> +#include <soc/qcom/smd.h> +#include <soc/qcom/smsm.h> +#include <soc/qcom/subsystem_restart.h> +#include <asm/ioctls.h> +#include <linux/pm.h> +#include <linux/of.h> +#include <linux/ipc_logging.h> + +#define MODULE_NAME "msm_smdpkt" +#define DEVICE_NAME "smdpkt" +#define WAKEUPSOURCE_TIMEOUT (2000) /* two seconds */ + +struct smd_pkt_dev { + struct list_head dev_list; + char dev_name[SMD_MAX_CH_NAME_LEN]; + char ch_name[SMD_MAX_CH_NAME_LEN]; + uint32_t edge; + + struct cdev cdev; + struct device *devicep; + void *pil; + + struct smd_channel *ch; + struct mutex ch_lock; + struct mutex rx_lock; + struct mutex tx_lock; + wait_queue_head_t ch_read_wait_queue; + wait_queue_head_t ch_write_wait_queue; + wait_queue_head_t ch_opened_wait_queue; + + int i; + int ref_cnt; + + int blocking_write; + int is_open; + int poll_mode; + unsigned ch_size; + uint open_modem_wait; + + int has_reset; + int do_reset_notification; + struct completion ch_allocated; + struct wakeup_source pa_ws; /* Packet Arrival Wakeup Source */ + struct work_struct packet_arrival_work; + spinlock_t pa_spinlock; + int ws_locked; +}; + + +struct smd_pkt_driver { + struct list_head list; + int ref_cnt; + char pdriver_name[SMD_MAX_CH_NAME_LEN]; + struct platform_driver driver; +}; + +static DEFINE_MUTEX(smd_pkt_driver_lock_lha1); +static LIST_HEAD(smd_pkt_driver_list); + +struct class *smd_pkt_classp; +static dev_t smd_pkt_number; +static struct delayed_work loopback_work; +static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp); +static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp); +static uint32_t is_modem_smsm_inited(void); + +static DEFINE_MUTEX(smd_pkt_dev_lock_lha1); +static LIST_HEAD(smd_pkt_dev_list); +static int num_smd_pkt_ports; + +#define SMD_PKT_IPC_LOG_PAGE_CNT 2 +static void *smd_pkt_ilctxt; + +static int msm_smd_pkt_debug_mask; +module_param_named(debug_mask, msm_smd_pkt_debug_mask, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +enum { + SMD_PKT_STATUS = 1U << 0, + SMD_PKT_READ = 1U << 1, + SMD_PKT_WRITE = 1U << 2, + SMD_PKT_POLL = 1U << 5, +}; + +#define DEBUG + +#ifdef DEBUG + +#define SMD_PKT_LOG_STRING(x...) \ +do { \ + if (smd_pkt_ilctxt) \ + ipc_log_string(smd_pkt_ilctxt, "<SMD_PKT>: "x); \ +} while (0) + +#define D_STATUS(x...) \ +do { \ + if (msm_smd_pkt_debug_mask & SMD_PKT_STATUS) \ + pr_info("Status: "x); \ + SMD_PKT_LOG_STRING(x); \ +} while (0) + +#define D_READ(x...) \ +do { \ + if (msm_smd_pkt_debug_mask & SMD_PKT_READ) \ + pr_info("Read: "x); \ + SMD_PKT_LOG_STRING(x); \ +} while (0) + +#define D_WRITE(x...) \ +do { \ + if (msm_smd_pkt_debug_mask & SMD_PKT_WRITE) \ + pr_info("Write: "x); \ + SMD_PKT_LOG_STRING(x); \ +} while (0) + +#define D_POLL(x...) \ +do { \ + if (msm_smd_pkt_debug_mask & SMD_PKT_POLL) \ + pr_info("Poll: "x); \ + SMD_PKT_LOG_STRING(x); \ +} while (0) + +#define E_SMD_PKT_SSR(x) \ +do { \ + if (x->do_reset_notification) \ + pr_err("%s notifying reset for smd_pkt_dev id:%d\n", \ + __func__, x->i); \ +} while (0) +#else +#define D_STATUS(x...) do {} while (0) +#define D_READ(x...) do {} while (0) +#define D_WRITE(x...) do {} while (0) +#define D_POLL(x...) do {} while (0) +#define E_SMD_PKT_SSR(x) do {} while (0) +#endif + +static ssize_t open_timeout_store(struct device *d, + struct device_attribute *attr, + const char *buf, + size_t n) +{ + struct smd_pkt_dev *smd_pkt_devp; + unsigned long tmp; + + mutex_lock(&smd_pkt_dev_lock_lha1); + list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) { + if (smd_pkt_devp->devicep == d) { + if (!kstrtoul(buf, 10, &tmp)) { + smd_pkt_devp->open_modem_wait = tmp; + mutex_unlock(&smd_pkt_dev_lock_lha1); + return n; + } + mutex_unlock(&smd_pkt_dev_lock_lha1); + pr_err("%s: unable to convert: %s to an int\n", + __func__, buf); + return -EINVAL; + } + } + mutex_unlock(&smd_pkt_dev_lock_lha1); + + pr_err("%s: unable to match device to valid smd_pkt port\n", __func__); + return -EINVAL; +} + +static ssize_t open_timeout_show(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct smd_pkt_dev *smd_pkt_devp; + + mutex_lock(&smd_pkt_dev_lock_lha1); + list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) { + if (smd_pkt_devp->devicep == d) { + mutex_unlock(&smd_pkt_dev_lock_lha1); + return snprintf(buf, PAGE_SIZE, "%d\n", + smd_pkt_devp->open_modem_wait); + } + } + mutex_unlock(&smd_pkt_dev_lock_lha1); + pr_err("%s: unable to match device to valid smd_pkt port\n", __func__); + return -EINVAL; + +} + +static DEVICE_ATTR(open_timeout, 0664, open_timeout_show, open_timeout_store); + +/** + * loopback_edge_store() - Set the edge type for loopback device + * @d: Linux device structure + * @attr: Device attribute structure + * @buf: Input string + * @n: Length of the input string + * + * This function is used to set the loopback device edge runtime + * by writing to the loopback_edge node. + */ +static ssize_t loopback_edge_store(struct device *d, + struct device_attribute *attr, + const char *buf, + size_t n) +{ + struct smd_pkt_dev *smd_pkt_devp; + unsigned long tmp; + + mutex_lock(&smd_pkt_dev_lock_lha1); + list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) { + if (smd_pkt_devp->devicep == d) { + if (!kstrtoul(buf, 10, &tmp)) { + smd_pkt_devp->edge = tmp; + mutex_unlock(&smd_pkt_dev_lock_lha1); + return n; + } + mutex_unlock(&smd_pkt_dev_lock_lha1); + pr_err("%s: unable to convert: %s to an int\n", + __func__, buf); + return -EINVAL; + } + } + mutex_unlock(&smd_pkt_dev_lock_lha1); + pr_err("%s: unable to match device to valid smd_pkt port\n", __func__); + return -EINVAL; +} + +/** + * loopback_edge_show() - Get the edge type for loopback device + * @d: Linux device structure + * @attr: Device attribute structure + * @buf: Output buffer + * + * This function is used to get the loopback device edge runtime + * by reading the loopback_edge node. + */ +static ssize_t loopback_edge_show(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct smd_pkt_dev *smd_pkt_devp; + + mutex_lock(&smd_pkt_dev_lock_lha1); + list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) { + if (smd_pkt_devp->devicep == d) { + mutex_unlock(&smd_pkt_dev_lock_lha1); + return snprintf(buf, PAGE_SIZE, "%d\n", + smd_pkt_devp->edge); + } + } + mutex_unlock(&smd_pkt_dev_lock_lha1); + pr_err("%s: unable to match device to valid smd_pkt port\n", __func__); + return -EINVAL; + +} + +static DEVICE_ATTR(loopback_edge, 0664, loopback_edge_show, + loopback_edge_store); + +static int notify_reset(struct smd_pkt_dev *smd_pkt_devp) +{ + smd_pkt_devp->do_reset_notification = 0; + + return -ENETRESET; +} + +static void clean_and_signal(struct smd_pkt_dev *smd_pkt_devp) +{ + smd_pkt_devp->do_reset_notification = 1; + smd_pkt_devp->has_reset = 1; + + smd_pkt_devp->is_open = 0; + + wake_up(&smd_pkt_devp->ch_read_wait_queue); + wake_up(&smd_pkt_devp->ch_write_wait_queue); + wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue); + D_STATUS("%s smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i); +} + +static void loopback_probe_worker(struct work_struct *work) +{ + + /* Wait for the modem SMSM to be inited for the SMD + ** Loopback channel to be allocated at the modem. Since + ** the wait need to be done atmost once, using msleep + ** doesn't degrade the performance. + */ + if (!is_modem_smsm_inited()) + schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000)); + else + smsm_change_state(SMSM_APPS_STATE, + 0, SMSM_SMD_LOOPBACK); + +} + +static void packet_arrival_worker(struct work_struct *work) +{ + struct smd_pkt_dev *smd_pkt_devp; + unsigned long flags; + + smd_pkt_devp = container_of(work, struct smd_pkt_dev, + packet_arrival_work); + mutex_lock(&smd_pkt_devp->ch_lock); + spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags); + if (smd_pkt_devp->ch && smd_pkt_devp->ws_locked) { + D_READ("%s locking smd_pkt_dev id:%d wakeup source\n", + __func__, smd_pkt_devp->i); + /* + * Keep system awake long enough to allow userspace client + * to process the packet. + */ + __pm_wakeup_event(&smd_pkt_devp->pa_ws, WAKEUPSOURCE_TIMEOUT); + } + spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags); + mutex_unlock(&smd_pkt_devp->ch_lock); +} + +static long smd_pkt_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret; + struct smd_pkt_dev *smd_pkt_devp; + uint32_t val; + + smd_pkt_devp = file->private_data; + if (!smd_pkt_devp) + return -EINVAL; + + mutex_lock(&smd_pkt_devp->ch_lock); + switch (cmd) { + case TIOCMGET: + D_STATUS("%s TIOCMGET command on smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + ret = smd_tiocmget(smd_pkt_devp->ch); + break; + case TIOCMSET: + ret = get_user(val, (uint32_t *)arg); + if (ret) { + pr_err("Error getting TIOCMSET value\n"); + mutex_unlock(&smd_pkt_devp->ch_lock); + return ret; + } + D_STATUS("%s TIOCSET command on smd_pkt_dev id:%d arg[0x%x]\n", + __func__, smd_pkt_devp->i, val); + ret = smd_tiocmset(smd_pkt_devp->ch, val, ~val); + break; + case SMD_PKT_IOCTL_BLOCKING_WRITE: + ret = get_user(smd_pkt_devp->blocking_write, (int *)arg); + break; + default: + pr_err_ratelimited("%s: Unrecognized ioctl command %d\n", + __func__, cmd); + ret = -ENOIOCTLCMD; + } + mutex_unlock(&smd_pkt_devp->ch_lock); + + return ret; +} + +ssize_t smd_pkt_read(struct file *file, + char __user *_buf, + size_t count, + loff_t *ppos) +{ + int r; + int bytes_read; + int pkt_size; + struct smd_pkt_dev *smd_pkt_devp; + unsigned long flags; + void *buf; + + smd_pkt_devp = file->private_data; + + if (!smd_pkt_devp) { + pr_err_ratelimited("%s on NULL smd_pkt_dev\n", __func__); + return -EINVAL; + } + + if (!smd_pkt_devp->ch) { + pr_err_ratelimited("%s on a closed smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return -EINVAL; + } + + if (smd_pkt_devp->do_reset_notification) { + /* notify client that a reset occurred */ + E_SMD_PKT_SSR(smd_pkt_devp); + return notify_reset(smd_pkt_devp); + } + D_READ("Begin %s on smd_pkt_dev id:%d buffer_size %zu\n", + __func__, smd_pkt_devp->i, count); + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + +wait_for_packet: + r = wait_event_interruptible(smd_pkt_devp->ch_read_wait_queue, + !smd_pkt_devp->ch || + (smd_cur_packet_size(smd_pkt_devp->ch) > 0 + && smd_read_avail(smd_pkt_devp->ch)) || + smd_pkt_devp->has_reset); + + mutex_lock(&smd_pkt_devp->rx_lock); + if (smd_pkt_devp->has_reset) { + mutex_unlock(&smd_pkt_devp->rx_lock); + E_SMD_PKT_SSR(smd_pkt_devp); + kfree(buf); + return notify_reset(smd_pkt_devp); + } + + if (!smd_pkt_devp->ch) { + mutex_unlock(&smd_pkt_devp->rx_lock); + pr_err_ratelimited("%s on a closed smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + kfree(buf); + return -EINVAL; + } + + if (r < 0) { + mutex_unlock(&smd_pkt_devp->rx_lock); + /* qualify error message */ + if (r != -ERESTARTSYS) { + /* we get this anytime a signal comes in */ + pr_err_ratelimited("%s: wait_event_interruptible on smd_pkt_dev id:%d ret %i\n", + __func__, smd_pkt_devp->i, r); + } + kfree(buf); + return r; + } + + /* Here we have a whole packet waiting for us */ + pkt_size = smd_cur_packet_size(smd_pkt_devp->ch); + + if (!pkt_size) { + pr_err_ratelimited("%s: No data on smd_pkt_dev id:%d, False wakeup\n", + __func__, smd_pkt_devp->i); + mutex_unlock(&smd_pkt_devp->rx_lock); + goto wait_for_packet; + } + + if (pkt_size < 0) { + pr_err_ratelimited("%s: Error %d obtaining packet size for Channel %s", + __func__, pkt_size, smd_pkt_devp->ch_name); + kfree(buf); + return pkt_size; + } + + if ((uint32_t)pkt_size > count) { + pr_err_ratelimited("%s: failure on smd_pkt_dev id: %d - packet size %d > buffer size %zu,", + __func__, smd_pkt_devp->i, + pkt_size, count); + mutex_unlock(&smd_pkt_devp->rx_lock); + kfree(buf); + return -ETOOSMALL; + } + + bytes_read = 0; + do { + r = smd_read(smd_pkt_devp->ch, + (buf + bytes_read), + (pkt_size - bytes_read)); + if (r < 0) { + mutex_unlock(&smd_pkt_devp->rx_lock); + if (smd_pkt_devp->has_reset) { + E_SMD_PKT_SSR(smd_pkt_devp); + return notify_reset(smd_pkt_devp); + } + pr_err_ratelimited("%s Error while reading %d\n", + __func__, r); + kfree(buf); + return r; + } + bytes_read += r; + if (pkt_size != bytes_read) + wait_event(smd_pkt_devp->ch_read_wait_queue, + smd_read_avail(smd_pkt_devp->ch) || + smd_pkt_devp->has_reset); + if (smd_pkt_devp->has_reset) { + mutex_unlock(&smd_pkt_devp->rx_lock); + E_SMD_PKT_SSR(smd_pkt_devp); + kfree(buf); + return notify_reset(smd_pkt_devp); + } + } while (pkt_size != bytes_read); + mutex_unlock(&smd_pkt_devp->rx_lock); + + mutex_lock(&smd_pkt_devp->ch_lock); + spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags); + if (smd_pkt_devp->poll_mode && + !smd_cur_packet_size(smd_pkt_devp->ch)) { + __pm_relax(&smd_pkt_devp->pa_ws); + smd_pkt_devp->ws_locked = 0; + smd_pkt_devp->poll_mode = 0; + D_READ("%s unlocked smd_pkt_dev id:%d wakeup_source\n", + __func__, smd_pkt_devp->i); + } + spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags); + mutex_unlock(&smd_pkt_devp->ch_lock); + + r = copy_to_user(_buf, buf, bytes_read); + if (r) { + kfree(buf); + return -EFAULT; + } + D_READ("Finished %s on smd_pkt_dev id:%d %d bytes\n", + __func__, smd_pkt_devp->i, bytes_read); + kfree(buf); + + /* check and wakeup read threads waiting on this device */ + check_and_wakeup_reader(smd_pkt_devp); + + return bytes_read; +} + +ssize_t smd_pkt_write(struct file *file, + const char __user *_buf, + size_t count, + loff_t *ppos) +{ + int r = 0, bytes_written; + struct smd_pkt_dev *smd_pkt_devp; + DEFINE_WAIT(write_wait); + void *buf; + + smd_pkt_devp = file->private_data; + + if (!smd_pkt_devp) { + pr_err_ratelimited("%s on NULL smd_pkt_dev\n", __func__); + return -EINVAL; + } + + if (!smd_pkt_devp->ch) { + pr_err_ratelimited("%s on a closed smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return -EINVAL; + } + + if (smd_pkt_devp->do_reset_notification || smd_pkt_devp->has_reset) { + E_SMD_PKT_SSR(smd_pkt_devp); + /* notify client that a reset occurred */ + return notify_reset(smd_pkt_devp); + } + D_WRITE("Begin %s on smd_pkt_dev id:%d data_size %zu\n", + __func__, smd_pkt_devp->i, count); + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + r = copy_from_user(buf, _buf, count); + if (r) { + kfree(buf); + return -EFAULT; + } + + mutex_lock(&smd_pkt_devp->tx_lock); + if (!smd_pkt_devp->blocking_write) { + if (smd_write_avail(smd_pkt_devp->ch) < count) { + pr_err_ratelimited("%s: Not enough space in smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + mutex_unlock(&smd_pkt_devp->tx_lock); + kfree(buf); + return -ENOMEM; + } + } + + r = smd_write_start(smd_pkt_devp->ch, count); + if (r < 0) { + mutex_unlock(&smd_pkt_devp->tx_lock); + pr_err_ratelimited("%s: Error:%d in smd_pkt_dev id:%d @ smd_write_start\n", + __func__, r, smd_pkt_devp->i); + kfree(buf); + return r; + } + + bytes_written = 0; + do { + prepare_to_wait(&smd_pkt_devp->ch_write_wait_queue, + &write_wait, TASK_UNINTERRUPTIBLE); + if (!smd_write_segment_avail(smd_pkt_devp->ch) && + !smd_pkt_devp->has_reset) { + smd_enable_read_intr(smd_pkt_devp->ch); + schedule(); + } + finish_wait(&smd_pkt_devp->ch_write_wait_queue, &write_wait); + smd_disable_read_intr(smd_pkt_devp->ch); + + if (smd_pkt_devp->has_reset) { + mutex_unlock(&smd_pkt_devp->tx_lock); + E_SMD_PKT_SSR(smd_pkt_devp); + kfree(buf); + return notify_reset(smd_pkt_devp); + } + r = smd_write_segment(smd_pkt_devp->ch, + (void *)(buf + bytes_written), + (count - bytes_written)); + if (r < 0) { + mutex_unlock(&smd_pkt_devp->tx_lock); + if (smd_pkt_devp->has_reset) { + E_SMD_PKT_SSR(smd_pkt_devp); + return notify_reset(smd_pkt_devp); + } + pr_err_ratelimited("%s on smd_pkt_dev id:%d failed r:%d\n", + __func__, smd_pkt_devp->i, r); + kfree(buf); + return r; + } + bytes_written += r; + } while (bytes_written != count); + smd_write_end(smd_pkt_devp->ch); + mutex_unlock(&smd_pkt_devp->tx_lock); + D_WRITE("Finished %s on smd_pkt_dev id:%d %zu bytes\n", + __func__, smd_pkt_devp->i, count); + + kfree(buf); + return count; +} + +static unsigned int smd_pkt_poll(struct file *file, poll_table *wait) +{ + struct smd_pkt_dev *smd_pkt_devp; + unsigned int mask = 0; + + smd_pkt_devp = file->private_data; + if (!smd_pkt_devp) { + pr_err_ratelimited("%s on a NULL device\n", __func__); + return POLLERR; + } + + smd_pkt_devp->poll_mode = 1; + poll_wait(file, &smd_pkt_devp->ch_read_wait_queue, wait); + mutex_lock(&smd_pkt_devp->ch_lock); + if (smd_pkt_devp->has_reset || !smd_pkt_devp->ch) { + mutex_unlock(&smd_pkt_devp->ch_lock); + return POLLERR; + } + + if (smd_read_avail(smd_pkt_devp->ch)) { + mask |= POLLIN | POLLRDNORM; + D_POLL("%s sets POLLIN for smd_pkt_dev id: %d\n", + __func__, smd_pkt_devp->i); + } + mutex_unlock(&smd_pkt_devp->ch_lock); + + return mask; +} + +static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp) +{ + int sz; + unsigned long flags; + + if (!smd_pkt_devp) { + pr_err("%s on a NULL device\n", __func__); + return; + } + + if (!smd_pkt_devp->ch) { + pr_err("%s on a closed smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return; + } + + sz = smd_cur_packet_size(smd_pkt_devp->ch); + if (sz == 0) { + D_READ("%s: No packet in smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return; + } + if (!smd_read_avail(smd_pkt_devp->ch)) { + D_READ( + "%s: packet size is %d in smd_pkt_dev id:%d - but the data isn't here\n", + __func__, sz, smd_pkt_devp->i); + return; + } + + /* here we have a packet of size sz ready */ + spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags); + __pm_stay_awake(&smd_pkt_devp->pa_ws); + smd_pkt_devp->ws_locked = 1; + spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags); + wake_up(&smd_pkt_devp->ch_read_wait_queue); + schedule_work(&smd_pkt_devp->packet_arrival_work); + D_READ("%s: wake_up smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i); +} + +static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp) +{ + int sz; + + if (!smd_pkt_devp) { + pr_err("%s on a NULL device\n", __func__); + return; + } + + if (!smd_pkt_devp->ch) { + pr_err("%s on a closed smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return; + } + + sz = smd_write_segment_avail(smd_pkt_devp->ch); + if (sz) { + D_WRITE("%s: %d bytes write space in smd_pkt_dev id:%d\n", + __func__, sz, smd_pkt_devp->i); + smd_disable_read_intr(smd_pkt_devp->ch); + wake_up(&smd_pkt_devp->ch_write_wait_queue); + } +} + +static void ch_notify(void *priv, unsigned event) +{ + struct smd_pkt_dev *smd_pkt_devp = priv; + + if (smd_pkt_devp->ch == 0) { + if (event != SMD_EVENT_CLOSE) + pr_err("%s on a closed smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + return; + } + + switch (event) { + case SMD_EVENT_DATA: { + D_STATUS("%s: DATA event in smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + check_and_wakeup_reader(smd_pkt_devp); + if (smd_pkt_devp->blocking_write) + check_and_wakeup_writer(smd_pkt_devp); + break; + } + case SMD_EVENT_OPEN: + D_STATUS("%s: OPEN event in smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + smd_pkt_devp->has_reset = 0; + smd_pkt_devp->is_open = 1; + wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue); + break; + case SMD_EVENT_CLOSE: + D_STATUS("%s: CLOSE event in smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + smd_pkt_devp->is_open = 0; + /* put port into reset state */ + clean_and_signal(smd_pkt_devp); + if (!strcmp(smd_pkt_devp->ch_name, "LOOPBACK")) + schedule_delayed_work(&loopback_work, + msecs_to_jiffies(1000)); + break; + } +} + +static int smd_pkt_dummy_probe(struct platform_device *pdev) +{ + struct smd_pkt_dev *smd_pkt_devp; + + mutex_lock(&smd_pkt_dev_lock_lha1); + list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) { + if (smd_pkt_devp->edge == pdev->id + && !strcmp(pdev->name, smd_pkt_devp->ch_name)) { + complete_all(&smd_pkt_devp->ch_allocated); + D_STATUS("%s allocated SMD ch for smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + break; + } + } + mutex_unlock(&smd_pkt_dev_lock_lha1); + return 0; +} + +static uint32_t is_modem_smsm_inited(void) +{ + uint32_t modem_state; + uint32_t ready_state = (SMSM_INIT | SMSM_SMDINIT); + + modem_state = smsm_get_state(SMSM_MODEM_STATE); + return (modem_state & ready_state) == ready_state; +} + +/** + * smd_pkt_add_driver() - Add platform drivers for smd pkt device + * + * @smd_pkt_devp: pointer to the smd pkt device structure + * + * @returns: 0 for success, standard Linux error code otherwise + * + * This function is used to register platform driver once for all + * smd pkt devices which have same names and increment the reference + * count for 2nd to nth devices. + */ +static int smd_pkt_add_driver(struct smd_pkt_dev *smd_pkt_devp) +{ + int r = 0; + struct smd_pkt_driver *smd_pkt_driverp; + struct smd_pkt_driver *item; + + if (!smd_pkt_devp) { + pr_err("%s on a NULL device\n", __func__); + return -EINVAL; + } + D_STATUS("Begin %s on smd_pkt_ch[%s]\n", __func__, + smd_pkt_devp->ch_name); + + mutex_lock(&smd_pkt_driver_lock_lha1); + list_for_each_entry(item, &smd_pkt_driver_list, list) { + if (!strcmp(item->pdriver_name, smd_pkt_devp->ch_name)) { + D_STATUS("%s:%s Already Platform driver reg. cnt:%d\n", + __func__, smd_pkt_devp->ch_name, item->ref_cnt); + ++item->ref_cnt; + goto exit; + } + } + + smd_pkt_driverp = kzalloc(sizeof(*smd_pkt_driverp), GFP_KERNEL); + if (IS_ERR_OR_NULL(smd_pkt_driverp)) { + pr_err("%s: kzalloc() failed for smd_pkt_driver[%s]\n", + __func__, smd_pkt_devp->ch_name); + r = -ENOMEM; + goto exit; + } + + smd_pkt_driverp->driver.probe = smd_pkt_dummy_probe; + scnprintf(smd_pkt_driverp->pdriver_name, SMD_MAX_CH_NAME_LEN, + "%s", smd_pkt_devp->ch_name); + smd_pkt_driverp->driver.driver.name = smd_pkt_driverp->pdriver_name; + smd_pkt_driverp->driver.driver.owner = THIS_MODULE; + r = platform_driver_register(&smd_pkt_driverp->driver); + if (r) { + pr_err("%s: %s Platform driver reg. failed\n", + __func__, smd_pkt_devp->ch_name); + kfree(smd_pkt_driverp); + goto exit; + } + ++smd_pkt_driverp->ref_cnt; + list_add(&smd_pkt_driverp->list, &smd_pkt_driver_list); + +exit: + D_STATUS("End %s on smd_pkt_ch[%s]\n", __func__, smd_pkt_devp->ch_name); + mutex_unlock(&smd_pkt_driver_lock_lha1); + return r; +} + +/** + * smd_pkt_remove_driver() - Remove the platform drivers for smd pkt device + * + * @smd_pkt_devp: pointer to the smd pkt device structure + * + * This function is used to decrement the reference count on + * platform drivers for smd pkt devices and removes the drivers + * when the reference count becomes zero. + */ +static void smd_pkt_remove_driver(struct smd_pkt_dev *smd_pkt_devp) +{ + struct smd_pkt_driver *smd_pkt_driverp; + bool found_item = false; + + if (!smd_pkt_devp) { + pr_err("%s on a NULL device\n", __func__); + return; + } + + D_STATUS("Begin %s on smd_pkt_ch[%s]\n", __func__, + smd_pkt_devp->ch_name); + mutex_lock(&smd_pkt_driver_lock_lha1); + list_for_each_entry(smd_pkt_driverp, &smd_pkt_driver_list, list) { + if (!strcmp(smd_pkt_driverp->pdriver_name, + smd_pkt_devp->ch_name)) { + found_item = true; + D_STATUS("%s:%s Platform driver cnt:%d\n", + __func__, smd_pkt_devp->ch_name, + smd_pkt_driverp->ref_cnt); + if (smd_pkt_driverp->ref_cnt > 0) + --smd_pkt_driverp->ref_cnt; + else + pr_warn("%s reference count <= 0\n", __func__); + break; + } + } + if (!found_item) + pr_err("%s:%s No item found in list.\n", + __func__, smd_pkt_devp->ch_name); + + if (found_item && smd_pkt_driverp->ref_cnt == 0) { + platform_driver_unregister(&smd_pkt_driverp->driver); + smd_pkt_driverp->driver.probe = NULL; + list_del(&smd_pkt_driverp->list); + kfree(smd_pkt_driverp); + } + mutex_unlock(&smd_pkt_driver_lock_lha1); + D_STATUS("End %s on smd_pkt_ch[%s]\n", __func__, smd_pkt_devp->ch_name); +} + +int smd_pkt_open(struct inode *inode, struct file *file) +{ + int r = 0; + struct smd_pkt_dev *smd_pkt_devp; + const char *peripheral = NULL; + + smd_pkt_devp = container_of(inode->i_cdev, struct smd_pkt_dev, cdev); + + if (!smd_pkt_devp) { + pr_err_ratelimited("%s on a NULL device\n", __func__); + return -EINVAL; + } + D_STATUS("Begin %s on smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i); + + file->private_data = smd_pkt_devp; + + mutex_lock(&smd_pkt_devp->ch_lock); + if (smd_pkt_devp->ch == 0) { + unsigned open_wait_rem = smd_pkt_devp->open_modem_wait * 1000; + + reinit_completion(&smd_pkt_devp->ch_allocated); + + r = smd_pkt_add_driver(smd_pkt_devp); + if (r) { + pr_err_ratelimited("%s: %s Platform driver reg. failed\n", + __func__, smd_pkt_devp->ch_name); + goto out; + } + + peripheral = smd_edge_to_pil_str(smd_pkt_devp->edge); + if (!IS_ERR_OR_NULL(peripheral)) { + smd_pkt_devp->pil = subsystem_get(peripheral); + if (IS_ERR(smd_pkt_devp->pil)) { + r = PTR_ERR(smd_pkt_devp->pil); + pr_err_ratelimited("%s failed on smd_pkt_dev id:%d - subsystem_get failed for %s\n", + __func__, smd_pkt_devp->i, peripheral); + /* + * Sleep inorder to reduce the frequency of + * retry by user-space modules and to avoid + * possible watchdog bite. + */ + msleep(open_wait_rem); + goto release_pd; + } + } + + /* Wait for the modem SMSM to be inited for the SMD + ** Loopback channel to be allocated at the modem. Since + ** the wait need to be done atmost once, using msleep + ** doesn't degrade the performance. + */ + if (!strcmp(smd_pkt_devp->ch_name, "LOOPBACK")) { + if (!is_modem_smsm_inited()) + msleep(5000); + smsm_change_state(SMSM_APPS_STATE, + 0, SMSM_SMD_LOOPBACK); + msleep(100); + } + + /* + * Wait for a packet channel to be allocated so we know + * the modem is ready enough. + */ + if (open_wait_rem) { + r = wait_for_completion_interruptible_timeout( + &smd_pkt_devp->ch_allocated, + msecs_to_jiffies(open_wait_rem)); + if (r >= 0) + open_wait_rem = jiffies_to_msecs(r); + if (r == 0) + r = -ETIMEDOUT; + if (r == -ERESTARTSYS) { + pr_info_ratelimited("%s: wait on smd_pkt_dev id:%d allocation interrupted\n", + __func__, smd_pkt_devp->i); + goto release_pil; + } + if (r < 0) { + pr_err_ratelimited("%s: wait on smd_pkt_dev id:%d allocation failed rc:%d\n", + __func__, smd_pkt_devp->i, r); + goto release_pil; + } + } + + r = smd_named_open_on_edge(smd_pkt_devp->ch_name, + smd_pkt_devp->edge, + &smd_pkt_devp->ch, + smd_pkt_devp, + ch_notify); + if (r < 0) { + pr_err_ratelimited("%s: %s open failed %d\n", __func__, + smd_pkt_devp->ch_name, r); + goto release_pil; + } + + open_wait_rem = max_t(unsigned, 2000, open_wait_rem); + r = wait_event_interruptible_timeout( + smd_pkt_devp->ch_opened_wait_queue, + smd_pkt_devp->is_open, + msecs_to_jiffies(open_wait_rem)); + if (r == 0) + r = -ETIMEDOUT; + + if (r < 0) { + /* close the ch to sync smd's state with smd_pkt */ + smd_close(smd_pkt_devp->ch); + smd_pkt_devp->ch = NULL; + } + + if (r == -ERESTARTSYS) { + pr_info_ratelimited("%s: wait on smd_pkt_dev id:%d OPEN interrupted\n", + __func__, smd_pkt_devp->i); + } else if (r < 0) { + pr_err_ratelimited("%s: wait on smd_pkt_dev id:%d OPEN event failed rc:%d\n", + __func__, smd_pkt_devp->i, r); + } else if (!smd_pkt_devp->is_open) { + pr_err_ratelimited("%s: Invalid OPEN event on smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + r = -ENODEV; + } else { + smd_disable_read_intr(smd_pkt_devp->ch); + smd_pkt_devp->ch_size = + smd_write_avail(smd_pkt_devp->ch); + r = 0; + smd_pkt_devp->ref_cnt++; + D_STATUS("Finished %s on smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + } + } else { + smd_pkt_devp->ref_cnt++; + } +release_pil: + if (peripheral && (r < 0)) { + subsystem_put(smd_pkt_devp->pil); + smd_pkt_devp->pil = NULL; + } + +release_pd: + if (r < 0) + smd_pkt_remove_driver(smd_pkt_devp); +out: + mutex_unlock(&smd_pkt_devp->ch_lock); + + + return r; +} + +int smd_pkt_release(struct inode *inode, struct file *file) +{ + int r = 0; + struct smd_pkt_dev *smd_pkt_devp = file->private_data; + unsigned long flags; + + if (!smd_pkt_devp) { + pr_err_ratelimited("%s on a NULL device\n", __func__); + return -EINVAL; + } + D_STATUS("Begin %s on smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + + mutex_lock(&smd_pkt_devp->ch_lock); + mutex_lock(&smd_pkt_devp->rx_lock); + mutex_lock(&smd_pkt_devp->tx_lock); + if (smd_pkt_devp->ref_cnt > 0) + smd_pkt_devp->ref_cnt--; + + if (smd_pkt_devp->ch != 0 && smd_pkt_devp->ref_cnt == 0) { + clean_and_signal(smd_pkt_devp); + r = smd_close(smd_pkt_devp->ch); + smd_pkt_devp->ch = 0; + smd_pkt_devp->blocking_write = 0; + smd_pkt_devp->poll_mode = 0; + smd_pkt_remove_driver(smd_pkt_devp); + if (smd_pkt_devp->pil) + subsystem_put(smd_pkt_devp->pil); + smd_pkt_devp->has_reset = 0; + smd_pkt_devp->do_reset_notification = 0; + spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags); + if (smd_pkt_devp->ws_locked) { + __pm_relax(&smd_pkt_devp->pa_ws); + smd_pkt_devp->ws_locked = 0; + } + spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags); + } + mutex_unlock(&smd_pkt_devp->tx_lock); + mutex_unlock(&smd_pkt_devp->rx_lock); + mutex_unlock(&smd_pkt_devp->ch_lock); + + if (flush_work(&smd_pkt_devp->packet_arrival_work)) + D_STATUS("%s: Flushed work for smd_pkt_dev id:%d\n", __func__, + smd_pkt_devp->i); + + D_STATUS("Finished %s on smd_pkt_dev id:%d\n", + __func__, smd_pkt_devp->i); + + return r; +} + +static const struct file_operations smd_pkt_fops = { + .owner = THIS_MODULE, + .open = smd_pkt_open, + .release = smd_pkt_release, + .read = smd_pkt_read, + .write = smd_pkt_write, + .poll = smd_pkt_poll, + .unlocked_ioctl = smd_pkt_ioctl, + .compat_ioctl = smd_pkt_ioctl, +}; + +static int smd_pkt_init_add_device(struct smd_pkt_dev *smd_pkt_devp, int i) +{ + int r = 0; + + smd_pkt_devp->i = i; + + init_waitqueue_head(&smd_pkt_devp->ch_read_wait_queue); + init_waitqueue_head(&smd_pkt_devp->ch_write_wait_queue); + smd_pkt_devp->is_open = 0; + smd_pkt_devp->poll_mode = 0; + smd_pkt_devp->ws_locked = 0; + init_waitqueue_head(&smd_pkt_devp->ch_opened_wait_queue); + + spin_lock_init(&smd_pkt_devp->pa_spinlock); + mutex_init(&smd_pkt_devp->ch_lock); + mutex_init(&smd_pkt_devp->rx_lock); + mutex_init(&smd_pkt_devp->tx_lock); + wakeup_source_init(&smd_pkt_devp->pa_ws, smd_pkt_devp->dev_name); + INIT_WORK(&smd_pkt_devp->packet_arrival_work, packet_arrival_worker); + init_completion(&smd_pkt_devp->ch_allocated); + + cdev_init(&smd_pkt_devp->cdev, &smd_pkt_fops); + smd_pkt_devp->cdev.owner = THIS_MODULE; + + r = cdev_add(&smd_pkt_devp->cdev, (smd_pkt_number + i), 1); + if (IS_ERR_VALUE(r)) { + pr_err("%s: cdev_add() failed for smd_pkt_dev id:%d ret:%i\n", + __func__, i, r); + return r; + } + + smd_pkt_devp->devicep = + device_create(smd_pkt_classp, + NULL, + (smd_pkt_number + i), + NULL, + smd_pkt_devp->dev_name); + + if (IS_ERR_OR_NULL(smd_pkt_devp->devicep)) { + pr_err("%s: device_create() failed for smd_pkt_dev id:%d\n", + __func__, i); + r = -ENOMEM; + cdev_del(&smd_pkt_devp->cdev); + wakeup_source_trash(&smd_pkt_devp->pa_ws); + return r; + } + if (device_create_file(smd_pkt_devp->devicep, + &dev_attr_open_timeout)) + pr_err("%s: unable to create device attr for smd_pkt_dev id:%d\n", + __func__, i); + + if (!strcmp(smd_pkt_devp->ch_name, "LOOPBACK")) { + if (device_create_file(smd_pkt_devp->devicep, + &dev_attr_loopback_edge)) + pr_err("%s: unable to create device attr for smd_pkt_dev id:%d\n", + __func__, i); + } + mutex_lock(&smd_pkt_dev_lock_lha1); + list_add(&smd_pkt_devp->dev_list, &smd_pkt_dev_list); + mutex_unlock(&smd_pkt_dev_lock_lha1); + + return r; +} + +static void smd_pkt_core_deinit(void) +{ + struct smd_pkt_dev *smd_pkt_devp; + struct smd_pkt_dev *index; + + mutex_lock(&smd_pkt_dev_lock_lha1); + list_for_each_entry_safe(smd_pkt_devp, index, &smd_pkt_dev_list, + dev_list) { + cdev_del(&smd_pkt_devp->cdev); + list_del(&smd_pkt_devp->dev_list); + device_destroy(smd_pkt_classp, + MKDEV(MAJOR(smd_pkt_number), smd_pkt_devp->i)); + kfree(smd_pkt_devp); + } + mutex_unlock(&smd_pkt_dev_lock_lha1); + + if (!IS_ERR_OR_NULL(smd_pkt_classp)) + class_destroy(smd_pkt_classp); + + unregister_chrdev_region(MAJOR(smd_pkt_number), num_smd_pkt_ports); +} + +static int smd_pkt_alloc_chrdev_region(void) +{ + int r = alloc_chrdev_region(&smd_pkt_number, + 0, + num_smd_pkt_ports, + DEVICE_NAME); + + if (IS_ERR_VALUE(r)) { + pr_err("%s: alloc_chrdev_region() failed ret:%i\n", + __func__, r); + return r; + } + + smd_pkt_classp = class_create(THIS_MODULE, DEVICE_NAME); + if (IS_ERR(smd_pkt_classp)) { + pr_err("%s: class_create() failed ENOMEM\n", __func__); + r = -ENOMEM; + unregister_chrdev_region(MAJOR(smd_pkt_number), + num_smd_pkt_ports); + return r; + } + + return 0; +} + +static int parse_smdpkt_devicetree(struct device_node *node, + struct smd_pkt_dev *smd_pkt_devp) +{ + int edge; + char *key; + const char *ch_name; + const char *dev_name; + const char *remote_ss; + + key = "qcom,smdpkt-remote"; + remote_ss = of_get_property(node, key, NULL); + if (!remote_ss) + goto error; + + edge = smd_remote_ss_to_edge(remote_ss); + if (edge < 0) + goto error; + + smd_pkt_devp->edge = edge; + D_STATUS("%s: %s = %d", __func__, key, edge); + + key = "qcom,smdpkt-port-name"; + ch_name = of_get_property(node, key, NULL); + if (!ch_name) + goto error; + + strlcpy(smd_pkt_devp->ch_name, ch_name, SMD_MAX_CH_NAME_LEN); + D_STATUS("%s ch_name = %s\n", __func__, ch_name); + + key = "qcom,smdpkt-dev-name"; + dev_name = of_get_property(node, key, NULL); + if (!dev_name) + goto error; + + strlcpy(smd_pkt_devp->dev_name, dev_name, SMD_MAX_CH_NAME_LEN); + D_STATUS("%s dev_name = %s\n", __func__, dev_name); + + return 0; + +error: + pr_err("%s: missing key: %s\n", __func__, key); + return -ENODEV; + +} + +static int smd_pkt_devicetree_init(struct platform_device *pdev) +{ + int ret; + int i = 0; + struct device_node *node; + struct smd_pkt_dev *smd_pkt_devp; + int subnode_num = 0; + + for_each_child_of_node(pdev->dev.of_node, node) + ++subnode_num; + + num_smd_pkt_ports = subnode_num; + + ret = smd_pkt_alloc_chrdev_region(); + if (ret) { + pr_err("%s: smd_pkt_alloc_chrdev_region() failed ret:%i\n", + __func__, ret); + return ret; + } + + for_each_child_of_node(pdev->dev.of_node, node) { + smd_pkt_devp = kzalloc(sizeof(struct smd_pkt_dev), GFP_KERNEL); + if (IS_ERR_OR_NULL(smd_pkt_devp)) { + pr_err("%s: kzalloc() failed for smd_pkt_dev id:%d\n", + __func__, i); + ret = -ENOMEM; + goto error_destroy; + } + + ret = parse_smdpkt_devicetree(node, smd_pkt_devp); + if (ret) { + pr_err(" failed to parse_smdpkt_devicetree %d\n", i); + kfree(smd_pkt_devp); + goto error_destroy; + } + + ret = smd_pkt_init_add_device(smd_pkt_devp, i); + if (ret < 0) { + pr_err("add device failed for idx:%d ret=%d\n", i, ret); + kfree(smd_pkt_devp); + goto error_destroy; + } + i++; + } + + INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker); + + D_STATUS("SMD Packet Port Driver Initialized.\n"); + return 0; + +error_destroy: + smd_pkt_core_deinit(); + return ret; +} + +static int msm_smd_pkt_probe(struct platform_device *pdev) +{ + int ret; + + if (pdev) { + if (pdev->dev.of_node) { + D_STATUS("%s device tree implementation\n", __func__); + ret = smd_pkt_devicetree_init(pdev); + if (ret) + pr_err("%s: device tree init failed\n", + __func__); + } + } + + return 0; +} + +static const struct of_device_id msm_smd_pkt_match_table[] = { + { .compatible = "qcom,smdpkt" }, + {}, +}; + +static struct platform_driver msm_smd_pkt_driver = { + .probe = msm_smd_pkt_probe, + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + .of_match_table = msm_smd_pkt_match_table, + }, +}; + +static int __init smd_pkt_init(void) +{ + int rc; + + INIT_LIST_HEAD(&smd_pkt_dev_list); + INIT_LIST_HEAD(&smd_pkt_driver_list); + rc = platform_driver_register(&msm_smd_pkt_driver); + if (rc) { + pr_err("%s: msm_smd_driver register failed %d\n", + __func__, rc); + return rc; + } + + smd_pkt_ilctxt = ipc_log_context_create(SMD_PKT_IPC_LOG_PAGE_CNT, + "smd_pkt", 0); + return 0; +} + +static void __exit smd_pkt_cleanup(void) +{ + smd_pkt_core_deinit(); +} + +module_init(smd_pkt_init); +module_exit(smd_pkt_cleanup); + +MODULE_DESCRIPTION("MSM Shared Memory Packet Port"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 65f7eecc45b0..f10a107614b4 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -401,7 +401,7 @@ static void disable_interrupts(struct tpm_chip *chip) iowrite32(intmask, chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); - free_irq(chip->vendor.irq, chip); + devm_free_irq(chip->pdev, chip->vendor.irq, chip); chip->vendor.irq = 0; } diff --git a/drivers/clk/msm/clock-local2.c b/drivers/clk/msm/clock-local2.c index 19956f030ae9..adb07cdb7e8d 100644 --- a/drivers/clk/msm/clock-local2.c +++ b/drivers/clk/msm/clock-local2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -928,7 +928,8 @@ static unsigned long branch_clk_get_rate(struct clk *c) { struct branch_clk *branch = to_branch_clk(c); - if (branch->max_div) + if (branch->max_div || + (branch->aggr_sibling_rates && !branch->is_prepared)) return branch->c.rate; return clk_get_rate(c->parent); diff --git a/drivers/clk/msm/clock-mmss-8998.c b/drivers/clk/msm/clock-mmss-8998.c index 6ebb3ed6ed91..fdaaa723accd 100644 --- a/drivers/clk/msm/clock-mmss-8998.c +++ b/drivers/clk/msm/clock-mmss-8998.c @@ -359,6 +359,7 @@ static struct rcg_clk mdp_clk_src = { .set_rate = set_rate_hid, .freq_tbl = ftbl_mdp_clk_src, .current_freq = &rcg_dummy_freq, + .non_local_children = true, .base = &virt_base, .c = { .dbg_name = "mdp_clk_src", diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c index f82ddc3b008b..8bf45f572c5e 100644 --- a/drivers/clk/qcom/clk-cpu-osm.c +++ b/drivers/clk/qcom/clk-cpu-osm.c @@ -84,12 +84,14 @@ enum clk_osm_trace_packet_id { #define VERSION_REG 0x0 #define OSM_TABLE_SIZE 40 +#define MAX_VIRTUAL_CORNER (OSM_TABLE_SIZE - 1) #define MAX_CLUSTER_CNT 2 #define LLM_SW_OVERRIDE_CNT 3 #define CORE_COUNT_VAL(val) ((val & GENMASK(18, 16)) >> 16) #define SINGLE_CORE 1 #define MAX_CORE_COUNT 4 #define DEBUG_REG_NUM 3 +#define OSM_SEQ_MINUS_ONE 0xff #define ENABLE_REG 0x1004 #define INDEX_REG 0x1150 @@ -353,8 +355,10 @@ struct clk_osm { u32 cluster_num; u32 irq; u32 apm_crossover_vc; + u32 apm_threshold_pre_vc; u32 apm_threshold_vc; u32 mem_acc_crossover_vc; + u32 mem_acc_threshold_pre_vc; u32 mem_acc_threshold_vc; u32 cycle_counter_reads; u32 cycle_counter_delay; @@ -772,7 +776,7 @@ static const char * const gcc_parent_names_1[] = { }; static struct freq_tbl ftbl_osm_clk_src[] = { - F(200000000, LMH_LITE_CLK_SRC, 3, 0, 0), + F(200000000, LMH_LITE_CLK_SRC, 1.5, 0, 0), { } }; @@ -1622,10 +1626,26 @@ static int clk_osm_resolve_crossover_corners(struct clk_osm *c, if (corner_volt >= apm_threshold) { c->apm_threshold_vc = c->osm_table[i].virtual_corner; + /* + * Handle case where VC 0 has open-loop + * greater than or equal to APM threshold voltage. + */ + c->apm_threshold_pre_vc = c->apm_threshold_vc ? + c->apm_threshold_vc - 1 : OSM_SEQ_MINUS_ONE; break; } } + /* + * This assumes the OSM table uses corners + * 0 to MAX_VIRTUAL_CORNER - 1. + */ + if (!c->apm_threshold_vc && + c->apm_threshold_pre_vc != OSM_SEQ_MINUS_ONE) { + c->apm_threshold_vc = MAX_VIRTUAL_CORNER; + c->apm_threshold_pre_vc = c->apm_threshold_vc - 1; + } + /* Determine MEM ACC threshold virtual corner */ if (mem_acc_threshold) { for (i = 0; i < OSM_TABLE_SIZE; i++) { @@ -1636,9 +1656,30 @@ static int clk_osm_resolve_crossover_corners(struct clk_osm *c, if (corner_volt >= mem_acc_threshold) { c->mem_acc_threshold_vc = c->osm_table[i].virtual_corner; + /* + * Handle case where VC 0 has open-loop + * greater than or equal to MEM-ACC threshold + * voltage. + */ + c->mem_acc_threshold_pre_vc = + c->mem_acc_threshold_vc ? + c->mem_acc_threshold_vc - 1 : + OSM_SEQ_MINUS_ONE; break; } } + + /* + * This assumes the OSM table uses corners + * 0 to MAX_VIRTUAL_CORNER - 1. + */ + if (!c->mem_acc_threshold_vc && c->mem_acc_threshold_pre_vc + != OSM_SEQ_MINUS_ONE) { + c->mem_acc_threshold_vc = + MAX_VIRTUAL_CORNER; + c->mem_acc_threshold_pre_vc = + c->mem_acc_threshold_vc - 1; + } } return 0; @@ -1989,13 +2030,20 @@ static void clk_osm_program_mem_acc_regs(struct clk_osm *c) * highest MEM ACC threshold if it is specified instead of the * fixed mapping in the LUT. */ - if (c->mem_acc_threshold_vc) { - threshold_vc[2] = c->mem_acc_threshold_vc - 1; + if (c->mem_acc_threshold_vc || c->mem_acc_threshold_pre_vc + == OSM_SEQ_MINUS_ONE) { + threshold_vc[2] = c->mem_acc_threshold_pre_vc; threshold_vc[3] = c->mem_acc_threshold_vc; - if (threshold_vc[1] >= threshold_vc[2]) - threshold_vc[1] = threshold_vc[2] - 1; - if (threshold_vc[0] >= threshold_vc[1]) - threshold_vc[0] = threshold_vc[1] - 1; + + if (c->mem_acc_threshold_pre_vc == OSM_SEQ_MINUS_ONE) { + threshold_vc[1] = threshold_vc[0] = + c->mem_acc_threshold_pre_vc; + } else { + if (threshold_vc[1] >= threshold_vc[2]) + threshold_vc[1] = threshold_vc[2] - 1; + if (threshold_vc[0] >= threshold_vc[1]) + threshold_vc[0] = threshold_vc[1] - 1; + } } scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(55), @@ -2249,8 +2297,7 @@ static void clk_osm_apm_vc_setup(struct clk_osm *c) clk_osm_write_reg(c, c->pbases[OSM_BASE] + SEQ_REG(1), SEQ_REG(8)); clk_osm_write_reg(c, c->apm_threshold_vc, SEQ_REG(15)); - clk_osm_write_reg(c, c->apm_threshold_vc != 0 ? - c->apm_threshold_vc - 1 : 0xff, + clk_osm_write_reg(c, c->apm_threshold_pre_vc, SEQ_REG(31)); clk_osm_write_reg(c, 0x3b | c->apm_threshold_vc << 6, SEQ_REG(73)); @@ -2268,8 +2315,7 @@ static void clk_osm_apm_vc_setup(struct clk_osm *c) scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(15), c->apm_threshold_vc); scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(31), - c->apm_threshold_vc != 0 ? - c->apm_threshold_vc - 1 : 0xff); + c->apm_threshold_pre_vc); scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(76), 0x39 | c->apm_threshold_vc << 6); } diff --git a/drivers/clk/qcom/gcc-sdm660.c b/drivers/clk/qcom/gcc-sdm660.c index b55310e091af..251acafca196 100644 --- a/drivers/clk/qcom/gcc-sdm660.c +++ b/drivers/clk/qcom/gcc-sdm660.c @@ -732,6 +732,7 @@ static struct clk_rcg2 gp3_clk_src = { }; static const struct freq_tbl ftbl_hmss_gpll0_clk_src[] = { + F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0), F(600000000, P_GPLL0_OUT_MAIN, 1, 0, 0), { } }; @@ -2755,6 +2756,9 @@ static int gcc_660_probe(struct platform_device *pdev) /* Keep bimc gfx clock port on all the time */ clk_prepare_enable(gcc_bimc_gfx_clk.clkr.hw.clk); + /* Set the HMSS_GPLL0_SRC for 300MHz to CPU subsystem */ + clk_set_rate(hmss_gpll0_clk_src.clkr.hw.clk, 300000000); + dev_info(&pdev->dev, "Registered GCC clocks\n"); return ret; @@ -2954,6 +2958,9 @@ static const char *const debug_mux_parent_names[] = { "mmss_video_axi_clk", "mmss_video_core_clk", "mmss_video_subcore0_clk", + "mmss_throttle_camss_axi_clk", + "mmss_throttle_mdss_axi_clk", + "mmss_throttle_video_axi_clk", "gpucc_gfx3d_clk", "gpucc_rbbmtimer_clk", "gpucc_rbcpr_clk", @@ -3219,6 +3226,12 @@ static struct clk_debug_mux gcc_debug_mux = { 0x00E, 0, 0, 0x1000, BM(14, 13) }, { "mmss_video_subcore0_clk", 0x22, MMCC, 0x01A, 0, 0, 0x1000, BM(14, 13) }, + { "mmss_throttle_camss_axi_clk", 0x22, MMCC, + 0x0AA, 0, 0, 0x1000, BM(14, 13) }, + { "mmss_throttle_mdss_axi_clk", 0x22, MMCC, + 0x0AB, 0, 0, 0x1000, BM(14, 13) }, + { "mmss_throttle_video_axi_clk", 0x22, MMCC, + 0x0AC, 0, 0, 0x1000, BM(14, 13) }, { "gpucc_gfx3d_clk", 0x13d, GPU, 0x008, 0, 0, 0, BM(18, 17) }, { "gpucc_rbbmtimer_clk", 0x13d, GPU, diff --git a/drivers/clk/qcom/mmcc-sdm660.c b/drivers/clk/qcom/mmcc-sdm660.c index 910c36c65b6a..542737e4d204 100644 --- a/drivers/clk/qcom/mmcc-sdm660.c +++ b/drivers/clk/qcom/mmcc-sdm660.c @@ -2053,6 +2053,19 @@ static struct clk_branch mmss_camss_jpeg_axi_clk = { }, }; +static struct clk_branch mmss_throttle_camss_axi_clk = { + .halt_reg = 0x3c3c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3c3c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_throttle_camss_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch mmss_camss_mclk0_clk = { .halt_reg = 0x3384, .halt_check = BRANCH_HALT, @@ -2341,6 +2354,19 @@ static struct clk_branch mmss_mdss_axi_clk = { }, }; +static struct clk_branch mmss_throttle_mdss_axi_clk = { + .halt_reg = 0x246c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x246c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_throttle_mdss_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch mmss_mdss_byte0_clk = { .halt_reg = 0x233c, .halt_check = BRANCH_HALT, @@ -2801,6 +2827,19 @@ static struct clk_branch mmss_video_axi_clk = { }, }; +static struct clk_branch mmss_throttle_video_axi_clk = { + .halt_reg = 0x118c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x118c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_throttle_video_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch mmss_video_core_clk = { .halt_reg = 0x1028, .halt_check = BRANCH_HALT, @@ -2962,6 +3001,9 @@ static struct clk_regmap *mmcc_660_clocks[] = { [MMSS_MISC_CXO_CLK] = &mmss_misc_cxo_clk.clkr, [MMSS_MNOC_AHB_CLK] = &mmss_mnoc_ahb_clk.clkr, [MMSS_SNOC_DVM_AXI_CLK] = &mmss_snoc_dvm_axi_clk.clkr, + [MMSS_THROTTLE_CAMSS_AXI_CLK] = &mmss_throttle_camss_axi_clk.clkr, + [MMSS_THROTTLE_MDSS_AXI_CLK] = &mmss_throttle_mdss_axi_clk.clkr, + [MMSS_THROTTLE_VIDEO_AXI_CLK] = &mmss_throttle_video_axi_clk.clkr, [MMSS_VIDEO_AHB_CLK] = &mmss_video_ahb_clk.clkr, [MMSS_VIDEO_AXI_CLK] = &mmss_video_axi_clk.clkr, [MMSS_VIDEO_CORE_CLK] = &mmss_video_core_clk.clkr, diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index e4d9aef1dda4..a0dba9beac05 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -698,9 +698,11 @@ static ssize_t show_cpuinfo_cur_freq(struct cpufreq_policy *policy, char *buf) { unsigned int cur_freq = __cpufreq_get(policy); - if (!cur_freq) - return sprintf(buf, "<unknown>"); - return sprintf(buf, "%u\n", cur_freq); + + if (cur_freq) + return sprintf(buf, "%u\n", cur_freq); + + return sprintf(buf, "<unknown>\n"); } /** diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 24ac49019b29..b91e115462ae 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -419,13 +419,13 @@ static u64 update_load(int cpu) ppol->policy->governor_data; u64 now; u64 now_idle; - unsigned int delta_idle; - unsigned int delta_time; + u64 delta_idle; + u64 delta_time; u64 active_time; now_idle = get_cpu_idle_time(cpu, &now, tunables->io_is_busy); - delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle); - delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp); + delta_idle = (now_idle - pcpu->time_in_idle); + delta_time = (now - pcpu->time_in_idle_timestamp); if (delta_time <= delta_idle) active_time = 0; diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index ce67145bb142..4224b594f1b8 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -43,6 +43,7 @@ #include <soc/qcom/event_timer.h> #include <soc/qcom/lpm-stats.h> #include <soc/qcom/jtag.h> +#include <soc/qcom/minidump.h> #include <asm/cputype.h> #include <asm/arch_timer.h> #include <asm/cacheflush.h> @@ -72,6 +73,8 @@ enum debug_event { CLUSTER_ENTER, CLUSTER_EXIT, PRE_PC_CB, + CPU_HP_STARTING, + CPU_HP_DYING, }; struct lpm_debug { @@ -341,10 +344,16 @@ static int lpm_cpu_callback(struct notifier_block *cpu_nb, switch (action & ~CPU_TASKS_FROZEN) { case CPU_DYING: + update_debug_pc_event(CPU_HP_DYING, cpu, + cluster->num_children_in_sync.bits[0], + cluster->child_cpus.bits[0], false); cluster_prepare(cluster, get_cpu_mask((unsigned int) cpu), NR_LPM_LEVELS, false, 0); break; case CPU_STARTING: + update_debug_pc_event(CPU_HP_STARTING, cpu, + cluster->num_children_in_sync.bits[0], + cluster->child_cpus.bits[0], false); cluster_unprepare(cluster, get_cpu_mask((unsigned int) cpu), NR_LPM_LEVELS, false, 0); break; @@ -682,7 +691,7 @@ static int cpu_power_select(struct cpuidle_device *dev, if (!cpu) return -EINVAL; - if (sleep_disabled) + if (sleep_disabled && !cpu_isolated(dev->cpu)) return 0; idx_restrict = cpu->nlevels + 1; @@ -1846,6 +1855,7 @@ static int lpm_probe(struct platform_device *pdev) int ret; int size; struct kobject *module_kobj = NULL; + struct md_region md_entry; get_online_cpus(); lpm_root_node = lpm_of_parse_cluster(pdev); @@ -1906,6 +1916,14 @@ static int lpm_probe(struct platform_device *pdev) goto failed; } + /* Add lpm_debug to Minidump*/ + strlcpy(md_entry.name, "KLPMDEBUG", sizeof(md_entry.name)); + md_entry.virt_addr = (uintptr_t)lpm_debug; + md_entry.phys_addr = lpm_debug_phys; + md_entry.size = size; + if (msm_minidump_add_region(&md_entry)) + pr_info("Failed to add lpm_debug in Minidump\n"); + return 0; failed: free_cluster_node(lpm_root_node); diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c index 7459401979fe..d04ca6f28f90 100644 --- a/drivers/crypto/msm/qcedev.c +++ b/drivers/crypto/msm/qcedev.c @@ -56,6 +56,7 @@ static uint8_t _std_init_vector_sha256_uint8[] = { static DEFINE_MUTEX(send_cmd_lock); static DEFINE_MUTEX(qcedev_sent_bw_req); +static DEFINE_MUTEX(hash_access_lock); static void qcedev_ce_high_bw_req(struct qcedev_control *podev, bool high_bw_req) @@ -1648,12 +1649,18 @@ long qcedev_ioctl(struct file *file, unsigned cmd, unsigned long arg) (void __user *)arg, sizeof(struct qcedev_sha_op_req))) return -EFAULT; - if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev)) + mutex_lock(&hash_access_lock); + if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev)) { + mutex_unlock(&hash_access_lock); return -EINVAL; + } qcedev_areq.op_type = QCEDEV_CRYPTO_OPER_SHA; err = qcedev_hash_init(&qcedev_areq, handle, &sg_src); - if (err) + if (err) { + mutex_unlock(&hash_access_lock); return err; + } + mutex_unlock(&hash_access_lock); if (copy_to_user((void __user *)arg, &qcedev_areq.sha_op_req, sizeof(struct qcedev_sha_op_req))) return -EFAULT; @@ -1671,32 +1678,42 @@ long qcedev_ioctl(struct file *file, unsigned cmd, unsigned long arg) (void __user *)arg, sizeof(struct qcedev_sha_op_req))) return -EFAULT; - if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev)) + mutex_lock(&hash_access_lock); + if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev)) { + mutex_unlock(&hash_access_lock); return -EINVAL; + } qcedev_areq.op_type = QCEDEV_CRYPTO_OPER_SHA; if (qcedev_areq.sha_op_req.alg == QCEDEV_ALG_AES_CMAC) { err = qcedev_hash_cmac(&qcedev_areq, handle, &sg_src); - if (err) + if (err) { + mutex_unlock(&hash_access_lock); return err; + } } else { if (handle->sha_ctxt.init_done == false) { pr_err("%s Init was not called\n", __func__); + mutex_unlock(&hash_access_lock); return -EINVAL; } err = qcedev_hash_update(&qcedev_areq, handle, &sg_src); - if (err) + if (err) { + mutex_unlock(&hash_access_lock); return err; + } } if (handle->sha_ctxt.diglen > QCEDEV_MAX_SHA_DIGEST) { pr_err("Invalid sha_ctxt.diglen %d\n", handle->sha_ctxt.diglen); + mutex_unlock(&hash_access_lock); return -EINVAL; } memcpy(&qcedev_areq.sha_op_req.digest[0], &handle->sha_ctxt.digest[0], handle->sha_ctxt.diglen); + mutex_unlock(&hash_access_lock); if (copy_to_user((void __user *)arg, &qcedev_areq.sha_op_req, sizeof(struct qcedev_sha_op_req))) return -EFAULT; @@ -1713,16 +1730,22 @@ long qcedev_ioctl(struct file *file, unsigned cmd, unsigned long arg) (void __user *)arg, sizeof(struct qcedev_sha_op_req))) return -EFAULT; - if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev)) + mutex_lock(&hash_access_lock); + if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev)) { + mutex_unlock(&hash_access_lock); return -EINVAL; + } qcedev_areq.op_type = QCEDEV_CRYPTO_OPER_SHA; err = qcedev_hash_final(&qcedev_areq, handle); - if (err) + if (err) { + mutex_unlock(&hash_access_lock); return err; + } qcedev_areq.sha_op_req.diglen = handle->sha_ctxt.diglen; memcpy(&qcedev_areq.sha_op_req.digest[0], &handle->sha_ctxt.digest[0], handle->sha_ctxt.diglen); + mutex_unlock(&hash_access_lock); if (copy_to_user((void __user *)arg, &qcedev_areq.sha_op_req, sizeof(struct qcedev_sha_op_req))) return -EFAULT; @@ -1737,20 +1760,28 @@ long qcedev_ioctl(struct file *file, unsigned cmd, unsigned long arg) (void __user *)arg, sizeof(struct qcedev_sha_op_req))) return -EFAULT; - if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev)) + mutex_lock(&hash_access_lock); + if (qcedev_check_sha_params(&qcedev_areq.sha_op_req, podev)) { + mutex_unlock(&hash_access_lock); return -EINVAL; + } qcedev_areq.op_type = QCEDEV_CRYPTO_OPER_SHA; qcedev_hash_init(&qcedev_areq, handle, &sg_src); err = qcedev_hash_update(&qcedev_areq, handle, &sg_src); - if (err) + if (err) { + mutex_unlock(&hash_access_lock); return err; + } err = qcedev_hash_final(&qcedev_areq, handle); - if (err) + if (err) { + mutex_unlock(&hash_access_lock); return err; + } qcedev_areq.sha_op_req.diglen = handle->sha_ctxt.diglen; memcpy(&qcedev_areq.sha_op_req.digest[0], &handle->sha_ctxt.digest[0], handle->sha_ctxt.diglen); + mutex_unlock(&hash_access_lock); if (copy_to_user((void __user *)arg, &qcedev_areq.sha_op_req, sizeof(struct qcedev_sha_op_req))) return -EFAULT; diff --git a/drivers/esoc/esoc_dev.c b/drivers/esoc/esoc_dev.c index ffb2237da5fa..bbe1d24fb1f6 100644 --- a/drivers/esoc/esoc_dev.c +++ b/drivers/esoc/esoc_dev.c @@ -338,7 +338,6 @@ int esoc_clink_del_device(struct device *dev, void *dummy) esoc_udev = esoc_udev_get_by_minor(esoc_clink->id); if (!esoc_udev) return 0; - return_esoc_udev(esoc_udev); device_destroy(esoc_class, MKDEV(esoc_major, esoc_clink->id)); return_esoc_udev(esoc_udev); return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c index 7c42ff670080..a0924330d125 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c @@ -25,6 +25,7 @@ * Alex Deucher * Jerome Glisse */ +#include <linux/irq.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> #include <drm/amdgpu_drm.h> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index cc1e16fd7e76..02a60a1df50d 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -89,6 +89,14 @@ struct detailed_mode_closure { #define LEVEL_GTF2 2 #define LEVEL_CVT 3 +/*Enum storing luminance types for HDR blocks in EDID*/ +enum luminance_value { + NO_LUMINANCE_DATA = 3, + MAXIMUM_LUMINANCE = 4, + FRAME_AVERAGE_LUMINANCE = 5, + MINIMUM_LUMINANCE = 6 +}; + static struct edid_quirk { char vendor[4]; int product_id; @@ -992,6 +1000,221 @@ static const struct drm_display_mode edid_cea_modes[] = { 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, + /* 65 - 1280x720@24Hz */ + { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040, + 3080, 3300, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 66 - 1280x720@25Hz */ + { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700, + 3740, 3960, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 67 - 1280x720@30Hz */ + { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040, + 3080, 3300, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 68 - 1280x720@50Hz */ + { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720, + 1760, 1980, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 69 - 1280x720@60Hz */ + { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390, + 1430, 1650, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 70 - 1280x720@100Hz */ + { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720, + 1760, 1980, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 71 - 1280x720@120Hz */ + { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390, + 1430, 1650, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 72 - 1920x1080@24Hz */ + { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558, + 2602, 2750, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 73 - 1920x1080@25Hz */ + { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448, + 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 74 - 1920x1080@30Hz */ + { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008, + 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 75 - 1920x1080@50Hz */ + { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448, + 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 76 - 1920x1080@60Hz */ + { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, + 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 77 - 1920x1080@100Hz */ + { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448, + 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 78 - 1920x1080@120Hz */ + { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008, + 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 79 - 1680x720@24Hz */ + { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 3040, + 3080, 3300, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 80 - 1680x720@25Hz */ + { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2908, + 2948, 3168, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 81 - 1680x720@30Hz */ + { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2380, + 2420, 2640, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 82 - 1680x720@50Hz */ + { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 82500, 1680, 1940, + 1980, 2200, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 83 - 1680x720@60Hz */ + { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 99000, 1680, 1940, + 1980, 2200, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 84 - 1680x720@100Hz */ + { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 165000, 1680, 1740, + 1780, 2000, 0, 720, 725, 730, 825, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 85 - 1680x720@120Hz */ + { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 198000, 1680, 1740, + 1780, 2000, 0, 720, 725, 730, 825, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 86 - 2560x1080@24Hz */ + { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 99000, 2560, 3558, + 3602, 3750, 0, 1080, 1084, 1089, 1100, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 87 - 2560x1080@25Hz */ + { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 90000, 2560, 3008, + 3052, 3200, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 88 - 2560x1080@30Hz */ + { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 118800, 2560, 3328, + 3372, 3520, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 89 - 2560x1080@50Hz */ + { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 185625, 2560, 3108, + 3152, 3300, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 90 - 2560x1080@60Hz */ + { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 198000, 2560, 2808, + 2852, 3000, 0, 1080, 1084, 1089, 1100, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 91 - 2560x1080@100Hz */ + { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 371250, 2560, 2778, + 2822, 2970, 0, 1080, 1084, 1089, 1250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 92 - 2560x1080@120Hz */ + { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 495000, 2560, 3108, + 3152, 3300, 0, 1080, 1084, 1089, 1250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + /* 93 - 3840x2160p@24Hz 16:9 */ + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116, + 5204, 5500, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,}, + /* 94 - 3840x2160p@25Hz 16:9 */ + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4896, + 4984, 5280, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9}, + /* 95 - 3840x2160p@30Hz 16:9 */ + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016, + 4104, 4400, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9}, + /* 96 - 3840x2160p@50Hz 16:9 */ + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896, + 4984, 5280, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9}, + /* 97 - 3840x2160p@60Hz 16:9 */ + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016, + 4104, 4400, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9}, + /* 98 - 4096x2160p@24Hz 256:135 */ + { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5116, + 5204, 5500, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135}, + /* 99 - 4096x2160p@25Hz 256:135 */ + { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5064, + 5152, 5280, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135}, + /* 100 - 4096x2160p@30Hz 256:135 */ + { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 4184, + 4272, 4400, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135}, + /* 101 - 4096x2160p@50Hz 256:135 */ + { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 5064, + 5152, 5280, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135}, + /* 102 - 4096x2160p@60Hz 256:135 */ + { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 4184, + 4272, 4400, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135}, + /* 103 - 3840x2160p@24Hz 64:27 */ + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116, + 5204, 5500, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27}, + /* 104 - 3840x2160p@25Hz 64:27 */ + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016, + 4104, 4400, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27}, + /* 105 - 3840x2160p@30Hz 64:27 */ + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016, + 4104, 4400, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27}, + /* 106 - 3840x2160p@50Hz 64:27 */ + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896, + 4984, 5280, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27}, + /* 107 - 3840x2160p@60Hz 64:27 */ + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016, + 4104, 4400, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27}, }; /* @@ -2482,12 +2705,15 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, return closure.modes; } - +#define VIDEO_CAPABILITY_EXTENDED_DATA_BLOCK 0x0 #define AUDIO_BLOCK 0x01 #define VIDEO_BLOCK 0x02 #define VENDOR_BLOCK 0x03 #define SPEAKER_BLOCK 0x04 +#define HDR_STATIC_METADATA_EXTENDED_DATA_BLOCK 0x06 +#define EXTENDED_TAG 0x07 #define VIDEO_CAPABILITY_BLOCK 0x07 +#define Y420_VIDEO_DATA_BLOCK 0x0E #define EDID_BASIC_AUDIO (1 << 6) #define EDID_CEA_YCRCB444 (1 << 5) #define EDID_CEA_YCRCB422 (1 << 4) @@ -3076,6 +3302,21 @@ static bool cea_db_is_hdmi_vsdb(const u8 *db) return hdmi_id == HDMI_IEEE_OUI; } +static bool cea_db_is_hdmi_hf_vsdb(const u8 *db) +{ + int hdmi_id; + + if (cea_db_tag(db) != VENDOR_BLOCK) + return false; + + if (cea_db_payload_len(db) < 7) + return false; + + hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16); + + return hdmi_id == HDMI_IEEE_OUI_HF; +} + #define for_each_cea_db(cea, i, start, end) \ for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1) @@ -3199,6 +3440,258 @@ parse_hdmi_vsdb(struct drm_connector *connector, const u8 *db) } static void +parse_hdmi_hf_vsdb(struct drm_connector *connector, const u8 *db) +{ + u8 len = cea_db_payload_len(db); + + if (len < 7) + return; + + if (db[4] != 1) + return; /* invalid version */ + + connector->max_tmds_char = db[5] * 5; + connector->scdc_present = db[6] & (1 << 7); + connector->rr_capable = db[6] & (1 << 6); + connector->flags_3d = db[6] & 0x7; + connector->supports_scramble = connector->scdc_present && + (db[6] & (1 << 3)); + + DRM_DEBUG_KMS("HDMI v2: max TMDS char %d, " + "scdc %s, " + "rr %s, " + "3D flags 0x%x, " + "scramble %s\n", + connector->max_tmds_char, + connector->scdc_present ? "available" : "not available", + connector->rr_capable ? "capable" : "not capable", + connector->flags_3d, + connector->supports_scramble ? + "supported" : "not supported"); +} + +static void +drm_hdmi_extract_vsdbs_info(struct drm_connector *connector, struct edid *edid) +{ + const u8 *cea = drm_find_cea_extension(edid); + const u8 *db = NULL; + + if (cea && cea_revision(cea) >= 3) { + int i, start, end; + + if (cea_db_offsets(cea, &start, &end)) + return; + + for_each_cea_db(cea, i, start, end) { + db = &cea[i]; + + if (cea_db_tag(db) == VENDOR_BLOCK) { + /* HDMI Vendor-Specific Data Block */ + if (cea_db_is_hdmi_vsdb(db)) + parse_hdmi_vsdb(connector, db); + /* HDMI Forum Vendor-Specific Data Block */ + else if (cea_db_is_hdmi_hf_vsdb(db)) + parse_hdmi_hf_vsdb(connector, db); + } + } + } +} + +/* + * drm_extract_vcdb_info - Parse the HDMI Video Capability Data Block + * @connector: connector corresponding to the HDMI sink + * @db: start of the CEA vendor specific block + * + * Parses the HDMI VCDB to extract sink info for @connector. + */ +static void +drm_extract_vcdb_info(struct drm_connector *connector, const u8 *db) +{ + /* + * Check if the sink specifies underscan + * support for: + * BIT 5: preferred video format + * BIT 3: IT video format + * BIT 1: CE video format + */ + + connector->pt_scan_info = + (db[2] & (BIT(4) | BIT(5))) >> 4; + connector->it_scan_info = + (db[2] & (BIT(3) | BIT(2))) >> 2; + connector->ce_scan_info = + db[2] & (BIT(1) | BIT(0)); + + DRM_DEBUG_KMS("Scan Info (pt|it|ce): (%d|%d|%d)", + (int) connector->pt_scan_info, + (int) connector->it_scan_info, + (int) connector->ce_scan_info); +} + +static bool drm_edid_is_luminance_value_present( +u32 block_length, enum luminance_value value) +{ + return block_length > NO_LUMINANCE_DATA && value <= block_length; +} + +/* + * drm_extract_hdr_db - Parse the HDMI HDR extended block + * @connector: connector corresponding to the HDMI sink + * @db: start of the HDMI HDR extended block + * + * Parses the HDMI HDR extended block to extract sink info for @connector. + */ +static void +drm_extract_hdr_db(struct drm_connector *connector, const u8 *db) +{ + + u8 len = 0; + + if (!db) { + DRM_ERROR("invalid db\n"); + return; + } + + len = db[0] & 0x1f; + /* Byte 3: Electro-Optical Transfer Functions */ + connector->hdr_eotf = db[2] & 0x3F; + + /* Byte 4: Static Metadata Descriptor Type 1 */ + connector->hdr_metadata_type_one = (db[3] & BIT(0)); + + /* Byte 5: Desired Content Maximum Luminance */ + if (drm_edid_is_luminance_value_present(len, MAXIMUM_LUMINANCE)) + connector->hdr_max_luminance = + db[MAXIMUM_LUMINANCE]; + + /* Byte 6: Desired Content Max Frame-average Luminance */ + if (drm_edid_is_luminance_value_present(len, FRAME_AVERAGE_LUMINANCE)) + connector->hdr_avg_luminance = + db[FRAME_AVERAGE_LUMINANCE]; + + /* Byte 7: Desired Content Min Luminance */ + if (drm_edid_is_luminance_value_present(len, MINIMUM_LUMINANCE)) + connector->hdr_min_luminance = + db[MINIMUM_LUMINANCE]; + + connector->hdr_supported = true; + + DRM_DEBUG_KMS("HDR electro-optical %d\n", connector->hdr_eotf); + DRM_DEBUG_KMS("metadata desc 1 %d\n", connector->hdr_metadata_type_one); + DRM_DEBUG_KMS("max luminance %d\n", connector->hdr_max_luminance); + DRM_DEBUG_KMS("avg luminance %d\n", connector->hdr_avg_luminance); + DRM_DEBUG_KMS("min luminance %d\n", connector->hdr_min_luminance); +} + +/* + * drm_hdmi_extract_extended_blk_info - Parse the HDMI extended tag blocks + * @connector: connector corresponding to the HDMI sink + * @edid: handle to the EDID structure + * Parses the all extended tag blocks extract sink info for @connector. + */ +static void +drm_hdmi_extract_extended_blk_info(struct drm_connector *connector, +struct edid *edid) +{ + const u8 *cea = drm_find_cea_extension(edid); + const u8 *db = NULL; + + if (cea && cea_revision(cea) >= 3) { + int i, start, end; + + if (cea_db_offsets(cea, &start, &end)) + return; + + for_each_cea_db(cea, i, start, end) { + db = &cea[i]; + + if (cea_db_tag(db) == EXTENDED_TAG) { + DRM_DEBUG_KMS("found extended tag block = %d\n", + db[1]); + switch (db[1]) { + case VIDEO_CAPABILITY_EXTENDED_DATA_BLOCK: + drm_extract_vcdb_info(connector, db); + break; + case HDR_STATIC_METADATA_EXTENDED_DATA_BLOCK: + drm_extract_hdr_db(connector, db); + break; + default: + break; + } + } + } + } +} + +static u8 * +drm_edid_find_extended_tag_block(struct edid *edid, int blk_id) +{ + u8 *db = NULL; + u8 *cea = NULL; + + if (!edid) { + pr_err("%s: invalid input\n", __func__); + return NULL; + } + + cea = drm_find_cea_extension(edid); + + if (cea && cea_revision(cea) >= 3) { + int i, start, end; + + if (cea_db_offsets(cea, &start, &end)) + return NULL; + + for_each_cea_db(cea, i, start, end) { + db = &cea[i]; + if ((cea_db_tag(db) == EXTENDED_TAG) && + (db[1] == blk_id)) + return db; + } + } + return NULL; +} + +/* + * add_YCbCr420VDB_modes - add the modes found in Ycbcr420 VDB block + * @connector: connector corresponding to the HDMI sink + * @edid: handle to the EDID structure + * Parses the YCbCr420 VDB block and adds the modes to @connector. + */ +static int +add_YCbCr420VDB_modes(struct drm_connector *connector, struct edid *edid) +{ + + const u8 *db = NULL; + u32 i = 0; + u32 modes = 0; + u32 video_format = 0; + u8 len = 0; + + /*Find the YCbCr420 VDB*/ + db = drm_edid_find_extended_tag_block(edid, Y420_VIDEO_DATA_BLOCK); + /* Offset to byte 3 */ + if (db) { + len = db[0] & 0x1F; + db += 2; + for (i = 0; i < len - 1; i++) { + struct drm_display_mode *mode; + + video_format = *(db + i) & 0x7F; + mode = drm_display_mode_from_vic_index(connector, + db, len-1, i); + if (mode) { + DRM_DEBUG_KMS("Adding mode for vic = %d\n", + video_format); + drm_mode_probed_add(connector, mode); + modes++; + } + } + } + return modes; +} + +static void monitor_name(struct detailed_timing *t, void *data) { if (t->data.other_data.type == EDID_DETAIL_MONITOR_NAME) @@ -3277,6 +3770,9 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid) /* HDMI Vendor-Specific Data Block */ if (cea_db_is_hdmi_vsdb(db)) parse_hdmi_vsdb(connector, db); + /* HDMI Forum Vendor-Specific Data Block */ + else if (cea_db_is_hdmi_hf_vsdb(db)) + parse_hdmi_hf_vsdb(connector, db); break; default: break; @@ -3733,6 +4229,10 @@ static void drm_add_display_info(struct edid *edid, info->color_formats |= DRM_COLOR_FORMAT_YCRCB422; } + /* Extract audio and video latency fields for the sink */ + drm_hdmi_extract_vsdbs_info(connector, edid); + /* Extract info from extended tag blocks */ + drm_hdmi_extract_extended_blk_info(connector, edid); /* HDMI deep color modes supported? Assign to info, if so */ drm_assign_hdmi_deep_color_info(edid, info, connector); @@ -3775,6 +4275,148 @@ static void drm_add_display_info(struct edid *edid, info->color_formats |= DRM_COLOR_FORMAT_YCRCB422; } +static int validate_displayid(u8 *displayid, int length, int idx) +{ + int i; + u8 csum = 0; + struct displayid_hdr *base; + + base = (struct displayid_hdr *)&displayid[idx]; + + DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n", + base->rev, base->bytes, base->prod_id, base->ext_count); + + if (base->bytes + 5 > length - idx) + return -EINVAL; + for (i = idx; i <= base->bytes + 5; i++) + csum += displayid[i]; + + if (csum) { + DRM_ERROR("DisplayID checksum invalid, remainder is %d\n", + csum); + return -EINVAL; + } + return 0; +} + +static struct drm_display_mode * +drm_mode_displayid_detailed(struct drm_device *dev, +struct displayid_detailed_timings_1 *timings) +{ + struct drm_display_mode *mode; + unsigned pixel_clock = (timings->pixel_clock[0] | + (timings->pixel_clock[1] << 8) | + (timings->pixel_clock[2] << 16)); + unsigned hactive = (timings->hactive[0] | timings->hactive[1] << 8) + 1; + unsigned hblank = + (timings->hblank[0] | + timings->hblank[1] << 8) + 1; + unsigned hsync = (timings->hsync[0] | + (timings->hsync[1] & 0x7f) << 8) + 1; + unsigned hsync_width = (timings->hsw[0] | timings->hsw[1] << 8) + 1; + unsigned vactive = (timings->vactive[0] | timings->vactive[1] << 8) + 1; + unsigned vblank = + (timings->vblank[0] | + timings->vblank[1] << 8) + 1; + unsigned vsync = + (timings->vsync[0] | + (timings->vsync[1] & 0x7f) << 8) + 1; + unsigned vsync_width = (timings->vsw[0] | timings->vsw[1] << 8) + 1; + bool hsync_positive = (timings->hsync[1] >> 7) & 0x1; + bool vsync_positive = (timings->vsync[1] >> 7) & 0x1; + + mode = drm_mode_create(dev); + if (!mode) + return NULL; + + mode->clock = pixel_clock * 10; + mode->hdisplay = hactive; + mode->hsync_start = mode->hdisplay + hsync; + mode->hsync_end = mode->hsync_start + hsync_width; + mode->htotal = mode->hdisplay + hblank; + + mode->vdisplay = vactive; + mode->vsync_start = mode->vdisplay + vsync; + mode->vsync_end = mode->vsync_start + vsync_width; + mode->vtotal = mode->vdisplay + vblank; + + mode->flags = 0; + mode->flags |= hsync_positive ? + DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; + mode->flags |= vsync_positive ? + DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; + mode->type = DRM_MODE_TYPE_DRIVER; + + if (timings->flags & 0x80) + mode->type |= DRM_MODE_TYPE_PREFERRED; + mode->vrefresh = drm_mode_vrefresh(mode); + drm_mode_set_name(mode); + + return mode; +} + +static int add_displayid_detailed_1_modes(struct drm_connector *connector, + struct displayid_block *block) +{ + struct displayid_detailed_timing_block *det = + (struct displayid_detailed_timing_block *)block; + int i; + int num_timings; + struct drm_display_mode *newmode; + int num_modes = 0; + /* blocks must be multiple of 20 bytes length */ + if (block->num_bytes % 20) + return 0; + + num_timings = block->num_bytes / 20; + for (i = 0; i < num_timings; i++) { + struct displayid_detailed_timings_1 *timings = &det->timings[i]; + + newmode = drm_mode_displayid_detailed(connector->dev, timings); + if (!newmode) + continue; + + drm_mode_probed_add(connector, newmode); + num_modes++; + } + return num_modes; +} + +static int add_displayid_detailed_modes(struct drm_connector *connector, + struct edid *edid) +{ + u8 *displayid; + int ret; + int idx = 1; + int length = EDID_LENGTH; + struct displayid_block *block; + int num_modes = 0; + + displayid = drm_find_displayid_extension(edid); + if (!displayid) + return 0; + + ret = validate_displayid(displayid, length, idx); + if (ret) + return 0; + + idx += sizeof(struct displayid_hdr); + while (block = (struct displayid_block *)&displayid[idx], + idx + sizeof(struct displayid_block) <= length && + idx + sizeof(struct displayid_block) + + block->num_bytes <= length && + block->num_bytes > 0) { + idx += block->num_bytes + sizeof(struct displayid_block); + switch (block->tag) { + case DATA_BLOCK_TYPE_1_DETAILED_TIMING: + num_modes += add_displayid_detailed_1_modes(connector, + block); + break; + } + } + return num_modes; +} + /** * drm_add_edid_modes - add modes from EDID data, if available * @connector: connector we're probing @@ -3820,6 +4462,8 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) num_modes += add_established_modes(connector, edid); num_modes += add_cea_modes(connector, edid); num_modes += add_alternate_cea_modes(connector, edid); + num_modes += add_displayid_detailed_modes(connector, edid); + num_modes += add_YCbCr420VDB_modes(connector, edid); if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) num_modes += add_inferred_modes(connector, edid); @@ -4029,96 +4673,105 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame, } EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode); +static int drm_parse_tiled_block(struct drm_connector *connector, + struct displayid_block *block) +{ + struct displayid_tiled_block *tile = + (struct displayid_tiled_block *)block; + u16 w, h; + u8 tile_v_loc, tile_h_loc; + u8 num_v_tile, num_h_tile; + struct drm_tile_group *tg; + + w = tile->tile_size[0] | tile->tile_size[1] << 8; + h = tile->tile_size[2] | tile->tile_size[3] << 8; + + num_v_tile = (tile->topo[0] & 0xf) | (tile->topo[2] & 0x30); + num_h_tile = (tile->topo[0] >> 4) | ((tile->topo[2] >> 2) & 0x30); + tile_v_loc = (tile->topo[1] & 0xf) | ((tile->topo[2] & 0x3) << 4); + tile_h_loc = (tile->topo[1] >> 4) | (((tile->topo[2] >> 2) & 0x3) << 4); + + connector->has_tile = true; + if (tile->tile_cap & 0x80) + connector->tile_is_single_monitor = true; + + connector->num_h_tile = num_h_tile + 1; + connector->num_v_tile = num_v_tile + 1; + connector->tile_h_loc = tile_h_loc; + connector->tile_v_loc = tile_v_loc; + connector->tile_h_size = w + 1; + connector->tile_v_size = h + 1; + + DRM_DEBUG_KMS("tile cap 0x%x\n", tile->tile_cap); + DRM_DEBUG_KMS("tile_size %d x %d\n", w + 1, h + 1); + DRM_DEBUG_KMS("topo num tiles %dx%d, location %dx%d\n", + num_h_tile + 1, num_v_tile + 1, tile_h_loc, tile_v_loc); + DRM_DEBUG_KMS("vend %c%c%c\n", tile->topology_id[0], + tile->topology_id[1], tile->topology_id[2]); + + tg = drm_mode_get_tile_group(connector->dev, tile->topology_id); + if (!tg) + tg = drm_mode_create_tile_group(connector->dev, + tile->topology_id); + + if (!tg) + return -ENOMEM; + + if (connector->tile_group != tg) { + /* if we haven't got a pointer, + * take the reference, drop ref to old tile group + */ + if (connector->tile_group) + drm_mode_put_tile_group(connector->dev, + connector->tile_group); + + connector->tile_group = tg; + } else + /* if same tile group, then release the ref we just took. */ + drm_mode_put_tile_group(connector->dev, tg); + return 0; +} + static int drm_parse_display_id(struct drm_connector *connector, u8 *displayid, int length, bool is_edid_extension) { /* if this is an EDID extension the first byte will be 0x70 */ int idx = 0; - struct displayid_hdr *base; struct displayid_block *block; - u8 csum = 0; - int i; + int ret; if (is_edid_extension) idx = 1; - base = (struct displayid_hdr *)&displayid[idx]; - - DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n", - base->rev, base->bytes, base->prod_id, base->ext_count); - - if (base->bytes + 5 > length - idx) - return -EINVAL; - - for (i = idx; i <= base->bytes + 5; i++) { - csum += displayid[i]; - } - if (csum) { - DRM_ERROR("DisplayID checksum invalid, remainder is %d\n", csum); - return -EINVAL; - } + ret = validate_displayid(displayid, length, idx); + if (ret) + return ret; - block = (struct displayid_block *)&displayid[idx + 4]; - DRM_DEBUG_KMS("block id %d, rev %d, len %d\n", - block->tag, block->rev, block->num_bytes); - - switch (block->tag) { - case DATA_BLOCK_TILED_DISPLAY: { - struct displayid_tiled_block *tile = (struct displayid_tiled_block *)block; - - u16 w, h; - u8 tile_v_loc, tile_h_loc; - u8 num_v_tile, num_h_tile; - struct drm_tile_group *tg; - - w = tile->tile_size[0] | tile->tile_size[1] << 8; - h = tile->tile_size[2] | tile->tile_size[3] << 8; - - num_v_tile = (tile->topo[0] & 0xf) | (tile->topo[2] & 0x30); - num_h_tile = (tile->topo[0] >> 4) | ((tile->topo[2] >> 2) & 0x30); - tile_v_loc = (tile->topo[1] & 0xf) | ((tile->topo[2] & 0x3) << 4); - tile_h_loc = (tile->topo[1] >> 4) | (((tile->topo[2] >> 2) & 0x3) << 4); - - connector->has_tile = true; - if (tile->tile_cap & 0x80) - connector->tile_is_single_monitor = true; - - connector->num_h_tile = num_h_tile + 1; - connector->num_v_tile = num_v_tile + 1; - connector->tile_h_loc = tile_h_loc; - connector->tile_v_loc = tile_v_loc; - connector->tile_h_size = w + 1; - connector->tile_v_size = h + 1; - - DRM_DEBUG_KMS("tile cap 0x%x\n", tile->tile_cap); - DRM_DEBUG_KMS("tile_size %d x %d\n", w + 1, h + 1); - DRM_DEBUG_KMS("topo num tiles %dx%d, location %dx%d\n", - num_h_tile + 1, num_v_tile + 1, tile_h_loc, tile_v_loc); - DRM_DEBUG_KMS("vend %c%c%c\n", tile->topology_id[0], tile->topology_id[1], tile->topology_id[2]); - - tg = drm_mode_get_tile_group(connector->dev, tile->topology_id); - if (!tg) { - tg = drm_mode_create_tile_group(connector->dev, tile->topology_id); + idx += sizeof(struct displayid_hdr); + while (block = (struct displayid_block *)&displayid[idx], + idx + sizeof(struct displayid_block) <= length && + idx + sizeof(struct displayid_block) + + block->num_bytes <= length && + block->num_bytes > 0) { + idx += block->num_bytes + sizeof(struct displayid_block); + DRM_DEBUG_KMS("block id 0x%x, rev %d, len %d\n", + block->tag, block->rev, block->num_bytes); + + switch (block->tag) { + case DATA_BLOCK_TILED_DISPLAY: + ret = drm_parse_tiled_block(connector, block); + if (ret) + return ret; + break; + case DATA_BLOCK_TYPE_1_DETAILED_TIMING: + /* handled in mode gathering code. */ + break; + default: + DRM_DEBUG_KMS("found DisplayID tag 0x%x, unhandled\n", + block->tag); + break; } - if (!tg) - return -ENOMEM; - - if (connector->tile_group != tg) { - /* if we haven't got a pointer, - take the reference, drop ref to old tile group */ - if (connector->tile_group) { - drm_mode_put_tile_group(connector->dev, connector->tile_group); - } - connector->tile_group = tg; - } else - /* if same tile group, then release the ref we just took. */ - drm_mode_put_tile_group(connector->dev, tg); - } - break; - default: - printk("unknown displayid tag %d\n", block->tag); - break; } return 0; } diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index ebf8be80a3d9..3d0617dbc514 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -49,6 +49,7 @@ msm_drm-y := \ sde/sde_vbif.o \ sde_dbg_evtlog.o \ sde_io_util.o \ + sde_edid_parser.o # use drm gpu driver only if qcom_kgsl driver not available ifneq ($(CONFIG_QCOM_KGSL),y) @@ -102,7 +103,6 @@ msm_drm-$(CONFIG_DRM_SDE_HDMI) += \ hdmi-staging/sde_hdmi.o \ hdmi-staging/sde_hdmi_bridge.o \ hdmi-staging/sde_hdmi_audio.o \ - hdmi-staging/sde_hdmi_edid.o msm_drm-$(CONFIG_DRM_MSM_DSI_PLL) += dsi/pll/dsi_pll.o \ dsi/pll/dsi_pll_28nm.o diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 5b2c7e77771c..9ceef8f437b5 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -133,10 +133,30 @@ static int a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) OUT_PKT7(ring, CP_YIELD_ENABLE, 1); OUT_RING(ring, 0x02); + /* Record the always on counter before command execution */ + if (submit->profile_buf_iova) { + uint64_t gpuaddr = submit->profile_buf_iova + + offsetof(struct drm_msm_gem_submit_profile_buffer, + ticks_submitted); + + /* + * Set bit[30] to make this command a 64 bit write operation. + * bits[18-29] is to specify number of consecutive registers + * to copy, so set this space with 2, since we want to copy + * data from REG_A5XX_RBBM_ALWAYSON_COUNTER_LO and [HI]. + */ + OUT_PKT7(ring, CP_REG_TO_MEM, 3); + OUT_RING(ring, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO | + (1 << 30) | (2 << 18)); + OUT_RING(ring, lower_32_bits(gpuaddr)); + OUT_RING(ring, upper_32_bits(gpuaddr)); + } + /* Submit the commands */ for (i = 0; i < submit->nr_cmds; i++) { switch (submit->cmd[i].type) { case MSM_SUBMIT_CMD_IB_TARGET_BUF: + case MSM_SUBMIT_CMD_PROFILE_BUF: break; case MSM_SUBMIT_CMD_BUF: OUT_PKT7(ring, CP_INDIRECT_BUFFER_PFE, 3); @@ -164,6 +184,19 @@ static int a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) OUT_PKT7(ring, CP_YIELD_ENABLE, 1); OUT_RING(ring, 0x01); + /* Record the always on counter after command execution */ + if (submit->profile_buf_iova) { + uint64_t gpuaddr = submit->profile_buf_iova + + offsetof(struct drm_msm_gem_submit_profile_buffer, + ticks_retired); + + OUT_PKT7(ring, CP_REG_TO_MEM, 3); + OUT_RING(ring, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO | + (1 << 30) | (2 << 18)); + OUT_RING(ring, lower_32_bits(gpuaddr)); + OUT_RING(ring, upper_32_bits(gpuaddr)); + } + /* Write the fence to the scratch register */ OUT_PKT4(ring, REG_A5XX_CP_SCRATCH_REG(2), 1); OUT_RING(ring, submit->fence); @@ -193,6 +226,35 @@ static int a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) /* Set bit 0 to trigger an interrupt on preempt complete */ OUT_RING(ring, 0x01); + if (submit->profile_buf_iova) { + unsigned long flags; + uint64_t ktime; + struct drm_msm_gem_submit_profile_buffer *profile_buf = + submit->profile_buf_vaddr; + + /* + * With this profiling, we are trying to create closest + * possible mapping between the CPU time domain(monotonic clock) + * and the GPU time domain(ticks). In order to make this + * happen, we need to briefly turn off interrupts to make sure + * interrupts do not run between collecting these two samples. + */ + local_irq_save(flags); + + profile_buf->ticks_queued = gpu_read64(gpu, + REG_A5XX_RBBM_ALWAYSON_COUNTER_LO, + REG_A5XX_RBBM_ALWAYSON_COUNTER_HI); + + ktime = ktime_get_raw_ns(); + + local_irq_restore(flags); + + do_div(ktime, NSEC_PER_SEC); + + profile_buf->queue_time = ktime; + profile_buf->submit_time = ktime; + } + a5xx_flush(gpu, ring); /* Check to see if we need to start preemption */ @@ -1039,8 +1101,10 @@ static irqreturn_t a5xx_irq(struct msm_gpu *gpu) if (status & A5XX_RBBM_INT_0_MASK_GPMU_VOLTAGE_DROOP) a5xx_gpmu_err_irq(gpu); - if (status & A5XX_RBBM_INT_0_MASK_CP_CACHE_FLUSH_TS) + if (status & A5XX_RBBM_INT_0_MASK_CP_CACHE_FLUSH_TS) { + a5xx_preempt_trigger(gpu); msm_gpu_retire(gpu); + } if (status & A5XX_RBBM_INT_0_MASK_CP_SW) a5xx_preempt_irq(gpu); diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 969ed810ce9d..19267b2a3b49 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -183,6 +183,7 @@ int adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) case MSM_SUBMIT_CMD_IB_TARGET_BUF: /* ignore IB-targets */ break; + case MSM_SUBMIT_CMD_PROFILE_BUF: case MSM_SUBMIT_CMD_CTX_RESTORE_BUF: break; case MSM_SUBMIT_CMD_BUF: diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index 347b78886b24..6a6d02c5444d 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -32,6 +32,22 @@ static DEFINE_MUTEX(sde_hdmi_list_lock); static LIST_HEAD(sde_hdmi_list); +/* HDMI SCDC register offsets */ +#define HDMI_SCDC_UPDATE_0 0x10 +#define HDMI_SCDC_UPDATE_1 0x11 +#define HDMI_SCDC_TMDS_CONFIG 0x20 +#define HDMI_SCDC_SCRAMBLER_STATUS 0x21 +#define HDMI_SCDC_CONFIG_0 0x30 +#define HDMI_SCDC_STATUS_FLAGS_0 0x40 +#define HDMI_SCDC_STATUS_FLAGS_1 0x41 +#define HDMI_SCDC_ERR_DET_0_L 0x50 +#define HDMI_SCDC_ERR_DET_0_H 0x51 +#define HDMI_SCDC_ERR_DET_1_L 0x52 +#define HDMI_SCDC_ERR_DET_1_H 0x53 +#define HDMI_SCDC_ERR_DET_2_L 0x54 +#define HDMI_SCDC_ERR_DET_2_H 0x55 +#define HDMI_SCDC_ERR_DET_CHECKSUM 0x56 + static const struct of_device_id sde_hdmi_dt_match[] = { {.compatible = "qcom,hdmi-display"}, {} @@ -69,16 +85,328 @@ static ssize_t _sde_hdmi_debugfs_dump_info_read(struct file *file, return len; } +static ssize_t _sde_hdmi_debugfs_edid_modes_read(struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + struct sde_hdmi *display = file->private_data; + char *buf; + u32 len = 0; + struct drm_connector *connector; + u32 mode_count = 0; + struct drm_display_mode *mode; + + if (!display) + return -ENODEV; + + if (!display->ctrl.ctrl || + !display->ctrl.ctrl->connector) { + SDE_ERROR("sde_hdmi=%p or hdmi or connector is NULL\n", + display); + return -ENOMEM; + } + + if (*ppos) + return 0; + + connector = display->ctrl.ctrl->connector; + + list_for_each_entry(mode, &connector->modes, head) { + mode_count++; + } + + /* Adding one more to store title */ + mode_count++; + + buf = kzalloc((mode_count * sizeof(*mode)), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len += snprintf(buf + len, PAGE_SIZE - len, + "name refresh (Hz) hdisp hss hse htot vdisp"); + + len += snprintf(buf + len, PAGE_SIZE - len, + " vss vse vtot flags\n"); + + list_for_each_entry(mode, &connector->modes, head) { + len += snprintf(buf + len, SZ_4K - len, + "%s %d %d %d %d %d %d %d %d %d 0x%x\n", + mode->name, mode->vrefresh, mode->hdisplay, + mode->hsync_start, mode->hsync_end, mode->htotal, + mode->vdisplay, mode->vsync_start, mode->vsync_end, + mode->vtotal, mode->flags); + } + + if (copy_to_user(buff, buf, len)) { + kfree(buf); + return -EFAULT; + } + + *ppos += len; + + kfree(buf); + return len; +} + +static ssize_t _sde_hdmi_debugfs_edid_vsdb_info_read(struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + struct sde_hdmi *display = file->private_data; + char buf[200]; + u32 len = 0; + struct drm_connector *connector; + + if (!display) + return -ENODEV; + + if (!display->ctrl.ctrl || + !display->ctrl.ctrl->connector) { + SDE_ERROR("sde_hdmi=%p or hdmi or connector is NULL\n", + display); + return -ENOMEM; + } + + SDE_HDMI_DEBUG("%s +", __func__); + if (*ppos) + return 0; + + connector = display->ctrl.ctrl->connector; + len += snprintf(buf + len, sizeof(buf) - len, + "max_tmds_clock = %d\n", + connector->max_tmds_clock); + len += snprintf(buf + len, sizeof(buf) - len, + "latency_present %d %d\n", + connector->latency_present[0], + connector->latency_present[1]); + len += snprintf(buf + len, sizeof(buf) - len, + "video_latency %d %d\n", + connector->video_latency[0], + connector->video_latency[1]); + len += snprintf(buf + len, sizeof(buf) - len, + "audio_latency %d %d\n", + connector->audio_latency[0], + connector->audio_latency[1]); + len += snprintf(buf + len, sizeof(buf) - len, + "dvi_dual %d\n", + (int)connector->dvi_dual); + + if (copy_to_user(buff, buf, len)) + return -EFAULT; + + *ppos += len; + SDE_HDMI_DEBUG("%s - ", __func__); + return len; +} + +static ssize_t _sde_hdmi_debugfs_edid_hdr_info_read(struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + struct sde_hdmi *display = file->private_data; + char buf[200]; + u32 len = 0; + struct drm_connector *connector; + + if (!display) + return -ENODEV; + + if (!display->ctrl.ctrl || + !display->ctrl.ctrl->connector) { + SDE_ERROR("sde_hdmi=%p or hdmi or connector is NULL\n", + display); + return -ENOMEM; + } + + SDE_HDMI_DEBUG("%s +", __func__); + if (*ppos) + return 0; + + connector = display->ctrl.ctrl->connector; + len += snprintf(buf, sizeof(buf), "hdr_eotf = %d\n" + "hdr_metadata_type_one %d\n" + "hdr_max_luminance %d\n" + "hdr_avg_luminance %d\n" + "hdr_min_luminance %d\n" + "hdr_supported %d\n", + connector->hdr_eotf, + connector->hdr_metadata_type_one, + connector->hdr_max_luminance, + connector->hdr_avg_luminance, + connector->hdr_min_luminance, + (int)connector->hdr_supported); + + if (copy_to_user(buff, buf, len)) + return -EFAULT; + + *ppos += len; + SDE_HDMI_DEBUG("%s - ", __func__); + return len; +} + +static ssize_t _sde_hdmi_debugfs_edid_hfvsdb_info_read(struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + struct sde_hdmi *display = file->private_data; + char buf[200]; + u32 len = 0; + struct drm_connector *connector; + + if (!display) + return -ENODEV; + + if (!display->ctrl.ctrl || + !display->ctrl.ctrl->connector) { + SDE_ERROR("sde_hdmi=%p or hdmi or connector is NULL\n", + display); + return -ENOMEM; + } + + SDE_HDMI_DEBUG("%s +", __func__); + if (*ppos) + return 0; + + connector = display->ctrl.ctrl->connector; + len += snprintf(buf, PAGE_SIZE - len, "max_tmds_char = %d\n" + "scdc_present %d\n" + "rr_capable %d\n" + "supports_scramble %d\n" + "flags_3d %d\n", + connector->max_tmds_char, + (int)connector->scdc_present, + (int)connector->rr_capable, + (int)connector->supports_scramble, + connector->flags_3d); + + if (copy_to_user(buff, buf, len)) + return -EFAULT; + + *ppos += len; + return len; +} + +static ssize_t _sde_hdmi_debugfs_edid_vcdb_info_read(struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + struct sde_hdmi *display = file->private_data; + char buf[100]; + u32 len = 0; + struct drm_connector *connector; + + if (!display) + return -ENODEV; + + if (!display->ctrl.ctrl || + !display->ctrl.ctrl->connector) { + SDE_ERROR("sde_hdmi=%p or hdmi or connector is NULL\n", + display); + return -ENOMEM; + } + + SDE_HDMI_DEBUG("%s +", __func__); + if (*ppos) + return 0; + + connector = display->ctrl.ctrl->connector; + len += snprintf(buf, PAGE_SIZE - len, "pt_scan_info = %d\n" + "it_scan_info = %d\n" + "ce_scan_info = %d\n", + (int)connector->pt_scan_info, + (int)connector->it_scan_info, + (int)connector->ce_scan_info); + + if (copy_to_user(buff, buf, len)) + return -EFAULT; + + *ppos += len; + SDE_HDMI_DEBUG("%s - ", __func__); + return len; +} + +static ssize_t _sde_hdmi_edid_vendor_name_read(struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + struct sde_hdmi *display = file->private_data; + char buf[100]; + u32 len = 0; + struct drm_connector *connector; + + if (!display) + return -ENODEV; + + if (!display->ctrl.ctrl || + !display->ctrl.ctrl->connector) { + SDE_ERROR("sde_hdmi=%p or hdmi or connector is NULL\n", + display); + return -ENOMEM; + } + + SDE_HDMI_DEBUG("%s +", __func__); + if (*ppos) + return 0; + + connector = display->ctrl.ctrl->connector; + len += snprintf(buf, PAGE_SIZE - len, "Vendor ID is %s\n", + display->edid_ctrl->vendor_id); + + if (copy_to_user(buff, buf, len)) + return -EFAULT; + + *ppos += len; + SDE_HDMI_DEBUG("%s - ", __func__); + return len; +} static const struct file_operations dump_info_fops = { .open = simple_open, .read = _sde_hdmi_debugfs_dump_info_read, }; +static const struct file_operations edid_modes_fops = { + .open = simple_open, + .read = _sde_hdmi_debugfs_edid_modes_read, +}; + +static const struct file_operations edid_vsdb_info_fops = { + .open = simple_open, + .read = _sde_hdmi_debugfs_edid_vsdb_info_read, +}; + +static const struct file_operations edid_hdr_info_fops = { + .open = simple_open, + .read = _sde_hdmi_debugfs_edid_hdr_info_read, +}; + +static const struct file_operations edid_hfvsdb_info_fops = { + .open = simple_open, + .read = _sde_hdmi_debugfs_edid_hfvsdb_info_read, +}; + +static const struct file_operations edid_vcdb_info_fops = { + .open = simple_open, + .read = _sde_hdmi_debugfs_edid_vcdb_info_read, +}; + +static const struct file_operations edid_vendor_name_fops = { + .open = simple_open, + .read = _sde_hdmi_edid_vendor_name_read, +}; + static int _sde_hdmi_debugfs_init(struct sde_hdmi *display) { int rc = 0; - struct dentry *dir, *dump_file; + struct dentry *dir, *dump_file, *edid_modes; + struct dentry *edid_vsdb_info, *edid_hdr_info, *edid_hfvsdb_info; + struct dentry *edid_vcdb_info, *edid_vendor_name; dir = debugfs_create_dir(display->name, NULL); if (!dir) { @@ -100,6 +428,83 @@ static int _sde_hdmi_debugfs_init(struct sde_hdmi *display) goto error_remove_dir; } + edid_modes = debugfs_create_file("edid_modes", + 0444, + dir, + display, + &edid_modes_fops); + + if (IS_ERR_OR_NULL(edid_modes)) { + rc = PTR_ERR(edid_modes); + SDE_ERROR("[%s]debugfs create file failed, rc=%d\n", + display->name, rc); + goto error_remove_dir; + } + + edid_vsdb_info = debugfs_create_file("edid_vsdb_info", + 0444, + dir, + display, + &edid_vsdb_info_fops); + + if (IS_ERR_OR_NULL(edid_vsdb_info)) { + rc = PTR_ERR(edid_vsdb_info); + SDE_ERROR("[%s]debugfs create file failed, rc=%d\n", + display->name, rc); + goto error_remove_dir; + } + + edid_hdr_info = debugfs_create_file("edid_hdr_info", + 0444, + dir, + display, + &edid_hdr_info_fops); + if (IS_ERR_OR_NULL(edid_hdr_info)) { + rc = PTR_ERR(edid_hdr_info); + SDE_ERROR("[%s]debugfs create file failed, rc=%d\n", + display->name, rc); + goto error_remove_dir; + } + + edid_hfvsdb_info = debugfs_create_file("edid_hfvsdb_info", + 0444, + dir, + display, + &edid_hfvsdb_info_fops); + + if (IS_ERR_OR_NULL(edid_hfvsdb_info)) { + rc = PTR_ERR(edid_hfvsdb_info); + SDE_ERROR("[%s]debugfs create file failed, rc=%d\n", + display->name, rc); + goto error_remove_dir; + } + + edid_vcdb_info = debugfs_create_file("edid_vcdb_info", + 0444, + dir, + display, + &edid_vcdb_info_fops); + + if (IS_ERR_OR_NULL(edid_vcdb_info)) { + rc = PTR_ERR(edid_vcdb_info); + SDE_ERROR("[%s]debugfs create file failed, rc=%d\n", + display->name, rc); + goto error_remove_dir; + } + + edid_vendor_name = debugfs_create_file("edid_vendor_name", + 0444, + dir, + display, + &edid_vendor_name_fops); + + if (IS_ERR_OR_NULL(edid_vendor_name)) { + rc = PTR_ERR(edid_vendor_name); + SDE_ERROR("[%s]debugfs create file failed, rc=%d\n", + display->name, rc); + goto error_remove_dir; + } + display->root = dir; return rc; error_remove_dir: @@ -395,20 +800,28 @@ static void _sde_hdmi_hotplug_work(struct work_struct *work) struct sde_hdmi *sde_hdmi = container_of(work, struct sde_hdmi, hpd_work); struct drm_connector *connector; + struct hdmi *hdmi = NULL; + u32 hdmi_ctrl; if (!sde_hdmi || !sde_hdmi->ctrl.ctrl || - !sde_hdmi->ctrl.ctrl->connector) { + !sde_hdmi->ctrl.ctrl->connector || + !sde_hdmi->edid_ctrl) { SDE_ERROR("sde_hdmi=%p or hdmi or connector is NULL\n", sde_hdmi); return; } - + hdmi = sde_hdmi->ctrl.ctrl; connector = sde_hdmi->ctrl.ctrl->connector; - if (sde_hdmi->connected) - sde_hdmi_get_edid(connector, sde_hdmi); - else - sde_hdmi_free_edid(sde_hdmi); + if (sde_hdmi->connected) { + hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL); + hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE); + sde_get_edid(connector, hdmi->i2c, + (void **)&sde_hdmi->edid_ctrl); + hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl); + hdmi->hdmi_mode = sde_detect_hdmi_monitor(sde_hdmi->edid_ctrl); + } else + sde_free_edid((void **)&sde_hdmi->edid_ctrl); sde_hdmi_notify_clients(connector, sde_hdmi->connected); drm_helper_hpd_irq_event(connector->dev); @@ -431,7 +844,7 @@ static void _sde_hdmi_connector_irq(struct sde_hdmi *sde_hdmi) hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, HDMI_HPD_INT_CTRL_INT_ACK); - DRM_DEBUG("status=%04x, ctrl=%04x", hpd_int_status, + SDE_HDMI_DEBUG("status=%04x, ctrl=%04x", hpd_int_status, hpd_int_ctrl); /* detect disconnect if we are connected or visa versa: */ @@ -504,11 +917,11 @@ static int _sde_hdmi_get_audio_edid_blk(struct platform_device *pdev, return -ENODEV; } - blk->audio_data_blk = display->edid.audio_data_block; - blk->audio_data_blk_size = display->edid.adb_size; + blk->audio_data_blk = display->edid_ctrl->audio_data_block; + blk->audio_data_blk_size = display->edid_ctrl->adb_size; - blk->spk_alloc_data_blk = display->edid.spkr_alloc_data_block; - blk->spk_alloc_data_blk_size = display->edid.sadb_size; + blk->spk_alloc_data_blk = display->edid_ctrl->spkr_alloc_data_block; + blk->spk_alloc_data_blk_size = display->edid_ctrl->sadb_size; return 0; } @@ -634,10 +1047,249 @@ void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on) hdmi_write(hdmi, REG_HDMI_CTRL, ctrl); spin_unlock_irqrestore(&hdmi->reg_lock, flags); - DRM_DEBUG("HDMI Core: %s, HDMI_CTRL=0x%08x\n", + SDE_HDMI_DEBUG("HDMI Core: %s, HDMI_CTRL=0x%08x\n", power_on ? "Enable" : "Disable", ctrl); } +int sde_hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset, + u8 *data, u16 data_len) +{ + int rc; + int retry = 5; + struct i2c_msg msgs[] = { + { + .addr = addr >> 1, + .flags = 0, + .len = 1, + .buf = &offset, + }, { + .addr = addr >> 1, + .flags = I2C_M_RD, + .len = data_len, + .buf = data, + } + }; + + SDE_HDMI_DEBUG("Start DDC read"); + retry: + rc = i2c_transfer(hdmi->i2c, msgs, 2); + + retry--; + if (rc == 2) + rc = 0; + else if (retry > 0) + goto retry; + else + rc = -EIO; + + SDE_HDMI_DEBUG("End DDC read %d", rc); + + return rc; +} + +#define DDC_WRITE_MAX_BYTE_NUM 32 + +int sde_hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset, + u8 *data, u16 data_len) +{ + int rc; + int retry = 10; + u8 buf[DDC_WRITE_MAX_BYTE_NUM]; + struct i2c_msg msgs[] = { + { + .addr = addr >> 1, + .flags = 0, + .len = 1, + } + }; + + SDE_HDMI_DEBUG("Start DDC write"); + if (data_len > (DDC_WRITE_MAX_BYTE_NUM - 1)) { + SDE_ERROR("%s: write size too big\n", __func__); + return -ERANGE; + } + + buf[0] = offset; + memcpy(&buf[1], data, data_len); + msgs[0].buf = buf; + msgs[0].len = data_len + 1; + retry: + rc = i2c_transfer(hdmi->i2c, msgs, 1); + + retry--; + if (rc == 1) + rc = 0; + else if (retry > 0) + goto retry; + else + rc = -EIO; + + SDE_HDMI_DEBUG("End DDC write %d", rc); + + return rc; +} + +int sde_hdmi_scdc_read(struct hdmi *hdmi, u32 data_type, u32 *val) +{ + int rc = 0; + u8 data_buf[2] = {0}; + u16 dev_addr, data_len; + u8 offset; + + if (!hdmi || !hdmi->i2c || !val) { + SDE_ERROR("Bad Parameters\n"); + return -EINVAL; + } + + if (data_type >= HDMI_TX_SCDC_MAX) { + SDE_ERROR("Unsupported data type\n"); + return -EINVAL; + } + + dev_addr = 0xA8; + + switch (data_type) { + case HDMI_TX_SCDC_SCRAMBLING_STATUS: + data_len = 1; + offset = HDMI_SCDC_SCRAMBLER_STATUS; + break; + case HDMI_TX_SCDC_SCRAMBLING_ENABLE: + case HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE: + data_len = 1; + offset = HDMI_SCDC_TMDS_CONFIG; + break; + case HDMI_TX_SCDC_CLOCK_DET_STATUS: + case HDMI_TX_SCDC_CH0_LOCK_STATUS: + case HDMI_TX_SCDC_CH1_LOCK_STATUS: + case HDMI_TX_SCDC_CH2_LOCK_STATUS: + data_len = 1; + offset = HDMI_SCDC_STATUS_FLAGS_0; + break; + case HDMI_TX_SCDC_CH0_ERROR_COUNT: + data_len = 2; + offset = HDMI_SCDC_ERR_DET_0_L; + break; + case HDMI_TX_SCDC_CH1_ERROR_COUNT: + data_len = 2; + offset = HDMI_SCDC_ERR_DET_1_L; + break; + case HDMI_TX_SCDC_CH2_ERROR_COUNT: + data_len = 2; + offset = HDMI_SCDC_ERR_DET_2_L; + break; + case HDMI_TX_SCDC_READ_ENABLE: + data_len = 1; + offset = HDMI_SCDC_CONFIG_0; + break; + default: + break; + } + + rc = sde_hdmi_ddc_read(hdmi, dev_addr, offset, data_buf, data_len); + if (rc) { + SDE_ERROR("DDC Read failed for %d\n", data_type); + return rc; + } + + switch (data_type) { + case HDMI_TX_SCDC_SCRAMBLING_STATUS: + *val = (data_buf[0] & BIT(0)) ? 1 : 0; + break; + case HDMI_TX_SCDC_SCRAMBLING_ENABLE: + *val = (data_buf[0] & BIT(0)) ? 1 : 0; + break; + case HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE: + *val = (data_buf[0] & BIT(1)) ? 1 : 0; + break; + case HDMI_TX_SCDC_CLOCK_DET_STATUS: + *val = (data_buf[0] & BIT(0)) ? 1 : 0; + break; + case HDMI_TX_SCDC_CH0_LOCK_STATUS: + *val = (data_buf[0] & BIT(1)) ? 1 : 0; + break; + case HDMI_TX_SCDC_CH1_LOCK_STATUS: + *val = (data_buf[0] & BIT(2)) ? 1 : 0; + break; + case HDMI_TX_SCDC_CH2_LOCK_STATUS: + *val = (data_buf[0] & BIT(3)) ? 1 : 0; + break; + case HDMI_TX_SCDC_CH0_ERROR_COUNT: + case HDMI_TX_SCDC_CH1_ERROR_COUNT: + case HDMI_TX_SCDC_CH2_ERROR_COUNT: + if (data_buf[1] & BIT(7)) + *val = (data_buf[0] | ((data_buf[1] & 0x7F) << 8)); + else + *val = 0; + break; + case HDMI_TX_SCDC_READ_ENABLE: + *val = (data_buf[0] & BIT(0)) ? 1 : 0; + break; + default: + break; + } + + return 0; +} + +int sde_hdmi_scdc_write(struct hdmi *hdmi, u32 data_type, u32 val) +{ + int rc = 0; + u8 data_buf[2] = {0}; + u8 read_val = 0; + u16 dev_addr, data_len; + u8 offset; + + if (!hdmi || !hdmi->i2c) { + SDE_ERROR("Bad Parameters\n"); + return -EINVAL; + } + + if (data_type >= HDMI_TX_SCDC_MAX) { + SDE_ERROR("Unsupported data type\n"); + return -EINVAL; + } + + dev_addr = 0xA8; + + switch (data_type) { + case HDMI_TX_SCDC_SCRAMBLING_ENABLE: + case HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE: + dev_addr = 0xA8; + data_len = 1; + offset = HDMI_SCDC_TMDS_CONFIG; + rc = sde_hdmi_ddc_read(hdmi, dev_addr, offset, &read_val, + data_len); + if (rc) { + SDE_ERROR("scdc read failed\n"); + return rc; + } + if (data_type == HDMI_TX_SCDC_SCRAMBLING_ENABLE) { + data_buf[0] = ((((u8)(read_val & 0xFF)) & (~BIT(0))) | + ((u8)(val & BIT(0)))); + } else { + data_buf[0] = ((((u8)(read_val & 0xFF)) & (~BIT(1))) | + (((u8)(val & BIT(0))) << 1)); + } + break; + case HDMI_TX_SCDC_READ_ENABLE: + data_len = 1; + offset = HDMI_SCDC_CONFIG_0; + data_buf[0] = (u8)(val & 0x1); + break; + default: + SDE_ERROR("Cannot write to read only reg (%d)\n", + data_type); + return -EINVAL; + } + + rc = sde_hdmi_ddc_write(hdmi, dev_addr, offset, data_buf, data_len); + if (rc) { + SDE_ERROR("DDC Read failed for %d\n", data_type); + return rc; + } + return 0; +} + int sde_hdmi_get_info(struct msm_display_info *info, void *display) { @@ -746,7 +1398,7 @@ int sde_hdmi_connector_post_init(struct drm_connector *connector, if (info) sde_kms_info_add_keystr(info, - "DISPLAY_TYPE", + "display type", sde_hdmi->display_type); hdmi->connector = connector; @@ -775,8 +1427,6 @@ sde_hdmi_connector_detect(struct drm_connector *connector, return status; } - SDE_DEBUG("\n"); - /* get display dsi_info */ memset(&info, 0x0, sizeof(info)); rc = sde_hdmi_get_info(&info, display); @@ -797,25 +1447,6 @@ sde_hdmi_connector_detect(struct drm_connector *connector, return status; } -int _sde_hdmi_update_modes(struct drm_connector *connector, - struct sde_hdmi *display) -{ - int rc = 0; - struct hdmi_edid_ctrl *edid_ctrl = &display->edid; - - if (edid_ctrl->edid) { - drm_mode_connector_update_edid_property(connector, - edid_ctrl->edid); - - rc = drm_add_edid_modes(connector, edid_ctrl->edid); - return rc; - } - - drm_mode_connector_update_edid_property(connector, NULL); - - return rc; -} - int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display) { struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display; @@ -828,8 +1459,6 @@ int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display) return 0; } - SDE_DEBUG("\n"); - if (hdmi_display->non_pluggable) { list_for_each_entry(mode, &hdmi_display->mode_list, head) { m = drm_mode_duplicate(connector->dev, mode); @@ -842,7 +1471,9 @@ int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display) } ret = hdmi_display->num_of_modes; } else { - ret = _sde_hdmi_update_modes(connector, display); + /* pluggable case assumes EDID is read when HPD */ + ret = _sde_edid_update_modes(connector, + hdmi_display->edid_ctrl); } return ret; @@ -864,8 +1495,6 @@ enum drm_mode_status sde_hdmi_mode_valid(struct drm_connector *connector, return 0; } - SDE_DEBUG("\n"); - hdmi = hdmi_display->ctrl.ctrl; priv = connector->dev->dev_private; kms = priv->kms; @@ -873,7 +1502,7 @@ enum drm_mode_status sde_hdmi_mode_valid(struct drm_connector *connector, actual = kms->funcs->round_pixclk(kms, requested, hdmi->encoder); - SDE_DEBUG("requested=%ld, actual=%ld", requested, actual); + SDE_HDMI_DEBUG("requested=%ld, actual=%ld", requested, actual); if (actual != requested) return MODE_CLOCK_RANGE; @@ -909,7 +1538,7 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) struct msm_drm_private *priv = NULL; struct platform_device *pdev = to_platform_device(dev); - SDE_ERROR("E\n"); + SDE_HDMI_DEBUG(" %s +\n", __func__); if (!dev || !pdev || !master) { pr_err("invalid param(s), dev %pK, pdev %pK, master %pK\n", dev, pdev, master); @@ -941,18 +1570,20 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) goto error; } - rc = sde_hdmi_edid_init(display); - if (rc) { - SDE_ERROR("[%s]Ext Disp init failed, rc=%d\n", - display->name, rc); + display->edid_ctrl = sde_edid_init(); + if (!display->edid_ctrl) { + SDE_ERROR("[%s]sde edid init failed\n", + display->name); + rc = -ENOMEM; goto error; } display_ctrl = &display->ctrl; display_ctrl->ctrl = priv->hdmi; - SDE_ERROR("display_ctrl->ctrl=%p\n", display_ctrl->ctrl); display->drm_dev = drm; + mutex_unlock(&display->display_lock); + return rc; error: (void)_sde_hdmi_debugfs_deinit(display); debug_error: @@ -978,7 +1609,7 @@ static void sde_hdmi_unbind(struct device *dev, struct device *master, } mutex_lock(&display->display_lock); (void)_sde_hdmi_debugfs_deinit(display); - (void)sde_hdmi_edid_deinit(display); + (void)sde_edid_deinit((void **)&display->edid_ctrl); display->drm_dev = NULL; mutex_unlock(&display->display_lock); } diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h index 869d1bebf9db..bb3061a6ed00 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h @@ -25,9 +25,13 @@ #include <drm/drm_crtc.h> #include "hdmi.h" -#define MAX_NUMBER_ADB 5 -#define MAX_AUDIO_DATA_BLOCK_SIZE 30 -#define MAX_SPKR_ALLOC_DATA_BLOCK_SIZE 3 +#include "sde_edid_parser.h" + +#ifdef HDMI_DEBUG_ENABLE +#define SDE_HDMI_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args) +#else +#define SDE_HDMI_DEBUG(fmt, args...) SDE_DEBUG(fmt, ##args) +#endif /** * struct sde_hdmi_info - defines hdmi display properties @@ -64,14 +68,6 @@ struct sde_hdmi_ctrl { u32 hdmi_ctrl_idx; }; -struct hdmi_edid_ctrl { - struct edid *edid; - u8 audio_data_block[MAX_NUMBER_ADB * MAX_AUDIO_DATA_BLOCK_SIZE]; - int adb_size; - u8 spkr_alloc_data_block[MAX_SPKR_ALLOC_DATA_BLOCK_SIZE]; - int sadb_size; -}; - /** * struct sde_hdmi - hdmi display information * @pdev: Pointer to platform device. @@ -102,7 +98,7 @@ struct sde_hdmi { struct platform_device *ext_pdev; struct msm_ext_disp_init_data ext_audio_data; - struct hdmi_edid_ctrl edid; + struct sde_edid_ctrl *edid_ctrl; bool non_pluggable; u32 num_of_modes; @@ -116,6 +112,38 @@ struct sde_hdmi { struct dentry *root; }; +/** + * hdmi_tx_scdc_access_type() - hdmi 2.0 DDC functionalities. + */ +enum hdmi_tx_scdc_access_type { + HDMI_TX_SCDC_SCRAMBLING_STATUS, + HDMI_TX_SCDC_SCRAMBLING_ENABLE, + HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE, + HDMI_TX_SCDC_CLOCK_DET_STATUS, + HDMI_TX_SCDC_CH0_LOCK_STATUS, + HDMI_TX_SCDC_CH1_LOCK_STATUS, + HDMI_TX_SCDC_CH2_LOCK_STATUS, + HDMI_TX_SCDC_CH0_ERROR_COUNT, + HDMI_TX_SCDC_CH1_ERROR_COUNT, + HDMI_TX_SCDC_CH2_ERROR_COUNT, + HDMI_TX_SCDC_READ_ENABLE, + HDMI_TX_SCDC_MAX, +}; + +#define HDMI_KHZ_TO_HZ 1000 +#define HDMI_MHZ_TO_HZ 1000000 +/** + * hdmi_tx_ddc_timer_type() - hdmi DDC timer functionalities. + */ +enum hdmi_tx_ddc_timer_type { + HDMI_TX_DDC_TIMER_HDCP2P2_RD_MSG, + HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS, + HDMI_TX_DDC_TIMER_UPDATE_FLAGS, + HDMI_TX_DDC_TIMER_STATUS_FLAGS, + HDMI_TX_DDC_TIMER_CED, + HDMI_TX_DDC_TIMER_MAX, + }; + #ifdef CONFIG_DRM_SDE_HDMI /** * sde_hdmi_get_num_of_displays() - returns number of display devices @@ -259,6 +287,52 @@ struct drm_bridge *sde_hdmi_bridge_init(struct hdmi *hdmi); void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on); /** + * sde_hdmi_ddc_read() - common hdmi ddc read API. + * @hdmi: Handle to the hdmi. + * @addr: Command address. + * @offset: Command offset. + * @data: Data buffer for read back. + * @data_len: Data buffer length. + * + * Return: error code. + */ +int sde_hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset, + u8 *data, u16 data_len); + +/** + * sde_hdmi_ddc_write() - common hdmi ddc write API. + * @hdmi: Handle to the hdmi. + * @addr: Command address. + * @offset: Command offset. + * @data: Data buffer for write. + * @data_len: Data buffer length. + * + * Return: error code. + */ +int sde_hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset, + u8 *data, u16 data_len); + +/** + * sde_hdmi_scdc_read() - hdmi 2.0 ddc read API. + * @hdmi: Handle to the hdmi. + * @data_type: DDC data type, refer to enum hdmi_tx_scdc_access_type. + * @val: Read back value. + * + * Return: error code. + */ +int sde_hdmi_scdc_read(struct hdmi *hdmi, u32 data_type, u32 *val); + +/** + * sde_hdmi_scdc_write() - hdmi 2.0 ddc write API. + * @hdmi: Handle to the hdmi. + * @data_type: DDC data type, refer to enum hdmi_tx_scdc_access_type. + * @val: Value write through DDC. + * + * Return: error code. + */ +int sde_hdmi_scdc_write(struct hdmi *hdmi, u32 data_type, u32 val); + +/** * sde_hdmi_audio_on() - enable hdmi audio. * @hdmi: Handle to the hdmi. * @params: audio setup parameters from codec. @@ -305,40 +379,6 @@ void sde_hdmi_notify_clients(struct drm_connector *connector, void sde_hdmi_ack_state(struct drm_connector *connector, enum drm_connector_status status); -/** - * sde_hdmi_edid_init() - init edid structure. - * @display: Handle to the sde_hdmi. - * - * Return: error code. - */ -int sde_hdmi_edid_init(struct sde_hdmi *display); - -/** - * sde_hdmi_edid_deinit() - deinit edid structure. - * @display: Handle to the sde_hdmi. - * - * Return: error code. - */ -int sde_hdmi_edid_deinit(struct sde_hdmi *display); - -/** - * sde_hdmi_get_edid() - get edid info. - * @connector: Handle to the drm_connector. - * @display: Handle to the sde_hdmi. - * - * Return: void. - */ -void sde_hdmi_get_edid(struct drm_connector *connector, - struct sde_hdmi *display); - -/** - * sde_hdmi_free_edid() - free edid structure. - * @display: Handle to the sde_hdmi. - * - * Return: error code. - */ -int sde_hdmi_free_edid(struct sde_hdmi *display); - #else /*#ifdef CONFIG_DRM_SDE_HDMI*/ static inline u32 sde_hdmi_get_num_of_displays(void) diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c index 681dca501f9b..c76e42c67885 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c @@ -28,6 +28,13 @@ struct sde_hdmi_bridge { }; #define to_hdmi_bridge(x) container_of(x, struct sde_hdmi_bridge, base) +/* TX major version that supports scrambling */ +#define HDMI_TX_SCRAMBLER_MIN_TX_VERSION 0x04 +#define HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ 340000 +#define HDMI_TX_SCRAMBLER_TIMEOUT_MSEC 200 +/* default hsyncs for 4k@60 for 200ms */ +#define HDMI_DEFAULT_TIMEOUT_HSYNC 28571 + /* for AVI program */ #define HDMI_AVI_INFOFRAME_BUFFER_SIZE \ (HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE) @@ -101,6 +108,265 @@ static void _sde_hdmi_bridge_power_off(struct drm_bridge *bridge) } } +static int _sde_hdmi_bridge_ddc_clear_irq(struct hdmi *hdmi, + char *what) +{ + u32 ddc_int_ctrl, ddc_status, in_use, timeout; + u32 sw_done_mask = BIT(2); + u32 sw_done_ack = BIT(1); + u32 in_use_by_sw = BIT(0); + u32 in_use_by_hw = BIT(1); + + /* clear and enable interrutps */ + ddc_int_ctrl = sw_done_mask | sw_done_ack; + + hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL, ddc_int_ctrl); + + /* wait until DDC HW is free */ + timeout = 100; + do { + ddc_status = hdmi_read(hdmi, REG_HDMI_DDC_HW_STATUS); + in_use = ddc_status & (in_use_by_sw | in_use_by_hw); + if (in_use) { + SDE_DEBUG("ddc is in use by %s, timeout(%d)\n", + ddc_status & in_use_by_sw ? "sw" : "hw", + timeout); + udelay(100); + } + } while (in_use && --timeout); + + if (!timeout) { + SDE_ERROR("%s: timedout\n", what); + return -ETIMEDOUT; + } + + return 0; +} + +static int _sde_hdmi_bridge_scrambler_ddc_check_status(struct hdmi *hdmi) +{ + int rc = 0; + u32 reg_val; + + /* check for errors and clear status */ + reg_val = hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_STATUS); + if (reg_val & BIT(4)) { + SDE_ERROR("ddc aborted\n"); + reg_val |= BIT(5); + rc = -ECONNABORTED; + } + + if (reg_val & BIT(8)) { + SDE_ERROR("timed out\n"); + reg_val |= BIT(9); + rc = -ETIMEDOUT; + } + + if (reg_val & BIT(12)) { + SDE_ERROR("NACK0\n"); + reg_val |= BIT(13); + rc = -EIO; + } + if (reg_val & BIT(14)) { + SDE_ERROR("NACK1\n"); + reg_val |= BIT(15); + rc = -EIO; + } + hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_STATUS, reg_val); + + return rc; +} + +static void _sde_hdmi_bridge_scrambler_ddc_reset(struct hdmi *hdmi) +{ + u32 reg_val; + + /* clear ack and disable interrupts */ + reg_val = BIT(14) | BIT(9) | BIT(5) | BIT(1); + hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL2, reg_val); + + /* Reset DDC timers */ + reg_val = BIT(0) | hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL); + hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val); + + reg_val = hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL); + reg_val &= ~BIT(0); + hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val); +} + +static void _sde_hdmi_bridge_scrambler_ddc_disable(struct hdmi *hdmi) +{ + u32 reg_val; + + _sde_hdmi_bridge_scrambler_ddc_reset(hdmi); + /* Disable HW DDC access to RxStatus register */ + reg_val = hdmi_read(hdmi, REG_HDMI_HW_DDC_CTRL); + reg_val &= ~(BIT(8) | BIT(9)); + hdmi_write(hdmi, REG_HDMI_HW_DDC_CTRL, reg_val); +} + +static int _sde_hdmi_bridge_scrambler_status_timer_setup(struct hdmi *hdmi, + u32 timeout_hsync) +{ + u32 reg_val; + int rc; + + _sde_hdmi_bridge_ddc_clear_irq(hdmi, "scrambler"); + + hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL, + timeout_hsync); + hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL2, + timeout_hsync); + reg_val = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL5); + reg_val |= BIT(10); + hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL5, reg_val); + + reg_val = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL2); + /* Trigger interrupt if scrambler status is 0 or DDC failure */ + reg_val |= BIT(10); + reg_val &= ~(BIT(15) | BIT(16)); + reg_val |= BIT(16); + hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL2, reg_val); + + /* Enable DDC access */ + reg_val = hdmi_read(hdmi, REG_HDMI_HW_DDC_CTRL); + + reg_val &= ~(BIT(8) | BIT(9)); + reg_val |= BIT(8); + hdmi_write(hdmi, REG_HDMI_HW_DDC_CTRL, reg_val); + + /* WAIT for 200ms as per HDMI 2.0 standard for sink to respond */ + msleep(200); + + /* clear the scrambler status */ + rc = _sde_hdmi_bridge_scrambler_ddc_check_status(hdmi); + if (rc) + SDE_ERROR("scrambling ddc error %d\n", rc); + + _sde_hdmi_bridge_scrambler_ddc_disable(hdmi); + + return rc; +} + +static int _sde_hdmi_bridge_setup_ddc_timers(struct hdmi *hdmi, + u32 type, u32 to_in_num_lines) +{ + if (type >= HDMI_TX_DDC_TIMER_MAX) { + SDE_ERROR("Invalid timer type %d\n", type); + return -EINVAL; + } + + switch (type) { + case HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS: + _sde_hdmi_bridge_scrambler_status_timer_setup(hdmi, + to_in_num_lines); + break; + default: + SDE_ERROR("%d type not supported\n", type); + return -EINVAL; + } + + return 0; +} + +static inline int _sde_hdmi_bridge_get_timeout_in_hysnc( + struct drm_display_mode *mode, u32 timeout_ms) +{ + /* + * pixel clock = h_total * v_total * fps + * 1 sec = pixel clock number of pixels are transmitted. + * time taken by one line (h_total) = 1s / (v_total * fps). + * lines for give time = (time_ms * 1000) / (1000000 / (v_total * fps)) + * = (time_ms * clock) / h_total + */ + + return (timeout_ms * mode->clock / mode->htotal); +} + +static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi, + struct drm_display_mode *mode) +{ + int rc = 0; + int timeout_hsync; + u32 reg_val = 0; + u32 tmds_clock_ratio = 0; + bool scrambler_on = false; + + struct drm_connector *connector = NULL; + + if (!hdmi || !mode) { + SDE_ERROR("invalid input\n"); + return -EINVAL; + } + connector = hdmi->connector; + + /* Read HDMI version */ + reg_val = hdmi_read(hdmi, REG_HDMI_VERSION); + reg_val = (reg_val & 0xF0000000) >> 28; + /* Scrambling is supported from HDMI TX 4.0 */ + if (reg_val < HDMI_TX_SCRAMBLER_MIN_TX_VERSION) { + DRM_INFO("scrambling not supported by tx\n"); + return 0; + } + + if (mode->clock > HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ) { + scrambler_on = true; + tmds_clock_ratio = 1; + } else { + scrambler_on = connector->supports_scramble; + } + + DRM_INFO("scrambler %s\n", scrambler_on ? "on" : "off"); + + if (scrambler_on) { + rc = sde_hdmi_scdc_write(hdmi, + HDMI_TX_SCDC_TMDS_BIT_CLOCK_RATIO_UPDATE, + tmds_clock_ratio); + if (rc) { + SDE_ERROR("TMDS CLK RATIO ERR\n"); + return rc; + } + + reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); + reg_val |= BIT(31); /* Enable Update DATAPATH_MODE */ + reg_val |= BIT(28); /* Set SCRAMBLER_EN bit */ + + hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); + + rc = sde_hdmi_scdc_write(hdmi, + HDMI_TX_SCDC_SCRAMBLING_ENABLE, 0x1); + if (rc) { + SDE_ERROR("failed to enable scrambling\n"); + return rc; + } + + /* + * Setup hardware to periodically check for scrambler + * status bit on the sink. Sink should set this bit + * with in 200ms after scrambler is enabled. + */ + timeout_hsync = _sde_hdmi_bridge_get_timeout_in_hysnc( + mode, + HDMI_TX_SCRAMBLER_TIMEOUT_MSEC); + if (timeout_hsync <= 0) { + SDE_ERROR("err in timeout hsync calc\n"); + timeout_hsync = HDMI_DEFAULT_TIMEOUT_HSYNC; + } + SDE_DEBUG("timeout for scrambling en: %d hsyncs\n", + timeout_hsync); + + rc = _sde_hdmi_bridge_setup_ddc_timers(hdmi, + HDMI_TX_DDC_TIMER_SCRAMBLER_STATUS, timeout_hsync); + } else { + sde_hdmi_scdc_write(hdmi, HDMI_TX_SCDC_SCRAMBLING_ENABLE, 0x0); + reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); + reg_val &= ~BIT(31); /* Disable Update DATAPATH_MODE */ + reg_val &= ~BIT(28); /* Unset SCRAMBLER_EN bit */ + hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); + } + return rc; +} + static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge) { struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); @@ -373,6 +639,7 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge, _sde_hdmi_bridge_set_spd_infoframe(hdmi, mode); DRM_DEBUG("hdmi setup info frame\n"); } + _sde_hdmi_bridge_setup_scrambler(hdmi, mode); } static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = { diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_edid.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_edid.c deleted file mode 100644 index 57c79e2aa812..000000000000 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_edid.c +++ /dev/null @@ -1,227 +0,0 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. - * - * This 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 <drm/drm_edid.h> - -#include "sde_kms.h" -#include "sde_hdmi.h" - -/* TODO: copy from drm_edid.c and mdss_hdmi_edid.c. remove if using ELD */ -#define DBC_START_OFFSET 4 -#define EDID_DTD_LEN 18 - -enum data_block_types { - RESERVED, - AUDIO_DATA_BLOCK, - VIDEO_DATA_BLOCK, - VENDOR_SPECIFIC_DATA_BLOCK, - SPEAKER_ALLOCATION_DATA_BLOCK, - VESA_DTC_DATA_BLOCK, - RESERVED2, - USE_EXTENDED_TAG -}; - -static u8 *_sde_hdmi_edid_find_cea_extension(struct edid *edid) -{ - u8 *edid_ext = NULL; - int i; - - /* No EDID or EDID extensions */ - if (edid == NULL || edid->extensions == 0) - return NULL; - - /* Find CEA extension */ - for (i = 0; i < edid->extensions; i++) { - edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1); - if (edid_ext[0] == CEA_EXT) - break; - } - - if (i == edid->extensions) - return NULL; - - return edid_ext; -} - -static const u8 *_sde_hdmi_edid_find_block(const u8 *in_buf, u32 start_offset, - u8 type, u8 *len) -{ - /* the start of data block collection, start of Video Data Block */ - u32 offset = start_offset; - u32 dbc_offset = in_buf[2]; - - /* - * * edid buffer 1, byte 2 being 4 means no non-DTD/Data block - * collection present. - * * edid buffer 1, byte 2 being 0 means no non-DTD/DATA block - * collection present and no DTD data present. - */ - if ((dbc_offset == 0) || (dbc_offset == 4)) { - SDE_ERROR("EDID: no DTD or non-DTD data present\n"); - return NULL; - } - - while (offset < dbc_offset) { - u8 block_len = in_buf[offset] & 0x1F; - - if ((offset + block_len <= dbc_offset) && - (in_buf[offset] >> 5) == type) { - *len = block_len; - SDE_DEBUG("EDID: block=%d found @ 0x%x w/ len=%d\n", - type, offset, block_len); - - return in_buf + offset; - } - offset += 1 + block_len; - } - - return NULL; -} - -static void _sde_hdmi_extract_audio_data_blocks( - struct hdmi_edid_ctrl *edid_ctrl) -{ - u8 len = 0; - u8 adb_max = 0; - const u8 *adb = NULL; - u32 offset = DBC_START_OFFSET; - u8 *cea = NULL; - - if (!edid_ctrl) { - SDE_ERROR("invalid edid_ctrl\n"); - return; - } - - cea = _sde_hdmi_edid_find_cea_extension(edid_ctrl->edid); - if (!cea) { - SDE_DEBUG("CEA extension not found\n"); - return; - } - - edid_ctrl->adb_size = 0; - - memset(edid_ctrl->audio_data_block, 0, - sizeof(edid_ctrl->audio_data_block)); - - do { - len = 0; - adb = _sde_hdmi_edid_find_block(cea, offset, AUDIO_DATA_BLOCK, - &len); - - if ((adb == NULL) || (len > MAX_AUDIO_DATA_BLOCK_SIZE || - adb_max >= MAX_NUMBER_ADB)) { - if (!edid_ctrl->adb_size) { - SDE_DEBUG("No/Invalid Audio Data Block\n"); - return; - } - - continue; - } - - memcpy(edid_ctrl->audio_data_block + edid_ctrl->adb_size, - adb + 1, len); - offset = (adb - cea) + 1 + len; - - edid_ctrl->adb_size += len; - adb_max++; - } while (adb); - -} - -static void _sde_hdmi_extract_speaker_allocation_data( - struct hdmi_edid_ctrl *edid_ctrl) -{ - u8 len; - const u8 *sadb = NULL; - u8 *cea = NULL; - - if (!edid_ctrl) { - SDE_ERROR("invalid edid_ctrl\n"); - return; - } - - cea = _sde_hdmi_edid_find_cea_extension(edid_ctrl->edid); - if (!cea) { - SDE_DEBUG("CEA extension not found\n"); - return; - } - - sadb = _sde_hdmi_edid_find_block(cea, DBC_START_OFFSET, - SPEAKER_ALLOCATION_DATA_BLOCK, &len); - if ((sadb == NULL) || (len != MAX_SPKR_ALLOC_DATA_BLOCK_SIZE)) { - SDE_DEBUG("No/Invalid Speaker Allocation Data Block\n"); - return; - } - - memcpy(edid_ctrl->spkr_alloc_data_block, sadb + 1, len); - edid_ctrl->sadb_size = len; - - SDE_DEBUG("EDID: speaker alloc data SP byte = %08x %s%s%s%s%s%s%s\n", - sadb[1], - (sadb[1] & BIT(0)) ? "FL/FR," : "", - (sadb[1] & BIT(1)) ? "LFE," : "", - (sadb[1] & BIT(2)) ? "FC," : "", - (sadb[1] & BIT(3)) ? "RL/RR," : "", - (sadb[1] & BIT(4)) ? "RC," : "", - (sadb[1] & BIT(5)) ? "FLC/FRC," : "", - (sadb[1] & BIT(6)) ? "RLC/RRC," : ""); -} - -int sde_hdmi_edid_init(struct sde_hdmi *display) -{ - int rc = 0; - - if (!display) { - SDE_ERROR("[%s]Invalid params\n", display->name); - return -EINVAL; - } - - memset(&display->edid, 0, sizeof(display->edid)); - - return rc; -} - -int sde_hdmi_free_edid(struct sde_hdmi *display) -{ - struct hdmi_edid_ctrl *edid_ctrl = &display->edid; - - kfree(edid_ctrl->edid); - edid_ctrl->edid = NULL; - - return 0; -} - -int sde_hdmi_edid_deinit(struct sde_hdmi *display) -{ - return sde_hdmi_free_edid(display); -} - -void sde_hdmi_get_edid(struct drm_connector *connector, - struct sde_hdmi *display) -{ - u32 hdmi_ctrl; - struct hdmi_edid_ctrl *edid_ctrl = &display->edid; - struct hdmi *hdmi = display->ctrl.ctrl; - - /* Read EDID */ - hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL); - hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE); - edid_ctrl->edid = drm_get_edid(connector, hdmi->i2c); - hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl); - - if (edid_ctrl->edid) { - hdmi->hdmi_mode = drm_detect_hdmi_monitor(edid_ctrl->edid); - - _sde_hdmi_extract_audio_data_blocks(edid_ctrl); - _sde_hdmi_extract_speaker_allocation_data(edid_ctrl); - } -}; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h index aef20f76bf02..0956617442af 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h @@ -563,6 +563,20 @@ static inline uint32_t HDMI_VSYNC_TOTAL_F2_V_TOTAL(uint32_t val) #define REG_HDMI_CEC_WR_CHECK_CONFIG 0x00000370 +#define REG_HDMI_DDC_INT_CTRL0 0x00000430 +#define REG_HDMI_DDC_INT_CTRL1 0x00000434 +#define REG_HDMI_DDC_INT_CTRL2 0x00000438 +#define REG_HDMI_DDC_INT_CTRL3 0x0000043C +#define REG_HDMI_DDC_INT_CTRL4 0x00000440 +#define REG_HDMI_DDC_INT_CTRL5 0x00000444 +#define REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL 0x00000464 +#define REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL 0x00000468 +#define REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL2 0x0000046C +#define REG_HDMI_SCRAMBLER_STATUS_DDC_STATUS 0x00000470 +#define REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_STATUS 0x00000474 +#define REG_HDMI_SCRAMBLER_STATUS_DDC_TIMER_STATUS2 0x00000478 +#define REG_HDMI_HW_DDC_CTRL 0x000004CC + #define REG_HDMI_8x60_PHY_REG0 0x00000300 #define HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__MASK 0x0000001c #define HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__SHIFT 2 diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index ac46c473791f..2045dc34c20a 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -125,6 +125,8 @@ struct msm_gem_submit { uint32_t fence; int ring; bool valid; + uint64_t profile_buf_iova; + void *profile_buf_vaddr; unsigned int nr_cmds; unsigned int nr_bos; struct { diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 0566cefaae81..52fc81420690 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -48,6 +48,9 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev, submit->nr_bos = 0; submit->nr_cmds = 0; + submit->profile_buf_vaddr = NULL; + submit->profile_buf_iova = 0; + INIT_LIST_HEAD(&submit->bo_list); ww_acquire_init(&submit->ticket, &reservation_ww_class); } @@ -393,6 +396,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, case MSM_SUBMIT_CMD_BUF: case MSM_SUBMIT_CMD_IB_TARGET_BUF: case MSM_SUBMIT_CMD_CTX_RESTORE_BUF: + case MSM_SUBMIT_CMD_PROFILE_BUF: break; default: DRM_ERROR("invalid type: %08x\n", submit_cmd.type); @@ -425,6 +429,12 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, submit->cmd[i].iova = iova + submit_cmd.submit_offset; submit->cmd[i].idx = submit_cmd.submit_idx; + if (submit_cmd.type == MSM_SUBMIT_CMD_PROFILE_BUF) { + submit->profile_buf_iova = submit->cmd[i].iova; + submit->profile_buf_vaddr = + msm_gem_vaddr_locked(&msm_obj->base); + } + if (submit->valid) continue; diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index 82da4c1bc86c..41322045ced3 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -51,6 +51,9 @@ #define LEFT_MIXER 0 #define RIGHT_MIXER 1 +/* indicates pending page flip events */ +#define PENDING_FLIP 0x2 + static inline struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc) { struct msm_drm_private *priv = crtc->dev->dev_private; @@ -399,13 +402,18 @@ static void sde_crtc_vblank_cb(void *data) { struct drm_crtc *crtc = (struct drm_crtc *)data; struct sde_crtc *sde_crtc = to_sde_crtc(crtc); + unsigned pending; + pending = atomic_xchg(&sde_crtc->pending, 0); /* keep statistics on vblank callback - with auto reset via debugfs */ if (ktime_equal(sde_crtc->vblank_cb_time, ktime_set(0, 0))) sde_crtc->vblank_cb_time = ktime_get(); else sde_crtc->vblank_cb_count++; - _sde_crtc_complete_flip(crtc, NULL); + + if (pending & PENDING_FLIP) + _sde_crtc_complete_flip(crtc, NULL); + drm_crtc_handle_vblank(crtc); DRM_DEBUG_VBL("crtc%d\n", crtc->base.id); SDE_EVT32_IRQ(DRMID(crtc)); @@ -519,6 +527,28 @@ static void sde_crtc_frame_event_cb(void *data, u32 event) queue_kthread_work(&priv->disp_thread[pipe_id].worker, &fevent->work); } +/** + * sde_crtc_request_flip_cb - callback to request page_flip events + * Once the HW flush is complete , userspace must be notified of + * PAGE_FLIP completed event in the next vblank event. + * Using this callback, a hint is set to signal any callers waiting + * for a PAGE_FLIP complete event. + * This is called within the enc_spinlock. + * @data: Pointer to cb private data + */ +static void sde_crtc_request_flip_cb(void *data) +{ + struct drm_crtc *crtc = (struct drm_crtc *)data; + struct sde_crtc *sde_crtc; + + if (!crtc) { + SDE_ERROR("invalid parameters\n"); + return; + } + sde_crtc = to_sde_crtc(crtc); + atomic_or(PENDING_FLIP, &sde_crtc->pending); +} + void sde_crtc_complete_commit(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { @@ -679,7 +709,6 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc, { struct sde_crtc *sde_crtc; struct drm_device *dev; - unsigned long flags; u32 i; if (!crtc) { @@ -701,14 +730,6 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc, if (!sde_crtc->num_mixers) _sde_crtc_setup_mixers(crtc); - if (sde_crtc->event) { - WARN_ON(sde_crtc->event); - } else { - spin_lock_irqsave(&dev->event_lock, flags); - sde_crtc->event = crtc->state->event; - spin_unlock_irqrestore(&dev->event_lock, flags); - } - /* Reset flush mask from previous commit */ for (i = 0; i < ARRAY_SIZE(sde_crtc->mixers); i++) { struct sde_hw_ctl *ctl = sde_crtc->mixers[i].hw_ctl; @@ -763,7 +784,8 @@ static void sde_crtc_atomic_flush(struct drm_crtc *crtc, dev = crtc->dev; if (sde_crtc->event) { - SDE_DEBUG("already received sde_crtc->event\n"); + SDE_ERROR("%s already received sde_crtc->event\n", + sde_crtc->name); } else { spin_lock_irqsave(&dev->event_lock, flags); sde_crtc->event = crtc->state->event; @@ -999,6 +1021,7 @@ static void sde_crtc_disable(struct drm_crtc *crtc) if (encoder->crtc != crtc) continue; sde_encoder_register_frame_event_callback(encoder, NULL, NULL); + sde_encoder_register_request_flip_callback(encoder, NULL, NULL); } memset(sde_crtc->mixers, 0, sizeof(sde_crtc->mixers)); @@ -1039,6 +1062,8 @@ static void sde_crtc_enable(struct drm_crtc *crtc) continue; sde_encoder_register_frame_event_callback(encoder, sde_crtc_frame_event_cb, (void *)crtc); + sde_encoder_register_request_flip_callback(encoder, + sde_crtc_request_flip_cb, (void *)crtc); } for (i = 0; i < sde_crtc->num_mixers; i++) { diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h index 25a93e882e6d..97a20b987ef5 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.h +++ b/drivers/gpu/drm/msm/sde/sde_crtc.h @@ -89,6 +89,7 @@ struct sde_crtc_frame_event { * @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 + * @pending : Whether any page-flip events are pending signal * @spin_lock : spin lock for frame event, transaction status, etc... */ struct sde_crtc { @@ -109,7 +110,7 @@ struct sde_crtc { /* output fence support */ struct sde_fence output_fence; - + atomic_t pending; struct sde_hw_stage_cfg stage_cfg; struct dentry *debugfs_root; diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c index 8cffb03fdfbb..030b192e5df4 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder.c @@ -83,6 +83,8 @@ * Bit0 = phys_encs[0] etc. * @crtc_frame_event_cb: callback handler for frame event * @crtc_frame_event_cb_data: callback handler private data + * @crtc_request_flip_cb: callback handler for requesting page-flip event + * @crtc_request_flip_cb_data: callback handler private data * @crtc_frame_event: callback event * @frame_done_timeout: frame done timeout in Hz * @frame_done_timer: watchdog timer for frame done event @@ -107,8 +109,9 @@ struct sde_encoder_virt { DECLARE_BITMAP(frame_busy_mask, MAX_PHYS_ENCODERS_PER_VIRTUAL); void (*crtc_frame_event_cb)(void *, u32 event); void *crtc_frame_event_cb_data; + void (*crtc_request_flip_cb)(void *); + void *crtc_request_flip_cb_data; u32 crtc_frame_event; - atomic_t frame_done_timeout; struct timer_list frame_done_timer; }; @@ -584,6 +587,24 @@ void sde_encoder_register_frame_event_callback(struct drm_encoder *drm_enc, spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags); } +void sde_encoder_register_request_flip_callback(struct drm_encoder *drm_enc, + void (*request_flip_cb)(void *), + void *request_flip_cb_data) +{ + struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc); + unsigned long lock_flags; + + if (!drm_enc) { + SDE_ERROR("invalid encoder\n"); + return; + } + + spin_lock_irqsave(&sde_enc->enc_spinlock, lock_flags); + sde_enc->crtc_request_flip_cb = request_flip_cb; + sde_enc->crtc_request_flip_cb_data = request_flip_cb_data; + spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags); +} + static void sde_encoder_frame_done_callback( struct drm_encoder *drm_enc, struct sde_encoder_phys *ready_phys, u32 event) @@ -759,6 +780,11 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc) pending_flush); } + /* HW flush has happened, request a flip complete event now */ + if (sde_enc->crtc_request_flip_cb) + sde_enc->crtc_request_flip_cb( + sde_enc->crtc_request_flip_cb_data); + _sde_encoder_trigger_start(sde_enc->cur_master); spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags); diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h index 82576b479bf8..6b74dca13ae9 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder.h +++ b/drivers/gpu/drm/msm/sde/sde_encoder.h @@ -72,6 +72,17 @@ void sde_encoder_register_frame_event_callback(struct drm_encoder *encoder, void (*cb)(void *, u32), void *data); /** + * sde_encoder_register_request_flip_callback - provide callback to encoder that + * will be called after HW flush is complete to request + * a page flip event from CRTC. + * @encoder: encoder pointer + * @cb: callback pointer, provide NULL to deregister + * @data: user data provided to callback + */ +void sde_encoder_register_request_flip_callback(struct drm_encoder *encoder, + void (*cb)(void *), void *data); + +/** * sde_encoder_prepare_for_kickoff - schedule double buffer flip of the ctl * path (i.e. ctl flush and start) at next appropriate time. * Immediately: if no previous commit is outstanding. diff --git a/drivers/gpu/drm/msm/sde_edid_parser.c b/drivers/gpu/drm/msm/sde_edid_parser.c new file mode 100644 index 000000000000..69ab367307ea --- /dev/null +++ b/drivers/gpu/drm/msm/sde_edid_parser.c @@ -0,0 +1,512 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This 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 <drm/drm_edid.h> + +#include "sde_kms.h" +#include "sde_edid_parser.h" + +/* TODO: copy from drm_edid.c and mdss_hdmi_edid.c. remove if using ELD */ +#define DBC_START_OFFSET 4 +#define EDID_DTD_LEN 18 + +enum data_block_types { + RESERVED, + AUDIO_DATA_BLOCK, + VIDEO_DATA_BLOCK, + VENDOR_SPECIFIC_DATA_BLOCK, + SPEAKER_ALLOCATION_DATA_BLOCK, + VESA_DTC_DATA_BLOCK, + RESERVED2, + USE_EXTENDED_TAG +}; + +static u8 *sde_find_edid_extension(struct edid *edid, int ext_id) +{ + u8 *edid_ext = NULL; + int i; + + /* No EDID or EDID extensions */ + if (edid == NULL || edid->extensions == 0) + return NULL; + + /* Find CEA extension */ + for (i = 0; i < edid->extensions; i++) { + edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1); + if (edid_ext[0] == ext_id) + break; + } + + if (i == edid->extensions) + return NULL; + + return edid_ext; +} + +static u8 *sde_find_cea_extension(struct edid *edid) +{ + return sde_find_edid_extension(edid, SDE_CEA_EXT); +} + +static int +sde_cea_db_payload_len(const u8 *db) +{ + return db[0] & 0x1f; +} + +static int +sde_cea_db_tag(const u8 *db) +{ + return db[0] >> 5; +} + +static int +sde_cea_revision(const u8 *cea) +{ + return cea[1]; +} + +static int +sde_cea_db_offsets(const u8 *cea, int *start, int *end) +{ + /* Data block offset in CEA extension block */ + *start = 4; + *end = cea[2]; + if (*end == 0) + *end = 127; + if (*end < 4 || *end > 127) + return -ERANGE; + return 0; +} + +#define sde_for_each_cea_db(cea, i, start, end) \ +for ((i) = (start); \ +(i) < (end) && (i) + sde_cea_db_payload_len(&(cea)[(i)]) < (end); \ +(i) += sde_cea_db_payload_len(&(cea)[(i)]) + 1) + +static u8 *sde_edid_find_extended_tag_block(struct edid *edid, int blk_id) +{ + u8 *db = NULL; + u8 *cea = NULL; + + if (!edid) { + pr_err("%s: invalid input\n", __func__); + return NULL; + } + + cea = sde_find_cea_extension(edid); + + if (cea && sde_cea_revision(cea) >= 3) { + int i, start, end; + + if (sde_cea_db_offsets(cea, &start, &end)) + return NULL; + + sde_for_each_cea_db(cea, i, start, end) { + db = &cea[i]; + if ((sde_cea_db_tag(db) == SDE_EXTENDED_TAG) && + (db[1] == blk_id)) + return db; + } + } + return NULL; +} + +static u8 * +sde_edid_find_block(struct edid *edid, int blk_id) +{ + u8 *db = NULL; + u8 *cea = NULL; + + if (!edid) { + pr_err("%s: invalid input\n", __func__); + return NULL; + } + + cea = sde_find_cea_extension(edid); + + if (cea && sde_cea_revision(cea) >= 3) { + int i, start, end; + + if (sde_cea_db_offsets(cea, &start, &end)) + return 0; + + sde_for_each_cea_db(cea, i, start, end) { + db = &cea[i]; + if (sde_cea_db_tag(db) == blk_id) + return db; + } + } + return NULL; +} + + +static const u8 *_sde_edid_find_block(const u8 *in_buf, u32 start_offset, + u8 type, u8 *len) +{ + /* the start of data block collection, start of Video Data Block */ + u32 offset = start_offset; + u32 dbc_offset = in_buf[2]; + + SDE_EDID_DEBUG("%s +", __func__); + /* + * * edid buffer 1, byte 2 being 4 means no non-DTD/Data block + * collection present. + * * edid buffer 1, byte 2 being 0 means no non-DTD/DATA block + * collection present and no DTD data present. + */ + if ((dbc_offset == 0) || (dbc_offset == 4)) { + SDE_ERROR("EDID: no DTD or non-DTD data present\n"); + return NULL; + } + + while (offset < dbc_offset) { + u8 block_len = in_buf[offset] & 0x1F; + + if ((offset + block_len <= dbc_offset) && + (in_buf[offset] >> 5) == type) { + *len = block_len; + SDE_EDID_DEBUG("block=%d found @ 0x%x w/ len=%d\n", + type, offset, block_len); + + return in_buf + offset; + } + offset += 1 + block_len; + } + + return NULL; +} + +static void sde_edid_extract_vendor_id(struct sde_edid_ctrl *edid_ctrl) +{ + char *vendor_id; + u32 id_codes; + + SDE_EDID_DEBUG("%s +", __func__); + if (!edid_ctrl) { + SDE_ERROR("%s: invalid input\n", __func__); + return; + } + + vendor_id = edid_ctrl->vendor_id; + id_codes = ((u32)edid_ctrl->edid->mfg_id[0] << 8) + + edid_ctrl->edid->mfg_id[1]; + + vendor_id[0] = 'A' - 1 + ((id_codes >> 10) & 0x1F); + vendor_id[1] = 'A' - 1 + ((id_codes >> 5) & 0x1F); + vendor_id[2] = 'A' - 1 + (id_codes & 0x1F); + vendor_id[3] = 0; + SDE_EDID_DEBUG("vendor id is %s ", vendor_id); + SDE_EDID_DEBUG("%s -", __func__); +} + +static void sde_edid_set_y420_support(struct drm_connector *connector, +u32 video_format) +{ + u8 cea_mode = 0; + struct drm_display_mode *mode; + + /* Need to add Y420 support flag to the modes */ + list_for_each_entry(mode, &connector->probed_modes, head) { + cea_mode = drm_match_cea_mode(mode); + if ((cea_mode != 0) && (cea_mode == video_format)) { + SDE_EDID_DEBUG("%s found match for %d ", __func__, + video_format); + mode->flags |= DRM_MODE_FLAG_SUPPORTS_YUV; + } + } +} + +static void sde_edid_parse_Y420CMDB( +struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl, +const u8 *db) +{ + u32 offset = 0; + u8 len = 0; + u8 svd_len = 0; + const u8 *svd = NULL; + u32 i = 0, j = 0; + u32 video_format = 0; + + if (!edid_ctrl) { + DEV_ERR("%s: edid_ctrl is NULL\n", __func__); + return; + } + + if (!db) { + DEV_ERR("%s: invalid input\n", __func__); + return; + } + SDE_EDID_DEBUG("%s +\n", __func__); + len = db[0] & 0x1f; + + if (len < 7) + return; + /* Byte 3 to L+1 contain SVDs */ + offset += 2; + + svd = sde_edid_find_block(edid_ctrl->edid, VIDEO_DATA_BLOCK); + + if (svd) { + /*moving to the next byte as vic info begins there*/ + ++svd; + svd_len = svd[0] & 0x1f; + } + + for (i = 0; i < svd_len; i++, j++) { + video_format = *svd & 0x7F; + if (db[offset] & (1 << j)) + sde_edid_set_y420_support(connector, video_format); + + if (j & 0x80) { + j = j/8; + offset++; + if (offset >= len) + break; + } + } + + SDE_EDID_DEBUG("%s -\n", __func__); + +} + +static void sde_edid_parse_Y420VDB( +struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl, +const u8 *db) +{ + u8 len = db[0] & 0x1f; + u32 i = 0; + u32 video_format = 0; + + if (!edid_ctrl) { + DEV_ERR("%s: invalid input\n", __func__); + return; + } + + SDE_EDID_DEBUG("%s +\n", __func__); + + /* Offset to byte 3 */ + db += 2; + for (i = 0; i < len - 1; i++) { + video_format = *(db + i) & 0x7F; + /* + * mode was already added in get_modes() + * only need to set the Y420 support flag + */ + sde_edid_set_y420_support(connector, video_format); + } + SDE_EDID_DEBUG("%s -", __func__); +} + +static void sde_edid_set_mode_format( +struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl) +{ + const u8 *db = NULL; + struct drm_display_mode *mode; + + SDE_EDID_DEBUG("%s +\n", __func__); + /* Set YUV mode support flags for YCbcr420VDB */ + db = sde_edid_find_extended_tag_block(edid_ctrl->edid, + Y420_VIDEO_DATA_BLOCK); + if (db) + sde_edid_parse_Y420VDB(connector, edid_ctrl, db); + else + SDE_EDID_DEBUG("YCbCr420 VDB is not present\n"); + + /* Set RGB supported on all modes where YUV is not set */ + list_for_each_entry(mode, &connector->probed_modes, head) { + if (!(mode->flags & DRM_MODE_FLAG_SUPPORTS_YUV)) + mode->flags |= DRM_MODE_FLAG_SUPPORTS_RGB; + } + + + db = sde_edid_find_extended_tag_block(edid_ctrl->edid, + Y420_CAPABILITY_MAP_DATA_BLOCK); + if (db) + sde_edid_parse_Y420CMDB(connector, edid_ctrl, db); + else + SDE_EDID_DEBUG("YCbCr420 CMDB is not present\n"); + + SDE_EDID_DEBUG("%s -\n", __func__); +} + +static void _sde_edid_extract_audio_data_blocks( + struct sde_edid_ctrl *edid_ctrl) +{ + u8 len = 0; + u8 adb_max = 0; + const u8 *adb = NULL; + u32 offset = DBC_START_OFFSET; + u8 *cea = NULL; + + if (!edid_ctrl) { + SDE_ERROR("invalid edid_ctrl\n"); + return; + } + SDE_EDID_DEBUG("%s +", __func__); + cea = sde_find_cea_extension(edid_ctrl->edid); + if (!cea) { + SDE_DEBUG("CEA extension not found\n"); + return; + } + + edid_ctrl->adb_size = 0; + + memset(edid_ctrl->audio_data_block, 0, + sizeof(edid_ctrl->audio_data_block)); + + do { + len = 0; + adb = _sde_edid_find_block(cea, offset, AUDIO_DATA_BLOCK, + &len); + + if ((adb == NULL) || (len > MAX_AUDIO_DATA_BLOCK_SIZE || + adb_max >= MAX_NUMBER_ADB)) { + if (!edid_ctrl->adb_size) { + SDE_DEBUG("No/Invalid Audio Data Block\n"); + return; + } + + continue; + } + + memcpy(edid_ctrl->audio_data_block + edid_ctrl->adb_size, + adb + 1, len); + offset = (adb - cea) + 1 + len; + + edid_ctrl->adb_size += len; + adb_max++; + } while (adb); + SDE_EDID_DEBUG("%s -", __func__); +} + +static void _sde_edid_extract_speaker_allocation_data( + struct sde_edid_ctrl *edid_ctrl) +{ + u8 len; + const u8 *sadb = NULL; + u8 *cea = NULL; + + if (!edid_ctrl) { + SDE_ERROR("invalid edid_ctrl\n"); + return; + } + SDE_EDID_DEBUG("%s +", __func__); + cea = sde_find_cea_extension(edid_ctrl->edid); + if (!cea) { + SDE_DEBUG("CEA extension not found\n"); + return; + } + + sadb = _sde_edid_find_block(cea, DBC_START_OFFSET, + SPEAKER_ALLOCATION_DATA_BLOCK, &len); + if ((sadb == NULL) || (len != MAX_SPKR_ALLOC_DATA_BLOCK_SIZE)) { + SDE_DEBUG("No/Invalid Speaker Allocation Data Block\n"); + return; + } + + memcpy(edid_ctrl->spkr_alloc_data_block, sadb + 1, len); + edid_ctrl->sadb_size = len; + + SDE_EDID_DEBUG("speaker alloc data SP byte = %08x %s%s%s%s%s%s%s\n", + sadb[1], + (sadb[1] & BIT(0)) ? "FL/FR," : "", + (sadb[1] & BIT(1)) ? "LFE," : "", + (sadb[1] & BIT(2)) ? "FC," : "", + (sadb[1] & BIT(3)) ? "RL/RR," : "", + (sadb[1] & BIT(4)) ? "RC," : "", + (sadb[1] & BIT(5)) ? "FLC/FRC," : "", + (sadb[1] & BIT(6)) ? "RLC/RRC," : ""); + SDE_EDID_DEBUG("%s -", __func__); +} + +struct sde_edid_ctrl *sde_edid_init(void) +{ + struct sde_edid_ctrl *edid_ctrl = NULL; + + SDE_EDID_DEBUG("%s +\n", __func__); + edid_ctrl = kzalloc(sizeof(*edid_ctrl), GFP_KERNEL); + if (!edid_ctrl) { + SDE_ERROR("edid_ctrl alloc failed\n"); + return NULL; + } + memset((edid_ctrl), 0, sizeof(*edid_ctrl)); + SDE_EDID_DEBUG("%s -\n", __func__); + return edid_ctrl; +} + +void sde_free_edid(void **input) +{ + struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(*input); + + SDE_EDID_DEBUG("%s +", __func__); + kfree(edid_ctrl->edid); + edid_ctrl->edid = NULL; +} + +void sde_edid_deinit(void **input) +{ + struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(*input); + + SDE_EDID_DEBUG("%s +", __func__); + sde_free_edid((void *)&edid_ctrl); + kfree(edid_ctrl); + SDE_EDID_DEBUG("%s -", __func__); +} + +int _sde_edid_update_modes(struct drm_connector *connector, + void *input) +{ + int rc = 0; + struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(input); + + SDE_EDID_DEBUG("%s +", __func__); + if (edid_ctrl->edid) { + drm_mode_connector_update_edid_property(connector, + edid_ctrl->edid); + + rc = drm_add_edid_modes(connector, edid_ctrl->edid); + sde_edid_set_mode_format(connector, edid_ctrl); + SDE_EDID_DEBUG("%s -", __func__); + return rc; + } + + drm_mode_connector_update_edid_property(connector, NULL); + SDE_EDID_DEBUG("%s null edid -", __func__); + return rc; +} + +bool sde_detect_hdmi_monitor(void *input) +{ + struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(input); + + return drm_detect_hdmi_monitor(edid_ctrl->edid); +} + +void sde_get_edid(struct drm_connector *connector, + struct i2c_adapter *adapter, void **input) +{ + struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(*input); + + edid_ctrl->edid = drm_get_edid(connector, adapter); + SDE_EDID_DEBUG("%s +\n", __func__); + + if (!edid_ctrl->edid) + SDE_ERROR("EDID read failed\n"); + + if (edid_ctrl->edid) { + sde_edid_extract_vendor_id(edid_ctrl); + _sde_edid_extract_audio_data_blocks(edid_ctrl); + _sde_edid_extract_speaker_allocation_data(edid_ctrl); + } + SDE_EDID_DEBUG("%s -\n", __func__); +}; diff --git a/drivers/gpu/drm/msm/sde_edid_parser.h b/drivers/gpu/drm/msm/sde_edid_parser.h new file mode 100644 index 000000000000..1143dc2c7bec --- /dev/null +++ b/drivers/gpu/drm/msm/sde_edid_parser.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This 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 _SDE_EDID_PARSER_H_ +#define _SDE_EDID_PARSER_H_ + +#include <linux/types.h> +#include <linux/bitops.h> +#include <linux/debugfs.h> +#include <linux/of_device.h> +#include <linux/i2c.h> +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_edid.h> + + +#define MAX_NUMBER_ADB 5 +#define MAX_AUDIO_DATA_BLOCK_SIZE 30 +#define MAX_SPKR_ALLOC_DATA_BLOCK_SIZE 3 +#define EDID_VENDOR_ID_SIZE 4 + +#define SDE_CEA_EXT 0x02 +#define SDE_EXTENDED_TAG 0x07 + +enum extended_data_block_types { + VIDEO_CAPABILITY_DATA_BLOCK = 0x0, + VENDOR_SPECIFIC_VIDEO_DATA_BLOCK = 0x01, + HDMI_VIDEO_DATA_BLOCK = 0x04, + HDR_STATIC_METADATA_DATA_BLOCK = 0x06, + Y420_VIDEO_DATA_BLOCK = 0x0E, + VIDEO_FORMAT_PREFERENCE_DATA_BLOCK = 0x0D, + Y420_CAPABILITY_MAP_DATA_BLOCK = 0x0F, + VENDOR_SPECIFIC_AUDIO_DATA_BLOCK = 0x11, + INFOFRAME_DATA_BLOCK = 0x20, +}; + +#ifdef SDE_EDID_DEBUG_ENABLE +#define SDE_EDID_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args) +#else +#define SDE_EDID_DEBUG(fmt, args...) SDE_DEBUG(fmt, ##args) +#endif + +/* + * struct hdmi_edid_hdr_data - HDR Static Metadata + * @eotf: Electro-Optical Transfer Function + * @metadata_type_one: Static Metadata Type 1 support + * @max_luminance: Desired Content Maximum Luminance + * @avg_luminance: Desired Content Frame-average Luminance + * @min_luminance: Desired Content Minimum Luminance + */ +struct sde_edid_hdr_data { + u32 eotf; + bool metadata_type_one; + u32 max_luminance; + u32 avg_luminance; + u32 min_luminance; +}; + +struct sde_edid_sink_caps { + u32 max_pclk_in_hz; + bool scdc_present; + bool scramble_support; /* scramble support for less than 340Mcsc */ + bool read_req_support; + bool osd_disparity; + bool dual_view_support; + bool ind_view_support; +}; + +struct sde_edid_ctrl { + struct edid *edid; + u8 pt_scan_info; + u8 it_scan_info; + u8 ce_scan_info; + u8 audio_data_block[MAX_NUMBER_ADB * MAX_AUDIO_DATA_BLOCK_SIZE]; + int adb_size; + u8 spkr_alloc_data_block[MAX_SPKR_ALLOC_DATA_BLOCK_SIZE]; + int sadb_size; + bool hdr_supported; + char vendor_id[EDID_VENDOR_ID_SIZE]; + struct sde_edid_sink_caps sink_caps; + struct sde_edid_hdr_data hdr_data; +}; + +/** + * sde_edid_init() - init edid structure. + * @edid_ctrl: Handle to the edid_ctrl structure. + * Return: handle to sde_edid_ctrl for the client. + */ +struct sde_edid_ctrl *sde_edid_init(void); + +/** + * sde_edid_deinit() - deinit edid structure. + * @edid_ctrl: Handle to the edid_ctrl structure. + * + * Return: void. + */ +void sde_edid_deinit(void **edid_ctrl); + +/** + * sde_get_edid() - get edid info. + * @connector: Handle to the drm_connector. + * @adapter: handle to i2c adapter for DDC read + * @edid_ctrl: Handle to the edid_ctrl structure. + * + * Return: void. + */ +void sde_get_edid(struct drm_connector *connector, +struct i2c_adapter *adapter, +void **edid_ctrl); + +/** + * sde_free_edid() - free edid structure. + * @edid_ctrl: Handle to the edid_ctrl structure. + * + * Return: void. + */ +void sde_free_edid(void **edid_ctrl); + +/** + * sde_detect_hdmi_monitor() - detect HDMI mode. + * @edid_ctrl: Handle to the edid_ctrl structure. + * + * Return: error code. + */ +bool sde_detect_hdmi_monitor(void *edid_ctrl); + +/** + * _sde_edid_update_modes() - populate EDID modes. + * @edid_ctrl: Handle to the edid_ctrl structure. + * + * Return: error code. + */ +int _sde_edid_update_modes(struct drm_connector *connector, + void *edid_ctrl); + +#endif /* _SDE_EDID_PARSER_H_ */ + diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c index f084ca9a62a1..54b8da5302e8 100644 --- a/drivers/gpu/msm/adreno_dispatch.c +++ b/drivers/gpu/msm/adreno_dispatch.c @@ -1041,6 +1041,13 @@ static void _set_ft_policy(struct adreno_device *adreno_dev, */ if (drawctxt->base.flags & KGSL_CONTEXT_NO_FAULT_TOLERANCE) set_bit(KGSL_FT_DISABLE, &cmdobj->fault_policy); + /* + * Set the fault tolerance policy to FT_REPLAY - As context wants + * to invalidate it after a replay attempt fails. This doesn't + * require to execute the default FT policy. + */ + else if (drawctxt->base.flags & KGSL_CONTEXT_INVALIDATE_ON_FAULT) + set_bit(KGSL_FT_REPLAY, &cmdobj->fault_policy); else cmdobj->fault_policy = adreno_dev->ft_policy; } diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c index 21a6399ba38e..b8ae24bc3935 100644 --- a/drivers/gpu/msm/adreno_drawctxt.c +++ b/drivers/gpu/msm/adreno_drawctxt.c @@ -346,6 +346,7 @@ adreno_drawctxt_create(struct kgsl_device_private *dev_priv, KGSL_CONTEXT_PER_CONTEXT_TS | KGSL_CONTEXT_USER_GENERATED_TS | KGSL_CONTEXT_NO_FAULT_TOLERANCE | + KGSL_CONTEXT_INVALIDATE_ON_FAULT | KGSL_CONTEXT_CTX_SWITCH | KGSL_CONTEXT_PRIORITY_MASK | KGSL_CONTEXT_TYPE_MASK | diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 1de8e212a703..986026aa1b66 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -260,9 +260,12 @@ kgsl_mem_entry_create(void) { struct kgsl_mem_entry *entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (entry != NULL) + if (entry != NULL) { kref_init(&entry->refcount); + /* put this ref in the caller functions after init */ + kref_get(&entry->refcount); + } return entry; } #ifdef CONFIG_DMA_SHARED_BUFFER @@ -1764,9 +1767,9 @@ long kgsl_ioctl_drawctxt_create(struct kgsl_device_private *dev_priv, /* Commit the pointer to the context in context_idr */ write_lock(&device->context_lock); idr_replace(&device->context_idr, context, context->id); + param->drawctxt_id = context->id; write_unlock(&device->context_lock); - param->drawctxt_id = context->id; done: return result; } @@ -2399,6 +2402,9 @@ long kgsl_ioctl_gpuobj_import(struct kgsl_device_private *dev_priv, trace_kgsl_mem_map(entry, fd); kgsl_mem_entry_commit_process(entry); + + /* put the extra refcount for kgsl_mem_entry_create() */ + kgsl_mem_entry_put(entry); return 0; unmap: @@ -2705,6 +2711,9 @@ long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, trace_kgsl_mem_map(entry, param->fd); kgsl_mem_entry_commit_process(entry); + + /* put the extra refcount for kgsl_mem_entry_create() */ + kgsl_mem_entry_put(entry); return result; error_attach: @@ -3143,6 +3152,9 @@ long kgsl_ioctl_gpuobj_alloc(struct kgsl_device_private *dev_priv, param->mmapsize = kgsl_memdesc_footprint(&entry->memdesc); param->id = entry->id; + /* put the extra refcount for kgsl_mem_entry_create() */ + kgsl_mem_entry_put(entry); + return 0; } @@ -3166,6 +3178,9 @@ long kgsl_ioctl_gpumem_alloc(struct kgsl_device_private *dev_priv, param->size = (size_t) entry->memdesc.size; param->flags = (unsigned int) entry->memdesc.flags; + /* put the extra refcount for kgsl_mem_entry_create() */ + kgsl_mem_entry_put(entry); + return 0; } @@ -3189,6 +3204,9 @@ long kgsl_ioctl_gpumem_alloc_id(struct kgsl_device_private *dev_priv, param->mmapsize = (size_t) kgsl_memdesc_footprint(&entry->memdesc); param->gpuaddr = (unsigned long) entry->memdesc.gpuaddr; + /* put the extra refcount for kgsl_mem_entry_create() */ + kgsl_mem_entry_put(entry); + return 0; } @@ -3306,6 +3324,9 @@ long kgsl_ioctl_sparse_phys_alloc(struct kgsl_device_private *dev_priv, trace_sparse_phys_alloc(entry->id, param->size, param->pagesize); kgsl_mem_entry_commit_process(entry); + /* put the extra refcount for kgsl_mem_entry_create() */ + kgsl_mem_entry_put(entry); + return 0; err_invalid_pages: @@ -3385,6 +3406,9 @@ long kgsl_ioctl_sparse_virt_alloc(struct kgsl_device_private *dev_priv, trace_sparse_virt_alloc(entry->id, param->size, param->pagesize); kgsl_mem_entry_commit_process(entry); + /* put the extra refcount for kgsl_mem_entry_create() */ + kgsl_mem_entry_put(entry); + return 0; } diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index 177b283a2dda..d93fd9bfbcd0 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -77,6 +77,7 @@ enum kgsl_event_results { { KGSL_CONTEXT_PER_CONTEXT_TS, "PER_CONTEXT_TS" }, \ { KGSL_CONTEXT_USER_GENERATED_TS, "USER_TS" }, \ { KGSL_CONTEXT_NO_FAULT_TOLERANCE, "NO_FT" }, \ + { KGSL_CONTEXT_INVALIDATE_ON_FAULT, "INVALIDATE_ON_FAULT" }, \ { KGSL_CONTEXT_PWR_CONSTRAINT, "PWR" }, \ { KGSL_CONTEXT_SAVE_GMEM, "SAVE_GMEM" } diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 57c191798699..ddbf7e7e0d98 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -274,7 +274,7 @@ cleanup: * * This routine is called normally during driver unloading or exiting. */ -void hv_cleanup(void) +void hv_cleanup(bool crash) { union hv_x64_msr_hypercall_contents hypercall_msr; @@ -284,7 +284,8 @@ void hv_cleanup(void) if (hv_context.hypercall_page) { hypercall_msr.as_uint64 = 0; wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); - vfree(hv_context.hypercall_page); + if (!crash) + vfree(hv_context.hypercall_page); hv_context.hypercall_page = NULL; } @@ -304,7 +305,8 @@ void hv_cleanup(void) hypercall_msr.as_uint64 = 0; wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64); - vfree(hv_context.tsc_page); + if (!crash) + vfree(hv_context.tsc_page); hv_context.tsc_page = NULL; } #endif diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index b853b4b083bd..43af91362be5 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -714,7 +714,7 @@ static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) * If the pfn range we are dealing with is not in the current * "hot add block", move on. */ - if ((start_pfn >= has->end_pfn)) + if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn) continue; /* * If the current hot add-request extends beyond @@ -768,7 +768,7 @@ static unsigned long handle_pg_range(unsigned long pg_start, * If the pfn range we are dealing with is not in the current * "hot add block", move on. */ - if ((start_pfn >= has->end_pfn)) + if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn) continue; old_covered_state = has->covered_end_pfn; diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 12156db2e88e..75e383e6d03d 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -581,7 +581,7 @@ struct hv_ring_buffer_debug_info { extern int hv_init(void); -extern void hv_cleanup(void); +extern void hv_cleanup(bool crash); extern int hv_post_message(union hv_connection_id connection_id, enum hv_message_type message_type, diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 509ed9731630..802dcb409030 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -889,7 +889,7 @@ err_alloc: bus_unregister(&hv_bus); err_cleanup: - hv_cleanup(); + hv_cleanup(false); return ret; } @@ -1254,7 +1254,7 @@ static void hv_kexec_handler(void) vmbus_initiate_unload(); for_each_online_cpu(cpu) smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1); - hv_cleanup(); + hv_cleanup(false); }; static void hv_crash_handler(struct pt_regs *regs) @@ -1266,7 +1266,7 @@ static void hv_crash_handler(struct pt_regs *regs) * for kdump. */ hv_synic_cleanup(NULL); - hv_cleanup(); + hv_cleanup(true); }; static int __init hv_acpi_init(void) @@ -1330,7 +1330,7 @@ static void __exit vmbus_exit(void) &hyperv_panic_block); } bus_unregister(&hv_bus); - hv_cleanup(); + hv_cleanup(false); for_each_online_cpu(cpu) { tasklet_kill(hv_context.event_dpc[cpu]); smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 287d839f98d0..9f9dd574c8d0 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, 2016 The Linux Foundation. All rights reserved. +/* Copyright (c) 2014, 2016-2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -2557,6 +2557,8 @@ static int etm4_set_reg_dump(struct etmv4_drvdata *drvdata) drvdata->reg_data.addr = virt_to_phys(baddr); drvdata->reg_data.len = size; + scnprintf(drvdata->reg_data.name, sizeof(drvdata->reg_data.name), + "KETM_REG%d", drvdata->cpu); dump_entry.id = MSM_DUMP_DATA_ETM_REG + drvdata->cpu; dump_entry.addr = virt_to_phys(&drvdata->reg_data); diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 34b12e015768..c5998bd5ce02 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, 2016 The Linux Foundation. All rights reserved. +/* Copyright (c) 2012, 2016-2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1726,6 +1726,9 @@ static int tmc_etf_set_buf_dump(struct tmc_drvdata *drvdata) drvdata->buf_data.addr = virt_to_phys(drvdata->buf); drvdata->buf_data.len = drvdata->size; + scnprintf(drvdata->buf_data.name, sizeof(drvdata->buf_data.name), + "KTMC_ETF%d", count); + dump_entry.id = MSM_DUMP_DATA_TMC_ETF + count; dump_entry.addr = virt_to_phys(&drvdata->buf_data); @@ -1817,6 +1820,8 @@ static int tmc_set_reg_dump(struct tmc_drvdata *drvdata) drvdata->reg_data.addr = virt_to_phys(drvdata->reg_buf); drvdata->reg_data.len = size; + scnprintf(drvdata->reg_data.name, sizeof(drvdata->reg_data.name), + "KTMC_REG%d", count); dump_entry.id = MSM_DUMP_DATA_TMC_REG + count; dump_entry.addr = virt_to_phys(&drvdata->reg_data); diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 0470fc843d4e..9b6854607d73 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -151,7 +151,9 @@ static irqreturn_t tiadc_irq_h(int irq, void *private) { struct iio_dev *indio_dev = private; struct tiadc_device *adc_dev = iio_priv(indio_dev); - unsigned int status, config; + unsigned int status, config, adc_fsm; + unsigned short count = 0; + status = tiadc_readl(adc_dev, REG_IRQSTATUS); /* @@ -165,6 +167,15 @@ static irqreturn_t tiadc_irq_h(int irq, void *private) tiadc_writel(adc_dev, REG_CTRL, config); tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW | IRQENB_FIFO1THRES); + + /* wait for idle state. + * ADC needs to finish the current conversion + * before disabling the module + */ + do { + adc_fsm = tiadc_readl(adc_dev, REG_ADCFSM); + } while (adc_fsm != 0x10 && count++ < 100); + tiadc_writel(adc_dev, REG_CTRL, (config | CNTRLREG_TSCSSENB)); return IRQ_HANDLED; } else if (status & IRQENB_FIFO1THRES) { diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index 595511022795..0a86ef43e781 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -51,8 +51,6 @@ static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state) st->report_state.report_id, st->report_state.index, HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM); - - poll_value = hid_sensor_read_poll_value(st); } else { int val; @@ -89,7 +87,9 @@ static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state) sensor_hub_get_feature(st->hsdev, st->power_state.report_id, st->power_state.index, sizeof(state_val), &state_val); - if (state && poll_value) + if (state) + poll_value = hid_sensor_read_poll_value(st); + if (poll_value > 0) msleep_interruptible(poll_value * 2); return 0; diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c index d96aa27dfcdc..db64adfbe1af 100644 --- a/drivers/input/joystick/iforce/iforce-usb.c +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -141,6 +141,9 @@ static int iforce_usb_probe(struct usb_interface *intf, interface = intf->cur_altsetting; + if (interface->desc.bNumEndpoints < 2) + return -ENODEV; + epirq = &interface->endpoint[0].desc; epout = &interface->endpoint[1].desc; diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c index 9365535ba7f1..50a7faa504f7 100644 --- a/drivers/input/misc/cm109.c +++ b/drivers/input/misc/cm109.c @@ -675,6 +675,10 @@ static int cm109_usb_probe(struct usb_interface *intf, int error = -ENOMEM; interface = intf->cur_altsetting; + + if (interface->desc.bNumEndpoints < 1) + return -ENODEV; + endpoint = &interface->endpoint[0].desc; if (!usb_endpoint_is_int_in(endpoint)) diff --git a/drivers/input/misc/hbtp_input.c b/drivers/input/misc/hbtp_input.c index 1df5d8812991..21898c308075 100644 --- a/drivers/input/misc/hbtp_input.c +++ b/drivers/input/misc/hbtp_input.c @@ -1,5 +1,5 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This 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,8 @@ struct hbtp_data { struct input_dev *input_dev; s32 count; struct mutex mutex; + struct mutex sensormutex; + struct hbtp_sensor_data *sensor_data; bool touch_status[HBTP_MAX_FINGER]; #if defined(CONFIG_FB) struct notifier_block fb_notif; @@ -87,10 +89,14 @@ struct hbtp_data { u32 power_on_delay; u32 power_off_delay; bool manage_pin_ctrl; + s16 ROI[MAX_ROI_SIZE]; + s16 accelBuffer[MAX_ACCEL_SIZE]; }; static struct hbtp_data *hbtp; +static struct kobject *sensor_kobject; + #if defined(CONFIG_FB) static int hbtp_fb_suspend(struct hbtp_data *ts); static int hbtp_fb_early_resume(struct hbtp_data *ts); @@ -103,10 +109,27 @@ static int fb_notifier_callback(struct notifier_block *self, { int blank; struct fb_event *evdata = data; + struct fb_info *fbi = NULL; struct hbtp_data *hbtp_data = container_of(self, struct hbtp_data, fb_notif); - if (evdata && evdata->data && hbtp_data && + if (!evdata) { + pr_debug("evdata is NULL"); + return 0; + } + fbi = evdata->info; + + /* + * Node 0 is the primary display and others are + * external displays such as HDMI/DP. + * We need to handle only fb event for the primary display. + */ + if (!fbi || fbi->node != 0) { + pr_debug("%s: no need to handle the fb event", __func__); + return 0; + } + + if (evdata->data && hbtp_data && (event == FB_EARLY_EVENT_BLANK || event == FB_R_EARLY_EVENT_BLANK)) { blank = *(int *)(evdata->data); @@ -135,7 +158,7 @@ static int fb_notifier_callback(struct notifier_block *self, } } - if (evdata && evdata->data && hbtp_data && + if (evdata->data && hbtp_data && event == FB_EVENT_BLANK) { blank = *(int *)(evdata->data); if (blank == FB_BLANK_POWERDOWN) { @@ -150,6 +173,46 @@ static int fb_notifier_callback(struct notifier_block *self, } #endif +static ssize_t hbtp_sensor_roi_show(struct file *dev, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t pos, + size_t size) { + mutex_lock(&hbtp->sensormutex); + memcpy(buf, hbtp->ROI, size); + mutex_unlock(&hbtp->sensormutex); + + return size; +} + +static ssize_t hbtp_sensor_vib_show(struct file *dev, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t pos, + size_t size) { + mutex_lock(&hbtp->sensormutex); + memcpy(buf, hbtp->accelBuffer, size); + mutex_unlock(&hbtp->sensormutex); + + return size; +} + +static struct bin_attribute capdata_attr = { + .attr = { + .name = "capdata", + .mode = S_IRUGO, + }, + .size = 1024, + .read = hbtp_sensor_roi_show, + .write = NULL, +}; + +static struct bin_attribute vibdata_attr = { + .attr = { + .name = "vib_data", + .mode = S_IRUGO, + }, + .size = MAX_ACCEL_SIZE*sizeof(int16_t), + .read = hbtp_sensor_vib_show, + .write = NULL, +}; + static int hbtp_input_open(struct inode *inode, struct file *file) { mutex_lock(&hbtp->mutex); @@ -748,6 +811,22 @@ static long hbtp_input_ioctl_handler(struct file *file, unsigned int cmd, return -EINVAL; } break; + + case HBTP_SET_SENSORDATA: + if (copy_from_user(hbtp->sensor_data, (void *)arg, + sizeof(struct hbtp_sensor_data))) { + pr_err("%s: Error copying data\n", __func__); + return -EFAULT; + } + mutex_lock(&hbtp->sensormutex); + memcpy(hbtp->ROI, hbtp->sensor_data->ROI, sizeof(hbtp->ROI)); + memcpy(hbtp->accelBuffer, hbtp->sensor_data->accelBuffer, + sizeof(hbtp->accelBuffer)); + mutex_unlock(&hbtp->sensormutex); + + error = 0; + break; + default: pr_err("%s: Unsupported ioctl command %u\n", __func__, cmd); error = -EINVAL; @@ -1358,7 +1437,13 @@ static int __init hbtp_init(void) if (!hbtp) return -ENOMEM; + hbtp->sensor_data = kzalloc(sizeof(struct hbtp_sensor_data), + GFP_KERNEL); + if (!hbtp->sensor_data) + goto err_sensordata; + mutex_init(&hbtp->mutex); + mutex_init(&hbtp->sensormutex); error = misc_register(&hbtp_input_misc); if (error) { @@ -1376,6 +1461,28 @@ static int __init hbtp_init(void) } #endif + sensor_kobject = kobject_create_and_add("hbtpsensor", kernel_kobj); + if (!sensor_kobject) { + pr_err("%s: Could not create hbtpsensor kobject\n", __func__); + goto err_kobject_create; + } + + error = sysfs_create_bin_file(sensor_kobject, &capdata_attr); + if (error < 0) { + pr_err("%s: hbtp capdata sysfs creation failed: %d\n", __func__, + error); + goto err_sysfs_create_capdata; + } + pr_debug("capdata sysfs creation success\n"); + + error = sysfs_create_bin_file(sensor_kobject, &vibdata_attr); + if (error < 0) { + pr_err("%s: vibdata sysfs creation failed: %d\n", __func__, + error); + goto err_sysfs_create_vibdata; + } + pr_debug("vibdata sysfs creation success\n"); + error = platform_driver_register(&hbtp_pdev_driver); if (error) { pr_err("Failed to register platform driver: %d\n", error); @@ -1385,12 +1492,20 @@ static int __init hbtp_init(void) return 0; err_platform_drv_reg: + sysfs_remove_bin_file(sensor_kobject, &vibdata_attr); +err_sysfs_create_vibdata: + sysfs_remove_bin_file(sensor_kobject, &capdata_attr); +err_sysfs_create_capdata: + kobject_put(sensor_kobject); +err_kobject_create: #if defined(CONFIG_FB) fb_unregister_client(&hbtp->fb_notif); err_fb_reg: #endif misc_deregister(&hbtp_input_misc); err_misc_reg: + kfree(hbtp->sensor_data); +err_sensordata: kfree(hbtp); return error; @@ -1398,6 +1513,9 @@ err_misc_reg: static void __exit hbtp_exit(void) { + sysfs_remove_bin_file(sensor_kobject, &vibdata_attr); + sysfs_remove_bin_file(sensor_kobject, &capdata_attr); + kobject_put(sensor_kobject); misc_deregister(&hbtp_input_misc); if (hbtp->input_dev) input_unregister_device(hbtp->input_dev); @@ -1408,6 +1526,7 @@ static void __exit hbtp_exit(void) platform_driver_unregister(&hbtp_pdev_driver); + kfree(hbtp->sensor_data); kfree(hbtp); } diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 9c0ea36913b4..f4e8fbec6a94 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1667,6 +1667,10 @@ static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pc return -EINVAL; alt = pcu->ctrl_intf->cur_altsetting; + + if (alt->desc.bNumEndpoints < 1) + return -ENODEV; + pcu->ep_ctrl = &alt->endpoint[0].desc; pcu->max_ctrl_size = usb_endpoint_maxp(pcu->ep_ctrl); diff --git a/drivers/input/misc/qpnp-power-on.c b/drivers/input/misc/qpnp-power-on.c index add11d47ea2f..339f94c072f4 100644 --- a/drivers/input/misc/qpnp-power-on.c +++ b/drivers/input/misc/qpnp-power-on.c @@ -207,7 +207,7 @@ struct qpnp_pon { int pon_power_off_reason; int num_pon_reg; int num_pon_config; - u32 dbc; + u32 dbc_time_us; u32 uvlo; int warm_reset_poff_type; int hard_reset_poff_type; @@ -219,6 +219,8 @@ struct qpnp_pon { u8 warm_reset_reason2; bool is_spon; bool store_hard_reset_reason; + bool kpdpwr_dbc_enable; + ktime_t kpdpwr_last_release_time; }; static int pon_ship_mode_en; @@ -381,7 +383,7 @@ static int qpnp_pon_set_dbc(struct qpnp_pon *pon, u32 delay) int rc = 0; u32 val; - if (delay == pon->dbc) + if (delay == pon->dbc_time_us) goto out; if (pon->pon_input) @@ -409,7 +411,7 @@ static int qpnp_pon_set_dbc(struct qpnp_pon *pon, u32 delay) goto unlock; } - pon->dbc = delay; + pon->dbc_time_us = delay; unlock: if (pon->pon_input) @@ -418,12 +420,34 @@ out: return rc; } +static int qpnp_pon_get_dbc(struct qpnp_pon *pon, u32 *delay) +{ + int rc; + unsigned int val; + + rc = regmap_read(pon->regmap, QPNP_PON_DBC_CTL(pon), &val); + if (rc) { + pr_err("Unable to read pon_dbc_ctl rc=%d\n", rc); + return rc; + } + val &= QPNP_PON_DBC_DELAY_MASK(pon); + + if (is_pon_gen2(pon)) + *delay = USEC_PER_SEC / + (1 << (QPNP_PON_GEN2_DELAY_BIT_SHIFT - val)); + else + *delay = USEC_PER_SEC / + (1 << (QPNP_PON_DELAY_BIT_SHIFT - val)); + + return rc; +} + static ssize_t qpnp_pon_dbc_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qpnp_pon *pon = dev_get_drvdata(dev); - return snprintf(buf, QPNP_PON_BUFFER_SIZE, "%d\n", pon->dbc); + return snprintf(buf, QPNP_PON_BUFFER_SIZE, "%d\n", pon->dbc_time_us); } static ssize_t qpnp_pon_dbc_store(struct device *dev, @@ -777,6 +801,7 @@ qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type) u8 pon_rt_bit = 0; u32 key_status; uint pon_rt_sts; + u64 elapsed_us; cfg = qpnp_get_cfg(pon, pon_type); if (!cfg) @@ -786,6 +811,15 @@ qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type) if (!cfg->key_code) return 0; + if (pon->kpdpwr_dbc_enable && cfg->pon_type == PON_KPDPWR) { + elapsed_us = ktime_us_delta(ktime_get(), + pon->kpdpwr_last_release_time); + if (elapsed_us < pon->dbc_time_us) { + pr_debug("Ignoring kpdpwr event - within debounce time\n"); + return 0; + } + } + /* check the RT status to get the current status of the line */ rc = regmap_read(pon->regmap, QPNP_PON_RT_STS(pon), &pon_rt_sts); if (rc) { @@ -814,6 +848,11 @@ qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type) cfg->key_code, pon_rt_sts); key_status = pon_rt_sts & pon_rt_bit; + if (pon->kpdpwr_dbc_enable && cfg->pon_type == PON_KPDPWR) { + if (!key_status) + pon->kpdpwr_last_release_time = ktime_get(); + } + /* * simulate press event in case release event occurred * without a press event @@ -2233,8 +2272,22 @@ static int qpnp_pon_probe(struct platform_device *pdev) } } else { rc = qpnp_pon_set_dbc(pon, delay); + if (rc) { + dev_err(&pdev->dev, + "Unable to set PON debounce delay rc=%d\n", rc); + return rc; + } + } + rc = qpnp_pon_get_dbc(pon, &pon->dbc_time_us); + if (rc) { + dev_err(&pdev->dev, + "Unable to get PON debounce delay rc=%d\n", rc); + return rc; } + pon->kpdpwr_dbc_enable = of_property_read_bool(pon->pdev->dev.of_node, + "qcom,kpdpwr-sw-debounce"); + rc = of_property_read_u32(pon->pdev->dev.of_node, "qcom,warm-reset-poweroff-type", &pon->warm_reset_poff_type); diff --git a/drivers/input/misc/vl53L0/stmvl53l0-cci.h b/drivers/input/misc/vl53L0/stmvl53l0-cci.h index 51477701cb1d..b99ffe783e59 100644 --- a/drivers/input/misc/vl53L0/stmvl53l0-cci.h +++ b/drivers/input/misc/vl53L0/stmvl53l0-cci.h @@ -51,11 +51,12 @@ struct cci_data { struct msm_camera_gpio_conf *gconf; struct msm_pinctrl_info pinctrl_info; uint8_t cam_pinctrl_status; - }; + int stmvl53l0_init_cci(void); void stmvl53l0_exit_cci(void *); int stmvl53l0_power_down_cci(void *); int stmvl53l0_power_up_cci(void *, unsigned int *); +int stmvl53l0_cci_power_status(void *); #endif /* CAMERA_CCI */ #endif /* STMVL53L0_CCI_H */ diff --git a/drivers/input/misc/vl53L0/stmvl53l0.h b/drivers/input/misc/vl53L0/stmvl53l0.h index b15d78cc6825..b6ef547e86e7 100644 --- a/drivers/input/misc/vl53L0/stmvl53l0.h +++ b/drivers/input/misc/vl53L0/stmvl53l0.h @@ -208,6 +208,7 @@ struct stmvl53l0_module_fn_t { void (*deinit)(void *); int (*power_up)(void *, unsigned int *); int (*power_down)(void *); + int (*query_power_status)(void *); }; diff --git a/drivers/input/misc/vl53L0/stmvl53l0_module-cci.c b/drivers/input/misc/vl53L0/stmvl53l0_module-cci.c index 79fba00ea086..fecafe8db949 100644 --- a/drivers/input/misc/vl53L0/stmvl53l0_module-cci.c +++ b/drivers/input/misc/vl53L0/stmvl53l0_module-cci.c @@ -187,8 +187,15 @@ static const struct v4l2_subdev_internal_ops msm_tof_internal_ops = { static long msm_tof_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { - vl53l0_dbgmsg("Subdev_ioctl not handled\n"); - return 0; + struct cci_data *cci_object = NULL; + int32_t rc = 0; + + cci_object = v4l2_get_subdevdata(sd); + if (cmd == MSM_SD_SHUTDOWN) + cci_object->power_up = 0; + + vl53l0_dbgmsg("cmd = %d power_up = %d", cmd, cci_object->power_up); + return rc; } static int32_t msm_tof_power(struct v4l2_subdev *sd, int on) @@ -493,6 +500,13 @@ int stmvl53l0_power_down_cci(void *cci_object) return ret; } +int stmvl53l0_cci_power_status(void *cci_object) +{ + struct cci_data *data = (struct cci_data *)cci_object; + + return data->power_up; +} + int stmvl53l0_init_cci(void) { int ret = 0; diff --git a/drivers/input/misc/vl53L0/stmvl53l0_module.c b/drivers/input/misc/vl53L0/stmvl53l0_module.c index 6881aba9fc64..c46be43a03e5 100644 --- a/drivers/input/misc/vl53L0/stmvl53l0_module.c +++ b/drivers/input/misc/vl53L0/stmvl53l0_module.c @@ -39,7 +39,6 @@ #include "vl53l010_api.h" #define USE_INT - /* #define DEBUG_TIME_LOG */ #ifdef DEBUG_TIME_LOG struct timeval start_tv, stop_tv; @@ -55,6 +54,7 @@ static struct stmvl53l0_module_fn_t stmvl53l0_module_func_tbl = { .deinit = stmvl53l0_exit_cci, .power_up = stmvl53l0_power_up_cci, .power_down = stmvl53l0_power_down_cci, + .query_power_status = stmvl53l0_cci_power_status, }; #else static struct stmvl53l0_module_fn_t stmvl53l0_module_func_tbl = { @@ -62,6 +62,7 @@ static struct stmvl53l0_module_fn_t stmvl53l0_module_func_tbl = { .deinit = stmvl53l0_exit_i2c, .power_up = stmvl53l0_power_up_i2c, .power_down = stmvl53l0_power_down_i2c, + .stmv53l0_cci_power_status = NULL; }; #endif struct stmvl53l0_module_fn_t *pmodule_func_tbl; @@ -956,6 +957,12 @@ static void stmvl53l0_work_handler(struct work_struct *work) mutex_lock(&data->work_mutex); /* vl53l0_dbgmsg("Enter\n"); */ + if (pmodule_func_tbl->query_power_status(data->client_object) == 0) { + if (data->enable_ps_sensor == 1) { + stmvl53l0_stop(data); + data->enable_ps_sensor = 0; + } + } if (vl53l0_dev->enable_ps_sensor == 1) { #ifdef DEBUG_TIME_LOG diff --git a/drivers/input/misc/yealink.c b/drivers/input/misc/yealink.c index 79c964c075f1..6e7ff9561d92 100644 --- a/drivers/input/misc/yealink.c +++ b/drivers/input/misc/yealink.c @@ -875,6 +875,10 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id) int ret, pipe, i; interface = intf->cur_altsetting; + + if (interface->desc.bNumEndpoints < 1) + return -ENODEV; + endpoint = &interface->endpoint[0].desc; if (!usb_endpoint_is_int_in(endpoint)) return -ENODEV; diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index ed1935f300a7..da5458dfb1e3 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -218,17 +218,19 @@ static int elan_query_product(struct elan_tp_data *data) static int elan_check_ASUS_special_fw(struct elan_tp_data *data) { - if (data->ic_type != 0x0E) - return false; - - switch (data->product_id) { - case 0x05 ... 0x07: - case 0x09: - case 0x13: + if (data->ic_type == 0x0E) { + switch (data->product_id) { + case 0x05 ... 0x07: + case 0x09: + case 0x13: + return true; + } + } else if (data->ic_type == 0x08 && data->product_id == 0x26) { + /* ASUS EeeBook X205TA */ return true; - default: - return false; } + + return false; } static int __elan_initialize(struct elan_tp_data *data) diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 0cdd95801a25..25eab453f2b2 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -120,6 +120,13 @@ static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = { }, }, { + /* Dell Embedded Box PC 3000 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Embedded Box PC 3000"), + }, + }, + { /* OQO Model 01 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "OQO"), diff --git a/drivers/input/tablet/hanwang.c b/drivers/input/tablet/hanwang.c index cd852059b99e..df4bea96d7ed 100644 --- a/drivers/input/tablet/hanwang.c +++ b/drivers/input/tablet/hanwang.c @@ -340,6 +340,9 @@ static int hanwang_probe(struct usb_interface *intf, const struct usb_device_id int error; int i; + if (intf->cur_altsetting->desc.bNumEndpoints < 1) + return -ENODEV; + hanwang = kzalloc(sizeof(struct hanwang), GFP_KERNEL); input_dev = input_allocate_device(); if (!hanwang || !input_dev) { diff --git a/drivers/input/tablet/kbtab.c b/drivers/input/tablet/kbtab.c index d2ac7c2b5b82..2812f9236b7d 100644 --- a/drivers/input/tablet/kbtab.c +++ b/drivers/input/tablet/kbtab.c @@ -122,6 +122,9 @@ static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *i struct input_dev *input_dev; int error = -ENOMEM; + if (intf->cur_altsetting->desc.bNumEndpoints < 1) + return -ENODEV; + kbtab = kzalloc(sizeof(struct kbtab), GFP_KERNEL); input_dev = input_allocate_device(); if (!kbtab || !input_dev) diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 45b466e3bbe8..0146e2c74649 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -500,6 +500,9 @@ static int sur40_probe(struct usb_interface *interface, if (iface_desc->desc.bInterfaceClass != 0xFF) return -ENODEV; + if (iface_desc->desc.bNumEndpoints < 5) + return -ENODEV; + /* Use endpoint #4 (0x86). */ endpoint = &iface_desc->endpoint[4].desc; if (endpoint->bEndpointAddress != TOUCH_ENDPOINT) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 51159711b1d8..7922608287d7 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -234,6 +234,7 @@ #define SCTLR_S1_ASIDPNE (1 << 12) #define SCTLR_CFCFG (1 << 7) +#define SCTLR_HUPCF (1 << 8) #define SCTLR_CFIE (1 << 6) #define SCTLR_CFRE (1 << 5) #define SCTLR_E (1 << 4) @@ -1628,6 +1629,11 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, /* SCTLR */ reg = SCTLR_CFCFG | SCTLR_CFIE | SCTLR_CFRE | SCTLR_EAE_SBOP; + if (smmu_domain->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))) || !stage1) @@ -3138,7 +3144,11 @@ static int arm_smmu_domain_get_attr(struct iommu_domain *domain, & (1 << DOMAIN_ATTR_ENABLE_TTBR1)); ret = 0; break; - + case DOMAIN_ATTR_CB_STALL_DISABLE: + *((int *)data) = !!(smmu_domain->attributes + & (1 << DOMAIN_ATTR_CB_STALL_DISABLE)); + ret = 0; + break; default: ret = -ENODEV; break; @@ -3288,6 +3298,49 @@ static int arm_smmu_domain_set_attr(struct iommu_domain *domain, 1 << DOMAIN_ATTR_ENABLE_TTBR1; ret = 0; break; + case DOMAIN_ATTR_GEOMETRY: { + struct iommu_domain_geometry *geometry = + (struct iommu_domain_geometry *)data; + + if (smmu_domain->smmu != NULL) { + dev_err(smmu_domain->smmu->dev, + "cannot set geometry attribute while attached\n"); + ret = -EBUSY; + break; + } + + if (geometry->aperture_start >= SZ_1G * 4ULL || + geometry->aperture_end >= SZ_1G * 4ULL) { + pr_err("fastmap does not support IOVAs >= 4GB\n"); + ret = -EINVAL; + break; + } + if (smmu_domain->attributes + & (1 << DOMAIN_ATTR_GEOMETRY)) { + if (geometry->aperture_start + < domain->geometry.aperture_start) + domain->geometry.aperture_start = + geometry->aperture_start; + + if (geometry->aperture_end + > domain->geometry.aperture_end) + domain->geometry.aperture_end = + geometry->aperture_end; + } else { + smmu_domain->attributes |= 1 << DOMAIN_ATTR_GEOMETRY; + domain->geometry.aperture_start = + geometry->aperture_start; + domain->geometry.aperture_end = geometry->aperture_end; + } + ret = 0; + break; + } + case DOMAIN_ATTR_CB_STALL_DISABLE: + if (*((int *)data)) + smmu_domain->attributes |= + 1 << DOMAIN_ATTR_CB_STALL_DISABLE; + ret = 0; + break; default: ret = -ENODEV; break; diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c index 8c6364f03eac..0881d68f34d8 100644 --- a/drivers/iommu/dma-mapping-fast.c +++ b/drivers/iommu/dma-mapping-fast.c @@ -188,7 +188,9 @@ static dma_addr_t __fast_smmu_alloc_iova(struct dma_fast_smmu_mapping *mapping, iommu_tlbiall(mapping->domain); mapping->have_stale_tlbs = false; - av8l_fast_clear_stale_ptes(mapping->pgtbl_pmds, mapping->base, + av8l_fast_clear_stale_ptes(mapping->pgtbl_pmds, + mapping->domain->geometry.aperture_start, + mapping->base, mapping->base + mapping->size - 1, skip_sync); } @@ -367,7 +369,8 @@ static dma_addr_t fast_smmu_map_page(struct device *dev, struct page *page, if (unlikely(iova == DMA_ERROR_CODE)) goto fail; - pmd = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, iova); + pmd = iopte_pmd_offset(mapping->pgtbl_pmds, + mapping->domain->geometry.aperture_start, iova); if (unlikely(av8l_fast_map_public(pmd, phys_to_map, len, prot))) goto fail_free_iova; @@ -391,7 +394,8 @@ static void fast_smmu_unmap_page(struct device *dev, dma_addr_t iova, struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast; unsigned long flags; av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, - mapping->base, iova); + mapping->domain->geometry.aperture_start, + iova); unsigned long offset = iova & ~FAST_PAGE_MASK; size_t len = ALIGN(size + offset, FAST_PAGE_SIZE); int nptes = len >> FAST_PAGE_SHIFT; @@ -414,7 +418,8 @@ static void fast_smmu_sync_single_for_cpu(struct device *dev, { struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast; av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, - mapping->base, iova); + mapping->domain->geometry.aperture_start, + iova); unsigned long offset = iova & ~FAST_PAGE_MASK; struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK)); @@ -427,7 +432,8 @@ static void fast_smmu_sync_single_for_device(struct device *dev, { struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast; av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, - mapping->base, iova); + mapping->domain->geometry.aperture_start, + iova); unsigned long offset = iova & ~FAST_PAGE_MASK; struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK)); @@ -555,8 +561,9 @@ static void *fast_smmu_alloc(struct device *dev, size_t size, while (sg_miter_next(&miter)) { int nptes = miter.length >> FAST_PAGE_SHIFT; - ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, - iova_iter); + ptep = iopte_pmd_offset(mapping->pgtbl_pmds, + mapping->domain->geometry.aperture_start, + iova_iter); if (unlikely(av8l_fast_map_public( ptep, page_to_phys(miter.page), miter.length, prot))) { @@ -584,7 +591,9 @@ static void *fast_smmu_alloc(struct device *dev, size_t size, out_unmap: /* need to take the lock again for page tables and iova */ spin_lock_irqsave(&mapping->lock, flags); - ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, dma_addr); + ptep = iopte_pmd_offset(mapping->pgtbl_pmds, + mapping->domain->geometry.aperture_start, + dma_addr); av8l_fast_unmap_public(ptep, size); fast_dmac_clean_range(mapping, ptep, ptep + count); out_free_iova: @@ -616,7 +625,8 @@ static void fast_smmu_free(struct device *dev, size_t size, pages = area->pages; dma_common_free_remap(vaddr, size, VM_USERMAP, false); - ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, dma_handle); + ptep = iopte_pmd_offset(mapping->pgtbl_pmds, + mapping->domain->geometry.aperture_start, dma_handle); spin_lock_irqsave(&mapping->lock, flags); av8l_fast_unmap_public(ptep, size); fast_dmac_clean_range(mapping, ptep, ptep + count); @@ -720,7 +730,7 @@ static const struct dma_map_ops fast_smmu_dma_ops = { * * Creates a mapping structure which holds information about used/unused IO * address ranges, which is required to perform mapping with IOMMU aware - * functions. The only VA range supported is [0, 4GB). + * functions. The only VA range supported is [0, 4GB]. * * The client device need to be attached to the mapping with * fast_smmu_attach_device function. @@ -774,6 +784,7 @@ int fast_smmu_attach_device(struct device *dev, struct iommu_domain *domain = mapping->domain; struct iommu_pgtbl_info info; u64 size = (u64)mapping->bits << PAGE_SHIFT; + struct iommu_domain_geometry geometry; if (mapping->base + size > (SZ_1G * 4ULL)) return -EINVAL; @@ -788,8 +799,11 @@ int fast_smmu_attach_device(struct device *dev, mapping->fast->domain = domain; mapping->fast->dev = dev; - domain->geometry.aperture_start = mapping->base; - domain->geometry.aperture_end = mapping->base + size - 1; + geometry.aperture_start = mapping->base; + geometry.aperture_end = mapping->base + size - 1; + if (iommu_domain_set_attr(domain, DOMAIN_ATTR_GEOMETRY, + &geometry)) + return -EINVAL; if (iommu_attach_device(domain, dev)) return -EINVAL; diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index f0fc6f7b5d98..0628372f3591 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -908,7 +908,7 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf * which we used for the IOMMU lookup. Strictly speaking * we could do this for all PCI devices; we only need to * get the BDF# from the scope table for ACPI matches. */ - if (pdev->is_virtfn) + if (pdev && pdev->is_virtfn) goto got_pdev; *bus = drhd->devices[i].bus; diff --git a/drivers/iommu/io-pgtable-fast.c b/drivers/iommu/io-pgtable-fast.c index 3582e206db68..5378e95c4627 100644 --- a/drivers/iommu/io-pgtable-fast.c +++ b/drivers/iommu/io-pgtable-fast.c @@ -133,6 +133,9 @@ struct av8l_fast_io_pgtable { #define AV8L_FAST_TCR_EPD1_SHIFT 23 #define AV8L_FAST_TCR_EPD1_FAULT 1 +#define AV8L_FAST_TCR_SEP_SHIFT (15 + 32) +#define AV8L_FAST_TCR_SEP_UPSTREAM 7ULL + #define AV8L_FAST_MAIR_ATTR_SHIFT(n) ((n) << 3) #define AV8L_FAST_MAIR_ATTR_MASK 0xff #define AV8L_FAST_MAIR_ATTR_DEVICE 0x04 @@ -173,12 +176,12 @@ static void __av8l_check_for_stale_tlb(av8l_fast_iopte *ptep) } void av8l_fast_clear_stale_ptes(av8l_fast_iopte *pmds, u64 base, - u64 end, bool skip_sync) + u64 start, u64 end, bool skip_sync) { int i; - av8l_fast_iopte *pmdp = pmds; + av8l_fast_iopte *pmdp = iopte_pmd_offset(pmds, base, start); - for (i = base >> AV8L_FAST_PAGE_SHIFT; + for (i = start >> AV8L_FAST_PAGE_SHIFT; i <= (end >> AV8L_FAST_PAGE_SHIFT); ++i) { if (!(*pmdp & AV8L_FAST_PTE_VALID)) { *pmdp = 0; @@ -256,16 +259,17 @@ void av8l_fast_unmap_public(av8l_fast_iopte *ptep, size_t size) __av8l_fast_unmap(ptep, size, true); } -/* upper layer must take care of TLB invalidation */ static size_t av8l_fast_unmap(struct io_pgtable_ops *ops, unsigned long iova, size_t size) { struct av8l_fast_io_pgtable *data = iof_pgtable_ops_to_data(ops); + struct io_pgtable *iop = &data->iop; av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, data->base, iova); unsigned long nptes = size >> AV8L_FAST_PAGE_SHIFT; __av8l_fast_unmap(ptep, size, false); dmac_clean_range(ptep, ptep + nptes); + iop->cfg.tlb->tlb_flush_all(iop->cookie); return size; } @@ -522,6 +526,7 @@ av8l_fast_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie) #if defined(CONFIG_ARM) reg |= ARM_32_LPAE_TCR_EAE; #endif + reg |= AV8L_FAST_TCR_SEP_UPSTREAM << AV8L_FAST_TCR_SEP_SHIFT; cfg->av8l_fast_cfg.tcr = reg; /* MAIRs */ @@ -668,7 +673,7 @@ static int __init av8l_fast_positive_testing(void) } /* sweep up TLB proving PTEs */ - av8l_fast_clear_stale_ptes(pmds, base, max, false); + av8l_fast_clear_stale_ptes(pmds, base, base, max, false); /* map the entire 4GB VA space with 8K map calls */ for (iova = base; iova < max; iova += SZ_8K) { @@ -689,7 +694,7 @@ static int __init av8l_fast_positive_testing(void) } /* sweep up TLB proving PTEs */ - av8l_fast_clear_stale_ptes(pmds, base, max, false); + av8l_fast_clear_stale_ptes(pmds, base, base, max, false); /* map the entire 4GB VA space with 16K map calls */ for (iova = base; iova < max; iova += SZ_16K) { @@ -710,7 +715,7 @@ static int __init av8l_fast_positive_testing(void) } /* sweep up TLB proving PTEs */ - av8l_fast_clear_stale_ptes(pmds, base, max, false); + av8l_fast_clear_stale_ptes(pmds, base, base, max, false); /* map the entire 4GB VA space with 64K map calls */ for (iova = base; iova < max; iova += SZ_64K) { diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c index 75fcde6e2c20..ffeb47c6b367 100644 --- a/drivers/iommu/iommu-debug.c +++ b/drivers/iommu/iommu-debug.c @@ -72,6 +72,8 @@ static const char *iommu_debug_attr_to_string(enum iommu_attr attr) return "DOMAIN_ATTR_FAST"; case DOMAIN_ATTR_EARLY_MAP: return "DOMAIN_ATTR_EARLY_MAP"; + case DOMAIN_ATTR_CB_STALL_DISABLE: + return "DOMAIN_ATTR_CB_STALL_DISABLE"; default: return "Unknown attr!"; } @@ -787,9 +789,13 @@ static int iommu_debug_profiling_fast_show(struct seq_file *s, void *ignored) enum iommu_attr attrs[] = { DOMAIN_ATTR_FAST, DOMAIN_ATTR_ATOMIC, + DOMAIN_ATTR_GEOMETRY, }; int one = 1; - void *attr_values[] = { &one, &one, &one }; + struct iommu_domain_geometry geometry = {0, 0, 0}; + void *attr_values[] = { &one, &one, &geometry}; + + geometry.aperture_end = (dma_addr_t)(SZ_1G * 4ULL - 1); iommu_debug_device_profiling(s, ddev->dev, attrs, attr_values, ARRAY_SIZE(attrs), sizes); diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c index aecec6d32463..7f1c625b08ec 100644 --- a/drivers/isdn/gigaset/bas-gigaset.c +++ b/drivers/isdn/gigaset/bas-gigaset.c @@ -2317,6 +2317,9 @@ static int gigaset_probe(struct usb_interface *interface, return -ENODEV; } + if (hostif->desc.bNumEndpoints < 1) + return -ENODEV; + dev_info(&udev->dev, "%s: Device matched (Vendor: 0x%x, Product: 0x%x)\n", __func__, le16_to_cpu(udev->descriptor.idVendor), diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c index 4c28e0922c84..1a6cad946a25 100644 --- a/drivers/leds/leds-qpnp-flash-v2.c +++ b/drivers/leds/leds-qpnp-flash-v2.c @@ -127,11 +127,11 @@ #define FLASH_LED_LMH_MITIGATION_DISABLE 0 #define FLASH_LED_CHGR_MITIGATION_ENABLE BIT(4) #define FLASH_LED_CHGR_MITIGATION_DISABLE 0 -#define FLASH_LED_MITIGATION_SEL_DEFAULT 2 +#define FLASH_LED_LMH_MITIGATION_SEL_DEFAULT 2 #define FLASH_LED_MITIGATION_SEL_MAX 2 #define FLASH_LED_CHGR_MITIGATION_SEL_SHIFT 4 -#define FLASH_LED_MITIGATION_THRSH_DEFAULT 0xA -#define FLASH_LED_MITIGATION_THRSH_MAX 0x1F +#define FLASH_LED_CHGR_MITIGATION_THRSH_DEFAULT 0xA +#define FLASH_LED_CHGR_MITIGATION_THRSH_MAX 0x1F #define FLASH_LED_LMH_OCV_THRESH_DEFAULT_UV 3700000 #define FLASH_LED_LMH_RBATT_THRESH_DEFAULT_UOHM 400000 #define FLASH_LED_IRES_BASE 3 @@ -152,12 +152,17 @@ #define FLASH_LED_MOD_ENABLE BIT(7) #define FLASH_LED_DISABLE 0x00 #define FLASH_LED_SAFETY_TMR_DISABLED 0x13 -#define FLASH_LED_MIN_CURRENT_MA 25 #define FLASH_LED_MAX_TOTAL_CURRENT_MA 3750 /* notifier call chain for flash-led irqs */ static ATOMIC_NOTIFIER_HEAD(irq_notifier_list); +enum flash_charger_mitigation { + FLASH_DISABLE_CHARGER_MITIGATION, + FLASH_HW_CHARGER_MITIGATION_BY_ILED_THRSHLD, + FLASH_SW_CHARGER_MITIGATION, +}; + enum flash_led_type { FLASH_LED_TYPE_FLASH, FLASH_LED_TYPE_TORCH, @@ -182,6 +187,7 @@ struct flash_node_data { int ires_ua; int max_current; int current_ma; + int prev_current_ma; u8 duration; u8 id; u8 type; @@ -261,6 +267,7 @@ struct qpnp_flash_led { int num_fnodes; int num_snodes; int enable; + int total_current_ma; u16 base; bool trigger_lmh; bool trigger_chgr; @@ -487,10 +494,12 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) if (rc < 0) return rc; + val = led->pdata->chgr_mitigation_sel + << FLASH_LED_CHGR_MITIGATION_SEL_SHIFT; rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_MITIGATION_SEL(led->base), FLASH_LED_CHGR_MITIGATION_SEL_MASK, - led->pdata->chgr_mitigation_sel); + val); if (rc < 0) return rc; @@ -876,14 +885,29 @@ static int qpnp_flash_led_get_max_avail_current(struct qpnp_flash_led *led) return max_avail_current; } +static void qpnp_flash_led_aggregate_max_current(struct flash_node_data *fnode) +{ + struct qpnp_flash_led *led = dev_get_drvdata(&fnode->pdev->dev); + + if (fnode->current_ma) + led->total_current_ma += fnode->current_ma + - fnode->prev_current_ma; + else + led->total_current_ma -= fnode->prev_current_ma; + + fnode->prev_current_ma = fnode->current_ma; +} + static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value) { int prgm_current_ma = value; + int min_ma = fnode->ires_ua / 1000; + struct qpnp_flash_led *led = dev_get_drvdata(&fnode->pdev->dev); if (value <= 0) prgm_current_ma = 0; - else if (value < FLASH_LED_MIN_CURRENT_MA) - prgm_current_ma = FLASH_LED_MIN_CURRENT_MA; + else if (value < min_ma) + prgm_current_ma = min_ma; prgm_current_ma = min(prgm_current_ma, fnode->max_current); fnode->current_ma = prgm_current_ma; @@ -891,6 +915,13 @@ static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value) fnode->current_reg_val = CURRENT_MA_TO_REG_VAL(prgm_current_ma, fnode->ires_ua); fnode->led_on = prgm_current_ma != 0; + + if (led->pdata->chgr_mitigation_sel == FLASH_SW_CHARGER_MITIGATION) { + qpnp_flash_led_aggregate_max_current(fnode); + led->trigger_chgr = false; + if (led->total_current_ma >= 1000) + led->trigger_chgr = true; + } } static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode) @@ -1158,10 +1189,6 @@ int qpnp_flash_led_prepare(struct led_trigger *trig, int options, *max_current = rc; } - led->trigger_chgr = false; - if (options & PRE_FLASH) - led->trigger_chgr = true; - return 0; } @@ -1335,7 +1362,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, struct flash_node_data *fnode, struct device_node *node) { const char *temp_string; - int rc; + int rc, min_ma; u32 val; bool strobe_sel = 0, edge_trigger = 0, active_high = 0; @@ -1391,10 +1418,11 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, return rc; } + min_ma = fnode->ires_ua / 1000; rc = of_property_read_u32(node, "qcom,max-current", &val); if (!rc) { - if (val < FLASH_LED_MIN_CURRENT_MA) - val = FLASH_LED_MIN_CURRENT_MA; + if (val < min_ma) + val = min_ma; fnode->max_current = val; fnode->cdev.max_brightness = val; } else { @@ -1404,11 +1432,10 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, rc = of_property_read_u32(node, "qcom,current-ma", &val); if (!rc) { - if (val < FLASH_LED_MIN_CURRENT_MA || - val > fnode->max_current) + if (val < min_ma || val > fnode->max_current) pr_warn("Invalid operational current specified, capping it\n"); - if (val < FLASH_LED_MIN_CURRENT_MA) - val = FLASH_LED_MIN_CURRENT_MA; + if (val < min_ma) + val = min_ma; if (val > fnode->max_current) val = fnode->max_current; fnode->current_ma = val; @@ -1956,7 +1983,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return rc; } - led->pdata->lmh_mitigation_sel = FLASH_LED_MITIGATION_SEL_DEFAULT; + led->pdata->lmh_mitigation_sel = FLASH_LED_LMH_MITIGATION_SEL_DEFAULT; rc = of_property_read_u32(node, "qcom,lmh-mitigation-sel", &val); if (!rc) { led->pdata->lmh_mitigation_sel = val; @@ -1970,7 +1997,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return -EINVAL; } - led->pdata->chgr_mitigation_sel = FLASH_LED_MITIGATION_SEL_DEFAULT; + led->pdata->chgr_mitigation_sel = FLASH_SW_CHARGER_MITIGATION; rc = of_property_read_u32(node, "qcom,chgr-mitigation-sel", &val); if (!rc) { led->pdata->chgr_mitigation_sel = val; @@ -1984,9 +2011,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return -EINVAL; } - led->pdata->chgr_mitigation_sel <<= FLASH_LED_CHGR_MITIGATION_SEL_SHIFT; - - led->pdata->iled_thrsh_val = FLASH_LED_MITIGATION_THRSH_DEFAULT; + led->pdata->iled_thrsh_val = FLASH_LED_CHGR_MITIGATION_THRSH_DEFAULT; rc = of_property_read_u32(node, "qcom,iled-thrsh-ma", &val); if (!rc) { led->pdata->iled_thrsh_val = MITIGATION_THRSH_MA_TO_VAL(val); @@ -1995,7 +2020,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return rc; } - if (led->pdata->iled_thrsh_val > FLASH_LED_MITIGATION_THRSH_MAX) { + if (led->pdata->iled_thrsh_val > FLASH_LED_CHGR_MITIGATION_THRSH_MAX) { pr_err("Invalid iled_thrsh_val specified\n"); return -EINVAL; } diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 36ca4e4cbfb7..4c15dee0857b 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -532,16 +532,27 @@ config DM_LOG_WRITES If unsure, say N. -config DM_ANDROID_VERITY - tristate "Android verity target support" +config DM_VERITY_AVB + tristate "Support AVB specific verity error behavior" depends on DM_VERITY + ---help--- + Enables Android Verified Boot platform-specific error + behavior. In particular, it will modify the vbmeta partition + specified on the kernel command-line when non-transient error + occurs (followed by a panic). + + If unsure, say N. + +config DM_ANDROID_VERITY + bool "Android verity target support" + depends on DM_VERITY=y depends on X509_CERTIFICATE_PARSER depends on SYSTEM_TRUSTED_KEYRING depends on PUBLIC_KEY_ALGO_RSA depends on KEYS depends on ASYMMETRIC_KEY_TYPE depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE - depends on MD_LINEAR + depends on MD_LINEAR=y select DM_VERITY_HASH_PREFETCH_MIN_SIZE_128 ---help--- This device-mapper target is virtually a VERITY target. This diff --git a/drivers/md/Makefile b/drivers/md/Makefile index 2b2ba36638cd..eef9097c60b3 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -74,3 +74,7 @@ endif ifeq ($(CONFIG_DM_ANDROID_VERITY),y) dm-verity-objs += dm-android-verity.o endif + +ifeq ($(CONFIG_DM_VERITY_AVB),y) +dm-verity-objs += dm-verity-avb.o +endif diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 13c60bee8af5..7cef735a01a7 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -115,6 +115,12 @@ static inline bool is_userdebug(void) return !strncmp(buildvariant, typeuserdebug, sizeof(typeuserdebug)); } +static inline bool is_unlocked(void) +{ + static const char unlocked[] = "orange"; + + return !strncmp(verifiedbootstate, unlocked, sizeof(unlocked)); +} static int table_extract_mpi_array(struct public_key_signature *pks, const void *data, size_t len) @@ -585,6 +591,8 @@ static int verify_verity_signature(char *key_id, if (IS_ERR(pks)) { DMERR("hashing failed"); + retval = PTR_ERR(pks); + pks = NULL; goto error; } @@ -648,6 +656,28 @@ static int add_as_linear_device(struct dm_target *ti, char *dev) return err; } +static int create_linear_device(struct dm_target *ti, dev_t dev, + char *target_device) +{ + u64 device_size = 0; + int err = find_size(dev, &device_size); + + if (err) { + DMERR("error finding bdev size"); + handle_error(); + return err; + } + + ti->len = device_size; + err = add_as_linear_device(ti, target_device); + if (err) { + handle_error(); + return err; + } + verity_enabled = false; + return 0; +} + /* * Target parameters: * <key id> Key id of the public key in the system keyring. @@ -671,7 +701,6 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) struct fec_ecc_metadata uninitialized_var(ecc); char buf[FEC_ARG_LENGTH], *buf_ptr; unsigned long long tmpll; - u64 uninitialized_var(device_size); if (argc == 1) { /* Use the default keyid */ @@ -699,23 +728,8 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) return -EINVAL; } - if (is_eng()) { - err = find_size(dev, &device_size); - if (err) { - DMERR("error finding bdev size"); - handle_error(); - return err; - } - - ti->len = device_size; - err = add_as_linear_device(ti, target_device); - if (err) { - handle_error(); - return err; - } - verity_enabled = false; - return 0; - } + if (is_eng()) + return create_linear_device(ti, dev, target_device); strreplace(key_id, '#', ' '); @@ -730,6 +744,11 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) err = extract_metadata(dev, &fec, &metadata, &verity_enabled); if (err) { + /* Allow invalid metadata when the device is unlocked */ + if (is_unlocked()) { + DMWARN("Allow invalid metadata when unlocked"); + return create_linear_device(ti, dev, target_device); + } DMERR("Error while extracting metadata"); handle_error(); goto free_metadata; diff --git a/drivers/md/dm-verity-avb.c b/drivers/md/dm-verity-avb.c new file mode 100644 index 000000000000..88487346c4c6 --- /dev/null +++ b/drivers/md/dm-verity-avb.c @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2017 Google. + * + * This file is released under the GPLv2. + * + * Based on drivers/md/dm-verity-chromeos.c + */ + +#include <linux/device-mapper.h> +#include <linux/module.h> +#include <linux/mount.h> + +#define DM_MSG_PREFIX "verity-avb" + +/* Set via module parameter. */ +static char avb_vbmeta_device[64]; + +static void invalidate_vbmeta_endio(struct bio *bio) +{ + complete(bio->bi_private); +} + +static int invalidate_vbmeta_submit(struct bio *bio, + struct block_device *bdev, + int rw, int access_last_sector, + struct page *page) +{ + DECLARE_COMPLETION_ONSTACK(wait); + + bio->bi_private = &wait; + bio->bi_end_io = invalidate_vbmeta_endio; + bio->bi_bdev = bdev; + + bio->bi_iter.bi_sector = 0; + if (access_last_sector) { + sector_t last_sector = (i_size_read(bdev->bd_inode)>>SECTOR_SHIFT) - 1; + bio->bi_iter.bi_sector = last_sector; + } + bio->bi_vcnt = 1; + bio->bi_iter.bi_idx = 0; + bio->bi_iter.bi_size = 512; + bio->bi_iter.bi_bvec_done = 0; + bio->bi_rw = rw; + bio->bi_io_vec[0].bv_page = page; + bio->bi_io_vec[0].bv_len = 512; + bio->bi_io_vec[0].bv_offset = 0; + + submit_bio(rw, bio); + /* Wait up to 2 seconds for completion or fail. */ + if (!wait_for_completion_timeout(&wait, msecs_to_jiffies(2000))) + return -EIO; + return 0; +} + +static int invalidate_vbmeta(dev_t vbmeta_devt) +{ + int ret = 0; + struct block_device *bdev; + struct bio *bio; + struct page *page; + fmode_t dev_mode; + /* Ensure we do synchronous unblocked I/O. We may also need + * sync_bdev() on completion, but it really shouldn't. + */ + int rw = REQ_SYNC | REQ_SOFTBARRIER | REQ_NOIDLE; + int access_last_sector = 0; + + /* First we open the device for reading. */ + dev_mode = FMODE_READ | FMODE_EXCL; + bdev = blkdev_get_by_dev(vbmeta_devt, dev_mode, + invalidate_vbmeta); + if (IS_ERR(bdev)) { + DMERR("invalidate_kernel: could not open device for reading"); + dev_mode = 0; + ret = -ENOENT; + goto failed_to_read; + } + + bio = bio_alloc(GFP_NOIO, 1); + if (!bio) { + ret = -ENOMEM; + goto failed_bio_alloc; + } + + page = alloc_page(GFP_NOIO); + if (!page) { + ret = -ENOMEM; + goto failed_to_alloc_page; + } + + access_last_sector = 0; + ret = invalidate_vbmeta_submit(bio, bdev, rw, access_last_sector, page); + if (ret) { + DMERR("invalidate_vbmeta: error reading"); + goto failed_to_submit_read; + } + + /* We have a page. Let's make sure it looks right. */ + if (memcmp("AVB0", page_address(page), 4) == 0) { + /* Stamp it. */ + memcpy(page_address(page), "AVE0", 4); + DMINFO("invalidate_vbmeta: found vbmeta partition"); + } else { + /* Could be this is on a AVB footer, check. Also, since the + * AVB footer is in the last 64 bytes, adjust for the fact that + * we're dealing with 512-byte sectors. + */ + size_t offset = (1<<SECTOR_SHIFT) - 64; + + access_last_sector = 1; + ret = invalidate_vbmeta_submit(bio, bdev, rw, + access_last_sector, page); + if (ret) { + DMERR("invalidate_vbmeta: error reading"); + goto failed_to_submit_read; + } + if (memcmp("AVBf", page_address(page) + offset, 4) != 0) { + DMERR("invalidate_vbmeta called on non-vbmeta partition"); + ret = -EINVAL; + goto invalid_header; + } + /* Stamp it. */ + memcpy(page_address(page) + offset, "AVE0", 4); + DMINFO("invalidate_vbmeta: found vbmeta footer partition"); + } + + /* Now rewrite the changed page - the block dev was being + * changed on read. Let's reopen here. + */ + blkdev_put(bdev, dev_mode); + dev_mode = FMODE_WRITE | FMODE_EXCL; + bdev = blkdev_get_by_dev(vbmeta_devt, dev_mode, + invalidate_vbmeta); + if (IS_ERR(bdev)) { + DMERR("invalidate_vbmeta: could not open device for writing"); + dev_mode = 0; + ret = -ENOENT; + goto failed_to_write; + } + + /* We re-use the same bio to do the write after the read. Need to reset + * it to initialize bio->bi_remaining. + */ + bio_reset(bio); + + rw |= REQ_WRITE; + ret = invalidate_vbmeta_submit(bio, bdev, rw, access_last_sector, page); + if (ret) { + DMERR("invalidate_vbmeta: error writing"); + goto failed_to_submit_write; + } + + DMERR("invalidate_vbmeta: completed."); + ret = 0; +failed_to_submit_write: +failed_to_write: +invalid_header: + __free_page(page); +failed_to_submit_read: + /* Technically, we'll leak a page with the pending bio, but + * we're about to reboot anyway. + */ +failed_to_alloc_page: + bio_put(bio); +failed_bio_alloc: + if (dev_mode) + blkdev_put(bdev, dev_mode); +failed_to_read: + return ret; +} + +void dm_verity_avb_error_handler(void) +{ + dev_t dev; + + DMINFO("AVB error handler called for %s", avb_vbmeta_device); + + if (avb_vbmeta_device[0] == '\0') { + DMERR("avb_vbmeta_device parameter not set"); + goto fail_no_dev; + } + + dev = name_to_dev_t(avb_vbmeta_device); + if (!dev) { + DMERR("No matching partition for device: %s", + avb_vbmeta_device); + goto fail_no_dev; + } + + invalidate_vbmeta(dev); + +fail_no_dev: + ; +} + +static int __init dm_verity_avb_init(void) +{ + DMINFO("AVB error handler initialized with vbmeta device: %s", + avb_vbmeta_device); + return 0; +} + +static void __exit dm_verity_avb_exit(void) +{ +} + +module_init(dm_verity_avb_init); +module_exit(dm_verity_avb_exit); + +MODULE_AUTHOR("David Zeuthen <zeuthen@google.com>"); +MODULE_DESCRIPTION("AVB-specific error handler for dm-verity"); +MODULE_LICENSE("GPL"); + +/* Declare parameter with no module prefix */ +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "androidboot.vbmeta." +module_param_string(device, avb_vbmeta_device, sizeof(avb_vbmeta_device), 0); diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index c7e97cf6e7fb..e34cf53bd068 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -233,8 +233,12 @@ out: if (v->mode == DM_VERITY_MODE_LOGGING) return 0; - if (v->mode == DM_VERITY_MODE_RESTART) + if (v->mode == DM_VERITY_MODE_RESTART) { +#ifdef CONFIG_DM_VERITY_AVB + dm_verity_avb_error_handler(); +#endif kernel_restart("dm-verity device corrupted"); + } return 1; } diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index 75effca400a3..a90d1d416107 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -136,4 +136,5 @@ extern void verity_io_hints(struct dm_target *ti, struct queue_limits *limits); extern void verity_dtr(struct dm_target *ti); extern int verity_ctr(struct dm_target *ti, unsigned argc, char **argv); extern int verity_map(struct dm_target *ti, struct bio *bio); +extern void dm_verity_avb_error_handler(void); #endif /* DM_VERITY_H */ diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index ebb0dd612ebd..a92979e704e3 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1072,6 +1072,8 @@ static void __make_request(struct mddev *mddev, struct bio *bio) int max_sectors; int sectors; + md_write_start(mddev, bio); + /* * Register the new request and wait if the reconstruction * thread has put up a bar for new requests. @@ -1455,8 +1457,6 @@ static void make_request(struct mddev *mddev, struct bio *bio) return; } - md_write_start(mddev, bio); - do { /* @@ -1477,7 +1477,25 @@ static void make_request(struct mddev *mddev, struct bio *bio) split = bio; } + /* + * If a bio is splitted, the first part of bio will pass + * barrier but the bio is queued in current->bio_list (see + * generic_make_request). If there is a raise_barrier() called + * here, the second part of bio can't pass barrier. But since + * the first part bio isn't dispatched to underlaying disks + * yet, the barrier is never released, hence raise_barrier will + * alays wait. We have a deadlock. + * Note, this only happens in read path. For write path, the + * first part of bio is dispatched in a schedule() call + * (because of blk plug) or offloaded to raid10d. + * Quitting from the function immediately can change the bio + * order queued in bio_list and avoid the deadlock. + */ __make_request(mddev, split); + if (split != bio && bio_data_dir(bio) == READ) { + generic_make_request(bio); + break; + } } while (split != bio); /* In case raid10d snuck in to freeze_array */ diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h index 139a4c9b49ee..b283f6277b87 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h @@ -497,6 +497,7 @@ struct msm_vfe_src_info { enum msm_vfe_dual_hw_type dual_hw_type; struct msm_vfe_dual_hw_ms_info dual_hw_ms_info; bool accept_frame; + uint32_t lpm; }; struct msm_vfe_fetch_engine_info { @@ -599,7 +600,7 @@ struct msm_vfe_tasklet_queue_cmd { struct vfe_device *vfe_dev; }; -#define MSM_VFE_TASKLETQ_SIZE 200 +#define MSM_VFE_TASKLETQ_SIZE 400 enum msm_vfe_overflow_state { NO_OVERFLOW, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index ebd3a32281d7..63e46125c292 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -773,6 +773,40 @@ void msm_isp_check_for_output_error(struct vfe_device *vfe_dev, } } +static int msm_isp_check_sync_time(struct msm_vfe_src_info *src_info, + struct msm_isp_timestamp *ts, + struct master_slave_resource_info *ms_res) +{ + int i; + struct msm_vfe_src_info *master_src_info = NULL; + uint32_t master_time = 0, current_time; + + if (!ms_res->src_sof_mask) + return 0; + + for (i = 0; i < MAX_VFE * VFE_SRC_MAX; i++) { + if (ms_res->src_info[i] == NULL) + continue; + if (src_info == ms_res->src_info[i] || + ms_res->src_info[i]->active == 0) + continue; + if (ms_res->src_sof_mask & + (1 << ms_res->src_info[i]->dual_hw_ms_info.index)) { + master_src_info = ms_res->src_info[i]; + break; + } + } + if (!master_src_info) + return 0; + master_time = master_src_info-> + dual_hw_ms_info.sof_info.mono_timestamp_ms; + current_time = ts->buf_time.tv_sec * 1000 + + ts->buf_time.tv_usec / 1000; + if ((current_time - master_time) > ms_res->sof_delta_threshold) + return 1; + return 0; +} + static void msm_isp_sync_dual_cam_frame_id( struct vfe_device *vfe_dev, struct master_slave_resource_info *ms_res, @@ -787,11 +821,24 @@ static void msm_isp_sync_dual_cam_frame_id( if (src_info->dual_hw_ms_info.sync_state == ms_res->dual_sync_mode) { - (frame_src == VFE_PIX_0) ? src_info->frame_id += + if (msm_isp_check_sync_time(src_info, ts, ms_res) == 0) { + (frame_src == VFE_PIX_0) ? src_info->frame_id += vfe_dev->axi_data.src_info[frame_src]. sof_counter_step : src_info->frame_id++; - return; + return; + } + ms_res->src_sof_mask = 0; + ms_res->active_src_mask = 0; + for (i = 0; i < MAX_VFE * VFE_SRC_MAX; i++) { + if (ms_res->src_info[i] == NULL) + continue; + if (ms_res->src_info[i]->active == 0) + continue; + ms_res->src_info[i]->dual_hw_ms_info. + sync_state = + MSM_ISP_DUAL_CAM_ASYNC; + } } WARN_ON(ms_res->dual_sync_mode == MSM_ISP_DUAL_CAM_ASYNC); @@ -948,8 +995,6 @@ static void msm_isp_update_pd_stats_idx(struct vfe_device *vfe_dev, uint32_t pingpong_status = 0, pingpong_bit = 0; struct msm_isp_buffer *done_buf = NULL; int vfe_idx = -1; - /* initialize pd_buf_idx with an invalid index 0xF */ - vfe_dev->pd_buf_idx = 0xF; if (frame_src < VFE_RAW_0 || frame_src > VFE_RAW_2) return; @@ -2421,6 +2466,7 @@ int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg) int i, rc = 0; uint64_t total_bandwidth = 0; int vfe_idx; + uint32_t intf; unsigned long flags; struct msm_vfe_axi_stream *stream_info; struct msm_vfe_dual_lpm_mode *ab_ib_vote = NULL; @@ -2436,7 +2482,13 @@ int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg) stream_info = msm_isp_get_stream_common_data(vfe_dev, ab_ib_vote->stream_src[i]); + if (stream_info == NULL) + continue; + /* loop all stream on current session */ spin_lock_irqsave(&stream_info->lock, flags); + intf = SRC_TO_INTF(stream_info->stream_src); + vfe_dev->axi_data.src_info[intf].lpm = + ab_ib_vote->lpm_mode; if (stream_info->state == ACTIVE) { vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, @@ -2457,7 +2509,12 @@ int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg) stream_info = msm_isp_get_stream_common_data(vfe_dev, ab_ib_vote->stream_src[i]); + if (stream_info == NULL) + continue; spin_lock_irqsave(&stream_info->lock, flags); + intf = SRC_TO_INTF(stream_info->stream_src); + vfe_dev->axi_data.src_info[intf].lpm = + ab_ib_vote->lpm_mode; if (stream_info->state == PAUSED) { vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, @@ -2883,7 +2940,9 @@ static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev, * those state transitions instead of directly forcing stream to * be INACTIVE */ - if (stream_info->state != PAUSED) { + intf = SRC_TO_INTF(stream_info->stream_src); + if ((!vfe_dev->axi_data.src_info[intf].lpm) || + stream_info->state != PAUSED) { while (stream_info->state != ACTIVE) __msm_isp_axi_stream_update(stream_info, ×tamp); @@ -2900,10 +2959,12 @@ static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev, vfe_dev->hw_info->vfe_ops.axi_ops. clear_wm_irq_mask(vfe_dev, stream_info); } - if (stream_info->state == ACTIVE) { + if (stream_info->state == ACTIVE && + !vfe_dev->axi_data.src_info[intf].lpm) { init_completion(&stream_info->inactive_comp); stream_info->state = STOP_PENDING; - } else if (stream_info->state == PAUSED) { + } else if (vfe_dev->axi_data.src_info[intf].lpm || + stream_info->state == PAUSED) { /* don't wait for reg update */ stream_info->state = STOP_PENDING; msm_isp_axi_stream_enable_cfg(stream_info); @@ -3018,6 +3079,7 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, int k; struct vfe_device *vfe_dev; struct msm_vfe_axi_shared_data *axi_data = &vfe_dev_ioctl->axi_data; + uint32_t intf; if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM) return -EINVAL; @@ -3081,7 +3143,7 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, cfg_wm_irq_mask(vfe_dev, stream_info); } } - + intf = SRC_TO_INTF(stream_info->stream_src); init_completion(&stream_info->active_comp); stream_info->state = START_PENDING; msm_isp_update_intf_stream_cnt(stream_info, 1); @@ -3091,6 +3153,11 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, vfe_dev_ioctl->pdev->id); if (src_state) { src_mask |= (1 << SRC_TO_INTF(stream_info->stream_src)); + if (vfe_dev_ioctl->axi_data.src_info[intf].lpm) { + while (stream_info->state != ACTIVE) + __msm_isp_axi_stream_update( + stream_info, ×tamp); + } } else { for (k = 0; k < stream_info->num_isp; k++) { vfe_dev = stream_info->vfe_dev[k]; @@ -3365,22 +3432,21 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, /* * If frame_id = 1 then no eof check is needed */ - if (vfe_dev->axi_data.src_info[VFE_PIX_0].active && - vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame == false) { + if (vfe_dev->axi_data.src_info[frame_src].active && + frame_src == VFE_PIX_0 && + vfe_dev->axi_data.src_info[frame_src].accept_frame == false) { pr_debug("%s:%d invalid time to request frame %d\n", __func__, __LINE__, frame_id); goto error; } - if ((vfe_dev->axi_data.src_info[VFE_PIX_0].active && (frame_id != - vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id + vfe_dev-> - axi_data.src_info[VFE_PIX_0].sof_counter_step)) || - ((!vfe_dev->axi_data.src_info[VFE_PIX_0].active) && (frame_id != + if ((vfe_dev->axi_data.src_info[frame_src].active && (frame_id != vfe_dev->axi_data.src_info[frame_src].frame_id + vfe_dev-> - axi_data.src_info[frame_src].sof_counter_step))) { + axi_data.src_info[VFE_PIX_0].sof_counter_step)) || + ((!vfe_dev->axi_data.src_info[frame_src].active))) { pr_debug("%s:%d invalid frame id %d cur frame id %d pix %d\n", __func__, __LINE__, frame_id, - vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id, - vfe_dev->axi_data.src_info[VFE_PIX_0].active); + vfe_dev->axi_data.src_info[frame_src].frame_id, + vfe_dev->axi_data.src_info[frame_src].active); goto error; } if (stream_info->undelivered_request_cnt >= MAX_BUFFERS_IN_HW) { @@ -3887,6 +3953,12 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) &update_cmd->req_frm_ver2; stream_info = msm_isp_get_stream_common_data(vfe_dev, HANDLE_TO_IDX(req_frm->stream_handle)); + if (stream_info == NULL) { + pr_err_ratelimited("%s: stream_info is NULL\n", + __func__); + rc = -EINVAL; + break; + } rc = msm_isp_request_frame(vfe_dev, stream_info, req_frm->user_stream_id, req_frm->frame_id, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h index 65009cb22286..a8d4cfb43927 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h @@ -141,6 +141,11 @@ static inline struct msm_vfe_axi_stream *msm_isp_get_stream_common_data( struct msm_vfe_common_dev_data *common_data = vfe_dev->common_data; struct msm_vfe_axi_stream *stream_info; + if (stream_idx >= VFE_AXI_SRC_MAX) { + pr_err("invalid stream_idx %d\n", stream_idx); + return NULL; + } + if (vfe_dev->is_split && stream_idx < RDI_INTF_0) stream_info = &common_data->streams[stream_idx]; else diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c index f2cf4d477b3f..73068a615380 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c @@ -228,9 +228,10 @@ static int32_t msm_isp_stats_buf_divert(struct vfe_device *vfe_dev, done_buf->buf_idx; stats_event->pd_stats_idx = 0xF; - if (stream_info->stats_type == MSM_ISP_STATS_BF) + if (stream_info->stats_type == MSM_ISP_STATS_BF) { stats_event->pd_stats_idx = vfe_dev->pd_buf_idx; - + vfe_dev->pd_buf_idx = 0xF; + } if (comp_stats_type_mask == NULL) { stats_event->stats_mask = 1 << stream_info->stats_type; @@ -765,7 +766,7 @@ void msm_isp_process_stats_reg_upd_epoch_irq(struct vfe_device *vfe_dev, spin_unlock_irqrestore(&stream_info->lock, flags); if (-EFAULT == rc) { msm_isp_halt_send_error(vfe_dev, - ISP_EVENT_BUF_FATAL_ERROR); + ISP_EVENT_PING_PONG_MISMATCH); return; } continue; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index 765bf6521759..2a9bb6e8e505 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -2070,6 +2070,7 @@ static void msm_isp_enqueue_tasklet_cmd(struct vfe_device *vfe_dev, if (queue_cmd->cmd_used) { pr_err("%s: Tasklet queue overflow: %d\n", __func__, vfe_dev->pdev->id); + spin_unlock_irqrestore(&tasklet->tasklet_lock, flags); return; } else { atomic_add(1, &vfe_dev->irq_cnt); @@ -2314,6 +2315,9 @@ int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) vfe_dev->reg_update_requested = 0; /* Register page fault handler */ vfe_dev->buf_mgr->pagefault_debug_disable = 0; + /* initialize pd_buf_idx with an invalid index 0xF */ + vfe_dev->pd_buf_idx = 0xF; + cam_smmu_reg_client_page_fault_handler( vfe_dev->buf_mgr->iommu_hdl, msm_vfe_iommu_fault_handler, vfe_dev); diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c index 41d8ef577a27..caf6639f5151 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c @@ -49,17 +49,31 @@ #define ISPIF_TIMEOUT_ALL_US 1000000 #define ISPIF_SOF_DEBUG_COUNT 5 +/* 3D Threshold value according guidelines for line width 1280 */ +#define STEREO_DEFAULT_3D_THRESHOLD 0x36 + +/* + * Overflows before restarting interface during stereo usecase + * to give some tolerance for cases when the two sensors sync fails + * this value is chosen by experiment + */ +#define MAX_PIX_OVERFLOW_ERROR_COUNT 10 +static int pix_overflow_error_count[VFE_MAX] = { 0 }; + #undef CDBG #ifdef CONFIG_MSMB_CAMERA_DEBUG #define CDBG(fmt, args...) pr_debug(fmt, ##args) #else -#define CDBG(fmt, args...) do { } while (0) +#define CDBG(fmt, args...) #endif static int msm_ispif_clk_ahb_enable(struct ispif_device *ispif, int enable); static int ispif_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh); static long msm_ispif_subdev_ioctl_unlocked(struct v4l2_subdev *sd, unsigned int cmd, void *arg); +static long msm_ispif_dispatch_cmd(enum ispif_cfg_type_t cmd, + struct ispif_device *ispif, + struct msm_ispif_param_data_ext *params); int msm_ispif_get_clk_info(struct ispif_device *ispif_dev, struct platform_device *pdev); @@ -249,16 +263,7 @@ static long msm_ispif_cmd_ext(struct v4l2_subdev *sd, } mutex_lock(&ispif->mutex); - switch (pcdata.cfg_type) { - case ISPIF_CFG2: - rc = msm_ispif_config2(ispif, params); - msm_ispif_io_dump_reg(ispif); - break; - default: - pr_err("%s: invalid cfg_type\n", __func__); - rc = -EINVAL; - break; - } + rc = msm_ispif_dispatch_cmd(pcdata.cfg_type, ispif, params); mutex_unlock(&ispif->mutex); kfree(params); return rc; @@ -855,15 +860,34 @@ static uint16_t msm_ispif_get_cids_mask_from_cfg( return cids_mask; } + +static uint16_t msm_ispif_get_right_cids_mask_from_cfg( + struct msm_ispif_right_param_entry *entry, int num_cids) +{ + int i; + uint16_t cids_mask = 0; + + if (WARN_ON(!entry)) + return cids_mask; + + for (i = 0; i < num_cids && i < MAX_CID_CH_PARAM_ENTRY; i++) { + if (entry->cids[i] < CID_MAX) + cids_mask |= (1 << entry->cids[i]); + } + + return cids_mask; +} + static int msm_ispif_config(struct ispif_device *ispif, void *data) { int rc = 0, i = 0; - uint16_t cid_mask; + uint16_t cid_mask = 0; + uint16_t cid_right_mask = 0; enum msm_ispif_intftype intftype; enum msm_ispif_vfe_intf vfe_intf; - struct msm_ispif_param_data *params = - (struct msm_ispif_param_data *)data; + struct msm_ispif_param_data_ext *params = + (struct msm_ispif_param_data_ext *)data; BUG_ON(!ispif); BUG_ON(!params); @@ -913,9 +937,15 @@ static int msm_ispif_config(struct ispif_device *ispif, return -EINVAL; } - if (ispif->csid_version >= CSID_VERSION_V30) + if (ispif->csid_version >= CSID_VERSION_V30) { msm_ispif_select_clk_mux(ispif, intftype, params->entries[i].csid, vfe_intf); + if (intftype == PIX0 && params->stereo_enable && + params->right_entries[i].csid < CSID_MAX) + msm_ispif_select_clk_mux(ispif, PIX1, + params->right_entries[i].csid, + vfe_intf); + } rc = msm_ispif_validate_intf_status(ispif, intftype, vfe_intf); if (rc) { @@ -926,10 +956,26 @@ static int msm_ispif_config(struct ispif_device *ispif, msm_ispif_sel_csid_core(ispif, intftype, params->entries[i].csid, vfe_intf); + if (intftype == PIX0 && params->stereo_enable && + params->right_entries[i].csid < CSID_MAX) + /* configure right stereo csid */ + msm_ispif_sel_csid_core(ispif, PIX1, + params->right_entries[i].csid, vfe_intf); + cid_mask = msm_ispif_get_cids_mask_from_cfg( ¶ms->entries[i]); msm_ispif_enable_intf_cids(ispif, intftype, cid_mask, vfe_intf, 1); + if (params->stereo_enable) + cid_right_mask = msm_ispif_get_right_cids_mask_from_cfg( + ¶ms->right_entries[i], + params->entries[i].num_cids); + else + cid_right_mask = 0; + if (cid_right_mask && params->stereo_enable) + /* configure right stereo cids */ + msm_ispif_enable_intf_cids(ispif, PIX1, + cid_right_mask, vfe_intf, 1); if (params->entries[i].crop_enable) msm_ispif_enable_crop(ispif, intftype, vfe_intf, params->entries[i].crop_start_pixel, @@ -962,8 +1008,28 @@ static int msm_ispif_config(struct ispif_device *ispif, return rc; } +static void msm_ispif_config_stereo(struct ispif_device *ispif, + struct msm_ispif_param_data_ext *params) { + + int i; + enum msm_ispif_vfe_intf vfe_intf; + + for (i = 0; i < params->num; i++) { + if (params->entries[i].intftype == PIX0 && + params->stereo_enable && + params->right_entries[i].csid < CSID_MAX) { + vfe_intf = params->entries[i].vfe_intf; + msm_camera_io_w_mb(0x3, + ispif->base + ISPIF_VFE_m_OUTPUT_SEL(vfe_intf)); + msm_camera_io_w_mb(STEREO_DEFAULT_3D_THRESHOLD, + ispif->base + + ISPIF_VFE_m_3D_THRESHOLD(vfe_intf)); + } + } +} + static void msm_ispif_intf_cmd(struct ispif_device *ispif, uint32_t cmd_bits, - struct msm_ispif_param_data *params) + struct msm_ispif_param_data_ext *params) { uint8_t vc; int i, k; @@ -1008,6 +1074,19 @@ static void msm_ispif_intf_cmd(struct ispif_device *ispif, uint32_t cmd_bits, ispif->applied_intf_cmd[vfe_intf].intf_cmd |= (cmd_bits << (vc * 2 + intf_type * 8)); } + if (intf_type == PIX0 && params->stereo_enable && + params->right_entries[i].cids[k] < CID_MAX) { + cid = params->right_entries[i].cids[k]; + vc = cid / 4; + + /* fill right stereo command */ + /* zero 2 bits */ + ispif->applied_intf_cmd[vfe_intf].intf_cmd &= + ~(0x3 << (vc * 2 + PIX1 * 8)); + /* set cmd bits */ + ispif->applied_intf_cmd[vfe_intf].intf_cmd |= + (cmd_bits << (vc * 2 + PIX1 * 8)); + } } /* cmd for PIX0, PIX1, RDI0, RDI1 */ if (ispif->applied_intf_cmd[vfe_intf].intf_cmd != 0xFFFFFFFF) @@ -1024,7 +1103,7 @@ static void msm_ispif_intf_cmd(struct ispif_device *ispif, uint32_t cmd_bits, } static int msm_ispif_stop_immediately(struct ispif_device *ispif, - struct msm_ispif_param_data *params) + struct msm_ispif_param_data_ext *params) { int i, rc = 0; uint16_t cid_mask = 0; @@ -1052,13 +1131,22 @@ static int msm_ispif_stop_immediately(struct ispif_device *ispif, ¶ms->entries[i]); msm_ispif_enable_intf_cids(ispif, params->entries[i].intftype, cid_mask, params->entries[i].vfe_intf, 0); + if (params->stereo_enable) { + cid_mask = msm_ispif_get_right_cids_mask_from_cfg( + ¶ms->right_entries[i], + params->entries[i].num_cids); + if (cid_mask) + msm_ispif_enable_intf_cids(ispif, + params->entries[i].intftype, cid_mask, + params->entries[i].vfe_intf, 0); + } } return rc; } static int msm_ispif_start_frame_boundary(struct ispif_device *ispif, - struct msm_ispif_param_data *params) + struct msm_ispif_param_data_ext *params) { int rc = 0; @@ -1074,13 +1162,14 @@ static int msm_ispif_start_frame_boundary(struct ispif_device *ispif, rc = -EINVAL; return rc; } + msm_ispif_config_stereo(ispif, params); msm_ispif_intf_cmd(ispif, ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY, params); return rc; } static int msm_ispif_restart_frame_boundary(struct ispif_device *ispif, - struct msm_ispif_param_data *params) + struct msm_ispif_param_data_ext *params) { int rc = 0, i; long timeout = 0; @@ -1222,10 +1311,11 @@ end: } static int msm_ispif_stop_frame_boundary(struct ispif_device *ispif, - struct msm_ispif_param_data *params) + struct msm_ispif_param_data_ext *params) { int i, rc = 0; uint16_t cid_mask = 0; + uint16_t cid_right_mask = 0; uint32_t intf_addr; enum msm_ispif_vfe_intf vfe_intf; uint32_t stop_flag = 0; @@ -1263,6 +1353,13 @@ static int msm_ispif_stop_frame_boundary(struct ispif_device *ispif, for (i = 0; i < params->num; i++) { cid_mask = msm_ispif_get_cids_mask_from_cfg(¶ms->entries[i]); + if (params->stereo_enable) + cid_right_mask = + msm_ispif_get_right_cids_mask_from_cfg( + ¶ms->right_entries[i], + params->entries[i].num_cids); + else + cid_right_mask = 0; vfe_intf = params->entries[i].vfe_intf; switch (params->entries[i].intftype) { @@ -1294,10 +1391,24 @@ static int msm_ispif_stop_frame_boundary(struct ispif_device *ispif, ISPIF_TIMEOUT_ALL_US); if (rc < 0) goto end; + if (cid_right_mask) { + intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 1); + rc = readl_poll_timeout(ispif->base + intf_addr, + stop_flag, + (stop_flag & 0xF) == 0xF, + ISPIF_TIMEOUT_SLEEP_US, + ISPIF_TIMEOUT_ALL_US); + if (rc < 0) + goto end; + } /* disable CIDs in CID_MASK register */ msm_ispif_enable_intf_cids(ispif, params->entries[i].intftype, cid_mask, vfe_intf, 0); + if (cid_right_mask) + msm_ispif_enable_intf_cids(ispif, + params->entries[i].intftype, cid_right_mask, + params->entries[i].vfe_intf, 0); } end: @@ -1318,6 +1429,14 @@ static void ispif_process_irq(struct ispif_device *ispif, ispif->sof_count[vfe_id].sof_cnt[PIX0]++; ispif->ispif_sof_debug++; } + if (out[vfe_id].ispifIrqStatus1 & + ISPIF_IRQ_STATUS_PIX_SOF_MASK) { + if (ispif->ispif_sof_debug < ISPIF_SOF_DEBUG_COUNT*2) + pr_err("%s: PIX1 frame id: %u\n", __func__, + ispif->sof_count[vfe_id].sof_cnt[PIX1]); + ispif->sof_count[vfe_id].sof_cnt[PIX1]++; + ispif->ispif_sof_debug++; + } if (out[vfe_id].ispifIrqStatus0 & ISPIF_IRQ_STATUS_RDI0_SOF_MASK) { if (ispif->ispif_rdi0_debug < ISPIF_SOF_DEBUG_COUNT) @@ -1344,12 +1463,55 @@ static void ispif_process_irq(struct ispif_device *ispif, } } +static int msm_ispif_reconfig_3d_output(struct ispif_device *ispif, + enum msm_ispif_vfe_intf vfe_id) +{ + uint32_t reg_data; + + if (WARN_ON(!ispif)) + return -EINVAL; + + if (!((vfe_id == VFE0) || (vfe_id == VFE1))) { + pr_err("%s;%d Cannot reconfigure 3D mode for VFE%d", __func__, + __LINE__, vfe_id); + return -EINVAL; + } + pr_info("%s;%d Reconfiguring 3D mode for VFE%d", __func__, __LINE__, + vfe_id); + reg_data = 0xFFFCFFFC; + msm_camera_io_w_mb(reg_data, ispif->base + + ISPIF_VFE_m_INTF_CMD_0(vfe_id)); + msm_camera_io_w_mb(reg_data, ispif->base + + ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR); + + if (vfe_id == VFE0) { + reg_data = 0; + reg_data |= (PIX_0_VFE_RST_STB | PIX_1_VFE_RST_STB | + STROBED_RST_EN | PIX_0_CSID_RST_STB | + PIX_1_CSID_RST_STB | PIX_OUTPUT_0_MISR_RST_STB); + msm_camera_io_w_mb(reg_data, ispif->base + ISPIF_RST_CMD_ADDR); + } else { + reg_data = 0; + reg_data |= (PIX_0_VFE_RST_STB | PIX_1_VFE_RST_STB | + STROBED_RST_EN | PIX_0_CSID_RST_STB | + PIX_1_CSID_RST_STB | PIX_OUTPUT_0_MISR_RST_STB); + msm_camera_io_w_mb(reg_data, ispif->base + + ISPIF_RST_CMD_1_ADDR); + } + + reg_data = 0xFFFDFFFD; + msm_camera_io_w_mb(reg_data, ispif->base + + ISPIF_VFE_m_INTF_CMD_0(vfe_id)); + return 0; +} + static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out, void *data) { struct ispif_device *ispif = (struct ispif_device *)data; bool fatal_err = false; int i = 0; + uint32_t reg_data; BUG_ON(!ispif); BUG_ON(!out); @@ -1400,6 +1562,12 @@ static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out, fatal_err = true; } + if (out[VFE0].ispifIrqStatus1 & PIX_INTF_1_OVERFLOW_IRQ) { + pr_err_ratelimited("%s: VFE0 pix1 overflow.\n", + __func__); + fatal_err = true; + } + if (out[VFE0].ispifIrqStatus0 & RAW_INTF_0_OVERFLOW_IRQ) { pr_err_ratelimited("%s: VFE0 rdi0 overflow.\n", __func__); @@ -1432,6 +1600,12 @@ static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out, fatal_err = true; } + if (out[VFE1].ispifIrqStatus1 & PIX_INTF_1_OVERFLOW_IRQ) { + pr_err_ratelimited("%s: VFE1 pix1 overflow.\n", + __func__); + fatal_err = true; + } + if (out[VFE1].ispifIrqStatus0 & RAW_INTF_0_OVERFLOW_IRQ) { pr_err_ratelimited("%s: VFE1 rdi0 overflow.\n", __func__); @@ -1453,6 +1627,43 @@ static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out, ispif_process_irq(ispif, out, VFE1); } + if ((out[VFE0].ispifIrqStatus0 & PIX_INTF_0_OVERFLOW_IRQ) || + (out[VFE0].ispifIrqStatus1 & PIX_INTF_0_OVERFLOW_IRQ) || + (out[VFE0].ispifIrqStatus2 & (L_R_SOF_MISMATCH_ERR_IRQ | + L_R_EOF_MISMATCH_ERR_IRQ | L_R_SOL_MISMATCH_ERR_IRQ))) { + reg_data = msm_camera_io_r(ispif->base + + ISPIF_VFE_m_OUTPUT_SEL(VFE0)); + if ((reg_data & 0x03) == VFE_PIX_INTF_SEL_3D) { + pix_overflow_error_count[VFE0]++; + if (pix_overflow_error_count[VFE0] >= + MAX_PIX_OVERFLOW_ERROR_COUNT) { + msm_ispif_reconfig_3d_output(ispif, VFE0); + pix_overflow_error_count[VFE0] = 0; + } + fatal_err = false; + } + } + + if (ispif->vfe_info.num_vfe > 1) { + if ((out[VFE1].ispifIrqStatus0 & PIX_INTF_0_OVERFLOW_IRQ) || + (out[VFE1].ispifIrqStatus1 & PIX_INTF_0_OVERFLOW_IRQ) || + (out[VFE1].ispifIrqStatus2 & (L_R_SOF_MISMATCH_ERR_IRQ | + L_R_EOF_MISMATCH_ERR_IRQ | L_R_SOL_MISMATCH_ERR_IRQ))) { + reg_data = msm_camera_io_r(ispif->base + + ISPIF_VFE_m_OUTPUT_SEL(VFE1)); + if ((reg_data & 0x03) == VFE_PIX_INTF_SEL_3D) { + pix_overflow_error_count[VFE1]++; + if (pix_overflow_error_count[VFE1] >= + MAX_PIX_OVERFLOW_ERROR_COUNT) { + msm_ispif_reconfig_3d_output(ispif, + VFE1); + pix_overflow_error_count[VFE1] = 0; + } + } + fatal_err = false; + } + } + if (fatal_err == true) { pr_err_ratelimited("%s: fatal error, stop ispif immediately\n", __func__); @@ -1561,61 +1772,97 @@ static void msm_ispif_release(struct ispif_device *ispif) pr_err("%s: failed to remove vote for AHB\n", __func__); } -static long msm_ispif_cmd(struct v4l2_subdev *sd, void *arg) +static long msm_ispif_dispatch_cmd(enum ispif_cfg_type_t cmd, + struct ispif_device *ispif, + struct msm_ispif_param_data_ext *params) { long rc = 0; - struct ispif_cfg_data *pcdata = (struct ispif_cfg_data *)arg; - struct ispif_device *ispif = - (struct ispif_device *)v4l2_get_subdevdata(sd); - - BUG_ON(!sd); - BUG_ON(!pcdata); - mutex_lock(&ispif->mutex); - switch (pcdata->cfg_type) { - case ISPIF_ENABLE_REG_DUMP: - ispif->enb_dump_reg = pcdata->reg_dump; /* save dump config */ - break; - case ISPIF_INIT: - rc = msm_ispif_init(ispif, pcdata->csid_version); - msm_ispif_io_dump_reg(ispif); - break; + switch (cmd) { case ISPIF_CFG: - rc = msm_ispif_config(ispif, &pcdata->params); + rc = msm_ispif_config(ispif, params); msm_ispif_io_dump_reg(ispif); break; case ISPIF_START_FRAME_BOUNDARY: - rc = msm_ispif_start_frame_boundary(ispif, &pcdata->params); + rc = msm_ispif_start_frame_boundary(ispif, params); msm_ispif_io_dump_reg(ispif); break; case ISPIF_RESTART_FRAME_BOUNDARY: - rc = msm_ispif_restart_frame_boundary(ispif, &pcdata->params); + rc = msm_ispif_restart_frame_boundary(ispif, params); msm_ispif_io_dump_reg(ispif); break; - case ISPIF_STOP_FRAME_BOUNDARY: - rc = msm_ispif_stop_frame_boundary(ispif, &pcdata->params); + rc = msm_ispif_stop_frame_boundary(ispif, params); msm_ispif_io_dump_reg(ispif); break; case ISPIF_STOP_IMMEDIATELY: - rc = msm_ispif_stop_immediately(ispif, &pcdata->params); + rc = msm_ispif_stop_immediately(ispif, params); msm_ispif_io_dump_reg(ispif); break; case ISPIF_RELEASE: msm_ispif_reset(ispif); msm_ispif_reset_hw(ispif); break; - case ISPIF_SET_VFE_INFO: - rc = msm_ispif_set_vfe_info(ispif, &pcdata->vfe_info); + case ISPIF_CFG2: + rc = msm_ispif_config2(ispif, params); + msm_ispif_io_dump_reg(ispif); break; default: pr_err("%s: invalid cfg_type\n", __func__); rc = -EINVAL; break; } + return rc; +} + +static long msm_ispif_cmd(struct v4l2_subdev *sd, void *arg) +{ + long rc = 0; + struct ispif_cfg_data *pcdata = (struct ispif_cfg_data *)arg; + struct ispif_device *ispif = + (struct ispif_device *)v4l2_get_subdevdata(sd); + int i; + struct msm_ispif_param_data_ext params; + + if (WARN_ON(!sd) || WARN_ON(!pcdata)) + return -EINVAL; + + mutex_lock(&ispif->mutex); + switch (pcdata->cfg_type) { + case ISPIF_ENABLE_REG_DUMP: + /* save dump config */ + ispif->enb_dump_reg = pcdata->reg_dump; + break; + case ISPIF_INIT: + rc = msm_ispif_init(ispif, pcdata->csid_version); + msm_ispif_io_dump_reg(ispif); + break; + case ISPIF_SET_VFE_INFO: + rc = msm_ispif_set_vfe_info(ispif, &pcdata->vfe_info); + break; + default: + memset(¶ms, 0, sizeof(params)); + if (pcdata->params.num > MAX_PARAM_ENTRIES) { + pr_err("%s: invalid num entries %u\n", __func__, + pcdata->params.num); + rc = -EINVAL; + } else { + params.num = pcdata->params.num; + for (i = 0; i < pcdata->params.num; i++) + memcpy(¶ms.entries[i], + &pcdata->params.entries[i], + sizeof(struct msm_ispif_params_entry)); + params.stereo_enable = 0; + rc = msm_ispif_dispatch_cmd(pcdata->cfg_type, ispif, + ¶ms); + } + break; + } mutex_unlock(&ispif->mutex); + return rc; } + static struct v4l2_file_operations msm_ispif_v4l2_subdev_fops; static long msm_ispif_subdev_ioctl_unlocked(struct v4l2_subdev *sd, diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v1.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v1.h index d488ca618537..49d7d0f7624e 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v1.h +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v1.h @@ -87,6 +87,12 @@ #define MISC_LOGIC_RST_STB BIT(1) #define STROBED_RST_EN BIT(0) +#define VFE_PIX_INTF_SEL_3D 0x3 +#define PIX_OUTPUT_0_MISR_RST_STB BIT(16) +#define L_R_SOF_MISMATCH_ERR_IRQ BIT(16) +#define L_R_EOF_MISMATCH_ERR_IRQ BIT(17) +#define L_R_SOL_MISMATCH_ERR_IRQ BIT(18) + #define ISPIF_RST_CMD_MASK 0xFE1C77FF #define ISPIF_RST_CMD_1_MASK 0xFFFFFFFF /* undefined */ diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v2.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v2.h index 8ae61dc2d4f6..9abf55efc46c 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v2.h +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v2.h @@ -22,6 +22,7 @@ #define ISPIF_VFE(m) ((m) * 0x200) #define ISPIF_VFE_m_CTRL_0(m) (0x200 + ISPIF_VFE(m)) +#define ISPIF_VFE_m_CTRL_1(m) (0x204 + ISPIF_VFE(m)) #define ISPIF_VFE_m_IRQ_MASK_0(m) (0x208 + ISPIF_VFE(m)) #define ISPIF_VFE_m_IRQ_MASK_1(m) (0x20C + ISPIF_VFE(m)) #define ISPIF_VFE_m_IRQ_MASK_2(m) (0x210 + ISPIF_VFE(m)) @@ -71,6 +72,12 @@ #define MISC_LOGIC_RST_STB BIT(1) #define STROBED_RST_EN BIT(0) +#define VFE_PIX_INTF_SEL_3D 0x3 +#define PIX_OUTPUT_0_MISR_RST_STB BIT(16) +#define L_R_SOF_MISMATCH_ERR_IRQ BIT(16) +#define L_R_EOF_MISMATCH_ERR_IRQ BIT(17) +#define L_R_SOL_MISMATCH_ERR_IRQ BIT(18) + #define ISPIF_RST_CMD_MASK 0xFE0F1FFF #define ISPIF_RST_CMD_1_MASK 0xFC0F1FF9 @@ -78,6 +85,7 @@ #define ISPIF_RST_CMD_1_MASK_RESTART 0x00001FF9 #define PIX_INTF_0_OVERFLOW_IRQ BIT(12) +#define PIX_INTF_1_OVERFLOW_IRQ BIT(12) #define RAW_INTF_0_OVERFLOW_IRQ BIT(25) #define RAW_INTF_1_OVERFLOW_IRQ BIT(25) #define RAW_INTF_2_OVERFLOW_IRQ BIT(12) diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v3.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v3.h index 94cc974441ee..5f2aa06f3e13 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v3.h +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_hwreg_v3.h @@ -74,6 +74,12 @@ #define MISC_LOGIC_RST_STB BIT(1) #define STROBED_RST_EN BIT(0) +#define VFE_PIX_INTF_SEL_3D 0x3 +#define PIX_OUTPUT_0_MISR_RST_STB BIT(16) +#define L_R_SOF_MISMATCH_ERR_IRQ BIT(16) +#define L_R_EOF_MISMATCH_ERR_IRQ BIT(17) +#define L_R_SOL_MISMATCH_ERR_IRQ BIT(18) + #define ISPIF_RST_CMD_MASK 0xFE7F1FFF #define ISPIF_RST_CMD_1_MASK 0xFC7F1FF9 @@ -81,6 +87,7 @@ #define ISPIF_RST_CMD_1_MASK_RESTART 0x7F1FF9 #define PIX_INTF_0_OVERFLOW_IRQ BIT(12) +#define PIX_INTF_1_OVERFLOW_IRQ BIT(12) #define RAW_INTF_0_OVERFLOW_IRQ BIT(25) #define RAW_INTF_1_OVERFLOW_IRQ BIT(25) #define RAW_INTF_2_OVERFLOW_IRQ BIT(12) diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c index 76a7c6942c68..af6206f83048 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c @@ -553,8 +553,7 @@ static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd, sizeof(struct msm_buf_mngr_info))) { return -EFAULT; } - MSM_CAM_GET_IOCTL_ARG_PTR(&k_ioctl.ioctl_ptr, - &buf_info, sizeof(void *)); + k_ioctl.ioctl_ptr = (uintptr_t)&buf_info; argp = &k_ioctl; rc = msm_cam_buf_mgr_ops(cmd, argp); } diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c index b067c4916341..75043e1b0427 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c +++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c @@ -47,6 +47,8 @@ #define CCI_DBG(fmt, args...) pr_debug(fmt, ##args) #endif +#define CCI_DUMP_REG 0 + /* Max bytes that can be read per CCI read transaction */ #define CCI_READ_MAX 12 #define CCI_I2C_READ_MAX_RETRIES 3 @@ -201,6 +203,7 @@ static int32_t msm_cci_validate_queue(struct cci_device *cci_dev, enum cci_i2c_queue_t queue) { int32_t rc = 0; + unsigned long flags; uint32_t read_val = 0; uint32_t reg_offset = master * 0x200 + queue * 0x100; read_val = msm_camera_io_r_mb(cci_dev->base + @@ -223,6 +226,8 @@ static int32_t msm_cci_validate_queue(struct cci_device *cci_dev, CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset); reg_val = 1 << ((master * 2) + queue); CDBG("%s:%d CCI_QUEUE_START_ADDR\n", __func__, __LINE__); + spin_lock_irqsave(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); atomic_set(&cci_dev->cci_master_info[master]. done_pending[queue], 1); msm_camera_io_w_mb(reg_val, cci_dev->base + @@ -230,6 +235,8 @@ static int32_t msm_cci_validate_queue(struct cci_device *cci_dev, CDBG("%s line %d wait_for_completion_timeout\n", __func__, __LINE__); atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1); + spin_unlock_irqrestore(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); rc = wait_for_completion_timeout(&cci_dev-> cci_master_info[master].report_q[queue], CCI_TIMEOUT); if (rc <= 0) { @@ -291,6 +298,9 @@ static uint32_t msm_cci_wait(struct cci_device *cci_dev, __func__, __LINE__); if (rc <= 0) { + if (CCI_DUMP_REG) + msm_cci_dump_registers(cci_dev, master, queue); + pr_err("%s: %d wait for queue: %d\n", __func__, __LINE__, queue); if (rc == 0) @@ -438,10 +448,17 @@ static int32_t msm_cci_wait_report_cmd(struct cci_device *cci_dev, enum cci_i2c_master_t master, enum cci_i2c_queue_t queue) { + unsigned long flags; uint32_t reg_val = 1 << ((master * 2) + queue); msm_cci_load_report_cmd(cci_dev, master, queue); + + spin_lock_irqsave(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1); atomic_set(&cci_dev->cci_master_info[master].done_pending[queue], 1); + spin_unlock_irqrestore(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); + msm_camera_io_w_mb(reg_val, cci_dev->base + CCI_QUEUE_START_ADDR); return msm_cci_wait(cci_dev, master, queue); @@ -451,13 +468,19 @@ static void msm_cci_process_half_q(struct cci_device *cci_dev, enum cci_i2c_master_t master, enum cci_i2c_queue_t queue) { + unsigned long flags; uint32_t reg_val = 1 << ((master * 2) + queue); + + spin_lock_irqsave(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); if (0 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) { msm_cci_load_report_cmd(cci_dev, master, queue); atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1); msm_camera_io_w_mb(reg_val, cci_dev->base + CCI_QUEUE_START_ADDR); } + spin_unlock_irqrestore(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); } static int32_t msm_cci_process_full_q(struct cci_device *cci_dev, @@ -465,15 +488,23 @@ static int32_t msm_cci_process_full_q(struct cci_device *cci_dev, enum cci_i2c_queue_t queue) { int32_t rc = 0; + unsigned long flags; + + spin_lock_irqsave(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); if (1 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) { atomic_set(&cci_dev->cci_master_info[master]. done_pending[queue], 1); + spin_unlock_irqrestore(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); rc = msm_cci_wait(cci_dev, master, queue); if (rc < 0) { pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc); return rc; } } else { + spin_unlock_irqrestore(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); rc = msm_cci_wait_report_cmd(cci_dev, master, queue); if (rc < 0) { pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc); @@ -501,8 +532,13 @@ static int32_t msm_cci_transfer_end(struct cci_device *cci_dev, enum cci_i2c_queue_t queue) { int32_t rc = 0; + unsigned long flags; + spin_lock_irqsave(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); if (0 == atomic_read(&cci_dev->cci_master_info[master].q_free[queue])) { + spin_unlock_irqrestore(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); rc = msm_cci_lock_queue(cci_dev, master, queue, 0); if (rc < 0) { pr_err("%s failed line %d\n", __func__, __LINE__); @@ -516,6 +552,8 @@ static int32_t msm_cci_transfer_end(struct cci_device *cci_dev, } else { atomic_set(&cci_dev->cci_master_info[master]. done_pending[queue], 1); + spin_unlock_irqrestore(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); rc = msm_cci_wait(cci_dev, master, queue); if (rc < 0) { pr_err("%s: %d failed rc %d\n", __func__, __LINE__, rc); @@ -570,6 +608,7 @@ static int32_t msm_cci_data_queue(struct cci_device *cci_dev, uint32_t reg_offset; uint32_t val = 0; uint32_t max_queue_size; + unsigned long flags; if (i2c_cmd == NULL) { pr_err("%s:%d Failed line\n", __func__, @@ -613,7 +652,11 @@ static int32_t msm_cci_data_queue(struct cci_device *cci_dev, msm_camera_io_w_mb(val, cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR + reg_offset); + spin_lock_irqsave(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 0); + spin_unlock_irqrestore(&cci_dev->cci_master_info[master]. + lock_q[queue], flags); max_queue_size = cci_dev->cci_i2c_queue_info[master][queue]. max_queue_size; @@ -889,7 +932,9 @@ static int32_t msm_cci_i2c_read(struct v4l2_subdev *sd, rc = wait_for_completion_timeout(&cci_dev-> cci_master_info[master].reset_complete, CCI_TIMEOUT); if (rc <= 0) { - msm_cci_dump_registers(cci_dev, master, queue); + if (CCI_DUMP_REG) + msm_cci_dump_registers(cci_dev, master, queue); + if (rc == 0) rc = -ETIMEDOUT; pr_err("%s: %d wait_for_completion_timeout rc = %d\n", @@ -1641,6 +1686,7 @@ static int32_t msm_cci_config(struct v4l2_subdev *sd, static irqreturn_t msm_cci_irq(int irq_num, void *data) { uint32_t irq; + unsigned long flags; struct cci_device *cci_dev = data; irq = msm_camera_io_r_mb(cci_dev->base + CCI_IRQ_STATUS_0_ADDR); msm_camera_io_w_mb(irq, cci_dev->base + CCI_IRQ_CLEAR_0_ADDR); @@ -1667,22 +1713,30 @@ static irqreturn_t msm_cci_irq(int irq_num, void *data) if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK) { struct msm_camera_cci_master_info *cci_master_info; cci_master_info = &cci_dev->cci_master_info[MASTER_0]; + spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_0]. + lock_q[QUEUE_0], flags); atomic_set(&cci_master_info->q_free[QUEUE_0], 0); cci_master_info->status = 0; if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) { complete(&cci_master_info->report_q[QUEUE_0]); atomic_set(&cci_master_info->done_pending[QUEUE_0], 0); } + spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_0]. + lock_q[QUEUE_0], flags); } if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK) { struct msm_camera_cci_master_info *cci_master_info; cci_master_info = &cci_dev->cci_master_info[MASTER_0]; + spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_0]. + lock_q[QUEUE_1], flags); atomic_set(&cci_master_info->q_free[QUEUE_1], 0); cci_master_info->status = 0; if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) { complete(&cci_master_info->report_q[QUEUE_1]); atomic_set(&cci_master_info->done_pending[QUEUE_1], 0); } + spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_0]. + lock_q[QUEUE_1], flags); } if (irq & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) { cci_dev->cci_master_info[MASTER_1].status = 0; @@ -1691,22 +1745,30 @@ static irqreturn_t msm_cci_irq(int irq_num, void *data) if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK) { struct msm_camera_cci_master_info *cci_master_info; cci_master_info = &cci_dev->cci_master_info[MASTER_1]; + spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_1]. + lock_q[QUEUE_0], flags); atomic_set(&cci_master_info->q_free[QUEUE_0], 0); cci_master_info->status = 0; if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) { complete(&cci_master_info->report_q[QUEUE_0]); atomic_set(&cci_master_info->done_pending[QUEUE_0], 0); } + spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_1]. + lock_q[QUEUE_0], flags); } if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK) { struct msm_camera_cci_master_info *cci_master_info; cci_master_info = &cci_dev->cci_master_info[MASTER_1]; + spin_lock_irqsave(&cci_dev->cci_master_info[MASTER_1]. + lock_q[QUEUE_1], flags); atomic_set(&cci_master_info->q_free[QUEUE_1], 0); cci_master_info->status = 0; if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) { complete(&cci_master_info->report_q[QUEUE_1]); atomic_set(&cci_master_info->done_pending[QUEUE_1], 0); } + spin_unlock_irqrestore(&cci_dev->cci_master_info[MASTER_1]. + lock_q[QUEUE_1], flags); } if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK) { cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE; @@ -1795,7 +1857,9 @@ static void msm_cci_init_cci_params(struct cci_device *new_cci_dev) mutex_init(&new_cci_dev->cci_master_info[i].mutex_q[j]); init_completion(&new_cci_dev-> cci_master_info[i].report_q[j]); - } + spin_lock_init(&new_cci_dev-> + cci_master_info[i].lock_q[j]); + } } return; } diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h index 6e39d814bd73..eb615cc7a62c 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h +++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -125,6 +125,7 @@ struct msm_camera_cci_master_info { struct mutex mutex_q[NUM_QUEUES]; struct completion report_q[NUM_QUEUES]; atomic_t done_pending[NUM_QUEUES]; + spinlock_t lock_q[NUM_QUEUES]; }; struct msm_cci_clk_params_t { diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c index 55a743737c59..7ae071176ef4 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c +++ b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c @@ -227,16 +227,34 @@ static void msm_csid_set_sof_freeze_debug_reg( static int msm_csid_reset(struct csid_device *csid_dev) { int32_t rc = 0; + uint32_t irq = 0, irq_bitshift; + + irq_bitshift = csid_dev->ctrl_reg->csid_reg.csid_rst_done_irq_bitshift; msm_camera_io_w(csid_dev->ctrl_reg->csid_reg.csid_rst_stb_all, csid_dev->base + csid_dev->ctrl_reg->csid_reg.csid_rst_cmd_addr); rc = wait_for_completion_timeout(&csid_dev->reset_complete, CSID_TIMEOUT); - if (rc <= 0) { + if (rc < 0) { pr_err("wait_for_completion in msm_csid_reset fail rc = %d\n", rc); + } else if (rc == 0) { + irq = msm_camera_io_r(csid_dev->base + + csid_dev->ctrl_reg->csid_reg.csid_irq_status_addr); + pr_err_ratelimited("%s CSID%d_IRQ_STATUS_ADDR = 0x%x\n", + __func__, csid_dev->pdev->id, irq); + if (irq & (0x1 << irq_bitshift)) { + rc = 1; + CDBG("%s succeeded", __func__); + } else { + rc = 0; + pr_err("%s reset csid_irq_status failed = 0x%x\n", + __func__, irq); + } if (rc == 0) rc = -ETIMEDOUT; + } else { + CDBG("%s succeeded", __func__); } return rc; } diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h index 5d57ec8c28ff..8f55f453bf03 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.h @@ -89,6 +89,7 @@ struct msm_sensor_ctrl_t { uint32_t set_mclk_23880000; uint8_t is_csid_tg_mode; uint32_t is_secure; + uint8_t bypass_video_node_creation; }; int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp); diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c index 1dd2b0d26007..344f1a6f8d92 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c @@ -86,11 +86,14 @@ static int32_t msm_sensor_driver_create_i2c_v4l_subdev struct i2c_client *client = s_ctrl->sensor_i2c_client->client; CDBG("%s %s I2c probe succeeded\n", __func__, client->name); - rc = camera_init_v4l2(&client->dev, &session_id); - if (rc < 0) { - pr_err("failed: camera_init_i2c_v4l2 rc %d", rc); - return rc; + if (s_ctrl->bypass_video_node_creation == 0) { + rc = camera_init_v4l2(&client->dev, &session_id); + if (rc < 0) { + pr_err("failed: camera_init_i2c_v4l2 rc %d", rc); + return rc; + } } + CDBG("%s rc %d session_id %d\n", __func__, rc, session_id); snprintf(s_ctrl->msm_sd.sd.name, sizeof(s_ctrl->msm_sd.sd.name), "%s", @@ -123,11 +126,14 @@ static int32_t msm_sensor_driver_create_v4l_subdev int32_t rc = 0; uint32_t session_id = 0; - rc = camera_init_v4l2(&s_ctrl->pdev->dev, &session_id); - if (rc < 0) { - pr_err("failed: camera_init_v4l2 rc %d", rc); - return rc; + if (s_ctrl->bypass_video_node_creation == 0) { + rc = camera_init_v4l2(&s_ctrl->pdev->dev, &session_id); + if (rc < 0) { + pr_err("failed: camera_init_v4l2 rc %d", rc); + return rc; + } } + CDBG("rc %d session_id %d", rc, session_id); s_ctrl->sensordata->sensor_info->session_id = session_id; @@ -773,6 +779,8 @@ int32_t msm_sensor_driver_probe(void *setting, slave_info32->sensor_init_params; slave_info->output_format = slave_info32->output_format; + slave_info->bypass_video_node_creation = + !!slave_info32->bypass_video_node_creation; kfree(slave_info32); } else #endif @@ -800,7 +808,8 @@ int32_t msm_sensor_driver_probe(void *setting, slave_info->sensor_init_params.position); CDBG("mount %d", slave_info->sensor_init_params.sensor_mount_angle); - + CDBG("bypass video node creation %d", + slave_info->bypass_video_node_creation); /* Validate camera id */ if (slave_info->camera_id >= MAX_CAMERAS) { pr_err("failed: invalid camera id %d max %d", @@ -980,6 +989,9 @@ CSID_TG: */ s_ctrl->is_probe_succeed = 1; + s_ctrl->bypass_video_node_creation = + slave_info->bypass_video_node_creation; + /* * Create /dev/videoX node, comment for now until dummy /dev/videoX * node is created and used by HAL 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 0cd8e613c224..cfee4efb6f16 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c @@ -465,6 +465,10 @@ static void sde_rotator_stop_streaming(struct vb2_queue *q) sde_rot_mgr_lock(rot_dev->mgr); sde_rotator_cancel_all_requests(rot_dev->mgr, ctx->private); sde_rot_mgr_unlock(rot_dev->mgr); + mutex_unlock(q->lock); + cancel_work_sync(&ctx->submit_work); + cancel_work_sync(&ctx->retire_work); + mutex_lock(q->lock); } sde_rotator_return_all_buffers(q, VB2_BUF_STATE_ERROR); diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c index 3b36b6bc76de..5eaa2910228e 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012, 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -507,10 +507,15 @@ struct sde_mdp_format_params *sde_get_format_params(u32 format) if (!fmt_found) { for (i = 0; i < ARRAY_SIZE(sde_mdp_format_ubwc_map); i++) { fmt = &sde_mdp_format_ubwc_map[i].mdp_format; - if (format == fmt->format) + if (format == fmt->format) { + fmt_found = true; break; + } } } + /* If format not supported than return NULL */ + if (!fmt_found) + fmt = NULL; return fmt; } diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c index c3a0cfb390c4..34ec6529d8ae 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c @@ -1245,6 +1245,33 @@ static int sde_hw_rotator_swts_create(struct sde_hw_rotator *rot) goto err_detach; } + ion_free(rot->iclient, handle); + + sde_smmu_ctrl(0); + + return rc; +err_detach: + dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment); +err_put: + dma_buf_put(data->srcp_dma_buf); + data->srcp_dma_buf = NULL; +imap_err: + ion_free(rot->iclient, handle); + + return rc; +} + +/* + * sde_hw_rotator_swts_map - map software timestamp buffer + * @rot: Pointer to rotator hw + * + */ +static int sde_hw_rotator_swts_map(struct sde_hw_rotator *rot) +{ + int rc = 0; + struct sde_mdp_img_data *data = &rot->swts_buf; + + sde_smmu_ctrl(1); rc = sde_smmu_map_dma_buf(data->srcp_dma_buf, data->srcp_table, SDE_IOMMU_DOMAIN_ROT_UNSECURE, &data->addr, &data->len, DMA_BIDIRECTIONAL); @@ -1264,35 +1291,25 @@ static int sde_hw_rotator_swts_create(struct sde_hw_rotator *rot) data->mapped = true; SDEROT_DBG("swts buffer mapped: %pad/%lx va:%p\n", &data->addr, - data->len, rot->swts_buffer); - - ion_free(rot->iclient, handle); - + data->len, rot->swts_buffer); sde_smmu_ctrl(0); - return rc; + kmap_err: sde_smmu_unmap_dma_buf(data->srcp_table, SDE_IOMMU_DOMAIN_ROT_UNSECURE, DMA_FROM_DEVICE, data->srcp_dma_buf); err_unmap: dma_buf_unmap_attachment(data->srcp_attachment, data->srcp_table, DMA_FROM_DEVICE); -err_detach: - dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment); -err_put: - dma_buf_put(data->srcp_dma_buf); - data->srcp_dma_buf = NULL; -imap_err: - ion_free(rot->iclient, handle); return rc; } /* - * sde_hw_rotator_swtc_destroy - destroy software timestamp buffer + * sde_hw_rotator_swtc_unmap - unmap software timestamp buffer * @rot: Pointer to rotator hw */ -static void sde_hw_rotator_swtc_destroy(struct sde_hw_rotator *rot) +static void sde_hw_rotator_swtc_unmap(struct sde_hw_rotator *rot) { struct sde_mdp_img_data *data; @@ -1301,9 +1318,29 @@ static void sde_hw_rotator_swtc_destroy(struct sde_hw_rotator *rot) dma_buf_end_cpu_access(data->srcp_dma_buf, 0, data->len, DMA_FROM_DEVICE); dma_buf_kunmap(data->srcp_dma_buf, 0, rot->swts_buffer); + rot->swts_buffer = NULL; sde_smmu_unmap_dma_buf(data->srcp_table, SDE_IOMMU_DOMAIN_ROT_UNSECURE, DMA_FROM_DEVICE, data->srcp_dma_buf); + data->addr = 0x0; + data->len = 0; + + data->mapped = false; +} + +/* + * sde_hw_rotator_swtc_destroy - destroy software timestamp buffer + * @rot: Pointer to rotator hw + */ +static void sde_hw_rotator_swtc_destroy(struct sde_hw_rotator *rot) +{ + struct sde_mdp_img_data *data; + + data = &rot->swts_buf; + + if (data->mapped) + sde_hw_rotator_swtc_unmap(rot); + dma_buf_unmap_attachment(data->srcp_attachment, data->srcp_table, DMA_FROM_DEVICE); dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment); @@ -1436,6 +1473,7 @@ static struct sde_rot_hw_resource *sde_hw_rotator_alloc_ext( struct sde_rot_mgr *mgr, u32 pipe_id, u32 wb_id) { struct sde_hw_rotator_resource_info *resinfo; + int ret = 0; if (!mgr || !mgr->hw_data) { SDEROT_ERR("null parameters\n"); @@ -1463,8 +1501,21 @@ static struct sde_rot_hw_resource *sde_hw_rotator_alloc_ext( else { resinfo->hw.max_active = SDE_HW_ROT_REGDMA_TOTAL_CTX - 1; - if (resinfo->rot->iclient == NULL) - sde_hw_rotator_swts_create(resinfo->rot); + if (resinfo->rot->iclient == NULL) { + ret = sde_hw_rotator_swts_create(resinfo->rot); + if (ret) { + SDEROT_ERR("swts buffer create failed\n"); + goto swts_fail; + } + } + + if (resinfo->rot->swts_buf.mapped == false) { + ret = sde_hw_rotator_swts_map(resinfo->rot); + if (ret) { + SDEROT_ERR("swts buffer map failed\n"); + goto swts_fail; + } + } } if (resinfo->rot->irq_num >= 0) @@ -1474,6 +1525,10 @@ static struct sde_rot_hw_resource *sde_hw_rotator_alloc_ext( resinfo, wb_id); return &resinfo->hw; + +swts_fail: + devm_kfree(&mgr->pdev->dev, resinfo); + return NULL; } /* @@ -1485,6 +1540,7 @@ static void sde_hw_rotator_free_ext(struct sde_rot_mgr *mgr, struct sde_rot_hw_resource *hw) { struct sde_hw_rotator_resource_info *resinfo; + struct sde_rot_data_type *mdata = sde_rot_get_mdata(); if (!mgr || !mgr->hw_data) return; @@ -1499,6 +1555,18 @@ static void sde_hw_rotator_free_ext(struct sde_rot_mgr *mgr, if (resinfo->rot->irq_num >= 0) sde_hw_rotator_disable_irq(resinfo->rot); + /* + * For SDM660 and SDM630, the IOMMU is shared between MDP and rotator. + * If IOMMU is detached from MDP driver, the timestamp buffer will be + * invalidated. It is safer to unmap the timestamp buffer when the + * rotator session ends, so that it will be mapped again when a fresh + * session starts. + */ + if (((mdata->mdss_version == MDSS_MDP_HW_REV_320) || + (mdata->mdss_version == MDSS_MDP_HW_REV_330)) && + resinfo->rot->swts_buf.mapped) + sde_hw_rotator_swtc_unmap(resinfo->rot); + devm_kfree(&mgr->pdev->dev, resinfo); } diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c index a63279715de6..a8dc1d010d62 100644 --- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c +++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c @@ -766,6 +766,8 @@ static int __init msm_vidc_init(void) if (rc) { dprintk(VIDC_ERR, "Failed to register platform driver\n"); + msm_vidc_debugfs_deinit_drv(); + debugfs_remove_recursive(vidc_driver->debugfs_root); kfree(vidc_driver); vidc_driver = NULL; } @@ -776,6 +778,7 @@ static int __init msm_vidc_init(void) static void __exit msm_vidc_exit(void) { platform_driver_unregister(&msm_vidc_driver); + msm_vidc_debugfs_deinit_drv(); debugfs_remove_recursive(vidc_driver->debugfs_root); mutex_destroy(&vidc_driver->lock); kfree(vidc_driver); diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c index ca9d7fba4ee3..30726354164b 100644 --- a/drivers/media/platform/msm/vidc/msm_venc.c +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -3511,6 +3511,48 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) frameqp = ctrl->val; pdata = &frameqp; break; + case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP: + { + rc = msm_venc_validate_qp_value(inst, ctrl); + if (rc) { + dprintk(VIDC_ERR, "Invalid Initial I QP\n"); + break; + } + /* + * Defer sending property from here, set_ext_ctrl + * will send it based on the rc value. + */ + property_id = 0; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP: + { + rc = msm_venc_validate_qp_value(inst, ctrl); + if (rc) { + dprintk(VIDC_ERR, "Invalid Initial B QP\n"); + break; + } + /* + * Defer sending property from here, set_ext_ctrl + * will send it based on the rc value. + */ + property_id = 0; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP: + { + rc = msm_venc_validate_qp_value(inst, ctrl); + if (rc) { + dprintk(VIDC_ERR, "Invalid Initial P QP\n"); + break; + } + /* + * Defer sending property from here, set_ext_ctrl + * will send it based on the rc value. + */ + property_id = 0; + break; + } case V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI: property_id = HAL_PARAM_VENC_VQZIP_SEI; enable.enable = ctrl->val; @@ -3745,7 +3787,7 @@ static int try_set_ext_ctrl(struct msm_vidc_inst *inst, struct hal_aspect_ratio sar; struct hal_bitrate bitrate; struct hal_frame_size blur_res; - struct v4l2_ctrl *temp_ctrl; + struct v4l2_control temp_ctrl; if (!inst || !inst->core || !inst->core->device || !ctrl) { dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); @@ -3812,12 +3854,15 @@ static int try_set_ext_ctrl(struct msm_vidc_inst *inst, /* Sanity check for the QP boundaries as we are using * same control to set Initial QP for all the codecs */ - temp_ctrl->id = + temp_ctrl.id = V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP; - temp_ctrl->val = control[i].value; - rc = msm_venc_validate_qp_value(inst, temp_ctrl); + temp_ctrl.value = control[i].value; + + rc = msm_comm_s_ctrl(inst, &temp_ctrl); if (rc) { - dprintk(VIDC_ERR, "Invalid Initial I QP\n"); + dprintk(VIDC_ERR, + "%s Failed setting Initial I Frame QP : %d\n", + __func__, rc); break; } quant.qpi = control[i].value; @@ -3825,12 +3870,14 @@ static int try_set_ext_ctrl(struct msm_vidc_inst *inst, pdata = &quant; break; case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP: - temp_ctrl->id = + temp_ctrl.id = V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP; - temp_ctrl->val = control[i].value; - rc = msm_venc_validate_qp_value(inst, temp_ctrl); + temp_ctrl.value = control[i].value; + rc = msm_comm_s_ctrl(inst, &temp_ctrl); if (rc) { - dprintk(VIDC_ERR, "Invalid Initial P QP\n"); + dprintk(VIDC_ERR, + "%s Failed setting Initial P Frame QP : %d\n", + __func__, rc); break; } quant.qpp = control[i].value; @@ -3838,12 +3885,14 @@ static int try_set_ext_ctrl(struct msm_vidc_inst *inst, pdata = &quant; break; case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP: - temp_ctrl->id = + temp_ctrl.id = V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP; - temp_ctrl->val = control[i].value; - rc = msm_venc_validate_qp_value(inst, temp_ctrl); + temp_ctrl.value = control[i].value; + rc = msm_comm_s_ctrl(inst, &temp_ctrl); if (rc) { - dprintk(VIDC_ERR, "Invalid Initial B QP\n"); + dprintk(VIDC_ERR, + "%s Failed setting Initial B Frame QP : %d\n", + __func__, rc); break; } quant.qpb = control[i].value; diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c index 1248a1c08103..a9b367d6fe93 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c @@ -38,6 +38,7 @@ bool msm_vidc_debug_timeout = false; #define MAX_DBG_BUF_SIZE 4096 struct debug_buffer { + struct mutex lock; char ptr[MAX_DBG_BUF_SIZE]; char *curr; u32 filled_size; @@ -64,8 +65,12 @@ static u32 write_str(struct debug_buffer *buffer, const char *fmt, ...) { va_list args; u32 size; + + char *curr = buffer->curr; + char *end = buffer->ptr + MAX_DBG_BUF_SIZE; + va_start(args, fmt); - size = vscnprintf(buffer->curr, MAX_DBG_BUF_SIZE - 1, fmt, args); + size = vscnprintf(curr, end - curr, fmt, args); va_end(args); buffer->curr += size; buffer->filled_size += size; @@ -79,12 +84,15 @@ static ssize_t core_info_read(struct file *file, char __user *buf, struct hfi_device *hdev; struct hal_fw_info fw_info = { {0} }; int i = 0, rc = 0; + ssize_t len = 0; if (!core || !core->device) { dprintk(VIDC_ERR, "Invalid params, core: %pK\n", core); return 0; } hdev = core->device; + + mutex_lock(&dbg_buf.lock); INIT_DBG_BUF(dbg_buf); write_str(&dbg_buf, "===============================\n"); write_str(&dbg_buf, "CORE %d: %pK\n", core->id, core); @@ -108,8 +116,11 @@ err_fw_info: completion_done(&core->completions[SYS_MSG_INDEX(i)]) ? "pending" : "done"); } - return simple_read_from_buffer(buf, count, ppos, + len = simple_read_from_buffer(buf, count, ppos, dbg_buf.ptr, dbg_buf.filled_size); + + mutex_unlock(&dbg_buf.lock); + return len; } static const struct file_operations core_info_fops = { @@ -147,7 +158,10 @@ static const struct file_operations ssr_fops = { struct dentry *msm_vidc_debugfs_init_drv(void) { bool ok = false; - struct dentry *dir = debugfs_create_dir("msm_vidc", NULL); + struct dentry *dir = NULL; + + mutex_init(&dbg_buf.lock); + dir = debugfs_create_dir("msm_vidc", NULL); if (IS_ERR_OR_NULL(dir)) { dir = NULL; goto failed_create_dir; @@ -216,6 +230,7 @@ struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core, dprintk(VIDC_ERR, "Failed to create debugfs for msm_vidc\n"); goto failed_create_dir; } + if (!debugfs_create_file("info", S_IRUGO, dir, core, &core_info_fops)) { dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); goto failed_create_dir; @@ -269,11 +284,14 @@ static ssize_t inst_info_read(struct file *file, char __user *buf, { struct msm_vidc_inst *inst = file->private_data; int i, j; + ssize_t len = 0; if (!inst) { dprintk(VIDC_ERR, "Invalid params, inst %pK\n", inst); return 0; } + + mutex_lock(&dbg_buf.lock); INIT_DBG_BUF(dbg_buf); write_str(&dbg_buf, "===============================\n"); write_str(&dbg_buf, "INSTANCE: %pK (%s)\n", inst, @@ -331,8 +349,10 @@ static ssize_t inst_info_read(struct file *file, char __user *buf, publish_unreleased_reference(inst); - return simple_read_from_buffer(buf, count, ppos, + len = simple_read_from_buffer(buf, count, ppos, dbg_buf.ptr, dbg_buf.filled_size); + mutex_unlock(&dbg_buf.lock); + return len; } static const struct file_operations inst_info_fops = { @@ -413,3 +433,8 @@ void msm_vidc_debugfs_update(struct msm_vidc_inst *inst, } } +void msm_vidc_debugfs_deinit_drv(void) +{ + mutex_destroy(&dbg_buf.lock); +} + diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.h b/drivers/media/platform/msm/vidc/msm_vidc_debug.h index 39ac6273f34e..853ce4b89f2b 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_debug.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.h @@ -126,6 +126,7 @@ struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst, struct dentry *parent); void msm_vidc_debugfs_update(struct msm_vidc_inst *inst, enum msm_vidc_debugfs_event e); +void msm_vidc_debugfs_deinit_drv(void); static inline void tic(struct msm_vidc_inst *i, enum profiling_points p, char *b) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 5cefca95734e..885f689ac870 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1595,6 +1595,114 @@ static const char *uvc_print_chain(struct uvc_video_chain *chain) return buffer; } +static struct uvc_video_chain *uvc_alloc_chain(struct uvc_device *dev) +{ + struct uvc_video_chain *chain; + + chain = kzalloc(sizeof(*chain), GFP_KERNEL); + if (chain == NULL) + return NULL; + + INIT_LIST_HEAD(&chain->entities); + mutex_init(&chain->ctrl_mutex); + chain->dev = dev; + v4l2_prio_init(&chain->prio); + + return chain; +} + +/* + * Fallback heuristic for devices that don't connect units and terminals in a + * valid chain. + * + * Some devices have invalid baSourceID references, causing uvc_scan_chain() + * to fail, but if we just take the entities we can find and put them together + * in the most sensible chain we can think of, turns out they do work anyway. + * Note: This heuristic assumes there is a single chain. + * + * At the time of writing, devices known to have such a broken chain are + * - Acer Integrated Camera (5986:055a) + * - Realtek rtl157a7 (0bda:57a7) + */ +static int uvc_scan_fallback(struct uvc_device *dev) +{ + struct uvc_video_chain *chain; + struct uvc_entity *iterm = NULL; + struct uvc_entity *oterm = NULL; + struct uvc_entity *entity; + struct uvc_entity *prev; + + /* + * Start by locating the input and output terminals. We only support + * devices with exactly one of each for now. + */ + list_for_each_entry(entity, &dev->entities, list) { + if (UVC_ENTITY_IS_ITERM(entity)) { + if (iterm) + return -EINVAL; + iterm = entity; + } + + if (UVC_ENTITY_IS_OTERM(entity)) { + if (oterm) + return -EINVAL; + oterm = entity; + } + } + + if (iterm == NULL || oterm == NULL) + return -EINVAL; + + /* Allocate the chain and fill it. */ + chain = uvc_alloc_chain(dev); + if (chain == NULL) + return -ENOMEM; + + if (uvc_scan_chain_entity(chain, oterm) < 0) + goto error; + + prev = oterm; + + /* + * Add all Processing and Extension Units with two pads. The order + * doesn't matter much, use reverse list traversal to connect units in + * UVC descriptor order as we build the chain from output to input. This + * leads to units appearing in the order meant by the manufacturer for + * the cameras known to require this heuristic. + */ + list_for_each_entry_reverse(entity, &dev->entities, list) { + if (entity->type != UVC_VC_PROCESSING_UNIT && + entity->type != UVC_VC_EXTENSION_UNIT) + continue; + + if (entity->num_pads != 2) + continue; + + if (uvc_scan_chain_entity(chain, entity) < 0) + goto error; + + prev->baSourceID[0] = entity->id; + prev = entity; + } + + if (uvc_scan_chain_entity(chain, iterm) < 0) + goto error; + + prev->baSourceID[0] = iterm->id; + + list_add_tail(&chain->list, &dev->chains); + + uvc_trace(UVC_TRACE_PROBE, + "Found a video chain by fallback heuristic (%s).\n", + uvc_print_chain(chain)); + + return 0; + +error: + kfree(chain); + return -EINVAL; +} + /* * Scan the device for video chains and register video devices. * @@ -1617,15 +1725,10 @@ static int uvc_scan_device(struct uvc_device *dev) if (term->chain.next || term->chain.prev) continue; - chain = kzalloc(sizeof(*chain), GFP_KERNEL); + chain = uvc_alloc_chain(dev); if (chain == NULL) return -ENOMEM; - INIT_LIST_HEAD(&chain->entities); - mutex_init(&chain->ctrl_mutex); - chain->dev = dev; - v4l2_prio_init(&chain->prio); - term->flags |= UVC_ENTITY_FLAG_DEFAULT; if (uvc_scan_chain(chain, term) < 0) { @@ -1639,6 +1742,9 @@ static int uvc_scan_device(struct uvc_device *dev) list_add_tail(&chain->list, &dev->chains); } + if (list_empty(&dev->chains)) + uvc_scan_fallback(dev); + if (list_empty(&dev->chains)) { uvc_printk(KERN_INFO, "No valid video chain found.\n"); return -1; diff --git a/drivers/mfd/wcd934x-regmap.c b/drivers/mfd/wcd934x-regmap.c index e8ba1495de2b..27249eeec013 100644 --- a/drivers/mfd/wcd934x-regmap.c +++ b/drivers/mfd/wcd934x-regmap.c @@ -1937,7 +1937,6 @@ static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg) case WCD934X_BIAS_VBG_FINE_ADJ: case WCD934X_CODEC_CPR_SVS_CX_VDD: case WCD934X_CODEC_CPR_SVS2_CX_VDD: - case WCD934X_CDC_TOP_TOP_CFG1: case WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL: return true; } diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 12ffa4192605..2a4abf736d89 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -549,11 +549,13 @@ config VEXPRESS_SYSCFG bus. System Configuration interface is one of the possible means of generating transactions on this bus. -config UID_CPUTIME - bool "Per-UID cpu time statistics" +config UID_SYS_STATS + bool "Per-UID statistics" depends on PROFILING help Per UID based cpu time statistics exported to /proc/uid_cputime + Per UID based io statistics exported to /proc/uid_io + Per UID based procstat control in /proc/uid_procstat config QPNP_MISC tristate "QPNP Misc Peripheral" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index c2941afc961e..b0718228d2d9 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -62,7 +62,7 @@ endif obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o obj-$(CONFIG_CXL_BASE) += cxl/ -obj-$(CONFIG_UID_CPUTIME) += uid_cputime.o +obj-$(CONFIG_UID_SYS_STATS) += uid_sys_stats.o obj-y += qcom/ obj-$(CONFIG_QPNP_MISC) += qpnp-misc.o obj-$(CONFIG_MEMORY_STATE_TIME) += memory_state_time.o diff --git a/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c index 9889d9c4723b..84761747f129 100644 --- a/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c +++ b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c @@ -148,6 +148,8 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd, case AUDIO_START: { pr_debug("%s: AUDIO_START\n", __func__); + mutex_lock(&effects->lock); + rc = q6asm_open_read_write_v2(effects->ac, FORMAT_LINEAR_PCM, FORMAT_MULTI_CHANNEL_LINEAR_PCM, @@ -159,6 +161,7 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd, pr_err("%s: Open failed for hw accelerated effects:rc=%d\n", __func__, rc); rc = -EINVAL; + mutex_unlock(&effects->lock); goto ioctl_fail; } effects->opened = 1; @@ -175,6 +178,7 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd, pr_err("%s: Write buffer Allocation failed rc = %d\n", __func__, rc); rc = -ENOMEM; + mutex_unlock(&effects->lock); goto ioctl_fail; } atomic_set(&effects->in_count, effects->config.input.num_buf); @@ -185,6 +189,7 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd, pr_err("%s: Read buffer Allocation failed rc = %d\n", __func__, rc); rc = -ENOMEM; + mutex_unlock(&effects->lock); goto readbuf_fail; } atomic_set(&effects->out_count, effects->config.output.num_buf); @@ -199,6 +204,7 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd, if (rc < 0) { pr_err("%s: pcm read block config failed\n", __func__); rc = -EINVAL; + mutex_unlock(&effects->lock); goto cfg_fail; } pr_debug("%s: dec: sample_rate: %d, num_channels: %d, bit_width: %d\n", @@ -213,6 +219,7 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd, pr_err("%s: pcm write format block config failed\n", __func__); rc = -EINVAL; + mutex_unlock(&effects->lock); goto cfg_fail; } @@ -225,6 +232,7 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd, effects->started = 0; pr_err("%s: ASM run state failed\n", __func__); } + mutex_unlock(&effects->lock); break; } case AUDIO_EFFECTS_WRITE: { @@ -286,8 +294,11 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd, uint32_t idx = 0; uint32_t size = 0; + mutex_lock(&effects->lock); + if (!effects->started) { rc = -EFAULT; + mutex_unlock(&effects->lock); goto ioctl_fail; } @@ -304,11 +315,13 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd, if (!rc) { pr_err("%s: read wait_event_timeout\n", __func__); rc = -EFAULT; + mutex_unlock(&effects->lock); goto ioctl_fail; } if (!atomic_read(&effects->in_count)) { pr_err("%s: pcm stopped in_count 0\n", __func__); rc = -EFAULT; + mutex_unlock(&effects->lock); goto ioctl_fail; } @@ -316,15 +329,18 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned cmd, if (bufptr) { if (!((void *)arg)) { rc = -EFAULT; + mutex_unlock(&effects->lock); goto ioctl_fail; } if ((effects->config.buf_cfg.input_len > size) || copy_to_user((void *)arg, bufptr, effects->config.buf_cfg.input_len)) { rc = -EFAULT; + mutex_unlock(&effects->lock); goto ioctl_fail; } } + mutex_unlock(&effects->lock); break; } default: @@ -456,6 +472,7 @@ static long audio_effects_ioctl(struct file *file, unsigned int cmd, switch (cmd) { case AUDIO_SET_EFFECTS_CONFIG: { pr_debug("%s: AUDIO_SET_EFFECTS_CONFIG\n", __func__); + mutex_lock(&effects->lock); memset(&effects->config, 0, sizeof(effects->config)); if (copy_from_user(&effects->config, (void *)arg, sizeof(effects->config))) { @@ -473,6 +490,7 @@ static long audio_effects_ioctl(struct file *file, unsigned int cmd, effects->config.input.num_buf, effects->config.input.sample_rate, effects->config.input.num_channels); + mutex_unlock(&effects->lock); break; } case AUDIO_EFFECTS_SET_BUF_LEN: { @@ -494,6 +512,7 @@ static long audio_effects_ioctl(struct file *file, unsigned int cmd, buf_avail.input_num_avail = atomic_read(&effects->in_count); buf_avail.output_num_avail = atomic_read(&effects->out_count); + mutex_lock(&effects->lock); pr_debug("%s: write buf avail: %d, read buf avail: %d\n", __func__, buf_avail.output_num_avail, buf_avail.input_num_avail); @@ -503,16 +522,20 @@ static long audio_effects_ioctl(struct file *file, unsigned int cmd, __func__); rc = -EFAULT; } + mutex_unlock(&effects->lock); break; } case AUDIO_EFFECTS_SET_PP_PARAMS: { + mutex_lock(&effects->lock); if (copy_from_user(argvalues, (void *)arg, MAX_PP_PARAMS_SZ*sizeof(long))) { pr_err("%s: copy from user for pp params failed\n", __func__); + mutex_unlock(&effects->lock); return -EFAULT; } rc = audio_effects_set_pp_param(effects, argvalues); + mutex_unlock(&effects->lock); break; } default: @@ -578,12 +601,14 @@ static long audio_effects_compat_ioctl(struct file *file, unsigned int cmd, case AUDIO_SET_EFFECTS_CONFIG32: { struct msm_hwacc_effects_config32 config32; struct msm_hwacc_effects_config *config = &effects->config; + mutex_lock(&effects->lock); memset(&effects->config, 0, sizeof(effects->config)); if (copy_from_user(&config32, (void *)arg, sizeof(config32))) { pr_err("%s: copy to user for AUDIO_SET_EFFECTS_CONFIG failed\n", __func__); rc = -EFAULT; + mutex_unlock(&effects->lock); break; } config->input.buf_size = config32.input.buf_size; @@ -620,16 +645,19 @@ static long audio_effects_compat_ioctl(struct file *file, unsigned int cmd, effects->config.input.num_buf, effects->config.input.sample_rate, effects->config.input.num_channels); + mutex_unlock(&effects->lock); break; } case AUDIO_EFFECTS_SET_BUF_LEN32: { struct msm_hwacc_buf_cfg32 buf_cfg32; struct msm_hwacc_effects_config *config = &effects->config; + mutex_lock(&effects->lock); if (copy_from_user(&buf_cfg32, (void *)arg, sizeof(buf_cfg32))) { pr_err("%s: copy from user for AUDIO_EFFECTS_SET_BUF_LEN failed\n", __func__); rc = -EFAULT; + mutex_unlock(&effects->lock); break; } config->buf_cfg.input_len = buf_cfg32.input_len; @@ -637,6 +665,7 @@ static long audio_effects_compat_ioctl(struct file *file, unsigned int cmd, pr_debug("%s: write buf len: %d, read buf len: %d\n", __func__, effects->config.buf_cfg.output_len, effects->config.buf_cfg.input_len); + mutex_unlock(&effects->lock); break; } case AUDIO_EFFECTS_GET_BUF_AVAIL32: { @@ -644,6 +673,7 @@ static long audio_effects_compat_ioctl(struct file *file, unsigned int cmd, memset(&buf_avail, 0, sizeof(buf_avail)); + mutex_lock(&effects->lock); buf_avail.input_num_avail = atomic_read(&effects->in_count); buf_avail.output_num_avail = atomic_read(&effects->out_count); pr_debug("%s: write buf avail: %d, read buf avail: %d\n", @@ -655,22 +685,26 @@ static long audio_effects_compat_ioctl(struct file *file, unsigned int cmd, __func__); rc = -EFAULT; } + mutex_unlock(&effects->lock); break; } case AUDIO_EFFECTS_SET_PP_PARAMS32: { long argvalues[MAX_PP_PARAMS_SZ] = {0}; int argvalues32[MAX_PP_PARAMS_SZ] = {0}; + mutex_lock(&effects->lock); if (copy_from_user(argvalues32, (void *)arg, MAX_PP_PARAMS_SZ*sizeof(int))) { pr_err("%s: copy from user failed for pp params\n", __func__); + mutex_unlock(&effects->lock); return -EFAULT; } for (i = 0; i < MAX_PP_PARAMS_SZ; i++) argvalues[i] = argvalues32[i]; rc = audio_effects_set_pp_param(effects, argvalues); + mutex_unlock(&effects->lock); break; } case AUDIO_START32: { diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c b/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c index 3bb95f50bc13..52f7d3d2f268 100644 --- a/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c +++ b/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This 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 const int s_button_map[] = { }; /* The opened devices container */ -static int s_opened_devs[MAX_DEVS_NUMBER]; +static atomic_t s_opened_devs[MAX_DEVS_NUMBER]; static struct wakeup_source usf_wakeup_source; @@ -2338,14 +2338,11 @@ static uint16_t add_opened_dev(int minor) uint16_t ind = 0; for (ind = 0; ind < MAX_DEVS_NUMBER; ++ind) { - if (minor == s_opened_devs[ind]) { + if (minor == atomic_cmpxchg(&s_opened_devs[ind], 0, minor)) { pr_err("%s: device %d is already opened\n", __func__, minor); return USF_UNDEF_DEV_ID; - } - - if (s_opened_devs[ind] == 0) { - s_opened_devs[ind] = minor; + } else { pr_debug("%s: device %d is added; ind=%d\n", __func__, minor, ind); return ind; @@ -2401,7 +2398,7 @@ static int usf_release(struct inode *inode, struct file *file) usf_disable(&usf->usf_tx); usf_disable(&usf->usf_rx); - s_opened_devs[usf->dev_ind] = 0; + atomic_set(&s_opened_devs[usf->dev_ind], 0); wakeup_source_trash(&usf_wakeup_source); mutex_unlock(&usf->mutex); diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_cputime.c deleted file mode 100644 index c1ad5246f564..000000000000 --- a/drivers/misc/uid_cputime.c +++ /dev/null @@ -1,240 +0,0 @@ -/* drivers/misc/uid_cputime.c - * - * Copyright (C) 2014 - 2015 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <linux/atomic.h> -#include <linux/err.h> -#include <linux/hashtable.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/proc_fs.h> -#include <linux/profile.h> -#include <linux/sched.h> -#include <linux/seq_file.h> -#include <linux/slab.h> -#include <linux/uaccess.h> - -#define UID_HASH_BITS 10 -DECLARE_HASHTABLE(hash_table, UID_HASH_BITS); - -static DEFINE_MUTEX(uid_lock); -static struct proc_dir_entry *parent; - -struct uid_entry { - uid_t uid; - cputime_t utime; - cputime_t stime; - cputime_t active_utime; - cputime_t active_stime; - struct hlist_node hash; -}; - -static struct uid_entry *find_uid_entry(uid_t uid) -{ - struct uid_entry *uid_entry; - hash_for_each_possible(hash_table, uid_entry, hash, uid) { - if (uid_entry->uid == uid) - return uid_entry; - } - return NULL; -} - -static struct uid_entry *find_or_register_uid(uid_t uid) -{ - struct uid_entry *uid_entry; - - uid_entry = find_uid_entry(uid); - if (uid_entry) - return uid_entry; - - uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC); - if (!uid_entry) - return NULL; - - uid_entry->uid = uid; - - hash_add(hash_table, &uid_entry->hash, uid); - - return uid_entry; -} - -static int uid_stat_show(struct seq_file *m, void *v) -{ - struct uid_entry *uid_entry; - struct task_struct *task, *temp; - cputime_t utime; - cputime_t stime; - unsigned long bkt; - - mutex_lock(&uid_lock); - - hash_for_each(hash_table, bkt, uid_entry, hash) { - uid_entry->active_stime = 0; - uid_entry->active_utime = 0; - } - - read_lock(&tasklist_lock); - do_each_thread(temp, task) { - uid_entry = find_or_register_uid(from_kuid_munged( - current_user_ns(), task_uid(task))); - if (!uid_entry) { - read_unlock(&tasklist_lock); - mutex_unlock(&uid_lock); - pr_err("%s: failed to find the uid_entry for uid %d\n", - __func__, from_kuid_munged(current_user_ns(), - task_uid(task))); - return -ENOMEM; - } - task_cputime_adjusted(task, &utime, &stime); - uid_entry->active_utime += utime; - uid_entry->active_stime += stime; - } while_each_thread(temp, task); - read_unlock(&tasklist_lock); - - hash_for_each(hash_table, bkt, uid_entry, hash) { - cputime_t total_utime = uid_entry->utime + - uid_entry->active_utime; - cputime_t total_stime = uid_entry->stime + - uid_entry->active_stime; - seq_printf(m, "%d: %llu %llu\n", uid_entry->uid, - (unsigned long long)jiffies_to_msecs( - cputime_to_jiffies(total_utime)) * USEC_PER_MSEC, - (unsigned long long)jiffies_to_msecs( - cputime_to_jiffies(total_stime)) * USEC_PER_MSEC); - } - - mutex_unlock(&uid_lock); - return 0; -} - -static int uid_stat_open(struct inode *inode, struct file *file) -{ - return single_open(file, uid_stat_show, PDE_DATA(inode)); -} - -static const struct file_operations uid_stat_fops = { - .open = uid_stat_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int uid_remove_open(struct inode *inode, struct file *file) -{ - return single_open(file, NULL, NULL); -} - -static ssize_t uid_remove_write(struct file *file, - const char __user *buffer, size_t count, loff_t *ppos) -{ - struct uid_entry *uid_entry; - struct hlist_node *tmp; - char uids[128]; - char *start_uid, *end_uid = NULL; - long int uid_start = 0, uid_end = 0; - - if (count >= sizeof(uids)) - count = sizeof(uids) - 1; - - if (copy_from_user(uids, buffer, count)) - return -EFAULT; - - uids[count] = '\0'; - end_uid = uids; - start_uid = strsep(&end_uid, "-"); - - if (!start_uid || !end_uid) - return -EINVAL; - - if (kstrtol(start_uid, 10, &uid_start) != 0 || - kstrtol(end_uid, 10, &uid_end) != 0) { - return -EINVAL; - } - mutex_lock(&uid_lock); - - for (; uid_start <= uid_end; uid_start++) { - hash_for_each_possible_safe(hash_table, uid_entry, tmp, - hash, (uid_t)uid_start) { - if (uid_start == uid_entry->uid) { - hash_del(&uid_entry->hash); - kfree(uid_entry); - } - } - } - - mutex_unlock(&uid_lock); - return count; -} - -static const struct file_operations uid_remove_fops = { - .open = uid_remove_open, - .release = single_release, - .write = uid_remove_write, -}; - -static int process_notifier(struct notifier_block *self, - unsigned long cmd, void *v) -{ - struct task_struct *task = v; - struct uid_entry *uid_entry; - cputime_t utime, stime; - uid_t uid; - - if (!task) - return NOTIFY_OK; - - mutex_lock(&uid_lock); - uid = from_kuid_munged(current_user_ns(), task_uid(task)); - uid_entry = find_or_register_uid(uid); - if (!uid_entry) { - pr_err("%s: failed to find uid %d\n", __func__, uid); - goto exit; - } - - task_cputime_adjusted(task, &utime, &stime); - uid_entry->utime += utime; - uid_entry->stime += stime; - -exit: - mutex_unlock(&uid_lock); - return NOTIFY_OK; -} - -static struct notifier_block process_notifier_block = { - .notifier_call = process_notifier, -}; - -static int __init proc_uid_cputime_init(void) -{ - hash_init(hash_table); - - parent = proc_mkdir("uid_cputime", NULL); - if (!parent) { - pr_err("%s: failed to create proc entry\n", __func__); - return -ENOMEM; - } - - proc_create_data("remove_uid_range", S_IWUGO, parent, &uid_remove_fops, - NULL); - - proc_create_data("show_uid_stat", S_IRUGO, parent, &uid_stat_fops, - NULL); - - profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block); - - return 0; -} - -early_initcall(proc_uid_cputime_init); diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c new file mode 100644 index 000000000000..204b23484266 --- /dev/null +++ b/drivers/misc/uid_sys_stats.c @@ -0,0 +1,463 @@ +/* drivers/misc/uid_cputime.c + * + * Copyright (C) 2014 - 2015 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/atomic.h> +#include <linux/err.h> +#include <linux/hashtable.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/proc_fs.h> +#include <linux/profile.h> +#include <linux/rtmutex.h> +#include <linux/sched.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +#define UID_HASH_BITS 10 +DECLARE_HASHTABLE(hash_table, UID_HASH_BITS); + +static DEFINE_RT_MUTEX(uid_lock); +static struct proc_dir_entry *cpu_parent; +static struct proc_dir_entry *io_parent; +static struct proc_dir_entry *proc_parent; + +struct io_stats { + u64 read_bytes; + u64 write_bytes; + u64 rchar; + u64 wchar; + u64 fsync; +}; + +#define UID_STATE_FOREGROUND 0 +#define UID_STATE_BACKGROUND 1 +#define UID_STATE_BUCKET_SIZE 2 + +#define UID_STATE_TOTAL_CURR 2 +#define UID_STATE_TOTAL_LAST 3 +#define UID_STATE_SIZE 4 + +struct uid_entry { + uid_t uid; + cputime_t utime; + cputime_t stime; + cputime_t active_utime; + cputime_t active_stime; + int state; + struct io_stats io[UID_STATE_SIZE]; + struct hlist_node hash; +}; + +static struct uid_entry *find_uid_entry(uid_t uid) +{ + struct uid_entry *uid_entry; + hash_for_each_possible(hash_table, uid_entry, hash, uid) { + if (uid_entry->uid == uid) + return uid_entry; + } + return NULL; +} + +static struct uid_entry *find_or_register_uid(uid_t uid) +{ + struct uid_entry *uid_entry; + + uid_entry = find_uid_entry(uid); + if (uid_entry) + return uid_entry; + + uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC); + if (!uid_entry) + return NULL; + + uid_entry->uid = uid; + + hash_add(hash_table, &uid_entry->hash, uid); + + return uid_entry; +} + +static int uid_cputime_show(struct seq_file *m, void *v) +{ + struct uid_entry *uid_entry; + struct task_struct *task, *temp; + cputime_t utime; + cputime_t stime; + unsigned long bkt; + + rt_mutex_lock(&uid_lock); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + uid_entry->active_stime = 0; + uid_entry->active_utime = 0; + } + + read_lock(&tasklist_lock); + do_each_thread(temp, task) { + uid_entry = find_or_register_uid(from_kuid_munged( + current_user_ns(), task_uid(task))); + if (!uid_entry) { + read_unlock(&tasklist_lock); + rt_mutex_unlock(&uid_lock); + pr_err("%s: failed to find the uid_entry for uid %d\n", + __func__, from_kuid_munged(current_user_ns(), + task_uid(task))); + return -ENOMEM; + } + task_cputime_adjusted(task, &utime, &stime); + uid_entry->active_utime += utime; + uid_entry->active_stime += stime; + } while_each_thread(temp, task); + read_unlock(&tasklist_lock); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + cputime_t total_utime = uid_entry->utime + + uid_entry->active_utime; + cputime_t total_stime = uid_entry->stime + + uid_entry->active_stime; + seq_printf(m, "%d: %llu %llu\n", uid_entry->uid, + (unsigned long long)jiffies_to_msecs( + cputime_to_jiffies(total_utime)) * USEC_PER_MSEC, + (unsigned long long)jiffies_to_msecs( + cputime_to_jiffies(total_stime)) * USEC_PER_MSEC); + } + + rt_mutex_unlock(&uid_lock); + return 0; +} + +static int uid_cputime_open(struct inode *inode, struct file *file) +{ + return single_open(file, uid_cputime_show, PDE_DATA(inode)); +} + +static const struct file_operations uid_cputime_fops = { + .open = uid_cputime_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int uid_remove_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, NULL); +} + +static ssize_t uid_remove_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + struct uid_entry *uid_entry; + struct hlist_node *tmp; + char uids[128]; + char *start_uid, *end_uid = NULL; + long int uid_start = 0, uid_end = 0; + + if (count >= sizeof(uids)) + count = sizeof(uids) - 1; + + if (copy_from_user(uids, buffer, count)) + return -EFAULT; + + uids[count] = '\0'; + end_uid = uids; + start_uid = strsep(&end_uid, "-"); + + if (!start_uid || !end_uid) + return -EINVAL; + + if (kstrtol(start_uid, 10, &uid_start) != 0 || + kstrtol(end_uid, 10, &uid_end) != 0) { + return -EINVAL; + } + rt_mutex_lock(&uid_lock); + + for (; uid_start <= uid_end; uid_start++) { + hash_for_each_possible_safe(hash_table, uid_entry, tmp, + hash, (uid_t)uid_start) { + if (uid_start == uid_entry->uid) { + hash_del(&uid_entry->hash); + kfree(uid_entry); + } + } + } + + rt_mutex_unlock(&uid_lock); + return count; +} + +static const struct file_operations uid_remove_fops = { + .open = uid_remove_open, + .release = single_release, + .write = uid_remove_write, +}; + +static u64 compute_write_bytes(struct task_struct *task) +{ + if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes) + return 0; + + return task->ioac.write_bytes - task->ioac.cancelled_write_bytes; +} + +static void add_uid_io_curr_stats(struct uid_entry *uid_entry, + struct task_struct *task) +{ + struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + + io_curr->read_bytes += task->ioac.read_bytes; + io_curr->write_bytes += compute_write_bytes(task); + io_curr->rchar += task->ioac.rchar; + io_curr->wchar += task->ioac.wchar; + io_curr->fsync += task->ioac.syscfs; +} + +static void clean_uid_io_last_stats(struct uid_entry *uid_entry, + struct task_struct *task) +{ + struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + + io_last->read_bytes -= task->ioac.read_bytes; + io_last->write_bytes -= compute_write_bytes(task); + io_last->rchar -= task->ioac.rchar; + io_last->wchar -= task->ioac.wchar; + io_last->fsync -= task->ioac.syscfs; +} + +static void update_io_stats_locked(void) +{ + struct uid_entry *uid_entry; + struct task_struct *task, *temp; + struct io_stats *io_bucket, *io_curr, *io_last; + unsigned long bkt; + + BUG_ON(!rt_mutex_is_locked(&uid_lock)); + + hash_for_each(hash_table, bkt, uid_entry, hash) + memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, + sizeof(struct io_stats)); + + read_lock(&tasklist_lock); + do_each_thread(temp, task) { + uid_entry = find_or_register_uid(from_kuid_munged( + current_user_ns(), task_uid(task))); + if (!uid_entry) + continue; + add_uid_io_curr_stats(uid_entry, task); + } while_each_thread(temp, task); + read_unlock(&tasklist_lock); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + io_bucket = &uid_entry->io[uid_entry->state]; + io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + + io_bucket->read_bytes += + io_curr->read_bytes - io_last->read_bytes; + io_bucket->write_bytes += + io_curr->write_bytes - io_last->write_bytes; + io_bucket->rchar += io_curr->rchar - io_last->rchar; + io_bucket->wchar += io_curr->wchar - io_last->wchar; + io_bucket->fsync += io_curr->fsync - io_last->fsync; + + io_last->read_bytes = io_curr->read_bytes; + io_last->write_bytes = io_curr->write_bytes; + io_last->rchar = io_curr->rchar; + io_last->wchar = io_curr->wchar; + io_last->fsync = io_curr->fsync; + } +} + +static int uid_io_show(struct seq_file *m, void *v) +{ + struct uid_entry *uid_entry; + unsigned long bkt; + + rt_mutex_lock(&uid_lock); + + update_io_stats_locked(); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n", + uid_entry->uid, + uid_entry->io[UID_STATE_FOREGROUND].rchar, + uid_entry->io[UID_STATE_FOREGROUND].wchar, + uid_entry->io[UID_STATE_FOREGROUND].read_bytes, + uid_entry->io[UID_STATE_FOREGROUND].write_bytes, + uid_entry->io[UID_STATE_BACKGROUND].rchar, + uid_entry->io[UID_STATE_BACKGROUND].wchar, + uid_entry->io[UID_STATE_BACKGROUND].read_bytes, + uid_entry->io[UID_STATE_BACKGROUND].write_bytes, + uid_entry->io[UID_STATE_FOREGROUND].fsync, + uid_entry->io[UID_STATE_BACKGROUND].fsync); + } + + rt_mutex_unlock(&uid_lock); + + return 0; +} + +static int uid_io_open(struct inode *inode, struct file *file) +{ + return single_open(file, uid_io_show, PDE_DATA(inode)); +} + +static const struct file_operations uid_io_fops = { + .open = uid_io_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int uid_procstat_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, NULL); +} + +static ssize_t uid_procstat_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + struct uid_entry *uid_entry; + uid_t uid; + int argc, state; + char input[128]; + + if (count >= sizeof(input)) + return -EINVAL; + + if (copy_from_user(input, buffer, count)) + return -EFAULT; + + input[count] = '\0'; + + argc = sscanf(input, "%u %d", &uid, &state); + if (argc != 2) + return -EINVAL; + + if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND) + return -EINVAL; + + rt_mutex_lock(&uid_lock); + + uid_entry = find_or_register_uid(uid); + if (!uid_entry) { + rt_mutex_unlock(&uid_lock); + return -EINVAL; + } + + if (uid_entry->state == state) { + rt_mutex_unlock(&uid_lock); + return count; + } + + update_io_stats_locked(); + + uid_entry->state = state; + + rt_mutex_unlock(&uid_lock); + + return count; +} + +static const struct file_operations uid_procstat_fops = { + .open = uid_procstat_open, + .release = single_release, + .write = uid_procstat_write, +}; + +static int process_notifier(struct notifier_block *self, + unsigned long cmd, void *v) +{ + struct task_struct *task = v; + struct uid_entry *uid_entry; + cputime_t utime, stime; + uid_t uid; + + if (!task) + return NOTIFY_OK; + + rt_mutex_lock(&uid_lock); + uid = from_kuid_munged(current_user_ns(), task_uid(task)); + uid_entry = find_or_register_uid(uid); + if (!uid_entry) { + pr_err("%s: failed to find uid %d\n", __func__, uid); + goto exit; + } + + task_cputime_adjusted(task, &utime, &stime); + uid_entry->utime += utime; + uid_entry->stime += stime; + + update_io_stats_locked(); + clean_uid_io_last_stats(uid_entry, task); + +exit: + rt_mutex_unlock(&uid_lock); + return NOTIFY_OK; +} + +static struct notifier_block process_notifier_block = { + .notifier_call = process_notifier, +}; + +static int __init proc_uid_sys_stats_init(void) +{ + hash_init(hash_table); + + cpu_parent = proc_mkdir("uid_cputime", NULL); + if (!cpu_parent) { + pr_err("%s: failed to create uid_cputime proc entry\n", + __func__); + goto err; + } + + proc_create_data("remove_uid_range", 0222, cpu_parent, + &uid_remove_fops, NULL); + proc_create_data("show_uid_stat", 0444, cpu_parent, + &uid_cputime_fops, NULL); + + io_parent = proc_mkdir("uid_io", NULL); + if (!io_parent) { + pr_err("%s: failed to create uid_io proc entry\n", + __func__); + goto err; + } + + proc_create_data("stats", 0444, io_parent, + &uid_io_fops, NULL); + + proc_parent = proc_mkdir("uid_procstat", NULL); + if (!proc_parent) { + pr_err("%s: failed to create uid_procstat proc entry\n", + __func__); + goto err; + } + + proc_create_data("set", 0222, proc_parent, + &uid_procstat_fops, NULL); + + profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block); + + return 0; + +err: + remove_proc_subtree("uid_cputime", NULL); + remove_proc_subtree("uid_io", NULL); + remove_proc_subtree("uid_procstat", NULL); + return -ENOMEM; +} + +early_initcall(proc_uid_sys_stats_init); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 3b79f514350e..afdf70000651 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -680,6 +680,12 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) card->ext_csd.ffu_capable = (ext_csd[EXT_CSD_SUPPORTED_MODE] & 0x1) && !(ext_csd[EXT_CSD_FW_CONFIG] & 0x1); + + card->ext_csd.pre_eol_info = ext_csd[EXT_CSD_PRE_EOL_INFO]; + card->ext_csd.device_life_time_est_typ_a = + ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A]; + card->ext_csd.device_life_time_est_typ_b = + ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B]; } out: return err; @@ -813,6 +819,11 @@ MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid); MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name); MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid); MMC_DEV_ATTR(prv, "0x%x\n", card->cid.prv); +MMC_DEV_ATTR(rev, "0x%x\n", card->ext_csd.rev); +MMC_DEV_ATTR(pre_eol_info, "%02x\n", card->ext_csd.pre_eol_info); +MMC_DEV_ATTR(life_time, "0x%02x 0x%02x\n", + card->ext_csd.device_life_time_est_typ_a, + card->ext_csd.device_life_time_est_typ_b); MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial); MMC_DEV_ATTR(enhanced_area_offset, "%llu\n", card->ext_csd.enhanced_area_offset); @@ -851,6 +862,9 @@ static struct attribute *mmc_std_attrs[] = { &dev_attr_name.attr, &dev_attr_oemid.attr, &dev_attr_prv.attr, + &dev_attr_rev.attr, + &dev_attr_pre_eol_info.attr, + &dev_attr_life_time.attr, &dev_attr_serial.attr, &dev_attr_enhanced_area_offset.attr, &dev_attr_enhanced_area_size.attr, diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c index d2c386f09d69..1d843357422e 100644 --- a/drivers/mmc/host/ushc.c +++ b/drivers/mmc/host/ushc.c @@ -426,6 +426,9 @@ static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id struct ushc_data *ushc; int ret; + if (intf->cur_altsetting->desc.bNumEndpoints < 1) + return -ENODEV; + mmc = mmc_alloc_host(sizeof(struct ushc_data), &intf->dev); if (mmc == NULL) return -ENOMEM; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index b6fa89102526..66ba1e0ff37e 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -913,8 +913,8 @@ #define RX_PACKET_ATTRIBUTES_CSUM_DONE_WIDTH 1 #define RX_PACKET_ATTRIBUTES_VLAN_CTAG_INDEX 1 #define RX_PACKET_ATTRIBUTES_VLAN_CTAG_WIDTH 1 -#define RX_PACKET_ATTRIBUTES_INCOMPLETE_INDEX 2 -#define RX_PACKET_ATTRIBUTES_INCOMPLETE_WIDTH 1 +#define RX_PACKET_ATTRIBUTES_LAST_INDEX 2 +#define RX_PACKET_ATTRIBUTES_LAST_WIDTH 1 #define RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_INDEX 3 #define RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_WIDTH 1 #define RX_PACKET_ATTRIBUTES_CONTEXT_INDEX 4 @@ -923,6 +923,8 @@ #define RX_PACKET_ATTRIBUTES_RX_TSTAMP_WIDTH 1 #define RX_PACKET_ATTRIBUTES_RSS_HASH_INDEX 6 #define RX_PACKET_ATTRIBUTES_RSS_HASH_WIDTH 1 +#define RX_PACKET_ATTRIBUTES_FIRST_INDEX 7 +#define RX_PACKET_ATTRIBUTES_FIRST_WIDTH 1 #define RX_NORMAL_DESC0_OVT_INDEX 0 #define RX_NORMAL_DESC0_OVT_WIDTH 16 diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index f6a7161e3b85..5e6238e0b2bd 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -1658,10 +1658,15 @@ static int xgbe_dev_read(struct xgbe_channel *channel) /* Get the header length */ if (XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, FD)) { + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + FIRST, 1); rdata->rx.hdr_len = XGMAC_GET_BITS_LE(rdesc->desc2, RX_NORMAL_DESC2, HL); if (rdata->rx.hdr_len) pdata->ext_stats.rx_split_header_packets++; + } else { + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + FIRST, 0); } /* Get the RSS hash */ @@ -1684,19 +1689,16 @@ static int xgbe_dev_read(struct xgbe_channel *channel) } } - /* Get the packet length */ - rdata->rx.len = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, PL); - - if (!XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, LD)) { - /* Not all the data has been transferred for this packet */ - XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, - INCOMPLETE, 1); + /* Not all the data has been transferred for this packet */ + if (!XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, LD)) return 0; - } /* This is the last of the data for this packet */ XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, - INCOMPLETE, 0); + LAST, 1); + + /* Get the packet length */ + rdata->rx.len = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, PL); /* Set checksum done indicator as appropriate */ if (netdev->features & NETIF_F_RXCSUM) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 53ce1222b11d..865b7e0b133b 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -1760,13 +1760,12 @@ static struct sk_buff *xgbe_create_skb(struct xgbe_prv_data *pdata, { struct sk_buff *skb; u8 *packet; - unsigned int copy_len; skb = napi_alloc_skb(napi, rdata->rx.hdr.dma_len); if (!skb) return NULL; - /* Start with the header buffer which may contain just the header + /* Pull in the header buffer which may contain just the header * or the header plus data */ dma_sync_single_range_for_cpu(pdata->dev, rdata->rx.hdr.dma_base, @@ -1775,30 +1774,49 @@ static struct sk_buff *xgbe_create_skb(struct xgbe_prv_data *pdata, packet = page_address(rdata->rx.hdr.pa.pages) + rdata->rx.hdr.pa.pages_offset; - copy_len = (rdata->rx.hdr_len) ? rdata->rx.hdr_len : len; - copy_len = min(rdata->rx.hdr.dma_len, copy_len); - skb_copy_to_linear_data(skb, packet, copy_len); - skb_put(skb, copy_len); - - len -= copy_len; - if (len) { - /* Add the remaining data as a frag */ - dma_sync_single_range_for_cpu(pdata->dev, - rdata->rx.buf.dma_base, - rdata->rx.buf.dma_off, - rdata->rx.buf.dma_len, - DMA_FROM_DEVICE); - - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, - rdata->rx.buf.pa.pages, - rdata->rx.buf.pa.pages_offset, - len, rdata->rx.buf.dma_len); - rdata->rx.buf.pa.pages = NULL; - } + skb_copy_to_linear_data(skb, packet, len); + skb_put(skb, len); return skb; } +static unsigned int xgbe_rx_buf1_len(struct xgbe_ring_data *rdata, + struct xgbe_packet_data *packet) +{ + /* Always zero if not the first descriptor */ + if (!XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, FIRST)) + return 0; + + /* First descriptor with split header, return header length */ + if (rdata->rx.hdr_len) + return rdata->rx.hdr_len; + + /* First descriptor but not the last descriptor and no split header, + * so the full buffer was used + */ + if (!XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, LAST)) + return rdata->rx.hdr.dma_len; + + /* First descriptor and last descriptor and no split header, so + * calculate how much of the buffer was used + */ + return min_t(unsigned int, rdata->rx.hdr.dma_len, rdata->rx.len); +} + +static unsigned int xgbe_rx_buf2_len(struct xgbe_ring_data *rdata, + struct xgbe_packet_data *packet, + unsigned int len) +{ + /* Always the full buffer if not the last descriptor */ + if (!XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, LAST)) + return rdata->rx.buf.dma_len; + + /* Last descriptor so calculate how much of the buffer was used + * for the last bit of data + */ + return rdata->rx.len - len; +} + static int xgbe_tx_poll(struct xgbe_channel *channel) { struct xgbe_prv_data *pdata = channel->pdata; @@ -1881,8 +1899,8 @@ static int xgbe_rx_poll(struct xgbe_channel *channel, int budget) struct napi_struct *napi; struct sk_buff *skb; struct skb_shared_hwtstamps *hwtstamps; - unsigned int incomplete, error, context_next, context; - unsigned int len, rdesc_len, max_len; + unsigned int last, error, context_next, context; + unsigned int len, buf1_len, buf2_len, max_len; unsigned int received = 0; int packet_count = 0; @@ -1892,7 +1910,7 @@ static int xgbe_rx_poll(struct xgbe_channel *channel, int budget) if (!ring) return 0; - incomplete = 0; + last = 0; context_next = 0; napi = (pdata->per_channel_irq) ? &channel->napi : &pdata->napi; @@ -1926,9 +1944,8 @@ read_again: received++; ring->cur++; - incomplete = XGMAC_GET_BITS(packet->attributes, - RX_PACKET_ATTRIBUTES, - INCOMPLETE); + last = XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + LAST); context_next = XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, CONTEXT_NEXT); @@ -1937,7 +1954,7 @@ read_again: CONTEXT); /* Earlier error, just drain the remaining data */ - if ((incomplete || context_next) && error) + if ((!last || context_next) && error) goto read_again; if (error || packet->errors) { @@ -1949,16 +1966,22 @@ read_again: } if (!context) { - /* Length is cumulative, get this descriptor's length */ - rdesc_len = rdata->rx.len - len; - len += rdesc_len; + /* Get the data length in the descriptor buffers */ + buf1_len = xgbe_rx_buf1_len(rdata, packet); + len += buf1_len; + buf2_len = xgbe_rx_buf2_len(rdata, packet, len); + len += buf2_len; - if (rdesc_len && !skb) { + if (!skb) { skb = xgbe_create_skb(pdata, napi, rdata, - rdesc_len); - if (!skb) + buf1_len); + if (!skb) { error = 1; - } else if (rdesc_len) { + goto skip_data; + } + } + + if (buf2_len) { dma_sync_single_range_for_cpu(pdata->dev, rdata->rx.buf.dma_base, rdata->rx.buf.dma_off, @@ -1968,13 +1991,14 @@ read_again: skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rdata->rx.buf.pa.pages, rdata->rx.buf.pa.pages_offset, - rdesc_len, + buf2_len, rdata->rx.buf.dma_len); rdata->rx.buf.pa.pages = NULL; } } - if (incomplete || context_next) +skip_data: + if (!last || context_next) goto read_again; if (!skb) @@ -2033,7 +2057,7 @@ next_packet: } /* Check if we need to save state before leaving */ - if (received && (incomplete || context_next)) { + if (received && (!last || context_next)) { rdata = XGBE_GET_DESC_DATA(ring, ring->cur); rdata->state_saved = 1; rdata->state.skb = skb; diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 91627561c58d..f971d92f7b41 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -3495,7 +3495,8 @@ static int bcmgenet_suspend(struct device *d) bcmgenet_netif_stop(dev); - phy_suspend(priv->phydev); + if (!device_may_wakeup(d)) + phy_suspend(priv->phydev); netif_device_detach(dev); @@ -3592,7 +3593,8 @@ static int bcmgenet_resume(struct device *d) netif_device_attach(dev); - phy_resume(priv->phydev); + if (!device_may_wakeup(d)) + phy_resume(priv->phydev); if (priv->eee.eee_enabled) bcmgenet_eee_enable_set(dev, true); diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 8bdfe53754ba..e96d1f95bb47 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -220,20 +220,6 @@ void bcmgenet_phy_power_set(struct net_device *dev, bool enable) udelay(60); } -static void bcmgenet_internal_phy_setup(struct net_device *dev) -{ - struct bcmgenet_priv *priv = netdev_priv(dev); - u32 reg; - - /* Power up PHY */ - bcmgenet_phy_power_set(dev, true); - /* enable APD */ - reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT); - reg |= EXT_PWR_DN_EN_LD; - bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT); - bcmgenet_mii_reset(dev); -} - static void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv) { u32 reg; @@ -281,7 +267,6 @@ int bcmgenet_mii_config(struct net_device *dev) if (priv->internal_phy) { phy_name = "internal PHY"; - bcmgenet_internal_phy_setup(dev); } else if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) { phy_name = "MoCA"; bcmgenet_moca_phy_setup(priv); diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c index 23ec28f43f6d..afaa98d1d4e4 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.c +++ b/drivers/net/ethernet/intel/igb/e1000_phy.c @@ -77,6 +77,10 @@ s32 igb_get_phy_id(struct e1000_hw *hw) s32 ret_val = 0; u16 phy_id; + /* ensure PHY page selection to fix misconfigured i210 */ + if ((hw->mac.type == e1000_i210) || (hw->mac.type == e1000_i211)) + phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, 0); + ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id); if (ret_val) goto out; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index cf0098596e85..e9408f5e2a1d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -197,6 +197,10 @@ static inline void mlx5e_build_rx_skb(struct mlx5_cqe64 *cqe, if (lro_num_seg > 1) { mlx5e_lro_update_hdr(skb, cqe); skb_shinfo(skb)->gso_size = DIV_ROUND_UP(cqe_bcnt, lro_num_seg); + /* Subtract one since we already counted this as one + * "regular" packet in mlx5e_complete_rx_cqe() + */ + rq->stats.packets += lro_num_seg - 1; rq->stats.lro_packets++; rq->stats.lro_bytes += cqe_bcnt; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index ba115ec7aa92..1e611980cf99 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -85,7 +85,7 @@ static struct mlx5_profile profile[] = { [2] = { .mask = MLX5_PROF_MASK_QP_SIZE | MLX5_PROF_MASK_MR_CACHE, - .log_max_qp = 17, + .log_max_qp = 18, .mr_cache[0] = { .size = 500, .limit = 250 diff --git a/drivers/net/ethernet/msm/msm_rmnet_mhi.c b/drivers/net/ethernet/msm/msm_rmnet_mhi.c index 015cb99d445b..60b7a64c2edb 100644 --- a/drivers/net/ethernet/msm/msm_rmnet_mhi.c +++ b/drivers/net/ethernet/msm/msm_rmnet_mhi.c @@ -983,14 +983,17 @@ static void rmnet_mhi_cb(struct mhi_cb_info *cb_info) if (cb_info->chan == rmnet_mhi_ptr->rx_channel) { rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Receive MHI_DISABLE notification for rx path\n"); - rmnet_mhi_disable(rmnet_mhi_ptr); + if (rmnet_mhi_ptr->dev) + rmnet_mhi_disable(rmnet_mhi_ptr); } else { rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Receive MHI_DISABLE notification for tx path\n"); rmnet_mhi_ptr->tx_enabled = 0; - rmnet_mhi_internal_clean_unmap_buffers - (rmnet_mhi_ptr->dev, &rmnet_mhi_ptr->tx_buffers, - DMA_TO_DEVICE); + if (rmnet_mhi_ptr->dev) + rmnet_mhi_internal_clean_unmap_buffers( + rmnet_mhi_ptr->dev, + &rmnet_mhi_ptr->tx_buffers, + DMA_TO_DEVICE); } /* Remove all votes disabling low power mode */ diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index e8a09ff9e724..c8a7802d2953 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -197,65 +197,6 @@ static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size, return ppi; } -union sub_key { - u64 k; - struct { - u8 pad[3]; - u8 kb; - u32 ka; - }; -}; - -/* Toeplitz hash function - * data: network byte order - * return: host byte order - */ -static u32 comp_hash(u8 *key, int klen, void *data, int dlen) -{ - union sub_key subk; - int k_next = 4; - u8 dt; - int i, j; - u32 ret = 0; - - subk.k = 0; - subk.ka = ntohl(*(u32 *)key); - - for (i = 0; i < dlen; i++) { - subk.kb = key[k_next]; - k_next = (k_next + 1) % klen; - dt = ((u8 *)data)[i]; - for (j = 0; j < 8; j++) { - if (dt & 0x80) - ret ^= subk.ka; - dt <<= 1; - subk.k <<= 1; - } - } - - return ret; -} - -static bool netvsc_set_hash(u32 *hash, struct sk_buff *skb) -{ - struct flow_keys flow; - int data_len; - - if (!skb_flow_dissect_flow_keys(skb, &flow, 0) || - !(flow.basic.n_proto == htons(ETH_P_IP) || - flow.basic.n_proto == htons(ETH_P_IPV6))) - return false; - - if (flow.basic.ip_proto == IPPROTO_TCP) - data_len = 12; - else - data_len = 8; - - *hash = comp_hash(netvsc_hash_key, HASH_KEYLEN, &flow, data_len); - - return true; -} - static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, void *accel_priv, select_queue_fallback_t fallback) { @@ -268,11 +209,9 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, if (nvsc_dev == NULL || ndev->real_num_tx_queues <= 1) return 0; - if (netvsc_set_hash(&hash, skb)) { - q_idx = nvsc_dev->send_table[hash % VRSS_SEND_TAB_SIZE] % - ndev->real_num_tx_queues; - skb_set_hash(skb, hash, PKT_HASH_TYPE_L3); - } + hash = skb_get_hash(skb); + q_idx = nvsc_dev->send_table[hash % VRSS_SEND_TAB_SIZE] % + ndev->real_num_tx_queues; return q_idx; } diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index d6b619667f1a..349aecbc210a 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -345,6 +345,7 @@ static netdev_tx_t is_ip_tx_frame(struct sk_buff *skb, struct net_device *dev) static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev) { + int len = skb->len; netdev_tx_t ret = is_ip_tx_frame(skb, dev); if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) { @@ -352,7 +353,7 @@ static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev) u64_stats_update_begin(&dstats->syncp); dstats->tx_pkts++; - dstats->tx_bytes += skb->len; + dstats->tx_bytes += len; u64_stats_update_end(&dstats->syncp); } else { this_cpu_inc(dev->dstats->tx_drps); diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 6fa8e165878e..590750ab6564 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2600,7 +2600,7 @@ static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[]) if (data[IFLA_VXLAN_ID]) { __u32 id = nla_get_u32(data[IFLA_VXLAN_ID]); - if (id >= VXLAN_VID_MASK) + if (id >= VXLAN_N_VID) return -ERANGE; } diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 6dd396430f19..b0d6c2614731 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -122,6 +122,19 @@ struct htt_msdu_ext_desc { | HTT_MSDU_EXT_DESC_FLAG_TCP_IPV4_CSUM_ENABLE \ | HTT_MSDU_EXT_DESC_FLAG_TCP_IPV6_CSUM_ENABLE) +#define HTT_TX_IPV4_CSUM_EN BIT(16) +#define HTT_TX_UDP_IPV4_CSUM_EN BIT(17) +#define HTT_TX_UDP_IPV6_CSUM_EN BIT(18) +#define HTT_TX_TCP_IPV4_CSUM_EN BIT(19) +#define HTT_TX_TCP_IPV6_CSUM_EN BIT(20) +#define HTT_TX_PARTIAL_CSUM_EN BIT(21) + +#define HTT_TX_CHECKSUM_ENABLE (HTT_TX_IPV4_CSUM_EN \ + | HTT_TX_UDP_IPV4_CSUM_EN \ + | HTT_TX_UDP_IPV6_CSUM_EN \ + | HTT_TX_TCP_IPV4_CSUM_EN \ + | HTT_TX_TCP_IPV6_CSUM_EN) + enum htt_data_tx_desc_flags0 { HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT = 1 << 0, HTT_DATA_TX_DESC_FLAGS0_NO_AGGR = 1 << 1, diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 62e7f5e4b6fc..1b59721a91ac 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -1051,8 +1051,14 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, !test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) { flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD; flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD; - if (ar->hw_params.continuous_frag_desc) + if (ar->hw_params.continuous_frag_desc) { ext_desc->flags |= HTT_MSDU_CHECKSUM_ENABLE; + if (QCA_REV_WCN3990(ar)) { + memset(ext_desc->tso_flag, 0, + sizeof(ext_desc->tso_flag)); + ext_desc->tso_flag[3] |= HTT_TX_CHECKSUM_ENABLE; + } + } } /* Prevent firmware from sending up tx inspection requests. There's diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 35e5d980ed49..b9d08b4b4cc5 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -4671,7 +4671,8 @@ static int ath10k_mac_txpower_recalc(struct ath10k *ar) lockdep_assert_held(&ar->conf_mutex); list_for_each_entry(arvif, &ar->arvifs, list) { - WARN_ON(arvif->txpower < 0); + if (arvif->txpower <= 0) + continue; if (txpower == -1) txpower = arvif->txpower; @@ -4679,8 +4680,8 @@ static int ath10k_mac_txpower_recalc(struct ath10k *ar) txpower = min(txpower, arvif->txpower); } - if (WARN_ON(txpower == -1)) - return -EINVAL; + if (txpower == -1) + return 0; ret = ath10k_mac_txpower_setup(ar, txpower); if (ret) { @@ -5190,6 +5191,10 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, ath10k_warn(ar, "failed to recalc monitor: %d\n", ret); } + ret = ath10k_mac_txpower_recalc(ar); + if (ret) + ath10k_warn(ar, "failed to recalc tx power: %d\n", ret); + spin_lock_bh(&ar->htt.tx_lock); ath10k_mac_vif_tx_unlock_all(arvif); spin_unlock_bh(&ar->htt.tx_lock); @@ -7993,10 +7998,8 @@ int ath10k_mac_register(struct ath10k *ar) goto err_free; } - if (!QCA_REV_WCN3990(ar)) { - if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) - ar->hw->netdev_features = NETIF_F_HW_CSUM; - } + if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) + ar->hw->netdev_features = NETIF_F_HW_CSUM; if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED)) { /* Init ath dfs pattern detector */ diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index f172671cb00f..84a9b1a9577c 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -854,7 +854,6 @@ int ath10k_snoc_start_qmi_service(struct ath10k *ar) goto out_destroy_wq; } - atomic_set(&qmi_cfg->fw_ready, 1); ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI service started successfully\n"); return 0; diff --git a/drivers/net/wireless/ath/ath10k/qmi.h b/drivers/net/wireless/ath/ath10k/qmi.h index c8bc26bb96b2..29ad5acdf414 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.h +++ b/drivers/net/wireless/ath/ath10k/qmi.h @@ -18,7 +18,7 @@ #define ATH10K_SNOC_WLAN_FW_READY_TIMEOUT 8000 #define WLFW_SERVICE_INS_ID_V01 0 -#define WLFW_CLIENT_ID 0x4b4e454c +#define WLFW_CLIENT_ID 0x41544851 #define WLFW_TIMEOUT_MS 20000 enum ath10k_snoc_driver_event_type { diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index add0a7cd9edb..2cbc8ee9abf9 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -650,6 +650,9 @@ static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id, if (!ar_snoc) return -EINVAL; + if (atomic_read(&ar_snoc->fw_crashed)) + return -ESHUTDOWN; + snoc_pipe = &ar_snoc->pipe_info[pipe_id]; ce_pipe = snoc_pipe->ce_hdl; src_ring = ce_pipe->src_ring; diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 476521b77008..37898146f01d 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -1414,6 +1414,8 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy, wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); wil_set_recovery_state(wil, fw_recovery_idle); + set_bit(wil_status_resetting, wil->status); + mutex_lock(&wil->mutex); wmi_pcp_stop(wil); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index f82bb8964868..fca8acffeed5 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -190,6 +190,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) break; } sta->status = wil_sta_unused; + sta->fst_link_loss = false; } /* reorder buffers */ for (i = 0; i < WIL_STA_TID_NUM; i++) { diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index d472e13fb9d9..2a515e848820 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -26,6 +26,10 @@ static bool use_msi = true; module_param(use_msi, bool, 0444); MODULE_PARM_DESC(use_msi, " Use MSI interrupt, default - true"); +static bool ftm_mode; +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, @@ -36,13 +40,15 @@ static int wil6210_pm_notify(struct notifier_block *notify_block, static void wil_set_capabilities(struct wil6210_priv *wil) { + const char *wil_fw_name; u32 jtag_id = wil_r(wil, RGF_USER_JTAG_DEV_ID); u8 chip_revision = (wil_r(wil, RGF_USER_REVISION_ID) & RGF_USER_REVISION_ID_MASK); bitmap_zero(wil->hw_capabilities, hw_capability_last); bitmap_zero(wil->fw_capabilities, WMI_FW_CAPABILITY_MAX); - wil->wil_fw_name = WIL_FW_NAME_DEFAULT; + wil->wil_fw_name = ftm_mode ? WIL_FW_NAME_FTM_DEFAULT : + WIL_FW_NAME_DEFAULT; wil->chip_revision = chip_revision; switch (jtag_id) { @@ -51,9 +57,11 @@ void wil_set_capabilities(struct wil6210_priv *wil) case REVISION_ID_SPARROW_D0: wil->hw_name = "Sparrow D0"; wil->hw_version = HW_VER_SPARROW_D0; - if (wil_fw_verify_file_exists(wil, - WIL_FW_NAME_SPARROW_PLUS)) - wil->wil_fw_name = WIL_FW_NAME_SPARROW_PLUS; + wil_fw_name = ftm_mode ? WIL_FW_NAME_FTM_SPARROW_PLUS : + WIL_FW_NAME_SPARROW_PLUS; + + if (wil_fw_verify_file_exists(wil, wil_fw_name)) + wil->wil_fw_name = wil_fw_name; break; case REVISION_ID_SPARROW_B0: wil->hw_name = "Sparrow B0"; diff --git a/drivers/net/wireless/ath/wil6210/sysfs.c b/drivers/net/wireless/ath/wil6210/sysfs.c index a3689738a070..b4c4d09011b4 100644 --- a/drivers/net/wireless/ath/wil6210/sysfs.c +++ b/drivers/net/wireless/ath/wil6210/sysfs.c @@ -204,9 +204,74 @@ out: static DEVICE_ATTR(thermal_throttling, 0644, wil_tt_sysfs_show, wil_tt_sysfs_store); +static ssize_t +wil_fst_link_loss_sysfs_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct wil6210_priv *wil = dev_get_drvdata(dev); + ssize_t len = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(wil->sta); i++) + if (wil->sta[i].status == wil_sta_connected) + len += snprintf(buf + len, PAGE_SIZE - len, + "[%d] %pM %s\n", i, wil->sta[i].addr, + wil->sta[i].fst_link_loss ? + "On" : "Off"); + + return len; +} + +static ssize_t +wil_fst_link_loss_sysfs_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wil6210_priv *wil = dev_get_drvdata(dev); + u8 addr[ETH_ALEN]; + char *token, *dupbuf, *tmp; + int rc = -EINVAL; + bool fst_link_loss; + + tmp = kmemdup(buf, count + 1, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + tmp[count] = '\0'; + dupbuf = tmp; + + token = strsep(&dupbuf, " "); + if (!token) + goto out; + + /* mac address */ + if (sscanf(token, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &addr[0], &addr[1], &addr[2], + &addr[3], &addr[4], &addr[5]) != 6) + goto out; + + /* On/Off */ + if (strtobool(dupbuf, &fst_link_loss)) + goto out; + + wil_dbg_misc(wil, "set [%pM] with %d\n", addr, fst_link_loss); + + rc = wmi_link_maintain_cfg_write(wil, addr, fst_link_loss); + if (!rc) + rc = count; + +out: + kfree(tmp); + return rc; +} + +static DEVICE_ATTR(fst_link_loss, 0644, + wil_fst_link_loss_sysfs_show, + wil_fst_link_loss_sysfs_store); + static struct attribute *wil6210_sysfs_entries[] = { &dev_attr_ftm_txrx_offset.attr, &dev_attr_thermal_throttling.attr, + &dev_attr_fst_link_loss.attr, NULL }; diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index cf9ed143fdf4..0529d10a8268 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -37,8 +37,13 @@ extern bool debug_fw; extern bool disable_ap_sme; #define WIL_NAME "wil6210" -#define WIL_FW_NAME_DEFAULT "wil6210.fw" /* code Sparrow B0 */ -#define WIL_FW_NAME_SPARROW_PLUS "wil6210_sparrow_plus.fw" /* code Sparrow D0 */ + +#define WIL_FW_NAME_DEFAULT "wil6210.fw" +#define WIL_FW_NAME_FTM_DEFAULT "wil6210_ftm.fw" + +#define WIL_FW_NAME_SPARROW_PLUS "wil6210_sparrow_plus.fw" +#define WIL_FW_NAME_FTM_SPARROW_PLUS "wil6210_sparrow_plus_ftm.fw" + #define WIL_BOARD_FILE_NAME "wil6210.brd" /* board & radio parameters */ #define WIL_DEFAULT_BUS_REQUEST_KBPS 128000 /* ~1Gbps */ @@ -534,6 +539,7 @@ struct wil_sta_info { struct wil_tid_crypto_rx tid_crypto_rx[WIL_STA_TID_NUM]; struct wil_tid_crypto_rx group_crypto_rx; u8 aid; /* 1-254; 0 if unknown/not reported */ + bool fst_link_loss; }; enum { @@ -990,5 +996,9 @@ void wil_ftm_evt_per_dest_res(struct wil6210_priv *wil, void wil_aoa_evt_meas(struct wil6210_priv *wil, struct wmi_aoa_meas_event *evt, int len); +/* link loss */ +int wmi_link_maintain_cfg_write(struct wil6210_priv *wil, + const u8 *addr, + bool fst_link_loss); #endif /* __WIL6210_H__ */ diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 01a80a7b981e..41afbdc34c18 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -518,16 +518,16 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) assoc_resp_ielen = 0; } - mutex_lock(&wil->mutex); if (test_bit(wil_status_resetting, wil->status) || !test_bit(wil_status_fwready, wil->status)) { wil_err(wil, "status_resetting, cancel connect event, CID %d\n", evt->cid); - mutex_unlock(&wil->mutex); /* no need for cleanup, wil_reset will do that */ return; } + mutex_lock(&wil->mutex); + if ((wdev->iftype == NL80211_IFTYPE_STATION) || (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { if (!test_bit(wil_status_fwconnecting, wil->status)) { @@ -631,6 +631,13 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, wil->sinfo_gen++; + if (test_bit(wil_status_resetting, wil->status) || + !test_bit(wil_status_fwready, wil->status)) { + wil_err(wil, "status_resetting, cancel disconnect event\n"); + /* no need for cleanup, wil_reset will do that */ + return; + } + mutex_lock(&wil->mutex); wil6210_disconnect(wil, evt->bssid, reason_code, true); mutex_unlock(&wil->mutex); @@ -1846,6 +1853,61 @@ void wmi_event_flush(struct wil6210_priv *wil) spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); } +int wmi_link_maintain_cfg_write(struct wil6210_priv *wil, + const u8 *addr, + bool fst_link_loss) +{ + int rc; + int cid = wil_find_cid(wil, addr); + u32 cfg_type; + struct wmi_link_maintain_cfg_write_cmd cmd; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_link_maintain_cfg_write_done_event evt; + } __packed reply; + + if (cid < 0) + return cid; + + switch (wil->wdev->iftype) { + case NL80211_IFTYPE_STATION: + cfg_type = fst_link_loss ? + WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_FST_STA : + WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_NORMAL_STA; + break; + case NL80211_IFTYPE_AP: + cfg_type = fst_link_loss ? + WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_FST_AP : + WMI_LINK_MAINTAIN_CFG_TYPE_DEFAULT_NORMAL_AP; + break; + default: + wil_err(wil, "Unsupported for iftype %d", wil->wdev->iftype); + return -EINVAL; + } + + wil_dbg_misc(wil, "Setting cid:%d with cfg_type:%d\n", cid, cfg_type); + + cmd.cfg_type = cpu_to_le32(cfg_type); + cmd.cid = cpu_to_le32(cid); + + reply.evt.status = cpu_to_le32(WMI_FW_STATUS_FAILURE); + + rc = wmi_call(wil, WMI_LINK_MAINTAIN_CFG_WRITE_CMDID, &cmd, sizeof(cmd), + WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENTID, &reply, + sizeof(reply), 250); + if (rc) { + wil_err(wil, "Failed to %s FST link loss", + fst_link_loss ? "enable" : "disable"); + } else if (reply.evt.status == WMI_FW_STATUS_SUCCESS) { + wil->sta[cid].fst_link_loss = fst_link_loss; + } else { + wil_err(wil, "WMI_LINK_MAINTAIN_CFG_WRITE_CMDID returned status %d", + reply.evt.status); + rc = -EINVAL; + } + return rc; +} + static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id, void *d, int len) { diff --git a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c index e93416ebd343..09c37c2383c6 100644 --- a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c +++ b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c @@ -181,7 +181,6 @@ void *wcnss_prealloc_get(unsigned int size) pr_err("wcnss: %s: prealloc not available for size: %d\n", __func__, size); - WARN_ON(1); return NULL; } diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c index 3f9eeabc5464..505a9e016777 100644 --- a/drivers/net/wireless/wcnss/wcnss_wlan.c +++ b/drivers/net/wireless/wcnss/wcnss_wlan.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -192,6 +192,8 @@ static DEFINE_SPINLOCK(reg_spinlock); #define WCNSS_USR_WLAN_MAC_ADDR (WCNSS_USR_CTRL_MSG_START + 3) #define MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x" +#define SHOW_MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x\n" +#define WCNSS_USER_MAC_ADDR_LENGTH 18 /* message types */ #define WCNSS_CTRL_MSG_START 0x01000000 @@ -396,7 +398,6 @@ static struct { int user_cal_available; u32 user_cal_rcvd; int user_cal_exp_size; - int device_opened; int iris_xo_mode_set; int fw_vbatt_state; char wlan_nv_macAddr[WLAN_MAC_ADDR_SIZE]; @@ -427,23 +428,28 @@ static struct { static ssize_t wcnss_wlan_macaddr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - char macAddr[WLAN_MAC_ADDR_SIZE]; + int index; + int macAddr[WLAN_MAC_ADDR_SIZE]; if (!penv) return -ENODEV; - pr_debug("%s: Receive MAC Addr From user space: %s\n", __func__, buf); + if (strlen(buf) != WCNSS_USER_MAC_ADDR_LENGTH) { + dev_err(dev, "%s: Invalid MAC addr length\n", __func__); + return -EINVAL; + } if (WLAN_MAC_ADDR_SIZE != sscanf(buf, MAC_ADDRESS_STR, - (int *)&macAddr[0], (int *)&macAddr[1], - (int *)&macAddr[2], (int *)&macAddr[3], - (int *)&macAddr[4], (int *)&macAddr[5])) { - + &macAddr[0], &macAddr[1], &macAddr[2], + &macAddr[3], &macAddr[4], &macAddr[5])) { pr_err("%s: Failed to Copy MAC\n", __func__); return -EINVAL; } - memcpy(penv->wlan_nv_macAddr, macAddr, sizeof(penv->wlan_nv_macAddr)); + for (index = 0; index < WLAN_MAC_ADDR_SIZE; index++) { + memcpy(&penv->wlan_nv_macAddr[index], + (char *)&macAddr[index], sizeof(char)); + } pr_info("%s: Write MAC Addr:" MAC_ADDRESS_STR "\n", __func__, penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1], @@ -459,7 +465,7 @@ static ssize_t wcnss_wlan_macaddr_show(struct device *dev, if (!penv) return -ENODEV; - return scnprintf(buf, PAGE_SIZE, MAC_ADDRESS_STR, + return scnprintf(buf, PAGE_SIZE, SHOW_MAC_ADDRESS_STR, penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1], penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3], penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]); @@ -2038,21 +2044,23 @@ void extract_cal_data(int len) return; } + mutex_lock(&penv->dev_lock); 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"); + mutex_unlock(&penv->dev_lock); return; } if (penv->fw_cal_exp_frag != calhdr.frag_number) { pr_err("wcnss: Invalid frgament"); - goto exit; + goto unlock_exit; } if (calhdr.frag_size > WCNSS_MAX_FRAME_SIZE) { pr_err("wcnss: Invalid fragment size"); - goto exit; + goto unlock_exit; } if (penv->fw_cal_available) { @@ -2061,8 +2069,9 @@ void extract_cal_data(int len) penv->fw_cal_exp_frag++; if (calhdr.msg_flags & LAST_FRAGMENT) { penv->fw_cal_exp_frag = 0; - goto exit; + goto unlock_exit; } + mutex_unlock(&penv->dev_lock); return; } @@ -2070,7 +2079,7 @@ void extract_cal_data(int len) if (calhdr.total_size > MAX_CALIBRATED_DATA_SIZE) { pr_err("wcnss: Invalid cal data size %d", calhdr.total_size); - goto exit; + goto unlock_exit; } kfree(penv->fw_cal_data); penv->fw_cal_rcvd = 0; @@ -2078,11 +2087,10 @@ void extract_cal_data(int len) GFP_KERNEL); if (penv->fw_cal_data == NULL) { smd_read(penv->smd_ch, NULL, calhdr.frag_size); - goto exit; + goto unlock_exit; } } - mutex_lock(&penv->dev_lock); if (penv->fw_cal_rcvd + calhdr.frag_size > MAX_CALIBRATED_DATA_SIZE) { pr_err("calibrated data size is more than expected %d", @@ -2117,13 +2125,10 @@ void extract_cal_data(int len) unlock_exit: mutex_unlock(&penv->dev_lock); - -exit: wcnss_send_cal_rsp(fw_status); return; } - static void wcnssctrl_rx_handler(struct work_struct *worker) { int len = 0; @@ -3258,14 +3263,6 @@ static int wcnss_node_open(struct inode *inode, struct file *file) return -EFAULT; } - mutex_lock(&penv->dev_lock); - penv->user_cal_rcvd = 0; - penv->user_cal_read = 0; - penv->user_cal_available = false; - penv->user_cal_data = NULL; - penv->device_opened = 1; - mutex_unlock(&penv->dev_lock); - return rc; } @@ -3274,7 +3271,7 @@ static ssize_t wcnss_wlan_read(struct file *fp, char __user { int rc = 0; - if (!penv || !penv->device_opened) + if (!penv) return -EFAULT; rc = wait_event_interruptible(penv->read_wait, penv->fw_cal_rcvd @@ -3311,55 +3308,66 @@ static ssize_t wcnss_wlan_write(struct file *fp, const char __user *user_buffer, size_t count, loff_t *position) { int rc = 0; - u32 size = 0; + char *cal_data = NULL; - if (!penv || !penv->device_opened || penv->user_cal_available) + if (!penv || penv->user_cal_available) return -EFAULT; - if (penv->user_cal_rcvd == 0 && count >= 4 - && !penv->user_cal_data) { - rc = copy_from_user((void *)&size, user_buffer, 4); - if (!size || size > MAX_CALIBRATED_DATA_SIZE) { - pr_err(DEVICE " invalid size to write %d\n", size); + if (!penv->user_cal_rcvd && count >= 4 && !penv->user_cal_exp_size) { + mutex_lock(&penv->dev_lock); + rc = copy_from_user((void *)&penv->user_cal_exp_size, + 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); + penv->user_cal_exp_size = 0; + mutex_unlock(&penv->dev_lock); return -EFAULT; } - - rc += count; - count -= 4; - penv->user_cal_exp_size = size; - penv->user_cal_data = kmalloc(size, GFP_KERNEL); - if (penv->user_cal_data == NULL) { - pr_err(DEVICE " no memory to write\n"); - return -ENOMEM; - } - if (0 == count) - goto exit; - - } else if (penv->user_cal_rcvd == 0 && count < 4) + mutex_unlock(&penv->dev_lock); + return count; + } else if (!penv->user_cal_rcvd && count < 4) { return -EFAULT; + } + 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); - rc = -ENOMEM; - goto exit; + mutex_unlock(&penv->dev_lock); + return -ENOMEM; } - rc = copy_from_user((void *)penv->user_cal_data + - penv->user_cal_rcvd, user_buffer, count); - if (0 == rc) { + + cal_data = kmalloc(count, GFP_KERNEL); + if (!cal_data) { + mutex_unlock(&penv->dev_lock); + return -ENOMEM; + } + + rc = copy_from_user(cal_data, user_buffer, count); + if (!rc) { + memcpy(penv->user_cal_data + penv->user_cal_rcvd, + cal_data, count); penv->user_cal_rcvd += count; rc += count; } + + 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"); } + mutex_unlock(&penv->dev_lock); -exit: return rc; } +static int wcnss_node_release(struct inode *inode, struct file *file) +{ + return 0; +} static int wcnss_notif_cb(struct notifier_block *this, unsigned long code, void *ss_handle) @@ -3418,6 +3426,7 @@ static const struct file_operations wcnss_node_fops = { .open = wcnss_node_open, .read = wcnss_wlan_read, .write = wcnss_wlan_write, + .release = wcnss_node_release, }; static struct miscdevice wcnss_misc = { @@ -3445,6 +3454,13 @@ wcnss_wlan_probe(struct platform_device *pdev) } penv->pdev = 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"); + return -ENOMEM; + } + /* register sysfs entries */ ret = wcnss_create_sysfs(&pdev->dev); if (ret) { @@ -3465,6 +3481,11 @@ wcnss_wlan_probe(struct platform_device *pdev) mutex_init(&penv->pm_qos_mutex); init_waitqueue_head(&penv->read_wait); + penv->user_cal_rcvd = 0; + penv->user_cal_read = 0; + penv->user_cal_exp_size = 0; + penv->user_cal_available = false; + /* Since we were built into the kernel we'll be called as part * of kernel initialization. We don't know if userspace * applications are available to service PIL at this time diff --git a/drivers/parport/share.c b/drivers/parport/share.c index 5ce5ef211bdb..754f21fd9768 100644 --- a/drivers/parport/share.c +++ b/drivers/parport/share.c @@ -936,8 +936,10 @@ parport_register_dev_model(struct parport *port, const char *name, * pardevice fields. -arca */ port->ops->init_state(par_dev, par_dev->state); - port->proc_device = par_dev; - parport_device_proc_register(par_dev); + if (!test_and_set_bit(PARPORT_DEVPROC_REGISTERED, &port->devflags)) { + port->proc_device = par_dev; + parport_device_proc_register(par_dev); + } return par_dev; diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c index 9dc678ce4a48..f364882943e1 100644 --- a/drivers/pci/host/pci-msm.c +++ b/drivers/pci/host/pci-msm.c @@ -5811,9 +5811,10 @@ static int msm_pcie_map_qgic_addr(struct msm_pcie_dev_t *dev, struct msi_msg *msg) { struct iommu_domain *domain = iommu_get_domain_for_dev(&pdev->dev); - int ret, bypass_en = 0; + struct iommu_domain_geometry geometry; + int ret, fastmap_en = 0, bypass_en = 0; dma_addr_t iova; - phys_addr_t pcie_base_addr, gicm_db_offset; + phys_addr_t gicm_db_offset; msg->address_hi = 0; msg->address_lo = dev->msi_gicm_addr; @@ -5835,16 +5836,25 @@ static int msm_pcie_map_qgic_addr(struct msm_pcie_dev_t *dev, if (bypass_en) return 0; - gicm_db_offset = dev->msi_gicm_addr - - rounddown(dev->msi_gicm_addr, PAGE_SIZE); - /* - * Use PCIe DBI address as the IOVA since client cannot - * use this address for their IOMMU mapping. This will - * prevent any conflicts between PCIe host and - * client's mapping. - */ - pcie_base_addr = dev->res[MSM_PCIE_RES_DM_CORE].resource->start; - iova = rounddown(pcie_base_addr, PAGE_SIZE); + iommu_domain_get_attr(domain, DOMAIN_ATTR_FAST, &fastmap_en); + if (fastmap_en) { + iommu_domain_get_attr(domain, DOMAIN_ATTR_GEOMETRY, &geometry); + iova = geometry.aperture_start; + PCIE_DBG(dev, + "PCIe: RC%d: Use client's IOVA 0x%llx to map QGIC MSI address\n", + dev->rc_idx, iova); + } else { + phys_addr_t pcie_base_addr; + + /* + * Use PCIe DBI address as the IOVA since client cannot + * use this address for their IOMMU mapping. This will + * prevent any conflicts between PCIe host and + * client's mapping. + */ + pcie_base_addr = dev->res[MSM_PCIE_RES_DM_CORE].resource->start; + iova = rounddown(pcie_base_addr, PAGE_SIZE); + } ret = iommu_map(domain, iova, rounddown(dev->msi_gicm_addr, PAGE_SIZE), PAGE_SIZE, IOMMU_READ | IOMMU_WRITE); @@ -5855,6 +5865,8 @@ static int msm_pcie_map_qgic_addr(struct msm_pcie_dev_t *dev, return -ENOMEM; } + gicm_db_offset = dev->msi_gicm_addr - + rounddown(dev->msi_gicm_addr, PAGE_SIZE); msg->address_lo = iova + gicm_db_offset; return 0; diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 31f31d460fc9..357527712539 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -303,13 +303,6 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) return rc; } - pci_iov_set_numvfs(dev, nr_virtfn); - iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE; - pci_cfg_access_lock(dev); - pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); - msleep(100); - pci_cfg_access_unlock(dev); - iov->initial_VFs = initial; if (nr_virtfn < initial) initial = nr_virtfn; @@ -320,6 +313,13 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) goto err_pcibios; } + pci_iov_set_numvfs(dev, nr_virtfn); + iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE; + pci_cfg_access_lock(dev); + pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); + msleep(100); + pci_cfg_access_unlock(dev); + for (i = 0; i < initial; i++) { rc = virtfn_add(dev, i, 0); if (rc) @@ -555,21 +555,61 @@ void pci_iov_release(struct pci_dev *dev) } /** - * pci_iov_resource_bar - get position of the SR-IOV BAR + * pci_iov_update_resource - update a VF BAR * @dev: the PCI device * @resno: the resource number * - * Returns position of the BAR encapsulated in the SR-IOV capability. + * Update a VF BAR in the SR-IOV capability of a PF. */ -int pci_iov_resource_bar(struct pci_dev *dev, int resno) +void pci_iov_update_resource(struct pci_dev *dev, int resno) { - if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END) - return 0; + struct pci_sriov *iov = dev->is_physfn ? dev->sriov : NULL; + struct resource *res = dev->resource + resno; + int vf_bar = resno - PCI_IOV_RESOURCES; + struct pci_bus_region region; + u16 cmd; + u32 new; + int reg; + + /* + * The generic pci_restore_bars() path calls this for all devices, + * including VFs and non-SR-IOV devices. If this is not a PF, we + * have nothing to do. + */ + if (!iov) + return; + + pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &cmd); + if ((cmd & PCI_SRIOV_CTRL_VFE) && (cmd & PCI_SRIOV_CTRL_MSE)) { + dev_WARN(&dev->dev, "can't update enabled VF BAR%d %pR\n", + vf_bar, res); + return; + } + + /* + * Ignore unimplemented BARs, unused resource slots for 64-bit + * BARs, and non-movable resources, e.g., those described via + * Enhanced Allocation. + */ + if (!res->flags) + return; + + if (res->flags & IORESOURCE_UNSET) + return; + + if (res->flags & IORESOURCE_PCI_FIXED) + return; - BUG_ON(!dev->is_physfn); + pcibios_resource_to_bus(dev->bus, ®ion, res); + new = region.start; + new |= res->flags & ~PCI_BASE_ADDRESS_MEM_MASK; - return dev->sriov->pos + PCI_SRIOV_BAR + - 4 * (resno - PCI_IOV_RESOURCES); + reg = iov->pos + PCI_SRIOV_BAR + 4 * vf_bar; + pci_write_config_dword(dev, reg, new); + if (res->flags & IORESOURCE_MEM_64) { + new = region.start >> 16 >> 16; + pci_write_config_dword(dev, reg + 4, new); + } } resource_size_t __weak pcibios_iov_resource_alignment(struct pci_dev *dev, diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e311a9bf2c90..0e53488f8ec1 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -519,10 +519,6 @@ static void pci_restore_bars(struct pci_dev *dev) { int i; - /* Per SR-IOV spec 3.4.1.11, VF BARs are RO zero */ - if (dev->is_virtfn) - return; - for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) pci_update_resource(dev, i); } @@ -4472,36 +4468,6 @@ int pci_select_bars(struct pci_dev *dev, unsigned long flags) } EXPORT_SYMBOL(pci_select_bars); -/** - * pci_resource_bar - get position of the BAR associated with a resource - * @dev: the PCI device - * @resno: the resource number - * @type: the BAR type to be filled in - * - * Returns BAR position in config space, or 0 if the BAR is invalid. - */ -int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type) -{ - int reg; - - if (resno < PCI_ROM_RESOURCE) { - *type = pci_bar_unknown; - return PCI_BASE_ADDRESS_0 + 4 * resno; - } else if (resno == PCI_ROM_RESOURCE) { - *type = pci_bar_mem32; - return dev->rom_base_reg; - } else if (resno < PCI_BRIDGE_RESOURCES) { - /* device specific resource */ - *type = pci_bar_unknown; - reg = pci_iov_resource_bar(dev, resno); - if (reg) - return reg; - } - - dev_err(&dev->dev, "BAR %d: invalid resource\n", resno); - return 0; -} - /* Some architectures require additional programming to enable VGA */ static arch_set_vga_state_t arch_set_vga_state; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index d390fc1475ec..c43e448873ca 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -232,7 +232,6 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl, int pci_setup_device(struct pci_dev *dev); int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, struct resource *res, unsigned int reg); -int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type); void pci_configure_ari(struct pci_dev *dev); void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head); @@ -276,7 +275,7 @@ static inline void pci_restore_ats_state(struct pci_dev *dev) #ifdef CONFIG_PCI_IOV int pci_iov_init(struct pci_dev *dev); void pci_iov_release(struct pci_dev *dev); -int pci_iov_resource_bar(struct pci_dev *dev, int resno); +void pci_iov_update_resource(struct pci_dev *dev, int resno); resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno); void pci_restore_iov_state(struct pci_dev *dev); int pci_iov_bus_range(struct pci_bus *bus); @@ -290,10 +289,6 @@ static inline void pci_iov_release(struct pci_dev *dev) { } -static inline int pci_iov_resource_bar(struct pci_dev *dev, int resno) -{ - return 0; -} static inline void pci_restore_iov_state(struct pci_dev *dev) { } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 71d9a6d1bd56..b83df942794f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -226,7 +226,8 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, mask64 = (u32)PCI_BASE_ADDRESS_MEM_MASK; } } else { - res->flags |= (l & IORESOURCE_ROM_ENABLE); + if (l & PCI_ROM_ADDRESS_ENABLE) + res->flags |= IORESOURCE_ROM_ENABLE; l64 = l & PCI_ROM_ADDRESS_MASK; sz64 = sz & PCI_ROM_ADDRESS_MASK; mask64 = (u32)PCI_ROM_ADDRESS_MASK; diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index eb0ad530dc43..3eea7fc5e1a2 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -31,6 +31,11 @@ int pci_enable_rom(struct pci_dev *pdev) if (!res->flags) return -1; + /* + * Ideally pci_update_resource() would update the ROM BAR address, + * and we would only set the enable bit here. But apparently some + * devices have buggy ROM BARs that read as zero when disabled. + */ pcibios_resource_to_bus(pdev->bus, ®ion, res); pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr); rom_addr &= ~PCI_ROM_ADDRESS_MASK; diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 604011e047d6..25062966cbfa 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -25,21 +25,18 @@ #include <linux/slab.h> #include "pci.h" - -void pci_update_resource(struct pci_dev *dev, int resno) +static void pci_std_update_resource(struct pci_dev *dev, int resno) { struct pci_bus_region region; bool disable; u16 cmd; u32 new, check, mask; int reg; - enum pci_bar_type type; struct resource *res = dev->resource + resno; - if (dev->is_virtfn) { - dev_warn(&dev->dev, "can't update VF BAR%d\n", resno); + /* Per SR-IOV spec 3.4.1.11, VF BARs are RO zero */ + if (dev->is_virtfn) return; - } /* * Ignore resources for unimplemented BARs and unused resource slots @@ -60,21 +57,34 @@ void pci_update_resource(struct pci_dev *dev, int resno) return; pcibios_resource_to_bus(dev->bus, ®ion, res); + new = region.start; - new = region.start | (res->flags & PCI_REGION_FLAG_MASK); - if (res->flags & IORESOURCE_IO) + if (res->flags & IORESOURCE_IO) { mask = (u32)PCI_BASE_ADDRESS_IO_MASK; - else + new |= res->flags & ~PCI_BASE_ADDRESS_IO_MASK; + } else if (resno == PCI_ROM_RESOURCE) { + mask = (u32)PCI_ROM_ADDRESS_MASK; + } else { mask = (u32)PCI_BASE_ADDRESS_MEM_MASK; + new |= res->flags & ~PCI_BASE_ADDRESS_MEM_MASK; + } - reg = pci_resource_bar(dev, resno, &type); - if (!reg) - return; - if (type != pci_bar_unknown) { + if (resno < PCI_ROM_RESOURCE) { + reg = PCI_BASE_ADDRESS_0 + 4 * resno; + } else if (resno == PCI_ROM_RESOURCE) { + + /* + * Apparently some Matrox devices have ROM BARs that read + * as zero when disabled, so don't update ROM BARs unless + * they're enabled. See https://lkml.org/lkml/2005/8/30/138. + */ if (!(res->flags & IORESOURCE_ROM_ENABLE)) return; + + reg = dev->rom_base_reg; new |= PCI_ROM_ADDRESS_ENABLE; - } + } else + return; /* * We can't update a 64-bit BAR atomically, so when possible, @@ -110,6 +120,16 @@ void pci_update_resource(struct pci_dev *dev, int resno) pci_write_config_word(dev, PCI_COMMAND, cmd); } +void pci_update_resource(struct pci_dev *dev, int resno) +{ + if (resno <= PCI_ROM_RESOURCE) + pci_std_update_resource(dev, resno); +#ifdef CONFIG_PCI_IOV + else if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END) + pci_iov_update_resource(dev, resno); +#endif +} + int pci_claim_resource(struct pci_dev *dev, int resource) { struct resource *res = &dev->resource[resource]; diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 4c75b4d392c6..39400dda27c2 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -367,6 +367,8 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu) return err; } + armpmu->pmu_state = ARM_PMU_STATE_RUNNING; + return 0; } @@ -601,10 +603,12 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu) struct platform_device *pmu_device = cpu_pmu->plat_device; struct pmu_hw_events __percpu *hw_events = cpu_pmu->hw_events; + cpu_pmu->pmu_state = ARM_PMU_STATE_GOING_DOWN; + irqs = min(pmu_device->num_resources, num_possible_cpus()); irq = platform_get_irq(pmu_device, 0); - if (irq >= 0 && irq_is_percpu(irq)) { + if (irq > 0 && irq_is_percpu(irq)) { on_each_cpu(cpu_pmu_disable_percpu_irq, &irq, 1); free_percpu_irq(irq, &hw_events->percpu_pmu); } else { @@ -617,10 +621,11 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu) if (!cpumask_test_and_clear_cpu(cpu, &cpu_pmu->active_irqs)) continue; irq = platform_get_irq(pmu_device, i); - if (irq >= 0) + if (irq > 0) free_irq(irq, per_cpu_ptr(&hw_events->percpu_pmu, cpu)); } } + cpu_pmu->pmu_state = ARM_PMU_STATE_OFF; } static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler) @@ -639,7 +644,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler) } irq = platform_get_irq(pmu_device, 0); - if (irq >= 0 && irq_is_percpu(irq)) { + if (irq > 0 && irq_is_percpu(irq)) { err = request_percpu_irq(irq, handler, "arm-pmu", &hw_events->percpu_pmu); if (err) { @@ -648,6 +653,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler) return err; } on_each_cpu(cpu_pmu_enable_percpu_irq, &irq, 1); + cpu_pmu->percpu_irq = irq; } else { for (i = 0; i < irqs; ++i) { int cpu = i; @@ -754,13 +760,6 @@ static void cpu_pm_pmu_common(void *info) return; } - /* - * Always reset the PMU registers on power-up even if - * there are no events running. - */ - if (cmd == CPU_PM_EXIT && armpmu->reset) - armpmu->reset(armpmu); - if (!enabled) { data->ret = NOTIFY_OK; return; @@ -795,6 +794,13 @@ static int cpu_pm_pmu_notify(struct notifier_block *b, unsigned long cmd, .cpu = smp_processor_id(), }; + /* + * Always reset the PMU registers on power-up even if + * there are no events running. + */ + if (cmd == CPU_PM_EXIT && data.armpmu->reset) + data.armpmu->reset(data.armpmu); + cpu_pm_pmu_common(&data); return data.ret; } @@ -824,6 +830,7 @@ static inline void cpu_pm_pmu_common(void *info) { } static int cpu_pmu_notify(struct notifier_block *b, unsigned long action, void *hcpu) { + int irq = -1; unsigned long masked_action = (action & ~CPU_TASKS_FROZEN); struct cpu_pm_pmu_args data = { .armpmu = container_of(b, struct arm_pmu, hotplug_nb), @@ -835,37 +842,37 @@ static int cpu_pmu_notify(struct notifier_block *b, unsigned long action, switch (masked_action) { case CPU_STARTING: - data.cmd = CPU_PM_EXIT; - break; - case CPU_DYING: - data.cmd = CPU_PM_ENTER; - break; case CPU_DOWN_FAILED: - data.cmd = CPU_PM_ENTER_FAILED; - break; - case CPU_ONLINE: - if (data.armpmu->plat_device) { - struct platform_device *pmu_device = - data.armpmu->plat_device; - int irq = platform_get_irq(pmu_device, 0); - - if (irq >= 0 && irq_is_percpu(irq)) { - smp_call_function_single(data.cpu, - cpu_pmu_enable_percpu_irq, &irq, 1); - } + /* + * Always reset the PMU registers on power-up even if + * there are no events running. + */ + if (data.armpmu->reset) + data.armpmu->reset(data.armpmu); + if (data.armpmu->pmu_state == ARM_PMU_STATE_RUNNING) { + if (data.armpmu->plat_device) + irq = data.armpmu->percpu_irq; + /* Arm the PMU IRQ before appearing. */ + if (irq > 0 && irq_is_percpu(irq)) + cpu_pmu_enable_percpu_irq(&irq); + data.cmd = CPU_PM_EXIT; + cpu_pm_pmu_common(&data); } - return NOTIFY_DONE; + return NOTIFY_OK; + case CPU_DYING: + if (data.armpmu->pmu_state != ARM_PMU_STATE_OFF) { + data.cmd = CPU_PM_ENTER; + cpu_pm_pmu_common(&data); + /* Disarm the PMU IRQ before disappearing. */ + if (data.armpmu->plat_device) + irq = data.armpmu->percpu_irq; + if (irq > 0 && irq_is_percpu(irq)) + cpu_pmu_disable_percpu_irq(&irq); + } + return NOTIFY_OK; default: return NOTIFY_DONE; } - - if (smp_processor_id() == data.cpu) - cpu_pm_pmu_common(&data); - else - smp_call_function_single(data.cpu, - cpu_pm_pmu_common, &data, 1); - - return data.ret; } static int cpu_pmu_init(struct arm_pmu *cpu_pmu) @@ -966,7 +973,7 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu) /* Check the IRQ type and prohibit a mix of PPIs and SPIs */ irq = platform_get_irq(pdev, i); - if (irq >= 0) { + if (irq > 0) { bool spi = !irq_is_percpu(irq); if (i > 0 && spi != using_spi) { @@ -1085,6 +1092,9 @@ int arm_pmu_device_probe(struct platform_device *pdev, if (ret) goto out_destroy; + pmu->pmu_state = ARM_PMU_STATE_OFF; + pmu->percpu_irq = -1; + pr_info("enabled with %s PMU driver, %d counters available\n", pmu->name, pmu->num_events); diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index a009ae34c5ef..930f0f25c1ce 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -1466,12 +1466,11 @@ static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq) offset += range->npins; } - /* Mask and clear all interrupts */ - chv_writel(0, pctrl->regs + CHV_INTMASK); + /* Clear all interrupts */ chv_writel(0xffff, pctrl->regs + CHV_INTSTAT); ret = gpiochip_irqchip_add(chip, &chv_gpio_irqchip, 0, - handle_simple_irq, IRQ_TYPE_NONE); + handle_bad_irq, IRQ_TYPE_NONE); if (ret) { dev_err(pctrl->dev, "failed to add IRQ chip\n"); goto fail; diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index c1448955d3ed..c5a351e7bb4e 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -602,10 +602,6 @@ static void msm_gpio_irq_unmask(struct irq_data *d) spin_lock_irqsave(&pctrl->lock, flags); - val = readl(pctrl->regs + g->intr_status_reg); - val &= ~BIT(g->intr_status_bit); - writel(val, pctrl->regs + g->intr_status_reg); - val = readl(pctrl->regs + g->intr_cfg_reg); val |= BIT(g->intr_enable_bit); writel(val, pctrl->regs + g->intr_cfg_reg); diff --git a/drivers/platform/msm/gsi/gsi_dbg.c b/drivers/platform/msm/gsi/gsi_dbg.c index 717c891788f2..eaf50ca1cea5 100644 --- a/drivers/platform/msm/gsi/gsi_dbg.c +++ b/drivers/platform/msm/gsi/gsi_dbg.c @@ -29,6 +29,7 @@ static struct dentry *dent; static char dbg_buff[4096]; +static void *gsi_ipc_logbuf_low; static void gsi_wq_print_dp_stats(struct work_struct *work); static DECLARE_DELAYED_WORK(gsi_print_dp_stats_work, gsi_wq_print_dp_stats); @@ -764,22 +765,20 @@ static ssize_t gsi_enable_ipc_low(struct file *file, if (kstrtos8(dbg_buff, 0, &option)) return -EFAULT; + mutex_lock(&gsi_ctx->mlock); if (option) { - if (!gsi_ctx->ipc_logbuf_low) { - gsi_ctx->ipc_logbuf_low = + if (!gsi_ipc_logbuf_low) { + gsi_ipc_logbuf_low = ipc_log_context_create(GSI_IPC_LOG_PAGES, "gsi_low", 0); + if (gsi_ipc_logbuf_low == NULL) + TERR("failed to get ipc_logbuf_low\n"); } - - if (gsi_ctx->ipc_logbuf_low == NULL) { - TERR("failed to get ipc_logbuf_low\n"); - return -EFAULT; - } + gsi_ctx->ipc_logbuf_low = gsi_ipc_logbuf_low; } else { - if (gsi_ctx->ipc_logbuf_low) - ipc_log_context_destroy(gsi_ctx->ipc_logbuf_low); gsi_ctx->ipc_logbuf_low = NULL; } + mutex_unlock(&gsi_ctx->mlock); return count; } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c index 100bbd582a5e..f01743d04e84 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c @@ -1584,6 +1584,7 @@ static int ipa_init_smem_region(int memory_region_size, struct ipa_hw_imm_cmd_dma_shared_mem *cmd = NULL; struct ipa_desc desc; struct ipa_mem_buffer mem; + gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0); int rc; if (memory_region_size == 0) @@ -1603,7 +1604,7 @@ static int ipa_init_smem_region(int memory_region_size, memset(mem.base, 0, mem.size); cmd = kzalloc(sizeof(*cmd), - GFP_KERNEL); + flag); if (cmd == NULL) { IPAERR("Failed to alloc immediate command object\n"); rc = -ENOMEM; @@ -2166,6 +2167,7 @@ int _ipa_init_sram_v2(void) struct ipa_hw_imm_cmd_dma_shared_mem *cmd = NULL; struct ipa_desc desc = {0}; struct ipa_mem_buffer mem; + gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0); int rc = 0; phys_addr = ipa_ctx->ipa_wrapper_base + @@ -2203,7 +2205,7 @@ int _ipa_init_sram_v2(void) } memset(mem.base, 0, mem.size); - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + cmd = kzalloc(sizeof(*cmd), flag); if (cmd == NULL) { IPAERR("Failed to alloc immediate command object\n"); rc = -ENOMEM; @@ -2314,6 +2316,7 @@ int _ipa_init_hdr_v2(void) struct ipa_desc desc = { 0 }; struct ipa_mem_buffer mem; struct ipa_hdr_init_local *cmd = NULL; + gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0); int rc = 0; mem.size = IPA_MEM_PART(modem_hdr_size) + IPA_MEM_PART(apps_hdr_size); @@ -2325,7 +2328,7 @@ int _ipa_init_hdr_v2(void) } memset(mem.base, 0, mem.size); - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + cmd = kzalloc(sizeof(*cmd), flag); if (cmd == NULL) { IPAERR("Failed to alloc header init command object\n"); rc = -ENOMEM; @@ -2360,6 +2363,7 @@ int _ipa_init_hdr_v2_5(void) struct ipa_mem_buffer mem; struct ipa_hdr_init_local *cmd = NULL; struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd = NULL; + gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0); mem.size = IPA_MEM_PART(modem_hdr_size) + IPA_MEM_PART(apps_hdr_size); mem.base = dma_alloc_coherent(ipa_ctx->pdev, mem.size, &mem.phys_base, @@ -2370,7 +2374,7 @@ int _ipa_init_hdr_v2_5(void) } memset(mem.base, 0, mem.size); - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + cmd = kzalloc(sizeof(*cmd), flag); if (cmd == NULL) { IPAERR("Failed to alloc header init command object\n"); dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, @@ -2411,7 +2415,7 @@ int _ipa_init_hdr_v2_5(void) memset(mem.base, 0, mem.size); memset(&desc, 0, sizeof(desc)); - dma_cmd = kzalloc(sizeof(*dma_cmd), GFP_KERNEL); + dma_cmd = kzalloc(sizeof(*dma_cmd), flag); if (dma_cmd == NULL) { IPAERR("Failed to alloc immediate command object\n"); dma_free_coherent(ipa_ctx->pdev, @@ -2462,6 +2466,7 @@ int _ipa_init_rt4_v2(void) struct ipa_desc desc = { 0 }; struct ipa_mem_buffer mem; struct ipa_ip_v4_routing_init *v4_cmd = NULL; + gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0); u32 *entry; int i; int rc = 0; @@ -2486,7 +2491,7 @@ int _ipa_init_rt4_v2(void) entry++; } - v4_cmd = kzalloc(sizeof(*v4_cmd), GFP_KERNEL); + v4_cmd = kzalloc(sizeof(*v4_cmd), flag); if (v4_cmd == NULL) { IPAERR("Failed to alloc v4 routing init command object\n"); rc = -ENOMEM; @@ -2522,6 +2527,7 @@ int _ipa_init_rt6_v2(void) struct ipa_desc desc = { 0 }; struct ipa_mem_buffer mem; struct ipa_ip_v6_routing_init *v6_cmd = NULL; + gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0); u32 *entry; int i; int rc = 0; @@ -2546,7 +2552,7 @@ int _ipa_init_rt6_v2(void) entry++; } - v6_cmd = kzalloc(sizeof(*v6_cmd), GFP_KERNEL); + v6_cmd = kzalloc(sizeof(*v6_cmd), flag); if (v6_cmd == NULL) { IPAERR("Failed to alloc v6 routing init command object\n"); rc = -ENOMEM; @@ -2582,6 +2588,7 @@ int _ipa_init_flt4_v2(void) struct ipa_desc desc = { 0 }; struct ipa_mem_buffer mem; struct ipa_ip_v4_filter_init *v4_cmd = NULL; + gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0); u32 *entry; int i; int rc = 0; @@ -2604,7 +2611,7 @@ int _ipa_init_flt4_v2(void) entry++; } - v4_cmd = kzalloc(sizeof(*v4_cmd), GFP_KERNEL); + v4_cmd = kzalloc(sizeof(*v4_cmd), flag); if (v4_cmd == NULL) { IPAERR("Failed to alloc v4 fliter init command object\n"); rc = -ENOMEM; @@ -2640,6 +2647,7 @@ int _ipa_init_flt6_v2(void) struct ipa_desc desc = { 0 }; struct ipa_mem_buffer mem; struct ipa_ip_v6_filter_init *v6_cmd = NULL; + gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0); u32 *entry; int i; int rc = 0; @@ -2662,7 +2670,7 @@ int _ipa_init_flt6_v2(void) entry++; } - v6_cmd = kzalloc(sizeof(*v6_cmd), GFP_KERNEL); + v6_cmd = kzalloc(sizeof(*v6_cmd), flag); if (v6_cmd == NULL) { IPAERR("Failed to alloc v6 fliter init command object\n"); rc = -ENOMEM; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c index 8d2213f30ec2..e48c19c522a7 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c @@ -123,6 +123,7 @@ static struct dentry *dfile_ipa_poll_iteration; static char dbg_buff[IPA_MAX_MSG_LEN]; static char *active_clients_buf; static s8 ep_reg_idx; +static void *ipa_ipc_low_buff; int _ipa_read_gen_reg_v1_1(char *buff, int max_len) { @@ -1823,23 +1824,20 @@ static ssize_t ipa_enable_ipc_low(struct file *file, if (kstrtos8(dbg_buff, 0, &option)) return -EFAULT; + mutex_lock(&ipa_ctx->lock); if (option) { - if (!ipa_ctx->logbuf_low) { - ipa_ctx->logbuf_low = + if (!ipa_ipc_low_buff) { + ipa_ipc_low_buff = ipc_log_context_create(IPA_IPC_LOG_PAGES, "ipa_low", 0); + if (ipa_ipc_low_buff == NULL) + IPAERR("failed to get logbuf_low\n"); } - - if (ipa_ctx->logbuf_low == NULL) { - IPAERR("failed to get logbuf_low\n"); - return -EFAULT; - } - + ipa_ctx->logbuf_low = ipa_ipc_low_buff; } else { - if (ipa_ctx->logbuf_low) - ipc_log_context_destroy(ipa_ctx->logbuf_low); - ipa_ctx->logbuf_low = NULL; + ipa_ctx->logbuf_low = NULL; } + mutex_unlock(&ipa_ctx->lock); return count; } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c index 25364e8efa38..23c8a5059c3b 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c @@ -322,8 +322,8 @@ int ipa_send_one(struct ipa_sys_context *sys, struct ipa_desc *desc, dma_address = desc->dma_address; tx_pkt->no_unmap_dma = true; } - if (!dma_address) { - IPAERR("failed to DMA wrap\n"); + if (dma_mapping_error(ipa_ctx->pdev, dma_address)) { + IPAERR("dma_map_single failed\n"); goto fail_dma_map; } @@ -445,7 +445,7 @@ int ipa_send(struct ipa_sys_context *sys, u32 num_desc, struct ipa_desc *desc, } dma_addr = dma_map_single(ipa_ctx->pdev, transfer.iovec, size, DMA_TO_DEVICE); - if (!dma_addr) { + if (dma_mapping_error(ipa_ctx->pdev, dma_addr)) { IPAERR("dma_map_single failed for sps xfr buff\n"); kfree(transfer.iovec); return -EFAULT; @@ -493,6 +493,15 @@ int ipa_send(struct ipa_sys_context *sys, u32 num_desc, struct ipa_desc *desc, tx_pkt->mem.base, tx_pkt->mem.size, DMA_TO_DEVICE); + + if (dma_mapping_error(ipa_ctx->pdev, + tx_pkt->mem.phys_base)) { + IPAERR("dma_map_single "); + IPAERR("failed\n"); + fail_dma_wrap = 1; + goto failure; + } + } else { tx_pkt->mem.phys_base = desc[i].dma_address; tx_pkt->no_unmap_dma = true; @@ -1874,8 +1883,8 @@ begin: rx_pkt->data.dma_addr = dma_map_single(ipa_ctx->pdev, ptr, sys->rx_buff_sz, DMA_FROM_DEVICE); - if (rx_pkt->data.dma_addr == 0 || - rx_pkt->data.dma_addr == ~0) { + if (dma_mapping_error(ipa_ctx->pdev, + rx_pkt->data.dma_addr)) { pr_err_ratelimited("%s dma map fail %p for %p sys=%p\n", __func__, (void *)rx_pkt->data.dma_addr, ptr, sys); @@ -2030,18 +2039,20 @@ static void ipa_alloc_wlan_rx_common_cache(u32 size) ptr = skb_put(rx_pkt->data.skb, IPA_WLAN_RX_BUFF_SZ); rx_pkt->data.dma_addr = dma_map_single(ipa_ctx->pdev, ptr, IPA_WLAN_RX_BUFF_SZ, DMA_FROM_DEVICE); - if (rx_pkt->data.dma_addr == 0 || - rx_pkt->data.dma_addr == ~0) { + if (dma_mapping_error(ipa_ctx->pdev, + rx_pkt->data.dma_addr)) { IPAERR("dma_map_single failure %p for %p\n", (void *)rx_pkt->data.dma_addr, ptr); goto fail_dma_mapping; } + spin_lock_bh(&ipa_ctx->wc_memb.wlan_spinlock); list_add_tail(&rx_pkt->link, &ipa_ctx->wc_memb.wlan_comm_desc_list); rx_len_cached = ++ipa_ctx->wc_memb.wlan_comm_total_cnt; ipa_ctx->wc_memb.wlan_comm_free_cnt++; + spin_unlock_bh(&ipa_ctx->wc_memb.wlan_spinlock); } @@ -2102,8 +2113,8 @@ static void ipa_replenish_rx_cache(struct ipa_sys_context *sys) rx_pkt->data.dma_addr = dma_map_single(ipa_ctx->pdev, ptr, sys->rx_buff_sz, DMA_FROM_DEVICE); - if (rx_pkt->data.dma_addr == 0 || - rx_pkt->data.dma_addr == ~0) { + if (dma_mapping_error(ipa_ctx->pdev, + rx_pkt->data.dma_addr)) { IPAERR("dma_map_single failure %p for %p\n", (void *)rx_pkt->data.dma_addr, ptr); goto fail_dma_mapping; @@ -2160,9 +2171,10 @@ static void ipa_replenish_rx_cache_recycle(struct ipa_sys_context *sys) ptr = skb_put(rx_pkt->data.skb, sys->rx_buff_sz); rx_pkt->data.dma_addr = dma_map_single(ipa_ctx->pdev, ptr, sys->rx_buff_sz, DMA_FROM_DEVICE); - if (rx_pkt->data.dma_addr == 0 || - rx_pkt->data.dma_addr == ~0) + if (dma_mapping_error(ipa_ctx->pdev, rx_pkt->data.dma_addr)) { + IPAERR("dma_map_single failure for rx_pkt\n"); goto fail_dma_mapping; + } list_add_tail(&rx_pkt->link, &sys->head_desc_list); rx_len_cached = ++sys->len; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c index e23de3f26613..f43981f15c31 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c @@ -268,6 +268,7 @@ int __ipa_commit_hdr_v2(void) struct ipa_mem_buffer mem; struct ipa_hdr_init_system *cmd = NULL; struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd = NULL; + gfp_t flag = GFP_ATOMIC | (ipa_ctx->use_dma_zone ? GFP_DMA : 0); int rc = -EFAULT; if (ipa_generate_hdr_hw_tbl(&mem)) { @@ -281,7 +282,7 @@ int __ipa_commit_hdr_v2(void) IPA_MEM_PART(apps_hdr_size)); goto fail_send_cmd; } else { - dma_cmd = kzalloc(sizeof(*dma_cmd), GFP_ATOMIC); + dma_cmd = kzalloc(sizeof(*dma_cmd), flag); if (dma_cmd == NULL) { IPAERR("fail to alloc immediate cmd\n"); rc = -ENOMEM; @@ -303,7 +304,7 @@ int __ipa_commit_hdr_v2(void) IPA_MEM_PART(apps_hdr_size_ddr)); goto fail_send_cmd; } else { - cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC); + cmd = kzalloc(sizeof(*cmd), flag); if (cmd == NULL) { IPAERR("fail to alloc hdr init cmd\n"); rc = -ENOMEM; @@ -359,6 +360,7 @@ int __ipa_commit_hdr_v2_5(void) struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd_hdr = NULL; struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd_ctx = NULL; struct ipa_register_write *reg_write_cmd = NULL; + gfp_t flag = GFP_ATOMIC | (ipa_ctx->use_dma_zone ? GFP_DMA : 0); int rc = -EFAULT; u32 proc_ctx_size; u32 proc_ctx_ofst; @@ -383,7 +385,7 @@ int __ipa_commit_hdr_v2_5(void) IPA_MEM_PART(apps_hdr_size)); goto fail_send_cmd1; } else { - dma_cmd_hdr = kzalloc(sizeof(*dma_cmd_hdr), GFP_ATOMIC); + dma_cmd_hdr = kzalloc(sizeof(*dma_cmd_hdr), flag); if (dma_cmd_hdr == NULL) { IPAERR("fail to alloc immediate cmd\n"); rc = -ENOMEM; @@ -406,7 +408,7 @@ int __ipa_commit_hdr_v2_5(void) goto fail_send_cmd1; } else { hdr_init_cmd = kzalloc(sizeof(*hdr_init_cmd), - GFP_ATOMIC); + flag); if (hdr_init_cmd == NULL) { IPAERR("fail to alloc immediate cmd\n"); rc = -ENOMEM; @@ -431,7 +433,7 @@ int __ipa_commit_hdr_v2_5(void) goto fail_send_cmd1; } else { dma_cmd_ctx = kzalloc(sizeof(*dma_cmd_ctx), - GFP_ATOMIC); + flag); if (dma_cmd_ctx == NULL) { IPAERR("fail to alloc immediate cmd\n"); rc = -ENOMEM; @@ -456,7 +458,7 @@ int __ipa_commit_hdr_v2_5(void) goto fail_send_cmd1; } else { reg_write_cmd = kzalloc(sizeof(*reg_write_cmd), - GFP_ATOMIC); + flag); if (reg_write_cmd == NULL) { IPAERR("fail to alloc immediate cmd\n"); rc = -ENOMEM; @@ -722,6 +724,11 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr) entry->hdr, entry->hdr_len, DMA_TO_DEVICE); + if (dma_mapping_error(ipa_ctx->pdev, + entry->phys_base)) { + IPAERR("dma_map_single failure for entry\n"); + goto fail_dma_mapping; + } } } else { entry->is_hdr_proc_ctx = false; @@ -798,6 +805,8 @@ fail_add_proc_ctx: list_del(&entry->link); dma_unmap_single(ipa_ctx->pdev, entry->phys_base, entry->hdr_len, DMA_TO_DEVICE); +fail_dma_mapping: + entry->is_hdr_proc_ctx = false; bad_hdr_len: entry->cookie = 0; kmem_cache_free(ipa_ctx->hdr_cache, entry); diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c index f5dea76764f8..4b6bc5b61bfc 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -698,6 +698,7 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip) struct ipa_mem_buffer head; struct ipa_hw_imm_cmd_dma_shared_mem *cmd1 = NULL; struct ipa_hw_imm_cmd_dma_shared_mem *cmd2 = NULL; + gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0); u16 avail; u32 num_modem_rt_index; int rc = 0; @@ -748,7 +749,7 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip) } cmd1 = kzalloc(sizeof(struct ipa_hw_imm_cmd_dma_shared_mem), - GFP_KERNEL); + flag); if (cmd1 == NULL) { IPAERR("Failed to alloc immediate command object\n"); rc = -ENOMEM; @@ -765,7 +766,7 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip) if (lcl) { cmd2 = kzalloc(sizeof(struct ipa_hw_imm_cmd_dma_shared_mem), - GFP_KERNEL); + flag); if (cmd2 == NULL) { IPAERR("Failed to alloc immediate command object\n"); rc = -ENOMEM; @@ -1353,6 +1354,10 @@ int ipa2_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup) mutex_lock(&ipa_ctx->lock); entry = __ipa_find_rt_tbl(lookup->ip, lookup->name); if (entry && entry->cookie == IPA_COOKIE) { + if (entry->ref_cnt == U32_MAX) { + IPAERR("fail: ref count crossed limit\n"); + goto ret; + } entry->ref_cnt++; lookup->hdl = entry->id; @@ -1362,6 +1367,8 @@ int ipa2_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup) result = 0; } + +ret: mutex_unlock(&ipa_ctx->lock); return result; diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index 246f6b68b839..774077494ea5 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -1542,6 +1542,9 @@ static int ipa_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) memcpy(mux_channel[rmnet_index].vchannel_name, extend_ioctl_data.u.rmnet_mux_val.vchannel_name, sizeof(mux_channel[rmnet_index].vchannel_name)); + mux_channel[rmnet_index].vchannel_name[ + IFNAMSIZ - 1] = '\0'; + IPAWANDBG("cashe device[%s:%d] in IPA_wan[%d]\n", mux_channel[rmnet_index].vchannel_name, mux_channel[rmnet_index].mux_id, diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index ddff50834f03..5ee6e5d2d9e3 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -2276,6 +2276,36 @@ static int ipa3_q6_set_ex_path_to_apps(void) desc[num_descs].len = cmd_pyld->len; num_descs++; } + + /* disable statuses for modem producers */ + if (IPA_CLIENT_IS_Q6_PROD(client_idx)) { + ipa_assert_on(num_descs >= ipa3_ctx->ipa_num_pipes); + + reg_write.skip_pipeline_clear = false; + reg_write.pipeline_clear_options = + IPAHAL_HPS_CLEAR; + reg_write.offset = + ipahal_get_reg_n_ofst(IPA_ENDP_STATUS_n, + ep_idx); + reg_write.value = 0; + reg_write.value_mask = ~0; + cmd_pyld = ipahal_construct_imm_cmd( + IPA_IMM_CMD_REGISTER_WRITE, ®_write, false); + if (!cmd_pyld) { + IPAERR("fail construct register_write cmd\n"); + ipa_assert(); + return -EFAULT; + } + + desc[num_descs].opcode = ipahal_imm_cmd_get_opcode( + IPA_IMM_CMD_REGISTER_WRITE); + desc[num_descs].type = IPA_IMM_CMD_DESC; + desc[num_descs].callback = ipa3_destroy_imm; + desc[num_descs].user1 = cmd_pyld; + desc[num_descs].pyld = cmd_pyld->data; + desc[num_descs].len = cmd_pyld->len; + num_descs++; + } } /* Will wait 500msecs for IPA tag process completion */ @@ -4036,6 +4066,7 @@ fail_register_device: unregister_chrdev_region(ipa3_ctx->dev_num, 1); if (ipa3_ctx->pipe_mem_pool) gen_pool_destroy(ipa3_ctx->pipe_mem_pool); + ipa3_free_dma_task_for_gsi(); ipa3_destroy_flt_tbl_idrs(); idr_destroy(&ipa3_ctx->ipa_idr); kmem_cache_destroy(ipa3_ctx->rx_pkt_wrapper_cache); @@ -4551,6 +4582,13 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, goto fail_dma_pool; } + /* allocate memory for DMA_TASK workaround */ + result = ipa3_allocate_dma_task_for_gsi(); + if (result) { + IPAERR("failed to allocate dma task\n"); + goto fail_dma_task; + } + /* init the various list heads */ INIT_LIST_HEAD(&ipa3_ctx->hdr_tbl.head_hdr_entry_list); for (i = 0; i < IPA_HDR_BIN_MAX; i++) { @@ -4723,6 +4761,8 @@ fail_cdev_add: fail_device_create: unregister_chrdev_region(ipa3_ctx->dev_num, 1); fail_alloc_chrdev_region: + ipa3_free_dma_task_for_gsi(); +fail_dma_task: if (ipa3_ctx->pipe_mem_pool) gen_pool_destroy(ipa3_ctx->pipe_mem_pool); ipa3_destroy_flt_tbl_idrs(); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c index 17e6973b807f..72eb3808c7a4 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c @@ -106,6 +106,7 @@ static char dbg_buff[IPA_MAX_MSG_LEN]; static char *active_clients_buf; static s8 ep_reg_idx; +static void *ipa_ipc_low_buff; static ssize_t ipa3_read_gen_reg(struct file *file, char __user *ubuf, @@ -1778,22 +1779,20 @@ static ssize_t ipa3_enable_ipc_low(struct file *file, if (kstrtos8(dbg_buff, 0, &option)) return -EFAULT; + mutex_lock(&ipa3_ctx->lock); if (option) { - if (!ipa3_ctx->logbuf_low) { - ipa3_ctx->logbuf_low = + if (!ipa_ipc_low_buff) { + ipa_ipc_low_buff = ipc_log_context_create(IPA_IPC_LOG_PAGES, "ipa_low", 0); } - - if (ipa3_ctx->logbuf_low == NULL) { - IPAERR("failed to get logbuf_low\n"); - return -EFAULT; - } + if (ipa_ipc_low_buff == NULL) + IPAERR("failed to get logbuf_low\n"); + ipa3_ctx->logbuf_low = ipa_ipc_low_buff; } else { - if (ipa3_ctx->logbuf_low) - ipc_log_context_destroy(ipa3_ctx->logbuf_low); ipa3_ctx->logbuf_low = NULL; } + mutex_unlock(&ipa3_ctx->lock); return count; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index 8ec0974711a4..c5b56f16788a 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -4055,6 +4055,7 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in, if (!gsi_channel_props.ring_base_vaddr) { IPAERR("fail to dma alloc %u bytes\n", gsi_channel_props.ring_len); + result = -ENOMEM; goto fail_alloc_channel_ring; } gsi_channel_props.ring_base_addr = dma_addr; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index 0cf77bbde496..ac7ef6a21952 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -1027,6 +1027,11 @@ struct ipa_tz_unlock_reg_info { u32 size; }; +struct ipa_dma_task_info { + struct ipa_mem_buffer mem; + struct ipahal_imm_cmd_pyld *cmd_pyld; +}; + /** * struct ipa3_context - IPA context * @class: pointer to the struct class @@ -1246,6 +1251,7 @@ struct ipa3_context { struct ipa3_smp2p_info smp2p_info; u32 ipa_tz_unlock_reg_num; struct ipa_tz_unlock_reg_info *ipa_tz_unlock_reg; + struct ipa_dma_task_info dma_task_info; }; /** @@ -2053,4 +2059,6 @@ int ipa3_get_ntn_stats(struct Ipa3HwStatsNTNInfoData_t *stats); struct dentry *ipa_debugfs_get_root(void); bool ipa3_is_msm_device(void); struct device *ipa3_get_pdev(void); +int ipa3_allocate_dma_task_for_gsi(void); +void ipa3_free_dma_task_for_gsi(void); #endif /* _IPA3_I_H_ */ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c index ac7e57f10062..7212ba2a165c 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1438,6 +1438,10 @@ int ipa3_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup) mutex_lock(&ipa3_ctx->lock); entry = __ipa3_find_rt_tbl(lookup->ip, lookup->name); if (entry && entry->cookie == IPA_COOKIE) { + if (entry->ref_cnt == U32_MAX) { + IPAERR("fail: ref count crossed limit\n"); + goto ret; + } entry->ref_cnt++; lookup->hdl = entry->id; @@ -1447,6 +1451,8 @@ int ipa3_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup) result = 0; } + +ret: mutex_unlock(&ipa3_ctx->lock); return result; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index c8ff06ddda87..f4a7319ca290 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -3430,6 +3430,7 @@ static void ipa3_gsi_poll_after_suspend(struct ipa3_ep_context *ep) /* queue a work to start polling if don't have one */ atomic_set(&ipa3_ctx->transport_pm.eot_activity, 1); if (!atomic_read(&ep->sys->curr_polling_state)) { + ipa3_inc_acquire_wakelock(); atomic_set(&ep->sys->curr_polling_state, 1); queue_work(ep->sys->wq, &ep->sys->work); } @@ -3482,6 +3483,51 @@ void ipa3_suspend_apps_pipes(bool suspend) } } +int ipa3_allocate_dma_task_for_gsi(void) +{ + struct ipahal_imm_cmd_dma_task_32b_addr cmd = { 0 }; + + IPADBG("Allocate mem\n"); + ipa3_ctx->dma_task_info.mem.size = IPA_GSI_CHANNEL_STOP_PKT_SIZE; + ipa3_ctx->dma_task_info.mem.base = dma_alloc_coherent(ipa3_ctx->pdev, + ipa3_ctx->dma_task_info.mem.size, + &ipa3_ctx->dma_task_info.mem.phys_base, + GFP_KERNEL); + if (!ipa3_ctx->dma_task_info.mem.base) { + IPAERR("no mem\n"); + return -EFAULT; + } + + cmd.flsh = 1; + cmd.size1 = ipa3_ctx->dma_task_info.mem.size; + cmd.addr1 = ipa3_ctx->dma_task_info.mem.phys_base; + cmd.packet_size = ipa3_ctx->dma_task_info.mem.size; + ipa3_ctx->dma_task_info.cmd_pyld = ipahal_construct_imm_cmd( + IPA_IMM_CMD_DMA_TASK_32B_ADDR, &cmd, false); + if (!ipa3_ctx->dma_task_info.cmd_pyld) { + IPAERR("failed to construct dma_task_32b_addr cmd\n"); + dma_free_coherent(ipa3_ctx->pdev, + ipa3_ctx->dma_task_info.mem.size, + ipa3_ctx->dma_task_info.mem.base, + ipa3_ctx->dma_task_info.mem.phys_base); + memset(&ipa3_ctx->dma_task_info, 0, + sizeof(ipa3_ctx->dma_task_info)); + return -EFAULT; + } + + return 0; +} + +void ipa3_free_dma_task_for_gsi(void) +{ + dma_free_coherent(ipa3_ctx->pdev, + ipa3_ctx->dma_task_info.mem.size, + ipa3_ctx->dma_task_info.mem.base, + ipa3_ctx->dma_task_info.mem.phys_base); + ipahal_destroy_imm_cmd(ipa3_ctx->dma_task_info.cmd_pyld); + memset(&ipa3_ctx->dma_task_info, 0, sizeof(ipa3_ctx->dma_task_info)); +} + /** * ipa3_inject_dma_task_for_gsi()- Send DMA_TASK to IPA for GSI stop channel * @@ -3490,41 +3536,12 @@ void ipa3_suspend_apps_pipes(bool suspend) */ int ipa3_inject_dma_task_for_gsi(void) { - static struct ipa_mem_buffer mem = {0}; - struct ipahal_imm_cmd_dma_task_32b_addr cmd = {0}; - static struct ipahal_imm_cmd_pyld *cmd_pyld; struct ipa3_desc desc = {0}; - /* allocate the memory only for the very first time */ - if (!mem.base) { - IPADBG("Allocate mem\n"); - mem.size = IPA_GSI_CHANNEL_STOP_PKT_SIZE; - mem.base = dma_alloc_coherent(ipa3_ctx->pdev, - mem.size, - &mem.phys_base, - GFP_KERNEL); - if (!mem.base) { - IPAERR("no mem\n"); - return -EFAULT; - } - } - if (!cmd_pyld) { - cmd.flsh = 1; - cmd.size1 = mem.size; - cmd.addr1 = mem.phys_base; - cmd.packet_size = mem.size; - cmd_pyld = ipahal_construct_imm_cmd( - IPA_IMM_CMD_DMA_TASK_32B_ADDR, &cmd, false); - if (!cmd_pyld) { - IPAERR("failed to construct dma_task_32b_addr cmd\n"); - return -EFAULT; - } - } - desc.opcode = ipahal_imm_cmd_get_opcode_param( IPA_IMM_CMD_DMA_TASK_32B_ADDR, 1); - desc.pyld = cmd_pyld->data; - desc.len = cmd_pyld->len; + desc.pyld = ipa3_ctx->dma_task_info.cmd_pyld->data; + desc.len = ipa3_ctx->dma_task_info.cmd_pyld->len; desc.type = IPA_IMM_CMD_DESC; IPADBG("sending 1B packet to IPA\n"); diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index 6731150ce4e7..258c0861ed52 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -1663,6 +1663,9 @@ static int ipa3_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) extend_ioctl_data.u.rmnet_mux_val.vchannel_name, sizeof(mux_channel[rmnet_index] .vchannel_name)); + mux_channel[rmnet_index].vchannel_name[ + IFNAMSIZ - 1] = '\0'; + IPAWANDBG("cashe device[%s:%d] in IPA_wan[%d]\n", mux_channel[rmnet_index].vchannel_name, mux_channel[rmnet_index].mux_id, @@ -2365,32 +2368,41 @@ static int rmnet_ipa_ap_suspend(struct device *dev) { struct net_device *netdev = IPA_NETDEV(); struct ipa3_wwan_private *wwan_ptr; + int ret; + + IPAWANDBG("Enter...\n"); - IPAWANDBG_LOW("Enter...\n"); if (netdev == NULL) { IPAWANERR("netdev is NULL.\n"); - return 0; + ret = 0; + goto bail; } + netif_tx_lock_bh(netdev); wwan_ptr = netdev_priv(netdev); if (wwan_ptr == NULL) { IPAWANERR("wwan_ptr is NULL.\n"); - return 0; + ret = 0; + goto unlock_and_bail; } /* Do not allow A7 to suspend in case there are oustanding packets */ if (atomic_read(&wwan_ptr->outstanding_pkts) != 0) { IPAWANDBG("Outstanding packets, postponing AP suspend.\n"); - return -EAGAIN; + ret = -EAGAIN; + goto unlock_and_bail; } /* Make sure that there is no Tx operation ongoing */ - netif_tx_lock_bh(netdev); + netif_stop_queue(netdev); ipa_rm_release_resource(IPA_RM_RESOURCE_WWAN_0_PROD); - netif_tx_unlock_bh(netdev); - IPAWANDBG_LOW("Exit\n"); + ret = 0; - return 0; +unlock_and_bail: + netif_tx_unlock_bh(netdev); +bail: + IPAWANDBG("Exit with %d\n", ret); + return ret; } /** @@ -2407,10 +2419,10 @@ static int rmnet_ipa_ap_resume(struct device *dev) { struct net_device *netdev = IPA_NETDEV(); - IPAWANDBG_LOW("Enter...\n"); + IPAWANDBG("Enter...\n"); if (netdev) netif_wake_queue(netdev); - IPAWANDBG_LOW("Exit\n"); + IPAWANDBG("Exit\n"); return 0; } diff --git a/drivers/platform/msm/mhi/mhi.h b/drivers/platform/msm/mhi/mhi.h index 60e02fcb5e4b..b4f3df4ec3d2 100644 --- a/drivers/platform/msm/mhi/mhi.h +++ b/drivers/platform/msm/mhi/mhi.h @@ -338,9 +338,8 @@ struct db_mode { u32 db_mode : 1; enum MHI_BRSTMODE brstmode; void (*process_db)(struct mhi_device_ctxt *mhi_dev_ctxt, - void __iomem *io_addr, - uintptr_t chan, - u32 val); + void __iomem *io_addr, unsigned int chan, + dma_addr_t val); }; struct mhi_ring { @@ -728,15 +727,13 @@ int mhi_set_bus_request(struct mhi_device_ctxt *mhi_dev_ctxt, int index); int start_chan_sync(struct mhi_client_handle *client_handle); void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt, - void __iomem *io_addr, - uintptr_t chan, - u32 val); + void __iomem *io_addr, unsigned int chan, + dma_addr_t val); void mhi_process_db_brstmode_disable(struct mhi_device_ctxt *mhi_dev_ctxt, - void __iomem *io_addr, - uintptr_t chan, - u32 val); + void __iomem *io_addr, unsigned int chan, + dma_addr_t val); void mhi_process_db(struct mhi_device_ctxt *mhi_dev_ctxt, void __iomem *io_addr, - uintptr_t io_offset, u32 val); + unsigned int chan, dma_addr_t val); void mhi_reg_write_field(struct mhi_device_ctxt *mhi_dev_ctxt, void __iomem *io_addr, uintptr_t io_offset, diff --git a/drivers/platform/msm/mhi/mhi_main.c b/drivers/platform/msm/mhi/mhi_main.c index 46baf7332900..78aa1beb870d 100644 --- a/drivers/platform/msm/mhi/mhi_main.c +++ b/drivers/platform/msm/mhi/mhi_main.c @@ -90,8 +90,9 @@ dma_pool_error: } static void mhi_write_db(struct mhi_device_ctxt *mhi_dev_ctxt, - void __iomem *io_addr_lower, - uintptr_t chan, u64 val) + void __iomem *io_addr_lower, + unsigned int chan, + dma_addr_t val) { uintptr_t io_offset = chan * sizeof(u64); void __iomem *io_addr_upper = @@ -1918,8 +1919,8 @@ EXPORT_SYMBOL(mhi_xfer_rddm); void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt, void __iomem *io_addr, - uintptr_t chan, - u32 val) + unsigned int chan, + dma_addr_t val) { struct mhi_ring *ring_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan]; @@ -1932,7 +1933,7 @@ void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt, mhi_local_event_ctxt[chan]; mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, - "db.set addr: %p io_offset 0x%lx val:0x%x\n", + "db.set addr: %p io_offset %u val:0x%llx\n", io_addr, chan, val); mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val); @@ -1942,7 +1943,7 @@ void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt, ring_ctxt->db_mode.db_mode = 0; } else { mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, - "Not ringing xfer db, chan %ld, brstmode %d db_mode %d\n", + "Not ringing xfer db, chan %u, brstmode %d db_mode %d\n", chan, ring_ctxt->db_mode.brstmode, ring_ctxt->db_mode.db_mode); } @@ -1950,23 +1951,24 @@ void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt, void mhi_process_db_brstmode_disable(struct mhi_device_ctxt *mhi_dev_ctxt, void __iomem *io_addr, - uintptr_t chan, - u32 val) + unsigned int chan, + dma_addr_t val) { mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, - "db.set addr: %p io_offset 0x%lx val:0x%x\n", + "db.set addr: %p io_offset %u val:0x%llx\n", io_addr, chan, val); mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val); mhi_write_db(mhi_dev_ctxt, io_addr, chan, val); } void mhi_process_db(struct mhi_device_ctxt *mhi_dev_ctxt, - void __iomem *io_addr, - uintptr_t chan, u32 val) + void __iomem *io_addr, + unsigned int chan, + dma_addr_t val) { mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, - "db.set addr: %p io_offset 0x%lx val:0x%x\n", + "db.set addr: %p io_offset %u val:0x%llx\n", io_addr, chan, val); mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val); @@ -1981,7 +1983,7 @@ void mhi_process_db(struct mhi_device_ctxt *mhi_dev_ctxt, chan_ctxt->db_mode.db_mode = 0; } else { mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, - "Not ringing xfer db, chan %ld, brstmode %d db_mode %d\n", + "Not ringing xfer db, chan %u, brstmode %d db_mode %d\n", chan, chan_ctxt->db_mode.brstmode, chan_ctxt->db_mode.db_mode); } diff --git a/drivers/platform/msm/mhi/mhi_ssr.c b/drivers/platform/msm/mhi/mhi_ssr.c index 9f18b1e7ef85..f4f2e427972f 100644 --- a/drivers/platform/msm/mhi/mhi_ssr.c +++ b/drivers/platform/msm/mhi/mhi_ssr.c @@ -29,11 +29,9 @@ static int mhi_ssr_notify_cb(struct notifier_block *nb, mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Received ESOC notifcation:%lu crashed:%d\n", action, crashed); switch (action) { - case SUBSYS_BEFORE_SHUTDOWN: - /* - * update internal states only, we'll clean up MHI context - * after device shutdown completely. - */ + case SUBSYS_AFTER_SHUTDOWN: + + /* Disable internal state, no more communication */ write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); cur_state = mhi_tryset_pm_state(mhi_dev_ctxt, MHI_PM_LD_ERR_FATAL_DETECT); @@ -42,8 +40,6 @@ static int mhi_ssr_notify_cb(struct notifier_block *nb, mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Failed to transition to state 0x%x from 0x%x\n", MHI_PM_LD_ERR_FATAL_DETECT, cur_state); - break; - case SUBSYS_AFTER_SHUTDOWN: if (mhi_dev_ctxt->mhi_pm_state != MHI_PM_DISABLE) process_disable_transition(MHI_PM_SHUTDOWN_PROCESS, mhi_dev_ctxt); diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.c b/drivers/platform/msm/msm_11ad/msm_11ad.c index c6009d767db5..f1e348969c7b 100644 --- a/drivers/platform/msm/msm_11ad/msm_11ad.c +++ b/drivers/platform/msm/msm_11ad/msm_11ad.c @@ -643,6 +643,9 @@ static int msm_11ad_smmu_init(struct msm11ad_ctx *ctx) int rc; int force_pt_coherent = 1; int smmu_bypass = !ctx->smmu_s1_en; + dma_addr_t iova_base = 0; + dma_addr_t iova_end = ctx->smmu_base + ctx->smmu_size - 1; + struct iommu_domain_geometry geometry; if (!ctx->use_smmu) return 0; @@ -700,6 +703,17 @@ static int msm_11ad_smmu_init(struct msm11ad_ctx *ctx) rc); goto release_mapping; } + memset(&geometry, 0, sizeof(geometry)); + geometry.aperture_start = iova_base; + geometry.aperture_end = iova_end; + rc = iommu_domain_set_attr(ctx->mapping->domain, + DOMAIN_ATTR_GEOMETRY, + &geometry); + if (rc) { + dev_err(ctx->dev, "Set geometry attribute to SMMU failed (%d)\n", + rc); + goto release_mapping; + } } } @@ -822,7 +836,6 @@ static void msm_11ad_ssr_deinit(struct msm11ad_ctx *ctx) ctx->ramdump_dev = NULL; } - kfree(ctx->ramdump_addr); ctx->ramdump_addr = NULL; if (ctx->subsys_handle) { @@ -855,6 +868,14 @@ static int msm_11ad_ssr_init(struct msm11ad_ctx *ctx) goto out_rc; } + ctx->ramdump_dev = create_ramdump_device(ctx->subsysdesc.name, + ctx->subsysdesc.dev); + if (!ctx->ramdump_dev) { + dev_err(ctx->dev, "Create ramdump device failed\n"); + rc = -ENOMEM; + goto out_rc; + } + /* register ramdump area */ ctx->ramdump_addr = kmalloc(WIGIG_RAMDUMP_SIZE, GFP_KERNEL); if (!ctx->ramdump_addr) { @@ -864,25 +885,19 @@ static int msm_11ad_ssr_init(struct msm11ad_ctx *ctx) ctx->dump_data.addr = virt_to_phys(ctx->ramdump_addr); ctx->dump_data.len = WIGIG_RAMDUMP_SIZE; + strlcpy(ctx->dump_data.name, "KWIGIG", + sizeof(ctx->dump_data.name)); dump_entry.id = MSM_DUMP_DATA_WIGIG; dump_entry.addr = virt_to_phys(&ctx->dump_data); rc = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry); if (rc) { dev_err(ctx->dev, "Dump table setup failed: %d\n", rc); - goto out_rc; - } - - ctx->ramdump_dev = create_ramdump_device(ctx->subsysdesc.name, - ctx->subsysdesc.dev); - if (!ctx->ramdump_dev) { - dev_err(ctx->dev, "Create ramdump device failed: %d\n", rc); - rc = -ENOMEM; + kfree(ctx->ramdump_addr); goto out_rc; } return 0; - out_rc: msm_11ad_ssr_deinit(ctx); return rc; diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c index a30ed90d6e92..8d038ba0770d 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c @@ -33,6 +33,7 @@ #include <soc/qcom/scm.h> #include <soc/qcom/restart.h> #include <soc/qcom/watchdog.h> +#include <soc/qcom/minidump.h> #define EMERGENCY_DLOAD_MAGIC1 0x322A4F99 #define EMERGENCY_DLOAD_MAGIC2 0xC67E4350 @@ -42,9 +43,10 @@ #define SCM_IO_DISABLE_PMIC_ARBITER 1 #define SCM_IO_DEASSERT_PS_HOLD 2 #define SCM_WDOG_DEBUG_BOOT_PART 0x9 -#define SCM_DLOAD_MODE 0X10 +#define SCM_DLOAD_FULLDUMP 0X10 #define SCM_EDLOAD_MODE 0X01 #define SCM_DLOAD_CMD 0x10 +#define SCM_DLOAD_MINIDUMP 0X20 static int restart_mode; @@ -69,6 +71,7 @@ static void scm_disable_sdi(void); #endif static int in_panic; +static int dload_type = SCM_DLOAD_FULLDUMP; static int download_mode = 1; static struct kobject dload_kobj; static void *dload_mode_addr, *dload_type_addr; @@ -142,7 +145,7 @@ static void set_dload_mode(int on) mb(); } - ret = scm_set_dload_mode(on ? SCM_DLOAD_MODE : 0, 0); + ret = scm_set_dload_mode(on ? dload_type : 0, 0); if (ret) pr_err("Failed to set secure DLOAD mode: %d\n", ret); @@ -185,7 +188,6 @@ static int dload_set(const char *val, struct kernel_param *kp) int old_val = download_mode; ret = param_set_int(val, kp); - if (ret) return ret; @@ -454,7 +456,7 @@ static ssize_t show_emmc_dload(struct kobject *kobj, struct attribute *attr, else show_val = 0; - return snprintf(buf, sizeof(show_val), "%u\n", show_val); + return scnprintf(buf, sizeof(show_val), "%u\n", show_val); } static size_t store_emmc_dload(struct kobject *kobj, struct attribute *attr, @@ -477,10 +479,50 @@ static size_t store_emmc_dload(struct kobject *kobj, struct attribute *attr, return count; } + +#ifdef CONFIG_QCOM_MINIDUMP + +static DEFINE_MUTEX(tcsr_lock); + +static ssize_t show_dload_mode(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "DLOAD dump type: %s\n", + (dload_type == SCM_DLOAD_MINIDUMP) ? "mini" : "full"); +} + +static size_t store_dload_mode(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + if (sysfs_streq(buf, "full")) { + dload_type = SCM_DLOAD_FULLDUMP; + } else if (sysfs_streq(buf, "mini")) { + if (!msm_minidump_enabled()) { + pr_info("Minidump is not enabled\n"); + return -ENODEV; + } + dload_type = SCM_DLOAD_MINIDUMP; + } else { + pr_info("Invalid value. Use 'full' or 'mini'\n"); + return -EINVAL; + } + + mutex_lock(&tcsr_lock); + /*Overwrite TCSR reg*/ + set_dload_mode(dload_type); + mutex_unlock(&tcsr_lock); + return count; +} +RESET_ATTR(dload_mode, 0644, show_dload_mode, store_dload_mode); +#endif + RESET_ATTR(emmc_dload, 0644, show_emmc_dload, store_emmc_dload); static struct attribute *reset_attrs[] = { &reset_attr_emmc_dload.attr, +#ifdef CONFIG_QCOM_MINIDUMP + &reset_attr_dload_mode.attr, +#endif NULL }; diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c index 67f9d4fafeb8..539e757d3e99 100644 --- a/drivers/power/supply/qcom/battery.c +++ b/drivers/power/supply/qcom/battery.c @@ -434,23 +434,28 @@ static int pl_fcc_vote_callback(struct votable *votable, void *data, return rc; } - split_fcc(chip, total_fcc_ua, &master_fcc_ua, &slave_fcc_ua); - pval.intval = slave_fcc_ua; - rc = power_supply_set_property(chip->pl_psy, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); - if (rc < 0) { - pr_err("Couldn't set parallel fcc, rc=%d\n", rc); - return rc; - } + if (chip->pl_mode != POWER_SUPPLY_PL_NONE) { + split_fcc(chip, total_fcc_ua, &master_fcc_ua, &slave_fcc_ua); + + pval.intval = slave_fcc_ua; + rc = power_supply_set_property(chip->pl_psy, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + &pval); + if (rc < 0) { + pr_err("Couldn't set parallel fcc, rc=%d\n", rc); + return rc; + } - chip->slave_fcc_ua = slave_fcc_ua; + chip->slave_fcc_ua = slave_fcc_ua; - pval.intval = master_fcc_ua; - rc = power_supply_set_property(chip->main_psy, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval); - if (rc < 0) { - pr_err("Could not set main fcc, rc=%d\n", rc); - return rc; + pval.intval = master_fcc_ua; + rc = power_supply_set_property(chip->main_psy, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + &pval); + if (rc < 0) { + pr_err("Could not set main fcc, rc=%d\n", rc); + return rc; + } } pl_dbg(chip, PR_PARALLEL, "master_fcc=%d slave_fcc=%d distribution=(%d/%d)\n", @@ -685,9 +690,6 @@ static bool is_main_available(struct pl_data *chip) chip->main_psy = power_supply_get_by_name("main"); - if (chip->main_psy) - rerun_election(chip->usb_icl_votable); - return !!chip->main_psy; } @@ -866,7 +868,18 @@ static void status_change_work(struct work_struct *work) struct pl_data *chip = container_of(work, struct pl_data, status_change_work); - if (!is_main_available(chip)) + if (!chip->main_psy && is_main_available(chip)) { + /* + * re-run election for FCC/FV/ICL once main_psy + * is available to ensure all votes are reflected + * on hardware + */ + rerun_election(chip->usb_icl_votable); + rerun_election(chip->fcc_votable); + rerun_election(chip->fv_votable); + } + + if (!chip->main_psy) return; if (!is_batt_available(chip)) diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h index 316313ff6408..25c9d0251cf1 100644 --- a/drivers/power/supply/qcom/fg-core.h +++ b/drivers/power/supply/qcom/fg-core.h @@ -128,11 +128,6 @@ enum fg_irq_index { FG_IRQ_MAX, }; -/* WA flags */ -enum { - DELTA_SOC_IRQ_WA = BIT(0), -}; - /* * List of FG_SRAM parameters. Please add a parameter only if it is an entry * that will be used either to configure an entity (e.g. termination current) @@ -152,6 +147,7 @@ enum fg_sram_param_id { FG_SRAM_CC_SOC, FG_SRAM_CC_SOC_SW, FG_SRAM_ACT_BATT_CAP, + FG_SRAM_TIMEBASE, /* Entries below here are configurable during initialization */ FG_SRAM_CUTOFF_VOLT, FG_SRAM_EMPTY_VOLT, @@ -165,12 +161,14 @@ enum fg_sram_param_id { FG_SRAM_ESR_PULSE_THRESH, FG_SRAM_SYS_TERM_CURR, FG_SRAM_CHG_TERM_CURR, + FG_SRAM_CHG_TERM_BASE_CURR, FG_SRAM_DELTA_MSOC_THR, FG_SRAM_DELTA_BSOC_THR, FG_SRAM_RECHARGE_SOC_THR, FG_SRAM_RECHARGE_VBATT_THR, FG_SRAM_KI_COEFF_MED_DISCHG, FG_SRAM_KI_COEFF_HI_DISCHG, + FG_SRAM_KI_COEFF_FULL_SOC, FG_SRAM_ESR_TIGHT_FILTER, FG_SRAM_ESR_BROAD_FILTER, FG_SRAM_SLOPE_LIMIT, @@ -210,6 +208,7 @@ struct fg_alg_flag { enum wa_flags { PMI8998_V1_REV_WA = BIT(0), + PM660_TSMC_OSC_WA = BIT(1), }; enum slope_limit_status { @@ -229,6 +228,7 @@ struct fg_dt_props { int empty_volt_mv; int vbatt_low_thr_mv; int chg_term_curr_ma; + int chg_term_base_curr_ma; int sys_term_curr_ma; int delta_soc_thr; int recharge_soc_thr; @@ -322,6 +322,23 @@ static const struct fg_pt fg_ln_table[] = { { 128000, 4852 }, }; +/* each tuple is - <temperature in degC, Timebase> */ +static const struct fg_pt fg_tsmc_osc_table[] = { + { -20, 395064 }, + { -10, 398114 }, + { 0, 401669 }, + { 10, 404641 }, + { 20, 408856 }, + { 25, 412449 }, + { 30, 416532 }, + { 40, 420289 }, + { 50, 425020 }, + { 60, 430160 }, + { 70, 434175 }, + { 80, 439475 }, + { 90, 444992 }, +}; + struct fg_chip { struct device *dev; struct pmic_revid_data *pmic_rev_id; @@ -333,6 +350,7 @@ struct fg_chip { struct power_supply *dc_psy; struct power_supply *parallel_psy; struct iio_channel *batt_id_chan; + struct iio_channel *die_temp_chan; struct fg_memif *sram; struct fg_irq_info *irqs; struct votable *awake_votable; @@ -356,6 +374,7 @@ struct fg_chip { u32 rradc_base; u32 wa_flags; int batt_id_ohms; + int ki_coeff_full_soc; int charge_status; int prev_charge_status; int charge_done; diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c index 2e9425040f72..4368709118ac 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen3.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c @@ -47,11 +47,14 @@ #define ESR_UPD_TIGHT_LOW_TEMP_OFFSET 2 #define ESR_UPD_BROAD_LOW_TEMP_OFFSET 3 #define KI_COEFF_MED_DISCHG_WORD 9 +#define TIMEBASE_OFFSET 1 #define KI_COEFF_MED_DISCHG_OFFSET 3 #define KI_COEFF_HI_DISCHG_WORD 10 #define KI_COEFF_HI_DISCHG_OFFSET 0 #define KI_COEFF_LOW_DISCHG_WORD 10 #define KI_COEFF_LOW_DISCHG_OFFSET 2 +#define KI_COEFF_FULL_SOC_WORD 12 +#define KI_COEFF_FULL_SOC_OFFSET 2 #define DELTA_MSOC_THR_WORD 12 #define DELTA_MSOC_THR_OFFSET 3 #define DELTA_BSOC_THR_WORD 13 @@ -128,6 +131,7 @@ #define RECHARGE_SOC_THR_v2_WORD 14 #define RECHARGE_SOC_THR_v2_OFFSET 1 #define CHG_TERM_CURR_v2_WORD 15 +#define CHG_TERM_BASE_CURR_v2_OFFSET 0 #define CHG_TERM_CURR_v2_OFFSET 1 #define EMPTY_VOLT_v2_WORD 15 #define EMPTY_VOLT_v2_OFFSET 3 @@ -226,6 +230,9 @@ static struct fg_sram_param pmi8998_v1_sram_params[] = { PARAM(KI_COEFF_HI_DISCHG, KI_COEFF_HI_DISCHG_WORD, KI_COEFF_HI_DISCHG_OFFSET, 1, 1000, 244141, 0, fg_encode_default, NULL), + PARAM(KI_COEFF_FULL_SOC, KI_COEFF_FULL_SOC_WORD, + KI_COEFF_FULL_SOC_OFFSET, 1, 1000, 244141, 0, + fg_encode_default, NULL), PARAM(ESR_TIGHT_FILTER, ESR_FILTER_WORD, ESR_UPD_TIGHT_OFFSET, 1, 512, 1000000, 0, fg_encode_default, NULL), PARAM(ESR_BROAD_FILTER, ESR_FILTER_WORD, ESR_UPD_BROAD_OFFSET, @@ -255,6 +262,8 @@ static struct fg_sram_param pmi8998_v2_sram_params[] = { fg_decode_cc_soc), PARAM(ACT_BATT_CAP, ACT_BATT_CAP_BKUP_WORD, ACT_BATT_CAP_BKUP_OFFSET, 2, 1, 1, 0, NULL, fg_decode_default), + PARAM(TIMEBASE, KI_COEFF_MED_DISCHG_WORD, TIMEBASE_OFFSET, 2, 1000, + 61000, 0, fg_encode_default, NULL), /* Entries below here are configurable during initialization */ PARAM(CUTOFF_VOLT, CUTOFF_VOLT_WORD, CUTOFF_VOLT_OFFSET, 2, 1000000, 244141, 0, fg_encode_voltage, NULL), @@ -270,6 +279,9 @@ static struct fg_sram_param pmi8998_v2_sram_params[] = { 1000000, 122070, 0, fg_encode_current, NULL), PARAM(CHG_TERM_CURR, CHG_TERM_CURR_v2_WORD, CHG_TERM_CURR_v2_OFFSET, 1, 100000, 390625, 0, fg_encode_current, NULL), + PARAM(CHG_TERM_BASE_CURR, CHG_TERM_CURR_v2_WORD, + CHG_TERM_BASE_CURR_v2_OFFSET, 1, 1024, 1000, 0, + fg_encode_current, NULL), PARAM(DELTA_MSOC_THR, DELTA_MSOC_THR_v2_WORD, DELTA_MSOC_THR_v2_OFFSET, 1, 2048, 100, 0, fg_encode_default, NULL), PARAM(DELTA_BSOC_THR, DELTA_BSOC_THR_v2_WORD, DELTA_BSOC_THR_v2_OFFSET, @@ -298,6 +310,9 @@ static struct fg_sram_param pmi8998_v2_sram_params[] = { PARAM(KI_COEFF_HI_DISCHG, KI_COEFF_HI_DISCHG_v2_WORD, KI_COEFF_HI_DISCHG_v2_OFFSET, 1, 1000, 244141, 0, fg_encode_default, NULL), + PARAM(KI_COEFF_FULL_SOC, KI_COEFF_FULL_SOC_WORD, + KI_COEFF_FULL_SOC_OFFSET, 1, 1000, 244141, 0, + fg_encode_default, NULL), PARAM(ESR_TIGHT_FILTER, ESR_FILTER_WORD, ESR_UPD_TIGHT_OFFSET, 1, 512, 1000000, 0, fg_encode_default, NULL), PARAM(ESR_BROAD_FILTER, ESR_FILTER_WORD, ESR_UPD_BROAD_OFFSET, @@ -1482,6 +1497,37 @@ static int fg_adjust_ki_coeff_dischg(struct fg_chip *chip) return 0; } +#define KI_COEFF_FULL_SOC_DEFAULT 733 +static int fg_adjust_ki_coeff_full_soc(struct fg_chip *chip, int batt_temp) +{ + int rc, ki_coeff_full_soc; + u8 val; + + if (batt_temp < 0) + ki_coeff_full_soc = 0; + else + ki_coeff_full_soc = KI_COEFF_FULL_SOC_DEFAULT; + + if (chip->ki_coeff_full_soc == ki_coeff_full_soc) + return 0; + + fg_encode(chip->sp, FG_SRAM_KI_COEFF_FULL_SOC, ki_coeff_full_soc, &val); + rc = fg_sram_write(chip, + chip->sp[FG_SRAM_KI_COEFF_FULL_SOC].addr_word, + chip->sp[FG_SRAM_KI_COEFF_FULL_SOC].addr_byte, &val, + chip->sp[FG_SRAM_KI_COEFF_FULL_SOC].len, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing ki_coeff_full_soc, rc=%d\n", rc); + return rc; + } + + chip->ki_coeff_full_soc = ki_coeff_full_soc; + fg_dbg(chip, FG_STATUS, "Wrote ki_coeff_full_soc %d\n", + ki_coeff_full_soc); + return 0; +} + static int fg_set_recharge_voltage(struct fg_chip *chip, int voltage_mv) { u8 buf; @@ -1924,7 +1970,7 @@ static int fg_esr_fcc_config(struct fg_chip *chip) { union power_supply_propval prop = {0, }; int rc; - bool parallel_en = false; + bool parallel_en = false, qnovo_en = false; if (is_parallel_charger_available(chip)) { rc = power_supply_get_property(chip->parallel_psy, @@ -1937,19 +1983,25 @@ static int fg_esr_fcc_config(struct fg_chip *chip) parallel_en = prop.intval; } - fg_dbg(chip, FG_POWER_SUPPLY, "charge_status: %d parallel_en: %d esr_fcc_ctrl_en: %d\n", - chip->charge_status, parallel_en, chip->esr_fcc_ctrl_en); + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE, &prop); + if (!rc) + qnovo_en = prop.intval; + + fg_dbg(chip, FG_POWER_SUPPLY, "chg_sts: %d par_en: %d qnov_en: %d esr_fcc_ctrl_en: %d\n", + chip->charge_status, parallel_en, qnovo_en, + chip->esr_fcc_ctrl_en); if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING && - parallel_en) { + (parallel_en || qnovo_en)) { if (chip->esr_fcc_ctrl_en) return 0; /* - * When parallel charging is enabled, configure ESR FCC to - * 300mA to trigger an ESR pulse. Without this, FG can ask - * the main charger to increase FCC when it is supposed to - * decrease it. + * When parallel charging or Qnovo is enabled, configure ESR + * FCC to 300mA to trigger an ESR pulse. Without this, FG can + * request the main charger to increase FCC when it is supposed + * to decrease it. */ rc = fg_masked_write(chip, BATT_INFO_ESR_FAST_CRG_CFG(chip), ESR_FAST_CRG_IVAL_MASK | @@ -1968,8 +2020,8 @@ static int fg_esr_fcc_config(struct fg_chip *chip) /* * If we're here, then it means either the device is not in - * charging state or parallel charging is disabled. Disable - * ESR fast charge current control in SW. + * charging state or parallel charging / Qnovo is disabled. + * Disable ESR fast charge current control in SW. */ rc = fg_masked_write(chip, BATT_INFO_ESR_FAST_CRG_CFG(chip), ESR_FAST_CRG_CTL_EN_BIT, 0); @@ -2066,6 +2118,11 @@ static void status_change_work(struct work_struct *work) if (rc < 0) pr_err("Error in configuring slope limiter rc:%d\n", rc); + + rc = fg_adjust_ki_coeff_full_soc(chip, batt_temp); + if (rc < 0) + pr_err("Error in configuring ki_coeff_full_soc rc:%d\n", + rc); } fg_batt_avg_update(chip); @@ -3101,6 +3158,21 @@ static int fg_hw_init(struct fg_chip *chip) return rc; } + if (!(chip->wa_flags & PMI8998_V1_REV_WA)) { + fg_encode(chip->sp, FG_SRAM_CHG_TERM_BASE_CURR, + chip->dt.chg_term_base_curr_ma, buf); + rc = fg_sram_write(chip, + chip->sp[FG_SRAM_CHG_TERM_BASE_CURR].addr_word, + chip->sp[FG_SRAM_CHG_TERM_BASE_CURR].addr_byte, + buf, chip->sp[FG_SRAM_CHG_TERM_BASE_CURR].len, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing chg_term_base_curr, rc=%d\n", + rc); + return rc; + } + } + if (chip->dt.vbatt_low_thr_mv > 0) { fg_encode(chip->sp, FG_SRAM_VBATT_LOW, chip->dt.vbatt_low_thr_mv, buf); @@ -3302,6 +3374,40 @@ static int fg_memif_init(struct fg_chip *chip) return fg_ima_init(chip); } +static int fg_adjust_timebase(struct fg_chip *chip) +{ + int rc = 0, die_temp; + s32 time_base = 0; + u8 buf[2] = {0}; + + if ((chip->wa_flags & PM660_TSMC_OSC_WA) && chip->die_temp_chan) { + rc = iio_read_channel_processed(chip->die_temp_chan, &die_temp); + if (rc < 0) { + pr_err("Error in reading die_temp, rc:%d\n", rc); + return rc; + } + + rc = fg_lerp(fg_tsmc_osc_table, ARRAY_SIZE(fg_tsmc_osc_table), + die_temp / 1000, &time_base); + if (rc < 0) { + pr_err("Error to lookup fg_tsmc_osc_table rc=%d\n", rc); + return rc; + } + + fg_encode(chip->sp, FG_SRAM_TIMEBASE, time_base, buf); + rc = fg_sram_write(chip, + chip->sp[FG_SRAM_TIMEBASE].addr_word, + chip->sp[FG_SRAM_TIMEBASE].addr_byte, buf, + chip->sp[FG_SRAM_TIMEBASE].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing timebase, rc=%d\n", rc); + return rc; + } + } + + return 0; +} + /* INTERRUPT HANDLERS STAY HERE */ static irqreturn_t fg_mem_xcp_irq_handler(int irq, void *data) @@ -3409,6 +3515,10 @@ static irqreturn_t fg_delta_batt_temp_irq_handler(int irq, void *data) if (rc < 0) pr_err("Error in configuring slope limiter rc:%d\n", rc); + rc = fg_adjust_ki_coeff_full_soc(chip, batt_temp); + if (rc < 0) + pr_err("Error in configuring ki_coeff_full_soc rc:%d\n", rc); + if (!batt_psy_initialized(chip)) { chip->last_batt_temp = batt_temp; return IRQ_HANDLED; @@ -3419,6 +3529,10 @@ static irqreturn_t fg_delta_batt_temp_irq_handler(int irq, void *data) chip->health = prop.intval; if (chip->last_batt_temp != batt_temp) { + rc = fg_adjust_timebase(chip); + if (rc < 0) + pr_err("Error in adjusting timebase, rc=%d\n", rc); + chip->last_batt_temp = batt_temp; power_supply_changed(chip->batt_psy); } @@ -3488,6 +3602,10 @@ static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data) if (rc < 0) pr_err("Error in validating ESR, rc=%d\n", rc); + rc = fg_adjust_timebase(chip); + if (rc < 0) + pr_err("Error in adjusting timebase, rc=%d\n", rc); + if (batt_psy_initialized(chip)) power_supply_changed(chip->batt_psy); @@ -3755,6 +3873,7 @@ static int fg_parse_ki_coefficients(struct fg_chip *chip) #define DEFAULT_EMPTY_VOLT_MV 2800 #define DEFAULT_RECHARGE_VOLT_MV 4250 #define DEFAULT_CHG_TERM_CURR_MA 100 +#define DEFAULT_CHG_TERM_BASE_CURR_MA 75 #define DEFAULT_SYS_TERM_CURR_MA -125 #define DEFAULT_DELTA_SOC_THR 1 #define DEFAULT_RECHARGE_SOC_THR 95 @@ -3829,6 +3948,8 @@ static int fg_parse_dt(struct fg_chip *chip) chip->sp = pmi8998_v2_sram_params; chip->alg_flags = pmi8998_v2_alg_flags; chip->use_ima_single_mode = true; + if (chip->pmic_rev_id->fab_id == PM660_FAB_ID_TSMC) + chip->wa_flags |= PM660_TSMC_OSC_WA; break; default: return -EINVAL; @@ -3909,6 +4030,12 @@ static int fg_parse_dt(struct fg_chip *chip) else chip->dt.sys_term_curr_ma = temp; + rc = of_property_read_u32(node, "qcom,fg-chg-term-base-current", &temp); + if (rc < 0) + chip->dt.chg_term_base_curr_ma = DEFAULT_CHG_TERM_BASE_CURR_MA; + else + chip->dt.chg_term_base_curr_ma = temp; + rc = of_property_read_u32(node, "qcom,fg-delta-soc-thr", &temp); if (rc < 0) chip->dt.delta_soc_thr = DEFAULT_DELTA_SOC_THR; @@ -4147,6 +4274,7 @@ static int fg_gen3_probe(struct platform_device *pdev) chip->irqs = fg_irqs; chip->charge_status = -EINVAL; chip->prev_charge_status = -EINVAL; + chip->ki_coeff_full_soc = -EINVAL; chip->regmap = dev_get_regmap(chip->dev->parent, NULL); if (!chip->regmap) { dev_err(chip->dev, "Parent regmap is unavailable\n"); @@ -4163,6 +4291,21 @@ static int fg_gen3_probe(struct platform_device *pdev) return rc; } + rc = of_property_match_string(chip->dev->of_node, + "io-channel-names", "rradc_die_temp"); + if (rc >= 0) { + chip->die_temp_chan = iio_channel_get(chip->dev, + "rradc_die_temp"); + if (IS_ERR(chip->die_temp_chan)) { + if (PTR_ERR(chip->die_temp_chan) != -EPROBE_DEFER) + pr_err("rradc_die_temp unavailable %ld\n", + PTR_ERR(chip->die_temp_chan)); + rc = PTR_ERR(chip->die_temp_chan); + chip->die_temp_chan = NULL; + return rc; + } + } + chip->awake_votable = create_votable("FG_WS", VOTE_SET_ANY, fg_awake_cb, chip); if (IS_ERR(chip->awake_votable)) { diff --git a/drivers/power/supply/qcom/qpnp-qnovo.c b/drivers/power/supply/qcom/qpnp-qnovo.c index c74dc8989821..eb97eb0ff2ac 100644 --- a/drivers/power/supply/qcom/qpnp-qnovo.c +++ b/drivers/power/supply/qcom/qpnp-qnovo.c @@ -89,7 +89,16 @@ #define QNOVO_STRM_CTRL 0xA8 #define QNOVO_IADC_OFFSET_OVR_VAL 0xA9 #define QNOVO_IADC_OFFSET_OVR 0xAA + #define QNOVO_DISABLE_CHARGING 0xAB +#define ERR_SWITCHER_DISABLED BIT(7) +#define ERR_JEITA_SOFT_CONDITION BIT(6) +#define ERR_BAT_OV BIT(5) +#define ERR_CV_MODE BIT(4) +#define ERR_BATTERY_MISSING BIT(3) +#define ERR_SAFETY_TIMER_EXPIRED BIT(2) +#define ERR_CHARGING_DISABLED BIT(1) +#define ERR_JEITA_HARD_CONDITION BIT(0) #define QNOVO_TR_IADC_OFFSET_0 0xF1 #define QNOVO_TR_IADC_OFFSET_1 0xF2 @@ -1107,24 +1116,28 @@ static int qnovo_update_status(struct qnovo *chip) { u8 val = 0; int rc; - bool charging; + bool ok_to_qnovo; bool changed = false; rc = qnovo_read(chip, QNOVO_ERROR_STS2, &val, 1); if (rc < 0) { pr_err("Couldn't read error sts rc = %d\n", rc); - charging = false; + ok_to_qnovo = false; } else { - charging = !(val & QNOVO_ERROR_CHARGING_DISABLED); + /* + * For CV mode keep qnovo enabled, userspace is expected to + * disable it after few runs + */ + ok_to_qnovo = (val == ERR_CV_MODE || val == 0) ? true : false; } - if (chip->ok_to_qnovo ^ charging) { + if (chip->ok_to_qnovo ^ ok_to_qnovo) { - vote(chip->disable_votable, OK_TO_QNOVO_VOTER, !charging, 0); - if (!charging) + vote(chip->disable_votable, OK_TO_QNOVO_VOTER, !ok_to_qnovo, 0); + if (!ok_to_qnovo) vote(chip->disable_votable, USER_VOTER, true, 0); - chip->ok_to_qnovo = charging; + chip->ok_to_qnovo = ok_to_qnovo; changed = true; } @@ -1247,6 +1260,16 @@ static int qnovo_hw_init(struct qnovo *chip) chip->v_gain_mega = 1000000000 + (s64)(s8)vadc_gain * GAIN_LSB_FACTOR; chip->v_gain_mega = div_s64(chip->v_gain_mega, 1000); + /* allow charger error conditions to disable qnovo, CV mode excluded */ + val = ERR_SWITCHER_DISABLED | ERR_JEITA_SOFT_CONDITION | ERR_BAT_OV | + ERR_BATTERY_MISSING | ERR_SAFETY_TIMER_EXPIRED | + ERR_CHARGING_DISABLED | ERR_JEITA_HARD_CONDITION; + rc = qnovo_write(chip, QNOVO_DISABLE_CHARGING, &val, 1); + if (rc < 0) { + pr_err("Couldn't write QNOVO_DISABLE_CHARGING rc = %d\n", rc); + return rc; + } + return 0; } diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index 81e656c06da0..8855a1c74e0b 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -244,6 +244,8 @@ struct smb_dt_props { int boost_threshold_ua; int fv_uv; int wipower_max_uw; + int min_freq_khz; + int max_freq_khz; u32 step_soc_threshold[STEP_CHARGING_MAX_STEPS - 1]; s32 step_cc_delta[STEP_CHARGING_MAX_STEPS]; struct device_node *revid_dev_node; @@ -338,6 +340,18 @@ static int smb2_parse_dt(struct smb2 *chip) if (rc < 0) chip->dt.boost_threshold_ua = MICRO_P1A; + rc = of_property_read_u32(node, + "qcom,min-freq-khz", + &chip->dt.min_freq_khz); + if (rc < 0) + chip->dt.min_freq_khz = -EINVAL; + + rc = of_property_read_u32(node, + "qcom,max-freq-khz", + &chip->dt.max_freq_khz); + if (rc < 0) + chip->dt.max_freq_khz = -EINVAL; + rc = of_property_read_u32(node, "qcom,wipower-max-uw", &chip->dt.wipower_max_uw); if (rc < 0) @@ -526,6 +540,12 @@ static int smb2_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_VOLTAGE_MIN: rc = smblib_set_prop_usb_voltage_min(chg, val); @@ -564,6 +584,8 @@ static int smb2_usb_set_prop(struct power_supply *psy, break; } +unlock: + mutex_unlock(&chg->lock); return rc; } @@ -1336,10 +1358,12 @@ static int smb2_configure_typec(struct smb_charger *chg) return rc; } - /* disable try.SINK mode */ - rc = smblib_masked_write(chg, TYPE_C_CFG_3_REG, EN_TRYSINK_MODE_BIT, 0); + /* disable try.SINK mode and legacy cable IRQs */ + rc = smblib_masked_write(chg, TYPE_C_CFG_3_REG, EN_TRYSINK_MODE_BIT | + TYPEC_NONCOMPLIANT_LEGACY_CABLE_INT_EN_BIT | + TYPEC_LEGACY_CABLE_INT_EN_BIT, 0); if (rc < 0) { - dev_err(chg->dev, "Couldn't set TRYSINK_MODE rc=%d\n", rc); + dev_err(chg->dev, "Couldn't set Type-C config rc=%d\n", rc); return rc; } @@ -1438,6 +1462,16 @@ static int smb2_init_hw(struct smb2 *chip) smblib_get_charge_param(chg, &chg->param.dc_icl, &chip->dt.dc_icl_ua); + if (chip->dt.min_freq_khz > 0) { + chg->param.freq_buck.min_u = chip->dt.min_freq_khz; + chg->param.freq_boost.min_u = chip->dt.min_freq_khz; + } + + if (chip->dt.max_freq_khz > 0) { + chg->param.freq_buck.max_u = chip->dt.max_freq_khz; + chg->param.freq_boost.max_u = chip->dt.max_freq_khz; + } + /* set a slower soft start setting for OTG */ rc = smblib_masked_write(chg, DC_ENG_SSUPPLY_CFG2_REG, ENG_SSUPPLY_IVREF_OTG_SS_MASK, OTG_SS_SLOW); @@ -1485,6 +1519,8 @@ static int smb2_init_hw(struct smb2 *chip) DEFAULT_VOTER, true, chip->dt.dc_icl_ua); vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, true, 0); + vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, + true, 0); vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER, chip->dt.hvdcp_disable, 0); vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, @@ -2022,6 +2058,16 @@ static int smb2_request_interrupts(struct smb2 *chip) return rc; } +static void smb2_disable_interrupts(struct smb_charger *chg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(smb2_irqs); i++) { + if (smb2_irqs[i].irq > 0) + disable_irq(smb2_irqs[i].irq); + } +} + #if defined(CONFIG_DEBUG_FS) static int force_batt_psy_update_write(void *data, u64 val) @@ -2284,6 +2330,9 @@ static void smb2_shutdown(struct platform_device *pdev) struct smb2 *chip = platform_get_drvdata(pdev); struct smb_charger *chg = &chip->chg; + /* disable all interrupts */ + smb2_disable_interrupts(chg); + /* configure power role for UFP */ smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, TYPEC_POWER_ROLE_CMD_MASK, UFP_EN_CMD_BIT); diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index bd96703579f6..1e417e8aa22d 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -543,30 +543,6 @@ static void smblib_rerun_apsd(struct smb_charger *chg) smblib_err(chg, "Couldn't re-run APSD rc=%d\n", rc); } -static int try_rerun_apsd_for_hvdcp(struct smb_charger *chg) -{ - const struct apsd_result *apsd_result; - - /* - * PD_INACTIVE_VOTER on hvdcp_disable_votable indicates whether - * apsd rerun was tried earlier - */ - if (get_client_vote(chg->hvdcp_disable_votable_indirect, - PD_INACTIVE_VOTER)) { - vote(chg->hvdcp_disable_votable_indirect, - PD_INACTIVE_VOTER, false, 0); - /* ensure hvdcp is enabled */ - if (!get_effective_result( - chg->hvdcp_disable_votable_indirect)) { - apsd_result = smblib_get_apsd_result(chg); - if (apsd_result->bit & (QC_2P0_BIT | QC_3P0_BIT)) { - smblib_rerun_apsd(chg); - } - } - } - return 0; -} - static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) { const struct apsd_result *apsd_result = smblib_get_apsd_result(chg); @@ -685,6 +661,7 @@ static void smblib_uusb_removal(struct smb_charger *chg) chg->voltage_max_uv = MICRO_5V; chg->usb_icl_delta_ua = 0; chg->pulse_cnt = 0; + chg->uusb_apsd_rerun_done = false; /* clear USB ICL vote for USB_PSY_VOTER */ rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); @@ -752,6 +729,7 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg) rc); } + chg->uusb_apsd_rerun_done = true; smblib_rerun_apsd(chg); return 0; @@ -1021,6 +999,7 @@ static int smblib_hvdcp_enable_vote_callback(struct votable *votable, struct smb_charger *chg = data; int rc; u8 val = HVDCP_AUTH_ALG_EN_CFG_BIT | HVDCP_EN_BIT; + u8 stat; /* vote to enable/disable HW autonomous INOV */ vote(chg->hvdcp_hw_inov_dis_votable, client, !hvdcp_enable, 0); @@ -1042,6 +1021,16 @@ static int smblib_hvdcp_enable_vote_callback(struct votable *votable, return rc; } + rc = smblib_read(chg, APSD_STATUS_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read APSD status rc=%d\n", rc); + return rc; + } + + /* re-run APSD if HVDCP was detected */ + if (stat & QC_CHARGER_BIT) + smblib_rerun_apsd(chg); + return 0; } @@ -1135,6 +1124,22 @@ static int smblib_usb_irq_enable_vote_callback(struct votable *votable, return 0; } +static int smblib_typec_irq_disable_vote_callback(struct votable *votable, + void *data, int disable, const char *client) +{ + struct smb_charger *chg = data; + + if (!chg->irq_info[TYPE_C_CHANGE_IRQ].irq) + return 0; + + if (disable) + disable_irq_nosync(chg->irq_info[TYPE_C_CHANGE_IRQ].irq); + else + enable_irq(chg->irq_info[TYPE_C_CHANGE_IRQ].irq); + + return 0; +} + /******************* * VCONN REGULATOR * * *****************/ @@ -1143,7 +1148,7 @@ static int smblib_usb_irq_enable_vote_callback(struct votable *votable, static int _smblib_vconn_regulator_enable(struct regulator_dev *rdev) { struct smb_charger *chg = rdev_get_drvdata(rdev); - u8 otg_stat, stat4; + u8 otg_stat, val; int rc = 0, i; if (!chg->external_vconn) { @@ -1174,17 +1179,12 @@ static int _smblib_vconn_regulator_enable(struct regulator_dev *rdev) * VCONN_EN_ORIENTATION is overloaded with overriding the CC pin used * for Vconn, and it should be set with reverse polarity of CC_OUT. */ - rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat4); - if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); - return rc; - } - smblib_dbg(chg, PR_OTG, "enabling VCONN\n"); - stat4 = stat4 & CC_ORIENTATION_BIT ? 0 : VCONN_EN_ORIENTATION_BIT; + val = chg->typec_status[3] & + CC_ORIENTATION_BIT ? 0 : VCONN_EN_ORIENTATION_BIT; rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, VCONN_EN_VALUE_BIT | VCONN_EN_ORIENTATION_BIT, - VCONN_EN_VALUE_BIT | stat4); + VCONN_EN_VALUE_BIT | val); if (rc < 0) { smblib_err(chg, "Couldn't enable vconn setting rc=%d\n", rc); return rc; @@ -1532,6 +1532,21 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, break; } + if (val->intval != POWER_SUPPLY_STATUS_CHARGING) + return 0; + + rc = smblib_read(chg, BATTERY_CHARGER_STATUS_7_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n", + rc); + return rc; + } + + stat &= ENABLE_TRICKLE_BIT | ENABLE_PRE_CHARGING_BIT | + ENABLE_FAST_CHARGING_BIT | ENABLE_FULLON_MODE_BIT; + if (!stat) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + return 0; } @@ -2132,23 +2147,13 @@ int smblib_get_prop_charger_temp_max(struct smb_charger *chg, int smblib_get_prop_typec_cc_orientation(struct smb_charger *chg, union power_supply_propval *val) { - int rc = 0; - u8 stat; - - rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); - if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); - return rc; - } - smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n", - stat); - - if (stat & CC_ATTACHED_BIT) - val->intval = (bool)(stat & CC_ORIENTATION_BIT) + 1; + if (chg->typec_status[3] & CC_ATTACHED_BIT) + val->intval = + (bool)(chg->typec_status[3] & CC_ORIENTATION_BIT) + 1; else val->intval = 0; - return rc; + return 0; } static const char * const smblib_typec_mode_name[] = { @@ -2166,17 +2171,7 @@ static const char * const smblib_typec_mode_name[] = { static int smblib_get_prop_ufp_mode(struct smb_charger *chg) { - int rc; - u8 stat; - - rc = smblib_read(chg, TYPE_C_STATUS_1_REG, &stat); - if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_1 rc=%d\n", rc); - return POWER_SUPPLY_TYPEC_NONE; - } - smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_1 = 0x%02x\n", stat); - - switch (stat) { + switch (chg->typec_status[0]) { case 0: return POWER_SUPPLY_TYPEC_NONE; case UFP_TYPEC_RDSTD_BIT: @@ -2194,17 +2189,7 @@ static int smblib_get_prop_ufp_mode(struct smb_charger *chg) static int smblib_get_prop_dfp_mode(struct smb_charger *chg) { - int rc; - u8 stat; - - rc = smblib_read(chg, TYPE_C_STATUS_2_REG, &stat); - if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_2 rc=%d\n", rc); - return POWER_SUPPLY_TYPEC_NONE; - } - smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_2 = 0x%02x\n", stat); - - switch (stat & DFP_TYPEC_MASK) { + switch (chg->typec_status[1] & DFP_TYPEC_MASK) { case DFP_RA_RA_BIT: return POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER; case DFP_RD_RD_BIT: @@ -2225,28 +2210,17 @@ static int smblib_get_prop_dfp_mode(struct smb_charger *chg) int smblib_get_prop_typec_mode(struct smb_charger *chg, union power_supply_propval *val) { - int rc; - u8 stat; - - rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); - if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); + if (!(chg->typec_status[3] & TYPEC_DEBOUNCE_DONE_STATUS_BIT)) { val->intval = POWER_SUPPLY_TYPEC_NONE; - return rc; - } - smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n", stat); - - if (!(stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT)) { - val->intval = POWER_SUPPLY_TYPEC_NONE; - return rc; + return 0; } - if (stat & UFP_DFP_MODE_STATUS_BIT) + if (chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT) val->intval = smblib_get_prop_dfp_mode(chg); else val->intval = smblib_get_prop_ufp_mode(chg); - return rc; + return 0; } int smblib_get_prop_typec_power_role(struct smb_charger *chg, @@ -2338,16 +2312,7 @@ int smblib_get_prop_input_voltage_settled(struct smb_charger *chg, int smblib_get_prop_pd_in_hard_reset(struct smb_charger *chg, union power_supply_propval *val) { - int rc; - u8 ctrl; - - rc = smblib_read(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, &ctrl); - if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG rc=%d\n", - rc); - return rc; - } - val->intval = ctrl & EXIT_SNK_BASED_ON_CC_BIT; + val->intval = chg->pd_hard_reset; return 0; } @@ -2543,53 +2508,60 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, const union power_supply_propval *val) { int rc; - u8 stat = 0; - bool cc_debounced; - bool orientation; - bool pd_active = val->intval; + bool orientation, cc_debounced, sink_attached, hvdcp; + u8 stat; - if (!get_effective_result(chg->pd_allowed_votable)) { - smblib_err(chg, "PD is not allowed\n"); + if (!get_effective_result(chg->pd_allowed_votable)) return -EINVAL; - } - vote(chg->apsd_disable_votable, PD_VOTER, pd_active, 0); - vote(chg->pd_allowed_votable, PD_VOTER, pd_active, 0); - vote(chg->usb_irq_enable_votable, PD_VOTER, pd_active, 0); - - /* - * VCONN_EN_ORIENTATION_BIT controls whether to use CC1 or CC2 line - * when TYPEC_SPARE_CFG_BIT (CC pin selection s/w override) is set - * or when VCONN_EN_VALUE_BIT is set. - */ - rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); + rc = smblib_read(chg, APSD_STATUS_REG, &stat); if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); + smblib_err(chg, "Couldn't read APSD status rc=%d\n", rc); return rc; } - if (pd_active) { - orientation = stat & CC_ORIENTATION_BIT; + cc_debounced = (bool) + (chg->typec_status[3] & TYPEC_DEBOUNCE_DONE_STATUS_BIT); + sink_attached = (bool) + (chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT); + hvdcp = stat & QC_CHARGER_BIT; + + chg->pd_active = val->intval; + if (chg->pd_active) { + vote(chg->apsd_disable_votable, PD_VOTER, true, 0); + vote(chg->pd_allowed_votable, PD_VOTER, true, 0); + vote(chg->usb_irq_enable_votable, PD_VOTER, true, 0); + + /* + * VCONN_EN_ORIENTATION_BIT controls whether to use CC1 or CC2 + * line when TYPEC_SPARE_CFG_BIT (CC pin selection s/w override) + * is set or when VCONN_EN_VALUE_BIT is set. + */ + orientation = chg->typec_status[3] & CC_ORIENTATION_BIT; rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, VCONN_EN_ORIENTATION_BIT, orientation ? 0 : VCONN_EN_ORIENTATION_BIT); - if (rc < 0) { + if (rc < 0) smblib_err(chg, "Couldn't enable vconn on CC line rc=%d\n", rc); - return rc; - } + + /* SW controlled CC_OUT */ + rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG, + TYPEC_SPARE_CFG_BIT, TYPEC_SPARE_CFG_BIT); + if (rc < 0) + smblib_err(chg, "Couldn't enable SW cc_out rc=%d\n", + rc); + /* * Enforce 500mA for PD until the real vote comes in later. * 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) { + if (rc < 0) smblib_err(chg, "Couldn't vote for USB ICL rc=%d\n", - rc); - return rc; - } + rc); /* since PD was found the cable must be non-legacy */ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0); @@ -2597,36 +2569,40 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, /* 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); + 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) { + if (rc < 0) smblib_err(chg, "Couldn't unvote USB_PSY rc=%d\n", rc); - return rc; - } - } + } else { + vote(chg->apsd_disable_votable, PD_VOTER, false, 0); + vote(chg->pd_allowed_votable, PD_VOTER, true, 0); + vote(chg->usb_irq_enable_votable, PD_VOTER, true, 0); + vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, + false, 0); - /* CC pin selection s/w override in PD session; h/w otherwise. */ - rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG, - TYPEC_SPARE_CFG_BIT, - pd_active ? TYPEC_SPARE_CFG_BIT : 0); - if (rc < 0) { - smblib_err(chg, "Couldn't change cc_out ctrl to %s rc=%d\n", - pd_active ? "SW" : "HW", rc); - return rc; - } + /* HW controlled CC_OUT */ + rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG, + TYPEC_SPARE_CFG_BIT, 0); + if (rc < 0) + smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n", + rc); - cc_debounced = (bool)(stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT); - if (!pd_active && cc_debounced) - try_rerun_apsd_for_hvdcp(chg); + /* + * This WA should only run for HVDCP. Non-legacy SDP/CDP could + * draw more, but this WA will remove Rd causing VBUS to drop, + * and data could be interrupted. Non-legacy DCP could also draw + * more, but it may impact compliance. + */ + if (!chg->typec_legacy_valid && cc_debounced && + !sink_attached && hvdcp) + schedule_work(&chg->legacy_detection_work); + } - chg->pd_active = pd_active; smblib_update_usb_type(chg); power_supply_changed(chg->usb_psy); - return rc; } @@ -2729,88 +2705,70 @@ static struct reg_info cc2_detach_settings[] = { static int smblib_cc2_sink_removal_enter(struct smb_charger *chg) { - int rc = 0; - union power_supply_propval cc2_val = {0, }; + int rc, ccout, ufp_mode; + u8 stat; if ((chg->wa_flags & TYPEC_CC2_REMOVAL_WA_BIT) == 0) - return rc; + return 0; - if (chg->cc2_sink_detach_flag != CC2_SINK_NONE) - return rc; + if (chg->cc2_detach_wa_active) + return 0; - rc = smblib_get_prop_typec_cc_orientation(chg, &cc2_val); + rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); if (rc < 0) { - smblib_err(chg, "Couldn't get cc orientation rc=%d\n", rc); + smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); return rc; } - if (cc2_val.intval == 1) - return rc; + ccout = (stat & CC_ATTACHED_BIT) ? + (!!(stat & CC_ORIENTATION_BIT) + 1) : 0; + ufp_mode = (stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT) ? + !(stat & UFP_DFP_MODE_STATUS_BIT) : 0; - rc = smblib_get_prop_typec_mode(chg, &cc2_val); - if (rc < 0) { - smblib_err(chg, "Couldn't get prop typec mode rc=%d\n", rc); - return rc; - } + if (ccout != 2) + return 0; - switch (cc2_val.intval) { - case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT: - smblib_reg_block_update(chg, cc2_detach_settings); - chg->cc2_sink_detach_flag = CC2_SINK_STD; - schedule_work(&chg->rdstd_cc2_detach_work); - break; - case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM: - case POWER_SUPPLY_TYPEC_SOURCE_HIGH: - chg->cc2_sink_detach_flag = CC2_SINK_MEDIUM_HIGH; - break; - default: - break; - } + if (!ufp_mode) + return 0; + chg->cc2_detach_wa_active = true; + /* The CC2 removal WA will cause a type-c-change IRQ storm */ + smblib_reg_block_update(chg, cc2_detach_settings); + schedule_work(&chg->rdstd_cc2_detach_work); return rc; } static int smblib_cc2_sink_removal_exit(struct smb_charger *chg) { - int rc = 0; - if ((chg->wa_flags & TYPEC_CC2_REMOVAL_WA_BIT) == 0) - return rc; - - if (chg->cc2_sink_detach_flag == CC2_SINK_STD) { - cancel_work_sync(&chg->rdstd_cc2_detach_work); - smblib_reg_block_restore(chg, cc2_detach_settings); - } + return 0; - chg->cc2_sink_detach_flag = CC2_SINK_NONE; + if (!chg->cc2_detach_wa_active) + return 0; - return rc; + chg->cc2_detach_wa_active = false; + cancel_work_sync(&chg->rdstd_cc2_detach_work); + smblib_reg_block_restore(chg, cc2_detach_settings); + return 0; } int smblib_set_prop_pd_in_hard_reset(struct smb_charger *chg, const union power_supply_propval *val) { - int rc; + int rc = 0; - rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, - EXIT_SNK_BASED_ON_CC_BIT, - (val->intval) ? EXIT_SNK_BASED_ON_CC_BIT : 0); - if (rc < 0) { - smblib_err(chg, "Could not set EXIT_SNK_BASED_ON_CC rc=%d\n", - rc); + if (chg->pd_hard_reset == val->intval) return rc; - } - vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, val->intval, 0); + chg->pd_hard_reset = val->intval; + rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + EXIT_SNK_BASED_ON_CC_BIT, + (chg->pd_hard_reset) ? EXIT_SNK_BASED_ON_CC_BIT : 0); + if (rc < 0) + smblib_err(chg, "Couldn't set EXIT_SNK_BASED_ON_CC rc=%d\n", + rc); - if (val->intval) - rc = smblib_cc2_sink_removal_enter(chg); - else - rc = smblib_cc2_sink_removal_exit(chg); - - if (rc < 0) { - smblib_err(chg, "Could not detect cc2 removal rc=%d\n", rc); - return rc; - } + vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, + chg->pd_hard_reset, 0); return rc; } @@ -3117,25 +3075,43 @@ irqreturn_t smblib_handle_usbin_uv(int irq, void *data) return IRQ_HANDLED; } +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; + smblib_update_usb_type(chg); + extcon_set_cable_state_(chg->extcon, EXTCON_USB, false); + smblib_uusb_removal(chg); + } +} + +static void smblib_typec_usb_plugin(struct smb_charger *chg, bool vbus_rising) +{ + if (vbus_rising) + smblib_cc2_sink_removal_exit(chg); + else + smblib_cc2_sink_removal_enter(chg); +} + #define PL_DELAY_MS 30000 -irqreturn_t smblib_handle_usb_plugin(int irq, void *data) +void smblib_usb_plugin_locked(struct smb_charger *chg) { - struct smb_irq_data *irq_data = data; - struct smb_charger *chg = irq_data->parent_data; int rc; u8 stat; bool vbus_rising; rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); - return IRQ_HANDLED; + smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); + return; } vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); - smblib_set_opt_freq_buck(chg, - vbus_rising ? chg->chg_freq.freq_5V : - chg->chg_freq.freq_removal); + smblib_set_opt_freq_buck(chg, vbus_rising ? chg->chg_freq.freq_5V : + chg->chg_freq.freq_removal); /* fetch the DPDM regulator */ if (!chg->dpdm_reg && of_get_property(chg->dev->of_node, @@ -3172,17 +3148,26 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n", rc); } - - if (chg->micro_usb_mode) { - smblib_update_usb_type(chg); - extcon_set_cable_state_(chg->extcon, EXTCON_USB, false); - smblib_uusb_removal(chg); - } } + if (chg->micro_usb_mode) + smblib_micro_usb_plugin(chg, vbus_rising); + else + smblib_typec_usb_plugin(chg, vbus_rising); + power_supply_changed(chg->usb_psy); - smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n", - irq_data->name, vbus_rising ? "attached" : "detached"); + smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n", + vbus_rising ? "attached" : "detached"); +} + +irqreturn_t smblib_handle_usb_plugin(int irq, void *data) +{ + struct smb_irq_data *irq_data = data; + struct smb_charger *chg = irq_data->parent_data; + + mutex_lock(&chg->lock); + smblib_usb_plugin_locked(chg); + mutex_unlock(&chg->lock); return IRQ_HANDLED; } @@ -3351,9 +3336,6 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg, if (rising) { vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, false, 0); - if (get_effective_result(chg->pd_disallowed_votable_indirect)) - /* could be a legacy cable, try doing hvdcp */ - try_rerun_apsd_for_hvdcp(chg); /* enable HDC and ICL irq for QC2/3 charger */ if (qc_charger) @@ -3388,6 +3370,10 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg, static void smblib_force_legacy_icl(struct smb_charger *chg, int pst) { + /* while PD is active it should have complete ICL control */ + if (chg->pd_active) + return; + switch (pst) { case POWER_SUPPLY_TYPE_USB: /* @@ -3427,7 +3413,7 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) apsd_result = smblib_update_usb_type(chg); - if (!chg->pd_active) + if (!chg->typec_legacy_valid) smblib_force_legacy_icl(chg, apsd_result->pst); switch (apsd_result->bit) { @@ -3469,6 +3455,17 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data) } smblib_dbg(chg, PR_REGISTER, "APSD_STATUS = 0x%02x\n", stat); + if (chg->micro_usb_mode && (stat & APSD_DTC_STATUS_DONE_BIT) + && !chg->uusb_apsd_rerun_done) { + /* + * Force re-run APSD to handle slow insertion related + * charger-mis-detection. + */ + chg->uusb_apsd_rerun_done = true; + smblib_rerun_apsd(chg); + return IRQ_HANDLED; + } + smblib_handle_apsd_done(chg, (bool)(stat & APSD_DTC_STATUS_DONE_BIT)); @@ -3502,71 +3499,6 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data) return IRQ_HANDLED; } -static void typec_source_removal(struct smb_charger *chg) -{ - int rc; - - /* reset legacy unknown vote */ - vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0); - - /* 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); - - cancel_delayed_work_sync(&chg->hvdcp_detect_work); - - if (chg->wa_flags & QC_AUTH_INTERRUPT_WA_BIT) { - /* re-enable AUTH_IRQ_EN_CFG_BIT */ - rc = smblib_masked_write(chg, - USBIN_SOURCE_CHANGE_INTRPT_ENB_REG, - AUTH_IRQ_EN_CFG_BIT, AUTH_IRQ_EN_CFG_BIT); - if (rc < 0) - smblib_err(chg, - "Couldn't enable QC auth setting rc=%d\n", rc); - } - - /* reconfigure allowed voltage for HVDCP */ - rc = smblib_set_adapter_allowance(chg, - USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V); - if (rc < 0) - smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n", - rc); - - chg->voltage_min_uv = MICRO_5V; - chg->voltage_max_uv = MICRO_5V; - - /* clear USB ICL vote for PD_VOTER */ - rc = vote(chg->usb_icl_votable, PD_VOTER, false, 0); - if (rc < 0) - smblib_err(chg, "Couldn't un-vote PD from USB ICL rc=%d\n", rc); - - /* clear USB ICL vote for USB_PSY_VOTER */ - rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); - if (rc < 0) - smblib_err(chg, - "Couldn't un-vote USB_PSY from 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); - -} - -static void typec_source_insertion(struct smb_charger *chg) -{ - /* - * at any time we want LEGACY_UNKNOWN, PD, or USB_PSY to be voting for - * ICL, so vote LEGACY_UNKNOWN here if none of the above three have - * casted their votes - */ - if (!is_client_vote_enabled(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER) - && !is_client_vote_enabled(chg->usb_icl_votable, PD_VOTER) - && !is_client_vote_enabled(chg->usb_icl_votable, USB_PSY_VOTER)) - vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000); -} - static void typec_sink_insertion(struct smb_charger *chg) { /* when a sink is inserted we should not wait on hvdcp timeout to @@ -3587,30 +3519,50 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) { int rc; + chg->cc2_detach_wa_active = false; + + /* reset APSD voters */ + vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, false, 0); + vote(chg->apsd_disable_votable, PD_VOTER, false, 0); + cancel_delayed_work_sync(&chg->pl_enable_work); - vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0); - vote(chg->awake_votable, PL_DELAY_VOTER, false, 0); + cancel_delayed_work_sync(&chg->hvdcp_detect_work); + + /* reset input current limit voters */ + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000); + 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); + vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, false, 0); + /* reset hvdcp voters */ + vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0); + vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, true, 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, HVDCP_TIMEOUT_VOTER, true, 0); + + /* reset usb irq voters */ vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0); vote(chg->usb_irq_enable_votable, QC_VOTER, false, 0); - /* reset votes from vbus_cc_short */ - vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, - true, 0); - vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, - true, 0); - /* - * cable could be removed during hard reset, remove its vote to - * disable apsd - */ - vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, false, 0); + /* reset parallel voters */ + vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0); + vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0); + 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; + chg->typec_legacy_valid = false; /* enable APSD CC trigger for next insertion */ rc = smblib_masked_write(chg, TYPE_C_CFG_REG, @@ -3618,15 +3570,48 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) if (rc < 0) smblib_err(chg, "Couldn't enable APSD_START_ON_CC rc=%d\n", rc); - smblib_update_usb_type(chg); - typec_source_removal(chg); + if (chg->wa_flags & QC_AUTH_INTERRUPT_WA_BIT) { + /* re-enable AUTH_IRQ_EN_CFG_BIT */ + rc = smblib_masked_write(chg, + USBIN_SOURCE_CHANGE_INTRPT_ENB_REG, + AUTH_IRQ_EN_CFG_BIT, AUTH_IRQ_EN_CFG_BIT); + if (rc < 0) + smblib_err(chg, + "Couldn't enable QC auth setting rc=%d\n", rc); + } + + /* reconfigure allowed voltage for HVDCP */ + rc = smblib_set_adapter_allowance(chg, + USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V); + if (rc < 0) + 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_INTRPT_ENB_SOFTWARE_CTRL_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, TAPER_TIMER_SEL_CFG_REG, + TYPEC_SPARE_CFG_BIT, 0); + if (rc < 0) + smblib_err(chg, "Couldn't enable HW cc_out rc=%d\n", rc); + + /* restore crude sensor */ + rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0xA5); + if (rc < 0) + smblib_err(chg, "Couldn't restore crude sensor rc=%d\n", rc); + typec_sink_removal(chg); + smblib_update_usb_type(chg); } static void smblib_handle_typec_insertion(struct smb_charger *chg, - bool sink_attached, bool legacy_cable) + bool sink_attached) { - int rp, rc; + int rc; vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, false, 0); @@ -3636,59 +3621,36 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg, smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n", rc); - if (sink_attached) { - typec_source_removal(chg); + if (sink_attached) typec_sink_insertion(chg); - } else { - typec_source_insertion(chg); + else typec_sink_removal(chg); - } - - rp = smblib_get_prop_ufp_mode(chg); - if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH - || rp == POWER_SUPPLY_TYPEC_NON_COMPLIANT) { - smblib_dbg(chg, PR_MISC, "VBUS & CC could be shorted; keeping HVDCP disabled\n"); - vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, - true, 0); - } else { - vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, - false, 0); - } } static void smblib_handle_typec_debounce_done(struct smb_charger *chg, - bool rising, bool sink_attached, bool legacy_cable) + bool rising, bool sink_attached) { int rc; union power_supply_propval pval = {0, }; - if (rising) - smblib_handle_typec_insertion(chg, sink_attached, legacy_cable); - else - smblib_handle_typec_removal(chg); + if (rising) { + if (!chg->typec_present) { + chg->typec_present = true; + smblib_dbg(chg, PR_MISC, "TypeC insertion\n"); + smblib_handle_typec_insertion(chg, sink_attached); + } + } else { + if (chg->typec_present) { + chg->typec_present = false; + smblib_dbg(chg, PR_MISC, "TypeC removal\n"); + smblib_handle_typec_removal(chg); + } + } rc = smblib_get_prop_typec_mode(chg, &pval); if (rc < 0) smblib_err(chg, "Couldn't get prop typec mode rc=%d\n", rc); - /* - * HW BUG - after cable is removed, medium or high rd reading - * falls to std. Use it for signal of typec cc detachment in - * software WA. - */ - if (chg->cc2_sink_detach_flag == CC2_SINK_MEDIUM_HIGH - && pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) { - - chg->cc2_sink_detach_flag = CC2_SINK_WA_DONE; - - rc = smblib_masked_write(chg, - TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, - EXIT_SNK_BASED_ON_CC_BIT, 0); - if (rc < 0) - smblib_err(chg, "Couldn't get prop typec mode rc=%d\n", - rc); - } - smblib_dbg(chg, PR_INTERRUPT, "IRQ: debounce-done %s; Type-C %s detected\n", rising ? "rising" : "falling", smblib_typec_mode_name[pval.intval]); @@ -3714,50 +3676,54 @@ irqreturn_t smblib_handle_usb_typec_change_for_uusb(struct smb_charger *chg) return IRQ_HANDLED; } -irqreturn_t smblib_handle_usb_typec_change(int irq, void *data) +static void smblib_usb_typec_change(struct smb_charger *chg) { - struct smb_irq_data *irq_data = data; - struct smb_charger *chg = irq_data->parent_data; int rc; - u8 stat4, stat5; - bool debounce_done, sink_attached, legacy_cable; - - if (chg->micro_usb_mode) - return smblib_handle_usb_typec_change_for_uusb(chg); - - /* WA - not when PD hard_reset WIP on cc2 in sink mode */ - if (chg->cc2_sink_detach_flag == CC2_SINK_STD) - return IRQ_HANDLED; - - rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat4); - if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); - return IRQ_HANDLED; - } + bool debounce_done, sink_attached; - rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat5); + rc = smblib_multibyte_read(chg, TYPE_C_STATUS_1_REG, + chg->typec_status, 5); if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_5 rc=%d\n", rc); - return IRQ_HANDLED; + smblib_err(chg, "Couldn't cache USB Type-C status rc=%d\n", rc); + return; } - debounce_done = (bool)(stat4 & TYPEC_DEBOUNCE_DONE_STATUS_BIT); - sink_attached = (bool)(stat4 & UFP_DFP_MODE_STATUS_BIT); - legacy_cable = (bool)(stat5 & TYPEC_LEGACY_CABLE_STATUS_BIT); + debounce_done = + (bool)(chg->typec_status[3] & TYPEC_DEBOUNCE_DONE_STATUS_BIT); + sink_attached = + (bool)(chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT); - smblib_handle_typec_debounce_done(chg, - debounce_done, sink_attached, legacy_cable); + smblib_handle_typec_debounce_done(chg, debounce_done, sink_attached); - if (stat4 & TYPEC_VBUS_ERROR_STATUS_BIT) - smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s vbus-error\n", - irq_data->name); + if (chg->typec_status[3] & TYPEC_VBUS_ERROR_STATUS_BIT) + smblib_dbg(chg, PR_INTERRUPT, "IRQ: vbus-error\n"); - if (stat4 & TYPEC_VCONN_OVERCURR_STATUS_BIT) + if (chg->typec_status[3] & TYPEC_VCONN_OVERCURR_STATUS_BIT) schedule_work(&chg->vconn_oc_work); power_supply_changed(chg->usb_psy); - smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n", stat4); - smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_5 = 0x%02x\n", stat5); +} + +irqreturn_t smblib_handle_usb_typec_change(int irq, void *data) +{ + struct smb_irq_data *irq_data = data; + struct smb_charger *chg = irq_data->parent_data; + + if (chg->micro_usb_mode) { + smblib_handle_usb_typec_change_for_uusb(chg); + return IRQ_HANDLED; + } + + if (chg->cc2_detach_wa_active || chg->typec_en_dis_active) { + smblib_dbg(chg, PR_INTERRUPT, "Ignoring since %s active\n", + chg->cc2_detach_wa_active ? + "cc2_detach_wa" : "typec_en_dis"); + return IRQ_HANDLED; + } + + mutex_lock(&chg->lock); + smblib_usb_typec_change(chg); + mutex_unlock(&chg->lock); return IRQ_HANDLED; } @@ -3785,7 +3751,7 @@ irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; - int rc; + int rc, usb_icl; u8 stat; if (!(chg->wa_flags & BOOST_BACK_WA)) @@ -3797,8 +3763,9 @@ irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data) return IRQ_HANDLED; } - if ((stat & USE_USBIN_BIT) && - get_effective_result(chg->usb_icl_votable) < USBIN_25MA) + /* skip suspending input if its already suspended by some other voter */ + usb_icl = get_effective_result(chg->usb_icl_votable); + if ((stat & USE_USBIN_BIT) && usb_icl >= 0 && usb_icl < USBIN_25MA) return IRQ_HANDLED; if (stat & USE_DCIN_BIT) @@ -3836,12 +3803,7 @@ static void smblib_hvdcp_detect_work(struct work_struct *work) vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, false, 0); - if (get_effective_result(chg->pd_disallowed_votable_indirect)) - /* pd is still disabled, try hvdcp */ - try_rerun_apsd_for_hvdcp(chg); - else - /* notify pd now that pd is allowed */ - power_supply_changed(chg->usb_psy); + power_supply_changed(chg->usb_psy); } static void bms_update_work(struct work_struct *work) @@ -3882,11 +3844,13 @@ static void clear_hdc_work(struct work_struct *work) static void rdstd_cc2_detach_work(struct work_struct *work) { int rc; - u8 stat; - struct smb_irq_data irq_data = {NULL, "cc2-removal-workaround"}; + u8 stat4, stat5; struct smb_charger *chg = container_of(work, struct smb_charger, rdstd_cc2_detach_work); + if (!chg->cc2_detach_wa_active) + return; + /* * WA steps - * 1. Enable both UFP and DFP, wait for 10ms. @@ -3894,7 +3858,7 @@ static void rdstd_cc2_detach_work(struct work_struct *work) * 3. Removal detected if both TYPEC_DEBOUNCE_DONE_STATUS * and TIMER_STAGE bits are gone, otherwise repeat all by * work rescheduling. - * Note, work will be cancelled when pd_hard_reset is 0. + * Note, work will be cancelled when USB_PLUGIN rises. */ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, @@ -3917,30 +3881,35 @@ static void rdstd_cc2_detach_work(struct work_struct *work) usleep_range(30000, 31000); - rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); + rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat4); if (rc < 0) { - smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", - rc); + smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); return; } - if (stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT) - goto rerun; - rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat); + rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat5); if (rc < 0) { smblib_err(chg, "Couldn't read TYPE_C_STATUS_5_REG rc=%d\n", rc); return; } - if (stat & TIMER_STAGE_2_BIT) + + if ((stat4 & TYPEC_DEBOUNCE_DONE_STATUS_BIT) + || (stat5 & TIMER_STAGE_2_BIT)) { + smblib_dbg(chg, PR_MISC, "rerunning DD=%d TS2BIT=%d\n", + (int)(stat4 & TYPEC_DEBOUNCE_DONE_STATUS_BIT), + (int)(stat5 & TIMER_STAGE_2_BIT)); goto rerun; + } - /* Bingo, cc2 removal detected */ + smblib_dbg(chg, PR_MISC, "Bingo CC2 Removal detected\n"); + chg->cc2_detach_wa_active = false; + rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + EXIT_SNK_BASED_ON_CC_BIT, 0); smblib_reg_block_restore(chg, cc2_detach_settings); - chg->cc2_sink_detach_flag = CC2_SINK_WA_DONE; - irq_data.parent_data = chg; - smblib_handle_usb_typec_change(0, &irq_data); - + mutex_lock(&chg->lock); + smblib_usb_typec_change(chg); + mutex_unlock(&chg->lock); return; rerun: @@ -4163,6 +4132,56 @@ static void smblib_pl_enable_work(struct work_struct *work) vote(chg->awake_votable, PL_DELAY_VOTER, false, 0); } +static void smblib_legacy_detection_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + legacy_detection_work); + int rc; + u8 stat; + bool legacy, rp_high; + + mutex_lock(&chg->lock); + chg->typec_en_dis_active = 1; + smblib_dbg(chg, PR_MISC, "running legacy unknown workaround\n"); + rc = smblib_masked_write(chg, + TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_DISABLE_CMD_BIT, + TYPEC_DISABLE_CMD_BIT); + if (rc < 0) + smblib_err(chg, "Couldn't disable type-c rc=%d\n", rc); + + /* wait for the adapter to turn off VBUS */ + msleep(500); + + rc = smblib_masked_write(chg, + TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_DISABLE_CMD_BIT, 0); + if (rc < 0) + smblib_err(chg, "Couldn't enable type-c rc=%d\n", rc); + + /* wait for type-c detection to complete */ + msleep(100); + + rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read typec stat5 rc = %d\n", rc); + goto unlock; + } + + chg->typec_legacy_valid = true; + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0); + legacy = stat & TYPEC_LEGACY_CABLE_STATUS_BIT; + rp_high = smblib_get_prop_ufp_mode(chg) == + POWER_SUPPLY_TYPEC_SOURCE_HIGH; + if (!legacy || !rp_high) + vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, + false, 0); + +unlock: + chg->typec_en_dis_active = 0; + mutex_unlock(&chg->lock); +} + static int smblib_create_votables(struct smb_charger *chg) { int rc = 0; @@ -4295,6 +4314,15 @@ static int smblib_create_votables(struct smb_charger *chg) return rc; } + chg->typec_irq_disable_votable = create_votable("TYPEC_IRQ_DISABLE", + VOTE_SET_ANY, + smblib_typec_irq_disable_vote_callback, + chg); + if (IS_ERR(chg->typec_irq_disable_votable)) { + rc = PTR_ERR(chg->typec_irq_disable_votable); + return rc; + } + return rc; } @@ -4320,6 +4348,8 @@ static void smblib_destroy_votables(struct smb_charger *chg) destroy_votable(chg->apsd_disable_votable); if (chg->hvdcp_hw_inov_dis_votable) destroy_votable(chg->hvdcp_hw_inov_dis_votable); + if (chg->typec_irq_disable_votable) + destroy_votable(chg->typec_irq_disable_votable); } static void smblib_iio_deinit(struct smb_charger *chg) @@ -4340,6 +4370,7 @@ int smblib_init(struct smb_charger *chg) { int rc = 0; + mutex_init(&chg->lock); mutex_init(&chg->write_lock); mutex_init(&chg->otg_oc_lock); INIT_WORK(&chg->bms_update_work, bms_update_work); @@ -4352,6 +4383,7 @@ int smblib_init(struct smb_charger *chg) INIT_DELAYED_WORK(&chg->otg_ss_done_work, smblib_otg_ss_done_work); INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work); INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work); + INIT_WORK(&chg->legacy_detection_work, smblib_legacy_detection_work); chg->fake_capacity = -EINVAL; switch (chg->mode) { diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index 49b9d3da783c..b0d84f014b0d 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -61,6 +61,7 @@ enum print_reason { #define SW_QC3_VOTER "SW_QC3_VOTER" #define AICL_RERUN_VOTER "AICL_RERUN_VOTER" #define LEGACY_UNKNOWN_VOTER "LEGACY_UNKNOWN_VOTER" +#define CC2_WA_VOTER "CC2_WA_VOTER" #define VCONN_MAX_ATTEMPTS 3 #define OTG_MAX_ATTEMPTS 3 @@ -71,13 +72,6 @@ enum smb_mode { NUM_MODES, }; -enum cc2_sink_type { - CC2_SINK_NONE = 0, - CC2_SINK_STD, - CC2_SINK_MEDIUM_HIGH, - CC2_SINK_WA_DONE, -}; - enum { QC_CHARGER_DETECTION_WA_BIT = BIT(0), BOOST_BACK_WA = BIT(1), @@ -236,6 +230,7 @@ struct smb_charger { int smb_version; /* locks */ + struct mutex lock; struct mutex write_lock; struct mutex ps_change_lock; struct mutex otg_oc_lock; @@ -276,6 +271,7 @@ struct smb_charger { struct votable *apsd_disable_votable; struct votable *hvdcp_hw_inov_dis_votable; struct votable *usb_irq_enable_votable; + struct votable *typec_irq_disable_votable; /* work */ struct work_struct bms_update_work; @@ -289,6 +285,7 @@ struct smb_charger { struct delayed_work otg_ss_done_work; struct delayed_work icl_change_work; struct delayed_work pl_enable_work; + struct work_struct legacy_detection_work; /* cached status */ int voltage_min_uv; @@ -312,10 +309,16 @@ struct smb_charger { int vconn_attempts; int default_icl_ua; int otg_cl_ua; + bool uusb_apsd_rerun_done; + bool pd_hard_reset; + bool typec_present; + u8 typec_status[5]; + bool typec_legacy_valid; /* workaround flag */ u32 wa_flags; - enum cc2_sink_type cc2_sink_detach_flag; + bool cc2_detach_wa_active; + bool typec_en_dis_active; int boost_current_ua; int temp_speed_reading_count; diff --git a/drivers/power/supply/qcom/smb-reg.h b/drivers/power/supply/qcom/smb-reg.h index 167666a8c548..3f260a407721 100644 --- a/drivers/power/supply/qcom/smb-reg.h +++ b/drivers/power/supply/qcom/smb-reg.h @@ -1025,4 +1025,14 @@ enum { /* CHGR FREQ Peripheral registers */ #define FREQ_CLK_DIV_REG (CHGR_FREQ_BASE + 0x50) +/* SMB1355 specific registers */ +#define SMB1355_TEMP_COMP_STATUS_REG (MISC_BASE + 0x07) +#define SKIN_TEMP_RST_HOT_BIT BIT(6) +#define SKIN_TEMP_UB_HOT_BIT BIT(5) +#define SKIN_TEMP_LB_HOT_BIT BIT(4) +#define DIE_TEMP_TSD_HOT_BIT BIT(3) +#define DIE_TEMP_RST_HOT_BIT BIT(2) +#define DIE_TEMP_UB_HOT_BIT BIT(1) +#define DIE_TEMP_LB_HOT_BIT BIT(0) + #endif /* __SMB2_CHARGER_REG_H */ diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c index 694591c3ec56..4e710cae6b78 100644 --- a/drivers/power/supply/qcom/smb138x-charger.c +++ b/drivers/power/supply/qcom/smb138x-charger.c @@ -104,6 +104,8 @@ struct smb138x { struct smb_dt_props dt; struct power_supply *parallel_psy; u32 wa_flags; + struct pmic_revid_data *pmic_rev_id; + char *name; }; static int __debug_mask; @@ -167,6 +169,14 @@ static int smb138x_parse_dt(struct smb138x *chip) if (rc < 0) chip->dt.pl_mode = POWER_SUPPLY_PL_USBMID_USBMID; + /* check that smb1355 is configured to run in mid-mid mode */ + if (chip->pmic_rev_id->pmic_subtype == SMB1355_SUBTYPE + && chip->dt.pl_mode != POWER_SUPPLY_PL_USBMID_USBMID) { + pr_err("Smb1355 can only run in MID-MID mode, saw = %d mode\n", + chip->dt.pl_mode); + return -EINVAL; + } + chip->dt.suspend_input = of_property_read_bool(node, "qcom,suspend-input"); @@ -479,6 +489,30 @@ static int smb138x_init_batt_psy(struct smb138x *chip) * PARALLEL PSY REGISTRATION * *****************************/ +static int smb1355_get_prop_connector_health(struct smb138x *chip) +{ + struct smb_charger *chg = &chip->chg; + u8 temp; + int rc; + + rc = smblib_read(chg, SMB1355_TEMP_COMP_STATUS_REG, &temp); + if (rc < 0) { + pr_err("Couldn't read comp stat reg rc = %d\n", rc); + return POWER_SUPPLY_HEALTH_UNKNOWN; + } + + if (temp & SKIN_TEMP_RST_HOT_BIT) + return POWER_SUPPLY_HEALTH_OVERHEAT; + + if (temp & SKIN_TEMP_UB_HOT_BIT) + return POWER_SUPPLY_HEALTH_HOT; + + if (temp & SKIN_TEMP_LB_HOT_BIT) + return POWER_SUPPLY_HEALTH_WARM; + + return POWER_SUPPLY_HEALTH_COOL; +} + static int smb138x_get_prop_connector_health(struct smb138x *chip) { struct smb_charger *chg = &chip->chg; @@ -536,16 +570,32 @@ static enum power_supply_property smb138x_parallel_props[] = { POWER_SUPPLY_PROP_PIN_ENABLED, POWER_SUPPLY_PROP_INPUT_SUSPEND, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, - POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_VOLTAGE_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, - POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_PARALLEL_MODE, + POWER_SUPPLY_PROP_CONNECTOR_HEALTH, + POWER_SUPPLY_PROP_SET_SHIP_MODE, POWER_SUPPLY_PROP_CHARGER_TEMP, POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_MAX, +}; + +static enum power_supply_property smb1355_parallel_props[] = { + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_CHARGING_ENABLED, + POWER_SUPPLY_PROP_PIN_ENABLED, + POWER_SUPPLY_PROP_INPUT_SUSPEND, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_PARALLEL_MODE, POWER_SUPPLY_PROP_CONNECTOR_HEALTH, POWER_SUPPLY_PROP_SET_SHIP_MODE, + POWER_SUPPLY_PROP_CHARGER_TEMP, + POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, }; static int smb138x_parallel_get_prop(struct power_supply *psy, @@ -583,14 +633,6 @@ static int smb138x_parallel_get_prop(struct power_supply *psy, else val->intval = 0; break; - case POWER_SUPPLY_PROP_CURRENT_MAX: - if ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) - || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) - rc = smblib_get_charge_param(chg, &chg->param.usb_icl, - &val->intval); - else - val->intval = 0; - break; case POWER_SUPPLY_PROP_VOLTAGE_MAX: rc = smblib_get_charge_param(chg, &chg->param.fv, &val->intval); break; @@ -598,28 +640,46 @@ static int smb138x_parallel_get_prop(struct power_supply *psy, rc = smblib_get_charge_param(chg, &chg->param.fcc, &val->intval); break; - case POWER_SUPPLY_PROP_CURRENT_NOW: - rc = smblib_get_prop_slave_current_now(chg, val); - break; - case POWER_SUPPLY_PROP_CHARGER_TEMP: - rc = smb138x_get_prop_charger_temp(chip, val); - break; - case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX: - rc = smblib_get_prop_charger_temp_max(chg, val); - break; case POWER_SUPPLY_PROP_MODEL_NAME: - val->strval = "smb138x"; + val->strval = chip->name; break; case POWER_SUPPLY_PROP_PARALLEL_MODE: val->intval = chip->dt.pl_mode; break; case POWER_SUPPLY_PROP_CONNECTOR_HEALTH: - val->intval = smb138x_get_prop_connector_health(chip); + if (chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE) + val->intval = smb138x_get_prop_connector_health(chip); + else + val->intval = smb1355_get_prop_connector_health(chip); break; case POWER_SUPPLY_PROP_SET_SHIP_MODE: /* Not in ship mode as long as device is active */ val->intval = 0; break; + case POWER_SUPPLY_PROP_CHARGER_TEMP: + if (chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE) + rc = smb138x_get_prop_charger_temp(chip, val); + else + rc = smblib_get_prop_charger_temp(chg, val); + break; + case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX: + rc = smblib_get_prop_charger_temp_max(chg, val); + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + if (chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE) + rc = smblib_get_prop_slave_current_now(chg, val); + else + rc = -ENODATA; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + if ((chip->pmic_rev_id->pmic_subtype != SMB1355_SUBTYPE) + && ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) + || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT))) + rc = smblib_get_charge_param(chg, &chg->param.usb_icl, + &val->intval); + else + rc = -ENODATA; + break; default: pr_err("parallel power supply get prop %d not supported\n", prop); @@ -703,7 +763,7 @@ static int smb138x_parallel_prop_is_writeable(struct power_supply *psy, return 0; } -static const struct power_supply_desc parallel_psy_desc = { +static struct power_supply_desc parallel_psy_desc = { .name = "parallel", .type = POWER_SUPPLY_TYPE_PARALLEL, .properties = smb138x_parallel_props, @@ -731,6 +791,28 @@ static int smb138x_init_parallel_psy(struct smb138x *chip) return 0; } +static int smb1355_init_parallel_psy(struct smb138x *chip) +{ + struct power_supply_config parallel_cfg = {}; + struct smb_charger *chg = &chip->chg; + + parallel_cfg.drv_data = chip; + parallel_cfg.of_node = chg->dev->of_node; + + /* change to smb1355's property list */ + parallel_psy_desc.properties = smb1355_parallel_props; + parallel_psy_desc.num_properties = ARRAY_SIZE(smb1355_parallel_props); + chip->parallel_psy = devm_power_supply_register(chg->dev, + ¶llel_psy_desc, + ¶llel_cfg); + if (IS_ERR(chip->parallel_psy)) { + pr_err("Couldn't register parallel power supply\n"); + return PTR_ERR(chip->parallel_psy); + } + + return 0; +} + /****************************** * VBUS REGULATOR REGISTRATION * ******************************/ @@ -1050,7 +1132,6 @@ static int smb138x_init_hw(struct smb138x *chip) static int smb138x_setup_wa_flags(struct smb138x *chip) { - struct pmic_revid_data *pmic_rev_id; struct device_node *revid_dev_node; revid_dev_node = of_parse_phandle(chip->chg.dev->of_node, @@ -1060,8 +1141,8 @@ static int smb138x_setup_wa_flags(struct smb138x *chip) return -EINVAL; } - pmic_rev_id = get_revid_data(revid_dev_node); - if (IS_ERR_OR_NULL(pmic_rev_id)) { + chip->pmic_rev_id = get_revid_data(revid_dev_node); + if (IS_ERR_OR_NULL(chip->pmic_rev_id)) { /* * the revid peripheral must be registered, any failure * here only indicates that the rev-id module has not @@ -1070,14 +1151,14 @@ static int smb138x_setup_wa_flags(struct smb138x *chip) return -EPROBE_DEFER; } - switch (pmic_rev_id->pmic_subtype) { + switch (chip->pmic_rev_id->pmic_subtype) { case SMB1381_SUBTYPE: - if (pmic_rev_id->rev4 < 2) /* SMB1381 rev 1.0 */ + if (chip->pmic_rev_id->rev4 < 2) /* SMB1381 rev 1.0 */ chip->wa_flags |= OOB_COMP_WA_BIT; break; default: pr_err("PMIC subtype %d not supported\n", - pmic_rev_id->pmic_subtype); + chip->pmic_rev_id->pmic_subtype); return -EINVAL; } @@ -1375,6 +1456,7 @@ static int smb138x_master_probe(struct smb138x *chip) chg->param = v1_params; + chip->name = "smb1381"; rc = smblib_init(chg); if (rc < 0) { pr_err("Couldn't initialize smblib rc=%d\n", rc); @@ -1435,7 +1517,7 @@ static int smb138x_master_probe(struct smb138x *chip) return rc; } -static int smb138x_slave_probe(struct smb138x *chip) +static int smb1355_slave_probe(struct smb138x *chip) { struct smb_charger *chg = &chip->chg; int rc = 0; @@ -1448,6 +1530,55 @@ static int smb138x_slave_probe(struct smb138x *chip) goto cleanup; } + rc = smb138x_parse_dt(chip); + if (rc < 0) { + pr_err("Couldn't parse device tree rc=%d\n", rc); + goto cleanup; + } + + rc = smb138x_init_slave_hw(chip); + if (rc < 0) { + pr_err("Couldn't initialize hardware rc=%d\n", rc); + goto cleanup; + } + + rc = smb1355_init_parallel_psy(chip); + if (rc < 0) { + pr_err("Couldn't initialize parallel psy rc=%d\n", rc); + goto cleanup; + } + + rc = smb138x_determine_initial_slave_status(chip); + if (rc < 0) { + pr_err("Couldn't determine initial status rc=%d\n", rc); + goto cleanup; + } + + rc = smb138x_request_interrupts(chip); + if (rc < 0) { + pr_err("Couldn't request interrupts rc=%d\n", rc); + goto cleanup; + } + + return 0; + +cleanup: + smblib_deinit(chg); + return rc; +} + +static int smb1381_slave_probe(struct smb138x *chip) +{ + struct smb_charger *chg = &chip->chg; + int rc = 0; + + chg->param = v1_params; + + rc = smblib_init(chg); + if (rc < 0) { + pr_err("Couldn't initialize smblib rc=%d\n", rc); + goto cleanup; + } chg->iio.temp_max_chan = iio_channel_get(chg->dev, "charger_temp_max"); if (IS_ERR(chg->iio.temp_max_chan)) { rc = PTR_ERR(chg->iio.temp_max_chan); @@ -1515,25 +1646,71 @@ static int smb138x_slave_probe(struct smb138x *chip) goto cleanup; } - return rc; + return 0; cleanup: smblib_deinit(chg); - if (chip->parallel_psy) - power_supply_unregister(chip->parallel_psy); - if (chg->vbus_vreg && chg->vbus_vreg->rdev) - regulator_unregister(chg->vbus_vreg->rdev); return rc; } +static int slave_probe(struct smb138x *chip) +{ + struct device_node *revid_dev_node; + int rc = 0; + + revid_dev_node = of_parse_phandle(chip->chg.dev->of_node, + "qcom,pmic-revid", 0); + if (!revid_dev_node) { + pr_err("Missing qcom,pmic-revid property\n"); + return -EINVAL; + } + + chip->pmic_rev_id = get_revid_data(revid_dev_node); + if (IS_ERR_OR_NULL(chip->pmic_rev_id)) { + /* + * the revid peripheral must be registered, any failure + * here only indicates that the rev-id module has not + * probed yet. + */ + return -EPROBE_DEFER; + } + + switch (chip->pmic_rev_id->pmic_subtype) { + case SMB1355_SUBTYPE: + chip->name = "smb1355"; + rc = smb1355_slave_probe(chip); + break; + case SMB1381_SUBTYPE: + chip->name = "smb1381"; + rc = smb1381_slave_probe(chip); + break; + default: + pr_err("Unsupported pmic subtype = 0x%02x\n", + chip->pmic_rev_id->pmic_subtype); + rc = -EINVAL; + } + + if (rc < 0) { + if (rc != -EPROBE_DEFER) + pr_err("Couldn't probe SMB138X rc=%d\n", rc); + return rc; + } + + return 0; +} + static const struct of_device_id match_table[] = { { - .compatible = "qcom,smb138x-charger", - .data = (void *) PARALLEL_MASTER + .compatible = "qcom,smb138x-charger", + .data = (void *) PARALLEL_MASTER, + }, + { + .compatible = "qcom,smb138x-parallel-slave", + .data = (void *) PARALLEL_SLAVE, }, { - .compatible = "qcom,smb138x-parallel-slave", - .data = (void *) PARALLEL_SLAVE + .compatible = "qcom,smb1355-parallel-slave", + .data = (void *) PARALLEL_SLAVE, }, { }, }; @@ -1580,7 +1757,7 @@ static int smb138x_probe(struct platform_device *pdev) rc = smb138x_master_probe(chip); break; case PARALLEL_SLAVE: - rc = smb138x_slave_probe(chip); + rc = slave_probe(chip); break; default: pr_err("Couldn't find a matching mode %d\n", chip->chg.mode); @@ -1594,7 +1771,8 @@ static int smb138x_probe(struct platform_device *pdev) goto cleanup; } - pr_info("SMB138X probed successfully mode=%d\n", chip->chg.mode); + pr_info("%s probed successfully mode=%d pl_mode = %d\n", + chip->name, chip->chg.mode, chip->dt.pl_mode); return rc; cleanup: diff --git a/drivers/regulator/qpnp-labibb-regulator.c b/drivers/regulator/qpnp-labibb-regulator.c index dbe2a08f1776..858ddcc228df 100644 --- a/drivers/regulator/qpnp-labibb-regulator.c +++ b/drivers/regulator/qpnp-labibb-regulator.c @@ -559,6 +559,7 @@ struct lab_regulator { int step_size; int slew_rate; int soft_start; + int sc_wait_time_ms; int vreg_enabled; }; @@ -608,6 +609,8 @@ struct qpnp_labibb { bool skip_2nd_swire_cmd; bool pfm_enable; bool notify_lab_vreg_ok_sts; + bool detect_lab_sc; + bool sc_detected; u32 swire_2nd_cmd_delay; u32 swire_ibb_ps_enable_delay; }; @@ -2138,8 +2141,10 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work) u8 val; struct qpnp_labibb *labibb = container_of(work, struct qpnp_labibb, lab_vreg_ok_work); + if (labibb->lab_vreg.sc_wait_time_ms != -EINVAL) + retries = labibb->lab_vreg.sc_wait_time_ms / 5; - while (retries--) { + while (retries) { rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_STATUS1, &val, 1); if (rc < 0) { @@ -2155,10 +2160,30 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work) } usleep_range(dly, dly + 100); + retries--; } - if (!retries) - pr_err("LAB_VREG_OK not set, failed to notify\n"); + if (!retries) { + if (labibb->detect_lab_sc) { + pr_crit("short circuit detected on LAB rail.. disabling the LAB/IBB/OLEDB modules\n"); + /* Disable LAB module */ + val = 0; + rc = qpnp_labibb_write(labibb, labibb->lab_base + + REG_LAB_MODULE_RDY, &val, 1); + if (rc < 0) { + pr_err("write register %x failed rc = %d\n", + REG_LAB_MODULE_RDY, rc); + return; + } + raw_notifier_call_chain(&labibb_notifier, + LAB_VREG_NOT_OK, NULL); + labibb->sc_detected = true; + labibb->lab_vreg.vreg_enabled = 0; + labibb->ibb_vreg.vreg_enabled = 0; + } else { + pr_err("LAB_VREG_OK not set, failed to notify\n"); + } + } } static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb) @@ -2323,6 +2348,11 @@ static int qpnp_lab_regulator_enable(struct regulator_dev *rdev) struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); + if (labibb->sc_detected) { + pr_info("Short circuit detected: disabled LAB/IBB rails\n"); + return 0; + } + if (labibb->skip_2nd_swire_cmd) { rc = qpnp_ibb_ps_config(labibb, false); if (rc < 0) { @@ -2363,7 +2393,7 @@ static int qpnp_lab_regulator_enable(struct regulator_dev *rdev) labibb->lab_vreg.vreg_enabled = 1; } - if (labibb->notify_lab_vreg_ok_sts) + if (labibb->notify_lab_vreg_ok_sts || labibb->detect_lab_sc) schedule_work(&labibb->lab_vreg_ok_work); return 0; @@ -2621,6 +2651,12 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb, labibb->notify_lab_vreg_ok_sts = of_property_read_bool(of_node, "qcom,notify-lab-vreg-ok-sts"); + labibb->lab_vreg.sc_wait_time_ms = -EINVAL; + if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE && + labibb->detect_lab_sc) + of_property_read_u32(of_node, "qcom,qpnp-lab-sc-wait-time-ms", + &labibb->lab_vreg.sc_wait_time_ms); + rc = of_property_read_u32(of_node, "qcom,qpnp-lab-soft-start", &(labibb->lab_vreg.soft_start)); if (!rc) { @@ -3255,6 +3291,11 @@ static int qpnp_ibb_regulator_enable(struct regulator_dev *rdev) u8 val; struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); + if (labibb->sc_detected) { + pr_info("Short circuit detected: disabled LAB/IBB rails\n"); + return 0; + } + if (!labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) { if (!labibb->standalone) @@ -3731,6 +3772,8 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev) if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) { labibb->mode = QPNP_LABIBB_AMOLED_MODE; + /* Enable polling for LAB short circuit detection for PM660A */ + labibb->detect_lab_sc = true; } else { rc = of_property_read_string(labibb->dev->of_node, "qcom,qpnp-labibb-mode", &mode_name); diff --git a/drivers/regulator/qpnp-oledb-regulator.c b/drivers/regulator/qpnp-oledb-regulator.c index c012f373e80e..bee9a3d82eeb 100644 --- a/drivers/regulator/qpnp-oledb-regulator.c +++ b/drivers/regulator/qpnp-oledb-regulator.c @@ -27,6 +27,7 @@ #include <linux/regulator/of_regulator.h> #include <linux/regulator/qpnp-labibb-regulator.h> #include <linux/qpnp/qpnp-pbs.h> +#include <linux/qpnp/qpnp-revid.h> #define QPNP_OLEDB_REGULATOR_DRIVER_NAME "qcom,qpnp-oledb-regulator" #define OLEDB_VOUT_STEP_MV 100 @@ -162,6 +163,7 @@ struct qpnp_oledb { struct notifier_block oledb_nb; struct mutex bus_lock; struct device_node *pbs_dev_node; + struct pmic_revid_data *pmic_rev_id; u32 base; u8 mod_enable; @@ -181,6 +183,8 @@ struct qpnp_oledb { bool dynamic_ext_pinctl_config; bool pbs_control; bool force_pd_control; + bool handle_lab_sc_notification; + bool lab_sc_detected; }; static const u16 oledb_warmup_dly_ns[] = {6700, 13300, 26700, 53400}; @@ -275,6 +279,11 @@ static int qpnp_oledb_regulator_enable(struct regulator_dev *rdev) struct qpnp_oledb *oledb = rdev_get_drvdata(rdev); + if (oledb->lab_sc_detected == true) { + pr_info("Short circuit detected: Disabled OLEDB rail\n"); + return 0; + } + if (oledb->ext_pin_control) { rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_EXT_PIN_CTL, &val, 1); @@ -368,12 +377,19 @@ static int qpnp_oledb_regulator_disable(struct regulator_dev *rdev) } if (val & OLEDB_FORCE_PD_CTL_SPARE_BIT) { - rc = qpnp_pbs_trigger_event(oledb->pbs_dev_node, - trigger_bitmap); + rc = qpnp_oledb_sec_masked_write(oledb, oledb->base + + OLEDB_SPARE_CTL, + OLEDB_FORCE_PD_CTL_SPARE_BIT, 0); if (rc < 0) { - pr_err("Failed to trigger the PBS sequence\n"); + pr_err("Failed to write SPARE_CTL rc=%d\n", rc); return rc; } + + rc = qpnp_pbs_trigger_event(oledb->pbs_dev_node, + trigger_bitmap); + if (rc < 0) + pr_err("Failed to trigger the PBS sequence\n"); + pr_debug("PBS event triggered\n"); } else { pr_debug("OLEDB_SPARE_CTL register bit not set\n"); @@ -1085,8 +1101,22 @@ static int qpnp_oledb_parse_fast_precharge(struct qpnp_oledb *oledb) static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb) { int rc = 0; + struct device_node *revid_dev_node; struct device_node *of_node = oledb->dev->of_node; + revid_dev_node = of_parse_phandle(oledb->dev->of_node, + "qcom,pmic-revid", 0); + if (!revid_dev_node) { + pr_err("Missing qcom,pmic-revid property - driver failed\n"); + return -EINVAL; + } + + oledb->pmic_rev_id = get_revid_data(revid_dev_node); + if (IS_ERR(oledb->pmic_rev_id)) { + pr_debug("Unable to get revid data\n"); + return -EPROBE_DEFER; + } + oledb->swire_control = of_property_read_bool(of_node, "qcom,swire-control"); @@ -1100,8 +1130,14 @@ static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb) oledb->pbs_control = of_property_read_bool(of_node, "qcom,pbs-control"); - oledb->force_pd_control = - of_property_read_bool(of_node, "qcom,force-pd-control"); + /* Use the force_pd_control only for PM660A versions <= v2.0 */ + if (oledb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE && + oledb->pmic_rev_id->rev4 <= PM660L_V2P0_REV4) { + if (!(oledb->pmic_rev_id->rev4 == PM660L_V2P0_REV4 && + oledb->pmic_rev_id->rev2 > PM660L_V2P0_REV2)) { + oledb->force_pd_control = true; + } + } if (oledb->force_pd_control) { oledb->pbs_dev_node = of_parse_phandle(of_node, @@ -1199,13 +1235,6 @@ static int qpnp_oledb_force_pulldown_config(struct qpnp_oledb *oledb) int rc = 0; u8 val; - rc = qpnp_oledb_sec_masked_write(oledb, oledb->base + - OLEDB_SPARE_CTL, OLEDB_FORCE_PD_CTL_SPARE_BIT, 0); - if (rc < 0) { - pr_err("Failed to write SPARE_CTL rc=%d\n", rc); - return rc; - } - val = 1; rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_PD_CTL, &val, 1); @@ -1227,14 +1256,31 @@ static int qpnp_labibb_notifier_cb(struct notifier_block *nb, unsigned long action, void *data) { int rc = 0; + u8 val; struct qpnp_oledb *oledb = container_of(nb, struct qpnp_oledb, oledb_nb); + if (action == LAB_VREG_NOT_OK) { + /* short circuit detected. Disable OLEDB module */ + val = 0; + rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_MODULE_RDY, + &val, 1); + if (rc < 0) { + pr_err("Failed to write MODULE_RDY rc=%d\n", rc); + return NOTIFY_STOP; + } + oledb->lab_sc_detected = true; + oledb->mod_enable = false; + pr_crit("LAB SC detected, disabling OLEDB forever!\n"); + } + if (action == LAB_VREG_OK) { /* Disable SWIRE pull down control and enable via spmi mode */ rc = qpnp_oledb_force_pulldown_config(oledb); - if (rc < 0) + if (rc < 0) { + pr_err("Failed to config force pull down\n"); return NOTIFY_STOP; + } } return NOTIFY_OK; @@ -1281,7 +1327,11 @@ static int qpnp_oledb_regulator_probe(struct platform_device *pdev) return rc; } - if (oledb->force_pd_control) { + /* Enable LAB short circuit notification support */ + if (oledb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) + oledb->handle_lab_sc_notification = true; + + if (oledb->force_pd_control || oledb->handle_lab_sc_notification) { oledb->oledb_nb.notifier_call = qpnp_labibb_notifier_cb; rc = qpnp_labibb_notifier_register(&oledb->oledb_nb); if (rc < 0) { diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 24ec282e15d8..7c3b8d3516e3 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -1651,6 +1651,9 @@ static void ap_scan_bus(struct work_struct *unused) ap_dev->queue_depth = queue_depth; ap_dev->raw_hwtype = device_type; ap_dev->device_type = device_type; + /* CEX6 toleration: map to CEX5 */ + if (device_type == AP_DEVICE_TYPE_CEX6) + ap_dev->device_type = AP_DEVICE_TYPE_CEX5; ap_dev->functions = device_functions; spin_lock_init(&ap_dev->lock); INIT_LIST_HEAD(&ap_dev->pendingq); diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 6adcbdf225d1..cc741e948170 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -105,6 +105,7 @@ static inline int ap_test_bit(unsigned int *ptr, unsigned int nr) #define AP_DEVICE_TYPE_CEX3C 9 #define AP_DEVICE_TYPE_CEX4 10 #define AP_DEVICE_TYPE_CEX5 11 +#define AP_DEVICE_TYPE_CEX6 12 /* * Known function facilities diff --git a/drivers/scsi/cxlflash/common.h b/drivers/scsi/cxlflash/common.h index 5ada9268a450..a8ac4c0a1493 100644 --- a/drivers/scsi/cxlflash/common.h +++ b/drivers/scsi/cxlflash/common.h @@ -34,7 +34,6 @@ extern const struct file_operations cxlflash_cxl_fops; sectors */ -#define NUM_RRQ_ENTRY 16 /* for master issued cmds */ #define MAX_RHT_PER_CONTEXT (PAGE_SIZE / sizeof(struct sisl_rht_entry)) /* AFU command retry limit */ @@ -48,9 +47,12 @@ extern const struct file_operations cxlflash_cxl_fops; index derivation */ -#define CXLFLASH_MAX_CMDS 16 +#define CXLFLASH_MAX_CMDS 256 #define CXLFLASH_MAX_CMDS_PER_LUN CXLFLASH_MAX_CMDS +/* RRQ for master issued cmds */ +#define NUM_RRQ_ENTRY CXLFLASH_MAX_CMDS + static inline void check_sizes(void) { @@ -149,7 +151,7 @@ struct afu_cmd { struct afu { /* Stuff requiring alignment go first. */ - u64 rrq_entry[NUM_RRQ_ENTRY]; /* 128B RRQ */ + u64 rrq_entry[NUM_RRQ_ENTRY]; /* 2K RRQ */ /* * Command & data for AFU commands. */ diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index c86847c68448..2882bcac918a 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -2305,7 +2305,7 @@ static struct scsi_host_template driver_template = { .eh_device_reset_handler = cxlflash_eh_device_reset_handler, .eh_host_reset_handler = cxlflash_eh_host_reset_handler, .change_queue_depth = cxlflash_change_queue_depth, - .cmd_per_lun = 16, + .cmd_per_lun = CXLFLASH_MAX_CMDS_PER_LUN, .can_queue = CXLFLASH_MAX_CMDS, .this_id = -1, .sg_tablesize = SG_NONE, /* No scatter gather support */ diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 6bffd91b973a..c1ccf1ee99ea 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -560,8 +560,12 @@ static void iscsi_complete_task(struct iscsi_task *task, int state) WARN_ON_ONCE(task->state == ISCSI_TASK_FREE); task->state = state; - if (!list_empty(&task->running)) + spin_lock_bh(&conn->taskqueuelock); + if (!list_empty(&task->running)) { + pr_debug_once("%s while task on list", __func__); list_del_init(&task->running); + } + spin_unlock_bh(&conn->taskqueuelock); if (conn->task == task) conn->task = NULL; @@ -783,7 +787,9 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, if (session->tt->xmit_task(task)) goto free_task; } else { + spin_lock_bh(&conn->taskqueuelock); list_add_tail(&task->running, &conn->mgmtqueue); + spin_unlock_bh(&conn->taskqueuelock); iscsi_conn_queue_work(conn); } @@ -1474,8 +1480,10 @@ void iscsi_requeue_task(struct iscsi_task *task) * this may be on the requeue list already if the xmit_task callout * is handling the r2ts while we are adding new ones */ + spin_lock_bh(&conn->taskqueuelock); if (list_empty(&task->running)) list_add_tail(&task->running, &conn->requeue); + spin_unlock_bh(&conn->taskqueuelock); iscsi_conn_queue_work(conn); } EXPORT_SYMBOL_GPL(iscsi_requeue_task); @@ -1512,22 +1520,26 @@ static int iscsi_data_xmit(struct iscsi_conn *conn) * only have one nop-out as a ping from us and targets should not * overflow us with nop-ins */ + spin_lock_bh(&conn->taskqueuelock); check_mgmt: while (!list_empty(&conn->mgmtqueue)) { conn->task = list_entry(conn->mgmtqueue.next, struct iscsi_task, running); list_del_init(&conn->task->running); + spin_unlock_bh(&conn->taskqueuelock); if (iscsi_prep_mgmt_task(conn, conn->task)) { /* regular RX path uses back_lock */ spin_lock_bh(&conn->session->back_lock); __iscsi_put_task(conn->task); spin_unlock_bh(&conn->session->back_lock); conn->task = NULL; + spin_lock_bh(&conn->taskqueuelock); continue; } rc = iscsi_xmit_task(conn); if (rc) goto done; + spin_lock_bh(&conn->taskqueuelock); } /* process pending command queue */ @@ -1535,19 +1547,24 @@ check_mgmt: conn->task = list_entry(conn->cmdqueue.next, struct iscsi_task, running); list_del_init(&conn->task->running); + spin_unlock_bh(&conn->taskqueuelock); if (conn->session->state == ISCSI_STATE_LOGGING_OUT) { fail_scsi_task(conn->task, DID_IMM_RETRY); + spin_lock_bh(&conn->taskqueuelock); continue; } rc = iscsi_prep_scsi_cmd_pdu(conn->task); if (rc) { if (rc == -ENOMEM || rc == -EACCES) { + spin_lock_bh(&conn->taskqueuelock); list_add_tail(&conn->task->running, &conn->cmdqueue); conn->task = NULL; + spin_unlock_bh(&conn->taskqueuelock); goto done; } else fail_scsi_task(conn->task, DID_ABORT); + spin_lock_bh(&conn->taskqueuelock); continue; } rc = iscsi_xmit_task(conn); @@ -1558,6 +1575,7 @@ check_mgmt: * we need to check the mgmt queue for nops that need to * be sent to aviod starvation */ + spin_lock_bh(&conn->taskqueuelock); if (!list_empty(&conn->mgmtqueue)) goto check_mgmt; } @@ -1577,12 +1595,15 @@ check_mgmt: conn->task = task; list_del_init(&conn->task->running); conn->task->state = ISCSI_TASK_RUNNING; + spin_unlock_bh(&conn->taskqueuelock); rc = iscsi_xmit_task(conn); if (rc) goto done; + spin_lock_bh(&conn->taskqueuelock); if (!list_empty(&conn->mgmtqueue)) goto check_mgmt; } + spin_unlock_bh(&conn->taskqueuelock); spin_unlock_bh(&conn->session->frwd_lock); return -ENODATA; @@ -1738,7 +1759,9 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc) goto prepd_reject; } } else { + spin_lock_bh(&conn->taskqueuelock); list_add_tail(&task->running, &conn->cmdqueue); + spin_unlock_bh(&conn->taskqueuelock); iscsi_conn_queue_work(conn); } @@ -2900,6 +2923,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size, INIT_LIST_HEAD(&conn->mgmtqueue); INIT_LIST_HEAD(&conn->cmdqueue); INIT_LIST_HEAD(&conn->requeue); + spin_lock_init(&conn->taskqueuelock); INIT_WORK(&conn->xmitwork, iscsi_xmitworker); /* allocate login_task used for the login/text sequences */ diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index c14ab6c3ae40..60c21093f865 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -11387,6 +11387,7 @@ static struct pci_driver lpfc_driver = { .id_table = lpfc_id_table, .probe = lpfc_pci_probe_one, .remove = lpfc_pci_remove_one, + .shutdown = lpfc_pci_remove_one, .suspend = lpfc_pci_suspend_one, .resume = lpfc_pci_resume_one, .err_handler = &lpfc_err_handler, diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 79bb3337ba36..c5393d517432 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -9238,10 +9238,11 @@ static int ufshcd_scale_gear(struct ufs_hba *hba, bool scale_up) /* scale up to G3 now */ new_pwr_info.gear_tx = UFS_HS_G3; new_pwr_info.gear_rx = UFS_HS_G3; - ret = ufshcd_change_power_mode(hba, &new_pwr_info); - if (ret) - goto out; + /* now, fall through to set the HS-G3 */ } + ret = ufshcd_change_power_mode(hba, &new_pwr_info); + if (ret) + goto out; } else { memcpy(&new_pwr_info, &hba->pwr_info, sizeof(struct ufs_pa_layer_attr)); diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 75bebf66376d..34b0adb108eb 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -441,6 +441,23 @@ config QCOM_MEMORY_DUMP_V2 of deadlocks or cpu hangs these dump regions are captured to give a snapshot of the system at the time of the crash. +config QCOM_MINIDUMP + bool "QCOM Minidump Support" + depends on MSM_SMEM && QCOM_DLOAD_MODE + help + This enables minidump feature. It allows various clients to + register to dump their state at system bad state (panic/WDT,etc.,). + This uses SMEM to store all registered client information. + This will dump all registered entries, only when DLOAD mode is enabled. + +config MINIDUMP_MAX_ENTRIES + int "Minidump Maximum num of entries" + default 200 + depends on QCOM_MINIDUMP + help + This defines maximum number of entries to be allocated for application + subsytem in Minidump SMEM table. + config ICNSS tristate "Platform driver for Q6 integrated connectivity" ---help--- diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index fa350d122384..87698b75d3b8 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_QCOM_SCM_XPU) += scm-xpu.o obj-$(CONFIG_QCOM_WATCHDOG_V2) += watchdog_v2.o obj-$(CONFIG_QCOM_MEMORY_DUMP) += memory_dump.o obj-$(CONFIG_QCOM_MEMORY_DUMP_V2) += memory_dump_v2.o +obj-$(CONFIG_QCOM_MINIDUMP) += msm_minidump.o obj-$(CONFIG_QCOM_DCC) += dcc.o obj-$(CONFIG_QCOM_WATCHDOG_V2) += watchdog_v2.o obj-$(CONFIG_QCOM_COMMON_LOG) += common_log.o diff --git a/drivers/soc/qcom/common_log.c b/drivers/soc/qcom/common_log.c index ecf89b2b3b37..f001e820b797 100644 --- a/drivers/soc/qcom/common_log.c +++ b/drivers/soc/qcom/common_log.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -18,6 +18,8 @@ #include <linux/slab.h> #include <linux/kmemleak.h> #include <soc/qcom/memory_dump.h> +#include <soc/qcom/minidump.h> +#include <asm/sections.h> #define MISC_DUMP_DATA_LEN 4096 #define PMIC_DUMP_DATA_LEN (64 * 1024) @@ -38,6 +40,8 @@ void register_misc_dump(void) misc_buf = kzalloc(MISC_DUMP_DATA_LEN, GFP_KERNEL); if (!misc_buf) goto err0; + + strlcpy(misc_data->name, "KMISC", sizeof(misc_data->name)); misc_data->addr = virt_to_phys(misc_buf); misc_data->len = MISC_DUMP_DATA_LEN; dump_entry.id = MSM_DUMP_DATA_MISC; @@ -70,6 +74,7 @@ static void register_pmic_dump(void) if (!dump_addr) goto err0; + strlcpy(dump_data->name, "KPMIC", sizeof(dump_data->name)); dump_data->addr = virt_to_phys(dump_addr); dump_data->len = PMIC_DUMP_DATA_LEN; dump_entry.id = MSM_DUMP_DATA_PMIC; @@ -104,6 +109,8 @@ static void register_vsense_dump(void) if (!dump_addr) goto err0; + strlcpy(dump_data->name, "KVSENSE", + sizeof(dump_data->name)); dump_data->addr = virt_to_phys(dump_addr); dump_data->len = VSENSE_DUMP_DATA_LEN; dump_entry.id = MSM_DUMP_DATA_VSENSE; @@ -136,6 +143,7 @@ void register_rpm_dump(void) if (!dump_addr) goto err0; + strlcpy(dump_data->name, "KRPM", sizeof(dump_data->name)); dump_data->addr = virt_to_phys(dump_addr); dump_data->len = RPM_DUMP_DATA_LEN; dump_entry.id = MSM_DUMP_DATA_RPM; @@ -217,8 +225,39 @@ static void __init common_log_register_log_buf(void) } } +static void __init register_kernel_sections(void) +{ + struct md_region ksec_entry; + char *data_name = "KDATABSS"; + const size_t static_size = __per_cpu_end - __per_cpu_start; + void __percpu *base = (void __percpu *)__per_cpu_start; + unsigned int cpu; + + strlcpy(ksec_entry.name, data_name, sizeof(ksec_entry.name)); + ksec_entry.virt_addr = (uintptr_t)_sdata; + ksec_entry.phys_addr = virt_to_phys(_sdata); + ksec_entry.size = roundup((__bss_stop - _sdata), 4); + if (msm_minidump_add_region(&ksec_entry)) + pr_err("Failed to add data section in Minidump\n"); + + /* Add percpu static sections */ + for_each_possible_cpu(cpu) { + void *start = per_cpu_ptr(base, cpu); + + memset(&ksec_entry, 0, sizeof(ksec_entry)); + scnprintf(ksec_entry.name, sizeof(ksec_entry.name), + "KSPERCPU%d", cpu); + ksec_entry.virt_addr = (uintptr_t)start; + ksec_entry.phys_addr = per_cpu_ptr_to_phys(start); + ksec_entry.size = static_size; + if (msm_minidump_add_region(&ksec_entry)) + pr_err("Failed to add percpu sections in Minidump\n"); + } +} + static int __init msm_common_log_init(void) { + register_kernel_sections(); common_log_register_log_buf(); register_misc_dump(); register_pmic_dump(); diff --git a/drivers/soc/qcom/cpuss_dump.c b/drivers/soc/qcom/cpuss_dump.c index 93c876a7ad73..2e08b78dee94 100644 --- a/drivers/soc/qcom/cpuss_dump.c +++ b/drivers/soc/qcom/cpuss_dump.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -75,6 +75,8 @@ static int cpuss_dump_probe(struct platform_device *pdev) dump_data->addr = dump_addr; dump_data->len = size; + scnprintf(dump_data->name, sizeof(dump_data->name), + "KCPUSS%X", id); dump_entry.id = id; dump_entry.addr = virt_to_phys(dump_data); ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry); diff --git a/drivers/soc/qcom/dcc.c b/drivers/soc/qcom/dcc.c index aced50bf7fda..e5f3f119065b 100644 --- a/drivers/soc/qcom/dcc.c +++ b/drivers/soc/qcom/dcc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1173,6 +1173,8 @@ static void dcc_allocate_dump_mem(struct dcc_drvdata *drvdata) /* Allocate memory for dcc reg dump */ drvdata->reg_buf = devm_kzalloc(dev, drvdata->reg_size, GFP_KERNEL); if (drvdata->reg_buf) { + strlcpy(drvdata->reg_data.name, "KDCC_REG", + sizeof(drvdata->reg_data.name)); drvdata->reg_data.addr = virt_to_phys(drvdata->reg_buf); drvdata->reg_data.len = drvdata->reg_size; reg_dump_entry.id = MSM_DUMP_DATA_DCC_REG; @@ -1190,6 +1192,8 @@ static void dcc_allocate_dump_mem(struct dcc_drvdata *drvdata) /* Allocate memory for dcc sram dump */ drvdata->sram_buf = devm_kzalloc(dev, drvdata->ram_size, GFP_KERNEL); if (drvdata->sram_buf) { + strlcpy(drvdata->sram_data.name, "KDCC_SRAM", + sizeof(drvdata->sram_data.name)); drvdata->sram_data.addr = virt_to_phys(drvdata->sram_buf); drvdata->sram_data.len = drvdata->ram_size; sram_dump_entry.id = MSM_DUMP_DATA_DCC_SRAM; diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c index cc809cbdd839..72f5829d1eb6 100644 --- a/drivers/soc/qcom/glink.c +++ b/drivers/soc/qcom/glink.c @@ -1845,7 +1845,7 @@ static void glink_ch_ctx_release(struct rwref_lock *ch_st_lock) /** * ch_name_to_ch_ctx_create() - lookup a channel by name, create the channel if - * it is not found. + * it is not found and get reference of context. * @xprt_ctx: Transport to search for a matching channel. * @name: Name of the desired channel. * @@ -1901,6 +1901,7 @@ check_ctx: spin_unlock_irqrestore(&xprt_ctx->xprt_ctx_lock_lhb1, flags); kfree(ctx); + rwref_get(&entry->ch_state_lhb2); rwref_write_put(&xprt_ctx->xprt_state_lhb0); return entry; } @@ -1935,6 +1936,7 @@ check_ctx: "%s: local:GLINK_CHANNEL_CLOSED\n", __func__); } + rwref_get(&ctx->ch_state_lhb2); spin_unlock_irqrestore(&xprt_ctx->xprt_ctx_lock_lhb1, flags); rwref_write_put(&xprt_ctx->xprt_state_lhb0); mutex_lock(&xprt_ctx->xprt_dbgfs_lock_lhb4); @@ -2579,6 +2581,7 @@ void *glink_open(const struct glink_open_config *cfg) GLINK_INFO_CH_XPRT(ctx, transport_ptr, "%s: Channel not ready to be re-opened. State: %u\n", __func__, ctx->local_open_state); + rwref_put(&ctx->ch_state_lhb2); return ERR_PTR(-EBUSY); } @@ -2627,11 +2630,13 @@ void *glink_open(const struct glink_open_config *cfg) ctx->local_open_state = GLINK_CHANNEL_CLOSED; GLINK_ERR_CH(ctx, "%s: Unable to send open command %d\n", __func__, ret); + rwref_put(&ctx->ch_state_lhb2); return ERR_PTR(ret); } GLINK_INFO_CH(ctx, "%s: Created channel, sent OPEN command. ctx %p\n", __func__, ctx); + rwref_put(&ctx->ch_state_lhb2); return ctx; } EXPORT_SYMBOL(glink_open); @@ -4804,6 +4809,7 @@ static void glink_core_rx_cmd_ch_remote_open(struct glink_transport_if *if_ptr, GLINK_ERR_CH(ctx, "%s: Duplicate remote open for rcid %u, name '%s'\n", __func__, rcid, name); + rwref_put(&ctx->ch_state_lhb2); glink_core_migration_edge_unlock(if_ptr->glink_core_priv); return; } @@ -4826,6 +4832,7 @@ static void glink_core_rx_cmd_ch_remote_open(struct glink_transport_if *if_ptr, if (do_migrate) ch_migrate(NULL, ctx); + rwref_put(&ctx->ch_state_lhb2); glink_core_migration_edge_unlock(if_ptr->glink_core_priv); } diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c index 2dc4208cbc51..3f969234b705 100644 --- a/drivers/soc/qcom/glink_smem_native_xprt.c +++ b/drivers/soc/qcom/glink_smem_native_xprt.c @@ -678,7 +678,8 @@ static void process_rx_data(struct edge_info *einfo, uint16_t cmd_id, err = true; } else if (intent->data == NULL) { if (einfo->intentless) { - intent->data = kmalloc(cmd.frag_size, GFP_ATOMIC); + intent->data = kmalloc(cmd.frag_size, + __GFP_ATOMIC | __GFP_HIGH); if (!intent->data) { err = true; GLINK_ERR( @@ -798,6 +799,12 @@ static bool get_rx_fifo(struct edge_info *einfo) einfo->remote_proc_id, SMEM_ITEM_CACHED_FLAG); if (!einfo->rx_fifo) + einfo->rx_fifo = smem_get_entry( + SMEM_GLINK_NATIVE_XPRT_FIFO_1, + &einfo->rx_fifo_size, + einfo->remote_proc_id, + 0); + if (!einfo->rx_fifo) return false; } diff --git a/drivers/soc/qcom/memory_dump_v2.c b/drivers/soc/qcom/memory_dump_v2.c index af141c808c81..092b1c1af44b 100644 --- a/drivers/soc/qcom/memory_dump_v2.c +++ b/drivers/soc/qcom/memory_dump_v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -20,6 +20,7 @@ #include <linux/kmemleak.h> #include <soc/qcom/memory_dump.h> #include <soc/qcom/scm.h> +#include <soc/qcom/minidump.h> #define MSM_DUMP_TABLE_VERSION MSM_DUMP_MAKE_VERSION(2, 0) @@ -87,6 +88,29 @@ static struct msm_dump_table *msm_dump_get_table(enum msm_dump_table_ids id) return table; } +int msm_dump_data_add_minidump(struct msm_dump_entry *entry) +{ + struct msm_dump_data *data; + struct md_region md_entry; + + data = (struct msm_dump_data *)(phys_to_virt(entry->addr)); + if (!strcmp(data->name, "")) { + pr_info("Entry name is NULL, Use ID %d for minidump\n", + entry->id); + snprintf(md_entry.name, sizeof(md_entry.name), "KMDT0x%X", + entry->id); + } else { + strlcpy(md_entry.name, data->name, sizeof(md_entry.name)); + } + + md_entry.phys_addr = data->addr; + md_entry.virt_addr = (uintptr_t)phys_to_virt(data->addr); + md_entry.size = data->len; + md_entry.id = entry->id; + + return msm_minidump_add_region(&md_entry); +} + int msm_dump_data_register(enum msm_dump_table_ids id, struct msm_dump_entry *entry) { @@ -107,6 +131,10 @@ int msm_dump_data_register(enum msm_dump_table_ids id, table->num_entries++; dmac_flush_range(table, (void *)table + sizeof(struct msm_dump_table)); + + if (msm_dump_data_add_minidump(entry)) + pr_info("Failed to add entry in Minidump table\n"); + return 0; } EXPORT_SYMBOL(msm_dump_data_register); diff --git a/drivers/soc/qcom/msm_minidump.c b/drivers/soc/qcom/msm_minidump.c new file mode 100644 index 000000000000..1cb36bf98555 --- /dev/null +++ b/drivers/soc/qcom/msm_minidump.c @@ -0,0 +1,371 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This 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) "Minidump: " fmt + +#include <linux/init.h> +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/elf.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <soc/qcom/smem.h> +#include <soc/qcom/scm.h> +#include <soc/qcom/minidump.h> + + +#define MAX_NUM_ENTRIES (CONFIG_MINIDUMP_MAX_ENTRIES + 1) +#define SMEM_ENTRY_SIZE 32 +#define MAX_MEM_LENGTH (SMEM_ENTRY_SIZE * MAX_NUM_ENTRIES) +#define MAX_STRTBL_SIZE (MAX_NUM_ENTRIES * MAX_NAME_LENGTH) +#define SMEM_MINIDUMP_TABLE_ID 602 + +/* Bootloader Minidump table */ +struct md_smem_table { + u32 version; + u32 smem_length; + u64 next_avail_offset; + char reserved[MAX_NAME_LENGTH]; + u64 *region_start; +}; + +/* Bootloader Minidump region */ +struct md_smem_region { + char name[MAX_NAME_LENGTH]; + u64 address; + u64 size; +}; + +/* md_table: APPS minidump table + * @num_regions: Number of entries registered + * @region_base_offset: APPS region start offset smem table + * @md_smem_table: Pointer smem table + * @region: Pointer to APPS region in smem table + * @entry: All registered client entries. + */ + +struct md_table { + u32 num_regions; + u32 region_base_offset; + struct md_smem_table *md_smem_table; + struct md_smem_region *region; + struct md_region entry[MAX_NUM_ENTRIES]; +}; + +/* Protect elfheader and smem table from deferred calls contention */ +static DEFINE_SPINLOCK(mdt_lock); +static bool minidump_enabled; +static struct md_table minidump_table; +static unsigned int pendings; +static unsigned int region_idx = 1; /* First entry is ELF header*/ + +/* ELF Header */ +static struct elfhdr *md_ehdr; +/* ELF Program header */ +static struct elf_phdr *phdr; +/* ELF Section header */ +static struct elf_shdr *shdr; +/* Section offset in elf image */ +static u64 elf_offset; +/* String table index, first byte must be '\0' */ +static unsigned int stringtable_idx = 1; + +static inline struct elf_shdr *elf_sheader(struct elfhdr *hdr) +{ + return (struct elf_shdr *)((size_t)hdr + (size_t)hdr->e_shoff); +} + +static inline struct elf_shdr *elf_section(struct elfhdr *hdr, int idx) +{ + return &elf_sheader(hdr)[idx]; +} + +static inline char *elf_str_table(struct elfhdr *hdr) +{ + if (hdr->e_shstrndx == SHN_UNDEF) + return NULL; + return (char *)hdr + elf_section(hdr, hdr->e_shstrndx)->sh_offset; +} + +static inline char *elf_lookup_string(struct elfhdr *hdr, int offset) +{ + char *strtab = elf_str_table(hdr); + + if ((strtab == NULL) | (stringtable_idx < offset)) + return NULL; + return strtab + offset; +} + +static inline unsigned int set_section_name(const char *name) +{ + char *strtab = elf_str_table(md_ehdr); + int ret = 0; + + if ((strtab == NULL) | (name == NULL)) + return 0; + + ret = stringtable_idx; + stringtable_idx += strlcpy((strtab + stringtable_idx), + name, MAX_NAME_LENGTH); + stringtable_idx += 1; + return ret; +} + +/* return 1 if name already exists */ +static inline bool md_check_name(const char *name) +{ + struct md_region *mde = minidump_table.entry; + int i, regno = minidump_table.num_regions; + + for (i = 0; i < regno; i++, mde++) + if (!strcmp(mde->name, name)) + return true; + return false; +} + +/* Update Mini dump table in SMEM */ +static int md_update_smem_table(const struct md_region *entry) +{ + struct md_smem_region *mdr; + + if (!minidump_enabled) { + pr_info("Table in smem is not setup\n"); + return -ENODEV; + } + + mdr = &minidump_table.region[region_idx++]; + + strlcpy(mdr->name, entry->name, sizeof(mdr->name)); + mdr->address = entry->phys_addr; + mdr->size = entry->size; + + /* Update elf header */ + shdr->sh_type = SHT_PROGBITS; + shdr->sh_name = set_section_name(mdr->name); + shdr->sh_addr = (elf_addr_t)entry->virt_addr; + shdr->sh_size = mdr->size; + shdr->sh_flags = SHF_WRITE; + shdr->sh_offset = elf_offset; + shdr->sh_entsize = 0; + + phdr->p_type = PT_LOAD; + phdr->p_offset = elf_offset; + phdr->p_vaddr = entry->virt_addr; + phdr->p_paddr = entry->phys_addr; + phdr->p_filesz = phdr->p_memsz = mdr->size; + phdr->p_flags = PF_R | PF_W; + + md_ehdr->e_shnum += 1; + md_ehdr->e_phnum += 1; + elf_offset += shdr->sh_size; + shdr++; + phdr++; + + return 0; +} + +bool msm_minidump_enabled(void) +{ + bool ret; + + spin_lock(&mdt_lock); + ret = minidump_enabled; + spin_unlock(&mdt_lock); + return ret; +} +EXPORT_SYMBOL(msm_minidump_enabled); + +int msm_minidump_add_region(const struct md_region *entry) +{ + u32 entries; + struct md_region *mdr; + int ret = 0; + + if (!entry) + return -EINVAL; + + if (((strlen(entry->name) > MAX_NAME_LENGTH) || + md_check_name(entry->name)) && !entry->virt_addr) { + pr_info("Invalid entry details\n"); + return -EINVAL; + } + + if (!IS_ALIGNED(entry->size, 4)) { + pr_info("size should be 4 byte aligned\n"); + return -EINVAL; + } + + spin_lock(&mdt_lock); + entries = minidump_table.num_regions; + if (entries >= MAX_NUM_ENTRIES) { + pr_info("Maximum entries reached.\n"); + spin_unlock(&mdt_lock); + return -ENOMEM; + } + + mdr = &minidump_table.entry[entries]; + strlcpy(mdr->name, entry->name, sizeof(mdr->name)); + mdr->virt_addr = entry->virt_addr; + mdr->phys_addr = entry->phys_addr; + mdr->size = entry->size; + mdr->id = entry->id; + + minidump_table.num_regions = entries + 1; + + if (minidump_enabled) + ret = md_update_smem_table(entry); + else + pendings++; + + spin_unlock(&mdt_lock); + + pr_debug("Minidump: added %s to %s list\n", + mdr->name, minidump_enabled ? "":"pending"); + return ret; +} +EXPORT_SYMBOL(msm_minidump_add_region); + +static int msm_minidump_add_header(void) +{ + struct md_smem_region *mdreg = &minidump_table.region[0]; + char *banner; + unsigned int strtbl_off, elfh_size, phdr_off; + + elfh_size = sizeof(*md_ehdr) + MAX_STRTBL_SIZE + MAX_MEM_LENGTH + + ((sizeof(*shdr) + sizeof(*phdr)) * (MAX_NUM_ENTRIES + 1)); + + md_ehdr = kzalloc(elfh_size, GFP_KERNEL); + if (!md_ehdr) + return -ENOMEM; + + strlcpy(mdreg->name, "KELF_HEADER", sizeof(mdreg->name)); + mdreg->address = virt_to_phys(md_ehdr); + mdreg->size = elfh_size; + + /* Section headers*/ + shdr = (struct elf_shdr *)(md_ehdr + 1); + phdr = (struct elf_phdr *)(shdr + MAX_NUM_ENTRIES); + phdr_off = sizeof(*md_ehdr) + (sizeof(*shdr) * MAX_NUM_ENTRIES); + + memcpy(md_ehdr->e_ident, ELFMAG, SELFMAG); + md_ehdr->e_ident[EI_CLASS] = ELF_CLASS; + md_ehdr->e_ident[EI_DATA] = ELF_DATA; + md_ehdr->e_ident[EI_VERSION] = EV_CURRENT; + md_ehdr->e_ident[EI_OSABI] = ELF_OSABI; + md_ehdr->e_type = ET_CORE; + md_ehdr->e_machine = ELF_ARCH; + md_ehdr->e_version = EV_CURRENT; + md_ehdr->e_ehsize = sizeof(*md_ehdr); + md_ehdr->e_phoff = phdr_off; + md_ehdr->e_phentsize = sizeof(*phdr); + md_ehdr->e_phnum = 1; + md_ehdr->e_shoff = sizeof(*md_ehdr); + md_ehdr->e_shentsize = sizeof(*shdr); + md_ehdr->e_shnum = 3; /* NULL, STR TABLE, Linux banner */ + md_ehdr->e_shstrndx = 1; + + elf_offset = elfh_size; + strtbl_off = sizeof(*md_ehdr) + + ((sizeof(*phdr) + sizeof(*shdr)) * MAX_NUM_ENTRIES); + /* First section header should be NULL + * 2nd entry for string table + */ + shdr++; + shdr->sh_type = SHT_STRTAB; + shdr->sh_offset = (elf_addr_t)strtbl_off; + shdr->sh_size = MAX_STRTBL_SIZE; + shdr->sh_entsize = 0; + shdr->sh_flags = 0; + shdr->sh_name = set_section_name("STR_TBL"); + shdr++; + + /* 3rd entry for linux banner */ + banner = (char *)md_ehdr + strtbl_off + MAX_STRTBL_SIZE; + strlcpy(banner, linux_banner, MAX_MEM_LENGTH); + + shdr->sh_type = SHT_PROGBITS; + shdr->sh_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE); + shdr->sh_size = strlen(linux_banner) + 1; + shdr->sh_addr = (elf_addr_t)linux_banner; + shdr->sh_entsize = 0; + shdr->sh_flags = SHF_WRITE; + shdr->sh_name = set_section_name("linux_banner"); + shdr++; + + phdr->p_type = PT_LOAD; + phdr->p_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE); + phdr->p_vaddr = (elf_addr_t)linux_banner; + phdr->p_paddr = virt_to_phys(linux_banner); + phdr->p_filesz = phdr->p_memsz = strlen(linux_banner) + 1; + phdr->p_flags = PF_R | PF_W; + + md_ehdr->e_phnum += 1; + phdr++; + + return 0; +} + +static int __init msm_minidump_init(void) +{ + unsigned int i, size; + struct md_region *mdr; + struct md_smem_table *smem_table; + + /* Get Minidump table */ + smem_table = smem_get_entry(SMEM_MINIDUMP_TABLE_ID, &size, 0, + SMEM_ANY_HOST_FLAG); + if (IS_ERR_OR_NULL(smem_table)) { + pr_info("SMEM is not initialized.\n"); + return -ENODEV; + } + + if ((smem_table->next_avail_offset + MAX_MEM_LENGTH) > + smem_table->smem_length) { + pr_info("SMEM memory not available.\n"); + return -ENOMEM; + } + + /* Get next_avail_offset and update it to reserve memory */ + minidump_table.region_base_offset = smem_table->next_avail_offset; + minidump_table.region = (struct md_smem_region *)((uintptr_t)smem_table + + minidump_table.region_base_offset); + + smem_table->next_avail_offset = + minidump_table.region_base_offset + MAX_MEM_LENGTH; + minidump_table.md_smem_table = smem_table; + + msm_minidump_add_header(); + + /* Add pending entries to smem table */ + spin_lock(&mdt_lock); + minidump_enabled = true; + + for (i = 0; i < pendings; i++) { + mdr = &minidump_table.entry[i]; + if (md_update_smem_table(mdr)) { + pr_info("Unable to add entry %s to smem table\n", + mdr->name); + spin_unlock(&mdt_lock); + return -ENODEV; + } + } + + pendings = 0; + spin_unlock(&mdt_lock); + + pr_info("Enabled, region base:%d, region 0x%pK\n", + minidump_table.region_base_offset, minidump_table.region); + + return 0; +} +subsys_initcall(msm_minidump_init) diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c index 53bddc5987df..988b6e8c9fd9 100644 --- a/drivers/soc/qcom/pil-msa.c +++ b/drivers/soc/qcom/pil-msa.c @@ -616,7 +616,15 @@ int pil_mss_reset_load_mba(struct pil_desc *pil) /* Load the MBA image into memory */ count = fw->size; - memcpy(mba_dp_virt, data, count); + if (count <= SZ_1M) { + /* Ensures memcpy is done for max 1MB fw size */ + memcpy(mba_dp_virt, data, count); + } else { + dev_err(pil->dev, "%s fw image loading into memory is failed due to fw size overflow\n", + __func__); + ret = -EINVAL; + goto err_mba_data; + } /* Ensure memcpy of the MBA memory is done before loading the DP */ wmb(); diff --git a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c index 3a6d84140bc9..8a2be787b70e 100644 --- a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c +++ b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c @@ -114,7 +114,7 @@ int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, { int rc = 0, retries = 0; void *pkt_data = NULL; - struct apr_tx_buf *tx_buf; + struct apr_tx_buf *tx_buf = NULL; struct apr_pkt_priv *pkt_priv_ptr = pkt_priv; if (!apr_ch->handle || !pkt_priv) diff --git a/drivers/soc/qcom/qdsp6v2/audio_notifier.c b/drivers/soc/qcom/qdsp6v2/audio_notifier.c index a59b436234c7..b46cd2067441 100644 --- a/drivers/soc/qcom/qdsp6v2/audio_notifier.c +++ b/drivers/soc/qcom/qdsp6v2/audio_notifier.c @@ -510,7 +510,7 @@ int audio_notifier_deregister(char *client_name) int ret = 0; int ret2; struct list_head *ptr, *next; - struct client_data *client_data; + struct client_data *client_data = NULL; if (client_name == NULL) { pr_err("%s: client_name is NULL\n", __func__); diff --git a/drivers/soc/qcom/qpnp-haptic.c b/drivers/soc/qcom/qpnp-haptic.c index c86eebcd390f..70cf11359e97 100644 --- a/drivers/soc/qcom/qpnp-haptic.c +++ b/drivers/soc/qcom/qpnp-haptic.c @@ -138,7 +138,7 @@ #define QPNP_HAP_WAV_SAMP_MAX 0x7E #define QPNP_HAP_BRAKE_PAT_LEN 4 #define QPNP_HAP_PLAY_EN 0x80 -#define QPNP_HAP_EN 0x80 +#define QPNP_HAP_EN_BIT BIT(7) #define QPNP_HAP_BRAKE_MASK BIT(0) #define QPNP_HAP_AUTO_RES_MASK BIT(7) #define AUTO_RES_ENABLE BIT(7) @@ -305,7 +305,6 @@ struct qpnp_pwm_info { * @ wave_samp - array of wave samples * @ shadow_wave_samp - shadow array of wave samples * @ brake_pat - pattern for active breaking - * @ reg_en_ctl - enable control register * @ reg_play - play register * @ lra_res_cal_period - period for resonance calibration * @ sc_duration - counter to determine the duration of short circuit condition @@ -358,6 +357,7 @@ struct qpnp_hap { u32 sc_irq; u32 status_flags; u16 base; + u16 last_rate_cfg; u16 drive_period_code_max_limit; u16 drive_period_code_min_limit; u16 lra_res_cal_period; @@ -368,7 +368,6 @@ struct qpnp_hap { u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN]; u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN]; u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN]; - u8 reg_en_ctl; u8 reg_play; u8 sc_duration; u8 ext_pwm_dtest_line; @@ -391,6 +390,18 @@ struct qpnp_hap { static struct qpnp_hap *ghap; /* helper to read a pmic register */ +static int qpnp_hap_read_mult_reg(struct qpnp_hap *hap, u16 addr, u8 *val, + int len) +{ + int rc; + + rc = regmap_bulk_read(hap->regmap, addr, val, len); + if (rc < 0) + pr_err("Error reading address: %X - ret %X\n", addr, rc); + + return rc; +} + static int qpnp_hap_read_reg(struct qpnp_hap *hap, u16 addr, u8 *val) { int rc; @@ -399,11 +410,28 @@ static int qpnp_hap_read_reg(struct qpnp_hap *hap, u16 addr, u8 *val) rc = regmap_read(hap->regmap, addr, &tmp); if (rc < 0) pr_err("Error reading address: %X - ret %X\n", addr, rc); - *val = (u8)tmp; + else + *val = (u8)tmp; + return rc; } /* helper to write a pmic register */ +static int qpnp_hap_write_mult_reg(struct qpnp_hap *hap, u16 addr, u8 *val, + int len) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(&hap->bus_lock, flags); + rc = regmap_bulk_write(hap->regmap, addr, val, len); + if (rc < 0) + pr_err("Error writing address: %X - ret %X\n", addr, rc); + + spin_unlock_irqrestore(&hap->bus_lock, flags); + return rc; +} + static int qpnp_hap_write_reg(struct qpnp_hap *hap, u16 addr, u8 val) { unsigned long flags; @@ -480,15 +508,12 @@ static void qpnp_handle_sc_irq(struct work_struct *work) } } -static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on) +static int qpnp_hap_mod_enable(struct qpnp_hap *hap, bool on) { u8 val; int rc, i; - val = hap->reg_en_ctl; - if (on) { - val |= QPNP_HAP_EN; - } else { + if (!on) { for (i = 0; i < QPNP_HAP_MAX_RETRIES; i++) { /* wait for 4 cycles of play rate */ unsigned long sleep_time = @@ -511,16 +536,13 @@ static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on) if (i >= QPNP_HAP_MAX_RETRIES) pr_debug("Haptics Busy. Force disable\n"); - - val &= ~QPNP_HAP_EN; } + val = on ? QPNP_HAP_EN_BIT : 0; rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), val); if (rc < 0) return rc; - hap->reg_en_ctl = val; - return 0; } @@ -1517,20 +1539,23 @@ static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable) static void update_lra_frequency(struct qpnp_hap *hap) { - u8 lra_auto_res_lo = 0, lra_auto_res_hi = 0, val; + u8 lra_auto_res[2], val; u32 play_rate_code; + u16 rate_cfg; int rc; - qpnp_hap_read_reg(hap, QPNP_HAP_LRA_AUTO_RES_LO(hap->base), - &lra_auto_res_lo); - qpnp_hap_read_reg(hap, QPNP_HAP_LRA_AUTO_RES_HI(hap->base), - &lra_auto_res_hi); + rc = qpnp_hap_read_mult_reg(hap, QPNP_HAP_LRA_AUTO_RES_LO(hap->base), + lra_auto_res, 2); + if (rc < 0) { + pr_err("Error in reading LRA_AUTO_RES_LO/HI, rc=%d\n", rc); + return; + } play_rate_code = - (lra_auto_res_hi & 0xF0) << 4 | (lra_auto_res_lo & 0xFF); + (lra_auto_res[1] & 0xF0) << 4 | (lra_auto_res[0] & 0xFF); pr_debug("lra_auto_res_lo = 0x%x lra_auto_res_hi = 0x%x play_rate_code = 0x%x\n", - lra_auto_res_lo, lra_auto_res_hi, play_rate_code); + lra_auto_res[0], lra_auto_res[1], play_rate_code); rc = qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val); if (rc < 0) @@ -1559,12 +1584,21 @@ static void update_lra_frequency(struct qpnp_hap *hap) return; } - qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base), - lra_auto_res_lo); + lra_auto_res[1] >>= 4; + rate_cfg = lra_auto_res[1] << 8 | lra_auto_res[0]; + if (hap->last_rate_cfg == rate_cfg) { + pr_debug("Same rate_cfg, skip updating\n"); + return; + } - lra_auto_res_hi = lra_auto_res_hi >> 4; - qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base), - lra_auto_res_hi); + rc = qpnp_hap_write_mult_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base), + lra_auto_res, 2); + if (rc < 0) { + pr_err("Error in writing to RATE_CFG1/2, rc=%d\n", rc); + } else { + pr_debug("Update RATE_CFG with [0x%x]\n", rate_cfg); + hap->last_rate_cfg = rate_cfg; + } } static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer) @@ -1983,6 +2017,8 @@ static int qpnp_hap_config(struct qpnp_hap *hap) if (rc) return rc; + hap->last_rate_cfg = hap->init_drive_period_code; + if (hap->act_type == QPNP_HAP_LRA && hap->perform_lra_auto_resonance_search) calculate_lra_code(hap); @@ -2019,12 +2055,6 @@ static int qpnp_hap_config(struct qpnp_hap *hap) return rc; } - /* Cache enable control register */ - rc = qpnp_hap_read_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), &val); - if (rc < 0) - return rc; - hap->reg_en_ctl = val; - /* Cache play register */ rc = qpnp_hap_read_reg(hap, QPNP_HAP_PLAY_REG(hap->base), &val); if (rc < 0) diff --git a/drivers/soc/qcom/rpm-smd.c b/drivers/soc/qcom/rpm-smd.c index f2784dedbc7a..e792c953f31f 100644 --- a/drivers/soc/qcom/rpm-smd.c +++ b/drivers/soc/qcom/rpm-smd.c @@ -108,9 +108,7 @@ static struct glink_apps_rpm_data *glink_data; #define RPM_DATA_LEN_SIZE 16 #define RPM_HDR_SIZE ((rpm_msg_fmt_ver == RPM_MSG_V0_FMT) ?\ sizeof(struct rpm_v0_hdr) : sizeof(struct rpm_v1_hdr)) -#define GET_FIELD(offset, size) (((1U << (offset + size)) - 1) - \ - ((1U << offset) - 1)) -#define CLEAR_FIELD(offset, size) (~GET_FIELD(offset, size)) +#define CLEAR_FIELD(offset, size) (~GENMASK(offset + size - 1, offset)) static ATOMIC_NOTIFIER_HEAD(msm_rpm_sleep_notifier); static bool standalone; @@ -223,7 +221,7 @@ static uint32_t msm_rpm_get_next_msg_id(void); static inline uint32_t get_offset_value(uint32_t val, uint32_t offset, uint32_t size) { - return (((val) & GET_FIELD(offset, size)) + return (((val) & GENMASK(offset + size - 1, offset)) >> offset); } diff --git a/drivers/soc/qcom/scm.c b/drivers/soc/qcom/scm.c index 045a5001fc9f..36804b988d62 100644 --- a/drivers/soc/qcom/scm.c +++ b/drivers/soc/qcom/scm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -654,7 +654,7 @@ int scm_call2(u32 fn_id, struct scm_desc *desc) if (unlikely(!is_scm_armv8())) return -ENODEV; - ret = allocate_extra_arg_buffer(desc, GFP_KERNEL); + ret = allocate_extra_arg_buffer(desc, GFP_NOIO); if (ret) return ret; diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c index 0625f75de373..97cd11201262 100644 --- a/drivers/soc/qcom/service-locator.c +++ b/drivers/soc/qcom/service-locator.c @@ -24,7 +24,6 @@ #include <linux/device.h> #include <linux/delay.h> #include <linux/workqueue.h> -#include <linux/debugfs.h> #include <soc/qcom/msm_qmi_interface.h> #include <soc/qcom/service-locator.h> @@ -440,140 +439,3 @@ int find_subsys(const char *pd_path, char *subsys) return 0; } EXPORT_SYMBOL(find_subsys); - -static struct pd_qmi_client_data test_data; - -static int servloc_test_pdr_cb(struct notifier_block *this, - unsigned long opcode, void *ptr) -{ - int i, rc = 0; - char subsys[QMI_SERVREG_LOC_NAME_LENGTH_V01]; - struct pd_qmi_client_data *return_data; - - return_data = (struct pd_qmi_client_data *)ptr; - - if (opcode) { - pr_err("%s: Failed to get process domain!, opcode = %lu\n", - __func__, opcode); - return -EIO; - } - - pr_err("Service Name: %s\tTotal Domains: %d\n", - return_data->service_name, return_data->total_domains); - - for (i = 0; i < return_data->total_domains; i++) { - pr_err("Instance ID: %d\t ", - return_data->domain_list[i].instance_id); - pr_err("Domain Name: %s\n", - return_data->domain_list[i].name); - rc = find_subsys(return_data->domain_list[i].name, - subsys); - if (rc < 0) - pr_err("No valid subsys found for %s!\n", - return_data->domain_list[i].name); - else - pr_err("Subsys: %s\n", subsys); - } - return 0; -} - -static struct notifier_block pdr_service_nb = { - .notifier_call = servloc_test_pdr_cb, -}; - -static ssize_t servloc_read(struct file *filp, char __user *ubuf, - size_t cnt, loff_t *ppos) -{ - int rc = 0; - char *node_name = filp->private_data; - - if (!strcmp(node_name, "test_servloc_get")) - rc = get_service_location(test_data.client_name, - test_data.service_name, &pdr_service_nb); - - return rc; -} - -static ssize_t servloc_write(struct file *fp, const char __user *buf, - size_t count, loff_t *unused) -{ - char *node_name = fp->private_data; - - if (!buf) - return -EIO; - if (!strcmp(node_name, "service_name")) { - snprintf(test_data.service_name, sizeof(test_data.service_name), - "%.*s", (int) min((size_t)count - 1, - (sizeof(test_data.service_name) - 1)), buf); - } else { - snprintf(test_data.client_name, sizeof(test_data.client_name), - "%.*s", (int) min((size_t)count - 1, - (sizeof(test_data.client_name) - 1)), buf); - } - return count; -} - -static const struct file_operations servloc_fops = { - .open = simple_open, - .read = servloc_read, - .write = servloc_write, -}; - -static struct dentry *servloc_base_dir; -static struct dentry *test_servloc_file; - -static int __init servloc_debugfs_init(void) -{ - servloc_base_dir = debugfs_create_dir("test_servloc", NULL); - return !servloc_base_dir ? -ENOMEM : 0; -} - -static void servloc_debugfs_exit(void) -{ - debugfs_remove_recursive(servloc_base_dir); -} - -static int servloc_debugfs_add(void) -{ - int rc; - - if (!servloc_base_dir) - return -ENOMEM; - - test_servloc_file = debugfs_create_file("client_name", - S_IRUGO | S_IWUSR, servloc_base_dir, - "client_name", &servloc_fops); - rc = !test_servloc_file ? -ENOMEM : 0; - - if (rc == 0) { - test_servloc_file = debugfs_create_file("service_name", - S_IRUGO | S_IWUSR, servloc_base_dir, - "service_name", &servloc_fops); - rc = !test_servloc_file ? -ENOMEM : 0; - } - - if (rc == 0) { - test_servloc_file = debugfs_create_file("test_servloc_get", - S_IRUGO | S_IWUSR, servloc_base_dir, - "test_servloc_get", &servloc_fops); - rc = !test_servloc_file ? -ENOMEM : 0; - } - return rc; -} - -static int __init service_locator_init(void) -{ - pr_debug("service_locator_status = %d\n", locator_status); - if (servloc_debugfs_init()) - pr_err("Could not create test_servloc base directory!"); - if (servloc_debugfs_add()) - pr_err("Could not create test_servloc node entries!"); - return 0; -} - -static void __exit service_locator_exit(void) -{ - servloc_debugfs_exit(); -} -module_init(service_locator_init); -module_exit(service_locator_exit); diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c index fa916ac5ade4..68592feccb33 100644 --- a/drivers/soc/qcom/service-notifier.c +++ b/drivers/soc/qcom/service-notifier.c @@ -21,7 +21,6 @@ #include <linux/slab.h> #include <linux/of.h> #include <linux/err.h> -#include <linux/debugfs.h> #include <linux/uaccess.h> #include <soc/qcom/subsystem_restart.h> @@ -752,179 +751,3 @@ int service_notif_unregister_notifier(void *service_notif_handle, &service_notif->service_notif_rcvr_list, nb); } EXPORT_SYMBOL(service_notif_unregister_notifier); - -struct service_notifier_test_data { - char service_path[MAX_STRING_LEN]; - int instance_id; - struct notifier_block nb; - void *service_notif_handle; -}; - -static struct service_notifier_test_data test_data; - -static void print_service_provider_state(int notification, char *type) -{ - if (notification == SERVREG_NOTIF_SERVICE_STATE_DOWN_V01) - pr_info("%s: Service %s down!\n", type, test_data.service_path); - else if (notification == SERVREG_NOTIF_SERVICE_STATE_UP_V01) - pr_info("%s: Service %s up!\n", type, test_data.service_path); - else if (notification == SERVREG_NOTIF_SERVICE_STATE_UNINIT_V01) - pr_info("%s: Service %s state uninit!\n", type, - test_data.service_path); - else - pr_info("%s: Service %s state Unknown 0x%x!\n", type, - test_data.service_path, notification); -} - -static int nb_callback(struct notifier_block *nb, - unsigned long notification, - void *data) -{ - print_service_provider_state((int)notification, "Notification:"); - return 0; -} - -static ssize_t show_service_path(struct seq_file *f, void *unused) -{ - if (test_data.service_notif_handle) - seq_printf(f, "Service Path: %s\n", test_data.service_path); - else - seq_puts(f, "No existing notifier\n"); - return 0; -} - - -static ssize_t set_service_notifier_register(struct file *fp, - const char __user *buf, - size_t count, loff_t *ppos) -{ - int curr_state = INT_MAX, rc; - - if (!buf) - return -EIO; - if (test_data.service_notif_handle) { - service_notif_unregister_notifier( - test_data.service_notif_handle, - &test_data.nb); - test_data.service_notif_handle = NULL; - pr_info("Unregistering existing notifier for %s\n", - test_data.service_path); - } - rc = simple_write_to_buffer(test_data.service_path, MAX_STRING_LEN, - ppos, buf, count - 1); - if (rc != count - 1) { - pr_err("Unable to read data into kernel buffer\n"); - goto err; - } - test_data.nb.notifier_call = nb_callback; - test_data.service_notif_handle = service_notif_register_notifier( - test_data.service_path, - test_data.instance_id, &test_data.nb, - &curr_state); - if (!IS_ERR(test_data.service_notif_handle)) { - pr_info("Notifier Registered for service %s\n", - test_data.service_path); - print_service_provider_state(curr_state, "Initial State"); - return count; - } -err: - test_data.service_notif_handle = NULL; - pr_err("Unable to register notifier for %s\n", test_data.service_path); - return -EIO; -} - -static int open_service_notifier_register(struct inode *inode, struct file *f) -{ - return single_open(f, (void *) show_service_path, - inode->i_private); -} - -static const struct file_operations service_notifier_register_fops = { - .open = open_service_notifier_register, - .read = seq_read, - .write = set_service_notifier_register, - .llseek = seq_lseek, - .release = seq_release, -}; - -static ssize_t show_service_notifier_id(struct seq_file *f, void *unused) -{ - seq_printf(f, "Service instance ID: %d\n", test_data.instance_id); - return 0; -} - -static ssize_t set_service_notifier_id(struct file *fp, - const char __user *buf, - size_t count, loff_t *unused) -{ - int val, rc; - char kbuf[MAX_STRING_LEN]; - - if (count > MAX_STRING_LEN) { - rc = -EIO; - goto err; - } - rc = copy_from_user(kbuf, buf, count); - if (rc != 0) { - rc = -EFAULT; - goto err; - } - - kbuf[count - 1] = '\0'; - rc = kstrtoint(kbuf, 0, &val); - if (rc < 0) - goto err; - - test_data.instance_id = val; - return count; -err: - pr_err("Invalid input parameters: rc = %d\n", rc); - return rc; -} - -static int open_service_notifier_id(struct inode *inode, struct file *f) -{ - return single_open(f, (void *) show_service_notifier_id, - inode->i_private); -} - -static const struct file_operations service_notifier_id_fops = { - .open = open_service_notifier_id, - .read = seq_read, - .write = set_service_notifier_id, - .llseek = seq_lseek, - .release = seq_release, -}; - -static struct dentry *service_notifier_dir; -static struct dentry *service_path_file; -static struct dentry *service_id_file; - -static int __init service_notifier_init(void) -{ - service_notifier_dir = debugfs_create_dir("service_notifier", NULL); - if (service_notifier_dir) { - service_path_file = debugfs_create_file("service_path", - S_IRUGO | S_IWUSR, service_notifier_dir, NULL, - &service_notifier_register_fops); - if (!service_path_file) - goto err; - service_id_file = debugfs_create_file("service_id", - S_IRUGO | S_IWUSR, service_notifier_dir, NULL, - &service_notifier_id_fops); - if (!service_id_file) - goto err; - } - return 0; -err: - debugfs_remove_recursive(service_notifier_dir); - return 0; -} - -static void __exit service_notifier_exit(void) -{ - debugfs_remove_recursive(service_notifier_dir); - test_data.nb.notifier_call = nb_callback; -} -module_init(service_notifier_init); -module_exit(service_notifier_exit); diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index dd3e545eb7da..c1d8748a5d08 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -46,6 +46,7 @@ #define SMEM_IMAGE_VERSION_OEM_OFFSET 95 #define SMEM_IMAGE_VERSION_PARTITION_APPS 10 +static DECLARE_RWSEM(current_image_rwsem); enum { HW_PLATFORM_UNKNOWN = 0, HW_PLATFORM_SURF = 1, @@ -530,7 +531,7 @@ static struct msm_soc_info cpu_of_id[] = { /* Cobalt IDs */ [292] = {MSM_CPU_8998, "MSM8998"}, - [319] = {MSM_CPU_8998, "APQ8998"}, + [319] = {MSM_CPU_8998, "APQ8098"}, /* Hamster ID */ [306] = {MSM_CPU_HAMSTER, "MSMHAMSTER"}, @@ -899,7 +900,9 @@ msm_get_image_version(struct device *dev, pr_err("Failed to get image version base address"); return snprintf(buf, SMEM_IMAGE_VERSION_NAME_SIZE, "Unknown"); } + down_read(¤t_image_rwsem); string_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE; + up_read(¤t_image_rwsem); return snprintf(buf, SMEM_IMAGE_VERSION_NAME_SIZE, "%-.75s\n", string_address); } @@ -912,14 +915,19 @@ msm_set_image_version(struct device *dev, { char *store_address; - if (current_image != SMEM_IMAGE_VERSION_PARTITION_APPS) + down_read(¤t_image_rwsem); + if (current_image != SMEM_IMAGE_VERSION_PARTITION_APPS) { + up_read(¤t_image_rwsem); return count; + } store_address = socinfo_get_image_version_base_address(); if (IS_ERR_OR_NULL(store_address)) { pr_err("Failed to get image version base address"); + up_read(¤t_image_rwsem); return count; } store_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE; + up_read(¤t_image_rwsem); snprintf(store_address, SMEM_IMAGE_VERSION_NAME_SIZE, "%-.75s", buf); return count; } @@ -937,7 +945,9 @@ msm_get_image_variant(struct device *dev, return snprintf(buf, SMEM_IMAGE_VERSION_VARIANT_SIZE, "Unknown"); } + down_read(¤t_image_rwsem); string_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE; + up_read(¤t_image_rwsem); string_address += SMEM_IMAGE_VERSION_VARIANT_OFFSET; return snprintf(buf, SMEM_IMAGE_VERSION_VARIANT_SIZE, "%-.20s\n", string_address); @@ -951,14 +961,19 @@ msm_set_image_variant(struct device *dev, { char *store_address; - if (current_image != SMEM_IMAGE_VERSION_PARTITION_APPS) + down_read(¤t_image_rwsem); + if (current_image != SMEM_IMAGE_VERSION_PARTITION_APPS) { + up_read(¤t_image_rwsem); return count; + } store_address = socinfo_get_image_version_base_address(); if (IS_ERR_OR_NULL(store_address)) { pr_err("Failed to get image version base address"); + up_read(¤t_image_rwsem); return count; } store_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE; + up_read(¤t_image_rwsem); store_address += SMEM_IMAGE_VERSION_VARIANT_OFFSET; snprintf(store_address, SMEM_IMAGE_VERSION_VARIANT_SIZE, "%-.20s", buf); return count; @@ -976,7 +991,9 @@ msm_get_image_crm_version(struct device *dev, pr_err("Failed to get image version base address"); return snprintf(buf, SMEM_IMAGE_VERSION_OEM_SIZE, "Unknown"); } + down_read(¤t_image_rwsem); string_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE; + up_read(¤t_image_rwsem); string_address += SMEM_IMAGE_VERSION_OEM_OFFSET; return snprintf(buf, SMEM_IMAGE_VERSION_OEM_SIZE, "%-.33s\n", string_address); @@ -990,14 +1007,19 @@ msm_set_image_crm_version(struct device *dev, { char *store_address; - if (current_image != SMEM_IMAGE_VERSION_PARTITION_APPS) + down_read(¤t_image_rwsem); + if (current_image != SMEM_IMAGE_VERSION_PARTITION_APPS) { + up_read(¤t_image_rwsem); return count; + } store_address = socinfo_get_image_version_base_address(); if (IS_ERR_OR_NULL(store_address)) { pr_err("Failed to get image version base address"); + up_read(¤t_image_rwsem); return count; } store_address += current_image * SMEM_IMAGE_VERSION_SINGLE_BLOCK_SIZE; + up_read(¤t_image_rwsem); store_address += SMEM_IMAGE_VERSION_OEM_OFFSET; snprintf(store_address, SMEM_IMAGE_VERSION_OEM_SIZE, "%-.33s", buf); return count; @@ -1008,8 +1030,14 @@ msm_get_image_number(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", + int ret; + + down_read(¤t_image_rwsem); + ret = snprintf(buf, PAGE_SIZE, "%d\n", current_image); + up_read(¤t_image_rwsem); + return ret; + } static ssize_t @@ -1021,10 +1049,12 @@ msm_select_image(struct device *dev, struct device_attribute *attr, ret = kstrtoint(buf, 10, &digit); if (ret) return ret; + down_write(¤t_image_rwsem); if (0 <= digit && digit < SMEM_IMAGE_VERSION_BLOCKS_COUNT) current_image = digit; else current_image = 0; + up_write(¤t_image_rwsem); return count; } @@ -1235,9 +1265,9 @@ static void * __init setup_dummy_socinfo(void) dummy_socinfo.id = 327; strlcpy(dummy_socinfo.build_id, "sda630 - ", sizeof(dummy_socinfo.build_id)); - } else if (early_machine_is_apq8998()) { + } else if (early_machine_is_apq8098()) { dummy_socinfo.id = 319; - strlcpy(dummy_socinfo.build_id, "apq8998 - ", + strlcpy(dummy_socinfo.build_id, "apq8098 - ", sizeof(dummy_socinfo.build_id)); } diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c index 0c44d76bc7c7..10eebb0316e8 100644 --- a/drivers/soc/qcom/spcom.c +++ b/drivers/soc/qcom/spcom.c @@ -463,8 +463,7 @@ static void spcom_notify_state(void *handle, const void *priv, unsigned event) switch (event) { case GLINK_CONNECTED: pr_debug("GLINK_CONNECTED, ch name [%s].\n", ch->name); - complete_all(&ch->connect); - + ch->glink_state = event; /* * if spcom_notify_state() is called within glink_open() * then ch->glink_handle is not updated yet. @@ -484,6 +483,7 @@ static void spcom_notify_state(void *handle, const void *priv, unsigned event) ch->rx_buf_size); ch->rx_buf_ready = true; } + complete_all(&ch->connect); break; case GLINK_LOCAL_DISCONNECTED: /* @@ -491,6 +491,7 @@ static void spcom_notify_state(void *handle, const void *priv, unsigned event) * only after *both* sides closed the channel. */ pr_debug("GLINK_LOCAL_DISCONNECTED, ch [%s].\n", ch->name); + ch->glink_state = event; complete_all(&ch->disconnect); break; case GLINK_REMOTE_DISCONNECTED: @@ -501,6 +502,8 @@ static void spcom_notify_state(void *handle, const void *priv, unsigned event) */ pr_err("GLINK_REMOTE_DISCONNECTED, ch [%s].\n", ch->name); + ch->glink_state = event; + /* * Abort any blocking read() operation. * The glink notification might be after REMOTE_DISCONNECT. @@ -518,8 +521,6 @@ static void spcom_notify_state(void *handle, const void *priv, unsigned event) (int) event, ch->name); return; } - - ch->glink_state = event; } /** @@ -898,12 +899,12 @@ static int spcom_rx(struct spcom_channel *ch, goto exit_err; } +copy_buf: if (!ch->glink_rx_buf) { pr_err("invalid glink_rx_buf.\n"); goto exit_err; } -copy_buf: /* Copy from glink buffer to spcom buffer */ size = min_t(int, ch->actual_rx_size, size); memcpy(buf, ch->glink_rx_buf, size); @@ -1723,12 +1724,16 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch, pr_debug("ion handle ok.\n"); + /* ION buf lock doesn't involve any rx/tx data to SP. */ + mutex_lock(&ch->lock); + /* Check if this ION buffer is already locked */ for (i = 0 ; i < ARRAY_SIZE(ch->ion_handle_table) ; i++) { if (ch->ion_handle_table[i] == ion_handle) { pr_err("fd [%d] ion buf is already locked.\n", fd); /* decrement back the ref count */ ion_free(spcom_dev->ion_client, ion_handle); + mutex_unlock(&ch->lock); return -EINVAL; } } @@ -1740,6 +1745,7 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch, ch->ion_fd_table[i] = fd; pr_debug("ch [%s] locked ion buf #%d, fd [%d].\n", ch->name, i, fd); + mutex_unlock(&ch->lock); return 0; } } @@ -1748,6 +1754,8 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch, /* decrement back the ref count */ ion_free(spcom_dev->ion_client, ion_handle); + mutex_unlock(&ch->lock); + return -EFAULT; } @@ -1826,8 +1834,13 @@ static int spcom_handle_unlock_ion_buf_command(struct spcom_channel *ch, return -EINVAL; } + /* ION buf unlock doesn't involve any rx/tx data to SP. */ + mutex_lock(&ch->lock); + ret = spcom_unlock_ion_buf(ch, fd); + mutex_unlock(&ch->lock); + return ret; } @@ -2132,7 +2145,6 @@ static int spcom_device_release(struct inode *inode, struct file *filp) { struct spcom_channel *ch; const char *name = file_to_filename(filp); - bool connected = false; pr_debug("Close file [%s].\n", name); @@ -2154,19 +2166,18 @@ static int spcom_device_release(struct inode *inode, struct file *filp) } /* channel might be already closed or disconnected */ - if (spcom_is_channel_open(ch) && spcom_is_channel_connected(ch)) - connected = true; + if (!spcom_is_channel_open(ch)) { + pr_err("ch [%s] already closed.\n", name); + return 0; + } reinit_completion(&ch->disconnect); spcom_close(ch); - if (connected) { - pr_debug("Wait for event GLINK_LOCAL_DISCONNECTED, ch [%s].\n", - name); - wait_for_completion(&ch->disconnect); - pr_debug("GLINK_LOCAL_DISCONNECTED signaled, ch [%s].\n", name); - } + pr_debug("Wait for event GLINK_LOCAL_DISCONNECTED, ch [%s].\n", name); + wait_for_completion(&ch->disconnect); + pr_debug("GLINK_LOCAL_DISCONNECTED signaled, ch [%s].\n", name); return 0; } diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c index c35ec26fefa2..d3d0b8594c9f 100644 --- a/drivers/soc/qcom/subsystem_restart.c +++ b/drivers/soc/qcom/subsystem_restart.c @@ -28,7 +28,6 @@ #include <linux/spinlock.h> #include <linux/device.h> #include <linux/idr.h> -#include <linux/debugfs.h> #include <linux/interrupt.h> #include <linux/of_gpio.h> #include <linux/cdev.h> @@ -149,7 +148,6 @@ struct restart_log { * @restart_level: restart level (0 - panic, 1 - related, 2 - independent, etc.) * @restart_order: order of other devices this devices restarts with * @crash_count: number of times the device has crashed - * @dentry: debugfs directory for this device * @do_ramdump_on_put: ramdump on subsystem_put() if true * @err_ready: completion variable to record error ready from subsystem * @crashed: indicates if subsystem has crashed @@ -171,9 +169,6 @@ struct subsys_device { int restart_level; int crash_count; struct subsys_soc_restart_order *restart_order; -#ifdef CONFIG_DEBUG_FS - struct dentry *dentry; -#endif bool do_ramdump_on_put; struct cdev char_dev; dev_t dev_no; @@ -352,10 +347,11 @@ static struct device_attribute subsys_attrs[] = { __ATTR_NULL, }; -static struct bus_type subsys_bus_type = { +struct bus_type subsys_bus_type = { .name = "msm_subsys", .dev_attrs = subsys_attrs, }; +EXPORT_SYMBOL(subsys_bus_type); static DEFINE_IDA(subsys_ida); @@ -1169,87 +1165,6 @@ void notify_proxy_unvote(struct device *device) notify_each_subsys_device(&dev, 1, SUBSYS_PROXY_UNVOTE, NULL); } -#ifdef CONFIG_DEBUG_FS -static ssize_t subsys_debugfs_read(struct file *filp, char __user *ubuf, - size_t cnt, loff_t *ppos) -{ - int r; - char buf[40]; - struct subsys_device *subsys = filp->private_data; - - r = snprintf(buf, sizeof(buf), "%d\n", subsys->count); - return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); -} - -static ssize_t subsys_debugfs_write(struct file *filp, - const char __user *ubuf, size_t cnt, loff_t *ppos) -{ - struct subsys_device *subsys = filp->private_data; - char buf[10]; - char *cmp; - - cnt = min(cnt, sizeof(buf) - 1); - if (copy_from_user(&buf, ubuf, cnt)) - return -EFAULT; - buf[cnt] = '\0'; - cmp = strstrip(buf); - - if (!strcmp(cmp, "restart")) { - if (subsystem_restart_dev(subsys)) - return -EIO; - } else if (!strcmp(cmp, "get")) { - if (subsystem_get(subsys->desc->name)) - return -EIO; - } else if (!strcmp(cmp, "put")) { - subsystem_put(subsys); - } else { - return -EINVAL; - } - - return cnt; -} - -static const struct file_operations subsys_debugfs_fops = { - .open = simple_open, - .read = subsys_debugfs_read, - .write = subsys_debugfs_write, -}; - -static struct dentry *subsys_base_dir; - -static int __init subsys_debugfs_init(void) -{ - subsys_base_dir = debugfs_create_dir("msm_subsys", NULL); - return !subsys_base_dir ? -ENOMEM : 0; -} - -static void subsys_debugfs_exit(void) -{ - debugfs_remove_recursive(subsys_base_dir); -} - -static int subsys_debugfs_add(struct subsys_device *subsys) -{ - if (!subsys_base_dir) - return -ENOMEM; - - subsys->dentry = debugfs_create_file(subsys->desc->name, - S_IRUGO | S_IWUSR, subsys_base_dir, - subsys, &subsys_debugfs_fops); - return !subsys->dentry ? -ENOMEM : 0; -} - -static void subsys_debugfs_remove(struct subsys_device *subsys) -{ - debugfs_remove(subsys->dentry); -} -#else -static int __init subsys_debugfs_init(void) { return 0; }; -static void subsys_debugfs_exit(void) { } -static int subsys_debugfs_add(struct subsys_device *subsys) { return 0; } -static void subsys_debugfs_remove(struct subsys_device *subsys) { } -#endif - static int subsys_device_open(struct inode *inode, struct file *file) { struct subsys_device *device, *subsys_dev = 0; @@ -1686,17 +1601,8 @@ struct subsys_device *subsys_register(struct subsys_desc *desc) mutex_init(&subsys->track.lock); - ret = subsys_debugfs_add(subsys); - if (ret) { - ida_simple_remove(&subsys_ida, subsys->id); - wakeup_source_trash(&subsys->ssr_wlock); - kfree(subsys); - return ERR_PTR(ret); - } - ret = device_register(&subsys->dev); if (ret) { - subsys_debugfs_remove(subsys); put_device(&subsys->dev); return ERR_PTR(ret); } @@ -1758,7 +1664,6 @@ err_setup_irqs: if (ofnode) subsys_remove_restart_order(ofnode); err_register: - subsys_debugfs_remove(subsys); device_unregister(&subsys->dev); return ERR_PTR(ret); } @@ -1787,7 +1692,6 @@ void subsys_unregister(struct subsys_device *subsys) WARN_ON(subsys->count); device_unregister(&subsys->dev); mutex_unlock(&subsys->track.lock); - subsys_debugfs_remove(subsys); subsys_char_device_remove(subsys); sysmon_notifier_unregister(subsys->desc); if (subsys->desc->edge) @@ -1827,9 +1731,6 @@ static int __init subsys_restart_init(void) ret = bus_register(&subsys_bus_type); if (ret) goto err_bus; - ret = subsys_debugfs_init(); - if (ret) - goto err_debugfs; char_class = class_create(THIS_MODULE, "subsys"); if (IS_ERR(char_class)) { @@ -1848,8 +1749,6 @@ static int __init subsys_restart_init(void) err_soc: class_destroy(char_class); err_class: - subsys_debugfs_exit(); -err_debugfs: bus_unregister(&subsys_bus_type); err_bus: destroy_workqueue(ssr_wq); diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c index 470ecfdd9f5e..745a069df88a 100644 --- a/drivers/soc/qcom/watchdog_v2.c +++ b/drivers/soc/qcom/watchdog_v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,6 +29,7 @@ #include <linux/wait.h> #include <soc/qcom/scm.h> #include <soc/qcom/memory_dump.h> +#include <soc/qcom/minidump.h> #include <soc/qcom/watchdog.h> #define MODULE_NAME "msm_watchdog" @@ -521,6 +522,8 @@ void register_scan_dump(struct msm_watchdog_data *wdog_dd) dump_data->addr = virt_to_phys(dump_addr); dump_data->len = wdog_dd->scandump_size; + strlcpy(dump_data->name, "KSCANDUMP", sizeof(dump_data->name)); + dump_entry.id = MSM_DUMP_DATA_SCANDUMP; dump_entry.addr = virt_to_phys(dump_data); ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry); @@ -605,6 +608,9 @@ static void configure_bark_dump(struct msm_watchdog_data *wdog_dd) cpu_data[cpu].addr = virt_to_phys(cpu_buf + cpu * MAX_CPU_CTX_SIZE); cpu_data[cpu].len = MAX_CPU_CTX_SIZE; + snprintf(cpu_data[cpu].name, sizeof(cpu_data[cpu].name), + "KCPU_CTX%d", cpu); + dump_entry.id = MSM_DUMP_DATA_CPU_CTX + cpu; dump_entry.addr = virt_to_phys(&cpu_data[cpu]); ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, @@ -820,6 +826,7 @@ static int msm_watchdog_probe(struct platform_device *pdev) { int ret; struct msm_watchdog_data *wdog_dd; + struct md_region md_entry; if (!pdev->dev.of_node || !enable) return -ENODEV; @@ -841,6 +848,15 @@ static int msm_watchdog_probe(struct platform_device *pdev) goto err; } init_watchdog_data(wdog_dd); + + /* Add wdog info to minidump table */ + strlcpy(md_entry.name, "KWDOGDATA", sizeof(md_entry.name)); + md_entry.virt_addr = (uintptr_t)wdog_dd; + md_entry.phys_addr = virt_to_phys(wdog_dd); + md_entry.size = sizeof(*wdog_dd); + if (msm_minidump_add_region(&md_entry)) + pr_info("Failed to add RTB in Minidump\n"); + return 0; err: kzfree(wdog_dd); diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c index 8efe18dcc98f..548efd11e856 100644 --- a/drivers/spi/spi_qsd.c +++ b/drivers/spi/spi_qsd.c @@ -1496,9 +1496,9 @@ static inline void msm_spi_set_cs(struct spi_device *spi, bool set_flag) u32 spi_ioc_orig; int rc; - if (dd->suspended) { - dev_err(dd->dev, "%s: SPI operational state not valid %d\n", - __func__, dd->suspended); + rc = pm_runtime_get_sync(dd->dev); + if (rc < 0) { + dev_err(dd->dev, "Failure during runtime get"); return; } @@ -1524,6 +1524,8 @@ static inline void msm_spi_set_cs(struct spi_device *spi, bool set_flag) writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL); if (dd->pdata->is_shared) put_local_resources(dd); + pm_runtime_mark_last_busy(dd->dev); + pm_runtime_put_autosuspend(dd->dev); } static void reset_core(struct msm_spi *dd) @@ -1707,16 +1709,23 @@ static int msm_spi_prepare_transfer_hardware(struct spi_master *master) struct msm_spi *dd = spi_master_get_devdata(master); int resume_state = 0; - if (!pm_runtime_enabled(dd->dev)) { - dev_err(dd->dev, "Runtime PM not available\n"); - resume_state = -EBUSY; - goto spi_finalize; - } - resume_state = pm_runtime_get_sync(dd->dev); if (resume_state < 0) goto spi_finalize; + /* + * Counter-part of system-suspend when runtime-pm is not enabled. + * This way, resume can be left empty and device will be put in + * active mode only if client requests anything on the bus + */ + if (!pm_runtime_enabled(dd->dev)) + resume_state = msm_spi_pm_resume_runtime(dd->dev); + if (resume_state < 0) + goto spi_finalize; + if (dd->suspended) { + resume_state = -EBUSY; + goto spi_finalize; + } return 0; spi_finalize: @@ -1728,11 +1737,6 @@ static int msm_spi_unprepare_transfer_hardware(struct spi_master *master) { struct msm_spi *dd = spi_master_get_devdata(master); - if (!pm_runtime_enabled(dd->dev)) { - dev_err(dd->dev, "Runtime PM not available\n"); - return -EBUSY; - } - pm_runtime_mark_last_busy(dd->dev); pm_runtime_put_autosuspend(dd->dev); return 0; @@ -2672,10 +2676,27 @@ resume_exit: #ifdef CONFIG_PM_SLEEP static int msm_spi_suspend(struct device *device) { - if (!pm_runtime_status_suspended(device)) { - dev_err(device, "Runtime not suspended, deny sys suspend"); - return -EBUSY; + if (!pm_runtime_enabled(device) || !pm_runtime_suspended(device)) { + struct platform_device *pdev = to_platform_device(device); + struct spi_master *master = platform_get_drvdata(pdev); + struct msm_spi *dd; + + dev_dbg(device, "system suspend"); + if (!master) + goto suspend_exit; + dd = spi_master_get_devdata(master); + if (!dd) + goto suspend_exit; + msm_spi_pm_suspend_runtime(device); + + /* + * set the device's runtime PM status to 'suspended' + */ + pm_runtime_disable(device); + pm_runtime_set_suspended(device); + pm_runtime_enable(device); } +suspend_exit: return 0; } diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index a5bfeab596ac..d1802bcba0fb 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -166,6 +166,7 @@ struct spmi_pmic_arb { u16 max_apid; u16 max_periph; u32 *mapping_table; + int reserved_chan; DECLARE_BITMAP(mapping_table_valid, PMIC_ARB_MAX_PERIPHS); struct irq_domain *domain; struct spmi_controller *spmic; @@ -861,6 +862,10 @@ static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pa, u16 ppid) * ppid_to_apid is an in-memory invert of that table. */ for (apid = pa->last_apid; apid < pa->max_periph; apid++) { + /* Do not keep the reserved channel in the mapping table */ + if (pa->reserved_chan >= 0 && apid == pa->reserved_chan) + continue; + regval = readl_relaxed(pa->cnfg + SPMI_OWNERSHIP_TABLE_REG(apid)); pa->apid_data[apid].irq_owner @@ -920,6 +925,10 @@ static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb *pa) * receive interrupts from the PPID. */ for (apid = 0; apid < pa->max_periph; apid++) { + /* Do not keep the reserved channel in the mapping table */ + if (pa->reserved_chan >= 0 && apid == pa->reserved_chan) + continue; + offset = pa->ver_ops->channel_map_offset(apid); if (offset >= pa->core_size) break; @@ -1340,6 +1349,10 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) pa->ee = ee; + pa->reserved_chan = -EINVAL; + of_property_read_u32(pdev->dev.of_node, "qcom,reserved-chan", + &pa->reserved_chan); + pa->mapping_table = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS - 1, sizeof(*pa->mapping_table), GFP_KERNEL); if (!pa->mapping_table) { diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index 3f1133230a1a..28c9afe538ca 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -392,6 +392,7 @@ static int ashmem_mmap(struct file *file, struct vm_area_struct *vma) ret = PTR_ERR(vmfile); goto out; } + vmfile->f_mode |= FMODE_LSEEK; asma->file = vmfile; } get_file(asma->file); @@ -752,10 +753,12 @@ static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case ASHMEM_SET_SIZE: ret = -EINVAL; + mutex_lock(&ashmem_mutex); if (!asma->file) { ret = 0; asma->size = (size_t)arg; } + mutex_unlock(&ashmem_mutex); break; case ASHMEM_GET_SIZE: ret = asma->size; diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index faa81c28a0d3..58bf3d2f52bd 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -114,6 +114,7 @@ struct ion_client { */ struct ion_handle { struct kref ref; + unsigned int user_ref_count; struct ion_client *client; struct ion_buffer *buffer; struct rb_node node; @@ -433,6 +434,50 @@ int ion_handle_put(struct ion_handle *handle) return ret; } +/* Must hold the client lock */ +static void user_ion_handle_get(struct ion_handle *handle) +{ + if (handle->user_ref_count++ == 0) + kref_get(&handle->ref); +} + +/* Must hold the client lock */ +static struct ion_handle *user_ion_handle_get_check_overflow( + struct ion_handle *handle) +{ + if (handle->user_ref_count + 1 == 0) + return ERR_PTR(-EOVERFLOW); + user_ion_handle_get(handle); + return handle; +} + +/* passes a kref to the user ref count. + * We know we're holding a kref to the object before and + * after this call, so no need to reverify handle. + */ +static struct ion_handle *pass_to_user(struct ion_handle *handle) +{ + struct ion_client *client = handle->client; + struct ion_handle *ret; + + mutex_lock(&client->lock); + ret = user_ion_handle_get_check_overflow(handle); + ion_handle_put_nolock(handle); + mutex_unlock(&client->lock); + return ret; +} + +/* Must hold the client lock */ +static int user_ion_handle_put_nolock(struct ion_handle *handle) +{ + int ret = 0; + + if (--handle->user_ref_count == 0) + ret = ion_handle_put_nolock(handle); + + return ret; +} + static struct ion_handle *ion_handle_lookup(struct ion_client *client, struct ion_buffer *buffer) { @@ -644,6 +689,25 @@ static void ion_free_nolock(struct ion_client *client, struct ion_handle *handle ion_handle_put_nolock(handle); } +static void user_ion_free_nolock(struct ion_client *client, + struct ion_handle *handle) +{ + bool valid_handle; + + WARN_ON(client != handle->client); + + valid_handle = ion_handle_validate(client, handle); + if (!valid_handle) { + WARN(1, "%s: invalid handle passed to free.\n", __func__); + return; + } + if (!handle->user_ref_count > 0) { + WARN(1, "%s: User does not have access!\n", __func__); + return; + } + user_ion_handle_put_nolock(handle); +} + void ion_free(struct ion_client *client, struct ion_handle *handle) { BUG_ON(client != handle->client); @@ -1518,7 +1582,7 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) data.allocation.flags, true); if (IS_ERR(handle)) return PTR_ERR(handle); - + pass_to_user(handle); data.allocation.handle = handle->id; cleanup_handle = handle; @@ -1534,7 +1598,7 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) mutex_unlock(&client->lock); return PTR_ERR(handle); } - ion_free_nolock(client, handle); + user_ion_free_nolock(client, handle); ion_handle_put_nolock(handle); mutex_unlock(&client->lock); break; @@ -1558,10 +1622,15 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) struct ion_handle *handle; handle = ion_import_dma_buf(client, data.fd.fd); - if (IS_ERR(handle)) + if (IS_ERR(handle)) { ret = PTR_ERR(handle); - else - data.handle.handle = handle->id; + } else { + handle = pass_to_user(handle); + if (IS_ERR(handle)) + ret = PTR_ERR(handle); + else + data.handle.handle = handle->id; + } break; } case ION_IOC_SYNC: @@ -1593,8 +1662,10 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (dir & _IOC_READ) { if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) { if (cleanup_handle) { - ion_free(client, cleanup_handle); - ion_handle_put(cleanup_handle); + mutex_lock(&client->lock); + user_ion_free_nolock(client, cleanup_handle); + ion_handle_put_nolock(cleanup_handle); + mutex_unlock(&client->lock); } return -EFAULT; } diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index bec687853c5d..205af6627b80 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -217,6 +217,22 @@ static int test_task_flag(struct task_struct *p, int flag) return 0; } +static int test_task_state(struct task_struct *p, int state) +{ + struct task_struct *t; + + for_each_thread(p, t) { + task_lock(t); + if (t->state & state) { + task_unlock(t); + return 1; + } + task_unlock(t); + } + + return 0; +} + static DEFINE_MUTEX(scan_mutex); int can_use_cma_pages(gfp_t gfp_mask) @@ -404,7 +420,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) int other_free; int other_file; - if (mutex_lock_interruptible(&scan_mutex) < 0) + if (!mutex_trylock(&scan_mutex)) return 0; other_free = global_page_state(NR_FREE_PAGES); @@ -462,8 +478,6 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) if (time_before_eq(jiffies, lowmem_deathpending_timeout)) { if (test_task_flag(tsk, TIF_MEMDIE)) { rcu_read_unlock(); - /* give the system time to free up the memory */ - msleep_interruptible(20); mutex_unlock(&scan_mutex); return 0; } @@ -497,6 +511,17 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) } if (selected) { long cache_size, cache_limit, free; + + if (test_task_flag(selected, TIF_MEMDIE) && + (test_task_state(selected, TASK_UNINTERRUPTIBLE))) { + lowmem_print(2, "'%s' (%d) is already killed\n", + selected->comm, + selected->pid); + rcu_read_unlock(); + mutex_unlock(&scan_mutex); + return 0; + } + task_lock(selected); send_sig(SIGKILL, selected, 0); /* @@ -565,7 +590,8 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) static struct shrinker lowmem_shrinker = { .scan_objects = lowmem_scan, .count_objects = lowmem_count, - .seeks = DEFAULT_SEEKS * 16 + .seeks = DEFAULT_SEEKS * 16, + .flags = SHRINKER_LMK }; static int __init lowmem_init(void) diff --git a/drivers/staging/android/sync.c b/drivers/staging/android/sync.c index 9b8d17ce3a5e..5238d67490ce 100644 --- a/drivers/staging/android/sync.c +++ b/drivers/staging/android/sync.c @@ -29,6 +29,7 @@ #include "sync.h" #define CREATE_TRACE_POINTS +#define SYNC_DUMP_TIME_LIMIT 7000 #include "trace/sync.h" static const struct fence_ops android_fence_ops; @@ -392,7 +393,9 @@ int sync_fence_wait(struct sync_fence *fence, long timeout) if (timeout) { pr_info("fence timeout on [%pK] after %dms\n", fence, jiffies_to_msecs(timeout)); - sync_dump(); + if (jiffies_to_msecs(timeout) >= + SYNC_DUMP_TIME_LIMIT) + sync_dump(); } return -ETIME; } diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index de18790eb21c..d72a4058fd08 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -154,7 +154,7 @@ static void pscsi_tape_read_blocksize(struct se_device *dev, buf = kzalloc(12, GFP_KERNEL); if (!buf) - return; + goto out_free; memset(cdb, 0, MAX_COMMAND_SIZE); cdb[0] = MODE_SENSE; @@ -169,9 +169,10 @@ static void pscsi_tape_read_blocksize(struct se_device *dev, * If MODE_SENSE still returns zero, set the default value to 1024. */ sdev->sector_size = (buf[9] << 16) | (buf[10] << 8) | (buf[11]); +out_free: if (!sdev->sector_size) sdev->sector_size = 1024; -out_free: + kfree(buf); } @@ -314,9 +315,10 @@ static int pscsi_add_device_to_list(struct se_device *dev, sd->lun, sd->queue_depth); } - dev->dev_attrib.hw_block_size = sd->sector_size; + dev->dev_attrib.hw_block_size = + min_not_zero((int)sd->sector_size, 512); dev->dev_attrib.hw_max_sectors = - min_t(int, sd->host->max_sectors, queue_max_hw_sectors(q)); + min_not_zero(sd->host->max_sectors, queue_max_hw_sectors(q)); dev->dev_attrib.hw_queue_depth = sd->queue_depth; /* @@ -339,8 +341,10 @@ static int pscsi_add_device_to_list(struct se_device *dev, /* * For TYPE_TAPE, attempt to determine blocksize with MODE_SENSE. */ - if (sd->type == TYPE_TAPE) + if (sd->type == TYPE_TAPE) { pscsi_tape_read_blocksize(dev, sd); + dev->dev_attrib.hw_block_size = sd->sector_size; + } return 0; } @@ -406,7 +410,7 @@ static int pscsi_create_type_disk(struct se_device *dev, struct scsi_device *sd) /* * Called with struct Scsi_Host->host_lock called. */ -static int pscsi_create_type_rom(struct se_device *dev, struct scsi_device *sd) +static int pscsi_create_type_nondisk(struct se_device *dev, struct scsi_device *sd) __releases(sh->host_lock) { struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr; @@ -433,28 +437,6 @@ static int pscsi_create_type_rom(struct se_device *dev, struct scsi_device *sd) return 0; } -/* - * Called with struct Scsi_Host->host_lock called. - */ -static int pscsi_create_type_other(struct se_device *dev, - struct scsi_device *sd) - __releases(sh->host_lock) -{ - struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr; - struct Scsi_Host *sh = sd->host; - int ret; - - spin_unlock_irq(sh->host_lock); - ret = pscsi_add_device_to_list(dev, sd); - if (ret) - return ret; - - pr_debug("CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d:%llu\n", - phv->phv_host_id, scsi_device_type(sd->type), sh->host_no, - sd->channel, sd->id, sd->lun); - return 0; -} - static int pscsi_configure_device(struct se_device *dev) { struct se_hba *hba = dev->se_hba; @@ -542,11 +524,8 @@ static int pscsi_configure_device(struct se_device *dev) case TYPE_DISK: ret = pscsi_create_type_disk(dev, sd); break; - case TYPE_ROM: - ret = pscsi_create_type_rom(dev, sd); - break; default: - ret = pscsi_create_type_other(dev, sd); + ret = pscsi_create_type_nondisk(dev, sd); break; } @@ -611,8 +590,7 @@ static void pscsi_free_device(struct se_device *dev) else if (pdv->pdv_lld_host) scsi_host_put(pdv->pdv_lld_host); - if ((sd->type == TYPE_DISK) || (sd->type == TYPE_ROM)) - scsi_device_put(sd); + scsi_device_put(sd); pdv->pdv_sd = NULL; } @@ -1088,7 +1066,6 @@ static sector_t pscsi_get_blocks(struct se_device *dev) if (pdv->pdv_bd && pdv->pdv_bd->bd_part) return pdv->pdv_bd->bd_part->nr_sects; - dump_stack(); return 0; } diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 2e27b1034ede..90c5dffc9fa4 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -1096,9 +1096,15 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) return ret; break; case VERIFY: + case VERIFY_16: size = 0; - sectors = transport_get_sectors_10(cdb); - cmd->t_task_lba = transport_lba_32(cdb); + if (cdb[0] == VERIFY) { + sectors = transport_get_sectors_10(cdb); + cmd->t_task_lba = transport_lba_32(cdb); + } else { + sectors = transport_get_sectors_16(cdb); + cmd->t_task_lba = transport_lba_64(cdb); + } cmd->execute_cmd = sbc_emulate_noop; goto check_lba; case REZERO_UNIT: diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 5b24ffd93649..83ff1724ec79 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -57,6 +57,7 @@ struct serial_private { unsigned int nr; void __iomem *remapped_bar[PCI_NUM_BAR_RESOURCES]; struct pci_serial_quirk *quirk; + const struct pciserial_board *board; int line[0]; }; @@ -4058,6 +4059,7 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) } } priv->nr = i; + priv->board = board; return priv; err_deinit: @@ -4068,7 +4070,7 @@ err_out: } EXPORT_SYMBOL_GPL(pciserial_init_ports); -void pciserial_remove_ports(struct serial_private *priv) +void pciserial_detach_ports(struct serial_private *priv) { struct pci_serial_quirk *quirk; int i; @@ -4088,7 +4090,11 @@ void pciserial_remove_ports(struct serial_private *priv) quirk = find_quirk(priv->dev); if (quirk->exit) quirk->exit(priv->dev); +} +void pciserial_remove_ports(struct serial_private *priv) +{ + pciserial_detach_ports(priv); kfree(priv); } EXPORT_SYMBOL_GPL(pciserial_remove_ports); @@ -5819,7 +5825,7 @@ static pci_ers_result_t serial8250_io_error_detected(struct pci_dev *dev, return PCI_ERS_RESULT_DISCONNECT; if (priv) - pciserial_suspend_ports(priv); + pciserial_detach_ports(priv); pci_disable_device(dev); @@ -5844,9 +5850,18 @@ static pci_ers_result_t serial8250_io_slot_reset(struct pci_dev *dev) static void serial8250_io_resume(struct pci_dev *dev) { struct serial_private *priv = pci_get_drvdata(dev); + const struct pciserial_board *board; - if (priv) - pciserial_resume_ports(priv); + if (!priv) + return; + + board = priv->board; + kfree(priv); + priv = pciserial_init_ports(dev, board); + + if (!IS_ERR(priv)) { + pci_set_drvdata(dev, priv); + } } static const struct pci_error_handlers serial8250_err_handler = { diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index deaddb950c20..24337ac3323f 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -1105,7 +1105,7 @@ static int usbtmc_probe(struct usb_interface *intf, dev_dbg(&intf->dev, "%s called\n", __func__); - data = kmalloc(sizeof(*data), GFP_KERNEL); + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -1163,6 +1163,12 @@ static int usbtmc_probe(struct usb_interface *intf, } } + if (!data->bulk_out || !data->bulk_in) { + dev_err(&intf->dev, "bulk endpoints not found\n"); + retcode = -ENODEV; + goto err_put; + } + retcode = get_capabilities(data); if (retcode) dev_err(&intf->dev, "can't read capabilities\n"); @@ -1186,6 +1192,7 @@ static int usbtmc_probe(struct usb_interface *intf, error_register: sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp); sysfs_remove_group(&intf->dev.kobj, &data_attr_grp); +err_put: kref_put(&data->kref, usbtmc_delete); return retcode; } diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index ac30a051ad71..325cbc9c35d8 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -246,6 +246,16 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, /* * Adjust bInterval for quirked devices. + */ + /* + * This quirk fixes bIntervals reported in ms. + */ + if (to_usb_device(ddev)->quirks & + USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL) { + n = clamp(fls(d->bInterval) + 3, i, j); + i = j = n; + } + /* * This quirk fixes bIntervals reported in * linear microframes. */ diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 9a3bf5e2977f..755dbca78646 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -966,7 +966,7 @@ static void usb_bus_init (struct usb_bus *bus) bus->bandwidth_allocated = 0; bus->bandwidth_int_reqs = 0; bus->bandwidth_isoc_reqs = 0; - mutex_init(&bus->usb_address0_mutex); + mutex_init(&bus->devnum_next_mutex); INIT_LIST_HEAD (&bus->bus_list); } @@ -2555,6 +2555,14 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, return NULL; } if (primary_hcd == NULL) { + hcd->address0_mutex = kmalloc(sizeof(*hcd->address0_mutex), + GFP_KERNEL); + if (!hcd->address0_mutex) { + kfree(hcd); + dev_dbg(dev, "hcd address0 mutex alloc failed\n"); + return NULL; + } + mutex_init(hcd->address0_mutex); hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex), GFP_KERNEL); if (!hcd->bandwidth_mutex) { @@ -2566,6 +2574,7 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, dev_set_drvdata(dev, hcd); } else { mutex_lock(&usb_port_peer_mutex); + hcd->address0_mutex = primary_hcd->address0_mutex; hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex; hcd->primary_hcd = primary_hcd; primary_hcd->primary_hcd = primary_hcd; @@ -2622,24 +2631,23 @@ EXPORT_SYMBOL_GPL(usb_create_hcd); * Don't deallocate the bandwidth_mutex until the last shared usb_hcd is * deallocated. * - * Make sure to only deallocate the bandwidth_mutex when the primary HCD is - * freed. When hcd_release() is called for either hcd in a peer set - * invalidate the peer's ->shared_hcd and ->primary_hcd pointers to - * block new peering attempts + * Make sure to deallocate the bandwidth_mutex only when the last HCD is + * freed. When hcd_release() is called for either hcd in a peer set, + * invalidate the peer's ->shared_hcd and ->primary_hcd pointers. */ static void hcd_release(struct kref *kref) { struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref); mutex_lock(&usb_port_peer_mutex); - if (hcd->primary_hcd == hcd) - kfree(hcd->bandwidth_mutex); if (hcd->shared_hcd) { struct usb_hcd *peer = hcd->shared_hcd; peer->shared_hcd = NULL; - if (peer->primary_hcd == hcd) - peer->primary_hcd = NULL; + peer->primary_hcd = NULL; + } else { + kfree(hcd->address0_mutex); + kfree(hcd->bandwidth_mutex); } mutex_unlock(&usb_port_peer_mutex); kfree(hcd); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index f84ef04284f5..41b1ef2135d7 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1991,7 +1991,7 @@ static void choose_devnum(struct usb_device *udev) struct usb_bus *bus = udev->bus; /* be safe when more hub events are proceed in parallel */ - mutex_lock(&bus->usb_address0_mutex); + mutex_lock(&bus->devnum_next_mutex); if (udev->wusb) { devnum = udev->portnum + 1; BUG_ON(test_bit(devnum, bus->devmap.devicemap)); @@ -2009,7 +2009,7 @@ static void choose_devnum(struct usb_device *udev) set_bit(devnum, bus->devmap.devicemap); udev->devnum = devnum; } - mutex_unlock(&bus->usb_address0_mutex); + mutex_unlock(&bus->devnum_next_mutex); } static void release_devnum(struct usb_device *udev) @@ -4212,7 +4212,7 @@ static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev) struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent); int connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN; - if (!udev->usb2_hw_lpm_capable) + if (!udev->usb2_hw_lpm_capable || !udev->bos) return; if (hub) @@ -4275,7 +4275,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, if (oldspeed == USB_SPEED_LOW) delay = HUB_LONG_RESET_TIME; - mutex_lock(&hdev->bus->usb_address0_mutex); + mutex_lock(hcd->address0_mutex); /* Reset the device; full speed may morph to high speed */ /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ @@ -4561,7 +4561,7 @@ fail: hub_port_disable(hub, port1, 0); update_devnum(udev, devnum); /* for disconnect processing */ } - mutex_unlock(&hdev->bus->usb_address0_mutex); + mutex_unlock(hcd->address0_mutex); return retval; } diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 24f9f98968a5..96b21b0dac1e 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -170,6 +170,14 @@ static const struct usb_device_id usb_quirk_list[] = { /* M-Systems Flash Disk Pioneers */ { USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Baum Vario Ultra */ + { USB_DEVICE(0x0904, 0x6101), .driver_info = + USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL }, + { USB_DEVICE(0x0904, 0x6102), .driver_info = + USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL }, + { USB_DEVICE(0x0904, 0x6103), .driver_info = + USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL }, + /* Keytouch QWERTY Panel keyboard */ { USB_DEVICE(0x0926, 0x3333), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index a80fb34cdce8..e394f123f918 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -764,6 +764,7 @@ static int dwc3_msm_ep_queue(struct usb_ep *ep, return 0; err: + list_del(&req_complete->list_item); spin_unlock_irqrestore(&dwc->lock, flags); kfree(req_complete); return ret; diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index c31aaf7a9880..e5d3a0bdf32a 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -160,7 +160,7 @@ static int usb_string_copy(const char *s, char **s_copy) if (!str) return -ENOMEM; } - strncpy(str, s, MAX_USB_STRING_WITH_NULL_LEN); + strlcpy(str, s, MAX_USB_STRING_WITH_NULL_LEN); if (str[ret - 1] == '\n') str[ret - 1] = '\0'; *s_copy = str; diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index cd096fb9078f..1ef3442cf618 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -77,9 +77,13 @@ struct acc_dev { struct usb_ep *ep_in; struct usb_ep *ep_out; - /* set to 1 when we connect */ + /* online indicates state of function_set_alt & function_unbind + * set to 1 when we connect + */ int online:1; - /* Set to 1 when we disconnect. + + /* disconnected indicates state of open & release + * Set to 1 when we disconnect. * Not cleared until our file is closed. */ int disconnected:1; @@ -307,7 +311,6 @@ static struct usb_request *req_get(struct acc_dev *dev, struct list_head *head) static void acc_set_disconnected(struct acc_dev *dev) { - dev->online = 0; dev->disconnected = 1; } @@ -721,9 +724,10 @@ static ssize_t acc_write(struct file *fp, const char __user *buf, req->zero = 0; } else { xfer = count; - /* If the data length is a multple of the + /* + * If the data length is a multple of the * maxpacket size then send a zero length packet(ZLP). - */ + */ req->zero = ((xfer % dev->ep_in->maxpacket) == 0); } if (copy_from_user(req->buf, buf, xfer)) { @@ -808,7 +812,10 @@ static int acc_release(struct inode *ip, struct file *fp) printk(KERN_INFO "acc_release\n"); WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0)); - _acc_dev->disconnected = 0; + /* indicate that we are disconnected + * still could be online so don't touch online flag + */ + _acc_dev->disconnected = 1; return 0; } @@ -868,11 +875,11 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev, unsigned long flags; /* - printk(KERN_INFO "acc_ctrlrequest " - "%02x.%02x v%04x i%04x l%u\n", - b_requestType, b_request, - w_value, w_index, w_length); -*/ + * printk(KERN_INFO "acc_ctrlrequest " + * "%02x.%02x v%04x i%04x l%u\n", + * b_requestType, b_request, + * w_value, w_index, w_length); + */ if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) { if (b_request == ACCESSORY_START) { @@ -1069,6 +1076,10 @@ acc_function_unbind(struct usb_configuration *c, struct usb_function *f) struct usb_request *req; int i; + dev->online = 0; /* clear online flag */ + wake_up(&dev->read_wq); /* unblock reads on closure */ + wake_up(&dev->write_wq); /* likewise for writes */ + while ((req = req_get(dev, &dev->tx_idle))) acc_request_free(req, dev->ep_in); for (i = 0; i < RX_REQ_MAX; i++) @@ -1200,6 +1211,7 @@ static int acc_function_set_alt(struct usb_function *f, } dev->online = 1; + dev->disconnected = 0; /* if online then not disconnected */ /* readers may be blocked waiting for us to go online */ wake_up(&dev->read_wq); @@ -1212,7 +1224,8 @@ static void acc_function_disable(struct usb_function *f) struct usb_composite_dev *cdev = dev->cdev; DBG(cdev, "acc_function_disable\n"); - acc_set_disconnected(dev); + acc_set_disconnected(dev); /* this now only sets disconnected */ + dev->online = 0; /* so now need to clear online flag here too */ usb_ep_disable(dev->ep_in); usb_ep_disable(dev->ep_out); diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 651e4afe0520..1bcfe819fad3 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -535,13 +535,15 @@ static int acm_notify_serial_state(struct f_acm *acm) { struct usb_composite_dev *cdev = acm->port.func.config->cdev; int status; + __le16 serial_state; spin_lock(&acm->lock); if (acm->notify_req) { dev_dbg(&cdev->gadget->dev, "acm ttyGS%d serial state %04x\n", acm->port_num, acm->serial_state); + serial_state = cpu_to_le16(acm->serial_state); status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE, - 0, &acm->serial_state, sizeof(acm->serial_state)); + 0, &serial_state, sizeof(acm->serial_state)); } else { acm->pending = true; status = 0; diff --git a/drivers/usb/gadget/function/f_ccid.h b/drivers/usb/gadget/function/f_ccid.h index 42a7ebbbccfc..935308cff0bc 100644 --- a/drivers/usb/gadget/function/f_ccid.h +++ b/drivers/usb/gadget/function/f_ccid.h @@ -55,29 +55,29 @@ #define CCID_READ_DTR _IOR('C', 3, int) struct usb_ccid_notification { - unsigned char buf[4]; + __u8 buf[4]; } __packed; struct ccid_bulk_in_header { - unsigned char bMessageType; - unsigned long wLength; - unsigned char bSlot; - unsigned char bSeq; - unsigned char bStatus; - unsigned char bError; - unsigned char bSpecific; - unsigned char abData[ABDATA_SIZE]; - unsigned char bSizeToSend; + __u8 bMessageType; + __u32 wLength; + __u8 bSlot; + __u8 bSeq; + __u8 bStatus; + __u8 bError; + __u8 bSpecific; + __u8 abData[ABDATA_SIZE]; + __u8 bSizeToSend; } __packed; struct ccid_bulk_out_header { - unsigned char bMessageType; - unsigned long wLength; - unsigned char bSlot; - unsigned char bSeq; - unsigned char bSpecific_0; - unsigned char bSpecific_1; - unsigned char bSpecific_2; - unsigned char APDU[ABDATA_SIZE]; + __u8 bMessageType; + __u32 wLength; + __u8 bSlot; + __u8 bSeq; + __u8 bSpecific_0; + __u8 bSpecific_1; + __u8 bSpecific_2; + __u8 APDU[ABDATA_SIZE]; } __packed; #endif diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index a1c00525a598..7c35241a487a 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -71,6 +71,7 @@ __ffs_data_got_strings(struct ffs_data *ffs, char *data, size_t len); /* The function structure ***************************************************/ struct ffs_ep; +static bool first_read_done; struct ffs_function { struct usb_configuration *conf; @@ -756,6 +757,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) { struct ffs_epfile *epfile = file->private_data; struct ffs_ep *ep; + struct ffs_data *ffs = epfile->ffs; char *data = NULL; ssize_t ret, data_len = -EINVAL; int halt; @@ -764,6 +766,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) atomic_read(&epfile->error), io_data->read ? "READ" : "WRITE"); smp_mb__before_atomic(); +retry: if (atomic_read(&epfile->error)) return -ENODEV; @@ -968,10 +971,36 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) * disabled (disconnect) or changed * (composition switch) ? */ - if (epfile->ep == ep) + if (epfile->ep == ep) { ret = ep->status; - else + if (ret >= 0) + first_read_done = true; + } else { ret = -ENODEV; + } + + /* do wait again if func eps are not enabled */ + if (io_data->read && !first_read_done + && ret < 0) { + unsigned short count = ffs->eps_count; + + pr_debug("%s: waiting for the online state\n", + __func__); + ret = 0; + kfree(data); + data = NULL; + data_len = -EINVAL; + spin_unlock_irq(&epfile->ffs->eps_lock); + mutex_unlock(&epfile->mutex); + epfile = ffs->epfiles; + do { + atomic_set(&epfile->error, 0); + ++epfile; + } while (--count); + epfile = file->private_data; + goto retry; + } + spin_unlock_irq(&epfile->ffs->eps_lock); if (io_data->read && ret > 0) { @@ -1038,6 +1067,7 @@ ffs_epfile_open(struct inode *inode, struct file *file) smp_mb__before_atomic(); atomic_set(&epfile->error, 0); + first_read_done = false; ffs_log("exit:state %d setup_state %d flag %lu", epfile->ffs->state, epfile->ffs->setup_state, epfile->ffs->flags); @@ -1159,7 +1189,7 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to) *to = p->data; } - ffs_log("enter"); + ffs_log("exit"); return res; } @@ -3354,6 +3384,8 @@ static int ffs_func_set_alt(struct usb_function *f, if (ffs->func) { ffs_func_eps_disable(ffs->func); ffs->func = NULL; + /* matching put to allow LPM on disconnect */ + usb_gadget_autopm_put_async(ffs->gadget); } if (ffs->state == FFS_DEACTIVATED) { @@ -3387,14 +3419,9 @@ static int ffs_func_set_alt(struct usb_function *f, static void ffs_func_disable(struct usb_function *f) { - struct ffs_function *func = ffs_func_from_usb(f); - struct ffs_data *ffs = func->ffs; - ffs_log("enter"); ffs_func_set_alt(f, 0, (unsigned)-1); - /* matching put to allow LPM on disconnect */ - usb_gadget_autopm_put_async(ffs->gadget); ffs_log("exit"); } diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 29b41b5dee04..c7689d05356c 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -625,7 +625,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; uvc_ss_streaming_comp.wBytesPerInterval = cpu_to_le16(max_packet_size * max_packet_mult * - opts->streaming_maxburst); + (opts->streaming_maxburst + 1)); /* Allocate endpoints. */ ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep); diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index 4e38683c653c..6d4e75785710 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -346,6 +346,9 @@ static int idmouse_probe(struct usb_interface *interface, if (iface_desc->desc.bInterfaceClass != 0x0A) return -ENODEV; + if (iface_desc->desc.bNumEndpoints < 1) + return -ENODEV; + /* allocate memory for our device state and initialize it */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c index 86b4e4b2ab9a..383fa007348f 100644 --- a/drivers/usb/misc/lvstest.c +++ b/drivers/usb/misc/lvstest.c @@ -370,6 +370,10 @@ static int lvs_rh_probe(struct usb_interface *intf, hdev = interface_to_usbdev(intf); desc = intf->cur_altsetting; + + if (desc->desc.bNumEndpoints < 1) + return -ENODEV; + endpoint = &desc->endpoint[0].desc; /* valid only for SS root hub */ diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index bbd029c9c725..442b6631162e 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -711,6 +711,11 @@ static int uss720_probe(struct usb_interface *intf, interface = intf->cur_altsetting; + if (interface->desc.bNumEndpoints < 3) { + usb_put_dev(usbdev); + return -ENODEV; + } + /* * Allocate parport interface */ diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index e499b862a946..88f26ac2a185 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -250,8 +250,27 @@ static void cppi41_dma_callback(void *private_data) transferred < cppi41_channel->packet_sz) cppi41_channel->prog_len = 0; - if (cppi41_channel->is_tx) - empty = musb_is_tx_fifo_empty(hw_ep); + if (cppi41_channel->is_tx) { + u8 type; + + if (is_host_active(musb)) + type = hw_ep->out_qh->type; + else + type = hw_ep->ep_in.type; + + if (type == USB_ENDPOINT_XFER_ISOC) + /* + * Don't use the early-TX-interrupt workaround below + * for Isoch transfter. Since Isoch are periodic + * transfer, by the time the next transfer is + * scheduled, the current one should be done already. + * + * This avoids audio playback underrun issue. + */ + empty = true; + else + empty = musb_is_tx_fifo_empty(hw_ep); + } if (!cppi41_channel->is_tx || empty) { cppi41_trans_done(cppi41_channel); diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c index aa11cf2f7417..6ba742076baa 100644 --- a/drivers/usb/phy/phy-msm-ssusb-qmp.c +++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c @@ -222,11 +222,6 @@ static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on) "enable phy->fpc_redrive_ldo failed\n"); return rc; } - - dev_dbg(phy->phy.dev, - "fpc redrive ldo: min_vol:%duV max_vol:%duV\n", - phy->redrive_voltage_levels[VOLTAGE_LEVEL_MIN], - phy->redrive_voltage_levels[VOLTAGE_LEVEL_MAX]); } rc = msm_ldo_enable(phy, phy->vdd, phy->vdd_levels, @@ -236,11 +231,6 @@ static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on) goto disable_fpc_redrive; } - dev_dbg(phy->phy.dev, - "vdd ldo: min_vol:%duV max_vol:%duV\n", - phy->vdd_levels[VOLTAGE_LEVEL_MIN], - phy->vdd_levels[VOLTAGE_LEVEL_MAX]); - rc = msm_ldo_enable(phy, phy->core_ldo, phy->core_voltage_levels, USB_SSPHY_HPM_LOAD); if (rc < 0) { @@ -248,11 +238,6 @@ static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on) goto disable_vdd; } - dev_dbg(phy->phy.dev, - "core ldo: min_vol:%duV max_vol:%duV\n", - phy->core_voltage_levels[VOLTAGE_LEVEL_MIN], - phy->core_voltage_levels[VOLTAGE_LEVEL_MAX]); - return 0; disable_regulators: diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 42cc72e54c05..af67a0de6b5d 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -233,6 +233,14 @@ static void option_instat_callback(struct urb *urb); #define BANDRICH_PRODUCT_1012 0x1012 #define QUALCOMM_VENDOR_ID 0x05C6 +/* These Quectel products use Qualcomm's vendor ID */ +#define QUECTEL_PRODUCT_UC20 0x9003 +#define QUECTEL_PRODUCT_UC15 0x9090 + +#define QUECTEL_VENDOR_ID 0x2c7c +/* These Quectel products use Quectel's vendor ID */ +#define QUECTEL_PRODUCT_EC21 0x0121 +#define QUECTEL_PRODUCT_EC25 0x0125 #define CMOTECH_VENDOR_ID 0x16d8 #define CMOTECH_PRODUCT_6001 0x6001 @@ -1161,7 +1169,14 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000)}, /* SIMCom SIM5218 */ - { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9003), /* Quectel UC20 */ + /* Quectel products using Qualcomm vendor ID */ + { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC15)}, + { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC20), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + /* Quectel products using Quectel vendor ID */ + { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC21), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC25), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) }, diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 696458db7e3c..38b3f0d8cd58 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -169,6 +169,8 @@ static const struct usb_device_id id_table[] = { {DEVICE_SWI(0x413c, 0x81a9)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */ {DEVICE_SWI(0x413c, 0x81b1)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */ {DEVICE_SWI(0x413c, 0x81b3)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */ + {DEVICE_SWI(0x413c, 0x81b5)}, /* Dell Wireless 5811e QDL */ + {DEVICE_SWI(0x413c, 0x81b6)}, /* Dell Wireless 5811e QDL */ /* Huawei devices */ {DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */ diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c index 252c7bd9218a..d01496fd27fe 100644 --- a/drivers/usb/wusbcore/wa-hc.c +++ b/drivers/usb/wusbcore/wa-hc.c @@ -39,6 +39,9 @@ int wa_create(struct wahc *wa, struct usb_interface *iface, int result; struct device *dev = &iface->dev; + if (iface->cur_altsetting->desc.bNumEndpoints < 3) + return -ENODEV; + result = wa_rpipes_create(wa); if (result < 0) goto error_rpipes_create; diff --git a/drivers/uwb/hwa-rc.c b/drivers/uwb/hwa-rc.c index 0257f35cfb9d..e75bbe5a10cd 100644 --- a/drivers/uwb/hwa-rc.c +++ b/drivers/uwb/hwa-rc.c @@ -825,6 +825,9 @@ static int hwarc_probe(struct usb_interface *iface, struct hwarc *hwarc; struct device *dev = &iface->dev; + if (iface->cur_altsetting->desc.bNumEndpoints < 1) + return -ENODEV; + result = -ENOMEM; uwb_rc = uwb_rc_alloc(); if (uwb_rc == NULL) { diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/uwb/i1480/dfu/usb.c index 2bfc846ac071..6345e85822a4 100644 --- a/drivers/uwb/i1480/dfu/usb.c +++ b/drivers/uwb/i1480/dfu/usb.c @@ -362,6 +362,9 @@ int i1480_usb_probe(struct usb_interface *iface, const struct usb_device_id *id) result); } + if (iface->cur_altsetting->desc.bNumEndpoints < 1) + return -ENODEV; + result = -ENOMEM; i1480_usb = kzalloc(sizeof(*i1480_usb), GFP_KERNEL); if (i1480_usb == NULL) { diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c index 0582b72ef377..1a9f18b40be6 100644 --- a/drivers/vfio/vfio_iommu_spapr_tce.c +++ b/drivers/vfio/vfio_iommu_spapr_tce.c @@ -511,6 +511,12 @@ static long tce_iommu_build_v2(struct tce_container *container, unsigned long hpa; enum dma_data_direction dirtmp; + if (!tbl->it_userspace) { + ret = tce_iommu_userspace_view_alloc(tbl); + if (ret) + return ret; + } + for (i = 0; i < pages; ++i) { struct mm_iommu_table_group_mem_t *mem = NULL; unsigned long *pua = IOMMU_TABLE_USERSPACE_ENTRY(tbl, @@ -584,15 +590,6 @@ static long tce_iommu_create_table(struct tce_container *container, WARN_ON(!ret && !(*ptbl)->it_ops->free); WARN_ON(!ret && ((*ptbl)->it_allocated_size != table_size)); - if (!ret && container->v2) { - ret = tce_iommu_userspace_view_alloc(*ptbl); - if (ret) - (*ptbl)->it_ops->free(*ptbl); - } - - if (ret) - decrement_locked_vm(table_size >> PAGE_SHIFT); - return ret; } @@ -1064,10 +1061,7 @@ static int tce_iommu_take_ownership(struct tce_container *container, if (!tbl || !tbl->it_map) continue; - rc = tce_iommu_userspace_view_alloc(tbl); - if (!rc) - rc = iommu_take_ownership(tbl); - + rc = iommu_take_ownership(tbl); if (rc) { for (j = 0; j < i; ++j) iommu_release_ownership( diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 6e92917ba77a..4e3c78d88832 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -1168,6 +1168,8 @@ static void fbcon_free_font(struct display *p, bool freefont) p->userfont = 0; } +static void set_vc_hi_font(struct vc_data *vc, bool set); + static void fbcon_deinit(struct vc_data *vc) { struct display *p = &fb_display[vc->vc_num]; @@ -1203,6 +1205,9 @@ finished: if (free_font) vc->vc_font.data = NULL; + if (vc->vc_hi_font_mask) + set_vc_hi_font(vc, false); + if (!con_is_bound(&fb_con)) fbcon_exit(); @@ -2439,32 +2444,10 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) return 0; } -static int fbcon_do_set_font(struct vc_data *vc, int w, int h, - const u8 * data, int userfont) +/* set/clear vc_hi_font_mask and update vc attrs accordingly */ +static void set_vc_hi_font(struct vc_data *vc, bool set) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; - struct fbcon_ops *ops = info->fbcon_par; - struct display *p = &fb_display[vc->vc_num]; - int resize; - int cnt; - char *old_data = NULL; - - if (CON_IS_VISIBLE(vc) && softback_lines) - fbcon_set_origin(vc); - - resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); - if (p->userfont) - old_data = vc->vc_font.data; - if (userfont) - cnt = FNTCHARCNT(data); - else - cnt = 256; - vc->vc_font.data = (void *)(p->fontdata = data); - if ((p->userfont = userfont)) - REFCOUNT(data)++; - vc->vc_font.width = w; - vc->vc_font.height = h; - if (vc->vc_hi_font_mask && cnt == 256) { + if (!set) { vc->vc_hi_font_mask = 0; if (vc->vc_can_do_color) { vc->vc_complement_mask >>= 1; @@ -2487,7 +2470,7 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, ((c & 0xfe00) >> 1) | (c & 0xff); vc->vc_attr >>= 1; } - } else if (!vc->vc_hi_font_mask && cnt == 512) { + } else { vc->vc_hi_font_mask = 0x100; if (vc->vc_can_do_color) { vc->vc_complement_mask <<= 1; @@ -2519,8 +2502,38 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, } else vc->vc_video_erase_char = c & ~0x100; } - } +} + +static int fbcon_do_set_font(struct vc_data *vc, int w, int h, + const u8 * data, int userfont) +{ + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fbcon_ops *ops = info->fbcon_par; + struct display *p = &fb_display[vc->vc_num]; + int resize; + int cnt; + char *old_data = NULL; + + if (CON_IS_VISIBLE(vc) && softback_lines) + fbcon_set_origin(vc); + + resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); + if (p->userfont) + old_data = vc->vc_font.data; + if (userfont) + cnt = FNTCHARCNT(data); + else + cnt = 256; + vc->vc_font.data = (void *)(p->fontdata = data); + if ((p->userfont = userfont)) + REFCOUNT(data)++; + vc->vc_font.width = w; + vc->vc_font.height = h; + if (vc->vc_hi_font_mask && cnt == 256) + set_vc_hi_font(vc, false); + else if (!vc->vc_hi_font_mask && cnt == 512) + set_vc_hi_font(vc, true); if (resize) { int cols, rows; diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c index 17644e3556b6..e9ba77501b38 100644 --- a/drivers/video/fbdev/msm/mdss_compat_utils.c +++ b/drivers/video/fbdev/msm/mdss_compat_utils.c @@ -226,6 +226,7 @@ static struct mdp_input_layer *__create_layer_list( layer->transp_mask = layer32->transp_mask; layer->bg_color = layer32->bg_color; layer->blend_op = layer32->blend_op; + layer->alpha = layer32->alpha; layer->color_space = layer32->color_space; layer->src_rect = layer32->src_rect; layer->dst_rect = layer32->dst_rect; diff --git a/drivers/video/fbdev/msm/mdss_dba_utils.c b/drivers/video/fbdev/msm/mdss_dba_utils.c index 0808a1a6e14b..3330f8f62b78 100644 --- a/drivers/video/fbdev/msm/mdss_dba_utils.c +++ b/drivers/video/fbdev/msm/mdss_dba_utils.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -46,6 +46,7 @@ struct mdss_dba_utils_data { struct cec_cbs ccbs; char disp_switch_name[MAX_SWITCH_NAME_SIZE]; u32 current_vic; + bool support_audio; }; static struct mdss_dba_utils_data *mdss_dba_utils_get_data( @@ -84,7 +85,7 @@ end: return udata; } -static void mdss_dba_utils_send_display_notification( +static void mdss_dba_utils_notify_display( struct mdss_dba_utils_data *udata, int val) { int state = 0; @@ -109,7 +110,7 @@ static void mdss_dba_utils_send_display_notification( udata->sdev_display.state); } -static void mdss_dba_utils_send_audio_notification( +static void mdss_dba_utils_notify_audio( struct mdss_dba_utils_data *udata, int val) { int state = 0; @@ -291,6 +292,31 @@ static void mdss_dba_utils_sysfs_remove(struct kobject *kobj) sysfs_remove_group(kobj, &mdss_dba_utils_fs_attrs_group); } +static bool mdss_dba_check_audio_support(struct mdss_dba_utils_data *udata) +{ + bool dvi_mode = false; + int audio_blk_size = 0; + struct msm_ext_disp_audio_edid_blk audio_blk; + + if (!udata) { + pr_debug("%s: Invalid input\n", __func__); + return false; + } + memset(&audio_blk, 0, sizeof(audio_blk)); + + /* check if sink is in DVI mode */ + dvi_mode = hdmi_edid_is_dvi_mode(udata->edid_data); + + /* get the audio block size info from EDID */ + hdmi_edid_get_audio_blk(udata->edid_data, &audio_blk); + audio_blk_size = audio_blk.audio_data_blk_size; + + if (dvi_mode || !audio_blk_size) + return false; + else + return true; +} + static void mdss_dba_utils_dba_cb(void *data, enum msm_dba_callback_event event) { int ret = -EINVAL; @@ -322,19 +348,26 @@ static void mdss_dba_utils_dba_cb(void *data, enum msm_dba_callback_event event) if (!ret) { hdmi_edid_parser(udata->edid_data); - hdmi_edid_get_audio_blk(udata->edid_data, &blk); - if (udata->ops.set_audio_block) - udata->ops.set_audio_block( + /* check whether audio is supported or not */ + udata->support_audio = + mdss_dba_check_audio_support(udata); + if (udata->support_audio) { + hdmi_edid_get_audio_blk( + udata->edid_data, &blk); + if (udata->ops.set_audio_block) + udata->ops.set_audio_block( udata->dba_data, sizeof(blk), &blk); + } } else { pr_err("failed to get edid%d\n", ret); } } if (pluggable) { - mdss_dba_utils_send_display_notification(udata, 1); - mdss_dba_utils_send_audio_notification(udata, 1); + mdss_dba_utils_notify_display(udata, 1); + if (udata->support_audio) + mdss_dba_utils_notify_audio(udata, 1); } else { mdss_dba_utils_video_on(udata, udata->pinfo); } @@ -346,8 +379,9 @@ static void mdss_dba_utils_dba_cb(void *data, enum msm_dba_callback_event event) if (!udata->hpd_state) break; if (pluggable) { - mdss_dba_utils_send_audio_notification(udata, 0); - mdss_dba_utils_send_display_notification(udata, 0); + if (udata->support_audio) + mdss_dba_utils_notify_audio(udata, 0); + mdss_dba_utils_notify_display(udata, 0); } else { mdss_dba_utils_video_off(udata); } diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c index e60869144339..ddb7a4c31f68 100644 --- a/drivers/video/fbdev/msm/mdss_debug.c +++ b/drivers/video/fbdev/msm/mdss_debug.c @@ -44,6 +44,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}; @@ -59,11 +95,13 @@ static int panel_debug_base_open(struct inode *inode, struct file *file) static int panel_debug_base_release(struct inode *inode, struct file *file) { struct mdss_debug_base *dbg = file->private_data; + mutex_lock(&mdss_debug_lock); if (dbg && dbg->buf) { kfree(dbg->buf); dbg->buf_len = 0; dbg->buf = NULL; } + mutex_unlock(&mdss_debug_lock); return 0; } @@ -385,11 +423,13 @@ static int mdss_debug_base_open(struct inode *inode, struct file *file) static int mdss_debug_base_release(struct inode *inode, struct file *file) { struct mdss_debug_base *dbg = file->private_data; + mutex_lock(&mdss_debug_lock); if (dbg && dbg->buf) { kfree(dbg->buf); dbg->buf_len = 0; dbg->buf = NULL; } + mutex_unlock(&mdss_debug_lock); return 0; } @@ -1788,6 +1828,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 1a44ab5c44a2..d778fb35ed2d 100644 --- a/drivers/video/fbdev/msm/mdss_debug.h +++ b/drivers/video/fbdev/msm/mdss_debug.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -170,6 +170,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 0a45ce036cf6..bf4117650e3c 100644 --- a/drivers/video/fbdev/msm/mdss_debug_xlog.c +++ b/drivers/video/fbdev/msm/mdss_debug_xlog.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This 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 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) @@ -579,7 +583,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; @@ -603,6 +607,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); } @@ -614,7 +622,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, ...) @@ -622,6 +631,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; @@ -657,6 +667,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; } @@ -667,10 +680,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); } } @@ -754,6 +768,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_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index 226198efbeec..1e878e9a00cb 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -69,6 +69,11 @@ static int mdss_dp_process_phy_test_pattern_request( static int mdss_dp_send_audio_notification( struct mdss_dp_drv_pdata *dp, int val); +static inline void mdss_dp_reset_sink_count(struct mdss_dp_drv_pdata *dp) +{ + memset(&dp->sink_count, 0, sizeof(dp->sink_count)); +} + static inline void mdss_dp_reset_test_data(struct mdss_dp_drv_pdata *dp) { dp->test_data = (const struct dpcd_test_request){ 0 }; @@ -133,22 +138,77 @@ static int mdss_dp_is_clk_prefix(const char *clk_prefix, const char *clk_name) return !strncmp(clk_name, clk_prefix, strlen(clk_prefix)); } +static void mdss_dp_reset_phy_config_indices(struct mdss_dp_drv_pdata *dp) +{ + int i = 0; + + for (i = 0; i < PHY_AUX_CFG_MAX; i++) + dp->aux_cfg[i].current_index = 0; +} + +static void mdss_dp_phy_aux_cfg_reset(struct mdss_dp_drv_pdata *dp) +{ + int i = 0; + + for (i = 0; i < PHY_AUX_CFG_MAX; i++) + dp->aux_cfg[i] = (const struct mdss_dp_phy_cfg){ 0 }; +} + +static int mdss_dp_parse_aux_cfg(struct platform_device *pdev, + struct mdss_dp_drv_pdata *dp) +{ + int len = 0, i = 0, j = 0, config_count = 0; + const char *data; + int const minimum_config_count = 1; + + for (i = 0; i < PHY_AUX_CFG_MAX; i++) { + const char *property = mdss_dp_get_phy_aux_config_property(i); + + data = of_get_property(pdev->dev.of_node, property, &len); + if (!data) { + pr_err("Unable to read %s\n", property); + goto error; + } + + config_count = len - 1; + if ((config_count < minimum_config_count) || + (config_count > MDSS_DP_MAX_PHY_CFG_VALUE_CNT)) { + pr_err("Invalid config count (%d) configs for %s\n", + config_count, property); + goto error; + } + + dp->aux_cfg[i].offset = data[0]; + dp->aux_cfg[i].cfg_cnt = config_count; + pr_debug("%s offset=0x%x, cfg_cnt=%d\n", + property, + dp->aux_cfg[i].offset, + dp->aux_cfg[i].cfg_cnt); + for (j = 1; j < len; j++) { + dp->aux_cfg[i].lut[j - 1] = data[j]; + pr_debug("%s lut[%d]=0x%x\n", + property, + i, + dp->aux_cfg[i].lut[j - 1]); + } + } + + return 0; + +error: + mdss_dp_phy_aux_cfg_reset(dp); + return -EINVAL; +} + static int mdss_dp_parse_prop(struct platform_device *pdev, struct mdss_dp_drv_pdata *dp_drv) { int len = 0, i = 0, rc = 0; const char *data; - data = of_get_property(pdev->dev.of_node, - "qcom,aux-cfg-settings", &len); - if ((!data) || (len != AUX_CFG_LEN)) { - pr_err("%s:%d, Unable to read DP AUX CFG settings", - __func__, __LINE__); - return -EINVAL; - } - - for (i = 0; i < len; i++) - dp_drv->aux_cfg[i] = data[i]; + rc = mdss_dp_parse_aux_cfg(pdev, dp_drv); + if (rc) + return rc; data = of_get_property(pdev->dev.of_node, "qcom,logical2physical-lane-map", &len); @@ -958,6 +1018,12 @@ void mdss_dp_config_ctrl(struct mdss_dp_drv_pdata *dp) mdss_dp_configuration_ctrl(&dp->ctrl_io, data); } +static inline void mdss_dp_ack_state(struct mdss_dp_drv_pdata *dp, int val) +{ + if (dp && dp->ext_audio_data.intf_ops.notify) + dp->ext_audio_data.intf_ops.notify(dp->ext_pdev, val); +} + static int mdss_dp_wait4video_ready(struct mdss_dp_drv_pdata *dp_drv) { int ret = 0; @@ -989,26 +1055,28 @@ static int mdss_dp_wait4video_ready(struct mdss_dp_drv_pdata *dp_drv) static void mdss_dp_update_cable_status(struct mdss_dp_drv_pdata *dp, bool connected) { - mutex_lock(&dp->pd_msg_mutex); + mutex_lock(&dp->attention_lock); pr_debug("cable_connected to %d\n", connected); if (dp->cable_connected != connected) dp->cable_connected = connected; else pr_debug("no change in cable status\n"); - mutex_unlock(&dp->pd_msg_mutex); + mutex_unlock(&dp->attention_lock); } static int dp_get_cable_status(struct platform_device *pdev, u32 vote) { - struct mdss_dp_drv_pdata *dp_ctrl = platform_get_drvdata(pdev); + struct mdss_dp_drv_pdata *dp = platform_get_drvdata(pdev); u32 hpd; - if (!dp_ctrl) { + if (!dp) { DEV_ERR("%s: invalid input\n", __func__); return -ENODEV; } - hpd = dp_ctrl->cable_connected; + mutex_lock(&dp->attention_lock); + hpd = dp->cable_connected; + mutex_unlock(&dp->attention_lock); return hpd; } @@ -1230,12 +1298,6 @@ static int dp_init_panel_info(struct mdss_dp_drv_pdata *dp_drv, u32 vic) return 0; } /* dp_init_panel_info */ -static inline void mdss_dp_ack_state(struct mdss_dp_drv_pdata *dp, int val) -{ - if (dp && dp->ext_audio_data.intf_ops.notify) - dp->ext_audio_data.intf_ops.notify(dp->ext_pdev, val); -} - /** * mdss_dp_get_lane_mapping() - returns lane mapping based on given orientation * @orientation: usb plug orientation @@ -1434,6 +1496,13 @@ static int mdss_dp_setup_main_link(struct mdss_dp_drv_pdata *dp, bool train) if (ret) goto end; + /* + * Skip the transfer unit setup and the routine to wait for the + * video ready interrupt as link training tests do not require + * video frames to be sent. + */ + if (mdss_dp_is_link_training_requested(dp)) + goto end; send_video: /* * Set up transfer unit values and set controller state to send @@ -1614,15 +1683,19 @@ int mdss_dp_on(struct mdss_panel_data *pdata) dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); - if (dp_drv->power_on) { - /* - * Acknowledge the connection event if link training has already - * been done. This will unblock the external display thread and - * allow the driver to progress. For example, in the case of - * video test pattern requests, to send the test response and - * start transmitting the test pattern. - */ - mdss_dp_ack_state(dp_drv, true); + /* + * If the link already active, then nothing needs to be done here. + * However, it is possible that the the power_on flag could be + * set to true but we would still need to initialize the DP host. + * An example of this use-case is when a multiport dongle is connected + * and subsequently the downstream sink is disconnected. This would + * only go through the IRQ HPD path where we tear down the link but + * the power_on flag remains set to true. When the downstream sink + * is subsequently connected again, we need to re-initialize DP + * host + */ + if (dp_drv->power_on && + (dp_drv->new_vic && (dp_drv->new_vic == dp_drv->vic))) { pr_debug("Link already setup, return\n"); return 0; } @@ -1640,6 +1713,23 @@ int mdss_dp_on(struct mdss_panel_data *pdata) return mdss_dp_on_hpd(dp_drv); } +static bool mdss_dp_is_ds_bridge(struct mdss_dp_drv_pdata *dp) +{ + return dp->dpcd.downstream_port.dfp_present; +} + +static bool mdss_dp_is_ds_bridge_sink_count_zero(struct mdss_dp_drv_pdata *dp) +{ + return (mdss_dp_is_ds_bridge(dp) && + (dp->sink_count.count == 0)); +} + +static bool mdss_dp_is_ds_bridge_no_local_edid(struct mdss_dp_drv_pdata *dp) +{ + return (mdss_dp_is_ds_bridge_sink_count_zero(dp) && + !(dp->dpcd.flags & DPCD_PORT_0_EDID_PRESENTED)); +} + static int mdss_dp_off_irq(struct mdss_dp_drv_pdata *dp_drv) { if (!dp_drv->power_on) { @@ -1657,10 +1747,16 @@ static int mdss_dp_off_irq(struct mdss_dp_drv_pdata *dp_drv) /* Make sure DP mainlink and audio engines are disabled */ wmb(); - mdss_dp_ack_state(dp_drv, false); + /* + * If downstream device is a brige which no longer has any + * downstream devices connected to it, then we should reset + * the current panel info + */ + if (mdss_dp_is_ds_bridge_sink_count_zero(dp_drv)) + dp_init_panel_info(dp_drv, HDMI_VFRMT_UNKNOWN); + mutex_unlock(&dp_drv->train_mutex); - complete_all(&dp_drv->irq_comp); pr_debug("end\n"); return 0; @@ -1687,11 +1783,11 @@ static int mdss_dp_off_hpd(struct mdss_dp_drv_pdata *dp_drv) mdss_dp_host_deinit(dp_drv); dp_drv->power_on = false; - dp_drv->sink_info_read = false; dp_init_panel_info(dp_drv, HDMI_VFRMT_UNKNOWN); - mdss_dp_ack_state(dp_drv, false); mdss_dp_reset_test_data(dp_drv); + mdss_dp_reset_sink_count(dp_drv); + dp_drv->prev_sink_count = dp_drv->sink_count; mutex_unlock(&dp_drv->train_mutex); pr_debug("DP off done\n"); @@ -1730,8 +1826,9 @@ static int mdss_dp_send_audio_notification( if (mdss_dp_sink_audio_supp(dp) || dp->audio_test_req) { dp->audio_test_req = false; - pr_debug("sending audio notification\n"); flags |= MSM_EXT_DISP_HPD_AUDIO; + pr_debug("sending audio notification = %d, flags = %d\n", val, + flags); if (dp->ext_audio_data.intf_ops.hpd) ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev, @@ -1756,7 +1853,8 @@ static int mdss_dp_send_video_notification( goto end; } - flags |= MSM_EXT_DISP_HPD_VIDEO; + flags |= MSM_EXT_DISP_HPD_ASYNC_VIDEO; + pr_debug("sending video notification = %d, flags = %d\n", val, flags); if (dp->ext_audio_data.intf_ops.hpd) ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev, @@ -1813,6 +1911,8 @@ static int mdss_dp_edid_init(struct mdss_panel_data *pdata) dp_drv->edid_buf = edid_init_data.buf; dp_drv->edid_buf_size = edid_init_data.buf_size; + mdss_dp_set_default_resolution(dp_drv); + return 0; } @@ -1864,14 +1964,16 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata) mdss_dp_ctrl_reset(&dp_drv->ctrl_io); mdss_dp_phy_reset(&dp_drv->ctrl_io); mdss_dp_aux_reset(&dp_drv->ctrl_io); + mdss_dp_aux_set_limits(&dp_drv->ctrl_io); + mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true); pr_debug("Ctrl_hw_rev =0x%x, phy hw_rev =0x%x\n", mdss_dp_get_ctrl_hw_version(&dp_drv->ctrl_io), mdss_dp_get_phy_hw_version(&dp_drv->phy_io)); - mdss_dp_phy_aux_setup(&dp_drv->phy_io, dp_drv->aux_cfg, - dp_drv->phy_reg_offset); + mdss_dp_reset_phy_config_indices(dp_drv); + mdss_dp_phy_aux_setup(dp_drv); mdss_dp_irq_enable(dp_drv); dp_drv->dp_initialized = true; @@ -1939,10 +2041,11 @@ static int mdss_dp_host_deinit(struct mdss_dp_drv_pdata *dp) static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, enum notification_status status) { - const int irq_comp_timeout = HZ * 2; int ret = 0; + bool notify = false; + bool connect; - mutex_lock(&dp->pd_msg_mutex); + pr_debug("beginning notification\n"); if (status == dp->hpd_notification_status) { pr_debug("No change in status %s --> %s\n", mdss_dp_notification_status_to_string(status), @@ -1955,39 +2058,40 @@ static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, case NOTIFY_CONNECT_IRQ_HPD: if (dp->hpd_notification_status != NOTIFY_DISCONNECT_IRQ_HPD) goto invalid_request; - /* Follow the same programming as for NOTIFY_CONNECT */ - mdss_dp_host_init(&dp->panel_data); - mdss_dp_send_video_notification(dp, true); + notify = true; + connect = true; break; case NOTIFY_CONNECT: - if ((dp->hpd_notification_status == NOTIFY_CONNECT_IRQ_HPD) || - (dp->hpd_notification_status == - NOTIFY_DISCONNECT_IRQ_HPD)) + if (dp->hpd_notification_status == NOTIFY_CONNECT_IRQ_HPD) goto invalid_request; - mdss_dp_host_init(&dp->panel_data); - mdss_dp_send_video_notification(dp, true); + notify = true; + connect = true; break; case NOTIFY_DISCONNECT: - mdss_dp_send_audio_notification(dp, false); - mdss_dp_send_video_notification(dp, false); + /* + * Userspace triggers a disconnect event on boot up, this must + * not be processed as there was no previously connected sink + * device. + */ + if (dp->hpd_notification_status == NOTIFY_UNKNOWN) + goto invalid_request; + if (dp->hpd_notification_status == NOTIFY_DISCONNECT_IRQ_HPD) { + /* + * user modules already turned off. Need to explicitly + * turn off DP core here. + */ + mdss_dp_off_hpd(dp); + } else { + notify = true; + connect = false; + } break; case NOTIFY_DISCONNECT_IRQ_HPD: if (dp->hpd_notification_status == NOTIFY_DISCONNECT) goto invalid_request; - mdss_dp_send_audio_notification(dp, false); - mdss_dp_send_video_notification(dp, false); - if (!IS_ERR_VALUE(ret) && ret) { - reinit_completion(&dp->irq_comp); - ret = wait_for_completion_timeout(&dp->irq_comp, - irq_comp_timeout); - if (ret <= 0) { - pr_warn("irq_comp timed out\n"); - ret = -EINVAL; - } else { - ret = 0; - } - } + notify = true; + connect = false; break; default: pr_err("Invalid notification status = %d\n", status); @@ -1995,7 +2099,7 @@ static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, break; } - goto end; + goto notify; invalid_request: pr_err("Invalid request %s --> %s\n", @@ -2004,16 +2108,34 @@ invalid_request: mdss_dp_notification_status_to_string(status)); ret = -EINVAL; -end: +notify: + if (ret || !notify) { + pr_debug("not sending notification\n"); + goto end; + } + + atomic_set(&dp->notification_pending, 1); + if (connect) { + mdss_dp_host_init(&dp->panel_data); + ret = mdss_dp_send_video_notification(dp, true); + } else { + mdss_dp_send_audio_notification(dp, false); + ret = mdss_dp_send_video_notification(dp, false); + } + if (!ret) { pr_debug("Successfully sent notification %s --> %s\n", mdss_dp_notification_status_to_string( dp->hpd_notification_status), mdss_dp_notification_status_to_string(status)); - dp->hpd_notification_status = status; + } else { + pr_err("%s Notification failed\n", + mdss_dp_notification_status_to_string(status)); + atomic_set(&dp->notification_pending, 0); } - mutex_unlock(&dp->pd_msg_mutex); +end: + dp->hpd_notification_status = status; return ret; } @@ -2022,9 +2144,6 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp) int ret; u32 max_pclk_khz; - if (dp->sink_info_read) - return 0; - pr_debug("start\n"); ret = mdss_dp_dpcd_cap_read(dp); @@ -2037,8 +2156,25 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp) */ pr_err("dpcd read failed, set failsafe parameters\n"); mdss_dp_set_default_link_parameters(dp); + goto read_edid; } + /* + * When connected to a multiport adaptor which does not have a + * local EDID present, do not attempt to read the EDID. + * When connected to a multiport adaptor with no downstream device + * connected to it, do not attempt to read the EDID. It is possible + * that the adaptor may advertise the presence of local EDID, but it + * is not guaranteed to work. + */ + if (mdss_dp_is_ds_bridge_sink_count_zero(dp)) { + if (mdss_dp_is_ds_bridge_no_local_edid(dp)) + pr_debug("No local EDID present on DS branch device\n"); + pr_info("no downstream devices, skip client notification\n"); + goto end; + } + +read_edid: ret = mdss_dp_edid_read(dp); if (ret) { pr_err("edid read error, setting default resolution\n"); @@ -2049,14 +2185,18 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp) hdmi_edid_set_max_pclk_rate(dp->panel_data.panel_info.edid_data, min(dp->max_pclk_khz, max_pclk_khz)); + if (dp->dpcd_read_required) { + pr_debug("reading DPCD with updated AUX config\n"); + mdss_dp_dpcd_cap_read(dp); + dp->dpcd_read_required = false; + } + ret = hdmi_edid_parser(dp->panel_data.panel_info.edid_data); if (ret) { pr_err("edid parse failed, setting default resolution\n"); goto notify; } - dp->sink_info_read = true; - notify: if (ret) { /* set failsafe parameters */ @@ -2083,7 +2223,6 @@ notify: end: pr_debug("end\n"); return ret; - } static int mdss_dp_check_params(struct mdss_dp_drv_pdata *dp, void *arg) @@ -2284,12 +2423,16 @@ static ssize_t mdss_dp_rda_connected(struct device *dev, { ssize_t ret; struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev); + bool cable_connected; if (!dp) return -EINVAL; - ret = snprintf(buf, PAGE_SIZE, "%d\n", dp->cable_connected); - pr_debug("%d\n", dp->cable_connected); + mutex_lock(&dp->attention_lock); + cable_connected = dp->cable_connected; + mutex_unlock(&dp->attention_lock); + ret = snprintf(buf, PAGE_SIZE, "%d\n", cable_connected); + pr_debug("%d\n", cable_connected); return ret; } @@ -2458,6 +2601,7 @@ static ssize_t mdss_dp_wta_hpd(struct device *dev, int hpd, rc; ssize_t ret = strnlen(buf, PAGE_SIZE); struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev); + bool cable_connected; if (!dp) { pr_err("invalid data\n"); @@ -2473,9 +2617,13 @@ static ssize_t mdss_dp_wta_hpd(struct device *dev, } dp->hpd = !!hpd; - pr_debug("hpd=%d\n", dp->hpd); + mutex_lock(&dp->attention_lock); + cable_connected = dp->cable_connected; + mutex_unlock(&dp->attention_lock); + pr_debug("hpd=%d cable_connected=%s\n", dp->hpd, + cable_connected ? "true" : "false"); - if (dp->hpd && dp->cable_connected) { + if (dp->hpd && cable_connected) { if (dp->alt_mode.current_state & DP_CONFIGURE_DONE) { mdss_dp_host_init(&dp->panel_data); mdss_dp_process_hpd_high(dp); @@ -2805,8 +2953,6 @@ static void mdss_dp_mainlink_push_idle(struct mdss_panel_data *pdata) /* wait until link training is completed */ mutex_lock(&dp_drv->train_mutex); - mdss_dp_aux_set_sink_power_state(dp_drv, SINK_POWER_OFF); - reinit_completion(&dp_drv->idle_comp); mdss_dp_state_ctrl(&dp_drv->ctrl_io, ST_PUSH_IDLE); if (!wait_for_completion_timeout(&dp_drv->idle_comp, @@ -2896,28 +3042,33 @@ static int mdss_dp_event_handler(struct mdss_panel_data *pdata, switch (event) { case MDSS_EVENT_UNBLANK: - mdss_dp_ack_state(dp, true); rc = mdss_dp_on(pdata); break; case MDSS_EVENT_PANEL_ON: mdss_dp_update_hdcp_info(dp); if (dp_is_hdcp_enabled(dp)) { - cancel_delayed_work(&dp->hdcp_cb_work); + cancel_delayed_work_sync(&dp->hdcp_cb_work); dp->hdcp_status = HDCP_STATE_AUTHENTICATING; queue_delayed_work(dp->workq, &dp->hdcp_cb_work, HZ / 2); } break; + case MDSS_EVENT_POST_PANEL_ON: + atomic_set(&dp->notification_pending, 0); + complete_all(&dp->notification_comp); + break; case MDSS_EVENT_PANEL_OFF: rc = mdss_dp_off(pdata); + atomic_set(&dp->notification_pending, 0); + complete_all(&dp->notification_comp); break; case MDSS_EVENT_BLANK: if (dp_is_hdcp_enabled(dp)) { dp->hdcp_status = HDCP_STATE_INACTIVE; - cancel_delayed_work(&dp->hdcp_cb_work); + cancel_delayed_work_sync(&dp->hdcp_cb_work); if (dp->hdcp.ops->off) dp->hdcp.ops->off(dp->hdcp.data); } @@ -3074,6 +3225,7 @@ static int mdss_retrieve_dp_ctrl_resources(struct platform_device *pdev, static void mdss_dp_video_ready(struct mdss_dp_drv_pdata *dp) { pr_debug("dp_video_ready\n"); + mdss_dp_ack_state(dp, true); complete(&dp->video_comp); } @@ -3105,10 +3257,21 @@ static int mdss_dp_event_thread(void *data) ev_data = (struct mdss_dp_event_data *)data; + pr_debug("starting\n"); while (!kthread_should_stop()) { wait_event(ev_data->event_q, (ev_data->pndx != ev_data->gndx) || - kthread_should_stop()); + kthread_should_stop() || + kthread_should_park()); + if (kthread_should_stop()) + return 0; + + if (kthread_should_park()) { + pr_debug("parking event thread\n"); + kthread_parkme(); + continue; + } + spin_lock_irqsave(&ev_data->event_lock, flag); ev = &(ev_data->event_list[ev_data->gndx++]); todo = ev->id; @@ -3200,6 +3363,7 @@ irqreturn_t dp_isr(int irq, void *ptr) spin_lock(&dp->lock); isr1 = dp_read(base + DP_INTR_STATUS); isr2 = dp_read(base + DP_INTR_STATUS2); + pr_debug("isr1=0x%08x, isr2=0x%08x\n", isr1, isr2); mask1 = isr1 & dp->mask1; @@ -3283,6 +3447,27 @@ static int mdss_dp_event_setup(struct mdss_dp_drv_pdata *dp) return 0; } +static void mdss_dp_reset_event_list(struct mdss_dp_drv_pdata *dp) +{ + struct mdss_dp_event_data *ev_data = &dp->dp_event; + + spin_lock(&ev_data->event_lock); + ev_data->pndx = ev_data->gndx = 0; + spin_unlock(&ev_data->event_lock); + + mutex_lock(&dp->attention_lock); + INIT_LIST_HEAD(&dp->attention_head); + mutex_unlock(&dp->attention_lock); +} + +static void mdss_dp_reset_sw_state(struct mdss_dp_drv_pdata *dp) +{ + pr_debug("enter\n"); + mdss_dp_reset_event_list(dp); + atomic_set(&dp->notification_pending, 0); + complete_all(&dp->notification_comp); +} + static void usbpd_connect_callback(struct usbpd_svid_handler *hdlr) { struct mdss_dp_drv_pdata *dp_drv; @@ -3294,6 +3479,8 @@ static void usbpd_connect_callback(struct usbpd_svid_handler *hdlr) } mdss_dp_update_cable_status(dp_drv, true); + if (dp_drv->ev_thread) + kthread_unpark(dp_drv->ev_thread); if (dp_drv->hpd) dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES); @@ -3313,6 +3500,9 @@ static void usbpd_disconnect_callback(struct usbpd_svid_handler *hdlr) mdss_dp_update_cable_status(dp_drv, false); dp_drv->alt_mode.current_state = UNKNOWN_STATE; + mdss_dp_reset_sw_state(dp_drv); + kthread_park(dp_drv->ev_thread); + /** * Manually turn off the DP controller if we are in PHY * testing mode. @@ -3416,6 +3606,17 @@ static inline void mdss_dp_link_maintenance(struct mdss_dp_drv_pdata *dp, if (mdss_dp_notify_clients(dp, NOTIFY_DISCONNECT_IRQ_HPD)) return; + if (atomic_read(&dp->notification_pending)) { + int ret; + + pr_debug("waiting for the disconnect to finish\n"); + ret = wait_for_completion_timeout(&dp->notification_comp, HZ); + if (ret <= 0) { + pr_warn("NOTIFY_DISCONNECT_IRQ_HPD timed out\n"); + return; + } + } + mdss_dp_on_irq(dp, lt_needed); } @@ -3567,7 +3768,7 @@ static int mdss_dp_process_audio_pattern_request(struct mdss_dp_drv_pdata *dp) return -EINVAL; if (dp_is_hdcp_enabled(dp) && dp->hdcp.ops->off) { - cancel_delayed_work(&dp->hdcp_cb_work); + cancel_delayed_work_sync(&dp->hdcp_cb_work); dp->hdcp.ops->off(dp->hdcp.data); } @@ -3613,10 +3814,46 @@ static int mdss_dp_process_audio_pattern_request(struct mdss_dp_drv_pdata *dp) static int mdss_dp_process_downstream_port_status_change( struct mdss_dp_drv_pdata *dp) { - if (!mdss_dp_is_downstream_port_status_changed(dp)) + bool ds_status_changed = false; + + if (mdss_dp_is_downstream_port_status_changed(dp)) { + pr_debug("downstream port status changed\n"); + ds_status_changed = true; + } + + /* + * Ideally sink should update the downstream port status changed + * whenever it updates the downstream sink count. However, it is + * possible that only the sink count is updated without setting + * the downstream port status changed bit. + */ + if (dp->sink_count.count != dp->prev_sink_count.count) { + pr_debug("downstream sink count changed from %d --> %d\n", + dp->prev_sink_count.count, dp->sink_count.count); + ds_status_changed = true; + } + + if (!ds_status_changed) return -EINVAL; - return mdss_dp_edid_read(dp); + mdss_dp_notify_clients(dp, NOTIFY_DISCONNECT_IRQ_HPD); + if (atomic_read(&dp->notification_pending)) { + int ret; + + pr_debug("waiting for the disconnect to finish\n"); + ret = wait_for_completion_timeout(&dp->notification_comp, HZ); + if (ret <= 0) { + pr_warn("NOTIFY_DISCONNECT_IRQ_HPD timed out\n"); + return -ETIMEDOUT; + } + } + + if (mdss_dp_is_ds_bridge_sink_count_zero(dp)) { + pr_debug("sink count is zero, nothing to do\n"); + return 0; + } + + return mdss_dp_process_hpd_high(dp); } static bool mdss_dp_video_pattern_test_lt_needed(struct mdss_dp_drv_pdata *dp) @@ -3714,19 +3951,19 @@ static int mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) mdss_dp_aux_parse_sink_status_field(dp); - ret = mdss_dp_process_link_training_request(dp); + ret = mdss_dp_process_downstream_port_status_change(dp); if (!ret) goto exit; - ret = mdss_dp_process_phy_test_pattern_request(dp); + ret = mdss_dp_process_link_training_request(dp); if (!ret) goto exit; - ret = mdss_dp_process_link_status_update(dp); + ret = mdss_dp_process_phy_test_pattern_request(dp); if (!ret) goto exit; - ret = mdss_dp_process_downstream_port_status_change(dp); + ret = mdss_dp_process_link_status_update(dp); if (!ret) goto exit; @@ -3739,7 +3976,6 @@ static int mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) goto exit; pr_debug("done\n"); - exit: dp->hpd_irq_on = false; return ret; @@ -3785,7 +4021,8 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, node->vdo = *vdos; mutex_lock(&dp_drv->attention_lock); - list_add_tail(&node->list, &dp_drv->attention_head); + if (dp_drv->cable_connected) + list_add_tail(&node->list, &dp_drv->attention_head); mutex_unlock(&dp_drv->attention_lock); dp_send_events(dp_drv, EV_USBPD_ATTENTION); @@ -3833,11 +4070,21 @@ static void mdss_dp_process_attention(struct mdss_dp_drv_pdata *dp_drv) if (!dp_drv->alt_mode.dp_status.hpd_high) { pr_debug("Attention: HPD low\n"); + if (!dp_drv->power_on) { + pr_debug("HPD already low\n"); + return; + } + if (dp_is_hdcp_enabled(dp_drv) && dp_drv->hdcp.ops->off) { - cancel_delayed_work(&dp_drv->hdcp_cb_work); + cancel_delayed_work_sync(&dp_drv->hdcp_cb_work); dp_drv->hdcp.ops->off(dp_drv->hdcp.data); } + /* + * Reset the sink count before nofifying clients since HPD Low + * indicates that the downstream device has been disconnected. + */ + mdss_dp_reset_sink_count(dp_drv); mdss_dp_notify_clients(dp_drv, NOTIFY_DISCONNECT); pr_debug("Attention: Notified clients\n"); @@ -3865,6 +4112,11 @@ static void mdss_dp_process_attention(struct mdss_dp_drv_pdata *dp_drv) pr_debug("Attention: HPD high\n"); + if (dp_drv->power_on) { + pr_debug("HPD high processed already\n"); + return; + } + dp_drv->alt_mode.current_state |= DP_STATUS_DONE; if (dp_drv->alt_mode.current_state & DP_CONFIGURE_DONE) { @@ -3886,7 +4138,13 @@ static void mdss_dp_handle_attention(struct mdss_dp_drv_pdata *dp) pr_debug("processing item %d in the list\n", ++i); + reinit_completion(&dp->notification_comp); mutex_lock(&dp->attention_lock); + if (!dp->cable_connected) { + pr_debug("cable disconnected, returning\n"); + mutex_unlock(&dp->attention_lock); + goto exit; + } node = list_first_entry(&dp->attention_head, struct mdss_dp_attention_node, list); @@ -3900,9 +4158,25 @@ static void mdss_dp_handle_attention(struct mdss_dp_drv_pdata *dp) mdss_dp_usbpd_ext_dp_status(&dp->alt_mode.dp_status); mdss_dp_process_attention(dp); + if (atomic_read(&dp->notification_pending)) { + pr_debug("waiting for the attention event to finish\n"); + /* + * This wait is intentionally implemented without a + * timeout since this is happens only in possible error + * conditions e.g. if the display framework does not + * power off/on the DisplayPort device in time. Other + * events might already be queued from the sink at this + * point and they cannot be processed until the power + * off/on is complete otherwise we might have problems + * with interleaving of these events e.g. un-clocked + * register access. + */ + wait_for_completion(&dp->notification_comp); + } pr_debug("done processing item %d in the list\n", i); }; +exit: pr_debug("exit\n"); } @@ -3978,7 +4252,6 @@ static int mdss_dp_probe(struct platform_device *pdev) dp_drv->mask1 = EDP_INTR_MASK1; dp_drv->mask2 = EDP_INTR_MASK2; mutex_init(&dp_drv->emutex); - mutex_init(&dp_drv->pd_msg_mutex); mutex_init(&dp_drv->attention_lock); mutex_init(&dp_drv->hdcp_mutex); spin_lock_init(&dp_drv->lock); @@ -4071,8 +4344,9 @@ static int mdss_dp_probe(struct platform_device *pdev) dp_drv->inited = true; dp_drv->hpd_irq_on = false; + atomic_set(&dp_drv->notification_pending, 0); mdss_dp_reset_test_data(dp_drv); - init_completion(&dp_drv->irq_comp); + init_completion(&dp_drv->notification_comp); dp_drv->suspend_vic = HDMI_VFRMT_UNKNOWN; pr_debug("done\n"); diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index 4decb26ea073..f358aad8a667 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -77,7 +77,7 @@ #define EDP_INTR_I2C_NACK BIT(18) #define EDP_INTR_I2C_DEFER BIT(21) #define EDP_INTR_PLL_UNLOCKED BIT(24) -#define EDP_INTR_AUX_ERROR BIT(27) +#define EDP_INTR_PHY_AUX_ERR BIT(27) #define EDP_INTR_STATUS1 \ @@ -85,7 +85,7 @@ EDP_INTR_WRONG_ADDR | EDP_INTR_TIMEOUT | \ EDP_INTR_NACK_DEFER | EDP_INTR_WRONG_DATA_CNT | \ EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER | \ - EDP_INTR_PLL_UNLOCKED | EDP_INTR_AUX_ERROR) + EDP_INTR_PLL_UNLOCKED | EDP_INTR_PHY_AUX_ERR) #define EDP_INTR_MASK1 (EDP_INTR_STATUS1 << 2) @@ -110,6 +110,8 @@ struct edp_buf { int len; /* dara length */ char trans_num; /* transaction number */ char i2c; /* 1 == i2c cmd, 0 == native cmd */ + bool no_send_addr; + bool no_send_stop; }; /* USBPD-TypeC specific Macros */ @@ -186,6 +188,7 @@ struct dp_alt_mode { #define DPCD_MAX_DOWNSPREAD_0_5 BIT(2) #define DPCD_NO_AUX_HANDSHAKE BIT(3) #define DPCD_PORT_0_EDID_PRESENTED BIT(4) +#define DPCD_PORT_1_EDID_PRESENTED BIT(5) /* event */ #define EV_EDP_AUX_SETUP BIT(0) @@ -239,6 +242,8 @@ struct downstream_port_config { bool oui_support; }; +#define DP_MAX_DS_PORT_COUNT 2 + struct dpcd_cap { char major; char minor; @@ -249,7 +254,7 @@ struct dpcd_cap { char enhanced_frame; u32 max_link_rate; /* 162, 270 and 540 Mb, divided by 10 */ u32 flags; - u32 rx_port0_buf_size; + u32 rx_port_buf_size[DP_MAX_DS_PORT_COUNT]; u32 training_read_interval;/* us */ struct downstream_port_config downstream_port; }; @@ -426,6 +431,102 @@ struct mdss_dp_crc_data { u32 b_cb; }; +#define MDSS_DP_MAX_PHY_CFG_VALUE_CNT 3 +struct mdss_dp_phy_cfg { + u32 cfg_cnt; + u32 current_index; + u32 offset; + u32 lut[MDSS_DP_MAX_PHY_CFG_VALUE_CNT]; +}; + +/* PHY AUX config registers */ +enum dp_phy_aux_config_type { + PHY_AUX_CFG0, + PHY_AUX_CFG1, + PHY_AUX_CFG2, + PHY_AUX_CFG3, + PHY_AUX_CFG4, + PHY_AUX_CFG5, + PHY_AUX_CFG6, + PHY_AUX_CFG7, + PHY_AUX_CFG8, + PHY_AUX_CFG9, + PHY_AUX_CFG_MAX, +}; + +static inline const char *mdss_dp_get_phy_aux_config_property(u32 cfg_type) +{ + switch (cfg_type) { + case PHY_AUX_CFG0: + return "qcom,aux-cfg0-settings"; + case PHY_AUX_CFG1: + return "qcom,aux-cfg1-settings"; + case PHY_AUX_CFG2: + return "qcom,aux-cfg2-settings"; + case PHY_AUX_CFG3: + return "qcom,aux-cfg3-settings"; + case PHY_AUX_CFG4: + return "qcom,aux-cfg4-settings"; + case PHY_AUX_CFG5: + return "qcom,aux-cfg5-settings"; + case PHY_AUX_CFG6: + return "qcom,aux-cfg6-settings"; + case PHY_AUX_CFG7: + return "qcom,aux-cfg7-settings"; + case PHY_AUX_CFG8: + return "qcom,aux-cfg8-settings"; + case PHY_AUX_CFG9: + return "qcom,aux-cfg9-settings"; + default: + return "unknown"; + } +} + +static inline char *mdss_dp_phy_aux_config_type_to_string(u32 cfg_type) +{ + switch (cfg_type) { + case PHY_AUX_CFG0: + return DP_ENUM_STR(PHY_AUX_CFG0); + case PHY_AUX_CFG1: + return DP_ENUM_STR(PHY_AUX_CFG1); + case PHY_AUX_CFG2: + return DP_ENUM_STR(PHY_AUX_CFG2); + case PHY_AUX_CFG3: + return DP_ENUM_STR(PHY_AUX_CFG3); + case PHY_AUX_CFG4: + return DP_ENUM_STR(PHY_AUX_CFG4); + case PHY_AUX_CFG5: + return DP_ENUM_STR(PHY_AUX_CFG5); + case PHY_AUX_CFG6: + return DP_ENUM_STR(PHY_AUX_CFG6); + case PHY_AUX_CFG7: + return DP_ENUM_STR(PHY_AUX_CFG7); + case PHY_AUX_CFG8: + return DP_ENUM_STR(PHY_AUX_CFG8); + case PHY_AUX_CFG9: + return DP_ENUM_STR(PHY_AUX_CFG9); + default: + return "unknown"; + } +} + +enum dp_aux_transaction { + DP_AUX_WRITE, + DP_AUX_READ +}; + +static inline char *mdss_dp_aux_transaction_to_string(u32 transaction) +{ + switch (transaction) { + case DP_AUX_WRITE: + return DP_ENUM_STR(DP_AUX_WRITE); + case DP_AUX_READ: + return DP_ENUM_STR(DP_AUX_READ); + default: + return "unknown"; + } +} + struct mdss_dp_drv_pdata { /* device driver */ int (*on) (struct mdss_panel_data *pdata); @@ -449,11 +550,11 @@ struct mdss_dp_drv_pdata { bool core_clks_on; bool link_clks_on; bool power_on; - bool sink_info_read; u32 suspend_vic; bool hpd; bool psm_enabled; bool audio_test_req; + bool dpcd_read_required; /* dp specific */ unsigned char *base; @@ -513,10 +614,9 @@ struct mdss_dp_drv_pdata { struct completion aux_comp; struct completion idle_comp; struct completion video_comp; - struct completion irq_comp; + struct completion notification_comp; struct mutex aux_mutex; struct mutex train_mutex; - struct mutex pd_msg_mutex; struct mutex attention_lock; struct mutex hdcp_mutex; bool cable_connected; @@ -540,13 +640,14 @@ struct mdss_dp_drv_pdata { struct dp_statistic dp_stat; bool hpd_irq_on; u32 hpd_notification_status; + atomic_t notification_pending; struct mdss_dp_event_data dp_event; struct task_struct *ev_thread; /* dt settings */ char l_map[4]; - u32 aux_cfg[AUX_CFG_LEN]; + struct mdss_dp_phy_cfg aux_cfg[PHY_AUX_CFG_MAX]; struct workqueue_struct *workq; struct delayed_work hdcp_cb_work; @@ -562,6 +663,7 @@ struct mdss_dp_drv_pdata { struct dpcd_test_request test_data; struct dpcd_sink_count sink_count; + struct dpcd_sink_count prev_sink_count; struct list_head attention_head; }; @@ -688,6 +790,7 @@ enum dp_aux_error { EDP_AUX_ERR_NACK = -3, EDP_AUX_ERR_DEFER = -4, EDP_AUX_ERR_NACK_DEFER = -5, + EDP_AUX_ERR_PHY = -6, }; static inline char *mdss_dp_get_aux_error(u32 aux_error) diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 8566b1d6985a..37209c161366 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -73,6 +73,21 @@ static int dp_buf_trailing(struct edp_buf *eb) return (int)(eb->end - eb->data); } +static void mdss_dp_aux_clear_hw_interrupts(void __iomem *phy_base) +{ + u32 data; + + data = dp_read(phy_base + DP_PHY_AUX_INTERRUPT_STATUS); + pr_debug("PHY_AUX_INTERRUPT_STATUS=0x%08x\n", data); + + dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0x1f); + dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0x9f); + dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0); + + /* Ensure that all interrupts are cleared and acked */ + wmb(); +} + /* * edp aux dp_buf_add_cmd: * NO native and i2c command mix allowed @@ -123,35 +138,46 @@ static int dp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd) return cmd->len - 1; } -static int dp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base) +static int dp_cmd_fifo_tx(struct mdss_dp_drv_pdata *dp) { u32 data; - char *dp; + char *datap; int len, cnt; + struct edp_buf *tp = &dp->txp; + void __iomem *base = dp->base; + len = tp->len; /* total byte to cmd fifo */ if (len == 0) return 0; cnt = 0; - dp = tp->start; + datap = tp->start; while (cnt < len) { - data = *dp; /* data byte */ + data = *datap; /* data byte */ data <<= 8; data &= 0x00ff00; /* index = 0, write */ if (cnt == 0) data |= BIT(31); /* INDEX_WRITE */ dp_write(base + DP_AUX_DATA, data); cnt++; - dp++; + datap++; } + /* clear the current tx request before queuing a new one */ + dp_write(base + DP_AUX_TRANS_CTRL, 0); + + /* clear any previous PHY AUX interrupts */ + mdss_dp_aux_clear_hw_interrupts(dp->phy_io.base); + data = (tp->trans_num - 1); if (tp->i2c) { data |= BIT(8); /* I2C */ - data |= BIT(10); /* NO SEND ADDR */ - data |= BIT(11); /* NO SEND STOP */ + if (tp->no_send_addr) + data |= BIT(10); /* NO SEND ADDR */ + if (tp->no_send_stop) + data |= BIT(11); /* NO SEND STOP */ } data |= BIT(9); /* GO */ @@ -164,7 +190,7 @@ static int dp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base) { u32 data; char *dp; - int i; + int i, actual_i; data = 0; /* index = 0 */ data |= BIT(31); /* INDEX_WRITE */ @@ -177,7 +203,12 @@ static int dp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base) data = dp_read(base + DP_AUX_DATA); for (i = 0; i < len; i++) { data = dp_read(base + DP_AUX_DATA); - *dp++ = (char)((data >> 8) & 0xff); + *dp++ = (char)((data >> 8) & 0xFF); + + actual_i = (data >> 16) & 0xFF; + if (i != actual_i) + pr_warn("Index mismatch: expected %d, found %d\n", + i, actual_i); } rp->len = len; @@ -214,9 +245,16 @@ static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep, reinit_completion(&ep->aux_comp); - len = dp_cmd_fifo_tx(&ep->txp, ep->base); + tp->no_send_addr = true; + tp->no_send_stop = true; + len = dp_cmd_fifo_tx(ep); - wait_for_completion_timeout(&ep->aux_comp, HZ/4); + if (!wait_for_completion_timeout(&ep->aux_comp, HZ/4)) { + pr_err("aux write timeout\n"); + ep->aux_error_num = EDP_AUX_ERR_TOUT; + /* Reset the AUX controller state machine */ + mdss_dp_aux_reset(&ep->ctrl_io); + } if (ep->aux_error_num == EDP_AUX_ERR_NONE) ret = len; @@ -228,13 +266,6 @@ static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep, return ret; } -int dp_aux_write(void *ep, struct edp_cmd *cmd) -{ - int rc = dp_aux_write_cmds(ep, cmd); - - return rc < 0 ? -EINVAL : 0; -} - static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, struct edp_cmd *cmds) { @@ -242,6 +273,7 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, struct edp_buf *tp; struct edp_buf *rp; int len, ret; + u32 data; mutex_lock(&ep->aux_mutex); ep->aux_cmd_busy = 1; @@ -270,10 +302,23 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, reinit_completion(&ep->aux_comp); - dp_cmd_fifo_tx(tp, ep->base); + tp->no_send_addr = true; + tp->no_send_stop = false; + dp_cmd_fifo_tx(ep); - wait_for_completion_timeout(&ep->aux_comp, HZ/4); + if (!wait_for_completion_timeout(&ep->aux_comp, HZ/4)) { + pr_err("aux read timeout\n"); + ep->aux_error_num = EDP_AUX_ERR_TOUT; + /* Reset the AUX controller state machine */ + mdss_dp_aux_reset(&ep->ctrl_io); + ret = ep->aux_error_num; + goto end; + } + /* clear the current rx request before queuing a new one */ + data = dp_read(ep->base + DP_AUX_TRANS_CTRL); + data &= (~BIT(9)); + dp_write(ep->base + DP_AUX_TRANS_CTRL, data); if (ep->aux_error_num == EDP_AUX_ERR_NONE) { ret = dp_cmd_fifo_rx(rp, len, ep->base); @@ -284,58 +329,128 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, ret = ep->aux_error_num; } +end: ep->aux_cmd_busy = 0; mutex_unlock(&ep->aux_mutex); return ret; } -int dp_aux_read(void *ep, struct edp_cmd *cmds) -{ - int rc = dp_aux_read_cmds(ep, cmds); - - return rc < 0 ? -EINVAL : 0; -} - void dp_aux_native_handler(struct mdss_dp_drv_pdata *ep, u32 isr) { - if (isr & EDP_INTR_AUX_I2C_DONE) + pr_debug("isr=0x%08x\n", isr); + if (isr & EDP_INTR_AUX_I2C_DONE) { ep->aux_error_num = EDP_AUX_ERR_NONE; - else if (isr & EDP_INTR_WRONG_ADDR) + } else if (isr & EDP_INTR_WRONG_ADDR) { ep->aux_error_num = EDP_AUX_ERR_ADDR; - else if (isr & EDP_INTR_TIMEOUT) + } else if (isr & EDP_INTR_TIMEOUT) { ep->aux_error_num = EDP_AUX_ERR_TOUT; - if (isr & EDP_INTR_NACK_DEFER) + } else if (isr & EDP_INTR_NACK_DEFER) { ep->aux_error_num = EDP_AUX_ERR_NACK; + } else if (isr & EDP_INTR_PHY_AUX_ERR) { + ep->aux_error_num = EDP_AUX_ERR_PHY; + mdss_dp_aux_clear_hw_interrupts(ep->phy_io.base); + } else { + ep->aux_error_num = EDP_AUX_ERR_NONE; + } complete(&ep->aux_comp); } void dp_aux_i2c_handler(struct mdss_dp_drv_pdata *ep, u32 isr) { + pr_debug("isr=0x%08x\n", isr); if (isr & EDP_INTR_AUX_I2C_DONE) { if (isr & (EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER)) ep->aux_error_num = EDP_AUX_ERR_NACK; else ep->aux_error_num = EDP_AUX_ERR_NONE; } else { - if (isr & EDP_INTR_WRONG_ADDR) + if (isr & EDP_INTR_WRONG_ADDR) { ep->aux_error_num = EDP_AUX_ERR_ADDR; - else if (isr & EDP_INTR_TIMEOUT) + } else if (isr & EDP_INTR_TIMEOUT) { ep->aux_error_num = EDP_AUX_ERR_TOUT; - if (isr & EDP_INTR_NACK_DEFER) + } else if (isr & EDP_INTR_NACK_DEFER) { ep->aux_error_num = EDP_AUX_ERR_NACK_DEFER; - if (isr & EDP_INTR_I2C_NACK) + } else if (isr & EDP_INTR_I2C_NACK) { ep->aux_error_num = EDP_AUX_ERR_NACK; - if (isr & EDP_INTR_I2C_DEFER) + } else if (isr & EDP_INTR_I2C_DEFER) { ep->aux_error_num = EDP_AUX_ERR_DEFER; + } else if (isr & EDP_INTR_PHY_AUX_ERR) { + ep->aux_error_num = EDP_AUX_ERR_PHY; + mdss_dp_aux_clear_hw_interrupts(ep->phy_io.base); + } else { + ep->aux_error_num = EDP_AUX_ERR_NONE; + } } complete(&ep->aux_comp); } -static int dp_aux_write_buf(struct mdss_dp_drv_pdata *ep, u32 addr, - char *buf, int len, int i2c) +static int dp_aux_rw_cmds_retry(struct mdss_dp_drv_pdata *dp, + struct edp_cmd *cmd, enum dp_aux_transaction transaction) +{ + int const retry_count = 5; + int adjust_count = 0; + int i; + u32 aux_cfg1_config_count; + int ret; + + aux_cfg1_config_count = mdss_dp_phy_aux_get_config_cnt(dp, + PHY_AUX_CFG1); +retry: + i = 0; + ret = 0; + do { + struct edp_cmd cmd1 = *cmd; + + dp->aux_error_num = EDP_AUX_ERR_NONE; + pr_debug("Trying %s, iteration count: %d\n", + mdss_dp_aux_transaction_to_string(transaction), + i + 1); + if (transaction == DP_AUX_READ) + ret = dp_aux_read_cmds(dp, &cmd1); + else if (transaction == DP_AUX_WRITE) + ret = dp_aux_write_cmds(dp, &cmd1); + + i++; + } while ((i < retry_count) && (ret < 0)); + + if (ret >= 0) /* rw success */ + goto end; + + if (adjust_count >= aux_cfg1_config_count) { + pr_err("PHY_AUX_CONFIG1 calibration failed\n"); + goto end; + } + + /* Adjust AUX configuration and retry */ + pr_debug("AUX failure (%d), adjust AUX settings\n", ret); + mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1); + adjust_count++; + goto retry; + +end: + return ret; +} + +/** + * dp_aux_write_buf_retry() - send a AUX write command + * @dp: display port driver data + * @addr: AUX address (in hex) to write the command to + * @buf: the buffer containing the actual payload + * @len: the length of the buffer @buf + * @i2c: indicates if it is an i2c-over-aux transaction + * @retry: specifies if retries should be attempted upon failures + * + * Send an AUX write command with the specified payload over the AUX + * channel. This function can send both native AUX command or an + * i2c-over-AUX command. In addition, if specified, it can also retry + * when failures are detected. The retry logic would adjust AUX PHY + * parameters on the fly. + */ +static int dp_aux_write_buf_retry(struct mdss_dp_drv_pdata *dp, u32 addr, + char *buf, int len, int i2c, bool retry) { struct edp_cmd cmd; @@ -346,11 +461,42 @@ static int dp_aux_write_buf(struct mdss_dp_drv_pdata *ep, u32 addr, cmd.len = len & 0x0ff; cmd.next = 0; - return dp_aux_write_cmds(ep, &cmd); + if (retry) + return dp_aux_rw_cmds_retry(dp, &cmd, DP_AUX_WRITE); + else + return dp_aux_write_cmds(dp, &cmd); } -static int dp_aux_read_buf(struct mdss_dp_drv_pdata *ep, u32 addr, - int len, int i2c) +static int dp_aux_write_buf(struct mdss_dp_drv_pdata *dp, u32 addr, + char *buf, int len, int i2c) +{ + return dp_aux_write_buf_retry(dp, addr, buf, len, i2c, true); +} + +int dp_aux_write(void *dp, struct edp_cmd *cmd) +{ + int rc = dp_aux_write_cmds(dp, cmd); + + return rc < 0 ? -EINVAL : 0; +} + +/** + * dp_aux_read_buf_retry() - send a AUX read command + * @dp: display port driver data + * @addr: AUX address (in hex) to write the command to + * @buf: the buffer containing the actual payload + * @len: the length of the buffer @buf + * @i2c: indicates if it is an i2c-over-aux transaction + * @retry: specifies if retries should be attempted upon failures + * + * Send an AUX write command with the specified payload over the AUX + * channel. This function can send both native AUX command or an + * i2c-over-AUX command. In addition, if specified, it can also retry + * when failures are detected. The retry logic would adjust AUX PHY + * parameters on the fly. + */ +static int dp_aux_read_buf_retry(struct mdss_dp_drv_pdata *dp, u32 addr, + int len, int i2c, bool retry) { struct edp_cmd cmd = {0}; @@ -361,7 +507,23 @@ static int dp_aux_read_buf(struct mdss_dp_drv_pdata *ep, u32 addr, cmd.len = len & 0x0ff; cmd.next = 0; - return dp_aux_read_cmds(ep, &cmd); + if (retry) + return dp_aux_rw_cmds_retry(dp, &cmd, DP_AUX_READ); + else + return dp_aux_read_cmds(dp, &cmd); +} + +static int dp_aux_read_buf(struct mdss_dp_drv_pdata *dp, u32 addr, + int len, int i2c) +{ + return dp_aux_read_buf_retry(dp, addr, len, i2c, true); +} + +int dp_aux_read(void *dp, struct edp_cmd *cmds) +{ + int rc = dp_aux_read_cmds(dp, cmds); + + return rc < 0 ? -EINVAL : 0; } /* @@ -733,16 +895,68 @@ static void dp_aux_send_checksum(struct mdss_dp_drv_pdata *dp, u32 checksum) dp_aux_write_buf(dp, 0x260, data, 1, 0); } +int mdss_dp_aux_read_edid(struct mdss_dp_drv_pdata *dp, + u8 *buf, int size, int blk_num) +{ + int max_size_bytes = 16; + int rc, read_size; + int ret = 0; + u8 offset_lut[] = {0x0, 0x80}; + u8 offset; + + if (dp->test_data.test_requested == TEST_EDID_READ) + max_size_bytes = 128; + + /* + * Calculate the offset of the desired EDID block to be read. + * For even blocks, offset starts at 0x0 + * For odd blocks, offset starts at 0x80 + */ + if (blk_num % 2) + offset = offset_lut[1]; + else + offset = offset_lut[0]; + + do { + struct edp_cmd cmd = {0}; + + read_size = min(size, max_size_bytes); + cmd.read = 1; + cmd.addr = EDID_START_ADDRESS; + cmd.len = read_size; + cmd.out_buf = buf; + cmd.i2c = 1; + + /* Write the offset first prior to reading the data */ + pr_debug("offset=0x%x, size=%d\n", offset, size); + dp_aux_write_buf_retry(dp, EDID_START_ADDRESS, &offset, 1, 1, + false); + rc = dp_aux_read(dp, &cmd); + if (rc < 0) { + pr_err("aux read failed\n"); + return rc; + } + + print_hex_dump(KERN_DEBUG, "DP:EDID: ", DUMP_PREFIX_NONE, 16, 1, + buf, read_size, false); + buf += read_size; + offset += read_size; + size -= read_size; + ret += read_size; + } while (size > 0); + + return ret; +} + int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp) { - struct edp_buf *rp = &dp->rxp; int rlen, ret = 0; int edid_blk = 0, blk_num = 0, retries = 10; bool edid_parsing_done = false; - const u8 cea_tag = 0x02, start_ext_blk = 0x1; u32 const segment_addr = 0x30; u32 checksum = 0; - char segment = 0x1; + bool phy_aux_update_requested = false; + bool ext_block_parsing_done = false; ret = dp_aux_chan_ready(dp); if (ret) { @@ -750,72 +964,91 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp) return ret; } + memset(dp->edid_buf, 0, dp->edid_buf_size); + /** * Parse the test request vector to see whether there is a * TEST_EDID_READ test request. */ dp_sink_parse_test_request(dp); - do { - rlen = dp_aux_read_buf(dp, EDID_START_ADDRESS + - (blk_num * EDID_BLOCK_SIZE), - EDID_BLOCK_SIZE, 1); + while (retries) { + u8 segment; + u8 edid_buf[EDID_BLOCK_SIZE] = {0}; + + /* + * Write the segment first. + * Segment = 0, for blocks 0 and 1 + * Segment = 1, for blocks 2 and 3 + * Segment = 2, for blocks 3 and 4 + * and so on ... + */ + segment = blk_num >> 1; + dp_aux_write_buf_retry(dp, segment_addr, &segment, 1, 1, false); + + rlen = mdss_dp_aux_read_edid(dp, edid_buf, EDID_BLOCK_SIZE, + blk_num); if (rlen != EDID_BLOCK_SIZE) { - pr_err("Read failed. rlen=%d\n", rlen); + pr_err("Read failed. rlen=%s\n", + mdss_dp_get_aux_error(rlen)); + mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1); + phy_aux_update_requested = true; + retries--; continue; } - pr_debug("blk_num=%d, rlen=%d\n", blk_num, rlen); - - if (dp_edid_is_valid_header(rp->data)) { - ret = dp_edid_buf_error(rp->data, rp->len); + print_hex_dump(KERN_DEBUG, "DP:EDID: ", DUMP_PREFIX_NONE, 16, 1, + edid_buf, EDID_BLOCK_SIZE, false); + if (dp_edid_is_valid_header(edid_buf)) { + ret = dp_edid_buf_error(edid_buf, rlen); if (ret) { pr_err("corrupt edid block detected\n"); + mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1); + phy_aux_update_requested = true; + retries--; continue; } if (edid_parsing_done) { + pr_debug("block 0 parsed already\n"); blk_num++; + retries--; continue; } - dp_extract_edid_manufacturer(&dp->edid, rp->data); - dp_extract_edid_product(&dp->edid, rp->data); - dp_extract_edid_version(&dp->edid, rp->data); - dp_extract_edid_ext_block_cnt(&dp->edid, rp->data); - dp_extract_edid_video_support(&dp->edid, rp->data); - dp_extract_edid_feature(&dp->edid, rp->data); + dp_extract_edid_manufacturer(&dp->edid, edid_buf); + dp_extract_edid_product(&dp->edid, edid_buf); + dp_extract_edid_version(&dp->edid, edid_buf); + dp_extract_edid_ext_block_cnt(&dp->edid, edid_buf); + dp_extract_edid_video_support(&dp->edid, edid_buf); + dp_extract_edid_feature(&dp->edid, edid_buf); dp_extract_edid_detailed_timing_description(&dp->edid, - rp->data); + edid_buf); edid_parsing_done = true; + } else if (!edid_parsing_done) { + pr_debug("Invalid edid block 0 header\n"); + /* Retry block 0 with adjusted phy aux settings */ + mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1); + phy_aux_update_requested = true; + retries--; + continue; } else { edid_blk++; blk_num++; - - /* fix dongle byte shift issue */ - if (edid_blk == 1 && rp->data[0] != cea_tag) { - u8 tmp[EDID_BLOCK_SIZE - 1]; - - memcpy(tmp, rp->data, EDID_BLOCK_SIZE - 1); - rp->data[0] = cea_tag; - memcpy(rp->data + 1, tmp, EDID_BLOCK_SIZE - 1); - } } memcpy(dp->edid_buf + (edid_blk * EDID_BLOCK_SIZE), - rp->data, EDID_BLOCK_SIZE); + edid_buf, EDID_BLOCK_SIZE); - checksum = rp->data[rp->len - 1]; + checksum = edid_buf[rlen - 1]; /* break if no more extension blocks present */ - if (edid_blk == dp->edid.ext_block_cnt) + if (edid_blk >= dp->edid.ext_block_cnt) { + ext_block_parsing_done = true; break; - - /* write segment number to read block 3 onwards */ - if (edid_blk == start_ext_blk) - dp_aux_write_buf(dp, segment_addr, &segment, 1, 1); - } while (retries--); + } + } if (dp->test_data.test_requested == TEST_EDID_READ) { pr_debug("sending checksum %d\n", checksum); @@ -823,6 +1056,18 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp) dp->test_data = (const struct dpcd_test_request){ 0 }; } + /* + * Trigger the reading of DPCD if there was a change in the AUX + * configuration caused by a failure while reading the EDID. + * This is required to ensure the integrity and validity + * of the sink capabilities read that will subsequently be used + * to establish the mainlink. + */ + if (edid_parsing_done && ext_block_parsing_done + && phy_aux_update_requested) { + dp->dpcd_read_required = true; + } + return ret; } @@ -834,6 +1079,10 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) struct dpcd_cap *cap; struct edp_buf *rp; int rlen; + int i; + + cap = &ep->dpcd; + memset(cap, 0, sizeof(*cap)); rlen = dp_aux_read_buf(ep, 0, len, 0); if (rlen <= 0) { @@ -848,11 +1097,8 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) } rp = &ep->rxp; - cap = &ep->dpcd; bp = rp->data; - memset(cap, 0, sizeof(*cap)); - data = *bp++; /* byte 0 */ cap->major = (data >> 4) & 0x0f; cap->minor = data & 0x0f; @@ -909,6 +1155,11 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) data = *bp++; /* Byte 7: DOWN_STREAM_PORT_COUNT */ cap->downstream_port.dfp_count = data & 0x7; + if (cap->downstream_port.dfp_count > DP_MAX_DS_PORT_COUNT) { + pr_debug("DS port count %d greater that max (%d) supported\n", + cap->downstream_port.dfp_count, DP_MAX_DS_PORT_COUNT); + cap->downstream_port.dfp_count = DP_MAX_DS_PORT_COUNT; + } cap->downstream_port.msa_timing_par_ignored = data & BIT(6); cap->downstream_port.oui_support = data & BIT(7); pr_debug("dfp_count = %d, msa_timing_par_ignored = %d\n", @@ -916,17 +1167,23 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) cap->downstream_port.msa_timing_par_ignored); pr_debug("oui_support = %d\n", cap->downstream_port.oui_support); - data = *bp++; /* byte 8 */ - if (data & BIT(1)) { - cap->flags |= DPCD_PORT_0_EDID_PRESENTED; - pr_debug("edid presented\n"); - } - - data = *bp++; /* byte 9 */ - cap->rx_port0_buf_size = (data + 1) * 32; - pr_debug("lane_buf_size=%d\n", cap->rx_port0_buf_size); + for (i = 0; i < DP_MAX_DS_PORT_COUNT; i++) { + data = *bp++; /* byte 8 + i*2 */ + pr_debug("parsing capabilities for DS port %d\n", i); + if (data & BIT(1)) { + if (i == 0) + cap->flags |= DPCD_PORT_0_EDID_PRESENTED; + else + cap->flags |= DPCD_PORT_1_EDID_PRESENTED; + pr_debug("local edid present\n"); + } else { + pr_debug("local edid absent\n"); + } - bp += 2; /* skip 10, 11 port1 capability */ + data = *bp++; /* byte 9 + i*2 */ + cap->rx_port_buf_size[i] = (data + 1) * 32; + pr_debug("lane_buf_size=%d\n", cap->rx_port_buf_size[i]); + } data = *bp++; /* byte 12 */ cap->i2c_speed_ctrl = data; @@ -1258,6 +1515,8 @@ static void dp_sink_parse_sink_count(struct mdss_dp_drv_pdata *ep) int const param_len = 0x1; int const sink_count_addr = 0x200; + ep->prev_sink_count = ep->sink_count; + rlen = dp_aux_read_buf(ep, sink_count_addr, param_len, 0); if (rlen < param_len) { pr_err("failed to read sink count\n"); @@ -2363,8 +2622,8 @@ clear: void mdss_dp_aux_parse_sink_status_field(struct mdss_dp_drv_pdata *ep) { dp_sink_parse_sink_count(ep); - dp_sink_parse_test_request(ep); mdss_dp_aux_link_status_read(ep, 6); + dp_sink_parse_test_request(ep); } int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *ep) diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c index ea492f54054c..0d9cf7b72b4d 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.c +++ b/drivers/video/fbdev/msm/mdss_dp_util.c @@ -858,6 +858,48 @@ void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io, u8 link_rate, pr_debug("dp_tu=0x%x\n", dp_tu); } +void mdss_dp_aux_set_limits(struct dss_io_data *ctrl_io) +{ + u32 const max_aux_timeout_count = 0xFFFFF; + u32 const max_aux_limits = 0xFFFFFFFF; + + pr_debug("timeout=0x%x, limits=0x%x\n", + max_aux_timeout_count, max_aux_limits); + + writel_relaxed(max_aux_timeout_count, + ctrl_io->base + DP_AUX_TIMEOUT_COUNT); + writel_relaxed(max_aux_limits, ctrl_io->base + DP_AUX_LIMITS); +} + +void mdss_dp_phy_aux_update_config(struct mdss_dp_drv_pdata *dp, + enum dp_phy_aux_config_type config_type) +{ + u32 new_index; + struct dss_io_data *phy_io = &dp->phy_io; + struct mdss_dp_phy_cfg *cfg = mdss_dp_phy_aux_get_config(dp, + config_type); + + if (!cfg) { + pr_err("invalid config type %s", + mdss_dp_phy_aux_config_type_to_string(config_type)); + return; + } + + new_index = (cfg->current_index + 1) % cfg->cfg_cnt; + + pr_debug("Updating %s from 0x%08x to 0x%08x\n", + mdss_dp_phy_aux_config_type_to_string(config_type), + cfg->lut[cfg->current_index], cfg->lut[new_index]); + writel_relaxed(cfg->lut[new_index], phy_io->base + cfg->offset); + cfg->current_index = new_index; + + /* Make sure the new HW configuration takes effect */ + wmb(); + + /* Reset the AUX controller before any subsequent transactions */ + mdss_dp_aux_reset(&dp->ctrl_io); +} + void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, char *l_map) { u8 bits_per_lane = 2; @@ -870,26 +912,24 @@ void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, char *l_map) ctrl_io->base + DP_LOGICAL2PHYSCIAL_LANE_MAPPING); } -void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io, u32 *aux_cfg, - u32 phy_reg_offset) +void mdss_dp_phy_aux_setup(struct mdss_dp_drv_pdata *dp) { - void __iomem *adjusted_phy_io_base = phy_io->base + phy_reg_offset; + int i; + void __iomem *adjusted_phy_io_base = dp->phy_io.base + + dp->phy_reg_offset; writel_relaxed(0x3d, adjusted_phy_io_base + DP_PHY_PD_CTL); - /* DP AUX CFG register programming */ - writel_relaxed(aux_cfg[0], adjusted_phy_io_base + DP_PHY_AUX_CFG0); - writel_relaxed(aux_cfg[1], adjusted_phy_io_base + DP_PHY_AUX_CFG1); - writel_relaxed(aux_cfg[2], adjusted_phy_io_base + DP_PHY_AUX_CFG2); - writel_relaxed(aux_cfg[3], adjusted_phy_io_base + DP_PHY_AUX_CFG3); - writel_relaxed(aux_cfg[4], adjusted_phy_io_base + DP_PHY_AUX_CFG4); - writel_relaxed(aux_cfg[5], adjusted_phy_io_base + DP_PHY_AUX_CFG5); - writel_relaxed(aux_cfg[6], adjusted_phy_io_base + DP_PHY_AUX_CFG6); - writel_relaxed(aux_cfg[7], adjusted_phy_io_base + DP_PHY_AUX_CFG7); - writel_relaxed(aux_cfg[8], adjusted_phy_io_base + DP_PHY_AUX_CFG8); - writel_relaxed(aux_cfg[9], adjusted_phy_io_base + DP_PHY_AUX_CFG9); - - writel_relaxed(0x1f, adjusted_phy_io_base + DP_PHY_AUX_INTERRUPT_MASK); + for (i = 0; i < PHY_AUX_CFG_MAX; i++) { + struct mdss_dp_phy_cfg *cfg = mdss_dp_phy_aux_get_config(dp, i); + + pr_debug("%s: offset=0x%08x, value=0x%08x\n", + mdss_dp_phy_aux_config_type_to_string(i), cfg->offset, + cfg->lut[cfg->current_index]); + writel_relaxed(cfg->lut[cfg->current_index], + dp->phy_io.base + cfg->offset); + }; + writel_relaxed(0x1e, adjusted_phy_io_base + DP_PHY_AUX_INTERRUPT_MASK); } int mdss_dp_irq_setup(struct mdss_dp_drv_pdata *dp_drv) diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h index 8f19e7cdf3cf..4c93e48e97dc 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.h +++ b/drivers/video/fbdev/msm/mdss_dp_util.h @@ -35,6 +35,8 @@ #define DP_AUX_CTRL (0x00000230) #define DP_AUX_DATA (0x00000234) #define DP_AUX_TRANS_CTRL (0x00000238) +#define DP_AUX_TIMEOUT_COUNT (0x0000023C) +#define DP_AUX_LIMITS (0x00000240) #define DP_AUX_STATUS (0x00000244) #define DP_DPCD_CP_IRQ (0x201) @@ -163,6 +165,7 @@ #define DP_PHY_AUX_CFG9 (0x00000040) #define DP_PHY_AUX_INTERRUPT_MASK (0x00000044) #define DP_PHY_AUX_INTERRUPT_CLEAR (0x00000048) +#define DP_PHY_AUX_INTERRUPT_STATUS (0x000000B8) #define DP_PHY_SPARE0 0x00A8 @@ -271,6 +274,19 @@ static const struct dp_vc_tu_mapping_table tu_table[] = { 0x21, 0x000c, false, 0x00, 0x00, 0x00, 0x27}, }; +static inline struct mdss_dp_phy_cfg *mdss_dp_phy_aux_get_config( + struct mdss_dp_drv_pdata *dp, enum dp_phy_aux_config_type cfg_type) +{ + return &dp->aux_cfg[cfg_type]; +} + +static inline u32 mdss_dp_phy_aux_get_config_cnt( + struct mdss_dp_drv_pdata *dp, enum dp_phy_aux_config_type cfg_type) +{ + return dp->aux_cfg[cfg_type].cfg_cnt; +} + +void mdss_dp_aux_set_limits(struct dss_io_data *ctrl_io); int dp_aux_read(void *ep, struct edp_cmd *cmds); int dp_aux_write(void *ep, struct edp_cmd *cmd); void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data); @@ -285,8 +301,9 @@ void mdss_dp_assert_phy_reset(struct dss_io_data *ctrl_io, bool assert); void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io, u8 link_rate, u8 ln_cnt, u32 res, struct mdss_panel_info *pinfo); void mdss_dp_config_misc(struct mdss_dp_drv_pdata *dp, u32 bd, u32 cc); -void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io, u32 *aux_cfg, - u32 phy_reg_offset); +void mdss_dp_phy_aux_setup(struct mdss_dp_drv_pdata *dp); +void mdss_dp_phy_aux_update_config(struct mdss_dp_drv_pdata *dp, + enum dp_phy_aux_config_type config_type); void mdss_dp_hpd_configure(struct dss_io_data *ctrl_io, bool enable); void mdss_dp_aux_ctrl(struct dss_io_data *ctrl_io, bool enable); void mdss_dp_mainlink_ctrl(struct dss_io_data *ctrl_io, bool enable); diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index 17722eac3006..c7cac996e5c0 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -26,6 +26,7 @@ #include <linux/uaccess.h> #include <linux/msm-bus.h> #include <linux/pm_qos.h> +#include <linux/dma-buf.h> #include "mdss.h" #include "mdss_panel.h" @@ -44,6 +45,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; @@ -570,7 +662,11 @@ static int mdss_dsi_get_dt_vreg_data(struct device *dev, mp->vreg_config[i].post_off_sleep = tmp; } - pr_debug("%s: %s min=%d, max=%d, enable=%d, disable=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d\n", + mp->vreg_config[i].lp_disable_allowed = + of_property_read_bool(supply_node, + "qcom,supply-lp-mode-disable-allowed"); + + pr_debug("%s: %s min=%d, max=%d, enable=%d, disable=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d lp_disable_allowed=%d\n", __func__, mp->vreg_config[i].vreg_name, mp->vreg_config[i].min_voltage, @@ -580,8 +676,8 @@ static int mdss_dsi_get_dt_vreg_data(struct device *dev, mp->vreg_config[i].pre_on_sleep, mp->vreg_config[i].post_on_sleep, mp->vreg_config[i].pre_off_sleep, - mp->vreg_config[i].post_off_sleep - ); + mp->vreg_config[i].post_off_sleep, + mp->vreg_config[i].lp_disable_allowed); ++i; } @@ -2713,10 +2809,7 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, rc = mdss_dsi_reconfig(pdata, mode); break; case MDSS_EVENT_DSI_PANEL_STATUS: - if (ctrl_pdata->check_status) - rc = ctrl_pdata->check_status(ctrl_pdata); - else - rc = true; + rc = mdss_dsi_check_panel_status(ctrl_pdata, arg); break; case MDSS_EVENT_PANEL_TIMING_SWITCH: rc = mdss_dsi_panel_timing_switch(ctrl_pdata, arg); @@ -3359,6 +3452,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: @@ -4269,6 +4364,9 @@ int dsi_panel_device_register(struct platform_device *ctrl_pdev, return rc; } + /* default state of gpio is false */ + ctrl_pdata->bklt_en_gpio_state = false; + pinfo->panel_max_fps = mdss_panel_get_framerate(pinfo); pinfo->panel_max_vtotal = mdss_panel_get_vtotal(pinfo); diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index 2a76466abf3e..62d88f0af652 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -315,6 +315,8 @@ struct mdss_dsi_data { * mutex, clocks, regulator information, setup information */ struct dsi_shared_data *shared_data; + u32 *dbg_bus; + int dbg_bus_size; }; /* @@ -452,6 +454,7 @@ struct mdss_dsi_ctrl_pdata { int disp_en_gpio; int bklt_en_gpio; bool bklt_en_gpio_invert; + bool bklt_en_gpio_state; int lcd_mode_sel_gpio; int bklt_ctrl; /* backlight ctrl */ bool pwm_pmi; @@ -704,6 +707,9 @@ void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, void mdss_dsi_set_reg(struct mdss_dsi_ctrl_pdata *ctrl, int off, u32 mask, u32 val); int mdss_dsi_phy_pll_reset_status(struct mdss_dsi_ctrl_pdata *ctrl); +int mdss_dsi_check_panel_status(struct mdss_dsi_ctrl_pdata *ctrl, void *arg); + +void mdss_dsi_debug_bus_init(struct mdss_dsi_data *sdata); static inline const char *__mdss_dsi_pm_name(enum dsi_pm_type module) { diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c index 9f4b7eb52492..1a471155072b 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_host.c +++ b/drivers/video/fbdev/msm/mdss_dsi_host.c @@ -778,6 +778,12 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event) u32 loop = 10, u_dly = 200; pr_debug("%s: MDSS DSI CTRL and PHY reset. ctrl-num = %d\n", __func__, ctrl->ndx); + + if (ctrl->panel_mode == DSI_CMD_MODE) { + pr_warn("ctl_phy_reset not applicable for cmd mode\n"); + return; + } + if (event == DSI_EV_DLNx_FIFO_OVERFLOW) { mask = BIT(20); /* clock lane only for overflow recovery */ } else if (event == DSI_EV_LP_RX_TIMEOUT) { @@ -792,15 +798,6 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event) ctrl0 = mdss_dsi_get_ctrl_by_index(DSI_CTRL_0); ctrl1 = mdss_dsi_get_ctrl_by_index(DSI_CTRL_1); - if (ctrl0->recovery) { - rc = ctrl0->recovery->fxn(ctrl0->recovery->data, - MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW); - if (rc < 0) { - pr_debug("%s: Target is in suspend/shutdown\n", - __func__); - return; - } - } /* * Disable PHY contention detection and receive. * Configure the strength ctrl 1 register. @@ -874,6 +871,15 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event) MIPI_OUTP(ctrl0->ctrl_base + 0x0ac, ln_ctrl0 & ~mask); MIPI_OUTP(ctrl1->ctrl_base + 0x0ac, ln_ctrl1 & ~mask); + if (ctrl0->recovery) { + rc = ctrl0->recovery->fxn(ctrl0->recovery->data, + MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW); + if (rc < 0) { + pr_debug("%s: Target is in suspend/shutdown\n", + __func__); + return; + } + } /* Enable Video mode for DSI controller */ MIPI_OUTP(ctrl0->ctrl_base + 0x004, data0); MIPI_OUTP(ctrl1->ctrl_base + 0x004, data1); @@ -890,15 +896,6 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event) */ udelay(200); } else { - if (ctrl->recovery) { - rc = ctrl->recovery->fxn(ctrl->recovery->data, - MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW); - if (rc < 0) { - pr_debug("%s: Target is in suspend/shutdown\n", - __func__); - return; - } - } /* Disable PHY contention detection and receive */ MIPI_OUTP((ctrl->phy_io.base) + 0x0188, 0); @@ -951,6 +948,15 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event) __func__, ln0); MIPI_OUTP(ctrl->ctrl_base + 0x0ac, ln_ctrl0 & ~mask); + if (ctrl->recovery) { + rc = ctrl->recovery->fxn(ctrl->recovery->data, + MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW); + if (rc < 0) { + pr_debug("%s: Target is in suspend/shutdown\n", + __func__); + return; + } + } /* Enable Video mode for DSI controller */ MIPI_OUTP(ctrl->ctrl_base + 0x004, data0); /* Enable PHY contention detection and receiver */ @@ -1311,6 +1317,31 @@ void mdss_dsi_set_burst_mode(struct mdss_dsi_ctrl_pdata *ctrl) } +static void mdss_dsi_split_link_setup(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + u32 data = 0; + struct mdss_panel_info *pinfo; + + if (!ctrl_pdata) + return; + + pinfo = &ctrl_pdata->panel_data.panel_info; + if (!pinfo->split_link_enabled) + return; + + pr_debug("%s: enable split link\n", __func__); + + data = MIPI_INP((ctrl_pdata->ctrl_base) + 0x330); + /* DMA_LINK_SEL */ + data |= 0x3 << 12; + /* MDP0_LINK_SEL */ + data |= 0x5 << 20; + /* EN */ + data |= 0x1; + /* DSI_SPLIT_LINK_CTRL */ + MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x330, data); +} + static void mdss_dsi_mode_setup(struct mdss_panel_data *pdata) { struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; @@ -1429,6 +1460,8 @@ static void mdss_dsi_mode_setup(struct mdss_panel_data *pdata) } mdss_dsi_dsc_config(ctrl_pdata, dsc); + + mdss_dsi_split_link_setup(ctrl_pdata); } void mdss_dsi_ctrl_setup(struct mdss_dsi_ctrl_pdata *ctrl) @@ -1529,11 +1562,13 @@ wait: MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", "dsi0_phy", "dsi1_ctrl", "dsi1_phy", "vbif", "vbif_nrt", "dbg_bus", - "vbif_dbg_bus", "panic"); + "vbif_dbg_bus", "dsi_dbg_bus", "panic"); /* 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(); @@ -2152,6 +2187,7 @@ static int mdss_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl, MIPI_OUTP((ctrl->ctrl_base) + 0x090, 0x01); wmb(); + MDSS_XLOG(ctrl->dma_addr, len); if (ctrl->do_unicast) { /* let cmd_trigger to kickoff later */ @@ -2528,7 +2564,7 @@ void mdss_dsi_cmd_mdp_busy(struct mdss_dsi_ctrl_pdata *ctrl) MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", "dsi0_phy", "dsi1_ctrl", "dsi1_phy", "vbif", "vbif_nrt", "dbg_bus", - "vbif_dbg_bus", "panic"); + "vbif_dbg_bus", "dsi_dbg_bus", "panic"); } } } @@ -2998,7 +3034,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); @@ -3151,7 +3190,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", "panic"); + "dsi1_ctrl", "dsi1_phy", "dsi_dbg_bus", "panic"); } } @@ -3229,8 +3268,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); @@ -3239,6 +3280,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 6f20c0ed0455..60012c71449c 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -260,16 +260,6 @@ static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata) rc); goto rst_gpio_err; } - if (gpio_is_valid(ctrl_pdata->bklt_en_gpio)) { - rc = gpio_request(ctrl_pdata->bklt_en_gpio, - "bklt_enable"); - if (rc) { - pr_err("request bklt gpio failed, rc=%d\n", - rc); - goto bklt_en_gpio_err; - } - } - if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { rc = gpio_request(ctrl_pdata->lcd_mode_sel_gpio, "mode_sel"); if (rc) { @@ -282,9 +272,6 @@ static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata) return rc; lcd_mode_sel_gpio_err: - if (gpio_is_valid(ctrl_pdata->bklt_en_gpio)) - gpio_free(ctrl_pdata->bklt_en_gpio); -bklt_en_gpio_err: gpio_free(ctrl_pdata->rst_gpio); rst_gpio_err: if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) @@ -293,6 +280,81 @@ disp_en_gpio_err: return rc; } +int mdss_dsi_bl_gpio_ctrl(struct mdss_panel_data *pdata, int enable) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + int rc = 0, val = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + if (ctrl_pdata == NULL) { + pr_err("%s: Invalid ctrl data\n", __func__); + return -EINVAL; + } + + /* if gpio is not valid */ + if (!gpio_is_valid(ctrl_pdata->bklt_en_gpio)) + return rc; + + pr_debug("%s: enable = %d\n", __func__, enable); + + /* + * if gpio state is false and enable (bl level) is + * non zero then toggle the gpio + */ + if (!ctrl_pdata->bklt_en_gpio_state && enable) { + rc = gpio_request(ctrl_pdata->bklt_en_gpio, "bklt_enable"); + if (rc) { + pr_err("request bklt gpio failed, rc=%d\n", rc); + goto free; + } + + if (ctrl_pdata->bklt_en_gpio_invert) + val = 0; + else + val = 1; + + rc = gpio_direction_output(ctrl_pdata->bklt_en_gpio, val); + if (rc) { + pr_err("%s: unable to set dir for bklt gpio val %d\n", + __func__, val); + goto free; + } + ctrl_pdata->bklt_en_gpio_state = true; + goto ret; + } else if (ctrl_pdata->bklt_en_gpio_state && !enable) { + /* + * if gpio state is true and enable (bl level) is + * zero then toggle the gpio + */ + if (ctrl_pdata->bklt_en_gpio_invert) + val = 1; + else + val = 0; + + rc = gpio_direction_output(ctrl_pdata->bklt_en_gpio, val); + if (rc) + pr_err("%s: unable to set dir for bklt gpio val %d\n", + __func__, val); + goto free; + } + + /* gpio state is true and bl level is non zero */ + goto ret; + +free: + pr_debug("%s: free bklt gpio\n", __func__); + ctrl_pdata->bklt_en_gpio_state = false; + gpio_free(ctrl_pdata->bklt_en_gpio); +ret: + return rc; +} + int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) { struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; @@ -362,26 +424,6 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) if (pdata->panel_info.rst_seq[++i]) usleep_range(pinfo->rst_seq[i] * 1000, pinfo->rst_seq[i] * 1000); } - - if (gpio_is_valid(ctrl_pdata->bklt_en_gpio)) { - - if (ctrl_pdata->bklt_en_gpio_invert) { - rc = gpio_direction_output( - ctrl_pdata->bklt_en_gpio, 0); - gpio_set_value( - (ctrl_pdata->bklt_en_gpio), 0); - } else { - rc = gpio_direction_output( - ctrl_pdata->bklt_en_gpio, 1); - gpio_set_value( - (ctrl_pdata->bklt_en_gpio), 1); - } - if (rc) { - pr_err("%s: unable to set dir for bklt gpio\n", - __func__); - goto exit; - } - } } if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { @@ -410,15 +452,6 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) pr_debug("%s: Reset panel done\n", __func__); } } else { - if (gpio_is_valid(ctrl_pdata->bklt_en_gpio)) { - - 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); gpio_free(ctrl_pdata->disp_en_gpio); @@ -801,6 +834,9 @@ static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata, if ((bl_level < pdata->panel_info.bl_min) && (bl_level != 0)) bl_level = pdata->panel_info.bl_min; + /* enable the backlight gpio if present */ + mdss_dsi_bl_gpio_ctrl(pdata, bl_level); + switch (ctrl_pdata->bklt_ctrl) { case BL_WLED: led_trigger_event(bl_led_trigger, bl_level); @@ -1350,6 +1386,44 @@ static int mdss_dsi_parse_hdr_settings(struct device_node *np, return 0; } +static int mdss_dsi_parse_split_link_settings(struct device_node *np, + struct mdss_panel_info *pinfo) +{ + u32 tmp; + int rc = 0; + + if (!np) { + pr_err("%s: device node pointer is NULL\n", __func__); + return -EINVAL; + } + + if (!pinfo) { + pr_err("%s: panel info is NULL\n", __func__); + return -EINVAL; + } + + pinfo->split_link_enabled = of_property_read_bool(np, + "qcom,split-link-enabled"); + + if (pinfo->split_link_enabled) { + rc = of_property_read_u32(np, + "qcom,sublinks-count", &tmp); + /* default num of sublink is 1*/ + pinfo->mipi.num_of_sublinks = (!rc ? tmp : 1); + + rc = of_property_read_u32(np, + "qcom,lanes-per-sublink", &tmp); + /* default num of lanes per sublink is 1 */ + pinfo->mipi.lanes_per_sublink = (!rc ? tmp : 1); + } + + pr_info("%s: enable %d sublinks-count %d lanes per sublink %d\n", + __func__, pinfo->split_link_enabled, + pinfo->mipi.num_of_sublinks, + pinfo->mipi.lanes_per_sublink); + return 0; +} + static int mdss_dsi_parse_dsc_version(struct device_node *np, struct mdss_panel_timing *timing) { @@ -2734,9 +2808,15 @@ static int mdss_panel_parse_dt(struct device_node *np, pinfo->mipi.data_lane3 = of_property_read_bool(np, "qcom,mdss-dsi-lane-3-state"); + /* parse split link properties */ + rc = mdss_dsi_parse_split_link_settings(np, pinfo); + if (rc) + return rc; + rc = mdss_panel_parse_display_timings(np, &ctrl_pdata->panel_data); if (rc) return rc; + rc = mdss_dsi_parse_hdr_settings(np, pinfo); if (rc) return rc; diff --git a/drivers/video/fbdev/msm/mdss_dsi_status.c b/drivers/video/fbdev/msm/mdss_dsi_status.c index 4208c2c43efb..0f24f66dbcc6 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_status.c +++ b/drivers/video/fbdev/msm/mdss_dsi_status.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -39,6 +39,35 @@ static uint32_t interval = STATUS_CHECK_INTERVAL_MS; static int32_t dsi_status_disable = DSI_STATUS_CHECK_INIT; struct dsi_status_data *pstatus_data; +int mdss_dsi_check_panel_status(struct mdss_dsi_ctrl_pdata *ctrl, void *arg) +{ + struct mdss_mdp_ctl *ctl = NULL; + struct msm_fb_data_type *mfd = arg; + int ret = 0; + + if (!mfd) + return -EINVAL; + + ctl = mfd_to_ctl(mfd); + + if (!ctl || !ctrl) + return -EINVAL; + + mutex_lock(&ctl->offlock); + /* + * if check_status method is not defined + * then no need to fail this function, + * instead return a positive value. + */ + if (ctrl->check_status) + ret = ctrl->check_status(ctrl); + else + ret = 1; + mutex_unlock(&ctl->offlock); + + return ret; +} + /* * check_dsi_ctrl_status() - Reads MFD structure and * calls platform specific DSI ctrl Status function. diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index fc47de7692e7..698c5633cf6a 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -374,7 +374,8 @@ static int mdss_fb_get_panel_xres(struct mdss_panel_info *pinfo) xres = pinfo->xres; if (pdata->next && pdata->next->active) xres += mdss_fb_get_panel_xres(&pdata->next->panel_info); - + if (pinfo->split_link_enabled) + xres = xres * pinfo->mipi.num_of_sublinks; return xres; } @@ -652,7 +653,7 @@ static ssize_t mdss_fb_get_panel_status(struct device *dev, ret = scnprintf(buf, PAGE_SIZE, "panel_status=%s\n", "suspend"); } else { panel_status = mdss_fb_send_panel_event(mfd, - MDSS_EVENT_DSI_PANEL_STATUS, NULL); + MDSS_EVENT_DSI_PANEL_STATUS, mfd); ret = scnprintf(buf, PAGE_SIZE, "panel_status=%s\n", panel_status > 0 ? "alive" : "dead"); } @@ -1595,13 +1596,30 @@ static int mdss_fb_resume(struct platform_device *pdev) static int mdss_fb_pm_suspend(struct device *dev) { struct msm_fb_data_type *mfd = dev_get_drvdata(dev); + int rc = 0; if (!mfd) return -ENODEV; dev_dbg(dev, "display pm suspend\n"); - return mdss_fb_suspend_sub(mfd); + rc = mdss_fb_suspend_sub(mfd); + + /* + * Call MDSS footswitch control to ensure GDSC is + * off after pm suspend call. There are cases when + * mdss runtime call doesn't trigger even when clock + * ref count is zero after fb pm suspend. + */ + if (!rc) { + if (mfd->mdp.footswitch_ctrl) + mfd->mdp.footswitch_ctrl(false); + } else { + pr_err("fb pm suspend failed, rc: %d\n", rc); + } + + return rc; + } static int mdss_fb_pm_resume(struct device *dev) @@ -1621,6 +1639,9 @@ static int mdss_fb_pm_resume(struct device *dev) pm_runtime_set_suspended(dev); pm_runtime_enable(dev); + if (mfd->mdp.footswitch_ctrl) + mfd->mdp.footswitch_ctrl(true); + return mdss_fb_resume_sub(mfd); } #endif diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h index f046ff08cbf7..518c24810acd 100644 --- a/drivers/video/fbdev/msm/mdss_fb.h +++ b/drivers/video/fbdev/msm/mdss_fb.h @@ -232,6 +232,7 @@ struct msm_mdp_interface { int (*configure_panel)(struct msm_fb_data_type *mfd, int mode, int dest_ctrl); int (*input_event_handler)(struct msm_fb_data_type *mfd); + void (*footswitch_ctrl)(bool on); int (*pp_release_fnc)(struct msm_fb_data_type *mfd); void *private1; }; @@ -392,6 +393,12 @@ static inline void mdss_fb_update_notify_update(struct msm_fb_data_type *mfd) } } +/* Function returns true for split link */ +static inline bool is_panel_split_link(struct msm_fb_data_type *mfd) +{ + return mfd && mfd->panel_info && mfd->panel_info->split_link_enabled; +} + /* Function returns true for either any kind of dual display */ static inline bool is_panel_split(struct msm_fb_data_type *mfd) { diff --git a/drivers/video/fbdev/msm/mdss_hdcp_1x.c b/drivers/video/fbdev/msm/mdss_hdcp_1x.c index 834726e84bda..2dc9c8f96c5b 100644 --- a/drivers/video/fbdev/msm/mdss_hdcp_1x.c +++ b/drivers/video/fbdev/msm/mdss_hdcp_1x.c @@ -1381,7 +1381,8 @@ int hdcp_1x_authenticate(void *input) flush_delayed_work(&hdcp->hdcp_auth_work); - if (!hdcp_1x_state(HDCP_STATE_INACTIVE)) { + if (!hdcp_1x_state(HDCP_STATE_INACTIVE) && + !hdcp_1x_state(HDCP_STATE_AUTH_FAIL)) { pr_err("invalid state\n"); return -EINVAL; } @@ -1443,7 +1444,6 @@ int hdcp_1x_reauthenticate(void *input) DSS_REG_W(io, reg_set->reset, reg & ~reg_set->reset_bit); - hdcp->hdcp_state = HDCP_STATE_INACTIVE; hdcp_1x_authenticate(hdcp); return ret; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c index 37c4be6135aa..599f6cb44c63 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c @@ -2604,6 +2604,11 @@ void hdmi_edid_set_video_resolution(void *input, u32 resolution, bool reset) return; } + if (resolution == HDMI_VFRMT_UNKNOWN) { + pr_debug("%s: Default video resolution not set\n", __func__); + return; + } + edid_ctrl->video_resolution = resolution; if (reset) { diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index 171f44815430..a645a3495593 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -233,7 +233,6 @@ static struct mdss_mdp_irq mdp_irq_map[] = { static struct intr_callback *mdp_intr_cb; -static void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on); static int mdss_mdp_parse_dt(struct platform_device *pdev); static int mdss_mdp_parse_dt_pipe(struct platform_device *pdev); static int mdss_mdp_parse_dt_mixer(struct platform_device *pdev); @@ -5172,7 +5171,7 @@ static void mdss_mdp_notify_idle_pc(struct mdss_data_type *mdata) * active (but likely in an idle state), the vote for the CX and the batfet * rails should not be released. */ -static void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on) +void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on) { int ret; int active_cnt = 0; diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 2fd047edd3e8..a2139f495f52 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -142,6 +142,25 @@ #define BITS_TO_BYTES(x) DIV_ROUND_UP(x, BITS_PER_BYTE) +#define PP_PROGRAM_PA 0x1 +#define PP_PROGRAM_PCC 0x2 +#define PP_PROGRAM_IGC 0x4 +#define PP_PROGRAM_ARGC 0x8 +#define PP_PROGRAM_HIST 0x10 +#define PP_PROGRAM_DITHER 0x20 +#define PP_PROGRAM_GAMUT 0x40 +#define PP_PROGRAM_PGC 0x100 +#define PP_PROGRAM_PA_DITHER 0x400 +#define PP_PROGRAM_AD 0x800 + +#define PP_NORMAL_PROGRAM_MASK (PP_PROGRAM_AD | PP_PROGRAM_PCC | \ + PP_PROGRAM_HIST) +#define PP_DEFER_PROGRAM_MASK (PP_PROGRAM_IGC | PP_PROGRAM_PGC | \ + PP_PROGRAM_ARGC | PP_PROGRAM_GAMUT | \ + PP_PROGRAM_PA | PP_PROGRAM_DITHER | \ + PP_PROGRAM_PA_DITHER) +#define PP_PROGRAM_ALL (PP_NORMAL_PROGRAM_MASK | PP_DEFER_PROGRAM_MASK) + enum mdss_mdp_perf_state_type { PERF_SW_COMMIT_STATE = 0, PERF_HW_MDP_STATE, @@ -773,6 +792,12 @@ struct mdss_pipe_pp_res { void *hist_lut_cfg_payload; }; +struct mdss_mdp_pp_program_info { + u32 pp_program_mask; + u32 pp_opmode_left; + u32 pp_opmode_right; +}; + struct mdss_mdp_pipe_smp_map { DECLARE_BITMAP(reserved, MAX_DRV_SUP_MMB_BLKS); DECLARE_BITMAP(allocated, MAX_DRV_SUP_MMB_BLKS); @@ -1267,6 +1292,8 @@ static inline u32 get_panel_width(struct mdss_mdp_ctl *ctl) width = get_panel_xres(&ctl->panel_data->panel_info); if (ctl->panel_data->next && is_pingpong_split(ctl->mfd)) width += get_panel_xres(&ctl->panel_data->next->panel_info); + else if (is_panel_split_link(ctl->mfd)) + width *= (ctl->panel_data->panel_info.mipi.num_of_sublinks); return width; } @@ -1638,6 +1665,7 @@ int mdss_mdp_set_intr_callback_nosync(u32 intr_type, u32 intf_num, void (*fnc_ptr)(void *), void *arg); u32 mdss_mdp_get_irq_mask(u32 intr_type, u32 intf_num); +void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on); void mdss_mdp_footswitch_ctrl_splash(int on); void mdss_mdp_batfet_ctrl(struct mdss_data_type *mdata, int enable); void mdss_mdp_set_clk_rate(unsigned long min_clk_rate, bool locked); @@ -1800,7 +1828,8 @@ int mdss_mdp_pp_resume(struct msm_fb_data_type *mfd); void mdss_mdp_pp_dest_scaler_resume(struct mdss_mdp_ctl *ctl); int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl); -int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl); +int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl, + struct mdss_mdp_pp_program_info *info); int mdss_mdp_pipe_pp_setup(struct mdss_mdp_pipe *pipe, u32 *op); void mdss_mdp_pipe_pp_clear(struct mdss_mdp_pipe *pipe); int mdss_mdp_pipe_sspp_setup(struct mdss_mdp_pipe *pipe, u32 *op); diff --git a/drivers/video/fbdev/msm/mdss_mdp_cdm.c b/drivers/video/fbdev/msm/mdss_mdp_cdm.c index f1d1bdd301e3..10928e6bceaa 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_cdm.c +++ b/drivers/video/fbdev/msm/mdss_mdp_cdm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -377,6 +377,17 @@ int mdss_mdp_cdm_destroy(struct mdss_mdp_cdm *cdm) return -EINVAL; } + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); + mutex_lock(&cdm->lock); + /* Disable HDMI packer */ + writel_relaxed(0x0, cdm->base + MDSS_MDP_REG_CDM_HDMI_PACK_OP_MODE); + + /* Put CDM in bypass */ + writel_relaxed(0x0, cdm->mdata->mdp_base + MDSS_MDP_MDP_OUT_CTL_0); + + mutex_unlock(&cdm->lock); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); + kref_put(&cdm->kref, mdss_mdp_cdm_free); return rc; diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index 49348e5e16a9..1f29a9f86e24 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -709,6 +709,8 @@ int mdss_mdp_get_panel_params(struct mdss_mdp_pipe *pipe, *h_total += mdss_panel_get_htotal( &mixer->ctl->panel_data->next->panel_info, false); + else if (is_panel_split_link(mixer->ctl->mfd)) + *h_total *= pinfo->mipi.num_of_sublinks; } else { *v_total = mixer->height; *xres = mixer->width; @@ -4090,6 +4092,13 @@ static void mdss_mdp_ctl_split_display_enable(int enable, if (is_pingpong_split(main_ctl->mfd)) lower |= BIT(2); upper = lower; + + /* + * align command mode stream0 output for + * intferace 1 and 2 to start of frame. + */ + if (main_ctl->mdata->mdp_rev >= MDSS_MDP_HW_REV_320) + lower |= BIT(12); } else { /* interface controlling sw trigger (video mode) */ if (main_ctl->intf_num == MDSS_MDP_INTF2) { @@ -4101,6 +4110,9 @@ static void mdss_mdp_ctl_split_display_enable(int enable, } } } + + if (is_panel_split_link(main_ctl->mfd)) + upper = lower = 0; writel_relaxed(upper, main_ctl->mdata->mdp_base + MDSS_MDP_REG_SPLIT_DISPLAY_UPPER_PIPE_CTRL); writel_relaxed(lower, main_ctl->mdata->mdp_base + @@ -4269,7 +4281,8 @@ void mdss_mdp_ctl_restore(bool locked) if (sctl) { mdss_mdp_ctl_restore_sub(sctl); mdss_mdp_ctl_split_display_enable(1, ctl, sctl); - } else if (is_pingpong_split(ctl->mfd)) { + } else if (is_pingpong_split(ctl->mfd) || + is_panel_split_link(ctl->mfd)) { mdss_mdp_ctl_pp_split_display_enable(1, ctl); } @@ -4396,6 +4409,8 @@ int mdss_mdp_ctl_start(struct mdss_mdp_ctl *ctl, bool handoff) } else if (is_pingpong_split(ctl->mfd)) { ctl->slave_intf_num = (ctl->intf_num + 1); mdss_mdp_ctl_pp_split_display_enable(true, ctl); + } else if (is_panel_split_link(ctl->mfd)) { + mdss_mdp_ctl_pp_split_display_enable(true, ctl); } } @@ -4570,7 +4585,7 @@ void mdss_mdp_check_ctl_reset_status(struct mdss_mdp_ctl *ctl) 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"); } } @@ -5737,6 +5752,9 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, bool is_bw_released, split_lm_valid; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); u32 ctl_flush_bits = 0, sctl_flush_bits = 0; + /* Must initialize pp_program_info */ + struct mdss_mdp_pp_program_info pp_program_info = { + PP_PROGRAM_ALL, 0, 0}; if (!ctl) { pr_err("display function not set\n"); @@ -5849,9 +5867,13 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, mdss_mdp_ctl_split_display_enable(split_lm_valid, ctl, sctl); ATRACE_BEGIN("postproc_programming"); - if (ctl->is_video_mode && ctl->mfd && ctl->mfd->dcm_state != DTM_ENTER) + if (ctl->mfd && ctl->mfd->dcm_state != DTM_ENTER) { /* postprocessing setup, including dspp */ - mdss_mdp_pp_setup_locked(ctl); + if (!ctl->is_video_mode) + pp_program_info.pp_program_mask = + PP_NORMAL_PROGRAM_MASK; + mdss_mdp_pp_setup_locked(ctl, &pp_program_info); + } if (sctl) { if (ctl->split_flush_en) { @@ -5907,11 +5929,17 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, } /* Moved pp programming to post ping pong */ + ATRACE_BEGIN("postproc_programming_deferred"); if (!ctl->is_video_mode && ctl->mfd && ctl->mfd->dcm_state != DTM_ENTER) { /* postprocessing setup, including dspp */ mutex_lock(&ctl->flush_lock); - mdss_mdp_pp_setup_locked(ctl); + pp_program_info.pp_program_mask = PP_DEFER_PROGRAM_MASK; + /* + * pp_program_info should not be modified beween normal and + * deferred stage calls. + */ + mdss_mdp_pp_setup_locked(ctl, &pp_program_info); if (sctl) { if (ctl->split_flush_en) { ctl->flush_bits |= sctl->flush_bits; @@ -5924,6 +5952,7 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, ctl_flush_bits |= ctl->flush_bits; mutex_unlock(&ctl->flush_lock); } + ATRACE_END("postproc_programming_deferred"); /* * if serialize_wait4pp is false then roi_bkup used in wait4pingpong * will be of previous frame as expected. @@ -6056,8 +6085,9 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, /* update backlight in commit */ if (mdss_mdp_handle_backlight_extn(ctl)) { - if (!IS_CALIB_MODE_BL(ctl->mfd) && (!ctl->mfd->ext_bl_ctrl || - !ctl->mfd->bl_level)) { + if (ctl->mfd && !IS_CALIB_MODE_BL(ctl->mfd) && + (!ctl->mfd->ext_bl_ctrl || + !ctl->mfd->bl_level)) { mutex_lock(&ctl->mfd->bl_lock); mdss_fb_set_backlight(ctl->mfd, ctl->mfd->bl_extn_level); diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h index 7d495232c198..d9e2b042bfc3 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h +++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h @@ -850,4 +850,8 @@ enum mdss_mdp_pingpong_index { #define MDSS_MDP_REG_TRAFFIC_SHAPER_WR_CLIENT(num) (0x060 + (num * 4)) #define MDSS_MDP_REG_TRAFFIC_SHAPER_FIXPOINT_FACTOR 4 +#define MDSS_MDP_REG_SPLIT_LINK 0x00060 +#define MDSS_MDP_REG_SPLIT_LINK_LEFT_LINK_EN BIT(1) +#define MDSS_MDP_REG_SPLIT_LINK_RIGHT_LINK_EN BIT(2) + #endif diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c index c9ce56fb96a4..bdbec0b4e721 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c @@ -2148,12 +2148,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); } ctx->pp_timeout_report_cnt++; @@ -3352,8 +3354,18 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state) * mode. */ send_panel_events = true; - if (mdss_panel_is_power_on_ulp(panel_power_state)) + if (mdss_panel_is_power_on_ulp(panel_power_state)) { turn_off_clocks = true; + } else if (atomic_read(&ctx->koff_cnt)) { + /* + * Transition from interactive to low power + * Wait for kickoffs to finish + */ + MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt)); + mdss_mdp_cmd_wait4pingpong(ctl, NULL); + if (sctl) + mdss_mdp_cmd_wait4pingpong(sctl, NULL); + } } else { /* Transitions between low power and ultra low power */ if (mdss_panel_is_power_on_ulp(panel_power_state)) { diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index a3511a1a07ef..13c70822e266 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -1071,6 +1071,13 @@ static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl, int panel_power_state) { int intfs_num, ret = 0; + if (ctl->cdm) { + if (!mdss_mdp_cdm_destroy(ctl->cdm)) + mdss_mdp_ctl_write(ctl, + MDSS_MDP_REG_CTL_FLUSH, BIT(26)); + ctl->cdm = NULL; + } + intfs_num = ctl->intf_num - MDSS_MDP_INTF0; ret = mdss_mdp_video_intfs_stop(ctl, ctl->panel_data, intfs_num); if (IS_ERR_VALUE(ret)) { @@ -1083,10 +1090,6 @@ static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl, int panel_power_state) mdss_mdp_ctl_reset(ctl, false); ctl->intf_ctx[MASTER_CTX] = NULL; - if (ctl->cdm) { - mdss_mdp_cdm_destroy(ctl->cdm); - ctl->cdm = NULL; - } return 0; } @@ -1384,7 +1387,7 @@ static int mdss_mdp_video_dfps_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; @@ -1682,6 +1685,16 @@ static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg) mdss_bus_bandwidth_ctrl(true); + /* configure the split link to both sublinks */ + if (is_panel_split_link(ctl->mfd)) { + mdp_video_write(ctx, MDSS_MDP_REG_SPLIT_LINK, 0x3); + /* + * ensure split link register is written before + * enabling timegen + */ + wmb(); + } + mdp_video_write(ctx, MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 1); wmb(); @@ -1796,7 +1809,9 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl, mdss_mdp_video_timegen_flush(ctl, sctx); /* wait for 1 VSYNC for the pipe to be unstaged */ - msleep(20); + mdss_mdp_video_wait4comp(ctl, NULL); + if (sctl) + mdss_mdp_video_wait4comp(sctl, NULL); ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_FINISH, NULL, diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c b/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c index 87ed56028edd..9b63499e64b0 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c @@ -792,7 +792,9 @@ static int mdss_mdp_writeback_stop(struct mdss_mdp_ctl *ctl, } if (ctl->cdm) { - mdss_mdp_cdm_destroy(ctl->cdm); + if (!mdss_mdp_cdm_destroy(ctl->cdm)) + mdss_mdp_ctl_write(ctl, + MDSS_MDP_REG_CTL_FLUSH, BIT(26)); ctl->cdm = NULL; } return 0; diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 8eb12d764be3..8c612e2b83fb 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -2601,6 +2601,18 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd, mdss_mdp_splash_cleanup(mfd, true); /* + * Wait for pingpong done only during resume for + * command mode panels. Ensure that one commit is + * sent before kickoff completes so that backlight + * update happens after it. + */ + if (mdss_fb_is_power_off(mfd) && + mfd->panel_info->type == MIPI_CMD_PANEL) { + pr_debug("wait for pp done after resume for cmd mode\n"); + mdss_mdp_display_wait4pingpong(ctl, true); + } + + /* * Configure Timing Engine, if new fps was set. * We need to do this after the wait for vsync * to guarantee that mdp flush bit and dsi flush @@ -6279,6 +6291,13 @@ int mdss_mdp_input_event_handler(struct msm_fb_data_type *mfd) return rc; } +void mdss_mdp_footswitch_ctrl_handler(bool on) +{ + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + + mdss_mdp_footswitch_ctrl(mdata, on); +} + int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) { struct device *dev = mfd->fbi->dev; @@ -6321,6 +6340,14 @@ 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; + /* + * Register footswitch control only for primary fb pm + * suspend/resume calls. + */ + if (mfd->panel_info->is_prim_panel) + mdp5_interface->footswitch_ctrl = + mdss_mdp_footswitch_ctrl_handler; + if (mfd->panel_info->type == WRITEBACK_PANEL) { mdp5_interface->atomic_validate = mdss_mdp_layer_atomic_validate_wfd; diff --git a/drivers/video/fbdev/msm/mdss_mdp_pipe.c b/drivers/video/fbdev/msm/mdss_mdp_pipe.c index 8d7bd60318ad..724913f376a7 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pipe.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pipe.c @@ -2292,6 +2292,9 @@ static int mdss_mdp_src_addr_setup(struct mdss_mdp_pipe *pipe, mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC3_ADDR, addr[2]); } + MDSS_XLOG(pipe->num, pipe->multirect.num, pipe->mixer_left->num, + pipe->play_cnt, addr[0], addr[1], addr[2], addr[3]); + return 0; } @@ -2734,9 +2737,6 @@ int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe, goto update_nobuf; } - MDSS_XLOG(pipe->num, pipe->multirect.num, pipe->mixer_left->num, - pipe->play_cnt, 0x222); - if (params_changed) { pipe->params_changed = 0; diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c index f10d4fb60f52..f128f82fab04 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c @@ -2322,7 +2322,9 @@ static void pp_dspp_opmode_config(struct mdss_mdp_ctl *ctl, u32 num, *opmode |= MDSS_MDP_DSPP_OP_ARGC_LUT_EN; } -static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) +static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer, + u32 pp_program_mask, int *op_mode) + { u32 ad_flags, flags, dspp_num, opmode = 0, ad_bypass; struct mdp_pgc_lut_data *pgc_config; @@ -2338,6 +2340,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) u32 mixer_id[MDSS_MDP_INTF_MAX_LAYERMIXER]; int side; + opmode = *op_mode; + if (!mixer || !mixer->ctl || !mixer->ctl->mdata) return -EINVAL; ctl = mixer->ctl; @@ -2358,19 +2362,23 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) } mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); - if ((mdata->pp_block_off.dspp_gamut_off != U32_MAX) && - (pp_driver_ops.gamut_clk_gate_en)) - pp_driver_ops.gamut_clk_gate_en(base + + if (pp_program_mask & PP_PROGRAM_GAMUT) { + if ((mdata->pp_block_off.dspp_gamut_off != U32_MAX) && + (pp_driver_ops.gamut_clk_gate_en)) + pp_driver_ops.gamut_clk_gate_en(base + mdata->pp_block_off.dspp_gamut_off); - + } if (disp_num < MDSS_BLOCK_DISP_NUM) { pp_sts = &mdss_pp_res->pp_disp_sts[disp_num]; pp_sts->side_sts = side; - ret = pp_hist_setup(&opmode, MDSS_PP_DSPP_CFG | dspp_num, mixer, - pp_sts); - if (ret) - goto dspp_exit; + if (pp_program_mask & PP_PROGRAM_HIST) { + ret = pp_hist_setup(&opmode, + MDSS_PP_DSPP_CFG | dspp_num, mixer, + pp_sts); + if (ret) + goto dspp_exit; + } flags = mdss_pp_res->pp_disp_flags[disp_num]; } else { @@ -2391,7 +2399,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) if ((!flags) && (!(opmode)) && (!ad_flags)) goto dspp_exit; - if (flags & PP_FLAGS_DIRTY_PA) { + if ((flags & PP_FLAGS_DIRTY_PA) && + (pp_program_mask & PP_PROGRAM_PA)) { if (!pp_ops[PA].pp_set_config) { if (mdata->mdp_rev >= MDSS_MDP_HW_REV_103) { pa_v2_cfg_data = @@ -2412,7 +2421,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) DSPP); } } - if (flags & PP_FLAGS_DIRTY_PCC) { + if ((flags & PP_FLAGS_DIRTY_PCC) && + (pp_program_mask & PP_PROGRAM_PCC)) { if (!pp_ops[PCC].pp_set_config) pp_pcc_config(flags, base + MDSS_MDP_REG_DSPP_PCC_BASE, pp_sts, @@ -2429,7 +2439,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) } } - if (flags & PP_FLAGS_DIRTY_IGC) { + if ((flags & PP_FLAGS_DIRTY_IGC) && + (pp_program_mask & PP_PROGRAM_IGC)) { if (!pp_ops[IGC].pp_set_config) { pp_igc_config(flags, mdata->mdp_base + MDSS_MDP_REG_IGC_DSPP_BASE, @@ -2449,7 +2460,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) DSPP); } - if (flags & PP_FLAGS_DIRTY_ENHIST) { + if ((flags & PP_FLAGS_DIRTY_ENHIST) && + (pp_program_mask & PP_PROGRAM_HIST)) { if (!pp_ops[HIST_LUT].pp_set_config) { pp_enhist_config(flags, base + MDSS_MDP_REG_DSPP_HIST_LUT_BASE, @@ -2473,7 +2485,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) } } - if (flags & PP_FLAGS_DIRTY_DITHER) { + if ((flags & PP_FLAGS_DIRTY_DITHER) && + (pp_program_mask & PP_PROGRAM_DITHER)) { if (!pp_ops[DITHER].pp_set_config && addr) { pp_dither_config(addr, pp_sts, &mdss_pp_res->dither_disp_cfg[disp_num]); @@ -2483,7 +2496,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) &mdss_pp_res->dither_disp_cfg[disp_num], DSPP); } } - if (flags & PP_FLAGS_DIRTY_GAMUT) { + if ((flags & PP_FLAGS_DIRTY_GAMUT) && + (pp_program_mask & PP_PROGRAM_GAMUT)) { if (!pp_ops[GAMUT].pp_set_config) { pp_gamut_config(&mdss_pp_res->gamut_disp_cfg[disp_num], base, pp_sts); @@ -2500,7 +2514,8 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) } } - if (flags & PP_FLAGS_DIRTY_PGC) { + if ((flags & PP_FLAGS_DIRTY_PGC) && + (pp_program_mask & PP_PROGRAM_PGC)) { pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num]; if (pp_ops[GC].pp_set_config) { if (mdata->pp_block_off.dspp_pgc_off == U32_MAX) { @@ -2526,6 +2541,7 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) } } if (flags & PP_FLAGS_DIRTY_PA_DITHER && + (pp_program_mask & PP_PROGRAM_PA_DITHER) && pp_ops[PA_DITHER].pp_set_config) { pp_ops[PA_DITHER].pp_set_config(base, pp_sts, &mdss_pp_res->pa_dither_cfg[disp_num], @@ -2536,7 +2552,7 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) pp_dspp_opmode_config(ctl, dspp_num, pp_sts, mdata->mdp_rev, &opmode); - if (ad_hw) { + if (ad_hw && (pp_program_mask & PP_PROGRAM_AD)) { mutex_lock(&ad->lock); ad_flags = ad->reg_sts; if (ad_flags & PP_AD_STS_DIRTY_DATA) @@ -2566,6 +2582,9 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) ctl->flush_bits |= BIT(13 + dspp_num); wmb(); + + *op_mode = opmode; + dspp_exit: mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); return ret; @@ -2684,6 +2703,8 @@ void mdss_mdp_pp_dest_scaler_resume(struct mdss_mdp_ctl *ctl) int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl) { int ret = 0; + struct mdss_mdp_pp_program_info pp_program_info = { + PP_PROGRAM_ALL, 0, 0}; if ((!ctl->mfd) || (!mdss_pp_res)) return -EINVAL; @@ -2695,14 +2716,15 @@ int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl) ret = -EPERM; goto error; } - ret = mdss_mdp_pp_setup_locked(ctl); + ret = mdss_mdp_pp_setup_locked(ctl, &pp_program_info); error: mutex_unlock(&ctl->lock); return ret; } -int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl) +int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl, + struct mdss_mdp_pp_program_info *info) { struct mdss_data_type *mdata; int ret = 0, i; @@ -2715,6 +2737,16 @@ int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl) bool valid_ad_panel = true; if ((!ctl) || (!ctl->mfd) || (!mdss_pp_res) || (!ctl->mdata)) return -EINVAL; + if (!info) { + pr_err("pp_program_info is NULL"); + return -EINVAL; + } + if (!(info->pp_program_mask == PP_NORMAL_PROGRAM_MASK || + info->pp_program_mask == PP_DEFER_PROGRAM_MASK || + info->pp_program_mask == PP_PROGRAM_ALL)) { + pr_err("Invalid pp program mask : %x ", info->pp_program_mask); + return -EINVAL; + } mdata = ctl->mdata; /* treat fb_num the same as block logical id*/ @@ -2748,7 +2780,11 @@ int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl) mutex_lock(&mdss_pp_mutex); - flags = mdss_pp_res->pp_disp_flags[disp_num]; + if (disp_num < MDSS_BLOCK_DISP_NUM) + flags = mdss_pp_res->pp_disp_flags[disp_num]; + else + flags = 0; + if (pp_ops[PA].pp_set_config) pa_v2_flags = mdss_pp_res->pa_v2_disp_cfg[disp_num].flags; else @@ -2759,50 +2795,72 @@ int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl) * increase the register bus bandwidth to maximum frequency * in order to speed up the register reprogramming. */ - max_bw_needed = (IS_PP_RESUME_COMMIT(flags) && - (IS_PP_LUT_DIRTY(flags) || - IS_SIX_ZONE_DIRTY(flags, pa_v2_flags))); - if (mdata->pp_reg_bus_clt && max_bw_needed) { - ret = mdss_update_reg_bus_vote(mdata->pp_reg_bus_clt, - VOTE_INDEX_HIGH); - if (ret) - pr_err("Updated reg_bus_scale failed, ret = %d", ret); + if (info->pp_program_mask & PP_DEFER_PROGRAM_MASK) { + max_bw_needed = (IS_PP_RESUME_COMMIT(flags) && + (IS_PP_LUT_DIRTY(flags) || + IS_SIX_ZONE_DIRTY(flags, pa_v2_flags))); + if (mdata->pp_reg_bus_clt && max_bw_needed) { + ret = mdss_update_reg_bus_vote(mdata->pp_reg_bus_clt, + VOTE_INDEX_HIGH); + if (ret) + pr_err("Updated reg_bus_scale failed, ret = %d", + ret); + } } if (ctl->mixer_left) { - pp_mixer_setup(ctl->mixer_left); - pp_dspp_setup(disp_num, ctl->mixer_left); - pp_ppb_setup(ctl->mixer_left); + if (info->pp_program_mask & PP_DEFER_PROGRAM_MASK) { + pp_mixer_setup(ctl->mixer_left); + pp_dspp_setup(disp_num, ctl->mixer_left, + info->pp_program_mask, &info->pp_opmode_left); + pp_ppb_setup(ctl->mixer_left); + } else { + pp_dspp_setup(disp_num, ctl->mixer_left, + info->pp_program_mask, &info->pp_opmode_left); + } } if (ctl->mixer_right) { - pp_mixer_setup(ctl->mixer_right); - pp_dspp_setup(disp_num, ctl->mixer_right); - pp_ppb_setup(ctl->mixer_right); + if (info->pp_program_mask & PP_DEFER_PROGRAM_MASK) { + pp_mixer_setup(ctl->mixer_right); + pp_dspp_setup(disp_num, ctl->mixer_right, + info->pp_program_mask, &info->pp_opmode_right); + pp_ppb_setup(ctl->mixer_right); + } else { + pp_dspp_setup(disp_num, ctl->mixer_right, + info->pp_program_mask, &info->pp_opmode_right); + } } - if (valid_mixers && (mixer_cnt <= mdata->nmax_concurrent_ad_hw) && - valid_ad_panel) { - ret = mdss_mdp_ad_ipc_reset(ctl->mfd); - if (ret < 0) - pr_warn("ad_setup(disp%d) returns %d\n", disp_num, ret); + if (info->pp_program_mask & PP_PROGRAM_AD) { + if (valid_mixers && + (mixer_cnt <= mdata->nmax_concurrent_ad_hw) && + valid_ad_panel) { + ret = mdss_mdp_ad_ipc_reset(ctl->mfd); + if (ret < 0) + pr_warn("ad_setup(disp%d) returns %d\n", + disp_num, ret); + } } - /* clear dirty flag */ - if (disp_num < MDSS_BLOCK_DISP_NUM) { - mdss_pp_res->pp_disp_flags[disp_num] = 0; - if (disp_num < mdata->nad_cfgs) - mdata->ad_cfgs[disp_num].reg_sts = 0; - } + if (info->pp_program_mask & PP_DEFER_PROGRAM_MASK) { + /* clear dirty flag */ + if (disp_num < MDSS_BLOCK_DISP_NUM) { + mdss_pp_res->pp_disp_flags[disp_num] = 0; + if (disp_num < mdata->nad_cfgs) + mdata->ad_cfgs[disp_num].reg_sts = 0; + } - if (mdata->pp_reg_bus_clt && max_bw_needed) { - ret = mdss_update_reg_bus_vote(mdata->pp_reg_bus_clt, - VOTE_INDEX_DISABLE); - if (ret) - pr_err("Updated reg_bus_scale failed, ret = %d", ret); + if (mdata->pp_reg_bus_clt && max_bw_needed) { + ret = mdss_update_reg_bus_vote(mdata->pp_reg_bus_clt, + VOTE_INDEX_DISABLE); + if (ret) + pr_err("Updated reg_bus_scale failed, ret = %d", + ret); + } + if (IS_PP_RESUME_COMMIT(flags)) + mdss_pp_res->pp_disp_flags[disp_num] &= + ~PP_FLAGS_RESUME_COMMIT; } - if (IS_PP_RESUME_COMMIT(flags)) - mdss_pp_res->pp_disp_flags[disp_num] &= - ~PP_FLAGS_RESUME_COMMIT; mutex_unlock(&mdss_pp_mutex); exit: return ret; diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index 92413e078244..fa1df94976f9 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -533,6 +533,8 @@ struct mipi_panel_info { char lp11_init; u32 init_delay; u32 post_init_delay; + u32 num_of_sublinks; + u32 lanes_per_sublink; }; struct edp_panel_info { @@ -847,6 +849,7 @@ struct mdss_panel_info { bool is_lpm_mode; bool is_split_display; /* two DSIs in one display, pp split or not */ bool use_pingpong_split; + bool split_link_enabled; /* * index[0] = left layer mixer, value of 0 not valid diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c index 62e25500060e..1b4765837c61 100644 --- a/drivers/video/fbdev/msm/mdss_smmu.c +++ b/drivers/video/fbdev/msm/mdss_smmu.c @@ -600,24 +600,30 @@ int mdss_smmu_fault_handler(struct iommu_domain *domain, struct device *dev, (struct mdss_smmu_client *)user_data; u32 fsynr1, mid, i; - if (!mdss_smmu || !mdss_smmu->mmu_base) + if (!mdss_smmu) goto end; - fsynr1 = readl_relaxed(mdss_smmu->mmu_base + SMMU_CBN_FSYNR1); - mid = fsynr1 & 0xff; - pr_err("mdss_smmu: iova:0x%lx flags:0x%x fsynr1: 0x%x mid: 0x%x\n", - iova, flags, fsynr1, mid); + if (mdss_smmu->mmu_base) { + fsynr1 = readl_relaxed(mdss_smmu->mmu_base + SMMU_CBN_FSYNR1); + mid = fsynr1 & 0xff; + pr_err("mdss_smmu: iova:0x%lx flags:0x%x fsynr1: 0x%x mid: 0x%x\n", + iova, flags, fsynr1, mid); - /* get domain id information */ - for (i = 0; i < MDSS_IOMMU_MAX_DOMAIN; i++) { - if (mdss_smmu == mdss_smmu_get_cb(i)) - break; - } + /* get domain id information */ + for (i = 0; i < MDSS_IOMMU_MAX_DOMAIN; i++) { + if (mdss_smmu == mdss_smmu_get_cb(i)) + break; + } - if (i == MDSS_IOMMU_MAX_DOMAIN) - goto end; + if (i == MDSS_IOMMU_MAX_DOMAIN) + goto end; - mdss_mdp_debug_mid(mid); + mdss_mdp_debug_mid(mid); + } else { + pr_err("mdss_smmu: iova:0x%lx flags:0x%x\n", + iova, flags); + MDSS_XLOG_TOUT_HANDLER("mdp"); + } end: return -ENOSYS; } @@ -694,13 +700,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_128K, (SZ_4G - SZ_128K)}; + "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_128K, (SZ_4G - SZ_128K)}; + 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_128K, (SZ_4G - SZ_128K)}; + "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_128K, (SZ_4G - SZ_128K)}; + 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}, @@ -844,14 +850,13 @@ int mdss_smmu_probe(struct platform_device *pdev) mdss_smmu->base.dev = dev; + iommu_set_fault_handler(mdss_smmu->mmu_mapping->domain, + mdss_smmu_fault_handler, mdss_smmu); address = of_get_address_by_name(pdev->dev.of_node, "mmu_cb", 0, 0); if (address) { size = address + 1; mdss_smmu->mmu_base = ioremap(be32_to_cpu(*address), be32_to_cpu(*size)); - if (mdss_smmu->mmu_base) - iommu_set_fault_handler(mdss_smmu->mmu_mapping->domain, - mdss_smmu_fault_handler, mdss_smmu); } else { pr_debug("unable to map context bank base\n"); } diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c index 03e78733d168..c81d45b57f8d 100644 --- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c +++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c @@ -38,16 +38,23 @@ #define MDSS_DSI_DSIPHY_GLBL_TEST_CTRL 0x1d4 #define MDSS_DSI_DSIPHY_CTRL_0 0x170 #define MDSS_DSI_DSIPHY_CTRL_1 0x174 +#define MDSS_DSI_DSIPHY_CMN_CLK_CFG0 0x0010 +#define MDSS_DSI_DSIPHY_CMN_CLK_CFG1 0x0014 + +#define MDSS_DSI_NUM_DATA_LANES 0x04 +#define MDSS_DSI_NUM_CLK_LANES 0x01 #define SW_RESET BIT(2) #define SW_RESET_PLL BIT(0) #define PWRDN_B BIT(7) /* 8996 */ -#define DATALANE_OFFSET_FROM_BASE_8996 0x100 -#define DSIPHY_CMN_PLL_CNTRL 0x0048 +#define DATALANE_OFFSET_FROM_BASE_8996 0x100 +#define CLKLANE_OFFSET_FROM_BASE_8996 0x300 #define DATALANE_SIZE_8996 0x80 +#define CLKLANE_SIZE_8996 0x80 +#define DSIPHY_CMN_PLL_CNTRL 0x0048 #define DSIPHY_CMN_GLBL_TEST_CTRL 0x0018 #define DSIPHY_CMN_CTRL_0 0x001c #define DSIPHY_CMN_CTRL_1 0x0020 @@ -55,6 +62,24 @@ #define DSIPHY_PLL_CLKBUFLR_EN 0x041c #define DSIPHY_PLL_PLL_BANDGAP 0x0508 +#define DSIPHY_LANE_STRENGTH_CTRL_NUM 0x0002 +#define DSIPHY_LANE_STRENGTH_CTRL_OFFSET 0x0004 +#define DSIPHY_LANE_STRENGTH_CTRL_BASE 0x0038 + +#define DSIPHY_LANE_CFG_NUM 0x0004 +#define DSIPHY_LANE_CFG_OFFSET 0x0004 +#define DSIPHY_LANE_CFG_BASE 0x0000 + +#define DSIPHY_LANE_VREG_NUM 0x0001 +#define DSIPHY_LANE_VREG_OFFSET 0x0004 +#define DSIPHY_LANE_VREG_BASE 0x0064 + +#define DSIPHY_LANE_TIMING_CTRL_NUM 0x0008 +#define DSIPHY_LANE_TIMING_CTRL_OFFSET 0x0004 +#define DSIPHY_LANE_TIMING_CTRL_BASE 0x0018 + +#define DSIPHY_LANE_TEST_STR 0x0014 + #define DSIPHY_LANE_STRENGTH_CTRL_1 0x003c #define DSIPHY_LANE_VREG_CNTRL 0x0064 @@ -131,6 +156,8 @@ #define DSIPHY_PLL_RESETSM_CNTRL5 0x043c +#define DSIPHY_CMN_CLK_CFG1_SPLIT_LINK 0x1 + #define PLL_CALC_DATA(addr0, addr1, data0, data1) \ (((data1) << 24) | ((((addr1)/4) & 0xFF) << 16) | \ ((data0) << 8) | (((addr0)/4) & 0xFF)) @@ -911,35 +938,59 @@ static void mdss_dsi_8996_phy_regulator_enable( int j, off, ln, cnt, ln_off; char *ip; void __iomem *base; + struct mdss_panel_info *panel_info; + if (!ctrl) { + pr_warn("%s: null ctrl pdata\n", __func__); + return; + } + + panel_info = &((ctrl->panel_data).panel_info); pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db); - if (pd->regulator_len != 5) { + if (pd->regulator_len != (MDSS_DSI_NUM_DATA_LANES + + MDSS_DSI_NUM_CLK_LANES)) { pr_warn("%s: invalid regulator settings\n", __func__); return; } - /* 4 lanes + clk lane configuration */ - for (ln = 0; ln < 5; ln++) { - /* - * data lane offset frome base: 0x100 - * data lane size: 0x80 - */ - base = ctrl->phy_io.base + - DATALANE_OFFSET_FROM_BASE_8996; - base += (ln * DATALANE_SIZE_8996); /* lane base */ - - /* vreg ctrl, 1 * 5 */ - cnt = 1; + /* + * data lane offset from base: 0x100 + * data lane size: 0x80 + */ + base = ctrl->phy_io.base + DATALANE_OFFSET_FROM_BASE_8996; + /* data lanes configuration */ + for (ln = 0; ln < MDSS_DSI_NUM_DATA_LANES; ln++) { + /* vreg ctrl, 1 * MDSS_DSI_NUM_DATA_LANES */ + cnt = DSIPHY_LANE_VREG_NUM; + off = DSIPHY_LANE_VREG_BASE; ln_off = cnt * ln; ip = &pd->regulator[ln_off]; - off = 0x64; - for (j = 0; j < cnt; j++, off += 4) + for (j = 0; j < cnt; j++) { MIPI_OUTP(base + off, *ip++); + off += DSIPHY_LANE_VREG_OFFSET; + } + base += DATALANE_SIZE_8996; /* next lane */ } - wmb(); /* make sure registers committed */ + /* + * clk lane offset from base: 0x300 + * clk lane size: 0x80 + */ + base = ctrl->phy_io.base + CLKLANE_OFFSET_FROM_BASE_8996; + /* + * clk lane configuration for vreg ctrl + * for split link there are two clock lanes, one + * clock lane per sublink needs to be configured + */ + off = DSIPHY_LANE_VREG_BASE; + ln_off = MDSS_DSI_NUM_DATA_LANES; + ip = &pd->regulator[ln_off]; + MIPI_OUTP(base + off, *ip); + if (panel_info->split_link_enabled) + MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip); + wmb(); /* make sure registers committed */ } static void mdss_dsi_8996_phy_power_off( @@ -948,31 +999,51 @@ static void mdss_dsi_8996_phy_power_off( int ln; void __iomem *base; u32 data; + struct mdss_panel_info *panel_info; + + if (ctrl) { + panel_info = &((ctrl->panel_data).panel_info); + } else { + pr_warn("%s: null ctrl pdata\n", __func__); + return; + } /* Turn off PLL power */ data = MIPI_INP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0); MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0, data & ~BIT(7)); - /* 4 lanes + clk lane configuration */ - for (ln = 0; ln < 5; ln++) { - base = ctrl->phy_io.base + - DATALANE_OFFSET_FROM_BASE_8996; - base += (ln * DATALANE_SIZE_8996); /* lane base */ - + /* data lanes configuration */ + base = ctrl->phy_io.base + DATALANE_OFFSET_FROM_BASE_8996; + for (ln = 0; ln < MDSS_DSI_NUM_DATA_LANES; ln++) { /* turn off phy ldo */ - MIPI_OUTP(base + DSIPHY_LANE_VREG_CNTRL, 0x1c); + MIPI_OUTP(base + DSIPHY_LANE_VREG_BASE, 0x1c); + base += DATALANE_SIZE_8996; /* next lane */ } - MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_LDO_CNTRL, 0x1c); - /* 4 lanes + clk lane configuration */ - for (ln = 0; ln < 5; ln++) { - base = ctrl->phy_io.base + - DATALANE_OFFSET_FROM_BASE_8996; - base += (ln * DATALANE_SIZE_8996); /* lane base */ + /* clk lane configuration */ + base = ctrl->phy_io.base + CLKLANE_OFFSET_FROM_BASE_8996; + /* turn off phy ldo */ + MIPI_OUTP(base + DSIPHY_LANE_VREG_BASE, 0x1c); + if (panel_info->split_link_enabled) + MIPI_OUTP(base + CLKLANE_SIZE_8996 + + DSIPHY_LANE_VREG_BASE, 0x1c); + + MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_LDO_CNTRL, 0x1c); + /* data lanes configuration */ + base = ctrl->phy_io.base + DATALANE_OFFSET_FROM_BASE_8996; + for (ln = 0; ln < MDSS_DSI_NUM_DATA_LANES; ln++) { MIPI_OUTP(base + DSIPHY_LANE_STRENGTH_CTRL_1, 0x0); + base += DATALANE_SIZE_8996; /* next lane */ } + /* clk lane configuration */ + base = ctrl->phy_io.base + CLKLANE_OFFSET_FROM_BASE_8996; + MIPI_OUTP(base + DSIPHY_LANE_STRENGTH_CTRL_1, 0x0); + if (panel_info->split_link_enabled) + MIPI_OUTP(base + CLKLANE_SIZE_8996 + + DSIPHY_LANE_STRENGTH_CTRL_1, 0x0); + wmb(); /* make sure registers committed */ } @@ -1008,22 +1079,46 @@ static void mdss_dsi_8996_phy_power_on( struct mdss_dsi_phy_ctrl *pd; char *ip; u32 data; + struct mdss_panel_info *panel_info; - pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db); + if (ctrl) { + panel_info = &((ctrl->panel_data).panel_info); + } else { + pr_warn("%s: null ctrl pdata\n", __func__); + return; + } - /* 4 lanes + clk lane configuration */ - for (ln = 0; ln < 5; ln++) { - base = ctrl->phy_io.base + - DATALANE_OFFSET_FROM_BASE_8996; - base += (ln * DATALANE_SIZE_8996); /* lane base */ + pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db); - /* strength, 2 * 5 */ - cnt = 2; + /* data lanes configuration */ + base = ctrl->phy_io.base + DATALANE_OFFSET_FROM_BASE_8996; + for (ln = 0; ln < MDSS_DSI_NUM_DATA_LANES; ln++) { + /* strength, 2 * MDSS_DSI_NUM_DATA_LANES */ + cnt = DSIPHY_LANE_STRENGTH_CTRL_NUM; ln_off = cnt * ln; ip = &pd->strength[ln_off]; - off = 0x38; - for (j = 0; j < cnt; j++, off += 4) + off = DSIPHY_LANE_STRENGTH_CTRL_BASE; + for (j = 0; j < cnt; j++, + off += DSIPHY_LANE_STRENGTH_CTRL_OFFSET) MIPI_OUTP(base + off, *ip++); + base += DATALANE_SIZE_8996; /* next lane */ + } + + /* + * clk lane configuration for strength ctrl + * for split link there are two clock lanes, one + * clock lane per sublink needs to be configured + */ + base = ctrl->phy_io.base + CLKLANE_OFFSET_FROM_BASE_8996; + cnt = DSIPHY_LANE_STRENGTH_CTRL_NUM; + ln_off = MDSS_DSI_NUM_DATA_LANES; + ip = &pd->strength[ln_off]; + off = DSIPHY_LANE_STRENGTH_CTRL_BASE; + for (j = 0; j < cnt; j++, + off += DSIPHY_LANE_STRENGTH_CTRL_OFFSET) { + MIPI_OUTP(base + off, *ip); + if (panel_info->split_link_enabled) + MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip); } mdss_dsi_8996_phy_regulator_enable(ctrl); @@ -1051,67 +1146,129 @@ static void mdss_dsi_8996_phy_config(struct mdss_dsi_ctrl_pdata *ctrl) int j, off, ln, cnt, ln_off; char *ip; void __iomem *base; + struct mdss_panel_info *panel_info; + int num_of_lanes = 0; + + if (ctrl) { + panel_info = &((ctrl->panel_data).panel_info); + } else { + pr_warn("%s: null ctrl pdata\n", __func__); + return; + } pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db); + num_of_lanes = MDSS_DSI_NUM_DATA_LANES + MDSS_DSI_NUM_CLK_LANES; MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_LDO_CNTRL, 0x1c); /* clk_en */ MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_GLBL_TEST_CTRL, 0x1); - if (pd->lanecfg_len != 20) { + if (pd->lanecfg_len != (num_of_lanes * DSIPHY_LANE_CFG_NUM)) { pr_err("%s: wrong lane cfg\n", __func__); return; } - if (pd->strength_len != 10) { + if (pd->strength_len != (num_of_lanes * + DSIPHY_LANE_STRENGTH_CTRL_NUM)) { pr_err("%s: wrong strength ctrl\n", __func__); return; } - if (pd->regulator_len != 5) { + if (pd->regulator_len != (num_of_lanes * DSIPHY_LANE_VREG_NUM)) { pr_err("%s: wrong regulator setting\n", __func__); return; } - /* 4 lanes + clk lane configuration */ - for (ln = 0; ln < 5; ln++) { - /* - * data lane offset frome base: 0x100 - * data lane size: 0x80 - */ - base = ctrl->phy_io.base + - DATALANE_OFFSET_FROM_BASE_8996; - base += (ln * DATALANE_SIZE_8996); /* lane base */ - - /* lane cfg, 4 * 5 */ - cnt = 4; + /* data lanes configuration */ + base = ctrl->phy_io.base + DATALANE_OFFSET_FROM_BASE_8996; + for (ln = 0; ln < MDSS_DSI_NUM_DATA_LANES; ln++) { + /* lane cfg, 4 * MDSS_DSI_NUM_DATA_LANES */ + cnt = DSIPHY_LANE_CFG_NUM; + off = DSIPHY_LANE_CFG_BASE; ln_off = cnt * ln; ip = &pd->lanecfg[ln_off]; - off = 0x0; for (j = 0; j < cnt; j++) { MIPI_OUTP(base + off, *ip++); - off += 4; + off += DSIPHY_LANE_CFG_OFFSET; } /* test str */ - MIPI_OUTP(base + 0x14, 0x0088); /* fixed */ + MIPI_OUTP(base + DSIPHY_LANE_TEST_STR, 0x88); /* fixed */ - /* phy timing, 8 * 5 */ - cnt = 8; + /* phy timing, 8 * MDSS_DSI_NUM_DATA_LANES */ + cnt = DSIPHY_LANE_TIMING_CTRL_NUM; + off = DSIPHY_LANE_TIMING_CTRL_BASE; ln_off = cnt * ln; ip = &pd->timing_8996[ln_off]; - off = 0x18; - for (j = 0; j < cnt; j++, off += 4) + for (j = 0; j < cnt; j++) { MIPI_OUTP(base + off, *ip++); + off += DSIPHY_LANE_TIMING_CTRL_OFFSET; + } - /* strength, 2 * 5 */ - cnt = 2; + /* strength, 2 * MDSS_DSI_NUM_DATA_LANES */ + cnt = DSIPHY_LANE_STRENGTH_CTRL_NUM; + off = DSIPHY_LANE_STRENGTH_CTRL_BASE; ln_off = cnt * ln; ip = &pd->strength[ln_off]; - off = 0x38; - for (j = 0; j < cnt; j++, off += 4) + for (j = 0; j < cnt; j++) { MIPI_OUTP(base + off, *ip++); + off += DSIPHY_LANE_STRENGTH_CTRL_OFFSET; + } + + base += DATALANE_SIZE_8996; /* next lane */ + } + + /* + * clk lane configuration + * for split link there are two clock lanes, one + * clock lane per sublink needs to be configured + */ + base = ctrl->phy_io.base + CLKLANE_OFFSET_FROM_BASE_8996; + cnt = DSIPHY_LANE_CFG_NUM; + off = DSIPHY_LANE_CFG_BASE; + ln_off = cnt * MDSS_DSI_NUM_DATA_LANES; + ip = &pd->lanecfg[ln_off]; + for (j = 0; j < cnt; j++) { + MIPI_OUTP(base + off, *ip); + if (panel_info->split_link_enabled) + MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip); + ip++; + off += DSIPHY_LANE_CFG_OFFSET; + } + + /* test str */ + MIPI_OUTP(base + DSIPHY_LANE_TEST_STR, 0x88); /* fixed */ + if (panel_info->split_link_enabled) + MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, 0x88); + + cnt = DSIPHY_LANE_TIMING_CTRL_NUM; + off = DSIPHY_LANE_TIMING_CTRL_BASE; + ln_off = cnt * MDSS_DSI_NUM_DATA_LANES; + ip = &pd->timing_8996[ln_off]; + for (j = 0; j < cnt; j++) { + MIPI_OUTP(base + off, *ip); + if (panel_info->split_link_enabled) + MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip); + ip++; + off += DSIPHY_LANE_TIMING_CTRL_OFFSET; + } + + /* + * clk lane configuration for timing + * for split link there are two clock lanes, one + * clock lane per sublink needs to be configured + */ + cnt = DSIPHY_LANE_STRENGTH_CTRL_NUM; + off = DSIPHY_LANE_STRENGTH_CTRL_BASE; + ln_off = cnt * MDSS_DSI_NUM_DATA_LANES; + ip = &pd->strength[ln_off]; + for (j = 0; j < cnt; j++) { + MIPI_OUTP(base + off, *ip); + if (panel_info->split_link_enabled) + MIPI_OUTP(base + CLKLANE_SIZE_8996 + off, *ip); + ip++; + off += DSIPHY_LANE_STRENGTH_CTRL_OFFSET; } wmb(); /* make sure registers committed */ @@ -1665,6 +1822,9 @@ int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, u32 dsi_pclk_rate; u8 lanes = 0, bpp; + if (!panel_info) + return -EINVAL; + if (panel_info->mipi.data_lane3) lanes += 1; if (panel_info->mipi.data_lane2) @@ -1690,6 +1850,8 @@ int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, } h_period = mdss_panel_get_htotal(panel_info, true); + if (panel_info->split_link_enabled) + h_period *= panel_info->mipi.num_of_sublinks; v_period = mdss_panel_get_vtotal(panel_info); if (ctrl_pdata->refresh_clk_rate || is_diff_frame_rate(panel_info, @@ -1710,7 +1872,12 @@ int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, clk_rate = panel_info->clk_rate; do_div(clk_rate, 8 * bpp); - dsi_pclk_rate = (u32) clk_rate * lanes; + + if (panel_info->split_link_enabled) + dsi_pclk_rate = (u32) clk_rate * + panel_info->mipi.lanes_per_sublink; + else + dsi_pclk_rate = (u32) clk_rate * lanes; if ((dsi_pclk_rate < 3300000) || (dsi_pclk_rate > 250000000)) dsi_pclk_rate = 35000000; @@ -2320,6 +2487,32 @@ int mdss_dsi_pre_clkoff_cb(void *priv, return rc; } +static void mdss_dsi_split_link_clk_cfg(struct mdss_dsi_ctrl_pdata *ctrl, + int enable) +{ + struct mdss_panel_data *pdata = NULL; + void __iomem *base; + u32 data = 0; + + if (ctrl) + pdata = &ctrl->panel_data; + else { + pr_err("%s: ctrl pdata is NULL\n", __func__); + return; + } + + /* + * for split link there are two clock lanes, and + * both clock lanes needs to be enabled + */ + if (pdata->panel_info.split_link_enabled) { + base = ctrl->phy_io.base; + data = MIPI_INP(base + MDSS_DSI_DSIPHY_CMN_CLK_CFG1); + data |= (enable << DSIPHY_CMN_CLK_CFG1_SPLIT_LINK); + MIPI_OUTP(base + MDSS_DSI_DSIPHY_CMN_CLK_CFG1, data); + } +} + int mdss_dsi_post_clkon_cb(void *priv, enum mdss_dsi_clk_type clk, enum mdss_dsi_clk_state curr_state) @@ -2393,6 +2586,9 @@ int mdss_dsi_post_clkon_cb(void *priv, } if (pdata->panel_info.mipi.force_clk_lane_hs) mdss_dsi_cfg_lane_ctrl(ctrl, BIT(28), 1); + + /* enable split link for cmn clk cfg1 */ + mdss_dsi_split_link_clk_cfg(ctrl, 1); } error: return rc; @@ -2419,9 +2615,16 @@ int mdss_dsi_post_clkoff_cb(void *priv, pdata = &ctrl->panel_data; for (i = DSI_MAX_PM - 1; i >= DSI_CORE_PM; i--) { - if ((ctrl->ctrl_state & CTRL_STATE_DSI_ACTIVE) && - (i != DSI_CORE_PM)) - continue; + /** + * If DSI_CTRL is active, proceed to turn off + * supplies which support turning off in low power + * state + */ + if (ctrl->ctrl_state & CTRL_STATE_DSI_ACTIVE) + if (!sdata->power_data[i].vreg_config + ->lp_disable_allowed) + continue; + rc = msm_dss_enable_vreg( sdata->power_data[i].vreg_config, sdata->power_data[i].num_vreg, 0); @@ -2431,6 +2634,12 @@ int mdss_dsi_post_clkoff_cb(void *priv, __mdss_dsi_pm_name(i)); rc = 0; } else { + pr_debug("%s: disabled vreg for %s panel_state %d\n", + __func__, + __mdss_dsi_pm_name(i), + pdata->panel_info.panel_power_state); + sdata->power_data[i].vreg_config->disabled = + true; ctrl->core_power = false; } } @@ -2470,7 +2679,7 @@ int mdss_dsi_pre_clkon_cb(void *priv, for (i = DSI_CORE_PM; i < DSI_MAX_PM; i++) { if ((ctrl->ctrl_state & CTRL_STATE_DSI_ACTIVE) && (!pdata->panel_info.cont_splash_enabled) && - (i != DSI_CORE_PM)) + (!sdata->power_data[i].vreg_config->disabled)) continue; rc = msm_dss_enable_vreg( sdata->power_data[i].vreg_config, @@ -2480,6 +2689,11 @@ int mdss_dsi_pre_clkon_cb(void *priv, __func__, __mdss_dsi_pm_name(i)); } else { + pr_debug("%s: enabled vregs for %s\n", + __func__, + __mdss_dsi_pm_name(i)); + sdata->power_data[i].vreg_config->disabled = + false; ctrl->core_power = true; } diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c index 162689227a23..1cf907ecded4 100644 --- a/drivers/video/hdmi.c +++ b/drivers/video/hdmi.c @@ -533,6 +533,10 @@ hdmi_picture_aspect_get_name(enum hdmi_picture_aspect picture_aspect) return "4:3"; case HDMI_PICTURE_ASPECT_16_9: return "16:9"; + case HDMI_PICTURE_ASPECT_64_27: + return "64:27"; + case HDMI_PICTURE_ASPECT_256_135: + return "256:135"; case HDMI_PICTURE_ASPECT_RESERVED: return "Reserved"; } diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 8959b320d472..84c6add93f1f 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -421,6 +421,8 @@ static int init_vqs(struct virtio_balloon *vb) * Prime this virtqueue with one buffer so the hypervisor can * use it to signal us later (it can't be broken yet!). */ + update_balloon_stats(vb); + sg_init_one(&sg, vb->stats, sizeof vb->stats); if (virtqueue_add_outbuf(vb->stats_vq, &sg, 1, vb, GFP_KERNEL) < 0) diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c index 611f9c11da85..2e319d0c395d 100644 --- a/drivers/xen/xen-acpi-processor.c +++ b/drivers/xen/xen-acpi-processor.c @@ -27,10 +27,10 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/types.h> +#include <linux/syscore_ops.h> #include <linux/acpi.h> #include <acpi/processor.h> #include <xen/xen.h> -#include <xen/xen-ops.h> #include <xen/interface/platform.h> #include <asm/xen/hypercall.h> @@ -466,15 +466,33 @@ static int xen_upload_processor_pm_data(void) return rc; } -static int xen_acpi_processor_resume(struct notifier_block *nb, - unsigned long action, void *data) +static void xen_acpi_processor_resume_worker(struct work_struct *dummy) { + int rc; + bitmap_zero(acpi_ids_done, nr_acpi_bits); - return xen_upload_processor_pm_data(); + + rc = xen_upload_processor_pm_data(); + if (rc != 0) + pr_info("ACPI data upload failed, error = %d\n", rc); +} + +static void xen_acpi_processor_resume(void) +{ + static DECLARE_WORK(wq, xen_acpi_processor_resume_worker); + + /* + * xen_upload_processor_pm_data() calls non-atomic code. + * However, the context for xen_acpi_processor_resume is syscore + * with only the boot CPU online and in an atomic context. + * + * So defer the upload for some point safer. + */ + schedule_work(&wq); } -struct notifier_block xen_acpi_processor_resume_nb = { - .notifier_call = xen_acpi_processor_resume, +static struct syscore_ops xap_syscore_ops = { + .resume = xen_acpi_processor_resume, }; static int __init xen_acpi_processor_init(void) @@ -527,7 +545,7 @@ static int __init xen_acpi_processor_init(void) if (rc) goto err_unregister; - xen_resume_notifier_register(&xen_acpi_processor_resume_nb); + register_syscore_ops(&xap_syscore_ops); return 0; err_unregister: @@ -544,7 +562,7 @@ static void __exit xen_acpi_processor_exit(void) { int i; - xen_resume_notifier_unregister(&xen_acpi_processor_resume_nb); + unregister_syscore_ops(&xap_syscore_ops); kfree(acpi_ids_done); kfree(acpi_id_present); kfree(acpi_id_cst_present); diff --git a/fs/9p/acl.c b/fs/9p/acl.c index 929b618da43b..c30c6ceac2c4 100644 --- a/fs/9p/acl.c +++ b/fs/9p/acl.c @@ -283,6 +283,7 @@ static int v9fs_xattr_set_acl(const struct xattr_handler *handler, case ACL_TYPE_ACCESS: if (acl) { struct iattr iattr; + struct posix_acl *old_acl = acl; retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl); if (retval) @@ -293,6 +294,7 @@ static int v9fs_xattr_set_acl(const struct xattr_handler *handler, * by the mode bits. So don't * update ACL. */ + posix_acl_release(old_acl); value = NULL; size = 0; } diff --git a/fs/attr.c b/fs/attr.c index 11be2265a2d5..c86b37c38fb7 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -211,7 +211,7 @@ int notify_change2(struct vfsmount *mnt, struct dentry * dentry, struct iattr * return -EPERM; if (!inode_owner_or_capable(inode)) { - error = inode_permission(inode, MAY_WRITE); + error = inode_permission2(mnt, inode, MAY_WRITE); if (error) return error; } diff --git a/fs/dcache.c b/fs/dcache.c index 7b8feb6d60c8..3a3b0f370304 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1364,7 +1364,7 @@ static enum d_walk_ret select_collect(void *_data, struct dentry *dentry) goto out; if (dentry->d_flags & DCACHE_SHRINK_LIST) { - data->found++; + goto out; } else { if (dentry->d_flags & DCACHE_LRU_LIST) d_lru_del(dentry); diff --git a/fs/exec.c b/fs/exec.c index 8c58183eccb7..b1f5ddbf7d56 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1132,8 +1132,10 @@ EXPORT_SYMBOL(flush_old_exec); void would_dump(struct linux_binprm *bprm, struct file *file) { struct inode *inode = file_inode(file); + if (inode_permission2(file->f_path.mnt, inode, MAY_READ) < 0) { struct user_namespace *old, *user_ns; + bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP; /* Ensure mm->user_ns contains the executable */ diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig index 95a49ef2781a..ebaff5ab93da 100644 --- a/fs/ext4/Kconfig +++ b/fs/ext4/Kconfig @@ -106,6 +106,7 @@ config EXT4_ENCRYPTION select CRYPTO_ECB select CRYPTO_XTS select CRYPTO_CTS + select CRYPTO_HEH select CRYPTO_CTR select CRYPTO_SHA256 select KEYS diff --git a/fs/ext4/crypto_fname.c b/fs/ext4/crypto_fname.c index 2fbef8a14760..e2645ca9b95e 100644 --- a/fs/ext4/crypto_fname.c +++ b/fs/ext4/crypto_fname.c @@ -44,7 +44,8 @@ static void ext4_dir_crypt_complete(struct crypto_async_request *req, int res) bool ext4_valid_filenames_enc_mode(uint32_t mode) { - return (mode == EXT4_ENCRYPTION_MODE_AES_256_CTS); + return (mode == EXT4_ENCRYPTION_MODE_AES_256_CTS || + mode == EXT4_ENCRYPTION_MODE_AES_256_HEH); } static unsigned max_name_len(struct inode *inode) diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c index 15342bfff70d..363e4c6bf37f 100644 --- a/fs/ext4/crypto_key.c +++ b/fs/ext4/crypto_key.c @@ -30,16 +30,16 @@ static void derive_crypt_complete(struct crypto_async_request *req, int rc) } /** - * ext4_derive_key_aes() - Derive a key using AES-128-ECB + * ext4_derive_key_v1() - Derive a key using AES-128-ECB * @deriving_key: Encryption key used for derivation. * @source_key: Source key to which to apply derivation. * @derived_key: Derived key. * - * Return: Zero on success; non-zero otherwise. + * Return: 0 on success, -errno on failure */ -static int ext4_derive_key_aes(char deriving_key[EXT4_AES_128_ECB_KEY_SIZE], - char source_key[EXT4_AES_256_XTS_KEY_SIZE], - char derived_key[EXT4_AES_256_XTS_KEY_SIZE]) +static int ext4_derive_key_v1(const char deriving_key[EXT4_AES_128_ECB_KEY_SIZE], + const char source_key[EXT4_AES_256_XTS_KEY_SIZE], + char derived_key[EXT4_AES_256_XTS_KEY_SIZE]) { int res = 0; struct ablkcipher_request *req = NULL; @@ -84,6 +84,91 @@ out: return res; } +/** + * ext4_derive_key_v2() - Derive a key non-reversibly + * @nonce: the nonce associated with the file + * @master_key: the master key referenced by the file + * @derived_key: (output) the resulting derived key + * + * This function computes the following: + * derived_key[0:127] = AES-256-ENCRYPT(master_key[0:255], nonce) + * derived_key[128:255] = AES-256-ENCRYPT(master_key[0:255], nonce ^ 0x01) + * derived_key[256:383] = AES-256-ENCRYPT(master_key[256:511], nonce) + * derived_key[384:511] = AES-256-ENCRYPT(master_key[256:511], nonce ^ 0x01) + * + * 'nonce ^ 0x01' denotes flipping the low order bit of the last byte. + * + * Unlike the v1 algorithm, the v2 algorithm is "non-reversible", meaning that + * compromising a derived key does not also compromise the master key. + * + * Return: 0 on success, -errno on failure + */ +static int ext4_derive_key_v2(const char nonce[EXT4_KEY_DERIVATION_NONCE_SIZE], + const char master_key[EXT4_MAX_KEY_SIZE], + char derived_key[EXT4_MAX_KEY_SIZE]) +{ + const int noncelen = EXT4_KEY_DERIVATION_NONCE_SIZE; + struct crypto_cipher *tfm; + int err; + int i; + + /* + * Since we only use each transform for a small number of encryptions, + * requesting just "aes" turns out to be significantly faster than + * "ecb(aes)", by about a factor of two. + */ + tfm = crypto_alloc_cipher("aes", 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + BUILD_BUG_ON(4 * EXT4_KEY_DERIVATION_NONCE_SIZE != EXT4_MAX_KEY_SIZE); + BUILD_BUG_ON(2 * EXT4_AES_256_ECB_KEY_SIZE != EXT4_MAX_KEY_SIZE); + for (i = 0; i < 2; i++) { + memcpy(derived_key, nonce, noncelen); + memcpy(derived_key + noncelen, nonce, noncelen); + derived_key[2 * noncelen - 1] ^= 0x01; + err = crypto_cipher_setkey(tfm, master_key, + EXT4_AES_256_ECB_KEY_SIZE); + if (err) + break; + crypto_cipher_encrypt_one(tfm, derived_key, derived_key); + crypto_cipher_encrypt_one(tfm, derived_key + noncelen, + derived_key + noncelen); + master_key += EXT4_AES_256_ECB_KEY_SIZE; + derived_key += 2 * noncelen; + } + crypto_free_cipher(tfm); + return err; +} + +/** + * ext4_derive_key() - Derive a per-file key from a nonce and master key + * @ctx: the encryption context associated with the file + * @master_key: the master key referenced by the file + * @derived_key: (output) the resulting derived key + * + * Return: 0 on success, -errno on failure + */ +static int ext4_derive_key(const struct ext4_encryption_context *ctx, + const char master_key[EXT4_MAX_KEY_SIZE], + char derived_key[EXT4_MAX_KEY_SIZE]) +{ + BUILD_BUG_ON(EXT4_AES_128_ECB_KEY_SIZE != EXT4_KEY_DERIVATION_NONCE_SIZE); + BUILD_BUG_ON(EXT4_AES_256_XTS_KEY_SIZE != EXT4_MAX_KEY_SIZE); + + /* + * Although the key derivation algorithm is logically independent of the + * choice of encryption modes, in this kernel it is bundled with HEH + * encryption of filenames, which is another crypto improvement that + * requires an on-disk format change and requires userspace to specify + * different encryption policies. + */ + if (ctx->filenames_encryption_mode == EXT4_ENCRYPTION_MODE_AES_256_HEH) + return ext4_derive_key_v2(ctx->nonce, master_key, derived_key); + else + return ext4_derive_key_v1(ctx->nonce, master_key, derived_key); +} + void ext4_free_crypt_info(struct ext4_crypt_info *ci) { if (!ci) @@ -192,6 +277,8 @@ retry: break; case EXT4_ENCRYPTION_MODE_PRIVATE: cipher_str = "bugon"; + case EXT4_ENCRYPTION_MODE_AES_256_HEH: + cipher_str = "heh(aes)"; break; default: printk_once(KERN_WARNING @@ -242,7 +329,7 @@ retry: up_read(&keyring_key->sem); goto out; } - res = ext4_derive_key_aes(ctx.nonce, master_key->raw, + res = ext4_derive_key(&ctx, master_key->raw, crypt_info->ci_raw_key); up_read(&keyring_key->sem); if (res) diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c index 8a9feb341f31..dd561f916f0b 100644 --- a/fs/ext4/crypto_policy.c +++ b/fs/ext4/crypto_policy.c @@ -156,6 +156,12 @@ int ext4_is_child_context_consistent_with_parent(struct inode *parent, WARN_ON(1); /* Should never happen */ return 0; } + + /* No restrictions on file types which are never encrypted */ + if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) && + !S_ISLNK(child->i_mode)) + return 1; + /* no restrictions if the parent directory is not encrypted */ if (!ext4_encrypted_inode(parent)) return 1; diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 14d7f72f8092..482d1caee849 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -590,6 +590,7 @@ enum { #define EXT4_ENCRYPTION_MODE_AES_256_CBC 3 #define EXT4_ENCRYPTION_MODE_AES_256_CTS 4 #define EXT4_ENCRYPTION_MODE_PRIVATE 127 +#define EXT4_ENCRYPTION_MODE_AES_256_HEH 126 #include "ext4_crypto.h" @@ -2339,19 +2340,6 @@ int _ext4_get_encryption_info(struct inode *inode); #ifdef CONFIG_EXT4_FS_ENCRYPTION int ext4_has_encryption_key(struct inode *inode); -static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode) -{ - return EXT4_I(inode)->i_crypt_info; -} - -static inline int ext4_using_hardware_encryption(struct inode *inode) -{ - struct ext4_crypt_info *ci = ext4_encryption_info(inode); - - return S_ISREG(inode->i_mode) && ci && - ci->ci_data_mode == EXT4_ENCRYPTION_MODE_PRIVATE; -} - static inline int ext4_get_encryption_info(struct inode *inode) { struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; @@ -2365,6 +2353,19 @@ static inline int ext4_get_encryption_info(struct inode *inode) return 0; } +static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode) +{ + return EXT4_I(inode)->i_crypt_info; +} + +static inline int ext4_using_hardware_encryption(struct inode *inode) +{ + struct ext4_crypt_info *ci = ext4_encryption_info(inode); + + return S_ISREG(inode->i_mode) && ci && + ci->ci_data_mode == EXT4_ENCRYPTION_MODE_PRIVATE; +} + #else static inline int ext4_has_encryption_key(struct inode *inode) { diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h index 95cbc9bc1995..e28cc5aab04a 100644 --- a/fs/ext4/ext4_crypto.h +++ b/fs/ext4/ext4_crypto.h @@ -59,8 +59,10 @@ struct ext4_encryption_context { #define EXT4_XTS_TWEAK_SIZE 16 #define EXT4_AES_128_ECB_KEY_SIZE 16 #define EXT4_AES_256_GCM_KEY_SIZE 32 +#define EXT4_AES_256_ECB_KEY_SIZE 32 #define EXT4_AES_256_CBC_KEY_SIZE 32 #define EXT4_AES_256_CTS_KEY_SIZE 32 +#define EXT4_AES_256_HEH_KEY_SIZE 32 #define EXT4_AES_256_XTS_KEY_SIZE 64 #define EXT4_PRIVATE_KEY_SIZE 64 #define EXT4_MAX_KEY_SIZE 64 @@ -127,6 +129,8 @@ static inline int ext4_encryption_key_size(int mode) return EXT4_AES_256_CBC_KEY_SIZE; case EXT4_ENCRYPTION_MODE_AES_256_CTS: return EXT4_AES_256_CTS_KEY_SIZE; + case EXT4_ENCRYPTION_MODE_AES_256_HEH: + return EXT4_AES_256_HEH_KEY_SIZE; default: BUG(); } diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index cb3d6f6419cd..280d67fe33a7 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -18,6 +18,7 @@ #include "ext4.h" #include "xattr.h" #include "truncate.h" +#include <trace/events/android_fs.h> #define EXT4_XATTR_SYSTEM_DATA "data" #define EXT4_MIN_INLINE_DATA_SIZE ((sizeof(__le32) * EXT4_N_BLOCKS)) @@ -502,6 +503,17 @@ int ext4_readpage_inline(struct inode *inode, struct page *page) return -EAGAIN; } + if (trace_android_fs_dataread_start_enabled()) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_dataread_start(inode, page_offset(page), + PAGE_SIZE, current->pid, + path, current->comm); + } + /* * Current inline data can only exist in the 1st page, * So for all the other pages, just set them uptodate. @@ -513,6 +525,8 @@ int ext4_readpage_inline(struct inode *inode, struct page *page) SetPageUptodate(page); } + trace_android_fs_dataread_end(inode, page_offset(page), PAGE_SIZE); + up_read(&EXT4_I(inode)->xattr_sem); unlock_page(page); @@ -1157,10 +1171,9 @@ static int ext4_finish_convert_inline_dir(handle_t *handle, set_buffer_uptodate(dir_block); err = ext4_handle_dirty_dirent_node(handle, inode, dir_block); if (err) - goto out; + return err; set_buffer_verified(dir_block); -out: - return err; + return ext4_mark_inode_dirty(handle, inode); } static int ext4_convert_inline_data_nolock(handle_t *handle, diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 99fa2fca52b1..72b384931f66 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -45,6 +45,7 @@ #include "ext4_ice.h" #include <trace/events/ext4.h> +#include <trace/events/android_fs.h> #define MPAGE_DA_EXTENT_TAIL 0x01 @@ -1018,6 +1019,16 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping, pgoff_t index; unsigned from, to; + if (trace_android_fs_datawrite_start_enabled()) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_datawrite_start(inode, pos, len, + current->pid, path, + current->comm); + } trace_ext4_write_begin(inode, pos, len, flags); /* * Reserve one block more for addition to orphan list in case @@ -1154,6 +1165,7 @@ static int ext4_write_end(struct file *file, int ret = 0, ret2; int i_size_changed = 0; + trace_android_fs_datawrite_end(inode, pos, len); trace_ext4_write_end(inode, pos, len, copied); if (ext4_test_inode_state(inode, EXT4_STATE_ORDERED_MODE)) { ret = ext4_jbd2_file_inode(handle, inode); @@ -1267,6 +1279,7 @@ static int ext4_journalled_write_end(struct file *file, unsigned from, to; int size_changed = 0; + trace_android_fs_datawrite_end(inode, pos, len); trace_ext4_journalled_write_end(inode, pos, len, copied); from = pos & (PAGE_CACHE_SIZE - 1); to = from + len; @@ -2742,6 +2755,16 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping, len, flags, pagep, fsdata); } *fsdata = (void *)0; + if (trace_android_fs_datawrite_start_enabled()) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_datawrite_start(inode, pos, len, + current->pid, + path, current->comm); + } trace_ext4_da_write_begin(inode, pos, len, flags); if (ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) { @@ -2860,6 +2883,7 @@ static int ext4_da_write_end(struct file *file, return ext4_write_end(file, mapping, pos, len, copied, page, fsdata); + trace_android_fs_datawrite_end(inode, pos, len); trace_ext4_da_write_end(inode, pos, len, copied); start = pos & (PAGE_CACHE_SIZE - 1); end = start + copied - 1; @@ -3352,12 +3376,42 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter, if (ext4_has_inline_data(inode)) return 0; + if (trace_android_fs_dataread_start_enabled() && + (iov_iter_rw(iter) == READ)) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_dataread_start(inode, offset, count, + current->pid, path, + current->comm); + } + if (trace_android_fs_datawrite_start_enabled() && + (iov_iter_rw(iter) == WRITE)) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_datawrite_start(inode, offset, count, + current->pid, path, + current->comm); + } trace_ext4_direct_IO_enter(inode, offset, count, iov_iter_rw(iter)); if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) ret = ext4_ext_direct_IO(iocb, iter, offset); else ret = ext4_ind_direct_IO(iocb, iter, offset); trace_ext4_direct_IO_exit(inode, offset, count, iov_iter_rw(iter), ret); + + if (trace_android_fs_dataread_start_enabled() && + (iov_iter_rw(iter) == READ)) + trace_android_fs_dataread_end(inode, offset, count); + if (trace_android_fs_datawrite_start_enabled() && + (iov_iter_rw(iter) == WRITE)) + trace_android_fs_datawrite_end(inode, offset, count); + return ret; } diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 7e974878d9a9..3a2594665b44 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -637,8 +637,12 @@ resizefs_out: if (err) goto encryption_policy_out; + mutex_lock(&inode->i_mutex); + err = ext4_process_policy(&policy, inode); + mutex_unlock(&inode->i_mutex); + mnt_drop_write_file(filp); encryption_policy_out: return err; diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index 71a08a294529..99f1bd8c7f05 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -46,6 +46,7 @@ #include "ext4.h" #include "ext4_ice.h" +#include <trace/events/android_fs.h> /* * Call ext4_decrypt on every single page, reusing the encryption @@ -92,6 +93,17 @@ static inline bool ext4_bio_encrypted(struct bio *bio) #endif } +static void +ext4_trace_read_completion(struct bio *bio) +{ + struct page *first_page = bio->bi_io_vec[0].bv_page; + + if (first_page != NULL) + trace_android_fs_dataread_end(first_page->mapping->host, + page_offset(first_page), + bio->bi_iter.bi_size); +} + /* * I/O completion handler for multipage BIOs. * @@ -109,6 +121,9 @@ static void mpage_end_io(struct bio *bio) struct bio_vec *bv; int i; + if (trace_android_fs_dataread_start_enabled()) + ext4_trace_read_completion(bio); + if (ext4_bio_encrypted(bio)) { struct ext4_crypto_ctx *ctx = bio->bi_private; @@ -136,6 +151,30 @@ static void mpage_end_io(struct bio *bio) bio_put(bio); } +static void +ext4_submit_bio_read(struct bio *bio) +{ + if (trace_android_fs_dataread_start_enabled()) { + struct page *first_page = bio->bi_io_vec[0].bv_page; + + if (first_page != NULL) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + first_page->mapping->host); + trace_android_fs_dataread_start( + first_page->mapping->host, + page_offset(first_page), + bio->bi_iter.bi_size, + current->pid, + path, + current->comm); + } + } + submit_bio(READ, bio); +} + int ext4_mpage_readpages(struct address_space *mapping, struct list_head *pages, struct page *page, unsigned nr_pages) @@ -277,7 +316,7 @@ int ext4_mpage_readpages(struct address_space *mapping, */ if (bio && (last_block_in_bio != blocks[0] - 1)) { submit_and_realloc: - submit_bio(READ, bio); + ext4_submit_bio_read(bio); bio = NULL; } if (bio == NULL) { @@ -309,14 +348,14 @@ int ext4_mpage_readpages(struct address_space *mapping, if (((map.m_flags & EXT4_MAP_BOUNDARY) && (relative_block == map.m_len)) || (first_hole != blocks_per_page)) { - submit_bio(READ, bio); + ext4_submit_bio_read(bio); bio = NULL; } else last_block_in_bio = blocks[blocks_per_page - 1]; goto next_page; confused: if (bio) { - submit_bio(READ, bio); + ext4_submit_bio_read(bio); bio = NULL; } if (!PageUptodate(page)) @@ -329,6 +368,6 @@ int ext4_mpage_readpages(struct address_space *mapping, } BUG_ON(pages && !list_empty(pages)); if (bio) - submit_bio(READ, bio); + ext4_submit_bio_read(bio); return 0; } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 6fe8e30eeb99..68345a9e59b8 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -3666,7 +3666,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) / EXT4_DESC_PER_BLOCK(sb); if (ext4_has_feature_meta_bg(sb)) { - if (le32_to_cpu(es->s_first_meta_bg) >= db_count) { + if (le32_to_cpu(es->s_first_meta_bg) > db_count) { ext4_msg(sb, KERN_WARNING, "first meta block group too large: %u " "(group descriptor block count %u)", diff --git a/fs/f2fs/crypto_policy.c b/fs/f2fs/crypto_policy.c index e504f548b64e..5bbd1989d5e6 100644 --- a/fs/f2fs/crypto_policy.c +++ b/fs/f2fs/crypto_policy.c @@ -149,6 +149,11 @@ int f2fs_is_child_context_consistent_with_parent(struct inode *parent, BUG_ON(1); } + /* No restrictions on file types which are never encrypted */ + if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) && + !S_ISLNK(child->i_mode)) + return 1; + /* no restrictions if the parent directory is not encrypted */ if (!f2fs_encrypted_inode(parent)) return 1; diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index f53826ec30f3..4fb5709256fd 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -26,6 +26,7 @@ #include "segment.h" #include "trace.h" #include <trace/events/f2fs.h> +#include <trace/events/android_fs.h> static void f2fs_read_end_io(struct bio *bio) { @@ -1405,6 +1406,16 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, struct dnode_of_data dn; int err = 0; + if (trace_android_fs_datawrite_start_enabled()) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_datawrite_start(inode, pos, len, + current->pid, path, + current->comm); + } trace_f2fs_write_begin(inode, pos, len, flags); f2fs_balance_fs(sbi); @@ -1533,6 +1544,7 @@ static int f2fs_write_end(struct file *file, { struct inode *inode = page->mapping->host; + trace_android_fs_datawrite_end(inode, pos, len); trace_f2fs_write_end(inode, pos, len, copied); set_page_dirty(page); @@ -1586,6 +1598,28 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter, trace_f2fs_direct_IO_enter(inode, offset, count, iov_iter_rw(iter)); + if (trace_android_fs_dataread_start_enabled() && + (iov_iter_rw(iter) == READ)) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_dataread_start(inode, offset, + count, current->pid, path, + current->comm); + } + if (trace_android_fs_datawrite_start_enabled() && + (iov_iter_rw(iter) == WRITE)) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_datawrite_start(inode, offset, count, + current->pid, path, + current->comm); + } if (iov_iter_rw(iter) == WRITE) { __allocate_data_blocks(inode, offset, count); if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) { @@ -1599,6 +1633,13 @@ out: if (err < 0 && iov_iter_rw(iter) == WRITE) f2fs_write_failed(mapping, offset + count); + if (trace_android_fs_dataread_start_enabled() && + (iov_iter_rw(iter) == READ)) + trace_android_fs_dataread_end(inode, offset, count); + if (trace_android_fs_datawrite_start_enabled() && + (iov_iter_rw(iter) == WRITE)) + trace_android_fs_datawrite_end(inode, offset, count); + trace_f2fs_direct_IO_exit(inode, offset, count, iov_iter_rw(iter), err); return err; diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index a197215ad52b..4b449d263333 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1535,12 +1535,19 @@ static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg) #ifdef CONFIG_F2FS_FS_ENCRYPTION struct f2fs_encryption_policy policy; struct inode *inode = file_inode(filp); + int err; if (copy_from_user(&policy, (struct f2fs_encryption_policy __user *)arg, sizeof(policy))) return -EFAULT; - return f2fs_process_policy(&policy, inode); + mutex_lock(&inode->i_mutex); + + err = f2fs_process_policy(&policy, inode); + + mutex_unlock(&inode->i_mutex); + + return err; #else return -EOPNOTSUPP; #endif diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index bda7126466c0..dbb2cc4df603 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -13,6 +13,7 @@ #include "f2fs.h" #include "node.h" +#include <trace/events/android_fs.h> bool f2fs_may_inline_data(struct inode *inode) { @@ -84,14 +85,29 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) { struct page *ipage; + if (trace_android_fs_dataread_start_enabled()) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_dataread_start(inode, page_offset(page), + PAGE_SIZE, current->pid, + path, current->comm); + } + ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino); if (IS_ERR(ipage)) { + trace_android_fs_dataread_end(inode, page_offset(page), + PAGE_SIZE); unlock_page(page); return PTR_ERR(ipage); } if (!f2fs_has_inline_data(inode)) { f2fs_put_page(ipage, 1); + trace_android_fs_dataread_end(inode, page_offset(page), + PAGE_SIZE); return -EAGAIN; } @@ -102,6 +118,8 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) SetPageUptodate(page); f2fs_put_page(ipage, 1); + trace_android_fs_dataread_end(inode, page_offset(page), + PAGE_SIZE); unlock_page(page); return 0; } diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c index ff0ac96a8e7b..db4c867369b5 100644 --- a/fs/gfs2/acl.c +++ b/fs/gfs2/acl.c @@ -78,8 +78,11 @@ int gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type) if (type == ACL_TYPE_ACCESS) { umode_t mode = inode->i_mode; - + struct posix_acl *old_acl = acl; error = posix_acl_update_mode(inode, &inode->i_mode, &acl); + + if (!acl) + posix_acl_release(old_acl); if (error) return error; if (mode != inode->i_mode) diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index de7b4f97ac75..be519416c112 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -207,7 +207,7 @@ struct lm_lockname { struct gfs2_sbd *ln_sbd; u64 ln_number; unsigned int ln_type; -}; +} __packed __aligned(sizeof(int)); #define lm_name_equal(name1, name2) \ (((name1)->ln_number == (name2)->ln_number) && \ diff --git a/fs/mpage.c b/fs/mpage.c index 1480d3a18037..0fd48fdcc1b1 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -30,6 +30,14 @@ #include <linux/cleancache.h> #include "internal.h" +#define CREATE_TRACE_POINTS +#include <trace/events/android_fs.h> + +EXPORT_TRACEPOINT_SYMBOL(android_fs_datawrite_start); +EXPORT_TRACEPOINT_SYMBOL(android_fs_datawrite_end); +EXPORT_TRACEPOINT_SYMBOL(android_fs_dataread_start); +EXPORT_TRACEPOINT_SYMBOL(android_fs_dataread_end); + /* * I/O completion handler for multipage BIOs. * @@ -47,6 +55,16 @@ static void mpage_end_io(struct bio *bio) struct bio_vec *bv; int i; + if (trace_android_fs_dataread_end_enabled() && + (bio_data_dir(bio) == READ)) { + struct page *first_page = bio->bi_io_vec[0].bv_page; + + if (first_page != NULL) + trace_android_fs_dataread_end(first_page->mapping->host, + page_offset(first_page), + bio->bi_iter.bi_size); + } + bio_for_each_segment_all(bv, bio, i) { struct page *page = bv->bv_page; page_endio(page, bio_data_dir(bio), bio->bi_error); @@ -57,6 +75,24 @@ static void mpage_end_io(struct bio *bio) static struct bio *mpage_bio_submit(int rw, struct bio *bio) { + if (trace_android_fs_dataread_start_enabled() && (rw == READ)) { + struct page *first_page = bio->bi_io_vec[0].bv_page; + + if (first_page != NULL) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + first_page->mapping->host); + trace_android_fs_dataread_start( + first_page->mapping->host, + page_offset(first_page), + bio->bi_iter.bi_size, + current->pid, + path, + current->comm); + } + } bio->bi_end_io = mpage_end_io; guard_bio_eod(rw, bio); submit_bio(rw, bio); diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index 971928ab6c21..8e31d1a80f0c 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -46,7 +46,8 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) spin_unlock(&dentry->d_lock); /* check uninitialized obb_dentry and - * whether the base obbpath has been changed or not */ + * whether the base obbpath has been changed or not + */ if (is_obbpath_invalid(dentry)) { d_drop(dentry); return 0; @@ -76,17 +77,13 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) if (dentry < lower_dentry) { spin_lock(&dentry->d_lock); - spin_lock(&lower_dentry->d_lock); + spin_lock_nested(&lower_dentry->d_lock, DENTRY_D_LOCK_NESTED); } else { spin_lock(&lower_dentry->d_lock); - spin_lock(&dentry->d_lock); + spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); } - if (dentry->d_name.len != lower_dentry->d_name.len) { - __d_drop(dentry); - err = 0; - } else if (strncasecmp(dentry->d_name.name, lower_dentry->d_name.name, - dentry->d_name.len) != 0) { + if (!qstr_case_eq(&dentry->d_name, &lower_dentry->d_name)) { __d_drop(dentry); err = 0; } @@ -110,12 +107,10 @@ out: static void sdcardfs_d_release(struct dentry *dentry) { /* release and reset the lower paths */ - if(has_graft_path(dentry)) { + if (has_graft_path(dentry)) sdcardfs_put_reset_orig_path(dentry); - } sdcardfs_put_reset_lower_path(dentry); free_dentry_private_data(dentry); - return; } static int sdcardfs_hash_ci(const struct dentry *dentry, @@ -132,12 +127,10 @@ static int sdcardfs_hash_ci(const struct dentry *dentry, unsigned long hash; name = qstr->name; - //len = vfat_striptail_len(qstr); len = qstr->len; hash = init_name_hash(); while (len--) - //hash = partial_name_hash(nls_tolower(t, *name++), hash); hash = partial_name_hash(tolower(*name++), hash); qstr->hash = end_name_hash(hash); @@ -151,35 +144,25 @@ static int sdcardfs_cmp_ci(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { - /* This function is copy of vfat_cmpi */ - // FIXME Should we support national language? - //struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io; - //unsigned int alen, blen; + /* FIXME Should we support national language? */ - /* A filename cannot end in '.' or we treat it like it has none */ - /* - alen = vfat_striptail_len(name); - blen = __vfat_striptail_len(len, str); - if (alen == blen) { - if (nls_strnicmp(t, name->name, str, alen) == 0) - return 0; - } - */ if (name->len == len) { - if (strncasecmp(name->name, str, len) == 0) + if (str_n_case_eq(name->name, str, len)) return 0; } return 1; } -static void sdcardfs_canonical_path(const struct path *path, struct path *actual_path) { +static void sdcardfs_canonical_path(const struct path *path, + struct path *actual_path) +{ sdcardfs_get_real_lower(path->dentry, actual_path); } const struct dentry_operations sdcardfs_ci_dops = { .d_revalidate = sdcardfs_d_revalidate, .d_release = sdcardfs_d_release, - .d_hash = sdcardfs_hash_ci, + .d_hash = sdcardfs_hash_ci, .d_compare = sdcardfs_cmp_ci, .d_canonical_path = sdcardfs_canonical_path, }; diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 0bb442338a85..f82fedd29007 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -37,7 +37,8 @@ static void inherit_derived_state(struct inode *parent, struct inode *child) /* helper function for derived state */ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, - uid_t uid, bool under_android, struct inode *top) + uid_t uid, bool under_android, + struct inode *top) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); @@ -50,12 +51,17 @@ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, set_top(info, top); } -/* While renaming, there is a point where we want the path from dentry, but the name from newdentry */ -void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name) +/* While renaming, there is a point where we want the path from dentry, + * but the name from newdentry + */ +void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, + const struct qstr *name) { struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); + struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); appid_t appid; + unsigned long user_num; + int err; struct qstr q_Android = QSTR_LITERAL("Android"); struct qstr q_data = QSTR_LITERAL("data"); struct qstr q_obb = QSTR_LITERAL("obb"); @@ -77,58 +83,61 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, co return; /* Derive custom permissions based on parent and current node */ switch (parent_info->perm) { - case PERM_INHERIT: - case PERM_ANDROID_PACKAGE_CACHE: - /* Already inherited above */ - break; - case PERM_PRE_ROOT: - /* Legacy internal layout places users at top level */ - info->perm = PERM_ROOT; - info->userid = simple_strtoul(name->name, NULL, 10); + case PERM_INHERIT: + case PERM_ANDROID_PACKAGE_CACHE: + /* Already inherited above */ + break; + case PERM_PRE_ROOT: + /* Legacy internal layout places users at top level */ + info->perm = PERM_ROOT; + err = kstrtoul(name->name, 10, &user_num); + if (err) + info->userid = 0; + else + info->userid = user_num; + set_top(info, &info->vfs_inode); + break; + case PERM_ROOT: + /* Assume masked off by default. */ + if (qstr_case_eq(name, &q_Android)) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID; + info->under_android = true; set_top(info, &info->vfs_inode); - break; - case PERM_ROOT: - /* Assume masked off by default. */ - if (qstr_case_eq(name, &q_Android)) { - /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID; - info->under_android = true; - set_top(info, &info->vfs_inode); - } - break; - case PERM_ANDROID: - if (qstr_case_eq(name, &q_data)) { - /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_DATA; - set_top(info, &info->vfs_inode); - } else if (qstr_case_eq(name, &q_obb)) { - /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_OBB; - info->under_obb = true; - set_top(info, &info->vfs_inode); - /* Single OBB directory is always shared */ - } else if (qstr_case_eq(name, &q_media)) { - /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_MEDIA; - set_top(info, &info->vfs_inode); - } - break; - case PERM_ANDROID_OBB: - case PERM_ANDROID_DATA: - case PERM_ANDROID_MEDIA: - info->perm = PERM_ANDROID_PACKAGE; - appid = get_appid(name->name); - if (appid != 0 && !is_excluded(name->name, parent_info->userid)) { - info->d_uid = multiuser_get_uid(parent_info->userid, appid); - } + } + break; + case PERM_ANDROID: + if (qstr_case_eq(name, &q_data)) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_DATA; set_top(info, &info->vfs_inode); - break; - case PERM_ANDROID_PACKAGE: - if (qstr_case_eq(name, &q_cache)) { - info->perm = PERM_ANDROID_PACKAGE_CACHE; - info->under_cache = true; - } - break; + } else if (qstr_case_eq(name, &q_obb)) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_OBB; + info->under_obb = true; + set_top(info, &info->vfs_inode); + /* Single OBB directory is always shared */ + } else if (qstr_case_eq(name, &q_media)) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_MEDIA; + set_top(info, &info->vfs_inode); + } + break; + case PERM_ANDROID_OBB: + case PERM_ANDROID_DATA: + case PERM_ANDROID_MEDIA: + info->perm = PERM_ANDROID_PACKAGE; + appid = get_appid(name->name); + if (appid != 0 && !is_excluded(name->name, parent_info->userid)) + info->d_uid = multiuser_get_uid(parent_info->userid, appid); + set_top(info, &info->vfs_inode); + break; + case PERM_ANDROID_PACKAGE: + if (qstr_case_eq(name, &q_cache)) { + info->perm = PERM_ANDROID_PACKAGE_CACHE; + info->under_cache = true; + } + break; } } @@ -137,7 +146,8 @@ void get_derived_permission(struct dentry *parent, struct dentry *dentry) get_derived_permission_new(parent, dentry, &dentry->d_name); } -static appid_t get_type(const char *name) { +static appid_t get_type(const char *name) +{ const char *ext = strrchr(name, '.'); appid_t id; @@ -149,7 +159,8 @@ static appid_t get_type(const char *name) { return AID_MEDIA_RW; } -void fixup_lower_ownership(struct dentry* dentry, const char *name) { +void fixup_lower_ownership(struct dentry *dentry, const char *name) +{ struct path path; struct inode *inode; struct inode *delegated_inode = NULL; @@ -175,49 +186,49 @@ void fixup_lower_ownership(struct dentry* dentry, const char *name) { } switch (perm) { - case PERM_ROOT: - case PERM_ANDROID: - case PERM_ANDROID_DATA: - case PERM_ANDROID_MEDIA: - case PERM_ANDROID_PACKAGE: - case PERM_ANDROID_PACKAGE_CACHE: - uid = multiuser_get_uid(info->userid, uid); - break; - case PERM_ANDROID_OBB: - uid = AID_MEDIA_OBB; - break; - case PERM_PRE_ROOT: - default: - break; + case PERM_ROOT: + case PERM_ANDROID: + case PERM_ANDROID_DATA: + case PERM_ANDROID_MEDIA: + case PERM_ANDROID_PACKAGE: + case PERM_ANDROID_PACKAGE_CACHE: + uid = multiuser_get_uid(info->userid, uid); + break; + case PERM_ANDROID_OBB: + uid = AID_MEDIA_OBB; + break; + case PERM_PRE_ROOT: + default: + break; } switch (perm) { - case PERM_ROOT: - case PERM_ANDROID: - case PERM_ANDROID_DATA: - case PERM_ANDROID_MEDIA: - if (S_ISDIR(d_inode(dentry)->i_mode)) - gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); - else - gid = multiuser_get_uid(info->userid, get_type(name)); - break; - case PERM_ANDROID_OBB: - gid = AID_MEDIA_OBB; - break; - case PERM_ANDROID_PACKAGE: - if (info->d_uid != 0) - gid = multiuser_get_ext_gid(info->userid, info->d_uid); - else - gid = multiuser_get_uid(info->userid, uid); - break; - case PERM_ANDROID_PACKAGE_CACHE: - if (info->d_uid != 0) - gid = multiuser_get_cache_gid(info->userid, info->d_uid); - else - gid = multiuser_get_uid(info->userid, uid); - break; - case PERM_PRE_ROOT: - default: - break; + case PERM_ROOT: + case PERM_ANDROID: + case PERM_ANDROID_DATA: + case PERM_ANDROID_MEDIA: + if (S_ISDIR(d_inode(dentry)->i_mode)) + gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); + else + gid = multiuser_get_uid(info->userid, get_type(name)); + break; + case PERM_ANDROID_OBB: + gid = AID_MEDIA_OBB; + break; + case PERM_ANDROID_PACKAGE: + if (info->d_uid != 0) + gid = multiuser_get_ext_gid(info->d_uid); + else + gid = multiuser_get_uid(info->userid, uid); + break; + case PERM_ANDROID_PACKAGE_CACHE: + if (info->d_uid != 0) + gid = multiuser_get_cache_gid(info->d_uid); + else + gid = multiuser_get_uid(info->userid, uid); + break; + case PERM_PRE_ROOT: + default: + break; } sdcardfs_get_lower_path(dentry, &path); @@ -243,9 +254,11 @@ retry_deleg: if (error) pr_err("sdcardfs: Failed to touch up lower fs gid/uid.\n"); } + sdcardfs_put_lower_path(dentry, &path); } -static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) { +static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) +{ if (info->perm == PERM_ROOT) return (limit->flags & BY_USERID)?info->userid == limit->userid:1; if (info->perm == PERM_PRE_ROOT || info->perm == PERM_ANDROID) @@ -253,85 +266,56 @@ static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct li return 0; } -static int needs_fixup(perm_t perm) { +static int needs_fixup(perm_t perm) +{ if (perm == PERM_ANDROID_DATA || perm == PERM_ANDROID_OBB || perm == PERM_ANDROID_MEDIA) return 1; return 0; } -void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) { +static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit, int depth) +{ struct dentry *child; struct sdcardfs_inode_info *info; - if (!dget(dentry)) - return; + + /* + * All paths will terminate their recursion on hitting PERM_ANDROID_OBB, + * PERM_ANDROID_MEDIA, or PERM_ANDROID_DATA. This happens at a depth of + * at most 3. + */ + WARN(depth > 3, "%s: Max expected depth exceeded!\n", __func__); + spin_lock_nested(&dentry->d_lock, depth); if (!d_inode(dentry)) { - dput(dentry); + spin_unlock(&dentry->d_lock); return; } info = SDCARDFS_I(d_inode(dentry)); if (needs_fixup(info->perm)) { - spin_lock(&dentry->d_lock); list_for_each_entry(child, &dentry->d_subdirs, d_child) { - dget(child); - if (!(limit->flags & BY_NAME) || !strncasecmp(child->d_name.name, limit->name, limit->length)) { + spin_lock_nested(&child->d_lock, depth + 1); + if (!(limit->flags & BY_NAME) || qstr_case_eq(&child->d_name, &limit->name)) { if (d_inode(child)) { get_derived_permission(dentry, child); fixup_tmp_permissions(d_inode(child)); - dput(child); + spin_unlock(&child->d_lock); break; } } - dput(child); + spin_unlock(&child->d_lock); } - spin_unlock(&dentry->d_lock); - } else if (descendant_may_need_fixup(info, limit)) { - spin_lock(&dentry->d_lock); + } else if (descendant_may_need_fixup(info, limit)) { list_for_each_entry(child, &dentry->d_subdirs, d_child) { - fixup_perms_recursive(child, limit); - } - spin_unlock(&dentry->d_lock); - } - dput(dentry); -} - -void drop_recursive(struct dentry *parent) { - struct dentry *dentry; - struct sdcardfs_inode_info *info; - if (!d_inode(parent)) - return; - info = SDCARDFS_I(d_inode(parent)); - spin_lock(&parent->d_lock); - list_for_each_entry(dentry, &parent->d_subdirs, d_child) { - if (d_inode(dentry)) { - if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) { - drop_recursive(dentry); - d_drop(dentry); - } + __fixup_perms_recursive(child, limit, depth + 1); } } - spin_unlock(&parent->d_lock); + spin_unlock(&dentry->d_lock); } -void fixup_top_recursive(struct dentry *parent) { - struct dentry *dentry; - struct sdcardfs_inode_info *info; - - if (!d_inode(parent)) - return; - info = SDCARDFS_I(d_inode(parent)); - spin_lock(&parent->d_lock); - list_for_each_entry(dentry, &parent->d_subdirs, d_child) { - if (d_inode(dentry)) { - if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) { - get_derived_permission(parent, dentry); - fixup_tmp_permissions(d_inode(dentry)); - fixup_top_recursive(dentry); - } - } - } - spin_unlock(&parent->d_lock); +void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) +{ + __fixup_perms_recursive(dentry, limit, 0); } /* main function for updating derived permission */ @@ -339,19 +323,17 @@ inline void update_derived_permission_lock(struct dentry *dentry) { struct dentry *parent; - if(!dentry || !d_inode(dentry)) { - printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__); + if (!dentry || !d_inode(dentry)) { + pr_err("sdcardfs: %s: invalid dentry\n", __func__); return; } /* FIXME: * 1. need to check whether the dentry is updated or not * 2. remove the root dentry update */ - if(IS_ROOT(dentry)) { - //setup_default_pre_root_state(d_inode(dentry)); - } else { + if (!IS_ROOT(dentry)) { parent = dget_parent(dentry); - if(parent) { + if (parent) { get_derived_permission(parent, dentry); dput(parent); } @@ -363,15 +345,15 @@ int need_graft_path(struct dentry *dentry) { int ret = 0; struct dentry *parent = dget_parent(dentry); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); + struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct qstr obb = QSTR_LITERAL("obb"); - if(parent_info->perm == PERM_ANDROID && + if (parent_info->perm == PERM_ANDROID && qstr_case_eq(&dentry->d_name, &obb)) { /* /Android/obb is the base obbpath of DERIVED_UNIFIED */ - if(!(sbi->options.multiuser == false + if (!(sbi->options.multiuser == false && parent_info->userid == 0)) { ret = 1; } @@ -386,22 +368,24 @@ int is_obbpath_invalid(struct dentry *dent) struct sdcardfs_dentry_info *di = SDCARDFS_D(dent); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dent->d_sb); char *path_buf, *obbpath_s; + int need_put = 0; + struct path lower_path; /* check the base obbpath has been changed. * this routine can check an uninitialized obb dentry as well. - * regarding the uninitialized obb, refer to the sdcardfs_mkdir() */ + * regarding the uninitialized obb, refer to the sdcardfs_mkdir() + */ spin_lock(&di->lock); - if(di->orig_path.dentry) { - if(!di->lower_path.dentry) { + if (di->orig_path.dentry) { + if (!di->lower_path.dentry) { ret = 1; } else { path_get(&di->lower_path); - //lower_parent = lock_parent(lower_path->dentry); path_buf = kmalloc(PATH_MAX, GFP_ATOMIC); - if(!path_buf) { + if (!path_buf) { ret = 1; - printk(KERN_ERR "sdcardfs: fail to allocate path_buf in %s.\n", __func__); + pr_err("sdcardfs: fail to allocate path_buf in %s.\n", __func__); } else { obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX); if (d_unhashed(di->lower_path.dentry) || @@ -411,11 +395,13 @@ int is_obbpath_invalid(struct dentry *dent) kfree(path_buf); } - //unlock_dir(lower_parent); - path_put(&di->lower_path); + pathcpy(&lower_path, &di->lower_path); + need_put = 1; } } spin_unlock(&di->lock); + if (need_put) + path_put(&lower_path); return ret; } @@ -423,13 +409,13 @@ int is_base_obbpath(struct dentry *dentry) { int ret = 0; struct dentry *parent = dget_parent(dentry); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); + struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct qstr q_obb = QSTR_LITERAL("obb"); spin_lock(&SDCARDFS_D(dentry)->lock); if (sbi->options.multiuser) { - if(parent_info->perm == PERM_PRE_ROOT && + if (parent_info->perm == PERM_PRE_ROOT && qstr_case_eq(&dentry->d_name, &q_obb)) { ret = 1; } @@ -444,7 +430,8 @@ int is_base_obbpath(struct dentry *dentry) /* The lower_path will be stored to the dentry's orig_path * and the base obbpath will be copyed to the lower_path variable. * if an error returned, there's no change in the lower_path - * returns: -ERRNO if error (0: no error) */ + * returns: -ERRNO if error (0: no error) + */ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) { int err = 0; @@ -453,23 +440,24 @@ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) /* A local obb dentry must have its own orig_path to support rmdir * and mkdir of itself. Usually, we expect that the sbi->obbpath - * is avaiable on this stage. */ + * is avaiable on this stage. + */ sdcardfs_set_orig_path(dentry, lower_path); err = kern_path(sbi->obbpath_s, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &obbpath); - if(!err) { + if (!err) { /* the obbpath base has been found */ - printk(KERN_INFO "sdcardfs: the sbi->obbpath is found\n"); pathcpy(lower_path, &obbpath); } else { /* if the sbi->obbpath is not available, we can optionally * setup the lower_path with its orig_path. * but, the current implementation just returns an error * because the sdcard daemon also regards this case as - * a lookup fail. */ - printk(KERN_INFO "sdcardfs: the sbi->obbpath is not available\n"); + * a lookup fail. + */ + pr_info("sdcardfs: the sbi->obbpath is not available\n"); } return err; } diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 23f8cd7f8877..c0146e03fa2e 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -65,7 +65,7 @@ static ssize_t sdcardfs_write(struct file *file, const char __user *buf, /* check disk space */ if (!check_min_free_space(dentry, count, 0)) { - printk(KERN_INFO "No minimum free space.\n"); + pr_err("No minimum free space.\n"); return -ENOSPC; } @@ -113,6 +113,10 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd, if (lower_file->f_op->unlocked_ioctl) err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); + /* some ioctls can change inode attributes (EXT2_IOC_SETFLAGS) */ + if (!err) + sdcardfs_copy_and_fix_attrs(file_inode(file), + file_inode(lower_file)); out: return err; } @@ -160,8 +164,7 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) lower_file = sdcardfs_lower_file(file); if (willwrite && !lower_file->f_mapping->a_ops->writepage) { err = -EINVAL; - printk(KERN_ERR "sdcardfs: lower file system does not " - "support writeable mmap\n"); + pr_err("sdcardfs: lower file system does not support writeable mmap\n"); goto out; } @@ -173,16 +176,10 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) if (!SDCARDFS_F(file)->lower_vm_ops) { err = lower_file->f_op->mmap(lower_file, vma); if (err) { - printk(KERN_ERR "sdcardfs: lower mmap failed %d\n", err); + pr_err("sdcardfs: lower mmap failed %d\n", err); goto out; } saved_vm_ops = vma->vm_ops; /* save: came from lower ->mmap */ - err = do_munmap(current->mm, vma->vm_start, - vma->vm_end - vma->vm_start); - if (err) { - printk(KERN_ERR "sdcardfs: do_munmap failed %d\n", err); - goto out; - } } /* @@ -216,10 +213,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) goto out_err; } - if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { err = -EACCES; goto out_err; } @@ -251,9 +245,8 @@ static int sdcardfs_open(struct inode *inode, struct file *file) if (err) kfree(SDCARDFS_F(file)); - else { + else sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode)); - } out_revert_cred: REVERT_CRED(saved_cred); @@ -323,6 +316,75 @@ static int sdcardfs_fasync(int fd, struct file *file, int flag) return err; } +/* + * Sdcardfs cannot use generic_file_llseek as ->llseek, because it would + * only set the offset of the upper file. So we have to implement our + * own method to set both the upper and lower file offsets + * consistently. + */ +static loff_t sdcardfs_file_llseek(struct file *file, loff_t offset, int whence) +{ + int err; + struct file *lower_file; + + err = generic_file_llseek(file, offset, whence); + if (err < 0) + goto out; + + lower_file = sdcardfs_lower_file(file); + err = generic_file_llseek(lower_file, offset, whence); + +out: + return err; +} + +/* + * Sdcardfs read_iter, redirect modified iocb to lower read_iter + */ +ssize_t sdcardfs_read_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + int err; + struct file *file = iocb->ki_filp, *lower_file; + + lower_file = sdcardfs_lower_file(file); + if (!lower_file->f_op->read_iter) { + err = -EINVAL; + goto out; + } + + get_file(lower_file); /* prevent lower_file from being released */ + iocb->ki_filp = lower_file; + err = lower_file->f_op->read_iter(iocb, iter); + /* ? wait IO finish to update atime as ecryptfs ? */ + iocb->ki_filp = file; + fput(lower_file); +out: + return err; +} + +/* + * Sdcardfs write_iter, redirect modified iocb to lower write_iter + */ +ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + int err; + struct file *file = iocb->ki_filp, *lower_file; + + lower_file = sdcardfs_lower_file(file); + if (!lower_file->f_op->write_iter) { + err = -EINVAL; + goto out; + } + + get_file(lower_file); /* prevent lower_file from being released */ + iocb->ki_filp = lower_file; + err = lower_file->f_op->write_iter(iocb, iter); + iocb->ki_filp = file; + fput(lower_file); +out: + return err; +} + const struct file_operations sdcardfs_main_fops = { .llseek = generic_file_llseek, .read = sdcardfs_read, @@ -337,11 +399,13 @@ const struct file_operations sdcardfs_main_fops = { .release = sdcardfs_file_release, .fsync = sdcardfs_fsync, .fasync = sdcardfs_fasync, + .read_iter = sdcardfs_read_iter, + .write_iter = sdcardfs_write_iter, }; /* trimmed directory options */ const struct file_operations sdcardfs_dir_fops = { - .llseek = generic_file_llseek, + .llseek = sdcardfs_file_llseek, .read = generic_read_dir, .iterate = sdcardfs_readdir, .unlocked_ioctl = sdcardfs_unlocked_ioctl, diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 68e615045616..f15cb11ca8fd 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -20,12 +20,13 @@ #include "sdcardfs.h" #include <linux/fs_struct.h> +#include <linux/ratelimit.h> /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ -const struct cred * override_fsids(struct sdcardfs_sb_info* sbi, struct sdcardfs_inode_info *info) +const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info) { - struct cred * cred; - const struct cred * old_cred; + struct cred *cred; + const struct cred *old_cred; uid_t uid; cred = prepare_creds(); @@ -45,9 +46,9 @@ const struct cred * override_fsids(struct sdcardfs_sb_info* sbi, struct sdcardfs } /* Do not directly use this function, use REVERT_CRED() instead. */ -void revert_fsids(const struct cred * old_cred) +void revert_fsids(const struct cred *old_cred) { - const struct cred * cur_cred; + const struct cred *cur_cred; cur_cred = current->cred; revert_creds(old_cred); @@ -66,10 +67,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, struct fs_struct *saved_fs; struct fs_struct *copied_fs; - if(!check_caller_access_to_name(dir, &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); + if (!check_caller_access_to_name(dir, &dentry->d_name)) { err = -EACCES; goto out_eacces; } @@ -168,10 +166,7 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) struct path lower_path; const struct cred *saved_cred = NULL; - if(!check_caller_access_to_name(dir, &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); + if (!check_caller_access_to_name(dir, &dentry->d_name)) { err = -EACCES; goto out_eacces; } @@ -245,14 +240,15 @@ out: } #endif -static int touch(char *abs_path, mode_t mode) { +static int touch(char *abs_path, mode_t mode) +{ struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode); + if (IS_ERR(filp)) { if (PTR_ERR(filp) == -EEXIST) { return 0; - } - else { - printk(KERN_ERR "sdcardfs: failed to open(%s): %ld\n", + } else { + pr_err("sdcardfs: failed to open(%s): %ld\n", abs_path, PTR_ERR(filp)); return PTR_ERR(filp); } @@ -278,10 +274,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode struct qstr q_obb = QSTR_LITERAL("obb"); struct qstr q_data = QSTR_LITERAL("data"); - if(!check_caller_access_to_name(dir, &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); + if (!check_caller_access_to_name(dir, &dentry->d_name)) { err = -EACCES; goto out_eacces; } @@ -291,7 +284,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode /* check disk space */ if (!check_min_free_space(dentry, 0, 1)) { - printk(KERN_INFO "sdcardfs: No minimum free space.\n"); + pr_err("sdcardfs: No minimum free space.\n"); err = -ENOSPC; goto out_revert; } @@ -323,19 +316,21 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } /* if it is a local obb dentry, setup it with the base obbpath */ - if(need_graft_path(dentry)) { + if (need_graft_path(dentry)) { err = setup_obb_dentry(dentry, &lower_path); - if(err) { + if (err) { /* if the sbi->obbpath is not available, the lower_path won't be * changed by setup_obb_dentry() but the lower path is saved to * its orig_path. this dentry will be revalidated later. - * but now, the lower_path should be NULL */ + * but now, the lower_path should be NULL + */ sdcardfs_put_reset_lower_path(dentry); /* the newly created lower path which saved to its orig_path or * the lower_path is the base obbpath. - * therefore, an additional path_get is required */ + * therefore, an additional path_get is required + */ path_get(&lower_path); } else make_nomedia_in_obb = 1; @@ -365,7 +360,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode set_fs_pwd(current->fs, &lower_path); touch_err = touch(".nomedia", 0664); if (touch_err) { - printk(KERN_ERR "sdcardfs: failed to create .nomedia in %s: %d\n", + pr_err("sdcardfs: failed to create .nomedia in %s: %d\n", lower_path.dentry->d_name.name, touch_err); goto out; } @@ -390,10 +385,7 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) struct path lower_path; const struct cred *saved_cred = NULL; - if(!check_caller_access_to_name(dir, &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); + if (!check_caller_access_to_name(dir, &dentry->d_name)) { err = -EACCES; goto out_eacces; } @@ -402,7 +394,8 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); /* sdcardfs_get_real_lower(): in case of remove an user's obb dentry - * the dentry on the original path should be deleted. */ + * the dentry on the original path should be deleted. + */ sdcardfs_get_real_lower(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -478,11 +471,8 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct path lower_old_path, lower_new_path; const struct cred *saved_cred = NULL; - if(!check_caller_access_to_name(old_dir, &old_dentry->d_name) || + if (!check_caller_access_to_name(old_dir, &old_dentry->d_name) || !check_caller_access_to_name(new_dir, &new_dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " new_dentry: %s, task:%s\n", - __func__, new_dentry->d_name.name, current->comm); err = -EACCES; goto out_eacces; } @@ -528,7 +518,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, get_derived_permission_new(new_dentry->d_parent, old_dentry, &new_dentry->d_name); fixup_tmp_permissions(d_inode(old_dentry)); fixup_lower_ownership(old_dentry, new_dentry->d_name.name); - drop_recursive(old_dentry); /* Can't fixup ownership recursively :( */ + d_invalidate(old_dentry); /* Can't fixup ownership recursively :( */ out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); @@ -599,7 +589,7 @@ static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie) static int sdcardfs_permission_wrn(struct inode *inode, int mask) { - WARN(1, "sdcardfs does not support permission. Use permission2.\n"); + WARN_RATELIMIT(1, "sdcardfs does not support permission. Use permission2.\n"); return -EINVAL; } @@ -652,7 +642,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma release_top(SDCARDFS_I(inode)); tmp.i_sb = inode->i_sb; if (IS_POSIXACL(inode)) - printk(KERN_WARNING "%s: This may be undefined behavior... \n", __func__); + pr_warn("%s: This may be undefined behavior...\n", __func__); err = generic_permission(&tmp, mask); /* XXX * Original sdcardfs code calls inode_permission(lower_inode,.. ) @@ -670,6 +660,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma * we check it with AID_MEDIA_RW permission */ struct inode *lower_inode; + OVERRIDE_CRED(SDCARDFS_SB(inode->sb)); lower_inode = sdcardfs_lower_inode(inode); @@ -684,7 +675,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia) { - WARN(1, "sdcardfs does not support setattr. User setattr2.\n"); + WARN_RATELIMIT(1, "sdcardfs does not support setattr. User setattr2.\n"); return -EINVAL; } @@ -738,19 +729,16 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct /* prepare our own lower struct iattr (with the lower file) */ memcpy(&lower_ia, ia, sizeof(lower_ia)); /* Allow touch updating timestamps. A previous permission check ensures - * we have write access. Changes to mode, owner, and group are ignored*/ + * we have write access. Changes to mode, owner, and group are ignored + */ ia->ia_valid |= ATTR_FORCE; err = inode_change_ok(&tmp, ia); if (!err) { /* check the Android group ID */ parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) err = -EACCES; - } dput(parent); } @@ -828,10 +816,12 @@ out_err: return err; } -static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct kstat *stat) +static int sdcardfs_fillattr(struct vfsmount *mnt, + struct inode *inode, struct kstat *stat) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); struct inode *top = grab_top(info); + if (!top) return -EINVAL; @@ -855,33 +845,27 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct k static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { - struct dentry *lower_dentry; - struct inode *inode; - struct inode *lower_inode; + struct kstat lower_stat; struct path lower_path; struct dentry *parent; int err; parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { dput(parent); return -EACCES; } dput(parent); - inode = d_inode(dentry); - sdcardfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_inode = sdcardfs_lower_inode(inode); - - sdcardfs_copy_and_fix_attrs(inode, lower_inode); - fsstack_copy_inode_size(inode, lower_inode); - - err = sdcardfs_fillattr(mnt, inode, stat); + err = vfs_getattr(&lower_path, &lower_stat); + if (err) + goto out; + sdcardfs_copy_and_fix_attrs(d_inode(dentry), + d_inode(lower_path.dentry)); + err = sdcardfs_fillattr(mnt, d_inode(dentry), stat); + stat->blocks = lower_stat.blocks; +out: sdcardfs_put_lower_path(dentry, &lower_path); return err; } diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 9135866b7766..f10f7752f514 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -36,8 +36,7 @@ int sdcardfs_init_dentry_cache(void) void sdcardfs_destroy_dentry_cache(void) { - if (sdcardfs_dentry_cachep) - kmem_cache_destroy(sdcardfs_dentry_cachep); + kmem_cache_destroy(sdcardfs_dentry_cachep); } void free_dentry_private_data(struct dentry *dentry) @@ -73,6 +72,7 @@ static int sdcardfs_inode_test(struct inode *inode, void *candidate_data/*void * { struct inode *current_lower_inode = sdcardfs_lower_inode(inode); userid_t current_userid = SDCARDFS_I(inode)->userid; + if (current_lower_inode == ((struct inode_data *)candidate_data)->lower_inode && current_userid == ((struct inode_data *)candidate_data)->id) return 1; /* found a match */ @@ -102,7 +102,7 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u * instead. */ lower_inode->i_ino, /* hashval */ - sdcardfs_inode_test, /* inode comparison function */ + sdcardfs_inode_test, /* inode comparison function */ sdcardfs_inode_set, /* inode init function */ &data); /* data passed to test+set fxns */ if (!inode) { @@ -164,27 +164,25 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u } /* - * Connect a sdcardfs inode dentry/inode with several lower ones. This is - * the classic stackable file system "vnode interposition" action. - * - * @dentry: sdcardfs's dentry which interposes on lower one - * @sb: sdcardfs's super_block - * @lower_path: the lower path (caller does path_get/put) + * Helper interpose routine, called directly by ->lookup to handle + * spliced dentries. */ -int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, - struct path *lower_path, userid_t id) +static struct dentry *__sdcardfs_interpose(struct dentry *dentry, + struct super_block *sb, + struct path *lower_path, + userid_t id) { - int err = 0; struct inode *inode; struct inode *lower_inode; struct super_block *lower_sb; + struct dentry *ret_dentry; lower_inode = d_inode(lower_path->dentry); lower_sb = sdcardfs_lower_super(sb); /* check that the lower file system didn't cross a mount point */ if (lower_inode->i_sb != lower_sb) { - err = -EXDEV; + ret_dentry = ERR_PTR(-EXDEV); goto out; } @@ -196,14 +194,54 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, /* inherit lower inode number for sdcardfs's inode */ inode = sdcardfs_iget(sb, lower_inode, id); if (IS_ERR(inode)) { - err = PTR_ERR(inode); + ret_dentry = ERR_CAST(inode); goto out; } - d_add(dentry, inode); + ret_dentry = d_splice_alias(inode, dentry); + dentry = ret_dentry ?: dentry; update_derived_permission_lock(dentry); out: - return err; + return ret_dentry; +} + +/* + * Connect an sdcardfs inode dentry/inode with several lower ones. This is + * the classic stackable file system "vnode interposition" action. + * + * @dentry: sdcardfs's dentry which interposes on lower one + * @sb: sdcardfs's super_block + * @lower_path: the lower path (caller does path_get/put) + */ +int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, + struct path *lower_path, userid_t id) +{ + struct dentry *ret_dentry; + + ret_dentry = __sdcardfs_interpose(dentry, sb, lower_path, id); + return PTR_ERR(ret_dentry); +} + +struct sdcardfs_name_data { + struct dir_context ctx; + const struct qstr *to_find; + char *name; + bool found; +}; + +static int sdcardfs_name_match(struct dir_context *ctx, const char *name, + int namelen, loff_t offset, u64 ino, unsigned int d_type) +{ + struct sdcardfs_name_data *buf = container_of(ctx, struct sdcardfs_name_data, ctx); + struct qstr candidate = QSTR_INIT(name, namelen); + + if (qstr_case_eq(buf->to_find, &candidate)) { + memcpy(buf->name, name, namelen); + buf->name[namelen] = 0; + buf->found = true; + return 1; + } + return 0; } /* @@ -221,6 +259,8 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, struct dentry *lower_dentry; const struct qstr *name; struct path lower_path; + struct qstr dname; + struct dentry *ret_dentry = NULL; struct sdcardfs_sb_info *sbi; sbi = SDCARDFS_SB(dentry->d_sb); @@ -241,60 +281,79 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, &lower_path); /* check for other cases */ if (err == -ENOENT) { - struct dentry *child; - struct dentry *match = NULL; - mutex_lock(&d_inode(lower_dir_dentry)->i_mutex); - spin_lock(&lower_dir_dentry->d_lock); - list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) { - if (child && d_inode(child)) { - if (qstr_case_eq(&child->d_name, name)) { - match = dget(child); - break; - } - } + struct file *file; + const struct cred *cred = current_cred(); + + struct sdcardfs_name_data buffer = { + .ctx.actor = sdcardfs_name_match, + .to_find = name, + .name = __getname(), + .found = false, + }; + + if (!buffer.name) { + err = -ENOMEM; + goto out; + } + file = dentry_open(lower_parent_path, O_RDONLY, cred); + if (IS_ERR(file)) { + err = PTR_ERR(file); + goto put_name; } - spin_unlock(&lower_dir_dentry->d_lock); - mutex_unlock(&d_inode(lower_dir_dentry)->i_mutex); - if (match) { + err = iterate_dir(file, &buffer.ctx); + fput(file); + if (err) + goto put_name; + + if (buffer.found) err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, - match->d_name.name, 0, + buffer.name, 0, &lower_path); - dput(match); - } + else + err = -ENOENT; +put_name: + __putname(buffer.name); } /* no error: handle positive dentries */ if (!err) { /* check if the dentry is an obb dentry * if true, the lower_inode must be replaced with - * the inode of the graft path */ + * the inode of the graft path + */ - if(need_graft_path(dentry)) { + if (need_graft_path(dentry)) { /* setup_obb_dentry() - * The lower_path will be stored to the dentry's orig_path + * The lower_path will be stored to the dentry's orig_path * and the base obbpath will be copyed to the lower_path variable. * if an error returned, there's no change in the lower_path - * returns: -ERRNO if error (0: no error) */ + * returns: -ERRNO if error (0: no error) + */ err = setup_obb_dentry(dentry, &lower_path); - if(err) { + if (err) { /* if the sbi->obbpath is not available, we can optionally * setup the lower_path with its orig_path. * but, the current implementation just returns an error * because the sdcard daemon also regards this case as - * a lookup fail. */ - printk(KERN_INFO "sdcardfs: base obbpath is not available\n"); + * a lookup fail. + */ + pr_info("sdcardfs: base obbpath is not available\n"); sdcardfs_put_reset_orig_path(dentry); goto out; } } sdcardfs_set_lower_path(dentry, &lower_path); - err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id); - if (err) /* path_put underlying path on error */ + ret_dentry = + __sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id); + if (IS_ERR(ret_dentry)) { + err = PTR_ERR(ret_dentry); + /* path_put underlying path on error */ sdcardfs_put_reset_lower_path(dentry); + } goto out; } @@ -306,11 +365,14 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, goto out; /* instatiate a new negative dentry */ - lower_dentry = d_lookup(lower_dir_dentry, name); + dname.name = name->name; + dname.len = name->len; + dname.hash = full_name_hash(dname.name, dname.len); + lower_dentry = d_lookup(lower_dir_dentry, &dname); if (lower_dentry) goto setup_lower; - lower_dentry = d_alloc(lower_dir_dentry, name); + lower_dentry = d_alloc(lower_dir_dentry, &dname); if (!lower_dentry) { err = -ENOMEM; goto out; @@ -331,14 +393,16 @@ setup_lower: err = 0; out: - return ERR_PTR(err); + if (err) + return ERR_PTR(err); + return ret_dentry; } /* * On success: - * fills dentry object appropriate values and returns NULL. + * fills dentry object appropriate values and returns NULL. * On fail (== error) - * returns error ptr + * returns error ptr * * @dir : Parent inode. It is locked (dir->i_mutex) * @dentry : Target dentry to lookup. we should set each of fields. @@ -355,13 +419,10 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { ret = ERR_PTR(-EACCES); - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); goto out_err; - } + } /* save current_cred and override it */ OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); @@ -377,9 +438,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, SDCARDFS_I(dir)->userid); if (IS_ERR(ret)) - { goto out; - } if (ret) dentry = ret; if (d_inode(dentry)) { diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 7a8eae29e44d..953d2156d2e9 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -29,7 +29,7 @@ enum { Opt_gid, Opt_debug, Opt_mask, - Opt_multiuser, // May need? + Opt_multiuser, Opt_userid, Opt_reserved_mb, Opt_err, @@ -72,6 +72,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, while ((p = strsep(&options, ",")) != NULL) { int token; + if (!*p) continue; @@ -116,19 +117,17 @@ static int parse_options(struct super_block *sb, char *options, int silent, break; /* unknown option */ default: - if (!silent) { - printk( KERN_ERR "Unrecognized mount option \"%s\" " - "or missing value", p); - } + if (!silent) + pr_err("Unrecognized mount option \"%s\" or missing value", p); return -EINVAL; } } if (*debug) { - printk( KERN_INFO "sdcardfs : options - debug:%d\n", *debug); - printk( KERN_INFO "sdcardfs : options - uid:%d\n", + pr_info("sdcardfs : options - debug:%d\n", *debug); + pr_info("sdcardfs : options - uid:%d\n", opts->fs_low_uid); - printk( KERN_INFO "sdcardfs : options - gid:%d\n", + pr_info("sdcardfs : options - gid:%d\n", opts->fs_low_gid); } @@ -148,6 +147,7 @@ int parse_options_remount(struct super_block *sb, char *options, int silent, while ((p = strsep(&options, ",")) != NULL) { int token; + if (!*p) continue; @@ -173,22 +173,20 @@ int parse_options_remount(struct super_block *sb, char *options, int silent, case Opt_fsuid: case Opt_fsgid: case Opt_reserved_mb: - printk( KERN_WARNING "Option \"%s\" can't be changed during remount\n", p); + pr_warn("Option \"%s\" can't be changed during remount\n", p); break; /* unknown option */ default: - if (!silent) { - printk( KERN_ERR "Unrecognized mount option \"%s\" " - "or missing value", p); - } + if (!silent) + pr_err("Unrecognized mount option \"%s\" or missing value", p); return -EINVAL; } } if (debug) { - printk( KERN_INFO "sdcardfs : options - debug:%d\n", debug); - printk( KERN_INFO "sdcardfs : options - gid:%d\n", vfsopts->gid); - printk( KERN_INFO "sdcardfs : options - mask:%d\n", vfsopts->mask); + pr_info("sdcardfs : options - debug:%d\n", debug); + pr_info("sdcardfs : options - gid:%d\n", vfsopts->gid); + pr_info("sdcardfs : options - mask:%d\n", vfsopts->mask); } return 0; @@ -223,8 +221,8 @@ static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb) #endif DEFINE_MUTEX(sdcardfs_super_list_lock); -LIST_HEAD(sdcardfs_super_list); EXPORT_SYMBOL_GPL(sdcardfs_super_list_lock); +LIST_HEAD(sdcardfs_super_list); EXPORT_SYMBOL_GPL(sdcardfs_super_list); /* @@ -242,31 +240,30 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, struct sdcardfs_vfsmount_options *mnt_opt = mnt->data; struct inode *inode; - printk(KERN_INFO "sdcardfs version 2.0\n"); + pr_info("sdcardfs version 2.0\n"); if (!dev_name) { - printk(KERN_ERR - "sdcardfs: read_super: missing dev_name argument\n"); + pr_err("sdcardfs: read_super: missing dev_name argument\n"); err = -EINVAL; goto out; } - printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name); - printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data); - printk(KERN_INFO "sdcardfs: mnt -> %p\n", mnt); + pr_info("sdcardfs: dev_name -> %s\n", dev_name); + pr_info("sdcardfs: options -> %s\n", (char *)raw_data); + pr_info("sdcardfs: mnt -> %p\n", mnt); /* parse lower path */ err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &lower_path); if (err) { - printk(KERN_ERR "sdcardfs: error accessing lower directory '%s'\n", dev_name); + pr_err("sdcardfs: error accessing lower directory '%s'\n", dev_name); goto out; } /* allocate superblock private data */ sb->s_fs_info = kzalloc(sizeof(struct sdcardfs_sb_info), GFP_KERNEL); if (!SDCARDFS_SB(sb)) { - printk(KERN_CRIT "sdcardfs: read_super: out of memory\n"); + pr_crit("sdcardfs: read_super: out of memory\n"); err = -ENOMEM; goto out_free; } @@ -275,7 +272,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, /* parse options */ err = parse_options(sb, raw_data, silent, &debug, mnt_opt, &sb_info->options); if (err) { - printk(KERN_ERR "sdcardfs: invalid options\n"); + pr_err("sdcardfs: invalid options\n"); goto out_freesbi; } @@ -328,14 +325,15 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, /* setup permission policy */ sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); mutex_lock(&sdcardfs_super_list_lock); - if(sb_info->options.multiuser) { - setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root)); + if (sb_info->options.multiuser) { + setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, + sb_info->options.fs_user_id, AID_ROOT, + false, d_inode(sb->s_root)); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); - /*err = prepare_dir(sb_info->obbpath_s, - sb_info->options.fs_low_uid, - sb_info->options.fs_low_gid, 00755);*/ } else { - setup_derived_state(d_inode(sb->s_root), PERM_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root)); + setup_derived_state(d_inode(sb->s_root), PERM_ROOT, + sb_info->options.fs_user_id, AID_ROOT, + false, d_inode(sb->s_root)); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } fixup_tmp_permissions(d_inode(sb->s_root)); @@ -344,7 +342,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, mutex_unlock(&sdcardfs_super_list_lock); if (!silent) - printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n", + pr_info("sdcardfs: mounted on top of %s type %s\n", dev_name, lower_sb->s_type->name); goto out; /* all is well */ @@ -368,8 +366,10 @@ out: /* A feature which supports mount_nodev() with options */ static struct dentry *mount_nodev_with_options(struct vfsmount *mnt, - struct file_system_type *fs_type, int flags, const char *dev_name, void *data, - int (*fill_super)(struct vfsmount *, struct super_block *, const char *, void *, int)) + struct file_system_type *fs_type, int flags, + const char *dev_name, void *data, + int (*fill_super)(struct vfsmount *, struct super_block *, + const char *, void *, int)) { int error; @@ -401,19 +401,22 @@ static struct dentry *sdcardfs_mount(struct vfsmount *mnt, raw_data, sdcardfs_read_super); } -static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, int flags, - const char *dev_name, void *raw_data) +static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data) { WARN(1, "sdcardfs does not support mount. Use mount2.\n"); return ERR_PTR(-EINVAL); } -void *sdcardfs_alloc_mnt_data(void) { +void *sdcardfs_alloc_mnt_data(void) +{ return kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); } -void sdcardfs_kill_sb(struct super_block *sb) { +void sdcardfs_kill_sb(struct super_block *sb) +{ struct sdcardfs_sb_info *sbi; + if (sb->s_magic == SDCARDFS_SUPER_MAGIC) { sbi = SDCARDFS_SB(sb); mutex_lock(&sdcardfs_super_list_lock); @@ -432,6 +435,7 @@ static struct file_system_type sdcardfs_fs_type = { .kill_sb = sdcardfs_kill_sb, .fs_flags = 0, }; +MODULE_ALIAS_FS(SDCARDFS_NAME); static int __init init_sdcardfs_fs(void) { @@ -467,10 +471,15 @@ static void __exit exit_sdcardfs_fs(void) pr_info("Completed sdcardfs module unload\n"); } -MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University" - " (http://www.fsl.cs.sunysb.edu/)"); -MODULE_DESCRIPTION("Wrapfs " SDCARDFS_VERSION - " (http://wrapfs.filesystems.org/)"); +/* Original wrapfs authors */ +MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University (http://www.fsl.cs.sunysb.edu/)"); + +/* Original sdcardfs authors */ +MODULE_AUTHOR("Woojoong Lee, Daeho Jeong, Kitae Lee, Yeongjin Gil System Memory Lab., Samsung Electronics"); + +/* Current maintainer */ +MODULE_AUTHOR("Daniel Rosenberg, Google"); +MODULE_DESCRIPTION("Sdcardfs " SDCARDFS_VERSION); MODULE_LICENSE("GPL"); module_init(init_sdcardfs_fs); diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c index e21f64675a80..0d4089c62c3a 100644 --- a/fs/sdcardfs/mmap.c +++ b/fs/sdcardfs/mmap.c @@ -48,34 +48,55 @@ static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return err; } +static int sdcardfs_page_mkwrite(struct vm_area_struct *vma, + struct vm_fault *vmf) +{ + int err = 0; + struct file *file, *lower_file; + const struct vm_operations_struct *lower_vm_ops; + struct vm_area_struct lower_vma; + + memcpy(&lower_vma, vma, sizeof(struct vm_area_struct)); + file = lower_vma.vm_file; + lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops; + BUG_ON(!lower_vm_ops); + if (!lower_vm_ops->page_mkwrite) + goto out; + + lower_file = sdcardfs_lower_file(file); + /* + * XXX: vm_ops->page_mkwrite may be called in parallel. + * Because we have to resort to temporarily changing the + * vma->vm_file to point to the lower file, a concurrent + * invocation of sdcardfs_page_mkwrite could see a different + * value. In this workaround, we keep a different copy of the + * vma structure in our stack, so we never expose a different + * value of the vma->vm_file called to us, even temporarily. + * A better fix would be to change the calling semantics of + * ->page_mkwrite to take an explicit file pointer. + */ + lower_vma.vm_file = lower_file; + err = lower_vm_ops->page_mkwrite(&lower_vma, vmf); +out: + return err; +} + static ssize_t sdcardfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t pos) { /* - * This function returns zero on purpose in order to support direct IO. - * __dentry_open checks a_ops->direct_IO and returns EINVAL if it is null. - * - * However, this function won't be called by certain file operations - * including generic fs functions. * reads and writes are delivered to - * the lower file systems and the direct IOs will be handled by them. - * - * NOTE: exceptionally, on the recent kernels (since Linux 3.8.x), - * swap_writepage invokes this function directly. + * This function should never be called directly. We need it + * to exist, to get past a check in open_check_o_direct(), + * which is called from do_last(). */ - printk(KERN_INFO "%s, operation is not supported\n", __func__); - return 0; + return -EINVAL; } -/* - * XXX: the default address_space_ops for sdcardfs is empty. We cannot set - * our inode->i_mapping->a_ops to NULL because too many code paths expect - * the a_ops vector to be non-NULL. - */ const struct address_space_operations sdcardfs_aops = { - /* empty on purpose */ .direct_IO = sdcardfs_direct_IO, }; const struct vm_operations_struct sdcardfs_vm_ops = { .fault = sdcardfs_fault, + .page_mkwrite = sdcardfs_page_mkwrite, }; diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h index 52bc20080904..2e89b5872314 100644 --- a/fs/sdcardfs/multiuser.h +++ b/fs/sdcardfs/multiuser.h @@ -28,22 +28,17 @@ typedef uid_t userid_t; typedef uid_t appid_t; -static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) { - return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET); +static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) +{ + return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET); } -static inline gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) { - if (app_id >= AID_APP_START && app_id <= AID_APP_END) { - return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START); - } else { - return -1; - } +static inline gid_t multiuser_get_cache_gid(uid_t uid) +{ + return uid - AID_APP_START + AID_CACHE_GID_START; } -static inline gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) { - if (app_id >= AID_APP_START && app_id <= AID_APP_END) { - return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START); - } else { - return -1; - } +static inline gid_t multiuser_get_ext_gid(uid_t uid) +{ + return uid - AID_APP_START + AID_EXT_GID_START; } diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index d96fcde041cc..89196e31073e 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -20,6 +20,7 @@ #include "sdcardfs.h" #include <linux/hashtable.h> +#include <linux/ctype.h> #include <linux/delay.h> #include <linux/radix-tree.h> #include <linux/dcache.h> @@ -44,13 +45,24 @@ static DEFINE_HASHTABLE(ext_to_groupid, 8); static struct kmem_cache *hashtable_entry_cachep; -static void inline qstr_init(struct qstr *q, const char *name) { +static unsigned int full_name_case_hash(const unsigned char *name, unsigned int len) +{ + unsigned long hash = init_name_hash(); + + while (len--) + hash = partial_name_hash(tolower(*name++), hash); + return end_name_hash(hash); +} + +static inline void qstr_init(struct qstr *q, const char *name) +{ q->name = name; q->len = strlen(q->name); - q->hash = full_name_hash(q->name, q->len); + q->hash = full_name_case_hash(q->name, q->len); } -static inline int qstr_copy(const struct qstr *src, struct qstr *dest) { +static inline int qstr_copy(const struct qstr *src, struct qstr *dest) +{ dest->name = kstrdup(src->name, GFP_KERNEL); dest->hash_len = src->hash_len; return !!dest->name; @@ -78,6 +90,7 @@ static appid_t __get_appid(const struct qstr *key) appid_t get_appid(const char *key) { struct qstr q; + qstr_init(&q, key); return __get_appid(&q); } @@ -103,6 +116,7 @@ static appid_t __get_ext_gid(const struct qstr *key) appid_t get_ext_gid(const char *key) { struct qstr q; + qstr_init(&q, key); return __get_ext_gid(&q); } @@ -133,8 +147,10 @@ appid_t is_excluded(const char *key, userid_t user) /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access - * even further, such as enforcing that apps hold sdcard_rw. */ -int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name) { + * even further, such as enforcing that apps hold sdcard_rw. + */ +int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name) +{ struct qstr q_autorun = QSTR_LITERAL("autorun.inf"); struct qstr q__android_secure = QSTR_LITERAL(".android_secure"); struct qstr q_android_secure = QSTR_LITERAL("android_secure"); @@ -149,26 +165,26 @@ int check_caller_access_to_name(struct inode *parent_node, const struct qstr *na } /* Root always has access; access for any other UIDs should always - * be controlled through packages.list. */ - if (from_kuid(&init_user_ns, current_fsuid()) == 0) { + * be controlled through packages.list. + */ + if (from_kuid(&init_user_ns, current_fsuid()) == 0) return 1; - } /* No extra permissions to enforce */ return 1; } /* This function is used when file opening. The open flags must be - * checked before calling check_caller_access_to_name() */ -int open_flags_to_access_mode(int open_flags) { - if((open_flags & O_ACCMODE) == O_RDONLY) { + * checked before calling check_caller_access_to_name() + */ +int open_flags_to_access_mode(int open_flags) +{ + if ((open_flags & O_ACCMODE) == O_RDONLY) return 0; /* R_OK */ - } else if ((open_flags & O_ACCMODE) == O_WRONLY) { + if ((open_flags & O_ACCMODE) == O_WRONLY) return 1; /* W_OK */ - } else { - /* Probably O_RDRW, but treat as default to be safe */ + /* Probably O_RDRW, but treat as default to be safe */ return 1; /* R_OK | W_OK */ - } } static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key, @@ -178,6 +194,8 @@ static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key, GFP_KERNEL); if (!ret) return NULL; + INIT_HLIST_NODE(&ret->dlist); + INIT_HLIST_NODE(&ret->hlist); if (!qstr_copy(key, &ret->key)) { kmem_cache_free(hashtable_entry_cachep, ret); @@ -249,8 +267,7 @@ static void fixup_all_perms_name(const struct qstr *key) struct sdcardfs_sb_info *sbinfo; struct limit_search limit = { .flags = BY_NAME, - .name = key->name, - .length = key->len, + .name = QSTR_INIT(key->name, key->len), }; list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { if (sbinfo_has_sdcard_magic(sbinfo)) @@ -263,8 +280,7 @@ static void fixup_all_perms_name_userid(const struct qstr *key, userid_t userid) struct sdcardfs_sb_info *sbinfo; struct limit_search limit = { .flags = BY_NAME | BY_USERID, - .name = key->name, - .length = key->len, + .name = QSTR_INIT(key->name, key->len), .userid = userid, }; list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { @@ -326,7 +342,6 @@ static int insert_userid_exclude_entry(const struct qstr *key, userid_t value) static void free_hashtable_entry(struct hashtable_entry *entry) { kfree(entry->key.name); - hash_del_rcu(&entry->dlist); kmem_cache_free(hashtable_entry_cachep, entry); } @@ -361,7 +376,6 @@ static void remove_packagelist_entry(const struct qstr *key) remove_packagelist_entry_locked(key); fixup_all_perms_name(key); mutex_unlock(&sdcardfs_super_list_lock); - return; } static void remove_ext_gid_entry_locked(const struct qstr *key, gid_t group) @@ -384,7 +398,6 @@ static void remove_ext_gid_entry(const struct qstr *key, gid_t group) mutex_lock(&sdcardfs_super_list_lock); remove_ext_gid_entry_locked(key, group); mutex_unlock(&sdcardfs_super_list_lock); - return; } static void remove_userid_all_entry_locked(userid_t userid) @@ -412,7 +425,6 @@ static void remove_userid_all_entry(userid_t userid) remove_userid_all_entry_locked(userid); fixup_all_perms_userid(userid); mutex_unlock(&sdcardfs_super_list_lock); - return; } static void remove_userid_exclude_entry_locked(const struct qstr *key, userid_t userid) @@ -437,7 +449,6 @@ static void remove_userid_exclude_entry(const struct qstr *key, userid_t userid) remove_userid_exclude_entry_locked(key, userid); fixup_all_perms_name_userid(key, userid); mutex_unlock(&sdcardfs_super_list_lock); - return; } static void packagelist_destroy(void) @@ -446,6 +457,7 @@ static void packagelist_destroy(void) struct hlist_node *h_t; HLIST_HEAD(free_list); int i; + mutex_lock(&sdcardfs_super_list_lock); hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) { hash_del_rcu(&hash_cur->hlist); @@ -459,7 +471,7 @@ static void packagelist_destroy(void) hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) free_hashtable_entry(hash_cur); mutex_unlock(&sdcardfs_super_list_lock); - printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n"); + pr_info("sdcardfs: destroyed packagelist pkgld\n"); } #define SDCARDFS_CONFIGFS_ATTR(_pfx, _name) \ @@ -575,7 +587,8 @@ static ssize_t package_details_clear_userid_store(struct config_item *item, static void package_details_release(struct config_item *item) { struct package_details *package_details = to_package_details(item); - printk(KERN_INFO "sdcardfs: removing %s\n", package_details->name.name); + + pr_info("sdcardfs: removing %s\n", package_details->name.name); remove_packagelist_entry(&package_details->name); kfree(package_details->name.name); kfree(package_details); @@ -593,7 +606,7 @@ static struct configfs_attribute *package_details_attrs[] = { }; static struct configfs_item_operations package_details_item_ops = { - .release = package_details_release, + .release = package_details_release, }; static struct config_item_type package_appid_type = { @@ -627,7 +640,7 @@ static void extension_details_release(struct config_item *item) { struct extension_details *extension_details = to_extension_details(item); - printk(KERN_INFO "sdcardfs: No longer mapping %s files to gid %d\n", + pr_info("sdcardfs: No longer mapping %s files to gid %d\n", extension_details->name.name, extension_details->num); remove_ext_gid_entry(&extension_details->name, extension_details->num); kfree(extension_details->name.name); @@ -649,6 +662,7 @@ static struct config_item *extension_details_make_item(struct config_group *grou struct extension_details *extension_details = kzalloc(sizeof(struct extension_details), GFP_KERNEL); const char *tmp; int ret; + if (!extension_details) return ERR_PTR(-ENOMEM); @@ -703,7 +717,8 @@ static struct config_group *extensions_make_group(struct config_group *group, co static void extensions_drop_group(struct config_group *group, struct config_item *item) { struct extensions_value *value = to_extensions_value(item); - printk(KERN_INFO "sdcardfs: No longer mapping any files to gid %d\n", value->num); + + pr_info("sdcardfs: No longer mapping any files to gid %d\n", value->num); kfree(value); } @@ -837,14 +852,14 @@ static int configfs_sdcardfs_init(void) { int ret, i; struct configfs_subsystem *subsys = &sdcardfs_packages; - for (i = 0; sd_default_groups[i]; i++) { + + for (i = 0; sd_default_groups[i]; i++) config_group_init(sd_default_groups[i]); - } config_group_init(&subsys->su_group); mutex_init(&subsys->su_mutex); ret = configfs_register_subsystem(subsys); if (ret) { - printk(KERN_ERR "Error %d while registering subsystem %s\n", + pr_err("Error %d while registering subsystem %s\n", ret, subsys->su_group.cg_item.ci_namebuf); } @@ -862,18 +877,17 @@ int packagelist_init(void) kmem_cache_create("packagelist_hashtable_entry", sizeof(struct hashtable_entry), 0, 0, NULL); if (!hashtable_entry_cachep) { - printk(KERN_ERR "sdcardfs: failed creating pkgl_hashtable entry slab cache\n"); + pr_err("sdcardfs: failed creating pkgl_hashtable entry slab cache\n"); return -ENOMEM; } configfs_sdcardfs_init(); - return 0; + return 0; } void packagelist_exit(void) { configfs_sdcardfs_exit(); packagelist_destroy(); - if (hashtable_entry_cachep) - kmem_cache_destroy(hashtable_entry_cachep); + kmem_cache_destroy(hashtable_entry_cachep); } diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index f3cced313108..2b67b9a8ef9f 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -29,6 +29,7 @@ #include <linux/dcache.h> #include <linux/file.h> #include <linux/fs.h> +#include <linux/aio.h> #include <linux/mm.h> #include <linux/mount.h> #include <linux/namei.h> @@ -52,7 +53,7 @@ #define SDCARDFS_ROOT_INO 1 /* useful for tracking code reachability */ -#define UDBG printk(KERN_DEFAULT "DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__) +#define UDBG pr_default("DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__) #define SDCARDFS_DIRENT_SIZE 256 @@ -86,54 +87,56 @@ } while (0) /* OVERRIDE_CRED() and REVERT_CRED() - * OVERRID_CRED() - * backup original task->cred - * and modifies task->cred->fsuid/fsgid to specified value. + * OVERRIDE_CRED() + * backup original task->cred + * and modifies task->cred->fsuid/fsgid to specified value. * REVERT_CRED() - * restore original task->cred->fsuid/fsgid. + * restore original task->cred->fsuid/fsgid. * These two macro should be used in pair, and OVERRIDE_CRED() should be * placed at the beginning of a function, right after variable declaration. */ #define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \ + do { \ saved_cred = override_fsids(sdcardfs_sbi, info); \ - if (!saved_cred) { return -ENOMEM; } + if (!saved_cred) \ + return -ENOMEM; \ + } while (0) #define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \ + do { \ saved_cred = override_fsids(sdcardfs_sbi, info); \ - if (!saved_cred) { return ERR_PTR(-ENOMEM); } + if (!saved_cred) \ + return ERR_PTR(-ENOMEM); \ + } while (0) #define REVERT_CRED(saved_cred) revert_fsids(saved_cred) -#define DEBUG_CRED() \ - printk("KAKJAGI: %s:%d fsuid %d fsgid %d\n", \ - __FUNCTION__, __LINE__, \ - (int)current->cred->fsuid, \ - (int)current->cred->fsgid); - /* Android 5.0 support */ /* Permission mode for a specific node. Controls how file permissions - * are derived for children nodes. */ + * are derived for children nodes. + */ typedef enum { - /* Nothing special; this node should just inherit from its parent. */ - PERM_INHERIT, - /* This node is one level above a normal root; used for legacy layouts - * which use the first level to represent user_id. */ - PERM_PRE_ROOT, - /* This node is "/" */ - PERM_ROOT, - /* This node is "/Android" */ - PERM_ANDROID, - /* This node is "/Android/data" */ - PERM_ANDROID_DATA, - /* This node is "/Android/obb" */ - PERM_ANDROID_OBB, - /* This node is "/Android/media" */ - PERM_ANDROID_MEDIA, - /* This node is "/Android/[data|media|obb]/[package]" */ - PERM_ANDROID_PACKAGE, - /* This node is "/Android/[data|media|obb]/[package]/cache" */ - PERM_ANDROID_PACKAGE_CACHE, + /* Nothing special; this node should just inherit from its parent. */ + PERM_INHERIT, + /* This node is one level above a normal root; used for legacy layouts + * which use the first level to represent user_id. + */ + PERM_PRE_ROOT, + /* This node is "/" */ + PERM_ROOT, + /* This node is "/Android" */ + PERM_ANDROID, + /* This node is "/Android/data" */ + PERM_ANDROID_DATA, + /* This node is "/Android/obb" */ + PERM_ANDROID_OBB, + /* This node is "/Android/media" */ + PERM_ANDROID_MEDIA, + /* This node is "/Android/[data|media|obb]/[package]" */ + PERM_ANDROID_PACKAGE, + /* This node is "/Android/[data|media|obb]/[package]/cache" */ + PERM_ANDROID_PACKAGE_CACHE, } perm_t; struct sdcardfs_sb_info; @@ -141,9 +144,9 @@ struct sdcardfs_mount_options; struct sdcardfs_inode_info; /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ -const struct cred * override_fsids(struct sdcardfs_sb_info* sbi, struct sdcardfs_inode_info *info); +const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info); /* Do not directly use this function, use REVERT_CRED() instead. */ -void revert_fsids(const struct cred * old_cred); +void revert_fsids(const struct cred *old_cred); /* operations vectors defined in specific files */ extern const struct file_operations sdcardfs_main_fops; @@ -220,7 +223,8 @@ struct sdcardfs_sb_info { struct super_block *sb; struct super_block *lower_sb; /* derived perm policy : some of options have been added - * to sdcardfs_mount_options (Android 4.4 support) */ + * to sdcardfs_mount_options (Android 4.4 support) + */ struct sdcardfs_mount_options options; spinlock_t lock; /* protects obbpath */ char *obbpath_s; @@ -331,7 +335,7 @@ static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \ { \ struct path pname; \ spin_lock(&SDCARDFS_D(dent)->lock); \ - if(SDCARDFS_D(dent)->pname.dentry) { \ + if (SDCARDFS_D(dent)->pname.dentry) { \ pathcpy(&pname, &SDCARDFS_D(dent)->pname); \ SDCARDFS_D(dent)->pname.dentry = NULL; \ SDCARDFS_D(dent)->pname.mnt = NULL; \ @@ -347,17 +351,17 @@ SDCARDFS_DENT_FUNC(orig_path) static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo) { - return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC; + return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC; } /* grab a refererence if we aren't linking to ourself */ static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top) { struct inode *old_top = NULL; + BUG_ON(IS_ERR_OR_NULL(top)); - if (info->top && info->top != &info->vfs_inode) { + if (info->top && info->top != &info->vfs_inode) old_top = info->top; - } if (top != &info->vfs_inode) igrab(top); info->top = top; @@ -367,11 +371,11 @@ static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top) static inline struct inode *grab_top(struct sdcardfs_inode_info *info) { struct inode *top = info->top; - if (top) { + + if (top) return igrab(top); - } else { + else return NULL; - } } static inline void release_top(struct sdcardfs_inode_info *info) @@ -379,21 +383,24 @@ static inline void release_top(struct sdcardfs_inode_info *info) iput(info->top); } -static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) { +static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) +{ struct sdcardfs_vfsmount_options *opts = mnt->data; - if (opts->gid == AID_SDCARD_RW) { + if (opts->gid == AID_SDCARD_RW) /* As an optimization, certain trusted system components only run * as owner but operate across all users. Since we're now handing * out the sdcard_rw GID only to trusted apps, we're okay relaxing * the user boundary enforcement for the default view. The UIDs - * assigned to app directories are still multiuser aware. */ + * assigned to app directories are still multiuser aware. + */ return AID_SDCARD_RW; - } else { + else return multiuser_get_uid(info->userid, opts->gid); - } } -static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) { + +static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) +{ int owner_mode; int filtered_mode; struct sdcardfs_vfsmount_options *opts = mnt->data; @@ -402,17 +409,18 @@ static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *inf if (info->perm == PERM_PRE_ROOT) { /* Top of multi-user view should always be visible to ensure - * secondary users can traverse inside. */ + * secondary users can traverse inside. + */ visible_mode = 0711; } else if (info->under_android) { /* Block "other" access to Android directories, since only apps * belonging to a specific user should be in there; we still - * leave +x open for the default view. */ - if (opts->gid == AID_SDCARD_RW) { + * leave +x open for the default view. + */ + if (opts->gid == AID_SDCARD_RW) visible_mode = visible_mode & ~0006; - } else { + else visible_mode = visible_mode & ~0007; - } } owner_mode = info->lower_inode->i_mode & 0700; filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6)); @@ -437,7 +445,7 @@ static inline void sdcardfs_get_real_lower(const struct dentry *dent, /* in case of a local obb dentry * the orig_path should be returned */ - if(has_graft_path(dent)) + if (has_graft_path(dent)) sdcardfs_get_orig_path(dent, real_lower); else sdcardfs_get_lower_path(dent, real_lower); @@ -446,7 +454,7 @@ static inline void sdcardfs_get_real_lower(const struct dentry *dent, static inline void sdcardfs_put_real_lower(const struct dentry *dent, struct path *real_lower) { - if(has_graft_path(dent)) + if (has_graft_path(dent)) sdcardfs_put_orig_path(dent, real_lower); else sdcardfs_put_lower_path(dent, real_lower); @@ -459,7 +467,7 @@ extern struct list_head sdcardfs_super_list; extern appid_t get_appid(const char *app_name); extern appid_t get_ext_gid(const char *app_name); extern appid_t is_excluded(const char *app_name, userid_t userid); -extern int check_caller_access_to_name(struct inode *parent_node, const struct qstr* name); +extern int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name); extern int open_flags_to_access_mode(int open_flags); extern int packagelist_init(void); extern void packagelist_exit(void); @@ -469,8 +477,7 @@ extern void packagelist_exit(void); #define BY_USERID (1 << 1) struct limit_search { unsigned int flags; - const char *name; - size_t length; + struct qstr name; userid_t userid; }; @@ -478,12 +485,10 @@ extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t useri uid_t uid, bool under_android, struct inode *top); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name); -extern void drop_recursive(struct dentry *parent); -extern void fixup_top_recursive(struct dentry *parent); extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit); extern void update_derived_permission_lock(struct dentry *dentry); -void fixup_lower_ownership(struct dentry* dentry, const char *name); +void fixup_lower_ownership(struct dentry *dentry, const char *name); extern int need_graft_path(struct dentry *dentry); extern int is_base_obbpath(struct dentry *dentry); extern int is_obbpath_invalid(struct dentry *dentry); @@ -493,6 +498,7 @@ extern int setup_obb_dentry(struct dentry *dentry, struct path *lower_path); static inline struct dentry *lock_parent(struct dentry *dentry) { struct dentry *dir = dget_parent(dentry); + mutex_lock_nested(&d_inode(dir)->i_mutex, I_MUTEX_PARENT); return dir; } @@ -611,6 +617,11 @@ static inline bool str_case_eq(const char *s1, const char *s2) return !strcasecmp(s1, s2); } +static inline bool str_n_case_eq(const char *s1, const char *s2, size_t len) +{ + return !strncasecmp(s1, s2, len); +} + static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2) { return q1->len == q2->len && str_case_eq(q1->name, q2->name); diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index edda32b68dc0..a3393e959c63 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -36,7 +36,7 @@ static void sdcardfs_put_super(struct super_block *sb) if (!spd) return; - if(spd->obbpath_s) { + if (spd->obbpath_s) { kfree(spd->obbpath_s); path_put(&spd->obbpath); } @@ -64,7 +64,7 @@ static int sdcardfs_statfs(struct dentry *dentry, struct kstatfs *buf) if (sbi->options.reserved_mb) { /* Invalid statfs informations. */ if (buf->f_bsize == 0) { - printk(KERN_ERR "Returned block size is zero.\n"); + pr_err("Returned block size is zero.\n"); return -EINVAL; } @@ -100,8 +100,7 @@ static int sdcardfs_remount_fs(struct super_block *sb, int *flags, char *options * SILENT, but anything else left over is an error. */ if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT)) != 0) { - printk(KERN_ERR - "sdcardfs: remount flags 0x%x unsupported\n", *flags); + pr_err("sdcardfs: remount flags 0x%x unsupported\n", *flags); err = -EINVAL; } @@ -125,29 +124,33 @@ static int sdcardfs_remount_fs2(struct vfsmount *mnt, struct super_block *sb, * SILENT, but anything else left over is an error. */ if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT | MS_REMOUNT)) != 0) { - printk(KERN_ERR - "sdcardfs: remount flags 0x%x unsupported\n", *flags); + pr_err("sdcardfs: remount flags 0x%x unsupported\n", *flags); err = -EINVAL; } - printk(KERN_INFO "Remount options were %s for vfsmnt %p.\n", options, mnt); + pr_info("Remount options were %s for vfsmnt %p.\n", options, mnt); err = parse_options_remount(sb, options, *flags & ~MS_SILENT, mnt->data); return err; } -static void* sdcardfs_clone_mnt_data(void *data) { - struct sdcardfs_vfsmount_options* opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); - struct sdcardfs_vfsmount_options* old = data; - if(!opt) return NULL; +static void *sdcardfs_clone_mnt_data(void *data) +{ + struct sdcardfs_vfsmount_options *opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); + struct sdcardfs_vfsmount_options *old = data; + + if (!opt) + return NULL; opt->gid = old->gid; opt->mask = old->mask; return opt; } -static void sdcardfs_copy_mnt_data(void *data, void *newdata) { - struct sdcardfs_vfsmount_options* old = data; - struct sdcardfs_vfsmount_options* new = newdata; +static void sdcardfs_copy_mnt_data(void *data, void *newdata) +{ + struct sdcardfs_vfsmount_options *old = data; + struct sdcardfs_vfsmount_options *new = newdata; + old->gid = new->gid; old->mask = new->mask; } @@ -218,8 +221,7 @@ int sdcardfs_init_inode_cache(void) /* sdcardfs inode cache destructor */ void sdcardfs_destroy_inode_cache(void) { - if (sdcardfs_inode_cachep) - kmem_cache_destroy(sdcardfs_inode_cachep); + kmem_cache_destroy(sdcardfs_inode_cachep); } /* @@ -235,7 +237,8 @@ static void sdcardfs_umount_begin(struct super_block *sb) lower_sb->s_op->umount_begin(lower_sb); } -static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, struct dentry *root) +static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, + struct dentry *root) { struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb); struct sdcardfs_mount_options *opts = &sbi->options; @@ -248,7 +251,7 @@ static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, struc if (vfsopts->gid != 0) seq_printf(m, ",gid=%u", vfsopts->gid); if (opts->multiuser) - seq_printf(m, ",multiuser"); + seq_puts(m, ",multiuser"); if (vfsopts->mask) seq_printf(m, ",mask=%u", vfsopts->mask); if (opts->fs_user_id) diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig index ffb093e72b6c..6dd158a216f4 100644 --- a/fs/squashfs/Kconfig +++ b/fs/squashfs/Kconfig @@ -26,34 +26,6 @@ config SQUASHFS If unsure, say N. choice - prompt "File decompression options" - depends on SQUASHFS - help - Squashfs now supports two options for decompressing file - data. Traditionally Squashfs has decompressed into an - intermediate buffer and then memcopied it into the page cache. - Squashfs now supports the ability to decompress directly into - the page cache. - - If unsure, select "Decompress file data into an intermediate buffer" - -config SQUASHFS_FILE_CACHE - bool "Decompress file data into an intermediate buffer" - help - Decompress file data into an intermediate buffer and then - memcopy it into the page cache. - -config SQUASHFS_FILE_DIRECT - bool "Decompress files directly into the page cache" - help - Directly decompress file data into the page cache. - Doing so can significantly improve performance because - it eliminates a memcpy and it also removes the lock contention - on the single buffer. - -endchoice - -choice prompt "Decompressor parallelisation options" depends on SQUASHFS help diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile index 246a6f329d89..fe51f1507ed1 100644 --- a/fs/squashfs/Makefile +++ b/fs/squashfs/Makefile @@ -5,8 +5,7 @@ obj-$(CONFIG_SQUASHFS) += squashfs.o squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o squashfs-y += namei.o super.o symlink.o decompressor.o -squashfs-$(CONFIG_SQUASHFS_FILE_CACHE) += file_cache.o -squashfs-$(CONFIG_SQUASHFS_FILE_DIRECT) += file_direct.o page_actor.o +squashfs-y += file_direct.o page_actor.o squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c index 0cea9b9236d0..2eb66decc5ab 100644 --- a/fs/squashfs/block.c +++ b/fs/squashfs/block.c @@ -28,9 +28,12 @@ #include <linux/fs.h> #include <linux/vfs.h> +#include <linux/bio.h> #include <linux/slab.h> #include <linux/string.h> +#include <linux/pagemap.h> #include <linux/buffer_head.h> +#include <linux/workqueue.h> #include "squashfs_fs.h" #include "squashfs_fs_sb.h" @@ -38,177 +41,434 @@ #include "decompressor.h" #include "page_actor.h" -/* - * Read the metadata block length, this is stored in the first two - * bytes of the metadata block. - */ -static struct buffer_head *get_block_length(struct super_block *sb, - u64 *cur_index, int *offset, int *length) +static struct workqueue_struct *squashfs_read_wq; + +struct squashfs_read_request { + struct super_block *sb; + u64 index; + int length; + int compressed; + int offset; + u64 read_end; + struct squashfs_page_actor *output; + enum { + SQUASHFS_COPY, + SQUASHFS_DECOMPRESS, + SQUASHFS_METADATA, + } data_processing; + bool synchronous; + + /* + * If the read is synchronous, it is possible to retrieve information + * about the request by setting these pointers. + */ + int *res; + int *bytes_read; + int *bytes_uncompressed; + + int nr_buffers; + struct buffer_head **bh; + struct work_struct offload; +}; + +struct squashfs_bio_request { + struct buffer_head **bh; + int nr_buffers; +}; + +static int squashfs_bio_submit(struct squashfs_read_request *req); + +int squashfs_init_read_wq(void) { - struct squashfs_sb_info *msblk = sb->s_fs_info; - struct buffer_head *bh; + squashfs_read_wq = create_workqueue("SquashFS read wq"); + return !!squashfs_read_wq; +} + +void squashfs_destroy_read_wq(void) +{ + flush_workqueue(squashfs_read_wq); + destroy_workqueue(squashfs_read_wq); +} + +static void free_read_request(struct squashfs_read_request *req, int error) +{ + if (!req->synchronous) + squashfs_page_actor_free(req->output, error); + if (req->res) + *(req->res) = error; + kfree(req->bh); + kfree(req); +} + +static void squashfs_process_blocks(struct squashfs_read_request *req) +{ + int error = 0; + int bytes, i, length; + struct squashfs_sb_info *msblk = req->sb->s_fs_info; + struct squashfs_page_actor *actor = req->output; + struct buffer_head **bh = req->bh; + int nr_buffers = req->nr_buffers; + + for (i = 0; i < nr_buffers; ++i) { + if (!bh[i]) + continue; + wait_on_buffer(bh[i]); + if (!buffer_uptodate(bh[i])) + error = -EIO; + } + if (error) + goto cleanup; + + if (req->data_processing == SQUASHFS_METADATA) { + /* Extract the length of the metadata block */ + if (req->offset != msblk->devblksize - 1) + length = *((u16 *)(bh[0]->b_data + req->offset)); + else { + length = bh[0]->b_data[req->offset]; + length |= bh[1]->b_data[0] << 8; + } + req->compressed = SQUASHFS_COMPRESSED(length); + req->data_processing = req->compressed ? SQUASHFS_DECOMPRESS + : SQUASHFS_COPY; + length = SQUASHFS_COMPRESSED_SIZE(length); + if (req->index + length + 2 > req->read_end) { + for (i = 0; i < nr_buffers; ++i) + put_bh(bh[i]); + kfree(bh); + req->length = length; + req->index += 2; + squashfs_bio_submit(req); + return; + } + req->length = length; + req->offset = (req->offset + 2) % PAGE_SIZE; + if (req->offset < 2) { + put_bh(bh[0]); + ++bh; + --nr_buffers; + } + } + if (req->bytes_read) + *(req->bytes_read) = req->length; - bh = sb_bread(sb, *cur_index); - if (bh == NULL) - return NULL; - - if (msblk->devblksize - *offset == 1) { - *length = (unsigned char) bh->b_data[*offset]; - put_bh(bh); - bh = sb_bread(sb, ++(*cur_index)); - if (bh == NULL) - return NULL; - *length |= (unsigned char) bh->b_data[0] << 8; - *offset = 1; - } else { - *length = (unsigned char) bh->b_data[*offset] | - (unsigned char) bh->b_data[*offset + 1] << 8; - *offset += 2; - - if (*offset == msblk->devblksize) { - put_bh(bh); - bh = sb_bread(sb, ++(*cur_index)); - if (bh == NULL) - return NULL; - *offset = 0; + if (req->data_processing == SQUASHFS_COPY) { + squashfs_bh_to_actor(bh, nr_buffers, req->output, req->offset, + req->length, msblk->devblksize); + } else if (req->data_processing == SQUASHFS_DECOMPRESS) { + req->length = squashfs_decompress(msblk, bh, nr_buffers, + req->offset, req->length, actor); + if (req->length < 0) { + error = -EIO; + goto cleanup; } } - return bh; + /* Last page may have trailing bytes not filled */ + bytes = req->length % PAGE_SIZE; + if (bytes && actor->page[actor->pages - 1]) + zero_user_segment(actor->page[actor->pages - 1], bytes, + PAGE_SIZE); + +cleanup: + if (req->bytes_uncompressed) + *(req->bytes_uncompressed) = req->length; + if (error) { + for (i = 0; i < nr_buffers; ++i) + if (bh[i]) + put_bh(bh[i]); + } + free_read_request(req, error); } +static void read_wq_handler(struct work_struct *work) +{ + squashfs_process_blocks(container_of(work, + struct squashfs_read_request, offload)); +} -/* - * Read and decompress a metadata block or datablock. Length is non-zero - * if a datablock is being read (the size is stored elsewhere in the - * filesystem), otherwise the length is obtained from the first two bytes of - * the metadata block. A bit in the length field indicates if the block - * is stored uncompressed in the filesystem (usually because compression - * generated a larger block - this does occasionally happen with compression - * algorithms). - */ -int squashfs_read_data(struct super_block *sb, u64 index, int length, - u64 *next_index, struct squashfs_page_actor *output) +static void squashfs_bio_end_io(struct bio *bio) { - struct squashfs_sb_info *msblk = sb->s_fs_info; - struct buffer_head **bh; - int offset = index & ((1 << msblk->devblksize_log2) - 1); - u64 cur_index = index >> msblk->devblksize_log2; - int bytes, compressed, b = 0, k = 0, avail, i; + int i; + int error = bio->bi_error; + struct squashfs_bio_request *bio_req = bio->bi_private; + + bio_put(bio); + + for (i = 0; i < bio_req->nr_buffers; ++i) { + if (!bio_req->bh[i]) + continue; + if (!error) + set_buffer_uptodate(bio_req->bh[i]); + else + clear_buffer_uptodate(bio_req->bh[i]); + unlock_buffer(bio_req->bh[i]); + } + kfree(bio_req); +} + +static int bh_is_optional(struct squashfs_read_request *req, int idx) +{ + int start_idx, end_idx; + struct squashfs_sb_info *msblk = req->sb->s_fs_info; - bh = kcalloc(((output->length + msblk->devblksize - 1) - >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL); - if (bh == NULL) + start_idx = (idx * msblk->devblksize - req->offset) / PAGE_CACHE_SIZE; + end_idx = ((idx + 1) * msblk->devblksize - req->offset + 1) / PAGE_CACHE_SIZE; + if (start_idx >= req->output->pages) + return 1; + if (start_idx < 0) + start_idx = end_idx; + if (end_idx >= req->output->pages) + end_idx = start_idx; + return !req->output->page[start_idx] && !req->output->page[end_idx]; +} + +static int actor_getblks(struct squashfs_read_request *req, u64 block) +{ + int i; + + req->bh = kmalloc_array(req->nr_buffers, sizeof(*(req->bh)), GFP_NOIO); + if (!req->bh) return -ENOMEM; - if (length) { + for (i = 0; i < req->nr_buffers; ++i) { /* - * Datablock. + * When dealing with an uncompressed block, the actor may + * contains NULL pages. There's no need to read the buffers + * associated with these pages. */ - bytes = -offset; - compressed = SQUASHFS_COMPRESSED_BLOCK(length); - length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length); - if (next_index) - *next_index = index + length; - - TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", - index, compressed ? "" : "un", length, output->length); - - if (length < 0 || length > output->length || - (index + length) > msblk->bytes_used) - goto read_failure; - - for (b = 0; bytes < length; b++, cur_index++) { - bh[b] = sb_getblk(sb, cur_index); - if (bh[b] == NULL) - goto block_release; - bytes += msblk->devblksize; + if (!req->compressed && bh_is_optional(req, i)) { + req->bh[i] = NULL; + continue; } - ll_rw_block(READ, b, bh); - } else { - /* - * Metadata block. - */ - if ((index + 2) > msblk->bytes_used) - goto read_failure; + req->bh[i] = sb_getblk(req->sb, block + i); + if (!req->bh[i]) { + while (--i) { + if (req->bh[i]) + put_bh(req->bh[i]); + } + return -1; + } + } + return 0; +} - bh[0] = get_block_length(sb, &cur_index, &offset, &length); - if (bh[0] == NULL) - goto read_failure; - b = 1; +static int squashfs_bio_submit(struct squashfs_read_request *req) +{ + struct bio *bio = NULL; + struct buffer_head *bh; + struct squashfs_bio_request *bio_req = NULL; + int b = 0, prev_block = 0; + struct squashfs_sb_info *msblk = req->sb->s_fs_info; - bytes = msblk->devblksize - offset; - compressed = SQUASHFS_COMPRESSED(length); - length = SQUASHFS_COMPRESSED_SIZE(length); - if (next_index) - *next_index = index + length + 2; + u64 read_start = round_down(req->index, msblk->devblksize); + u64 read_end = round_up(req->index + req->length, msblk->devblksize); + sector_t block = read_start >> msblk->devblksize_log2; + sector_t block_end = read_end >> msblk->devblksize_log2; + int offset = read_start - round_down(req->index, PAGE_SIZE); + int nr_buffers = block_end - block; + int blksz = msblk->devblksize; + int bio_max_pages = nr_buffers > BIO_MAX_PAGES ? BIO_MAX_PAGES + : nr_buffers; - TRACE("Block @ 0x%llx, %scompressed size %d\n", index, - compressed ? "" : "un", length); + /* Setup the request */ + req->read_end = read_end; + req->offset = req->index - read_start; + req->nr_buffers = nr_buffers; + if (actor_getblks(req, block) < 0) + goto getblk_failed; - if (length < 0 || length > output->length || - (index + length) > msblk->bytes_used) - goto block_release; + /* Create and submit the BIOs */ + for (b = 0; b < nr_buffers; ++b, offset += blksz) { + bh = req->bh[b]; + if (!bh || !trylock_buffer(bh)) + continue; + if (buffer_uptodate(bh)) { + unlock_buffer(bh); + continue; + } + offset %= PAGE_SIZE; - for (; bytes < length; b++) { - bh[b] = sb_getblk(sb, ++cur_index); - if (bh[b] == NULL) - goto block_release; - bytes += msblk->devblksize; + /* Append the buffer to the current BIO if it is contiguous */ + if (bio && bio_req && prev_block + 1 == b) { + if (bio_add_page(bio, bh->b_page, blksz, offset)) { + bio_req->nr_buffers += 1; + prev_block = b; + continue; + } } - ll_rw_block(READ, b - 1, bh + 1); + + /* Otherwise, submit the current BIO and create a new one */ + if (bio) + submit_bio(READ, bio); + bio_req = kcalloc(1, sizeof(struct squashfs_bio_request), + GFP_NOIO); + if (!bio_req) + goto req_alloc_failed; + bio_req->bh = &req->bh[b]; + bio = bio_alloc(GFP_NOIO, bio_max_pages); + if (!bio) + goto bio_alloc_failed; + bio->bi_bdev = req->sb->s_bdev; + bio->bi_iter.bi_sector = (block + b) + << (msblk->devblksize_log2 - 9); + bio->bi_private = bio_req; + bio->bi_end_io = squashfs_bio_end_io; + + bio_add_page(bio, bh->b_page, blksz, offset); + bio_req->nr_buffers += 1; + prev_block = b; } + if (bio) + submit_bio(READ, bio); - for (i = 0; i < b; i++) { - wait_on_buffer(bh[i]); - if (!buffer_uptodate(bh[i])) - goto block_release; + if (req->synchronous) + squashfs_process_blocks(req); + else { + INIT_WORK(&req->offload, read_wq_handler); + schedule_work(&req->offload); } + return 0; - if (compressed) { - length = squashfs_decompress(msblk, bh, b, offset, length, - output); - if (length < 0) - goto read_failure; - } else { - /* - * Block is uncompressed. - */ - int in, pg_offset = 0; - void *data = squashfs_first_page(output); - - for (bytes = length; k < b; k++) { - in = min(bytes, msblk->devblksize - offset); - bytes -= in; - while (in) { - if (pg_offset == PAGE_CACHE_SIZE) { - data = squashfs_next_page(output); - pg_offset = 0; - } - avail = min_t(int, in, PAGE_CACHE_SIZE - - pg_offset); - memcpy(data + pg_offset, bh[k]->b_data + offset, - avail); - in -= avail; - pg_offset += avail; - offset += avail; - } - offset = 0; - put_bh(bh[k]); - } - squashfs_finish_page(output); +bio_alloc_failed: + kfree(bio_req); +req_alloc_failed: + unlock_buffer(bh); + while (--nr_buffers >= b) + if (req->bh[nr_buffers]) + put_bh(req->bh[nr_buffers]); + while (--b >= 0) + if (req->bh[b]) + wait_on_buffer(req->bh[b]); +getblk_failed: + free_read_request(req, -ENOMEM); + return -ENOMEM; +} + +static int read_metadata_block(struct squashfs_read_request *req, + u64 *next_index) +{ + int ret, error, bytes_read = 0, bytes_uncompressed = 0; + struct squashfs_sb_info *msblk = req->sb->s_fs_info; + + if (req->index + 2 > msblk->bytes_used) { + free_read_request(req, -EINVAL); + return -EINVAL; + } + req->length = 2; + + /* Do not read beyond the end of the device */ + if (req->index + req->length > msblk->bytes_used) + req->length = msblk->bytes_used - req->index; + req->data_processing = SQUASHFS_METADATA; + + /* + * Reading metadata is always synchronous because we don't know the + * length in advance and the function is expected to update + * 'next_index' and return the length. + */ + req->synchronous = true; + req->res = &error; + req->bytes_read = &bytes_read; + req->bytes_uncompressed = &bytes_uncompressed; + + TRACE("Metadata block @ 0x%llx, %scompressed size %d, src size %d\n", + req->index, req->compressed ? "" : "un", bytes_read, + req->output->length); + + ret = squashfs_bio_submit(req); + if (ret) + return ret; + if (error) + return error; + if (next_index) + *next_index += 2 + bytes_read; + return bytes_uncompressed; +} + +static int read_data_block(struct squashfs_read_request *req, int length, + u64 *next_index, bool synchronous) +{ + int ret, error = 0, bytes_uncompressed = 0, bytes_read = 0; + + req->compressed = SQUASHFS_COMPRESSED_BLOCK(length); + req->length = length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length); + req->data_processing = req->compressed ? SQUASHFS_DECOMPRESS + : SQUASHFS_COPY; + + req->synchronous = synchronous; + if (synchronous) { + req->res = &error; + req->bytes_read = &bytes_read; + req->bytes_uncompressed = &bytes_uncompressed; + } + + TRACE("Data block @ 0x%llx, %scompressed size %d, src size %d\n", + req->index, req->compressed ? "" : "un", req->length, + req->output->length); + + ret = squashfs_bio_submit(req); + if (ret) + return ret; + if (synchronous) + ret = error ? error : bytes_uncompressed; + if (next_index) + *next_index += length; + return ret; +} + +/* + * Read and decompress a metadata block or datablock. Length is non-zero + * if a datablock is being read (the size is stored elsewhere in the + * filesystem), otherwise the length is obtained from the first two bytes of + * the metadata block. A bit in the length field indicates if the block + * is stored uncompressed in the filesystem (usually because compression + * generated a larger block - this does occasionally happen with compression + * algorithms). + */ +static int __squashfs_read_data(struct super_block *sb, u64 index, int length, + u64 *next_index, struct squashfs_page_actor *output, bool sync) +{ + struct squashfs_read_request *req; + + req = kcalloc(1, sizeof(struct squashfs_read_request), GFP_KERNEL); + if (!req) { + if (!sync) + squashfs_page_actor_free(output, -ENOMEM); + return -ENOMEM; + } + + req->sb = sb; + req->index = index; + req->output = output; + + if (next_index) + *next_index = index; + + if (length) + length = read_data_block(req, length, next_index, sync); + else + length = read_metadata_block(req, next_index); + + if (length < 0) { + ERROR("squashfs_read_data failed to read block 0x%llx\n", + (unsigned long long)index); + return -EIO; } - kfree(bh); return length; +} -block_release: - for (; k < b; k++) - put_bh(bh[k]); +int squashfs_read_data(struct super_block *sb, u64 index, int length, + u64 *next_index, struct squashfs_page_actor *output) +{ + return __squashfs_read_data(sb, index, length, next_index, output, + true); +} + +int squashfs_read_data_async(struct super_block *sb, u64 index, int length, + u64 *next_index, struct squashfs_page_actor *output) +{ -read_failure: - ERROR("squashfs_read_data failed to read block 0x%llx\n", - (unsigned long long) index); - kfree(bh); - return -EIO; + return __squashfs_read_data(sb, index, length, next_index, output, + false); } diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c index 1cb70a0b2168..6785d086ab38 100644 --- a/fs/squashfs/cache.c +++ b/fs/squashfs/cache.c @@ -209,17 +209,14 @@ void squashfs_cache_put(struct squashfs_cache_entry *entry) */ void squashfs_cache_delete(struct squashfs_cache *cache) { - int i, j; + int i; if (cache == NULL) return; for (i = 0; i < cache->entries; i++) { - if (cache->entry[i].data) { - for (j = 0; j < cache->pages; j++) - kfree(cache->entry[i].data[j]); - kfree(cache->entry[i].data); - } + if (cache->entry[i].page) + free_page_array(cache->entry[i].page, cache->pages); kfree(cache->entry[i].actor); } @@ -236,7 +233,7 @@ void squashfs_cache_delete(struct squashfs_cache *cache) struct squashfs_cache *squashfs_cache_init(char *name, int entries, int block_size) { - int i, j; + int i; struct squashfs_cache *cache = kzalloc(sizeof(*cache), GFP_KERNEL); if (cache == NULL) { @@ -268,22 +265,13 @@ struct squashfs_cache *squashfs_cache_init(char *name, int entries, init_waitqueue_head(&cache->entry[i].wait_queue); entry->cache = cache; entry->block = SQUASHFS_INVALID_BLK; - entry->data = kcalloc(cache->pages, sizeof(void *), GFP_KERNEL); - if (entry->data == NULL) { + entry->page = alloc_page_array(cache->pages, GFP_KERNEL); + if (!entry->page) { ERROR("Failed to allocate %s cache entry\n", name); goto cleanup; } - - for (j = 0; j < cache->pages; j++) { - entry->data[j] = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL); - if (entry->data[j] == NULL) { - ERROR("Failed to allocate %s buffer\n", name); - goto cleanup; - } - } - - entry->actor = squashfs_page_actor_init(entry->data, - cache->pages, 0); + entry->actor = squashfs_page_actor_init(entry->page, + cache->pages, 0, NULL); if (entry->actor == NULL) { ERROR("Failed to allocate %s cache entry\n", name); goto cleanup; @@ -314,18 +302,20 @@ int squashfs_copy_data(void *buffer, struct squashfs_cache_entry *entry, return min(length, entry->length - offset); while (offset < entry->length) { - void *buff = entry->data[offset / PAGE_CACHE_SIZE] - + (offset % PAGE_CACHE_SIZE); + void *buff = kmap_atomic(entry->page[offset / PAGE_CACHE_SIZE]) + + (offset % PAGE_CACHE_SIZE); int bytes = min_t(int, entry->length - offset, PAGE_CACHE_SIZE - (offset % PAGE_CACHE_SIZE)); if (bytes >= remaining) { memcpy(buffer, buff, remaining); + kunmap_atomic(buff); remaining = 0; break; } memcpy(buffer, buff, bytes); + kunmap_atomic(buff); buffer += bytes; remaining -= bytes; offset += bytes; @@ -416,43 +406,38 @@ struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *sb, void *squashfs_read_table(struct super_block *sb, u64 block, int length) { int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - int i, res; - void *table, *buffer, **data; + struct page **page; + void *buff; + int res; struct squashfs_page_actor *actor; - table = buffer = kmalloc(length, GFP_KERNEL); - if (table == NULL) + page = alloc_page_array(pages, GFP_KERNEL); + if (!page) return ERR_PTR(-ENOMEM); - data = kcalloc(pages, sizeof(void *), GFP_KERNEL); - if (data == NULL) { - res = -ENOMEM; - goto failed; - } - - actor = squashfs_page_actor_init(data, pages, length); + actor = squashfs_page_actor_init(page, pages, length, NULL); if (actor == NULL) { res = -ENOMEM; - goto failed2; + goto failed; } - for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE) - data[i] = buffer; - res = squashfs_read_data(sb, block, length | SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor); - kfree(data); - kfree(actor); - if (res < 0) - goto failed; + goto failed2; - return table; + buff = kmalloc(length, GFP_KERNEL); + if (!buff) + goto failed2; + squashfs_actor_to_buf(actor, buff, length); + squashfs_page_actor_free(actor, 0); + free_page_array(page, pages); + return buff; failed2: - kfree(data); + squashfs_page_actor_free(actor, 0); failed: - kfree(table); + free_page_array(page, pages); return ERR_PTR(res); } diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c index e9034bf6e5ae..7de35bf297aa 100644 --- a/fs/squashfs/decompressor.c +++ b/fs/squashfs/decompressor.c @@ -24,7 +24,8 @@ #include <linux/types.h> #include <linux/mutex.h> #include <linux/slab.h> -#include <linux/buffer_head.h> +#include <linux/highmem.h> +#include <linux/fs.h> #include "squashfs_fs.h" #include "squashfs_fs_sb.h" @@ -94,40 +95,44 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id) static void *get_comp_opts(struct super_block *sb, unsigned short flags) { struct squashfs_sb_info *msblk = sb->s_fs_info; - void *buffer = NULL, *comp_opts; + void *comp_opts, *buffer = NULL; + struct page *page; struct squashfs_page_actor *actor = NULL; int length = 0; + if (!SQUASHFS_COMP_OPTS(flags)) + return squashfs_comp_opts(msblk, buffer, length); + /* * Read decompressor specific options from file system if present */ - if (SQUASHFS_COMP_OPTS(flags)) { - buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL); - if (buffer == NULL) { - comp_opts = ERR_PTR(-ENOMEM); - goto out; - } - - actor = squashfs_page_actor_init(&buffer, 1, 0); - if (actor == NULL) { - comp_opts = ERR_PTR(-ENOMEM); - goto out; - } - - length = squashfs_read_data(sb, - sizeof(struct squashfs_super_block), 0, NULL, actor); - - if (length < 0) { - comp_opts = ERR_PTR(length); - goto out; - } + + page = alloc_page(GFP_KERNEL); + if (!page) + return ERR_PTR(-ENOMEM); + + actor = squashfs_page_actor_init(&page, 1, 0, NULL); + if (actor == NULL) { + comp_opts = ERR_PTR(-ENOMEM); + goto actor_error; + } + + length = squashfs_read_data(sb, + sizeof(struct squashfs_super_block), 0, NULL, actor); + + if (length < 0) { + comp_opts = ERR_PTR(length); + goto read_error; } + buffer = kmap_atomic(page); comp_opts = squashfs_comp_opts(msblk, buffer, length); + kunmap_atomic(buffer); -out: - kfree(actor); - kfree(buffer); +read_error: + squashfs_page_actor_free(actor, 0); +actor_error: + __free_page(page); return comp_opts; } diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c index e5c9689062ba..6f5ef8d7e55a 100644 --- a/fs/squashfs/file.c +++ b/fs/squashfs/file.c @@ -47,12 +47,16 @@ #include <linux/string.h> #include <linux/pagemap.h> #include <linux/mutex.h> +#include <linux/mm_inline.h> #include "squashfs_fs.h" #include "squashfs_fs_sb.h" #include "squashfs_fs_i.h" #include "squashfs.h" +// Backported from 4.5 +#define lru_to_page(head) (list_entry((head)->prev, struct page, lru)) + /* * Locate cache slot in range [offset, index] for specified inode. If * there's more than one return the slot closest to index. @@ -438,6 +442,21 @@ static int squashfs_readpage_fragment(struct page *page) return res; } +static int squashfs_readpages_fragment(struct page *page, + struct list_head *readahead_pages, struct address_space *mapping) +{ + if (!page) { + page = lru_to_page(readahead_pages); + list_del(&page->lru); + if (add_to_page_cache_lru(page, mapping, page->index, + mapping_gfp_constraint(mapping, GFP_KERNEL))) { + put_page(page); + return 0; + } + } + return squashfs_readpage_fragment(page); +} + static int squashfs_readpage_sparse(struct page *page, int index, int file_end) { struct inode *inode = page->mapping->host; @@ -450,54 +469,105 @@ static int squashfs_readpage_sparse(struct page *page, int index, int file_end) return 0; } -static int squashfs_readpage(struct file *file, struct page *page) +static int squashfs_readpages_sparse(struct page *page, + struct list_head *readahead_pages, int index, int file_end, + struct address_space *mapping) { - struct inode *inode = page->mapping->host; + if (!page) { + page = lru_to_page(readahead_pages); + list_del(&page->lru); + if (add_to_page_cache_lru(page, mapping, page->index, + mapping_gfp_constraint(mapping, GFP_KERNEL))) { + put_page(page); + return 0; + } + } + return squashfs_readpage_sparse(page, index, file_end); +} + +static int __squashfs_readpages(struct file *file, struct page *page, + struct list_head *readahead_pages, unsigned int nr_pages, + struct address_space *mapping) +{ + struct inode *inode = mapping->host; struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; - int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT); int file_end = i_size_read(inode) >> msblk->block_log; int res; - void *pageaddr; - TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", - page->index, squashfs_i(inode)->start); + do { + struct page *cur_page = page ? page + : lru_to_page(readahead_pages); + int page_index = cur_page->index; + int index = page_index >> (msblk->block_log - PAGE_CACHE_SHIFT); + + if (page_index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> + PAGE_CACHE_SHIFT)) + return 1; + + if (index < file_end || squashfs_i(inode)->fragment_block == + SQUASHFS_INVALID_BLK) { + u64 block = 0; + int bsize = read_blocklist(inode, index, &block); + + if (bsize < 0) + return -1; + + if (bsize == 0) { + res = squashfs_readpages_sparse(page, + readahead_pages, index, file_end, + mapping); + } else { + res = squashfs_readpages_block(page, + readahead_pages, &nr_pages, mapping, + page_index, block, bsize); + } + } else { + res = squashfs_readpages_fragment(page, + readahead_pages, mapping); + } + if (res) + return 0; + page = NULL; + } while (readahead_pages && !list_empty(readahead_pages)); - if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> - PAGE_CACHE_SHIFT)) - goto out; + return 0; +} - if (index < file_end || squashfs_i(inode)->fragment_block == - SQUASHFS_INVALID_BLK) { - u64 block = 0; - int bsize = read_blocklist(inode, index, &block); - if (bsize < 0) - goto error_out; +static int squashfs_readpage(struct file *file, struct page *page) +{ + int ret; - if (bsize == 0) - res = squashfs_readpage_sparse(page, index, file_end); + TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", + page->index, squashfs_i(page->mapping->host)->start); + + get_page(page); + + ret = __squashfs_readpages(file, page, NULL, 1, page->mapping); + if (ret) { + flush_dcache_page(page); + if (ret < 0) + SetPageError(page); else - res = squashfs_readpage_block(page, block, bsize); - } else - res = squashfs_readpage_fragment(page); - - if (!res) - return 0; - -error_out: - SetPageError(page); -out: - pageaddr = kmap_atomic(page); - memset(pageaddr, 0, PAGE_CACHE_SIZE); - kunmap_atomic(pageaddr); - flush_dcache_page(page); - if (!PageError(page)) - SetPageUptodate(page); - unlock_page(page); + SetPageUptodate(page); + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + unlock_page(page); + put_page(page); + } return 0; } +static int squashfs_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned int nr_pages) +{ + TRACE("Entered squashfs_readpages, %u pages, first page index %lx\n", + nr_pages, lru_to_page(pages)->index); + __squashfs_readpages(file, NULL, pages, nr_pages, mapping); + return 0; +} + const struct address_space_operations squashfs_aops = { - .readpage = squashfs_readpage + .readpage = squashfs_readpage, + .readpages = squashfs_readpages, }; diff --git a/fs/squashfs/file_cache.c b/fs/squashfs/file_cache.c deleted file mode 100644 index f2310d2a2019..000000000000 --- a/fs/squashfs/file_cache.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2013 - * Phillip Lougher <phillip@squashfs.org.uk> - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - */ - -#include <linux/fs.h> -#include <linux/vfs.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/pagemap.h> -#include <linux/mutex.h> - -#include "squashfs_fs.h" -#include "squashfs_fs_sb.h" -#include "squashfs_fs_i.h" -#include "squashfs.h" - -/* Read separately compressed datablock and memcopy into page cache */ -int squashfs_readpage_block(struct page *page, u64 block, int bsize) -{ - struct inode *i = page->mapping->host; - struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, - block, bsize); - int res = buffer->error; - - if (res) - ERROR("Unable to read page, block %llx, size %x\n", block, - bsize); - else - squashfs_copy_cache(page, buffer, buffer->length, 0); - - squashfs_cache_put(buffer); - return res; -} diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c index 43e7a7eddac0..c97af4c6ccd0 100644 --- a/fs/squashfs/file_direct.c +++ b/fs/squashfs/file_direct.c @@ -13,6 +13,7 @@ #include <linux/string.h> #include <linux/pagemap.h> #include <linux/mutex.h> +#include <linux/mm_inline.h> #include "squashfs_fs.h" #include "squashfs_fs_sb.h" @@ -20,157 +21,139 @@ #include "squashfs.h" #include "page_actor.h" -static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, - int pages, struct page **page); - -/* Read separately compressed datablock directly into page cache */ -int squashfs_readpage_block(struct page *target_page, u64 block, int bsize) +// Backported from 4.5 +#define lru_to_page(head) (list_entry((head)->prev, struct page, lru)) +static void release_actor_pages(struct page **page, int pages, int error) { - struct inode *inode = target_page->mapping->host; - struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; + int i; - int file_end = (i_size_read(inode) - 1) >> PAGE_CACHE_SHIFT; - int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1; - int start_index = target_page->index & ~mask; - int end_index = start_index | mask; - int i, n, pages, missing_pages, bytes, res = -ENOMEM; + for (i = 0; i < pages; i++) { + if (!page[i]) + continue; + flush_dcache_page(page[i]); + if (!error) + SetPageUptodate(page[i]); + else { + SetPageError(page[i]); + zero_user_segment(page[i], 0, PAGE_CACHE_SIZE); + } + unlock_page(page[i]); + put_page(page[i]); + } + kfree(page); +} + +/* + * Create a "page actor" which will kmap and kunmap the + * page cache pages appropriately within the decompressor + */ +static struct squashfs_page_actor *actor_from_page_cache( + unsigned int actor_pages, struct page *target_page, + struct list_head *rpages, unsigned int *nr_pages, int start_index, + struct address_space *mapping) +{ struct page **page; struct squashfs_page_actor *actor; - void *pageaddr; - - if (end_index > file_end) - end_index = file_end; - - pages = end_index - start_index + 1; - - page = kmalloc_array(pages, sizeof(void *), GFP_KERNEL); - if (page == NULL) - return res; - - /* - * Create a "page actor" which will kmap and kunmap the - * page cache pages appropriately within the decompressor - */ - actor = squashfs_page_actor_init_special(page, pages, 0); - if (actor == NULL) - goto out; - - /* Try to grab all the pages covered by the Squashfs block */ - for (missing_pages = 0, i = 0, n = start_index; i < pages; i++, n++) { - page[i] = (n == target_page->index) ? target_page : - grab_cache_page_nowait(target_page->mapping, n); + int i, n; + gfp_t gfp = mapping_gfp_constraint(mapping, GFP_KERNEL); + + page = kmalloc_array(actor_pages, sizeof(void *), GFP_KERNEL); + if (!page) + return NULL; + + for (i = 0, n = start_index; i < actor_pages; i++, n++) { + if (target_page == NULL && rpages && !list_empty(rpages)) { + struct page *cur_page = lru_to_page(rpages); + + if (cur_page->index < start_index + actor_pages) { + list_del(&cur_page->lru); + --(*nr_pages); + if (add_to_page_cache_lru(cur_page, mapping, + cur_page->index, gfp)) + put_page(cur_page); + else + target_page = cur_page; + } else + rpages = NULL; + } - if (page[i] == NULL) { - missing_pages++; - continue; + if (target_page && target_page->index == n) { + page[i] = target_page; + target_page = NULL; + } else { + page[i] = grab_cache_page_nowait(mapping, n); + if (page[i] == NULL) + continue; } if (PageUptodate(page[i])) { unlock_page(page[i]); - page_cache_release(page[i]); + put_page(page[i]); page[i] = NULL; - missing_pages++; } } - if (missing_pages) { - /* - * Couldn't get one or more pages, this page has either - * been VM reclaimed, but others are still in the page cache - * and uptodate, or we're racing with another thread in - * squashfs_readpage also trying to grab them. Fall back to - * using an intermediate buffer. - */ - res = squashfs_read_cache(target_page, block, bsize, pages, - page); - if (res < 0) - goto mark_errored; - - goto out; + actor = squashfs_page_actor_init(page, actor_pages, 0, + release_actor_pages); + if (!actor) { + release_actor_pages(page, actor_pages, -ENOMEM); + kfree(page); + return NULL; } - - /* Decompress directly into the page cache buffers */ - res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor); - if (res < 0) - goto mark_errored; - - /* Last page may have trailing bytes not filled */ - bytes = res % PAGE_CACHE_SIZE; - if (bytes) { - pageaddr = kmap_atomic(page[pages - 1]); - memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); - kunmap_atomic(pageaddr); - } - - /* Mark pages as uptodate, unlock and release */ - for (i = 0; i < pages; i++) { - flush_dcache_page(page[i]); - SetPageUptodate(page[i]); - unlock_page(page[i]); - if (page[i] != target_page) - page_cache_release(page[i]); - } - - kfree(actor); - kfree(page); - - return 0; - -mark_errored: - /* Decompression failed, mark pages as errored. Target_page is - * dealt with by the caller - */ - for (i = 0; i < pages; i++) { - if (page[i] == NULL || page[i] == target_page) - continue; - flush_dcache_page(page[i]); - SetPageError(page[i]); - unlock_page(page[i]); - page_cache_release(page[i]); - } - -out: - kfree(actor); - kfree(page); - return res; + return actor; } +int squashfs_readpages_block(struct page *target_page, + struct list_head *readahead_pages, + unsigned int *nr_pages, + struct address_space *mapping, + int page_index, u64 block, int bsize) -static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, - int pages, struct page **page) { - struct inode *i = target_page->mapping->host; - struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, - block, bsize); - int bytes = buffer->length, res = buffer->error, n, offset = 0; - void *pageaddr; - - if (res) { - ERROR("Unable to read page, block %llx, size %x\n", block, - bsize); - goto out; - } - - for (n = 0; n < pages && bytes > 0; n++, - bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) { - int avail = min_t(int, bytes, PAGE_CACHE_SIZE); - - if (page[n] == NULL) - continue; + struct squashfs_page_actor *actor; + struct inode *inode = mapping->host; + struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; + int start_index, end_index, file_end, actor_pages, res; + int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1; - pageaddr = kmap_atomic(page[n]); - squashfs_copy_data(pageaddr, buffer, offset, avail); - memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail); - kunmap_atomic(pageaddr); - flush_dcache_page(page[n]); - SetPageUptodate(page[n]); - unlock_page(page[n]); - if (page[n] != target_page) - page_cache_release(page[n]); + /* + * If readpage() is called on an uncompressed datablock, we can just + * read the pages instead of fetching the whole block. + * This greatly improves the performance when a process keep doing + * random reads because we only fetch the necessary data. + * The readahead algorithm will take care of doing speculative reads + * if necessary. + * We can't read more than 1 block even if readahead provides use more + * pages because we don't know yet if the next block is compressed or + * not. + */ + if (bsize && !SQUASHFS_COMPRESSED_BLOCK(bsize)) { + u64 block_end = block + msblk->block_size; + + block += (page_index & mask) * PAGE_CACHE_SIZE; + actor_pages = (block_end - block) / PAGE_CACHE_SIZE; + if (*nr_pages < actor_pages) + actor_pages = *nr_pages; + start_index = page_index; + bsize = min_t(int, bsize, (PAGE_CACHE_SIZE * actor_pages) + | SQUASHFS_COMPRESSED_BIT_BLOCK); + } else { + file_end = (i_size_read(inode) - 1) >> PAGE_CACHE_SHIFT; + start_index = page_index & ~mask; + end_index = start_index | mask; + if (end_index > file_end) + end_index = file_end; + actor_pages = end_index - start_index + 1; } -out: - squashfs_cache_put(buffer); - return res; + actor = actor_from_page_cache(actor_pages, target_page, + readahead_pages, nr_pages, start_index, + mapping); + if (!actor) + return -ENOMEM; + + res = squashfs_read_data_async(inode->i_sb, block, bsize, NULL, + actor); + return res < 0 ? res : 0; } diff --git a/fs/squashfs/lz4_wrapper.c b/fs/squashfs/lz4_wrapper.c index c31e2bc9c081..df4fa3c7ddd0 100644 --- a/fs/squashfs/lz4_wrapper.c +++ b/fs/squashfs/lz4_wrapper.c @@ -94,39 +94,17 @@ static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm, struct buffer_head **bh, int b, int offset, int length, struct squashfs_page_actor *output) { - struct squashfs_lz4 *stream = strm; - void *buff = stream->input, *data; - int avail, i, bytes = length, res; + int res; size_t dest_len = output->length; + struct squashfs_lz4 *stream = strm; - for (i = 0; i < b; i++) { - avail = min(bytes, msblk->devblksize - offset); - memcpy(buff, bh[i]->b_data + offset, avail); - buff += avail; - bytes -= avail; - offset = 0; - put_bh(bh[i]); - } - + squashfs_bh_to_buf(bh, b, stream->input, offset, length, + msblk->devblksize); res = lz4_decompress_unknownoutputsize(stream->input, length, stream->output, &dest_len); if (res) return -EIO; - - bytes = dest_len; - data = squashfs_first_page(output); - buff = stream->output; - while (data) { - if (bytes <= PAGE_CACHE_SIZE) { - memcpy(data, buff, bytes); - break; - } - memcpy(data, buff, PAGE_CACHE_SIZE); - buff += PAGE_CACHE_SIZE; - bytes -= PAGE_CACHE_SIZE; - data = squashfs_next_page(output); - } - squashfs_finish_page(output); + squashfs_buf_to_actor(stream->output, output, dest_len); return dest_len; } diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c index 244b9fbfff7b..2c844d53a59e 100644 --- a/fs/squashfs/lzo_wrapper.c +++ b/fs/squashfs/lzo_wrapper.c @@ -79,45 +79,19 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm, struct buffer_head **bh, int b, int offset, int length, struct squashfs_page_actor *output) { - struct squashfs_lzo *stream = strm; - void *buff = stream->input, *data; - int avail, i, bytes = length, res; + int res; size_t out_len = output->length; + struct squashfs_lzo *stream = strm; - for (i = 0; i < b; i++) { - avail = min(bytes, msblk->devblksize - offset); - memcpy(buff, bh[i]->b_data + offset, avail); - buff += avail; - bytes -= avail; - offset = 0; - put_bh(bh[i]); - } - + squashfs_bh_to_buf(bh, b, stream->input, offset, length, + msblk->devblksize); res = lzo1x_decompress_safe(stream->input, (size_t)length, stream->output, &out_len); if (res != LZO_E_OK) - goto failed; + return -EIO; + squashfs_buf_to_actor(stream->output, output, out_len); - res = bytes = (int)out_len; - data = squashfs_first_page(output); - buff = stream->output; - while (data) { - if (bytes <= PAGE_CACHE_SIZE) { - memcpy(data, buff, bytes); - break; - } else { - memcpy(data, buff, PAGE_CACHE_SIZE); - buff += PAGE_CACHE_SIZE; - bytes -= PAGE_CACHE_SIZE; - data = squashfs_next_page(output); - } - } - squashfs_finish_page(output); - - return res; - -failed: - return -EIO; + return out_len; } const struct squashfs_decompressor squashfs_lzo_comp_ops = { diff --git a/fs/squashfs/page_actor.c b/fs/squashfs/page_actor.c index 5a1c11f56441..53863508e400 100644 --- a/fs/squashfs/page_actor.c +++ b/fs/squashfs/page_actor.c @@ -9,39 +9,11 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/pagemap.h> +#include <linux/buffer_head.h> #include "page_actor.h" -/* - * This file contains implementations of page_actor for decompressing into - * an intermediate buffer, and for decompressing directly into the - * page cache. - * - * Calling code should avoid sleeping between calls to squashfs_first_page() - * and squashfs_finish_page(). - */ - -/* Implementation of page_actor for decompressing into intermediate buffer */ -static void *cache_first_page(struct squashfs_page_actor *actor) -{ - actor->next_page = 1; - return actor->buffer[0]; -} - -static void *cache_next_page(struct squashfs_page_actor *actor) -{ - if (actor->next_page == actor->pages) - return NULL; - - return actor->buffer[actor->next_page++]; -} - -static void cache_finish_page(struct squashfs_page_actor *actor) -{ - /* empty */ -} - -struct squashfs_page_actor *squashfs_page_actor_init(void **buffer, - int pages, int length) +struct squashfs_page_actor *squashfs_page_actor_init(struct page **page, + int pages, int length, void (*release_pages)(struct page **, int, int)) { struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL); @@ -49,52 +21,133 @@ struct squashfs_page_actor *squashfs_page_actor_init(void **buffer, return NULL; actor->length = length ? : pages * PAGE_CACHE_SIZE; - actor->buffer = buffer; + actor->page = page; actor->pages = pages; actor->next_page = 0; - actor->squashfs_first_page = cache_first_page; - actor->squashfs_next_page = cache_next_page; - actor->squashfs_finish_page = cache_finish_page; + actor->pageaddr = NULL; + actor->release_pages = release_pages; return actor; } -/* Implementation of page_actor for decompressing directly into page cache. */ -static void *direct_first_page(struct squashfs_page_actor *actor) +void squashfs_page_actor_free(struct squashfs_page_actor *actor, int error) +{ + if (!actor) + return; + + if (actor->release_pages) + actor->release_pages(actor->page, actor->pages, error); + kfree(actor); +} + +void squashfs_actor_to_buf(struct squashfs_page_actor *actor, void *buf, + int length) { - actor->next_page = 1; - return actor->pageaddr = kmap_atomic(actor->page[0]); + void *pageaddr; + int pos = 0, avail, i; + + for (i = 0; i < actor->pages && pos < length; ++i) { + avail = min_t(int, length - pos, PAGE_CACHE_SIZE); + if (actor->page[i]) { + pageaddr = kmap_atomic(actor->page[i]); + memcpy(buf + pos, pageaddr, avail); + kunmap_atomic(pageaddr); + } + pos += avail; + } } -static void *direct_next_page(struct squashfs_page_actor *actor) +void squashfs_buf_to_actor(void *buf, struct squashfs_page_actor *actor, + int length) { - if (actor->pageaddr) - kunmap_atomic(actor->pageaddr); + void *pageaddr; + int pos = 0, avail, i; + + for (i = 0; i < actor->pages && pos < length; ++i) { + avail = min_t(int, length - pos, PAGE_CACHE_SIZE); + if (actor->page[i]) { + pageaddr = kmap_atomic(actor->page[i]); + memcpy(pageaddr, buf + pos, avail); + kunmap_atomic(pageaddr); + } + pos += avail; + } +} - return actor->pageaddr = actor->next_page == actor->pages ? NULL : - kmap_atomic(actor->page[actor->next_page++]); +void squashfs_bh_to_actor(struct buffer_head **bh, int nr_buffers, + struct squashfs_page_actor *actor, int offset, int length, int blksz) +{ + void *kaddr = NULL; + int bytes = 0, pgoff = 0, b = 0, p = 0, avail, i; + + while (bytes < length) { + if (actor->page[p]) { + kaddr = kmap_atomic(actor->page[p]); + while (pgoff < PAGE_CACHE_SIZE && bytes < length) { + avail = min_t(int, blksz - offset, + PAGE_CACHE_SIZE - pgoff); + memcpy(kaddr + pgoff, bh[b]->b_data + offset, + avail); + pgoff += avail; + bytes += avail; + offset = (offset + avail) % blksz; + if (!offset) { + put_bh(bh[b]); + ++b; + } + } + kunmap_atomic(kaddr); + pgoff = 0; + } else { + for (i = 0; i < PAGE_CACHE_SIZE / blksz; ++i) { + if (bh[b]) + put_bh(bh[b]); + ++b; + } + bytes += PAGE_CACHE_SIZE; + } + ++p; + } } -static void direct_finish_page(struct squashfs_page_actor *actor) +void squashfs_bh_to_buf(struct buffer_head **bh, int nr_buffers, void *buf, + int offset, int length, int blksz) { - if (actor->pageaddr) - kunmap_atomic(actor->pageaddr); + int i, avail, bytes = 0; + + for (i = 0; i < nr_buffers && bytes < length; ++i) { + avail = min_t(int, length - bytes, blksz - offset); + if (bh[i]) { + memcpy(buf + bytes, bh[i]->b_data + offset, avail); + put_bh(bh[i]); + } + bytes += avail; + offset = 0; + } } -struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page, - int pages, int length) +void free_page_array(struct page **page, int nr_pages) { - struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL); + int i; - if (actor == NULL) - return NULL; + for (i = 0; i < nr_pages; ++i) + __free_page(page[i]); + kfree(page); +} - actor->length = length ? : pages * PAGE_CACHE_SIZE; - actor->page = page; - actor->pages = pages; - actor->next_page = 0; - actor->pageaddr = NULL; - actor->squashfs_first_page = direct_first_page; - actor->squashfs_next_page = direct_next_page; - actor->squashfs_finish_page = direct_finish_page; - return actor; +struct page **alloc_page_array(int nr_pages, int gfp_mask) +{ + int i; + struct page **page; + + page = kcalloc(nr_pages, sizeof(struct page *), gfp_mask); + if (!page) + return NULL; + for (i = 0; i < nr_pages; ++i) { + page[i] = alloc_page(gfp_mask); + if (!page[i]) { + free_page_array(page, i); + return NULL; + } + } + return page; } diff --git a/fs/squashfs/page_actor.h b/fs/squashfs/page_actor.h index 26dd82008b82..aa1ed790b5a3 100644 --- a/fs/squashfs/page_actor.h +++ b/fs/squashfs/page_actor.h @@ -5,77 +5,61 @@ * Phillip Lougher <phillip@squashfs.org.uk> * * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. + * the COPYING file in the top-level squashfsory. */ -#ifndef CONFIG_SQUASHFS_FILE_DIRECT struct squashfs_page_actor { - void **page; + struct page **page; + void *pageaddr; int pages; int length; int next_page; + void (*release_pages)(struct page **, int, int); }; -static inline struct squashfs_page_actor *squashfs_page_actor_init(void **page, - int pages, int length) -{ - struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL); - - if (actor == NULL) - return NULL; +extern struct squashfs_page_actor *squashfs_page_actor_init(struct page **, + int, int, void (*)(struct page **, int, int)); +extern void squashfs_page_actor_free(struct squashfs_page_actor *, int); - actor->length = length ? : pages * PAGE_CACHE_SIZE; - actor->page = page; - actor->pages = pages; - actor->next_page = 0; - return actor; -} +extern void squashfs_actor_to_buf(struct squashfs_page_actor *, void *, int); +extern void squashfs_buf_to_actor(void *, struct squashfs_page_actor *, int); +extern void squashfs_bh_to_actor(struct buffer_head **, int, + struct squashfs_page_actor *, int, int, int); +extern void squashfs_bh_to_buf(struct buffer_head **, int, void *, int, int, + int); +/* + * Calling code should avoid sleeping between calls to squashfs_first_page() + * and squashfs_finish_page(). + */ static inline void *squashfs_first_page(struct squashfs_page_actor *actor) { actor->next_page = 1; - return actor->page[0]; + return actor->pageaddr = actor->page[0] ? kmap_atomic(actor->page[0]) + : NULL; } static inline void *squashfs_next_page(struct squashfs_page_actor *actor) { - return actor->next_page == actor->pages ? NULL : - actor->page[actor->next_page++]; -} + if (!IS_ERR_OR_NULL(actor->pageaddr)) + kunmap_atomic(actor->pageaddr); -static inline void squashfs_finish_page(struct squashfs_page_actor *actor) -{ - /* empty */ -} -#else -struct squashfs_page_actor { - union { - void **buffer; - struct page **page; - }; - void *pageaddr; - void *(*squashfs_first_page)(struct squashfs_page_actor *); - void *(*squashfs_next_page)(struct squashfs_page_actor *); - void (*squashfs_finish_page)(struct squashfs_page_actor *); - int pages; - int length; - int next_page; -}; + if (actor->next_page == actor->pages) + return actor->pageaddr = ERR_PTR(-ENODATA); -extern struct squashfs_page_actor *squashfs_page_actor_init(void **, int, int); -extern struct squashfs_page_actor *squashfs_page_actor_init_special(struct page - **, int, int); -static inline void *squashfs_first_page(struct squashfs_page_actor *actor) -{ - return actor->squashfs_first_page(actor); -} -static inline void *squashfs_next_page(struct squashfs_page_actor *actor) -{ - return actor->squashfs_next_page(actor); + actor->pageaddr = actor->page[actor->next_page] ? + kmap_atomic(actor->page[actor->next_page]) : NULL; + ++actor->next_page; + return actor->pageaddr; } + static inline void squashfs_finish_page(struct squashfs_page_actor *actor) { - actor->squashfs_finish_page(actor); + if (!IS_ERR_OR_NULL(actor->pageaddr)) + kunmap_atomic(actor->pageaddr); } -#endif + +extern struct page **alloc_page_array(int, int); +extern void free_page_array(struct page **, int); + #endif diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h index 887d6d270080..6093579c6c5d 100644 --- a/fs/squashfs/squashfs.h +++ b/fs/squashfs/squashfs.h @@ -28,8 +28,14 @@ #define WARNING(s, args...) pr_warn("SQUASHFS: "s, ## args) /* block.c */ +extern int squashfs_init_read_wq(void); +extern void squashfs_destroy_read_wq(void); extern int squashfs_read_data(struct super_block *, u64, int, u64 *, struct squashfs_page_actor *); +extern int squashfs_read_data(struct super_block *, u64, int, u64 *, + struct squashfs_page_actor *); +extern int squashfs_read_data_async(struct super_block *, u64, int, u64 *, + struct squashfs_page_actor *); /* cache.c */ extern struct squashfs_cache *squashfs_cache_init(char *, int, int); @@ -70,8 +76,9 @@ extern __le64 *squashfs_read_fragment_index_table(struct super_block *, void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int, int); -/* file_xxx.c */ -extern int squashfs_readpage_block(struct page *, u64, int); +/* file_direct.c */ +extern int squashfs_readpages_block(struct page *, struct list_head *, + unsigned int *, struct address_space *, int, u64, int); /* id.c */ extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *); diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h index 1da565cb50c3..8a6995de0277 100644 --- a/fs/squashfs/squashfs_fs_sb.h +++ b/fs/squashfs/squashfs_fs_sb.h @@ -49,7 +49,7 @@ struct squashfs_cache_entry { int num_waiters; wait_queue_head_t wait_queue; struct squashfs_cache *cache; - void **data; + struct page **page; struct squashfs_page_actor *actor; }; diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c index 5056babe00df..61cd0b39ed0e 100644 --- a/fs/squashfs/super.c +++ b/fs/squashfs/super.c @@ -444,9 +444,15 @@ static int __init init_squashfs_fs(void) if (err) return err; + if (!squashfs_init_read_wq()) { + destroy_inodecache(); + return -ENOMEM; + } + err = register_filesystem(&squashfs_fs_type); if (err) { destroy_inodecache(); + squashfs_destroy_read_wq(); return err; } @@ -460,6 +466,7 @@ static void __exit exit_squashfs_fs(void) { unregister_filesystem(&squashfs_fs_type); destroy_inodecache(); + squashfs_destroy_read_wq(); } diff --git a/fs/squashfs/xz_wrapper.c b/fs/squashfs/xz_wrapper.c index c609624e4b8a..14cd373e1897 100644 --- a/fs/squashfs/xz_wrapper.c +++ b/fs/squashfs/xz_wrapper.c @@ -55,7 +55,7 @@ static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk, struct comp_opts *opts; int err = 0, n; - opts = kmalloc(sizeof(*opts), GFP_KERNEL); + opts = kmalloc(sizeof(*opts), GFP_ATOMIC); if (opts == NULL) { err = -ENOMEM; goto out2; @@ -136,6 +136,7 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, enum xz_ret xz_err; int avail, total = 0, k = 0; struct squashfs_xz *stream = strm; + void *buf = NULL; xz_dec_reset(stream->state); stream->buf.in_pos = 0; @@ -156,12 +157,20 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, if (stream->buf.out_pos == stream->buf.out_size) { stream->buf.out = squashfs_next_page(output); - if (stream->buf.out != NULL) { + if (!IS_ERR(stream->buf.out)) { stream->buf.out_pos = 0; total += PAGE_CACHE_SIZE; } } + if (!stream->buf.out) { + if (!buf) { + buf = kmalloc(PAGE_CACHE_SIZE, GFP_ATOMIC); + if (!buf) + goto out; + } + stream->buf.out = buf; + } xz_err = xz_dec_run(stream->state, &stream->buf); if (stream->buf.in_pos == stream->buf.in_size && k < b) @@ -173,11 +182,13 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, if (xz_err != XZ_STREAM_END || k < b) goto out; + kfree(buf); return total + stream->buf.out_pos; out: for (; k < b; k++) put_bh(bh[k]); + kfree(buf); return -EIO; } diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c index 8727caba6882..09c892b5308e 100644 --- a/fs/squashfs/zlib_wrapper.c +++ b/fs/squashfs/zlib_wrapper.c @@ -66,6 +66,7 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm, struct buffer_head **bh, int b, int offset, int length, struct squashfs_page_actor *output) { + void *buf = NULL; int zlib_err, zlib_init = 0, k = 0; z_stream *stream = strm; @@ -84,10 +85,19 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm, if (stream->avail_out == 0) { stream->next_out = squashfs_next_page(output); - if (stream->next_out != NULL) + if (!IS_ERR(stream->next_out)) stream->avail_out = PAGE_CACHE_SIZE; } + if (!stream->next_out) { + if (!buf) { + buf = kmalloc(PAGE_CACHE_SIZE, GFP_ATOMIC); + if (!buf) + goto out; + } + stream->next_out = buf; + } + if (!zlib_init) { zlib_err = zlib_inflateInit(stream); if (zlib_err != Z_OK) { @@ -115,11 +125,13 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm, if (k < b) goto out; + kfree(buf); return stream->total_out; out: for (; k < b; k++) put_bh(bh[k]); + kfree(buf); return -EIO; } diff --git a/fs/sync.c b/fs/sync.c index dd5d1711c7ac..452179e31c39 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -218,6 +218,7 @@ static int do_fsync(unsigned int fd, int datasync) if (f.file) { ret = vfs_fsync(f.file, datasync); fdput(f); + inc_syscfs(current); } return ret; } diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c index 1aabfda669b0..7183b7ea065b 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.c +++ b/fs/xfs/libxfs/xfs_inode_buf.c @@ -299,6 +299,14 @@ xfs_dinode_verify( if (dip->di_magic != cpu_to_be16(XFS_DINODE_MAGIC)) return false; + /* don't allow invalid i_size */ + if (be64_to_cpu(dip->di_size) & (1ULL << 63)) + return false; + + /* No zero-length symlinks. */ + if (S_ISLNK(be16_to_cpu(dip->di_mode)) && dip->di_size == 0) + return false; + /* only version 3 or greater inodes are extensively verified here */ if (dip->di_version < 3) return true; diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c index c5101a3295d8..62ba66e1c598 100644 --- a/fs/xfs/xfs_acl.c +++ b/fs/xfs/xfs_acl.c @@ -289,8 +289,10 @@ xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type) if (type == ACL_TYPE_ACCESS) { umode_t mode; - + struct posix_acl *old_acl = acl; error = posix_acl_update_mode(inode, &mode, &acl); + if (!acl) + posix_acl_release(old_acl); if (error) return error; error = xfs_set_mode(inode, mode); diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index dbae6490a79a..832764ee035a 100644 --- a/fs/xfs/xfs_bmap_util.c +++ b/fs/xfs/xfs_bmap_util.c @@ -1713,6 +1713,7 @@ xfs_swap_extents( xfs_trans_t *tp; xfs_bstat_t *sbp = &sxp->sx_stat; xfs_ifork_t *tempifp, *ifp, *tifp; + xfs_extnum_t nextents; int src_log_flags, target_log_flags; int error = 0; int aforkblks = 0; @@ -1899,7 +1900,8 @@ xfs_swap_extents( * pointer. Otherwise it's already NULL or * pointing to the extent. */ - if (ip->i_d.di_nextents <= XFS_INLINE_EXTS) { + nextents = ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t); + if (nextents <= XFS_INLINE_EXTS) { ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext; } @@ -1918,7 +1920,8 @@ xfs_swap_extents( * pointer. Otherwise it's already NULL or * pointing to the extent. */ - if (tip->i_d.di_nextents <= XFS_INLINE_EXTS) { + nextents = tip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t); + if (nextents <= XFS_INLINE_EXTS) { tifp->if_u1.if_extents = tifp->if_u2.if_inline_ext; } diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index eb1b8c8acfcb..8146b0cf20ce 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -375,6 +375,7 @@ retry: out_free_pages: for (i = 0; i < bp->b_page_count; i++) __free_page(bp->b_pages[i]); + bp->b_flags &= ~_XBF_PAGES; return error; } diff --git a/include/asm-generic/memory_model.h b/include/asm-generic/memory_model.h index 4b4b056a6eb0..5148150cc80b 100644 --- a/include/asm-generic/memory_model.h +++ b/include/asm-generic/memory_model.h @@ -1,6 +1,8 @@ #ifndef __ASM_MEMORY_MODEL_H #define __ASM_MEMORY_MODEL_H +#include <linux/pfn.h> + #ifndef __ASSEMBLY__ #if defined(CONFIG_FLATMEM) @@ -72,7 +74,7 @@ /* * Convert a physical address to a Page Frame Number and back */ -#define __phys_to_pfn(paddr) ((unsigned long)((paddr) >> PAGE_SHIFT)) +#define __phys_to_pfn(paddr) PHYS_PFN(paddr) #define __pfn_to_phys(pfn) PFN_PHYS(pfn) #define page_to_pfn __page_to_pfn diff --git a/include/crypto/algapi.h b/include/crypto/algapi.h index c9fe145f7dd3..04661e1fb625 100644 --- a/include/crypto/algapi.h +++ b/include/crypto/algapi.h @@ -202,6 +202,9 @@ int blkcipher_aead_walk_virt_block(struct blkcipher_desc *desc, struct blkcipher_walk *walk, struct crypto_aead *tfm, unsigned int blocksize); +int blkcipher_ablkcipher_walk_virt(struct blkcipher_desc *desc, + struct blkcipher_walk *walk, + struct crypto_ablkcipher *tfm); int ablkcipher_walk_done(struct ablkcipher_request *req, struct ablkcipher_walk *walk, int err); diff --git a/include/crypto/gf128mul.h b/include/crypto/gf128mul.h index da2530e34b26..230760aef93b 100644 --- a/include/crypto/gf128mul.h +++ b/include/crypto/gf128mul.h @@ -43,7 +43,7 @@ --------------------------------------------------------------------------- Issue Date: 31/01/2006 - An implementation of field multiplication in Galois Field GF(128) + An implementation of field multiplication in Galois Field GF(2^128) */ #ifndef _CRYPTO_GF128MUL_H @@ -65,7 +65,7 @@ * are left and the lsb's are right. char b[16] is an array and b[0] is * the first octet. * - * 80000000 00000000 00000000 00000000 .... 00000000 00000000 00000000 + * 10000000 00000000 00000000 00000000 .... 00000000 00000000 00000000 * b[0] b[1] b[2] b[3] b[13] b[14] b[15] * * Every bit is a coefficient of some power of X. We can store the bits @@ -99,21 +99,21 @@ * * bbe on a little endian machine u32 x[4]: * - * MS x[0] LS MS x[1] LS + * MS x[0] LS MS x[1] LS * ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls * 103..96 111.104 119.112 127.120 71...64 79...72 87...80 95...88 * - * MS x[2] LS MS x[3] LS + * MS x[2] LS MS x[3] LS * ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls * 39...32 47...40 55...48 63...56 07...00 15...08 23...16 31...24 * * ble on a little endian machine * - * MS x[0] LS MS x[1] LS + * MS x[0] LS MS x[1] LS * ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls * 31...24 23...16 15...08 07...00 63...56 55...48 47...40 39...32 * - * MS x[2] LS MS x[3] LS + * MS x[2] LS MS x[3] LS * ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls * 95...88 87...80 79...72 71...64 127.120 199.112 111.104 103..96 * @@ -127,7 +127,7 @@ * machines this will automatically aligned to wordsize and on a 64-bit * machine also. */ -/* Multiply a GF128 field element by x. Field elements are held in arrays +/* Multiply a GF128 field element by x. Field elements are held in arrays of bytes in which field bits 8n..8n + 7 are held in byte[n], with lower indexed bits placed in the more numerically significant bit positions within bytes. @@ -135,62 +135,65 @@ On little endian machines the bit indexes translate into the bit positions within four 32-bit words in the following way - MS x[0] LS MS x[1] LS + MS x[0] LS MS x[1] LS ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls 24...31 16...23 08...15 00...07 56...63 48...55 40...47 32...39 - MS x[2] LS MS x[3] LS + MS x[2] LS MS x[3] LS ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls 88...95 80...87 72...79 64...71 120.127 112.119 104.111 96..103 On big endian machines the bit indexes translate into the bit positions within four 32-bit words in the following way - MS x[0] LS MS x[1] LS + MS x[0] LS MS x[1] LS ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls 00...07 08...15 16...23 24...31 32...39 40...47 48...55 56...63 - MS x[2] LS MS x[3] LS + MS x[2] LS MS x[3] LS ms ls ms ls ms ls ms ls ms ls ms ls ms ls ms ls 64...71 72...79 80...87 88...95 96..103 104.111 112.119 120.127 */ -/* A slow generic version of gf_mul, implemented for lle and bbe - * It multiplies a and b and puts the result in a */ +/* A slow generic version of gf_mul, implemented for lle, bbe, and ble. + * It multiplies a and b and puts the result in a + */ void gf128mul_lle(be128 *a, const be128 *b); - void gf128mul_bbe(be128 *a, const be128 *b); +void gf128mul_ble(be128 *a, const be128 *b); -/* multiply by x in ble format, needed by XTS */ +/* multiply by x in ble format, needed by XTS and HEH */ void gf128mul_x_ble(be128 *a, const be128 *b); /* 4k table optimization */ - struct gf128mul_4k { be128 t[256]; }; struct gf128mul_4k *gf128mul_init_4k_lle(const be128 *g); struct gf128mul_4k *gf128mul_init_4k_bbe(const be128 *g); +struct gf128mul_4k *gf128mul_init_4k_ble(const be128 *g); void gf128mul_4k_lle(be128 *a, struct gf128mul_4k *t); void gf128mul_4k_bbe(be128 *a, struct gf128mul_4k *t); +void gf128mul_4k_ble(be128 *a, struct gf128mul_4k *t); static inline void gf128mul_free_4k(struct gf128mul_4k *t) { - kfree(t); + kzfree(t); } -/* 64k table optimization, implemented for lle and bbe */ +/* 64k table optimization, implemented for lle, ble, and bbe */ struct gf128mul_64k { struct gf128mul_4k *t[16]; }; -/* first initialize with the constant factor with which you - * want to multiply and then call gf128_64k_lle with the other - * factor in the first argument, the table in the second and a - * scratch register in the third. Afterwards *a = *r. */ +/* First initialize with the constant factor with which you + * want to multiply and then call gf128mul_64k_bbe with the other + * factor in the first argument, and the table in the second. + * Afterwards, the result is stored in *a. + */ struct gf128mul_64k *gf128mul_init_64k_lle(const be128 *g); struct gf128mul_64k *gf128mul_init_64k_bbe(const be128 *g); void gf128mul_free_64k(struct gf128mul_64k *t); diff --git a/include/crypto/internal/hash.h b/include/crypto/internal/hash.h index 3b4af1d7c7e9..476d99d0edb7 100644 --- a/include/crypto/internal/hash.h +++ b/include/crypto/internal/hash.h @@ -102,6 +102,8 @@ int shash_register_instance(struct crypto_template *tmpl, struct shash_instance *inst); void shash_free_instance(struct crypto_instance *inst); +int crypto_grab_shash(struct crypto_shash_spawn *spawn, + const char *name, u32 type, u32 mask); int crypto_init_shash_spawn(struct crypto_shash_spawn *spawn, struct shash_alg *alg, struct crypto_instance *inst); @@ -111,6 +113,12 @@ static inline void crypto_drop_shash(struct crypto_shash_spawn *spawn) crypto_drop_spawn(&spawn->base); } +static inline struct shash_alg *crypto_spawn_shash_alg( + struct crypto_shash_spawn *spawn) +{ + return container_of(spawn->base.alg, struct shash_alg, base); +} + struct shash_alg *shash_attr_alg(struct rtattr *rta, u32 type, u32 mask); int shash_ahash_update(struct ahash_request *req, struct shash_desc *desc); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index aab1530661a5..6555fdbee88a 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -644,6 +644,20 @@ struct drm_encoder { * @audio_latency: audio latency info from ELD, if found * @null_edid_counter: track sinks that give us all zeros for the EDID * @bad_edid_counter: track sinks that give us an EDID with invalid checksum + * @max_tmds_char: indicates the maximum TMDS Character Rate supported + * @scdc_present: when set the sink supports SCDC functionality + * @rr_capable: when set the sink is capable of initiating an SCDC read request + * @supports_scramble: when set the sink supports less than 340Mcsc scrambling + * @flags_3d: 3D view(s) supported by the sink, see drm_edid.h (DRM_EDID_3D_*) + * @pt_scan_info: PT scan info obtained from the VCDB of EDID + * @it_scan_info: IT scan info obtained from the VCDB of EDID + * @ce_scan_info: CE scan info obtained from the VCDB of EDID + * @hdr_eotf: Electro optical transfer function obtained from HDR block + * @hdr_metadata_type_one: Metadata type one obtained from HDR block + * @hdr_max_luminance: desired max luminance obtained from HDR block + * @hdr_avg_luminance: desired avg luminance obtained from HDR block + * @hdr_min_luminance: desired min luminance obtained from HDR block + * @hdr_supported: does the sink support HDR content * @edid_corrupt: indicates whether the last read EDID was corrupt * @debugfs_entry: debugfs directory for this connector * @state: current atomic state for this connector @@ -717,6 +731,21 @@ struct drm_connector { int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */ unsigned bad_edid_counter; + /* EDID bits HDMI 2.0 */ + int max_tmds_char; /* in Mcsc */ + bool scdc_present; + bool rr_capable; + bool supports_scramble; + int flags_3d; + u8 pt_scan_info; + u8 it_scan_info; + u8 ce_scan_info; + u32 hdr_eotf; + bool hdr_metadata_type_one; + u32 hdr_max_luminance; + u32 hdr_avg_luminance; + u32 hdr_min_luminance; + bool hdr_supported; /* Flag for raw EDID header corruption - used in Displayport * compliance testing - * Displayport Link CTS Core 1.2 rev1.1 4.2.2.6 */ diff --git a/include/drm/drm_displayid.h b/include/drm/drm_displayid.h index 623b4e98e748..c0d4df6a606f 100644 --- a/include/drm/drm_displayid.h +++ b/include/drm/drm_displayid.h @@ -73,4 +73,21 @@ struct displayid_tiled_block { u8 topology_id[8]; } __packed; +struct displayid_detailed_timings_1 { + u8 pixel_clock[3]; + u8 flags; + u8 hactive[2]; + u8 hblank[2]; + u8 hsync[2]; + u8 hsw[2]; + u8 vactive[2]; + u8 vblank[2]; + u8 vsync[2]; + u8 vsw[2]; +} __packed; + +struct displayid_detailed_timing_block { + struct displayid_block base; + struct displayid_detailed_timings_1 timings[0]; +}; #endif diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 2af97691e878..4317ea41382f 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -266,6 +266,11 @@ struct detailed_timing { #define DRM_ELD_CEA_SAD(mnl, sad) (20 + (mnl) + 3 * (sad)) +/* HDMI 2.0 */ +#define DRM_EDID_3D_INDEPENDENT_VIEW (1 << 2) +#define DRM_EDID_3D_DUAL_VIEW (1 << 1) +#define DRM_EDID_3D_OSD_DISPARITY (1 << 0) + struct edid { u8 header[8]; /* Vendor & product info */ diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 702b6c53c12f..2043af2141c1 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -161,8 +161,8 @@ struct dentry_operations { struct vfsmount *(*d_automount)(struct path *); int (*d_manage)(struct dentry *, bool); struct inode *(*d_select_inode)(struct dentry *, unsigned); - void (*d_canonical_path)(const struct path *, struct path *); struct dentry *(*d_real)(struct dentry *, struct inode *); + void (*d_canonical_path)(const struct path *, struct path *); } ____cacheline_aligned; /* diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 61d042bbbf60..68449293c4b6 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -163,6 +163,7 @@ struct dccp_request_sock { __u64 dreq_isr; __u64 dreq_gsr; __be32 dreq_service; + spinlock_t dreq_lock; struct list_head dreq_featneg; __u32 dreq_timestamp_echo; __u32 dreq_timestamp_time; diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 5c720864db89..403a239b7aa1 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -415,6 +415,13 @@ union map_info *dm_get_rq_mapinfo(struct request *rq); struct queue_limits *dm_get_queue_limits(struct mapped_device *md); +void dm_lock_md_type(struct mapped_device *md); +void dm_unlock_md_type(struct mapped_device *md); +void dm_set_md_type(struct mapped_device *md, unsigned type); +unsigned dm_get_md_type(struct mapped_device *md); +int dm_setup_md_queue(struct mapped_device *md); +unsigned dm_table_get_type(struct dm_table *t); + /* * Geometry functions. */ diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index e9744202fa29..bc38b99a9fc9 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -35,6 +35,7 @@ enum hdmi_infoframe_type { }; #define HDMI_IEEE_OUI 0x000c03 +#define HDMI_IEEE_OUI_HF 0xc45dd8 #define HDMI_INFOFRAME_HEADER_SIZE 4 #define HDMI_AVI_INFOFRAME_SIZE 13 #define HDMI_SPD_INFOFRAME_SIZE 25 @@ -78,6 +79,8 @@ enum hdmi_picture_aspect { HDMI_PICTURE_ASPECT_NONE, HDMI_PICTURE_ASPECT_4_3, HDMI_PICTURE_ASPECT_16_9, + HDMI_PICTURE_ASPECT_64_27, + HDMI_PICTURE_ASPECT_256_135, HDMI_PICTURE_ASPECT_RESERVED, }; diff --git a/include/linux/io-pgtable-fast.h b/include/linux/io-pgtable-fast.h index 029e11f9919b..6a56f0039f15 100644 --- a/include/linux/io-pgtable-fast.h +++ b/include/linux/io-pgtable-fast.h @@ -37,7 +37,7 @@ void av8l_fast_unmap_public(av8l_fast_iopte *ptep, size_t size); #define AV8L_FAST_PTE_UNMAPPED_NEED_TLBI 0xa void av8l_fast_clear_stale_ptes(av8l_fast_iopte *puds, u64 base, - u64 end, bool skip_sync); + u64 start, u64 end, bool skip_sync); void av8l_register_notify(struct notifier_block *nb); #else /* !CONFIG_IOMMU_IO_PGTABLE_FAST_PROVE_TLB */ @@ -46,6 +46,7 @@ void av8l_register_notify(struct notifier_block *nb); static inline void av8l_fast_clear_stale_ptes(av8l_fast_iopte *puds, u64 base, + u64 start, u64 end, bool skip_sync) { diff --git a/include/linux/iommu.h b/include/linux/iommu.h index c4c25651ff21..55ae8b928411 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -137,6 +137,7 @@ enum iommu_attr { DOMAIN_ATTR_PAGE_TABLE_IS_COHERENT, DOMAIN_ATTR_PAGE_TABLE_FORCE_COHERENT, DOMAIN_ATTR_ENABLE_TTBR1, + DOMAIN_ATTR_CB_STALL_DISABLE, DOMAIN_ATTR_MAX, }; diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 1129d68cb6cc..cc69810fcc32 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -18,6 +18,7 @@ struct ipv6_devconf { __s32 dad_transmits; __s32 rtr_solicits; __s32 rtr_solicit_interval; + __s32 rtr_solicit_max_interval; __s32 rtr_solicit_delay; __s32 force_mld_version; __s32 mldv1_unsolicited_report_interval; @@ -36,6 +37,7 @@ struct ipv6_devconf { __s32 accept_ra_rtr_pref; __s32 rtr_probe_interval; #ifdef CONFIG_IPV6_ROUTE_INFO + __s32 accept_ra_rt_info_min_plen; __s32 accept_ra_rt_info_max_plen; #endif #endif diff --git a/include/linux/leds-qpnp-flash.h b/include/linux/leds-qpnp-flash.h index 4b5a339970fa..1fe6e1709fa6 100644 --- a/include/linux/leds-qpnp-flash.h +++ b/include/linux/leds-qpnp-flash.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -18,7 +18,6 @@ #define ENABLE_REGULATOR BIT(0) #define DISABLE_REGULATOR BIT(1) #define QUERY_MAX_CURRENT BIT(2) -#define PRE_FLASH BIT(3) #define FLASH_LED_PREPARE_OPTIONS_MASK GENMASK(3, 0) diff --git a/include/linux/log2.h b/include/linux/log2.h index fd7ff3d91e6a..f38fae23bdac 100644 --- a/include/linux/log2.h +++ b/include/linux/log2.h @@ -16,12 +16,6 @@ #include <linux/bitops.h> /* - * deal with unrepresentable constant logarithms - */ -extern __attribute__((const, noreturn)) -int ____ilog2_NaN(void); - -/* * non-constant log of base 2 calculators * - the arch may override these in asm/bitops.h if they can be implemented * more efficiently than using fls() and fls64() @@ -85,7 +79,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n) #define ilog2(n) \ ( \ __builtin_constant_p(n) ? ( \ - (n) < 1 ? ____ilog2_NaN() : \ + (n) < 2 ? 0 : \ (n) & (1ULL << 63) ? 63 : \ (n) & (1ULL << 62) ? 62 : \ (n) & (1ULL << 61) ? 61 : \ @@ -148,10 +142,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n) (n) & (1ULL << 4) ? 4 : \ (n) & (1ULL << 3) ? 3 : \ (n) & (1ULL << 2) ? 2 : \ - (n) & (1ULL << 1) ? 1 : \ - (n) & (1ULL << 0) ? 0 : \ - ____ilog2_NaN() \ - ) : \ + 1 ) : \ (sizeof(n) <= 4) ? \ __ilog2_u32(n) : \ __ilog2_u64(n) \ diff --git a/include/linux/mdss_io_util.h b/include/linux/mdss_io_util.h index 5b2587b28737..3cca007e618c 100644 --- a/include/linux/mdss_io_util.h +++ b/include/linux/mdss_io_util.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012, 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -58,6 +58,8 @@ struct dss_vreg { int post_on_sleep; int pre_off_sleep; int post_off_sleep; + bool lp_disable_allowed; + bool disabled; }; struct dss_gpio { diff --git a/include/linux/mm.h b/include/linux/mm.h index 186d2ab9cd13..3c10d4638646 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2146,6 +2146,17 @@ static inline void vm_stat_account(struct mm_struct *mm, } #endif /* CONFIG_PROC_FS */ +#ifdef CONFIG_PAGE_POISONING +extern bool page_poisoning_enabled(void); +extern void kernel_poison_pages(struct page *page, int numpages, int enable); +extern bool page_is_poisoned(struct page *page); +#else +static inline bool page_poisoning_enabled(void) { return false; } +static inline void kernel_poison_pages(struct page *page, int numpages, + int enable) { } +static inline bool page_is_poisoned(struct page *page) { return false; } +#endif + #ifdef CONFIG_DEBUG_PAGEALLOC extern bool _debug_pagealloc_enabled; extern void __kernel_map_pages(struct page *page, int numpages, int enable); @@ -2295,7 +2306,6 @@ extern void copy_user_huge_page(struct page *dst, struct page *src, #endif /* CONFIG_TRANSPARENT_HUGEPAGE || CONFIG_HUGETLBFS */ extern struct page_ext_operations debug_guardpage_ops; -extern struct page_ext_operations page_poisoning_ops; #ifdef CONFIG_DEBUG_PAGEALLOC extern unsigned int _debug_guardpage_minorder; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 31cc6f40baa5..8e76f46adde3 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -131,6 +131,9 @@ struct mmc_ext_csd { #define MMC_BKOPS_URGENCY_MASK 0x3 u8 raw_bkops_status; /* 246 */ u8 raw_sectors[4]; /* 212 - 4 bytes */ + u8 pre_eol_info; /* 267 */ + u8 device_life_time_est_typ_a; /* 268 */ + u8 device_life_time_est_typ_b; /* 269 */ u8 cmdq_depth; /* 307 */ u8 cmdq_support; /* 308 */ u8 barrier_support; /* 486 */ diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 9cd5afaef626..307a8e4be91f 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -282,6 +282,9 @@ struct _mmc_csd { #define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */ #define EXT_CSD_PWR_CL_DDR_200_360 253 /* RO */ #define EXT_CSD_FIRMWARE_VERSION 254 /* RO, 8 bytes */ +#define EXT_CSD_PRE_EOL_INFO 267 /* RO */ +#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A 268 /* RO */ +#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B 269 /* RO */ #define EXT_CSD_CMDQ_DEPTH 307 /* RO */ #define EXT_CSD_CMDQ_SUPPORT 308 /* RO */ #define EXT_CSD_BARRIER_SUPPORT 486 /* RO */ diff --git a/include/linux/msm_smd_pkt.h b/include/linux/msm_smd_pkt.h new file mode 100644 index 000000000000..c79933d27d4a --- /dev/null +++ b/include/linux/msm_smd_pkt.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2010,2017 The Linux Foundation. All rights reserved. + * + * This 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 __LINUX_MSM_SMD_PKT_H +#define __LINUX_MSM_SMD_PKT_H + +#include <linux/ioctl.h> + +#define SMD_PKT_IOCTL_MAGIC (0xC2) + +#define SMD_PKT_IOCTL_BLOCKING_WRITE \ + _IOR(SMD_PKT_IOCTL_MAGIC, 0, unsigned int) + +#endif /* __LINUX_MSM_SMD_PKT_H */ diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h index 654bb97a3188..5bc4836af286 100644 --- a/include/linux/perf/arm_pmu.h +++ b/include/linux/perf/arm_pmu.h @@ -77,6 +77,12 @@ struct pmu_hw_events { struct arm_pmu *percpu_pmu; }; +enum armpmu_pmu_states { + ARM_PMU_STATE_OFF, + ARM_PMU_STATE_RUNNING, + ARM_PMU_STATE_GOING_DOWN, +}; + struct arm_pmu { struct pmu pmu; cpumask_t active_irqs; @@ -101,6 +107,8 @@ struct arm_pmu { void (*free_irq)(struct arm_pmu *); int (*map_event)(struct perf_event *event); int num_events; + int pmu_state; + int percpu_irq; atomic_t active_events; struct mutex reserve_mutex; u64 max_period; diff --git a/include/linux/pfn.h b/include/linux/pfn.h index 7646637221f3..97f3e88aead4 100644 --- a/include/linux/pfn.h +++ b/include/linux/pfn.h @@ -9,5 +9,6 @@ #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) #define PFN_PHYS(x) ((phys_addr_t)(x) << PAGE_SHIFT) +#define PHYS_PFN(x) ((unsigned long)((x) >> PAGE_SHIFT)) #endif diff --git a/include/linux/poison.h b/include/linux/poison.h index 317e16de09e5..199ffec4bdf3 100644 --- a/include/linux/poison.h +++ b/include/linux/poison.h @@ -30,7 +30,11 @@ #define TIMER_ENTRY_STATIC ((void *) 0x74737461) /********** mm/debug-pagealloc.c **********/ +#ifdef CONFIG_PAGE_POISONING_ZERO +#define PAGE_POISON 0x00 +#else #define PAGE_POISON 0xaa +#endif /********** mm/slab.c **********/ /* diff --git a/include/linux/qpnp/qpnp-revid.h b/include/linux/qpnp/qpnp-revid.h index 7254f4d16176..b025df568259 100644 --- a/include/linux/qpnp/qpnp-revid.h +++ b/include/linux/qpnp/qpnp-revid.h @@ -214,6 +214,11 @@ #define PM660L_V1P1_REV3 0x01 #define PM660L_V1P1_REV4 0x01 +#define PM660L_V2P0_REV1 0x00 +#define PM660L_V2P0_REV2 0x00 +#define PM660L_V2P0_REV3 0x00 +#define PM660L_V2P0_REV4 0x02 + /* PMI8998 FAB_ID */ #define PMI8998_FAB_ID_SMIC 0x11 #define PMI8998_FAB_ID_GF 0x30 @@ -235,6 +240,9 @@ /* SMB1381 */ #define SMB1381_SUBTYPE 0x17 +/* SMB1355 */ +#define SMB1355_SUBTYPE 0x1C + struct pmic_revid_data { u8 rev1; u8 rev2; diff --git a/include/linux/regulator/qpnp-labibb-regulator.h b/include/linux/regulator/qpnp-labibb-regulator.h index 247069507fd9..33985afeb6e9 100644 --- a/include/linux/regulator/qpnp-labibb-regulator.h +++ b/include/linux/regulator/qpnp-labibb-regulator.h @@ -15,6 +15,7 @@ enum labibb_notify_event { LAB_VREG_OK = 1, + LAB_VREG_NOT_OK, }; int qpnp_labibb_notifier_register(struct notifier_block *nb); diff --git a/include/linux/sched.h b/include/linux/sched.h index 4b56a62a0a58..9cb0c1712792 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -3446,6 +3446,11 @@ static inline void inc_syscw(struct task_struct *tsk) { tsk->ioac.syscw++; } + +static inline void inc_syscfs(struct task_struct *tsk) +{ + tsk->ioac.syscfs++; +} #else static inline void add_rchar(struct task_struct *tsk, ssize_t amt) { @@ -3462,6 +3467,9 @@ static inline void inc_syscr(struct task_struct *tsk) static inline void inc_syscw(struct task_struct *tsk) { } +static inline void inc_syscfs(struct task_struct *tsk) +{ +} #endif #ifndef TASK_SIZE_OF diff --git a/include/linux/shrinker.h b/include/linux/shrinker.h index 4fcacd915d45..e77f648f9662 100644 --- a/include/linux/shrinker.h +++ b/include/linux/shrinker.h @@ -66,6 +66,7 @@ struct shrinker { /* Flags */ #define SHRINKER_NUMA_AWARE (1 << 0) #define SHRINKER_MEMCG_AWARE (1 << 1) +#define SHRINKER_LMK (1 << 2) extern int register_shrinker(struct shrinker *); extern void unregister_shrinker(struct shrinker *); diff --git a/include/linux/task_io_accounting.h b/include/linux/task_io_accounting.h index bdf855c2856f..2dd338fdf881 100644 --- a/include/linux/task_io_accounting.h +++ b/include/linux/task_io_accounting.h @@ -18,6 +18,8 @@ struct task_io_accounting { u64 syscr; /* # of write syscalls */ u64 syscw; + /* # of fsync syscalls */ + u64 syscfs; #endif /* CONFIG_TASK_XACCT */ #ifdef CONFIG_TASK_IO_ACCOUNTING diff --git a/include/linux/task_io_accounting_ops.h b/include/linux/task_io_accounting_ops.h index 4d090f9ee608..1b505c804af3 100644 --- a/include/linux/task_io_accounting_ops.h +++ b/include/linux/task_io_accounting_ops.h @@ -96,6 +96,7 @@ static inline void task_chr_io_accounting_add(struct task_io_accounting *dst, dst->wchar += src->wchar; dst->syscr += src->syscr; dst->syscw += src->syscw; + dst->syscfs += src->syscfs; } #else static inline void task_chr_io_accounting_add(struct task_io_accounting *dst, diff --git a/include/linux/timer.h b/include/linux/timer.h index b1617e8932b2..1239c6ef949e 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -173,6 +173,9 @@ extern int mod_timer_pending(struct timer_list *timer, unsigned long expires); extern int mod_timer_pinned(struct timer_list *timer, unsigned long expires); extern void set_timer_slack(struct timer_list *time, int slack_hz); +#ifdef CONFIG_SMP +extern bool check_pending_deferrable_timers(int cpu); +#endif #define TIMER_NOT_PINNED 0 #define TIMER_PINNED 1 diff --git a/include/linux/usb.h b/include/linux/usb.h index 7e6e4665212f..a55f127d6836 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -371,14 +371,13 @@ struct usb_bus { int devnum_next; /* Next open device number in * round-robin allocation */ + struct mutex devnum_next_mutex; /* devnum_next mutex */ struct usb_devmap devmap; /* device address allocation map */ struct usb_device *root_hub; /* Root hub */ struct usb_bus *hs_companion; /* Companion EHCI bus, if any */ struct list_head bus_list; /* list of busses */ - struct mutex usb_address0_mutex; /* unaddressed device mutex */ - int bandwidth_allocated; /* on this bus: how much of the time * reserved for periodic (intr/iso) * requests is used, on average? diff --git a/include/linux/usb/audio-v3.h b/include/linux/usb/audio-v3.h new file mode 100644 index 000000000000..f2322f3c74f7 --- /dev/null +++ b/include/linux/usb/audio-v3.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This 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. + * + * This file holds USB constants and structures defined + * by the USB Device Class Definition for Audio Devices in version 3.0. + * Comments below reference relevant sections of the documents contained + * in http://www.usb.org/developers/docs/devclass_docs/USB_Audio_v3.0.zip + */ + +#ifndef __LINUX_USB_AUDIO_V3_H +#define __LINUX_USB_AUDIO_V3_H + +#include <linux/types.h> + +#define UAC3_MIXER_UNIT_V3 0x05 +#define UAC3_FEATURE_UNIT_V3 0x07 +#define UAC3_CLOCK_SOURCE 0x0b + +#define BADD_MAXPSIZE_SYNC_MONO_16 0x0060 +#define BADD_MAXPSIZE_SYNC_MONO_24 0x0090 +#define BADD_MAXPSIZE_SYNC_STEREO_16 0x00c0 +#define BADD_MAXPSIZE_SYNC_STEREO_24 0x0120 + +#define BADD_MAXPSIZE_ASYNC_MONO_16 0x0062 +#define BADD_MAXPSIZE_ASYNC_MONO_24 0x0093 +#define BADD_MAXPSIZE_ASYNC_STEREO_16 0x00c4 +#define BADD_MAXPSIZE_ASYNC_STEREO_24 0x0126 + +#define BIT_RES_16_BIT 0x10 +#define BIT_RES_24_BIT 0x18 + +#define SUBSLOTSIZE_16_BIT 0x02 +#define SUBSLOTSIZE_24_BIT 0x03 + +#define BADD_SAMPLING_RATE 48000 + +#define NUM_CHANNELS_MONO 1 +#define NUM_CHANNELS_STEREO 2 +#define BADD_CH_CONFIG_MONO 0 +#define BADD_CH_CONFIG_STEREO 3 +#define CLUSTER_ID_MONO 0x0001 +#define CLUSTER_ID_STEREO 0x0002 + +#define FULL_ADC_PROFILE 0x01 + +/* BADD Profile IDs */ +#define PROF_GENERIC_IO 0x20 +#define PROF_HEADPHONE 0x21 +#define PROF_SPEAKER 0x22 +#define PROF_MICROPHONE 0x23 +#define PROF_HEADSET 0x24 +#define PROF_HEADSET_ADAPTER 0x25 +#define PROF_SPEAKERPHONE 0x26 + +/* BADD Entity IDs */ +#define BADD_OUT_TERM_ID_BAOF 0x03 +#define BADD_OUT_TERM_ID_BAIF 0x06 +#define BADD_IN_TERM_ID_BAOF 0x01 +#define BADD_IN_TERM_ID_BAIF 0x04 +#define BADD_FU_ID_BAOF 0x02 +#define BADD_FU_ID_BAIF 0x05 +#define BADD_CLOCK_SOURCE 0x09 +#define BADD_FU_ID_BAIOF 0x07 +#define BADD_MU_ID_BAIOF 0x08 + +#define UAC_BIDIR_TERMINAL_HEADSET 0x0402 +#define UAC_BIDIR_TERMINAL_SPEAKERPHONE 0x0403 + +#define NUM_BADD_DESCS 7 + +struct uac3_input_terminal_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bTerminalID; + __u16 wTerminalType; + __u8 bAssocTerminal; + __u8 bCSourceID; + __u32 bmControls; + __u16 wClusterDescrID; + __u16 wExTerminalDescrID; + __u16 wConnectorsDescrID; + __u16 wTerminalDescrStr; +} __packed; + +#define UAC3_DT_INPUT_TERMINAL_SIZE 0x14 + +extern struct uac3_input_terminal_descriptor badd_baif_in_term_desc; +extern struct uac3_input_terminal_descriptor badd_baof_in_term_desc; + +struct uac3_output_terminal_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bTerminalID; + __u16 wTerminalType; + __u8 bAssocTerminal; + __u8 bSourceID; + __u8 bCSourceID; + __u32 bmControls; + __u16 wExTerminalDescrID; + __u16 wConnectorsDescrID; + __u16 wTerminalDescrStr; +} __packed; + +#define UAC3_DT_OUTPUT_TERMINAL_SIZE 0x13 + +extern struct uac3_output_terminal_descriptor badd_baif_out_term_desc; +extern struct uac3_output_terminal_descriptor badd_baof_out_term_desc; + +extern __u8 monoControls[]; +extern __u8 stereoControls[]; +extern __u8 badd_mu_src_ids[]; + +struct uac3_mixer_unit_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bUnitID; + __u8 bNrInPins; + __u8 *baSourceID; + __u16 wClusterDescrID; + __u8 bmMixerControls; + __u32 bmControls; + __u16 wMixerDescrStr; +} __packed; + +#define UAC3_DT_MIXER_UNIT_SIZE 0x10 + +extern struct uac3_mixer_unit_descriptor badd_baiof_mu_desc; + +struct uac3_feature_unit_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bUnitID; + __u8 bSourceID; + __u8 *bmaControls; + __u16 wFeatureDescrStr; +} __packed; + +extern struct uac3_feature_unit_descriptor badd_baif_fu_desc; +extern struct uac3_feature_unit_descriptor badd_baof_fu_desc; +extern struct uac3_feature_unit_descriptor badd_baiof_fu_desc; + +struct uac3_clock_source_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bClockID; + __u8 bmAttributes; + __u32 bmControls; + __u8 bReferenceTerminal; + __u16 wClockSourceStr; +} __packed; + +#define UAC3_DT_CLOCK_SRC_SIZE 0x0c + +extern struct uac3_clock_source_descriptor badd_clock_desc; + +extern void *badd_desc_list[]; + +#endif /* __LINUX_USB_AUDIO_V3_H */ diff --git a/include/linux/usb/ccid_desc.h b/include/linux/usb/ccid_desc.h index 9a0c72650cd2..2e6dbb5afe71 100644 --- a/include/linux/usb/ccid_desc.h +++ b/include/linux/usb/ccid_desc.h @@ -86,27 +86,27 @@ * Table 5.1-1 Smart Card Device Class descriptors */ struct usb_ccid_class_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short bcdCCID; - unsigned char bMaxSlotIndex; - unsigned char bVoltageSupport; - unsigned long dwProtocols; - unsigned long dwDefaultClock; - unsigned long dwMaximumClock; - unsigned char bNumClockSupported; - unsigned long dwDataRate; - unsigned long dwMaxDataRate; - unsigned char bNumDataRatesSupported; - unsigned long dwMaxIFSD; - unsigned long dwSynchProtocols; - unsigned long dwMechanical; - unsigned long dwFeatures; - unsigned long dwMaxCCIDMessageLength; - unsigned char bClassGetResponse; - unsigned char bClassEnvelope; - unsigned short wLcdLayout; - unsigned char bPINSupport; - unsigned char bMaxCCIDBusySlots; + __u8 bLength; + __u8 bDescriptorType; + __u16 bcdCCID; + __u8 bMaxSlotIndex; + __u8 bVoltageSupport; + __u32 dwProtocols; + __u32 dwDefaultClock; + __u32 dwMaximumClock; + __u8 bNumClockSupported; + __u32 dwDataRate; + __u32 dwMaxDataRate; + __u8 bNumDataRatesSupported; + __u32 dwMaxIFSD; + __u32 dwSynchProtocols; + __u32 dwMechanical; + __u32 dwFeatures; + __u32 dwMaxCCIDMessageLength; + __u8 bClassGetResponse; + __u8 bClassEnvelope; + __u16 wLcdLayout; + __u8 bPINSupport; + __u8 bMaxCCIDBusySlots; } __packed; #endif diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index cef429cf3dce..e888eb9a2eb9 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -180,6 +180,7 @@ struct usb_hcd { * bandwidth_mutex should be dropped after a successful control message * to the device, or resetting the bandwidth after a failed attempt. */ + struct mutex *address0_mutex; struct mutex *bandwidth_mutex; struct usb_hcd *shared_hcd; struct usb_hcd *primary_hcd; diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h index 1d0043dc34e4..de2a722fe3cf 100644 --- a/include/linux/usb/quirks.h +++ b/include/linux/usb/quirks.h @@ -50,4 +50,10 @@ /* device can't handle Link Power Management */ #define USB_QUIRK_NO_LPM BIT(10) +/* + * Device reports its bInterval as linear frames instead of the + * USB 2.0 calculation. + */ +#define USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL BIT(11) + #endif /* __LINUX_USB_QUIRKS_H */ diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h index 75d0912aa459..762f1c51620c 100644 --- a/include/media/msm_cam_sensor.h +++ b/include/media/msm_cam_sensor.h @@ -41,6 +41,7 @@ struct msm_camera_sensor_slave_info32 { uint8_t is_init_params_valid; struct msm_sensor_init_params sensor_init_params; enum msm_sensor_output_format_t output_format; + uint8_t bypass_video_node_creation; }; struct msm_camera_csid_lut_params32 { diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 3275ddf9f00d..d540657819ef 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -1,8 +1,9 @@ #ifndef _ADDRCONF_H #define _ADDRCONF_H -#define MAX_RTR_SOLICITATIONS 3 +#define MAX_RTR_SOLICITATIONS -1 /* unlimited */ #define RTR_SOLICITATION_INTERVAL (4*HZ) +#define RTR_SOLICITATION_MAX_INTERVAL (3600*HZ) /* 1 hour */ #define MIN_VALID_LIFETIME (2*3600) /* 2 hours */ diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index 1c8b6820b694..515352c6280a 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -201,6 +201,7 @@ struct inet6_dev { struct ipv6_devstat stats; struct timer_list rs_timer; + __s32 rs_interval; /* in jiffies */ __u8 rs_probes; __u8 addr_gen_mode; diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index 4d1c46aac331..c7b1dc713cdd 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -196,6 +196,7 @@ struct iscsi_conn { struct iscsi_task *task; /* xmit task in progress */ /* xmit */ + spinlock_t taskqueuelock; /* protects the next three lists */ struct list_head mgmtqueue; /* mgmt (control) xmit queue */ struct list_head cmdqueue; /* data-path cmd queue */ struct list_head requeue; /* tasks needing another run */ diff --git a/include/soc/qcom/minidump.h b/include/soc/qcom/minidump.h new file mode 100644 index 000000000000..9d993a17fb89 --- /dev/null +++ b/include/soc/qcom/minidump.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This 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 __MINIDUMP_H +#define __MINIDUMP_H + +#define MAX_NAME_LENGTH 16 +/* md_region - Minidump table entry + * @name: Entry name, Minidump will dump binary with this name. + * @id: Entry ID, used only for SDI dumps. + * @virt_addr: Address of the entry. + * @phys_addr: Physical address of the entry to dump. + * @size: Number of byte to dump from @address location + * it should be 4 byte aligned. + */ +struct md_region { + char name[MAX_NAME_LENGTH]; + u32 id; + u64 virt_addr; + u64 phys_addr; + u64 size; +}; + +/* Register an entry in Minidump table + * Returns: + * Zero: on successful addition + * Negetive error number on failures + */ +#ifdef CONFIG_QCOM_MINIDUMP +extern int msm_minidump_add_region(const struct md_region *entry); +extern bool msm_minidump_enabled(void); +#else +static inline int msm_minidump_add_region(const struct md_region *entry) +{ + return -ENODEV; +} +static inline bool msm_minidump_enabled(void) { return false; } +#endif +#endif diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h index ac36df5769ee..dfb0280d146c 100644 --- a/include/soc/qcom/socinfo.h +++ b/include/soc/qcom/socinfo.h @@ -90,8 +90,8 @@ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8929") #define early_machine_is_msm8998() \ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8998") -#define early_machine_is_apq8998() \ - of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,apq8998") +#define early_machine_is_apq8098() \ + of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,apq8098") #define early_machine_is_msmhamster() \ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmhamster") #define early_machine_is_sdm660() \ @@ -140,7 +140,7 @@ #define early_machine_is_msm8976() 0 #define early_machine_is_msm8929() 0 #define early_machine_is_msm8998() 0 -#define early_machine_is_apq8998() 0 +#define early_machine_is_apq8098() 0 #define early_machine_is_msmhamster() 0 #define early_machine_is_sdm660() 0 #define early_machine_is_sda660() 0 diff --git a/include/soc/qcom/subsystem_restart.h b/include/soc/qcom/subsystem_restart.h index 763eaa9ad918..59749210001a 100644 --- a/include/soc/qcom/subsystem_restart.h +++ b/include/soc/qcom/subsystem_restart.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -18,6 +18,7 @@ #include <linux/interrupt.h> struct subsys_device; +extern struct bus_type subsys_bus_type; enum { RESET_SOC = 0, diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h index a5ba1496eef7..bcb53faaf678 100644 --- a/include/sound/apr_audio-v2.h +++ b/include/sound/apr_audio-v2.h @@ -5057,6 +5057,7 @@ struct asm_amrwbplus_fmt_blk_v2 { #define ASM_MEDIA_FMT_VORBIS 0x00010C15 #define ASM_MEDIA_FMT_APE 0x00012F32 #define ASM_MEDIA_FMT_DSD 0x00012F3E +#define ASM_MEDIA_FMT_TRUEHD 0x00013215 /* Media format ID for adaptive transform acoustic coding. This * ID is used by the #ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED command diff --git a/include/sound/q6adm-v2.h b/include/sound/q6adm-v2.h index 42d048f24e12..f3443b79ba51 100644 --- a/include/sound/q6adm-v2.h +++ b/include/sound/q6adm-v2.h @@ -164,4 +164,6 @@ int adm_get_sound_focus(int port_id, int copp_idx, struct sound_focus_param *soundFocusData); int adm_get_source_tracking(int port_id, int copp_idx, struct source_tracking_param *sourceTrackingData); +int adm_swap_speaker_channels(int port_id, int copp_idx, int sample_rate, + bool spk_swap); #endif /* __Q6_ADM_V2_H__ */ diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h index 29707b26644a..41fcdeb50831 100644 --- a/include/sound/q6asm-v2.h +++ b/include/sound/q6asm-v2.h @@ -55,6 +55,7 @@ #define FORMAT_DSD 0x001d #define FORMAT_APTX 0x001e #define FORMAT_GEN_COMPR 0x001f +#define FORMAT_TRUEHD 0x0020 #define ENCDEC_SBCBITRATE 0x0001 #define ENCDEC_IMMEDIATE_DECODE 0x0002 diff --git a/include/trace/events/android_fs.h b/include/trace/events/android_fs.h new file mode 100644 index 000000000000..49509533d3fa --- /dev/null +++ b/include/trace/events/android_fs.h @@ -0,0 +1,65 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM android_fs + +#if !defined(_TRACE_ANDROID_FS_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_ANDROID_FS_H + +#include <linux/tracepoint.h> +#include <trace/events/android_fs_template.h> + +DEFINE_EVENT(android_fs_data_start_template, android_fs_dataread_start, + TP_PROTO(struct inode *inode, loff_t offset, int bytes, + pid_t pid, char *pathname, char *command), + TP_ARGS(inode, offset, bytes, pid, pathname, command)); + +DEFINE_EVENT(android_fs_data_end_template, android_fs_dataread_end, + TP_PROTO(struct inode *inode, loff_t offset, int bytes), + TP_ARGS(inode, offset, bytes)); + +DEFINE_EVENT(android_fs_data_start_template, android_fs_datawrite_start, + TP_PROTO(struct inode *inode, loff_t offset, int bytes, + pid_t pid, char *pathname, char *command), + TP_ARGS(inode, offset, bytes, pid, pathname, command)); + +DEFINE_EVENT(android_fs_data_end_template, android_fs_datawrite_end, + TP_PROTO(struct inode *inode, loff_t offset, int bytes), + TP_ARGS(inode, offset, bytes)); + +#endif /* _TRACE_ANDROID_FS_H */ + +/* This part must be outside protection */ +#include <trace/define_trace.h> + +#ifndef ANDROID_FSTRACE_GET_PATHNAME +#define ANDROID_FSTRACE_GET_PATHNAME + +/* Sizes an on-stack array, so careful if sizing this up ! */ +#define MAX_TRACE_PATHBUF_LEN 256 + +static inline char * +android_fstrace_get_pathname(char *buf, int buflen, struct inode *inode) +{ + char *path; + struct dentry *d; + + /* + * d_obtain_alias() will either iput() if it locates an existing + * dentry or transfer the reference to the new dentry created. + * So get an extra reference here. + */ + ihold(inode); + d = d_obtain_alias(inode); + if (likely(!IS_ERR(d))) { + path = dentry_path_raw(d, buf, buflen); + if (unlikely(IS_ERR(path))) { + strcpy(buf, "ERROR"); + path = buf; + } + dput(d); + } else { + strcpy(buf, "ERROR"); + path = buf; + } + return path; +} +#endif diff --git a/include/trace/events/android_fs_template.h b/include/trace/events/android_fs_template.h new file mode 100644 index 000000000000..b23d17b56c63 --- /dev/null +++ b/include/trace/events/android_fs_template.h @@ -0,0 +1,64 @@ +#if !defined(_TRACE_ANDROID_FS_TEMPLATE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_ANDROID_FS_TEMPLATE_H + +#include <linux/tracepoint.h> + +DECLARE_EVENT_CLASS(android_fs_data_start_template, + TP_PROTO(struct inode *inode, loff_t offset, int bytes, + pid_t pid, char *pathname, char *command), + TP_ARGS(inode, offset, bytes, pid, pathname, command), + TP_STRUCT__entry( + __string(pathbuf, pathname); + __field(loff_t, offset); + __field(int, bytes); + __field(loff_t, i_size); + __string(cmdline, command); + __field(pid_t, pid); + __field(ino_t, ino); + ), + TP_fast_assign( + { + /* + * Replace the spaces in filenames and cmdlines + * because this screws up the tooling that parses + * the traces. + */ + __assign_str(pathbuf, pathname); + (void)strreplace(__get_str(pathbuf), ' ', '_'); + __entry->offset = offset; + __entry->bytes = bytes; + __entry->i_size = i_size_read(inode); + __assign_str(cmdline, command); + (void)strreplace(__get_str(cmdline), ' ', '_'); + __entry->pid = pid; + __entry->ino = inode->i_ino; + } + ), + TP_printk("entry_name %s, offset %llu, bytes %d, cmdline %s," + " pid %d, i_size %llu, ino %lu", + __get_str(pathbuf), __entry->offset, __entry->bytes, + __get_str(cmdline), __entry->pid, __entry->i_size, + (unsigned long) __entry->ino) +); + +DECLARE_EVENT_CLASS(android_fs_data_end_template, + TP_PROTO(struct inode *inode, loff_t offset, int bytes), + TP_ARGS(inode, offset, bytes), + TP_STRUCT__entry( + __field(ino_t, ino); + __field(loff_t, offset); + __field(int, bytes); + ), + TP_fast_assign( + { + __entry->ino = inode->i_ino; + __entry->offset = offset; + __entry->bytes = bytes; + } + ), + TP_printk("ino %lu, offset %llu, bytes %d", + (unsigned long) __entry->ino, + __entry->offset, __entry->bytes) +); + +#endif /* _TRACE_ANDROID_FS_TEMPLATE_H */ diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 09c22caf34dd..1c5e74f1ea39 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -73,7 +73,8 @@ #define DRM_MODE_FLAG_3D_TOP_AND_BOTTOM (7<<14) #define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF (8<<14) #define DRM_MODE_FLAG_SEAMLESS (1<<19) - +#define DRM_MODE_FLAG_SUPPORTS_RGB (1<<20) +#define DRM_MODE_FLAG_SUPPORTS_YUV (1<<21) /* DPMS flags */ /* bit compatible with the xorg definitions. */ diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index 8baf2bf6df2e..c974714a9abe 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -152,10 +152,13 @@ struct drm_msm_gem_submit_reloc { * this buffer in the first-level ringbuffer * CTX_RESTORE_BUF - only executed if there has been a GPU context * switch since the last SUBMIT ioctl + * PROFILE_BUF - A profiling buffer written to by both GPU and CPU. */ #define MSM_SUBMIT_CMD_BUF 0x0001 #define MSM_SUBMIT_CMD_IB_TARGET_BUF 0x0002 #define MSM_SUBMIT_CMD_CTX_RESTORE_BUF 0x0003 +#define MSM_SUBMIT_CMD_PROFILE_BUF 0x0004 + struct drm_msm_gem_submit_cmd { __u32 type; /* in, one of MSM_SUBMIT_CMD_x */ __u32 submit_idx; /* in, index of submit_bo cmdstream buffer */ @@ -207,6 +210,14 @@ struct drm_msm_gem_submit { __u64 __user cmds; /* in, ptr to array of submit_cmd's */ }; +struct drm_msm_gem_submit_profile_buffer { + __s64 queue_time; /* out, Ringbuffer queue time (seconds) */ + __s64 submit_time; /* out, Ringbuffer submission time (seconds) */ + __u64 ticks_queued; /* out, GPU ticks at ringbuffer submission */ + __u64 ticks_submitted; /* out, GPU ticks before cmdstream execution*/ + __u64 ticks_retired; /* out, GPU ticks after cmdstream execution */ +}; + /* The normal way to synchronize with the GPU is just to CPU_PREP on * a buffer if you need to access it from the CPU (other cmdstream * submission from same or other contexts, PAGE_FLIP ioctl, etc, all diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index 51f891fb1b18..7668b5791c91 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -132,6 +132,7 @@ enum { /* struct binder_fd_array_object - object describing an array of fds in a buffer * @hdr: common header structure + * @pad: padding to ensure correct alignment * @num_fds: number of file descriptors in the buffer * @parent: index in offset array to buffer holding the fd array * @parent_offset: start offset of fd array in the buffer @@ -152,6 +153,7 @@ enum { */ struct binder_fd_array_object { struct binder_object_header hdr; + __u32 pad; binder_size_t num_fds; binder_size_t parent; binder_size_t parent_offset; diff --git a/include/uapi/linux/hbtp_input.h b/include/uapi/linux/hbtp_input.h index 9173c2ab72ed..3b124ffcdcf3 100644 --- a/include/uapi/linux/hbtp_input.h +++ b/include/uapi/linux/hbtp_input.h @@ -6,6 +6,8 @@ #define HBTP_MAX_FINGER 20 #define HBTP_ABS_MT_FIRST ABS_MT_TOUCH_MAJOR #define HBTP_ABS_MT_LAST ABS_MT_TOOL_Y +#define MAX_ROI_SIZE 144 +#define MAX_ACCEL_SIZE 128 #define HBTP_EVENT_TYPE_DISPLAY "EVENT_TYPE=HBTP_DISPLAY" @@ -20,6 +22,11 @@ struct hbtp_input_touch { __s32 orientation; }; +struct hbtp_sensor_data { + __s16 accelBuffer[MAX_ACCEL_SIZE]; + __s16 ROI[MAX_ROI_SIZE]; +}; + struct hbtp_input_mt { __s32 num_touches; struct hbtp_input_touch touches[HBTP_MAX_FINGER]; @@ -68,6 +75,8 @@ enum hbtp_afe_power_ctrl { enum hbtp_afe_signal) #define HBTP_SET_POWER_CTRL _IOW(HBTP_INPUT_IOCTL_BASE, 206, \ enum hbtp_afe_power_ctrl) +#define HBTP_SET_SENSORDATA _IOW(HBTP_INPUT_IOCTL_BASE, 207, \ + struct hbtp_sensor_data) #endif /* _UAPI_HBTP_INPUT_H */ diff --git a/include/uapi/linux/hw_breakpoint.h b/include/uapi/linux/hw_breakpoint.h index b04000a2296a..2b65efd19a46 100644 --- a/include/uapi/linux/hw_breakpoint.h +++ b/include/uapi/linux/hw_breakpoint.h @@ -4,7 +4,11 @@ enum { HW_BREAKPOINT_LEN_1 = 1, HW_BREAKPOINT_LEN_2 = 2, + HW_BREAKPOINT_LEN_3 = 3, HW_BREAKPOINT_LEN_4 = 4, + HW_BREAKPOINT_LEN_5 = 5, + HW_BREAKPOINT_LEN_6 = 6, + HW_BREAKPOINT_LEN_7 = 7, HW_BREAKPOINT_LEN_8 = 8, }; diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 35bf6f1b186d..f0c24f24f0c3 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -176,6 +176,16 @@ enum { DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT, DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN, DEVCONF_ACCEPT_RA_PREFIX_ROUTE, + DEVCONF_DROP_UNICAST_IN_L2_MULTICAST, + DEVCONF_DROP_UNSOLICITED_NA, + DEVCONF_KEEP_ADDR_ON_DOWN, + DEVCONF_RTR_SOLICIT_MAX_INTERVAL, + DEVCONF_SEG6_ENABLED, + DEVCONF_SEG6_REQUIRE_HMAC, + DEVCONF_ENHANCED_DAD, + DEVCONF_ADDR_GEN_MODE, + DEVCONF_DISABLE_POLICY, + DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN, DEVCONF_MAX }; diff --git a/include/uapi/linux/msm_kgsl.h b/include/uapi/linux/msm_kgsl.h index 843e02711aa7..5fd58dfb153e 100644 --- a/include/uapi/linux/msm_kgsl.h +++ b/include/uapi/linux/msm_kgsl.h @@ -67,6 +67,8 @@ #define KGSL_CONTEXT_TYPE_RS 4 #define KGSL_CONTEXT_TYPE_UNKNOWN 0x1E +#define KGSL_CONTEXT_INVALIDATE_ON_FAULT 0x10000000 + #define KGSL_CONTEXT_INVALID 0xffffffff /* diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h index ee4581d652f4..a9d1772199bf 100644 --- a/include/uapi/linux/netlink.h +++ b/include/uapi/linux/netlink.h @@ -107,8 +107,10 @@ struct nlmsgerr { #define NETLINK_PKTINFO 3 #define NETLINK_BROADCAST_ERROR 4 #define NETLINK_NO_ENOBUFS 5 +#ifndef __KERNEL__ #define NETLINK_RX_RING 6 #define NETLINK_TX_RING 7 +#endif #define NETLINK_LISTEN_ALL_NSID 8 #define NETLINK_LIST_MEMBERSHIPS 9 #define NETLINK_CAP_ACK 10 @@ -134,6 +136,7 @@ struct nl_mmap_hdr { __u32 nm_gid; }; +#ifndef __KERNEL__ enum nl_mmap_status { NL_MMAP_STATUS_UNUSED, NL_MMAP_STATUS_RESERVED, @@ -145,6 +148,7 @@ enum nl_mmap_status { #define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO #define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT) #define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr)) +#endif #define NET_MAJOR 36 /* Major 36 is reserved for networking */ diff --git a/include/uapi/linux/netlink_diag.h b/include/uapi/linux/netlink_diag.h index f2159d30d1f5..d79399394b46 100644 --- a/include/uapi/linux/netlink_diag.h +++ b/include/uapi/linux/netlink_diag.h @@ -48,6 +48,8 @@ enum { #define NDIAG_SHOW_MEMINFO 0x00000001 /* show memory info of a socket */ #define NDIAG_SHOW_GROUPS 0x00000002 /* show groups of a netlink socket */ +#ifndef __KERNEL__ #define NDIAG_SHOW_RING_CFG 0x00000004 /* show ring configuration */ +#endif #endif diff --git a/include/uapi/linux/packet_diag.h b/include/uapi/linux/packet_diag.h index d08c63f3dd6f..0c5d5dd61b6a 100644 --- a/include/uapi/linux/packet_diag.h +++ b/include/uapi/linux/packet_diag.h @@ -64,7 +64,7 @@ struct packet_diag_mclist { __u32 pdmc_count; __u16 pdmc_type; __u16 pdmc_alen; - __u8 pdmc_addr[MAX_ADDR_LEN]; + __u8 pdmc_addr[32]; /* MAX_ADDR_LEN */ }; struct packet_diag_ring { diff --git a/include/uapi/linux/sysctl.h b/include/uapi/linux/sysctl.h index 33c6cde783ce..01eb22ca6b3d 100644 --- a/include/uapi/linux/sysctl.h +++ b/include/uapi/linux/sysctl.h @@ -571,6 +571,7 @@ enum { NET_IPV6_PROXY_NDP=23, NET_IPV6_ACCEPT_SOURCE_ROUTE=25, NET_IPV6_ACCEPT_RA_FROM_LOCAL=26, + NET_IPV6_ACCEPT_RA_RT_INFO_MIN_PLEN=27, __NET_IPV6_MAX }; diff --git a/include/uapi/linux/usb/audio.h b/include/uapi/linux/usb/audio.h index d2314be4f0c0..c6f5b096c594 100644 --- a/include/uapi/linux/usb/audio.h +++ b/include/uapi/linux/usb/audio.h @@ -26,6 +26,7 @@ /* bInterfaceProtocol values to denote the version of the standard used */ #define UAC_VERSION_1 0x00 #define UAC_VERSION_2 0x20 +#define UAC_VERSION_3 0x30 /* A.2 Audio Interface Subclass Codes */ #define USB_SUBCLASS_AUDIOCONTROL 0x01 diff --git a/include/uapi/media/msm_camsensor_sdk.h b/include/uapi/media/msm_camsensor_sdk.h index ad0825e33217..83927c614e91 100644 --- a/include/uapi/media/msm_camsensor_sdk.h +++ b/include/uapi/media/msm_camsensor_sdk.h @@ -48,6 +48,8 @@ #define MSM_EEPROM_MEMORY_MAP_MAX_SIZE 80 #define MSM_EEPROM_MAX_MEM_MAP_CNT 8 +#define MSM_SENSOR_BYPASS_VIDEO_NODE 1 + enum msm_sensor_camera_id_t { CAMERA_0, CAMERA_1, @@ -300,6 +302,7 @@ struct msm_camera_sensor_slave_info { unsigned char is_init_params_valid; struct msm_sensor_init_params sensor_init_params; enum msm_sensor_output_format_t output_format; + uint8_t bypass_video_node_creation; }; struct msm_camera_i2c_reg_array { diff --git a/include/uapi/media/msmb_ispif.h b/include/uapi/media/msmb_ispif.h index 3720056aa28d..c3a6e006b2ff 100644 --- a/include/uapi/media/msmb_ispif.h +++ b/include/uapi/media/msmb_ispif.h @@ -36,7 +36,6 @@ enum msm_ispif_intftype { #define RDI1_MASK (1 << RDI1) #define RDI2_MASK (1 << RDI2) - enum msm_ispif_vc { VC0, VC1, @@ -102,10 +101,17 @@ struct msm_ispif_params_entry { uint16_t crop_end_pixel; }; +struct msm_ispif_right_param_entry { + enum msm_ispif_cid cids[MAX_CID_CH_PARAM_ENTRY]; + enum msm_ispif_csid csid; +}; + struct msm_ispif_param_data_ext { uint32_t num; struct msm_ispif_params_entry entries[MAX_PARAM_ENTRIES]; struct msm_ispif_pack_cfg pack_cfg[CID_MAX]; + struct msm_ispif_right_param_entry right_entries[MAX_PARAM_ENTRIES]; + uint32_t stereo_enable; }; struct msm_ispif_param_data { @@ -157,6 +163,8 @@ struct ispif_cfg_data_ext { #define ISPIF_RDI_PACK_MODE_SUPPORT 1 +#define ISPIF_3D_SUPPORT 1 + #define VIDIOC_MSM_ISPIF_CFG \ _IOWR('V', BASE_VIDIOC_PRIVATE, struct ispif_cfg_data) diff --git a/include/uapi/sound/compress_params.h b/include/uapi/sound/compress_params.h index 6a297ad6d380..f4028e805cc7 100644 --- a/include/uapi/sound/compress_params.h +++ b/include/uapi/sound/compress_params.h @@ -104,7 +104,8 @@ #define SND_AUDIOCODEC_APE ((__u32) 0x00000020) #define SND_AUDIOCODEC_DSD ((__u32) 0x00000021) #define SND_AUDIOCODEC_APTX ((__u32) 0x00000022) -#define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_APTX +#define SND_AUDIOCODEC_TRUEHD ((__u32) 0x00000023) +#define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_TRUEHD /* * Profile and modes are listed with bit masks. This allows for a * more compact representation of fields that will not evolve diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c index ecda58df9a19..bce1c2fbb915 100644 --- a/init/do_mounts_dm.c +++ b/init/do_mounts_dm.c @@ -5,13 +5,17 @@ * * This file is released under the GPL. */ +#include <linux/async.h> +#include <linux/ctype.h> #include <linux/device-mapper.h> #include <linux/fs.h> #include <linux/string.h> +#include <linux/delay.h> #include "do_mounts.h" -#include "../drivers/md/dm.h" +#define DM_MAX_DEVICES 256 +#define DM_MAX_TARGETS 256 #define DM_MAX_NAME 32 #define DM_MAX_UUID 129 #define DM_NO_UUID "none" @@ -19,14 +23,47 @@ #define DM_MSG_PREFIX "init" /* Separators used for parsing the dm= argument. */ -#define DM_FIELD_SEP ' ' -#define DM_LINE_SEP ',' +#define DM_FIELD_SEP " " +#define DM_LINE_SEP "," +#define DM_ANY_SEP DM_FIELD_SEP DM_LINE_SEP /* * When the device-mapper and any targets are compiled into the kernel - * (not a module), one target may be created and used as the root device at - * boot time with the parameters given with the boot line dm=... - * The code for that is here. + * (not a module), one or more device-mappers may be created and used + * as the root device at boot time with the parameters given with the + * boot line dm=... + * + * Multiple device-mappers can be stacked specifing the number of + * devices. A device can have multiple targets if the the number of + * targets is specified. + * + * TODO(taysom:defect 32847) + * In the future, the <num> field will be mandatory. + * + * <device> ::= [<num>] <device-mapper>+ + * <device-mapper> ::= <head> "," <target>+ + * <head> ::= <name> <uuid> <mode> [<num>] + * <target> ::= <start> <length> <type> <options> "," + * <mode> ::= "ro" | "rw" + * <uuid> ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | "none" + * <type> ::= "verity" | "bootcache" | ... + * + * Example: + * 2 vboot none ro 1, + * 0 1768000 bootcache + * device=aa55b119-2a47-8c45-946a-5ac57765011f+1 + * signature=76e9be054b15884a9fa85973e9cb274c93afadb6 + * cache_start=1768000 max_blocks=100000 size_limit=23 max_trace=20000, + * vroot none ro 1, + * 0 1740800 verity payload=254:0 hashtree=254:0 hashstart=1740800 alg=sha1 + * root_hexdigest=76e9be054b15884a9fa85973e9cb274c93afadb6 + * salt=5b3549d54d6c7a3837b9b81ed72e49463a64c03680c47835bef94d768e5646fe + * + * Notes: + * 1. uuid is a label for the device and we set it to "none". + * 2. The <num> field will be optional initially and assumed to be 1. + * Once all the scripts that set these fields have been set, it will + * be made mandatory. */ struct dm_setup_target { @@ -38,381 +75,388 @@ struct dm_setup_target { struct dm_setup_target *next; }; -static struct { +struct dm_device { int minor; int ro; char name[DM_MAX_NAME]; char uuid[DM_MAX_UUID]; - char *targets; + unsigned long num_targets; struct dm_setup_target *target; int target_count; + struct dm_device *next; +}; + +struct dm_option { + char *start; + char *next; + size_t len; + char delim; +}; + +static struct { + unsigned long num_devices; + char *str; } dm_setup_args __initdata; static __initdata int dm_early_setup; -static size_t __init get_dm_option(char *str, char **next, char sep) +static int __init get_dm_option(struct dm_option *opt, const char *accept) { - size_t len = 0; - char *endp = NULL; + char *str = opt->next; + char *endp; if (!str) return 0; - endp = strchr(str, sep); + str = skip_spaces(str); + opt->start = str; + endp = strpbrk(str, accept); if (!endp) { /* act like strchrnul */ - len = strlen(str); - endp = str + len; + opt->len = strlen(str); + endp = str + opt->len; } else { - len = endp - str; + opt->len = endp - str; } - - if (endp == str) - return 0; - - if (!next) - return len; - + opt->delim = *endp; if (*endp == 0) { /* Don't advance past the nul. */ - *next = endp; + opt->next = endp; } else { - *next = endp + 1; + opt->next = endp + 1; } - return len; -} - -static int __init dm_setup_args_init(void) -{ - dm_setup_args.minor = 0; - dm_setup_args.ro = 0; - dm_setup_args.target = NULL; - dm_setup_args.target_count = 0; - return 0; + return opt->len != 0; } -static int __init dm_setup_cleanup(void) +static int __init dm_setup_cleanup(struct dm_device *devices) { - struct dm_setup_target *target = dm_setup_args.target; - struct dm_setup_target *old_target = NULL; - while (target) { - kfree(target->type); - kfree(target->params); - old_target = target; - target = target->next; - kfree(old_target); - dm_setup_args.target_count--; + struct dm_device *dev = devices; + + while (dev) { + struct dm_device *old_dev = dev; + struct dm_setup_target *target = dev->target; + while (target) { + struct dm_setup_target *old_target = target; + kfree(target->type); + kfree(target->params); + target = target->next; + kfree(old_target); + dev->target_count--; + } + BUG_ON(dev->target_count); + dev = dev->next; + kfree(old_dev); } - BUG_ON(dm_setup_args.target_count); return 0; } -static char * __init dm_setup_parse_device_args(char *str) +static char * __init dm_parse_device(struct dm_device *dev, char *str) { - char *next = NULL; - size_t len = 0; + struct dm_option opt; + size_t len; /* Grab the logical name of the device to be exported to udev */ - len = get_dm_option(str, &next, DM_FIELD_SEP); - if (!len) { + opt.next = str; + if (!get_dm_option(&opt, DM_FIELD_SEP)) { DMERR("failed to parse device name"); goto parse_fail; } - len = min(len + 1, sizeof(dm_setup_args.name)); - strlcpy(dm_setup_args.name, str, len); /* includes nul */ - str = skip_spaces(next); + len = min(opt.len + 1, sizeof(dev->name)); + strlcpy(dev->name, opt.start, len); /* includes nul */ /* Grab the UUID value or "none" */ - len = get_dm_option(str, &next, DM_FIELD_SEP); - if (!len) { + if (!get_dm_option(&opt, DM_FIELD_SEP)) { DMERR("failed to parse device uuid"); goto parse_fail; } - len = min(len + 1, sizeof(dm_setup_args.uuid)); - strlcpy(dm_setup_args.uuid, str, len); - str = skip_spaces(next); + len = min(opt.len + 1, sizeof(dev->uuid)); + strlcpy(dev->uuid, opt.start, len); /* Determine if the table/device will be read only or read-write */ - if (!strncmp("ro,", str, 3)) { - dm_setup_args.ro = 1; - } else if (!strncmp("rw,", str, 3)) { - dm_setup_args.ro = 0; + get_dm_option(&opt, DM_ANY_SEP); + if (!strncmp("ro", opt.start, opt.len)) { + dev->ro = 1; + } else if (!strncmp("rw", opt.start, opt.len)) { + dev->ro = 0; } else { DMERR("failed to parse table mode"); goto parse_fail; } - str = skip_spaces(str + 3); - return str; + /* Optional number field */ + /* XXX: The <num> field will be mandatory in the next round */ + if (opt.delim == DM_FIELD_SEP[0]) { + if (!get_dm_option(&opt, DM_LINE_SEP)) + return NULL; + dev->num_targets = simple_strtoul(opt.start, NULL, 10); + } else { + dev->num_targets = 1; + } + if (dev->num_targets > DM_MAX_TARGETS) { + DMERR("too many targets %lu > %d", + dev->num_targets, DM_MAX_TARGETS); + } + return opt.next; parse_fail: return NULL; } -static void __init dm_substitute_devices(char *str, size_t str_len) +static char * __init dm_parse_targets(struct dm_device *dev, char *str) { - char *candidate = str; - char *candidate_end = str; - char old_char; - size_t len = 0; - dev_t dev; - - if (str_len < 3) - return; - - while (str && *str) { - candidate = strchr(str, '/'); - if (!candidate) - break; - - /* Avoid embedded slashes */ - if (candidate != str && *(candidate - 1) != DM_FIELD_SEP) { - str = strchr(candidate, DM_FIELD_SEP); - continue; - } - - len = get_dm_option(candidate, &candidate_end, DM_FIELD_SEP); - str = skip_spaces(candidate_end); - if (len < 3 || len > 37) /* name_to_dev_t max; maj:mix min */ - continue; - - /* Temporarily terminate with a nul */ - if (*candidate_end) - candidate_end--; - old_char = *candidate_end; - *candidate_end = '\0'; - - DMDEBUG("converting candidate device '%s' to dev_t", candidate); - /* Use the boot-time specific device naming */ - dev = name_to_dev_t(candidate); - *candidate_end = old_char; - - DMDEBUG(" -> %u", dev); - /* No suitable replacement found */ - if (!dev) - continue; - - /* Rewrite the /dev/path as a major:minor */ - len = snprintf(candidate, len, "%u:%u", MAJOR(dev), MINOR(dev)); - if (!len) { - DMERR("error substituting device major/minor."); - break; - } - candidate += len; - /* Pad out with spaces (fixing our nul) */ - while (candidate < candidate_end) - *(candidate++) = DM_FIELD_SEP; - } -} - -static int __init dm_setup_parse_targets(char *str) -{ - char *next = NULL; - size_t len = 0; - struct dm_setup_target **target = NULL; + struct dm_option opt; + struct dm_setup_target **target = &dev->target; + unsigned long num_targets = dev->num_targets; + unsigned long i; /* Targets are defined as per the table format but with a * comma as a newline separator. */ - target = &dm_setup_args.target; - while (str && *str) { + opt.next = str; + for (i = 0; i < num_targets; i++) { *target = kzalloc(sizeof(struct dm_setup_target), GFP_KERNEL); if (!*target) { - DMERR("failed to allocate memory for target %d", - dm_setup_args.target_count); + DMERR("failed to allocate memory for target %s<%ld>", + dev->name, i); goto parse_fail; } - dm_setup_args.target_count++; + dev->target_count++; - (*target)->begin = simple_strtoull(str, &next, 10); - if (!next || *next != DM_FIELD_SEP) { - DMERR("failed to parse starting sector for target %d", - dm_setup_args.target_count - 1); + if (!get_dm_option(&opt, DM_FIELD_SEP)) { + DMERR("failed to parse starting sector" + " for target %s<%ld>", dev->name, i); goto parse_fail; } - str = skip_spaces(next + 1); + (*target)->begin = simple_strtoull(opt.start, NULL, 10); - (*target)->length = simple_strtoull(str, &next, 10); - if (!next || *next != DM_FIELD_SEP) { - DMERR("failed to parse length for target %d", - dm_setup_args.target_count - 1); + if (!get_dm_option(&opt, DM_FIELD_SEP)) { + DMERR("failed to parse length for target %s<%ld>", + dev->name, i); goto parse_fail; } - str = skip_spaces(next + 1); - - len = get_dm_option(str, &next, DM_FIELD_SEP); - if (!len || - !((*target)->type = kstrndup(str, len, GFP_KERNEL))) { - DMERR("failed to parse type for target %d", - dm_setup_args.target_count - 1); + (*target)->length = simple_strtoull(opt.start, NULL, 10); + + if (get_dm_option(&opt, DM_FIELD_SEP)) + (*target)->type = kstrndup(opt.start, opt.len, + GFP_KERNEL); + if (!((*target)->type)) { + DMERR("failed to parse type for target %s<%ld>", + dev->name, i); goto parse_fail; } - str = skip_spaces(next); - - len = get_dm_option(str, &next, DM_LINE_SEP); - if (!len || - !((*target)->params = kstrndup(str, len, GFP_KERNEL))) { - DMERR("failed to parse params for target %d", - dm_setup_args.target_count - 1); + if (get_dm_option(&opt, DM_LINE_SEP)) + (*target)->params = kstrndup(opt.start, opt.len, + GFP_KERNEL); + if (!((*target)->params)) { + DMERR("failed to parse params for target %s<%ld>", + dev->name, i); goto parse_fail; } - str = skip_spaces(next); - - /* Before moving on, walk through the copied target and - * attempt to replace all /dev/xxx with the major:minor number. - * It may not be possible to resolve them traditionally at - * boot-time. */ - dm_substitute_devices((*target)->params, len); - target = &((*target)->next); } - DMDEBUG("parsed %d targets", dm_setup_args.target_count); + DMDEBUG("parsed %d targets", dev->target_count); - return 0; + return opt.next; parse_fail: - return 1; + return NULL; +} + +static struct dm_device * __init dm_parse_args(void) +{ + struct dm_device *devices = NULL; + struct dm_device **tail = &devices; + struct dm_device *dev; + char *str = dm_setup_args.str; + unsigned long num_devices = dm_setup_args.num_devices; + unsigned long i; + + if (!str) + return NULL; + for (i = 0; i < num_devices; i++) { + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + DMERR("failed to allocated memory for dev"); + goto error; + } + *tail = dev; + tail = &dev->next; + /* + * devices are given minor numbers 0 - n-1 + * in the order they are found in the arg + * string. + */ + dev->minor = i; + str = dm_parse_device(dev, str); + if (!str) /* NULL indicates error in parsing, bail */ + goto error; + + str = dm_parse_targets(dev, str); + if (!str) + goto error; + } + return devices; +error: + dm_setup_cleanup(devices); + return NULL; } /* * Parse the command-line parameters given our kernel, but do not * actually try to invoke the DM device now; that is handled by - * dm_setup_drive after the low-level disk drivers have initialised. - * dm format is as follows: - * dm="name uuid fmode,[table line 1],[table line 2],..." - * May be used with root=/dev/dm-0 as it always uses the first dm minor. + * dm_setup_drives after the low-level disk drivers have initialised. + * dm format is described at the top of the file. + * + * Because dm minor numbers are assigned in assending order starting with 0, + * You can assume the first device is /dev/dm-0, the next device is /dev/dm-1, + * and so forth. */ - static int __init dm_setup(char *str) { - dm_setup_args_init(); + struct dm_option opt; + unsigned long num_devices; - str = dm_setup_parse_device_args(str); if (!str) { DMDEBUG("str is NULL"); goto parse_fail; } - - /* Target parsing is delayed until we have dynamic memory */ - dm_setup_args.targets = str; - - printk(KERN_INFO "dm: will configure '%s' on dm-%d\n", - dm_setup_args.name, dm_setup_args.minor); - + opt.next = str; + if (!get_dm_option(&opt, DM_FIELD_SEP)) + goto parse_fail; + if (isdigit(opt.start[0])) { /* XXX: Optional number field */ + num_devices = simple_strtoul(opt.start, NULL, 10); + str = opt.next; + } else { + num_devices = 1; + /* Don't advance str */ + } + if (num_devices > DM_MAX_DEVICES) { + DMDEBUG("too many devices %lu > %d", + num_devices, DM_MAX_DEVICES); + } + dm_setup_args.str = str; + dm_setup_args.num_devices = num_devices; + DMINFO("will configure %lu devices", num_devices); dm_early_setup = 1; return 1; parse_fail: - printk(KERN_WARNING "dm: Invalid arguments supplied to dm=.\n"); + DMWARN("Invalid arguments supplied to dm=."); return 0; } - -static void __init dm_setup_drive(void) +static void __init dm_setup_drives(void) { struct mapped_device *md = NULL; struct dm_table *table = NULL; struct dm_setup_target *target; - char *uuid = dm_setup_args.uuid; + struct dm_device *dev; + char *uuid; fmode_t fmode = FMODE_READ; + struct dm_device *devices; - /* Finish parsing the targets. */ - if (dm_setup_parse_targets(dm_setup_args.targets)) - goto parse_fail; - - if (dm_create(dm_setup_args.minor, &md)) { - DMDEBUG("failed to create the device"); - goto dm_create_fail; - } - DMDEBUG("created device '%s'", dm_device_name(md)); - - /* In addition to flagging the table below, the disk must be - * set explicitly ro/rw. */ - set_disk_ro(dm_disk(md), dm_setup_args.ro); + devices = dm_parse_args(); - if (!dm_setup_args.ro) - fmode |= FMODE_WRITE; - if (dm_table_create(&table, fmode, dm_setup_args.target_count, md)) { - DMDEBUG("failed to create the table"); - goto dm_table_create_fail; - } + for (dev = devices; dev; dev = dev->next) { + if (dm_create(dev->minor, &md)) { + DMDEBUG("failed to create the device"); + goto dm_create_fail; + } + DMDEBUG("created device '%s'", dm_device_name(md)); + + /* + * In addition to flagging the table below, the disk must be + * set explicitly ro/rw. + */ + set_disk_ro(dm_disk(md), dev->ro); + + if (!dev->ro) + fmode |= FMODE_WRITE; + if (dm_table_create(&table, fmode, dev->target_count, md)) { + DMDEBUG("failed to create the table"); + goto dm_table_create_fail; + } - dm_lock_md_type(md); - target = dm_setup_args.target; - while (target) { - DMINFO("adding target '%llu %llu %s %s'", - (unsigned long long) target->begin, - (unsigned long long) target->length, target->type, - target->params); - if (dm_table_add_target(table, target->type, target->begin, - target->length, target->params)) { - DMDEBUG("failed to add the target to the table"); - goto add_target_fail; + dm_lock_md_type(md); + + for (target = dev->target; target; target = target->next) { + DMINFO("adding target '%llu %llu %s %s'", + (unsigned long long) target->begin, + (unsigned long long) target->length, + target->type, target->params); + if (dm_table_add_target(table, target->type, + target->begin, + target->length, + target->params)) { + DMDEBUG("failed to add the target" + " to the table"); + goto add_target_fail; + } + } + if (dm_table_complete(table)) { + DMDEBUG("failed to complete the table"); + goto table_complete_fail; } - target = target->next; - } - if (dm_table_complete(table)) { - DMDEBUG("failed to complete the table"); - goto table_complete_fail; - } + /* Suspend the device so that we can bind it to the table. */ + if (dm_suspend(md, 0)) { + DMDEBUG("failed to suspend the device pre-bind"); + goto suspend_fail; + } - if (dm_get_md_type(md) == DM_TYPE_NONE) { + /* Initial table load: acquire type of table. */ dm_set_md_type(md, dm_table_get_type(table)); + + /* Setup md->queue to reflect md's type. */ if (dm_setup_md_queue(md)) { DMWARN("unable to set up device queue for new table."); goto setup_md_queue_fail; } - } else if (dm_get_md_type(md) != dm_table_get_type(table)) { - DMWARN("can't change device type after initial table load."); - goto setup_md_queue_fail; - } - - /* Suspend the device so that we can bind it to the table. */ - if (dm_suspend(md, 0)) { - DMDEBUG("failed to suspend the device pre-bind"); - goto suspend_fail; - } - /* Bind the table to the device. This is the only way to associate - * md->map with the table and set the disk capacity directly. */ - if (dm_swap_table(md, table)) { /* should return NULL. */ - DMDEBUG("failed to bind the device to the table"); - goto table_bind_fail; - } + /* + * Bind the table to the device. This is the only way + * to associate md->map with the table and set the disk + * capacity directly. + */ + if (dm_swap_table(md, table)) { /* should return NULL. */ + DMDEBUG("failed to bind the device to the table"); + goto table_bind_fail; + } - /* Finally, resume and the device should be ready. */ - if (dm_resume(md)) { - DMDEBUG("failed to resume the device"); - goto resume_fail; - } + /* Finally, resume and the device should be ready. */ + if (dm_resume(md)) { + DMDEBUG("failed to resume the device"); + goto resume_fail; + } - /* Export the dm device via the ioctl interface */ - if (!strcmp(DM_NO_UUID, dm_setup_args.uuid)) - uuid = NULL; - if (dm_ioctl_export(md, dm_setup_args.name, uuid)) { - DMDEBUG("failed to export device with given name and uuid"); - goto export_fail; - } - printk(KERN_INFO "dm: dm-%d is ready\n", dm_setup_args.minor); + /* Export the dm device via the ioctl interface */ + if (!strcmp(DM_NO_UUID, dev->uuid)) + uuid = NULL; + if (dm_ioctl_export(md, dev->name, uuid)) { + DMDEBUG("failed to export device with given" + " name and uuid"); + goto export_fail; + } - dm_unlock_md_type(md); - dm_setup_cleanup(); + dm_unlock_md_type(md); + + DMINFO("dm-%d is ready", dev->minor); + } + dm_setup_cleanup(devices); return; export_fail: resume_fail: table_bind_fail: -suspend_fail: setup_md_queue_fail: +suspend_fail: table_complete_fail: add_target_fail: dm_unlock_md_type(md); dm_table_create_fail: dm_put(md); dm_create_fail: - dm_setup_cleanup(); -parse_fail: - printk(KERN_WARNING "dm: starting dm-%d (%s) failed\n", - dm_setup_args.minor, dm_setup_args.name); + DMWARN("starting dm-%d (%s) failed", + dev->minor, dev->name); + dm_setup_cleanup(devices); } __setup("dm=", dm_setup); @@ -421,6 +465,6 @@ void __init dm_run_setup(void) { if (!dm_early_setup) return; - printk(KERN_INFO "dm: attempting early device configuration.\n"); - dm_setup_drive(); + DMINFO("attempting early device configuration."); + dm_setup_drives(); } diff --git a/kernel/cpu.c b/kernel/cpu.c index 0ca3599cee1f..e822cb0e18d5 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -593,8 +593,9 @@ out: if (!switch_err) { switch_err = switch_to_fair_policy(); - pr_err("Hotplug policy switch err. Task %s pid=%d\n", - current->comm, current->pid); + if (switch_err) + pr_err("Hotplug policy switch err=%d Task %s pid=%d\n", + switch_err, current->comm, current->pid); } return err; diff --git a/kernel/events/core.c b/kernel/events/core.c index f9c6f554460e..06db8b64129d 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -9455,7 +9455,7 @@ static int perf_event_init_context(struct task_struct *child, int ctxn) ret = inherit_task_group(event, parent, parent_ctx, child, ctxn, &inherited_all); if (ret) - break; + goto out_unlock; } /* @@ -9471,7 +9471,7 @@ static int perf_event_init_context(struct task_struct *child, int ctxn) ret = inherit_task_group(event, parent, parent_ctx, child, ctxn, &inherited_all); if (ret) - break; + goto out_unlock; } raw_spin_lock_irqsave(&parent_ctx->lock, flags); @@ -9499,6 +9499,7 @@ static int perf_event_init_context(struct task_struct *child, int ctxn) } raw_spin_unlock_irqrestore(&parent_ctx->lock, flags); +out_unlock: mutex_unlock(&parent_ctx->mutex); perf_unpin_context(parent_ctx); diff --git a/kernel/fork.c b/kernel/fork.c index 75573eeb49b2..622571d2a833 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -333,13 +333,14 @@ void set_task_stack_end_magic(struct task_struct *tsk) *stackend = STACK_END_MAGIC; /* for overflow detection */ } -static struct task_struct *dup_task_struct(struct task_struct *orig) +static struct task_struct *dup_task_struct(struct task_struct *orig, int node) { struct task_struct *tsk; struct thread_info *ti; - int node = tsk_fork_get_node(orig); int err; + if (node == NUMA_NO_NODE) + node = tsk_fork_get_node(orig); tsk = alloc_task_struct_node(node); if (!tsk) return NULL; @@ -1276,7 +1277,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, int __user *child_tidptr, struct pid *pid, int trace, - unsigned long tls) + unsigned long tls, + int node) { int retval; struct task_struct *p; @@ -1329,7 +1331,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, goto fork_out; retval = -ENOMEM; - p = dup_task_struct(current); + p = dup_task_struct(current, node); if (!p) goto fork_out; @@ -1706,7 +1708,8 @@ static inline void init_idle_pids(struct pid_link *links) struct task_struct *fork_idle(int cpu) { struct task_struct *task; - task = copy_process(CLONE_VM, 0, 0, NULL, &init_struct_pid, 0, 0); + task = copy_process(CLONE_VM, 0, 0, NULL, &init_struct_pid, 0, 0, + cpu_to_node(cpu)); if (!IS_ERR(task)) { init_idle_pids(task->pids); init_idle(task, cpu, false); @@ -1751,7 +1754,7 @@ long _do_fork(unsigned long clone_flags, } p = copy_process(clone_flags, stack_start, stack_size, - child_tidptr, NULL, trace, tls); + child_tidptr, NULL, trace, tls, NUMA_NO_NODE); /* * Do this prior waking up the new thread - the thread pointer * might get invalid after that point, if the thread exits quickly. diff --git a/kernel/futex.c b/kernel/futex.c index beb042dcc332..af29863f3349 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -2690,7 +2690,6 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags, { struct hrtimer_sleeper timeout, *to = NULL; struct rt_mutex_waiter rt_waiter; - struct rt_mutex *pi_mutex = NULL; struct futex_hash_bucket *hb; union futex_key key2 = FUTEX_KEY_INIT; struct futex_q q = futex_q_init; @@ -2774,6 +2773,8 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags, if (q.pi_state && (q.pi_state->owner != current)) { spin_lock(q.lock_ptr); ret = fixup_pi_state_owner(uaddr2, &q, current); + if (ret && rt_mutex_owner(&q.pi_state->pi_mutex) == current) + rt_mutex_unlock(&q.pi_state->pi_mutex); /* * Drop the reference to the pi state which * the requeue_pi() code acquired for us. @@ -2782,6 +2783,8 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags, spin_unlock(q.lock_ptr); } } else { + struct rt_mutex *pi_mutex; + /* * We have been woken up by futex_unlock_pi(), a timeout, or a * signal. futex_unlock_pi() will not destroy the lock_ptr nor @@ -2805,18 +2808,19 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags, if (res) ret = (res < 0) ? res : 0; + /* + * If fixup_pi_state_owner() faulted and was unable to handle + * the fault, unlock the rt_mutex and return the fault to + * userspace. + */ + if (ret && rt_mutex_owner(pi_mutex) == current) + rt_mutex_unlock(pi_mutex); + /* Unqueue and drop the lock. */ unqueue_me_pi(&q); } - /* - * If fixup_pi_state_owner() faulted and was unable to handle the - * fault, unlock the rt_mutex and return the fault to userspace. - */ - if (ret == -EFAULT) { - if (pi_mutex && rt_mutex_owner(pi_mutex) == current) - rt_mutex_unlock(pi_mutex); - } else if (ret == -EINTR) { + if (ret == -EINTR) { /* * We've already been requeued, but cannot restart by calling * futex_lock_pi() directly. We could restart this syscall, but diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 3124cebaec31..2fc1177383a0 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -1159,6 +1159,22 @@ static int __init kaslr_nohibernate_setup(char *str) return nohibernate_setup(str); } +static int __init page_poison_nohibernate_setup(char *str) +{ +#ifdef CONFIG_PAGE_POISONING_ZERO + /* + * The zeroing option for page poison skips the checks on alloc. + * since hibernation doesn't save free pages there's no way to + * guarantee the pages will still be zeroed. + */ + if (!strcmp(str, "on")) { + pr_info("Disabling hibernation due to page poisoning\n"); + return nohibernate_setup(str); + } +#endif + return 1; +} + __setup("noresume", noresume_setup); __setup("resume_offset=", resume_offset_setup); __setup("resume=", resume_setup); @@ -1167,3 +1183,4 @@ __setup("resumewait", resumewait_setup); __setup("resumedelay=", resumedelay_setup); __setup("nohibernate", nohibernate_setup); __setup("kaslr", kaslr_nohibernate_setup); +__setup("page_poison=", page_poison_nohibernate_setup); diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c index 1dde338d30f2..0b5f2dea18a1 100644 --- a/kernel/sched/core_ctl.c +++ b/kernel/sched/core_ctl.c @@ -35,6 +35,7 @@ struct cluster_data { unsigned int busy_down_thres[MAX_CPUS_PER_CLUSTER]; unsigned int active_cpus; unsigned int num_cpus; + unsigned int nr_isolated_cpus; cpumask_t cpu_mask; unsigned int need_cpus; unsigned int task_thres; @@ -260,6 +261,7 @@ static ssize_t show_global_state(const struct cluster_data *state, char *buf) ssize_t count = 0; unsigned int cpu; + spin_lock_irq(&state_lock); for_each_possible_cpu(cpu) { c = &per_cpu(cpu_state, cpu); cluster = c->cluster; @@ -293,8 +295,12 @@ static ssize_t show_global_state(const struct cluster_data *state, char *buf) count += snprintf(buf + count, PAGE_SIZE - count, "\tNeed CPUs: %u\n", cluster->need_cpus); count += snprintf(buf + count, PAGE_SIZE - count, + "\tNr isolated CPUs: %u\n", + cluster->nr_isolated_cpus); + count += snprintf(buf + count, PAGE_SIZE - count, "\tBoost: %u\n", (unsigned int) cluster->boost); } + spin_unlock_irq(&state_lock); return count; } @@ -525,7 +531,7 @@ static bool adjustment_possible(const struct cluster_data *cluster, unsigned int need) { return (need < cluster->active_cpus || (need > cluster->active_cpus && - sched_isolate_count(&cluster->cpu_mask, false))); + cluster->nr_isolated_cpus)); } static bool eval_need(struct cluster_data *cluster) @@ -535,9 +541,8 @@ static bool eval_need(struct cluster_data *cluster) unsigned int need_cpus = 0, last_need, thres_idx; int ret = 0; bool need_flag = false; - unsigned int active_cpus; unsigned int new_need; - s64 now; + s64 now, elapsed; if (unlikely(!cluster->inited)) return 0; @@ -547,8 +552,8 @@ static bool eval_need(struct cluster_data *cluster) if (cluster->boost) { need_cpus = cluster->max_cpus; } else { - active_cpus = get_active_cpu_count(cluster); - thres_idx = active_cpus ? active_cpus - 1 : 0; + cluster->active_cpus = get_active_cpu_count(cluster); + thres_idx = cluster->active_cpus ? cluster->active_cpus - 1 : 0; list_for_each_entry(c, &cluster->lru, sib) { if (c->busy >= cluster->busy_up_thres[thres_idx]) c->is_busy = true; @@ -564,17 +569,16 @@ static bool eval_need(struct cluster_data *cluster) last_need = cluster->need_cpus; now = ktime_to_ms(ktime_get()); - if (new_need == last_need) { - cluster->need_ts = now; - spin_unlock_irqrestore(&state_lock, flags); - return 0; - } - - if (need_cpus > cluster->active_cpus) { + if (new_need > cluster->active_cpus) { ret = 1; - } else if (need_cpus < cluster->active_cpus) { - s64 elapsed = now - cluster->need_ts; + } else { + if (new_need == last_need) { + cluster->need_ts = now; + spin_unlock_irqrestore(&state_lock, flags); + return 0; + } + elapsed = now - cluster->need_ts; ret = elapsed >= cluster->offline_delay_ms; } @@ -582,7 +586,7 @@ static bool eval_need(struct cluster_data *cluster) cluster->need_ts = now; cluster->need_cpus = new_need; } - trace_core_ctl_eval_need(cluster->first_cpu, last_need, need_cpus, + trace_core_ctl_eval_need(cluster->first_cpu, last_need, new_need, ret && need_flag); spin_unlock_irqrestore(&state_lock, flags); @@ -718,6 +722,7 @@ static void try_to_isolate(struct cluster_data *cluster, unsigned int need) struct cpu_data *c, *tmp; unsigned long flags; unsigned int num_cpus = cluster->num_cpus; + unsigned int nr_isolated = 0; /* * Protect against entry being removed (and added at tail) by other @@ -742,12 +747,14 @@ static void try_to_isolate(struct cluster_data *cluster, unsigned int need) if (!sched_isolate_cpu(c->cpu)) { c->isolated_by_us = true; move_cpu_lru(c); + nr_isolated++; } else { pr_debug("Unable to isolate CPU%u\n", c->cpu); } cluster->active_cpus = get_active_cpu_count(cluster); spin_lock_irqsave(&state_lock, flags); } + cluster->nr_isolated_cpus += nr_isolated; spin_unlock_irqrestore(&state_lock, flags); /* @@ -757,6 +764,7 @@ static void try_to_isolate(struct cluster_data *cluster, unsigned int need) if (cluster->active_cpus <= cluster->max_cpus) return; + nr_isolated = 0; num_cpus = cluster->num_cpus; spin_lock_irqsave(&state_lock, flags); list_for_each_entry_safe(c, tmp, &cluster->lru, sib) { @@ -774,12 +782,14 @@ static void try_to_isolate(struct cluster_data *cluster, unsigned int need) if (!sched_isolate_cpu(c->cpu)) { c->isolated_by_us = true; move_cpu_lru(c); + nr_isolated++; } else { pr_debug("Unable to isolate CPU%u\n", c->cpu); } cluster->active_cpus = get_active_cpu_count(cluster); spin_lock_irqsave(&state_lock, flags); } + cluster->nr_isolated_cpus += nr_isolated; spin_unlock_irqrestore(&state_lock, flags); } @@ -790,6 +800,7 @@ static void __try_to_unisolate(struct cluster_data *cluster, struct cpu_data *c, *tmp; unsigned long flags; unsigned int num_cpus = cluster->num_cpus; + unsigned int nr_unisolated = 0; /* * Protect against entry being removed (and added at tail) by other @@ -814,12 +825,14 @@ static void __try_to_unisolate(struct cluster_data *cluster, if (!sched_unisolate_cpu(c->cpu)) { c->isolated_by_us = false; move_cpu_lru(c); + nr_unisolated++; } else { pr_debug("Unable to unisolate CPU%u\n", c->cpu); } cluster->active_cpus = get_active_cpu_count(cluster); spin_lock_irqsave(&state_lock, flags); } + cluster->nr_isolated_cpus -= nr_unisolated; spin_unlock_irqrestore(&state_lock, flags); } @@ -885,10 +898,11 @@ static int __ref cpu_callback(struct notifier_block *nfb, struct cpu_data *state = &per_cpu(cpu_state, cpu); struct cluster_data *cluster = state->cluster; unsigned int need; - int ret = NOTIFY_OK; + bool do_wakeup, unisolated = false; + unsigned long flags; if (unlikely(!cluster || !cluster->inited)) - return NOTIFY_OK; + return NOTIFY_DONE; switch (action & ~CPU_TASKS_FROZEN) { case CPU_ONLINE: @@ -911,6 +925,7 @@ static int __ref cpu_callback(struct notifier_block *nfb, if (state->isolated_by_us) { sched_unisolate_cpu_unlocked(cpu); state->isolated_by_us = false; + unisolated = true; } /* Move a CPU to the end of the LRU when it goes offline. */ @@ -919,13 +934,20 @@ static int __ref cpu_callback(struct notifier_block *nfb, state->busy = 0; cluster->active_cpus = get_active_cpu_count(cluster); break; + default: + return NOTIFY_DONE; } need = apply_limits(cluster, cluster->need_cpus); - if (adjustment_possible(cluster, need)) + spin_lock_irqsave(&state_lock, flags); + if (unisolated) + cluster->nr_isolated_cpus--; + do_wakeup = adjustment_possible(cluster, need); + spin_unlock_irqrestore(&state_lock, flags); + if (do_wakeup) wake_up_core_ctl_thread(cluster); - return ret; + return NOTIFY_OK; } static struct notifier_block __refdata cpu_notifier = { diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index 685ae83b2bfa..3d55ec89c400 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -1843,12 +1843,11 @@ static void switched_to_dl(struct rq *rq, struct task_struct *p) #ifdef CONFIG_SMP if (p->nr_cpus_allowed > 1 && rq->dl.overloaded) queue_push_tasks(rq); -#else +#endif if (dl_task(rq->curr)) check_preempt_curr_dl(rq, p, 0); else resched_curr(rq); -#endif } } diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 8d5353906c8d..4d96380b35e8 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6060,7 +6060,7 @@ long group_norm_util(struct energy_env *eenv, struct sched_group *sg) } static int find_new_capacity(struct energy_env *eenv, - const struct sched_group_energy const *sge) + const struct sched_group_energy * const sge) { int idx; unsigned long util = group_max_util(eenv); diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 07b2c63e4983..29345ed74069 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -2425,10 +2425,9 @@ static void switched_to_rt(struct rq *rq, struct task_struct *p) #ifdef CONFIG_SMP if (p->nr_cpus_allowed > 1 && rq->rt.overloaded) queue_push_tasks(rq); -#else +#endif /* CONFIG_SMP */ if (p->prio < rq->curr->prio) resched_curr(rq); -#endif /* CONFIG_SMP */ } } diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 75500042fd32..276a2387f06f 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1011,7 +1011,7 @@ struct sched_group { unsigned int group_weight; struct sched_group_capacity *sgc; - const struct sched_group_energy const *sge; + const struct sched_group_energy *sge; /* * The CPUs this group covers. diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 651ff1a3a306..ec2102104cb8 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -19,6 +19,7 @@ #include <linux/percpu.h> #include <linux/profile.h> #include <linux/sched.h> +#include <linux/timer.h> #include <linux/module.h> #include <linux/irq_work.h> #include <linux/posix-timers.h> @@ -809,6 +810,11 @@ static void __tick_nohz_idle_enter(struct tick_sched *ts) now = tick_nohz_start_idle(ts); +#ifdef CONFIG_SMP + if (check_pending_deferrable_timers(cpu)) + raise_softirq_irqoff(TIMER_SOFTIRQ); +#endif + if (can_stop_idle_tick(cpu, ts)) { int was_stopped = ts->tick_stopped; diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 2bde2c2b1cb3..8315d4d72cc3 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -102,6 +102,7 @@ static DEFINE_PER_CPU(struct tvec_base, tvec_bases); unsigned int sysctl_timer_migration = 1; struct tvec_base tvec_base_deferrable; +static atomic_t deferrable_pending; void timers_update_migration(bool update_nohz) { @@ -150,9 +151,12 @@ static inline struct tvec_base *get_target_base(struct tvec_base *base, static inline void __run_deferrable_timers(void) { - if (smp_processor_id() == tick_do_timer_cpu && - time_after_eq(jiffies, tvec_base_deferrable.timer_jiffies)) - __run_timers(&tvec_base_deferrable); + if (time_after_eq(jiffies, tvec_base_deferrable.timer_jiffies)) { + if ((atomic_cmpxchg(&deferrable_pending, 1, 0) && + tick_do_timer_cpu == TICK_DO_TIMER_NONE) || + tick_do_timer_cpu == smp_processor_id()) + __run_timers(&tvec_base_deferrable); + } } static inline void init_timer_deferrable_global(void) @@ -1428,6 +1432,30 @@ static u64 cmp_next_hrtimer_event(u64 basem, u64 expires) return DIV_ROUND_UP_ULL(nextevt, TICK_NSEC) * TICK_NSEC; } +#ifdef CONFIG_SMP +/* + * check_pending_deferrable_timers - Check for unbound deferrable timer expiry. + * @cpu - Current CPU + * + * The function checks whether any global deferrable pending timers + * are exipired or not. This function does not check cpu bounded + * diferrable pending timers expiry. + * + * The function returns true when a cpu unbounded deferrable timer is expired. + */ +bool check_pending_deferrable_timers(int cpu) +{ + if (cpu == tick_do_timer_cpu || + tick_do_timer_cpu == TICK_DO_TIMER_NONE) { + if (time_after_eq(jiffies, tvec_base_deferrable.timer_jiffies) + && !atomic_cmpxchg(&deferrable_pending, 0, 1)) { + return true; + } + } + return false; +} +#endif + /** * get_next_timer_interrupt - return the time (clock mono) of the next timer * @basej: base time jiffies diff --git a/kernel/trace/msm_rtb.c b/kernel/trace/msm_rtb.c index 80058b544cb5..587082117842 100644 --- a/kernel/trace/msm_rtb.c +++ b/kernel/trace/msm_rtb.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -28,6 +28,7 @@ #include <asm-generic/sizes.h> #include <linux/msm_rtb.h> #include <asm/timex.h> +#include <soc/qcom/minidump.h> #define SENTINEL_BYTE_1 0xFF #define SENTINEL_BYTE_2 0xAA @@ -243,6 +244,7 @@ EXPORT_SYMBOL(uncached_logk); static int msm_rtb_probe(struct platform_device *pdev) { struct msm_rtb_platform_data *d = pdev->dev.platform_data; + struct md_region md_entry; #if defined(CONFIG_QCOM_RTB_SEPARATE_CPUS) unsigned int cpu; #endif @@ -294,6 +296,12 @@ static int msm_rtb_probe(struct platform_device *pdev) memset(msm_rtb.rtb, 0, msm_rtb.size); + strlcpy(md_entry.name, "KRTB_BUF", sizeof(md_entry.name)); + md_entry.virt_addr = (uintptr_t)msm_rtb.rtb; + md_entry.phys_addr = msm_rtb.phys; + md_entry.size = msm_rtb.size; + if (msm_minidump_add_region(&md_entry)) + pr_info("Failed to add RTB in Minidump\n"); #if defined(CONFIG_QCOM_RTB_SEPARATE_CPUS) for_each_possible_cpu(cpu) { diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 1de2ef8ec926..f527d8e65d69 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -123,9 +123,7 @@ static unsigned long soft_lockup_nmi_warn; #ifdef CONFIG_HARDLOCKUP_DETECTOR unsigned int __read_mostly hardlockup_panic = CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE; -#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI static unsigned long __maybe_unused hardlockup_allcpu_dumped; -#endif /* * We may not want to enable hard lockup detection by default in all cases, * for example when running the kernel as a guest on a hypervisor. In these diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug index 9e55d6b141aa..7470fd60fc59 100644 --- a/mm/Kconfig.debug +++ b/mm/Kconfig.debug @@ -40,9 +40,6 @@ config DEBUG_PAGEALLOC_ENABLE_DEFAULT Enable debug page memory allocations by default? This value can be overridden by debug_pagealloc=off|on. -config PAGE_POISONING - bool - config SLUB_DEBUG_PANIC_ON bool "Enable to Panic on SLUB corruption detection" depends on SLUB_DEBUG @@ -52,3 +49,53 @@ config SLUB_DEBUG_PANIC_ON debug options this may not be desirable as it prevents from investigating the root cause which may be rooted within cache or memory. + +config PAGE_POISONING + bool "Poison pages after freeing" + select PAGE_EXTENSION + select PAGE_POISONING_NO_SANITY if HIBERNATION + ---help--- + Fill the pages with poison patterns after free_pages() and verify + the patterns before alloc_pages. The filling of the memory helps + reduce the risk of information leaks from freed data. This does + have a potential performance impact. + + Note that "poison" here is not the same thing as the "HWPoison" + for CONFIG_MEMORY_FAILURE. This is software poisoning only. + + If unsure, say N + +config PAGE_POISONING_ENABLE_DEFAULT + bool "Enable page poisoning by default?" + default n + depends on PAGE_POISONING + ---help--- + Enable page poisoning of free pages by default? This value + can be overridden by page_poison=off|on. This can be used + to avoid passing the kernel parameter and let page poisoning + feature enabled by default. + +config PAGE_POISONING_NO_SANITY + depends on PAGE_POISONING + bool "Only poison, don't sanity check" + ---help--- + Skip the sanity checking on alloc, only fill the pages with + poison on free. This reduces some of the overhead of the + poisoning feature. + + If you are only interested in sanitization, say Y. Otherwise + say N. + +config PAGE_POISONING_ZERO + bool "Use zero for poisoning instead of random data" + depends on PAGE_POISONING + ---help--- + Instead of using the existing poison value, fill the pages with + zeros. This makes it harder to detect when errors are occurring + due to sanitization but the zeroing at free means that it is + no longer necessary to write zeros when GFP_ZERO is used on + allocation. + + Enabling page poisoning with this option will disable hibernation + + If unsure, say N diff --git a/mm/Makefile b/mm/Makefile index 4b1a69abce7a..130d06ac56e0 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -52,7 +52,7 @@ obj-$(CONFIG_SPARSEMEM_VMEMMAP) += sparse-vmemmap.o obj-$(CONFIG_SLOB) += slob.o obj-$(CONFIG_MMU_NOTIFIER) += mmu_notifier.o obj-$(CONFIG_KSM) += ksm.o -obj-$(CONFIG_PAGE_POISONING) += debug-pagealloc.o +obj-$(CONFIG_PAGE_POISONING) += page_poison.o obj-$(CONFIG_SLAB) += slab.o obj-$(CONFIG_SLUB) += slub.o obj-$(CONFIG_KMEMCHECK) += kmemcheck.o diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 28f60d9ea074..170c1486e5c9 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1012,6 +1012,7 @@ static bool free_pages_prepare(struct page *page, unsigned int order) PAGE_SIZE << order); } arch_free_page(page, order); + kernel_poison_pages(page, 1 << order, 0); kernel_map_pages(page, 1 << order, 0); kasan_free_pages(page, order); @@ -1396,6 +1397,12 @@ static inline int check_new_page(struct page *page) return 0; } +static inline bool free_pages_prezeroed(void) +{ + return IS_ENABLED(CONFIG_PAGE_POISONING_ZERO) && + page_poisoning_enabled(); +} + static int prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags, int alloc_flags) { @@ -1413,8 +1420,9 @@ static int prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags, kasan_alloc_pages(page, order); arch_alloc_page(page, order); kernel_map_pages(page, 1 << order, 1); + kernel_poison_pages(page, 1 << order, 1); - if (gfp_flags & __GFP_ZERO) + if (!free_pages_prezeroed() && (gfp_flags & __GFP_ZERO)) for (i = 0; i < (1 << order); i++) clear_highpage(page + i); diff --git a/mm/page_ext.c b/mm/page_ext.c index 292ca7b8debd..916accfec86a 100644 --- a/mm/page_ext.c +++ b/mm/page_ext.c @@ -54,9 +54,6 @@ static struct page_ext_operations *page_ext_ops[] = { &debug_guardpage_ops, -#ifdef CONFIG_PAGE_POISONING - &page_poisoning_ops, -#endif #ifdef CONFIG_PAGE_OWNER &page_owner_ops, #endif @@ -106,12 +103,15 @@ struct page_ext *lookup_page_ext(struct page *page) struct page_ext *base; base = NODE_DATA(page_to_nid(page))->node_page_ext; -#ifdef CONFIG_DEBUG_VM +#if defined(CONFIG_DEBUG_VM) || defined(CONFIG_PAGE_POISONING) /* * The sanity checks the page allocator does upon freeing a * page can reach here before the page_ext arrays are * allocated when feeding a range of pages to the allocator * for the first time during bootup or memory hotplug. + * + * This check is also necessary for ensuring page poisoning + * works as expected when enabled */ if (unlikely(!base)) return NULL; @@ -180,12 +180,15 @@ struct page_ext *lookup_page_ext(struct page *page) { unsigned long pfn = page_to_pfn(page); struct mem_section *section = __pfn_to_section(pfn); -#ifdef CONFIG_DEBUG_VM +#if defined(CONFIG_DEBUG_VM) || defined(CONFIG_PAGE_POISONING) /* * The sanity checks the page allocator does upon freeing a * page can reach here before the page_ext arrays are * allocated when feeding a range of pages to the allocator * for the first time during bootup or memory hotplug. + * + * This check is also necessary for ensuring page poisoning + * works as expected when enabled */ if (!section->page_ext) return NULL; diff --git a/mm/debug-pagealloc.c b/mm/page_poison.c index 100963091cc6..c8cf230dbfcb 100644 --- a/mm/debug-pagealloc.c +++ b/mm/page_poison.c @@ -6,68 +6,41 @@ #include <linux/poison.h> #include <linux/ratelimit.h> -#ifndef mark_addr_rdonly -#define mark_addr_rdonly(a) -#endif - -#ifndef mark_addr_rdwrite -#define mark_addr_rdwrite(a) -#endif - -static bool page_poisoning_enabled __read_mostly; - -static bool need_page_poisoning(void) -{ - if (!debug_pagealloc_enabled()) - return false; - - return true; -} - -static void init_page_poisoning(void) -{ - if (!debug_pagealloc_enabled()) - return; - - page_poisoning_enabled = true; -} - -struct page_ext_operations page_poisoning_ops = { - .need = need_page_poisoning, - .init = init_page_poisoning, -}; +static bool want_page_poisoning __read_mostly + = IS_ENABLED(CONFIG_PAGE_POISONING_ENABLE_DEFAULT); -static inline void set_page_poison(struct page *page) +static int early_page_poison_param(char *buf) { - struct page_ext *page_ext; + if (!buf) + return -EINVAL; - page_ext = lookup_page_ext(page); - __set_bit(PAGE_EXT_DEBUG_POISON, &page_ext->flags); -} - -static inline void clear_page_poison(struct page *page) -{ - struct page_ext *page_ext; + if (strcmp(buf, "on") == 0) + want_page_poisoning = true; + else if (strcmp(buf, "off") == 0) + want_page_poisoning = false; - page_ext = lookup_page_ext(page); - __clear_bit(PAGE_EXT_DEBUG_POISON, &page_ext->flags); + return 0; } +early_param("page_poison", early_page_poison_param); -static inline bool page_poison(struct page *page) +bool page_poisoning_enabled(void) { - struct page_ext *page_ext; - - page_ext = lookup_page_ext(page); - return test_bit(PAGE_EXT_DEBUG_POISON, &page_ext->flags); + /* + * Assumes that debug_pagealloc_enabled is set before + * free_all_bootmem. + * Page poisoning is debug page alloc for some arches. If + * either of those options are enabled, enable poisoning. + */ + return (want_page_poisoning || + (!IS_ENABLED(CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC) && + debug_pagealloc_enabled())); } static void poison_page(struct page *page) { void *addr = kmap_atomic(page); - set_page_poison(page); memset(addr, PAGE_POISON, PAGE_SIZE); - mark_addr_rdonly(addr); kunmap_atomic(addr); } @@ -93,6 +66,9 @@ static void check_poison_mem(struct page *page, unsigned char *start; unsigned char *end; + if (IS_ENABLED(CONFIG_PAGE_POISONING_NO_SANITY)) + return; + start = memchr_inv(mem, PAGE_POISON, bytes); if (!start) return; @@ -121,13 +97,13 @@ static void unpoison_page(struct page *page) { void *addr; - if (!page_poison(page)) - return; - addr = kmap_atomic(page); + /* + * Page poisoning when enabled poisons each and every page + * that is freed to buddy. Thus no extra check is done to + * see if a page was posioned. + */ check_poison_mem(page, addr, PAGE_SIZE); - mark_addr_rdwrite(addr); - clear_page_poison(page); kunmap_atomic(addr); } @@ -139,9 +115,9 @@ static void unpoison_pages(struct page *page, int n) unpoison_page(page + i); } -void __kernel_map_pages(struct page *page, int numpages, int enable) +void kernel_poison_pages(struct page *page, int numpages, int enable) { - if (!page_poisoning_enabled) + if (!page_poisoning_enabled()) return; if (enable) @@ -149,3 +125,10 @@ void __kernel_map_pages(struct page *page, int numpages, int enable) else poison_pages(page, numpages); } + +#ifndef CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC +void __kernel_map_pages(struct page *page, int numpages, int enable) +{ + /* This function does nothing, all work is done via poison pages */ +} +#endif diff --git a/mm/percpu.c b/mm/percpu.c index 1f376bce413c..ef6353f0adbd 100644 --- a/mm/percpu.c +++ b/mm/percpu.c @@ -1012,8 +1012,11 @@ area_found: mutex_unlock(&pcpu_alloc_mutex); } - if (chunk != pcpu_reserved_chunk) + if (chunk != pcpu_reserved_chunk) { + spin_lock_irqsave(&pcpu_lock, flags); pcpu_nr_empty_pop_pages -= occ_pages; + spin_unlock_irqrestore(&pcpu_lock, flags); + } if (pcpu_nr_empty_pop_pages < PCPU_EMPTY_POP_PAGES_LOW) pcpu_schedule_balance_work(); diff --git a/mm/vmscan.c b/mm/vmscan.c index 5e9e74955bd1..94fecacf0ddc 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -399,6 +399,35 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, return freed; } +static void shrink_slab_lmk(gfp_t gfp_mask, int nid, + struct mem_cgroup *memcg, + unsigned long nr_scanned, + unsigned long nr_eligible) +{ + struct shrinker *shrinker; + + if (nr_scanned == 0) + nr_scanned = SWAP_CLUSTER_MAX; + + if (!down_read_trylock(&shrinker_rwsem)) + goto out; + + list_for_each_entry(shrinker, &shrinker_list, list) { + struct shrink_control sc = { + .gfp_mask = gfp_mask, + }; + + if (!(shrinker->flags & SHRINKER_LMK)) + continue; + + do_shrink_slab(&sc, shrinker, nr_scanned, nr_eligible); + } + + up_read(&shrinker_rwsem); +out: + cond_resched(); +} + /** * shrink_slab - shrink slab caches * @gfp_mask: allocation context @@ -460,6 +489,9 @@ static unsigned long shrink_slab(gfp_t gfp_mask, int nid, .memcg = memcg, }; + if (shrinker->flags & SHRINKER_LMK) + continue; + if (memcg && !(shrinker->flags & SHRINKER_MEMCG_AWARE)) continue; @@ -2626,6 +2658,7 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc) gfp_t orig_mask; enum zone_type requested_highidx = gfp_zone(sc->gfp_mask); bool reclaimable = false; + unsigned long lru_pages = 0; /* * If the number of buffer_heads in the machine exceeds the maximum @@ -2653,6 +2686,7 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc) * to global LRU. */ if (global_reclaim(sc)) { + lru_pages += zone_reclaimable_pages(zone); if (!cpuset_zone_allowed(zone, GFP_KERNEL | __GFP_HARDWALL)) continue; @@ -2703,6 +2737,9 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc) reclaimable = true; } + if (global_reclaim(sc)) + shrink_slab_lmk(sc->gfp_mask, 0, NULL, + sc->nr_scanned, lru_pages); /* * Restore to original mask to avoid the impact on the caller if we * promoted it to __GFP_HIGHMEM. @@ -3181,7 +3218,8 @@ static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order, long remaining, */ static bool kswapd_shrink_zone(struct zone *zone, int classzone_idx, - struct scan_control *sc) + struct scan_control *sc, + unsigned long lru_pages) { unsigned long balance_gap; bool lowmem_pressure; @@ -3208,6 +3246,8 @@ static bool kswapd_shrink_zone(struct zone *zone, return true; shrink_zone(zone, sc, zone_idx(zone) == classzone_idx); + shrink_slab_lmk(sc->gfp_mask, zone_to_nid(zone), NULL, + sc->nr_scanned, lru_pages); clear_bit(ZONE_WRITEBACK, &zone->flags); @@ -3265,6 +3305,7 @@ static int balance_pgdat(pg_data_t *pgdat, int order, int classzone_idx) do { bool raise_priority = true; + unsigned long lru_pages = 0; sc.nr_reclaimed = 0; @@ -3322,6 +3363,15 @@ static int balance_pgdat(pg_data_t *pgdat, int order, int classzone_idx) if (sc.priority < DEF_PRIORITY - 2) sc.may_writepage = 1; + for (i = 0; i <= end_zone; i++) { + struct zone *zone = pgdat->node_zones + i; + + if (!populated_zone(zone)) + continue; + + lru_pages += zone_reclaimable_pages(zone); + } + /* * Now scan the zone in the dma->highmem direction, stopping * at the last zone which needs scanning. @@ -3358,7 +3408,7 @@ static int balance_pgdat(pg_data_t *pgdat, int order, int classzone_idx) * that that high watermark would be met at 100% * efficiency. */ - if (kswapd_shrink_zone(zone, end_zone, &sc)) + if (kswapd_shrink_zone(zone, end_zone, &sc, lru_pages)) raise_priority = false; } diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index f7fba74108a9..e24754a0e052 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -29,6 +29,7 @@ EXPORT_SYMBOL(br_should_route_hook); static int br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb) { + br_drop_fake_rtable(skb); return netif_receive_skb(skb); } diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 7ddbe7ec81d6..97fc19f001bf 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -516,21 +516,6 @@ static unsigned int br_nf_pre_routing(void *priv, } -/* PF_BRIDGE/LOCAL_IN ************************************************/ -/* The packet is locally destined, which requires a real - * dst_entry, so detach the fake one. On the way up, the - * packet would pass through PRE_ROUTING again (which already - * took place when the packet entered the bridge), but we - * register an IPv4 PRE_ROUTING 'sabotage' hook that will - * prevent this from happening. */ -static unsigned int br_nf_local_in(void *priv, - struct sk_buff *skb, - const struct nf_hook_state *state) -{ - br_drop_fake_rtable(skb); - return NF_ACCEPT; -} - /* PF_BRIDGE/FORWARD *************************************************/ static int br_nf_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { @@ -901,12 +886,6 @@ static struct nf_hook_ops br_nf_ops[] __read_mostly = { .priority = NF_BR_PRI_BRNF, }, { - .hook = br_nf_local_in, - .pf = NFPROTO_BRIDGE, - .hooknum = NF_BR_LOCAL_IN, - .priority = NF_BR_PRI_BRNF, - }, - { .hook = br_nf_forward_ip, .pf = NFPROTO_BRIDGE, .hooknum = NF_BR_FORWARD, diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index ddc3573894b0..bc95e48d5cfb 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -1265,7 +1265,6 @@ static int decode_new_up_state_weight(void **p, void *end, if ((map->osd_state[osd] & CEPH_OSD_EXISTS) && (xorstate & CEPH_OSD_EXISTS)) { pr_info("osd%d does not exist\n", osd); - map->osd_weight[osd] = CEPH_OSD_IN; ret = set_primary_affinity(map, osd, CEPH_OSD_DEFAULT_PRIMARY_AFFINITY); if (ret) diff --git a/net/core/dev.c b/net/core/dev.c index 51aed87e8eec..2587d7f30191 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1679,27 +1679,54 @@ EXPORT_SYMBOL_GPL(net_dec_ingress_queue); static struct static_key netstamp_needed __read_mostly; #ifdef HAVE_JUMP_LABEL static atomic_t netstamp_needed_deferred; +static atomic_t netstamp_wanted; static void netstamp_clear(struct work_struct *work) { int deferred = atomic_xchg(&netstamp_needed_deferred, 0); + int wanted; - while (deferred--) - static_key_slow_dec(&netstamp_needed); + wanted = atomic_add_return(deferred, &netstamp_wanted); + if (wanted > 0) + static_key_enable(&netstamp_needed); + else + static_key_disable(&netstamp_needed); } static DECLARE_WORK(netstamp_work, netstamp_clear); #endif void net_enable_timestamp(void) { +#ifdef HAVE_JUMP_LABEL + int wanted; + + while (1) { + wanted = atomic_read(&netstamp_wanted); + if (wanted <= 0) + break; + if (atomic_cmpxchg(&netstamp_wanted, wanted, wanted + 1) == wanted) + return; + } + atomic_inc(&netstamp_needed_deferred); + schedule_work(&netstamp_work); +#else static_key_slow_inc(&netstamp_needed); +#endif } EXPORT_SYMBOL(net_enable_timestamp); void net_disable_timestamp(void) { #ifdef HAVE_JUMP_LABEL - /* net_disable_timestamp() can be called from non process context */ - atomic_inc(&netstamp_needed_deferred); + int wanted; + + while (1) { + wanted = atomic_read(&netstamp_wanted); + if (wanted <= 1) + break; + if (atomic_cmpxchg(&netstamp_wanted, wanted, wanted - 1) == wanted) + return; + } + atomic_dec(&netstamp_needed_deferred); schedule_work(&netstamp_work); #else static_key_slow_dec(&netstamp_needed); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 5dd643d524d6..fedcee8263b6 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3704,13 +3704,14 @@ void skb_complete_tx_timestamp(struct sk_buff *skb, if (!skb_may_tx_timestamp(sk, false)) return; - /* take a reference to prevent skb_orphan() from freeing the socket */ - sock_hold(sk); - - *skb_hwtstamps(skb) = *hwtstamps; - __skb_complete_tx_timestamp(skb, sk, SCM_TSTAMP_SND); - - sock_put(sk); + /* Take a reference to prevent skb_orphan() from freeing the socket, + * but only if the socket refcount is not zero. + */ + if (likely(atomic_inc_not_zero(&sk->sk_refcnt))) { + *skb_hwtstamps(skb) = *hwtstamps; + __skb_complete_tx_timestamp(skb, sk, SCM_TSTAMP_SND); + sock_put(sk); + } } EXPORT_SYMBOL_GPL(skb_complete_tx_timestamp); @@ -3761,7 +3762,7 @@ void skb_complete_wifi_ack(struct sk_buff *skb, bool acked) { struct sock *sk = skb->sk; struct sock_exterr_skb *serr; - int err; + int err = 1; skb->wifi_acked_valid = 1; skb->wifi_acked = acked; @@ -3771,14 +3772,15 @@ void skb_complete_wifi_ack(struct sk_buff *skb, bool acked) serr->ee.ee_errno = ENOMSG; serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS; - /* take a reference to prevent skb_orphan() from freeing the socket */ - sock_hold(sk); - - err = sock_queue_err_skb(sk, skb); + /* Take a reference to prevent skb_orphan() from freeing the socket, + * but only if the socket refcount is not zero. + */ + if (likely(atomic_inc_not_zero(&sk->sk_refcnt))) { + err = sock_queue_err_skb(sk, skb); + sock_put(sk); + } if (err) kfree_skb(skb); - - sock_put(sk); } EXPORT_SYMBOL_GPL(skb_complete_wifi_ack); diff --git a/net/core/sock.c b/net/core/sock.c index a84a154cdf0c..4efaa3b6633d 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1463,6 +1463,11 @@ static void __sk_destruct(struct rcu_head *head) pr_debug("%s: optmem leakage (%d bytes) detected\n", __func__, atomic_read(&sk->sk_omem_alloc)); + if (sk->sk_frag.page) { + put_page(sk->sk_frag.page); + sk->sk_frag.page = NULL; + } + if (sk->sk_peer_cred) put_cred(sk->sk_peer_cred); put_pid(sk->sk_peer_pid); @@ -1564,6 +1569,12 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) is_charged = sk_filter_charge(newsk, filter); if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk, sk))) { + /* We need to make sure that we don't uncharge the new + * socket if we couldn't charge it in the first place + * as otherwise we uncharge the parent's filter. + */ + if (!is_charged) + RCU_INIT_POINTER(newsk->sk_filter, NULL); /* It is still raw copy of parent, so invalidate * destructor and make plain sk_free() */ newsk->sk_destruct = NULL; @@ -2706,11 +2717,6 @@ void sk_common_release(struct sock *sk) sk_refcnt_debug_release(sk); - if (sk->sk_frag.page) { - put_page(sk->sk_frag.page); - sk->sk_frag.page = NULL; - } - sock_put(sk); } EXPORT_SYMBOL(sk_common_release); diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c index f053198e730c..5e3a7302f774 100644 --- a/net/dccp/ccids/ccid2.c +++ b/net/dccp/ccids/ccid2.c @@ -749,6 +749,7 @@ static void ccid2_hc_tx_exit(struct sock *sk) for (i = 0; i < hc->tx_seqbufc; i++) kfree(hc->tx_seqbuf[i]); hc->tx_seqbufc = 0; + dccp_ackvec_parsed_cleanup(&hc->tx_av_chunks); } static void ccid2_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb) diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 0759f5b9180e..6467bf392e1b 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -289,7 +289,8 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info) switch (type) { case ICMP_REDIRECT: - dccp_do_redirect(skb, sk); + if (!sock_owned_by_user(sk)) + dccp_do_redirect(skb, sk); goto out; case ICMP_SOURCE_QUENCH: /* Just silently ignore these. */ diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 27c4e81efa24..8113ad58fcb4 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -122,10 +122,12 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, np = inet6_sk(sk); if (type == NDISC_REDIRECT) { - struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie); + if (!sock_owned_by_user(sk)) { + struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie); - if (dst) - dst->ops->redirect(dst, sk, skb); + if (dst) + dst->ops->redirect(dst, sk, skb); + } goto out; } diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index 1994f8af646b..68eed344b471 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -122,6 +122,7 @@ struct sock *dccp_create_openreq_child(const struct sock *sk, /* It is still raw copy of parent, so invalidate * destructor and make plain sk_free() */ newsk->sk_destruct = NULL; + bh_unlock_sock(newsk); sk_free(newsk); return NULL; } @@ -145,6 +146,13 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb, struct dccp_request_sock *dreq = dccp_rsk(req); bool own_req; + /* TCP/DCCP listeners became lockless. + * DCCP stores complex state in its request_sock, so we need + * a protection for them, now this code runs without being protected + * by the parent (listener) lock. + */ + spin_lock_bh(&dreq->dreq_lock); + /* Check for retransmitted REQUEST */ if (dccp_hdr(skb)->dccph_type == DCCP_PKT_REQUEST) { @@ -159,7 +167,7 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb, inet_rtx_syn_ack(sk, req); } /* Network Duplicate, discard packet */ - return NULL; + goto out; } DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_PACKET_ERROR; @@ -185,20 +193,20 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb, child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL, req, &own_req); - if (!child) - goto listen_overflow; - - return inet_csk_complete_hashdance(sk, child, req, own_req); + if (child) { + child = inet_csk_complete_hashdance(sk, child, req, own_req); + goto out; + } -listen_overflow: - dccp_pr_debug("listen_overflow!\n"); DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_TOO_BUSY; drop: if (dccp_hdr(skb)->dccph_type != DCCP_PKT_RESET) req->rsk_ops->send_reset(sk, skb); inet_csk_reqsk_queue_drop(sk, req); - return NULL; +out: + spin_unlock_bh(&dreq->dreq_lock); + return child; } EXPORT_SYMBOL_GPL(dccp_check_req); @@ -249,6 +257,7 @@ int dccp_reqsk_init(struct request_sock *req, { struct dccp_request_sock *dreq = dccp_rsk(req); + spin_lock_init(&dreq->dreq_lock); inet_rsk(req)->ir_rmt_port = dccp_hdr(skb)->dccph_sport; inet_rsk(req)->ir_num = ntohs(dccp_hdr(skb)->dccph_dport); inet_rsk(req)->acked = 0; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 7e30c7b50a28..a353d1d92f01 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1081,7 +1081,8 @@ static void nl_fib_input(struct sk_buff *skb) net = sock_net(skb->sk); nlh = nlmsg_hdr(skb); - if (skb->len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len || + if (skb->len < nlmsg_total_size(sizeof(*frn)) || + skb->len < nlh->nlmsg_len || nlmsg_len(nlh) < sizeof(*frn)) return; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 6ace04d14e30..9af0f461b00d 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1963,6 +1963,7 @@ int ip_route_input_noref(struct sk_buff *skb, __be32 daddr, __be32 saddr, { int res; + tos &= IPTOS_RT_MASK; rcu_read_lock(); /* Multicast recognition logic is moved from route cache to here. diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index e7e227f6760f..3f87c731477f 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5437,6 +5437,7 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb) struct inet_connection_sock *icsk = inet_csk(sk); tcp_set_state(sk, TCP_ESTABLISHED); + icsk->icsk_ack.lrcvtime = tcp_time_stamp; if (skb) { icsk->icsk_af_ops->sk_rx_dst_set(sk, skb); @@ -5649,7 +5650,6 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, * to stand against the temptation 8) --ANK */ inet_csk_schedule_ack(sk); - icsk->icsk_ack.lrcvtime = tcp_time_stamp; tcp_enter_quickack_mode(sk); inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, TCP_DELACK_MAX, TCP_RTO_MAX); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 51a77e20f6c6..1660613ddae4 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -271,10 +271,13 @@ EXPORT_SYMBOL(tcp_v4_connect); */ void tcp_v4_mtu_reduced(struct sock *sk) { - struct dst_entry *dst; struct inet_sock *inet = inet_sk(sk); - u32 mtu = tcp_sk(sk)->mtu_info; + struct dst_entry *dst; + u32 mtu; + if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) + return; + mtu = tcp_sk(sk)->mtu_info; dst = inet_csk_update_pmtu(sk, mtu); if (!dst) return; @@ -420,7 +423,8 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) switch (type) { case ICMP_REDIRECT: - do_redirect(icmp_skb, sk); + if (!sock_owned_by_user(sk)) + do_redirect(icmp_skb, sk); goto out; case ICMP_SOURCE_QUENCH: /* Just silently ignore these. */ diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 9475a2748a9a..019db68bdb9f 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -472,6 +472,7 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, newtp->mdev_us = jiffies_to_usecs(TCP_TIMEOUT_INIT); newtp->rtt_min[0].rtt = ~0U; newicsk->icsk_rto = TCP_TIMEOUT_INIT; + newicsk->icsk_ack.lrcvtime = tcp_time_stamp; newtp->packets_out = 0; newtp->retrans_out = 0; diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index ce20968de667..4aef80d30fab 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -257,7 +257,8 @@ void tcp_delack_timer_handler(struct sock *sk) sk_mem_reclaim_partial(sk); - if (sk->sk_state == TCP_CLOSE || !(icsk->icsk_ack.pending & ICSK_ACK_TIMER)) + if (((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN)) || + !(icsk->icsk_ack.pending & ICSK_ACK_TIMER)) goto out; if (time_after(icsk->icsk_ack.timeout, jiffies)) { @@ -538,7 +539,8 @@ void tcp_write_timer_handler(struct sock *sk) struct inet_connection_sock *icsk = inet_csk(sk); int event; - if (sk->sk_state == TCP_CLOSE || !icsk->icsk_pending) + if (((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN)) || + !icsk->icsk_pending) goto out; if (time_after(icsk->icsk_timeout, jiffies)) { diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 01455f492e17..3d72aeffa3f1 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -112,6 +112,27 @@ static inline u32 cstamp_delta(unsigned long cstamp) return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; } +static inline s32 rfc3315_s14_backoff_init(s32 irt) +{ + /* multiply 'initial retransmission time' by 0.9 .. 1.1 */ + u64 tmp = (900000 + prandom_u32() % 200001) * (u64)irt; + do_div(tmp, 1000000); + return (s32)tmp; +} + +static inline s32 rfc3315_s14_backoff_update(s32 rt, s32 mrt) +{ + /* multiply 'retransmission timeout' by 1.9 .. 2.1 */ + u64 tmp = (1900000 + prandom_u32() % 200001) * (u64)rt; + do_div(tmp, 1000000); + if ((s32)tmp > mrt) { + /* multiply 'maximum retransmission time' by 0.9 .. 1.1 */ + tmp = (900000 + prandom_u32() % 200001) * (u64)mrt; + do_div(tmp, 1000000); + } + return (s32)tmp; +} + #ifdef CONFIG_SYSCTL static int addrconf_sysctl_register(struct inet6_dev *idev); static void addrconf_sysctl_unregister(struct inet6_dev *idev); @@ -187,6 +208,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .dad_transmits = 1, .rtr_solicits = MAX_RTR_SOLICITATIONS, .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL, + .rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL, .rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY, .use_tempaddr = 0, .temp_valid_lft = TEMP_VALID_LIFETIME, @@ -202,6 +224,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .accept_ra_rtr_pref = 1, .rtr_probe_interval = 60 * HZ, #ifdef CONFIG_IPV6_ROUTE_INFO + .accept_ra_rt_info_min_plen = 0, .accept_ra_rt_info_max_plen = 0, #endif #endif @@ -233,6 +256,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .dad_transmits = 1, .rtr_solicits = MAX_RTR_SOLICITATIONS, .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL, + .rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL, .rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY, .use_tempaddr = 0, .temp_valid_lft = TEMP_VALID_LIFETIME, @@ -248,6 +272,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .accept_ra_rtr_pref = 1, .rtr_probe_interval = 60 * HZ, #ifdef CONFIG_IPV6_ROUTE_INFO + .accept_ra_rt_info_min_plen = 0, .accept_ra_rt_info_max_plen = 0, #endif #endif @@ -3502,7 +3527,7 @@ static void addrconf_rs_timer(unsigned long data) if (idev->if_flags & IF_RA_RCVD) goto out; - if (idev->rs_probes++ < idev->cnf.rtr_solicits) { + if (idev->rs_probes++ < idev->cnf.rtr_solicits || idev->cnf.rtr_solicits < 0) { write_unlock(&idev->lock); if (!ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE)) ndisc_send_rs(dev, &lladdr, @@ -3511,11 +3536,13 @@ static void addrconf_rs_timer(unsigned long data) goto put; write_lock(&idev->lock); + idev->rs_interval = rfc3315_s14_backoff_update( + idev->rs_interval, idev->cnf.rtr_solicit_max_interval); /* The wait after the last probe can be shorter */ addrconf_mod_rs_timer(idev, (idev->rs_probes == idev->cnf.rtr_solicits) ? idev->cnf.rtr_solicit_delay : - idev->cnf.rtr_solicit_interval); + idev->rs_interval); } else { /* * Note: we do not support deprecated "all on-link" @@ -3743,7 +3770,7 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp) send_mld = ifp->scope == IFA_LINK && ipv6_lonely_lladdr(ifp); send_rs = send_mld && ipv6_accept_ra(ifp->idev) && - ifp->idev->cnf.rtr_solicits > 0 && + ifp->idev->cnf.rtr_solicits != 0 && (dev->flags&IFF_LOOPBACK) == 0; read_unlock_bh(&ifp->idev->lock); @@ -3765,10 +3792,11 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp) write_lock_bh(&ifp->idev->lock); spin_lock(&ifp->lock); + ifp->idev->rs_interval = rfc3315_s14_backoff_init( + ifp->idev->cnf.rtr_solicit_interval); ifp->idev->rs_probes = 1; ifp->idev->if_flags |= IF_RS_SENT; - addrconf_mod_rs_timer(ifp->idev, - ifp->idev->cnf.rtr_solicit_interval); + addrconf_mod_rs_timer(ifp->idev, ifp->idev->rs_interval); spin_unlock(&ifp->lock); write_unlock_bh(&ifp->idev->lock); } @@ -4685,6 +4713,8 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_RTR_SOLICITS] = cnf->rtr_solicits; array[DEVCONF_RTR_SOLICIT_INTERVAL] = jiffies_to_msecs(cnf->rtr_solicit_interval); + array[DEVCONF_RTR_SOLICIT_MAX_INTERVAL] = + jiffies_to_msecs(cnf->rtr_solicit_max_interval); array[DEVCONF_RTR_SOLICIT_DELAY] = jiffies_to_msecs(cnf->rtr_solicit_delay); array[DEVCONF_FORCE_MLD_VERSION] = cnf->force_mld_version; @@ -4706,6 +4736,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_RTR_PROBE_INTERVAL] = jiffies_to_msecs(cnf->rtr_probe_interval); #ifdef CONFIG_IPV6_ROUTE_INFO + array[DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN] = cnf->accept_ra_rt_info_min_plen; array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen; #endif #endif @@ -4893,7 +4924,7 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token) return -EINVAL; if (!ipv6_accept_ra(idev)) return -EINVAL; - if (idev->cnf.rtr_solicits <= 0) + if (idev->cnf.rtr_solicits == 0) return -EINVAL; write_lock_bh(&idev->lock); @@ -4918,8 +4949,10 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token) if (update_rs) { idev->if_flags |= IF_RS_SENT; + idev->rs_interval = rfc3315_s14_backoff_init( + idev->cnf.rtr_solicit_interval); idev->rs_probes = 1; - addrconf_mod_rs_timer(idev, idev->cnf.rtr_solicit_interval); + addrconf_mod_rs_timer(idev, idev->rs_interval); } /* Well, that's kinda nasty ... */ @@ -5557,6 +5590,13 @@ static struct addrconf_sysctl_table .proc_handler = proc_dointvec_jiffies, }, { + .procname = "router_solicitation_max_interval", + .data = &ipv6_devconf.rtr_solicit_max_interval, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_jiffies, + }, + { .procname = "router_solicitation_delay", .data = &ipv6_devconf.rtr_solicit_delay, .maxlen = sizeof(int), @@ -5666,6 +5706,13 @@ static struct addrconf_sysctl_table }, #ifdef CONFIG_IPV6_ROUTE_INFO { + .procname = "accept_ra_rt_info_min_plen", + .data = &ipv6_devconf.accept_ra_rt_info_min_plen, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { .procname = "accept_ra_rt_info_max_plen", .data = &ipv6_devconf.accept_ra_rt_info_max_plen, .maxlen = sizeof(int), diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 34cf46d74554..85bf86458706 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -903,6 +903,8 @@ add: ins = &rt->dst.rt6_next; iter = *ins; while (iter) { + if (iter->rt6i_metric > rt->rt6i_metric) + break; if (rt6_qualify_for_ecmp(iter)) { *ins = iter->dst.rt6_next; fib6_purge_rt(iter, fn, info->nl_net); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index cd96a01032a2..cf90a9bf26a3 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -742,13 +742,14 @@ slow_path: * Fragment the datagram. */ - *prevhdr = NEXTHDR_FRAGMENT; troom = rt->dst.dev->needed_tailroom; /* * Keep copying data until we run out. */ while (left > 0) { + u8 *fragnexthdr_offset; + len = left; /* IF: it doesn't fit, use 'mtu' - the data space left */ if (len > mtu) @@ -793,6 +794,10 @@ slow_path: */ skb_copy_from_linear_data(skb, skb_network_header(frag), hlen); + fragnexthdr_offset = skb_network_header(frag); + fragnexthdr_offset += prevhdr - skb_network_header(skb); + *fragnexthdr_offset = NEXTHDR_FRAGMENT; + /* * Build fragment header. */ diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index 24fb9c0efd00..5b7433887eda 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -681,6 +681,10 @@ vti6_parm_to_user(struct ip6_tnl_parm2 *u, const struct __ip6_tnl_parm *p) u->link = p->link; u->i_key = p->i_key; u->o_key = p->o_key; + if (u->i_key) + u->i_flags |= GRE_KEY; + if (u->o_key) + u->o_flags |= GRE_KEY; u->proto = p->proto; memcpy(u->name, p->name, sizeof(u->name)); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 84afb9a77278..3452f9037ad4 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1358,6 +1358,8 @@ skip_linkparms: if (ri->prefix_len == 0 && !in6_dev->cnf.accept_ra_defrtr) continue; + if (ri->prefix_len < in6_dev->cnf.accept_ra_rt_info_min_plen) + continue; if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen) continue; rt6_route_rcv(skb->dev, (u8 *)p, (p->nd_opt_len) << 3, diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index e10a04c9cdc7..cf336d670f8b 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -135,6 +135,18 @@ config IP6_NF_IPTABLES if IP6_NF_IPTABLES +config IP6_NF_IPTABLES_128 + tristate "128 bit arithmetic for iptables matching" + depends on IP6_NF_IPTABLES + help + This enables 128 bit matching in ip6tables to help optimize cases + where there is no match required. ip6tables matching for ipv6 always + has a mask if an address is specified for match. Adding a check for + mask prior to that helps to improve performance as it avoids the + masked comparison. + + Note that this feature depends on the architecture. If unsure, say N. + # The simple matches. config IP6_NF_MATCH_AH tristate '"ah" match support' diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 22f39e00bef3..6fd784643d6e 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -94,22 +94,26 @@ ip6_packet_match(const struct sk_buff *skb, { unsigned long ret; const struct ipv6hdr *ipv6 = ipv6_hdr(skb); +#if IS_ENABLED(IP6_NF_IPTABLES_128) + const __uint128_t *ulm1 = (const __uint128_t *)&ip6info->smsk; + const __uint128_t *ulm2 = (const __uint128_t *)&ip6info->dmsk; +#endif #define FWINV(bool, invflg) ((bool) ^ !!(ip6info->invflags & (invflg))) - if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk, - &ip6info->src), IP6T_INV_SRCIP) || - FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk, - &ip6info->dst), IP6T_INV_DSTIP)) { - dprintf("Source or dest mismatch.\n"); -/* - dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr, - ipinfo->smsk.s_addr, ipinfo->src.s_addr, - ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : ""); - dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr, - ipinfo->dmsk.s_addr, ipinfo->dst.s_addr, - ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/ - return false; +#if IS_ENABLED(IP6_NF_IPTABLES_128) + if (*ulm1 || *ulm2) +#endif + { + if (FWINV(ipv6_masked_addr_cmp + (&ipv6->saddr, &ip6info->smsk, &ip6info->src), + IP6T_INV_SRCIP) || + FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk, + &ip6info->dst), + IP6T_INV_DSTIP)) { + dprintf("Source or dest mismatch.\n"); + return false; + } } ret = ifname_compare_aligned(indev, ip6info->iniface, ip6info->iniface_mask); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 108b39967694..8532768b4eaa 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -377,10 +377,12 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, np = inet6_sk(sk); if (type == NDISC_REDIRECT) { - struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie); + if (!sock_owned_by_user(sk)) { + struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie); - if (dst) - dst->ops->redirect(dst, sk, skb); + if (dst) + dst->ops->redirect(dst, sk, skb); + } goto out; } diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 445b7cd0826a..48ab93842322 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -383,7 +383,7 @@ static int l2tp_ip_backlog_recv(struct sock *sk, struct sk_buff *skb) drop: IP_INC_STATS(sock_net(sk), IPSTATS_MIB_INDISCARDS); kfree_skb(skb); - return -1; + return 0; } /* Userspace will call sendmsg() on the tunnel socket to send L2TP diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 881bc2072809..52cfc4478511 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -1567,6 +1567,7 @@ static void mpls_net_exit(struct net *net) for (index = 0; index < platform_labels; index++) { struct mpls_route *rt = rtnl_dereference(platform_label[index]); RCU_INIT_POINTER(platform_label[index], NULL); + mpls_notify_route(net, index, rt, NULL, NULL); mpls_rt_free(rt); } rtnl_unlock(); diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index ececa65868ef..e0d8e9ad315b 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1802,8 +1802,11 @@ ret_res: } #ifdef DDEBUG -/* This function is not in xt_qtaguid_print.c because of locks visibility */ -static void prdebug_full_state(int indent_level, const char *fmt, ...) +/* + * This function is not in xt_qtaguid_print.c because of locks visibility. + * The lock of sock_tag_list must be aquired before calling this function + */ +static void prdebug_full_state_locked(int indent_level, const char *fmt, ...) { va_list args; char *fmt_buff; @@ -1824,16 +1827,12 @@ static void prdebug_full_state(int indent_level, const char *fmt, ...) kfree(buff); va_end(args); - spin_lock_bh(&sock_tag_list_lock); prdebug_sock_tag_tree(indent_level, &sock_tag_tree); - spin_unlock_bh(&sock_tag_list_lock); - spin_lock_bh(&sock_tag_list_lock); spin_lock_bh(&uid_tag_data_tree_lock); prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree); prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree); spin_unlock_bh(&uid_tag_data_tree_lock); - spin_unlock_bh(&sock_tag_list_lock); spin_lock_bh(&iface_stat_list_lock); prdebug_iface_stat_list(indent_level, &iface_stat_list); @@ -1842,7 +1841,7 @@ static void prdebug_full_state(int indent_level, const char *fmt, ...) pr_debug("qtaguid: %s(): }\n", __func__); } #else -static void prdebug_full_state(int indent_level, const char *fmt, ...) {} +static void prdebug_full_state_locked(int indent_level, const char *fmt, ...) {} #endif struct proc_ctrl_print_info { @@ -1965,8 +1964,11 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) (u64)atomic64_read(&qtu_events.match_no_sk), (u64)atomic64_read(&qtu_events.match_no_sk_file)); - /* Count the following as part of the last item_index */ - prdebug_full_state(0, "proc ctrl"); + /* Count the following as part of the last item_index. No need + * to lock the sock_tag_list here since it is already locked when + * starting the seq_file operation + */ + prdebug_full_state_locked(0, "proc ctrl"); } return 0; @@ -2875,8 +2877,10 @@ static int qtudev_release(struct inode *inode, struct file *file) sock_tag_tree_erase(&st_to_free_tree); - prdebug_full_state(0, "%s(): pid=%u tgid=%u", __func__, + spin_lock_bh(&sock_tag_list_lock); + prdebug_full_state_locked(0, "%s(): pid=%u tgid=%u", __func__, current->pid, current->tgid); + spin_unlock_bh(&sock_tag_list_lock); return 0; } diff --git a/net/netlink/Kconfig b/net/netlink/Kconfig index 2c5e95e9bfbd..5d6e8c05b3d4 100644 --- a/net/netlink/Kconfig +++ b/net/netlink/Kconfig @@ -2,15 +2,6 @@ # Netlink Sockets # -config NETLINK_MMAP - bool "NETLINK: mmaped IO" - ---help--- - This option enables support for memory mapped netlink IO. This - reduces overhead by avoiding copying data between kernel- and - userspace. - - If unsure, say N. - config NETLINK_DIAG tristate "NETLINK: socket monitoring interface" default n diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index f4dd706c6cd1..96fe1c103bf9 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -225,7 +225,7 @@ static int __netlink_deliver_tap_skb(struct sk_buff *skb, dev_hold(dev); - if (netlink_skb_is_mmaped(skb) || is_vmalloc_addr(skb->head)) + if (is_vmalloc_addr(skb->head)) nskb = netlink_to_full_skb(skb, GFP_ATOMIC); else nskb = skb_clone(skb, GFP_ATOMIC); @@ -300,610 +300,8 @@ static void netlink_rcv_wake(struct sock *sk) wake_up_interruptible(&nlk->wait); } -#ifdef CONFIG_NETLINK_MMAP -static bool netlink_rx_is_mmaped(struct sock *sk) -{ - return nlk_sk(sk)->rx_ring.pg_vec != NULL; -} - -static bool netlink_tx_is_mmaped(struct sock *sk) -{ - return nlk_sk(sk)->tx_ring.pg_vec != NULL; -} - -static __pure struct page *pgvec_to_page(const void *addr) -{ - if (is_vmalloc_addr(addr)) - return vmalloc_to_page(addr); - else - return virt_to_page(addr); -} - -static void free_pg_vec(void **pg_vec, unsigned int order, unsigned int len) -{ - unsigned int i; - - for (i = 0; i < len; i++) { - if (pg_vec[i] != NULL) { - if (is_vmalloc_addr(pg_vec[i])) - vfree(pg_vec[i]); - else - free_pages((unsigned long)pg_vec[i], order); - } - } - kfree(pg_vec); -} - -static void *alloc_one_pg_vec_page(unsigned long order) -{ - void *buffer; - gfp_t gfp_flags = GFP_KERNEL | __GFP_COMP | __GFP_ZERO | - __GFP_NOWARN | __GFP_NORETRY; - - buffer = (void *)__get_free_pages(gfp_flags, order); - if (buffer != NULL) - return buffer; - - buffer = vzalloc((1 << order) * PAGE_SIZE); - if (buffer != NULL) - return buffer; - - gfp_flags &= ~__GFP_NORETRY; - return (void *)__get_free_pages(gfp_flags, order); -} - -static void **alloc_pg_vec(struct netlink_sock *nlk, - struct nl_mmap_req *req, unsigned int order) -{ - unsigned int block_nr = req->nm_block_nr; - unsigned int i; - void **pg_vec; - - pg_vec = kcalloc(block_nr, sizeof(void *), GFP_KERNEL); - if (pg_vec == NULL) - return NULL; - - for (i = 0; i < block_nr; i++) { - pg_vec[i] = alloc_one_pg_vec_page(order); - if (pg_vec[i] == NULL) - goto err1; - } - - return pg_vec; -err1: - free_pg_vec(pg_vec, order, block_nr); - return NULL; -} - - -static void -__netlink_set_ring(struct sock *sk, struct nl_mmap_req *req, bool tx_ring, void **pg_vec, - unsigned int order) -{ - struct netlink_sock *nlk = nlk_sk(sk); - struct sk_buff_head *queue; - struct netlink_ring *ring; - - queue = tx_ring ? &sk->sk_write_queue : &sk->sk_receive_queue; - ring = tx_ring ? &nlk->tx_ring : &nlk->rx_ring; - - spin_lock_bh(&queue->lock); - - ring->frame_max = req->nm_frame_nr - 1; - ring->head = 0; - ring->frame_size = req->nm_frame_size; - ring->pg_vec_pages = req->nm_block_size / PAGE_SIZE; - - swap(ring->pg_vec_len, req->nm_block_nr); - swap(ring->pg_vec_order, order); - swap(ring->pg_vec, pg_vec); - - __skb_queue_purge(queue); - spin_unlock_bh(&queue->lock); - - WARN_ON(atomic_read(&nlk->mapped)); - - if (pg_vec) - free_pg_vec(pg_vec, order, req->nm_block_nr); -} - -static int netlink_set_ring(struct sock *sk, struct nl_mmap_req *req, - bool tx_ring) -{ - struct netlink_sock *nlk = nlk_sk(sk); - struct netlink_ring *ring; - void **pg_vec = NULL; - unsigned int order = 0; - - ring = tx_ring ? &nlk->tx_ring : &nlk->rx_ring; - - if (atomic_read(&nlk->mapped)) - return -EBUSY; - if (atomic_read(&ring->pending)) - return -EBUSY; - - if (req->nm_block_nr) { - if (ring->pg_vec != NULL) - return -EBUSY; - - if ((int)req->nm_block_size <= 0) - return -EINVAL; - if (!PAGE_ALIGNED(req->nm_block_size)) - return -EINVAL; - if (req->nm_frame_size < NL_MMAP_HDRLEN) - return -EINVAL; - if (!IS_ALIGNED(req->nm_frame_size, NL_MMAP_MSG_ALIGNMENT)) - return -EINVAL; - - ring->frames_per_block = req->nm_block_size / - req->nm_frame_size; - if (ring->frames_per_block == 0) - return -EINVAL; - if (ring->frames_per_block * req->nm_block_nr != - req->nm_frame_nr) - return -EINVAL; - - order = get_order(req->nm_block_size); - pg_vec = alloc_pg_vec(nlk, req, order); - if (pg_vec == NULL) - return -ENOMEM; - } else { - if (req->nm_frame_nr) - return -EINVAL; - } - - mutex_lock(&nlk->pg_vec_lock); - if (atomic_read(&nlk->mapped) == 0) { - __netlink_set_ring(sk, req, tx_ring, pg_vec, order); - mutex_unlock(&nlk->pg_vec_lock); - return 0; - } - - mutex_unlock(&nlk->pg_vec_lock); - - if (pg_vec) - free_pg_vec(pg_vec, order, req->nm_block_nr); - - return -EBUSY; -} - -static void netlink_mm_open(struct vm_area_struct *vma) -{ - struct file *file = vma->vm_file; - struct socket *sock = file->private_data; - struct sock *sk = sock->sk; - - if (sk) - atomic_inc(&nlk_sk(sk)->mapped); -} - -static void netlink_mm_close(struct vm_area_struct *vma) -{ - struct file *file = vma->vm_file; - struct socket *sock = file->private_data; - struct sock *sk = sock->sk; - - if (sk) - atomic_dec(&nlk_sk(sk)->mapped); -} - -static const struct vm_operations_struct netlink_mmap_ops = { - .open = netlink_mm_open, - .close = netlink_mm_close, -}; - -static int netlink_mmap(struct file *file, struct socket *sock, - struct vm_area_struct *vma) -{ - struct sock *sk = sock->sk; - struct netlink_sock *nlk = nlk_sk(sk); - struct netlink_ring *ring; - unsigned long start, size, expected; - unsigned int i; - int err = -EINVAL; - - if (vma->vm_pgoff) - return -EINVAL; - - mutex_lock(&nlk->pg_vec_lock); - - expected = 0; - for (ring = &nlk->rx_ring; ring <= &nlk->tx_ring; ring++) { - if (ring->pg_vec == NULL) - continue; - expected += ring->pg_vec_len * ring->pg_vec_pages * PAGE_SIZE; - } - - if (expected == 0) - goto out; - - size = vma->vm_end - vma->vm_start; - if (size != expected) - goto out; - - start = vma->vm_start; - for (ring = &nlk->rx_ring; ring <= &nlk->tx_ring; ring++) { - if (ring->pg_vec == NULL) - continue; - - for (i = 0; i < ring->pg_vec_len; i++) { - struct page *page; - void *kaddr = ring->pg_vec[i]; - unsigned int pg_num; - - for (pg_num = 0; pg_num < ring->pg_vec_pages; pg_num++) { - page = pgvec_to_page(kaddr); - err = vm_insert_page(vma, start, page); - if (err < 0) - goto out; - start += PAGE_SIZE; - kaddr += PAGE_SIZE; - } - } - } - - atomic_inc(&nlk->mapped); - vma->vm_ops = &netlink_mmap_ops; - err = 0; -out: - mutex_unlock(&nlk->pg_vec_lock); - return err; -} - -static void netlink_frame_flush_dcache(const struct nl_mmap_hdr *hdr, unsigned int nm_len) -{ -#if ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE == 1 - struct page *p_start, *p_end; - - /* First page is flushed through netlink_{get,set}_status */ - p_start = pgvec_to_page(hdr + PAGE_SIZE); - p_end = pgvec_to_page((void *)hdr + NL_MMAP_HDRLEN + nm_len - 1); - while (p_start <= p_end) { - flush_dcache_page(p_start); - p_start++; - } -#endif -} - -static enum nl_mmap_status netlink_get_status(const struct nl_mmap_hdr *hdr) -{ - smp_rmb(); - flush_dcache_page(pgvec_to_page(hdr)); - return hdr->nm_status; -} - -static void netlink_set_status(struct nl_mmap_hdr *hdr, - enum nl_mmap_status status) -{ - smp_mb(); - hdr->nm_status = status; - flush_dcache_page(pgvec_to_page(hdr)); -} - -static struct nl_mmap_hdr * -__netlink_lookup_frame(const struct netlink_ring *ring, unsigned int pos) -{ - unsigned int pg_vec_pos, frame_off; - - pg_vec_pos = pos / ring->frames_per_block; - frame_off = pos % ring->frames_per_block; - - return ring->pg_vec[pg_vec_pos] + (frame_off * ring->frame_size); -} - -static struct nl_mmap_hdr * -netlink_lookup_frame(const struct netlink_ring *ring, unsigned int pos, - enum nl_mmap_status status) -{ - struct nl_mmap_hdr *hdr; - - hdr = __netlink_lookup_frame(ring, pos); - if (netlink_get_status(hdr) != status) - return NULL; - - return hdr; -} - -static struct nl_mmap_hdr * -netlink_current_frame(const struct netlink_ring *ring, - enum nl_mmap_status status) -{ - return netlink_lookup_frame(ring, ring->head, status); -} - -static void netlink_increment_head(struct netlink_ring *ring) -{ - ring->head = ring->head != ring->frame_max ? ring->head + 1 : 0; -} - -static void netlink_forward_ring(struct netlink_ring *ring) -{ - unsigned int head = ring->head; - const struct nl_mmap_hdr *hdr; - - do { - hdr = __netlink_lookup_frame(ring, ring->head); - if (hdr->nm_status == NL_MMAP_STATUS_UNUSED) - break; - if (hdr->nm_status != NL_MMAP_STATUS_SKIP) - break; - netlink_increment_head(ring); - } while (ring->head != head); -} - -static bool netlink_has_valid_frame(struct netlink_ring *ring) -{ - unsigned int head = ring->head, pos = head; - const struct nl_mmap_hdr *hdr; - - do { - hdr = __netlink_lookup_frame(ring, pos); - if (hdr->nm_status == NL_MMAP_STATUS_VALID) - return true; - pos = pos != 0 ? pos - 1 : ring->frame_max; - } while (pos != head); - - return false; -} - -static bool netlink_dump_space(struct netlink_sock *nlk) -{ - struct netlink_ring *ring = &nlk->rx_ring; - struct nl_mmap_hdr *hdr; - unsigned int n; - - hdr = netlink_current_frame(ring, NL_MMAP_STATUS_UNUSED); - if (hdr == NULL) - return false; - - n = ring->head + ring->frame_max / 2; - if (n > ring->frame_max) - n -= ring->frame_max; - - hdr = __netlink_lookup_frame(ring, n); - - return hdr->nm_status == NL_MMAP_STATUS_UNUSED; -} - -static unsigned int netlink_poll(struct file *file, struct socket *sock, - poll_table *wait) -{ - struct sock *sk = sock->sk; - struct netlink_sock *nlk = nlk_sk(sk); - unsigned int mask; - int err; - - if (nlk->rx_ring.pg_vec != NULL) { - /* Memory mapped sockets don't call recvmsg(), so flow control - * for dumps is performed here. A dump is allowed to continue - * if at least half the ring is unused. - */ - while (nlk->cb_running && netlink_dump_space(nlk)) { - err = netlink_dump(sk); - if (err < 0) { - sk->sk_err = -err; - sk->sk_error_report(sk); - break; - } - } - netlink_rcv_wake(sk); - } - - mask = datagram_poll(file, sock, wait); - - /* We could already have received frames in the normal receive - * queue, that will show up as NL_MMAP_STATUS_COPY in the ring, - * so if mask contains pollin/etc already, there's no point - * walking the ring. - */ - if ((mask & (POLLIN | POLLRDNORM)) != (POLLIN | POLLRDNORM)) { - spin_lock_bh(&sk->sk_receive_queue.lock); - if (nlk->rx_ring.pg_vec) { - if (netlink_has_valid_frame(&nlk->rx_ring)) - mask |= POLLIN | POLLRDNORM; - } - spin_unlock_bh(&sk->sk_receive_queue.lock); - } - - spin_lock_bh(&sk->sk_write_queue.lock); - if (nlk->tx_ring.pg_vec) { - if (netlink_current_frame(&nlk->tx_ring, NL_MMAP_STATUS_UNUSED)) - mask |= POLLOUT | POLLWRNORM; - } - spin_unlock_bh(&sk->sk_write_queue.lock); - - return mask; -} - -static struct nl_mmap_hdr *netlink_mmap_hdr(struct sk_buff *skb) -{ - return (struct nl_mmap_hdr *)(skb->head - NL_MMAP_HDRLEN); -} - -static void netlink_ring_setup_skb(struct sk_buff *skb, struct sock *sk, - struct netlink_ring *ring, - struct nl_mmap_hdr *hdr) -{ - unsigned int size; - void *data; - - size = ring->frame_size - NL_MMAP_HDRLEN; - data = (void *)hdr + NL_MMAP_HDRLEN; - - skb->head = data; - skb->data = data; - skb_reset_tail_pointer(skb); - skb->end = skb->tail + size; - skb->len = 0; - - skb->destructor = netlink_skb_destructor; - NETLINK_CB(skb).flags |= NETLINK_SKB_MMAPED; - NETLINK_CB(skb).sk = sk; -} - -static int netlink_mmap_sendmsg(struct sock *sk, struct msghdr *msg, - u32 dst_portid, u32 dst_group, - struct scm_cookie *scm) -{ - struct netlink_sock *nlk = nlk_sk(sk); - struct netlink_ring *ring; - struct nl_mmap_hdr *hdr; - struct sk_buff *skb; - unsigned int maxlen; - int err = 0, len = 0; - - mutex_lock(&nlk->pg_vec_lock); - - ring = &nlk->tx_ring; - maxlen = ring->frame_size - NL_MMAP_HDRLEN; - - do { - unsigned int nm_len; - - hdr = netlink_current_frame(ring, NL_MMAP_STATUS_VALID); - if (hdr == NULL) { - if (!(msg->msg_flags & MSG_DONTWAIT) && - atomic_read(&nlk->tx_ring.pending)) - schedule(); - continue; - } - - nm_len = ACCESS_ONCE(hdr->nm_len); - if (nm_len > maxlen) { - err = -EINVAL; - goto out; - } - - netlink_frame_flush_dcache(hdr, nm_len); - - skb = alloc_skb(nm_len, GFP_KERNEL); - if (skb == NULL) { - err = -ENOBUFS; - goto out; - } - __skb_put(skb, nm_len); - memcpy(skb->data, (void *)hdr + NL_MMAP_HDRLEN, nm_len); - netlink_set_status(hdr, NL_MMAP_STATUS_UNUSED); - - netlink_increment_head(ring); - - NETLINK_CB(skb).portid = nlk->portid; - NETLINK_CB(skb).dst_group = dst_group; - NETLINK_CB(skb).creds = scm->creds; - - err = security_netlink_send(sk, skb); - if (err) { - kfree_skb(skb); - goto out; - } - - if (unlikely(dst_group)) { - atomic_inc(&skb->users); - netlink_broadcast(sk, skb, dst_portid, dst_group, - GFP_KERNEL); - } - err = netlink_unicast(sk, skb, dst_portid, - msg->msg_flags & MSG_DONTWAIT); - if (err < 0) - goto out; - len += err; - - } while (hdr != NULL || - (!(msg->msg_flags & MSG_DONTWAIT) && - atomic_read(&nlk->tx_ring.pending))); - - if (len > 0) - err = len; -out: - mutex_unlock(&nlk->pg_vec_lock); - return err; -} - -static void netlink_queue_mmaped_skb(struct sock *sk, struct sk_buff *skb) -{ - struct nl_mmap_hdr *hdr; - - hdr = netlink_mmap_hdr(skb); - hdr->nm_len = skb->len; - hdr->nm_group = NETLINK_CB(skb).dst_group; - hdr->nm_pid = NETLINK_CB(skb).creds.pid; - hdr->nm_uid = from_kuid(sk_user_ns(sk), NETLINK_CB(skb).creds.uid); - hdr->nm_gid = from_kgid(sk_user_ns(sk), NETLINK_CB(skb).creds.gid); - netlink_frame_flush_dcache(hdr, hdr->nm_len); - netlink_set_status(hdr, NL_MMAP_STATUS_VALID); - - NETLINK_CB(skb).flags |= NETLINK_SKB_DELIVERED; - kfree_skb(skb); -} - -static void netlink_ring_set_copied(struct sock *sk, struct sk_buff *skb) -{ - struct netlink_sock *nlk = nlk_sk(sk); - struct netlink_ring *ring = &nlk->rx_ring; - struct nl_mmap_hdr *hdr; - - spin_lock_bh(&sk->sk_receive_queue.lock); - hdr = netlink_current_frame(ring, NL_MMAP_STATUS_UNUSED); - if (hdr == NULL) { - spin_unlock_bh(&sk->sk_receive_queue.lock); - kfree_skb(skb); - netlink_overrun(sk); - return; - } - netlink_increment_head(ring); - __skb_queue_tail(&sk->sk_receive_queue, skb); - spin_unlock_bh(&sk->sk_receive_queue.lock); - - hdr->nm_len = skb->len; - hdr->nm_group = NETLINK_CB(skb).dst_group; - hdr->nm_pid = NETLINK_CB(skb).creds.pid; - hdr->nm_uid = from_kuid(sk_user_ns(sk), NETLINK_CB(skb).creds.uid); - hdr->nm_gid = from_kgid(sk_user_ns(sk), NETLINK_CB(skb).creds.gid); - netlink_set_status(hdr, NL_MMAP_STATUS_COPY); -} - -#else /* CONFIG_NETLINK_MMAP */ -#define netlink_rx_is_mmaped(sk) false -#define netlink_tx_is_mmaped(sk) false -#define netlink_mmap sock_no_mmap -#define netlink_poll datagram_poll -#define netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group, scm) 0 -#endif /* CONFIG_NETLINK_MMAP */ - static void netlink_skb_destructor(struct sk_buff *skb) { -#ifdef CONFIG_NETLINK_MMAP - struct nl_mmap_hdr *hdr; - struct netlink_ring *ring; - struct sock *sk; - - /* If a packet from the kernel to userspace was freed because of an - * error without being delivered to userspace, the kernel must reset - * the status. In the direction userspace to kernel, the status is - * always reset here after the packet was processed and freed. - */ - if (netlink_skb_is_mmaped(skb)) { - hdr = netlink_mmap_hdr(skb); - sk = NETLINK_CB(skb).sk; - - if (NETLINK_CB(skb).flags & NETLINK_SKB_TX) { - netlink_set_status(hdr, NL_MMAP_STATUS_UNUSED); - ring = &nlk_sk(sk)->tx_ring; - } else { - if (!(NETLINK_CB(skb).flags & NETLINK_SKB_DELIVERED)) { - hdr->nm_len = 0; - netlink_set_status(hdr, NL_MMAP_STATUS_VALID); - } - ring = &nlk_sk(sk)->rx_ring; - } - - WARN_ON(atomic_read(&ring->pending) == 0); - atomic_dec(&ring->pending); - sock_put(sk); - - skb->head = NULL; - } -#endif if (is_vmalloc_addr(skb->head)) { if (!skb->cloned || !atomic_dec_return(&(skb_shinfo(skb)->dataref))) @@ -927,18 +325,6 @@ static void netlink_skb_set_owner_r(struct sk_buff *skb, struct sock *sk) static void netlink_sock_destruct(struct sock *sk) { skb_queue_purge(&sk->sk_receive_queue); -#ifdef CONFIG_NETLINK_MMAP - if (1) { - struct nl_mmap_req req; - - memset(&req, 0, sizeof(req)); - if (nlk->rx_ring.pg_vec) - __netlink_set_ring(sk, &req, false, NULL, 0); - memset(&req, 0, sizeof(req)); - if (nlk->tx_ring.pg_vec) - __netlink_set_ring(sk, &req, true, NULL, 0); - } -#endif /* CONFIG_NETLINK_MMAP */ if (!sock_flag(sk, SOCK_DEAD)) { printk(KERN_ERR "Freeing alive netlink socket %p\n", sk); @@ -1185,9 +571,6 @@ static int __netlink_create(struct net *net, struct socket *sock, mutex_init(nlk->cb_mutex); } init_waitqueue_head(&nlk->wait); -#ifdef CONFIG_NETLINK_MMAP - mutex_init(&nlk->pg_vec_lock); -#endif sock_set_flag(sk, SOCK_RCU_FREE); sk->sk_destruct = netlink_sock_destruct; @@ -1725,8 +1108,7 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb, nlk = nlk_sk(sk); if ((atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf || - test_bit(NETLINK_S_CONGESTED, &nlk->state)) && - !netlink_skb_is_mmaped(skb)) { + test_bit(NETLINK_S_CONGESTED, &nlk->state))) { DECLARE_WAITQUEUE(wait, current); if (!*timeo) { if (!ssk || netlink_is_kernel(ssk)) @@ -1764,14 +1146,7 @@ static int __netlink_sendskb(struct sock *sk, struct sk_buff *skb) netlink_deliver_tap(skb); -#ifdef CONFIG_NETLINK_MMAP - if (netlink_skb_is_mmaped(skb)) - netlink_queue_mmaped_skb(sk, skb); - else if (netlink_rx_is_mmaped(sk)) - netlink_ring_set_copied(sk, skb); - else -#endif /* CONFIG_NETLINK_MMAP */ - skb_queue_tail(&sk->sk_receive_queue, skb); + skb_queue_tail(&sk->sk_receive_queue, skb); sk->sk_data_ready(sk); return len; } @@ -1795,9 +1170,6 @@ static struct sk_buff *netlink_trim(struct sk_buff *skb, gfp_t allocation) int delta; WARN_ON(skb->sk != NULL); - if (netlink_skb_is_mmaped(skb)) - return skb; - delta = skb->end - skb->tail; if (is_vmalloc_addr(skb->head) || delta * 2 < skb->truesize) return skb; @@ -1877,71 +1249,6 @@ struct sk_buff *__netlink_alloc_skb(struct sock *ssk, unsigned int size, unsigned int ldiff, u32 dst_portid, gfp_t gfp_mask) { -#ifdef CONFIG_NETLINK_MMAP - unsigned int maxlen, linear_size; - struct sock *sk = NULL; - struct sk_buff *skb; - struct netlink_ring *ring; - struct nl_mmap_hdr *hdr; - - sk = netlink_getsockbyportid(ssk, dst_portid); - if (IS_ERR(sk)) - goto out; - - ring = &nlk_sk(sk)->rx_ring; - /* fast-path without atomic ops for common case: non-mmaped receiver */ - if (ring->pg_vec == NULL) - goto out_put; - - /* We need to account the full linear size needed as a ring - * slot cannot have non-linear parts. - */ - linear_size = size + ldiff; - if (ring->frame_size - NL_MMAP_HDRLEN < linear_size) - goto out_put; - - skb = alloc_skb_head(gfp_mask); - if (skb == NULL) - goto err1; - - spin_lock_bh(&sk->sk_receive_queue.lock); - /* check again under lock */ - if (ring->pg_vec == NULL) - goto out_free; - - /* check again under lock */ - maxlen = ring->frame_size - NL_MMAP_HDRLEN; - if (maxlen < linear_size) - goto out_free; - - netlink_forward_ring(ring); - hdr = netlink_current_frame(ring, NL_MMAP_STATUS_UNUSED); - if (hdr == NULL) - goto err2; - - netlink_ring_setup_skb(skb, sk, ring, hdr); - netlink_set_status(hdr, NL_MMAP_STATUS_RESERVED); - atomic_inc(&ring->pending); - netlink_increment_head(ring); - - spin_unlock_bh(&sk->sk_receive_queue.lock); - return skb; - -err2: - kfree_skb(skb); - spin_unlock_bh(&sk->sk_receive_queue.lock); - netlink_overrun(sk); -err1: - sock_put(sk); - return NULL; - -out_free: - kfree_skb(skb); - spin_unlock_bh(&sk->sk_receive_queue.lock); -out_put: - sock_put(sk); -out: -#endif return alloc_skb(size, gfp_mask); } EXPORT_SYMBOL_GPL(__netlink_alloc_skb); @@ -2222,8 +1529,7 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, if (level != SOL_NETLINK) return -ENOPROTOOPT; - if (optname != NETLINK_RX_RING && optname != NETLINK_TX_RING && - optlen >= sizeof(int) && + if (optlen >= sizeof(int) && get_user(val, (unsigned int __user *)optval)) return -EFAULT; @@ -2276,25 +1582,6 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, } err = 0; break; -#ifdef CONFIG_NETLINK_MMAP - case NETLINK_RX_RING: - case NETLINK_TX_RING: { - struct nl_mmap_req req; - - /* Rings might consume more memory than queue limits, require - * CAP_NET_ADMIN. - */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (optlen < sizeof(req)) - return -EINVAL; - if (copy_from_user(&req, optval, sizeof(req))) - return -EFAULT; - err = netlink_set_ring(sk, &req, - optname == NETLINK_TX_RING); - break; - } -#endif /* CONFIG_NETLINK_MMAP */ case NETLINK_LISTEN_ALL_NSID: if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_BROADCAST)) return -EPERM; @@ -2464,18 +1751,6 @@ static int netlink_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) smp_rmb(); } - /* It's a really convoluted way for userland to ask for mmaped - * sendmsg(), but that's what we've got... - */ - if (netlink_tx_is_mmaped(sk) && - iter_is_iovec(&msg->msg_iter) && - msg->msg_iter.nr_segs == 1 && - msg->msg_iter.iov->iov_base == NULL) { - err = netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group, - &scm); - goto out; - } - err = -EMSGSIZE; if (len > sk->sk_sndbuf - 32) goto out; @@ -2792,8 +2067,7 @@ static int netlink_dump(struct sock *sk) goto errout_skb; } - if (!netlink_rx_is_mmaped(sk) && - atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf) + if (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf) goto errout_skb; /* NLMSG_GOODSIZE is small to avoid high order allocations being @@ -2882,16 +2156,7 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb, struct netlink_sock *nlk; int ret; - /* Memory mapped dump requests need to be copied to avoid looping - * on the pending state in netlink_mmap_sendmsg() while the CB hold - * a reference to the skb. - */ - if (netlink_skb_is_mmaped(skb)) { - skb = skb_copy(skb, GFP_KERNEL); - if (skb == NULL) - return -ENOBUFS; - } else - atomic_inc(&skb->users); + atomic_inc(&skb->users); sk = netlink_lookup(sock_net(ssk), ssk->sk_protocol, NETLINK_CB(skb).portid); if (sk == NULL) { @@ -3235,7 +2500,7 @@ static const struct proto_ops netlink_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = netlink_getname, - .poll = netlink_poll, + .poll = datagram_poll, .ioctl = sock_no_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, @@ -3243,7 +2508,7 @@ static const struct proto_ops netlink_ops = { .getsockopt = netlink_getsockopt, .sendmsg = netlink_sendmsg, .recvmsg = netlink_recvmsg, - .mmap = netlink_mmap, + .mmap = sock_no_mmap, .sendpage = sock_no_sendpage, }; diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h index b0c1ddc97260..15e62973cfc6 100644 --- a/net/netlink/af_netlink.h +++ b/net/netlink/af_netlink.h @@ -45,12 +45,6 @@ struct netlink_sock { int (*netlink_bind)(struct net *net, int group); void (*netlink_unbind)(struct net *net, int group); struct module *module; -#ifdef CONFIG_NETLINK_MMAP - struct mutex pg_vec_lock; - struct netlink_ring rx_ring; - struct netlink_ring tx_ring; - atomic_t mapped; -#endif /* CONFIG_NETLINK_MMAP */ struct rhash_head node; struct work_struct work; @@ -61,15 +55,6 @@ static inline struct netlink_sock *nlk_sk(struct sock *sk) return container_of(sk, struct netlink_sock, sk); } -static inline bool netlink_skb_is_mmaped(const struct sk_buff *skb) -{ -#ifdef CONFIG_NETLINK_MMAP - return NETLINK_CB(skb).flags & NETLINK_SKB_MMAPED; -#else - return false; -#endif /* CONFIG_NETLINK_MMAP */ -} - struct netlink_table { struct rhashtable hash; struct hlist_head mc_list; diff --git a/net/netlink/diag.c b/net/netlink/diag.c index 3ee63a3cff30..8dd836a8dd60 100644 --- a/net/netlink/diag.c +++ b/net/netlink/diag.c @@ -8,41 +8,6 @@ #include "af_netlink.h" -#ifdef CONFIG_NETLINK_MMAP -static int sk_diag_put_ring(struct netlink_ring *ring, int nl_type, - struct sk_buff *nlskb) -{ - struct netlink_diag_ring ndr; - - ndr.ndr_block_size = ring->pg_vec_pages << PAGE_SHIFT; - ndr.ndr_block_nr = ring->pg_vec_len; - ndr.ndr_frame_size = ring->frame_size; - ndr.ndr_frame_nr = ring->frame_max + 1; - - return nla_put(nlskb, nl_type, sizeof(ndr), &ndr); -} - -static int sk_diag_put_rings_cfg(struct sock *sk, struct sk_buff *nlskb) -{ - struct netlink_sock *nlk = nlk_sk(sk); - int ret; - - mutex_lock(&nlk->pg_vec_lock); - ret = sk_diag_put_ring(&nlk->rx_ring, NETLINK_DIAG_RX_RING, nlskb); - if (!ret) - ret = sk_diag_put_ring(&nlk->tx_ring, NETLINK_DIAG_TX_RING, - nlskb); - mutex_unlock(&nlk->pg_vec_lock); - - return ret; -} -#else -static int sk_diag_put_rings_cfg(struct sock *sk, struct sk_buff *nlskb) -{ - return 0; -} -#endif - static int sk_diag_dump_groups(struct sock *sk, struct sk_buff *nlskb) { struct netlink_sock *nlk = nlk_sk(sk); @@ -87,10 +52,6 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, sock_diag_put_meminfo(sk, skb, NETLINK_DIAG_MEMINFO)) goto out_nlmsg_trim; - if ((req->ndiag_show & NDIAG_SHOW_RING_CFG) && - sk_diag_put_rings_cfg(sk, skb)) - goto out_nlmsg_trim; - nlmsg_end(skb, nlh); return 0; diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index d1bd4a45ca2d..d26b28def310 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -588,7 +588,7 @@ static int ip_tun_from_nlattr(const struct nlattr *attr, ipv4 = true; break; case OVS_TUNNEL_KEY_ATTR_IPV6_SRC: - SW_FLOW_KEY_PUT(match, tun_key.u.ipv6.dst, + SW_FLOW_KEY_PUT(match, tun_key.u.ipv6.src, nla_get_in6_addr(a), is_mask); ipv6 = true; break; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index d805cd577a60..3975ac809934 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -3021,7 +3021,7 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; - char name[15]; + char name[sizeof(uaddr->sa_data) + 1]; /* * Check legality @@ -3029,7 +3029,11 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, if (addr_len != sizeof(struct sockaddr)) return -EINVAL; - strlcpy(name, uaddr->sa_data, sizeof(name)); + /* uaddr->sa_data comes from the userspace, it's not guaranteed to be + * zero-terminated. + */ + memcpy(name, uaddr->sa_data, sizeof(uaddr->sa_data)); + name[sizeof(uaddr->sa_data)] = 0; return packet_do_bind(sk, name, 0, pkt_sk(sk)->num); } diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 06e7c4a37245..694a06f1e0d5 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -820,10 +820,8 @@ static int tca_action_flush(struct net *net, struct nlattr *nla, goto out_module_put; err = a.ops->walk(skb, &dcb, RTM_DELACTION, &a); - if (err < 0) + if (err <= 0) goto out_module_put; - if (err == 0) - goto noflush_out; nla_nest_end(skb, nest); @@ -840,7 +838,6 @@ static int tca_action_flush(struct net *net, struct nlattr *nla, out_module_put: module_put(a.ops->owner); err_out: -noflush_out: kfree_skb(skb); return err; } diff --git a/net/sched/act_connmark.c b/net/sched/act_connmark.c index bb41699c6c49..7ecb14f3db54 100644 --- a/net/sched/act_connmark.c +++ b/net/sched/act_connmark.c @@ -109,6 +109,9 @@ static int tcf_connmark_init(struct net *net, struct nlattr *nla, if (ret < 0) return ret; + if (!tb[TCA_CONNMARK_PARMS]) + return -EINVAL; + parm = nla_data(tb[TCA_CONNMARK_PARMS]); if (!tcf_hash_check(parm->index, a, bind)) { diff --git a/net/unix/garbage.c b/net/unix/garbage.c index 6a0d48525fcf..c36757e72844 100644 --- a/net/unix/garbage.c +++ b/net/unix/garbage.c @@ -146,6 +146,7 @@ void unix_notinflight(struct user_struct *user, struct file *fp) if (s) { struct unix_sock *u = unix_sk(s); + BUG_ON(!atomic_long_read(&u->inflight)); BUG_ON(list_empty(&u->link)); if (atomic_long_dec_and_test(&u->inflight)) @@ -341,6 +342,14 @@ void unix_gc(void) } list_del(&cursor); + /* Now gc_candidates contains only garbage. Restore original + * inflight counters for these as well, and remove the skbuffs + * which are creating the cycle(s). + */ + skb_queue_head_init(&hitlist); + list_for_each_entry(u, &gc_candidates, link) + scan_children(&u->sk, inc_inflight, &hitlist); + /* not_cycle_list contains those sockets which do not make up a * cycle. Restore these to the inflight list. */ @@ -350,14 +359,6 @@ void unix_gc(void) list_move_tail(&u->link, &gc_inflight_list); } - /* Now gc_candidates contains only garbage. Restore original - * inflight counters for these as well, and remove the skbuffs - * which are creating the cycle(s). - */ - skb_queue_head_init(&hitlist); - list_for_each_entry(u, &gc_candidates, link) - scan_children(&u->sk, inc_inflight, &hitlist); - spin_unlock(&unix_gc_lock); /* Here we are. Hitlist is filled. Die. */ diff --git a/net/wireless/db.txt b/net/wireless/db.txt index 449e4a31a3ec..a71f8801edb3 100644 --- a/net/wireless/db.txt +++ b/net/wireless/db.txt @@ -54,7 +54,8 @@ country AN: DFS-ETSI country AR: (2402 - 2482 @ 40), (36) - (5170 - 5330 @ 160), (23) + (5170 - 5250 @ 80), (23), AUTO-BW + (5250 - 5330 @ 80), (36), AUTO-BW (5490 - 5590 @ 80), (36) (5650 - 5730 @ 80), (36) (5735 - 5835 @ 80), (36) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 129ea1c2e010..d0a03abbb1c9 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -512,21 +512,17 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb, { int err; - rtnl_lock(); - if (!cb->args[0]) { err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, nl80211_fam.attrbuf, nl80211_fam.maxattr, nl80211_policy); if (err) - goto out_unlock; + return err; *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), nl80211_fam.attrbuf); - if (IS_ERR(*wdev)) { - err = PTR_ERR(*wdev); - goto out_unlock; - } + if (IS_ERR(*wdev)) + return PTR_ERR(*wdev); *rdev = wiphy_to_rdev((*wdev)->wiphy); /* 0 is the first index - add 1 to parse only once */ cb->args[0] = (*rdev)->wiphy_idx + 1; @@ -536,10 +532,8 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb, struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1); struct wireless_dev *tmp; - if (!wiphy) { - err = -ENODEV; - goto out_unlock; - } + if (!wiphy) + return -ENODEV; *rdev = wiphy_to_rdev(wiphy); *wdev = NULL; @@ -550,21 +544,11 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb, } } - if (!*wdev) { - err = -ENODEV; - goto out_unlock; - } + if (!*wdev) + return -ENODEV; } return 0; - out_unlock: - rtnl_unlock(); - return err; -} - -static void nl80211_finish_wdev_dump(struct cfg80211_registered_device *rdev) -{ - rtnl_unlock(); } /* IE validation */ @@ -4281,9 +4265,10 @@ static int nl80211_dump_station(struct sk_buff *skb, int sta_idx = cb->args[2]; int err; + rtnl_lock(); err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); if (err) - return err; + goto out_err; if (!wdev->netdev) { err = -EINVAL; @@ -4319,7 +4304,7 @@ static int nl80211_dump_station(struct sk_buff *skb, cb->args[2] = sta_idx; err = skb->len; out_err: - nl80211_finish_wdev_dump(rdev); + rtnl_unlock(); return err; } @@ -5036,9 +5021,10 @@ static int nl80211_dump_mpath(struct sk_buff *skb, int path_idx = cb->args[2]; int err; + rtnl_lock(); err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); if (err) - return err; + goto out_err; if (!rdev->ops->dump_mpath) { err = -EOPNOTSUPP; @@ -5072,7 +5058,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb, cb->args[2] = path_idx; err = skb->len; out_err: - nl80211_finish_wdev_dump(rdev); + rtnl_unlock(); return err; } @@ -5232,9 +5218,10 @@ static int nl80211_dump_mpp(struct sk_buff *skb, int path_idx = cb->args[2]; int err; + rtnl_lock(); err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); if (err) - return err; + goto out_err; if (!rdev->ops->dump_mpp) { err = -EOPNOTSUPP; @@ -5267,7 +5254,7 @@ static int nl80211_dump_mpp(struct sk_buff *skb, cb->args[2] = path_idx; err = skb->len; out_err: - nl80211_finish_wdev_dump(rdev); + rtnl_unlock(); return err; } @@ -7336,9 +7323,12 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb) int start = cb->args[2], idx = 0; int err; + rtnl_lock(); err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); - if (err) + if (err) { + rtnl_unlock(); return err; + } wdev_lock(wdev); spin_lock_bh(&rdev->bss_lock); @@ -7361,7 +7351,7 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb) wdev_unlock(wdev); cb->args[2] = idx; - nl80211_finish_wdev_dump(rdev); + rtnl_unlock(); return skb->len; } @@ -7445,9 +7435,10 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb) int res; bool radio_stats; + rtnl_lock(); res = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); if (res) - return res; + goto out_err; /* prepare_wdev_dump parsed the attributes */ radio_stats = nl80211_fam.attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS]; @@ -7488,7 +7479,7 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb) cb->args[2] = survey_idx; res = skb->len; out_err: - nl80211_finish_wdev_dump(rdev); + rtnl_unlock(); return res; } @@ -10573,17 +10564,13 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb, void *data = NULL; unsigned int data_len = 0; - rtnl_lock(); - if (cb->args[0]) { /* subtract the 1 again here */ struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1); struct wireless_dev *tmp; - if (!wiphy) { - err = -ENODEV; - goto out_unlock; - } + if (!wiphy) + return -ENODEV; *rdev = wiphy_to_rdev(wiphy); *wdev = NULL; @@ -10604,13 +10591,11 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb, nl80211_fam.attrbuf, nl80211_fam.maxattr, nl80211_policy); if (err) - goto out_unlock; + return err; if (!nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID] || - !nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]) { - err = -EINVAL; - goto out_unlock; - } + !nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]) + return -EINVAL; *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), nl80211_fam.attrbuf); @@ -10619,10 +10604,8 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb, *rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), nl80211_fam.attrbuf); - if (IS_ERR(*rdev)) { - err = PTR_ERR(*rdev); - goto out_unlock; - } + if (IS_ERR(*rdev)) + return PTR_ERR(*rdev); vid = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_ID]); subcmd = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_SUBCMD]); @@ -10635,19 +10618,15 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb, if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd) continue; - if (!vcmd->dumpit) { - err = -EOPNOTSUPP; - goto out_unlock; - } + if (!vcmd->dumpit) + return -EOPNOTSUPP; vcmd_idx = i; break; } - if (vcmd_idx < 0) { - err = -EOPNOTSUPP; - goto out_unlock; - } + if (vcmd_idx < 0) + return -EOPNOTSUPP; if (nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]) { data = nla_data(nl80211_fam.attrbuf[NL80211_ATTR_VENDOR_DATA]); @@ -10664,9 +10643,6 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb, /* keep rtnl locked in successful case */ return 0; - out_unlock: - rtnl_unlock(); - return err; } static int nl80211_vendor_cmd_dump(struct sk_buff *skb, @@ -10681,9 +10657,10 @@ static int nl80211_vendor_cmd_dump(struct sk_buff *skb, int err; struct nlattr *vendor_data; + rtnl_lock(); err = nl80211_prepare_vendor_dump(skb, cb, &rdev, &wdev); if (err) - return err; + goto out; vcmd_idx = cb->args[2]; data = (void *)cb->args[3]; @@ -10692,18 +10669,26 @@ static int nl80211_vendor_cmd_dump(struct sk_buff *skb, if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV)) { - if (!wdev) - return -EINVAL; + if (!wdev) { + err = -EINVAL; + goto out; + } if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV && - !wdev->netdev) - return -EINVAL; + !wdev->netdev) { + err = -EINVAL; + goto out; + } if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) { if (wdev->netdev && - !netif_running(wdev->netdev)) - return -ENETDOWN; - if (!wdev->netdev && !wdev->p2p_started) - return -ENETDOWN; + !netif_running(wdev->netdev)) { + err = -ENETDOWN; + goto out; + } + if (!wdev->netdev && !wdev->p2p_started) { + err = -ENETDOWN; + goto out; + } } } diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 5b4906ad1b81..a6451bf9a717 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -69,7 +69,7 @@ module_param(bss_entries_limit, int, 0644); MODULE_PARM_DESC(bss_entries_limit, "limit to number of scan BSS entries (per wiphy, default 1000)"); -#define IEEE80211_SCAN_RESULT_EXPIRE (7 * HZ) +#define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) static void bss_free(struct cfg80211_internal_bss *bss) { diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index cf0193b74ae3..d5b4ac7bf0d8 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -3027,6 +3027,11 @@ static int __net_init xfrm_net_init(struct net *net) { int rv; + /* Initialize the per-net locks here */ + spin_lock_init(&net->xfrm.xfrm_state_lock); + rwlock_init(&net->xfrm.xfrm_policy_lock); + mutex_init(&net->xfrm.xfrm_cfg_mutex); + rv = xfrm_statistics_init(net); if (rv < 0) goto out_statistics; @@ -3043,11 +3048,6 @@ static int __net_init xfrm_net_init(struct net *net) if (rv < 0) goto out; - /* Initialize the per-net locks here */ - spin_lock_init(&net->xfrm.xfrm_state_lock); - rwlock_init(&net->xfrm.xfrm_policy_lock); - mutex_init(&net->xfrm.xfrm_cfg_mutex); - return 0; out: diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 805681a7d356..7a5a64e70b4d 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -412,7 +412,14 @@ static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_es up = nla_data(rp); ulen = xfrm_replay_state_esn_len(up); - if (nla_len(rp) < ulen || xfrm_replay_state_esn_len(replay_esn) != ulen) + /* Check the overall length and the internal bitmap length to avoid + * potential overflow. */ + if (nla_len(rp) < ulen || + xfrm_replay_state_esn_len(replay_esn) != ulen || + replay_esn->bmp_len != up->bmp_len) + return -EINVAL; + + if (up->replay_window > up->bmp_len * sizeof(__u32) * 8) return -EINVAL; return 0; diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 58e79e02f217..c67f9c212dd1 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -1921,6 +1921,7 @@ static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client, info.output_pool != client->pool->size)) { if (snd_seq_write_pool_allocated(client)) { /* remove all existing cells */ + snd_seq_pool_mark_closing(client->pool); snd_seq_queue_client_leave_cells(client->number); snd_seq_pool_done(client->pool); } diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c index 86240d02b530..3f4efcb85df5 100644 --- a/sound/core/seq/seq_fifo.c +++ b/sound/core/seq/seq_fifo.c @@ -70,6 +70,9 @@ void snd_seq_fifo_delete(struct snd_seq_fifo **fifo) return; *fifo = NULL; + if (f->pool) + snd_seq_pool_mark_closing(f->pool); + snd_seq_fifo_clear(f); /* wake up clients if any */ diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index dfa5156f3585..5847c4475bf3 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -414,6 +414,18 @@ int snd_seq_pool_init(struct snd_seq_pool *pool) return 0; } +/* refuse the further insertion to the pool */ +void snd_seq_pool_mark_closing(struct snd_seq_pool *pool) +{ + unsigned long flags; + + if (snd_BUG_ON(!pool)) + return; + spin_lock_irqsave(&pool->lock, flags); + pool->closing = 1; + spin_unlock_irqrestore(&pool->lock, flags); +} + /* remove events */ int snd_seq_pool_done(struct snd_seq_pool *pool) { @@ -424,10 +436,6 @@ int snd_seq_pool_done(struct snd_seq_pool *pool) return -EINVAL; /* wait for closing all threads */ - spin_lock_irqsave(&pool->lock, flags); - pool->closing = 1; - spin_unlock_irqrestore(&pool->lock, flags); - if (waitqueue_active(&pool->output_sleep)) wake_up(&pool->output_sleep); @@ -484,6 +492,7 @@ int snd_seq_pool_delete(struct snd_seq_pool **ppool) *ppool = NULL; if (pool == NULL) return 0; + snd_seq_pool_mark_closing(pool); snd_seq_pool_done(pool); kfree(pool); return 0; diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h index 4a2ec779b8a7..32f959c17786 100644 --- a/sound/core/seq/seq_memory.h +++ b/sound/core/seq/seq_memory.h @@ -84,6 +84,7 @@ static inline int snd_seq_total_cells(struct snd_seq_pool *pool) int snd_seq_pool_init(struct snd_seq_pool *pool); /* done pool - free events */ +void snd_seq_pool_mark_closing(struct snd_seq_pool *pool); int snd_seq_pool_done(struct snd_seq_pool *pool); /* create pool */ diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index ab4cdab5cfa5..79edd88d5cd0 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1905,7 +1905,7 @@ static int hw_card_start(struct hw *hw) return err; /* Set DMA transfer mask */ - if (dma_set_mask(&pci->dev, DMA_BIT_MASK(dma_bits))) { + if (!dma_set_mask(&pci->dev, DMA_BIT_MASK(dma_bits))) { dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(dma_bits)); } else { dma_set_mask(&pci->dev, DMA_BIT_MASK(32)); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cf0785ddbd14..1d4f34379f56 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6040,6 +6040,8 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { ALC295_STANDARD_PINS, {0x17, 0x21014040}, {0x18, 0x21a19050}), + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC295_STANDARD_PINS), SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, ALC298_STANDARD_PINS, {0x17, 0x90170110}), diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/sound/soc/codecs/wcd-dsp-mgr.c index 71a2052f1089..9b1c8c98946c 100644 --- a/sound/soc/codecs/wcd-dsp-mgr.c +++ b/sound/soc/codecs/wcd-dsp-mgr.c @@ -14,6 +14,7 @@ #include <linux/slab.h> #include <linux/stringify.h> #include <linux/of.h> +#include <linux/debugfs.h> #include <linux/component.h> #include <linux/dma-mapping.h> #include <soc/qcom/ramdump.h> @@ -181,6 +182,10 @@ struct wdsp_mgr_priv { struct work_struct ssr_work; u16 ready_status; struct completion ready_compl; + + /* Debugfs related */ + struct dentry *entry; + bool panic_on_error; }; static char *wdsp_get_ssr_type_string(enum wdsp_ssr_type type) @@ -655,6 +660,12 @@ static void wdsp_collect_ramdumps(struct wdsp_mgr_priv *wdsp) goto err_read_dumps; } + /* + * If panic_on_error flag is explicitly set through the debugfs, + * then cause a BUG here to aid debugging. + */ + BUG_ON(wdsp->panic_on_error); + rd_seg.address = (unsigned long) wdsp->dump_data.rd_v_addr; rd_seg.size = img_section.size; rd_seg.v_address = wdsp->dump_data.rd_v_addr; @@ -948,6 +959,22 @@ static int wdsp_mgr_compare_of(struct device *dev, void *data) !strcmp(dev_name(dev), cmpnt->cdev_name))); } +static void wdsp_mgr_debugfs_init(struct wdsp_mgr_priv *wdsp) +{ + wdsp->entry = debugfs_create_dir("wdsp_mgr", NULL); + if (IS_ERR_OR_NULL(wdsp->entry)) + return; + + debugfs_create_bool("panic_on_error", S_IRUGO | S_IWUSR, + wdsp->entry, &wdsp->panic_on_error); +} + +static void wdsp_mgr_debugfs_remove(struct wdsp_mgr_priv *wdsp) +{ + debugfs_remove_recursive(wdsp->entry); + wdsp->entry = NULL; +} + static int wdsp_mgr_bind(struct device *dev) { struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev); @@ -977,6 +1004,8 @@ static int wdsp_mgr_bind(struct device *dev) } } + wdsp_mgr_debugfs_init(wdsp); + /* Schedule the work to download image if binding was successful. */ if (!ret) schedule_work(&wdsp->load_fw_work); @@ -992,6 +1021,8 @@ static void wdsp_mgr_unbind(struct device *dev) component_unbind_all(dev, wdsp->ops); + wdsp_mgr_debugfs_remove(wdsp); + if (wdsp->dump_data.rd_dev) { destroy_ramdump_device(wdsp->dump_data.rd_dev); wdsp->dump_data.rd_dev = NULL; diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 7f9ad8ebcd3d..44141ae5668e 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -2217,7 +2217,7 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug); - init_completion(&mbhc->btn_press_compl); + reinit_completion(&mbhc->btn_press_compl); WCD_MBHC_RSC_UNLOCK(mbhc); pr_debug("%s: leave\n", __func__); @@ -2854,6 +2854,7 @@ int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, } mutex_init(&mbhc->hphl_pa_lock); mutex_init(&mbhc->hphr_pa_lock); + init_completion(&mbhc->btn_press_compl); /* Register event notifier */ mbhc->nblock.notifier_call = wcd_event_notify; diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c index 3d2fe2c6bed9..fde1d3e815d3 100644 --- a/sound/soc/codecs/wcd-spi.c +++ b/sound/soc/codecs/wcd-spi.c @@ -553,7 +553,7 @@ static int wcd_spi_clk_enable(struct spi_device *spi) { struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); int ret; - u32 rd_status; + u32 rd_status = 0; ret = wcd_spi_cmd_nop(spi); if (IS_ERR_VALUE(ret)) { diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index d533984558f0..7ad3aeaa8fb7 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -192,7 +192,7 @@ module_param(sido_buck_svs_voltage, int, MODULE_PARM_DESC(sido_buck_svs_voltage, "setting for SVS voltage for SIDO BUCK"); -#define TASHA_TX_UNMUTE_DELAY_MS 25 +#define TASHA_TX_UNMUTE_DELAY_MS 40 static int tx_unmute_delay = TASHA_TX_UNMUTE_DELAY_MS; module_param(tx_unmute_delay, int, @@ -4945,7 +4945,7 @@ static int tasha_codec_enable_spline_src(struct snd_soc_codec *codec, int src_num, int event) { - u16 src_paired_reg; + u16 src_paired_reg = 0; struct tasha_priv *tasha; u16 rx_path_cfg_reg = WCD9335_CDC_RX1_RX_PATH_CFG0; u16 rx_path_ctl_reg = WCD9335_CDC_RX1_RX_PATH_CTL; diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c index e125ed8c2a16..12a4d348d9a9 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -507,7 +507,7 @@ static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); -#define WCD934X_TX_UNMUTE_DELAY_MS 25 +#define WCD934X_TX_UNMUTE_DELAY_MS 40 static int tx_unmute_delay = WCD934X_TX_UNMUTE_DELAY_MS; module_param(tx_unmute_delay, int, @@ -5476,7 +5476,7 @@ static int tavil_mad_input_put(struct snd_kcontrol *kcontrol, u32 adc, i, mic_bias_found = 0; int ret = 0; char *mad_input; - bool is_adc2_input = false; + bool is_adc_input = false; tavil_mad_input = ucontrol->value.integer.value[0]; @@ -5524,8 +5524,7 @@ static int tavil_mad_input_put(struct snd_kcontrol *kcontrol, snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc); mad_input_widget = mad_amic_input_widget; - if (adc == 2) - is_adc2_input = true; + is_adc_input = true; } else { /* DMIC type input widget*/ mad_input_widget = tavil_conn_mad_text[tavil_mad_input]; @@ -5533,7 +5532,7 @@ static int tavil_mad_input_put(struct snd_kcontrol *kcontrol, dev_dbg(codec->dev, "%s: tavil input widget = %s, adc_input = %s\n", __func__, - mad_input_widget, is_adc2_input ? "true" : "false"); + mad_input_widget, is_adc_input ? "true" : "false"); for (i = 0; i < card->num_of_dapm_routes; i++) { if (!strcmp(card->of_dapm_routes[i].sink, mad_input_widget)) { @@ -5578,8 +5577,8 @@ static int tavil_mad_input_put(struct snd_kcontrol *kcontrol, 0x0F, tavil_mad_input); snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP, 0x07, mic_bias_found); - /* for adc2 input, mad should be in micbias mode with BG enabled */ - if (is_adc2_input) + /* for all adc inputs, mad should be in micbias mode with BG enabled */ + if (is_adc_input) snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP, 0x88, 0x88); else @@ -8581,6 +8580,7 @@ static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = { {WCD934X_TLMM_DMIC3_CLK_PINCFG, 0xFF, 0x0a}, {WCD934X_TLMM_DMIC3_DATA_PINCFG, 0xFF, 0x0a}, {WCD934X_CPE_SS_SVA_CFG, 0x60, 0x00}, + {WCD934X_CPE_SS_CPAR_CFG, 0x10, 0x10}, }; static void tavil_codec_init_reg(struct tavil_priv *priv) diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c index 2082c356203d..3b681f53b17a 100644 --- a/sound/soc/codecs/wcd_cpe_core.c +++ b/sound/soc/codecs/wcd_cpe_core.c @@ -3556,6 +3556,8 @@ static int wcd_cpe_lsm_lab_control( pr_debug("%s: enter payload_size = %d Enable %d\n", __func__, pld_size, enable); + memset(&cpe_lab_enable, 0, sizeof(cpe_lab_enable)); + if (fill_lsm_cmd_header_v0_inband(&cpe_lab_enable.hdr, session->id, (u8) pld_size, CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { return -EINVAL; diff --git a/sound/soc/msm/apq8096-auto.c b/sound/soc/msm/apq8096-auto.c index c01150f883ff..e1baca358d5f 100644 --- a/sound/soc/msm/apq8096-auto.c +++ b/sound/soc/msm/apq8096-auto.c @@ -22,6 +22,7 @@ #include <linux/module.h> #include <linux/switch.h> #include <linux/input.h> +#include <linux/qdsp6v2/apr.h> #include <sound/core.h> #include <sound/soc.h> #include <sound/soc-dapm.h> @@ -68,6 +69,7 @@ static int msm_tert_tdm_rx_0_ch = 2; /* ICC STREAM */ static int msm_tert_tdm_rx_1_ch = 2; static int msm_tert_tdm_rx_2_ch = 2; static int msm_tert_tdm_rx_3_ch = 2; +static int msm_tert_tdm_rx_4_ch; static int msm_tert_tdm_tx_0_ch = 6; /* EC_REF1-EC_REF6(6 CHAN) */ static int msm_tert_tdm_tx_1_ch = 1; @@ -94,6 +96,7 @@ static int msm_tert_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; static int msm_tert_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE; static int msm_tert_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE; static int msm_tert_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_tert_tdm_rx_4_bit_format = SNDRV_PCM_FORMAT_S16_LE; static int msm_tert_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; static int msm_tert_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE; @@ -836,6 +839,24 @@ static int msm_tert_tdm_rx_3_ch_put(struct snd_kcontrol *kcontrol, return 0; } +static int msm_tert_tdm_rx_4_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_tert_tdm_rx_4_ch = %d\n", __func__, + msm_tert_tdm_rx_4_ch); + ucontrol->value.integer.value[0] = msm_tert_tdm_rx_4_ch - 1; + return 0; +} + +static int msm_tert_tdm_rx_4_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_tert_tdm_rx_4_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_tert_tdm_rx_4_ch = %d\n", __func__, + msm_tert_tdm_rx_4_ch); + return 0; +} + static int msm_tert_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1324,6 +1345,40 @@ static int msm_tert_tdm_rx_3_bit_format_put(struct snd_kcontrol *kcontrol, return 0; } +static int msm_tert_tdm_rx_4_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_tert_tdm_rx_4_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_tert_tdm_rx_4_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_tert_tdm_rx_4_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_tert_tdm_rx_4_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_tert_tdm_rx_4_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_tert_tdm_rx_4_bit_format = %d\n", + __func__, msm_tert_tdm_rx_4_bit_format); + return 0; +} + static int msm_tert_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2020,6 +2075,11 @@ static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, msm_tert_tdm_rx_3_bit_format); break; + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + channels->min = channels->max = msm_tert_tdm_rx_4_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_tert_tdm_rx_4_bit_format); + break; case AFE_PORT_ID_TERTIARY_TDM_TX: channels->min = channels->max = msm_tert_tdm_tx_0_ch; param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, @@ -2600,6 +2660,8 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { msm_tert_tdm_rx_2_ch_get, msm_tert_tdm_rx_2_ch_put), SOC_ENUM_EXT("TERT_TDM_RX_3 Channels", msm_snd_enum[5], msm_tert_tdm_rx_3_ch_get, msm_tert_tdm_rx_3_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_4 Channels", msm_snd_enum[5], + msm_tert_tdm_rx_4_ch_get, msm_tert_tdm_rx_4_ch_put), SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", msm_snd_enum[5], msm_tert_tdm_tx_0_ch_get, msm_tert_tdm_tx_0_ch_put), SOC_ENUM_EXT("TERT_TDM_TX_1 Channels", msm_snd_enum[5], @@ -2648,6 +2710,9 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { SOC_ENUM_EXT("TERT_TDM_RX_3 Bit Format", msm_snd_enum[6], msm_tert_tdm_rx_3_bit_format_get, msm_tert_tdm_rx_3_bit_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_4 Bit Format", msm_snd_enum[6], + msm_tert_tdm_rx_4_bit_format_get, + msm_tert_tdm_rx_4_bit_format_put), SOC_ENUM_EXT("TERT_TDM_TX_0 Bit Format", msm_snd_enum[6], msm_tert_tdm_tx_0_bit_format_get, msm_tert_tdm_tx_0_bit_format_put), @@ -4160,6 +4225,20 @@ static struct snd_soc_dai_link apq8096_auto_be_dai_links[] = { .ignore_suspend = 1, }, { + .name = LPASS_BE_TERT_TDM_RX_4, + .stream_name = "Tertiary TDM4 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36904", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_4, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { .name = LPASS_BE_TERT_TDM_TX_0, .stream_name = "Tertiary TDM0 Capture", .cpu_dai_name = "msm-dai-q6-tdm.36897", @@ -4606,12 +4685,21 @@ static int apq8096_asoc_machine_probe(struct platform_device *pdev) struct snd_soc_card *card; const struct of_device_id *match; int ret; + enum apr_subsys_state q6_state; if (!pdev->dev.of_node) { dev_err(&pdev->dev, "No platform supplied from device tree\n"); return -EINVAL; } + q6_state = apr_get_q6_state(); + if (q6_state == APR_SUBSYS_DOWN) { + dev_dbg(&pdev->dev, "deferring %s, adsp_state %d\n", + __func__, q6_state); + ret = -EPROBE_DEFER; + goto err; + } + card = populate_snd_card_dailinks(&pdev->dev); if (!card) { dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__); diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c index a7a25ea070da..33443ab151e9 100644 --- a/sound/soc/msm/msm-dai-fe.c +++ b/sound/soc/msm/msm-dai-fe.c @@ -113,7 +113,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, - .channels_max = 4, + .channels_max = 8, .rate_min = 8000, .rate_max = 48000, }, diff --git a/sound/soc/msm/msm8998.c b/sound/soc/msm/msm8998.c index ec02cdf3ca3c..f6a5d1344568 100644 --- a/sound/soc/msm/msm8998.c +++ b/sound/soc/msm/msm8998.c @@ -4155,9 +4155,6 @@ static void update_mi2s_clk_val(int dai_id, int stream) mi2s_clk[dai_id].clk_freq_in_hz = mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample; } - - if (!mi2s_intf_conf[dai_id].msm_is_mi2s_master) - mi2s_clk[dai_id].clk_freq_in_hz = 0; } static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) @@ -4206,6 +4203,13 @@ static int msm_set_pinctrl(struct msm_pinctrl_info *pinctrl_info, ret = -EINVAL; goto err; } + + if (pinctrl_info->pinctrl == NULL) { + pr_err("%s: pinctrl_info->pinctrl is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + curr_state = pinctrl_info->curr_state; pinctrl_info->curr_state = new_state; pr_debug("%s: curr_state = %s new_state = %s\n", __func__, @@ -4474,6 +4478,7 @@ static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) struct snd_soc_card *card = rtd->card; struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 0; dev_dbg(rtd->card->dev, "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", @@ -4488,11 +4493,10 @@ static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) goto done; } if (index == QUAT_MI2S) { - ret = msm_set_pinctrl(pinctrl_info, STATE_MI2S_ACTIVE); - if (ret) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_MI2S_ACTIVE); + if (ret_pinctrl) { pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", - __func__, ret); - goto done; + __func__, ret_pinctrl); } } @@ -4551,6 +4555,7 @@ static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) struct snd_soc_card *card = rtd->card; struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 0; pr_debug("%s(): substream = %s stream = %d\n", __func__, substream->name, substream->stream); @@ -4571,10 +4576,10 @@ static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) mutex_unlock(&mi2s_intf_conf[index].lock); if (index == QUAT_MI2S) { - ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); - if (ret) + ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); + if (ret_pinctrl) pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", - __func__, ret); + __func__, ret_pinctrl); } } diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c index 2929ea0d735b..8fc09130ab7d 100644 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -187,7 +187,7 @@ struct msm_compr_audio { const u32 compr_codecs[] = { SND_AUDIOCODEC_AC3, SND_AUDIOCODEC_EAC3, SND_AUDIOCODEC_DTS, - SND_AUDIOCODEC_DSD}; + SND_AUDIOCODEC_DSD, SND_AUDIOCODEC_TRUEHD}; struct query_audio_effect { uint32_t mod_id; @@ -904,7 +904,7 @@ static void populate_codec_list(struct msm_compr_audio *prtd) COMPR_PLAYBACK_MIN_NUM_FRAGMENTS; prtd->compr_cap.max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; - prtd->compr_cap.num_codecs = 15; + prtd->compr_cap.num_codecs = 16; prtd->compr_cap.codecs[0] = SND_AUDIOCODEC_MP3; prtd->compr_cap.codecs[1] = SND_AUDIOCODEC_AAC; prtd->compr_cap.codecs[2] = SND_AUDIOCODEC_AC3; @@ -920,6 +920,7 @@ static void populate_codec_list(struct msm_compr_audio *prtd) prtd->compr_cap.codecs[12] = SND_AUDIOCODEC_DTS; prtd->compr_cap.codecs[13] = SND_AUDIOCODEC_DSD; prtd->compr_cap.codecs[14] = SND_AUDIOCODEC_APTX; + prtd->compr_cap.codecs[15] = SND_AUDIOCODEC_TRUEHD; } static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream, @@ -1174,6 +1175,10 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream, pr_err("%s: CMD DSD Format block failed ret %d\n", __func__, ret); break; + case FORMAT_TRUEHD: + pr_debug("SND_AUDIOCODEC_TRUEHD\n"); + /* no media format block needed */ + break; case FORMAT_APTX: pr_debug("SND_AUDIOCODEC_APTX\n"); memset(&aptx_cfg, 0x0, sizeof(struct aptx_dec_bt_addr_cfg)); @@ -1972,6 +1977,12 @@ static int msm_compr_set_params(struct snd_compr_stream *cstream, break; } + case SND_AUDIOCODEC_TRUEHD: { + pr_debug("%s: SND_AUDIOCODEC_TRUEHD\n", __func__); + prtd->codec = FORMAT_TRUEHD; + break; + } + case SND_AUDIOCODEC_APTX: { pr_debug("%s: SND_AUDIOCODEC_APTX\n", __func__); prtd->codec = FORMAT_APTX; @@ -2836,20 +2847,14 @@ static int msm_compr_get_codec_caps(struct snd_compr_stream *cstream, SND_AUDIOSTREAMFORMAT_RAW); break; case SND_AUDIOCODEC_AC3: - break; case SND_AUDIOCODEC_EAC3: - break; case SND_AUDIOCODEC_FLAC: - break; case SND_AUDIOCODEC_VORBIS: - break; case SND_AUDIOCODEC_ALAC: - break; case SND_AUDIOCODEC_APE: - break; case SND_AUDIOCODEC_DTS: - break; case SND_AUDIOCODEC_DSD: + case SND_AUDIOCODEC_TRUEHD: case SND_AUDIOCODEC_APTX: break; default: @@ -3318,6 +3323,7 @@ static int msm_compr_send_dec_params(struct snd_compr_stream *cstream, switch (prtd->codec) { case FORMAT_MP3: case FORMAT_MPEG4_AAC: + case FORMAT_TRUEHD: case FORMAT_APTX: pr_debug("%s: no runtime parameters for codec: %d\n", __func__, prtd->codec); @@ -3385,6 +3391,7 @@ static int msm_compr_dec_params_put(struct snd_kcontrol *kcontrol, case FORMAT_APE: case FORMAT_DTS: case FORMAT_DSD: + case FORMAT_TRUEHD: case FORMAT_APTX: pr_debug("%s: no runtime parameters for codec: %d\n", __func__, prtd->codec); diff --git a/sound/soc/msm/qdsp6v2/msm-dai-slim.c b/sound/soc/msm/qdsp6v2/msm-dai-slim.c index 43a27eb35a44..64ecd7e031f0 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-slim.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-slim.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -314,7 +314,7 @@ static int msm_dai_slim_prepare(struct snd_pcm_substream *substream, struct msm_slim_dai_data *dai_data = NULL; struct slim_ch prop; int rc; - u8 i; + u8 i, j; dai_data = msm_slim_get_dai_data(drv_data, dai); if (!dai_data) { @@ -351,10 +351,6 @@ static int msm_dai_slim_prepare(struct snd_pcm_substream *substream, } } - /* To decrement the channel ref count*/ - for (i = 0; i < dai_data->ch_cnt; i++) - slim_dealloc_ch(drv_data->sdev, dai_data->chan_h[i]); - prop.prot = SLIM_AUTO_ISO; prop.baser = SLIM_RATE_4000HZ; prop.dataf = SLIM_CH_DATAF_NOT_DEFINED; @@ -378,6 +374,8 @@ static int msm_dai_slim_prepare(struct snd_pcm_substream *substream, error_define_chan: error_chan_query: + for (j = 0; j < i; j++) + slim_dealloc_ch(drv_data->sdev, dai_data->chan_h[j]); return rc; } @@ -387,6 +385,7 @@ static void msm_dai_slim_shutdown(struct snd_pcm_substream *stream, struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev); struct msm_slim_dma_data *dma_data = NULL; struct msm_slim_dai_data *dai_data; + int i, rc = 0; dai_data = msm_slim_get_dai_data(drv_data, dai); dma_data = snd_soc_dai_get_dma_data(dai, stream); @@ -405,6 +404,15 @@ static void msm_dai_slim_shutdown(struct snd_pcm_substream *stream, return; } + for (i = 0; i < dai_data->ch_cnt; i++) { + rc = slim_dealloc_ch(drv_data->sdev, dai_data->chan_h[i]); + if (rc) { + dev_err(dai->dev, + "%s: dealloc_ch failed, err = %d\n", + __func__, rc); + } + } + snd_soc_dai_set_dma_data(dai, stream, NULL); /* clear prepared state for the dai */ CLR_DAI_STATE(dai_data->status, DAI_STATE_PREPARED); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index 7ce73484a681..f461f360d88d 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -80,6 +80,7 @@ static uint32_t voc_session_id = ALL_SESSION_VSID; static int msm_route_ext_ec_ref; static bool is_custom_stereo_on; static bool is_ds2_on; +static bool swap_ch; enum { MADNONE, @@ -7933,6 +7934,9 @@ static const struct snd_kcontrol_new aux_pcm_rx_port_mixer_controls[] = { SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_RX, MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new sec_auxpcm_rx_port_mixer_controls[] = { @@ -13325,6 +13329,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SLIM7_UL_HL", NULL, "HFP_SLIM7_UL_HL"}, {"HFP_SLIM7_UL_HL", "Switch", "SLIMBUS_7_TX"}, {"AUX_PCM_RX", NULL, "AUXPCM_DL_HL"}, + {"AUX_PCM_RX", NULL, "INTHFP_DL_HL"}, {"AUXPCM_UL_HL", NULL, "AUX_PCM_TX"}, {"MI2S_RX", NULL, "MI2S_DL_HL"}, {"MI2S_UL_HL", NULL, "MI2S_TX"}, @@ -13729,6 +13734,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"AUX_PCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"AUX_PCM_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, {"AUX_PCM_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"AUX_PCM_RX Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, {"AUX_PCM_RX", NULL, "AUX_PCM_RX Port Mixer"}, {"SEC_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, @@ -14562,6 +14568,67 @@ static const struct snd_kcontrol_new }, }; +static int msm_routing_stereo_channel_reverse_control_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = swap_ch; + pr_debug("%s: Swap channel value: %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_stereo_channel_reverse_control_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i, idx, be_index, port_id; + int ret = 0; + unsigned long copp; + + pr_debug("%s Swap channel value:%ld\n", __func__, + ucontrol->value.integer.value[0]); + + swap_ch = ucontrol->value.integer.value[0]; + + mutex_lock(&routing_lock); + for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) { + port_id = msm_bedais[be_index].port_id; + if (!msm_bedais[be_index].active) + continue; + + for_each_set_bit(i, &msm_bedais[be_index].fe_sessions[0], + MSM_FRONTEND_DAI_MM_SIZE) { + copp = session_copp_map[i][SESSION_TYPE_RX][be_index]; + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + if (!test_bit(idx, &copp)) + continue; + + pr_debug("%s: swap channel control of portid:%d, coppid:%d\n", + __func__, port_id, idx); + ret = adm_swap_speaker_channels( + port_id, idx, + msm_bedais[be_index].sample_rate, + swap_ch); + if (ret) { + pr_err("%s:Swap_channel failed, err=%d\n", + __func__, ret); + goto done; + } + } + } + } +done: + mutex_unlock(&routing_lock); + return ret; +} + +static const struct snd_kcontrol_new stereo_channel_reverse_control[] = { + SOC_SINGLE_EXT("Swap channel", SND_SOC_NOPM, 0, + 1, 0, msm_routing_stereo_channel_reverse_control_get, + msm_routing_stereo_channel_reverse_control_put), +}; + static struct snd_pcm_ops msm_routing_pcm_ops = { .hw_params = msm_pcm_routing_hw_params, .close = msm_pcm_routing_close, @@ -14627,6 +14694,8 @@ static int msm_routing_probe(struct snd_soc_platform *platform) snd_soc_add_platform_controls(platform, aptx_dec_license_controls, ARRAY_SIZE(aptx_dec_license_controls)); + snd_soc_add_platform_controls(platform, stereo_channel_reverse_control, + ARRAY_SIZE(stereo_channel_reverse_control)); return 0; } diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c index 2da7e6dc11e7..f026e82a73cc 100644 --- a/sound/soc/msm/qdsp6v2/q6adm.c +++ b/sound/soc/msm/qdsp6v2/q6adm.c @@ -4288,6 +4288,136 @@ end: return ret; } +/** + * adm_swap_speaker_channels + * + * Receives port_id, copp_idx, sample rate, spk_swap and + * send MFC command to swap speaker channel. + * Return zero on success. On failure returns nonzero. + * + * port_id - Passed value, port_id for which channels swap is wanted + * copp_idx - Passed value, copp_idx for which channels swap is wanted + * sample_rate - Passed value, sample rate used by app type config + * spk_swap - Passed value, spk_swap for check if swap flag is set +*/ +int adm_swap_speaker_channels(int port_id, int copp_idx, + int sample_rate, bool spk_swap) +{ + struct audproc_mfc_output_media_fmt mfc_cfg; + uint16_t num_channels; + int port_idx; + int ret = 0; + + pr_debug("%s: Enter, port_id %d, copp_idx %d\n", + __func__, port_id, copp_idx); + port_id = q6audio_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + ret = -EINVAL; + goto done; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + ret = -EINVAL; + goto done; + } + + num_channels = atomic_read( + &this_adm.copp.channels[port_idx][copp_idx]); + if (num_channels != 2) { + pr_debug("%s: Invalid number of channels: %d\n", + __func__, num_channels); + ret = -EINVAL; + goto done; + } + + memset(&mfc_cfg, 0, sizeof(mfc_cfg)); + mfc_cfg.params.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mfc_cfg.params.hdr.pkt_size = + sizeof(mfc_cfg); + mfc_cfg.params.hdr.src_svc = APR_SVC_ADM; + mfc_cfg.params.hdr.src_domain = APR_DOMAIN_APPS; + mfc_cfg.params.hdr.src_port = port_id; + mfc_cfg.params.hdr.dest_svc = APR_SVC_ADM; + mfc_cfg.params.hdr.dest_domain = APR_DOMAIN_ADSP; + mfc_cfg.params.hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + mfc_cfg.params.hdr.token = port_idx << 16 | copp_idx; + mfc_cfg.params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + mfc_cfg.params.payload_addr_lsw = 0; + mfc_cfg.params.payload_addr_msw = 0; + mfc_cfg.params.mem_map_handle = 0; + mfc_cfg.params.payload_size = sizeof(mfc_cfg) - + sizeof(mfc_cfg.params); + mfc_cfg.data.module_id = AUDPROC_MODULE_ID_MFC; + mfc_cfg.data.param_id = AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT; + mfc_cfg.data.param_size = mfc_cfg.params.payload_size - + sizeof(mfc_cfg.data); + mfc_cfg.data.reserved = 0; + mfc_cfg.sampling_rate = sample_rate; + mfc_cfg.bits_per_sample = + atomic_read(&this_adm.copp.bit_width[port_idx][copp_idx]); + mfc_cfg.num_channels = num_channels; + + /* Currently applying speaker swap for only 2 channel use case */ + if (spk_swap) { + mfc_cfg.channel_type[0] = + (uint16_t) PCM_CHANNEL_FR; + mfc_cfg.channel_type[1] = + (uint16_t) PCM_CHANNEL_FL; + } else { + mfc_cfg.channel_type[0] = + (uint16_t) PCM_CHANNEL_FL; + mfc_cfg.channel_type[1] = + (uint16_t) PCM_CHANNEL_FR; + } + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + pr_debug("%s: mfc config: port_idx %d copp_idx %d copp SR %d copp BW %d copp chan %d\n", + __func__, port_idx, copp_idx, mfc_cfg.sampling_rate, + mfc_cfg.bits_per_sample, mfc_cfg.num_channels); + + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&mfc_cfg); + if (ret < 0) { + pr_err("%s: port_id: for[0x%x] failed %d\n", + __func__, port_id, ret); + goto done; + } + /* Wait for the callback with copp id */ + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: mfc_cfg Set params timed out for port_id: for [0x%x]\n", + __func__, port_id); + ret = -ETIMEDOUT; + goto done; + } + + if (atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto done; + } + + pr_debug("%s: mfc_cfg Set params returned success", __func__); + ret = 0; + +done: + return ret; +} +EXPORT_SYMBOL(adm_swap_speaker_channels); + int adm_set_sound_focus(int port_id, int copp_idx, struct sound_focus_param soundFocusData) { diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c index 6616edcd3347..f0a78dc8aee8 100644 --- a/sound/soc/msm/qdsp6v2/q6afe.c +++ b/sound/soc/msm/qdsp6v2/q6afe.c @@ -5193,7 +5193,7 @@ static int afe_sidetone(u16 tx_port_id, u16 rx_port_id, bool enable) AFE_API_VERSION_LOOPBACK_CONFIG; cmd_sidetone.cfg_data.dst_port_id = rx_port_id; cmd_sidetone.cfg_data.routing_mode = LB_MODE_SIDETONE; - cmd_sidetone.cfg_data.enable = ((enable == 1) ? sidetone_enable : 0); + cmd_sidetone.cfg_data.enable = enable; pr_debug("%s rx(0x%x) tx(0x%x) enable(%d) mid(0x%x) gain(%d) sidetone_enable(%d)\n", __func__, rx_port_id, tx_port_id, diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index 731f439f5286..a9241cfdfec7 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -2782,6 +2782,9 @@ static int __q6asm_open_write(struct audio_client *ac, uint32_t format, case FORMAT_GEN_COMPR: open.dec_fmt_id = ASM_MEDIA_FMT_GENERIC_COMPRESSED; break; + case FORMAT_TRUEHD: + open.dec_fmt_id = ASM_MEDIA_FMT_TRUEHD; + break; default: pr_err("%s: Invalid format 0x%x\n", __func__, format); rc = -EINVAL; diff --git a/sound/soc/msm/qdsp6v2/q6lsm.c b/sound/soc/msm/qdsp6v2/q6lsm.c index 525ec1c30f48..11574a874a5a 100644 --- a/sound/soc/msm/qdsp6v2/q6lsm.c +++ b/sound/soc/msm/qdsp6v2/q6lsm.c @@ -1243,14 +1243,12 @@ static int q6lsm_send_cal(struct lsm_client *client, mutex_lock(&lsm_common.cal_data[LSM_CAL_IDX]->lock); cal_block = cal_utils_get_only_cal_block( lsm_common.cal_data[LSM_CAL_IDX]); - if (cal_block == NULL) - goto unlock; - if (cal_block->cal_data.size <= 0) { + if (!cal_block || cal_block->cal_data.size <= 0) { pr_debug("%s: No cal to send!\n", __func__); - rc = -EINVAL; goto unlock; } + if (cal_block->cal_data.size != client->lsm_cal_size) { pr_err("%s: Cal size %zd doesn't match lsm cal size %d\n", __func__, cal_block->cal_data.size, diff --git a/sound/soc/msm/sdm660-common.c b/sound/soc/msm/sdm660-common.c index edf5719fbf3e..90b661dff7ec 100644 --- a/sound/soc/msm/sdm660-common.c +++ b/sound/soc/msm/sdm660-common.c @@ -2412,9 +2412,6 @@ static void update_mi2s_clk_val(int dai_id, int stream) mi2s_clk[dai_id].clk_freq_in_hz = mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample; } - - if (!mi2s_intf_conf[dai_id].msm_is_mi2s_master) - mi2s_clk[dai_id].clk_freq_in_hz = 0; } static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) diff --git a/sound/usb/Makefile b/sound/usb/Makefile index d2ac0386d3da..083887ba0346 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -13,7 +13,8 @@ snd-usb-audio-objs := card.o \ pcm.o \ proc.o \ quirks.o \ - stream.o + stream.o \ + badd.o snd-usbmidi-lib-objs := midi.o diff --git a/sound/usb/badd.c b/sound/usb/badd.c new file mode 100644 index 000000000000..cc6c26cbb624 --- /dev/null +++ b/sound/usb/badd.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/usb.h> +#include <linux/usb/audio.h> +#include <linux/usb/audio-v2.h> +#include <linux/usb/audio-v3.h> + +struct uac3_input_terminal_descriptor badd_baif_in_term_desc = { + .bLength = UAC3_DT_INPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_INPUT_TERMINAL, + .bTerminalID = BADD_IN_TERM_ID_BAIF, + .bCSourceID = BADD_CLOCK_SOURCE, + .wExTerminalDescrID = 0x0000, + .wTerminalDescrStr = 0x0000 +}; + +struct uac3_input_terminal_descriptor badd_baof_in_term_desc = { + .bLength = UAC3_DT_INPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_INPUT_TERMINAL, + .bTerminalID = BADD_IN_TERM_ID_BAOF, + .wTerminalType = UAC_TERMINAL_STREAMING, + .bAssocTerminal = 0x00, + .bCSourceID = BADD_CLOCK_SOURCE, + .bmControls = 0x00000000, + .wExTerminalDescrID = 0x0000, + .wConnectorsDescrID = 0x0000, + .wTerminalDescrStr = 0x0000 +}; + +struct uac3_output_terminal_descriptor badd_baif_out_term_desc = { + .bLength = UAC3_DT_OUTPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, + .bTerminalID = BADD_OUT_TERM_ID_BAIF, + .wTerminalType = UAC_TERMINAL_STREAMING, + .bAssocTerminal = 0x00, /* No associated terminal */ + .bSourceID = BADD_FU_ID_BAIF, + .bCSourceID = BADD_CLOCK_SOURCE, + .bmControls = 0x00000000, /* No controls */ + .wExTerminalDescrID = 0x0000, + .wConnectorsDescrID = 0x0000, + .wTerminalDescrStr = 0x0000 +}; + +struct uac3_output_terminal_descriptor badd_baof_out_term_desc = { + .bLength = UAC3_DT_OUTPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, + .bTerminalID = BADD_OUT_TERM_ID_BAOF, + .bSourceID = BADD_FU_ID_BAOF, + .bCSourceID = BADD_CLOCK_SOURCE, + .wExTerminalDescrID = 0x0000, + .wTerminalDescrStr = 0x0000 +}; + +__u8 monoControls[] = { + 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00}; + +__u8 stereoControls[] = { + 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00 +}; + +__u8 badd_mu_src_ids[] = {BADD_IN_TERM_ID_BAOF, BADD_FU_ID_BAIOF}; + +struct uac3_mixer_unit_descriptor badd_baiof_mu_desc = { + .bLength = UAC3_DT_MIXER_UNIT_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC3_MIXER_UNIT_V3, + .bUnitID = BADD_MU_ID_BAIOF, + .bNrInPins = 0x02, + .baSourceID = badd_mu_src_ids, + .bmMixerControls = 0x00, + .bmControls = 0x00000000, + .wMixerDescrStr = 0x0000 +}; + +struct uac3_feature_unit_descriptor badd_baif_fu_desc = { + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC3_FEATURE_UNIT_V3, + .bUnitID = BADD_FU_ID_BAIF, + .bSourceID = BADD_IN_TERM_ID_BAIF, + .wFeatureDescrStr = 0x0000 +}; + +struct uac3_feature_unit_descriptor badd_baof_fu_desc = { + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC3_FEATURE_UNIT_V3, + .bUnitID = BADD_FU_ID_BAOF, + .wFeatureDescrStr = 0x0000 +}; + +struct uac3_feature_unit_descriptor badd_baiof_fu_desc = { + .bLength = 0x0f, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC3_FEATURE_UNIT_V3, + .bUnitID = BADD_FU_ID_BAIOF, + .bSourceID = BADD_IN_TERM_ID_BAIF, + .bmaControls = monoControls, + .wFeatureDescrStr = 0x0000 +}; + +struct uac3_clock_source_descriptor badd_clock_desc = { + .bLength = UAC3_DT_CLOCK_SRC_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC3_CLOCK_SOURCE, + .bClockID = BADD_CLOCK_SOURCE, + .bmControls = 0x00000001, + .bReferenceTerminal = 0x00, + .wClockSourceStr = 0x0000 +}; + +void *badd_desc_list[] = { + &badd_baif_in_term_desc, + &badd_baof_in_term_desc, + &badd_baiof_mu_desc, + &badd_baif_fu_desc, + &badd_baof_fu_desc, + &badd_baiof_fu_desc, + &badd_clock_desc +}; + diff --git a/sound/usb/card.c b/sound/usb/card.c index 7bc935bab369..23ea575f3bcf 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -45,6 +45,7 @@ #include <linux/usb/audio.h> #include <linux/usb/audio-v2.h> #include <linux/module.h> +#include <linux/usb/audio-v3.h> #include <sound/control.h> #include <sound/core.h> @@ -281,7 +282,6 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) struct usb_host_interface *host_iface; struct usb_interface_descriptor *altsd; struct usb_interface *usb_iface; - void *control_header; int i, protocol; usb_iface = usb_ifnum_to_if(dev, ctrlif); @@ -298,16 +298,13 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) return -EINVAL; } - control_header = snd_usb_find_csint_desc(host_iface->extra, - host_iface->extralen, - NULL, UAC_HEADER); altsd = get_iface_desc(host_iface); protocol = altsd->bInterfaceProtocol; - if (!control_header) { - dev_err(&dev->dev, "cannot find UAC_HEADER\n"); - return -EINVAL; - } + /* + * UAC 1.0 devices use AC HEADER Desc for linking AS interfaces; + * UAC 2.0 and 3.0 devices use IAD for linking AS interfaces + */ switch (protocol) { default: @@ -317,8 +314,17 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) /* fall through */ case UAC_VERSION_1: { - struct uac1_ac_header_descriptor *h1 = control_header; + void *control_header; + struct uac1_ac_header_descriptor *h1; + + control_header = snd_usb_find_csint_desc(host_iface->extra, + host_iface->extralen, NULL, UAC_HEADER); + if (!control_header) { + dev_err(&dev->dev, "cannot find UAC_HEADER\n"); + return -EINVAL; + } + h1 = control_header; if (!h1->bInCollection) { dev_info(&dev->dev, "skipping empty audio interface (v1)\n"); return -EINVAL; @@ -335,7 +341,8 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) break; } - case UAC_VERSION_2: { + case UAC_VERSION_2: + case UAC_VERSION_3: { struct usb_interface_assoc_descriptor *assoc = usb_iface->intf_assoc; if (!assoc) { @@ -354,7 +361,8 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) } if (!assoc) { - dev_err(&dev->dev, "Audio class v2 interfaces need an interface association\n"); + dev_err(&dev->dev, "Audio class V%d interfaces need an interface association\n", + protocol); return -EINVAL; } @@ -554,6 +562,15 @@ static int usb_audio_probe(struct usb_interface *intf, struct usb_host_interface *alts; int ifnum; u32 id; + struct usb_interface_assoc_descriptor *assoc; + + assoc = intf->intf_assoc; + if (assoc && assoc->bFunctionClass == USB_CLASS_AUDIO && + assoc->bFunctionProtocol == UAC_VERSION_3 && + assoc->bFunctionSubClass == FULL_ADC_PROFILE) { + dev_info(&dev->dev, "No support for full-fledged ADC 3.0 yet!!\n"); + return -EINVAL; + } alts = &intf->altsetting[0]; ifnum = get_iface_desc(alts)->bInterfaceNumber; diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 7ccbcaf6a147..2cd09ceba5e9 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -424,6 +424,10 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, case UAC_VERSION_2: return set_sample_rate_v2(chip, iface, alts, fmt, rate); + + /* Clock rate is fixed at 48 kHz for BADD devices */ + case UAC_VERSION_3: + return 0; } } diff --git a/sound/usb/format.c b/sound/usb/format.c index 789d19ec035d..2cc3b92f1fba 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -20,6 +20,7 @@ #include <linux/usb.h> #include <linux/usb/audio.h> #include <linux/usb/audio-v2.h> +#include <linux/usb/audio-v3.h> #include <sound/core.h> #include <sound/pcm.h> @@ -69,6 +70,30 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, format <<= 1; break; } + + case UAC_VERSION_3: { + switch (fp->maxpacksize) { + case BADD_MAXPSIZE_SYNC_MONO_16: + case BADD_MAXPSIZE_SYNC_STEREO_16: + case BADD_MAXPSIZE_ASYNC_MONO_16: + case BADD_MAXPSIZE_ASYNC_STEREO_16: { + sample_width = BIT_RES_16_BIT; + sample_bytes = SUBSLOTSIZE_16_BIT; + break; + } + + case BADD_MAXPSIZE_SYNC_MONO_24: + case BADD_MAXPSIZE_SYNC_STEREO_24: + case BADD_MAXPSIZE_ASYNC_MONO_24: + case BADD_MAXPSIZE_ASYNC_STEREO_24: { + sample_width = BIT_RES_24_BIT; + sample_bytes = SUBSLOTSIZE_24_BIT; + break; + } + } + format = 1 << format; + break; + } } if ((pcm_formats == 0) && @@ -366,6 +391,22 @@ err: return ret; } +static int badd_set_audio_rate_v3(struct snd_usb_audio *chip, + struct audioformat *fp) +{ + unsigned int rate; + + fp->rate_table = kmalloc(sizeof(int), GFP_KERNEL); + if (fp->rate_table == NULL) + return -ENOMEM; + + fp->nr_rates = 1; + rate = BADD_SAMPLING_RATE; + fp->rate_min = fp->rate_max = fp->rate_table[0] = rate; + fp->rates |= snd_pcm_rate_to_rate_bit(rate); + return 0; +} + /* * parse the format type I and III descriptors */ @@ -415,6 +456,9 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, /* fp->channels is already set in this case */ ret = parse_audio_format_rates_v2(chip, fp); break; + case UAC_VERSION_3: + ret = badd_set_audio_rate_v3(chip, fp); + break; } if (fp->channels < 1) { @@ -502,7 +546,10 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip, fmt->bFormatType); return -ENOTSUPP; } - fp->fmt_type = fmt->bFormatType; + if (fp->protocol == UAC_VERSION_3) + fp->fmt_type = UAC_FORMAT_TYPE_I; + else + fp->fmt_type = fmt->bFormatType; if (err < 0) return err; #if 1 diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 157c0704817c..b9d9d2e99c78 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -50,6 +50,7 @@ #include <linux/usb.h> #include <linux/usb/audio.h> #include <linux/usb/audio-v2.h> +#include <linux/usb/audio-v3.h> #include <sound/core.h> #include <sound/control.h> @@ -184,6 +185,17 @@ static void *find_audio_control_unit(struct mixer_build *state, /* we just parse the header */ struct uac_feature_unit_descriptor *hdr = NULL; + if (state->mixer->protocol == UAC_VERSION_3) { + int i; + + for (i = 0; i < NUM_BADD_DESCS; i++) { + hdr = (void *)badd_desc_list[i]; + if (hdr->bUnitID == unit) + return hdr; + } + + return NULL; + } while ((hdr = snd_usb_find_desc(state->buffer, state->buflen, hdr, USB_DT_CS_INTERFACE)) != NULL) { if (hdr->bLength >= 4 && @@ -717,7 +729,7 @@ static int check_input_term(struct mixer_build *state, int id, term->channels = d->bNrChannels; term->chconfig = le16_to_cpu(d->wChannelConfig); term->name = d->iTerminal; - } else { /* UAC_VERSION_2 */ + } else if (state->mixer->protocol == UAC_VERSION_2) { struct uac2_input_terminal_descriptor *d = p1; /* call recursively to verify that the @@ -734,6 +746,24 @@ static int check_input_term(struct mixer_build *state, int id, term->channels = d->bNrChannels; term->chconfig = le32_to_cpu(d->bmChannelConfig); term->name = d->iTerminal; + } else { /* UAC_VERSION_3 */ + struct uac3_input_terminal_descriptor *d = p1; + + err = check_input_term(state, + d->bCSourceID, term); + if (err < 0) + return err; + + term->id = id; + term->type = d->wTerminalType; + if (d->wClusterDescrID == CLUSTER_ID_MONO) { + term->channels = NUM_CHANNELS_MONO; + term->chconfig = BADD_CH_CONFIG_MONO; + } else { + term->channels = NUM_CHANNELS_STEREO; + term->chconfig = BADD_CH_CONFIG_STEREO; + } + term->name = d->wTerminalDescrStr; } return 0; case UAC_FEATURE_UNIT: { @@ -751,41 +781,81 @@ static int check_input_term(struct mixer_build *state, int id, return 0; } case UAC_SELECTOR_UNIT: - case UAC2_CLOCK_SELECTOR: { - struct uac_selector_unit_descriptor *d = p1; - /* call recursively to retrieve the channel info */ - err = check_input_term(state, d->baSourceID[0], term); - if (err < 0) - return err; - term->type = d->bDescriptorSubtype << 16; /* virtual type */ - term->id = id; - term->name = uac_selector_unit_iSelector(d); + /* UAC3_MIXER_UNIT_V3 */ + case UAC2_CLOCK_SELECTOR: + /* UAC3_CLOCK_SOURCE */ { + if (state->mixer->protocol == UAC_VERSION_3 + && hdr[2] == UAC3_CLOCK_SOURCE) { + struct uac3_clock_source_descriptor *d = p1; + + term->type = d->bDescriptorSubtype << 16; + term->id = id; + term->name = d->wClockSourceStr; + } else if (state->mixer->protocol == UAC_VERSION_3 + && hdr[2] == UAC3_MIXER_UNIT_V3) { + struct uac3_mixer_unit_descriptor *d = p1; + + term->type = d->bDescriptorSubtype << 16; + if (d->wClusterDescrID == CLUSTER_ID_MONO) { + term->channels = NUM_CHANNELS_MONO; + term->chconfig = BADD_CH_CONFIG_MONO; + } else { + term->channels = NUM_CHANNELS_STEREO; + term->chconfig = BADD_CH_CONFIG_STEREO; + } + term->name = d->wMixerDescrStr; + } else { + struct uac_selector_unit_descriptor *d = p1; + /* call recursively to retrieve channel info */ + err = check_input_term(state, + d->baSourceID[0], term); + if (err < 0) + return err; + /* virtual type */ + term->type = d->bDescriptorSubtype << 16; + term->id = id; + term->name = uac_selector_unit_iSelector(d); + } return 0; } case UAC1_PROCESSING_UNIT: case UAC1_EXTENSION_UNIT: /* UAC2_PROCESSING_UNIT_V2 */ /* UAC2_EFFECT_UNIT */ + /* UAC3_FEATURE_UNIT_V3 */ case UAC2_EXTENSION_UNIT_V2: { - struct uac_processing_unit_descriptor *d = p1; + if (state->mixer->protocol == UAC_VERSION_3) { + struct uac_feature_unit_descriptor *d = p1; + + id = d->bSourceID; + } else { + struct uac_processing_unit_descriptor *d = p1; + + if (state->mixer->protocol == UAC_VERSION_2 && + hdr[2] == UAC2_EFFECT_UNIT) { + /* UAC2/UAC1 unit IDs overlap here in an + * uncompatible way. Ignore this unit + * for now. + */ + return 0; + } - if (state->mixer->protocol == UAC_VERSION_2 && - hdr[2] == UAC2_EFFECT_UNIT) { - /* UAC2/UAC1 unit IDs overlap here in an - * uncompatible way. Ignore this unit for now. - */ + if (d->bNrInPins) { + id = d->baSourceID[0]; + break; /* continue to parse */ + } + /* virtual type */ + term->type = d->bDescriptorSubtype << 16; + term->channels = + uac_processing_unit_bNrChannels(d); + term->chconfig = + uac_processing_unit_wChannelConfig( + d, state->mixer->protocol); + term->name = uac_processing_unit_iProcessing( + d, state->mixer->protocol); return 0; } - - if (d->bNrInPins) { - id = d->baSourceID[0]; - break; /* continue to parse */ - } - term->type = d->bDescriptorSubtype << 16; /* virtual type */ - term->channels = uac_processing_unit_bNrChannels(d); - term->chconfig = uac_processing_unit_wChannelConfig(d, state->mixer->protocol); - term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol); - return 0; + break; } case UAC2_CLOCK_SOURCE: { struct uac_clock_source_descriptor *d = p1; @@ -1232,12 +1302,18 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, struct usb_feature_control_info *ctl_info; unsigned int len = 0; int mapped_name = 0; - int nameid = uac_feature_unit_iFeature(desc); + int nameid; struct snd_kcontrol *kctl; struct usb_mixer_elem_info *cval; const struct usbmix_name_map *map; unsigned int range; + if (state->mixer->protocol == UAC_VERSION_3) + nameid = ((struct uac3_feature_unit_descriptor *) + raw_desc)->wFeatureDescrStr; + else + nameid = uac_feature_unit_iFeature(desc); + control++; /* change from zero-based to 1-based value */ if (control == UAC_FU_GRAPHIC_EQUALIZER) { @@ -1258,7 +1334,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, ctl_info = &audio_feature_info[control-1]; if (state->mixer->protocol == UAC_VERSION_1) cval->val_type = ctl_info->type; - else /* UAC_VERSION_2 */ + else /* UAC_VERSION_2 or UAC_VERSION_3*/ cval->val_type = ctl_info->type_uac2 >= 0 ? ctl_info->type_uac2 : ctl_info->type; @@ -1381,6 +1457,62 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, snd_usb_mixer_add_control(&cval->head, kctl); } +static int find_num_channels(struct mixer_build *state, int dir) +{ + int num_ch = -EINVAL, num, i, j, wMaxPacketSize; + int ctrlif = get_iface_desc(state->mixer->hostif)->bInterfaceNumber; + struct usb_interface *usb_iface = + usb_ifnum_to_if(state->mixer->chip->dev, ctrlif); + struct usb_interface_assoc_descriptor *assoc = usb_iface->intf_assoc; + struct usb_host_interface *alts; + + for (i = 0; i < assoc->bInterfaceCount; i++) { + int intf = assoc->bFirstInterface + i; + + if (intf != ctrlif) { + struct usb_interface *iface = + usb_ifnum_to_if(state->mixer->chip->dev, intf); + + alts = &iface->altsetting[1]; + if (dir == USB_DIR_OUT && + get_endpoint(alts, 0)->bEndpointAddress & + USB_DIR_IN) + continue; + if (dir == USB_DIR_IN && + !(get_endpoint(alts, 0)->bEndpointAddress & + USB_DIR_IN)) + continue; + num = iface->num_altsetting; + for (j = 1; j < num; j++) { + num_ch = NUM_CHANNELS_MONO; + alts = &iface->altsetting[j]; + wMaxPacketSize = le16_to_cpu( + get_endpoint(alts, 0)-> + wMaxPacketSize); + switch (wMaxPacketSize) { + case BADD_MAXPSIZE_SYNC_MONO_16: + case BADD_MAXPSIZE_SYNC_MONO_24: + case BADD_MAXPSIZE_ASYNC_MONO_16: + case BADD_MAXPSIZE_ASYNC_MONO_24: + break; + case BADD_MAXPSIZE_SYNC_STEREO_16: + case BADD_MAXPSIZE_SYNC_STEREO_24: + case BADD_MAXPSIZE_ASYNC_STEREO_16: + case BADD_MAXPSIZE_ASYNC_STEREO_24: + num_ch = NUM_CHANNELS_STEREO; + break; + } + if (num_ch == NUM_CHANNELS_MONO) + continue; + else + break; + } + } + } + + return num_ch; +} + /* * parse a feature unit * @@ -1412,7 +1544,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, unitid); return -EINVAL; } - } else { + } else if (state->mixer->protocol == UAC_VERSION_2) { struct uac2_feature_unit_descriptor *ftr = _ftr; csize = 4; channels = (hdr->bLength - 6) / 4 - 1; @@ -1423,11 +1555,114 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, unitid); return -EINVAL; } + } else { + struct usb_interface *usb_iface = + usb_ifnum_to_if(state->mixer->chip->dev, + get_iface_desc(state->mixer->hostif)->bInterfaceNumber); + struct usb_interface_assoc_descriptor *assoc = + usb_iface->intf_assoc; + + csize = 4; + switch (unitid) { + case BADD_FU_ID_BAIOF: + channels = NUM_CHANNELS_MONO; + bmaControls = monoControls; + badd_baif_in_term_desc.wClusterDescrID = + CLUSTER_ID_MONO; + break; + + case BADD_FU_ID_BAOF: + switch (assoc->bFunctionSubClass) { + case PROF_HEADPHONE: + case PROF_HEADSET_ADAPTER: + channels = NUM_CHANNELS_STEREO; + bmaControls = stereoControls; + badd_baiof_mu_desc.wClusterDescrID = + CLUSTER_ID_MONO; + break; + case PROF_SPEAKERPHONE: + channels = NUM_CHANNELS_MONO; + bmaControls = monoControls; + badd_baof_in_term_desc.wClusterDescrID = + CLUSTER_ID_MONO; + break; + default: + channels = find_num_channels(state, + USB_DIR_OUT); + if (channels < 0) { + usb_audio_err(state->chip, + "unit %u: Cant find num of channels\n", + unitid); + return channels; + } + + bmaControls = (channels == NUM_CHANNELS_MONO) ? + monoControls : stereoControls; + badd_baof_in_term_desc.wClusterDescrID = + (channels == NUM_CHANNELS_MONO) ? + CLUSTER_ID_MONO : CLUSTER_ID_STEREO; + break; + } + break; + + case BADD_FU_ID_BAIF: + switch (assoc->bFunctionSubClass) { + case PROF_HEADSET: + case PROF_HEADSET_ADAPTER: + case PROF_SPEAKERPHONE: + channels = NUM_CHANNELS_MONO; + bmaControls = monoControls; + badd_baif_in_term_desc.wClusterDescrID = + CLUSTER_ID_MONO; + break; + default: + channels = find_num_channels(state, USB_DIR_IN); + if (channels < 0) { + usb_audio_err(state->chip, + "unit %u: Cant find num of channels\n", + unitid); + return channels; + } + + bmaControls = (channels == NUM_CHANNELS_MONO) ? + monoControls : stereoControls; + badd_baif_in_term_desc.wClusterDescrID = + (channels == NUM_CHANNELS_MONO) ? + CLUSTER_ID_MONO : CLUSTER_ID_STEREO; + break; + } + break; + } } /* parse the source unit */ - if ((err = parse_audio_unit(state, hdr->bSourceID)) < 0) - return err; + if (state->mixer->protocol != UAC_VERSION_3) { + err = parse_audio_unit(state, hdr->bSourceID); + if (err < 0) + return err; + } else { + struct usb_interface *usb_iface = + usb_ifnum_to_if(state->mixer->chip->dev, + get_iface_desc(state->mixer->hostif)->bInterfaceNumber); + struct usb_interface_assoc_descriptor *assoc = + usb_iface->intf_assoc; + + switch (unitid) { + case BADD_FU_ID_BAOF: + switch (assoc->bFunctionSubClass) { + case PROF_HEADSET: + case PROF_HEADSET_ADAPTER: + hdr->bSourceID = BADD_MU_ID_BAIOF; + break; + default: + hdr->bSourceID = BADD_IN_TERM_ID_BAOF; + break; + } + } + err = parse_audio_unit(state, hdr->bSourceID); + if (err < 0) + return err; + } /* determine the input source type and name */ err = check_input_term(state, hdr->bSourceID, &iterm); @@ -1481,7 +1716,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, build_feature_ctl(state, _ftr, 0, i, &iterm, unitid, 0); } - } else { /* UAC_VERSION_2 */ + } else { /* UAC_VERSION_2 or UAC_VERSION_3*/ for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) { unsigned int ch_bits = 0; unsigned int ch_read_only = 0; @@ -1599,12 +1834,19 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, int input_pins, num_ins, num_outs; int pin, ich, err; - if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) || - !(num_outs = uac_mixer_unit_bNrChannels(desc))) { - usb_audio_err(state->chip, - "invalid MIXER UNIT descriptor %d\n", - unitid); - return -EINVAL; + if (state->mixer->protocol == UAC_VERSION_3) { + input_pins = badd_baiof_mu_desc.bNrInPins; + num_outs = + (badd_baiof_mu_desc.wClusterDescrID == CLUSTER_ID_MONO) ? + NUM_CHANNELS_MONO : NUM_CHANNELS_STEREO; + } else { + if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) || + !(num_outs = uac_mixer_unit_bNrChannels(desc))) { + usb_audio_err(state->chip, + "invalid MIXER UNIT descriptor %d\n", + unitid); + return -EINVAL; + } } num_ins = 0; @@ -1624,9 +1866,14 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, int och, ich_has_controls = 0; for (och = 0; och < num_outs; och++) { - __u8 *c = uac_mixer_unit_bmControls(desc, - state->mixer->protocol); + __u8 *c = NULL; + if (state->mixer->protocol == UAC_VERSION_3) + c = + &(badd_baiof_mu_desc.bmMixerControls); + else + c = uac_mixer_unit_bmControls(desc, + state->mixer->protocol); if (check_matrix_bitmap(c, ich, och, num_outs)) { ich_has_controls = 1; break; @@ -2134,16 +2381,28 @@ static int parse_audio_unit(struct mixer_build *state, int unitid) case UAC_MIXER_UNIT: return parse_audio_mixer_unit(state, unitid, p1); case UAC_SELECTOR_UNIT: + /* UAC3_MIXER_UNIT_V3 has the same value */ case UAC2_CLOCK_SELECTOR: - return parse_audio_selector_unit(state, unitid, p1); + /* UAC3_CLOCK_SOURCE has the same value */ + if (state->mixer->protocol == UAC_VERSION_3 && + p1[2] == UAC3_CLOCK_SOURCE) + return 0; /* NOP */ + else if (state->mixer->protocol == UAC_VERSION_3 + && p1[2] == UAC3_MIXER_UNIT_V3) + return parse_audio_mixer_unit(state, unitid, p1); + else + return parse_audio_selector_unit(state, unitid, p1); case UAC_FEATURE_UNIT: return parse_audio_feature_unit(state, unitid, p1); case UAC1_PROCESSING_UNIT: /* UAC2_EFFECT_UNIT has the same value */ + /* UAC3_FEATURE_UNIT_V3 has the same value */ if (state->mixer->protocol == UAC_VERSION_1) return parse_audio_processing_unit(state, unitid, p1); - else + else if (state->mixer->protocol == UAC_VERSION_2) return 0; /* FIXME - effect units not implemented yet */ + else + return parse_audio_feature_unit(state, unitid, p1); case UAC1_EXTENSION_UNIT: /* UAC2_PROCESSING_UNIT_V2 has the same value */ if (state->mixer->protocol == UAC_VERSION_1) @@ -2178,6 +2437,23 @@ static int snd_usb_mixer_dev_free(struct snd_device *device) return 0; } +static int make_out_term(struct mixer_build state, int wTerminalType) +{ + struct uac3_output_terminal_descriptor *desc = NULL; + + if (wTerminalType == UAC_TERMINAL_STREAMING) + desc = &badd_baif_out_term_desc; + else { + desc = &badd_baof_out_term_desc; + desc->wTerminalType = wTerminalType; + } + set_bit(desc->bTerminalID, state.unitbitmap); + state.oterm.id = desc->bTerminalID; + state.oterm.type = desc->wTerminalType; + state.oterm.name = desc->wTerminalDescrStr; + return parse_audio_unit(&state, desc->bSourceID); +} + /* * create mixer controls * @@ -2186,9 +2462,8 @@ static int snd_usb_mixer_dev_free(struct snd_device *device) static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) { struct mixer_build state; - int err; + int err = -EINVAL; const struct usbmix_ctl_map *map; - void *p; memset(&state, 0, sizeof(state)); state.chip = mixer->chip; @@ -2206,44 +2481,108 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) } } - p = NULL; - while ((p = snd_usb_find_csint_desc(mixer->hostif->extra, - mixer->hostif->extralen, - p, UAC_OUTPUT_TERMINAL)) != NULL) { - if (mixer->protocol == UAC_VERSION_1) { - struct uac1_output_terminal_descriptor *desc = p; - - if (desc->bLength < sizeof(*desc)) - continue; /* invalid descriptor? */ - /* mark terminal ID as visited */ - set_bit(desc->bTerminalID, state.unitbitmap); - state.oterm.id = desc->bTerminalID; - state.oterm.type = le16_to_cpu(desc->wTerminalType); - state.oterm.name = desc->iTerminal; - err = parse_audio_unit(&state, desc->bSourceID); + if (mixer->protocol == UAC_VERSION_3) { + struct usb_interface *usb_iface = + usb_ifnum_to_if(mixer->chip->dev, + get_iface_desc(mixer->hostif)->bInterfaceNumber); + struct usb_interface_assoc_descriptor *assoc = + usb_iface->intf_assoc; + + switch (assoc->bFunctionSubClass) { + case PROF_GENERIC_IO: { + if (assoc->bInterfaceCount == 0x02) { + if (get_endpoint(mixer->hostif, + 0)->bEndpointAddress | USB_DIR_IN) + err = make_out_term(state, + UAC_TERMINAL_STREAMING); + else + err = make_out_term(state, + UAC_OUTPUT_TERMINAL_UNDEFINED); + } else { + err = make_out_term(state, + UAC_OUTPUT_TERMINAL_UNDEFINED); + if (err < 0 && err != -EINVAL) + return err; + err = make_out_term(state, + UAC_TERMINAL_STREAMING); + } + break; + } + + case PROF_HEADPHONE: + err = make_out_term(state, + UAC_OUTPUT_TERMINAL_HEADPHONES); + break; + case PROF_SPEAKER: + err = make_out_term(state, UAC_OUTPUT_TERMINAL_SPEAKER); + break; + case PROF_MICROPHONE: + err = make_out_term(state, UAC_TERMINAL_STREAMING); + break; + case PROF_HEADSET: + case PROF_HEADSET_ADAPTER: + err = make_out_term(state, UAC_BIDIR_TERMINAL_HEADSET); if (err < 0 && err != -EINVAL) return err; - } else { /* UAC_VERSION_2 */ - struct uac2_output_terminal_descriptor *desc = p; - - if (desc->bLength < sizeof(*desc)) - continue; /* invalid descriptor? */ - /* mark terminal ID as visited */ - set_bit(desc->bTerminalID, state.unitbitmap); - state.oterm.id = desc->bTerminalID; - state.oterm.type = le16_to_cpu(desc->wTerminalType); - state.oterm.name = desc->iTerminal; - err = parse_audio_unit(&state, desc->bSourceID); + err = make_out_term(state, UAC_TERMINAL_STREAMING); + break; + case PROF_SPEAKERPHONE: + err = make_out_term(state, + UAC_BIDIR_TERMINAL_SPEAKERPHONE); if (err < 0 && err != -EINVAL) return err; + err = make_out_term(state, UAC_TERMINAL_STREAMING); + break; + } + if (err < 0 && err != -EINVAL) + return err; + } else { + void *p; + + p = NULL; + while ((p = snd_usb_find_csint_desc(mixer->hostif->extra, + mixer->hostif->extralen, p, + UAC_OUTPUT_TERMINAL)) != NULL) { + if (mixer->protocol == UAC_VERSION_1) { + struct uac1_output_terminal_descriptor *desc = + p; + + if (desc->bLength < sizeof(*desc)) + continue; /* invalid descriptor? */ + /* mark terminal ID as visited */ + set_bit(desc->bTerminalID, state.unitbitmap); + state.oterm.id = desc->bTerminalID; + state.oterm.type = + le16_to_cpu(desc->wTerminalType); + state.oterm.name = desc->iTerminal; + err = parse_audio_unit(&state, desc->bSourceID); + if (err < 0 && err != -EINVAL) + return err; + } else { /* UAC_VERSION_2 */ + struct uac2_output_terminal_descriptor *desc = + p; + + if (desc->bLength < sizeof(*desc)) + continue; /* invalid descriptor? */ + /* mark terminal ID as visited */ + set_bit(desc->bTerminalID, state.unitbitmap); + state.oterm.id = desc->bTerminalID; + state.oterm.type = + le16_to_cpu(desc->wTerminalType); + state.oterm.name = desc->iTerminal; + err = parse_audio_unit(&state, desc->bSourceID); + if (err < 0 && err != -EINVAL) + return err; - /* - * For UAC2, use the same approach to also add the - * clock selectors - */ - err = parse_audio_unit(&state, desc->bCSourceID); - if (err < 0 && err != -EINVAL) - return err; + /* + * For UAC2, use the same approach to also add + * the clock selectors + */ + err = parse_audio_unit(&state, + desc->bCSourceID); + if (err < 0 && err != -EINVAL) + return err; + } } } @@ -2478,6 +2817,9 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, case UAC_VERSION_2: mixer->protocol = UAC_VERSION_2; break; + case UAC_VERSION_3: + mixer->protocol = UAC_VERSION_3; + break; } if ((err = snd_usb_mixer_controls(mixer)) < 0 || diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 98b2c28ae46b..2bb503576134 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -20,6 +20,7 @@ #include <linux/usb.h> #include <linux/usb/audio.h> #include <linux/usb/audio-v2.h> +#include <linux/usb/audio-v3.h> #include <sound/core.h> #include <sound/pcm.h> @@ -284,8 +285,6 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits, 0 /* terminator */ }; struct snd_pcm_chmap_elem *chmap; - const unsigned int *maps; - int c; if (channels > ARRAY_SIZE(chmap->map)) return NULL; @@ -294,27 +293,42 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits, if (!chmap) return NULL; - maps = protocol == UAC_VERSION_2 ? uac2_maps : uac1_maps; chmap->channels = channels; - c = 0; - if (bits) { - for (; bits && *maps; maps++, bits >>= 1) - if (bits & 1) - chmap->map[c++] = *maps; + if (protocol == UAC_VERSION_3) { + switch (channels) { + case 1: + chmap->map[0] = SNDRV_CHMAP_MONO; + break; + case 2: + chmap->map[0] = SNDRV_CHMAP_FL; + chmap->map[1] = SNDRV_CHMAP_FR; + break; + } } else { - /* If we're missing wChannelConfig, then guess something - to make sure the channel map is not skipped entirely */ - if (channels == 1) - chmap->map[c++] = SNDRV_CHMAP_MONO; - else - for (; c < channels && *maps; maps++) - chmap->map[c++] = *maps; + int c = 0; + const unsigned int *maps = + protocol == UAC_VERSION_2 ? uac2_maps : uac1_maps; + + if (bits) { + for (; bits && *maps; maps++, bits >>= 1) + if (bits & 1) + chmap->map[c++] = *maps; + } else { + /* + * If we're missing wChannelConfig, then guess something + * to make sure the channel map is not skipped entirely + */ + if (channels == 1) + chmap->map[c++] = SNDRV_CHMAP_MONO; + else + for (; c < channels && *maps; maps++) + chmap->map[c++] = *maps; + } + for (; c < channels; c++) + chmap->map[c] = SNDRV_CHMAP_UNKNOWN; } - for (; c < channels; c++) - chmap->map[c] = SNDRV_CHMAP_UNKNOWN; - return chmap; } @@ -411,6 +425,9 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, struct usb_interface_descriptor *altsd = get_iface_desc(alts); int attributes = 0; + if (protocol == UAC_VERSION_3) + return 0; + csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT); /* Creamware Noah has this descriptor after the 2nd endpoint */ @@ -631,6 +648,39 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) iface_no, altno, as->bTerminalLink); continue; } + + case UAC_VERSION_3: { + int wMaxPacketSize; + + format = UAC_FORMAT_TYPE_I_PCM; + clock = BADD_CLOCK_SOURCE; + wMaxPacketSize = le16_to_cpu(get_endpoint(alts, 0) + ->wMaxPacketSize); + switch (wMaxPacketSize) { + case BADD_MAXPSIZE_SYNC_MONO_16: + case BADD_MAXPSIZE_SYNC_MONO_24: + case BADD_MAXPSIZE_ASYNC_MONO_16: + case BADD_MAXPSIZE_ASYNC_MONO_24: { + num_channels = NUM_CHANNELS_MONO; + chconfig = BADD_CH_CONFIG_MONO; + goto populate_fp; + } + + case BADD_MAXPSIZE_SYNC_STEREO_16: + case BADD_MAXPSIZE_SYNC_STEREO_24: + case BADD_MAXPSIZE_ASYNC_STEREO_16: + case BADD_MAXPSIZE_ASYNC_STEREO_24: { + num_channels = NUM_CHANNELS_STEREO; + chconfig = BADD_CH_CONFIG_STEREO; + goto populate_fp; + } + default: + dev_err(&dev->dev, + "%u:%d: invalid wMaxPacketSize\n", + iface_no, altno); + continue; + } + } } /* get format type */ @@ -664,6 +714,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) fp->maxpacksize * 2) continue; +populate_fp: fp = kzalloc(sizeof(*fp), GFP_KERNEL); if (! fp) { dev_err(&dev->dev, "cannot malloc\n"); diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c index a495a7f6cb22..14ea2239fe11 100644 --- a/sound/usb/usb_audio_qmi_svc.c +++ b/sound/usb/usb_audio_qmi_svc.c @@ -28,6 +28,7 @@ #include <linux/iommu.h> #include <linux/qcom_iommu.h> #include <linux/platform_device.h> +#include <linux/usb/audio-v3.h> #include "usbaudio.h" #include "card.h" @@ -428,12 +429,14 @@ static int prepare_qmi_response(struct snd_usb_substream *subs, protocol = altsd->bInterfaceProtocol; /* get format type */ - fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, - UAC_FORMAT_TYPE); - if (!fmt) { - pr_err("%s: %u:%d : no UAC_FORMAT_TYPE desc\n", __func__, - subs->interface, subs->altset_idx); - goto err; + if (protocol != UAC_VERSION_3) { + fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, + UAC_FORMAT_TYPE); + if (!fmt) { + pr_err("%s: %u:%d : no UAC_FORMAT_TYPE desc\n", + __func__, subs->interface, subs->altset_idx); + goto err; + } } if (!uadev[card_num].ctrl_intf) { @@ -441,12 +444,15 @@ static int prepare_qmi_response(struct snd_usb_substream *subs, goto err; } - hdr_ptr = snd_usb_find_csint_desc(uadev[card_num].ctrl_intf->extra, - uadev[card_num].ctrl_intf->extralen, - NULL, UAC_HEADER); - if (!hdr_ptr) { - pr_err("%s: no UAC_HEADER desc\n", __func__); - goto err; + if (protocol != UAC_VERSION_3) { + hdr_ptr = snd_usb_find_csint_desc( + uadev[card_num].ctrl_intf->extra, + uadev[card_num].ctrl_intf->extralen, + NULL, UAC_HEADER); + if (!hdr_ptr) { + pr_err("%s: no UAC_HEADER desc\n", __func__); + goto err; + } } if (protocol == UAC_VERSION_1) { @@ -474,6 +480,25 @@ static int prepare_qmi_response(struct snd_usb_substream *subs, resp->usb_audio_spec_revision = ((struct uac2_ac_header_descriptor *)hdr_ptr)->bcdADC; resp->usb_audio_spec_revision_valid = 1; + } else if (protocol == UAC_VERSION_3) { + switch (le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize)) { + case BADD_MAXPSIZE_SYNC_MONO_16: + case BADD_MAXPSIZE_SYNC_STEREO_16: + case BADD_MAXPSIZE_ASYNC_MONO_16: + case BADD_MAXPSIZE_ASYNC_STEREO_16: { + resp->usb_audio_subslot_size = SUBSLOTSIZE_16_BIT; + break; + } + + case BADD_MAXPSIZE_SYNC_MONO_24: + case BADD_MAXPSIZE_SYNC_STEREO_24: + case BADD_MAXPSIZE_ASYNC_MONO_24: + case BADD_MAXPSIZE_ASYNC_STEREO_24: { + resp->usb_audio_subslot_size = SUBSLOTSIZE_24_BIT; + break; + } + } + resp->usb_audio_subslot_size_valid = 1; } else { pr_err("%s: unknown protocol version %x\n", __func__, protocol); goto err; diff --git a/tools/include/linux/log2.h b/tools/include/linux/log2.h index 41446668ccce..d5677d39c1e4 100644 --- a/tools/include/linux/log2.h +++ b/tools/include/linux/log2.h @@ -13,12 +13,6 @@ #define _TOOLS_LINUX_LOG2_H /* - * deal with unrepresentable constant logarithms - */ -extern __attribute__((const, noreturn)) -int ____ilog2_NaN(void); - -/* * non-constant log of base 2 calculators * - the arch may override these in asm/bitops.h if they can be implemented * more efficiently than using fls() and fls64() @@ -78,7 +72,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n) #define ilog2(n) \ ( \ __builtin_constant_p(n) ? ( \ - (n) < 1 ? ____ilog2_NaN() : \ + (n) < 2 ? 0 : \ (n) & (1ULL << 63) ? 63 : \ (n) & (1ULL << 62) ? 62 : \ (n) & (1ULL << 61) ? 61 : \ @@ -141,10 +135,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n) (n) & (1ULL << 4) ? 4 : \ (n) & (1ULL << 3) ? 3 : \ (n) & (1ULL << 2) ? 2 : \ - (n) & (1ULL << 1) ? 1 : \ - (n) & (1ULL << 0) ? 0 : \ - ____ilog2_NaN() \ - ) : \ + 1 ) : \ (sizeof(n) <= 4) ? \ __ilog2_u32(n) : \ __ilog2_u64(n) \ |