Merge "diag: Prevent NULL pointer dereference while closing logging process" into msm-4.9
diff --git a/Documentation/devicetree/bindings/arm/arch_timer.txt b/Documentation/devicetree/bindings/arm/arch_timer.txt
index ef5fbe9..ad440a2 100644
--- a/Documentation/devicetree/bindings/arm/arch_timer.txt
+++ b/Documentation/devicetree/bindings/arm/arch_timer.txt
@@ -38,6 +38,11 @@
architecturally-defined reset values. Only supported for 32-bit
systems which follow the ARMv7 architected reset values.
+- arm,no-tick-in-suspend : The main counter does not tick when the system is in
+ low-power system suspend on some SoCs. This behavior does not match the
+ Architecture Reference Manual's specification that the system counter "must
+ be implemented in an always-on power domain."
+
Example:
diff --git a/Documentation/devicetree/bindings/arm/msm/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt
index baae281..bf93a2a 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm.txt
@@ -110,6 +110,9 @@
- MDMCALIFORNIUM
compatible = "qcom,mdmcalifornium"
+- SDXPOORWILLS
+ compatible = "qcom,sdxpoorwills"
+
- VPIPA
compatible = "qcom,msmvpipa"
@@ -302,3 +305,4 @@
compatible = "qcom,mdmcalifornium-mtp"
compatible = "qcom,apq8009-cdp"
compatible = "qcom,apq8009-mtp"
+compatible = "qcom,sdxpoorwills-rumi"
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_bus_adhoc.txt b/Documentation/devicetree/bindings/arm/msm/msm_bus_adhoc.txt
index 96e42c5..6bf6a57 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm_bus_adhoc.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm_bus_adhoc.txt
@@ -218,7 +218,16 @@
master-id, slave-id, arbitrated bandwidth
in KBps, instantaneous bandwidth in KBps
-Example:
+The following are optional properties for client's device nodes:
+
+- qcom,msm-bus,alc-voter: Boolean alc_voter flag to indicate that client
+ will vote as an Active Latency Client.
+- qcom,msm-bus,vectors-alc: Arrays of unsigned integers representing:
+ first access latency, idle time in ns, this
+ property is required if qcom,msm-bus,alc-voter
+ is present.
+
+Example for default client:
qcom,msm-bus,name = "client-name";
qcom,msm-bus,num-cases = <3>;
@@ -229,3 +238,12 @@
<22 512 320000 3200000>, <26 512 3200000 3200000>,
<22 512 160000 1600000>, <26 512 1600000 1600000>;
+Example for ALC client:
+
+ qcom,msm-bus,name = "client-name";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,alc-voter;
+ qcom,msm-bus,vectors-alc =
+ <0 0>,
+ <500 1600>;
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
index 964fea6..2347477 100644
--- a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
@@ -25,9 +25,8 @@
Value type: <stringlist>
Definition: Address names. Must be "osm_l3_base", "osm_pwrcl_base",
"osm_perfcl_base", "l3_pll", "pwrcl_pll", "perfcl_pll",
- "l3_sequencer", "pwrcl_sequencer", "perfcl_sequencer" or
- "apps_itm_ctl". Optionally, "l3_efuse", "pwrcl_efuse"
- "perfcl_efuse".
+ "l3_sequencer", "pwrcl_sequencer", "perfcl_sequencer".
+ Optionally, "l3_efuse", "pwrcl_efuse", "perfcl_efuse".
Must be specified in the same order as the corresponding
addresses are specified in the reg property.
@@ -350,12 +349,11 @@
<0x178b0000 0x1000>,
<0x17d42400 0x0c00>,
<0x17d44400 0x0c00>,
- <0x17d46c00 0x0c00>,
- <0x17810090 0x8>;
+ <0x17d46c00 0x0c00>;
reg-names = "osm_l3_base", "osm_pwrcl_base", "osm_perfcl_base",
"l3_pll", "pwrcl_pll", "perfcl_pll",
"l3_sequencer", "pwrcl_sequencer",
- "perfcl_sequencer", "apps_itm_ctl";
+ "perfcl_sequencer";
vdd-l3-supply = <&apc0_l3_vreg>;
vdd-pwrcl-supply = <&apc0_pwrcl_vreg>;
diff --git a/Documentation/devicetree/bindings/arm/msm/spss_utils.txt b/Documentation/devicetree/bindings/arm/msm/spss_utils.txt
new file mode 100644
index 0000000..d325574
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/spss_utils.txt
@@ -0,0 +1,35 @@
+Qualcomm Technologies, Inc. Secure Processor SubSystem Utilities (spss_utils)
+
+The Secure Processor SubSystem (SPSS) is a dedicated subsystem for security.
+It has its own CPU, memories, and cryptographic engine.
+It shall provide cryptographic services to other subsystems.
+The SPSS firmware is loaded by PIL driver.
+The communication with SPSS is done via spcom driver, using glink.
+
+The spss_utils driver selects the SPSS firmware file,
+according to a dedicated fuse and the platform HW version.
+
+Required properties:
+-compatible : should be "qcom,spss_utils"
+-qcom,spss-fuse1-addr: fuse1 register physical address
+-qcom,spss-fuse1-bit: fuse1 relevant bit
+-qcom,spss-fuse2-addr: fuse2 register physical address
+-qcom,spss-fuse2-bit: fuse2 relevant bit
+-qcom,spss-dev-firmware-name: dev firmware file name
+-qcom,spss-test-firmware-name: test firmware file name
+-qcom,spss-prod-firmware-name: production firmware file name
+-qcom,spss-debug-reg-addr: debug register physical address
+
+Example:
+ qcom,spss_utils {
+ compatible = "qcom,spss-utils";
+
+ qcom,spss-fuse1-addr = <0x007841c4>;
+ qcom,spss-fuse1-bit = <27>;
+ qcom,spss-fuse2-addr = <0x007841c4>;
+ qcom,spss-fuse2-bit = <26>;
+ qcom,spss-dev-firmware-name = "spss1d"; /* 8 chars max */
+ qcom,spss-test-firmware-name = "spss1t"; /* 8 chars max */
+ qcom,spss-prod-firmware-name = "spss1p"; /* 8 chars max */
+ qcom,spss-debug-reg-addr = <0x01886020>;
+ };
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
index b95696d..4f7ae75 100644
--- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
+++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
@@ -28,6 +28,8 @@
Required properties:
- compatible: value must be either:
* allwinner,sun5i-a13-tcon
+ * allwinner,sun6i-a31-tcon
+ * allwinner,sun6i-a31s-tcon
* allwinner,sun8i-a33-tcon
- reg: base address and size of memory-mapped region
- interrupts: interrupt associated to this IP
@@ -50,7 +52,7 @@
second the block connected to the TCON channel 1 (usually the TV
encoder)
-On the A13, there is one more clock required:
+On SoCs other than the A33, there is one more clock required:
- 'tcon-ch1': The clock driving the TCON channel 1
DRC
@@ -87,6 +89,7 @@
Required properties:
- compatible: value must be one of:
* allwinner,sun5i-a13-display-backend
+ * allwinner,sun6i-a31-display-backend
* allwinner,sun8i-a33-display-backend
- reg: base address and size of the memory-mapped region.
- clocks: phandles to the clocks feeding the frontend and backend
@@ -117,6 +120,7 @@
Required properties:
- compatible: value must be one of:
* allwinner,sun5i-a13-display-frontend
+ * allwinner,sun6i-a31-display-frontend
* allwinner,sun8i-a33-display-frontend
- reg: base address and size of the memory-mapped region.
- interrupts: interrupt associated to this IP
@@ -142,6 +146,8 @@
Required properties:
- compatible: value must be one of:
* allwinner,sun5i-a13-display-engine
+ * allwinner,sun6i-a31-display-engine
+ * allwinner,sun6i-a31s-display-engine
* allwinner,sun8i-a33-display-engine
- allwinner,pipelines: list of phandle to the display engine
diff --git a/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt b/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt
index af0b903..dfc14f7 100644
--- a/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt
+++ b/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt
@@ -5,7 +5,10 @@
Required properties:
- compatible: Should be "linux,extcon-usb-gpio"
+
+Either one of id-gpio or vbus-gpio must be present. Both can be present as well.
- id-gpio: gpio for USB ID pin. See gpio binding.
+- vbus-gpio: gpio for USB VBUS pin.
Example: Examples of extcon-usb-gpio node in dra7-evm.dts as listed below:
extcon_usb1 {
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
index 0db9970..95e6f6c 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
@@ -4,9 +4,13 @@
Required properties:
- compatible : should be "qcom,sdhci-msm"
+ For SDCC version 5.0.0, MCI registers are removed from SDCC interface
+ and some registers are moved to HC. New compatible string is added to
+ support this change - "qcom,sdhci-msm-v5".
- reg : should contain SDHC, SD Core register map.
- reg-names : indicates various resources passed to driver (via reg proptery) by name.
Required "reg-names" are "hc_mem" and "core_mem"
+ optional ones are "tlmm_mem"
- interrupts : should contain SDHC interrupts.
- interrupt-names : indicates interrupts passed to driver (via interrupts property) by name.
Required "interrupt-names" are "hc_irq" and "pwr_irq".
@@ -25,12 +29,49 @@
1, 4 and 8.
- qcom,nonremovable - specifies whether the card in slot is
hot pluggable or hard wired.
+ - qcom,nonhotplug - specifies the card in slot is not hot pluggable.
+ if card lost or removed manually at runtime, don't retry
+ to redetect it until next reboot probe.
- qcom,bus-speed-mode - specifies supported bus speed modes by host.
The supported bus speed modes are :
"HS200_1p8v" - indicates that host can support HS200 at 1.8v.
"HS200_1p2v" - indicates that host can support HS200 at 1.2v.
"DDR_1p8v" - indicates that host can support DDR mode at 1.8v.
"DDR_1p2v" - indicates that host can support DDR mode at 1.2v.
+ - qcom,devfreq,freq-table - specifies supported frequencies for clock scaling.
+ Clock scaling logic shall toggle between these frequencies based
+ on card load. In case the defined frequencies are over or below
+ the supported card frequencies, they will be overridden
+ during card init. In case this entry is not supplied,
+ the driver will construct one based on the card
+ supported max and min frequencies.
+ The frequencies must be ordered from lowest to highest.
+ - qcom,pm-qos-irq-type - the PM QoS request type to be used for IRQ voting.
+ Can be either "affine_cores" or "affine_irq". If not specified, will default
+ to "affine_cores". Use "affine_irq" setting in case an IRQ balancer is active,
+ and IRQ affinity changes during runtime.
+ - qcom,pm-qos-irq-cpu - specifies the CPU for which IRQ voting shall be done.
+ If "affine_cores" was specified for property 'qcom,pm-qos-irq-type'
+ then this property must be defined, and is not relevant otherwise.
+ - qcom,pm-qos-irq-latency - a tuple defining two latency values with which
+ PM QoS IRQ voting shall be done. The first value is the latecy to be used
+ when load is high (performance mode) and the second is for low loads
+ (power saving mode).
+ - qcom,pm-qos-cpu-groups - defines cpu groups mapping.
+ Each cell represnets a group, which is a cpu bitmask defining which cpus belong
+ to that group.
+ - qcom,pm-qos-<mode>-latency-us - where <mode> is either "cmdq" or "legacy".
+ An array of latency value tuples, each tuple corresponding to a cpu group in the order
+ defined in property 'qcom,pm-qos-cpu-groups'. The first value is the latecy to be used
+ when load is high (performance mode) and the second is for low loads
+ (power saving mode). These values will be used for cpu group voting for
+ command-queueing mode or legacy respectively.
+ - qcom,core_3_0v_support: an optional property that is used to fake
+ 3.0V support for SDIO devices.
+ - qcom,scaling-lower-bus-speed-mode: specifies the lower bus speed mode to be used
+ during clock scaling. If this property is not
+ defined, then it falls back to the default HS
+ bus speed mode to maintain backward compatibility.
In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage).
- qcom,<supply>-always-on - specifies whether supply should be kept "on" always.
@@ -47,6 +88,17 @@
Tlmm pins are specified as <clk cmd data> and starting with eMMC5.0 as
<clk cmd data rclk>
+ - Refer to "Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt"
+ for following optional properties:
+ - pinctrl-names
+ - pinctrl-0, pinctrl-1,.. pinctrl-n
+
+ - qcom,large-address-bus - specifies whether the soc is capable of
+ supporting larger than 32 bit address bus width.
+
+ - qcom,wakeup-on-idle: if configured, the mmcqd thread will call
+ set_wake_up_idle(), thereby voting for it to be called on idle CPUs.
+
Example:
aliases {
@@ -70,10 +122,20 @@
qcom,vdd-io-voltage-level = <1800000 2950000>;
qcom,vdd-io-current-level = <6 22000>;
+ qcom,devfreq,freq-table = <52000000 200000000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on>;
+ pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_on &sdc1_data_on>;
+
+
qcom,bus-width = <4>;
qcom,nonremovable;
+ qcom,large-address-bus;
qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+ qcom,scaling-lower-bus-speed-mode = "DDR52";
+
gpios = <&msmgpio 40 0>, /* CLK */
<&msmgpio 39 0>, /* CMD */
<&msmgpio 38 0>, /* DATA0 */
@@ -81,4 +143,16 @@
<&msmgpio 36 0>, /* DATA2 */
<&msmgpio 35 0>; /* DATA3 */
qcom,gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
+
+ qcom,pm-qos-irq-type = "affine_cores";
+ qcom,pm-qos-irq-cpu = <0>;
+ qcom,pm-qos-irq-latency = <500 100>;
+ qcom,pm-qos-cpu-groups = <0x03 0x0c>;
+ qcom,pm-qos-cmdq-latency-us = <50 100>, <50 100>;
+ qcom,pm-qos-legacy-latency-us = <50 100>, <50 100>;
+ };
+
+ sdhc_2: qcom,sdhc@f98a4900 {
+ qcom,pm-qos-irq-type = "affine_irq";
+ qcom,pm-qos-irq-latency = <120 200>;
};
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sdxpoorwill-pinctrl b/Documentation/devicetree/bindings/pinctrl/qcom,sdxpoorwill-pinctrl
new file mode 100644
index 0000000..9a69084
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,sdxpoorwill-pinctrl
@@ -0,0 +1,186 @@
+Qualcomm Technologies, Inc. SDXPOORWILLS TLMM block
+
+This binding describes the Top Level Mode Multiplexer block found in the
+SDXPOORWILLS platform.
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be "qcom,sdxpoorwills-pinctrl"
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: the base address and size of the TLMM register space.
+
+- interrupts:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: should specify the TLMM summary IRQ.
+
+- interrupt-controller:
+ Usage: required
+ Value type: <none>
+ Definition: identifies this node as an interrupt controller
+
+- #interrupt-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: must be 2. Specifying the pin number and flags, as defined
+ in <dt-bindings/interrupt-controller/irq.h>
+
+- gpio-controller:
+ Usage: required
+ Value type: <none>
+ Definition: identifies this node as a gpio controller
+
+- #gpio-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: must be 2. Specifying the pin number and flags, as defined
+ in <dt-bindings/gpio/gpio.h>
+
+Please refer to ../gpio/gpio.txt and ../interrupt-controller/interrupts.txt for
+a general description of GPIO and interrupt bindings.
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+The pin configuration nodes act as a container for an arbitrary number of
+subnodes. Each of these subnodes represents some desired configuration for a
+pin, a group, or a list of pins or groups. This configuration can include the
+mux function to select on those pin(s)/group(s), and various pin configuration
+parameters, such as pull-up, drive strength, etc.
+
+
+PIN CONFIGURATION NODES:
+
+The name of each subnode is not important; all subnodes should be enumerated
+and processed purely based on their content.
+
+Each subnode only affects those parameters that are explicitly listed. In
+other words, a subnode that lists a mux function but no pin configuration
+parameters implies no information about any pin configuration parameters.
+Similarly, a pin subnode that describes a pullup parameter implies no
+information about e.g. the mux function.
+
+
+The following generic properties as defined in pinctrl-bindings.txt are valid
+to specify in a pin configuration subnode:
+
+- pins:
+ Usage: required
+ Value type: <string-array>
+ Definition: List of gpio pins affected by the properties specified in
+ this subnode.
+
+ Valid pins are:
+ gpio0-gpio149
+ Supports mux, bias and drive-strength
+
+ sdc1_clk, sdc1_cmd, sdc1_data sdc2_clk, sdc2_cmd,
+ sdc2_data sdc1_rclk
+ Supports bias and drive-strength
+
+- function:
+ Usage: required
+ Value type: <string>
+ Definition: Specify the alternative function to be configured for the
+ specified pins. Functions are only valid for gpio pins.
+ Valid values are:
+
+ blsp_uart1, blsp_spi1, blsp_i2c1, blsp_uim1, atest_tsens,
+ bimc_dte1, dac_calib0, blsp_spi8, blsp_uart8, blsp_uim8,
+ qdss_cti_trig_out_b, bimc_dte0, dac_calib1, qdss_cti_trig_in_b,
+ dac_calib2, atest_tsens2, atest_usb1, blsp_spi10, blsp_uart10,
+ blsp_uim10, atest_bbrx1, atest_usb13, atest_bbrx0, atest_usb12,
+ mdp_vsync, edp_lcd, blsp_i2c10, atest_gpsadc1, atest_usb11,
+ atest_gpsadc0, edp_hot, atest_usb10, m_voc, dac_gpio, atest_char,
+ cam_mclk, pll_bypassnl, qdss_stm7, blsp_i2c8, qdss_tracedata_b,
+ pll_reset, qdss_stm6, qdss_stm5, qdss_stm4, atest_usb2, cci_i2c,
+ qdss_stm3, dac_calib3, atest_usb23, atest_char3, dac_calib4,
+ qdss_stm2, atest_usb22, atest_char2, qdss_stm1, dac_calib5,
+ atest_usb21, atest_char1, dbg_out, qdss_stm0, dac_calib6,
+ atest_usb20, atest_char0, dac_calib10, qdss_stm10,
+ qdss_cti_trig_in_a, cci_timer4, blsp_spi6, blsp_uart6, blsp_uim6,
+ blsp2_spi, qdss_stm9, qdss_cti_trig_out_a, dac_calib11,
+ qdss_stm8, cci_timer0, qdss_stm13, dac_calib7, cci_timer1,
+ qdss_stm12, dac_calib8, cci_timer2, blsp1_spi, qdss_stm11,
+ dac_calib9, cci_timer3, cci_async, dac_calib12, blsp_i2c6,
+ qdss_tracectl_a, dac_calib13, qdss_traceclk_a, dac_calib14,
+ dac_calib15, hdmi_rcv, dac_calib16, hdmi_cec, pwr_modem,
+ dac_calib17, hdmi_ddc, pwr_nav, dac_calib18, pwr_crypto,
+ dac_calib19, hdmi_hot, dac_calib20, dac_calib21, pci_e0,
+ dac_calib22, dac_calib23, dac_calib24, tsif1_sync, dac_calib25,
+ sd_write, tsif1_error, blsp_spi2, blsp_uart2, blsp_uim2,
+ qdss_cti, blsp_i2c2, blsp_spi3, blsp_uart3, blsp_uim3, blsp_i2c3,
+ uim3, blsp_spi9, blsp_uart9, blsp_uim9, blsp10_spi, blsp_i2c9,
+ blsp_spi7, blsp_uart7, blsp_uim7, qdss_tracedata_a, blsp_i2c7,
+ qua_mi2s, gcc_gp1_clk_a, ssc_irq, uim4, blsp_spi11, blsp_uart11,
+ blsp_uim11, gcc_gp2_clk_a, gcc_gp3_clk_a, blsp_i2c11, cri_trng0,
+ cri_trng1, cri_trng, qdss_stm18, pri_mi2s, qdss_stm17, blsp_spi4,
+ blsp_uart4, blsp_uim4, qdss_stm16, qdss_stm15, blsp_i2c4,
+ qdss_stm14, dac_calib26, spkr_i2s, audio_ref, lpass_slimbus,
+ isense_dbg, tsense_pwm1, tsense_pwm2, btfm_slimbus, ter_mi2s,
+ qdss_stm22, qdss_stm21, qdss_stm20, qdss_stm19, gcc_gp1_clk_b,
+ sec_mi2s, blsp_spi5, blsp_uart5, blsp_uim5, gcc_gp2_clk_b,
+ gcc_gp3_clk_b, blsp_i2c5, blsp_spi12, blsp_uart12, blsp_uim12,
+ qdss_stm25, qdss_stm31, blsp_i2c12, qdss_stm30, qdss_stm29,
+ tsif1_clk, qdss_stm28, tsif1_en, tsif1_data, sdc4_cmd, qdss_stm27,
+ qdss_traceclk_b, tsif2_error, sdc43, vfr_1, qdss_stm26, tsif2_clk,
+ sdc4_clk, qdss_stm24, tsif2_en, sdc42, qdss_stm23, qdss_tracectl_b,
+ sd_card, tsif2_data, sdc41, tsif2_sync, sdc40, mdp_vsync_p_b,
+ ldo_en, mdp_vsync_s_b, ldo_update, blsp11_uart_tx_b, blsp11_uart_rx_b,
+ blsp11_i2c_sda_b, prng_rosc, blsp11_i2c_scl_b, uim2, uim1, uim_batt,
+ pci_e2, pa_indicator, adsp_ext, ddr_bist, qdss_tracedata_11,
+ qdss_tracedata_12, modem_tsync, nav_dr, nav_pps, pci_e1, gsm_tx,
+ qspi_cs, ssbi2, ssbi1, mss_lte, qspi_clk, qspi0, qspi1, qspi2, qspi3,
+ gpio
+
+- bias-disable:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins should be configued as no pull.
+
+- bias-pull-down:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins should be configued as pull down.
+
+- bias-pull-up:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins should be configued as pull up.
+
+- output-high:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins are configured in output mode, driven
+ high.
+ Not valid for sdc pins.
+
+- output-low:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins are configured in output mode, driven
+ low.
+ Not valid for sdc pins.
+
+- drive-strength:
+ Usage: optional
+ Value type: <u32>
+ Definition: Selects the drive strength for the specified pins, in mA.
+ Valid values are: 2, 4, 6, 8, 10, 12, 14 and 16
+
+Example:
+
+ tlmm: pinctrl@03900000 {
+ compatible = "qcom,sdxpoorwills-pinctrl";
+ reg = <0x03900000 0x300000>;
+ interrupts = <0 212 0>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
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 addb0a6..12d32ec 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 @@
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/soc/qcom/dcc.txt b/Documentation/devicetree/bindings/soc/qcom/dcc.txt
index 0fd4e15..8a9761c 100644
--- a/Documentation/devicetree/bindings/soc/qcom/dcc.txt
+++ b/Documentation/devicetree/bindings/soc/qcom/dcc.txt
@@ -14,6 +14,11 @@
of the component.
- reg-names : names corresponding to each reg property value.
+ dcc-base: Base address for DCC configuration reg
+ dcc-ram-base: Start of HLOS address space in SRAM
+ dcc-xpu-base: Base address for XPU configuration reg
+
+- dcc-ram-offset: Address offset from the start of the SRAM address space.
Optional properties:
diff --git a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb-debug.txt b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb-debug.txt
new file mode 100644
index 0000000..ceac719
--- /dev/null
+++ b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb-debug.txt
@@ -0,0 +1,63 @@
+Qualcomm Technologies, Inc. SPMI Debug Controller (PMIC Arbiter)
+
+The SPMI PMIC Arbiter is found on various QTI chips. It is an SPMI controller
+with wrapping arbitration logic to allow for multiple on-chip devices to control
+a single SPMI master.
+
+The PMIC Arbiter debug bus is present starting at arbiter version 5. It has
+read and write access to all PMIC peripherals regardless of ownership
+configurations. It cannot be used on production devices because it is disabled
+by an eFuse.
+
+See spmi.txt for the generic SPMI controller binding requirements for child
+nodes.
+
+Supported Properties:
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: Must be "qcom,spmi-pmic-arb-debug".
+
+- reg
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: List of address and size pairs. The address of the PMIC
+ arbiter module is required. The address of the debug bus
+ disabling fuse is optional.
+
+- reg-names
+ Usage: required
+ Value type: <stringlist>
+ Definition: Address names. Must include "core" for the PMIC arbiter
+ module and may include "fuse" for the debug bus disabling
+ fuse. The strings must be specified in the same order as
+ the corresponding addresses are specified in the reg
+ property.
+
+- #address-cells
+ Usage: required
+ Value type: <u32>
+ Definition: Must be 2.
+
+- #size-cells
+ Usage: required
+ Value type: <u32>
+ Definition: Must be 0.
+
+- qcom,fuse-disable-bit
+ Usage: required if "fuse" is listed in reg-names property
+ Value type: <u32>
+ Definition: The bit within the fuse register which is set when the debug
+ bus is not available. Supported values are 0 to 31.
+
+Example:
+
+qcom,spmi-debug@6b22000 {
+ compatible = "qcom,spmi-pmic-arb-debug";
+ reg = <0x6b22000 0x60>, <0x7820A8 4>;
+ reg-names = "core", "fuse";
+ qcom,fuse-disable-bit = <12>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+};
diff --git a/Documentation/devicetree/bindings/thermal/qcom-lmh-dcvs.txt b/Documentation/devicetree/bindings/thermal/qcom-lmh-dcvs.txt
index 080d4da..8bead0d 100644
--- a/Documentation/devicetree/bindings/thermal/qcom-lmh-dcvs.txt
+++ b/Documentation/devicetree/bindings/thermal/qcom-lmh-dcvs.txt
@@ -25,12 +25,18 @@
interrupt generated by the LMH DCVSh hardware. LMH
DCVSh hardware will generate this interrupt whenever
it makes a new cpu DCVS decision.
+- qcom,affinity:
+ Usage: Required
+ Value type: <u32>
+ Definition: Should specify the cluster affinity this hardware
+ corresponds to.
Example:
lmh_dcvs0: qcom,limits-dcvs@0 {
compatible = "qcom,msm-hw-limits";
interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+ qcom,affinity = <0>;
};
CPU0: cpu@0 {
diff --git a/Documentation/devicetree/bindings/thermal/tsens.txt b/Documentation/devicetree/bindings/thermal/tsens.txt
index 1065456..3e59c43 100644
--- a/Documentation/devicetree/bindings/thermal/tsens.txt
+++ b/Documentation/devicetree/bindings/thermal/tsens.txt
@@ -21,34 +21,25 @@
The compatible property is used to identify the respective controller to use
for the corresponding SoC.
- reg : offset and length of the TSENS registers with associated property in reg-names
- as "tsens_physical" for TSENS TM physical address region.
+ as "tsens_srot_physical" for TSENS SROT physical address region. TSENS TM
+ physical address region as "tsens_tm_physical".
- reg-names : resource names used for the physical address of the TSENS
- registers. Should be "tsens_physical" for physical address of the TSENS.
+ registers. Should be "tsens_srot_physical" for physical address of the TSENS
+ SROT region and "tsens_tm_physical" for physical address of the TM region.
- interrupts : TSENS interrupt to notify Upper/Lower and Critical temperature threshold.
- interrupt-names: Should be "tsens-upper-lower" for temperature threshold.
Add "tsens-critical" for Critical temperature threshold notification
in addition to "tsens-upper-lower" for 8996 TSENS since
8996 supports Upper/Lower and Critical temperature threshold.
-- qcom,sensors : Total number of available Temperature sensors for TSENS.
-
-Optional properties:
-- qcom,sensor-id : If the flag is present map the TSENS sensors based on the
- remote sensors that are enabled in HW. Ensure the mapping is not
- more than the number of supported sensors.
-- qcom,client-id : If the flag is present use it to identify the SW ID mapping
- used to associate it with the controller and the physical sensor
- mapping within the controller. The physical sensor mapping within
- each controller is done using the qcom,sensor-id property. If the
- property is not present the SW ID mapping with default from 0 to
- total number of supported sensors with each controller instance.
Example:
tsens@fc4a8000 {
compatible = "qcom,msm-tsens";
- reg = <0xfc4a8000 0x2000>;,
- reg-names = "tsens_physical";
+ reg = <0xfc4a8000 0x10>,
+ <0xfc4b8000 0x1ff>;
+ reg-names = "tsens_srot_physical",
+ "tsens_tm_physical";
interrupts = <0 184 0>;
interrupt-names = "tsens-upper-lower";
- qcom,sensors = <11>;
};
diff --git a/Documentation/devicetree/bindings/uio/msm_sharedmem.txt b/Documentation/devicetree/bindings/uio/msm_sharedmem.txt
new file mode 100644
index 0000000..749c6e85
--- /dev/null
+++ b/Documentation/devicetree/bindings/uio/msm_sharedmem.txt
@@ -0,0 +1,18 @@
+msm_sharedmem provides the shared memory addresses for various clients in user-space
+
+Required properties:
+- compatible: Must be "qcom,sharedmem-uio"
+- reg : The address and size of the shared memory. The address/sizes may vary.
+ A reg address of Zero indicates that the shared memory is dynamically
+ allocated using dma_alloc_coherent. A non zero reg address is used
+ directly.
+- reg-names : Indicates various client-names.
+- qcom,client-id : The client id for the QMI clients.
+
+Example:
+ qcom,msm_sharedmem@0dc80000 {
+ compatible = "qcom,sharedmem-uio";
+ reg = <0x0dc80000 0x00180000>,
+ reg-names = "rmtfs";
+ qcom,client-id = <0x00000001>;
+ };
diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt
index 966885c..7790c81 100644
--- a/Documentation/devicetree/bindings/usb/usb-xhci.txt
+++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt
@@ -26,6 +26,7 @@
Optional properties:
- clocks: reference to a clock
- usb3-lpm-capable: determines if platform is USB3 LPM capable
+ - quirk-broken-port-ped: set if the controller has broken port disable mechanism
Example:
usb@f0931000 {
diff --git a/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt b/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt
index 8f3d96a..1f6e101 100644
--- a/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt
+++ b/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt
@@ -6,10 +6,11 @@
Required properties:
- compatible : should be one among the following
- (a) "samsung,s3c2410-wdt" for Exynos4 and previous SoCs
- (b) "samsung,exynos5250-wdt" for Exynos5250
- (c) "samsung,exynos5420-wdt" for Exynos5420
- (c) "samsung,exynos7-wdt" for Exynos7
+ - "samsung,s3c2410-wdt" for S3C2410
+ - "samsung,s3c6410-wdt" for S3C6410, S5PV210 and Exynos4
+ - "samsung,exynos5250-wdt" for Exynos5250
+ - "samsung,exynos5420-wdt" for Exynos5420
+ - "samsung,exynos7-wdt" for Exynos7
- reg : base physical address of the controller and length of memory mapped
region.
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 9877ebf..8527965 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -306,6 +306,16 @@
use by PCI
Format: <irq>,<irq>...
+ acpi_mask_gpe= [HW,ACPI]
+ Due to the existence of _Lxx/_Exx, some GPEs triggered
+ by unsupported hardware/firmware features can result in
+ GPE floodings that cannot be automatically disabled by
+ the GPE dispatcher.
+ This facility can be used to prevent such uncontrolled
+ GPE floodings.
+ Format: <int>
+ Support masking of GPEs numbered from 0x00 to 0x7f.
+
acpi_no_auto_serialize [HW,ACPI]
Disable auto-serialization of AML methods
AML control methods that contain the opcodes to create
diff --git a/Documentation/mmc/mmc-dev-attrs.txt b/Documentation/mmc/mmc-dev-attrs.txt
index 379dc99..1d832d4 100644
--- a/Documentation/mmc/mmc-dev-attrs.txt
+++ b/Documentation/mmc/mmc-dev-attrs.txt
@@ -8,12 +8,29 @@
force_ro Enforce read-only access even if write protect switch is off.
+ num_wr_reqs_to_start_packing This attribute is used to determine
+ the trigger for activating the write packing, in case the write
+ packing control feature is enabled.
+
+ When the MMC manages to reach a point where num_wr_reqs_to_start_packing
+ write requests could be packed, it enables the write packing feature.
+ This allows us to start the write packing only when it is beneficial
+ and has minimum affect on the read latency.
+
+ The number of potential packed requests that will trigger the packing
+ can be configured via sysfs by writing the required value to:
+ /sys/block/<block_dev_name>/num_wr_reqs_to_start_packing.
+
+ The default value of num_wr_reqs_to_start_packing was determined by
+ running parallel lmdd write and lmdd read operations and calculating
+ the max number of packed writes requests.
+
SD and MMC Device Attributes
============================
All attributes are read-only.
- cid Card Identifaction Register
+ cid Card Identification Register
csd Card Specific Data Register
scr SD Card Configuration Register (SD only)
date Manufacturing Date (from CID Register)
@@ -84,3 +101,41 @@
clkgate_delay Tune the clock gating delay with desired value in milliseconds.
echo <desired delay> > /sys/class/mmc_host/mmcX/clkgate_delay
+
+SD/MMC/SDIO Clock Scaling Attributes
+====================================
+
+Read and write accesses are provided to following attributes.
+
+ polling_interval Measured in milliseconds, this attribute
+ defines how often we need to check the card
+ usage and make decisions on frequency scaling.
+
+ up_threshold This attribute defines what should be the
+ average card usage between the polling
+ interval for the mmc core to make a decision
+ on whether it should increase the frequency.
+ For example when it is set to '35' it means
+ that between the checking intervals the card
+ needs to be on average more than 35% in use to
+ scale up the frequency. The value should be
+ between 0 - 100 so that it can be compared
+ against load percentage.
+
+ down_threshold Similar to up_threshold, but on lowering the
+ frequency. For example, when it is set to '2'
+ it means that between the checking intervals
+ the card needs to be on average less than 2%
+ in use to scale down the clocks to minimum
+ frequency. The value should be between 0 - 100
+ so that it can be compared against load
+ percentage.
+
+ enable Enable clock scaling for hosts (and cards)
+ that support ultrahigh speed modes
+ (SDR104, DDR50, HS200).
+
+echo <desired value> > /sys/class/mmc_host/mmcX/clk_scaling/polling_interval
+echo <desired value> > /sys/class/mmc_host/mmcX/clk_scaling/up_threshold
+echo <desired value> > /sys/class/mmc_host/mmcX/clk_scaling/down_threshold
+echo <desired value> > /sys/class/mmc_host/mmcX/clk_scaling/enable
diff --git a/Documentation/stable_kernel_rules.txt b/Documentation/stable_kernel_rules.txt
index 4d82e31..501af5d 100644
--- a/Documentation/stable_kernel_rules.txt
+++ b/Documentation/stable_kernel_rules.txt
@@ -124,7 +124,7 @@
.. code-block:: none
- Cc: <stable@vger.kernel.org> # 3.3.x-
+ Cc: <stable@vger.kernel.org> # 3.3.x
The tag has the meaning of:
diff --git a/Makefile b/Makefile
index e75a1d9..c2660d2 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 4
PATCHLEVEL = 9
-SUBLEVEL = 21
+SUBLEVEL = 23
EXTRAVERSION =
NAME = Roaring Lionus
@@ -374,7 +374,7 @@
CFLAGS_KERNEL =
AFLAGS_KERNEL =
LDFLAGS_vmlinux =
-CFLAGS_GCOV = -fprofile-arcs -ftest-coverage -fno-tree-loop-im -Wno-maybe-uninitialized
+CFLAGS_GCOV := -fprofile-arcs -ftest-coverage -fno-tree-loop-im $(call cc-disable-warning,maybe-uninitialized,)
CFLAGS_KCOV := $(call cc-option,-fsanitize-coverage=trace-pc,)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index b174261..d04e168 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -550,7 +550,7 @@
config ARCH_QCOM
bool "Qualcomm MSM (non-multiplatform)"
- select ARCH_REQUIRE_GPIOLIB
+ select GPIOLIB
select CPU_V7
select AUTO_ZRELADDR
select HAVE_SMP
diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile
index 14422e5..7eb0c7f 100644
--- a/arch/arm/boot/dts/qcom/Makefile
+++ b/arch/arm/boot/dts/qcom/Makefile
@@ -1,4 +1,5 @@
+dtb-$(CONFIG_ARCH_SDXPOORWILLS) += sdxpoorwills-rumi.dtb
ifeq ($(CONFIG_ARM64),y)
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi
new file mode 100644
index 0000000..ac02429
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi
@@ -0,0 +1,35 @@
+/* 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.
+ */
+
+&soc {
+ tlmm: pinctrl@3900000 {
+ compatible = "qcom,sdxpoorwills-pinctrl";
+ reg = <0x3900000 0x300000>;
+ interrupts = <0 212 0>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ uart2_console_active: uart2_console_active {
+ mux {
+ pins = "gpio4", "gpio5";
+ function = "blsp_uart2";
+ };
+ config {
+ pins = "gpio4", "gpio5";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-rumi.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-rumi.dts
new file mode 100644
index 0000000..967e71f
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-rumi.dts
@@ -0,0 +1,29 @@
+/* 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 "sdxpoorwills.dtsi"
+#include "sdxpoorwills-pinctrl.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDXPOORWILLS RUMI";
+ compatible = "qcom,sdxpoorwills-rumi",
+ "qcom,sdxpoorwills", "qcom,rumi";
+ qcom,board-id = <15 0>;
+};
+
+&blsp1_uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart2_console_active>;
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
new file mode 100644
index 0000000..cb7ab27
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -0,0 +1,144 @@
+/* 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 "skeleton.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDX POORWILLS";
+ compatible = "qcom,sdxpoorwills";
+ qcom,msm-id = <334 0x0>;
+ interrupt-parent = <&intc>;
+
+ reserved-memory {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ peripheral2_mem: peripheral2_region@8fd00000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0x8fd00000 0x300000>;
+ label = "peripheral2_mem";
+ };
+ };
+
+ cpus {
+ #size-cells = <0>;
+ #address-cells = <1>;
+
+ CPU0: cpu@0 {
+ device-type = "cpu";
+ compatible = "arm,cortex-a7";
+ reg = <0x0>;
+ };
+ };
+
+ soc: soc { };
+};
+
+
+&soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ intc: interrupt-controller@17800000 {
+ compatible = "qcom,msm-qgic2";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ reg = <0x17800000 0x1000>,
+ <0x17802000 0x1000>;
+ };
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupts = <1 13 0xf08>,
+ <1 12 0xf08>,
+ <1 10 0xf08>,
+ <1 11 0xf08>;
+ clock-frequency = <19200000>;
+ };
+
+ timer@17820000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ compatible = "arm,armv7-timer-mem";
+ reg = <0x17820000 0x1000>;
+ clock-frequency = <19200000>;
+
+ frame@17821000 {
+ frame-number = <0>;
+ interrupts = <0 7 0x4>,
+ <0 6 0x4>;
+ reg = <0x17821000 0x1000>,
+ <0x17822000 0x1000>;
+ };
+
+ frame@17823000 {
+ frame-number = <1>;
+ interrupts = <0 8 0x4>;
+ reg = <0x17823000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@17824000 {
+ frame-number = <2>;
+ interrupts = <0 9 0x4>;
+ reg = <0x17824000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@17825000 {
+ frame-number = <3>;
+ interrupts = <0 10 0x4>;
+ reg = <0x17825000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@17826000 {
+ frame-number = <4>;
+ interrupts = <0 11 0x4>;
+ reg = <0x17826000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@17827000 {
+ frame-number = <5>;
+ interrupts = <0 12 0x4>;
+ reg = <0x17827000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@17828000 {
+ frame-number = <6>;
+ interrupts = <0 13 0x4>;
+ reg = <0x17828000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@17829000 {
+ frame-number = <7>;
+ interrupts = <0 14 0x4>;
+ reg = <0x17829000 0x1000>;
+ status = "disabled";
+ };
+ };
+
+ blsp1_uart2: serial@831000 {
+ compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
+ reg = <0x831000 0x200>;
+ interrupts = <0 26 0>;
+ status = "disabled";
+ };
+};
diff --git a/arch/arm/boot/dts/stih407-family.dtsi b/arch/arm/boot/dts/stih407-family.dtsi
index 8f79b41..acdcbf9 100644
--- a/arch/arm/boot/dts/stih407-family.dtsi
+++ b/arch/arm/boot/dts/stih407-family.dtsi
@@ -680,6 +680,7 @@
phy-names = "usb2-phy", "usb3-phy";
phys = <&usb2_picophy0>,
<&phy_port2 PHY_TYPE_USB3>;
+ snps,dis_u3_susphy_quirk;
};
};
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
new file mode 100644
index 0000000..dc44f9d
--- /dev/null
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -0,0 +1,286 @@
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHED=y
+# CONFIG_FAIR_GROUP_SCHED is not set
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+# CONFIG_SLUB_DEBUG is not set
+CONFIG_PROFILING=y
+CONFIG_CC_STACKPROTECTOR_REGULAR=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_ARCH_QCOM=y
+CONFIG_ARCH_SDXPOORWILLS=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_CMA=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_IPV6_MROUTE=y
+CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IPV6_PIMSM_V2=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_DEBUG=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CONNTRACK_TIMEOUT=y
+CONFIG_NF_CONNTRACK_TIMESTAMP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_SNMP=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SIP=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NF_CT_NETLINK_TIMEOUT=y
+CONFIG_NETFILTER_XT_TARGET_LOG=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
+CONFIG_NETFILTER_XT_MATCH_CONNLABEL=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_DSCP=y
+CONFIG_NETFILTER_XT_MATCH_ESP=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_IP_SET=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_TARGET_ECN=y
+CONFIG_IP_NF_TARGET_TTL=y
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_AH=y
+CONFIG_IP6_NF_MATCH_FRAG=y
+CONFIG_IP6_NF_MATCH_OPTS=y
+CONFIG_IP6_NF_MATCH_HL=y
+CONFIG_IP6_NF_MATCH_IPV6HEADER=y
+CONFIG_IP6_NF_MATCH_MH=y
+CONFIG_IP6_NF_MATCH_RT=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_BRIDGE_NF_EBTABLES=y
+CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_BRIDGE_EBT_T_FILTER=y
+CONFIG_BRIDGE_EBT_T_NAT=y
+CONFIG_BRIDGE_EBT_ARP=y
+CONFIG_BRIDGE_EBT_IP=y
+CONFIG_BRIDGE_EBT_IP6=y
+CONFIG_BRIDGE_EBT_ARPREPLY=y
+CONFIG_BRIDGE_EBT_DNAT=y
+CONFIG_BRIDGE_EBT_SNAT=y
+CONFIG_BRIDGE=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_BT=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=y
+CONFIG_BT_HCIUART=y
+CONFIG_BT_HCIUART_H4=y
+CONFIG_MSM_BT_POWER=y
+CONFIG_CFG80211=y
+CONFIG_CFG80211_DEBUGFS=y
+CONFIG_CFG80211_INTERNAL_REGDB=y
+CONFIG_CFG80211_WEXT=y
+CONFIG_RFKILL=y
+CONFIG_IPC_ROUTER=y
+CONFIG_IPC_ROUTER_SECURITY=y
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=12
+CONFIG_MTD=y
+CONFIG_MTD_TESTS=m
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_UBI=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_NETDEVICES=y
+CONFIG_TUN=y
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CIRRUS is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+CONFIG_KS8851=y
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+CONFIG_PPP=y
+CONFIG_PPP_ASYNC=y
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_SMSC75XX=y
+CONFIG_USB_NET_SMSC95XX=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
+CONFIG_CLD_LL_CORE=y
+# CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=m
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_HW_RANDOM=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_SOUNDWIRE=y
+CONFIG_SPI=y
+CONFIG_SPI_QUP=y
+CONFIG_SPI_SPIDEV=m
+CONFIG_PINCTRL_SDXPOORWILLS=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_THERMAL=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_UHID=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MSM=y
+CONFIG_USB_ACM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_DEBUG=y
+CONFIG_USB_STORAGE_DATAFAB=y
+CONFIG_USB_STORAGE_FREECOM=y
+CONFIG_USB_STORAGE_ISD200=y
+CONFIG_USB_STORAGE_USBAT=y
+CONFIG_USB_STORAGE_SDDR09=y
+CONFIG_USB_STORAGE_SDDR55=y
+CONFIG_USB_STORAGE_JUMPSHOT=y
+CONFIG_USB_STORAGE_ALAUDA=y
+CONFIG_USB_STORAGE_ONETOUCH=y
+CONFIG_USB_STORAGE_KARMA=y
+CONFIG_USB_STORAGE_CYPRESS_ATACB=y
+CONFIG_USB_DWC3=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_USB_MSM_SSPHY_QMP=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_MMC=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=m
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_MSM=y
+CONFIG_RTC_CLASS=y
+CONFIG_DMADEVICES=y
+CONFIG_UIO=y
+CONFIG_STAGING=y
+CONFIG_GSI=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_USB_BAM=y
+CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MSM_BOOT_STATS=y
+CONFIG_MSM_SMEM=y
+CONFIG_TRACER_PKT=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_SMP2P_TEST=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_PWM=y
+CONFIG_ANDROID=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_UBIFS_FS=y
+CONFIG_UBIFS_FS_ADVANCED_COMPR=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_PANIC_TIMEOUT=5
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_IPC_LOGGING=y
+CONFIG_BLK_DEV_IO_TRACE=y
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
new file mode 100644
index 0000000..2bfcf87
--- /dev/null
+++ b/arch/arm/configs/sdxpoorwills_defconfig
@@ -0,0 +1,294 @@
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHED=y
+# CONFIG_FAIR_GROUP_SCHED is not set
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZ4 is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+CONFIG_PROFILING=y
+CONFIG_CC_STACKPROTECTOR_REGULAR=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_ARCH_QCOM=y
+CONFIG_ARCH_SDXPOORWILLS=y
+# CONFIG_VDSO is not set
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_CMA=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_IPV6_MROUTE=y
+CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IPV6_PIMSM_V2=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_DEBUG=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CONNTRACK_TIMEOUT=y
+CONFIG_NF_CONNTRACK_TIMESTAMP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_SNMP=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SIP=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NF_CT_NETLINK_TIMEOUT=y
+CONFIG_NETFILTER_XT_TARGET_LOG=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
+CONFIG_NETFILTER_XT_MATCH_CONNLABEL=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_DSCP=y
+CONFIG_NETFILTER_XT_MATCH_ESP=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_IP_SET=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_TARGET_ECN=y
+CONFIG_IP_NF_TARGET_TTL=y
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_AH=y
+CONFIG_IP6_NF_MATCH_FRAG=y
+CONFIG_IP6_NF_MATCH_OPTS=y
+CONFIG_IP6_NF_MATCH_HL=y
+CONFIG_IP6_NF_MATCH_IPV6HEADER=y
+CONFIG_IP6_NF_MATCH_MH=y
+CONFIG_IP6_NF_MATCH_RT=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_BRIDGE_NF_EBTABLES=y
+CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_BRIDGE_EBT_T_FILTER=y
+CONFIG_BRIDGE_EBT_T_NAT=y
+CONFIG_BRIDGE_EBT_ARP=y
+CONFIG_BRIDGE_EBT_IP=y
+CONFIG_BRIDGE_EBT_IP6=y
+CONFIG_BRIDGE_EBT_ARPREPLY=y
+CONFIG_BRIDGE_EBT_DNAT=y
+CONFIG_BRIDGE_EBT_SNAT=y
+CONFIG_BRIDGE=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_RFKILL=y
+CONFIG_IPC_ROUTER=y
+CONFIG_IPC_ROUTER_SECURITY=y
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=12
+CONFIG_MTD=y
+CONFIG_MTD_TESTS=m
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_UBI=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_NETDEVICES=y
+CONFIG_TUN=y
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CIRRUS is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+CONFIG_KS8851=y
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+CONFIG_PPP=y
+CONFIG_PPP_ASYNC=y
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_SMSC75XX=y
+CONFIG_USB_NET_SMSC95XX=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
+CONFIG_CLD_LL_CORE=y
+# CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=m
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+CONFIG_HVC_DCC=y
+CONFIG_HW_RANDOM=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_SOUNDWIRE=y
+CONFIG_SPI=y
+CONFIG_SPI_SPIDEV=m
+CONFIG_SLIMBUS=y
+CONFIG_PINCTRL_SDXPOORWILLS=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_THERMAL=y
+CONFIG_MSM_CDC_PINCTRL=y
+CONFIG_MSM_CDC_SUPPLY=y
+CONFIG_FB=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_UHID=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_ACM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_DEBUG=y
+CONFIG_USB_STORAGE_DATAFAB=y
+CONFIG_USB_STORAGE_FREECOM=y
+CONFIG_USB_STORAGE_ISD200=y
+CONFIG_USB_STORAGE_USBAT=y
+CONFIG_USB_STORAGE_SDDR09=y
+CONFIG_USB_STORAGE_SDDR55=y
+CONFIG_USB_STORAGE_JUMPSHOT=y
+CONFIG_USB_STORAGE_ALAUDA=y
+CONFIG_USB_STORAGE_ONETOUCH=y
+CONFIG_USB_STORAGE_KARMA=y
+CONFIG_USB_STORAGE_CYPRESS_ATACB=y
+CONFIG_USB_DWC3=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_MMC=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=m
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_RTC_CLASS=y
+CONFIG_DMADEVICES=y
+CONFIG_UIO=y
+CONFIG_STAGING=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_HWSPINLOCK_QCOM=y
+CONFIG_QCOM_SMEM=y
+CONFIG_QCOM_SMD=y
+CONFIG_MSM_BOOT_STATS=y
+CONFIG_TRACER_PKT=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y
+CONFIG_PWM=y
+CONFIG_QCOM_SHOW_RESUME_IRQ=y
+CONFIG_RESET_CONTROLLER=y
+CONFIG_ANDROID=y
+CONFIG_STM=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_UBIFS_FS=y
+CONFIG_UBIFS_FS_ADVANCED_COMPR=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_INFO=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_PAGEALLOC=y
+CONFIG_DEBUG_KMEMLEAK=y
+CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
+CONFIG_DEBUG_STACK_USAGE=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+CONFIG_DEBUG_LIST=y
+CONFIG_FAULT_INJECTION=y
+CONFIG_FAIL_PAGE_ALLOC=y
+CONFIG_FAULT_INJECTION_DEBUG_FS=y
+CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
+CONFIG_IPC_LOGGING=y
+CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_DEBUG_USER=y
+CONFIG_CRYPTO_ECB=y
+CONFIG_CRYPTO_CMAC=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_XZ_DEC=y
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
index 7e45f69..8e8d20c 100644
--- a/arch/arm/kernel/armksyms.c
+++ b/arch/arm/kernel/armksyms.c
@@ -178,6 +178,6 @@ EXPORT_SYMBOL(__pv_offset);
#endif
#ifdef CONFIG_HAVE_ARM_SMCCC
-EXPORT_SYMBOL(arm_smccc_smc);
-EXPORT_SYMBOL(arm_smccc_hvc);
+EXPORT_SYMBOL(__arm_smccc_smc);
+EXPORT_SYMBOL(__arm_smccc_hvc);
#endif
diff --git a/arch/arm/kernel/smccc-call.S b/arch/arm/kernel/smccc-call.S
index 2e48b67..e5d4306 100644
--- a/arch/arm/kernel/smccc-call.S
+++ b/arch/arm/kernel/smccc-call.S
@@ -46,17 +46,19 @@
/*
* void smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
* unsigned long a3, unsigned long a4, unsigned long a5,
- * unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
+ * unsigned long a6, unsigned long a7, struct arm_smccc_res *res,
+ * struct arm_smccc_quirk *quirk)
*/
-ENTRY(arm_smccc_smc)
+ENTRY(__arm_smccc_smc)
SMCCC SMCCC_SMC
-ENDPROC(arm_smccc_smc)
+ENDPROC(__arm_smccc_smc)
/*
* void smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a2,
* unsigned long a3, unsigned long a4, unsigned long a5,
- * unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
+ * unsigned long a6, unsigned long a7, struct arm_smccc_res *res,
+ * struct arm_smccc_quirk *quirk)
*/
-ENTRY(arm_smccc_hvc)
+ENTRY(__arm_smccc_hvc)
SMCCC SMCCC_HVC
-ENDPROC(arm_smccc_hvc)
+ENDPROC(__arm_smccc_hvc)
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index a5265ed..2fd5c13 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -292,11 +292,18 @@ static void unmap_stage2_range(struct kvm *kvm, phys_addr_t start, u64 size)
phys_addr_t addr = start, end = start + size;
phys_addr_t next;
+ assert_spin_locked(&kvm->mmu_lock);
pgd = kvm->arch.pgd + stage2_pgd_index(addr);
do {
next = stage2_pgd_addr_end(addr, end);
if (!stage2_pgd_none(*pgd))
unmap_stage2_puds(kvm, pgd, addr, next);
+ /*
+ * If the range is too large, release the kvm->mmu_lock
+ * to prevent starvation and lockup detector warnings.
+ */
+ if (next != end)
+ cond_resched_lock(&kvm->mmu_lock);
} while (pgd++, addr = next, addr != end);
}
@@ -803,6 +810,7 @@ void stage2_unmap_vm(struct kvm *kvm)
int idx;
idx = srcu_read_lock(&kvm->srcu);
+ down_read(¤t->mm->mmap_sem);
spin_lock(&kvm->mmu_lock);
slots = kvm_memslots(kvm);
@@ -810,6 +818,7 @@ void stage2_unmap_vm(struct kvm *kvm)
stage2_unmap_memslot(kvm, memslot);
spin_unlock(&kvm->mmu_lock);
+ up_read(¤t->mm->mmap_sem);
srcu_read_unlock(&kvm->srcu, idx);
}
@@ -829,7 +838,10 @@ void kvm_free_stage2_pgd(struct kvm *kvm)
if (kvm->arch.pgd == NULL)
return;
+ spin_lock(&kvm->mmu_lock);
unmap_stage2_range(kvm, 0, KVM_PHYS_SIZE);
+ spin_unlock(&kvm->mmu_lock);
+
/* Free the HW pgd, one page at a time */
free_pages_exact(kvm->arch.pgd, S2_PGD_SIZE);
kvm->arch.pgd = NULL;
@@ -1804,6 +1816,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
(KVM_PHYS_SIZE >> PAGE_SHIFT))
return -EFAULT;
+ down_read(¤t->mm->mmap_sem);
/*
* A memory region could potentially cover multiple VMAs, and any holes
* between them, so iterate over all of them to find out if we can map
@@ -1847,8 +1860,10 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
pa += vm_start - vma->vm_start;
/* IO region dirty page logging not allowed */
- if (memslot->flags & KVM_MEM_LOG_DIRTY_PAGES)
- return -EINVAL;
+ if (memslot->flags & KVM_MEM_LOG_DIRTY_PAGES) {
+ ret = -EINVAL;
+ goto out;
+ }
ret = kvm_phys_addr_ioremap(kvm, gpa, pa,
vm_end - vm_start,
@@ -1860,7 +1875,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
} while (hva < reg_end);
if (change == KVM_MR_FLAGS_ONLY)
- return ret;
+ goto out;
spin_lock(&kvm->mmu_lock);
if (ret)
@@ -1868,6 +1883,8 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
else
stage2_flush_memslot(kvm, memslot);
spin_unlock(&kvm->mmu_lock);
+out:
+ up_read(¤t->mm->mmap_sem);
return ret;
}
diff --git a/arch/arm/mach-davinci/da8xx-dt.c b/arch/arm/mach-davinci/da8xx-dt.c
index c9f7e92..aed44dc 100644
--- a/arch/arm/mach-davinci/da8xx-dt.c
+++ b/arch/arm/mach-davinci/da8xx-dt.c
@@ -46,6 +46,7 @@ static struct of_dev_auxdata da850_auxdata_lookup[] __initdata = {
static void __init da850_init_machine(void)
{
of_platform_default_populate(NULL, da850_auxdata_lookup, NULL);
+ davinci_pm_init();
}
static const char *const da850_boards_compat[] __initconst = {
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
index 28866e9..ef75d2f 100644
--- a/arch/arm/mach-qcom/Kconfig
+++ b/arch/arm/mach-qcom/Kconfig
@@ -38,5 +38,16 @@
select CLKSRC_OF
select COMMON_CLK
+config ARCH_SDXPOORWILLS
+ bool "Enable support for SDXPOORWILLS"
+ select CPU_V7
+ select HAVE_ARM_ARCH_TIMER
+ select MSM_CORTEX_A7
+ select COMMON_CLK_MSM
+ select PINCTRL
+ select QCOM_SCM if SMP
+ select MSM_JTAG_MM if CORESIGHT_ETM
+ select PM_DEVFREQ
+ select COMMON_CLK
endmenu
endif
diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile
index e7ffa04..d893b27 100644
--- a/arch/arm/mach-qcom/Makefile
+++ b/arch/arm/mach-qcom/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_USE_OF) += board-dt.o
obj-$(CONFIG_SMP) += platsmp.o
+obj-$(CONFIG_ARCH_SDXPOORWILLS) += board-poorwills.o
diff --git a/arch/arm/mach-qcom/board-poorwills.c b/arch/arm/mach-qcom/board-poorwills.c
new file mode 100644
index 0000000..31f5d3e
--- /dev/null
+++ b/arch/arm/mach-qcom/board-poorwills.c
@@ -0,0 +1,32 @@
+/* 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/kernel.h>
+#include "board-dt.h"
+#include <asm/mach/map.h>
+#include <asm/mach/arch.h>
+
+static const char *sdxpoorwills_dt_match[] __initconst = {
+ "qcom,sdxpoorwills",
+ NULL
+};
+
+static void __init sdxpoorwills_init(void)
+{
+ board_dt_populate(NULL);
+}
+
+DT_MACHINE_START(SDXPOORWILLS_DT,
+ "Qualcomm Technologies, Inc. SDX POORWILLS (Flattened Device Tree)")
+ .init_machine = sdxpoorwills_init,
+ .dt_compat = sdxpoorwills_dt_match,
+MACHINE_END
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 33f3cc6..19f444e 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -2401,6 +2401,7 @@ void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
set_dma_ops(dev, dma_ops);
}
+EXPORT_SYMBOL(arch_setup_dma_ops);
void arch_teardown_dma_ops(struct device *dev)
{
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index ba0695b..d2deed5 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -83,7 +83,6 @@
select HAVE_FUNCTION_GRAPH_TRACER
select HAVE_GCC_PLUGINS
select HAVE_GENERIC_DMA_COHERENT
- select HAVE_HW_BREAKPOINT if PERF_EVENTS
select HAVE_IRQ_TIME_ACCOUNTING
select HAVE_MEMBLOCK
select HAVE_MEMBLOCK_NODE_MAP if NUMA
diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
index af45041..f2eb12c 100644
--- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
@@ -590,7 +590,7 @@
reg = <0 0xa2000000 0 0x10000>;
sas-addr = [50 01 88 20 16 00 00 00];
hisilicon,sas-syscon = <&pcie_subctl>;
- am-max-trans;
+ hip06-sas-v2-quirk-amt;
ctrl-reset-reg = <0xa18>;
ctrl-reset-sts-reg = <0x5a0c>;
ctrl-clock-ena-reg = <0x318>;
diff --git a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
index 44d6f18..3766d51 100644
--- a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
@@ -11,6 +11,7 @@
*/
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/msm/msm-bus-ids.h>
&soc {
kgsl_smmu: arm,smmu-kgsl@5040000 {
@@ -32,6 +33,18 @@
<GIC_SPI 369 IRQ_TYPE_EDGE_RISING>,
<GIC_SPI 370 IRQ_TYPE_EDGE_RISING>,
<GIC_SPI 371 IRQ_TYPE_EDGE_RISING>;
+ clock-names = "gcc_gpu_cfg_ahb_clk",
+ "gcc_ddrss_gpu_axi_clk",
+ "gcc_gpu_memnoc_gfx_clk",
+ "gcc_gpu_snoc_dvm_gfx_clk",
+ "gpu_cc_ahb_clk",
+ "gpu_cc_cx_gmu_clk";
+ clocks = <&clock_gcc GCC_GPU_CFG_AHB_CLK>,
+ <&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
+ <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>,
+ <&clock_gcc GCC_GPU_SNOC_DVM_GFX_CLK>,
+ <&clock_gpucc GPU_CC_AHB_CLK>,
+ <&clock_gpucc GPU_CC_CX_GMU_CLK>;
attach-impl-defs =
<0x6000 0x2378>,
<0x6060 0x1055>,
@@ -123,6 +136,17 @@
<GIC_SPI 341 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 342 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 343 IRQ_TYPE_LEVEL_HIGH>;
+ qcom,msm-bus,name = "apps_smmu";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <MSM_BUS_MASTER_GNOC_SNOC>,
+ <MSM_BUS_SLAVE_IMEM_CFG>,
+ <0 0>,
+ <MSM_BUS_MASTER_GNOC_SNOC>,
+ <MSM_BUS_SLAVE_IMEM_CFG>,
+ <0 1000>;
anoc_1_tbu: anoc_1_tbu@0x150c5000 {
status = "disabled";
@@ -133,6 +157,17 @@
qcom,stream-id-range = <0x0 0x400>;
qcom,regulator-names = "vdd";
vdd-supply = <&hlos1_vote_aggre_noc_mmu_tbu1_gdsc>;
+ qcom,msm-bus,name = "apps_smmu";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <MSM_BUS_MASTER_GNOC_SNOC>,
+ <MSM_BUS_SLAVE_IMEM_CFG>,
+ <0 0>,
+ <MSM_BUS_MASTER_GNOC_SNOC>,
+ <MSM_BUS_SLAVE_IMEM_CFG>,
+ <0 1000>;
};
anoc_2_tbu: anoc_2_tbu@0x150c9000 {
@@ -144,6 +179,17 @@
qcom,stream-id-range = <0x400 0x400>;
qcom,regulator-names = "vdd";
vdd-supply = <&hlos1_vote_aggre_noc_mmu_tbu2_gdsc>;
+ qcom,msm-bus,name = "apps_smmu";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <MSM_BUS_MASTER_GNOC_SNOC>,
+ <MSM_BUS_SLAVE_IMEM_CFG>,
+ <0 0>,
+ <MSM_BUS_MASTER_GNOC_SNOC>,
+ <MSM_BUS_SLAVE_IMEM_CFG>,
+ <0 1000>;
};
mnoc_hf_0_tbu: mnoc_hf_0_tbu@0x150cd000 {
@@ -155,6 +201,17 @@
qcom,stream-id-range = <0x800 0x400>;
qcom,regulator-names = "vdd";
vdd-supply = <&hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc>;
+ qcom,msm-bus,name = "mnoc_hf_0_tbu";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <MSM_BUS_MASTER_MDP_PORT0>,
+ <MSM_BUS_SLAVE_MNOC_HF_MEM_NOC>,
+ <0 0>,
+ <MSM_BUS_MASTER_MDP_PORT0>,
+ <MSM_BUS_SLAVE_MNOC_HF_MEM_NOC>,
+ <0 1000>;
};
mnoc_hf_1_tbu: mnoc_hf_1_tbu@0x150d1000 {
@@ -166,6 +223,17 @@
qcom,stream-id-range = <0xc00 0x400>;
qcom,regulator-names = "vdd";
vdd-supply = <&hlos1_vote_mmnoc_mmu_tbu_hf1_gdsc>;
+ qcom,msm-bus,name = "mnoc_hf_1_tbu";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <MSM_BUS_MASTER_MDP_PORT0>,
+ <MSM_BUS_SLAVE_MNOC_HF_MEM_NOC>,
+ <0 0>,
+ <MSM_BUS_MASTER_MDP_PORT0>,
+ <MSM_BUS_SLAVE_MNOC_HF_MEM_NOC>,
+ <0 1000>;
};
mnoc_sf_0_tbu: mnoc_sf_0_tbu@0x150d5000 {
@@ -177,6 +245,17 @@
qcom,stream-id-range = <0x1000 0x400>;
qcom,regulator-names = "vdd";
vdd-supply = <&hlos1_vote_mmnoc_mmu_tbu_sf_gdsc>;
+ qcom,msm-bus,name = "mnoc_sf_0_tbu";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <MSM_BUS_MASTER_CAMNOC_SF>,
+ <MSM_BUS_SLAVE_MNOC_SF_MEM_NOC>,
+ <0 0>,
+ <MSM_BUS_MASTER_CAMNOC_SF>,
+ <MSM_BUS_SLAVE_MNOC_SF_MEM_NOC>,
+ <0 1000>;
};
compute_dsp_tbu: compute_dsp_tbu@0x150d9000 {
@@ -187,6 +266,17 @@
reg-names = "base", "status-reg";
qcom,stream-id-range = <0x1400 0x400>;
/* No GDSC */
+ qcom,msm-bus,name = "apps_smmu";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <MSM_BUS_MASTER_GNOC_SNOC>,
+ <MSM_BUS_SLAVE_IMEM_CFG>,
+ <0 0>,
+ <MSM_BUS_MASTER_GNOC_SNOC>,
+ <MSM_BUS_SLAVE_IMEM_CFG>,
+ <0 1000>;
};
adsp_tbu: adsp_tbu@0x150dd000 {
@@ -198,6 +288,17 @@
qcom,stream-id-range = <0x1800 0x400>;
qcom,regulator-names = "vdd";
vdd-supply = <&hlos1_vote_aggre_noc_mmu_audio_tbu_gdsc>;
+ qcom,msm-bus,name = "apps_smmu";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <MSM_BUS_MASTER_GNOC_SNOC>,
+ <MSM_BUS_SLAVE_IMEM_CFG>,
+ <0 0>,
+ <MSM_BUS_MASTER_GNOC_SNOC>,
+ <MSM_BUS_SLAVE_IMEM_CFG>,
+ <0 1000>;
};
anoc_1_pcie_tbu: anoc_1_pcie_tbu@0x150e1000 {
@@ -209,6 +310,19 @@
qcom,stream-id-range = <0x1c00 0x400>;
qcom,regulator-names = "vdd";
vdd-supply = <&hlos1_vote_aggre_noc_mmu_pcie_tbu_gdsc>;
+ clock-names = "gcc_aggre_noc_pcie_tbu_clk";
+ clocks = <&clock_gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>;
+ qcom,msm-bus,name = "apps_smmu";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <MSM_BUS_MASTER_GNOC_SNOC>,
+ <MSM_BUS_SLAVE_IMEM_CFG>,
+ <0 0>,
+ <MSM_BUS_MASTER_GNOC_SNOC>,
+ <MSM_BUS_SLAVE_IMEM_CFG>,
+ <0 1000>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi
index 539685a..923804f 100644
--- a/arch/arm64/boot/dts/qcom/pmi8998.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi8998.dtsi
@@ -232,6 +232,25 @@
<12000 2250>; /* 12V @ 2.25A */
};
+ bcl_sensor: bcl@4200 {
+ compatible = "qcom,msm-bcl-lmh";
+ reg = <0x4200 0xff>,
+ <0x4300 0xff>;
+ reg-names = "fg_user_adc",
+ "fg_lmh";
+ interrupts = <0x2 0x42 0x0 IRQ_TYPE_NONE>,
+ <0x2 0x42 0x1 IRQ_TYPE_NONE>,
+ <0x2 0x42 0x2 IRQ_TYPE_NONE>,
+ <0x2 0x42 0x3 IRQ_TYPE_NONE>,
+ <0x2 0x42 0x4 IRQ_TYPE_NONE>;
+ interrupt-names = "bcl-high-ibat",
+ "bcl-very-high-ibat",
+ "bcl-low-vbat",
+ "bcl-very-low-vbat",
+ "bcl-crit-low-vbat";
+ #thermal-sensor-cells = <1>;
+ };
+
pmi8998_rradc: rradc@4500 {
compatible = "qcom,rradc";
reg = <0x4500 0x100>;
@@ -651,3 +670,130 @@
};
};
};
+
+&thermal_zones {
+ ibat-high {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "step_wise";
+ thermal-sensors = <&bcl_sensor 0>;
+
+ trips {
+ ibat_high: low-ibat {
+ temperature = <4200>;
+ hysteresis = <200>;
+ type = "passive";
+ };
+ };
+ };
+ ibat-vhigh {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "step_wise";
+ thermal-sensors = <&bcl_sensor 1>;
+
+ trips {
+ ibat_vhigh: ibat_vhigh {
+ temperature = <4300>;
+ hysteresis = <100>;
+ type = "passive";
+ };
+ };
+ };
+ vbat {
+ polling-delay-passive = <100>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_cap";
+ thermal-sensors = <&bcl_sensor 2>;
+ tracks-low;
+
+ trips {
+ low_vbat: low-vbat {
+ temperature = <3300>;
+ hysteresis = <100>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ vbat_cpu4 {
+ trip = <&low_vbat>;
+ cooling-device = <&CPU4 22 22>;
+ };
+ vbat_cpu5 {
+ trip = <&low_vbat>;
+ cooling-device = <&CPU5 22 22>;
+ };
+ vbat_map6 {
+ trip = <&low_vbat>;
+ cooling-device = <&CPU6 22 22>;
+ };
+ vbat_map7 {
+ trip = <&low_vbat>;
+ cooling-device = <&CPU7 22 22>;
+ };
+ };
+ };
+ vbat_low {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_cap";
+ thermal-sensors = <&bcl_sensor 3>;
+ tracks-low;
+
+ trips {
+ low-vbat {
+ temperature = <3100>;
+ hysteresis = <0>;
+ type = "passive";
+ };
+ };
+ };
+ vbat_too_low {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_cap";
+ thermal-sensors = <&bcl_sensor 4>;
+ tracks-low;
+
+ trips {
+ low-vbat {
+ temperature = <2900>;
+ hysteresis = <0>;
+ type = "passive";
+ };
+ };
+ };
+ soc {
+ polling-delay-passive = <100>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_cap";
+ thermal-sensors = <&bcl_sensor 5>;
+ tracks-low;
+
+ trips {
+ low_soc: low-soc {
+ temperature = <10>;
+ hysteresis = <0>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ soc_cpu4 {
+ trip = <&low_soc>;
+ cooling-device = <&CPU4 22 22>;
+ };
+ soc_cpu5 {
+ trip = <&low_soc>;
+ cooling-device = <&CPU5 22 22>;
+ };
+ soc_map6 {
+ trip = <&low_soc>;
+ cooling-device = <&CPU6 22 22>;
+ };
+ soc_map7 {
+ trip = <&low_soc>;
+ cooling-device = <&CPU7 22 22>;
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-audio.dtsi b/arch/arm64/boot/dts/qcom/sdm845-audio.dtsi
index b66ca94..9d59a16 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-audio.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-audio.dtsi
@@ -18,7 +18,7 @@
&msm_audio_ion {
iommus = <&apps_smmu 0x1821>;
- qcom,smmu-sid-mask = <0xf>;
+ qcom,smmu-sid-mask = /bits/ 64 <0xf>;
};
&soc {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi b/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi
index a51f411..1702e80 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi
@@ -191,6 +191,7 @@
cell-id = <MSM_BUS_BCM_QUP0>;
label = "QUP0";
qcom,bcm-name = "QUP0";
+ qcom,rscs = <&rsc_apps>;
qcom,bcm-dev;
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-cdp.dtsi
new file mode 100644
index 0000000..fce1687
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-cdp.dtsi
@@ -0,0 +1,352 @@
+/*
+ * 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ led_flash_rear: qcom,camera-flash@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,camera-flash";
+ qcom,flash-source = <&pmi8998_flash0 &pmi8998_flash1>;
+ qcom,torch-source = <&pmi8998_torch0 &pmi8998_torch1>;
+ qcom,switch-source = <&pmi8998_switch0>;
+ status = "ok";
+ };
+
+ led_flash_front: qcom,camera-flash@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,camera-flash";
+ qcom,flash-source = <&pmi8998_flash2>;
+ qcom,torch-source = <&pmi8998_torch2>;
+ qcom,switch-source = <&pmi8998_switch1>;
+ status = "ok";
+ };
+
+ actuator_regulator: gpio-regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0x0>;
+ regulator-name = "actuator_regulator";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ regulator-enable-ramp-delay = <100>;
+ enable-active-high;
+ gpio = <&tlmm 27 0>;
+ vin-supply = <&pmi8998_bob>;
+ };
+
+ camera_rear_ldo: gpio-regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <0x1>;
+ regulator-name = "camera_rear_ldo";
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1050000>;
+ regulator-enable-ramp-delay = <135>;
+ enable-active-high;
+ gpio = <&pm8998_gpios 12 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&camera_rear_dvdd_en_default>;
+ vin-supply = <&pm8998_s3>;
+ };
+
+ camera_ldo: gpio-regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <0x2>;
+ regulator-name = "camera_ldo";
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1050000>;
+ regulator-enable-ramp-delay = <233>;
+ enable-active-high;
+ gpio = <&pm8998_gpios 9 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&camera_dvdd_en_default>;
+ vin-supply = <&pm8998_s3>;
+ };
+};
+
+&cci {
+ actuator_rear: qcom,actuator@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <0>;
+ cam_vaf-supply = <&actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2800000>;
+ qcom,cam-vreg-max-voltage = <2800000>;
+ qcom,cam-vreg-op-mode = <0>;
+ };
+
+ actuator_front: qcom,actuator@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <1>;
+ cam_vaf-supply = <&actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2800000>;
+ qcom,cam-vreg-max-voltage = <2800000>;
+ qcom,cam-vreg-op-mode = <0>;
+ };
+
+ ois_rear: qcom,ois@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,ois";
+ qcom,cci-master = <0>;
+ cam_vaf-supply = <&actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2800000>;
+ qcom,cam-vreg-max-voltage = <2800000>;
+ qcom,cam-vreg-op-mode = <0>;
+ status = "disabled";
+ };
+
+ eeprom_rear: qcom,eeprom@0 {
+ cell-index = <0>;
+ reg = <0>;
+ compatible = "qcom,eeprom";
+ cam_vio-supply = <&pm8998_lvs1>;
+ cam_vana-supply = <&pmi8998_bob>;
+ cam_vdig-supply = <&camera_rear_ldo>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage = <0 3312000 1050000>;
+ qcom,cam-vreg-max-voltage = <0 3600000 1050000>;
+ qcom,cam-vreg-op-mode = <0 80000 105000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_active
+ &cam_sensor_rear_active>;
+ pinctrl-1 = <&cam_sensor_mclk0_suspend
+ &cam_sensor_rear_suspend>;
+ gpios = <&tlmm 13 0>,
+ <&tlmm 80 0>,
+ <&tlmm 79 0>,
+ <&tlmm 27 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-vaf = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_VANA0",
+ "CAM_VAF";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+ clock-names = "cam_clk";
+ qcom,clock-rates = <24000000>;
+ };
+
+ eeprom_rear_aux: qcom,eeprom@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,eeprom";
+ cam_vdig-supply = <&camera_ldo>;
+ cam_vio-supply = <&pm8998_lvs1>;
+ cam_vana-supply = <&pmi8998_bob>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-min-voltage = <1050000 0 3312000>;
+ qcom,cam-vreg-max-voltage = <1050000 0 3600000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_active
+ &cam_sensor_rear2_active>;
+ pinctrl-1 = <&cam_sensor_mclk2_suspend
+ &cam_sensor_rear2_suspend>;
+ gpios = <&tlmm 15 0>,
+ <&tlmm 9 0>,
+ <&tlmm 8 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_VANA1";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+ clock-names = "cam_clk";
+ qcom,clock-rates = <24000000>;
+ };
+
+ eeprom_front: qcom,eeprom@2 {
+ cell-index = <2>;
+ reg = <0x2>;
+ compatible = "qcom,eeprom";
+ cam_vio-supply = <&pm8998_lvs1>;
+ cam_vana-supply = <&pmi8998_bob>;
+ cam_vdig-supply = <&camera_ldo>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage = <0 3312000 1050000>;
+ qcom,cam-vreg-max-voltage = <0 3600000 1050000>;
+ qcom,cam-vreg-op-mode = <0 80000 105000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_active
+ &cam_sensor_front_active>;
+ pinctrl-1 = <&cam_sensor_mclk1_suspend
+ &cam_sensor_front_suspend>;
+ gpios = <&tlmm 14 0>,
+ <&tlmm 28 0>,
+ <&tlmm 8 0>,
+ <&tlmm 27 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-vaf = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_VANA2",
+ "CAM_VAF";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_camcc CAM_CC_MCLK1_CLK>;
+ clock-names = "cam_clk";
+ qcom,clock-rates = <24000000>;
+ };
+
+ qcom,cam-sensor@0 {
+ cell-index = <0>;
+ compatible = "qcom,cam-sensor";
+ reg = <0x0>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,sensor-position-roll = <90>;
+ qcom,sensor-position-pitch = <0>;
+ qcom,sensor-position-yaw = <180>;
+ qcom,led-flash-src = <&led_flash_rear>;
+ qcom,actuator-src = <&actuator_rear>;
+ qcom,ois-src = <&ois_rear>;
+ qcom,eeprom-src = <&eeprom_rear>;
+ cam_vio-supply = <&pm8998_lvs1>;
+ cam_vana-supply = <&pmi8998_bob>;
+ cam_vdig-supply = <&camera_rear_ldo>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage = <0 3312000 1050000>;
+ qcom,cam-vreg-max-voltage = <0 3600000 1050000>;
+ qcom,cam-vreg-op-mode = <0 80000 105000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_active
+ &cam_sensor_rear_active>;
+ pinctrl-1 = <&cam_sensor_mclk0_suspend
+ &cam_sensor_rear_suspend>;
+ gpios = <&tlmm 13 0>,
+ <&tlmm 80 0>,
+ <&tlmm 79 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_VANA";
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+ clock-names = "cam_clk";
+ qcom,clock-rates = <24000000>;
+ };
+
+ qcom,cam-sensor@1 {
+ cell-index = <1>;
+ compatible = "qcom,cam-sensor";
+ reg = <0x1>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,sensor-position-roll = <90>;
+ qcom,sensor-position-pitch = <0>;
+ qcom,sensor-position-yaw = <180>;
+ qcom,eeprom-src = <&eeprom_rear_aux>;
+ cam_vdig-supply = <&camera_ldo>;
+ cam_vio-supply = <&pm8998_lvs1>;
+ cam_vana-supply = <&pmi8998_bob>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-min-voltage = <1050000 0 3312000>;
+ qcom,cam-vreg-max-voltage = <1050000 0 3600000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_active
+ &cam_sensor_rear2_active>;
+ pinctrl-1 = <&cam_sensor_mclk2_suspend
+ &cam_sensor_rear2_suspend>;
+ gpios = <&tlmm 15 0>,
+ <&tlmm 9 0>,
+ <&tlmm 8 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_VANA1";
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+ clock-names = "cam_clk";
+ qcom,clock-rates = <24000000>;
+ };
+
+ qcom,cam-sensor@2 {
+ cell-index = <2>;
+ compatible = "qcom,cam-sensor";
+ reg = <0x02>;
+ qcom,csiphy-sd-index = <2>;
+ qcom,sensor-position-roll = <90>;
+ qcom,sensor-position-pitch = <0>;
+ qcom,sensor-position-yaw = <0>;
+ qcom,eeprom-src = <&eeprom_front>;
+ qcom,actuator-src = <&actuator_front>;
+ qcom,led-flash-src = <&led_flash_front>;
+ cam_vio-supply = <&pm8998_lvs1>;
+ cam_vana-supply = <&pmi8998_bob>;
+ cam_vdig-supply = <&camera_ldo>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage = <0 3312000 1050000>;
+ qcom,cam-vreg-max-voltage = <0 3600000 1050000>;
+ qcom,cam-vreg-op-mode = <0 80000 105000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_active
+ &cam_sensor_front_active>;
+ pinctrl-1 = <&cam_sensor_mclk1_suspend
+ &cam_sensor_front_suspend>;
+ gpios = <&tlmm 14 0>,
+ <&tlmm 28 0>,
+ <&tlmm 8 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_VANA1";
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_camcc CAM_CC_MCLK1_CLK>;
+ clock-names = "cam_clk";
+ qcom,clock-rates = <24000000>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi
new file mode 100644
index 0000000..fce1687
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi
@@ -0,0 +1,352 @@
+/*
+ * 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ led_flash_rear: qcom,camera-flash@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,camera-flash";
+ qcom,flash-source = <&pmi8998_flash0 &pmi8998_flash1>;
+ qcom,torch-source = <&pmi8998_torch0 &pmi8998_torch1>;
+ qcom,switch-source = <&pmi8998_switch0>;
+ status = "ok";
+ };
+
+ led_flash_front: qcom,camera-flash@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,camera-flash";
+ qcom,flash-source = <&pmi8998_flash2>;
+ qcom,torch-source = <&pmi8998_torch2>;
+ qcom,switch-source = <&pmi8998_switch1>;
+ status = "ok";
+ };
+
+ actuator_regulator: gpio-regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0x0>;
+ regulator-name = "actuator_regulator";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ regulator-enable-ramp-delay = <100>;
+ enable-active-high;
+ gpio = <&tlmm 27 0>;
+ vin-supply = <&pmi8998_bob>;
+ };
+
+ camera_rear_ldo: gpio-regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <0x1>;
+ regulator-name = "camera_rear_ldo";
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1050000>;
+ regulator-enable-ramp-delay = <135>;
+ enable-active-high;
+ gpio = <&pm8998_gpios 12 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&camera_rear_dvdd_en_default>;
+ vin-supply = <&pm8998_s3>;
+ };
+
+ camera_ldo: gpio-regulator@2 {
+ compatible = "regulator-fixed";
+ reg = <0x2>;
+ regulator-name = "camera_ldo";
+ regulator-min-microvolt = <1050000>;
+ regulator-max-microvolt = <1050000>;
+ regulator-enable-ramp-delay = <233>;
+ enable-active-high;
+ gpio = <&pm8998_gpios 9 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&camera_dvdd_en_default>;
+ vin-supply = <&pm8998_s3>;
+ };
+};
+
+&cci {
+ actuator_rear: qcom,actuator@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <0>;
+ cam_vaf-supply = <&actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2800000>;
+ qcom,cam-vreg-max-voltage = <2800000>;
+ qcom,cam-vreg-op-mode = <0>;
+ };
+
+ actuator_front: qcom,actuator@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <1>;
+ cam_vaf-supply = <&actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2800000>;
+ qcom,cam-vreg-max-voltage = <2800000>;
+ qcom,cam-vreg-op-mode = <0>;
+ };
+
+ ois_rear: qcom,ois@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,ois";
+ qcom,cci-master = <0>;
+ cam_vaf-supply = <&actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2800000>;
+ qcom,cam-vreg-max-voltage = <2800000>;
+ qcom,cam-vreg-op-mode = <0>;
+ status = "disabled";
+ };
+
+ eeprom_rear: qcom,eeprom@0 {
+ cell-index = <0>;
+ reg = <0>;
+ compatible = "qcom,eeprom";
+ cam_vio-supply = <&pm8998_lvs1>;
+ cam_vana-supply = <&pmi8998_bob>;
+ cam_vdig-supply = <&camera_rear_ldo>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage = <0 3312000 1050000>;
+ qcom,cam-vreg-max-voltage = <0 3600000 1050000>;
+ qcom,cam-vreg-op-mode = <0 80000 105000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_active
+ &cam_sensor_rear_active>;
+ pinctrl-1 = <&cam_sensor_mclk0_suspend
+ &cam_sensor_rear_suspend>;
+ gpios = <&tlmm 13 0>,
+ <&tlmm 80 0>,
+ <&tlmm 79 0>,
+ <&tlmm 27 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-vaf = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_VANA0",
+ "CAM_VAF";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+ clock-names = "cam_clk";
+ qcom,clock-rates = <24000000>;
+ };
+
+ eeprom_rear_aux: qcom,eeprom@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,eeprom";
+ cam_vdig-supply = <&camera_ldo>;
+ cam_vio-supply = <&pm8998_lvs1>;
+ cam_vana-supply = <&pmi8998_bob>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-min-voltage = <1050000 0 3312000>;
+ qcom,cam-vreg-max-voltage = <1050000 0 3600000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_active
+ &cam_sensor_rear2_active>;
+ pinctrl-1 = <&cam_sensor_mclk2_suspend
+ &cam_sensor_rear2_suspend>;
+ gpios = <&tlmm 15 0>,
+ <&tlmm 9 0>,
+ <&tlmm 8 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_VANA1";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+ clock-names = "cam_clk";
+ qcom,clock-rates = <24000000>;
+ };
+
+ eeprom_front: qcom,eeprom@2 {
+ cell-index = <2>;
+ reg = <0x2>;
+ compatible = "qcom,eeprom";
+ cam_vio-supply = <&pm8998_lvs1>;
+ cam_vana-supply = <&pmi8998_bob>;
+ cam_vdig-supply = <&camera_ldo>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage = <0 3312000 1050000>;
+ qcom,cam-vreg-max-voltage = <0 3600000 1050000>;
+ qcom,cam-vreg-op-mode = <0 80000 105000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_active
+ &cam_sensor_front_active>;
+ pinctrl-1 = <&cam_sensor_mclk1_suspend
+ &cam_sensor_front_suspend>;
+ gpios = <&tlmm 14 0>,
+ <&tlmm 28 0>,
+ <&tlmm 8 0>,
+ <&tlmm 27 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-vaf = <3>;
+ qcom,gpio-req-tbl-num = <0 1 2 3>;
+ qcom,gpio-req-tbl-flags = <1 0 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_VANA2",
+ "CAM_VAF";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_camcc CAM_CC_MCLK1_CLK>;
+ clock-names = "cam_clk";
+ qcom,clock-rates = <24000000>;
+ };
+
+ qcom,cam-sensor@0 {
+ cell-index = <0>;
+ compatible = "qcom,cam-sensor";
+ reg = <0x0>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,sensor-position-roll = <90>;
+ qcom,sensor-position-pitch = <0>;
+ qcom,sensor-position-yaw = <180>;
+ qcom,led-flash-src = <&led_flash_rear>;
+ qcom,actuator-src = <&actuator_rear>;
+ qcom,ois-src = <&ois_rear>;
+ qcom,eeprom-src = <&eeprom_rear>;
+ cam_vio-supply = <&pm8998_lvs1>;
+ cam_vana-supply = <&pmi8998_bob>;
+ cam_vdig-supply = <&camera_rear_ldo>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage = <0 3312000 1050000>;
+ qcom,cam-vreg-max-voltage = <0 3600000 1050000>;
+ qcom,cam-vreg-op-mode = <0 80000 105000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_active
+ &cam_sensor_rear_active>;
+ pinctrl-1 = <&cam_sensor_mclk0_suspend
+ &cam_sensor_rear_suspend>;
+ gpios = <&tlmm 13 0>,
+ <&tlmm 80 0>,
+ <&tlmm 79 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_VANA";
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+ clock-names = "cam_clk";
+ qcom,clock-rates = <24000000>;
+ };
+
+ qcom,cam-sensor@1 {
+ cell-index = <1>;
+ compatible = "qcom,cam-sensor";
+ reg = <0x1>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,sensor-position-roll = <90>;
+ qcom,sensor-position-pitch = <0>;
+ qcom,sensor-position-yaw = <180>;
+ qcom,eeprom-src = <&eeprom_rear_aux>;
+ cam_vdig-supply = <&camera_ldo>;
+ cam_vio-supply = <&pm8998_lvs1>;
+ cam_vana-supply = <&pmi8998_bob>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-min-voltage = <1050000 0 3312000>;
+ qcom,cam-vreg-max-voltage = <1050000 0 3600000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk2_active
+ &cam_sensor_rear2_active>;
+ pinctrl-1 = <&cam_sensor_mclk2_suspend
+ &cam_sensor_rear2_suspend>;
+ gpios = <&tlmm 15 0>,
+ <&tlmm 9 0>,
+ <&tlmm 8 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_VANA1";
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+ clock-names = "cam_clk";
+ qcom,clock-rates = <24000000>;
+ };
+
+ qcom,cam-sensor@2 {
+ cell-index = <2>;
+ compatible = "qcom,cam-sensor";
+ reg = <0x02>;
+ qcom,csiphy-sd-index = <2>;
+ qcom,sensor-position-roll = <90>;
+ qcom,sensor-position-pitch = <0>;
+ qcom,sensor-position-yaw = <0>;
+ qcom,eeprom-src = <&eeprom_front>;
+ qcom,actuator-src = <&actuator_front>;
+ qcom,led-flash-src = <&led_flash_front>;
+ cam_vio-supply = <&pm8998_lvs1>;
+ cam_vana-supply = <&pmi8998_bob>;
+ cam_vdig-supply = <&camera_ldo>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage = <0 3312000 1050000>;
+ qcom,cam-vreg-max-voltage = <0 3600000 1050000>;
+ qcom,cam-vreg-op-mode = <0 80000 105000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk1_active
+ &cam_sensor_front_active>;
+ pinctrl-1 = <&cam_sensor_mclk1_suspend
+ &cam_sensor_front_suspend>;
+ gpios = <&tlmm 14 0>,
+ <&tlmm 28 0>,
+ <&tlmm 8 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vana = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_VANA1";
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_camcc CAM_CC_MCLK1_CLK>;
+ clock-names = "cam_clk";
+ qcom,clock-rates = <24000000>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
index c197d65..cd9c8a8 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
@@ -16,4 +16,213 @@
compatible = "qcom,cam-req-mgr";
status = "ok";
};
+
+ qcom,csiphy@ac65000 {
+ cell-index = <0>;
+ compatible = "qcom,csiphy-v1.0", "qcom,csiphy";
+ reg = <0x0ac65000 0x1000>;
+ reg-names = "csiphy";
+ interrupts = <0 477 0>;
+ interrupt-names = "csiphy";
+ gdscr-supply = <&titan_top_gdsc>;
+ qcom,cam-vreg-name = "gdscr";
+ qcom,csi-vdd-voltage = <1200000>;
+ qcom,mipi-csi-vdd-supply = <&pm8998_l26>;
+ clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+ <&clock_camcc CAM_CC_SOC_AHB_CLK>,
+ <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+ <&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+ <&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>,
+ <&clock_camcc CAM_CC_CSIPHY0_CLK>,
+ <&clock_camcc CAM_CC_CSI0PHYTIMER_CLK_SRC>,
+ <&clock_camcc CAM_CC_CSI0PHYTIMER_CLK>,
+ <&clock_camcc CAM_CC_IFE_0_CSID_CLK>,
+ <&clock_camcc CAM_CC_IFE_0_CSID_CLK_SRC>;
+ clock-names = "camnoc_axi_clk",
+ "soc_ahb_clk",
+ "slow_ahb_src_clk",
+ "cpas_ahb_clk",
+ "cphy_rx_clk_src",
+ "csiphy0_clk",
+ "csi0phytimer_clk_src",
+ "csi0phytimer_clk",
+ "ife_0_csid_clk",
+ "ife_0_csid_clk_src";
+ qcom,clock-rates =
+ <0 0 80000000 0 320000000 0 269333333 0 0 384000000>;
+ status = "ok";
+ };
+
+ qcom,csiphy@ac66000{
+ cell-index = <1>;
+ compatible = "qcom,csiphy-v1.0", "qcom,csiphy";
+ reg = <0xac66000 0x1000>;
+ reg-names = "csiphy";
+ interrupts = <0 478 0>;
+ interrupt-names = "csiphy";
+ gdscr-supply = <&titan_top_gdsc>;
+ qcom,cam-vreg-name = "gdscr";
+ qcom,csi-vdd-voltage = <1200000>;
+ qcom,mipi-csi-vdd-supply = <&pm8998_l26>;
+ clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+ <&clock_camcc CAM_CC_SOC_AHB_CLK>,
+ <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+ <&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+ <&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>,
+ <&clock_camcc CAM_CC_CSIPHY1_CLK>,
+ <&clock_camcc CAM_CC_CSI1PHYTIMER_CLK_SRC>,
+ <&clock_camcc CAM_CC_CSI0PHYTIMER_CLK>,
+ <&clock_camcc CAM_CC_IFE_1_CSID_CLK>,
+ <&clock_camcc CAM_CC_IFE_1_CSID_CLK_SRC>;
+ clock-names = "camnoc_axi_clk",
+ "soc_ahb_clk",
+ "slow_ahb_src_clk",
+ "cpas_ahb_clk",
+ "cphy_rx_clk_src",
+ "csiphy1_clk",
+ "csi1phytimer_clk_src",
+ "csi1phytimer_clk",
+ "ife_1_csid_clk",
+ "ife_1_csid_clk_src";
+ qcom,clock-rates =
+ <0 0 80000000 0 320000000 0 269333333 0 0 384000000>;
+
+ status = "ok";
+ };
+
+ qcom,csiphy@ac67000 {
+ cell-index = <2>;
+ compatible = "qcom,csiphy-v1.0", "qcom,csiphy";
+ reg = <0xac67000 0x1000>;
+ reg-names = "csiphy";
+ interrupts = <0 479 0>;
+ interrupt-names = "csiphy";
+ gdscr-supply = <&titan_top_gdsc>;
+ qcom,cam-vreg-name = "gdscr";
+ qcom,csi-vdd-voltage = <1200000>;
+ qcom,mipi-csi-vdd-supply = <&pm8998_l26>;
+ clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+ <&clock_camcc CAM_CC_SOC_AHB_CLK>,
+ <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+ <&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+ <&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>,
+ <&clock_camcc CAM_CC_CSIPHY2_CLK>,
+ <&clock_camcc CAM_CC_CSI2PHYTIMER_CLK_SRC>,
+ <&clock_camcc CAM_CC_CSI2PHYTIMER_CLK>,
+ <&clock_camcc CAM_CC_IFE_LITE_CSID_CLK>,
+ <&clock_camcc CAM_CC_IFE_LITE_CSID_CLK_SRC>;
+ clock-names = "camnoc_axi_clk",
+ "soc_ahb_clk",
+ "slow_ahb_src_clk",
+ "cpas_ahb_clk",
+ "cphy_rx_clk_src",
+ "csiphy2_clk",
+ "csi2phytimer_clk_src",
+ "csi2phytimer_clk",
+ "ife_lite_csid_clk",
+ "ife_lite_csid_clk_src";
+ qcom,clock-rates =
+ <0 0 80000000 0 320000000 0 269333333 0 0 384000000>;
+ status = "ok";
+ };
+
+ cci: qcom,cci@ac4a000 {
+ cell-index = <0>;
+ compatible = "qcom,cci";
+ reg = <0xac4a000 0x4000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "cci";
+ interrupts = <0 460 0>;
+ interrupt-names = "cci";
+ status = "ok";
+ gdscr-supply = <&titan_top_gdsc>;
+ qcom,cam-vreg-name = "gdscr";
+ clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+ <&clock_camcc CAM_CC_SOC_AHB_CLK>,
+ <&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+ <&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+ <&clock_camcc CAM_CC_CCI_CLK>,
+ <&clock_camcc CAM_CC_CCI_CLK_SRC>;
+ clock-names = "camnoc_axi_clk",
+ "soc_ahb_clk",
+ "slow_ahb_src_clk",
+ "cpas_ahb_clk",
+ "cci_clk",
+ "cci_clk_src";
+ qcom,clock-rates = <0 0 80000000 0 0 37500000>;
+ pinctrl-names = "cci_default", "cci_suspend";
+ pinctrl-0 = <&cci0_active &cci1_active>;
+ pinctrl-1 = <&cci0_suspend &cci1_suspend>;
+ gpios = <&tlmm 17 0>,
+ <&tlmm 18 0>,
+ <&tlmm 19 0>,
+ <&tlmm 20 0>;
+ qcom,gpio-tbl-num = <0 1 2 3>;
+ qcom,gpio-tbl-flags = <1 1 1 1>;
+ qcom,gpio-tbl-label = "CCI_I2C_DATA0",
+ "CCI_I2C_CLK0",
+ "CCI_I2C_DATA1",
+ "CCI_I2C_CLK1";
+
+ i2c_freq_100Khz: qcom,i2c_standard_mode {
+ qcom,hw-thigh = <201>;
+ qcom,hw-tlow = <174>;
+ qcom,hw-tsu-sto = <204>;
+ qcom,hw-tsu-sta = <231>;
+ qcom,hw-thd-dat = <22>;
+ qcom,hw-thd-sta = <162>;
+ qcom,hw-tbuf = <227>;
+ qcom,hw-scl-stretch-en = <0>;
+ qcom,hw-trdhld = <6>;
+ qcom,hw-tsp = <3>;
+ qcom,cci-clk-src = <37500000>;
+ status = "ok";
+ };
+
+ i2c_freq_400Khz: qcom,i2c_fast_mode {
+ qcom,hw-thigh = <38>;
+ qcom,hw-tlow = <56>;
+ qcom,hw-tsu-sto = <40>;
+ qcom,hw-tsu-sta = <40>;
+ qcom,hw-thd-dat = <22>;
+ qcom,hw-thd-sta = <35>;
+ qcom,hw-tbuf = <62>;
+ qcom,hw-scl-stretch-en = <0>;
+ qcom,hw-trdhld = <6>;
+ qcom,hw-tsp = <3>;
+ qcom,cci-clk-src = <37500000>;
+ status = "ok";
+ };
+
+ i2c_freq_custom: qcom,i2c_custom_mode {
+ qcom,hw-thigh = <38>;
+ qcom,hw-tlow = <56>;
+ qcom,hw-tsu-sto = <40>;
+ qcom,hw-tsu-sta = <40>;
+ qcom,hw-thd-dat = <22>;
+ qcom,hw-thd-sta = <35>;
+ qcom,hw-tbuf = <62>;
+ qcom,hw-scl-stretch-en = <1>;
+ qcom,hw-trdhld = <6>;
+ qcom,hw-tsp = <3>;
+ qcom,cci-clk-src = <37500000>;
+ status = "ok";
+ };
+
+ i2c_freq_1Mhz: qcom,i2c_fast_plus_mode {
+ qcom,hw-thigh = <16>;
+ qcom,hw-tlow = <22>;
+ qcom,hw-tsu-sto = <17>;
+ qcom,hw-tsu-sta = <18>;
+ qcom,hw-thd-dat = <16>;
+ qcom,hw-thd-sta = <15>;
+ qcom,hw-tbuf = <24>;
+ qcom,hw-scl-stretch-en = <0>;
+ qcom,hw-trdhld = <3>;
+ qcom,hw-tsp = <3>;
+ qcom,cci-clk-src = <37500000>;
+ status = "ok";
+ };
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
index 3f0cf77..e98a973 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
@@ -11,6 +11,30 @@
*/
#include <dt-bindings/gpio/gpio.h>
+#include "sdm845-camera-sensor-cdp.dtsi"
+
+/ {
+ bluetooth: bt_wcn3990 {
+ compatible = "qca,wcn3990";
+ qca,bt-vdd-io-supply = <&pm8998_s3>;
+ qca,bt-vdd-xtal-supply = <&pm8998_s5>;
+ qca,bt-vdd-core-supply = <&pm8998_l7>;
+ qca,bt-vdd-pa-supply = <&pm8998_l17>;
+ qca,bt-vdd-ldo-supply = <&pm8998_l25>;
+
+ qca,bt-vdd-io-voltage-level = <1352000 1352000>;
+ qca,bt-vdd-xtal-voltage-level = <2040000 2040000>;
+ qca,bt-vdd-core-voltage-level = <1800000 1800000>;
+ qca,bt-vdd-pa-voltage-level = <1304000 1304000>;
+ qca,bt-vdd-ldo-voltage-level = <3312000 3312000>;
+
+ qca,bt-vdd-io-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-xtal-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-core-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-pa-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-ldo-current-level = <1>; /* LPM/PFM */
+ };
+};
&soc {
sound-tavil {
@@ -119,6 +143,26 @@
status = "ok";
};
+&sdhc_2 {
+ vdd-supply = <&pm8998_l21>;
+ qcom,vdd-voltage-level = <2950000 2960000>;
+ qcom,vdd-current-level = <200 800000>;
+
+ vdd-io-supply = <&pm8998_l13>;
+ qcom,vdd-io-voltage-level = <1808000 2960000>;
+ qcom,vdd-io-current-level = <200 22000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off>;
+
+ qcom,clk-rates = <400000 20000000 25000000
+ 50000000 100000000 200000000>;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+
+ status = "ok";
+};
+
&pmi8998_switch1 {
pinctrl-names = "led_enable", "led_disable";
pinctrl-0 = <&flash_led3_front_en>;
@@ -130,6 +174,29 @@
};
/ {
+ extcon_usb1: extcon_usb1 {
+ compatible = "linux,extcon-usb-gpio";
+ id-gpio = <&pmi8998_gpios 9 GPIO_ACTIVE_HIGH>;
+ vbus-gpio = <&pmi8998_gpios 8 GPIO_ACTIVE_HIGH>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&usb2_vbus_det_default
+ &usb2_id_det_default>;
+ };
+
+ usb1_vbus_vreg: usb1_vbus_vreg {
+ compatible = "regulator-fixed";
+ regulator-name = "usb_vbus_vreg";
+ gpio = <&pmi8998_gpios 2 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+
+ /* Typical EN-to-VBUS turn on time for NX5P1100 */
+ regulator-enable-ramp-delay = <630>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&usb2_vbus_boost_default>;
+ };
+
aliases {
serial0 = &qupv3_se9_2uart;
spi0 = &qupv3_se8_spi;
@@ -153,3 +220,141 @@
&qupv3_se10_i2c {
status = "ok";
};
+
+&usb1 {
+ status = "okay";
+ extcon = <&extcon_usb1>;
+ vbus_dwc3-supply = <&usb1_vbus_vreg>;
+};
+
+&qusb_phy1 {
+ status = "okay";
+};
+
+&usb_qmp_phy {
+ status = "okay";
+};
+
+&pm8998_vadc {
+ chan@83 {
+ label = "vph_pwr";
+ reg = <0x83>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@85 {
+ label = "vcoin";
+ reg = <0x85>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@4c {
+ label = "xo_therm";
+ reg = <0x4c>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@4d {
+ label = "msm_therm";
+ reg = <0x4d>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@4f {
+ label = "pa_therm1";
+ reg = <0x4f>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@51 {
+ label = "quiet_therm";
+ reg = <0x51>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+};
+
+&pm8998_adc_tm {
+ chan@83 {
+ label = "vph_pwr";
+ reg = <0x83>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,btm-channel-number = <0x60>;
+ };
+
+ chan@4d {
+ label = "msm_therm";
+ reg = <0x4d>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x68>;
+ qcom,thermal-node;
+ };
+
+ chan@51 {
+ label = "quiet_therm";
+ reg = <0x51>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x70>;
+ qcom,thermal-node;
+ };
+
+ chan@4c {
+ label = "xo_therm";
+ reg = <0x4c>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x78>;
+ qcom,thermal-node;
+ };
+
+ chan@4f {
+ label = "pa_therm1";
+ reg = <0x4f>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x80>;
+ qcom,thermal-node;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi b/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
index a3adcec..1a0ec0b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
@@ -511,6 +511,7 @@
qcom,dsb-elem-size = <0 32>,
<2 32>,
<3 32>,
+ <5 32>,
<10 32>,
<11 32>,
<13 32>;
@@ -562,6 +563,15 @@
};
port@4 {
+ reg = <5>;
+ tpda_in_funnel_lpass: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_lpass_out_tpda>;
+ };
+ };
+
+ port@5 {
reg = <7>;
tpda_in_tpdm_vsense: endpoint {
slave-mode;
@@ -570,7 +580,7 @@
};
};
- port@5 {
+ port@6 {
reg = <10>;
tpda_in_tpdm_qm: endpoint {
slave-mode;
@@ -579,7 +589,7 @@
};
};
- port@6 {
+ port@7 {
reg = <11>;
tpda_in_tpdm_north: endpoint {
slave-mode;
@@ -588,7 +598,7 @@
};
};
- port@7 {
+ port@8 {
reg = <13>;
tpda_in_tpdm_pimem: endpoint {
slave-mode;
@@ -690,6 +700,60 @@
};
};
+ funnel_lpass: funnel@6845000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
+ reg = <0x6845000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-lpass";
+
+ clocks = <&clock_gcc RPMH_QDSS_CLK>,
+ <&clock_gcc RPMH_QDSS_A_CLK>;
+ clock-names = "apb_pclk", "core_a_clk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ funnel_lpass_out_tpda: endpoint {
+ remote-endpoint =
+ <&tpda_in_funnel_lpass>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ funnel_lpass_in_tpdm_lpass: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpdm_lpass_out_funnel_lpass>;
+ };
+ };
+ };
+ };
+
+ tpdm_lpass: tpdm@6844000 {
+ compatible = "qcom,coresight-tpdm";
+ reg = <0x6844000 0x1000>;
+ reg-names = "tpdm-base";
+
+ coresight-name = "coresight-tpdm-lpass";
+
+ clocks = <&clock_gcc RPMH_QDSS_CLK>,
+ <&clock_gcc RPMH_QDSS_A_CLK>;
+ clock-names = "core_clk", "core_a_clk";
+
+ port {
+ tpdm_lpass_out_funnel_lpass: endpoint {
+ remote-endpoint = <&funnel_lpass_in_tpdm_lpass>;
+ };
+ };
+ };
+
tpdm_center: tpdm@6c28000 {
compatible = "qcom,coresight-tpdm";
reg = <0x6c28000 0x1000>;
@@ -1615,6 +1679,166 @@
};
};
+ etm0: etm@7040000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb95d>;
+
+ reg = <0x7040000 0x1000>;
+ cpu = <&CPU0>;
+
+ coresight-name = "coresight-etm0";
+
+ clocks = <&clock_gcc RPMH_QDSS_CLK>,
+ <&clock_gcc RPMH_QDSS_A_CLK>;
+ clock-names = "apb_pclk", "core_a_clk";
+
+ port {
+ etm0_out_funnel_apss: endpoint {
+ remote-endpoint = <&funnel_apss_in_etm0>;
+ };
+ };
+ };
+
+ etm1: etm@7140000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb95d>;
+
+ reg = <0x7140000 0x1000>;
+ cpu = <&CPU1>;
+
+ coresight-name = "coresight-etm1";
+
+ clocks = <&clock_gcc RPMH_QDSS_CLK>,
+ <&clock_gcc RPMH_QDSS_A_CLK>;
+ clock-names = "apb_pclk", "core_a_clk";
+
+ port {
+ etm1_out_funnel_apss: endpoint {
+ remote-endpoint = <&funnel_apss_in_etm1>;
+ };
+ };
+ };
+
+ etm2: etm@7240000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb95d>;
+
+ reg = <0x7240000 0x1000>;
+ cpu = <&CPU2>;
+
+ coresight-name = "coresight-etm2";
+
+ clocks = <&clock_gcc RPMH_QDSS_CLK>,
+ <&clock_gcc RPMH_QDSS_A_CLK>;
+ clock-names = "apb_pclk", "core_a_clk";
+
+ port {
+ etm2_out_funnel_apss: endpoint {
+ remote-endpoint = <&funnel_apss_in_etm2>;
+ };
+ };
+ };
+
+ etm3: etm@7340000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb95d>;
+
+ reg = <0x7340000 0x1000>;
+ cpu = <&CPU3>;
+
+ coresight-name = "coresight-etm3";
+
+ clocks = <&clock_gcc RPMH_QDSS_CLK>,
+ <&clock_gcc RPMH_QDSS_A_CLK>;
+ clock-names = "apb_pclk", "core_a_clk";
+
+ port {
+ etm3_out_funnel_apss: endpoint {
+ remote-endpoint = <&funnel_apss_in_etm3>;
+ };
+ };
+ };
+
+ etm4: etm@7440000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb95d>;
+
+ reg = <0x7440000 0x1000>;
+ cpu = <&CPU4>;
+
+ coresight-name = "coresight-etm4";
+
+ clocks = <&clock_gcc RPMH_QDSS_CLK>,
+ <&clock_gcc RPMH_QDSS_A_CLK>;
+ clock-names = "apb_pclk", "core_a_clk";
+
+ port {
+ etm4_out_funnel_apss: endpoint {
+ remote-endpoint = <&funnel_apss_in_etm4>;
+ };
+ };
+ };
+
+ etm5: etm@7540000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb95d>;
+
+ reg = <0x7540000 0x1000>;
+ cpu = <&CPU5>;
+
+ coresight-name = "coresight-etm5";
+
+ clocks = <&clock_gcc RPMH_QDSS_CLK>,
+ <&clock_gcc RPMH_QDSS_A_CLK>;
+ clock-names = "apb_pclk", "core_a_clk";
+
+ port {
+ etm5_out_funnel_apss: endpoint {
+ remote-endpoint = <&funnel_apss_in_etm5>;
+ };
+ };
+ };
+
+ etm6: etm@7640000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb95d>;
+
+ reg = <0x7640000 0x1000>;
+ cpu = <&CPU6>;
+
+ coresight-name = "coresight-etm6";
+
+ clocks = <&clock_gcc RPMH_QDSS_CLK>,
+ <&clock_gcc RPMH_QDSS_A_CLK>;
+ clock-names = "apb_pclk", "core_a_clk";
+
+ port {
+ etm6_out_funnel_apss: endpoint {
+ remote-endpoint = <&funnel_apss_in_etm6>;
+ };
+ };
+ };
+
+ etm7: etm@7740000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x000bb95d>;
+
+ reg = <0x7740000 0x1000>;
+ cpu = <&CPU7>;
+
+ coresight-name = "coresight-etm7";
+
+ clocks = <&clock_gcc RPMH_QDSS_CLK>,
+ <&clock_gcc RPMH_QDSS_A_CLK>;
+ clock-names = "apb_pclk", "core_a_clk";
+
+ port {
+ etm7_out_funnel_apss: endpoint {
+ remote-endpoint = <&funnel_apss_in_etm7>;
+ };
+ };
+ };
+
funnel_apss: funnel@7800000 {
compatible = "arm,primecell";
arm,primecell-periphid = <0x0003b908>;
@@ -1639,6 +1863,77 @@
<&funnel_apss_merg_in_funnel_apss>;
};
};
+ port@1 {
+ reg = <0>;
+ funnel_apss_in_etm0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm0_out_funnel_apss>;
+ };
+ };
+
+ port@2 {
+ reg = <1>;
+ funnel_apss_in_etm1: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm1_out_funnel_apss>;
+ };
+ };
+
+ port@3 {
+ reg = <2>;
+ funnel_apss_in_etm2: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm2_out_funnel_apss>;
+ };
+ };
+
+ port@4 {
+ reg = <3>;
+ funnel_apss_in_etm3: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm3_out_funnel_apss>;
+ };
+ };
+
+ port@5 {
+ reg = <4>;
+ funnel_apss_in_etm4: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm4_out_funnel_apss>;
+ };
+ };
+
+ port@6 {
+ reg = <5>;
+ funnel_apss_in_etm5: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm5_out_funnel_apss>;
+ };
+ };
+
+ port@7 {
+ reg = <6>;
+ funnel_apss_in_etm6: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm6_out_funnel_apss>;
+ };
+ };
+
+ port@8 {
+ reg = <7>;
+ funnel_apss_in_etm7: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&etm7_out_funnel_apss>;
+ };
+ };
};
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
index 3f2317a..bfbaabb 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
@@ -74,6 +74,7 @@
qcom,gpu-qdss-stm = <0x161c0000 0x40000>; // base addr, size
qcom,tsens-name = "tsens_tz_sensor12";
+ #cooling-cells = <2>;
clocks = <&clock_gfx GPU_CC_GX_GFX3D_CLK>,
<&clock_gcc GCC_GPU_CFG_AHB_CLK>,
diff --git a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
index ca89f38..6a4f19a 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
@@ -11,6 +11,30 @@
*/
#include <dt-bindings/gpio/gpio.h>
+#include "sdm845-camera-sensor-mtp.dtsi"
+
+/ {
+ bluetooth: bt_wcn3990 {
+ compatible = "qca,wcn3990";
+ qca,bt-vdd-io-supply = <&pm8998_s3>;
+ qca,bt-vdd-xtal-supply = <&pm8998_s5>;
+ qca,bt-vdd-core-supply = <&pm8998_l7>;
+ qca,bt-vdd-pa-supply = <&pm8998_l17>;
+ qca,bt-vdd-ldo-supply = <&pm8998_l25>;
+
+ qca,bt-vdd-io-voltage-level = <1352000 1352000>;
+ qca,bt-vdd-xtal-voltage-level = <2040000 2040000>;
+ qca,bt-vdd-core-voltage-level = <1800000 1800000>;
+ qca,bt-vdd-pa-voltage-level = <1304000 1304000>;
+ qca,bt-vdd-ldo-voltage-level = <3312000 3312000>;
+
+ qca,bt-vdd-io-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-xtal-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-core-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-pa-current-level = <1>; /* LPM/PFM */
+ qca,bt-vdd-ldo-current-level = <1>; /* LPM/PFM */
+ };
+};
&soc {
gpio_keys {
@@ -104,6 +128,26 @@
status = "ok";
};
+&sdhc_2 {
+ vdd-supply = <&pm8998_l21>;
+ qcom,vdd-voltage-level = <2950000 2960000>;
+ qcom,vdd-current-level = <200 800000>;
+
+ vdd-io-supply = <&pm8998_l13>;
+ qcom,vdd-io-voltage-level = <1808000 2960000>;
+ qcom,vdd-io-current-level = <200 22000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off>;
+
+ qcom,clk-rates = <400000 20000000 25000000
+ 50000000 100000000 200000000>;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+
+ status = "ok";
+};
+
&pmi8998_switch1 {
pinctrl-names = "led_enable", "led_disable";
pinctrl-0 = <&flash_led3_front_en>;
@@ -117,6 +161,14 @@
#include "fg-gen3-batterydata-ascent-3450mah.dtsi"
#include "fg-gen3-batterydata-demo-6000mah.dtsi"
};
+
+ extcon_usb1: extcon_usb1 {
+ compatible = "linux,extcon-usb-gpio";
+ vbus-gpio = <&pmi8998_gpios 8 GPIO_ACTIVE_HIGH>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&usb2_vbus_det_default>;
+ };
};
&pmi8998_fg {
@@ -147,3 +199,140 @@
&qupv3_se10_i2c {
status = "ok";
};
+
+&usb1 {
+ status = "okay";
+ extcon = <&extcon_usb1>;
+};
+
+&qusb_phy1 {
+ status = "okay";
+};
+
+&usb_qmp_phy {
+ status = "okay";
+};
+
+&pm8998_vadc {
+ chan@83 {
+ label = "vph_pwr";
+ reg = <0x83>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@85 {
+ label = "vcoin";
+ reg = <0x85>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@4c {
+ label = "xo_therm";
+ reg = <0x4c>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@4d {
+ label = "msm_therm";
+ reg = <0x4d>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@4f {
+ label = "pa_therm1";
+ reg = <0x4f>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@51 {
+ label = "quiet_therm";
+ reg = <0x51>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ };
+};
+
+&pm8998_adc_tm {
+ chan@83 {
+ label = "vph_pwr";
+ reg = <0x83>;
+ qcom,pre-div-channel-scaling = <1>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,btm-channel-number = <0x60>;
+ };
+
+ chan@4d {
+ label = "msm_therm";
+ reg = <0x4d>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x68>;
+ qcom,thermal-node;
+ };
+
+ chan@51 {
+ label = "quiet_therm";
+ reg = <0x51>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x70>;
+ qcom,thermal-node;
+ };
+
+ chan@4c {
+ label = "xo_therm";
+ reg = <0x4c>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x78>;
+ qcom,thermal-node;
+ };
+
+ chan@4f {
+ label = "pa_therm1";
+ reg = <0x4f>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,btm-channel-number = <0x80>;
+ qcom,thermal-node;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
index aa96cec..d6af58b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
@@ -112,6 +112,54 @@
};
};
+ sdc2_clk_on: sdc2_clk_on {
+ config {
+ pins = "sdc2_clk";
+ bias-disable; /* NO pull */
+ drive-strength = <16>; /* 16 MA */
+ };
+ };
+
+ sdc2_clk_off: sdc2_clk_off {
+ config {
+ pins = "sdc2_clk";
+ bias-disable; /* NO pull */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ sdc2_cmd_on: sdc2_cmd_on {
+ config {
+ pins = "sdc2_cmd";
+ bias-pull-up; /* pull up */
+ drive-strength = <10>; /* 10 MA */
+ };
+ };
+
+ sdc2_cmd_off: sdc2_cmd_off {
+ config {
+ pins = "sdc2_cmd";
+ bias-pull-up; /* pull up */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ sdc2_data_on: sdc2_data_on {
+ config {
+ pins = "sdc2_data";
+ bias-pull-up; /* pull up */
+ drive-strength = <10>; /* 10 MA */
+ };
+ };
+
+ sdc2_data_off: sdc2_data_off {
+ config {
+ pins = "sdc2_data";
+ bias-pull-up; /* pull up */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
cdc_reset_ctrl {
cdc_reset_sleep: cdc_reset_sleep {
mux {
@@ -2285,6 +2333,231 @@
};
};
};
+
+ cci0_active: cci0_active {
+ mux {
+ /* CLK, DATA */
+ pins = "gpio17","gpio18"; // Only 2
+ function = "cci_i2c";
+ };
+
+ config {
+ pins = "gpio17","gpio18";
+ bias-pull-up; /* PULL UP*/
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cci0_suspend: cci0_suspend {
+ mux {
+ /* CLK, DATA */
+ pins = "gpio17","gpio18";
+ function = "cci_i2c";
+ };
+
+ config {
+ pins = "gpio17","gpio18";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cci1_active: cci1_active {
+ mux {
+ /* CLK, DATA */
+ pins = "gpio19","gpio20";
+ function = "cci_i2c";
+ };
+
+ config {
+ pins = "gpio19","gpio20";
+ bias-pull-up; /* PULL UP*/
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cci1_suspend: cci1_suspend {
+ mux {
+ /* CLK, DATA */
+ pins = "gpio19","gpio20";
+ function = "cci_i2c";
+ };
+
+ config {
+ pins = "gpio19","gpio20";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_mclk0_active: cam_sensor_mclk0_active {
+ /* MCLK0 */
+ mux {
+ pins = "gpio13";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio13";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_mclk0_suspend: cam_sensor_mclk0_suspend {
+ /* MCLK0 */
+ mux {
+ pins = "gpio13";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio13";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_rear_active: cam_sensor_rear_active {
+ /* RESET, AVDD LDO */
+ mux {
+ pins = "gpio80","gpio79";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio80","gpio79";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_rear_suspend: cam_sensor_rear_suspend {
+ /* RESET, AVDD LDO */
+ mux {
+ pins = "gpio80","gpio79";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio80","gpio79";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_mclk1_active: cam_sensor_mclk1_active {
+ /* MCLK1 */
+ mux {
+ pins = "gpio14";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio14";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_mclk1_suspend: cam_sensor_mclk1_suspend {
+ /* MCLK1 */
+ mux {
+ pins = "gpio14";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio14";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_front_active: cam_sensor_front_active {
+ /* RESET AVDD_LDO*/
+ mux {
+ pins = "gpio28", "gpio8";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio28", "gpio8";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_front_suspend: cam_sensor_front_suspend {
+ /* RESET */
+ mux {
+ pins = "gpio28";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio28";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_mclk2_active: cam_sensor_mclk2_active {
+ /* MCLK1 */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio15";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio15";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_mclk2_suspend: cam_sensor_mclk2_suspend {
+ /* MCLK1 */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio15";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio15";
+ bias-pull-down; /* PULL DOWN */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_rear2_active: cam_sensor_rear2_active {
+ /* RESET, STANDBY */
+ mux {
+ pins = "gpio9","gpio8";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio9","gpio8";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_rear2_suspend: cam_sensor_rear2_suspend {
+ /* RESET, STANDBY */
+ mux {
+ pins = "gpio9","gpio8";
+ function = "gpio";
+ };
+ config {
+ pins = "gpio9","gpio8";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
};
};
@@ -2328,4 +2601,53 @@
power-source = <0>;
};
};
+
+ camera_dvdd_en {
+ camera_dvdd_en_default: camera_dvdd_en_default {
+ pins = "gpio9";
+ function = "normal";
+ power-source = <0>;
+ output-low;
+ };
+ };
+
+ camera_rear_dvdd_en {
+ camera_rear_dvdd_en_default: camera_rear_dvdd_en_default {
+ pins = "gpio12";
+ function = "normal";
+ power-source = <0>;
+ output-low;
+ };
+ };
+};
+
+&pmi8998_gpios {
+ usb2_vbus_boost {
+ usb2_vbus_boost_default: usb2_vbus_boost_default {
+ pins = "gpio2";
+ function = "normal";
+ output-low;
+ power-source = <0>;
+ };
+ };
+
+ usb2_vbus_det {
+ usb2_vbus_det_default: usb2_vbus_det_default {
+ pins = "gpio8";
+ function = "normal";
+ input-enable;
+ bias-pull-down;
+ power-source = <1>; /* VPH input supply */
+ };
+ };
+
+ usb2_id_det {
+ usb2_id_det_default: usb2_id_det_default {
+ pins = "gpio9";
+ function = "normal";
+ input-enable;
+ bias-pull-up;
+ power-source = <0>;
+ };
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
index ba725cb..1d5bf3a 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
@@ -21,3 +21,11 @@
&pmi8998_fg {
qcom,battery-data = <&qrd_batterydata>;
};
+
+&soc {
+ sound-tavil {
+ qcom,wsa-max-devs = <1>;
+ qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0213>;
+ qcom,wsa-aux-dev-prefix = "SpkrRight", "SpkrRight";
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
index eba6d77..6989f326 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
@@ -28,6 +28,32 @@
};
};
+&spmi_bus {
+ qcom,pm8998@1 {
+ /* PM8998 S12 + S11 + S10 = VDD_APC1 supply */
+ pm8998_s12: regulator@3500 {
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x3500 0x100>;
+ regulator-name = "pm8998_s12";
+ regulator-min-microvolt = <568000>;
+ regulator-max-microvolt = <1056000>;
+ qcom,enable-time = <500>;
+ regulator-always-on;
+ };
+
+ /* PM8998 S13 = VDD_APC0 supply */
+ pm8998_s13: regulator@3800 {
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x3800 0x100>;
+ regulator-name = "pm8998_s13";
+ regulator-min-microvolt = <568000>;
+ regulator-max-microvolt = <928000>;
+ qcom,enable-time = <500>;
+ regulator-always-on;
+ };
+ };
+};
+
&soc {
/* CPR controller regulators */
apc0_cpr: cprh-ctrl@17dc0000 {
@@ -59,7 +85,10 @@
qcom,cpr-saw-use-unit-mV;
qcom,saw-avs-ctrl = <0x101C031>;
- qcom,saw-avs-limit = <0x3A00000>;
+ qcom,saw-avs-limit = <0x3A003A0>;
+
+ qcom,cpr-enable;
+ qcom,cpr-hw-closed-loop;
qcom,cpr-panic-reg-addr-list =
<0x17dc3a84 0x17dc3a88 0x17840c18>;
@@ -68,6 +97,9 @@
"APSS_SILVER_CPRH_STATUS_1",
"SILVER_SAW4_PMIC_STS";
+ qcom,cpr-aging-ref-voltage = <928000>;
+ vdd-supply = <&pm8998_s13>;
+
thread@1 {
qcom,cpr-thread-id = <1>;
qcom,cpr-consecutive-up = <0>;
@@ -134,6 +166,14 @@
qcom,allow-voltage-interpolation;
qcom,allow-quotient-interpolation;
qcom,cpr-scaled-open-loop-voltage-as-ceiling;
+
+ qcom,cpr-aging-max-voltage-adjustment = <15000>;
+ qcom,cpr-aging-ref-corner = <17>;
+ qcom,cpr-aging-ro-scaling-factor = <1620>;
+ qcom,allow-aging-voltage-adjustment =
+ <0 1 1 1 1 1 1 1>;
+ qcom,allow-aging-open-loop-voltage-adjustment =
+ <1>;
};
};
@@ -194,6 +234,14 @@
qcom,allow-voltage-interpolation;
qcom,allow-quotient-interpolation;
qcom,cpr-scaled-open-loop-voltage-as-ceiling;
+
+ qcom,cpr-aging-max-voltage-adjustment = <15000>;
+ qcom,cpr-aging-ref-corner = <9>;
+ qcom,cpr-aging-ro-scaling-factor = <1620>;
+ qcom,allow-aging-voltage-adjustment =
+ <0 1 1 1 1 1 1 1>;
+ qcom,allow-aging-open-loop-voltage-adjustment =
+ <1>;
};
};
};
@@ -232,13 +280,19 @@
qcom,cpr-saw-use-unit-mV;
qcom,saw-avs-ctrl = <0x101C031>;
- qcom,saw-avs-limit = <0x4200000>;
+ qcom,saw-avs-limit = <0x4200420>;
+
+ qcom,cpr-enable;
+ qcom,cpr-hw-closed-loop;
qcom,cpr-panic-reg-addr-list =
<0x17db3a84 0x17830c18>;
qcom,cpr-panic-reg-name-list =
"APSS_GOLD_CPRH_STATUS_0", "GOLD_SAW4_PMIC_STS";
+ qcom,cpr-aging-ref-voltage = <1056000>;
+ vdd-supply = <&pm8998_s12>;
+
thread@0 {
qcom,cpr-thread-id = <0>;
qcom,cpr-consecutive-up = <0>;
@@ -311,6 +365,14 @@
qcom,allow-voltage-interpolation;
qcom,allow-quotient-interpolation;
qcom,cpr-scaled-open-loop-voltage-as-ceiling;
+
+ qcom,cpr-aging-max-voltage-adjustment = <15000>;
+ qcom,cpr-aging-ref-corner = <22>;
+ qcom,cpr-aging-ro-scaling-factor = <1700>;
+ qcom,allow-aging-voltage-adjustment =
+ <0 1 1 1 1 1 1 1>;
+ qcom,allow-aging-open-loop-voltage-adjustment =
+ <1>;
};
};
};
@@ -459,6 +521,7 @@
regulator-max-microvolt = <1200000>;
qcom,init-voltage = <1200000>;
qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
+ regulator-always-on;
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi b/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
index 3ec83f5..6991b17 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
@@ -44,6 +44,26 @@
status = "ok";
};
+&sdhc_2 {
+ vdd-supply = <&pm8998_l21>;
+ qcom,vdd-voltage-level = <2950000 2960000>;
+ qcom,vdd-current-level = <200 800000>;
+
+ vdd-io-supply = <&pm8998_l13>;
+ qcom,vdd-io-voltage-level = <1808000 2960000>;
+ qcom,vdd-io-current-level = <200 22000>;
+
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off>;
+
+ qcom,clk-rates = <400000 20000000 25000000
+ 50000000 100000000 200000000>;
+ qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+
+ status = "ok";
+};
+
&soc {
qcom,icnss@18800000 {
compatible = "qcom,icnss";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
index 7c310cd..c80343a 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
@@ -12,6 +12,8 @@
*/
#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/msm/msm-bus-ids.h>
+
&soc {
/* Primary USB port related DWC3 controller */
usb0: ssusb@a600000 {
@@ -49,18 +51,30 @@
resets = <&clock_gcc GCC_USB30_PRIM_BCR>;
reset-names = "core_reset";
+ qcom,msm-bus,name = "usb0";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <3>;
+ qcom,msm-bus,vectors-KBps =
+ <MSM_BUS_MASTER_USB3 MSM_BUS_SLAVE_EBI_CH0 0 0>,
+ <MSM_BUS_MASTER_USB3 MSM_BUS_SLAVE_IPA_CFG 0 0>,
+ <MSM_BUS_MASTER_AMPSS_M0 MSM_BUS_SLAVE_USB3 0 0>,
+ <MSM_BUS_MASTER_USB3
+ MSM_BUS_SLAVE_EBI_CH0 240000 800000>,
+ <MSM_BUS_MASTER_USB3
+ MSM_BUS_SLAVE_IPA_CFG 0 2400>,
+ <MSM_BUS_MASTER_AMPSS_M0 MSM_BUS_SLAVE_USB3 0 80000>;
+
dwc3@a600000 {
compatible = "snps,dwc3";
reg = <0x0a600000 0xcd00>;
interrupt-parent = <&intc>;
interrupts = <0 133 0>;
- usb-phy = <&qusb_phy0>, <&usb_nop_phy>;
+ usb-phy = <&qusb_phy0>, <&usb_qmp_dp_phy>;
tx-fifo-resize;
linux,sysdev_is_parent;
snps,disable-clk-gating;
snps,has-lpm-erratum;
snps,hird-threshold = /bits/ 8 <0x10>;
- maximum-speed = "high-speed";
};
qcom,usbbam@a704000 {
@@ -123,7 +137,7 @@
0x29 0x244 /* TUNE2 */
0xca 0x248 /* TUNE3 */
0x04 0x24c /* TUNE4 */
- 0x00 0x250 /* TUNE5 */
+ 0x03 0x250 /* TUNE5 */
0x00 0x23c /* CHG_CTRL2 */
0x22 0x210>; /* PWR_CTRL1 */
@@ -151,7 +165,7 @@
<0x1048 0x07 0x00 /* COM_PLL_IVCO */
0x1080 0x14 0x00 /* COM_SYSCLK_EN_SEL */
0x1034 0x08 0x00 /* COM_BIAS_EN_CLKBUFLR_EN */
- 0x1137 0x30 0x00 /* COM_CLK_SELECT */
+ 0x1138 0x30 0x00 /* COM_CLK_SELECT */
0x103c 0x02 0x00 /* COM_SYS_CLK_CTRL */
0x108c 0x08 0x00 /* COM_RESETSM_CNTRL2 */
0x115c 0x16 0x00 /* COM_CMN_CONFIG */
@@ -171,7 +185,7 @@
0x1148 0x0a 0x00 /* COM_CORECLK_DIV_MODE0 */
0x10a0 0x00 0x00 /* COM_LOCK_CMP3_MODE0 */
0x109c 0x34 0x00 /* COM_LOCK_CMP2_MODE0 */
- 0x1018 0x15 0x00 /* COM_LOCK_CMP1_MODE0 */
+ 0x1098 0x15 0x00 /* COM_LOCK_CMP1_MODE0 */
0x1090 0x04 0x00 /* COM_LOCK_CMP_EN */
0x1154 0x00 0x00 /* COM_CORE_CLK_EN */
0x1094 0x00 0x00 /* COM_LOCK_CMP_CFG */
@@ -203,8 +217,8 @@
0x1260 0x10 0x00 /* TXA_HIGHZ_DRVR_EN */
0x12a4 0x12 0x00 /* TXA_RCV_DETECT_LVL_2 */
0x128c 0x16 0x00 /* TXA_LANE_MODE_1 */
- 0x1648 0x09 0x00 /* TXB_RES_CODE_LANE_OFFSET_RX */
- 0x1644 0x0d 0x00 /* TXB_RES_CODE_LANE_OFFSET_TX */
+ 0x1248 0x09 0x00 /* TXA_RES_CODE_LANE_OFFSET_RX */
+ 0x1244 0x0d 0x00 /* TXA_RES_CODE_LANE_OFFSET_TX */
0x1660 0x10 0x00 /* TXB_HIGHZ_DRVR_EN */
0x16a4 0x12 0x00 /* TXB_RCV_DETECT_LVL_2 */
0x168c 0x16 0x00 /* TXB_LANE_MODE_1 */
@@ -272,14 +286,15 @@
<&clock_gcc GCC_USB3_PRIM_PHY_PIPE_CLK>,
<&clock_rpmh RPMH_CXO_CLK>,
<&clock_gcc GCC_USB3_PRIM_CLKREF_CLK>,
- <&clock_gcc GCC_USB3_PRIM_PHY_COM_AUX_CLK>;
+ <&clock_gcc GCC_USB3_PRIM_PHY_COM_AUX_CLK>,
+ <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>;
clock-names = "aux_clk", "pipe_clk", "ref_clk_src",
- "ref_clk", "com_aux_clk";
+ "ref_clk", "com_aux_clk", "cfg_ahb_clk";
- resets = <&clock_gcc GCC_USB3_DP_PHY_PRIM_BCR>;
- reset-names = "phy_reset";
- status = "disabled";
+ resets = <&clock_gcc GCC_USB3_DP_PHY_PRIM_BCR>,
+ <&clock_gcc GCC_USB3_PHY_PRIM_BCR>;
+ reset-names = "global_phy_reset", "phy_reset";
};
dbm_1p5: dbm@a6f8000 {
@@ -326,6 +341,16 @@
reset-names = "core_reset";
status = "disabled";
+ qcom,msm-bus,name = "usb1";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <2>;
+ qcom,msm-bus,vectors-KBps =
+ <MSM_BUS_MASTER_USB3_1 MSM_BUS_SLAVE_EBI_CH0 0 0>,
+ <MSM_BUS_MASTER_AMPSS_M0 MSM_BUS_SLAVE_USB3_1 0 0>,
+ <MSM_BUS_MASTER_USB3_1
+ MSM_BUS_SLAVE_EBI_CH0 240000 800000>,
+ <MSM_BUS_MASTER_AMPSS_M0 MSM_BUS_SLAVE_USB3_1 0 80000>;
+
dwc3@a600000 {
compatible = "snps,dwc3";
reg = <0x0a800000 0xcd00>;
@@ -367,7 +392,7 @@
0x29 0x244 /* TUNE2 */
0xca 0x248 /* TUNE3 */
0x04 0x24c /* TUNE4 */
- 0x00 0x250 /* TUNE5 */
+ 0x03 0x250 /* TUNE5 */
0x00 0x23c /* CHG_CTRL2 */
0x22 0x210>; /* PWR_CTRL1 */
@@ -505,10 +530,11 @@
clocks = <&clock_gcc GCC_USB3_SEC_PHY_AUX_CLK>,
<&clock_gcc GCC_USB3_SEC_PHY_PIPE_CLK>,
<&clock_rpmh RPMH_CXO_CLK>,
- <&clock_gcc GCC_USB3_SEC_CLKREF_CLK>;
+ <&clock_gcc GCC_USB3_SEC_CLKREF_CLK>,
+ <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>;
clock-names = "aux_clk", "pipe_clk", "ref_clk_src",
- "ref_clk";
+ "ref_clk", "cfg_ahb_clk";
resets = <&clock_gcc GCC_USB3_PHY_SEC_BCR>,
<&clock_gcc GCC_USB3PHY_PHY_SEC_BCR>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-vidc.dtsi b/arch/arm64/boot/dts/qcom/sdm845-vidc.dtsi
index af88108..d6ef67d 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-vidc.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-vidc.dtsi
@@ -17,11 +17,13 @@
&soc {
msm_vidc: qcom,vidc@aa00000 {
compatible = "qcom,msm-vidc";
- status = "disabled";
+ status = "ok";
reg = <0xaa00000 0x200000>;
interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
qcom,hfi = "venus";
qcom,firmware-name = "venus";
+ qcom,never-unload-fw;
+ qcom,sw-power-collapse;
qcom,max-secure-instances = <5>;
qcom,max-hw-load = <2563200>; /* Full 4k @ 60 + 1080p @ 60 */
@@ -32,15 +34,19 @@
/* Clocks */
clock-names = "core_clk", "iface_clk", "bus_clk",
- "core0_clk", "core1_clk";
+ "core0_clk", "core0_bus_clk",
+ "core1_clk", "core1_bus_clk";
clocks = <&clock_videocc VIDEO_CC_VENUS_CTL_CORE_CLK>,
<&clock_videocc VIDEO_CC_VENUS_AHB_CLK>,
<&clock_videocc VIDEO_CC_VENUS_CTL_AXI_CLK>,
<&clock_videocc VIDEO_CC_VCODEC0_CORE_CLK>,
- <&clock_videocc VIDEO_CC_VCODEC1_CORE_CLK>;
+ <&clock_videocc VIDEO_CC_VCODEC0_AXI_CLK>,
+ <&clock_videocc VIDEO_CC_VCODEC1_CORE_CLK>,
+ <&clock_videocc VIDEO_CC_VCODEC1_AXI_CLK>;
qcom,proxy-clock-names = "core_clk", "iface_clk",
- "bus_clk", "core0_clk", "core1_clk";
- qcom,clock-configs = <0x0 0x0 0x0 0x0 0x0>;
+ "bus_clk", "core0_clk", "core0_bus_clk",
+ "core1_clk", "core1_bus_clk";
+ qcom,clock-configs = <0x1 0x0 0x0 0x1 0x0 0x1 0x0>;
qcom,allowed-clock-rates = <200000000 320000000 380000000
444000000 533000000>;
qcom,clock-freq-tbl {
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 9c2f81f..fb39e6f 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -21,6 +21,8 @@
#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/soc/qcom,tcs-mbox.h>
+#include <dt-bindings/spmi/spmi.h>
+#include <dt-bindings/thermal/thermal.h>
/ {
model = "Qualcomm Technologies, Inc. SDM845";
@@ -31,6 +33,7 @@
aliases {
ufshc1 = &ufshc_mem; /* Embedded UFS slot */
ufshc2 = &ufshc_card; /* Removable UFS slot */
+ sdhc2 = &sdhc_2; /* SDC2 SD card slot */
};
cpus {
@@ -45,6 +48,8 @@
efficiency = <1024>;
cache-size = <0x8000>;
cpu-release-addr = <0x0 0x90000000>;
+ qcom,lmh-dcvs = <&lmh_dcvs0>;
+ #cooling-cells = <2>;
next-level-cache = <&L2_0>;
L2_0: l2-cache {
compatible = "arm,arch-cache";
@@ -76,6 +81,8 @@
efficiency = <1024>;
cache-size = <0x8000>;
cpu-release-addr = <0x0 0x90000000>;
+ qcom,lmh-dcvs = <&lmh_dcvs0>;
+ #cooling-cells = <2>;
next-level-cache = <&L2_100>;
L2_100: l2-cache {
compatible = "arm,arch-cache";
@@ -101,6 +108,8 @@
efficiency = <1024>;
cache-size = <0x8000>;
cpu-release-addr = <0x0 0x90000000>;
+ qcom,lmh-dcvs = <&lmh_dcvs0>;
+ #cooling-cells = <2>;
next-level-cache = <&L2_200>;
L2_200: l2-cache {
compatible = "arm,arch-cache";
@@ -126,6 +135,8 @@
efficiency = <1024>;
cache-size = <0x8000>;
cpu-release-addr = <0x0 0x90000000>;
+ qcom,lmh-dcvs = <&lmh_dcvs0>;
+ #cooling-cells = <2>;
next-level-cache = <&L2_300>;
L2_300: l2-cache {
compatible = "arm,arch-cache";
@@ -151,6 +162,8 @@
efficiency = <1740>;
cache-size = <0x20000>;
cpu-release-addr = <0x0 0x90000000>;
+ qcom,lmh-dcvs = <&lmh_dcvs1>;
+ #cooling-cells = <2>;
next-level-cache = <&L2_400>;
L2_400: l2-cache {
compatible = "arm,arch-cache";
@@ -176,6 +189,8 @@
efficiency = <1740>;
cache-size = <0x20000>;
cpu-release-addr = <0x0 0x90000000>;
+ qcom,lmh-dcvs = <&lmh_dcvs1>;
+ #cooling-cells = <2>;
next-level-cache = <&L2_500>;
L2_500: l2-cache {
compatible = "arm,arch-cache";
@@ -201,6 +216,8 @@
efficiency = <1740>;
cache-size = <0x20000>;
cpu-release-addr = <0x0 0x90000000>;
+ qcom,lmh-dcvs = <&lmh_dcvs1>;
+ #cooling-cells = <2>;
next-level-cache = <&L2_600>;
L2_600: l2-cache {
compatible = "arm,arch-cache";
@@ -226,6 +243,8 @@
efficiency = <1740>;
cache-size = <0x20000>;
cpu-release-addr = <0x0 0x90000000>;
+ qcom,lmh-dcvs = <&lmh_dcvs1>;
+ #cooling-cells = <2>;
next-level-cache = <&L2_700>;
L2_700: l2-cache {
compatible = "arm,arch-cache";
@@ -525,6 +544,57 @@
cell-index = <0>;
};
+ spmi_debug_bus: qcom,spmi-debug@6b22000 {
+ compatible = "qcom,spmi-pmic-arb-debug";
+ reg = <0x6b22000 0x60>, <0x7820A8 4>;
+ reg-names = "core", "fuse";
+ qcom,fuse-disable-bit = <12>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ qcom,pm8998-debug@0 {
+ compatible = "qcom,spmi-pmic";
+ reg = <0x0 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ };
+
+ qcom,pm8998-debug@1 {
+ compatible = "qcom,spmi-pmic";
+ reg = <0x1 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ };
+
+ qcom,pmi8998-debug@2 {
+ compatible = "qcom,spmi-pmic";
+ reg = <0x2 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ };
+
+ qcom,pmi8998-debug@3 {
+ compatible = "qcom,spmi-pmic";
+ reg = <0x3 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ };
+
+ qcom,pm8005-debug@4 {
+ compatible = "qcom,spmi-pmic";
+ reg = <0x4 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ };
+
+ qcom,pm8005-debug@5 {
+ compatible = "qcom,spmi-pmic";
+ reg = <0x5 SPMI_USID>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+ };
+ };
+
msm_cpufreq: qcom,msm-cpufreq {
compatible = "qcom,msm-cpufreq";
clock-names = "cpu0_clk", "cpu4_clk";
@@ -778,7 +848,7 @@
clock_dispcc: qcom,dispcc@af00000 {
compatible = "qcom,dispcc-sdm845", "syscon";
- reg = <0xaf00000 0x100000>;
+ reg = <0xaf00000 0x10000>;
reg-names = "cc_base";
vdd_cx-supply = <&pm8998_s9_level>;
#clock-cells = <1>;
@@ -816,12 +886,11 @@
<0x178b0000 0x1000>,
<0x17d42400 0x0c00>,
<0x17d44400 0x0c00>,
- <0x17d46c00 0x0c00>,
- <0x17810090 0x8>;
+ <0x17d46c00 0x0c00>;
reg-names = "osm_l3_base", "osm_pwrcl_base", "osm_perfcl_base",
"l3_pll", "pwrcl_pll", "perfcl_pll",
"l3_sequencer", "pwrcl_sequencer",
- "perfcl_sequencer", "apps_itm_ctl";
+ "perfcl_sequencer";
vdd-l3-supply = <&apc0_l3_vreg>;
vdd-pwrcl-supply = <&apc0_pwrcl_vreg>;
@@ -858,7 +927,7 @@
< 652800000 0x401c0422 0x00002020 0x1 5 >,
< 729600000 0x401c0526 0x00002020 0x1 6 >,
< 806400000 0x401c062a 0x00002222 0x1 7 >,
- < 883200000 0x4024072b 0x00002525 0x1 8 >,
+ < 883200000 0x4024072e 0x00002525 0x1 8 >,
< 960000000 0x40240832 0x00002828 0x1 9 >,
< 1036800000 0x40240936 0x00002b2b 0x1 10 >,
< 1113600000 0x402c0a3a 0x00002e2e 0x1 11 >,
@@ -915,7 +984,6 @@
qcom,safe-fsm-en;
qcom,ps-fsm-en;
qcom,droop-fsm-en;
- qcom,osm-pll-setup;
clock-names = "xo_ao";
clocks = <&clock_rpmh RPMH_CXO_CLK_A>;
@@ -953,7 +1021,7 @@
clock-names = "ref_clk_src",
"ref_clk",
"ref_aux_clk";
- clocks = <&clock_rpmh RPMH_LN_BB_CLK1>,
+ clocks = <&clock_rpmh RPMH_CXO_CLK>,
<&clock_gcc GCC_UFS_MEM_CLKREF_CLK>,
<&clock_gcc GCC_UFS_PHY_PHY_AUX_CLK>;
@@ -987,7 +1055,7 @@
<&clock_gcc GCC_UFS_PHY_AHB_CLK>,
<&clock_gcc GCC_UFS_PHY_UNIPRO_CORE_CLK>,
<&clock_gcc GCC_UFS_PHY_ICE_CORE_CLK>,
- <&clock_rpmh RPMH_LN_BB_CLK1>,
+ <&clock_rpmh RPMH_CXO_CLK>,
<&clock_gcc GCC_UFS_PHY_TX_SYMBOL_0_CLK>,
<&clock_gcc GCC_UFS_PHY_RX_SYMBOL_0_CLK>,
<&clock_gcc GCC_UFS_PHY_RX_SYMBOL_1_CLK>;
@@ -1073,7 +1141,7 @@
clock-names = "ref_clk_src",
"ref_clk",
"ref_aux_clk";
- clocks = <&clock_rpmh RPMH_LN_BB_CLK1>,
+ clocks = <&clock_rpmh RPMH_CXO_CLK>,
<&clock_gcc GCC_UFS_CARD_CLKREF_CLK>,
<&clock_gcc GCC_UFS_CARD_PHY_AUX_CLK>;
@@ -1106,7 +1174,7 @@
<&clock_gcc GCC_UFS_CARD_AHB_CLK>,
<&clock_gcc GCC_UFS_CARD_UNIPRO_CORE_CLK>,
<&clock_gcc GCC_UFS_CARD_ICE_CORE_CLK>,
- <&clock_rpmh RPMH_LN_BB_CLK1>,
+ <&clock_rpmh RPMH_CXO_CLK>,
<&clock_gcc GCC_UFS_CARD_TX_SYMBOL_0_CLK>,
<&clock_gcc GCC_UFS_CARD_RX_SYMBOL_0_CLK>;
freq-table-hz =
@@ -1154,6 +1222,55 @@
status = "disabled";
};
+ sdhc_2: sdhci@8804000 {
+ compatible = "qcom,sdhci-msm-v5";
+ reg = <0x8804000 0x1000>;
+ reg-names = "hc_mem";
+
+ interrupts = <0 204 0>, <0 222 0>;
+ interrupt-names = "hc_irq", "pwr_irq";
+
+ qcom,bus-width = <4>;
+ qcom,large-address-bus;
+
+ qcom,msm-bus,name = "sdhc2";
+ qcom,msm-bus,num-cases = <8>;
+ qcom,msm-bus,num-paths = <2>;
+ qcom,msm-bus,vectors-KBps =
+ /* No vote */
+ <81 512 0 0>, <1 608 0 0>,
+ /* 400 KB/s*/
+ <81 512 1046 1600>,
+ <1 608 1600 1600>,
+ /* 20 MB/s */
+ <81 512 52286 80000>,
+ <1 608 80000 80000>,
+ /* 25 MB/s */
+ <81 512 65360 100000>,
+ <1 608 100000 100000>,
+ /* 50 MB/s */
+ <81 512 130718 200000>,
+ <1 608 133320 133320>,
+ /* 100 MB/s */
+ <81 512 261438 200000>,
+ <1 608 150000 150000>,
+ /* 200 MB/s */
+ <81 512 261438 400000>,
+ <1 608 300000 300000>,
+ /* Max. bandwidth */
+ <81 512 1338562 4096000>,
+ <1 608 1338562 4096000>;
+ qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
+ 100000000 200000000 4294967295>;
+
+ qcom,devfreq,freq-table = <50000000 200000000>;
+ clocks = <&clock_gcc GCC_SDCC2_AHB_CLK>,
+ <&clock_gcc GCC_SDCC2_APPS_CLK>;
+ clock-names = "iface_clk", "core_clk";
+
+ status = "disabled";
+ };
+
pil_modem: qcom,mss@4080000 {
compatible = "qcom,pil-q6v55-mss";
reg = <0x4080000 0x100>,
@@ -1171,11 +1288,13 @@
<&clock_gcc GCC_BOOT_ROM_AHB_CLK>,
<&clock_gcc GCC_MSS_GPLL0_DIV_CLK_SRC>,
<&clock_gcc GCC_MSS_SNOC_AXI_CLK>,
- <&clock_gcc GCC_MSS_MFAB_AXIS_CLK>;
+ <&clock_gcc GCC_MSS_AXIS2_CLK>,
+ <&clock_gcc GCC_MSS_MFAB_AXIS_CLK>,
+ <&clock_gcc GCC_PRNG_AHB_CLK>;
clock-names = "xo", "iface_clk", "bus_clk",
"mem_clk", "gpll0_mss_clk", "snoc_axi_clk",
- "mnoc_axi_clk";
- qcom,proxy-clock-names = "xo";
+ "axis2_clk","mnoc_axi_clk", "prng_clk";
+ qcom,proxy-clock-names = "xo", "axis2_clk", "prng_clk";
qcom,active-clock-names = "iface_clk", "bus_clk", "mem_clk",
"gpll0_mss_clk", "snoc_axi_clk",
"mnoc_axi_clk";
@@ -1241,7 +1360,7 @@
qcom,ssc@5c00000 {
compatible = "qcom,pil-tz-generic";
reg = <0x5c00000 0x4000>;
- interrupts = <0 494 1>;
+ interrupts = <0 377 1>;
vdd_cx-supply = <&pm8998_l27_level>;
vdd_px-supply = <&pm8998_lvs2>;
@@ -1327,7 +1446,7 @@
compatible = "qcom,msm-watchdog";
reg = <0x17980000 0x1000>;
reg-names = "wdt-base";
- interrupts = <0 3 0>, <0 4 0>;
+ interrupts = <0 0 0>, <0 1 0>;
qcom,bark-time = <11000>;
qcom,pet-time = <10000>;
qcom,ipi-ping;
@@ -1349,7 +1468,7 @@
qcom,pas-id = <18>;
qcom,proxy-timeout-ms = <10000>;
- qcom,smem-id = <423>;
+ qcom,smem-id = <601>;
qcom,sysmon-id = <7>;
qcom,ssctl-instance-id = <0x17>;
qcom,firmware-name = "cdsp";
@@ -1656,7 +1775,7 @@
reg-names = "msgram", "irq-reg-base";
qcom,irq-mask = <0x1>;
interrupts = <0 389 1>;
- mbox_desc_offset = <0x0>;
+ mbox-desc-offset = <0x0>;
#mbox-cells = <1>;
};
@@ -1827,6 +1946,20 @@
status = "ok";
};
+ spss_utils: qcom,spss_utils {
+ compatible = "qcom,spss-utils";
+ /* spss fuses physical address */
+ qcom,spss-fuse1-addr = <0x007841c4>;
+ qcom,spss-fuse1-bit = <27>;
+ qcom,spss-fuse2-addr = <0x007841c4>;
+ qcom,spss-fuse2-bit = <26>;
+ qcom,spss-dev-firmware-name = "spss1d"; /* 8 chars max */
+ qcom,spss-test-firmware-name = "spss1t"; /* 8 chars max */
+ qcom,spss-prod-firmware-name = "spss1p"; /* 8 chars max */
+ qcom,spss-debug-reg-addr = <0x01886020>;
+ status = "ok";
+ };
+
qcom,glink_pkt {
compatible = "qcom,glinkpkt";
@@ -1892,10 +2025,45 @@
qcom,pipe-attr-ee;
};
+ qcom_seecom: qseecom@86d00000 {
+ compatible = "qcom,qseecom";
+ reg = <0x86d00000 0x2200000>;
+ reg-names = "secapp-region";
+ qcom,hlos-num-ce-hw-instances = <1>;
+ qcom,hlos-ce-hw-instance = <0>;
+ qcom,qsee-ce-hw-instance = <0>;
+ qcom,disk-encrypt-pipe-pair = <2>;
+ qcom,support-fde;
+ qcom,no-clock-support;
+ qcom,msm-bus,name = "qseecom-noc";
+ qcom,msm-bus,num-cases = <4>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <125 512 0 0>,
+ <125 512 200000 400000>,
+ <125 512 300000 800000>,
+ <125 512 400000 1000000>;
+ clock-names = "core_clk_src", "core_clk",
+ "iface_clk", "bus_clk";
+ clocks = <&clock_gcc GCC_CE1_CLK>,
+ <&clock_gcc GCC_CE1_CLK>,
+ <&clock_gcc GCC_CE1_AHB_CLK>,
+ <&clock_gcc GCC_CE1_AXI_CLK>;
+ qcom,ce-opp-freq = <171430000>;
+ qcom,qsee-reentrancy-support = <2>;
+ };
+
qcom,msm_gsi {
compatible = "qcom,msm_gsi";
};
+ qcom,rmtfs_sharedmem@0 {
+ compatible = "qcom,sharedmem-uio";
+ reg = <0x0 0x200000>;
+ reg-names = "rmtfs";
+ qcom,client-id = <0x00000001>;
+ };
+
qcom,rmnet-ipa {
compatible = "qcom,rmnet-ipa3";
qcom,rmnet-ipa-ssr;
@@ -2079,12 +2247,1076 @@
reg = <0x10a2000 0x1000>,
<0x10ae000 0x2000>;
reg-names = "dcc-base", "dcc-ram-base";
+
+ dcc-ram-offset = <0x6000>;
};
qcom,msm-core@780000 {
compatible = "qcom,apss-core-ea";
reg = <0x780000 0x1000>;
};
+
+ qcom,icnss@18800000 {
+ compatible = "qcom,icnss";
+ reg = <0x18800000 0x800000>,
+ <0xa0000000 0x10000000>,
+ <0xb0000000 0x10000>;
+ reg-names = "membase", "smmu_iova_base", "smmu_iova_ipa";
+ iommus = <&apps_smmu 0x0040>,
+ <&apps_smmu 0x0041>;
+ interrupts = <0 414 0 /* CE0 */ >,
+ <0 415 0 /* CE1 */ >,
+ <0 416 0 /* CE2 */ >,
+ <0 417 0 /* CE3 */ >,
+ <0 418 0 /* CE4 */ >,
+ <0 419 0 /* CE5 */ >,
+ <0 420 0 /* CE6 */ >,
+ <0 421 0 /* CE7 */ >,
+ <0 422 0 /* CE8 */ >,
+ <0 423 0 /* CE9 */ >,
+ <0 424 0 /* CE10 */ >,
+ <0 425 0 /* CE11 */ >;
+ qcom,wlan-msa-memory = <0x100000>;
+ };
+
+ thermal_zones: thermal-zones {
+ aoss0-ts0-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "user_space";
+ thermal-sensors = <&tsens0 0>;
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ cpu0-silver-ts0-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "user_space";
+ thermal-sensors = <&tsens0 1>;
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ cpu1-silver-ts0-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "user_space";
+ thermal-sensors = <&tsens0 2>;
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ cpu2-silver-ts0-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "user_space";
+ thermal-sensors = <&tsens0 3>;
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ cpu3-silver-ts0-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens0 4>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ kryo-l3-0-ts0-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens0 5>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ kryo-l3-1-ts0-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens0 6>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ cpu0-gold-ts0-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens0 7>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ cpu1-gold-ts0-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens0 8>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ cpu2-gold-ts0-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens0 9>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ cpu3-gold-ts0-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens0 10>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ gpu0-ts0-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens0 11>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ gpu1-ts0-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "user_space";
+ thermal-sensors = <&tsens0 12>;
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ aoss1-ts1-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens1 0>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ mdm-dsp-ts1-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens1 1>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+
+
+ ddr-ts1-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens1 2>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ wlan-ts1-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens1 3>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ compute-hvx-ts1-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens1 4>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ camera-ts1-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens1 5>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ mmss-ts1-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens1 6>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ mdm-core-ts1-h {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens1 7>;
+ thermal-governor = "user_space";
+ trips {
+ active-config0 {
+ temperature = <65000>;
+ hysteresis = <1000>;
+ type = "passive";
+ };
+ };
+ };
+
+ gpu0 {
+ polling-delay-passive = <10>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens0 11>;
+ thermal-governor = "step_wise";
+ trips {
+ gpu0_trip: gpu0-trip {
+ temperature = <95000>;
+ hysteresis = <0>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ gpu0_cdev {
+ trip = <&gpu0_trip>;
+ cooling-device =
+ <&msm_gpu 1 THERMAL_NO_LIMIT>;
+ };
+ };
+ };
+
+ gpu1 {
+ polling-delay-passive = <10>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens0 12>;
+ thermal-governor = "step_wise";
+ trips {
+ gpu1_trip: gpu1-trip {
+ temperature = <95000>;
+ hysteresis = <0>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ gpu1_cdev {
+ trip = <&gpu1_trip>;
+ cooling-device =
+ <&msm_gpu 1 THERMAL_NO_LIMIT>;
+ };
+ };
+ };
+
+ pop-mem {
+ polling-delay-passive = <10>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens1 2>;
+ thermal-governor = "step_wise";
+ trips {
+ pop_trip: pop-trip {
+ temperature = <95000>;
+ hysteresis = <0>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ pop_cdev {
+ trip = <&pop_trip>;
+ cooling-device =
+ <&CPU4 1 THERMAL_NO_LIMIT>;
+ };
+ };
+ };
+
+ aoss0-ts0-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 0>;
+ tracks-low;
+ trips {
+ aoss0_trip: aoss0-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&aoss0_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&aoss0_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&aoss0_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ cpu0-silver-ts0-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 1>;
+ tracks-low;
+ trips {
+ cpu0_trip: cpu0-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&cpu0_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&cpu0_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&cpu0_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ cpu1-silver-ts0-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 2>;
+ tracks-low;
+ trips {
+ cpu1_trip: cpu1-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&cpu1_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&cpu1_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&cpu1_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ cpu2-silver-ts0-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 3>;
+ tracks-low;
+ trips {
+ cpu2_trip: cpu2-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&cpu2_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&cpu2_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&cpu2_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ cpu3-silver-ts0-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 4>;
+ tracks-low;
+ trips {
+ cpu3_trip: cpu3-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&cpu3_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&cpu3_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&cpu3_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ kryo-l3-0-ts0-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 5>;
+ tracks-low;
+ trips {
+ l3_0_trip: l3-0-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&l3_0_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&l3_0_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&l3_0_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ kryo-l3-1-ts0-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 6>;
+ tracks-low;
+ trips {
+ l3_1_trip: l3-1-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&l3_1_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&l3_1_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&l3_1_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ cpu0-gold-ts0-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 7>;
+ tracks-low;
+ trips {
+ cpug0_trip: cpug0-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&cpug0_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&cpug0_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&cpug0_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ cpu1-gold-ts0-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 8>;
+ tracks-low;
+ trips {
+ cpug1_trip: cpug1-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&cpug1_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&cpug1_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&cpug1_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ cpu2-gold-ts0-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 9>;
+ tracks-low;
+ trips {
+ cpug2_trip: cpug2-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&cpug2_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&cpug2_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&cpug2_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ cpu3-gold-ts0-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 9>;
+ tracks-low;
+ trips {
+ cpug3_trip: cpug3-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&cpug3_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&cpug3_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&cpug3_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ gpu0-ts0-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 10>;
+ tracks-low;
+ trips {
+ gpu0_trip_l: gpu0-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&gpu0_trip_l>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&gpu0_trip_l>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&gpu0_trip_l>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ gpu1-ts0-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens0 11>;
+ tracks-low;
+ trips {
+ gpu1_trip_l: gpu1-trip_l {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&gpu1_trip_l>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&gpu1_trip_l>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&gpu1_trip_l>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ aoss1-ts1-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens1 0>;
+ tracks-low;
+ trips {
+ aoss1_trip: aoss1-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&aoss1_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&aoss1_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&aoss1_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ mdm-dsp-ts1-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens1 1>;
+ tracks-low;
+ trips {
+ dsp_trip: dsp-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&dsp_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&dsp_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&dsp_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ ddr-ts1-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens1 2>;
+ tracks-low;
+ trips {
+ ddr_trip: ddr-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&ddr_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&ddr_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&ddr_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ wlan-ts1-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens1 3>;
+ tracks-low;
+ trips {
+ wlan_trip: wlan-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&wlan_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&wlan_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&wlan_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ compute-hvx-ts1-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens1 4>;
+ tracks-low;
+ trips {
+ hvx_trip: hvx-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&hvx_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&hvx_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&hvx_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ camera-ts1-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens1 5>;
+ tracks-low;
+ trips {
+ camera_trip: camera-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&camera_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&camera_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&camera_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ mmss-ts1-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens1 6>;
+ tracks-low;
+ trips {
+ mmss_trip: mmss-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&mmss_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&mmss_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&mmss_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ mdm-core-ts1-l {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "low_limits_floor";
+ thermal-sensors = <&tsens1 7>;
+ tracks-low;
+ trips {
+ mdm_trip: mdm-trip {
+ temperature = <5000>;
+ hysteresis = <5000>;
+ type = "passive";
+ };
+ };
+ cooling-maps {
+ cpu0_vdd_cdev {
+ trip = <&mdm_trip>;
+ cooling-device = <&CPU0 12 12>;
+ };
+ cpu4_vdd_cdev {
+ trip = <&mdm_trip>;
+ cooling-device = <&CPU4 12 12>;
+ };
+ gpu_vdd_cdev {
+ trip = <&mdm_trip>;
+ cooling-device = <&msm_gpu 4 4>;
+ };
+ };
+ };
+
+ lmh-dcvs-01 {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "user_space";
+ thermal-sensors = <&lmh_dcvs1>;
+
+ trips {
+ active-config {
+ temperature = <95000>;
+ hysteresis = <30000>;
+ type = "passive";
+ };
+ };
+ };
+
+ lmh-dcvs-00 {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-governor = "user_space";
+ thermal-sensors = <&lmh_dcvs0>;
+
+ trips {
+ active-config {
+ temperature = <95000>;
+ hysteresis = <30000>;
+ type = "passive";
+ };
+ };
+ };
+
+ };
+
+ tsens0: tsens@c222000 {
+ compatible = "qcom,sdm845-tsens";
+ reg = <0xc222000 0x4>,
+ <0xc263000 0x1ff>;
+ reg-names = "tsens_srot_physical",
+ "tsens_tm_physical";
+ interrupts = <0 506 0>, <0 508 0>;
+ interrupt-names = "tsens-upper-lower", "tsens-critical";
+ #thermal-sensor-cells = <1>;
+ };
+
+ tsens1: tsens@c223000 {
+ compatible = "qcom,sdm845-tsens";
+ reg = <0xc223000 0x4>,
+ <0xc265000 0x1ff>;
+ reg-names = "tsens_srot_physical",
+ "tsens_tm_physical";
+ interrupts = <0 507 0>, <0 509 0>;
+ interrupt-names = "tsens-upper-lower", "tsens-critical";
+ #thermal-sensor-cells = <1>;
+ };
+};
+
+&clock_cpucc {
+ lmh_dcvs0: qcom,limits-dcvs@0 {
+ compatible = "qcom,msm-hw-limits";
+ interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
+ qcom,affinity = <0>;
+ #thermal-sensor-cells = <0>;
+ };
+
+ lmh_dcvs1: qcom,limits-dcvs@1 {
+ compatible = "qcom,msm-hw-limits";
+ interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+ qcom,affinity = <1>;
+ #thermal-sensor-cells = <0>;
+ };
};
&pcie_0_gdsc {
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index 9618917..629f712 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -1,5 +1,6 @@
CONFIG_LOCALVERSION="-perf"
# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_FHANDLE is not set
CONFIG_AUDIT=y
# CONFIG_AUDITSYSCALL is not set
CONFIG_NO_HZ=y
@@ -224,6 +225,7 @@
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_QSEECOM=y
CONFIG_MEMORY_STATE_TIME=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
@@ -246,6 +248,7 @@
CONFIG_DUMMY=y
CONFIG_TUN=y
CONFIG_SKY2=y
+CONFIG_RNDIS_IPA=y
CONFIG_SMSC911X=y
CONFIG_PPP=y
CONFIG_PPP_BSDCOMP=y
@@ -261,6 +264,7 @@
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_INPUT_MISC=y
+CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_QPNP_POWER_ON=y
CONFIG_INPUT_UINPUT=y
# CONFIG_SERIO_SERPORT is not set
@@ -281,6 +285,7 @@
CONFIG_SPI_SPIDEV=y
CONFIG_SLIMBUS_MSM_NGD=y
CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
CONFIG_PINCTRL_SDM845=y
CONFIG_PINCTRL_SDM830=y
CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
@@ -296,8 +301,17 @@
CONFIG_QPNP_QNOVO=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_THERMAL=y
+CONFIG_THERMAL_WRITABLE_TRIPS=y
+CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE=y
+CONFIG_THERMAL_GOV_STEP_WISE=y
+CONFIG_THERMAL_GOV_LOW_LIMITS=y
+CONFIG_CPU_THERMAL=y
+CONFIG_DEVFREQ_THERMAL=y
CONFIG_THERMAL_QPNP=y
CONFIG_THERMAL_QPNP_ADC_TM=y
+CONFIG_THERMAL_TSENS=y
+CONFIG_MSM_BCL_PERIPHERAL_CTL=y
+CONFIG_QTI_THERMAL_LIMITS_DCVS=y
CONFIG_MFD_I2C_PMIC=y
CONFIG_MFD_SPMI_PMIC=y
CONFIG_WCD934X_CODEC=y
@@ -377,6 +391,7 @@
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
CONFIG_MMC_TEST=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
@@ -391,16 +406,22 @@
CONFIG_RTC_DRV_QPNP=y
CONFIG_DMADEVICES=y
CONFIG_UIO=y
+CONFIG_UIO_MSM_SHAREDMEM=y
CONFIG_STAGING=y
CONFIG_ASHMEM=y
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
CONFIG_ION=y
CONFIG_ION_MSM=y
+CONFIG_GSI=y
+CONFIG_IPA3=y
+CONFIG_RMNET_IPA3=y
+CONFIG_IPA_UT=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_QPNP_COINCELL=y
CONFIG_QPNP_REVID=y
CONFIG_USB_BAM=y
+CONFIG_SEEMP_CORE=y
CONFIG_MSM_GCC_SDM845=y
CONFIG_MSM_VIDEOCC_SDM845=y
CONFIG_MSM_CAMCC_SDM845=y
@@ -432,6 +453,7 @@
CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
CONFIG_MSM_GLINK_SPI_XPRT=y
CONFIG_MSM_SPCOM=y
+CONFIG_MSM_SPSS_UTILS=y
CONFIG_TRACER_PKT=y
CONFIG_QTI_RPMH_API=y
CONFIG_MSM_SMP2P=y
@@ -459,6 +481,7 @@
CONFIG_DEVFREQ_SIMPLE_DEV=y
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_EXTCON=y
+CONFIG_EXTCON_USB_GPIO=y
CONFIG_IIO=y
CONFIG_QCOM_RRADC=y
CONFIG_PWM=y
@@ -466,7 +489,6 @@
CONFIG_ARM_GIC_V3_ACL=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
-CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder"
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 1e7d407..3f3ff8d 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -1,4 +1,5 @@
# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_FHANDLE is not set
CONFIG_AUDIT=y
# CONFIG_AUDITSYSCALL is not set
CONFIG_NO_HZ=y
@@ -233,6 +234,7 @@
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_QSEECOM=y
CONFIG_UID_SYS_STATS=y
CONFIG_MEMORY_STATE_TIME=y
CONFIG_SCSI=y
@@ -255,6 +257,7 @@
CONFIG_BONDING=y
CONFIG_DUMMY=y
CONFIG_TUN=y
+CONFIG_RNDIS_IPA=y
CONFIG_PPP=y
CONFIG_PPP_BSDCOMP=y
CONFIG_PPP_DEFLATE=y
@@ -270,6 +273,7 @@
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_INPUT_MISC=y
+CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_QPNP_POWER_ON=y
CONFIG_INPUT_UINPUT=y
# CONFIG_SERIO_SERPORT is not set
@@ -291,6 +295,7 @@
CONFIG_SPI_SPIDEV=y
CONFIG_SLIMBUS_MSM_NGD=y
CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
CONFIG_PINCTRL_SDM845=y
CONFIG_PINCTRL_SDM830=y
CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
@@ -306,8 +311,17 @@
CONFIG_QPNP_QNOVO=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_THERMAL=y
+CONFIG_THERMAL_WRITABLE_TRIPS=y
+CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE=y
+CONFIG_THERMAL_GOV_STEP_WISE=y
+CONFIG_THERMAL_GOV_LOW_LIMITS=y
+CONFIG_CPU_THERMAL=y
+CONFIG_DEVFREQ_THERMAL=y
CONFIG_THERMAL_QPNP=y
CONFIG_THERMAL_QPNP_ADC_TM=y
+CONFIG_THERMAL_TSENS=y
+CONFIG_MSM_BCL_PERIPHERAL_CTL=y
+CONFIG_QTI_THERMAL_LIMITS_DCVS=y
CONFIG_MFD_I2C_PMIC=y
CONFIG_MFD_SPMI_PMIC=y
CONFIG_WCD934X_CODEC=y
@@ -324,7 +338,6 @@
CONFIG_VIDEO_ADV_DEBUG=y
CONFIG_VIDEO_FIXED_MINOR_RANGES=y
CONFIG_V4L_PLATFORM_DRIVERS=y
-CONFIG_SPECTRA_CAMERA=y
CONFIG_MSM_VIDC_V4L2=y
CONFIG_MSM_VIDC_VMEM=y
CONFIG_MSM_VIDC_GOVERNORS=y
@@ -387,6 +400,7 @@
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
CONFIG_MMC_TEST=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
@@ -409,16 +423,22 @@
CONFIG_RTC_DRV_QPNP=y
CONFIG_DMADEVICES=y
CONFIG_UIO=y
+CONFIG_UIO_MSM_SHAREDMEM=y
CONFIG_STAGING=y
CONFIG_ASHMEM=y
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
CONFIG_ION=y
CONFIG_ION_MSM=y
+CONFIG_GSI=y
+CONFIG_IPA3=y
+CONFIG_RMNET_IPA3=y
+CONFIG_IPA_UT=y
CONFIG_SPS=y
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_QPNP_COINCELL=y
CONFIG_QPNP_REVID=y
CONFIG_USB_BAM=y
+CONFIG_SEEMP_CORE=y
CONFIG_MSM_GCC_SDM845=y
CONFIG_MSM_VIDEOCC_SDM845=y
CONFIG_MSM_CAMCC_SDM845=y
@@ -453,6 +473,7 @@
CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
CONFIG_MSM_GLINK_SPI_XPRT=y
CONFIG_MSM_SPCOM=y
+CONFIG_MSM_SPSS_UTILS=y
CONFIG_TRACER_PKT=y
CONFIG_QTI_RPMH_API=y
CONFIG_MSM_SMP2P=y
@@ -482,6 +503,7 @@
CONFIG_DEVFREQ_SIMPLE_DEV=y
CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_EXTCON=y
+CONFIG_EXTCON_USB_GPIO=y
CONFIG_IIO=y
CONFIG_QCOM_RRADC=y
CONFIG_PWM=y
@@ -490,7 +512,6 @@
CONFIG_PHY_XGENE=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
-CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder"
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
@@ -529,7 +550,6 @@
CONFIG_DEBUG_MEMORY_INIT=y
CONFIG_LOCKUP_DETECTOR=y
CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y
-CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
CONFIG_WQ_WATCHDOG=y
CONFIG_PANIC_TIMEOUT=5
CONFIG_PANIC_ON_SCHED_BUG=y
@@ -542,7 +562,6 @@
CONFIG_DEBUG_MUTEXES=y
CONFIG_DEBUG_ATOMIC_SLEEP=y
CONFIG_DEBUG_LIST=y
-CONFIG_RCU_PANIC_ON_STALL=1
CONFIG_FAULT_INJECTION=y
CONFIG_FAIL_PAGE_ALLOC=y
CONFIG_FAULT_INJECTION_DEBUG_FS=y
diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c
index da095e8..dd918d0 100644
--- a/arch/arm64/kernel/arm64ksyms.c
+++ b/arch/arm64/kernel/arm64ksyms.c
@@ -74,8 +74,8 @@ NOKPROBE_SYMBOL(_mcount);
#endif
/* arm-smccc */
-EXPORT_SYMBOL(arm_smccc_smc);
-EXPORT_SYMBOL(arm_smccc_hvc);
+EXPORT_SYMBOL(__arm_smccc_smc);
+EXPORT_SYMBOL(__arm_smccc_hvc);
/* caching functions */
EXPORT_SYMBOL(__dma_inv_area);
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index d42e61c..5cdbc55 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -146,8 +146,11 @@ int main(void)
DEFINE(SLEEP_STACK_DATA_SYSTEM_REGS, offsetof(struct sleep_stack_data, system_regs));
DEFINE(SLEEP_STACK_DATA_CALLEE_REGS, offsetof(struct sleep_stack_data, callee_saved_regs));
#endif
- DEFINE(ARM_SMCCC_RES_X0_OFFS, offsetof(struct arm_smccc_res, a0));
- DEFINE(ARM_SMCCC_RES_X2_OFFS, offsetof(struct arm_smccc_res, a2));
+ DEFINE(ARM_SMCCC_RES_X0_OFFS, offsetof(struct arm_smccc_res, a0));
+ DEFINE(ARM_SMCCC_RES_X2_OFFS, offsetof(struct arm_smccc_res, a2));
+ DEFINE(ARM_SMCCC_QUIRK_ID_OFFS, offsetof(struct arm_smccc_quirk, id));
+ DEFINE(ARM_SMCCC_QUIRK_STATE_OFFS, offsetof(struct arm_smccc_quirk, state));
+
BLANK();
DEFINE(HIBERN_PBE_ORIG, offsetof(struct pbe, orig_address));
DEFINE(HIBERN_PBE_ADDR, offsetof(struct pbe, address));
diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
index acf3872..409abc4 100644
--- a/arch/arm64/kernel/pci.c
+++ b/arch/arm64/kernel/pci.c
@@ -121,6 +121,7 @@ int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
static struct pci_config_window *
pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root)
{
+ struct device *dev = &root->device->dev;
struct resource *bus_res = &root->secondary;
u16 seg = root->segment;
struct pci_config_window *cfg;
@@ -132,8 +133,7 @@ pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root)
root->mcfg_addr = pci_mcfg_lookup(seg, bus_res);
if (!root->mcfg_addr) {
- dev_err(&root->device->dev, "%04x:%pR ECAM region not found\n",
- seg, bus_res);
+ dev_err(dev, "%04x:%pR ECAM region not found\n", seg, bus_res);
return NULL;
}
@@ -141,11 +141,10 @@ pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root)
cfgres.start = root->mcfg_addr + bus_res->start * bsz;
cfgres.end = cfgres.start + resource_size(bus_res) * bsz - 1;
cfgres.flags = IORESOURCE_MEM;
- cfg = pci_ecam_create(&root->device->dev, &cfgres, bus_res,
- &pci_generic_ecam_ops);
+ cfg = pci_ecam_create(dev, &cfgres, bus_res, &pci_generic_ecam_ops);
if (IS_ERR(cfg)) {
- dev_err(&root->device->dev, "%04x:%pR error %ld mapping ECAM\n",
- seg, bus_res, PTR_ERR(cfg));
+ dev_err(dev, "%04x:%pR error %ld mapping ECAM\n", seg, bus_res,
+ PTR_ERR(cfg));
return NULL;
}
@@ -159,33 +158,36 @@ static void pci_acpi_generic_release_info(struct acpi_pci_root_info *ci)
ri = container_of(ci, struct acpi_pci_generic_root_info, common);
pci_ecam_free(ri->cfg);
+ kfree(ci->ops);
kfree(ri);
}
-static struct acpi_pci_root_ops acpi_pci_root_ops = {
- .release_info = pci_acpi_generic_release_info,
-};
-
/* Interface called from ACPI code to setup PCI host controller */
struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
{
int node = acpi_get_node(root->device->handle);
struct acpi_pci_generic_root_info *ri;
struct pci_bus *bus, *child;
+ struct acpi_pci_root_ops *root_ops;
ri = kzalloc_node(sizeof(*ri), GFP_KERNEL, node);
if (!ri)
return NULL;
+ root_ops = kzalloc_node(sizeof(*root_ops), GFP_KERNEL, node);
+ if (!root_ops)
+ return NULL;
+
ri->cfg = pci_acpi_setup_ecam_mapping(root);
if (!ri->cfg) {
kfree(ri);
+ kfree(root_ops);
return NULL;
}
- acpi_pci_root_ops.pci_ops = &ri->cfg->ops->pci_ops;
- bus = acpi_pci_root_create(root, &acpi_pci_root_ops, &ri->common,
- ri->cfg);
+ root_ops->release_info = pci_acpi_generic_release_info;
+ root_ops->pci_ops = &ri->cfg->ops->pci_ops;
+ bus = acpi_pci_root_create(root, root_ops, &ri->common, ri->cfg);
if (!bus)
return NULL;
diff --git a/arch/arm64/kernel/smccc-call.S b/arch/arm64/kernel/smccc-call.S
index ae0496f..6252234 100644
--- a/arch/arm64/kernel/smccc-call.S
+++ b/arch/arm64/kernel/smccc-call.S
@@ -12,6 +12,7 @@
*
*/
#include <linux/linkage.h>
+#include <linux/arm-smccc.h>
#include <asm/asm-offsets.h>
.macro SMCCC instr
@@ -20,24 +21,32 @@
ldr x4, [sp]
stp x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS]
stp x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS]
- ret
+ ldr x4, [sp, #8]
+ cbz x4, 1f /* no quirk structure */
+ ldr x9, [x4, #ARM_SMCCC_QUIRK_ID_OFFS]
+ cmp x9, #ARM_SMCCC_QUIRK_QCOM_A6
+ b.ne 1f
+ str x6, [x4, ARM_SMCCC_QUIRK_STATE_OFFS]
+1: ret
.cfi_endproc
.endm
/*
* void arm_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
* unsigned long a3, unsigned long a4, unsigned long a5,
- * unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
+ * unsigned long a6, unsigned long a7, struct arm_smccc_res *res,
+ * struct arm_smccc_quirk *quirk)
*/
-ENTRY(arm_smccc_smc)
+ENTRY(__arm_smccc_smc)
SMCCC smc
-ENDPROC(arm_smccc_smc)
+ENDPROC(__arm_smccc_smc)
/*
* void arm_smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a2,
* unsigned long a3, unsigned long a4, unsigned long a5,
- * unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
+ * unsigned long a6, unsigned long a7, struct arm_smccc_res *res,
+ * struct arm_smccc_quirk *quirk)
*/
-ENTRY(arm_smccc_hvc)
+ENTRY(__arm_smccc_hvc)
SMCCC hvc
-ENDPROC(arm_smccc_hvc)
+ENDPROC(__arm_smccc_hvc)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index 3b40f26..aaf4bd7 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -394,7 +394,7 @@ const struct sched_group_energy * const cpu_cluster_energy(int cpu)
{
struct sched_group_energy *sge = sge_array[cpu][SD_LEVEL1];
- if (!sge) {
+ if (sched_is_energy_aware() && !sge) {
pr_warn("Invalid sched_group_energy for Cluster%d\n", cpu);
return NULL;
}
@@ -407,7 +407,7 @@ const struct sched_group_energy * const cpu_core_energy(int cpu)
{
struct sched_group_energy *sge = sge_array[cpu][SD_LEVEL0];
- if (!sge) {
+ if (sched_is_energy_aware() && !sge) {
pr_warn("Invalid sched_group_energy for CPU%d\n", cpu);
return NULL;
}
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 5fc1112..296e139 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -43,7 +43,20 @@
#include <asm/kryo3xx-arm64-edac.h>
#include <soc/qcom/scm.h>
-static const char *fault_name(unsigned int esr);
+struct fault_info {
+ int (*fn)(unsigned long addr, unsigned int esr,
+ struct pt_regs *regs);
+ int sig;
+ int code;
+ const char *name;
+};
+
+static const struct fault_info fault_info[];
+
+static inline const struct fault_info *esr_to_fault_info(unsigned int esr)
+{
+ return fault_info + (esr & 63);
+}
#ifdef CONFIG_KPROBES
static inline int notify_page_fault(struct pt_regs *regs, unsigned int esr)
@@ -198,10 +211,12 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr,
struct pt_regs *regs)
{
struct siginfo si;
+ const struct fault_info *inf;
if (unhandled_signal(tsk, sig) && show_unhandled_signals_ratelimited()) {
+ inf = esr_to_fault_info(esr);
pr_info("%s[%d]: unhandled %s (%d) at 0x%08lx, esr 0x%03x\n",
- tsk->comm, task_pid_nr(tsk), fault_name(esr), sig,
+ tsk->comm, task_pid_nr(tsk), inf->name, sig,
addr, esr);
show_pte(tsk->mm, addr);
show_regs(regs);
@@ -220,14 +235,16 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re
{
struct task_struct *tsk = current;
struct mm_struct *mm = tsk->active_mm;
+ const struct fault_info *inf;
/*
* If we are in kernel mode at this point, we have no context to
* handle this fault with.
*/
- if (user_mode(regs))
- __do_user_fault(tsk, addr, esr, SIGSEGV, SEGV_MAPERR, regs);
- else
+ if (user_mode(regs)) {
+ inf = esr_to_fault_info(esr);
+ __do_user_fault(tsk, addr, esr, inf->sig, inf->code, regs);
+ } else
__do_kernel_fault(mm, addr, esr, regs);
}
@@ -507,12 +524,7 @@ static int do_bad(unsigned long addr, unsigned int esr, struct pt_regs *regs)
return 1;
}
-static const struct fault_info {
- int (*fn)(unsigned long addr, unsigned int esr, struct pt_regs *regs);
- int sig;
- int code;
- const char *name;
-} fault_info[] = {
+static const struct fault_info fault_info[] = {
{ do_bad, SIGBUS, 0, "ttbr address size fault" },
{ do_bad, SIGBUS, 0, "level 1 address size fault" },
{ do_bad, SIGBUS, 0, "level 2 address size fault" },
@@ -579,19 +591,13 @@ static const struct fault_info {
{ do_bad, SIGBUS, 0, "unknown 63" },
};
-static const char *fault_name(unsigned int esr)
-{
- const struct fault_info *inf = fault_info + (esr & 63);
- return inf->name;
-}
-
/*
* Dispatch a data abort to the relevant handler.
*/
asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
{
- const struct fault_info *inf = fault_info + (esr & 63);
+ const struct fault_info *inf = esr_to_fault_info(esr);
struct siginfo info;
if (!inf->fn(addr, esr, regs))
diff --git a/arch/metag/include/asm/uaccess.h b/arch/metag/include/asm/uaccess.h
index 273e612..07238b3 100644
--- a/arch/metag/include/asm/uaccess.h
+++ b/arch/metag/include/asm/uaccess.h
@@ -197,20 +197,21 @@ extern long __must_check strnlen_user(const char __user *src, long count);
#define strlen_user(str) strnlen_user(str, 32767)
-extern unsigned long __must_check __copy_user_zeroing(void *to,
- const void __user *from,
- unsigned long n);
+extern unsigned long raw_copy_from_user(void *to, const void __user *from,
+ unsigned long n);
static inline unsigned long
copy_from_user(void *to, const void __user *from, unsigned long n)
{
+ unsigned long res = n;
if (likely(access_ok(VERIFY_READ, from, n)))
- return __copy_user_zeroing(to, from, n);
- memset(to, 0, n);
- return n;
+ res = raw_copy_from_user(to, from, n);
+ if (unlikely(res))
+ memset(to + (n - res), 0, res);
+ return res;
}
-#define __copy_from_user(to, from, n) __copy_user_zeroing(to, from, n)
+#define __copy_from_user(to, from, n) raw_copy_from_user(to, from, n)
#define __copy_from_user_inatomic __copy_from_user
extern unsigned long __must_check __copy_user(void __user *to,
diff --git a/arch/metag/lib/usercopy.c b/arch/metag/lib/usercopy.c
index b3ebfe9..2792fc6 100644
--- a/arch/metag/lib/usercopy.c
+++ b/arch/metag/lib/usercopy.c
@@ -29,7 +29,6 @@
COPY \
"1:\n" \
" .section .fixup,\"ax\"\n" \
- " MOV D1Ar1,#0\n" \
FIXUP \
" MOVT D1Ar1,#HI(1b)\n" \
" JUMP D1Ar1,#LO(1b)\n" \
@@ -260,27 +259,31 @@
"MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
"22:\n" \
"MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %3, %3, #32\n" \
"23:\n" \
- "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
+ "SUB %3, %3, #32\n" \
"24:\n" \
+ "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
+ "25:\n" \
"MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "26:\n" \
"SUB %3, %3, #32\n" \
"DCACHE [%1+#-64], D0Ar6\n" \
"BR $Lloop"id"\n" \
\
"MOV RAPF, %1\n" \
- "25:\n" \
- "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "26:\n" \
- "MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %3, %3, #32\n" \
"27:\n" \
"MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
"28:\n" \
"MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %0, %0, #8\n" \
"29:\n" \
+ "SUB %3, %3, #32\n" \
+ "30:\n" \
+ "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
+ "31:\n" \
+ "MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "32:\n" \
+ "SUB %0, %0, #8\n" \
+ "33:\n" \
"SETL [%0++], D0.7, D1.7\n" \
"SUB %3, %3, #32\n" \
"1:" \
@@ -312,11 +315,15 @@
" .long 26b,3b\n" \
" .long 27b,3b\n" \
" .long 28b,3b\n" \
- " .long 29b,4b\n" \
+ " .long 29b,3b\n" \
+ " .long 30b,3b\n" \
+ " .long 31b,3b\n" \
+ " .long 32b,3b\n" \
+ " .long 33b,4b\n" \
" .previous\n" \
: "=r" (to), "=r" (from), "=r" (ret), "=d" (n) \
: "0" (to), "1" (from), "2" (ret), "3" (n) \
- : "D1Ar1", "D0Ar2", "memory")
+ : "D1Ar1", "D0Ar2", "cc", "memory")
/* rewind 'to' and 'from' pointers when a fault occurs
*
@@ -342,7 +349,7 @@
#define __asm_copy_to_user_64bit_rapf_loop(to, from, ret, n, id)\
__asm_copy_user_64bit_rapf_loop(to, from, ret, n, id, \
"LSR D0Ar2, D0Ar2, #8\n" \
- "AND D0Ar2, D0Ar2, #0x7\n" \
+ "ANDS D0Ar2, D0Ar2, #0x7\n" \
"ADDZ D0Ar2, D0Ar2, #4\n" \
"SUB D0Ar2, D0Ar2, #1\n" \
"MOV D1Ar1, #4\n" \
@@ -403,47 +410,55 @@
"MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
"22:\n" \
"MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %3, %3, #16\n" \
"23:\n" \
- "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "24:\n" \
- "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
"SUB %3, %3, #16\n" \
- "25:\n" \
+ "24:\n" \
"MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "26:\n" \
+ "25:\n" \
"MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "26:\n" \
"SUB %3, %3, #16\n" \
"27:\n" \
"MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
"28:\n" \
"MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "29:\n" \
+ "SUB %3, %3, #16\n" \
+ "30:\n" \
+ "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
+ "31:\n" \
+ "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "32:\n" \
"SUB %3, %3, #16\n" \
"DCACHE [%1+#-64], D0Ar6\n" \
"BR $Lloop"id"\n" \
\
"MOV RAPF, %1\n" \
- "29:\n" \
- "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "30:\n" \
- "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %3, %3, #16\n" \
- "31:\n" \
- "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "32:\n" \
- "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %3, %3, #16\n" \
"33:\n" \
"MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
"34:\n" \
"MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %3, %3, #16\n" \
"35:\n" \
- "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
+ "SUB %3, %3, #16\n" \
"36:\n" \
- "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %0, %0, #4\n" \
+ "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
"37:\n" \
+ "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "38:\n" \
+ "SUB %3, %3, #16\n" \
+ "39:\n" \
+ "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
+ "40:\n" \
+ "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "41:\n" \
+ "SUB %3, %3, #16\n" \
+ "42:\n" \
+ "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
+ "43:\n" \
+ "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "44:\n" \
+ "SUB %0, %0, #4\n" \
+ "45:\n" \
"SETD [%0++], D0.7\n" \
"SUB %3, %3, #16\n" \
"1:" \
@@ -483,11 +498,19 @@
" .long 34b,3b\n" \
" .long 35b,3b\n" \
" .long 36b,3b\n" \
- " .long 37b,4b\n" \
+ " .long 37b,3b\n" \
+ " .long 38b,3b\n" \
+ " .long 39b,3b\n" \
+ " .long 40b,3b\n" \
+ " .long 41b,3b\n" \
+ " .long 42b,3b\n" \
+ " .long 43b,3b\n" \
+ " .long 44b,3b\n" \
+ " .long 45b,4b\n" \
" .previous\n" \
: "=r" (to), "=r" (from), "=r" (ret), "=d" (n) \
: "0" (to), "1" (from), "2" (ret), "3" (n) \
- : "D1Ar1", "D0Ar2", "memory")
+ : "D1Ar1", "D0Ar2", "cc", "memory")
/* rewind 'to' and 'from' pointers when a fault occurs
*
@@ -513,7 +536,7 @@
#define __asm_copy_to_user_32bit_rapf_loop(to, from, ret, n, id)\
__asm_copy_user_32bit_rapf_loop(to, from, ret, n, id, \
"LSR D0Ar2, D0Ar2, #8\n" \
- "AND D0Ar2, D0Ar2, #0x7\n" \
+ "ANDS D0Ar2, D0Ar2, #0x7\n" \
"ADDZ D0Ar2, D0Ar2, #4\n" \
"SUB D0Ar2, D0Ar2, #1\n" \
"MOV D1Ar1, #4\n" \
@@ -538,23 +561,31 @@ unsigned long __copy_user(void __user *pdst, const void *psrc,
if ((unsigned long) src & 1) {
__asm_copy_to_user_1(dst, src, retn);
n--;
+ if (retn)
+ return retn + n;
}
if ((unsigned long) dst & 1) {
/* Worst case - byte copy */
while (n > 0) {
__asm_copy_to_user_1(dst, src, retn);
n--;
+ if (retn)
+ return retn + n;
}
}
if (((unsigned long) src & 2) && n >= 2) {
__asm_copy_to_user_2(dst, src, retn);
n -= 2;
+ if (retn)
+ return retn + n;
}
if ((unsigned long) dst & 2) {
/* Second worst case - word copy */
while (n >= 2) {
__asm_copy_to_user_2(dst, src, retn);
n -= 2;
+ if (retn)
+ return retn + n;
}
}
@@ -569,6 +600,8 @@ unsigned long __copy_user(void __user *pdst, const void *psrc,
while (n >= 8) {
__asm_copy_to_user_8x64(dst, src, retn);
n -= 8;
+ if (retn)
+ return retn + n;
}
}
if (n >= RAPF_MIN_BUF_SIZE) {
@@ -581,6 +614,8 @@ unsigned long __copy_user(void __user *pdst, const void *psrc,
while (n >= 8) {
__asm_copy_to_user_8x64(dst, src, retn);
n -= 8;
+ if (retn)
+ return retn + n;
}
}
#endif
@@ -588,11 +623,15 @@ unsigned long __copy_user(void __user *pdst, const void *psrc,
while (n >= 16) {
__asm_copy_to_user_16(dst, src, retn);
n -= 16;
+ if (retn)
+ return retn + n;
}
while (n >= 4) {
__asm_copy_to_user_4(dst, src, retn);
n -= 4;
+ if (retn)
+ return retn + n;
}
switch (n) {
@@ -609,6 +648,10 @@ unsigned long __copy_user(void __user *pdst, const void *psrc,
break;
}
+ /*
+ * If we get here, retn correctly reflects the number of failing
+ * bytes.
+ */
return retn;
}
EXPORT_SYMBOL(__copy_user);
@@ -617,16 +660,14 @@ EXPORT_SYMBOL(__copy_user);
__asm_copy_user_cont(to, from, ret, \
" GETB D1Ar1,[%1++]\n" \
"2: SETB [%0++],D1Ar1\n", \
- "3: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
+ "3: ADD %2,%2,#1\n", \
" .long 2b,3b\n")
#define __asm_copy_from_user_2x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
__asm_copy_user_cont(to, from, ret, \
" GETW D1Ar1,[%1++]\n" \
"2: SETW [%0++],D1Ar1\n" COPY, \
- "3: ADD %2,%2,#2\n" \
- " SETW [%0++],D1Ar1\n" FIXUP, \
+ "3: ADD %2,%2,#2\n" FIXUP, \
" .long 2b,3b\n" TENTRY)
#define __asm_copy_from_user_2(to, from, ret) \
@@ -636,145 +677,26 @@ EXPORT_SYMBOL(__copy_user);
__asm_copy_from_user_2x_cont(to, from, ret, \
" GETB D1Ar1,[%1++]\n" \
"4: SETB [%0++],D1Ar1\n", \
- "5: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
+ "5: ADD %2,%2,#1\n", \
" .long 4b,5b\n")
#define __asm_copy_from_user_4x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
__asm_copy_user_cont(to, from, ret, \
" GETD D1Ar1,[%1++]\n" \
"2: SETD [%0++],D1Ar1\n" COPY, \
- "3: ADD %2,%2,#4\n" \
- " SETD [%0++],D1Ar1\n" FIXUP, \
+ "3: ADD %2,%2,#4\n" FIXUP, \
" .long 2b,3b\n" TENTRY)
#define __asm_copy_from_user_4(to, from, ret) \
__asm_copy_from_user_4x_cont(to, from, ret, "", "", "")
-#define __asm_copy_from_user_5(to, from, ret) \
- __asm_copy_from_user_4x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "4: SETB [%0++],D1Ar1\n", \
- "5: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 4b,5b\n")
-
-#define __asm_copy_from_user_6x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_4x_cont(to, from, ret, \
- " GETW D1Ar1,[%1++]\n" \
- "4: SETW [%0++],D1Ar1\n" COPY, \
- "5: ADD %2,%2,#2\n" \
- " SETW [%0++],D1Ar1\n" FIXUP, \
- " .long 4b,5b\n" TENTRY)
-
-#define __asm_copy_from_user_6(to, from, ret) \
- __asm_copy_from_user_6x_cont(to, from, ret, "", "", "")
-
-#define __asm_copy_from_user_7(to, from, ret) \
- __asm_copy_from_user_6x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "6: SETB [%0++],D1Ar1\n", \
- "7: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 6b,7b\n")
-
-#define __asm_copy_from_user_8x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_4x_cont(to, from, ret, \
- " GETD D1Ar1,[%1++]\n" \
- "4: SETD [%0++],D1Ar1\n" COPY, \
- "5: ADD %2,%2,#4\n" \
- " SETD [%0++],D1Ar1\n" FIXUP, \
- " .long 4b,5b\n" TENTRY)
-
-#define __asm_copy_from_user_8(to, from, ret) \
- __asm_copy_from_user_8x_cont(to, from, ret, "", "", "")
-
-#define __asm_copy_from_user_9(to, from, ret) \
- __asm_copy_from_user_8x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "6: SETB [%0++],D1Ar1\n", \
- "7: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 6b,7b\n")
-
-#define __asm_copy_from_user_10x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_8x_cont(to, from, ret, \
- " GETW D1Ar1,[%1++]\n" \
- "6: SETW [%0++],D1Ar1\n" COPY, \
- "7: ADD %2,%2,#2\n" \
- " SETW [%0++],D1Ar1\n" FIXUP, \
- " .long 6b,7b\n" TENTRY)
-
-#define __asm_copy_from_user_10(to, from, ret) \
- __asm_copy_from_user_10x_cont(to, from, ret, "", "", "")
-
-#define __asm_copy_from_user_11(to, from, ret) \
- __asm_copy_from_user_10x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "8: SETB [%0++],D1Ar1\n", \
- "9: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 8b,9b\n")
-
-#define __asm_copy_from_user_12x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_8x_cont(to, from, ret, \
- " GETD D1Ar1,[%1++]\n" \
- "6: SETD [%0++],D1Ar1\n" COPY, \
- "7: ADD %2,%2,#4\n" \
- " SETD [%0++],D1Ar1\n" FIXUP, \
- " .long 6b,7b\n" TENTRY)
-
-#define __asm_copy_from_user_12(to, from, ret) \
- __asm_copy_from_user_12x_cont(to, from, ret, "", "", "")
-
-#define __asm_copy_from_user_13(to, from, ret) \
- __asm_copy_from_user_12x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "8: SETB [%0++],D1Ar1\n", \
- "9: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 8b,9b\n")
-
-#define __asm_copy_from_user_14x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_12x_cont(to, from, ret, \
- " GETW D1Ar1,[%1++]\n" \
- "8: SETW [%0++],D1Ar1\n" COPY, \
- "9: ADD %2,%2,#2\n" \
- " SETW [%0++],D1Ar1\n" FIXUP, \
- " .long 8b,9b\n" TENTRY)
-
-#define __asm_copy_from_user_14(to, from, ret) \
- __asm_copy_from_user_14x_cont(to, from, ret, "", "", "")
-
-#define __asm_copy_from_user_15(to, from, ret) \
- __asm_copy_from_user_14x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "10: SETB [%0++],D1Ar1\n", \
- "11: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 10b,11b\n")
-
-#define __asm_copy_from_user_16x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_12x_cont(to, from, ret, \
- " GETD D1Ar1,[%1++]\n" \
- "8: SETD [%0++],D1Ar1\n" COPY, \
- "9: ADD %2,%2,#4\n" \
- " SETD [%0++],D1Ar1\n" FIXUP, \
- " .long 8b,9b\n" TENTRY)
-
-#define __asm_copy_from_user_16(to, from, ret) \
- __asm_copy_from_user_16x_cont(to, from, ret, "", "", "")
-
#define __asm_copy_from_user_8x64(to, from, ret) \
asm volatile ( \
" GETL D0Ar2,D1Ar1,[%1++]\n" \
"2: SETL [%0++],D0Ar2,D1Ar1\n" \
"1:\n" \
" .section .fixup,\"ax\"\n" \
- " MOV D1Ar1,#0\n" \
- " MOV D0Ar2,#0\n" \
"3: ADD %2,%2,#8\n" \
- " SETL [%0++],D0Ar2,D1Ar1\n" \
" MOVT D0Ar2,#HI(1b)\n" \
" JUMP D0Ar2,#LO(1b)\n" \
" .previous\n" \
@@ -789,36 +711,57 @@ EXPORT_SYMBOL(__copy_user);
*
* Rationale:
* A fault occurs while reading from user buffer, which is the
- * source. Since the fault is at a single address, we only
- * need to rewind by 8 bytes.
+ * source.
* Since we don't write to kernel buffer until we read first,
* the kernel buffer is at the right state and needn't be
- * corrected.
+ * corrected, but the source must be rewound to the beginning of
+ * the block, which is LSM_STEP*8 bytes.
+ * LSM_STEP is bits 10:8 in TXSTATUS which is already read
+ * and stored in D0Ar2
+ *
+ * NOTE: If a fault occurs at the last operation in M{G,S}ETL
+ * LSM_STEP will be 0. ie: we do 4 writes in our case, if
+ * a fault happens at the 4th write, LSM_STEP will be 0
+ * instead of 4. The code copes with that.
*/
#define __asm_copy_from_user_64bit_rapf_loop(to, from, ret, n, id) \
__asm_copy_user_64bit_rapf_loop(to, from, ret, n, id, \
- "SUB %1, %1, #8\n")
+ "LSR D0Ar2, D0Ar2, #5\n" \
+ "ANDS D0Ar2, D0Ar2, #0x38\n" \
+ "ADDZ D0Ar2, D0Ar2, #32\n" \
+ "SUB %1, %1, D0Ar2\n")
/* rewind 'from' pointer when a fault occurs
*
* Rationale:
* A fault occurs while reading from user buffer, which is the
- * source. Since the fault is at a single address, we only
- * need to rewind by 4 bytes.
+ * source.
* Since we don't write to kernel buffer until we read first,
* the kernel buffer is at the right state and needn't be
- * corrected.
+ * corrected, but the source must be rewound to the beginning of
+ * the block, which is LSM_STEP*4 bytes.
+ * LSM_STEP is bits 10:8 in TXSTATUS which is already read
+ * and stored in D0Ar2
+ *
+ * NOTE: If a fault occurs at the last operation in M{G,S}ETL
+ * LSM_STEP will be 0. ie: we do 4 writes in our case, if
+ * a fault happens at the 4th write, LSM_STEP will be 0
+ * instead of 4. The code copes with that.
*/
#define __asm_copy_from_user_32bit_rapf_loop(to, from, ret, n, id) \
__asm_copy_user_32bit_rapf_loop(to, from, ret, n, id, \
- "SUB %1, %1, #4\n")
+ "LSR D0Ar2, D0Ar2, #6\n" \
+ "ANDS D0Ar2, D0Ar2, #0x1c\n" \
+ "ADDZ D0Ar2, D0Ar2, #16\n" \
+ "SUB %1, %1, D0Ar2\n")
-/* Copy from user to kernel, zeroing the bytes that were inaccessible in
- userland. The return-value is the number of bytes that were
- inaccessible. */
-unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc,
- unsigned long n)
+/*
+ * Copy from user to kernel. The return-value is the number of bytes that were
+ * inaccessible.
+ */
+unsigned long raw_copy_from_user(void *pdst, const void __user *psrc,
+ unsigned long n)
{
register char *dst asm ("A0.2") = pdst;
register const char __user *src asm ("A1.2") = psrc;
@@ -830,6 +773,8 @@ unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc,
if ((unsigned long) src & 1) {
__asm_copy_from_user_1(dst, src, retn);
n--;
+ if (retn)
+ return retn + n;
}
if ((unsigned long) dst & 1) {
/* Worst case - byte copy */
@@ -837,12 +782,14 @@ unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc,
__asm_copy_from_user_1(dst, src, retn);
n--;
if (retn)
- goto copy_exception_bytes;
+ return retn + n;
}
}
if (((unsigned long) src & 2) && n >= 2) {
__asm_copy_from_user_2(dst, src, retn);
n -= 2;
+ if (retn)
+ return retn + n;
}
if ((unsigned long) dst & 2) {
/* Second worst case - word copy */
@@ -850,16 +797,10 @@ unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc,
__asm_copy_from_user_2(dst, src, retn);
n -= 2;
if (retn)
- goto copy_exception_bytes;
+ return retn + n;
}
}
- /* We only need one check after the unalignment-adjustments,
- because if both adjustments were done, either both or
- neither reference had an exception. */
- if (retn != 0)
- goto copy_exception_bytes;
-
#ifdef USE_RAPF
/* 64 bit copy loop */
if (!(((unsigned long) src | (unsigned long) dst) & 7)) {
@@ -872,7 +813,7 @@ unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc,
__asm_copy_from_user_8x64(dst, src, retn);
n -= 8;
if (retn)
- goto copy_exception_bytes;
+ return retn + n;
}
}
@@ -888,7 +829,7 @@ unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc,
__asm_copy_from_user_8x64(dst, src, retn);
n -= 8;
if (retn)
- goto copy_exception_bytes;
+ return retn + n;
}
}
#endif
@@ -898,7 +839,7 @@ unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc,
n -= 4;
if (retn)
- goto copy_exception_bytes;
+ return retn + n;
}
/* If we get here, there were no memory read faults. */
@@ -924,21 +865,8 @@ unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc,
/* If we get here, retn correctly reflects the number of failing
bytes. */
return retn;
-
- copy_exception_bytes:
- /* We already have "retn" bytes cleared, and need to clear the
- remaining "n" bytes. A non-optimized simple byte-for-byte in-line
- memset is preferred here, since this isn't speed-critical code and
- we'd rather have this a leaf-function than calling memset. */
- {
- char *endp;
- for (endp = dst + n; dst < endp; dst++)
- *dst = 0;
- }
-
- return retn + n;
}
-EXPORT_SYMBOL(__copy_user_zeroing);
+EXPORT_SYMBOL(raw_copy_from_user);
#define __asm_clear_8x64(to, ret) \
asm volatile ( \
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index b3c5bde..5a4f2eb 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -9,6 +9,7 @@
select HAVE_CONTEXT_TRACKING
select HAVE_GENERIC_DMA_COHERENT
select HAVE_IDE
+ select HAVE_IRQ_EXIT_ON_IRQ_STACK
select HAVE_OPROFILE
select HAVE_PERF_EVENTS
select PERF_USE_VMALLOC
@@ -1526,7 +1527,7 @@
select CPU_SUPPORTS_HIGHMEM
select CPU_SUPPORTS_MSA
select GENERIC_CSUM
- select MIPS_O32_FP64_SUPPORT if MIPS32_O32
+ select MIPS_O32_FP64_SUPPORT if 32BIT || MIPS32_O32
select HAVE_KVM
help
Choose this option to build a kernel for release 6 or later of the
diff --git a/arch/mips/include/asm/irq.h b/arch/mips/include/asm/irq.h
index 6bf10e7..956db6e 100644
--- a/arch/mips/include/asm/irq.h
+++ b/arch/mips/include/asm/irq.h
@@ -17,6 +17,18 @@
#include <irq.h>
+#define IRQ_STACK_SIZE THREAD_SIZE
+
+extern void *irq_stack[NR_CPUS];
+
+static inline bool on_irq_stack(int cpu, unsigned long sp)
+{
+ unsigned long low = (unsigned long)irq_stack[cpu];
+ unsigned long high = low + IRQ_STACK_SIZE;
+
+ return (low <= sp && sp <= high);
+}
+
#ifdef CONFIG_I8259
static inline int irq_canonicalize(int irq)
{
diff --git a/arch/mips/include/asm/spinlock.h b/arch/mips/include/asm/spinlock.h
index f485afe..a8df44d 100644
--- a/arch/mips/include/asm/spinlock.h
+++ b/arch/mips/include/asm/spinlock.h
@@ -127,7 +127,7 @@ static inline void arch_spin_lock(arch_spinlock_t *lock)
" andi %[ticket], %[ticket], 0xffff \n"
" bne %[ticket], %[my_ticket], 4f \n"
" subu %[ticket], %[my_ticket], %[ticket] \n"
- "2: \n"
+ "2: .insn \n"
" .subsection 2 \n"
"4: andi %[ticket], %[ticket], 0xffff \n"
" sll %[ticket], 5 \n"
@@ -202,7 +202,7 @@ static inline unsigned int arch_spin_trylock(arch_spinlock_t *lock)
" sc %[ticket], %[ticket_ptr] \n"
" beqz %[ticket], 1b \n"
" li %[ticket], 1 \n"
- "2: \n"
+ "2: .insn \n"
" .subsection 2 \n"
"3: b 2b \n"
" li %[ticket], 0 \n"
@@ -382,7 +382,7 @@ static inline int arch_read_trylock(arch_rwlock_t *rw)
" .set reorder \n"
__WEAK_LLSC_MB
" li %2, 1 \n"
- "2: \n"
+ "2: .insn \n"
: "=" GCC_OFF_SMALL_ASM() (rw->lock), "=&r" (tmp), "=&r" (ret)
: GCC_OFF_SMALL_ASM() (rw->lock)
: "memory");
@@ -422,7 +422,7 @@ static inline int arch_write_trylock(arch_rwlock_t *rw)
" lui %1, 0x8000 \n"
" sc %1, %0 \n"
" li %2, 1 \n"
- "2: \n"
+ "2: .insn \n"
: "=" GCC_OFF_SMALL_ASM() (rw->lock), "=&r" (tmp),
"=&r" (ret)
: GCC_OFF_SMALL_ASM() (rw->lock)
diff --git a/arch/mips/include/asm/stackframe.h b/arch/mips/include/asm/stackframe.h
index eebf395..2f182bd 100644
--- a/arch/mips/include/asm/stackframe.h
+++ b/arch/mips/include/asm/stackframe.h
@@ -216,12 +216,19 @@
LONG_S $25, PT_R25(sp)
LONG_S $28, PT_R28(sp)
LONG_S $31, PT_R31(sp)
+
+ /* Set thread_info if we're coming from user mode */
+ mfc0 k0, CP0_STATUS
+ sll k0, 3 /* extract cu0 bit */
+ bltz k0, 9f
+
ori $28, sp, _THREAD_MASK
xori $28, _THREAD_MASK
#ifdef CONFIG_CPU_CAVIUM_OCTEON
.set mips64
pref 0, 0($28) /* Prefetch the current pointer */
#endif
+9:
.set pop
.endm
diff --git a/arch/mips/kernel/asm-offsets.c b/arch/mips/kernel/asm-offsets.c
index fae2f94..4be2763 100644
--- a/arch/mips/kernel/asm-offsets.c
+++ b/arch/mips/kernel/asm-offsets.c
@@ -102,6 +102,7 @@ void output_thread_info_defines(void)
OFFSET(TI_REGS, thread_info, regs);
DEFINE(_THREAD_SIZE, THREAD_SIZE);
DEFINE(_THREAD_MASK, THREAD_MASK);
+ DEFINE(_IRQ_STACK_SIZE, IRQ_STACK_SIZE);
BLANK();
}
diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
index dd31754..921211b 100644
--- a/arch/mips/kernel/cpu-probe.c
+++ b/arch/mips/kernel/cpu-probe.c
@@ -1824,7 +1824,7 @@ static inline void cpu_probe_loongson(struct cpuinfo_mips *c, unsigned int cpu)
}
decode_configs(c);
- c->options |= MIPS_CPU_TLBINV | MIPS_CPU_LDPTE;
+ c->options |= MIPS_CPU_FTLB | MIPS_CPU_TLBINV | MIPS_CPU_LDPTE;
c->writecombine = _CACHE_UNCACHED_ACCELERATED;
break;
default:
diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S
index dc0b296..2ac6c26 100644
--- a/arch/mips/kernel/genex.S
+++ b/arch/mips/kernel/genex.S
@@ -187,9 +187,44 @@
LONG_L s0, TI_REGS($28)
LONG_S sp, TI_REGS($28)
- PTR_LA ra, ret_from_irq
- PTR_LA v0, plat_irq_dispatch
- jr v0
+
+ /*
+ * SAVE_ALL ensures we are using a valid kernel stack for the thread.
+ * Check if we are already using the IRQ stack.
+ */
+ move s1, sp # Preserve the sp
+
+ /* Get IRQ stack for this CPU */
+ ASM_CPUID_MFC0 k0, ASM_SMP_CPUID_REG
+#if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32)
+ lui k1, %hi(irq_stack)
+#else
+ lui k1, %highest(irq_stack)
+ daddiu k1, %higher(irq_stack)
+ dsll k1, 16
+ daddiu k1, %hi(irq_stack)
+ dsll k1, 16
+#endif
+ LONG_SRL k0, SMP_CPUID_PTRSHIFT
+ LONG_ADDU k1, k0
+ LONG_L t0, %lo(irq_stack)(k1)
+
+ # Check if already on IRQ stack
+ PTR_LI t1, ~(_THREAD_SIZE-1)
+ and t1, t1, sp
+ beq t0, t1, 2f
+
+ /* Switch to IRQ stack */
+ li t1, _IRQ_STACK_SIZE
+ PTR_ADD sp, t0, t1
+
+2:
+ jal plat_irq_dispatch
+
+ /* Restore sp */
+ move sp, s1
+
+ j ret_from_irq
#ifdef CONFIG_CPU_MICROMIPS
nop
#endif
@@ -262,8 +297,44 @@
LONG_L s0, TI_REGS($28)
LONG_S sp, TI_REGS($28)
- PTR_LA ra, ret_from_irq
- jr v0
+
+ /*
+ * SAVE_ALL ensures we are using a valid kernel stack for the thread.
+ * Check if we are already using the IRQ stack.
+ */
+ move s1, sp # Preserve the sp
+
+ /* Get IRQ stack for this CPU */
+ ASM_CPUID_MFC0 k0, ASM_SMP_CPUID_REG
+#if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32)
+ lui k1, %hi(irq_stack)
+#else
+ lui k1, %highest(irq_stack)
+ daddiu k1, %higher(irq_stack)
+ dsll k1, 16
+ daddiu k1, %hi(irq_stack)
+ dsll k1, 16
+#endif
+ LONG_SRL k0, SMP_CPUID_PTRSHIFT
+ LONG_ADDU k1, k0
+ LONG_L t0, %lo(irq_stack)(k1)
+
+ # Check if already on IRQ stack
+ PTR_LI t1, ~(_THREAD_SIZE-1)
+ and t1, t1, sp
+ beq t0, t1, 2f
+
+ /* Switch to IRQ stack */
+ li t1, _IRQ_STACK_SIZE
+ PTR_ADD sp, t0, t1
+
+2:
+ jalr v0
+
+ /* Restore sp */
+ move sp, s1
+
+ j ret_from_irq
END(except_vec_vi_handler)
/*
@@ -448,7 +519,7 @@
BUILD_HANDLER reserved reserved sti verbose /* others */
.align 5
- LEAF(handle_ri_rdhwr_vivt)
+ LEAF(handle_ri_rdhwr_tlbp)
.set push
.set noat
.set noreorder
@@ -467,7 +538,7 @@
.set pop
bltz k1, handle_ri /* slow path */
/* fall thru */
- END(handle_ri_rdhwr_vivt)
+ END(handle_ri_rdhwr_tlbp)
LEAF(handle_ri_rdhwr)
.set push
diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c
index f25f7ea..2b0a371 100644
--- a/arch/mips/kernel/irq.c
+++ b/arch/mips/kernel/irq.c
@@ -25,6 +25,8 @@
#include <linux/atomic.h>
#include <asm/uaccess.h>
+void *irq_stack[NR_CPUS];
+
/*
* 'what should we do if we get a hw irq event on an illegal vector'.
* each architecture has to answer this themselves.
@@ -58,6 +60,15 @@ void __init init_IRQ(void)
clear_c0_status(ST0_IM);
arch_init_irq();
+
+ for_each_possible_cpu(i) {
+ int irq_pages = IRQ_STACK_SIZE / PAGE_SIZE;
+ void *s = (void *)__get_free_pages(GFP_KERNEL, irq_pages);
+
+ irq_stack[i] = s;
+ pr_debug("CPU%d IRQ stack at 0x%p - 0x%p\n", i,
+ irq_stack[i], irq_stack[i] + IRQ_STACK_SIZE);
+ }
}
#ifdef CONFIG_DEBUG_STACKOVERFLOW
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index 1652f36..fbbf5fc 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -33,6 +33,7 @@
#include <asm/dsemul.h>
#include <asm/dsp.h>
#include <asm/fpu.h>
+#include <asm/irq.h>
#include <asm/msa.h>
#include <asm/pgtable.h>
#include <asm/mipsregs.h>
@@ -556,7 +557,19 @@ EXPORT_SYMBOL(unwind_stack_by_address);
unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
unsigned long pc, unsigned long *ra)
{
- unsigned long stack_page = (unsigned long)task_stack_page(task);
+ unsigned long stack_page = 0;
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ if (on_irq_stack(cpu, *sp)) {
+ stack_page = (unsigned long)irq_stack[cpu];
+ break;
+ }
+ }
+
+ if (!stack_page)
+ stack_page = (unsigned long)task_stack_page(task);
+
return unwind_stack_by_address(stack_page, sp, pc, ra);
}
#endif
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 3905003..ec87ef9 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -81,7 +81,7 @@ extern asmlinkage void handle_dbe(void);
extern asmlinkage void handle_sys(void);
extern asmlinkage void handle_bp(void);
extern asmlinkage void handle_ri(void);
-extern asmlinkage void handle_ri_rdhwr_vivt(void);
+extern asmlinkage void handle_ri_rdhwr_tlbp(void);
extern asmlinkage void handle_ri_rdhwr(void);
extern asmlinkage void handle_cpu(void);
extern asmlinkage void handle_ov(void);
@@ -2352,9 +2352,18 @@ void __init trap_init(void)
set_except_vector(EXCCODE_SYS, handle_sys);
set_except_vector(EXCCODE_BP, handle_bp);
- set_except_vector(EXCCODE_RI, rdhwr_noopt ? handle_ri :
- (cpu_has_vtag_icache ?
- handle_ri_rdhwr_vivt : handle_ri_rdhwr));
+
+ if (rdhwr_noopt)
+ set_except_vector(EXCCODE_RI, handle_ri);
+ else {
+ if (cpu_has_vtag_icache)
+ set_except_vector(EXCCODE_RI, handle_ri_rdhwr_tlbp);
+ else if (current_cpu_type() == CPU_LOONGSON3)
+ set_except_vector(EXCCODE_RI, handle_ri_rdhwr_tlbp);
+ else
+ set_except_vector(EXCCODE_RI, handle_ri_rdhwr);
+ }
+
set_except_vector(EXCCODE_CPU, handle_cpu);
set_except_vector(EXCCODE_OV, handle_ov);
set_except_vector(EXCCODE_TR, handle_tr);
diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c
index 9a61671..9056547 100644
--- a/arch/mips/lantiq/xway/sysctrl.c
+++ b/arch/mips/lantiq/xway/sysctrl.c
@@ -467,7 +467,7 @@ void __init ltq_soc_init(void)
if (!np_xbar)
panic("Failed to load xbar nodes from devicetree");
- if (of_address_to_resource(np_pmu, 0, &res_xbar))
+ if (of_address_to_resource(np_xbar, 0, &res_xbar))
panic("Failed to get xbar resources");
if (request_mem_region(res_xbar.start, resource_size(&res_xbar),
res_xbar.name) < 0)
diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
index 88cfaf8..9d0107f 100644
--- a/arch/mips/mm/c-r4k.c
+++ b/arch/mips/mm/c-r4k.c
@@ -1558,6 +1558,7 @@ static void probe_vcache(void)
vcache_size = c->vcache.sets * c->vcache.ways * c->vcache.linesz;
c->vcache.waybit = 0;
+ c->vcache.waysize = vcache_size / c->vcache.ways;
pr_info("Unified victim cache %ldkB %s, linesize %d bytes.\n",
vcache_size >> 10, way_string[c->vcache.ways], c->vcache.linesz);
@@ -1660,6 +1661,7 @@ static void __init loongson3_sc_init(void)
/* Loongson-3 has 4 cores, 1MB scache for each. scaches are shared */
scache_size *= 4;
c->scache.waybit = 0;
+ c->scache.waysize = scache_size / c->scache.ways;
pr_info("Unified secondary cache %ldkB %s, linesize %d bytes.\n",
scache_size >> 10, way_string[c->scache.ways], c->scache.linesz);
if (scache_size)
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index 55ce396..2da5649 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -762,7 +762,8 @@ static void build_huge_update_entries(u32 **p, unsigned int pte,
static void build_huge_handler_tail(u32 **p, struct uasm_reloc **r,
struct uasm_label **l,
unsigned int pte,
- unsigned int ptr)
+ unsigned int ptr,
+ unsigned int flush)
{
#ifdef CONFIG_SMP
UASM_i_SC(p, pte, 0, ptr);
@@ -771,6 +772,22 @@ static void build_huge_handler_tail(u32 **p, struct uasm_reloc **r,
#else
UASM_i_SW(p, pte, 0, ptr);
#endif
+ if (cpu_has_ftlb && flush) {
+ BUG_ON(!cpu_has_tlbinv);
+
+ UASM_i_MFC0(p, ptr, C0_ENTRYHI);
+ uasm_i_ori(p, ptr, ptr, MIPS_ENTRYHI_EHINV);
+ UASM_i_MTC0(p, ptr, C0_ENTRYHI);
+ build_tlb_write_entry(p, l, r, tlb_indexed);
+
+ uasm_i_xori(p, ptr, ptr, MIPS_ENTRYHI_EHINV);
+ UASM_i_MTC0(p, ptr, C0_ENTRYHI);
+ build_huge_update_entries(p, pte, ptr);
+ build_huge_tlb_write_entry(p, l, r, pte, tlb_random, 0);
+
+ return;
+ }
+
build_huge_update_entries(p, pte, ptr);
build_huge_tlb_write_entry(p, l, r, pte, tlb_indexed, 0);
}
@@ -2197,7 +2214,7 @@ static void build_r4000_tlb_load_handler(void)
uasm_l_tlbl_goaround2(&l, p);
}
uasm_i_ori(&p, wr.r1, wr.r1, (_PAGE_ACCESSED | _PAGE_VALID));
- build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2);
+ build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2, 1);
#endif
uasm_l_nopage_tlbl(&l, p);
@@ -2252,7 +2269,7 @@ static void build_r4000_tlb_store_handler(void)
build_tlb_probe_entry(&p);
uasm_i_ori(&p, wr.r1, wr.r1,
_PAGE_ACCESSED | _PAGE_MODIFIED | _PAGE_VALID | _PAGE_DIRTY);
- build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2);
+ build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2, 1);
#endif
uasm_l_nopage_tlbs(&l, p);
@@ -2308,7 +2325,7 @@ static void build_r4000_tlb_modify_handler(void)
build_tlb_probe_entry(&p);
uasm_i_ori(&p, wr.r1, wr.r1,
_PAGE_ACCESSED | _PAGE_MODIFIED | _PAGE_VALID | _PAGE_DIRTY);
- build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2);
+ build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2, 0);
#endif
uasm_l_nopage_tlbm(&l, p);
diff --git a/arch/mips/ralink/rt3883.c b/arch/mips/ralink/rt3883.c
index 3e0aa09..9e4631a 100644
--- a/arch/mips/ralink/rt3883.c
+++ b/arch/mips/ralink/rt3883.c
@@ -36,7 +36,7 @@ static struct rt2880_pmx_func uartlite_func[] = { FUNC("uartlite", 0, 15, 2) };
static struct rt2880_pmx_func jtag_func[] = { FUNC("jtag", 0, 17, 5) };
static struct rt2880_pmx_func mdio_func[] = { FUNC("mdio", 0, 22, 2) };
static struct rt2880_pmx_func lna_a_func[] = { FUNC("lna a", 0, 32, 3) };
-static struct rt2880_pmx_func lna_g_func[] = { FUNC("lna a", 0, 35, 3) };
+static struct rt2880_pmx_func lna_g_func[] = { FUNC("lna g", 0, 35, 3) };
static struct rt2880_pmx_func pci_func[] = {
FUNC("pci-dev", 0, 40, 32),
FUNC("pci-host2", 1, 40, 32),
@@ -44,7 +44,7 @@ static struct rt2880_pmx_func pci_func[] = {
FUNC("pci-fnc", 3, 40, 32)
};
static struct rt2880_pmx_func ge1_func[] = { FUNC("ge1", 0, 72, 12) };
-static struct rt2880_pmx_func ge2_func[] = { FUNC("ge1", 0, 84, 12) };
+static struct rt2880_pmx_func ge2_func[] = { FUNC("ge2", 0, 84, 12) };
static struct rt2880_pmx_group rt3883_pinmux_data[] = {
GRP("i2c", i2c_func, 1, RT3883_GPIO_MODE_I2C),
diff --git a/arch/nios2/kernel/prom.c b/arch/nios2/kernel/prom.c
index 367c542..3901b80 100644
--- a/arch/nios2/kernel/prom.c
+++ b/arch/nios2/kernel/prom.c
@@ -48,6 +48,13 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align)
return alloc_bootmem_align(size, align);
}
+int __init early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size,
+ bool nomap)
+{
+ reserve_bootmem(base, size, BOOTMEM_DEFAULT);
+ return 0;
+}
+
void __init early_init_devtree(void *params)
{
__be32 *dtb = (u32 *)__dtb_start;
diff --git a/arch/nios2/kernel/setup.c b/arch/nios2/kernel/setup.c
index a4ff86d..6c4e351 100644
--- a/arch/nios2/kernel/setup.c
+++ b/arch/nios2/kernel/setup.c
@@ -195,6 +195,9 @@ void __init setup_arch(char **cmdline_p)
}
#endif /* CONFIG_BLK_DEV_INITRD */
+ early_init_fdt_reserve_self();
+ early_init_fdt_scan_reserved_mem();
+
unflatten_and_copy_device_tree();
setup_cpuinfo();
diff --git a/arch/powerpc/crypto/crc32c-vpmsum_glue.c b/arch/powerpc/crypto/crc32c-vpmsum_glue.c
index 4119945..f058e0c 100644
--- a/arch/powerpc/crypto/crc32c-vpmsum_glue.c
+++ b/arch/powerpc/crypto/crc32c-vpmsum_glue.c
@@ -33,10 +33,13 @@ static u32 crc32c_vpmsum(u32 crc, unsigned char const *p, size_t len)
}
if (len & ~VMX_ALIGN_MASK) {
+ preempt_disable();
pagefault_disable();
enable_kernel_altivec();
crc = __crc32c_vpmsum(crc, p, len & ~VMX_ALIGN_MASK);
+ disable_kernel_altivec();
pagefault_enable();
+ preempt_enable();
}
tail = len & VMX_ALIGN_MASK;
diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c
index 033f338..b2da7c8 100644
--- a/arch/powerpc/kernel/align.c
+++ b/arch/powerpc/kernel/align.c
@@ -807,14 +807,25 @@ int fix_alignment(struct pt_regs *regs)
nb = aligninfo[instr].len;
flags = aligninfo[instr].flags;
- /* ldbrx/stdbrx overlap lfs/stfs in the DSISR unfortunately */
- if (IS_XFORM(instruction) && ((instruction >> 1) & 0x3ff) == 532) {
- nb = 8;
- flags = LD+SW;
- } else if (IS_XFORM(instruction) &&
- ((instruction >> 1) & 0x3ff) == 660) {
- nb = 8;
- flags = ST+SW;
+ /*
+ * Handle some cases which give overlaps in the DSISR values.
+ */
+ if (IS_XFORM(instruction)) {
+ switch (get_xop(instruction)) {
+ case 532: /* ldbrx */
+ nb = 8;
+ flags = LD+SW;
+ break;
+ case 660: /* stdbrx */
+ nb = 8;
+ flags = ST+SW;
+ break;
+ case 20: /* lwarx */
+ case 84: /* ldarx */
+ case 116: /* lharx */
+ case 276: /* lqarx */
+ return 0; /* not emulated ever */
+ }
}
/* Byteswap little endian loads and stores */
diff --git a/arch/powerpc/kernel/misc_64.S b/arch/powerpc/kernel/misc_64.S
index 4f17867..4cefe688 100644
--- a/arch/powerpc/kernel/misc_64.S
+++ b/arch/powerpc/kernel/misc_64.S
@@ -67,7 +67,7 @@
* flush all bytes from start through stop-1 inclusive
*/
-_GLOBAL(flush_icache_range)
+_GLOBAL_TOC(flush_icache_range)
BEGIN_FTR_SECTION
PURGE_PREFETCHED_INS
blr
@@ -120,7 +120,7 @@
*
* flush all bytes from start to stop-1 inclusive
*/
-_GLOBAL(flush_dcache_range)
+_GLOBAL_TOC(flush_dcache_range)
/*
* Flush the data cache to memory
diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c
index 8d586cf..a12be60 100644
--- a/arch/powerpc/kernel/setup_64.c
+++ b/arch/powerpc/kernel/setup_64.c
@@ -245,6 +245,15 @@ static void cpu_ready_for_interrupts(void)
mtspr(SPRN_LPCR, lpcr | LPCR_AIL_3);
}
+ /*
+ * Fixup HFSCR:TM based on CPU features. The bit is set by our
+ * early asm init because at that point we haven't updated our
+ * CPU features from firmware and device-tree. Here we have,
+ * so let's do it.
+ */
+ if (cpu_has_feature(CPU_FTR_HVMODE) && !cpu_has_feature(CPU_FTR_TM_COMP))
+ mtspr(SPRN_HFSCR, mfspr(SPRN_HFSCR) & ~HFSCR_TM);
+
/* Set IR and DR in PACA MSR */
get_paca()->kernel_msr = MSR_KERNEL;
}
diff --git a/arch/powerpc/mm/hash_native_64.c b/arch/powerpc/mm/hash_native_64.c
index ad9fd52..197f0a6 100644
--- a/arch/powerpc/mm/hash_native_64.c
+++ b/arch/powerpc/mm/hash_native_64.c
@@ -636,6 +636,10 @@ static void native_flush_hash_range(unsigned long number, int local)
unsigned long psize = batch->psize;
int ssize = batch->ssize;
int i;
+ unsigned int use_local;
+
+ use_local = local && mmu_has_feature(MMU_FTR_TLBIEL) &&
+ mmu_psize_defs[psize].tlbiel && !cxl_ctx_in_use();
local_irq_save(flags);
@@ -665,8 +669,7 @@ static void native_flush_hash_range(unsigned long number, int local)
} pte_iterate_hashed_end();
}
- if (mmu_has_feature(MMU_FTR_TLBIEL) &&
- mmu_psize_defs[psize].tlbiel && local) {
+ if (use_local) {
asm volatile("ptesync":::"memory");
for (i = 0; i < number; i++) {
vpn = batch->vpn[i];
diff --git a/arch/s390/boot/compressed/misc.c b/arch/s390/boot/compressed/misc.c
index 4da604e..ca15613 100644
--- a/arch/s390/boot/compressed/misc.c
+++ b/arch/s390/boot/compressed/misc.c
@@ -141,31 +141,34 @@ static void check_ipl_parmblock(void *start, unsigned long size)
unsigned long decompress_kernel(void)
{
- unsigned long output_addr;
- unsigned char *output;
+ void *output, *kernel_end;
- output_addr = ((unsigned long) &_end + HEAP_SIZE + 4095UL) & -4096UL;
- check_ipl_parmblock((void *) 0, output_addr + SZ__bss_start);
- memset(&_bss, 0, &_ebss - &_bss);
- free_mem_ptr = (unsigned long)&_end;
- free_mem_end_ptr = free_mem_ptr + HEAP_SIZE;
- output = (unsigned char *) output_addr;
+ output = (void *) ALIGN((unsigned long) &_end + HEAP_SIZE, PAGE_SIZE);
+ kernel_end = output + SZ__bss_start;
+ check_ipl_parmblock((void *) 0, (unsigned long) kernel_end);
#ifdef CONFIG_BLK_DEV_INITRD
/*
* Move the initrd right behind the end of the decompressed
- * kernel image.
+ * kernel image. This also prevents initrd corruption caused by
+ * bss clearing since kernel_end will always be located behind the
+ * current bss section..
*/
- if (INITRD_START && INITRD_SIZE &&
- INITRD_START < (unsigned long) output + SZ__bss_start) {
- check_ipl_parmblock(output + SZ__bss_start,
- INITRD_START + INITRD_SIZE);
- memmove(output + SZ__bss_start,
- (void *) INITRD_START, INITRD_SIZE);
- INITRD_START = (unsigned long) output + SZ__bss_start;
+ if (INITRD_START && INITRD_SIZE && kernel_end > (void *) INITRD_START) {
+ check_ipl_parmblock(kernel_end, INITRD_SIZE);
+ memmove(kernel_end, (void *) INITRD_START, INITRD_SIZE);
+ INITRD_START = (unsigned long) kernel_end;
}
#endif
+ /*
+ * Clear bss section. free_mem_ptr and free_mem_end_ptr need to be
+ * initialized afterwards since they reside in bss.
+ */
+ memset(&_bss, 0, &_ebss - &_bss);
+ free_mem_ptr = (unsigned long) &_end;
+ free_mem_end_ptr = free_mem_ptr + HEAP_SIZE;
+
puts("Uncompressing Linux... ");
__decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error);
puts("Ok, booting the kernel.\n");
diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h
index 52d7c87..a7ef702 100644
--- a/arch/s390/include/asm/uaccess.h
+++ b/arch/s390/include/asm/uaccess.h
@@ -144,7 +144,7 @@ unsigned long __must_check __copy_to_user(void __user *to, const void *from,
" jg 2b\n" \
".popsection\n" \
EX_TABLE(0b,3b) EX_TABLE(1b,3b) \
- : "=d" (__rc), "=Q" (*(to)) \
+ : "=d" (__rc), "+Q" (*(to)) \
: "d" (size), "Q" (*(from)), \
"d" (__reg0), "K" (-EFAULT) \
: "cc"); \
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index e244c19..067f981 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -223,6 +223,22 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = {
DMI_MATCH(DMI_BOARD_NAME, "P4S800"),
},
},
+ { /* Handle problems with rebooting on ASUS EeeBook X205TA */
+ .callback = set_acpi_reboot,
+ .ident = "ASUS EeeBook X205TA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X205TA"),
+ },
+ },
+ { /* Handle problems with rebooting on ASUS EeeBook X205TAW */
+ .callback = set_acpi_reboot,
+ .ident = "ASUS EeeBook X205TAW",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X205TAW"),
+ },
+ },
/* Certec */
{ /* Handle problems with rebooting on Certec BPC600 */
diff --git a/arch/xtensa/include/asm/page.h b/arch/xtensa/include/asm/page.h
index 976b1d7..4ddbfd5 100644
--- a/arch/xtensa/include/asm/page.h
+++ b/arch/xtensa/include/asm/page.h
@@ -164,8 +164,21 @@ void copy_user_highpage(struct page *to, struct page *from,
#define ARCH_PFN_OFFSET (PHYS_OFFSET >> PAGE_SHIFT)
+#ifdef CONFIG_MMU
+static inline unsigned long ___pa(unsigned long va)
+{
+ unsigned long off = va - PAGE_OFFSET;
+
+ if (off >= XCHAL_KSEG_SIZE)
+ off -= XCHAL_KSEG_SIZE;
+
+ return off + PHYS_OFFSET;
+}
+#define __pa(x) ___pa((unsigned long)(x))
+#else
#define __pa(x) \
((unsigned long) (x) - PAGE_OFFSET + PHYS_OFFSET)
+#endif
#define __va(x) \
((void *)((unsigned long) (x) - PHYS_OFFSET + PAGE_OFFSET))
#define pfn_valid(pfn) \
diff --git a/block/blk-mq.c b/block/blk-mq.c
index ee54ad0..7b597ec 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -1474,7 +1474,7 @@ static struct blk_mq_tags *blk_mq_init_rq_map(struct blk_mq_tag_set *set,
INIT_LIST_HEAD(&tags->page_list);
tags->rqs = kzalloc_node(set->queue_depth * sizeof(struct request *),
- GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY,
+ GFP_NOIO | __GFP_NOWARN | __GFP_NORETRY,
set->numa_node);
if (!tags->rqs) {
blk_mq_free_tags(tags);
@@ -1500,7 +1500,7 @@ static struct blk_mq_tags *blk_mq_init_rq_map(struct blk_mq_tag_set *set,
do {
page = alloc_pages_node(set->numa_node,
- GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY | __GFP_ZERO,
+ GFP_NOIO | __GFP_NOWARN | __GFP_NORETRY | __GFP_ZERO,
this_order);
if (page)
break;
@@ -1521,7 +1521,7 @@ static struct blk_mq_tags *blk_mq_init_rq_map(struct blk_mq_tag_set *set,
* Allow kmemleak to scan these pages as they contain pointers
* to additional allocations like via ops->init_request().
*/
- kmemleak_alloc(p, order_to_size(this_order), 1, GFP_KERNEL);
+ kmemleak_alloc(p, order_to_size(this_order), 1, GFP_NOIO);
entries_per_page = order_to_size(this_order) / rq_size;
to_do = min(entries_per_page, set->queue_depth - i);
left -= to_do * rq_size;
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index e19f530..6d5a8c1 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -113,7 +113,7 @@ struct acpi_button {
static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier);
static struct acpi_device *lid_device;
-static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
+static u8 lid_init_state = ACPI_BUTTON_LID_INIT_OPEN;
static unsigned long lid_report_interval __read_mostly = 500;
module_param(lid_report_interval, ulong, 0644);
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 1b41a27..0c45226 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -37,6 +37,7 @@ void acpi_amba_init(void);
static inline void acpi_amba_init(void) {}
#endif
int acpi_sysfs_init(void);
+void acpi_gpe_apply_masked_gpes(void);
void acpi_container_init(void);
void acpi_memory_hotplug_init(void);
#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 3d1856f..5a2fdf1 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -2044,6 +2044,7 @@ int __init acpi_scan_init(void)
}
}
+ acpi_gpe_apply_masked_gpes();
acpi_update_all_gpes();
acpi_ec_ecdt_start();
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 54abb26..a4327af 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -130,6 +130,12 @@ void __init acpi_nvs_nosave_s3(void)
nvs_nosave_s3 = true;
}
+static int __init init_nvs_save_s3(const struct dmi_system_id *d)
+{
+ nvs_nosave_s3 = false;
+ return 0;
+}
+
/*
* ACPI 1.0 wants us to execute _PTS before suspending devices, so we allow the
* user to request that behavior by using the 'acpi_old_suspend_ordering'
@@ -324,6 +330,19 @@ static struct dmi_system_id acpisleep_dmi_table[] __initdata = {
DMI_MATCH(DMI_PRODUCT_NAME, "K54HR"),
},
},
+ /*
+ * https://bugzilla.kernel.org/show_bug.cgi?id=189431
+ * Lenovo G50-45 is a platform later than 2012, but needs nvs memory
+ * saving during S3.
+ */
+ {
+ .callback = init_nvs_save_s3,
+ .ident = "Lenovo G50-45",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "80E3"),
+ },
+ },
{},
};
diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c
index 703c26e..cf05ae9 100644
--- a/drivers/acpi/sysfs.c
+++ b/drivers/acpi/sysfs.c
@@ -708,6 +708,62 @@ static ssize_t counter_set(struct kobject *kobj,
return result ? result : size;
}
+/*
+ * A Quirk Mechanism for GPE Flooding Prevention:
+ *
+ * Quirks may be needed to prevent GPE flooding on a specific GPE. The
+ * flooding typically cannot be detected and automatically prevented by
+ * ACPI_GPE_DISPATCH_NONE check because there is a _Lxx/_Exx prepared in
+ * the AML tables. This normally indicates a feature gap in Linux, thus
+ * instead of providing endless quirk tables, we provide a boot parameter
+ * for those who want this quirk. For example, if the users want to prevent
+ * the GPE flooding for GPE 00, they need to specify the following boot
+ * parameter:
+ * acpi_mask_gpe=0x00
+ * The masking status can be modified by the following runtime controlling
+ * interface:
+ * echo unmask > /sys/firmware/acpi/interrupts/gpe00
+ */
+
+/*
+ * Currently, the GPE flooding prevention only supports to mask the GPEs
+ * numbered from 00 to 7f.
+ */
+#define ACPI_MASKABLE_GPE_MAX 0x80
+
+static u64 __initdata acpi_masked_gpes;
+
+static int __init acpi_gpe_set_masked_gpes(char *val)
+{
+ u8 gpe;
+
+ if (kstrtou8(val, 0, &gpe) || gpe > ACPI_MASKABLE_GPE_MAX)
+ return -EINVAL;
+ acpi_masked_gpes |= ((u64)1<<gpe);
+
+ return 1;
+}
+__setup("acpi_mask_gpe=", acpi_gpe_set_masked_gpes);
+
+void __init acpi_gpe_apply_masked_gpes(void)
+{
+ acpi_handle handle;
+ acpi_status status;
+ u8 gpe;
+
+ for (gpe = 0;
+ gpe < min_t(u8, ACPI_MASKABLE_GPE_MAX, acpi_current_gpe_count);
+ gpe++) {
+ if (acpi_masked_gpes & ((u64)1<<gpe)) {
+ status = acpi_get_gpe_device(gpe, &handle);
+ if (ACPI_SUCCESS(status)) {
+ pr_info("Masking GPE 0x%x.\n", gpe);
+ (void)acpi_mask_gpe(handle, gpe, TRUE);
+ }
+ }
+ }
+}
+
void acpi_irq_stats_init(void)
{
acpi_status status;
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index d7eb419..6485c77 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -1765,7 +1765,9 @@ static int binder_translate_fd(int fd,
ret = -EBADF;
goto err_fget;
}
+ preempt_enable_no_resched();
ret = security_binder_transfer_file(proc->tsk, target_proc->tsk, file);
+ preempt_disable();
if (ret < 0) {
ret = -EPERM;
goto err_security;
diff --git a/drivers/ata/ahci_da850.c b/drivers/ata/ahci_da850.c
index 267a3d3..52f2674 100644
--- a/drivers/ata/ahci_da850.c
+++ b/drivers/ata/ahci_da850.c
@@ -54,11 +54,42 @@ static void da850_sata_init(struct device *dev, void __iomem *pwrdn_reg,
writel(val, ahci_base + SATA_P0PHYCR_REG);
}
+static int ahci_da850_softreset(struct ata_link *link,
+ unsigned int *class, unsigned long deadline)
+{
+ int pmp, ret;
+
+ pmp = sata_srst_pmp(link);
+
+ /*
+ * There's an issue with the SATA controller on da850 SoCs: if we
+ * enable Port Multiplier support, but the drive is connected directly
+ * to the board, it can't be detected. As a workaround: if PMP is
+ * enabled, we first call ahci_do_softreset() and pass it the result of
+ * sata_srst_pmp(). If this call fails, we retry with pmp = 0.
+ */
+ ret = ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready);
+ if (pmp && ret == -EBUSY)
+ return ahci_do_softreset(link, class, 0,
+ deadline, ahci_check_ready);
+
+ return ret;
+}
+
+static struct ata_port_operations ahci_da850_port_ops = {
+ .inherits = &ahci_platform_ops,
+ .softreset = ahci_da850_softreset,
+ /*
+ * No need to override .pmp_softreset - it's only used for actual
+ * PMP-enabled ports.
+ */
+};
+
static const struct ata_port_info ahci_da850_port_info = {
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
- .port_ops = &ahci_platform_ops,
+ .port_ops = &ahci_da850_port_ops,
};
static struct scsi_host_template ahci_platform_sht = {
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 4e58256..da97163 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -731,6 +731,53 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data,
return ret;
}
+static int regcache_sync_block_raw_multi_reg(struct regmap *map, void *block,
+ unsigned long *cache_present,
+ unsigned int block_base,
+ unsigned int start,
+ unsigned int end)
+{
+ unsigned int i, val;
+ unsigned int regtmp = 0;
+ int ret = 0;
+ struct reg_sequence *regs;
+ size_t num_regs = ((end - start) + 1);
+
+ regs = kcalloc(num_regs, sizeof(struct reg_sequence), GFP_KERNEL);
+ if (!regs)
+ return -ENOMEM;
+
+ num_regs = 0;
+ for (i = start; i < end; i++) {
+ regtmp = block_base + (i * map->reg_stride);
+
+ /* skip registers that are not defined/available */
+ if (!regcache_reg_present(cache_present, i))
+ continue;
+
+ val = regcache_get_val(map, block, i);
+
+ /* Is this the hardware default? If so skip. */
+ ret = regcache_lookup_reg(map, regtmp);
+ if (ret >= 0 && val == map->reg_defaults[ret].def) {
+ continue;
+ } else {
+ regs[num_regs].reg = regtmp;
+ regs[num_regs].def = val;
+ regs[num_regs].delay_us = 0;
+ num_regs += 1;
+ }
+ }
+ ret = 0;
+ if (num_regs) {
+ dev_dbg(map->dev, "%s: start: 0x%x - end: 0x%x\n",
+ __func__, regs[0].reg, regs[num_regs-1].reg);
+ ret = _regmap_raw_multi_reg_write(map, regs, num_regs);
+ }
+ kfree(regs);
+ return ret;
+}
+
static int regcache_sync_block_raw(struct regmap *map, void *block,
unsigned long *cache_present,
unsigned int block_base, unsigned int start,
@@ -778,7 +825,12 @@ int regcache_sync_block(struct regmap *map, void *block,
unsigned int block_base, unsigned int start,
unsigned int end)
{
- if (regmap_can_raw_write(map) && !map->use_single_write)
+ if (regmap_can_raw_write(map) && map->can_multi_write)
+ return regcache_sync_block_raw_multi_reg(map, block,
+ cache_present,
+ block_base, start,
+ end);
+ else if (regmap_can_raw_write(map) && !map->use_single_write)
return regcache_sync_block_raw(map, block, cache_present,
block_base, start, end);
else
diff --git a/drivers/base/regmap/regmap-swr.c b/drivers/base/regmap/regmap-swr.c
index 1a2e09e..be1eb00 100644
--- a/drivers/base/regmap/regmap-swr.c
+++ b/drivers/base/regmap/regmap-swr.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 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
@@ -11,6 +11,9 @@
* GNU General Public License for more details.
*/
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/soundwire/soundwire.h>
#include <linux/module.h>
@@ -20,16 +23,22 @@
static int regmap_swr_gather_write(void *context,
const void *reg, size_t reg_size,
- const void *val, size_t val_size)
+ const void *val, size_t val_len)
{
struct device *dev = context;
struct swr_device *swr = to_swr_device(dev);
struct regmap *map = dev_get_regmap(dev, NULL);
- size_t addr_bytes = map->format.reg_bytes;
- int ret = 0;
- int i;
- u32 reg_addr = 0;
+ size_t addr_bytes;
+ size_t val_bytes;
+ int i, ret = 0;
+ u16 reg_addr = 0;
+ u8 *value;
+ if (map == NULL) {
+ dev_err(dev, "%s: regmap is NULL\n", __func__);
+ return -EINVAL;
+ }
+ addr_bytes = map->format.reg_bytes;
if (swr == NULL) {
dev_err(dev, "%s: swr device is NULL\n", __func__);
return -EINVAL;
@@ -40,29 +49,107 @@ static int regmap_swr_gather_write(void *context,
return -EINVAL;
}
reg_addr = *(u16 *)reg;
- for (i = 0; i < val_size; i++) {
- ret = swr_write(swr, swr->dev_num, (reg_addr+i),
- (u32 *)(val+i));
+ val_bytes = map->format.val_bytes;
+ /* val_len = val_bytes * val_count */
+ for (i = 0; i < (val_len / val_bytes); i++) {
+ value = (u8 *)val + (val_bytes * i);
+ ret = swr_write(swr, swr->dev_num, (reg_addr + i), value);
if (ret < 0) {
dev_err(dev, "%s: write reg 0x%x failed, err %d\n",
- __func__, (reg_addr+i), ret);
+ __func__, (reg_addr + i), ret);
break;
}
}
return ret;
}
+static int regmap_swr_raw_multi_reg_write(void *context, const void *data,
+ size_t count)
+{
+ struct device *dev = context;
+ struct swr_device *swr = to_swr_device(dev);
+ struct regmap *map = dev_get_regmap(dev, NULL);
+ size_t addr_bytes;
+ size_t val_bytes;
+ size_t pad_bytes;
+ size_t num_regs;
+ int i = 0;
+ int ret = 0;
+ u16 *reg;
+ u8 *val;
+ u8 *buf;
+
+ if (swr == NULL) {
+ dev_err(dev, "%s: swr device is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (map == NULL) {
+ dev_err(dev, "%s: regmap is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ addr_bytes = map->format.reg_bytes;
+ val_bytes = map->format.val_bytes;
+ pad_bytes = map->format.pad_bytes;
+
+ if (addr_bytes + val_bytes + pad_bytes == 0) {
+ dev_err(dev, "%s: sum of addr, value and pad is 0\n", __func__);
+ return -EINVAL;
+ }
+ num_regs = count / (addr_bytes + val_bytes + pad_bytes);
+
+ reg = kcalloc(num_regs, sizeof(u16), GFP_KERNEL);
+ if (!reg)
+ return -ENOMEM;
+
+ val = kcalloc(num_regs, sizeof(u8), GFP_KERNEL);
+ if (!val) {
+ ret = -ENOMEM;
+ goto mem_fail;
+ }
+
+ buf = (u8 *)data;
+ for (i = 0; i < num_regs; i++) {
+ reg[i] = *(u16 *)buf;
+ buf += (map->format.reg_bytes + map->format.pad_bytes);
+ val[i] = *buf;
+ buf += map->format.val_bytes;
+ }
+ ret = swr_bulk_write(swr, swr->dev_num, reg, val, num_regs);
+ if (ret)
+ dev_err(dev, "%s: multi reg write failed\n", __func__);
+
+ kfree(val);
+mem_fail:
+ kfree(reg);
+ return ret;
+}
+
static int regmap_swr_write(void *context, const void *data, size_t count)
{
struct device *dev = context;
struct regmap *map = dev_get_regmap(dev, NULL);
- size_t addr_bytes = map->format.reg_bytes;
+ size_t addr_bytes;
+ size_t val_bytes;
+ size_t pad_bytes;
+
+ if (map == NULL) {
+ dev_err(dev, "%s: regmap is NULL\n", __func__);
+ return -EINVAL;
+ }
+ addr_bytes = map->format.reg_bytes;
+ val_bytes = map->format.val_bytes;
+ pad_bytes = map->format.pad_bytes;
WARN_ON(count < addr_bytes);
- return regmap_swr_gather_write(context, data, addr_bytes,
- (data + addr_bytes),
- (count - addr_bytes));
+ if (count > (addr_bytes + val_bytes + pad_bytes))
+ return regmap_swr_raw_multi_reg_write(context, data, count);
+ else
+ return regmap_swr_gather_write(context, data, addr_bytes,
+ (data + addr_bytes),
+ (count - addr_bytes));
}
static int regmap_swr_read(void *context,
@@ -72,10 +159,15 @@ static int regmap_swr_read(void *context,
struct device *dev = context;
struct swr_device *swr = to_swr_device(dev);
struct regmap *map = dev_get_regmap(dev, NULL);
- size_t addr_bytes = map->format.reg_bytes;
+ size_t addr_bytes;
int ret = 0;
- u32 reg_addr = 0;
+ u16 reg_addr = 0;
+ if (map == NULL) {
+ dev_err(dev, "%s: regmap is NULL\n", __func__);
+ return -EINVAL;
+ }
+ addr_bytes = map->format.reg_bytes;
if (swr == NULL) {
dev_err(dev, "%s: swr is NULL\n", __func__);
return -EINVAL;
@@ -85,7 +177,7 @@ static int regmap_swr_read(void *context,
__func__, reg_size);
return -EINVAL;
}
- reg_addr = *(u32 *)reg;
+ reg_addr = *(u16 *)reg;
ret = swr_read(swr, swr->dev_num, reg_addr, val, val_size);
if (ret < 0)
dev_err(dev, "%s: codec reg 0x%x read failed %d\n",
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index bc48a84..3e1367a 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -573,9 +573,12 @@
controlling the behavior of this hardware.
config DEVPORT
- bool
+ bool "/dev/port character device"
depends on ISA || PCI
default y
+ help
+ Say Y here if you want to support the /dev/port device. The /dev/port
+ device is similar to /dev/mem, but for I/O ports.
source "drivers/s390/char/Kconfig"
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index 4d0ab08..e13871e 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -662,7 +662,7 @@ int diag_add_diag_id_to_list(uint8_t diag_id, char *process_name)
if (!new_item)
return -ENOMEM;
kmemleak_not_leak(new_item);
- new_item->process_name = kzalloc(strlen(process_name), GFP_KERNEL);
+ new_item->process_name = kzalloc(strlen(process_name) + 1, GFP_KERNEL);
if (!new_item->process_name) {
kfree(new_item);
new_item = NULL;
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index 6af1ce0..336d02a 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -84,8 +84,14 @@ struct pp_struct {
struct ieee1284_info state;
struct ieee1284_info saved_state;
long default_inactivity;
+ int index;
};
+/* should we use PARDEVICE_MAX here? */
+static struct device *devices[PARPORT_MAX];
+
+static DEFINE_IDA(ida_index);
+
/* pp_struct.flags bitfields */
#define PP_CLAIMED (1<<0)
#define PP_EXCL (1<<1)
@@ -287,6 +293,7 @@ static int register_device(int minor, struct pp_struct *pp)
struct pardevice *pdev = NULL;
char *name;
struct pardev_cb ppdev_cb;
+ int index;
name = kasprintf(GFP_KERNEL, CHRDEV "%x", minor);
if (name == NULL)
@@ -299,20 +306,23 @@ static int register_device(int minor, struct pp_struct *pp)
return -ENXIO;
}
+ index = ida_simple_get(&ida_index, 0, 0, GFP_KERNEL);
memset(&ppdev_cb, 0, sizeof(ppdev_cb));
ppdev_cb.irq_func = pp_irq;
ppdev_cb.flags = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0;
ppdev_cb.private = pp;
- pdev = parport_register_dev_model(port, name, &ppdev_cb, minor);
+ pdev = parport_register_dev_model(port, name, &ppdev_cb, index);
parport_put_port(port);
if (!pdev) {
printk(KERN_WARNING "%s: failed to register device!\n", name);
+ ida_simple_remove(&ida_index, index);
kfree(name);
return -ENXIO;
}
pp->pdev = pdev;
+ pp->index = index;
dev_dbg(&pdev->dev, "registered pardevice\n");
return 0;
}
@@ -749,6 +759,7 @@ static int pp_release(struct inode *inode, struct file *file)
if (pp->pdev) {
parport_unregister_device(pp->pdev);
+ ida_simple_remove(&ida_index, pp->index);
pp->pdev = NULL;
pr_debug(CHRDEV "%x: unregistered pardevice\n", minor);
}
@@ -789,13 +800,29 @@ static const struct file_operations pp_fops = {
static void pp_attach(struct parport *port)
{
- device_create(ppdev_class, port->dev, MKDEV(PP_MAJOR, port->number),
- NULL, "parport%d", port->number);
+ struct device *ret;
+
+ if (devices[port->number])
+ return;
+
+ ret = device_create(ppdev_class, port->dev,
+ MKDEV(PP_MAJOR, port->number), NULL,
+ "parport%d", port->number);
+ if (IS_ERR(ret)) {
+ pr_err("Failed to create device parport%d\n",
+ port->number);
+ return;
+ }
+ devices[port->number] = ret;
}
static void pp_detach(struct parport *port)
{
+ if (!devices[port->number])
+ return;
+
device_destroy(ppdev_class, MKDEV(PP_MAJOR, port->number));
+ devices[port->number] = NULL;
}
static int pp_probe(struct pardevice *par_dev)
diff --git a/drivers/char/random.c b/drivers/char/random.c
index d6876d5..08d1dd5 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -2042,64 +2042,66 @@ struct ctl_table random_table[] = {
};
#endif /* CONFIG_SYSCTL */
-static u32 random_int_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned;
-
-int random_int_secret_init(void)
-{
- get_random_bytes(random_int_secret, sizeof(random_int_secret));
- return 0;
-}
-
-static DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash)
- __aligned(sizeof(unsigned long));
+struct batched_entropy {
+ union {
+ unsigned long entropy_long[CHACHA20_BLOCK_SIZE / sizeof(unsigned long)];
+ unsigned int entropy_int[CHACHA20_BLOCK_SIZE / sizeof(unsigned int)];
+ };
+ unsigned int position;
+};
/*
- * Get a random word for internal kernel use only. Similar to urandom but
- * with the goal of minimal entropy pool depletion. As a result, the random
- * value is not cryptographically secure but for several uses the cost of
- * depleting entropy is too high
+ * Get a random word for internal kernel use only. The quality of the random
+ * number is either as good as RDRAND or as good as /dev/urandom, with the
+ * goal of being quite fast and not depleting entropy.
*/
-unsigned int get_random_int(void)
-{
- __u32 *hash;
- unsigned int ret;
-
- if (arch_get_random_int(&ret))
- return ret;
-
- hash = get_cpu_var(get_random_int_hash);
-
- hash[0] += current->pid + jiffies + random_get_entropy();
- md5_transform(hash, random_int_secret);
- ret = hash[0];
- put_cpu_var(get_random_int_hash);
-
- return ret;
-}
-EXPORT_SYMBOL(get_random_int);
-
-/*
- * Same as get_random_int(), but returns unsigned long.
- */
+static DEFINE_PER_CPU(struct batched_entropy, batched_entropy_long);
unsigned long get_random_long(void)
{
- __u32 *hash;
unsigned long ret;
+ struct batched_entropy *batch;
if (arch_get_random_long(&ret))
return ret;
- hash = get_cpu_var(get_random_int_hash);
-
- hash[0] += current->pid + jiffies + random_get_entropy();
- md5_transform(hash, random_int_secret);
- ret = *(unsigned long *)hash;
- put_cpu_var(get_random_int_hash);
-
+ batch = &get_cpu_var(batched_entropy_long);
+ if (batch->position % ARRAY_SIZE(batch->entropy_long) == 0) {
+ extract_crng((u8 *)batch->entropy_long);
+ batch->position = 0;
+ }
+ ret = batch->entropy_long[batch->position++];
+ put_cpu_var(batched_entropy_long);
return ret;
}
EXPORT_SYMBOL(get_random_long);
+#if BITS_PER_LONG == 32
+unsigned int get_random_int(void)
+{
+ return get_random_long();
+}
+#else
+static DEFINE_PER_CPU(struct batched_entropy, batched_entropy_int);
+unsigned int get_random_int(void)
+{
+ unsigned int ret;
+ struct batched_entropy *batch;
+
+ if (arch_get_random_int(&ret))
+ return ret;
+
+ batch = &get_cpu_var(batched_entropy_int);
+ if (batch->position % ARRAY_SIZE(batch->entropy_int) == 0) {
+ extract_crng((u8 *)batch->entropy_int);
+ batch->position = 0;
+ }
+ ret = batch->entropy_int[batch->position++];
+ put_cpu_var(batched_entropy_int);
+ return ret;
+}
+#endif
+EXPORT_SYMBOL(get_random_int);
+
/**
* randomize_page - Generate a random, page aligned address
* @start: The smallest acceptable address the caller will take.
diff --git a/drivers/clk/nxp/clk-lpc32xx.c b/drivers/clk/nxp/clk-lpc32xx.c
index 34c9735..5b98ff9 100644
--- a/drivers/clk/nxp/clk-lpc32xx.c
+++ b/drivers/clk/nxp/clk-lpc32xx.c
@@ -1282,13 +1282,13 @@ static struct clk_hw_proto clk_hw_proto[LPC32XX_CLK_HW_MAX] = {
LPC32XX_DEFINE_MUX(PWM1_MUX, PWMCLK_CTRL, 1, 0x1, NULL, 0),
LPC32XX_DEFINE_DIV(PWM1_DIV, PWMCLK_CTRL, 4, 4, NULL,
- CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO),
+ CLK_DIVIDER_ONE_BASED),
LPC32XX_DEFINE_GATE(PWM1_GATE, PWMCLK_CTRL, 0, 0),
LPC32XX_DEFINE_COMPOSITE(PWM1, PWM1_MUX, PWM1_DIV, PWM1_GATE),
LPC32XX_DEFINE_MUX(PWM2_MUX, PWMCLK_CTRL, 3, 0x1, NULL, 0),
LPC32XX_DEFINE_DIV(PWM2_DIV, PWMCLK_CTRL, 8, 4, NULL,
- CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO),
+ CLK_DIVIDER_ONE_BASED),
LPC32XX_DEFINE_GATE(PWM2_GATE, PWMCLK_CTRL, 2, 0),
LPC32XX_DEFINE_COMPOSITE(PWM2, PWM2_MUX, PWM2_DIV, PWM2_GATE),
@@ -1335,8 +1335,7 @@ static struct clk_hw_proto clk_hw_proto[LPC32XX_CLK_HW_MAX] = {
LPC32XX_DEFINE_GATE(USB_DIV_GATE, USB_CTRL, 17, 0),
LPC32XX_DEFINE_COMPOSITE(USB_DIV, _NULL, USB_DIV_DIV, USB_DIV_GATE),
- LPC32XX_DEFINE_DIV(SD_DIV, MS_CTRL, 0, 4, NULL,
- CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO),
+ LPC32XX_DEFINE_DIV(SD_DIV, MS_CTRL, 0, 4, NULL, CLK_DIVIDER_ONE_BASED),
LPC32XX_DEFINE_CLK(SD_GATE, MS_CTRL, BIT(5) | BIT(9), BIT(5) | BIT(9),
0x0, BIT(5) | BIT(9), 0x0, 0x0, clk_mask_ops),
LPC32XX_DEFINE_COMPOSITE(SD, _NULL, SD_DIV, SD_GATE),
@@ -1478,6 +1477,20 @@ static struct clk * __init lpc32xx_clk_register(u32 id)
return clk;
}
+static void __init lpc32xx_clk_div_quirk(u32 reg, u32 div_mask, u32 gate)
+{
+ u32 val;
+
+ regmap_read(clk_regmap, reg, &val);
+
+ if (!(val & div_mask)) {
+ val &= ~gate;
+ val |= BIT(__ffs(div_mask));
+ }
+
+ regmap_update_bits(clk_regmap, reg, gate | div_mask, val);
+}
+
static void __init lpc32xx_clk_init(struct device_node *np)
{
unsigned int i;
@@ -1517,6 +1530,17 @@ static void __init lpc32xx_clk_init(struct device_node *np)
return;
}
+ /*
+ * Divider part of PWM and MS clocks requires a quirk to avoid
+ * a misinterpretation of formally valid zero value in register
+ * bitfield, which indicates another clock gate. Instead of
+ * adding complexity to a gate clock ensure that zero value in
+ * divider clock is never met in runtime.
+ */
+ lpc32xx_clk_div_quirk(LPC32XX_CLKPWR_PWMCLK_CTRL, 0xf0, BIT(0));
+ lpc32xx_clk_div_quirk(LPC32XX_CLKPWR_PWMCLK_CTRL, 0xf00, BIT(2));
+ lpc32xx_clk_div_quirk(LPC32XX_CLKPWR_MS_CTRL, 0xf, BIT(5) | BIT(9));
+
for (i = 1; i < LPC32XX_CLK_MAX; i++) {
clk[i] = lpc32xx_clk_register(i);
if (IS_ERR(clk[i])) {
diff --git a/drivers/clk/qcom/camcc-sdm845.c b/drivers/clk/qcom/camcc-sdm845.c
index a274975..84e9698 100644
--- a/drivers/clk/qcom/camcc-sdm845.c
+++ b/drivers/clk/qcom/camcc-sdm845.c
@@ -764,6 +764,7 @@ static struct clk_rcg2 cam_cc_lrme_clk_src = {
};
static const struct freq_tbl ftbl_cam_cc_mclk0_clk_src[] = {
+ F(24000000, P_CAM_CC_PLL3_OUT_EVEN, 16, 0, 0),
F(33333333, P_CAM_CC_PLL0_OUT_EVEN, 2, 1, 9),
F(34285714, P_CAM_CC_PLL2_OUT_EVEN, 14, 0, 0),
{ }
diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c
index a358dd0..035d337 100644
--- a/drivers/clk/qcom/clk-cpu-osm.c
+++ b/drivers/clk/qcom/clk-cpu-osm.c
@@ -509,34 +509,22 @@ const struct clk_ops clk_ops_l3_osm = {
.set_rate = l3_clk_set_rate,
};
-enum {
- P_XO,
-};
-
-static const struct parent_map gcc_parent_map_1[] = {
- { P_XO, 0 },
-};
-
-static const char * const gcc_parent_names_1[] = {
- "xo",
-};
-
static struct clk_init_data osm_clks_init[] = {
[0] = {
.name = "l3_clk",
- .parent_names = (const char *[]){ "bi_tcxo" },
+ .parent_names = (const char *[]){ "bi_tcxo_ao" },
.num_parents = 1,
.ops = &clk_ops_l3_osm,
},
[1] = {
.name = "pwrcl_clk",
- .parent_names = (const char *[]){ "bi_tcxo" },
+ .parent_names = (const char *[]){ "bi_tcxo_ao" },
.num_parents = 1,
.ops = &clk_ops_cpu_osm,
},
[2] = {
.name = "perfcl_clk",
- .parent_names = (const char *[]){ "bi_tcxo" },
+ .parent_names = (const char *[]){ "bi_tcxo_ao" },
.num_parents = 1,
.ops = &clk_ops_cpu_osm,
},
@@ -2266,8 +2254,6 @@ static int clk_cpu_osm_driver_probe(struct platform_device *pdev)
struct clk_osm *c;
struct device *dev = &pdev->dev;
struct clk_onecell_data *clk_data;
- struct resource *res;
- void *vbase;
char l3speedbinstr[] = "qcom,l3-speedbin0-v0";
char perfclspeedbinstr[] = "qcom,perfcl-speedbin0-v0";
char pwrclspeedbinstr[] = "qcom,pwrcl-speedbin0-v0";
@@ -2475,30 +2461,6 @@ static int clk_cpu_osm_driver_probe(struct platform_device *pdev)
(0x39 | (perfcl_clk.apm_threshold_vc << 6)));
}
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "apps_itm_ctl");
- if (!res) {
- dev_err(&pdev->dev,
- "Unable to get platform resource for apps_itm_ctl\n");
- return -ENOMEM;
- }
-
- vbase = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!vbase) {
- dev_err(&pdev->dev,
- "Unable to map in apps_itm_ctl base\n");
- return -ENOMEM;
- }
-
- val = readl_relaxed(vbase + 0x0);
- val &= ~BIT(0);
- writel_relaxed(val, vbase + 0x0);
-
- val = readl_relaxed(vbase + 0x4);
- val &= ~BIT(0);
- writel_relaxed(val, vbase + 0x4);
-
/*
* Perform typical secure-world HW initialization
* as necessary.
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 8484b57..2f9cfdf 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -418,7 +418,7 @@ static long clk_rcg2_list_rate(struct clk_hw *hw, unsigned int n,
static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
- const struct freq_tbl *f;
+ const struct freq_tbl *f, *f_curr;
int ret, curr_src_index, new_src_index;
struct clk_hw *curr_src = NULL, *new_src = NULL;
@@ -436,15 +436,17 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
}
if (rcg->flags & FORCE_ENABLE_RCG) {
- if (!rcg->current_freq)
- rcg->current_freq = cxo_f.freq;
-
+ rcg->current_freq = clk_get_rate(hw->clk);
if (rcg->current_freq == cxo_f.freq)
curr_src_index = 0;
else {
- f = qcom_find_freq(rcg->freq_tbl, rcg->current_freq);
+ f_curr = qcom_find_freq(rcg->freq_tbl,
+ rcg->current_freq);
+ if (!f_curr)
+ return -EINVAL;
+
curr_src_index = qcom_find_src_index(hw,
- rcg->parent_map, f->src);
+ rcg->parent_map, f_curr->src);
}
new_src_index = qcom_find_src_index(hw, rcg->parent_map,
diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c
index bcee3ee..5e11485 100644
--- a/drivers/clk/qcom/clk-rpmh.c
+++ b/drivers/clk/qcom/clk-rpmh.c
@@ -277,9 +277,6 @@ DEFINE_RSC_TYPE(disp_rsc, "display", true);
DEFINE_CLK_RPMH_ARC(sdm845, bi_tcxo, bi_tcxo_ao, "xo.lvl", 0x3, 0x0,
&apps_rsc, 19200000, CLK_RPMH_APPS_RSC_STATE_MASK,
CLK_RPMH_APPS_RSC_AO_STATE_MASK);
-DEFINE_CLK_RPMH_VRM(sdm845, ln_bb_clk1, ln_bb_clk1_ao, "lnbclka1", &apps_rsc,
- 19200000, CLK_RPMH_APPS_RSC_STATE_MASK,
- CLK_RPMH_APPS_RSC_AO_STATE_MASK);
DEFINE_CLK_RPMH_VRM(sdm845, ln_bb_clk2, ln_bb_clk2_ao, "lnbclka2", &apps_rsc,
19200000, CLK_RPMH_APPS_RSC_STATE_MASK,
CLK_RPMH_APPS_RSC_AO_STATE_MASK);
@@ -299,8 +296,6 @@ DEFINE_CLK_RPMH_VRM(sdm845, rf_clk3, rf_clk3_ao, "rfclka3", &apps_rsc,
static struct clk_hw *sdm845_rpmh_clocks[] = {
[RPMH_CXO_CLK] = &sdm845_bi_tcxo.hw,
[RPMH_CXO_CLK_A] = &sdm845_bi_tcxo_ao.hw,
- [RPMH_LN_BB_CLK1] = &sdm845_ln_bb_clk1.hw,
- [RPMH_LN_BB_CLK1_A] = &sdm845_ln_bb_clk1_ao.hw,
[RPMH_LN_BB_CLK2] = &sdm845_ln_bb_clk2.hw,
[RPMH_LN_BB_CLK2_A] = &sdm845_ln_bb_clk2_ao.hw,
[RPMH_LN_BB_CLK3] = &sdm845_ln_bb_clk3.hw,
diff --git a/drivers/clk/qcom/dispcc-sdm845.c b/drivers/clk/qcom/dispcc-sdm845.c
index cb073a8..20ce78b 100644
--- a/drivers/clk/qcom/dispcc-sdm845.c
+++ b/drivers/clk/qcom/dispcc-sdm845.c
@@ -546,7 +546,7 @@ static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = {
"disp_cc_mdss_byte0_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
+ .flags = CLK_GET_RATE_NOCACHE,
.ops = &clk_regmap_div_ops,
},
},
@@ -599,7 +599,7 @@ static struct clk_regmap_div disp_cc_mdss_byte1_div_clk_src = {
"disp_cc_mdss_byte1_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
+ .flags = CLK_GET_RATE_NOCACHE,
.ops = &clk_regmap_div_ops,
},
},
diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c
index 2e8ef93..5fcb1f5 100644
--- a/drivers/clk/qcom/gcc-sdm845.c
+++ b/drivers/clk/qcom/gcc-sdm845.c
@@ -151,6 +151,13 @@ static const char * const gcc_parent_names_6[] = {
"core_bi_pll_test_se",
};
+static const char * const gcc_parent_names_7[] = {
+ "bi_tcxo_ao",
+ "gpll0",
+ "gpll0_out_even",
+ "core_bi_pll_test_se",
+};
+
static struct clk_dummy measure_only_snoc_clk = {
.rrate = 1000,
.hw.init = &(struct clk_init_data){
@@ -256,8 +263,6 @@ static struct clk_alpha_pll gpll1 = {
static const struct freq_tbl ftbl_gcc_cpuss_ahb_clk_src[] = {
F(19200000, P_BI_TCXO, 1, 0, 0),
- F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0),
- F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
{ }
};
@@ -269,7 +274,7 @@ static struct clk_rcg2 gcc_cpuss_ahb_clk_src = {
.freq_tbl = ftbl_gcc_cpuss_ahb_clk_src,
.clkr.hw.init = &(struct clk_init_data){
.name = "gcc_cpuss_ahb_clk_src",
- .parent_names = gcc_parent_names_0,
+ .parent_names = gcc_parent_names_7,
.num_parents = 4,
.flags = CLK_SET_RATE_PARENT,
.ops = &clk_rcg2_ops,
@@ -796,7 +801,10 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s7_clk_src = {
};
static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = {
+ F(400000, P_BI_TCXO, 12, 1, 4),
F(19200000, P_BI_TCXO, 1, 0, 0),
+ F(25000000, P_GPLL0_OUT_MAIN, 12, 1, 2),
+ F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0),
F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0),
{ }
@@ -824,8 +832,9 @@ static struct clk_rcg2 gcc_sdcc2_apps_clk_src = {
};
static const struct freq_tbl ftbl_gcc_sdcc4_apps_clk_src[] = {
- F(9600000, P_BI_TCXO, 2, 0, 0),
+ F(400000, P_BI_TCXO, 12, 1, 4),
F(19200000, P_BI_TCXO, 1, 0, 0),
+ F(25000000, P_GPLL0_OUT_MAIN, 12, 1, 2),
F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0),
F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
{ }
@@ -1474,7 +1483,7 @@ static struct clk_branch gcc_cxo_tx1_clkref_clk = {
static struct clk_branch gcc_ddrss_gpu_axi_clk = {
.halt_reg = 0x44038,
- .halt_check = BRANCH_HALT,
+ .halt_check = BRANCH_VOTED,
.clkr = {
.enable_reg = 0x44038,
.enable_mask = BIT(0),
@@ -1661,7 +1670,7 @@ static struct clk_gate2 gcc_gpu_gpll0_div_clk_src = {
static struct clk_branch gcc_gpu_memnoc_gfx_clk = {
.halt_reg = 0x7100c,
- .halt_check = BRANCH_HALT,
+ .halt_check = BRANCH_VOTED,
.clkr = {
.enable_reg = 0x7100c,
.enable_mask = BIT(0),
@@ -2450,19 +2459,6 @@ static struct clk_branch gcc_rx2_qlink_clkref_clk = {
},
};
-static struct clk_branch gcc_rx3_modem_clkref_clk = {
- .halt_reg = 0x8c01c,
- .halt_check = BRANCH_HALT,
- .clkr = {
- .enable_reg = 0x8c01c,
- .enable_mask = BIT(0),
- .hw.init = &(struct clk_init_data){
- .name = "gcc_rx3_modem_clkref_clk",
- .ops = &clk_branch2_ops,
- },
- },
-};
-
static struct clk_branch gcc_sdcc2_ahb_clk = {
.halt_reg = 0x14008,
.halt_check = BRANCH_HALT,
@@ -3244,7 +3240,6 @@ static struct clk_regmap *gcc_sdm845_clocks[] = {
[GCC_QUPV3_WRAP_1_S_AHB_CLK] = &gcc_qupv3_wrap_1_s_ahb_clk.clkr,
[GCC_RX1_USB2_CLKREF_CLK] = &gcc_rx1_usb2_clkref_clk.clkr,
[GCC_RX2_QLINK_CLKREF_CLK] = &gcc_rx2_qlink_clkref_clk.clkr,
- [GCC_RX3_MODEM_CLKREF_CLK] = &gcc_rx3_modem_clkref_clk.clkr,
[GCC_SDCC2_AHB_CLK] = &gcc_sdcc2_ahb_clk.clkr,
[GCC_SDCC2_APPS_CLK] = &gcc_sdcc2_apps_clk.clkr,
[GCC_SDCC2_APPS_CLK_SRC] = &gcc_sdcc2_apps_clk_src.clkr,
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 82e62c5..550a59c 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -81,6 +81,7 @@ static struct clock_event_device __percpu *arch_timer_evt;
static enum ppi_nr arch_timer_uses_ppi = VIRT_PPI;
static bool arch_timer_c3stop;
static bool arch_timer_mem_use_virtual;
+static bool arch_counter_suspend_stop;
static bool evtstrm_enable = IS_ENABLED(CONFIG_ARM_ARCH_TIMER_EVTSTREAM);
@@ -577,7 +578,7 @@ static struct clocksource clocksource_counter = {
.rating = 400,
.read = arch_counter_read,
.mask = CLOCKSOURCE_MASK(56),
- .flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_SUSPEND_NONSTOP,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static struct cyclecounter cyclecounter = {
@@ -617,6 +618,8 @@ static void __init arch_counter_register(unsigned type)
arch_timer_read_counter = arch_counter_get_cntvct_mem;
}
+ if (!arch_counter_suspend_stop)
+ clocksource_counter.flags |= CLOCK_SOURCE_SUSPEND_NONSTOP;
start_count = arch_timer_read_counter();
clocksource_register_hz(&clocksource_counter, arch_timer_rate);
cyclecounter.mult = clocksource_counter.mult;
@@ -909,6 +912,10 @@ static int __init arch_timer_of_init(struct device_node *np)
of_property_read_bool(np, "arm,cpu-registers-not-fw-configured"))
arch_timer_uses_ppi = PHYS_SECURE_PPI;
+ /* On some systems, the counter stops ticking when in suspend. */
+ arch_counter_suspend_stop = of_property_read_bool(np,
+ "arm,no-tick-in-suspend");
+
return arch_timer_init();
}
CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init);
diff --git a/drivers/crypto/caam/caampkc.c b/drivers/crypto/caam/caampkc.c
index 851015e..354a16a 100644
--- a/drivers/crypto/caam/caampkc.c
+++ b/drivers/crypto/caam/caampkc.c
@@ -506,7 +506,7 @@ static int caam_rsa_init_tfm(struct crypto_akcipher *tfm)
ctx->dev = caam_jr_alloc();
if (IS_ERR(ctx->dev)) {
- dev_err(ctx->dev, "Job Ring Device allocation for transform failed\n");
+ pr_err("Job Ring Device allocation for transform failed\n");
return PTR_ERR(ctx->dev);
}
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c
index e483b78..98468b9 100644
--- a/drivers/crypto/caam/ctrl.c
+++ b/drivers/crypto/caam/ctrl.c
@@ -282,7 +282,8 @@ static int deinstantiate_rng(struct device *ctrldev, int state_handle_mask)
/* Try to run it through DECO0 */
ret = run_descriptor_deco0(ctrldev, desc, &status);
- if (ret || status) {
+ if (ret ||
+ (status && status != JRSTA_SSRC_JUMP_HALT_CC)) {
dev_err(ctrldev,
"Failed to deinstantiate RNG4 SH%d\n",
sh_idx);
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index 6476c5e..b8effac 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -156,6 +156,15 @@
governor is unlikely to be useful for other
devices.
+config DEVFREQ_GOV_QCOM_GPUBW_MON
+ tristate "GPU BW voting governor"
+ depends on DEVFREQ_GOV_QCOM_ADRENO_TZ
+ help
+ This governor works together with Adreno Trustzone governor,
+ and select bus frequency votes using an "on-demand" alorithm.
+ This governor is unlikely to be useful for non-Adreno
+ devices.
+
config ARM_EXYNOS_BUS_DEVFREQ
tristate "ARM EXYNOS Generic Memory Bus DEVFREQ Driver"
depends on ARCH_EXYNOS || COMPILE_TEST
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
index f488f12..f248e02 100644
--- a/drivers/devfreq/Makefile
+++ b/drivers/devfreq/Makefile
@@ -8,6 +8,7 @@
obj-$(CONFIG_DEVFREQ_GOV_CPUFREQ) += governor_cpufreq.o
obj-$(CONFIG_DEVFREQ_GOV_QCOM_ADRENO_TZ) += governor_msm_adreno_tz.o
obj-$(CONFIG_DEVFREQ_GOV_QCOM_GPUBW_MON) += governor_bw_vbif.o
+obj-$(CONFIG_DEVFREQ_GOV_QCOM_GPUBW_MON) += governor_gpubw_mon.o
obj-$(CONFIG_QCOM_BIMC_BWMON) += bimc-bwmon.o
obj-$(CONFIG_ARM_MEMLAT_MON) += arm-memlat-mon.o
obj-$(CONFIG_QCOMCCI_HWMON) += msmcci-hwmon.o
diff --git a/drivers/devfreq/devfreq_trace.h b/drivers/devfreq/devfreq_trace.h
new file mode 100644
index 0000000..7dacc0e
--- /dev/null
+++ b/drivers/devfreq/devfreq_trace.h
@@ -0,0 +1,44 @@
+/* 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#if !defined(_DEVFREQ_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _DEVFREQ_TRACE_H
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM devfreq
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE devfreq_trace
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(devfreq_msg,
+ TP_PROTO(const char *msg),
+ TP_ARGS(msg),
+ TP_STRUCT__entry(
+ __string(msg, msg)
+ ),
+ TP_fast_assign(
+ __assign_str(msg, msg);
+ ),
+ TP_printk(
+ "%s", __get_str(msg)
+ )
+);
+
+#endif /* _DEVFREQ_TRACE_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
+
diff --git a/drivers/devfreq/governor_gpubw_mon.c b/drivers/devfreq/governor_gpubw_mon.c
new file mode 100644
index 0000000..9c24eef
--- /dev/null
+++ b/drivers/devfreq/governor_gpubw_mon.c
@@ -0,0 +1,266 @@
+/* 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
+ * 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/devfreq.h>
+#include <linux/module.h>
+#include <linux/msm_adreno_devfreq.h>
+#include <linux/slab.h>
+
+#include "devfreq_trace.h"
+#include "governor.h"
+
+#define MIN_BUSY 1000
+#define LONG_FLOOR 50000
+#define HIST 5
+#define TARGET 80
+#define CAP 75
+/* AB vote is in multiple of BW_STEP Mega bytes */
+#define BW_STEP 160
+
+static void _update_cutoff(struct devfreq_msm_adreno_tz_data *priv,
+ unsigned int norm_max)
+{
+ int i;
+
+ priv->bus.max = norm_max;
+ for (i = 0; i < priv->bus.num; i++) {
+ priv->bus.up[i] = priv->bus.p_up[i] * norm_max / 100;
+ priv->bus.down[i] = priv->bus.p_down[i] * norm_max / 100;
+ }
+}
+
+static inline int devfreq_get_freq_level(struct devfreq *devfreq,
+ unsigned long freq)
+{
+ int lev;
+
+ for (lev = 0; lev < devfreq->profile->max_state; lev++)
+ if (freq == devfreq->profile->freq_table[lev])
+ return lev;
+
+ return -EINVAL;
+}
+
+static int devfreq_gpubw_get_target(struct devfreq *df,
+ unsigned long *freq)
+{
+
+ struct devfreq_msm_adreno_tz_data *priv = df->data;
+ struct msm_busmon_extended_profile *bus_profile = container_of(
+ (df->profile),
+ struct msm_busmon_extended_profile,
+ profile);
+ struct devfreq_dev_status stats;
+ struct xstats b;
+ int result;
+ int level = 0;
+ int act_level;
+ int norm_cycles;
+ int gpu_percent;
+ /*
+ * Normalized AB should at max usage be the gpu_bimc frequency in MHz.
+ * Start with a reasonable value and let the system push it up to max.
+ */
+ static int norm_ab_max = 300;
+ int norm_ab;
+ unsigned long ab_mbytes = 0;
+
+ if (priv == NULL)
+ return 0;
+
+ stats.private_data = &b;
+
+ result = df->profile->get_dev_status(df->dev.parent, &stats);
+
+ *freq = stats.current_frequency;
+
+ priv->bus.total_time += stats.total_time;
+ priv->bus.gpu_time += stats.busy_time;
+ priv->bus.ram_time += b.ram_time;
+ priv->bus.ram_wait += b.ram_wait;
+
+ level = devfreq_get_freq_level(df, stats.current_frequency);
+
+ if (priv->bus.total_time < LONG_FLOOR)
+ return result;
+
+ norm_cycles = (unsigned int)(priv->bus.ram_time + priv->bus.ram_wait) /
+ (unsigned int) priv->bus.total_time;
+ gpu_percent = (100 * (unsigned int)priv->bus.gpu_time) /
+ (unsigned int) priv->bus.total_time;
+
+ /*
+ * If there's a new high watermark, update the cutoffs and send the
+ * FAST hint. Otherwise check the current value against the current
+ * cutoffs.
+ */
+ if (norm_cycles > priv->bus.max) {
+ _update_cutoff(priv, norm_cycles);
+ bus_profile->flag = DEVFREQ_FLAG_FAST_HINT;
+ } else {
+ /* GPU votes for IB not AB so don't under vote the system */
+ norm_cycles = (100 * norm_cycles) / TARGET;
+ act_level = priv->bus.index[level] + b.mod;
+ act_level = (act_level < 0) ? 0 : act_level;
+ act_level = (act_level >= priv->bus.num) ?
+ (priv->bus.num - 1) : act_level;
+ if (norm_cycles > priv->bus.up[act_level] &&
+ gpu_percent > CAP)
+ bus_profile->flag = DEVFREQ_FLAG_FAST_HINT;
+ else if (norm_cycles < priv->bus.down[act_level] && level)
+ bus_profile->flag = DEVFREQ_FLAG_SLOW_HINT;
+ }
+
+ /* Calculate the AB vote based on bus width if defined */
+ if (priv->bus.width) {
+ norm_ab = (unsigned int)priv->bus.ram_time /
+ (unsigned int) priv->bus.total_time;
+ /* Calculate AB in Mega Bytes and roundup in BW_STEP */
+ ab_mbytes = (norm_ab * priv->bus.width * 1000000ULL) >> 20;
+ bus_profile->ab_mbytes = roundup(ab_mbytes, BW_STEP);
+ } else if (bus_profile->flag) {
+ /* Re-calculate the AB percentage for a new IB vote */
+ norm_ab = (unsigned int)priv->bus.ram_time /
+ (unsigned int) priv->bus.total_time;
+ if (norm_ab > norm_ab_max)
+ norm_ab_max = norm_ab;
+ bus_profile->percent_ab = (100 * norm_ab) / norm_ab_max;
+ }
+
+ priv->bus.total_time = 0;
+ priv->bus.gpu_time = 0;
+ priv->bus.ram_time = 0;
+ priv->bus.ram_wait = 0;
+
+ return result;
+}
+
+static int gpubw_start(struct devfreq *devfreq)
+{
+ struct devfreq_msm_adreno_tz_data *priv;
+
+ struct msm_busmon_extended_profile *bus_profile = container_of(
+ (devfreq->profile),
+ struct msm_busmon_extended_profile,
+ profile);
+ unsigned int t1, t2 = 2 * HIST;
+ int i, bus_size;
+
+
+ devfreq->data = bus_profile->private_data;
+ priv = devfreq->data;
+
+ bus_size = sizeof(u32) * priv->bus.num;
+ priv->bus.up = kzalloc(bus_size, GFP_KERNEL);
+ priv->bus.down = kzalloc(bus_size, GFP_KERNEL);
+ priv->bus.p_up = kzalloc(bus_size, GFP_KERNEL);
+ priv->bus.p_down = kzalloc(bus_size, GFP_KERNEL);
+ if (priv->bus.up == NULL || priv->bus.down == NULL ||
+ priv->bus.p_up == NULL || priv->bus.p_down == NULL)
+ return -ENOMEM;
+
+ /* Set up the cut-over percentages for the bus calculation. */
+ for (i = 0; i < priv->bus.num; i++) {
+ t1 = (u32)(100 * priv->bus.ib[i]) /
+ (u32)priv->bus.ib[priv->bus.num - 1];
+ priv->bus.p_up[i] = t1 - HIST;
+ priv->bus.p_down[i] = t2 - 2 * HIST;
+ t2 = t1;
+ }
+ /* Set the upper-most and lower-most bounds correctly. */
+ priv->bus.p_down[0] = 0;
+ priv->bus.p_down[1] = (priv->bus.p_down[1] > (2 * HIST)) ?
+ priv->bus.p_down[1] : (2 * HIST);
+ if (priv->bus.num >= 1)
+ priv->bus.p_up[priv->bus.num - 1] = 100;
+ _update_cutoff(priv, priv->bus.max);
+
+ return 0;
+}
+
+static int gpubw_stop(struct devfreq *devfreq)
+{
+ struct devfreq_msm_adreno_tz_data *priv = devfreq->data;
+
+ if (priv) {
+ kfree(priv->bus.up);
+ kfree(priv->bus.down);
+ kfree(priv->bus.p_up);
+ kfree(priv->bus.p_down);
+ }
+ devfreq->data = NULL;
+ return 0;
+}
+
+static int devfreq_gpubw_event_handler(struct devfreq *devfreq,
+ unsigned int event, void *data)
+{
+ int result = 0;
+ unsigned long freq;
+
+ mutex_lock(&devfreq->lock);
+ freq = devfreq->previous_freq;
+ switch (event) {
+ case DEVFREQ_GOV_START:
+ result = gpubw_start(devfreq);
+ break;
+ case DEVFREQ_GOV_STOP:
+ result = gpubw_stop(devfreq);
+ break;
+ case DEVFREQ_GOV_RESUME:
+ /* TODO ..... */
+ /* ret = update_devfreq(devfreq); */
+ break;
+ case DEVFREQ_GOV_SUSPEND:
+ {
+ struct devfreq_msm_adreno_tz_data *priv = devfreq->data;
+
+ priv->bus.total_time = 0;
+ priv->bus.gpu_time = 0;
+ priv->bus.ram_time = 0;
+ }
+ break;
+ default:
+ result = 0;
+ break;
+ }
+ mutex_unlock(&devfreq->lock);
+ return result;
+}
+
+static struct devfreq_governor devfreq_gpubw = {
+ .name = "gpubw_mon",
+ .get_target_freq = devfreq_gpubw_get_target,
+ .event_handler = devfreq_gpubw_event_handler,
+};
+
+static int __init devfreq_gpubw_init(void)
+{
+ return devfreq_add_governor(&devfreq_gpubw);
+}
+subsys_initcall(devfreq_gpubw_init);
+
+static void __exit devfreq_gpubw_exit(void)
+{
+ int ret;
+
+ ret = devfreq_remove_governor(&devfreq_gpubw);
+ if (ret)
+ pr_err("%s: failed remove governor %d\n", __func__, ret);
+
+}
+module_exit(devfreq_gpubw_exit);
+
+MODULE_DESCRIPTION("GPU bus bandwidth voting driver. Uses VBIF counters");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index cf04d24..6b54e02 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -303,6 +303,9 @@ static const struct file_operations dma_buf_fops = {
.llseek = dma_buf_llseek,
.poll = dma_buf_poll,
.unlocked_ioctl = dma_buf_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = dma_buf_ioctl,
+#endif
};
/*
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 192080e..13b8b71 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -514,6 +514,17 @@
For debugging issues having to do with stability and overall system
health, you should probably say 'Y' here.
+config EDAC_LLCC_POLL
+ depends on EDAC_QCOM_LLCC
+ bool "Poll on LLCC ECC registers - LLCC"
+ help
+ This option chooses whether or not you want to poll on the LLCC
+ ECC registers. When this is enabled, the polling rate can be set as
+ a module parameter. By default, it will call the polling function
+ every second.
+ This option should only be used if the associated interrupt lines
+ are not enabled.
+
config EDAC_QCOM_LLCC_PANIC_ON_CE
depends on EDAC_QCOM_LLCC
bool "panic on correctable errors - qcom llcc"
diff --git a/drivers/edac/kryo3xx_arm64_edac.c b/drivers/edac/kryo3xx_arm64_edac.c
index 7e2aadc..4ac880b 100644
--- a/drivers/edac/kryo3xx_arm64_edac.c
+++ b/drivers/edac/kryo3xx_arm64_edac.c
@@ -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
@@ -60,6 +60,13 @@ static inline void set_errxctlr_el1(void)
asm volatile("msr s3_0_c5_c4_1, %0" : : "r" (val));
}
+static inline void set_errxmisc_overflow(void)
+{
+ u64 val = 0x7F7F00000000;
+
+ asm volatile("msr s3_0_c5_c5_0, %0" : : "r" (val));
+}
+
static inline void write_errselr_el1(u64 val)
{
asm volatile("msr s3_0_c5_c3_1, %0" : : "r" (val));
@@ -319,9 +326,7 @@ void kryo3xx_poll_cache_errors(struct edac_device_ctl_info *edev_ctl)
static irqreturn_t kryo3xx_l1_l2_handler(int irq, void *drvdata)
{
- struct erp_drvdata *drv = *(struct erp_drvdata **)(drvdata);
-
- kryo3xx_check_l1_l2_ecc(drv->edev_ctl);
+ kryo3xx_check_l1_l2_ecc(panic_handler_drvdata->edev_ctl);
return IRQ_HANDLED;
}
@@ -334,14 +339,24 @@ static irqreturn_t kryo3xx_l3_scu_handler(int irq, void *drvdata)
return IRQ_HANDLED;
}
+static void initialize_registers(void *info)
+{
+ set_errxctlr_el1();
+ set_errxmisc_overflow();
+}
+
static int kryo3xx_cpu_erp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct erp_drvdata *drv;
int rc = 0;
int fail = 0;
+ int cpu;
- set_errxctlr_el1();
+ for_each_possible_cpu(cpu)
+ smp_call_function_single(cpu, initialize_registers, NULL, 1);
+
+
drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
diff --git a/drivers/edac/qcom_llcc_edac.c b/drivers/edac/qcom_llcc_edac.c
index 6bec860..4403f86 100644
--- a/drivers/edac/qcom_llcc_edac.c
+++ b/drivers/edac/qcom_llcc_edac.c
@@ -78,10 +78,12 @@
#define DRP_TRP_INT_CLEAR 0x3
#define DRP_TRP_CNT_CLEAR 0x3
+#ifdef CONFIG_EDAC_LLCC_POLL
static int poll_msec = 5000;
module_param(poll_msec, int, 0444);
+#endif
-static int interrupt_mode;
+static int interrupt_mode = 1;
module_param(interrupt_mode, int, 0444);
MODULE_PARM_DESC(interrupt_mode,
"Controls whether to use interrupt or poll mode");
@@ -331,10 +333,12 @@ static void qcom_llcc_check_cache_errors
}
}
+#ifdef CONFIG_EDAC_LLCC_POLL
static void qcom_llcc_poll_cache_errors(struct edac_device_ctl_info *edev_ctl)
{
qcom_llcc_check_cache_errors(edev_ctl);
}
+#endif
static irqreturn_t llcc_ecc_irq_handler
(int irq, void *edev_ctl)
@@ -360,9 +364,11 @@ static int qcom_llcc_erp_probe(struct platform_device *pdev)
edev_ctl->mod_name = dev_name(dev);
edev_ctl->dev_name = dev_name(dev);
edev_ctl->ctl_name = "llcc";
+#ifdef CONFIG_EDAC_LLCC_POLL
edev_ctl->poll_msec = poll_msec;
edev_ctl->edac_check = qcom_llcc_poll_cache_errors;
edev_ctl->defer_work = 1;
+#endif
edev_ctl->panic_on_ce = LLCC_ERP_PANIC_ON_CE;
edev_ctl->panic_on_ue = LLCC_ERP_PANIC_ON_UE;
@@ -383,7 +389,7 @@ static int qcom_llcc_erp_probe(struct platform_device *pdev)
}
rc = devm_request_irq(dev, drv->ecc_irq, llcc_ecc_irq_handler,
- IRQF_TRIGGER_RISING, "llcc_ecc", edev_ctl);
+ IRQF_TRIGGER_HIGH, "llcc_ecc", edev_ctl);
if (rc) {
dev_err(dev, "failed to request ecc irq\n");
goto out;
diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c
index a27d350..2e093c3 100644
--- a/drivers/extcon/extcon-usb-gpio.c
+++ b/drivers/extcon/extcon-usb-gpio.c
@@ -24,7 +24,6 @@
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
-#include <linux/pm_wakeirq.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/acpi.h>
@@ -36,7 +35,9 @@ struct usb_extcon_info {
struct extcon_dev *edev;
struct gpio_desc *id_gpiod;
+ struct gpio_desc *vbus_gpiod;
int id_irq;
+ int vbus_irq;
unsigned long debounce_jiffies;
struct delayed_work wq_detcable;
@@ -48,31 +49,47 @@ static const unsigned int usb_extcon_cable[] = {
EXTCON_NONE,
};
+/*
+ * "USB" = VBUS and "USB-HOST" = !ID, so we have:
+ * Both "USB" and "USB-HOST" can't be set as active at the
+ * same time so if "USB-HOST" is active (i.e. ID is 0) we keep "USB" inactive
+ * even if VBUS is on.
+ *
+ * State | ID | VBUS
+ * ----------------------------------------
+ * [1] USB | H | H
+ * [2] none | H | L
+ * [3] USB-HOST | L | H
+ * [4] USB-HOST | L | L
+ *
+ * In case we have only one of these signals:
+ * - VBUS only - we want to distinguish between [1] and [2], so ID is always 1.
+ * - ID only - we want to distinguish between [1] and [4], so VBUS = ID.
+ */
static void usb_extcon_detect_cable(struct work_struct *work)
{
- int id;
+ int id, vbus;
struct usb_extcon_info *info = container_of(to_delayed_work(work),
struct usb_extcon_info,
wq_detcable);
- /* check ID and update cable state */
- id = gpiod_get_value_cansleep(info->id_gpiod);
- if (id) {
- /*
- * ID = 1 means USB HOST cable detached.
- * As we don't have event for USB peripheral cable attached,
- * we simulate USB peripheral attach here.
- */
+ /* check ID and VBUS and update cable state */
+ id = info->id_gpiod ?
+ gpiod_get_value_cansleep(info->id_gpiod) : 1;
+ vbus = info->vbus_gpiod ?
+ gpiod_get_value_cansleep(info->vbus_gpiod) : id;
+
+ /* at first we clean states which are no longer active */
+ if (id)
extcon_set_state_sync(info->edev, EXTCON_USB_HOST, false);
- extcon_set_state_sync(info->edev, EXTCON_USB, true);
- } else {
- /*
- * ID = 0 means USB HOST cable attached.
- * As we don't have event for USB peripheral cable detached,
- * we simulate USB peripheral detach here.
- */
+ if (!vbus)
extcon_set_state_sync(info->edev, EXTCON_USB, false);
+
+ if (!id) {
extcon_set_state_sync(info->edev, EXTCON_USB_HOST, true);
+ } else {
+ if (vbus)
+ extcon_set_state_sync(info->edev, EXTCON_USB, true);
}
}
@@ -101,12 +118,21 @@ static int usb_extcon_probe(struct platform_device *pdev)
return -ENOMEM;
info->dev = dev;
- info->id_gpiod = devm_gpiod_get(&pdev->dev, "id", GPIOD_IN);
- if (IS_ERR(info->id_gpiod)) {
- dev_err(dev, "failed to get ID GPIO\n");
- return PTR_ERR(info->id_gpiod);
+ info->id_gpiod = devm_gpiod_get_optional(&pdev->dev, "id", GPIOD_IN);
+ info->vbus_gpiod = devm_gpiod_get_optional(&pdev->dev, "vbus",
+ GPIOD_IN);
+
+ if (!info->id_gpiod && !info->vbus_gpiod) {
+ dev_err(dev, "failed to get gpios\n");
+ return -ENODEV;
}
+ if (IS_ERR(info->id_gpiod))
+ return PTR_ERR(info->id_gpiod);
+
+ if (IS_ERR(info->vbus_gpiod))
+ return PTR_ERR(info->vbus_gpiod);
+
info->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable);
if (IS_ERR(info->edev)) {
dev_err(dev, "failed to allocate extcon device\n");
@@ -119,32 +145,58 @@ static int usb_extcon_probe(struct platform_device *pdev)
return ret;
}
- ret = gpiod_set_debounce(info->id_gpiod,
- USB_GPIO_DEBOUNCE_MS * 1000);
+ if (info->id_gpiod)
+ ret = gpiod_set_debounce(info->id_gpiod,
+ USB_GPIO_DEBOUNCE_MS * 1000);
+ if (!ret && info->vbus_gpiod)
+ ret = gpiod_set_debounce(info->vbus_gpiod,
+ USB_GPIO_DEBOUNCE_MS * 1000);
+
if (ret < 0)
info->debounce_jiffies = msecs_to_jiffies(USB_GPIO_DEBOUNCE_MS);
INIT_DELAYED_WORK(&info->wq_detcable, usb_extcon_detect_cable);
- info->id_irq = gpiod_to_irq(info->id_gpiod);
- if (info->id_irq < 0) {
- dev_err(dev, "failed to get ID IRQ\n");
- return info->id_irq;
+ if (info->id_gpiod) {
+ info->id_irq = gpiod_to_irq(info->id_gpiod);
+ if (info->id_irq < 0) {
+ dev_err(dev, "failed to get ID IRQ\n");
+ return info->id_irq;
+ }
+
+ ret = devm_request_threaded_irq(dev, info->id_irq, NULL,
+ usb_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ pdev->name, info);
+ if (ret < 0) {
+ dev_err(dev, "failed to request handler for ID IRQ\n");
+ return ret;
+ }
}
- ret = devm_request_threaded_irq(dev, info->id_irq, NULL,
- usb_irq_handler,
- IRQF_TRIGGER_RISING |
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- pdev->name, info);
- if (ret < 0) {
- dev_err(dev, "failed to request handler for ID IRQ\n");
- return ret;
+ if (info->vbus_gpiod) {
+ info->vbus_irq = gpiod_to_irq(info->vbus_gpiod);
+ if (info->vbus_irq < 0) {
+ dev_err(dev, "failed to get VBUS IRQ\n");
+ return info->vbus_irq;
+ }
+
+ ret = devm_request_threaded_irq(dev, info->vbus_irq, NULL,
+ usb_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ pdev->name, info);
+ if (ret < 0) {
+ dev_err(dev, "failed to request handler for VBUS IRQ\n");
+ return ret;
+ }
}
platform_set_drvdata(pdev, info);
device_init_wakeup(dev, true);
- dev_pm_set_wake_irq(dev, info->id_irq);
/* Perform initial detection */
usb_extcon_detect_cable(&info->wq_detcable.work);
@@ -157,8 +209,6 @@ static int usb_extcon_remove(struct platform_device *pdev)
struct usb_extcon_info *info = platform_get_drvdata(pdev);
cancel_delayed_work_sync(&info->wq_detcable);
-
- dev_pm_clear_wake_irq(&pdev->dev);
device_init_wakeup(&pdev->dev, false);
return 0;
@@ -170,12 +220,32 @@ static int usb_extcon_suspend(struct device *dev)
struct usb_extcon_info *info = dev_get_drvdata(dev);
int ret = 0;
+ if (device_may_wakeup(dev)) {
+ if (info->id_gpiod) {
+ ret = enable_irq_wake(info->id_irq);
+ if (ret)
+ return ret;
+ }
+ if (info->vbus_gpiod) {
+ ret = enable_irq_wake(info->vbus_irq);
+ if (ret) {
+ if (info->id_gpiod)
+ disable_irq_wake(info->id_irq);
+
+ return ret;
+ }
+ }
+ }
+
/*
* We don't want to process any IRQs after this point
* as GPIOs used behind I2C subsystem might not be
* accessible until resume completes. So disable IRQ.
*/
- disable_irq(info->id_irq);
+ if (info->id_gpiod)
+ disable_irq(info->id_irq);
+ if (info->vbus_gpiod)
+ disable_irq(info->vbus_irq);
return ret;
}
@@ -185,7 +255,28 @@ static int usb_extcon_resume(struct device *dev)
struct usb_extcon_info *info = dev_get_drvdata(dev);
int ret = 0;
- enable_irq(info->id_irq);
+ if (device_may_wakeup(dev)) {
+ if (info->id_gpiod) {
+ ret = disable_irq_wake(info->id_irq);
+ if (ret)
+ return ret;
+ }
+ if (info->vbus_gpiod) {
+ ret = disable_irq_wake(info->vbus_irq);
+ if (ret) {
+ if (info->id_gpiod)
+ enable_irq_wake(info->id_irq);
+
+ return ret;
+ }
+ }
+ }
+
+ if (info->id_gpiod)
+ enable_irq(info->id_irq);
+ if (info->vbus_gpiod)
+ enable_irq(info->vbus_irq);
+
if (!device_may_wakeup(dev))
queue_delayed_work(system_power_efficient_wq,
&info->wq_detcable, 0);
diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c
index 4a0f5ea..1e2e519 100644
--- a/drivers/firmware/qcom_scm-64.c
+++ b/drivers/firmware/qcom_scm-64.c
@@ -91,6 +91,7 @@ static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
dma_addr_t args_phys = 0;
void *args_virt = NULL;
size_t alloc_len;
+ struct arm_smccc_quirk quirk = {.id = ARM_SMCCC_QUIRK_QCOM_A6};
if (unlikely(arglen > N_REGISTER_ARGS)) {
alloc_len = N_EXT_QCOM_SCM_ARGS * sizeof(u64);
@@ -131,10 +132,16 @@ static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
qcom_smccc_convention,
ARM_SMCCC_OWNER_SIP, fn_id);
+ quirk.state.a6 = 0;
+
do {
- arm_smccc_smc(cmd, desc->arginfo, desc->args[0],
- desc->args[1], desc->args[2], x5, 0, 0,
- res);
+ arm_smccc_smc_quirk(cmd, desc->arginfo, desc->args[0],
+ desc->args[1], desc->args[2], x5,
+ quirk.state.a6, 0, res, &quirk);
+
+ if (res->a0 == QCOM_SCM_INTERRUPTED)
+ cmd = res->a0;
+
} while (res->a0 == QCOM_SCM_INTERRUPTED);
mutex_unlock(&qcom_scm_lock);
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index 72a4b32..986248f 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -571,8 +571,10 @@ struct gpio_desc *acpi_find_gpio(struct device *dev,
}
desc = acpi_get_gpiod_by_index(adev, propname, idx, &info);
- if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER))
+ if (!IS_ERR(desc))
break;
+ if (PTR_ERR(desc) == -EPROBE_DEFER)
+ return ERR_CAST(desc);
}
/* Then from plain _CRS GPIOs */
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 7491180..0bc0afb 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -90,7 +90,7 @@ struct detailed_mode_closure {
#define LEVEL_GTF2 2
#define LEVEL_CVT 3
-static struct edid_quirk {
+static const struct edid_quirk {
char vendor[4];
int product_id;
u32 quirks;
@@ -1449,7 +1449,7 @@ EXPORT_SYMBOL(drm_edid_duplicate);
*
* Returns true if @vendor is in @edid, false otherwise
*/
-static bool edid_vendor(struct edid *edid, char *vendor)
+static bool edid_vendor(struct edid *edid, const char *vendor)
{
char edid_vendor[3];
@@ -1469,7 +1469,7 @@ static bool edid_vendor(struct edid *edid, char *vendor)
*/
static u32 edid_get_quirks(struct edid *edid)
{
- struct edid_quirk *quirk;
+ const struct edid_quirk *quirk;
int i;
for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) {
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 670beeb..923150d 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -240,6 +240,7 @@ static int i915_getparam(struct drm_device *dev, void *data,
case I915_PARAM_IRQ_ACTIVE:
case I915_PARAM_ALLOW_BATCHBUFFER:
case I915_PARAM_LAST_DISPATCH:
+ case I915_PARAM_HAS_EXEC_CONSTANTS:
/* Reject all old ums/dri params. */
return -ENODEV;
case I915_PARAM_CHIPSET_ID:
@@ -266,9 +267,6 @@ static int i915_getparam(struct drm_device *dev, void *data,
case I915_PARAM_HAS_BSD2:
value = intel_engine_initialized(&dev_priv->engine[VCS2]);
break;
- case I915_PARAM_HAS_EXEC_CONSTANTS:
- value = INTEL_GEN(dev_priv) >= 4;
- break;
case I915_PARAM_HAS_LLC:
value = HAS_LLC(dev_priv);
break;
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index da832d3..e0d7245 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1225,7 +1225,7 @@ struct intel_gen6_power_mgmt {
unsigned boosts;
/* manual wa residency calculations */
- struct intel_rps_ei up_ei, down_ei;
+ struct intel_rps_ei ei;
/*
* Protects RPS/RC6 register access and PCU communication.
@@ -1751,8 +1751,6 @@ struct drm_i915_private {
const struct intel_device_info info;
- int relative_constants_mode;
-
void __iomem *regs;
struct intel_uncore uncore;
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 00eb481..7b203092 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -4587,8 +4587,6 @@ i915_gem_load_init(struct drm_device *dev)
init_waitqueue_head(&dev_priv->gpu_error.wait_queue);
init_waitqueue_head(&dev_priv->gpu_error.reset_queue);
- dev_priv->relative_constants_mode = I915_EXEC_CONSTANTS_REL_GENERAL;
-
init_waitqueue_head(&dev_priv->pending_flip_queue);
dev_priv->mm.interruptible = true;
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 0c400f8..2117f17 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -1454,10 +1454,7 @@ execbuf_submit(struct i915_execbuffer_params *params,
struct drm_i915_gem_execbuffer2 *args,
struct list_head *vmas)
{
- struct drm_i915_private *dev_priv = params->request->i915;
u64 exec_start, exec_len;
- int instp_mode;
- u32 instp_mask;
int ret;
ret = i915_gem_execbuffer_move_to_gpu(params->request, vmas);
@@ -1468,56 +1465,11 @@ execbuf_submit(struct i915_execbuffer_params *params,
if (ret)
return ret;
- instp_mode = args->flags & I915_EXEC_CONSTANTS_MASK;
- instp_mask = I915_EXEC_CONSTANTS_MASK;
- switch (instp_mode) {
- case I915_EXEC_CONSTANTS_REL_GENERAL:
- case I915_EXEC_CONSTANTS_ABSOLUTE:
- case I915_EXEC_CONSTANTS_REL_SURFACE:
- if (instp_mode != 0 && params->engine->id != RCS) {
- DRM_DEBUG("non-0 rel constants mode on non-RCS\n");
- return -EINVAL;
- }
-
- if (instp_mode != dev_priv->relative_constants_mode) {
- if (INTEL_INFO(dev_priv)->gen < 4) {
- DRM_DEBUG("no rel constants on pre-gen4\n");
- return -EINVAL;
- }
-
- if (INTEL_INFO(dev_priv)->gen > 5 &&
- instp_mode == I915_EXEC_CONSTANTS_REL_SURFACE) {
- DRM_DEBUG("rel surface constants mode invalid on gen5+\n");
- return -EINVAL;
- }
-
- /* The HW changed the meaning on this bit on gen6 */
- if (INTEL_INFO(dev_priv)->gen >= 6)
- instp_mask &= ~I915_EXEC_CONSTANTS_REL_SURFACE;
- }
- break;
- default:
- DRM_DEBUG("execbuf with unknown constants: %d\n", instp_mode);
+ if (args->flags & I915_EXEC_CONSTANTS_MASK) {
+ DRM_DEBUG("I915_EXEC_CONSTANTS_* unsupported\n");
return -EINVAL;
}
- if (params->engine->id == RCS &&
- instp_mode != dev_priv->relative_constants_mode) {
- struct intel_ring *ring = params->request->ring;
-
- ret = intel_ring_begin(params->request, 4);
- if (ret)
- return ret;
-
- intel_ring_emit(ring, MI_NOOP);
- intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
- intel_ring_emit_reg(ring, INSTPM);
- intel_ring_emit(ring, instp_mask << 16 | instp_mode);
- intel_ring_advance(ring);
-
- dev_priv->relative_constants_mode = instp_mode;
- }
-
if (args->flags & I915_EXEC_GEN7_SOL_RESET) {
ret = i915_reset_gen7_sol_offsets(params->request);
if (ret)
diff --git a/drivers/gpu/drm/i915/i915_gem_shrinker.c b/drivers/gpu/drm/i915/i915_gem_shrinker.c
index 1c237d0..755d788 100644
--- a/drivers/gpu/drm/i915/i915_gem_shrinker.c
+++ b/drivers/gpu/drm/i915/i915_gem_shrinker.c
@@ -233,7 +233,7 @@ unsigned long i915_gem_shrink_all(struct drm_i915_private *dev_priv)
I915_SHRINK_BOUND |
I915_SHRINK_UNBOUND |
I915_SHRINK_ACTIVE);
- rcu_barrier(); /* wait until our RCU delayed slab frees are completed */
+ synchronize_rcu(); /* wait for our earlier RCU delayed slab frees */
return freed;
}
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 3fc286c..02908e3 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -990,68 +990,51 @@ static void vlv_c0_read(struct drm_i915_private *dev_priv,
ei->media_c0 = I915_READ(VLV_MEDIA_C0_COUNT);
}
-static bool vlv_c0_above(struct drm_i915_private *dev_priv,
- const struct intel_rps_ei *old,
- const struct intel_rps_ei *now,
- int threshold)
-{
- u64 time, c0;
- unsigned int mul = 100;
-
- if (old->cz_clock == 0)
- return false;
-
- if (I915_READ(VLV_COUNTER_CONTROL) & VLV_COUNT_RANGE_HIGH)
- mul <<= 8;
-
- time = now->cz_clock - old->cz_clock;
- time *= threshold * dev_priv->czclk_freq;
-
- /* Workload can be split between render + media, e.g. SwapBuffers
- * being blitted in X after being rendered in mesa. To account for
- * this we need to combine both engines into our activity counter.
- */
- c0 = now->render_c0 - old->render_c0;
- c0 += now->media_c0 - old->media_c0;
- c0 *= mul * VLV_CZ_CLOCK_TO_MILLI_SEC;
-
- return c0 >= time;
-}
-
void gen6_rps_reset_ei(struct drm_i915_private *dev_priv)
{
- vlv_c0_read(dev_priv, &dev_priv->rps.down_ei);
- dev_priv->rps.up_ei = dev_priv->rps.down_ei;
+ memset(&dev_priv->rps.ei, 0, sizeof(dev_priv->rps.ei));
}
static u32 vlv_wa_c0_ei(struct drm_i915_private *dev_priv, u32 pm_iir)
{
+ const struct intel_rps_ei *prev = &dev_priv->rps.ei;
struct intel_rps_ei now;
u32 events = 0;
- if ((pm_iir & (GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_UP_EI_EXPIRED)) == 0)
+ if ((pm_iir & GEN6_PM_RP_UP_EI_EXPIRED) == 0)
return 0;
vlv_c0_read(dev_priv, &now);
if (now.cz_clock == 0)
return 0;
- if (pm_iir & GEN6_PM_RP_DOWN_EI_EXPIRED) {
- if (!vlv_c0_above(dev_priv,
- &dev_priv->rps.down_ei, &now,
- dev_priv->rps.down_threshold))
- events |= GEN6_PM_RP_DOWN_THRESHOLD;
- dev_priv->rps.down_ei = now;
+ if (prev->cz_clock) {
+ u64 time, c0;
+ unsigned int mul;
+
+ mul = VLV_CZ_CLOCK_TO_MILLI_SEC * 100; /* scale to threshold% */
+ if (I915_READ(VLV_COUNTER_CONTROL) & VLV_COUNT_RANGE_HIGH)
+ mul <<= 8;
+
+ time = now.cz_clock - prev->cz_clock;
+ time *= dev_priv->czclk_freq;
+
+ /* Workload can be split between render + media,
+ * e.g. SwapBuffers being blitted in X after being rendered in
+ * mesa. To account for this we need to combine both engines
+ * into our activity counter.
+ */
+ c0 = now.render_c0 - prev->render_c0;
+ c0 += now.media_c0 - prev->media_c0;
+ c0 *= mul;
+
+ if (c0 > time * dev_priv->rps.up_threshold)
+ events = GEN6_PM_RP_UP_THRESHOLD;
+ else if (c0 < time * dev_priv->rps.down_threshold)
+ events = GEN6_PM_RP_DOWN_THRESHOLD;
}
- if (pm_iir & GEN6_PM_RP_UP_EI_EXPIRED) {
- if (vlv_c0_above(dev_priv,
- &dev_priv->rps.up_ei, &now,
- dev_priv->rps.up_threshold))
- events |= GEN6_PM_RP_UP_THRESHOLD;
- dev_priv->rps.up_ei = now;
- }
-
+ dev_priv->rps.ei = now;
return events;
}
@@ -4490,7 +4473,7 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
/* Let's track the enabled rps events */
if (IS_VALLEYVIEW(dev_priv))
/* WaGsvRC0ResidencyMethod:vlv */
- dev_priv->pm_rps_events = GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_UP_EI_EXPIRED;
+ dev_priv->pm_rps_events = GEN6_PM_RP_UP_EI_EXPIRED;
else
dev_priv->pm_rps_events = GEN6_PM_RPS_EVENTS;
@@ -4531,6 +4514,16 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
if (!IS_GEN2(dev_priv))
dev->vblank_disable_immediate = true;
+ /* Most platforms treat the display irq block as an always-on
+ * power domain. vlv/chv can disable it at runtime and need
+ * special care to avoid writing any of the display block registers
+ * outside of the power domain. We defer setting up the display irqs
+ * in this case to the runtime pm.
+ */
+ dev_priv->display_irqs_enabled = true;
+ if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
+ dev_priv->display_irqs_enabled = false;
+
dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp;
dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c
index 31e6edd..9e94886 100644
--- a/drivers/gpu/drm/i915/i915_pci.c
+++ b/drivers/gpu/drm/i915/i915_pci.c
@@ -417,6 +417,7 @@ static const struct pci_device_id pciidlist[] = {
INTEL_VLV_IDS(&intel_valleyview_info),
INTEL_BDW_GT12_IDS(&intel_broadwell_info),
INTEL_BDW_GT3_IDS(&intel_broadwell_gt3_info),
+ INTEL_BDW_RSVD_IDS(&intel_broadwell_info),
INTEL_CHV_IDS(&intel_cherryview_info),
INTEL_SKL_GT1_IDS(&intel_skylake_info),
INTEL_SKL_GT2_IDS(&intel_skylake_info),
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index b9be8a6..5dc6082 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -3696,10 +3696,6 @@ static void intel_update_pipe_config(struct intel_crtc *crtc,
/* drm_atomic_helper_update_legacy_modeset_state might not be called. */
crtc->base.mode = crtc->base.state->mode;
- DRM_DEBUG_KMS("Updating pipe size %ix%i -> %ix%i\n",
- old_crtc_state->pipe_src_w, old_crtc_state->pipe_src_h,
- pipe_config->pipe_src_w, pipe_config->pipe_src_h);
-
/*
* Update pipe size and adjust fitter if needed: the reason for this is
* that in compute_mode_changes we check the native mode (not the pfit
@@ -4832,23 +4828,17 @@ static void skylake_pfit_enable(struct intel_crtc *crtc)
struct intel_crtc_scaler_state *scaler_state =
&crtc->config->scaler_state;
- DRM_DEBUG_KMS("for crtc_state = %p\n", crtc->config);
-
if (crtc->config->pch_pfit.enabled) {
int id;
- if (WARN_ON(crtc->config->scaler_state.scaler_id < 0)) {
- DRM_ERROR("Requesting pfit without getting a scaler first\n");
+ if (WARN_ON(crtc->config->scaler_state.scaler_id < 0))
return;
- }
id = scaler_state->scaler_id;
I915_WRITE(SKL_PS_CTRL(pipe, id), PS_SCALER_EN |
PS_FILTER_MEDIUM | scaler_state->scalers[id].mode);
I915_WRITE(SKL_PS_WIN_POS(pipe, id), crtc->config->pch_pfit.pos);
I915_WRITE(SKL_PS_WIN_SZ(pipe, id), crtc->config->pch_pfit.size);
-
- DRM_DEBUG_KMS("for crtc_state = %p scaler_id = %d\n", crtc->config, id);
}
}
diff --git a/drivers/gpu/drm/i915/intel_hotplug.c b/drivers/gpu/drm/i915/intel_hotplug.c
index 334d47b..db3afdf 100644
--- a/drivers/gpu/drm/i915/intel_hotplug.c
+++ b/drivers/gpu/drm/i915/intel_hotplug.c
@@ -219,7 +219,7 @@ static void intel_hpd_irq_storm_reenable_work(struct work_struct *work)
}
}
}
- if (dev_priv->display.hpd_irq_setup)
+ if (dev_priv->display_irqs_enabled && dev_priv->display.hpd_irq_setup)
dev_priv->display.hpd_irq_setup(dev_priv);
spin_unlock_irq(&dev_priv->irq_lock);
@@ -425,7 +425,7 @@ void intel_hpd_irq_handler(struct drm_i915_private *dev_priv,
}
}
- if (storm_detected)
+ if (storm_detected && dev_priv->display_irqs_enabled)
dev_priv->display.hpd_irq_setup(dev_priv);
spin_unlock(&dev_priv->irq_lock);
@@ -471,10 +471,12 @@ void intel_hpd_init(struct drm_i915_private *dev_priv)
* Interrupt setup is already guaranteed to be single-threaded, this is
* just to make the assert_spin_locked checks happy.
*/
- spin_lock_irq(&dev_priv->irq_lock);
- if (dev_priv->display.hpd_irq_setup)
- dev_priv->display.hpd_irq_setup(dev_priv);
- spin_unlock_irq(&dev_priv->irq_lock);
+ if (dev_priv->display_irqs_enabled && dev_priv->display.hpd_irq_setup) {
+ spin_lock_irq(&dev_priv->irq_lock);
+ if (dev_priv->display_irqs_enabled)
+ dev_priv->display.hpd_irq_setup(dev_priv);
+ spin_unlock_irq(&dev_priv->irq_lock);
+ }
}
static void i915_hpd_poll_init_work(struct work_struct *work)
diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
index 4147e51..67db157 100644
--- a/drivers/gpu/drm/i915/intel_lrc.c
+++ b/drivers/gpu/drm/i915/intel_lrc.c
@@ -2152,42 +2152,30 @@ static int execlists_context_deferred_alloc(struct i915_gem_context *ctx,
void intel_lr_context_resume(struct drm_i915_private *dev_priv)
{
+ struct i915_gem_context *ctx = dev_priv->kernel_context;
struct intel_engine_cs *engine;
- struct i915_gem_context *ctx;
- /* Because we emit WA_TAIL_DWORDS there may be a disparity
- * between our bookkeeping in ce->ring->head and ce->ring->tail and
- * that stored in context. As we only write new commands from
- * ce->ring->tail onwards, everything before that is junk. If the GPU
- * starts reading from its RING_HEAD from the context, it may try to
- * execute that junk and die.
- *
- * So to avoid that we reset the context images upon resume. For
- * simplicity, we just zero everything out.
- */
- list_for_each_entry(ctx, &dev_priv->context_list, link) {
- for_each_engine(engine, dev_priv) {
- struct intel_context *ce = &ctx->engine[engine->id];
- u32 *reg;
+ for_each_engine(engine, dev_priv) {
+ struct intel_context *ce = &ctx->engine[engine->id];
+ void *vaddr;
+ uint32_t *reg_state;
- if (!ce->state)
- continue;
+ if (!ce->state)
+ continue;
- reg = i915_gem_object_pin_map(ce->state->obj,
- I915_MAP_WB);
- if (WARN_ON(IS_ERR(reg)))
- continue;
+ vaddr = i915_gem_object_pin_map(ce->state->obj, I915_MAP_WB);
+ if (WARN_ON(IS_ERR(vaddr)))
+ continue;
- reg += LRC_STATE_PN * PAGE_SIZE / sizeof(*reg);
- reg[CTX_RING_HEAD+1] = 0;
- reg[CTX_RING_TAIL+1] = 0;
+ reg_state = vaddr + LRC_STATE_PN * PAGE_SIZE;
- ce->state->obj->dirty = true;
- i915_gem_object_unpin_map(ce->state->obj);
+ reg_state[CTX_RING_HEAD+1] = 0;
+ reg_state[CTX_RING_TAIL+1] = 0;
- ce->ring->head = ce->ring->tail = 0;
- ce->ring->last_retired_head = -1;
- intel_ring_update_space(ce->ring);
- }
+ ce->state->obj->dirty = true;
+ i915_gem_object_unpin_map(ce->state->obj);
+
+ ce->ring->head = 0;
+ ce->ring->tail = 0;
}
}
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index e559a45..2c6d59d 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -4903,6 +4903,12 @@ static void gen6_set_rps_thresholds(struct drm_i915_private *dev_priv, u8 val)
break;
}
+ /* When byt can survive without system hang with dynamic
+ * sw freq adjustments, this restriction can be lifted.
+ */
+ if (IS_VALLEYVIEW(dev_priv))
+ goto skip_hw_write;
+
I915_WRITE(GEN6_RP_UP_EI,
GT_INTERVAL_FROM_US(dev_priv, ei_up));
I915_WRITE(GEN6_RP_UP_THRESHOLD,
@@ -4923,6 +4929,7 @@ static void gen6_set_rps_thresholds(struct drm_i915_private *dev_priv, u8 val)
GEN6_RP_UP_BUSY_AVG |
GEN6_RP_DOWN_IDLE_AVG);
+skip_hw_write:
dev_priv->rps.power = new_power;
dev_priv->rps.up_threshold = threshold_up;
dev_priv->rps.down_threshold = threshold_down;
@@ -4933,8 +4940,9 @@ static u32 gen6_rps_pm_mask(struct drm_i915_private *dev_priv, u8 val)
{
u32 mask = 0;
+ /* We use UP_EI_EXPIRED interupts for both up/down in manual mode */
if (val > dev_priv->rps.min_freq_softlimit)
- mask |= GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_DOWN_THRESHOLD | GEN6_PM_RP_DOWN_TIMEOUT;
+ mask |= GEN6_PM_RP_UP_EI_EXPIRED | GEN6_PM_RP_DOWN_THRESHOLD | GEN6_PM_RP_DOWN_TIMEOUT;
if (val < dev_priv->rps.max_freq_softlimit)
mask |= GEN6_PM_RP_UP_EI_EXPIRED | GEN6_PM_RP_UP_THRESHOLD;
@@ -5034,7 +5042,7 @@ void gen6_rps_busy(struct drm_i915_private *dev_priv)
{
mutex_lock(&dev_priv->rps.hw_lock);
if (dev_priv->rps.enabled) {
- if (dev_priv->pm_rps_events & (GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_UP_EI_EXPIRED))
+ if (dev_priv->pm_rps_events & GEN6_PM_RP_UP_EI_EXPIRED)
gen6_rps_reset_ei(dev_priv);
I915_WRITE(GEN6_PMINTRMSK,
gen6_rps_pm_mask(dev_priv, dev_priv->rps.cur_freq));
@@ -7960,10 +7968,10 @@ static bool skl_pcode_try_request(struct drm_i915_private *dev_priv, u32 mbox,
* @timeout_base_ms: timeout for polling with preemption enabled
*
* Keep resending the @request to @mbox until PCODE acknowledges it, PCODE
- * reports an error or an overall timeout of @timeout_base_ms+10 ms expires.
+ * reports an error or an overall timeout of @timeout_base_ms+50 ms expires.
* The request is acknowledged once the PCODE reply dword equals @reply after
* applying @reply_mask. Polling is first attempted with preemption enabled
- * for @timeout_base_ms and if this times out for another 10 ms with
+ * for @timeout_base_ms and if this times out for another 50 ms with
* preemption disabled.
*
* Returns 0 on success, %-ETIMEDOUT in case of a timeout, <0 in case of some
@@ -7999,14 +8007,15 @@ int skl_pcode_request(struct drm_i915_private *dev_priv, u32 mbox, u32 request,
* worst case) _and_ PCODE was busy for some reason even after a
* (queued) request and @timeout_base_ms delay. As a workaround retry
* the poll with preemption disabled to maximize the number of
- * requests. Increase the timeout from @timeout_base_ms to 10ms to
+ * requests. Increase the timeout from @timeout_base_ms to 50ms to
* account for interrupts that could reduce the number of these
- * requests.
+ * requests, and for any quirks of the PCODE firmware that delays
+ * the request completion.
*/
DRM_DEBUG_KMS("PCODE timeout, retrying with preemption disabled\n");
WARN_ON_ONCE(timeout_base_ms > 3);
preempt_disable();
- ret = wait_for_atomic(COND, 10);
+ ret = wait_for_atomic(COND, 50);
preempt_enable();
out:
diff --git a/drivers/gpu/drm/mga/mga_dma.c b/drivers/gpu/drm/mga/mga_dma.c
index 1f2f9ca..4556e2b 100644
--- a/drivers/gpu/drm/mga/mga_dma.c
+++ b/drivers/gpu/drm/mga/mga_dma.c
@@ -392,6 +392,24 @@ int mga_driver_load(struct drm_device *dev, unsigned long flags)
drm_mga_private_t *dev_priv;
int ret;
+ /* There are PCI versions of the G450. These cards have the
+ * same PCI ID as the AGP G450, but have an additional PCI-to-PCI
+ * bridge chip. We detect these cards, which are not currently
+ * supported by this driver, by looking at the device ID of the
+ * bus the "card" is on. If vendor is 0x3388 (Hint Corp) and the
+ * device is 0x0021 (HB6 Universal PCI-PCI bridge), we reject the
+ * device.
+ */
+ if ((dev->pdev->device == 0x0525) && dev->pdev->bus->self
+ && (dev->pdev->bus->self->vendor == 0x3388)
+ && (dev->pdev->bus->self->device == 0x0021)
+ && dev->agp) {
+ /* FIXME: This should be quirked in the pci core, but oh well
+ * the hw probably stopped existing. */
+ arch_phys_wc_del(dev->agp->agp_mtrr);
+ kfree(dev->agp);
+ dev->agp = NULL;
+ }
dev_priv = kzalloc(sizeof(drm_mga_private_t), GFP_KERNEL);
if (!dev_priv)
return -ENOMEM;
@@ -698,7 +716,7 @@ static int mga_do_pci_dma_bootstrap(struct drm_device *dev,
static int mga_do_dma_bootstrap(struct drm_device *dev,
drm_mga_dma_bootstrap_t *dma_bs)
{
- const int is_agp = (dma_bs->agp_mode != 0) && drm_pci_device_is_agp(dev);
+ const int is_agp = (dma_bs->agp_mode != 0) && dev->agp;
int err;
drm_mga_private_t *const dev_priv =
(drm_mga_private_t *) dev->dev_private;
diff --git a/drivers/gpu/drm/mga/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c
index 25b2a1a..63ba0699 100644
--- a/drivers/gpu/drm/mga/mga_drv.c
+++ b/drivers/gpu/drm/mga/mga_drv.c
@@ -37,8 +37,6 @@
#include <drm/drm_pciids.h>
-static int mga_driver_device_is_agp(struct drm_device *dev);
-
static struct pci_device_id pciidlist[] = {
mga_PCI_IDS
};
@@ -66,7 +64,6 @@ static struct drm_driver driver = {
.lastclose = mga_driver_lastclose,
.set_busid = drm_pci_set_busid,
.dma_quiescent = mga_driver_dma_quiescent,
- .device_is_agp = mga_driver_device_is_agp,
.get_vblank_counter = mga_get_vblank_counter,
.enable_vblank = mga_enable_vblank,
.disable_vblank = mga_disable_vblank,
@@ -107,37 +104,3 @@ module_exit(mga_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL and additional rights");
-
-/**
- * Determine if the device really is AGP or not.
- *
- * In addition to the usual tests performed by \c drm_device_is_agp, this
- * function detects PCI G450 cards that appear to the system exactly like
- * AGP G450 cards.
- *
- * \param dev The device to be tested.
- *
- * \returns
- * If the device is a PCI G450, zero is returned. Otherwise 2 is returned.
- */
-static int mga_driver_device_is_agp(struct drm_device *dev)
-{
- const struct pci_dev *const pdev = dev->pdev;
-
- /* There are PCI versions of the G450. These cards have the
- * same PCI ID as the AGP G450, but have an additional PCI-to-PCI
- * bridge chip. We detect these cards, which are not currently
- * supported by this driver, by looking at the device ID of the
- * bus the "card" is on. If vendor is 0x3388 (Hint Corp) and the
- * device is 0x0021 (HB6 Universal PCI-PCI bridge), we reject the
- * device.
- */
-
- if ((pdev->device == 0x0525) && pdev->bus->self
- && (pdev->bus->self->vendor == 0x3388)
- && (pdev->bus->self->device == 0x0021)) {
- return 0;
- }
-
- return 2;
-}
diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c
index 5127b75..7250ffc 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_device.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_device.c
@@ -25,9 +25,6 @@ bool hang_debug = false;
MODULE_PARM_DESC(hang_debug, "Dump registers when hang is detected (can be slow!)");
module_param_named(hang_debug, hang_debug, bool, 0600);
-struct msm_gpu *a3xx_gpu_init(struct drm_device *dev);
-struct msm_gpu *a4xx_gpu_init(struct drm_device *dev);
-
static const struct adreno_info gpulist[] = {
{
.rev = ADRENO_REV(3, 0, 5, ANY_ID),
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
index a54f6e0..07d99bd 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
@@ -311,4 +311,7 @@ static inline void adreno_gpu_write(struct adreno_gpu *gpu,
gpu_write(&gpu->base, reg - 1, data);
}
+struct msm_gpu *a3xx_gpu_init(struct drm_device *dev);
+struct msm_gpu *a4xx_gpu_init(struct drm_device *dev);
+
#endif /* __ADRENO_GPU_H__ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index bcaf428..2c5bd76 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -1105,7 +1105,7 @@ static ssize_t dsi_host_transfer(struct mipi_dsi_host *host,
goto error_disable_clks;
}
- if (display->ctrl_count > 1) {
+ if (display->ctrl_count > 1 && !(msg->flags & MIPI_DSI_MSG_UNICAST)) {
rc = dsi_display_broadcast_cmd(display, msg);
if (rc) {
pr_err("[%s] cmd broadcast failed, rc=%d\n",
@@ -1113,7 +1113,10 @@ static ssize_t dsi_host_transfer(struct mipi_dsi_host *host,
goto error_disable_cmd_engine;
}
} else {
- rc = dsi_ctrl_cmd_transfer(display->ctrl[0].ctrl, msg,
+ int ctrl_idx = (msg->flags & MIPI_DSI_MSG_UNICAST) ?
+ msg->ctrl : 0;
+
+ rc = dsi_ctrl_cmd_transfer(display->ctrl[ctrl_idx].ctrl, msg,
DSI_CTRL_CMD_FIFO_STORE);
if (rc) {
pr_err("[%s] cmd transfer failed, rc=%d\n",
@@ -3027,6 +3030,12 @@ int dsi_display_prepare(struct dsi_display *display)
return rc;
}
+int dsi_display_pre_kickoff(struct dsi_display *display,
+ struct msm_display_kickoff_params *params)
+{
+ return 0;
+}
+
int dsi_display_enable(struct dsi_display *display)
{
int rc = 0;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
index cfbb14e..d2bc7d8 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
@@ -417,4 +417,14 @@ int dsi_display_set_backlight(void *display, u32 bl_lvl);
* Return: error code
*/
int dsi_display_soft_reset(void *display);
+
+/*
+ * dsi_display_pre_kickoff - program kickoff-time features
+ * @display: Pointer to private display structure
+ * @params: Parameters for kickoff-time programming
+ * Returns: Zero on success
+ */
+int dsi_display_pre_kickoff(struct dsi_display *display,
+ struct msm_display_kickoff_params *params);
+
#endif /* _DSI_DISPLAY_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
index 24a740b..556c0d8 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
@@ -475,6 +475,18 @@ enum drm_mode_status dsi_conn_mode_valid(struct drm_connector *connector,
return MODE_OK;
}
+int dsi_conn_pre_kickoff(struct drm_connector *connector,
+ void *display,
+ struct msm_display_kickoff_params *params)
+{
+ if (!connector || !display || !params) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ return dsi_display_pre_kickoff(display, params);
+}
+
struct dsi_bridge *dsi_drm_bridge_init(struct dsi_display *display,
struct drm_device *dev,
struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h
index 934899b..4339a11 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h
@@ -1,5 +1,5 @@
/*
- * 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
@@ -80,4 +80,15 @@ struct dsi_bridge *dsi_drm_bridge_init(struct dsi_display *display,
void dsi_drm_bridge_cleanup(struct dsi_bridge *bridge);
+/**
+ * dsi_display_pre_kickoff - program kickoff-time features
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display structure
+ * @params: Parameters for kickoff-time programming
+ * Returns: Zero on success
+ */
+int dsi_conn_pre_kickoff(struct drm_connector *connector,
+ void *display,
+ struct msm_display_kickoff_params *params);
+
#endif /* _DSI_DRM_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
index 57226ba..9f63089 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
@@ -109,6 +109,7 @@ struct dsi_panel_cmd_set {
enum dsi_cmd_set_type type;
enum dsi_cmd_set_state state;
u32 count;
+ int ctrl_idx;
struct dsi_cmd_desc *cmds;
};
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index ced015f..4e0b678 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -15,6 +15,27 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
#include <linux/of_address.h>
#include <linux/kthread.h>
@@ -335,8 +356,8 @@ static int get_mdp_ver(struct platform_device *pdev)
{
.compatible = "qcom,sde-kms",
.data = (void *)KMS_SDE,
- /* end node */
- } };
+ },
+ {} };
struct device *dev = &pdev->dev;
const struct of_device_id *match;
@@ -1331,6 +1352,109 @@ static int msm_release(struct inode *inode, struct file *filp)
return drm_release(inode, filp);
}
+/**
+ * msm_drv_framebuffer_remove - remove and unreference a framebuffer object
+ * @fb: framebuffer to remove
+ */
+void msm_drv_framebuffer_remove(struct drm_framebuffer *fb)
+{
+ struct drm_device *dev;
+
+ if (!fb)
+ return;
+
+ dev = fb->dev;
+
+ WARN_ON(!list_empty(&fb->filp_head));
+
+ drm_framebuffer_unreference(fb);
+}
+
+struct msm_drv_rmfb2_work {
+ struct work_struct work;
+ struct list_head fbs;
+};
+
+static void msm_drv_rmfb2_work_fn(struct work_struct *w)
+{
+ struct msm_drv_rmfb2_work *arg = container_of(w, typeof(*arg), work);
+
+ while (!list_empty(&arg->fbs)) {
+ struct drm_framebuffer *fb =
+ list_first_entry(&arg->fbs, typeof(*fb), filp_head);
+
+ list_del_init(&fb->filp_head);
+ msm_drv_framebuffer_remove(fb);
+ }
+}
+
+/**
+ * msm_ioctl_rmfb2 - remove an FB from the configuration
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Remove the FB specified by the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int msm_ioctl_rmfb2(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_framebuffer *fb = NULL;
+ struct drm_framebuffer *fbl = NULL;
+ uint32_t *id = data;
+ int found = 0;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return -EINVAL;
+
+ fb = drm_framebuffer_lookup(dev, *id);
+ if (!fb)
+ return -ENOENT;
+
+ /* drop extra ref from traversing drm_framebuffer_lookup */
+ drm_framebuffer_unreference(fb);
+
+ mutex_lock(&file_priv->fbs_lock);
+ list_for_each_entry(fbl, &file_priv->fbs, filp_head)
+ if (fb == fbl)
+ found = 1;
+ if (!found) {
+ mutex_unlock(&file_priv->fbs_lock);
+ return -ENOENT;
+ }
+
+ list_del_init(&fb->filp_head);
+ mutex_unlock(&file_priv->fbs_lock);
+
+ /*
+ * we now own the reference that was stored in the fbs list
+ *
+ * drm_framebuffer_remove may fail with -EINTR on pending signals,
+ * so run this in a separate stack as there's no way to correctly
+ * handle this after the fb is already removed from the lookup table.
+ */
+ if (drm_framebuffer_read_refcount(fb) > 1) {
+ struct msm_drv_rmfb2_work arg;
+
+ INIT_WORK_ONSTACK(&arg.work, msm_drv_rmfb2_work_fn);
+ INIT_LIST_HEAD(&arg.fbs);
+ list_add_tail(&fb->filp_head, &arg.fbs);
+
+ schedule_work(&arg.work);
+ flush_work(&arg.work);
+ destroy_work_on_stack(&arg.work);
+ } else
+ drm_framebuffer_unreference(fb);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_ioctl_rmfb2);
+
static const struct drm_ioctl_desc msm_ioctls[] = {
DRM_IOCTL_DEF_DRV(MSM_GET_PARAM, msm_ioctl_get_param, DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(MSM_GEM_NEW, msm_ioctl_gem_new, DRM_AUTH|DRM_RENDER_ALLOW),
@@ -1345,6 +1469,8 @@ static const struct drm_ioctl_desc msm_ioctls[] = {
DRM_UNLOCKED|DRM_CONTROL_ALLOW),
DRM_IOCTL_DEF_DRV(MSM_DEREGISTER_EVENT, msm_ioctl_deregister_event,
DRM_UNLOCKED|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF_DRV(MSM_RMFB2, msm_ioctl_rmfb2,
+ DRM_CONTROL_ALLOW|DRM_UNLOCKED),
};
static const struct vm_operations_struct vm_ops = {
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index da76fbc..4b263d3 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -363,6 +363,29 @@ struct msm_display_info {
struct msm_compression_info comp_info;
};
+#define MSM_MAX_ROI 4
+
+/**
+ * struct msm_roi_mapping - Regions of interest structure for mapping CRTC to
+ * Connector output
+ * @num_rects: number of valid rectangles in src and dst arrays
+ * @src: source roi rectangle
+ * @dst: destination roi rectangle
+ */
+struct msm_roi_mapping {
+ uint32_t num_rects;
+ struct drm_clip_rect src[MSM_MAX_ROI];
+ struct drm_clip_rect dst[MSM_MAX_ROI];
+};
+
+/**
+ * struct - msm_display_kickoff_params - info for display features at kickoff
+ * @rois: Regions of interest structure for mapping CRTC to Connector output
+ */
+struct msm_display_kickoff_params {
+ struct msm_roi_mapping *rois;
+};
+
/**
* struct msm_drm_event - defines custom event notification struct
* @base: base object required for event notification by DRM framework.
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index 1f39180..6e793d9 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -215,6 +215,38 @@ int sde_connector_get_info(struct drm_connector *connector,
return c_conn->ops.get_info(info, c_conn->display);
}
+int sde_connector_pre_kickoff(struct drm_connector *connector)
+{
+ struct sde_connector *c_conn;
+ struct sde_connector_state *c_state;
+ struct msm_display_kickoff_params params;
+ int rc;
+
+ if (!connector) {
+ SDE_ERROR("invalid argument\n");
+ return -EINVAL;
+ }
+
+ c_conn = to_sde_connector(connector);
+ c_state = to_sde_connector_state(connector->state);
+
+ if (!c_conn->display) {
+ SDE_ERROR("invalid argument\n");
+ return -EINVAL;
+ }
+
+ if (!c_conn->ops.pre_kickoff)
+ return 0;
+
+ params.rois = &c_state->rois;
+
+ SDE_EVT32_VERBOSE(connector->base.id);
+
+ rc = c_conn->ops.pre_kickoff(connector, c_conn->display, ¶ms);
+
+ return rc;
+}
+
static void sde_connector_destroy(struct drm_connector *connector)
{
struct sde_connector *c_conn;
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index 9d36851..70d4952 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -138,6 +138,18 @@ struct sde_connector_ops {
* Return: Zero on success, -ERROR otherwise
*/
int (*soft_reset)(void *display);
+
+ /**
+ * pre_kickoff - trigger display to program kickoff-time features
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display structure
+ * @params: Parameter bundle of connector-stored information for
+ * kickoff-time programming into the display
+ * Returns: Zero on success
+ */
+ int (*pre_kickoff)(struct drm_connector *connector,
+ void *display,
+ struct msm_display_kickoff_params *params);
};
/**
@@ -253,12 +265,15 @@ struct sde_connector {
* @out_fb: Pointer to output frame buffer, if applicable
* @mmu_id: MMU ID for accessing frame buffer objects, if applicable
* @property_values: Local cache of current connector property values
+ * @rois: Regions of interest structure for mapping CRTC to Connector output
*/
struct sde_connector_state {
struct drm_connector_state base;
struct drm_framebuffer *out_fb;
int mmu_id;
uint64_t property_values[CONNECTOR_PROP_COUNT];
+
+ struct msm_roi_mapping rois;
};
/**
@@ -403,5 +418,12 @@ void sde_connector_unregister_event(struct drm_connector *connector,
int sde_connector_register_custom_event(struct sde_kms *kms,
struct drm_connector *conn_drm, u32 event, bool en);
+/**
+ * sde_connector_pre_kickoff - trigger kickoff time feature programming
+ * @connector: Pointer to drm connector object
+ * Returns: Zero on success
+ */
+int sde_connector_pre_kickoff(struct drm_connector *connector);
+
#endif /* _SDE_CONNECTOR_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.c b/drivers/gpu/drm/msm/sde/sde_core_irq.c
index 71a8bdf..5adef2d 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.c
@@ -320,7 +320,7 @@ static int sde_debugfs_core_irq_show(struct seq_file *s, void *v)
DEFINE_SDE_DEBUGFS_SEQ_FOPS(sde_debugfs_core_irq);
-static int sde_debugfs_core_irq_init(struct sde_kms *sde_kms,
+int sde_debugfs_core_irq_init(struct sde_kms *sde_kms,
struct dentry *parent)
{
sde_kms->irq_obj.debugfs_file = debugfs_create_file("core_irq", 0644,
@@ -330,20 +330,20 @@ static int sde_debugfs_core_irq_init(struct sde_kms *sde_kms,
return 0;
}
-static void sde_debugfs_core_irq_destroy(struct sde_kms *sde_kms)
+void sde_debugfs_core_irq_destroy(struct sde_kms *sde_kms)
{
debugfs_remove(sde_kms->irq_obj.debugfs_file);
sde_kms->irq_obj.debugfs_file = NULL;
}
#else
-static int sde_debugfs_core_irq_init(struct sde_kms *sde_kms,
+int sde_debugfs_core_irq_init(struct sde_kms *sde_kms,
struct dentry *parent)
{
return 0;
}
-static void sde_debugfs_core_irq_destroy(struct sde_kms *sde_kms)
+void sde_debugfs_core_irq_destroy(struct sde_kms *sde_kms)
{
}
#endif
@@ -385,8 +385,6 @@ void sde_core_irq_preinstall(struct sde_kms *sde_kms)
atomic_set(&sde_kms->irq_obj.enable_counts[i], 0);
atomic_set(&sde_kms->irq_obj.irq_counts[i], 0);
}
-
- sde_debugfs_core_irq_init(sde_kms, sde_debugfs_get_root(sde_kms));
}
int sde_core_irq_postinstall(struct sde_kms *sde_kms)
@@ -411,8 +409,6 @@ void sde_core_irq_uninstall(struct sde_kms *sde_kms)
}
priv = sde_kms->dev->dev_private;
- sde_debugfs_core_irq_destroy(sde_kms);
-
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true);
for (i = 0; i < sde_kms->irq_obj.total_irqs; i++)
if (atomic_read(&sde_kms->irq_obj.enable_counts[i]) ||
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.h b/drivers/gpu/drm/msm/sde/sde_core_irq.h
index 92642e7..64f4160 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.h
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.h
@@ -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
@@ -135,4 +135,19 @@ int sde_core_irq_unregister_callback(
int irq_idx,
struct sde_irq_callback *irq_cb);
+/**
+ * sde_debugfs_core_irq_init - register core irq debugfs
+ * @sde_kms: pointer to kms
+ * @parent: debugfs directory root
+ * @Return: 0 on success
+ */
+int sde_debugfs_core_irq_init(struct sde_kms *sde_kms,
+ struct dentry *parent);
+
+/**
+ * sde_debugfs_core_irq_destroy - deregister core irq debugfs
+ * @sde_kms: pointer to kms
+ */
+void sde_debugfs_core_irq_destroy(struct sde_kms *sde_kms);
+
#endif /* __SDE_CORE_IRQ_H__ */
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 54acf41a..e7a867f 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -602,10 +602,9 @@ static void _sde_crtc_setup_dim_layer_cfg(struct drm_crtc *crtc,
struct sde_crtc *sde_crtc, struct sde_crtc_mixer *mixer,
struct sde_hw_dim_layer *dim_layer)
{
+ struct sde_crtc_state *cstate;
struct sde_hw_mixer *lm;
- struct sde_rect mixer_rect;
struct sde_hw_dim_layer split_dim_layer;
- u32 mixer_width, mixer_height;
int i;
if (!dim_layer->rect.w || !dim_layer->rect.h) {
@@ -613,9 +612,7 @@ static void _sde_crtc_setup_dim_layer_cfg(struct drm_crtc *crtc,
return;
}
- mixer_width = get_crtc_split_width(crtc);
- mixer_height = get_crtc_mixer_height(crtc);
- mixer_rect = (struct sde_rect) {0, 0, mixer_width, mixer_height};
+ cstate = to_sde_crtc_state(crtc->state);
split_dim_layer.stage = dim_layer->stage;
split_dim_layer.color_fill = dim_layer->color_fill;
@@ -626,17 +623,15 @@ static void _sde_crtc_setup_dim_layer_cfg(struct drm_crtc *crtc,
*/
for (i = 0; i < sde_crtc->num_mixers; i++) {
split_dim_layer.flags = dim_layer->flags;
- mixer_rect.x = i * mixer_width;
- sde_kms_rect_intersect(&split_dim_layer.rect, &mixer_rect,
- &dim_layer->rect);
- if (!split_dim_layer.rect.w && !split_dim_layer.rect.h) {
+ sde_kms_rect_intersect(&cstate->lm_bounds[i], &dim_layer->rect,
+ &split_dim_layer.rect);
+ if (sde_kms_rect_is_null(&split_dim_layer.rect)) {
/*
* no extra programming required for non-intersecting
* layer mixers with INCLUSIVE dim layer
*/
- if (split_dim_layer.flags
- & SDE_DRM_DIM_LAYER_INCLUSIVE)
+ if (split_dim_layer.flags & SDE_DRM_DIM_LAYER_INCLUSIVE)
continue;
/*
@@ -646,12 +641,13 @@ static void _sde_crtc_setup_dim_layer_cfg(struct drm_crtc *crtc,
*/
split_dim_layer.flags &= ~SDE_DRM_DIM_LAYER_EXCLUSIVE;
split_dim_layer.flags |= SDE_DRM_DIM_LAYER_INCLUSIVE;
- split_dim_layer.rect = (struct sde_rect) {0, 0,
- mixer_width, mixer_height};
+ memcpy(&split_dim_layer.rect, &cstate->lm_bounds[i],
+ sizeof(split_dim_layer.rect));
} else {
- split_dim_layer.rect.x = split_dim_layer.rect.x
- - (i * mixer_width);
+ split_dim_layer.rect.x =
+ split_dim_layer.rect.x -
+ cstate->lm_bounds[i].w;
}
lm = mixer[i].hw_lm;
@@ -660,6 +656,32 @@ static void _sde_crtc_setup_dim_layer_cfg(struct drm_crtc *crtc,
}
}
+static void _sde_crtc_program_lm_output_roi(struct drm_crtc *crtc)
+{
+ struct sde_crtc *sde_crtc;
+ struct sde_crtc_state *crtc_state;
+ int lm_idx, lm_horiz_position;
+
+ sde_crtc = to_sde_crtc(crtc);
+ crtc_state = to_sde_crtc_state(crtc->state);
+
+ lm_horiz_position = 0;
+ for (lm_idx = 0; lm_idx < sde_crtc->num_mixers; lm_idx++) {
+ const struct sde_rect *lm_roi = &crtc_state->lm_bounds[lm_idx];
+ struct sde_hw_mixer *hw_lm = sde_crtc->mixers[lm_idx].hw_lm;
+ struct sde_hw_mixer_cfg cfg;
+
+ if (sde_kms_rect_is_null(lm_roi))
+ continue;
+
+ cfg.out_width = lm_roi->w;
+ cfg.out_height = lm_roi->h;
+ cfg.right_mixer = lm_horiz_position++;
+ cfg.flags = 0;
+ hw_lm->ops.setup_mixer_out(hw_lm, &cfg);
+ }
+}
+
static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
struct sde_crtc *sde_crtc, struct sde_crtc_mixer *mixer)
{
@@ -672,13 +694,12 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
struct sde_hw_ctl *ctl;
struct sde_hw_mixer *lm;
struct sde_hw_stage_cfg *stage_cfg;
+ struct sde_rect plane_crtc_roi;
- u32 flush_mask = 0, crtc_split_width;
- uint32_t lm_idx = LEFT_MIXER, idx;
+ u32 flush_mask = 0;
+ uint32_t lm_idx = LEFT_MIXER, stage_idx;
bool bg_alpha_enable[CRTC_DUAL_MIXERS] = {false};
- bool lm_right = false;
- int left_crtc_zpos_cnt[SDE_STAGE_MAX + 1] = {0};
- int right_crtc_zpos_cnt[SDE_STAGE_MAX + 1] = {0};
+ int zpos_cnt[CRTC_DUAL_MIXERS][SDE_STAGE_MAX + 1] = { {0} };
int i;
bool sbuf_mode = false;
u32 prefill = 0;
@@ -691,13 +712,18 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
ctl = mixer->hw_ctl;
lm = mixer->hw_lm;
stage_cfg = &sde_crtc->stage_cfg;
- crtc_split_width = get_crtc_split_width(crtc);
+ cstate = to_sde_crtc_state(crtc->state);
drm_atomic_crtc_for_each_plane(plane, crtc) {
state = plane->state;
if (!state)
continue;
+ plane_crtc_roi.x = state->crtc_x;
+ plane_crtc_roi.y = state->crtc_y;
+ plane_crtc_roi.w = state->crtc_w;
+ plane_crtc_roi.h = state->crtc_h;
+
pstate = to_sde_plane_state(state);
fb = state->fb;
@@ -706,25 +732,6 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
sde_plane_get_ctl_flush(plane, ctl, &flush_mask);
- /* always stage plane on either left or right lm */
- if (state->crtc_x >= crtc_split_width) {
- lm_idx = RIGHT_MIXER;
- idx = right_crtc_zpos_cnt[pstate->stage]++;
- } else {
- lm_idx = LEFT_MIXER;
- idx = left_crtc_zpos_cnt[pstate->stage]++;
- }
-
- /* stage plane on right LM if it crosses the boundary */
- lm_right = (lm_idx == LEFT_MIXER) &&
- (state->crtc_x + state->crtc_w > crtc_split_width);
-
- stage_cfg->stage[lm_idx][pstate->stage][idx] =
- sde_plane_pipe(plane);
- stage_cfg->multirect_index
- [lm_idx][pstate->stage][idx] =
- pstate->multirect_index;
- mixer[lm_idx].flush_mask |= flush_mask;
SDE_DEBUG("crtc %d stage:%d - plane %d sspp %d fb %d\n",
crtc->base.id,
@@ -738,47 +745,46 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
SDE_EVT32(DRMID(plane), state->src_x, state->src_y,
state->src_w >> 16, state->src_h >> 16, state->crtc_x,
state->crtc_y, state->crtc_w, state->crtc_h);
- SDE_EVT32(DRMID(plane), DRMID(crtc), lm_idx, lm_right,
- pstate->stage, pstate->multirect_index,
- pstate->multirect_mode, format->base.pixel_format,
- fb ? fb->modifier[0] : 0);
- /* blend config update */
- if (pstate->stage != SDE_STAGE_BASE) {
- _sde_crtc_setup_blend_cfg(mixer + lm_idx, pstate,
- format);
+ for (lm_idx = 0; lm_idx < sde_crtc->num_mixers; lm_idx++) {
+ struct sde_rect intersect;
- if (bg_alpha_enable[lm_idx] && !format->alpha_enable)
- mixer[lm_idx].mixer_op_mode = 0;
- else
- mixer[lm_idx].mixer_op_mode |=
- 1 << pstate->stage;
- } else if (format->alpha_enable) {
- bg_alpha_enable[lm_idx] = true;
- }
+ /* skip if the roi doesn't fall within LM's bounds */
+ sde_kms_rect_intersect(&plane_crtc_roi,
+ &cstate->lm_bounds[lm_idx],
+ &intersect);
+ if (sde_kms_rect_is_null(&intersect))
+ continue;
- if (lm_right) {
- idx = right_crtc_zpos_cnt[pstate->stage]++;
- stage_cfg->stage[RIGHT_MIXER][pstate->stage][idx] =
- sde_plane_pipe(plane);
+ stage_idx = zpos_cnt[lm_idx][pstate->stage]++;
+ stage_cfg->stage[lm_idx][pstate->stage][stage_idx] =
+ sde_plane_pipe(plane);
stage_cfg->multirect_index
- [RIGHT_MIXER][pstate->stage][idx] =
- pstate->multirect_index;
- mixer[RIGHT_MIXER].flush_mask |= flush_mask;
+ [lm_idx][pstate->stage][stage_idx] =
+ pstate->multirect_index;
+
+ mixer[lm_idx].flush_mask |= flush_mask;
+
+
+ SDE_EVT32(DRMID(plane), DRMID(crtc), lm_idx, stage_idx,
+ pstate->stage, pstate->multirect_index,
+ pstate->multirect_mode,
+ format->base.pixel_format,
+ fb ? fb->modifier[0] : 0);
/* blend config update */
if (pstate->stage != SDE_STAGE_BASE) {
- _sde_crtc_setup_blend_cfg(mixer + RIGHT_MIXER,
- pstate, format);
+ _sde_crtc_setup_blend_cfg(mixer + lm_idx,
+ pstate, format);
- if (bg_alpha_enable[RIGHT_MIXER] &&
+ if (bg_alpha_enable[lm_idx] &&
!format->alpha_enable)
- mixer[RIGHT_MIXER].mixer_op_mode = 0;
+ mixer[lm_idx].mixer_op_mode = 0;
else
- mixer[RIGHT_MIXER].mixer_op_mode |=
+ mixer[lm_idx].mixer_op_mode |=
1 << pstate->stage;
} else if (format->alpha_enable) {
- bg_alpha_enable[RIGHT_MIXER] = true;
+ bg_alpha_enable[lm_idx] = true;
}
}
}
@@ -804,6 +810,8 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
ctl->ops.setup_sbuf_cfg(ctl, &cstate->sbuf_cfg);
}
+
+ _sde_crtc_program_lm_output_roi(crtc);
}
/**
@@ -1296,9 +1304,44 @@ static void _sde_crtc_setup_mixers(struct drm_crtc *crtc)
_sde_crtc_setup_mixer_for_encoder(crtc, enc);
}
+
mutex_unlock(&sde_crtc->crtc_lock);
}
+static void _sde_crtc_setup_lm_bounds(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ struct sde_crtc *sde_crtc;
+ struct sde_crtc_state *cstate;
+ struct drm_display_mode *adj_mode;
+ u32 crtc_split_width;
+ int i;
+
+ if (!crtc || !state) {
+ SDE_ERROR("invalid args\n");
+ return;
+ }
+
+ sde_crtc = to_sde_crtc(crtc);
+ cstate = to_sde_crtc_state(state);
+
+ adj_mode = &state->adjusted_mode;
+ crtc_split_width = sde_crtc_mixer_width(sde_crtc, adj_mode);
+
+ for (i = 0; i < sde_crtc->num_mixers; i++) {
+ struct sde_rect *lm_bound = &cstate->lm_bounds[i];
+
+ lm_bound->x = crtc_split_width * i;
+ lm_bound->y = 0;
+ lm_bound->w = crtc_split_width;
+ lm_bound->h = adj_mode->vdisplay;
+ SDE_EVT32(DRMID(crtc), i, lm_bound->x, lm_bound->y,
+ lm_bound->w, lm_bound->h);
+ }
+
+ drm_mode_debug_printmodeline(adj_mode);
+}
+
static void sde_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
@@ -1326,6 +1369,8 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc,
if (!sde_crtc->num_mixers)
_sde_crtc_setup_mixers(crtc);
+ _sde_crtc_setup_lm_bounds(crtc, crtc->state);
+
if (sde_crtc->event) {
WARN_ON(sde_crtc->event);
} else {
@@ -1776,13 +1821,10 @@ static void sde_crtc_disable(struct drm_crtc *crtc)
static void sde_crtc_enable(struct drm_crtc *crtc)
{
struct sde_crtc *sde_crtc;
- struct sde_crtc_mixer *mixer;
- struct sde_hw_mixer *lm;
- struct drm_display_mode *mode;
struct drm_encoder *encoder;
unsigned long flags;
struct sde_crtc_irq_info *node = NULL;
- int i, ret;
+ int ret;
if (!crtc) {
SDE_ERROR("invalid crtc\n");
@@ -1791,16 +1833,7 @@ static void sde_crtc_enable(struct drm_crtc *crtc)
SDE_DEBUG("crtc%d\n", crtc->base.id);
SDE_EVT32(DRMID(crtc));
-
sde_crtc = to_sde_crtc(crtc);
- mixer = sde_crtc->mixers;
-
- if (WARN_ON(!crtc->state))
- return;
-
- mode = &crtc->state->adjusted_mode;
-
- drm_mode_debug_printmodeline(mode);
drm_for_each_encoder(encoder, crtc->dev) {
if (encoder->crtc != crtc)
@@ -1809,15 +1842,6 @@ static void sde_crtc_enable(struct drm_crtc *crtc)
sde_crtc_frame_event_cb, (void *)crtc);
}
- for (i = 0; i < sde_crtc->num_mixers; i++) {
- lm = mixer[i].hw_lm;
- lm->cfg.out_width = sde_crtc_mixer_width(sde_crtc, mode);
- lm->cfg.out_height = mode->vdisplay;
- lm->cfg.right_mixer = (i == 0) ? false : true;
- lm->cfg.flags = 0;
- lm->ops.setup_mixer_out(lm, &lm->cfg);
- }
-
spin_lock_irqsave(&sde_crtc->spin_lock, flags);
list_for_each_entry(node, &sde_crtc->user_event_list, list) {
ret = 0;
@@ -1894,6 +1918,8 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
mixer_width = sde_crtc_mixer_width(sde_crtc, mode);
+ _sde_crtc_setup_lm_bounds(crtc, state);
+
/* get plane state for all drm planes associated with crtc state */
drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
if (IS_ERR_OR_NULL(pstate)) {
@@ -2652,7 +2678,7 @@ static int _sde_crtc_init_debugfs(struct drm_crtc *crtc)
return -EINVAL;
sde_crtc->debugfs_root = debugfs_create_dir(sde_crtc->name,
- sde_debugfs_get_root(sde_kms));
+ crtc->dev->primary->debugfs_root);
if (!sde_crtc->debugfs_root)
return -ENOMEM;
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index 19ae27f..36231d4 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -108,7 +108,7 @@ struct sde_crtc_event {
* @name : ASCII description of this crtc
* @num_ctls : Number of ctl paths in use
* @num_mixers : Number of mixers in use
- * @mixer : List of active mixers
+ * @mixers : List of active mixers
* @event : Pointer to last received drm vblank event. If there is a
* pending vblank event, this will be non-null.
* @vsync_count : Running count of received vsync events
@@ -248,6 +248,8 @@ struct sde_crtc_respool {
* @num_connectors: Number of associated drm connectors
* @intf_mode : Interface mode of the primary connector
* @rsc_client : sde rsc client when mode is valid
+ * @lm_bounds : LM boundaries based on current mode full resolution, no ROI.
+ * Origin top left of CRTC.
* @property_values: Current crtc property values
* @input_fence_timeout_ns : Cached input fence timeout, in ns
* @property_blobs: Reference pointers for blob properties
@@ -267,6 +269,8 @@ struct sde_crtc_state {
struct sde_rsc_client *rsc_client;
bool rsc_update;
+ struct sde_rect lm_bounds[CRTC_DUAL_MIXERS];
+
uint64_t property_values[CRTC_PROP_COUNT];
uint64_t input_fence_timeout_ns;
struct drm_property_blob *property_blobs[CRTC_PROP_COUNT];
@@ -303,30 +307,6 @@ static inline int sde_crtc_mixer_width(struct sde_crtc *sde_crtc,
mode->hdisplay / CRTC_DUAL_MIXERS : mode->hdisplay;
}
-static inline uint32_t get_crtc_split_width(struct drm_crtc *crtc)
-{
- struct drm_display_mode *mode;
- struct sde_crtc *sde_crtc;
-
- if (!crtc || !crtc->state)
- return 0;
-
- sde_crtc = to_sde_crtc(crtc);
- mode = &crtc->state->adjusted_mode;
- return sde_crtc_mixer_width(sde_crtc, mode);
-}
-
-static inline uint32_t get_crtc_mixer_height(struct drm_crtc *crtc)
-{
- struct drm_display_mode *mode;
-
- if (!crtc || !crtc->state)
- return 0;
-
- mode = &crtc->state->adjusted_mode;
- return mode->vdisplay;
-}
-
/**
* sde_crtc_vblank - enable or disable vblanks for this crtc
* @crtc: Pointer to drm crtc object
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 20d5e52..67fb783 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -1241,6 +1241,7 @@ void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc,
struct sde_encoder_phys *phys;
bool needs_hw_reset = false;
unsigned int i;
+ int rc;
if (!drm_enc) {
SDE_ERROR("invalid encoder\n");
@@ -1270,6 +1271,14 @@ void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc,
phys->ops.hw_reset(phys);
}
}
+
+ if (sde_enc->cur_master && sde_enc->cur_master->connector) {
+ rc = sde_connector_pre_kickoff(sde_enc->cur_master->connector);
+ if (rc)
+ SDE_ERROR_ENC(sde_enc, "kickoff conn%d failed rc %d\n",
+ sde_enc->cur_master->connector->base.id,
+ rc);
+ }
}
void sde_encoder_kickoff(struct drm_encoder *drm_enc)
@@ -1530,6 +1539,7 @@ static int _sde_encoder_init_debugfs(struct drm_encoder *drm_enc)
struct sde_encoder_virt *sde_enc;
struct msm_drm_private *priv;
struct sde_kms *sde_kms;
+ int i;
static const struct file_operations debugfs_status_fops = {
.open = _sde_encoder_debugfs_status_open,
@@ -1559,7 +1569,7 @@ static int _sde_encoder_init_debugfs(struct drm_encoder *drm_enc)
/* create overall sub-directory for the encoder */
sde_enc->debugfs_root = debugfs_create_dir(name,
- sde_debugfs_get_root(sde_kms));
+ drm_enc->dev->primary->debugfs_root);
if (!sde_enc->debugfs_root)
return -ENOMEM;
@@ -1570,6 +1580,13 @@ static int _sde_encoder_init_debugfs(struct drm_encoder *drm_enc)
debugfs_create_file("misr_data", 0644,
sde_enc->debugfs_root, sde_enc, &debugfs_misr_fops);
+ for (i = 0; i < sde_enc->num_phys_encs; i++)
+ if (sde_enc->phys_encs[i] &&
+ sde_enc->phys_encs[i]->ops.late_register)
+ sde_enc->phys_encs[i]->ops.late_register(
+ sde_enc->phys_encs[i],
+ sde_enc->debugfs_root);
+
return 0;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
index a68da4e..5b59828 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -135,8 +135,7 @@ static void sde_encoder_phys_cmd_pp_rd_ptr_irq(void *arg, int irq_idx)
phys_enc);
}
-static bool _sde_encoder_phys_is_ppsplit_slave(
- struct sde_encoder_phys *phys_enc)
+static bool _sde_encoder_phys_is_ppsplit(struct sde_encoder_phys *phys_enc)
{
enum sde_rm_topology_name topology;
@@ -144,8 +143,7 @@ static bool _sde_encoder_phys_is_ppsplit_slave(
return false;
topology = sde_connector_get_topology_name(phys_enc->connector);
- if (topology == SDE_RM_TOPOLOGY_PPSPLIT &&
- phys_enc->split_role == ENC_ROLE_SLAVE)
+ if (topology == SDE_RM_TOPOLOGY_PPSPLIT)
return true;
return false;
@@ -199,6 +197,16 @@ static int _sde_encoder_phys_cmd_handle_ppdone_timeout(
return -ETIMEDOUT;
}
+static bool _sde_encoder_phys_is_ppsplit_slave(
+ struct sde_encoder_phys *phys_enc)
+{
+ if (!phys_enc)
+ return false;
+
+ return _sde_encoder_phys_is_ppsplit(phys_enc) &&
+ phys_enc->split_role == ENC_ROLE_SLAVE;
+}
+
static int _sde_encoder_phys_cmd_wait_for_idle(
struct sde_encoder_phys *phys_enc)
{
@@ -463,13 +471,10 @@ static void sde_encoder_phys_cmd_pingpong_config(
static bool sde_encoder_phys_cmd_needs_single_flush(
struct sde_encoder_phys *phys_enc)
{
- enum sde_rm_topology_name topology;
-
if (!phys_enc)
return false;
- topology = sde_connector_get_topology_name(phys_enc->connector);
- return topology == SDE_RM_TOPOLOGY_PPSPLIT;
+ return _sde_encoder_phys_is_ppsplit(phys_enc);
}
static int sde_encoder_phys_cmd_control_vblank_irq(
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 7e18a0e..ba773b0 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -302,8 +302,9 @@ static int _sde_debugfs_init(struct sde_kms *sde_kms)
/* allow debugfs_root to be NULL */
debugfs_create_x32(SDE_DEBUGFS_HWMASKNAME, 0644, debugfs_root, p);
- sde_debugfs_danger_init(sde_kms, debugfs_root);
- sde_debugfs_vbif_init(sde_kms, debugfs_root);
+ (void) sde_debugfs_danger_init(sde_kms, debugfs_root);
+ (void) sde_debugfs_vbif_init(sde_kms, debugfs_root);
+ (void) sde_debugfs_core_irq_init(sde_kms, debugfs_root);
rc = sde_core_perf_debugfs_init(&sde_kms->perf, debugfs_root);
if (rc) {
@@ -320,6 +321,7 @@ static void _sde_debugfs_destroy(struct sde_kms *sde_kms)
if (sde_kms) {
sde_debugfs_vbif_destroy(sde_kms);
sde_debugfs_danger_destroy(sde_kms);
+ sde_debugfs_core_irq_destroy(sde_kms);
}
}
#else
@@ -549,7 +551,8 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
.mode_valid = dsi_conn_mode_valid,
.get_info = dsi_display_get_info,
.set_backlight = dsi_display_set_backlight,
- .soft_reset = dsi_display_soft_reset
+ .soft_reset = dsi_display_soft_reset,
+ .pre_kickoff = dsi_conn_pre_kickoff
};
static const struct sde_connector_ops wb_ops = {
.post_init = sde_wb_connector_post_init,
@@ -1512,7 +1515,7 @@ static int sde_kms_hw_init(struct msm_kms *kms)
}
rc = sde_core_perf_init(&sde_kms->perf, dev, sde_kms->catalog,
- &priv->phandle, priv->pclient, "core_clk_src");
+ &priv->phandle, priv->pclient, "core_clk");
if (rc) {
SDE_ERROR("failed to init perf %d\n", rc);
goto perf_err;
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index ebc277e..d38a6b9 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -397,13 +397,47 @@ void sde_kms_info_append_format(struct sde_kms_info *info,
void sde_kms_info_stop(struct sde_kms_info *info);
/**
- * sde_kms_rect_intersect() - find the intersecting region between two rects
- * @res: Intersecting region between the two rectangles
- * @rect1: first rectangle coordinates
- * @rect2: second rectangle coordinates
+ * sde_kms_rect_intersect - intersect two rectangles
+ * @r1: first rectangle
+ * @r2: scissor rectangle
+ * @result: result rectangle, all 0's on no intersection found
*/
-void sde_kms_rect_intersect(struct sde_rect *res,
- const struct sde_rect *rect1, const struct sde_rect *rect2);
+void sde_kms_rect_intersect(const struct sde_rect *r1,
+ const struct sde_rect *r2,
+ struct sde_rect *result);
+
+/**
+ * sde_kms_rect_is_equal - compares two rects
+ * @r1: rect value to compare
+ * @r2: rect value to compare
+ *
+ * Returns 1 if the rects are same, 0 otherwise.
+ */
+static inline bool sde_kms_rect_is_equal(struct sde_rect *r1,
+ struct sde_rect *r2)
+{
+ if ((!r1 && r2) || (r1 && !r2))
+ return false;
+
+ if (!r1 && !r2)
+ return true;
+
+ return r1->x == r2->x && r1->y == r2->y && r1->w == r2->w &&
+ r1->h == r2->h;
+}
+
+/**
+ * sde_kms_rect_is_null - returns true if the width or height of a rect is 0
+ * @rect: rectangle to check for zero size
+ * @Return: True if width or height of rectangle is 0
+ */
+static inline bool sde_kms_rect_is_null(const struct sde_rect *r)
+{
+ if (!r)
+ return true;
+
+ return (!r->w || !r->h);
+}
/**
* Vblank enable/disable functions
diff --git a/drivers/gpu/drm/msm/sde/sde_kms_utils.c b/drivers/gpu/drm/msm/sde/sde_kms_utils.c
index f95f5df..30e12c9 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms_utils.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms_utils.c
@@ -152,18 +152,26 @@ void sde_kms_info_stop(struct sde_kms_info *info)
}
}
-void sde_kms_rect_intersect(struct sde_rect *res,
- const struct sde_rect *rect1, const struct sde_rect *rect2)
+void sde_kms_rect_intersect(const struct sde_rect *r1,
+ const struct sde_rect *r2,
+ struct sde_rect *result)
{
int l, t, r, b;
- l = max(rect1->x, rect2->x);
- t = max(rect1->y, rect2->y);
- r = min((rect1->x + rect1->w), (rect2->x + rect2->w));
- b = min((rect1->y + rect1->h), (rect2->y + rect2->h));
+ if (!r1 || !r2 || !result)
+ return;
- if (r < l || b < t)
- *res = (struct sde_rect) {0, 0, 0, 0};
- else
- *res = (struct sde_rect) {l, t, (r - l), (b - t)};
+ l = max(r1->x, r2->x);
+ t = max(r1->y, r2->y);
+ r = min((r1->x + r1->w), (r2->x + r2->w));
+ b = min((r1->y + r1->h), (r2->y + r2->h));
+
+ if (r < l || b < t) {
+ memset(result, 0, sizeof(*result));
+ } else {
+ result->x = l;
+ result->y = t;
+ result->w = r - l;
+ result->h = b - t;
+ }
}
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index e8892fb..bd6b302 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -2853,7 +2853,7 @@ static int sde_plane_sspp_atomic_check(struct drm_plane *plane,
* Cropping is not required as hardware will consider only the
* intersecting region with the src rect.
*/
- sde_kms_rect_intersect(&intersect, &src, &pstate->excl_rect);
+ sde_kms_rect_intersect(&src, &pstate->excl_rect, &intersect);
if (!intersect.w || !intersect.h || SDE_FORMAT_IS_YUV(fmt)) {
SDE_ERROR_PLANE(psde,
"invalid excl_rect:{%d,%d,%d,%d} src:{%d,%d,%d,%d}, fmt: %4.4s\n",
@@ -3734,7 +3734,7 @@ static int _sde_plane_init_debugfs(struct drm_plane *plane)
/* create overall sub-directory for the pipe */
psde->debugfs_root =
debugfs_create_dir(psde->pipe_name,
- sde_debugfs_get_root(kms));
+ plane->dev->primary->debugfs_root);
if (!psde->debugfs_root)
return -ENOMEM;
@@ -3953,7 +3953,7 @@ struct drm_plane *sde_plane_init(struct drm_device *dev,
if (master_plane_id)
format_list = plane_formats;
- psde->nformats = sde_populate_formats(plane_formats,
+ psde->nformats = sde_populate_formats(format_list,
psde->formats,
0,
ARRAY_SIZE(psde->formats));
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c
index 32c0584..6e6c59a 100644
--- a/drivers/gpu/drm/sun4i/sun4i_backend.c
+++ b/drivers/gpu/drm/sun4i/sun4i_backend.c
@@ -408,6 +408,7 @@ static int sun4i_backend_remove(struct platform_device *pdev)
static const struct of_device_id sun4i_backend_of_table[] = {
{ .compatible = "allwinner,sun5i-a13-display-backend" },
+ { .compatible = "allwinner,sun6i-a31-display-backend" },
{ .compatible = "allwinner,sun8i-a33-display-backend" },
{ }
};
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index 70e9fd5..c3b2186 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -201,12 +201,15 @@ static const struct component_master_ops sun4i_drv_master_ops = {
static bool sun4i_drv_node_is_frontend(struct device_node *node)
{
return of_device_is_compatible(node, "allwinner,sun5i-a13-display-frontend") ||
+ of_device_is_compatible(node, "allwinner,sun6i-a31-display-frontend") ||
of_device_is_compatible(node, "allwinner,sun8i-a33-display-frontend");
}
static bool sun4i_drv_node_is_tcon(struct device_node *node)
{
return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon") ||
+ of_device_is_compatible(node, "allwinner,sun6i-a31-tcon") ||
+ of_device_is_compatible(node, "allwinner,sun6i-a31s-tcon") ||
of_device_is_compatible(node, "allwinner,sun8i-a33-tcon");
}
@@ -322,6 +325,8 @@ static int sun4i_drv_remove(struct platform_device *pdev)
static const struct of_device_id sun4i_drv_of_table[] = {
{ .compatible = "allwinner,sun5i-a13-display-engine" },
+ { .compatible = "allwinner,sun6i-a31-display-engine" },
+ { .compatible = "allwinner,sun6i-a31s-display-engine" },
{ .compatible = "allwinner,sun8i-a33-display-engine" },
{ }
};
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index cadacb5..c6afb24 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -20,6 +20,7 @@
#include <linux/component.h>
#include <linux/ioport.h>
#include <linux/of_address.h>
+#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/of_irq.h>
#include <linux/regmap.h>
@@ -62,7 +63,7 @@ void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel)
return;
}
- WARN_ON(!tcon->has_channel_1);
+ WARN_ON(!tcon->quirks->has_channel_1);
regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
SUN4I_TCON1_CTL_TCON_ENABLE, 0);
clk_disable_unprepare(tcon->sclk1);
@@ -80,7 +81,7 @@ void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel)
return;
}
- WARN_ON(!tcon->has_channel_1);
+ WARN_ON(!tcon->quirks->has_channel_1);
regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
SUN4I_TCON1_CTL_TCON_ENABLE,
SUN4I_TCON1_CTL_TCON_ENABLE);
@@ -202,7 +203,7 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
u8 clk_delay;
u32 val;
- WARN_ON(!tcon->has_channel_1);
+ WARN_ON(!tcon->quirks->has_channel_1);
/* Adjust clock delay */
clk_delay = sun4i_tcon_get_clk_delay(mode, 1);
@@ -266,7 +267,7 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
/*
* FIXME: Undocumented bits
*/
- if (tcon->has_mux)
+ if (tcon->quirks->has_unknown_mux)
regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, 1);
}
EXPORT_SYMBOL(sun4i_tcon1_mode_set);
@@ -327,7 +328,7 @@ static int sun4i_tcon_init_clocks(struct device *dev,
return PTR_ERR(tcon->sclk0);
}
- if (tcon->has_channel_1) {
+ if (tcon->quirks->has_channel_1) {
tcon->sclk1 = devm_clk_get(dev, "tcon-ch1");
if (IS_ERR(tcon->sclk1)) {
dev_err(dev, "Couldn't get the TCON channel 1 clock\n");
@@ -487,14 +488,7 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
drv->tcon = tcon;
tcon->drm = drm;
tcon->dev = dev;
-
- if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a13-tcon")) {
- tcon->has_mux = true;
- tcon->has_channel_1 = true;
- } else {
- tcon->has_mux = false;
- tcon->has_channel_1 = false;
- }
+ tcon->quirks = of_device_get_match_data(dev);
tcon->lcd_rst = devm_reset_control_get(dev, "lcd");
if (IS_ERR(tcon->lcd_rst)) {
@@ -588,9 +582,28 @@ static int sun4i_tcon_remove(struct platform_device *pdev)
return 0;
}
+static const struct sun4i_tcon_quirks sun5i_a13_quirks = {
+ .has_unknown_mux = true,
+ .has_channel_1 = true,
+};
+
+static const struct sun4i_tcon_quirks sun6i_a31_quirks = {
+ .has_channel_1 = true,
+};
+
+static const struct sun4i_tcon_quirks sun6i_a31s_quirks = {
+ .has_channel_1 = true,
+};
+
+static const struct sun4i_tcon_quirks sun8i_a33_quirks = {
+ /* nothing is supported */
+};
+
static const struct of_device_id sun4i_tcon_of_table[] = {
- { .compatible = "allwinner,sun5i-a13-tcon" },
- { .compatible = "allwinner,sun8i-a33-tcon" },
+ { .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks },
+ { .compatible = "allwinner,sun6i-a31-tcon", .data = &sun6i_a31_quirks },
+ { .compatible = "allwinner,sun6i-a31s-tcon", .data = &sun6i_a31s_quirks },
+ { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks },
{ }
};
MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h
index 12bd489..166064b 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.h
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h
@@ -142,6 +142,11 @@
#define SUN4I_TCON_MAX_CHANNELS 2
+struct sun4i_tcon_quirks {
+ bool has_unknown_mux; /* sun5i has undocumented mux */
+ bool has_channel_1; /* a33 does not have channel 1 */
+};
+
struct sun4i_tcon {
struct device *dev;
struct drm_device *drm;
@@ -160,12 +165,10 @@ struct sun4i_tcon {
/* Reset control */
struct reset_control *lcd_rst;
- /* Platform adjustments */
- bool has_mux;
-
struct drm_panel *panel;
- bool has_channel_1;
+ /* Platform adjustments */
+ const struct sun4i_tcon_quirks *quirks;
};
struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node);
diff --git a/drivers/gpu/drm/ttm/ttm_object.c b/drivers/gpu/drm/ttm/ttm_object.c
index 4f5fa8d..144367c 100644
--- a/drivers/gpu/drm/ttm/ttm_object.c
+++ b/drivers/gpu/drm/ttm/ttm_object.c
@@ -179,7 +179,7 @@ int ttm_base_object_init(struct ttm_object_file *tfile,
if (unlikely(ret != 0))
goto out_err0;
- ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
+ ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL, false);
if (unlikely(ret != 0))
goto out_err1;
@@ -318,7 +318,8 @@ EXPORT_SYMBOL(ttm_ref_object_exists);
int ttm_ref_object_add(struct ttm_object_file *tfile,
struct ttm_base_object *base,
- enum ttm_ref_type ref_type, bool *existed)
+ enum ttm_ref_type ref_type, bool *existed,
+ bool require_existed)
{
struct drm_open_hash *ht = &tfile->ref_hash[ref_type];
struct ttm_ref_object *ref;
@@ -345,6 +346,9 @@ int ttm_ref_object_add(struct ttm_object_file *tfile,
}
rcu_read_unlock();
+ if (require_existed)
+ return -EPERM;
+
ret = ttm_mem_global_alloc(mem_glob, sizeof(*ref),
false, false);
if (unlikely(ret != 0))
@@ -635,7 +639,7 @@ int ttm_prime_fd_to_handle(struct ttm_object_file *tfile,
prime = (struct ttm_prime_object *) dma_buf->priv;
base = &prime->base;
*handle = base->hash.key;
- ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
+ ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL, false);
dma_buf_put(dma_buf);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
index 26ac8e8..967450d 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
@@ -538,7 +538,7 @@ int vmw_fence_create(struct vmw_fence_manager *fman,
struct vmw_fence_obj **p_fence)
{
struct vmw_fence_obj *fence;
- int ret;
+ int ret;
fence = kzalloc(sizeof(*fence), GFP_KERNEL);
if (unlikely(fence == NULL))
@@ -701,6 +701,41 @@ void vmw_fence_fifo_up(struct vmw_fence_manager *fman)
}
+/**
+ * vmw_fence_obj_lookup - Look up a user-space fence object
+ *
+ * @tfile: A struct ttm_object_file identifying the caller.
+ * @handle: A handle identifying the fence object.
+ * @return: A struct vmw_user_fence base ttm object on success or
+ * an error pointer on failure.
+ *
+ * The fence object is looked up and type-checked. The caller needs
+ * to have opened the fence object first, but since that happens on
+ * creation and fence objects aren't shareable, that's not an
+ * issue currently.
+ */
+static struct ttm_base_object *
+vmw_fence_obj_lookup(struct ttm_object_file *tfile, u32 handle)
+{
+ struct ttm_base_object *base = ttm_base_object_lookup(tfile, handle);
+
+ if (!base) {
+ pr_err("Invalid fence object handle 0x%08lx.\n",
+ (unsigned long)handle);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (base->refcount_release != vmw_user_fence_base_release) {
+ pr_err("Invalid fence object handle 0x%08lx.\n",
+ (unsigned long)handle);
+ ttm_base_object_unref(&base);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return base;
+}
+
+
int vmw_fence_obj_wait_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
@@ -726,13 +761,9 @@ int vmw_fence_obj_wait_ioctl(struct drm_device *dev, void *data,
arg->kernel_cookie = jiffies + wait_timeout;
}
- base = ttm_base_object_lookup(tfile, arg->handle);
- if (unlikely(base == NULL)) {
- printk(KERN_ERR "Wait invalid fence object handle "
- "0x%08lx.\n",
- (unsigned long)arg->handle);
- return -EINVAL;
- }
+ base = vmw_fence_obj_lookup(tfile, arg->handle);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
fence = &(container_of(base, struct vmw_user_fence, base)->fence);
@@ -771,13 +802,9 @@ int vmw_fence_obj_signaled_ioctl(struct drm_device *dev, void *data,
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
struct vmw_private *dev_priv = vmw_priv(dev);
- base = ttm_base_object_lookup(tfile, arg->handle);
- if (unlikely(base == NULL)) {
- printk(KERN_ERR "Fence signaled invalid fence object handle "
- "0x%08lx.\n",
- (unsigned long)arg->handle);
- return -EINVAL;
- }
+ base = vmw_fence_obj_lookup(tfile, arg->handle);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
fence = &(container_of(base, struct vmw_user_fence, base)->fence);
fman = fman_from_fence(fence);
@@ -1024,6 +1051,7 @@ int vmw_fence_event_ioctl(struct drm_device *dev, void *data,
(struct drm_vmw_fence_event_arg *) data;
struct vmw_fence_obj *fence = NULL;
struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
+ struct ttm_object_file *tfile = vmw_fp->tfile;
struct drm_vmw_fence_rep __user *user_fence_rep =
(struct drm_vmw_fence_rep __user *)(unsigned long)
arg->fence_rep;
@@ -1037,24 +1065,18 @@ int vmw_fence_event_ioctl(struct drm_device *dev, void *data,
*/
if (arg->handle) {
struct ttm_base_object *base =
- ttm_base_object_lookup_for_ref(dev_priv->tdev,
- arg->handle);
+ vmw_fence_obj_lookup(tfile, arg->handle);
- if (unlikely(base == NULL)) {
- DRM_ERROR("Fence event invalid fence object handle "
- "0x%08lx.\n",
- (unsigned long)arg->handle);
- return -EINVAL;
- }
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
fence = &(container_of(base, struct vmw_user_fence,
base)->fence);
(void) vmw_fence_obj_reference(fence);
if (user_fence_rep != NULL) {
- bool existed;
-
ret = ttm_ref_object_add(vmw_fp->tfile, base,
- TTM_REF_USAGE, &existed);
+ TTM_REF_USAGE, NULL, false);
if (unlikely(ret != 0)) {
DRM_ERROR("Failed to reference a fence "
"object.\n");
@@ -1097,8 +1119,7 @@ int vmw_fence_event_ioctl(struct drm_device *dev, void *data,
return 0;
out_no_create:
if (user_fence_rep != NULL)
- ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile,
- handle, TTM_REF_USAGE);
+ ttm_ref_object_base_unref(tfile, handle, TTM_REF_USAGE);
out_no_ref_obj:
vmw_fence_obj_unreference(&fence);
return ret;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
index b8c6a03..5ec24fd 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
@@ -114,8 +114,6 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data,
param->value = dev_priv->has_dx;
break;
default:
- DRM_ERROR("Illegal vmwgfx get param request: %d\n",
- param->param);
return -EINVAL;
}
@@ -186,7 +184,7 @@ int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data,
bool gb_objects = !!(dev_priv->capabilities & SVGA_CAP_GBOBJECTS);
struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
- if (unlikely(arg->pad64 != 0)) {
+ if (unlikely(arg->pad64 != 0 || arg->max_size == 0)) {
DRM_ERROR("Illegal GET_3D_CAP argument.\n");
return -EINVAL;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
index 52ca1c9..bc354f7 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
@@ -589,7 +589,7 @@ static int vmw_user_dmabuf_synccpu_grab(struct vmw_user_dma_buffer *user_bo,
return ret;
ret = ttm_ref_object_add(tfile, &user_bo->prime.base,
- TTM_REF_SYNCCPU_WRITE, &existed);
+ TTM_REF_SYNCCPU_WRITE, &existed, false);
if (ret != 0 || existed)
ttm_bo_synccpu_write_release(&user_bo->dma.base);
@@ -773,7 +773,7 @@ int vmw_user_dmabuf_reference(struct ttm_object_file *tfile,
*handle = user_bo->prime.base.hash.key;
return ttm_ref_object_add(tfile, &user_bo->prime.base,
- TTM_REF_USAGE, NULL);
+ TTM_REF_USAGE, NULL, false);
}
/*
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
index b445ce9..05fa092 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
@@ -713,11 +713,14 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
128;
num_sizes = 0;
- for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i)
+ for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) {
+ if (req->mip_levels[i] > DRM_VMW_MAX_MIP_LEVELS)
+ return -EINVAL;
num_sizes += req->mip_levels[i];
+ }
- if (num_sizes > DRM_VMW_MAX_SURFACE_FACES *
- DRM_VMW_MAX_MIP_LEVELS)
+ if (num_sizes > DRM_VMW_MAX_SURFACE_FACES * DRM_VMW_MAX_MIP_LEVELS ||
+ num_sizes == 0)
return -EINVAL;
size = vmw_user_surface_size + 128 +
@@ -891,17 +894,16 @@ vmw_surface_handle_reference(struct vmw_private *dev_priv,
uint32_t handle;
struct ttm_base_object *base;
int ret;
+ bool require_exist = false;
if (handle_type == DRM_VMW_HANDLE_PRIME) {
ret = ttm_prime_fd_to_handle(tfile, u_handle, &handle);
if (unlikely(ret != 0))
return ret;
} else {
- if (unlikely(drm_is_render_client(file_priv))) {
- DRM_ERROR("Render client refused legacy "
- "surface reference.\n");
- return -EACCES;
- }
+ if (unlikely(drm_is_render_client(file_priv)))
+ require_exist = true;
+
if (ACCESS_ONCE(vmw_fpriv(file_priv)->locked_master)) {
DRM_ERROR("Locked master refused legacy "
"surface reference.\n");
@@ -929,17 +931,14 @@ vmw_surface_handle_reference(struct vmw_private *dev_priv,
/*
* Make sure the surface creator has the same
- * authenticating master.
+ * authenticating master, or is already registered with us.
*/
if (drm_is_primary_client(file_priv) &&
- user_srf->master != file_priv->master) {
- DRM_ERROR("Trying to reference surface outside of"
- " master domain.\n");
- ret = -EACCES;
- goto out_bad_resource;
- }
+ user_srf->master != file_priv->master)
+ require_exist = true;
- ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
+ ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL,
+ require_exist);
if (unlikely(ret != 0)) {
DRM_ERROR("Could not add a reference to a surface.\n");
goto out_bad_resource;
diff --git a/drivers/gpu/msm/a6xx_reg.h b/drivers/gpu/msm/a6xx_reg.h
index fddfb2c..9b9a571 100644
--- a/drivers/gpu/msm/a6xx_reg.h
+++ b/drivers/gpu/msm/a6xx_reg.h
@@ -764,7 +764,6 @@
#define PDC_GPU_TCS1_CMD0_MSGID 0x21575
#define PDC_GPU_TCS1_CMD0_ADDR 0x21576
#define PDC_GPU_TCS1_CMD0_DATA 0x21577
-#define PDC_GPU_TIMESTAMP_UNIT1_EN_DRV0 0x23489
#define PDC_GPU_SEQ_MEM_0 0xA0000
#endif /* _A6XX_REG_H */
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index d278389..af92990 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -504,15 +504,6 @@ static void _regwrite(void __iomem *regbase,
__raw_writel(value, reg);
}
-static void _gmu_regrmw(struct kgsl_device *device,
- unsigned int offsetwords, unsigned int mask)
-{
- unsigned int value;
-
- kgsl_gmu_regread(device, offsetwords, &value);
- kgsl_gmu_regwrite(device, offsetwords, value | mask);
-}
-
/*
* _load_gmu_rpmh_ucode() - Load the ucode into the GPU PDC/RSC blocks
* PDC and RSC execute GPU power on/off RPMh sequence
@@ -651,22 +642,25 @@ static void a6xx_gmu_power_config(struct kgsl_device *device)
/* Configure registers for idle setting. The setting is cumulative */
switch (gmu->idle_level) {
case GPU_HW_MIN_VOLT:
- _gmu_regrmw(device, A6XX_GMU_RPMH_CTRL, MIN_BW_ENABLE_MASK);
- _gmu_regrmw(device, A6XX_GMU_RPMH_HYST_CTRL, MIN_BW_HYST);
+ kgsl_gmu_regrmw(device, A6XX_GMU_RPMH_CTRL, 0,
+ MIN_BW_ENABLE_MASK);
+ kgsl_gmu_regrmw(device, A6XX_GMU_RPMH_HYST_CTRL, 0,
+ MIN_BW_HYST);
/* fall through */
case GPU_HW_NAP:
- _gmu_regrmw(device, A6XX_GMU_GPU_NAP_CTRL, HW_NAP_ENABLE_MASK);
+ kgsl_gmu_regrmw(device, A6XX_GMU_GPU_NAP_CTRL, 0,
+ HW_NAP_ENABLE_MASK);
/* fall through */
case GPU_HW_IFPC:
kgsl_gmu_regwrite(device, A6XX_GMU_PWR_COL_INTER_FRAME_HYST,
0x000A0080);
- _gmu_regrmw(device, A6XX_GMU_PWR_COL_INTER_FRAME_CTRL,
+ kgsl_gmu_regrmw(device, A6XX_GMU_PWR_COL_INTER_FRAME_CTRL, 0,
IFPC_ENABLE_MASK);
/* fall through */
case GPU_HW_SPTP_PC:
kgsl_gmu_regwrite(device, A6XX_GMU_PWR_COL_SPTPRAC_HYST,
0x000A0080);
- _gmu_regrmw(device, A6XX_GMU_PWR_COL_INTER_FRAME_CTRL,
+ kgsl_gmu_regrmw(device, A6XX_GMU_PWR_COL_INTER_FRAME_CTRL, 0,
SPTP_ENABLE_MASK);
/* fall through */
default:
@@ -675,11 +669,13 @@ static void a6xx_gmu_power_config(struct kgsl_device *device)
/* ACD feature enablement */
if (ADRENO_FEATURE(adreno_dev, ADRENO_LM))
- _gmu_regrmw(device, A6XX_GMU_BOOT_KMD_LM_HANDSHAKE, BIT(10));
+ kgsl_gmu_regrmw(device, A6XX_GMU_BOOT_KMD_LM_HANDSHAKE, 0,
+ BIT(10));
/* Enable RPMh GPU client */
if (ADRENO_FEATURE(adreno_dev, ADRENO_RPMH))
- _gmu_regrmw(device, A6XX_GMU_RPMH_CTRL, RPMH_ENABLE_MASK);
+ kgsl_gmu_regrmw(device, A6XX_GMU_RPMH_CTRL, 0,
+ RPMH_ENABLE_MASK);
/* Disable reference bandgap voltage */
kgsl_gmu_regwrite(device, A6XX_GMU_AO_SPARE_CNTL, 1);
@@ -810,7 +806,7 @@ static int a6xx_sptprac_enable(struct adreno_device *adreno_dev)
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
struct gmu_device *gmu = &device->gmu;
- if (!kgsl_gmu_isenabled(device))
+ if (!gmu->pdev)
return -EINVAL;
kgsl_gmu_regwrite(device, A6XX_GMU_GX_SPTPRAC_POWER_CONTROL,
@@ -837,7 +833,7 @@ static void a6xx_sptprac_disable(struct adreno_device *adreno_dev)
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
struct gmu_device *gmu = &device->gmu;
- if (!kgsl_gmu_isenabled(device))
+ if (!gmu->pdev)
return;
kgsl_gmu_regwrite(device, A6XX_GMU_GX_SPTPRAC_POWER_CONTROL,
@@ -1092,7 +1088,7 @@ static int a6xx_rpmh_power_off_gpu(struct kgsl_device *device)
ret = a6xx_hm_sptprac_control(device, false);
/* RSC sleep sequence */
- _regwrite(gmu->pdc_reg_virt, PDC_GPU_TIMESTAMP_UNIT1_EN_DRV0, 1);
+ kgsl_gmu_regwrite(device, A6XX_RSCC_TIMESTAMP_UNIT1_EN_DRV0, 1);
kgsl_gmu_regwrite(device, A6XX_GMU_RSCC_CONTROL_REQ, 1);
wmb();
diff --git a/drivers/gpu/msm/adreno_a6xx_snapshot.c b/drivers/gpu/msm/adreno_a6xx_snapshot.c
index e501a68..9259e71 100644
--- a/drivers/gpu/msm/adreno_a6xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a6xx_snapshot.c
@@ -126,31 +126,31 @@ static struct a6xx_cluster_dbgahb_registers {
ARRAY_SIZE(a6xx_sp_vs_hlsq_cluster) / 2 },
{ CP_CLUSTER_SP_VS, 0x0002A000, 0x21, a6xx_sp_vs_sp_cluster,
ARRAY_SIZE(a6xx_sp_vs_sp_cluster) / 2 },
- { CP_CLUSTER_SP_VS, 0x0002EC00, 0x41, a6xx_hlsq_duplicate_cluster,
+ { CP_CLUSTER_SP_VS, 0x0002E000, 0x41, a6xx_hlsq_duplicate_cluster,
ARRAY_SIZE(a6xx_hlsq_duplicate_cluster) / 2 },
{ CP_CLUSTER_SP_VS, 0x0002F000, 0x45, a6xx_hlsq_2d_duplicate_cluster,
ARRAY_SIZE(a6xx_hlsq_2d_duplicate_cluster) / 2 },
- { CP_CLUSTER_SP_VS, 0x0002AC00, 0x21, a6xx_sp_duplicate_cluster,
+ { CP_CLUSTER_SP_VS, 0x0002A000, 0x21, a6xx_sp_duplicate_cluster,
ARRAY_SIZE(a6xx_sp_duplicate_cluster) / 2 },
- { CP_CLUSTER_SP_VS, 0x0002CC00, 0x1, a6xx_tp_duplicate_cluster,
+ { CP_CLUSTER_SP_VS, 0x0002C000, 0x1, a6xx_tp_duplicate_cluster,
ARRAY_SIZE(a6xx_tp_duplicate_cluster) / 2 },
- { CP_CLUSTER_SP_PS, 0x0002E600, 0x42, a6xx_sp_ps_hlsq_cluster,
+ { CP_CLUSTER_SP_PS, 0x0002E000, 0x42, a6xx_sp_ps_hlsq_cluster,
ARRAY_SIZE(a6xx_sp_ps_hlsq_cluster) / 2 },
- { CP_CLUSTER_SP_PS, 0x0002F300, 0x46, a6xx_sp_ps_hlsq_2d_cluster,
+ { CP_CLUSTER_SP_PS, 0x0002F000, 0x46, a6xx_sp_ps_hlsq_2d_cluster,
ARRAY_SIZE(a6xx_sp_ps_hlsq_2d_cluster) / 2 },
- { CP_CLUSTER_SP_PS, 0x0002A600, 0x22, a6xx_sp_ps_sp_cluster,
+ { CP_CLUSTER_SP_PS, 0x0002A000, 0x22, a6xx_sp_ps_sp_cluster,
ARRAY_SIZE(a6xx_sp_ps_sp_cluster) / 2 },
- { CP_CLUSTER_SP_PS, 0x0002B300, 0x26, a6xx_sp_ps_sp_2d_cluster,
+ { CP_CLUSTER_SP_PS, 0x0002B000, 0x26, a6xx_sp_ps_sp_2d_cluster,
ARRAY_SIZE(a6xx_sp_ps_sp_2d_cluster) / 2 },
- { CP_CLUSTER_SP_PS, 0x0002C600, 0x2, a6xx_sp_ps_tp_cluster,
+ { CP_CLUSTER_SP_PS, 0x0002C000, 0x2, a6xx_sp_ps_tp_cluster,
ARRAY_SIZE(a6xx_sp_ps_tp_cluster) / 2 },
- { CP_CLUSTER_SP_PS, 0x0002D300, 0x6, a6xx_sp_ps_tp_2d_cluster,
+ { CP_CLUSTER_SP_PS, 0x0002D000, 0x6, a6xx_sp_ps_tp_2d_cluster,
ARRAY_SIZE(a6xx_sp_ps_tp_2d_cluster) / 2 },
- { CP_CLUSTER_SP_PS, 0x0002EC00, 0x42, a6xx_hlsq_duplicate_cluster,
+ { CP_CLUSTER_SP_PS, 0x0002E000, 0x42, a6xx_hlsq_duplicate_cluster,
ARRAY_SIZE(a6xx_hlsq_duplicate_cluster) / 2 },
- { CP_CLUSTER_SP_VS, 0x0002AC00, 0x22, a6xx_sp_duplicate_cluster,
+ { CP_CLUSTER_SP_VS, 0x0002A000, 0x22, a6xx_sp_duplicate_cluster,
ARRAY_SIZE(a6xx_sp_duplicate_cluster) / 2 },
- { CP_CLUSTER_SP_VS, 0x0002CC00, 0x2, a6xx_tp_duplicate_cluster,
+ { CP_CLUSTER_SP_VS, 0x0002C000, 0x2, a6xx_tp_duplicate_cluster,
ARRAY_SIZE(a6xx_tp_duplicate_cluster) / 2 },
};
@@ -221,12 +221,12 @@ a6xx_vbif_snapshot_registers[] = {
static const unsigned int a6xx_registers[] = {
/* RBBM */
- 0x0000, 0x0002, 0x0010, 0x0010, 0x0012, 0x0012, 0x0014, 0x0014,
- 0x0018, 0x001B, 0x001e, 0x0032, 0x0038, 0x003C, 0x0042, 0x0042,
- 0x0044, 0x0044, 0x0047, 0x0047, 0x0056, 0x0056, 0x00AD, 0x00AE,
- 0x00B0, 0x00FB, 0x0100, 0x011D, 0x0200, 0x020D, 0x0210, 0x0213,
- 0x0218, 0x023D, 0x0400, 0x04F9, 0x0500, 0x0500, 0x0505, 0x050B,
- 0x050E, 0x0511, 0x0533, 0x0533, 0x0540, 0x0555,
+ 0x0000, 0x0002, 0x0010, 0x0010, 0x0012, 0x0012, 0x0018, 0x001B,
+ 0x001e, 0x0032, 0x0038, 0x003C, 0x0042, 0x0042, 0x0044, 0x0044,
+ 0x0047, 0x0047, 0x0056, 0x0056, 0x00AD, 0x00AE, 0x00B0, 0x00FB,
+ 0x0100, 0x011D, 0x0200, 0x020D, 0x0210, 0x0213, 0x0218, 0x023D,
+ 0x0400, 0x04F9, 0x0500, 0x0500, 0x0505, 0x050B, 0x050E, 0x0511,
+ 0x0533, 0x0533, 0x0540, 0x0555,
/* CP */
0x0800, 0x0808, 0x0810, 0x0813, 0x0820, 0x0821, 0x0823, 0x0827,
0x0830, 0x0833, 0x0840, 0x0843, 0x084F, 0x086F, 0x0880, 0x088A,
@@ -240,8 +240,8 @@ static const unsigned int a6xx_registers[] = {
0x0E10, 0x0E13, 0x0E17, 0x0E19, 0x0E1C, 0x0E2B, 0x0E30, 0x0E32,
0x0E38, 0x0E39,
/* GRAS */
- 0x8600, 0x8601, 0x8604, 0x8605, 0x8610, 0x861B, 0x8620, 0x8620,
- 0x8628, 0x862B, 0x8630, 0x8637,
+ 0x8600, 0x8601, 0x8610, 0x861B, 0x8620, 0x8620, 0x8628, 0x862B,
+ 0x8630, 0x8637,
/* RB */
0x8E01, 0x8E01, 0x8E04, 0x8E05, 0x8E07, 0x8E08, 0x8E0C, 0x8E0C,
0x8E10, 0x8E1C, 0x8E20, 0x8E25, 0x8E28, 0x8E28, 0x8E2C, 0x8E2F,
@@ -254,7 +254,7 @@ static const unsigned int a6xx_registers[] = {
0x9E70, 0x9E72, 0x9E78, 0x9E79, 0x9E80, 0x9FFF,
/* VFD */
0xA600, 0xA601, 0xA603, 0xA603, 0xA60A, 0xA60A, 0xA610, 0xA617,
- 0xA630, 0xA630, 0xD200, 0xD263,
+ 0xA630, 0xA630,
};
enum a6xx_debugbus_id {
@@ -275,11 +275,12 @@ enum a6xx_debugbus_id {
A6XX_DBGBUS_LRZ = 0x10,
A6XX_DBGBUS_A2D = 0x11,
A6XX_DBGBUS_CCUFCHE = 0x12,
- A6XX_DBGBUS_GMU = 0x13,
+ A6XX_DBGBUS_GMU_CX = 0x13,
A6XX_DBGBUS_RBP = 0x14,
A6XX_DBGBUS_DCS = 0x15,
A6XX_DBGBUS_RBBM_CFG = 0x16,
A6XX_DBGBUS_CX = 0x17,
+ A6XX_DBGBUS_GMU_GX = 0x18,
A6XX_DBGBUS_TPFCHE = 0x19,
A6XX_DBGBUS_GPC = 0x1d,
A6XX_DBGBUS_LARC = 0x1e,
@@ -321,6 +322,7 @@ static const struct adreno_debugbus_block a6xx_dbgc_debugbus_blocks[] = {
{ A6XX_DBGBUS_RBP, 0x100, },
{ A6XX_DBGBUS_DCS, 0x100, },
{ A6XX_DBGBUS_RBBM_CFG, 0x100, },
+ { A6XX_DBGBUS_GMU_GX, 0x100, },
{ A6XX_DBGBUS_TPFCHE, 0x100, },
{ A6XX_DBGBUS_GPC, 0x100, },
{ A6XX_DBGBUS_LARC, 0x100, },
@@ -345,7 +347,7 @@ static const struct adreno_debugbus_block a6xx_dbgc_debugbus_blocks[] = {
static void __iomem *a6xx_cx_dbgc;
static const struct adreno_debugbus_block a6xx_cx_dbgc_debugbus_blocks[] = {
{ A6XX_DBGBUS_VBIF, 0x100, },
- { A6XX_DBGBUS_GMU, 0x100, },
+ { A6XX_DBGBUS_GMU_CX, 0x100, },
{ A6XX_DBGBUS_CX, 0x100, },
};
@@ -883,12 +885,14 @@ static void a6xx_dbgc_debug_bus_read(struct kgsl_device *device,
static size_t a6xx_snapshot_dbgc_debugbus_block(struct kgsl_device *device,
u8 *buf, size_t remain, void *priv)
{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct kgsl_snapshot_debugbus *header =
(struct kgsl_snapshot_debugbus *)buf;
struct adreno_debugbus_block *block = priv;
int i;
unsigned int *data = (unsigned int *)(buf + sizeof(*header));
unsigned int dwords;
+ unsigned int block_id;
size_t size;
dwords = block->dwords;
@@ -904,9 +908,14 @@ static size_t a6xx_snapshot_dbgc_debugbus_block(struct kgsl_device *device,
header->id = block->block_id;
header->count = dwords * 2;
+ block_id = block->block_id;
+ /* GMU_GX data is read using the GMU_CX block id on A630 */
+ if (adreno_is_a630(adreno_dev) &&
+ (block_id == A6XX_DBGBUS_GMU_GX))
+ block_id = A6XX_DBGBUS_GMU_CX;
+
for (i = 0; i < dwords; i++)
- a6xx_dbgc_debug_bus_read(device, block->block_id, i,
- &data[i*2]);
+ a6xx_dbgc_debug_bus_read(device, block_id, i, &data[i*2]);
return size;
}
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index fa4ca39..2283096 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -261,8 +261,11 @@ 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 userspace memory alloc and map ioctls */
+ kref_get(&entry->refcount);
+ }
return entry;
}
@@ -2412,6 +2415,10 @@ 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 ref from kgsl_mem_entry_create() */
+ kgsl_mem_entry_put(entry);
+
return 0;
unmap:
@@ -2718,6 +2725,10 @@ 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 ref from kgsl_mem_entry_create() */
+ kgsl_mem_entry_put(entry);
+
return result;
error_attach:
@@ -3155,6 +3166,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 ref from kgsl_mem_entry_create() */
+ kgsl_mem_entry_put(entry);
+
return 0;
}
@@ -3178,6 +3192,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 ref from kgsl_mem_entry_create() */
+ kgsl_mem_entry_put(entry);
+
return 0;
}
@@ -3201,6 +3218,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 ref from kgsl_mem_entry_create() */
+ kgsl_mem_entry_put(entry);
+
return 0;
}
@@ -3318,6 +3338,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 ref from kgsl_mem_entry_create() */
+ kgsl_mem_entry_put(entry);
+
return 0;
err_invalid_pages:
@@ -3397,6 +3420,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 ref from 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 bf31c00..d955aa0 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -569,6 +569,17 @@ static inline void kgsl_regrmw(struct kgsl_device *device,
device->ftbl->regwrite(device, offsetwords, val | bits);
}
+static inline void kgsl_gmu_regrmw(struct kgsl_device *device,
+ unsigned int offsetwords,
+ unsigned int mask, unsigned int bits)
+{
+ unsigned int val = 0;
+
+ kgsl_gmu_regread(device, offsetwords, &val);
+ val &= ~mask;
+ kgsl_gmu_regwrite(device, offsetwords, val | bits);
+}
+
static inline int kgsl_idle(struct kgsl_device *device)
{
return device->ftbl->idle(device);
diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index 97e4b6f..17c10eb 100644
--- a/drivers/gpu/msm/kgsl_gmu.c
+++ b/drivers/gpu/msm/kgsl_gmu.c
@@ -1153,7 +1153,6 @@ static int gmu_disable_clks(struct gmu_device *gmu)
while ((j < MAX_GMU_CLKS) && gmu->clks[j]) {
clk_disable_unprepare(gmu->clks[j]);
- gmu->clks[j] = NULL;
j++;
}
@@ -1351,7 +1350,7 @@ void gmu_remove(struct kgsl_device *device)
{
struct gmu_device *gmu = &device->gmu;
struct kgsl_hfi *hfi = &gmu->hfi;
- int i;
+ int i = 0;
if (!device->gmu.pdev)
return;
@@ -1360,6 +1359,11 @@ void gmu_remove(struct kgsl_device *device)
gmu_stop(device);
+ while ((i < MAX_GMU_CLKS) && gmu->clks[i]) {
+ gmu->clks[i] = NULL;
+ i++;
+ }
+
if (gmu->gmu_interrupt_num) {
disable_irq(gmu->gmu_interrupt_num);
devm_free_irq(&gmu->pdev->dev,
diff --git a/drivers/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c
index 3b57b73..9c078b6 100644
--- a/drivers/gpu/msm/kgsl_sync.c
+++ b/drivers/gpu/msm/kgsl_sync.c
@@ -152,6 +152,17 @@ static int _add_fence_event(struct kgsl_device *device,
return ret;
}
+/* Only to be used if creating a related event failed */
+static void kgsl_sync_cancel(struct kgsl_sync_fence *kfence)
+{
+ spin_lock(&kfence->parent->lock);
+ if (!list_empty(&kfence->child_list)) {
+ list_del_init(&kfence->child_list);
+ fence_put(&kfence->fence);
+ }
+ spin_unlock(&kfence->parent->lock);
+}
+
/**
* kgsl_add_fence_event - Create a new fence event
* @device - KGSL device to create the event on
@@ -235,6 +246,7 @@ int kgsl_add_fence_event(struct kgsl_device *device,
put_unused_fd(priv.fence_fd);
if (kfence) {
+ kgsl_sync_cancel(kfence);
/*
* Put the refcount of sync file. This will release
* kfence->fence as well.
@@ -366,7 +378,7 @@ static void kgsl_sync_timeline_signal(struct kgsl_sync_timeline *ktimeline,
list_for_each_entry_safe(kfence, next, &ktimeline->child_list_head,
child_list) {
if (fence_is_signaled_locked(&kfence->fence)) {
- list_del(&kfence->child_list);
+ list_del_init(&kfence->child_list);
fence_put(&kfence->fence);
}
}
@@ -447,7 +459,7 @@ struct kgsl_sync_fence_cb *kgsl_sync_fence_async_wait(int fd,
if (status) {
kfree(kcb);
- if (fence_is_signaled(fence))
+ if (!fence_is_signaled(fence))
kcb = ERR_PTR(status);
else
kcb = NULL;
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 2b89c70..a5dd7e6 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -728,7 +728,6 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type)
hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 ||
hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP ||
hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP ||
- hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 ||
hid->product == USB_DEVICE_ID_MS_POWER_COVER) &&
hid->group == HID_GROUP_MULTITOUCH)
hid->group = HID_GROUP_GENERIC;
@@ -1984,7 +1983,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP) },
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3KV1) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 9845189..da93077 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -318,8 +318,11 @@
#define USB_VENDOR_ID_DMI 0x0c0b
#define USB_DEVICE_ID_DMI_ENC 0x5fab
-#define USB_VENDOR_ID_DRAGONRISE 0x0079
-#define USB_DEVICE_ID_DRAGONRISE_WIIU 0x1800
+#define USB_VENDOR_ID_DRAGONRISE 0x0079
+#define USB_DEVICE_ID_DRAGONRISE_WIIU 0x1800
+#define USB_DEVICE_ID_DRAGONRISE_PS3 0x1801
+#define USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR 0x1803
+#define USB_DEVICE_ID_DRAGONRISE_GAMECUBE 0x1843
#define USB_VENDOR_ID_DWAV 0x0eef
#define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001
@@ -365,6 +368,9 @@
#define USB_VENDOR_ID_FLATFROG 0x25b5
#define USB_DEVICE_ID_MULTITOUCH_3200 0x0002
+#define USB_VENDOR_ID_FUTABA 0x0547
+#define USB_DEVICE_ID_LED_DISPLAY 0x7000
+
#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f
#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
@@ -722,7 +728,6 @@
#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 0x07e2
#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP 0x07dd
#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP 0x07e9
-#define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07de
#define USB_DEVICE_ID_MS_POWER_COVER 0x07da
#define USB_VENDOR_ID_MOJO 0x8282
@@ -1037,6 +1042,10 @@
#define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH 0x0500
#define USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET 0x0502
+#define USB_VENDOR_ID_WEIDA 0x2575
+#define USB_DEVICE_ID_WEIDA_8752 0xC300
+#define USB_DEVICE_ID_WEIDA_8755 0xC301
+
#define USB_VENDOR_ID_WISEGROUP 0x0925
#define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005
#define USB_DEVICE_ID_SUPER_JOY_BOX_3 0x8888
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index c6cd392..ba02667 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -282,8 +282,6 @@ static const struct hid_device_id ms_devices[] = {
.driver_data = MS_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP),
.driver_data = MS_HIDINPUT },
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3),
- .driver_data = MS_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER),
.driver_data = MS_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD),
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index fb6f1f4..89e9032 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -108,6 +108,7 @@ struct mt_device {
int cc_value_index; /* contact count value index in the field */
unsigned last_slot_field; /* the last field of a slot */
unsigned mt_report_id; /* the report ID of the multitouch device */
+ unsigned long initial_quirks; /* initial quirks state */
__s16 inputmode; /* InputMode HID feature, -1 if non-existent */
__s16 inputmode_index; /* InputMode HID feature index in the report */
__s16 maxcontact_report_id; /* Maximum Contact Number HID feature,
@@ -318,13 +319,10 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)
u8 *buf;
/*
- * Only fetch the feature report if initial reports are not already
- * been retrieved. Currently this is only done for Windows 8 touch
- * devices.
+ * Do not fetch the feature report if the device has been explicitly
+ * marked as non-capable.
*/
- if (!(hdev->quirks & HID_QUIRK_NO_INIT_REPORTS))
- return;
- if (td->mtclass.name != MT_CLS_WIN_8)
+ if (td->initial_quirks & HID_QUIRK_NO_INIT_REPORTS)
return;
buf = hid_alloc_report_buf(report, GFP_KERNEL);
@@ -842,7 +840,9 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
if (!td->mtclass.export_all_inputs &&
field->application != HID_DG_TOUCHSCREEN &&
field->application != HID_DG_PEN &&
- field->application != HID_DG_TOUCHPAD)
+ field->application != HID_DG_TOUCHPAD &&
+ field->application != HID_GD_KEYBOARD &&
+ field->application != HID_CP_CONSUMER_CONTROL)
return -1;
/*
@@ -1083,36 +1083,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
}
- /* This allows the driver to correctly support devices
- * that emit events over several HID messages.
- */
- hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
-
- /*
- * This allows the driver to handle different input sensors
- * that emits events through different reports on the same HID
- * device.
- */
- hdev->quirks |= HID_QUIRK_MULTI_INPUT;
- hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT;
-
- /*
- * Handle special quirks for Windows 8 certified devices.
- */
- if (id->group == HID_GROUP_MULTITOUCH_WIN_8)
- /*
- * Some multitouch screens do not like to be polled for input
- * reports. Fortunately, the Win8 spec says that all touches
- * should be sent during each report, making the initialization
- * of input reports unnecessary.
- *
- * In addition some touchpads do not behave well if we read
- * all feature reports from them. Instead we prevent
- * initial report fetching and then selectively fetch each
- * report we are interested in.
- */
- hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
-
td = devm_kzalloc(&hdev->dev, sizeof(struct mt_device), GFP_KERNEL);
if (!td) {
dev_err(&hdev->dev, "cannot allocate multitouch data\n");
@@ -1136,6 +1106,39 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
td->serial_maybe = true;
+ /*
+ * Store the initial quirk state
+ */
+ td->initial_quirks = hdev->quirks;
+
+ /* This allows the driver to correctly support devices
+ * that emit events over several HID messages.
+ */
+ hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
+
+ /*
+ * This allows the driver to handle different input sensors
+ * that emits events through different reports on the same HID
+ * device.
+ */
+ hdev->quirks |= HID_QUIRK_MULTI_INPUT;
+ hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT;
+
+ /*
+ * Some multitouch screens do not like to be polled for input
+ * reports. Fortunately, the Win8 spec says that all touches
+ * should be sent during each report, making the initialization
+ * of input reports unnecessary. For Win7 devices, well, let's hope
+ * they will still be happy (this is only be a problem if a touch
+ * was already there while probing the device).
+ *
+ * In addition some touchpads do not behave well if we read
+ * all feature reports from them. Instead we prevent
+ * initial report fetching and then selectively fetch each
+ * report we are interested in.
+ */
+ hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
+
ret = hid_parse(hdev);
if (ret != 0)
return ret;
@@ -1204,8 +1207,11 @@ static int mt_resume(struct hid_device *hdev)
static void mt_remove(struct hid_device *hdev)
{
+ struct mt_device *td = hid_get_drvdata(hdev);
+
sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
hid_hw_stop(hdev);
+ hdev->quirks = td->initial_quirks;
}
/*
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
index 8f6c353..4ef7337 100644
--- a/drivers/hid/hid-sensor-hub.c
+++ b/drivers/hid/hid-sensor-hub.c
@@ -796,6 +796,12 @@ static const struct hid_device_id sensor_hub_devices[] = {
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT,
USB_DEVICE_ID_MS_TYPE_COVER_2),
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+ { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT,
+ 0x07bd), /* Microsoft Surface 3 */
+ .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+ { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROCHIP,
+ 0x0f01), /* MM7150 */
+ .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0,
USB_DEVICE_ID_STM_HID_SENSOR),
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index b3ec4f2..b1bce80 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -41,6 +41,11 @@
#include <linux/i2c/i2c-hid.h>
+#include "../hid-ids.h"
+
+/* quirks to control the device */
+#define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV BIT(0)
+
/* flags */
#define I2C_HID_STARTED 0
#define I2C_HID_RESET_PENDING 1
@@ -143,6 +148,7 @@ struct i2c_hid {
char *argsbuf; /* Command arguments buffer */
unsigned long flags; /* device flags */
+ unsigned long quirks; /* Various quirks */
wait_queue_head_t wait; /* For waiting the interrupt */
struct gpio_desc *desc;
@@ -154,6 +160,39 @@ struct i2c_hid {
struct mutex reset_lock;
};
+static const struct i2c_hid_quirks {
+ __u16 idVendor;
+ __u16 idProduct;
+ __u32 quirks;
+} i2c_hid_quirks[] = {
+ { USB_VENDOR_ID_WEIDA, USB_DEVICE_ID_WEIDA_8752,
+ I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
+ { USB_VENDOR_ID_WEIDA, USB_DEVICE_ID_WEIDA_8755,
+ I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
+ { 0, 0 }
+};
+
+/*
+ * i2c_hid_lookup_quirk: return any quirks associated with a I2C HID device
+ * @idVendor: the 16-bit vendor ID
+ * @idProduct: the 16-bit product ID
+ *
+ * Returns: a u32 quirks value.
+ */
+static u32 i2c_hid_lookup_quirk(const u16 idVendor, const u16 idProduct)
+{
+ u32 quirks = 0;
+ int n;
+
+ for (n = 0; i2c_hid_quirks[n].idVendor; n++)
+ if (i2c_hid_quirks[n].idVendor == idVendor &&
+ (i2c_hid_quirks[n].idProduct == (__u16)HID_ANY_ID ||
+ i2c_hid_quirks[n].idProduct == idProduct))
+ quirks = i2c_hid_quirks[n].quirks;
+
+ return quirks;
+}
+
static int __i2c_hid_command(struct i2c_client *client,
const struct i2c_hid_cmd *command, u8 reportID,
u8 reportType, u8 *args, int args_len,
@@ -346,11 +385,27 @@ static int i2c_hid_set_power(struct i2c_client *client, int power_state)
i2c_hid_dbg(ihid, "%s\n", __func__);
+ /*
+ * Some devices require to send a command to wakeup before power on.
+ * The call will get a return value (EREMOTEIO) but device will be
+ * triggered and activated. After that, it goes like a normal device.
+ */
+ if (power_state == I2C_HID_PWR_ON &&
+ ihid->quirks & I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV) {
+ ret = i2c_hid_command(client, &hid_set_power_cmd, NULL, 0);
+
+ /* Device was already activated */
+ if (!ret)
+ goto set_pwr_exit;
+ }
+
ret = __i2c_hid_command(client, &hid_set_power_cmd, power_state,
0, NULL, 0, NULL, 0);
+
if (ret)
dev_err(&client->dev, "failed to change power setting.\n");
+set_pwr_exit:
return ret;
}
@@ -1050,6 +1105,8 @@ static int i2c_hid_probe(struct i2c_client *client,
client->name, hid->vendor, hid->product);
strlcpy(hid->phys, dev_name(&client->dev), sizeof(hid->phys));
+ ihid->quirks = i2c_hid_lookup_quirk(hid->vendor, hid->product);
+
ret = hid_add_device(hid);
if (ret) {
if (ret != -ENODEV)
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index cde060f..97dbb25 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -83,10 +83,14 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3, HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR, HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_ELAN, HID_ANY_ID, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_FUTABA, USB_DEVICE_ID_LED_DISPLAY, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0A4A, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL },
@@ -103,7 +107,6 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP, HID_QUIRK_NO_INIT_REPORTS },
- { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 623be90..0e07a76 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -2896,6 +2896,9 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
{
struct wacom_features *features = &wacom_wac->features;
+ if ((features->type == HID_GENERIC) && features->numbered_buttons > 0)
+ features->device_type |= WACOM_DEVICETYPE_PAD;
+
if (!(features->device_type & WACOM_DEVICETYPE_PAD))
return -ENODEV;
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index 33b760f..d883483 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -392,78 +392,78 @@ static const struct qpnp_vadc_map_pt adcmap_qrd_skut1_btm_threshold[] = {
/* Voltage to temperature */
static const struct qpnp_vadc_map_pt adcmap_100k_104ef_104fb[] = {
- {1758, -40},
- {1742, -35},
- {1719, -30},
- {1691, -25},
- {1654, -20},
- {1608, -15},
- {1551, -10},
- {1483, -5},
+ {1758, -40000},
+ {1742, -35000},
+ {1719, -30000},
+ {1691, -25000},
+ {1654, -20000},
+ {1608, -15000},
+ {1551, -10000},
+ {1483, -5000},
{1404, 0},
- {1315, 5},
- {1218, 10},
- {1114, 15},
- {1007, 20},
- {900, 25},
- {795, 30},
- {696, 35},
- {605, 40},
- {522, 45},
- {448, 50},
- {383, 55},
- {327, 60},
- {278, 65},
- {237, 70},
- {202, 75},
- {172, 80},
- {146, 85},
- {125, 90},
- {107, 95},
- {92, 100},
- {79, 105},
- {68, 110},
- {59, 115},
- {51, 120},
- {44, 125}
+ {1315, 5000},
+ {1218, 10000},
+ {1114, 15000},
+ {1007, 20000},
+ {900, 25000},
+ {795, 30000},
+ {696, 35000},
+ {605, 40000},
+ {522, 45000},
+ {448, 50000},
+ {383, 55000},
+ {327, 60000},
+ {278, 65000},
+ {237, 70000},
+ {202, 75000},
+ {172, 80000},
+ {146, 85000},
+ {125, 90000},
+ {107, 95000},
+ {92, 100000},
+ {79, 105000},
+ {68, 110000},
+ {59, 115000},
+ {51, 120000},
+ {44, 125000}
};
/* Voltage to temperature */
static const struct qpnp_vadc_map_pt adcmap_150k_104ef_104fb[] = {
- {1738, -40},
- {1714, -35},
- {1682, -30},
- {1641, -25},
- {1589, -20},
- {1526, -15},
- {1451, -10},
- {1363, -5},
+ {1738, -40000},
+ {1714, -35000},
+ {1682, -30000},
+ {1641, -25000},
+ {1589, -20000},
+ {1526, -15000},
+ {1451, -10000},
+ {1363, -5000},
{1266, 0},
- {1159, 5},
- {1048, 10},
- {936, 15},
- {825, 20},
- {720, 25},
- {622, 30},
- {533, 35},
- {454, 40},
- {385, 45},
- {326, 50},
- {275, 55},
- {232, 60},
- {195, 65},
- {165, 70},
- {139, 75},
- {118, 80},
- {100, 85},
- {85, 90},
- {73, 95},
- {62, 100},
- {53, 105},
- {46, 110},
- {40, 115},
- {34, 120},
- {30, 125}
+ {1159, 5000},
+ {1048, 10000},
+ {936, 15000},
+ {825, 20000},
+ {720, 25000},
+ {622, 30000},
+ {533, 35000},
+ {454, 40000},
+ {385, 45000},
+ {326, 50000},
+ {275, 55000},
+ {232, 60000},
+ {195, 65000},
+ {165, 70000},
+ {139, 75000},
+ {118, 80000},
+ {100, 85000},
+ {85, 90000},
+ {73, 95000},
+ {62, 100000},
+ {53, 105000},
+ {46, 110000},
+ {40, 115000},
+ {34, 120000},
+ {30, 125000}
};
static const struct qpnp_vadc_map_pt adcmap_smb_batt_therm[] = {
@@ -595,40 +595,40 @@ static const struct qpnp_vadc_map_pt adcmap_ncp03wf683[] = {
* 1.875V reference.
*/
static const struct qpnp_vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
- { 1831, -40 },
- { 1814, -35 },
- { 1791, -30 },
- { 1761, -25 },
- { 1723, -20 },
- { 1675, -15 },
- { 1616, -10 },
- { 1545, -5 },
+ { 1831, -40000 },
+ { 1814, -35000 },
+ { 1791, -30000 },
+ { 1761, -25000 },
+ { 1723, -20000 },
+ { 1675, -15000 },
+ { 1616, -10000 },
+ { 1545, -5000 },
{ 1463, 0 },
- { 1370, 5 },
- { 1268, 10 },
- { 1160, 15 },
- { 1049, 20 },
- { 937, 25 },
- { 828, 30 },
- { 726, 35 },
- { 630, 40 },
- { 544, 45 },
- { 467, 50 },
- { 399, 55 },
- { 340, 60 },
- { 290, 65 },
- { 247, 70 },
- { 209, 75 },
- { 179, 80 },
- { 153, 85 },
- { 130, 90 },
- { 112, 95 },
- { 96, 100 },
- { 82, 105 },
- { 71, 110 },
- { 62, 115 },
- { 53, 120 },
- { 46, 125 },
+ { 1370, 5000 },
+ { 1268, 10000 },
+ { 1160, 15000 },
+ { 1049, 20000 },
+ { 937, 25000 },
+ { 828, 30000 },
+ { 726, 35000 },
+ { 630, 40000 },
+ { 544, 45000 },
+ { 467, 50000 },
+ { 399, 55000 },
+ { 340, 60000 },
+ { 290, 65000 },
+ { 247, 70000 },
+ { 209, 75000 },
+ { 179, 80000 },
+ { 153, 85000 },
+ { 130, 90000 },
+ { 112, 95000 },
+ { 96, 100000 },
+ { 82, 105000 },
+ { 71, 110000 },
+ { 62, 115000 },
+ { 53, 120000 },
+ { 46, 125000 },
};
static int32_t qpnp_adc_map_voltage_temp(const struct qpnp_vadc_map_pt *pts,
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
index 6cd63b2..4b5e206 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -2562,10 +2562,9 @@ static int32_t qpnp_vadc_init_hwmon(struct qpnp_vadc_chip *vadc,
return rc;
}
-static int qpnp_vadc_get_temp(struct thermal_zone_device *thermal,
- int *temp)
+static int qpnp_vadc_get_temp(void *data, int *temp)
{
- struct qpnp_vadc_thermal_data *vadc_therm = thermal->devdata;
+ struct qpnp_vadc_thermal_data *vadc_therm = data;
struct qpnp_vadc_chip *vadc = vadc_therm->vadc_dev;
struct qpnp_vadc_result result;
int rc = 0;
@@ -2583,7 +2582,7 @@ static int qpnp_vadc_get_temp(struct thermal_zone_device *thermal,
return rc;
}
-static struct thermal_zone_device_ops qpnp_vadc_thermal_ops = {
+static struct thermal_zone_of_device_ops qpnp_vadc_thermal_ops = {
.get_temp = qpnp_vadc_get_temp,
};
@@ -2612,9 +2611,11 @@ static int32_t qpnp_vadc_init_thermal(struct qpnp_vadc_chip *vadc,
vadc->adc->adc_channels[i].name);
vadc->vadc_therm_chan[i].vadc_dev = vadc;
vadc->vadc_therm_chan[i].tz_dev =
- thermal_zone_device_register(name,
- 0, 0, &vadc->vadc_therm_chan[i],
- &qpnp_vadc_thermal_ops, NULL, 0, 0);
+ devm_thermal_zone_of_sensor_register(
+ vadc->dev,
+ vadc->vadc_therm_chan[i].vadc_channel,
+ &vadc->vadc_therm_chan[i],
+ &qpnp_vadc_thermal_ops);
if (IS_ERR(vadc->vadc_therm_chan[i].tz_dev)) {
pr_err("thermal device register failed.\n");
goto thermal_err_sens;
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index 5d67089..af94ad7 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -22,5 +22,5 @@
obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
obj-$(CONFIG_CORESIGHT_CSR) += coresight-csr.o
obj-$(CONFIG_CORESIGHT_HWEVENT) += coresight-hwevent.o
-obj-$(CONFIG_CORESIGHT_SOURCE_DUMMY) += coresight-dummy.o
+obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o
obj-$(CONFIG_CORESIGHT_REMOTE_ETM) += coresight-remote-etm.o
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index 4fc5916..b04e8da 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
@@ -62,7 +62,7 @@ static void etm4_os_unlock(struct etmv4_drvdata *drvdata)
static bool etm4_arch_supported(u8 arch)
{
switch (arch) {
- case ETM_ARCH_V4:
+ case ETM_ARCH_MAJOR_V4:
break;
default:
return false;
@@ -482,7 +482,7 @@ static void etm4_init_arch_data(void *info)
* TRCARCHMIN, bits[7:4] architecture the minor version number
* TRCARCHMAJ, bits[11:8] architecture major versin number
*/
- drvdata->arch = BMVAL(etmidr1, 4, 11);
+ drvdata->arch = BMVAL(etmidr1, 8, 11);
/* maximum size of resources */
etmidr2 = readl_relaxed(drvdata->base + TRCIDR2);
@@ -528,8 +528,8 @@ static void etm4_init_arch_data(void *info)
else
drvdata->sysstall = false;
- /* NUMPROC, bits[30:28] the number of PEs available for tracing */
- drvdata->nr_pe = BMVAL(etmidr3, 28, 30);
+ /* NUMPROC, bits[13:12, 30:28] the number of PEs available for trace */
+ drvdata->nr_pe = (BMVAL(etmidr3, 12, 13) << 3) | BMVAL(etmidr3, 28, 30);
/* NOOVERFLOW, bit[31] is trace overflow prevention supported */
if (BMVAL(etmidr3, 31, 31))
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h
index ba8d3f8..4e51ecdc 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.h
+++ b/drivers/hwtracing/coresight/coresight-etm4x.h
@@ -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
@@ -143,7 +143,7 @@
#define ETM_MAX_RES_SEL 16
#define ETM_MAX_SS_CMP 8
-#define ETM_ARCH_V4 0x40
+#define ETM_ARCH_MAJOR_V4 0x4
#define ETMv4_SYNC_MASK 0x1F
#define ETM_CYC_THRESHOLD_MASK 0xFFF
#define ETMv4_EVENT_MASK 0xFF
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 2d2abe3..833f10d 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -447,7 +447,9 @@ void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO);
- writel_relaxed(0x0, drvdata->base + TMC_DBAHI);
+ writel_relaxed(((u64)drvdata->paddr >> 32) & 0xFF,
+ drvdata->base + TMC_DBAHI);
+
writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
TMC_FFCR_TRIGON_TRIGIN,
diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c
index d4f3239..f283b71 100644
--- a/drivers/i2c/busses/i2c-bcm2835.c
+++ b/drivers/i2c/busses/i2c-bcm2835.c
@@ -64,6 +64,7 @@ struct bcm2835_i2c_dev {
int irq;
struct i2c_adapter adapter;
struct completion completion;
+ struct i2c_msg *curr_msg;
u32 msg_err;
u8 *msg_buf;
size_t msg_buf_remaining;
@@ -126,14 +127,13 @@ static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data)
return IRQ_HANDLED;
}
- if (val & BCM2835_I2C_S_RXD) {
- bcm2835_drain_rxfifo(i2c_dev);
- if (!(val & BCM2835_I2C_S_DONE))
- return IRQ_HANDLED;
- }
-
if (val & BCM2835_I2C_S_DONE) {
- if (i2c_dev->msg_buf_remaining)
+ if (i2c_dev->curr_msg->flags & I2C_M_RD) {
+ bcm2835_drain_rxfifo(i2c_dev);
+ val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
+ }
+
+ if ((val & BCM2835_I2C_S_RXD) || i2c_dev->msg_buf_remaining)
i2c_dev->msg_err = BCM2835_I2C_S_LEN;
else
i2c_dev->msg_err = 0;
@@ -141,11 +141,16 @@ static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data)
return IRQ_HANDLED;
}
- if (val & BCM2835_I2C_S_TXD) {
+ if (val & BCM2835_I2C_S_TXW) {
bcm2835_fill_txfifo(i2c_dev);
return IRQ_HANDLED;
}
+ if (val & BCM2835_I2C_S_RXR) {
+ bcm2835_drain_rxfifo(i2c_dev);
+ return IRQ_HANDLED;
+ }
+
return IRQ_NONE;
}
@@ -155,6 +160,7 @@ static int bcm2835_i2c_xfer_msg(struct bcm2835_i2c_dev *i2c_dev,
u32 c;
unsigned long time_left;
+ i2c_dev->curr_msg = msg;
i2c_dev->msg_buf = msg->buf;
i2c_dev->msg_buf_remaining = msg->len;
reinit_completion(&i2c_dev->completion);
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index 4466a2f..5ded9b2 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -724,6 +724,50 @@ static struct cpuidle_state atom_cstates[] = {
{
.enter = NULL }
};
+static struct cpuidle_state tangier_cstates[] = {
+ {
+ .name = "C1-TNG",
+ .desc = "MWAIT 0x00",
+ .flags = MWAIT2flg(0x00),
+ .exit_latency = 1,
+ .target_residency = 4,
+ .enter = &intel_idle,
+ .enter_freeze = intel_idle_freeze, },
+ {
+ .name = "C4-TNG",
+ .desc = "MWAIT 0x30",
+ .flags = MWAIT2flg(0x30) | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 100,
+ .target_residency = 400,
+ .enter = &intel_idle,
+ .enter_freeze = intel_idle_freeze, },
+ {
+ .name = "C6-TNG",
+ .desc = "MWAIT 0x52",
+ .flags = MWAIT2flg(0x52) | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 140,
+ .target_residency = 560,
+ .enter = &intel_idle,
+ .enter_freeze = intel_idle_freeze, },
+ {
+ .name = "C7-TNG",
+ .desc = "MWAIT 0x60",
+ .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 1200,
+ .target_residency = 4000,
+ .enter = &intel_idle,
+ .enter_freeze = intel_idle_freeze, },
+ {
+ .name = "C9-TNG",
+ .desc = "MWAIT 0x64",
+ .flags = MWAIT2flg(0x64) | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 10000,
+ .target_residency = 20000,
+ .enter = &intel_idle,
+ .enter_freeze = intel_idle_freeze, },
+ {
+ .enter = NULL }
+};
static struct cpuidle_state avn_cstates[] = {
{
.name = "C1-AVN",
@@ -978,6 +1022,10 @@ static const struct idle_cpu idle_cpu_atom = {
.state_table = atom_cstates,
};
+static const struct idle_cpu idle_cpu_tangier = {
+ .state_table = tangier_cstates,
+};
+
static const struct idle_cpu idle_cpu_lincroft = {
.state_table = atom_cstates,
.auto_demotion_disable_flags = ATM_LNC_C6_AUTO_DEMOTE,
@@ -1066,6 +1114,7 @@ static const struct x86_cpu_id intel_idle_ids[] __initconst = {
ICPU(INTEL_FAM6_SANDYBRIDGE_X, idle_cpu_snb),
ICPU(INTEL_FAM6_ATOM_CEDARVIEW, idle_cpu_atom),
ICPU(INTEL_FAM6_ATOM_SILVERMONT1, idle_cpu_byt),
+ ICPU(INTEL_FAM6_ATOM_MERRIFIELD, idle_cpu_tangier),
ICPU(INTEL_FAM6_ATOM_AIRMONT, idle_cpu_cht),
ICPU(INTEL_FAM6_IVYBRIDGE, idle_cpu_ivb),
ICPU(INTEL_FAM6_IVYBRIDGE_X, idle_cpu_ivt),
diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c
index f7fcfa8..821919d 100644
--- a/drivers/iio/gyro/bmg160_core.c
+++ b/drivers/iio/gyro/bmg160_core.c
@@ -27,6 +27,7 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/regmap.h>
+#include <linux/delay.h>
#include "bmg160.h"
#define BMG160_IRQ_NAME "bmg160_event"
@@ -52,6 +53,9 @@
#define BMG160_DEF_BW 100
#define BMG160_REG_PMU_BW_RES BIT(7)
+#define BMG160_GYRO_REG_RESET 0x14
+#define BMG160_GYRO_RESET_VAL 0xb6
+
#define BMG160_REG_INT_MAP_0 0x17
#define BMG160_INT_MAP_0_BIT_ANY BIT(1)
@@ -236,6 +240,14 @@ static int bmg160_chip_init(struct bmg160_data *data)
int ret;
unsigned int val;
+ /*
+ * Reset chip to get it in a known good state. A delay of 30ms after
+ * reset is required according to the datasheet.
+ */
+ regmap_write(data->regmap, BMG160_GYRO_REG_RESET,
+ BMG160_GYRO_RESET_VAL);
+ usleep_range(30000, 30700);
+
ret = regmap_read(data->regmap, BMG160_REG_CHIP_ID, &val);
if (ret < 0) {
dev_err(dev, "Error reading reg_chip_id\n");
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index 2909365..9b8079c 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -26,6 +26,7 @@
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
@@ -35,6 +36,7 @@
struct gpio_button_data {
const struct gpio_keys_button *button;
struct input_dev *input;
+ struct gpio_desc *gpiod;
struct timer_list release_timer;
unsigned int release_delay; /* in msecs, for IRQ-only buttons */
@@ -140,7 +142,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata)
*/
disable_irq(bdata->irq);
- if (gpio_is_valid(bdata->button->gpio))
+ if (bdata->gpiod)
cancel_delayed_work_sync(&bdata->work);
else
del_timer_sync(&bdata->release_timer);
@@ -358,19 +360,20 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
const struct gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input;
unsigned int type = button->type ?: EV_KEY;
- int state = gpio_get_value_cansleep(button->gpio);
+ int state;
+ state = gpiod_get_value_cansleep(bdata->gpiod);
if (state < 0) {
- dev_err(input->dev.parent, "failed to get gpio state\n");
+ dev_err(input->dev.parent,
+ "failed to get gpio state: %d\n", state);
return;
}
- state = (state ? 1 : 0) ^ button->active_low;
if (type == EV_ABS) {
if (state)
input_event(input, type, button->code, button->value);
} else {
- input_event(input, type, button->code, !!state);
+ input_event(input, type, button->code, state);
}
input_sync(input);
}
@@ -456,7 +459,7 @@ static void gpio_keys_quiesce_key(void *data)
{
struct gpio_button_data *bdata = data;
- if (gpio_is_valid(bdata->button->gpio))
+ if (bdata->gpiod)
cancel_delayed_work_sync(&bdata->work);
else
del_timer_sync(&bdata->release_timer);
@@ -478,18 +481,30 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
bdata->button = button;
spin_lock_init(&bdata->lock);
+ /*
+ * Legacy GPIO number, so request the GPIO here and
+ * convert it to descriptor.
+ */
if (gpio_is_valid(button->gpio)) {
+ unsigned flags = GPIOF_IN;
- error = devm_gpio_request_one(&pdev->dev, button->gpio,
- GPIOF_IN, desc);
+ if (button->active_low)
+ flags |= GPIOF_ACTIVE_LOW;
+
+ error = devm_gpio_request_one(&pdev->dev, button->gpio, flags,
+ desc);
if (error < 0) {
dev_err(dev, "Failed to request GPIO %d, error %d\n",
button->gpio, error);
return error;
}
+ bdata->gpiod = gpio_to_desc(button->gpio);
+ if (!bdata->gpiod)
+ return -EINVAL;
+
if (button->debounce_interval) {
- error = gpio_set_debounce(button->gpio,
+ error = gpiod_set_debounce(bdata->gpiod,
button->debounce_interval * 1000);
/* use timer if gpiolib doesn't provide debounce */
if (error < 0)
@@ -500,7 +515,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
if (button->irq) {
bdata->irq = button->irq;
} else {
- irq = gpio_to_irq(button->gpio);
+ irq = gpiod_to_irq(bdata->gpiod);
if (irq < 0) {
error = irq;
dev_err(dev,
@@ -575,7 +590,7 @@ static void gpio_keys_report_state(struct gpio_keys_drvdata *ddata)
for (i = 0; i < ddata->pdata->nbuttons; i++) {
struct gpio_button_data *bdata = &ddata->data[i];
- if (gpio_is_valid(bdata->button->gpio))
+ if (bdata->gpiod)
gpio_keys_gpio_report_event(bdata);
}
input_sync(input);
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 34df44c..1864e76 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -828,26 +828,26 @@ static int arm_smmu_power_on_slow(struct arm_smmu_power_resources *pwr)
return 0;
}
- ret = regulator_bulk_enable(pwr->num_gdscs, pwr->gdscs);
+ ret = arm_smmu_request_bus(pwr);
if (ret)
goto out_unlock;
- ret = arm_smmu_request_bus(pwr);
+ ret = regulator_bulk_enable(pwr->num_gdscs, pwr->gdscs);
if (ret)
- goto out_disable_regulators;
+ goto out_disable_bus;
ret = arm_smmu_prepare_clocks(pwr);
if (ret)
- goto out_disable_bus;
+ goto out_disable_regulators;
pwr->power_count = 1;
mutex_unlock(&pwr->power_lock);
return 0;
-out_disable_bus:
- arm_smmu_unrequest_bus(pwr);
out_disable_regulators:
regulator_bulk_disable(pwr->num_gdscs, pwr->gdscs);
+out_disable_bus:
+ arm_smmu_unrequest_bus(pwr);
out_unlock:
mutex_unlock(&pwr->power_lock);
return ret;
@@ -868,9 +868,9 @@ static void arm_smmu_power_off_slow(struct arm_smmu_power_resources *pwr)
}
arm_smmu_unprepare_clocks(pwr);
- arm_smmu_unrequest_bus(pwr);
regulator_bulk_disable(pwr->num_gdscs, pwr->gdscs);
-
+ arm_smmu_unrequest_bus(pwr);
+ pwr->power_count = 0;
mutex_unlock(&pwr->power_lock);
}
@@ -3341,7 +3341,7 @@ static int arm_smmu_init_regulators(struct arm_smmu_power_resources *pwr)
i = 0;
of_property_for_each_string(dev->of_node, "qcom,regulator-names",
prop, cname)
- pwr->gdscs[i].supply = cname;
+ pwr->gdscs[i++].supply = cname;
ret = devm_regulator_bulk_get(dev, pwr->num_gdscs, pwr->gdscs);
return ret;
@@ -3366,7 +3366,6 @@ static int arm_smmu_init_bus_scaling(struct arm_smmu_power_resources *pwr)
pwr->bus_client = msm_bus_scale_register_client(pwr->bus_dt_data);
if (!pwr->bus_client) {
dev_err(dev, "Bus client registration failed\n");
- msm_bus_cl_clear_pdata(pwr->bus_dt_data);
return -EINVAL;
}
@@ -3407,12 +3406,11 @@ static struct arm_smmu_power_resources *arm_smmu_init_power_resources(
}
/*
- * Bus APIs are not devm-safe.
+ * Bus APIs are devm-safe.
*/
static void arm_smmu_exit_power_resources(struct arm_smmu_power_resources *pwr)
{
msm_bus_scale_unregister_client(pwr->bus_client);
- msm_bus_cl_clear_pdata(pwr->bus_dt_data);
}
static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
@@ -3977,6 +3975,8 @@ struct qsmmuv500_archdata {
void __iomem *tcu_base;
u32 version;
};
+#define get_qsmmuv500_archdata(smmu) \
+ ((struct qsmmuv500_archdata *)(smmu->archdata))
struct qsmmuv500_tbu_device {
struct list_head list;
@@ -3997,7 +3997,7 @@ struct qsmmuv500_tbu_device {
static int qsmmuv500_tbu_power_on_all(struct arm_smmu_device *smmu)
{
struct qsmmuv500_tbu_device *tbu;
- struct qsmmuv500_archdata *data = smmu->archdata;
+ struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
int ret = 0;
list_for_each_entry(tbu, &data->tbus, list) {
@@ -4017,7 +4017,7 @@ static int qsmmuv500_tbu_power_on_all(struct arm_smmu_device *smmu)
static void qsmmuv500_tbu_power_off_all(struct arm_smmu_device *smmu)
{
struct qsmmuv500_tbu_device *tbu;
- struct qsmmuv500_archdata *data = smmu->archdata;
+ struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
list_for_each_entry_reverse(tbu, &data->tbus, list) {
arm_smmu_power_off(tbu->pwr);
@@ -4085,7 +4085,7 @@ static void qsmmuv500_tbu_resume(struct qsmmuv500_tbu_device *tbu)
static int qsmmuv500_halt_all(struct arm_smmu_device *smmu)
{
struct qsmmuv500_tbu_device *tbu;
- struct qsmmuv500_archdata *data = smmu->archdata;
+ struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
int ret = 0;
list_for_each_entry(tbu, &data->tbus, list) {
@@ -4106,7 +4106,7 @@ static int qsmmuv500_halt_all(struct arm_smmu_device *smmu)
static void qsmmuv500_resume_all(struct arm_smmu_device *smmu)
{
struct qsmmuv500_tbu_device *tbu;
- struct qsmmuv500_archdata *data = smmu->archdata;
+ struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
list_for_each_entry(tbu, &data->tbus, list) {
qsmmuv500_tbu_resume(tbu);
@@ -4117,14 +4117,14 @@ static struct qsmmuv500_tbu_device *qsmmuv500_find_tbu(
struct arm_smmu_device *smmu, u32 sid)
{
struct qsmmuv500_tbu_device *tbu = NULL;
- struct qsmmuv500_archdata *data = smmu->archdata;
+ struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
list_for_each_entry(tbu, &data->tbus, list) {
if (tbu->sid_start <= sid &&
sid < tbu->sid_start + tbu->num_sids)
- break;
+ return tbu;
}
- return tbu;
+ return NULL;
}
static void qsmmuv500_device_reset(struct arm_smmu_device *smmu)
@@ -4150,7 +4150,7 @@ static int qsmmuv500_ecats_lock(struct arm_smmu_domain *smmu_domain,
unsigned long *flags)
{
struct arm_smmu_device *smmu = tbu->smmu;
- struct qsmmuv500_archdata *data = smmu->archdata;
+ struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
u32 val;
spin_lock_irqsave(&smmu->atos_lock, *flags);
@@ -4174,7 +4174,7 @@ static void qsmmuv500_ecats_unlock(struct arm_smmu_domain *smmu_domain,
unsigned long *flags)
{
struct arm_smmu_device *smmu = tbu->smmu;
- struct qsmmuv500_archdata *data = smmu->archdata;
+ struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
/* The status register is not accessible on version 1.0 */
if (data->version != 0x01000000)
@@ -4326,11 +4326,11 @@ static phys_addr_t qsmmuv500_iova_to_phys_hard(
return qsmmuv500_iova_to_phys(domain, iova, sid);
}
-static int qsmmuv500_tbu_register(struct device *dev, void *data)
+static int qsmmuv500_tbu_register(struct device *dev, void *cookie)
{
- struct arm_smmu_device *smmu = data;
+ struct arm_smmu_device *smmu = cookie;
struct qsmmuv500_tbu_device *tbu;
- struct list_head *list = smmu->archdata;
+ struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
if (!dev->driver) {
dev_err(dev, "TBU failed probe, QSMMUV500 cannot continue!\n");
@@ -4341,7 +4341,7 @@ static int qsmmuv500_tbu_register(struct device *dev, void *data)
INIT_LIST_HEAD(&tbu->list);
tbu->smmu = smmu;
- list_add(&tbu->list, list);
+ list_add(&tbu->list, &data->tbus);
return 0;
}
diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c
index 5730126..181e889 100644
--- a/drivers/iommu/iommu-debug.c
+++ b/drivers/iommu/iommu-debug.c
@@ -124,29 +124,6 @@ static const struct file_operations iommu_debug_attachment_info_fops = {
.release = single_release,
};
-static ssize_t iommu_debug_attachment_trigger_fault_write(
- struct file *file, const char __user *ubuf, size_t count,
- loff_t *offset)
-{
- struct iommu_debug_attachment *attach = file->private_data;
- unsigned long flags;
-
- if (kstrtoul_from_user(ubuf, count, 0, &flags)) {
- pr_err("Invalid flags format\n");
- return -EFAULT;
- }
-
- iommu_trigger_fault(attach->domain, flags);
-
- return count;
-}
-
-static const struct file_operations
-iommu_debug_attachment_trigger_fault_fops = {
- .open = simple_open,
- .write = iommu_debug_attachment_trigger_fault_write,
-};
-
static ssize_t iommu_debug_attachment_reg_offset_write(
struct file *file, const char __user *ubuf, size_t count,
loff_t *offset)
@@ -271,14 +248,6 @@ static int iommu_debug_attach_add_debugfs(
}
if (!debugfs_create_file(
- "trigger_fault", S_IRUSR, attach->dentry, attach,
- &iommu_debug_attachment_trigger_fault_fops)) {
- pr_err("Couldn't create iommu/attachments/%s/trigger_fault debugfs file for domain 0x%p\n",
- dev_name(dev), domain);
- goto err_rmdir;
- }
-
- if (!debugfs_create_file(
"reg_offset", S_IRUSR, attach->dentry, attach,
&iommu_debug_attachment_reg_offset_fops)) {
pr_err("Couldn't create iommu/attachments/%s/reg_offset debugfs file for domain 0x%p\n",
@@ -1859,6 +1828,33 @@ static const struct file_operations iommu_debug_config_clocks_fops = {
.write = iommu_debug_config_clocks_write,
};
+static ssize_t iommu_debug_trigger_fault_write(
+ struct file *file, const char __user *ubuf, size_t count,
+ loff_t *offset)
+{
+ struct iommu_debug_device *ddev = file->private_data;
+ unsigned long flags;
+
+ if (!ddev->domain) {
+ pr_err("No domain. Did you already attach?\n");
+ return -EINVAL;
+ }
+
+ if (kstrtoul_from_user(ubuf, count, 0, &flags)) {
+ pr_err("Invalid flags format\n");
+ return -EFAULT;
+ }
+
+ iommu_trigger_fault(ddev->domain, flags);
+
+ return count;
+}
+
+static const struct file_operations iommu_debug_trigger_fault_fops = {
+ .open = simple_open,
+ .write = iommu_debug_trigger_fault_write,
+};
+
/*
* The following will only work for drivers that implement the generic
* device tree bindings described in
@@ -1974,6 +1970,13 @@ static int snarf_iommu_devices(struct device *dev, void *ignored)
goto err_rmdir;
}
+ if (!debugfs_create_file("trigger-fault", 0200, dir, ddev,
+ &iommu_debug_trigger_fault_fops)) {
+ pr_err("Couldn't create iommu/devices/%s/trigger-fault debugfs file\n",
+ dev_name(dev));
+ goto err_rmdir;
+ }
+
list_add(&ddev->list, &iommu_debug_devices);
return 0;
diff --git a/drivers/mailbox/qti-tcs.c b/drivers/mailbox/qti-tcs.c
index 1c73c5a2..0a802fd 100644
--- a/drivers/mailbox/qti-tcs.c
+++ b/drivers/mailbox/qti-tcs.c
@@ -13,6 +13,7 @@
#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__
+#include <linux/atomic.h>
#include <linux/bitmap.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
@@ -132,6 +133,7 @@ struct tcs_drv {
int num_tcs;
struct workqueue_struct *wq;
struct tcs_response_pool *resp_pool;
+ atomic_t tcs_in_use[TCS_TYPE_NR * MAX_TCS_PER_TYPE];
};
static void tcs_notify_tx_done(unsigned long data);
@@ -228,9 +230,12 @@ static inline void write_tcs_reg_sync(void __iomem *base, int reg, int m, int n,
} while (1);
}
-static inline bool tcs_is_free(void __iomem *base, int m)
+static inline bool tcs_is_free(struct tcs_drv *drv, int m)
{
- return read_tcs_reg(base, TCS_DRV_STATUS, m, 0);
+ void __iomem *base = drv->reg_base;
+
+ return read_tcs_reg(base, TCS_DRV_STATUS, m, 0) &&
+ !atomic_read(&drv->tcs_in_use[m]);
}
static inline struct tcs_mbox *get_tcs_from_index(struct tcs_drv *drv, int m)
@@ -400,6 +405,13 @@ static irqreturn_t tcs_irq_handler(int irq, void *p)
/* Clear the TCS IRQ status */
write_tcs_reg(base, TCS_DRV_IRQ_CLEAR, 0, 0, irq_clear);
+ /* Mark the TCS as free */
+ for (m = 0; irq_status >= BIT(m); m++) {
+ if (!(irq_status & BIT(m)))
+ continue;
+ atomic_set(&drv->tcs_in_use[m], 0);
+ }
+
return IRQ_HANDLED;
}
@@ -440,14 +452,13 @@ static void tcs_notify_timeout(struct work_struct *work)
struct tcs_mbox_msg *msg = resp->msg;
struct tcs_drv *drv = resp->drv;
int m = resp->m;
- int err = -EIO;
/*
* In case the RPMH resource fails to respond to the completion
* request, the TCS would be blocked forever waiting on the response.
* There is no way to recover from this case.
*/
- if (!tcs_is_free(drv->reg_base, m)) {
+ if (!tcs_is_free(drv, m)) {
bool pending = false;
struct tcs_cmd *cmd;
int i;
@@ -471,7 +482,7 @@ static void tcs_notify_timeout(struct work_struct *work)
}
free_response_to_pool(resp);
- mbox_notify_tx_done(chan, msg, -1, err);
+ mbox_notify_tx_done(chan, msg, -1, -ETIMEDOUT);
}
static void __tcs_buffer_write(struct tcs_drv *drv, int d, int m, int n,
@@ -514,8 +525,8 @@ static void __tcs_buffer_write(struct tcs_drv *drv, int d, int m, int n,
write_tcs_reg(base, TCS_DRV_CMD_ENABLE, m, 0, cmd_enable);
if (trigger) {
- /* Clear pending interrupt bits for this TCS, OK to not lock */
- write_tcs_reg(base, TCS_DRV_IRQ_CLEAR, 0, 0, BIT(m));
+ /* Mark the TCS as busy */
+ atomic_set(&drv->tcs_in_use[m], 1);
/* HW req: Clear the DRV_CONTROL and enable TCS again */
write_tcs_reg_sync(base, TCS_DRV_CONTROL, m, 0, 0);
write_tcs_reg_sync(base, TCS_DRV_CONTROL, m, 0, enable);
@@ -543,7 +554,7 @@ static bool tcs_drv_is_idle(struct mbox_controller *mbox)
tcs = get_tcs_of_type(drv, WAKE_TCS);
for (m = tcs->tcs_offset; m < tcs->tcs_offset + tcs->num_tcs; m++)
- if (!tcs_is_free(drv->reg_base, m))
+ if (!tcs_is_free(drv, m))
return false;
return true;
@@ -561,7 +572,7 @@ static void wait_for_req_inflight(struct tcs_drv *drv, struct tcs_mbox *tcs,
for (i = 1; i > tcs->tcs_mask; i = i << 1) {
if (!(tcs->tcs_mask & i))
continue;
- if (tcs_is_free(drv->reg_base, i))
+ if (tcs_is_free(drv, i))
continue;
curr_enabled = read_tcs_reg(drv->reg_base,
TCS_DRV_CMD_ENABLE, i, 0);
@@ -589,11 +600,11 @@ static int find_free_tcs(struct tcs_mbox *tcs)
/* Loop until we find a free AMC */
do {
- if (tcs_is_free(tcs->drv->reg_base, tcs->tcs_offset + m)) {
+ if (tcs_is_free(tcs->drv, tcs->tcs_offset + m)) {
slot = m * tcs->ncpt;
break;
}
- if (++m > tcs->num_tcs)
+ if (++m >= tcs->num_tcs)
m = 0;
cpu_relax();
} while (1);
@@ -721,7 +732,7 @@ static int tcs_mbox_write(struct mbox_chan *chan, struct tcs_mbox_msg *msg,
/* Block, if we have an address from the msg in flight */
wait_for_req_inflight(drv, tcs, msg);
/* If the TCS is busy there is nothing to do but spin wait */
- while (!tcs_is_free(drv->reg_base, m))
+ while (!tcs_is_free(drv, m))
cpu_relax();
}
@@ -759,7 +770,7 @@ static int tcs_mbox_invalidate(struct mbox_chan *chan)
for (i = 0; i < tcs->num_tcs; i++) {
m = i + tcs->tcs_offset;
spin_lock(&tcs->tcs_m_lock[i]);
- while (!tcs_is_free(drv->reg_base, m))
+ while (!tcs_is_free(drv, m))
cpu_relax();
__tcs_buffer_invalidate(drv->reg_base, m);
spin_unlock(&tcs->tcs_m_lock[i]);
@@ -1029,6 +1040,8 @@ static int tcs_drv_probe(struct platform_device *pdev)
tcs->ncpt = (tcs->type == CONTROL_TCS) ? TCS_HIDDEN_MAX_SLOTS
: ncpt;
spin_lock_init(&tcs->tcs_lock);
+ for (j = 0; j < ARRAY_SIZE(tcs->tcs_m_lock); j++)
+ spin_lock_init(&tcs->tcs_m_lock[j]);
if (tcs->num_tcs <= 0 || tcs->type == CONTROL_TCS)
continue;
@@ -1048,8 +1061,6 @@ static int tcs_drv_probe(struct platform_device *pdev)
if (!tcs->cmd_addr)
return -ENOMEM;
- for (j = 0; j < tcs->num_tcs; j++)
- spin_lock_init(&tcs->tcs_m_lock[j]);
}
/* Allocate only that many channels specified in DT for our MBOX */
@@ -1120,6 +1131,9 @@ static int tcs_drv_probe(struct platform_device *pdev)
drv->tcs[WAKE_TCS].tcs_mask;
write_tcs_reg(drv->reg_base, TCS_DRV_IRQ_ENABLE, 0, 0, irq_mask);
+ for (i = 0; i < ARRAY_SIZE(drv->tcs_in_use); i++)
+ atomic_set(&drv->tcs_in_use[i], 0);
+
ret = mbox_controller_register(&drv->mbox);
if (ret)
return ret;
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index 15daa36..ee75e35 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -3589,7 +3589,7 @@ static int raid_preresume(struct dm_target *ti)
return r;
/* Resize bitmap to adjust to changed region size (aka MD bitmap chunksize) */
- if (test_bit(RT_FLAG_RS_BITMAP_LOADED, &rs->runtime_flags) &&
+ if (test_bit(RT_FLAG_RS_BITMAP_LOADED, &rs->runtime_flags) && mddev->bitmap &&
mddev->bitmap_info.chunksize != to_bytes(rs->requested_bitmap_chunk_sectors)) {
r = bitmap_resize(mddev->bitmap, mddev->dev_sectors,
to_bytes(rs->requested_bitmap_chunk_sectors), 0);
diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c
index a8d4d2f..3b62315 100644
--- a/drivers/md/dm-verity-fec.c
+++ b/drivers/md/dm-verity-fec.c
@@ -147,8 +147,6 @@ static int fec_decode_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio,
block = fec_buffer_rs_block(v, fio, n, i);
res = fec_decode_rs8(v, fio, block, &par[offset], neras);
if (res < 0) {
- dm_bufio_release(buf);
-
r = res;
goto error;
}
@@ -173,6 +171,8 @@ static int fec_decode_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio,
done:
r = corrected;
error:
+ dm_bufio_release(buf);
+
if (r < 0 && neras)
DMERR_LIMIT("%s: FEC %llu: failed to correct: %d",
v->data_dev->name, (unsigned long long)rsb, r);
@@ -272,7 +272,7 @@ static int fec_read_bufs(struct dm_verity *v, struct dm_verity_io *io,
&is_zero) == 0) {
/* skip known zero blocks entirely */
if (is_zero)
- continue;
+ goto done;
/*
* skip if we have already found the theoretical
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index 3378ff0..b424fbb 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -300,8 +300,8 @@ static int hfi_process_session_error(u32 device_id,
cmd_done.device_id = device_id;
cmd_done.session_id = (void *)(uintptr_t)pkt->session_id;
cmd_done.status = hfi_map_err_status(pkt->event_data1);
- dprintk(VIDC_INFO, "Received: SESSION_ERROR with event id : %d\n",
- pkt->event_data1);
+ dprintk(VIDC_INFO, "Received: SESSION_ERROR with event id : %#x %#x\n",
+ pkt->event_data1, pkt->event_data2);
switch (pkt->event_data1) {
case HFI_ERR_SESSION_INVALID_SCALE_FACTOR:
case HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE:
@@ -313,7 +313,9 @@ static int hfi_process_session_error(u32 device_id,
info->response.cmd = cmd_done;
return 0;
default:
- dprintk(VIDC_ERR, "HFI_EVENT_SESSION_ERROR\n");
+ dprintk(VIDC_ERR,
+ "HFI_EVENT_SESSION_ERROR: data1 %#x, data2 %#x\n",
+ pkt->event_data1, pkt->event_data2);
info->response_type = HAL_SESSION_ERROR;
info->response.cmd = cmd_done;
return 0;
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index 7c99e90..85e721f 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -522,7 +522,7 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
inst->prop.width[CAPTURE_PORT] == f->fmt.pix_mp.width &&
inst->prop.height[CAPTURE_PORT] ==
f->fmt.pix_mp.height) {
- dprintk(VIDC_DBG, "Thank you : Nothing changed\n");
+ dprintk(VIDC_DBG, "No change in CAPTURE port params\n");
return 0;
}
memcpy(&inst->fmts[fmt->type], fmt,
@@ -564,6 +564,8 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
inst->bufq[CAPTURE_PORT].plane_sizes[i] =
f->fmt.pix_mp.plane_fmt[i].sizeimage;
}
+
+ rc = msm_comm_try_get_bufreqs(inst);
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats,
@@ -590,7 +592,7 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
inst->prop.width[OUTPUT_PORT] == f->fmt.pix_mp.width &&
inst->prop.height[OUTPUT_PORT] ==
f->fmt.pix_mp.height) {
- dprintk(VIDC_DBG, "Thank you : Nothing changed\n");
+ dprintk(VIDC_DBG, "No change in OUTPUT port params\n");
return 0;
}
inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width;
@@ -890,6 +892,7 @@ int msm_vdec_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
"Failed setting OUTPUT2 size : %d\n",
rc);
+ rc = msm_comm_try_get_bufreqs(inst);
break;
default:
dprintk(VIDC_ERR,
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 12f4c54..32611e4 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -352,20 +352,6 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.qmenu = mpeg_video_rate_control,
},
{
- .id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
- .name = "Bitrate Control",
- .type = V4L2_CTRL_TYPE_MENU,
- .minimum = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
- .maximum = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
- .default_value = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
- .step = 0,
- .menu_skip_mask = ~(
- (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) |
- (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
- ),
- .qmenu = NULL,
- },
- {
.id = V4L2_CID_MPEG_VIDEO_BITRATE,
.name = "Bit Rate",
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -822,7 +808,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.name = "Layer ID for different settings",
.type = V4L2_CTRL_TYPE_INTEGER,
.minimum = 0,
- .maximum = 6,
+ .maximum = MSM_VIDC_ALL_LAYER_ID,
.default_value = 0,
.step = 1,
.qmenu = NULL,
@@ -1269,69 +1255,17 @@ int msm_venc_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
pdata = &request_iframe;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL:
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
{
- int final_mode = 0;
- struct v4l2_ctrl update_ctrl;
-
- update_ctrl.id = 0;
- update_ctrl.val = 0;
-
- /* V4L2_CID_MPEG_VIDEO_BITRATE_MODE and _RATE_CONTROL
- * manipulate the same thing. If one control's state
- * changes, try to mirror the state in the other control's
- * value
- */
- if (ctrl->id == V4L2_CID_MPEG_VIDEO_BITRATE_MODE) {
- if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) {
- final_mode = HAL_RATE_CONTROL_VBR_CFR;
- update_ctrl.val =
- V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_CFR;
- } else {/* ...if (ctrl->val == _BITRATE_MODE_CBR) */
- final_mode = HAL_RATE_CONTROL_CBR_CFR;
- update_ctrl.val =
- V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR;
- }
-
- update_ctrl.id = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL;
-
- } else if (ctrl->id == V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL) {
- switch (ctrl->val) {
- case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF:
- case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_VFR:
- case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_CFR:
- update_ctrl.val =
- V4L2_MPEG_VIDEO_BITRATE_MODE_VBR;
- break;
- case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_VFR:
- case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR:
- case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR:
- case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR:
- update_ctrl.val =
- V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
- break;
- }
-
- final_mode = ctrl->val;
- update_ctrl.id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE;
- }
-
- if (update_ctrl.id) {
- temp_ctrl = TRY_GET_CTRL(update_ctrl.id);
- temp_ctrl->val = update_ctrl.val;
- }
-
property_id = HAL_PARAM_VENC_RATE_CONTROL;
- property_val = final_mode;
+ property_val = ctrl->val;
pdata = &property_val;
-
break;
}
case V4L2_CID_MPEG_VIDEO_BITRATE:
{
property_id = HAL_CONFIG_VENC_TARGET_BITRATE;
bitrate.bit_rate = ctrl->val;
- bitrate.layer_id = MSM_VIDC_ALL_LAYER_ID;
+ bitrate.layer_id = 0;
pdata = &bitrate;
inst->bitrate = ctrl->val;
break;
@@ -2229,6 +2163,7 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
struct hfi_device *hdev;
int extra_idx = 0, i = 0;
struct hal_buffer_requirements *buff_req_buffer;
+ struct hal_frame_size frame_sz;
if (!inst || !f) {
dprintk(VIDC_ERR,
@@ -2267,6 +2202,19 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width;
inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height;
+ frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
+ frame_sz.width = inst->prop.width[CAPTURE_PORT];
+ frame_sz.height = inst->prop.height[CAPTURE_PORT];
+ dprintk(VIDC_DBG, "CAPTURE port width = %d, height = %d\n",
+ frame_sz.width, frame_sz.height);
+ rc = call_hfi_op(hdev, session_set_property, (void *)
+ inst->session, HAL_PARAM_FRAME_SIZE, &frame_sz);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to set framesize for CAPTURE port\n");
+ goto exit;
+ }
+
rc = msm_comm_try_get_bufreqs(inst);
if (rc) {
dprintk(VIDC_ERR,
@@ -2315,7 +2263,7 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
frame_sz.buffer_type = HAL_BUFFER_INPUT;
frame_sz.width = inst->prop.width[OUTPUT_PORT];
frame_sz.height = inst->prop.height[OUTPUT_PORT];
- dprintk(VIDC_DBG, "width = %d, height = %d\n",
+ dprintk(VIDC_DBG, "OUTPUT port width = %d, height = %d\n",
frame_sz.width, frame_sz.height);
rc = call_hfi_op(hdev, session_set_property, (void *)
inst->session, HAL_PARAM_FRAME_SIZE, &frame_sz);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index cce1a8e..d96963c 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -26,6 +26,9 @@
#define MAX_EVENTS 30
+static int try_get_ctrl(struct msm_vidc_inst *inst,
+ struct v4l2_ctrl *ctrl);
+
static int get_poll_flags(void *instance)
{
struct msm_vidc_inst *inst = instance;
@@ -254,11 +257,20 @@ EXPORT_SYMBOL(msm_vidc_s_ctrl);
int msm_vidc_g_ctrl(void *instance, struct v4l2_control *control)
{
struct msm_vidc_inst *inst = instance;
+ struct v4l2_ctrl *ctrl = NULL;
+ int rc = 0;
if (!inst || !control)
return -EINVAL;
- return msm_comm_g_ctrl(instance, control);
+ ctrl = v4l2_ctrl_find(&inst->ctrl_handler, control->id);
+ if (ctrl) {
+ rc = try_get_ctrl(inst, ctrl);
+ if (!rc)
+ control->value = ctrl->val;
+ }
+
+ return rc;
}
EXPORT_SYMBOL(msm_vidc_g_ctrl);
@@ -1006,12 +1018,6 @@ int msm_vidc_dqbuf(void *instance, struct v4l2_buffer *b)
b->m.planes[i].m.userptr = buffer_info->uvaddr[i];
b->m.planes[i].reserved[0] = buffer_info->fd[i];
b->m.planes[i].reserved[1] = buffer_info->buff_off[i];
- if (!b->m.planes[i].m.userptr) {
- dprintk(VIDC_ERR,
- "%s: Failed to find user virtual address, %#lx, %d, %d\n",
- __func__, b->m.planes[i].m.userptr, b->type, i);
- return -EINVAL;
- }
}
if (!buffer_info) {
@@ -1222,21 +1228,22 @@ static int msm_vidc_queue_setup(struct vb2_queue *q,
HAL_BUFFER_INPUT);
return -EINVAL;
}
- if (*num_buffers < bufreq->buffer_count_actual) {
+ if (*num_buffers < bufreq->buffer_count_min_host) {
dprintk(VIDC_ERR,
"Invalid parameters : Req = %d Act = %d\n",
- *num_buffers, bufreq->buffer_count_actual);
+ *num_buffers, bufreq->buffer_count_min_host);
return -EINVAL;
}
*num_planes = inst->bufq[OUTPUT_PORT].num_planes;
if (*num_buffers < MIN_NUM_OUTPUT_BUFFERS ||
*num_buffers > MAX_NUM_OUTPUT_BUFFERS)
- *num_buffers = MIN_NUM_OUTPUT_BUFFERS;
+ bufreq->buffer_count_actual = *num_buffers =
+ MIN_NUM_OUTPUT_BUFFERS;
for (i = 0; i < *num_planes; i++)
sizes[i] = inst->bufq[OUTPUT_PORT].plane_sizes[i];
bufreq->buffer_count_actual = *num_buffers;
- rc = set_buffer_count(inst, bufreq->buffer_count_min_host,
+ rc = set_buffer_count(inst, bufreq->buffer_count_actual,
*num_buffers, HAL_BUFFER_INPUT);
}
@@ -1251,22 +1258,27 @@ static int msm_vidc_queue_setup(struct vb2_queue *q,
buffer_type);
return -EINVAL;
}
- if (*num_buffers < bufreq->buffer_count_actual) {
- dprintk(VIDC_ERR,
- "Invalid parameters : Req = %d Act = %d\n",
- *num_buffers, bufreq->buffer_count_actual);
- return -EINVAL;
+ if (inst->session_type != MSM_VIDC_DECODER &&
+ inst->state > MSM_VIDC_LOAD_RESOURCES_DONE) {
+ if (*num_buffers < bufreq->buffer_count_min_host) {
+ dprintk(VIDC_ERR,
+ "Invalid parameters : Req = %d Act = %d\n",
+ *num_buffers,
+ bufreq->buffer_count_min_host);
+ return -EINVAL;
+ }
}
*num_planes = inst->bufq[CAPTURE_PORT].num_planes;
if (*num_buffers < MIN_NUM_CAPTURE_BUFFERS ||
*num_buffers > MAX_NUM_CAPTURE_BUFFERS)
- *num_buffers = MIN_NUM_CAPTURE_BUFFERS;
+ bufreq->buffer_count_actual = *num_buffers =
+ MIN_NUM_CAPTURE_BUFFERS;
for (i = 0; i < *num_planes; i++)
sizes[i] = inst->bufq[CAPTURE_PORT].plane_sizes[i];
bufreq->buffer_count_actual = *num_buffers;
- rc = set_buffer_count(inst, bufreq->buffer_count_min_host,
+ rc = set_buffer_count(inst, bufreq->buffer_count_actual,
*num_buffers, buffer_type);
}
break;
@@ -1290,24 +1302,40 @@ static inline int msm_vidc_verify_buffer_counts(struct msm_vidc_inst *inst)
{
int rc = 0, i = 0;
+ /* For decoder No need to sanity till LOAD_RESOURCES */
+ if (inst->session_type == MSM_VIDC_DECODER &&
+ inst->state < MSM_VIDC_LOAD_RESOURCES_DONE) {
+ dprintk(VIDC_DBG,
+ "No need to verify buffer counts : %pK\n", inst);
+ return 0;
+ }
+
for (i = 0; i < HAL_BUFFER_MAX; i++) {
struct hal_buffer_requirements *req = &inst->buff_req.buffer[i];
- dprintk(VIDC_DBG, "Verifying Buffer : %d\n", req->buffer_type);
- if (!req ||
- req->buffer_count_actual < req->buffer_count_min_host ||
- req->buffer_count_min_host < req->buffer_count_min) {
- dprintk(VIDC_ERR, "Invalid data : Counts mismatch\n");
- dprintk(VIDC_ERR,
- "Min Count = %d ", req->buffer_count_min);
- dprintk(VIDC_ERR,
- "Min Host Count = %d ",
- req->buffer_count_min_host);
- dprintk(VIDC_ERR,
- "Min Actual Count = %d\n",
- req->buffer_count_actual);
- rc = -EINVAL;
- break;
+ if (req && (msm_comm_get_hal_output_buffer(inst) ==
+ req->buffer_type)) {
+ dprintk(VIDC_DBG, "Verifying Buffer : %d\n",
+ req->buffer_type);
+ if (req->buffer_count_actual <
+ req->buffer_count_min_host ||
+ req->buffer_count_min_host <
+ req->buffer_count_min) {
+
+ dprintk(VIDC_ERR,
+ "Invalid data : Counts mismatch\n");
+ dprintk(VIDC_ERR,
+ "Min Count = %d ",
+ req->buffer_count_min);
+ dprintk(VIDC_ERR,
+ "Min Host Count = %d ",
+ req->buffer_count_min_host);
+ dprintk(VIDC_ERR,
+ "Min Actual Count = %d\n",
+ req->buffer_count_actual);
+ rc = -EINVAL;
+ break;
+ }
}
}
return rc;
@@ -1394,18 +1422,9 @@ static inline int start_streaming(struct msm_vidc_inst *inst)
* - v4l2 client issues CONTINUE to firmware to resume decoding of
* submitted ETBs.
*/
- if (inst->in_reconfig) {
- dprintk(VIDC_DBG, "send session_continue after reconfig\n");
- rc = call_hfi_op(hdev, session_continue,
- (void *) inst->session);
- if (rc) {
- dprintk(VIDC_ERR,
- "%s - failed to send session_continue\n",
- __func__);
- goto fail_start;
- }
- }
- inst->in_reconfig = false;
+ rc = msm_comm_session_continue(inst);
+ if (rc)
+ goto fail_start;
msm_comm_scale_clocks_and_bus(inst);
@@ -1729,7 +1748,7 @@ static int set_actual_buffer_count(struct msm_vidc_inst *inst,
}
-static int msm_vdec_get_count(struct msm_vidc_inst *inst,
+static int msm_vidc_get_count(struct msm_vidc_inst *inst,
struct v4l2_ctrl *ctrl)
{
int rc = 0;
@@ -1750,15 +1769,19 @@ static int msm_vdec_get_count(struct msm_vidc_inst *inst,
}
if (ctrl->val > bufreq->buffer_count_min_host) {
dprintk(VIDC_DBG,
- "Interesting : Usually shouldn't happen\n");
+ "Buffer count Host changed from %d to %d\n",
+ bufreq->buffer_count_min_host,
+ ctrl->val);
bufreq->buffer_count_min_host = ctrl->val;
+ } else {
+ ctrl->val = bufreq->buffer_count_min_host;
}
- rc = set_actual_buffer_count(inst, ctrl->val,
+ rc = set_actual_buffer_count(inst,
+ bufreq->buffer_count_min_host,
HAL_BUFFER_INPUT);
return rc;
} else if (ctrl->id == V4L2_CID_MIN_BUFFERS_FOR_CAPTURE) {
- int count = 0;
buffer_type = msm_comm_get_hal_output_buffer(inst);
bufreq = get_buff_req_buffer(inst,
@@ -1775,7 +1798,7 @@ static int msm_vdec_get_count(struct msm_vidc_inst *inst,
else
return 0;
}
- count = bufreq->buffer_count_min_host;
+
if (inst->in_reconfig) {
rc = msm_comm_try_get_bufreqs(inst);
@@ -1787,21 +1810,28 @@ static int msm_vdec_get_count(struct msm_vidc_inst *inst,
buffer_type);
return 0;
}
- newreq->buffer_count_min_host = count =
- newreq->buffer_count_min +
- msm_dcvs_get_extra_buff_count(inst);
+ ctrl->val = newreq->buffer_count_min;
}
- if (!inst->in_reconfig &&
+ if (inst->session_type == MSM_VIDC_DECODER &&
+ !inst->in_reconfig &&
inst->state < MSM_VIDC_LOAD_RESOURCES_DONE) {
- dprintk(VIDC_DBG, "Clients will correct this\n");
- rc = set_actual_buffer_count(inst, ctrl->val,
- buffer_type);
+ dprintk(VIDC_DBG,
+ "Clients updates Buffer count from %d to %d\n",
+ bufreq->buffer_count_min_host, ctrl->val);
bufreq->buffer_count_min_host = ctrl->val;
- return 0;
}
- bufreq->buffer_count_min_host = ctrl->val = count;
- rc = set_actual_buffer_count(inst, ctrl->val,
- buffer_type);
+ if (ctrl->val > bufreq->buffer_count_min_host) {
+ dprintk(VIDC_DBG,
+ "Buffer count Host changed from %d to %d\n",
+ bufreq->buffer_count_min_host,
+ ctrl->val);
+ bufreq->buffer_count_min_host = ctrl->val;
+ } else {
+ ctrl->val = bufreq->buffer_count_min_host;
+ }
+ rc = set_actual_buffer_count(inst,
+ bufreq->buffer_count_min_host,
+ HAL_BUFFER_OUTPUT);
return rc;
}
@@ -1817,7 +1847,6 @@ static int try_get_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
* lower level code that attempts to do g_ctrl() will end up deadlocking
* us.
*/
- v4l2_ctrl_unlock(ctrl);
switch (ctrl->id) {
@@ -1838,7 +1867,7 @@ static int try_get_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
- rc = msm_vdec_get_count(inst, ctrl);
+ rc = msm_vidc_get_count(inst, ctrl);
break;
default:
/*
@@ -1846,8 +1875,7 @@ static int try_get_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
* modify ctrl->value
*/
break;
- }
- v4l2_ctrl_lock(ctrl);
+}
return rc;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
index 70427d3..1c78a45 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
@@ -422,19 +422,29 @@ int msm_comm_scale_clocks_and_bus(struct msm_vidc_inst *inst)
int msm_dcvs_try_enable(struct msm_vidc_inst *inst)
{
+ bool force_disable = false;
+
if (!inst) {
dprintk(VIDC_ERR, "%s: Invalid args: %p\n", __func__, inst);
return -EINVAL;
}
- if (inst->flags & VIDC_THUMBNAIL) {
+
+ force_disable = inst->session_type == MSM_VIDC_ENCODER ?
+ !msm_vidc_enc_dcvs_mode :
+ !msm_vidc_dec_dcvs_mode;
+
+ if (force_disable || inst->flags & VIDC_THUMBNAIL) {
dprintk(VIDC_PROF, "Thumbnail sessions don't need DCVS : %pK\n",
inst);
+ inst->dcvs.extra_capture_buffer_count = 0;
+ inst->dcvs.extra_output_buffer_count = 0;
return false;
}
inst->dcvs_mode = true;
// TODO : Update with proper number based on on-target tuning.
- inst->dcvs.extra_buffer_count = DCVS_DEC_EXTRA_OUTPUT_BUFFERS;
+ inst->dcvs.extra_capture_buffer_count = DCVS_DEC_EXTRA_OUTPUT_BUFFERS;
+ inst->dcvs.extra_output_buffer_count = DCVS_DEC_EXTRA_OUTPUT_BUFFERS;
return true;
}
@@ -566,14 +576,17 @@ void msm_dcvs_init(struct msm_vidc_inst *inst)
msm_dcvs_print_dcvs_stats(dcvs);
}
-int msm_dcvs_get_extra_buff_count(struct msm_vidc_inst *inst)
+int msm_vidc_get_extra_buff_count(struct msm_vidc_inst *inst,
+ enum hal_buffer buffer_type)
{
if (!inst) {
dprintk(VIDC_ERR, "%s Invalid args\n", __func__);
return 0;
}
- return inst->dcvs.extra_buffer_count;
+ return buffer_type == HAL_BUFFER_INPUT ?
+ inst->dcvs.extra_output_buffer_count :
+ inst->dcvs.extra_capture_buffer_count;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.h b/drivers/media/platform/msm/vidc/msm_vidc_clocks.h
index 0229ccb..79fd8f6 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_clocks.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_clocks.h
@@ -32,7 +32,8 @@
#define DCVS_BUFFER_SAFEGUARD (DCVS_DEC_EXTRA_OUTPUT_BUFFERS - 1)
void msm_dcvs_init(struct msm_vidc_inst *inst);
-int msm_dcvs_get_extra_buff_count(struct msm_vidc_inst *inst);
+int msm_vidc_get_extra_buff_count(struct msm_vidc_inst *inst,
+ enum hal_buffer buffer_type);
int msm_dcvs_try_enable(struct msm_vidc_inst *inst);
int msm_comm_scale_clocks_and_bus(struct msm_vidc_inst *inst);
int msm_comm_init_clocks_and_bus_data(struct msm_vidc_inst *inst);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 274eed7..853edf5 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -41,6 +41,8 @@ const char *const mpeg_video_vidc_extradata[] = {
"Extradata none",
"Extradata MB Quantization",
"Extradata Interlace Video",
+ "Reserved",
+ "Reserved",
"Extradata timestamp",
"Extradata S3D Frame Packing",
"Extradata Frame Rate",
@@ -1165,53 +1167,70 @@ static int msm_vidc_comm_update_ctrl(struct msm_vidc_inst *inst,
static void msm_vidc_comm_update_ctrl_limits(struct msm_vidc_inst *inst)
{
- msm_vidc_comm_update_ctrl(inst,
- V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE,
- &inst->capability.hier_p_hybrid);
- msm_vidc_comm_update_ctrl(inst,
- V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS,
- &inst->capability.hier_b);
- msm_vidc_comm_update_ctrl(inst,
- V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS,
- &inst->capability.hier_p);
- msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDEO_BITRATE,
- &inst->capability.bitrate);
- msm_vidc_comm_update_ctrl(inst,
- V4L2_CID_MPEG_VIDC_VENC_PARAM_LAYER_BITRATE,
- &inst->capability.bitrate);
- msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
- &inst->capability.peakbitrate);
- msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP,
- &inst->capability.i_qp);
- msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP,
- &inst->capability.p_qp);
- msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP,
- &inst->capability.b_qp);
- msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MIN,
- &inst->capability.i_qp);
- msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MIN,
- &inst->capability.p_qp);
- msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MIN,
- &inst->capability.b_qp);
- msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MAX,
- &inst->capability.i_qp);
- msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MAX,
- &inst->capability.p_qp);
- msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MAX,
- &inst->capability.b_qp);
- msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_BLUR_WIDTH,
- &inst->capability.blur_width);
- msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_BLUR_HEIGHT,
- &inst->capability.blur_height);
- msm_vidc_comm_update_ctrl(inst,
- V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES,
- &inst->capability.slice_bytes);
- msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB,
- &inst->capability.slice_mbs);
- msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT,
- &inst->capability.ltr_count);
- msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES,
- &inst->capability.bframe);
+ if (inst->session_type == MSM_VIDC_ENCODER) {
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE,
+ &inst->capability.hier_p_hybrid);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS,
+ &inst->capability.hier_b);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS,
+ &inst->capability.hier_p);
+ msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDEO_BITRATE,
+ &inst->capability.bitrate);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDC_VENC_PARAM_LAYER_BITRATE,
+ &inst->capability.bitrate);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+ &inst->capability.peakbitrate);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP,
+ &inst->capability.i_qp);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP,
+ &inst->capability.p_qp);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP,
+ &inst->capability.b_qp);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MIN,
+ &inst->capability.i_qp);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MIN,
+ &inst->capability.p_qp);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MIN,
+ &inst->capability.b_qp);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MAX,
+ &inst->capability.i_qp);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MAX,
+ &inst->capability.p_qp);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MAX,
+ &inst->capability.b_qp);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDC_VIDEO_BLUR_WIDTH,
+ &inst->capability.blur_width);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDC_VIDEO_BLUR_HEIGHT,
+ &inst->capability.blur_height);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES,
+ &inst->capability.slice_bytes);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB,
+ &inst->capability.slice_mbs);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT,
+ &inst->capability.ltr_count);
+ msm_vidc_comm_update_ctrl(inst,
+ V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES,
+ &inst->capability.bframe);
+ }
}
static void handle_session_init_done(enum hal_command_response cmd, void *data)
@@ -1359,7 +1378,7 @@ static void handle_event_change(enum hal_command_response cmd, void *data)
switch (event_notify->hal_event_type) {
case HAL_EVENT_SEQ_CHANGED_SUFFICIENT_RESOURCES:
- event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
+ event = V4L2_EVENT_SEQ_CHANGED_SUFFICIENT;
break;
case HAL_EVENT_SEQ_CHANGED_INSUFFICIENT_RESOURCES:
event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
@@ -1432,18 +1451,17 @@ static void handle_event_change(enum hal_command_response cmd, void *data)
}
/* Bit depth and pic struct changed event are combined into a single
- * event (insufficient event) for the userspace. Currently bitdepth
- * changes is only for HEVC and interlaced support is for all
- * codecs except HEVC
- * event data is now as follows:
- * u32 *ptr = seq_changed_event.u.data;
- * ptr[0] = height
- * ptr[1] = width
- * ptr[2] = flag to indicate bit depth or/and pic struct changed
- * ptr[3] = bit depth
- * ptr[4] = pic struct (progressive or interlaced)
- * ptr[5] = colour space
- */
+ * event (insufficient event) for the userspace. Currently bitdepth
+ * changes is only for HEVC and interlaced support is for all
+ * codecs except HEVC
+ * event data is now as follows:
+ * u32 *ptr = seq_changed_event.u.data;
+ * ptr[0] = height
+ * ptr[1] = width
+ * ptr[2] = bit depth
+ * ptr[3] = pic struct (progressive or interlaced)
+ * ptr[4] = colour space
+ */
inst->entropy_mode = msm_comm_hal_to_v4l2(
V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
@@ -1456,55 +1474,35 @@ static void handle_event_change(enum hal_command_response cmd, void *data)
event_notify->level);
ptr = (u32 *)seq_changed_event.u.data;
+ ptr[0] = event_notify->height;
+ ptr[1] = event_notify->width;
+ ptr[2] = event_notify->bit_depth;
+ ptr[3] = event_notify->pic_struct;
+ ptr[4] = event_notify->colour_space;
- if (ptr != NULL) {
- ptr[2] = 0x0;
- ptr[3] = inst->bit_depth;
- ptr[4] = inst->pic_struct;
- ptr[5] = inst->colour_space;
+ dprintk(VIDC_DBG,
+ "Event payload: height = %d width = %d\n",
+ event_notify->height, event_notify->width);
- if (inst->bit_depth != event_notify->bit_depth) {
- inst->bit_depth = event_notify->bit_depth;
- ptr[2] |= V4L2_EVENT_BITDEPTH_FLAG;
- ptr[3] = inst->bit_depth;
- event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
- dprintk(VIDC_DBG,
- "V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT due to bit-depth change\n");
- }
+ dprintk(VIDC_DBG,
+ "Event payload: bit_depth = %d pic_struct = %d colour_space = %d\n",
+ event_notify->bit_depth, event_notify->pic_struct,
+ event_notify->colour_space);
- if (inst->pic_struct != event_notify->pic_struct) {
- inst->pic_struct = event_notify->pic_struct;
- event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
- ptr[2] |= V4L2_EVENT_PICSTRUCT_FLAG;
- ptr[4] = inst->pic_struct;
- dprintk(VIDC_DBG,
- "V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT due to pic-struct change\n");
- }
-
- if (inst->bit_depth == MSM_VIDC_BIT_DEPTH_10
- && inst->colour_space !=
- event_notify->colour_space) {
- inst->colour_space = event_notify->colour_space;
- event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
- ptr[2] |= V4L2_EVENT_COLOUR_SPACE_FLAG;
- ptr[5] = inst->colour_space;
- dprintk(VIDC_DBG,
- "V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT due to colour space change\n");
- }
-
- }
+ mutex_lock(&inst->lock);
+ inst->in_reconfig = true;
+ inst->reconfig_height = event_notify->height;
+ inst->reconfig_width = event_notify->width;
+ mutex_unlock(&inst->lock);
if (event == V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT) {
dprintk(VIDC_DBG, "V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT\n");
- inst->reconfig_height = event_notify->height;
- inst->reconfig_width = event_notify->width;
- inst->in_reconfig = true;
} else {
dprintk(VIDC_DBG, "V4L2_EVENT_SEQ_CHANGED_SUFFICIENT\n");
dprintk(VIDC_DBG,
- "event_notify->height = %d event_notify->width = %d\n",
- event_notify->height,
- event_notify->width);
+ "event_notify->height = %d event_notify->width = %d\n",
+ event_notify->height,
+ event_notify->width);
inst->prop.height[OUTPUT_PORT] = event_notify->height;
inst->prop.width[OUTPUT_PORT] = event_notify->width;
}
@@ -1512,13 +1510,6 @@ static void handle_event_change(enum hal_command_response cmd, void *data)
rc = msm_vidc_check_session_supported(inst);
if (!rc) {
seq_changed_event.type = event;
- if (event == V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT) {
- u32 *ptr = NULL;
-
- ptr = (u32 *)seq_changed_event.u.data;
- ptr[0] = event_notify->height;
- ptr[1] = event_notify->width;
- }
v4l2_event_queue_fh(&inst->event_handler, &seq_changed_event);
} else if (rc == -ENOTSUPP) {
msm_vidc_queue_v4l2_event(inst,
@@ -3649,6 +3640,11 @@ int msm_vidc_comm_cmd(void *instance, union msm_v4l2_cmd *cmd)
}
break;
}
+ case V4L2_QCOM_CMD_SESSION_CONTINUE:
+ {
+ rc = msm_comm_session_continue(inst);
+ break;
+ }
default:
dprintk(VIDC_ERR, "Unknown Command %d\n", which_cmd);
rc = -ENOTSUPP;
@@ -3992,32 +3988,123 @@ int msm_comm_qbuf(struct msm_vidc_inst *inst, struct vb2_buffer *vb)
return rc;
}
+static int msm_vidc_update_host_buff_counts(struct msm_vidc_inst *inst)
+{
+ int extra_buffers;
+ struct hal_buffer_requirements *bufreq;
+
+ bufreq = get_buff_req_buffer(inst,
+ HAL_BUFFER_INPUT);
+ if (!bufreq) {
+ dprintk(VIDC_ERR,
+ "Failed : No buffer requirements : %x\n",
+ HAL_BUFFER_INPUT);
+ return -EINVAL;
+ }
+ extra_buffers = msm_vidc_get_extra_buff_count(inst, HAL_BUFFER_INPUT);
+
+ bufreq->buffer_count_min_host = bufreq->buffer_count_min +
+ extra_buffers;
+
+ if (msm_comm_get_stream_output_mode(inst) ==
+ HAL_VIDEO_DECODER_SECONDARY) {
+
+ bufreq = get_buff_req_buffer(inst,
+ HAL_BUFFER_OUTPUT);
+ if (!bufreq) {
+ dprintk(VIDC_ERR,
+ "Failed : No buffer requirements : %x\n",
+ HAL_BUFFER_OUTPUT);
+ return -EINVAL;
+ }
+
+ /* For DPB buffers, no need to add Extra buffers */
+
+ bufreq->buffer_count_actual = bufreq->buffer_count_min_host =
+ bufreq->buffer_count_min;
+
+ bufreq = get_buff_req_buffer(inst,
+ HAL_BUFFER_OUTPUT2);
+ if (!bufreq) {
+ dprintk(VIDC_ERR,
+ "Failed : No buffer requirements : %x\n",
+ HAL_BUFFER_OUTPUT2);
+ return -EINVAL;
+ }
+
+ extra_buffers = msm_vidc_get_extra_buff_count(inst,
+ HAL_BUFFER_OUTPUT);
+
+ bufreq->buffer_count_min_host =
+ bufreq->buffer_count_min + extra_buffers;
+ } else {
+
+ bufreq = get_buff_req_buffer(inst,
+ HAL_BUFFER_OUTPUT);
+ if (!bufreq) {
+ dprintk(VIDC_ERR,
+ "Failed : No buffer requirements : %x\n",
+ HAL_BUFFER_OUTPUT);
+ return -EINVAL;
+ }
+
+ extra_buffers = msm_vidc_get_extra_buff_count(inst,
+ HAL_BUFFER_OUTPUT);
+
+ bufreq->buffer_count_actual = bufreq->buffer_count_min_host =
+ bufreq->buffer_count_min + extra_buffers;
+ }
+
+ return 0;
+}
+
int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst)
{
int rc = 0, i = 0;
union hal_get_property hprop;
+ memset(&hprop, 0x0, sizeof(hprop));
+
rc = msm_comm_try_get_prop(inst, HAL_PARAM_GET_BUFFER_REQUIREMENTS,
- &hprop);
+ &hprop);
if (rc) {
dprintk(VIDC_ERR, "Failed getting buffer requirements: %d", rc);
return rc;
}
- dprintk(VIDC_DBG, "Buffer requirements:\n");
- dprintk(VIDC_DBG, "%15s %8s %8s\n", "buffer type", "count", "size");
+ dprintk(VIDC_DBG, "Buffer requirements from HW:\n");
+ dprintk(VIDC_DBG, "%15s %8s %8s %8s %8s\n",
+ "buffer type", "count", "mincount_host", "mincount_fw", "size");
for (i = 0; i < HAL_BUFFER_MAX; i++) {
struct hal_buffer_requirements req = hprop.buf_req.buffer[i];
inst->buff_req.buffer[i] = req;
- dprintk(VIDC_DBG, "%15s %8d %8d\n",
+ if (req.buffer_type != HAL_BUFFER_NONE) {
+ dprintk(VIDC_DBG, "%15s %8d %8d %8d %8d\n",
get_buffer_name(req.buffer_type),
- req.buffer_count_actual, req.buffer_size);
+ req.buffer_count_actual,
+ req.buffer_count_min_host,
+ req.buffer_count_min, req.buffer_size);
+ }
}
- dprintk(VIDC_PROF, "Input buffers: %d, Output buffers: %d\n",
- inst->buff_req.buffer[0].buffer_count_actual,
- inst->buff_req.buffer[1].buffer_count_actual);
+ rc = msm_vidc_update_host_buff_counts(inst);
+
+ dprintk(VIDC_DBG, "Buffer requirements host adjusted:\n");
+ dprintk(VIDC_DBG, "%15s %8s %8s %8s %8s\n",
+ "buffer type", "count", "mincount_host", "mincount_fw", "size");
+ for (i = 0; i < HAL_BUFFER_MAX; i++) {
+ struct hal_buffer_requirements req = hprop.buf_req.buffer[i];
+
+ inst->buff_req.buffer[i] = req;
+ if (req.buffer_type != HAL_BUFFER_NONE) {
+ dprintk(VIDC_DBG, "%15s %8d %8d %8d %8d\n",
+ get_buffer_name(req.buffer_type),
+ req.buffer_count_actual,
+ req.buffer_count_min_host,
+ req.buffer_count_min, req.buffer_size);
+ }
+ }
return rc;
}
@@ -5292,21 +5379,18 @@ int msm_vidc_comm_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a)
goto exit;
}
- if (inst->prop.fps != fps) {
- dprintk(VIDC_PROF, "reported fps changed for %pK: %d->%d\n",
- inst, inst->prop.fps, fps);
- inst->prop.fps = fps;
+ dprintk(VIDC_PROF, "reported fps changed for %pK: %d->%d\n",
+ inst, inst->prop.fps, fps);
+ inst->prop.fps = fps;
+ if (inst->session_type == MSM_VIDC_ENCODER) {
frame_rate.frame_rate = inst->prop.fps * BIT(16);
frame_rate.buffer_type = HAL_BUFFER_OUTPUT;
pdata = &frame_rate;
- if (inst->session_type == MSM_VIDC_ENCODER) {
- rc = call_hfi_op(hdev, session_set_property,
- inst->session, property_id, pdata);
-
- if (rc)
- dprintk(VIDC_WARN,
- "Failed to set frame rate %d\n", rc);
- }
+ rc = call_hfi_op(hdev, session_set_property,
+ inst->session, property_id, pdata);
+ if (rc)
+ dprintk(VIDC_WARN,
+ "Failed to set frame rate %d\n", rc);
}
exit:
return rc;
@@ -5403,3 +5487,36 @@ static void msm_comm_print_debug_info(struct msm_vidc_inst *inst)
}
mutex_unlock(&core->lock);
}
+
+int msm_comm_session_continue(void *instance)
+{
+ struct msm_vidc_inst *inst = instance;
+ int rc = 0;
+ struct hfi_device *hdev;
+
+ if (!inst || !inst->core || !inst->core->device)
+ return -EINVAL;
+ hdev = inst->core->device;
+ mutex_lock(&inst->lock);
+ if (inst->session_type == MSM_VIDC_DECODER && inst->in_reconfig) {
+ dprintk(VIDC_DBG, "send session_continue\n");
+ rc = call_hfi_op(hdev, session_continue,
+ (void *)inst->session);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "failed to send session_continue\n");
+ rc = -EINVAL;
+ goto sess_continue_fail;
+ }
+ inst->in_reconfig = false;
+ } else if (inst->session_type == MSM_VIDC_ENCODER) {
+ dprintk(VIDC_DBG,
+ "session_continue not supported for encoder");
+ } else {
+ dprintk(VIDC_ERR,
+ "session_continue called in wrong state for decoder");
+ }
+sess_continue_fail:
+ mutex_unlock(&inst->lock);
+ return rc;
+}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h
index 39a28b3..9c7eec5 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h
@@ -97,4 +97,5 @@ bool msm_comm_turbo_session(struct msm_vidc_inst *inst);
void msm_comm_print_inst_info(struct msm_vidc_inst *inst);
int msm_comm_v4l2_to_hal(int id, int value);
int msm_comm_hal_to_v4l2(int id, int value);
+int msm_comm_session_continue(void *instance);
#endif
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index 8562e8f..4b91193 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -212,7 +212,8 @@ struct dcvs_stats {
int load_high;
int min_threshold;
int max_threshold;
- unsigned int extra_buffer_count;
+ unsigned int extra_capture_buffer_count;
+ unsigned int extra_output_buffer_count;
enum hal_buffer buffer_type;
};
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
index 8b9018c..763c41d 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
@@ -499,7 +499,7 @@ static int msm_vidc_load_cycles_per_mb_table(
"qcom,vpp-cycles-per-mb", NULL)) {
rc = of_property_read_u32(child_node,
"qcom,vpp-cycles-per-mb",
- &entry->vsp_cycles);
+ &entry->vpp_cycles);
if (rc) {
dprintk(VIDC_ERR,
"qcom,vpp-cycles-per-mb not found\n");
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index eb36b33..c042a4a 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -1195,44 +1195,6 @@ static enum hal_default_properties venus_hfi_get_default_properties(void *dev)
return prop;
}
-static int __halt_axi(struct venus_hfi_device *device)
-{
- u32 reg;
- int rc = 0;
-
- if (!device) {
- dprintk(VIDC_ERR, "Invalid input: %pK\n", device);
- return -EINVAL;
- }
-
- /*
- * Driver needs to make sure that clocks are enabled to read Venus AXI
- * registers. If not skip AXI HALT.
- */
- if (!device->power_enabled) {
- dprintk(VIDC_WARN,
- "Clocks are OFF, skipping AXI HALT\n");
- WARN_ON(1);
- return -EINVAL;
- }
-
- /* Halt AXI and AXI IMEM VBIF Access */
- reg = __read_register(device, VENUS_VBIF_AXI_HALT_CTRL0);
- reg |= VENUS_VBIF_AXI_HALT_CTRL0_HALT_REQ;
- __write_register(device, VENUS_VBIF_AXI_HALT_CTRL0, reg);
-
- /* Request for AXI bus port halt */
- rc = readl_poll_timeout(device->hal_data->register_base
- + VENUS_VBIF_AXI_HALT_CTRL1,
- reg, reg & VENUS_VBIF_AXI_HALT_CTRL1_HALT_ACK,
- POLL_INTERVAL_US,
- VENUS_VBIF_AXI_HALT_ACK_TIMEOUT_US);
- if (rc)
- dprintk(VIDC_WARN, "AXI bus port halt timeout\n");
-
- return rc;
-}
-
static int __set_clocks(struct venus_hfi_device *device, u32 freq)
{
struct clock_info *cl;
@@ -1432,12 +1394,6 @@ static int __iface_dbgq_read(struct venus_hfi_device *device, void *pkt)
__strict_check(device);
- if (!__core_in_valid_state(device)) {
- dprintk(VIDC_DBG, "%s - fw not in init state\n", __func__);
- rc = -EINVAL;
- goto dbg_error_null;
- }
-
if (device->iface_queues[VIDC_IFACEQ_DBGQ_IDX].
q_array.align_virtual_addr == 0) {
dprintk(VIDC_ERR, "cannot read from shared DBG Q's\n");
@@ -2921,9 +2877,17 @@ static void venus_hfi_pm_handler(struct work_struct *work)
VIDC_WRAPPER_CPU_STATUS);
idle_status = __read_register(device,
VIDC_CPU_CS_SCIACMDARG0);
- if (!(wfi_status & BIT(0)) ||
- !(idle_status & BIT(30))) {
- dprintk(VIDC_WARN, "Skipping PC\n");
+ if (!(wfi_status & BIT(0))) {
+ dprintk(VIDC_WARN,
+ "Skipping PC as wfi_status (%#x) bit not set\n",
+ wfi_status);
+ goto skip_power_off;
+ }
+ if (device->res->sys_idle_indicator &&
+ !(idle_status & BIT(30))) {
+ dprintk(VIDC_WARN,
+ "Skipping PC as idle_status (%#x) bit not set\n",
+ idle_status);
goto skip_power_off;
}
@@ -2981,15 +2945,6 @@ static void __process_sys_error(struct venus_hfi_device *device)
__set_state(device, VENUS_STATE_DEINIT);
- /*
- * Once SYS_ERROR received from HW, it is safe to halt the AXI.
- * With SYS_ERROR, Venus FW may have crashed and HW might be
- * active and causing unnecessary transactions. Hence it is
- * safe to stop all AXI transactions from venus sub-system.
- */
- if (__halt_axi(device))
- dprintk(VIDC_WARN, "Failed to halt AXI after SYS_ERROR\n");
-
vsfr = (struct hfi_sfr_struct *)device->sfr.align_virtual_addr;
if (vsfr) {
void *p = memchr(vsfr->rg_data, '\0', vsfr->bufSize);
@@ -3425,6 +3380,7 @@ static inline int __init_clocks(struct venus_hfi_device *device)
static inline void __disable_unprepare_clks(struct venus_hfi_device *device)
{
struct clock_info *cl;
+ int rc = 0;
if (!device) {
dprintk(VIDC_ERR, "Invalid params: %pK\n", device);
@@ -3434,6 +3390,18 @@ static inline void __disable_unprepare_clks(struct venus_hfi_device *device)
venus_hfi_for_each_clock_reverse(device, cl) {
dprintk(VIDC_DBG, "Clock: %s disable and unprepare\n",
cl->name);
+ rc = clk_set_flags(cl->clk, CLKFLAG_NORETAIN_PERIPH);
+ if (rc) {
+ dprintk(VIDC_WARN,
+ "Failed set flag NORETAIN_PERIPH %s\n",
+ cl->name);
+ }
+ rc = clk_set_flags(cl->clk, CLKFLAG_NORETAIN_MEM);
+ if (rc) {
+ dprintk(VIDC_WARN,
+ "Failed set flag NORETAIN_MEM %s\n",
+ cl->name);
+ }
clk_disable_unprepare(cl->clk);
}
}
@@ -3457,22 +3425,18 @@ static inline int __prepare_enable_clks(struct venus_hfi_device *device)
if (cl->has_scaling)
clk_set_rate(cl->clk, clk_round_rate(cl->clk, 0));
- if (cl->has_mem_retention) {
- rc = clk_set_flags(cl->clk, CLKFLAG_NORETAIN_PERIPH);
- if (rc) {
- dprintk(VIDC_WARN,
- "Failed set flag NORETAIN_PERIPH %s\n",
+ rc = clk_set_flags(cl->clk, CLKFLAG_RETAIN_PERIPH);
+ if (rc) {
+ dprintk(VIDC_WARN,
+ "Failed set flag RETAIN_PERIPH %s\n",
cl->name);
- }
-
- rc = clk_set_flags(cl->clk, CLKFLAG_NORETAIN_MEM);
- if (rc) {
- dprintk(VIDC_WARN,
- "Failed set flag NORETAIN_MEM %s\n",
- cl->name);
- }
}
-
+ rc = clk_set_flags(cl->clk, CLKFLAG_RETAIN_MEM);
+ if (rc) {
+ dprintk(VIDC_WARN,
+ "Failed set flag RETAIN_MEM %s\n",
+ cl->name);
+ }
rc = clk_prepare_enable(cl->clk);
if (rc) {
dprintk(VIDC_ERR, "Failed to enable clocks\n");
@@ -3897,7 +3861,7 @@ static int __venus_power_on(struct venus_hfi_device *device)
return rc;
}
-static void __venus_power_off(struct venus_hfi_device *device, bool halt_axi)
+static void __venus_power_off(struct venus_hfi_device *device)
{
if (!device->power_enabled)
return;
@@ -3906,12 +3870,6 @@ static void __venus_power_off(struct venus_hfi_device *device, bool halt_axi)
disable_irq_nosync(device->hal_data->irq);
device->intr_status = 0;
- /* Halt the AXI to make sure there are no pending transactions.
- * Clocks should be unprepared after making sure axi is halted.
- */
- if (halt_axi && __halt_axi(device))
- dprintk(VIDC_WARN, "Failed to halt AXI\n");
-
__disable_unprepare_clks(device);
if (__disable_regulators(device))
dprintk(VIDC_WARN, "Failed to disable regulators\n");
@@ -3947,7 +3905,7 @@ static inline int __suspend(struct venus_hfi_device *device)
goto err_tzbsp_suspend;
}
- __venus_power_off(device, true);
+ __venus_power_off(device);
dprintk(VIDC_PROF, "Venus power collapsed\n");
return rc;
@@ -4013,7 +3971,7 @@ static inline int __resume(struct venus_hfi_device *device)
err_reset_core:
__tzbsp_set_video_state(TZBSP_VIDEO_STATE_SUSPEND);
err_set_video_state:
- __venus_power_off(device, true);
+ __venus_power_off(device);
err_venus_power_on:
dprintk(VIDC_ERR, "Failed to resume from power collapse\n");
return rc;
@@ -4072,7 +4030,7 @@ static int __load_fw(struct venus_hfi_device *device)
subsystem_put(device->resources.fw.cookie);
device->resources.fw.cookie = NULL;
fail_load_fw:
- __venus_power_off(device, true);
+ __venus_power_off(device);
fail_venus_power_on:
fail_init_pkt:
__deinit_resources(device);
@@ -4093,7 +4051,7 @@ static void __unload_fw(struct venus_hfi_device *device)
__vote_buses(device, NULL, 0);
subsystem_put(device->resources.fw.cookie);
__interface_queues_release(device);
- __venus_power_off(device, false);
+ __venus_power_off(device);
device->resources.fw.cookie = NULL;
__deinit_resources(device);
}
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index f2eeb38..9116551 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -19,6 +19,14 @@
This is an option for use by developers; most people should
say N here. This enables MMC core and driver debugging.
+config MMC_PERF_PROFILING
+ bool "MMC performance profiling"
+ depends on MMC != n
+ default n
+ help
+ If you say Y here, support will be added for collecting
+ performance numbers at the MMC Queue and Host layers.
+
if MMC
source "drivers/mmc/core/Kconfig"
diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
index 6142ec1..91f2445 100644
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -50,6 +50,17 @@
If unsure, say Y here.
+config MMC_BLOCK_DEFERRED_RESUME
+ bool "Defer MMC layer resume until I/O is requested"
+ depends on MMC_BLOCK
+ default n
+ help
+ Say Y here to enable deferred MMC resume until I/O
+ is requested.
+
+ This will reduce overall resume latency and
+ save power when there is an SD card inserted but not being used.
+
config SDIO_UART
tristate "SDIO UART/GPS class support"
depends on TTY
diff --git a/drivers/mmc/card/Makefile b/drivers/mmc/card/Makefile
index c73b406..d55107f 100644
--- a/drivers/mmc/card/Makefile
+++ b/drivers/mmc/card/Makefile
@@ -8,3 +8,4 @@
obj-$(CONFIG_SDIO_UART) += sdio_uart.o
+obj-$(CONFIG_MMC_BLOCK_TEST) += mmc_block_test.o
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index ace47ae..bcc296b 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -30,15 +30,18 @@
#include <linux/blkdev.h>
#include <linux/mutex.h>
#include <linux/scatterlist.h>
+#include <linux/bitops.h>
#include <linux/string_helpers.h>
#include <linux/delay.h>
#include <linux/capability.h>
#include <linux/compat.h>
#include <linux/pm_runtime.h>
+#include <linux/ioprio.h>
#include <linux/idr.h>
#include <linux/mmc/ioctl.h>
#include <linux/mmc/card.h>
+#include <linux/mmc/core.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
@@ -60,15 +63,33 @@ MODULE_ALIAS("mmc:block");
#define INAND_CMD38_ARG_SECERASE 0x80
#define INAND_CMD38_ARG_SECTRIM1 0x81
#define INAND_CMD38_ARG_SECTRIM2 0x88
-#define MMC_BLK_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
+#define MMC_BLK_TIMEOUT_MS (30 * 1000) /* 30 sec timeout */
#define MMC_SANITIZE_REQ_TIMEOUT 240000
#define MMC_EXTRACT_INDEX_FROM_ARG(x) ((x & 0x00FF0000) >> 16)
+#define MMC_CMDQ_STOP_TIMEOUT_MS 100
#define mmc_req_rel_wr(req) ((req->cmd_flags & REQ_FUA) && \
(rq_data_dir(req) == WRITE))
#define PACKED_CMD_VER 0x01
#define PACKED_CMD_WR 0x02
+#define PACKED_TRIGGER_MAX_ELEMENTS 5000
+#define MMC_BLK_MAX_RETRIES 5 /* max # of retries before aborting a command */
+#define MMC_BLK_UPDATE_STOP_REASON(stats, reason) \
+ do { \
+ if (stats->enabled) \
+ stats->pack_stop_reason[reason]++; \
+ } while (0)
+
+#define MAX_RETRIES 5
+#define PCKD_TRGR_INIT_MEAN_POTEN 17
+#define PCKD_TRGR_POTEN_LOWER_BOUND 5
+#define PCKD_TRGR_URGENT_PENALTY 2
+#define PCKD_TRGR_LOWER_BOUND 5
+#define PCKD_TRGR_PRECISION_MULTIPLIER 100
+
+static struct mmc_cmdq_req *mmc_cmdq_prep_dcmd(
+ struct mmc_queue_req *mqrq, struct mmc_queue *mq);
static DEFINE_MUTEX(block_mutex);
/*
@@ -103,6 +124,7 @@ struct mmc_blk_data {
#define MMC_BLK_CMD23 (1 << 0) /* Can do SET_BLOCK_COUNT for multiblock */
#define MMC_BLK_REL_WR (1 << 1) /* MMC Reliable write support */
#define MMC_BLK_PACKED_CMD (1 << 2) /* MMC packed command support */
+#define MMC_BLK_CMD_QUEUE (1 << 3) /* MMC command queue support */
unsigned int usage;
unsigned int read_only;
@@ -112,6 +134,8 @@ struct mmc_blk_data {
#define MMC_BLK_WRITE BIT(1)
#define MMC_BLK_DISCARD BIT(2)
#define MMC_BLK_SECDISCARD BIT(3)
+#define MMC_BLK_FLUSH BIT(4)
+#define MMC_BLK_PARTSWITCH BIT(5)
/*
* Only set in main mmc_blk_data associated
@@ -121,6 +145,8 @@ struct mmc_blk_data {
unsigned int part_curr;
struct device_attribute force_ro;
struct device_attribute power_ro_lock;
+ struct device_attribute num_wr_reqs_to_start_packing;
+ struct device_attribute no_pack_for_random;
int area_type;
};
@@ -138,6 +164,8 @@ MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device");
static inline int mmc_blk_part_switch(struct mmc_card *card,
struct mmc_blk_data *md);
static int get_card_status(struct mmc_card *card, u32 *status, int retries);
+static int mmc_blk_cmdq_switch(struct mmc_card *card,
+ struct mmc_blk_data *md, bool enable);
static inline void mmc_blk_clear_packed(struct mmc_queue_req *mqrq)
{
@@ -194,9 +222,13 @@ static ssize_t power_ro_lock_show(struct device *dev,
{
int ret;
struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
- struct mmc_card *card = md->queue.card;
+ struct mmc_card *card;
int locked = 0;
+ if (!md)
+ return -EINVAL;
+
+ card = md->queue.card;
if (card->ext_csd.boot_ro_lock & EXT_CSD_BOOT_WP_B_PERM_WP_EN)
locked = 2;
else if (card->ext_csd.boot_ro_lock & EXT_CSD_BOOT_WP_B_PWR_WP_EN)
@@ -224,6 +256,8 @@ static ssize_t power_ro_lock_store(struct device *dev,
return count;
md = mmc_blk_get(dev_to_disk(dev));
+ if (!md)
+ return -EINVAL;
card = md->queue.card;
mmc_get_card(card);
@@ -261,6 +295,9 @@ static ssize_t force_ro_show(struct device *dev, struct device_attribute *attr,
int ret;
struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ if (!md)
+ return -EINVAL;
+
ret = snprintf(buf, PAGE_SIZE, "%d\n",
get_disk_ro(dev_to_disk(dev)) ^
md->read_only);
@@ -275,6 +312,10 @@ static ssize_t force_ro_store(struct device *dev, struct device_attribute *attr,
char *end;
struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
unsigned long set = simple_strtoul(buf, &end, 0);
+
+ if (!md)
+ return -EINVAL;
+
if (end == buf) {
ret = -EINVAL;
goto out;
@@ -287,6 +328,119 @@ static ssize_t force_ro_store(struct device *dev, struct device_attribute *attr,
return ret;
}
+static ssize_t
+no_pack_for_random_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ int ret;
+
+ if (!md)
+ return -EINVAL;
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", md->queue.no_pack_for_random);
+
+ mmc_blk_put(md);
+ return ret;
+}
+
+static ssize_t
+no_pack_for_random_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int value;
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ struct mmc_card *card;
+ int ret = count;
+
+ if (!md)
+ return -EINVAL;
+
+ card = md->queue.card;
+ if (!card) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ sscanf(buf, "%d", &value);
+
+ if (value < 0) {
+ pr_err("%s: value %d is not valid. old value remains = %d",
+ mmc_hostname(card->host), value,
+ md->queue.no_pack_for_random);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ md->queue.no_pack_for_random = (value > 0) ? true : false;
+
+ pr_debug("%s: no_pack_for_random: new value = %d",
+ mmc_hostname(card->host),
+ md->queue.no_pack_for_random);
+
+exit:
+ mmc_blk_put(md);
+ return ret;
+}
+
+static ssize_t
+num_wr_reqs_to_start_packing_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ int num_wr_reqs_to_start_packing;
+ int ret;
+
+ if (!md)
+ return -EINVAL;
+ num_wr_reqs_to_start_packing = md->queue.num_wr_reqs_to_start_packing;
+
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", num_wr_reqs_to_start_packing);
+
+ mmc_blk_put(md);
+ return ret;
+}
+
+static ssize_t
+num_wr_reqs_to_start_packing_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int value;
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ struct mmc_card *card;
+ int ret = count;
+
+ if (!md)
+ return -EINVAL;
+
+ card = md->queue.card;
+ if (!card) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ sscanf(buf, "%d", &value);
+
+ if (value >= 0) {
+ md->queue.num_wr_reqs_to_start_packing =
+ min_t(int, value, (int)card->ext_csd.max_packed_writes);
+
+ pr_debug("%s: trigger to pack: new value = %d",
+ mmc_hostname(card->host),
+ md->queue.num_wr_reqs_to_start_packing);
+ } else {
+ pr_err("%s: value %d is not valid. old value remains = %d",
+ mmc_hostname(card->host), value,
+ md->queue.num_wr_reqs_to_start_packing);
+ ret = -EINVAL;
+ }
+
+exit:
+ mmc_blk_put(md);
+ return ret;
+}
+
#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
static int max_read_speed, max_write_speed, cache_size = 4;
@@ -680,11 +834,12 @@ static int ioctl_do_sanitize(struct mmc_card *card)
{
int err;
- if (!mmc_can_sanitize(card)) {
- pr_warn("%s: %s - SANITIZE is not supported\n",
+ if (!mmc_can_sanitize(card) &&
+ (card->host->caps2 & MMC_CAP2_SANITIZE)) {
+ pr_warn("%s: %s - SANITIZE is not supported\n",
mmc_hostname(card->host), __func__);
- err = -EOPNOTSUPP;
- goto out;
+ err = -EOPNOTSUPP;
+ goto out;
}
pr_debug("%s: %s - SANITIZE IN PROGRESS...\n",
@@ -712,15 +867,10 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
struct mmc_request mrq = {NULL};
struct scatterlist sg;
int err;
- int is_rpmb = false;
- u32 status = 0;
if (!card || !md || !idata)
return -EINVAL;
- if (md->area_type & MMC_BLK_DATA_AREA_RPMB)
- is_rpmb = true;
-
cmd.opcode = idata->ic.opcode;
cmd.arg = idata->ic.arg;
cmd.flags = idata->ic.flags;
@@ -763,6 +913,15 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
mrq.cmd = &cmd;
+ if (mmc_card_doing_bkops(card)) {
+ err = mmc_stop_bkops(card);
+ if (err) {
+ dev_err(mmc_dev(card->host),
+ "%s: stop_bkops failed %d\n", __func__, err);
+ return err;
+ }
+ }
+
err = mmc_blk_part_switch(card, md);
if (err)
return err;
@@ -773,13 +932,6 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
return err;
}
- if (is_rpmb) {
- err = mmc_set_blockcount(card, data.blocks,
- idata->ic.write_flag & (1 << 31));
- if (err)
- return err;
- }
-
if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_SANITIZE_START) &&
(cmd.opcode == MMC_SWITCH)) {
err = ioctl_do_sanitize(card);
@@ -813,7 +965,183 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
memcpy(&(idata->ic.response), cmd.resp, sizeof(cmd.resp));
- if (is_rpmb) {
+ return err;
+}
+
+struct mmc_blk_ioc_rpmb_data {
+ struct mmc_blk_ioc_data *data[MMC_IOC_MAX_RPMB_CMD];
+};
+
+static struct mmc_blk_ioc_rpmb_data *mmc_blk_ioctl_rpmb_copy_from_user(
+ struct mmc_ioc_rpmb __user *user)
+{
+ struct mmc_blk_ioc_rpmb_data *idata;
+ int err, i;
+
+ idata = kzalloc(sizeof(*idata), GFP_KERNEL);
+ if (!idata) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < MMC_IOC_MAX_RPMB_CMD; i++) {
+ idata->data[i] = mmc_blk_ioctl_copy_from_user(&(user->cmds[i]));
+ if (IS_ERR(idata->data[i])) {
+ err = PTR_ERR(idata->data[i]);
+ goto copy_err;
+ }
+ }
+
+ return idata;
+
+copy_err:
+ while (--i >= 0) {
+ kfree(idata->data[i]->buf);
+ kfree(idata->data[i]);
+ }
+ kfree(idata);
+out:
+ return ERR_PTR(err);
+}
+
+static int mmc_blk_ioctl_rpmb_cmd(struct block_device *bdev,
+ struct mmc_ioc_rpmb __user *ic_ptr)
+{
+ struct mmc_blk_ioc_rpmb_data *idata;
+ struct mmc_blk_data *md;
+ struct mmc_card *card = NULL;
+ struct mmc_command cmd = {0};
+ struct mmc_data data = {0};
+ struct mmc_request mrq = {NULL};
+ struct scatterlist sg;
+ int err = 0, i = 0;
+ u32 status = 0;
+
+ /* The caller must have CAP_SYS_RAWIO */
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ md = mmc_blk_get(bdev->bd_disk);
+ /* make sure this is a rpmb partition */
+ if ((!md) || (!(md->area_type & MMC_BLK_DATA_AREA_RPMB))) {
+ err = -EINVAL;
+ return err;
+ }
+
+ idata = mmc_blk_ioctl_rpmb_copy_from_user(ic_ptr);
+ if (IS_ERR(idata)) {
+ err = PTR_ERR(idata);
+ goto cmd_done;
+ }
+
+ card = md->queue.card;
+ if (IS_ERR(card)) {
+ err = PTR_ERR(card);
+ goto idata_free;
+ }
+
+ mmc_get_card(card);
+
+ if (mmc_card_doing_bkops(card)) {
+ if (mmc_card_cmdq(card)) {
+ err = mmc_cmdq_halt(card->host, true);
+ if (err)
+ goto cmd_rel_host;
+ }
+ err = mmc_stop_bkops(card);
+ if (err) {
+ dev_err(mmc_dev(card->host),
+ "%s: stop_bkops failed %d\n", __func__, err);
+ goto cmd_rel_host;
+ }
+ if (mmc_card_cmdq(card)) {
+ err = mmc_cmdq_halt(card->host, false);
+ if (err)
+ goto cmd_rel_host;
+ }
+ }
+
+ err = mmc_blk_part_switch(card, md);
+ if (err)
+ goto cmd_rel_host;
+
+ for (i = 0; i < MMC_IOC_MAX_RPMB_CMD; i++) {
+ struct mmc_blk_ioc_data *curr_data;
+ struct mmc_ioc_cmd *curr_cmd;
+
+ curr_data = idata->data[i];
+ curr_cmd = &curr_data->ic;
+ if (!curr_cmd->opcode)
+ break;
+
+ cmd.opcode = curr_cmd->opcode;
+ cmd.arg = curr_cmd->arg;
+ cmd.flags = curr_cmd->flags;
+
+ if (curr_data->buf_bytes) {
+ data.sg = &sg;
+ data.sg_len = 1;
+ data.blksz = curr_cmd->blksz;
+ data.blocks = curr_cmd->blocks;
+
+ sg_init_one(data.sg, curr_data->buf,
+ curr_data->buf_bytes);
+
+ if (curr_cmd->write_flag)
+ data.flags = MMC_DATA_WRITE;
+ else
+ data.flags = MMC_DATA_READ;
+
+ /* data.flags must already be set before doing this. */
+ mmc_set_data_timeout(&data, card);
+
+ /*
+ * Allow overriding the timeout_ns for empirical tuning.
+ */
+ if (curr_cmd->data_timeout_ns)
+ data.timeout_ns = curr_cmd->data_timeout_ns;
+
+ mrq.data = &data;
+ }
+
+ mrq.cmd = &cmd;
+
+ err = mmc_set_blockcount(card, data.blocks,
+ curr_cmd->write_flag & (1 << 31));
+ if (err)
+ goto cmd_rel_host;
+
+ mmc_wait_for_req(card->host, &mrq);
+
+ if (cmd.error) {
+ dev_err(mmc_dev(card->host), "%s: cmd error %d\n",
+ __func__, cmd.error);
+ err = cmd.error;
+ goto cmd_rel_host;
+ }
+ if (data.error) {
+ dev_err(mmc_dev(card->host), "%s: data error %d\n",
+ __func__, data.error);
+ err = data.error;
+ goto cmd_rel_host;
+ }
+
+ if (copy_to_user(&(ic_ptr->cmds[i].response), cmd.resp,
+ sizeof(cmd.resp))) {
+ err = -EFAULT;
+ goto cmd_rel_host;
+ }
+
+ if (!curr_cmd->write_flag) {
+ if (copy_to_user((void __user *)(unsigned long)
+ curr_cmd->data_ptr,
+ curr_data->buf,
+ curr_data->buf_bytes)) {
+ err = -EFAULT;
+ goto cmd_rel_host;
+ }
+ }
+
/*
* Ensure RPMB command has completed by polling CMD13
* "Send Status".
@@ -825,6 +1153,20 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
__func__, status, err);
}
+cmd_rel_host:
+ mmc_put_card(card);
+
+idata_free:
+ for (i = 0; i < MMC_IOC_MAX_RPMB_CMD; i++) {
+ kfree(idata->data[i]->buf);
+ kfree(idata->data[i]);
+ }
+ kfree(idata);
+
+cmd_done:
+ mmc_blk_put(md);
+ if (card && card->cmdq_init)
+ wake_up(&card->host->cmdq_ctx.wait);
return err;
}
@@ -845,7 +1187,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
return -EPERM;
idata = mmc_blk_ioctl_copy_from_user(ic_ptr);
- if (IS_ERR(idata))
+ if (IS_ERR_OR_NULL(idata))
return PTR_ERR(idata);
md = mmc_blk_get(bdev->bd_disk);
@@ -855,13 +1197,24 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
}
card = md->queue.card;
- if (IS_ERR(card)) {
+ if (IS_ERR_OR_NULL(card)) {
err = PTR_ERR(card);
goto cmd_done;
}
mmc_get_card(card);
+ if (mmc_card_cmdq(card)) {
+ err = mmc_cmdq_halt_on_empty_queue(card->host);
+ if (err) {
+ pr_err("%s: halt failed while doing %s err (%d)\n",
+ mmc_hostname(card->host),
+ __func__, err);
+ mmc_put_card(card);
+ goto cmd_done;
+ }
+ }
+
ioc_err = __mmc_blk_ioctl_cmd(card, md, idata);
/* Always switch back to main area after RPMB access */
@@ -872,6 +1225,12 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
err = mmc_blk_ioctl_copy_to_user(ic_ptr, idata);
+ if (mmc_card_cmdq(card)) {
+ if (mmc_cmdq_halt(card->host, false))
+ pr_err("%s: %s: cmdq unhalt failed\n",
+ mmc_hostname(card->host), __func__);
+ }
+
cmd_done:
mmc_blk_put(md);
cmd_err:
@@ -963,6 +1322,9 @@ static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
case MMC_IOC_CMD:
return mmc_blk_ioctl_cmd(bdev,
(struct mmc_ioc_cmd __user *)arg);
+ case MMC_IOC_RPMB_CMD:
+ return mmc_blk_ioctl_rpmb_cmd(bdev,
+ (struct mmc_ioc_rpmb __user *)arg);
case MMC_IOC_MULTI_CMD:
return mmc_blk_ioctl_multi_cmd(bdev,
(struct mmc_ioc_multi_cmd __user *)arg);
@@ -990,18 +1352,76 @@ static const struct block_device_operations mmc_bdops = {
#endif
};
+static int mmc_blk_cmdq_switch(struct mmc_card *card,
+ struct mmc_blk_data *md, bool enable)
+{
+ int ret = 0;
+ bool cmdq_mode = !!mmc_card_cmdq(card);
+ struct mmc_host *host = card->host;
+ struct mmc_cmdq_context_info *ctx = &host->cmdq_ctx;
+
+ if (!(card->host->caps2 & MMC_CAP2_CMD_QUEUE) ||
+ !card->ext_csd.cmdq_support ||
+ (enable && !(md->flags & MMC_BLK_CMD_QUEUE)) ||
+ (cmdq_mode == enable))
+ return 0;
+
+ if (enable) {
+ ret = mmc_set_blocklen(card, MMC_CARD_CMDQ_BLK_SIZE);
+ if (ret) {
+ pr_err("%s: failed (%d) to set block-size to %d\n",
+ __func__, ret, MMC_CARD_CMDQ_BLK_SIZE);
+ goto out;
+ }
+
+ } else {
+ if (!test_bit(CMDQ_STATE_HALT, &ctx->curr_state)) {
+ ret = mmc_cmdq_halt(host, true);
+ if (ret) {
+ pr_err("%s: halt: failed: %d\n",
+ mmc_hostname(host), ret);
+ goto out;
+ }
+ }
+ }
+
+ ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_CMDQ, enable,
+ card->ext_csd.generic_cmd6_time);
+ if (ret) {
+ pr_err("%s: cmdq mode %sable failed %d\n",
+ md->disk->disk_name, enable ? "en" : "dis", ret);
+ goto out;
+ }
+
+ if (enable)
+ mmc_card_set_cmdq(card);
+ else
+ mmc_card_clr_cmdq(card);
+out:
+ return ret;
+}
+
static inline int mmc_blk_part_switch(struct mmc_card *card,
struct mmc_blk_data *md)
{
int ret;
struct mmc_blk_data *main_md = dev_get_drvdata(&card->dev);
- if (main_md->part_curr == md->part_type)
+ if ((main_md->part_curr == md->part_type) &&
+ (card->part_curr == md->part_type))
return 0;
if (mmc_card_mmc(card)) {
u8 part_config = card->ext_csd.part_config;
+ if (md->part_type) {
+ /* disable CQ mode for non-user data partitions */
+ ret = mmc_blk_cmdq_switch(card, md, false);
+ if (ret)
+ return ret;
+ }
+
if (md->part_type == EXT_CSD_PART_CONFIG_ACC_RPMB)
mmc_retune_pause(card->host);
@@ -1012,12 +1432,16 @@ static inline int mmc_blk_part_switch(struct mmc_card *card,
EXT_CSD_PART_CONFIG, part_config,
card->ext_csd.part_time);
if (ret) {
+ pr_err("%s: mmc_blk_part_switch failure, %d -> %d\n",
+ mmc_hostname(card->host), main_md->part_curr,
+ md->part_type);
if (md->part_type == EXT_CSD_PART_CONFIG_ACC_RPMB)
mmc_retune_unpause(card->host);
return ret;
}
card->ext_csd.part_config = part_config;
+ card->part_curr = md->part_type;
if (main_md->part_curr == EXT_CSD_PART_CONFIG_ACC_RPMB)
mmc_retune_unpause(card->host);
@@ -1201,18 +1625,20 @@ static int mmc_blk_cmd_error(struct request *req, const char *name, int error,
switch (error) {
case -EILSEQ:
/* response crc error, retry the r/w cmd */
- pr_err("%s: %s sending %s command, card status %#x\n",
- req->rq_disk->disk_name, "response CRC error",
+ pr_err_ratelimited(
+ "%s: response CRC error sending %s command, card status %#x\n",
+ req->rq_disk->disk_name,
name, status);
return ERR_RETRY;
case -ETIMEDOUT:
- pr_err("%s: %s sending %s command, card status %#x\n",
- req->rq_disk->disk_name, "timed out", name, status);
+ pr_err_ratelimited(
+ "%s: timed out sending %s command, card status %#x\n",
+ req->rq_disk->disk_name, name, status);
/* If the status cmd initially failed, retry the r/w cmd */
if (!status_valid) {
- pr_err("%s: status not valid, retrying timeout\n",
+ pr_err_ratelimited("%s: status not valid, retrying timeout\n",
req->rq_disk->disk_name);
return ERR_RETRY;
}
@@ -1223,17 +1649,22 @@ static int mmc_blk_cmd_error(struct request *req, const char *name, int error,
* have corrected the state problem above.
*/
if (status & (R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND)) {
- pr_err("%s: command error, retrying timeout\n",
+ pr_err_ratelimited(
+ "%s: command error, retrying timeout\n",
req->rq_disk->disk_name);
return ERR_RETRY;
}
/* Otherwise abort the command */
+ pr_err_ratelimited(
+ "%s: not retrying timeout\n",
+ req->rq_disk->disk_name);
return ERR_ABORT;
default:
/* We don't understand the error code the driver gave us */
- pr_err("%s: unknown error %d sending read/write command, card status %#x\n",
+ pr_err_ratelimited(
+ "%s: unknown error %d sending read/write command, card status %#x\n",
req->rq_disk->disk_name, error, status);
return ERR_ABORT;
}
@@ -1372,8 +1803,15 @@ static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host,
md->reset_done |= type;
err = mmc_hw_reset(host);
+ if (err && err != -EOPNOTSUPP) {
+ /* We failed to reset so we need to abort the request */
+ pr_err("%s: %s: failed to reset %d\n", mmc_hostname(host),
+ __func__, err);
+ return -ENODEV;
+ }
+
/* Ensure we switch back to the correct partition */
- if (err != -EOPNOTSUPP) {
+ if (host->card) {
struct mmc_blk_data *main_md =
dev_get_drvdata(&host->card->dev);
int part_err;
@@ -1408,6 +1846,77 @@ int mmc_access_rpmb(struct mmc_queue *mq)
return false;
}
+static struct mmc_cmdq_req *mmc_blk_cmdq_prep_discard_req(struct mmc_queue *mq,
+ struct request *req)
+{
+ struct mmc_blk_data *md = mq->data;
+ struct mmc_card *card = md->queue.card;
+ struct mmc_host *host = card->host;
+ struct mmc_cmdq_context_info *ctx_info = &host->cmdq_ctx;
+ struct mmc_cmdq_req *cmdq_req;
+ struct mmc_queue_req *active_mqrq;
+
+ BUG_ON(req->tag > card->ext_csd.cmdq_depth);
+ BUG_ON(test_and_set_bit(req->tag, &host->cmdq_ctx.active_reqs));
+
+ set_bit(CMDQ_STATE_DCMD_ACTIVE, &ctx_info->curr_state);
+
+ active_mqrq = &mq->mqrq_cmdq[req->tag];
+ active_mqrq->req = req;
+
+ cmdq_req = mmc_cmdq_prep_dcmd(active_mqrq, mq);
+ cmdq_req->cmdq_req_flags |= QBR;
+ cmdq_req->mrq.cmd = &cmdq_req->cmd;
+ cmdq_req->tag = req->tag;
+ return cmdq_req;
+}
+
+static int mmc_blk_cmdq_issue_discard_rq(struct mmc_queue *mq,
+ struct request *req)
+{
+ struct mmc_blk_data *md = mq->data;
+ struct mmc_card *card = md->queue.card;
+ struct mmc_cmdq_req *cmdq_req = NULL;
+ unsigned int from, nr, arg;
+ int err = 0;
+
+ if (!mmc_can_erase(card)) {
+ err = -EOPNOTSUPP;
+ blk_end_request(req, err, blk_rq_bytes(req));
+ goto out;
+ }
+
+ from = blk_rq_pos(req);
+ nr = blk_rq_sectors(req);
+
+ if (mmc_can_discard(card))
+ arg = MMC_DISCARD_ARG;
+ else if (mmc_can_trim(card))
+ arg = MMC_TRIM_ARG;
+ else
+ arg = MMC_ERASE_ARG;
+
+ cmdq_req = mmc_blk_cmdq_prep_discard_req(mq, req);
+ if (card->quirks & MMC_QUIRK_INAND_CMD38) {
+ __mmc_switch_cmdq_mode(cmdq_req->mrq.cmd,
+ EXT_CSD_CMD_SET_NORMAL,
+ INAND_CMD38_ARG_EXT_CSD,
+ arg == MMC_TRIM_ARG ?
+ INAND_CMD38_ARG_TRIM :
+ INAND_CMD38_ARG_ERASE,
+ 0, true, false);
+ err = mmc_cmdq_wait_for_dcmd(card->host, cmdq_req);
+ if (err)
+ goto clear_dcmd;
+ }
+ err = mmc_cmdq_erase(cmdq_req, card, from, nr, arg);
+clear_dcmd:
+ mmc_host_clk_hold(card->host);
+ blk_complete_request(req);
+out:
+ return err ? 1 : 0;
+}
+
static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
{
struct mmc_blk_data *md = mq->data;
@@ -1451,6 +1960,69 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
return err ? 0 : 1;
}
+static int mmc_blk_cmdq_issue_secdiscard_rq(struct mmc_queue *mq,
+ struct request *req)
+{
+ struct mmc_blk_data *md = mq->data;
+ struct mmc_card *card = md->queue.card;
+ struct mmc_cmdq_req *cmdq_req = NULL;
+ unsigned int from, nr, arg;
+ int err = 0;
+
+ if (!(mmc_can_secure_erase_trim(card))) {
+ err = -EOPNOTSUPP;
+ blk_end_request(req, err, blk_rq_bytes(req));
+ goto out;
+ }
+
+ from = blk_rq_pos(req);
+ nr = blk_rq_sectors(req);
+
+ if (mmc_can_trim(card) && !mmc_erase_group_aligned(card, from, nr))
+ arg = MMC_SECURE_TRIM1_ARG;
+ else
+ arg = MMC_SECURE_ERASE_ARG;
+
+ cmdq_req = mmc_blk_cmdq_prep_discard_req(mq, req);
+ if (card->quirks & MMC_QUIRK_INAND_CMD38) {
+ __mmc_switch_cmdq_mode(cmdq_req->mrq.cmd,
+ EXT_CSD_CMD_SET_NORMAL,
+ INAND_CMD38_ARG_EXT_CSD,
+ arg == MMC_SECURE_TRIM1_ARG ?
+ INAND_CMD38_ARG_SECTRIM1 :
+ INAND_CMD38_ARG_SECERASE,
+ 0, true, false);
+ err = mmc_cmdq_wait_for_dcmd(card->host, cmdq_req);
+ if (err)
+ goto clear_dcmd;
+ }
+
+ err = mmc_cmdq_erase(cmdq_req, card, from, nr, arg);
+ if (err)
+ goto clear_dcmd;
+
+ if (arg == MMC_SECURE_TRIM1_ARG) {
+ if (card->quirks & MMC_QUIRK_INAND_CMD38) {
+ __mmc_switch_cmdq_mode(cmdq_req->mrq.cmd,
+ EXT_CSD_CMD_SET_NORMAL,
+ INAND_CMD38_ARG_EXT_CSD,
+ INAND_CMD38_ARG_SECTRIM2,
+ 0, true, false);
+ err = mmc_cmdq_wait_for_dcmd(card->host, cmdq_req);
+ if (err)
+ goto clear_dcmd;
+ }
+
+ err = mmc_cmdq_erase(cmdq_req, card, from, nr,
+ MMC_SECURE_TRIM2_ARG);
+ }
+clear_dcmd:
+ mmc_host_clk_hold(card->host);
+ blk_complete_request(req);
+out:
+ return err ? 1 : 0;
+}
+
static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
struct request *req)
{
@@ -1524,9 +2096,45 @@ static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
struct mmc_card *card = md->queue.card;
int ret = 0;
- ret = mmc_flush_cache(card);
- if (ret)
+ if (!req)
+ return 0;
+
+ if (req->cmd_flags & REQ_BARRIER) {
+ /*
+ * If eMMC cache flush policy is set to 1, then the device
+ * shall flush the requests in First-In-First-Out (FIFO) order.
+ * In this case, as per spec, the host must not send any cache
+ * barrier requests as they are redundant and add unnecessary
+ * overhead to both device and host.
+ */
+ if (card->ext_csd.cache_flush_policy & 1)
+ goto end_req;
+
+ /*
+ * In case barrier is not supported or enabled in the device,
+ * use flush as a fallback option.
+ */
+ ret = mmc_cache_barrier(card);
+ if (ret)
+ ret = mmc_flush_cache(card);
+ } else if (req_op(req) == REQ_OP_FLUSH) {
+ ret = mmc_flush_cache(card);
+ }
+ if (ret == -ENODEV) {
+ pr_err("%s: %s: restart mmc card",
+ req->rq_disk->disk_name, __func__);
+ if (mmc_blk_reset(md, card->host, MMC_BLK_FLUSH))
+ pr_err("%s: %s: fail to restart mmc",
+ req->rq_disk->disk_name, __func__);
+ else
+ mmc_blk_reset_success(md, MMC_BLK_FLUSH);
+ }
+
+ if (ret) {
+ pr_err("%s: %s: notify flush error to upper layers",
+ req->rq_disk->disk_name, __func__);
ret = -EIO;
+ }
#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
else if (atomic_read(&mq->cache_size)) {
@@ -1545,6 +2153,7 @@ static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
}
}
#endif
+end_req:
blk_end_request_all(req, ret);
return ret ? 0 : 1;
@@ -1773,6 +2382,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
brq->stop.arg = 0;
brq->data.blocks = blk_rq_sectors(req);
+ brq->data.fault_injected = false;
/*
* The block layer doesn't support all sector count
* restrictions, so we need to be prepared for too big
@@ -1896,6 +2506,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
}
mqrq->mmc_active.mrq = &brq->mrq;
+ mqrq->mmc_active.mrq->req = mqrq->req;
mqrq->mmc_active.err_check = mmc_blk_err_check;
mmc_queue_bounce_pre(mqrq);
@@ -1917,6 +2528,178 @@ static inline u8 mmc_calc_packed_hdr_segs(struct request_queue *q,
return nr_segs;
}
+/**
+ * mmc_blk_disable_wr_packing() - disables packing mode
+ * @mq: MMC queue.
+ *
+ */
+void mmc_blk_disable_wr_packing(struct mmc_queue *mq)
+{
+ if (mq) {
+ mq->wr_packing_enabled = false;
+ mq->num_of_potential_packed_wr_reqs = 0;
+ }
+}
+EXPORT_SYMBOL(mmc_blk_disable_wr_packing);
+
+static int get_packed_trigger(int potential, struct mmc_card *card,
+ struct request *req, int curr_trigger)
+{
+ static int num_mean_elements = 1;
+ static unsigned long mean_potential = PCKD_TRGR_INIT_MEAN_POTEN;
+ unsigned int trigger = curr_trigger;
+ unsigned int pckd_trgr_upper_bound = card->ext_csd.max_packed_writes;
+
+ /* scale down the upper bound to 75% */
+ pckd_trgr_upper_bound = (pckd_trgr_upper_bound * 3) / 4;
+
+ /*
+ * since the most common calls for this function are with small
+ * potential write values and since we don't want these calls to affect
+ * the packed trigger, set a lower bound and ignore calls with
+ * potential lower than that bound
+ */
+ if (potential <= PCKD_TRGR_POTEN_LOWER_BOUND)
+ return trigger;
+
+ /*
+ * this is to prevent integer overflow in the following calculation:
+ * once every PACKED_TRIGGER_MAX_ELEMENTS reset the algorithm
+ */
+ if (num_mean_elements > PACKED_TRIGGER_MAX_ELEMENTS) {
+ num_mean_elements = 1;
+ mean_potential = PCKD_TRGR_INIT_MEAN_POTEN;
+ }
+
+ /*
+ * get next mean value based on previous mean value and current
+ * potential packed writes. Calculation is as follows:
+ * mean_pot[i+1] =
+ * ((mean_pot[i] * num_mean_elem) + potential)/(num_mean_elem + 1)
+ */
+ mean_potential *= num_mean_elements;
+ /*
+ * add num_mean_elements so that the division of two integers doesn't
+ * lower mean_potential too much
+ */
+ if (potential > mean_potential)
+ mean_potential += num_mean_elements;
+ mean_potential += potential;
+ /* this is for gaining more precision when dividing two integers */
+ mean_potential *= PCKD_TRGR_PRECISION_MULTIPLIER;
+ /* this completes the mean calculation */
+ mean_potential /= ++num_mean_elements;
+ mean_potential /= PCKD_TRGR_PRECISION_MULTIPLIER;
+
+ /*
+ * if current potential packed writes is greater than the mean potential
+ * then the heuristic is that the following workload will contain many
+ * write requests, therefore we lower the packed trigger. In the
+ * opposite case we want to increase the trigger in order to get less
+ * packing events.
+ */
+ if (potential >= mean_potential)
+ trigger = (trigger <= PCKD_TRGR_LOWER_BOUND) ?
+ PCKD_TRGR_LOWER_BOUND : trigger - 1;
+ else
+ trigger = (trigger >= pckd_trgr_upper_bound) ?
+ pckd_trgr_upper_bound : trigger + 1;
+
+ /*
+ * an urgent read request indicates a packed list being interrupted
+ * by this read, therefore we aim for less packing, hence the trigger
+ * gets increased
+ */
+ if (req && (req->cmd_flags & REQ_URGENT) && (rq_data_dir(req) == READ))
+ trigger += PCKD_TRGR_URGENT_PENALTY;
+
+ return trigger;
+}
+
+static void mmc_blk_write_packing_control(struct mmc_queue *mq,
+ struct request *req)
+{
+ struct mmc_host *host = mq->card->host;
+ int data_dir;
+
+ if (!(host->caps2 & MMC_CAP2_PACKED_WR))
+ return;
+
+ /* Support for the write packing on eMMC 4.5 or later */
+ if (mq->card->ext_csd.rev <= 5)
+ return;
+
+ /*
+ * In case the packing control is not supported by the host, it should
+ * not have an effect on the write packing. Therefore we have to enable
+ * the write packing
+ */
+ if (!(host->caps2 & MMC_CAP2_PACKED_WR_CONTROL)) {
+ mq->wr_packing_enabled = true;
+ return;
+ }
+
+ if (!req || (req && (req->cmd_flags & REQ_PREFLUSH))) {
+ if (mq->num_of_potential_packed_wr_reqs >
+ mq->num_wr_reqs_to_start_packing)
+ mq->wr_packing_enabled = true;
+ mq->num_wr_reqs_to_start_packing =
+ get_packed_trigger(mq->num_of_potential_packed_wr_reqs,
+ mq->card, req,
+ mq->num_wr_reqs_to_start_packing);
+ mq->num_of_potential_packed_wr_reqs = 0;
+ return;
+ }
+
+ data_dir = rq_data_dir(req);
+
+ if (data_dir == READ) {
+ mmc_blk_disable_wr_packing(mq);
+ mq->num_wr_reqs_to_start_packing =
+ get_packed_trigger(mq->num_of_potential_packed_wr_reqs,
+ mq->card, req,
+ mq->num_wr_reqs_to_start_packing);
+ mq->num_of_potential_packed_wr_reqs = 0;
+ mq->wr_packing_enabled = false;
+ return;
+ } else if (data_dir == WRITE) {
+ mq->num_of_potential_packed_wr_reqs++;
+ }
+
+ if (mq->num_of_potential_packed_wr_reqs >
+ mq->num_wr_reqs_to_start_packing)
+ mq->wr_packing_enabled = true;
+}
+
+struct mmc_wr_pack_stats *mmc_blk_get_packed_statistics(struct mmc_card *card)
+{
+ if (!card)
+ return NULL;
+
+ return &card->wr_pack_stats;
+}
+EXPORT_SYMBOL(mmc_blk_get_packed_statistics);
+
+void mmc_blk_init_packed_statistics(struct mmc_card *card)
+{
+ int max_num_of_packed_reqs = 0;
+
+ if (!card || !card->wr_pack_stats.packing_events)
+ return;
+
+ max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
+
+ spin_lock(&card->wr_pack_stats.lock);
+ memset(card->wr_pack_stats.packing_events, 0,
+ (max_num_of_packed_reqs + 1) *
+ sizeof(*card->wr_pack_stats.packing_events));
+ memset(&card->wr_pack_stats.pack_stop_reason, 0,
+ sizeof(card->wr_pack_stats.pack_stop_reason));
+ card->wr_pack_stats.enabled = true;
+ spin_unlock(&card->wr_pack_stats.lock);
+}
+EXPORT_SYMBOL(mmc_blk_init_packed_statistics);
+
static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
{
struct request_queue *q = mq->queue;
@@ -1930,6 +2713,7 @@ static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
bool put_back = true;
u8 max_packed_rw = 0;
u8 reqs = 0;
+ struct mmc_wr_pack_stats *stats = &card->wr_pack_stats;
/*
* We don't need to check packed for any further
@@ -1946,6 +2730,9 @@ static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
if (!(md->flags & MMC_BLK_PACKED_CMD))
goto no_packed;
+ if (!mq->wr_packing_enabled)
+ goto no_packed;
+
if ((rq_data_dir(cur) == WRITE) &&
mmc_host_packed_wr(card->host))
max_packed_rw = card->ext_csd.max_packed_writes;
@@ -1961,6 +2748,9 @@ static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
!IS_ALIGNED(blk_rq_sectors(cur), 8))
goto no_packed;
+ if (cur->cmd_flags & REQ_FUA)
+ goto no_packed;
+
mmc_blk_clear_packed(mqrq);
max_blk_count = min(card->host->max_blk_count,
@@ -1977,6 +2767,7 @@ static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
phys_segments += mmc_calc_packed_hdr_segs(q, card);
}
+ spin_lock(&stats->lock);
do {
if (reqs >= max_packed_rw - 1) {
put_back = false;
@@ -1987,34 +2778,65 @@ static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
next = blk_fetch_request(q);
spin_unlock_irq(q->queue_lock);
if (!next) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, EMPTY_QUEUE);
put_back = false;
break;
}
if (mmc_large_sector(card) &&
- !IS_ALIGNED(blk_rq_sectors(next), 8))
+ !IS_ALIGNED(blk_rq_sectors(next), 8)) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, LARGE_SEC_ALIGN);
break;
+ }
if (req_op(next) == REQ_OP_DISCARD ||
req_op(next) == REQ_OP_SECURE_ERASE ||
- req_op(next) == REQ_OP_FLUSH)
+ req_op(next) == REQ_OP_FLUSH) {
+ if (req_op(next) != REQ_OP_SECURE_ERASE)
+ MMC_BLK_UPDATE_STOP_REASON(stats, FLUSH_OR_DISCARD);
break;
+ }
- if (rq_data_dir(cur) != rq_data_dir(next))
+ if (next->cmd_flags & REQ_FUA) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, FUA);
break;
+ }
+
+ if (rq_data_dir(cur) != rq_data_dir(next)) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, WRONG_DATA_DIR);
+ break;
+ }
if (mmc_req_rel_wr(next) &&
- (md->flags & MMC_BLK_REL_WR) && !en_rel_wr)
+ (md->flags & MMC_BLK_REL_WR) && !en_rel_wr) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, REL_WRITE);
break;
+ }
req_sectors += blk_rq_sectors(next);
- if (req_sectors > max_blk_count)
+ if (req_sectors > max_blk_count) {
+ if (stats->enabled)
+ stats->pack_stop_reason[EXCEEDS_SECTORS]++;
break;
+ }
phys_segments += next->nr_phys_segments;
- if (phys_segments > max_phys_segs)
+ if (phys_segments > max_phys_segs) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, EXCEEDS_SEGMENTS);
break;
+ }
+ if (mq->no_pack_for_random) {
+ if ((blk_rq_pos(cur) + blk_rq_sectors(cur)) !=
+ blk_rq_pos(next)) {
+ MMC_BLK_UPDATE_STOP_REASON(stats, RANDOM);
+ put_back = 1;
+ break;
+ }
+ }
+
+ if (rq_data_dir(next) == WRITE)
+ mq->num_of_potential_packed_wr_reqs++;
list_add_tail(&next->queuelist, &mqrq->packed->list);
cur = next;
reqs++;
@@ -2026,6 +2848,15 @@ static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
spin_unlock_irq(q->queue_lock);
}
+ if (stats->enabled) {
+ if (reqs + 1 <= card->ext_csd.max_packed_writes)
+ stats->packing_events[reqs + 1]++;
+ if (reqs + 1 == max_packed_rw)
+ MMC_BLK_UPDATE_STOP_REASON(stats, THRESHOLD);
+ }
+
+ spin_unlock(&stats->lock);
+
if (reqs > 0) {
list_add(&req->queuelist, &mqrq->packed->list);
mqrq->packed->nr_entries = ++reqs;
@@ -2103,6 +2934,7 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq,
brq->data.blksz = 512;
brq->data.blocks = packed->blocks + hdr_blocks;
brq->data.flags = MMC_DATA_WRITE;
+ brq->data.fault_injected = false;
brq->stop.opcode = MMC_STOP_TRANSMISSION;
brq->stop.arg = 0;
@@ -2114,7 +2946,18 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq,
brq->data.sg_len = mmc_queue_map_sg(mq, mqrq);
mqrq->mmc_active.mrq = &brq->mrq;
- mqrq->mmc_active.err_check = mmc_blk_packed_err_check;
+
+ /*
+ * This is intended for packed commands tests usage - in case these
+ * functions are not in use the respective pointers are NULL
+ */
+ if (mq->err_check_fn)
+ mqrq->mmc_active.err_check = mq->err_check_fn;
+ else
+ mqrq->mmc_active.err_check = mmc_blk_packed_err_check;
+
+ if (mq->packed_test_fn)
+ mq->packed_test_fn(mq->queue, mqrq);
mmc_queue_bounce_pre(mqrq);
}
@@ -2136,11 +2979,12 @@ static int mmc_blk_cmd_err(struct mmc_blk_data *md, struct mmc_card *card,
*/
if (mmc_card_sd(card)) {
u32 blocks;
-
- blocks = mmc_sd_num_wr_blocks(card);
- if (blocks != (u32)-1) {
- ret = blk_end_request(req, 0, blocks << 9);
- }
+ if (!brq->data.fault_injected) {
+ blocks = mmc_sd_num_wr_blocks(card);
+ if (blocks != (u32)-1)
+ ret = blk_end_request(req, 0, blocks << 9);
+ } else
+ ret = blk_end_request(req, 0, brq->data.bytes_xfered);
} else {
if (!mmc_packed_cmd(mq_rq->cmd_type))
ret = blk_end_request(req, 0, brq->data.bytes_xfered);
@@ -2214,6 +3058,576 @@ static void mmc_blk_revert_packed_req(struct mmc_queue *mq,
mmc_blk_clear_packed(mq_rq);
}
+static int mmc_blk_cmdq_start_req(struct mmc_host *host,
+ struct mmc_cmdq_req *cmdq_req)
+{
+ struct mmc_request *mrq = &cmdq_req->mrq;
+
+ mrq->done = mmc_blk_cmdq_req_done;
+ return mmc_cmdq_start_req(host, cmdq_req);
+}
+
+/* prepare for non-data commands */
+static struct mmc_cmdq_req *mmc_cmdq_prep_dcmd(
+ struct mmc_queue_req *mqrq, struct mmc_queue *mq)
+{
+ struct request *req = mqrq->req;
+ struct mmc_cmdq_req *cmdq_req = &mqrq->cmdq_req;
+
+ memset(&mqrq->cmdq_req, 0, sizeof(struct mmc_cmdq_req));
+
+ cmdq_req->mrq.data = NULL;
+ cmdq_req->cmd_flags = req->cmd_flags;
+ cmdq_req->mrq.req = mqrq->req;
+ req->special = mqrq;
+ cmdq_req->cmdq_req_flags |= DCMD;
+ cmdq_req->mrq.cmdq_req = cmdq_req;
+
+ return &mqrq->cmdq_req;
+}
+
+
+#define IS_RT_CLASS_REQ(x) \
+ (IOPRIO_PRIO_CLASS(req_get_ioprio(x)) == IOPRIO_CLASS_RT)
+
+static struct mmc_cmdq_req *mmc_blk_cmdq_rw_prep(
+ struct mmc_queue_req *mqrq, struct mmc_queue *mq)
+{
+ struct mmc_card *card = mq->card;
+ struct request *req = mqrq->req;
+ struct mmc_blk_data *md = mq->data;
+ bool do_rel_wr = mmc_req_rel_wr(req) && (md->flags & MMC_BLK_REL_WR);
+ bool do_data_tag;
+ bool read_dir = (rq_data_dir(req) == READ);
+ bool prio = IS_RT_CLASS_REQ(req);
+ struct mmc_cmdq_req *cmdq_rq = &mqrq->cmdq_req;
+
+ memset(&mqrq->cmdq_req, 0, sizeof(struct mmc_cmdq_req));
+
+ cmdq_rq->tag = req->tag;
+ if (read_dir) {
+ cmdq_rq->cmdq_req_flags |= DIR;
+ cmdq_rq->data.flags = MMC_DATA_READ;
+ } else {
+ cmdq_rq->data.flags = MMC_DATA_WRITE;
+ }
+ if (prio)
+ cmdq_rq->cmdq_req_flags |= PRIO;
+
+ if (do_rel_wr)
+ cmdq_rq->cmdq_req_flags |= REL_WR;
+
+ cmdq_rq->data.blocks = blk_rq_sectors(req);
+ cmdq_rq->blk_addr = blk_rq_pos(req);
+ cmdq_rq->data.blksz = MMC_CARD_CMDQ_BLK_SIZE;
+
+ mmc_set_data_timeout(&cmdq_rq->data, card);
+
+ do_data_tag = (card->ext_csd.data_tag_unit_size) &&
+ (req->cmd_flags & REQ_META) &&
+ (rq_data_dir(req) == WRITE) &&
+ ((cmdq_rq->data.blocks * cmdq_rq->data.blksz) >=
+ card->ext_csd.data_tag_unit_size);
+ if (do_data_tag)
+ cmdq_rq->cmdq_req_flags |= DAT_TAG;
+ cmdq_rq->data.sg = mqrq->sg;
+ cmdq_rq->data.sg_len = mmc_queue_map_sg(mq, mqrq);
+
+ /*
+ * Adjust the sg list so it is the same size as the
+ * request.
+ */
+ if (cmdq_rq->data.blocks > card->host->max_blk_count)
+ cmdq_rq->data.blocks = card->host->max_blk_count;
+
+ if (cmdq_rq->data.blocks != blk_rq_sectors(req)) {
+ int i, data_size = cmdq_rq->data.blocks << 9;
+ struct scatterlist *sg;
+
+ for_each_sg(cmdq_rq->data.sg, sg, cmdq_rq->data.sg_len, i) {
+ data_size -= sg->length;
+ if (data_size <= 0) {
+ sg->length += data_size;
+ i++;
+ break;
+ }
+ }
+ cmdq_rq->data.sg_len = i;
+ }
+
+ mqrq->cmdq_req.cmd_flags = req->cmd_flags;
+ mqrq->cmdq_req.mrq.req = mqrq->req;
+ mqrq->cmdq_req.mrq.cmdq_req = &mqrq->cmdq_req;
+ mqrq->cmdq_req.mrq.data = &mqrq->cmdq_req.data;
+ mqrq->req->special = mqrq;
+
+ pr_debug("%s: %s: mrq: 0x%p req: 0x%p mqrq: 0x%p bytes to xf: %d mmc_cmdq_req: 0x%p card-addr: 0x%08x dir(r-1/w-0): %d\n",
+ mmc_hostname(card->host), __func__, &mqrq->cmdq_req.mrq,
+ mqrq->req, mqrq, (cmdq_rq->data.blocks * cmdq_rq->data.blksz),
+ cmdq_rq, cmdq_rq->blk_addr,
+ (cmdq_rq->cmdq_req_flags & DIR) ? 1 : 0);
+
+ return &mqrq->cmdq_req;
+}
+
+static int mmc_blk_cmdq_issue_rw_rq(struct mmc_queue *mq, struct request *req)
+{
+ struct mmc_queue_req *active_mqrq;
+ struct mmc_card *card = mq->card;
+ struct mmc_host *host = card->host;
+ struct mmc_cmdq_context_info *ctx = &host->cmdq_ctx;
+ struct mmc_cmdq_req *mc_rq;
+ u8 active_small_sector_read = 0;
+ int ret = 0;
+
+ mmc_deferred_scaling(host);
+ mmc_cmdq_clk_scaling_start_busy(host, true);
+
+ BUG_ON((req->tag < 0) || (req->tag > card->ext_csd.cmdq_depth));
+ BUG_ON(test_and_set_bit(req->tag, &host->cmdq_ctx.data_active_reqs));
+ BUG_ON(test_and_set_bit(req->tag, &host->cmdq_ctx.active_reqs));
+
+ active_mqrq = &mq->mqrq_cmdq[req->tag];
+ active_mqrq->req = req;
+
+ mc_rq = mmc_blk_cmdq_rw_prep(active_mqrq, mq);
+
+ if (card->quirks & MMC_QUIRK_CMDQ_EMPTY_BEFORE_DCMD) {
+ unsigned int sectors = blk_rq_sectors(req);
+
+ if (((sectors > 0) && (sectors < 8))
+ && (rq_data_dir(req) == READ))
+ active_small_sector_read = 1;
+ }
+ ret = mmc_blk_cmdq_start_req(card->host, mc_rq);
+ if (!ret && active_small_sector_read)
+ host->cmdq_ctx.active_small_sector_read_reqs++;
+ /*
+ * When in SVS2 on low load scenario and there are lots of requests
+ * queued for CMDQ we need to wait till the queue is empty to scale
+ * back up to Nominal even if there is a sudden increase in load.
+ * This impacts performance where lots of IO get executed in SVS2
+ * frequency since the queue is full. As SVS2 is a low load use case
+ * we can serialize the requests and not queue them in parallel
+ * without impacting other use cases. This makes sure the queue gets
+ * empty faster and we will be able to scale up to Nominal frequency
+ * when needed.
+ */
+ if (!ret && (host->clk_scaling.state == MMC_LOAD_LOW))
+ wait_event_interruptible(ctx->queue_empty_wq,
+ (!ctx->active_reqs));
+
+ return ret;
+}
+
+/*
+ * Issues a flush (dcmd) request
+ */
+int mmc_blk_cmdq_issue_flush_rq(struct mmc_queue *mq, struct request *req)
+{
+ int err;
+ struct mmc_queue_req *active_mqrq;
+ struct mmc_card *card = mq->card;
+ struct mmc_host *host;
+ struct mmc_cmdq_req *cmdq_req;
+ struct mmc_cmdq_context_info *ctx_info;
+
+ BUG_ON(!card);
+ host = card->host;
+ BUG_ON(!host);
+ BUG_ON(req->tag > card->ext_csd.cmdq_depth);
+ BUG_ON(test_and_set_bit(req->tag, &host->cmdq_ctx.active_reqs));
+
+ ctx_info = &host->cmdq_ctx;
+
+ set_bit(CMDQ_STATE_DCMD_ACTIVE, &ctx_info->curr_state);
+
+ active_mqrq = &mq->mqrq_cmdq[req->tag];
+ active_mqrq->req = req;
+
+ cmdq_req = mmc_cmdq_prep_dcmd(active_mqrq, mq);
+ cmdq_req->cmdq_req_flags |= QBR;
+ cmdq_req->mrq.cmd = &cmdq_req->cmd;
+ cmdq_req->tag = req->tag;
+
+ err = mmc_cmdq_prepare_flush(cmdq_req->mrq.cmd);
+ if (err) {
+ pr_err("%s: failed (%d) preparing flush req\n",
+ mmc_hostname(host), err);
+ return err;
+ }
+ err = mmc_blk_cmdq_start_req(card->host, cmdq_req);
+ return err;
+}
+EXPORT_SYMBOL(mmc_blk_cmdq_issue_flush_rq);
+
+static void mmc_blk_cmdq_reset(struct mmc_host *host, bool clear_all)
+{
+ int err = 0;
+
+ if (mmc_cmdq_halt(host, true)) {
+ pr_err("%s: halt failed\n", mmc_hostname(host));
+ goto reset;
+ }
+
+ if (clear_all)
+ mmc_cmdq_discard_queue(host, 0);
+reset:
+ mmc_host_clk_hold(host);
+ host->cmdq_ops->disable(host, true);
+ mmc_host_clk_release(host);
+ err = mmc_cmdq_hw_reset(host);
+ if (err && err != -EOPNOTSUPP) {
+ pr_err("%s: failed to cmdq_hw_reset err = %d\n",
+ mmc_hostname(host), err);
+ mmc_host_clk_hold(host);
+ host->cmdq_ops->enable(host);
+ mmc_host_clk_release(host);
+ mmc_cmdq_halt(host, false);
+ goto out;
+ }
+ /*
+ * CMDQ HW reset would have already made CQE
+ * in unhalted state, but reflect the same
+ * in software state of cmdq_ctx.
+ */
+ mmc_host_clr_halt(host);
+out:
+ return;
+}
+
+/**
+ * is_cmdq_dcmd_req - Checks if tag belongs to DCMD request.
+ * @q: request_queue pointer.
+ * @tag: tag number of request to check.
+ *
+ * This function checks if the request with tag number "tag"
+ * is a DCMD request or not based on cmdq_req_flags set.
+ *
+ * returns true if DCMD req, otherwise false.
+ */
+static bool is_cmdq_dcmd_req(struct request_queue *q, int tag)
+{
+ struct request *req;
+ struct mmc_queue_req *mq_rq;
+ struct mmc_cmdq_req *cmdq_req;
+
+ req = blk_queue_find_tag(q, tag);
+ if (WARN_ON(!req))
+ goto out;
+ mq_rq = req->special;
+ if (WARN_ON(!mq_rq))
+ goto out;
+ cmdq_req = &(mq_rq->cmdq_req);
+ return (cmdq_req->cmdq_req_flags & DCMD);
+out:
+ return -ENOENT;
+}
+
+/**
+ * mmc_blk_cmdq_reset_all - Reset everything for CMDQ block request.
+ * @host: mmc_host pointer.
+ * @err: error for which reset is performed.
+ *
+ * This function implements reset_all functionality for
+ * cmdq. It resets the controller, power cycle the card,
+ * and invalidate all busy tags(requeue all request back to
+ * elevator).
+ */
+static void mmc_blk_cmdq_reset_all(struct mmc_host *host, int err)
+{
+ struct mmc_request *mrq = host->err_mrq;
+ struct mmc_card *card = host->card;
+ struct mmc_cmdq_context_info *ctx_info = &host->cmdq_ctx;
+ struct request_queue *q;
+ int itag = 0;
+ int ret = 0;
+
+ if (WARN_ON(!mrq))
+ return;
+
+ q = mrq->req->q;
+ WARN_ON(!test_bit(CMDQ_STATE_ERR, &ctx_info->curr_state));
+
+ #ifdef CONFIG_MMC_CLKGATE
+ pr_debug("%s: %s: active_reqs = %lu, clk_requests = %d\n",
+ mmc_hostname(host), __func__,
+ ctx_info->active_reqs, host->clk_requests);
+ #endif
+
+ mmc_blk_cmdq_reset(host, false);
+
+ for_each_set_bit(itag, &ctx_info->active_reqs,
+ host->num_cq_slots) {
+ ret = is_cmdq_dcmd_req(q, itag);
+ if (WARN_ON(ret == -ENOENT))
+ continue;
+ if (!ret) {
+ WARN_ON(!test_and_clear_bit(itag,
+ &ctx_info->data_active_reqs));
+ mmc_cmdq_post_req(host, itag, err);
+ } else {
+ clear_bit(CMDQ_STATE_DCMD_ACTIVE,
+ &ctx_info->curr_state);
+ }
+ WARN_ON(!test_and_clear_bit(itag,
+ &ctx_info->active_reqs));
+ mmc_host_clk_release(host);
+ mmc_put_card(card);
+ }
+
+ spin_lock_irq(q->queue_lock);
+ blk_queue_invalidate_tags(q);
+ spin_unlock_irq(q->queue_lock);
+}
+
+static void mmc_blk_cmdq_shutdown(struct mmc_queue *mq)
+{
+ int err;
+ struct mmc_card *card = mq->card;
+ struct mmc_host *host = card->host;
+
+ mmc_get_card(card);
+ mmc_host_clk_hold(host);
+ err = mmc_cmdq_halt(host, true);
+ if (err) {
+ pr_err("%s: halt: failed: %d\n", __func__, err);
+ goto out;
+ }
+
+ /* disable CQ mode in card */
+ if (mmc_card_cmdq(card)) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_CMDQ, 0,
+ card->ext_csd.generic_cmd6_time);
+ if (err) {
+ pr_err("%s: failed to switch card to legacy mode: %d\n",
+ __func__, err);
+ goto out;
+ }
+ mmc_card_clr_cmdq(card);
+ }
+ host->cmdq_ops->disable(host, false);
+ host->card->cmdq_init = false;
+out:
+ mmc_host_clk_release(host);
+ mmc_put_card(card);
+}
+
+static enum blk_eh_timer_return mmc_blk_cmdq_req_timed_out(struct request *req)
+{
+ struct mmc_queue *mq = req->q->queuedata;
+ struct mmc_host *host = mq->card->host;
+ struct mmc_queue_req *mq_rq = req->special;
+ struct mmc_request *mrq;
+ struct mmc_cmdq_req *cmdq_req;
+ struct mmc_cmdq_context_info *ctx_info = &host->cmdq_ctx;
+
+ BUG_ON(!host);
+
+ /*
+ * The mmc_queue_req will be present only if the request
+ * is issued to the LLD. The request could be fetched from
+ * block layer queue but could be waiting to be issued
+ * (for e.g. clock scaling is waiting for an empty cmdq queue)
+ * Reset the timer in such cases to give LLD more time
+ */
+ if (!mq_rq) {
+ pr_warn("%s: restart timer for tag: %d\n", __func__, req->tag);
+ return BLK_EH_RESET_TIMER;
+ }
+
+ mrq = &mq_rq->cmdq_req.mrq;
+ cmdq_req = &mq_rq->cmdq_req;
+
+ BUG_ON(!mrq || !cmdq_req);
+
+ if (cmdq_req->cmdq_req_flags & DCMD)
+ mrq->cmd->error = -ETIMEDOUT;
+ else
+ mrq->data->error = -ETIMEDOUT;
+
+ if (mrq->cmd && mrq->cmd->error) {
+ if (!(mrq->req->cmd_flags & REQ_PREFLUSH)) {
+ /*
+ * Notify completion for non flush commands like
+ * discard that wait for DCMD finish.
+ */
+ set_bit(CMDQ_STATE_REQ_TIMED_OUT,
+ &ctx_info->curr_state);
+ complete(&mrq->completion);
+ return BLK_EH_NOT_HANDLED;
+ }
+ }
+
+ if (test_bit(CMDQ_STATE_REQ_TIMED_OUT, &ctx_info->curr_state) ||
+ test_bit(CMDQ_STATE_ERR, &ctx_info->curr_state))
+ return BLK_EH_NOT_HANDLED;
+
+ set_bit(CMDQ_STATE_REQ_TIMED_OUT, &ctx_info->curr_state);
+ return BLK_EH_HANDLED;
+}
+
+/*
+ * mmc_blk_cmdq_err: error handling of cmdq error requests.
+ * Function should be called in context of error out request
+ * which has claim_host and rpm acquired.
+ * This may be called with CQ engine halted. Make sure to
+ * unhalt it after error recovery.
+ *
+ * TODO: Currently cmdq error handler does reset_all in case
+ * of any erorr. Need to optimize error handling.
+ */
+static void mmc_blk_cmdq_err(struct mmc_queue *mq)
+{
+ struct mmc_host *host = mq->card->host;
+ struct mmc_request *mrq = host->err_mrq;
+ struct mmc_cmdq_context_info *ctx_info = &host->cmdq_ctx;
+ struct request_queue *q;
+ int err, ret;
+ u32 status = 0;
+
+ mmc_host_clk_hold(host);
+ host->cmdq_ops->dumpstate(host);
+ mmc_host_clk_release(host);
+
+ if (WARN_ON(!mrq))
+ return;
+
+ q = mrq->req->q;
+ err = mmc_cmdq_halt(host, true);
+ if (err) {
+ pr_err("halt: failed: %d\n", err);
+ goto reset;
+ }
+
+ /* RED error - Fatal: requires reset */
+ if (mrq->cmdq_req->resp_err) {
+ err = mrq->cmdq_req->resp_err;
+ if (mmc_host_halt(host) || mmc_host_cq_disable(host)) {
+ ret = get_card_status(host->card, &status, 0);
+ if (ret)
+ pr_err("%s: CMD13 failed with err %d\n",
+ mmc_hostname(host), ret);
+ }
+ pr_err("%s: Response error detected with device status 0x%08x\n",
+ mmc_hostname(host), status);
+ goto reset;
+ }
+
+ /*
+ * In case of software request time-out, we schedule err work only for
+ * the first error out request and handles all other request in flight
+ * here.
+ */
+ if (test_bit(CMDQ_STATE_REQ_TIMED_OUT, &ctx_info->curr_state)) {
+ err = -ETIMEDOUT;
+ } else if (mrq->data && mrq->data->error) {
+ err = mrq->data->error;
+ } else if (mrq->cmd && mrq->cmd->error) {
+ /* DCMD commands */
+ err = mrq->cmd->error;
+ }
+
+reset:
+ mmc_blk_cmdq_reset_all(host, err);
+ if (mrq->cmdq_req->resp_err)
+ mrq->cmdq_req->resp_err = false;
+ mmc_cmdq_halt(host, false);
+
+ host->err_mrq = NULL;
+ clear_bit(CMDQ_STATE_REQ_TIMED_OUT, &ctx_info->curr_state);
+ WARN_ON(!test_and_clear_bit(CMDQ_STATE_ERR, &ctx_info->curr_state));
+ wake_up(&ctx_info->wait);
+}
+
+/* invoked by block layer in softirq context */
+void mmc_blk_cmdq_complete_rq(struct request *rq)
+{
+ struct mmc_queue_req *mq_rq = rq->special;
+ struct mmc_request *mrq = &mq_rq->cmdq_req.mrq;
+ struct mmc_host *host = mrq->host;
+ struct mmc_cmdq_context_info *ctx_info = &host->cmdq_ctx;
+ struct mmc_cmdq_req *cmdq_req = &mq_rq->cmdq_req;
+ struct mmc_queue *mq = (struct mmc_queue *)rq->q->queuedata;
+ int err = 0;
+ bool is_dcmd = false;
+
+ if (mrq->cmd && mrq->cmd->error)
+ err = mrq->cmd->error;
+ else if (mrq->data && mrq->data->error)
+ err = mrq->data->error;
+
+ if (err || cmdq_req->resp_err) {
+ pr_err("%s: %s: txfr error(%d)/resp_err(%d)\n",
+ mmc_hostname(mrq->host), __func__, err,
+ cmdq_req->resp_err);
+ if (test_bit(CMDQ_STATE_ERR, &ctx_info->curr_state)) {
+ pr_err("%s: CQ in error state, ending current req: %d\n",
+ __func__, err);
+ } else {
+ set_bit(CMDQ_STATE_ERR, &ctx_info->curr_state);
+ BUG_ON(host->err_mrq != NULL);
+ host->err_mrq = mrq;
+ schedule_work(&mq->cmdq_err_work);
+ }
+ goto out;
+ }
+ /*
+ * In case of error CMDQ is expected to be either in halted
+ * or disable state so cannot receive any completion of
+ * other requests.
+ */
+ BUG_ON(test_bit(CMDQ_STATE_ERR, &ctx_info->curr_state));
+
+ /* clear pending request */
+ BUG_ON(!test_and_clear_bit(cmdq_req->tag,
+ &ctx_info->active_reqs));
+ if (cmdq_req->cmdq_req_flags & DCMD)
+ is_dcmd = true;
+ else
+ BUG_ON(!test_and_clear_bit(cmdq_req->tag,
+ &ctx_info->data_active_reqs));
+ if (!is_dcmd)
+ mmc_cmdq_post_req(host, cmdq_req->tag, err);
+ if (cmdq_req->cmdq_req_flags & DCMD) {
+ clear_bit(CMDQ_STATE_DCMD_ACTIVE, &ctx_info->curr_state);
+ blk_end_request_all(rq, err);
+ goto out;
+ }
+
+ blk_end_request(rq, err, cmdq_req->data.bytes_xfered);
+
+out:
+
+ mmc_cmdq_clk_scaling_stop_busy(host, true, is_dcmd);
+ if (!test_bit(CMDQ_STATE_ERR, &ctx_info->curr_state)) {
+ mmc_host_clk_release(host);
+ wake_up(&ctx_info->wait);
+ mmc_put_card(host->card);
+ }
+
+ if (!ctx_info->active_reqs)
+ wake_up_interruptible(&host->cmdq_ctx.queue_empty_wq);
+
+ if (blk_queue_stopped(mq->queue) && !ctx_info->active_reqs)
+ complete(&mq->cmdq_shutdown_complete);
+
+ return;
+}
+
+/*
+ * Complete reqs from block layer softirq context
+ * Invoked in irq context
+ */
+void mmc_blk_cmdq_req_done(struct mmc_request *mrq)
+{
+ struct request *req = mrq->req;
+
+ blk_complete_request(req);
+}
+EXPORT_SYMBOL(mmc_blk_cmdq_req_done);
+
static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
{
struct mmc_blk_data *md = mq->data;
@@ -2261,7 +3675,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
areq = mmc_start_req(card->host, areq, (int *) &status);
if (!areq) {
if (status == MMC_BLK_NEW_REQUEST)
- mq->flags |= MMC_QUEUE_NEW_REQUEST;
+ set_bit(MMC_QUEUE_NEW_REQUEST, &mq->flags);
return 0;
}
@@ -2311,11 +3725,12 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
break;
case MMC_BLK_RETRY:
retune_retry_done = brq->retune_retry_done;
- if (retry++ < 5)
+ if (retry++ < MMC_BLK_MAX_RETRIES)
break;
/* Fall through */
case MMC_BLK_ABORT:
- if (!mmc_blk_reset(md, card->host, type))
+ if (!mmc_blk_reset(md, card->host, type) &&
+ (retry++ < (MMC_BLK_MAX_RETRIES + 1)))
break;
goto cmd_abort;
case MMC_BLK_DATA_ERR: {
@@ -2408,6 +3823,132 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
return 0;
}
+static inline int mmc_blk_cmdq_part_switch(struct mmc_card *card,
+ struct mmc_blk_data *md)
+{
+ struct mmc_blk_data *main_md = mmc_get_drvdata(card);
+ struct mmc_host *host = card->host;
+ struct mmc_cmdq_context_info *ctx = &host->cmdq_ctx;
+ u8 part_config = card->ext_csd.part_config;
+
+ if ((main_md->part_curr == md->part_type) &&
+ (card->part_curr == md->part_type))
+ return 0;
+
+ WARN_ON(!((card->host->caps2 & MMC_CAP2_CMD_QUEUE) &&
+ card->ext_csd.cmdq_support &&
+ (md->flags & MMC_BLK_CMD_QUEUE)));
+
+ if (!test_bit(CMDQ_STATE_HALT, &ctx->curr_state))
+ WARN_ON(mmc_cmdq_halt(host, true));
+
+ /* disable CQ mode in card */
+ if (mmc_card_cmdq(card)) {
+ WARN_ON(mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_CMDQ, 0,
+ card->ext_csd.generic_cmd6_time));
+ mmc_card_clr_cmdq(card);
+ }
+
+ part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
+ part_config |= md->part_type;
+
+ WARN_ON(mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_PART_CONFIG, part_config,
+ card->ext_csd.part_time));
+
+ card->ext_csd.part_config = part_config;
+ card->part_curr = md->part_type;
+
+ main_md->part_curr = md->part_type;
+
+ WARN_ON(mmc_blk_cmdq_switch(card, md, true));
+ WARN_ON(mmc_cmdq_halt(host, false));
+
+ return 0;
+}
+
+static int mmc_blk_cmdq_issue_rq(struct mmc_queue *mq, struct request *req)
+{
+ int ret;
+ struct mmc_blk_data *md = mq->data;
+ struct mmc_card *card = md->queue.card;
+
+ mmc_get_card(card);
+
+ if (!card->host->cmdq_ctx.active_reqs && mmc_card_doing_bkops(card)) {
+ ret = mmc_cmdq_halt(card->host, true);
+ if (ret)
+ goto out;
+ ret = mmc_stop_bkops(card);
+ if (ret) {
+ pr_err("%s: %s: mmc_stop_bkops failed %d\n",
+ md->disk->disk_name, __func__, ret);
+ goto out;
+ }
+ ret = mmc_cmdq_halt(card->host, false);
+ if (ret)
+ goto out;
+ }
+
+ ret = mmc_blk_cmdq_part_switch(card, md);
+ if (ret) {
+ pr_err("%s: %s: partition switch failed %d\n",
+ md->disk->disk_name, __func__, ret);
+ goto out;
+ }
+
+ if (req) {
+ struct mmc_host *host = card->host;
+ struct mmc_cmdq_context_info *ctx = &host->cmdq_ctx;
+
+ if ((req_op(req) == REQ_OP_FLUSH || req_op(req) == REQ_OP_DISCARD) &&
+ (card->quirks & MMC_QUIRK_CMDQ_EMPTY_BEFORE_DCMD) &&
+ ctx->active_small_sector_read_reqs) {
+ ret = wait_event_interruptible(ctx->queue_empty_wq,
+ !ctx->active_reqs);
+ if (ret) {
+ pr_err("%s: failed while waiting for the CMDQ to be empty %s err (%d)\n",
+ mmc_hostname(host),
+ __func__, ret);
+ BUG_ON(1);
+ }
+ /* clear the counter now */
+ ctx->active_small_sector_read_reqs = 0;
+ /*
+ * If there were small sector (less than 8 sectors) read
+ * operations in progress then we have to wait for the
+ * outstanding requests to finish and should also have
+ * atleast 6 microseconds delay before queuing the DCMD
+ * request.
+ */
+ udelay(MMC_QUIRK_CMDQ_DELAY_BEFORE_DCMD);
+ }
+
+ if (req_op(req) == REQ_OP_DISCARD) {
+ ret = mmc_blk_cmdq_issue_discard_rq(mq, req);
+ } else if (req_op(req) == REQ_OP_SECURE_ERASE) {
+ if (!(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN))
+ ret = mmc_blk_cmdq_issue_secdiscard_rq(mq, req);
+ else
+ ret = mmc_blk_cmdq_issue_discard_rq(mq, req);
+ } else if (req_op(req) == REQ_OP_FLUSH) {
+ ret = mmc_blk_cmdq_issue_flush_rq(mq, req);
+ } else {
+ ret = mmc_blk_cmdq_issue_rw_rq(mq, req);
+ }
+ }
+
+ return ret;
+
+out:
+ if (req)
+ blk_end_request_all(req, ret);
+ mmc_put_card(card);
+
+ return ret;
+}
+
int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
{
int ret;
@@ -2415,14 +3956,33 @@ int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
struct mmc_card *card = md->queue.card;
struct mmc_host *host = card->host;
unsigned long flags;
+ unsigned int cmd_flags = req ? req->cmd_flags : 0;
bool req_is_special = mmc_req_is_special(req);
+ int err;
- if (req && !mq->mqrq_prev->req)
+ if (req && !mq->mqrq_prev->req) {
/* claim host only for the first request */
mmc_get_card(card);
+ if (mmc_card_doing_bkops(host->card)) {
+ ret = mmc_stop_bkops(host->card);
+ if (ret)
+ goto out;
+ }
+ }
+
ret = mmc_blk_part_switch(card, md);
+
if (ret) {
+ err = mmc_blk_reset(md, card->host, MMC_BLK_PARTSWITCH);
+ if (!err) {
+ pr_err("%s: mmc_blk_reset(MMC_BLK_PARTSWITCH) succeeded.\n",
+ mmc_hostname(host));
+ mmc_blk_reset_success(md, MMC_BLK_PARTSWITCH);
+ } else
+ pr_err("%s: mmc_blk_reset(MMC_BLK_PARTSWITCH) failed.\n",
+ mmc_hostname(host));
+
if (req) {
blk_end_request_all(req, -EIO);
}
@@ -2430,7 +3990,9 @@ int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
goto out;
}
- mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
+ mmc_blk_write_packing_control(mq, req);
+
+ clear_bit(MMC_QUEUE_NEW_REQUEST, &mq->flags);
if (req && req_op(req) == REQ_OP_DISCARD) {
/* complete ongoing async transfer before issuing discard */
if (card->host->areq)
@@ -2440,8 +4002,12 @@ int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
/* complete ongoing async transfer before issuing secure erase*/
if (card->host->areq)
mmc_blk_issue_rw_rq(mq, NULL);
- ret = mmc_blk_issue_secdiscard_rq(mq, req);
- } else if (req && req_op(req) == REQ_OP_FLUSH) {
+ if (!(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN))
+ ret = mmc_blk_issue_secdiscard_rq(mq, req);
+ else
+ ret = mmc_blk_issue_discard_rq(mq, req);
+ } else if ((req && req_op(req) == REQ_OP_FLUSH) ||
+ (cmd_flags & REQ_BARRIER)) {
/* complete ongoing async transfer before issuing flush */
if (card->host->areq)
mmc_blk_issue_rw_rq(mq, NULL);
@@ -2456,7 +4022,8 @@ int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
}
out:
- if ((!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) || req_is_special)
+ if ((!req && !(test_bit(MMC_QUEUE_NEW_REQUEST, &mq->flags))) ||
+ req_is_special)
/*
* Release host when there are no more requests
* and after special request(discard, flush) is done.
@@ -2525,7 +4092,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
INIT_LIST_HEAD(&md->part);
md->usage = 1;
- ret = mmc_init_queue(&md->queue, card, &md->lock, subname);
+ ret = mmc_init_queue(&md->queue, card, NULL, subname, area_type);
if (ret)
goto err_putdisk;
@@ -2581,7 +4148,16 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
blk_queue_write_cache(md->queue.queue, true, true);
}
- if (mmc_card_mmc(card) &&
+ if (card->cmdq_init) {
+ md->flags |= MMC_BLK_CMD_QUEUE;
+ md->queue.cmdq_complete_fn = mmc_blk_cmdq_complete_rq;
+ md->queue.cmdq_issue_fn = mmc_blk_cmdq_issue_rq;
+ md->queue.cmdq_error_fn = mmc_blk_cmdq_err;
+ md->queue.cmdq_req_timed_out = mmc_blk_cmdq_req_timed_out;
+ md->queue.cmdq_shutdown = mmc_blk_cmdq_shutdown;
+ }
+
+ if (mmc_card_mmc(card) && !card->cmdq_init &&
(area_type == MMC_BLK_DATA_AREA_MAIN) &&
(md->flags & MMC_BLK_CMD23) &&
card->ext_csd.packed_event_en) {
@@ -2694,6 +4270,10 @@ static void mmc_blk_remove_req(struct mmc_blk_data *md)
mmc_cleanup_queue(&md->queue);
if (md->flags & MMC_BLK_PACKED_CMD)
mmc_packed_clean(&md->queue);
+ if (md->flags & MMC_BLK_CMD_QUEUE)
+ mmc_cmdq_clean(&md->queue, card);
+ device_remove_file(disk_to_dev(md->disk),
+ &md->num_wr_reqs_to_start_packing);
if (md->disk->flags & GENHD_FL_UP) {
device_remove_file(disk_to_dev(md->disk), &md->force_ro);
if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
@@ -2781,8 +4361,37 @@ static int mmc_add_disk(struct mmc_blk_data *md)
if (ret)
goto power_ro_lock_fail;
}
+
+ md->num_wr_reqs_to_start_packing.show =
+ num_wr_reqs_to_start_packing_show;
+ md->num_wr_reqs_to_start_packing.store =
+ num_wr_reqs_to_start_packing_store;
+ sysfs_attr_init(&md->num_wr_reqs_to_start_packing.attr);
+ md->num_wr_reqs_to_start_packing.attr.name =
+ "num_wr_reqs_to_start_packing";
+ md->num_wr_reqs_to_start_packing.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(disk_to_dev(md->disk),
+ &md->num_wr_reqs_to_start_packing);
+ if (ret)
+ goto num_wr_reqs_to_start_packing_fail;
+
+ md->no_pack_for_random.show = no_pack_for_random_show;
+ md->no_pack_for_random.store = no_pack_for_random_store;
+ sysfs_attr_init(&md->no_pack_for_random.attr);
+ md->no_pack_for_random.attr.name = "no_pack_for_random";
+ md->no_pack_for_random.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(disk_to_dev(md->disk),
+ &md->no_pack_for_random);
+ if (ret)
+ goto no_pack_for_random_fails;
+
return ret;
+no_pack_for_random_fails:
+ device_remove_file(disk_to_dev(md->disk),
+ &md->num_wr_reqs_to_start_packing);
+num_wr_reqs_to_start_packing_fail:
+ device_remove_file(disk_to_dev(md->disk), &md->power_ro_lock);
power_ro_lock_fail:
#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
device_remove_file(disk_to_dev(md->disk), &dev_attr_cache_size);
@@ -2830,6 +4439,8 @@ static const struct mmc_fixup blk_fixups[] =
MMC_QUIRK_BLK_NO_CMD23),
MMC_FIXUP("MMC32G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
MMC_QUIRK_BLK_NO_CMD23),
+ MMC_FIXUP(CID_NAME_ANY, CID_MANFID_TOSHIBA, CID_OEMID_ANY,
+ add_quirk_mmc, MMC_QUIRK_CMDQ_EMPTY_BEFORE_DCMD),
/*
* Some MMC cards need longer data read timeout than indicated in CSD.
@@ -2847,6 +4458,13 @@ static const struct mmc_fixup blk_fixups[] =
MMC_QUIRK_LONG_READ_TIME),
/*
+ * Hynix eMMC cards need longer data read timeout than
+ * indicated in CSD.
+ */
+ MMC_FIXUP(CID_NAME_ANY, CID_MANFID_HYNIX, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_LONG_READ_TIME),
+
+ /*
* On these Samsung MoviNAND parts, performing secure erase or
* secure trim can result in unrecoverable corruption due to a
* firmware bug.
@@ -2877,6 +4495,10 @@ static const struct mmc_fixup blk_fixups[] =
MMC_FIXUP("V10016", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc,
MMC_QUIRK_TRIM_BROKEN),
+ /* Some INAND MCP devices advertise incorrect timeout values */
+ MMC_FIXUP("SEM04G", 0x45, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_INAND_DATA_TIMEOUT),
+
END_FIXUP
};
@@ -2916,7 +4538,8 @@ static int mmc_blk_probe(struct mmc_card *card)
goto out;
}
- pm_runtime_set_autosuspend_delay(&card->dev, 3000);
+ pm_runtime_use_autosuspend(&card->dev);
+ pm_runtime_set_autosuspend_delay(&card->dev, MMC_AUTOSUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(&card->dev);
/*
@@ -2952,23 +4575,40 @@ static void mmc_blk_remove(struct mmc_card *card)
dev_set_drvdata(&card->dev, NULL);
}
-static int _mmc_blk_suspend(struct mmc_card *card)
+static int _mmc_blk_suspend(struct mmc_card *card, bool wait)
{
struct mmc_blk_data *part_md;
struct mmc_blk_data *md = dev_get_drvdata(&card->dev);
+ int rc = 0;
if (md) {
- mmc_queue_suspend(&md->queue);
+ rc = mmc_queue_suspend(&md->queue, wait);
+ if (rc)
+ goto out;
list_for_each_entry(part_md, &md->part, part) {
- mmc_queue_suspend(&part_md->queue);
+ rc = mmc_queue_suspend(&part_md->queue, wait);
+ if (rc)
+ goto out_resume;
}
}
- return 0;
+ goto out;
+
+ out_resume:
+ mmc_queue_resume(&md->queue);
+ list_for_each_entry(part_md, &md->part, part) {
+ mmc_queue_resume(&part_md->queue);
+ }
+ out:
+ return rc;
}
static void mmc_blk_shutdown(struct mmc_card *card)
{
- _mmc_blk_suspend(card);
+ _mmc_blk_suspend(card, 1);
+
+ /* send power off notification */
+ if (mmc_card_mmc(card))
+ mmc_send_pon(card);
}
#ifdef CONFIG_PM_SLEEP
@@ -2976,7 +4616,7 @@ static int mmc_blk_suspend(struct device *dev)
{
struct mmc_card *card = mmc_dev_to_card(dev);
- return _mmc_blk_suspend(card);
+ return _mmc_blk_suspend(card, 0);
}
static int mmc_blk_resume(struct device *dev)
diff --git a/drivers/mmc/card/mmc_block_test.c b/drivers/mmc/card/mmc_block_test.c
new file mode 100644
index 0000000..967affa
--- /dev/null
+++ b/drivers/mmc/card/mmc_block_test.c
@@ -0,0 +1,2038 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This 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.
+ *
+ */
+
+/* MMC block test */
+
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/debugfs.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/delay.h>
+#include <linux/test-iosched.h>
+#include "queue.h"
+
+#define MODULE_NAME "mmc_block_test"
+#define TEST_MAX_SECTOR_RANGE (600*1024*1024) /* 600 MB */
+#define TEST_MAX_BIOS_PER_REQ 120
+#define CMD23_PACKED_BIT (1 << 30)
+#define LARGE_PRIME_1 1103515367
+#define LARGE_PRIME_2 35757
+#define PACKED_HDR_VER_MASK 0x000000FF
+#define PACKED_HDR_RW_MASK 0x0000FF00
+#define PACKED_HDR_NUM_REQS_MASK 0x00FF0000
+#define PACKED_HDR_BITS_16_TO_29_SET 0x3FFF0000
+
+#define test_pr_debug(fmt, args...) pr_debug("%s: "fmt"\n", MODULE_NAME, args)
+#define test_pr_info(fmt, args...) pr_info("%s: "fmt"\n", MODULE_NAME, args)
+#define test_pr_err(fmt, args...) pr_err("%s: "fmt"\n", MODULE_NAME, args)
+
+enum is_random {
+ NON_RANDOM_TEST,
+ RANDOM_TEST,
+};
+
+enum mmc_block_test_testcases {
+ /* Start of send write packing test group */
+ SEND_WRITE_PACKING_MIN_TESTCASE,
+ TEST_STOP_DUE_TO_READ = SEND_WRITE_PACKING_MIN_TESTCASE,
+ TEST_STOP_DUE_TO_READ_AFTER_MAX_REQS,
+ TEST_STOP_DUE_TO_FLUSH,
+ TEST_STOP_DUE_TO_FLUSH_AFTER_MAX_REQS,
+ TEST_STOP_DUE_TO_EMPTY_QUEUE,
+ TEST_STOP_DUE_TO_MAX_REQ_NUM,
+ TEST_STOP_DUE_TO_THRESHOLD,
+ SEND_WRITE_PACKING_MAX_TESTCASE = TEST_STOP_DUE_TO_THRESHOLD,
+
+ /* Start of err check test group */
+ ERR_CHECK_MIN_TESTCASE,
+ TEST_RET_ABORT = ERR_CHECK_MIN_TESTCASE,
+ TEST_RET_PARTIAL_FOLLOWED_BY_SUCCESS,
+ TEST_RET_PARTIAL_FOLLOWED_BY_ABORT,
+ TEST_RET_PARTIAL_MULTIPLE_UNTIL_SUCCESS,
+ TEST_RET_PARTIAL_MAX_FAIL_IDX,
+ TEST_RET_RETRY,
+ TEST_RET_CMD_ERR,
+ TEST_RET_DATA_ERR,
+ ERR_CHECK_MAX_TESTCASE = TEST_RET_DATA_ERR,
+
+ /* Start of send invalid test group */
+ INVALID_CMD_MIN_TESTCASE,
+ TEST_HDR_INVALID_VERSION = INVALID_CMD_MIN_TESTCASE,
+ TEST_HDR_WRONG_WRITE_CODE,
+ TEST_HDR_INVALID_RW_CODE,
+ TEST_HDR_DIFFERENT_ADDRESSES,
+ TEST_HDR_REQ_NUM_SMALLER_THAN_ACTUAL,
+ TEST_HDR_REQ_NUM_LARGER_THAN_ACTUAL,
+ TEST_HDR_CMD23_PACKED_BIT_SET,
+ TEST_CMD23_MAX_PACKED_WRITES,
+ TEST_CMD23_ZERO_PACKED_WRITES,
+ TEST_CMD23_PACKED_BIT_UNSET,
+ TEST_CMD23_REL_WR_BIT_SET,
+ TEST_CMD23_BITS_16TO29_SET,
+ TEST_CMD23_HDR_BLK_NOT_IN_COUNT,
+ INVALID_CMD_MAX_TESTCASE = TEST_CMD23_HDR_BLK_NOT_IN_COUNT,
+
+ /*
+ * Start of packing control test group.
+ * in these next testcases the abbreviation FB = followed by
+ */
+ PACKING_CONTROL_MIN_TESTCASE,
+ TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ =
+ PACKING_CONTROL_MIN_TESTCASE,
+ TEST_PACKING_EXP_N_OVER_TRIGGER,
+ TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ,
+ TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N,
+ TEST_PACKING_EXP_THRESHOLD_OVER_TRIGGER,
+ TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS,
+ TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS,
+ TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER,
+ TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER,
+ TEST_PACK_MIX_PACKED_NO_PACKED_PACKED,
+ TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED,
+ PACKING_CONTROL_MAX_TESTCASE = TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED,
+};
+
+enum mmc_block_test_group {
+ TEST_NO_GROUP,
+ TEST_GENERAL_GROUP,
+ TEST_SEND_WRITE_PACKING_GROUP,
+ TEST_ERR_CHECK_GROUP,
+ TEST_SEND_INVALID_GROUP,
+ TEST_PACKING_CONTROL_GROUP,
+};
+
+struct mmc_block_test_debug {
+ struct dentry *send_write_packing_test;
+ struct dentry *err_check_test;
+ struct dentry *send_invalid_packed_test;
+ struct dentry *random_test_seed;
+ struct dentry *packing_control_test;
+};
+
+struct mmc_block_test_data {
+ /* The number of write requests that the test will issue */
+ int num_requests;
+ /* The expected write packing statistics for the current test */
+ struct mmc_wr_pack_stats exp_packed_stats;
+ /*
+ * A user-defined seed for random choices of number of bios written in
+ * a request, and of number of requests issued in a test
+ * This field is randomly updated after each use
+ */
+ unsigned int random_test_seed;
+ /* A retry counter used in err_check tests */
+ int err_check_counter;
+ /* Can be one of the values of enum test_group */
+ enum mmc_block_test_group test_group;
+ /*
+ * Indicates if the current testcase is running with random values of
+ * num_requests and num_bios (in each request)
+ */
+ int is_random;
+ /* Data structure for debugfs dentrys */
+ struct mmc_block_test_debug debug;
+ /*
+ * Data structure containing individual test information, including
+ * self-defined specific data
+ */
+ struct test_info test_info;
+ /* mmc block device test */
+ struct blk_dev_test_type bdt;
+};
+
+static struct mmc_block_test_data *mbtd;
+
+void print_mmc_packing_stats(struct mmc_card *card)
+{
+ int i;
+ int max_num_of_packed_reqs = 0;
+
+ if ((!card) || (!card->wr_pack_stats.packing_events))
+ return;
+
+ max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
+
+ spin_lock(&card->wr_pack_stats.lock);
+
+ pr_info("%s: write packing statistics:\n",
+ mmc_hostname(card->host));
+
+ for (i = 1 ; i <= max_num_of_packed_reqs ; ++i) {
+ if (card->wr_pack_stats.packing_events[i] != 0)
+ pr_info("%s: Packed %d reqs - %d times\n",
+ mmc_hostname(card->host), i,
+ card->wr_pack_stats.packing_events[i]);
+ }
+
+ pr_info("%s: stopped packing due to the following reasons:\n",
+ mmc_hostname(card->host));
+
+ if (card->wr_pack_stats.pack_stop_reason[EXCEEDS_SEGMENTS])
+ pr_info("%s: %d times: exceedmax num of segments\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[EXCEEDS_SEGMENTS]);
+ if (card->wr_pack_stats.pack_stop_reason[EXCEEDS_SECTORS])
+ pr_info("%s: %d times: exceeding the max num of sectors\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[EXCEEDS_SECTORS]);
+ if (card->wr_pack_stats.pack_stop_reason[WRONG_DATA_DIR])
+ pr_info("%s: %d times: wrong data direction\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[WRONG_DATA_DIR]);
+ if (card->wr_pack_stats.pack_stop_reason[FLUSH_OR_DISCARD])
+ pr_info("%s: %d times: flush or discard\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[FLUSH_OR_DISCARD]);
+ if (card->wr_pack_stats.pack_stop_reason[EMPTY_QUEUE])
+ pr_info("%s: %d times: empty queue\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[EMPTY_QUEUE]);
+ if (card->wr_pack_stats.pack_stop_reason[REL_WRITE])
+ pr_info("%s: %d times: rel write\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[REL_WRITE]);
+ if (card->wr_pack_stats.pack_stop_reason[THRESHOLD])
+ pr_info("%s: %d times: Threshold\n",
+ mmc_hostname(card->host),
+ card->wr_pack_stats.pack_stop_reason[THRESHOLD]);
+
+ spin_unlock(&card->wr_pack_stats.lock);
+}
+
+/*
+ * A callback assigned to the packed_test_fn field.
+ * Called from block layer in mmc_blk_packed_hdr_wrq_prep.
+ * Here we alter the packed header or CMD23 in order to send an invalid
+ * packed command to the card.
+ */
+static void test_invalid_packed_cmd(struct request_queue *q,
+ struct mmc_queue_req *mqrq)
+{
+ struct mmc_queue *mq = q->queuedata;
+ u32 *packed_cmd_hdr = mqrq->packed->cmd_hdr;
+ struct request *req = mqrq->req;
+ struct request *second_rq;
+ struct test_request *test_rq;
+ struct mmc_blk_request *brq = &mqrq->brq;
+ int num_requests;
+ int max_packed_reqs;
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return;
+ }
+
+ test_rq = (struct test_request *)req->elv.priv[0];
+ if (!test_rq) {
+ test_pr_err("%s: NULL test_rq", __func__);
+ return;
+ }
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+
+ switch (mbtd->test_info.testcase) {
+ case TEST_HDR_INVALID_VERSION:
+ test_pr_info("%s: set invalid header version", __func__);
+ /* Put 0 in header version field (1 byte, offset 0 in header) */
+ packed_cmd_hdr[0] = packed_cmd_hdr[0] & ~PACKED_HDR_VER_MASK;
+ break;
+ case TEST_HDR_WRONG_WRITE_CODE:
+ test_pr_info("%s: wrong write code", __func__);
+ /* Set R/W field with R value (1 byte, offset 1 in header) */
+ packed_cmd_hdr[0] = packed_cmd_hdr[0] & ~PACKED_HDR_RW_MASK;
+ packed_cmd_hdr[0] = packed_cmd_hdr[0] | 0x00000100;
+ break;
+ case TEST_HDR_INVALID_RW_CODE:
+ test_pr_info("%s: invalid r/w code", __func__);
+ /* Set R/W field with invalid value */
+ packed_cmd_hdr[0] = packed_cmd_hdr[0] & ~PACKED_HDR_RW_MASK;
+ packed_cmd_hdr[0] = packed_cmd_hdr[0] | 0x00000400;
+ break;
+ case TEST_HDR_DIFFERENT_ADDRESSES:
+ test_pr_info("%s: different addresses", __func__);
+ second_rq = list_entry(req->queuelist.next, struct request,
+ queuelist);
+ test_pr_info("%s: test_rq->sector=%ld, second_rq->sector=%ld",
+ __func__, (long)req->__sector,
+ (long)second_rq->__sector);
+ /*
+ * Put start sector of second write request in the first write
+ * request's cmd25 argument in the packed header
+ */
+ packed_cmd_hdr[3] = second_rq->__sector;
+ break;
+ case TEST_HDR_REQ_NUM_SMALLER_THAN_ACTUAL:
+ test_pr_info("%s: request num smaller than actual" , __func__);
+ num_requests = (packed_cmd_hdr[0] & PACKED_HDR_NUM_REQS_MASK)
+ >> 16;
+ /* num of entries is decremented by 1 */
+ num_requests = (num_requests - 1) << 16;
+ /*
+ * Set number of requests field in packed write header to be
+ * smaller than the actual number (1 byte, offset 2 in header)
+ */
+ packed_cmd_hdr[0] = (packed_cmd_hdr[0] &
+ ~PACKED_HDR_NUM_REQS_MASK) + num_requests;
+ break;
+ case TEST_HDR_REQ_NUM_LARGER_THAN_ACTUAL:
+ test_pr_info("%s: request num larger than actual" , __func__);
+ num_requests = (packed_cmd_hdr[0] & PACKED_HDR_NUM_REQS_MASK)
+ >> 16;
+ /* num of entries is incremented by 1 */
+ num_requests = (num_requests + 1) << 16;
+ /*
+ * Set number of requests field in packed write header to be
+ * larger than the actual number (1 byte, offset 2 in header).
+ */
+ packed_cmd_hdr[0] = (packed_cmd_hdr[0] &
+ ~PACKED_HDR_NUM_REQS_MASK) + num_requests;
+ break;
+ case TEST_HDR_CMD23_PACKED_BIT_SET:
+ test_pr_info("%s: header CMD23 packed bit set" , __func__);
+ /*
+ * Set packed bit (bit 30) in cmd23 argument of first and second
+ * write requests in packed write header.
+ * These are located at bytes 2 and 4 in packed write header
+ */
+ packed_cmd_hdr[2] = packed_cmd_hdr[2] | CMD23_PACKED_BIT;
+ packed_cmd_hdr[4] = packed_cmd_hdr[4] | CMD23_PACKED_BIT;
+ break;
+ case TEST_CMD23_MAX_PACKED_WRITES:
+ test_pr_info("%s: CMD23 request num > max_packed_reqs",
+ __func__);
+ /*
+ * Set the individual packed cmd23 request num to
+ * max_packed_reqs + 1
+ */
+ brq->sbc.arg = MMC_CMD23_ARG_PACKED | (max_packed_reqs + 1);
+ break;
+ case TEST_CMD23_ZERO_PACKED_WRITES:
+ test_pr_info("%s: CMD23 request num = 0", __func__);
+ /* Set the individual packed cmd23 request num to zero */
+ brq->sbc.arg = MMC_CMD23_ARG_PACKED;
+ break;
+ case TEST_CMD23_PACKED_BIT_UNSET:
+ test_pr_info("%s: CMD23 packed bit unset", __func__);
+ /*
+ * Set the individual packed cmd23 packed bit to 0,
+ * although there is a packed write request
+ */
+ brq->sbc.arg &= ~CMD23_PACKED_BIT;
+ break;
+ case TEST_CMD23_REL_WR_BIT_SET:
+ test_pr_info("%s: CMD23 REL WR bit set", __func__);
+ /* Set the individual packed cmd23 reliable write bit */
+ brq->sbc.arg = MMC_CMD23_ARG_PACKED | MMC_CMD23_ARG_REL_WR;
+ break;
+ case TEST_CMD23_BITS_16TO29_SET:
+ test_pr_info("%s: CMD23 bits [16-29] set", __func__);
+ brq->sbc.arg = MMC_CMD23_ARG_PACKED |
+ PACKED_HDR_BITS_16_TO_29_SET;
+ break;
+ case TEST_CMD23_HDR_BLK_NOT_IN_COUNT:
+ test_pr_info("%s: CMD23 hdr not in block count", __func__);
+ brq->sbc.arg = MMC_CMD23_ARG_PACKED |
+ ((rq_data_dir(req) == READ) ? 0 : mqrq->packed->blocks);
+ break;
+ default:
+ test_pr_err("%s: unexpected testcase %d",
+ __func__, mbtd->test_info.testcase);
+ break;
+ }
+}
+
+/*
+ * A callback assigned to the err_check_fn field of the mmc_request by the
+ * MMC/card/block layer.
+ * Called upon request completion by the MMC/core layer.
+ * Here we emulate an error return value from the card.
+ */
+static int test_err_check(struct mmc_card *card, struct mmc_async_req *areq)
+{
+ struct mmc_queue_req *mq_rq = container_of(areq, struct mmc_queue_req,
+ mmc_active);
+ struct request_queue *req_q = test_iosched_get_req_queue();
+ struct mmc_queue *mq;
+ int max_packed_reqs;
+ int ret = 0;
+
+ if (req_q)
+ mq = req_q->queuedata;
+ else {
+ test_pr_err("%s: NULL request_queue", __func__);
+ return 0;
+ }
+
+ if (!mq) {
+ test_pr_err("%s: %s: NULL mq", __func__,
+ mmc_hostname(card->host));
+ return 0;
+ }
+
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+
+ if (!mq_rq) {
+ test_pr_err("%s: %s: NULL mq_rq", __func__,
+ mmc_hostname(card->host));
+ return 0;
+ }
+
+ switch (mbtd->test_info.testcase) {
+ case TEST_RET_ABORT:
+ test_pr_info("%s: return abort", __func__);
+ ret = MMC_BLK_ABORT;
+ break;
+ case TEST_RET_PARTIAL_FOLLOWED_BY_SUCCESS:
+ test_pr_info("%s: return partial followed by success",
+ __func__);
+ /*
+ * Since in this testcase num_requests is always >= 2,
+ * we can be sure that packed_fail_idx is always >= 1
+ */
+ mq_rq->packed->idx_failure = (mbtd->num_requests / 2);
+ test_pr_info("%s: packed_fail_idx = %d"
+ , __func__, mq_rq->packed->idx_failure);
+ mq->err_check_fn = NULL;
+ ret = MMC_BLK_PARTIAL;
+ break;
+ case TEST_RET_PARTIAL_FOLLOWED_BY_ABORT:
+ if (!mbtd->err_check_counter) {
+ test_pr_info("%s: return partial followed by abort",
+ __func__);
+ mbtd->err_check_counter++;
+ /*
+ * Since in this testcase num_requests is always >= 3,
+ * we have that packed_fail_idx is always >= 1
+ */
+ mq_rq->packed->idx_failure = (mbtd->num_requests / 2);
+ test_pr_info("%s: packed_fail_idx = %d"
+ , __func__, mq_rq->packed->idx_failure);
+ ret = MMC_BLK_PARTIAL;
+ break;
+ }
+ mbtd->err_check_counter = 0;
+ mq->err_check_fn = NULL;
+ ret = MMC_BLK_ABORT;
+ break;
+ case TEST_RET_PARTIAL_MULTIPLE_UNTIL_SUCCESS:
+ test_pr_info("%s: return partial multiple until success",
+ __func__);
+ if (++mbtd->err_check_counter >= (mbtd->num_requests)) {
+ mq->err_check_fn = NULL;
+ mbtd->err_check_counter = 0;
+ ret = MMC_BLK_PARTIAL;
+ break;
+ }
+ mq_rq->packed->idx_failure = 1;
+ ret = MMC_BLK_PARTIAL;
+ break;
+ case TEST_RET_PARTIAL_MAX_FAIL_IDX:
+ test_pr_info("%s: return partial max fail_idx", __func__);
+ mq_rq->packed->idx_failure = max_packed_reqs - 1;
+ mq->err_check_fn = NULL;
+ ret = MMC_BLK_PARTIAL;
+ break;
+ case TEST_RET_RETRY:
+ test_pr_info("%s: return retry", __func__);
+ ret = MMC_BLK_RETRY;
+ break;
+ case TEST_RET_CMD_ERR:
+ test_pr_info("%s: return cmd err", __func__);
+ ret = MMC_BLK_CMD_ERR;
+ break;
+ case TEST_RET_DATA_ERR:
+ test_pr_info("%s: return data err", __func__);
+ ret = MMC_BLK_DATA_ERR;
+ break;
+ default:
+ test_pr_err("%s: unexpected testcase %d",
+ __func__, mbtd->test_info.testcase);
+ }
+
+ return ret;
+}
+
+/*
+ * This is a specific implementation for the get_test_case_str_fn function
+ * pointer in the test_info data structure. Given a valid test_data instance,
+ * the function returns a string resembling the test name, based on the testcase
+ */
+static char *get_test_case_str(struct test_data *td)
+{
+ if (!td) {
+ test_pr_err("%s: NULL td", __func__);
+ return NULL;
+ }
+
+ switch (td->test_info.testcase) {
+ case TEST_STOP_DUE_TO_FLUSH:
+ return " stop due to flush";
+ case TEST_STOP_DUE_TO_FLUSH_AFTER_MAX_REQS:
+ return " stop due to flush after max-1 reqs";
+ case TEST_STOP_DUE_TO_READ:
+ return " stop due to read";
+ case TEST_STOP_DUE_TO_READ_AFTER_MAX_REQS:
+ return "Test stop due to read after max-1 reqs";
+ case TEST_STOP_DUE_TO_EMPTY_QUEUE:
+ return "Test stop due to empty queue";
+ case TEST_STOP_DUE_TO_MAX_REQ_NUM:
+ return "Test stop due to max req num";
+ case TEST_STOP_DUE_TO_THRESHOLD:
+ return "Test stop due to exceeding threshold";
+ case TEST_RET_ABORT:
+ return "Test err_check return abort";
+ case TEST_RET_PARTIAL_FOLLOWED_BY_SUCCESS:
+ return "Test err_check return partial followed by success";
+ case TEST_RET_PARTIAL_FOLLOWED_BY_ABORT:
+ return "Test err_check return partial followed by abort";
+ case TEST_RET_PARTIAL_MULTIPLE_UNTIL_SUCCESS:
+ return "Test err_check return partial multiple until success";
+ case TEST_RET_PARTIAL_MAX_FAIL_IDX:
+ return "Test err_check return partial max fail index";
+ case TEST_RET_RETRY:
+ return "Test err_check return retry";
+ case TEST_RET_CMD_ERR:
+ return "Test err_check return cmd error";
+ case TEST_RET_DATA_ERR:
+ return "Test err_check return data error";
+ case TEST_HDR_INVALID_VERSION:
+ return "Test invalid - wrong header version";
+ case TEST_HDR_WRONG_WRITE_CODE:
+ return "Test invalid - wrong write code";
+ case TEST_HDR_INVALID_RW_CODE:
+ return "Test invalid - wrong R/W code";
+ case TEST_HDR_DIFFERENT_ADDRESSES:
+ return "Test invalid - header different addresses";
+ case TEST_HDR_REQ_NUM_SMALLER_THAN_ACTUAL:
+ return "Test invalid - header req num smaller than actual";
+ case TEST_HDR_REQ_NUM_LARGER_THAN_ACTUAL:
+ return "Test invalid - header req num larger than actual";
+ case TEST_HDR_CMD23_PACKED_BIT_SET:
+ return "Test invalid - header cmd23 packed bit set";
+ case TEST_CMD23_MAX_PACKED_WRITES:
+ return "Test invalid - cmd23 max packed writes";
+ case TEST_CMD23_ZERO_PACKED_WRITES:
+ return "Test invalid - cmd23 zero packed writes";
+ case TEST_CMD23_PACKED_BIT_UNSET:
+ return "Test invalid - cmd23 packed bit unset";
+ case TEST_CMD23_REL_WR_BIT_SET:
+ return "Test invalid - cmd23 rel wr bit set";
+ case TEST_CMD23_BITS_16TO29_SET:
+ return "Test invalid - cmd23 bits [16-29] set";
+ case TEST_CMD23_HDR_BLK_NOT_IN_COUNT:
+ return "Test invalid - cmd23 header block not in count";
+ case TEST_PACKING_EXP_N_OVER_TRIGGER:
+ return "\nTest packing control - pack n";
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ:
+ return "\nTest packing control - pack n followed by read";
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N:
+ return "\nTest packing control - pack n followed by flush";
+ case TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ:
+ return "\nTest packing control - pack one followed by read";
+ case TEST_PACKING_EXP_THRESHOLD_OVER_TRIGGER:
+ return "\nTest packing control - pack threshold";
+ case TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS:
+ return "\nTest packing control - no packing";
+ case TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS:
+ return "\nTest packing control - no packing, trigger requests";
+ case TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER:
+ return "\nTest packing control - no pack, trigger-read-trigger";
+ case TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER:
+ return "\nTest packing control- no pack, trigger-flush-trigger";
+ case TEST_PACK_MIX_PACKED_NO_PACKED_PACKED:
+ return "\nTest packing control - mix: pack -> no pack -> pack";
+ case TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED:
+ return "\nTest packing control - mix: no pack->pack->no pack";
+ default:
+ return "Unknown testcase";
+ }
+
+ return NULL;
+}
+
+/*
+ * Compare individual testcase's statistics to the expected statistics:
+ * Compare stop reason and number of packing events
+ */
+static int check_wr_packing_statistics(struct test_data *td)
+{
+ struct mmc_wr_pack_stats *mmc_packed_stats;
+ struct mmc_queue *mq = td->req_q->queuedata;
+ int max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+ int i;
+ struct mmc_card *card = mq->card;
+ struct mmc_wr_pack_stats expected_stats;
+ int *stop_reason;
+ int ret = 0;
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ expected_stats = mbtd->exp_packed_stats;
+
+ mmc_packed_stats = mmc_blk_get_packed_statistics(card);
+ if (!mmc_packed_stats) {
+ test_pr_err("%s: NULL mmc_packed_stats", __func__);
+ return -EINVAL;
+ }
+
+ if (!mmc_packed_stats->packing_events) {
+ test_pr_err("%s: NULL packing_events", __func__);
+ return -EINVAL;
+ }
+
+ spin_lock(&mmc_packed_stats->lock);
+
+ if (!mmc_packed_stats->enabled) {
+ test_pr_err("%s write packing statistics are not enabled",
+ __func__);
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ stop_reason = mmc_packed_stats->pack_stop_reason;
+
+ for (i = 1; i <= max_packed_reqs; ++i) {
+ if (mmc_packed_stats->packing_events[i] !=
+ expected_stats.packing_events[i]) {
+ test_pr_err(
+ "%s: Wrong pack stats in index %d, got %d, expected %d",
+ __func__, i, mmc_packed_stats->packing_events[i],
+ expected_stats.packing_events[i]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[EXCEEDS_SEGMENTS] !=
+ expected_stats.pack_stop_reason[EXCEEDS_SEGMENTS]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason EXCEEDS_SEGMENTS %d, expected %d",
+ __func__, stop_reason[EXCEEDS_SEGMENTS],
+ expected_stats.pack_stop_reason[EXCEEDS_SEGMENTS]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[EXCEEDS_SECTORS] !=
+ expected_stats.pack_stop_reason[EXCEEDS_SECTORS]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason EXCEEDS_SECTORS %d, expected %d",
+ __func__, stop_reason[EXCEEDS_SECTORS],
+ expected_stats.pack_stop_reason[EXCEEDS_SECTORS]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[WRONG_DATA_DIR] !=
+ expected_stats.pack_stop_reason[WRONG_DATA_DIR]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason WRONG_DATA_DIR %d, expected %d",
+ __func__, stop_reason[WRONG_DATA_DIR],
+ expected_stats.pack_stop_reason[WRONG_DATA_DIR]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[FLUSH_OR_DISCARD] !=
+ expected_stats.pack_stop_reason[FLUSH_OR_DISCARD]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason FLUSH_OR_DISCARD %d, expected %d",
+ __func__, stop_reason[FLUSH_OR_DISCARD],
+ expected_stats.pack_stop_reason[FLUSH_OR_DISCARD]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[EMPTY_QUEUE] !=
+ expected_stats.pack_stop_reason[EMPTY_QUEUE]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason EMPTY_QUEUE %d, expected %d",
+ __func__, stop_reason[EMPTY_QUEUE],
+ expected_stats.pack_stop_reason[EMPTY_QUEUE]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+ if (mmc_packed_stats->pack_stop_reason[REL_WRITE] !=
+ expected_stats.pack_stop_reason[REL_WRITE]) {
+ test_pr_err(
+ "%s: Wrong pack stop reason REL_WRITE %d, expected %d",
+ __func__, stop_reason[REL_WRITE],
+ expected_stats.pack_stop_reason[REL_WRITE]);
+ if (td->fs_wr_reqs_during_test)
+ goto cancel_round;
+ ret = -EINVAL;
+ goto exit_err;
+ }
+
+exit_err:
+ spin_unlock(&mmc_packed_stats->lock);
+ if (ret && mmc_packed_stats->enabled)
+ print_mmc_packing_stats(card);
+ return ret;
+cancel_round:
+ spin_unlock(&mmc_packed_stats->lock);
+ test_iosched_set_ignore_round(true);
+ return 0;
+}
+
+/*
+ * Pseudo-randomly choose a seed based on the last seed, and update it in
+ * seed_number. then return seed_number (mod max_val), or min_val.
+ */
+static unsigned int pseudo_random_seed(unsigned int *seed_number,
+ unsigned int min_val,
+ unsigned int max_val)
+{
+ int ret = 0;
+
+ if (!seed_number)
+ return 0;
+
+ *seed_number = ((unsigned int)(((unsigned long)*seed_number *
+ (unsigned long)LARGE_PRIME_1) + LARGE_PRIME_2));
+ ret = (unsigned int)((*seed_number) % max_val);
+
+ return (ret > min_val ? ret : min_val);
+}
+
+/*
+ * Given a pseudo-random seed, find a pseudo-random num_of_bios.
+ * Make sure that num_of_bios is not larger than TEST_MAX_SECTOR_RANGE
+ */
+static void pseudo_rnd_num_of_bios(unsigned int *num_bios_seed,
+ unsigned int *num_of_bios)
+{
+ do {
+ *num_of_bios = pseudo_random_seed(num_bios_seed, 1,
+ TEST_MAX_BIOS_PER_REQ);
+ if (!(*num_of_bios))
+ *num_of_bios = 1;
+ } while ((*num_of_bios) * BIO_U32_SIZE * 4 > TEST_MAX_SECTOR_RANGE);
+}
+
+/* Add a single read request to the given td's request queue */
+static int prepare_request_add_read(struct test_data *td)
+{
+ int ret;
+ int start_sec;
+
+ if (td)
+ start_sec = td->start_sector;
+ else {
+ test_pr_err("%s: NULL td", __func__);
+ return 0;
+ }
+
+ test_pr_info("%s: Adding a read request, first req_id=%d", __func__,
+ td->wr_rd_next_req_id);
+
+ ret = test_iosched_add_wr_rd_test_req(0, READ, start_sec, 2,
+ TEST_PATTERN_5A, NULL);
+ if (ret) {
+ test_pr_err("%s: failed to add a read request", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Add a single flush request to the given td's request queue */
+static int prepare_request_add_flush(struct test_data *td)
+{
+ int ret;
+
+ if (!td) {
+ test_pr_err("%s: NULL td", __func__);
+ return 0;
+ }
+
+ test_pr_info("%s: Adding a flush request, first req_id=%d", __func__,
+ td->unique_next_req_id);
+ ret = test_iosched_add_unique_test_req(0, REQ_UNIQUE_FLUSH,
+ 0, 0, NULL);
+ if (ret) {
+ test_pr_err("%s: failed to add a flush request", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+/*
+ * Add num_requets amount of write requests to the given td's request queue.
+ * If random test mode is chosen we pseudo-randomly choose the number of bios
+ * for each write request, otherwise add between 1 to 5 bio per request.
+ */
+static int prepare_request_add_write_reqs(struct test_data *td,
+ int num_requests, int is_err_expected,
+ int is_random)
+{
+ int i;
+ unsigned int start_sec;
+ int num_bios;
+ int ret = 0;
+ unsigned int *bio_seed = &mbtd->random_test_seed;
+
+ if (td)
+ start_sec = td->start_sector;
+ else {
+ test_pr_err("%s: NULL td", __func__);
+ return ret;
+ }
+
+ test_pr_info("%s: Adding %d write requests, first req_id=%d", __func__,
+ num_requests, td->wr_rd_next_req_id);
+
+ for (i = 1; i <= num_requests; i++) {
+ start_sec = td->start_sector + 4096 * td->num_of_write_bios;
+ if (is_random)
+ pseudo_rnd_num_of_bios(bio_seed, &num_bios);
+ else
+ /*
+ * For the non-random case, give num_bios a value
+ * between 1 and 5, to keep a small number of BIOs
+ */
+ num_bios = (i%5)+1;
+
+ ret = test_iosched_add_wr_rd_test_req(is_err_expected, WRITE,
+ start_sec, num_bios, TEST_PATTERN_5A, NULL);
+
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Prepare the write, read and flush requests for a generic packed commands
+ * testcase
+ */
+static int prepare_packed_requests(struct test_data *td, int is_err_expected,
+ int num_requests, int is_random)
+{
+ int ret = 0;
+ struct mmc_queue *mq;
+ int max_packed_reqs;
+ struct request_queue *req_q;
+
+ if (!td) {
+ pr_err("%s: NULL td", __func__);
+ return -EINVAL;
+ }
+
+ req_q = td->req_q;
+
+ if (!req_q) {
+ pr_err("%s: NULL request queue", __func__);
+ return -EINVAL;
+ }
+
+ mq = req_q->queuedata;
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+
+ if (mbtd->random_test_seed <= 0) {
+ mbtd->random_test_seed =
+ (unsigned int)(get_jiffies_64() & 0xFFFF);
+ test_pr_info("%s: got seed from jiffies %d",
+ __func__, mbtd->random_test_seed);
+ }
+
+ ret = prepare_request_add_write_reqs(td, num_requests, is_err_expected,
+ is_random);
+ if (ret)
+ return ret;
+
+ /* Avoid memory corruption in upcoming stats set */
+ if (td->test_info.testcase == TEST_STOP_DUE_TO_THRESHOLD)
+ num_requests--;
+
+ memset((void *)mbtd->exp_packed_stats.pack_stop_reason, 0,
+ sizeof(mbtd->exp_packed_stats.pack_stop_reason));
+ memset(mbtd->exp_packed_stats.packing_events, 0,
+ (max_packed_reqs + 1) * sizeof(u32));
+ if (num_requests <= max_packed_reqs)
+ mbtd->exp_packed_stats.packing_events[num_requests] = 1;
+
+ switch (td->test_info.testcase) {
+ case TEST_STOP_DUE_TO_FLUSH:
+ case TEST_STOP_DUE_TO_FLUSH_AFTER_MAX_REQS:
+ ret = prepare_request_add_flush(td);
+ if (ret)
+ return ret;
+
+ mbtd->exp_packed_stats.pack_stop_reason[FLUSH_OR_DISCARD] = 1;
+ break;
+ case TEST_STOP_DUE_TO_READ:
+ case TEST_STOP_DUE_TO_READ_AFTER_MAX_REQS:
+ ret = prepare_request_add_read(td);
+ if (ret)
+ return ret;
+
+ mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1;
+ break;
+ case TEST_STOP_DUE_TO_THRESHOLD:
+ mbtd->exp_packed_stats.packing_events[num_requests] = 1;
+ mbtd->exp_packed_stats.packing_events[1] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[THRESHOLD] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+ break;
+ case TEST_STOP_DUE_TO_MAX_REQ_NUM:
+ case TEST_RET_PARTIAL_MAX_FAIL_IDX:
+ mbtd->exp_packed_stats.pack_stop_reason[THRESHOLD] = 1;
+ break;
+ default:
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+ }
+ mbtd->num_requests = num_requests;
+
+ return 0;
+}
+
+/*
+ * Prepare the write, read and flush requests for the packing control
+ * testcases
+ */
+static int prepare_packed_control_tests_requests(struct test_data *td,
+ int is_err_expected, int num_requests, int is_random)
+{
+ int ret = 0;
+ struct mmc_queue *mq;
+ int max_packed_reqs;
+ int temp_num_req = num_requests;
+ struct request_queue *req_q;
+ int test_packed_trigger;
+ int num_packed_reqs;
+
+ if (!td) {
+ test_pr_err("%s: NULL td\n", __func__);
+ return -EINVAL;
+ }
+
+ req_q = td->req_q;
+
+ if (!req_q) {
+ test_pr_err("%s: NULL request queue\n", __func__);
+ return -EINVAL;
+ }
+
+ mq = req_q->queuedata;
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+ test_packed_trigger = mq->num_wr_reqs_to_start_packing;
+ num_packed_reqs = num_requests - test_packed_trigger;
+
+ if (mbtd->random_test_seed == 0) {
+ mbtd->random_test_seed =
+ (unsigned int)(get_jiffies_64() & 0xFFFF);
+ test_pr_info("%s: got seed from jiffies %d",
+ __func__, mbtd->random_test_seed);
+ }
+
+ if (td->test_info.testcase ==
+ TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED) {
+ temp_num_req = num_requests;
+ num_requests = test_packed_trigger - 1;
+ }
+
+ /* Verify that the packing is disabled before starting the test */
+ mq->wr_packing_enabled = false;
+ mq->num_of_potential_packed_wr_reqs = 0;
+
+ if (td->test_info.testcase == TEST_PACK_MIX_PACKED_NO_PACKED_PACKED) {
+ mq->num_of_potential_packed_wr_reqs = test_packed_trigger + 1;
+ mq->wr_packing_enabled = true;
+ num_requests = test_packed_trigger + 2;
+ }
+
+ ret = prepare_request_add_write_reqs(td, num_requests, is_err_expected,
+ is_random);
+ if (ret)
+ goto exit;
+
+ if (td->test_info.testcase == TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED)
+ num_requests = temp_num_req;
+
+ memset((void *)mbtd->exp_packed_stats.pack_stop_reason, 0,
+ sizeof(mbtd->exp_packed_stats.pack_stop_reason));
+ memset(mbtd->exp_packed_stats.packing_events, 0,
+ (max_packed_reqs + 1) * sizeof(u32));
+
+ switch (td->test_info.testcase) {
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ:
+ case TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ:
+ ret = prepare_request_add_read(td);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1;
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ break;
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N:
+ ret = prepare_request_add_flush(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, num_packed_reqs,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[FLUSH_OR_DISCARD] = 1;
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 2;
+ break;
+ case TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER:
+ ret = prepare_request_add_read(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, test_packed_trigger,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ break;
+ case TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER:
+ ret = prepare_request_add_flush(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, test_packed_trigger,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ break;
+ case TEST_PACK_MIX_PACKED_NO_PACKED_PACKED:
+ ret = prepare_request_add_read(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, test_packed_trigger-1,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, num_requests,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.packing_events[num_requests] = 1;
+ mbtd->exp_packed_stats.packing_events[num_requests-1] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+ break;
+ case TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED:
+ ret = prepare_request_add_read(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, num_requests,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_read(td);
+ if (ret)
+ goto exit;
+
+ ret = prepare_request_add_write_reqs(td, test_packed_trigger-1,
+ is_err_expected, is_random);
+ if (ret)
+ goto exit;
+
+ mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1;
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ break;
+ case TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS:
+ case TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS:
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ break;
+ default:
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+ mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1;
+ }
+ mbtd->num_requests = num_requests;
+
+exit:
+ return ret;
+}
+
+/*
+ * Prepare requests for the TEST_RET_PARTIAL_FOLLOWED_BY_ABORT testcase.
+ * In this testcase we have mixed error expectations from different
+ * write requests, hence the special prepare function.
+ */
+static int prepare_partial_followed_by_abort(struct test_data *td,
+ int num_requests)
+{
+ int i, start_address;
+ int is_err_expected = 0;
+ int ret = 0;
+ struct mmc_queue *mq = test_iosched_get_req_queue()->queuedata;
+ int max_packed_reqs;
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+
+ for (i = 1; i <= num_requests; i++) {
+ if (i > (num_requests / 2))
+ is_err_expected = 1;
+
+ start_address = td->start_sector + 4096 * td->num_of_write_bios;
+ ret = test_iosched_add_wr_rd_test_req(is_err_expected, WRITE,
+ start_address, (i % 5) + 1, TEST_PATTERN_5A,
+ NULL);
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ return ret;
+ }
+ }
+
+ memset((void *)&mbtd->exp_packed_stats.pack_stop_reason, 0,
+ sizeof(mbtd->exp_packed_stats.pack_stop_reason));
+ memset(mbtd->exp_packed_stats.packing_events, 0,
+ (max_packed_reqs + 1) * sizeof(u32));
+ mbtd->exp_packed_stats.packing_events[num_requests] = 1;
+ mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1;
+
+ mbtd->num_requests = num_requests;
+
+ return ret;
+}
+
+/*
+ * Get number of write requests for current testcase. If random test mode was
+ * chosen, pseudo-randomly choose the number of requests, otherwise set to
+ * two less than the packing threshold.
+ */
+static int get_num_requests(struct test_data *td)
+{
+ int *seed = &mbtd->random_test_seed;
+ struct request_queue *req_q;
+ struct mmc_queue *mq;
+ int max_num_requests;
+ int num_requests;
+ int min_num_requests = 2;
+ int is_random = mbtd->is_random;
+ int max_for_double;
+ int test_packed_trigger;
+
+ req_q = test_iosched_get_req_queue();
+ if (req_q)
+ mq = req_q->queuedata;
+ else {
+ test_pr_err("%s: NULL request queue", __func__);
+ return 0;
+ }
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_num_requests = mq->card->ext_csd.max_packed_writes;
+ num_requests = max_num_requests - 2;
+ test_packed_trigger = mq->num_wr_reqs_to_start_packing;
+
+ /*
+ * Here max_for_double is intended for packed control testcases
+ * in which we issue many write requests. It's purpose is to prevent
+ * exceeding max number of req_queue requests.
+ */
+ max_for_double = max_num_requests - 10;
+
+ if (td->test_info.testcase ==
+ TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS)
+ /* Don't expect packing, so issue up to trigger-1 reqs */
+ num_requests = test_packed_trigger - 1;
+
+ if (is_random) {
+ if (td->test_info.testcase ==
+ TEST_RET_PARTIAL_FOLLOWED_BY_ABORT)
+ /*
+ * Here we don't want num_requests to be less than 1
+ * as a consequence of division by 2.
+ */
+ min_num_requests = 3;
+
+ if (td->test_info.testcase ==
+ TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS)
+ /* Don't expect packing, so issue up to trigger reqs */
+ max_num_requests = test_packed_trigger;
+
+ num_requests = pseudo_random_seed(seed, min_num_requests,
+ max_num_requests - 1);
+ }
+
+ if (td->test_info.testcase ==
+ TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS)
+ num_requests -= test_packed_trigger;
+
+ if (td->test_info.testcase == TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N)
+ num_requests =
+ num_requests > max_for_double ? max_for_double : num_requests;
+
+ if (mbtd->test_group == TEST_PACKING_CONTROL_GROUP)
+ num_requests += test_packed_trigger;
+
+ if (td->test_info.testcase == TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS)
+ num_requests = test_packed_trigger;
+
+ return num_requests;
+}
+
+/*
+ * An implementation for the prepare_test_fn pointer in the test_info
+ * data structure. According to the testcase we add the right number of requests
+ * and decide if an error is expected or not.
+ */
+static int prepare_test(struct test_data *td)
+{
+ struct mmc_queue *mq = test_iosched_get_req_queue()->queuedata;
+ int max_num_requests;
+ int num_requests = 0;
+ int ret = 0;
+ int is_random = mbtd->is_random;
+ int test_packed_trigger = mq->num_wr_reqs_to_start_packing;
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_num_requests = mq->card->ext_csd.max_packed_writes;
+
+ if (is_random && mbtd->random_test_seed == 0) {
+ mbtd->random_test_seed =
+ (unsigned int)(get_jiffies_64() & 0xFFFF);
+ test_pr_info("%s: got seed from jiffies %d",
+ __func__, mbtd->random_test_seed);
+ }
+
+ num_requests = get_num_requests(td);
+
+ if (mbtd->test_group == TEST_SEND_INVALID_GROUP)
+ mq->packed_test_fn =
+ test_invalid_packed_cmd;
+
+ if (mbtd->test_group == TEST_ERR_CHECK_GROUP)
+ mq->err_check_fn = test_err_check;
+
+ switch (td->test_info.testcase) {
+ case TEST_STOP_DUE_TO_FLUSH:
+ case TEST_STOP_DUE_TO_READ:
+ case TEST_RET_PARTIAL_FOLLOWED_BY_SUCCESS:
+ case TEST_RET_PARTIAL_MULTIPLE_UNTIL_SUCCESS:
+ case TEST_STOP_DUE_TO_EMPTY_QUEUE:
+ case TEST_CMD23_PACKED_BIT_UNSET:
+ ret = prepare_packed_requests(td, 0, num_requests, is_random);
+ break;
+ case TEST_STOP_DUE_TO_FLUSH_AFTER_MAX_REQS:
+ case TEST_STOP_DUE_TO_READ_AFTER_MAX_REQS:
+ ret = prepare_packed_requests(td, 0, max_num_requests - 1,
+ is_random);
+ break;
+ case TEST_RET_PARTIAL_FOLLOWED_BY_ABORT:
+ ret = prepare_partial_followed_by_abort(td, num_requests);
+ break;
+ case TEST_STOP_DUE_TO_MAX_REQ_NUM:
+ case TEST_RET_PARTIAL_MAX_FAIL_IDX:
+ ret = prepare_packed_requests(td, 0, max_num_requests,
+ is_random);
+ break;
+ case TEST_STOP_DUE_TO_THRESHOLD:
+ ret = prepare_packed_requests(td, 0, max_num_requests + 1,
+ is_random);
+ break;
+ case TEST_RET_ABORT:
+ case TEST_RET_RETRY:
+ case TEST_RET_CMD_ERR:
+ case TEST_RET_DATA_ERR:
+ case TEST_HDR_INVALID_VERSION:
+ case TEST_HDR_WRONG_WRITE_CODE:
+ case TEST_HDR_INVALID_RW_CODE:
+ case TEST_HDR_DIFFERENT_ADDRESSES:
+ case TEST_HDR_REQ_NUM_SMALLER_THAN_ACTUAL:
+ case TEST_HDR_REQ_NUM_LARGER_THAN_ACTUAL:
+ case TEST_CMD23_MAX_PACKED_WRITES:
+ case TEST_CMD23_ZERO_PACKED_WRITES:
+ case TEST_CMD23_REL_WR_BIT_SET:
+ case TEST_CMD23_BITS_16TO29_SET:
+ case TEST_CMD23_HDR_BLK_NOT_IN_COUNT:
+ case TEST_HDR_CMD23_PACKED_BIT_SET:
+ ret = prepare_packed_requests(td, 1, num_requests, is_random);
+ break;
+ case TEST_PACKING_EXP_N_OVER_TRIGGER:
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ:
+ case TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS:
+ case TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS:
+ case TEST_PACK_MIX_PACKED_NO_PACKED_PACKED:
+ case TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED:
+ ret = prepare_packed_control_tests_requests(td, 0, num_requests,
+ is_random);
+ break;
+ case TEST_PACKING_EXP_THRESHOLD_OVER_TRIGGER:
+ ret = prepare_packed_control_tests_requests(td, 0,
+ max_num_requests, is_random);
+ break;
+ case TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ:
+ ret = prepare_packed_control_tests_requests(td, 0,
+ test_packed_trigger + 1,
+ is_random);
+ break;
+ case TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N:
+ ret = prepare_packed_control_tests_requests(td, 0, num_requests,
+ is_random);
+ break;
+ case TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER:
+ case TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER:
+ ret = prepare_packed_control_tests_requests(td, 0,
+ test_packed_trigger, is_random);
+ break;
+ default:
+ test_pr_info("%s: Invalid test case...", __func__);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int run_packed_test(struct test_data *td)
+{
+ struct mmc_queue *mq;
+ struct request_queue *req_q;
+
+ if (!td) {
+ pr_err("%s: NULL td", __func__);
+ return -EINVAL;
+ }
+
+ req_q = td->req_q;
+
+ if (!req_q) {
+ pr_err("%s: NULL request queue", __func__);
+ return -EINVAL;
+ }
+
+ mq = req_q->queuedata;
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+ mmc_blk_init_packed_statistics(mq->card);
+
+ if (td->test_info.testcase != TEST_PACK_MIX_PACKED_NO_PACKED_PACKED) {
+ /*
+ * Verify that the packing is disabled before starting the
+ * test
+ */
+ mq->wr_packing_enabled = false;
+ mq->num_of_potential_packed_wr_reqs = 0;
+ }
+
+ __blk_run_queue(td->req_q);
+
+ return 0;
+}
+
+/*
+ * An implementation for the post_test_fn in the test_info data structure.
+ * In our case we just reset the function pointers in the mmc_queue in order for
+ * the FS to be able to dispatch it's requests correctly after the test is
+ * finished.
+ */
+static int post_test(struct test_data *td)
+{
+ struct mmc_queue *mq;
+
+ if (!td)
+ return -EINVAL;
+
+ mq = td->req_q->queuedata;
+
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ mq->packed_test_fn = NULL;
+ mq->err_check_fn = NULL;
+
+ return 0;
+}
+
+/*
+ * This function checks, based on the current test's test_group, that the
+ * packed commands capability and control are set right. In addition, we check
+ * if the card supports the packed command feature.
+ */
+static int validate_packed_commands_settings(void)
+{
+ struct request_queue *req_q;
+ struct mmc_queue *mq;
+ int max_num_requests;
+ struct mmc_host *host;
+
+ req_q = test_iosched_get_req_queue();
+ if (!req_q) {
+ test_pr_err("%s: test_iosched_get_req_queue failed", __func__);
+ test_iosched_set_test_result(TEST_FAILED);
+ return -EINVAL;
+ }
+
+ mq = req_q->queuedata;
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+
+ max_num_requests = mq->card->ext_csd.max_packed_writes;
+ host = mq->card->host;
+
+ if (!(host->caps2 && MMC_CAP2_PACKED_WR)) {
+ test_pr_err("%s: Packed Write capability disabled, exit test",
+ __func__);
+ test_iosched_set_test_result(TEST_NOT_SUPPORTED);
+ return -EINVAL;
+ }
+
+ if (max_num_requests == 0) {
+ test_pr_err(
+ "%s: no write packing support, ext_csd.max_packed_writes=%d",
+ __func__, mq->card->ext_csd.max_packed_writes);
+ test_iosched_set_test_result(TEST_NOT_SUPPORTED);
+ return -EINVAL;
+ }
+
+ test_pr_info("%s: max number of packed requests supported is %d ",
+ __func__, max_num_requests);
+
+ switch (mbtd->test_group) {
+ case TEST_SEND_WRITE_PACKING_GROUP:
+ case TEST_ERR_CHECK_GROUP:
+ case TEST_SEND_INVALID_GROUP:
+ /* disable the packing control */
+ host->caps2 &= ~MMC_CAP2_PACKED_WR_CONTROL;
+ break;
+ case TEST_PACKING_CONTROL_GROUP:
+ host->caps2 |= MMC_CAP2_PACKED_WR_CONTROL;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static bool message_repeat;
+static int test_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ message_repeat = 1;
+ return 0;
+}
+
+/* send_packing TEST */
+static ssize_t send_write_packing_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ int j = 0;
+
+ test_pr_info("%s: -- send_write_packing TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+
+ mbtd->test_group = TEST_SEND_WRITE_PACKING_GROUP;
+
+ if (validate_packed_commands_settings())
+ return count;
+
+ if (mbtd->random_test_seed > 0)
+ test_pr_info("%s: Test seed: %d", __func__,
+ mbtd->random_test_seed);
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.run_test_fn = run_packed_test;
+ mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+ mbtd->test_info.post_test_fn = post_test;
+
+ for (i = 0; i < number; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ for (j = SEND_WRITE_PACKING_MIN_TESTCASE;
+ j <= SEND_WRITE_PACKING_MAX_TESTCASE; j++) {
+
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = NON_RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+ }
+
+ test_pr_info("%s: Completed all the test cases.", __func__);
+
+ return count;
+}
+
+static ssize_t send_write_packing_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nsend_write_packing_test\n"
+ "=========\n"
+ "Description:\n"
+ "This test checks the following scenarios\n"
+ "- Pack due to FLUSH message\n"
+ "- Pack due to FLUSH after threshold writes\n"
+ "- Pack due to READ message\n"
+ "- Pack due to READ after threshold writes\n"
+ "- Pack due to empty queue\n"
+ "- Pack due to threshold writes\n"
+ "- Pack due to one over threshold writes\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else {
+ return 0;
+ }
+}
+
+const struct file_operations send_write_packing_test_ops = {
+ .open = test_open,
+ .write = send_write_packing_test_write,
+ .read = send_write_packing_test_read,
+};
+
+/* err_check TEST */
+static ssize_t err_check_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ int j = 0;
+
+ test_pr_info("%s: -- err_check TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ mbtd->test_group = TEST_ERR_CHECK_GROUP;
+
+ if (validate_packed_commands_settings())
+ return count;
+
+ if (mbtd->random_test_seed > 0)
+ test_pr_info("%s: Test seed: %d", __func__,
+ mbtd->random_test_seed);
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.run_test_fn = run_packed_test;
+ mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+ mbtd->test_info.post_test_fn = post_test;
+
+ for (i = 0; i < number; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ for (j = ERR_CHECK_MIN_TESTCASE;
+ j <= ERR_CHECK_MAX_TESTCASE ; j++) {
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = NON_RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+ }
+
+ test_pr_info("%s: Completed all the test cases.", __func__);
+
+ return count;
+}
+
+static ssize_t err_check_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nerr_check_TEST\n"
+ "=========\n"
+ "Description:\n"
+ "This test checks the following scenarios\n"
+ "- Return ABORT\n"
+ "- Return PARTIAL followed by success\n"
+ "- Return PARTIAL followed by abort\n"
+ "- Return PARTIAL multiple times until success\n"
+ "- Return PARTIAL with fail index = threshold\n"
+ "- Return RETRY\n"
+ "- Return CMD_ERR\n"
+ "- Return DATA_ERR\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else {
+ return 0;
+ }
+}
+
+const struct file_operations err_check_test_ops = {
+ .open = test_open,
+ .write = err_check_test_write,
+ .read = err_check_test_read,
+};
+
+/* send_invalid_packed TEST */
+static ssize_t send_invalid_packed_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ int j = 0;
+ int num_of_failures = 0;
+
+ test_pr_info("%s: -- send_invalid_packed TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ mbtd->test_group = TEST_SEND_INVALID_GROUP;
+
+ if (validate_packed_commands_settings())
+ return count;
+
+ if (mbtd->random_test_seed > 0)
+ test_pr_info("%s: Test seed: %d", __func__,
+ mbtd->random_test_seed);
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.run_test_fn = run_packed_test;
+ mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+ mbtd->test_info.post_test_fn = post_test;
+
+ for (i = 0; i < number; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ for (j = INVALID_CMD_MIN_TESTCASE;
+ j <= INVALID_CMD_MAX_TESTCASE ; j++) {
+
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ num_of_failures++;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = NON_RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ num_of_failures++;
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+ }
+
+ test_pr_info("%s: Completed all the test cases.", __func__);
+
+ if (num_of_failures > 0) {
+ test_iosched_set_test_result(TEST_FAILED);
+ test_pr_err(
+ "There were %d failures during the test, TEST FAILED",
+ num_of_failures);
+ }
+ return count;
+}
+
+static ssize_t send_invalid_packed_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nsend_invalid_packed_TEST\n"
+ "=========\n"
+ "Description:\n"
+ "This test checks the following scenarios\n"
+ "- Send an invalid header version\n"
+ "- Send the wrong write code\n"
+ "- Send an invalid R/W code\n"
+ "- Send wrong start address in header\n"
+ "- Send header with block_count smaller than actual\n"
+ "- Send header with block_count larger than actual\n"
+ "- Send header CMD23 packed bit set\n"
+ "- Send CMD23 with block count over threshold\n"
+ "- Send CMD23 with block_count equals zero\n"
+ "- Send CMD23 packed bit unset\n"
+ "- Send CMD23 reliable write bit set\n"
+ "- Send CMD23 bits [16-29] set\n"
+ "- Send CMD23 header block not in block_count\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else {
+ return 0;
+ }
+}
+
+const struct file_operations send_invalid_packed_test_ops = {
+ .open = test_open,
+ .write = send_invalid_packed_test_write,
+ .read = send_invalid_packed_test_read,
+};
+
+/* packing_control TEST */
+static ssize_t write_packing_control_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ int j = 0;
+ struct mmc_queue *mq = test_iosched_get_req_queue()->queuedata;
+ int max_num_requests = mq->card->ext_csd.max_packed_writes;
+ int test_successful = 1;
+
+ test_pr_info("%s: -- write_packing_control TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ test_pr_info("%s: max_num_requests = %d ", __func__,
+ max_num_requests);
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+ mbtd->test_group = TEST_PACKING_CONTROL_GROUP;
+
+ if (validate_packed_commands_settings())
+ return count;
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.run_test_fn = run_packed_test;
+ mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+
+ for (i = 0; i < number; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ for (j = PACKING_CONTROL_MIN_TESTCASE;
+ j <= PACKING_CONTROL_MAX_TESTCASE; j++) {
+
+ test_successful = 1;
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret) {
+ test_successful = 0;
+ break;
+ }
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+
+ mbtd->test_info.testcase = j;
+ mbtd->is_random = NON_RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret) {
+ test_successful = 0;
+ break;
+ }
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+
+ if (!test_successful)
+ break;
+ }
+
+ test_pr_info("%s: Completed all the test cases.", __func__);
+
+ return count;
+}
+
+static ssize_t write_packing_control_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nwrite_packing_control_test\n"
+ "=========\n"
+ "Description:\n"
+ "This test checks the following scenarios\n"
+ "- Packing expected - one over trigger\n"
+ "- Packing expected - N over trigger\n"
+ "- Packing expected - N over trigger followed by read\n"
+ "- Packing expected - N over trigger followed by flush\n"
+ "- Packing expected - threshold over trigger FB by flush\n"
+ "- Packing not expected - less than trigger\n"
+ "- Packing not expected - trigger requests\n"
+ "- Packing not expected - trigger, read, trigger\n"
+ "- Mixed state - packing -> no packing -> packing\n"
+ "- Mixed state - no packing -> packing -> no packing\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else {
+ return 0;
+ }
+}
+
+const struct file_operations write_packing_control_test_ops = {
+ .open = test_open,
+ .write = write_packing_control_test_write,
+ .read = write_packing_control_test_read,
+};
+
+static void mmc_block_test_debugfs_cleanup(void)
+{
+ debugfs_remove(mbtd->debug.random_test_seed);
+ debugfs_remove(mbtd->debug.send_write_packing_test);
+ debugfs_remove(mbtd->debug.err_check_test);
+ debugfs_remove(mbtd->debug.send_invalid_packed_test);
+ debugfs_remove(mbtd->debug.packing_control_test);
+}
+
+static int mmc_block_test_debugfs_init(void)
+{
+ struct dentry *utils_root, *tests_root;
+
+ utils_root = test_iosched_get_debugfs_utils_root();
+ tests_root = test_iosched_get_debugfs_tests_root();
+
+ if (!utils_root || !tests_root)
+ return -EINVAL;
+
+ mbtd->debug.random_test_seed = debugfs_create_u32(
+ "random_test_seed",
+ S_IRUGO | S_IWUGO,
+ utils_root,
+ &mbtd->random_test_seed);
+
+ if (!mbtd->debug.random_test_seed)
+ goto err_nomem;
+
+ mbtd->debug.send_write_packing_test =
+ debugfs_create_file("send_write_packing_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &send_write_packing_test_ops);
+
+ if (!mbtd->debug.send_write_packing_test)
+ goto err_nomem;
+
+ mbtd->debug.err_check_test =
+ debugfs_create_file("err_check_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &err_check_test_ops);
+
+ if (!mbtd->debug.err_check_test)
+ goto err_nomem;
+
+ mbtd->debug.send_invalid_packed_test =
+ debugfs_create_file("send_invalid_packed_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &send_invalid_packed_test_ops);
+
+ if (!mbtd->debug.send_invalid_packed_test)
+ goto err_nomem;
+
+ mbtd->debug.packing_control_test = debugfs_create_file(
+ "packing_control_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &write_packing_control_test_ops);
+
+ if (!mbtd->debug.packing_control_test)
+ goto err_nomem;
+
+ return 0;
+
+err_nomem:
+ mmc_block_test_debugfs_cleanup();
+ return -ENOMEM;
+}
+
+static void mmc_block_test_probe(void)
+{
+ struct request_queue *q = test_iosched_get_req_queue();
+ struct mmc_queue *mq;
+ int max_packed_reqs;
+
+ if (!q) {
+ test_pr_err("%s: NULL request queue", __func__);
+ return;
+ }
+
+ mq = q->queuedata;
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return;
+ }
+
+ max_packed_reqs = mq->card->ext_csd.max_packed_writes;
+ mbtd->exp_packed_stats.packing_events =
+ kzalloc((max_packed_reqs + 1) *
+ sizeof(*mbtd->exp_packed_stats.packing_events),
+ GFP_KERNEL);
+
+ mmc_block_test_debugfs_init();
+}
+
+static void mmc_block_test_remove(void)
+{
+ mmc_block_test_debugfs_cleanup();
+}
+
+static int __init mmc_block_test_init(void)
+{
+ mbtd = kzalloc(sizeof(struct mmc_block_test_data), GFP_KERNEL);
+ if (!mbtd) {
+ test_pr_err("%s: failed to allocate mmc_block_test_data",
+ __func__);
+ return -ENODEV;
+ }
+
+ mbtd->bdt.init_fn = mmc_block_test_probe;
+ mbtd->bdt.exit_fn = mmc_block_test_remove;
+ INIT_LIST_HEAD(&mbtd->bdt.list);
+ test_iosched_register(&mbtd->bdt);
+
+ return 0;
+}
+
+static void __exit mmc_block_test_exit(void)
+{
+ test_iosched_unregister(&mbtd->bdt);
+ kfree(mbtd);
+}
+
+module_init(mmc_block_test_init);
+module_exit(mmc_block_test_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MMC block test");
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index df382be..b9c8824 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -3115,7 +3115,8 @@ static ssize_t mtf_test_write(struct file *file, const char __user *buf,
}
#ifdef CONFIG_HIGHMEM
- __free_pages(test->highmem, BUFFER_ORDER);
+ if (test->highmem)
+ __free_pages(test->highmem, BUFFER_ORDER);
#endif
kfree(test->buffer);
kfree(test);
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 1810f76..ccfd225 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -16,6 +16,8 @@
#include <linux/kthread.h>
#include <linux/scatterlist.h>
#include <linux/dma-mapping.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
@@ -27,6 +29,13 @@
#define MMC_QUEUE_BOUNCESZ 65536
/*
+ * Based on benchmark tests the default num of requests to trigger the write
+ * packing was determined, to keep the read latency as low as possible and
+ * manage to keep the high write throughput.
+ */
+#define DEFAULT_NUM_REQS_TO_START_PACK 17
+
+/*
* Prepare a MMC request. This just filters out odd stuff.
*/
static int mmc_prep_request(struct request_queue *q, struct request *req)
@@ -50,10 +59,98 @@ static int mmc_prep_request(struct request_queue *q, struct request *req)
return BLKPREP_OK;
}
+static struct request *mmc_peek_request(struct mmc_queue *mq)
+{
+ struct request_queue *q = mq->queue;
+ mq->cmdq_req_peeked = NULL;
+
+ spin_lock_irq(q->queue_lock);
+ if (!blk_queue_stopped(q))
+ mq->cmdq_req_peeked = blk_peek_request(q);
+ spin_unlock_irq(q->queue_lock);
+
+ return mq->cmdq_req_peeked;
+}
+
+static bool mmc_check_blk_queue_start_tag(struct request_queue *q,
+ struct request *req)
+{
+ int ret;
+
+ spin_lock_irq(q->queue_lock);
+ ret = blk_queue_start_tag(q, req);
+ spin_unlock_irq(q->queue_lock);
+
+ return !!ret;
+}
+
+static inline void mmc_cmdq_ready_wait(struct mmc_host *host,
+ struct mmc_queue *mq)
+{
+ struct mmc_cmdq_context_info *ctx = &host->cmdq_ctx;
+ struct request_queue *q = mq->queue;
+
+ /*
+ * Wait until all of the following conditions are true:
+ * 1. There is a request pending in the block layer queue
+ * to be processed.
+ * 2. If the peeked request is flush/discard then there shouldn't
+ * be any other direct command active.
+ * 3. cmdq state should be unhalted.
+ * 4. cmdq state shouldn't be in error state.
+ * 5. free tag available to process the new request.
+ */
+ wait_event(ctx->wait, kthread_should_stop()
+ || (mmc_peek_request(mq) &&
+ !(((req_op(mq->cmdq_req_peeked) == REQ_OP_FLUSH) ||
+ (req_op(mq->cmdq_req_peeked) == REQ_OP_DISCARD))
+ && test_bit(CMDQ_STATE_DCMD_ACTIVE, &ctx->curr_state))
+ && !(!host->card->part_curr && !mmc_card_suspended(host->card)
+ && mmc_host_halt(host))
+ && !(!host->card->part_curr && mmc_host_cq_disable(host) &&
+ !mmc_card_suspended(host->card))
+ && !test_bit(CMDQ_STATE_ERR, &ctx->curr_state)
+ && !mmc_check_blk_queue_start_tag(q, mq->cmdq_req_peeked)));
+}
+
+static int mmc_cmdq_thread(void *d)
+{
+ struct mmc_queue *mq = d;
+ struct mmc_card *card = mq->card;
+
+ struct mmc_host *host = card->host;
+
+ current->flags |= PF_MEMALLOC;
+ if (card->host->wakeup_on_idle)
+ set_wake_up_idle(true);
+
+ while (1) {
+ int ret = 0;
+
+ mmc_cmdq_ready_wait(host, mq);
+ if (kthread_should_stop())
+ break;
+
+ ret = mq->cmdq_issue_fn(mq, mq->cmdq_req_peeked);
+ /*
+ * Don't requeue if issue_fn fails, just bug on.
+ * We don't expect failure here and there is no recovery other
+ * than fixing the actual issue if there is any.
+ * Also we end the request if there is a partition switch error,
+ * so we should not requeue the request here.
+ */
+ if (ret)
+ BUG_ON(1);
+ } /* loop */
+
+ return 0;
+}
+
static int mmc_queue_thread(void *d)
{
struct mmc_queue *mq = d;
struct request_queue *q = mq->queue;
+ struct mmc_card *card = mq->card;
struct sched_param scheduler_params = {0};
scheduler_params.sched_priority = 1;
@@ -61,6 +158,8 @@ static int mmc_queue_thread(void *d)
sched_setscheduler(current, SCHED_FIFO, &scheduler_params);
current->flags |= PF_MEMALLOC;
+ if (card->host->wakeup_on_idle)
+ set_wake_up_idle(true);
down(&mq->thread_sem);
do {
@@ -78,8 +177,8 @@ static int mmc_queue_thread(void *d)
set_current_state(TASK_RUNNING);
mmc_blk_issue_rq(mq, req);
cond_resched();
- if (mq->flags & MMC_QUEUE_NEW_REQUEST) {
- mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
+ if (test_bit(MMC_QUEUE_NEW_REQUEST, &mq->flags)) {
+ clear_bit(MMC_QUEUE_NEW_REQUEST, &mq->flags);
continue; /* fetch again */
}
@@ -111,6 +210,13 @@ static int mmc_queue_thread(void *d)
return 0;
}
+static void mmc_cmdq_dispatch_req(struct request_queue *q)
+{
+ struct mmc_queue *mq = q->queuedata;
+
+ wake_up(&mq->card->host->cmdq_ctx.wait);
+}
+
/*
* Generic MMC request handler. This is called for any queue on a
* particular host. When the host is not busy, we look for a request
@@ -186,6 +292,32 @@ static void mmc_queue_setup_discard(struct request_queue *q,
}
/**
+ * mmc_blk_cmdq_setup_queue
+ * @mq: mmc queue
+ * @card: card to attach to this queue
+ *
+ * Setup queue for CMDQ supporting MMC card
+ */
+void mmc_cmdq_setup_queue(struct mmc_queue *mq, struct mmc_card *card)
+{
+ u64 limit = BLK_BOUNCE_HIGH;
+ struct mmc_host *host = card->host;
+
+ if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask)
+ limit = *mmc_dev(host)->dma_mask;
+
+ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
+ if (mmc_can_erase(card))
+ mmc_queue_setup_discard(mq->queue, card);
+
+ blk_queue_bounce_limit(mq->queue, limit);
+ blk_queue_max_hw_sectors(mq->queue, min(host->max_blk_count,
+ host->max_req_size / 512));
+ blk_queue_max_segment_size(mq->queue, host->max_seg_size);
+ blk_queue_max_segments(mq->queue, host->max_segs);
+}
+
+/**
* mmc_init_queue - initialise a queue structure.
* @mq: mmc queue
* @card: mmc card to attach this queue
@@ -195,7 +327,7 @@ static void mmc_queue_setup_discard(struct request_queue *q,
* Initialise a MMC card request queue.
*/
int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
- spinlock_t *lock, const char *subname)
+ spinlock_t *lock, const char *subname, int area_type)
{
struct mmc_host *host = card->host;
u64 limit = BLK_BOUNCE_HIGH;
@@ -207,6 +339,37 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
limit = (u64)dma_max_pfn(mmc_dev(host)) << PAGE_SHIFT;
mq->card = card;
+ if (card->ext_csd.cmdq_support &&
+ (area_type == MMC_BLK_DATA_AREA_MAIN)) {
+ mq->queue = blk_init_queue(mmc_cmdq_dispatch_req, lock);
+ if (!mq->queue)
+ return -ENOMEM;
+ mmc_cmdq_setup_queue(mq, card);
+ ret = mmc_cmdq_init(mq, card);
+ if (ret) {
+ pr_err("%s: %d: cmdq: unable to set-up\n",
+ mmc_hostname(card->host), ret);
+ blk_cleanup_queue(mq->queue);
+ } else {
+ sema_init(&mq->thread_sem, 1);
+ /* hook for pm qos cmdq init */
+ if (card->host->cmdq_ops->init)
+ card->host->cmdq_ops->init(card->host);
+ mq->queue->queuedata = mq;
+ mq->thread = kthread_run(mmc_cmdq_thread, mq,
+ "mmc-cmdqd/%d%s",
+ host->index,
+ subname ? subname : "");
+ if (IS_ERR(mq->thread)) {
+ pr_err("%s: %d: cmdq: failed to start mmc-cmdqd thread\n",
+ mmc_hostname(card->host), ret);
+ ret = PTR_ERR(mq->thread);
+ }
+
+ return ret;
+ }
+ }
+
mq->queue = blk_init_queue(mmc_request_fn, lock);
if (!mq->queue)
return -ENOMEM;
@@ -214,6 +377,9 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
mq->mqrq_cur = mqrq_cur;
mq->mqrq_prev = mqrq_prev;
mq->queue->queuedata = mq;
+ mq->num_wr_reqs_to_start_packing =
+ min_t(int, (int)card->ext_csd.max_packed_writes,
+ DEFAULT_NUM_REQS_TO_START_PACK);
blk_queue_prep_rq(mq->queue, mmc_prep_request);
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
@@ -279,24 +445,49 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
#endif
if (!mqrq_cur->bounce_buf && !mqrq_prev->bounce_buf) {
+ unsigned int max_segs = host->max_segs;
+
blk_queue_bounce_limit(mq->queue, limit);
blk_queue_max_hw_sectors(mq->queue,
min(host->max_blk_count, host->max_req_size / 512));
- blk_queue_max_segments(mq->queue, host->max_segs);
blk_queue_max_segment_size(mq->queue, host->max_seg_size);
+retry:
+ blk_queue_max_segments(mq->queue, host->max_segs);
mqrq_cur->sg = mmc_alloc_sg(host->max_segs, &ret);
- if (ret)
+ if (ret == -ENOMEM)
+ goto cur_sg_alloc_failed;
+ else if (ret)
goto cleanup_queue;
-
mqrq_prev->sg = mmc_alloc_sg(host->max_segs, &ret);
- if (ret)
+ if (ret == -ENOMEM)
+ goto prev_sg_alloc_failed;
+ else if (ret)
goto cleanup_queue;
+
+ goto success;
+
+prev_sg_alloc_failed:
+ kfree(mqrq_cur->sg);
+ mqrq_cur->sg = NULL;
+cur_sg_alloc_failed:
+ host->max_segs /= 2;
+ if (host->max_segs) {
+ goto retry;
+ } else {
+ host->max_segs = max_segs;
+ goto cleanup_queue;
+ }
}
+success:
sema_init(&mq->thread_sem, 1);
+ /* hook for pm qos legacy init */
+ if (card->host->ops->init)
+ card->host->ops->init(card->host);
+
mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s",
host->index, subname ? subname : "");
@@ -411,28 +602,195 @@ void mmc_packed_clean(struct mmc_queue *mq)
mqrq_prev->packed = NULL;
}
+static void mmc_cmdq_softirq_done(struct request *rq)
+{
+ struct mmc_queue *mq = rq->q->queuedata;
+
+ mq->cmdq_complete_fn(rq);
+}
+
+static void mmc_cmdq_error_work(struct work_struct *work)
+{
+ struct mmc_queue *mq = container_of(work, struct mmc_queue,
+ cmdq_err_work);
+
+ mq->cmdq_error_fn(mq);
+}
+
+enum blk_eh_timer_return mmc_cmdq_rq_timed_out(struct request *req)
+{
+ struct mmc_queue *mq = req->q->queuedata;
+
+ pr_err("%s: request with tag: %d flags: 0x%llx timed out\n",
+ mmc_hostname(mq->card->host), req->tag, req->cmd_flags);
+
+ return mq->cmdq_req_timed_out(req);
+}
+
+int mmc_cmdq_init(struct mmc_queue *mq, struct mmc_card *card)
+{
+ int i, ret = 0;
+ /* one slot is reserved for dcmd requests */
+ int q_depth = card->ext_csd.cmdq_depth - 1;
+
+ card->cmdq_init = false;
+ if (!(card->host->caps2 & MMC_CAP2_CMD_QUEUE)) {
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ init_waitqueue_head(&card->host->cmdq_ctx.queue_empty_wq);
+ init_waitqueue_head(&card->host->cmdq_ctx.wait);
+
+ mq->mqrq_cmdq = kzalloc(
+ sizeof(struct mmc_queue_req) * q_depth, GFP_KERNEL);
+ if (!mq->mqrq_cmdq) {
+ pr_warn("%s: unable to allocate mqrq's for q_depth %d\n",
+ mmc_card_name(card), q_depth);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* sg is allocated for data request slots only */
+ for (i = 0; i < q_depth; i++) {
+ mq->mqrq_cmdq[i].sg = mmc_alloc_sg(card->host->max_segs, &ret);
+ if (ret) {
+ pr_warn("%s: unable to allocate cmdq sg of size %d\n",
+ mmc_card_name(card),
+ card->host->max_segs);
+ goto free_mqrq_sg;
+ }
+ }
+
+ ret = blk_queue_init_tags(mq->queue, q_depth, NULL, BLK_TAG_ALLOC_FIFO);
+ if (ret) {
+ pr_warn("%s: unable to allocate cmdq tags %d\n",
+ mmc_card_name(card), q_depth);
+ goto free_mqrq_sg;
+ }
+
+ blk_queue_softirq_done(mq->queue, mmc_cmdq_softirq_done);
+ INIT_WORK(&mq->cmdq_err_work, mmc_cmdq_error_work);
+ init_completion(&mq->cmdq_shutdown_complete);
+ init_completion(&mq->cmdq_pending_req_done);
+
+ blk_queue_rq_timed_out(mq->queue, mmc_cmdq_rq_timed_out);
+ blk_queue_rq_timeout(mq->queue, 120 * HZ);
+ card->cmdq_init = true;
+
+ goto out;
+
+free_mqrq_sg:
+ for (i = 0; i < q_depth; i++)
+ kfree(mq->mqrq_cmdq[i].sg);
+ kfree(mq->mqrq_cmdq);
+ mq->mqrq_cmdq = NULL;
+out:
+ return ret;
+}
+
+void mmc_cmdq_clean(struct mmc_queue *mq, struct mmc_card *card)
+{
+ int i;
+ int q_depth = card->ext_csd.cmdq_depth - 1;
+
+ blk_free_tags(mq->queue->queue_tags);
+ mq->queue->queue_tags = NULL;
+ blk_queue_free_tags(mq->queue);
+
+ for (i = 0; i < q_depth; i++)
+ kfree(mq->mqrq_cmdq[i].sg);
+ kfree(mq->mqrq_cmdq);
+ mq->mqrq_cmdq = NULL;
+}
+
/**
* mmc_queue_suspend - suspend a MMC request queue
* @mq: MMC queue to suspend
+ * @wait: Wait till MMC request queue is empty
*
* Stop the block request queue, and wait for our thread to
* complete any outstanding requests. This ensures that we
* won't suspend while a request is being processed.
*/
-void mmc_queue_suspend(struct mmc_queue *mq)
+int mmc_queue_suspend(struct mmc_queue *mq, int wait)
{
struct request_queue *q = mq->queue;
unsigned long flags;
+ int rc = 0;
+ struct mmc_card *card = mq->card;
+ struct request *req;
- if (!(mq->flags & MMC_QUEUE_SUSPENDED)) {
- mq->flags |= MMC_QUEUE_SUSPENDED;
+ if (card->cmdq_init && blk_queue_tagged(q)) {
+ struct mmc_host *host = card->host;
- spin_lock_irqsave(q->queue_lock, flags);
- blk_stop_queue(q);
- spin_unlock_irqrestore(q->queue_lock, flags);
+ if (test_and_set_bit(MMC_QUEUE_SUSPENDED, &mq->flags))
+ goto out;
- down(&mq->thread_sem);
+ if (wait) {
+
+ /*
+ * After blk_stop_queue is called, wait for all
+ * active_reqs to complete.
+ * Then wait for cmdq thread to exit before calling
+ * cmdq shutdown to avoid race between issuing
+ * requests and shutdown of cmdq.
+ */
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_stop_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ if (host->cmdq_ctx.active_reqs)
+ wait_for_completion(
+ &mq->cmdq_shutdown_complete);
+ kthread_stop(mq->thread);
+ mq->cmdq_shutdown(mq);
+ } else {
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_stop_queue(q);
+ wake_up(&host->cmdq_ctx.wait);
+ req = blk_peek_request(q);
+ if (req || mq->cmdq_req_peeked ||
+ host->cmdq_ctx.active_reqs) {
+ clear_bit(MMC_QUEUE_SUSPENDED, &mq->flags);
+ blk_start_queue(q);
+ rc = -EBUSY;
+ }
+ spin_unlock_irqrestore(q->queue_lock, flags);
+ }
+
+ goto out;
}
+
+ if (!(test_and_set_bit(MMC_QUEUE_SUSPENDED, &mq->flags))) {
+ if (!wait) {
+ /* suspend/stop the queue in case of suspend */
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_stop_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+ } else {
+ /* shutdown the queue in case of shutdown/reboot */
+ blk_cleanup_queue(q);
+ }
+
+ rc = down_trylock(&mq->thread_sem);
+ if (rc && !wait) {
+ /*
+ * Failed to take the lock so better to abort the
+ * suspend because mmcqd thread is processing requests.
+ */
+ clear_bit(MMC_QUEUE_SUSPENDED, &mq->flags);
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_start_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+ rc = -EBUSY;
+ } else if (rc && wait) {
+ down(&mq->thread_sem);
+ rc = 0;
+ }
+ }
+out:
+ return rc;
}
/**
@@ -442,12 +800,13 @@ void mmc_queue_suspend(struct mmc_queue *mq)
void mmc_queue_resume(struct mmc_queue *mq)
{
struct request_queue *q = mq->queue;
+ struct mmc_card *card = mq->card;
unsigned long flags;
- if (mq->flags & MMC_QUEUE_SUSPENDED) {
- mq->flags &= ~MMC_QUEUE_SUSPENDED;
+ if (test_and_clear_bit(MMC_QUEUE_SUSPENDED, &mq->flags)) {
- up(&mq->thread_sem);
+ if (!(card->cmdq_init && blk_queue_tagged(q)))
+ up(&mq->thread_sem);
spin_lock_irqsave(q->queue_lock, flags);
blk_start_queue(q);
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index fe58d31..bf7a95b 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -48,20 +48,40 @@ struct mmc_queue_req {
struct mmc_async_req mmc_active;
enum mmc_packed_type cmd_type;
struct mmc_packed *packed;
+ struct mmc_cmdq_req cmdq_req;
};
struct mmc_queue {
struct mmc_card *card;
struct task_struct *thread;
struct semaphore thread_sem;
- unsigned int flags;
-#define MMC_QUEUE_SUSPENDED (1 << 0)
-#define MMC_QUEUE_NEW_REQUEST (1 << 1)
+ unsigned long flags;
+#define MMC_QUEUE_SUSPENDED 0
+#define MMC_QUEUE_NEW_REQUEST 1
+ int (*issue_fn)(struct mmc_queue *, struct request *);
+ int (*cmdq_issue_fn)(struct mmc_queue *,
+ struct request *);
+ void (*cmdq_complete_fn)(struct request *);
+ void (*cmdq_error_fn)(struct mmc_queue *);
+ enum blk_eh_timer_return (*cmdq_req_timed_out)(struct request *);
void *data;
struct request_queue *queue;
struct mmc_queue_req mqrq[2];
struct mmc_queue_req *mqrq_cur;
struct mmc_queue_req *mqrq_prev;
+ struct mmc_queue_req *mqrq_cmdq;
+ bool wr_packing_enabled;
+ int num_of_potential_packed_wr_reqs;
+ int num_wr_reqs_to_start_packing;
+ bool no_pack_for_random;
+ struct work_struct cmdq_err_work;
+
+ struct completion cmdq_pending_req_done;
+ struct completion cmdq_shutdown_complete;
+ struct request *cmdq_req_peeked;
+ int (*err_check_fn)(struct mmc_card *, struct mmc_async_req *);
+ void (*packed_test_fn)(struct request_queue *, struct mmc_queue_req *);
+ void (*cmdq_shutdown)(struct mmc_queue *);
#ifdef CONFIG_MMC_SIMULATE_MAX_SPEED
atomic_t max_write_speed;
atomic_t max_read_speed;
@@ -73,9 +93,9 @@ struct mmc_queue {
};
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,
- const char *);
+ const char *, int);
extern void mmc_cleanup_queue(struct mmc_queue *);
-extern void mmc_queue_suspend(struct mmc_queue *);
+extern int mmc_queue_suspend(struct mmc_queue *, int);
extern void mmc_queue_resume(struct mmc_queue *);
extern unsigned int mmc_queue_map_sg(struct mmc_queue *,
@@ -88,4 +108,9 @@ extern void mmc_packed_clean(struct mmc_queue *);
extern int mmc_access_rpmb(struct mmc_queue *);
+extern void print_mmc_packing_stats(struct mmc_card *card);
+
+extern int mmc_cmdq_init(struct mmc_queue *mq, struct mmc_card *card);
+extern void mmc_cmdq_clean(struct mmc_queue *mq, struct mmc_card *card);
+
#endif
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index 36d9d69..c0592ac 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -23,6 +23,17 @@
This driver can also be built as a module. If so, the module
will be called pwrseq_simple.
+config MMC_RING_BUFFER
+ bool "MMC_RING_BUFFER"
+ depends on MMC
+ default n
+ help
+ This enables the ring buffer tracing of significant
+ events for mmc driver to provide command history for
+ debugging purpose.
+
+ If unsure, say N.
+
config MMC_EMBEDDED_SDIO
boolean "MMC embedded SDIO device support (EXPERIMENTAL)"
help
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
index f007151..dbd1f1b 100644
--- a/drivers/mmc/core/Makefile
+++ b/drivers/mmc/core/Makefile
@@ -12,3 +12,4 @@
obj-$(CONFIG_PWRSEQ_SIMPLE) += pwrseq_simple.o
obj-$(CONFIG_PWRSEQ_EMMC) += pwrseq_emmc.o
mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o
+obj-$(CONFIG_MMC_RING_BUFFER) += ring_buffer.o
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index c64266f..1c28cf8 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -132,6 +132,16 @@ static void mmc_bus_shutdown(struct device *dev)
struct mmc_host *host = card->host;
int ret;
+ if (!drv) {
+ pr_debug("%s: %s: drv is NULL\n", dev_name(dev), __func__);
+ return;
+ }
+
+ if (!card) {
+ pr_debug("%s: %s: card is NULL\n", dev_name(dev), __func__);
+ return;
+ }
+
if (dev->driver && drv->shutdown)
drv->shutdown(card);
@@ -154,6 +164,8 @@ static int mmc_bus_suspend(struct device *dev)
if (ret)
return ret;
+ if (mmc_bus_needs_resume(host))
+ return 0;
ret = host->bus_ops->suspend(host);
return ret;
}
@@ -164,11 +176,17 @@ static int mmc_bus_resume(struct device *dev)
struct mmc_host *host = card->host;
int ret;
+ if (mmc_bus_manual_resume(host)) {
+ host->bus_resume_flags |= MMC_BUSRESUME_NEEDS_RESUME;
+ goto skip_full_resume;
+ }
+
ret = host->bus_ops->resume(host);
if (ret)
pr_warn("%s: error %d during resume (card was removed?)\n",
mmc_hostname(host), ret);
+skip_full_resume:
ret = pm_generic_resume(dev);
return ret;
}
@@ -180,6 +198,9 @@ static int mmc_runtime_suspend(struct device *dev)
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
+ if (mmc_bus_needs_resume(host))
+ return 0;
+
return host->bus_ops->runtime_suspend(host);
}
@@ -188,8 +209,12 @@ static int mmc_runtime_resume(struct device *dev)
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
+ if (mmc_bus_needs_resume(host))
+ host->bus_resume_flags &= ~MMC_BUSRESUME_NEEDS_RESUME;
+
return host->bus_ops->runtime_resume(host);
}
+
#endif /* !CONFIG_PM */
static const struct dev_pm_ops mmc_bus_pm_ops = {
@@ -273,6 +298,9 @@ struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)
card->dev.release = mmc_release_card;
card->dev.type = type;
+ spin_lock_init(&card->wr_pack_stats.lock);
+ spin_lock_init(&card->bkops.stats.lock);
+
return card;
}
@@ -350,6 +378,13 @@ int mmc_add_card(struct mmc_card *card)
card->dev.of_node = mmc_of_find_child_device(card->host, 0);
+ if (mmc_card_sdio(card)) {
+ ret = device_init_wakeup(&card->dev, true);
+ if (ret)
+ pr_err("%s: %s: failed to init wakeup: %d\n",
+ mmc_hostname(card->host), __func__, ret);
+ }
+
device_enable_async_suspend(&card->dev);
ret = device_add(&card->dev);
@@ -383,6 +418,9 @@ void mmc_remove_card(struct mmc_card *card)
of_node_put(card->dev.of_node);
}
+ kfree(card->wr_pack_stats.packing_events);
+ kfree(card->cached_ext_csd);
+
put_device(&card->dev);
}
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 9551238..9987859 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -14,6 +14,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
+#include <linux/devfreq.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/pagemap.h>
@@ -29,6 +30,8 @@
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/pm.h>
+#include <linux/jiffies.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
@@ -109,6 +112,7 @@ static void mmc_should_fail_request(struct mmc_host *host,
data->error = data_errors[prandom_u32() % ARRAY_SIZE(data_errors)];
data->bytes_xfered = (prandom_u32() % (data->bytes_xfered >> 9)) << 9;
+ data->fault_injected = true;
}
#else /* CONFIG_FAIL_MMC_REQUEST */
@@ -120,6 +124,748 @@ static inline void mmc_should_fail_request(struct mmc_host *host,
#endif /* CONFIG_FAIL_MMC_REQUEST */
+static bool mmc_is_data_request(struct mmc_request *mmc_request)
+{
+ switch (mmc_request->cmd->opcode) {
+ case MMC_READ_SINGLE_BLOCK:
+ case MMC_READ_MULTIPLE_BLOCK:
+ case MMC_WRITE_BLOCK:
+ case MMC_WRITE_MULTIPLE_BLOCK:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void mmc_clk_scaling_start_busy(struct mmc_host *host, bool lock_needed)
+{
+ struct mmc_devfeq_clk_scaling *clk_scaling = &host->clk_scaling;
+
+ if (!clk_scaling->enable)
+ return;
+
+ if (lock_needed)
+ spin_lock_bh(&clk_scaling->lock);
+
+ clk_scaling->start_busy = ktime_get();
+ clk_scaling->is_busy_started = true;
+
+ if (lock_needed)
+ spin_unlock_bh(&clk_scaling->lock);
+}
+
+static void mmc_clk_scaling_stop_busy(struct mmc_host *host, bool lock_needed)
+{
+ struct mmc_devfeq_clk_scaling *clk_scaling = &host->clk_scaling;
+
+ if (!clk_scaling->enable)
+ return;
+
+ if (lock_needed)
+ spin_lock_bh(&clk_scaling->lock);
+
+ if (!clk_scaling->is_busy_started) {
+ WARN_ON(1);
+ goto out;
+ }
+
+ clk_scaling->total_busy_time_us +=
+ ktime_to_us(ktime_sub(ktime_get(),
+ clk_scaling->start_busy));
+ pr_debug("%s: accumulated busy time is %lu usec\n",
+ mmc_hostname(host), clk_scaling->total_busy_time_us);
+ clk_scaling->is_busy_started = false;
+
+out:
+ if (lock_needed)
+ spin_unlock_bh(&clk_scaling->lock);
+}
+
+/**
+ * mmc_cmdq_clk_scaling_start_busy() - start busy timer for data requests
+ * @host: pointer to mmc host structure
+ * @lock_needed: flag indication if locking is needed
+ *
+ * This function starts the busy timer in case it was not already started.
+ */
+void mmc_cmdq_clk_scaling_start_busy(struct mmc_host *host,
+ bool lock_needed)
+{
+ if (!host->clk_scaling.enable)
+ return;
+
+ if (lock_needed)
+ spin_lock_bh(&host->clk_scaling.lock);
+
+ if (!host->clk_scaling.is_busy_started &&
+ !test_bit(CMDQ_STATE_DCMD_ACTIVE,
+ &host->cmdq_ctx.curr_state)) {
+ host->clk_scaling.start_busy = ktime_get();
+ host->clk_scaling.is_busy_started = true;
+ }
+
+ if (lock_needed)
+ spin_unlock_bh(&host->clk_scaling.lock);
+}
+EXPORT_SYMBOL(mmc_cmdq_clk_scaling_start_busy);
+
+/**
+ * mmc_cmdq_clk_scaling_stop_busy() - stop busy timer for last data requests
+ * @host: pointer to mmc host structure
+ * @lock_needed: flag indication if locking is needed
+ *
+ * This function stops the busy timer in case it is the last data request.
+ * In case the current request is not the last one, the busy time till
+ * now will be accumulated and the counter will be restarted.
+ */
+void mmc_cmdq_clk_scaling_stop_busy(struct mmc_host *host,
+ bool lock_needed, bool is_cmdq_dcmd)
+{
+ if (!host->clk_scaling.enable)
+ return;
+
+ if (lock_needed)
+ spin_lock_bh(&host->clk_scaling.lock);
+
+ /*
+ * For CQ mode: In completion of DCMD request, start busy time in
+ * case of pending data requests
+ */
+ if (is_cmdq_dcmd) {
+ if (host->cmdq_ctx.data_active_reqs) {
+ host->clk_scaling.is_busy_started = true;
+ host->clk_scaling.start_busy = ktime_get();
+ }
+ goto out;
+ }
+
+ host->clk_scaling.total_busy_time_us +=
+ ktime_to_us(ktime_sub(ktime_get(),
+ host->clk_scaling.start_busy));
+
+ if (host->cmdq_ctx.data_active_reqs) {
+ host->clk_scaling.is_busy_started = true;
+ host->clk_scaling.start_busy = ktime_get();
+ } else {
+ host->clk_scaling.is_busy_started = false;
+ }
+out:
+ if (lock_needed)
+ spin_unlock_bh(&host->clk_scaling.lock);
+
+}
+EXPORT_SYMBOL(mmc_cmdq_clk_scaling_stop_busy);
+
+/**
+ * mmc_can_scale_clk() - Check clock scaling capability
+ * @host: pointer to mmc host structure
+ */
+bool mmc_can_scale_clk(struct mmc_host *host)
+{
+ if (!host) {
+ pr_err("bad host parameter\n");
+ WARN_ON(1);
+ return false;
+ }
+
+ return host->caps2 & MMC_CAP2_CLK_SCALE;
+}
+EXPORT_SYMBOL(mmc_can_scale_clk);
+
+static int mmc_devfreq_get_dev_status(struct device *dev,
+ struct devfreq_dev_status *status)
+{
+ struct mmc_host *host = container_of(dev, struct mmc_host, class_dev);
+ struct mmc_devfeq_clk_scaling *clk_scaling;
+
+ if (!host) {
+ pr_err("bad host parameter\n");
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ clk_scaling = &host->clk_scaling;
+
+ if (!clk_scaling->enable)
+ return 0;
+
+ spin_lock_bh(&clk_scaling->lock);
+
+ /* accumulate the busy time of ongoing work */
+ memset(status, 0, sizeof(*status));
+ if (clk_scaling->is_busy_started) {
+ if (mmc_card_cmdq(host->card)) {
+ /* the "busy-timer" will be restarted in case there
+ * are pending data requests */
+ mmc_cmdq_clk_scaling_stop_busy(host, false, false);
+ } else {
+ mmc_clk_scaling_stop_busy(host, false);
+ mmc_clk_scaling_start_busy(host, false);
+ }
+ }
+
+ status->busy_time = clk_scaling->total_busy_time_us;
+ status->total_time = ktime_to_us(ktime_sub(ktime_get(),
+ clk_scaling->measure_interval_start));
+ clk_scaling->total_busy_time_us = 0;
+ status->current_frequency = clk_scaling->curr_freq;
+ clk_scaling->measure_interval_start = ktime_get();
+
+ pr_debug("%s: status: load = %lu%% - total_time=%lu busy_time = %lu, clk=%lu\n",
+ mmc_hostname(host),
+ (status->busy_time*100)/status->total_time,
+ status->total_time, status->busy_time,
+ status->current_frequency);
+
+ spin_unlock_bh(&clk_scaling->lock);
+
+ return 0;
+}
+
+static bool mmc_is_valid_state_for_clk_scaling(struct mmc_host *host)
+{
+ struct mmc_card *card = host->card;
+ u32 status;
+
+ /*
+ * If the current partition type is RPMB, clock switching may not
+ * work properly as sending tuning command (CMD21) is illegal in
+ * this mode.
+ */
+ if (!card || (mmc_card_mmc(card) &&
+ (card->part_curr == EXT_CSD_PART_CONFIG_ACC_RPMB ||
+ mmc_card_doing_bkops(card))))
+ return false;
+
+ if (mmc_send_status(card, &status)) {
+ pr_err("%s: Get card status fail\n", mmc_hostname(card->host));
+ return false;
+ }
+
+ return R1_CURRENT_STATE(status) == R1_STATE_TRAN;
+}
+
+int mmc_cmdq_halt_on_empty_queue(struct mmc_host *host)
+{
+ int err = 0;
+
+ err = wait_event_interruptible(host->cmdq_ctx.queue_empty_wq,
+ (!host->cmdq_ctx.active_reqs));
+ if (host->cmdq_ctx.active_reqs) {
+ pr_err("%s: %s: unexpected active requests (%lu)\n",
+ mmc_hostname(host), __func__,
+ host->cmdq_ctx.active_reqs);
+ return -EPERM;
+ }
+
+ err = mmc_cmdq_halt(host, true);
+ if (err) {
+ pr_err("%s: %s: mmc_cmdq_halt failed (%d)\n",
+ mmc_hostname(host), __func__, err);
+ goto out;
+ }
+
+out:
+ return err;
+}
+EXPORT_SYMBOL(mmc_cmdq_halt_on_empty_queue);
+
+int mmc_clk_update_freq(struct mmc_host *host,
+ unsigned long freq, enum mmc_load state)
+{
+ int err = 0;
+ bool cmdq_mode;
+
+ if (!host) {
+ pr_err("bad host parameter\n");
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ mmc_host_clk_hold(host);
+ cmdq_mode = mmc_card_cmdq(host->card);
+
+ /* make sure the card supports the frequency we want */
+ if (unlikely(freq > host->card->clk_scaling_highest)) {
+ freq = host->card->clk_scaling_highest;
+ pr_warn("%s: %s: frequency was overridden to %lu\n",
+ mmc_hostname(host), __func__,
+ host->card->clk_scaling_highest);
+ }
+
+ if (unlikely(freq < host->card->clk_scaling_lowest)) {
+ freq = host->card->clk_scaling_lowest;
+ pr_warn("%s: %s: frequency was overridden to %lu\n",
+ mmc_hostname(host), __func__,
+ host->card->clk_scaling_lowest);
+ }
+
+ if (freq == host->clk_scaling.curr_freq)
+ goto out;
+
+ if (host->ops->notify_load) {
+ err = host->ops->notify_load(host, state);
+ if (err) {
+ pr_err("%s: %s: fail on notify_load\n",
+ mmc_hostname(host), __func__);
+ goto out;
+ }
+ }
+
+ if (cmdq_mode) {
+ err = mmc_cmdq_halt_on_empty_queue(host);
+ if (err) {
+ pr_err("%s: %s: failed halting queue (%d)\n",
+ mmc_hostname(host), __func__, err);
+ goto halt_failed;
+ }
+ }
+
+ if (!mmc_is_valid_state_for_clk_scaling(host)) {
+ pr_debug("%s: invalid state for clock scaling - skipping",
+ mmc_hostname(host));
+ goto invalid_state;
+ }
+
+ err = host->bus_ops->change_bus_speed(host, &freq);
+ if (!err)
+ host->clk_scaling.curr_freq = freq;
+ else
+ pr_err("%s: %s: failed (%d) at freq=%lu\n",
+ mmc_hostname(host), __func__, err, freq);
+
+invalid_state:
+ if (cmdq_mode) {
+ if (mmc_cmdq_halt(host, false))
+ pr_err("%s: %s: cmdq unhalt failed\n",
+ mmc_hostname(host), __func__);
+ }
+
+halt_failed:
+ if (err) {
+ /* restore previous state */
+ if (host->ops->notify_load)
+ if (host->ops->notify_load(host,
+ host->clk_scaling.state))
+ pr_err("%s: %s: fail on notify_load restore\n",
+ mmc_hostname(host), __func__);
+ }
+out:
+ mmc_host_clk_release(host);
+ return err;
+}
+EXPORT_SYMBOL(mmc_clk_update_freq);
+
+static int mmc_devfreq_set_target(struct device *dev,
+ unsigned long *freq, u32 devfreq_flags)
+{
+ struct mmc_host *host = container_of(dev, struct mmc_host, class_dev);
+ struct mmc_devfeq_clk_scaling *clk_scaling;
+ int err = 0;
+ int abort;
+
+ if (!(host && freq)) {
+ pr_err("%s: unexpected host/freq parameter\n", __func__);
+ err = -EINVAL;
+ goto out;
+ }
+ clk_scaling = &host->clk_scaling;
+
+ if (!clk_scaling->enable)
+ goto out;
+
+ if (*freq == UINT_MAX)
+ *freq = clk_scaling->freq_table[1];
+ else
+ *freq = clk_scaling->freq_table[0];
+
+ pr_debug("%s: target freq = %lu (%s)\n", mmc_hostname(host),
+ *freq, current->comm);
+
+ if ((clk_scaling->curr_freq == *freq) ||
+ clk_scaling->skip_clk_scale_freq_update)
+ goto out;
+
+ /* No need to scale the clocks if they are gated */
+ if (!host->ios.clock)
+ goto out;
+
+ spin_lock_bh(&clk_scaling->lock);
+ if (clk_scaling->clk_scaling_in_progress) {
+ pr_debug("%s: clocks scaling is already in-progress by mmc thread\n",
+ mmc_hostname(host));
+ spin_unlock_bh(&clk_scaling->lock);
+ goto out;
+ }
+ clk_scaling->need_freq_change = true;
+ clk_scaling->target_freq = *freq;
+ clk_scaling->state = *freq < clk_scaling->curr_freq ?
+ MMC_LOAD_LOW : MMC_LOAD_HIGH;
+ spin_unlock_bh(&clk_scaling->lock);
+
+ abort = __mmc_claim_host(host, &clk_scaling->devfreq_abort);
+ if (abort)
+ goto out;
+
+ /*
+ * In case we were able to claim host there is no need to
+ * defer the frequency change. It will be done now
+ */
+ clk_scaling->need_freq_change = false;
+
+ mmc_host_clk_hold(host);
+ err = mmc_clk_update_freq(host, *freq, clk_scaling->state);
+ if (err && err != -EAGAIN)
+ pr_err("%s: clock scale to %lu failed with error %d\n",
+ mmc_hostname(host), *freq, err);
+ else
+ pr_debug("%s: clock change to %lu finished successfully (%s)\n",
+ mmc_hostname(host), *freq, current->comm);
+
+
+ mmc_host_clk_release(host);
+ mmc_release_host(host);
+out:
+ return err;
+}
+
+/**
+ * mmc_deferred_scaling() - scale clocks from data path (mmc thread context)
+ * @host: pointer to mmc host structure
+ *
+ * This function does clock scaling in case "need_freq_change" flag was set
+ * by the clock scaling logic.
+ */
+void mmc_deferred_scaling(struct mmc_host *host)
+{
+ unsigned long target_freq;
+ int err;
+
+ if (!host->clk_scaling.enable)
+ return;
+
+ spin_lock_bh(&host->clk_scaling.lock);
+
+ if (host->clk_scaling.clk_scaling_in_progress ||
+ !(host->clk_scaling.need_freq_change)) {
+ spin_unlock_bh(&host->clk_scaling.lock);
+ return;
+ }
+
+
+ atomic_inc(&host->clk_scaling.devfreq_abort);
+ target_freq = host->clk_scaling.target_freq;
+ host->clk_scaling.clk_scaling_in_progress = true;
+ host->clk_scaling.need_freq_change = false;
+ spin_unlock_bh(&host->clk_scaling.lock);
+ pr_debug("%s: doing deferred frequency change (%lu) (%s)\n",
+ mmc_hostname(host),
+ target_freq, current->comm);
+
+ err = mmc_clk_update_freq(host, target_freq,
+ host->clk_scaling.state);
+ if (err && err != -EAGAIN)
+ pr_err("%s: failed on deferred scale clocks (%d)\n",
+ mmc_hostname(host), err);
+ else
+ pr_debug("%s: clocks were successfully scaled to %lu (%s)\n",
+ mmc_hostname(host),
+ target_freq, current->comm);
+ host->clk_scaling.clk_scaling_in_progress = false;
+ atomic_dec(&host->clk_scaling.devfreq_abort);
+}
+EXPORT_SYMBOL(mmc_deferred_scaling);
+
+static int mmc_devfreq_create_freq_table(struct mmc_host *host)
+{
+ int i;
+ struct mmc_devfeq_clk_scaling *clk_scaling = &host->clk_scaling;
+
+ pr_debug("%s: supported: lowest=%lu, highest=%lu\n",
+ mmc_hostname(host),
+ host->card->clk_scaling_lowest,
+ host->card->clk_scaling_highest);
+
+ if (!clk_scaling->freq_table) {
+ pr_debug("%s: no frequency table defined - setting default\n",
+ mmc_hostname(host));
+ clk_scaling->freq_table = kzalloc(
+ 2*sizeof(*(clk_scaling->freq_table)), GFP_KERNEL);
+ if (!clk_scaling->freq_table)
+ return -ENOMEM;
+ clk_scaling->freq_table[0] = host->card->clk_scaling_lowest;
+ clk_scaling->freq_table[1] = host->card->clk_scaling_highest;
+ clk_scaling->freq_table_sz = 2;
+ goto out;
+ }
+
+ if (host->card->clk_scaling_lowest >
+ clk_scaling->freq_table[0])
+ pr_debug("%s: frequency table undershot possible freq\n",
+ mmc_hostname(host));
+
+ for (i = 0; i < clk_scaling->freq_table_sz; i++) {
+ if (clk_scaling->freq_table[i] <=
+ host->card->clk_scaling_highest)
+ continue;
+ clk_scaling->freq_table[i] =
+ host->card->clk_scaling_highest;
+ clk_scaling->freq_table_sz = i + 1;
+ pr_debug("%s: frequency table overshot possible freq (%d)\n",
+ mmc_hostname(host), clk_scaling->freq_table[i]);
+ break;
+ }
+
+out:
+ clk_scaling->devfreq_profile.freq_table = (unsigned long *)clk_scaling->freq_table;
+ clk_scaling->devfreq_profile.max_state = clk_scaling->freq_table_sz;
+
+ for (i = 0; i < clk_scaling->freq_table_sz; i++)
+ pr_debug("%s: freq[%d] = %u\n",
+ mmc_hostname(host), i, clk_scaling->freq_table[i]);
+
+ return 0;
+}
+
+/**
+ * mmc_init_devfreq_clk_scaling() - Initialize clock scaling
+ * @host: pointer to mmc host structure
+ *
+ * Initialize clock scaling for supported hosts. It is assumed that the caller
+ * ensure clock is running at maximum possible frequency before calling this
+ * function. Shall use struct devfreq_simple_ondemand_data to configure
+ * governor.
+ */
+int mmc_init_clk_scaling(struct mmc_host *host)
+{
+ int err;
+
+ if (!host || !host->card) {
+ pr_err("%s: unexpected host/card parameters\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!mmc_can_scale_clk(host) ||
+ !host->bus_ops->change_bus_speed) {
+ pr_debug("%s: clock scaling is not supported\n",
+ mmc_hostname(host));
+ return 0;
+ }
+
+ pr_debug("registering %s dev (%p) to devfreq",
+ mmc_hostname(host),
+ mmc_classdev(host));
+
+ if (host->clk_scaling.devfreq) {
+ pr_err("%s: dev is already registered for dev %p\n",
+ mmc_hostname(host),
+ mmc_dev(host));
+ return -EPERM;
+ }
+ spin_lock_init(&host->clk_scaling.lock);
+ atomic_set(&host->clk_scaling.devfreq_abort, 0);
+ host->clk_scaling.curr_freq = host->ios.clock;
+ host->clk_scaling.clk_scaling_in_progress = false;
+ host->clk_scaling.need_freq_change = false;
+ host->clk_scaling.is_busy_started = false;
+
+ host->clk_scaling.devfreq_profile.polling_ms =
+ host->clk_scaling.polling_delay_ms;
+ host->clk_scaling.devfreq_profile.get_dev_status =
+ mmc_devfreq_get_dev_status;
+ host->clk_scaling.devfreq_profile.target = mmc_devfreq_set_target;
+ host->clk_scaling.devfreq_profile.initial_freq = host->ios.clock;
+
+ host->clk_scaling.ondemand_gov_data.simple_scaling = true;
+ host->clk_scaling.ondemand_gov_data.upthreshold =
+ host->clk_scaling.upthreshold;
+ host->clk_scaling.ondemand_gov_data.downdifferential =
+ host->clk_scaling.upthreshold - host->clk_scaling.downthreshold;
+
+ err = mmc_devfreq_create_freq_table(host);
+ if (err) {
+ pr_err("%s: fail to create devfreq frequency table\n",
+ mmc_hostname(host));
+ return err;
+ }
+
+ pr_debug("%s: adding devfreq with: upthreshold=%u downthreshold=%u polling=%u\n",
+ mmc_hostname(host),
+ host->clk_scaling.ondemand_gov_data.upthreshold,
+ host->clk_scaling.ondemand_gov_data.downdifferential,
+ host->clk_scaling.devfreq_profile.polling_ms);
+ host->clk_scaling.devfreq = devfreq_add_device(
+ mmc_classdev(host),
+ &host->clk_scaling.devfreq_profile,
+ "simple_ondemand",
+ &host->clk_scaling.ondemand_gov_data);
+ if (!host->clk_scaling.devfreq) {
+ pr_err("%s: unable to register with devfreq\n",
+ mmc_hostname(host));
+ return -EPERM;
+ }
+
+ pr_debug("%s: clk scaling is enabled for device %s (%p) with devfreq %p (clock = %uHz)\n",
+ mmc_hostname(host),
+ dev_name(mmc_classdev(host)),
+ mmc_classdev(host),
+ host->clk_scaling.devfreq,
+ host->ios.clock);
+
+ host->clk_scaling.enable = true;
+
+ return err;
+}
+EXPORT_SYMBOL(mmc_init_clk_scaling);
+
+/**
+ * mmc_suspend_clk_scaling() - suspend clock scaling
+ * @host: pointer to mmc host structure
+ *
+ * This API will suspend devfreq feature for the specific host.
+ * The statistics collected by mmc will be cleared.
+ * This function is intended to be called by the pm callbacks
+ * (e.g. runtime_suspend, suspend) of the mmc device
+ */
+int mmc_suspend_clk_scaling(struct mmc_host *host)
+{
+ int err;
+
+ if (!host) {
+ WARN(1, "bad host parameter\n");
+ return -EINVAL;
+ }
+
+ if (!mmc_can_scale_clk(host) || !host->clk_scaling.enable)
+ return 0;
+
+ if (!host->clk_scaling.devfreq) {
+ pr_err("%s: %s: no devfreq is assosiated with this device\n",
+ mmc_hostname(host), __func__);
+ return -EPERM;
+ }
+
+ atomic_inc(&host->clk_scaling.devfreq_abort);
+ wake_up(&host->wq);
+ err = devfreq_suspend_device(host->clk_scaling.devfreq);
+ if (err) {
+ pr_err("%s: %s: failed to suspend devfreq\n",
+ mmc_hostname(host), __func__);
+ return err;
+ }
+ host->clk_scaling.enable = false;
+
+ host->clk_scaling.total_busy_time_us = 0;
+
+ pr_debug("%s: devfreq was removed\n", mmc_hostname(host));
+
+ return 0;
+}
+EXPORT_SYMBOL(mmc_suspend_clk_scaling);
+
+/**
+ * mmc_resume_clk_scaling() - resume clock scaling
+ * @host: pointer to mmc host structure
+ *
+ * This API will resume devfreq feature for the specific host.
+ * This API is intended to be called by the pm callbacks
+ * (e.g. runtime_suspend, suspend) of the mmc device
+ */
+int mmc_resume_clk_scaling(struct mmc_host *host)
+{
+ int err = 0;
+ u32 max_clk_idx = 0;
+ u32 devfreq_max_clk = 0;
+ u32 devfreq_min_clk = 0;
+
+ if (!host) {
+ WARN(1, "bad host parameter\n");
+ return -EINVAL;
+ }
+
+ if (!mmc_can_scale_clk(host))
+ return 0;
+
+ if (!host->clk_scaling.devfreq) {
+ pr_err("%s: %s: no devfreq is assosiated with this device\n",
+ mmc_hostname(host), __func__);
+ return -EPERM;
+ }
+
+ atomic_set(&host->clk_scaling.devfreq_abort, 0);
+
+ max_clk_idx = host->clk_scaling.freq_table_sz - 1;
+ devfreq_max_clk = host->clk_scaling.freq_table[max_clk_idx];
+ devfreq_min_clk = host->clk_scaling.freq_table[0];
+
+ host->clk_scaling.curr_freq = devfreq_max_clk;
+ if (host->ios.clock < host->card->clk_scaling_highest)
+ host->clk_scaling.curr_freq = devfreq_min_clk;
+
+ host->clk_scaling.clk_scaling_in_progress = false;
+ host->clk_scaling.need_freq_change = false;
+
+ err = devfreq_resume_device(host->clk_scaling.devfreq);
+ if (err) {
+ pr_err("%s: %s: failed to resume devfreq (%d)\n",
+ mmc_hostname(host), __func__, err);
+ } else {
+ host->clk_scaling.enable = true;
+ pr_debug("%s: devfreq resumed\n", mmc_hostname(host));
+ }
+
+ return err;
+}
+EXPORT_SYMBOL(mmc_resume_clk_scaling);
+
+/**
+ * mmc_exit_devfreq_clk_scaling() - Disable clock scaling
+ * @host: pointer to mmc host structure
+ *
+ * Disable clock scaling permanently.
+ */
+int mmc_exit_clk_scaling(struct mmc_host *host)
+{
+ int err;
+
+ if (!host) {
+ pr_err("%s: bad host parameter\n", __func__);
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ if (!mmc_can_scale_clk(host))
+ return 0;
+
+ if (!host->clk_scaling.devfreq) {
+ pr_err("%s: %s: no devfreq is assosiated with this device\n",
+ mmc_hostname(host), __func__);
+ return -EPERM;
+ }
+
+ err = mmc_suspend_clk_scaling(host);
+ if (err) {
+ pr_err("%s: %s: fail to suspend clock scaling (%d)\n",
+ mmc_hostname(host), __func__, err);
+ return err;
+ }
+
+ err = devfreq_remove_device(host->clk_scaling.devfreq);
+ if (err) {
+ pr_err("%s: remove devfreq failed (%d)\n",
+ mmc_hostname(host), err);
+ return err;
+ }
+
+ host->clk_scaling.devfreq = NULL;
+ atomic_set(&host->clk_scaling.devfreq_abort, 1);
+ pr_debug("%s: devfreq was removed\n", mmc_hostname(host));
+
+ return 0;
+}
+EXPORT_SYMBOL(mmc_exit_clk_scaling);
+
static inline void mmc_complete_cmd(struct mmc_request *mrq)
{
if (mrq->cap_cmd_during_tfr && !completion_done(&mrq->cmd_completion))
@@ -150,6 +896,12 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
{
struct mmc_command *cmd = mrq->cmd;
int err = cmd->error;
+#ifdef CONFIG_MMC_PERF_PROFILING
+ ktime_t diff;
+#endif
+
+ if (host->clk_scaling.is_busy_started)
+ mmc_clk_scaling_stop_busy(host, true);
/* Flag re-tuning needed on CRC errors */
if ((cmd->opcode != MMC_SEND_TUNING_BLOCK &&
@@ -198,6 +950,24 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
cmd->resp[2], cmd->resp[3]);
if (mrq->data) {
+#ifdef CONFIG_MMC_PERF_PROFILING
+ if (host->perf_enable) {
+ diff = ktime_sub(ktime_get(), host->perf.start);
+ if (mrq->data->flags == MMC_DATA_READ) {
+ host->perf.rbytes_drv +=
+ mrq->data->bytes_xfered;
+ host->perf.rtime_drv =
+ ktime_add(host->perf.rtime_drv,
+ diff);
+ } else {
+ host->perf.wbytes_drv +=
+ mrq->data->bytes_xfered;
+ host->perf.wtime_drv =
+ ktime_add(host->perf.wtime_drv,
+ diff);
+ }
+ }
+#endif
pr_debug("%s: %d bytes transferred: %d\n",
mmc_hostname(host),
mrq->data->bytes_xfered, mrq->data->error);
@@ -341,33 +1111,194 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
mrq->stop->error = 0;
mrq->stop->mrq = mrq;
}
+#ifdef CONFIG_MMC_PERF_PROFILING
+ if (host->perf_enable)
+ host->perf.start = ktime_get();
+#endif
}
mmc_host_clk_hold(host);
led_trigger_event(host->led, LED_FULL);
+
+ if (mmc_is_data_request(mrq)) {
+ mmc_deferred_scaling(host);
+ mmc_clk_scaling_start_busy(host, true);
+ }
+
__mmc_start_request(host, mrq);
return 0;
}
+static void mmc_start_cmdq_request(struct mmc_host *host,
+ struct mmc_request *mrq)
+{
+ if (mrq->data) {
+ pr_debug("%s: blksz %d blocks %d flags %08x tsac %lu ms nsac %d\n",
+ mmc_hostname(host), mrq->data->blksz,
+ mrq->data->blocks, mrq->data->flags,
+ mrq->data->timeout_ns / NSEC_PER_MSEC,
+ mrq->data->timeout_clks);
+
+ BUG_ON(mrq->data->blksz > host->max_blk_size);
+ BUG_ON(mrq->data->blocks > host->max_blk_count);
+ BUG_ON(mrq->data->blocks * mrq->data->blksz >
+ host->max_req_size);
+ mrq->data->error = 0;
+ mrq->data->mrq = mrq;
+ }
+
+ if (mrq->cmd) {
+ mrq->cmd->error = 0;
+ mrq->cmd->mrq = mrq;
+ }
+
+ mmc_host_clk_hold(host);
+ if (likely(host->cmdq_ops->request))
+ host->cmdq_ops->request(host, mrq);
+ else
+ pr_err("%s: %s: issue request failed\n", mmc_hostname(host),
+ __func__);
+}
+
/**
- * mmc_start_bkops - start BKOPS for supported cards
+ * mmc_blk_init_bkops_statistics - initialize bkops statistics
* @card: MMC card to start BKOPS
- * @form_exception: A flag to indicate if this function was
- * called due to an exception raised by the card
*
- * Start background operations whenever requested.
- * When the urgent BKOPS bit is set in a R1 command response
- * then background operations should be started immediately.
+ * Initialize and enable the bkops statistics
+ */
+void mmc_blk_init_bkops_statistics(struct mmc_card *card)
+{
+ int i;
+ struct mmc_bkops_stats *stats;
+
+ if (!card)
+ return;
+
+ stats = &card->bkops.stats;
+ spin_lock(&stats->lock);
+
+ stats->manual_start = 0;
+ stats->hpi = 0;
+ stats->auto_start = 0;
+ stats->auto_stop = 0;
+ for (i = 0 ; i < MMC_BKOPS_NUM_SEVERITY_LEVELS ; i++)
+ stats->level[i] = 0;
+ stats->enabled = true;
+
+ spin_unlock(&stats->lock);
+}
+EXPORT_SYMBOL(mmc_blk_init_bkops_statistics);
+
+static void mmc_update_bkops_hpi(struct mmc_bkops_stats *stats)
+{
+ spin_lock_irq(&stats->lock);
+ if (stats->enabled)
+ stats->hpi++;
+ spin_unlock_irq(&stats->lock);
+}
+
+static void mmc_update_bkops_start(struct mmc_bkops_stats *stats)
+{
+ spin_lock_irq(&stats->lock);
+ if (stats->enabled)
+ stats->manual_start++;
+ spin_unlock_irq(&stats->lock);
+}
+
+static void mmc_update_bkops_auto_on(struct mmc_bkops_stats *stats)
+{
+ spin_lock_irq(&stats->lock);
+ if (stats->enabled)
+ stats->auto_start++;
+ spin_unlock_irq(&stats->lock);
+}
+
+static void mmc_update_bkops_auto_off(struct mmc_bkops_stats *stats)
+{
+ spin_lock_irq(&stats->lock);
+ if (stats->enabled)
+ stats->auto_stop++;
+ spin_unlock_irq(&stats->lock);
+}
+
+static void mmc_update_bkops_level(struct mmc_bkops_stats *stats,
+ unsigned level)
+{
+ BUG_ON(level >= MMC_BKOPS_NUM_SEVERITY_LEVELS);
+ spin_lock_irq(&stats->lock);
+ if (stats->enabled)
+ stats->level[level]++;
+ spin_unlock_irq(&stats->lock);
+}
+
+/**
+ * mmc_set_auto_bkops - set auto BKOPS for supported cards
+ * @card: MMC card to start BKOPS
+ * @enable: enable/disable flag
+ * Configure the card to run automatic BKOPS.
+ *
+ * Should be called when host is claimed.
*/
-void mmc_start_bkops(struct mmc_card *card, bool from_exception)
+int mmc_set_auto_bkops(struct mmc_card *card, bool enable)
+{
+ int ret = 0;
+ u8 bkops_en;
+
+ BUG_ON(!card);
+ enable = !!enable;
+
+ if (unlikely(!mmc_card_support_auto_bkops(card))) {
+ pr_err("%s: %s: card doesn't support auto bkops\n",
+ mmc_hostname(card->host), __func__);
+ return -EPERM;
+ }
+
+ if (enable) {
+ if (mmc_card_doing_auto_bkops(card))
+ goto out;
+ bkops_en = card->ext_csd.bkops_en | EXT_CSD_BKOPS_AUTO_EN;
+ } else {
+ if (!mmc_card_doing_auto_bkops(card))
+ goto out;
+ bkops_en = card->ext_csd.bkops_en & ~EXT_CSD_BKOPS_AUTO_EN;
+ }
+
+ ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BKOPS_EN,
+ bkops_en, 0);
+ if (ret) {
+ pr_err("%s: %s: error in setting auto bkops to %d (%d)\n",
+ mmc_hostname(card->host), __func__, enable, ret);
+ } else {
+ if (enable) {
+ mmc_card_set_auto_bkops(card);
+ mmc_update_bkops_auto_on(&card->bkops.stats);
+ } else {
+ mmc_card_clr_auto_bkops(card);
+ mmc_update_bkops_auto_off(&card->bkops.stats);
+ }
+ card->ext_csd.bkops_en = bkops_en;
+ pr_debug("%s: %s: bkops state %x\n",
+ mmc_hostname(card->host), __func__, bkops_en);
+ }
+out:
+ return ret;
+}
+EXPORT_SYMBOL(mmc_set_auto_bkops);
+
+/**
+ * mmc_check_bkops - check BKOPS for supported cards
+ * @card: MMC card to check BKOPS
+ *
+ * Read the BKOPS status in order to determine whether the
+ * card requires bkops to be started.
+ */
+void mmc_check_bkops(struct mmc_card *card)
{
int err;
- int timeout;
- bool use_busy_signal;
BUG_ON(!card);
- if (!card->ext_csd.man_bkops_en || mmc_card_doing_bkops(card))
+ if (mmc_card_doing_bkops(card))
return;
err = mmc_read_bkops_status(card);
@@ -377,47 +1308,50 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
return;
}
- if (!card->ext_csd.raw_bkops_status)
+ card->bkops.needs_check = false;
+
+ mmc_update_bkops_level(&card->bkops.stats,
+ card->ext_csd.raw_bkops_status);
+
+ card->bkops.needs_bkops = card->ext_csd.raw_bkops_status > 0;
+}
+EXPORT_SYMBOL(mmc_check_bkops);
+
+/**
+ * mmc_start_manual_bkops - start BKOPS for supported cards
+ * @card: MMC card to start BKOPS
+ *
+ * Send START_BKOPS to the card.
+ * The function should be called with claimed host.
+ */
+void mmc_start_manual_bkops(struct mmc_card *card)
+{
+ int err;
+
+ BUG_ON(!card);
+
+ if (unlikely(!mmc_card_configured_manual_bkops(card)))
return;
- if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2 &&
- from_exception)
+ if (mmc_card_doing_bkops(card))
return;
- mmc_claim_host(card->host);
- if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
- timeout = MMC_BKOPS_MAX_TIMEOUT;
- use_busy_signal = true;
- } else {
- timeout = 0;
- use_busy_signal = false;
- }
-
mmc_retune_hold(card->host);
- err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_BKOPS_START, 1, timeout,
- use_busy_signal, true, false);
+ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BKOPS_START,
+ 1, 0, false, true, false);
if (err) {
- pr_warn("%s: Error %d starting bkops\n",
- mmc_hostname(card->host), err);
- mmc_retune_release(card->host);
- goto out;
+ pr_err("%s: Error %d starting manual bkops\n",
+ mmc_hostname(card->host), err);
+ } else {
+ mmc_card_set_doing_bkops(card);
+ mmc_update_bkops_start(&card->bkops.stats);
+ card->bkops.needs_bkops = false;
}
- /*
- * For urgent bkops status (LEVEL_2 and more)
- * bkops executed synchronously, otherwise
- * the operation is in progress
- */
- if (!use_busy_signal)
- mmc_card_set_doing_bkops(card);
- else
- mmc_retune_release(card->host);
-out:
- mmc_release_host(card->host);
+ mmc_retune_release(card->host);
}
-EXPORT_SYMBOL(mmc_start_bkops);
+EXPORT_SYMBOL(mmc_start_manual_bkops);
/*
* mmc_wait_data_done() - done callback for data request
@@ -427,10 +1361,13 @@ EXPORT_SYMBOL(mmc_start_bkops);
*/
static void mmc_wait_data_done(struct mmc_request *mrq)
{
+ unsigned long flags;
struct mmc_context_info *context_info = &mrq->host->context_info;
+ spin_lock_irqsave(&context_info->lock, flags);
context_info->is_done_rcv = true;
wake_up_interruptible(&context_info->wait);
+ spin_unlock_irqrestore(&context_info->lock, flags);
}
static void mmc_wait_done(struct mmc_request *mrq)
@@ -520,6 +1457,7 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host,
struct mmc_command *cmd;
struct mmc_context_info *context_info = &host->context_info;
int err;
+ bool is_done_rcv = false;
unsigned long flags;
while (1) {
@@ -527,9 +1465,10 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host,
(context_info->is_done_rcv ||
context_info->is_new_req));
spin_lock_irqsave(&context_info->lock, flags);
+ is_done_rcv = context_info->is_done_rcv;
context_info->is_waiting_last_req = false;
spin_unlock_irqrestore(&context_info->lock, flags);
- if (context_info->is_done_rcv) {
+ if (is_done_rcv) {
context_info->is_done_rcv = false;
context_info->is_new_req = false;
cmd = mrq->cmd;
@@ -564,20 +1503,20 @@ void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq)
struct mmc_command *cmd;
while (1) {
- wait_for_completion(&mrq->completion);
+ wait_for_completion_io(&mrq->completion);
cmd = mrq->cmd;
/*
- * If host has timed out waiting for the sanitize
+ * If host has timed out waiting for the sanitize/bkops
* to complete, card might be still in programming state
* so let's try to bring the card out of programming
* state.
*/
- if (cmd->sanitize_busy && cmd->error == -ETIMEDOUT) {
+ if ((cmd->bkops_busy || cmd->sanitize_busy) && cmd->error == -ETIMEDOUT) {
if (!mmc_interrupt_hpi(host->card)) {
- pr_warn("%s: %s: Interrupted sanitize\n",
- mmc_hostname(host), __func__);
+ pr_warn("%s: %s: Interrupted sanitize/bkops\n",
+ mmc_hostname(host), __func__);
cmd->error = 0;
break;
} else {
@@ -664,6 +1603,134 @@ static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq,
}
/**
+ * mmc_cmdq_discard_card_queue - discard the task[s] in the device
+ * @host: host instance
+ * @tasks: mask of tasks to be knocked off
+ * 0: remove all queued tasks
+ */
+int mmc_cmdq_discard_queue(struct mmc_host *host, u32 tasks)
+{
+ return mmc_discard_queue(host, tasks);
+}
+EXPORT_SYMBOL(mmc_cmdq_discard_queue);
+
+
+/**
+ * mmc_cmdq_post_req - post process of a completed request
+ * @host: host instance
+ * @tag: the request tag.
+ * @err: non-zero is error, success otherwise
+ */
+void mmc_cmdq_post_req(struct mmc_host *host, int tag, int err)
+{
+ if (likely(host->cmdq_ops->post_req))
+ host->cmdq_ops->post_req(host, tag, err);
+}
+EXPORT_SYMBOL(mmc_cmdq_post_req);
+
+/**
+ * mmc_cmdq_halt - halt/un-halt the command queue engine
+ * @host: host instance
+ * @halt: true - halt, un-halt otherwise
+ *
+ * Host halts the command queue engine. It should complete
+ * the ongoing transfer and release the bus.
+ * All legacy commands can be sent upon successful
+ * completion of this function.
+ * Returns 0 on success, negative otherwise
+ */
+int mmc_cmdq_halt(struct mmc_host *host, bool halt)
+{
+ int err = 0;
+
+ if (mmc_host_cq_disable(host)) {
+ pr_debug("%s: %s: CQE is already disabled\n",
+ mmc_hostname(host), __func__);
+ return 0;
+ }
+
+ if ((halt && mmc_host_halt(host)) ||
+ (!halt && !mmc_host_halt(host))) {
+ pr_debug("%s: %s: CQE is already %s\n", mmc_hostname(host),
+ __func__, halt ? "halted" : "un-halted");
+ return 0;
+ }
+
+ mmc_host_clk_hold(host);
+ if (host->cmdq_ops->halt) {
+ err = host->cmdq_ops->halt(host, halt);
+ if (!err && host->ops->notify_halt)
+ host->ops->notify_halt(host, halt);
+ if (!err && halt)
+ mmc_host_set_halt(host);
+ else if (!err && !halt) {
+ mmc_host_clr_halt(host);
+ wake_up(&host->cmdq_ctx.wait);
+ }
+ } else {
+ err = -ENOSYS;
+ }
+ mmc_host_clk_release(host);
+ return err;
+}
+EXPORT_SYMBOL(mmc_cmdq_halt);
+
+int mmc_cmdq_start_req(struct mmc_host *host, struct mmc_cmdq_req *cmdq_req)
+{
+ struct mmc_request *mrq = &cmdq_req->mrq;
+
+ mrq->host = host;
+ if (mmc_card_removed(host->card)) {
+ mrq->cmd->error = -ENOMEDIUM;
+ return -ENOMEDIUM;
+ }
+ mmc_start_cmdq_request(host, mrq);
+ return 0;
+}
+EXPORT_SYMBOL(mmc_cmdq_start_req);
+
+static void mmc_cmdq_dcmd_req_done(struct mmc_request *mrq)
+{
+ mmc_host_clk_release(mrq->host);
+ complete(&mrq->completion);
+}
+
+int mmc_cmdq_wait_for_dcmd(struct mmc_host *host,
+ struct mmc_cmdq_req *cmdq_req)
+{
+ struct mmc_request *mrq = &cmdq_req->mrq;
+ struct mmc_command *cmd = mrq->cmd;
+ int err = 0;
+
+ init_completion(&mrq->completion);
+ mrq->done = mmc_cmdq_dcmd_req_done;
+ err = mmc_cmdq_start_req(host, cmdq_req);
+ if (err)
+ return err;
+
+ wait_for_completion_io(&mrq->completion);
+ if (cmd->error) {
+ pr_err("%s: DCMD %d failed with err %d\n",
+ mmc_hostname(host), cmd->opcode,
+ cmd->error);
+ err = cmd->error;
+ mmc_host_clk_hold(host);
+ host->cmdq_ops->dumpstate(host);
+ mmc_host_clk_release(host);
+ }
+ return err;
+}
+EXPORT_SYMBOL(mmc_cmdq_wait_for_dcmd);
+
+int mmc_cmdq_prepare_flush(struct mmc_command *cmd)
+{
+ return __mmc_switch_cmdq_mode(cmd, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_FLUSH_CACHE, 1,
+ 0, true, true);
+}
+EXPORT_SYMBOL(mmc_cmdq_prepare_flush);
+
+/**
* mmc_start_req - start a non-blocking request
* @host: MMC host to start command
* @areq: async request to start
@@ -713,7 +1780,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
if (areq)
mmc_post_req(host, areq->mrq, -EINVAL);
- mmc_start_bkops(host->card, true);
+ mmc_check_bkops(host->card);
/* prepare the request again */
if (areq)
@@ -735,8 +1802,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
if (host->areq)
mmc_post_req(host, host->areq->mrq, 0);
- /* Cancel a prepared request if it was not started. */
- if ((err || start_err) && areq)
+ if (err && areq)
mmc_post_req(host, areq->mrq, -EINVAL);
if (err)
@@ -764,6 +1830,10 @@ EXPORT_SYMBOL(mmc_start_req);
*/
void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
{
+#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
+ if (mmc_bus_needs_resume(host))
+ mmc_resume_bus(host);
+#endif
__mmc_start_req(host, mrq);
if (!mrq->cap_cmd_during_tfr)
@@ -819,8 +1889,6 @@ int mmc_interrupt_hpi(struct mmc_card *card)
}
err = mmc_send_hpi_cmd(card, &status);
- if (err)
- goto out;
prg_wait = jiffies + msecs_to_jiffies(card->ext_csd.out_of_int_time);
do {
@@ -828,8 +1896,13 @@ int mmc_interrupt_hpi(struct mmc_card *card)
if (!err && R1_CURRENT_STATE(status) == R1_STATE_TRAN)
break;
- if (time_after(jiffies, prg_wait))
- err = -ETIMEDOUT;
+ if (time_after(jiffies, prg_wait)) {
+ err = mmc_send_status(card, &status);
+ if (!err && R1_CURRENT_STATE(status) != R1_STATE_TRAN)
+ err = -ETIMEDOUT;
+ else
+ break;
+ }
} while (!err);
out:
@@ -881,6 +1954,11 @@ int mmc_stop_bkops(struct mmc_card *card)
int err = 0;
BUG_ON(!card);
+ if (unlikely(!mmc_card_configured_manual_bkops(card)))
+ goto out;
+ if (!mmc_card_doing_bkops(card))
+ goto out;
+
err = mmc_interrupt_hpi(card);
/*
@@ -889,10 +1967,11 @@ int mmc_stop_bkops(struct mmc_card *card)
*/
if (!err || (err == -EINVAL)) {
mmc_card_clr_doing_bkops(card);
+ mmc_update_bkops_hpi(&card->bkops.stats);
mmc_retune_release(card->host);
err = 0;
}
-
+out:
return err;
}
EXPORT_SYMBOL(mmc_stop_bkops);
@@ -908,8 +1987,14 @@ int mmc_read_bkops_status(struct mmc_card *card)
if (err)
return err;
- card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
- card->ext_csd.raw_exception_status = ext_csd[EXT_CSD_EXP_EVENTS_STATUS];
+ card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS] &
+ MMC_BKOPS_URGENCY_MASK;
+ card->ext_csd.raw_exception_status =
+ ext_csd[EXT_CSD_EXP_EVENTS_STATUS] & (EXT_CSD_URGENT_BKOPS |
+ EXT_CSD_DYNCAP_NEEDED |
+ EXT_CSD_SYSPOOL_EXHAUSTED
+ | EXT_CSD_PACKED_FAILURE);
+
kfree(ext_csd);
return 0;
}
@@ -927,6 +2012,10 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
{
unsigned int mult;
+ if (!card) {
+ WARN_ON(1);
+ return;
+ }
/*
* SDIO cards only define an upper 1 s limit on access.
*/
@@ -993,9 +2082,11 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
* Address this by setting the read timeout to a "reasonably high"
* value. For the cards tested, 600ms has proven enough. If necessary,
* this value can be increased if other problematic cards require this.
+ * Certain Hynix 5.x cards giving read timeout even with 300ms.
+ * Increasing further to max value (4s).
*/
if (mmc_card_long_read_time(card) && data->flags & MMC_DATA_READ) {
- data->timeout_ns = 600000000;
+ data->timeout_ns = 4000000000u;
data->timeout_clks = 0;
}
@@ -1014,6 +2105,11 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
data->timeout_ns = 100000000; /* 100ms */
}
}
+ /* Increase the timeout values for some bad INAND MCP devices */
+ if (card->quirks & MMC_QUIRK_INAND_DATA_TIMEOUT) {
+ data->timeout_ns = 4000000000u; /* 4s */
+ data->timeout_clks = 0;
+ }
}
EXPORT_SYMBOL(mmc_set_data_timeout);
@@ -1064,6 +2160,7 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
might_sleep();
add_wait_queue(&host->wq, &wait);
+
spin_lock_irqsave(&host->lock, flags);
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);
@@ -1135,9 +2232,14 @@ void mmc_get_card(struct mmc_card *card)
{
pm_runtime_get_sync(&card->dev);
mmc_claim_host(card->host);
+#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
+ if (mmc_bus_needs_resume(card->host))
+ mmc_resume_bus(card->host);
+#endif
}
EXPORT_SYMBOL(mmc_get_card);
+
/*
* This is a helper function, which releases the host and drops the runtime
* pm reference for the card device.
@@ -1154,7 +2256,7 @@ EXPORT_SYMBOL(mmc_put_card);
* Internal function that does the actual ios call to the host driver,
* optionally printing some debug output.
*/
-static inline void mmc_set_ios(struct mmc_host *host)
+void mmc_set_ios(struct mmc_host *host)
{
struct mmc_ios *ios = &host->ios;
@@ -1167,7 +2269,21 @@ static inline void mmc_set_ios(struct mmc_host *host)
if (ios->clock > 0)
mmc_set_ungated(host);
host->ops->set_ios(host, ios);
+ if (ios->old_rate != ios->clock) {
+ if (likely(ios->clk_ts)) {
+ char trace_info[80];
+ snprintf(trace_info, 80,
+ "%s: freq_KHz %d --> %d | t = %d",
+ mmc_hostname(host), ios->old_rate / 1000,
+ ios->clock / 1000, jiffies_to_msecs(
+ (long)jiffies - (long)ios->clk_ts));
+ trace_mmc_clk(trace_info);
+ }
+ ios->old_rate = ios->clock;
+ ios->clk_ts = jiffies;
+ }
}
+EXPORT_SYMBOL(mmc_set_ios);
/*
* Control chip select pin on a host.
@@ -1210,6 +2326,8 @@ void mmc_gate_clock(struct mmc_host *host)
{
unsigned long flags;
+ WARN_ON(!host->ios.clock);
+
spin_lock_irqsave(&host->clk_lock, flags);
host->clk_old = host->ios.clock;
host->ios.clock = 0;
@@ -1232,7 +2350,7 @@ void mmc_ungate_clock(struct mmc_host *host)
* we just ignore the call.
*/
if (host->clk_old) {
- BUG_ON(host->ios.clock);
+ WARN_ON(host->ios.clock);
/* This call will also set host->clk_gated to false */
__mmc_set_clock(host, host->clk_old);
}
@@ -1255,6 +2373,10 @@ void mmc_set_ungated(struct mmc_host *host)
void mmc_set_ungated(struct mmc_host *host)
{
}
+
+void mmc_gate_clock(struct mmc_host *host)
+{
+}
#endif
int mmc_execute_tuning(struct mmc_card *card)
@@ -1315,9 +2437,10 @@ void mmc_set_initial_state(struct mmc_host *host)
if (mmc_host_is_spi(host))
host->ios.chip_select = MMC_CS_HIGH;
- else
+ else {
host->ios.chip_select = MMC_CS_DONTCARE;
- host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
+ host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+ }
host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY;
host->ios.drv_type = 0;
@@ -1796,12 +2919,15 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
pr_warn("%s: cannot verify signal voltage switch\n",
mmc_hostname(host));
- mmc_host_clk_hold(host);
-
cmd.opcode = SD_SWITCH_VOLTAGE;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ /*
+ * Hold the clock reference so clock doesn't get auto gated during this
+ * voltage switch sequence.
+ */
+ mmc_host_clk_hold(host);
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
goto err_command;
@@ -2057,6 +3183,40 @@ static inline void mmc_bus_put(struct mmc_host *host)
spin_unlock_irqrestore(&host->lock, flags);
}
+int mmc_resume_bus(struct mmc_host *host)
+{
+ unsigned long flags;
+ int err = 0;
+
+ if (!mmc_bus_needs_resume(host))
+ return -EINVAL;
+
+ pr_debug("%s: Starting deferred resume\n", mmc_hostname(host));
+ spin_lock_irqsave(&host->lock, flags);
+ host->bus_resume_flags &= ~MMC_BUSRESUME_NEEDS_RESUME;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ mmc_bus_get(host);
+ if (host->bus_ops && !host->bus_dead && host->card) {
+ mmc_power_up(host, host->card->ocr);
+ BUG_ON(!host->bus_ops->resume);
+ host->bus_ops->resume(host);
+ if (mmc_card_cmdq(host->card)) {
+ err = mmc_cmdq_halt(host, false);
+ if (err)
+ pr_err("%s: %s: unhalt failed: %d\n",
+ mmc_hostname(host), __func__, err);
+ else
+ mmc_card_clr_suspended(host->card);
+ }
+ }
+
+ mmc_bus_put(host);
+ pr_debug("%s: Deferred resume completed\n", mmc_hostname(host));
+ return 0;
+}
+EXPORT_SYMBOL(mmc_resume_bus);
+
/*
* Assign a mmc bus handler to a host. Only one bus handler may control a
* host at any given time.
@@ -2288,16 +3448,9 @@ static unsigned int mmc_erase_timeout(struct mmc_card *card,
return mmc_mmc_erase_timeout(card, arg, qty);
}
-static int mmc_do_erase(struct mmc_card *card, unsigned int from,
- unsigned int to, unsigned int arg)
+static u32 mmc_get_erase_qty(struct mmc_card *card, u32 from, u32 to)
{
- struct mmc_command cmd = {0};
- unsigned int qty = 0, busy_timeout = 0;
- bool use_r1b_resp = false;
- unsigned long timeout;
- int err;
-
- mmc_retune_hold(card->host);
+ u32 qty = 0;
/*
* qty is used to calculate the erase timeout which depends on how many
@@ -2323,12 +3476,120 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
else
qty += ((to / card->erase_size) -
(from / card->erase_size)) + 1;
+ return qty;
+}
+
+static int mmc_cmdq_send_erase_cmd(struct mmc_cmdq_req *cmdq_req,
+ struct mmc_card *card, u32 opcode, u32 arg, u32 qty)
+{
+ struct mmc_command *cmd = cmdq_req->mrq.cmd;
+ int err;
+
+ memset(cmd, 0, sizeof(struct mmc_command));
+
+ cmd->opcode = opcode;
+ cmd->arg = arg;
+ if (cmd->opcode == MMC_ERASE) {
+ cmd->flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+ cmd->busy_timeout = mmc_erase_timeout(card, arg, qty);
+ } else {
+ cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+ }
+
+ err = mmc_cmdq_wait_for_dcmd(card->host, cmdq_req);
+ if (err) {
+ pr_err("mmc_erase: group start error %d, status %#x\n",
+ err, cmd->resp[0]);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int mmc_cmdq_do_erase(struct mmc_cmdq_req *cmdq_req,
+ struct mmc_card *card, unsigned int from,
+ unsigned int to, unsigned int arg)
+{
+ struct mmc_command *cmd = cmdq_req->mrq.cmd;
+ unsigned int qty = 0;
+ unsigned long timeout;
+ unsigned int fr, nr;
+ int err;
+
+ fr = from;
+ nr = to - from + 1;
+
+ qty = mmc_get_erase_qty(card, from, to);
if (!mmc_card_blockaddr(card)) {
from <<= 9;
to <<= 9;
}
+ err = mmc_cmdq_send_erase_cmd(cmdq_req, card, MMC_ERASE_GROUP_START,
+ from, qty);
+ if (err)
+ goto out;
+
+ err = mmc_cmdq_send_erase_cmd(cmdq_req, card, MMC_ERASE_GROUP_END,
+ to, qty);
+ if (err)
+ goto out;
+
+ err = mmc_cmdq_send_erase_cmd(cmdq_req, card, MMC_ERASE,
+ arg, qty);
+ if (err)
+ goto out;
+
+ timeout = jiffies + msecs_to_jiffies(MMC_CORE_TIMEOUT_MS);
+ do {
+ memset(cmd, 0, sizeof(struct mmc_command));
+ cmd->opcode = MMC_SEND_STATUS;
+ cmd->arg = card->rca << 16;
+ cmd->flags = MMC_RSP_R1 | MMC_CMD_AC;
+ /* Do not retry else we can't see errors */
+ err = mmc_cmdq_wait_for_dcmd(card->host, cmdq_req);
+ if (err || (cmd->resp[0] & 0xFDF92000)) {
+ pr_err("error %d requesting status %#x\n",
+ err, cmd->resp[0]);
+ err = -EIO;
+ goto out;
+ }
+ /* Timeout if the device never becomes ready for data and
+ * never leaves the program state.
+ */
+ if (time_after(jiffies, timeout)) {
+ pr_err("%s: Card stuck in programming state! %s\n",
+ mmc_hostname(card->host), __func__);
+ err = -EIO;
+ goto out;
+ }
+ } while (!(cmd->resp[0] & R1_READY_FOR_DATA) ||
+ (R1_CURRENT_STATE(cmd->resp[0]) == R1_STATE_PRG));
+out:
+ return err;
+}
+
+static int mmc_do_erase(struct mmc_card *card, unsigned int from,
+ unsigned int to, unsigned int arg)
+{
+ struct mmc_command cmd = {0};
+ unsigned int qty = 0, busy_timeout = 0;
+ bool use_r1b_resp = false;
+ unsigned long timeout;
+ unsigned int fr, nr;
+ int err;
+
+ fr = from;
+ nr = to - from + 1;
+
+ qty = mmc_get_erase_qty(card, from, to);
+
+ if (!mmc_card_blockaddr(card)) {
+ from <<= 9;
+ to <<= 9;
+ }
+
+ mmc_retune_hold(card->host);
if (mmc_card_sd(card))
cmd.opcode = SD_ERASE_WR_BLK_START;
else
@@ -2475,20 +3736,9 @@ static unsigned int mmc_align_erase_size(struct mmc_card *card,
return nr_new;
}
-/**
- * mmc_erase - erase sectors.
- * @card: card to erase
- * @from: first sector to erase
- * @nr: number of sectors to erase
- * @arg: erase command argument (SD supports only %MMC_ERASE_ARG)
- *
- * Caller must claim host before calling this function.
- */
-int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
- unsigned int arg)
+int mmc_erase_sanity_check(struct mmc_card *card, unsigned int from,
+ unsigned int nr, unsigned int arg)
{
- unsigned int rem, to = from + nr;
- int err;
if (!(card->host->caps & MMC_CAP_ERASE) ||
!(card->csd.cmdclass & CCC_ERASE))
@@ -2512,6 +3762,68 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
if (from % card->erase_size || nr % card->erase_size)
return -EINVAL;
}
+ return 0;
+}
+
+int mmc_cmdq_erase(struct mmc_cmdq_req *cmdq_req,
+ struct mmc_card *card, unsigned int from, unsigned int nr,
+ unsigned int arg)
+{
+ unsigned int rem, to = from + nr;
+ int ret;
+
+ ret = mmc_erase_sanity_check(card, from, nr, arg);
+ if (ret)
+ return ret;
+
+ if (arg == MMC_ERASE_ARG) {
+ rem = from % card->erase_size;
+ if (rem) {
+ rem = card->erase_size - rem;
+ from += rem;
+ if (nr > rem)
+ nr -= rem;
+ else
+ return 0;
+ }
+ rem = nr % card->erase_size;
+ if (rem)
+ nr -= rem;
+ }
+
+ if (nr == 0)
+ return 0;
+
+ to = from + nr;
+
+ if (to <= from)
+ return -EINVAL;
+
+ /* 'from' and 'to' are inclusive */
+ to -= 1;
+
+ return mmc_cmdq_do_erase(cmdq_req, card, from, to, arg);
+}
+EXPORT_SYMBOL(mmc_cmdq_erase);
+
+/**
+ * mmc_erase - erase sectors.
+ * @card: card to erase
+ * @from: first sector to erase
+ * @nr: number of sectors to erase
+ * @arg: erase command argument (SD supports only %MMC_ERASE_ARG)
+ *
+ * Caller must claim host before calling this function.
+ */
+int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
+ unsigned int arg)
+{
+ unsigned int rem, to = from + nr;
+ int ret;
+
+ ret = mmc_erase_sanity_check(card, from, nr, arg);
+ if (ret)
+ return ret;
if (arg == MMC_ERASE_ARG)
nr = mmc_align_erase_size(card, &from, &to, nr);
@@ -2535,10 +3847,10 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
*/
rem = card->erase_size - (from % card->erase_size);
if ((arg & MMC_TRIM_ARGS) && (card->eg_boundary) && (nr > rem)) {
- err = mmc_do_erase(card, from, from + rem - 1, arg);
+ ret = mmc_do_erase(card, from, from + rem - 1, arg);
from += rem;
- if ((err) || (to <= from))
- return err;
+ if ((ret) || (to <= from))
+ return ret;
}
return mmc_do_erase(card, from, to, arg);
@@ -2688,6 +4000,9 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
struct mmc_host *host = card->host;
unsigned int max_discard, max_trim;
+ if (!host->max_busy_timeout ||
+ (host->caps2 & MMC_CAP2_MAX_DISCARD_SIZE))
+
/*
* Without erase_group_def set, MMC erase timeout depends on clock
* frequence which can change. In that case, the best choice is
@@ -2749,6 +4064,23 @@ static void mmc_hw_reset_for_init(struct mmc_host *host)
mmc_host_clk_release(host);
}
+/*
+ * mmc_cmdq_hw_reset: Helper API for doing
+ * reset_all of host and reinitializing card.
+ * This must be called with mmc_claim_host
+ * acquired by the caller.
+ */
+int mmc_cmdq_hw_reset(struct mmc_host *host)
+{
+ if (!host->bus_ops->power_restore)
+ return -EOPNOTSUPP;
+
+ mmc_power_cycle(host, host->ocr_avail);
+ mmc_select_voltage(host, host->card->ocr);
+ return host->bus_ops->power_restore(host);
+}
+EXPORT_SYMBOL(mmc_cmdq_hw_reset);
+
int mmc_hw_reset(struct mmc_host *host)
{
int ret;
@@ -2889,12 +4221,16 @@ EXPORT_SYMBOL(mmc_detect_card_removed);
void mmc_rescan(struct work_struct *work)
{
+ unsigned long flags;
struct mmc_host *host =
container_of(work, struct mmc_host, detect.work);
- int i;
- if (host->rescan_disable)
+ spin_lock_irqsave(&host->lock, flags);
+ if (host->rescan_disable) {
+ spin_unlock_irqrestore(&host->lock, flags);
return;
+ }
+ spin_unlock_irqrestore(&host->lock, flags);
/* If there is a non-removable card registered, only scan once */
if (!mmc_card_is_removable(host) && host->rescan_entered)
@@ -2945,13 +4281,7 @@ void mmc_rescan(struct work_struct *work)
mmc_release_host(host);
goto out;
}
-
- for (i = 0; i < ARRAY_SIZE(freqs); i++) {
- if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
- break;
- if (freqs[i] <= host->f_min)
- break;
- }
++ mmc_rescan_try_freq(host, host->f_min);
mmc_release_host(host);
out:
@@ -2961,18 +4291,18 @@ void mmc_rescan(struct work_struct *work)
void mmc_start_host(struct mmc_host *host)
{
+ mmc_claim_host(host);
host->f_init = max(freqs[0], host->f_min);
host->rescan_disable = 0;
host->ios.power_mode = MMC_POWER_UNDEFINED;
- mmc_claim_host(host);
if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
mmc_power_off(host);
else
mmc_power_up(host, host->ocr_avail);
- mmc_release_host(host);
mmc_gpiod_request_cd_irq(host);
+ mmc_release_host(host);
_mmc_detect_change(host, 0, false);
}
@@ -3055,7 +4385,9 @@ int mmc_power_restore_host(struct mmc_host *host)
}
mmc_power_up(host, host->card->ocr);
+ mmc_claim_host(host);
ret = host->bus_ops->power_restore(host);
+ mmc_release_host(host);
mmc_bus_put(host);
@@ -3064,6 +4396,40 @@ int mmc_power_restore_host(struct mmc_host *host)
EXPORT_SYMBOL(mmc_power_restore_host);
/*
+ * Add barrier request to the requests in cache
+ */
+int mmc_cache_barrier(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+ int err = 0;
+
+ if (!card->ext_csd.cache_ctrl ||
+ (card->quirks & MMC_QUIRK_CACHE_DISABLE))
+ goto out;
+
+ if (!mmc_card_mmc(card))
+ goto out;
+
+ if (!card->ext_csd.barrier_en)
+ return -ENOTSUPP;
+
+ /*
+ * If a device receives maximum supported barrier
+ * requests, a barrier command is treated as a
+ * flush command. Hence, it is betetr to use
+ * flush timeout instead a generic CMD6 timeout
+ */
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_FLUSH_CACHE, 0x2, 0);
+ if (err)
+ pr_err("%s: cache barrier error %d\n",
+ mmc_hostname(host), err);
+out:
+ return err;
+}
+EXPORT_SYMBOL(mmc_cache_barrier);
+
+/*
* Flush the cache to the non-volatile storage.
*/
int mmc_flush_cache(struct mmc_card *card)
@@ -3072,12 +4438,23 @@ int mmc_flush_cache(struct mmc_card *card)
if (mmc_card_mmc(card) &&
(card->ext_csd.cache_size > 0) &&
- (card->ext_csd.cache_ctrl & 1)) {
+ (card->ext_csd.cache_ctrl & 1) &&
+ (!(card->quirks & MMC_QUIRK_CACHE_DISABLE))) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_FLUSH_CACHE, 1, 0);
- if (err)
+ if (err == -ETIMEDOUT) {
+ pr_err("%s: cache flush timeout\n",
+ mmc_hostname(card->host));
+ err = mmc_interrupt_hpi(card);
+ if (err) {
+ pr_err("%s: mmc_interrupt_hpi() failed (%d)\n",
+ mmc_hostname(card->host), err);
+ err = -ENODEV;
+ }
+ } else if (err) {
pr_err("%s: cache flush error %d\n",
mmc_hostname(card->host), err);
+ }
}
return err;
@@ -3130,6 +4507,10 @@ static int mmc_pm_notify(struct notifier_block *notify_block,
spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 0;
+ if (mmc_bus_manual_resume(host)) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ break;
+ }
spin_unlock_irqrestore(&host->lock, flags);
_mmc_detect_change(host, 0, false);
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index c975c7a..3f39058 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -15,21 +15,6 @@
#define MMC_CMD_RETRIES 3
-struct mmc_bus_ops {
- void (*remove)(struct mmc_host *);
- void (*detect)(struct mmc_host *);
- int (*pre_suspend)(struct mmc_host *);
- int (*suspend)(struct mmc_host *);
- int (*resume)(struct mmc_host *);
- int (*runtime_suspend)(struct mmc_host *);
- int (*runtime_resume)(struct mmc_host *);
- int (*power_save)(struct mmc_host *);
- int (*power_restore)(struct mmc_host *);
- int (*alive)(struct mmc_host *);
- int (*shutdown)(struct mmc_host *);
- int (*reset)(struct mmc_host *);
-};
-
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
void mmc_detach_bus(struct mmc_host *host);
@@ -40,6 +25,8 @@ void mmc_init_erase(struct mmc_card *card);
void mmc_set_chip_select(struct mmc_host *host, int mode);
void mmc_set_clock(struct mmc_host *host, unsigned int hz);
+int mmc_clk_update_freq(struct mmc_host *host,
+ unsigned long freq, enum mmc_load state);
void mmc_gate_clock(struct mmc_host *host);
void mmc_ungate_clock(struct mmc_host *host);
void mmc_set_ungated(struct mmc_host *host);
@@ -62,6 +49,8 @@ static inline void mmc_delay(unsigned int ms)
if (ms < 1000 / HZ) {
cond_resched();
mdelay(ms);
+ } else if (ms < jiffies_to_msecs(2)) {
+ usleep_range(ms * 1000, (ms + 1) * 1000);
} else {
msleep(ms);
}
@@ -89,6 +78,13 @@ void mmc_remove_card_debugfs(struct mmc_card *card);
void mmc_init_context_info(struct mmc_host *host);
+extern bool mmc_can_scale_clk(struct mmc_host *host);
+extern int mmc_init_clk_scaling(struct mmc_host *host);
+extern int mmc_suspend_clk_scaling(struct mmc_host *host);
+extern int mmc_resume_clk_scaling(struct mmc_host *host);
+extern int mmc_exit_clk_scaling(struct mmc_host *host);
+extern unsigned long mmc_get_max_frequency(struct mmc_host *host);
+
int mmc_execute_tuning(struct mmc_card *card);
int mmc_hs200_to_hs400(struct mmc_card *card);
int mmc_hs400_to_hs200(struct mmc_card *card);
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index bf0f6ce..0d0d56f 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/fault-inject.h>
+#include <linux/uaccess.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
@@ -31,6 +32,26 @@ module_param(fail_request, charp, 0);
#endif /* CONFIG_FAIL_MMC_REQUEST */
/* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */
+static int mmc_ring_buffer_show(struct seq_file *s, void *data)
+{
+ struct mmc_host *mmc = s->private;
+
+ mmc_dump_trace_buffer(mmc, s);
+ return 0;
+}
+
+static int mmc_ring_buffer_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mmc_ring_buffer_show, inode->i_private);
+}
+
+static const struct file_operations mmc_ring_buffer_fops = {
+ .open = mmc_ring_buffer_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static int mmc_ios_show(struct seq_file *s, void *data)
{
static const char *vdd_str[] = {
@@ -234,6 +255,100 @@ static int mmc_clock_opt_set(void *data, u64 val)
DEFINE_SIMPLE_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
"%llu\n");
+#include <linux/delay.h>
+
+static int mmc_scale_get(void *data, u64 *val)
+{
+ struct mmc_host *host = data;
+
+ *val = host->clk_scaling.curr_freq;
+
+ return 0;
+}
+
+static int mmc_scale_set(void *data, u64 val)
+{
+ int err = 0;
+ struct mmc_host *host = data;
+
+ mmc_claim_host(host);
+ mmc_host_clk_hold(host);
+
+ /* change frequency from sysfs manually */
+ err = mmc_clk_update_freq(host, val, host->clk_scaling.state);
+ if (err == -EAGAIN)
+ err = 0;
+ else if (err)
+ pr_err("%s: clock scale to %llu failed with error %d\n",
+ mmc_hostname(host), val, err);
+ else
+ pr_debug("%s: clock change to %llu finished successfully (%s)\n",
+ mmc_hostname(host), val, current->comm);
+
+ mmc_host_clk_release(host);
+ mmc_release_host(host);
+
+ return err;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(mmc_scale_fops, mmc_scale_get, mmc_scale_set,
+ "%llu\n");
+
+static int mmc_max_clock_get(void *data, u64 *val)
+{
+ struct mmc_host *host = data;
+
+ if (!host)
+ return -EINVAL;
+
+ *val = host->f_max;
+
+ return 0;
+}
+
+static int mmc_max_clock_set(void *data, u64 val)
+{
+ struct mmc_host *host = data;
+ int err = -EINVAL;
+ unsigned long freq = val;
+ unsigned int old_freq;
+
+ if (!host || (val < host->f_min))
+ goto out;
+
+ mmc_claim_host(host);
+ if (host->bus_ops && host->bus_ops->change_bus_speed) {
+ old_freq = host->f_max;
+ host->f_max = freq;
+
+ err = host->bus_ops->change_bus_speed(host, &freq);
+
+ if (err)
+ host->f_max = old_freq;
+ }
+ mmc_release_host(host);
+out:
+ return err;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(mmc_max_clock_fops, mmc_max_clock_get,
+ mmc_max_clock_set, "%llu\n");
+
+static int mmc_force_err_set(void *data, u64 val)
+{
+ struct mmc_host *host = data;
+
+ if (host && host->ops && host->ops->force_err_irq) {
+ mmc_host_clk_hold(host);
+ host->ops->force_err_irq(host, val);
+ mmc_host_clk_release(host);
+ }
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(mmc_force_err_fops, NULL, mmc_force_err_set, "%llu\n");
+
void mmc_add_host_debugfs(struct mmc_host *host)
{
struct dentry *root;
@@ -256,6 +371,29 @@ void mmc_add_host_debugfs(struct mmc_host *host)
&mmc_clock_fops))
goto err_node;
+ if (!debugfs_create_file("max_clock", S_IRUSR | S_IWUSR, root, host,
+ &mmc_max_clock_fops))
+ goto err_node;
+
+ if (!debugfs_create_file("scale", S_IRUSR | S_IWUSR, root, host,
+ &mmc_scale_fops))
+ goto err_node;
+
+ if (!debugfs_create_bool("skip_clk_scale_freq_update",
+ S_IRUSR | S_IWUSR, root,
+ &host->clk_scaling.skip_clk_scale_freq_update))
+ goto err_node;
+
+ if (!debugfs_create_bool("cmdq_task_history",
+ S_IRUSR | S_IWUSR, root,
+ &host->cmdq_thist_enabled))
+ goto err_node;
+
+#ifdef CONFIG_MMC_RING_BUFFER
+ if (!debugfs_create_file("ring_buffer", S_IRUSR,
+ root, host, &mmc_ring_buffer_fops))
+ goto err_node;
+#endif
#ifdef CONFIG_MMC_CLKGATE
if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR),
root, &host->clk_delay))
@@ -270,6 +408,10 @@ void mmc_add_host_debugfs(struct mmc_host *host)
&host->fail_mmc_request)))
goto err_node;
#endif
+ if (!debugfs_create_file("force_error", S_IWUSR, root, host,
+ &mmc_force_err_fops))
+ goto err_node;
+
return;
err_node:
@@ -291,11 +433,26 @@ static int mmc_dbg_card_status_get(void *data, u64 *val)
int ret;
mmc_get_card(card);
+ if (mmc_card_cmdq(card)) {
+ ret = mmc_cmdq_halt_on_empty_queue(card->host);
+ if (ret) {
+ pr_err("%s: halt failed while doing %s err (%d)\n",
+ mmc_hostname(card->host), __func__,
+ ret);
+ goto out;
+ }
+ }
ret = mmc_send_status(data, &status);
if (!ret)
*val = status;
+ if (mmc_card_cmdq(card)) {
+ if (mmc_cmdq_halt(card->host, false))
+ pr_err("%s: %s: cmdq unhalt failed\n",
+ mmc_hostname(card->host), __func__);
+ }
+out:
mmc_put_card(card);
return ret;
@@ -318,8 +475,18 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
return -ENOMEM;
mmc_get_card(card);
+ if (mmc_card_cmdq(card)) {
+ err = mmc_cmdq_halt_on_empty_queue(card->host);
+ if (err) {
+ pr_err("%s: halt failed while doing %s err (%d)\n",
+ mmc_hostname(card->host), __func__,
+ err);
+ mmc_put_card(card);
+ goto out_free_halt;
+ }
+ }
+
err = mmc_get_ext_csd(card, &ext_csd);
- mmc_put_card(card);
if (err)
goto out_free;
@@ -329,10 +496,25 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
BUG_ON(n != EXT_CSD_STR_LEN);
filp->private_data = buf;
+
+ if (mmc_card_cmdq(card)) {
+ if (mmc_cmdq_halt(card->host, false))
+ pr_err("%s: %s: cmdq unhalt failed\n",
+ mmc_hostname(card->host), __func__);
+ }
+
+ mmc_put_card(card);
kfree(ext_csd);
return 0;
out_free:
+ if (mmc_card_cmdq(card)) {
+ if (mmc_cmdq_halt(card->host, false))
+ pr_err("%s: %s: cmdq unhalt failed\n",
+ mmc_hostname(card->host), __func__);
+ }
+ mmc_put_card(card);
+out_free_halt:
kfree(buf);
return err;
}
@@ -359,6 +541,275 @@ static const struct file_operations mmc_dbg_ext_csd_fops = {
.llseek = default_llseek,
};
+static int mmc_wr_pack_stats_open(struct inode *inode, struct file *filp)
+{
+ struct mmc_card *card = inode->i_private;
+
+ filp->private_data = card;
+ card->wr_pack_stats.print_in_read = 1;
+ return 0;
+}
+
+#define TEMP_BUF_SIZE 256
+static ssize_t mmc_wr_pack_stats_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct mmc_card *card = filp->private_data;
+ struct mmc_wr_pack_stats *pack_stats;
+ int i;
+ int max_num_of_packed_reqs = 0;
+ char *temp_buf;
+
+ if (!card)
+ return cnt;
+
+ if (!access_ok(VERIFY_WRITE, ubuf, cnt))
+ return cnt;
+
+ if (!card->wr_pack_stats.print_in_read)
+ return 0;
+
+ if (!card->wr_pack_stats.enabled) {
+ pr_info("%s: write packing statistics are disabled\n",
+ mmc_hostname(card->host));
+ goto exit;
+ }
+
+ pack_stats = &card->wr_pack_stats;
+
+ if (!pack_stats->packing_events) {
+ pr_info("%s: NULL packing_events\n", mmc_hostname(card->host));
+ goto exit;
+ }
+
+ max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
+
+ temp_buf = kmalloc(TEMP_BUF_SIZE, GFP_KERNEL);
+ if (!temp_buf)
+ goto exit;
+
+ spin_lock(&pack_stats->lock);
+
+ snprintf(temp_buf, TEMP_BUF_SIZE, "%s: write packing statistics:\n",
+ mmc_hostname(card->host));
+ strlcat(ubuf, temp_buf, cnt);
+
+ for (i = 1 ; i <= max_num_of_packed_reqs ; ++i) {
+ if (pack_stats->packing_events[i]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: Packed %d reqs - %d times\n",
+ mmc_hostname(card->host), i,
+ pack_stats->packing_events[i]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ }
+
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: stopped packing due to the following reasons:\n",
+ mmc_hostname(card->host));
+ strlcat(ubuf, temp_buf, cnt);
+
+ if (pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: exceed max num of segments\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[EXCEEDS_SECTORS]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: exceed max num of sectors\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[EXCEEDS_SECTORS]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[WRONG_DATA_DIR]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: wrong data direction\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[WRONG_DATA_DIR]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: flush or discard\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[EMPTY_QUEUE]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: empty queue\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[EMPTY_QUEUE]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[REL_WRITE]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: rel write\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[REL_WRITE]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[THRESHOLD]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: Threshold\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[THRESHOLD]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+
+ if (pack_stats->pack_stop_reason[LARGE_SEC_ALIGN]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: Large sector alignment\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[LARGE_SEC_ALIGN]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[RANDOM]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: random request\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[RANDOM]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+ if (pack_stats->pack_stop_reason[FUA]) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: %d times: fua request\n",
+ mmc_hostname(card->host),
+ pack_stats->pack_stop_reason[FUA]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+
+ spin_unlock(&pack_stats->lock);
+
+ kfree(temp_buf);
+
+ pr_info("%s", ubuf);
+
+exit:
+ if (card->wr_pack_stats.print_in_read == 1) {
+ card->wr_pack_stats.print_in_read = 0;
+ return strnlen(ubuf, cnt);
+ }
+
+ return 0;
+}
+
+static ssize_t mmc_wr_pack_stats_write(struct file *filp,
+ const char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+{
+ struct mmc_card *card = filp->private_data;
+ int value;
+
+ if (!card)
+ return cnt;
+
+ if (!access_ok(VERIFY_READ, ubuf, cnt))
+ return cnt;
+
+ sscanf(ubuf, "%d", &value);
+ if (value) {
+ mmc_blk_init_packed_statistics(card);
+ } else {
+ spin_lock(&card->wr_pack_stats.lock);
+ card->wr_pack_stats.enabled = false;
+ spin_unlock(&card->wr_pack_stats.lock);
+ }
+
+ return cnt;
+}
+
+static const struct file_operations mmc_dbg_wr_pack_stats_fops = {
+ .open = mmc_wr_pack_stats_open,
+ .read = mmc_wr_pack_stats_read,
+ .write = mmc_wr_pack_stats_write,
+};
+
+static int mmc_bkops_stats_read(struct seq_file *file, void *data)
+{
+ struct mmc_card *card = file->private;
+ struct mmc_bkops_stats *stats;
+ int i;
+
+ if (!card)
+ return -EINVAL;
+
+ stats = &card->bkops.stats;
+
+ if (!stats->enabled) {
+ pr_info("%s: bkops statistics are disabled\n",
+ mmc_hostname(card->host));
+ goto exit;
+ }
+
+ spin_lock(&stats->lock);
+
+ seq_printf(file, "%s: bkops statistics:\n",
+ mmc_hostname(card->host));
+ seq_printf(file, "%s: BKOPS: sent START_BKOPS to device: %u\n",
+ mmc_hostname(card->host), stats->manual_start);
+ seq_printf(file, "%s: BKOPS: stopped due to HPI: %u\n",
+ mmc_hostname(card->host), stats->hpi);
+ seq_printf(file, "%s: BKOPS: sent AUTO_EN set to 1: %u\n",
+ mmc_hostname(card->host), stats->auto_start);
+ seq_printf(file, "%s: BKOPS: sent AUTO_EN set to 0: %u\n",
+ mmc_hostname(card->host), stats->auto_stop);
+
+ for (i = 0 ; i < MMC_BKOPS_NUM_SEVERITY_LEVELS ; ++i)
+ seq_printf(file, "%s: BKOPS: due to level %d: %u\n",
+ mmc_hostname(card->host), i, stats->level[i]);
+
+ spin_unlock(&stats->lock);
+
+exit:
+
+ return 0;
+}
+
+static ssize_t mmc_bkops_stats_write(struct file *filp,
+ const char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+{
+ struct mmc_card *card = filp->f_mapping->host->i_private;
+ int value;
+ struct mmc_bkops_stats *stats;
+ int err;
+
+ if (!card)
+ return cnt;
+
+ stats = &card->bkops.stats;
+
+ err = kstrtoint_from_user(ubuf, cnt, 0, &value);
+ if (err) {
+ pr_err("%s: %s: error parsing input from user (%d)\n",
+ mmc_hostname(card->host), __func__, err);
+ return err;
+ }
+ if (value) {
+ mmc_blk_init_bkops_statistics(card);
+ } else {
+ spin_lock(&stats->lock);
+ stats->enabled = false;
+ spin_unlock(&stats->lock);
+ }
+
+ return cnt;
+}
+
+static int mmc_bkops_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mmc_bkops_stats_read, inode->i_private);
+}
+
+static const struct file_operations mmc_dbg_bkops_stats_fops = {
+ .open = mmc_bkops_stats_open,
+ .read = seq_read,
+ .write = mmc_bkops_stats_write,
+};
+
void mmc_add_card_debugfs(struct mmc_card *card)
{
struct mmc_host *host = card->host;
@@ -391,6 +842,19 @@ void mmc_add_card_debugfs(struct mmc_card *card)
&mmc_dbg_ext_csd_fops))
goto err;
+ if (mmc_card_mmc(card) && (card->ext_csd.rev >= 6) &&
+ (card->host->caps2 & MMC_CAP2_PACKED_WR))
+ if (!debugfs_create_file("wr_pack_stats", S_IRUSR, root, card,
+ &mmc_dbg_wr_pack_stats_fops))
+ goto err;
+
+ if (mmc_card_mmc(card) && (card->ext_csd.rev >= 5) &&
+ (mmc_card_configured_auto_bkops(card) ||
+ mmc_card_configured_manual_bkops(card)))
+ if (!debugfs_create_file("bkops_stats", S_IRUSR, root, card,
+ &mmc_dbg_bkops_stats_fops))
+ goto err;
+
return;
err:
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index e9f74a2..eb730fd 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -4,6 +4,7 @@
* Copyright (C) 2003 Russell King, All Rights Reserved.
* Copyright (C) 2007-2008 Pierre Ossman
* Copyright (C) 2010 Linus Walleij
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* 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
@@ -24,6 +25,8 @@
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
+#include <linux/mmc/ring_buffer.h>
+
#include <linux/mmc/slot-gpio.h>
#include "core.h"
@@ -31,6 +34,10 @@
#include "slot-gpio.h"
#include "pwrseq.h"
+#define MMC_DEVFRQ_DEFAULT_UP_THRESHOLD 35
+#define MMC_DEVFRQ_DEFAULT_DOWN_THRESHOLD 5
+#define MMC_DEVFRQ_DEFAULT_POLLING_MSEC 100
+
static DEFINE_IDA(mmc_host_ida);
static DEFINE_SPINLOCK(mmc_host_lock);
@@ -164,6 +171,7 @@ void mmc_host_clk_hold(struct mmc_host *host)
if (host->clk_gated) {
spin_unlock_irqrestore(&host->clk_lock, flags);
mmc_ungate_clock(host);
+
spin_lock_irqsave(&host->clk_lock, flags);
pr_debug("%s: ungated MCI clock\n", mmc_hostname(host));
}
@@ -176,11 +184,19 @@ void mmc_host_clk_hold(struct mmc_host *host)
* mmc_host_may_gate_card - check if this card may be gated
* @card: card to check.
*/
-static bool mmc_host_may_gate_card(struct mmc_card *card)
+bool mmc_host_may_gate_card(struct mmc_card *card)
{
/* If there is no card we may gate it */
if (!card)
return true;
+
+ /*
+ * SDIO3.0 card allows the clock to be gated off so check if
+ * that is the case or not.
+ */
+ if (mmc_card_sdio(card) && card->cccr.async_intr_sup)
+ return true;
+
/*
* Don't gate SDIO cards! These need to be clocked at all times
* since they may be independent systems generating interrupts
@@ -649,6 +665,217 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
EXPORT_SYMBOL(mmc_alloc_host);
+static ssize_t show_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_host *host = cls_dev_to_mmc_host(dev);
+
+ if (!host)
+ return -EINVAL;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", mmc_can_scale_clk(host));
+}
+
+static ssize_t store_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mmc_host *host = cls_dev_to_mmc_host(dev);
+ unsigned long value;
+
+ if (!host || !host->card || kstrtoul(buf, 0, &value))
+ return -EINVAL;
+
+ mmc_get_card(host->card);
+
+ if (!value) {
+ /*turning off clock scaling*/
+ mmc_exit_clk_scaling(host);
+ host->caps2 &= ~MMC_CAP2_CLK_SCALE;
+ host->clk_scaling.state = MMC_LOAD_HIGH;
+ /* Set to max. frequency when disabling */
+ mmc_clk_update_freq(host, host->card->clk_scaling_highest,
+ host->clk_scaling.state);
+ } else if (value) {
+ /* starting clock scaling, will restart in case started */
+ host->caps2 |= MMC_CAP2_CLK_SCALE;
+ if (host->clk_scaling.enable)
+ mmc_exit_clk_scaling(host);
+ mmc_init_clk_scaling(host);
+ }
+
+ mmc_put_card(host->card);
+
+ return count;
+}
+
+static ssize_t show_up_threshold(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_host *host = cls_dev_to_mmc_host(dev);
+
+ if (!host)
+ return -EINVAL;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", host->clk_scaling.upthreshold);
+}
+
+#define MAX_PERCENTAGE 100
+static ssize_t store_up_threshold(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mmc_host *host = cls_dev_to_mmc_host(dev);
+ unsigned long value;
+
+ if (!host || kstrtoul(buf, 0, &value) || (value > MAX_PERCENTAGE))
+ return -EINVAL;
+
+ host->clk_scaling.upthreshold = value;
+
+ pr_debug("%s: clkscale_up_thresh set to %lu\n",
+ mmc_hostname(host), value);
+ return count;
+}
+
+static ssize_t show_down_threshold(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_host *host = cls_dev_to_mmc_host(dev);
+
+ if (!host)
+ return -EINVAL;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ host->clk_scaling.downthreshold);
+}
+
+static ssize_t store_down_threshold(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mmc_host *host = cls_dev_to_mmc_host(dev);
+ unsigned long value;
+
+ if (!host || kstrtoul(buf, 0, &value) || (value > MAX_PERCENTAGE))
+ return -EINVAL;
+
+ host->clk_scaling.downthreshold = value;
+
+ pr_debug("%s: clkscale_down_thresh set to %lu\n",
+ mmc_hostname(host), value);
+ return count;
+}
+
+static ssize_t show_polling(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_host *host = cls_dev_to_mmc_host(dev);
+
+ if (!host)
+ return -EINVAL;
+
+ return snprintf(buf, PAGE_SIZE, "%lu milliseconds\n",
+ host->clk_scaling.polling_delay_ms);
+}
+
+static ssize_t store_polling(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mmc_host *host = cls_dev_to_mmc_host(dev);
+ unsigned long value;
+
+ if (!host || kstrtoul(buf, 0, &value))
+ return -EINVAL;
+
+ host->clk_scaling.polling_delay_ms = value;
+
+ pr_debug("%s: clkscale_polling_delay_ms set to %lu\n",
+ mmc_hostname(host), value);
+ return count;
+}
+
+DEVICE_ATTR(enable, S_IRUGO | S_IWUSR,
+ show_enable, store_enable);
+DEVICE_ATTR(polling_interval, S_IRUGO | S_IWUSR,
+ show_polling, store_polling);
+DEVICE_ATTR(up_threshold, S_IRUGO | S_IWUSR,
+ show_up_threshold, store_up_threshold);
+DEVICE_ATTR(down_threshold, S_IRUGO | S_IWUSR,
+ show_down_threshold, store_down_threshold);
+
+static struct attribute *clk_scaling_attrs[] = {
+ &dev_attr_enable.attr,
+ &dev_attr_up_threshold.attr,
+ &dev_attr_down_threshold.attr,
+ &dev_attr_polling_interval.attr,
+ NULL,
+};
+
+static struct attribute_group clk_scaling_attr_grp = {
+ .name = "clk_scaling",
+ .attrs = clk_scaling_attrs,
+};
+
+#ifdef CONFIG_MMC_PERF_PROFILING
+static ssize_t
+show_perf(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct mmc_host *host = cls_dev_to_mmc_host(dev);
+ int64_t rtime_drv, wtime_drv;
+ unsigned long rbytes_drv, wbytes_drv, flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ rbytes_drv = host->perf.rbytes_drv;
+ wbytes_drv = host->perf.wbytes_drv;
+
+ rtime_drv = ktime_to_us(host->perf.rtime_drv);
+ wtime_drv = ktime_to_us(host->perf.wtime_drv);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return snprintf(buf, PAGE_SIZE, "Write performance at driver Level:"
+ "%lu bytes in %lld microseconds\n"
+ "Read performance at driver Level:"
+ "%lu bytes in %lld microseconds\n",
+ wbytes_drv, wtime_drv,
+ rbytes_drv, rtime_drv);
+}
+
+static ssize_t
+set_perf(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mmc_host *host = cls_dev_to_mmc_host(dev);
+ int64_t value;
+ unsigned long flags;
+
+ sscanf(buf, "%lld", &value);
+ spin_lock_irqsave(&host->lock, flags);
+ if (!value) {
+ memset(&host->perf, 0, sizeof(host->perf));
+ host->perf_enable = false;
+ } else {
+ host->perf_enable = true;
+ }
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return count;
+}
+
+static DEVICE_ATTR(perf, S_IRUGO | S_IWUSR,
+ show_perf, set_perf);
+
+#endif
+
+static struct attribute *dev_attrs[] = {
+#ifdef CONFIG_MMC_PERF_PROFILING
+ &dev_attr_perf.attr,
+#endif
+ NULL,
+};
+static struct attribute_group dev_attr_grp = {
+ .attrs = dev_attrs,
+};
+
/**
* mmc_add_host - initialise host hardware
* @host: mmc host
@@ -670,15 +897,31 @@ int mmc_add_host(struct mmc_host *host)
led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
+ host->clk_scaling.upthreshold = MMC_DEVFRQ_DEFAULT_UP_THRESHOLD;
+ host->clk_scaling.downthreshold = MMC_DEVFRQ_DEFAULT_DOWN_THRESHOLD;
+ host->clk_scaling.polling_delay_ms = MMC_DEVFRQ_DEFAULT_POLLING_MSEC;
+ host->clk_scaling.skip_clk_scale_freq_update = false;
+
#ifdef CONFIG_DEBUG_FS
mmc_add_host_debugfs(host);
#endif
mmc_host_clk_sysfs_init(host);
+ mmc_trace_init(host);
+
+ err = sysfs_create_group(&host->class_dev.kobj, &clk_scaling_attr_grp);
+ if (err)
+ pr_err("%s: failed to create clk scale sysfs group with err %d\n",
+ __func__, err);
#ifdef CONFIG_BLOCK
mmc_latency_hist_sysfs_init(host);
#endif
+ err = sysfs_create_group(&host->class_dev.kobj, &dev_attr_grp);
+ if (err)
+ pr_err("%s: failed to create sysfs group with err %d\n",
+ __func__, err);
+
mmc_start_host(host);
if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
mmc_register_pm_notifier(host);
@@ -710,6 +953,9 @@ void mmc_remove_host(struct mmc_host *host)
mmc_latency_hist_sysfs_exit(host);
#endif
+ sysfs_remove_group(&host->parent->kobj, &dev_attr_grp);
+ sysfs_remove_group(&host->class_dev.kobj, &clk_scaling_attr_grp);
+
device_del(&host->class_dev);
led_trigger_unregister_simple(host->led);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 56e6355..a36bcbb 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -19,6 +19,8 @@
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
+#include <linux/reboot.h>
+#include <trace/events/mmc.h>
#include "core.h"
#include "host.h"
@@ -72,6 +74,7 @@ static const struct mmc_fixup mmc_ext_csd_fixups[] = {
__res & __mask; \
})
+static int mmc_switch_status(struct mmc_card *card, bool ignore_crc);
/*
* Given the decoded CSD structure, decode the raw CID to our CID structure.
*/
@@ -137,6 +140,19 @@ static void mmc_set_erase_size(struct mmc_card *card)
mmc_init_erase(card);
}
+static const struct mmc_fixup mmc_fixups[] = {
+
+ /* avoid HPI for specific cards */
+ MMC_FIXUP_EXT_CSD_REV("MMC16G", CID_MANFID_KINGSTON, CID_OEMID_ANY,
+ add_quirk, MMC_QUIRK_BROKEN_HPI, MMC_V4_41),
+
+ /* Disable cache for specific cards */
+ MMC_FIXUP("MMC16G", CID_MANFID_KINGSTON, CID_OEMID_ANY,
+ add_quirk_mmc, MMC_QUIRK_CACHE_DISABLE),
+
+ END_FIXUP
+};
+
/*
* Given a 128-bit response, decode to our card CSD structure.
*/
@@ -388,9 +404,6 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
*/
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
- /* fixup device after ext_csd revision field is updated */
- mmc_fixup_device(card, mmc_ext_csd_fixups);
-
card->ext_csd.raw_sectors[0] = ext_csd[EXT_CSD_SEC_CNT + 0];
card->ext_csd.raw_sectors[1] = ext_csd[EXT_CSD_SEC_CNT + 1];
card->ext_csd.raw_sectors[2] = ext_csd[EXT_CSD_SEC_CNT + 2];
@@ -516,6 +529,25 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
ext_csd[EXT_CSD_PWR_CL_DDR_200_360];
}
+ /* check whether the eMMC card supports HPI */
+ if ((ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) &&
+ !(card->quirks & MMC_QUIRK_BROKEN_HPI)) {
+ card->ext_csd.hpi = 1;
+ if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2)
+ card->ext_csd.hpi_cmd = MMC_STOP_TRANSMISSION;
+ else
+ card->ext_csd.hpi_cmd = MMC_SEND_STATUS;
+ /*
+ * Indicate the maximum timeout to close
+ * a command interrupted by HPI
+ */
+ card->ext_csd.out_of_int_time =
+ ext_csd[EXT_CSD_OUT_OF_INTERRUPT_TIME] * 10;
+ pr_info("%s: Out-of-interrupt timeout is %d[ms]\n",
+ mmc_hostname(card->host),
+ card->ext_csd.out_of_int_time);
+ }
+
if (card->ext_csd.rev >= 5) {
/* Adjust production date as per JEDEC JESD84-B451 */
if (card->cid.year < 2010)
@@ -523,16 +555,16 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
/* check whether the eMMC card supports BKOPS */
if (!mmc_card_broken_hpi(card) &&
- ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
+ (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) &&
+ card->ext_csd.hpi) {
card->ext_csd.bkops = 1;
- card->ext_csd.man_bkops_en =
- (ext_csd[EXT_CSD_BKOPS_EN] &
- EXT_CSD_MANUAL_BKOPS_MASK);
+ card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN];
card->ext_csd.raw_bkops_status =
ext_csd[EXT_CSD_BKOPS_STATUS];
- if (!card->ext_csd.man_bkops_en)
- pr_debug("%s: MAN_BKOPS_EN bit is not set\n",
- mmc_hostname(card->host));
+ if (!card->ext_csd.bkops_en)
+ pr_info("%s: BKOPS_EN equals 0x%x\n",
+ mmc_hostname(card->host),
+ card->ext_csd.bkops_en);
}
/* check whether the eMMC card supports HPI */
@@ -555,6 +587,19 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
card->ext_csd.rst_n_function = ext_csd[EXT_CSD_RST_N_FUNCTION];
/*
+ * Some eMMC vendors violate eMMC 5.0 spec and set
+ * REL_WR_SEC_C register to 0x10 to indicate the
+ * ability of RPMB throughput improvement thus lead
+ * to failure when TZ module write data to RPMB
+ * partition. So check bit[4] of EXT_CSD[166] and
+ * if it is not set then change value of REL_WR_SEC_C
+ * to 0x1 directly ignoring value of EXT_CSD[222].
+ */
+ if (!(card->ext_csd.rel_param &
+ EXT_CSD_WR_REL_PARAM_EN_RPMB_REL_WR))
+ card->ext_csd.rel_sectors = 0x1;
+
+ /*
* RPMB regions are defined in multiples of 128K.
*/
card->ext_csd.raw_rpmb_size_mult = ext_csd[EXT_CSD_RPMB_MULT];
@@ -610,6 +655,46 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
card->ext_csd.data_sector_size = 512;
}
+ if (card->ext_csd.rev >= 7) {
+ /* Enhance Strobe is supported since v5.1 which rev should be
+ * 8 but some eMMC devices can support it with rev 7. So handle
+ * Enhance Strobe here.
+ */
+ card->ext_csd.strobe_support = ext_csd[EXT_CSD_STROBE_SUPPORT];
+ card->ext_csd.cmdq_support = ext_csd[EXT_CSD_CMDQ_SUPPORT];
+ card->ext_csd.fw_version = ext_csd[EXT_CSD_FIRMWARE_VERSION];
+ pr_info("%s: eMMC FW version: 0x%02x\n",
+ mmc_hostname(card->host),
+ card->ext_csd.fw_version);
+ if (card->ext_csd.cmdq_support) {
+ /*
+ * Queue Depth = N + 1,
+ * see JEDEC JESD84-B51 section 7.4.19
+ */
+ card->ext_csd.cmdq_depth =
+ ext_csd[EXT_CSD_CMDQ_DEPTH] + 1;
+ pr_info("%s: CMDQ supported: depth: %d\n",
+ mmc_hostname(card->host),
+ card->ext_csd.cmdq_depth);
+ }
+ card->ext_csd.barrier_support =
+ ext_csd[EXT_CSD_BARRIER_SUPPORT];
+ card->ext_csd.cache_flush_policy =
+ ext_csd[EXT_CSD_CACHE_FLUSH_POLICY];
+ pr_info("%s: cache barrier support %d flush policy %d\n",
+ mmc_hostname(card->host),
+ card->ext_csd.barrier_support,
+ card->ext_csd.cache_flush_policy);
+ card->ext_csd.enhanced_rpmb_supported =
+ (card->ext_csd.rel_param &
+ EXT_CSD_WR_REL_PARAM_EN_RPMB_REL_WR);
+ } else {
+ card->ext_csd.cmdq_support = 0;
+ card->ext_csd.cmdq_depth = 0;
+ card->ext_csd.barrier_support = 0;
+ card->ext_csd.cache_flush_policy = 0;
+ }
+
/* eMMC v5 or later */
if (card->ext_csd.rev >= 7) {
memcpy(card->ext_csd.fwrev, &ext_csd[EXT_CSD_FIRMWARE_VERSION],
@@ -630,6 +715,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
static int mmc_read_ext_csd(struct mmc_card *card)
{
+ struct mmc_host *host = card->host;
u8 *ext_csd;
int err;
@@ -638,6 +724,9 @@ static int mmc_read_ext_csd(struct mmc_card *card)
err = mmc_get_ext_csd(card, &ext_csd);
if (err) {
+ pr_err("%s: %s: mmc_get_ext_csd() fails %d\n",
+ mmc_hostname(host), __func__, err);
+
/* If the host or the card can't do the switch,
* fail more gracefully. */
if ((err != -EINVAL)
@@ -661,6 +750,7 @@ static int mmc_read_ext_csd(struct mmc_card *card)
return err;
}
+ card->cached_ext_csd = ext_csd;
err = mmc_decode_ext_csd(card, ext_csd);
kfree(ext_csd);
return err;
@@ -762,6 +852,8 @@ MMC_DEV_ATTR(enhanced_area_offset, "%llu\n",
card->ext_csd.enhanced_area_offset);
MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
+MMC_DEV_ATTR(enhanced_rpmb_supported, "%#x\n",
+ card->ext_csd.enhanced_rpmb_supported);
MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
MMC_DEV_ATTR(ocr, "%08x\n", card->ocr);
@@ -817,6 +909,7 @@ static struct attribute *mmc_std_attrs[] = {
&dev_attr_enhanced_area_offset.attr,
&dev_attr_enhanced_area_size.attr,
&dev_attr_raw_rpmb_size_mult.attr,
+ &dev_attr_enhanced_rpmb_supported.attr,
&dev_attr_rel_sectors.attr,
&dev_attr_ocr.attr,
&dev_attr_dsr.attr,
@@ -953,11 +1046,11 @@ static void mmc_set_bus_speed(struct mmc_card *card)
*/
static int mmc_select_bus_width(struct mmc_card *card)
{
- static unsigned ext_csd_bits[] = {
+ static const unsigned ext_csd_bits[] = {
EXT_CSD_BUS_WIDTH_8,
EXT_CSD_BUS_WIDTH_4,
};
- static unsigned bus_widths[] = {
+ static const unsigned bus_widths[] = {
MMC_BUS_WIDTH_8,
MMC_BUS_WIDTH_4,
};
@@ -1018,12 +1111,12 @@ static int mmc_select_bus_width(struct mmc_card *card)
}
/* Caller must hold re-tuning */
-static int mmc_switch_status(struct mmc_card *card)
+static int mmc_switch_status(struct mmc_card *card, bool ignore_crc)
{
u32 status;
int err;
- err = mmc_send_status(card, &status);
+ err = __mmc_send_status(card, &status, ignore_crc);
if (err)
return err;
@@ -1043,7 +1136,7 @@ static int mmc_select_hs(struct mmc_card *card)
true, false, true);
if (!err) {
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
- err = mmc_switch_status(card);
+ err = mmc_switch_status(card, false);
}
if (err)
@@ -1072,10 +1165,11 @@ static int mmc_select_hs_ddr(struct mmc_card *card)
ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4;
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH,
ext_csd_bits,
- card->ext_csd.generic_cmd6_time);
+ card->ext_csd.generic_cmd6_time,
+ true, false, false);
if (err) {
pr_err("%s: switch to bus width %d ddr failed\n",
mmc_hostname(host), 1 << bus_width);
@@ -1118,8 +1212,10 @@ static int mmc_select_hs_ddr(struct mmc_card *card)
if (err)
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
- if (!err)
+ if (!err) {
mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
+ err = mmc_switch_status(card, false);
+ }
return err;
}
@@ -1134,9 +1230,28 @@ static int mmc_select_hs400(struct mmc_card *card)
/*
* HS400 mode requires 8-bit bus width
*/
- if (!(card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
- host->ios.bus_width == MMC_BUS_WIDTH_8))
- return 0;
+ if (card->ext_csd.strobe_support) {
+ if (!(card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
+ host->caps & MMC_CAP_8_BIT_DATA))
+ return 0;
+
+ /* For Enhance Strobe flow. For non Enhance Strobe, signal
+ * voltage will not be set.
+ */
+ if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V)
+ err = __mmc_set_signal_voltage(host,
+ MMC_SIGNAL_VOLTAGE_120);
+
+ if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_8V)
+ err = __mmc_set_signal_voltage(host,
+ MMC_SIGNAL_VOLTAGE_180);
+ if (err)
+ return err;
+ } else {
+ if (!(card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
+ host->ios.bus_width == MMC_BUS_WIDTH_8))
+ return 0;
+ }
/* Switch card to HS mode */
val = EXT_CSD_TIMING_HS;
@@ -1157,14 +1272,22 @@ static int mmc_select_hs400(struct mmc_card *card)
max_dtr = card->ext_csd.hs_max_dtr;
mmc_set_clock(host, max_dtr);
- err = mmc_switch_status(card);
+ err = mmc_switch_status(card, false);
if (err)
goto out_err;
+ val = EXT_CSD_DDR_BUS_WIDTH_8;
+ if (card->ext_csd.strobe_support) {
+ err = mmc_select_bus_width(card);
+ if (IS_ERR_VALUE((unsigned long)err))
+ return err;
+ val |= EXT_CSD_BUS_WIDTH_STROBE;
+ }
+
/* Switch card to DDR */
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH,
- EXT_CSD_DDR_BUS_WIDTH_8,
+ val,
card->ext_csd.generic_cmd6_time);
if (err) {
pr_err("%s: switch to bus width for hs400 failed, err:%d\n",
@@ -1189,7 +1312,28 @@ static int mmc_select_hs400(struct mmc_card *card)
mmc_set_timing(host, MMC_TIMING_MMC_HS400);
mmc_set_bus_speed(card);
- err = mmc_switch_status(card);
+ if (card->ext_csd.strobe_support && host->ops->enhanced_strobe) {
+ mmc_host_clk_hold(host);
+ err = host->ops->enhanced_strobe(host);
+ mmc_host_clk_release(host);
+ } else if ((host->caps2 & MMC_CAP2_HS400_POST_TUNING) &&
+ host->ops->execute_tuning) {
+ mmc_host_clk_hold(host);
+ err = host->ops->execute_tuning(host,
+ MMC_SEND_TUNING_BLOCK_HS200);
+ mmc_host_clk_release(host);
+
+ if (err)
+ pr_warn("%s: tuning execution failed\n",
+ mmc_hostname(host));
+ }
+
+ /*
+ * Sending of CMD13 should be done after the host calibration
+ * for enhanced_strobe or HS400 mode is completed.
+ * Otherwise may see CMD13 timeouts or CRC errors.
+ */
+ err = mmc_switch_status(card, false);
if (err)
goto out_err;
@@ -1227,7 +1371,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
- err = mmc_switch_status(card);
+ err = mmc_switch_status(card, false);
if (err)
goto out_err;
@@ -1240,7 +1384,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
mmc_set_timing(host, MMC_TIMING_MMC_HS);
- err = mmc_switch_status(card);
+ err = mmc_switch_status(card, false);
if (err)
goto out_err;
@@ -1255,7 +1399,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
mmc_set_timing(host, MMC_TIMING_MMC_HS200);
- err = mmc_switch_status(card);
+ err = mmc_switch_status(card, false);
if (err)
goto out_err;
@@ -1301,7 +1445,7 @@ static int mmc_select_hs400es(struct mmc_card *card)
mmc_set_clock(host, card->ext_csd.hs_max_dtr);
- err = mmc_switch_status(card);
+ err = mmc_switch_status(card, false);
if (err)
goto out_err;
@@ -1338,7 +1482,7 @@ static int mmc_select_hs400es(struct mmc_card *card)
if (host->ops->hs400_enhanced_strobe)
host->ops->hs400_enhanced_strobe(host, &host->ios);
- err = mmc_switch_status(card);
+ err = mmc_switch_status(card, false);
if (err)
goto out_err;
@@ -1411,7 +1555,12 @@ static int mmc_select_hs200(struct mmc_card *card)
old_timing = host->ios.timing;
mmc_set_timing(host, MMC_TIMING_MMC_HS200);
- err = mmc_switch_status(card);
+ /*
+ * Since after switching to hs200, crc errors might
+ * occur for commands send before tuning.
+ * So ignore crc error for cmd13.
+ */
+ err = mmc_switch_status(card, true);
/*
* mmc_select_timing() assumes timing has not changed if
* it is a switch error.
@@ -1431,6 +1580,17 @@ static int mmc_select_hs200(struct mmc_card *card)
return err;
}
+static int mmc_reboot_notify(struct notifier_block *notify_block,
+ unsigned long event, void *unused)
+{
+ struct mmc_card *card = container_of(
+ notify_block, struct mmc_card, reboot_notify);
+
+ card->pon_type = (event != SYS_RESTART) ? MMC_LONG_PON : MMC_SHRT_PON;
+
+ return NOTIFY_OK;
+}
+
/*
* Activate High Speed, HS200 or HS400ES mode if supported.
*/
@@ -1441,7 +1601,10 @@ static int mmc_select_timing(struct mmc_card *card)
if (!mmc_can_ext_csd(card))
goto bus_speed;
- if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES)
+ /* For Enhance Strobe HS400 flow */
+ if (card->ext_csd.strobe_support &&
+ card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
+ card->host->caps & MMC_CAP_8_BIT_DATA)
err = mmc_select_hs400es(card);
else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
err = mmc_select_hs200(card);
@@ -1474,12 +1637,242 @@ static int mmc_hs200_tuning(struct mmc_card *card)
*/
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
host->ios.bus_width == MMC_BUS_WIDTH_8)
- if (host->ops->prepare_hs400_tuning)
- host->ops->prepare_hs400_tuning(host, &host->ios);
+ mmc_set_timing(host, MMC_TIMING_MMC_HS400);
return mmc_execute_tuning(card);
}
+static int mmc_select_cmdq(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+ int ret = 0;
+
+ if (!host->cmdq_ops) {
+ pr_err("%s: host controller doesn't support CMDQ\n",
+ mmc_hostname(host));
+ return 0;
+ }
+
+ ret = mmc_set_blocklen(card, MMC_CARD_CMDQ_BLK_SIZE);
+ if (ret)
+ goto out;
+
+ ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_CMDQ, 1,
+ card->ext_csd.generic_cmd6_time);
+ if (ret)
+ goto out;
+
+ mmc_card_set_cmdq(card);
+ mmc_host_clk_hold(card->host);
+ ret = host->cmdq_ops->enable(card->host);
+ if (ret) {
+ mmc_host_clk_release(card->host);
+ pr_err("%s: failed (%d) enabling CMDQ on host\n",
+ mmc_hostname(host), ret);
+ mmc_card_clr_cmdq(card);
+ ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_CMDQ, 0,
+ card->ext_csd.generic_cmd6_time);
+ goto out;
+ }
+
+ mmc_host_clk_release(card->host);
+ pr_info_once("%s: CMDQ enabled on card\n", mmc_hostname(host));
+out:
+ return ret;
+}
+
+static int mmc_select_hs_ddr52(struct mmc_host *host)
+{
+ int err;
+
+ mmc_select_hs(host->card);
+ err = mmc_select_bus_width(host->card);
+ if (err < 0) {
+ pr_err("%s: %s: select_bus_width failed(%d)\n",
+ mmc_hostname(host), __func__, err);
+ return err;
+ }
+
+ err = mmc_select_hs_ddr(host->card);
+ mmc_set_clock(host, MMC_HIGH_52_MAX_DTR);
+
+ return err;
+}
+
+/*
+ * Scale down from HS400 to HS in order to allow frequency change.
+ * This is needed for cards that doesn't support changing frequency in HS400
+ */
+static int mmc_scale_low(struct mmc_host *host, unsigned long freq)
+{
+ int err = 0;
+
+ mmc_set_timing(host, MMC_TIMING_LEGACY);
+ mmc_set_clock(host, MMC_HIGH_26_MAX_DTR);
+
+ if (host->clk_scaling.lower_bus_speed_mode &
+ MMC_SCALING_LOWER_DDR52_MODE) {
+ err = mmc_select_hs_ddr52(host);
+ if (err)
+ pr_err("%s: %s: failed to switch to DDR52: err: %d\n",
+ mmc_hostname(host), __func__, err);
+ else
+ return err;
+ }
+
+ err = mmc_select_hs(host->card);
+ if (err) {
+ pr_err("%s: %s: scaling low: failed (%d)\n",
+ mmc_hostname(host), __func__, err);
+ return err;
+ }
+
+ err = mmc_select_bus_width(host->card);
+ if (err < 0) {
+ pr_err("%s: %s: select_bus_width failed(%d)\n",
+ mmc_hostname(host), __func__, err);
+ return err;
+ }
+
+ mmc_set_clock(host, freq);
+
+ return 0;
+}
+
+/*
+ * Scale UP from HS to HS200/H400
+ */
+static int mmc_scale_high(struct mmc_host *host)
+{
+ int err = 0;
+
+ if (mmc_card_ddr52(host->card)) {
+ mmc_set_timing(host, MMC_TIMING_LEGACY);
+ mmc_set_clock(host, MMC_HIGH_26_MAX_DTR);
+ }
+
+ if (!host->card->ext_csd.strobe_support) {
+ if (!(host->card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)) {
+ pr_err("%s: %s: card does not support HS200\n",
+ mmc_hostname(host), __func__);
+ WARN_ON(1);
+ return -EPERM;
+ }
+
+ err = mmc_select_hs200(host->card);
+ if (err) {
+ pr_err("%s: %s: selecting HS200 failed (%d)\n",
+ mmc_hostname(host), __func__, err);
+ return err;
+ }
+
+ mmc_set_bus_speed(host->card);
+
+ err = mmc_hs200_tuning(host->card);
+ if (err) {
+ pr_err("%s: %s: hs200 tuning failed (%d)\n",
+ mmc_hostname(host), __func__, err);
+ return err;
+ }
+
+ if (!(host->card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400)) {
+ pr_debug("%s: card does not support HS400\n",
+ mmc_hostname(host));
+ return 0;
+ }
+ }
+
+ err = mmc_select_hs400(host->card);
+ if (err) {
+ pr_err("%s: %s: select hs400 failed (%d)\n",
+ mmc_hostname(host), __func__, err);
+ return err;
+ }
+
+ return err;
+}
+
+static int mmc_set_clock_bus_speed(struct mmc_card *card, unsigned long freq)
+{
+ int err = 0;
+
+ if (freq == MMC_HS200_MAX_DTR)
+ err = mmc_scale_high(card->host);
+ else
+ err = mmc_scale_low(card->host, freq);
+
+ return err;
+}
+
+static inline unsigned long mmc_ddr_freq_accommodation(unsigned long freq)
+{
+ if (freq == MMC_HIGH_DDR_MAX_DTR)
+ return freq;
+
+ return freq/2;
+}
+
+/**
+ * mmc_change_bus_speed() - Change MMC card bus frequency at runtime
+ * @host: pointer to mmc host structure
+ * @freq: pointer to desired frequency to be set
+ *
+ * Change the MMC card bus frequency at runtime after the card is
+ * initialized. Callers are expected to make sure of the card's
+ * state (DATA/RCV/TRANSFER) before changing the frequency at runtime.
+ *
+ * If the frequency to change is greater than max. supported by card,
+ * *freq is changed to max. supported by card. If it is less than min.
+ * supported by host, *freq is changed to min. supported by host.
+ * Host is assumed to be calimed while calling this funciton.
+ */
+static int mmc_change_bus_speed(struct mmc_host *host, unsigned long *freq)
+{
+ int err = 0;
+ struct mmc_card *card;
+ unsigned long actual_freq;
+
+ card = host->card;
+
+ if (!card || !freq) {
+ err = -EINVAL;
+ goto out;
+ }
+ actual_freq = *freq;
+
+ WARN_ON(!host->claimed);
+
+ /*
+ * For scaling up/down HS400 we'll need special handling,
+ * for other timings we can simply do clock frequency change
+ */
+ if (mmc_card_hs400(card) ||
+ (!mmc_card_hs200(host->card) && *freq == MMC_HS200_MAX_DTR)) {
+ err = mmc_set_clock_bus_speed(card, *freq);
+ if (err) {
+ pr_err("%s: %s: failed (%d)to set bus and clock speed (freq=%lu)\n",
+ mmc_hostname(host), __func__, err, *freq);
+ goto out;
+ }
+ } else if (mmc_card_hs200(host->card)) {
+ mmc_set_clock(host, *freq);
+ err = mmc_hs200_tuning(host->card);
+ if (err) {
+ pr_warn("%s: %s: tuning execution failed %d\n",
+ mmc_hostname(card->host),
+ __func__, err);
+ mmc_set_clock(host, host->clk_scaling.curr_freq);
+ }
+ } else {
+ if (mmc_card_ddr52(host->card))
+ actual_freq = mmc_ddr_freq_accommodation(*freq);
+ mmc_set_clock(host, actual_freq);
+ }
+
+out:
+ return err;
+}
+
/*
* Handle the detection and initialisation of a card.
*
@@ -1508,20 +1901,27 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* respond.
* mmc_go_idle is needed for eMMC that are asleep
*/
+reinit:
mmc_go_idle(host);
/* The extra bit indicates that we support high capacity */
err = mmc_send_op_cond(host, ocr | (1 << 30), &rocr);
- if (err)
+ if (err) {
+ pr_err("%s: %s: mmc_send_op_cond() fails %d\n",
+ mmc_hostname(host), __func__, err);
goto err;
+ }
/*
* For SPI, enable CRC as appropriate.
*/
if (mmc_host_is_spi(host)) {
err = mmc_spi_set_crc(host, use_spi_crc);
- if (err)
+ if (err) {
+ pr_err("%s: %s: mmc_spi_set_crc() fails %d\n",
+ mmc_hostname(host), __func__, err);
goto err;
+ }
}
/*
@@ -1531,12 +1931,17 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
err = mmc_send_cid(host, cid);
else
err = mmc_all_send_cid(host, cid);
- if (err)
+ if (err) {
+ pr_err("%s: %s: mmc_send_cid() fails %d\n",
+ mmc_hostname(host), __func__, err);
goto err;
+ }
if (oldcard) {
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
err = -ENOENT;
+ pr_err("%s: %s: CID memcmp failed %d\n",
+ mmc_hostname(host), __func__, err);
goto err;
}
@@ -1548,6 +1953,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
card = mmc_alloc_card(host, &mmc_type);
if (IS_ERR(card)) {
err = PTR_ERR(card);
+ pr_err("%s: %s: no memory to allocate for card %d\n",
+ mmc_hostname(host), __func__, err);
goto err;
}
@@ -1555,6 +1962,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
card->type = MMC_TYPE_MMC;
card->rca = 1;
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+ host->card = card;
+ card->reboot_notify.notifier_call = mmc_reboot_notify;
}
/*
@@ -1568,8 +1977,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
*/
if (!mmc_host_is_spi(host)) {
err = mmc_set_relative_addr(card);
- if (err)
+ if (err) {
+ pr_err("%s: %s: mmc_set_relative_addr() fails %d\n",
+ mmc_hostname(host), __func__, err);
goto free_card;
+ }
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}
@@ -1579,15 +1991,24 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* Fetch CSD from card.
*/
err = mmc_send_csd(card, card->raw_csd);
- if (err)
+ if (err) {
+ pr_err("%s: %s: mmc_send_csd() fails %d\n",
+ mmc_hostname(host), __func__, err);
goto free_card;
+ }
err = mmc_decode_csd(card);
- if (err)
+ if (err) {
+ pr_err("%s: %s: mmc_decode_csd() fails %d\n",
+ mmc_hostname(host), __func__, err);
goto free_card;
+ }
err = mmc_decode_cid(card);
- if (err)
+ if (err) {
+ pr_err("%s: %s: mmc_decode_cid() fails %d\n",
+ mmc_hostname(host), __func__, err);
goto free_card;
+ }
}
/*
@@ -1602,15 +2023,21 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
*/
if (!mmc_host_is_spi(host)) {
err = mmc_select_card(card);
- if (err)
+ if (err) {
+ pr_err("%s: %s: mmc_select_card() fails %d\n",
+ mmc_hostname(host), __func__, err);
goto free_card;
+ }
}
if (!oldcard) {
/* Read extended CSD. */
err = mmc_read_ext_csd(card);
- if (err)
+ if (err) {
+ pr_err("%s: %s: mmc_read_ext_csd() fails %d\n",
+ mmc_hostname(host), __func__, err);
goto free_card;
+ }
/*
* If doing byte addressing, check if required to do sector
@@ -1623,6 +2050,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
/* Erase size depends on CSD and Extended CSD */
mmc_set_erase_size(card);
+
+ if (card->ext_csd.sectors && (rocr & MMC_CARD_SECTOR_ADDR))
+ mmc_card_set_blockaddr(card);
}
/*
@@ -1635,8 +2065,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
EXT_CSD_ERASE_GROUP_DEF, 1,
card->ext_csd.generic_cmd6_time);
- if (err && err != -EBADMSG)
+ if (err && err != -EBADMSG) {
+ pr_err("%s: %s: mmc_switch() for ERASE_GRP_DEF fails %d\n",
+ mmc_hostname(host), __func__, err);
goto free_card;
+ }
if (err) {
err = 0;
@@ -1666,8 +2099,13 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONFIG,
card->ext_csd.part_config,
card->ext_csd.part_time);
- if (err && err != -EBADMSG)
+ if (err && err != -EBADMSG) {
+ pr_err("%s: %s: mmc_switch() for PART_CONFIG fails %d\n",
+ mmc_hostname(host), __func__, err);
goto free_card;
+ }
+ card->part_curr = card->ext_csd.part_config &
+ EXT_CSD_PART_CONFIG_ACC_MASK;
}
/*
@@ -1678,8 +2116,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
EXT_CSD_POWER_OFF_NOTIFICATION,
EXT_CSD_POWER_ON,
card->ext_csd.generic_cmd6_time);
- if (err && err != -EBADMSG)
+ if (err && err != -EBADMSG) {
+ pr_err("%s: %s: mmc_switch() for POWER_ON PON fails %d\n",
+ mmc_hostname(host), __func__, err);
goto free_card;
+ }
/*
* The err can be -EBADMSG or 0,
@@ -1693,8 +2134,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* Select timing interface
*/
err = mmc_select_timing(card);
- if (err)
+ if (err) {
+ pr_err("%s: %s: mmc_select_timing() fails %d\n",
+ mmc_hostname(host), __func__, err);
goto free_card;
+ }
if (mmc_card_hs200(card)) {
err = mmc_hs200_tuning(card);
@@ -1714,6 +2158,16 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
}
+ card->clk_scaling_lowest = host->f_min;
+ if ((card->mmc_avail_type | EXT_CSD_CARD_TYPE_HS400) ||
+ (card->mmc_avail_type | EXT_CSD_CARD_TYPE_HS200))
+ card->clk_scaling_highest = card->ext_csd.hs200_max_dtr;
+ else if ((card->mmc_avail_type | EXT_CSD_CARD_TYPE_HS) ||
+ (card->mmc_avail_type | EXT_CSD_CARD_TYPE_DDR_52))
+ card->clk_scaling_highest = card->ext_csd.hs_max_dtr;
+ else
+ card->clk_scaling_highest = card->csd.max_dtr;
+
/*
* Choose the power class with selected bus interface
*/
@@ -1726,8 +2180,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HPI_MGMT, 1,
card->ext_csd.generic_cmd6_time);
- if (err && err != -EBADMSG)
+ if (err && err != -EBADMSG) {
+ pr_err("%s: %s: mmc_switch() for HPI_MGMT fails %d\n",
+ mmc_hostname(host), __func__, err);
goto free_card;
+ }
if (err) {
pr_warn("%s: Enabling HPI failed\n",
mmc_hostname(card->host));
@@ -1739,28 +2196,72 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
/*
* If cache size is higher than 0, this indicates
* the existence of cache and it can be turned on.
+ * If HPI is not supported then cache shouldn't be enabled.
*/
if (!mmc_card_broken_hpi(card) &&
card->ext_csd.cache_size > 0) {
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_CACHE_CTRL, 1,
- card->ext_csd.generic_cmd6_time);
- if (err && err != -EBADMSG)
- goto free_card;
+ if (card->ext_csd.hpi_en &&
+ (!(card->quirks & MMC_QUIRK_CACHE_DISABLE))) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_CACHE_CTRL, 1,
+ card->ext_csd.generic_cmd6_time);
+ if (err && err != -EBADMSG) {
+ pr_err("%s: %s: fail on CACHE_CTRL ON %d\n",
+ mmc_hostname(host), __func__, err);
+ goto free_card;
+ }
- /*
- * Only if no error, cache is turned on successfully.
- */
- if (err) {
- pr_warn("%s: Cache is supported, but failed to turn on (%d)\n",
- mmc_hostname(card->host), err);
- card->ext_csd.cache_ctrl = 0;
- err = 0;
+ /*
+ * Only if no error, cache is turned on successfully.
+ */
+ if (err) {
+ pr_warn("%s: Cache is supported, but failed to turn on (%d)\n",
+ mmc_hostname(card->host), err);
+ card->ext_csd.cache_ctrl = 0;
+ err = 0;
+ } else {
+ card->ext_csd.cache_ctrl = 1;
+ }
+ /* enable cache barrier if supported by the device */
+ if (card->ext_csd.cache_ctrl &&
+ card->ext_csd.barrier_support) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BARRIER_CTRL, 1,
+ card->ext_csd.generic_cmd6_time);
+ if (err && err != -EBADMSG) {
+ pr_err("%s: %s: mmc_switch() for BARRIER_CTRL fails %d\n",
+ mmc_hostname(host), __func__,
+ err);
+ goto free_card;
+ }
+ if (err) {
+ pr_warn("%s: Barrier is supported but failed to turn on (%d)\n",
+ mmc_hostname(card->host), err);
+ card->ext_csd.barrier_en = 0;
+ err = 0;
+ } else {
+ card->ext_csd.barrier_en = 1;
+ }
+ }
} else {
- card->ext_csd.cache_ctrl = 1;
+ /*
+ * mmc standard doesn't say what is the card default
+ * value for EXT_CSD_CACHE_CTRL.
+ * Hence, cache may be enabled by default by
+ * card vendors.
+ * Thus, it is best to explicitly disable cache in case
+ * we want to avoid cache.
+ */
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_CACHE_CTRL, 0,
+ card->ext_csd.generic_cmd6_time);
+ if (err) {
+ pr_err("%s: %s: fail on CACHE_CTRL OFF %d\n",
+ mmc_hostname(host), __func__, err);
+ goto free_card;
+ }
}
}
-
/*
* The mandatory minimum values are defined for packed command.
* read: 5, write: 3
@@ -1772,8 +2273,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
EXT_CSD_EXP_EVENTS_CTRL,
EXT_CSD_PACKED_EVENT_EN,
card->ext_csd.generic_cmd6_time);
- if (err && err != -EBADMSG)
+ if (err && err != -EBADMSG) {
+ pr_err("%s: %s: mmc_switch() for EXP_EVENTS_CTRL fails %d\n",
+ mmc_hostname(host), __func__, err);
goto free_card;
+ }
if (err) {
pr_warn("%s: Enabling packed event failed\n",
mmc_hostname(card->host));
@@ -1782,42 +2286,125 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
} else {
card->ext_csd.packed_event_en = 1;
}
+
}
- if (!oldcard)
- host->card = card;
+ if (!oldcard) {
+ if ((host->caps2 & MMC_CAP2_PACKED_CMD) &&
+ (card->ext_csd.max_packed_writes > 0)) {
+ /*
+ * We would like to keep the statistics in an index
+ * that equals the num of packed requests
+ * (1 to max_packed_writes)
+ */
+ card->wr_pack_stats.packing_events = kzalloc(
+ (card->ext_csd.max_packed_writes + 1) *
+ sizeof(*card->wr_pack_stats.packing_events),
+ GFP_KERNEL);
+ if (!card->wr_pack_stats.packing_events) {
+ pr_err("%s: %s: no memory for packing events\n",
+ mmc_hostname(host), __func__);
+ goto free_card;
+ }
+ }
+ }
+
+ /*
+ * Start auto bkops, if supported.
+ *
+ * Note: This leaves the possibility of having both manual and
+ * auto bkops running in parallel. The runtime implementation
+ * will allow this, but ignore bkops exceptions on the premises
+ * that auto bkops will eventually kick in and the device will
+ * handle bkops without START_BKOPS from the host.
+ */
+ if (mmc_card_support_auto_bkops(card)) {
+ /*
+ * Ignore the return value of setting auto bkops.
+ * If it failed, will run in backward compatible mode.
+ */
+ (void)mmc_set_auto_bkops(card, true);
+ }
+
+ if (card->ext_csd.cmdq_support && (card->host->caps2 &
+ MMC_CAP2_CMD_QUEUE)) {
+ err = mmc_select_cmdq(card);
+ if (err) {
+ pr_err("%s: selecting CMDQ mode: failed: %d\n",
+ mmc_hostname(card->host), err);
+ card->ext_csd.cmdq_support = 0;
+ oldcard = card;
+ goto reinit;
+ }
+ }
return 0;
free_card:
- if (!oldcard)
+ if (!oldcard) {
+ host->card = NULL;
mmc_remove_card(card);
+ }
err:
return err;
}
-static int mmc_can_sleep(struct mmc_card *card)
+static int mmc_can_sleepawake(struct mmc_host *host)
{
- return (card && card->ext_csd.rev >= 3);
+ return host && (host->caps2 & MMC_CAP2_SLEEP_AWAKE) && host->card &&
+ (host->card->ext_csd.rev >= 3);
}
-static int mmc_sleep(struct mmc_host *host)
+static int mmc_sleepawake(struct mmc_host *host, bool sleep)
{
struct mmc_command cmd = {0};
struct mmc_card *card = host->card;
- unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);
+ unsigned int timeout_ms;
int err;
+ if (!card) {
+ pr_err("%s: %s: invalid card\n", mmc_hostname(host), __func__);
+ return -EINVAL;
+ }
+
+ timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);
+ if (card->ext_csd.rev >= 3 &&
+ card->part_curr == EXT_CSD_PART_CONFIG_ACC_RPMB) {
+ u8 part_config = card->ext_csd.part_config;
+
+ /*
+ * If the last access before suspend is RPMB access, then
+ * switch to default part config so that sleep command CMD5
+ * and deselect CMD7 can be sent to the card.
+ */
+ part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_PART_CONFIG,
+ part_config,
+ card->ext_csd.part_time);
+ if (err) {
+ pr_err("%s: %s: failed to switch to default part config %x\n",
+ mmc_hostname(host), __func__, part_config);
+ return err;
+ }
+ card->ext_csd.part_config = part_config;
+ card->part_curr = card->ext_csd.part_config &
+ EXT_CSD_PART_CONFIG_ACC_MASK;
+ }
+
/* Re-tuning can't be done once the card is deselected */
mmc_retune_hold(host);
- err = mmc_deselect_cards(host);
- if (err)
- goto out_release;
+ if (sleep) {
+ err = mmc_deselect_cards(host);
+ if (err)
+ goto out_release;
+ }
cmd.opcode = MMC_SLEEP_AWAKE;
cmd.arg = card->rca << 16;
- cmd.arg |= 1 << 15;
+ if (sleep)
+ cmd.arg |= 1 << 15;
/*
* If the max_busy_timeout of the host is specified, validate it against
@@ -1845,6 +2432,9 @@ static int mmc_sleep(struct mmc_host *host)
if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
mmc_delay(timeout_ms);
+ if (!sleep)
+ err = mmc_select_card(card);
+
out_release:
mmc_retune_release(host);
return err;
@@ -1879,6 +2469,27 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
return err;
}
+int mmc_send_pon(struct mmc_card *card)
+{
+ int err = 0;
+ struct mmc_host *host = card->host;
+
+ if (!mmc_can_poweroff_notify(card))
+ goto out;
+
+ mmc_get_card(card);
+ if (card->pon_type & MMC_LONG_PON)
+ err = mmc_poweroff_notify(host->card, EXT_CSD_POWER_OFF_LONG);
+ else if (card->pon_type & MMC_SHRT_PON)
+ err = mmc_poweroff_notify(host->card, EXT_CSD_POWER_OFF_SHORT);
+ if (err)
+ pr_warn("%s: error %d sending PON type %u",
+ mmc_hostname(host), err, card->pon_type);
+ mmc_put_card(card);
+out:
+ return err;
+}
+
/*
* Host is being removed. Free up the current card.
*/
@@ -1887,8 +2498,14 @@ static void mmc_remove(struct mmc_host *host)
BUG_ON(!host);
BUG_ON(!host->card);
+ unregister_reboot_notifier(&host->card->reboot_notify);
+
+ mmc_exit_clk_scaling(host);
mmc_remove_card(host->card);
+
+ mmc_claim_host(host);
host->card = NULL;
+ mmc_release_host(host);
}
/*
@@ -1928,20 +2545,101 @@ static void mmc_detect(struct mmc_host *host)
}
}
+static int mmc_cache_card_ext_csd(struct mmc_host *host)
+{
+ int err;
+ u8 *ext_csd;
+ struct mmc_card *card = host->card;
+
+ err = mmc_get_ext_csd(card, &ext_csd);
+ if (err || !ext_csd) {
+ pr_err("%s: %s: mmc_get_ext_csd failed (%d)\n",
+ mmc_hostname(host), __func__, err);
+ return err;
+ }
+
+ /* only cache read/write fields that the sw changes */
+ card->ext_csd.raw_ext_csd_cmdq = ext_csd[EXT_CSD_CMDQ];
+ card->ext_csd.raw_ext_csd_cache_ctrl = ext_csd[EXT_CSD_CACHE_CTRL];
+ card->ext_csd.raw_ext_csd_bus_width = ext_csd[EXT_CSD_BUS_WIDTH];
+ card->ext_csd.raw_ext_csd_hs_timing = ext_csd[EXT_CSD_HS_TIMING];
+
+ kfree(ext_csd);
+
+ return 0;
+}
+
+static int mmc_test_awake_ext_csd(struct mmc_host *host)
+{
+ int err;
+ u8 *ext_csd;
+ struct mmc_card *card = host->card;
+
+ err = mmc_get_ext_csd(card, &ext_csd);
+ if (err || !ext_csd) {
+ pr_err("%s: %s: mmc_get_ext_csd failed (%d)\n",
+ mmc_hostname(host), __func__, err);
+ return err;
+ }
+
+ /* only compare read/write fields that the sw changes */
+ pr_debug("%s: %s: type(cached:current) cmdq(%d:%d) cache_ctrl(%d:%d) bus_width (%d:%d) timing(%d:%d)\n",
+ mmc_hostname(host), __func__,
+ card->ext_csd.raw_ext_csd_cmdq,
+ ext_csd[EXT_CSD_CMDQ],
+ card->ext_csd.raw_ext_csd_cache_ctrl,
+ ext_csd[EXT_CSD_CACHE_CTRL],
+ card->ext_csd.raw_ext_csd_bus_width,
+ ext_csd[EXT_CSD_BUS_WIDTH],
+ card->ext_csd.raw_ext_csd_hs_timing,
+ ext_csd[EXT_CSD_HS_TIMING]);
+
+ err = !((card->ext_csd.raw_ext_csd_cmdq ==
+ ext_csd[EXT_CSD_CMDQ]) &&
+ (card->ext_csd.raw_ext_csd_cache_ctrl ==
+ ext_csd[EXT_CSD_CACHE_CTRL]) &&
+ (card->ext_csd.raw_ext_csd_bus_width ==
+ ext_csd[EXT_CSD_BUS_WIDTH]) &&
+ (card->ext_csd.raw_ext_csd_hs_timing ==
+ ext_csd[EXT_CSD_HS_TIMING]));
+
+ kfree(ext_csd);
+
+ return err;
+}
+
static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
{
int err = 0;
- unsigned int notify_type = is_suspend ? EXT_CSD_POWER_OFF_SHORT :
- EXT_CSD_POWER_OFF_LONG;
BUG_ON(!host);
BUG_ON(!host->card);
+ err = mmc_suspend_clk_scaling(host);
+ if (err) {
+ pr_err("%s: %s: fail to suspend clock scaling (%d)\n",
+ mmc_hostname(host), __func__, err);
+ return err;
+ }
+
mmc_claim_host(host);
if (mmc_card_suspended(host->card))
goto out;
+ if (host->card->cmdq_init) {
+ BUG_ON(host->cmdq_ctx.active_reqs);
+
+ err = mmc_cmdq_halt(host, true);
+ if (err) {
+ pr_err("%s: halt: failed: %d\n", __func__, err);
+ goto out;
+ }
+ mmc_host_clk_hold(host);
+ host->cmdq_ops->disable(host, true);
+ mmc_host_clk_release(host);
+ }
+
if (mmc_card_doing_bkops(host->card)) {
err = mmc_stop_bkops(host->card);
if (err)
@@ -1952,36 +2650,117 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
if (err)
goto out;
- if (mmc_can_poweroff_notify(host->card) &&
- ((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend))
- err = mmc_poweroff_notify(host->card, notify_type);
- else if (mmc_can_sleep(host->card))
- err = mmc_sleep(host);
- else if (!mmc_host_is_spi(host))
+ if (mmc_can_sleepawake(host)) {
+ /*
+ * For caching host->ios to cached_ios we need to
+ * make sure that clocks are not gated otherwise
+ * cached_ios->clock will be 0.
+ */
+ mmc_host_clk_hold(host);
+ memcpy(&host->cached_ios, &host->ios, sizeof(host->cached_ios));
+ mmc_cache_card_ext_csd(host);
+ err = mmc_sleepawake(host, true);
+ mmc_host_clk_release(host);
+ } else if (!mmc_host_is_spi(host)) {
err = mmc_deselect_cards(host);
+ }
if (!err) {
mmc_power_off(host);
mmc_card_set_suspended(host->card);
}
out:
+ /* Kick CMDQ thread to process any requests came in while suspending */
+ if (host->card->cmdq_init)
+ wake_up(&host->cmdq_ctx.wait);
+
mmc_release_host(host);
return err;
}
+static int mmc_partial_init(struct mmc_host *host)
+{
+ int err = 0;
+ struct mmc_card *card = host->card;
+
+ pr_debug("%s: %s: starting partial init\n",
+ mmc_hostname(host), __func__);
+
+ mmc_set_bus_width(host, host->cached_ios.bus_width);
+ mmc_set_timing(host, host->cached_ios.timing);
+ mmc_set_clock(host, host->cached_ios.clock);
+ mmc_set_bus_mode(host, host->cached_ios.bus_mode);
+
+ mmc_host_clk_hold(host);
+
+ if (mmc_card_hs400(card)) {
+ if (card->ext_csd.strobe_support && host->ops->enhanced_strobe)
+ err = host->ops->enhanced_strobe(host);
+ } else if (mmc_card_hs200(card) && host->ops->execute_tuning) {
+ err = host->ops->execute_tuning(host,
+ MMC_SEND_TUNING_BLOCK_HS200);
+ if (err)
+ pr_warn("%s: %s: tuning execution failed (%d)\n",
+ mmc_hostname(host), __func__, err);
+ }
+
+ /*
+ * The ext_csd is read to make sure the card did not went through
+ * Power-failure during sleep period.
+ * A subset of the W/E_P, W/C_P register will be tested. In case
+ * these registers values are different from the values that were
+ * cached during suspend, we will conclude that a Power-failure occurred
+ * and will do full initialization sequence.
+ * In addition, full init sequence also transfer ext_csd before moving
+ * to CMDQ mode which has a side affect of configuring SDHCI registers
+ * which needed to be done before moving to CMDQ mode. The same
+ * registers need to be configured for partial init.
+ */
+ err = mmc_test_awake_ext_csd(host);
+ if (err) {
+ pr_debug("%s: %s: fail on ext_csd read (%d)\n",
+ mmc_hostname(host), __func__, err);
+ goto out;
+ }
+ pr_debug("%s: %s: reading and comparing ext_csd successful\n",
+ mmc_hostname(host), __func__);
+
+ if (card->ext_csd.cmdq_support && (card->host->caps2 &
+ MMC_CAP2_CMD_QUEUE)) {
+ err = mmc_select_cmdq(card);
+ if (err) {
+ pr_warn("%s: %s: enabling CMDQ mode failed (%d)\n",
+ mmc_hostname(card->host),
+ __func__, err);
+ }
+ }
+out:
+ mmc_host_clk_release(host);
+
+ pr_debug("%s: %s: done partial init (%d)\n",
+ mmc_hostname(host), __func__, err);
+
+ return err;
+}
+
/*
* Suspend callback
*/
static int mmc_suspend(struct mmc_host *host)
{
int err;
+ ktime_t start = ktime_get();
+ MMC_TRACE(host, "%s: Enter\n", __func__);
err = _mmc_suspend(host, true);
if (!err) {
pm_runtime_disable(&host->card->dev);
pm_runtime_set_suspended(&host->card->dev);
}
+ trace_mmc_suspend(mmc_hostname(host), err,
+ ktime_to_us(ktime_sub(ktime_get(), start)));
+ MMC_TRACE(host, "%s: Exit err: %d\n", __func__, err);
return err;
}
@@ -1991,43 +2770,61 @@ static int mmc_suspend(struct mmc_host *host)
*/
static int _mmc_resume(struct mmc_host *host)
{
- int err = 0;
+ int err = -ENOSYS;
+ int retries;
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
- if (!mmc_card_suspended(host->card))
+ if (!mmc_card_suspended(host->card)) {
+ mmc_release_host(host);
goto out;
+ }
mmc_power_up(host, host->card->ocr);
- err = mmc_init_card(host, host->card->ocr, host->card);
+ retries = 3;
+ while (retries) {
+ if (mmc_can_sleepawake(host)) {
+ err = mmc_sleepawake(host, false);
+ if (!err)
+ err = mmc_partial_init(host);
+ if (err)
+ pr_err("%s: %s: awake failed (%d), fallback to full init\n",
+ mmc_hostname(host), __func__, err);
+ }
+
+ if (err)
+ err = mmc_init_card(host, host->card->ocr, host->card);
+
+ if (err) {
+ pr_err("%s: MMC card re-init failed rc = %d (retries = %d)\n",
+ mmc_hostname(host), err, retries);
+ retries--;
+ mmc_power_off(host);
+ usleep_range(5000, 5500);
+ mmc_power_up(host, host->card->ocr);
+ mmc_select_voltage(host, host->card->ocr);
+ continue;
+ }
+ break;
+ }
+ if (!err && mmc_card_cmdq(host->card)) {
+ err = mmc_cmdq_halt(host, false);
+ if (err)
+ pr_err("%s: un-halt: failed: %d\n", __func__, err);
+ }
mmc_card_clr_suspended(host->card);
-out:
mmc_release_host(host);
- return err;
-}
-/*
- * Shutdown callback
- */
-static int mmc_shutdown(struct mmc_host *host)
-{
- int err = 0;
+ err = mmc_resume_clk_scaling(host);
+ if (err)
+ pr_err("%s: %s: fail to resume clock scaling (%d)\n",
+ mmc_hostname(host), __func__, err);
- /*
- * In a specific case for poweroff notify, we need to resume the card
- * before we can shutdown it properly.
- */
- if (mmc_can_poweroff_notify(host->card) &&
- !(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE))
- err = _mmc_resume(host);
-
- if (!err)
- err = _mmc_suspend(host, false);
-
+out:
return err;
}
@@ -2036,25 +2833,97 @@ static int mmc_shutdown(struct mmc_host *host)
*/
static int mmc_resume(struct mmc_host *host)
{
+ int err = 0;
+
+ MMC_TRACE(host, "%s: Enter\n", __func__);
pm_runtime_enable(&host->card->dev);
+
+ MMC_TRACE(host, "%s: Exit err: %d\n", __func__, err);
return 0;
}
+#define MAX_DEFER_SUSPEND_COUNTER 20
+static bool mmc_process_bkops(struct mmc_host *host)
+{
+ int err = 0;
+ bool is_running = false;
+ u32 status;
+
+ mmc_claim_host(host);
+ if (mmc_card_cmdq(host->card)) {
+ BUG_ON(host->cmdq_ctx.active_reqs);
+
+ err = mmc_cmdq_halt(host, true);
+ if (err) {
+ pr_err("%s: halt: failed: %d\n", __func__, err);
+ goto unhalt;
+ }
+ }
+
+ if (mmc_card_doing_bkops(host->card)) {
+ /* check that manual bkops finished */
+ err = mmc_send_status(host->card, &status);
+ if (err) {
+ pr_err("%s: Get card status fail\n", __func__);
+ goto unhalt;
+ }
+ if (R1_CURRENT_STATE(status) != R1_STATE_PRG) {
+ mmc_card_clr_doing_bkops(host->card);
+ goto unhalt;
+ }
+ } else {
+ mmc_check_bkops(host->card);
+ }
+
+ if (host->card->bkops.needs_bkops &&
+ !mmc_card_support_auto_bkops(host->card))
+ mmc_start_manual_bkops(host->card);
+
+unhalt:
+ if (mmc_card_cmdq(host->card)) {
+ err = mmc_cmdq_halt(host, false);
+ if (err)
+ pr_err("%s: unhalt: failed: %d\n", __func__, err);
+ }
+ mmc_release_host(host);
+
+ if (host->card->bkops.needs_bkops ||
+ mmc_card_doing_bkops(host->card)) {
+ if (host->card->bkops.retry_counter++ <
+ MAX_DEFER_SUSPEND_COUNTER) {
+ host->card->bkops.needs_check = true;
+ is_running = true;
+ } else {
+ host->card->bkops.retry_counter = 0;
+ }
+ }
+ return is_running;
+}
+
/*
* Callback for runtime_suspend.
*/
static int mmc_runtime_suspend(struct mmc_host *host)
{
int err;
+ ktime_t start = ktime_get();
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
return 0;
+ if (mmc_process_bkops(host)) {
+ pm_runtime_mark_last_busy(&host->card->dev);
+ pr_debug("%s: defered, need bkops\n", __func__);
+ return -EBUSY;
+ }
+
err = _mmc_suspend(host, true);
if (err)
pr_err("%s: error %d doing aggressive suspend\n",
mmc_hostname(host), err);
+ trace_mmc_runtime_suspend(mmc_hostname(host), err,
+ ktime_to_us(ktime_sub(ktime_get(), start)));
return err;
}
@@ -2064,13 +2933,17 @@ static int mmc_runtime_suspend(struct mmc_host *host)
static int mmc_runtime_resume(struct mmc_host *host)
{
int err;
+ ktime_t start = ktime_get();
err = _mmc_resume(host);
if (err && err != -ENOMEDIUM)
pr_err("%s: error %d doing runtime resume\n",
mmc_hostname(host), err);
- return 0;
+ trace_mmc_runtime_resume(mmc_hostname(host), err,
+ ktime_to_us(ktime_sub(ktime_get(), start)));
+
+ return err;
}
int mmc_can_reset(struct mmc_card *card)
@@ -2118,7 +2991,7 @@ static const struct mmc_bus_ops mmc_ops = {
.runtime_suspend = mmc_runtime_suspend,
.runtime_resume = mmc_runtime_resume,
.alive = mmc_alive,
- .shutdown = mmc_shutdown,
+ .change_bus_speed = mmc_change_bus_speed,
.reset = mmc_reset,
};
@@ -2177,6 +3050,14 @@ int mmc_attach_mmc(struct mmc_host *host)
goto remove_card;
mmc_claim_host(host);
+ err = mmc_init_clk_scaling(host);
+ if (err) {
+ mmc_release_host(host);
+ goto remove_card;
+ }
+
+ register_reboot_notifier(&host->card->reboot_notify);
+
return 0;
remove_card:
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index ad6e979..16f7c58 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -54,7 +54,7 @@ static const u8 tuning_blk_pattern_8bit[] = {
0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
};
-static inline int __mmc_send_status(struct mmc_card *card, u32 *status,
+int __mmc_send_status(struct mmc_card *card, u32 *status,
bool ignore_crc)
{
int err;
@@ -456,6 +456,45 @@ int mmc_switch_status_error(struct mmc_host *host, u32 status)
}
/**
+ * mmc_prepare_switch - helper; prepare to modify EXT_CSD register
+ * @card: the MMC card associated with the data transfer
+ * @set: cmd set values
+ * @index: EXT_CSD register index
+ * @value: value to program into EXT_CSD register
+ * @tout_ms: timeout (ms) for operation performed by register write,
+ * timeout of zero implies maximum possible timeout
+ * @use_busy_signal: use the busy signal as response type
+ *
+ * Helper to prepare to modify EXT_CSD register for selected card.
+ */
+
+static inline void mmc_prepare_switch(struct mmc_command *cmd, u8 index,
+ u8 value, u8 set, unsigned int tout_ms,
+ bool use_busy_signal)
+{
+ cmd->opcode = MMC_SWITCH;
+ cmd->arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+ (index << 16) |
+ (value << 8) |
+ set;
+ cmd->flags = MMC_CMD_AC;
+ cmd->busy_timeout = tout_ms;
+ if (use_busy_signal)
+ cmd->flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
+ else
+ cmd->flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1;
+}
+
+int __mmc_switch_cmdq_mode(struct mmc_command *cmd, u8 set, u8 index, u8 value,
+ unsigned int timeout_ms, bool use_busy_signal,
+ bool ignore_timeout)
+{
+ mmc_prepare_switch(cmd, index, value, set, timeout_ms, use_busy_signal);
+ return 0;
+}
+EXPORT_SYMBOL(__mmc_switch_cmdq_mode);
+
+/**
* __mmc_switch - modify EXT_CSD register
* @card: the MMC card associated with the data transfer
* @set: cmd set values
@@ -479,6 +518,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned long timeout;
u32 status = 0;
bool use_r1b_resp = use_busy_signal;
+ int retries = 5;
bool expired = false;
bool busy = false;
@@ -494,12 +534,8 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
(timeout_ms > host->max_busy_timeout))
use_r1b_resp = false;
- cmd.opcode = MMC_SWITCH;
- cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
- (index << 16) |
- (value << 8) |
- set;
- cmd.flags = MMC_CMD_AC;
+ mmc_prepare_switch(&cmd, index, value, set, timeout_ms,
+ use_r1b_resp);
if (use_r1b_resp) {
cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
/*
@@ -513,6 +549,8 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
if (index == EXT_CSD_SANITIZE_START)
cmd.sanitize_busy = true;
+ else if (index == EXT_CSD_BKOPS_START)
+ cmd.bkops_busy = true;
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err)
@@ -570,10 +608,17 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
/* Timeout if the device never leaves the program state. */
if (expired &&
(R1_CURRENT_STATE(status) == R1_STATE_PRG || busy)) {
- pr_err("%s: Card stuck in programming state! %s\n",
- mmc_hostname(host), __func__);
- err = -ETIMEDOUT;
- goto out;
+ pr_err("%s: Card stuck in programming state! %s, timeout:%ums, retries:%d\n",
+ mmc_hostname(host), __func__,
+ timeout_ms, retries);
+ if (retries)
+ timeout = jiffies +
+ msecs_to_jiffies(timeout_ms);
+ else {
+ err = -ETIMEDOUT;
+ goto out;
+ }
+ retries--;
}
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG || busy);
@@ -717,7 +762,10 @@ mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
data.sg = &sg;
data.sg_len = 1;
+ data.timeout_ns = 1000000;
+ data.timeout_clks = 0;
mmc_set_data_timeout(&data, card);
+
sg_init_one(&sg, data_buf, len);
mmc_wait_for_req(host, &mrq);
err = 0;
@@ -765,7 +813,7 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
unsigned int opcode;
int err;
- if (!card->ext_csd.hpi) {
+ if (!card->ext_csd.hpi_en) {
pr_warn("%s: Card didn't support HPI command\n",
mmc_hostname(card->host));
return -EINVAL;
@@ -782,7 +830,7 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err) {
- pr_warn("%s: error %d interrupting operation. "
+ pr_debug("%s: error %d interrupting operation. "
"HPI command response %#x\n", mmc_hostname(card->host),
err, cmd.resp[0]);
return err;
@@ -797,3 +845,21 @@ int mmc_can_ext_csd(struct mmc_card *card)
{
return (card && card->csd.mmca_vsn > CSD_SPEC_VER_3);
}
+
+int mmc_discard_queue(struct mmc_host *host, u32 tasks)
+{
+ struct mmc_command cmd = {0};
+
+ cmd.opcode = MMC_CMDQ_TASK_MGMT;
+ if (tasks) {
+ cmd.arg = DISCARD_TASK;
+ cmd.arg |= (tasks << 16);
+ } else {
+ cmd.arg = DISCARD_QUEUE;
+ }
+
+ cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+
+ return mmc_wait_for_cmd(host, &cmd, 0);
+}
+EXPORT_SYMBOL(mmc_discard_queue);
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index f1b8e81..ad1058c 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -27,10 +27,12 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
int mmc_can_ext_csd(struct mmc_card *card);
+int mmc_discard_queue(struct mmc_host *host, u32 tasks);
int mmc_switch_status_error(struct mmc_host *host, u32 status);
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms, bool use_busy_signal, bool send_status,
bool ignore_crc);
-
+int __mmc_send_status(struct mmc_card *card, u32 *status,
+ bool ignore_crc);
#endif
diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c
index 4e65ea5..ba44cfd 100644
--- a/drivers/mmc/core/quirks.c
+++ b/drivers/mmc/core/quirks.c
@@ -35,6 +35,51 @@
#define SDIO_DEVICE_ID_MARVELL_8797_F0 0x9128
#endif
+#ifndef SDIO_VENDOR_ID_MSM
+#define SDIO_VENDOR_ID_MSM 0x0070
+#endif
+
+#ifndef SDIO_DEVICE_ID_MSM_WCN1314
+#define SDIO_DEVICE_ID_MSM_WCN1314 0x2881
+#endif
+
+#ifndef SDIO_VENDOR_ID_MSM_QCA
+#define SDIO_VENDOR_ID_MSM_QCA 0x271
+#endif
+
+#ifndef SDIO_DEVICE_ID_MSM_QCA_AR6003_1
+#define SDIO_DEVICE_ID_MSM_QCA_AR6003_1 0x300
+#endif
+
+#ifndef SDIO_DEVICE_ID_MSM_QCA_AR6003_2
+#define SDIO_DEVICE_ID_MSM_QCA_AR6003_2 0x301
+#endif
+
+#ifndef SDIO_DEVICE_ID_MSM_QCA_AR6004_1
+#define SDIO_DEVICE_ID_MSM_QCA_AR6004_1 0x400
+#endif
+
+#ifndef SDIO_DEVICE_ID_MSM_QCA_AR6004_2
+#define SDIO_DEVICE_ID_MSM_QCA_AR6004_2 0x401
+#endif
+
+#ifndef SDIO_VENDOR_ID_QCA6574
+#define SDIO_VENDOR_ID_QCA6574 0x271
+#endif
+
+#ifndef SDIO_DEVICE_ID_QCA6574
+#define SDIO_DEVICE_ID_QCA6574 0x50a
+#endif
+
+#ifndef SDIO_VENDOR_ID_QCA9377
+#define SDIO_VENDOR_ID_QCA9377 0x271
+#endif
+
+#ifndef SDIO_DEVICE_ID_QCA9377
+#define SDIO_DEVICE_ID_QCA9377 0x701
+#endif
+
+
/*
* This hook just adds a quirk for all sdio devices
*/
@@ -54,6 +99,21 @@ static const struct mmc_fixup mmc_fixup_methods[] = {
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+ SDIO_FIXUP(SDIO_VENDOR_ID_MSM, SDIO_DEVICE_ID_MSM_WCN1314,
+ remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+
+ SDIO_FIXUP(SDIO_VENDOR_ID_MSM_QCA, SDIO_DEVICE_ID_MSM_QCA_AR6003_1,
+ remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+
+ SDIO_FIXUP(SDIO_VENDOR_ID_MSM_QCA, SDIO_DEVICE_ID_MSM_QCA_AR6003_2,
+ remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+
+ SDIO_FIXUP(SDIO_VENDOR_ID_MSM_QCA, SDIO_DEVICE_ID_MSM_QCA_AR6004_1,
+ remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+
+ SDIO_FIXUP(SDIO_VENDOR_ID_MSM_QCA, SDIO_DEVICE_ID_MSM_QCA_AR6004_2,
+ remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
add_quirk, MMC_QUIRK_NONSTD_FUNC_IF),
@@ -66,6 +126,11 @@ static const struct mmc_fixup mmc_fixup_methods[] = {
SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0,
add_quirk, MMC_QUIRK_BROKEN_IRQ_POLLING),
+ SDIO_FIXUP(SDIO_VENDOR_ID_QCA6574, SDIO_DEVICE_ID_QCA6574,
+ add_quirk, MMC_QUIRK_QCA6574_SETTINGS),
+
+ SDIO_FIXUP(SDIO_VENDOR_ID_QCA9377, SDIO_DEVICE_ID_QCA9377,
+ add_quirk, MMC_QUIRK_QCA9377_SETTINGS),
END_FIXUP
};
@@ -86,6 +151,8 @@ void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table)
(f->name == CID_NAME_ANY ||
!strncmp(f->name, card->cid.prod_name,
sizeof(card->cid.prod_name))) &&
+ (f->ext_csd_rev == EXT_CSD_REV_ANY ||
+ f->ext_csd_rev == card->ext_csd.rev) &&
(f->cis_vendor == card->cis.vendor ||
f->cis_vendor == (u16) SDIO_ANY_ID) &&
(f->cis_device == card->cis.device ||
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 60542b2..7f66ad3 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -27,6 +27,12 @@
#include "sd.h"
#include "sd_ops.h"
+#define UHS_SDR104_MIN_DTR (100 * 1000 * 1000)
+#define UHS_DDR50_MIN_DTR (50 * 1000 * 1000)
+#define UHS_SDR50_MIN_DTR (50 * 1000 * 1000)
+#define UHS_SDR25_MIN_DTR (25 * 1000 * 1000)
+#define UHS_SDR12_MIN_DTR (12.5 * 1000 * 1000)
+
static const unsigned int tran_exp[] = {
10000, 100000, 1000000, 10000000,
0, 0, 0, 0
@@ -368,9 +374,9 @@ int mmc_sd_switch_hs(struct mmc_card *card)
goto out;
if ((status[16] & 0xF) != 1) {
- pr_warn("%s: Problem switching card into high-speed mode!\n",
- mmc_hostname(card->host));
- err = 0;
+ pr_warn("%s: Problem switching card into high-speed mode!, status:%x\n",
+ mmc_hostname(card->host), (status[16] & 0xF));
+ err = -EBUSY;
} else {
err = 1;
}
@@ -424,18 +430,22 @@ static void sd_update_bus_speed_mode(struct mmc_card *card)
}
if ((card->host->caps & MMC_CAP_UHS_SDR104) &&
- (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104)) {
+ (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104) &&
+ (card->host->f_max > UHS_SDR104_MIN_DTR)) {
card->sd_bus_speed = UHS_SDR104_BUS_SPEED;
} else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
- (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50)) {
+ (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50) &&
+ (card->host->f_max > UHS_DDR50_MIN_DTR)) {
card->sd_bus_speed = UHS_DDR50_BUS_SPEED;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode &
- SD_MODE_UHS_SDR50)) {
+ SD_MODE_UHS_SDR50) &&
+ (card->host->f_max > UHS_SDR50_MIN_DTR)) {
card->sd_bus_speed = UHS_SDR50_BUS_SPEED;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) &&
- (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25)) {
+ (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25) &&
+ (card->host->f_max > UHS_SDR25_MIN_DTR)) {
card->sd_bus_speed = UHS_SDR25_BUS_SPEED;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 |
@@ -479,15 +489,17 @@ static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
if (err)
return err;
- if ((status[16] & 0xF) != card->sd_bus_speed)
- pr_warn("%s: Problem setting bus speed mode!\n",
- mmc_hostname(card->host));
- else {
+ if ((status[16] & 0xF) != card->sd_bus_speed) {
+ pr_warn("%s: Problem setting bus speed mode(%u)! max_dtr:%u, timing:%u, status:%x\n",
+ mmc_hostname(card->host), card->sd_bus_speed,
+ card->sw_caps.uhs_max_dtr, timing, (status[16] & 0xF));
+ err = -EBUSY;
+ } else {
mmc_set_timing(card->host, timing);
mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
}
- return 0;
+ return err;
}
/* Get host's max current setting at its current voltage */
@@ -579,6 +591,64 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
return 0;
}
+/**
+ * mmc_sd_change_bus_speed() - Change SD card bus frequency at runtime
+ * @host: pointer to mmc host structure
+ * @freq: pointer to desired frequency to be set
+ *
+ * Change the SD card bus frequency at runtime after the card is
+ * initialized. Callers are expected to make sure of the card's
+ * state (DATA/RCV/TRANSFER) beforing changing the frequency at runtime.
+ *
+ * If the frequency to change is greater than max. supported by card,
+ * *freq is changed to max. supported by card and if it is less than min.
+ * supported by host, *freq is changed to min. supported by host.
+ */
+static int mmc_sd_change_bus_speed(struct mmc_host *host, unsigned long *freq)
+{
+ int err = 0;
+ struct mmc_card *card;
+
+ mmc_claim_host(host);
+ /*
+ * Assign card pointer after claiming host to avoid race
+ * conditions that may arise during removal of the card.
+ */
+ card = host->card;
+
+ /* sanity checks */
+ if (!card || !freq) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ mmc_set_clock(host, (unsigned int) (*freq));
+
+ if (!mmc_host_is_spi(card->host) && mmc_card_uhs(card)
+ && card->host->ops->execute_tuning) {
+ /*
+ * We try to probe host driver for tuning for any
+ * frequency, it is host driver responsibility to
+ * perform actual tuning only when required.
+ */
+ mmc_host_clk_hold(card->host);
+ err = card->host->ops->execute_tuning(card->host,
+ MMC_SEND_TUNING_BLOCK);
+ mmc_host_clk_release(card->host);
+
+ if (err) {
+ pr_warn("%s: %s: tuning execution failed %d. Restoring to previous clock %lu\n",
+ mmc_hostname(card->host), __func__, err,
+ host->clk_scaling.curr_freq);
+ mmc_set_clock(host, host->clk_scaling.curr_freq);
+ }
+ }
+
+out:
+ mmc_release_host(host);
+ return err;
+}
+
/*
* UHS-I specific initialization procedure
*/
@@ -935,7 +1005,10 @@ unsigned mmc_sd_get_max_clock(struct mmc_card *card)
{
unsigned max_dtr = (unsigned int)-1;
- if (mmc_card_hs(card)) {
+ if (mmc_card_uhs(card)) {
+ if (max_dtr > card->sw_caps.uhs_max_dtr)
+ max_dtr = card->sw_caps.uhs_max_dtr;
+ } else if (mmc_card_hs(card)) {
if (max_dtr > card->sw_caps.hs_max_dtr)
max_dtr = card->sw_caps.hs_max_dtr;
} else if (max_dtr > card->csd.max_dtr) {
@@ -997,6 +1070,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
err = mmc_send_relative_addr(host, &card->rca);
if (err)
goto free_card;
+ host->card = card;
}
if (!oldcard) {
@@ -1060,12 +1134,16 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
}
}
- host->card = card;
+ card->clk_scaling_highest = mmc_sd_get_max_clock(card);
+ card->clk_scaling_lowest = host->f_min;
+
return 0;
free_card:
- if (!oldcard)
+ if (!oldcard) {
+ host->card = NULL;
mmc_remove_card(card);
+ }
return err;
}
@@ -1078,8 +1156,12 @@ static void mmc_sd_remove(struct mmc_host *host)
BUG_ON(!host);
BUG_ON(!host->card);
+ mmc_exit_clk_scaling(host);
mmc_remove_card(host->card);
+
+ mmc_claim_host(host);
host->card = NULL;
+ mmc_release_host(host);
}
/*
@@ -1121,6 +1203,7 @@ static void mmc_sd_detect(struct mmc_host *host)
if (!retries) {
printk(KERN_ERR "%s(%s): Unable to re-detect card (%d)\n",
__func__, mmc_hostname(host), err);
+ err = _mmc_detect_card_removed(host);
}
#else
err = _mmc_detect_card_removed(host);
@@ -1145,6 +1228,13 @@ static int _mmc_sd_suspend(struct mmc_host *host)
BUG_ON(!host);
BUG_ON(!host->card);
+ err = mmc_suspend_clk_scaling(host);
+ if (err) {
+ pr_err("%s: %s: fail to suspend clock scaling (%d)\n",
+ mmc_hostname(host), __func__, err);
+ return err;
+ }
+
mmc_claim_host(host);
if (mmc_card_suspended(host->card))
@@ -1170,11 +1260,13 @@ static int mmc_sd_suspend(struct mmc_host *host)
{
int err;
+ MMC_TRACE(host, "%s: Enter\n", __func__);
err = _mmc_sd_suspend(host);
if (!err) {
pm_runtime_disable(&host->card->dev);
pm_runtime_set_suspended(&host->card->dev);
}
+ MMC_TRACE(host, "%s: Exit err: %d\n", __func__, err);
return err;
}
@@ -1207,8 +1299,11 @@ static int _mmc_sd_resume(struct mmc_host *host)
if (err) {
printk(KERN_ERR "%s: Re-init card rc = %d (retries = %d)\n",
mmc_hostname(host), err, retries);
- mdelay(5);
retries--;
+ mmc_power_off(host);
+ usleep_range(5000, 5500);
+ mmc_power_up(host, host->card->ocr);
+ mmc_select_voltage(host, host->card->ocr);
continue;
}
break;
@@ -1218,6 +1313,13 @@ static int _mmc_sd_resume(struct mmc_host *host)
#endif
mmc_card_clr_suspended(host->card);
+ err = mmc_resume_clk_scaling(host);
+ if (err) {
+ pr_err("%s: %s: fail to resume clock scaling (%d)\n",
+ mmc_hostname(host), __func__, err);
+ goto out;
+ }
+
out:
mmc_release_host(host);
return err;
@@ -1228,7 +1330,12 @@ static int _mmc_sd_resume(struct mmc_host *host)
*/
static int mmc_sd_resume(struct mmc_host *host)
{
+ int err = 0;
+
+ MMC_TRACE(host, "%s: Enter\n", __func__);
pm_runtime_enable(&host->card->dev);
+
+ MMC_TRACE(host, "%s: Exit err: %d\n", __func__, err);
return 0;
}
@@ -1279,7 +1386,7 @@ static const struct mmc_bus_ops mmc_sd_ops = {
.suspend = mmc_sd_suspend,
.resume = mmc_sd_resume,
.alive = mmc_sd_alive,
- .shutdown = mmc_sd_suspend,
+ .change_bus_speed = mmc_sd_change_bus_speed,
.reset = mmc_sd_reset,
};
@@ -1335,6 +1442,10 @@ int mmc_attach_sd(struct mmc_host *host)
err = mmc_sd_init_card(host, rocr, NULL);
if (err) {
retries--;
+ mmc_power_off(host);
+ usleep_range(5000, 5500);
+ mmc_power_up(host, rocr);
+ mmc_select_voltage(host, rocr);
continue;
}
break;
@@ -1357,6 +1468,13 @@ int mmc_attach_sd(struct mmc_host *host)
goto remove_card;
mmc_claim_host(host);
+
+ err = mmc_init_clk_scaling(host);
+ if (err) {
+ mmc_release_host(host);
+ goto remove_card;
+ }
+
return 0;
remove_card:
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 8e10bdc..9ebe730 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -186,6 +186,23 @@ static int sdio_read_cccr(struct mmc_card *card, u32 ocr)
card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_C;
if (data & SDIO_DRIVE_SDTD)
card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_D;
+
+ ret = mmc_io_rw_direct(card, 0, 0,
+ SDIO_CCCR_INTERRUPT_EXTENSION, 0, &data);
+ if (ret)
+ goto out;
+ if (data & SDIO_SUPPORT_ASYNC_INTR) {
+ if (card->host->caps2 &
+ MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE) {
+ data |= SDIO_ENABLE_ASYNC_INTR;
+ ret = mmc_io_rw_direct(card, 1, 0,
+ SDIO_CCCR_INTERRUPT_EXTENSION,
+ data, NULL);
+ if (ret)
+ goto out;
+ card->cccr.async_intr_sup = 1;
+ }
+ }
}
/* if no uhs mode ensure we check for high speed */
@@ -204,12 +221,60 @@ static int sdio_read_cccr(struct mmc_card *card, u32 ocr)
return ret;
}
+static void sdio_enable_vendor_specific_settings(struct mmc_card *card)
+{
+ int ret;
+ u8 settings;
+
+ if (mmc_enable_qca6574_settings(card) ||
+ mmc_enable_qca9377_settings(card)) {
+ ret = mmc_io_rw_direct(card, 1, 0, 0xF2, 0x0F, NULL);
+ if (ret) {
+ pr_crit("%s: failed to write to fn 0xf2 %d\n",
+ mmc_hostname(card->host), ret);
+ goto out;
+ }
+
+ ret = mmc_io_rw_direct(card, 0, 0, 0xF1, 0, &settings);
+ if (ret) {
+ pr_crit("%s: failed to read fn 0xf1 %d\n",
+ mmc_hostname(card->host), ret);
+ goto out;
+ }
+
+ settings |= 0x80;
+ ret = mmc_io_rw_direct(card, 1, 0, 0xF1, settings, NULL);
+ if (ret) {
+ pr_crit("%s: failed to write to fn 0xf1 %d\n",
+ mmc_hostname(card->host), ret);
+ goto out;
+ }
+
+ ret = mmc_io_rw_direct(card, 0, 0, 0xF0, 0, &settings);
+ if (ret) {
+ pr_crit("%s: failed to read fn 0xf0 %d\n",
+ mmc_hostname(card->host), ret);
+ goto out;
+ }
+
+ settings |= 0x20;
+ ret = mmc_io_rw_direct(card, 1, 0, 0xF0, settings, NULL);
+ if (ret) {
+ pr_crit("%s: failed to write to fn 0xf0 %d\n",
+ mmc_hostname(card->host), ret);
+ goto out;
+ }
+ }
+out:
+ return;
+}
+
static int sdio_enable_wide(struct mmc_card *card)
{
int ret;
u8 ctrl;
- if (!(card->host->caps & MMC_CAP_4_BIT_DATA))
+ if (!(card->host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)))
return 0;
if (card->cccr.low_speed && !card->cccr.wide_bus)
@@ -225,7 +290,10 @@ static int sdio_enable_wide(struct mmc_card *card)
/* set as 4-bit bus width */
ctrl &= ~SDIO_BUS_WIDTH_MASK;
- ctrl |= SDIO_BUS_WIDTH_4BIT;
+ if (card->host->caps & MMC_CAP_8_BIT_DATA)
+ ctrl |= SDIO_BUS_WIDTH_8BIT;
+ else if (card->host->caps & MMC_CAP_4_BIT_DATA)
+ ctrl |= SDIO_BUS_WIDTH_4BIT;
ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL);
if (ret)
@@ -266,7 +334,7 @@ static int sdio_disable_wide(struct mmc_card *card)
int ret;
u8 ctrl;
- if (!(card->host->caps & MMC_CAP_4_BIT_DATA))
+ if (!(card->host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)))
return 0;
if (card->cccr.low_speed && !card->cccr.wide_bus)
@@ -276,10 +344,10 @@ static int sdio_disable_wide(struct mmc_card *card)
if (ret)
return ret;
- if (!(ctrl & SDIO_BUS_WIDTH_4BIT))
+ if (!(ctrl & (SDIO_BUS_WIDTH_4BIT | SDIO_BUS_WIDTH_8BIT)))
return 0;
- ctrl &= ~SDIO_BUS_WIDTH_4BIT;
+ ctrl &= ~(SDIO_BUS_WIDTH_4BIT | SDIO_BUS_WIDTH_8BIT);
ctrl |= SDIO_BUS_ASYNC_INT;
ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL);
@@ -496,6 +564,9 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card)
if (err)
return err;
+ /* Vendor specific settings based on card quirks */
+ sdio_enable_vendor_specific_settings(card);
+
speed &= ~SDIO_SPEED_BSS_MASK;
speed |= bus_speed;
err = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL);
@@ -622,8 +693,11 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
/*
* Call the optional HC's init_card function to handle quirks.
*/
- if (host->ops->init_card)
+ if (host->ops->init_card) {
+ mmc_host_clk_hold(host);
host->ops->init_card(host, card);
+ mmc_host_clk_release(host);
+ }
/*
* If the host and card support UHS-I mode request the card
@@ -790,7 +864,12 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
* Switch to wider bus (if supported).
*/
err = sdio_enable_4bit_bus(card);
- if (err)
+ if (err > 0) {
+ if (card->host->caps & MMC_CAP_8_BIT_DATA)
+ mmc_set_bus_width(card->host, MMC_BUS_WIDTH_8);
+ else if (card->host->caps & MMC_CAP_4_BIT_DATA)
+ mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
+ } else if (err)
goto remove;
}
finish:
@@ -917,6 +996,7 @@ static int mmc_sdio_pre_suspend(struct mmc_host *host)
*/
static int mmc_sdio_suspend(struct mmc_host *host)
{
+ MMC_TRACE(host, "%s: Enter\n", __func__);
mmc_claim_host(host);
if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host))
@@ -924,13 +1004,15 @@ static int mmc_sdio_suspend(struct mmc_host *host)
if (!mmc_card_keep_power(host)) {
mmc_power_off(host);
+ } else if (host->ios.clock) {
+ mmc_gate_clock(host);
} else if (host->retune_period) {
mmc_retune_timer_stop(host);
mmc_retune_needed(host);
}
mmc_release_host(host);
-
+ MMC_TRACE(host, "%s: Exit\n", __func__);
return 0;
}
@@ -941,6 +1023,7 @@ static int mmc_sdio_resume(struct mmc_host *host)
BUG_ON(!host);
BUG_ON(!host->card);
+ MMC_TRACE(host, "%s: Enter\n", __func__);
/* Basic card reinitialization. */
mmc_claim_host(host);
@@ -973,6 +1056,13 @@ static int mmc_sdio_resume(struct mmc_host *host)
} else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
/* We may have switched to 1-bit mode during suspend */
err = sdio_enable_4bit_bus(host->card);
+ if (err > 0) {
+ if (host->caps & MMC_CAP_8_BIT_DATA)
+ mmc_set_bus_width(host, MMC_BUS_WIDTH_8);
+ else if (host->caps & MMC_CAP_4_BIT_DATA)
+ mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
+ err = 0;
+ }
}
if (!err && host->sdio_irqs) {
@@ -988,6 +1078,8 @@ static int mmc_sdio_resume(struct mmc_host *host)
mmc_release_host(host);
host->pm_flags &= ~MMC_PM_KEEP_POWER;
+ host->pm_flags &= ~MMC_PM_WAKE_SDIO_IRQ;
+ MMC_TRACE(host, "%s: Exit err: %d\n", __func__, err);
return err;
}
@@ -1222,40 +1314,6 @@ int mmc_attach_sdio(struct mmc_host *host)
int sdio_reset_comm(struct mmc_card *card)
{
- struct mmc_host *host = card->host;
- u32 ocr;
- u32 rocr;
- int err;
-
- printk("%s():\n", __func__);
- mmc_claim_host(host);
-
- mmc_retune_disable(host);
-
- mmc_go_idle(host);
-
- mmc_set_clock(host, host->f_min);
-
- err = mmc_send_io_op_cond(host, 0, &ocr);
- if (err)
- goto err;
-
- rocr = mmc_select_voltage(host, ocr);
- if (!rocr) {
- err = -EINVAL;
- goto err;
- }
-
- err = mmc_sdio_init_card(host, rocr, card, 0);
- if (err)
- goto err;
-
- mmc_release_host(host);
- return 0;
-err:
- printk("%s: Error resetting SDIO communications (%d)\n",
- mmc_hostname(host), err);
- mmc_release_host(host);
- return err;
+ return mmc_power_restore_host(card->host);
}
EXPORT_SYMBOL(sdio_reset_comm);
diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c
index dcb3dee..5d7f198 100644
--- a/drivers/mmc/core/sdio_cis.c
+++ b/drivers/mmc/core/sdio_cis.c
@@ -55,7 +55,7 @@ static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func,
for (i = 0; i < nr_strings; i++) {
buffer[i] = string;
- strcpy(string, buf);
+ strlcpy(string, buf, strlen(buf) + 1);
string += strlen(string) + 1;
buf += strlen(buf) + 1;
}
@@ -276,8 +276,16 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
break;
/* null entries have no link field or data */
- if (tpl_code == 0x00)
- continue;
+ if (tpl_code == 0x00) {
+ if (card->cis.vendor == 0x70 &&
+ (card->cis.device == 0x2460 ||
+ card->cis.device == 0x0460 ||
+ card->cis.device == 0x23F1 ||
+ card->cis.device == 0x23F0))
+ break;
+ else
+ continue;
+ }
ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link);
if (ret)
diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c
index 09cc67d..95589d1 100644
--- a/drivers/mmc/core/sdio_irq.c
+++ b/drivers/mmc/core/sdio_irq.c
@@ -93,7 +93,9 @@ void sdio_run_irqs(struct mmc_host *host)
{
mmc_claim_host(host);
host->sdio_irq_pending = true;
+ mmc_host_clk_hold(host);
process_sdio_pending_irqs(host);
+ mmc_host_clk_release(host);
mmc_release_host(host);
}
EXPORT_SYMBOL_GPL(sdio_run_irqs);
@@ -104,6 +106,7 @@ static int sdio_irq_thread(void *_host)
struct sched_param param = { .sched_priority = 1 };
unsigned long period, idle_period;
int ret;
+ bool ws;
sched_setscheduler(current, SCHED_FIFO, ¶m);
@@ -137,6 +140,17 @@ static int sdio_irq_thread(void *_host)
ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort);
if (ret)
break;
+ ws = false;
+ /*
+ * prevent suspend if it has started when scheduled;
+ * 100 msec (approx. value) should be enough for the system to
+ * resume and attend to the card's request
+ */
+ if ((host->dev_status == DEV_SUSPENDING) ||
+ (host->dev_status == DEV_SUSPENDED)) {
+ pm_wakeup_event(&host->card->dev, 100);
+ ws = true;
+ }
ret = process_sdio_pending_irqs(host);
host->sdio_irq_pending = false;
mmc_release_host(host);
@@ -173,6 +187,12 @@ static int sdio_irq_thread(void *_host)
host->ops->enable_sdio_irq(host, 1);
mmc_host_clk_release(host);
}
+ /*
+ * function drivers would have processed the event from card
+ * unless suspended, hence release wake source
+ */
+ if (ws && (host->dev_status == DEV_RESUMED))
+ pm_relax(&host->card->dev);
if (!kthread_should_stop())
schedule_timeout(period);
set_current_state(TASK_RUNNING);
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index defb66e..515abb2 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -399,6 +399,8 @@
tristate "Qualcomm Technologies, Inc. SDHCI Controller Support"
depends on ARCH_QCOM || ARCH_MSM || (ARM && COMPILE_TEST)
depends on MMC_SDHCI_PLTFM
+ select PM_DEVFREQ
+ select DEVFREQ_GOV_SIMPLE_ONDEMAND
help
This selects the Secure Digital Host Controller Interface (SDHCI)
support present in Qualcomm Technologies, Inc. SOCs. The controller
@@ -408,6 +410,17 @@
If unsure, say N.
+config MMC_SDHCI_MSM_ICE
+ bool "Qualcomm Technologies, Inc Inline Crypto Engine for SDHCI core"
+ depends on MMC_SDHCI_MSM && CRYPTO_DEV_QCOM_ICE
+ help
+ This selects the QTI specific additions to support Inline Crypto
+ Engine (ICE). ICE accelerates the crypto operations and maintains
+ the high SDHCI performance.
+
+ Select this if you have ICE supported for SDHCI on QTI chipset.
+ If unsure, say N.
+
config MMC_MSM
tristate "Qualcomm SDCC Controller Support"
depends on MMC && (ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50)
@@ -772,6 +785,19 @@
This selects support for the SD/MMC Host Controller on
Allwinner sunxi SoCs.
+config MMC_CQ_HCI
+ tristate "Command Queue Support"
+ depends on HAS_DMA
+ help
+ This selects the Command Queue Host Controller Interface (CQHCI)
+ support present in host controllers of Qualcomm Technologies, Inc
+ amongst others.
+ This controller supports eMMC devices with command queue support.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
+
config MMC_TOSHIBA_PCI
tristate "Toshiba Type A SD/MMC Card Interface Driver"
depends on PCI
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index ef56624..64defc5 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -72,8 +72,10 @@
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
+obj-$(CONFIG_MMC_SDHCI_MSM_ICE) += sdhci-msm-ice.o
obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o
+obj-$(CONFIG_MMC_CQ_HCI) += cmdq_hci.o
obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o
obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.o
diff --git a/drivers/mmc/host/cmdq_hci.c b/drivers/mmc/host/cmdq_hci.c
new file mode 100644
index 0000000..77c5ca3
--- /dev/null
+++ b/drivers/mmc/host/cmdq_hci.c
@@ -0,0 +1,1197 @@
+/* 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/highmem.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/scatterlist.h>
+#include <linux/platform_device.h>
+#include <linux/blkdev.h>
+
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/pm_runtime.h>
+#include <linux/workqueue.h>
+
+#include "cmdq_hci.h"
+#include "sdhci.h"
+#include "sdhci-msm.h"
+
+#define DCMD_SLOT 31
+#define NUM_SLOTS 32
+
+/* 10 sec */
+#define HALT_TIMEOUT_MS 10000
+
+static int cmdq_halt_poll(struct mmc_host *mmc, bool halt);
+static int cmdq_halt(struct mmc_host *mmc, bool halt);
+
+#ifdef CONFIG_PM_RUNTIME
+static int cmdq_runtime_pm_get(struct cmdq_host *host)
+{
+ return pm_runtime_get_sync(host->mmc->parent);
+}
+static int cmdq_runtime_pm_put(struct cmdq_host *host)
+{
+ pm_runtime_mark_last_busy(host->mmc->parent);
+ return pm_runtime_put_autosuspend(host->mmc->parent);
+}
+#else
+static inline int cmdq_runtime_pm_get(struct cmdq_host *host)
+{
+ return 0;
+}
+static inline int cmdq_runtime_pm_put(struct cmdq_host *host)
+{
+ return 0;
+}
+#endif
+static inline struct mmc_request *get_req_by_tag(struct cmdq_host *cq_host,
+ unsigned int tag)
+{
+ return cq_host->mrq_slot[tag];
+}
+
+static inline u8 *get_desc(struct cmdq_host *cq_host, u8 tag)
+{
+ return cq_host->desc_base + (tag * cq_host->slot_sz);
+}
+
+static inline u8 *get_link_desc(struct cmdq_host *cq_host, u8 tag)
+{
+ u8 *desc = get_desc(cq_host, tag);
+
+ return desc + cq_host->task_desc_len;
+}
+
+static inline dma_addr_t get_trans_desc_dma(struct cmdq_host *cq_host, u8 tag)
+{
+ return cq_host->trans_desc_dma_base +
+ (cq_host->mmc->max_segs * tag *
+ cq_host->trans_desc_len);
+}
+
+static inline u8 *get_trans_desc(struct cmdq_host *cq_host, u8 tag)
+{
+ return cq_host->trans_desc_base +
+ (cq_host->trans_desc_len * cq_host->mmc->max_segs * tag);
+}
+
+static void setup_trans_desc(struct cmdq_host *cq_host, u8 tag)
+{
+ u8 *link_temp;
+ dma_addr_t trans_temp;
+
+ link_temp = get_link_desc(cq_host, tag);
+ trans_temp = get_trans_desc_dma(cq_host, tag);
+
+ memset(link_temp, 0, cq_host->link_desc_len);
+ if (cq_host->link_desc_len > 8)
+ *(link_temp + 8) = 0;
+
+ if (tag == DCMD_SLOT) {
+ *link_temp = VALID(0) | ACT(0) | END(1);
+ return;
+ }
+
+ *link_temp = VALID(1) | ACT(0x6) | END(0);
+
+ if (cq_host->dma64) {
+ __le64 *data_addr = (__le64 __force *)(link_temp + 4);
+ data_addr[0] = cpu_to_le64(trans_temp);
+ } else {
+ __le32 *data_addr = (__le32 __force *)(link_temp + 4);
+ data_addr[0] = cpu_to_le32(trans_temp);
+ }
+}
+
+static void cmdq_set_halt_irq(struct cmdq_host *cq_host, bool enable)
+{
+ u32 ier;
+
+ ier = cmdq_readl(cq_host, CQISTE);
+ if (enable) {
+ cmdq_writel(cq_host, ier | HALT, CQISTE);
+ cmdq_writel(cq_host, ier | HALT, CQISGE);
+ } else {
+ cmdq_writel(cq_host, ier & ~HALT, CQISTE);
+ cmdq_writel(cq_host, ier & ~HALT, CQISGE);
+ }
+}
+
+static void cmdq_clear_set_irqs(struct cmdq_host *cq_host, u32 clear, u32 set)
+{
+ u32 ier;
+
+ ier = cmdq_readl(cq_host, CQISTE);
+ ier &= ~clear;
+ ier |= set;
+ cmdq_writel(cq_host, ier, CQISTE);
+ cmdq_writel(cq_host, ier, CQISGE);
+ /* ensure the writes are done */
+ mb();
+}
+
+
+#define DRV_NAME "cmdq-host"
+
+static void cmdq_dump_task_history(struct cmdq_host *cq_host)
+{
+ int i;
+
+ if (likely(!cq_host->mmc->cmdq_thist_enabled))
+ return;
+
+ if (!cq_host->thist) {
+ pr_err("%s: %s: CMDQ task history buffer not allocated\n",
+ mmc_hostname(cq_host->mmc), __func__);
+ return;
+ }
+
+ pr_err("---- Circular Task History ----\n");
+ pr_err(DRV_NAME ": Last entry index: %d", cq_host->thist_idx - 1);
+
+ for (i = 0; i < cq_host->num_slots; i++) {
+ pr_err(DRV_NAME ": [%02d]%s Task: 0x%08x | Args: 0x%08x\n", i,
+ (cq_host->thist[i].is_dcmd) ? "DCMD" : "DATA",
+ lower_32_bits(cq_host->thist[i].task),
+ upper_32_bits(cq_host->thist[i].task));
+ }
+ pr_err("-------------------------\n");
+}
+
+static void cmdq_dump_adma_mem(struct cmdq_host *cq_host)
+{
+ struct mmc_host *mmc = cq_host->mmc;
+ dma_addr_t desc_dma;
+ int tag = 0;
+ unsigned long data_active_reqs =
+ mmc->cmdq_ctx.data_active_reqs;
+ unsigned long desc_size =
+ (cq_host->mmc->max_segs * cq_host->trans_desc_len);
+
+ for_each_set_bit(tag, &data_active_reqs, cq_host->num_slots) {
+ desc_dma = get_trans_desc_dma(cq_host, tag);
+ pr_err("%s: %s: tag = %d, trans_dma(phys) = %pad, trans_desc(virt) = 0x%p\n",
+ mmc_hostname(mmc), __func__, tag,
+ &desc_dma, get_trans_desc(cq_host, tag));
+ print_hex_dump(KERN_ERR, "cmdq-adma:", DUMP_PREFIX_ADDRESS,
+ 32, 8, get_trans_desc(cq_host, tag),
+ (desc_size), false);
+ }
+}
+
+static void cmdq_dumpregs(struct cmdq_host *cq_host)
+{
+ struct mmc_host *mmc = cq_host->mmc;
+
+ MMC_TRACE(mmc,
+ "%s: 0x0C=0x%08x 0x10=0x%08x 0x14=0x%08x 0x18=0x%08x 0x28=0x%08x 0x2C=0x%08x 0x30=0x%08x 0x34=0x%08x 0x54=0x%08x 0x58=0x%08x 0x5C=0x%08x 0x48=0x%08x\n",
+ __func__, cmdq_readl(cq_host, CQCTL), cmdq_readl(cq_host, CQIS),
+ cmdq_readl(cq_host, CQISTE), cmdq_readl(cq_host, CQISGE),
+ cmdq_readl(cq_host, CQTDBR), cmdq_readl(cq_host, CQTCN),
+ cmdq_readl(cq_host, CQDQS), cmdq_readl(cq_host, CQDPT),
+ cmdq_readl(cq_host, CQTERRI), cmdq_readl(cq_host, CQCRI),
+ cmdq_readl(cq_host, CQCRA), cmdq_readl(cq_host, CQCRDCT));
+ pr_err(DRV_NAME ": ========== REGISTER DUMP (%s)==========\n",
+ mmc_hostname(mmc));
+
+ pr_err(DRV_NAME ": Caps: 0x%08x | Version: 0x%08x\n",
+ cmdq_readl(cq_host, CQCAP),
+ cmdq_readl(cq_host, CQVER));
+ pr_err(DRV_NAME ": Queing config: 0x%08x | Queue Ctrl: 0x%08x\n",
+ cmdq_readl(cq_host, CQCFG),
+ cmdq_readl(cq_host, CQCTL));
+ pr_err(DRV_NAME ": Int stat: 0x%08x | Int enab: 0x%08x\n",
+ cmdq_readl(cq_host, CQIS),
+ cmdq_readl(cq_host, CQISTE));
+ pr_err(DRV_NAME ": Int sig: 0x%08x | Int Coal: 0x%08x\n",
+ cmdq_readl(cq_host, CQISGE),
+ cmdq_readl(cq_host, CQIC));
+ pr_err(DRV_NAME ": TDL base: 0x%08x | TDL up32: 0x%08x\n",
+ cmdq_readl(cq_host, CQTDLBA),
+ cmdq_readl(cq_host, CQTDLBAU));
+ pr_err(DRV_NAME ": Doorbell: 0x%08x | Comp Notif: 0x%08x\n",
+ cmdq_readl(cq_host, CQTDBR),
+ cmdq_readl(cq_host, CQTCN));
+ pr_err(DRV_NAME ": Dev queue: 0x%08x | Dev Pend: 0x%08x\n",
+ cmdq_readl(cq_host, CQDQS),
+ cmdq_readl(cq_host, CQDPT));
+ pr_err(DRV_NAME ": Task clr: 0x%08x | Send stat 1: 0x%08x\n",
+ cmdq_readl(cq_host, CQTCLR),
+ cmdq_readl(cq_host, CQSSC1));
+ pr_err(DRV_NAME ": Send stat 2: 0x%08x | DCMD resp: 0x%08x\n",
+ cmdq_readl(cq_host, CQSSC2),
+ cmdq_readl(cq_host, CQCRDCT));
+ pr_err(DRV_NAME ": Resp err mask: 0x%08x | Task err: 0x%08x\n",
+ cmdq_readl(cq_host, CQRMEM),
+ cmdq_readl(cq_host, CQTERRI));
+ pr_err(DRV_NAME ": Resp idx 0x%08x | Resp arg: 0x%08x\n",
+ cmdq_readl(cq_host, CQCRI),
+ cmdq_readl(cq_host, CQCRA));
+ pr_err(DRV_NAME": Vendor cfg 0x%08x\n",
+ cmdq_readl(cq_host, CQ_VENDOR_CFG));
+ pr_err(DRV_NAME ": ===========================================\n");
+
+ cmdq_dump_task_history(cq_host);
+ if (cq_host->ops->dump_vendor_regs)
+ cq_host->ops->dump_vendor_regs(mmc);
+}
+
+/**
+ * The allocated descriptor table for task, link & transfer descritors
+ * looks like:
+ * |----------|
+ * |task desc | |->|----------|
+ * |----------| | |trans desc|
+ * |link desc-|->| |----------|
+ * |----------| .
+ * . .
+ * no. of slots max-segs
+ * . |----------|
+ * |----------|
+ * The idea here is to create the [task+trans] table and mark & point the
+ * link desc to the transfer desc table on a per slot basis.
+ */
+static int cmdq_host_alloc_tdl(struct cmdq_host *cq_host)
+{
+
+ size_t desc_size;
+ size_t data_size;
+ int i = 0;
+
+ /* task descriptor can be 64/128 bit irrespective of arch */
+ if (cq_host->caps & CMDQ_TASK_DESC_SZ_128) {
+ cmdq_writel(cq_host, cmdq_readl(cq_host, CQCFG) |
+ CQ_TASK_DESC_SZ, CQCFG);
+ cq_host->task_desc_len = 16;
+ } else {
+ cq_host->task_desc_len = 8;
+ }
+
+ /*
+ * 96 bits length of transfer desc instead of 128 bits which means
+ * ADMA would expect next valid descriptor at the 96th bit
+ * or 128th bit
+ */
+ if (cq_host->dma64) {
+ if (cq_host->quirks & CMDQ_QUIRK_SHORT_TXFR_DESC_SZ)
+ cq_host->trans_desc_len = 12;
+ else
+ cq_host->trans_desc_len = 16;
+ cq_host->link_desc_len = 16;
+ } else {
+ cq_host->trans_desc_len = 8;
+ cq_host->link_desc_len = 8;
+ }
+
+ /* total size of a slot: 1 task & 1 transfer (link) */
+ cq_host->slot_sz = cq_host->task_desc_len + cq_host->link_desc_len;
+
+ desc_size = cq_host->slot_sz * cq_host->num_slots;
+
+ data_size = cq_host->trans_desc_len * cq_host->mmc->max_segs *
+ (cq_host->num_slots - 1);
+
+ pr_info("%s: desc_size: %d data_sz: %d slot-sz: %d\n", __func__,
+ (int)desc_size, (int)data_size, cq_host->slot_sz);
+
+ /*
+ * allocate a dma-mapped chunk of memory for the descriptors
+ * allocate a dma-mapped chunk of memory for link descriptors
+ * setup each link-desc memory offset per slot-number to
+ * the descriptor table.
+ */
+ cq_host->desc_base = dmam_alloc_coherent(mmc_dev(cq_host->mmc),
+ desc_size,
+ &cq_host->desc_dma_base,
+ GFP_KERNEL);
+ cq_host->trans_desc_base = dmam_alloc_coherent(mmc_dev(cq_host->mmc),
+ data_size,
+ &cq_host->trans_desc_dma_base,
+ GFP_KERNEL);
+ cq_host->thist = devm_kzalloc(mmc_dev(cq_host->mmc),
+ (sizeof(*cq_host->thist) *
+ cq_host->num_slots),
+ GFP_KERNEL);
+ if (!cq_host->desc_base || !cq_host->trans_desc_base)
+ return -ENOMEM;
+
+ pr_info("desc-base: 0x%p trans-base: 0x%p\n desc_dma 0x%llx trans_dma: 0x%llx\n",
+ cq_host->desc_base, cq_host->trans_desc_base,
+ (unsigned long long)cq_host->desc_dma_base,
+ (unsigned long long) cq_host->trans_desc_dma_base);
+
+ for (; i < (cq_host->num_slots); i++)
+ setup_trans_desc(cq_host, i);
+
+ return 0;
+}
+
+static int cmdq_enable(struct mmc_host *mmc)
+{
+ int err = 0;
+ u32 cqcfg;
+ bool dcmd_enable;
+ struct cmdq_host *cq_host = mmc_cmdq_private(mmc);
+
+ if (!cq_host || !mmc->card || !mmc_card_cmdq(mmc->card)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (cq_host->enabled)
+ goto out;
+
+ cmdq_runtime_pm_get(cq_host);
+ cqcfg = cmdq_readl(cq_host, CQCFG);
+ if (cqcfg & 0x1) {
+ pr_info("%s: %s: cq_host is already enabled\n",
+ mmc_hostname(mmc), __func__);
+ WARN_ON(1);
+ goto pm_ref_count;
+ }
+
+ if (cq_host->quirks & CMDQ_QUIRK_NO_DCMD)
+ dcmd_enable = false;
+ else
+ dcmd_enable = true;
+
+ cqcfg = ((cq_host->caps & CMDQ_TASK_DESC_SZ_128 ? CQ_TASK_DESC_SZ : 0) |
+ (dcmd_enable ? CQ_DCMD : 0));
+
+ cmdq_writel(cq_host, cqcfg, CQCFG);
+ /* enable CQ_HOST */
+ cmdq_writel(cq_host, cmdq_readl(cq_host, CQCFG) | CQ_ENABLE,
+ CQCFG);
+
+ if (!cq_host->desc_base ||
+ !cq_host->trans_desc_base) {
+ err = cmdq_host_alloc_tdl(cq_host);
+ if (err)
+ goto pm_ref_count;
+ }
+
+ cmdq_writel(cq_host, lower_32_bits(cq_host->desc_dma_base), CQTDLBA);
+ cmdq_writel(cq_host, upper_32_bits(cq_host->desc_dma_base), CQTDLBAU);
+
+ /*
+ * disable all vendor interrupts
+ * enable CMDQ interrupts
+ * enable the vendor error interrupts
+ */
+ if (cq_host->ops->clear_set_irqs)
+ cq_host->ops->clear_set_irqs(mmc, true);
+
+ cmdq_clear_set_irqs(cq_host, 0x0, CQ_INT_ALL);
+
+ /* cq_host would use this rca to address the card */
+ cmdq_writel(cq_host, mmc->card->rca, CQSSC2);
+
+ /* send QSR at lesser intervals than the default */
+ cmdq_writel(cq_host, SEND_QSR_INTERVAL, CQSSC1);
+
+ /* enable bkops exception indication */
+ if (mmc_card_configured_manual_bkops(mmc->card) &&
+ !mmc_card_configured_auto_bkops(mmc->card))
+ cmdq_writel(cq_host, cmdq_readl(cq_host, CQRMEM) | CQ_EXCEPTION,
+ CQRMEM);
+
+ /* ensure the writes are done before enabling CQE */
+ mb();
+
+ cq_host->enabled = true;
+ mmc_host_clr_cq_disable(mmc);
+
+ if (cq_host->ops->set_transfer_params)
+ cq_host->ops->set_transfer_params(mmc);
+
+ if (cq_host->ops->set_block_size)
+ cq_host->ops->set_block_size(cq_host->mmc);
+
+ if (cq_host->ops->set_data_timeout)
+ cq_host->ops->set_data_timeout(mmc, 0xf);
+
+ if (cq_host->ops->clear_set_dumpregs)
+ cq_host->ops->clear_set_dumpregs(mmc, 1);
+
+ if (cq_host->ops->enhanced_strobe_mask)
+ cq_host->ops->enhanced_strobe_mask(mmc, true);
+
+pm_ref_count:
+ cmdq_runtime_pm_put(cq_host);
+out:
+ MMC_TRACE(mmc, "%s: CQ enabled err: %d\n", __func__, err);
+ return err;
+}
+
+static void cmdq_disable_nosync(struct mmc_host *mmc, bool soft)
+{
+ struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc);
+
+ if (soft) {
+ cmdq_writel(cq_host, cmdq_readl(
+ cq_host, CQCFG) & ~(CQ_ENABLE),
+ CQCFG);
+ }
+ if (cq_host->ops->enhanced_strobe_mask)
+ cq_host->ops->enhanced_strobe_mask(mmc, false);
+
+ cq_host->enabled = false;
+ mmc_host_set_cq_disable(mmc);
+ MMC_TRACE(mmc, "%s: CQ disabled\n", __func__);
+}
+
+static void cmdq_disable(struct mmc_host *mmc, bool soft)
+{
+ struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc);
+
+ cmdq_runtime_pm_get(cq_host);
+ cmdq_disable_nosync(mmc, soft);
+ cmdq_runtime_pm_put(cq_host);
+}
+
+static void cmdq_reset(struct mmc_host *mmc, bool soft)
+{
+ struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc);
+ unsigned int cqcfg;
+ unsigned int tdlba;
+ unsigned int tdlbau;
+ unsigned int rca;
+ int ret;
+
+ cmdq_runtime_pm_get(cq_host);
+ cqcfg = cmdq_readl(cq_host, CQCFG);
+ tdlba = cmdq_readl(cq_host, CQTDLBA);
+ tdlbau = cmdq_readl(cq_host, CQTDLBAU);
+ rca = cmdq_readl(cq_host, CQSSC2);
+
+ cmdq_disable(mmc, true);
+
+ if (cq_host->ops->reset) {
+ ret = cq_host->ops->reset(mmc);
+ if (ret) {
+ pr_crit("%s: reset CMDQ controller: failed\n",
+ mmc_hostname(mmc));
+ BUG();
+ }
+ }
+
+ cmdq_writel(cq_host, tdlba, CQTDLBA);
+ cmdq_writel(cq_host, tdlbau, CQTDLBAU);
+
+ if (cq_host->ops->clear_set_irqs)
+ cq_host->ops->clear_set_irqs(mmc, true);
+
+ cmdq_clear_set_irqs(cq_host, 0x0, CQ_INT_ALL);
+
+ /* cq_host would use this rca to address the card */
+ cmdq_writel(cq_host, rca, CQSSC2);
+
+ /* ensure the writes are done before enabling CQE */
+ mb();
+
+ cmdq_writel(cq_host, cqcfg, CQCFG);
+ cmdq_runtime_pm_put(cq_host);
+ cq_host->enabled = true;
+ mmc_host_clr_cq_disable(mmc);
+}
+
+static void cmdq_prep_task_desc(struct mmc_request *mrq,
+ u64 *data, bool intr, bool qbr)
+{
+ struct mmc_cmdq_req *cmdq_req = mrq->cmdq_req;
+ u32 req_flags = cmdq_req->cmdq_req_flags;
+
+ pr_debug("%s: %s: data-tag: 0x%08x - dir: %d - prio: %d - cnt: 0x%08x - addr: 0x%llx\n",
+ mmc_hostname(mrq->host), __func__,
+ !!(req_flags & DAT_TAG), !!(req_flags & DIR),
+ !!(req_flags & PRIO), cmdq_req->data.blocks,
+ (u64)mrq->cmdq_req->blk_addr);
+
+ *data = VALID(1) |
+ END(1) |
+ INT(intr) |
+ ACT(0x5) |
+ FORCED_PROG(!!(req_flags & FORCED_PRG)) |
+ CONTEXT(mrq->cmdq_req->ctx_id) |
+ DATA_TAG(!!(req_flags & DAT_TAG)) |
+ DATA_DIR(!!(req_flags & DIR)) |
+ PRIORITY(!!(req_flags & PRIO)) |
+ QBAR(qbr) |
+ REL_WRITE(!!(req_flags & REL_WR)) |
+ BLK_COUNT(mrq->cmdq_req->data.blocks) |
+ BLK_ADDR((u64)mrq->cmdq_req->blk_addr);
+
+ MMC_TRACE(mrq->host,
+ "%s: Task: 0x%08x | Args: 0x%08x | cnt: 0x%08x\n", __func__,
+ lower_32_bits(*data),
+ upper_32_bits(*data),
+ mrq->cmdq_req->data.blocks);
+}
+
+static int cmdq_dma_map(struct mmc_host *host, struct mmc_request *mrq)
+{
+ int sg_count;
+ struct mmc_data *data = mrq->data;
+
+ if (!data)
+ return -EINVAL;
+
+ sg_count = dma_map_sg(mmc_dev(host), data->sg,
+ data->sg_len,
+ (data->flags & MMC_DATA_WRITE) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ if (!sg_count) {
+ pr_err("%s: sg-len: %d\n", __func__, data->sg_len);
+ return -ENOMEM;
+ }
+
+ return sg_count;
+}
+
+static void cmdq_set_tran_desc(u8 *desc, dma_addr_t addr, int len,
+ bool end, bool is_dma64)
+{
+ __le32 *attr = (__le32 __force *)desc;
+
+ *attr = (VALID(1) |
+ END(end ? 1 : 0) |
+ INT(0) |
+ ACT(0x4) |
+ DAT_LENGTH(len));
+
+ if (is_dma64) {
+ __le64 *dataddr = (__le64 __force *)(desc + 4);
+
+ dataddr[0] = cpu_to_le64(addr);
+ } else {
+ __le32 *dataddr = (__le32 __force *)(desc + 4);
+
+ dataddr[0] = cpu_to_le32(addr);
+ }
+}
+
+static int cmdq_prep_tran_desc(struct mmc_request *mrq,
+ struct cmdq_host *cq_host, int tag)
+{
+ struct mmc_data *data = mrq->data;
+ int i, sg_count, len;
+ bool end = false;
+ dma_addr_t addr;
+ u8 *desc;
+ struct scatterlist *sg;
+
+ sg_count = cmdq_dma_map(mrq->host, mrq);
+ if (sg_count < 0) {
+ pr_err("%s: %s: unable to map sg lists, %d\n",
+ mmc_hostname(mrq->host), __func__, sg_count);
+ return sg_count;
+ }
+
+ desc = get_trans_desc(cq_host, tag);
+ memset(desc, 0, cq_host->trans_desc_len * cq_host->mmc->max_segs);
+
+ for_each_sg(data->sg, sg, sg_count, i) {
+ addr = sg_dma_address(sg);
+ len = sg_dma_len(sg);
+
+ if ((i+1) == sg_count)
+ end = true;
+ cmdq_set_tran_desc(desc, addr, len, end, cq_host->dma64);
+ desc += cq_host->trans_desc_len;
+ }
+
+ pr_debug("%s: req: 0x%p tag: %d calc_trans_des: 0x%p sg-cnt: %d\n",
+ __func__, mrq->req, tag, desc, sg_count);
+
+ return 0;
+}
+
+static void cmdq_log_task_desc_history(struct cmdq_host *cq_host, u64 task,
+ bool is_dcmd)
+{
+ if (likely(!cq_host->mmc->cmdq_thist_enabled))
+ return;
+
+ if (!cq_host->thist) {
+ pr_err("%s: %s: CMDQ task history buffer not allocated\n",
+ mmc_hostname(cq_host->mmc), __func__);
+ return;
+ }
+
+ if (cq_host->thist_idx >= cq_host->num_slots)
+ cq_host->thist_idx = 0;
+
+ cq_host->thist[cq_host->thist_idx].is_dcmd = is_dcmd;
+ memcpy(&cq_host->thist[cq_host->thist_idx++].task,
+ &task, cq_host->task_desc_len);
+}
+
+static void cmdq_prep_dcmd_desc(struct mmc_host *mmc,
+ struct mmc_request *mrq)
+{
+ u64 *task_desc = NULL;
+ u64 data = 0;
+ u8 resp_type;
+ u8 *desc;
+ __le64 *dataddr;
+ struct cmdq_host *cq_host = mmc_cmdq_private(mmc);
+ u8 timing;
+
+ if (!(mrq->cmd->flags & MMC_RSP_PRESENT)) {
+ resp_type = 0x0;
+ timing = 0x1;
+ } else {
+ if (mrq->cmd->flags & MMC_RSP_BUSY) {
+ resp_type = 0x3;
+ timing = 0x0;
+ } else {
+ resp_type = 0x2;
+ timing = 0x1;
+ }
+ }
+
+ task_desc = (__le64 __force *)get_desc(cq_host, cq_host->dcmd_slot);
+ memset(task_desc, 0, cq_host->task_desc_len);
+ data |= (VALID(1) |
+ END(1) |
+ INT(1) |
+ QBAR(1) |
+ ACT(0x5) |
+ CMD_INDEX(mrq->cmd->opcode) |
+ CMD_TIMING(timing) | RESP_TYPE(resp_type));
+ *task_desc |= data;
+ desc = (u8 *)task_desc;
+ pr_debug("cmdq: dcmd: cmd: %d timing: %d resp: %d\n",
+ mrq->cmd->opcode, timing, resp_type);
+ dataddr = (__le64 __force *)(desc + 4);
+ dataddr[0] = cpu_to_le64((u64)mrq->cmd->arg);
+ cmdq_log_task_desc_history(cq_host, *task_desc, true);
+ MMC_TRACE(mrq->host,
+ "%s: DCMD: Task: 0x%08x | Args: 0x%08x\n",
+ __func__,
+ lower_32_bits(*task_desc),
+ upper_32_bits(*task_desc));
+}
+
+static void cmdq_pm_qos_vote(struct sdhci_host *host, struct mmc_request *mrq)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ sdhci_msm_pm_qos_cpu_vote(host,
+ msm_host->pdata->pm_qos_data.cmdq_latency, mrq->req->cpu);
+}
+
+static void cmdq_pm_qos_unvote(struct sdhci_host *host, struct mmc_request *mrq)
+{
+ /* use async as we're inside an atomic context (soft-irq) */
+ sdhci_msm_pm_qos_cpu_unvote(host, mrq->req->cpu, true);
+}
+
+static int cmdq_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ int err = 0;
+ u64 data = 0;
+ u64 *task_desc = NULL;
+ u32 tag = mrq->cmdq_req->tag;
+ struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc);
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ if (!cq_host->enabled) {
+ pr_err("%s: CMDQ host not enabled yet !!!\n",
+ mmc_hostname(mmc));
+ err = -EINVAL;
+ goto out;
+ }
+
+ cmdq_runtime_pm_get(cq_host);
+
+ if (mrq->cmdq_req->cmdq_req_flags & DCMD) {
+ cmdq_prep_dcmd_desc(mmc, mrq);
+ cq_host->mrq_slot[DCMD_SLOT] = mrq;
+ /* DCMD's are always issued on a fixed slot */
+ tag = DCMD_SLOT;
+ goto ring_doorbell;
+ }
+
+ task_desc = (__le64 __force *)get_desc(cq_host, tag);
+
+ cmdq_prep_task_desc(mrq, &data, 1,
+ (mrq->cmdq_req->cmdq_req_flags & QBR));
+ *task_desc = cpu_to_le64(data);
+ cmdq_log_task_desc_history(cq_host, *task_desc, false);
+
+ err = cmdq_prep_tran_desc(mrq, cq_host, tag);
+ if (err) {
+ pr_err("%s: %s: failed to setup tx desc: %d\n",
+ mmc_hostname(mmc), __func__, err);
+ goto out;
+ }
+
+ cq_host->mrq_slot[tag] = mrq;
+
+ /* PM QoS */
+ sdhci_msm_pm_qos_irq_vote(host);
+ cmdq_pm_qos_vote(host, mrq);
+ring_doorbell:
+ /* Ensure the task descriptor list is flushed before ringing doorbell */
+ wmb();
+ if (cmdq_readl(cq_host, CQTDBR) & (1 << tag)) {
+ cmdq_dumpregs(cq_host);
+ BUG_ON(1);
+ }
+ MMC_TRACE(mmc, "%s: tag: %d\n", __func__, tag);
+ cmdq_writel(cq_host, 1 << tag, CQTDBR);
+ /* Commit the doorbell write immediately */
+ wmb();
+
+out:
+ return err;
+}
+
+static void cmdq_finish_data(struct mmc_host *mmc, unsigned int tag)
+{
+ struct mmc_request *mrq;
+ struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc);
+
+ mrq = get_req_by_tag(cq_host, tag);
+ if (tag == cq_host->dcmd_slot)
+ mrq->cmd->resp[0] = cmdq_readl(cq_host, CQCRDCT);
+
+ if (mrq->cmdq_req->cmdq_req_flags & DCMD)
+ cmdq_writel(cq_host, cmdq_readl(cq_host, CQ_VENDOR_CFG) |
+ CMDQ_SEND_STATUS_TRIGGER, CQ_VENDOR_CFG);
+
+ cmdq_runtime_pm_put(cq_host);
+ mrq->done(mrq);
+}
+
+irqreturn_t cmdq_irq(struct mmc_host *mmc, int err)
+{
+ u32 status;
+ unsigned long tag = 0, comp_status;
+ struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc);
+ unsigned long err_info = 0;
+ struct mmc_request *mrq;
+ int ret;
+ u32 dbr_set = 0;
+
+ status = cmdq_readl(cq_host, CQIS);
+
+ if (!status && !err)
+ return IRQ_NONE;
+ MMC_TRACE(mmc, "%s: CQIS: 0x%x err: %d\n",
+ __func__, status, err);
+
+ if (err || (status & CQIS_RED)) {
+ err_info = cmdq_readl(cq_host, CQTERRI);
+ pr_err("%s: err: %d status: 0x%08x task-err-info (0x%08lx)\n",
+ mmc_hostname(mmc), err, status, err_info);
+
+ /*
+ * Need to halt CQE in case of error in interrupt context itself
+ * otherwise CQE may proceed with sending CMD to device even if
+ * CQE/card is in error state.
+ * CMDQ error handling will make sure that it is unhalted after
+ * handling all the errors.
+ */
+ ret = cmdq_halt_poll(mmc, true);
+ if (ret)
+ pr_err("%s: %s: halt failed ret=%d\n",
+ mmc_hostname(mmc), __func__, ret);
+
+ /*
+ * Clear the CQIS after halting incase of error. This is done
+ * because if CQIS is cleared before halting, the CQ will
+ * continue with issueing commands for rest of requests with
+ * Doorbell rung. This will overwrite the Resp Arg register.
+ * So CQ must be halted first and then CQIS cleared incase
+ * of error
+ */
+ cmdq_writel(cq_host, status, CQIS);
+
+ cmdq_dumpregs(cq_host);
+
+ if (!err_info) {
+ /*
+ * It may so happen sometimes for few errors(like ADMA)
+ * that HW cannot give CQTERRI info.
+ * Thus below is a HW WA for recovering from such
+ * scenario.
+ * - To halt/disable CQE and do reset_all.
+ * Since there is no way to know which tag would
+ * have caused such error, so check for any first
+ * bit set in doorbell and proceed with an error.
+ */
+ dbr_set = cmdq_readl(cq_host, CQTDBR);
+ if (!dbr_set) {
+ pr_err("%s: spurious/force error interrupt\n",
+ mmc_hostname(mmc));
+ cmdq_halt_poll(mmc, false);
+ mmc_host_clr_halt(mmc);
+ return IRQ_HANDLED;
+ }
+
+ tag = ffs(dbr_set) - 1;
+ pr_err("%s: error tag selected: tag = %lu\n",
+ mmc_hostname(mmc), tag);
+ mrq = get_req_by_tag(cq_host, tag);
+ if (mrq->data)
+ mrq->data->error = err;
+ else
+ mrq->cmd->error = err;
+ /*
+ * Get ADMA descriptor memory in case of ADMA
+ * error for debug.
+ */
+ if (err == -EIO)
+ cmdq_dump_adma_mem(cq_host);
+ goto skip_cqterri;
+ }
+
+ if (err_info & CQ_RMEFV) {
+ tag = GET_CMD_ERR_TAG(err_info);
+ pr_err("%s: CMD err tag: %lu\n", __func__, tag);
+
+ mrq = get_req_by_tag(cq_host, tag);
+ /* CMD44/45/46/47 will not have a valid cmd */
+ if (mrq->cmd)
+ mrq->cmd->error = err;
+ else
+ mrq->data->error = err;
+ } else {
+ tag = GET_DAT_ERR_TAG(err_info);
+ pr_err("%s: Dat err tag: %lu\n", __func__, tag);
+ mrq = get_req_by_tag(cq_host, tag);
+ mrq->data->error = err;
+ }
+
+skip_cqterri:
+ /*
+ * If CQE halt fails then, disable CQE
+ * from processing any further requests
+ */
+ if (ret) {
+ cmdq_disable_nosync(mmc, true);
+ /*
+ * Enable legacy interrupts as CQE halt has failed.
+ * This is needed to send legacy commands like status
+ * cmd as part of error handling work.
+ */
+ if (cq_host->ops->clear_set_irqs)
+ cq_host->ops->clear_set_irqs(mmc, false);
+ }
+
+ /*
+ * CQE detected a response error from device
+ * In most cases, this would require a reset.
+ */
+ if (status & CQIS_RED) {
+ /*
+ * will check if the RED error is due to a bkops
+ * exception once the queue is empty
+ */
+ BUG_ON(!mmc->card);
+ if (mmc_card_configured_manual_bkops(mmc->card) ||
+ mmc_card_configured_auto_bkops(mmc->card))
+ mmc->card->bkops.needs_check = true;
+
+ mrq->cmdq_req->resp_err = true;
+ pr_err("%s: Response error (0x%08x) from card !!!",
+ mmc_hostname(mmc), cmdq_readl(cq_host, CQCRA));
+
+ } else {
+ mrq->cmdq_req->resp_idx = cmdq_readl(cq_host, CQCRI);
+ mrq->cmdq_req->resp_arg = cmdq_readl(cq_host, CQCRA);
+ }
+
+ cmdq_finish_data(mmc, tag);
+ } else {
+ cmdq_writel(cq_host, status, CQIS);
+ }
+
+ if (status & CQIS_TCC) {
+ /* read CQTCN and complete the request */
+ comp_status = cmdq_readl(cq_host, CQTCN);
+ if (!comp_status)
+ goto out;
+ /*
+ * The CQTCN must be cleared before notifying req completion
+ * to upper layers to avoid missing completion notification
+ * of new requests with the same tag.
+ */
+ cmdq_writel(cq_host, comp_status, CQTCN);
+ /*
+ * A write memory barrier is necessary to guarantee that CQTCN
+ * gets cleared first before next doorbell for the same tag is
+ * set but that is already achieved by the barrier present
+ * before setting doorbell, hence one is not needed here.
+ */
+ for_each_set_bit(tag, &comp_status, cq_host->num_slots) {
+ /* complete the corresponding mrq */
+ pr_debug("%s: completing tag -> %lu\n",
+ mmc_hostname(mmc), tag);
+ MMC_TRACE(mmc, "%s: completing tag -> %lu\n",
+ __func__, tag);
+ cmdq_finish_data(mmc, tag);
+ }
+ }
+
+ if (status & CQIS_HAC) {
+ if (cq_host->ops->post_cqe_halt)
+ cq_host->ops->post_cqe_halt(mmc);
+ /* halt done: re-enable legacy interrupts */
+ if (cq_host->ops->clear_set_irqs)
+ cq_host->ops->clear_set_irqs(mmc, false);
+ /* halt is completed, wakeup waiting thread */
+ complete(&cq_host->halt_comp);
+ }
+
+out:
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(cmdq_irq);
+
+/* cmdq_halt_poll - Halting CQE using polling method.
+ * @mmc: struct mmc_host
+ * @halt: bool halt
+ * This is used mainly from interrupt context to halt/unhalt
+ * CQE engine.
+ */
+static int cmdq_halt_poll(struct mmc_host *mmc, bool halt)
+{
+ struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc);
+ int retries = 100;
+
+ if (!halt) {
+ if (cq_host->ops->set_data_timeout)
+ cq_host->ops->set_data_timeout(mmc, 0xf);
+ if (cq_host->ops->clear_set_irqs)
+ cq_host->ops->clear_set_irqs(mmc, true);
+ cmdq_writel(cq_host, cmdq_readl(cq_host, CQCTL) & ~HALT,
+ CQCTL);
+ return 0;
+ }
+
+ cmdq_set_halt_irq(cq_host, false);
+ cmdq_writel(cq_host, cmdq_readl(cq_host, CQCTL) | HALT, CQCTL);
+ while (retries) {
+ if (!(cmdq_readl(cq_host, CQCTL) & HALT)) {
+ udelay(5);
+ retries--;
+ continue;
+ } else {
+ if (cq_host->ops->post_cqe_halt)
+ cq_host->ops->post_cqe_halt(mmc);
+ /* halt done: re-enable legacy interrupts */
+ if (cq_host->ops->clear_set_irqs)
+ cq_host->ops->clear_set_irqs(mmc,
+ false);
+ mmc_host_set_halt(mmc);
+ break;
+ }
+ }
+ cmdq_set_halt_irq(cq_host, true);
+ return retries ? 0 : -ETIMEDOUT;
+}
+
+/* May sleep */
+static int cmdq_halt(struct mmc_host *mmc, bool halt)
+{
+ struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc);
+ u32 ret = 0;
+ u32 config = 0;
+ int retries = 3;
+
+ cmdq_runtime_pm_get(cq_host);
+ if (halt) {
+ while (retries) {
+ cmdq_writel(cq_host, cmdq_readl(cq_host, CQCTL) | HALT,
+ CQCTL);
+ ret = wait_for_completion_timeout(&cq_host->halt_comp,
+ msecs_to_jiffies(HALT_TIMEOUT_MS));
+ if (!ret) {
+ pr_warn("%s: %s: HAC int timeout\n",
+ mmc_hostname(mmc), __func__);
+ if ((cmdq_readl(cq_host, CQCTL) & HALT)) {
+ /*
+ * Don't retry if CQE is halted but irq
+ * is not triggered in timeout period.
+ * And since we are returning error,
+ * un-halt CQE. Since irq was not fired
+ * yet, no need to set other params
+ */
+ retries = 0;
+ config = cmdq_readl(cq_host, CQCTL);
+ config &= ~HALT;
+ cmdq_writel(cq_host, config, CQCTL);
+ } else {
+ pr_warn("%s: %s: retryng halt (%d)\n",
+ mmc_hostname(mmc), __func__,
+ retries);
+ retries--;
+ continue;
+ }
+ } else {
+ MMC_TRACE(mmc, "%s: halt done , retries: %d\n",
+ __func__, retries);
+ break;
+ }
+ }
+ ret = retries ? 0 : -ETIMEDOUT;
+ } else {
+ if (cq_host->ops->set_transfer_params)
+ cq_host->ops->set_transfer_params(mmc);
+ if (cq_host->ops->set_block_size)
+ cq_host->ops->set_block_size(mmc);
+ if (cq_host->ops->set_data_timeout)
+ cq_host->ops->set_data_timeout(mmc, 0xf);
+ if (cq_host->ops->clear_set_irqs)
+ cq_host->ops->clear_set_irqs(mmc, true);
+ MMC_TRACE(mmc, "%s: unhalt done\n", __func__);
+ cmdq_writel(cq_host, cmdq_readl(cq_host, CQCTL) & ~HALT,
+ CQCTL);
+ }
+ cmdq_runtime_pm_put(cq_host);
+ return ret;
+}
+
+static void cmdq_post_req(struct mmc_host *mmc, int tag, int err)
+{
+ struct cmdq_host *cq_host;
+ struct mmc_request *mrq;
+ struct mmc_data *data;
+ struct sdhci_host *sdhci_host = mmc_priv(mmc);
+
+ if (WARN_ON(!mmc))
+ return;
+
+ cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc);
+ mrq = get_req_by_tag(cq_host, tag);
+ data = mrq->data;
+
+ if (data) {
+ data->error = err;
+ dma_unmap_sg(mmc_dev(mmc), data->sg, data->sg_len,
+ (data->flags & MMC_DATA_READ) ?
+ DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ if (err)
+ data->bytes_xfered = 0;
+ else
+ data->bytes_xfered = blk_rq_bytes(mrq->req);
+
+ /* we're in atomic context (soft-irq) so unvote async. */
+ sdhci_msm_pm_qos_irq_unvote(sdhci_host, true);
+ cmdq_pm_qos_unvote(sdhci_host, mrq);
+ }
+}
+
+static void cmdq_dumpstate(struct mmc_host *mmc)
+{
+ struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc);
+ cmdq_runtime_pm_get(cq_host);
+ cmdq_dumpregs(cq_host);
+ cmdq_runtime_pm_put(cq_host);
+}
+
+static int cmdq_late_init(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ /*
+ * TODO: This should basically move to something like "sdhci-cmdq-msm"
+ * for msm specific implementation.
+ */
+ sdhci_msm_pm_qos_irq_init(host);
+
+ if (msm_host->pdata->pm_qos_data.cmdq_valid)
+ sdhci_msm_pm_qos_cpu_init(host,
+ msm_host->pdata->pm_qos_data.cmdq_latency);
+ return 0;
+}
+
+static const struct mmc_cmdq_host_ops cmdq_host_ops = {
+ .init = cmdq_late_init,
+ .enable = cmdq_enable,
+ .disable = cmdq_disable,
+ .request = cmdq_request,
+ .post_req = cmdq_post_req,
+ .halt = cmdq_halt,
+ .reset = cmdq_reset,
+ .dumpstate = cmdq_dumpstate,
+};
+
+struct cmdq_host *cmdq_pltfm_init(struct platform_device *pdev)
+{
+ struct cmdq_host *cq_host;
+ struct resource *cmdq_memres = NULL;
+
+ /* check and setup CMDQ interface */
+ cmdq_memres = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "cmdq_mem");
+ if (!cmdq_memres) {
+ dev_dbg(&pdev->dev, "CMDQ not supported\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ cq_host = kzalloc(sizeof(*cq_host), GFP_KERNEL);
+ if (!cq_host) {
+ dev_err(&pdev->dev, "failed to allocate memory for CMDQ\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ cq_host->mmio = devm_ioremap(&pdev->dev,
+ cmdq_memres->start,
+ resource_size(cmdq_memres));
+ if (!cq_host->mmio) {
+ dev_err(&pdev->dev, "failed to remap cmdq regs\n");
+ kfree(cq_host);
+ return ERR_PTR(-EBUSY);
+ }
+ dev_dbg(&pdev->dev, "CMDQ ioremap: done\n");
+
+ return cq_host;
+}
+EXPORT_SYMBOL(cmdq_pltfm_init);
+
+int cmdq_init(struct cmdq_host *cq_host, struct mmc_host *mmc,
+ bool dma64)
+{
+ int err = 0;
+
+ cq_host->dma64 = dma64;
+ cq_host->mmc = mmc;
+ cq_host->mmc->cmdq_private = cq_host;
+
+ cq_host->num_slots = NUM_SLOTS;
+ cq_host->dcmd_slot = DCMD_SLOT;
+
+ mmc->cmdq_ops = &cmdq_host_ops;
+ mmc->num_cq_slots = NUM_SLOTS;
+ mmc->dcmd_cq_slot = DCMD_SLOT;
+
+ cq_host->mrq_slot = kzalloc(sizeof(cq_host->mrq_slot) *
+ cq_host->num_slots, GFP_KERNEL);
+ if (!cq_host->mrq_slot)
+ return -ENOMEM;
+
+ init_completion(&cq_host->halt_comp);
+ return err;
+}
+EXPORT_SYMBOL(cmdq_init);
diff --git a/drivers/mmc/host/cmdq_hci.h b/drivers/mmc/host/cmdq_hci.h
new file mode 100644
index 0000000..5347b3ab
--- /dev/null
+++ b/drivers/mmc/host/cmdq_hci.h
@@ -0,0 +1,233 @@
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This 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_MMC_CQ_HCI_H
+#define LINUX_MMC_CQ_HCI_H
+#include <linux/mmc/core.h>
+
+/* registers */
+/* version */
+#define CQVER 0x00
+/* capabilities */
+#define CQCAP 0x04
+/* configuration */
+#define CQCFG 0x08
+#define CQ_DCMD 0x00001000
+#define CQ_TASK_DESC_SZ 0x00000100
+#define CQ_ENABLE 0x00000001
+
+/* control */
+#define CQCTL 0x0C
+#define CLEAR_ALL_TASKS 0x00000100
+#define HALT 0x00000001
+
+/* interrupt status */
+#define CQIS 0x10
+#define CQIS_HAC (1 << 0)
+#define CQIS_TCC (1 << 1)
+#define CQIS_RED (1 << 2)
+#define CQIS_TCL (1 << 3)
+
+/* interrupt status enable */
+#define CQISTE 0x14
+
+/* interrupt signal enable */
+#define CQISGE 0x18
+
+/* interrupt coalescing */
+#define CQIC 0x1C
+#define CQIC_ENABLE (1 << 31)
+#define CQIC_RESET (1 << 16)
+#define CQIC_ICCTHWEN (1 << 15)
+#define CQIC_ICCTH(x) ((x & 0x1F) << 8)
+#define CQIC_ICTOVALWEN (1 << 7)
+#define CQIC_ICTOVAL(x) (x & 0x7F)
+
+/* task list base address */
+#define CQTDLBA 0x20
+
+/* task list base address upper */
+#define CQTDLBAU 0x24
+
+/* door-bell */
+#define CQTDBR 0x28
+
+/* task completion notification */
+#define CQTCN 0x2C
+
+/* device queue status */
+#define CQDQS 0x30
+
+/* device pending tasks */
+#define CQDPT 0x34
+
+/* task clear */
+#define CQTCLR 0x38
+
+/* send status config 1 */
+#define CQSSC1 0x40
+/*
+ * Value n means CQE would send CMD13 during the transfer of data block
+ * BLOCK_CNT-n
+ */
+#define SEND_QSR_INTERVAL 0x70001
+
+/* send status config 2 */
+#define CQSSC2 0x44
+
+/* response for dcmd */
+#define CQCRDCT 0x48
+
+/* response mode error mask */
+#define CQRMEM 0x50
+#define CQ_EXCEPTION (1 << 6)
+
+/* task error info */
+#define CQTERRI 0x54
+
+/* CQTERRI bit fields */
+#define CQ_RMECI 0x1F
+#define CQ_RMETI (0x1F << 8)
+#define CQ_RMEFV (1 << 15)
+#define CQ_DTECI (0x3F << 16)
+#define CQ_DTETI (0x1F << 24)
+#define CQ_DTEFV (1 << 31)
+
+#define GET_CMD_ERR_TAG(__r__) ((__r__ & CQ_RMETI) >> 8)
+#define GET_DAT_ERR_TAG(__r__) ((__r__ & CQ_DTETI) >> 24)
+
+/* command response index */
+#define CQCRI 0x58
+
+/* command response argument */
+#define CQCRA 0x5C
+
+#define CQ_INT_ALL 0xF
+#define CQIC_DEFAULT_ICCTH 31
+#define CQIC_DEFAULT_ICTOVAL 1
+
+/* attribute fields */
+#define VALID(x) ((x & 1) << 0)
+#define END(x) ((x & 1) << 1)
+#define INT(x) ((x & 1) << 2)
+#define ACT(x) ((x & 0x7) << 3)
+
+/* data command task descriptor fields */
+#define FORCED_PROG(x) ((x & 1) << 6)
+#define CONTEXT(x) ((x & 0xF) << 7)
+#define DATA_TAG(x) ((x & 1) << 11)
+#define DATA_DIR(x) ((x & 1) << 12)
+#define PRIORITY(x) ((x & 1) << 13)
+#define QBAR(x) ((x & 1) << 14)
+#define REL_WRITE(x) ((x & 1) << 15)
+#define BLK_COUNT(x) ((x & 0xFFFF) << 16)
+#define BLK_ADDR(x) ((x & 0xFFFFFFFF) << 32)
+
+/* direct command task descriptor fields */
+#define CMD_INDEX(x) ((x & 0x3F) << 16)
+#define CMD_TIMING(x) ((x & 1) << 22)
+#define RESP_TYPE(x) ((x & 0x3) << 23)
+
+/* transfer descriptor fields */
+#define DAT_LENGTH(x) ((x & 0xFFFF) << 16)
+#define DAT_ADDR_LO(x) ((x & 0xFFFFFFFF) << 32)
+#define DAT_ADDR_HI(x) ((x & 0xFFFFFFFF) << 0)
+
+#define CQ_VENDOR_CFG 0x100
+#define CMDQ_SEND_STATUS_TRIGGER (1 << 31)
+
+struct task_history {
+ u64 task;
+ bool is_dcmd;
+};
+
+struct cmdq_host {
+ const struct cmdq_host_ops *ops;
+ void __iomem *mmio;
+ struct mmc_host *mmc;
+
+ /* 64 bit DMA */
+ bool dma64;
+ int num_slots;
+
+ u32 dcmd_slot;
+ u32 caps;
+#define CMDQ_TASK_DESC_SZ_128 0x1
+
+ u32 quirks;
+#define CMDQ_QUIRK_SHORT_TXFR_DESC_SZ 0x1
+#define CMDQ_QUIRK_NO_DCMD 0x2
+
+ bool enabled;
+ bool halted;
+ bool init_done;
+
+ u8 *desc_base;
+
+ /* total descriptor size */
+ u8 slot_sz;
+
+ /* 64/128 bit depends on CQCFG */
+ u8 task_desc_len;
+
+ /* 64 bit on 32-bit arch, 128 bit on 64-bit */
+ u8 link_desc_len;
+
+ u8 *trans_desc_base;
+ /* same length as transfer descriptor */
+ u8 trans_desc_len;
+
+ dma_addr_t desc_dma_base;
+ dma_addr_t trans_desc_dma_base;
+
+ struct task_history *thist;
+ u8 thist_idx;
+
+ struct completion halt_comp;
+ struct mmc_request **mrq_slot;
+ void *private;
+};
+
+struct cmdq_host_ops {
+ void (*set_transfer_params)(struct mmc_host *mmc);
+ void (*set_data_timeout)(struct mmc_host *mmc, u32 val);
+ void (*clear_set_irqs)(struct mmc_host *mmc, bool clear);
+ void (*set_block_size)(struct mmc_host *mmc);
+ void (*dump_vendor_regs)(struct mmc_host *mmc);
+ void (*write_l)(struct cmdq_host *host, u32 val, int reg);
+ u32 (*read_l)(struct cmdq_host *host, int reg);
+ void (*clear_set_dumpregs)(struct mmc_host *mmc, bool set);
+ void (*enhanced_strobe_mask)(struct mmc_host *mmc, bool set);
+ int (*reset)(struct mmc_host *mmc);
+ void (*post_cqe_halt)(struct mmc_host *mmc);
+};
+
+static inline void cmdq_writel(struct cmdq_host *host, u32 val, int reg)
+{
+ if (unlikely(host->ops && host->ops->write_l))
+ host->ops->write_l(host, val, reg);
+ else
+ writel_relaxed(val, host->mmio + reg);
+}
+
+static inline u32 cmdq_readl(struct cmdq_host *host, int reg)
+{
+ if (unlikely(host->ops && host->ops->read_l))
+ return host->ops->read_l(host, reg);
+ else
+ return readl_relaxed(host->mmio + reg);
+}
+
+extern irqreturn_t cmdq_irq(struct mmc_host *mmc, int err);
+extern int cmdq_init(struct cmdq_host *cq_host, struct mmc_host *mmc,
+ bool dma64);
+extern struct cmdq_host *cmdq_pltfm_init(struct platform_device *pdev);
+#endif
diff --git a/drivers/mmc/host/sdhci-msm-ice.c b/drivers/mmc/host/sdhci-msm-ice.c
new file mode 100644
index 0000000..ba6e51c
--- /dev/null
+++ b/drivers/mmc/host/sdhci-msm-ice.c
@@ -0,0 +1,444 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This 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 "sdhci-msm-ice.h"
+
+static void sdhci_msm_ice_success_cb(void *host_ctrl,
+ enum ice_event_completion evt)
+{
+ struct sdhci_msm_host *msm_host = (struct sdhci_msm_host *)host_ctrl;
+
+ if ((msm_host->ice.state == SDHCI_MSM_ICE_STATE_DISABLED &&
+ evt == ICE_INIT_COMPLETION) || (msm_host->ice.state ==
+ SDHCI_MSM_ICE_STATE_SUSPENDED && evt == ICE_RESUME_COMPLETION))
+ msm_host->ice.state = SDHCI_MSM_ICE_STATE_ACTIVE;
+
+ complete(&msm_host->ice.async_done);
+}
+
+static void sdhci_msm_ice_error_cb(void *host_ctrl, u32 error)
+{
+ struct sdhci_msm_host *msm_host = (struct sdhci_msm_host *)host_ctrl;
+
+ dev_err(&msm_host->pdev->dev, "%s: Error in ice operation 0x%x",
+ __func__, error);
+
+ if (msm_host->ice.state == SDHCI_MSM_ICE_STATE_ACTIVE)
+ msm_host->ice.state = SDHCI_MSM_ICE_STATE_DISABLED;
+
+ complete(&msm_host->ice.async_done);
+}
+
+static struct platform_device *sdhci_msm_ice_get_pdevice(struct device *dev)
+{
+ struct device_node *node;
+ struct platform_device *ice_pdev = NULL;
+
+ node = of_parse_phandle(dev->of_node, SDHC_MSM_CRYPTO_LABEL, 0);
+ if (!node) {
+ dev_dbg(dev, "%s: sdhc-msm-crypto property not specified\n",
+ __func__);
+ goto out;
+ }
+ ice_pdev = qcom_ice_get_pdevice(node);
+out:
+ return ice_pdev;
+}
+
+static
+struct qcom_ice_variant_ops *sdhci_msm_ice_get_vops(struct device *dev)
+{
+ struct qcom_ice_variant_ops *ice_vops = NULL;
+ struct device_node *node;
+
+ node = of_parse_phandle(dev->of_node, SDHC_MSM_CRYPTO_LABEL, 0);
+ if (!node) {
+ dev_dbg(dev, "%s: sdhc-msm-crypto property not specified\n",
+ __func__);
+ goto out;
+ }
+ ice_vops = qcom_ice_get_variant_ops(node);
+ of_node_put(node);
+out:
+ return ice_vops;
+}
+
+static
+void sdhci_msm_enable_ice_hci(struct sdhci_host *host, bool enable)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ u32 config = 0;
+ u32 ice_cap = 0;
+
+ /*
+ * Enable the cryptographic support inside SDHC.
+ * This is a global config which needs to be enabled
+ * all the time.
+ * Only when it it is enabled, the ICE_HCI capability
+ * will get reflected in CQCAP register.
+ */
+ config = readl_relaxed(host->ioaddr + HC_VENDOR_SPECIFIC_FUNC4);
+
+ if (enable)
+ config &= ~DISABLE_CRYPTO;
+ else
+ config |= DISABLE_CRYPTO;
+ writel_relaxed(config, host->ioaddr + HC_VENDOR_SPECIFIC_FUNC4);
+
+ /*
+ * CQCAP register is in different register space from above
+ * ice global enable register. So a mb() is required to ensure
+ * above write gets completed before reading the CQCAP register.
+ */
+ mb();
+
+ /*
+ * Check if ICE HCI capability support is present
+ * If present, enable it.
+ */
+ ice_cap = readl_relaxed(msm_host->cryptoio + ICE_CQ_CAPABILITIES);
+ if (ice_cap & ICE_HCI_SUPPORT) {
+ config = readl_relaxed(msm_host->cryptoio + ICE_CQ_CONFIG);
+
+ if (enable)
+ config |= CRYPTO_GENERAL_ENABLE;
+ else
+ config &= ~CRYPTO_GENERAL_ENABLE;
+ writel_relaxed(config, msm_host->cryptoio + ICE_CQ_CONFIG);
+ }
+}
+
+int sdhci_msm_ice_get_dev(struct sdhci_host *host)
+{
+ struct device *sdhc_dev;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ if (!msm_host || !msm_host->pdev) {
+ pr_err("%s: invalid msm_host %p or msm_host->pdev\n",
+ __func__, msm_host);
+ return -EINVAL;
+ }
+
+ sdhc_dev = &msm_host->pdev->dev;
+ msm_host->ice.vops = sdhci_msm_ice_get_vops(sdhc_dev);
+ msm_host->ice.pdev = sdhci_msm_ice_get_pdevice(sdhc_dev);
+
+ if (msm_host->ice.pdev == ERR_PTR(-EPROBE_DEFER)) {
+ dev_err(sdhc_dev, "%s: ICE device not probed yet\n",
+ __func__);
+ msm_host->ice.pdev = NULL;
+ msm_host->ice.vops = NULL;
+ return -EPROBE_DEFER;
+ }
+
+ if (!msm_host->ice.pdev) {
+ dev_dbg(sdhc_dev, "%s: invalid platform device\n", __func__);
+ msm_host->ice.vops = NULL;
+ return -ENODEV;
+ }
+ if (!msm_host->ice.vops) {
+ dev_dbg(sdhc_dev, "%s: invalid ice vops\n", __func__);
+ msm_host->ice.pdev = NULL;
+ return -ENODEV;
+ }
+ msm_host->ice.state = SDHCI_MSM_ICE_STATE_DISABLED;
+ return 0;
+}
+
+static
+int sdhci_msm_ice_pltfm_init(struct sdhci_msm_host *msm_host)
+{
+ struct resource *ice_memres = NULL;
+ struct platform_device *pdev = msm_host->pdev;
+ int err = 0;
+
+ if (!msm_host->ice_hci_support)
+ goto out;
+ /*
+ * ICE HCI registers are present in cmdq register space.
+ * So map the cmdq mem for accessing ICE HCI registers.
+ */
+ ice_memres = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "cmdq_mem");
+ if (!ice_memres) {
+ dev_err(&pdev->dev, "Failed to get iomem resource for ice\n");
+ err = -EINVAL;
+ goto out;
+ }
+ msm_host->cryptoio = devm_ioremap(&pdev->dev,
+ ice_memres->start,
+ resource_size(ice_memres));
+ if (!msm_host->cryptoio) {
+ dev_err(&pdev->dev, "Failed to remap registers\n");
+ err = -ENOMEM;
+ }
+out:
+ return err;
+}
+
+int sdhci_msm_ice_init(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int err = 0;
+
+ init_completion(&msm_host->ice.async_done);
+ if (msm_host->ice.vops->config) {
+ err = msm_host->ice.vops->init(msm_host->ice.pdev,
+ msm_host,
+ sdhci_msm_ice_success_cb,
+ sdhci_msm_ice_error_cb);
+ if (err) {
+ pr_err("%s: ice init err %d\n",
+ mmc_hostname(host->mmc), err);
+ return err;
+ }
+ }
+
+ if (!wait_for_completion_timeout(&msm_host->ice.async_done,
+ msecs_to_jiffies(SDHCI_MSM_ICE_COMPLETION_TIMEOUT_MS))) {
+ pr_err("%s: ice init timedout after %d ms\n",
+ mmc_hostname(host->mmc),
+ SDHCI_MSM_ICE_COMPLETION_TIMEOUT_MS);
+ sdhci_msm_ice_print_regs(host);
+ return -ETIMEDOUT;
+ }
+
+ if (msm_host->ice.state != SDHCI_MSM_ICE_STATE_ACTIVE) {
+ pr_err("%s: ice is in invalid state %d\n",
+ mmc_hostname(host->mmc), msm_host->ice.state);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int sdhci_msm_ice_cfg(struct sdhci_host *host, struct mmc_request *mrq,
+ u32 slot)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int err = 0;
+ struct ice_data_setting ice_set;
+ sector_t lba = 0;
+ unsigned int ctrl_info_val = 0;
+ unsigned int bypass = SDHCI_MSM_ICE_ENABLE_BYPASS;
+ struct request *req;
+
+ if (msm_host->ice.state != SDHCI_MSM_ICE_STATE_ACTIVE) {
+ pr_err("%s: ice is in invalid state %d\n",
+ mmc_hostname(host->mmc), msm_host->ice.state);
+ return -EINVAL;
+ }
+
+ BUG_ON(!mrq);
+ memset(&ice_set, 0, sizeof(struct ice_data_setting));
+ req = mrq->req;
+ if (req) {
+ lba = req->__sector;
+ if (msm_host->ice.vops->config) {
+ err = msm_host->ice.vops->config(msm_host->ice.pdev,
+ req, &ice_set);
+ if (err) {
+ pr_err("%s: ice config failed %d\n",
+ mmc_hostname(host->mmc), err);
+ return err;
+ }
+ }
+ /* if writing data command */
+ if (rq_data_dir(req) == WRITE)
+ bypass = ice_set.encr_bypass ?
+ SDHCI_MSM_ICE_ENABLE_BYPASS :
+ SDHCI_MSM_ICE_DISABLE_BYPASS;
+ /* if reading data command */
+ else if (rq_data_dir(req) == READ)
+ bypass = ice_set.decr_bypass ?
+ SDHCI_MSM_ICE_ENABLE_BYPASS :
+ SDHCI_MSM_ICE_DISABLE_BYPASS;
+ pr_debug("%s: %s: slot %d encr_bypass %d bypass %d decr_bypass %d key_index %d\n",
+ mmc_hostname(host->mmc),
+ (rq_data_dir(req) == WRITE) ? "WRITE" : "READ",
+ slot, ice_set.encr_bypass, bypass,
+ ice_set.decr_bypass,
+ ice_set.crypto_data.key_index);
+ }
+
+ /* Configure ICE index */
+ ctrl_info_val =
+ (ice_set.crypto_data.key_index &
+ MASK_SDHCI_MSM_ICE_CTRL_INFO_KEY_INDEX)
+ << OFFSET_SDHCI_MSM_ICE_CTRL_INFO_KEY_INDEX;
+
+ /* Configure data unit size of transfer request */
+ ctrl_info_val |=
+ (SDHCI_MSM_ICE_TR_DATA_UNIT_512_B &
+ MASK_SDHCI_MSM_ICE_CTRL_INFO_CDU)
+ << OFFSET_SDHCI_MSM_ICE_CTRL_INFO_CDU;
+
+ /* Configure ICE bypass mode */
+ ctrl_info_val |=
+ (bypass & MASK_SDHCI_MSM_ICE_CTRL_INFO_BYPASS)
+ << OFFSET_SDHCI_MSM_ICE_CTRL_INFO_BYPASS;
+
+ writel_relaxed((lba & 0xFFFFFFFF),
+ host->ioaddr + CORE_VENDOR_SPEC_ICE_CTRL_INFO_1_n + 16 * slot);
+ writel_relaxed(((lba >> 32) & 0xFFFFFFFF),
+ host->ioaddr + CORE_VENDOR_SPEC_ICE_CTRL_INFO_2_n + 16 * slot);
+ writel_relaxed(ctrl_info_val,
+ host->ioaddr + CORE_VENDOR_SPEC_ICE_CTRL_INFO_3_n + 16 * slot);
+
+ /* Ensure ICE registers are configured before issuing SDHCI request */
+ mb();
+ return 0;
+}
+
+int sdhci_msm_ice_reset(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int err = 0;
+
+ if (msm_host->ice.state != SDHCI_MSM_ICE_STATE_ACTIVE) {
+ pr_err("%s: ice is in invalid state before reset %d\n",
+ mmc_hostname(host->mmc), msm_host->ice.state);
+ return -EINVAL;
+ }
+
+ init_completion(&msm_host->ice.async_done);
+
+ if (msm_host->ice.vops->reset) {
+ err = msm_host->ice.vops->reset(msm_host->ice.pdev);
+ if (err) {
+ pr_err("%s: ice reset failed %d\n",
+ mmc_hostname(host->mmc), err);
+ return err;
+ }
+ }
+
+ if (!wait_for_completion_timeout(&msm_host->ice.async_done,
+ msecs_to_jiffies(SDHCI_MSM_ICE_COMPLETION_TIMEOUT_MS))) {
+ pr_err("%s: ice reset timedout after %d ms\n",
+ mmc_hostname(host->mmc),
+ SDHCI_MSM_ICE_COMPLETION_TIMEOUT_MS);
+ sdhci_msm_ice_print_regs(host);
+ return -ETIMEDOUT;
+ }
+
+ if (msm_host->ice.state != SDHCI_MSM_ICE_STATE_ACTIVE) {
+ pr_err("%s: ice is in invalid state after reset %d\n",
+ mmc_hostname(host->mmc), msm_host->ice.state);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int sdhci_msm_ice_resume(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int err = 0;
+
+ if (msm_host->ice.state !=
+ SDHCI_MSM_ICE_STATE_SUSPENDED) {
+ pr_err("%s: ice is in invalid state before resume %d\n",
+ mmc_hostname(host->mmc), msm_host->ice.state);
+ return -EINVAL;
+ }
+
+ init_completion(&msm_host->ice.async_done);
+
+ if (msm_host->ice.vops->resume) {
+ err = msm_host->ice.vops->resume(msm_host->ice.pdev);
+ if (err) {
+ pr_err("%s: ice resume failed %d\n",
+ mmc_hostname(host->mmc), err);
+ return err;
+ }
+ }
+
+ if (!wait_for_completion_timeout(&msm_host->ice.async_done,
+ msecs_to_jiffies(SDHCI_MSM_ICE_COMPLETION_TIMEOUT_MS))) {
+ pr_err("%s: ice resume timedout after %d ms\n",
+ mmc_hostname(host->mmc),
+ SDHCI_MSM_ICE_COMPLETION_TIMEOUT_MS);
+ sdhci_msm_ice_print_regs(host);
+ return -ETIMEDOUT;
+ }
+
+ if (msm_host->ice.state != SDHCI_MSM_ICE_STATE_ACTIVE) {
+ pr_err("%s: ice is in invalid state after resume %d\n",
+ mmc_hostname(host->mmc), msm_host->ice.state);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int sdhci_msm_ice_suspend(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int err = 0;
+
+ if (msm_host->ice.state !=
+ SDHCI_MSM_ICE_STATE_ACTIVE) {
+ pr_err("%s: ice is in invalid state before resume %d\n",
+ mmc_hostname(host->mmc), msm_host->ice.state);
+ return -EINVAL;
+ }
+
+ if (msm_host->ice.vops->suspend) {
+ err = msm_host->ice.vops->suspend(msm_host->ice.pdev);
+ if (err) {
+ pr_err("%s: ice suspend failed %d\n",
+ mmc_hostname(host->mmc), err);
+ return -EINVAL;
+ }
+ }
+ msm_host->ice.state = SDHCI_MSM_ICE_STATE_SUSPENDED;
+ return 0;
+}
+
+int sdhci_msm_ice_get_status(struct sdhci_host *host, int *ice_status)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int stat = -EINVAL;
+
+ if (msm_host->ice.state != SDHCI_MSM_ICE_STATE_ACTIVE) {
+ pr_err("%s: ice is in invalid state %d\n",
+ mmc_hostname(host->mmc), msm_host->ice.state);
+ return -EINVAL;
+ }
+
+ if (msm_host->ice.vops->status) {
+ *ice_status = 0;
+ stat = msm_host->ice.vops->status(msm_host->ice.pdev);
+ if (stat < 0) {
+ pr_err("%s: ice get sts failed %d\n",
+ mmc_hostname(host->mmc), stat);
+ return -EINVAL;
+ }
+ *ice_status = stat;
+ }
+ return 0;
+}
+
+void sdhci_msm_ice_print_regs(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ if (msm_host->ice.vops->debug)
+ msm_host->ice.vops->debug(msm_host->ice.pdev);
+}
diff --git a/drivers/mmc/host/sdhci-msm-ice.h b/drivers/mmc/host/sdhci-msm-ice.h
new file mode 100644
index 0000000..88ef0e2
--- /dev/null
+++ b/drivers/mmc/host/sdhci-msm-ice.h
@@ -0,0 +1,150 @@
+/*
+ * 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
+ * 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 __SDHCI_MSM_ICE_H__
+#define __SDHCI_MSM_ICE_H__
+
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/async.h>
+#include <linux/blkdev.h>
+#include <crypto/ice.h>
+
+#include "sdhci-msm.h"
+
+#define SDHC_MSM_CRYPTO_LABEL "sdhc-msm-crypto"
+/* Timeout waiting for ICE initialization, that requires TZ access */
+#define SDHCI_MSM_ICE_COMPLETION_TIMEOUT_MS 500
+
+/*
+ * SDHCI host controller ICE registers. There are n [0..31]
+ * of each of these registers
+ */
+#define NUM_SDHCI_MSM_ICE_CTRL_INFO_n_REGS 32
+
+#define CORE_VENDOR_SPEC_ICE_CTRL 0x300
+#define CORE_VENDOR_SPEC_ICE_CTRL_INFO_1_n 0x304
+#define CORE_VENDOR_SPEC_ICE_CTRL_INFO_2_n 0x308
+#define CORE_VENDOR_SPEC_ICE_CTRL_INFO_3_n 0x30C
+
+/* ICE3.0 register which got added cmdq reg space */
+#define ICE_CQ_CAPABILITIES 0x04
+#define ICE_HCI_SUPPORT (1 << 28)
+#define ICE_CQ_CONFIG 0x08
+#define CRYPTO_GENERAL_ENABLE (1 << 1)
+
+/* ICE3.0 register which got added hc reg space */
+#define HC_VENDOR_SPECIFIC_FUNC4 0x260
+#define DISABLE_CRYPTO (1 << 15)
+#define HC_VENDOR_SPECIFIC_ICE_CTRL 0x800
+#define ICE_SW_RST_EN (1 << 0)
+
+/* SDHCI MSM ICE CTRL Info register offset */
+enum {
+ OFFSET_SDHCI_MSM_ICE_CTRL_INFO_BYPASS = 0,
+ OFFSET_SDHCI_MSM_ICE_CTRL_INFO_KEY_INDEX = 0x1,
+ OFFSET_SDHCI_MSM_ICE_CTRL_INFO_CDU = 0x6,
+};
+
+/* SDHCI MSM ICE CTRL Info register masks */
+enum {
+ MASK_SDHCI_MSM_ICE_CTRL_INFO_BYPASS = 0x1,
+ MASK_SDHCI_MSM_ICE_CTRL_INFO_KEY_INDEX = 0x1F,
+ MASK_SDHCI_MSM_ICE_CTRL_INFO_CDU = 0x7,
+};
+
+/* SDHCI MSM ICE encryption/decryption bypass state */
+enum {
+ SDHCI_MSM_ICE_DISABLE_BYPASS = 0,
+ SDHCI_MSM_ICE_ENABLE_BYPASS = 1,
+};
+
+/* SDHCI MSM ICE Crypto Data Unit of target DUN of Transfer Request */
+enum {
+ SDHCI_MSM_ICE_TR_DATA_UNIT_512_B = 0,
+ SDHCI_MSM_ICE_TR_DATA_UNIT_1_KB = 1,
+ SDHCI_MSM_ICE_TR_DATA_UNIT_2_KB = 2,
+ SDHCI_MSM_ICE_TR_DATA_UNIT_4_KB = 3,
+ SDHCI_MSM_ICE_TR_DATA_UNIT_8_KB = 4,
+ SDHCI_MSM_ICE_TR_DATA_UNIT_16_KB = 5,
+ SDHCI_MSM_ICE_TR_DATA_UNIT_32_KB = 6,
+ SDHCI_MSM_ICE_TR_DATA_UNIT_64_KB = 7,
+};
+
+/* SDHCI MSM ICE internal state */
+enum {
+ SDHCI_MSM_ICE_STATE_DISABLED = 0,
+ SDHCI_MSM_ICE_STATE_ACTIVE = 1,
+ SDHCI_MSM_ICE_STATE_SUSPENDED = 2,
+};
+
+/* crypto context fields in cmdq data command task descriptor */
+#define DATA_UNIT_NUM(x) (((u64)(x) & 0xFFFFFFFF) << 0)
+#define CRYPTO_CONFIG_INDEX(x) (((u64)(x) & 0xFF) << 32)
+#define CRYPTO_ENABLE(x) (((u64)(x) & 0x1) << 47)
+
+#ifdef CONFIG_MMC_SDHCI_MSM_ICE
+int sdhci_msm_ice_get_dev(struct sdhci_host *host);
+int sdhci_msm_ice_init(struct sdhci_host *host);
+int sdhci_msm_ice_cfg(struct sdhci_host *host, struct mmc_request *mrq,
+ u32 slot);
+int sdhci_msm_ice_reset(struct sdhci_host *host);
+int sdhci_msm_ice_resume(struct sdhci_host *host);
+int sdhci_msm_ice_suspend(struct sdhci_host *host);
+int sdhci_msm_ice_get_status(struct sdhci_host *host, int *ice_status);
+void sdhci_msm_ice_print_regs(struct sdhci_host *host);
+#else
+inline int sdhci_msm_ice_get_dev(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ if (msm_host) {
+ msm_host->ice.pdev = NULL;
+ msm_host->ice.vops = NULL;
+ }
+ return -ENODEV;
+}
+inline int sdhci_msm_ice_init(struct sdhci_host *host)
+{
+ return 0;
+}
+inline int sdhci_msm_ice_cfg(struct sdhci_host *host,
+ struct mmc_request *mrq, u32 slot)
+{
+ return 0;
+}
+inline int sdhci_msm_ice_reset(struct sdhci_host *host)
+{
+ return 0;
+}
+inline int sdhci_msm_ice_resume(struct sdhci_host *host)
+{
+ return 0;
+}
+inline int sdhci_msm_ice_suspend(struct sdhci_host *host)
+{
+ return 0;
+}
+inline int sdhci_msm_ice_get_status(struct sdhci_host *host,
+ int *ice_status)
+{
+ return 0;
+}
+inline void sdhci_msm_ice_print_regs(struct sdhci_host *host)
+{
+ return;
+}
+#endif /* CONFIG_MMC_SDHCI_MSM_ICE */
+#endif /* __SDHCI_MSM_ICE_H__ */
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index cf48a04..9650085 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -2,7 +2,7 @@
* drivers/mmc/host/sdhci-msm.c - Qualcomm Technologies, Inc. MSM SDHCI Platform
* driver source file
*
- * Copyright (c) 2012-2014, 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
@@ -21,6 +21,7 @@
#include <linux/mmc/sdio_func.h>
#include <linux/gfp.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/types.h>
@@ -31,16 +32,34 @@
#include <linux/delay.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
-#include <linux/mmc/mmc.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/dma-mapping.h>
#include <linux/iopoll.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/iopoll.h>
#include <linux/msm-bus.h>
+#include <linux/pm_runtime.h>
+#include <trace/events/mmc.h>
-#include "sdhci-pltfm.h"
+#include "sdhci-msm.h"
+#include "cmdq_hci.h"
+
+#define QOS_REMOVE_DELAY_MS 10
+#define CORE_POWER 0x0
+#define CORE_SW_RST (1 << 7)
#define SDHCI_VER_100 0x2B
+#define CORE_VERSION_STEP_MASK 0x0000FFFF
+#define CORE_VERSION_MINOR_MASK 0x0FFF0000
+#define CORE_VERSION_MINOR_SHIFT 16
+#define CORE_VERSION_MAJOR_MASK 0xF0000000
+#define CORE_VERSION_MAJOR_SHIFT 28
+#define CORE_VERSION_TARGET_MASK 0x000000FF
+#define SDHCI_MSM_VER_420 0x49
+
+#define SWITCHABLE_SIGNALLING_VOL (1 << 29)
+
#define CORE_VERSION_MAJOR_MASK 0xF0000000
#define CORE_VERSION_MAJOR_SHIFT 28
@@ -48,22 +67,6 @@
#define HC_MODE_EN 0x1
#define FF_CLK_SW_RST_DIS (1 << 13)
-#define CORE_GENERICS 0x70
-#define SWITCHABLE_SIGNALLING_VOL (1 << 29)
-
-#define CORE_POWER 0x0
-#define CORE_SW_RST (1 << 7)
-
-#define CORE_MCI_VERSION 0x050
-#define CORE_TESTBUS_CONFIG 0x0CC
-#define CORE_TESTBUS_ENA (1 << 3)
-#define CORE_SDCC_DEBUG_REG 0x124
-
-#define CORE_PWRCTL_STATUS 0xDC
-#define CORE_PWRCTL_MASK 0xE0
-#define CORE_PWRCTL_CLEAR 0xE4
-#define CORE_PWRCTL_CTL 0xE8
-
#define CORE_PWRCTL_BUS_OFF 0x01
#define CORE_PWRCTL_BUS_ON (1 << 1)
#define CORE_PWRCTL_IO_LOW (1 << 2)
@@ -77,7 +80,6 @@
#define INT_MASK 0xF
#define MAX_PHASES 16
-#define CORE_DLL_CONFIG 0x100
#define CORE_CMD_DAT_TRACK_SEL (1 << 0)
#define CORE_DLL_EN (1 << 16)
#define CORE_CDR_EN (1 << 17)
@@ -86,29 +88,30 @@
#define CORE_DLL_PDN (1 << 29)
#define CORE_DLL_RST (1 << 30)
-#define CORE_DLL_STATUS 0x108
#define CORE_DLL_LOCK (1 << 7)
+#define CORE_DDR_DLL_LOCK (1 << 11)
-#define CORE_VENDOR_SPEC 0x10C
-#define CORE_CLK_PWRSAVE (1 << 1)
-#define CORE_HC_MCLK_SEL_DFLT (2 << 8)
-#define CORE_HC_MCLK_SEL_HS400 (3 << 8)
-#define CORE_HC_MCLK_SEL_MASK (3 << 8)
-#define CORE_HC_AUTO_CMD21_EN (1 << 6)
+#define CORE_CLK_PWRSAVE (1 << 1)
+#define CORE_HC_MCLK_SEL_DFLT (2 << 8)
+#define CORE_HC_MCLK_SEL_HS400 (3 << 8)
+#define CORE_HC_MCLK_SEL_MASK (3 << 8)
+#define CORE_HC_AUTO_CMD21_EN (1 << 6)
+#define CORE_IO_PAD_PWR_SWITCH_EN (1 << 15)
#define CORE_IO_PAD_PWR_SWITCH (1 << 16)
#define CORE_HC_SELECT_IN_EN (1 << 18)
#define CORE_HC_SELECT_IN_HS400 (6 << 19)
#define CORE_HC_SELECT_IN_MASK (7 << 19)
+#define CORE_VENDOR_SPEC_POR_VAL 0xA1C
-#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11C
+#define HC_SW_RST_WAIT_IDLE_DIS (1 << 20)
+#define HC_SW_RST_REQ (1 << 21)
+#define CORE_ONE_MID_EN (1 << 25)
+
#define CORE_8_BIT_SUPPORT (1 << 18)
#define CORE_3_3V_SUPPORT (1 << 24)
#define CORE_3_0V_SUPPORT (1 << 25)
#define CORE_1_8V_SUPPORT (1 << 26)
-#define CORE_SYS_BUS_SUPPORT_64_BIT 28
-
-#define CORE_VENDOR_SPEC_ADMA_ERR_ADDR0 0x114
-#define CORE_VENDOR_SPEC_ADMA_ERR_ADDR1 0x118
+#define CORE_SYS_BUS_SUPPORT_64_BIT BIT(28)
#define CORE_CSR_CDC_CTLR_CFG0 0x130
#define CORE_SW_TRIG_FULL_CALIB (1 << 16)
@@ -129,30 +132,189 @@
#define CORE_CDC_ERROR_CODE_MASK 0x7000000
+#define CQ_CMD_DBG_RAM 0x110
+#define CQ_CMD_DBG_RAM_WA 0x150
+#define CQ_CMD_DBG_RAM_OL 0x154
+
#define CORE_CSR_CDC_GEN_CFG 0x178
#define CORE_CDC_SWITCH_BYPASS_OFF (1 << 0)
#define CORE_CDC_SWITCH_RC_EN (1 << 1)
-#define CORE_DDR_200_CFG 0x184
#define CORE_CDC_T4_DLY_SEL (1 << 0)
+#define CORE_CMDIN_RCLK_EN (1 << 1)
#define CORE_START_CDC_TRAFFIC (1 << 6)
-#define CORE_MCI_DATA_CNT 0x30
-#define CORE_MCI_STATUS 0x34
-#define CORE_MCI_FIFO_CNT 0x44
+#define CORE_PWRSAVE_DLL (1 << 3)
+#define CORE_CMDEN_HS400_INPUT_MASK_CNT (1 << 13)
-#define CORE_TESTBUS_SEL2_BIT 4
-#define CORE_TESTBUS_SEL2 (1 << CORE_TESTBUS_SEL2_BIT)
+#define CORE_DDR_CAL_EN (1 << 0)
+#define CORE_FLL_CYCLE_CNT (1 << 18)
+#define CORE_DLL_CLOCK_DISABLE (1 << 21)
-/* 8KB descriptors */
-#define SDHCI_MSM_MAX_SEGMENTS (1 << 13)
+#define DDR_CONFIG_POR_VAL 0x80040853
+#define DDR_CONFIG_PRG_RCLK_DLY_MASK 0x1FF
+#define DDR_CONFIG_PRG_RCLK_DLY 115
+#define DDR_CONFIG_2_POR_VAL 0x80040873
+
+/* 512 descriptors */
+#define SDHCI_MSM_MAX_SEGMENTS (1 << 9)
#define SDHCI_MSM_MMC_CLK_GATE_DELAY 200 /* msecs */
#define CORE_FREQ_100MHZ (100 * 1000 * 1000)
+#define TCXO_FREQ 19200000
#define INVALID_TUNING_PHASE -1
+#define sdhci_is_valid_gpio_wakeup_int(_h) ((_h)->pdata->sdiowakeup_irq >= 0)
-#define CORE_VERSION_TARGET_MASK 0x000000FF
+#define NUM_TUNING_PHASES 16
+#define MAX_DRV_TYPES_SUPPORTED_HS200 4
+#define MSM_AUTOSUSPEND_DELAY_MS 100
+
+struct sdhci_msm_offset {
+ u32 CORE_MCI_DATA_CNT;
+ u32 CORE_MCI_STATUS;
+ u32 CORE_MCI_FIFO_CNT;
+ u32 CORE_MCI_VERSION;
+ u32 CORE_GENERICS;
+ u32 CORE_TESTBUS_CONFIG;
+ u32 CORE_TESTBUS_SEL2_BIT;
+ u32 CORE_TESTBUS_ENA;
+ u32 CORE_TESTBUS_SEL2;
+ u32 CORE_PWRCTL_STATUS;
+ u32 CORE_PWRCTL_MASK;
+ u32 CORE_PWRCTL_CLEAR;
+ u32 CORE_PWRCTL_CTL;
+ u32 CORE_SDCC_DEBUG_REG;
+ u32 CORE_DLL_CONFIG;
+ u32 CORE_DLL_STATUS;
+ u32 CORE_VENDOR_SPEC;
+ u32 CORE_VENDOR_SPEC_ADMA_ERR_ADDR0;
+ u32 CORE_VENDOR_SPEC_ADMA_ERR_ADDR1;
+ u32 CORE_VENDOR_SPEC_FUNC2;
+ u32 CORE_VENDOR_SPEC_CAPABILITIES0;
+ u32 CORE_DDR_200_CFG;
+ u32 CORE_VENDOR_SPEC3;
+ u32 CORE_DLL_CONFIG_2;
+ u32 CORE_DDR_CONFIG;
+ u32 CORE_DDR_CONFIG_2;
+};
+
+struct sdhci_msm_offset sdhci_msm_offset_mci_removed = {
+ .CORE_MCI_DATA_CNT = 0x35C,
+ .CORE_MCI_STATUS = 0x324,
+ .CORE_MCI_FIFO_CNT = 0x308,
+ .CORE_MCI_VERSION = 0x318,
+ .CORE_GENERICS = 0x320,
+ .CORE_TESTBUS_CONFIG = 0x32C,
+ .CORE_TESTBUS_SEL2_BIT = 3,
+ .CORE_TESTBUS_ENA = (1 << 31),
+ .CORE_TESTBUS_SEL2 = (1 << 3),
+ .CORE_PWRCTL_STATUS = 0x240,
+ .CORE_PWRCTL_MASK = 0x244,
+ .CORE_PWRCTL_CLEAR = 0x248,
+ .CORE_PWRCTL_CTL = 0x24C,
+ .CORE_SDCC_DEBUG_REG = 0x358,
+ .CORE_DLL_CONFIG = 0x200,
+ .CORE_DLL_STATUS = 0x208,
+ .CORE_VENDOR_SPEC = 0x20C,
+ .CORE_VENDOR_SPEC_ADMA_ERR_ADDR0 = 0x214,
+ .CORE_VENDOR_SPEC_ADMA_ERR_ADDR1 = 0x218,
+ .CORE_VENDOR_SPEC_FUNC2 = 0x210,
+ .CORE_VENDOR_SPEC_CAPABILITIES0 = 0x21C,
+ .CORE_DDR_200_CFG = 0x224,
+ .CORE_VENDOR_SPEC3 = 0x250,
+ .CORE_DLL_CONFIG_2 = 0x254,
+ .CORE_DDR_CONFIG = 0x258,
+ .CORE_DDR_CONFIG_2 = 0x25C,
+};
+
+struct sdhci_msm_offset sdhci_msm_offset_mci_present = {
+ .CORE_MCI_DATA_CNT = 0x30,
+ .CORE_MCI_STATUS = 0x34,
+ .CORE_MCI_FIFO_CNT = 0x44,
+ .CORE_MCI_VERSION = 0x050,
+ .CORE_GENERICS = 0x70,
+ .CORE_TESTBUS_CONFIG = 0x0CC,
+ .CORE_TESTBUS_SEL2_BIT = 4,
+ .CORE_TESTBUS_ENA = (1 << 3),
+ .CORE_TESTBUS_SEL2 = (1 << 4),
+ .CORE_PWRCTL_STATUS = 0xDC,
+ .CORE_PWRCTL_MASK = 0xE0,
+ .CORE_PWRCTL_CLEAR = 0xE4,
+ .CORE_PWRCTL_CTL = 0xE8,
+ .CORE_SDCC_DEBUG_REG = 0x124,
+ .CORE_DLL_CONFIG = 0x100,
+ .CORE_DLL_STATUS = 0x108,
+ .CORE_VENDOR_SPEC = 0x10C,
+ .CORE_VENDOR_SPEC_ADMA_ERR_ADDR0 = 0x114,
+ .CORE_VENDOR_SPEC_ADMA_ERR_ADDR1 = 0x118,
+ .CORE_VENDOR_SPEC_FUNC2 = 0x110,
+ .CORE_VENDOR_SPEC_CAPABILITIES0 = 0x11C,
+ .CORE_DDR_200_CFG = 0x184,
+ .CORE_VENDOR_SPEC3 = 0x1B0,
+ .CORE_DLL_CONFIG_2 = 0x1B4,
+ .CORE_DDR_CONFIG = 0x1B8,
+ .CORE_DDR_CONFIG_2 = 0x1BC,
+};
+
+u8 sdhci_msm_readb_relaxed(struct sdhci_host *host, u32 offset)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ void __iomem *base_addr;
+
+ if (msm_host->mci_removed)
+ base_addr = host->ioaddr;
+ else
+ base_addr = msm_host->core_mem;
+
+ return readb_relaxed(base_addr + offset);
+}
+
+u32 sdhci_msm_readl_relaxed(struct sdhci_host *host, u32 offset)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ void __iomem *base_addr;
+
+ if (msm_host->mci_removed)
+ base_addr = host->ioaddr;
+ else
+ base_addr = msm_host->core_mem;
+
+ return readl_relaxed(base_addr + offset);
+}
+
+void sdhci_msm_writeb_relaxed(u8 val, struct sdhci_host *host, u32 offset)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ void __iomem *base_addr;
+
+ if (msm_host->mci_removed)
+ base_addr = host->ioaddr;
+ else
+ base_addr = msm_host->core_mem;
+
+ writeb_relaxed(val, base_addr + offset);
+}
+
+void sdhci_msm_writel_relaxed(u32 val, struct sdhci_host *host, u32 offset)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ void __iomem *base_addr;
+
+ if (msm_host->mci_removed)
+ base_addr = host->ioaddr;
+ else
+ base_addr = msm_host->core_mem;
+
+ writel_relaxed(val, base_addr + offset);
+}
+
+/* Timeout value to avoid infinite waiting for pwr_irq */
+#define MSM_PWR_IRQ_TIMEOUT_MS 5000
static const u32 tuning_block_64[] = {
0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE,
@@ -172,122 +334,15 @@ static const u32 tuning_block_128[] = {
0xFFFFBBBB, 0xFFFF77FF, 0xFF7777FF, 0xEEDDBB77
};
+/* global to hold each slot instance for debug */
+static struct sdhci_msm_host *sdhci_slot[2];
+
static int disable_slots;
/* root can write, others read */
module_param(disable_slots, int, S_IRUGO|S_IWUSR);
-/* This structure keeps information per regulator */
-struct sdhci_msm_reg_data {
- /* voltage regulator handle */
- struct regulator *reg;
- /* regulator name */
- const char *name;
- /* voltage level to be set */
- u32 low_vol_level;
- u32 high_vol_level;
- /* Load values for low power and high power mode */
- u32 lpm_uA;
- u32 hpm_uA;
-
- /* is this regulator enabled? */
- bool is_enabled;
- /* is this regulator needs to be always on? */
- bool is_always_on;
- /* is low power mode setting required for this regulator? */
- bool lpm_sup;
- bool set_voltage_sup;
-};
-
-/*
- * This structure keeps information for all the
- * regulators required for a SDCC slot.
- */
-struct sdhci_msm_slot_reg_data {
- /* keeps VDD/VCC regulator info */
- struct sdhci_msm_reg_data *vdd_data;
- /* keeps VDD IO regulator info */
- struct sdhci_msm_reg_data *vdd_io_data;
-};
-
-struct sdhci_msm_gpio {
- u32 no;
- const char *name;
- bool is_enabled;
-};
-
-struct sdhci_msm_gpio_data {
- struct sdhci_msm_gpio *gpio;
- u8 size;
-};
-
-struct sdhci_msm_pin_data {
- /*
- * = 1 if controller pins are using gpios
- * = 0 if controller has dedicated MSM pads
- */
- bool cfg_sts;
- struct sdhci_msm_gpio_data *gpio_data;
-};
-
-struct sdhci_msm_bus_voting_data {
- struct msm_bus_scale_pdata *bus_pdata;
- unsigned int *bw_vecs;
- unsigned int bw_vecs_size;
-};
-
-struct sdhci_msm_pltfm_data {
- /* Supported UHS-I Modes */
- u32 caps;
-
- /* More capabilities */
- u32 caps2;
-
- unsigned long mmc_bus_width;
- struct sdhci_msm_slot_reg_data *vreg_data;
- bool nonremovable;
- struct sdhci_msm_pin_data *pin_data;
- u32 cpu_dma_latency_us;
- int status_gpio; /* card detection GPIO that is configured as IRQ */
- struct sdhci_msm_bus_voting_data *voting_data;
- u32 *sup_clk_table;
- unsigned char sup_clk_cnt;
-};
-
-struct sdhci_msm_bus_vote {
- uint32_t client_handle;
- uint32_t curr_vote;
- int min_bw_vote;
- int max_bw_vote;
- bool is_max_bw_needed;
- struct delayed_work vote_work;
- struct device_attribute max_bus_bw;
-};
-
-struct sdhci_msm_host {
- struct platform_device *pdev;
- void __iomem *core_mem; /* MSM SDCC mapped address */
- struct clk *clk; /* main SD/MMC bus clock */
- struct clk *pclk; /* SDHC peripheral bus clock */
- struct clk *bus_clk; /* SDHC bus voter clock */
- struct clk *ff_clk; /* CDC calibration fixed feedback clock */
- struct clk *sleep_clk; /* CDC calibration sleep clock */
- atomic_t clks_on; /* Set if clocks are enabled */
- struct sdhci_msm_pltfm_data *pdata;
- struct mmc_host *mmc;
- struct sdhci_pltfm_data sdhci_msm_pdata;
- u32 curr_pwr_state;
- u32 curr_io_level;
- struct completion pwr_irq_completion;
- struct sdhci_msm_bus_vote msm_bus_vote;
- struct device_attribute polling;
- u32 clk_rate; /* Keeps track of current clock rate that is set */
- bool tuning_done;
- bool calibration_done;
- u8 saved_tuning_phase;
- bool en_auto_cmd21;
- struct device_attribute auto_cmd21_attr;
- atomic_t controller_clock;
-};
+static bool nocmdq;
+module_param(nocmdq, bool, S_IRUGO|S_IWUSR);
enum vdd_io_level {
/* set vdd_io_data->low_vol_level */
@@ -309,10 +364,14 @@ static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host,
u32 wait_cnt = 50;
u8 ck_out_en = 0;
struct mmc_host *mmc = host->mmc;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
/* poll for CK_OUT_EN bit. max. poll time = 50us */
- ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) &
- CORE_CK_OUT_EN);
+ ck_out_en = !!(readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG) & CORE_CK_OUT_EN);
while (ck_out_en != poll) {
if (--wait_cnt == 0) {
@@ -324,7 +383,7 @@ static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host,
udelay(1);
ck_out_en = !!(readl_relaxed(host->ioaddr +
- CORE_DLL_CONFIG) & CORE_CK_OUT_EN);
+ msm_host_offset->CORE_DLL_CONFIG) & CORE_CK_OUT_EN);
}
out:
return rc;
@@ -338,18 +397,25 @@ static int msm_enable_cdr_cm_sdc4_dll(struct sdhci_host *host)
{
int rc = 0;
u32 config;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
- config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+ config = readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG);
config |= CORE_CDR_EN;
config &= ~(CORE_CDR_EXT_EN | CORE_CK_OUT_EN);
- writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+ writel_relaxed(config, host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG);
rc = msm_dll_poll_ck_out_en(host, 0);
if (rc)
goto err;
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) |
- CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG) | CORE_CK_OUT_EN),
+ host->ioaddr + msm_host_offset->CORE_DLL_CONFIG);
rc = msm_dll_poll_ck_out_en(host, 1);
if (rc)
@@ -396,6 +462,8 @@ static int sdhci_msm_config_auto_tuning_cmd(struct sdhci_host *host,
int rc = 0;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
u32 val = 0;
if (!msm_host->en_auto_cmd21)
@@ -408,11 +476,13 @@ static int sdhci_msm_config_auto_tuning_cmd(struct sdhci_host *host,
if (enable) {
rc = msm_enable_cdr_cm_sdc4_dll(host);
- writel_relaxed(readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) |
- val, host->ioaddr + CORE_VENDOR_SPEC);
+ writel_relaxed(readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC) | val,
+ host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC);
} else {
- writel_relaxed(readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) &
- ~val, host->ioaddr + CORE_VENDOR_SPEC);
+ writel_relaxed(readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC) & ~val,
+ host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC);
}
return rc;
}
@@ -420,6 +490,10 @@ static int sdhci_msm_config_auto_tuning_cmd(struct sdhci_host *host,
static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase)
{
int rc = 0;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
u8 grey_coded_phase_table[] = {0x0, 0x1, 0x3, 0x2, 0x6, 0x7, 0x5, 0x4,
0xC, 0xD, 0xF, 0xE, 0xA, 0xB, 0x9,
0x8};
@@ -430,10 +504,12 @@ static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase)
pr_debug("%s: Enter %s\n", mmc_hostname(mmc), __func__);
spin_lock_irqsave(&host->lock, flags);
- config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+ config = readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG);
config &= ~(CORE_CDR_EN | CORE_CK_OUT_EN);
config |= (CORE_CDR_EXT_EN | CORE_DLL_EN);
- writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+ writel_relaxed(config, host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG);
/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */
rc = msm_dll_poll_ck_out_en(host, 0);
@@ -444,24 +520,28 @@ static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase)
* Write the selected DLL clock output phase (0 ... 15)
* to CDR_SELEXT bit field of DLL_CONFIG register.
*/
- writel_relaxed(((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ writel_relaxed(((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG)
& ~(0xF << 20))
| (grey_coded_phase_table[phase] << 20)),
- host->ioaddr + CORE_DLL_CONFIG);
+ host->ioaddr + msm_host_offset->CORE_DLL_CONFIG);
/* Set CK_OUT_EN bit of DLL_CONFIG register to 1. */
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
- | CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG) | CORE_CK_OUT_EN),
+ host->ioaddr + msm_host_offset->CORE_DLL_CONFIG);
/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */
rc = msm_dll_poll_ck_out_en(host, 1);
if (rc)
goto err_out;
- config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+ config = readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG);
config |= CORE_CDR_EN;
config &= ~CORE_CDR_EXT_EN;
- writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+ writel_relaxed(config, host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG);
goto out;
err_out:
@@ -590,6 +670,10 @@ static int msm_find_most_appropriate_phase(struct sdhci_host *host,
static inline void msm_cm_dll_set_freq(struct sdhci_host *host)
{
u32 mclk_freq = 0;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
/* Program the MCLK value to MCLK_FREQ bit field */
if (host->clock <= 112000000)
@@ -609,14 +693,19 @@ static inline void msm_cm_dll_set_freq(struct sdhci_host *host)
else if (host->clock <= 200000000)
mclk_freq = 7;
- writel_relaxed(((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ writel_relaxed(((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG)
& ~(7 << 24)) | (mclk_freq << 24)),
- host->ioaddr + CORE_DLL_CONFIG);
+ host->ioaddr + msm_host_offset->CORE_DLL_CONFIG);
}
/* Initialize the DLL (Programmable Delay Line ) */
static int msm_init_cm_dll(struct sdhci_host *host)
{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
struct mmc_host *mmc = host->mmc;
int rc = 0;
unsigned long flags;
@@ -625,8 +714,8 @@ static int msm_init_cm_dll(struct sdhci_host *host)
pr_debug("%s: Enter %s\n", mmc_hostname(mmc), __func__);
spin_lock_irqsave(&host->lock, flags);
- prev_pwrsave = !!(readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) &
- CORE_CLK_PWRSAVE);
+ prev_pwrsave = !!(readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC) & CORE_CLK_PWRSAVE);
curr_pwrsave = prev_pwrsave;
/*
* Make sure that clock is always enabled when DLL
@@ -635,41 +724,89 @@ static int msm_init_cm_dll(struct sdhci_host *host)
* here and re-enable it once tuning is completed.
*/
if (prev_pwrsave) {
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
- & ~CORE_CLK_PWRSAVE),
- host->ioaddr + CORE_VENDOR_SPEC);
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC)
+ & ~CORE_CLK_PWRSAVE), host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC);
curr_pwrsave = false;
}
+ if (msm_host->use_updated_dll_reset) {
+ /* Disable the DLL clock */
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG)
+ & ~CORE_CK_OUT_EN), host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG);
+
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG_2)
+ | CORE_DLL_CLOCK_DISABLE), host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG_2);
+ }
+
/* Write 1 to DLL_RST bit of DLL_CONFIG register */
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
- | CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG);
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG) | CORE_DLL_RST),
+ host->ioaddr + msm_host_offset->CORE_DLL_CONFIG);
/* Write 1 to DLL_PDN bit of DLL_CONFIG register */
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
- | CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG);
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG) | CORE_DLL_PDN),
+ host->ioaddr + msm_host_offset->CORE_DLL_CONFIG);
msm_cm_dll_set_freq(host);
+ if (msm_host->use_updated_dll_reset) {
+ u32 mclk_freq = 0;
+
+ if ((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG_2)
+ & CORE_FLL_CYCLE_CNT))
+ mclk_freq = (u32) ((host->clock / TCXO_FREQ) * 8);
+ else
+ mclk_freq = (u32) ((host->clock / TCXO_FREQ) * 4);
+
+ writel_relaxed(((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG_2)
+ & ~(0xFF << 10)) | (mclk_freq << 10)),
+ host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_2);
+ /* wait for 5us before enabling DLL clock */
+ udelay(5);
+ }
+
/* Write 0 to DLL_RST bit of DLL_CONFIG register */
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
- & ~CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG);
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG) & ~CORE_DLL_RST),
+ host->ioaddr + msm_host_offset->CORE_DLL_CONFIG);
/* Write 0 to DLL_PDN bit of DLL_CONFIG register */
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
- & ~CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG);
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG) & ~CORE_DLL_PDN),
+ host->ioaddr + msm_host_offset->CORE_DLL_CONFIG);
+
+ if (msm_host->use_updated_dll_reset) {
+ msm_cm_dll_set_freq(host);
+ /* Enable the DLL clock */
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG_2)
+ & ~CORE_DLL_CLOCK_DISABLE), host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG_2);
+ }
/* Set DLL_EN bit to 1. */
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
- | CORE_DLL_EN), host->ioaddr + CORE_DLL_CONFIG);
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG) | CORE_DLL_EN),
+ host->ioaddr + msm_host_offset->CORE_DLL_CONFIG);
/* Set CK_OUT_EN bit to 1. */
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
- | CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG)
+ | CORE_CK_OUT_EN), host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG);
wait_cnt = 50;
/* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */
- while (!(readl_relaxed(host->ioaddr + CORE_DLL_STATUS) &
- CORE_DLL_LOCK)) {
+ while (!(readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_STATUS) & CORE_DLL_LOCK)) {
/* max. wait for 50us sec for LOCK bit to be set */
if (--wait_cnt == 0) {
pr_err("%s: %s: DLL failed to LOCK\n",
@@ -684,14 +821,16 @@ static int msm_init_cm_dll(struct sdhci_host *host)
out:
/* Restore the correct PWRSAVE state */
if (prev_pwrsave ^ curr_pwrsave) {
- u32 reg = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
+ u32 reg = readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC);
if (prev_pwrsave)
reg |= CORE_CLK_PWRSAVE;
else
reg &= ~CORE_CLK_PWRSAVE;
- writel_relaxed(reg, host->ioaddr + CORE_VENDOR_SPEC);
+ writel_relaxed(reg, host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC);
}
spin_unlock_irqrestore(&host->lock, flags);
@@ -701,36 +840,21 @@ static int msm_init_cm_dll(struct sdhci_host *host)
static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)
{
- u32 wait_cnt;
+ u32 calib_done;
int ret = 0;
int cdc_err = 0;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
pr_debug("%s: Enter %s\n", mmc_hostname(host->mmc), __func__);
- /*
- * Retuning in HS400 (DDR mode) will fail, just reset the
- * tuning block and restore the saved tuning phase.
- */
- ret = msm_init_cm_dll(host);
- if (ret)
- goto out;
-
- /* Set the selected phase in delay line hw block */
- ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase);
- if (ret)
- goto out;
-
- /* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
- | CORE_CMD_DAT_TRACK_SEL),
- host->ioaddr + CORE_DLL_CONFIG);
-
/* Write 0 to CDC_T4_DLY_SEL field in VENDOR_SPEC_DDR200_CFG */
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_DDR_200_CFG)
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DDR_200_CFG)
& ~CORE_CDC_T4_DLY_SEL),
- host->ioaddr + CORE_DDR_200_CFG);
+ host->ioaddr + msm_host_offset->CORE_DDR_200_CFG);
/* Write 0 to CDC_SWITCH_BYPASS_OFF field in CORE_CSR_CDC_GEN_CFG */
writel_relaxed((readl_relaxed(host->ioaddr + CORE_CSR_CDC_GEN_CFG)
@@ -743,9 +867,10 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)
host->ioaddr + CORE_CSR_CDC_GEN_CFG);
/* Write 0 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_DDR_200_CFG)
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DDR_200_CFG)
& ~CORE_START_CDC_TRAFFIC),
- host->ioaddr + CORE_DDR_200_CFG);
+ host->ioaddr + msm_host_offset->CORE_DDR_200_CFG);
/*
* Perform CDC Register Initialization Sequence
@@ -767,7 +892,7 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)
writel_relaxed(0x4, host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG1);
writel_relaxed(0xCB732020, host->ioaddr + CORE_CSR_CDC_REFCOUNT_CFG);
writel_relaxed(0xB19, host->ioaddr + CORE_CSR_CDC_COARSE_CAL_CFG);
- writel_relaxed(0x3AC, host->ioaddr + CORE_CSR_CDC_DELAY_CFG);
+ writel_relaxed(0x4E2, host->ioaddr + CORE_CSR_CDC_DELAY_CFG);
writel_relaxed(0x0, host->ioaddr + CORE_CDC_OFFSET_CFG);
writel_relaxed(0x16334, host->ioaddr + CORE_CDC_SLAVE_DDA_CFG);
@@ -796,18 +921,13 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)
mb();
/* Poll on CALIBRATION_DONE field in CORE_CSR_CDC_STATUS0 to be 1 */
- wait_cnt = 50;
- while (!(readl_relaxed(host->ioaddr + CORE_CSR_CDC_STATUS0)
- & CORE_CALIBRATION_DONE)) {
- /* max. wait for 50us sec for CALIBRATION_DONE bit to be set */
- if (--wait_cnt == 0) {
- pr_err("%s: %s: CDC Calibration was not completed\n",
+ ret = readl_poll_timeout(host->ioaddr + CORE_CSR_CDC_STATUS0,
+ calib_done, (calib_done & CORE_CALIBRATION_DONE), 1, 50);
+
+ if (ret == -ETIMEDOUT) {
+ pr_err("%s: %s: CDC Calibration was not completed\n",
mmc_hostname(host->mmc), __func__);
- ret = -ETIMEDOUT;
- goto out;
- }
- /* wait for 1us before polling again */
- udelay(1);
+ goto out;
}
/* Verify CDC_ERROR_CODE field in CORE_CSR_CDC_STATUS0 is 0 */
@@ -821,20 +941,196 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)
}
/* Write 1 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_DDR_200_CFG)
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DDR_200_CFG)
| CORE_START_CDC_TRAFFIC),
- host->ioaddr + CORE_DDR_200_CFG);
+ host->ioaddr + msm_host_offset->CORE_DDR_200_CFG);
out:
pr_debug("%s: Exit %s, ret:%d\n", mmc_hostname(host->mmc),
__func__, ret);
return ret;
}
+static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
+ u32 dll_status, ddr_config;
+ int ret = 0;
+
+ pr_debug("%s: Enter %s\n", mmc_hostname(host->mmc), __func__);
+
+ /*
+ * Reprogramming the value in case it might have been modified by
+ * bootloaders.
+ */
+ if (msm_host->rclk_delay_fix) {
+ writel_relaxed(DDR_CONFIG_2_POR_VAL, host->ioaddr +
+ msm_host_offset->CORE_DDR_CONFIG_2);
+ } else {
+ ddr_config = DDR_CONFIG_POR_VAL &
+ ~DDR_CONFIG_PRG_RCLK_DLY_MASK;
+ ddr_config |= DDR_CONFIG_PRG_RCLK_DLY;
+ writel_relaxed(ddr_config, host->ioaddr +
+ msm_host_offset->CORE_DDR_CONFIG);
+ }
+
+ if (msm_host->enhanced_strobe && mmc_card_strobe(msm_host->mmc->card))
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DDR_200_CFG)
+ | CORE_CMDIN_RCLK_EN), host->ioaddr +
+ msm_host_offset->CORE_DDR_200_CFG);
+
+ /* Write 1 to DDR_CAL_EN field in CORE_DLL_CONFIG_2 */
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG_2)
+ | CORE_DDR_CAL_EN),
+ host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_2);
+
+ /* Poll on DDR_DLL_LOCK bit in CORE_DLL_STATUS to be set */
+ ret = readl_poll_timeout(host->ioaddr +
+ msm_host_offset->CORE_DLL_STATUS,
+ dll_status, (dll_status & CORE_DDR_DLL_LOCK), 10, 1000);
+
+ if (ret == -ETIMEDOUT) {
+ pr_err("%s: %s: CM_DLL_SDC4 Calibration was not completed\n",
+ mmc_hostname(host->mmc), __func__);
+ goto out;
+ }
+
+ /*
+ * set CORE_PWRSAVE_DLL bit in CORE_VENDOR_SPEC3.
+ * when MCLK is gated OFF, it is not gated for less than 0.5us
+ * and MCLK must be switched on for at-least 1us before DATA
+ * starts coming. Controllers with 14lpp tech DLL cannot
+ * guarantee above requirement. So PWRSAVE_DLL should not be
+ * turned on for host controllers using this DLL.
+ */
+ if (!msm_host->use_14lpp_dll)
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC3)
+ | CORE_PWRSAVE_DLL), host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC3);
+ mb();
+out:
+ pr_debug("%s: Exit %s, ret:%d\n", mmc_hostname(host->mmc),
+ __func__, ret);
+ return ret;
+}
+
+static int sdhci_msm_enhanced_strobe(struct sdhci_host *host)
+{
+ int ret = 0;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ struct mmc_host *mmc = host->mmc;
+
+ pr_debug("%s: Enter %s\n", mmc_hostname(host->mmc), __func__);
+
+ if (!msm_host->enhanced_strobe || !mmc_card_strobe(mmc->card)) {
+ pr_debug("%s: host/card does not support hs400 enhanced strobe\n",
+ mmc_hostname(mmc));
+ return -EINVAL;
+ }
+
+ if (msm_host->calibration_done ||
+ !(mmc->ios.timing == MMC_TIMING_MMC_HS400)) {
+ return 0;
+ }
+
+ /*
+ * Reset the tuning block.
+ */
+ ret = msm_init_cm_dll(host);
+ if (ret)
+ goto out;
+
+ ret = sdhci_msm_cm_dll_sdc4_calibration(host);
+out:
+ if (!ret)
+ msm_host->calibration_done = true;
+ pr_debug("%s: Exit %s, ret:%d\n", mmc_hostname(host->mmc),
+ __func__, ret);
+ return ret;
+}
+
+static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host)
+{
+ int ret = 0;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
+
+ pr_debug("%s: Enter %s\n", mmc_hostname(host->mmc), __func__);
+
+ /*
+ * Retuning in HS400 (DDR mode) will fail, just reset the
+ * tuning block and restore the saved tuning phase.
+ */
+ ret = msm_init_cm_dll(host);
+ if (ret)
+ goto out;
+
+ /* Set the selected phase in delay line hw block */
+ ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase);
+ if (ret)
+ goto out;
+
+ /* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG)
+ | CORE_CMD_DAT_TRACK_SEL), host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG);
+
+ if (msm_host->use_cdclp533)
+ /* Calibrate CDCLP533 DLL HW */
+ ret = sdhci_msm_cdclp533_calibration(host);
+ else
+ /* Calibrate CM_DLL_SDC4 HW */
+ ret = sdhci_msm_cm_dll_sdc4_calibration(host);
+out:
+ pr_debug("%s: Exit %s, ret:%d\n", mmc_hostname(host->mmc),
+ __func__, ret);
+ return ret;
+}
+
+static void sdhci_msm_set_mmc_drv_type(struct sdhci_host *host, u32 opcode,
+ u8 drv_type)
+{
+ struct mmc_command cmd = {0};
+ struct mmc_request mrq = {NULL};
+ struct mmc_host *mmc = host->mmc;
+ u8 val = ((drv_type << 4) | 2);
+
+ cmd.opcode = MMC_SWITCH;
+ cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+ (EXT_CSD_HS_TIMING << 16) |
+ (val << 8) |
+ EXT_CSD_CMD_SET_NORMAL;
+ cmd.flags = MMC_CMD_AC | MMC_RSP_R1B;
+ /* 1 sec */
+ cmd.busy_timeout = 1000 * 1000;
+
+ memset(cmd.resp, 0, sizeof(cmd.resp));
+ cmd.retries = 3;
+
+ mrq.cmd = &cmd;
+ cmd.data = NULL;
+
+ mmc_wait_for_req(mmc, &mrq);
+ pr_debug("%s: %s: set card drive type to %d\n",
+ mmc_hostname(mmc), __func__,
+ drv_type);
+}
+
int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
{
unsigned long flags;
int tuning_seq_cnt = 3;
- u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0;
+ u8 phase, *data_buf, tuned_phases[NUM_TUNING_PHASES], tuned_phase_cnt;
const u32 *tuning_block_pattern = tuning_block_64;
int size = sizeof(tuning_block_64); /* Tuning pattern size in bytes */
int rc;
@@ -842,6 +1138,10 @@ int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
struct mmc_ios ios = host->mmc->ios;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ u8 drv_type = 0;
+ bool drv_type_changed = false;
+ struct mmc_card *card = host->mmc->card;
+ int sts_retry;
/*
* Tuning is required for SDR104, HS200 and HS400 cards and
@@ -853,12 +1153,19 @@ int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
(ios.timing == MMC_TIMING_UHS_SDR104)))
return 0;
+ /*
+ * Don't allow re-tuning for CRC errors observed for any commands
+ * that are sent during tuning sequence itself.
+ */
+ if (msm_host->tuning_in_progress)
+ return 0;
+ msm_host->tuning_in_progress = true;
pr_debug("%s: Enter %s\n", mmc_hostname(mmc), __func__);
- /* CDCLP533 HW calibration is only required for HS400 mode*/
+ /* CDC/SDC4 DLL HW calibration is only required for HS400 mode*/
if (msm_host->tuning_done && !msm_host->calibration_done &&
(mmc->ios.timing == MMC_TIMING_MMC_HS400)) {
- rc = sdhci_msm_cdclp533_calibration(host);
+ rc = sdhci_msm_hs400_dll_calibration(host);
spin_lock_irqsave(&host->lock, flags);
if (!rc)
msm_host->calibration_done = true;
@@ -882,6 +1189,8 @@ int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
}
retry:
+ tuned_phase_cnt = 0;
+
/* first of all reset the tuning block */
rc = msm_init_cm_dll(host);
if (rc)
@@ -896,6 +1205,7 @@ int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
.data = &data
};
struct scatterlist sg;
+ struct mmc_command sts_cmd = {0};
/* set the phase in delay line hw block */
rc = msm_config_cm_dll_phase(host, phase);
@@ -916,15 +1226,82 @@ int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
memset(data_buf, 0, size);
mmc_wait_for_req(mmc, &mrq);
+ if (card && (cmd.error || data.error)) {
+ sts_cmd.opcode = MMC_SEND_STATUS;
+ sts_cmd.arg = card->rca << 16;
+ sts_cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ sts_retry = 5;
+ while (sts_retry) {
+ mmc_wait_for_cmd(mmc, &sts_cmd, 0);
+
+ if (sts_cmd.error ||
+ (R1_CURRENT_STATE(sts_cmd.resp[0])
+ != R1_STATE_TRAN)) {
+ sts_retry--;
+ /*
+ * wait for at least 146 MCLK cycles for
+ * the card to move to TRANS state. As
+ * the MCLK would be min 200MHz for
+ * tuning, we need max 0.73us delay. To
+ * be on safer side 1ms delay is given.
+ */
+ usleep_range(1000, 1200);
+ pr_debug("%s: phase %d sts cmd err %d resp 0x%x\n",
+ mmc_hostname(mmc), phase,
+ sts_cmd.error, sts_cmd.resp[0]);
+ continue;
+ }
+ break;
+ };
+ }
+
if (!cmd.error && !data.error &&
!memcmp(data_buf, tuning_block_pattern, size)) {
/* tuning is successful at this tuning point */
tuned_phases[tuned_phase_cnt++] = phase;
- pr_debug("%s: %s: found good phase = %d\n",
+ pr_debug("%s: %s: found *** good *** phase = %d\n",
+ mmc_hostname(mmc), __func__, phase);
+ } else {
+ pr_debug("%s: %s: found ## bad ## phase = %d\n",
mmc_hostname(mmc), __func__, phase);
}
} while (++phase < 16);
+ if ((tuned_phase_cnt == NUM_TUNING_PHASES) &&
+ card && mmc_card_mmc(card)) {
+ /*
+ * If all phases pass then its a problem. So change the card's
+ * drive type to a different value, if supported and repeat
+ * tuning until at least one phase fails. Then set the original
+ * drive type back.
+ *
+ * If all the phases still pass after trying all possible
+ * drive types, then one of those 16 phases will be picked.
+ * This is no different from what was going on before the
+ * modification to change drive type and retune.
+ */
+ pr_debug("%s: tuned phases count: %d\n", mmc_hostname(mmc),
+ tuned_phase_cnt);
+
+ /* set drive type to other value . default setting is 0x0 */
+ while (++drv_type <= MAX_DRV_TYPES_SUPPORTED_HS200) {
+ pr_debug("%s: trying different drive strength (%d)\n",
+ mmc_hostname(mmc), drv_type);
+ if (card->ext_csd.raw_driver_strength &
+ (1 << drv_type)) {
+ sdhci_msm_set_mmc_drv_type(host, opcode,
+ drv_type);
+ if (!drv_type_changed)
+ drv_type_changed = true;
+ goto retry;
+ }
+ }
+ }
+
+ /* reset drive type to default (50 ohm) if changed */
+ if (drv_type_changed)
+ sdhci_msm_set_mmc_drv_type(host, opcode, 0);
+
if (tuned_phase_cnt) {
rc = msm_find_most_appropriate_phase(host, tuned_phases,
tuned_phase_cnt);
@@ -959,6 +1336,7 @@ int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
if (!rc)
msm_host->tuning_done = true;
spin_unlock_irqrestore(&host->lock, flags);
+ msm_host->tuning_in_progress = false;
pr_debug("%s: Exit %s, err(%d)\n", mmc_hostname(mmc), __func__, rc);
return rc;
}
@@ -1001,16 +1379,43 @@ static int sdhci_msm_setup_gpio(struct sdhci_msm_pltfm_data *pdata, bool enable)
return ret;
}
+static int sdhci_msm_setup_pinctrl(struct sdhci_msm_pltfm_data *pdata,
+ bool enable)
+{
+ int ret = 0;
+
+ if (enable)
+ ret = pinctrl_select_state(pdata->pctrl_data->pctrl,
+ pdata->pctrl_data->pins_active);
+ else
+ ret = pinctrl_select_state(pdata->pctrl_data->pctrl,
+ pdata->pctrl_data->pins_sleep);
+
+ if (ret < 0)
+ pr_err("%s state for pinctrl failed with %d\n",
+ enable ? "Enabling" : "Disabling", ret);
+
+ return ret;
+}
+
static int sdhci_msm_setup_pins(struct sdhci_msm_pltfm_data *pdata, bool enable)
{
int ret = 0;
- if (!pdata->pin_data || (pdata->pin_data->cfg_sts == enable))
+ if (pdata->pin_cfg_sts == enable) {
return 0;
+ } else if (pdata->pctrl_data) {
+ ret = sdhci_msm_setup_pinctrl(pdata, enable);
+ goto out;
+ } else if (!pdata->pin_data) {
+ return 0;
+ }
- ret = sdhci_msm_setup_gpio(pdata, enable);
+ if (pdata->pin_data->is_gpio)
+ ret = sdhci_msm_setup_gpio(pdata, enable);
+out:
if (!ret)
- pdata->pin_data->cfg_sts = enable;
+ pdata->pin_cfg_sts = enable;
return ret;
}
@@ -1119,6 +1524,46 @@ static int sdhci_msm_dt_parse_vreg_info(struct device *dev,
return ret;
}
+static int sdhci_msm_parse_pinctrl_info(struct device *dev,
+ struct sdhci_msm_pltfm_data *pdata)
+{
+ struct sdhci_pinctrl_data *pctrl_data;
+ struct pinctrl *pctrl;
+ int ret = 0;
+
+ /* Try to obtain pinctrl handle */
+ pctrl = devm_pinctrl_get(dev);
+ if (IS_ERR(pctrl)) {
+ ret = PTR_ERR(pctrl);
+ goto out;
+ }
+ pctrl_data = devm_kzalloc(dev, sizeof(*pctrl_data), GFP_KERNEL);
+ if (!pctrl_data) {
+ dev_err(dev, "No memory for sdhci_pinctrl_data\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ pctrl_data->pctrl = pctrl;
+ /* Look-up and keep the states handy to be used later */
+ pctrl_data->pins_active = pinctrl_lookup_state(
+ pctrl_data->pctrl, "active");
+ if (IS_ERR(pctrl_data->pins_active)) {
+ ret = PTR_ERR(pctrl_data->pins_active);
+ dev_err(dev, "Could not get active pinstates, err:%d\n", ret);
+ goto out;
+ }
+ pctrl_data->pins_sleep = pinctrl_lookup_state(
+ pctrl_data->pctrl, "sleep");
+ if (IS_ERR(pctrl_data->pins_sleep)) {
+ ret = PTR_ERR(pctrl_data->pins_sleep);
+ dev_err(dev, "Could not get sleep pinstates, err:%d\n", ret);
+ goto out;
+ }
+ pdata->pctrl_data = pctrl_data;
+out:
+ return ret;
+}
+
#define GPIO_NAME_MAX_LEN 32
static int sdhci_msm_dt_parse_gpio_info(struct device *dev,
struct sdhci_msm_pltfm_data *pdata)
@@ -1127,6 +1572,17 @@ static int sdhci_msm_dt_parse_gpio_info(struct device *dev,
struct sdhci_msm_pin_data *pin_data;
struct device_node *np = dev->of_node;
+ ret = sdhci_msm_parse_pinctrl_info(dev, pdata);
+ if (!ret) {
+ goto out;
+ } else if (ret == -EPROBE_DEFER) {
+ dev_err(dev, "Pinctrl framework not registered, err:%d\n", ret);
+ goto out;
+ } else {
+ dev_err(dev, "Parsing Pinctrl failed with %d, falling back on GPIO lib\n",
+ ret);
+ ret = 0;
+ }
pin_data = devm_kzalloc(dev, sizeof(*pin_data), GFP_KERNEL);
if (!pin_data) {
dev_err(dev, "No memory for pin_data\n");
@@ -1166,27 +1622,200 @@ static int sdhci_msm_dt_parse_gpio_info(struct device *dev,
dev_dbg(dev, "%s: gpio[%s] = %d\n", __func__,
pin_data->gpio_data->gpio[i].name,
pin_data->gpio_data->gpio[i].no);
- pdata->pin_data = pin_data;
}
}
-
+ pdata->pin_data = pin_data;
out:
if (ret)
dev_err(dev, "%s failed with err %d\n", __func__, ret);
return ret;
}
+#ifdef CONFIG_SMP
+static inline void parse_affine_irq(struct sdhci_msm_pltfm_data *pdata)
+{
+ pdata->pm_qos_data.irq_req_type = PM_QOS_REQ_AFFINE_IRQ;
+}
+#else
+static inline void parse_affine_irq(struct sdhci_msm_pltfm_data *pdata) { }
+#endif
+
+static int sdhci_msm_pm_qos_parse_irq(struct device *dev,
+ struct sdhci_msm_pltfm_data *pdata)
+{
+ struct device_node *np = dev->of_node;
+ const char *str;
+ u32 cpu;
+ int ret = 0;
+ int i;
+
+ pdata->pm_qos_data.irq_valid = false;
+ pdata->pm_qos_data.irq_req_type = PM_QOS_REQ_AFFINE_CORES;
+ if (!of_property_read_string(np, "qcom,pm-qos-irq-type", &str) &&
+ !strcmp(str, "affine_irq")) {
+ parse_affine_irq(pdata);
+ }
+
+ /* must specify cpu for "affine_cores" type */
+ if (pdata->pm_qos_data.irq_req_type == PM_QOS_REQ_AFFINE_CORES) {
+ pdata->pm_qos_data.irq_cpu = -1;
+ ret = of_property_read_u32(np, "qcom,pm-qos-irq-cpu", &cpu);
+ if (ret) {
+ dev_err(dev, "%s: error %d reading irq cpu\n", __func__,
+ ret);
+ goto out;
+ }
+ if (cpu < 0 || cpu >= num_possible_cpus()) {
+ dev_err(dev, "%s: invalid irq cpu %d (NR_CPUS=%d)\n",
+ __func__, cpu, num_possible_cpus());
+ ret = -EINVAL;
+ goto out;
+ }
+ pdata->pm_qos_data.irq_cpu = cpu;
+ }
+
+ if (of_property_count_u32_elems(np, "qcom,pm-qos-irq-latency") !=
+ SDHCI_POWER_POLICY_NUM) {
+ dev_err(dev, "%s: could not read %d values for 'qcom,pm-qos-irq-latency'\n",
+ __func__, SDHCI_POWER_POLICY_NUM);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < SDHCI_POWER_POLICY_NUM; i++)
+ of_property_read_u32_index(np, "qcom,pm-qos-irq-latency", i,
+ &pdata->pm_qos_data.irq_latency.latency[i]);
+
+ pdata->pm_qos_data.irq_valid = true;
+out:
+ return ret;
+}
+
+static int sdhci_msm_pm_qos_parse_cpu_groups(struct device *dev,
+ struct sdhci_msm_pltfm_data *pdata)
+{
+ struct device_node *np = dev->of_node;
+ u32 mask;
+ int nr_groups;
+ int ret;
+ int i;
+
+ /* Read cpu group mapping */
+ nr_groups = of_property_count_u32_elems(np, "qcom,pm-qos-cpu-groups");
+ if (nr_groups <= 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+ pdata->pm_qos_data.cpu_group_map.nr_groups = nr_groups;
+ pdata->pm_qos_data.cpu_group_map.mask =
+ kcalloc(nr_groups, sizeof(cpumask_t), GFP_KERNEL);
+ if (!pdata->pm_qos_data.cpu_group_map.mask) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < nr_groups; i++) {
+ of_property_read_u32_index(np, "qcom,pm-qos-cpu-groups",
+ i, &mask);
+
+ pdata->pm_qos_data.cpu_group_map.mask[i].bits[0] = mask;
+ if (!cpumask_subset(&pdata->pm_qos_data.cpu_group_map.mask[i],
+ cpu_possible_mask)) {
+ dev_err(dev, "%s: invalid mask 0x%x of cpu group #%d\n",
+ __func__, mask, i);
+ ret = -EINVAL;
+ goto free_res;
+ }
+ }
+ return 0;
+
+free_res:
+ kfree(pdata->pm_qos_data.cpu_group_map.mask);
+out:
+ return ret;
+}
+
+static int sdhci_msm_pm_qos_parse_latency(struct device *dev, const char *name,
+ int nr_groups, struct sdhci_msm_pm_qos_latency **latency)
+{
+ struct device_node *np = dev->of_node;
+ struct sdhci_msm_pm_qos_latency *values;
+ int ret;
+ int i;
+ int group;
+ int cfg;
+
+ ret = of_property_count_u32_elems(np, name);
+ if (ret > 0 && ret != SDHCI_POWER_POLICY_NUM * nr_groups) {
+ dev_err(dev, "%s: invalid number of values for property %s: expected=%d actual=%d\n",
+ __func__, name, SDHCI_POWER_POLICY_NUM * nr_groups,
+ ret);
+ return -EINVAL;
+ } else if (ret < 0) {
+ return ret;
+ }
+
+ values = kcalloc(nr_groups, sizeof(struct sdhci_msm_pm_qos_latency),
+ GFP_KERNEL);
+ if (!values)
+ return -ENOMEM;
+
+ for (i = 0; i < SDHCI_POWER_POLICY_NUM * nr_groups; i++) {
+ group = i / SDHCI_POWER_POLICY_NUM;
+ cfg = i % SDHCI_POWER_POLICY_NUM;
+ of_property_read_u32_index(np, name, i,
+ &(values[group].latency[cfg]));
+ }
+
+ *latency = values;
+ return 0;
+}
+
+static void sdhci_msm_pm_qos_parse(struct device *dev,
+ struct sdhci_msm_pltfm_data *pdata)
+{
+ if (sdhci_msm_pm_qos_parse_irq(dev, pdata))
+ dev_notice(dev, "%s: PM QoS voting for IRQ will be disabled\n",
+ __func__);
+
+ if (!sdhci_msm_pm_qos_parse_cpu_groups(dev, pdata)) {
+ pdata->pm_qos_data.cmdq_valid =
+ !sdhci_msm_pm_qos_parse_latency(dev,
+ "qcom,pm-qos-cmdq-latency-us",
+ pdata->pm_qos_data.cpu_group_map.nr_groups,
+ &pdata->pm_qos_data.cmdq_latency);
+ pdata->pm_qos_data.legacy_valid =
+ !sdhci_msm_pm_qos_parse_latency(dev,
+ "qcom,pm-qos-legacy-latency-us",
+ pdata->pm_qos_data.cpu_group_map.nr_groups,
+ &pdata->pm_qos_data.latency);
+ if (!pdata->pm_qos_data.cmdq_valid &&
+ !pdata->pm_qos_data.legacy_valid) {
+ /* clean-up previously allocated arrays */
+ kfree(pdata->pm_qos_data.latency);
+ kfree(pdata->pm_qos_data.cmdq_latency);
+ dev_err(dev, "%s: invalid PM QoS latency values. Voting for cpu group will be disabled\n",
+ __func__);
+ }
+ } else {
+ dev_notice(dev, "%s: PM QoS voting for cpu group will be disabled\n",
+ __func__);
+ }
+}
+
/* Parse platform data */
-static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev)
+static
+struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev,
+ struct sdhci_msm_host *msm_host)
{
struct sdhci_msm_pltfm_data *pdata = NULL;
struct device_node *np = dev->of_node;
u32 bus_width = 0;
- u32 cpu_dma_latency;
int len, i;
int clk_table_len;
u32 *clk_table = NULL;
enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW;
+ const char *lower_bus_speed = NULL;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
@@ -1208,9 +1837,27 @@ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev)
pdata->mmc_bus_width = 0;
}
- if (!of_property_read_u32(np, "qcom,cpu-dma-latency-us",
- &cpu_dma_latency))
- pdata->cpu_dma_latency_us = cpu_dma_latency;
+ if (sdhci_msm_dt_get_array(dev, "qcom,devfreq,freq-table",
+ &msm_host->mmc->clk_scaling.freq_table,
+ &msm_host->mmc->clk_scaling.freq_table_sz, 0))
+ pr_debug("%s: no clock scaling frequencies were supplied\n",
+ dev_name(dev));
+ else if (!msm_host->mmc->clk_scaling.freq_table ||
+ !msm_host->mmc->clk_scaling.freq_table_sz)
+ dev_err(dev, "bad dts clock scaling frequencies\n");
+
+ /*
+ * Few hosts can support DDR52 mode at the same lower
+ * system voltage corner as high-speed mode. In such cases,
+ * it is always better to put it in DDR mode which will
+ * improve the performance without any power impact.
+ */
+ if (!of_property_read_string(np, "qcom,scaling-lower-bus-speed-mode",
+ &lower_bus_speed)) {
+ if (!strcmp(lower_bus_speed, "DDR52"))
+ msm_host->mmc->clk_scaling.lower_bus_speed_mode |=
+ MMC_SCALING_LOWER_DDR52_MODE;
+ }
if (sdhci_msm_dt_get_array(dev, "qcom,clk-rates",
&clk_table, &clk_table_len, 0)) {
@@ -1278,6 +1925,20 @@ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev)
if (of_get_property(np, "qcom,nonremovable", NULL))
pdata->nonremovable = true;
+ if (of_get_property(np, "qcom,nonhotplug", NULL))
+ pdata->nonhotplug = true;
+
+ pdata->largeaddressbus =
+ of_property_read_bool(np, "qcom,large-address-bus");
+
+ if (of_property_read_bool(np, "qcom,wakeup-on-idle"))
+ msm_host->mmc->wakeup_on_idle = true;
+
+ sdhci_msm_pm_qos_parse(dev, pdata);
+
+ if (of_get_property(np, "qcom,core_3_0v_support", NULL))
+ pdata->core_3_0v_support = true;
+
return pdata;
out:
return NULL;
@@ -1782,23 +2443,84 @@ static int sdhci_msm_set_vdd_io_vol(struct sdhci_msm_pltfm_data *pdata,
return ret;
}
+/*
+ * Acquire spin-lock host->lock before calling this function
+ */
+static void sdhci_msm_cfg_sdiowakeup_gpio_irq(struct sdhci_host *host,
+ bool enable)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ if (enable && !msm_host->is_sdiowakeup_enabled)
+ enable_irq(msm_host->pdata->sdiowakeup_irq);
+ else if (!enable && msm_host->is_sdiowakeup_enabled)
+ disable_irq_nosync(msm_host->pdata->sdiowakeup_irq);
+ else
+ dev_warn(&msm_host->pdev->dev, "%s: wakeup to config: %d curr: %d\n",
+ __func__, enable, msm_host->is_sdiowakeup_enabled);
+ msm_host->is_sdiowakeup_enabled = enable;
+}
+
+static irqreturn_t sdhci_msm_sdiowakeup_irq(int irq, void *data)
+{
+ struct sdhci_host *host = (struct sdhci_host *)data;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ unsigned long flags;
+
+ pr_debug("%s: irq (%d) received\n", __func__, irq);
+
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false);
+ spin_unlock_irqrestore(&host->lock, flags);
+ msm_host->sdio_pending_processing = true;
+
+ return IRQ_HANDLED;
+}
+
+void sdhci_msm_dump_pwr_ctrl_regs(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
+
+ pr_err("%s: PWRCTL_STATUS: 0x%08x | PWRCTL_MASK: 0x%08x | PWRCTL_CTL: 0x%08x\n",
+ mmc_hostname(host->mmc),
+ sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_PWRCTL_STATUS),
+ sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_PWRCTL_MASK),
+ sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_PWRCTL_CTL));
+}
+
static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
{
struct sdhci_host *host = (struct sdhci_host *)data;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
u8 irq_status = 0;
u8 irq_ack = 0;
int ret = 0;
int pwr_state = 0, io_level = 0;
unsigned long flags;
+ int retry = 10;
- irq_status = readb_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS);
+ irq_status = sdhci_msm_readb_relaxed(host,
+ msm_host_offset->CORE_PWRCTL_STATUS);
+
pr_debug("%s: Received IRQ(%d), status=0x%x\n",
mmc_hostname(msm_host->mmc), irq, irq_status);
/* Clear the interrupt */
- writeb_relaxed(irq_status, (msm_host->core_mem + CORE_PWRCTL_CLEAR));
+ sdhci_msm_writeb_relaxed(irq_status, host,
+ msm_host_offset->CORE_PWRCTL_CLEAR);
+
/*
* SDHC has core_mem and hc_mem device memory and these memory
* addresses do not fall within 1KB region. Hence, any update to
@@ -1806,6 +2528,29 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
* completed before its next update to registers within hc_mem.
*/
mb();
+ /*
+ * There is a rare HW scenario where the first clear pulse could be
+ * lost when actual reset and clear/read of status register is
+ * happening at a time. Hence, retry for at least 10 times to make
+ * sure status register is cleared. Otherwise, this will result in
+ * a spurious power IRQ resulting in system instability.
+ */
+ while (irq_status & sdhci_msm_readb_relaxed(host,
+ msm_host_offset->CORE_PWRCTL_STATUS)) {
+ if (retry == 0) {
+ pr_err("%s: Timedout clearing (0x%x) pwrctl status register\n",
+ mmc_hostname(host->mmc), irq_status);
+ sdhci_msm_dump_pwr_ctrl_regs(host);
+ BUG_ON(1);
+ }
+ sdhci_msm_writeb_relaxed(irq_status, host,
+ msm_host_offset->CORE_PWRCTL_CLEAR);
+ retry--;
+ udelay(10);
+ }
+ if (likely(retry < 10))
+ pr_debug("%s: success clearing (0x%x) pwrctl status register, retries left %d\n",
+ mmc_hostname(host->mmc), irq_status, retry);
/* Handle BUS ON/OFF*/
if (irq_status & CORE_PWRCTL_BUS_ON) {
@@ -1861,7 +2606,8 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
}
/* ACK status to the core */
- writeb_relaxed(irq_ack, (msm_host->core_mem + CORE_PWRCTL_CTL));
+ sdhci_msm_writeb_relaxed(irq_ack, host,
+ msm_host_offset->CORE_PWRCTL_CTL);
/*
* SDHC has core_mem and hc_mem device memory and these memory
* addresses do not fall within 1KB region. Hence, any update to
@@ -1870,14 +2616,17 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
*/
mb();
- if (io_level & REQ_IO_HIGH)
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) &
- ~CORE_IO_PAD_PWR_SWITCH),
- host->ioaddr + CORE_VENDOR_SPEC);
- else if (io_level & REQ_IO_LOW)
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) |
- CORE_IO_PAD_PWR_SWITCH),
- host->ioaddr + CORE_VENDOR_SPEC);
+ if ((io_level & REQ_IO_HIGH) && (msm_host->caps_0 & CORE_3_0V_SUPPORT))
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC) &
+ ~CORE_IO_PAD_PWR_SWITCH), host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC);
+ else if ((io_level & REQ_IO_LOW) ||
+ (msm_host->caps_0 & CORE_1_8V_SUPPORT))
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC) |
+ CORE_IO_PAD_PWR_SWITCH), host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC);
mb();
pr_debug("%s: Handled IRQ(%d), ret=%d, ack=0x%x\n",
@@ -1962,6 +2711,8 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
unsigned long flags;
bool done = false;
u32 io_sig_sts;
@@ -1970,7 +2721,9 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type)
pr_debug("%s: %s: request %d curr_pwr_state %x curr_io_level %x\n",
mmc_hostname(host->mmc), __func__, req_type,
msm_host->curr_pwr_state, msm_host->curr_io_level);
- io_sig_sts = readl_relaxed(msm_host->core_mem + CORE_GENERICS);
+ io_sig_sts = sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_GENERICS);
+
/*
* The IRQ for request type IO High/Low will be generated when -
* 1. SWITCHABLE_SIGNALLING_VOL is enabled in HW.
@@ -2008,8 +2761,10 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type)
*/
if (done)
init_completion(&msm_host->pwr_irq_completion);
- else
- wait_for_completion(&msm_host->pwr_irq_completion);
+ else if (!wait_for_completion_timeout(&msm_host->pwr_irq_completion,
+ msecs_to_jiffies(MSM_PWR_IRQ_TIMEOUT_MS)))
+ __WARN_printf("%s: request(%d) timed out waiting for pwr_irq\n",
+ mmc_hostname(host->mmc), req_type);
pr_debug("%s: %s: request %d done\n", mmc_hostname(host->mmc),
__func__, req_type);
@@ -2017,14 +2772,24 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type)
static void sdhci_msm_toggle_cdr(struct sdhci_host *host, bool enable)
{
- if (enable)
- writel_relaxed((readl_relaxed(host->ioaddr +
- CORE_DLL_CONFIG) | CORE_CDR_EN),
- host->ioaddr + CORE_DLL_CONFIG);
- else
- writel_relaxed((readl_relaxed(host->ioaddr +
- CORE_DLL_CONFIG) & ~CORE_CDR_EN),
- host->ioaddr + CORE_DLL_CONFIG);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
+ u32 config = readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG);
+
+ if (enable) {
+ config |= CORE_CDR_EN;
+ config &= ~CORE_CDR_EXT_EN;
+ writel_relaxed(config, host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG);
+ } else {
+ config &= ~CORE_CDR_EN;
+ config |= CORE_CDR_EXT_EN;
+ writel_relaxed(config, host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG);
+ }
}
static unsigned int sdhci_msm_max_segs(void)
@@ -2117,7 +2882,24 @@ static int sdhci_msm_enable_controller_clock(struct sdhci_host *host)
return rc;
}
+static void sdhci_msm_disable_controller_clock(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ if (atomic_read(&msm_host->controller_clock)) {
+ if (!IS_ERR(msm_host->clk))
+ clk_disable_unprepare(msm_host->clk);
+ if (!IS_ERR(msm_host->pclk))
+ clk_disable_unprepare(msm_host->pclk);
+ if (!IS_ERR(msm_host->ice_clk))
+ clk_disable_unprepare(msm_host->ice_clk);
+ sdhci_msm_bus_voting(host, 0);
+ atomic_set(&msm_host->controller_clock, 0);
+ pr_debug("%s: %s: disabled controller clock\n",
+ mmc_hostname(host->mmc), __func__);
+ }
+}
static int sdhci_msm_prepare_clocks(struct sdhci_host *host, bool enable)
{
@@ -2219,8 +3001,11 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
int rc;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
+ struct mmc_card *card = host->mmc->card;
struct mmc_ios curr_ios = host->mmc->ios;
- u32 sup_clock, ddr_clock;
+ u32 sup_clock, ddr_clock, dll_lock;
bool curr_pwrsave;
if (!clock) {
@@ -2228,8 +3013,10 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
* disable pwrsave to ensure clock is not auto-gated until
* the rate is >400KHz (initialization complete).
*/
- writel_relaxed(readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) &
- ~CORE_CLK_PWRSAVE, host->ioaddr + CORE_VENDOR_SPEC);
+ writel_relaxed(readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC) &
+ ~CORE_CLK_PWRSAVE, host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC);
sdhci_msm_prepare_clocks(host, false);
host->clock = clock;
goto out;
@@ -2239,24 +3026,27 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
if (rc)
goto out;
- curr_pwrsave = !!(readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) &
- CORE_CLK_PWRSAVE);
+ curr_pwrsave = !!(readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC) & CORE_CLK_PWRSAVE);
if ((clock > 400000) &&
- !curr_pwrsave && mmc_host_may_gate_card(host->mmc->card))
- writel_relaxed(readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
- | CORE_CLK_PWRSAVE,
- host->ioaddr + CORE_VENDOR_SPEC);
+ !curr_pwrsave && card && mmc_host_may_gate_card(card))
+ writel_relaxed(readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC)
+ | CORE_CLK_PWRSAVE, host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC);
/*
* Disable pwrsave for a newly added card if doesn't allow clock
* gating.
*/
- else if (curr_pwrsave && !mmc_host_may_gate_card(host->mmc->card))
- writel_relaxed(readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
- & ~CORE_CLK_PWRSAVE,
- host->ioaddr + CORE_VENDOR_SPEC);
+ else if (curr_pwrsave && card && !mmc_host_may_gate_card(card))
+ writel_relaxed(readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC)
+ & ~CORE_CLK_PWRSAVE, host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC);
sup_clock = sdhci_msm_get_sup_clk_rate(host, clock);
if ((curr_ios.timing == MMC_TIMING_UHS_DDR50) ||
+ (curr_ios.timing == MMC_TIMING_MMC_DDR52) ||
(curr_ios.timing == MMC_TIMING_MMC_HS400)) {
/*
* The SDHC requires internal clock frequency to be double the
@@ -2288,31 +3078,58 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
*/
if (curr_ios.timing == MMC_TIMING_MMC_HS400) {
/* Select the divided clock (free running MCLK/2) */
- writel_relaxed(((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
- & ~CORE_HC_MCLK_SEL_MASK)
- | CORE_HC_MCLK_SEL_HS400),
- host->ioaddr + CORE_VENDOR_SPEC);
+ writel_relaxed(((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC)
+ & ~CORE_HC_MCLK_SEL_MASK)
+ | CORE_HC_MCLK_SEL_HS400), host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC);
/*
* Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC
* register
*/
- if (msm_host->tuning_done && !msm_host->calibration_done) {
+ if ((msm_host->tuning_done ||
+ (card && mmc_card_strobe(card) &&
+ msm_host->enhanced_strobe)) &&
+ !msm_host->calibration_done) {
/*
* Write 0x6 to HC_SELECT_IN and 1 to HC_SELECT_IN_EN
* field in VENDOR_SPEC_FUNC
*/
writel_relaxed((readl_relaxed(host->ioaddr + \
- CORE_VENDOR_SPEC)
+ msm_host_offset->CORE_VENDOR_SPEC)
| CORE_HC_SELECT_IN_HS400
- | CORE_HC_SELECT_IN_EN),
- host->ioaddr + CORE_VENDOR_SPEC);
+ | CORE_HC_SELECT_IN_EN), host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC);
+ }
+ if (!host->mmc->ios.old_rate && !msm_host->use_cdclp533) {
+ /*
+ * Poll on DLL_LOCK and DDR_DLL_LOCK bits in
+ * CORE_DLL_STATUS to be set. This should get set
+ * with in 15 us at 200 MHz.
+ */
+ rc = readl_poll_timeout(host->ioaddr +
+ msm_host_offset->CORE_DLL_STATUS,
+ dll_lock, (dll_lock & (CORE_DLL_LOCK |
+ CORE_DDR_DLL_LOCK)), 10, 1000);
+ if (rc == -ETIMEDOUT)
+ pr_err("%s: Unable to get DLL_LOCK/DDR_DLL_LOCK, dll_status: 0x%08x\n",
+ mmc_hostname(host->mmc),
+ dll_lock);
}
} else {
+ if (!msm_host->use_cdclp533)
+ /* set CORE_PWRSAVE_DLL bit in CORE_VENDOR_SPEC3 */
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC3)
+ & ~CORE_PWRSAVE_DLL), host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC3);
+
/* Select the default clock (free running MCLK) */
- writel_relaxed(((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
+ writel_relaxed(((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC)
& ~CORE_HC_MCLK_SEL_MASK)
- | CORE_HC_MCLK_SEL_DFLT),
- host->ioaddr + CORE_VENDOR_SPEC);
+ | CORE_HC_MCLK_SEL_DFLT), host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC);
/*
* Disable HC_SELECT_IN to be able to use the UHS mode select
@@ -2322,10 +3139,11 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
* Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field
* in VENDOR_SPEC_FUNC
*/
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC)
& ~CORE_HC_SELECT_IN_EN
- & ~CORE_HC_SELECT_IN_MASK),
- host->ioaddr + CORE_VENDOR_SPEC);
+ & ~CORE_HC_SELECT_IN_MASK), host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC);
}
mb();
@@ -2356,14 +3174,16 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
u16 ctrl_2;
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
/* Select Bus Speed Mode for host */
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
- if (uhs == MMC_TIMING_MMC_HS400)
- ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
- else if (uhs == MMC_TIMING_MMC_HS200)
+ if ((uhs == MMC_TIMING_MMC_HS400) ||
+ (uhs == MMC_TIMING_MMC_HS200) ||
+ (uhs == MMC_TIMING_UHS_SDR104))
ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
else if (uhs == MMC_TIMING_UHS_SDR12)
ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
@@ -2371,9 +3191,8 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
else if (uhs == MMC_TIMING_UHS_SDR50)
ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
- else if (uhs == MMC_TIMING_UHS_SDR104)
- ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
- else if (uhs == MMC_TIMING_UHS_DDR50)
+ else if ((uhs == MMC_TIMING_UHS_DDR50) ||
+ (uhs == MMC_TIMING_MMC_DDR52))
ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
/*
* When clock frquency is less than 100MHz, the feedback clock must be
@@ -2392,14 +3211,16 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
*
* Write 1 to DLL_RST bit of DLL_CONFIG register
*/
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
- | CORE_DLL_RST),
- host->ioaddr + CORE_DLL_CONFIG);
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG)
+ | CORE_DLL_RST), host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG);
/* Write 1 to DLL_PDN bit of DLL_CONFIG register */
- writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
- | CORE_DLL_PDN),
- host->ioaddr + CORE_DLL_CONFIG);
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG)
+ | CORE_DLL_PDN), host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG);
mb();
/*
@@ -2415,30 +3236,80 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
}
-#define MAX_TEST_BUS 20
+#define MAX_TEST_BUS 60
+#define DRV_NAME "cmdq-host"
+static void sdhci_msm_cmdq_dump_debug_ram(struct sdhci_host *host)
+{
+ int i = 0;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
+ struct cmdq_host *cq_host = host->cq_host;
+
+ u32 version = sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_MCI_VERSION);
+ u16 minor = version & CORE_VERSION_TARGET_MASK;
+ /* registers offset changed starting from 4.2.0 */
+ int offset = minor >= SDHCI_MSM_VER_420 ? 0 : 0x48;
+
+ pr_err("---- Debug RAM dump ----\n");
+ pr_err(DRV_NAME ": Debug RAM wrap-around: 0x%08x | Debug RAM overlap: 0x%08x\n",
+ cmdq_readl(cq_host, CQ_CMD_DBG_RAM_WA + offset),
+ cmdq_readl(cq_host, CQ_CMD_DBG_RAM_OL + offset));
+
+ while (i < 16) {
+ pr_err(DRV_NAME ": Debug RAM dump [%d]: 0x%08x\n", i,
+ cmdq_readl(cq_host, CQ_CMD_DBG_RAM + offset + (4 * i)));
+ i++;
+ }
+ pr_err("-------------------------\n");
+}
void sdhci_msm_dump_vendor_regs(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
int tbsel, tbsel2;
int i, index = 0;
u32 test_bus_val = 0;
u32 debug_reg[MAX_TEST_BUS] = {0};
pr_info("----------- VENDOR REGISTER DUMP -----------\n");
+ if (host->cq_host)
+ sdhci_msm_cmdq_dump_debug_ram(host);
+
+ MMC_TRACE(host->mmc, "Data cnt: 0x%08x | Fifo cnt: 0x%08x\n",
+ sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_MCI_DATA_CNT),
+ sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_MCI_FIFO_CNT));
pr_info("Data cnt: 0x%08x | Fifo cnt: 0x%08x | Int sts: 0x%08x\n",
- readl_relaxed(msm_host->core_mem + CORE_MCI_DATA_CNT),
- readl_relaxed(msm_host->core_mem + CORE_MCI_FIFO_CNT),
- readl_relaxed(msm_host->core_mem + CORE_MCI_STATUS));
+ sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_MCI_DATA_CNT),
+ sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_MCI_FIFO_CNT),
+ sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_MCI_STATUS));
pr_info("DLL cfg: 0x%08x | DLL sts: 0x%08x | SDCC ver: 0x%08x\n",
- readl_relaxed(host->ioaddr + CORE_DLL_CONFIG),
- readl_relaxed(host->ioaddr + CORE_DLL_STATUS),
- readl_relaxed(msm_host->core_mem + CORE_MCI_VERSION));
+ readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_CONFIG),
+ readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_DLL_STATUS),
+ sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_MCI_VERSION));
pr_info("Vndr func: 0x%08x | Vndr adma err : addr0: 0x%08x addr1: 0x%08x\n",
- readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC),
- readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC_ADMA_ERR_ADDR0),
- readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC_ADMA_ERR_ADDR1));
+ readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC),
+ readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC_ADMA_ERR_ADDR0),
+ readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC_ADMA_ERR_ADDR1));
+ pr_info("Vndr func2: 0x%08x\n",
+ readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC_FUNC2));
/*
* tbsel indicates [2:0] bits and tbsel2 indicates [7:4] bits
@@ -2449,31 +3320,609 @@ void sdhci_msm_dump_vendor_regs(struct sdhci_host *host)
* to select test bus 14, write 0x1E to CORE_TESTBUS_CONFIG register
* i.e., tbsel2[7:4] = 0001, tbsel[2:0] = 110.
*/
- for (tbsel2 = 0; tbsel2 < 3; tbsel2++) {
+ for (tbsel2 = 0; tbsel2 < 7; tbsel2++) {
for (tbsel = 0; tbsel < 8; tbsel++) {
if (index >= MAX_TEST_BUS)
break;
- test_bus_val = (tbsel2 << CORE_TESTBUS_SEL2_BIT) |
- tbsel | CORE_TESTBUS_ENA;
- writel_relaxed(test_bus_val,
- msm_host->core_mem + CORE_TESTBUS_CONFIG);
- debug_reg[index++] = readl_relaxed(msm_host->core_mem +
- CORE_SDCC_DEBUG_REG);
+ test_bus_val =
+ (tbsel2 << msm_host_offset->CORE_TESTBUS_SEL2_BIT) |
+ tbsel | msm_host_offset->CORE_TESTBUS_ENA;
+ sdhci_msm_writel_relaxed(test_bus_val, host,
+ msm_host_offset->CORE_TESTBUS_CONFIG);
+ debug_reg[index++] = sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_SDCC_DEBUG_REG);
}
}
for (i = 0; i < MAX_TEST_BUS; i = i + 4)
pr_info(" Test bus[%d to %d]: 0x%08x 0x%08x 0x%08x 0x%08x\n",
i, i + 3, debug_reg[i], debug_reg[i+1],
debug_reg[i+2], debug_reg[i+3]);
- /* Disable test bus */
- writel_relaxed(~CORE_TESTBUS_ENA, msm_host->core_mem +
- CORE_TESTBUS_CONFIG);
+}
+
+/*
+ * sdhci_msm_enhanced_strobe_mask :-
+ * Before running CMDQ transfers in HS400 Enhanced Strobe mode,
+ * SW should write 3 to
+ * HC_VENDOR_SPECIFIC_FUNC3.CMDEN_HS400_INPUT_MASK_CNT register.
+ * The default reset value of this register is 2.
+ */
+static void sdhci_msm_enhanced_strobe_mask(struct sdhci_host *host, bool set)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
+
+ if (!msm_host->enhanced_strobe ||
+ !mmc_card_strobe(msm_host->mmc->card)) {
+ pr_debug("%s: host/card does not support hs400 enhanced strobe\n",
+ mmc_hostname(host->mmc));
+ return;
+ }
+
+ if (set) {
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC3)
+ | CORE_CMDEN_HS400_INPUT_MASK_CNT),
+ host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC3);
+ } else {
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC3)
+ & ~CORE_CMDEN_HS400_INPUT_MASK_CNT),
+ host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC3);
+ }
+}
+
+static void sdhci_msm_clear_set_dumpregs(struct sdhci_host *host, bool set)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
+
+ if (set) {
+ sdhci_msm_writel_relaxed(msm_host_offset->CORE_TESTBUS_ENA,
+ host, msm_host_offset->CORE_TESTBUS_CONFIG);
+ } else {
+ u32 value;
+
+ value = sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_TESTBUS_CONFIG);
+ value &= ~(msm_host_offset->CORE_TESTBUS_ENA);
+ sdhci_msm_writel_relaxed(value, host,
+ msm_host_offset->CORE_TESTBUS_CONFIG);
+ }
+}
+
+void sdhci_msm_reset_workaround(struct sdhci_host *host, u32 enable)
+{
+ u32 vendor_func2;
+ unsigned long timeout;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
+
+ vendor_func2 = readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC_FUNC2);
+
+ if (enable) {
+ writel_relaxed(vendor_func2 | HC_SW_RST_REQ, host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC_FUNC2);
+ timeout = 10000;
+ while (readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC_FUNC2) & HC_SW_RST_REQ) {
+ if (timeout == 0) {
+ pr_info("%s: Applying wait idle disable workaround\n",
+ mmc_hostname(host->mmc));
+ /*
+ * Apply the reset workaround to not wait for
+ * pending data transfers on AXI before
+ * resetting the controller. This could be
+ * risky if the transfers were stuck on the
+ * AXI bus.
+ */
+ vendor_func2 = readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC_FUNC2);
+ writel_relaxed(vendor_func2 |
+ HC_SW_RST_WAIT_IDLE_DIS, host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC_FUNC2);
+ host->reset_wa_t = ktime_get();
+ return;
+ }
+ timeout--;
+ udelay(10);
+ }
+ pr_info("%s: waiting for SW_RST_REQ is successful\n",
+ mmc_hostname(host->mmc));
+ } else {
+ writel_relaxed(vendor_func2 & ~HC_SW_RST_WAIT_IDLE_DIS,
+ host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC_FUNC2);
+ }
+}
+
+static void sdhci_msm_pm_qos_irq_unvote_work(struct work_struct *work)
+{
+ struct sdhci_msm_pm_qos_irq *pm_qos_irq =
+ container_of(work, struct sdhci_msm_pm_qos_irq,
+ unvote_work.work);
+
+ if (atomic_read(&pm_qos_irq->counter))
+ return;
+
+ pm_qos_irq->latency = PM_QOS_DEFAULT_VALUE;
+ pm_qos_update_request(&pm_qos_irq->req, pm_qos_irq->latency);
+}
+
+void sdhci_msm_pm_qos_irq_vote(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ struct sdhci_msm_pm_qos_latency *latency =
+ &msm_host->pdata->pm_qos_data.irq_latency;
+ int counter;
+
+ if (!msm_host->pm_qos_irq.enabled)
+ return;
+
+ counter = atomic_inc_return(&msm_host->pm_qos_irq.counter);
+ /* Make sure to update the voting in case power policy has changed */
+ if (msm_host->pm_qos_irq.latency == latency->latency[host->power_policy]
+ && counter > 1)
+ return;
+
+ cancel_delayed_work_sync(&msm_host->pm_qos_irq.unvote_work);
+ msm_host->pm_qos_irq.latency = latency->latency[host->power_policy];
+ pm_qos_update_request(&msm_host->pm_qos_irq.req,
+ msm_host->pm_qos_irq.latency);
+}
+
+void sdhci_msm_pm_qos_irq_unvote(struct sdhci_host *host, bool async)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int counter;
+
+ if (!msm_host->pm_qos_irq.enabled)
+ return;
+
+ if (atomic_read(&msm_host->pm_qos_irq.counter)) {
+ counter = atomic_dec_return(&msm_host->pm_qos_irq.counter);
+ } else {
+ WARN(1, "attempt to decrement pm_qos_irq.counter when it's 0");
+ return;
+ }
+
+ if (counter)
+ return;
+
+ if (async) {
+ schedule_delayed_work(&msm_host->pm_qos_irq.unvote_work,
+ msecs_to_jiffies(QOS_REMOVE_DELAY_MS));
+ return;
+ }
+
+ msm_host->pm_qos_irq.latency = PM_QOS_DEFAULT_VALUE;
+ pm_qos_update_request(&msm_host->pm_qos_irq.req,
+ msm_host->pm_qos_irq.latency);
+}
+
+static ssize_t
+sdhci_msm_pm_qos_irq_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ struct sdhci_msm_pm_qos_irq *irq = &msm_host->pm_qos_irq;
+
+ return snprintf(buf, PAGE_SIZE,
+ "IRQ PM QoS: enabled=%d, counter=%d, latency=%d\n",
+ irq->enabled, atomic_read(&irq->counter), irq->latency);
+}
+
+static ssize_t
+sdhci_msm_pm_qos_irq_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", msm_host->pm_qos_irq.enabled);
+}
+
+static ssize_t
+sdhci_msm_pm_qos_irq_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ uint32_t value;
+ bool enable;
+ int ret;
+
+ ret = kstrtou32(buf, 0, &value);
+ if (ret)
+ goto out;
+ enable = !!value;
+
+ if (enable == msm_host->pm_qos_irq.enabled)
+ goto out;
+
+ msm_host->pm_qos_irq.enabled = enable;
+ if (!enable) {
+ cancel_delayed_work_sync(&msm_host->pm_qos_irq.unvote_work);
+ atomic_set(&msm_host->pm_qos_irq.counter, 0);
+ msm_host->pm_qos_irq.latency = PM_QOS_DEFAULT_VALUE;
+ pm_qos_update_request(&msm_host->pm_qos_irq.req,
+ msm_host->pm_qos_irq.latency);
+ }
+
+out:
+ return count;
+}
+
+#ifdef CONFIG_SMP
+static inline void set_affine_irq(struct sdhci_msm_host *msm_host,
+ struct sdhci_host *host)
+{
+ msm_host->pm_qos_irq.req.irq = host->irq;
+}
+#else
+static inline void set_affine_irq(struct sdhci_msm_host *msm_host,
+ struct sdhci_host *host) { }
+#endif
+
+void sdhci_msm_pm_qos_irq_init(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ struct sdhci_msm_pm_qos_latency *irq_latency;
+ int ret;
+
+ if (!msm_host->pdata->pm_qos_data.irq_valid)
+ return;
+
+ /* Initialize only once as this gets called per partition */
+ if (msm_host->pm_qos_irq.enabled)
+ return;
+
+ atomic_set(&msm_host->pm_qos_irq.counter, 0);
+ msm_host->pm_qos_irq.req.type =
+ msm_host->pdata->pm_qos_data.irq_req_type;
+ if ((msm_host->pm_qos_irq.req.type != PM_QOS_REQ_AFFINE_CORES) &&
+ (msm_host->pm_qos_irq.req.type != PM_QOS_REQ_ALL_CORES))
+ set_affine_irq(msm_host, host);
+ else
+ cpumask_copy(&msm_host->pm_qos_irq.req.cpus_affine,
+ cpumask_of(msm_host->pdata->pm_qos_data.irq_cpu));
+
+ INIT_DELAYED_WORK(&msm_host->pm_qos_irq.unvote_work,
+ sdhci_msm_pm_qos_irq_unvote_work);
+ /* For initialization phase, set the performance latency */
+ irq_latency = &msm_host->pdata->pm_qos_data.irq_latency;
+ msm_host->pm_qos_irq.latency =
+ irq_latency->latency[SDHCI_PERFORMANCE_MODE];
+ pm_qos_add_request(&msm_host->pm_qos_irq.req, PM_QOS_CPU_DMA_LATENCY,
+ msm_host->pm_qos_irq.latency);
+ msm_host->pm_qos_irq.enabled = true;
+
+ /* sysfs */
+ msm_host->pm_qos_irq.enable_attr.show =
+ sdhci_msm_pm_qos_irq_enable_show;
+ msm_host->pm_qos_irq.enable_attr.store =
+ sdhci_msm_pm_qos_irq_enable_store;
+ sysfs_attr_init(&msm_host->pm_qos_irq.enable_attr.attr);
+ msm_host->pm_qos_irq.enable_attr.attr.name = "pm_qos_irq_enable";
+ msm_host->pm_qos_irq.enable_attr.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(&msm_host->pdev->dev,
+ &msm_host->pm_qos_irq.enable_attr);
+ if (ret)
+ pr_err("%s: fail to create pm_qos_irq_enable (%d)\n",
+ __func__, ret);
+
+ msm_host->pm_qos_irq.status_attr.show = sdhci_msm_pm_qos_irq_show;
+ msm_host->pm_qos_irq.status_attr.store = NULL;
+ sysfs_attr_init(&msm_host->pm_qos_irq.status_attr.attr);
+ msm_host->pm_qos_irq.status_attr.attr.name = "pm_qos_irq_status";
+ msm_host->pm_qos_irq.status_attr.attr.mode = S_IRUGO;
+ ret = device_create_file(&msm_host->pdev->dev,
+ &msm_host->pm_qos_irq.status_attr);
+ if (ret)
+ pr_err("%s: fail to create pm_qos_irq_status (%d)\n",
+ __func__, ret);
+}
+
+static ssize_t sdhci_msm_pm_qos_group_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ struct sdhci_msm_pm_qos_group *group;
+ int i;
+ int nr_groups = msm_host->pdata->pm_qos_data.cpu_group_map.nr_groups;
+ int offset = 0;
+
+ for (i = 0; i < nr_groups; i++) {
+ group = &msm_host->pm_qos[i];
+ offset += snprintf(&buf[offset], PAGE_SIZE,
+ "Group #%d (mask=0x%lx) PM QoS: enabled=%d, counter=%d, latency=%d\n",
+ i, group->req.cpus_affine.bits[0],
+ msm_host->pm_qos_group_enable,
+ atomic_read(&group->counter),
+ group->latency);
+ }
+
+ return offset;
+}
+
+static ssize_t sdhci_msm_pm_qos_group_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ msm_host->pm_qos_group_enable ? "enabled" : "disabled");
+}
+
+static ssize_t sdhci_msm_pm_qos_group_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int nr_groups = msm_host->pdata->pm_qos_data.cpu_group_map.nr_groups;
+ uint32_t value;
+ bool enable;
+ int ret;
+ int i;
+
+ ret = kstrtou32(buf, 0, &value);
+ if (ret)
+ goto out;
+ enable = !!value;
+
+ if (enable == msm_host->pm_qos_group_enable)
+ goto out;
+
+ msm_host->pm_qos_group_enable = enable;
+ if (!enable) {
+ for (i = 0; i < nr_groups; i++) {
+ cancel_delayed_work_sync(
+ &msm_host->pm_qos[i].unvote_work);
+ atomic_set(&msm_host->pm_qos[i].counter, 0);
+ msm_host->pm_qos[i].latency = PM_QOS_DEFAULT_VALUE;
+ pm_qos_update_request(&msm_host->pm_qos[i].req,
+ msm_host->pm_qos[i].latency);
+ }
+ }
+
+out:
+ return count;
+}
+
+static int sdhci_msm_get_cpu_group(struct sdhci_msm_host *msm_host, int cpu)
+{
+ int i;
+ struct sdhci_msm_cpu_group_map *map =
+ &msm_host->pdata->pm_qos_data.cpu_group_map;
+
+ if (cpu < 0)
+ goto not_found;
+
+ for (i = 0; i < map->nr_groups; i++)
+ if (cpumask_test_cpu(cpu, &map->mask[i]))
+ return i;
+
+not_found:
+ return -EINVAL;
+}
+
+void sdhci_msm_pm_qos_cpu_vote(struct sdhci_host *host,
+ struct sdhci_msm_pm_qos_latency *latency, int cpu)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int group = sdhci_msm_get_cpu_group(msm_host, cpu);
+ struct sdhci_msm_pm_qos_group *pm_qos_group;
+ int counter;
+
+ if (!msm_host->pm_qos_group_enable || group < 0)
+ return;
+
+ pm_qos_group = &msm_host->pm_qos[group];
+ counter = atomic_inc_return(&pm_qos_group->counter);
+
+ /* Make sure to update the voting in case power policy has changed */
+ if (pm_qos_group->latency == latency->latency[host->power_policy]
+ && counter > 1)
+ return;
+
+ cancel_delayed_work_sync(&pm_qos_group->unvote_work);
+
+ pm_qos_group->latency = latency->latency[host->power_policy];
+ pm_qos_update_request(&pm_qos_group->req, pm_qos_group->latency);
+}
+
+static void sdhci_msm_pm_qos_cpu_unvote_work(struct work_struct *work)
+{
+ struct sdhci_msm_pm_qos_group *group =
+ container_of(work, struct sdhci_msm_pm_qos_group,
+ unvote_work.work);
+
+ if (atomic_read(&group->counter))
+ return;
+
+ group->latency = PM_QOS_DEFAULT_VALUE;
+ pm_qos_update_request(&group->req, group->latency);
+}
+
+bool sdhci_msm_pm_qos_cpu_unvote(struct sdhci_host *host, int cpu, bool async)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int group = sdhci_msm_get_cpu_group(msm_host, cpu);
+
+ if (!msm_host->pm_qos_group_enable || group < 0 ||
+ atomic_dec_return(&msm_host->pm_qos[group].counter))
+ return false;
+
+ if (async) {
+ schedule_delayed_work(&msm_host->pm_qos[group].unvote_work,
+ msecs_to_jiffies(QOS_REMOVE_DELAY_MS));
+ return true;
+ }
+
+ msm_host->pm_qos[group].latency = PM_QOS_DEFAULT_VALUE;
+ pm_qos_update_request(&msm_host->pm_qos[group].req,
+ msm_host->pm_qos[group].latency);
+ return true;
+}
+
+void sdhci_msm_pm_qos_cpu_init(struct sdhci_host *host,
+ struct sdhci_msm_pm_qos_latency *latency)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int nr_groups = msm_host->pdata->pm_qos_data.cpu_group_map.nr_groups;
+ struct sdhci_msm_pm_qos_group *group;
+ int i;
+ int ret;
+
+ if (msm_host->pm_qos_group_enable)
+ return;
+
+ msm_host->pm_qos = kcalloc(nr_groups, sizeof(*msm_host->pm_qos),
+ GFP_KERNEL);
+ if (!msm_host->pm_qos)
+ return;
+
+ for (i = 0; i < nr_groups; i++) {
+ group = &msm_host->pm_qos[i];
+ INIT_DELAYED_WORK(&group->unvote_work,
+ sdhci_msm_pm_qos_cpu_unvote_work);
+ atomic_set(&group->counter, 0);
+ group->req.type = PM_QOS_REQ_AFFINE_CORES;
+ cpumask_copy(&group->req.cpus_affine,
+ &msm_host->pdata->pm_qos_data.cpu_group_map.mask[i]);
+ /* For initialization phase, set the performance mode latency */
+ group->latency = latency[i].latency[SDHCI_PERFORMANCE_MODE];
+ pm_qos_add_request(&group->req, PM_QOS_CPU_DMA_LATENCY,
+ group->latency);
+ pr_info("%s (): voted for group #%d (mask=0x%lx) latency=%d (0x%p)\n",
+ __func__, i,
+ group->req.cpus_affine.bits[0],
+ group->latency,
+ &latency[i].latency[SDHCI_PERFORMANCE_MODE]);
+ }
+ msm_host->pm_qos_prev_cpu = -1;
+ msm_host->pm_qos_group_enable = true;
+
+ /* sysfs */
+ msm_host->pm_qos_group_status_attr.show = sdhci_msm_pm_qos_group_show;
+ msm_host->pm_qos_group_status_attr.store = NULL;
+ sysfs_attr_init(&msm_host->pm_qos_group_status_attr.attr);
+ msm_host->pm_qos_group_status_attr.attr.name =
+ "pm_qos_cpu_groups_status";
+ msm_host->pm_qos_group_status_attr.attr.mode = S_IRUGO;
+ ret = device_create_file(&msm_host->pdev->dev,
+ &msm_host->pm_qos_group_status_attr);
+ if (ret)
+ dev_err(&msm_host->pdev->dev, "%s: fail to create pm_qos_group_status_attr (%d)\n",
+ __func__, ret);
+ msm_host->pm_qos_group_enable_attr.show =
+ sdhci_msm_pm_qos_group_enable_show;
+ msm_host->pm_qos_group_enable_attr.store =
+ sdhci_msm_pm_qos_group_enable_store;
+ sysfs_attr_init(&msm_host->pm_qos_group_enable_attr.attr);
+ msm_host->pm_qos_group_enable_attr.attr.name =
+ "pm_qos_cpu_groups_enable";
+ msm_host->pm_qos_group_enable_attr.attr.mode = S_IRUGO;
+ ret = device_create_file(&msm_host->pdev->dev,
+ &msm_host->pm_qos_group_enable_attr);
+ if (ret)
+ dev_err(&msm_host->pdev->dev, "%s: fail to create pm_qos_group_enable_attr (%d)\n",
+ __func__, ret);
+}
+
+static void sdhci_msm_pre_req(struct sdhci_host *host,
+ struct mmc_request *mmc_req)
+{
+ int cpu;
+ int group;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int prev_group = sdhci_msm_get_cpu_group(msm_host,
+ msm_host->pm_qos_prev_cpu);
+
+ sdhci_msm_pm_qos_irq_vote(host);
+
+ cpu = get_cpu();
+ put_cpu();
+ group = sdhci_msm_get_cpu_group(msm_host, cpu);
+ if (group < 0)
+ return;
+
+ if (group != prev_group && prev_group >= 0) {
+ sdhci_msm_pm_qos_cpu_unvote(host,
+ msm_host->pm_qos_prev_cpu, false);
+ prev_group = -1; /* make sure to vote for new group */
+ }
+
+ if (prev_group < 0) {
+ sdhci_msm_pm_qos_cpu_vote(host,
+ msm_host->pdata->pm_qos_data.latency, cpu);
+ msm_host->pm_qos_prev_cpu = cpu;
+ }
+}
+
+static void sdhci_msm_post_req(struct sdhci_host *host,
+ struct mmc_request *mmc_req)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ sdhci_msm_pm_qos_irq_unvote(host, false);
+
+ if (sdhci_msm_pm_qos_cpu_unvote(host, msm_host->pm_qos_prev_cpu, false))
+ msm_host->pm_qos_prev_cpu = -1;
+}
+
+static void sdhci_msm_init(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ sdhci_msm_pm_qos_irq_init(host);
+
+ if (msm_host->pdata->pm_qos_data.legacy_valid)
+ sdhci_msm_pm_qos_cpu_init(host,
+ msm_host->pdata->pm_qos_data.latency);
+}
+
+static unsigned int sdhci_msm_get_current_limit(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ struct sdhci_msm_slot_reg_data *curr_slot = msm_host->pdata->vreg_data;
+ u32 max_curr = 0;
+
+ if (curr_slot && curr_slot->vdd_data)
+ max_curr = curr_slot->vdd_data->hpm_uA;
+
+ return max_curr;
}
static struct sdhci_ops sdhci_msm_ops = {
.set_uhs_signaling = sdhci_msm_set_uhs_signaling,
.check_power_status = sdhci_msm_check_power_status,
.platform_execute_tuning = sdhci_msm_execute_tuning,
+ .enhanced_strobe = sdhci_msm_enhanced_strobe,
.toggle_cdr = sdhci_msm_toggle_cdr,
.get_max_segments = sdhci_msm_max_segs,
.set_clock = sdhci_msm_set_clock,
@@ -2482,44 +3931,185 @@ static struct sdhci_ops sdhci_msm_ops = {
.dump_vendor_regs = sdhci_msm_dump_vendor_regs,
.config_auto_tuning_cmd = sdhci_msm_config_auto_tuning_cmd,
.enable_controller_clock = sdhci_msm_enable_controller_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_reset,
+ .clear_set_dumpregs = sdhci_msm_clear_set_dumpregs,
+ .enhanced_strobe_mask = sdhci_msm_enhanced_strobe_mask,
+ .reset_workaround = sdhci_msm_reset_workaround,
+ .init = sdhci_msm_init,
+ .pre_req = sdhci_msm_pre_req,
+ .post_req = sdhci_msm_post_req,
+ .get_current_limit = sdhci_msm_get_current_limit,
};
static void sdhci_set_default_hw_caps(struct sdhci_msm_host *msm_host,
struct sdhci_host *host)
{
- u32 version, caps;
+ u32 version, caps = 0;
u16 minor;
u8 major;
+ u32 val;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
- version = readl_relaxed(msm_host->core_mem + CORE_MCI_VERSION);
+ version = sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_MCI_VERSION);
major = (version & CORE_VERSION_MAJOR_MASK) >>
CORE_VERSION_MAJOR_SHIFT;
minor = version & CORE_VERSION_TARGET_MASK;
+ caps = readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES);
+
/*
* Starting with SDCC 5 controller (core major version = 1)
- * controller won't advertise 3.0v and 8-bit features except for
- * some targets.
+ * controller won't advertise 3.0v, 1.8v and 8-bit features
+ * except for some targets.
*/
if (major >= 1 && minor != 0x11 && minor != 0x12) {
- caps = CORE_3_0V_SUPPORT;
+ struct sdhci_msm_reg_data *vdd_io_reg;
+ /*
+ * Enable 1.8V support capability on controllers that
+ * support dual voltage
+ */
+ vdd_io_reg = msm_host->pdata->vreg_data->vdd_io_data;
+ if (vdd_io_reg && (vdd_io_reg->high_vol_level > 2700000))
+ caps |= CORE_3_0V_SUPPORT;
+ if (vdd_io_reg && (vdd_io_reg->low_vol_level < 1950000))
+ caps |= CORE_1_8V_SUPPORT;
if (msm_host->pdata->mmc_bus_width == MMC_CAP_8_BIT_DATA)
caps |= CORE_8_BIT_SUPPORT;
- writel_relaxed(
- (readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES) |
- caps), host->ioaddr + CORE_VENDOR_SPEC_CAPABILITIES0);
}
+
+ /*
+ * Enable one MID mode for SDCC5 (major 1) on 8916/8939 (minor 0x2e) and
+ * on 8992 (minor 0x3e) as a workaround to reset for data stuck issue.
+ */
+ if (major == 1 && (minor == 0x2e || minor == 0x3e)) {
+ host->quirks2 |= SDHCI_QUIRK2_USE_RESET_WORKAROUND;
+ val = readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC_FUNC2);
+ writel_relaxed((val | CORE_ONE_MID_EN),
+ host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC_FUNC2);
+ }
+ /*
+ * SDCC 5 controller with major version 1, minor version 0x34 and later
+ * with HS 400 mode support will use CM DLL instead of CDC LP 533 DLL.
+ */
+ if ((major == 1) && (minor < 0x34))
+ msm_host->use_cdclp533 = true;
+
+ /*
+ * SDCC 5 controller with major version 1, minor version 0x42 and later
+ * will require additional steps when resetting DLL.
+ * It also supports HS400 enhanced strobe mode.
+ */
+ if ((major == 1) && (minor >= 0x42)) {
+ msm_host->use_updated_dll_reset = true;
+ msm_host->enhanced_strobe = true;
+ }
+
+ /*
+ * SDCC 5 controller with major version 1 and minor version 0x42,
+ * 0x46 and 0x49 currently uses 14lpp tech DLL whose internal
+ * gating cannot guarantee MCLK timing requirement i.e.
+ * when MCLK is gated OFF, it is not gated for less than 0.5us
+ * and MCLK must be switched on for at-least 1us before DATA
+ * starts coming.
+ */
+ if ((major == 1) && ((minor == 0x42) || (minor == 0x46) ||
+ (minor == 0x49)))
+ msm_host->use_14lpp_dll = true;
+
+ /* Fake 3.0V support for SDIO devices which requires such voltage */
+ if (msm_host->pdata->core_3_0v_support) {
+ caps |= CORE_3_0V_SUPPORT;
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ SDHCI_CAPABILITIES) | caps), host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC_CAPABILITIES0);
+ }
+
+ if ((major == 1) && (minor >= 0x49))
+ msm_host->rclk_delay_fix = true;
+ /*
+ * Mask 64-bit support for controller with 32-bit address bus so that
+ * smaller descriptor size will be used and improve memory consumption.
+ */
+ if (!msm_host->pdata->largeaddressbus)
+ caps &= ~CORE_SYS_BUS_SUPPORT_64_BIT;
+
+ writel_relaxed(caps, host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC_CAPABILITIES0);
+ /* keep track of the value in SDHCI_CAPABILITIES */
+ msm_host->caps_0 = caps;
+
+ if ((major == 1) && (minor >= 0x6b))
+ msm_host->ice_hci_support = true;
+}
+
+#ifdef CONFIG_MMC_CQ_HCI
+static void sdhci_msm_cmdq_init(struct sdhci_host *host,
+ struct platform_device *pdev)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ if (nocmdq) {
+ dev_dbg(&pdev->dev, "CMDQ disabled via cmdline\n");
+ return;
+ }
+
+ host->cq_host = cmdq_pltfm_init(pdev);
+ if (IS_ERR(host->cq_host)) {
+ dev_dbg(&pdev->dev, "cmdq-pltfm init: failed: %ld\n",
+ PTR_ERR(host->cq_host));
+ host->cq_host = NULL;
+ } else {
+ msm_host->mmc->caps2 |= MMC_CAP2_CMD_QUEUE;
+ }
+}
+#else
+static void sdhci_msm_cmdq_init(struct sdhci_host *host,
+ struct platform_device *pdev)
+{
+
+}
+#endif
+
+static bool sdhci_msm_is_bootdevice(struct device *dev)
+{
+ if (strnstr(saved_command_line, "androidboot.bootdevice=",
+ strlen(saved_command_line))) {
+ char search_string[50];
+
+ snprintf(search_string, ARRAY_SIZE(search_string),
+ "androidboot.bootdevice=%s", dev_name(dev));
+ if (strnstr(saved_command_line, search_string,
+ strlen(saved_command_line)))
+ return true;
+ else
+ return false;
+ }
+
+ /*
+ * "androidboot.bootdevice=" argument is not present then
+ * return true as we don't know the boot device anyways.
+ */
+ return true;
}
static int sdhci_msm_probe(struct platform_device *pdev)
{
+ const struct sdhci_msm_offset *msm_host_offset;
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_msm_host *msm_host;
struct resource *core_memres = NULL;
- int ret = 0, pwr_irq = 0, dead = 0;
+ int ret = 0, dead = 0;
u16 host_version;
- u32 pwr, irq_status, irq_ctl;
+ u32 irq_status, irq_ctl;
+ struct resource *tlmm_memres = NULL;
+ void __iomem *tlmm_mem;
+ unsigned long flags;
pr_debug("%s: Enter %s\n", dev_name(&pdev->dev), __func__);
msm_host = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_msm_host),
@@ -2529,6 +4119,14 @@ static int sdhci_msm_probe(struct platform_device *pdev)
goto out;
}
+ if (of_find_compatible_node(NULL, NULL, "qcom,sdhci-msm-v5")) {
+ msm_host->mci_removed = true;
+ msm_host->offset = &sdhci_msm_offset_mci_removed;
+ } else {
+ msm_host->mci_removed = false;
+ msm_host->offset = &sdhci_msm_offset_mci_present;
+ }
+ msm_host_offset = msm_host->offset;
msm_host->sdhci_msm_pdata.ops = &sdhci_msm_ops;
host = sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata, 0);
if (IS_ERR(host)) {
@@ -2544,11 +4142,18 @@ static int sdhci_msm_probe(struct platform_device *pdev)
/* Extract platform data */
if (pdev->dev.of_node) {
ret = of_alias_get_id(pdev->dev.of_node, "sdhc");
- if (ret < 0) {
+ if (ret <= 0) {
dev_err(&pdev->dev, "Failed to get slot index %d\n",
ret);
goto pltfm_free;
}
+
+ /* skip the probe if eMMC isn't a boot device */
+ if ((ret == 1) && !sdhci_msm_is_bootdevice(&pdev->dev)) {
+ ret = -ENODEV;
+ goto pltfm_free;
+ }
+
if (disable_slots & (1 << (ret - 1))) {
dev_info(&pdev->dev, "%s: Slot %d disabled\n", __func__,
ret);
@@ -2556,7 +4161,11 @@ static int sdhci_msm_probe(struct platform_device *pdev)
goto pltfm_free;
}
- msm_host->pdata = sdhci_msm_populate_pdata(&pdev->dev);
+ if (ret <= 2)
+ sdhci_slot[ret-1] = msm_host;
+
+ msm_host->pdata = sdhci_msm_populate_pdata(&pdev->dev,
+ msm_host);
if (!msm_host->pdata) {
dev_err(&pdev->dev, "DT parsing error\n");
goto pltfm_free;
@@ -2646,42 +4255,62 @@ static int sdhci_msm_probe(struct platform_device *pdev)
/* Reset the core and Enable SDHC mode */
core_memres = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "core_mem");
- msm_host->core_mem = devm_ioremap(&pdev->dev, core_memres->start,
- resource_size(core_memres));
+ if (!msm_host->mci_removed) {
+ if (!core_memres) {
+ dev_err(&pdev->dev, "Failed to get iomem resource\n");
+ goto vreg_deinit;
+ }
+ msm_host->core_mem = devm_ioremap(&pdev->dev,
+ core_memres->start, resource_size(core_memres));
- if (!msm_host->core_mem) {
- dev_err(&pdev->dev, "Failed to remap registers\n");
- ret = -ENOMEM;
- goto vreg_deinit;
+ if (!msm_host->core_mem) {
+ dev_err(&pdev->dev, "Failed to remap registers\n");
+ ret = -ENOMEM;
+ goto vreg_deinit;
+ }
}
- /* Unset HC_MODE_EN bit in HC_MODE register */
- writel_relaxed(0, (msm_host->core_mem + CORE_HC_MODE));
+ tlmm_memres = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "tlmm_mem");
+ if (tlmm_memres) {
+ tlmm_mem = devm_ioremap(&pdev->dev, tlmm_memres->start,
+ resource_size(tlmm_memres));
- /* Set SW_RST bit in POWER register (Offset 0x0) */
- writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_POWER) |
- CORE_SW_RST, msm_host->core_mem + CORE_POWER);
+ if (!tlmm_mem) {
+ dev_err(&pdev->dev, "Failed to remap tlmm registers\n");
+ ret = -ENOMEM;
+ goto vreg_deinit;
+ }
+ writel_relaxed(readl_relaxed(tlmm_mem) | 0x2, tlmm_mem);
+ dev_dbg(&pdev->dev, "tlmm reg %pa value 0x%08x\n",
+ &tlmm_memres->start, readl_relaxed(tlmm_mem));
+ }
+
/*
- * SW reset can take upto 10HCLK + 15MCLK cycles.
- * Calculating based on min clk rates (hclk = 27MHz,
- * mclk = 400KHz) it comes to ~40us. Let's poll for
- * max. 1ms for reset completion.
+ * Reset the vendor spec register to power on reset state.
*/
- ret = readl_poll_timeout(msm_host->core_mem + CORE_POWER,
- pwr, !(pwr & CORE_SW_RST), 100, 10);
+ writel_relaxed(CORE_VENDOR_SPEC_POR_VAL,
+ host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC);
- if (ret) {
- dev_err(&pdev->dev, "reset failed (%d)\n", ret);
- goto vreg_deinit;
+ if (!msm_host->mci_removed) {
+ /* Set HC_MODE_EN bit in HC_MODE register */
+ writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
+
+ /* Set FF_CLK_SW_RST_DIS bit in HC_MODE register */
+ writel_relaxed(readl_relaxed(msm_host->core_mem +
+ CORE_HC_MODE) | FF_CLK_SW_RST_DIS,
+ msm_host->core_mem + CORE_HC_MODE);
}
- /* Set HC_MODE_EN bit in HC_MODE register */
- writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
-
- /* Set FF_CLK_SW_RST_DIS bit in HC_MODE register */
- writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_HC_MODE) |
- FF_CLK_SW_RST_DIS, msm_host->core_mem + CORE_HC_MODE);
-
sdhci_set_default_hw_caps(msm_host, host);
+
+ /*
+ * Set the PAD_PWR_SWTICH_EN bit so that the PAD_PWR_SWITCH bit can
+ * be used as required later on.
+ */
+ writel_relaxed((readl_relaxed(host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC) |
+ CORE_IO_PAD_PWR_SWITCH_EN), host->ioaddr +
+ msm_host_offset->CORE_VENDOR_SPEC);
/*
* CORE_SW_RST above may trigger power irq if previous status of PWRCTL
* was either BUS_ON or IO_HIGH_V. So before we enable the power irq
@@ -2689,14 +4318,20 @@ static int sdhci_msm_probe(struct platform_device *pdev)
* ensure that any pending power irq interrupt status is acknowledged
* otherwise power irq interrupt handler would be fired prematurely.
*/
- irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS);
- writel_relaxed(irq_status, (msm_host->core_mem + CORE_PWRCTL_CLEAR));
- irq_ctl = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL);
+ irq_status = sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_PWRCTL_STATUS);
+ sdhci_msm_writel_relaxed(irq_status, host,
+ msm_host_offset->CORE_PWRCTL_CLEAR);
+ irq_ctl = sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_PWRCTL_CTL);
+
if (irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF))
irq_ctl |= CORE_PWRCTL_BUS_SUCCESS;
if (irq_status & (CORE_PWRCTL_IO_HIGH | CORE_PWRCTL_IO_LOW))
irq_ctl |= CORE_PWRCTL_IO_SUCCESS;
- writel_relaxed(irq_ctl, (msm_host->core_mem + CORE_PWRCTL_CTL));
+ sdhci_msm_writel_relaxed(irq_ctl, host,
+ msm_host_offset->CORE_PWRCTL_CTL);
+
/*
* Ensure that above writes are propogated before interrupt enablement
* in GIC.
@@ -2711,10 +4346,13 @@ static int sdhci_msm_probe(struct platform_device *pdev)
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE;
host->quirks |= SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN;
+ host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
host->quirks2 |= SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK;
host->quirks2 |= SDHCI_QUIRK2_IGNORE_DATATOUT_FOR_R1BCMD;
host->quirks2 |= SDHCI_QUIRK2_BROKEN_PRESET_VALUE;
host->quirks2 |= SDHCI_QUIRK2_USE_RESERVED_MAX_TIMEOUT;
+ host->quirks2 |= SDHCI_QUIRK2_NON_STANDARD_TUNING;
+ host->quirks2 |= SDHCI_QUIRK2_USE_PIO_FOR_EMMC_TUNING;
if (host->quirks2 & SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK)
host->quirks2 |= SDHCI_QUIRK2_DIVIDE_TOUT_BY_4;
@@ -2740,23 +4378,24 @@ static int sdhci_msm_probe(struct platform_device *pdev)
host->quirks2 |= SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR;
/* Setup PWRCTL irq */
- pwr_irq = platform_get_irq_byname(pdev, "pwr_irq");
- if (pwr_irq < 0) {
+ msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq");
+ if (msm_host->pwr_irq < 0) {
dev_err(&pdev->dev, "Failed to get pwr_irq by name (%d)\n",
- pwr_irq);
+ msm_host->pwr_irq);
goto vreg_deinit;
}
- ret = devm_request_threaded_irq(&pdev->dev, pwr_irq, NULL,
+ ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL,
sdhci_msm_pwr_irq, IRQF_ONESHOT,
dev_name(&pdev->dev), host);
if (ret) {
dev_err(&pdev->dev, "Request threaded irq(%d) failed (%d)\n",
- pwr_irq, ret);
+ msm_host->pwr_irq, ret);
goto vreg_deinit;
}
/* Enable pwr irq interrupts */
- writel_relaxed(INT_MASK, (msm_host->core_mem + CORE_PWRCTL_MASK));
+ sdhci_msm_writel_relaxed(INT_MASK, host,
+ msm_host_offset->CORE_PWRCTL_MASK);
#ifdef CONFIG_MMC_CLKGATE
/* Set clock gating delay to be used when CONFIG_MMC_CLKGATE is set */
@@ -2766,21 +4405,38 @@ static int sdhci_msm_probe(struct platform_device *pdev)
/* Set host capabilities */
msm_host->mmc->caps |= msm_host->pdata->mmc_bus_width;
msm_host->mmc->caps |= msm_host->pdata->caps;
+ msm_host->mmc->caps |= MMC_CAP_AGGRESSIVE_PM;
+ msm_host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
msm_host->mmc->caps2 |= msm_host->pdata->caps2;
- msm_host->mmc->caps2 |= MMC_CAP2_PACKED_WR;
- msm_host->mmc->caps2 |= MMC_CAP2_PACKED_WR_CONTROL;
+ msm_host->mmc->caps2 |= MMC_CAP2_BOOTPART_NOACC;
+ msm_host->mmc->caps2 |= MMC_CAP2_HS400_POST_TUNING;
msm_host->mmc->caps2 |= MMC_CAP2_CLK_SCALE;
- msm_host->mmc->caps2 |= MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE;
- msm_host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
+ msm_host->mmc->caps2 |= MMC_CAP2_SANITIZE;
+ msm_host->mmc->caps2 |= MMC_CAP2_MAX_DISCARD_SIZE;
+ msm_host->mmc->caps2 |= MMC_CAP2_SLEEP_AWAKE;
+ msm_host->mmc->pm_caps |= MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ;
if (msm_host->pdata->nonremovable)
msm_host->mmc->caps |= MMC_CAP_NONREMOVABLE;
- host->cpu_dma_latency_us = msm_host->pdata->cpu_dma_latency_us;
+ if (msm_host->pdata->nonhotplug)
+ msm_host->mmc->caps2 |= MMC_CAP2_NONHOTPLUG;
init_completion(&msm_host->pwr_irq_completion);
if (gpio_is_valid(msm_host->pdata->status_gpio)) {
+ /*
+ * Set up the card detect GPIO in active configuration before
+ * configuring it as an IRQ. Otherwise, it can be in some
+ * weird/inconsistent state resulting in flood of interrupts.
+ */
+ sdhci_msm_setup_pins(msm_host->pdata, true);
+
+ /*
+ * This delay is needed for stabilizing the card detect GPIO
+ * line after changing the pull configs.
+ */
+ usleep_range(10000, 10500);
ret = mmc_gpio_request_cd(msm_host->mmc,
msm_host->pdata->status_gpio, 0);
if (ret) {
@@ -2794,19 +4450,51 @@ static int sdhci_msm_probe(struct platform_device *pdev)
(dma_supported(mmc_dev(host->mmc), DMA_BIT_MASK(64)))) {
host->dma_mask = DMA_BIT_MASK(64);
mmc_dev(host->mmc)->dma_mask = &host->dma_mask;
+ mmc_dev(host->mmc)->coherent_dma_mask = host->dma_mask;
} else if (dma_supported(mmc_dev(host->mmc), DMA_BIT_MASK(32))) {
host->dma_mask = DMA_BIT_MASK(32);
mmc_dev(host->mmc)->dma_mask = &host->dma_mask;
+ mmc_dev(host->mmc)->coherent_dma_mask = host->dma_mask;
} else {
dev_err(&pdev->dev, "%s: Failed to set dma mask\n", __func__);
}
+ msm_host->pdata->sdiowakeup_irq = platform_get_irq_byname(pdev,
+ "sdiowakeup_irq");
+ if (sdhci_is_valid_gpio_wakeup_int(msm_host)) {
+ dev_info(&pdev->dev, "%s: sdiowakeup_irq = %d\n", __func__,
+ msm_host->pdata->sdiowakeup_irq);
+ msm_host->is_sdiowakeup_enabled = true;
+ ret = request_irq(msm_host->pdata->sdiowakeup_irq,
+ sdhci_msm_sdiowakeup_irq,
+ IRQF_SHARED | IRQF_TRIGGER_HIGH,
+ "sdhci-msm sdiowakeup", host);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: request sdiowakeup IRQ %d: failed: %d\n",
+ __func__, msm_host->pdata->sdiowakeup_irq, ret);
+ msm_host->pdata->sdiowakeup_irq = -1;
+ msm_host->is_sdiowakeup_enabled = false;
+ goto vreg_deinit;
+ } else {
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false);
+ msm_host->sdio_pending_processing = false;
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+ }
+
+ sdhci_msm_cmdq_init(host, pdev);
ret = sdhci_add_host(host);
if (ret) {
dev_err(&pdev->dev, "Add host failed (%d)\n", ret);
goto vreg_deinit;
}
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, MSM_AUTOSUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+
msm_host->msm_bus_vote.max_bus_bw.show = show_sdhci_max_bus_bw;
msm_host->msm_bus_vote.max_bus_bw.store = store_sdhci_max_bus_bw;
sysfs_attr_init(&msm_host->msm_bus_vote.max_bus_bw.attr);
@@ -2839,7 +4527,6 @@ static int sdhci_msm_probe(struct platform_device *pdev)
mmc_hostname(host->mmc), __func__, ret);
device_remove_file(&pdev->dev, &msm_host->auto_cmd21_attr);
}
-
/* Successful initialization */
goto out;
@@ -2847,6 +4534,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
device_remove_file(&pdev->dev, &msm_host->msm_bus_vote.max_bus_bw);
remove_host:
dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
+ pm_runtime_disable(&pdev->dev);
sdhci_remove_host(host, dead);
vreg_deinit:
sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false);
@@ -2889,13 +4577,14 @@ static int sdhci_msm_remove(struct platform_device *pdev)
if (!gpio_is_valid(msm_host->pdata->status_gpio))
device_remove_file(&pdev->dev, &msm_host->polling);
device_remove_file(&pdev->dev, &msm_host->msm_bus_vote.max_bus_bw);
+ pm_runtime_disable(&pdev->dev);
sdhci_remove_host(host, dead);
sdhci_pltfm_free(pdev);
sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false);
- if (pdata->pin_data)
- sdhci_msm_setup_gpio(pdata, false);
+ sdhci_msm_setup_pins(pdata, true);
+ sdhci_msm_setup_pins(pdata, false);
if (msm_host->msm_bus_vote.client_handle) {
sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
@@ -2904,8 +4593,209 @@ static int sdhci_msm_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int sdhci_msm_cfg_sdio_wakeup(struct sdhci_host *host, bool enable)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!(host->mmc->card && mmc_card_sdio(host->mmc->card) &&
+ sdhci_is_valid_gpio_wakeup_int(msm_host) &&
+ mmc_card_wake_sdio_irq(host->mmc))) {
+ msm_host->sdio_pending_processing = false;
+ return 1;
+ }
+
+ spin_lock_irqsave(&host->lock, flags);
+ if (enable) {
+ /* configure DAT1 gpio if applicable */
+ if (sdhci_is_valid_gpio_wakeup_int(msm_host)) {
+ msm_host->sdio_pending_processing = false;
+ ret = enable_irq_wake(msm_host->pdata->sdiowakeup_irq);
+ if (!ret)
+ sdhci_msm_cfg_sdiowakeup_gpio_irq(host, true);
+ goto out;
+ } else {
+ pr_err("%s: sdiowakeup_irq(%d) invalid\n",
+ mmc_hostname(host->mmc), enable);
+ }
+ } else {
+ if (sdhci_is_valid_gpio_wakeup_int(msm_host)) {
+ ret = disable_irq_wake(msm_host->pdata->sdiowakeup_irq);
+ sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false);
+ msm_host->sdio_pending_processing = false;
+ } else {
+ pr_err("%s: sdiowakeup_irq(%d)invalid\n",
+ mmc_hostname(host->mmc), enable);
+
+ }
+ }
+out:
+ if (ret)
+ pr_err("%s: %s: %sable wakeup: failed: %d gpio: %d\n",
+ mmc_hostname(host->mmc), __func__, enable ? "en" : "dis",
+ ret, msm_host->pdata->sdiowakeup_irq);
+ spin_unlock_irqrestore(&host->lock, flags);
+ return ret;
+}
+
+
+static int sdhci_msm_runtime_suspend(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ ktime_t start = ktime_get();
+
+ if (host->mmc->card && mmc_card_sdio(host->mmc->card))
+ goto defer_disable_host_irq;
+
+ sdhci_cfg_irq(host, false, true);
+
+defer_disable_host_irq:
+ disable_irq(msm_host->pwr_irq);
+
+ /*
+ * Remove the vote immediately only if clocks are off in which
+ * case we might have queued work to remove vote but it may not
+ * be completed before runtime suspend or system suspend.
+ */
+ if (!atomic_read(&msm_host->clks_on)) {
+ if (msm_host->msm_bus_vote.client_handle)
+ sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
+ }
+ trace_sdhci_msm_runtime_suspend(mmc_hostname(host->mmc), 0,
+ ktime_to_us(ktime_sub(ktime_get(), start)));
+
+ return 0;
+}
+
+static int sdhci_msm_runtime_resume(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ ktime_t start = ktime_get();
+
+ if (host->mmc->card && mmc_card_sdio(host->mmc->card))
+ goto defer_enable_host_irq;
+
+ sdhci_cfg_irq(host, true, true);
+
+defer_enable_host_irq:
+ enable_irq(msm_host->pwr_irq);
+
+ trace_sdhci_msm_runtime_resume(mmc_hostname(host->mmc), 0,
+ ktime_to_us(ktime_sub(ktime_get(), start)));
+ return 0;
+}
+
+static int sdhci_msm_suspend(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int ret = 0;
+ int sdio_cfg = 0;
+ ktime_t start = ktime_get();
+
+ if (gpio_is_valid(msm_host->pdata->status_gpio) &&
+ (msm_host->mmc->slot.cd_irq >= 0))
+ disable_irq(msm_host->mmc->slot.cd_irq);
+
+ if (pm_runtime_suspended(dev)) {
+ pr_debug("%s: %s: already runtime suspended\n",
+ mmc_hostname(host->mmc), __func__);
+ goto out;
+ }
+ ret = sdhci_msm_runtime_suspend(dev);
+out:
+ sdhci_msm_disable_controller_clock(host);
+ if (host->mmc->card && mmc_card_sdio(host->mmc->card)) {
+ sdio_cfg = sdhci_msm_cfg_sdio_wakeup(host, true);
+ if (sdio_cfg)
+ sdhci_cfg_irq(host, false, true);
+ }
+
+ trace_sdhci_msm_suspend(mmc_hostname(host->mmc), ret,
+ ktime_to_us(ktime_sub(ktime_get(), start)));
+ return ret;
+}
+
+static int sdhci_msm_resume(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int ret = 0;
+ int sdio_cfg = 0;
+ ktime_t start = ktime_get();
+
+ if (gpio_is_valid(msm_host->pdata->status_gpio) &&
+ (msm_host->mmc->slot.cd_irq >= 0))
+ enable_irq(msm_host->mmc->slot.cd_irq);
+
+ if (pm_runtime_suspended(dev)) {
+ pr_debug("%s: %s: runtime suspended, defer system resume\n",
+ mmc_hostname(host->mmc), __func__);
+ goto out;
+ }
+
+ ret = sdhci_msm_runtime_resume(dev);
+out:
+ if (host->mmc->card && mmc_card_sdio(host->mmc->card)) {
+ sdio_cfg = sdhci_msm_cfg_sdio_wakeup(host, false);
+ if (sdio_cfg)
+ sdhci_cfg_irq(host, true, true);
+ }
+
+ trace_sdhci_msm_resume(mmc_hostname(host->mmc), ret,
+ ktime_to_us(ktime_sub(ktime_get(), start)));
+ return ret;
+}
+
+static int sdhci_msm_suspend_noirq(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int ret = 0;
+
+ /*
+ * ksdioirqd may be running, hence retry
+ * suspend in case the clocks are ON
+ */
+ if (atomic_read(&msm_host->clks_on)) {
+ pr_warn("%s: %s: clock ON after suspend, aborting suspend\n",
+ mmc_hostname(host->mmc), __func__);
+ ret = -EAGAIN;
+ }
+
+ if (host->mmc->card && mmc_card_sdio(host->mmc->card))
+ if (msm_host->sdio_pending_processing)
+ ret = -EBUSY;
+
+ return ret;
+}
+
+static const struct dev_pm_ops sdhci_msm_pmops = {
+ SET_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend, sdhci_msm_resume)
+ SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, sdhci_msm_runtime_resume,
+ NULL)
+ .suspend_noirq = sdhci_msm_suspend_noirq,
+};
+
+#define SDHCI_MSM_PMOPS (&sdhci_msm_pmops)
+
+#else
+#define SDHCI_MSM_PMOPS NULL
+#endif
static const struct of_device_id sdhci_msm_dt_match[] = {
{.compatible = "qcom,sdhci-msm"},
+ {.compatible = "qcom,sdhci-msm-v5"},
+ {},
};
MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);
@@ -2916,6 +4806,7 @@ static struct platform_driver sdhci_msm_driver = {
.name = "sdhci_msm",
.owner = THIS_MODULE,
.of_match_table = sdhci_msm_dt_match,
+ .pm = SDHCI_MSM_PMOPS,
},
};
diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h
new file mode 100644
index 0000000..533b241
--- /dev/null
+++ b/drivers/mmc/host/sdhci-msm.h
@@ -0,0 +1,235 @@
+/*
+ * 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
+ * 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 __SDHCI_MSM_H__
+#define __SDHCI_MSM_H__
+
+#include <linux/mmc/mmc.h>
+#include <linux/pm_qos.h>
+#include "sdhci-pltfm.h"
+
+/* This structure keeps information per regulator */
+struct sdhci_msm_reg_data {
+ /* voltage regulator handle */
+ struct regulator *reg;
+ /* regulator name */
+ const char *name;
+ /* voltage level to be set */
+ u32 low_vol_level;
+ u32 high_vol_level;
+ /* Load values for low power and high power mode */
+ u32 lpm_uA;
+ u32 hpm_uA;
+
+ /* is this regulator enabled? */
+ bool is_enabled;
+ /* is this regulator needs to be always on? */
+ bool is_always_on;
+ /* is low power mode setting required for this regulator? */
+ bool lpm_sup;
+ bool set_voltage_sup;
+};
+
+/*
+ * This structure keeps information for all the
+ * regulators required for a SDCC slot.
+ */
+struct sdhci_msm_slot_reg_data {
+ /* keeps VDD/VCC regulator info */
+ struct sdhci_msm_reg_data *vdd_data;
+ /* keeps VDD IO regulator info */
+ struct sdhci_msm_reg_data *vdd_io_data;
+};
+
+struct sdhci_msm_gpio {
+ u32 no;
+ const char *name;
+ bool is_enabled;
+};
+
+struct sdhci_msm_gpio_data {
+ struct sdhci_msm_gpio *gpio;
+ u8 size;
+};
+
+struct sdhci_msm_pin_data {
+ /*
+ * = 1 if controller pins are using gpios
+ * = 0 if controller has dedicated MSM pads
+ */
+ u8 is_gpio;
+ struct sdhci_msm_gpio_data *gpio_data;
+};
+
+struct sdhci_pinctrl_data {
+ struct pinctrl *pctrl;
+ struct pinctrl_state *pins_active;
+ struct pinctrl_state *pins_sleep;
+};
+
+struct sdhci_msm_bus_voting_data {
+ struct msm_bus_scale_pdata *bus_pdata;
+ unsigned int *bw_vecs;
+ unsigned int bw_vecs_size;
+};
+
+struct sdhci_msm_cpu_group_map {
+ int nr_groups;
+ cpumask_t *mask;
+};
+
+struct sdhci_msm_pm_qos_latency {
+ s32 latency[SDHCI_POWER_POLICY_NUM];
+};
+
+struct sdhci_msm_pm_qos_data {
+ struct sdhci_msm_cpu_group_map cpu_group_map;
+ enum pm_qos_req_type irq_req_type;
+ int irq_cpu;
+ struct sdhci_msm_pm_qos_latency irq_latency;
+ struct sdhci_msm_pm_qos_latency *cmdq_latency;
+ struct sdhci_msm_pm_qos_latency *latency;
+ bool irq_valid;
+ bool cmdq_valid;
+ bool legacy_valid;
+};
+
+/*
+ * PM QoS for group voting management - each cpu group defined is associated
+ * with 1 instance of this structure.
+ */
+struct sdhci_msm_pm_qos_group {
+ struct pm_qos_request req;
+ struct delayed_work unvote_work;
+ atomic_t counter;
+ s32 latency;
+};
+
+/* PM QoS HW IRQ voting */
+struct sdhci_msm_pm_qos_irq {
+ struct pm_qos_request req;
+ struct delayed_work unvote_work;
+ struct device_attribute enable_attr;
+ struct device_attribute status_attr;
+ atomic_t counter;
+ s32 latency;
+ bool enabled;
+};
+
+struct sdhci_msm_pltfm_data {
+ /* Supported UHS-I Modes */
+ u32 caps;
+
+ /* More capabilities */
+ u32 caps2;
+
+ unsigned long mmc_bus_width;
+ struct sdhci_msm_slot_reg_data *vreg_data;
+ bool nonremovable;
+ bool nonhotplug;
+ bool largeaddressbus;
+ bool pin_cfg_sts;
+ struct sdhci_msm_pin_data *pin_data;
+ struct sdhci_pinctrl_data *pctrl_data;
+ int status_gpio; /* card detection GPIO that is configured as IRQ */
+ struct sdhci_msm_bus_voting_data *voting_data;
+ u32 *sup_clk_table;
+ unsigned char sup_clk_cnt;
+ int sdiowakeup_irq;
+ u32 *sup_ice_clk_table;
+ unsigned char sup_ice_clk_cnt;
+ struct sdhci_msm_pm_qos_data pm_qos_data;
+ bool core_3_0v_support;
+};
+
+struct sdhci_msm_bus_vote {
+ uint32_t client_handle;
+ uint32_t curr_vote;
+ int min_bw_vote;
+ int max_bw_vote;
+ bool is_max_bw_needed;
+ struct delayed_work vote_work;
+ struct device_attribute max_bus_bw;
+};
+
+struct sdhci_msm_ice_data {
+ struct qcom_ice_variant_ops *vops;
+ struct completion async_done;
+ struct platform_device *pdev;
+ int state;
+};
+
+struct sdhci_msm_host {
+ struct platform_device *pdev;
+ void __iomem *core_mem; /* MSM SDCC mapped address */
+ void __iomem *cryptoio; /* ICE HCI mapped address */
+ bool ice_hci_support;
+ int pwr_irq; /* power irq */
+ struct clk *clk; /* main SD/MMC bus clock */
+ struct clk *pclk; /* SDHC peripheral bus clock */
+ struct clk *bus_clk; /* SDHC bus voter clock */
+ struct clk *ff_clk; /* CDC calibration fixed feedback clock */
+ struct clk *sleep_clk; /* CDC calibration sleep clock */
+ struct clk *ice_clk; /* SDHC peripheral ICE clock */
+ atomic_t clks_on; /* Set if clocks are enabled */
+ struct sdhci_msm_pltfm_data *pdata;
+ struct mmc_host *mmc;
+ struct sdhci_pltfm_data sdhci_msm_pdata;
+ u32 curr_pwr_state;
+ u32 curr_io_level;
+ struct completion pwr_irq_completion;
+ struct sdhci_msm_bus_vote msm_bus_vote;
+ struct device_attribute polling;
+ u32 clk_rate; /* Keeps track of current clock rate that is set */
+ bool tuning_done;
+ bool calibration_done;
+ u8 saved_tuning_phase;
+ bool en_auto_cmd21;
+ struct device_attribute auto_cmd21_attr;
+ bool is_sdiowakeup_enabled;
+ bool sdio_pending_processing;
+ atomic_t controller_clock;
+ bool use_cdclp533;
+ bool use_updated_dll_reset;
+ bool use_14lpp_dll;
+ bool enhanced_strobe;
+ bool rclk_delay_fix;
+ u32 caps_0;
+ struct sdhci_msm_ice_data ice;
+ u32 ice_clk_rate;
+ struct sdhci_msm_pm_qos_group *pm_qos;
+ int pm_qos_prev_cpu;
+ struct device_attribute pm_qos_group_enable_attr;
+ struct device_attribute pm_qos_group_status_attr;
+ bool pm_qos_group_enable;
+ struct sdhci_msm_pm_qos_irq pm_qos_irq;
+ bool tuning_in_progress;
+ bool mci_removed;
+ const struct sdhci_msm_offset *offset;
+};
+
+extern char *saved_command_line;
+
+void sdhci_msm_pm_qos_irq_init(struct sdhci_host *host);
+void sdhci_msm_pm_qos_irq_vote(struct sdhci_host *host);
+void sdhci_msm_pm_qos_irq_unvote(struct sdhci_host *host, bool async);
+
+void sdhci_msm_pm_qos_cpu_init(struct sdhci_host *host,
+ struct sdhci_msm_pm_qos_latency *latency);
+void sdhci_msm_pm_qos_cpu_vote(struct sdhci_host *host,
+ struct sdhci_msm_pm_qos_latency *latency, int cpu);
+bool sdhci_msm_pm_qos_cpu_unvote(struct sdhci_host *host, int cpu, bool async);
+
+
+#endif /* __SDHCI_MSM_H__ */
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 1bb11e4..3c27401 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -559,16 +559,19 @@ static const struct sdhci_ops sdhci_esdhc_le_ops = {
};
static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = {
- .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION
- | SDHCI_QUIRK_NO_CARD_NO_RESET
- | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+ .quirks = ESDHC_DEFAULT_QUIRKS |
+#ifdef CONFIG_PPC
+ SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+#endif
+ SDHCI_QUIRK_NO_CARD_NO_RESET |
+ SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.ops = &sdhci_esdhc_be_ops,
};
static const struct sdhci_pltfm_data sdhci_esdhc_le_pdata = {
- .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION
- | SDHCI_QUIRK_NO_CARD_NO_RESET
- | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+ .quirks = ESDHC_DEFAULT_QUIRKS |
+ SDHCI_QUIRK_NO_CARD_NO_RESET |
+ SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.ops = &sdhci_esdhc_le_ops,
};
@@ -623,8 +626,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
of_device_is_compatible(np, "fsl,p5020-esdhc") ||
of_device_is_compatible(np, "fsl,p4080-esdhc") ||
of_device_is_compatible(np, "fsl,p1020-esdhc") ||
- of_device_is_compatible(np, "fsl,t1040-esdhc") ||
- of_device_is_compatible(np, "fsl,ls1021a-esdhc"))
+ of_device_is_compatible(np, "fsl,t1040-esdhc"))
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
if (of_device_is_compatible(np, "fsl,ls1021a-esdhc"))
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 60be7c5..744e520 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -32,7 +32,10 @@
#include <linux/mmc/slot-gpio.h>
#include <linux/mmc/sdio.h>
+#include <trace/events/mmc.h>
+
#include "sdhci.h"
+#include "cmdq_hci.h"
#define DRIVER_NAME "sdhci"
@@ -41,6 +44,9 @@
#define MAX_TUNING_LOOP 40
+#define SDHCI_DBG_DUMP_RS_INTERVAL (10 * HZ)
+#define SDHCI_DBG_DUMP_RS_BURST 2
+
static unsigned int debug_quirks = 0;
static unsigned int debug_quirks2;
@@ -48,6 +54,8 @@ static void sdhci_finish_data(struct sdhci_host *);
static bool sdhci_check_state(struct sdhci_host *);
+static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable);
+
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
static void sdhci_dump_state(struct sdhci_host *host)
@@ -55,9 +63,10 @@ static void sdhci_dump_state(struct sdhci_host *host)
struct mmc_host *mmc = host->mmc;
#ifdef CONFIG_MMC_CLKGATE
- pr_info("%s: clk: %d clk-gated: %d claimer: %s pwr: %d\n",
+ pr_info("%s: clk: %d clk-gated: %d claimer: %s pwr: %d host->irq = %d\n",
mmc_hostname(mmc), host->clock, mmc->clk_gated,
- mmc->claimer->comm, host->pwr);
+ mmc->claimer->comm, host->pwr,
+ (host->flags & SDHCI_HOST_IRQ_STATUS));
#else
pr_info("%s: clk: %d claimer: %s pwr: %d\n",
mmc_hostname(mmc), host->clock,
@@ -71,6 +80,17 @@ static void sdhci_dump_state(struct sdhci_host *host)
static void sdhci_dumpregs(struct sdhci_host *host)
{
+ MMC_TRACE(host->mmc,
+ "%s: 0x04=0x%08x 0x06=0x%08x 0x0E=0x%08x 0x30=0x%08x 0x34=0x%08x 0x38=0x%08x\n",
+ __func__,
+ sdhci_readw(host, SDHCI_BLOCK_SIZE),
+ sdhci_readw(host, SDHCI_BLOCK_COUNT),
+ sdhci_readw(host, SDHCI_COMMAND),
+ sdhci_readl(host, SDHCI_INT_STATUS),
+ sdhci_readl(host, SDHCI_INT_ENABLE),
+ sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
+ mmc_stop_tracing(host->mmc);
+
pr_info(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
mmc_hostname(host->mmc));
@@ -134,33 +154,6 @@ static void sdhci_dumpregs(struct sdhci_host *host)
pr_info(DRIVER_NAME ": ===========================================\n");
}
-#define MAX_PM_QOS_TIMEOUT_VALUE 100000 /* 100 ms */
-static ssize_t
-show_sdhci_pm_qos_tout(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct sdhci_host *host = dev_get_drvdata(dev);
-
- return snprintf(buf, PAGE_SIZE, "%d us\n", host->pm_qos_timeout_us);
-}
-
-static ssize_t
-store_sdhci_pm_qos_tout(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct sdhci_host *host = dev_get_drvdata(dev);
- uint32_t value;
- unsigned long flags;
-
- if (!kstrtou32(buf, 0, &value)) {
- spin_lock_irqsave(&host->lock, flags);
- if (value <= MAX_PM_QOS_TIMEOUT_VALUE)
- host->pm_qos_timeout_us = value;
- spin_unlock_irqrestore(&host->lock, flags);
- }
- return count;
-}
-
/*****************************************************************************\
* *
* Low level functions *
@@ -224,6 +217,7 @@ void sdhci_reset(struct sdhci_host *host, u8 mask)
{
unsigned long timeout;
+retry_reset:
sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
if (mask & SDHCI_RESET_ALL) {
@@ -234,23 +228,58 @@ void sdhci_reset(struct sdhci_host *host, u8 mask)
}
/* Wait max 100 ms */
- timeout = 100;
+ timeout = 100000;
if (host->ops->check_power_status && host->pwr &&
(mask & SDHCI_RESET_ALL))
host->ops->check_power_status(host, REQ_BUS_OFF);
+ /* clear pending normal/error interrupt status */
+ sdhci_writel(host, sdhci_readl(host, SDHCI_INT_STATUS),
+ SDHCI_INT_STATUS);
+
/* hw clears the bit when it's done */
while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
if (timeout == 0) {
pr_err("%s: Reset 0x%x never completed.\n",
mmc_hostname(host->mmc), (int)mask);
+ if ((host->quirks2 & SDHCI_QUIRK2_USE_RESET_WORKAROUND)
+ && host->ops->reset_workaround) {
+ if (!host->reset_wa_applied) {
+ /*
+ * apply the workaround and issue
+ * reset again.
+ */
+ host->ops->reset_workaround(host, 1);
+ host->reset_wa_applied = 1;
+ host->reset_wa_cnt++;
+ goto retry_reset;
+ } else {
+ pr_err("%s: Reset 0x%x failed with workaround\n",
+ mmc_hostname(host->mmc),
+ (int)mask);
+ /* clear the workaround */
+ host->ops->reset_workaround(host, 0);
+ host->reset_wa_applied = 0;
+ }
+ }
+
sdhci_dumpregs(host);
return;
}
timeout--;
- mdelay(1);
+ udelay(1);
}
+
+ if ((host->quirks2 & SDHCI_QUIRK2_USE_RESET_WORKAROUND) &&
+ host->ops->reset_workaround && host->reset_wa_applied) {
+ pr_info("%s: Reset 0x%x successful with workaround\n",
+ mmc_hostname(host->mmc), (int)mask);
+ /* clear the workaround */
+ host->ops->reset_workaround(host, 0);
+ host->reset_wa_applied = 0;
+ }
+
}
EXPORT_SYMBOL_GPL(sdhci_reset);
@@ -336,6 +365,9 @@ static void sdhci_led_control(struct led_classdev *led,
struct sdhci_host *host = container_of(led, struct sdhci_host, led);
unsigned long flags;
+ if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+ return;
+
spin_lock_irqsave(&host->lock, flags);
if (host->runtime_suspended || sdhci_check_state(host))
@@ -684,6 +716,9 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
void *align;
char *buffer;
unsigned long flags;
+ u32 command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
+
+ trace_mmc_adma_table_post(command, data->sg_len);
if (data->flags & MMC_DATA_READ) {
bool has_unaligned = false;
@@ -860,6 +895,10 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
host->flags |= SDHCI_REQ_USE_DMA;
+ if ((host->quirks2 & SDHCI_QUIRK2_USE_PIO_FOR_EMMC_TUNING) &&
+ cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
+ host->flags &= ~SDHCI_REQ_USE_DMA;
+
/*
* FIXME: This doesn't account for merging when mapping the
* scatterlist.
@@ -914,6 +953,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
WARN_ON(1);
host->flags &= ~SDHCI_REQ_USE_DMA;
} else if (host->flags & SDHCI_USE_ADMA) {
+ trace_mmc_adma_table_pre(cmd->opcode, data->sg_len);
sdhci_adma_table_pre(host, data, sg_cnt);
sdhci_writel(host, host->adma_addr, SDHCI_ADMA_ADDRESS);
@@ -965,6 +1005,11 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
/* Set the DMA boundary value and block size */
sdhci_set_blk_size_reg(host, data->blksz, SDHCI_DEFAULT_BOUNDARY_ARG);
sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
+ MMC_TRACE(host->mmc,
+ "%s: 0x28=0x%08x 0x3E=0x%08x 0x06=0x%08x\n", __func__,
+ sdhci_readb(host, SDHCI_HOST_CONTROL),
+ sdhci_readw(host, SDHCI_HOST_CONTROL2),
+ sdhci_readw(host, SDHCI_BLOCK_COUNT));
}
static inline bool sdhci_auto_cmd12(struct sdhci_host *host,
@@ -1015,8 +1060,14 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
if (data->flags & MMC_DATA_READ) {
mode |= SDHCI_TRNS_READ;
- if (host->ops->toggle_cdr)
- host->ops->toggle_cdr(host, true);
+ if (host->ops->toggle_cdr) {
+ if ((cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) ||
+ (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS400) ||
+ (cmd->opcode == MMC_SEND_TUNING_BLOCK))
+ host->ops->toggle_cdr(host, false);
+ else
+ host->ops->toggle_cdr(host, true);
+ }
}
if (host->ops->toggle_cdr && (data->flags & MMC_DATA_WRITE))
host->ops->toggle_cdr(host, false);
@@ -1024,6 +1075,9 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
mode |= SDHCI_TRNS_DMA;
sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
+ MMC_TRACE(host->mmc, "%s: 0x00=0x%08x 0x0C=0x%08x\n", __func__,
+ sdhci_readw(host, SDHCI_ARGUMENT2),
+ sdhci_readw(host, SDHCI_TRANSFER_MODE));
}
static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
@@ -1084,6 +1138,8 @@ static void sdhci_finish_data(struct sdhci_host *host)
host->data = NULL;
host->data_cmd = NULL;
+ MMC_TRACE(host->mmc, "%s: 0x24=0x%08x\n", __func__,
+ sdhci_readl(host, SDHCI_PRESENT_STATE));
if ((host->flags & (SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA)) ==
(SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA))
sdhci_adma_table_post(host, data);
@@ -1169,7 +1225,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
cmd->flags |= MMC_RSP_BUSY;
/* Wait max 10 ms */
- timeout = 10;
+ timeout = 10000;
mask = SDHCI_CMD_INHIBIT;
if (sdhci_data_line_cmd(cmd))
@@ -1190,7 +1246,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
return;
}
timeout--;
- mdelay(1);
+ udelay(1);
}
timeout = jiffies;
@@ -1241,6 +1297,12 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
if (cmd->data)
host->data_start_time = ktime_get();
+ trace_mmc_cmd_rw_start(cmd->opcode, cmd->arg, cmd->flags);
+ MMC_TRACE(host->mmc,
+ "%s: updated 0x8=0x%08x 0xC=0x%08x 0xE=0x%08x\n", __func__,
+ sdhci_readl(host, SDHCI_ARGUMENT),
+ sdhci_readw(host, SDHCI_TRANSFER_MODE),
+ sdhci_readw(host, SDHCI_COMMAND));
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
}
EXPORT_SYMBOL_GPL(sdhci_send_command);
@@ -1263,8 +1325,14 @@ static void sdhci_finish_command(struct sdhci_host *host)
sdhci_readb(host,
SDHCI_RESPONSE + (3-i)*4-1);
}
+ MMC_TRACE(host->mmc,
+ "%s: resp 0: 0x%08x resp 1: 0x%08x resp 2: 0x%08x resp 3: 0x%08x\n",
+ __func__, cmd->resp[0], cmd->resp[1],
+ cmd->resp[2], cmd->resp[3]);
} else {
cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE);
+ MMC_TRACE(host->mmc, "%s: resp 0: 0x%08x\n",
+ __func__, cmd->resp[0]);
}
}
@@ -1465,9 +1533,7 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
return;
}
timeout--;
- spin_unlock_irq(&host->lock);
usleep_range(900, 1100);
- spin_lock_irq(&host->lock);
}
clk |= SDHCI_CLOCK_CARD_EN;
@@ -1586,9 +1652,6 @@ static int sdhci_enable(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
- if (host->cpu_dma_latency_us)
- pm_qos_update_request(&host->pm_qos_req_dma,
- host->cpu_dma_latency_us);
if (host->ops->platform_bus_voting)
host->ops->platform_bus_voting(host, 1);
@@ -1599,29 +1662,26 @@ static int sdhci_disable(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
- if (host->cpu_dma_latency_us) {
- /*
- * In performance mode, release QoS vote after a timeout to
- * make sure back-to-back requests don't suffer from latencies
- * that are involved to wake CPU from low power modes in cases
- * where the CPU goes into low power mode as soon as QoS vote is
- * released.
- */
- if (host->power_policy == SDHCI_PERFORMANCE_MODE)
- pm_qos_update_request_timeout(&host->pm_qos_req_dma,
- host->cpu_dma_latency_us,
- host->pm_qos_timeout_us);
- else
- pm_qos_update_request(&host->pm_qos_req_dma,
- PM_QOS_DEFAULT_VALUE);
- }
-
if (host->ops->platform_bus_voting)
host->ops->platform_bus_voting(host, 0);
return 0;
}
+static void sdhci_notify_halt(struct mmc_host *mmc, bool halt)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ pr_debug("%s: halt notification was sent, halt=%d\n",
+ mmc_hostname(mmc), halt);
+ if (host->flags & SDHCI_USE_64_BIT_DMA) {
+ if (halt)
+ host->desc_sz = 16;
+ else
+ host->desc_sz = SDHCI_ADMA2_64_DESC_SZ;
+ }
+}
+
static inline void sdhci_update_power_policy(struct sdhci_host *host,
enum sdhci_power_policy policy)
{
@@ -1645,6 +1705,9 @@ static int sdhci_notify_load(struct mmc_host *mmc, enum mmc_load state)
break;
}
+ if (host->ops->notify_load)
+ err = host->ops->notify_load(host, state);
+
return err;
}
@@ -1698,7 +1761,9 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
mrq->cmd->error = -EIO;
if (mrq->data)
mrq->data->error = -EIO;
- tasklet_schedule(&host->finish_tasklet);
+ host->mrq = NULL;
+ sdhci_dumpregs(host);
+ mmc_request_done(host->mmc, mrq);
return;
}
@@ -1721,7 +1786,8 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
spin_lock_irqsave(&host->lock, flags);
- sdhci_led_activate(host);
+ if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_LED_CONTROL))
+ sdhci_led_activate(host);
/*
* Ensure we don't send the STOP for non-SET_BLOCK_COUNTED
@@ -1803,6 +1869,21 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
}
EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling);
+void sdhci_cfg_irq(struct sdhci_host *host, bool enable, bool sync)
+{
+ if (enable && !(host->flags & SDHCI_HOST_IRQ_STATUS)) {
+ enable_irq(host->irq);
+ host->flags |= SDHCI_HOST_IRQ_STATUS;
+ } else if (!enable && (host->flags & SDHCI_HOST_IRQ_STATUS)) {
+ if (sync)
+ disable_irq(host->irq);
+ else
+ disable_irq_nosync(host->irq);
+ host->flags &= ~SDHCI_HOST_IRQ_STATUS;
+ }
+}
+EXPORT_SYMBOL(sdhci_cfg_irq);
+
static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct sdhci_host *host = mmc_priv(mmc);
@@ -1822,6 +1903,34 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
sdhci_enable_preset_value(host, false);
+ spin_lock_irqsave(&host->lock, flags);
+ if (host->mmc && host->mmc->card &&
+ mmc_card_sdio(host->mmc->card))
+ sdhci_cfg_irq(host, false, false);
+
+ if (ios->clock &&
+ ((ios->clock != host->clock) || (ios->timing != host->timing))) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ host->ops->set_clock(host, ios->clock);
+ spin_lock_irqsave(&host->lock, flags);
+ host->clock = ios->clock;
+
+ if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK &&
+ host->clock) {
+ host->timeout_clk = host->mmc->actual_clock ?
+ host->mmc->actual_clock / 1000 :
+ host->clock / 1000;
+ host->mmc->max_busy_timeout =
+ host->ops->get_max_timeout_count ?
+ host->ops->get_max_timeout_count(host) :
+ 1 << 27;
+ host->mmc->max_busy_timeout /= host->timeout_clk;
+ }
+ }
+ if (ios->clock && host->sdio_irq_async_status)
+ sdhci_enable_sdio_irq_nolock(host, false);
+ spin_unlock_irqrestore(&host->lock, flags);
+
/*
* The controller clocks may be off during power-up and we may end up
* enabling card clock before giving power to the card. Hence, during
@@ -1842,23 +1951,12 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
spin_lock_irqsave(&host->lock, flags);
- if (!ios->clock || ios->clock != host->clock) {
+ if (!host->clock) {
+ if (host->mmc && host->mmc->card &&
+ mmc_card_sdio(host->mmc->card))
+ sdhci_cfg_irq(host, true, false);
spin_unlock_irqrestore(&host->lock, flags);
- host->ops->set_clock(host, ios->clock);
- spin_lock_irqsave(&host->lock, flags);
- host->clock = ios->clock;
-
- if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK &&
- host->clock) {
- host->timeout_clk = host->mmc->actual_clock ?
- host->mmc->actual_clock / 1000 :
- host->clock / 1000;
- host->mmc->max_busy_timeout =
- host->ops->get_max_timeout_count ?
- host->ops->get_max_timeout_count(host) :
- 1 << 27;
- host->mmc->max_busy_timeout /= host->timeout_clk;
- }
+ return;
}
spin_unlock_irqrestore(&host->lock, flags);
@@ -1938,8 +2036,11 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
/* Re-enable SD Clock */
- if (ios->clock)
+ if (ios->clock) {
+ spin_unlock_irqrestore(&host->lock, flags);
host->ops->set_clock(host, host->clock);
+ spin_lock_irqsave(&host->lock, flags);
+ }
}
/* Reset SD Clock Enable */
@@ -1966,8 +2067,11 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
/* Re-enable SD Clock */
- if (ios->clock)
+ if (ios->clock) {
+ spin_unlock_irqrestore(&host->lock, flags);
host->ops->set_clock(host, host->clock);
+ spin_lock_irqsave(&host->lock, flags);
+ }
} else
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
@@ -1990,7 +2094,13 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
sdhci_set_power(host, ios->power_mode, ios->vdd);
}
if (!ios->clock)
- sdhci_set_clock(host, ios->clock);
+ host->ops->set_clock(host, ios->clock);
+
+ spin_lock_irqsave(&host->lock, flags);
+ if (host->mmc && host->mmc->card &&
+ mmc_card_sdio(host->mmc->card))
+ sdhci_cfg_irq(host, true, false);
+ spin_unlock_irqrestore(&host->lock, flags);
mmiowb();
}
@@ -2075,16 +2185,28 @@ static void sdhci_hw_reset(struct mmc_host *mmc)
static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable)
{
- if (!(host->flags & SDHCI_DEVICE_DEAD)) {
- if (enable)
- host->ier |= SDHCI_INT_CARD_INT;
- else
- host->ier &= ~SDHCI_INT_CARD_INT;
+ u16 ctrl = 0;
- sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
- sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
- mmiowb();
+ if (host->flags & SDHCI_DEVICE_DEAD)
+ return;
+
+ if (mmc_card_and_host_support_async_int(host->mmc)) {
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ if (enable)
+ ctrl |= SDHCI_CTRL_ASYNC_INT_ENABLE;
+ else
+ ctrl &= ~SDHCI_CTRL_ASYNC_INT_ENABLE;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
}
+
+ if (enable)
+ host->ier |= SDHCI_INT_CARD_INT;
+ else
+ host->ier &= ~SDHCI_INT_CARD_INT;
+
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+ mmiowb();
}
static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
@@ -2229,6 +2351,17 @@ static int sdhci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
return 0;
}
+static int sdhci_enhanced_strobe(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ int err = 0;
+
+ if (host->ops->enhanced_strobe)
+ err = host->ops->enhanced_strobe(host);
+
+ return err;
+}
+
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct sdhci_host *host = mmc_priv(mmc);
@@ -2257,9 +2390,10 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
switch (host->timing) {
/* HS400 tuning is done in HS200 mode */
case MMC_TIMING_MMC_HS400:
- err = -EINVAL;
- goto out_unlock;
-
+ if (!(mmc->caps2 & MMC_CAP2_HS400_POST_TUNING)) {
+ err = -EINVAL;
+ goto out_unlock;
+ }
case MMC_TIMING_MMC_HS200:
/*
* Periodic re-tuning for HS400 is not expected to be needed, so
@@ -2493,6 +2627,9 @@ static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
DMA_TO_DEVICE : DMA_FROM_DEVICE);
data->host_cookie = COOKIE_UNMAPPED;
+
+ if (host->ops->pre_req)
+ host->ops->pre_req(host, mrq);
}
static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
@@ -2554,7 +2691,27 @@ static void sdhci_card_event(struct mmc_host *mmc)
spin_unlock_irqrestore(&host->lock, flags);
}
+static int sdhci_late_init(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ if (host->ops->init)
+ host->ops->init(host);
+
+ return 0;
+}
+
+static void sdhci_force_err_irq(struct mmc_host *mmc, u64 errmask)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u16 mask = errmask & 0xFFFF;
+
+ pr_err("%s: Force raise error mask:0x%04x\n", __func__, mask);
+ sdhci_writew(host, mask, SDHCI_SET_INT_ERROR);
+}
+
static const struct mmc_host_ops sdhci_ops = {
+ .init = sdhci_late_init,
.request = sdhci_request,
.post_req = sdhci_post_req,
.pre_req = sdhci_pre_req,
@@ -2566,12 +2723,15 @@ static const struct mmc_host_ops sdhci_ops = {
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
.prepare_hs400_tuning = sdhci_prepare_hs400_tuning,
.execute_tuning = sdhci_execute_tuning,
+ .enhanced_strobe = sdhci_enhanced_strobe,
.select_drive_strength = sdhci_select_drive_strength,
.card_event = sdhci_card_event,
.card_busy = sdhci_card_busy,
.enable = sdhci_enable,
.disable = sdhci_disable,
.notify_load = sdhci_notify_load,
+ .notify_halt = sdhci_notify_halt,
+ .force_err_irq = sdhci_force_err_irq,
};
/*****************************************************************************\
@@ -2650,7 +2810,8 @@ static bool sdhci_request_done(struct sdhci_host *host)
}
if (!sdhci_has_requests(host))
- sdhci_led_deactivate(host);
+ if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_LED_CONTROL))
+ sdhci_led_deactivate(host);
host->mrqs_done[i] = NULL;
host->auto_cmd_err_sts = 0;
@@ -2752,6 +2913,9 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
return;
}
+ trace_mmc_cmd_rw_end(host->cmd->opcode, intmask,
+ sdhci_readl(host, SDHCI_RESPONSE));
+
if (intmask & (SDHCI_INT_TIMEOUT | SDHCI_INT_CRC |
SDHCI_INT_END_BIT | SDHCI_INT_INDEX |
SDHCI_INT_AUTO_CMD_ERR)) {
@@ -2762,7 +2926,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
if (intmask & SDHCI_INT_AUTO_CMD_ERR) {
auto_cmd_status = host->auto_cmd_err_sts;
- pr_err("%s: %s: AUTO CMD err sts 0x%08x\n",
+ pr_err_ratelimited("%s: %s: AUTO CMD err sts 0x%08x\n",
mmc_hostname(host->mmc), __func__, auto_cmd_status);
if (auto_cmd_status & (SDHCI_AUTO_CMD12_NOT_EXEC |
SDHCI_AUTO_CMD_INDEX_ERR |
@@ -2783,10 +2947,13 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
* If the card did not receive the command or returned an
* error which prevented it sending data, the data phase
* will time out.
+ *
+ * Even in case of cmd INDEX OR ENDBIT error we
+ * handle it the same way.
*/
if (host->cmd->data &&
- (intmask & (SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)) ==
- SDHCI_INT_CRC) {
+ (((intmask & (SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)) ==
+ SDHCI_INT_CRC) || (host->cmd->error == -EILSEQ))) {
host->cmd = NULL;
return;
}
@@ -2837,11 +3004,14 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
u32 command;
bool pr_msg = false;
+ command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
+ trace_mmc_data_rw_end(command, intmask);
+
/* CMD19 generates _only_ Buffer Read Ready interrupt */
if (intmask & SDHCI_INT_DATA_AVAIL) {
- command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
- if (command == MMC_SEND_TUNING_BLOCK ||
- command == MMC_SEND_TUNING_BLOCK_HS200) {
+ if (!(host->quirks2 & SDHCI_QUIRK2_NON_STANDARD_TUNING) &&
+ (command == MMC_SEND_TUNING_BLOCK ||
+ command == MMC_SEND_TUNING_BLOCK_HS200)) {
host->tuning_done = 1;
wake_up(&host->buf_ready_int);
return;
@@ -2901,8 +3071,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
else if (intmask & SDHCI_INT_DATA_END_BIT)
host->data->error = -EILSEQ;
else if ((intmask & SDHCI_INT_DATA_CRC) &&
- SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))
- != MMC_BUS_TEST_R)
+ (command != MMC_BUS_TEST_R))
host->data->error = -EILSEQ;
else if (intmask & SDHCI_INT_ADMA_ERROR) {
pr_err("%s: ADMA error\n", mmc_hostname(host->mmc));
@@ -2912,7 +3081,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
host->ops->adma_workaround(host, intmask);
}
if (host->data->error) {
- if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT)) {
+ if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT
+ | SDHCI_INT_DATA_END_BIT)) {
command = SDHCI_GET_CMD(sdhci_readw(host,
SDHCI_COMMAND));
if ((command != MMC_SEND_TUNING_BLOCK_HS200) &&
@@ -2921,7 +3091,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
} else {
pr_msg = true;
}
- if (pr_msg) {
+ if (pr_msg && __ratelimit(&host->dbg_dump_rs)) {
pr_err("%s: data txfr (0x%08x) error: %d after %lld ms\n",
mmc_hostname(host->mmc), intmask,
host->data->error, ktime_to_ms(ktime_sub(
@@ -2975,6 +3145,58 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
}
}
+#ifdef CONFIG_MMC_CQ_HCI
+static int sdhci_get_cmd_err(u32 intmask)
+{
+ if (intmask & SDHCI_INT_TIMEOUT)
+ return -ETIMEDOUT;
+ else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT |
+ SDHCI_INT_INDEX))
+ return -EILSEQ;
+ return 0;
+}
+
+static int sdhci_get_data_err(u32 intmask)
+{
+ if (intmask & SDHCI_INT_DATA_TIMEOUT)
+ return -ETIMEDOUT;
+ else if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC))
+ return -EILSEQ;
+ else if (intmask & SDHCI_INT_ADMA_ERROR)
+ return -EIO;
+ return 0;
+}
+
+static irqreturn_t sdhci_cmdq_irq(struct sdhci_host *host, u32 intmask)
+{
+ int err = 0;
+ u32 mask = 0;
+ irqreturn_t ret;
+
+ if (intmask & SDHCI_INT_CMD_MASK)
+ err = sdhci_get_cmd_err(intmask);
+ else if (intmask & SDHCI_INT_DATA_MASK)
+ err = sdhci_get_data_err(intmask);
+
+ ret = cmdq_irq(host->mmc, err);
+ if (err) {
+ /* Clear the error interrupts */
+ mask = intmask & SDHCI_INT_ERROR_MASK;
+ sdhci_writel(host, mask, SDHCI_INT_STATUS);
+ }
+ return ret;
+
+}
+
+#else
+static irqreturn_t sdhci_cmdq_irq(struct sdhci_host *host, u32 intmask)
+{
+ pr_err("%s: Received cmdq-irq when disabled !!!!\n",
+ mmc_hostname(host->mmc));
+ return IRQ_NONE;
+}
+#endif
+
static irqreturn_t sdhci_irq(int irq, void *dev_id)
{
irqreturn_t result = IRQ_NONE;
@@ -2989,6 +3211,31 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
return IRQ_NONE;
}
+ if (!host->clock && host->mmc->card &&
+ mmc_card_sdio(host->mmc->card)) {
+ if (!mmc_card_and_host_support_async_int(host->mmc)) {
+ spin_unlock(&host->lock);
+ return IRQ_NONE;
+ }
+ /*
+ * async card interrupt is level sensitive and received
+ * when clocks are off.
+ * If sdio card has asserted async interrupt, in that
+ * case we need to disable host->irq.
+ * Later we can disable card interrupt and re-enable
+ * host->irq.
+ */
+
+ pr_debug("%s: %s: sdio_async intr. received\n",
+ mmc_hostname(host->mmc), __func__);
+ sdhci_cfg_irq(host, false, false);
+ host->sdio_irq_async_status = true;
+ host->thread_isr |= SDHCI_INT_CARD_INT;
+ result = IRQ_WAKE_THREAD;
+ spin_unlock(&host->lock);
+ return result;
+ }
+
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
if (!intmask || intmask == 0xffffffff) {
result = IRQ_NONE;
@@ -2996,6 +3243,19 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
}
do {
+ if (host->mmc->card && mmc_card_cmdq(host->mmc->card) &&
+ !mmc_host_halt(host->mmc) && !mmc_host_cq_disable(host->mmc)) {
+ pr_debug("*** %s: cmdq intr: 0x%08x\n",
+ mmc_hostname(host->mmc),
+ intmask);
+ result = sdhci_cmdq_irq(host, intmask);
+ if (result == IRQ_HANDLED)
+ goto out;
+ }
+
+ MMC_TRACE(host->mmc,
+ "%s: intmask: 0x%x\n", __func__, intmask);
+
if (intmask & SDHCI_INT_AUTO_CMD_ERR)
host->auto_cmd_err_sts = sdhci_readw(host,
SDHCI_AUTO_CMD_ERR);
@@ -3114,8 +3374,11 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
sdio_run_irqs(host->mmc);
spin_lock_irqsave(&host->lock, flags);
- if (host->flags & SDHCI_SDIO_IRQ_ENABLED)
+ if (host->flags & SDHCI_SDIO_IRQ_ENABLED) {
+ if (host->sdio_irq_async_status)
+ host->sdio_irq_async_status = false;
sdhci_enable_sdio_irq_nolock(host, true);
+ }
spin_unlock_irqrestore(&host->lock, flags);
}
@@ -3329,12 +3592,183 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev,
host->flags = SDHCI_SIGNALING_330;
spin_lock_init(&host->lock);
+ ratelimit_state_init(&host->dbg_dump_rs, SDHCI_DBG_DUMP_RS_INTERVAL,
+ SDHCI_DBG_DUMP_RS_BURST);
return host;
}
EXPORT_SYMBOL_GPL(sdhci_alloc_host);
+#ifdef CONFIG_MMC_CQ_HCI
+static void sdhci_cmdq_set_transfer_params(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u8 ctrl;
+
+ if (host->version >= SDHCI_SPEC_200) {
+ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+ ctrl &= ~SDHCI_CTRL_DMA_MASK;
+ if (host->flags & SDHCI_USE_64_BIT_DMA)
+ ctrl |= SDHCI_CTRL_ADMA64;
+ else
+ ctrl |= SDHCI_CTRL_ADMA32;
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+ }
+ if (host->ops->toggle_cdr)
+ host->ops->toggle_cdr(host, false);
+}
+
+static void sdhci_cmdq_clear_set_irqs(struct mmc_host *mmc, bool clear)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u32 ier = 0;
+
+ ier &= ~SDHCI_INT_ALL_MASK;
+
+ if (clear) {
+ ier = SDHCI_INT_CMDQ_EN | SDHCI_INT_ERROR_MASK;
+ sdhci_writel(host, ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
+ } else {
+ ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
+ SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT |
+ SDHCI_INT_INDEX | SDHCI_INT_END_BIT |
+ SDHCI_INT_CRC | SDHCI_INT_TIMEOUT |
+ SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE |
+ SDHCI_INT_AUTO_CMD_ERR;
+ sdhci_writel(host, ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
+ }
+}
+
+static void sdhci_cmdq_set_data_timeout(struct mmc_host *mmc, u32 val)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ sdhci_writeb(host, val, SDHCI_TIMEOUT_CONTROL);
+}
+
+static void sdhci_cmdq_dump_vendor_regs(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ sdhci_dumpregs(host);
+}
+
+static int sdhci_cmdq_init(struct sdhci_host *host, struct mmc_host *mmc,
+ bool dma64)
+{
+ return cmdq_init(host->cq_host, mmc, dma64);
+}
+
+static void sdhci_cmdq_set_block_size(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ sdhci_set_blk_size_reg(host, 512, 0);
+}
+
+static void sdhci_enhanced_strobe_mask(struct mmc_host *mmc, bool set)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ if (host->ops->enhanced_strobe_mask)
+ host->ops->enhanced_strobe_mask(host, set);
+}
+
+static void sdhci_cmdq_clear_set_dumpregs(struct mmc_host *mmc, bool set)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ if (host->ops->clear_set_dumpregs)
+ host->ops->clear_set_dumpregs(host, set);
+}
+
+static void sdhci_cmdq_post_cqe_halt(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ sdhci_writel(host, sdhci_readl(host, SDHCI_INT_ENABLE) |
+ SDHCI_INT_RESPONSE, SDHCI_INT_ENABLE);
+ sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS);
+}
+#else
+static void sdhci_cmdq_set_transfer_params(struct mmc_host *mmc)
+{
+
+}
+static void sdhci_cmdq_clear_set_irqs(struct mmc_host *mmc, bool clear)
+{
+
+}
+
+static void sdhci_cmdq_set_data_timeout(struct mmc_host *mmc, u32 val)
+{
+
+}
+
+static void sdhci_cmdq_dump_vendor_regs(struct mmc_host *mmc)
+{
+
+}
+
+static int sdhci_cmdq_init(struct sdhci_host *host, struct mmc_host *mmc,
+ bool dma64)
+{
+ return -ENOSYS;
+}
+
+static void sdhci_cmdq_set_block_size(struct mmc_host *mmc)
+{
+
+}
+
+static void sdhci_enhanced_strobe_mask(struct mmc_host *mmc, bool set)
+{
+
+}
+
+static void sdhci_cmdq_clear_set_dumpregs(struct mmc_host *mmc, bool set)
+{
+
+}
+
+static void sdhci_cmdq_post_cqe_halt(struct mmc_host *mmc)
+{
+}
+#endif
+
+static const struct cmdq_host_ops sdhci_cmdq_ops = {
+ .clear_set_irqs = sdhci_cmdq_clear_set_irqs,
+ .set_data_timeout = sdhci_cmdq_set_data_timeout,
+ .dump_vendor_regs = sdhci_cmdq_dump_vendor_regs,
+ .set_block_size = sdhci_cmdq_set_block_size,
+ .clear_set_dumpregs = sdhci_cmdq_clear_set_dumpregs,
+ .enhanced_strobe_mask = sdhci_enhanced_strobe_mask,
+ .post_cqe_halt = sdhci_cmdq_post_cqe_halt,
+ .set_transfer_params = sdhci_cmdq_set_transfer_params,
+};
+
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+static int sdhci_is_adma2_64bit(struct sdhci_host *host)
+{
+ u32 caps;
+
+ caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
+ sdhci_readl(host, SDHCI_CAPABILITIES);
+
+ if (caps & SDHCI_CAN_64BIT)
+ return 1;
+ return 0;
+}
+#else
+static int sdhci_is_adma2_64bit(struct sdhci_host *host)
+{
+ return 0;
+}
+#endif
+
static int sdhci_set_dma_mask(struct sdhci_host *host)
{
struct mmc_host *mmc = host->mmc;
@@ -3400,6 +3834,7 @@ EXPORT_SYMBOL_GPL(__sdhci_read_caps);
int sdhci_setup_host(struct sdhci_host *host)
{
struct mmc_host *mmc;
+ u32 caps[2] = {0, 0};
u32 max_current_caps;
unsigned int ocr_avail;
unsigned int override_timeout_clk;
@@ -3424,6 +3859,8 @@ int sdhci_setup_host(struct sdhci_host *host)
sdhci_read_caps(host);
+ caps[0] = host->caps;
+
override_timeout_clk = host->timeout_clk;
if (host->version > SDHCI_SPEC_300) {
@@ -3461,7 +3898,7 @@ int sdhci_setup_host(struct sdhci_host *host)
* SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to
* implement.
*/
- if (host->caps & SDHCI_CAN_64BIT)
+ if (sdhci_is_adma2_64bit(host))
host->flags |= SDHCI_USE_64_BIT_DMA;
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
@@ -3619,6 +4056,9 @@ int sdhci_setup_host(struct sdhci_host *host)
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
+ if (caps[0] & SDHCI_CAN_ASYNC_INT)
+ mmc->caps2 |= MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE;
+
if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
host->flags |= SDHCI_AUTO_CMD12;
@@ -3651,7 +4091,8 @@ int sdhci_setup_host(struct sdhci_host *host)
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
mmc_card_is_removable(mmc) &&
- mmc_gpio_get_cd(host->mmc) < 0)
+ mmc_gpio_get_cd(host->mmc) < 0 &&
+ !(mmc->caps2 & MMC_CAP2_NONHOTPLUG))
mmc->caps |= MMC_CAP_NEEDS_POLL;
/* If vqmmc regulator and no 1.8V signalling, then there's no UHS */
@@ -3742,10 +4183,15 @@ int sdhci_setup_host(struct sdhci_host *host)
* value.
*/
max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
- if (!max_current_caps && !IS_ERR(mmc->supply.vmmc)) {
- int curr = regulator_get_current_limit(mmc->supply.vmmc);
- if (curr > 0) {
+ if (!max_current_caps) {
+ u32 curr = 0;
+ if (!IS_ERR(mmc->supply.vmmc))
+ curr = regulator_get_current_limit(mmc->supply.vmmc);
+ else if (host->ops->get_current_limit)
+ curr = host->ops->get_current_limit(host);
+
+ if (curr > 0) {
/* convert to SDHCI_MAX_CURRENT format */
curr = curr/1000; /* convert to mA */
curr = curr/SDHCI_MAX_CURRENT_MULTIPLIER;
@@ -3909,6 +4355,8 @@ int __sdhci_add_host(struct sdhci_host *host)
init_waitqueue_head(&host->buf_ready_int);
+ host->flags |= SDHCI_HOST_IRQ_STATUS;
+
sdhci_init(host, 0);
ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq,
@@ -3923,54 +4371,54 @@ int __sdhci_add_host(struct sdhci_host *host)
sdhci_dumpregs(host);
#endif
- ret = sdhci_led_register(host);
- if (ret) {
- pr_err("%s: Failed to register LED device: %d\n",
- mmc_hostname(mmc), ret);
- goto unirq;
+ if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_LED_CONTROL)) {
+ ret = sdhci_led_register(host);
+ if (ret) {
+ pr_err("%s: Failed to register LED device: %d\n",
+ mmc_hostname(mmc), ret);
+ goto unirq;
+ }
}
mmiowb();
- if (host->cpu_dma_latency_us) {
- host->pm_qos_timeout_us = 10000; /* default value */
- pm_qos_add_request(&host->pm_qos_req_dma,
- PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
-
- host->pm_qos_tout.show = show_sdhci_pm_qos_tout;
- host->pm_qos_tout.store = store_sdhci_pm_qos_tout;
- sysfs_attr_init(&host->pm_qos_tout.attr);
- host->pm_qos_tout.attr.name = "pm_qos_unvote_delay";
- host->pm_qos_tout.attr.mode = S_IRUGO | S_IWUSR;
- ret = device_create_file(mmc_dev(mmc), &host->pm_qos_tout);
- if (ret)
- pr_err("%s: cannot create pm_qos_unvote_delay %d\n",
- mmc_hostname(mmc), ret);
-
- }
-
- ret = mmc_add_host(mmc);
- if (ret)
- goto unled;
-
if (host->quirks2 & SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR) {
host->ier = (host->ier & ~SDHCI_INT_DATA_END_BIT);
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
}
- pr_info("%s: SDHCI controller on %s [%s] using %s\n",
- mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
+ if (mmc->caps2 & MMC_CAP2_CMD_QUEUE) {
+ bool dma64 = (host->flags & SDHCI_USE_64_BIT_DMA) ?
+ true : false;
+ ret = sdhci_cmdq_init(host, mmc, dma64);
+ if (ret)
+ pr_err("%s: CMDQ init: failed (%d)\n",
+ mmc_hostname(host->mmc), ret);
+ else
+ host->cq_host->ops = &sdhci_cmdq_ops;
+ }
+
+ pr_info("%s: SDHCI controller on %s [%s] using %s in %s mode\n",
+ mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
(host->flags & SDHCI_USE_ADMA) ?
- (host->flags & SDHCI_USE_64_BIT_DMA) ? "ADMA 64-bit" : "ADMA" :
- (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO");
+ ((host->flags & SDHCI_USE_64_BIT_DMA) ?
+ "64-bit ADMA" : "32-bit ADMA") :
+ ((host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO"),
+ ((mmc->caps2 & MMC_CAP2_CMD_QUEUE) && !ret) ?
+ "CMDQ" : "legacy");
sdhci_enable_card_detection(host);
+ ret = mmc_add_host(mmc);
+ if (ret)
+ goto unled;
+
return 0;
unled:
- sdhci_led_unregister(host);
+ if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_LED_CONTROL))
+ sdhci_led_unregister(host);
unirq:
sdhci_do_reset(host, SDHCI_RESET_ALL);
sdhci_writel(host, 0, SDHCI_INT_ENABLE);
@@ -4026,11 +4474,10 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
sdhci_disable_card_detection(host);
- if (host->cpu_dma_latency_us)
- pm_qos_remove_request(&host->pm_qos_req_dma);
mmc_remove_host(host->mmc);
- sdhci_led_unregister(host);
+ if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_LED_CONTROL))
+ sdhci_led_unregister(host);
if (!dead)
sdhci_do_reset(host, SDHCI_RESET_ALL);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 5e809d4..d9e656a 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -17,8 +17,7 @@
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/io.h>
-#include <linux/pm_qos.h>
-
+#include <linux/ratelimit.h>
#include <linux/mmc/host.h>
/*
@@ -157,6 +156,8 @@
SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR | \
SDHCI_INT_BLK_GAP)
+
+#define SDHCI_INT_CMDQ_EN (0x1 << 14)
#define SDHCI_INT_ALL_MASK ((unsigned int)-1)
#define SDHCI_AUTO_CMD_ERR 0x3C
@@ -183,6 +184,7 @@
#define SDHCI_CTRL_DRV_TYPE_D 0x0030
#define SDHCI_CTRL_EXEC_TUNING 0x0040
#define SDHCI_CTRL_TUNED_CLK 0x0080
+#define SDHCI_CTRL_ASYNC_INT_ENABLE 0x4000
#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
#define SDHCI_CAPABILITIES 0x40
@@ -204,6 +206,7 @@
#define SDHCI_CAN_VDD_300 0x02000000
#define SDHCI_CAN_VDD_180 0x04000000
#define SDHCI_CAN_64BIT 0x10000000
+#define SDHCI_CAN_ASYNC_INT 0x20000000
#define SDHCI_SUPPORT_SDR50 0x00000001
#define SDHCI_SUPPORT_SDR104 0x00000002
@@ -340,6 +343,7 @@ enum sdhci_cookie {
enum sdhci_power_policy {
SDHCI_PERFORMANCE_MODE,
SDHCI_POWER_SAVE_MODE,
+ SDHCI_POWER_POLICY_NUM /* Always keep this one last */
};
struct sdhci_host {
@@ -451,6 +455,11 @@ struct sdhci_host {
*/
#define SDHCI_QUIRK2_SLOW_INT_CLR (1<<18)
+/*
+ * If the base clock can be scalable, then there should be no further
+ * clock dividing as the input clock itself will be scaled down to
+ * required frequency.
+ */
#define SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK (1<<19)
/*
@@ -459,20 +468,20 @@ struct sdhci_host {
* could be lager than the maximum timeout value that controller
* can handle.
*/
-#define SDHCI_QUIRK2_IGNORE_DATATOUT_FOR_R1BCMD (1<<21)
+#define SDHCI_QUIRK2_IGNORE_DATATOUT_FOR_R1BCMD (1<<20)
/*
* The preset value registers are not properly initialized by
* some hardware and hence preset value must not be enabled for
* such controllers.
*/
-#define SDHCI_QUIRK2_BROKEN_PRESET_VALUE (1<<22)
+#define SDHCI_QUIRK2_BROKEN_PRESET_VALUE (1<<21)
/*
* Some controllers define the usage of 0xF in data timeout counter
* register (0x2E) which is actually a reserved bit as per
* specification.
*/
-#define SDHCI_QUIRK2_USE_RESERVED_MAX_TIMEOUT (1<<23)
+#define SDHCI_QUIRK2_USE_RESERVED_MAX_TIMEOUT (1<<22)
/*
* This is applicable for controllers that advertize timeout clock
* value in capabilities register (bit 5-0) as just 50MHz whereas the
@@ -492,6 +501,26 @@ struct sdhci_host {
* 1-bit mode of SDIO.
*/
#define SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR (1<<24)
+
+/* Use reset workaround in case sdhci reset timeouts */
+#define SDHCI_QUIRK2_USE_RESET_WORKAROUND (1<<26)
+
+/* Some controllers doesn't have have any LED control */
+#define SDHCI_QUIRK2_BROKEN_LED_CONTROL (1<<27)
+
+/*
+ * Some controllers doesn't follow the tuning procedure as defined in spec.
+ * The tuning data has to be compared from SW driver to validate the correct
+ * phase.
+ */
+#define SDHCI_QUIRK2_NON_STANDARD_TUNING (1 << 28)
+/*
+ * Some controllers may use PIO mode to workaround HW issues in ADMA for
+ * eMMC tuning commands.
+ */
+#define SDHCI_QUIRK2_USE_PIO_FOR_EMMC_TUNING (1 << 23)
+
+
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
@@ -501,6 +530,7 @@ struct sdhci_host {
struct mmc_host *mmc; /* MMC structure */
struct mmc_host_ops mmc_host_ops; /* MMC host ops */
u64 dma_mask; /* custom DMA mask */
+ u64 coherent_dma_mask;
#if IS_ENABLED(CONFIG_LEDS_CLASS)
struct led_classdev led; /* LED control */
@@ -524,6 +554,7 @@ struct sdhci_host {
#define SDHCI_SIGNALING_330 (1<<14) /* Host is capable of 3.3V signaling */
#define SDHCI_SIGNALING_180 (1<<15) /* Host is capable of 1.8V signaling */
#define SDHCI_SIGNALING_120 (1<<16) /* Host is capable of 1.2V signaling */
+#define SDHCI_HOST_IRQ_STATUS (1<<17) /* host->irq status */
unsigned int version; /* SDHCI spec. version */
@@ -557,10 +588,6 @@ struct sdhci_host {
size_t adma_table_sz; /* ADMA descriptor table size */
size_t align_buffer_sz; /* Bounce buffer size */
- unsigned int adma_desc_sz; /* ADMA descriptor table size */
- unsigned int align_buf_sz; /* Bounce buffer size */
- unsigned int adma_max_desc; /* Max ADMA descriptos (max sg segments) */
-
dma_addr_t adma_addr; /* Mapped ADMA descr. table */
dma_addr_t align_addr; /* Mapped bounce buffer */
@@ -596,16 +623,18 @@ struct sdhci_host {
#define SDHCI_TUNING_MODE_2 1
#define SDHCI_TUNING_MODE_3 2
- unsigned int cpu_dma_latency_us;
- struct pm_qos_request pm_qos_req_dma;
ktime_t data_start_time;
- unsigned int pm_qos_timeout_us; /* timeout for PM QoS request */
- struct device_attribute pm_qos_tout;
-
enum sdhci_power_policy power_policy;
+ bool sdio_irq_async_status;
+
u32 auto_cmd_err_sts;
+ struct ratelimit_state dbg_dump_rs;
+ struct cmdq_host *cq_host;
+ int reset_wa_applied; /* reset workaround status */
+ ktime_t reset_wa_t; /* time when the reset workaround is applied */
+ int reset_wa_cnt; /* total number of times workaround is used */
unsigned long private[0] ____cacheline_aligned;
};
@@ -646,6 +675,7 @@ struct sdhci_ops {
#define REQ_IO_LOW (1 << 2)
#define REQ_IO_HIGH (1 << 3)
void (*card_event)(struct sdhci_host *host);
+ int (*enhanced_strobe)(struct sdhci_host *host);
void (*platform_bus_voting)(struct sdhci_host *host, u32 enable);
void (*toggle_cdr)(struct sdhci_host *host, bool enable);
void (*check_power_status)(struct sdhci_host *host, u32 req_type);
@@ -653,12 +683,20 @@ struct sdhci_ops {
bool enable,
u32 type);
int (*enable_controller_clock)(struct sdhci_host *host);
+ void (*clear_set_dumpregs)(struct sdhci_host *host, bool set);
+ void (*enhanced_strobe_mask)(struct sdhci_host *host, bool set);
void (*dump_vendor_regs)(struct sdhci_host *host);
void (*voltage_switch)(struct sdhci_host *host);
int (*select_drive_strength)(struct sdhci_host *host,
struct mmc_card *card,
unsigned int max_dtr, int host_drv,
int card_drv, int *drv_type);
+ int (*notify_load)(struct sdhci_host *host, enum mmc_load state);
+ void (*reset_workaround)(struct sdhci_host *host, u32 enable);
+ void (*init)(struct sdhci_host *host);
+ void (*pre_req)(struct sdhci_host *host, struct mmc_request *req);
+ void (*post_req)(struct sdhci_host *host, struct mmc_request *req);
+ unsigned int (*get_current_limit)(struct sdhci_host *host);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
@@ -793,4 +831,5 @@ extern int sdhci_runtime_suspend_host(struct sdhci_host *host);
extern int sdhci_runtime_resume_host(struct sdhci_host *host);
#endif
+void sdhci_cfg_irq(struct sdhci_host *host, bool enable, bool sync);
#endif /* __SDHCI_HW_H */
diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c
index 3779475..283ff7e 100644
--- a/drivers/mtd/bcm47xxpart.c
+++ b/drivers/mtd/bcm47xxpart.c
@@ -229,12 +229,10 @@ static int bcm47xxpart_parse(struct mtd_info *master,
last_trx_part = curr_part - 1;
- /*
- * We have whole TRX scanned, skip to the next part. Use
- * roundown (not roundup), as the loop will increase
- * offset in next step.
- */
- offset = rounddown(offset + trx->length, blocksize);
+ /* Jump to the end of TRX */
+ offset = roundup(offset + trx->length, blocksize);
+ /* Next loop iteration will increase the offset */
+ offset -= blocksize;
continue;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/cq.c b/drivers/net/ethernet/mellanox/mlx4/cq.c
index a849da9..6b86353 100644
--- a/drivers/net/ethernet/mellanox/mlx4/cq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/cq.c
@@ -101,13 +101,19 @@ void mlx4_cq_completion(struct mlx4_dev *dev, u32 cqn)
{
struct mlx4_cq *cq;
+ rcu_read_lock();
cq = radix_tree_lookup(&mlx4_priv(dev)->cq_table.tree,
cqn & (dev->caps.num_cqs - 1));
+ rcu_read_unlock();
+
if (!cq) {
mlx4_dbg(dev, "Completion event for bogus CQ %08x\n", cqn);
return;
}
+ /* Acessing the CQ outside of rcu_read_lock is safe, because
+ * the CQ is freed only after interrupt handling is completed.
+ */
++cq->arm_sn;
cq->comp(cq);
@@ -118,23 +124,19 @@ void mlx4_cq_event(struct mlx4_dev *dev, u32 cqn, int event_type)
struct mlx4_cq_table *cq_table = &mlx4_priv(dev)->cq_table;
struct mlx4_cq *cq;
- spin_lock(&cq_table->lock);
-
+ rcu_read_lock();
cq = radix_tree_lookup(&cq_table->tree, cqn & (dev->caps.num_cqs - 1));
- if (cq)
- atomic_inc(&cq->refcount);
-
- spin_unlock(&cq_table->lock);
+ rcu_read_unlock();
if (!cq) {
- mlx4_warn(dev, "Async event for bogus CQ %08x\n", cqn);
+ mlx4_dbg(dev, "Async event for bogus CQ %08x\n", cqn);
return;
}
+ /* Acessing the CQ outside of rcu_read_lock is safe, because
+ * the CQ is freed only after interrupt handling is completed.
+ */
cq->event(cq, event_type);
-
- if (atomic_dec_and_test(&cq->refcount))
- complete(&cq->free);
}
static int mlx4_SW2HW_CQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox,
@@ -301,9 +303,9 @@ int mlx4_cq_alloc(struct mlx4_dev *dev, int nent,
if (err)
return err;
- spin_lock_irq(&cq_table->lock);
+ spin_lock(&cq_table->lock);
err = radix_tree_insert(&cq_table->tree, cq->cqn, cq);
- spin_unlock_irq(&cq_table->lock);
+ spin_unlock(&cq_table->lock);
if (err)
goto err_icm;
@@ -349,9 +351,9 @@ int mlx4_cq_alloc(struct mlx4_dev *dev, int nent,
return 0;
err_radix:
- spin_lock_irq(&cq_table->lock);
+ spin_lock(&cq_table->lock);
radix_tree_delete(&cq_table->tree, cq->cqn);
- spin_unlock_irq(&cq_table->lock);
+ spin_unlock(&cq_table->lock);
err_icm:
mlx4_cq_free_icm(dev, cq->cqn);
@@ -370,15 +372,15 @@ void mlx4_cq_free(struct mlx4_dev *dev, struct mlx4_cq *cq)
if (err)
mlx4_warn(dev, "HW2SW_CQ failed (%d) for CQN %06x\n", err, cq->cqn);
+ spin_lock(&cq_table->lock);
+ radix_tree_delete(&cq_table->tree, cq->cqn);
+ spin_unlock(&cq_table->lock);
+
synchronize_irq(priv->eq_table.eq[MLX4_CQ_TO_EQ_VECTOR(cq->vector)].irq);
if (priv->eq_table.eq[MLX4_CQ_TO_EQ_VECTOR(cq->vector)].irq !=
priv->eq_table.eq[MLX4_EQ_ASYNC].irq)
synchronize_irq(priv->eq_table.eq[MLX4_EQ_ASYNC].irq);
- spin_lock_irq(&cq_table->lock);
- radix_tree_delete(&cq_table->tree, cq->cqn);
- spin_unlock_irq(&cq_table->lock);
-
if (atomic_dec_and_test(&cq->refcount))
complete(&cq->free);
wait_for_completion(&cq->free);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index 4d3ddc2..5d48458 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -444,8 +444,14 @@ int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv)
ring->cqn = priv->rx_cq[ring_ind]->mcq.cqn;
ring->stride = stride;
- if (ring->stride <= TXBB_SIZE)
+ if (ring->stride <= TXBB_SIZE) {
+ /* Stamp first unused send wqe */
+ __be32 *ptr = (__be32 *)ring->buf;
+ __be32 stamp = cpu_to_be32(1 << STAMP_SHIFT);
+ *ptr = stamp;
+ /* Move pointer to start of rx section */
ring->buf += TXBB_SIZE;
+ }
ring->log_stride = ffs(ring->stride) - 1;
ring->buf_size = ring->size * ring->stride;
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index 75d07fa..b2ca8a6 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -4020,49 +4020,51 @@ int mlx4_restart_one(struct pci_dev *pdev)
return err;
}
+#define MLX_SP(id) { PCI_VDEVICE(MELLANOX, id), MLX4_PCI_DEV_FORCE_SENSE_PORT }
+#define MLX_VF(id) { PCI_VDEVICE(MELLANOX, id), MLX4_PCI_DEV_IS_VF }
+#define MLX_GN(id) { PCI_VDEVICE(MELLANOX, id), 0 }
+
static const struct pci_device_id mlx4_pci_table[] = {
- /* MT25408 "Hermon" SDR */
- { PCI_VDEVICE(MELLANOX, 0x6340), MLX4_PCI_DEV_FORCE_SENSE_PORT },
- /* MT25408 "Hermon" DDR */
- { PCI_VDEVICE(MELLANOX, 0x634a), MLX4_PCI_DEV_FORCE_SENSE_PORT },
- /* MT25408 "Hermon" QDR */
- { PCI_VDEVICE(MELLANOX, 0x6354), MLX4_PCI_DEV_FORCE_SENSE_PORT },
- /* MT25408 "Hermon" DDR PCIe gen2 */
- { PCI_VDEVICE(MELLANOX, 0x6732), MLX4_PCI_DEV_FORCE_SENSE_PORT },
- /* MT25408 "Hermon" QDR PCIe gen2 */
- { PCI_VDEVICE(MELLANOX, 0x673c), MLX4_PCI_DEV_FORCE_SENSE_PORT },
- /* MT25408 "Hermon" EN 10GigE */
- { PCI_VDEVICE(MELLANOX, 0x6368), MLX4_PCI_DEV_FORCE_SENSE_PORT },
- /* MT25408 "Hermon" EN 10GigE PCIe gen2 */
- { PCI_VDEVICE(MELLANOX, 0x6750), MLX4_PCI_DEV_FORCE_SENSE_PORT },
- /* MT25458 ConnectX EN 10GBASE-T 10GigE */
- { PCI_VDEVICE(MELLANOX, 0x6372), MLX4_PCI_DEV_FORCE_SENSE_PORT },
- /* MT25458 ConnectX EN 10GBASE-T+Gen2 10GigE */
- { PCI_VDEVICE(MELLANOX, 0x675a), MLX4_PCI_DEV_FORCE_SENSE_PORT },
- /* MT26468 ConnectX EN 10GigE PCIe gen2*/
- { PCI_VDEVICE(MELLANOX, 0x6764), MLX4_PCI_DEV_FORCE_SENSE_PORT },
- /* MT26438 ConnectX EN 40GigE PCIe gen2 5GT/s */
- { PCI_VDEVICE(MELLANOX, 0x6746), MLX4_PCI_DEV_FORCE_SENSE_PORT },
- /* MT26478 ConnectX2 40GigE PCIe gen2 */
- { PCI_VDEVICE(MELLANOX, 0x676e), MLX4_PCI_DEV_FORCE_SENSE_PORT },
- /* MT25400 Family [ConnectX-2 Virtual Function] */
- { PCI_VDEVICE(MELLANOX, 0x1002), MLX4_PCI_DEV_IS_VF },
+ /* MT25408 "Hermon" */
+ MLX_SP(PCI_DEVICE_ID_MELLANOX_HERMON_SDR), /* SDR */
+ MLX_SP(PCI_DEVICE_ID_MELLANOX_HERMON_DDR), /* DDR */
+ MLX_SP(PCI_DEVICE_ID_MELLANOX_HERMON_QDR), /* QDR */
+ MLX_SP(PCI_DEVICE_ID_MELLANOX_HERMON_DDR_GEN2), /* DDR Gen2 */
+ MLX_SP(PCI_DEVICE_ID_MELLANOX_HERMON_QDR_GEN2), /* QDR Gen2 */
+ MLX_SP(PCI_DEVICE_ID_MELLANOX_HERMON_EN), /* EN 10GigE */
+ MLX_SP(PCI_DEVICE_ID_MELLANOX_HERMON_EN_GEN2), /* EN 10GigE Gen2 */
+ /* MT25458 ConnectX EN 10GBASE-T */
+ MLX_SP(PCI_DEVICE_ID_MELLANOX_CONNECTX_EN),
+ MLX_SP(PCI_DEVICE_ID_MELLANOX_CONNECTX_EN_T_GEN2), /* Gen2 */
+ /* MT26468 ConnectX EN 10GigE PCIe Gen2*/
+ MLX_SP(PCI_DEVICE_ID_MELLANOX_CONNECTX_EN_GEN2),
+ /* MT26438 ConnectX EN 40GigE PCIe Gen2 5GT/s */
+ MLX_SP(PCI_DEVICE_ID_MELLANOX_CONNECTX_EN_5_GEN2),
+ /* MT26478 ConnectX2 40GigE PCIe Gen2 */
+ MLX_SP(PCI_DEVICE_ID_MELLANOX_CONNECTX2),
+ /* MT25400 Family [ConnectX-2] */
+ MLX_VF(0x1002), /* Virtual Function */
/* MT27500 Family [ConnectX-3] */
- { PCI_VDEVICE(MELLANOX, 0x1003), 0 },
- /* MT27500 Family [ConnectX-3 Virtual Function] */
- { PCI_VDEVICE(MELLANOX, 0x1004), MLX4_PCI_DEV_IS_VF },
- { PCI_VDEVICE(MELLANOX, 0x1005), 0 }, /* MT27510 Family */
- { PCI_VDEVICE(MELLANOX, 0x1006), 0 }, /* MT27511 Family */
- { PCI_VDEVICE(MELLANOX, 0x1007), 0 }, /* MT27520 Family */
- { PCI_VDEVICE(MELLANOX, 0x1008), 0 }, /* MT27521 Family */
- { PCI_VDEVICE(MELLANOX, 0x1009), 0 }, /* MT27530 Family */
- { PCI_VDEVICE(MELLANOX, 0x100a), 0 }, /* MT27531 Family */
- { PCI_VDEVICE(MELLANOX, 0x100b), 0 }, /* MT27540 Family */
- { PCI_VDEVICE(MELLANOX, 0x100c), 0 }, /* MT27541 Family */
- { PCI_VDEVICE(MELLANOX, 0x100d), 0 }, /* MT27550 Family */
- { PCI_VDEVICE(MELLANOX, 0x100e), 0 }, /* MT27551 Family */
- { PCI_VDEVICE(MELLANOX, 0x100f), 0 }, /* MT27560 Family */
- { PCI_VDEVICE(MELLANOX, 0x1010), 0 }, /* MT27561 Family */
+ MLX_GN(PCI_DEVICE_ID_MELLANOX_CONNECTX3),
+ MLX_VF(0x1004), /* Virtual Function */
+ MLX_GN(0x1005), /* MT27510 Family */
+ MLX_GN(0x1006), /* MT27511 Family */
+ MLX_GN(PCI_DEVICE_ID_MELLANOX_CONNECTX3_PRO), /* MT27520 Family */
+ MLX_GN(0x1008), /* MT27521 Family */
+ MLX_GN(0x1009), /* MT27530 Family */
+ MLX_GN(0x100a), /* MT27531 Family */
+ MLX_GN(0x100b), /* MT27540 Family */
+ MLX_GN(0x100c), /* MT27541 Family */
+ MLX_GN(0x100d), /* MT27550 Family */
+ MLX_GN(0x100e), /* MT27551 Family */
+ MLX_GN(0x100f), /* MT27560 Family */
+ MLX_GN(0x1010), /* MT27561 Family */
+
+ /*
+ * See the mellanox_check_broken_intx_masking() quirk when
+ * adding devices
+ */
+
{ 0, }
};
diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
index c548bea..32f76bf 100644
--- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
@@ -2980,6 +2980,9 @@ int mlx4_RST2INIT_QP_wrapper(struct mlx4_dev *dev, int slave,
put_res(dev, slave, srqn, RES_SRQ);
qp->srq = srq;
}
+
+ /* Save param3 for dynamic changes from VST back to VGT */
+ qp->param3 = qpc->param3;
put_res(dev, slave, rcqn, RES_CQ);
put_res(dev, slave, mtt_base, RES_MTT);
res_end_move(dev, slave, RES_QP, qpn);
@@ -3772,7 +3775,6 @@ int mlx4_INIT2RTR_QP_wrapper(struct mlx4_dev *dev, int slave,
int qpn = vhcr->in_modifier & 0x7fffff;
struct res_qp *qp;
u8 orig_sched_queue;
- __be32 orig_param3 = qpc->param3;
u8 orig_vlan_control = qpc->pri_path.vlan_control;
u8 orig_fvl_rx = qpc->pri_path.fvl_rx;
u8 orig_pri_path_fl = qpc->pri_path.fl;
@@ -3814,7 +3816,6 @@ int mlx4_INIT2RTR_QP_wrapper(struct mlx4_dev *dev, int slave,
*/
if (!err) {
qp->sched_queue = orig_sched_queue;
- qp->param3 = orig_param3;
qp->vlan_control = orig_vlan_control;
qp->fvl_rx = orig_fvl_rx;
qp->pri_path_fl = orig_pri_path_fl;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
index de19c7c..85d949e 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
@@ -2238,14 +2238,16 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
struct brcmf_p2p_info *p2p = &cfg->p2p;
struct brcmf_cfg80211_vif *vif;
+ enum nl80211_iftype iftype;
bool wait_for_disable = false;
int err;
brcmf_dbg(TRACE, "delete P2P vif\n");
vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+ iftype = vif->wdev.iftype;
brcmf_cfg80211_arm_vif_event(cfg, vif);
- switch (vif->wdev.iftype) {
+ switch (iftype) {
case NL80211_IFTYPE_P2P_CLIENT:
if (test_bit(BRCMF_VIF_STATUS_DISCONNECTING, &vif->sme_state))
wait_for_disable = true;
@@ -2275,7 +2277,7 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
BRCMF_P2P_DISABLE_TIMEOUT);
err = 0;
- if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE) {
+ if (iftype != NL80211_IFTYPE_P2P_DEVICE) {
brcmf_vif_clear_mgmt_ies(vif);
err = brcmf_p2p_release_p2p_if(vif);
}
@@ -2291,7 +2293,7 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
brcmf_remove_interface(vif->ifp, true);
brcmf_cfg80211_arm_vif_event(cfg, NULL);
- if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE)
+ if (iftype != NL80211_IFTYPE_P2P_DEVICE)
p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL;
return err;
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
index 4e0c565..b7273be 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
@@ -1422,7 +1422,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)
cancel_work_sync(&rt2x00dev->intf_work);
cancel_delayed_work_sync(&rt2x00dev->autowakeup_work);
cancel_work_sync(&rt2x00dev->sleep_work);
-#ifdef CONFIG_RT2X00_LIB_USB
+#if IS_ENABLED(CONFIG_RT2X00_LIB_USB)
if (rt2x00_is_usb(rt2x00dev)) {
usb_kill_anchored_urbs(rt2x00dev->anchor);
hrtimer_cancel(&rt2x00dev->txstatus_timer);
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
index 6005e14..662705e 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
@@ -319,10 +319,8 @@ static bool rt2x00usb_kick_tx_entry(struct queue_entry *entry, void *data)
entry->skb->data, length,
rt2x00usb_interrupt_txdone, entry);
- usb_anchor_urb(entry_priv->urb, rt2x00dev->anchor);
status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC);
if (status) {
- usb_unanchor_urb(entry_priv->urb);
if (status == -ENODEV)
clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags);
set_bit(ENTRY_DATA_IO_FAILED, &entry->flags);
@@ -410,10 +408,8 @@ static bool rt2x00usb_kick_rx_entry(struct queue_entry *entry, void *data)
entry->skb->data, entry->skb->len,
rt2x00usb_interrupt_rxdone, entry);
- usb_anchor_urb(entry_priv->urb, rt2x00dev->anchor);
status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC);
if (status) {
- usb_unanchor_urb(entry_priv->urb);
if (status == -ENODEV)
clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags);
set_bit(ENTRY_DATA_IO_FAILED, &entry->flags);
@@ -824,10 +820,6 @@ int rt2x00usb_probe(struct usb_interface *usb_intf,
if (retval)
goto exit_free_device;
- retval = rt2x00lib_probe_dev(rt2x00dev);
- if (retval)
- goto exit_free_reg;
-
rt2x00dev->anchor = devm_kmalloc(&usb_dev->dev,
sizeof(struct usb_anchor),
GFP_KERNEL);
@@ -835,10 +827,17 @@ int rt2x00usb_probe(struct usb_interface *usb_intf,
retval = -ENOMEM;
goto exit_free_reg;
}
-
init_usb_anchor(rt2x00dev->anchor);
+
+ retval = rt2x00lib_probe_dev(rt2x00dev);
+ if (retval)
+ goto exit_free_anchor;
+
return 0;
+exit_free_anchor:
+ usb_kill_anchored_urbs(rt2x00dev->anchor);
+
exit_free_reg:
rt2x00usb_free_reg(rt2x00dev);
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index bde769b..5f2feee 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -1204,8 +1204,8 @@ static void nvme_set_queue_limits(struct nvme_ctrl *ctrl,
blk_queue_max_hw_sectors(q, ctrl->max_hw_sectors);
blk_queue_max_segments(q, min_t(u32, max_segments, USHRT_MAX));
}
- if (ctrl->stripe_size)
- blk_queue_chunk_sectors(q, ctrl->stripe_size >> 9);
+ if (ctrl->quirks & NVME_QUIRK_STRIPE_SIZE)
+ blk_queue_chunk_sectors(q, ctrl->max_hw_sectors);
blk_queue_virt_boundary(q, ctrl->page_size - 1);
if (ctrl->vwc & NVME_CTRL_VWC_PRESENT)
vwc = true;
@@ -1261,19 +1261,6 @@ int nvme_init_identify(struct nvme_ctrl *ctrl)
ctrl->max_hw_sectors =
min_not_zero(ctrl->max_hw_sectors, max_hw_sectors);
- if ((ctrl->quirks & NVME_QUIRK_STRIPE_SIZE) && id->vs[3]) {
- unsigned int max_hw_sectors;
-
- ctrl->stripe_size = 1 << (id->vs[3] + page_shift);
- max_hw_sectors = ctrl->stripe_size >> (page_shift - 9);
- if (ctrl->max_hw_sectors) {
- ctrl->max_hw_sectors = min(max_hw_sectors,
- ctrl->max_hw_sectors);
- } else {
- ctrl->max_hw_sectors = max_hw_sectors;
- }
- }
-
nvme_set_queue_limits(ctrl, ctrl->admin_q);
ctrl->sgls = le32_to_cpu(id->sgls);
ctrl->kas = le16_to_cpu(id->kas);
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index d47f5a5..8edafd8 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -121,7 +121,6 @@ struct nvme_ctrl {
u32 page_size;
u32 max_hw_sectors;
- u32 stripe_size;
u16 oncs;
u16 vid;
atomic_t abort_limit;
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 366d8c3..c807c28 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -1,7 +1,7 @@
/*
* Device tree based initialization code for reserved memory.
*
- * Copyright (c) 2013, 2015 The Linux Foundation. All Rights Reserved.
+ * Copyright (c) 2013, 2015, 2017 The Linux Foundation. All Rights Reserved.
* Copyright (c) 2013,2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com
* Author: Marek Szyprowski <m.szyprowski@samsung.com>
@@ -25,7 +25,7 @@
#include <linux/sort.h>
#include <linux/slab.h>
-#define MAX_RESERVED_REGIONS 16
+#define MAX_RESERVED_REGIONS 32
static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
static int reserved_mem_count;
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index 1e45c73..8b3216cd 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -5819,9 +5819,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;
@@ -5843,16 +5844,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);
@@ -5863,6 +5873,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/host/pci-thunder-pem.c b/drivers/pci/host/pci-thunder-pem.c
index 6abaf80..c3276ee 100644
--- a/drivers/pci/host/pci-thunder-pem.c
+++ b/drivers/pci/host/pci-thunder-pem.c
@@ -284,35 +284,16 @@ static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn,
return pci_generic_config_write(bus, devfn, where, size, val);
}
-static int thunder_pem_init(struct pci_config_window *cfg)
+static int thunder_pem_init(struct device *dev, struct pci_config_window *cfg,
+ struct resource *res_pem)
{
- struct device *dev = cfg->parent;
- resource_size_t bar4_start;
- struct resource *res_pem;
struct thunder_pem_pci *pem_pci;
- struct platform_device *pdev;
-
- /* Only OF support for now */
- if (!dev->of_node)
- return -EINVAL;
+ resource_size_t bar4_start;
pem_pci = devm_kzalloc(dev, sizeof(*pem_pci), GFP_KERNEL);
if (!pem_pci)
return -ENOMEM;
- pdev = to_platform_device(dev);
-
- /*
- * The second register range is the PEM bridge to the PCIe
- * bus. It has a different config access method than those
- * devices behind the bridge.
- */
- res_pem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!res_pem) {
- dev_err(dev, "missing \"reg[1]\"property\n");
- return -EINVAL;
- }
-
pem_pci->pem_reg_base = devm_ioremap(dev, res_pem->start, 0x10000);
if (!pem_pci->pem_reg_base)
return -ENOMEM;
@@ -332,9 +313,32 @@ static int thunder_pem_init(struct pci_config_window *cfg)
return 0;
}
+static int thunder_pem_platform_init(struct pci_config_window *cfg)
+{
+ struct device *dev = cfg->parent;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct resource *res_pem;
+
+ if (!dev->of_node)
+ return -EINVAL;
+
+ /*
+ * The second register range is the PEM bridge to the PCIe
+ * bus. It has a different config access method than those
+ * devices behind the bridge.
+ */
+ res_pem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res_pem) {
+ dev_err(dev, "missing \"reg[1]\"property\n");
+ return -EINVAL;
+ }
+
+ return thunder_pem_init(dev, cfg, res_pem);
+}
+
static struct pci_ecam_ops pci_thunder_pem_ops = {
.bus_shift = 24,
- .init = thunder_pem_init,
+ .init = thunder_pem_platform_init,
.pci_ops = {
.map_bus = pci_ecam_map_bus,
.read = thunder_pem_config_read,
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 087a218..5d8151b 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -1634,6 +1634,7 @@ static void quirk_pcie_mch(struct pci_dev *pdev)
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7520_MCH, quirk_pcie_mch);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7320_MCH, quirk_pcie_mch);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7525_MCH, quirk_pcie_mch);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_HUAWEI, 0x1610, quirk_pcie_mch);
/*
@@ -2156,7 +2157,7 @@ static void quirk_blacklist_vpd(struct pci_dev *dev)
{
if (dev->vpd) {
dev->vpd->len = 0;
- dev_warn(&dev->dev, FW_BUG "VPD access disabled\n");
+ dev_warn(&dev->dev, FW_BUG "disabling VPD access (can't determine size of non-standard VPD format)\n");
}
}
@@ -2240,6 +2241,27 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_BROADCOM,
PCI_DEVICE_ID_TIGON3_5719,
quirk_brcm_5719_limit_mrrs);
+#ifdef CONFIG_PCIE_IPROC_PLATFORM
+static void quirk_paxc_bridge(struct pci_dev *pdev)
+{
+ /* The PCI config space is shared with the PAXC root port and the first
+ * Ethernet device. So, we need to workaround this by telling the PCI
+ * code that the bridge is not an Ethernet device.
+ */
+ if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
+ pdev->class = PCI_CLASS_BRIDGE_PCI << 8;
+
+ /* MPSS is not being set properly (as it is currently 0). This is
+ * because that area of the PCI config space is hard coded to zero, and
+ * is not modifiable by firmware. Set this to 2 (e.g., 512 byte MPS)
+ * so that the MPS can be set to the real max value.
+ */
+ pdev->pcie_mpss = 2;
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16cd, quirk_paxc_bridge);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16f0, quirk_paxc_bridge);
+#endif
+
/* Originally in EDAC sources for i82875P:
* Intel tells BIOS developers to hide device 6 which
* configures the overflow device access containing
@@ -3114,30 +3136,32 @@ static void quirk_remove_d3_delay(struct pci_dev *dev)
{
dev->d3_delay = 0;
}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0c00, quirk_remove_d3_delay);
+/* C600 Series devices do not need 10ms d3_delay */
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0412, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0c00, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0c0c, quirk_remove_d3_delay);
+/* Lynxpoint-H PCH devices do not need 10ms d3_delay */
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c02, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c18, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c1c, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c20, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c22, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c26, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c2d, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c31, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c3a, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c3d, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c2d, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c20, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c18, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c1c, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c26, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c4e, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c02, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c22, quirk_remove_d3_delay);
/* Intel Cherrytrail devices do not need 10ms d3_delay */
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2280, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2298, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x229c, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b0, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b5, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b7, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b8, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22d8, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22dc, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b5, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b7, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2298, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x229c, quirk_remove_d3_delay);
/*
* Some devices may pass our check in pci_intx_mask_supported() if
@@ -4137,6 +4161,26 @@ static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags)
}
/*
+ * These QCOM root ports do provide ACS-like features to disable peer
+ * transactions and validate bus numbers in requests, but do not provide an
+ * actual PCIe ACS capability. Hardware supports source validation but it
+ * will report the issue as Completer Abort instead of ACS Violation.
+ * Hardware doesn't support peer-to-peer and each root port is a root
+ * complex with unique segment numbers. It is not possible for one root
+ * port to pass traffic to another root port. All PCIe transactions are
+ * terminated inside the root port.
+ */
+static int pci_quirk_qcom_rp_acs(struct pci_dev *dev, u16 acs_flags)
+{
+ u16 flags = (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_SV);
+ int ret = acs_flags & ~flags ? 0 : 1;
+
+ dev_info(&dev->dev, "Using QCOM ACS Quirk (%d)\n", ret);
+
+ return ret;
+}
+
+/*
* Sunrise Point PCH root ports implement ACS, but unfortunately as shown in
* the datasheet (Intel 100 Series Chipset Family PCH Datasheet, Vol. 2,
* 12.1.46, 12.1.47)[1] this chipset uses dwords for the ACS capability and
@@ -4151,15 +4195,35 @@ static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags)
*
* N.B. This doesn't fix what lspci shows.
*
+ * The 100 series chipset specification update includes this as errata #23[3].
+ *
+ * The 200 series chipset (Union Point) has the same bug according to the
+ * specification update (Intel 200 Series Chipset Family Platform Controller
+ * Hub, Specification Update, January 2017, Revision 001, Document# 335194-001,
+ * Errata 22)[4]. Per the datasheet[5], root port PCI Device IDs for this
+ * chipset include:
+ *
+ * 0xa290-0xa29f PCI Express Root port #{0-16}
+ * 0xa2e7-0xa2ee PCI Express Root port #{17-24}
+ *
* [1] http://www.intel.com/content/www/us/en/chipsets/100-series-chipset-datasheet-vol-2.html
* [2] http://www.intel.com/content/www/us/en/chipsets/100-series-chipset-datasheet-vol-1.html
+ * [3] http://www.intel.com/content/www/us/en/chipsets/100-series-chipset-spec-update.html
+ * [4] http://www.intel.com/content/www/us/en/chipsets/200-series-chipset-pch-spec-update.html
+ * [5] http://www.intel.com/content/www/us/en/chipsets/200-series-chipset-pch-datasheet-vol-1.html
*/
static bool pci_quirk_intel_spt_pch_acs_match(struct pci_dev *dev)
{
- return pci_is_pcie(dev) &&
- pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT &&
- ((dev->device & ~0xf) == 0xa110 ||
- (dev->device >= 0xa167 && dev->device <= 0xa16a));
+ if (!pci_is_pcie(dev) || pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT)
+ return false;
+
+ switch (dev->device) {
+ case 0xa110 ... 0xa11f: case 0xa167 ... 0xa16a: /* Sunrise Point */
+ case 0xa290 ... 0xa29f: case 0xa2e7 ... 0xa2ee: /* Union Point */
+ return true;
+ }
+
+ return false;
}
#define INTEL_SPT_ACS_CTRL (PCI_ACS_CAP + 4)
@@ -4272,6 +4336,9 @@ static const struct pci_dev_acs_enabled {
/* I219 */
{ PCI_VENDOR_ID_INTEL, 0x15b7, pci_quirk_mf_endpoint_acs },
{ PCI_VENDOR_ID_INTEL, 0x15b8, pci_quirk_mf_endpoint_acs },
+ /* QCOM QDF2xxx root ports */
+ { 0x17cb, 0x400, pci_quirk_qcom_rp_acs },
+ { 0x17cb, 0x401, pci_quirk_qcom_rp_acs },
/* Intel PCH root ports */
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs },
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_spt_pch_acs },
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index ba6d5ce..304e206 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -97,6 +97,15 @@
Qualcomm Technologies Inc TLMM block found on the Qualcomm
Technologies Inc SDM830 platform.
+config PINCTRL_SDXPOORWILLS
+ tristate "Qualcomm Technologies Inc SDXPOORWILLS pin controller driver"
+ depends on GPIOLIB && OF
+ select PINCTRL_MSM
+ help
+ This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+ Qualcomm Technologies Inc TLMM block found on the Qualcomm
+ Technologies Inc SDXPOORWILLS platform.
+
config PINCTRL_MSM8996
tristate "Qualcomm MSM8996 pin controller driver"
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index 5e05e89..4786960 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -17,5 +17,6 @@
obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-mpp.o
obj-$(CONFIG_PINCTRL_SDM845) += pinctrl-sdm845.o
obj-$(CONFIG_PINCTRL_SDM830) += pinctrl-sdm830.o
+obj-$(CONFIG_PINCTRL_SDXPOORWILLS) += pinctrl-sdxpoorwills.o
obj-$(CONFIG_PINCTRL_WCD) += pinctrl-wcd.o
obj-$(CONFIG_PINCTRL_LPI) += pinctrl-lpi.o
diff --git a/drivers/pinctrl/qcom/pinctrl-sdxpoorwills.c b/drivers/pinctrl/qcom/pinctrl-sdxpoorwills.c
new file mode 100644
index 0000000..4a21eb6
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-sdxpoorwills.c
@@ -0,0 +1,1205 @@
+/*
+ * 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/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#include "pinctrl-msm.h"
+
+#define FUNCTION(fname) \
+ [msm_mux_##fname] = { \
+ .name = #fname, \
+ .groups = fname##_groups, \
+ .ngroups = ARRAY_SIZE(fname##_groups), \
+ }
+
+#define REG_BASE 0x0
+#define REG_SIZE 0x1000
+#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9) \
+ { \
+ .name = "gpio" #id, \
+ .pins = gpio##id##_pins, \
+ .npins = (unsigned int)ARRAY_SIZE(gpio##id##_pins), \
+ .funcs = (int[]){ \
+ msm_mux_gpio, /* gpio mode */ \
+ msm_mux_##f1, \
+ msm_mux_##f2, \
+ msm_mux_##f3, \
+ msm_mux_##f4, \
+ msm_mux_##f5, \
+ msm_mux_##f6, \
+ msm_mux_##f7, \
+ msm_mux_##f8, \
+ msm_mux_##f9 \
+ }, \
+ .nfuncs = 10, \
+ .ctl_reg = REG_BASE + REG_SIZE * id, \
+ .io_reg = REG_BASE + 0x4 + REG_SIZE * id, \
+ .intr_cfg_reg = REG_BASE + 0x8 + REG_SIZE * id, \
+ .intr_status_reg = REG_BASE + 0xc + REG_SIZE * id, \
+ .intr_target_reg = REG_BASE + 0x8 + REG_SIZE * id, \
+ .mux_bit = 2, \
+ .pull_bit = 0, \
+ .drv_bit = 6, \
+ .oe_bit = 9, \
+ .in_bit = 0, \
+ .out_bit = 1, \
+ .intr_enable_bit = 0, \
+ .intr_status_bit = 0, \
+ .intr_target_bit = 5, \
+ .intr_target_kpss_val = 3, \
+ .intr_raw_status_bit = 4, \
+ .intr_polarity_bit = 1, \
+ .intr_detection_bit = 2, \
+ .intr_detection_width = 2, \
+ }
+
+#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \
+ { \
+ .name = #pg_name, \
+ .pins = pg_name##_pins, \
+ .npins = (unsigned int)ARRAY_SIZE(pg_name##_pins), \
+ .ctl_reg = ctl, \
+ .io_reg = 0, \
+ .intr_cfg_reg = 0, \
+ .intr_status_reg = 0, \
+ .intr_target_reg = 0, \
+ .mux_bit = -1, \
+ .pull_bit = pull, \
+ .drv_bit = drv, \
+ .oe_bit = -1, \
+ .in_bit = -1, \
+ .out_bit = -1, \
+ .intr_enable_bit = -1, \
+ .intr_status_bit = -1, \
+ .intr_target_bit = -1, \
+ .intr_polarity_bit = -1, \
+ .intr_detection_bit = -1, \
+ .intr_detection_width = -1, \
+ }
+static const struct pinctrl_pin_desc sdxpoorwills_pins[] = {
+ PINCTRL_PIN(0, "GPIO_0"),
+ PINCTRL_PIN(1, "GPIO_1"),
+ PINCTRL_PIN(2, "GPIO_2"),
+ PINCTRL_PIN(3, "GPIO_3"),
+ PINCTRL_PIN(4, "GPIO_4"),
+ PINCTRL_PIN(5, "GPIO_5"),
+ PINCTRL_PIN(6, "GPIO_6"),
+ PINCTRL_PIN(7, "GPIO_7"),
+ PINCTRL_PIN(8, "GPIO_8"),
+ PINCTRL_PIN(9, "GPIO_9"),
+ PINCTRL_PIN(10, "GPIO_10"),
+ PINCTRL_PIN(11, "GPIO_11"),
+ PINCTRL_PIN(12, "GPIO_12"),
+ PINCTRL_PIN(13, "GPIO_13"),
+ PINCTRL_PIN(14, "GPIO_14"),
+ PINCTRL_PIN(15, "GPIO_15"),
+ PINCTRL_PIN(16, "GPIO_16"),
+ PINCTRL_PIN(17, "GPIO_17"),
+ PINCTRL_PIN(18, "GPIO_18"),
+ PINCTRL_PIN(19, "GPIO_19"),
+ PINCTRL_PIN(20, "GPIO_20"),
+ PINCTRL_PIN(21, "GPIO_21"),
+ PINCTRL_PIN(22, "GPIO_22"),
+ PINCTRL_PIN(23, "GPIO_23"),
+ PINCTRL_PIN(24, "GPIO_24"),
+ PINCTRL_PIN(25, "GPIO_25"),
+ PINCTRL_PIN(26, "GPIO_26"),
+ PINCTRL_PIN(27, "GPIO_27"),
+ PINCTRL_PIN(28, "GPIO_28"),
+ PINCTRL_PIN(29, "GPIO_29"),
+ PINCTRL_PIN(30, "GPIO_30"),
+ PINCTRL_PIN(31, "GPIO_31"),
+ PINCTRL_PIN(32, "GPIO_32"),
+ PINCTRL_PIN(33, "GPIO_33"),
+ PINCTRL_PIN(34, "GPIO_34"),
+ PINCTRL_PIN(35, "GPIO_35"),
+ PINCTRL_PIN(36, "GPIO_36"),
+ PINCTRL_PIN(37, "GPIO_37"),
+ PINCTRL_PIN(38, "GPIO_38"),
+ PINCTRL_PIN(39, "GPIO_39"),
+ PINCTRL_PIN(40, "GPIO_40"),
+ PINCTRL_PIN(41, "GPIO_41"),
+ PINCTRL_PIN(42, "GPIO_42"),
+ PINCTRL_PIN(43, "GPIO_43"),
+ PINCTRL_PIN(44, "GPIO_44"),
+ PINCTRL_PIN(45, "GPIO_45"),
+ PINCTRL_PIN(46, "GPIO_46"),
+ PINCTRL_PIN(47, "GPIO_47"),
+ PINCTRL_PIN(48, "GPIO_48"),
+ PINCTRL_PIN(49, "GPIO_49"),
+ PINCTRL_PIN(50, "GPIO_50"),
+ PINCTRL_PIN(51, "GPIO_51"),
+ PINCTRL_PIN(52, "GPIO_52"),
+ PINCTRL_PIN(53, "GPIO_53"),
+ PINCTRL_PIN(54, "GPIO_54"),
+ PINCTRL_PIN(55, "GPIO_55"),
+ PINCTRL_PIN(56, "GPIO_56"),
+ PINCTRL_PIN(57, "GPIO_57"),
+ PINCTRL_PIN(58, "GPIO_58"),
+ PINCTRL_PIN(59, "GPIO_59"),
+ PINCTRL_PIN(60, "GPIO_60"),
+ PINCTRL_PIN(61, "GPIO_61"),
+ PINCTRL_PIN(62, "GPIO_62"),
+ PINCTRL_PIN(63, "GPIO_63"),
+ PINCTRL_PIN(64, "GPIO_64"),
+ PINCTRL_PIN(65, "GPIO_65"),
+ PINCTRL_PIN(66, "GPIO_66"),
+ PINCTRL_PIN(67, "GPIO_67"),
+ PINCTRL_PIN(68, "GPIO_68"),
+ PINCTRL_PIN(69, "GPIO_69"),
+ PINCTRL_PIN(70, "GPIO_70"),
+ PINCTRL_PIN(71, "GPIO_71"),
+ PINCTRL_PIN(72, "GPIO_72"),
+ PINCTRL_PIN(73, "GPIO_73"),
+ PINCTRL_PIN(74, "GPIO_74"),
+ PINCTRL_PIN(75, "GPIO_75"),
+ PINCTRL_PIN(76, "GPIO_76"),
+ PINCTRL_PIN(77, "GPIO_77"),
+ PINCTRL_PIN(78, "GPIO_78"),
+ PINCTRL_PIN(79, "GPIO_79"),
+ PINCTRL_PIN(80, "GPIO_80"),
+ PINCTRL_PIN(81, "GPIO_81"),
+ PINCTRL_PIN(82, "GPIO_82"),
+ PINCTRL_PIN(83, "GPIO_83"),
+ PINCTRL_PIN(84, "GPIO_84"),
+ PINCTRL_PIN(85, "GPIO_85"),
+ PINCTRL_PIN(86, "GPIO_86"),
+ PINCTRL_PIN(87, "GPIO_87"),
+ PINCTRL_PIN(88, "GPIO_88"),
+ PINCTRL_PIN(89, "GPIO_89"),
+ PINCTRL_PIN(90, "GPIO_90"),
+ PINCTRL_PIN(91, "GPIO_91"),
+ PINCTRL_PIN(92, "GPIO_92"),
+ PINCTRL_PIN(93, "GPIO_93"),
+ PINCTRL_PIN(94, "GPIO_94"),
+ PINCTRL_PIN(95, "GPIO_95"),
+ PINCTRL_PIN(96, "GPIO_96"),
+ PINCTRL_PIN(97, "GPIO_97"),
+ PINCTRL_PIN(98, "GPIO_98"),
+ PINCTRL_PIN(99, "GPIO_99"),
+ PINCTRL_PIN(100, "SDC1_CLK"),
+ PINCTRL_PIN(101, "SDC1_CMD"),
+ PINCTRL_PIN(102, "SDC1_DATA"),
+ PINCTRL_PIN(103, "SDC2_CLK"),
+ PINCTRL_PIN(104, "SDC2_CMD"),
+ PINCTRL_PIN(105, "SDC2_DATA"),
+};
+
+#define DECLARE_MSM_GPIO_PINS(pin) \
+ static const unsigned int gpio##pin##_pins[] = { pin }
+DECLARE_MSM_GPIO_PINS(0);
+DECLARE_MSM_GPIO_PINS(1);
+DECLARE_MSM_GPIO_PINS(2);
+DECLARE_MSM_GPIO_PINS(3);
+DECLARE_MSM_GPIO_PINS(4);
+DECLARE_MSM_GPIO_PINS(5);
+DECLARE_MSM_GPIO_PINS(6);
+DECLARE_MSM_GPIO_PINS(7);
+DECLARE_MSM_GPIO_PINS(8);
+DECLARE_MSM_GPIO_PINS(9);
+DECLARE_MSM_GPIO_PINS(10);
+DECLARE_MSM_GPIO_PINS(11);
+DECLARE_MSM_GPIO_PINS(12);
+DECLARE_MSM_GPIO_PINS(13);
+DECLARE_MSM_GPIO_PINS(14);
+DECLARE_MSM_GPIO_PINS(15);
+DECLARE_MSM_GPIO_PINS(16);
+DECLARE_MSM_GPIO_PINS(17);
+DECLARE_MSM_GPIO_PINS(18);
+DECLARE_MSM_GPIO_PINS(19);
+DECLARE_MSM_GPIO_PINS(20);
+DECLARE_MSM_GPIO_PINS(21);
+DECLARE_MSM_GPIO_PINS(22);
+DECLARE_MSM_GPIO_PINS(23);
+DECLARE_MSM_GPIO_PINS(24);
+DECLARE_MSM_GPIO_PINS(25);
+DECLARE_MSM_GPIO_PINS(26);
+DECLARE_MSM_GPIO_PINS(27);
+DECLARE_MSM_GPIO_PINS(28);
+DECLARE_MSM_GPIO_PINS(29);
+DECLARE_MSM_GPIO_PINS(30);
+DECLARE_MSM_GPIO_PINS(31);
+DECLARE_MSM_GPIO_PINS(32);
+DECLARE_MSM_GPIO_PINS(33);
+DECLARE_MSM_GPIO_PINS(34);
+DECLARE_MSM_GPIO_PINS(35);
+DECLARE_MSM_GPIO_PINS(36);
+DECLARE_MSM_GPIO_PINS(37);
+DECLARE_MSM_GPIO_PINS(38);
+DECLARE_MSM_GPIO_PINS(39);
+DECLARE_MSM_GPIO_PINS(40);
+DECLARE_MSM_GPIO_PINS(41);
+DECLARE_MSM_GPIO_PINS(42);
+DECLARE_MSM_GPIO_PINS(43);
+DECLARE_MSM_GPIO_PINS(44);
+DECLARE_MSM_GPIO_PINS(45);
+DECLARE_MSM_GPIO_PINS(46);
+DECLARE_MSM_GPIO_PINS(47);
+DECLARE_MSM_GPIO_PINS(48);
+DECLARE_MSM_GPIO_PINS(49);
+DECLARE_MSM_GPIO_PINS(50);
+DECLARE_MSM_GPIO_PINS(51);
+DECLARE_MSM_GPIO_PINS(52);
+DECLARE_MSM_GPIO_PINS(53);
+DECLARE_MSM_GPIO_PINS(54);
+DECLARE_MSM_GPIO_PINS(55);
+DECLARE_MSM_GPIO_PINS(56);
+DECLARE_MSM_GPIO_PINS(57);
+DECLARE_MSM_GPIO_PINS(58);
+DECLARE_MSM_GPIO_PINS(59);
+DECLARE_MSM_GPIO_PINS(60);
+DECLARE_MSM_GPIO_PINS(61);
+DECLARE_MSM_GPIO_PINS(62);
+DECLARE_MSM_GPIO_PINS(63);
+DECLARE_MSM_GPIO_PINS(64);
+DECLARE_MSM_GPIO_PINS(65);
+DECLARE_MSM_GPIO_PINS(66);
+DECLARE_MSM_GPIO_PINS(67);
+DECLARE_MSM_GPIO_PINS(68);
+DECLARE_MSM_GPIO_PINS(69);
+DECLARE_MSM_GPIO_PINS(70);
+DECLARE_MSM_GPIO_PINS(71);
+DECLARE_MSM_GPIO_PINS(72);
+DECLARE_MSM_GPIO_PINS(73);
+DECLARE_MSM_GPIO_PINS(74);
+DECLARE_MSM_GPIO_PINS(75);
+DECLARE_MSM_GPIO_PINS(76);
+DECLARE_MSM_GPIO_PINS(77);
+DECLARE_MSM_GPIO_PINS(78);
+DECLARE_MSM_GPIO_PINS(79);
+DECLARE_MSM_GPIO_PINS(80);
+DECLARE_MSM_GPIO_PINS(81);
+DECLARE_MSM_GPIO_PINS(82);
+DECLARE_MSM_GPIO_PINS(83);
+DECLARE_MSM_GPIO_PINS(84);
+DECLARE_MSM_GPIO_PINS(85);
+DECLARE_MSM_GPIO_PINS(86);
+DECLARE_MSM_GPIO_PINS(87);
+DECLARE_MSM_GPIO_PINS(88);
+DECLARE_MSM_GPIO_PINS(89);
+DECLARE_MSM_GPIO_PINS(90);
+DECLARE_MSM_GPIO_PINS(91);
+DECLARE_MSM_GPIO_PINS(92);
+DECLARE_MSM_GPIO_PINS(93);
+DECLARE_MSM_GPIO_PINS(94);
+DECLARE_MSM_GPIO_PINS(95);
+DECLARE_MSM_GPIO_PINS(96);
+DECLARE_MSM_GPIO_PINS(97);
+DECLARE_MSM_GPIO_PINS(98);
+DECLARE_MSM_GPIO_PINS(99);
+
+static const unsigned int sdc1_clk_pins[] = { 100 };
+static const unsigned int sdc1_cmd_pins[] = { 101 };
+static const unsigned int sdc1_data_pins[] = { 102 };
+static const unsigned int sdc2_clk_pins[] = { 103 };
+static const unsigned int sdc2_cmd_pins[] = { 104 };
+static const unsigned int sdc2_data_pins[] = { 105 };
+
+enum sdxpoorwills_functions {
+ msm_mux_qdss_stm31,
+ msm_mux_blsp_uart1,
+ msm_mux_gpio,
+ msm_mux_uim2_data,
+ msm_mux_ebi0_wrcdc,
+ msm_mux_uim2_present,
+ msm_mux_qdss_stm30,
+ msm_mux_uim2_reset,
+ msm_mux_blsp_i2c1,
+ msm_mux_qdss_stm29,
+ msm_mux_uim2_clk,
+ msm_mux_qdss_stm28,
+ msm_mux_blsp_spi2,
+ msm_mux_blsp_uart2,
+ msm_mux_qdss_stm23,
+ msm_mux_qdss3,
+ msm_mux_qdss_stm22,
+ msm_mux_qdss2,
+ msm_mux_blsp_i2c2,
+ msm_mux_qdss_stm21,
+ msm_mux_qdss1,
+ msm_mux_qdss_stm20,
+ msm_mux_qdss0,
+ msm_mux_pri_mi2s,
+ msm_mux_blsp_spi3,
+ msm_mux_blsp_uart3,
+ msm_mux_ext_dbg,
+ msm_mux_ldo_en,
+ msm_mux_blsp_i2c3,
+ msm_mux_gcc_gp3,
+ msm_mux_qdss_stm19,
+ msm_mux_qdss12,
+ msm_mux_qdss_stm18,
+ msm_mux_qdss13,
+ msm_mux_qdss_stm17,
+ msm_mux_qdss14,
+ msm_mux_bimc_dte0,
+ msm_mux_native_tsens,
+ msm_mux_vsense_trigger,
+ msm_mux_qdss_stm26,
+ msm_mux_qdss9,
+ msm_mux_blsp_i2c4,
+ msm_mux_gcc_gp1,
+ msm_mux_qdss_stm25,
+ msm_mux_qdss10,
+ msm_mux_jitter_bist,
+ msm_mux_gcc_gp2,
+ msm_mux_qdss_stm24,
+ msm_mux_qdss11,
+ msm_mux_qdss_stm16,
+ msm_mux_qdss15,
+ msm_mux_bimc_dte1,
+ msm_mux_sec_mi2s,
+ msm_mux_blsp_spi4,
+ msm_mux_blsp_uart4,
+ msm_mux_qdss_cti,
+ msm_mux_qdss_stm27,
+ msm_mux_qdss8,
+ msm_mux_ebi2_a,
+ msm_mux_qdss_stm3,
+ msm_mux_ebi2_lcd,
+ msm_mux_qdss_stm2,
+ msm_mux_pll_bist,
+ msm_mux_qdss_stm1,
+ msm_mux_qdss_stm0,
+ msm_mux_adsp_ext,
+ msm_mux_epm1,
+ msm_mux_m_voc,
+ msm_mux_native_char,
+ msm_mux_native_char1,
+ msm_mux_pa_indicator,
+ msm_mux_qdss_traceclk,
+ msm_mux_native_char0,
+ msm_mux_qlink_en,
+ msm_mux_qlink_req,
+ msm_mux_pll_test,
+ msm_mux_cri_trng,
+ msm_mux_wmss_reset,
+ msm_mux_native_char3,
+ msm_mux_nav_pps,
+ msm_mux_nav_dr,
+ msm_mux_native_char2,
+ msm_mux_native_tsense,
+ msm_mux_prng_rosc,
+ msm_mux_cri_trng0,
+ msm_mux_cri_trng1,
+ msm_mux_pll_ref,
+ msm_mux_coex_uart,
+ msm_mux_qdss_stm11,
+ msm_mux_qdss_stm10,
+ msm_mux_ddr_pxi0,
+ msm_mux_ap2mdm_status,
+ msm_mux_ddr_bist,
+ msm_mux_mdm2ap_status,
+ msm_mux_ap2mdm_err,
+ msm_mux_mdm2ap_err,
+ msm_mux_ap2mdm_vdd,
+ msm_mux_mdm2ap_vdd,
+ msm_mux_ap2mdm_wake,
+ msm_mux_pciehost_rst,
+ msm_mux_blsp_spi1,
+ msm_mux_qdss_stm14,
+ msm_mux_pcie_wake,
+ msm_mux_mdm2ap_wake,
+ msm_mux_pci_e,
+ msm_mux_qdss_stm13,
+ msm_mux_i2s_mclk,
+ msm_mux_audio_ref,
+ msm_mux_ldo_update,
+ msm_mux_qdss_stm8,
+ msm_mux_qdss_stm7,
+ msm_mux_qdss4,
+ msm_mux_tgu_ch0,
+ msm_mux_pcie_clkreq,
+ msm_mux_qdss_stm9,
+ msm_mux_qdss_stm15,
+ msm_mux_mgpi_clk,
+ msm_mux_qdss_stm12,
+ msm_mux_qdss_tracectl,
+ msm_mux_atest_char,
+ msm_mux_qdss_stm6,
+ msm_mux_qdss5,
+ msm_mux_atest_char3,
+ msm_mux_qdss_stm5,
+ msm_mux_qdss6,
+ msm_mux_atest_char2,
+ msm_mux_qdss_stm4,
+ msm_mux_qdss7,
+ msm_mux_atest_char1,
+ msm_mux_uim1_data,
+ msm_mux_atest_char0,
+ msm_mux_uim1_present,
+ msm_mux_uim1_reset,
+ msm_mux_uim1_clk,
+ msm_mux_dbg_out,
+ msm_mux_gcc_plltest,
+ msm_mux_usb2phy_ac,
+ msm_mux_NA,
+};
+
+static const char * const qdss_stm31_groups[] = {
+ "gpio0",
+};
+static const char * const blsp_uart1_groups[] = {
+ "gpio0", "gpio1", "gpio2", "gpio3", "gpio20", "gpio21", "gpio22",
+ "gpio23",
+};
+static const char * const gpio_groups[] = {
+ "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7",
+ "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14",
+ "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21",
+ "gpio22", "gpio23", "gpio24", "gpio26", "gpio27", "gpio28", "gpio29",
+ "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36",
+ "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", "gpio43",
+ "gpio44", "gpio45", "gpio54", "gpio55", "gpio56", "gpio58", "gpio59",
+ "gpio60", "gpio61", "gpio62", "gpio63", "gpio64", "gpio65", "gpio66",
+ "gpio67", "gpio68", "gpio69", "gpio70", "gpio71", "gpio72", "gpio73",
+ "gpio74", "gpio75", "gpio76", "gpio77", "gpio78", "gpio79", "gpio80",
+ "gpio81", "gpio82", "gpio83", "gpio84", "gpio85", "gpio86", "gpio87",
+ "gpio88", "gpio89", "gpio90", "gpio91", "gpio92", "gpio93", "gpio94",
+ "gpio95", "gpio96", "gpio97", "gpio98", "gpio99",
+};
+static const char * const uim2_data_groups[] = {
+ "gpio0",
+};
+static const char * const ebi0_wrcdc_groups[] = {
+ "gpio0", "gpio2",
+};
+static const char * const uim2_present_groups[] = {
+ "gpio1",
+};
+static const char * const qdss_stm30_groups[] = {
+ "gpio1",
+};
+static const char * const uim2_reset_groups[] = {
+ "gpio2",
+};
+static const char * const blsp_i2c1_groups[] = {
+ "gpio2", "gpio3", "gpio74", "gpio75",
+};
+static const char * const qdss_stm29_groups[] = {
+ "gpio2",
+};
+static const char * const uim2_clk_groups[] = {
+ "gpio3",
+};
+static const char * const qdss_stm28_groups[] = {
+ "gpio3",
+};
+static const char * const blsp_spi2_groups[] = {
+ "gpio4", "gpio5", "gpio6", "gpio7", "gpio52", "gpio62", "gpio71",
+};
+static const char * const blsp_uart2_groups[] = {
+ "gpio4", "gpio5", "gpio6", "gpio7", "gpio63", "gpio64", "gpio65",
+ "gpio66",
+};
+static const char * const qdss_stm23_groups[] = {
+ "gpio4",
+};
+static const char * const qdss3_groups[] = {
+ "gpio4",
+};
+static const char * const qdss_stm22_groups[] = {
+ "gpio5",
+};
+static const char * const qdss2_groups[] = {
+ "gpio5",
+};
+static const char * const blsp_i2c2_groups[] = {
+ "gpio6", "gpio7", "gpio65", "gpio66",
+};
+static const char * const qdss_stm21_groups[] = {
+ "gpio6",
+};
+static const char * const qdss1_groups[] = {
+ "gpio6",
+};
+static const char * const qdss_stm20_groups[] = {
+ "gpio7",
+};
+static const char * const qdss0_groups[] = {
+ "gpio7",
+};
+static const char * const pri_mi2s_groups[] = {
+ "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14",
+ "gpio15",
+};
+static const char * const blsp_spi3_groups[] = {
+ "gpio8", "gpio9", "gpio10", "gpio11", "gpio52", "gpio62", "gpio71",
+};
+static const char * const blsp_uart3_groups[] = {
+ "gpio8", "gpio9", "gpio10", "gpio11",
+};
+static const char * const ext_dbg_groups[] = {
+ "gpio8", "gpio9", "gpio10", "gpio11",
+};
+static const char * const ldo_en_groups[] = {
+ "gpio8",
+};
+static const char * const blsp_i2c3_groups[] = {
+ "gpio10", "gpio11",
+};
+static const char * const gcc_gp3_groups[] = {
+ "gpio11",
+};
+static const char * const qdss_stm19_groups[] = {
+ "gpio12",
+};
+static const char * const qdss12_groups[] = {
+ "gpio12",
+};
+static const char * const qdss_stm18_groups[] = {
+ "gpio13",
+};
+static const char * const qdss13_groups[] = {
+ "gpio13",
+};
+static const char * const qdss_stm17_groups[] = {
+ "gpio14",
+};
+static const char * const qdss14_groups[] = {
+ "gpio14",
+};
+static const char * const bimc_dte0_groups[] = {
+ "gpio14", "gpio59",
+};
+static const char * const native_tsens_groups[] = {
+ "gpio14",
+};
+static const char * const vsense_trigger_groups[] = {
+ "gpio14",
+};
+static const char * const qdss_stm26_groups[] = {
+ "gpio17",
+};
+static const char * const qdss9_groups[] = {
+ "gpio17",
+};
+static const char * const blsp_i2c4_groups[] = {
+ "gpio18", "gpio19", "gpio76", "gpio77",
+};
+static const char * const gcc_gp1_groups[] = {
+ "gpio18",
+};
+static const char * const qdss_stm25_groups[] = {
+ "gpio18",
+};
+static const char * const qdss10_groups[] = {
+ "gpio18",
+};
+static const char * const jitter_bist_groups[] = {
+ "gpio19",
+};
+static const char * const gcc_gp2_groups[] = {
+ "gpio19",
+};
+static const char * const qdss_stm24_groups[] = {
+ "gpio19",
+};
+static const char * const qdss11_groups[] = {
+ "gpio19",
+};
+static const char * const qdss_stm16_groups[] = {
+ "gpio15",
+};
+static const char * const qdss15_groups[] = {
+ "gpio15",
+};
+static const char * const bimc_dte1_groups[] = {
+ "gpio15", "gpio60",
+};
+static const char * const sec_mi2s_groups[] = {
+ "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22",
+ "gpio23",
+};
+static const char * const blsp_spi4_groups[] = {
+ "gpio16", "gpio17", "gpio18", "gpio19", "gpio52", "gpio62", "gpio71",
+};
+static const char * const blsp_uart4_groups[] = {
+ "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22",
+ "gpio23",
+};
+static const char * const qdss_cti_groups[] = {
+ "gpio16", "gpio16", "gpio17", "gpio17", "gpio22", "gpio22", "gpio23",
+ "gpio23", "gpio54", "gpio54", "gpio55", "gpio55", "gpio59", "gpio61",
+ "gpio88", "gpio88", "gpio89", "gpio89",
+};
+static const char * const qdss_stm27_groups[] = {
+ "gpio16",
+};
+static const char * const qdss8_groups[] = {
+ "gpio16",
+};
+static const char * const ebi2_a_groups[] = {
+ "gpio20",
+};
+static const char * const qdss_stm3_groups[] = {
+ "gpio20",
+};
+static const char * const ebi2_lcd_groups[] = {
+ "gpio21", "gpio22", "gpio23",
+};
+static const char * const qdss_stm2_groups[] = {
+ "gpio21",
+};
+static const char * const pll_bist_groups[] = {
+ "gpio22",
+};
+static const char * const qdss_stm1_groups[] = {
+ "gpio22",
+};
+static const char * const qdss_stm0_groups[] = {
+ "gpio23",
+};
+static const char * const adsp_ext_groups[] = {
+ "gpio24", "gpio25",
+};
+static const char * const epm1_groups[] = {
+ "gpio25",
+};
+static const char * const m_voc_groups[] = {
+ "gpio25", "gpio46", "gpio59", "gpio61",
+};
+static const char * const native_char_groups[] = {
+ "gpio26",
+};
+static const char * const native_char1_groups[] = {
+ "gpio32",
+};
+static const char * const pa_indicator_groups[] = {
+ "gpio33",
+};
+static const char * const qdss_traceclk_groups[] = {
+ "gpio33",
+};
+static const char * const native_char0_groups[] = {
+ "gpio33",
+};
+static const char * const qlink_en_groups[] = {
+ "gpio34",
+};
+static const char * const qlink_req_groups[] = {
+ "gpio35",
+};
+static const char * const pll_test_groups[] = {
+ "gpio35",
+};
+static const char * const cri_trng_groups[] = {
+ "gpio36",
+};
+static const char * const wmss_reset_groups[] = {
+ "gpio28",
+};
+static const char * const native_char3_groups[] = {
+ "gpio28",
+};
+static const char * const nav_pps_groups[] = {
+ "gpio29", "gpio42", "gpio62",
+};
+static const char * const nav_dr_groups[] = {
+ "gpio29", "gpio42", "gpio62",
+};
+static const char * const native_char2_groups[] = {
+ "gpio29",
+};
+static const char * const native_tsense_groups[] = {
+ "gpio29",
+};
+static const char * const prng_rosc_groups[] = {
+ "gpio38",
+};
+static const char * const cri_trng0_groups[] = {
+ "gpio40",
+};
+static const char * const cri_trng1_groups[] = {
+ "gpio41",
+};
+static const char * const pll_ref_groups[] = {
+ "gpio42",
+};
+static const char * const coex_uart_groups[] = {
+ "gpio44", "gpio45",
+};
+static const char * const qdss_stm11_groups[] = {
+ "gpio44",
+};
+static const char * const qdss_stm10_groups[] = {
+ "gpio45",
+};
+static const char * const ddr_pxi0_groups[] = {
+ "gpio45", "gpio46",
+};
+static const char * const ap2mdm_status_groups[] = {
+ "gpio46",
+};
+static const char * const ddr_bist_groups[] = {
+ "gpio46", "gpio47", "gpio48", "gpio49",
+};
+static const char * const mdm2ap_status_groups[] = {
+ "gpio47",
+};
+static const char * const ap2mdm_err_groups[] = {
+ "gpio48",
+};
+static const char * const mdm2ap_err_groups[] = {
+ "gpio49",
+};
+static const char * const ap2mdm_vdd_groups[] = {
+ "gpio50",
+};
+static const char * const mdm2ap_vdd_groups[] = {
+ "gpio51",
+};
+static const char * const ap2mdm_wake_groups[] = {
+ "gpio52",
+};
+static const char * const pciehost_rst_groups[] = {
+ "gpio52",
+};
+static const char * const blsp_spi1_groups[] = {
+ "gpio52", "gpio62", "gpio71", "gpio72", "gpio73", "gpio74", "gpio75",
+};
+static const char * const qdss_stm14_groups[] = {
+ "gpio52",
+};
+static const char * const pcie_wake_groups[] = {
+ "gpio53",
+};
+static const char * const mdm2ap_wake_groups[] = {
+ "gpio53",
+};
+static const char * const pci_e_groups[] = {
+ "gpio53", "gpio57",
+};
+static const char * const qdss_stm13_groups[] = {
+ "gpio53",
+};
+static const char * const i2s_mclk_groups[] = {
+ "gpio62",
+};
+static const char * const audio_ref_groups[] = {
+ "gpio62",
+};
+static const char * const ldo_update_groups[] = {
+ "gpio62",
+};
+static const char * const qdss_stm8_groups[] = {
+ "gpio62",
+};
+static const char * const qdss_stm7_groups[] = {
+ "gpio63",
+};
+static const char * const qdss4_groups[] = {
+ "gpio63",
+};
+static const char * const tgu_ch0_groups[] = {
+ "gpio55",
+};
+static const char * const pcie_clkreq_groups[] = {
+ "gpio56",
+};
+static const char * const qdss_stm9_groups[] = {
+ "gpio56",
+};
+static const char * const qdss_stm15_groups[] = {
+ "gpio57",
+};
+static const char * const mgpi_clk_groups[] = {
+ "gpio60", "gpio71",
+};
+static const char * const qdss_stm12_groups[] = {
+ "gpio60",
+};
+static const char * const qdss_tracectl_groups[] = {
+ "gpio60",
+};
+static const char * const atest_char_groups[] = {
+ "gpio63",
+};
+static const char * const qdss_stm6_groups[] = {
+ "gpio64",
+};
+static const char * const qdss5_groups[] = {
+ "gpio64",
+};
+static const char * const atest_char3_groups[] = {
+ "gpio64",
+};
+static const char * const qdss_stm5_groups[] = {
+ "gpio65",
+};
+static const char * const qdss6_groups[] = {
+ "gpio65",
+};
+static const char * const atest_char2_groups[] = {
+ "gpio65",
+};
+static const char * const qdss_stm4_groups[] = {
+ "gpio66",
+};
+static const char * const qdss7_groups[] = {
+ "gpio66",
+};
+static const char * const atest_char1_groups[] = {
+ "gpio66",
+};
+static const char * const uim1_data_groups[] = {
+ "gpio67",
+};
+static const char * const atest_char0_groups[] = {
+ "gpio67",
+};
+static const char * const uim1_present_groups[] = {
+ "gpio68",
+};
+static const char * const uim1_reset_groups[] = {
+ "gpio69",
+};
+static const char * const uim1_clk_groups[] = {
+ "gpio70",
+};
+static const char * const dbg_out_groups[] = {
+ "gpio71",
+};
+static const char * const gcc_plltest_groups[] = {
+ "gpio73", "gpio74",
+};
+static const char * const usb2phy_ac_groups[] = {
+ "gpio87",
+};
+
+static const struct msm_function sdxpoorwills_functions[] = {
+ FUNCTION(qdss_stm31),
+ FUNCTION(blsp_uart1),
+ FUNCTION(gpio),
+ FUNCTION(uim2_data),
+ FUNCTION(ebi0_wrcdc),
+ FUNCTION(uim2_present),
+ FUNCTION(qdss_stm30),
+ FUNCTION(uim2_reset),
+ FUNCTION(blsp_i2c1),
+ FUNCTION(qdss_stm29),
+ FUNCTION(uim2_clk),
+ FUNCTION(qdss_stm28),
+ FUNCTION(blsp_spi2),
+ FUNCTION(blsp_uart2),
+ FUNCTION(qdss_stm23),
+ FUNCTION(qdss3),
+ FUNCTION(qdss_stm22),
+ FUNCTION(qdss2),
+ FUNCTION(blsp_i2c2),
+ FUNCTION(qdss_stm21),
+ FUNCTION(qdss1),
+ FUNCTION(qdss_stm20),
+ FUNCTION(qdss0),
+ FUNCTION(pri_mi2s),
+ FUNCTION(blsp_spi3),
+ FUNCTION(blsp_uart3),
+ FUNCTION(ext_dbg),
+ FUNCTION(ldo_en),
+ FUNCTION(blsp_i2c3),
+ FUNCTION(gcc_gp3),
+ FUNCTION(qdss_stm19),
+ FUNCTION(qdss12),
+ FUNCTION(qdss_stm18),
+ FUNCTION(qdss13),
+ FUNCTION(qdss_stm17),
+ FUNCTION(qdss14),
+ FUNCTION(bimc_dte0),
+ FUNCTION(native_tsens),
+ FUNCTION(vsense_trigger),
+ FUNCTION(qdss_stm26),
+ FUNCTION(qdss9),
+ FUNCTION(blsp_i2c4),
+ FUNCTION(gcc_gp1),
+ FUNCTION(qdss_stm25),
+ FUNCTION(qdss10),
+ FUNCTION(jitter_bist),
+ FUNCTION(gcc_gp2),
+ FUNCTION(qdss_stm24),
+ FUNCTION(qdss11),
+ FUNCTION(qdss_stm16),
+ FUNCTION(qdss15),
+ FUNCTION(bimc_dte1),
+ FUNCTION(sec_mi2s),
+ FUNCTION(blsp_spi4),
+ FUNCTION(blsp_uart4),
+ FUNCTION(qdss_cti),
+ FUNCTION(qdss_stm27),
+ FUNCTION(qdss8),
+ FUNCTION(ebi2_a),
+ FUNCTION(qdss_stm3),
+ FUNCTION(ebi2_lcd),
+ FUNCTION(qdss_stm2),
+ FUNCTION(pll_bist),
+ FUNCTION(qdss_stm1),
+ FUNCTION(qdss_stm0),
+ FUNCTION(adsp_ext),
+ FUNCTION(epm1),
+ FUNCTION(m_voc),
+ FUNCTION(native_char),
+ FUNCTION(native_char1),
+ FUNCTION(pa_indicator),
+ FUNCTION(qdss_traceclk),
+ FUNCTION(native_char0),
+ FUNCTION(qlink_en),
+ FUNCTION(qlink_req),
+ FUNCTION(pll_test),
+ FUNCTION(cri_trng),
+ FUNCTION(wmss_reset),
+ FUNCTION(native_char3),
+ FUNCTION(nav_pps),
+ FUNCTION(nav_dr),
+ FUNCTION(native_char2),
+ FUNCTION(native_tsense),
+ FUNCTION(prng_rosc),
+ FUNCTION(cri_trng0),
+ FUNCTION(cri_trng1),
+ FUNCTION(pll_ref),
+ FUNCTION(coex_uart),
+ FUNCTION(qdss_stm11),
+ FUNCTION(qdss_stm10),
+ FUNCTION(ddr_pxi0),
+ FUNCTION(ap2mdm_status),
+ FUNCTION(ddr_bist),
+ FUNCTION(mdm2ap_status),
+ FUNCTION(ap2mdm_err),
+ FUNCTION(mdm2ap_err),
+ FUNCTION(ap2mdm_vdd),
+ FUNCTION(mdm2ap_vdd),
+ FUNCTION(ap2mdm_wake),
+ FUNCTION(pciehost_rst),
+ FUNCTION(blsp_spi1),
+ FUNCTION(qdss_stm14),
+ FUNCTION(pcie_wake),
+ FUNCTION(mdm2ap_wake),
+ FUNCTION(pci_e),
+ FUNCTION(qdss_stm13),
+ FUNCTION(i2s_mclk),
+ FUNCTION(audio_ref),
+ FUNCTION(ldo_update),
+ FUNCTION(qdss_stm8),
+ FUNCTION(qdss_stm7),
+ FUNCTION(qdss4),
+ FUNCTION(tgu_ch0),
+ FUNCTION(pcie_clkreq),
+ FUNCTION(qdss_stm9),
+ FUNCTION(qdss_stm15),
+ FUNCTION(mgpi_clk),
+ FUNCTION(qdss_stm12),
+ FUNCTION(qdss_tracectl),
+ FUNCTION(atest_char),
+ FUNCTION(qdss_stm6),
+ FUNCTION(qdss5),
+ FUNCTION(atest_char3),
+ FUNCTION(qdss_stm5),
+ FUNCTION(qdss6),
+ FUNCTION(atest_char2),
+ FUNCTION(qdss_stm4),
+ FUNCTION(qdss7),
+ FUNCTION(atest_char1),
+ FUNCTION(uim1_data),
+ FUNCTION(atest_char0),
+ FUNCTION(uim1_present),
+ FUNCTION(uim1_reset),
+ FUNCTION(uim1_clk),
+ FUNCTION(dbg_out),
+ FUNCTION(gcc_plltest),
+ FUNCTION(usb2phy_ac),
+};
+
+static const struct msm_pingroup sdxpoorwills_groups[] = {
+ PINGROUP(0, uim2_data, blsp_uart1, qdss_stm31, ebi0_wrcdc, NA, NA, NA,
+ NA, NA),
+ PINGROUP(1, uim2_present, blsp_uart1, qdss_stm30, NA, NA, NA, NA, NA,
+ NA),
+ PINGROUP(2, uim2_reset, blsp_uart1, blsp_i2c1, qdss_stm29, ebi0_wrcdc,
+ NA, NA, NA, NA),
+ PINGROUP(3, uim2_clk, blsp_uart1, blsp_i2c1, qdss_stm28, NA, NA, NA,
+ NA, NA),
+ PINGROUP(4, blsp_spi2, blsp_uart2, NA, qdss_stm23, qdss3, NA, NA, NA,
+ NA),
+ PINGROUP(5, blsp_spi2, blsp_uart2, NA, qdss_stm22, qdss2, NA, NA, NA,
+ NA),
+ PINGROUP(6, blsp_spi2, blsp_uart2, blsp_i2c2, NA, qdss_stm21, qdss1,
+ NA, NA, NA),
+ PINGROUP(7, blsp_spi2, blsp_uart2, blsp_i2c2, NA, qdss_stm20, qdss0,
+ NA, NA, NA),
+ PINGROUP(8, pri_mi2s, blsp_spi3, blsp_uart3, ext_dbg, ldo_en, NA, NA,
+ NA, NA),
+ PINGROUP(9, pri_mi2s, blsp_spi3, blsp_uart3, ext_dbg, NA, NA, NA, NA,
+ NA),
+ PINGROUP(10, pri_mi2s, blsp_spi3, blsp_uart3, blsp_i2c3, ext_dbg, NA,
+ NA, NA, NA),
+ PINGROUP(11, pri_mi2s, blsp_spi3, blsp_uart3, blsp_i2c3, ext_dbg,
+ gcc_gp3, NA, NA, NA),
+ PINGROUP(12, pri_mi2s, NA, qdss_stm19, qdss12, NA, NA, NA, NA, NA),
+ PINGROUP(13, pri_mi2s, NA, qdss_stm18, qdss13, NA, NA, NA, NA, NA),
+ PINGROUP(14, pri_mi2s, NA, NA, qdss_stm17, qdss14, bimc_dte0,
+ native_tsens, vsense_trigger, NA),
+ PINGROUP(15, pri_mi2s, NA, NA, qdss_stm16, qdss15, NA, NA, bimc_dte1,
+ NA),
+ PINGROUP(16, sec_mi2s, blsp_spi4, blsp_uart4, qdss_cti, qdss_cti, NA,
+ NA, qdss_stm27, qdss8),
+ PINGROUP(17, sec_mi2s, blsp_spi4, blsp_uart4, qdss_cti, qdss_cti, NA,
+ qdss_stm26, qdss9, NA),
+ PINGROUP(18, sec_mi2s, blsp_spi4, blsp_uart4, blsp_i2c4, gcc_gp1, NA,
+ qdss_stm25, qdss10, NA),
+ PINGROUP(19, sec_mi2s, blsp_spi4, blsp_uart4, blsp_i2c4, jitter_bist,
+ gcc_gp2, NA, qdss_stm24, qdss11),
+ PINGROUP(20, sec_mi2s, ebi2_a, blsp_uart1, blsp_uart4, NA, qdss_stm3,
+ NA, NA, NA),
+ PINGROUP(21, sec_mi2s, ebi2_lcd, blsp_uart1, blsp_uart4, NA, NA,
+ qdss_stm2, NA, NA),
+ PINGROUP(22, sec_mi2s, ebi2_lcd, blsp_uart1, qdss_cti, qdss_cti,
+ blsp_uart4, pll_bist, NA, qdss_stm1),
+ PINGROUP(23, sec_mi2s, ebi2_lcd, qdss_cti, qdss_cti, blsp_uart1,
+ blsp_uart4, NA, qdss_stm0, NA),
+ PINGROUP(24, adsp_ext, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(25, m_voc, adsp_ext, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(26, NA, NA, NA, native_char, NA, NA, NA, NA, NA),
+ PINGROUP(27, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(28, wmss_reset, native_char3, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(29, NA, NA, nav_pps, nav_dr, NA, native_char2, native_tsense,
+ NA, NA),
+ PINGROUP(30, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(31, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(32, NA, native_char1, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(33, NA, pa_indicator, qdss_traceclk, native_char0, NA, NA, NA,
+ NA, NA),
+ PINGROUP(34, qlink_en, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(35, qlink_req, pll_test, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(36, NA, NA, cri_trng, NA, NA, NA, NA, NA, NA),
+ PINGROUP(37, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(38, NA, NA, prng_rosc, NA, NA, NA, NA, NA, NA),
+ PINGROUP(39, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(40, NA, NA, cri_trng0, NA, NA, NA, NA, NA, NA),
+ PINGROUP(41, NA, NA, cri_trng1, NA, NA, NA, NA, NA, NA),
+ PINGROUP(42, nav_pps, NA, nav_dr, pll_ref, NA, NA, NA, NA, NA),
+ PINGROUP(43, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(44, coex_uart, NA, qdss_stm11, NA, NA, NA, NA, NA, NA),
+ PINGROUP(45, coex_uart, NA, qdss_stm10, ddr_pxi0, NA, NA, NA, NA, NA),
+ PINGROUP(46, m_voc, ddr_bist, ddr_pxi0, NA, NA, NA, NA, NA, NA),
+ PINGROUP(47, ddr_bist, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(48, ddr_bist, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(49, ddr_bist, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(50, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(51, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(52, blsp_spi2, blsp_spi1, blsp_spi3, blsp_spi4, NA, NA,
+ qdss_stm14, NA, NA),
+ PINGROUP(53, pci_e, NA, NA, qdss_stm13, NA, NA, NA, NA, NA),
+ PINGROUP(54, qdss_cti, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(55, qdss_cti, qdss_cti, tgu_ch0, NA, NA, NA, NA, NA, NA),
+ PINGROUP(56, pcie_clkreq, NA, qdss_stm9, NA, NA, NA, NA, NA, NA),
+ PINGROUP(57, NA, qdss_stm15, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(58, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(59, qdss_cti, m_voc, bimc_dte0, NA, NA, NA, NA, NA, NA),
+ PINGROUP(60, mgpi_clk, NA, qdss_stm12, qdss_tracectl, bimc_dte1, NA,
+ NA, NA, NA),
+ PINGROUP(61, qdss_cti, NA, m_voc, NA, NA, NA, NA, NA, NA),
+ PINGROUP(62, i2s_mclk, nav_pps, nav_dr, audio_ref, blsp_spi1,
+ blsp_spi2, blsp_spi3, blsp_spi4, ldo_update),
+ PINGROUP(63, blsp_uart2, NA, qdss_stm7, qdss4, atest_char, NA, NA, NA,
+ NA),
+ PINGROUP(64, blsp_uart2, NA, qdss_stm6, qdss5, atest_char3, NA, NA, NA,
+ NA),
+ PINGROUP(65, blsp_uart2, blsp_i2c2, NA, qdss_stm5, qdss6, atest_char2,
+ NA, NA, NA),
+ PINGROUP(66, blsp_uart2, blsp_i2c2, NA, qdss_stm4, qdss7, atest_char1,
+ NA, NA, NA),
+ PINGROUP(67, uim1_data, atest_char0, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(68, uim1_present, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(69, uim1_reset, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(70, uim1_clk, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(71, mgpi_clk, blsp_spi1, blsp_spi2, blsp_spi3, blsp_spi4,
+ dbg_out, NA, NA, NA),
+ PINGROUP(72, NA, blsp_spi1, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(73, NA, blsp_spi1, NA, gcc_plltest, NA, NA, NA, NA, NA),
+ PINGROUP(74, NA, blsp_spi1, NA, blsp_i2c1, gcc_plltest, NA, NA, NA, NA),
+ PINGROUP(75, NA, blsp_spi1, NA, blsp_i2c1, NA, NA, NA, NA, NA),
+ PINGROUP(76, blsp_i2c4, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(77, blsp_i2c4, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(78, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(79, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(80, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(81, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(82, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(83, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(84, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(85, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(86, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(87, NA, NA, usb2phy_ac, NA, NA, NA, NA, NA, NA),
+ PINGROUP(88, qdss_cti, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(89, qdss_cti, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(90, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(91, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(92, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(93, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(94, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(95, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(96, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(97, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(98, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(99, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ SDC_QDSD_PINGROUP(sdc1_clk, 0x9a000, 13, 6),
+ SDC_QDSD_PINGROUP(sdc1_cmd, 0x9a000, 11, 3),
+ SDC_QDSD_PINGROUP(sdc1_data, 0x9a000, 9, 0),
+};
+
+static const struct msm_pinctrl_soc_data sdxpoorwills_pinctrl = {
+ .pins = sdxpoorwills_pins,
+ .npins = ARRAY_SIZE(sdxpoorwills_pins),
+ .functions = sdxpoorwills_functions,
+ .nfunctions = ARRAY_SIZE(sdxpoorwills_functions),
+ .groups = sdxpoorwills_groups,
+ .ngroups = ARRAY_SIZE(sdxpoorwills_groups),
+ .ngpios = 100,
+};
+
+static int sdxpoorwills_pinctrl_probe(struct platform_device *pdev)
+{
+ return msm_pinctrl_probe(pdev, &sdxpoorwills_pinctrl);
+}
+
+static const struct of_device_id sdxpoorwills_pinctrl_of_match[] = {
+ { .compatible = "qcom,sdxpoorwills-pinctrl", },
+ { },
+};
+
+static struct platform_driver sdxpoorwills_pinctrl_driver = {
+ .driver = {
+ .name = "sdxpoorwills-pinctrl",
+ .owner = THIS_MODULE,
+ .of_match_table = sdxpoorwills_pinctrl_of_match,
+ },
+ .probe = sdxpoorwills_pinctrl_probe,
+ .remove = msm_pinctrl_remove,
+};
+
+static int __init sdxpoorwills_pinctrl_init(void)
+{
+ return platform_driver_register(&sdxpoorwills_pinctrl_driver);
+}
+arch_initcall(sdxpoorwills_pinctrl_init);
+
+static void __exit sdxpoorwills_pinctrl_exit(void)
+{
+ platform_driver_unregister(&sdxpoorwills_pinctrl_driver);
+}
+module_exit(sdxpoorwills_pinctrl_exit);
+
+MODULE_DESCRIPTION("QTI sdxpoorwills pinctrl driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, sdxpoorwills_pinctrl_of_match);
diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig
index 54a9cb2..3a6e214 100644
--- a/drivers/platform/msm/Kconfig
+++ b/drivers/platform/msm/Kconfig
@@ -144,4 +144,12 @@
If you choose to build it as a module, it will be called
msm_11ad_proxy.
+config SEEMP_CORE
+ tristate "SEEMP Core"
+ help
+ This option enables QTI Snapdragron Smart Protection to detect
+ anomalies in various activities. It records task activities in
+ a log and rates the actions according to whether a typical user would
+ use the tools.
+
endmenu
diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile
index 0bf87f4..cf24d7a 100644
--- a/drivers/platform/msm/Makefile
+++ b/drivers/platform/msm/Makefile
@@ -9,4 +9,5 @@
obj-$(CONFIG_QPNP_REVID) += qpnp-revid.o
obj-$(CONFIG_MSM_MHI_DEV) += mhi_dev/
obj-$(CONFIG_USB_BAM) += usb_bam.o
-obj-$(CONFIG_MSM_11AD) += msm_11ad/
\ No newline at end of file
+obj-$(CONFIG_MSM_11AD) += msm_11ad/
+obj-$(CONFIG_SEEMP_CORE) += seemp_core/
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 5fd6dcc..a7dbfc6 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -2268,6 +2268,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 */
@@ -4064,6 +4094,7 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p,
cdev_del(&ipa3_ctx->cdev);
device_destroy(ipa3_ctx->class, ipa3_ctx->dev_num);
unregister_chrdev_region(ipa3_ctx->dev_num, 1);
+ 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);
@@ -4604,6 +4635,13 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p,
goto fail_rx_pkt_wrapper_cache;
}
+ /* 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++) {
@@ -4744,6 +4782,8 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p,
fail_device_create:
unregister_chrdev_region(ipa3_ctx->dev_num, 1);
fail_alloc_chrdev_region:
+ ipa3_free_dma_task_for_gsi();
+fail_dma_task:
idr_destroy(&ipa3_ctx->ipa_idr);
kmem_cache_destroy(ipa3_ctx->rx_pkt_wrapper_cache);
fail_rx_pkt_wrapper_cache:
@@ -5488,13 +5528,6 @@ int ipa3_plat_drv_probe(struct platform_device *pdev_p,
return result;
}
- result = of_platform_populate(pdev_p->dev.of_node,
- pdrv_match, NULL, &pdev_p->dev);
- if (result) {
- IPAERR("failed to populate platform\n");
- return result;
- }
-
if (of_property_read_bool(pdev_p->dev.of_node, "qcom,arm-smmu")) {
if (of_property_read_bool(pdev_p->dev.of_node,
"qcom,smmu-s1-bypass"))
@@ -5540,6 +5573,13 @@ int ipa3_plat_drv_probe(struct platform_device *pdev_p,
}
}
+ result = of_platform_populate(pdev_p->dev.of_node,
+ pdrv_match, NULL, &pdev_p->dev);
+ if (result) {
+ IPAERR("failed to populate platform\n");
+ return result;
+ }
+
return result;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 8261a26..18ee4ee 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -997,6 +997,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
@@ -1206,6 +1211,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;
};
struct ipa3_plat_drv_res {
@@ -1982,4 +1988,6 @@ struct device *ipa3_get_pdev(void);
void ipa3_enable_dcd(void);
void ipa3_disable_prefetch(enum ipa_client_type client);
int ipa3_alloc_common_event_ring(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_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index bc9f693..4208a49 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -1149,18 +1149,18 @@ static struct msm_bus_vectors ipa_nominal_perf_vectors_v3_0[] = {
static struct msm_bus_paths ipa_usecases_v3_0[] = {
{
- ARRAY_SIZE(ipa_init_vectors_v3_0),
- ipa_init_vectors_v3_0,
+ .num_paths = ARRAY_SIZE(ipa_init_vectors_v3_0),
+ .vectors = ipa_init_vectors_v3_0,
},
{
- ARRAY_SIZE(ipa_nominal_perf_vectors_v3_0),
- ipa_nominal_perf_vectors_v3_0,
+ .num_paths = ARRAY_SIZE(ipa_nominal_perf_vectors_v3_0),
+ .vectors = ipa_nominal_perf_vectors_v3_0,
},
};
static struct msm_bus_scale_pdata ipa_bus_client_pdata_v3_0 = {
- ipa_usecases_v3_0,
- ARRAY_SIZE(ipa_usecases_v3_0),
+ .usecase = ipa_usecases_v3_0,
+ .num_usecases = ARRAY_SIZE(ipa_usecases_v3_0),
.name = "ipa",
};
@@ -4163,6 +4163,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);
}
@@ -4215,6 +4216,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
*
@@ -4223,41 +4269,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 0688562..cb25b09 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -2357,32 +2357,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_LOW("Enter...\n");
+ IPAWANDBG("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;
}
/**
@@ -2399,10 +2408,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/seemp_core/Makefile b/drivers/platform/msm/seemp_core/Makefile
new file mode 100644
index 0000000..a26db43
--- /dev/null
+++ b/drivers/platform/msm/seemp_core/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Iinclude/linux
+obj-$(CONFIG_SEEMP_CORE) += seemp_core.o
+seemp_core-objs:= seemp_logk.o seemp_ringbuf.o seemp_event_encoder.o
\ No newline at end of file
diff --git a/drivers/platform/msm/seemp_core/seemp_event_encoder.c b/drivers/platform/msm/seemp_core/seemp_event_encoder.c
new file mode 100644
index 0000000..6d9aa81
--- /dev/null
+++ b/drivers/platform/msm/seemp_core/seemp_event_encoder.c
@@ -0,0 +1,179 @@
+/*
+ * 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
+ * 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/seemp_param_id.h>
+#include "seemp_logk.h"
+#include "seemp_event_encoder.h"
+
+static char *scan_id(char *s);
+static void encode_seemp_section(char *section_start, char *section_eq,
+ char *section_end, bool param, bool numeric,
+ int id, __s32 numeric_value);
+
+static void check_param_range(char *section_eq, bool param,
+ bool *numeric, int val_len, __s32 *numeric_value)
+{
+ long long_value = 0;
+
+ if (param && *numeric) {
+ /*check if 2 bytes & in[-99999,999999]*/
+ *numeric = (val_len >= 2) && (val_len <= 6);
+ if (*numeric) {
+ if (kstrtol(section_eq + 1, 10, &long_value)
+ != 0) {
+ *numeric = false;
+ } else {
+ *numeric_value = (__s32)long_value;
+ /* We are checking whether the value
+ * lies within 16bits
+ */
+ *numeric = (long_value >= -32768) &&
+ (long_value <= 32767);
+ }
+ }
+ }
+}
+
+void encode_seemp_params(struct seemp_logk_blk *blk)
+{
+ struct seemp_logk_blk tmp;
+ char *s = 0;
+ char *msg_section_start = 0;
+ char *msg_section_eq = 0;
+ char *msg_s = 0;
+
+ memcpy(tmp.payload.msg, blk->payload.msg, BLK_MAX_MSG_SZ);
+ s = tmp.payload.msg + 1;
+ tmp.payload.msg[BLK_MAX_MSG_SZ - 1] = 0; /* zero-terminate */
+
+ while (true) {
+ char *section_start = s;
+ char *section_eq = scan_id(s);
+ bool param = (section_eq - section_start >= 2) &&
+ (*section_eq == '=') && (section_eq[1] != ' ');
+ bool numeric = false;
+ int id = -1;
+ __s32 numeric_value = 0;
+ int id_len;
+ int val_len;
+ char ch;
+
+ if (param) {
+ id = param_id_index(section_start, section_eq);
+
+ if (id < 0)
+ param = false;
+ }
+
+ if (!param) {
+ s = section_eq;
+ while ((*s != 0) && (*s != ','))
+ s++;
+ } else {
+ s = section_eq + 1; /* equal sign */
+ numeric = (*s == '-') || ((*s >= '0') && (*s <= '9'));
+
+ if (numeric)
+ s++; /* first char of number */
+
+ while ((*s != 0) && (*s != ',')) {
+ if (*s == '=')
+ param = false;
+ else if (!((*s >= '0') && (*s <= '9')))
+ numeric = false;
+
+ s++;
+ }
+
+ if (param) {
+ id_len = section_eq - section_start;
+ val_len = s - (section_eq + 1);
+ param = (id_len >= 2) && (id_len <= 31)
+ && (val_len <= 31);
+ ch = *s;
+ *s = 0;
+
+ check_param_range(section_eq, param,
+ &numeric, val_len, &numeric_value);
+ *s = ch;
+ }
+ }
+
+ msg_section_start = blk->payload.msg + (section_start -
+ tmp.payload.msg);
+ msg_section_eq = blk->payload.msg + (section_eq -
+ tmp.payload.msg);
+ msg_s = blk->payload.msg + (s - tmp.payload.msg);
+ encode_seemp_section(msg_section_start, msg_section_eq,
+ msg_s, param, numeric, id, numeric_value);
+
+ if (*s == 0)
+ break;
+
+ s++;
+ }
+
+ blk->len = s - blk->payload.msg;
+}
+
+static char *scan_id(char *s)
+{
+ while ((*s == '_') ||
+ ((*s >= 'A') && (*s <= 'Z')) ||
+ ((*s >= 'a') && (*s <= 'z'))) {
+ s++;
+ }
+
+ return s;
+}
+
+static void encode_seemp_section(char *section_start, char *section_eq,
+ char *section_end, bool param, bool numeric,
+ int id, __s32 numeric_value) {
+ param = param && (section_eq + 1 < section_end);
+
+ if (!param) {
+ /* Encode skip section */
+ int skip_len = section_end - section_start;
+ char skip_len_hi = skip_len & 0xE0;
+ char skip_len_lo = skip_len & 0x1F;
+
+ if (skip_len < 32) {
+ section_start[-1] = 0xC0 | skip_len_lo;
+ /* [1:1:0:0 0000] */
+ } else {
+ section_start[-1] = 0xE0 | skip_len_lo;
+ /* [1:1:1:0 0000] */
+
+ if (skip_len_hi & 0x20)
+ section_start[0] |= 0x80;
+
+ if (skip_len_hi & 0x40)
+ section_start[1] |= 0x80;
+
+ if (skip_len_hi & 0x80)
+ section_start[2] |= 0x80;
+ }
+ } else {
+ /* Encode ID=VALUE section */
+ char id_len = section_eq - section_start;
+ char value_len = section_end - (section_eq + 1);
+
+ section_start[-1] = 0x00 | id_len;
+ *(__s16 *)section_start = id;
+ section_eq[0] = (!numeric ? 0x80 : 0x00) | value_len;
+
+ if (numeric)
+ *(__s16 *)(section_eq + 1) = numeric_value;
+ }
+}
diff --git a/drivers/platform/msm/seemp_core/seemp_event_encoder.h b/drivers/platform/msm/seemp_core/seemp_event_encoder.h
new file mode 100644
index 0000000..7cb7274
--- /dev/null
+++ b/drivers/platform/msm/seemp_core/seemp_event_encoder.h
@@ -0,0 +1,21 @@
+/*
+ * 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
+ * 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 __SEEMP_EVENT_ENCODER_H__
+#define __SEEMP_EVENT_ENCODER_H__
+
+#include "seemp_logk.h"
+
+void encode_seemp_params(struct seemp_logk_blk *blk);
+
+#endif /* __SEEMP_EVENT_ENCODER_H__ */
diff --git a/drivers/platform/msm/seemp_core/seemp_logk.c b/drivers/platform/msm/seemp_core/seemp_logk.c
new file mode 100644
index 0000000..ce073ed
--- /dev/null
+++ b/drivers/platform/msm/seemp_core/seemp_logk.c
@@ -0,0 +1,688 @@
+/*
+ * 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
+ * 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) "seemp: %s: " fmt, __func__
+
+#include "seemp_logk.h"
+#include "seemp_ringbuf.h"
+
+#ifndef VM_RESERVED
+#define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP)
+#endif
+
+#define MASK_BUFFER_SIZE 256
+#define FOUR_MB 4
+#define YEAR_BASE 1900
+
+static struct seemp_logk_dev *slogk_dev;
+
+static unsigned int ring_sz = FOUR_MB;
+
+/*
+ * default is besteffort, apps do not get blocked
+ */
+static unsigned int block_apps;
+
+
+/*
+ * When this flag is turned on,
+ * kmalloc should be used for ring buf allocation
+ * otherwise it is vmalloc.
+ * default is to use vmalloc
+ * kmalloc has a limit of 4MB
+ */
+unsigned int kmalloc_flag;
+
+static struct class *cl;
+
+static rwlock_t filter_lock;
+static struct seemp_source_mask *pmask;
+static unsigned int num_sources;
+
+static long seemp_logk_reserve_rdblks(
+ struct seemp_logk_dev *sdev, unsigned long arg);
+static long seemp_logk_set_mask(unsigned long arg);
+static long seemp_logk_set_mapping(unsigned long arg);
+static long seemp_logk_check_filter(unsigned long arg);
+
+void* (*seemp_logk_kernel_begin)(char **buf);
+
+void (*seemp_logk_kernel_end)(void *blck);
+
+/*
+ * the last param is the permission bits *
+ * kernel logging is done in three steps:
+ * (1) fetch a block, fill everything except payload.
+ * (2) return payload pointer to the caller.
+ * (3) caller fills its data directly into the payload area.
+ * (4) caller invoked finish_record(), to finish writing.
+ */
+void *seemp_logk_kernel_start_record(char **buf)
+{
+ struct seemp_logk_blk *blk;
+ struct timespec now;
+ struct tm ts;
+ int idx;
+ int ret;
+
+ DEFINE_WAIT(write_wait);
+
+ ret = 0;
+ idx = 0;
+ now = current_kernel_time();
+ blk = ringbuf_fetch_wr_block(slogk_dev);
+ if (!blk) {
+ /*
+ * there is no blk to write
+ * if block_apps == 0; quietly return
+ */
+ if (!block_apps) {
+ *buf = NULL;
+ return NULL;
+ }
+ /*else wait for the blks to be available*/
+ while (1) {
+ mutex_lock(&slogk_dev->lock);
+ prepare_to_wait(&slogk_dev->writers_wq,
+ &write_wait, TASK_INTERRUPTIBLE);
+ ret = (slogk_dev->num_write_avail_blks <= 0);
+ if (!ret) {
+ /* don't have to wait*/
+ break;
+ }
+ mutex_unlock(&slogk_dev->lock);
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ break;
+ }
+ schedule();
+ }
+
+ finish_wait(&slogk_dev->writers_wq, &write_wait);
+ if (ret)
+ return NULL;
+
+ idx = slogk_dev->write_idx;
+ slogk_dev->write_idx =
+ (slogk_dev->write_idx + 1) % slogk_dev->num_tot_blks;
+ slogk_dev->num_write_avail_blks--;
+ slogk_dev->num_write_in_prog_blks++;
+ slogk_dev->num_writers++;
+
+ blk = &slogk_dev->ring[idx];
+ /*mark block invalid*/
+ blk->status = 0x0;
+ mutex_unlock(&slogk_dev->lock);
+ }
+
+ blk->version = OBSERVER_VERSION;
+ blk->pid = current->tgid;
+ blk->tid = current->pid;
+ blk->uid = (current_uid()).val;
+ blk->sec = now.tv_sec;
+ blk->nsec = now.tv_nsec;
+ strlcpy(blk->appname, current->comm, TASK_COMM_LEN);
+ time_to_tm(now.tv_sec, 0, &ts);
+ ts.tm_year += YEAR_BASE;
+ ts.tm_mon += 1;
+
+ snprintf(blk->ts, TS_SIZE, "%04ld-%02d-%02d %02d:%02d:%02d",
+ ts.tm_year, ts.tm_mon, ts.tm_mday,
+ ts.tm_hour, ts.tm_min, ts.tm_sec);
+
+ *buf = blk->payload.msg;
+
+ return blk;
+}
+
+void seemp_logk_kernel_end_record(void *blck)
+{
+ struct seemp_logk_blk *blk = (struct seemp_logk_blk *)blck;
+
+ if (blk) {
+ /*update status at the very end*/
+ blk->status |= 0x1;
+ blk->uid = (current_uid()).val;
+
+ ringbuf_finish_writer(slogk_dev, blk);
+ }
+}
+
+static int seemp_logk_usr_record(const char __user *buf, size_t count)
+{
+ struct seemp_logk_blk *blk;
+ struct seemp_logk_blk usr_blk;
+ struct seemp_logk_blk *local_blk;
+ struct timespec now;
+ struct tm ts;
+ int idx, ret;
+
+ DEFINE_WAIT(write_wait);
+
+ if (buf) {
+ local_blk = (struct seemp_logk_blk *)buf;
+ if (copy_from_user(&usr_blk.pid, &local_blk->pid,
+ sizeof(usr_blk.pid)) != 0)
+ return -EFAULT;
+ if (copy_from_user(&usr_blk.tid, &local_blk->tid,
+ sizeof(usr_blk.tid)) != 0)
+ return -EFAULT;
+ if (copy_from_user(&usr_blk.uid, &local_blk->uid,
+ sizeof(usr_blk.uid)) != 0)
+ return -EFAULT;
+ if (copy_from_user(&usr_blk.len, &local_blk->len,
+ sizeof(usr_blk.len)) != 0)
+ return -EFAULT;
+ if (copy_from_user(&usr_blk.payload, &local_blk->payload,
+ sizeof(struct blk_payload)) != 0)
+ return -EFAULT;
+ } else {
+ return -EFAULT;
+ }
+ idx = ret = 0;
+ now = current_kernel_time();
+ blk = ringbuf_fetch_wr_block(slogk_dev);
+ if (!blk) {
+ if (!block_apps)
+ return 0;
+ while (1) {
+ mutex_lock(&slogk_dev->lock);
+ prepare_to_wait(&slogk_dev->writers_wq,
+ &write_wait,
+ TASK_INTERRUPTIBLE);
+ ret = (slogk_dev->num_write_avail_blks <= 0);
+ if (!ret)
+ break;
+ mutex_unlock(&slogk_dev->lock);
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ break;
+ }
+ schedule();
+ }
+ finish_wait(&slogk_dev->writers_wq, &write_wait);
+ if (ret)
+ return -EINTR;
+
+ idx = slogk_dev->write_idx;
+ slogk_dev->write_idx =
+ (slogk_dev->write_idx + 1) % slogk_dev->num_tot_blks;
+ slogk_dev->num_write_avail_blks--;
+ slogk_dev->num_write_in_prog_blks++;
+ slogk_dev->num_writers++;
+ blk = &slogk_dev->ring[idx];
+ /*mark block invalid*/
+ blk->status = 0x0;
+ mutex_unlock(&slogk_dev->lock);
+ }
+ if (usr_blk.len > sizeof(struct blk_payload)-1)
+ usr_blk.len = sizeof(struct blk_payload)-1;
+
+ memcpy(&blk->payload, &usr_blk.payload, sizeof(struct blk_payload));
+ blk->pid = usr_blk.pid;
+ blk->uid = usr_blk.uid;
+ blk->tid = usr_blk.tid;
+ blk->sec = now.tv_sec;
+ blk->nsec = now.tv_nsec;
+ time_to_tm(now.tv_sec, 0, &ts);
+ ts.tm_year += YEAR_BASE;
+ ts.tm_mon += 1;
+ snprintf(blk->ts, TS_SIZE, "%02ld-%02d-%02d %02d:%02d:%02d",
+ ts.tm_year, ts.tm_mon, ts.tm_mday,
+ ts.tm_hour, ts.tm_min, ts.tm_sec);
+ strlcpy(blk->appname, current->comm, TASK_COMM_LEN);
+ blk->status |= 0x1;
+ ringbuf_finish_writer(slogk_dev, blk);
+ return ret;
+}
+
+static void seemp_logk_attach(void)
+{
+ seemp_logk_kernel_end = seemp_logk_kernel_end_record;
+ seemp_logk_kernel_begin = seemp_logk_kernel_start_record;
+}
+
+static void seemp_logk_detach(void)
+{
+ seemp_logk_kernel_begin = NULL;
+ seemp_logk_kernel_end = NULL;
+}
+
+static ssize_t
+seemp_logk_write(struct file *file, const char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ return seemp_logk_usr_record(buf, count);
+}
+
+static int
+seemp_logk_open(struct inode *inode, struct file *filp)
+{
+ int ret;
+
+ /*disallow seeks on this file*/
+ ret = nonseekable_open(inode, filp);
+ if (ret) {
+ pr_err("ret= %d\n", ret);
+ return ret;
+ }
+
+ slogk_dev->minor = iminor(inode);
+ filp->private_data = slogk_dev;
+
+ return 0;
+}
+
+static bool seemp_logk_get_bit_from_vector(__u8 *pVec, __u32 index)
+{
+ unsigned int byte_num = index/8;
+ unsigned int bit_num = index%8;
+ unsigned char byte;
+
+ if (DIV_ROUND_UP(index, 8) > MASK_BUFFER_SIZE)
+ return false;
+
+ byte = pVec[byte_num];
+
+ return !(byte & (1 << bit_num));
+}
+
+static long seemp_logk_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct seemp_logk_dev *sdev;
+ int ret;
+
+ sdev = (struct seemp_logk_dev *) filp->private_data;
+
+ if (cmd == SEEMP_CMD_RESERVE_RDBLKS) {
+ return seemp_logk_reserve_rdblks(sdev, arg);
+ } else if (cmd == SEEMP_CMD_RELEASE_RDBLKS) {
+ mutex_lock(&sdev->lock);
+ sdev->read_idx = (sdev->read_idx + sdev->num_read_in_prog_blks)
+ % sdev->num_tot_blks;
+ sdev->num_write_avail_blks += sdev->num_read_in_prog_blks;
+ ret = sdev->num_read_in_prog_blks;
+ sdev->num_read_in_prog_blks = 0;
+ /*wake up any waiting writers*/
+ mutex_unlock(&sdev->lock);
+ if (ret && block_apps)
+ wake_up_interruptible(&sdev->writers_wq);
+ } else if (cmd == SEEMP_CMD_GET_RINGSZ) {
+ if (copy_to_user((unsigned int *)arg, &sdev->ring_sz,
+ sizeof(unsigned int)))
+ return -EFAULT;
+ } else if (cmd == SEEMP_CMD_GET_BLKSZ) {
+ if (copy_to_user((unsigned int *)arg, &sdev->blk_sz,
+ sizeof(unsigned int)))
+ return -EFAULT;
+ } else if (cmd == SEEMP_CMD_SET_MASK) {
+ return seemp_logk_set_mask(arg);
+ } else if (cmd == SEEMP_CMD_SET_MAPPING) {
+ return seemp_logk_set_mapping(arg);
+ } else if (cmd == SEEMP_CMD_CHECK_FILTER) {
+ return seemp_logk_check_filter(arg);
+ }
+ pr_err("Invalid Request %X\n", cmd);
+ return -ENOIOCTLCMD;
+}
+
+static long seemp_logk_reserve_rdblks(
+ struct seemp_logk_dev *sdev, unsigned long arg)
+{
+ int ret;
+ struct read_range rrange;
+
+ DEFINE_WAIT(read_wait);
+
+ mutex_lock(&sdev->lock);
+ if (sdev->num_writers > 0 || sdev->num_read_avail_blks <= 0) {
+ ret = -EPERM;
+ pr_debug("(reserve): blocking, cannot read.\n");
+ pr_debug("num_writers=%d num_read_avail_blks=%d\n",
+ sdev->num_writers,
+ sdev->num_read_avail_blks);
+ mutex_unlock(&sdev->lock);
+ /*
+ * unlock the device
+ * wait on a wait queue
+ * after wait, grab the dev lock again
+ */
+ while (1) {
+ mutex_lock(&sdev->lock);
+ prepare_to_wait(&sdev->readers_wq, &read_wait,
+ TASK_INTERRUPTIBLE);
+ ret = (sdev->num_writers > 0 ||
+ sdev->num_read_avail_blks <= 0);
+ if (!ret) {
+ /*don't have to wait*/
+ break;
+ }
+ mutex_unlock(&sdev->lock);
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ break;
+ }
+ schedule();
+ }
+
+ finish_wait(&sdev->readers_wq, &read_wait);
+ if (ret)
+ return -EINTR;
+ }
+
+ /*sdev->lock is held at this point*/
+ sdev->num_read_in_prog_blks = sdev->num_read_avail_blks;
+ sdev->num_read_avail_blks = 0;
+ rrange.start_idx = sdev->read_idx;
+ rrange.num = sdev->num_read_in_prog_blks;
+ mutex_unlock(&sdev->lock);
+
+ if (copy_to_user((unsigned int *)arg, &rrange,
+ sizeof(struct read_range)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static long seemp_logk_set_mask(unsigned long arg)
+{
+ __u8 buffer[256];
+ int i;
+ unsigned int num_elements;
+
+ if (copy_from_user(&num_elements,
+ (unsigned int __user *) arg, sizeof(unsigned int)))
+ return -EFAULT;
+
+ read_lock(&filter_lock);
+ if (num_sources == 0) {
+ read_unlock(&filter_lock);
+ return -EINVAL;
+ }
+
+ if (num_elements == 0 ||
+ DIV_ROUND_UP(num_sources, 8) > MASK_BUFFER_SIZE) {
+ read_unlock(&filter_lock);
+ return -EINVAL;
+ }
+
+ if (copy_from_user(buffer,
+ (__u8 *)arg, DIV_ROUND_UP(num_sources, 8))) {
+ read_unlock(&filter_lock);
+ return -EFAULT;
+ }
+
+ read_unlock(&filter_lock);
+ write_lock(&filter_lock);
+ if (num_elements != num_sources) {
+ write_unlock(&filter_lock);
+ return -EPERM;
+ }
+
+ for (i = 0; i < num_sources; i++) {
+ pmask[i].isOn =
+ seemp_logk_get_bit_from_vector(
+ (__u8 *)buffer, i);
+ }
+ write_unlock(&filter_lock);
+ return 0;
+}
+
+static long seemp_logk_set_mapping(unsigned long arg)
+{
+ __u32 num_elements;
+ __u32 *pbuffer;
+ int i;
+ struct seemp_source_mask *pnewmask;
+
+ if (copy_from_user(&num_elements,
+ (__u32 __user *)arg, sizeof(__u32)))
+ return -EFAULT;
+
+ if ((num_elements == 0) || (num_elements >
+ (UINT_MAX / sizeof(struct seemp_source_mask))))
+ return -EFAULT;
+
+ write_lock(&filter_lock);
+ if (pmask != NULL) {
+ /*
+ * Mask is getting set again.
+ * seemp_core was probably restarted.
+ */
+ struct seemp_source_mask *ptempmask;
+
+ num_sources = 0;
+ ptempmask = pmask;
+ pmask = NULL;
+ kfree(ptempmask);
+ }
+ write_unlock(&filter_lock);
+ pbuffer = kmalloc(sizeof(struct seemp_source_mask)
+ * num_elements, GFP_KERNEL);
+ if (pbuffer == NULL)
+ return -ENOMEM;
+
+ /*
+ * Use our new table as scratch space for now.
+ * We copy an ordered list of hash values into our buffer.
+ */
+ if (copy_from_user(pbuffer, &((__u32 __user *)arg)[1],
+ num_elements*sizeof(unsigned int))) {
+ kfree(pbuffer);
+ return -EFAULT;
+ }
+ /*
+ * We arrange the user data into a more usable form.
+ * This is done in-place.
+ */
+ pnewmask = (struct seemp_source_mask *) pbuffer;
+ for (i = num_elements - 1; i >= 0; i--) {
+ pnewmask[i].hash = pbuffer[i];
+ /* Observer is off by default*/
+ pnewmask[i].isOn = 0;
+ }
+ write_lock(&filter_lock);
+ pmask = pnewmask;
+ num_sources = num_elements;
+ write_unlock(&filter_lock);
+ return 0;
+}
+
+static long seemp_logk_check_filter(unsigned long arg)
+{
+ int i;
+ unsigned int hash = (unsigned int) arg;
+
+ /*
+ * This lock may be a bit long.
+ * If it is a problem, it can be fixed.
+ */
+ read_lock(&filter_lock);
+ for (i = 0; i < num_sources; i++) {
+ if (hash == pmask[i].hash) {
+ int result = pmask[i].isOn;
+
+ read_unlock(&filter_lock);
+ return result;
+ }
+ }
+ read_unlock(&filter_lock);
+ return 0;
+}
+
+static int seemp_logk_mmap(struct file *filp,
+ struct vm_area_struct *vma)
+{
+ int ret;
+ char *vptr;
+ unsigned long length, pfn;
+ unsigned long start = vma->vm_start;
+
+ length = vma->vm_end - vma->vm_start;
+
+ if (length > (unsigned long) slogk_dev->ring_sz) {
+ pr_err("len check failed\n");
+ return -EIO;
+ }
+
+ vma->vm_flags |= VM_RESERVED | VM_SHARED;
+ vptr = (char *) slogk_dev->ring;
+ ret = 0;
+
+ if (kmalloc_flag) {
+ ret = remap_pfn_range(vma,
+ start,
+ virt_to_phys((void *)
+ ((unsigned long)slogk_dev->ring)) >> PAGE_SHIFT,
+ length,
+ vma->vm_page_prot);
+ if (ret != 0) {
+ pr_err("remap_pfn_range() fails with ret = %d\n",
+ ret);
+ return -EAGAIN;
+ }
+ } else {
+ while (length > 0) {
+ pfn = vmalloc_to_pfn(vptr);
+
+ ret = remap_pfn_range(vma, start, pfn, PAGE_SIZE,
+ vma->vm_page_prot);
+ if (ret < 0) {
+ pr_err("remap_pfn_range() fails with ret = %d\n",
+ ret);
+ return ret;
+ }
+ start += PAGE_SIZE;
+ vptr += PAGE_SIZE;
+ length -= PAGE_SIZE;
+ }
+ }
+
+ return 0;
+}
+
+static const struct file_operations seemp_logk_fops = {
+ .write = seemp_logk_write,
+ .open = seemp_logk_open,
+ .unlocked_ioctl = seemp_logk_ioctl,
+ .compat_ioctl = seemp_logk_ioctl,
+ .mmap = seemp_logk_mmap,
+};
+
+__init int seemp_logk_init(void)
+{
+ int ret;
+ int devno = 0;
+
+ num_sources = 0;
+ kmalloc_flag = 0;
+ block_apps = 0;
+ pmask = NULL;
+
+ if (kmalloc_flag && ring_sz > FOUR_MB) {
+ pr_err("kmalloc cannot allocate > 4MB\n");
+ return -ENOMEM;
+ }
+
+ ring_sz = ring_sz * SZ_1M;
+ if (ring_sz <= 0) {
+ pr_err("Too small a ring_sz=%d\n", ring_sz);
+ return -EINVAL;
+ }
+
+ slogk_dev = kmalloc(sizeof(*slogk_dev), GFP_KERNEL);
+ if (slogk_dev == NULL)
+ return -ENOMEM;
+
+ slogk_dev->ring_sz = ring_sz;
+ slogk_dev->blk_sz = sizeof(struct seemp_logk_blk);
+ /*initialize ping-pong buffers*/
+ ret = ringbuf_init(slogk_dev);
+ if (ret < 0) {
+ pr_err("Init Failed, ret = %d\n", ret);
+ goto pingpong_fail;
+ }
+
+ ret = alloc_chrdev_region(&devno, 0, seemp_LOGK_NUM_DEVS,
+ seemp_LOGK_DEV_NAME);
+ if (ret < 0) {
+ pr_err("alloc_chrdev_region failed with ret = %d\n",
+ ret);
+ goto register_fail;
+ }
+
+ slogk_dev->major = MAJOR(devno);
+
+ pr_debug("logk: major# = %d\n", slogk_dev->major);
+
+ cl = class_create(THIS_MODULE, seemp_LOGK_DEV_NAME);
+ if (cl == NULL) {
+ pr_err("class create failed");
+ goto cdev_fail;
+ }
+ if (device_create(cl, NULL, devno, NULL,
+ seemp_LOGK_DEV_NAME) == NULL) {
+ pr_err("device create failed");
+ goto class_destroy_fail;
+ }
+ cdev_init(&(slogk_dev->cdev), &seemp_logk_fops);
+
+ slogk_dev->cdev.owner = THIS_MODULE;
+ ret = cdev_add(&(slogk_dev->cdev), MKDEV(slogk_dev->major, 0), 1);
+ if (ret) {
+ pr_err("cdev_add failed with ret = %d", ret);
+ goto class_destroy_fail;
+ }
+
+ seemp_logk_attach();
+ mutex_init(&slogk_dev->lock);
+ init_waitqueue_head(&slogk_dev->readers_wq);
+ init_waitqueue_head(&slogk_dev->writers_wq);
+ rwlock_init(&filter_lock);
+ return 0;
+class_destroy_fail:
+ class_destroy(cl);
+cdev_fail:
+ unregister_chrdev_region(devno, seemp_LOGK_NUM_DEVS);
+register_fail:
+ ringbuf_cleanup(slogk_dev);
+pingpong_fail:
+ kfree(slogk_dev);
+ return -EPERM;
+}
+
+__exit void seemp_logk_cleanup(void)
+{
+ dev_t devno = MKDEV(slogk_dev->major, slogk_dev->minor);
+
+ seemp_logk_detach();
+
+ cdev_del(&slogk_dev->cdev);
+
+ unregister_chrdev_region(devno, seemp_LOGK_NUM_DEVS);
+ ringbuf_cleanup(slogk_dev);
+ kfree(slogk_dev);
+
+ if (pmask != NULL) {
+ kfree(pmask);
+ pmask = NULL;
+ }
+}
+
+module_init(seemp_logk_init);
+module_exit(seemp_logk_cleanup);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("seemp Observer");
+
diff --git a/drivers/platform/msm/seemp_core/seemp_logk.h b/drivers/platform/msm/seemp_core/seemp_logk.h
new file mode 100644
index 0000000..1a41d4c
--- /dev/null
+++ b/drivers/platform/msm/seemp_core/seemp_logk.h
@@ -0,0 +1,161 @@
+/*
+ * 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
+ * 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 __SEEMP_LOGK_H__
+#define __SEEMP_LOGK_H__
+
+#define OBSERVER_VERSION 0x01
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioctl.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/vmalloc.h>
+#include <asm/ioctls.h>
+
+#define seemp_LOGK_NUM_DEVS 1
+#define seemp_LOGK_DEV_NAME "seemplog"
+
+/*
+ * The logcat driver on Android uses four 256k ring buffers
+ * here, we use two ring buffers of the same size.
+ * we think this is reasonable
+ */
+#define FULL_BUF_SIZE (64 * 1024 * 1024)
+#define HALF_BUF_SIZE (32 * 1024 * 1024)
+#define FULL_BLOCKS (8 * 1024)
+#define HALF_BLOCKS (4 * 1024)
+
+#define READER_NOT_READY 0
+#define READER_READY 1
+
+#define MAGIC 'z'
+
+#define SEEMP_CMD_RESERVE_RDBLKS _IOR(MAGIC, 1, int)
+#define SEEMP_CMD_RELEASE_RDBLKS _IO(MAGIC, 2)
+#define SEEMP_CMD_GET_RINGSZ _IOR(MAGIC, 3, int)
+#define SEEMP_CMD_GET_BLKSZ _IOR(MAGIC, 4, int)
+#define SEEMP_CMD_SET_MASK _IO(MAGIC, 5)
+#define SEEMP_CMD_SET_MAPPING _IO(MAGIC, 6)
+#define SEEMP_CMD_CHECK_FILTER _IOR(MAGIC, 7, int)
+
+struct read_range {
+ int start_idx;
+ int num;
+};
+
+struct seemp_logk_dev {
+ unsigned int major;
+ unsigned int minor;
+
+ struct cdev cdev;
+ struct class *cls;
+ /*the full buffer*/
+ struct seemp_logk_blk *ring;
+ /*an array of blks*/
+ unsigned int ring_sz;
+ unsigned int blk_sz;
+
+ int num_tot_blks;
+
+ int num_write_avail_blks;
+ int num_write_in_prog_blks;
+
+ int num_read_avail_blks;
+ int num_read_in_prog_blks;
+
+ int num_writers;
+
+ /*
+ * there is always one reader
+ * which is the observer daemon
+ * therefore there is no necessity
+ * for num_readers variable
+ */
+
+ /*
+ * read_idx and write_idx loop through from zero to ring_sz,
+ * and then back to zero in a circle, as they advance
+ * based on the reader's and writers' accesses
+ */
+ int read_idx;
+
+ int write_idx;
+
+ /*
+ * wait queues
+ * readers_wq: implement wait for readers
+ * writers_wq: implement wait for writers
+ *
+ * whether writers are blocked or not is driven by the policy:
+ * case 1: (best_effort_logging == 1)
+ * writers are not blocked, and
+ * when there is no mem in the ring to store logs,
+ * the logs are simply dropped.
+ * case 2: (best_effort_logging == 0)
+ * when there is no mem in the ring to store logs,
+ * the process gets blocked until there is space.
+ */
+ wait_queue_head_t readers_wq;
+ wait_queue_head_t writers_wq;
+
+ /*
+ * protects everything in the device
+ * including ring buffer and all the num_ variables
+ * spinlock_t lock;
+ */
+ struct mutex lock;
+};
+
+#define BLK_SIZE 256
+#define BLK_HDR_SIZE 64
+#define TS_SIZE 20
+#define BLK_MAX_MSG_SZ (BLK_SIZE - BLK_HDR_SIZE)
+
+struct blk_payload {
+ __u32 api_id; /* event API id */
+ char msg[BLK_MAX_MSG_SZ]; /* event parameters */
+} __packed;
+
+struct seemp_logk_blk {
+ __u8 status; /* bits: 0->valid/invalid; 1-7: unused as of now! */
+ __u16 len; /* length of the payload */
+ __u8 version; /* version number */
+ __s32 pid; /* generating process's pid */
+ __s32 uid; /* generating process's uid - app specific */
+ __s32 tid; /* generating process's tid */
+ __s32 sec; /* seconds since Epoch */
+ __s32 nsec; /* nanoseconds */
+ char ts[TS_SIZE]; /* Time Stamp */
+ char appname[TASK_COMM_LEN];
+ struct blk_payload payload;
+} __packed;
+
+
+extern unsigned int kmalloc_flag;
+
+struct seemp_source_mask {
+ __u32 hash;
+ bool isOn;
+};
+#endif
diff --git a/drivers/platform/msm/seemp_core/seemp_ringbuf.c b/drivers/platform/msm/seemp_core/seemp_ringbuf.c
new file mode 100644
index 0000000..4558051
--- /dev/null
+++ b/drivers/platform/msm/seemp_core/seemp_ringbuf.c
@@ -0,0 +1,164 @@
+/*
+ * 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
+ * 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) "seemp: %s: " fmt, __func__
+
+#include "seemp_logk.h"
+#include "seemp_ringbuf.h"
+#include "seemp_event_encoder.h"
+
+/*initial function no need to hold ring_lock*/
+int ringbuf_init(struct seemp_logk_dev *sdev)
+{
+ char *buf;
+ unsigned long virt_addr;
+
+ if (kmalloc_flag) {
+ sdev->ring = kmalloc(sdev->ring_sz, GFP_KERNEL);
+ if (sdev->ring == NULL) {
+ pr_err("kmalloc failed, ring_sz= %d\n", sdev->ring_sz);
+ return -ENOMEM;
+ }
+
+ buf = (char *)sdev->ring;
+
+ /*reserve kmalloc memory as pages to make them remapable*/
+ for (virt_addr = (unsigned long)buf;
+ virt_addr < (unsigned long)buf + sdev->ring_sz;
+ virt_addr += PAGE_SIZE) {
+ SetPageReserved(virt_to_page((virt_addr)));
+ }
+ } else {
+ sdev->ring = vmalloc(sdev->ring_sz);
+ if (sdev->ring == NULL) {
+ pr_err("vmalloc failed, ring_sz = %d\n", sdev->ring_sz);
+ return -ENOMEM;
+ }
+ buf = (char *)sdev->ring;
+
+ /*reserve vmalloc memory as pages to make them remapable*/
+ for (virt_addr = (unsigned long)buf;
+ virt_addr < (unsigned long)buf + sdev->ring_sz;
+ virt_addr += PAGE_SIZE) {
+ SetPageReserved(vmalloc_to_page(
+ (unsigned long *) virt_addr));
+ }
+ }
+
+ memset(sdev->ring, 0, sdev->ring_sz);
+
+ sdev->num_tot_blks = (sdev->ring_sz / BLK_SIZE);
+ sdev->num_writers = 0;
+ sdev->write_idx = 0;
+ sdev->read_idx = 0;
+
+ sdev->num_write_avail_blks = sdev->num_tot_blks;
+ /*no. of blocks available for write*/
+ sdev->num_write_in_prog_blks = 0;
+ /*no. of blocks held by writers to perform writes*/
+
+ sdev->num_read_avail_blks = 0;
+ /*no. of blocks ready for read*/
+ sdev->num_read_in_prog_blks = 0;
+ /*no. of blocks held by the reader to perform read*/
+
+ return 0;
+}
+
+void ringbuf_cleanup(struct seemp_logk_dev *sdev)
+{
+ unsigned long virt_addr;
+
+ if (kmalloc_flag) {
+ for (virt_addr = (unsigned long)sdev->ring;
+ virt_addr < (unsigned long)sdev->ring + sdev->ring_sz;
+ virt_addr += PAGE_SIZE) {
+ /*clear all pages*/
+ ClearPageReserved(virt_to_page((unsigned long *)
+ virt_addr));
+ }
+ kfree(sdev->ring);
+ } else {
+ for (virt_addr = (unsigned long)sdev->ring;
+ virt_addr < (unsigned long)sdev->ring + sdev->ring_sz;
+ virt_addr += PAGE_SIZE) {
+ /*clear all pages*/
+ ClearPageReserved(vmalloc_to_page((unsigned long *)
+ virt_addr));
+ }
+ vfree(sdev->ring);
+ }
+}
+
+struct seemp_logk_blk *ringbuf_fetch_wr_block
+ (struct seemp_logk_dev *sdev)
+{
+ struct seemp_logk_blk *blk = NULL;
+ int idx;
+
+ mutex_lock(&sdev->lock);
+ if (sdev->num_write_avail_blks == 0) {
+ idx = -1;
+ mutex_unlock(&sdev->lock);
+ return blk;
+ }
+
+ idx = sdev->write_idx;
+ sdev->write_idx = (sdev->write_idx + 1) % sdev->num_tot_blks;
+ sdev->num_write_avail_blks--;
+ sdev->num_write_in_prog_blks++;
+ sdev->num_writers++;
+
+ blk = &sdev->ring[idx];
+ blk->status = 0x0;
+
+ mutex_unlock(&sdev->lock);
+ return blk;
+}
+
+void ringbuf_finish_writer(struct seemp_logk_dev *sdev,
+ struct seemp_logk_blk *blk)
+{
+ /* Encode seemp parameters in multi-threaded mode (before mutex lock) */
+ encode_seemp_params(blk);
+
+ /*
+ * finish writing...
+ * the calling process will no longer access this block.
+ */
+ mutex_lock(&sdev->lock);
+
+ sdev->num_writers--;
+ sdev->num_write_in_prog_blks--;
+ sdev->num_read_avail_blks++;
+
+ /*wake up any readers*/
+ if (sdev->num_writers == 0)
+ wake_up_interruptible(&sdev->readers_wq);
+
+ mutex_unlock(&sdev->lock);
+}
+
+int ringbuf_count_marked(struct seemp_logk_dev *sdev)
+{
+ int i;
+ unsigned int marked;
+
+ mutex_lock(&sdev->lock);
+ for (marked = 0, i = 0; i < sdev->num_tot_blks; i++)
+ if (sdev->ring[i].status & 0x1)
+ marked++;
+ mutex_unlock(&sdev->lock);
+
+ return marked;
+}
diff --git a/drivers/platform/msm/seemp_core/seemp_ringbuf.h b/drivers/platform/msm/seemp_core/seemp_ringbuf.h
new file mode 100644
index 0000000..3abdf77
--- /dev/null
+++ b/drivers/platform/msm/seemp_core/seemp_ringbuf.h
@@ -0,0 +1,29 @@
+/*
+ * 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
+ * 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 __SEEMP_RINGBUF_H__
+#define __SEEMP_RINGBUF_H__
+
+/*
+ * This header exports pingpong's API
+ */
+
+int ringbuf_init(struct seemp_logk_dev *sdev);
+struct seemp_logk_blk *ringbuf_fetch_wr_block
+(struct seemp_logk_dev *sdev);
+void ringbuf_finish_writer(struct seemp_logk_dev *sdev,
+ struct seemp_logk_blk *blk);
+void ringbuf_cleanup(struct seemp_logk_dev *sdev);
+int ringbuf_count_marked(struct seemp_logk_dev *sdev);
+
+#endif
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index 79d64ea..a66192f 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -355,6 +355,32 @@ static const struct dmi_system_id acer_blacklist[] __initconst = {
{}
};
+static const struct dmi_system_id amw0_whitelist[] __initconst = {
+ {
+ .ident = "Acer",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ },
+ },
+ {
+ .ident = "Gateway",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Gateway"),
+ },
+ },
+ {
+ .ident = "Packard Bell",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Packard Bell"),
+ },
+ },
+ {}
+};
+
+/*
+ * This quirk table is only for Acer/Gateway/Packard Bell family
+ * that those machines are supported by acer-wmi driver.
+ */
static const struct dmi_system_id acer_quirks[] __initconst = {
{
.callback = dmi_matched,
@@ -464,6 +490,17 @@ static const struct dmi_system_id acer_quirks[] __initconst = {
},
.driver_data = &quirk_acer_travelmate_2490,
},
+ {}
+};
+
+/*
+ * This quirk list is for those non-acer machines that have AMW0_GUID1
+ * but supported by acer-wmi in past days. Keeping this quirk list here
+ * is only for backward compatible. Please do not add new machine to
+ * here anymore. Those non-acer machines should be supported by
+ * appropriate wmi drivers.
+ */
+static const struct dmi_system_id non_acer_quirks[] __initconst = {
{
.callback = dmi_matched,
.ident = "Fujitsu Siemens Amilo Li 1718",
@@ -598,6 +635,7 @@ static void __init find_quirks(void)
{
if (!force_series) {
dmi_check_system(acer_quirks);
+ dmi_check_system(non_acer_quirks);
} else if (force_series == 2490) {
quirks = &quirk_acer_travelmate_2490;
}
@@ -2108,6 +2146,24 @@ static int __init acer_wmi_init(void)
find_quirks();
/*
+ * The AMW0_GUID1 wmi is not only found on Acer family but also other
+ * machines like Lenovo, Fujitsu and Medion. In the past days,
+ * acer-wmi driver handled those non-Acer machines by quirks list.
+ * But actually acer-wmi driver was loaded on any machines that have
+ * AMW0_GUID1. This behavior is strange because those machines should
+ * be supported by appropriate wmi drivers. e.g. fujitsu-laptop,
+ * ideapad-laptop. So, here checks the machine that has AMW0_GUID1
+ * should be in Acer/Gateway/Packard Bell white list, or it's already
+ * in the past quirk list.
+ */
+ if (wmi_has_guid(AMW0_GUID1) &&
+ !dmi_check_system(amw0_whitelist) &&
+ quirks == &quirk_unknown) {
+ pr_err("Unsupported machine has AMW0_GUID1, unable to load\n");
+ return -ENODEV;
+ }
+
+ /*
* Detect which ACPI-WMI interface we're using.
*/
if (wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index 6032b70..6eb2837 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -116,6 +116,10 @@ static struct quirk_entry quirk_asus_ux303ub = {
.wmi_backlight_native = true,
};
+static struct quirk_entry quirk_asus_x550lb = {
+ .xusb2pr = 0x01D9,
+};
+
static int dmi_matched(const struct dmi_system_id *dmi)
{
quirks = dmi->driver_data;
@@ -407,6 +411,15 @@ static const struct dmi_system_id asus_quirks[] = {
},
.driver_data = &quirk_asus_ux303ub,
},
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X550LB",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X550LB"),
+ },
+ .driver_data = &quirk_asus_x550lb,
+ },
{},
};
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index ce6ca31..8499d3a 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -156,6 +156,11 @@ MODULE_LICENSE("GPL");
#define ASUS_FAN_CTRL_MANUAL 1
#define ASUS_FAN_CTRL_AUTO 2
+#define USB_INTEL_XUSB2PR 0xD0
+#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
+
+static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
+
struct bios_args {
u32 arg0;
u32 arg1;
@@ -1080,6 +1085,29 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus)
return result;
}
+static void asus_wmi_set_xusb2pr(struct asus_wmi *asus)
+{
+ struct pci_dev *xhci_pdev;
+ u32 orig_ports_available;
+ u32 ports_available = asus->driver->quirks->xusb2pr;
+
+ xhci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI,
+ NULL);
+
+ if (!xhci_pdev)
+ return;
+
+ pci_read_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,
+ &orig_ports_available);
+
+ pci_write_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,
+ cpu_to_le32(ports_available));
+
+ pr_info("set USB_INTEL_XUSB2PR old: 0x%04x, new: 0x%04x\n",
+ orig_ports_available, ports_available);
+}
+
/*
* Hwmon device
*/
@@ -2025,6 +2053,16 @@ static int asus_wmi_fan_init(struct asus_wmi *asus)
return 0;
}
+static bool ashs_present(void)
+{
+ int i = 0;
+ while (ashs_ids[i]) {
+ if (acpi_dev_found(ashs_ids[i++]))
+ return true;
+ }
+ return false;
+}
+
/*
* WMI Driver
*/
@@ -2069,6 +2107,13 @@ static int asus_wmi_add(struct platform_device *pdev)
if (err)
goto fail_leds;
+ asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WLAN, &result);
+ if (result & (ASUS_WMI_DSTS_PRESENCE_BIT | ASUS_WMI_DSTS_USER_BIT))
+ asus->driver->wlan_ctrl_by_user = 1;
+
+ if (asus->driver->wlan_ctrl_by_user && ashs_present())
+ asus->driver->quirks->no_rfkill = 1;
+
if (!asus->driver->quirks->no_rfkill) {
err = asus_wmi_rfkill_init(asus);
if (err)
@@ -2087,6 +2132,9 @@ static int asus_wmi_add(struct platform_device *pdev)
if (asus->driver->quirks->wmi_backlight_native)
acpi_video_set_dmi_backlight_type(acpi_backlight_native);
+ if (asus->driver->quirks->xusb2pr)
+ asus_wmi_set_xusb2pr(asus);
+
if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
err = asus_wmi_backlight_init(asus);
if (err && err != -ENODEV)
@@ -2105,10 +2153,6 @@ static int asus_wmi_add(struct platform_device *pdev)
if (err)
goto fail_debugfs;
- asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WLAN, &result);
- if (result & (ASUS_WMI_DSTS_PRESENCE_BIT | ASUS_WMI_DSTS_USER_BIT))
- asus->driver->wlan_ctrl_by_user = 1;
-
return 0;
fail_debugfs:
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h
index 0e19014..fdff626 100644
--- a/drivers/platform/x86/asus-wmi.h
+++ b/drivers/platform/x86/asus-wmi.h
@@ -53,6 +53,7 @@ struct quirk_entry {
* and let the ACPI interrupt to send out the key event.
*/
int no_display_toggle;
+ u32 xusb2pr;
bool (*i8042_filter)(unsigned char data, unsigned char str,
struct serio *serio);
diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h
index 48fe04f..ca551a5a 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -165,12 +165,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,
@@ -229,6 +231,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;
@@ -356,6 +359,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 7ab5b31..b42a65d 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -52,6 +52,8 @@
#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 +130,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 +229,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,
@@ -270,6 +276,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 +307,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 +1494,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;
@@ -2066,6 +2109,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 +3149,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);
@@ -3409,6 +3472,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;
@@ -3755,6 +3822,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
@@ -3909,6 +3977,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 +4221,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");
diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c
index 07a0aef..9566e24 100644
--- a/drivers/regulator/cpr3-regulator.c
+++ b/drivers/regulator/cpr3-regulator.c
@@ -1382,6 +1382,11 @@ static int cpr3_regulator_init_cprh(struct cpr3_controller *ctrl)
(ctrl->use_dynamic_step_quot
? CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN : 0));
+ if (ctrl->thread_count > 1)
+ cpr3_masked_write(ctrl, CPR4_REG_CPR_TIMER_CLAMP,
+ CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN,
+ CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN);
+
if (ctrl->voltage_settling_time) {
/*
* Configure the settling timer used to account for
diff --git a/drivers/regulator/cprh-kbss-regulator.c b/drivers/regulator/cprh-kbss-regulator.c
index a93e7d8..b84d9f0 100644
--- a/drivers/regulator/cprh-kbss-regulator.c
+++ b/drivers/regulator/cprh-kbss-regulator.c
@@ -983,6 +983,9 @@ static int cprh_kbss_read_fuse_data(struct cpr3_regulator *vreg)
return -EINVAL;
}
+ vreg->fuse_corner_count = fuse_corners;
+ vreg->platform_fuses = fuse;
+
fuse->ro_sel = devm_kcalloc(vreg->thread->ctrl->dev, fuse_corners,
sizeof(*fuse->ro_sel), GFP_KERNEL);
fuse->init_voltage = devm_kcalloc(vreg->thread->ctrl->dev, fuse_corners,
@@ -1037,8 +1040,6 @@ static int cprh_kbss_read_fuse_data(struct cpr3_regulator *vreg)
vreg->speed_bin_fuse = fuse->speed_bin;
vreg->cpr_rev_fuse = fuse->cpr_fusing_rev;
- vreg->fuse_corner_count = fuse_corners;
- vreg->platform_fuses = fuse;
return 0;
}
diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h
index e7a59d4..0fa7d93 100644
--- a/drivers/scsi/ufs/ufs_quirks.h
+++ b/drivers/scsi/ufs/ufs_quirks.h
@@ -138,6 +138,7 @@ struct ufs_card_fix {
*/
#define UFS_DEVICE_QUIRK_HS_G1_TO_HS_G3_SWITCH (1 << 8)
+
struct ufs_hba;
void ufs_advertise_fixup_device(struct ufs_hba *hba);
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 7b91717..602c359 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -276,6 +276,7 @@ enum {
UFSHCD_STATE_RESET,
UFSHCD_STATE_ERROR,
UFSHCD_STATE_OPERATIONAL,
+ UFSHCD_STATE_EH_SCHEDULED,
};
/* UFSHCD error handling flags */
@@ -1404,7 +1405,7 @@ static void ufshcd_gate_work(struct work_struct *work)
* state to CLKS_ON.
*/
if (hba->clk_gating.is_suspended ||
- (hba->clk_gating.state == REQ_CLKS_ON)) {
+ (hba->clk_gating.state != REQ_CLKS_OFF)) {
hba->clk_gating.state = CLKS_ON;
trace_ufshcd_clk_gating(dev_name(hba->dev),
hba->clk_gating.state);
@@ -2386,7 +2387,7 @@ ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
*
* Returns 0 in case of success, non-zero value in case of failure
*/
-static int ufshcd_map_sg(struct ufshcd_lrb *lrbp)
+static int ufshcd_map_sg(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
{
struct ufshcd_sg_entry *prd_table;
struct scatterlist *sg;
@@ -2400,8 +2401,13 @@ static int ufshcd_map_sg(struct ufshcd_lrb *lrbp)
return sg_segments;
if (sg_segments) {
- lrbp->utr_descriptor_ptr->prd_table_length =
- cpu_to_le16((u16) (sg_segments));
+ if (hba->quirks & UFSHCD_QUIRK_PRDT_BYTE_GRAN)
+ lrbp->utr_descriptor_ptr->prd_table_length =
+ cpu_to_le16((u16)(sg_segments *
+ sizeof(struct ufshcd_sg_entry)));
+ else
+ lrbp->utr_descriptor_ptr->prd_table_length =
+ cpu_to_le16((u16) (sg_segments));
prd_table = (struct ufshcd_sg_entry *)lrbp->ucd_prdt_ptr;
@@ -2823,6 +2829,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
switch (hba->ufshcd_state) {
case UFSHCD_STATE_OPERATIONAL:
break;
+ case UFSHCD_STATE_EH_SCHEDULED:
case UFSHCD_STATE_RESET:
err = SCSI_MLQUEUE_HOST_BUSY;
goto out_unlock;
@@ -2913,7 +2920,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
goto out;
}
- err = ufshcd_map_sg(lrbp);
+ err = ufshcd_map_sg(hba, lrbp);
if (err) {
lrbp->cmd = NULL;
clear_bit_unlock(tag, &hba->lrb_in_use);
@@ -3855,12 +3862,21 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba)
cpu_to_le32(upper_32_bits(cmd_desc_element_addr));
/* Response upiu and prdt offset should be in double words */
- utrdlp[i].response_upiu_offset =
+ if (hba->quirks & UFSHCD_QUIRK_PRDT_BYTE_GRAN) {
+ utrdlp[i].response_upiu_offset =
+ cpu_to_le16(response_offset);
+ utrdlp[i].prd_table_offset =
+ cpu_to_le16(prdt_offset);
+ utrdlp[i].response_upiu_length =
+ cpu_to_le16(ALIGNED_UPIU_SIZE);
+ } else {
+ utrdlp[i].response_upiu_offset =
cpu_to_le16((response_offset >> 2));
- utrdlp[i].prd_table_offset =
+ utrdlp[i].prd_table_offset =
cpu_to_le16((prdt_offset >> 2));
- utrdlp[i].response_upiu_length =
+ utrdlp[i].response_upiu_length =
cpu_to_le16(ALIGNED_UPIU_SIZE >> 2);
+ }
hba->lrb[i].utr_descriptor_ptr = (utrdlp + i);
hba->lrb[i].utrd_dma_addr = hba->utrdl_dma_addr +
@@ -6236,7 +6252,7 @@ static irqreturn_t ufshcd_check_errors(struct ufs_hba *hba)
*/
ufshcd_set_eh_in_progress(hba);
- hba->ufshcd_state = UFSHCD_STATE_ERROR;
+ hba->ufshcd_state = UFSHCD_STATE_EH_SCHEDULED;
schedule_work(&hba->eh_work);
}
retval |= IRQ_HANDLED;
@@ -9399,10 +9415,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/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index b70606b..11916ac 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -303,6 +303,7 @@ struct ufs_pwr_mode_info {
* @pwr_change_notify: called before and after a power mode change
* is carried out to allow vendor spesific capabilities
* to be set.
+ * @apply_dev_quirks: called to apply device specific quirks
* @suspend: called during host controller PM callback
* @resume: called during host controller PM callback
* @full_reset: called during link recovery for handling variant specific
@@ -336,6 +337,7 @@ struct ufs_hba_variant_ops {
int (*resume)(struct ufs_hba *, enum ufs_pm_op);
int (*full_reset)(struct ufs_hba *);
void (*dbg_register_dump)(struct ufs_hba *hba, bool no_sleep);
+ int (*phy_initialization)(struct ufs_hba *);
int (*update_sec_cfg)(struct ufs_hba *hba, bool restore_sec_cfg);
u32 (*get_scale_down_gear)(struct ufs_hba *);
int (*set_bus_vote)(struct ufs_hba *, bool);
@@ -385,6 +387,9 @@ struct ufs_hba_variant {
struct ufs_hba_variant_ops *vops;
struct ufs_hba_crypto_variant_ops *crypto_vops;
struct ufs_hba_pm_qos_variant_ops *pm_qos_vops;
+ int (*suspend)(struct ufs_hba *, enum ufs_pm_op);
+ int (*resume)(struct ufs_hba *, enum ufs_pm_op);
+ void (*dbg_register_dump)(struct ufs_hba *hba);
};
/* clock gating state */
@@ -778,6 +783,11 @@ struct ufs_hba {
/* Auto hibern8 support is broken */
#define UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8 UFS_BIT(6)
+ /*
+ * This quirk needs to be enabled if the host contoller regards
+ * resolution of the values of PRDTO and PRDTL in UTRD as byte.
+ */
+ #define UFSHCD_QUIRK_PRDT_BYTE_GRAN UFS_BIT(7)
unsigned int quirks; /* Deviations from standard UFSHCI spec. */
@@ -1237,8 +1247,8 @@ static inline int ufshcd_vops_apply_dev_quirks(struct ufs_hba *hba)
static inline int ufshcd_vops_suspend(struct ufs_hba *hba, enum ufs_pm_op op)
{
- if (hba->var && hba->var->vops && hba->var->vops->suspend)
- return hba->var->vops->suspend(hba, op);
+ if (hba->var && hba->var->vops && hba->var->vops->apply_dev_quirks)
+ return hba->var->vops->apply_dev_quirks(hba);
return 0;
}
diff --git a/drivers/scsi/ufs/unipro.h b/drivers/scsi/ufs/unipro.h
index 602e196..a8cfa5e 100644
--- a/drivers/scsi/ufs/unipro.h
+++ b/drivers/scsi/ufs/unipro.h
@@ -125,6 +125,9 @@
#define PA_GRANULARITY_MIN_VAL 1
#define PA_GRANULARITY_MAX_VAL 6
+#define PA_GRANULARITY_MIN_VAL 1
+#define PA_GRANULARITY_MAX_VAL 6
+
/* PHY Adapter Protocol Constants */
#define PA_MAXDATALANES 4
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 0f8d9b6..dbb7ebe 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -324,6 +324,17 @@
spcom provides clients/server API, although currently only one client
or server is allowed per logical channel.
+config MSM_SPSS_UTILS
+ depends on MSM_PIL
+ bool "Secure Processor Utilities"
+ help
+ spss-utils driver selects Secure Processor firmware file name.
+ The firmware file name for dev, test or production is selected
+ based on two fuses.
+ Different file name is used for differnt SPSS HW versions,
+ because the SPSS firmware size is too small to support multiple
+ HW versions.
+
config TRACER_PKT
bool "Tracer Packet"
help
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 00a1284..060ac04 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -31,6 +31,7 @@
obj-$(CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT) += glink_smem_native_xprt.o
obj-$(CONFIG_MSM_GLINK_SPI_XPRT) += glink_spi_xprt.o
obj-$(CONFIG_MSM_SPCOM) += spcom.o
+obj-$(CONFIG_MSM_SPSS_UTILS) += spss_utils.o
obj-$(CONFIG_TRACER_PKT) += tracer_pkt.o
obj-$(CONFIG_QCOM_BUS_SCALING) += msm_bus/
obj-$(CONFIG_QTI_RPMH_API) += rpmh.o
diff --git a/drivers/soc/qcom/dcc_v2.c b/drivers/soc/qcom/dcc_v2.c
index cb212b2..4d2f54d 100644
--- a/drivers/soc/qcom/dcc_v2.c
+++ b/drivers/soc/qcom/dcc_v2.c
@@ -121,6 +121,7 @@ struct dcc_drvdata {
struct mutex mutex;
void __iomem *ram_base;
uint32_t ram_size;
+ uint32_t ram_offset;
enum dcc_data_sink data_sink;
enum dcc_func_type func_type[DCC_MAX_LINK_LIST];
uint32_t ram_cfg;
@@ -517,9 +518,10 @@ static int dcc_enable(struct dcc_drvdata *drvdata)
/* 3. If in capture mode program DCC_RAM_CFG reg */
if (drvdata->func_type[list] == DCC_FUNC_TYPE_CAPTURE) {
- dcc_writel(drvdata, ram_cfg_base, DCC_LL_BASE(list));
- dcc_writel(drvdata, drvdata->ram_start,
- DCC_FD_BASE(list));
+ dcc_writel(drvdata, ram_cfg_base +
+ drvdata->ram_offset/4, DCC_LL_BASE(list));
+ dcc_writel(drvdata, drvdata->ram_start +
+ drvdata->ram_offset/4, DCC_FD_BASE(list));
dcc_writel(drvdata, 0, DCC_LL_TIMEOUT(list));
}
@@ -1342,6 +1344,11 @@ static int dcc_probe(struct platform_device *pdev)
if (!drvdata->ram_base)
return -ENOMEM;
+ ret = of_property_read_u32(pdev->dev.of_node, "dcc-ram-offset",
+ &drvdata->ram_offset);
+ if (ret)
+ return -EINVAL;
+
drvdata->save_reg = of_property_read_bool(pdev->dev.of_node,
"qcom,save-reg");
diff --git a/drivers/soc/qcom/eud.c b/drivers/soc/qcom/eud.c
index 11965a2..1455069 100644
--- a/drivers/soc/qcom/eud.c
+++ b/drivers/soc/qcom/eud.c
@@ -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
@@ -144,10 +144,10 @@ static void eud_event_notifier(struct work_struct *eud_work)
eud_work);
if (chip->int_status == EUD_INT_VBUS)
- extcon_set_cable_state_(chip->extcon, chip->extcon_id,
+ extcon_set_state_sync(chip->extcon, chip->extcon_id,
chip->usb_attach);
else if (chip->int_status == EUD_INT_CHGR)
- extcon_set_cable_state_(chip->extcon, chip->extcon_id,
+ extcon_set_state_sync(chip->extcon, chip->extcon_id,
chip->chgr_enable);
}
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c
index 8cd5d3c..fd4c604 100644
--- a/drivers/soc/qcom/glink.c
+++ b/drivers/soc/qcom/glink.c
@@ -1846,7 +1846,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.
*
@@ -1902,6 +1902,7 @@ static struct channel_ctx *ch_name_to_ch_ctx_create(
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 @@ static struct channel_ctx *ch_name_to_ch_ctx_create(
"%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);
@@ -4805,6 +4810,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;
}
@@ -4827,6 +4833,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 266c0a2..3c4759c 100644
--- a/drivers/soc/qcom/glink_smem_native_xprt.c
+++ b/drivers/soc/qcom/glink_smem_native_xprt.c
@@ -822,6 +822,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/lpm-stats.c b/drivers/soc/qcom/lpm-stats.c
index 74a86ec..ee68433 100644
--- a/drivers/soc/qcom/lpm-stats.c
+++ b/drivers/soc/qcom/lpm-stats.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
@@ -425,7 +425,10 @@ static inline void update_exit_stats(struct lpm_stats *stats, uint32_t index,
uint64_t exit_time = 0;
/* Update time stats only when exit is preceded by enter */
- exit_time = stats->sleep_time;
+ if (stats->sleep_time < 0)
+ success = false;
+ else
+ exit_time = stats->sleep_time;
update_level_stats(&stats->time_stats[index], exit_time,
success);
}
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_arb_rpmh.c b/drivers/soc/qcom/msm_bus/msm_bus_arb_rpmh.c
index c977d1b..bf5a526 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_arb_rpmh.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_arb_rpmh.c
@@ -24,6 +24,9 @@
#define NUM_LNODES 3
#define MAX_STR_CL 50
+#define MSM_BUS_MAS_ALC 144
+#define MSM_BUS_RSC_APPS 8000
+
struct bus_search_type {
struct list_head link;
struct list_head node_list;
@@ -38,6 +41,7 @@ static struct handle_type handle_list;
static LIST_HEAD(input_list);
static LIST_HEAD(apply_list);
static LIST_HEAD(commit_list);
+static LIST_HEAD(late_init_clist);
static LIST_HEAD(query_list);
DEFINE_RT_MUTEX(msm_bus_adhoc_lock);
@@ -123,6 +127,9 @@ static void bcm_add_bus_req(struct device *dev)
goto exit_bcm_add_bus_req;
}
+ if (cur_dev->node_info->bcm_req_idx != -1)
+ goto exit_bcm_add_bus_req;
+
if (!cur_dev->node_info->num_bcm_devs)
goto exit_bcm_add_bus_req;
@@ -179,8 +186,6 @@ static void bcm_add_bus_req(struct device *dev)
cur_dev->node_info->bcm_req_idx = lnode_idx;
memset(lnode->lnode_ib, 0, sizeof(uint64_t) * NUM_CTX);
memset(lnode->lnode_ab, 0, sizeof(uint64_t) * NUM_CTX);
- MSM_BUS_ERR("%s: Added %d entry to bcm %d @ %d\n", __func__,
- lnode->bus_dev_id, bcm_dev->node_info->id, lnode_idx);
}
exit_bcm_add_bus_req:
@@ -316,7 +321,6 @@ static int prune_path(struct list_head *route_list, int dest, int src,
MSM_BUS_ERR("%s: Can't find dest dev %d", __func__, dest);
goto exit_prune_path;
}
- MSM_BUS_ERR("%s: dest dev %d", __func__, dest);
lnode_hop = gen_lnode(dest_dev, search_dev_id, lnode_hop, cl_name);
bcm_add_bus_req(dest_dev);
@@ -520,7 +524,6 @@ static void bcm_update_bus_req(struct device *dev, int ctx)
max_ib = max(max_ib,
max(bcm_dev->lnode_list[i].lnode_ib[ACTIVE_CTX],
bcm_dev->lnode_list[i].lnode_ib[DUAL_CTX]));
-
max_ab = max(max_ab,
bcm_dev->lnode_list[i].lnode_ab[ACTIVE_CTX] +
bcm_dev->lnode_list[i].lnode_ab[DUAL_CTX]);
@@ -531,9 +534,14 @@ static void bcm_update_bus_req(struct device *dev, int ctx)
bcm_dev->lnode_list[i].lnode_ab[ctx]);
}
}
-
bcm_dev->node_bw[ctx].max_ab = max_ab;
bcm_dev->node_bw[ctx].max_ib = max_ib;
+
+ max_ab = msm_bus_div64(max_ab, bcm_dev->bcmdev->unit_size);
+ max_ib = msm_bus_div64(max_ib, bcm_dev->bcmdev->unit_size);
+
+ bcm_dev->node_vec[ctx].vec_a = max_ab;
+ bcm_dev->node_vec[ctx].vec_b = max_ib;
}
exit_bcm_update_bus_req:
return;
@@ -598,44 +606,81 @@ static void bcm_query_bus_req(struct device *dev, int ctx)
}
}
+ max_query_ab = msm_bus_div64(max_query_ab,
+ bcm_dev->bcmdev->unit_size);
+ max_query_ib = msm_bus_div64(max_query_ib,
+ bcm_dev->bcmdev->unit_size);
+
bcm_dev->node_bw[ctx].max_query_ab = max_query_ab;
bcm_dev->node_bw[ctx].max_query_ib = max_query_ib;
-
}
exit_bcm_query_bus_req:
return;
}
-
-
-int bcm_remove_handoff_req(struct device *dev, void *data)
+static void bcm_update_alc_req(struct msm_bus_node_device_type *dev, int ctx)
{
struct msm_bus_node_device_type *bcm_dev = NULL;
int i;
- uint64_t max_ib = 0;
- uint64_t max_ab = 0;
+ uint64_t max_alc = 0;
+
+ if (!dev || !to_msm_bus_node(dev->node_info->bus_device)) {
+ MSM_BUS_ERR("Bus node pointer is Invalid");
+ goto exit_bcm_update_alc_req;
+ }
+
+ for (i = 0; i < dev->num_lnodes; i++)
+ max_alc = max(max_alc, dev->lnode_list[i].alc_idx[ctx]);
+
+ dev->node_bw[ctx].max_alc = max_alc;
+
+ bcm_dev = to_msm_bus_node(dev->node_info->bcm_devs[0]);
+
+ if (ctx == ACTIVE_CTX) {
+ max_alc = max(max_alc,
+ max(dev->node_bw[ACTIVE_CTX].max_alc,
+ dev->node_bw[DUAL_CTX].max_alc));
+ } else {
+ max_alc = dev->node_bw[ctx].max_alc;
+ }
+
+ bcm_dev->node_bw[ctx].max_alc = max_alc;
+ bcm_dev->node_vec[ctx].vec_a = max_alc;
+ bcm_dev->node_vec[ctx].vec_b = 0;
+
+exit_bcm_update_alc_req:
+ return;
+}
+
+int bcm_remove_handoff_req(struct device *dev, void *data)
+{
+ struct msm_bus_node_device_type *bus_dev = NULL;
+ struct msm_bus_node_device_type *cur_bcm = NULL;
+ struct msm_bus_node_device_type *cur_rsc = NULL;
int ret = 0;
rt_mutex_lock(&msm_bus_adhoc_lock);
- bcm_dev = to_msm_bus_node(dev);
- if (!bcm_dev) {
- MSM_BUS_ERR("%s: Null device ptr", __func__);
- goto exit_bcm_remove_handoff_req;
- }
-
- if (!bcm_dev->node_info->is_bcm_dev)
+ bus_dev = to_msm_bus_node(dev);
+ if (bus_dev->node_info->is_bcm_dev ||
+ bus_dev->node_info->is_fab_dev ||
+ bus_dev->node_info->is_rsc_dev)
goto exit_bcm_remove_handoff_req;
- for (i = 0; i < bcm_dev->num_lnodes; i++) {
- max_ib = max(max_ib,
- bcm_dev->lnode_list[i].lnode_ib[0]);
- max_ab = max(max_ab,
- bcm_dev->lnode_list[i].lnode_ab[0]);
+ if (bus_dev->node_info->num_bcm_devs) {
+ cur_bcm = to_msm_bus_node(bus_dev->node_info->bcm_devs[0]);
+ if (cur_bcm->node_info->num_rsc_devs) {
+ cur_rsc =
+ to_msm_bus_node(cur_bcm->node_info->rsc_devs[0]);
+ if (cur_rsc->node_info->id != MSM_BUS_RSC_APPS)
+ goto exit_bcm_remove_handoff_req;
+ }
}
- bcm_dev->node_bw[0].max_ab = max_ab;
- bcm_dev->node_bw[0].max_ib = max_ib;
+ if (!bus_dev->dirty) {
+ list_add_tail(&bus_dev->link, &late_init_clist);
+ bus_dev->dirty = true;
+ }
exit_bcm_remove_handoff_req:
rt_mutex_unlock(&msm_bus_adhoc_lock);
@@ -684,7 +729,6 @@ static void aggregate_bus_query_req(struct msm_bus_node_device_type *bus_dev,
sum_ab += bus_dev->lnode_list[i].lnode_query_ab[ctx];
}
- MSM_BUS_ERR("aggregate: query_ab:%llu\n", sum_ab);
bus_dev->node_bw[ctx].sum_query_ab = sum_ab;
bus_dev->node_bw[ctx].max_query_ib = max_ib;
@@ -766,6 +810,18 @@ static void commit_data(void)
INIT_LIST_HEAD(&commit_list);
}
+void commit_late_init_data(void)
+{
+ rt_mutex_lock(&msm_bus_adhoc_lock);
+
+ msm_bus_commit_data(&late_init_clist);
+ INIT_LIST_HEAD(&late_init_clist);
+
+ rt_mutex_unlock(&msm_bus_adhoc_lock);
+}
+
+
+
static void add_node_to_clist(struct msm_bus_node_device_type *node)
{
struct msm_bus_node_device_type *node_parent =
@@ -870,6 +926,63 @@ static int update_path(struct device *src_dev, int dest, uint64_t act_req_ib,
return ret;
}
+static int update_alc_vote(struct device *alc_dev, uint64_t act_req_fa_lat,
+ uint64_t act_req_idle_time, uint64_t slp_req_fa_lat,
+ uint64_t slp_req_idle_time, uint64_t cur_fa_lat,
+ uint64_t cur_idle_time, int idx, int ctx)
+{
+ struct link_node *lnode = NULL;
+ struct msm_bus_node_device_type *dev_info = NULL;
+ int curr_idx, i;
+ int ret = 0;
+
+ if (IS_ERR_OR_NULL(alc_dev)) {
+ MSM_BUS_ERR("%s: No source device", __func__);
+ ret = -ENODEV;
+ goto exit_update_alc_vote;
+ }
+
+ if (idx < 0) {
+ MSM_BUS_ERR("%s: Invalid lnode idx %d", __func__, idx);
+ ret = -ENXIO;
+ goto exit_update_alc_vote;
+ }
+
+ dev_info = to_msm_bus_node(alc_dev);
+ curr_idx = idx;
+
+ if (curr_idx >= dev_info->num_lnodes) {
+ MSM_BUS_ERR("%s: Invalid lnode Idx %d num lnodes %d",
+ __func__, curr_idx, dev_info->num_lnodes);
+ ret = -ENXIO;
+ goto exit_update_alc_vote;
+ }
+
+ lnode = &dev_info->lnode_list[curr_idx];
+ if (!lnode) {
+ MSM_BUS_ERR("%s: Invalid lnode ptr lnode %d",
+ __func__, curr_idx);
+ ret = -ENXIO;
+ goto exit_update_alc_vote;
+ }
+
+ /*
+ * Add aggregation and mapping logic once LUT is avail.
+ * Use default values for time being.
+ */
+ lnode->alc_idx[ACTIVE_CTX] = 12;
+ lnode->alc_idx[DUAL_CTX] = 0;
+
+ for (i = 0; i < NUM_CTX; i++)
+ bcm_update_alc_req(dev_info, i);
+
+ add_node_to_clist(dev_info);
+
+exit_update_alc_vote:
+ return ret;
+}
+
+
static int query_path(struct device *src_dev, int dest, uint64_t act_req_ib,
uint64_t act_req_bw, uint64_t slp_req_ib,
uint64_t slp_req_bw, uint64_t cur_ib, uint64_t cur_bw,
@@ -1160,6 +1273,40 @@ static uint32_t register_client_adhoc(struct msm_bus_scale_pdata *pdata)
}
client->pdata = pdata;
+ if (pdata->alc) {
+ client->curr = -1;
+ lnode = kzalloc(sizeof(int), GFP_KERNEL);
+
+ if (ZERO_OR_NULL_PTR(lnode)) {
+ MSM_BUS_ERR("%s: Error allocating lnode!", __func__);
+ goto exit_lnode_malloc_fail;
+ }
+ client->src_pnode = lnode;
+
+ client->src_devs = kzalloc(sizeof(struct device *),
+ GFP_KERNEL);
+ if (IS_ERR_OR_NULL(client->src_devs)) {
+ MSM_BUS_ERR("%s: Error allocating src_dev!", __func__);
+ goto exit_src_dev_malloc_fail;
+ }
+ src = MSM_BUS_MAS_ALC;
+ dev = bus_find_device(&msm_bus_type, NULL,
+ (void *) &src,
+ msm_bus_device_match_adhoc);
+ if (IS_ERR_OR_NULL(dev)) {
+ MSM_BUS_ERR("%s:Failed to find alc device",
+ __func__);
+ goto exit_invalid_data;
+ }
+ gen_lnode(dev, MSM_BUS_MAS_ALC, 0, pdata->name);
+ bcm_add_bus_req(dev);
+
+ client->src_devs[0] = dev;
+
+ handle = gen_handle(client);
+ goto exit_register_client;
+ }
+
lnode = kcalloc(pdata->usecase->num_paths, sizeof(int), GFP_KERNEL);
if (ZERO_OR_NULL_PTR(lnode)) {
MSM_BUS_ERR("%s: Error allocating pathnode ptr!", __func__);
@@ -1293,6 +1440,58 @@ static int update_client_paths(struct msm_bus_client *client, bool log_trns,
return ret;
}
+static int update_client_alc(struct msm_bus_client *client, bool log_trns,
+ unsigned int idx)
+{
+ int lnode, cur_idx;
+ uint64_t req_idle_time, req_fal, dual_idle_time, dual_fal,
+ cur_idle_time, cur_fal;
+ int ret = 0;
+ struct msm_bus_scale_pdata *pdata;
+ struct device *src_dev;
+
+ if (!client) {
+ MSM_BUS_ERR("Client handle Null");
+ ret = -ENXIO;
+ goto exit_update_client_alc;
+ }
+
+ pdata = client->pdata;
+ if (!pdata) {
+ MSM_BUS_ERR("Client pdata Null");
+ ret = -ENXIO;
+ goto exit_update_client_alc;
+ }
+
+ cur_idx = client->curr;
+ client->curr = idx;
+ req_fal = pdata->usecase_lat[idx].fal_ns;
+ req_idle_time = pdata->usecase_lat[idx].idle_t_ns;
+ lnode = client->src_pnode[0];
+ src_dev = client->src_devs[0];
+
+ if (pdata->active_only) {
+ dual_fal = 0;
+ dual_idle_time = 0;
+ } else {
+ dual_fal = req_fal;
+ dual_idle_time = req_idle_time;
+ }
+
+ ret = update_alc_vote(src_dev, req_fal, req_idle_time, dual_fal,
+ dual_idle_time, cur_fal, cur_idle_time, lnode,
+ pdata->active_only);
+
+ if (ret) {
+ MSM_BUS_ERR("%s: Update path failed! %d ctx %d\n",
+ __func__, ret, pdata->active_only);
+ goto exit_update_client_alc;
+ }
+ commit_data();
+exit_update_client_alc:
+ return ret;
+}
+
static int query_usecase(struct msm_bus_client *client, bool log_trns,
unsigned int idx,
struct msm_bus_tcs_usecase *tcs_usecase)
@@ -1483,8 +1682,13 @@ static int update_request_adhoc(uint32_t cl, unsigned int index)
MSM_BUS_DBG("%s: cl: %u index: %d curr: %d num_paths: %d\n", __func__,
cl, index, client->curr, client->pdata->usecase->num_paths);
- msm_bus_dbg_client_data(client->pdata, index, cl);
- ret = update_client_paths(client, log_transaction, index);
+
+ if (pdata->alc)
+ ret = update_client_alc(client, log_transaction, index);
+ else {
+ msm_bus_dbg_client_data(client->pdata, index, cl);
+ ret = update_client_paths(client, log_transaction, index);
+ }
if (ret) {
pr_err("%s: Err updating path\n", __func__);
goto exit_update_request;
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_dbg.c b/drivers/soc/qcom/msm_bus/msm_bus_dbg.c
index 5908122..015edb3 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_dbg.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_dbg.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, 2014-2016, The Linux Foundation. All rights
+/* Copyright (c) 2010-2012, 2014-2017, The Linux Foundation. All rights
* reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -439,7 +439,6 @@ static void msm_bus_dbg_free_client(uint32_t clid)
list_for_each_entry(cldata, &cl_list, list) {
if (cldata->clid == clid) {
- debugfs_remove(cldata->file);
list_del(&cldata->list);
kfree(cldata);
break;
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c b/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c
index beb5c2b..91d6349 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c
@@ -45,6 +45,7 @@ static int msm_bus_dev_init_qos(struct device *dev, void *data);
static struct list_head bcm_query_list_inorder[VCD_MAX_CNT];
static struct msm_bus_node_device_type *cur_rsc;
+static bool init_time = true;
struct bcm_db {
uint32_t unit_size;
@@ -265,27 +266,23 @@ static int setrate_nodeclk(struct nodeclk *nclk, long rate)
}
static int tcs_cmd_gen(struct msm_bus_node_device_type *cur_bcm,
- struct tcs_cmd *cmd, uint64_t ib,
- uint64_t ab, bool commit)
+ struct tcs_cmd *cmd, uint64_t vec_a,
+ uint64_t vec_b, bool commit)
{
int ret = 0;
bool valid = true;
- if (ib == 0 && ab == 0) {
+ if (vec_a == 0 && vec_b == 0)
valid = false;
- } else {
- do_div(ib, cur_bcm->bcmdev->unit_size);
- do_div(ab, cur_bcm->bcmdev->unit_size);
- }
- if (ib > BCM_TCS_CMD_VOTE_MASK)
- ib = BCM_TCS_CMD_VOTE_MASK;
+ if (vec_a > BCM_TCS_CMD_VOTE_MASK)
+ vec_a = BCM_TCS_CMD_VOTE_MASK;
- if (ab > BCM_TCS_CMD_VOTE_MASK)
- ab = BCM_TCS_CMD_VOTE_MASK;
+ if (vec_b > BCM_TCS_CMD_VOTE_MASK)
+ vec_b = BCM_TCS_CMD_VOTE_MASK;
cmd->addr = cur_bcm->bcmdev->addr;
- cmd->data = BCM_TCS_CMD(commit, valid, ab, ib);
+ cmd->data = BCM_TCS_CMD(commit, valid, vec_a, vec_b);
cmd->complete = commit;
return ret;
@@ -314,7 +311,12 @@ static int tcs_cmd_list_gen(int *n_active,
if (list_empty(&cur_bcm_clist[i]))
continue;
list_for_each_entry(cur_bcm, &cur_bcm_clist[i], link) {
- if (cur_bcm->updated) {
+ if (cur_bcm->updated ||
+ (cur_bcm->node_vec[DUAL_CTX].vec_a == 0 &&
+ cur_bcm->node_vec[ACTIVE_CTX].vec_a == 0 &&
+ cur_bcm->node_vec[DUAL_CTX].vec_b == 0 &&
+ cur_bcm->node_vec[ACTIVE_CTX].vec_b == 0 &&
+ init_time == true)) {
if (last_tcs != -1 &&
list_is_last(&cur_bcm->link,
&cur_bcm_clist[i])) {
@@ -333,8 +335,8 @@ static int tcs_cmd_list_gen(int *n_active,
idx++;
}
tcs_cmd_gen(cur_bcm, &cmdlist_active[k],
- cur_bcm->node_bw[ACTIVE_CTX].max_ib,
- cur_bcm->node_bw[ACTIVE_CTX].max_ab, commit);
+ cur_bcm->node_vec[ACTIVE_CTX].vec_a,
+ cur_bcm->node_vec[ACTIVE_CTX].vec_b, commit);
k++;
last_tcs = k;
cur_bcm->updated = true;
@@ -352,25 +354,26 @@ static int tcs_cmd_list_gen(int *n_active,
continue;
list_for_each_entry(cur_bcm, &cur_bcm_clist[i], link) {
commit = false;
- if ((cur_bcm->node_bw[DUAL_CTX].max_ab ==
- cur_bcm->node_bw[ACTIVE_CTX].max_ab) &&
- (cur_bcm->node_bw[DUAL_CTX].max_ib ==
- cur_bcm->node_bw[ACTIVE_CTX].max_ib)) {
+ if ((cur_bcm->node_vec[DUAL_CTX].vec_a ==
+ cur_bcm->node_vec[ACTIVE_CTX].vec_a) &&
+ (cur_bcm->node_vec[DUAL_CTX].vec_b ==
+ cur_bcm->node_vec[ACTIVE_CTX].vec_b)) {
if (last_tcs != -1 &&
list_is_last(&cur_bcm->link,
&cur_bcm_clist[i])) {
- cmdlist_wake[k].data |=
+ cmdlist_wake[last_tcs].data |=
BCM_TCS_CMD_COMMIT_MASK;
- cmdlist_sleep[k].data |=
+ cmdlist_sleep[last_tcs].data |=
BCM_TCS_CMD_COMMIT_MASK;
- cmdlist_wake[k].complete = true;
- cmdlist_sleep[k].complete = true;
+ cmdlist_wake[last_tcs].complete = true;
+ cmdlist_sleep[last_tcs].complete = true;
idx++;
}
continue;
}
last_tcs = k;
n_sleep[idx]++;
+ n_wake[idx]++;
if (list_is_last(&cur_bcm->link,
&cur_bcm_clist[i])) {
commit = true;
@@ -378,11 +381,11 @@ static int tcs_cmd_list_gen(int *n_active,
}
tcs_cmd_gen(cur_bcm, &cmdlist_wake[k],
- cur_bcm->node_bw[ACTIVE_CTX].max_ib,
- cur_bcm->node_bw[ACTIVE_CTX].max_ab, commit);
+ cur_bcm->node_vec[ACTIVE_CTX].vec_a,
+ cur_bcm->node_vec[ACTIVE_CTX].vec_b, commit);
tcs_cmd_gen(cur_bcm, &cmdlist_sleep[k],
- cur_bcm->node_bw[DUAL_CTX].max_ib,
- cur_bcm->node_bw[DUAL_CTX].max_ab, commit);
+ cur_bcm->node_vec[DUAL_CTX].vec_a,
+ cur_bcm->node_vec[DUAL_CTX].vec_b, commit);
k++;
}
}
@@ -485,10 +488,11 @@ static int bcm_clist_clean(struct msm_bus_node_device_type *cur_dev)
cur_bcm = to_msm_bus_node(cur_dev->node_info->bcm_devs[0]);
- if (cur_bcm->node_bw[DUAL_CTX].max_ab == 0 &&
- cur_bcm->node_bw[ACTIVE_CTX].max_ab == 0 &&
- cur_bcm->node_bw[DUAL_CTX].max_ib == 0 &&
- cur_bcm->node_bw[ACTIVE_CTX].max_ib == 0) {
+ if (cur_bcm->node_vec[DUAL_CTX].vec_a == 0 &&
+ cur_bcm->node_vec[ACTIVE_CTX].vec_a == 0 &&
+ cur_bcm->node_vec[DUAL_CTX].vec_b == 0 &&
+ cur_bcm->node_vec[ACTIVE_CTX].vec_b == 0 &&
+ init_time == false) {
cur_bcm->dirty = false;
list_del_init(&cur_bcm->link);
}
@@ -550,25 +554,31 @@ int msm_bus_commit_data(struct list_head *clist)
if (list_empty(&cur_bcm_clist[i]))
continue;
list_for_each_entry(cur_bcm, &cur_bcm_clist[i], link) {
- if ((cur_bcm->node_bw[DUAL_CTX].max_ab !=
- cur_bcm->node_bw[ACTIVE_CTX].max_ab) ||
- (cur_bcm->node_bw[DUAL_CTX].max_ib !=
- cur_bcm->node_bw[ACTIVE_CTX].max_ib)) {
+ if ((cur_bcm->node_vec[DUAL_CTX].vec_a !=
+ cur_bcm->node_vec[ACTIVE_CTX].vec_a) ||
+ (cur_bcm->node_vec[DUAL_CTX].vec_b !=
+ cur_bcm->node_vec[ACTIVE_CTX].vec_b)) {
cnt_sleep++;
cnt_wake++;
}
- if (!cur_bcm->updated)
- cnt_active++;
+ if (cur_bcm->updated ||
+ (cur_bcm->node_vec[DUAL_CTX].vec_a == 0 &&
+ cur_bcm->node_vec[ACTIVE_CTX].vec_a == 0 &&
+ cur_bcm->node_vec[DUAL_CTX].vec_b == 0 &&
+ cur_bcm->node_vec[ACTIVE_CTX].vec_b == 0 &&
+ init_time == true))
+ continue;
+ cnt_active++;
}
cnt_vcd++;
}
- MSM_BUS_ERR("%s: cmd_gen\n", __func__);
n_active = kcalloc(cnt_vcd+1, sizeof(int), GFP_KERNEL);
n_wake = kcalloc(cnt_vcd+1, sizeof(int), GFP_KERNEL);
n_sleep = kcalloc(cnt_vcd+1, sizeof(int), GFP_KERNEL);
- cmdlist_active = kcalloc(cnt_active, sizeof(struct tcs_cmd),
+ if (cnt_active)
+ cmdlist_active = kcalloc(cnt_active, sizeof(struct tcs_cmd),
GFP_KERNEL);
if (cnt_sleep && cnt_wake) {
cmdlist_wake = kcalloc(cnt_wake, sizeof(struct tcs_cmd),
@@ -580,7 +590,11 @@ int msm_bus_commit_data(struct list_head *clist)
cmdlist_wake, cmdlist_sleep, cur_bcm_clist);
ret = rpmh_invalidate(cur_mbox);
- ret = rpmh_write_passthru(cur_mbox, cur_rsc->rscdev->req_state,
+ if (cur_rsc->rscdev->req_state == RPMH_AWAKE_STATE)
+ ret = rpmh_write(cur_mbox, cur_rsc->rscdev->req_state,
+ cmdlist_active, cnt_active);
+ else
+ ret = rpmh_write_passthru(cur_mbox, cur_rsc->rscdev->req_state,
cmdlist_active, n_active);
ret = rpmh_write_passthru(cur_mbox, RPMH_WAKE_ONLY_STATE,
@@ -1086,6 +1100,7 @@ static int msm_bus_copy_node_info(struct msm_bus_node_device_type *pdata,
node_info->name = pdata_node_info->name;
node_info->id = pdata_node_info->id;
+ node_info->bcm_req_idx = -1;
node_info->bus_device_id = pdata_node_info->bus_device_id;
node_info->mas_rpm_id = pdata_node_info->mas_rpm_id;
node_info->slv_rpm_id = pdata_node_info->slv_rpm_id;
@@ -1289,6 +1304,7 @@ static struct device *msm_bus_device_init(
bus_node->node_info = node_info;
bus_node->ap_owned = pdata->ap_owned;
+ bus_node->dirty = false;
bus_dev->of_node = pdata->of_node;
if (msm_bus_copy_node_info(pdata, bus_dev) < 0) {
@@ -1653,9 +1669,11 @@ int __init msm_bus_device_late_init(void)
int rc;
MSM_BUS_ERR("msm_bus_late_init: Remove handoff bw requests\n");
+ init_time = false;
rc = bus_for_each_dev(&msm_bus_type, NULL, NULL,
bcm_remove_handoff_req);
+ commit_late_init_data();
return rc;
}
subsys_initcall(msm_bus_device_init_driver);
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_of.c b/drivers/soc/qcom/msm_bus/msm_bus_of.c
index 856dcce..fd72ae6 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_of.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_of.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
@@ -46,6 +46,7 @@ static struct msm_bus_scale_pdata *get_pdata(struct platform_device *pdev,
{
struct msm_bus_scale_pdata *pdata = NULL;
struct msm_bus_paths *usecase = NULL;
+ struct msm_bus_lat_vectors *usecase_lat = NULL;
int i = 0, j, ret, num_usecases = 0, num_paths, len;
const uint32_t *vec_arr = NULL;
bool mem_err = false;
@@ -85,6 +86,42 @@ static struct msm_bus_scale_pdata *get_pdata(struct platform_device *pdev,
pr_debug("Using dual context by default\n");
}
+ pdata->alc = of_property_read_bool(of_node, "qcom,msm-bus,alc-voter");
+
+ if (pdata->alc) {
+ usecase_lat = devm_kzalloc(&pdev->dev,
+ (sizeof(struct msm_bus_lat_vectors) *
+ pdata->num_usecases), GFP_KERNEL);
+ if (!usecase_lat) {
+ mem_err = true;
+ goto err;
+ }
+
+ vec_arr = of_get_property(of_node,
+ "qcom,msm-bus,vectors-alc", &len);
+ if (vec_arr == NULL) {
+ pr_err("Error: Lat vector array not found\n");
+ goto err;
+ }
+
+ if (len != num_usecases * sizeof(uint32_t) * 2) {
+ pr_err("Error: Length-error on getting vectors\n");
+ goto err;
+ }
+
+ for (i = 0; i < num_usecases; i++) {
+ int index = i * 2;
+
+ usecase_lat[i].fal_ns = (uint64_t)
+ KBTOB(be32_to_cpu(vec_arr[index]));
+ usecase_lat[i].idle_t_ns = (uint64_t)
+ KBTOB(be32_to_cpu(vec_arr[index + 1]));
+ }
+
+ pdata->usecase_lat = usecase_lat;
+ return pdata;
+ }
+
usecase = devm_kzalloc(&pdev->dev, (sizeof(struct msm_bus_paths) *
pdata->num_usecases), GFP_KERNEL);
if (!usecase) {
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h b/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h
index f415735..fad7afa 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h
+++ b/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h
@@ -29,6 +29,7 @@ struct link_node {
uint64_t lnode_ab[NUM_CTX];
uint64_t lnode_query_ib[NUM_CTX];
uint64_t lnode_query_ab[NUM_CTX];
+ uint64_t alc_idx[NUM_CTX];
int next;
struct device *next_dev;
struct list_head link;
@@ -61,11 +62,17 @@ struct nodebw {
uint64_t sum_query_ab;
uint64_t max_query_ib;
uint64_t max_query_ab;
+ uint64_t max_alc;
uint64_t cur_clk_hz;
uint32_t util_used;
uint32_t vrail_used;
};
+struct nodevector {
+ uint64_t vec_a;
+ uint64_t vec_b;
+};
+
struct msm_bus_rsc_device_type {
struct rpmh_client *mbox;
struct list_head bcm_clist[VCD_MAX_CNT];
@@ -167,6 +174,7 @@ struct msm_bus_node_device_type {
int num_lnodes;
struct link_node *lnode_list;
struct nodebw node_bw[NUM_CTX];
+ struct nodevector node_vec[NUM_CTX];
struct list_head link;
struct list_head query_link;
struct nodeclk clk[NUM_CTX];
@@ -193,6 +201,7 @@ int msm_bus_enable_limiter(struct msm_bus_node_device_type *nodedev,
int throttle_en, uint64_t lim_bw);
int msm_bus_commit_data(struct list_head *clist);
int bcm_remove_handoff_req(struct device *dev, void *data);
+void commit_late_init_data(void);
int msm_bus_query_gen(struct list_head *qlist,
struct msm_bus_tcs_usecase *tcs_usecase);
void *msm_bus_realloc_devmem(struct device *dev, void *p, size_t old_size,
diff --git a/drivers/soc/qcom/msm_glink_pkt.c b/drivers/soc/qcom/msm_glink_pkt.c
index 2471d27..519bb611 100644
--- a/drivers/soc/qcom/msm_glink_pkt.c
+++ b/drivers/soc/qcom/msm_glink_pkt.c
@@ -625,14 +625,17 @@ ssize_t glink_pkt_read(struct file *file,
return -ENETRESET;
}
+ mutex_lock(&devp->ch_lock);
if (!glink_rx_intent_exists(devp->handle, count)) {
ret = glink_queue_rx_intent(devp->handle, devp, count);
if (ret) {
GLINK_PKT_ERR("%s: failed to queue_rx_intent ret[%d]\n",
__func__, ret);
+ mutex_unlock(&devp->ch_lock);
return ret;
}
}
+ mutex_unlock(&devp->ch_lock);
GLINK_PKT_INFO("Begin %s on glink_pkt_dev id:%d buffer_size %zu\n",
__func__, devp->i, count);
diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c
index 2cbbe2e..bbde4b6 100644
--- a/drivers/soc/qcom/pil-q6v5-mss.c
+++ b/drivers/soc/qcom/pil-q6v5-mss.c
@@ -266,8 +266,8 @@ static int pil_mss_loadable_init(struct modem_data *drv,
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"rmb_base");
q6->rmb_base = devm_ioremap_resource(&pdev->dev, res);
- if (!q6->rmb_base)
- return -ENOMEM;
+ if (IS_ERR(q6->rmb_base))
+ return PTR_ERR(q6->rmb_base);
drv->rmb_base = q6->rmb_base;
q6_desc->ops = &pil_msa_mss_ops_selfauth;
}
diff --git a/drivers/soc/qcom/pil-q6v5.c b/drivers/soc/qcom/pil-q6v5.c
index d9d6c72..b41a173 100644
--- a/drivers/soc/qcom/pil-q6v5.c
+++ b/drivers/soc/qcom/pil-q6v5.c
@@ -120,6 +120,18 @@ int pil_q6v5_make_proxy_votes(struct pil_desc *pil)
goto err_qdss_vote;
}
+ ret = clk_prepare_enable(drv->prng_clk);
+ if (ret) {
+ dev_err(pil->dev, "Failed to vote for prng(rc:%d)\n", ret);
+ goto err_prng_vote;
+ }
+
+ ret = clk_prepare_enable(drv->axis2_clk);
+ if (ret) {
+ dev_err(pil->dev, "Failed to vote for axis2(rc:%d)\n", ret);
+ goto err_axis2_vote;
+ }
+
ret = regulator_set_voltage(drv->vreg_cx, uv, INT_MAX);
if (ret) {
dev_err(pil->dev, "Failed to request vdd_cx voltage(rc:%d)\n",
@@ -157,6 +169,10 @@ int pil_q6v5_make_proxy_votes(struct pil_desc *pil)
err_cx_mode:
regulator_set_voltage(drv->vreg_cx, 0, uv);
err_cx_voltage:
+ clk_disable_unprepare(drv->axis2_clk);
+err_axis2_vote:
+ clk_disable_unprepare(drv->prng_clk);
+err_prng_vote:
clk_disable_unprepare(drv->qdss_clk);
err_qdss_vote:
clk_disable_unprepare(drv->pnoc_clk);
@@ -189,6 +205,8 @@ void pil_q6v5_remove_proxy_votes(struct pil_desc *pil)
clk_disable_unprepare(drv->xo);
clk_disable_unprepare(drv->pnoc_clk);
clk_disable_unprepare(drv->qdss_clk);
+ clk_disable_unprepare(drv->prng_clk);
+ clk_disable_unprepare(drv->axis2_clk);
}
EXPORT_SYMBOL(pil_q6v5_remove_proxy_votes);
@@ -628,8 +646,8 @@ struct q6v5_data *pil_q6v5_init(struct platform_device *pdev)
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6_base");
drv->reg_base = devm_ioremap_resource(&pdev->dev, res);
- if (!drv->reg_base)
- return ERR_PTR(-ENOMEM);
+ if (IS_ERR(drv->reg_base))
+ return drv->reg_base;
desc = &drv->desc;
ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
@@ -763,6 +781,24 @@ struct q6v5_data *pil_q6v5_init(struct platform_device *pdev)
drv->qdss_clk = NULL;
}
+ if (of_property_match_string(pdev->dev.of_node,
+ "qcom,proxy-clock-names", "prng_clk") >= 0) {
+ drv->prng_clk = devm_clk_get(&pdev->dev, "prng_clk");
+ if (IS_ERR(drv->prng_clk))
+ return ERR_CAST(drv->prng_clk);
+ } else {
+ drv->prng_clk = NULL;
+ }
+
+ if (of_property_match_string(pdev->dev.of_node,
+ "qcom,proxy-clock-names", "axis2_clk") >= 0) {
+ drv->axis2_clk = devm_clk_get(&pdev->dev, "axis2_clk");
+ if (IS_ERR(drv->axis2_clk))
+ return ERR_CAST(drv->axis2_clk);
+ } else {
+ drv->axis2_clk = NULL;
+ }
+
drv->vreg_cx = devm_regulator_get(&pdev->dev, "vdd_cx");
if (IS_ERR(drv->vreg_cx))
return ERR_CAST(drv->vreg_cx);
diff --git a/drivers/soc/qcom/pil-q6v5.h b/drivers/soc/qcom/pil-q6v5.h
index 49aee97..1725253 100644
--- a/drivers/soc/qcom/pil-q6v5.h
+++ b/drivers/soc/qcom/pil-q6v5.h
@@ -1,5 +1,5 @@
/*
- * 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
@@ -35,6 +35,8 @@ struct q6v5_data {
struct clk *snoc_axi_clk;
struct clk *mnoc_axi_clk;
struct clk *qdss_clk;
+ struct clk *prng_clk;
+ struct clk *axis2_clk;
void __iomem *axi_halt_base; /* Halt base of q6, mss,
* nc are in same 4K page
*/
diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c
index aeecf29..1635bab 100644
--- a/drivers/soc/qcom/rpmh.c
+++ b/drivers/soc/qcom/rpmh.c
@@ -37,14 +37,13 @@
.msg = { 0 }, \
.msg.state = s, \
.msg.is_complete = true, \
- .msg.payload = &name.cmd, \
- .msg.num_payload = 1, \
- .cmd = { 0 }, \
+ .msg.payload = name.cmd, \
+ .msg.num_payload = 0, \
+ .cmd = { { 0 } }, \
.waitq = q, \
.wait_count = c, \
.rc = rc, \
.bit = -1, \
- .free_cmd = NULL, \
}
struct rpmh_req {
@@ -56,13 +55,11 @@ struct rpmh_req {
struct rpmh_msg {
struct tcs_mbox_msg msg;
- /* A single command for our use here */
- struct tcs_cmd cmd;
+ struct tcs_cmd cmd[MAX_RPMH_PAYLOAD];
wait_queue_head_t *waitq;
atomic_t *wait_count;
struct rpmh_client *rc;
int bit;
- void *free_cmd;
int err; /* relay error from mbox for sync calls */
};
@@ -120,7 +117,6 @@ static void rpmh_tx_done(struct mbox_client *cl, void *msg, int r)
struct rpmh_mbox *rpm = rpm_msg->rc->rpmh;
atomic_t *wc = rpm_msg->wait_count;
wait_queue_head_t *waitq = rpm_msg->waitq;
- void *free = rpm_msg->free_cmd;
rpm_msg->err = r;
@@ -139,7 +135,7 @@ static void rpmh_tx_done(struct mbox_client *cl, void *msg, int r)
/*
* Copy the child object pointers before freeing up the parent,
* This way even if the parent (rpm_msg) object gets reused, we
- * can free up the child objects (free_cmd and wq/wc) parallely.
+ * can free up the child objects (wq/wc) parallely.
* If you free up the children before the parent, then we run
* into an issue that the stack allocated parent object may be
* invalid before we can check the ->bit value.
@@ -152,9 +148,6 @@ static void rpmh_tx_done(struct mbox_client *cl, void *msg, int r)
spin_unlock(&rpm->lock);
}
- /* Nobody should be needing the request anymore */
- kfree(free);
-
/* Signal the blocking thread we are done */
if (waitq) {
atomic_dec(wc);
@@ -254,6 +247,9 @@ int __rpmh_write(struct rpmh_client *rc, enum rpmh_state state,
ret = mbox_send_message(rc->chan, &rpm_msg->msg);
if (ret > 0)
ret = 0;
+ } else {
+ /* Clean up our call by spoofing tx_done */
+ rpmh_tx_done(&rc->client, &rpm_msg->msg, ret);
}
return ret;
@@ -285,10 +281,10 @@ int rpmh_write_single_async(struct rpmh_client *rc, enum rpmh_state state,
if (!rpm_msg)
return -ENOMEM;
- rpm_msg->cmd.addr = addr;
- rpm_msg->cmd.data = data;
+ rpm_msg->cmd[0].addr = addr;
+ rpm_msg->cmd[0].data = data;
- rpm_msg->msg.payload = &rpm_msg->cmd;
+ rpm_msg->msg.payload = rpm_msg->cmd;
rpm_msg->msg.num_payload = 1;
return __rpmh_write(rc, state, rpm_msg);
@@ -325,8 +321,9 @@ int rpmh_write_single(struct rpmh_client *rc, enum rpmh_state state,
if (rpmh_standalone)
return 0;
- rpm_msg.cmd.addr = addr;
- rpm_msg.cmd.data = data;
+ rpm_msg.cmd[0].addr = addr;
+ rpm_msg.cmd[0].data = data;
+ rpm_msg.msg.num_payload = 1;
ret = __rpmh_write(rc, state, &rpm_msg);
if (ret < 0)
@@ -341,29 +338,22 @@ int rpmh_write_single(struct rpmh_client *rc, enum rpmh_state state,
EXPORT_SYMBOL(rpmh_write_single);
struct rpmh_msg *__get_rpmh_msg_async(struct rpmh_client *rc,
- enum rpmh_state state, struct tcs_cmd *cmd, int n, bool fast)
+ enum rpmh_state state, struct tcs_cmd *cmd, int n)
{
struct rpmh_msg *rpm_msg;
- struct tcs_cmd *tcs_cmd;
if (IS_ERR_OR_NULL(rc) || !cmd || n <= 0 || n > MAX_RPMH_PAYLOAD)
return ERR_PTR(-EINVAL);
- tcs_cmd = kcalloc(n, sizeof(*cmd), fast ? GFP_ATOMIC : GFP_KERNEL);
- if (!tcs_cmd)
- return ERR_PTR(-ENOMEM);
- memcpy(tcs_cmd, cmd, n * sizeof(*tcs_cmd));
-
rpm_msg = get_msg_from_pool(rc);
- if (!rpm_msg) {
- kfree(tcs_cmd);
+ if (!rpm_msg)
return ERR_PTR(-ENOMEM);
- }
+
+ memcpy(rpm_msg->cmd, cmd, n * sizeof(*cmd));
rpm_msg->msg.state = state;
- rpm_msg->msg.payload = tcs_cmd;
+ rpm_msg->msg.payload = rpm_msg->cmd;
rpm_msg->msg.num_payload = n;
- rpm_msg->free_cmd = tcs_cmd;
return rpm_msg;
}
@@ -389,8 +379,7 @@ int rpmh_write_async(struct rpmh_client *rc, enum rpmh_state state,
if (rpmh_standalone)
return 0;
- rpm_msg = __get_rpmh_msg_async(rc, state, cmd, n, true);
-
+ rpm_msg = __get_rpmh_msg_async(rc, state, cmd, n);
if (IS_ERR(rpm_msg))
return PTR_ERR(rpm_msg);
@@ -430,7 +419,7 @@ int rpmh_write(struct rpmh_client *rc, enum rpmh_state state,
if (rpmh_standalone)
return 0;
- rpm_msg.msg.payload = cmd;
+ memcpy(rpm_msg.cmd, cmd, n * sizeof(*cmd));
rpm_msg.msg.num_payload = n;
ret = __rpmh_write(rc, state, &rpm_msg);
@@ -502,7 +491,7 @@ int rpmh_write_passthru(struct rpmh_client *rc, enum rpmh_state state,
/* Create async request batches */
for (i = 0; i < count; i++) {
- rpm_msg[i] = __get_rpmh_msg_async(rc, state, cmd, n[i], false);
+ rpm_msg[i] = __get_rpmh_msg_async(rc, state, cmd, n[i]);
if (IS_ERR_OR_NULL(rpm_msg[i]))
return PTR_ERR(rpm_msg[i]);
rpm_msg[i]->waitq = &waitq;
@@ -551,15 +540,16 @@ int rpmh_write_control(struct rpmh_client *rc, struct tcs_cmd *cmd, int n)
{
DEFINE_RPMH_MSG_ONSTACK(rc, 0, NULL, NULL, rpm_msg);
- if (IS_ERR_OR_NULL(rc))
+ if (IS_ERR_OR_NULL(rc) || n > MAX_RPMH_PAYLOAD)
return -EINVAL;
if (rpmh_standalone)
return 0;
- rpm_msg.msg.payload = cmd;
+ memcpy(rpm_msg.cmd, cmd, n * sizeof(*cmd));
rpm_msg.msg.num_payload = n;
rpm_msg.msg.is_control = true;
+ rpm_msg.msg.is_complete = false;
return mbox_send_controller_data(rc->chan, &rpm_msg.msg);
}
@@ -587,6 +577,7 @@ int rpmh_invalidate(struct rpmh_client *rc)
rpm = rc->rpmh;
rpm_msg.msg.invalidate = true;
+ rpm_msg.msg.is_complete = false;
spin_lock(&rpm->lock);
rpm->dirty = true;
@@ -621,8 +612,9 @@ int rpmh_read(struct rpmh_client *rc, u32 addr, u32 *resp)
if (rpmh_standalone)
return 0;
- rpm_msg.cmd.addr = addr;
- rpm_msg.cmd.data = 0;
+ rpm_msg.cmd[0].addr = addr;
+ rpm_msg.cmd[0].data = 0;
+ rpm_msg.msg.num_payload = 1;
rpm_msg.msg.is_read = true;
@@ -636,7 +628,7 @@ int rpmh_read(struct rpmh_client *rc, u32 addr, u32 *resp)
return ret;
/* Read the data back from the tcs_mbox_msg structrure */
- *resp = rpm_msg.cmd.data;
+ *resp = rpm_msg.cmd[0].data;
return rpm_msg.err;
}
@@ -655,8 +647,10 @@ int send_single(struct rpmh_client *rc, enum rpmh_state state, u32 addr,
/* Wake sets are always complete and sleep sets are not */
rpm_msg.msg.is_complete = (state == RPMH_WAKE_ONLY_STATE);
- rpm_msg.cmd.addr = addr;
- rpm_msg.cmd.data = data;
+ rpm_msg.cmd[0].addr = addr;
+ rpm_msg.cmd[0].data = data;
+ rpm_msg.msg.num_payload = 1;
+ rpm_msg.msg.is_complete = false;
return mbox_send_controller_data(rc->chan, &rpm_msg.msg);
}
diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c
index b40d678..6a54048 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>
@@ -441,140 +440,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",
- 0644, servloc_base_dir,
- "client_name", &servloc_fops);
- rc = !test_servloc_file ? -ENOMEM : 0;
-
- if (rc == 0) {
- test_servloc_file = debugfs_create_file("service_name",
- 0644, 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",
- 0644, 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 fca1c68..68592fe 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>
@@ -104,6 +103,7 @@ struct qmi_client_info {
struct work_struct svc_exit;
struct work_struct svc_rcv_msg;
struct work_struct ind_ack;
+ struct work_struct qmi_handle_free;
struct workqueue_struct *svc_event_wq;
struct qmi_handle *clnt_handle;
struct notifier_block notifier;
@@ -123,6 +123,18 @@ static void root_service_clnt_recv_msg(struct work_struct *work);
static void root_service_service_arrive(struct work_struct *work);
static void root_service_exit_work(struct work_struct *work);
+static void free_qmi_handle(struct work_struct *work)
+{
+ struct qmi_client_info *data = container_of(work,
+ struct qmi_client_info, qmi_handle_free);
+
+ mutex_lock(&qmi_client_release_lock);
+ data->service_connected = false;
+ qmi_handle_destroy(data->clnt_handle);
+ data->clnt_handle = NULL;
+ mutex_unlock(&qmi_client_release_lock);
+}
+
static struct service_notif_info *_find_service_info(const char *service_path)
{
struct service_notif_info *service_notif;
@@ -426,11 +438,7 @@ static void root_service_service_exit(struct qmi_client_info *data,
* Destroy client handle and try connecting when
* service comes up again.
*/
- mutex_lock(&qmi_client_release_lock);
- data->service_connected = false;
- qmi_handle_destroy(data->clnt_handle);
- data->clnt_handle = NULL;
- mutex_unlock(&qmi_client_release_lock);
+ queue_work(data->svc_event_wq, &data->qmi_handle_free);
}
static void root_service_exit_work(struct work_struct *work)
@@ -486,7 +494,7 @@ static int ssr_event_notify(struct notifier_block *this,
info->subsys_state = ROOT_PD_SHUTDOWN;
break;
}
- queue_work(info->svc_event_wq, &info->svc_exit);
+ root_service_service_exit(info, info->subsys_state);
break;
default:
break;
@@ -561,6 +569,7 @@ static void *add_service_notif(const char *service_path, int instance_id,
INIT_WORK(&qmi_data->svc_exit, root_service_exit_work);
INIT_WORK(&qmi_data->svc_rcv_msg, root_service_clnt_recv_msg);
INIT_WORK(&qmi_data->ind_ack, send_ind_ack);
+ INIT_WORK(&qmi_data->qmi_handle_free, free_qmi_handle);
*curr_state = service_notif->curr_state =
SERVREG_NOTIF_SERVICE_STATE_UNINIT_V01;
@@ -742,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",
- 0644, service_notifier_dir, NULL,
- &service_notifier_register_fops);
- if (!service_path_file)
- goto err;
- service_id_file = debugfs_create_file("service_id",
- 0644, 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 55fa5d2..119ede3 100644
--- a/drivers/soc/qcom/socinfo.c
+++ b/drivers/soc/qcom/socinfo.c
@@ -550,6 +550,9 @@ static struct msm_soc_info cpu_of_id[] = {
/* Bat ID */
[328] = {MSM_CPU_SDM830, "SDM830"},
+ /* sdxpoorwills ID */
+ [334] = {SDX_CPU_SDXPOORWILLS, "SDXPOORWILLS"},
+
/* Uninitialized IDs are not known to run Linux.
* MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are
* considered as unknown CPU.
@@ -1256,6 +1259,10 @@ static void * __init setup_dummy_socinfo(void)
dummy_socinfo.id = 328;
strlcpy(dummy_socinfo.build_id, "sdm830 - ",
sizeof(dummy_socinfo.build_id));
+ } else if (early_machine_is_sdxpoorwills()) {
+ dummy_socinfo.id = 334;
+ strlcpy(dummy_socinfo.build_id, "sdxpoorwills - ",
+ sizeof(dummy_socinfo.build_id));
}
strlcat(dummy_socinfo.build_id, "Dummy socinfo",
diff --git a/drivers/soc/qcom/spss_utils.c b/drivers/soc/qcom/spss_utils.c
new file mode 100644
index 0000000..c70ef3a
--- /dev/null
+++ b/drivers/soc/qcom/spss_utils.c
@@ -0,0 +1,439 @@
+/*
+ * 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
+ * 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.
+ */
+
+/*
+ * Secure-Processor-SubSystem (SPSS) utilities.
+ *
+ * This driver provides utilities for the Secure Processor (SP).
+ *
+ * The SP daemon needs to load different SPSS images based on:
+ *
+ * 1. Test/Production key used to sign the SPSS image (read fuses).
+ * 2. SPSS HW version (selected via Device Tree).
+ *
+ */
+
+#define pr_fmt(fmt) "spss_utils [%s]: " fmt, __func__
+
+#include <linux/kernel.h> /* min() */
+#include <linux/module.h> /* MODULE_LICENSE */
+#include <linux/device.h> /* class_create() */
+#include <linux/slab.h> /* kzalloc() */
+#include <linux/fs.h> /* file_operations */
+#include <linux/cdev.h> /* cdev_add() */
+#include <linux/errno.h> /* EINVAL, ETIMEDOUT */
+#include <linux/printk.h> /* pr_err() */
+#include <linux/bitops.h> /* BIT(x) */
+#include <linux/platform_device.h> /* platform_driver_register() */
+#include <linux/of.h> /* of_property_count_strings() */
+#include <linux/io.h> /* ioremap_nocache() */
+
+#include <soc/qcom/subsystem_restart.h>
+
+/* driver name */
+#define DEVICE_NAME "spss-utils"
+
+enum spss_firmware_type {
+ SPSS_FW_TYPE_DEV = 'd',
+ SPSS_FW_TYPE_TEST = 't',
+ SPSS_FW_TYPE_PROD = 'p',
+};
+
+static enum spss_firmware_type firmware_type = SPSS_FW_TYPE_TEST;
+static const char *dev_firmware_name;
+static const char *test_firmware_name;
+static const char *prod_firmware_name;
+static const char *firmware_name = "NA";
+static struct device *spss_dev;
+static u32 spss_debug_reg_addr; /* SP_SCSR_MBn_SP2CL_GPm(n,m) */
+
+/*==========================================================================*/
+/* Device Sysfs */
+/*==========================================================================*/
+
+static ssize_t firmware_name_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+
+ if (!dev || !attr || !buf) {
+ pr_err("invalid param.\n");
+ return -EINVAL;
+ }
+
+ if (firmware_name == NULL)
+ ret = snprintf(buf, PAGE_SIZE, "%s\n", "unknown");
+ else
+ ret = snprintf(buf, PAGE_SIZE, "%s\n", firmware_name);
+
+ return ret;
+}
+
+static ssize_t firmware_name_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ pr_err("set firmware name is not allowed.\n");
+
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(firmware_name, 0444,
+ firmware_name_show, firmware_name_store);
+
+static ssize_t test_fuse_state_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+
+ if (!dev || !attr || !buf) {
+ pr_err("invalid param.\n");
+ return -EINVAL;
+ }
+
+ switch (firmware_type) {
+ case SPSS_FW_TYPE_DEV:
+ ret = snprintf(buf, PAGE_SIZE, "%s", "dev");
+ break;
+ case SPSS_FW_TYPE_TEST:
+ ret = snprintf(buf, PAGE_SIZE, "%s", "test");
+ break;
+ case SPSS_FW_TYPE_PROD:
+ ret = snprintf(buf, PAGE_SIZE, "%s", "prod");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static ssize_t test_fuse_state_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ pr_err("set test fuse state is not allowed.\n");
+
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(test_fuse_state, 0444,
+ test_fuse_state_show, test_fuse_state_store);
+
+static ssize_t spss_debug_reg_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ void __iomem *spss_debug_reg = NULL;
+ u32 val1, val2;
+
+ if (!dev || !attr || !buf) {
+ pr_err("invalid param.\n");
+ return -EINVAL;
+ }
+
+ pr_debug("spss_debug_reg_addr [0x%x].\n", spss_debug_reg_addr);
+
+ spss_debug_reg = ioremap_nocache(spss_debug_reg_addr, sizeof(u32)*2);
+
+ if (!spss_debug_reg) {
+ pr_err("can't map debug reg addr.\n");
+ return -EFAULT;
+ }
+
+ val1 = readl_relaxed(spss_debug_reg);
+ val2 = readl_relaxed(((char *) spss_debug_reg) + sizeof(u32));
+
+ ret = snprintf(buf, PAGE_SIZE, "val1 [0x%x] val2 [0x%x]", val1, val2);
+
+ iounmap(spss_debug_reg);
+
+ return ret;
+}
+
+static ssize_t spss_debug_reg_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ pr_err("set debug reg is not allowed.\n");
+
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(spss_debug_reg, 0444,
+ spss_debug_reg_show, spss_debug_reg_store);
+
+static int spss_create_sysfs(struct device *dev)
+{
+ int ret;
+
+ ret = device_create_file(dev, &dev_attr_firmware_name);
+ if (ret < 0) {
+ pr_err("failed to create sysfs file for firmware_name.\n");
+ return ret;
+ }
+
+ ret = device_create_file(dev, &dev_attr_test_fuse_state);
+ if (ret < 0) {
+ pr_err("failed to create sysfs file for test_fuse_state.\n");
+ device_remove_file(dev, &dev_attr_firmware_name);
+ return ret;
+ }
+
+ ret = device_create_file(dev, &dev_attr_spss_debug_reg);
+ if (ret < 0) {
+ pr_err("failed to create sysfs file for spss_debug_reg.\n");
+ device_remove_file(dev, &dev_attr_firmware_name);
+ device_remove_file(dev, &dev_attr_test_fuse_state);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*==========================================================================*/
+/* Device Tree */
+/*==========================================================================*/
+
+/**
+ * spss_parse_dt() - Parse Device Tree info.
+ */
+static int spss_parse_dt(struct device_node *node)
+{
+ int ret;
+ u32 spss_fuse1_addr = 0;
+ u32 spss_fuse1_bit = 0;
+ u32 spss_fuse1_mask = 0;
+ void __iomem *spss_fuse1_reg = NULL;
+ u32 spss_fuse2_addr = 0;
+ u32 spss_fuse2_bit = 0;
+ u32 spss_fuse2_mask = 0;
+ void __iomem *spss_fuse2_reg = NULL;
+ u32 val1 = 0;
+ u32 val2 = 0;
+
+ ret = of_property_read_string(node, "qcom,spss-dev-firmware-name",
+ &dev_firmware_name);
+ if (ret < 0) {
+ pr_err("can't get dev fw name.\n");
+ return -EFAULT;
+ }
+
+ ret = of_property_read_string(node, "qcom,spss-test-firmware-name",
+ &test_firmware_name);
+ if (ret < 0) {
+ pr_err("can't get test fw name.\n");
+ return -EFAULT;
+ }
+
+ ret = of_property_read_string(node, "qcom,spss-prod-firmware-name",
+ &prod_firmware_name);
+ if (ret < 0) {
+ pr_err("can't get prod fw name.\n");
+ return -EFAULT;
+ }
+
+ ret = of_property_read_u32(node, "qcom,spss-fuse1-addr",
+ &spss_fuse1_addr);
+ if (ret < 0) {
+ pr_err("can't get fuse1 addr.\n");
+ return -EFAULT;
+ }
+
+ ret = of_property_read_u32(node, "qcom,spss-fuse2-addr",
+ &spss_fuse2_addr);
+ if (ret < 0) {
+ pr_err("can't get fuse2 addr.\n");
+ return -EFAULT;
+ }
+
+ ret = of_property_read_u32(node, "qcom,spss-fuse1-bit",
+ &spss_fuse1_bit);
+ if (ret < 0) {
+ pr_err("can't get fuse1 bit.\n");
+ return -EFAULT;
+ }
+
+ ret = of_property_read_u32(node, "qcom,spss-fuse2-bit",
+ &spss_fuse2_bit);
+ if (ret < 0) {
+ pr_err("can't get fuse2 bit.\n");
+ return -EFAULT;
+ }
+
+
+ spss_fuse1_mask = BIT(spss_fuse1_bit);
+ spss_fuse2_mask = BIT(spss_fuse2_bit);
+
+ pr_debug("spss fuse1 addr [0x%x] bit [%d] .\n",
+ (int) spss_fuse1_addr, (int) spss_fuse1_bit);
+ pr_debug("spss fuse2 addr [0x%x] bit [%d] .\n",
+ (int) spss_fuse2_addr, (int) spss_fuse2_bit);
+
+ spss_fuse1_reg = ioremap_nocache(spss_fuse1_addr, sizeof(u32));
+
+ if (!spss_fuse1_reg) {
+ pr_err("can't map fuse1 addr.\n");
+ return -EFAULT;
+ }
+
+ spss_fuse2_reg = ioremap_nocache(spss_fuse2_addr, sizeof(u32));
+
+ if (!spss_fuse2_reg) {
+ iounmap(spss_fuse1_reg);
+ pr_err("can't map fuse2 addr.\n");
+ return -EFAULT;
+ }
+
+ val1 = readl_relaxed(spss_fuse1_reg);
+ val2 = readl_relaxed(spss_fuse2_reg);
+
+ pr_debug("spss fuse1 value [0x%08x].\n", (int) val1);
+ pr_debug("spss fuse2 value [0x%08x].\n", (int) val2);
+
+ pr_debug("spss fuse1 mask [0x%08x].\n", (int) spss_fuse1_mask);
+ pr_debug("spss fuse2 mask [0x%08x].\n", (int) spss_fuse2_mask);
+
+ /**
+ * Set firmware_type based on fuses:
+ * SPSS_CONFIG_MODE 11: dev
+ * SPSS_CONFIG_MODE 01 or 10: test
+ * SPSS_CONFIG_MODE 00: prod
+ */
+ if ((val1 & spss_fuse1_mask) && (val2 & spss_fuse2_mask))
+ firmware_type = SPSS_FW_TYPE_DEV;
+ else if ((val1 & spss_fuse1_mask) || (val2 & spss_fuse2_mask))
+ firmware_type = SPSS_FW_TYPE_TEST;
+ else
+ firmware_type = SPSS_FW_TYPE_PROD;
+
+ iounmap(spss_fuse1_reg);
+ iounmap(spss_fuse2_reg);
+
+ ret = of_property_read_u32(node, "qcom,spss-debug-reg-addr",
+ &spss_debug_reg_addr);
+ if (ret < 0) {
+ pr_err("can't get debug regs addr.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * spss_probe() - initialization sequence
+ */
+static int spss_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct device_node *np = NULL;
+ struct device *dev = NULL;
+
+ if (!pdev) {
+ pr_err("invalid pdev.\n");
+ return -ENODEV;
+ }
+
+ np = pdev->dev.of_node;
+ if (!np) {
+ pr_err("invalid DT node.\n");
+ return -EINVAL;
+ }
+
+ dev = &pdev->dev;
+ spss_dev = dev;
+
+ if (dev == NULL) {
+ pr_err("invalid dev.\n");
+ return -EINVAL;
+ }
+
+ platform_set_drvdata(pdev, dev);
+
+ ret = spss_parse_dt(np);
+ if (ret < 0) {
+ pr_err("fail to parse device tree.\n");
+ return -EFAULT;
+ }
+
+ switch (firmware_type) {
+ case SPSS_FW_TYPE_DEV:
+ firmware_name = dev_firmware_name;
+ break;
+ case SPSS_FW_TYPE_TEST:
+ firmware_name = test_firmware_name;
+ break;
+ case SPSS_FW_TYPE_PROD:
+ firmware_name = prod_firmware_name;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = subsystem_set_fwname("spss", firmware_name);
+ if (ret < 0) {
+ pr_err("fail to set fw name.\n");
+ return -EFAULT;
+ }
+
+ ret = spss_create_sysfs(dev);
+ if (ret < 0) {
+ pr_err("fail to create sysfs.\n");
+ return -EFAULT;
+ }
+
+ pr_info("Initialization completed ok, firmware_name [%s].\n",
+ firmware_name);
+
+ return 0;
+}
+
+static const struct of_device_id spss_match_table[] = {
+ { .compatible = "qcom,spss-utils", },
+ { },
+};
+
+static struct platform_driver spss_driver = {
+ .probe = spss_probe,
+ .driver = {
+ .name = DEVICE_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(spss_match_table),
+ },
+};
+
+/*==========================================================================*/
+/* Driver Init/Exit */
+/*==========================================================================*/
+static int __init spss_init(void)
+{
+ int ret = 0;
+
+ pr_info("spss-utils driver Ver 2.0 30-Mar-2017.\n");
+
+ ret = platform_driver_register(&spss_driver);
+ if (ret)
+ pr_err("register platform driver failed, ret [%d]\n", ret);
+
+ return ret;
+}
+late_initcall(spss_init); /* start after PIL driver */
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Secure Processor Utilities");
diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c
index b5e3814..272e70a 100644
--- a/drivers/soc/qcom/subsys-pil-tz.c
+++ b/drivers/soc/qcom/subsys-pil-tz.c
@@ -536,6 +536,17 @@ static int pil_make_proxy_vote(struct pil_desc *pil)
if (d->subsys_desc.no_auth)
return 0;
+ if (d->bus_client) {
+ rc = msm_bus_scale_client_update_request(d->bus_client, 1);
+ if (rc) {
+ dev_err(pil->dev, "bandwidth request failed(rc:%d)\n",
+ rc);
+ return rc;
+ }
+ } else
+ WARN(d->enable_bus_scaling, "Bus scaling not set up for %s!\n",
+ d->subsys_desc.name);
+
rc = enable_regulators(d, pil->dev, d->proxy_regs,
d->proxy_reg_count, false);
if (rc)
@@ -546,20 +557,8 @@ static int pil_make_proxy_vote(struct pil_desc *pil)
if (rc)
goto err_clks;
- if (d->bus_client) {
- rc = msm_bus_scale_client_update_request(d->bus_client, 1);
- if (rc) {
- dev_err(pil->dev, "bandwidth request failed(rc:%d)\n",
- rc);
- goto err_bw;
- }
- } else
- WARN(d->enable_bus_scaling, "Bus scaling not set up for %s!\n",
- d->subsys_desc.name);
-
return 0;
-err_bw:
- disable_unprepare_clocks(d->proxy_clks, d->proxy_clk_count);
+
err_clks:
disable_regulators(d, d->proxy_regs, d->proxy_reg_count, false);
@@ -573,15 +572,15 @@ static void pil_remove_proxy_vote(struct pil_desc *pil)
if (d->subsys_desc.no_auth)
return;
+ disable_unprepare_clocks(d->proxy_clks, d->proxy_clk_count);
+
+ disable_regulators(d, d->proxy_regs, d->proxy_reg_count, true);
+
if (d->bus_client)
msm_bus_scale_client_update_request(d->bus_client, 0);
else
WARN(d->enable_bus_scaling, "Bus scaling not set up for %s!\n",
d->subsys_desc.name);
-
- disable_unprepare_clocks(d->proxy_clks, d->proxy_clk_count);
-
- disable_regulators(d, d->proxy_regs, d->proxy_reg_count, true);
}
static int pil_init_image_trusted(struct pil_desc *pil,
@@ -693,6 +692,10 @@ static int pil_auth_and_reset(struct pil_desc *pil)
desc.args[0] = proc = d->pas_id;
desc.arginfo = SCM_ARGS(1);
+ rc = scm_pas_enable_bw();
+ if (rc)
+ return rc;
+
rc = enable_regulators(d, pil->dev, d->regs, d->reg_count, false);
if (rc)
return rc;
@@ -701,10 +704,6 @@ static int pil_auth_and_reset(struct pil_desc *pil)
if (rc)
goto err_clks;
- rc = scm_pas_enable_bw();
- if (rc)
- goto err_reset;
-
if (!is_scm_armv8()) {
rc = scm_call(SCM_SVC_PIL, PAS_AUTH_AND_RESET_CMD, &proc,
sizeof(proc), &scm_ret, sizeof(scm_ret));
diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c
index c846d26..e7c2bb2 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;
@@ -354,10 +349,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);
@@ -1172,87 +1168,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,
- 0644, 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;
@@ -1690,17 +1605,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);
}
@@ -1761,7 +1667,6 @@ struct subsys_device *subsys_register(struct subsys_desc *desc)
if (ofnode)
subsys_remove_restart_order(ofnode);
err_register:
- subsys_debugfs_remove(subsys);
device_unregister(&subsys->dev);
return ERR_PTR(ret);
}
@@ -1790,7 +1695,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)
@@ -1830,9 +1734,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)) {
@@ -1851,8 +1752,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/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c
index 7e33e8b..ce2a367 100644
--- a/drivers/soundwire/swr-wcd-ctrl.c
+++ b/drivers/soundwire/swr-wcd-ctrl.c
@@ -524,7 +524,7 @@ static int swrm_read(struct swr_master *master, u8 dev_num, u16 reg_addr,
{
struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master);
int ret = 0;
- int val = 0;
+ int val;
u8 *reg_val = (u8 *)buf;
if (!swrm) {
@@ -538,7 +538,9 @@ static int swrm_read(struct swr_master *master, u8 dev_num, u16 reg_addr,
else
val = swrm->read(swrm->handle, reg_addr);
- *reg_val = (u8)val;
+ if (!ret)
+ *reg_val = (u8)val;
+
pm_runtime_mark_last_busy(&swrm->pdev->dev);
return ret;
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index 403c799..db12900 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -16,6 +16,7 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/pm_runtime.h>
#include <linux/qcom-geni-se.h>
#include <linux/spi/spi.h>
diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig
index 0d3b70b..633632a 100644
--- a/drivers/spmi/Kconfig
+++ b/drivers/spmi/Kconfig
@@ -24,4 +24,14 @@
This is required for communicating with Qualcomm PMICs and
other devices that have the SPMI interface.
+config SPMI_MSM_PMIC_ARB_DEBUG
+ tristate "QTI SPMI Debug Controller (PMIC Arbiter)"
+ depends on ARCH_QCOM || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ If you say yes to this option, support will be included for the
+ built-in SPMI PMIC Arbiter debug interface on Qualcomm Technologies,
+ Inc. (QTI) MSM family processors. This feature is available on chips
+ with PMIC arbiter version 5 and above.
+
endif
diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile
index fc75104..4f20815 100644
--- a/drivers/spmi/Makefile
+++ b/drivers/spmi/Makefile
@@ -4,3 +4,4 @@
obj-$(CONFIG_SPMI) += spmi.o
obj-$(CONFIG_SPMI_MSM_PMIC_ARB) += spmi-pmic-arb.o
+obj-$(CONFIG_SPMI_MSM_PMIC_ARB_DEBUG) += spmi-pmic-arb-debug.o
diff --git a/drivers/spmi/spmi-pmic-arb-debug.c b/drivers/spmi/spmi-pmic-arb-debug.c
new file mode 100644
index 0000000..c5a31a9
--- /dev/null
+++ b/drivers/spmi/spmi-pmic-arb-debug.c
@@ -0,0 +1,354 @@
+/*
+ * 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+
+/* PMIC Arbiter debug register offsets */
+#define PMIC_ARB_DEBUG_CMD0 0x00
+#define PMIC_ARB_DEBUG_CMD1 0x04
+#define PMIC_ARB_DEBUG_CMD2 0x08
+#define PMIC_ARB_DEBUG_CMD3 0x0C
+#define PMIC_ARB_DEBUG_STATUS 0x14
+#define PMIC_ARB_DEBUG_WDATA(n) (0x18 + 4 * (n))
+#define PMIC_ARB_DEBUG_RDATA(n) (0x38 + 4 * (n))
+
+/* Transaction status flag bits */
+enum pmic_arb_chnl_status {
+ PMIC_ARB_STATUS_DONE = BIT(0),
+ PMIC_ARB_STATUS_FAILURE = BIT(1),
+ PMIC_ARB_STATUS_DENIED = BIT(2),
+ PMIC_ARB_STATUS_DROPPED = BIT(3),
+};
+
+/* Command Opcodes */
+enum pmic_arb_cmd_op_code {
+ PMIC_ARB_OP_EXT_WRITEL = 0,
+ PMIC_ARB_OP_EXT_READL = 1,
+ PMIC_ARB_OP_EXT_WRITE = 2,
+ PMIC_ARB_OP_RESET = 3,
+ PMIC_ARB_OP_SLEEP = 4,
+ PMIC_ARB_OP_SHUTDOWN = 5,
+ PMIC_ARB_OP_WAKEUP = 6,
+ PMIC_ARB_OP_AUTHENTICATE = 7,
+ PMIC_ARB_OP_MSTR_READ = 8,
+ PMIC_ARB_OP_MSTR_WRITE = 9,
+ PMIC_ARB_OP_EXT_READ = 13,
+ PMIC_ARB_OP_WRITE = 14,
+ PMIC_ARB_OP_READ = 15,
+ PMIC_ARB_OP_ZERO_WRITE = 16,
+};
+
+#define PMIC_ARB_TIMEOUT_US 100
+#define PMIC_ARB_MAX_TRANS_BYTES 8
+#define PMIC_ARB_MAX_SID 0xF
+
+/**
+ * spmi_pmic_arb_debug - SPMI PMIC Arbiter debug object
+ *
+ * @addr: base address of SPMI PMIC arbiter debug module
+ * @lock: lock to synchronize accesses.
+ */
+struct spmi_pmic_arb_debug {
+ void __iomem *addr;
+ raw_spinlock_t lock;
+};
+
+static inline void pmic_arb_debug_write(struct spmi_pmic_arb_debug *pa,
+ u32 offset, u32 val)
+{
+ writel_relaxed(val, pa->addr + offset);
+}
+
+static inline u32 pmic_arb_debug_read(struct spmi_pmic_arb_debug *pa,
+ u32 offset)
+{
+ return readl_relaxed(pa->addr + offset);
+}
+
+/* pa->lock must be held by the caller. */
+static int pmic_arb_debug_wait_for_done(struct spmi_controller *ctrl)
+{
+ struct spmi_pmic_arb_debug *pa = spmi_controller_get_drvdata(ctrl);
+ u32 status = 0;
+ u32 timeout = PMIC_ARB_TIMEOUT_US;
+
+ while (timeout--) {
+ status = pmic_arb_debug_read(pa, PMIC_ARB_DEBUG_STATUS);
+
+ if (status & PMIC_ARB_STATUS_DONE) {
+ if (status & PMIC_ARB_STATUS_DENIED) {
+ dev_err(&ctrl->dev, "%s: transaction denied (0x%x)\n",
+ __func__, status);
+ return -EPERM;
+ }
+
+ if (status & PMIC_ARB_STATUS_FAILURE) {
+ dev_err(&ctrl->dev, "%s: transaction failed (0x%x)\n",
+ __func__, status);
+ return -EIO;
+ }
+
+ if (status & PMIC_ARB_STATUS_DROPPED) {
+ dev_err(&ctrl->dev, "%s: transaction dropped (0x%x)\n",
+ __func__, status);
+ return -EIO;
+ }
+
+ return 0;
+ }
+ udelay(1);
+ }
+
+ dev_err(&ctrl->dev, "%s: timeout, status 0x%x\n", __func__, status);
+ return -ETIMEDOUT;
+}
+
+/* pa->lock must be held by the caller. */
+static int pmic_arb_debug_issue_command(struct spmi_controller *ctrl, u8 opc,
+ u8 sid, u16 addr, size_t len)
+{
+ struct spmi_pmic_arb_debug *pa = spmi_controller_get_drvdata(ctrl);
+ u16 pid = (addr >> 8) & 0xFF;
+ u16 offset = addr & 0xFF;
+ u8 byte_count = len - 1;
+
+ if (byte_count >= PMIC_ARB_MAX_TRANS_BYTES) {
+ dev_err(&ctrl->dev, "pmic-arb supports 1 to %d bytes per transaction, but %zu requested",
+ PMIC_ARB_MAX_TRANS_BYTES, len);
+ return -EINVAL;
+ }
+
+ if (sid > PMIC_ARB_MAX_SID) {
+ dev_err(&ctrl->dev, "pmic-arb supports sid 0 to %u, but %u requested",
+ PMIC_ARB_MAX_SID, sid);
+ return -EINVAL;
+ }
+
+ pmic_arb_debug_write(pa, PMIC_ARB_DEBUG_CMD3, offset);
+ pmic_arb_debug_write(pa, PMIC_ARB_DEBUG_CMD2, pid);
+ pmic_arb_debug_write(pa, PMIC_ARB_DEBUG_CMD1, (byte_count << 4) | sid);
+
+ /* Start the transaction */
+ pmic_arb_debug_write(pa, PMIC_ARB_DEBUG_CMD0, opc << 1);
+
+ return pmic_arb_debug_wait_for_done(ctrl);
+}
+
+/* Non-data command */
+static int pmic_arb_debug_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
+{
+ dev_dbg(&ctrl->dev, "cmd op:0x%x sid:%d\n", opc, sid);
+
+ /* Check for valid non-data command */
+ if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP)
+ return -EINVAL;
+
+ return -EOPNOTSUPP;
+}
+
+static int pmic_arb_debug_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
+ u16 addr, u8 *buf, size_t len)
+{
+ struct spmi_pmic_arb_debug *pa = spmi_controller_get_drvdata(ctrl);
+ unsigned long flags;
+ int i, rc;
+
+ /* Check the opcode */
+ if (opc >= 0x60 && opc <= 0x7F)
+ opc = PMIC_ARB_OP_READ;
+ else if (opc >= 0x20 && opc <= 0x2F)
+ opc = PMIC_ARB_OP_EXT_READ;
+ else if (opc >= 0x38 && opc <= 0x3F)
+ opc = PMIC_ARB_OP_EXT_READL;
+ else
+ return -EINVAL;
+
+ raw_spin_lock_irqsave(&pa->lock, flags);
+
+ rc = pmic_arb_debug_issue_command(ctrl, opc, sid, addr, len);
+ if (rc)
+ goto done;
+
+ /* Read data from FIFO */
+ for (i = 0; i < len; i++)
+ buf[i] = pmic_arb_debug_read(pa, PMIC_ARB_DEBUG_RDATA(i));
+done:
+ raw_spin_unlock_irqrestore(&pa->lock, flags);
+
+ return rc;
+}
+
+static int pmic_arb_debug_write_cmd(struct spmi_controller *ctrl, u8 opc,
+ u8 sid, u16 addr, const u8 *buf, size_t len)
+{
+ struct spmi_pmic_arb_debug *pa = spmi_controller_get_drvdata(ctrl);
+ unsigned long flags;
+ int i, rc;
+
+ if (len > PMIC_ARB_MAX_TRANS_BYTES) {
+ dev_err(&ctrl->dev, "pmic-arb supports 1 to %d bytes per transaction, but %zu requested",
+ PMIC_ARB_MAX_TRANS_BYTES, len);
+ return -EINVAL;
+ }
+
+ /* Check the opcode */
+ if (opc >= 0x40 && opc <= 0x5F)
+ opc = PMIC_ARB_OP_WRITE;
+ else if (opc >= 0x00 && opc <= 0x0F)
+ opc = PMIC_ARB_OP_EXT_WRITE;
+ else if (opc >= 0x30 && opc <= 0x37)
+ opc = PMIC_ARB_OP_EXT_WRITEL;
+ else if (opc >= 0x80)
+ opc = PMIC_ARB_OP_ZERO_WRITE;
+ else
+ return -EINVAL;
+
+ raw_spin_lock_irqsave(&pa->lock, flags);
+
+ /* Write data to FIFO */
+ for (i = 0; i < len; i++)
+ pmic_arb_debug_write(pa, PMIC_ARB_DEBUG_WDATA(i), buf[i]);
+
+ rc = pmic_arb_debug_issue_command(ctrl, opc, sid, addr, len);
+
+ raw_spin_unlock_irqrestore(&pa->lock, flags);
+
+ return rc;
+}
+
+static int spmi_pmic_arb_debug_probe(struct platform_device *pdev)
+{
+ struct spmi_pmic_arb_debug *pa;
+ struct spmi_controller *ctrl;
+ struct resource *res;
+ int rc;
+ u32 fuse_val, fuse_bit;
+ void __iomem *fuse_addr;
+
+ /* Check if the debug bus is disabled by a fuse. */
+ rc = of_property_read_u32(pdev->dev.of_node, "qcom,fuse-disable-bit",
+ &fuse_bit);
+ if (!rc) {
+ if (fuse_bit > 31) {
+ dev_err(&pdev->dev, "qcom,fuse-disable-bit supports values 0 to 31, but %u specified\n",
+ fuse_bit);
+ return -EINVAL;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "fuse");
+ if (!res) {
+ dev_err(&pdev->dev, "fuse address not specified\n");
+ return -EINVAL;
+ }
+
+ fuse_addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fuse_addr))
+ return PTR_ERR(fuse_addr);
+
+ fuse_val = readl_relaxed(fuse_addr);
+ devm_iounmap(&pdev->dev, fuse_addr);
+
+ if (fuse_val & BIT(fuse_bit)) {
+ dev_err(&pdev->dev, "SPMI PMIC arbiter debug bus disabled by fuse\n");
+ return -ENODEV;
+ }
+ }
+
+
+ ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa));
+ if (!ctrl)
+ return -ENOMEM;
+
+ pa = spmi_controller_get_drvdata(ctrl);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
+ if (!res) {
+ dev_err(&pdev->dev, "core address not specified\n");
+ rc = -EINVAL;
+ goto err_put_ctrl;
+ }
+
+ pa->addr = devm_ioremap_resource(&ctrl->dev, res);
+ if (IS_ERR(pa->addr)) {
+ rc = PTR_ERR(pa->addr);
+ goto err_put_ctrl;
+ }
+
+ platform_set_drvdata(pdev, ctrl);
+ raw_spin_lock_init(&pa->lock);
+
+ ctrl->cmd = pmic_arb_debug_cmd;
+ ctrl->read_cmd = pmic_arb_debug_read_cmd;
+ ctrl->write_cmd = pmic_arb_debug_write_cmd;
+
+ rc = spmi_controller_add(ctrl);
+ if (rc)
+ goto err_put_ctrl;
+
+ dev_info(&ctrl->dev, "SPMI PMIC arbiter debug bus controller added\n");
+
+ return 0;
+
+err_put_ctrl:
+ spmi_controller_put(ctrl);
+ return rc;
+}
+
+static int spmi_pmic_arb_debug_remove(struct platform_device *pdev)
+{
+ struct spmi_controller *ctrl = platform_get_drvdata(pdev);
+
+ spmi_controller_remove(ctrl);
+ spmi_controller_put(ctrl);
+
+ return 0;
+}
+
+static const struct of_device_id spmi_pmic_arb_debug_match_table[] = {
+ { .compatible = "qcom,spmi-pmic-arb-debug", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, spmi_pmic_arb_debug_match_table);
+
+static struct platform_driver spmi_pmic_arb_debug_driver = {
+ .probe = spmi_pmic_arb_debug_probe,
+ .remove = spmi_pmic_arb_debug_remove,
+ .driver = {
+ .name = "spmi_pmic_arb_debug",
+ .of_match_table = spmi_pmic_arb_debug_match_table,
+ },
+};
+
+int __init spmi_pmic_arb_debug_init(void)
+{
+ return platform_driver_register(&spmi_pmic_arb_debug_driver);
+}
+arch_initcall(spmi_pmic_arb_debug_init);
+
+static void __exit spmi_pmic_arb_debug_exit(void)
+{
+ platform_driver_unregister(&spmi_pmic_arb_debug_driver);
+}
+module_exit(spmi_pmic_arb_debug_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:spmi_pmic_arb_debug");
diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c
index 3a52b29..eb41e84 100644
--- a/drivers/staging/android/ashmem.c
+++ b/drivers/staging/android/ashmem.c
@@ -405,6 +405,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);
@@ -765,10 +766,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/thermal/Makefile b/drivers/thermal/Makefile
index 2faed7f..acbd26b 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -52,7 +52,7 @@
obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o
obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o
obj-$(CONFIG_ST_THERMAL) += st/
-obj-$(CONFIG_QCOM_TSENS) += qcom/
+obj-$(CONFIG_ARCH_QCOM) += qcom/
obj-$(CONFIG_TEGRA_SOCTHERM) += tegra/
obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index a6245d5..37125c0 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -548,11 +548,29 @@ static int cpufreq_set_min_state(struct thermal_cooling_device *cdev,
if (cpufreq_device->cpufreq_floor_state == state)
return 0;
- floor_freq = cpufreq_device->freq_table[state];
cpufreq_device->cpufreq_floor_state = state;
- cpufreq_device->floor_freq = floor_freq;
- cpufreq_update_policy(cpu);
+ /*
+ * Check if the device has a platform mitigation function that
+ * can handle the CPU freq mitigation, if not, notify cpufreq
+ * framework.
+ */
+ if (cpufreq_device->plat_ops &&
+ cpufreq_device->plat_ops->floor_limit) {
+ /*
+ * Last level is core isolation so use the frequency
+ * of previous state.
+ */
+ if (state == cpufreq_device->max_level)
+ state--;
+ floor_freq = cpufreq_device->freq_table[state];
+ cpufreq_device->floor_freq = floor_freq;
+ cpufreq_device->plat_ops->floor_limit(cpu, floor_freq);
+ } else {
+ floor_freq = cpufreq_device->freq_table[state];
+ cpufreq_device->floor_freq = floor_freq;
+ cpufreq_update_policy(cpu);
+ }
return 0;
}
diff --git a/drivers/thermal/fair_share.c b/drivers/thermal/fair_share.c
index 68bd1b5..f2011f6 100644
--- a/drivers/thermal/fair_share.c
+++ b/drivers/thermal/fair_share.c
@@ -52,7 +52,7 @@ static int get_trip_level(struct thermal_zone_device *tz)
*/
if (count > 0) {
tz->ops->get_trip_type(tz, count - 1, &trip_type);
- trace_thermal_zone_trip(tz, count - 1, trip_type);
+ trace_thermal_zone_trip(tz, count - 1, trip_type, true);
}
return count;
diff --git a/drivers/thermal/gov_low_limits.c b/drivers/thermal/gov_low_limits.c
index cf2dbc4..b75abf7 100644
--- a/drivers/thermal/gov_low_limits.c
+++ b/drivers/thermal/gov_low_limits.c
@@ -67,10 +67,11 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
if (old_target == THERMAL_NO_TARGET &&
instance->target != THERMAL_NO_TARGET) {
- trace_thermal_zone_trip(tz, trip, trip_type);
+ trace_thermal_zone_trip(tz, trip, trip_type, true);
tz->passive += 1;
} else if (old_target != THERMAL_NO_TARGET &&
instance->target == THERMAL_NO_TARGET) {
+ trace_thermal_zone_trip(tz, trip, trip_type, false);
tz->passive -= 1;
}
diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c
index 2013e7e..432adbc 100644
--- a/drivers/thermal/msm-tsens.c
+++ b/drivers/thermal/msm-tsens.c
@@ -24,29 +24,28 @@
LIST_HEAD(tsens_device_list);
-static int tsens_get_temp(struct tsens_sensor *s, int *temp)
+static int tsens_get_temp(void *data, int *temp)
{
+ struct tsens_sensor *s = data;
struct tsens_device *tmdev = s->tmdev;
return tmdev->ops->get_temp(s, temp);
}
-static int tsens_set_trip_temp(struct tsens_sensor *s, int trip, int temp)
+static int tsens_set_trip_temp(void *data, int low_temp, int high_temp)
{
+ struct tsens_sensor *s = data;
struct tsens_device *tmdev = s->tmdev;
- if (tmdev->ops->set_trip_temp)
- return tmdev->ops->set_trip_temp(s, trip, temp);
+ if (tmdev->ops->set_trips)
+ return tmdev->ops->set_trips(s, low_temp, high_temp);
return 0;
}
static int tsens_init(struct tsens_device *tmdev)
{
- if (tmdev->ops->hw_init)
- return tmdev->ops->hw_init(tmdev);
-
- return 0;
+ return tmdev->ops->hw_init(tmdev);
}
static int tsens_register_interrupts(struct tsens_device *tmdev)
@@ -85,19 +84,16 @@ MODULE_DEVICE_TABLE(of, tsens_table);
static struct thermal_zone_of_device_ops tsens_tm_thermal_zone_ops = {
.get_temp = tsens_get_temp,
- .set_trip_temp = tsens_set_trip_temp,
+ .set_trips = tsens_set_trip_temp,
};
static int get_device_tree_data(struct platform_device *pdev,
struct tsens_device *tmdev)
{
struct device_node *of_node = pdev->dev.of_node;
- u32 *hw_id, *client_id;
- u32 rc = 0, i, tsens_num_sensors = 0;
- int tsens_len;
const struct of_device_id *id;
const struct tsens_data *data;
- struct resource *res_tsens_mem, *res_mem = NULL;
+ struct resource *res_tsens_mem;
if (!of_match_node(tsens_table, of_node)) {
pr_err("Need to read SoC specific fuse map\n");
@@ -111,16 +107,6 @@ static int get_device_tree_data(struct platform_device *pdev,
}
data = id->data;
- hw_id = devm_kzalloc(&pdev->dev,
- tsens_num_sensors * sizeof(u32), GFP_KERNEL);
- if (!hw_id)
- return -ENOMEM;
-
- client_id = devm_kzalloc(&pdev->dev,
- tsens_num_sensors * sizeof(u32), GFP_KERNEL);
- if (!client_id)
- return -ENOMEM;
-
tmdev->ops = data->ops;
tmdev->ctrl_data = data;
tmdev->pdev = pdev;
@@ -132,49 +118,32 @@ static int get_device_tree_data(struct platform_device *pdev,
/* TSENS register region */
res_tsens_mem = platform_get_resource_byname(pdev,
- IORESOURCE_MEM, "tsens_physical");
+ IORESOURCE_MEM, "tsens_srot_physical");
if (!res_tsens_mem) {
pr_err("Could not get tsens physical address resource\n");
return -EINVAL;
}
- tsens_len = res_tsens_mem->end - res_tsens_mem->start + 1;
+ tmdev->tsens_srot_addr = devm_ioremap_resource(&pdev->dev,
+ res_tsens_mem);
+ if (IS_ERR(tmdev->tsens_srot_addr)) {
+ dev_err(&pdev->dev, "Failed to IO map TSENS registers.\n");
+ return PTR_ERR(tmdev->tsens_srot_addr);
+ }
- res_mem = request_mem_region(res_tsens_mem->start,
- tsens_len, res_tsens_mem->name);
- if (!res_mem) {
- pr_err("Request tsens physical memory region failed\n");
+ /* TSENS TM register region */
+ res_tsens_mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "tsens_tm_physical");
+ if (!res_tsens_mem) {
+ pr_err("Could not get tsens physical address resource\n");
return -EINVAL;
}
- tmdev->tsens_addr = ioremap(res_mem->start, tsens_len);
- if (!tmdev->tsens_addr) {
- pr_err("Failed to IO map TSENS registers.\n");
- return -EINVAL;
- }
-
- rc = of_property_read_u32_array(of_node,
- "qcom,sensor-id", hw_id, tsens_num_sensors);
- if (rc) {
- pr_err("Default sensor id mapping\n");
- for (i = 0; i < tsens_num_sensors; i++)
- tmdev->sensor[i].hw_id = i;
- } else {
- pr_err("Use specified sensor id mapping\n");
- for (i = 0; i < tsens_num_sensors; i++)
- tmdev->sensor[i].hw_id = hw_id[i];
- }
-
- rc = of_property_read_u32_array(of_node,
- "qcom,client-id", client_id, tsens_num_sensors);
- if (rc) {
- for (i = 0; i < tsens_num_sensors; i++)
- tmdev->sensor[i].id = i;
- pr_debug("Default client id mapping\n");
- } else {
- for (i = 0; i < tsens_num_sensors; i++)
- tmdev->sensor[i].id = client_id[i];
- pr_debug("Use specified client id mapping\n");
+ tmdev->tsens_tm_addr = devm_ioremap_resource(&pdev->dev,
+ res_tsens_mem);
+ if (IS_ERR(tmdev->tsens_tm_addr)) {
+ dev_err(&pdev->dev, "Failed to IO map TSENS TM registers.\n");
+ return PTR_ERR(tmdev->tsens_tm_addr);
}
return 0;
@@ -182,20 +151,28 @@ static int get_device_tree_data(struct platform_device *pdev,
static int tsens_thermal_zone_register(struct tsens_device *tmdev)
{
- int rc = 0, i = 0;
+ int i = 0, sensor_missing = 0;
- for (i = 0; i < tmdev->num_sensors; i++) {
+ for (i = 0; i < TSENS_MAX_SENSORS; i++) {
tmdev->sensor[i].tmdev = tmdev;
- tmdev->sensor[i].tzd = devm_thermal_zone_of_sensor_register(
- &tmdev->pdev->dev, i, &tmdev->sensor[i],
- &tsens_tm_thermal_zone_ops);
+ tmdev->sensor[i].hw_id = i;
+ tmdev->sensor[i].tzd =
+ devm_thermal_zone_of_sensor_register(
+ &tmdev->pdev->dev, i,
+ &tmdev->sensor[i], &tsens_tm_thermal_zone_ops);
if (IS_ERR(tmdev->sensor[i].tzd)) {
- pr_err("Error registering sensor:%d\n", i);
+ pr_debug("Error registering sensor:%d\n", i);
+ sensor_missing++;
continue;
}
}
- return rc;
+ if (sensor_missing == TSENS_MAX_SENSORS) {
+ pr_err("No TSENS sensors to register?\n");
+ return -ENODEV;
+ }
+
+ return 0;
}
static int tsens_tm_remove(struct platform_device *pdev)
@@ -207,32 +184,19 @@ static int tsens_tm_remove(struct platform_device *pdev)
int tsens_tm_probe(struct platform_device *pdev)
{
- struct device_node *of_node = pdev->dev.of_node;
struct tsens_device *tmdev = NULL;
- u32 tsens_num_sensors = 0;
int rc;
if (!(pdev->dev.of_node))
return -ENODEV;
- rc = of_property_read_u32(of_node,
- "qcom,sensors", &tsens_num_sensors);
- if (rc || (!tsens_num_sensors)) {
- dev_err(&pdev->dev, "missing sensors\n");
- return -ENODEV;
- }
-
tmdev = devm_kzalloc(&pdev->dev,
sizeof(struct tsens_device) +
- tsens_num_sensors *
+ TSENS_MAX_SENSORS *
sizeof(struct tsens_sensor),
GFP_KERNEL);
- if (tmdev == NULL) {
- pr_err("%s: kzalloc() failed.\n", __func__);
+ if (tmdev == NULL)
return -ENOMEM;
- }
-
- tmdev->num_sensors = tsens_num_sensors;
rc = get_device_tree_data(pdev, tmdev);
if (rc) {
@@ -241,8 +205,10 @@ int tsens_tm_probe(struct platform_device *pdev)
}
rc = tsens_init(tmdev);
- if (rc)
+ if (rc) {
+ pr_err("Error initializing TSENS controller\n");
return rc;
+ }
rc = tsens_thermal_zone_register(tmdev);
if (rc) {
diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c
index b337ad7..f0be6e9 100644
--- a/drivers/thermal/of-thermal.c
+++ b/drivers/thermal/of-thermal.c
@@ -140,16 +140,11 @@ static int of_thermal_set_trips(struct thermal_zone_device *tz,
mutex_lock(&data->senps->lock);
of_thermal_aggregate_trip_types(tz, GENMASK(THERMAL_TRIP_CRITICAL, 0),
&low, &high);
- if (low == data->senps->trip_low
- && high == data->senps->trip_high)
- goto set_trips_exit;
-
data->senps->trip_low = low;
data->senps->trip_high = high;
ret = data->senps->ops->set_trips(data->senps->sensor_data,
low, high);
-set_trips_exit:
mutex_unlock(&data->senps->lock);
return ret;
}
diff --git a/drivers/thermal/qcom/bcl_peripheral.c b/drivers/thermal/qcom/bcl_peripheral.c
index 55ff770..75e553f 100644
--- a/drivers/thermal/qcom/bcl_peripheral.c
+++ b/drivers/thermal/qcom/bcl_peripheral.c
@@ -259,7 +259,7 @@ static int bcl_set_vbat(void *data, int low, int high)
*/
for (vbat_idx = 2; vbat_idx < BCL_STD_VBAT_NR;
vbat_idx++) {
- if (vbat_uv > vbat_low[vbat_idx])
+ if (vbat_uv >= vbat_low[vbat_idx])
continue;
break;
}
@@ -274,7 +274,7 @@ static int bcl_set_vbat(void *data, int low, int high)
*/
for (vbat_idx = 1; vbat_idx < (BCL_STD_VBAT_NR - 1);
vbat_idx++) {
- if (vbat_uv > vbat_low[vbat_idx])
+ if (vbat_uv >= vbat_low[vbat_idx])
continue;
break;
}
diff --git a/drivers/thermal/qcom/msm_lmh_dcvs.c b/drivers/thermal/qcom/msm_lmh_dcvs.c
index c93d650..74f5ce0 100644
--- a/drivers/thermal/qcom/msm_lmh_dcvs.c
+++ b/drivers/thermal/qcom/msm_lmh_dcvs.c
@@ -78,6 +78,12 @@ enum lmh_hw_trips {
LIMITS_TRIP_MAX,
};
+struct __limits_cdev_data {
+ struct thermal_cooling_device *cdev;
+ u32 max_freq;
+ u32 min_freq;
+};
+
struct limits_dcvs_hw {
char sensor_name[THERMAL_NAME_LENGTH];
uint32_t affinity;
@@ -93,6 +99,8 @@ struct limits_dcvs_hw {
unsigned long hw_freq_limit;
struct list_head list;
atomic_t is_irq_enabled;
+ struct mutex access_lock;
+ struct __limits_cdev_data *cdev_data;
};
LIST_HEAD(lmh_dcvs_hw_list);
@@ -318,25 +326,49 @@ static int enable_lmh(void)
static int lmh_set_max_limit(int cpu, u32 freq)
{
struct limits_dcvs_hw *hw = get_dcvsh_hw_from_cpu(cpu);
+ int ret = 0, cpu_idx, idx = 0;
+ u32 max_freq = U32_MAX;
if (!hw)
return -EINVAL;
- return limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_GENERAL,
- LIMITS_DOMAIN_MAX, freq);
+ mutex_lock(&hw->access_lock);
+ for_each_cpu(cpu_idx, &hw->core_map) {
+ if (cpu_idx == cpu)
+ hw->cdev_data[idx].max_freq = freq;
+ if (max_freq > hw->cdev_data[idx].max_freq)
+ max_freq = hw->cdev_data[idx].max_freq;
+ idx++;
+ }
+ ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_GENERAL,
+ LIMITS_DOMAIN_MAX, max_freq);
+ mutex_unlock(&hw->access_lock);
+
+ return ret;
}
static int lmh_set_min_limit(int cpu, u32 freq)
{
struct limits_dcvs_hw *hw = get_dcvsh_hw_from_cpu(cpu);
+ int cpu_idx, idx = 0;
+ u32 min_freq = 0;
if (!hw)
return -EINVAL;
- if (freq != hw->min_freq)
+ mutex_lock(&hw->access_lock);
+ for_each_cpu(cpu_idx, &hw->core_map) {
+ if (cpu_idx == cpu)
+ hw->cdev_data[idx].min_freq = freq;
+ if (min_freq < hw->cdev_data[idx].min_freq)
+ min_freq = hw->cdev_data[idx].min_freq;
+ idx++;
+ }
+ if (min_freq != hw->min_freq)
writel_relaxed(0x01, hw->min_freq_reg);
else
writel_relaxed(0x00, hw->min_freq_reg);
+ mutex_unlock(&hw->access_lock);
return 0;
}
@@ -351,12 +383,11 @@ static int limits_dcvs_probe(struct platform_device *pdev)
int affinity = -1;
struct limits_dcvs_hw *hw;
struct thermal_zone_device *tzdev;
- struct thermal_cooling_device *cdev;
struct device_node *dn = pdev->dev.of_node;
struct device_node *cpu_node, *lmh_node;
uint32_t request_reg, clear_reg, min_reg;
unsigned long max_freq, min_freq;
- int cpu;
+ int cpu, idx;
cpumask_t mask = { CPU_BITS_NONE };
for_each_possible_cpu(cpu) {
@@ -365,8 +396,6 @@ static int limits_dcvs_probe(struct platform_device *pdev)
continue;
lmh_node = of_parse_phandle(cpu_node, "qcom,lmh-dcvs", 0);
if (lmh_node == dn) {
- affinity = MPIDR_AFFINITY_LEVEL(
- cpu_logical_map(cpu), 1);
/*set the cpumask*/
cpumask_set_cpu(cpu, &(mask));
}
@@ -378,7 +407,7 @@ static int limits_dcvs_probe(struct platform_device *pdev)
* We return error if none of the CPUs have
* reference to our LMH node
*/
- if (affinity == -1)
+ if (cpumask_empty(&mask))
return -EINVAL;
ret = limits_dcvs_get_freq_limits(cpumask_first(&mask), &max_freq,
@@ -388,8 +417,16 @@ static int limits_dcvs_probe(struct platform_device *pdev)
hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
if (!hw)
return -ENOMEM;
+ hw->cdev_data = devm_kcalloc(&pdev->dev, cpumask_weight(&mask),
+ sizeof(*hw->cdev_data),
+ GFP_KERNEL);
+ if (!hw->cdev_data)
+ return -ENOMEM;
cpumask_copy(&hw->core_map, &mask);
+ ret = of_property_read_u32(dn, "qcom,affinity", &affinity);
+ if (ret)
+ return -ENODEV;
switch (affinity) {
case 0:
hw->affinity = LIMITS_CLUSTER_0;
@@ -444,9 +481,20 @@ static int limits_dcvs_probe(struct platform_device *pdev)
return PTR_ERR(tzdev);
/* Setup cooling devices to request mitigation states */
- cdev = cpufreq_platform_cooling_register(&hw->core_map, &cd_ops);
- if (IS_ERR_OR_NULL(cdev))
- return PTR_ERR(cdev);
+ mutex_init(&hw->access_lock);
+ idx = 0;
+ for_each_cpu(cpu, &hw->core_map) {
+ cpumask_t cpu_mask = { CPU_BITS_NONE };
+
+ cpumask_set_cpu(cpu, &cpu_mask);
+ hw->cdev_data[idx].cdev = cpufreq_platform_cooling_register(
+ &cpu_mask, &cd_ops);
+ if (IS_ERR_OR_NULL(hw->cdev_data[idx].cdev))
+ return PTR_ERR(hw->cdev_data[idx].cdev);
+ hw->cdev_data[idx].max_freq = U32_MAX;
+ hw->cdev_data[idx].min_freq = 0;
+ idx++;
+ }
switch (affinity) {
case 0:
diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c
index 4fa7f82..ecfc4ef 100644
--- a/drivers/thermal/step_wise.c
+++ b/drivers/thermal/step_wise.c
@@ -169,12 +169,10 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
*/
if (tz->temperature >= trip_temp ||
(tz->temperature >= hyst_temp &&
- old_target != THERMAL_NO_TARGET)) {
+ old_target != THERMAL_NO_TARGET))
throttle = true;
- trace_thermal_zone_trip(tz, trip, trip_type);
- } else {
+ else
throttle = false;
- }
instance->target = get_target_state(instance, trend, throttle);
dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n",
@@ -185,12 +183,15 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
/* Activate a passive thermal instance */
if (old_target == THERMAL_NO_TARGET &&
- instance->target != THERMAL_NO_TARGET)
+ instance->target != THERMAL_NO_TARGET) {
update_passive_instance(tz, trip_type, 1);
+ trace_thermal_zone_trip(tz, trip, trip_type, true);
/* Deactivate a passive thermal instance */
- else if (old_target != THERMAL_NO_TARGET &&
- instance->target == THERMAL_NO_TARGET)
+ } else if (old_target != THERMAL_NO_TARGET &&
+ instance->target == THERMAL_NO_TARGET) {
update_passive_instance(tz, trip_type, -1);
+ trace_thermal_zone_trip(tz, trip, trip_type, false);
+ }
instance->initialized = true;
mutex_lock(&instance->cdev->lock);
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index f905103..b137c4e 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -444,7 +444,7 @@ static void handle_critical_trips(struct thermal_zone_device *tz,
if (trip_temp <= 0 || tz->temperature < trip_temp)
return;
- trace_thermal_zone_trip(tz, trip, trip_type);
+ trace_thermal_zone_trip(tz, trip, trip_type, true);
if (tz->ops->notify)
tz->ops->notify(tz, trip, trip_type);
@@ -553,10 +553,6 @@ void thermal_zone_set_trips(struct thermal_zone_device *tz)
high = trip_temp;
}
- /* No need to change trip points */
- if (tz->prev_low_trip == low && tz->prev_high_trip == high)
- goto exit;
-
tz->prev_low_trip = low;
tz->prev_high_trip = high;
@@ -815,8 +811,7 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
*/
ret = tz->ops->set_trip_hyst(tz, trip, temperature);
- if (!ret)
- thermal_zone_set_trips(tz);
+ thermal_zone_set_trips(tz);
return ret ? ret : count;
}
@@ -1290,6 +1285,30 @@ static DEVICE_ATTR(min_state, 0644,
thermal_cooling_device_min_state_store);
static ssize_t
+thermal_cooling_device_lower_limit_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct thermal_instance *instance;
+
+ instance =
+ container_of(attr, struct thermal_instance, lower_attr);
+
+ return snprintf(buf, PAGE_SIZE, "%lu\n", instance->lower);
+}
+
+static ssize_t
+thermal_cooling_device_upper_limit_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct thermal_instance *instance;
+
+ instance =
+ container_of(attr, struct thermal_instance, upper_attr);
+
+ return snprintf(buf, PAGE_SIZE, "%lu\n", instance->upper);
+}
+
+static ssize_t
thermal_cooling_device_trip_point_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1443,6 +1462,26 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
if (result)
goto remove_symbol_link;
+ snprintf(dev->upper_attr_name, THERMAL_NAME_LENGTH,
+ "cdev%d_upper_limit", dev->id);
+ sysfs_attr_init(&dev->upper_attr.attr);
+ dev->upper_attr.attr.name = dev->upper_attr_name;
+ dev->upper_attr.attr.mode = 0444;
+ dev->upper_attr.show = thermal_cooling_device_upper_limit_show;
+ result = device_create_file(&tz->device, &dev->upper_attr);
+ if (result)
+ goto remove_trip_file;
+
+ snprintf(dev->lower_attr_name, THERMAL_NAME_LENGTH,
+ "cdev%d_lower_limit", dev->id);
+ sysfs_attr_init(&dev->lower_attr.attr);
+ dev->lower_attr.attr.name = dev->lower_attr_name;
+ dev->lower_attr.attr.mode = 0444;
+ dev->lower_attr.show = thermal_cooling_device_lower_limit_show;
+ result = device_create_file(&tz->device, &dev->lower_attr);
+ if (result)
+ goto remove_upper_file;
+
sprintf(dev->weight_attr_name, "cdev%d_weight", dev->id);
sysfs_attr_init(&dev->weight_attr.attr);
dev->weight_attr.attr.name = dev->weight_attr_name;
@@ -1451,7 +1490,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
dev->weight_attr.store = thermal_cooling_device_weight_store;
result = device_create_file(&tz->device, &dev->weight_attr);
if (result)
- goto remove_trip_file;
+ goto remove_lower_file;
mutex_lock(&tz->lock);
mutex_lock(&cdev->lock);
@@ -1472,6 +1511,10 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
return 0;
device_remove_file(&tz->device, &dev->weight_attr);
+remove_lower_file:
+ device_remove_file(&tz->device, &dev->lower_attr);
+remove_upper_file:
+ device_remove_file(&tz->device, &dev->upper_attr);
remove_trip_file:
device_remove_file(&tz->device, &dev->attr);
remove_symbol_link:
@@ -1521,6 +1564,8 @@ int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
return -ENODEV;
unbind:
+ device_remove_file(&tz->device, &pos->lower_attr);
+ device_remove_file(&tz->device, &pos->upper_attr);
device_remove_file(&tz->device, &pos->weight_attr);
device_remove_file(&tz->device, &pos->attr);
sysfs_remove_link(&tz->device.kobj, pos->name);
@@ -1768,7 +1813,7 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev)
cdev->ops->set_min_state(cdev, min_target);
cdev->updated = true;
mutex_unlock(&cdev->lock);
- trace_cdev_update(cdev, current_target);
+ trace_cdev_update(cdev, current_target, min_target);
dev_dbg(&cdev->device, "set to state %lu min state %lu\n",
current_target, min_target);
}
diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
index eca8c3c..567f630 100644
--- a/drivers/thermal/thermal_core.h
+++ b/drivers/thermal/thermal_core.h
@@ -49,6 +49,10 @@ struct thermal_instance {
struct device_attribute attr;
char weight_attr_name[THERMAL_NAME_LENGTH];
struct device_attribute weight_attr;
+ char upper_attr_name[THERMAL_NAME_LENGTH];
+ struct device_attribute upper_attr;
+ char lower_attr_name[THERMAL_NAME_LENGTH];
+ struct device_attribute lower_attr;
struct list_head tz_node; /* node in tz->thermal_instances */
struct list_head cdev_node; /* node in cdev->thermal_instances */
unsigned int weight; /* The weight of the cooling device */
diff --git a/drivers/thermal/tsens.h b/drivers/thermal/tsens.h
index b9ebb65..3b9b01a 100644
--- a/drivers/thermal/tsens.h
+++ b/drivers/thermal/tsens.h
@@ -23,9 +23,9 @@
#define DEBUG_SIZE 10
#define TSENS_MAX_SENSORS 16
-#define TSENS_CONTROLLER_ID(n) ((n) + 0x1000)
+#define TSENS_CONTROLLER_ID(n) (n)
#define TSENS_CTRL_ADDR(n) (n)
-#define TSENS_TM_SN_STATUS(n) ((n) + 0x10a0)
+#define TSENS_TM_SN_STATUS(n) ((n) + 0xa0)
enum tsens_dbg_type {
TSENS_DBG_POLL,
@@ -87,7 +87,7 @@ struct tsens_sensor {
struct tsens_ops {
int (*hw_init)(struct tsens_device *);
int (*get_temp)(struct tsens_sensor *, int *);
- int (*set_trip_temp)(struct tsens_sensor *, int, int);
+ int (*set_trips)(struct tsens_sensor *, int, int);
int (*interrupts_reg)(struct tsens_device *);
int (*dbg)(struct tsens_device *, u32, u32, int *);
};
@@ -121,7 +121,8 @@ struct tsens_device {
u32 num_sensors;
struct regmap *map;
struct regmap_field *status_field;
- void *tsens_addr;
+ void __iomem *tsens_srot_addr;
+ void __iomem *tsens_tm_addr;
const struct tsens_ops *ops;
struct tsens_dbg_context tsens_dbg;
spinlock_t tsens_crit_lock;
diff --git a/drivers/thermal/tsens2xxx.c b/drivers/thermal/tsens2xxx.c
index 1f0bee9..13b183d 100644
--- a/drivers/thermal/tsens2xxx.c
+++ b/drivers/thermal/tsens2xxx.c
@@ -18,19 +18,20 @@
#include <linux/of.h>
#include <linux/vmalloc.h>
#include "tsens.h"
+#include "thermal_core.h"
#define TSENS_DRIVER_NAME "msm-tsens"
-#define TSENS_TM_INT_EN(n) ((n) + 0x1004)
-#define TSENS_TM_CRITICAL_INT_STATUS(n) ((n) + 0x1014)
-#define TSENS_TM_CRITICAL_INT_CLEAR(n) ((n) + 0x1018)
-#define TSENS_TM_CRITICAL_INT_MASK(n) ((n) + 0x101c)
+#define TSENS_TM_INT_EN(n) ((n) + 0x4)
+#define TSENS_TM_CRITICAL_INT_STATUS(n) ((n) + 0x14)
+#define TSENS_TM_CRITICAL_INT_CLEAR(n) ((n) + 0x18)
+#define TSENS_TM_CRITICAL_INT_MASK(n) ((n) + 0x1c)
#define TSENS_TM_CRITICAL_WD_BARK BIT(31)
#define TSENS_TM_CRITICAL_CYCLE_MONITOR BIT(30)
#define TSENS_TM_CRITICAL_INT_EN BIT(2)
#define TSENS_TM_UPPER_INT_EN BIT(1)
#define TSENS_TM_LOWER_INT_EN BIT(0)
-#define TSENS_TM_SN_UPPER_LOWER_THRESHOLD(n) ((n) + 0x1020)
+#define TSENS_TM_SN_UPPER_LOWER_THRESHOLD(n) ((n) + 0x20)
#define TSENS_TM_SN_ADDR_OFFSET 0x4
#define TSENS_TM_UPPER_THRESHOLD_SET(n) ((n) << 12)
#define TSENS_TM_UPPER_THRESHOLD_VALUE_SHIFT(n) ((n) >> 12)
@@ -39,13 +40,13 @@
#define TSENS_TM_UPPER_THRESHOLD_MASK 0xfff000
#define TSENS_TM_LOWER_THRESHOLD_MASK 0xfff
#define TSENS_TM_UPPER_THRESHOLD_SHIFT 12
-#define TSENS_TM_SN_CRITICAL_THRESHOLD(n) ((n) + 0x1060)
+#define TSENS_TM_SN_CRITICAL_THRESHOLD(n) ((n) + 0x60)
#define TSENS_STATUS_ADDR_OFFSET 2
#define TSENS_TM_UPPER_INT_MASK(n) (((n) & 0xffff0000) >> 16)
#define TSENS_TM_LOWER_INT_MASK(n) ((n) & 0xffff)
-#define TSENS_TM_UPPER_LOWER_INT_STATUS(n) ((n) + 0x1008)
-#define TSENS_TM_UPPER_LOWER_INT_CLEAR(n) ((n) + 0x100c)
-#define TSENS_TM_UPPER_LOWER_INT_MASK(n) ((n) + 0x1010)
+#define TSENS_TM_UPPER_LOWER_INT_STATUS(n) ((n) + 0x8)
+#define TSENS_TM_UPPER_LOWER_INT_CLEAR(n) ((n) + 0xc)
+#define TSENS_TM_UPPER_LOWER_INT_MASK(n) ((n) + 0x10)
#define TSENS_TM_UPPER_INT_SET(n) (1 << (n + 16))
#define TSENS_TM_SN_CRITICAL_THRESHOLD_MASK 0xfff
#define TSENS_TM_SN_STATUS_VALID_BIT BIT(21)
@@ -55,6 +56,7 @@
#define TSENS_TM_SN_LAST_TEMP_MASK 0xfff
#define TSENS_TM_CODE_BIT_MASK 0xfff
#define TSENS_TM_CODE_SIGN_BIT 0x800
+#define TSENS_TM_SCALE_DECI_MILLIDEG 100
#define TSENS_EN BIT(0)
@@ -67,7 +69,7 @@ static void msm_tsens_convert_temp(int last_temp, int *temp)
last_temp |= code_mask;
}
- *temp = last_temp * 100;
+ *temp = last_temp * TSENS_TM_SCALE_DECI_MILLIDEG;
}
static int tsens2xxx_get_temp(struct tsens_sensor *sensor, int *temp)
@@ -81,7 +83,7 @@ static int tsens2xxx_get_temp(struct tsens_sensor *sensor, int *temp)
return -EINVAL;
tmdev = sensor->tmdev;
- sensor_addr = TSENS_TM_SN_STATUS(tmdev->tsens_addr);
+ sensor_addr = TSENS_TM_SN_STATUS(tmdev->tsens_tm_addr);
code = readl_relaxed_no_log(sensor_addr +
(sensor->hw_id << TSENS_STATUS_ADDR_OFFSET));
@@ -130,7 +132,6 @@ static int tsens_tm_activate_trip_type(struct tsens_sensor *tm_sensor,
{
struct tsens_device *tmdev = NULL;
unsigned int reg_cntl, mask;
- unsigned long flags;
int rc = 0;
/* clear the interrupt and unmask */
@@ -141,56 +142,57 @@ static int tsens_tm_activate_trip_type(struct tsens_sensor *tm_sensor,
if (!tmdev)
return -EINVAL;
- spin_lock_irqsave(&tmdev->tsens_upp_low_lock, flags);
+
mask = (tm_sensor->hw_id);
switch (trip) {
case THERMAL_TRIP_CRITICAL:
tmdev->sensor[tm_sensor->hw_id].
thr_state.crit_th_state = mode;
reg_cntl = readl_relaxed(TSENS_TM_CRITICAL_INT_MASK
- (tmdev->tsens_addr));
+ (tmdev->tsens_tm_addr));
if (mode == THERMAL_TRIP_ACTIVATION_DISABLED)
writel_relaxed(reg_cntl | (1 << mask),
(TSENS_TM_CRITICAL_INT_MASK
- (tmdev->tsens_addr)));
+ (tmdev->tsens_tm_addr)));
else
writel_relaxed(reg_cntl & ~(1 << mask),
(TSENS_TM_CRITICAL_INT_MASK
- (tmdev->tsens_addr)));
+ (tmdev->tsens_tm_addr)));
break;
- case THERMAL_TRIP_ACTIVE:
+ case THERMAL_TRIP_CONFIGURABLE_HI:
tmdev->sensor[tm_sensor->hw_id].
thr_state.high_th_state = mode;
reg_cntl = readl_relaxed(TSENS_TM_UPPER_LOWER_INT_MASK
- (tmdev->tsens_addr));
+ (tmdev->tsens_tm_addr));
if (mode == THERMAL_TRIP_ACTIVATION_DISABLED)
writel_relaxed(reg_cntl |
(TSENS_TM_UPPER_INT_SET(mask)),
(TSENS_TM_UPPER_LOWER_INT_MASK
- (tmdev->tsens_addr)));
+ (tmdev->tsens_tm_addr)));
else
writel_relaxed(reg_cntl &
~(TSENS_TM_UPPER_INT_SET(mask)),
(TSENS_TM_UPPER_LOWER_INT_MASK
- (tmdev->tsens_addr)));
+ (tmdev->tsens_tm_addr)));
break;
- case THERMAL_TRIP_PASSIVE:
+ case THERMAL_TRIP_CONFIGURABLE_LOW:
tmdev->sensor[tm_sensor->hw_id].
thr_state.low_th_state = mode;
reg_cntl = readl_relaxed(TSENS_TM_UPPER_LOWER_INT_MASK
- (tmdev->tsens_addr));
+ (tmdev->tsens_tm_addr));
if (mode == THERMAL_TRIP_ACTIVATION_DISABLED)
writel_relaxed(reg_cntl | (1 << mask),
- (TSENS_TM_UPPER_LOWER_INT_MASK(tmdev->tsens_addr)));
+ (TSENS_TM_UPPER_LOWER_INT_MASK
+ (tmdev->tsens_tm_addr)));
else
writel_relaxed(reg_cntl & ~(1 << mask),
- (TSENS_TM_UPPER_LOWER_INT_MASK(tmdev->tsens_addr)));
+ (TSENS_TM_UPPER_LOWER_INT_MASK
+ (tmdev->tsens_tm_addr)));
break;
default:
rc = -EINVAL;
}
- spin_unlock_irqrestore(&tmdev->tsens_upp_low_lock, flags);
/* Activate and enable the respective trip threshold setting */
mb();
@@ -198,14 +200,14 @@ static int tsens_tm_activate_trip_type(struct tsens_sensor *tm_sensor,
}
static int tsens2xxx_set_trip_temp(struct tsens_sensor *tm_sensor,
- int trip, int temp)
+ int low_temp, int high_temp)
{
unsigned int reg_cntl;
unsigned long flags;
struct tsens_device *tmdev = NULL;
int rc = 0;
- if (!tm_sensor || trip < 0)
+ if (!tm_sensor)
return -EINVAL;
tmdev = tm_sensor->tmdev;
@@ -213,56 +215,81 @@ static int tsens2xxx_set_trip_temp(struct tsens_sensor *tm_sensor,
return -EINVAL;
spin_lock_irqsave(&tmdev->tsens_upp_low_lock, flags);
- switch (trip) {
- case THERMAL_TRIP_CRITICAL:
+
+ if (high_temp != INT_MAX) {
tmdev->sensor[tm_sensor->hw_id].
- thr_state.crit_temp = temp;
- temp &= TSENS_TM_SN_CRITICAL_THRESHOLD_MASK;
- writel_relaxed(temp,
- (TSENS_TM_SN_CRITICAL_THRESHOLD(tmdev->tsens_addr) +
- (tm_sensor->hw_id * TSENS_TM_SN_ADDR_OFFSET)));
- break;
- case THERMAL_TRIP_ACTIVE:
- tmdev->sensor[tm_sensor->hw_id].
- thr_state.high_temp = temp;
+ thr_state.high_temp = high_temp;
reg_cntl = readl_relaxed((TSENS_TM_SN_UPPER_LOWER_THRESHOLD
- (tmdev->tsens_addr)) +
+ (tmdev->tsens_tm_addr)) +
(tm_sensor->hw_id *
TSENS_TM_SN_ADDR_OFFSET));
- temp = TSENS_TM_UPPER_THRESHOLD_SET(temp);
- temp &= TSENS_TM_UPPER_THRESHOLD_MASK;
+ high_temp /= TSENS_TM_SCALE_DECI_MILLIDEG;
+ high_temp = TSENS_TM_UPPER_THRESHOLD_SET(high_temp);
+ high_temp &= TSENS_TM_UPPER_THRESHOLD_MASK;
reg_cntl &= ~TSENS_TM_UPPER_THRESHOLD_MASK;
- writel_relaxed(reg_cntl | temp,
- (TSENS_TM_SN_UPPER_LOWER_THRESHOLD(tmdev->tsens_addr) +
+ writel_relaxed(reg_cntl | high_temp,
+ (TSENS_TM_SN_UPPER_LOWER_THRESHOLD
+ (tmdev->tsens_tm_addr) +
(tm_sensor->hw_id * TSENS_TM_SN_ADDR_OFFSET)));
- break;
- case THERMAL_TRIP_PASSIVE:
- tmdev->sensor[tm_sensor->hw_id].
- thr_state.low_temp = temp;
- reg_cntl = readl_relaxed((TSENS_TM_SN_UPPER_LOWER_THRESHOLD
- (tmdev->tsens_addr)) +
- (tm_sensor->hw_id *
- TSENS_TM_SN_ADDR_OFFSET));
- temp &= TSENS_TM_LOWER_THRESHOLD_MASK;
- reg_cntl &= ~TSENS_TM_LOWER_THRESHOLD_MASK;
- writel_relaxed(reg_cntl | temp,
- (TSENS_TM_SN_UPPER_LOWER_THRESHOLD(tmdev->tsens_addr) +
- (tm_sensor->hw_id * TSENS_TM_SN_ADDR_OFFSET)));
- break;
- default:
- pr_err("Invalid trip to TSENS: %d\n", trip);
- rc = -EINVAL;
}
- spin_unlock_irqrestore(&tmdev->tsens_upp_low_lock, flags);
+ if (low_temp != INT_MIN) {
+ tmdev->sensor[tm_sensor->hw_id].
+ thr_state.low_temp = low_temp;
+ reg_cntl = readl_relaxed((TSENS_TM_SN_UPPER_LOWER_THRESHOLD
+ (tmdev->tsens_tm_addr)) +
+ (tm_sensor->hw_id *
+ TSENS_TM_SN_ADDR_OFFSET));
+ low_temp /= TSENS_TM_SCALE_DECI_MILLIDEG;
+ low_temp &= TSENS_TM_LOWER_THRESHOLD_MASK;
+ reg_cntl &= ~TSENS_TM_LOWER_THRESHOLD_MASK;
+ writel_relaxed(reg_cntl | low_temp,
+ (TSENS_TM_SN_UPPER_LOWER_THRESHOLD
+ (tmdev->tsens_tm_addr) +
+ (tm_sensor->hw_id * TSENS_TM_SN_ADDR_OFFSET)));
+ }
+
/* Set trip temperature thresholds */
mb();
- rc = tsens_tm_activate_trip_type(tm_sensor, trip,
+ if (high_temp != INT_MAX) {
+ rc = tsens_tm_activate_trip_type(tm_sensor,
+ THERMAL_TRIP_CONFIGURABLE_HI,
THERMAL_TRIP_ACTIVATION_ENABLED);
- if (rc)
- pr_err("Error during trip activation :%d\n", rc);
+ if (rc) {
+ pr_err("trip high enable error :%d\n", rc);
+ goto fail;
+ }
+ } else {
+ rc = tsens_tm_activate_trip_type(tm_sensor,
+ THERMAL_TRIP_CONFIGURABLE_HI,
+ THERMAL_TRIP_ACTIVATION_DISABLED);
+ if (rc) {
+ pr_err("trip high disable error :%d\n", rc);
+ goto fail;
+ }
+ }
+ if (low_temp != INT_MIN) {
+ rc = tsens_tm_activate_trip_type(tm_sensor,
+ THERMAL_TRIP_CONFIGURABLE_LOW,
+ THERMAL_TRIP_ACTIVATION_ENABLED);
+ if (rc) {
+ pr_err("trip low enable activation error :%d\n", rc);
+ goto fail;
+ }
+ } else {
+ rc = tsens_tm_activate_trip_type(tm_sensor,
+ THERMAL_TRIP_CONFIGURABLE_LOW,
+ THERMAL_TRIP_ACTIVATION_DISABLED);
+ if (rc) {
+ pr_err("trip low disable error :%d\n", rc);
+ goto fail;
+ }
+ }
+
+fail:
+ spin_unlock_irqrestore(&tmdev->tsens_upp_low_lock, flags);
return rc;
}
@@ -277,13 +304,13 @@ static irqreturn_t tsens_tm_critical_irq_thread(int irq, void *data)
void __iomem *wd_critical_addr;
int wd_mask;
- sensor_status_addr = TSENS_TM_SN_STATUS(tm->tsens_addr);
+ sensor_status_addr = TSENS_TM_SN_STATUS(tm->tsens_tm_addr);
sensor_int_mask_addr =
- TSENS_TM_CRITICAL_INT_MASK(tm->tsens_addr);
+ TSENS_TM_CRITICAL_INT_MASK(tm->tsens_tm_addr);
sensor_critical_addr =
- TSENS_TM_SN_CRITICAL_THRESHOLD(tm->tsens_addr);
+ TSENS_TM_SN_CRITICAL_THRESHOLD(tm->tsens_tm_addr);
wd_critical_addr =
- TSENS_TM_CRITICAL_INT_STATUS(tm->tsens_addr);
+ TSENS_TM_CRITICAL_INT_STATUS(tm->tsens_tm_addr);
if (tm->ctrl_data->wd_bark) {
wd_mask = readl_relaxed(wd_critical_addr);
@@ -294,19 +321,22 @@ static irqreturn_t tsens_tm_critical_irq_thread(int irq, void *data)
*/
writel_relaxed(wd_mask | TSENS_TM_CRITICAL_WD_BARK,
(TSENS_TM_CRITICAL_INT_CLEAR
- (tm->tsens_addr)));
+ (tm->tsens_tm_addr)));
writel_relaxed(wd_mask & ~(TSENS_TM_CRITICAL_WD_BARK),
(TSENS_TM_CRITICAL_INT_CLEAR
- (tm->tsens_addr)));
+ (tm->tsens_tm_addr)));
tm->tsens_dbg.tsens_critical_wd_cnt++;
return IRQ_HANDLED;
}
}
- for (i = 0; i < tm->num_sensors; i++) {
+ for (i = 0; i < TSENS_MAX_SENSORS; i++) {
int int_mask, int_mask_val;
u32 addr_offset;
+ if (IS_ERR(tm->sensor[i].tzd))
+ continue;
+
spin_lock_irqsave(&tm->tsens_crit_lock, flags);
addr_offset = tm->sensor[i].hw_id *
TSENS_TM_SN_ADDR_OFFSET;
@@ -320,13 +350,14 @@ static irqreturn_t tsens_tm_critical_irq_thread(int irq, void *data)
/* Mask the corresponding interrupt for the sensors */
writel_relaxed(int_mask | int_mask_val,
TSENS_TM_CRITICAL_INT_MASK(
- tm->tsens_addr));
+ tm->tsens_tm_addr));
/* Clear the corresponding sensors interrupt */
writel_relaxed(int_mask_val,
- TSENS_TM_CRITICAL_INT_CLEAR(tm->tsens_addr));
+ TSENS_TM_CRITICAL_INT_CLEAR
+ (tm->tsens_tm_addr));
writel_relaxed(0,
TSENS_TM_CRITICAL_INT_CLEAR(
- tm->tsens_addr));
+ tm->tsens_tm_addr));
tm->sensor[i].thr_state.
crit_th_state = THERMAL_DEVICE_DISABLED;
}
@@ -342,22 +373,31 @@ static irqreturn_t tsens_tm_critical_irq_thread(int irq, void *data)
static irqreturn_t tsens_tm_irq_thread(int irq, void *data)
{
struct tsens_device *tm = data;
- unsigned int i, status, threshold;
+ unsigned int i, status, threshold, temp;
unsigned long flags;
void __iomem *sensor_status_addr;
void __iomem *sensor_int_mask_addr;
void __iomem *sensor_upper_lower_addr;
u32 addr_offset = 0;
- sensor_status_addr = TSENS_TM_SN_STATUS(tm->tsens_addr);
+ sensor_status_addr = TSENS_TM_SN_STATUS(tm->tsens_tm_addr);
sensor_int_mask_addr =
- TSENS_TM_UPPER_LOWER_INT_MASK(tm->tsens_addr);
+ TSENS_TM_UPPER_LOWER_INT_MASK(tm->tsens_tm_addr);
sensor_upper_lower_addr =
- TSENS_TM_SN_UPPER_LOWER_THRESHOLD(tm->tsens_addr);
+ TSENS_TM_SN_UPPER_LOWER_THRESHOLD(tm->tsens_tm_addr);
- for (i = 0; i < tm->num_sensors; i++) {
+ for (i = 0; i < TSENS_MAX_SENSORS; i++) {
bool upper_thr = false, lower_thr = false;
- int int_mask, int_mask_val = 0;
+ int int_mask, int_mask_val = 0, rc;
+
+ if (IS_ERR(tm->sensor[i].tzd))
+ continue;
+
+ rc = tsens2xxx_get_temp(&tm->sensor[i], &temp);
+ if (rc) {
+ pr_debug("Error:%d reading temp sensor:%d\n", rc, i);
+ continue;
+ }
spin_lock_irqsave(&tm->tsens_upp_low_lock, flags);
addr_offset = tm->sensor[i].hw_id *
@@ -376,17 +416,28 @@ static irqreturn_t tsens_tm_irq_thread(int irq, void *data)
/* Mask the corresponding interrupt for the sensors */
writel_relaxed(int_mask | int_mask_val,
TSENS_TM_UPPER_LOWER_INT_MASK(
- tm->tsens_addr));
+ tm->tsens_tm_addr));
/* Clear the corresponding sensors interrupt */
writel_relaxed(int_mask_val,
TSENS_TM_UPPER_LOWER_INT_CLEAR(
- tm->tsens_addr));
+ tm->tsens_tm_addr));
writel_relaxed(0,
TSENS_TM_UPPER_LOWER_INT_CLEAR(
- tm->tsens_addr));
- upper_thr = true;
- tm->sensor[i].thr_state.
+ tm->tsens_tm_addr));
+ if (TSENS_TM_UPPER_THRESHOLD_VALUE(threshold) >
+ (temp/TSENS_TM_SCALE_DECI_MILLIDEG)) {
+ pr_debug("Re-arm high threshold\n");
+ rc = tsens_tm_activate_trip_type(
+ &tm->sensor[i],
+ THERMAL_TRIP_CONFIGURABLE_HI,
+ THERMAL_TRIP_ACTIVATION_ENABLED);
+ if (rc)
+ pr_err("high rearm failed:%d\n", rc);
+ } else {
+ upper_thr = true;
+ tm->sensor[i].thr_state.
high_th_state = THERMAL_DEVICE_DISABLED;
+ }
}
if ((status & TSENS_TM_SN_STATUS_LOWER_STATUS) &&
@@ -397,32 +448,36 @@ static irqreturn_t tsens_tm_irq_thread(int irq, void *data)
/* Mask the corresponding interrupt for the sensors */
writel_relaxed(int_mask | int_mask_val,
TSENS_TM_UPPER_LOWER_INT_MASK(
- tm->tsens_addr));
+ tm->tsens_tm_addr));
/* Clear the corresponding sensors interrupt */
writel_relaxed(int_mask_val,
TSENS_TM_UPPER_LOWER_INT_CLEAR(
- tm->tsens_addr));
+ tm->tsens_tm_addr));
writel_relaxed(0,
TSENS_TM_UPPER_LOWER_INT_CLEAR(
- tm->tsens_addr));
- lower_thr = true;
- tm->sensor[i].thr_state.
+ tm->tsens_tm_addr));
+ if (TSENS_TM_LOWER_THRESHOLD_VALUE(threshold)
+ < (temp/TSENS_TM_SCALE_DECI_MILLIDEG)) {
+ pr_debug("Re-arm low threshold\n");
+ rc = tsens_tm_activate_trip_type(
+ &tm->sensor[i],
+ THERMAL_TRIP_CONFIGURABLE_LOW,
+ THERMAL_TRIP_ACTIVATION_ENABLED);
+ if (rc)
+ pr_err("low rearm failed:%d\n", rc);
+ } else {
+ lower_thr = true;
+ tm->sensor[i].thr_state.
low_th_state = THERMAL_DEVICE_DISABLED;
+ }
}
spin_unlock_irqrestore(&tm->tsens_upp_low_lock, flags);
if (upper_thr || lower_thr) {
- int temp;
- enum thermal_trip_type trip =
- THERMAL_TRIP_CONFIGURABLE_LOW;
-
- if (upper_thr)
- trip = THERMAL_TRIP_CONFIGURABLE_HI;
- tsens2xxx_get_temp(&tm->sensor[i], &temp);
/* Use id for multiple controllers */
pr_debug("sensor:%d trigger temp (%d degC)\n",
- tm->sensor[i].hw_id,
- (status & TSENS_TM_SN_LAST_TEMP_MASK));
+ tm->sensor[i].hw_id, temp);
+ of_thermal_handle_trip(tm->sensor[i].tzd);
}
}
@@ -442,7 +497,7 @@ static int tsens2xxx_hw_init(struct tsens_device *tmdev)
unsigned int srot_val;
int crit_mask;
- srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_addr + 0x4);
+ srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4);
srot_val = readl_relaxed(srot_addr);
if (!(srot_val & TSENS_EN)) {
pr_err("TSENS device is not enabled\n");
@@ -451,18 +506,18 @@ static int tsens2xxx_hw_init(struct tsens_device *tmdev)
if (tmdev->ctrl_data->cycle_monitor) {
sensor_int_mask_addr =
- TSENS_TM_CRITICAL_INT_MASK(tmdev->tsens_addr);
+ TSENS_TM_CRITICAL_INT_MASK(tmdev->tsens_tm_addr);
crit_mask = readl_relaxed(sensor_int_mask_addr);
writel_relaxed(
crit_mask | tmdev->ctrl_data->cycle_compltn_monitor_val,
(TSENS_TM_CRITICAL_INT_MASK
- (tmdev->tsens_addr)));
+ (tmdev->tsens_tm_addr)));
/*Update critical cycle monitoring*/
mb();
}
writel_relaxed(TSENS_TM_CRITICAL_INT_EN |
TSENS_TM_UPPER_INT_EN | TSENS_TM_LOWER_INT_EN,
- TSENS_TM_INT_EN(tmdev->tsens_addr));
+ TSENS_TM_INT_EN(tmdev->tsens_tm_addr));
spin_lock_init(&tmdev->tsens_crit_lock);
spin_lock_init(&tmdev->tsens_upp_low_lock);
@@ -513,7 +568,7 @@ static int tsens2xxx_register_interrupts(struct tsens_device *tmdev)
static const struct tsens_ops ops_tsens2xxx = {
.hw_init = tsens2xxx_hw_init,
.get_temp = tsens2xxx_get_temp,
- .set_trip_temp = tsens2xxx_set_trip_temp,
+ .set_trips = tsens2xxx_set_trip_temp,
.interrupts_reg = tsens2xxx_register_interrupts,
.dbg = tsens2xxx_dbg,
};
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 61ad6c3..f4eb807 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -1075,15 +1075,15 @@ static int omap8250_no_handle_irq(struct uart_port *port)
}
static const u8 am3352_habit = OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE;
-static const u8 am4372_habit = UART_ERRATA_CLOCK_DISABLE;
+static const u8 dra742_habit = UART_ERRATA_CLOCK_DISABLE;
static const struct of_device_id omap8250_dt_ids[] = {
{ .compatible = "ti,omap2-uart" },
{ .compatible = "ti,omap3-uart" },
{ .compatible = "ti,omap4-uart" },
{ .compatible = "ti,am3352-uart", .data = &am3352_habit, },
- { .compatible = "ti,am4372-uart", .data = &am4372_habit, },
- { .compatible = "ti,dra742-uart", .data = &am4372_habit, },
+ { .compatible = "ti,am4372-uart", .data = &am3352_habit, },
+ { .compatible = "ti,dra742-uart", .data = &dra742_habit, },
{},
};
MODULE_DEVICE_TABLE(of, omap8250_dt_ids);
@@ -1218,9 +1218,6 @@ static int omap8250_probe(struct platform_device *pdev)
priv->omap8250_dma.rx_size = RX_TRIGGER;
priv->omap8250_dma.rxconf.src_maxburst = RX_TRIGGER;
priv->omap8250_dma.txconf.dst_maxburst = TX_TRIGGER;
-
- if (of_machine_is_compatible("ti,am33xx"))
- priv->habit |= OMAP_DMA_TX_KICK;
/*
* pause is currently not supported atleast on omap-sdma
* and edma on most earlier kernels.
diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index 5b48323..b3cb5c0 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -21,6 +21,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/qcom-geni-se.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
@@ -204,15 +205,17 @@ static void msm_geni_serial_power_off(struct uart_port *uport)
}
static int msm_geni_serial_poll_bit(struct uart_port *uport,
- int offset, int bit_field)
+ int offset, int bit_field, bool set)
{
int iter = 0;
unsigned int reg;
bool met = false;
+ bool cond = false;
while (iter < 1000) {
reg = geni_read_reg_nolog(uport->membase, offset);
- if (reg & bit_field) {
+ cond = reg & bit_field;
+ if (cond == set) {
met = true;
break;
}
@@ -225,8 +228,11 @@ static int msm_geni_serial_poll_bit(struct uart_port *uport,
static void msm_geni_serial_setup_tx(struct uart_port *uport,
unsigned int xmit_size)
{
+ u32 m_cmd = 0;
+
geni_write_reg_nolog(xmit_size, uport->membase, SE_UART_TX_TRANS_LEN);
- geni_setup_m_cmd(uport->membase, UART_START_TX, 0);
+ m_cmd |= (UART_START_TX << M_OPCODE_SHFT);
+ geni_write_reg_nolog(m_cmd, uport->membase, SE_GENI_M_CMD0);
/*
* Writes to enable the primary sequencer should go through before
* exiting this function.
@@ -240,21 +246,36 @@ static void msm_geni_serial_poll_cancel_tx(struct uart_port *uport)
unsigned int irq_clear = M_CMD_DONE_EN;
done = msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
- M_CMD_DONE_EN);
+ M_CMD_DONE_EN, true);
if (!done) {
- geni_cancel_m_cmd(uport->membase);
+ geni_write_reg_nolog(M_GENI_CMD_CANCEL, uport->membase,
+ SE_GENI_S_CMD_CTRL_REG);
irq_clear |= M_CMD_CANCEL_EN;
if (!msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
- M_CMD_CANCEL_EN)) {
- geni_abort_m_cmd(uport->membase);
+ M_CMD_CANCEL_EN, true)) {
+ geni_write_reg_nolog(M_GENI_CMD_ABORT, uport->membase,
+ SE_GENI_M_CMD_CTRL_REG);
irq_clear |= M_CMD_ABORT_EN;
msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
- M_CMD_ABORT_EN);
+ M_CMD_ABORT_EN, true);
}
}
geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_M_IRQ_CLEAR);
}
+static void msm_geni_serial_abort_rx(struct uart_port *uport)
+{
+ unsigned int irq_clear = S_CMD_DONE_EN;
+
+ geni_abort_s_cmd(uport->membase);
+ /* Ensure this goes through before polling. */
+ mb();
+ irq_clear |= S_CMD_ABORT_EN;
+ msm_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
+ S_GENI_CMD_ABORT, false);
+ geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
+}
+
#ifdef CONFIG_CONSOLE_POLL
static int msm_geni_serial_get_char(struct uart_port *uport)
{
@@ -263,7 +284,7 @@ static int msm_geni_serial_get_char(struct uart_port *uport)
unsigned int s_irq_status;
if (!(msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
- M_SEC_IRQ_EN))) {
+ M_SEC_IRQ_EN, true))) {
dev_err(uport->dev, "%s: Failed waiting for SE\n", __func__);
return -ENXIO;
}
@@ -276,7 +297,7 @@ static int msm_geni_serial_get_char(struct uart_port *uport)
geni_write_reg_nolog(s_irq_status, uport->membase, SE_GENI_S_IRQ_CLEAR);
if (!(msm_geni_serial_poll_bit(uport, SE_GENI_RX_FIFO_STATUS,
- RX_FIFO_WC_MSK))) {
+ RX_FIFO_WC_MSK, true))) {
dev_err(uport->dev, "%s: Failed waiting for Rx\n", __func__);
return -ENXIO;
}
@@ -302,7 +323,7 @@ static void msm_geni_serial_poll_put_char(struct uart_port *uport,
SE_GENI_TX_WATERMARK_REG);
msm_geni_serial_setup_tx(uport, 1);
if (!msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
- M_TX_FIFO_WATERMARK_EN))
+ M_TX_FIFO_WATERMARK_EN, true))
WARN_ON(1);
geni_write_reg_nolog(b, uport->membase, SE_GENI_TX_FIFOn);
geni_write_reg_nolog(M_TX_FIFO_WATERMARK_EN, uport->membase,
@@ -350,10 +371,15 @@ __msm_geni_serial_console_write(struct uart_port *uport, const char *s,
while (i < count) {
u32 chars_to_write = 0;
u32 avail_fifo_bytes = (port->tx_fifo_depth - port->tx_wm);
-
+ /*
+ * If the WM bit never set, then the Tx state machine is not
+ * in a valid state, so break, cancel/abort any existing
+ * command. Unfortunately the current data being written is
+ * lost.
+ */
while (!msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
- M_TX_FIFO_WATERMARK_EN))
- cpu_relax();
+ M_TX_FIFO_WATERMARK_EN, true))
+ break;
chars_to_write = min((unsigned int)(count - i),
avail_fifo_bytes);
if ((chars_to_write << 1) > avail_fifo_bytes)
@@ -470,10 +496,10 @@ static void msm_geni_serial_stop_tx(struct uart_port *uport)
geni_cancel_m_cmd(uport->membase);
if (!msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
- M_CMD_CANCEL_EN)) {
+ M_CMD_CANCEL_EN, true)) {
geni_abort_m_cmd(uport->membase);
msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
- M_CMD_ABORT_EN);
+ M_CMD_ABORT_EN, true);
geni_write_reg_nolog(M_CMD_ABORT_EN, uport->membase,
SE_GENI_M_IRQ_CLEAR);
}
@@ -485,6 +511,7 @@ static void msm_geni_serial_start_rx(struct uart_port *uport)
unsigned int geni_s_irq_en;
unsigned int geni_m_irq_en;
+ msm_geni_serial_abort_rx(uport);
geni_s_irq_en = geni_read_reg_nolog(uport->membase,
SE_GENI_S_IRQ_EN);
geni_m_irq_en = geni_read_reg_nolog(uport->membase,
@@ -522,11 +549,7 @@ static void msm_geni_serial_stop_rx(struct uart_port *uport)
/* Possible stop rx is called multiple times. */
if (!(geni_status & S_GENI_CMD_ACTIVE))
return;
- geni_write_reg_nolog(S_GENI_CMD_CANCEL, uport->membase,
- SE_GENI_S_CMD_CTRL_REG);
- if (!msm_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS,
- S_CMD_CANCEL_EN))
- WARN_ON(1);
+ msm_geni_serial_abort_rx(uport);
}
static int handle_rx_hs(struct uart_port *uport,
@@ -1011,6 +1034,11 @@ static int __init msm_geni_console_setup(struct console *co, char *options)
if (!dev_port->port_setup)
msm_geni_serial_port_setup(uport);
+ /*
+ * Make an unconditional cancel on the main sequencer to reset
+ * it else we could end up in data loss scenarios.
+ */
+ msm_geni_serial_poll_cancel_tx(uport);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -1087,6 +1115,11 @@ msm_geni_serial_earlycon_setup(struct earlycon_device *dev,
s_clk_cfg |= SER_CLK_EN;
s_clk_cfg |= (clk_div << CLK_DIV_SHFT);
+ /*
+ * Make an unconditional cancel on the main sequencer to reset
+ * it else we could end up in data loss scenarios.
+ */
+ msm_geni_serial_poll_cancel_tx(uport);
geni_serial_write_term_regs(uport, 0, tx_trans_cfg,
tx_parity_cfg, rx_trans_cfg, rx_parity_cfg, bits_per_char,
stop_bit, rx_stale, s_clk_cfg);
diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig
index 52c98ce..ee15d7d 100644
--- a/drivers/uio/Kconfig
+++ b/drivers/uio/Kconfig
@@ -155,4 +155,11 @@
If you compile this as a module, it will be called uio_mf624.
+config UIO_MSM_SHAREDMEM
+ bool "MSM shared memory driver"
+ default n
+ help
+ Provides the clients with their respective alloted shared memory
+ addresses which are used as transport buffer.
+
endif
diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile
index 8560dad..2282a69 100644
--- a/drivers/uio/Makefile
+++ b/drivers/uio/Makefile
@@ -9,3 +9,4 @@
obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o
obj-$(CONFIG_UIO_MF624) += uio_mf624.o
obj-$(CONFIG_UIO_FSL_ELBC_GPCM) += uio_fsl_elbc_gpcm.o
+obj-$(CONFIG_UIO_MSM_SHAREDMEM) += msm_sharedmem/
diff --git a/drivers/uio/msm_sharedmem/Makefile b/drivers/uio/msm_sharedmem/Makefile
new file mode 100644
index 0000000..e6b8570
--- /dev/null
+++ b/drivers/uio/msm_sharedmem/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_UIO_MSM_SHAREDMEM) := \
+ msm_sharedmem.o \
+ remote_filesystem_access_v01.o \
+ sharedmem_qmi.o \
diff --git a/drivers/uio/msm_sharedmem/msm_sharedmem.c b/drivers/uio/msm_sharedmem/msm_sharedmem.c
new file mode 100644
index 0000000..b25f55a
--- /dev/null
+++ b/drivers/uio/msm_sharedmem/msm_sharedmem.c
@@ -0,0 +1,240 @@
+/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ *
+ * This 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 DRIVER_NAME "msm_sharedmem"
+#define pr_fmt(fmt) DRIVER_NAME ": %s: " fmt, __func__
+
+#include <linux/uio_driver.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/dma-mapping.h>
+
+#include <soc/qcom/secure_buffer.h>
+
+#include "sharedmem_qmi.h"
+
+#define CLIENT_ID_PROP "qcom,client-id"
+
+#define MPSS_RMTS_CLIENT_ID 1
+
+static int uio_get_mem_index(struct uio_info *info, struct vm_area_struct *vma)
+{
+ if (vma->vm_pgoff >= MAX_UIO_MAPS)
+ return -EINVAL;
+
+ if (info->mem[vma->vm_pgoff].size == 0)
+ return -EINVAL;
+
+ return (int)vma->vm_pgoff;
+}
+
+static int sharedmem_mmap(struct uio_info *info, struct vm_area_struct *vma)
+{
+ int result;
+ struct uio_mem *mem;
+ int mem_index = uio_get_mem_index(info, vma);
+
+ if (mem_index < 0) {
+ pr_err("mem_index is invalid errno %d\n", mem_index);
+ return mem_index;
+ }
+
+ mem = info->mem + mem_index;
+
+ if (vma->vm_end - vma->vm_start > mem->size) {
+ pr_err("vm_end[%lu] - vm_start[%lu] [%lu] > mem->size[%pa]\n",
+ vma->vm_end, vma->vm_start,
+ (vma->vm_end - vma->vm_start), &mem->size);
+ return -EINVAL;
+ }
+ pr_debug("Attempting to setup mmap.\n");
+
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+ result = remap_pfn_range(vma,
+ vma->vm_start,
+ mem->addr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot);
+ if (result != 0)
+ pr_err("mmap Failed with errno %d\n", result);
+ else
+ pr_debug("mmap success\n");
+
+ return result;
+}
+
+/* Setup the shared ram permissions.
+ * This function currently supports the mpss client only.
+ */
+static void setup_shared_ram_perms(u32 client_id, phys_addr_t addr, u32 size)
+{
+ int ret;
+ u32 source_vmlist[1] = {VMID_HLOS};
+ int dest_vmids[2] = {VMID_HLOS, VMID_MSS_MSA};
+ int dest_perms[2] = {PERM_READ|PERM_WRITE,
+ PERM_READ|PERM_WRITE};
+
+ if (client_id != MPSS_RMTS_CLIENT_ID)
+ return;
+
+ ret = hyp_assign_phys(addr, size, source_vmlist, 1, dest_vmids,
+ dest_perms, 2);
+ if (ret != 0) {
+ if (ret == -EINVAL)
+ pr_warn("hyp_assign_phys is not supported!");
+ else
+ pr_err("hyp_assign_phys failed IPA=0x016%pa size=%u err=%d\n",
+ &addr, size, ret);
+ }
+}
+
+static int msm_sharedmem_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct uio_info *info = NULL;
+ struct resource *clnt_res = NULL;
+ u32 client_id = ((u32)~0U);
+ u32 shared_mem_size = 0;
+ void *shared_mem = NULL;
+ phys_addr_t shared_mem_pyhsical = 0;
+ bool is_addr_dynamic = false;
+ struct sharemem_qmi_entry qmi_entry;
+
+ /* Get the addresses from platform-data */
+ if (!pdev->dev.of_node) {
+ pr_err("Node not found\n");
+ ret = -ENODEV;
+ goto out;
+ }
+ clnt_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!clnt_res) {
+ pr_err("resource not found\n");
+ return -ENODEV;
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node, CLIENT_ID_PROP,
+ &client_id);
+ if (ret) {
+ client_id = ((u32)~0U);
+ pr_warn("qcom,client-id property not found\n");
+ }
+
+ info = devm_kzalloc(&pdev->dev, sizeof(struct uio_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ shared_mem_size = resource_size(clnt_res);
+ shared_mem_pyhsical = clnt_res->start;
+
+ if (shared_mem_size == 0) {
+ pr_err("Shared memory size is zero\n");
+ return -EINVAL;
+ }
+
+ if (shared_mem_pyhsical == 0) {
+ is_addr_dynamic = true;
+ shared_mem = dma_alloc_coherent(&pdev->dev, shared_mem_size,
+ &shared_mem_pyhsical, GFP_KERNEL);
+ if (shared_mem == NULL) {
+ pr_err("Shared mem alloc client=%s, size=%u\n",
+ clnt_res->name, shared_mem_size);
+ return -ENOMEM;
+ }
+ }
+
+ /* Set up the permissions for the shared ram that was allocated. */
+ setup_shared_ram_perms(client_id, shared_mem_pyhsical, shared_mem_size);
+
+ /* Setup device */
+ info->mmap = sharedmem_mmap; /* Custom mmap function. */
+ info->name = clnt_res->name;
+ info->version = "1.0";
+ info->mem[0].addr = shared_mem_pyhsical;
+ info->mem[0].size = shared_mem_size;
+ info->mem[0].memtype = UIO_MEM_PHYS;
+
+ ret = uio_register_device(&pdev->dev, info);
+ if (ret) {
+ pr_err("uio register failed ret=%d\n", ret);
+ goto out;
+ }
+ dev_set_drvdata(&pdev->dev, info);
+
+ qmi_entry.client_id = client_id;
+ qmi_entry.client_name = info->name;
+ qmi_entry.address = info->mem[0].addr;
+ qmi_entry.size = info->mem[0].size;
+ qmi_entry.is_addr_dynamic = is_addr_dynamic;
+
+ sharedmem_qmi_add_entry(&qmi_entry);
+ pr_info("Device created for client '%s'\n", clnt_res->name);
+out:
+ return ret;
+}
+
+static int msm_sharedmem_remove(struct platform_device *pdev)
+{
+ struct uio_info *info = dev_get_drvdata(&pdev->dev);
+
+ uio_unregister_device(info);
+
+ return 0;
+}
+
+static const struct of_device_id msm_sharedmem_of_match[] = {
+ {.compatible = "qcom,sharedmem-uio",},
+ {},
+};
+MODULE_DEVICE_TABLE(of, msm_sharedmem_of_match);
+
+static struct platform_driver msm_sharedmem_driver = {
+ .probe = msm_sharedmem_probe,
+ .remove = msm_sharedmem_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_sharedmem_of_match,
+ },
+};
+
+
+static int __init msm_sharedmem_init(void)
+{
+ int result;
+
+ result = sharedmem_qmi_init();
+ if (result < 0) {
+ pr_err("sharedmem_qmi_init failed result = %d\n", result);
+ return result;
+ }
+
+ result = platform_driver_register(&msm_sharedmem_driver);
+ if (result != 0) {
+ pr_err("Platform driver registration failed\n");
+ return result;
+ }
+ return 0;
+}
+
+static void __exit msm_sharedmem_exit(void)
+{
+ platform_driver_unregister(&msm_sharedmem_driver);
+ sharedmem_qmi_exit();
+}
+
+module_init(msm_sharedmem_init);
+module_exit(msm_sharedmem_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/uio/msm_sharedmem/remote_filesystem_access_v01.c b/drivers/uio/msm_sharedmem/remote_filesystem_access_v01.c
new file mode 100644
index 0000000..b04c913
--- /dev/null
+++ b/drivers/uio/msm_sharedmem/remote_filesystem_access_v01.c
@@ -0,0 +1,80 @@
+ /* 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
+ * 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/qmi_encdec.h>
+
+#include <soc/qcom/msm_qmi_interface.h>
+
+#include "remote_filesystem_access_v01.h"
+
+struct elem_info rfsa_get_buff_addr_req_msg_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct rfsa_get_buff_addr_req_msg_v01,
+ client_id),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct rfsa_get_buff_addr_req_msg_v01,
+ size),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info rfsa_get_buff_addr_resp_msg_v01_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct qmi_response_type_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct rfsa_get_buff_addr_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct rfsa_get_buff_addr_resp_msg_v01,
+ address_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct rfsa_get_buff_addr_resp_msg_v01,
+ address),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
diff --git a/drivers/uio/msm_sharedmem/remote_filesystem_access_v01.h b/drivers/uio/msm_sharedmem/remote_filesystem_access_v01.h
new file mode 100644
index 0000000..7ea8ce6
--- /dev/null
+++ b/drivers/uio/msm_sharedmem/remote_filesystem_access_v01.h
@@ -0,0 +1,39 @@
+ /* 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
+ * 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 __REMOTE_FILESYSTEM_ACCESS_V01_H__
+#define __REMOTE_FILESYSTEM_ACCESS_V01_H__
+
+#define RFSA_SERVICE_ID_V01 0x1C
+#define RFSA_SERVICE_VERS_V01 0x01
+
+#define QMI_RFSA_GET_BUFF_ADDR_REQ_MSG_V01 0x0023
+#define QMI_RFSA_GET_BUFF_ADDR_RESP_MSG_V01 0x0023
+
+#define RFSA_GET_BUFF_ADDR_REQ_MSG_MAX_LEN_V01 14
+#define RFSA_GET_BUFF_ADDR_RESP_MSG_MAX_LEN_V01 18
+
+extern struct elem_info rfsa_get_buff_addr_req_msg_v01_ei[];
+extern struct elem_info rfsa_get_buff_addr_resp_msg_v01_ei[];
+
+struct rfsa_get_buff_addr_req_msg_v01 {
+ uint32_t client_id;
+ uint32_t size;
+};
+
+struct rfsa_get_buff_addr_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+ uint8_t address_valid;
+ uint64_t address;
+};
+
+#endif /* __REMOTE_FILESYSTEM_ACCESS_V01_H__ */
diff --git a/drivers/uio/msm_sharedmem/sharedmem_qmi.c b/drivers/uio/msm_sharedmem/sharedmem_qmi.c
new file mode 100644
index 0000000..fd95dee
--- /dev/null
+++ b/drivers/uio/msm_sharedmem/sharedmem_qmi.c
@@ -0,0 +1,453 @@
+/* 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
+ * 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 DRIVER_NAME "msm_sharedmem"
+#define pr_fmt(fmt) DRIVER_NAME ": %s: " fmt, __func__
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/rwsem.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/debugfs.h>
+#include <soc/qcom/msm_qmi_interface.h>
+#include "sharedmem_qmi.h"
+#include "remote_filesystem_access_v01.h"
+
+#define RFSA_SERVICE_INSTANCE_NUM 1
+#define SHARED_ADDR_ENTRY_NAME_MAX_LEN 10
+
+struct shared_addr_entry {
+ u32 id;
+ u64 address;
+ u32 size;
+ u64 request_count;
+ bool is_addr_dynamic;
+ char name[SHARED_ADDR_ENTRY_NAME_MAX_LEN + 1];
+};
+
+struct shared_addr_list {
+ struct list_head node;
+ struct shared_addr_entry entry;
+};
+
+static struct shared_addr_list list;
+
+static struct qmi_handle *sharedmem_qmi_svc_handle;
+static void sharedmem_qmi_svc_recv_msg(struct work_struct *work);
+static DECLARE_DELAYED_WORK(work_recv_msg, sharedmem_qmi_svc_recv_msg);
+static struct workqueue_struct *sharedmem_qmi_svc_workqueue;
+static struct dentry *dir_ent;
+
+static u32 rfsa_count;
+static u32 rmts_count;
+
+static DECLARE_RWSEM(sharedmem_list_lock); /* declare list lock semaphore */
+
+static struct work_struct sharedmem_qmi_init_work;
+
+static struct msg_desc rfsa_get_buffer_addr_req_desc = {
+ .max_msg_len = RFSA_GET_BUFF_ADDR_REQ_MSG_MAX_LEN_V01,
+ .msg_id = QMI_RFSA_GET_BUFF_ADDR_REQ_MSG_V01,
+ .ei_array = rfsa_get_buff_addr_req_msg_v01_ei,
+};
+
+static struct msg_desc rfsa_get_buffer_addr_resp_desc = {
+ .max_msg_len = RFSA_GET_BUFF_ADDR_RESP_MSG_MAX_LEN_V01,
+ .msg_id = QMI_RFSA_GET_BUFF_ADDR_RESP_MSG_V01,
+ .ei_array = rfsa_get_buff_addr_resp_msg_v01_ei,
+};
+
+void sharedmem_qmi_add_entry(struct sharemem_qmi_entry *qmi_entry)
+{
+ struct shared_addr_list *list_entry;
+
+ list_entry = kzalloc(sizeof(*list_entry), GFP_KERNEL);
+
+ /* If we cannot add the entry log the failure and bail */
+ if (list_entry == NULL) {
+ pr_err("Alloc of new list entry failed\n");
+ return;
+ }
+
+ /* Copy as much of the client name that can fit in the entry. */
+ strlcpy(list_entry->entry.name, qmi_entry->client_name,
+ sizeof(list_entry->entry.name));
+
+ /* Setup the rest of the entry. */
+ list_entry->entry.id = qmi_entry->client_id;
+ list_entry->entry.address = qmi_entry->address;
+ list_entry->entry.size = qmi_entry->size;
+ list_entry->entry.is_addr_dynamic = qmi_entry->is_addr_dynamic;
+ list_entry->entry.request_count = 0;
+
+ down_write(&sharedmem_list_lock);
+ list_add_tail(&(list_entry->node), &(list.node));
+ up_write(&sharedmem_list_lock);
+ pr_debug("Added new entry to list\n");
+
+}
+
+static int get_buffer_for_client(u32 id, u32 size, u64 *address)
+{
+ int result = -ENOENT;
+ int client_found = 0;
+ struct list_head *curr_node;
+ struct shared_addr_list *list_entry;
+
+ if (size == 0)
+ return -ENOMEM;
+
+ down_read(&sharedmem_list_lock);
+
+ list_for_each(curr_node, &list.node) {
+ list_entry = list_entry(curr_node, struct shared_addr_list,
+ node);
+ if (list_entry->entry.id == id) {
+ if (list_entry->entry.size >= size) {
+ *address = list_entry->entry.address;
+ list_entry->entry.request_count++;
+ result = 0;
+ } else {
+ pr_err("Shared mem req too large for id=%u\n",
+ id);
+ result = -ENOMEM;
+ }
+ client_found = 1;
+ break;
+ }
+ }
+
+ up_read(&sharedmem_list_lock);
+
+ if (client_found != 1) {
+ pr_err("Unknown client id %u\n", id);
+ result = -ENOENT;
+ }
+ return result;
+}
+
+static int sharedmem_qmi_get_buffer(void *conn_h, void *req_handle, void *req)
+{
+ struct rfsa_get_buff_addr_req_msg_v01 *get_buffer_req;
+ struct rfsa_get_buff_addr_resp_msg_v01 get_buffer_resp;
+ int result;
+ u64 address = 0;
+
+ get_buffer_req = (struct rfsa_get_buff_addr_req_msg_v01 *)req;
+ pr_debug("req->client_id = 0x%X and req->size = %d\n",
+ get_buffer_req->client_id, get_buffer_req->size);
+
+ result = get_buffer_for_client(get_buffer_req->client_id,
+ get_buffer_req->size, &address);
+ if (result != 0)
+ return result;
+
+ if (address == 0) {
+ pr_err("Entry found for client id= 0x%X but address is zero\n",
+ get_buffer_req->client_id);
+ return -ENOMEM;
+ }
+
+ memset(&get_buffer_resp, 0, sizeof(get_buffer_resp));
+ get_buffer_resp.address_valid = 1;
+ get_buffer_resp.address = address;
+ get_buffer_resp.resp.result = QMI_RESULT_SUCCESS_V01;
+
+ result = qmi_send_resp_from_cb(sharedmem_qmi_svc_handle, conn_h,
+ req_handle,
+ &rfsa_get_buffer_addr_resp_desc,
+ &get_buffer_resp,
+ sizeof(get_buffer_resp));
+ return result;
+}
+
+
+static int sharedmem_qmi_connect_cb(struct qmi_handle *handle, void *conn_h)
+{
+ if (sharedmem_qmi_svc_handle != handle || !conn_h)
+ return -EINVAL;
+ return 0;
+}
+
+static int sharedmem_qmi_disconnect_cb(struct qmi_handle *handle, void *conn_h)
+{
+ if (sharedmem_qmi_svc_handle != handle || !conn_h)
+ return -EINVAL;
+ return 0;
+}
+
+static int sharedmem_qmi_req_desc_cb(unsigned int msg_id,
+ struct msg_desc **req_desc)
+{
+ int rc;
+
+ switch (msg_id) {
+ case QMI_RFSA_GET_BUFF_ADDR_REQ_MSG_V01:
+ *req_desc = &rfsa_get_buffer_addr_req_desc;
+ rc = sizeof(struct rfsa_get_buff_addr_req_msg_v01);
+ break;
+
+ default:
+ rc = -ENOTSUPP;
+ break;
+ }
+ return rc;
+}
+
+static int sharedmem_qmi_req_cb(struct qmi_handle *handle, void *conn_h,
+ void *req_handle, unsigned int msg_id,
+ void *req)
+{
+ int rc = -ENOTSUPP;
+
+ if (sharedmem_qmi_svc_handle != handle || !conn_h)
+ return -EINVAL;
+
+ if (msg_id == QMI_RFSA_GET_BUFF_ADDR_REQ_MSG_V01)
+ rc = sharedmem_qmi_get_buffer(conn_h, req_handle, req);
+
+ return rc;
+}
+
+#define DEBUG_BUF_SIZE (2048)
+static char *debug_buffer;
+static u32 debug_data_size;
+static struct mutex dbg_buf_lock; /* mutex for debug_buffer */
+
+static ssize_t debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *file_pos)
+{
+ return simple_read_from_buffer(buf, count, file_pos, debug_buffer,
+ debug_data_size);
+}
+
+static u32 fill_debug_info(char *buffer, u32 buffer_size)
+{
+ u32 size = 0;
+ struct list_head *curr_node;
+ struct shared_addr_list *list_entry;
+
+ memset(buffer, 0, buffer_size);
+ size += scnprintf(buffer + size, buffer_size - size, "\n");
+
+ down_read(&sharedmem_list_lock);
+ list_for_each(curr_node, &list.node) {
+ list_entry = list_entry(curr_node, struct shared_addr_list,
+ node);
+ size += scnprintf(buffer + size, buffer_size - size,
+ "Client_name: %s\n", list_entry->entry.name);
+ size += scnprintf(buffer + size, buffer_size - size,
+ "Client_id: 0x%08X\n", list_entry->entry.id);
+ size += scnprintf(buffer + size, buffer_size - size,
+ "Buffer Size: 0x%08X (%d)\n",
+ list_entry->entry.size,
+ list_entry->entry.size);
+ size += scnprintf(buffer + size, buffer_size - size,
+ "Address: 0x%016llX\n",
+ list_entry->entry.address);
+ size += scnprintf(buffer + size, buffer_size - size,
+ "Address Allocation: %s\n",
+ (list_entry->entry.is_addr_dynamic ?
+ "Dynamic" : "Static"));
+ size += scnprintf(buffer + size, buffer_size - size,
+ "Request count: %llu\n",
+ list_entry->entry.request_count);
+ size += scnprintf(buffer + size, buffer_size - size, "\n\n");
+ }
+ up_read(&sharedmem_list_lock);
+
+ size += scnprintf(buffer + size, buffer_size - size,
+ "RFSA server start count = %u\n", rfsa_count);
+ size += scnprintf(buffer + size, buffer_size - size,
+ "RMTS server start count = %u\n", rmts_count);
+
+ size += scnprintf(buffer + size, buffer_size - size, "\n");
+ return size;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+ u32 buffer_size;
+
+ mutex_lock(&dbg_buf_lock);
+ if (debug_buffer != NULL) {
+ mutex_unlock(&dbg_buf_lock);
+ return -EBUSY;
+ }
+ buffer_size = DEBUG_BUF_SIZE;
+ debug_buffer = kzalloc(buffer_size, GFP_KERNEL);
+ if (debug_buffer == NULL) {
+ mutex_unlock(&dbg_buf_lock);
+ return -ENOMEM;
+ }
+ debug_data_size = fill_debug_info(debug_buffer, buffer_size);
+ mutex_unlock(&dbg_buf_lock);
+ return 0;
+}
+
+static int debug_close(struct inode *inode, struct file *file)
+{
+ mutex_lock(&dbg_buf_lock);
+ kfree(debug_buffer);
+ debug_buffer = NULL;
+ debug_data_size = 0;
+ mutex_unlock(&dbg_buf_lock);
+ return 0;
+}
+
+static const struct file_operations debug_ops = {
+ .read = debug_read,
+ .open = debug_open,
+ .release = debug_close,
+};
+
+static int rfsa_increment(void *data, u64 val)
+{
+ if (rfsa_count != ~0)
+ rfsa_count++;
+ return 0;
+}
+
+static int rmts_increment(void *data, u64 val)
+{
+ if (rmts_count != ~0)
+ rmts_count++;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(rfsa_fops, NULL, rfsa_increment, "%llu\n");
+DEFINE_SIMPLE_ATTRIBUTE(rmts_fops, NULL, rmts_increment, "%llu\n");
+
+static void debugfs_init(void)
+{
+ struct dentry *f_ent;
+
+ mutex_init(&dbg_buf_lock);
+ dir_ent = debugfs_create_dir("rmt_storage", NULL);
+ if (IS_ERR(dir_ent)) {
+ pr_err("Failed to create debug_fs directory\n");
+ return;
+ }
+
+ f_ent = debugfs_create_file("info", 0400, dir_ent, NULL, &debug_ops);
+ if (IS_ERR(f_ent)) {
+ pr_err("Failed to create debug_fs info file\n");
+ return;
+ }
+
+ f_ent = debugfs_create_file("rfsa", 0200, dir_ent, NULL, &rfsa_fops);
+ if (IS_ERR(f_ent)) {
+ pr_err("Failed to create debug_fs rfsa file\n");
+ return;
+ }
+
+ f_ent = debugfs_create_file("rmts", 0200, dir_ent, NULL, &rmts_fops);
+ if (IS_ERR(f_ent)) {
+ pr_err("Failed to create debug_fs rmts file\n");
+ return;
+ }
+}
+
+static void debugfs_exit(void)
+{
+ debugfs_remove_recursive(dir_ent);
+ mutex_destroy(&dbg_buf_lock);
+}
+
+static void sharedmem_qmi_svc_recv_msg(struct work_struct *work)
+{
+ int rc;
+
+ do {
+ pr_debug("Notified about a Receive Event\n");
+ } while ((rc = qmi_recv_msg(sharedmem_qmi_svc_handle)) == 0);
+
+ if (rc != -ENOMSG)
+ pr_err("Error receiving message\n");
+}
+
+static void sharedmem_qmi_notify(struct qmi_handle *handle,
+ enum qmi_event_type event, void *priv)
+{
+ switch (event) {
+ case QMI_RECV_MSG:
+ queue_delayed_work(sharedmem_qmi_svc_workqueue,
+ &work_recv_msg, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+static struct qmi_svc_ops_options sharedmem_qmi_ops_options = {
+ .version = 1,
+ .service_id = RFSA_SERVICE_ID_V01,
+ .service_vers = RFSA_SERVICE_VERS_V01,
+ .service_ins = RFSA_SERVICE_INSTANCE_NUM,
+ .connect_cb = sharedmem_qmi_connect_cb,
+ .disconnect_cb = sharedmem_qmi_disconnect_cb,
+ .req_desc_cb = sharedmem_qmi_req_desc_cb,
+ .req_cb = sharedmem_qmi_req_cb,
+};
+
+
+static void sharedmem_register_qmi(void)
+{
+ int rc;
+
+ sharedmem_qmi_svc_workqueue =
+ create_singlethread_workqueue("sharedmem_qmi_work");
+ if (!sharedmem_qmi_svc_workqueue)
+ return;
+
+ sharedmem_qmi_svc_handle = qmi_handle_create(sharedmem_qmi_notify,
+ NULL);
+ if (!sharedmem_qmi_svc_handle) {
+ pr_err("Creating sharedmem_qmi qmi handle failed\n");
+ destroy_workqueue(sharedmem_qmi_svc_workqueue);
+ return;
+ }
+ rc = qmi_svc_register(sharedmem_qmi_svc_handle,
+ &sharedmem_qmi_ops_options);
+ if (rc < 0) {
+ pr_err("Registering sharedmem_qmi failed %d\n", rc);
+ qmi_handle_destroy(sharedmem_qmi_svc_handle);
+ destroy_workqueue(sharedmem_qmi_svc_workqueue);
+ return;
+ }
+ pr_info("qmi init successful\n");
+}
+
+static void sharedmem_qmi_init_worker(struct work_struct *work)
+{
+ sharedmem_register_qmi();
+ debugfs_init();
+}
+
+int sharedmem_qmi_init(void)
+{
+ INIT_LIST_HEAD(&list.node);
+ INIT_WORK(&sharedmem_qmi_init_work, sharedmem_qmi_init_worker);
+ schedule_work(&sharedmem_qmi_init_work);
+ return 0;
+}
+
+void sharedmem_qmi_exit(void)
+{
+ qmi_svc_unregister(sharedmem_qmi_svc_handle);
+ flush_workqueue(sharedmem_qmi_svc_workqueue);
+ qmi_handle_destroy(sharedmem_qmi_svc_handle);
+ destroy_workqueue(sharedmem_qmi_svc_workqueue);
+ debugfs_exit();
+}
diff --git a/drivers/uio/msm_sharedmem/sharedmem_qmi.h b/drivers/uio/msm_sharedmem/sharedmem_qmi.h
new file mode 100644
index 0000000..7353916
--- /dev/null
+++ b/drivers/uio/msm_sharedmem/sharedmem_qmi.h
@@ -0,0 +1,33 @@
+/* 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
+ * 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 __SHAREDMEM_QMI_H__
+#define __SHAREDMEM_QMI_H__
+
+#include <linux/module.h>
+
+struct sharemem_qmi_entry {
+ const char *client_name;
+ u32 client_id;
+ u64 address;
+ u32 size;
+ bool is_addr_dynamic;
+};
+
+int sharedmem_qmi_init(void);
+
+void sharedmem_qmi_exit(void);
+
+void sharedmem_qmi_add_entry(struct sharemem_qmi_entry *qmi_entry);
+
+#endif /* __SHAREDMEM_QMI_H__ */
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index 3889809..37591a4 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -24,7 +24,6 @@ static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
switch (event) {
case CI_HDRC_CONTROLLER_RESET_EVENT:
dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n");
- writel(0, USB_AHBBURST);
/* use AHB transactor, allow posted data writes */
writel(0x8, USB_AHBMODE);
usb_phy_init(ci->usb_phy);
@@ -47,7 +46,8 @@ static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = {
.name = "ci_hdrc_msm",
.capoffset = DEF_CAPOFFSET,
.flags = CI_HDRC_REGS_SHARED |
- CI_HDRC_DISABLE_STREAMING,
+ CI_HDRC_DISABLE_STREAMING |
+ CI_HDRC_OVERRIDE_AHB_BURST,
.notify_event = ci_hdrc_msm_notify_event,
};
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index c3d249f..edb7a9a 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2661,8 +2661,15 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
if (ret < 0)
return ret;
- /* The port state is unknown until the reset completes. */
- if (!(portstatus & USB_PORT_STAT_RESET))
+ /*
+ * The port state is unknown until the reset completes.
+ *
+ * On top of that, some chips may require additional time
+ * to re-establish a connection after the reset is complete,
+ * so also wait for the connection to be re-established.
+ */
+ if (!(portstatus & USB_PORT_STAT_RESET) &&
+ (portstatus & USB_PORT_STAT_CONNECTION))
break;
/* switch to the long delay after two short delay failures */
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 96684f4..a159011 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -195,8 +195,6 @@ struct dwc3_msm {
unsigned int max_power;
bool charging_disabled;
enum usb_otg_state otg_state;
- struct work_struct bus_vote_w;
- unsigned int bus_vote;
u32 bus_perf_client;
struct msm_bus_scale_pdata *bus_scale_table;
struct power_supply *usb_psy;
@@ -810,7 +808,8 @@ static void gsi_get_channel_info(struct usb_ep *ep,
/* Provide physical USB addresses for DEPCMD and GEVENTCNT registers */
ch_info->depcmd_low_addr = (u32)(dwc->reg_phys +
- DWC3_DEPCMD);
+ DWC3_DEP_BASE(dep->number) + DWC3_DEPCMD);
+
ch_info->depcmd_hi_addr = 0;
ch_info->xfer_ring_base_addr = dwc3_trb_dma_offset(dep,
@@ -1964,17 +1963,6 @@ static int dwc3_msm_prepare_suspend(struct dwc3_msm *mdwc)
return 0;
}
-static void dwc3_msm_bus_vote_w(struct work_struct *w)
-{
- struct dwc3_msm *mdwc = container_of(w, struct dwc3_msm, bus_vote_w);
- int ret;
-
- ret = msm_bus_scale_client_update_request(mdwc->bus_perf_client,
- mdwc->bus_vote);
- if (ret)
- dev_err(mdwc->dev, "Failed to reset bus bw vote %d\n", ret);
-}
-
static void dwc3_set_phy_speed_flags(struct dwc3_msm *mdwc)
{
struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
@@ -2135,8 +2123,12 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
/* Remove bus voting */
if (mdwc->bus_perf_client) {
- mdwc->bus_vote = 0;
- schedule_work(&mdwc->bus_vote_w);
+ dbg_event(0xFF, "bus_devote_start", 0);
+ ret = msm_bus_scale_client_update_request(
+ mdwc->bus_perf_client, 0);
+ dbg_event(0xFF, "bus_devote_finish", 0);
+ if (ret)
+ dev_err(mdwc->dev, "bus bw unvoting failed %d\n", ret);
}
/*
@@ -2190,8 +2182,12 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)
/* Enable bus voting */
if (mdwc->bus_perf_client) {
- mdwc->bus_vote = 1;
- schedule_work(&mdwc->bus_vote_w);
+ dbg_event(0xFF, "bus_vote_start", 1);
+ ret = msm_bus_scale_client_update_request(
+ mdwc->bus_perf_client, 1);
+ dbg_event(0xFF, "bus_vote_finish", 1);
+ if (ret)
+ dev_err(mdwc->dev, "bus bw voting failed %d\n", ret);
}
/* Vote for TCXO while waking up USB HSPHY */
@@ -2371,9 +2367,47 @@ static void dwc3_resume_work(struct work_struct *w)
{
struct dwc3_msm *mdwc = container_of(w, struct dwc3_msm, resume_work);
struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
+ union extcon_property_value val;
+ unsigned int extcon_id;
+ struct extcon_dev *edev = NULL;
+ int ret = 0;
dev_dbg(mdwc->dev, "%s: dwc3 resume work\n", __func__);
+ if (mdwc->vbus_active) {
+ edev = mdwc->extcon_vbus;
+ extcon_id = EXTCON_USB;
+ } else if (mdwc->id_state == DWC3_ID_GROUND) {
+ edev = mdwc->extcon_id;
+ extcon_id = EXTCON_USB_HOST;
+ }
+
+ /* Check speed and Type-C polarity values in order to configure PHY */
+ if (edev && extcon_get_state(edev, extcon_id)) {
+ ret = extcon_get_property(edev, extcon_id,
+ EXTCON_PROP_USB_SS, &val);
+
+ /* Use default dwc->maximum_speed if speed isn't reported */
+ if (!ret)
+ dwc->maximum_speed = (val.intval == 0) ?
+ USB_SPEED_HIGH : USB_SPEED_SUPER;
+
+ if (dwc->maximum_speed > dwc->max_hw_supp_speed)
+ dwc->maximum_speed = dwc->max_hw_supp_speed;
+
+ dbg_event(0xFF, "speed", dwc->maximum_speed);
+
+ ret = extcon_get_property(edev, extcon_id,
+ EXTCON_PROP_USB_TYPEC_POLARITY, &val);
+ if (ret)
+ mdwc->typec_orientation = ORIENTATION_NONE;
+ else
+ mdwc->typec_orientation = val.intval ?
+ ORIENTATION_CC2 : ORIENTATION_CC1;
+
+ dbg_event(0xFF, "cc_state", mdwc->typec_orientation);
+ }
+
/*
* exit LPM first to meet resume timeline from device side.
* resume_pending flag would prevent calling
@@ -2617,45 +2651,18 @@ static int dwc3_msm_id_notifier(struct notifier_block *nb,
{
struct dwc3_msm *mdwc = container_of(nb, struct dwc3_msm, id_nb);
struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
- struct extcon_dev *edev = ptr;
enum dwc3_id_state id;
- int cc_state;
- int speed;
-
- if (!edev) {
- dev_err(mdwc->dev, "%s: edev null\n", __func__);
- goto done;
- }
id = event ? DWC3_ID_GROUND : DWC3_ID_FLOAT;
dev_dbg(mdwc->dev, "host:%ld (id:%d) event received\n", event, id);
- cc_state = extcon_get_cable_state_(edev, EXTCON_USB_CC);
- if (cc_state < 0)
- mdwc->typec_orientation = ORIENTATION_NONE;
- else
- mdwc->typec_orientation =
- cc_state ? ORIENTATION_CC2 : ORIENTATION_CC1;
-
- dbg_event(0xFF, "cc_state", mdwc->typec_orientation);
-
- speed = extcon_get_cable_state_(edev, EXTCON_USB_SPEED);
- /* Use default dwc->maximum_speed if extcon doesn't report speed. */
- if (speed >= 0)
- dwc->maximum_speed =
- (speed == 0) ? USB_SPEED_HIGH : USB_SPEED_SUPER;
-
- if (dwc->maximum_speed > dwc->max_hw_supp_speed)
- dwc->maximum_speed = dwc->max_hw_supp_speed;
-
if (mdwc->id_state != id) {
mdwc->id_state = id;
dbg_event(0xFF, "id_state", mdwc->id_state);
queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
}
-done:
return NOTIFY_DONE;
}
@@ -2664,44 +2671,19 @@ static int dwc3_msm_vbus_notifier(struct notifier_block *nb,
{
struct dwc3_msm *mdwc = container_of(nb, struct dwc3_msm, vbus_nb);
struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
- struct extcon_dev *edev = ptr;
- int cc_state;
- int speed;
-
- if (!edev) {
- dev_err(mdwc->dev, "%s: edev null\n", __func__);
- goto done;
- }
dev_dbg(mdwc->dev, "vbus:%ld event received\n", event);
if (mdwc->vbus_active == event)
return NOTIFY_DONE;
- cc_state = extcon_get_cable_state_(edev, EXTCON_USB_CC);
- if (cc_state < 0)
- mdwc->typec_orientation = ORIENTATION_NONE;
- else
- mdwc->typec_orientation =
- cc_state ? ORIENTATION_CC2 : ORIENTATION_CC1;
-
- dbg_event(0xFF, "cc_state", mdwc->typec_orientation);
-
- speed = extcon_get_cable_state_(edev, EXTCON_USB_SPEED);
- /* Use default dwc->maximum_speed if extcon doesn't report speed. */
- if (speed >= 0)
- dwc->maximum_speed =
- (speed == 0) ? USB_SPEED_HIGH : USB_SPEED_SUPER;
-
- if (dwc->maximum_speed > dwc->max_hw_supp_speed)
- dwc->maximum_speed = dwc->max_hw_supp_speed;
-
mdwc->vbus_active = event;
if (dwc->is_drd && !mdwc->in_restart)
queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
-done:
+
return NOTIFY_DONE;
}
+
/*
* Handle EUD based soft detach/attach event, and force USB high speed mode
* functionality on receiving soft attach event.
@@ -2717,12 +2699,6 @@ static int dwc3_msm_eud_notifier(struct notifier_block *nb,
{
struct dwc3_msm *mdwc = container_of(nb, struct dwc3_msm, eud_event_nb);
struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
- struct extcon_dev *edev = ptr;
-
- if (!edev) {
- dev_err(mdwc->dev, "%s: edev null\n", __func__);
- goto done;
- }
dbg_event(0xFF, "EUD_NB", event);
dev_dbg(mdwc->dev, "eud:%ld event received\n", event);
@@ -2735,7 +2711,7 @@ static int dwc3_msm_eud_notifier(struct notifier_block *nb,
mdwc->vbus_active = event;
if (dwc->is_drd && !mdwc->in_restart)
queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
-done:
+
return NOTIFY_DONE;
}
@@ -2971,7 +2947,6 @@ static int dwc3_msm_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&mdwc->req_complete_list);
INIT_WORK(&mdwc->resume_work, dwc3_resume_work);
INIT_WORK(&mdwc->restart_usb_work, dwc3_restart_usb_work);
- INIT_WORK(&mdwc->bus_vote_w, dwc3_msm_bus_vote_w);
INIT_WORK(&mdwc->vbus_draw_work, dwc3_msm_vbus_draw_work);
INIT_DELAYED_WORK(&mdwc->sm_work, dwc3_otg_sm_work);
INIT_DELAYED_WORK(&mdwc->perf_vote_work, msm_dwc3_perf_vote_work);
@@ -3276,10 +3251,10 @@ static int dwc3_msm_probe(struct platform_device *pdev)
}
/* Update initial VBUS/ID state from extcon */
- if (mdwc->extcon_vbus && extcon_get_cable_state_(mdwc->extcon_vbus,
+ if (mdwc->extcon_vbus && extcon_get_state(mdwc->extcon_vbus,
EXTCON_USB))
dwc3_msm_vbus_notifier(&mdwc->vbus_nb, true, mdwc->extcon_vbus);
- else if (mdwc->extcon_id && extcon_get_cable_state_(mdwc->extcon_id,
+ else if (mdwc->extcon_id && extcon_get_state(mdwc->extcon_id,
EXTCON_USB_HOST))
dwc3_msm_id_notifier(&mdwc->id_nb, true, mdwc->extcon_id);
else if (!pval.intval) {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index a5d3209f..0dfe271 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -284,6 +284,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status)
{
struct dwc3 *dwc = dep->dwc;
+ unsigned int unmap_after_complete = false;
req->started = false;
list_del(&req->list);
@@ -292,11 +293,22 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
if (req->request.status == -EINPROGRESS)
req->request.status = status;
- if (dwc->ep0_bounced && dep->number <= 1)
+ /*
+ * NOTICE we don't want to unmap before calling ->complete() if we're
+ * dealing with a bounced ep0 request. If we unmap it here, we would end
+ * up overwritting the contents of req->buf and this could confuse the
+ * gadget driver.
+ */
+ if (dwc->ep0_bounced && dep->number <= 1) {
dwc->ep0_bounced = false;
- usb_gadget_unmap_request_by_dev(dwc->sysdev,
- &req->request, req->direction);
+ usb_gadget_unmap_request_by_dev(dwc->sysdev,
+ &req->request, req->direction);
+ unmap_after_complete = true;
+ } else {
+ usb_gadget_unmap_request(&dwc->gadget,
+ &req->request, req->direction);
+ }
trace_dwc3_gadget_giveback(req);
@@ -304,8 +316,9 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
usb_gadget_giveback_request(&dep->endpoint, &req->request);
spin_lock(&dwc->lock);
- if (dep->number > 1)
- pm_runtime_put(dwc->dev);
+ if (unmap_after_complete)
+ usb_gadget_unmap_request(&dwc->gadget,
+ &req->request, req->direction);
}
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
@@ -1225,7 +1238,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
return ret;
}
- pm_runtime_get(dwc->dev);
req->request.actual = 0;
req->request.status = -EINPROGRESS;
req->direction = dep->direction;
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index e52bf45..e9e8f46 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -21,11 +21,12 @@
int dwc3_host_init(struct dwc3 *dwc)
{
- struct property_entry props[2];
+ struct property_entry props[3];
struct platform_device *xhci;
int ret, irq;
struct resource *res;
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
+ int prop_idx = 0;
irq = platform_get_irq_byname(dwc3_pdev, "host");
if (irq == -EPROBE_DEFER)
@@ -85,8 +86,22 @@ int dwc3_host_init(struct dwc3 *dwc)
memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
- if (dwc->usb3_lpm_capable) {
- props[0].name = "usb3-lpm-capable";
+ if (dwc->usb3_lpm_capable)
+ props[prop_idx++].name = "usb3-lpm-capable";
+
+ /**
+ * WORKAROUND: dwc3 revisions <=3.00a have a limitation
+ * where Port Disable command doesn't work.
+ *
+ * The suggested workaround is that we avoid Port Disable
+ * completely.
+ *
+ * This following flag tells XHCI to do just that.
+ */
+ if (dwc->revision <= DWC3_REVISION_300A)
+ props[prop_idx++].name = "quirk-broken-port-ped";
+
+ if (prop_idx) {
ret = platform_device_add_properties(xhci, props);
if (ret) {
dev_err(dwc->dev, "failed to add properties to xHCI\n");
diff --git a/drivers/usb/gadget/function/f_diag.c b/drivers/usb/gadget/function/f_diag.c
index 51e6104..e908ecf 100644
--- a/drivers/usb/gadget/function/f_diag.c
+++ b/drivers/usb/gadget/function/f_diag.c
@@ -307,21 +307,8 @@ static void diag_write_complete(struct usb_ep *ep,
ctxt->dpkts_tolaptop_pending--;
- if (!req->status) {
- if ((req->length >= ep->maxpacket) &&
- ((req->length % ep->maxpacket) == 0)) {
- ctxt->dpkts_tolaptop_pending++;
- req->length = 0;
- d_req->actual = req->actual;
- d_req->status = req->status;
- /* Queue zero length packet */
- if (!usb_ep_queue(ctxt->in, req, GFP_ATOMIC))
- return;
- ctxt->dpkts_tolaptop_pending--;
- } else {
- ctxt->dpkts_tolaptop++;
- }
- }
+ if (!req->status)
+ ctxt->dpkts_tolaptop++;
spin_lock_irqsave(&ctxt->lock, flags);
list_add_tail(&req->list, &ctxt->write_pool);
@@ -481,6 +468,7 @@ int usb_diag_alloc_req(struct usb_diag_ch *ch, int n_write, int n_read)
goto fail;
kmemleak_not_leak(req);
req->complete = diag_write_complete;
+ req->zero = true;
list_add_tail(&req->list, &ctxt->write_pool);
}
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 54e14b1..cca261e 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -2285,16 +2285,15 @@ static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg)
fsg->bulk_out_enabled = 0;
}
+ /* allow usb LPM after eps are disabled */
+ usb_gadget_autopm_put_async(common->gadget);
common->fsg = NULL;
wake_up(&common->fsg_wait);
}
common->running = 0;
- if (!new_fsg || rc) {
- /* allow usb LPM after eps are disabled */
- usb_gadget_autopm_put_async(common->gadget);
+ if (!new_fsg || rc)
return rc;
- }
common->fsg = new_fsg;
fsg = common->fsg;
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 681b77a..47b2817 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -469,6 +469,12 @@ static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,
return;
}
+ if (xhci->quirks & XHCI_BROKEN_PORT_PED) {
+ xhci_dbg(xhci,
+ "Broken Port Enabled/Disabled, ignoring port disable request.\n");
+ return;
+ }
+
/* Write 1 to disable the port */
writel(port_status | PORT_PE, addr);
port_status = readl(addr);
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index fa1323b..a0bc61f 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -310,6 +310,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
if (device_property_read_bool(sysdev, "usb3-lpm-capable"))
xhci->quirks |= XHCI_LPM_SUPPORT;
+ if (device_property_read_bool(&pdev->dev, "quirk-broken-port-ped"))
+ xhci->quirks |= XHCI_BROKEN_PORT_PED;
+
if (device_property_read_u32(sysdev, "snps,xhci-imod-value", &imod))
imod = 0;
@@ -329,6 +332,8 @@ static int xhci_plat_probe(struct platform_device *pdev)
if (ret)
goto disable_usb_phy;
+ device_wakeup_enable(&hcd->self.root_hub->dev);
+
if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
xhci->shared_hcd->can_do_streams = 1;
@@ -336,6 +341,8 @@ static int xhci_plat_probe(struct platform_device *pdev)
if (ret)
goto dealloc_usb2_hcd;
+ device_wakeup_enable(&xhci->shared_hcd->self.root_hub->dev);
+
/* override imod interval if specified */
if (imod) {
imod &= ER_IRQ_INTERVAL_MASK;
@@ -428,7 +435,7 @@ static int xhci_plat_runtime_suspend(struct device *dev)
dev_dbg(dev, "xhci-plat runtime suspend\n");
- return 0;
+ return xhci_suspend(xhci, true);
}
static int xhci_plat_runtime_resume(struct device *dev)
@@ -442,7 +449,7 @@ static int xhci_plat_runtime_resume(struct device *dev)
dev_dbg(dev, "xhci-plat runtime resume\n");
- ret = 0;
+ ret = xhci_resume(xhci, false);
pm_runtime_mark_last_busy(dev);
return ret;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 0fe91df..6012da3 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1665,6 +1665,9 @@ struct xhci_hcd {
#define XHCI_SSIC_PORT_UNUSED (1 << 22)
#define XHCI_NO_64BIT_SUPPORT (1 << 23)
#define XHCI_MISSING_CAS (1 << 24)
+/* For controller with a broken Port Disable implementation */
+#define XHCI_BROKEN_PORT_PED (1 << 25)
+
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index da08047..3ee2938 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -373,8 +373,6 @@ static LIST_HEAD(_usbpd); /* useful for debugging */
static const unsigned int usbpd_extcon_cable[] = {
EXTCON_USB,
EXTCON_USB_HOST,
- EXTCON_USB_CC,
- EXTCON_USB_SPEED,
EXTCON_NONE,
};
@@ -397,32 +395,43 @@ EXPORT_SYMBOL(usbpd_get_plug_orientation);
static inline void stop_usb_host(struct usbpd *pd)
{
- extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 0);
+ extcon_set_state_sync(pd->extcon, EXTCON_USB_HOST, 0);
}
static inline void start_usb_host(struct usbpd *pd, bool ss)
{
enum plug_orientation cc = usbpd_get_plug_orientation(pd);
+ union extcon_property_value val;
- extcon_set_cable_state_(pd->extcon, EXTCON_USB_CC,
- cc == ORIENTATION_CC2);
- extcon_set_cable_state_(pd->extcon, EXTCON_USB_SPEED, ss);
- extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 1);
+ val.intval = (cc == ORIENTATION_CC2);
+ extcon_set_property(pd->extcon, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_TYPEC_POLARITY, val);
+
+ val.intval = ss;
+ extcon_set_property(pd->extcon, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_SS, val);
+
+ extcon_set_state_sync(pd->extcon, EXTCON_USB_HOST, 1);
}
static inline void stop_usb_peripheral(struct usbpd *pd)
{
- extcon_set_cable_state_(pd->extcon, EXTCON_USB, 0);
+ extcon_set_state_sync(pd->extcon, EXTCON_USB, 0);
}
static inline void start_usb_peripheral(struct usbpd *pd)
{
enum plug_orientation cc = usbpd_get_plug_orientation(pd);
+ union extcon_property_value val;
- extcon_set_cable_state_(pd->extcon, EXTCON_USB_CC,
- cc == ORIENTATION_CC2);
- extcon_set_cable_state_(pd->extcon, EXTCON_USB_SPEED, 1);
- extcon_set_cable_state_(pd->extcon, EXTCON_USB, 1);
+ val.intval = (cc == ORIENTATION_CC2);
+ extcon_set_property(pd->extcon, EXTCON_USB,
+ EXTCON_PROP_USB_TYPEC_POLARITY, val);
+
+ val.intval = 1;
+ extcon_set_property(pd->extcon, EXTCON_USB, EXTCON_PROP_USB_SS, val);
+
+ extcon_set_state_sync(pd->extcon, EXTCON_USB, 1);
}
static int set_power_role(struct usbpd *pd, enum power_role pr)
@@ -1417,6 +1426,7 @@ static void dr_swap(struct usbpd *pd)
}
pd_phy_update_roles(pd->current_dr, pd->current_pr);
+ dual_role_instance_changed(pd->dual_role);
}
@@ -1817,7 +1827,7 @@ static void usbpd_sm(struct work_struct *w)
regulator_disable(pd->vbus);
if (pd->current_dr != DR_DFP) {
- extcon_set_cable_state_(pd->extcon, EXTCON_USB, 0);
+ extcon_set_state_sync(pd->extcon, EXTCON_USB, 0);
pd->current_dr = DR_DFP;
pd_phy_update_roles(pd->current_dr, pd->current_pr);
}
@@ -2656,11 +2666,17 @@ static int usbpd_dr_set_property(struct dual_role_phy_instance *dual_role,
static int usbpd_dr_prop_writeable(struct dual_role_phy_instance *dual_role,
enum dual_role_property prop)
{
+ struct usbpd *pd = dual_role_get_drvdata(dual_role);
+
switch (prop) {
case DUAL_ROLE_PROP_MODE:
+ return 1;
case DUAL_ROLE_PROP_DR:
case DUAL_ROLE_PROP_PR:
- return 1;
+ if (pd)
+ return pd->current_state == PE_SNK_READY ||
+ pd->current_state == PE_SRC_READY;
+ break;
default:
break;
}
@@ -3207,6 +3223,16 @@ struct usbpd *usbpd_create(struct device *parent)
goto put_psy;
}
+ /* Support reporting polarity and speed via properties */
+ extcon_set_property_capability(pd->extcon, EXTCON_USB,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+ extcon_set_property_capability(pd->extcon, EXTCON_USB,
+ EXTCON_PROP_USB_SS);
+ extcon_set_property_capability(pd->extcon, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_TYPEC_POLARITY);
+ extcon_set_property_capability(pd->extcon, EXTCON_USB_HOST,
+ EXTCON_PROP_USB_SS);
+
pd->vbus = devm_regulator_get(parent, "vbus");
if (IS_ERR(pd->vbus)) {
ret = PTR_ERR(pd->vbus);
diff --git a/drivers/usb/phy/class-dual-role.c b/drivers/usb/phy/class-dual-role.c
index 51fcb54..9ef8895 100644
--- a/drivers/usb/phy/class-dual-role.c
+++ b/drivers/usb/phy/class-dual-role.c
@@ -70,15 +70,7 @@ static char *kstrdupcase(const char *str, gfp_t gfp, bool to_upper)
return ret;
}
-static void dual_role_changed_work(struct work_struct *work)
-{
- struct dual_role_phy_instance *dual_role =
- container_of(work, struct dual_role_phy_instance,
- changed_work);
-
- dev_dbg(&dual_role->dev, "%s\n", __func__);
- kobject_uevent(&dual_role->dev.kobj, KOBJ_CHANGE);
-}
+static void dual_role_changed_work(struct work_struct *work);
void dual_role_instance_changed(struct dual_role_phy_instance *dual_role)
{
@@ -505,6 +497,17 @@ int dual_role_uevent(struct device *dev, struct kobj_uevent_env *env)
return ret;
}
+static void dual_role_changed_work(struct work_struct *work)
+{
+ struct dual_role_phy_instance *dual_role =
+ container_of(work, struct dual_role_phy_instance,
+ changed_work);
+
+ dev_dbg(&dual_role->dev, "%s\n", __func__);
+ sysfs_update_group(&dual_role->dev.kobj, &dual_role_attr_group);
+ kobject_uevent(&dual_role->dev.kobj, KOBJ_CHANGE);
+}
+
/******************* Module Init ***********************************/
static int __init dual_role_class_init(void)
diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c
index 9ef34c3..8bdd9fd 100644
--- a/drivers/usb/phy/phy-msm-ssusb-qmp.c
+++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c
@@ -126,6 +126,7 @@ struct msm_ssphy_qmp {
struct clk *pipe_clk;
struct reset_control *phy_reset;
struct reset_control *phy_phy_reset;
+ struct reset_control *global_phy_reset;
bool power_enabled;
bool clk_enabled;
bool cable_connected;
@@ -136,8 +137,6 @@ struct msm_ssphy_qmp {
int init_seq_len;
unsigned int *qmp_phy_reg_offset;
int reg_offset_cnt;
-
- int port_select;
};
static const struct of_device_id msm_usb_id_table[] = {
@@ -157,6 +156,7 @@ static const struct of_device_id msm_usb_id_table[] = {
};
MODULE_DEVICE_TABLE(of, msm_usb_id_table);
+static void usb_qmp_powerup_phy(struct msm_ssphy_qmp *phy);
static void msm_ssphy_qmp_enable_clks(struct msm_ssphy_qmp *phy, bool on);
static inline char *get_cable_status_str(struct msm_ssphy_qmp *phy)
@@ -334,15 +334,13 @@ static void usb_qmp_update_portselect_phymode(struct msm_ssphy_qmp *phy)
/* perform lane selection */
val = -EINVAL;
- if (phy->phy.flags & PHY_LANE_A) {
+ if (phy->phy.flags & PHY_LANE_A)
val = SW_PORTSELECT_MX;
- phy->port_select = PHY_LANE_A;
- }
-
- if (phy->phy.flags & PHY_LANE_B) {
+ else if (phy->phy.flags & PHY_LANE_B)
val = SW_PORTSELECT | SW_PORTSELECT_MX;
- phy->port_select = PHY_LANE_B;
- }
+
+ /* PHY must be powered up before updating portselect and phymode. */
+ usb_qmp_powerup_phy(phy);
switch (phy->phy.type) {
case USB_PHY_TYPE_USB3_DP:
@@ -362,10 +360,6 @@ static void usb_qmp_update_portselect_phymode(struct msm_ssphy_qmp *phy)
writel_relaxed(USB3_MODE | DP_MODE,
phy->base + phy->phy_reg[USB3_DP_COM_PHY_MODE_CTRL]);
- /* activate register access of LANE for both USB3 and DP */
- writel_relaxed(USB3_SWI_ACT_ACCESS_EN | DP_SWI_ACT_ACCESS_EN,
- phy->base + phy->phy_reg[USB3_DP_COM_SWI_CTRL]);
-
/* bring both QMP USB and QMP DP PHYs PCS block out of reset */
writel_relaxed(0x00,
phy->base + phy->phy_reg[USB3_DP_COM_RESET_OVRD_CTRL]);
@@ -396,9 +390,10 @@ static void usb_qmp_powerup_phy(struct msm_ssphy_qmp *phy)
phy->base + phy->phy_reg[USB3_DP_COM_POWER_DOWN_CTRL]);
/*
- * Don't write 0x0 to DP_COM_SW_RESET as next operation is to
- * update phymode and port select which needs DP_COM_SW_RESET
- * as 0x1.
+ * Don't write 0x0 to DP_COM_SW_RESET here as portselect and
+ * phymode operation needs DP_COM_SW_RESET as 0x1.
+ * msm_ssphy_qmp_init() writes 0x0 to DP_COM_SW_RESET before
+ * initializing PHY.
*/
/* intentional fall-through */
@@ -440,12 +435,12 @@ static int msm_ssphy_qmp_init(struct usb_phy *uphy)
msm_ssphy_qmp_enable_clks(phy, true);
- /* power up PHY */
- usb_qmp_powerup_phy(phy);
-
/* select appropriate port select and PHY mode if applicable */
usb_qmp_update_portselect_phymode(phy);
+ /* power up PHY */
+ usb_qmp_powerup_phy(phy);
+
reg = (struct qmp_reg_val *)phy->qmp_phy_init_seq;
/* Main configuration */
@@ -494,28 +489,31 @@ static int msm_ssphy_qmp_dp_combo_reset(struct usb_phy *uphy)
phy);
int ret = 0;
- /*
- * Avoid global reset if there is no change in port select which
- * shall be useful while changing PHY mode i.e. transition from
- * 4 LANE/2 LANE.
- */
- if ((((phy->phy.flags & PHY_LANE_A) == phy->port_select)) ||
- ((phy->phy.flags & PHY_LANE_B) == phy->port_select))
- goto exit;
-
dev_dbg(uphy->dev, "Global reset of QMP DP combo phy\n");
- /* Assert QMP USB DP combo PHY reset */
+ /* Assert global PHY reset */
+ ret = reset_control_assert(phy->global_phy_reset);
+ if (ret) {
+ dev_err(uphy->dev, "global_phy_reset assert failed\n");
+ goto exit;
+ }
+
+ /* Assert QMP USB PHY reset */
ret = reset_control_assert(phy->phy_reset);
if (ret) {
dev_err(uphy->dev, "phy_reset assert failed\n");
goto exit;
}
- /* De-Assert QMP USB DP combo PHY reset */
+ /* De-Assert QMP USB PHY reset */
ret = reset_control_deassert(phy->phy_reset);
if (ret)
dev_err(uphy->dev, "phy_reset deassert failed\n");
+ /* De-Assert global PHY reset */
+ ret = reset_control_deassert(phy->global_phy_reset);
+ if (ret)
+ dev_err(uphy->dev, "global_phy_reset deassert failed\n");
+
exit:
return ret;
}
@@ -809,7 +807,15 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev)
goto err;
}
- if (phy->phy.type == USB_PHY_TYPE_USB3) {
+ if (phy->phy.type == USB_PHY_TYPE_USB3_DP) {
+ phy->global_phy_reset = devm_reset_control_get(dev,
+ "global_phy_reset");
+ if (IS_ERR(phy->global_phy_reset)) {
+ ret = PTR_ERR(phy->global_phy_reset);
+ dev_dbg(dev, "failed to get global_phy_reset\n");
+ goto err;
+ }
+ } else {
phy->phy_phy_reset = devm_reset_control_get(dev,
"phy_phy_reset");
if (IS_ERR(phy->phy_phy_reset)) {
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 16cc183..9129f6c 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -2071,6 +2071,20 @@ UNUSUAL_DEV( 0x1370, 0x6828, 0x0110, 0x0110,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_IGNORE_RESIDUE ),
+/*
+ * Reported by Tobias Jakobi <tjakobi@math.uni-bielefeld.de>
+ * The INIC-3619 bridge is used in the StarTech SLSODDU33B
+ * SATA-USB enclosure for slimline optical drives.
+ *
+ * The quirk enables MakeMKV to properly exchange keys with
+ * an installed BD drive.
+ */
+UNUSUAL_DEV( 0x13fd, 0x3609, 0x0209, 0x0209,
+ "Initio Corporation",
+ "INIC-3619",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
/* Reported by Qinglin Ye <yestyle@gmail.com> */
UNUSUAL_DEV( 0x13fe, 0x3600, 0x0100, 0x0100,
"Kingston",
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index 59e9576..c5a567a 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -46,6 +46,7 @@
#define S3C2410_WTCON 0x00
#define S3C2410_WTDAT 0x04
#define S3C2410_WTCNT 0x08
+#define S3C2410_WTCLRINT 0x0c
#define S3C2410_WTCNT_MAXCNT 0xffff
@@ -72,6 +73,7 @@
#define EXYNOS5_WDT_MASK_RESET_REG_OFFSET 0x040c
#define QUIRK_HAS_PMU_CONFIG (1 << 0)
#define QUIRK_HAS_RST_STAT (1 << 1)
+#define QUIRK_HAS_WTCLRINT_REG (1 << 2)
/* These quirks require that we have a PMU register map */
#define QUIRKS_HAVE_PMUREG (QUIRK_HAS_PMU_CONFIG | \
@@ -143,13 +145,18 @@ static const struct s3c2410_wdt_variant drv_data_s3c2410 = {
};
#ifdef CONFIG_OF
+static const struct s3c2410_wdt_variant drv_data_s3c6410 = {
+ .quirks = QUIRK_HAS_WTCLRINT_REG,
+};
+
static const struct s3c2410_wdt_variant drv_data_exynos5250 = {
.disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
.mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
.mask_bit = 20,
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
.rst_stat_bit = 20,
- .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT,
+ .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
+ | QUIRK_HAS_WTCLRINT_REG,
};
static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
@@ -158,7 +165,8 @@ static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
.mask_bit = 0,
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
.rst_stat_bit = 9,
- .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT,
+ .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
+ | QUIRK_HAS_WTCLRINT_REG,
};
static const struct s3c2410_wdt_variant drv_data_exynos7 = {
@@ -167,12 +175,15 @@ static const struct s3c2410_wdt_variant drv_data_exynos7 = {
.mask_bit = 23,
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
.rst_stat_bit = 23, /* A57 WDTRESET */
- .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT,
+ .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
+ | QUIRK_HAS_WTCLRINT_REG,
};
static const struct of_device_id s3c2410_wdt_match[] = {
{ .compatible = "samsung,s3c2410-wdt",
.data = &drv_data_s3c2410 },
+ { .compatible = "samsung,s3c6410-wdt",
+ .data = &drv_data_s3c6410 },
{ .compatible = "samsung,exynos5250-wdt",
.data = &drv_data_exynos5250 },
{ .compatible = "samsung,exynos5420-wdt",
@@ -418,6 +429,10 @@ static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
dev_info(wdt->dev, "watchdog timer expired (irq)\n");
s3c2410wdt_keepalive(&wdt->wdt_device);
+
+ if (wdt->drv_data->quirks & QUIRK_HAS_WTCLRINT_REG)
+ writel(0x1, wdt->reg_base + S3C2410_WTCLRINT);
+
return IRQ_HANDLED;
}
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 8745722..bdd3292 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -1104,6 +1104,10 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
return -EINVAL;
}
+ /* SMB2 TREE_CONNECT request must be called with TreeId == 0 */
+ if (tcon)
+ tcon->tid = 0;
+
rc = small_smb2_init(SMB2_TREE_CONNECT, tcon, (void **) &req);
if (rc) {
kfree(unc_path);
diff --git a/fs/orangefs/devorangefs-req.c b/fs/orangefs/devorangefs-req.c
index 516ffb4..f419dd9 100644
--- a/fs/orangefs/devorangefs-req.c
+++ b/fs/orangefs/devorangefs-req.c
@@ -402,8 +402,9 @@ static ssize_t orangefs_devreq_write_iter(struct kiocb *iocb,
/* remove the op from the in progress hash table */
op = orangefs_devreq_remove_op(head.tag);
if (!op) {
- gossip_err("WARNING: No one's waiting for tag %llu\n",
- llu(head.tag));
+ gossip_debug(GOSSIP_DEV_DEBUG,
+ "%s: No one's waiting for tag %llu\n",
+ __func__, llu(head.tag));
return ret;
}
diff --git a/fs/orangefs/orangefs-debugfs.c b/fs/orangefs/orangefs-debugfs.c
index 38887cc..0748a26 100644
--- a/fs/orangefs/orangefs-debugfs.c
+++ b/fs/orangefs/orangefs-debugfs.c
@@ -671,8 +671,10 @@ int orangefs_prepare_debugfs_help_string(int at_boot)
*/
cdm_element_count =
orangefs_prepare_cdm_array(client_debug_array_string);
- if (cdm_element_count <= 0)
+ if (cdm_element_count <= 0) {
+ kfree(new);
goto out;
+ }
for (i = 0; i < cdm_element_count; i++) {
strlcat(new, "\t", string_size);
@@ -963,13 +965,13 @@ int orangefs_debugfs_new_client_string(void __user *arg)
int ret;
ret = copy_from_user(&client_debug_array_string,
- (void __user *)arg,
- ORANGEFS_MAX_DEBUG_STRING_LEN);
+ (void __user *)arg,
+ ORANGEFS_MAX_DEBUG_STRING_LEN);
if (ret != 0) {
pr_info("%s: CLIENT_STRING: copy_from_user failed\n",
__func__);
- return -EIO;
+ return -EFAULT;
}
/*
@@ -984,17 +986,18 @@ int orangefs_debugfs_new_client_string(void __user *arg)
*/
client_debug_array_string[ORANGEFS_MAX_DEBUG_STRING_LEN - 1] =
'\0';
-
+
pr_info("%s: client debug array string has been received.\n",
__func__);
if (!help_string_initialized) {
/* Build a proper debug help string. */
- if (orangefs_prepare_debugfs_help_string(0)) {
+ ret = orangefs_prepare_debugfs_help_string(0);
+ if (ret) {
gossip_err("%s: no debug help string \n",
__func__);
- return -EIO;
+ return ret;
}
}
@@ -1007,7 +1010,7 @@ int orangefs_debugfs_new_client_string(void __user *arg)
help_string_initialized++;
- return ret;
+ return 0;
}
int orangefs_debugfs_new_debug(void __user *arg)
diff --git a/fs/orangefs/orangefs-dev-proto.h b/fs/orangefs/orangefs-dev-proto.h
index a3d84ff..f380f9ed 100644
--- a/fs/orangefs/orangefs-dev-proto.h
+++ b/fs/orangefs/orangefs-dev-proto.h
@@ -50,8 +50,7 @@
* Misc constants. Please retain them as multiples of 8!
* Otherwise 32-64 bit interactions will be messed up :)
*/
-#define ORANGEFS_MAX_DEBUG_STRING_LEN 0x00000400
-#define ORANGEFS_MAX_DEBUG_ARRAY_LEN 0x00000800
+#define ORANGEFS_MAX_DEBUG_STRING_LEN 0x00000800
/*
* The maximum number of directory entries in a single request is 96.
diff --git a/fs/orangefs/super.c b/fs/orangefs/super.c
index 67c2435..cd261c8 100644
--- a/fs/orangefs/super.c
+++ b/fs/orangefs/super.c
@@ -263,8 +263,13 @@ int orangefs_remount(struct orangefs_sb_info_s *orangefs_sb)
if (!new_op)
return -ENOMEM;
new_op->upcall.req.features.features = 0;
- ret = service_operation(new_op, "orangefs_features", 0);
- orangefs_features = new_op->downcall.resp.features.features;
+ ret = service_operation(new_op, "orangefs_features",
+ ORANGEFS_OP_PRIORITY | ORANGEFS_OP_NO_MUTEX);
+ if (!ret)
+ orangefs_features =
+ new_op->downcall.resp.features.features;
+ else
+ orangefs_features = 0;
op_release(new_op);
} else {
orangefs_features = 0;
diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c
index c0146e0..1f6921e 100644
--- a/fs/sdcardfs/file.c
+++ b/fs/sdcardfs/file.c
@@ -192,6 +192,9 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma)
file->f_mapping->a_ops = &sdcardfs_aops; /* set our aops */
if (!SDCARDFS_F(file)->lower_vm_ops) /* save for our ->fault */
SDCARDFS_F(file)->lower_vm_ops = saved_vm_ops;
+ vma->vm_private_data = file;
+ get_file(lower_file);
+ vma->vm_file = lower_file;
out:
return err;
diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c
index 51266f5..391d2a7 100644
--- a/fs/sdcardfs/mmap.c
+++ b/fs/sdcardfs/mmap.c
@@ -23,60 +23,45 @@
static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
int err;
- struct file *file, *lower_file;
+ struct file *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;
+ file = (struct file *)vma->vm_private_data;
lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops;
BUG_ON(!lower_vm_ops);
- lower_file = sdcardfs_lower_file(file);
- /*
- * XXX: vm_ops->fault 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_fault 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 ->fault to
- * take an explicit file pointer.
- */
- lower_vma.vm_file = lower_file;
- err = lower_vm_ops->fault(&lower_vma, vmf);
+ err = lower_vm_ops->fault(vma, vmf);
return err;
}
+static void sdcardfs_vm_open(struct vm_area_struct *vma)
+{
+ struct file *file = (struct file *)vma->vm_private_data;
+
+ get_file(file);
+}
+
+static void sdcardfs_vm_close(struct vm_area_struct *vma)
+{
+ struct file *file = (struct file *)vma->vm_private_data;
+
+ fput(file);
+}
+
static int sdcardfs_page_mkwrite(struct vm_area_struct *vma,
struct vm_fault *vmf)
{
int err = 0;
- struct file *file, *lower_file;
+ struct file *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;
+ file = (struct file *)vma->vm_private_data;
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);
+ err = lower_vm_ops->page_mkwrite(vma, vmf);
out:
return err;
}
@@ -98,4 +83,6 @@ const struct address_space_operations sdcardfs_aops = {
const struct vm_operations_struct sdcardfs_vm_ops = {
.fault = sdcardfs_fault,
.page_mkwrite = sdcardfs_page_mkwrite,
+ .open = sdcardfs_vm_open,
+ .close = sdcardfs_vm_close,
};
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index b803213..39c75a8 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -108,7 +108,7 @@ static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf,
{
const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
struct kobject *kobj = of->kn->parent->priv;
- size_t len;
+ ssize_t len;
/*
* If buf != of->prealloc_buf, we don't know how
@@ -117,13 +117,15 @@ static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf,
if (WARN_ON_ONCE(buf != of->prealloc_buf))
return 0;
len = ops->show(kobj, of->kn->priv, buf);
+ if (len < 0)
+ return len;
if (pos) {
if (len <= pos)
return 0;
len -= pos;
memmove(buf, buf + pos, len);
}
- return min(count, len);
+ return min_t(ssize_t, count, len);
}
/* kernfs write callback for regular sysfs files */
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index 5c395e4..5328ecd 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -1318,8 +1318,16 @@ xfs_free_file_space(
/*
* Now that we've unmap all full blocks we'll have to zero out any
* partial block at the beginning and/or end. xfs_zero_range is
- * smart enough to skip any holes, including those we just created.
+ * smart enough to skip any holes, including those we just created,
+ * but we must take care not to zero beyond EOF and enlarge i_size.
*/
+
+ if (offset >= XFS_ISIZE(ip))
+ return 0;
+
+ if (offset + len > XFS_ISIZE(ip))
+ len = XFS_ISIZE(ip) - offset;
+
return xfs_zero_range(ip, offset, len, NULL);
}
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index 4fef190..f5678aa 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -21,12 +21,15 @@ struct mipi_dsi_device;
#define MIPI_DSI_MSG_REQ_ACK BIT(0)
/* use Low Power Mode to transmit message */
#define MIPI_DSI_MSG_USE_LPM BIT(1)
+/* read mipi_dsi_msg.ctrl and unicast to only that ctrls */
+#define MIPI_DSI_MSG_UNICAST BIT(2)
/**
* struct mipi_dsi_msg - read/write DSI buffer
* @channel: virtual channel id
* @type: payload data type
* @flags: flags controlling this message transmission
+ * @ctrl: ctrl index to transmit on
* @tx_len: length of @tx_buf
* @tx_buf: data to be written
* @rx_len: length of @rx_buf
@@ -36,6 +39,7 @@ struct mipi_dsi_msg {
u8 channel;
u8 type;
u16 flags;
+ u32 ctrl;
size_t tx_len;
const void *tx_buf;
diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h
index 0d5f426..61766a4 100644
--- a/include/drm/i915_pciids.h
+++ b/include/drm/i915_pciids.h
@@ -226,23 +226,18 @@
INTEL_VGA_DEVICE(0x162A, info), /* Server */ \
INTEL_VGA_DEVICE(0x162D, info) /* Workstation */
-#define INTEL_BDW_RSVDM_IDS(info) \
+#define INTEL_BDW_RSVD_IDS(info) \
INTEL_VGA_DEVICE(0x1632, info), /* ULT */ \
INTEL_VGA_DEVICE(0x1636, info), /* ULT */ \
INTEL_VGA_DEVICE(0x163B, info), /* Iris */ \
- INTEL_VGA_DEVICE(0x163E, info) /* ULX */
-
-#define INTEL_BDW_RSVDD_IDS(info) \
+ INTEL_VGA_DEVICE(0x163E, info), /* ULX */ \
INTEL_VGA_DEVICE(0x163A, info), /* Server */ \
INTEL_VGA_DEVICE(0x163D, info) /* Workstation */
#define INTEL_BDW_IDS(info) \
INTEL_BDW_GT12_IDS(info), \
INTEL_BDW_GT3_IDS(info), \
- INTEL_BDW_RSVDM_IDS(info), \
- INTEL_BDW_GT12_IDS(info), \
- INTEL_BDW_GT3_IDS(info), \
- INTEL_BDW_RSVDD_IDS(info)
+ INTEL_BDW_RSVD_IDS(info)
#define INTEL_CHV_IDS(info) \
INTEL_VGA_DEVICE(0x22b0, info), \
diff --git a/include/drm/ttm/ttm_object.h b/include/drm/ttm/ttm_object.h
index ed953f9..1487011 100644
--- a/include/drm/ttm/ttm_object.h
+++ b/include/drm/ttm/ttm_object.h
@@ -229,6 +229,8 @@ extern void ttm_base_object_unref(struct ttm_base_object **p_base);
* @ref_type: The type of reference.
* @existed: Upon completion, indicates that an identical reference object
* already existed, and the refcount was upped on that object instead.
+ * @require_existed: Fail with -EPERM if an identical ref object didn't
+ * already exist.
*
* Checks that the base object is shareable and adds a ref object to it.
*
@@ -243,7 +245,8 @@ extern void ttm_base_object_unref(struct ttm_base_object **p_base);
*/
extern int ttm_ref_object_add(struct ttm_object_file *tfile,
struct ttm_base_object *base,
- enum ttm_ref_type ref_type, bool *existed);
+ enum ttm_ref_type ref_type, bool *existed,
+ bool require_existed);
extern bool ttm_ref_object_exists(struct ttm_object_file *tfile,
struct ttm_base_object *base);
diff --git a/include/dt-bindings/clock/qcom,gcc-sdm845.h b/include/dt-bindings/clock/qcom,gcc-sdm845.h
index 96461d4..f7a6978 100644
--- a/include/dt-bindings/clock/qcom,gcc-sdm845.h
+++ b/include/dt-bindings/clock/qcom,gcc-sdm845.h
@@ -124,73 +124,72 @@
#define GCC_QUPV3_WRAP_1_S_AHB_CLK 106
#define GCC_RX1_USB2_CLKREF_CLK 107
#define GCC_RX2_QLINK_CLKREF_CLK 108
-#define GCC_RX3_MODEM_CLKREF_CLK 109
-#define GCC_SDCC2_AHB_CLK 110
-#define GCC_SDCC2_APPS_CLK 111
-#define GCC_SDCC2_APPS_CLK_SRC 112
-#define GCC_SDCC4_AHB_CLK 113
-#define GCC_SDCC4_APPS_CLK 114
-#define GCC_SDCC4_APPS_CLK_SRC 115
-#define GCC_SYS_NOC_CPUSS_AHB_CLK 116
-#define GCC_TSIF_AHB_CLK 117
-#define GCC_TSIF_INACTIVITY_TIMERS_CLK 118
-#define GCC_TSIF_REF_CLK 119
-#define GCC_TSIF_REF_CLK_SRC 120
-#define GCC_UFS_CARD_AHB_CLK 121
-#define GCC_UFS_CARD_AXI_CLK 122
-#define GCC_UFS_CARD_AXI_CLK_SRC 123
-#define GCC_UFS_CARD_CLKREF_CLK 124
-#define GCC_UFS_CARD_ICE_CORE_CLK 125
-#define GCC_UFS_CARD_ICE_CORE_CLK_SRC 126
-#define GCC_UFS_CARD_PHY_AUX_CLK 127
-#define GCC_UFS_CARD_PHY_AUX_CLK_SRC 128
-#define GCC_UFS_CARD_RX_SYMBOL_0_CLK 129
-#define GCC_UFS_CARD_RX_SYMBOL_1_CLK 130
-#define GCC_UFS_CARD_TX_SYMBOL_0_CLK 131
-#define GCC_UFS_CARD_UNIPRO_CORE_CLK 132
-#define GCC_UFS_CARD_UNIPRO_CORE_CLK_SRC 133
-#define GCC_UFS_MEM_CLKREF_CLK 134
-#define GCC_UFS_PHY_AHB_CLK 135
-#define GCC_UFS_PHY_AXI_CLK 136
-#define GCC_UFS_PHY_AXI_CLK_SRC 137
-#define GCC_UFS_PHY_ICE_CORE_CLK 138
-#define GCC_UFS_PHY_ICE_CORE_CLK_SRC 139
-#define GCC_UFS_PHY_PHY_AUX_CLK 140
-#define GCC_UFS_PHY_PHY_AUX_CLK_SRC 141
-#define GCC_UFS_PHY_RX_SYMBOL_0_CLK 142
-#define GCC_UFS_PHY_RX_SYMBOL_1_CLK 143
-#define GCC_UFS_PHY_TX_SYMBOL_0_CLK 144
-#define GCC_UFS_PHY_UNIPRO_CORE_CLK 145
-#define GCC_UFS_PHY_UNIPRO_CORE_CLK_SRC 146
-#define GCC_USB30_PRIM_MASTER_CLK 147
-#define GCC_USB30_PRIM_MASTER_CLK_SRC 148
-#define GCC_USB30_PRIM_MOCK_UTMI_CLK 149
-#define GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC 150
-#define GCC_USB30_PRIM_SLEEP_CLK 151
-#define GCC_USB30_SEC_MASTER_CLK 152
-#define GCC_USB30_SEC_MASTER_CLK_SRC 153
-#define GCC_USB30_SEC_MOCK_UTMI_CLK 154
-#define GCC_USB30_SEC_MOCK_UTMI_CLK_SRC 155
-#define GCC_USB30_SEC_SLEEP_CLK 156
-#define GCC_USB3_PRIM_CLKREF_CLK 157
-#define GCC_USB3_PRIM_PHY_AUX_CLK 158
-#define GCC_USB3_PRIM_PHY_AUX_CLK_SRC 159
-#define GCC_USB3_PRIM_PHY_COM_AUX_CLK 160
-#define GCC_USB3_PRIM_PHY_PIPE_CLK 161
-#define GCC_USB3_SEC_CLKREF_CLK 162
-#define GCC_USB3_SEC_PHY_AUX_CLK 163
-#define GCC_USB3_SEC_PHY_AUX_CLK_SRC 164
-#define GCC_USB3_SEC_PHY_COM_AUX_CLK 165
-#define GCC_USB3_SEC_PHY_PIPE_CLK 166
-#define GCC_USB_PHY_CFG_AHB2PHY_CLK 167
-#define GCC_VIDEO_AHB_CLK 168
-#define GCC_VIDEO_AXI_CLK 169
-#define GCC_VIDEO_XO_CLK 170
-#define GPLL0 171
-#define GPLL0_OUT_EVEN 172
-#define GPLL0_OUT_MAIN 173
-#define GPLL1 174
-#define GPLL1_OUT_MAIN 175
+#define GCC_SDCC2_AHB_CLK 109
+#define GCC_SDCC2_APPS_CLK 110
+#define GCC_SDCC2_APPS_CLK_SRC 111
+#define GCC_SDCC4_AHB_CLK 112
+#define GCC_SDCC4_APPS_CLK 113
+#define GCC_SDCC4_APPS_CLK_SRC 114
+#define GCC_SYS_NOC_CPUSS_AHB_CLK 115
+#define GCC_TSIF_AHB_CLK 116
+#define GCC_TSIF_INACTIVITY_TIMERS_CLK 117
+#define GCC_TSIF_REF_CLK 118
+#define GCC_TSIF_REF_CLK_SRC 119
+#define GCC_UFS_CARD_AHB_CLK 120
+#define GCC_UFS_CARD_AXI_CLK 121
+#define GCC_UFS_CARD_AXI_CLK_SRC 122
+#define GCC_UFS_CARD_CLKREF_CLK 123
+#define GCC_UFS_CARD_ICE_CORE_CLK 124
+#define GCC_UFS_CARD_ICE_CORE_CLK_SRC 125
+#define GCC_UFS_CARD_PHY_AUX_CLK 126
+#define GCC_UFS_CARD_PHY_AUX_CLK_SRC 127
+#define GCC_UFS_CARD_RX_SYMBOL_0_CLK 128
+#define GCC_UFS_CARD_RX_SYMBOL_1_CLK 129
+#define GCC_UFS_CARD_TX_SYMBOL_0_CLK 130
+#define GCC_UFS_CARD_UNIPRO_CORE_CLK 131
+#define GCC_UFS_CARD_UNIPRO_CORE_CLK_SRC 132
+#define GCC_UFS_MEM_CLKREF_CLK 133
+#define GCC_UFS_PHY_AHB_CLK 134
+#define GCC_UFS_PHY_AXI_CLK 135
+#define GCC_UFS_PHY_AXI_CLK_SRC 136
+#define GCC_UFS_PHY_ICE_CORE_CLK 137
+#define GCC_UFS_PHY_ICE_CORE_CLK_SRC 138
+#define GCC_UFS_PHY_PHY_AUX_CLK 139
+#define GCC_UFS_PHY_PHY_AUX_CLK_SRC 140
+#define GCC_UFS_PHY_RX_SYMBOL_0_CLK 141
+#define GCC_UFS_PHY_RX_SYMBOL_1_CLK 142
+#define GCC_UFS_PHY_TX_SYMBOL_0_CLK 143
+#define GCC_UFS_PHY_UNIPRO_CORE_CLK 144
+#define GCC_UFS_PHY_UNIPRO_CORE_CLK_SRC 145
+#define GCC_USB30_PRIM_MASTER_CLK 146
+#define GCC_USB30_PRIM_MASTER_CLK_SRC 147
+#define GCC_USB30_PRIM_MOCK_UTMI_CLK 148
+#define GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC 149
+#define GCC_USB30_PRIM_SLEEP_CLK 150
+#define GCC_USB30_SEC_MASTER_CLK 151
+#define GCC_USB30_SEC_MASTER_CLK_SRC 152
+#define GCC_USB30_SEC_MOCK_UTMI_CLK 153
+#define GCC_USB30_SEC_MOCK_UTMI_CLK_SRC 154
+#define GCC_USB30_SEC_SLEEP_CLK 155
+#define GCC_USB3_PRIM_CLKREF_CLK 156
+#define GCC_USB3_PRIM_PHY_AUX_CLK 157
+#define GCC_USB3_PRIM_PHY_AUX_CLK_SRC 158
+#define GCC_USB3_PRIM_PHY_COM_AUX_CLK 159
+#define GCC_USB3_PRIM_PHY_PIPE_CLK 160
+#define GCC_USB3_SEC_CLKREF_CLK 161
+#define GCC_USB3_SEC_PHY_AUX_CLK 162
+#define GCC_USB3_SEC_PHY_AUX_CLK_SRC 163
+#define GCC_USB3_SEC_PHY_COM_AUX_CLK 164
+#define GCC_USB3_SEC_PHY_PIPE_CLK 165
+#define GCC_USB_PHY_CFG_AHB2PHY_CLK 166
+#define GCC_VIDEO_AHB_CLK 167
+#define GCC_VIDEO_AXI_CLK 168
+#define GCC_VIDEO_XO_CLK 169
+#define GPLL0 170
+#define GPLL0_OUT_EVEN 171
+#define GPLL0_OUT_MAIN 172
+#define GPLL1 173
+#define GPLL1_OUT_MAIN 174
/* GCC reset clocks */
#define GCC_GPU_BCR 0
diff --git a/include/dt-bindings/clock/qcom,rpmh.h b/include/dt-bindings/clock/qcom,rpmh.h
index 778d11b..a31fa20 100644
--- a/include/dt-bindings/clock/qcom,rpmh.h
+++ b/include/dt-bindings/clock/qcom,rpmh.h
@@ -17,19 +17,17 @@
/* RPMh controlled clocks */
#define RPMH_CXO_CLK 0
#define RPMH_CXO_CLK_A 1
-#define RPMH_LN_BB_CLK1 2
-#define RPMH_LN_BB_CLK1_A 3
-#define RPMH_LN_BB_CLK2 4
-#define RPMH_LN_BB_CLK2_A 5
-#define RPMH_LN_BB_CLK3 6
-#define RPMH_LN_BB_CLK3_A 7
-#define RPMH_RF_CLK1 8
-#define RPMH_RF_CLK1_A 9
-#define RPMH_RF_CLK2 10
-#define RPMH_RF_CLK2_A 11
-#define RPMH_RF_CLK3 12
-#define RPMH_RF_CLK3_A 13
-#define RPMH_QDSS_CLK 14
-#define RPMH_QDSS_A_CLK 15
+#define RPMH_LN_BB_CLK2 2
+#define RPMH_LN_BB_CLK2_A 3
+#define RPMH_LN_BB_CLK3 4
+#define RPMH_LN_BB_CLK3_A 5
+#define RPMH_RF_CLK1 6
+#define RPMH_RF_CLK1_A 7
+#define RPMH_RF_CLK2 8
+#define RPMH_RF_CLK2_A 9
+#define RPMH_RF_CLK3 10
+#define RPMH_RF_CLK3_A 11
+#define RPMH_QDSS_CLK 12
+#define RPMH_QDSS_A_CLK 13
#endif
diff --git a/include/dt-bindings/msm/msm-bus-ids.h b/include/dt-bindings/msm/msm-bus-ids.h
index 8135da9..be2210c 100644
--- a/include/dt-bindings/msm/msm-bus-ids.h
+++ b/include/dt-bindings/msm/msm-bus-ids.h
@@ -244,7 +244,8 @@
#define MSM_BUS_MASTER_PIMEM 141
#define MSM_BUS_MASTER_MEM_NOC_SNOC 142
#define MSM_BUS_MASTER_IPA_CORE 143
-#define MSM_BUS_MASTER_MASTER_LAST 144
+#define MSM_BUS_MASTER_ALC 144
+#define MSM_BUS_MASTER_MASTER_LAST 145
#define MSM_BUS_MASTER_LLCC_DISPLAY 20000
#define MSM_BUS_MASTER_MNOC_HF_MEM_NOC_DISPLAY 20001
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
index b5abfda..4c5bca38 100644
--- a/include/linux/arm-smccc.h
+++ b/include/linux/arm-smccc.h
@@ -14,9 +14,6 @@
#ifndef __LINUX_ARM_SMCCC_H
#define __LINUX_ARM_SMCCC_H
-#include <linux/linkage.h>
-#include <linux/types.h>
-
/*
* This file provides common defines for ARM SMC Calling Convention as
* specified in
@@ -60,6 +57,13 @@
#define ARM_SMCCC_OWNER_TRUSTED_OS 50
#define ARM_SMCCC_OWNER_TRUSTED_OS_END 63
+#define ARM_SMCCC_QUIRK_NONE 0
+#define ARM_SMCCC_QUIRK_QCOM_A6 1 /* Save/restore register a6 */
+
+#ifndef __ASSEMBLY__
+
+#include <linux/linkage.h>
+#include <linux/types.h>
/**
* struct arm_smccc_res - Result from SMC/HVC call
* @a0-a3 result values from registers 0 to 3
@@ -72,33 +76,59 @@ struct arm_smccc_res {
};
/**
- * arm_smccc_smc() - make SMC calls
+ * struct arm_smccc_quirk - Contains quirk information
+ * @id: quirk identification
+ * @state: quirk specific information
+ * @a6: Qualcomm quirk entry for returning post-smc call contents of a6
+ */
+struct arm_smccc_quirk {
+ int id;
+ union {
+ unsigned long a6;
+ } state;
+};
+
+/**
+ * __arm_smccc_smc() - make SMC calls
* @a0-a7: arguments passed in registers 0 to 7
* @res: result values from registers 0 to 3
+ * @quirk: points to an arm_smccc_quirk, or NULL when no quirks are required.
*
* This function is used to make SMC calls following SMC Calling Convention.
* The content of the supplied param are copied to registers 0 to 7 prior
* to the SMC instruction. The return values are updated with the content
- * from register 0 to 3 on return from the SMC instruction.
+ * from register 0 to 3 on return from the SMC instruction. An optional
+ * quirk structure provides vendor specific behavior.
*/
-asmlinkage void arm_smccc_smc(unsigned long a0, unsigned long a1,
+asmlinkage void __arm_smccc_smc(unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, unsigned long a6, unsigned long a7,
- struct arm_smccc_res *res);
+ struct arm_smccc_res *res, struct arm_smccc_quirk *quirk);
/**
- * arm_smccc_hvc() - make HVC calls
+ * __arm_smccc_hvc() - make HVC calls
* @a0-a7: arguments passed in registers 0 to 7
* @res: result values from registers 0 to 3
+ * @quirk: points to an arm_smccc_quirk, or NULL when no quirks are required.
*
* This function is used to make HVC calls following SMC Calling
* Convention. The content of the supplied param are copied to registers 0
* to 7 prior to the HVC instruction. The return values are updated with
- * the content from register 0 to 3 on return from the HVC instruction.
+ * the content from register 0 to 3 on return from the HVC instruction. An
+ * optional quirk structure provides vendor specific behavior.
*/
-asmlinkage void arm_smccc_hvc(unsigned long a0, unsigned long a1,
+asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, unsigned long a6, unsigned long a7,
- struct arm_smccc_res *res);
+ struct arm_smccc_res *res, struct arm_smccc_quirk *quirk);
+#define arm_smccc_smc(...) __arm_smccc_smc(__VA_ARGS__, NULL)
+
+#define arm_smccc_smc_quirk(...) __arm_smccc_smc(__VA_ARGS__)
+
+#define arm_smccc_hvc(...) __arm_smccc_hvc(__VA_ARGS__, NULL)
+
+#define arm_smccc_hvc_quirk(...) __arm_smccc_hvc(__VA_ARGS__)
+
+#endif /*__ASSEMBLY__*/
#endif /*__LINUX_ARM_SMCCC_H*/
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index fc08c407..bda14ef 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -162,6 +162,7 @@ enum rq_flag_bits {
__REQ_INTEGRITY, /* I/O includes block integrity payload */
__REQ_FUA, /* forced unit access */
__REQ_PREFLUSH, /* request for cache flush */
+ __REQ_BARRIER, /* marks flush req as barrier */
/* bio only flags */
__REQ_RAHEAD, /* read ahead, can fail anytime */
@@ -207,7 +208,7 @@ enum rq_flag_bits {
(REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER)
#define REQ_COMMON_MASK \
(REQ_FAILFAST_MASK | REQ_SYNC | REQ_META | REQ_PRIO | REQ_NOIDLE | \
- REQ_PREFLUSH | REQ_FUA | REQ_INTEGRITY | REQ_NOMERGE)
+ REQ_PREFLUSH | REQ_FUA | REQ_INTEGRITY | REQ_NOMERGE | REQ_BARRIER)
#define REQ_CLONE_MASK REQ_COMMON_MASK
/* This mask is used for both bio and request merge checking */
@@ -220,6 +221,7 @@ enum rq_flag_bits {
#define REQ_SORTED (1ULL << __REQ_SORTED)
#define REQ_SOFTBARRIER (1ULL << __REQ_SOFTBARRIER)
#define REQ_FUA (1ULL << __REQ_FUA)
+#define REQ_BARRIER (1ULL << __REQ_BARRIER)
#define REQ_NOMERGE (1ULL << __REQ_NOMERGE)
#define REQ_STARTED (1ULL << __REQ_STARTED)
#define REQ_DONTPREP (1ULL << __REQ_DONTPREP)
diff --git a/include/linux/coresight-pmu.h b/include/linux/coresight-pmu.h
index 7d41026..7d8b528 100644
--- a/include/linux/coresight-pmu.h
+++ b/include/linux/coresight-pmu.h
@@ -19,7 +19,7 @@
#define _LINUX_CORESIGHT_PMU_H
#define CORESIGHT_ETM_PMU_NAME "cs_etm"
-#define CORESIGHT_ETM_PMU_SEED 0x10
+#define CORESIGHT_ETM_PMU_SEED 0x01
/* ETMv3.5/PTM's ETMCR config bit */
#define ETM_OPT_CYCACC 12
@@ -33,7 +33,7 @@ static inline int coresight_get_trace_id(int cpu)
* the common convention is to have data trace IDs be I(N) + 1,
* set instruction trace IDs as a function of the CPU number.
*/
- return (CORESIGHT_ETM_PMU_SEED + (cpu * 2));
+ return (CORESIGHT_ETM_PMU_SEED + cpu);
}
#endif
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
index 22fd849..b871c0c 100644
--- a/include/linux/extcon.h
+++ b/include/linux/extcon.h
@@ -65,12 +65,6 @@
#define EXTCON_JACK_SPDIF_IN 26 /* Sony Philips Digital InterFace */
#define EXTCON_JACK_SPDIF_OUT 27
-/* connector orientation 0 - CC1, 1 - CC2 */
-#define EXTCON_USB_CC 28
-
-/* connector speed 0 - High Speed, 1 - super speed */
-#define EXTCON_USB_SPEED 29
-
/* Display external connector */
#define EXTCON_DISP_HDMI 40 /* High-Definition Multimedia Interface */
#define EXTCON_DISP_MHL 41 /* Mobile High-Definition Link */
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index d265f60..d0ec667 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -12,7 +12,11 @@
#include <linux/device.h>
#include <linux/mmc/core.h>
+#include <linux/mmc/mmc.h>
#include <linux/mod_devicetable.h>
+#include <linux/notifier.h>
+
+#define MMC_CARD_CMDQ_BLK_SIZE 512
struct mmc_cid {
unsigned int manfid;
@@ -52,6 +56,7 @@ struct mmc_ext_csd {
u8 sec_feature_support;
u8 rel_sectors;
u8 rel_param;
+ bool enhanced_rpmb_supported;
u8 part_config;
u8 cache_ctrl;
u8 rst_n_function;
@@ -83,11 +88,13 @@ struct mmc_ext_csd {
bool hpi; /* HPI support bit */
unsigned int hpi_cmd; /* cmd used as HPI */
bool bkops; /* background support bit */
- bool man_bkops_en; /* manual bkops enable bit */
+ u8 bkops_en; /* bkops enable */
unsigned int data_sector_size; /* 512 bytes or 4KB */
unsigned int data_tag_unit_size; /* DATA TAG UNIT size */
unsigned int boot_ro_lock; /* ro lock support */
bool boot_ro_lockable;
+ u8 raw_ext_csd_cmdq; /* 15 */
+ u8 raw_ext_csd_cache_ctrl; /* 33 */
bool ffu_capable; /* Firmware upgrade support */
#define MMC_FIRMWARE_LEN 8
u8 fwrev[MMC_FIRMWARE_LEN]; /* FW version */
@@ -95,7 +102,10 @@ struct mmc_ext_csd {
u8 raw_partition_support; /* 160 */
u8 raw_rpmb_size_mult; /* 168 */
u8 raw_erased_mem_count; /* 181 */
+ u8 raw_ext_csd_bus_width; /* 183 */
u8 strobe_support; /* 184 */
+#define MMC_STROBE_SUPPORT (1 << 0)
+ u8 raw_ext_csd_hs_timing; /* 185 */
u8 raw_ext_csd_structure; /* 194 */
u8 raw_card_type; /* 196 */
u8 raw_driver_strength; /* 197 */
@@ -116,13 +126,20 @@ struct mmc_ext_csd {
u8 raw_pwr_cl_200_360; /* 237 */
u8 raw_pwr_cl_ddr_52_195; /* 238 */
u8 raw_pwr_cl_ddr_52_360; /* 239 */
+ u8 cache_flush_policy; /* 240 */
+#define MMC_BKOPS_URGENCY_MASK 0x3
u8 raw_pwr_cl_ddr_200_360; /* 253 */
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 */
+ u8 barrier_en;
+ u8 fw_version; /* 254 */
unsigned int feature_support;
#define MMC_DISCARD_FEATURE BIT(0) /* CMD38 feature */
};
@@ -193,7 +210,8 @@ struct sdio_cccr {
wide_bus:1,
high_power:1,
high_speed:1,
- disable_cd:1;
+ disable_cd:1,
+ async_intr_sup:1;
};
struct sdio_cis {
@@ -222,6 +240,28 @@ enum mmc_blk_status {
MMC_BLK_NEW_REQUEST,
};
+enum mmc_packed_stop_reasons {
+ EXCEEDS_SEGMENTS = 0,
+ EXCEEDS_SECTORS,
+ WRONG_DATA_DIR,
+ FLUSH_OR_DISCARD,
+ EMPTY_QUEUE,
+ REL_WRITE,
+ THRESHOLD,
+ LARGE_SEC_ALIGN,
+ RANDOM,
+ FUA,
+ MAX_REASONS,
+};
+
+struct mmc_wr_pack_stats {
+ u32 *packing_events;
+ u32 pack_stop_reason[MAX_REASONS];
+ spinlock_t lock;
+ bool enabled;
+ bool print_in_read;
+};
+
/* The number of MMC physical partitions. These consist of:
* boot partitions (2), general purpose partitions (4) and
* RPMB partition (1) in MMC v4.4.
@@ -246,6 +286,62 @@ struct mmc_part {
#define MMC_BLK_DATA_AREA_RPMB (1<<3)
};
+enum {
+ MMC_BKOPS_NO_OP,
+ MMC_BKOPS_NOT_CRITICAL,
+ MMC_BKOPS_PERF_IMPACT,
+ MMC_BKOPS_CRITICAL,
+ MMC_BKOPS_NUM_SEVERITY_LEVELS,
+};
+
+/**
+ * struct mmc_bkops_stats - BKOPS statistics
+ * @lock: spinlock used for synchronizing the debugfs and the runtime accesses
+ * to this structure. No need to call with spin_lock_irq api
+ * @manual_start: number of times START_BKOPS was sent to the device
+ * @hpi: number of times HPI was sent to the device
+ * @auto_start: number of times AUTO_EN was set to 1
+ * @auto_stop: number of times AUTO_EN was set to 0
+ * @level: number of times the device reported the need for each level of
+ * bkops handling
+ * @enabled: control over whether statistics should be gathered
+ *
+ * This structure is used to collect statistics regarding the bkops
+ * configuration and use-patterns. It is collected during runtime and can be
+ * shown to the user via a debugfs entry.
+ */
+struct mmc_bkops_stats {
+ spinlock_t lock;
+ unsigned int manual_start;
+ unsigned int hpi;
+ unsigned int auto_start;
+ unsigned int auto_stop;
+ unsigned int level[MMC_BKOPS_NUM_SEVERITY_LEVELS];
+ bool enabled;
+};
+
+/**
+ * struct mmc_bkops_info - BKOPS data
+ * @stats: statistic information regarding bkops
+ * @needs_check: indication whether need to check with the device
+ * whether it requires handling of BKOPS (CMD8)
+ * @needs_manual: indication whether have to send START_BKOPS
+ * to the device
+ */
+struct mmc_bkops_info {
+ struct mmc_bkops_stats stats;
+ bool needs_check;
+ bool needs_bkops;
+ u32 retry_counter;
+};
+
+enum mmc_pon_type {
+ MMC_LONG_PON = 1,
+ MMC_SHRT_PON,
+};
+
+#define MMC_QUIRK_CMDQ_DELAY_BEFORE_DCMD 6 /* microseconds */
+
/*
* MMC device
*/
@@ -253,6 +349,10 @@ struct mmc_card {
struct mmc_host *host; /* the host this device belongs to */
struct device dev; /* the device */
u32 ocr; /* the current OCR setting */
+ unsigned long clk_scaling_lowest; /* lowest scaleable
+ * frequency */
+ unsigned long clk_scaling_highest; /* highest scaleable
+ * frequency */
unsigned int rca; /* relative card address of device */
unsigned int type; /* card type */
#define MMC_TYPE_MMC 0 /* MMC card */
@@ -265,8 +365,10 @@ struct mmc_card {
#define MMC_STATE_BLOCKADDR (1<<2) /* card uses block-addressing */
#define MMC_CARD_SDXC (1<<3) /* card is SDXC */
#define MMC_CARD_REMOVED (1<<4) /* card has been removed */
-#define MMC_STATE_DOING_BKOPS (1<<5) /* card is doing BKOPS */
+#define MMC_STATE_DOING_BKOPS (1<<5) /* card is doing manual BKOPS */
#define MMC_STATE_SUSPENDED (1<<6) /* card is suspended */
+#define MMC_STATE_CMDQ (1<<12) /* card is in cmd queue mode */
+#define MMC_STATE_AUTO_BKOPS (1<<13) /* card is doing auto BKOPS */
unsigned int quirks; /* card quirks */
#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */
#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */
@@ -285,6 +387,15 @@ struct mmc_card {
#define MMC_QUIRK_BROKEN_IRQ_POLLING (1<<11) /* Polling SDIO_CCCR_INTx could create a fake interrupt */
#define MMC_QUIRK_TRIM_BROKEN (1<<12) /* Skip trim */
#define MMC_QUIRK_BROKEN_HPI (1<<13) /* Disable broken HPI support */
+ /* byte mode */
+#define MMC_QUIRK_INAND_DATA_TIMEOUT (1<<14) /* For incorrect data timeout */
+#define MMC_QUIRK_CACHE_DISABLE (1 << 15) /* prevent cache enable */
+#define MMC_QUIRK_QCA6574_SETTINGS (1 << 16) /* QCA6574 card settings*/
+#define MMC_QUIRK_QCA9377_SETTINGS (1 << 17) /* QCA9377 card settings*/
+
+
+/* Make sure CMDQ is empty before queuing DCMD */
+#define MMC_QUIRK_CMDQ_EMPTY_BEFORE_DCMD (1 << 18)
unsigned int erase_size; /* erase size in sectors */
@@ -320,6 +431,14 @@ struct mmc_card {
struct dentry *debugfs_root;
struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
unsigned int nr_parts;
+ unsigned int part_curr;
+
+ struct mmc_wr_pack_stats wr_pack_stats; /* packed commands stats*/
+ struct notifier_block reboot_notify;
+ enum mmc_pon_type pon_type;
+ u8 *cached_ext_csd;
+ bool cmdq_init;
+ struct mmc_bkops_info bkops;
};
/*
@@ -382,6 +501,19 @@ struct mmc_fixup {
#define END_FIXUP { NULL }
+/* extended CSD mapping to mmc version */
+enum mmc_version_ext_csd_rev {
+ MMC_V4_0,
+ MMC_V4_1,
+ MMC_V4_2,
+ MMC_V4_41 = 5,
+ MMC_V4_5,
+ MMC_V4_51 = MMC_V4_5,
+ MMC_V5_0,
+ MMC_V5_01 = MMC_V5_0,
+ MMC_V5_1
+};
+
#define _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end, \
_cis_vendor, _cis_device, \
_fixup, _data, _ext_csd_rev) \
@@ -457,6 +589,8 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
#define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED))
#define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS)
#define mmc_card_suspended(c) ((c)->state & MMC_STATE_SUSPENDED)
+#define mmc_card_cmdq(c) ((c)->state & MMC_STATE_CMDQ)
+#define mmc_card_doing_auto_bkops(c) ((c)->state & MMC_STATE_AUTO_BKOPS)
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
@@ -467,6 +601,12 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
#define mmc_card_clr_doing_bkops(c) ((c)->state &= ~MMC_STATE_DOING_BKOPS)
#define mmc_card_set_suspended(c) ((c)->state |= MMC_STATE_SUSPENDED)
#define mmc_card_clr_suspended(c) ((c)->state &= ~MMC_STATE_SUSPENDED)
+#define mmc_card_set_cmdq(c) ((c)->state |= MMC_STATE_CMDQ)
+#define mmc_card_clr_cmdq(c) ((c)->state &= ~MMC_STATE_CMDQ)
+#define mmc_card_set_auto_bkops(c) ((c)->state |= MMC_STATE_AUTO_BKOPS)
+#define mmc_card_clr_auto_bkops(c) ((c)->state &= ~MMC_STATE_AUTO_BKOPS)
+
+#define mmc_card_strobe(c) (((c)->ext_csd).strobe_support & MMC_STROBE_SUPPORT)
/*
* Quirk add/remove for MMC products.
@@ -542,10 +682,37 @@ static inline int mmc_card_broken_hpi(const struct mmc_card *c)
return c->quirks & MMC_QUIRK_BROKEN_HPI;
}
+static inline bool mmc_card_support_auto_bkops(const struct mmc_card *c)
+{
+ return c->ext_csd.rev >= MMC_V5_1;
+}
+
+static inline bool mmc_card_configured_manual_bkops(const struct mmc_card *c)
+{
+ return c->ext_csd.bkops_en & EXT_CSD_BKOPS_MANUAL_EN;
+}
+
+static inline bool mmc_card_configured_auto_bkops(const struct mmc_card *c)
+{
+ return c->ext_csd.bkops_en & EXT_CSD_BKOPS_AUTO_EN;
+}
+
+static inline bool mmc_enable_qca6574_settings(const struct mmc_card *c)
+{
+ return c->quirks & MMC_QUIRK_QCA6574_SETTINGS;
+}
+
+static inline bool mmc_enable_qca9377_settings(const struct mmc_card *c)
+{
+ return c->quirks & MMC_QUIRK_QCA9377_SETTINGS;
+}
+
#define mmc_card_name(c) ((c)->cid.prod_name)
#define mmc_card_id(c) (dev_name(&(c)->dev))
#define mmc_dev_to_card(d) container_of(d, struct mmc_card, dev)
+#define mmc_get_drvdata(c) dev_get_drvdata(&(c)->dev)
+#define mmc_set_drvdata(c,d) dev_set_drvdata(&(c)->dev, d)
/*
* MMC device driver (e.g., Flash card, I/O card...)
@@ -562,5 +729,9 @@ extern void mmc_unregister_driver(struct mmc_driver *);
extern void mmc_fixup_device(struct mmc_card *card,
const struct mmc_fixup *table);
-
+extern struct mmc_wr_pack_stats *mmc_blk_get_packed_statistics(
+ struct mmc_card *card);
+extern void mmc_blk_init_packed_statistics(struct mmc_card *card);
+extern int mmc_send_pon(struct mmc_card *card);
+extern void mmc_blk_cmdq_req_done(struct mmc_request *mrq);
#endif /* LINUX_MMC_CARD_H */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 2b58cbd..5d5aff1 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -71,6 +71,8 @@ struct mmc_command {
unsigned int busy_timeout; /* busy detect timeout in ms */
/* Set this flag only for blocking sanitize request */
bool sanitize_busy;
+ /* Set this flag only for blocking bkops request */
+ bool bkops_busy;
struct mmc_data *data; /* data segment associated with cmd */
struct mmc_request *mrq; /* associated request */
@@ -96,6 +98,7 @@ struct mmc_data {
int sg_count; /* mapped sg entries */
struct scatterlist *sg; /* I/O scatter list */
s32 host_cookie; /* host private data */
+ bool fault_injected; /* fault injected */
};
struct mmc_host;
@@ -109,6 +112,8 @@ struct mmc_request {
struct completion cmd_completion;
void (*done)(struct mmc_request *);/* completion function */
struct mmc_host *host;
+ struct mmc_cmdq_req *cmdq_req;
+ struct request *req;
/* Allow other commands during this ongoing data transfer or busy wait */
bool cap_cmd_during_tfr;
@@ -118,8 +123,39 @@ struct mmc_request {
#endif
};
+struct mmc_bus_ops {
+ void (*remove)(struct mmc_host *);
+ void (*detect)(struct mmc_host *);
+ int (*pre_suspend)(struct mmc_host *);
+ int (*suspend)(struct mmc_host *);
+ int (*resume)(struct mmc_host *);
+ int (*runtime_suspend)(struct mmc_host *);
+ int (*runtime_resume)(struct mmc_host *);
+ int (*runtime_idle)(struct mmc_host *);
+ int (*power_save)(struct mmc_host *);
+ int (*power_restore)(struct mmc_host *);
+ int (*alive)(struct mmc_host *);
+ int (*shutdown)(struct mmc_host *);
+ int (*reset)(struct mmc_host *);
+ int (*change_bus_speed)(struct mmc_host *, unsigned long *);
+};
+
struct mmc_card;
struct mmc_async_req;
+struct mmc_cmdq_req;
+
+extern int mmc_cmdq_discard_queue(struct mmc_host *host, u32 tasks);
+extern int mmc_cmdq_halt(struct mmc_host *host, bool enable);
+extern int mmc_cmdq_halt_on_empty_queue(struct mmc_host *host);
+extern void mmc_cmdq_post_req(struct mmc_host *host, int tag, int err);
+extern int mmc_cmdq_start_req(struct mmc_host *host,
+ struct mmc_cmdq_req *cmdq_req);
+extern int mmc_cmdq_prepare_flush(struct mmc_command *cmd);
+extern int mmc_cmdq_wait_for_dcmd(struct mmc_host *host,
+ struct mmc_cmdq_req *cmdq_req);
+extern int mmc_cmdq_erase(struct mmc_cmdq_req *cmdq_req,
+ struct mmc_card *card, unsigned int from, unsigned int nr,
+ unsigned int arg);
extern int mmc_stop_bkops(struct mmc_card *);
extern int mmc_read_bkops_status(struct mmc_card *);
@@ -134,10 +170,15 @@ extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
struct mmc_command *, int);
-extern void mmc_start_bkops(struct mmc_card *card, bool from_exception);
+extern void mmc_check_bkops(struct mmc_card *card);
+extern void mmc_start_manual_bkops(struct mmc_card *card);
extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
+extern int __mmc_switch_cmdq_mode(struct mmc_command *cmd, u8 set, u8 index,
+ u8 value, unsigned int timeout_ms,
+ bool use_busy_signal, bool ignore_timeout);
extern int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error);
extern int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd);
+extern int mmc_set_auto_bkops(struct mmc_card *card, bool enable);
#define MMC_ERASE_ARG 0x00000000
#define MMC_SECURE_ERASE_ARG 0x80000000
@@ -164,6 +205,7 @@ extern int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen);
extern int mmc_set_blockcount(struct mmc_card *card, unsigned int blockcount,
bool is_rel_write);
extern int mmc_hw_reset(struct mmc_host *host);
+extern int mmc_cmdq_hw_reset(struct mmc_host *host);
extern int mmc_can_reset(struct mmc_card *card);
extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *);
@@ -174,11 +216,22 @@ extern void mmc_release_host(struct mmc_host *host);
extern void mmc_get_card(struct mmc_card *card);
extern void mmc_put_card(struct mmc_card *card);
+extern void __mmc_put_card(struct mmc_card *card);
+extern void mmc_set_ios(struct mmc_host *host);
extern int mmc_flush_cache(struct mmc_card *);
+extern int mmc_cache_barrier(struct mmc_card *);
extern int mmc_detect_card_removed(struct mmc_host *host);
+extern void mmc_blk_init_bkops_statistics(struct mmc_card *card);
+
+extern void mmc_deferred_scaling(struct mmc_host *host);
+extern void mmc_cmdq_clk_scaling_start_busy(struct mmc_host *host,
+ bool lock_needed);
+extern void mmc_cmdq_clk_scaling_stop_busy(struct mmc_host *host,
+ bool lock_needed, bool is_cmdq_dcmd);
+
/**
* mmc_claim_host - exclusively claim a host
* @host: mmc host to claim
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 9403bbe..ba1e826 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -15,6 +15,7 @@
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/device.h>
+#include <linux/devfreq.h>
#include <linux/fault-inject.h>
#include <linux/blkdev.h>
@@ -22,9 +23,14 @@
#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/pm.h>
+#include <linux/mmc/ring_buffer.h>
+
+#define MMC_AUTOSUSPEND_DELAY_MS 3000
struct mmc_ios {
unsigned int clock; /* clock rate */
+ unsigned int old_rate; /* saved clock rate */
+ unsigned long clk_ts; /* time stamp of last updated clock */
unsigned short vdd;
/* vdd stores the bit number of the selected voltage range from below. */
@@ -89,7 +95,19 @@ enum mmc_load {
MMC_LOAD_LOW,
};
+struct mmc_cmdq_host_ops {
+ int (*init)(struct mmc_host *host);
+ int (*enable)(struct mmc_host *host);
+ void (*disable)(struct mmc_host *host, bool soft);
+ int (*request)(struct mmc_host *host, struct mmc_request *mrq);
+ void (*post_req)(struct mmc_host *host, int tag, int err);
+ int (*halt)(struct mmc_host *host, bool halt);
+ void (*reset)(struct mmc_host *host, bool soft);
+ void (*dumpstate)(struct mmc_host *host);
+};
+
struct mmc_host_ops {
+ int (*init)(struct mmc_host *host);
/*
* 'enable' is called when the host is claimed and 'disable' is called
* when the host is released. 'enable' and 'disable' are deprecated.
@@ -159,6 +177,7 @@ struct mmc_host_ops {
/* Prepare HS400 target operating frequency depending host driver */
int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
+ int (*enhanced_strobe)(struct mmc_host *host);
/* Prepare enhanced strobe depending host driver */
void (*hs400_enhanced_strobe)(struct mmc_host *host,
struct mmc_ios *ios);
@@ -174,12 +193,42 @@ struct mmc_host_ops {
*/
int (*multi_io_quirk)(struct mmc_card *card,
unsigned int direction, int blk_size);
+
+ unsigned long (*get_max_frequency)(struct mmc_host *host);
+ unsigned long (*get_min_frequency)(struct mmc_host *host);
+
int (*notify_load)(struct mmc_host *, enum mmc_load);
+ void (*notify_halt)(struct mmc_host *mmc, bool halt);
+ void (*force_err_irq)(struct mmc_host *host, u64 errmask);
};
struct mmc_card;
struct device;
+struct mmc_cmdq_req {
+ unsigned int cmd_flags;
+ u32 blk_addr;
+ /* active mmc request */
+ struct mmc_request mrq;
+ struct mmc_data data;
+ struct mmc_command cmd;
+#define DCMD (1 << 0)
+#define QBR (1 << 1)
+#define DIR (1 << 2)
+#define PRIO (1 << 3)
+#define REL_WR (1 << 4)
+#define DAT_TAG (1 << 5)
+#define FORCED_PRG (1 << 6)
+ unsigned int cmdq_req_flags;
+
+ unsigned int resp_idx;
+ unsigned int resp_arg;
+ unsigned int dev_pend_tasks;
+ bool resp_err;
+ int tag; /* used for command queuing */
+ u8 ctx_id;
+};
+
struct mmc_async_req {
/* active mmc request */
struct mmc_request *mrq;
@@ -206,6 +255,33 @@ struct mmc_slot {
void *handler_priv;
};
+
+/**
+ * mmc_cmdq_context_info - describes the contexts of cmdq
+ * @active_reqs requests being processed
+ * @data_active_reqs data requests being processed
+ * @curr_state state of cmdq engine
+ * @cmdq_ctx_lock acquire this before accessing this structure
+ * @queue_empty_wq workqueue for waiting for all
+ * the outstanding requests to be completed
+ * @wait waiting for all conditions described in
+ * mmc_cmdq_ready_wait to be satisified before
+ * issuing the new request to LLD.
+ */
+struct mmc_cmdq_context_info {
+ unsigned long active_reqs; /* in-flight requests */
+ unsigned long data_active_reqs; /* in-flight data requests */
+ unsigned long curr_state;
+#define CMDQ_STATE_ERR 0
+#define CMDQ_STATE_DCMD_ACTIVE 1
+#define CMDQ_STATE_HALT 2
+#define CMDQ_STATE_CQ_DISABLE 3
+#define CMDQ_STATE_REQ_TIMED_OUT 4
+ wait_queue_head_t queue_empty_wq;
+ wait_queue_head_t wait;
+ int active_small_sector_read_reqs;
+};
+
/**
* mmc_context_info - synchronization details for mmc context
* @is_done_rcv wake up reason was done request
@@ -230,11 +306,68 @@ struct mmc_supply {
struct regulator *vqmmc; /* Optional Vccq supply */
};
+enum dev_state {
+ DEV_SUSPENDING = 1,
+ DEV_SUSPENDED,
+ DEV_RESUMED,
+};
+
+/**
+ * struct mmc_devfeq_clk_scaling - main context for MMC clock scaling logic
+ *
+ * @lock: spinlock to protect statistics
+ * @devfreq: struct that represent mmc-host as a client for devfreq
+ * @devfreq_profile: MMC device profile, mostly polling interval and callbacks
+ * @ondemand_gov_data: struct supplied to ondemmand governor (thresholds)
+ * @state: load state, can be HIGH or LOW. used to notify mmc_host_ops callback
+ * @start_busy: timestamped armed once a data request is started
+ * @measure_interval_start: timestamped armed once a measure interval started
+ * @devfreq_abort: flag to sync between different contexts relevant to devfreq
+ * @skip_clk_scale_freq_update: flag that enable/disable frequency change
+ * @freq_table_sz: table size of frequencies supplied to devfreq
+ * @freq_table: frequencies table supplied to devfreq
+ * @curr_freq: current frequency
+ * @polling_delay_ms: polling interval for status collection used by devfreq
+ * @upthreshold: up-threshold supplied to ondemand governor
+ * @downthreshold: down-threshold supplied to ondemand governor
+ * @need_freq_change: flag indicating if a frequency change is required
+ * @clk_scaling_in_progress: flag indicating if there's ongoing frequency change
+ * @is_busy_started: flag indicating if a request is handled by the HW
+ * @enable: flag indicating if the clock scaling logic is enabled for this host
+ */
+struct mmc_devfeq_clk_scaling {
+ spinlock_t lock;
+ struct devfreq *devfreq;
+ struct devfreq_dev_profile devfreq_profile;
+ struct devfreq_simple_ondemand_data ondemand_gov_data;
+ enum mmc_load state;
+ ktime_t start_busy;
+ ktime_t measure_interval_start;
+ atomic_t devfreq_abort;
+ bool skip_clk_scale_freq_update;
+ int freq_table_sz;
+ u32 *freq_table;
+ unsigned long total_busy_time_us;
+ unsigned long target_freq;
+ unsigned long curr_freq;
+ unsigned long polling_delay_ms;
+ unsigned int upthreshold;
+ unsigned int downthreshold;
+ unsigned int lower_bus_speed_mode;
+#define MMC_SCALING_LOWER_DDR52_MODE 1
+ bool need_freq_change;
+ bool clk_scaling_in_progress;
+ bool is_busy_started;
+ bool enable;
+};
+
struct mmc_host {
struct device *parent;
struct device class_dev;
+ struct mmc_devfeq_clk_scaling clk_scaling;
int index;
const struct mmc_host_ops *ops;
+ const struct mmc_cmdq_host_ops *cmdq_ops;
struct mmc_pwrseq *pwrseq;
unsigned int f_min;
unsigned int f_max;
@@ -329,6 +462,14 @@ struct mmc_host {
#define MMC_CAP2_PACKED_WR_CONTROL (1 << 23) /* Allow write packing control */
#define MMC_CAP2_CLK_SCALE (1 << 24) /* Allow dynamic clk scaling */
#define MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE (1 << 25) /* Allows Asynchronous SDIO irq while card is in 4-bit mode */
+#define MMC_CAP2_NONHOTPLUG (1 << 26) /*Don't support hotplug*/
+/* Some hosts need additional tuning */
+#define MMC_CAP2_HS400_POST_TUNING (1 << 27)
+#define MMC_CAP2_CMD_QUEUE (1 << 28) /* support eMMC command queue */
+#define MMC_CAP2_SANITIZE (1 << 29) /* Support Sanitize */
+#define MMC_CAP2_SLEEP_AWAKE (1 << 30) /* Use Sleep/Awake (CMD5) */
+/* use max discard ignoring max_busy_timeout parameter */
+#define MMC_CAP2_MAX_DISCARD_SIZE (1 << 31)
mmc_pm_flag_t pm_caps; /* supported pm features */
@@ -357,6 +498,7 @@ struct mmc_host {
spinlock_t lock; /* lock for claim and bus ops */
struct mmc_ios ios; /* current io bus settings */
+ struct mmc_ios cached_ios;
/* group bitfields together to minimize padding */
unsigned int use_spi_crc:1;
@@ -384,6 +526,7 @@ struct mmc_host {
wait_queue_head_t wq;
struct task_struct *claimer; /* task that has host claimed */
+ struct task_struct *suspend_task;
int claim_cnt; /* "claim" nesting count */
struct delayed_work detect;
@@ -393,6 +536,10 @@ struct mmc_host {
const struct mmc_bus_ops *bus_ops; /* current bus driver */
unsigned int bus_refs; /* reference counter */
+ unsigned int bus_resume_flags;
+#define MMC_BUSRESUME_MANUAL_RESUME (1 << 0)
+#define MMC_BUSRESUME_NEEDS_RESUME (1 << 1)
+
unsigned int sdio_irqs;
struct task_struct *sdio_irq_thread;
bool sdio_irq_pending;
@@ -445,6 +592,33 @@ struct mmc_host {
* actually disabling the clock from it's source.
*/
bool card_clock_off;
+
+#ifdef CONFIG_MMC_PERF_PROFILING
+ struct {
+
+ unsigned long rbytes_drv; /* Rd bytes MMC Host */
+ unsigned long wbytes_drv; /* Wr bytes MMC Host */
+ ktime_t rtime_drv; /* Rd time MMC Host */
+ ktime_t wtime_drv; /* Wr time MMC Host */
+ ktime_t start;
+ } perf;
+ bool perf_enable;
+#endif
+ struct mmc_trace_buffer trace_buf;
+ enum dev_state dev_status;
+ bool wakeup_on_idle;
+ struct mmc_cmdq_context_info cmdq_ctx;
+ int num_cq_slots;
+ int dcmd_cq_slot;
+ bool cmdq_thist_enabled;
+ /*
+ * several cmdq supporting host controllers are extensions
+ * of legacy controllers. This variable can be used to store
+ * a reference to the cmdq extension of the existing host
+ * controller.
+ */
+ void *cmdq_private;
+ struct mmc_request *err_mrq;
unsigned long private[0] ____cacheline_aligned;
};
@@ -468,11 +642,30 @@ static inline void *mmc_priv(struct mmc_host *host)
return (void *)host->private;
}
+static inline void *mmc_cmdq_private(struct mmc_host *host)
+{
+ return host->cmdq_private;
+}
+
#define mmc_host_is_spi(host) ((host)->caps & MMC_CAP_SPI)
#define mmc_dev(x) ((x)->parent)
#define mmc_classdev(x) (&(x)->class_dev)
#define mmc_hostname(x) (dev_name(&(x)->class_dev))
+#define mmc_bus_needs_resume(host) ((host)->bus_resume_flags & \
+ MMC_BUSRESUME_NEEDS_RESUME)
+#define mmc_bus_manual_resume(host) ((host)->bus_resume_flags & \
+ MMC_BUSRESUME_MANUAL_RESUME)
+
+static inline void mmc_set_bus_resume_policy(struct mmc_host *host, int manual)
+{
+ if (manual)
+ host->bus_resume_flags |= MMC_BUSRESUME_MANUAL_RESUME;
+ else
+ host->bus_resume_flags &= ~MMC_BUSRESUME_MANUAL_RESUME;
+}
+
+extern int mmc_resume_bus(struct mmc_host *host);
int mmc_power_save_host(struct mmc_host *host);
int mmc_power_restore_host(struct mmc_host *host);
@@ -544,6 +737,12 @@ static inline int mmc_boot_partition_access(struct mmc_host *host)
return !(host->caps2 & MMC_CAP2_BOOTPART_NOACC);
}
+static inline bool mmc_card_and_host_support_async_int(struct mmc_host *host)
+{
+ return ((host->caps2 & MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE) &&
+ (host->card->cccr.async_intr_sup));
+}
+
static inline int mmc_host_uhs(struct mmc_host *host)
{
return host->caps &
@@ -557,6 +756,36 @@ static inline int mmc_host_packed_wr(struct mmc_host *host)
return host->caps2 & MMC_CAP2_PACKED_WR;
}
+static inline void mmc_host_set_halt(struct mmc_host *host)
+{
+ set_bit(CMDQ_STATE_HALT, &host->cmdq_ctx.curr_state);
+}
+
+static inline void mmc_host_clr_halt(struct mmc_host *host)
+{
+ clear_bit(CMDQ_STATE_HALT, &host->cmdq_ctx.curr_state);
+}
+
+static inline int mmc_host_halt(struct mmc_host *host)
+{
+ return test_bit(CMDQ_STATE_HALT, &host->cmdq_ctx.curr_state);
+}
+
+static inline void mmc_host_set_cq_disable(struct mmc_host *host)
+{
+ set_bit(CMDQ_STATE_CQ_DISABLE, &host->cmdq_ctx.curr_state);
+}
+
+static inline void mmc_host_clr_cq_disable(struct mmc_host *host)
+{
+ clear_bit(CMDQ_STATE_CQ_DISABLE, &host->cmdq_ctx.curr_state);
+}
+
+static inline int mmc_host_cq_disable(struct mmc_host *host)
+{
+ return test_bit(CMDQ_STATE_CQ_DISABLE, &host->cmdq_ctx.curr_state);
+}
+
#ifdef CONFIG_MMC_CLKGATE
void mmc_host_clk_hold(struct mmc_host *host);
void mmc_host_clk_release(struct mmc_host *host);
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 68f60b8..2186a36 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -165,6 +165,7 @@ struct _mmc_csd {
* OCR bits are mostly in host.h
*/
#define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */
+#define MMC_CARD_SECTOR_ADDR 0x40000000 /* Card supports sectors */
/*
* Card Command Classes (CCC)
@@ -214,6 +215,8 @@ struct _mmc_csd {
* EXT_CSD fields
*/
+#define EXT_CSD_CMDQ 15 /* R/W */
+#define EXT_CSD_BARRIER_CTRL 31 /* R/W */
#define EXT_CSD_FLUSH_CACHE 32 /* W */
#define EXT_CSD_CACHE_CTRL 33 /* R/W */
#define EXT_CSD_POWER_OFF_NOTIFICATION 34 /* R/W */
@@ -267,6 +270,7 @@ struct _mmc_csd {
#define EXT_CSD_PWR_CL_200_360 237 /* RO */
#define EXT_CSD_PWR_CL_DDR_52_195 238 /* RO */
#define EXT_CSD_PWR_CL_DDR_52_360 239 /* RO */
+#define EXT_CSD_CACHE_FLUSH_POLICY 240 /* RO */
#define EXT_CSD_BKOPS_STATUS 246 /* RO */
#define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */
#define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */
@@ -276,6 +280,9 @@ struct _mmc_csd {
#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 */
#define EXT_CSD_SUPPORTED_MODE 493 /* RO */
#define EXT_CSD_TAG_UNIT_SIZE 498 /* RO */
#define EXT_CSD_DATA_TAG_SUPPORT 499 /* RO */
@@ -288,7 +295,8 @@ struct _mmc_csd {
* EXT_CSD field definitions
*/
-#define EXT_CSD_WR_REL_PARAM_EN (1<<2)
+#define EXT_CSD_WR_REL_PARAM_EN (1<<2)
+#define EXT_CSD_WR_REL_PARAM_EN_RPMB_REL_WR (1<<4)
#define EXT_CSD_BOOT_WP_B_PWR_WP_DIS (0x40)
#define EXT_CSD_BOOT_WP_B_PERM_WP_DIS (0x10)
@@ -361,6 +369,9 @@ struct _mmc_csd {
#define EXT_CSD_PACKED_EVENT_EN BIT(3)
+#define EXT_CSD_BKOPS_MANUAL_EN BIT(0)
+#define EXT_CSD_BKOPS_AUTO_EN BIT(1)
+
/*
* EXCEPTION_EVENT_STATUS field
*/
diff --git a/include/linux/mmc/ring_buffer.h b/include/linux/mmc/ring_buffer.h
new file mode 100644
index 0000000..e6bf163
--- /dev/null
+++ b/include/linux/mmc/ring_buffer.h
@@ -0,0 +1,55 @@
+/*
+ * 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 __MMC_RING_BUFFER__
+#define __MMC_RING_BUFFER__
+
+#include <linux/mmc/card.h>
+#include <linux/smp.h>
+
+#include "core.h"
+
+#define MMC_TRACE_RBUF_SZ_ORDER 2 /* 2^2 pages */
+#define MMC_TRACE_RBUF_SZ (PAGE_SIZE * (1 << MMC_TRACE_RBUF_SZ_ORDER))
+#define MMC_TRACE_EVENT_SZ 256
+#define MMC_TRACE_RBUF_NUM_EVENTS (MMC_TRACE_RBUF_SZ / MMC_TRACE_EVENT_SZ)
+
+struct mmc_host;
+struct mmc_trace_buffer {
+ int wr_idx;
+ bool stop_tracing;
+ spinlock_t trace_lock;
+ char *data;
+};
+
+#ifdef CONFIG_MMC_RING_BUFFER
+void mmc_stop_tracing(struct mmc_host *mmc);
+void mmc_trace_write(struct mmc_host *mmc, const char *fmt, ...);
+void mmc_trace_init(struct mmc_host *mmc);
+void mmc_trace_free(struct mmc_host *mmc);
+void mmc_dump_trace_buffer(struct mmc_host *mmc, struct seq_file *s);
+#else
+static inline void mmc_stop_tracing(struct mmc_host *mmc) {}
+static inline void mmc_trace_write(struct mmc_host *mmc,
+ const char *fmt, ...) {}
+static inline void mmc_trace_init(struct mmc_host *mmc) {}
+static inline void mmc_trace_free(struct mmc_host *mmc) {}
+static inline void mmc_dump_trace_buffer(struct mmc_host *mmc,
+ struct seq_file *s) {}
+#endif
+
+#define MMC_TRACE(mmc, fmt, ...) \
+ mmc_trace_write(mmc, fmt, ##__VA_ARGS__)
+
+#endif /* __MMC_RING_BUFFER__ */
diff --git a/include/linux/mmc/sdio.h b/include/linux/mmc/sdio.h
index 17446d3..3fc07d7 100644
--- a/include/linux/mmc/sdio.h
+++ b/include/linux/mmc/sdio.h
@@ -102,6 +102,7 @@
#define SDIO_BUS_WIDTH_1BIT 0x00
#define SDIO_BUS_WIDTH_RESERVED 0x01
#define SDIO_BUS_WIDTH_4BIT 0x02
+#define SDIO_BUS_WIDTH_8BIT 0x03
#define SDIO_BUS_ECSI 0x20 /* Enable continuous SPI interrupt */
#define SDIO_BUS_SCSI 0x40 /* Support continuous SPI interrupt */
@@ -163,6 +164,10 @@
#define SDIO_DTSx_SET_TYPE_A (1 << SDIO_DRIVE_DTSx_SHIFT)
#define SDIO_DTSx_SET_TYPE_C (2 << SDIO_DRIVE_DTSx_SHIFT)
#define SDIO_DTSx_SET_TYPE_D (3 << SDIO_DRIVE_DTSx_SHIFT)
+
+#define SDIO_CCCR_INTERRUPT_EXTENSION 0x16
+#define SDIO_SUPPORT_ASYNC_INTR (1<<0)
+#define SDIO_ENABLE_ASYNC_INTR (1<<1)
/*
* Function Basic Registers (FBR)
*/
diff --git a/include/linux/msm-bus.h b/include/linux/msm-bus.h
index 16e3bb2..26e948f 100644
--- a/include/linux/msm-bus.h
+++ b/include/linux/msm-bus.h
@@ -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
@@ -55,8 +55,14 @@ struct msm_bus_paths {
struct msm_bus_vectors *vectors;
};
+struct msm_bus_lat_vectors {
+ uint64_t fal_ns; /* First Access Latency */
+ uint64_t idle_t_ns; /* Idle Time */
+};
+
struct msm_bus_scale_pdata {
struct msm_bus_paths *usecase;
+ struct msm_bus_lat_vectors *usecase_lat;
int num_usecases;
const char *name;
/*
@@ -66,6 +72,11 @@ struct msm_bus_scale_pdata {
* of the CPU state.
*/
unsigned int active_only;
+ /*
+ * If the ALC(Active Latency Client) flag is set to 1,
+ * use lat_usecases for latency voting.
+ */
+ unsigned int alc;
};
struct msm_bus_client_handle {
diff --git a/include/linux/msm_adreno_devfreq.h b/include/linux/msm_adreno_devfreq.h
index 1e580d3..2b94289 100644
--- a/include/linux/msm_adreno_devfreq.h
+++ b/include/linux/msm_adreno_devfreq.h
@@ -21,6 +21,10 @@
#define ADRENO_DEVFREQ_NOTIFY_RETIRE 2
#define ADRENO_DEVFREQ_NOTIFY_IDLE 3
+#define DEVFREQ_FLAG_WAKEUP_MAXFREQ 0x2
+#define DEVFREQ_FLAG_FAST_HINT 0x4
+#define DEVFREQ_FLAG_SLOW_HINT 0x8
+
struct device;
int kgsl_devfreq_add_notifier(struct device *device,
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index f020ab4..3e5dbbe 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2513,6 +2513,8 @@
#define PCI_DEVICE_ID_KORENIX_JETCARDF2 0x1700
#define PCI_DEVICE_ID_KORENIX_JETCARDF3 0x17ff
+#define PCI_VENDOR_ID_HUAWEI 0x19e5
+
#define PCI_VENDOR_ID_NETRONOME 0x19ee
#define PCI_DEVICE_ID_NETRONOME_NFP3200 0x3200
#define PCI_DEVICE_ID_NETRONOME_NFP3240 0x3240
diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h
index 0de4da6..a0f161e 100644
--- a/include/linux/qcom-geni-se.h
+++ b/include/linux/qcom-geni-se.h
@@ -87,6 +87,7 @@ struct se_geni_rsc {
#define SE_GENI_RX_RFR_WATERMARK_REG (0x814)
#define SE_GENI_M_GP_LENGTH (0x910)
#define SE_GENI_S_GP_LENGTH (0x914)
+#define SE_GSI_EVENT_EN (0xE18)
#define SE_IRQ_EN (0xE1C)
#define SE_HW_PARAM_0 (0xE24)
#define SE_HW_PARAM_1 (0xE28)
@@ -220,6 +221,12 @@ struct se_geni_rsc {
#define RX_LAST_BYTE_VALID_SHFT (28)
#define RX_FIFO_WC_MSK (GENMASK(24, 0))
+/* SE_GSI_EVENT_EN fields */
+#define DMA_RX_EVENT_EN (BIT(0))
+#define DMA_TX_EVENT_EN (BIT(1))
+#define GENI_M_EVENT_EN (BIT(2))
+#define GENI_S_EVENT_EN (BIT(3))
+
/* SE_IRQ_EN fields */
#define DMA_RX_IRQ_EN (BIT(0))
#define DMA_TX_IRQ_EN (BIT(1))
@@ -331,9 +338,11 @@ static inline int se_io_set_mode(void __iomem *base, int mode)
int ret = 0;
unsigned int io_mode = 0;
unsigned int geni_dma_mode = 0;
+ unsigned int gsi_event_en = 0;
io_mode = geni_read_reg(base, SE_IRQ_EN);
geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
+ gsi_event_en = geni_read_reg(base, SE_GSI_EVENT_EN);
switch (mode) {
case FIFO_MODE:
@@ -341,15 +350,23 @@ static inline int se_io_set_mode(void __iomem *base, int mode)
io_mode |= (GENI_M_IRQ_EN | GENI_S_IRQ_EN);
io_mode |= (DMA_TX_IRQ_EN | DMA_RX_IRQ_EN);
geni_dma_mode &= ~GENI_DMA_MODE_EN;
+ gsi_event_en = 0;
break;
}
+ case GSI_DMA:
+ geni_dma_mode |= GENI_DMA_MODE_EN;
+ io_mode &= ~(DMA_TX_IRQ_EN | DMA_RX_IRQ_EN);
+ gsi_event_en |= (DMA_RX_EVENT_EN | DMA_TX_EVENT_EN |
+ GENI_M_EVENT_EN | GENI_S_EVENT_EN);
+ break;
default:
ret = -ENXIO;
goto exit_set_mode;
}
geni_write_reg(io_mode, base, SE_IRQ_EN);
geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
+ geni_write_reg(gsi_event_en, base, SE_GSI_EVENT_EN);
exit_set_mode:
return ret;
}
@@ -429,7 +446,7 @@ static inline void geni_abort_m_cmd(void __iomem *base)
geni_write_reg(M_GENI_CMD_ABORT, base, SE_GENI_M_CMD_CTRL_REG);
}
-static inline void qcom_geni_abort_s_cmd(void __iomem *base)
+static inline void geni_abort_s_cmd(void __iomem *base)
{
geni_write_reg(S_GENI_CMD_ABORT, base, SE_GENI_S_CMD_CTRL_REG);
}
diff --git a/include/linux/random.h b/include/linux/random.h
index 7bd2403..16ab429 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -37,7 +37,6 @@ extern void get_random_bytes(void *buf, int nbytes);
extern int add_random_ready_callback(struct random_ready_callback *rdy);
extern void del_random_ready_callback(struct random_ready_callback *rdy);
extern void get_random_bytes_arch(void *buf, int nbytes);
-extern int random_int_secret_init(void);
#ifndef MODULE
extern const struct file_operations random_fops, urandom_fops;
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 867de7d..4b943cd 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1167,6 +1167,8 @@ struct sched_group_energy {
struct capacity_state *cap_states; /* ptr to capacity state array */
};
+extern bool sched_is_energy_aware(void);
+
unsigned long capacity_curr_of(int cpu);
struct sched_group;
diff --git a/include/linux/seemp_instrumentation.h b/include/linux/seemp_instrumentation.h
new file mode 100644
index 0000000..21bc436
--- /dev/null
+++ b/include/linux/seemp_instrumentation.h
@@ -0,0 +1,76 @@
+/*
+ * 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
+ * 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 __SEEMP_LOGK_STUB__
+#define __SEEMP_LOGK_STUB__
+
+#ifdef CONFIG_SEEMP_CORE
+#include <linux/kernel.h>
+
+#define MAX_BUF_SIZE 188
+
+#define SEEMP_LOGK_API_SIZE sizeof(int)
+
+/* Write: api_id + skip encoding byte + params */
+#define SEEMP_LOGK_RECORD(api_id, format, ...) do { \
+ *((int *)(buf - SEEMP_LOGK_API_SIZE)) = api_id; \
+ snprintf(buf + 1, MAX_BUF_SIZE - 1, format, ##__VA_ARGS__); \
+} while (0)
+
+extern void *(*seemp_logk_kernel_begin)(char **buf);
+extern void (*seemp_logk_kernel_end)(void *blck);
+
+static inline void *seemp_setup_buf(char **buf)
+{
+ void *blck;
+
+ if (seemp_logk_kernel_begin && seemp_logk_kernel_end) {
+ blck = seemp_logk_kernel_begin(buf);
+ if (!*buf) {
+ seemp_logk_kernel_end(blck);
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+ return blck;
+}
+/*
+ * NOTE: only sendto is going to be instrumented
+ * since send sys call internally calls sendto
+ * with 2 extra parameters
+ */
+static inline void seemp_logk_sendto(int fd, void __user *buff, size_t len,
+ unsigned int flags, struct sockaddr __user *addr, int addr_len)
+{
+ char *buf = NULL;
+ void *blck = NULL;
+
+ /*sets up buf and blck correctly*/
+ blck = seemp_setup_buf(&buf);
+ if (!blck)
+ return;
+
+ /*fill the buf*/
+ SEEMP_LOGK_RECORD(SEEMP_API_kernel__sendto, "len=%u,fd=%d",
+ (unsigned int)len, fd);
+
+ seemp_logk_kernel_end(blck);
+}
+#else
+static inline void seemp_logk_sendto(int fd, void __user *buff,
+ size_t len, unsigned int flags, struct sockaddr __user *addr,
+ int addr_len)
+{
+}
+#endif
+#endif
diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h
index 2656d5d..b54eefc 100644
--- a/include/soc/qcom/socinfo.h
+++ b/include/soc/qcom/socinfo.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-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
@@ -96,6 +96,8 @@
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmhamster")
#define early_machine_is_msmfalcon() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmfalcon")
+#define early_machine_is_sdxpoorwills() \
+ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdxpoorwills")
#define early_machine_is_sdm845() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdm845")
#define early_machine_is_sdm830() \
@@ -137,6 +139,7 @@
#define early_machine_is_apqcobalt() 0
#define early_machine_is_msmhamster() 0
#define early_machine_is_msmfalcon() 0
+#define early_machine_is_sdxpoorwills() 0
#define early_machine_is_sdm845() 0
#define early_machine_is_sdm830() 0
#endif
@@ -198,6 +201,7 @@ enum msm_cpu {
MSM_CPU_COBALT,
MSM_CPU_HAMSTER,
MSM_CPU_FALCON,
+ SDX_CPU_SDXPOORWILLS,
MSM_CPU_SDM845,
MSM_CPU_SDM830,
};
diff --git a/include/soc/qcom/subsystem_restart.h b/include/soc/qcom/subsystem_restart.h
index 9ea0736..5478417 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/trace/events/mmc.h b/include/trace/events/mmc.h
index a72f9b9..917b3f0 100644
--- a/include/trace/events/mmc.h
+++ b/include/trace/events/mmc.h
@@ -176,7 +176,152 @@ TRACE_EVENT(mmc_request_done,
__entry->hold_retune, __entry->retune_period)
);
-#endif /* _TRACE_MMC_H */
+TRACE_EVENT(mmc_cmd_rw_start,
+ TP_PROTO(unsigned int cmd, unsigned int arg, unsigned int flags),
+ TP_ARGS(cmd, arg, flags),
+ TP_STRUCT__entry(
+ __field(unsigned int, cmd)
+ __field(unsigned int, arg)
+ __field(unsigned int, flags)
+ ),
+ TP_fast_assign(
+ __entry->cmd = cmd;
+ __entry->arg = arg;
+ __entry->flags = flags;
+ ),
+ TP_printk("cmd=%u,arg=0x%08x,flags=0x%08x",
+ __entry->cmd, __entry->arg, __entry->flags)
+);
+
+TRACE_EVENT(mmc_cmd_rw_end,
+ TP_PROTO(unsigned int cmd, unsigned int status, unsigned int resp),
+ TP_ARGS(cmd, status, resp),
+ TP_STRUCT__entry(
+ __field(unsigned int, cmd)
+ __field(unsigned int, status)
+ __field(unsigned int, resp)
+ ),
+ TP_fast_assign(
+ __entry->cmd = cmd;
+ __entry->status = status;
+ __entry->resp = resp;
+ ),
+ TP_printk("cmd=%u,int_status=0x%08x,response=0x%08x",
+ __entry->cmd, __entry->status, __entry->resp)
+);
+
+TRACE_EVENT(mmc_data_rw_end,
+ TP_PROTO(unsigned int cmd, unsigned int status),
+ TP_ARGS(cmd, status),
+ TP_STRUCT__entry(
+ __field(unsigned int, cmd)
+ __field(unsigned int, status)
+ ),
+ TP_fast_assign(
+ __entry->cmd = cmd;
+ __entry->status = status;
+ ),
+ TP_printk("cmd=%u,int_status=0x%08x",
+ __entry->cmd, __entry->status)
+);
+
+DECLARE_EVENT_CLASS(mmc_adma_class,
+ TP_PROTO(unsigned int cmd, unsigned int len),
+ TP_ARGS(cmd, len),
+ TP_STRUCT__entry(
+ __field(unsigned int, cmd)
+ __field(unsigned int, len)
+ ),
+ TP_fast_assign(
+ __entry->cmd = cmd;
+ __entry->len = len;
+ ),
+ TP_printk("cmd=%u,sg_len=0x%08x", __entry->cmd, __entry->len)
+);
+
+DEFINE_EVENT(mmc_adma_class, mmc_adma_table_pre,
+ TP_PROTO(unsigned int cmd, unsigned int len),
+ TP_ARGS(cmd, len));
+
+DEFINE_EVENT(mmc_adma_class, mmc_adma_table_post,
+ TP_PROTO(unsigned int cmd, unsigned int len),
+ TP_ARGS(cmd, len));
+
+TRACE_EVENT(mmc_clk,
+ TP_PROTO(char *print_info),
+
+ TP_ARGS(print_info),
+
+ TP_STRUCT__entry(
+ __string(print_info, print_info)
+ ),
+
+ TP_fast_assign(
+ __assign_str(print_info, print_info);
+ ),
+
+ TP_printk("%s",
+ __get_str(print_info)
+ )
+);
+
+DECLARE_EVENT_CLASS(mmc_pm_template,
+ TP_PROTO(const char *dev_name, int err, s64 usecs),
+
+ TP_ARGS(dev_name, err, usecs),
+
+ TP_STRUCT__entry(
+ __field(s64, usecs)
+ __field(int, err)
+ __string(dev_name, dev_name)
+ ),
+
+ TP_fast_assign(
+ __entry->usecs = usecs;
+ __entry->err = err;
+ __assign_str(dev_name, dev_name);
+ ),
+
+ TP_printk(
+ "took %lld usecs, %s err %d",
+ __entry->usecs,
+ __get_str(dev_name),
+ __entry->err
+ )
+);
+
+DEFINE_EVENT(mmc_pm_template, mmc_runtime_suspend,
+ TP_PROTO(const char *dev_name, int err, s64 usecs),
+ TP_ARGS(dev_name, err, usecs));
+
+DEFINE_EVENT(mmc_pm_template, mmc_runtime_resume,
+ TP_PROTO(const char *dev_name, int err, s64 usecs),
+ TP_ARGS(dev_name, err, usecs));
+
+DEFINE_EVENT(mmc_pm_template, mmc_suspend,
+ TP_PROTO(const char *dev_name, int err, s64 usecs),
+ TP_ARGS(dev_name, err, usecs));
+
+DEFINE_EVENT(mmc_pm_template, mmc_resume,
+ TP_PROTO(const char *dev_name, int err, s64 usecs),
+ TP_ARGS(dev_name, err, usecs));
+
+DEFINE_EVENT(mmc_pm_template, sdhci_msm_suspend,
+ TP_PROTO(const char *dev_name, int err, s64 usecs),
+ TP_ARGS(dev_name, err, usecs));
+
+DEFINE_EVENT(mmc_pm_template, sdhci_msm_resume,
+ TP_PROTO(const char *dev_name, int err, s64 usecs),
+ TP_ARGS(dev_name, err, usecs));
+
+DEFINE_EVENT(mmc_pm_template, sdhci_msm_runtime_suspend,
+ TP_PROTO(const char *dev_name, int err, s64 usecs),
+ TP_ARGS(dev_name, err, usecs));
+
+DEFINE_EVENT(mmc_pm_template, sdhci_msm_runtime_resume,
+ TP_PROTO(const char *dev_name, int err, s64 usecs),
+ TP_ARGS(dev_name, err, usecs));
+#endif /* if !defined(_TRACE_MMC_H) || defined(TRACE_HEADER_MULTI_READ) */
/* This part must be outside protection */
#include <trace/define_trace.h>
diff --git a/include/trace/events/thermal.h b/include/trace/events/thermal.h
index 2b4a8ff..031ae49 100644
--- a/include/trace/events/thermal.h
+++ b/include/trace/events/thermal.h
@@ -47,35 +47,40 @@ TRACE_EVENT(thermal_temperature,
TRACE_EVENT(cdev_update,
- TP_PROTO(struct thermal_cooling_device *cdev, unsigned long target),
+ TP_PROTO(struct thermal_cooling_device *cdev, unsigned long target,
+ unsigned long min_target),
- TP_ARGS(cdev, target),
+ TP_ARGS(cdev, target, min_target),
TP_STRUCT__entry(
__string(type, cdev->type)
__field(unsigned long, target)
+ __field(unsigned long, min_target)
),
TP_fast_assign(
__assign_str(type, cdev->type);
__entry->target = target;
+ __entry->min_target = min_target;
),
- TP_printk("type=%s target=%lu", __get_str(type), __entry->target)
+ TP_printk("type=%s target=%lu min_target=%lu", __get_str(type),
+ __entry->target, __entry->min_target)
);
TRACE_EVENT(thermal_zone_trip,
TP_PROTO(struct thermal_zone_device *tz, int trip,
- enum thermal_trip_type trip_type),
+ enum thermal_trip_type trip_type, bool is_trip),
- TP_ARGS(tz, trip, trip_type),
+ TP_ARGS(tz, trip, trip_type, is_trip),
TP_STRUCT__entry(
__string(thermal_zone, tz->type)
__field(int, id)
__field(int, trip)
__field(enum thermal_trip_type, trip_type)
+ __field(bool, is_trip)
),
TP_fast_assign(
@@ -83,10 +88,13 @@ TRACE_EVENT(thermal_zone_trip,
__entry->id = tz->id;
__entry->trip = trip;
__entry->trip_type = trip_type;
+ __entry->is_trip = is_trip;
),
- TP_printk("thermal_zone=%s id=%d trip=%d trip_type=%s",
- __get_str(thermal_zone), __entry->id, __entry->trip,
+ TP_printk("thermal_zone=%s id=%d %s=%d trip_type=%s",
+ __get_str(thermal_zone), __entry->id,
+ (__entry->is_trip) ? "trip" : "hyst",
+ __entry->trip,
show_tzt_type(__entry->trip_type))
);
diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h
index fda50e9..eb18389 100644
--- a/include/uapi/drm/msm_drm.h
+++ b/include/uapi/drm/msm_drm.h
@@ -316,6 +316,7 @@ struct drm_msm_event_resp {
#define DRM_SDE_WB_CONFIG 0x40
#define DRM_MSM_REGISTER_EVENT 0x41
#define DRM_MSM_DEREGISTER_EVENT 0x42
+#define DRM_MSM_RMFB2 0x43
/* sde custom events */
#define DRM_EVENT_HISTOGRAM 0x80000000
@@ -335,6 +336,8 @@ struct drm_msm_event_resp {
DRM_MSM_REGISTER_EVENT), struct drm_msm_event_req)
#define DRM_IOCTL_MSM_DEREGISTER_EVENT DRM_IOW((DRM_COMMAND_BASE + \
DRM_MSM_DEREGISTER_EVENT), struct drm_msm_event_req)
+#define DRM_IOCTL_MSM_RMFB2 DRM_IOW((DRM_COMMAND_BASE + \
+ DRM_MSM_RMFB2), unsigned int)
#if defined(__cplusplus)
}
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 33ba430..7c1899e 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -414,6 +414,8 @@
header-y += sdla.h
header-y += seccomp.h
header-y += securebits.h
+header-y += seemp_api.h
+header-y += seemp_param_id.h
header-y += selinux_netlink.h
header-y += sem.h
header-y += serial_core.h
diff --git a/include/uapi/linux/mmc/ioctl.h b/include/uapi/linux/mmc/ioctl.h
index 7e385b8..8fec144 100644
--- a/include/uapi/linux/mmc/ioctl.h
+++ b/include/uapi/linux/mmc/ioctl.h
@@ -63,6 +63,61 @@ struct mmc_ioc_multi_cmd {
* commands in array in sequence to card.
*/
#define MMC_IOC_MULTI_CMD _IOWR(MMC_BLOCK_MAJOR, 1, struct mmc_ioc_multi_cmd)
+
+/**
+ * There are four request types that are applicable for rpmb accesses- two
+ * under read category and two under write. They are
+ *
+ * Reads
+ * -------
+ * 1. Read Write Counter
+ * 2. Authenticated data read
+ *
+ *
+ * Writes
+ * -------
+ * 1. Provision RPMB key (though it might be done in a secure environment)
+ * 2. Authenticated data write
+ *
+ * While its given that the rpmb data frames are going to have that
+ * information encoded in it and the frames should be generated by a secure
+ * piece of code, the request types can be classified as above.
+ *
+ * So here are the set of commands that should be executed atomically in the
+ * ioctl for rpmb read operation
+ * 1. Switch partition
+ * 2. Set block count
+ * 3. Write data frame - CMD25 to write the rpmb data frame
+ * 4. Set block count
+ * 5. Read the data - CMD18 to do the actual read
+ *
+ * Similarly for rpmb write operation, these are the commands that should be
+ * executed atomically in the ioctl for rpmb write operation
+ * 1. Switch partition
+ * 2. Set block count
+ * 3. Write data frame - CMD25 to write the rpmb data frame with data
+ * 4. Set block count
+ * 5. Read the data - CMD25 to write rpmb data frame indicating that rpmb
+ * result register is about to be read
+ * 6. Set block count
+ * 7. Read rpmb result - CMD18 to read the rpmb result register
+ *
+ * Each of the above commands should be sent individually via struct mmc_ioc_cmd
+ * and fields like is_acmd that are not needed for rpmb operations will be
+ * ignored.
+ */
+#define MMC_IOC_MAX_RPMB_CMD 3
+struct mmc_ioc_rpmb {
+ struct mmc_ioc_cmd cmds[MMC_IOC_MAX_RPMB_CMD];
+}
+;
+/*
+ * This ioctl is meant for use with rpmb partitions. This is needed since the
+ * access procedure for this particular partition is different from regular
+ * or normal partitions.
+ */
+#define MMC_IOC_RPMB_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_rpmb)
+
/*
* Since this ioctl is only meant to enhance (and not replace) normal access
* to the mmc bus device, an upper data transfer limit of MMC_IOC_MAX_BYTES
diff --git a/include/uapi/linux/mmc/mmc.h b/include/uapi/linux/mmc/mmc.h
index f75ae94..8de329b 100644
--- a/include/uapi/linux/mmc/mmc.h
+++ b/include/uapi/linux/mmc/mmc.h
@@ -29,6 +29,7 @@
#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */
#define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */
#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */
+#define MMC_SEND_TUNING_BLOCK_HS400 MMC_SEND_TUNING_BLOCK_HS200
#define MMC_TUNING_BLK_PATTERN_4BIT_SIZE 64
#define MMC_TUNING_BLK_PATTERN_8BIT_SIZE 128
@@ -64,4 +65,9 @@
#define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */
#define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */
+/* class 11 */
+#define MMC_CMDQ_TASK_MGMT 48 /* ac [31:0] task ID R1b */
+#define DISCARD_QUEUE 0x1
+#define DISCARD_TASK 0x2
+
#endif /* UAPI_MMC_MMC_H */
diff --git a/include/uapi/linux/seemp_api.h b/include/uapi/linux/seemp_api.h
new file mode 100644
index 0000000..4dfc257
--- /dev/null
+++ b/include/uapi/linux/seemp_api.h
@@ -0,0 +1,395 @@
+#ifndef _SEEMP_API_H_
+#define _SEEMP_API_H_
+
+#define SEEMP_API_kernel__oom_adjust_write 0
+#define SEEMP_API_kernel__sendto 1
+#define SEEMP_API_kernel__recvfrom 2
+#define SEEMP_API_View__onTouchEvent 3
+#define SEEMP_API_View__onKeyDown 4
+#define SEEMP_API_View__onKeyUp 5
+#define SEEMP_API_View__onTrackBallEvent 6
+#define SEEMP_API_android_provider_Settings__get_ANDROID_ID_ 7
+#define SEEMP_API_TelephonyManager__getDeviceId 8
+#define SEEMP_API_TelephonyManager__getLine1Number 9
+#define SEEMP_API_Telephony__query 10
+#define SEEMP_API_CallerInfo__getCallerId 11
+#define SEEMP_API_CallerInfo__getCallerInfo 12
+#define SEEMP_API_ContentResolver__query 13
+#define SEEMP_API_AccountManagerService__getPassword 14
+#define SEEMP_API_AccountManagerService__getUserData 15
+#define SEEMP_API_AccountManagerService__addAccount 16
+#define SEEMP_API_AccountManagerService__removeAccount 17
+#define SEEMP_API_AccountManagerService__setPassword 18
+#define SEEMP_API_AccountManagerService__clearPassword 19
+#define SEEMP_API_AccountManagerService__setUserData 20
+#define SEEMP_API_AccountManagerService__editProperties 21
+#define SEEMP_API_AccountManager__getPassword 22
+#define SEEMP_API_AccountManager__getUserData 23
+#define SEEMP_API_AccountManager__addAccountExplicitly 24
+#define SEEMP_API_AccountManager__removeAccount 25
+#define SEEMP_API_AccountManager__setPassword 26
+#define SEEMP_API_AccountManager__clearPassword 27
+#define SEEMP_API_AccountManager__setUserData 28
+#define SEEMP_API_AccountManager__addAccount 29
+#define SEEMP_API_AccountManager__editProperties 30
+#define SEEMP_API_AccountManager__doWork 31
+#define SEEMP_API_Browser__getAllBookmarks 32
+#define SEEMP_API_Browser__getAllVisitedUrls 33
+#define SEEMP_API_Browser__getVisitedLike 34
+#define SEEMP_API_Browser__getVisitedHistory 35
+#define SEEMP_API_Browser__requestAllIcons 36
+#define SEEMP_API_ContentResolver__insert 37
+#define SEEMP_API_CalendarContract__insert 38
+#define SEEMP_API_CalendarContract__alarmExists 39
+#define SEEMP_API_CalendarContract__findNextAlarmTime 40
+#define SEEMP_API_CalendarContract__query 41
+#define SEEMP_API_LocationManager___requestLocationUpdates 42
+#define SEEMP_API_LocationManager__addGpsStatusListener 43
+#define SEEMP_API_LocationManager__addNmeaListener 44
+#define SEEMP_API_LocationManager__addProximityAlert 45
+#define SEEMP_API_LocationManager__getLastKnownLocation 46
+#define SEEMP_API_LocationManager__requestLocationUpdates 47
+#define SEEMP_API_LocationManager__sendExtraCommand 48
+#define SEEMP_API_TelephonyManager__getCellLocation 49
+#define SEEMP_API_TelephonyManager__getNeighboringCellInfo 50
+#define SEEMP_API_GeolocationService__registerForLocationUpdates 51
+#define SEEMP_API_GeolocationService__setEnableGps 52
+#define SEEMP_API_GeolocationService__start 53
+#define SEEMP_API_WebChromeClient__onGeolocationPermissionsShowPrompt 54
+#define SEEMP_API_WifiManager__getScanResults 55
+#define SEEMP_API_adB__enable 56
+#define SEEMP_API_adB__disable 57
+#define SEEMP_API_adB__startDiscovery 58
+#define SEEMP_API_adB__listenUsingInsecureRfcommWithServiceRecord 59
+#define SEEMP_API_adB__listenUsingSecureRfcommWithServiceRecord 60
+#define SEEMP_API_adB__getBondedDevices 61
+#define SEEMP_API_adB__getRemoteDevice 62
+#define SEEMP_API_adB__getState 63
+#define SEEMP_API_adB__getProfileConnectionState 64
+#define SEEMP_API_Camera__takePicture 65
+#define SEEMP_API_Camera__setPreviewCallback 66
+#define SEEMP_API_Camera__setPreviewCallbackWithBuffer 67
+#define SEEMP_API_Camera__setOneShotPreviewCallback 68
+#define SEEMP_API_android_media_MediaRecorder__start 69
+#define SEEMP_API_AudioRecord__startRecording 70
+#define SEEMP_API_AudioRecord__start 71
+#define SEEMP_API_SpeechRecognizer__startListening 72
+#define SEEMP_API_at_SmsManager__sendDataMessage 73
+#define SEEMP_API_at_SmsManager__sendMultipartTextMessage 74
+#define SEEMP_API_at_SmsManager__sendTextMessage 75
+#define SEEMP_API_at_gsm_SmsManager__sendDataMessage 76
+#define SEEMP_API_at_gsm_SmsManager__sendMultipartTextMessage 77
+#define SEEMP_API_at_gsm_SmsManager__sendTextMessage 78
+#define SEEMP_API_at_SmsManager__copyMessageToIcc 79
+#define SEEMP_API_at_SmsManager__deleteMessageFromIcc 80
+#define SEEMP_API_at_SmsManager__updateMessageOnIcc 81
+#define SEEMP_API_at_gsm_SmsManager__copyMessageToSim 82
+#define SEEMP_API_at_gsm_SmsManager__deleteMessageFromSim 83
+#define SEEMP_API_at_gsm_SmsManager__updateMessageOnSim 84
+#define SEEMP_API_at_gsm_SmsManager__getAllMessagesFromSim 85
+#define SEEMP_API_ContactsContract__getLookupUri 86
+#define SEEMP_API_ContactsContract__lookupContact 87
+#define SEEMP_API_ContactsContract__openContactPhotoInputStream 88
+#define SEEMP_API_ContactsContract__getContactLookupUri 89
+#define SEEMP_API_PackageManagerService__installPackage 90
+#define SEEMP_API_URL__openConnection 91
+#define SEEMP_API_URI__URI 92
+#define SEEMP_API_HttpGet__HttpGet 93
+#define SEEMP_API_HttpPut__HttpPut 94
+#define SEEMP_API_HttpPost__HttpPost 95
+#define SEEMP_API_apS__get_ACCELEROMETER_ROTATION_ 96
+#define SEEMP_API_apS__get_USER_ROTATION_ 97
+#define SEEMP_API_apS__get_ADB_ENABLED_ 98
+#define SEEMP_API_apS__get_DEBUG_APP_ 99
+#define SEEMP_API_apS__get_WAIT_FOR_DEBUGGER_ 100
+#define SEEMP_API_apS__get_AIRPLANE_MODE_ON_ 101
+#define SEEMP_API_apS__get_AIRPLANE_MODE_RADIOS_ 102
+#define SEEMP_API_apS__get_ALARM_ALERT_ 103
+#define SEEMP_API_apS__get_NEXT_ALARM_FORMATTED_ 104
+#define SEEMP_API_apS__get_ALWAYS_FINISH_ACTIVITIES_ 105
+#define SEEMP_API_apS__get_LOGGING_ID_ 106
+#define SEEMP_API_apS__get_ANIMATOR_DURATION_SCALE_ 107
+#define SEEMP_API_apS__get_WINDOW_ANIMATION_SCALE_ 108
+#define SEEMP_API_apS__get_FONT_SCALE_ 109
+#define SEEMP_API_apS__get_SCREEN_BRIGHTNESS_ 110
+#define SEEMP_API_apS__get_SCREEN_BRIGHTNESS_MODE_ 111
+#define SEEMP_API_apS__get_SCREEN_BRIGHTNESS_MODE_AUTOMATIC_ 112
+#define SEEMP_API_apS__get_SCREEN_BRIGHTNESS_MODE_MANUAL_ 113
+#define SEEMP_API_apS__get_SCREEN_OFF_TIMEOUT_ 114
+#define SEEMP_API_apS__get_DIM_SCREEN_ 115
+#define SEEMP_API_apS__get_TRANSITION_ANIMATION_SCALE_ 116
+#define SEEMP_API_apS__get_STAY_ON_WHILE_PLUGGED_IN_ 117
+#define SEEMP_API_apS__get_WALLPAPER_ACTIVITY_ 118
+#define SEEMP_API_apS__get_SHOW_PROCESSES_ 119
+#define SEEMP_API_apS__get_SHOW_WEB_SUGGESTIONS_ 120
+#define SEEMP_API_apS__get_SHOW_GTALK_SERVICE_STATUS_ 121
+#define SEEMP_API_apS__get_USE_GOOGLE_MAIL_ 122
+#define SEEMP_API_apS__get_AUTO_TIME_ 123
+#define SEEMP_API_apS__get_AUTO_TIME_ZONE_ 124
+#define SEEMP_API_apS__get_DATE_FORMAT_ 125
+#define SEEMP_API_apS__get_TIME_12_24_ 126
+#define SEEMP_API_apS__get_BLUETOOTH_DISCOVERABILITY_ 127
+#define SEEMP_API_apS__get_BLUETOOTH_DISCOVERABILITY_TIMEOUT_ 128
+#define SEEMP_API_apS__get_BLUETOOTH_ON_ 129
+#define SEEMP_API_apS__get_DEVICE_PROVISIONED_ 130
+#define SEEMP_API_apS__get_SETUP_WIZARD_HAS_RUN_ 131
+#define SEEMP_API_apS__get_DTMF_TONE_WHEN_DIALING_ 132
+#define SEEMP_API_apS__get_END_BUTTON_BEHAVIOR_ 133
+#define SEEMP_API_apS__get_RINGTONE_ 134
+#define SEEMP_API_apS__get_MODE_RINGER_ 135
+#define SEEMP_API_apS__get_INSTALL_NON_MARKET_APPS_ 136
+#define SEEMP_API_apS__get_LOCATION_PROVIDERS_ALLOWED_ 137
+#define SEEMP_API_apS__get_LOCK_PATTERN_ENABLED_ 138
+#define SEEMP_API_apS__get_LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED_ 139
+#define SEEMP_API_apS__get_LOCK_PATTERN_VISIBLE_ 140
+#define SEEMP_API_apS__get_NETWORK_PREFERENCE_ 141
+#define SEEMP_API_apS__get_DATA_ROAMING_ 142
+#define SEEMP_API_apS__get_HTTP_PROXY_ 143
+#define SEEMP_API_apS__get_PARENTAL_CONTROL_ENABLED_ 144
+#define SEEMP_API_apS__get_PARENTAL_CONTROL_LAST_UPDATE_ 145
+#define SEEMP_API_apS__get_PARENTAL_CONTROL_REDIRECT_URL_ 146
+#define SEEMP_API_apS__get_RADIO_BLUETOOTH_ 147
+#define SEEMP_API_apS__get_RADIO_CELL_ 148
+#define SEEMP_API_apS__get_RADIO_NFC_ 149
+#define SEEMP_API_apS__get_RADIO_WIFI_ 150
+#define SEEMP_API_apS__get_SYS_PROP_SETTING_VERSION_ 151
+#define SEEMP_API_apS__get_SETTINGS_CLASSNAME_ 152
+#define SEEMP_API_apS__get_TEXT_AUTO_CAPS_ 153
+#define SEEMP_API_apS__get_TEXT_AUTO_PUNCTUATE_ 154
+#define SEEMP_API_apS__get_TEXT_AUTO_REPLACE_ 155
+#define SEEMP_API_apS__get_TEXT_SHOW_PASSWORD_ 156
+#define SEEMP_API_apS__get_USB_MASS_STORAGE_ENABLED_ 157
+#define SEEMP_API_apS__get_VIBRATE_ON_ 158
+#define SEEMP_API_apS__get_HAPTIC_FEEDBACK_ENABLED_ 159
+#define SEEMP_API_apS__get_VOLUME_ALARM_ 160
+#define SEEMP_API_apS__get_VOLUME_BLUETOOTH_SCO_ 161
+#define SEEMP_API_apS__get_VOLUME_MUSIC_ 162
+#define SEEMP_API_apS__get_VOLUME_NOTIFICATION_ 163
+#define SEEMP_API_apS__get_VOLUME_RING_ 164
+#define SEEMP_API_apS__get_VOLUME_SYSTEM_ 165
+#define SEEMP_API_apS__get_VOLUME_VOICE_ 166
+#define SEEMP_API_apS__get_SOUND_EFFECTS_ENABLED_ 167
+#define SEEMP_API_apS__get_MODE_RINGER_STREAMS_AFFECTED_ 168
+#define SEEMP_API_apS__get_MUTE_STREAMS_AFFECTED_ 169
+#define SEEMP_API_apS__get_NOTIFICATION_SOUND_ 170
+#define SEEMP_API_apS__get_APPEND_FOR_LAST_AUDIBLE_ 171
+#define SEEMP_API_apS__get_WIFI_MAX_DHCP_RETRY_COUNT_ 172
+#define SEEMP_API_apS__get_WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS_ 173
+#define SEEMP_API_apS__get_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_ 174
+#define SEEMP_API_apS__get_WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_ 175
+#define SEEMP_API_apS__get_WIFI_NUM_OPEN_NETWORKS_KEPT_ 176
+#define SEEMP_API_apS__get_WIFI_ON_ 177
+#define SEEMP_API_apS__get_WIFI_SLEEP_POLICY_ 178
+#define SEEMP_API_apS__get_WIFI_SLEEP_POLICY_DEFAULT_ 179
+#define SEEMP_API_apS__get_WIFI_SLEEP_POLICY_NEVER_ 180
+#define SEEMP_API_apS__get_WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED_ 181
+#define SEEMP_API_apS__get_WIFI_STATIC_DNS1_ 182
+#define SEEMP_API_apS__get_WIFI_STATIC_DNS2_ 183
+#define SEEMP_API_apS__get_WIFI_STATIC_GATEWAY_ 184
+#define SEEMP_API_apS__get_WIFI_STATIC_IP_ 185
+#define SEEMP_API_apS__get_WIFI_STATIC_NETMASK_ 186
+#define SEEMP_API_apS__get_WIFI_USE_STATIC_IP_ 187
+#define SEEMP_API_apS__get_WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE_ 188
+#define SEEMP_API_apS__get_WIFI_WATCHDOG_AP_COUNT_ 189
+#define SEEMP_API_apS__get_WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS_ 190
+#define SEEMP_API_apS__get_WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED_ 191
+#define SEEMP_API_apS__get_WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS_ 192
+#define SEEMP_API_apS__get_WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT_ 193
+#define SEEMP_API_apS__get_WIFI_WATCHDOG_MAX_AP_CHECKS_ 194
+#define SEEMP_API_apS__get_WIFI_WATCHDOG_ON_ 195
+#define SEEMP_API_apS__get_WIFI_WATCHDOG_PING_COUNT_ 196
+#define SEEMP_API_apS__get_WIFI_WATCHDOG_PING_DELAY_MS_ 197
+#define SEEMP_API_apS__get_WIFI_WATCHDOG_PING_TIMEOUT_MS_ 198
+#define SEEMP_API_apS__put_ACCELEROMETER_ROTATION_ 199
+#define SEEMP_API_apS__put_USER_ROTATION_ 200
+#define SEEMP_API_apS__put_ADB_ENABLED_ 201
+#define SEEMP_API_apS__put_DEBUG_APP_ 202
+#define SEEMP_API_apS__put_WAIT_FOR_DEBUGGER_ 203
+#define SEEMP_API_apS__put_AIRPLANE_MODE_ON_ 204
+#define SEEMP_API_apS__put_AIRPLANE_MODE_RADIOS_ 205
+#define SEEMP_API_apS__put_ALARM_ALERT_ 206
+#define SEEMP_API_apS__put_NEXT_ALARM_FORMATTED_ 207
+#define SEEMP_API_apS__put_ALWAYS_FINISH_ACTIVITIES_ 208
+#define SEEMP_API_apS__put_ANDROID_ID_ 209
+#define SEEMP_API_apS__put_LOGGING_ID_ 210
+#define SEEMP_API_apS__put_ANIMATOR_DURATION_SCALE_ 211
+#define SEEMP_API_apS__put_WINDOW_ANIMATION_SCALE_ 212
+#define SEEMP_API_apS__put_FONT_SCALE_ 213
+#define SEEMP_API_apS__put_SCREEN_BRIGHTNESS_ 214
+#define SEEMP_API_apS__put_SCREEN_BRIGHTNESS_MODE_ 215
+#define SEEMP_API_apS__put_SCREEN_BRIGHTNESS_MODE_AUTOMATIC_ 216
+#define SEEMP_API_apS__put_SCREEN_BRIGHTNESS_MODE_MANUAL_ 217
+#define SEEMP_API_apS__put_SCREEN_OFF_TIMEOUT_ 218
+#define SEEMP_API_apS__put_DIM_SCREEN_ 219
+#define SEEMP_API_apS__put_TRANSITION_ANIMATION_SCALE_ 220
+#define SEEMP_API_apS__put_STAY_ON_WHILE_PLUGGED_IN_ 221
+#define SEEMP_API_apS__put_WALLPAPER_ACTIVITY_ 222
+#define SEEMP_API_apS__put_SHOW_PROCESSES_ 223
+#define SEEMP_API_apS__put_SHOW_WEB_SUGGESTIONS_ 224
+#define SEEMP_API_apS__put_SHOW_GTALK_SERVICE_STATUS_ 225
+#define SEEMP_API_apS__put_USE_GOOGLE_MAIL_ 226
+#define SEEMP_API_apS__put_AUTO_TIME_ 227
+#define SEEMP_API_apS__put_AUTO_TIME_ZONE_ 228
+#define SEEMP_API_apS__put_DATE_FORMAT_ 229
+#define SEEMP_API_apS__put_TIME_12_24_ 230
+#define SEEMP_API_apS__put_BLUETOOTH_DISCOVERABILITY_ 231
+#define SEEMP_API_apS__put_BLUETOOTH_DISCOVERABILITY_TIMEOUT_ 232
+#define SEEMP_API_apS__put_BLUETOOTH_ON_ 233
+#define SEEMP_API_apS__put_DEVICE_PROVISIONED_ 234
+#define SEEMP_API_apS__put_SETUP_WIZARD_HAS_RUN_ 235
+#define SEEMP_API_apS__put_DTMF_TONE_WHEN_DIALING_ 236
+#define SEEMP_API_apS__put_END_BUTTON_BEHAVIOR_ 237
+#define SEEMP_API_apS__put_RINGTONE_ 238
+#define SEEMP_API_apS__put_MODE_RINGER_ 239
+#define SEEMP_API_apS__put_INSTALL_NON_MARKET_APPS_ 240
+#define SEEMP_API_apS__put_LOCATION_PROVIDERS_ALLOWED_ 241
+#define SEEMP_API_apS__put_LOCK_PATTERN_ENABLED_ 242
+#define SEEMP_API_apS__put_LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED_ 243
+#define SEEMP_API_apS__put_LOCK_PATTERN_VISIBLE_ 244
+#define SEEMP_API_apS__put_NETWORK_PREFERENCE_ 245
+#define SEEMP_API_apS__put_DATA_ROAMING_ 246
+#define SEEMP_API_apS__put_HTTP_PROXY_ 247
+#define SEEMP_API_apS__put_PARENTAL_CONTROL_ENABLED_ 248
+#define SEEMP_API_apS__put_PARENTAL_CONTROL_LAST_UPDATE_ 249
+#define SEEMP_API_apS__put_PARENTAL_CONTROL_REDIRECT_URL_ 250
+#define SEEMP_API_apS__put_RADIO_BLUETOOTH_ 251
+#define SEEMP_API_apS__put_RADIO_CELL_ 252
+#define SEEMP_API_apS__put_RADIO_NFC_ 253
+#define SEEMP_API_apS__put_RADIO_WIFI_ 254
+#define SEEMP_API_apS__put_SYS_PROP_SETTING_VERSION_ 255
+#define SEEMP_API_apS__put_SETTINGS_CLASSNAME_ 256
+#define SEEMP_API_apS__put_TEXT_AUTO_CAPS_ 257
+#define SEEMP_API_apS__put_TEXT_AUTO_PUNCTUATE_ 258
+#define SEEMP_API_apS__put_TEXT_AUTO_REPLACE_ 259
+#define SEEMP_API_apS__put_TEXT_SHOW_PASSWORD_ 260
+#define SEEMP_API_apS__put_USB_MASS_STORAGE_ENABLED_ 261
+#define SEEMP_API_apS__put_VIBRATE_ON_ 262
+#define SEEMP_API_apS__put_HAPTIC_FEEDBACK_ENABLED_ 263
+#define SEEMP_API_apS__put_VOLUME_ALARM_ 264
+#define SEEMP_API_apS__put_VOLUME_BLUETOOTH_SCO_ 265
+#define SEEMP_API_apS__put_VOLUME_MUSIC_ 266
+#define SEEMP_API_apS__put_VOLUME_NOTIFICATION_ 267
+#define SEEMP_API_apS__put_VOLUME_RING_ 268
+#define SEEMP_API_apS__put_VOLUME_SYSTEM_ 269
+#define SEEMP_API_apS__put_VOLUME_VOICE_ 270
+#define SEEMP_API_apS__put_SOUND_EFFECTS_ENABLED_ 271
+#define SEEMP_API_apS__put_MODE_RINGER_STREAMS_AFFECTED_ 272
+#define SEEMP_API_apS__put_MUTE_STREAMS_AFFECTED_ 273
+#define SEEMP_API_apS__put_NOTIFICATION_SOUND_ 274
+#define SEEMP_API_apS__put_APPEND_FOR_LAST_AUDIBLE_ 275
+#define SEEMP_API_apS__put_WIFI_MAX_DHCP_RETRY_COUNT_ 276
+#define SEEMP_API_apS__put_WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS_ 277
+#define SEEMP_API_apS__put_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_ 278
+#define SEEMP_API_apS__put_WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_ 279
+#define SEEMP_API_apS__put_WIFI_NUM_OPEN_NETWORKS_KEPT_ 280
+#define SEEMP_API_apS__put_WIFI_ON_ 281
+#define SEEMP_API_apS__put_WIFI_SLEEP_POLICY_ 282
+#define SEEMP_API_apS__put_WIFI_SLEEP_POLICY_DEFAULT_ 283
+#define SEEMP_API_apS__put_WIFI_SLEEP_POLICY_NEVER_ 284
+#define SEEMP_API_apS__put_WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED_ 285
+#define SEEMP_API_apS__put_WIFI_STATIC_DNS1_ 286
+#define SEEMP_API_apS__put_WIFI_STATIC_DNS2_ 287
+#define SEEMP_API_apS__put_WIFI_STATIC_GATEWAY_ 288
+#define SEEMP_API_apS__put_WIFI_STATIC_IP_ 289
+#define SEEMP_API_apS__put_WIFI_STATIC_NETMASK_ 290
+#define SEEMP_API_apS__put_WIFI_USE_STATIC_IP_ 291
+#define SEEMP_API_apS__put_WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE_ 292
+#define SEEMP_API_apS__put_WIFI_WATCHDOG_AP_COUNT_ 293
+#define SEEMP_API_apS__put_WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS_ 294
+#define SEEMP_API_apS__put_WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED_ 295
+#define SEEMP_API_apS__put_WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS_ 296
+#define SEEMP_API_apS__put_WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT_ 297
+#define SEEMP_API_apS__put_WIFI_WATCHDOG_MAX_AP_CHECKS_ 298
+#define SEEMP_API_apS__put_WIFI_WATCHDOG_ON_ 299
+#define SEEMP_API_apS__put_WIFI_WATCHDOG_PING_COUNT_ 300
+#define SEEMP_API_apS__put_WIFI_WATCHDOG_PING_DELAY_MS_ 301
+#define SEEMP_API_apS__put_WIFI_WATCHDOG_PING_TIMEOUT_MS_ 302
+#define SEEMP_API_Poll__setCumulativeWifiRxMBytes 303
+#define SEEMP_API_Poll__setInstantaneousWifiRxMBytes 304
+#define SEEMP_API_Poll__setCumulativeWifiRxPackets 305
+#define SEEMP_API_Poll__setInstantaneousWifiRxPackets 306
+#define SEEMP_API_Poll__setCumulativeWifiTxMBytes 307
+#define SEEMP_API_Poll__setInstantaneousWifiTxMBytes 308
+#define SEEMP_API_Poll__setCumulativeWifiTxPackets 309
+#define SEEMP_API_Poll__setInstantaneousWifiTxPackets 310
+#define SEEMP_API_Poll__setCumulativeWifiRxTcpMBytes 311
+#define SEEMP_API_Poll__setInstantaneousWifiRxTcpMBytes 312
+#define SEEMP_API_Poll__setCumulativeWifiRxTcpPackets 313
+#define SEEMP_API_Poll__setInstantaneousWifiRxTcpPackets 314
+#define SEEMP_API_Poll__setCumulativeWifiRxUdpMBytes 315
+#define SEEMP_API_Poll__setInstantaneousWifiRxUdpMBytes 316
+#define SEEMP_API_Poll__setCumulativeWifiRxUdpPackets 317
+#define SEEMP_API_Poll__setInstantaneousWifiRxUdpPackets 318
+#define SEEMP_API_Poll__setCumulativeWifiRxOtherMBytes 319
+#define SEEMP_API_Poll__setInstantaneousWifiRxOtherMBytes 320
+#define SEEMP_API_Poll__setCumulativeWifiRxOtherPackets 321
+#define SEEMP_API_Poll__setInstantaneousWifiRxOtherPackets 322
+#define SEEMP_API_Poll__setCumulativeWifiTxTcpMBytes 323
+#define SEEMP_API_Poll__setInstantaneousWifiTxTcpMBytes 324
+#define SEEMP_API_Poll__setCumulativeWifiTxTcpPackets 325
+#define SEEMP_API_Poll__setInstantaneousWifiTxTcpPackets 326
+#define SEEMP_API_Poll__setCumulativeWifiTxUdpMBytes 327
+#define SEEMP_API_Poll__setInstantaneousWifiTxUdpMBytes 328
+#define SEEMP_API_Poll__setCumulativeWifiTxUdpPackets 329
+#define SEEMP_API_Poll__setInstantaneousWifiTxUdpPackets 330
+#define SEEMP_API_Poll__setCumulativeWifiTxOtherMBytes 331
+#define SEEMP_API_Poll__setInstantaneousWifiTxOtherMBytes 332
+#define SEEMP_API_Poll__setCumulativeWifiTxOtherPackets 333
+#define SEEMP_API_Poll__setInstantaneousWifiTxOtherPackets 334
+#define SEEMP_API_Poll__setCumulativeMobileRxMBytes 335
+#define SEEMP_API_Poll__setInstantaneousMobileRxMBytes 336
+#define SEEMP_API_Poll__setCumulativeMobileRxPackets 337
+#define SEEMP_API_Poll__setInstantaneousMobileRxPackets 338
+#define SEEMP_API_Poll__setCumulativeMobileTxMBytes 339
+#define SEEMP_API_Poll__setInstantaneousMobileTxMBytes 340
+#define SEEMP_API_Poll__setCumulativeMobileTxPackets 341
+#define SEEMP_API_Poll__setInstantaneousMobileTxPackets 342
+#define SEEMP_API_Poll__setCumulativeMobileRxTcpMBytes 343
+#define SEEMP_API_Poll__setInstantaneousMobileRxTcpMBytes 344
+#define SEEMP_API_Poll__setCumulativeMobileRxTcpPackets 345
+#define SEEMP_API_Poll__setInstantaneousMobileRxTcpPackets 346
+#define SEEMP_API_Poll__setCumulativeMobileRxUdpMBytes 347
+#define SEEMP_API_Poll__setInstantaneousMobileRxUdpMBytes 348
+#define SEEMP_API_Poll__setCumulativeMobileRxUdpPackets 349
+#define SEEMP_API_Poll__setInstantaneousMobileRxUdpPackets 350
+#define SEEMP_API_Poll__setCumulativeMobileRxOtherMBytes 351
+#define SEEMP_API_Poll__setInstantaneousMobileRxOtherMBytes 352
+#define SEEMP_API_Poll__setCumulativeMobileRxOtherPackets 353
+#define SEEMP_API_Poll__setInstantaneousMobileRxOtherPackets 354
+#define SEEMP_API_Poll__setCumulativeMobileTxTcpMBytes 355
+#define SEEMP_API_Poll__setInstantaneousMobileTxTcpMBytes 356
+#define SEEMP_API_Poll__setCumulativeMobileTxTcpPackets 357
+#define SEEMP_API_Poll__setInstantaneousMobileTxTcpPackets 358
+#define SEEMP_API_Poll__setCumulativeMobileTxUdpMBytes 359
+#define SEEMP_API_Poll__setInstantaneousMobileTxUdpMBytes 360
+#define SEEMP_API_Poll__setCumulativeMobileTxUdpPackets 361
+#define SEEMP_API_Poll__setInstantaneousMobileTxUdpPackets 362
+#define SEEMP_API_Poll__setCumulativeMobileTxOtherMBytes 363
+#define SEEMP_API_Poll__setInstantaneousMobileTxOtherMBytes 364
+#define SEEMP_API_Poll__setCumulativeMobileTxOtherPackets 365
+#define SEEMP_API_Poll__setInstantaneousMobileTxOtherPackets 366
+#define SEEMP_API_Poll__setNumSockets 367
+#define SEEMP_API_Poll__setNumTcpStateListen 368
+#define SEEMP_API_Poll__setNumTcpStateEstablished 369
+#define SEEMP_API_Poll__setNumLocalIp 370
+#define SEEMP_API_Poll__setNumLocalPort 371
+#define SEEMP_API_Poll__setNumRemoteIp 372
+#define SEEMP_API_Poll__setNumRemotePort 373
+#define SEEMP_API_Poll__setNumRemoteTuple 374
+#define SEEMP_API_Poll__setNumInode 375
+#define SEEMP_API_Instrumentation__startActivitySync 376
+#define SEEMP_API_Instrumentation__execStartActivity 377
+#define SEEMP_API_Instrumentation__execStartActivitiesAsUser 378
+#define SEEMP_API_Instrumentation__execStartActivityAsCaller 379
+#define SEEMP_API_Instrumentation__execStartActivityFromAppTask 380
+#define SEEMP_API_ah_SystemSensorManager__registerListenerImpl 381
+#define SEEMP_API_ah_SystemSensorManager__unregisterListenerImpl 382
+#define SEEMP_API_WindowManagerImpl__addView 383
+#define SEEMP_API_WindowManagerImpl__updateViewLayout 384
+#define SEEMP_API_ActivityManagerService__applyOomAdjLocked 385
+#define SEEMP_API_ProcessRecord__makeActive 386
+#define SEEMP_API_ProcessRecord__makeInactive 387
+#define SEEMP_API_TelephonyManager__getSimSerialNumber 388
+#define SEEMP_API_TelephonyManager__getSubscriberId 389
+
+#endif /* _SEEMP_API_H_*/
diff --git a/include/uapi/linux/seemp_param_id.h b/include/uapi/linux/seemp_param_id.h
new file mode 100644
index 0000000..c72c579
--- /dev/null
+++ b/include/uapi/linux/seemp_param_id.h
@@ -0,0 +1,93 @@
+#ifndef _PARAM_ID_H_
+#define _PARAM_ID_H_
+
+#include <linux/string.h>
+#include <linux/types.h>
+
+#define PARAM_ID_LEN 0
+#define PARAM_ID_OOM_ADJ 1
+#define PARAM_ID_APP_UID 2
+#define PARAM_ID_APP_PID 3
+#define PARAM_ID_VALUE 4
+#define PARAM_ID_SIZE 5
+#define PARAM_ID_FD 6
+#define PARAM_ID_RATE 7
+#define PARAM_ID_SENSOR 8
+#define PARAM_ID_WINDOW_TYPE 9
+#define PARAM_ID_WINDOW_FLAG 10
+#define NUM_PARAM_IDS 11
+
+static inline int param_id_index(const char *param, const char *end)
+{
+ int id = -1;
+ int len = ((end != NULL) ? (end - param) : (int)strlen(param));
+
+ if ((len == 3) && !memcmp(param, "len", 3))
+ id = 0;
+ else if ((len == 7) && !memcmp(param, "oom_adj", 7))
+ id = 1;
+ else if ((len == 7) && !memcmp(param, "app_uid", 7))
+ id = 2;
+ else if ((len == 7) && !memcmp(param, "app_pid", 7))
+ id = 3;
+ else if ((len == 5) && !memcmp(param, "value", 5))
+ id = 4;
+ else if ((len == 4) && !memcmp(param, "size", 4))
+ id = 5;
+ else if ((len == 2) && !memcmp(param, "fd", 2))
+ id = 6;
+ else if ((len == 4) && !memcmp(param, "rate", 4))
+ id = 7;
+ else if ((len == 6) && !memcmp(param, "sensor", 6))
+ id = 8;
+ else if ((len == 11) && !memcmp(param, "window_type", 11))
+ id = 9;
+ else if ((len == 11) && !memcmp(param, "window_flag", 11))
+ id = 10;
+
+ return id;
+}
+
+static inline const char *get_param_id_name(int id)
+{
+ const char *name = "?";
+
+ switch (id) {
+ case 0:
+ name = "len";
+ break;
+ case 1:
+ name = "oom_adj";
+ break;
+ case 2:
+ name = "app_uid";
+ break;
+ case 3:
+ name = "app_pid";
+ break;
+ case 4:
+ name = "value";
+ break;
+ case 5:
+ name = "size";
+ break;
+ case 6:
+ name = "fd";
+ break;
+ case 7:
+ name = "rate";
+ break;
+ case 8:
+ name = "sensor";
+ break;
+ case 9:
+ name = "window_type";
+ break;
+ case 10:
+ name = "window_flag";
+ break;
+ }
+ return name;
+}
+
+#endif /* _PARAM_ID_H_ */
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 86cb858..4f1f9e9 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -1890,7 +1890,7 @@ struct v4l2_encoder_cmd {
#define V4L2_DEC_CMD_RESUME (3)
#define V4L2_QCOM_CMD_FLUSH (4)
#define V4L2_DEC_QCOM_CMD_RECONFIG_HINT (5)
-
+#define V4L2_QCOM_CMD_SESSION_CONTINUE (6)
/* Flags for V4L2_DEC_CMD_START */
#define V4L2_DEC_CMD_START_MUTE_AUDIO (1 << 0)
diff --git a/include/uapi/media/cam_defs.h b/include/uapi/media/cam_defs.h
index 12afef9..cf56211 100644
--- a/include/uapi/media/cam_defs.h
+++ b/include/uapi/media/cam_defs.h
@@ -21,12 +21,13 @@
#define CAM_HANDLE_MEM_HANDLE 2
/**
- * struct cam_control - struct used by ioctl control for camera
+ * struct cam_control - Structure used by ioctl control for camera
+ *
* @op_code: This is the op code for camera control
- * @size: control command size
- * @handle_type: user pointer or shared memory handle
- * @reserved: reserved field for 64 bit alignment
- * @handle: control command payload
+ * @size: Control command size
+ * @handle_type: User pointer or shared memory handle
+ * @reserved: Reserved field for 64 bit alignment
+ * @handle: Control command payload
*/
struct cam_control {
uint32_t op_code;
@@ -46,7 +47,7 @@ struct cam_control {
* @major : Hardware version major
* @minor : Hardware version minor
* @incr : Hardware version increment
- * @reserved : reserved for 64 bit aligngment
+ * @reserved : Reserved for 64 bit aligngment
*/
struct cam_hw_version {
uint32_t major;
@@ -141,6 +142,7 @@ struct cam_iommu_handle {
/* camera buffer direction */
#define CAM_BUF_INPUT 1
#define CAM_BUF_OUTPUT 2
+#define CAM_BUF_IN_OUT 3
/* camera packet device Type */
#define CAM_PACKET_DEV_BASE 0
@@ -168,12 +170,20 @@ struct cam_iommu_handle {
#define CAM_PACKET_MAX_PLANES 3
/**
- * struct cam_plane_cfg - plane configuration info
+ * struct cam_plane_cfg - Plane configuration info
*
- * @width: plane width in pixels
- * @height: plane height in lines
- * @plane_stride: plane stride in pixel
- * @slice_height: slice height in line (not used by ISP)
+ * @width: Plane width in pixels
+ * @height: Plane height in lines
+ * @plane_stride: Plane stride in pixel
+ * @slice_height: Slice height in line (not used by ISP)
+ * @meta_stride: UBWC metadata stride
+ * @meta_size: UBWC metadata plane size
+ * @meta_offset: UBWC metadata offset
+ * @packer_config: UBWC packer config
+ * @mode_config: UBWC mode config
+ * @tile_config: UBWC tile config
+ * @h_init: UBWC horizontal initial coordinate in pixels
+ * @v_init: UBWC vertical initial coordinate in lines
*
*/
struct cam_plane_cfg {
@@ -181,60 +191,26 @@ struct cam_plane_cfg {
uint32_t height;
uint32_t plane_stride;
uint32_t slice_height;
-};
-
-/**
- * struct cam_buf_io_cfg - Buffer io configuration for buffers
- *
- * @mem_handle: mem_handle array for the buffers.
- * @offsets: offsets for each planes in the buffer
- * @planes: per plane information
- * @width: main plane width in pixel
- * @height: main plane height in lines
- * @format: format of the buffer
- * @color_space: color space for the buffer
- * @color_pattern: color pattern in the buffer
- * @bpp: bit per pixel
- * @rotation: rotation information for the buffer
- * @resource_type: resource type associated with the buffer
- * @fence: fence handle
- * @cmd_buf_index: command buffer index to patch the buffer info
- * @cmd_buf_offset: offset within the command buffer to patch
- * @flag: flags for extra information
- * @direction: buffer direction: input or output
- * @padding: padding for the structure
- *
- */
-struct cam_buf_io_cfg {
- int32_t mem_handle[CAM_PACKET_MAX_PLANES];
- uint32_t offsets[CAM_PACKET_MAX_PLANES];
- struct cam_plane_cfg planes[CAM_PACKET_MAX_PLANES];
- uint32_t width;
- uint32_t height;
- uint32_t format;
- uint32_t color_space;
- uint32_t color_pattern;
- uint32_t bpp;
- uint32_t rotation;
- uint32_t resource_type;
- int32_t fence;
- uint32_t cmd_buf_index;
- uint32_t cmd_buf_offset;
- uint32_t flag;
- uint32_t direction;
- uint32_t padding;
+ uint32_t meta_stride;
+ uint32_t meta_size;
+ uint32_t meta_offset;
+ uint32_t packer_config;
+ uint32_t mode_config;
+ uint32_t tile_config;
+ uint32_t h_init;
+ uint32_t v_init;
};
/**
* struct cam_cmd_buf_desc - Command buffer descriptor
*
- * @mem_handle: command buffer handle
- * @offset: command start offset
- * @size: size of the command buffer in bytes
- * @length: used memory in command buffer in bytes
- * @type: type of the command buffer
- * @meta_data: data type for private command buffer
- * between UMD and KMD
+ * @mem_handle: Command buffer handle
+ * @offset: Command start offset
+ * @size: Size of the command buffer in bytes
+ * @length: Used memory in command buffer in bytes
+ * @type: Type of the command buffer
+ * @meta_data: Data type for private command buffer
+ * Between UMD and KMD
*
*/
struct cam_cmd_buf_desc {
@@ -247,13 +223,69 @@ struct cam_cmd_buf_desc {
};
/**
- * struct cam_packet_header - camera packet header
+ * struct cam_buf_io_cfg - Buffer io configuration for buffers
*
- * @op_code: camera packet opcode
- * @size: size of the camera packet in bytes
- * @request_id: request id for this camera packet
- * @flags: flags for the camera packet
- * @dev_type: camera packet device type
+ * @mem_handle: Mem_handle array for the buffers.
+ * @offsets: Offsets for each planes in the buffer
+ * @planes: Per plane information
+ * @width: Main plane width in pixel
+ * @height: Main plane height in lines
+ * @format: Format of the buffer
+ * @color_space: Color space for the buffer
+ * @color_pattern: Color pattern in the buffer
+ * @bpp: Bit per pixel
+ * @rotation: Rotation information for the buffer
+ * @resource_type: Resource type associated with the buffer
+ * @fence: Fence handle
+ * @early_fence: Fence handle for early signal
+ * @aux_cmd_buf: An auxiliary command buffer that may be
+ * used for programming the IO
+ * @direction: Direction of the config
+ * @batch_size: Batch size in HFR mode
+ * @subsample_pattern: Subsample pattern. Used in HFR mode. It
+ * should be consistent with batchSize and
+ * CAMIF programming.
+ * @subsample_period: Subsample period. Used in HFR mode. It
+ * should be consistent with batchSize and
+ * CAMIF programming.
+ * @framedrop_pattern: Framedrop pattern
+ * @framedrop_period: Framedrop period
+ * @flag: Flags for extra information
+ * @direction: Buffer direction: input or output
+ * @padding: Padding for the structure
+ *
+ */
+struct cam_buf_io_cfg {
+ int32_t mem_handle[CAM_PACKET_MAX_PLANES];
+ uint32_t offsets[CAM_PACKET_MAX_PLANES];
+ struct cam_plane_cfg planes[CAM_PACKET_MAX_PLANES];
+ uint32_t format;
+ uint32_t color_space;
+ uint32_t color_pattern;
+ uint32_t bpp;
+ uint32_t rotation;
+ uint32_t resource_type;
+ int32_t fence;
+ int32_t early_fence;
+ struct cam_cmd_buf_desc aux_cmd_buf;
+ uint32_t direction;
+ uint32_t batch_size;
+ uint32_t subsample_pattern;
+ uint32_t subsample_period;
+ uint32_t framedrop_pattern;
+ uint32_t framedrop_period;
+ uint32_t flag;
+ uint32_t padding;
+};
+
+/**
+ * struct cam_packet_header - Camera packet header
+ *
+ * @op_code: Camera packet opcode
+ * @size: Size of the camera packet in bytes
+ * @request_id: Request id for this camera packet
+ * @flags: Flags for the camera packet
+ * @padding: Padding
*
*/
struct cam_packet_header {
@@ -261,16 +293,16 @@ struct cam_packet_header {
uint32_t size;
uint64_t request_id;
uint32_t flags;
- uint32_t dev_type;
+ uint32_t padding;
};
/**
* struct cam_patch_desc - Patch structure
*
- * @dst_buf_hdl: memory handle for the dest buffer
- * @dst_offset: offset byte in the dest buffer
- * @src_buf_hdl: memory handle for the source buffer
- * @src_offset: offset byte in the source buffer
+ * @dst_buf_hdl: Memory handle for the dest buffer
+ * @dst_offset: Offset byte in the dest buffer
+ * @src_buf_hdl: Memory handle for the source buffer
+ * @src_offset: Offset byte in the source buffer
*
*/
struct cam_patch_desc {
@@ -281,20 +313,20 @@ struct cam_patch_desc {
};
/**
- * struct cam_packet - cam packet structure
+ * struct cam_packet - Camera packet structure
*
- * @header: camera packet header
- * @cmd_buf_offset: command buffer start offset
- * @num_cmd_buf: number of the command buffer in the packet
- * @io_config_offset: buffer io configuration start offset
- * @num_io_configs: number of the buffer io configurations
- * @patch_offset: patch offset for the patch structure
- * @num_patches: number of the patch structure
- * @kmd_cmd_buf_index: command buffer index which contains extra
- * space for the KMD buffer
- * @kmd_cmd_buf_offset: offset from the beginning of the command
- * buffer for KMD usage.
- * @payload: camera packet payload
+ * @header: Camera packet header
+ * @cmd_buf_offset: Command buffer start offset
+ * @num_cmd_buf: Number of the command buffer in the packet
+ * @io_config_offset: Buffer io configuration start offset
+ * @num_io_configs: Number of the buffer io configurations
+ * @patch_offset: Patch offset for the patch structure
+ * @num_patches: Number of the patch structure
+ * @kmd_cmd_buf_index: Command buffer index which contains extra
+ * space for the KMD buffer
+ * @kmd_cmd_buf_offset: Offset from the beginning of the command
+ * buffer for KMD usage.
+ * @payload: Camera packet payload
*
*/
struct cam_packet {
@@ -313,10 +345,10 @@ struct cam_packet {
/* Release Device */
/**
- * struct cam_release_dev_cmd - control payload for release devices
+ * struct cam_release_dev_cmd - Control payload for release devices
*
- * @session_handle: session handle for the release
- * @dev_handle: device handle for the release
+ * @session_handle: Session handle for the release
+ * @dev_handle: Device handle for the release
*/
struct cam_release_dev_cmd {
int32_t session_handle;
@@ -325,10 +357,10 @@ struct cam_release_dev_cmd {
/* Start/Stop device */
/**
- * struct cam_start_stop_dev_cmd - control payload for start/stop device
+ * struct cam_start_stop_dev_cmd - Control payload for start/stop device
*
- * @session_handle: session handle for the start/stop command
- * @dev_handle: device handle for the start/stop command
+ * @session_handle: Session handle for the start/stop command
+ * @dev_handle: Device handle for the start/stop command
*
*/
struct cam_start_stop_dev_cmd {
@@ -338,13 +370,13 @@ struct cam_start_stop_dev_cmd {
/* Configure Device */
/**
- * struct cam_config_dev_cmd - command payload for configure device
+ * struct cam_config_dev_cmd - Command payload for configure device
*
- * @session_handle: session handle for the command
- * @dev_handle: device handle for the command
- * @offset: offset byte in the packet handle.
- * @packet_handle: packet memory handle for the actual packet:
- * struct cam_packet.
+ * @session_handle: Session handle for the command
+ * @dev_handle: Device handle for the command
+ * @offset: Offset byte in the packet handle.
+ * @packet_handle: Packet memory handle for the actual packet:
+ * struct cam_packet.
*
*/
struct cam_config_dev_cmd {
@@ -356,11 +388,11 @@ struct cam_config_dev_cmd {
/* Query Device Caps */
/**
- * struct cam_query_cap_cmd - payload for query device capability
+ * struct cam_query_cap_cmd - Payload for query device capability
*
- * @size: handle size
- * @handle_type: user pointer or shared memory handle
- * @caps_handle: device specific query command payload
+ * @size: Handle size
+ * @handle_type: User pointer or shared memory handle
+ * @caps_handle: Device specific query command payload
*
*/
struct cam_query_cap_cmd {
@@ -371,16 +403,16 @@ struct cam_query_cap_cmd {
/* Acquire Device */
/**
- * struct cam_acquire_dev_cmd - control payload for acquire devices
+ * struct cam_acquire_dev_cmd - Control payload for acquire devices
*
- * @session_handle: session handle for the acquire command
- * @dev_handle: device handle to be returned
- * @handle_type: resource handle type:
- * 1 = user poniter, 2 = mem handle
- * @num_resources: number of the resources to be acquired
- * @resources_hdl: resource handle that refers to the actual
- * resource array. Each item in this
- * array is device specific resource structure
+ * @session_handle: Session handle for the acquire command
+ * @dev_handle: Device handle to be returned
+ * @handle_type: Resource handle type:
+ * 1 = user pointer, 2 = mem handle
+ * @num_resources: Number of the resources to be acquired
+ * @resources_hdl: Resource handle that refers to the actual
+ * resource array. Each item in this
+ * array is device specific resource structure
*
*/
struct cam_acquire_dev_cmd {
diff --git a/include/uapi/media/cam_req_mgr.h b/include/uapi/media/cam_req_mgr.h
index fea6a70..68db5e1 100644
--- a/include/uapi/media/cam_req_mgr.h
+++ b/include/uapi/media/cam_req_mgr.h
@@ -21,6 +21,7 @@
#define CAM_CSIPHY_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 8)
#define CAM_ACTUATOR_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 9)
#define CAM_CCI_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 10)
+#define CAM_FLASH_DEVICE_TYPE (CAM_DEVICE_TYPE_BASE + 11)
/* cam_req_mgr hdl info */
#define CAM_REQ_MGR_HDL_IDX_POS 8
diff --git a/include/uapi/scsi/Kbuild b/include/uapi/scsi/Kbuild
index fad00e0..9a0da84 100644
--- a/include/uapi/scsi/Kbuild
+++ b/include/uapi/scsi/Kbuild
@@ -2,6 +2,8 @@
header-y += fc/
header-y += ufs/
header-y += scsi_bsg_fc.h
+header-y += sg.h
+header-y += scsi_ioctl.h
header-y += scsi_netlink.h
header-y += scsi_netlink_fc.h
header-y += cxlflash_ioctl.h
diff --git a/include/scsi/scsi_ioctl.h b/include/uapi/scsi/scsi_ioctl.h
similarity index 97%
rename from include/scsi/scsi_ioctl.h
rename to include/uapi/scsi/scsi_ioctl.h
index 8d19d1d..516c581a 100644
--- a/include/scsi/scsi_ioctl.h
+++ b/include/uapi/scsi/scsi_ioctl.h
@@ -1,6 +1,8 @@
#ifndef _SCSI_IOCTL_H
#define _SCSI_IOCTL_H
+#include <linux/types.h>
+
#define SCSI_IOCTL_SEND_COMMAND 1
#define SCSI_IOCTL_TEST_UNIT_READY 2
#define SCSI_IOCTL_BENCHMARK_COMMAND 3
diff --git a/include/scsi/sg.h b/include/uapi/scsi/sg.h
similarity index 99%
rename from include/scsi/sg.h
rename to include/uapi/scsi/sg.h
index 3afec70..08d3beb 100644
--- a/include/scsi/sg.h
+++ b/include/uapi/scsi/sg.h
@@ -2,6 +2,7 @@
#define _SCSI_GENERIC_H
#include <linux/compiler.h>
+#include <linux/param.h>
/*
* History:
@@ -209,7 +210,6 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */
/* Alternate style type names, "..._t" variants preferred */
typedef struct sg_io_hdr Sg_io_hdr;
-typedef struct sg_io_vec Sg_io_vec;
typedef struct sg_scsi_id Sg_scsi_id;
typedef struct sg_req_info Sg_req_info;
diff --git a/init/main.c b/init/main.c
index c91ca2c..aca8f3e 100644
--- a/init/main.c
+++ b/init/main.c
@@ -867,7 +867,6 @@ static void __init do_basic_setup(void)
do_ctors();
usermodehelper_enable();
do_initcalls();
- random_int_secret_init();
}
static void __init do_pre_smp_initcalls(void)
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 69df75d..1d203e1 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -1122,8 +1122,9 @@ static int do_cpu_up(unsigned int cpu, enum cpuhp_state target)
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/ptrace.c b/kernel/ptrace.c
index 49ba7c1..a5caece 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -181,11 +181,17 @@ static void ptrace_unfreeze_traced(struct task_struct *task)
WARN_ON(!task->ptrace || task->parent != current);
+ /*
+ * PTRACE_LISTEN can allow ptrace_trap_notify to wake us up remotely.
+ * Recheck state under the lock to close this race.
+ */
spin_lock_irq(&task->sighand->siglock);
- if (__fatal_signal_pending(task))
- wake_up_state(task, __TASK_TRACED);
- else
- task->state = TASK_TRACED;
+ if (task->state == __TASK_TRACED) {
+ if (__fatal_signal_pending(task))
+ wake_up_state(task, __TASK_TRACED);
+ else
+ task->state = TASK_TRACED;
+ }
spin_unlock_irq(&task->sighand->siglock);
}
diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c
index 1dde338..1040a43 100644
--- a/kernel/sched/core_ctl.c
+++ b/kernel/sched/core_ctl.c
@@ -23,7 +23,7 @@
#include <trace/events/sched.h>
-#define MAX_CPUS_PER_CLUSTER 4
+#define MAX_CPUS_PER_CLUSTER 6
#define MAX_CLUSTERS 2
struct cluster_data {
@@ -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;
@@ -166,7 +167,9 @@ static ssize_t store_busy_up_thres(struct cluster_data *state,
unsigned int val[MAX_CPUS_PER_CLUSTER];
int ret, i;
- ret = sscanf(buf, "%u %u %u %u\n", &val[0], &val[1], &val[2], &val[3]);
+ ret = sscanf(buf, "%u %u %u %u %u %u\n",
+ &val[0], &val[1], &val[2], &val[3],
+ &val[4], &val[5]);
if (ret != 1 && ret != state->num_cpus)
return -EINVAL;
@@ -199,7 +202,9 @@ static ssize_t store_busy_down_thres(struct cluster_data *state,
unsigned int val[MAX_CPUS_PER_CLUSTER];
int ret, i;
- ret = sscanf(buf, "%u %u %u %u\n", &val[0], &val[1], &val[2], &val[3]);
+ ret = sscanf(buf, "%u %u %u %u %u %u\n",
+ &val[0], &val[1], &val[2], &val[3],
+ &val[4], &val[5]);
if (ret != 1 && ret != state->num_cpus)
return -EINVAL;
@@ -260,6 +265,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 +299,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;
}
@@ -308,7 +318,9 @@ static ssize_t store_not_preferred(struct cluster_data *state,
unsigned long flags;
int ret;
- ret = sscanf(buf, "%u %u %u %u\n", &val[0], &val[1], &val[2], &val[3]);
+ ret = sscanf(buf, "%u %u %u %u %u %u\n",
+ &val[0], &val[1], &val[2], &val[3],
+ &val[4], &val[5]);
if (ret != state->num_cpus)
return -EINVAL;
@@ -525,7 +537,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 +547,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 +558,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 +575,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 +592,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 +728,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 +753,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 +770,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 +788,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 +806,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 +831,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 +904,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 +931,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 +940,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/energy.c b/kernel/sched/energy.c
index b0656b7..05dd2cb 100644
--- a/kernel/sched/energy.c
+++ b/kernel/sched/energy.c
@@ -56,6 +56,9 @@ void init_sched_energy_costs(void)
int sd_level, i, nstates, cpu;
const __be32 *val;
+ if (!sched_is_energy_aware())
+ return;
+
for_each_possible_cpu(cpu) {
cn = of_get_cpu_node(cpu, NULL);
if (!cn) {
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 2a8643c..6fb615e 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -5343,6 +5343,15 @@ static inline bool energy_aware(void)
return sched_feat(ENERGY_AWARE);
}
+/*
+ * Externally visible function. Let's keep the one above
+ * so that the check is inlined/optimized in the sched paths.
+ */
+bool sched_is_energy_aware(void)
+{
+ return energy_aware();
+}
+
struct energy_env {
struct sched_group *sg_top;
struct sched_group *sg_cap;
diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c
index 7f686ff..1b4bb23 100644
--- a/kernel/sched/walt.c
+++ b/kernel/sched/walt.c
@@ -81,7 +81,7 @@ __read_mostly unsigned int walt_ravg_window = 20000000;
static unsigned int sync_cpu;
static ktime_t ktime_last;
-static bool walt_ktime_suspended;
+static __read_mostly bool walt_ktime_suspended;
static unsigned int task_load(struct task_struct *p)
{
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index 4223c4a..a088a55 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -1616,20 +1616,35 @@ int hrtimers_prepare_cpu(unsigned int cpu)
}
#ifdef CONFIG_HOTPLUG_CPU
-static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
- struct hrtimer_clock_base *new_base,
+static void migrate_hrtimer_list(struct hrtimer_cpu_base *old_base,
+ struct hrtimer_cpu_base *new_base,
+ unsigned int i, bool wait,
bool remove_pinned)
{
struct hrtimer *timer;
struct timerqueue_node *node;
struct timerqueue_head pinned;
int is_pinned;
+ struct hrtimer_clock_base *old_c_base = &old_base->clock_base[i];
+ struct hrtimer_clock_base *new_c_base = &new_base->clock_base[i];
timerqueue_init_head(&pinned);
- while ((node = timerqueue_getnext(&old_base->active))) {
+ while ((node = timerqueue_getnext(&old_c_base->active))) {
timer = container_of(node, struct hrtimer, node);
- BUG_ON(hrtimer_callback_running(timer));
+ if (wait) {
+ /* Ensure timers are done running before continuing */
+ while (hrtimer_callback_running(timer)) {
+ raw_spin_unlock(&old_base->lock);
+ raw_spin_unlock(&new_base->lock);
+ cpu_relax();
+ raw_spin_lock(&new_base->lock);
+ raw_spin_lock_nested(&old_base->lock,
+ SINGLE_DEPTH_NESTING);
+ }
+ } else {
+ BUG_ON(hrtimer_callback_running(timer));
+ }
debug_deactivate(timer);
/*
@@ -1637,7 +1652,7 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
* timer could be seen as !active and just vanish away
* under us on another CPU
*/
- __remove_hrtimer(timer, old_base, HRTIMER_STATE_ENQUEUED, 0);
+ __remove_hrtimer(timer, old_c_base, HRTIMER_STATE_ENQUEUED, 0);
is_pinned = timer->state & HRTIMER_STATE_PINNED;
if (!remove_pinned && is_pinned) {
@@ -1645,7 +1660,7 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
continue;
}
- timer->base = new_base;
+ timer->base = new_c_base;
/*
* Enqueue the timers on the new cpu. This does not
* reprogram the event device in case the timer
@@ -1654,7 +1669,7 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
* sort out already expired timers and reprogram the
* event device.
*/
- enqueue_hrtimer(timer, new_base);
+ enqueue_hrtimer(timer, new_c_base);
}
/* Re-queue pinned timers for non-hotplug usecase */
@@ -1662,11 +1677,12 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
timer = container_of(node, struct hrtimer, node);
timerqueue_del(&pinned, &timer->node);
- enqueue_hrtimer(timer, old_base);
+ enqueue_hrtimer(timer, old_c_base);
}
}
-static void __migrate_hrtimers(unsigned int scpu, bool remove_pinned)
+static void
+__migrate_hrtimers(unsigned int scpu, bool wait, bool remove_pinned)
{
struct hrtimer_cpu_base *old_base, *new_base;
unsigned long flags;
@@ -1683,8 +1699,8 @@ static void __migrate_hrtimers(unsigned int scpu, bool remove_pinned)
raw_spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING);
for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) {
- migrate_hrtimer_list(&old_base->clock_base[i],
- &new_base->clock_base[i], remove_pinned);
+ migrate_hrtimer_list(old_base, new_base, i, wait,
+ remove_pinned);
}
raw_spin_unlock(&old_base->lock);
@@ -1700,13 +1716,13 @@ int hrtimers_dead_cpu(unsigned int scpu)
BUG_ON(cpu_online(scpu));
tick_cancel_sched_timer(scpu);
- __migrate_hrtimers(scpu, true);
+ __migrate_hrtimers(scpu, false, true);
return 0;
}
void hrtimer_quiesce_cpu(void *cpup)
{
- __migrate_hrtimers(*(int *)cpup, false);
+ __migrate_hrtimers(*(int *)cpup, true, false);
}
#endif /* CONFIG_HOTPLUG_CPU */
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 5f98592..9055429 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -1165,7 +1165,7 @@ void tick_irq_enter(void)
static void update_rq_stats(void)
{
unsigned long jiffy_gap = 0;
- unsigned int rq_avg = 0;
+ unsigned long long rq_avg = 0;
unsigned long flags = 0;
jiffy_gap = jiffies - rq_info.rq_poll_last_jiffy;
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 9c14373..f30847a 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -4870,9 +4870,9 @@ static __init int test_ringbuffer(void)
rb_data[cpu].cnt = cpu;
rb_threads[cpu] = kthread_create(rb_test, &rb_data[cpu],
"rbtester/%d", cpu);
- if (WARN_ON(!rb_threads[cpu])) {
+ if (WARN_ON(IS_ERR(rb_threads[cpu]))) {
pr_cont("FAILED\n");
- ret = -1;
+ ret = PTR_ERR(rb_threads[cpu]);
goto out_free;
}
@@ -4882,9 +4882,9 @@ static __init int test_ringbuffer(void)
/* Now create the rb hammer! */
rb_hammer = kthread_run(rb_hammer_test, NULL, "rbhammer");
- if (WARN_ON(!rb_hammer)) {
+ if (WARN_ON(IS_ERR(rb_hammer))) {
pr_cont("FAILED\n");
- ret = -1;
+ ret = PTR_ERR(rb_hammer);
goto out_free;
}
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 0adc6f9..9ff5657 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -1525,7 +1525,6 @@ COMPAT_SYSCALL_DEFINE5(get_mempolicy, int __user *, policy,
COMPAT_SYSCALL_DEFINE3(set_mempolicy, int, mode, compat_ulong_t __user *, nmask,
compat_ulong_t, maxnode)
{
- long err = 0;
unsigned long __user *nm = NULL;
unsigned long nr_bits, alloc_size;
DECLARE_BITMAP(bm, MAX_NUMNODES);
@@ -1534,14 +1533,13 @@ COMPAT_SYSCALL_DEFINE3(set_mempolicy, int, mode, compat_ulong_t __user *, nmask,
alloc_size = ALIGN(nr_bits, BITS_PER_LONG) / 8;
if (nmask) {
- err = compat_get_bitmap(bm, nmask, nr_bits);
+ if (compat_get_bitmap(bm, nmask, nr_bits))
+ return -EFAULT;
nm = compat_alloc_user_space(alloc_size);
- err |= copy_to_user(nm, bm, alloc_size);
+ if (copy_to_user(nm, bm, alloc_size))
+ return -EFAULT;
}
- if (err)
- return -EFAULT;
-
return sys_set_mempolicy(mode, nm, nr_bits+1);
}
@@ -1549,7 +1547,6 @@ COMPAT_SYSCALL_DEFINE6(mbind, compat_ulong_t, start, compat_ulong_t, len,
compat_ulong_t, mode, compat_ulong_t __user *, nmask,
compat_ulong_t, maxnode, compat_ulong_t, flags)
{
- long err = 0;
unsigned long __user *nm = NULL;
unsigned long nr_bits, alloc_size;
nodemask_t bm;
@@ -1558,14 +1555,13 @@ COMPAT_SYSCALL_DEFINE6(mbind, compat_ulong_t, start, compat_ulong_t, len,
alloc_size = ALIGN(nr_bits, BITS_PER_LONG) / 8;
if (nmask) {
- err = compat_get_bitmap(nodes_addr(bm), nmask, nr_bits);
+ if (compat_get_bitmap(nodes_addr(bm), nmask, nr_bits))
+ return -EFAULT;
nm = compat_alloc_user_space(alloc_size);
- err |= copy_to_user(nm, nodes_addr(bm), alloc_size);
+ if (copy_to_user(nm, nodes_addr(bm), alloc_size))
+ return -EFAULT;
}
- if (err)
- return -EFAULT;
-
return sys_mbind(start, len, mode, nm, nr_bits+1, flags);
}
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index f61724f4f..8e57301 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -4374,13 +4374,13 @@ void show_free_areas(unsigned int filter)
K(node_page_state(pgdat, NR_FILE_MAPPED)),
K(node_page_state(pgdat, NR_FILE_DIRTY)),
K(node_page_state(pgdat, NR_WRITEBACK)),
+ K(node_page_state(pgdat, NR_SHMEM)),
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
K(node_page_state(pgdat, NR_SHMEM_THPS) * HPAGE_PMD_NR),
K(node_page_state(pgdat, NR_SHMEM_PMDMAPPED)
* HPAGE_PMD_NR),
K(node_page_state(pgdat, NR_ANON_THPS) * HPAGE_PMD_NR),
#endif
- K(node_page_state(pgdat, NR_SHMEM)),
K(node_page_state(pgdat, NR_WRITEBACK_TEMP)),
K(node_page_state(pgdat, NR_UNSTABLE_NFS)),
node_page_state(pgdat, NR_PAGES_SCANNED),
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 873df83..e6acef5 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -798,6 +798,7 @@ static void ip_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buf
struct rtable *rt;
struct flowi4 fl4;
const struct iphdr *iph = (const struct iphdr *) skb->data;
+ struct net *net = dev_net(skb->dev);
int oif = skb->dev->ifindex;
u8 tos = RT_TOS(iph->tos);
u8 prot = iph->protocol;
@@ -805,7 +806,7 @@ static void ip_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buf
rt = (struct rtable *) dst;
- __build_flow_key(sock_net(sk), &fl4, sk, iph, oif, tos, prot, mark, 0);
+ __build_flow_key(net, &fl4, sk, iph, oif, tos, prot, mark, 0);
__ip_do_redirect(rt, skb, &fl4, true);
}
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 0cfb91f..7588fa9 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -5978,13 +5978,6 @@ static const struct ctl_table addrconf_sysctl[] = {
.mode = 0644,
.proc_handler = proc_dointvec,
},
- {
- .procname = "accept_ra_rt_table",
- .data = &ipv6_devconf.accept_ra_rt_table,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- },
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
{
.procname = "optimistic_dad",
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 638ec07..8d7747e 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -726,7 +726,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
ieee80211_recalc_ps(local);
if (sdata->vif.type == NL80211_IFTYPE_MONITOR ||
- sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+ sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+ local->ops->wake_tx_queue) {
/* XXX: for AP_VLAN, actually track AP queues */
netif_tx_start_all_queues(dev);
} else if (dev) {
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index f2b04a7..8ab0974 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -4235,8 +4235,8 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
if (unlikely(!PAGE_ALIGNED(req->tp_block_size)))
goto out;
if (po->tp_version >= TPACKET_V3 &&
- (int)(req->tp_block_size -
- BLK_PLUS_PRIV(req_u->req3.tp_sizeof_priv)) <= 0)
+ req->tp_block_size <=
+ BLK_PLUS_PRIV((u64)req_u->req3.tp_sizeof_priv))
goto out;
if (unlikely(req->tp_frame_size < po->tp_hdrlen +
po->tp_reserve))
diff --git a/net/socket.c b/net/socket.c
index e825856..a4fb472 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -89,6 +89,8 @@
#include <linux/magic.h>
#include <linux/slab.h>
#include <linux/xattr.h>
+#include <linux/seemp_api.h>
+#include <linux/seemp_instrumentation.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
@@ -533,7 +535,7 @@ static ssize_t sockfs_listxattr(struct dentry *dentry, char *buffer,
return used;
}
-int sockfs_setattr(struct dentry *dentry, struct iattr *iattr)
+static int sockfs_setattr(struct dentry *dentry, struct iattr *iattr)
{
int err = simple_setattr(dentry, iattr);
@@ -1646,6 +1648,8 @@ SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len,
struct iovec iov;
int fput_needed;
+ seemp_logk_sendto(fd, buff, len, flags, addr, addr_len);
+
err = import_single_range(WRITE, buff, len, &iov, &msg.msg_iter);
if (unlikely(err))
return err;
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 6fdffde..1530825 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1548,7 +1548,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
ret = SVC_COMPLETE;
goto out;
drop:
- ret = SVC_DROP;
+ ret = SVC_CLOSE;
out:
if (rsci)
cache_put(&rsci->h, sn->rsc_cache);
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 7c8070e..75f290b 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1155,8 +1155,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
case SVC_DENIED:
goto err_bad_auth;
case SVC_CLOSE:
- if (test_bit(XPT_TEMP, &rqstp->rq_xprt->xpt_flags))
- svc_close_xprt(rqstp->rq_xprt);
+ goto close;
case SVC_DROP:
goto dropit;
case SVC_COMPLETE:
@@ -1246,7 +1245,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
sendit:
if (svc_authorise(rqstp))
- goto dropit;
+ goto close;
return 1; /* Caller can now send it */
dropit:
@@ -1254,11 +1253,16 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
dprintk("svc: svc_process dropit\n");
return 0;
+ close:
+ if (test_bit(XPT_TEMP, &rqstp->rq_xprt->xpt_flags))
+ svc_close_xprt(rqstp->rq_xprt);
+ dprintk("svc: svc_process close\n");
+ return 0;
+
err_short_len:
svc_printk(rqstp, "short len %Zd, dropping request\n",
argv->iov_len);
-
- goto dropit; /* drop request */
+ goto close;
err_bad_rpc:
serv->sv_stats->rpcbadfmt++;
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index a2fc3a0..449e4a3 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -1,17 +1,1491 @@
-#
-# This file is a placeholder to prevent accidental build breakage if someone
-# enables CONFIG_CFG80211_INTERNAL_REGDB. Almost no one actually needs to
-# enable that build option.
-#
-# You should be using CRDA instead. It is even better if you use the CRDA
-# package provided by your distribution, since they will probably keep it
-# up-to-date on your behalf.
-#
-# If you _really_ intend to use CONFIG_CFG80211_INTERNAL_REGDB then you will
-# need to replace this file with one containing appropriately formatted
-# regulatory rules that cover the regulatory domains you will be using. Your
-# best option is to extract the db.txt file from the wireless-regdb git
-# repository:
-#
-# git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-regdb.git
-#
+# This is the world regulatory domain
+country 00:
+ (2402 - 2472 @ 40), (20)
+ # Channel 12 - 13.
+ (2457 - 2482 @ 40), (20), PASSIVE-SCAN, NO-IBSS
+ # Channel 14. Only JP enables this and for 802.11b only
+ (2474 - 2494 @ 20), (20), PASSIVE-SCAN, NO-IBSS, NO-OFDM
+ # Channel 36 - 48
+ (5170 - 5250 @ 80), (20), PASSIVE-SCAN, NO-IBSS
+ (5250 - 5330 @ 80), (20), PASSIVE-SCAN, NO-IBSS
+ (5490 - 5710 @ 80), (20), PASSIVE-SCAN, NO-IBSS
+ # NB: 5260 MHz - 5700 MHz requies DFS
+ # Channel 149 - 165
+ (5735 - 5835 @ 80), (20), PASSIVE-SCAN, NO-IBSS
+ # IEEE 802.11ad (60GHz), channels 1..3
+ (57240 - 63720 @ 2160), (0)
+
+
+country AE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country AF: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country AI: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country AL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5150 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5350 @ 80), (23), DFS, AUTO-BW
+ (5470 - 5710 @ 160), (30), DFS
+
+country AM: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 20), (18)
+ (5250 - 5330 @ 20), (18), DFS
+
+country AN: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country AR:
+ (2402 - 2482 @ 40), (36)
+ (5170 - 5330 @ 160), (23)
+ (5490 - 5590 @ 80), (36)
+ (5650 - 5730 @ 80), (36)
+ (5735 - 5835 @ 80), (36)
+
+country AS: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5850 @ 80), (30)
+
+country AT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country AU: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5590 @ 80), (24), DFS
+ (5650 - 5730 @ 80), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country AW: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country AZ: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (18), AUTO-BW
+ (5250 - 5330 @ 80), (18), DFS, AUTO-BW
+
+country BA: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country BB: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+country BD:
+ (2402 - 2482 @ 40), (20)
+ (5735 - 5835 @ 80), (30)
+
+country BE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country BF: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country BG: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country BH:
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5330 @ 20), (23)
+ (5735 - 5835 @ 20), (33)
+
+country BL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country BM: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country BN: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (20)
+
+country BO: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5250 - 5330 @ 80), (30), DFS
+ (5735 - 5835 @ 80), (30)
+
+country BR: DFS-FCC
+ (2402 - 2482 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country BS: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country BT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country BY: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country BZ:
+ (2402 - 2482 @ 40), (36)
+ (5170 - 5330 @ 160), (27)
+ (5490 - 5730 @ 160), (36)
+ (5735 - 5835 @ 80), (36)
+
+country CA: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5590 @ 80), (24), DFS
+ (5650 - 5730 @ 80), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country CF: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 40), (24)
+ (5250 - 5330 @ 40), (24), DFS
+ (5490 - 5730 @ 40), (24), DFS
+ (5735 - 5835 @ 40), (30)
+
+country CH: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+
+country CI: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country CL:
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5330 @ 160), (20)
+ (5735 - 5835 @ 80), (20)
+
+country CN: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (33)
+ # 60 gHz band channels 1,4: 28dBm, channels 2,3: 44dBm
+ # ref: http://www.miit.gov.cn/n11293472/n11505629/n11506593/n11960250/n11960606/n11960700/n12330791.files/n12330790.pdf
+ (57240 - 59400 @ 2160), (28)
+ (59400 - 63720 @ 2160), (44)
+ (63720 - 65880 @ 2160), (28)
+
+country CO: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country CR: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 20), (24)
+ (5250 - 5330 @ 20), (24), DFS
+ (5490 - 5730 @ 20), (24), DFS
+ (5735 - 5835 @ 20), (30)
+
+country CX: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country CY: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+# Data from http://www.ctu.eu/164/download/VOR/VOR-12-08-2005-34.pdf
+# and http://www.ctu.eu/164/download/VOR/VOR-12-05-2007-6-AN.pdf
+country CZ: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+# Data from "Frequenznutzungsplan" (as published in April 2008), downloaded from
+# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38448/publicationFile/2659/Frequenznutzungsplan2008_Id17448pdf.pdf
+# For the 5GHz range also see
+# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38216/publicationFile/6579/WLAN5GHzVfg7_2010_28042010pdf.pdf
+
+country DE: DFS-ETSI
+ # entries 279004 and 280006
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country DK: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country DM: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+country DO: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+country DZ: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5670 @ 160), (23), DFS
+
+country EC: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 20), (24)
+ (5250 - 5330 @ 20), (24), DFS
+ (5490 - 5730 @ 20), (24), DFS
+ (5735 - 5835 @ 20), (30)
+
+country EE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country EG: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 40), (23)
+ (5250 - 5330 @ 40), (23), DFS
+
+country ES: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country ET: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country FI: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country FM: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country FR: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country GB: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country GD: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country GE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (18), AUTO-BW
+ (5250 - 5330 @ 80), (18), DFS, AUTO-BW
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country GF: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country GH: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country GL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country GP: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country GR: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country GT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country GU: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country GY:
+ (2402 - 2482 @ 40), (30)
+ (5735 - 5835 @ 80), (30)
+
+country HK: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country HN:
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5330 @ 160), (24)
+ (5490 - 5730 @ 160), (24)
+ (5735 - 5835 @ 80), (30)
+
+country HR: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country HT: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country HU: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country ID:
+ # ref: http://www.postel.go.id/content/ID/regulasi/standardisasi/kepdir/bwa%205,8%20ghz.pdf
+ (2402 - 2482 @ 40), (30)
+ (5735 - 5815 @ 20), (30)
+
+country IE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country IL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+
+country IN:
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5330 @ 160), (23)
+ (5735 - 5835 @ 80), (30)
+
+country IR:
+ (2402 - 2482 @ 40), (20)
+ (5735 - 5835 @ 80), (30)
+
+country IS: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country IT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country JM: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country JO:
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23)
+ (5735 - 5835 @ 80), (23)
+
+country JP: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (2474 - 2494 @ 20), (20), NO-OFDM
+ (5170 - 5250 @ 80), (20), AUTO-BW, NO-OUTDOOR
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW, NO-OUTDOOR
+ (5490 - 5710 @ 160), (20), DFS
+ # 60 GHz band channels 2-4 at 10mW,
+ # ref: http://www.arib.or.jp/english/html/overview/doc/1-STD-T74v1_1.pdf
+ (59000 - 66000 @ 2160), (10 mW)
+
+country KE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23)
+ (5490 - 5570 @ 80), (30), DFS
+ (5735 - 5775 @ 40), (23)
+
+country KH: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country KN: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ (5735 - 5815 @ 80), (30)
+
+country KR: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ (5735 - 5835 @ 80), (30)
+ # 60 GHz band channels 1-4,
+ # ref: http://www.law.go.kr/%ED%96%89%EC%A0%95%EA%B7%9C%EC%B9%99/%EB%AC%B4%EC%84%A0%EC%84%A4%EB%B9%84%EA%B7%9C%EC%B9%99
+ (57000 - 66000 @ 2160), (43)
+
+country KP: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20)
+ (5250 - 5330 @ 80), (20), DFS
+ (5490 - 5630 @ 80), (30), DFS
+ (5735 - 5815 @ 80), (30)
+
+country KW: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+
+country KY: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country KZ:
+ (2402 - 2482 @ 40), (20)
+
+country LB: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country LC: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (30), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ (5735 - 5815 @ 80), (30)
+
+country LI: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+
+country LK: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 20), (24)
+ (5250 - 5330 @ 20), (24), DFS
+ (5490 - 5730 @ 20), (24), DFS
+ (5735 - 5835 @ 20), (30)
+
+country LS: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country LT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country LU: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country LV: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country MA: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+
+country MC: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country MD: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country ME: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country MF: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country MH: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country MK: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country MN: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country MO: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country MP: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country MQ: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country MR: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country MT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country MU: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country MV: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (20)
+
+country MW: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country MX: DFS-FCC
+ (2402 - 2482 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country MY: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5650 @ 160), (24), DFS
+ (5735 - 5815 @ 80), (24)
+
+country NA: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ (5735 - 5835 @ 80), (33)
+
+country NG: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5250 - 5330 @ 80), (30), DFS
+ (5735 - 5835 @ 80), (30)
+
+country NI: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country NL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country NO: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country NP:
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5330 @ 160), (20)
+ (5735 - 5835 @ 80), (20)
+
+country NZ: DFS-FCC
+ (2402 - 2482 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country OM: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country PA:
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (23), AUT0-BW
+ (5250 - 5330 @ 80), (30), AUTO-BW
+ (5735 - 5835 @ 80), (36)
+
+country PE: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country PF: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country PG: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country PH: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country PK:
+ (2402 - 2482 @ 40), (30)
+ (5735 - 5835 @ 80), (30)
+
+country PL: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country PM: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country PR: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+# Public Safety FCCA, FCC4
+# 27dBm [4.9GHz 1/4 rate], 30dBm [1/2 rate], 33dBm [full rate], and 5GHz same as FCC1
+# db.txt cannot express the limitation on 5G so disable all 5G channels for FCC4
+country PS: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (4940 - 4990 @ 40), (33)
+
+country PT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country PW: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country PY: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country QA:
+ (2402 - 2482 @ 40), (20)
+ (5735 - 5835 @ 80), (30)
+
+country RE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country RO: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+# Source:
+# http://www.ratel.rs/upload/documents/Plan_namene/Plan_namene-sl_glasnik.pdf
+country RS: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country RU:
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5330 @ 160), (23)
+ (5490 - 5730 @ 160), (30)
+ (5735 - 5835 @ 80), (30)
+
+country RW: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country SA: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country SE: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country SG: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country SI: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country SK: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 5.9ghz band
+ # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country SN:
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5330 @ 160), (24)
+ (5490 - 5730 @ 160), (24)
+ (5735 - 5835 @ 80), (30)
+
+country SR: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country SV: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 20), (23)
+ (5250 - 5330 @ 20), (23), DFS
+ (5735 - 5835 @ 20), (30)
+
+country SY:
+ (2402 - 2482 @ 40), (20)
+
+country TC: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country TD: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country TG: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 40), (23)
+ (5250 - 5330 @ 40), (23), DFS
+ (5490 - 5710 @ 40), (30), DFS
+
+country TH: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country TN: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+
+country TR: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country TT:
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5330 @ 160), (27)
+ (5490 - 5730 @ 160), (36)
+ (5735 - 5835 @ 80), (36)
+
+country TW: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country TZ:
+ (2402 - 2482 @ 40), (20)
+ (5735 - 5835 @ 80), (30)
+
+# Source:
+# #914 / 06 Sep 2007: http://www.ucrf.gov.ua/uk/doc/nkrz/1196068874
+# #1174 / 23 Oct 2008: http://www.nkrz.gov.ua/uk/activities/ruling/1225269361
+# (appendix 8)
+# Listed 5GHz range is a lowest common denominator for all related
+# rules in the referenced laws. Such a range is used because of
+# disputable definitions there.
+country UA: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (20), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5490 - 5670 @ 160), (20), DFS
+ (5735 - 5835 @ 80), (20)
+ # 60 gHz band channels 1-4, ref: Etsi En 302 567
+ (57240 - 65880 @ 2160), (40), NO-OUTDOOR
+
+country UG: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country US: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+ # 5.9ghz band
+ # reference: https://apps.fcc.gov/edocs_public/attachmatch/FCC-03-324A1.pdf
+ (5842 - 5863 @ 5), (30)
+ (5850 - 5870 @ 10), (30)
+ (5860 - 5880 @ 10), (30)
+ (5865 - 5885 @ 20), (30)
+ (5870 - 5890 @ 10), (30)
+ (5880 - 5900 @ 10), (30)
+ (5890 - 5910 @ 10), (30)
+ (5895 - 5915 @ 20), (30)
+ (5900 - 5920 @ 10), (30)
+ (5910 - 5930 @ 10), (30)
+ # 60g band
+ # reference: http://cfr.regstoday.com/47cfr15.aspx#47_CFR_15p255
+ # channels 1,2,3, EIRP=40dBm(43dBm peak)
+ (57240 - 63720 @ 2160), (40)
+
+country UY: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+country UZ: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW
+
+country VC: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country VE: DFS-FCC
+ (2402 - 2482 @ 40), (30)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5735 - 5835 @ 80), (30)
+
+country VI: DFS-FCC
+ (2402 - 2472 @ 40), (30)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country VN: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country VU: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country WF: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country WS: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 40), (23)
+ (5250 - 5330 @ 40), (23), DFS
+ (5490 - 5710 @ 40), (30), DFS
+
+country XA: DFS-JP
+ (2402 - 2482 @ 40), (20)
+ (2474 - 2494 @ 20), (20), NO-OFDM
+ (5170 - 5250 @ 80), (20), NO-IR, AUTO-BW, NO-OUTDOOR
+ (5250 - 5330 @ 80), (20), DFS, AUTO-BW, NO-OUTDOOR
+ (5490 - 5710 @ 160), (20), DFS
+ (59000 - 66000 @ 2160), (10 mW)
+
+country YE:
+ (2402 - 2482 @ 40), (20)
+
+country YT: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
+
+country ZA: DFS-FCC
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (24), AUTO-BW
+ (5250 - 5330 @ 80), (24), DFS, AUTO-BW
+ (5490 - 5730 @ 160), (24), DFS
+ (5735 - 5835 @ 80), (30)
+
+country ZW: DFS-ETSI
+ (2402 - 2482 @ 40), (20)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (23), DFS, AUTO-BW
+ (5490 - 5710 @ 160), (30), DFS
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
index 14b3f00..2927d06 100644
--- a/net/wireless/sysfs.c
+++ b/net/wireless/sysfs.c
@@ -130,12 +130,10 @@ static int wiphy_resume(struct device *dev)
/* Age scan results with time spent in suspend */
cfg80211_bss_age(rdev, get_seconds() - rdev->suspend_at);
- if (rdev->ops->resume) {
- rtnl_lock();
- if (rdev->wiphy.registered)
- ret = rdev_resume(rdev);
- rtnl_unlock();
- }
+ rtnl_lock();
+ if (rdev->wiphy.registered && rdev->ops->resume)
+ ret = rdev_resume(rdev);
+ rtnl_unlock();
return ret;
}
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index c5b281e..f93db0e 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -2687,6 +2687,7 @@
# Check for git id commit length and improperly formed commit descriptions
if ($in_commit_log && !$commit_log_possible_stack_dump &&
$line !~ /^\s*(?:Link|Patchwork|http|https|BugLink):/i &&
+ $line !~ /^This reverts commit [0-9a-f]{7,40}/ &&
($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i ||
($line =~ /(?:\s|^)[0-9a-f]{12,40}(?:[\s"'\(\[]|$)/i &&
$line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i &&
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index b4867ff..9c6f471 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -888,6 +888,8 @@
config SND_SOC_WCD9335
tristate
depends on WCD9335_CODEC
+ select SND_SOC_WCD_MBHC
+ select SND_SOC_WCD_MBHC_LEGACY
config SND_SOC_WCD934X
tristate
@@ -903,6 +905,7 @@
tristate
depends on SND_SOC_WCD934X
select SND_SOC_WCD_MBHC
+ select SND_SOC_WCD_MBHC_ADC
config SND_SOC_WSA881X
tristate
@@ -931,8 +934,6 @@
config SND_SOC_WCD_MBHC
tristate
- default y if (SND_SOC_MSM8909_WCD=y || SND_SOC_SDM660_CDC=y || SND_SOC_WCD9335=y) && SND_SOC_MDMCALIFORNIUM!=y
- select SND_SOC_WCD_MBHC_LEGACY
config SND_SOC_WCD_MBHC_LEGACY
tristate
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index 49caf13..fdc14e5 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -2813,6 +2813,8 @@ MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id);
#ifdef CONFIG_ACPI
static const struct acpi_device_id rt5670_acpi_match[] = {
{ "10EC5670", 0},
+ { "10EC5672", 0},
+ { "10EC5640", 0}, /* quirk */
{ },
};
MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match);
diff --git a/sound/soc/codecs/sdm660_cdc/Kconfig b/sound/soc/codecs/sdm660_cdc/Kconfig
index d370da3..2f36c39 100644
--- a/sound/soc/codecs/sdm660_cdc/Kconfig
+++ b/sound/soc/codecs/sdm660_cdc/Kconfig
@@ -1,3 +1,5 @@
config SND_SOC_SDM660_CDC
tristate "MSM Internal PMIC based codec"
+ select SND_SOC_WCD_MBHC
+ select SND_SOC_WCD_MBHC_LEGACY
diff --git a/sound/soc/codecs/wcd-mbhc-adc.c b/sound/soc/codecs/wcd-mbhc-adc.c
index 2c7d667..7278431 100644
--- a/sound/soc/codecs/wcd-mbhc-adc.c
+++ b/sound/soc/codecs/wcd-mbhc-adc.c
@@ -354,7 +354,7 @@ static bool wcd_mbhc_adc_check_for_spl_headset(struct wcd_mbhc *mbhc,
{
bool spl_hs = false;
int output_mv = 0;
- int adc_threshold = 0;
+ int adc_threshold = 0, adc_hph_threshold = 0;
pr_debug("%s: enter\n", __func__);
if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
@@ -372,8 +372,11 @@ static bool wcd_mbhc_adc_check_for_spl_headset(struct wcd_mbhc *mbhc,
output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV *
wcd_mbhc_get_micbias(mbhc))/WCD_MBHC_ADC_MICBIAS_MV);
+ adc_hph_threshold = ((WCD_MBHC_ADC_HPH_THRESHOLD_MV *
+ wcd_mbhc_get_micbias(mbhc))/
+ WCD_MBHC_ADC_MICBIAS_MV);
- if (output_mv > adc_threshold) {
+ if (output_mv > adc_threshold || output_mv < adc_hph_threshold) {
spl_hs = false;
} else {
spl_hs = true;
@@ -382,15 +385,16 @@ static bool wcd_mbhc_adc_check_for_spl_headset(struct wcd_mbhc *mbhc,
}
/* MB2 back to 1.8v if the type is not special headset */
- if (!spl_hs) {
+ if (spl_hs_cnt && (*spl_hs_cnt != WCD_MBHC_SPL_HS_CNT)) {
mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec,
mbhc->mbhc_cfg->mbhc_micbias, false);
/* Add 10ms delay for micbias to settle */
usleep_range(10000, 10100);
- } else {
- pr_debug("%s: Detected special HS (%d)\n", __func__, spl_hs);
}
+ if (spl_hs)
+ pr_debug("%s: Detected special HS (%d)\n", __func__, spl_hs);
+
exit:
pr_debug("%s: leave\n", __func__);
return spl_hs;
@@ -401,28 +405,63 @@ static bool wcd_is_special_headset(struct wcd_mbhc *mbhc)
int delay = 0;
bool ret = false;
bool is_spl_hs = false;
- int spl_hs_count = 0;
+ int output_mv = 0;
+ int adc_threshold = 0;
+
+ /*
+ * Increase micbias to 2.7V to detect headsets with
+ * threshold on microphone
+ */
+ if (mbhc->mbhc_cb->mbhc_micbias_control &&
+ !mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) {
+ pr_debug("%s: callback fn micb_ctrl_thr_mic not defined\n",
+ __func__);
+ return false;
+ } else if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) {
+ ret = mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec,
+ MIC_BIAS_2, true);
+ if (ret) {
+ pr_err("%s: mbhc_micb_ctrl_thr_mic failed, ret: %d\n",
+ __func__, ret);
+ return false;
+ }
+ }
+
+ adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV *
+ wcd_mbhc_get_micbias(mbhc)) /
+ WCD_MBHC_ADC_MICBIAS_MV);
while (!is_spl_hs) {
- delay += 50;
if (mbhc->hs_detect_work_stop) {
pr_debug("%s: stop requested: %d\n", __func__,
mbhc->hs_detect_work_stop);
break;
}
+ delay += 50;
/* Wait for 50ms for FSM to update result */
msleep(50);
- is_spl_hs = wcd_mbhc_adc_check_for_spl_headset(mbhc,
- &spl_hs_count);
- if (is_spl_hs)
- pr_debug("%s: Spl headset detected in %d msecs\n",
+ output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
+ if (output_mv <= adc_threshold) {
+ pr_debug("%s: Special headset detected in %d msecs\n",
__func__, delay);
+ is_spl_hs = true;
+ }
+
if (delay == SPECIAL_HS_DETECT_TIME_MS) {
pr_debug("%s: Spl headset not found in 2 sec\n",
__func__);
break;
}
}
+ if (is_spl_hs) {
+ pr_debug("%s: Headset with threshold found\n", __func__);
+ mbhc->micbias_enable = true;
+ ret = true;
+ }
+ if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic &&
+ !mbhc->micbias_enable)
+ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, MIC_BIAS_2,
+ false);
pr_debug("%s: leave, micb_enable: %d\n", __func__,
mbhc->micbias_enable);
@@ -533,19 +572,6 @@ static int wcd_mbhc_get_plug_from_adc(int adc_result)
return plug_type;
}
-static int wcd_mbhc_get_plug_type(struct wcd_mbhc *mbhc)
-{
- int result_mv = 0;
-
- /*
- * Use ADC single mode to minimize the chance of missing out
- * btn press/release for HEADSET type during correct work.
- */
- result_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
-
- return wcd_mbhc_get_plug_from_adc(result_mv);
-}
-
static void wcd_correct_swch_plug(struct work_struct *work)
{
struct wcd_mbhc *mbhc;
@@ -553,8 +579,9 @@ static void wcd_correct_swch_plug(struct work_struct *work)
enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
unsigned long timeout;
bool wrk_complete = false;
- int gnd_mic_swap_cnt = 0;
- bool is_pa_on = false, spl_hs = false;
+ int pt_gnd_mic_swap_cnt = 0;
+ int no_gnd_mic_swap_cnt = 0;
+ bool is_pa_on = false, spl_hs = false, spl_hs_reported = false;
int ret = 0;
int spl_hs_count = 0;
int output_mv = 0;
@@ -647,8 +674,11 @@ static void wcd_correct_swch_plug(struct work_struct *work)
spl_hs = wcd_mbhc_adc_check_for_spl_headset(mbhc,
&spl_hs_count);
- if (spl_hs_count == WCD_MBHC_SPL_HS_CNT)
+ if (spl_hs_count == WCD_MBHC_SPL_HS_CNT) {
+ output_mv = WCD_MBHC_ADC_HS_THRESHOLD_MV;
+ spl_hs = true;
mbhc->micbias_enable = true;
+ }
}
if (mbhc->mbhc_cb->hph_pa_on_status)
@@ -660,9 +690,14 @@ static void wcd_correct_swch_plug(struct work_struct *work)
ret = wcd_check_cross_conn(mbhc);
if (ret < 0)
continue;
- if (ret > 0) {
- /* Found cross connection, swap mic/gnd */
- if (gnd_mic_swap_cnt > GND_MIC_SWAP_THRESHOLD) {
+ else if (ret > 0) {
+ pt_gnd_mic_swap_cnt++;
+ no_gnd_mic_swap_cnt = 0;
+ if (pt_gnd_mic_swap_cnt <
+ GND_MIC_SWAP_THRESHOLD) {
+ continue;
+ } else if (pt_gnd_mic_swap_cnt >
+ GND_MIC_SWAP_THRESHOLD) {
/*
* This is due to GND/MIC switch didn't
* work, Report unsupported plug.
@@ -671,37 +706,57 @@ static void wcd_correct_swch_plug(struct work_struct *work)
__func__);
plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
goto report;
+ } else {
+ plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
}
- gnd_mic_swap_cnt++;
+ } else {
+ no_gnd_mic_swap_cnt++;
+ pt_gnd_mic_swap_cnt = 0;
+ plug_type = wcd_mbhc_get_plug_from_adc(
+ output_mv);
+ if ((no_gnd_mic_swap_cnt <
+ GND_MIC_SWAP_THRESHOLD) &&
+ (spl_hs_count != WCD_MBHC_SPL_HS_CNT)) {
+ continue;
+ } else {
+ no_gnd_mic_swap_cnt = 0;
+ }
+ }
+ if ((pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) &&
+ (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) {
+ /*
+ * if switch is toggled, check again,
+ * otherwise report unsupported plug
+ */
if (mbhc->mbhc_cfg->swap_gnd_mic &&
mbhc->mbhc_cfg->swap_gnd_mic(codec)) {
pr_debug("%s: US_EU gpio present,flip switch\n"
, __func__);
continue;
}
- } else {
- gnd_mic_swap_cnt++;
- plug_type = wcd_mbhc_get_plug_type(mbhc);
- if ((gnd_mic_swap_cnt <=
- GND_MIC_SWAP_THRESHOLD) &&
- (spl_hs_count != WCD_MBHC_SPL_HS_CNT)) {
- continue;
- } else {
- gnd_mic_swap_cnt = 0;
- }
}
}
- if (!spl_hs && (plug_type == MBHC_PLUG_TYPE_HIGH_HPH)) {
+ if (output_mv > WCD_MBHC_ADC_HS_THRESHOLD_MV) {
pr_debug("%s: cable is extension cable\n", __func__);
+ plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
wrk_complete = true;
} else {
+ pr_debug("%s: cable might be headset: %d\n", __func__,
+ plug_type);
if (plug_type != MBHC_PLUG_TYPE_GND_MIC_SWAP) {
- if (!spl_hs)
- plug_type =
- wcd_mbhc_get_plug_type(mbhc);
- else
- plug_type = MBHC_PLUG_TYPE_HEADSET;
+ plug_type = wcd_mbhc_get_plug_from_adc(
+ output_mv);
+ if (!spl_hs_reported &&
+ spl_hs_count == WCD_MBHC_SPL_HS_CNT) {
+ spl_hs_reported = true;
+ WCD_MBHC_RSC_LOCK(mbhc);
+ wcd_mbhc_find_plug_and_report(mbhc,
+ plug_type);
+ WCD_MBHC_RSC_UNLOCK(mbhc);
+ continue;
+ } else if (spl_hs_reported)
+ continue;
/*
* Report headset only if not already reported
* and if there is not button press without
@@ -710,12 +765,13 @@ static void wcd_correct_swch_plug(struct work_struct *work)
if ((mbhc->current_plug !=
MBHC_PLUG_TYPE_HEADSET) &&
(mbhc->current_plug !=
- MBHC_PLUG_TYPE_ANC_HEADPHONE)) {
- if (plug_type == MBHC_PLUG_TYPE_HEADSET)
- pr_debug("%s: cable is %s headset\n",
- __func__,
- ((spl_hs) ?
- "special ":""));
+ MBHC_PLUG_TYPE_ANC_HEADPHONE) &&
+ !wcd_swch_level_remove(mbhc)) {
+ pr_debug("%s: cable is %s headset\n",
+ __func__,
+ ((spl_hs_count ==
+ WCD_MBHC_SPL_HS_CNT) ?
+ "special ":""));
goto report;
}
}
@@ -782,6 +838,29 @@ static void wcd_correct_swch_plug(struct work_struct *work)
!mbhc->micbias_enable)
mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2,
MICB_DISABLE);
+
+ /*
+ * If plug type is corrected from special headset to headphone,
+ * clear the micbias enable flag, set micbias back to 1.8V and
+ * disable micbias.
+ */
+ if (plug_type == MBHC_PLUG_TYPE_HEADPHONE &&
+ mbhc->micbias_enable) {
+ if (mbhc->mbhc_cb->mbhc_micbias_control)
+ mbhc->mbhc_cb->mbhc_micbias_control(
+ codec, MIC_BIAS_2,
+ MICB_DISABLE);
+ if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
+ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
+ codec,
+ MIC_BIAS_2, false);
+ if (mbhc->mbhc_cb->set_micbias_value) {
+ mbhc->mbhc_cb->set_micbias_value(codec);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0);
+ }
+ mbhc->micbias_enable = false;
+ }
+
if (mbhc->mbhc_cfg->detect_extn_cable &&
((plug_type == MBHC_PLUG_TYPE_HEADPHONE) ||
(plug_type == MBHC_PLUG_TYPE_HEADSET)) &&
@@ -813,9 +892,41 @@ static void wcd_correct_swch_plug(struct work_struct *work)
static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data)
{
struct wcd_mbhc *mbhc = data;
+ unsigned long timeout;
+ int adc_threshold, output_mv, retry = 0;
pr_debug("%s: enter\n", __func__);
WCD_MBHC_RSC_LOCK(mbhc);
+
+ timeout = jiffies +
+ msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS);
+ adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV *
+ wcd_mbhc_get_micbias(mbhc)) /
+ WCD_MBHC_ADC_MICBIAS_MV);
+ do {
+ retry++;
+ /*
+ * read output_mv every 10ms to look for
+ * any change in IN2_P
+ */
+ usleep_range(10000, 10100);
+ output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P);
+
+ pr_debug("%s: Check for fake removal: output_mv %d\n",
+ __func__, output_mv);
+ if ((output_mv <= adc_threshold) &&
+ retry > FAKE_REM_RETRY_ATTEMPTS) {
+ pr_debug("%s: headset is NOT actually removed\n",
+ __func__);
+ goto exit;
+ }
+ } while (!time_after(jiffies, timeout));
+
+ if (wcd_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low ", __func__);
+ goto exit;
+ }
+
/*
* ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE,
* need to reject the ADC COMPLETE interrupt which follows ELEC_REM one
@@ -827,6 +938,7 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data)
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0);
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0);
wcd_mbhc_elec_hs_report_unplug(mbhc);
+exit:
WCD_MBHC_RSC_UNLOCK(mbhc);
pr_debug("%s: leave\n", __func__);
return IRQ_HANDLED;
diff --git a/sound/soc/codecs/wcd-mbhc-legacy.c b/sound/soc/codecs/wcd-mbhc-legacy.c
index ffba7f6..83023bc 100644
--- a/sound/soc/codecs/wcd-mbhc-legacy.c
+++ b/sound/soc/codecs/wcd-mbhc-legacy.c
@@ -445,7 +445,7 @@ static void wcd_correct_swch_plug(struct work_struct *work)
bool wrk_complete = false;
int pt_gnd_mic_swap_cnt = 0;
int no_gnd_mic_swap_cnt = 0;
- bool is_pa_on = false, spl_hs = false;
+ bool is_pa_on = false, spl_hs = false, spl_hs_reported = false;
bool micbias2 = false;
bool micbias1 = false;
int ret = 0;
@@ -652,6 +652,16 @@ static void wcd_correct_swch_plug(struct work_struct *work)
plug_type);
if (!(plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) {
plug_type = MBHC_PLUG_TYPE_HEADSET;
+ if (!spl_hs_reported &&
+ spl_hs_count == WCD_MBHC_SPL_HS_CNT) {
+ spl_hs_reported = true;
+ WCD_MBHC_RSC_LOCK(mbhc);
+ wcd_mbhc_find_plug_and_report(mbhc,
+ plug_type);
+ WCD_MBHC_RSC_UNLOCK(mbhc);
+ continue;
+ } else if (spl_hs_reported)
+ continue;
/*
* Report headset only if not already reported
* and if there is not button press without
@@ -726,6 +736,29 @@ static void wcd_correct_swch_plug(struct work_struct *work)
!mbhc->micbias_enable)
mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2,
MICB_DISABLE);
+
+ /*
+ * If plug type is corrected from special headset to headphone,
+ * clear the micbias enable flag, set micbias back to 1.8V and
+ * disable micbias.
+ */
+ if (plug_type == MBHC_PLUG_TYPE_HEADPHONE &&
+ mbhc->micbias_enable) {
+ if (mbhc->mbhc_cb->mbhc_micbias_control)
+ mbhc->mbhc_cb->mbhc_micbias_control(
+ codec, MIC_BIAS_2,
+ MICB_DISABLE);
+ if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
+ mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
+ codec,
+ MIC_BIAS_2, false);
+ if (mbhc->mbhc_cb->set_micbias_value) {
+ mbhc->mbhc_cb->set_micbias_value(codec);
+ WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0);
+ }
+ mbhc->micbias_enable = false;
+ }
+
if (mbhc->mbhc_cb->micbias_enable_status) {
micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
MIC_BIAS_1);
diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h
index dd3d35c..4ea4401 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.h
+++ b/sound/soc/codecs/wcd-mbhc-v2.h
@@ -150,7 +150,7 @@ do { \
#define WCD_MBHC_BTN_PRESS_COMPL_TIMEOUT_MS 50
#define ANC_DETECT_RETRY_CNT 7
-#define WCD_MBHC_SPL_HS_CNT 2
+#define WCD_MBHC_SPL_HS_CNT 1
enum wcd_mbhc_detect_logic {
WCD_DETECTION_LEGACY,
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
index 0a88537..0bfa688 100644
--- a/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -400,6 +400,7 @@ static int sst_acpi_remove(struct platform_device *pdev)
static unsigned long cht_machine_id;
#define CHT_SURFACE_MACH 1
+#define BYT_THINKPAD_10 2
static int cht_surface_quirk_cb(const struct dmi_system_id *id)
{
@@ -407,6 +408,23 @@ static int cht_surface_quirk_cb(const struct dmi_system_id *id)
return 1;
}
+static int byt_thinkpad10_quirk_cb(const struct dmi_system_id *id)
+{
+ cht_machine_id = BYT_THINKPAD_10;
+ return 1;
+}
+
+
+static const struct dmi_system_id byt_table[] = {
+ {
+ .callback = byt_thinkpad10_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "20C3001VHH"),
+ },
+ },
+ { }
+};
static const struct dmi_system_id cht_table[] = {
{
@@ -424,6 +442,10 @@ static struct sst_acpi_mach cht_surface_mach = {
"10EC5640", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
&chv_platform_data };
+static struct sst_acpi_mach byt_thinkpad_10 = {
+ "10EC5640", "cht-bsw-rt5672", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
+ &byt_rvp_platform_data };
+
static struct sst_acpi_mach *cht_quirk(void *arg)
{
struct sst_acpi_mach *mach = arg;
@@ -436,8 +458,21 @@ static struct sst_acpi_mach *cht_quirk(void *arg)
return mach;
}
+static struct sst_acpi_mach *byt_quirk(void *arg)
+{
+ struct sst_acpi_mach *mach = arg;
+
+ dmi_check_system(byt_table);
+
+ if (cht_machine_id == BYT_THINKPAD_10)
+ return &byt_thinkpad_10;
+ else
+ return mach;
+}
+
+
static struct sst_acpi_mach sst_acpi_bytcr[] = {
- {"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
+ {"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", byt_quirk,
&byt_rvp_platform_data },
{"10EC5642", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
&byt_rvp_platform_data },
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index bff77a1..4c8ff29 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -57,9 +57,7 @@ struct byt_rt5640_private {
struct clk *mclk;
};
-static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
- BYT_RT5640_DMIC_EN |
- BYT_RT5640_MCLK_EN;
+static unsigned long byt_rt5640_quirk = BYT_RT5640_MCLK_EN;
static void log_quirks(struct device *dev)
{
@@ -389,6 +387,16 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
BYT_RT5640_SSP0_AIF1),
},
+ {
+ .callback = byt_rt5640_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ },
+ .driver_data = (unsigned long *)(BYT_RT5640_IN3_MAP |
+ BYT_RT5640_MCLK_EN |
+ BYT_RT5640_SSP0_AIF1),
+
+ },
{}
};
@@ -738,6 +746,13 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
if (res_info->acpi_ipc_irq_index == 0) {
byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2;
}
+
+ /* change defaults for Baytrail-CR capture */
+ byt_rt5640_quirk |= BYT_RT5640_IN1_MAP;
+ byt_rt5640_quirk |= BYT_RT5640_DIFF_MIC;
+ } else {
+ byt_rt5640_quirk |= (BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_DMIC_EN);
}
/* check quirks before creating card */
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index 16c94c4..9052561 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -24,6 +24,9 @@
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <asm/cpu_device_id.h>
+#include <asm/platform_sst_audio.h>
+#include <linux/clk.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -45,6 +48,7 @@ struct cht_mc_private {
struct snd_soc_jack jack;
struct cht_acpi_card *acpi_card;
char codec_name[16];
+ struct clk *mclk;
};
static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
@@ -65,6 +69,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
struct snd_soc_dapm_context *dapm = w->dapm;
struct snd_soc_card *card = dapm->card;
struct snd_soc_dai *codec_dai;
+ struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
int ret;
codec_dai = cht_get_codec_dai(card);
@@ -73,19 +78,30 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
return -EIO;
}
- if (!SND_SOC_DAPM_EVENT_OFF(event))
- return 0;
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ if (ctx->mclk) {
+ ret = clk_prepare_enable(ctx->mclk);
+ if (ret < 0) {
+ dev_err(card->dev,
+ "could not configure MCLK state");
+ return ret;
+ }
+ }
+ } else {
+ /* Set codec sysclk source to its internal clock because codec PLL will
+ * be off when idle and MCLK will also be off when codec is
+ * runtime suspended. Codec needs clock for jack detection and button
+ * press. MCLK is turned off with clock framework or ACPI.
+ */
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK,
+ 48000 * 512, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
- /* Set codec sysclk source to its internal clock because codec PLL will
- * be off when idle and MCLK will also be off by ACPI when codec is
- * runtime suspended. Codec needs clock for jack detection and button
- * press.
- */
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK,
- 0, SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
- return ret;
+ if (ctx->mclk)
+ clk_disable_unprepare(ctx->mclk);
}
return 0;
@@ -97,7 +113,7 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Int Mic", NULL),
SND_SOC_DAPM_SPK("Ext Spk", NULL),
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
- platform_clock_control, SND_SOC_DAPM_POST_PMD),
+ platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
};
static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = {
@@ -225,6 +241,26 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
rt5645_set_jack_detect(codec, &ctx->jack, &ctx->jack, &ctx->jack);
+ if (ctx->mclk) {
+ /*
+ * The firmware might enable the clock at
+ * boot (this information may or may not
+ * be reflected in the enable clock register).
+ * To change the rate we must disable the clock
+ * first to cover these cases. Due to common
+ * clock framework restrictions that do not allow
+ * to disable a clock that has not been enabled,
+ * we need to enable the clock first.
+ */
+ ret = clk_prepare_enable(ctx->mclk);
+ if (!ret)
+ clk_disable_unprepare(ctx->mclk);
+
+ ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ);
+
+ if (ret)
+ dev_err(runtime->dev, "unable to set MCLK rate\n");
+ }
return ret;
}
@@ -349,6 +385,18 @@ static struct cht_acpi_card snd_soc_cards[] = {
static char cht_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
+static bool is_valleyview(void)
+{
+ static const struct x86_cpu_id cpu_ids[] = {
+ { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */
+ {}
+ };
+
+ if (!x86_match_cpu(cpu_ids))
+ return false;
+ return true;
+}
+
static int snd_cht_mc_probe(struct platform_device *pdev)
{
int ret_val = 0;
@@ -358,22 +406,32 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
struct sst_acpi_mach *mach;
const char *i2c_name = NULL;
int dai_index = 0;
+ bool found = false;
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
if (!drv)
return -ENOMEM;
+ mach = (&pdev->dev)->platform_data;
+
for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) {
- if (acpi_dev_found(snd_soc_cards[i].codec_id)) {
+ if (acpi_dev_found(snd_soc_cards[i].codec_id) &&
+ (!strncmp(snd_soc_cards[i].codec_id, mach->id, 8))) {
dev_dbg(&pdev->dev,
"found codec %s\n", snd_soc_cards[i].codec_id);
card = snd_soc_cards[i].soc_card;
drv->acpi_card = &snd_soc_cards[i];
+ found = true;
break;
}
}
+
+ if (!found) {
+ dev_err(&pdev->dev, "No matching HID found in supported list\n");
+ return -ENODEV;
+ }
+
card->dev = &pdev->dev;
- mach = card->dev->platform_data;
sprintf(drv->codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
/* set correct codec name */
@@ -391,6 +449,16 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
cht_dailink[dai_index].codec_name = cht_rt5640_codec_name;
}
+ if (is_valleyview()) {
+ drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
+ if (IS_ERR(drv->mclk)) {
+ dev_err(&pdev->dev,
+ "Failed to get MCLK from pmc_plt_clk_3: %ld\n",
+ PTR_ERR(drv->mclk));
+ return PTR_ERR(drv->mclk);
+ }
+ }
+
snd_soc_card_set_drvdata(card, drv);
ret_val = devm_snd_soc_register_card(&pdev->dev, card);
if (ret_val) {
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
index e39e642..826f566 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
@@ -394,12 +394,13 @@ static int msm_voice_sidetone_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int ret;
- bool sidetone_enable = ucontrol->value.integer.value[0];
+ long value = ucontrol->value.integer.value[0];
+ bool sidetone_enable = value;
uint32_t session_id = ALL_SESSION_VSID;
- if (sidetone_enable < 0) {
- pr_err("%s: Invalid arguments sidetone enable %d\n",
- __func__, sidetone_enable);
+ if (value < 0) {
+ pr_err("%s: Invalid arguments sidetone enable %ld\n",
+ __func__, value);
ret = -EINVAL;
return ret;
}
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index 687a8f8..15c9240 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -14,9 +14,11 @@
#include <linux/clk.h>
#include <linux/dmaengine.h>
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
+#include <linux/reset.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
@@ -92,6 +94,7 @@ struct sun4i_i2s {
struct clk *bus_clk;
struct clk *mod_clk;
struct regmap *regmap;
+ struct reset_control *rst;
struct snd_dmaengine_dai_dma_data playback_dma_data;
};
@@ -585,9 +588,22 @@ static int sun4i_i2s_runtime_suspend(struct device *dev)
return 0;
}
+struct sun4i_i2s_quirks {
+ bool has_reset;
+};
+
+static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
+ .has_reset = false,
+};
+
+static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
+ .has_reset = true,
+};
+
static int sun4i_i2s_probe(struct platform_device *pdev)
{
struct sun4i_i2s *i2s;
+ const struct sun4i_i2s_quirks *quirks;
struct resource *res;
void __iomem *regs;
int irq, ret;
@@ -608,6 +624,12 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
return irq;
}
+ quirks = of_device_get_match_data(&pdev->dev);
+ if (!quirks) {
+ dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
+ return -ENODEV;
+ }
+
i2s->bus_clk = devm_clk_get(&pdev->dev, "apb");
if (IS_ERR(i2s->bus_clk)) {
dev_err(&pdev->dev, "Can't get our bus clock\n");
@@ -626,7 +648,24 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Can't get our mod clock\n");
return PTR_ERR(i2s->mod_clk);
}
-
+
+ if (quirks->has_reset) {
+ i2s->rst = devm_reset_control_get(&pdev->dev, NULL);
+ if (IS_ERR(i2s->rst)) {
+ dev_err(&pdev->dev, "Failed to get reset control\n");
+ return PTR_ERR(i2s->rst);
+ }
+ }
+
+ if (!IS_ERR(i2s->rst)) {
+ ret = reset_control_deassert(i2s->rst);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to deassert the reset control\n");
+ return -EINVAL;
+ }
+ }
+
i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG;
i2s->playback_dma_data.maxburst = 4;
@@ -658,23 +697,37 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
sun4i_i2s_runtime_suspend(&pdev->dev);
err_pm_disable:
pm_runtime_disable(&pdev->dev);
+ if (!IS_ERR(i2s->rst))
+ reset_control_assert(i2s->rst);
return ret;
}
static int sun4i_i2s_remove(struct platform_device *pdev)
{
+ struct sun4i_i2s *i2s = dev_get_drvdata(&pdev->dev);
+
snd_dmaengine_pcm_unregister(&pdev->dev);
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
sun4i_i2s_runtime_suspend(&pdev->dev);
+ if (!IS_ERR(i2s->rst))
+ reset_control_assert(i2s->rst);
+
return 0;
}
static const struct of_device_id sun4i_i2s_match[] = {
- { .compatible = "allwinner,sun4i-a10-i2s", },
+ {
+ .compatible = "allwinner,sun4i-a10-i2s",
+ .data = &sun4i_a10_i2s_quirks,
+ },
+ {
+ .compatible = "allwinner,sun6i-a31-i2s",
+ .data = &sun6i_a31_i2s_quirks,
+ },
{}
};
MODULE_DEVICE_TABLE(of, sun4i_i2s_match);
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index db85d92..8279009 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -348,6 +348,16 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
alts = &iface->altsetting[1];
goto add_sync_ep;
+ case USB_ID(0x2466, 0x8003):
+ ep = 0x86;
+ iface = usb_ifnum_to_if(dev, 2);
+
+ if (!iface || iface->num_altsetting == 0)
+ return -EINVAL;
+
+ alts = &iface->altsetting[1];
+ goto add_sync_ep;
+
}
if (attr == USB_ENDPOINT_SYNC_ASYNC &&
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 93bb14e7..eb4b9f7 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1166,6 +1166,18 @@ static bool is_marantz_denon_dac(unsigned int id)
return false;
}
+/* TEAC UD-501/UD-503/NT-503 USB DACs need a vendor cmd to switch
+ * between PCM/DOP and native DSD mode
+ */
+static bool is_teac_50X_dac(unsigned int id)
+{
+ switch (id) {
+ case USB_ID(0x0644, 0x8043): /* TEAC UD-501/UD-503/NT-503 */
+ return true;
+ }
+ return false;
+}
+
int snd_usb_select_mode_quirk(struct snd_usb_substream *subs,
struct audioformat *fmt)
{
@@ -1193,6 +1205,26 @@ int snd_usb_select_mode_quirk(struct snd_usb_substream *subs,
break;
}
mdelay(20);
+ } else if (is_teac_50X_dac(subs->stream->chip->usb_id)) {
+ /* Vendor mode switch cmd is required. */
+ switch (fmt->altsetting) {
+ case 3: /* DSD mode (DSD_U32) requested */
+ err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0,
+ USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+ 1, 1, NULL, 0);
+ if (err < 0)
+ return err;
+ break;
+
+ case 2: /* PCM or DOP mode (S32) requested */
+ case 1: /* PCM mode (S16) requested */
+ err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0,
+ USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+ 0, 1, NULL, 0);
+ if (err < 0)
+ return err;
+ break;
+ }
}
return 0;
}
@@ -1338,5 +1370,11 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
}
+ /* TEAC devices with USB DAC functionality */
+ if (is_teac_50X_dac(chip->usb_id)) {
+ if (fp->altsetting == 3)
+ return SNDRV_PCM_FMTBIT_DSD_U32_BE;
+ }
+
return 0;
}