Merge "security: pfk: Error code was overwritten"
diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index 4987417..dfd56ec 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -350,3 +350,19 @@
Description: AArch64 CPU registers
'identification' directory exposes the CPU ID registers for
identifying model and revision of the CPU.
+
+What: /sys/devices/system/cpu/vulnerabilities
+ /sys/devices/system/cpu/vulnerabilities/meltdown
+ /sys/devices/system/cpu/vulnerabilities/spectre_v1
+ /sys/devices/system/cpu/vulnerabilities/spectre_v2
+Date: January 2018
+Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
+Description: Information about CPU vulnerabilities
+
+ The files are named after the code names of CPU
+ vulnerabilities. The output of those files reflects the
+ state of the CPUs in the system. Possible output values:
+
+ "Not affected" CPU is not affected by the vulnerability
+ "Vulnerable" CPU is affected and no mitigation in effect
+ "Mitigation: $M" CPU is affected and mitigation $M is in effect
diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt
index 9a9a6d0..3a96610 100644
--- a/Documentation/devicetree/bindings/arm/coresight.txt
+++ b/Documentation/devicetree/bindings/arm/coresight.txt
@@ -190,6 +190,20 @@
* reg-names: funnel-base-real: actual register space for the
duplicate funnel.
+* Optional properties for CSRs:
+
+ * qcom,usb-bam-support: boolean, indicates CSR has the ability to operate on
+ usb bam, include enable,disable and flush.
+
+ * qcom,hwctrl-set-support: boolean, indicates CSR has the ability to operate on
+ to "HWCTRL" register.
+
+ * qcom,set-byte-cntr-support:boolean, indicates CSR has the ability to operate on
+ to "BYTECNT" register.
+
+ * qcom,timestamp-support:boolean, indicates CSR support sys interface to read
+ timestamp value.
+
Example:
1. Sinks
diff --git a/Documentation/devicetree/bindings/arm/msm/clock-controller.txt b/Documentation/devicetree/bindings/arm/msm/clock-controller.txt
index 4cc49a59..83a1601 100644
--- a/Documentation/devicetree/bindings/arm/msm/clock-controller.txt
+++ b/Documentation/devicetree/bindings/arm/msm/clock-controller.txt
@@ -12,10 +12,14 @@
Required properties:
- compatible: Must be one of following,
"qcom,gcc-8953"
+ "qcom,gcc-sdm632"
"qcom,cc-debug-8953"
+ "qcom,cc-debug-sdm632"
"qcom,gcc-mdss-8953"
+ "qcom,gcc-mdss-sdm632"
"qcom,gcc-gfx-8953"
"qcom,gcc-gfx-sdm450"
+ "qcom,gcc-gfx-sdm632"
- reg: Pairs of physical base addresses and region sizes of
memory mapped registers.
diff --git a/Documentation/devicetree/bindings/arm/msm/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt
index 9bc8168..d150116 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm.txt
@@ -328,6 +328,9 @@
compatible = "qcom,sdm450-cdp"
compatible = "qcom,sdm450-qrd"
compatible = "qcom,sdm632-rumi"
+compatible = "qcom,sdm632-cdp"
+compatible = "qcom,sdm632-mtp"
+compatible = "qcom,sdm632-qrd"
compatible = "qcom,mdm9640-cdp"
compatible = "qcom,mdm9640-mtp"
compatible = "qcom,mdm9640-rumi"
diff --git a/Documentation/devicetree/bindings/fb/mdss-pll.txt b/Documentation/devicetree/bindings/fb/mdss-pll.txt
index d746a52..2c193c2 100644
--- a/Documentation/devicetree/bindings/fb/mdss-pll.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-pll.txt
@@ -1,8 +1,7 @@
Qualcomm MDSS pll for DSI/EDP/HDMI
-mdss-pll is a pll controller device which supports pll devices that
-are compatiable with MIPI display serial interface specification,
-HDMI and edp.
+mdss-pll is a pll controller device which supports pll devices that are
+compatiable with MIPI display serial interface specification, HDMI and edp.
Required properties:
- compatible: Compatible name used in the driver. Should be one of:
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
index b0c5b57..d9d3470 100644
--- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
@@ -51,6 +51,8 @@
device (see pinctrl binding [0]).
[0]: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
+- #thermal-sensor-cells : To register ADC sensors with of_thermal. Should be 1.
+ See ./thermal.txt for a description.
Client required property:
- qcom,<consumer name>-vadc : The phandle to the corresponding vadc device.
diff --git a/Documentation/devicetree/bindings/input/qpnp-power-on.txt b/Documentation/devicetree/bindings/input/qpnp-power-on.txt
index c2550e6..33d0236 100644
--- a/Documentation/devicetree/bindings/input/qpnp-power-on.txt
+++ b/Documentation/devicetree/bindings/input/qpnp-power-on.txt
@@ -84,7 +84,34 @@
case.
- qcom,kpdpwr-sw-debounce Boolean property to enable the debounce logic
on the KPDPWR_N rising edge.
-
+- qcom,resin-pon-reset Boolean property which indicates that resin
+ needs to be configured during reset in addition
+ to the primary PON device that is configured
+ for system reset through qcom,system-reset
+ property.
+- qcom,resin-warm-reset-type Poweroff type required to be configured on
+ RESIN reset control register when the system
+ goes for warm reset. If this property is not
+ specified, then the default type, warm reset
+ will be configured to RESIN reset control
+ register. This property is effective only if
+ qcom,resin-pon-reset is defined.
+- qcom,resin-hard-reset-type Same description as qcom,resin-warm-reset-type
+ but this applies for the system hard reset case.
+- qcom,resin-shutdown-type Same description as qcom,resin-warm-reset-type
+ but this applies for the system shutdown case.
+- qcom,resin-shutdown-disable Boolean property to disable RESIN POFF
+ trigger during system shutdown case.
+ This property is effective only if
+ qcom,resin-pon-reset is defined.
+- qcom,resin-hard-reset-disable Boolean property to disable RESIN POFF
+ trigger during system hard reset case.
+ This property is effective only if
+ qcom,resin-pon-reset is defined.
+- qcom,ps-hold-shutdown-disable Boolean property to disable PS_HOLD POFF
+ trigger during system shutdown case.
+- qcom,ps-hold-hard-reset-disable Boolean property to disable PS_HOLD
+ POFF trigger during system hard reset case.
All the below properties are in the sub-node section (properties of the child
node).
diff --git a/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt b/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt
new file mode 100644
index 0000000..c33daab
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt
@@ -0,0 +1,66 @@
+Synaptics DSXV26 touch controller
+
+Please add this description here: The Synaptics Touch controller is connected to the
+host processor via I2C. The controller generates interrupts when the user touches
+the panel. The host controller is expected to read the touch coordinates over I2C and
+pass the coordinates to the rest of the system.
+
+Required properties:
+
+ - compatible : should be "synaptics,dsx-i2c".
+ - reg : i2c slave address of the device.
+ - interrupt-parent : parent of interrupt.
+ - synaptics,irq-gpio : irq gpio.
+ - synaptics,reset-gpio : reset gpio.
+ - synaptics,power-gpio : power switch gpio.
+ - synaptics,irq-flags : irq flags.
+
+Optional property:
+ - vdd_ana-supply : digital voltage power supply needed to power device.
+ - vcc_i2c-supply : analog voltage power supply needed to power device.
+ - synaptics,pwr-reg-name : power reg name of digital voltage.
+ - synaptics,bus-reg-name : bus reg name of analog voltage.
+ - synaptics,irq-on-state : irq gpio active state.
+ - synaptics,reset-on-state : reset gpio active state.
+ - synaptics,power-on-state : power switch active state.
+ - synaptics,ub-i2c-addr : microbootloader mode I2C slave address.
+ - synaptics,cap-button-codes : virtual key code mappings to be used.
+ - synaptics,vir-button-codes : virtual key code and the response region on panel.
+ - synaptics,x-flip : modify orientation of the x axis.
+ - synaptics,y-flip : modify orientation of the y axis.
+ - synaptics,reset-delay-ms : reset delay for controller (ms), default 100.
+ - synaptics,reset-active-ms : reset active duration for controller (ms), default 100.
+ - synaptics,power-delay-ms : power delay for controller (ms), default 100.
+ - synaptics,max-y-for-2d : maximal y value of the panel.
+ - synaptics,swap-axes : specify whether to swap axes.
+ - synaptics,resume-in-workqueue : specify whether to defer the resume to workqueue.
+ - clock-names : Clock names used for secure touch. They are: "iface_clk", "core_clk"
+ - clocks : Defined if 'clock-names' DT property is defined. These clocks
+ are associated with the underlying I2C bus.
+
+Example:
+ i2c@78b7000 {
+ status = "ok";
+ synaptics@4b {
+ compatible = "synaptics,dsx-i2c";
+ reg = <0x4b>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <65 0x2008>;
+ vdd_ana-supply = <&pm8953_l17>;
+ vcc_i2c-supply = <&pm8953_l6>;
+ synaptics,pwr-reg-name = "vdd_ana";
+ synaptics,bus-reg-name = "vcc_i2c";
+ synaptics,irq-gpio = <&tlmm 65 0x2008>;
+ synaptics,irq-on-state = <0>;
+ synaptics,irq-flags = <0x2008>; /* IRQF_ONESHOT | IRQF_TRIGGER_LOW */
+ synaptics,power-delay-ms = <200>;
+ synaptics,reset-delay-ms = <200>;
+ synaptics,max-y-for-2d = <1919>; /* remove if no virtual buttons */
+ synaptics,cap-button-codes = <139 172 158>;
+ synaptics,vir-button-codes = <139 180 2000 320 160 172 540 2000 320 160 158 900 2000 320 160>;
+ /* Underlying clocks used by secure touch */
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>,
+ <&clock_gcc clk_gcc_blsp1_qup3_i2c_apps_clk>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/interrupt-controller/qti,pdc.txt b/Documentation/devicetree/bindings/interrupt-controller/qti,pdc.txt
index 07667a4..8d6fad0 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/qti,pdc.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/qti,pdc.txt
@@ -31,6 +31,7 @@
* "qcom,pdc-sdm845": For sdm845 pin data
* "qcom,pdc-sdm845-v2": For sdm845 v2 pin data
* "qcom,pdc-sdm670": For sdm670 pin data
+ * "qcom,pdc-sdxpoorwills": For sdxpoorwills pin data
- reg:
Usage: required
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-haptics.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-haptics.txt
index b7ce662..1a76d5d 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp-haptics.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp-haptics.txt
@@ -118,6 +118,18 @@
Following properties are specific only to LRA vibrators.
+- qcom,lra-auto-mode
+ Usage: optional
+ Value type: <empty>
+ Definition: If specified, a set of pre-configured settings will be applied
+ based on the pattern duration. For example, for a duration of
+ < 20 ms (short duration), one set of settings will be applied
+ and for a duration of >= 20 ms (long duration), another set of
+ settings will be applied. The parameters configured in the
+ driver when this property is specified is based on the LRA
+ tested internally. Those parameters should be fine-tuned or
+ adjusted based on the LRA used on different hardware platforms.
+
- qcom,lra-auto-res-mode
Usage: optional
Value type: <string>
@@ -200,9 +212,13 @@
- qcom,wave-samples
Usage: optional
Value type: <prop-encoded-array>
- Definition: Wave samples in an array of 8 elements. Each element takes the
+ Definition: Wave samples in an array of 32 elements. Each element takes the
following representation, bit 0: unused, bits[5:1] : amplitude,
bit 6: overdrive, bit 7: sign. Default sample value is 0x3E.
+ Since the hardware supports configuring upto 8 samples, a set
+ of 8 samples will be configured initially and the next set will
+ be configured upon the play interrupt until all the samples are
+ configured and played.
Following properties are applicable only when "qcom,play-mode" is set to
"pwm".
diff --git a/Documentation/devicetree/bindings/leds/leds-qti-tri-led.txt b/Documentation/devicetree/bindings/leds/leds-qti-tri-led.txt
new file mode 100644
index 0000000..c088d42
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-qti-tri-led.txt
@@ -0,0 +1,60 @@
+Qualcomm Technologies, Inc. TRI_LED driver specific bindings
+
+This binding document describes the properties of TRI_LED module in
+Qualcomm Technologies, Inc. PMIC chips.
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Must be "qcom,tri-led".
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Register base of the TRI_LED module and length.
+
+Properties for child nodes:
+- pwms:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: The PWM device (phandle) used for controlling LED.
+
+- led-sources:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: see Documentation/devicetree/bindings/leds/common.txt;
+ Device current output identifiers are: 0 - LED1_EN,
+ 1 - LED2_EN, 2 - LED3_EN.
+
+- label:
+ Usage: optional
+ Value type: <string>
+ Definition: see Documentation/devicetree/bindings/leds/common.txt;
+
+- linux,default-trigger:
+ Usage: optional
+ Value_type: <string>
+ Definition: see Documentation/devicetree/bindings/leds/common.txt;
+
+Example:
+
+ pmi8998_rgb: tri-led@d000{
+ compatible = "qcom,tri-led";
+ reg = <0xd000 0x100>;
+
+ red {
+ label = "red";
+ pwms = <&pmi8998_lpg 4 1000000>;
+ led-sources = <0>;
+ };
+ green {
+ label = "green";
+ pwms = <&pmi8998_lpg 3 1000000>;
+ led-sources = <1>;
+ };
+ blue {
+ label = "blue";
+ pwms = <&pmi8998_lpg 2 1000000>;
+ led-sources = <2>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
index 001f74f3..6c61ada 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
@@ -121,6 +121,10 @@
- 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.
+ - nvmem-cells: specifies the handle to represent the SoC revision.
+ usually it is defined by qfprom device node.
+ - nvmem-cell-names: specifies the given nvmem cell name as defined in
+ qfprom node.
Example:
diff --git a/Documentation/devicetree/bindings/pci/msm_pcie.txt b/Documentation/devicetree/bindings/pci/msm_pcie.txt
index 6af2bac..dfe5852 100644
--- a/Documentation/devicetree/bindings/pci/msm_pcie.txt
+++ b/Documentation/devicetree/bindings/pci/msm_pcie.txt
@@ -37,6 +37,7 @@
MSIs, virtual IRQ's (INT#), link state notifications.
- perst-gpio: PERST GPIO specified by PCIe spec.
- wake-gpio: WAKE GPIO specified by PCIe spec.
+ - phy-status-offset: Offset from PCIe PHY base to check if PCIe PHY is up.
- <supply-name>-supply: phandle to the regulator device tree node.
Refer to the schematics for the corresponding voltage regulators.
vreg-1.8-supply: phandle to the analog supply for the PCIe controller.
@@ -274,6 +275,7 @@
qcom,switch-latency = <100>;
qcom,wr-halt-size = <0xa>; /* 1KB */
qcom,slv-addr-space-size = <0x1000000>; /* 16MB */
+ qcom,phy-status-offset = <0x800>;
qcom,cpl-timeout = <0x2>;
iommus = <&anoc0_smmu>;
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,msm8937-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,msm8937-pinctrl.txt
new file mode 100644
index 0000000..f697704
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,msm8937-pinctrl.txt
@@ -0,0 +1,204 @@
+Qualcomm Technologies, Inc. MSM8937 TLMM block
+
+This binding describes the Top Level Mode Multiplexer block found in the
+MSM8937 platform.
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be "qcom,msm8937-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-gpio133,
+ sdc1_clk,
+ sdc1_cmd,
+ sdc1_data,
+ sdc1_rclk,
+ sdc2_clk,
+ sdc2_cmd,
+ sdc2_data,
+ qdsd_clk,
+ qdsd_cmd,
+ qdsd_data0,
+ qdsd_data1,
+ qdsd_data2,
+ qdsd_data3,
+
+- 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:
+ qdss_tracedata_b, blsp_uart1, gpio, blsp_spi1, adsp_ext, blsp_i2c1, prng_rosc,
+ qdss_cti_trig_out_b0, blsp_spi2, blsp_uart2, blsp_uart3, pbs0, pbs1,
+ pwr_modem_enabled_b, blsp_i2c3, gcc_gp2_clk_b, ldo_update,
+ atest_combodac_to_gpio_native, ldo_en, blsp_i2c2, gcc_gp1_clk_b, pbs2,
+ atest_gpsadc_dtest0_native, blsp_spi3, gcc_gp3_clk_b, blsp_spi4, blsp_uart4,
+ sec_mi2s, pwr_nav_enabled_b, codec_mad, pwr_crypto_enabled_b, blsp_i2c4,
+ blsp_spi5, blsp_uart5, qdss_traceclk_a, atest_bbrx1, m_voc,
+ qdss_cti_trig_in_a0, qdss_cti_trig_in_b0, blsp_i2c6, qdss_traceclk_b,
+ atest_wlan0, atest_wlan1, atest_bbrx0, blsp_i2c5, qdss_tracectl_a,
+ atest_gpsadc_dtest1_native, qdss_tracedata_a, blsp_spi6, blsp_uart6,
+ qdss_tracectl_b, mdp_vsync, pri_mi2s_mclk_a, sec_mi2s_mclk_a, cam_mclk,
+ cci_i2c, pwr_modem_enabled_a, cci_timer0, cci_timer1, cam1_standby,
+ pwr_nav_enabled_a, cam1_rst, pwr_crypto_enabled_a, forced_usb,
+ qdss_cti_trig_out_b1, cam2_rst, webcam_standby, cci_async, webcam_rst,
+ ov_ldo, sd_write, accel_int, gcc_gp1_clk_a, alsp_int, gcc_gp2_clk_a,
+ mag_int, gcc_gp3_clk_a, blsp6_spi, fp_int, qdss_cti_trig_in_b1, uim_batt,
+ cam2_standby, uim1_data, uim1_clk, uim1_reset, uim1_present, uim2_data,
+ uim2_clk, uim2_reset, uim2_present, sensor_rst, mipi_dsi0, smb_int,
+ cam0_ldo, us_euro, atest_char3, dbg_out, bimc_dte0, ts_resout, ts_sample,
+ sec_mi2s_mclk_b, pri_mi2s, sdcard_det, atest_char1, ebi_cdc, audio_reset,
+ atest_char0, audio_ref, cdc_pdm0, pri_mi2s_mclk_b, lpass_slimbus,
+ lpass_slimbus0, lpass_slimbus1, codec_int1, codec_int2, wcss_bt,
+ atest_char2, ebi_ch0, wcss_wlan2, wcss_wlan1, wcss_wlan0, wcss_wlan,
+ wcss_fm, ext_lpass, cri_trng, cri_trng1, cri_trng0, blsp_spi7, blsp_uart7,
+ pri_mi2s_ws, blsp_i2c7, gcc_tlmm, dmic0_clk, dmic0_data, key_volp,
+ qdss_cti_trig_in_a1, us_emitter, wsa_irq, wsa_io, wsa_reset, blsp_spi8,
+ blsp_uart8, blsp_i2c8, gcc_plltest, nav_pps_in_a, pa_indicator, modem_tsync,
+ nav_tsync, nav_pps_in_b, nav_pps, gsm0_tx, atest_char, atest_tsens,
+ bimc_dte1, ssbi_wtr1, fp_gpio, coex_uart, key_snapshot, key_focus, nfc_pwr,
+ blsp8_spi, qdss_cti_trig_out_a0, qdss_cti_trig_out_a1
+
+- 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@1000000 {
+ compatible = "qcom,msm8937-pinctrl";
+ reg = <0x1000000 0x300000>;
+ interrupts = <0 208 0>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ pmx-uartconsole {
+ uart_console_active: uart_console_active {
+ mux {
+ pins = "gpio4", "gpio5";
+ function = "blsp_uart2";
+ };
+
+ config {
+ pins = "gpio4", "gpio5";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+
+ uart_console_sleep: uart_console_sleep {
+ mux {
+ pins = "gpio4", "gpio5";
+ function = "blsp_uart2";
+ };
+
+ config {
+ pins = "gpio4", "gpio5";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+
+ };
+ };
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 f50fd88..75996a5 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
@@ -104,6 +104,13 @@
this property is not specified, then the default value used
will be 75mA.
+- qcom,fg-cutoff-current
+ Usage: optional
+ Value type: <u32>
+ Definition: Minimum Battery current (in mA) used for cutoff SOC
+ estimate. If this property is not specified, then a default
+ value of 500 mA will be applied.
+
- qcom,fg-delta-soc-thr
Usage: optional
Value type: <u32>
diff --git a/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
new file mode 100644
index 0000000..3174ccb
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
@@ -0,0 +1,36 @@
+Qualcomm Technologies, Inc. LPG driver specific bindings
+
+This binding document describes the properties of LPG (Light Pulse Generator)
+device module in Qualcomm Technologies, Inc. PMIC chips.
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Must be "qcom,pwm-lpg".
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Register base and length for LPG modules. The length
+ varies based on the number of channels available in
+ the PMIC chips.
+
+- reg-names:
+ Usage: required
+ Value type: <string>
+ Definition: The name of the register defined in the reg property.
+ It must be "lpg-base".
+
+- #pwm-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: See Documentation/devicetree/bindings/pwm/pwm.txt;
+
+Example:
+
+ pmi8998_lpg: lpg@b100 {
+ compatible = "qcom,pwm-lpg";
+ reg = <0xb100 0x600>;
+ reg-names = "lpg-base";
+ #pwm-cells = <2>;
+ };
diff --git a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
index fba7204..de40a7c 100644
--- a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
+++ b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
@@ -14,6 +14,7 @@
- qcom,fastrpc-glink: Flag to use glink instead of smd for IPC
- qcom,rpc-latency-us: FastRPC QoS latency vote
- qcom,adsp-remoteheap-vmid: FastRPC remote heap VMID list
+- qcom,fastrpc-adsp-audio-pdr: Flag to enable ADSP Audio PDR
Optional subnodes:
- qcom,msm_fastrpc_compute_cb : Child nodes representing the compute context
@@ -46,36 +47,39 @@
Legacy SMMU v1/v2:
Required properties:
-- compatible : Must be "qcom,msm-fastprc-legacy-compute-cb"
+- compatible : Must be "qcom,msm-fastrpc-legacy-compute"
Required subnode:
- qcom,msm_fastrpc_compute_cb : Child nodes representing the compute context
banks
Required subnode properties:
-- qcom,adsp-shared-phandle: phandle that describe the context bank handle
-- qcom,adsp-shared-sids : A list of SID associated with the context bank
-- qcom,virtual-addr-pool : Virtual address range that the context bank
- will be using
+- compatible : Must be "qcom,msm-fastrpc-legacy-compute-cb"
+- label : Label describing the channel this context bank belongs to
+- iommus : A list of phandle and IOMMU specifier pairs that describe the
+ IOMMU master interfaces of the device
+- sids : A list of SID associated with the context bank
Example:
- qcom,adsprpc_domains {
- compatible = "qcom,msm-fastrpc-legacy-compute-cb";
- qcom,msm_fastrpc_compute_cb {
- qcom,adsp-shared-phandle = <&adsp_shared>;
- qcom,adsp-shared-sids = <0x8 0x9>;
- qcom,virtual-addr-pool = <0x80000000 0x7FFFFFFF>;
- };
- };
-
+ qcom,msm_fastrpc {
+ compatible = "qcom,msm-fastrpc-legacy-compute";
+ qcom,msm_fastrpc_compute_cb {
+ compatible = "qcom,msm-fastrpc-legacy-compute-cb";
+ label = "adsprpc-smd";
+ iommus = <&apps_iommu 0x2408 0x7>;
+ sids = <0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf>;
+ };
+ };
Remote Heap:
Required properties:
- compatible : Must be "qcom,msm-adsprpc-mem-region"
- memory-region : CMA region which is owned by this device
+- restrict-access : Blocking vote for hyp_assign_phys function call
Example:
qcom,adsprpc-mem {
compatible = "qcom,msm-adsprpc-mem-region";
memory-region = <&adsp_mem>;
+ restrict-access;
};
diff --git a/Documentation/devicetree/bindings/regulator/cpr-regulator.txt b/Documentation/devicetree/bindings/regulator/cpr-regulator.txt
index 1c4dfbf..0f5e27a 100644
--- a/Documentation/devicetree/bindings/regulator/cpr-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/cpr-regulator.txt
@@ -709,19 +709,6 @@
The number of quadruples should be equal to the number of values specified in
the qcom,cpr-aging-sensor-id property. This property is required if
the qcom,cpr-aging-sensor-id property has been specified.
-- qcom,cpr-thermal-sensor-id: TSENS hardware sensor-id of the sensor which
- needs to be monitored.
-- qcom,cpr-disable-temp-threshold: The TSENS temperature threshold in degrees Celsius at which CPR
- closed-loop is disabled. CPR closed-loop will stay disabled as long as the
- temperature is below this threshold. This property is required
- only if 'qcom,cpr-thermal-sensor-id' is present.
-- qcom,cpr-enable-temp-threshold: The TSENS temperature threshold in degrees Celsius at which CPR
- closed-loop is enabled. CPR closed-loop will stay enabled above this
- temperature threshold. This property is required only if
- 'qcom,cpr-thermal-sensor-id' is present.
-- qcom,disable-closed-loop-in-pc: Bool property to disable closed-loop CPR during
- power-collapse. This can be enabled only for single core
- designs. The property 'qcom,cpr-cpus' is required to enable this logic.
Example:
apc_vreg_corner: regulator@f9018000 {
status = "okay";
@@ -971,8 +958,4 @@
qcom,cpr-fuse-aging-init-quot-diff =
<101 0 8 0>,
<101 8 8 0>;
-
- qcom,cpr-thermal-sensor-id = <9>;
- qcom,cpr-disable-temp-threshold = <5>;
- qcom,cpr-enable-temp-threshold = <10>;
};
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 34c2963..58c9bf8 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -293,6 +293,19 @@
- compatible : "qcom,msm-pcm-hostless"
+* audio-load-mod
+
+Required properties:
+
+ - compatible : "qcom,audio-load-mod"
+
+Optional properties:
+
+ - compatible : "qcom,audio-test-mod"
+ Add this compatible as child device to load-module device.
+ This child device is added after lpass is up to invoke
+ deferred probe devices.
+
* msm-ocmem-audio
Required properties:
@@ -355,6 +368,8 @@
- qcom,mclk-clk-reg: Indicate the register address for mclk.
+ - qcom,lpass-mclk-id: Property to update LPASS MCLK Id.
+
* audio_slimslave
Required properties:
@@ -640,6 +655,13 @@
compatible = "qcom,msm-pcm-hostless";
};
+ audio_load_mod {
+ compatible = "qcom,audio-load-mod";
+ audio_test_mod {
+ compatible = "qcom,audio-test-mod";
+ };
+ };
+
qcom,msm-ocmem-audio {
compatible = "qcom,msm-ocmem-audio";
qcom,msm_bus,name = "audio-ocmem";
diff --git a/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt b/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt
index 28ab2dd..97b71a7 100644
--- a/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt
+++ b/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt
@@ -61,6 +61,8 @@
- qcom,adc-tm-recalib-check: Add this property to check if recalibration required due to inaccuracy.
- hkadc_ldo-supply : Add this property if VADC needs to perform a Software Vote for the HKADC.
- hkadc_ok-supply : Add this property if the VADC needs to perform a Software vote for the HKADC VREG_OK.
+- #thermal-sensor-cells : To register ADC sensors with of_thermal. Should be 1.
+ See ./thermal.txt for a description.
Client required property:
- qcom,<consumer name>-adc_tm : The phandle to the corresponding adc_tm device.
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
index 8654a3e..e13a6a3 100644
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
@@ -1,110 +1,315 @@
MSM SoC HSUSB controllers
-EHCI
+OTG:
-Required properties:
-- compatible: Should contain "qcom,ehci-host"
-- regs: offset and length of the register set in the memory map
-- usb-phy: phandle for the PHY device
+Required properties :
+- compatible : should be "qcom,hsusb-otg"
+- regs : Array of offset and length of the register sets in the memory map
+- reg-names : indicates various iomem resources passed by name. The possible
+ strings in this field are:
+ "core": USB controller register space. (Required)
+ "tcsr": TCSR register for routing USB Controller signals to
+ either picoPHY0 or picoPHY1. (Optional)
+ "phy_csr": PHY Wrapper CSR register space. Provides register level
+ interface through AHB2PHY for performing PHY related
+ operations like retention and HV interrupts management.
+- interrupts: IRQ line
+- interrupt-names: OTG interrupt name(s) referenced in interrupts above
+ HSUSB OTG expects "core_irq" which is IRQ line from CORE and
+ "async_irq" from HSPHY for asynchronous wakeup events in LPM.
+ optional ones are described in next section.
+- qcom,hsusb-otg-phy-type: PHY type can be one of
+ 1 - Chipidea PHY (obsolete)
+ 2 - Synopsis Pico PHY
+ 3 - Synopsis Femto PHY
+ 4 - QUSB ULPI PHY
+- qcom,hsusb-otg-mode: Operational mode. Can be one of
+ 1 - Peripheral only mode
+ 2 - Host only mode
+ 3 - OTG mode
+ Based on the mode, OTG driver registers platform devices for
+ gadget and host.
+- qcom,hsusb-otg-otg-control: OTG control (VBUS and ID notifications)
+ can be one of
+ 1 - PHY control
+ 2 - PMIC control
+ 3 - User control (via debugfs)
+- <supply-name>-supply: handle to the regulator device tree node
+ Required "supply-name" is "HSUSB_VDDCX" (when voting for VDDCX) or
+ "hsusb_vdd_dig" (when voting for VDDCX Corner voltage),
+ "HSUSB_1p8-supply" and "HSUSB_3p3-supply".
+- qcom,vdd-voltage-level: This property must be a list of three integer
+ values (none, min, max) where each value represents either a voltage
+ in microvolts or a value corresponding to voltage corner. If usb core
+ supports svs, min value will have absolute SVS or SVS corner otherwise
+ min value will have absolute nominal or nominal corner.
+- clocks: a list of phandles to the USB clocks. Usage is as per
+ Documentation/devicetree/bindings/clock/clock-bindings.txt
+- clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
+ property.
-Example EHCI controller device node:
+ Required clocks:
+ "core_clk": USB core clock that is required for data transfers.
+ "iface_clk": USB core clock that is required for register access.
- ehci: ehci@f9a55000 {
- compatible = "qcom,ehci-host";
- reg = <0xf9a55000 0x400>;
- usb-phy = <&usb_otg>;
+ Optional clocks:
+ "sleep_clk": PHY sleep clock. Required for interrupts.
+ "phy_reset_clk": PHY blocks asynchronous reset clock. Required
+ for the USB block reset. It is a reset only clock.
+ "phy_por_clk": Reset only clock for asserting/de-asserting
+ PHY POR signal. Required for overriding PHY parameters.
+ "phy_csr_clk": Required for accessing PHY CSR registers through
+ AHB2PHY interface.
+ "phy_ref_clk": Required when PHY have referance clock,
+ "xo": XO clock. The source clock that is used as a reference clock
+ to the PHY.
+ "bimc_clk", "snoc_clk", "pcnoc_clk": bus voting clocks. Used to
+ keep buses at a nominal frequency during USB peripheral
+ mode for achieving max throughput.
+- qcom,max-nominal-sysclk-rate: Indicates maximum nominal frequency for which
+ system clock should be voted whenever streaming mode is enabled.
+- resets: reset specifier pair consists of phandle for the reset provider
+ and reset lines used by this controller.
+- reset-names: reset signal name strings sorted in the same order as the resets
+ property.
+
+Optional properties :
+- interrupt-names : Optional interrupt resource entries are:
+ "pmic_id_irq" : Interrupt from PMIC for external ID pin notification.
+ "phy_irq" : Interrupt from PHY. Used for ID detection.
+- qcom,hsusb-otg-disable-reset: If present then core is RESET only during
+ init, otherwise core is RESET for every cable disconnect as well
+- qcom,hsusb-otg-pnoc-errata-fix: If present then workaround for PNOC
+ performance issue is applied which requires changing the mem-type
+ attribute via VMIDMT.
+- qcom,hsusb-otg-default-mode: The default USB mode after boot-up.
+ Applicable only when OTG is controlled by user. Can be one of
+ 0 - None. Low power mode
+ 1 - Peripheral
+ 2 - Host
+- qcom,hsusb-otg-phy-init-seq: PHY configuration sequence. val, reg pairs
+ terminate with -1
+- qcom,hsusb-otg-power-budget: VBUS power budget in mA
+ 0 will be treated as 500mA
+- qcom,hsusb-otg-pclk-src-name: The source of pclk
+- Refer to "Documentation/devicetree/bindings/arm/msm/msm-bus.txt" for
+ below optional properties:
+ - qcom,msm-bus,name
+ - qcom,msm-bus,num_cases - There are three valid cases for this: NONE, MAX
+ and MIN bandwidth votes. Minimum two cases must be defined for
+ both NONE and MAX votes. If MIN vote is different from NONE VOTE
+ then specify third case for MIN VOTE. If explicit NOC clock rates
+ are not specified then MAX value should be large enough to get
+ desired BUS frequencies. In case explicit NOC clock rates are
+ specified, peripheral mode bus bandwidth vote should be defined
+ to vote for arbitrated bandwidth so that 60MHz frequency is met.
+
+ - qcom,msm-bus,num_paths
+ - qcom,msm-bus,vectors
+- qcom,hsusb-otg-lpm-on-dev-suspend: If present then USB enter to
+ low power mode upon receiving bus suspend.
+- qcom,hsusb-otg-clk-always-on-workaround: If present then USB core clocks
+ remain active upon receiving bus suspend and USB cable is connected.
+ Used for allowing USB to respond for remote wakup.
+- qcom,hsusb-otg-delay-lpm: If present then USB core will wait one second
+ after disconnect before entering low power mode.
+- <supply-name>-supply: handle to the regulator device tree node.
+ Optional "supply-name" is "vbus_otg" to supply vbus in host mode.
+- qcom,dp-manual-pullup: If present, vbus is not routed to USB controller/phy
+ and controller driver therefore enables pull-up explicitly before
+ starting controller using usbcmd run/stop bit.
+- qcom,usb2-enable-hsphy2: If present then USB2 controller is connected to 2nd
+ HSPHY.
+- qcom,hsusb-log2-itc: value of 2^(log2_itc-1) will be used as the
+ interrupt threshold (ITC), when log2_itc is between 1 to 7.
+- qcom,hsusb-l1-supported: If present, the device supports l1 (Link power
+ management).
+- qcom,no-selective-suspend: If present selective suspend is disabled on hub ports.
+- qcom,hsusb-otg-mpm-dpsehv-int: If present, indicates mpm interrupt to be
+ configured for detection of dp line transition during VDD minimization.
+- qcom,hsusb-otg-mpm-dmsehv-int: If present, indicates mpm interrupt to be
+ configured for detection of dm line transition during VDD minimization.
+- pinctrl-names : This should be defined if a target uses gpio and pinctrl framework.
+ See "pinctrl" in Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt.
+ It should specify the names of the configs that pinctrl can install in driver
+ Following are the pinctrl config that can be installed
+ "hsusb_active" : Active configuration of pins, this should specify active
+ config of vddmin gpio (if used) defined in their pin groups.
+ "hsusb_sleep" : Disabled configuration of pins, this should specify sleep
+ config of vddmin gpio (if used) defined in their pin groups.
+- qcom,hsusb-otg-vddmin-gpio = If present, indicates a gpio that will be used
+ to supply voltage to the D+ line during VDD minimization and peripheral
+ bus suspend. If not exists, then VDD minimization will not be allowed
+ during peripheral bus suspend.
+- qcom,ahb-async-bridge-bypass: If present, indicates that enable AHB2AHB By Pass
+ mode with device controller for better throughput. With this mode, USB Core
+ runs using PNOC clock and synchronous to it. Hence it is must to have proper
+ "qcom,msm-bus,vectors" to have high bus frequency. User shouldn't try to
+ enable this feature without proper bus voting. When this feature is enabled,
+ it is required to do HW reset during cable disconnect for host mode functionality
+ working and hence need to disable qcom,hsusb-otg-disable-reset. With this feature
+ enabled, USB HW has to vote for maximum PNOC frequency as USB HW cannot tolerate
+ changes in PNOC frequency which results in USB functionality failure.
+- qcom,disable-retention-with-vdd-min: If present don't allow phy retention but allow
+ vdd min.
+- qcom,usbin-vadc: Corresponding vadc device's phandle to read usbin voltage using VADC.
+ This will be used to get value of usb power supply's VOLTAGE_NOW property.
+- qcom,usbid-gpio: This corresponds to gpio which is used for USB ID detection.
+- qcom,hub-reset-gpio: This corresponds to gpio which is used for HUB reset.
+- qcom,sw-sel-gpio: This corresponds to gpio which is used for switch select routing
+ of D+/D- between the USB HUB and type B USB jack for peripheral mode.
+- qcom,bus-clk-rate: If present, indicates nominal bus frequency to be voted for
+ bimc/snoc/pcnoc clock with usb cable connected. If AHB2AHB bypass is enabled,
+ pcnoc value should be defined to very large number so that PNOC runs at max
+ frequency. If 'qcom,default-mode-svs' is also set then two set of frequencies
+ must be specified for SVS and NOM modes which user can change using sysfs node.
+- qcom,phy-dvdd-always-on: If present PHY DVDD is supplied by a always-on
+ regulator unlike vddcx/vddmx. PHY can keep D+ pull-up and D+/D-
+ pull-down resistors during peripheral and host bus suspend without
+ any re-work.
+- qcom,emulation: Indicates that we are running on emulation platform.
+- qcom,boost-sysclk-with-streaming: If present, enable controller specific
+ streaming feature. Also this flag can bump up usb system clock to max in streaming
+ mode. This flag enables streaming mode for all compositions and is different from
+ streaming-func property defined in android device node. Please refer Doumentation/
+ devicetree/bindings/usb/android-dev.txt for details about "streaming-func" property.
+- qcom,axi-prefetch-enable: If present, AXI64 interface will be used for transferring data
+ to/from DDR by controller.
+- qcom,enable-sdp-typec-current-limit: Indicates whether type-c current for SDP CHARGER to
+ be limited.
+- qcom,enable-phy-id-pullup: If present, PHY can keep D+ pull-up resistor on USB ID line
+ during cable disconnect.
+- qcom,max-svs-sysclk-rate: Indicates system clock frequency voted by driver in
+ non-perf mode. In perf mode driver uses qcom,max-nominal-sysclk-rate.
+- qcom,pm-qos-latency: This represents max tolerable CPU latency in microsecs,
+ which is used as a vote by driver to get max performance in perf mode.
+- qcom,default-mode-svs: Indicates USB system clock should run at SVS frequency.
+ User can bump it up using 'perf_mode' sysfs attribute for gadget.
+- qcom,vbus-low-as-hostmode: If present, specifies USB_VBUS to switch to host mode
+ if USB_VBUS is low or device mode if USB_VBUS is high.
+- qcom,usbeth-reset-gpio: If present then an external usb-to-eth is connected to
+ the USB host controller and its RESET_N signal is connected to this
+ usbeth-reset-gpio GPIO. It should be driven LOW to RESET the usb-to-eth.
+- extcon: phandles to external connector devices. First phandle should point to
+ external connector, which provide "USB" cable events, the second should
+ point to external connector device, which provide "USB-HOST" cable events.
+ A single phandle may be specified if a single connector device provides
+ both "USB" and "USB-HOST" events.
+
+Example HSUSB OTG controller device node :
+ usb@f9690000 {
+ compatible = "qcom,hsusb-otg";
+ reg = <0xf9690000 0x400>;
+ reg-names = "core";
+ interrupts = <134>;
+ interrupt-names = "core_irq";
+
+ qcom,hsusb-otg-phy-type = <2>;
+ qcom,hsusb-otg-mode = <1>;
+ qcom,hsusb-otg-otg-control = <1>;
+ qcom,hsusb-otg-disable-reset;
+ qcom,hsusb-otg-pnoc-errata-fix;
+ qcom,hsusb-otg-default-mode = <2>;
+ qcom,hsusb-otg-phy-init-seq = <0x01 0x90 0xffffffff>;
+ qcom,hsusb-otg-power-budget = <500>;
+ qcom,hsusb-otg-pclk-src-name = "dfab_usb_clk";
+ qcom,hsusb-otg-lpm-on-dev-suspend;
+ qcom,hsusb-otg-clk-always-on-workaround;
+ hsusb_vdd_dig-supply = <&pm8226_s1_corner>;
+ HSUSB_1p8-supply = <&pm8226_l10>;
+ HSUSB_3p3-supply = <&pm8226_l20>;
+ qcom,vdd-voltage-level = <1 5 7>;
+ qcom,dp-manual-pullup;
+ qcom,hsusb-otg-mpm-dpsehv-int = <49>;
+ qcom,hsusb-otg-mpm-dmsehv-int = <58>;
+ qcom,max-nominal-sysclk-rate = <133330000>;
+ qcom,max-svs-sysclk-rate = <100000000>;
+ qcom,pm-qos-latency = <59>;
+
+ qcom,msm-bus,name = "usb2";
+ qcom,msm-bus,num_cases = <2>;
+ qcom,msm-bus,num_paths = <1>;
+ qcom,msm-bus,vectors =
+ <87 512 0 0>,
+ <87 512 60000000 960000000>;
+ pinctrl-names = "hsusb_active","hsusb_sleep";
+ pinctrl-0 = <&vddmin_act>;
+ pinctrl-0 = <&vddmin_sus>;
+ qcom,hsusb-otg-vddmin-gpio = <&pm8019_mpps 6 0>;
+ qcom,disable-retention-with-vdd-min;
+ qcom,usbin-vadc = <&pm8226_vadc>;
+ qcom,usbid-gpio = <&msm_gpio 110 0>;
};
-USB PHY with optional OTG:
+MSM HSUSB EHCI controller
-Required properties:
-- compatible: Should contain:
- "qcom,usb-otg-ci" for chipsets with ChipIdea 45nm PHY
- "qcom,usb-otg-snps" for chipsets with Synopsys 28nm PHY
+Required properties :
+- compatible : should be "qcom,ehci-host"
+- reg : offset and length of the register set in the memory map
+- interrupts: IRQ lines used by this controller
+- interrupt-names : Required interrupt resource entries are:
+ HSUSB EHCI expects "core_irq" and optionally "async_irq".
+- <supply-name>-supply: handle to the regulator device tree node
+ Required "supply-name" is either "hsusb_vdd_dig" or "HSUSB_VDDCX"
+ "HSUSB_1p8-supply" "HSUSB_3p3-supply".
+- qcom,usb2-power-budget: maximum vbus power (in mA) that can be provided.
+- qcom,vdd-voltage-level: This property must be a list of five integer
+ values (no, 0.5vsuspend, 0.75suspend, min, max) where each value respresents
+ either a voltage in microvolts or a value corresponding to voltage corner.
+ First value represents value to vote when USB is not at all active, second
+ value represents value to vote when target is not connected to dock during low
+ power mode, third value represents vlaue to vote when target is connected to dock
+ and no peripheral connected over dock during low power mode, fourth value represents
+ minimum value to vote when USB is operational, fifth item represents maximum value
+ to vote for USB is operational.
-- regs: Offset and length of the register set in the memory map
-- interrupts: interrupt-specifier for the OTG interrupt.
+Optional properties :
+- qcom,usb2-enable-hsphy2: If present, select second PHY for USB operation.
+- pinctrl-names : This should be defined if a target uses pinctrl framework.
+ See "pinctrl" in Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt.
+ It should specify the names of the configs that pinctrl can install in driver
+ Following are the pinctrl configs that can be installed
+ "ehci_active" : Active configuration of pins, this should specify active
+ config defined in pin groups of used gpio's from resume and
+ ext-hub-reset.
+ "ehci_sleep" : Disabled configuration of pins, this should specify sleep
+ config defined in pin groups of used gpio's from resume and
+ ext-hub-reset.
+- qcom,resume-gpio: if present then peripheral connected to usb controller
+ cannot wakeup from XO shutdown using in-band usb bus resume. Use resume
+ gpio to wakeup peripheral.
+- qcom,ext-hub-reset-gpio: If present then an external HUB is connected to
+ the USB host controller and its RESET_N signal is connected to this
+ ext-hub-reset-gpio GPIO. It should be driven LOW to RESET the HUB.
+- qcom,usb2-enable-uicc: If present, usb2 port will be used for uicc card connection.
+- usb-phy: phandle for the PHY device, if described as a separate device tree node
+- qcom,pm-qos-latency: This property represents the maximum tolerable CPU latency in
+ microsecs, which is used as a vote to keep the CPUs in a high enough power state when
+ USB bus is in use (not suspended).
+- Refer to "Documentation/devicetree/bindings/arm/msm/msm-bus.txt" for
+ below optional properties:
+ - qcom,msm-bus,name
+ - qcom,msm-bus,num_cases - Two cases (NONE and MAX) for voting are supported.
+ - qcom,msm-bus,num_paths
+ - qcom,msm-bus,vectors
-- clocks: A list of phandle + clock-specifier pairs for the
- clocks listed in clock-names
-- clock-names: Should contain the following:
- "phy" USB PHY reference clock
- "core" Protocol engine clock
- "iface" Interface bus clock
- "alt_core" Protocol engine clock for targets with asynchronous
- reset methodology. (optional)
-
-- vdccx-supply: phandle to the regulator for the vdd supply for
- digital circuit operation.
-- v1p8-supply: phandle to the regulator for the 1.8V supply
-- v3p3-supply: phandle to the regulator for the 3.3V supply
-
-- resets: A list of phandle + reset-specifier pairs for the
- resets listed in reset-names
-- reset-names: Should contain the following:
- "phy" USB PHY controller reset
- "link" USB LINK controller reset
-
-- qcom,otg-control: OTG control (VBUS and ID notifications) can be one of
- 1 - PHY control
- 2 - PMIC control
-
-Optional properties:
-- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg"
-
-- switch-gpio: A phandle + gpio-specifier pair. Some boards are using Dual
- SPDT USB Switch, witch is cotrolled by GPIO to de/multiplex
- D+/D- USB lines between connectors.
-
-- qcom,phy-init-sequence: PHY configuration sequence values. This is related to Device
- Mode Eye Diagram test. Start address at which these values will be
- written is ULPI_EXT_VENDOR_SPECIFIC. Value of -1 is reserved as
- "do not overwrite default value at this address".
- For example: qcom,phy-init-sequence = < -1 0x63 >;
- Will update only value at address ULPI_EXT_VENDOR_SPECIFIC + 1.
-
-- qcom,phy-num: Select number of pyco-phy to use, can be one of
- 0 - PHY one, default
- 1 - Second PHY
- Some platforms may have configuration to allow USB
- controller work with any of the two HSPHYs present.
-
-- qcom,vdd-levels: This property must be a list of three integer values
- (no, min, max) where each value represents either a voltage
- in microvolts or a value corresponding to voltage corner.
-
-- qcom,manual-pullup: If present, vbus is not routed to USB controller/phy
- and controller driver therefore enables pull-up explicitly
- before starting controller using usbcmd run/stop bit.
-
-- extcon: phandles to external connector devices. First phandle
- should point to external connector, which provide "USB"
- cable events, the second should point to external connector
- device, which provide "USB-HOST" cable events. If one of
- the external connector devices is not required empty <0>
- phandle should be specified.
-
-Example HSUSB OTG controller device node:
-
- usb@f9a55000 {
- compatible = "qcom,usb-otg-snps";
- reg = <0xf9a55000 0x400>;
- interrupts = <0 134 0>;
- dr_mode = "peripheral";
-
- clocks = <&gcc GCC_XO_CLK>, <&gcc GCC_USB_HS_SYSTEM_CLK>,
- <&gcc GCC_USB_HS_AHB_CLK>;
-
- clock-names = "phy", "core", "iface";
-
- vddcx-supply = <&pm8841_s2_corner>;
- v1p8-supply = <&pm8941_l6>;
- v3p3-supply = <&pm8941_l24>;
-
- resets = <&gcc GCC_USB2A_PHY_BCR>, <&gcc GCC_USB_HS_BCR>;
- reset-names = "phy", "link";
-
- qcom,otg-control = <1>;
- qcom,phy-init-sequence = < -1 0x63 >;
- qcom,vdd-levels = <1 5 7>;
+Example MSM HSUSB EHCI controller device node :
+ ehci: qcom,ehci-host@f9a55000 {
+ compatible = "qcom,ehci-host";
+ reg = <0xf9a55000 0x400>;
+ interrupts = <0 134 0>, <0 140 0>;
+ interrupt-names = "core_irq", "async_irq";
+ /* If pinctrl is used and ext-hub-reset and resume gpio's are present*/
+ pinctrl-names = "ehci_active","ehci_sleep";
+ pinctrl-0 = <&ehci_reset_act &resume_act>;
+ pinctrl-1 = <&ehci_reset_sus &resume_sus>;
+ qcom,resume-gpio = <&msm_gpio 80 0>;
+ qcom,ext-hub-reset-gpio = <&msm_gpio 0 0>;
+ hsusb_vdd_dig-supply = <&pm8841_s2_corner>;
+ HSUSB_1p8-supply = <&pm8941_l6>;
+ HSUSB_3p3-supply = <&pm8941_l24>;
+ qcom,usb2-enable-hsphy2;
+ qcom,usb2-power-budget = <500>;
+ qcom,vdd-voltage-level = <1 2 3 5 7>;
+ qcom,usb2-enable-uicc;
};
diff --git a/Documentation/devicetree/bindings/usb/msm-phy.txt b/Documentation/devicetree/bindings/usb/msm-phy.txt
index f8c8a69..e94299f 100644
--- a/Documentation/devicetree/bindings/usb/msm-phy.txt
+++ b/Documentation/devicetree/bindings/usb/msm-phy.txt
@@ -101,6 +101,8 @@
- qcom,core-voltage-level: This property must be a list of three integer
values (no, min, max) where each value represents either a voltage in
microvolts or a value corresponding to voltage corner.
+ - "pcs_clamp_enable_reg" : Clamps the phy data inputs and enables USB3
+ autonomous mode.
Example:
ssphy0: ssphy@f9b38000 {
@@ -184,6 +186,10 @@
state when attached in host mode and "suspend" state when detached.
- qcom,tune2-efuse-correction: The value to be adjusted from fused value for
improved rise/fall times.
+ - nvmem-cells: specifies the handle to represent the SoC revision.
+ usually it is defined by qfprom device node.
+ - nvmem-cell-names: specifies the given nvmem cell name as defined in
+ qfprom node.
Example:
qusb_phy: qusb@f9b39000 {
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index a37e441..e996ba5 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -272,6 +272,7 @@
SUNW Sun Microsystems, Inc
swir Sierra Wireless
syna Synaptics Inc.
+synaptics Synaptics Inc.
synology Synology, Inc.
tbs TBS Technologies
tcg Trusted Computing Group
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 2b576cc..9f5bfd6 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -2707,6 +2707,11 @@
nosmt [KNL,S390] Disable symmetric multithreading (SMT).
Equivalent to smt=1.
+ nospectre_v2 [X86] Disable all mitigations for the Spectre variant 2
+ (indirect branch prediction) vulnerability. System may
+ allow data leaks with this option, which is equivalent
+ to spectre_v2=off.
+
noxsave [BUGS=X86] Disables x86 extended register state save
and restore using xsave. The kernel will fallback to
enabling legacy floating-point and sse state.
@@ -2811,6 +2816,8 @@
nopat [X86] Disable PAT (page attribute table extension of
pagetables) support.
+ nopcid [X86-64] Disable the PCID cpu feature.
+
norandmaps Don't use address space randomization. Equivalent to
echo 0 > /proc/sys/kernel/randomize_va_space
@@ -3339,6 +3346,21 @@
pt. [PARIDE]
See Documentation/blockdev/paride.txt.
+ pti= [X86_64] Control Page Table Isolation of user and
+ kernel address spaces. Disabling this feature
+ removes hardening, but improves performance of
+ system calls and interrupts.
+
+ on - unconditionally enable
+ off - unconditionally disable
+ auto - kernel detects whether your CPU model is
+ vulnerable to issues that PTI mitigates
+
+ Not specifying this option is equivalent to pti=auto.
+
+ nopti [X86_64]
+ Equivalent to pti=off
+
pty.legacy_count=
[KNL] Number of legacy pty's. Overwrites compiled-in
default number.
@@ -3943,6 +3965,29 @@
sonypi.*= [HW] Sony Programmable I/O Control Device driver
See Documentation/laptops/sonypi.txt
+ spectre_v2= [X86] Control mitigation of Spectre variant 2
+ (indirect branch speculation) vulnerability.
+
+ on - unconditionally enable
+ off - unconditionally disable
+ auto - kernel detects whether your CPU model is
+ vulnerable
+
+ Selecting 'on' will, and 'auto' may, choose a
+ mitigation method at run time according to the
+ CPU, the available microcode, the setting of the
+ CONFIG_RETPOLINE configuration option, and the
+ compiler with which the kernel was built.
+
+ Specific mitigations can also be selected manually:
+
+ retpoline - replace indirect branches
+ retpoline,generic - google's original retpoline
+ retpoline,amd - AMD-specific minimal thunk
+
+ Not specifying this option is equivalent to
+ spectre_v2=auto.
+
spia_io_base= [HW,MTD]
spia_fio_base=
spia_pedr=
diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt
index 95ccbe6..206c9b0 100644
--- a/Documentation/sysctl/vm.txt
+++ b/Documentation/sysctl/vm.txt
@@ -30,6 +30,7 @@
- dirty_writeback_centisecs
- drop_caches
- extfrag_threshold
+- extra_free_kbytes
- hugepages_treat_as_movable
- hugetlb_shm_group
- laptop_mode
@@ -240,6 +241,21 @@
==============================================================
+extra_free_kbytes
+
+This parameter tells the VM to keep extra free memory between the threshold
+where background reclaim (kswapd) kicks in, and the threshold where direct
+reclaim (by allocating processes) kicks in.
+
+This is useful for workloads that require low latency memory allocations
+and have a bounded burstiness in memory allocations, for example a
+realtime application that receives and transmits network traffic
+(causing in-kernel memory allocations) with a maximum total message burst
+size of 200MB may need 200MB of extra free memory to avoid direct reclaim
+related latencies.
+
+==============================================================
+
hugepages_treat_as_movable
This parameter controls whether we can allocate hugepages from ZONE_MOVABLE
diff --git a/Documentation/x86/pti.txt b/Documentation/x86/pti.txt
new file mode 100644
index 0000000..5cd5843
--- /dev/null
+++ b/Documentation/x86/pti.txt
@@ -0,0 +1,186 @@
+Overview
+========
+
+Page Table Isolation (pti, previously known as KAISER[1]) is a
+countermeasure against attacks on the shared user/kernel address
+space such as the "Meltdown" approach[2].
+
+To mitigate this class of attacks, we create an independent set of
+page tables for use only when running userspace applications. When
+the kernel is entered via syscalls, interrupts or exceptions, the
+page tables are switched to the full "kernel" copy. When the system
+switches back to user mode, the user copy is used again.
+
+The userspace page tables contain only a minimal amount of kernel
+data: only what is needed to enter/exit the kernel such as the
+entry/exit functions themselves and the interrupt descriptor table
+(IDT). There are a few strictly unnecessary things that get mapped
+such as the first C function when entering an interrupt (see
+comments in pti.c).
+
+This approach helps to ensure that side-channel attacks leveraging
+the paging structures do not function when PTI is enabled. It can be
+enabled by setting CONFIG_PAGE_TABLE_ISOLATION=y at compile time.
+Once enabled at compile-time, it can be disabled at boot with the
+'nopti' or 'pti=' kernel parameters (see kernel-parameters.txt).
+
+Page Table Management
+=====================
+
+When PTI is enabled, the kernel manages two sets of page tables.
+The first set is very similar to the single set which is present in
+kernels without PTI. This includes a complete mapping of userspace
+that the kernel can use for things like copy_to_user().
+
+Although _complete_, the user portion of the kernel page tables is
+crippled by setting the NX bit in the top level. This ensures
+that any missed kernel->user CR3 switch will immediately crash
+userspace upon executing its first instruction.
+
+The userspace page tables map only the kernel data needed to enter
+and exit the kernel. This data is entirely contained in the 'struct
+cpu_entry_area' structure which is placed in the fixmap which gives
+each CPU's copy of the area a compile-time-fixed virtual address.
+
+For new userspace mappings, the kernel makes the entries in its
+page tables like normal. The only difference is when the kernel
+makes entries in the top (PGD) level. In addition to setting the
+entry in the main kernel PGD, a copy of the entry is made in the
+userspace page tables' PGD.
+
+This sharing at the PGD level also inherently shares all the lower
+layers of the page tables. This leaves a single, shared set of
+userspace page tables to manage. One PTE to lock, one set of
+accessed bits, dirty bits, etc...
+
+Overhead
+========
+
+Protection against side-channel attacks is important. But,
+this protection comes at a cost:
+
+1. Increased Memory Use
+ a. Each process now needs an order-1 PGD instead of order-0.
+ (Consumes an additional 4k per process).
+ b. The 'cpu_entry_area' structure must be 2MB in size and 2MB
+ aligned so that it can be mapped by setting a single PMD
+ entry. This consumes nearly 2MB of RAM once the kernel
+ is decompressed, but no space in the kernel image itself.
+
+2. Runtime Cost
+ a. CR3 manipulation to switch between the page table copies
+ must be done at interrupt, syscall, and exception entry
+ and exit (it can be skipped when the kernel is interrupted,
+ though.) Moves to CR3 are on the order of a hundred
+ cycles, and are required at every entry and exit.
+ b. A "trampoline" must be used for SYSCALL entry. This
+ trampoline depends on a smaller set of resources than the
+ non-PTI SYSCALL entry code, so requires mapping fewer
+ things into the userspace page tables. The downside is
+ that stacks must be switched at entry time.
+ c. Global pages are disabled for all kernel structures not
+ mapped into both kernel and userspace page tables. This
+ feature of the MMU allows different processes to share TLB
+ entries mapping the kernel. Losing the feature means more
+ TLB misses after a context switch. The actual loss of
+ performance is very small, however, never exceeding 1%.
+ d. Process Context IDentifiers (PCID) is a CPU feature that
+ allows us to skip flushing the entire TLB when switching page
+ tables by setting a special bit in CR3 when the page tables
+ are changed. This makes switching the page tables (at context
+ switch, or kernel entry/exit) cheaper. But, on systems with
+ PCID support, the context switch code must flush both the user
+ and kernel entries out of the TLB. The user PCID TLB flush is
+ deferred until the exit to userspace, minimizing the cost.
+ See intel.com/sdm for the gory PCID/INVPCID details.
+ e. The userspace page tables must be populated for each new
+ process. Even without PTI, the shared kernel mappings
+ are created by copying top-level (PGD) entries into each
+ new process. But, with PTI, there are now *two* kernel
+ mappings: one in the kernel page tables that maps everything
+ and one for the entry/exit structures. At fork(), we need to
+ copy both.
+ f. In addition to the fork()-time copying, there must also
+ be an update to the userspace PGD any time a set_pgd() is done
+ on a PGD used to map userspace. This ensures that the kernel
+ and userspace copies always map the same userspace
+ memory.
+ g. On systems without PCID support, each CR3 write flushes
+ the entire TLB. That means that each syscall, interrupt
+ or exception flushes the TLB.
+ h. INVPCID is a TLB-flushing instruction which allows flushing
+ of TLB entries for non-current PCIDs. Some systems support
+ PCIDs, but do not support INVPCID. On these systems, addresses
+ can only be flushed from the TLB for the current PCID. When
+ flushing a kernel address, we need to flush all PCIDs, so a
+ single kernel address flush will require a TLB-flushing CR3
+ write upon the next use of every PCID.
+
+Possible Future Work
+====================
+1. We can be more careful about not actually writing to CR3
+ unless its value is actually changed.
+2. Allow PTI to be enabled/disabled at runtime in addition to the
+ boot-time switching.
+
+Testing
+========
+
+To test stability of PTI, the following test procedure is recommended,
+ideally doing all of these in parallel:
+
+1. Set CONFIG_DEBUG_ENTRY=y
+2. Run several copies of all of the tools/testing/selftests/x86/ tests
+ (excluding MPX and protection_keys) in a loop on multiple CPUs for
+ several minutes. These tests frequently uncover corner cases in the
+ kernel entry code. In general, old kernels might cause these tests
+ themselves to crash, but they should never crash the kernel.
+3. Run the 'perf' tool in a mode (top or record) that generates many
+ frequent performance monitoring non-maskable interrupts (see "NMI"
+ in /proc/interrupts). This exercises the NMI entry/exit code which
+ is known to trigger bugs in code paths that did not expect to be
+ interrupted, including nested NMIs. Using "-c" boosts the rate of
+ NMIs, and using two -c with separate counters encourages nested NMIs
+ and less deterministic behavior.
+
+ while true; do perf record -c 10000 -e instructions,cycles -a sleep 10; done
+
+4. Launch a KVM virtual machine.
+5. Run 32-bit binaries on systems supporting the SYSCALL instruction.
+ This has been a lightly-tested code path and needs extra scrutiny.
+
+Debugging
+=========
+
+Bugs in PTI cause a few different signatures of crashes
+that are worth noting here.
+
+ * Failures of the selftests/x86 code. Usually a bug in one of the
+ more obscure corners of entry_64.S
+ * Crashes in early boot, especially around CPU bringup. Bugs
+ in the trampoline code or mappings cause these.
+ * Crashes at the first interrupt. Caused by bugs in entry_64.S,
+ like screwing up a page table switch. Also caused by
+ incorrectly mapping the IRQ handler entry code.
+ * Crashes at the first NMI. The NMI code is separate from main
+ interrupt handlers and can have bugs that do not affect
+ normal interrupts. Also caused by incorrectly mapping NMI
+ code. NMIs that interrupt the entry code must be very
+ careful and can be the cause of crashes that show up when
+ running perf.
+ * Kernel crashes at the first exit to userspace. entry_64.S
+ bugs, or failing to map some of the exit code.
+ * Crashes at first interrupt that interrupts userspace. The paths
+ in entry_64.S that return to userspace are sometimes separate
+ from the ones that return to the kernel.
+ * Double faults: overflowing the kernel stack because of page
+ faults upon page faults. Caused by touching non-pti-mapped
+ data in the entry code, or forgetting to switch to kernel
+ CR3 before calling into C functions which are not pti-mapped.
+ * Userspace segfaults early in boot, sometimes manifesting
+ as mount(8) failing to mount the rootfs. These have
+ tended to be TLB invalidation issues. Usually invalidating
+ the wrong PCID, or otherwise missing an invalidation.
+
+1. https://gruss.cc/files/kaiser.pdf
+2. https://meltdownattack.com/meltdown.pdf
diff --git a/Makefile b/Makefile
index 323aa25..b075470 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 4
PATCHLEVEL = 9
-SUBLEVEL = 73
+SUBLEVEL = 80
EXTRAVERSION =
NAME = Roaring Lionus
@@ -792,6 +792,9 @@
# disable invalid "can't wrap" optimizations for signed / pointers
KBUILD_CFLAGS += $(call cc-option,-fno-strict-overflow)
+# Make sure -fstack-check isn't enabled (like gentoo apparently did)
+KBUILD_CFLAGS += $(call cc-option,-fno-stack-check,)
+
# conserve stack if available
KBUILD_CFLAGS += $(call cc-option,-fconserve-stack)
diff --git a/arch/arc/include/asm/uaccess.h b/arch/arc/include/asm/uaccess.h
index 41faf17..0684fd2 100644
--- a/arch/arc/include/asm/uaccess.h
+++ b/arch/arc/include/asm/uaccess.h
@@ -673,6 +673,7 @@
return 0;
__asm__ __volatile__(
+ " mov lp_count, %5 \n"
" lp 3f \n"
"1: ldb.ab %3, [%2, 1] \n"
" breq.d %3, 0, 3f \n"
@@ -689,8 +690,8 @@
" .word 1b, 4b \n"
" .previous \n"
: "+r"(res), "+r"(dst), "+r"(src), "=r"(val)
- : "g"(-EFAULT), "l"(count)
- : "memory");
+ : "g"(-EFAULT), "r"(count)
+ : "lp_count", "lp_start", "lp_end", "memory");
return res;
}
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index d8d8b82..41245ce 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -12,6 +12,7 @@
select ARCH_USE_BUILTIN_BSWAP
select ARCH_USE_CMPXCHG_LOCKREF
select ARCH_WANT_IPC_PARSE_VERSION
+ select ARM_PSCI_FW if PM
select BUILDTIME_EXTABLE_SORT if MMU
select CLONE_BACKWARDS
select CPU_PM if (SUSPEND || CPU_IDLE)
diff --git a/arch/arm/boot/dts/bcm-nsp.dtsi b/arch/arm/boot/dts/bcm-nsp.dtsi
index 7c9e0fa..65e0db1 100644
--- a/arch/arm/boot/dts/bcm-nsp.dtsi
+++ b/arch/arm/boot/dts/bcm-nsp.dtsi
@@ -85,7 +85,7 @@
timer@20200 {
compatible = "arm,cortex-a9-global-timer";
reg = <0x20200 0x100>;
- interrupts = <GIC_PPI 11 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_PPI 11 IRQ_TYPE_EDGE_RISING>;
clocks = <&periph_clk>;
};
@@ -93,7 +93,7 @@
compatible = "arm,cortex-a9-twd-timer";
reg = <0x20600 0x20>;
interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(2) |
- IRQ_TYPE_LEVEL_HIGH)>;
+ IRQ_TYPE_EDGE_RISING)>;
clocks = <&periph_clk>;
};
diff --git a/arch/arm/boot/dts/kirkwood-openblocks_a7.dts b/arch/arm/boot/dts/kirkwood-openblocks_a7.dts
index cf2f524..27cc913 100644
--- a/arch/arm/boot/dts/kirkwood-openblocks_a7.dts
+++ b/arch/arm/boot/dts/kirkwood-openblocks_a7.dts
@@ -53,7 +53,8 @@
};
pinctrl: pin-controller@10000 {
- pinctrl-0 = <&pmx_dip_switches &pmx_gpio_header>;
+ pinctrl-0 = <&pmx_dip_switches &pmx_gpio_header
+ &pmx_gpio_header_gpo>;
pinctrl-names = "default";
pmx_uart0: pmx-uart0 {
@@ -85,11 +86,16 @@
* ground.
*/
pmx_gpio_header: pmx-gpio-header {
- marvell,pins = "mpp17", "mpp7", "mpp29", "mpp28",
+ marvell,pins = "mpp17", "mpp29", "mpp28",
"mpp35", "mpp34", "mpp40";
marvell,function = "gpio";
};
+ pmx_gpio_header_gpo: pxm-gpio-header-gpo {
+ marvell,pins = "mpp7";
+ marvell,function = "gpo";
+ };
+
pmx_gpio_init: pmx-init {
marvell,pins = "mpp38";
marvell,function = "gpio";
diff --git a/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi b/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi
index 2106759..8f7edab 100644
--- a/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -97,6 +97,7 @@
qcom,adc-vdd-reference = <1875>;
pinctrl-names = "default";
pinctrl-0 = <&ambient_therm_default>;
+ #thermal-sensor-cells = <1>;
chan@6 {
label = "die_temp";
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-coresight.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-coresight.dtsi
new file mode 100644
index 0000000..eb5c210
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-coresight.dtsi
@@ -0,0 +1,1090 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ csr: csr@6001000 {
+ compatible = "qcom,coresight-csr";
+ reg = <0x6001000 0x1000>;
+ reg-names = "csr-base";
+
+ coresight-name = "coresight-csr";
+ qcom,usb-bam-support;
+ qcom,hwctrl-set-support;
+ qcom,set-byte-cntr-support;
+
+ qcom,blk-size = <1>;
+ };
+
+ swao_csr: csr@6b0e000 {
+ compatible = "qcom,coresight-csr";
+ reg = <0x6b0e000 0x1000>;
+ reg-names = "csr-base";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ coresight-name = "coresight-swao-csr";
+ qcom,timestamp-support;
+
+ qcom,blk-size = <1>;
+ };
+
+ tmc_etr: tmc@6048000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b961>;
+
+ reg = <0x6048000 0x1000>,
+ <0x6064000 0x15000>;
+ reg-names = "tmc-base", "bam-base";
+
+ arm,buffer-size = <0x400000>;
+ arm,sg-enable;
+
+ coresight-name = "coresight-tmc-etr";
+ coresight-ctis = <&cti0 &cti8>;
+ coresight-csr = <&csr>;
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ interrupts = <GIC_SPI 251 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "byte-cntr-irq";
+
+ port {
+ tmc_etr_in_replicator: endpoint {
+ slave-mode;
+ remote-endpoint = <&replicator_out_tmc_etr>;
+ };
+ };
+ };
+
+ replicator_qdss: replicator@6046000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b909>;
+
+ reg = <0x6046000 0x1000>;
+ reg-names = "replicator-base";
+
+ coresight-name = "coresight-replicator";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ replicator_out_tmc_etr: endpoint {
+ remote-endpoint=
+ <&tmc_etr_in_replicator>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ replicator_in_tmc_etf: endpoint {
+ slave-mode;
+ remote-endpoint=
+ <&tmc_etf_out_replicator>;
+ };
+ };
+ };
+ };
+
+ tmc_etf: tmc@6047000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b961>;
+
+ reg = <0x6047000 0x1000>;
+ reg-names = "tmc-base";
+
+ coresight-name = "coresight-tmc-etf";
+ coresight-ctis = <&cti0 &cti8>;
+ coresight-csr = <&csr>;
+ arm,default-sink;
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ tmc_etf_out_replicator: endpoint {
+ remote-endpoint =
+ <&replicator_in_tmc_etf>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ tmc_etf_in_funnel_merg: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_merg_out_tmc_etf>;
+ };
+ };
+ };
+ };
+
+ funnel_merg: funnel@6045000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
+ reg = <0x6045000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-merg";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ funnel_merg_out_tmc_etf: endpoint {
+ remote-endpoint =
+ <&tmc_etf_in_funnel_merg>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ funnel_merg_in_funnel_in0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_in0_out_funnel_merg>;
+ };
+ };
+
+ port@2 {
+ reg = <1>;
+ funnel_merg_in_funnel_in1: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_in1_out_funnel_merg>;
+ };
+ };
+ };
+ };
+
+ funnel_in0: funnel@6041000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
+ reg = <0x6041000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-in0";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ funnel_in0_out_funnel_merg: endpoint {
+ remote-endpoint =
+ <&funnel_merg_in_funnel_in0>;
+ };
+ };
+
+ port@1 {
+ reg = <6>;
+ funnel_in0_in_funnel_qatb: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_qatb_out_funnel_in0>;
+ };
+ };
+
+ port@2 {
+ reg = <7>;
+ funnel_in0_in_stm: endpoint {
+ slave-mode;
+ remote-endpoint = <&stm_out_funnel_in0>;
+ };
+ };
+ };
+ };
+
+ stm: stm@6002000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b962>;
+
+ reg = <0x6002000 0x1000>,
+ <0x16280000 0x180000>;
+ reg-names = "stm-base", "stm-stimulus-base";
+
+ coresight-name = "coresight-stm";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ port {
+ stm_out_funnel_in0: endpoint {
+ remote-endpoint = <&funnel_in0_in_stm>;
+ };
+ };
+
+ };
+
+ funnel_qatb: funnel@6005000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
+ reg = <0x6005000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-qatb";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ funnel_qatb_out_funnel_in0: endpoint {
+ remote-endpoint =
+ <&funnel_in0_in_funnel_qatb>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ funnel_qatb_in_tpda: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpda_out_funnel_qatb>;
+ };
+ };
+ };
+ };
+
+ tpda: tpda@6004000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b969>;
+ reg = <0x6004000 0x1000>;
+ reg-names = "tpda-base";
+
+ coresight-name = "coresight-tpda";
+
+ qcom,tpda-atid = <65>;
+ qcom,bc-elem-size = <10 32>,
+ <13 32>;
+ qcom,tc-elem-size = <13 32>;
+ qcom,dsb-elem-size = <0 32>,
+ <2 32>,
+ <3 32>,
+ <5 32>,
+ <6 32>,
+ <10 32>,
+ <11 32>,
+ <13 32>;
+ qcom,cmb-elem-size = <3 64>,
+ <7 64>,
+ <9 64>,
+ <13 64>;
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+ tpda_out_funnel_qatb: endpoint {
+ remote-endpoint =
+ <&funnel_qatb_in_tpda>;
+ };
+
+ };
+
+ port@1 {
+ reg = <0>;
+ tpda_in_funnel_ddr_0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_ddr_0_out_tpda>;
+ };
+ };
+
+ port@2 {
+ reg = <1>;
+ tpda_in_tpdm_vsense: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpdm_vsense_out_tpda>;
+ };
+ };
+
+ port@3 {
+ reg = <2>;
+ tpda_in_tpdm_dcc: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpdm_dcc_out_tpda>;
+ };
+ };
+
+ port@4 {
+ reg = <5>;
+ tpda_in_tpdm_center: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpdm_center_out_tpda>;
+ };
+ };
+ };
+ };
+
+ funnel_ddr_0: funnel@69e2000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
+ reg = <0x69e2000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-ddr-0";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ funnel_ddr_0_out_tpda: endpoint {
+ remote-endpoint =
+ <&tpda_in_funnel_ddr_0>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ funnel_ddr_0_in_tpdm_ddr: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpdm_ddr_out_funnel_ddr_0>;
+ };
+ };
+ };
+ };
+
+ tpdm_dcc: tpdm@6870280 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b968>;
+ reg = <0x6870280 0x1000>;
+ reg-names = "tpdm-base";
+
+ coresight-name = "coresight-tpdm-dcc";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ port{
+ tpdm_dcc_out_tpda: endpoint {
+ remote-endpoint = <&tpda_in_tpdm_dcc>;
+ };
+ };
+ };
+
+ tpdm_vsense: tpdm@6840000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b968>;
+ reg = <0x6840000 0x1000>;
+ reg-names = "tpdm-base";
+
+ coresight-name = "coresight-tpdm-vsense";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ port{
+ tpdm_vsense_out_tpda: endpoint {
+ remote-endpoint = <&tpda_in_tpdm_vsense>;
+ };
+ };
+ };
+
+ tpdm_center: tpdm@6c28000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b968>;
+ reg = <0x6c28000 0x1000>;
+ reg-names = "tpdm-base";
+
+ coresight-name = "coresight-tpdm-center";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ port{
+ tpdm_center_out_tpda: endpoint {
+ remote-endpoint = <&tpda_in_tpdm_center>;
+ };
+ };
+ };
+
+ tpdm_ddr: tpdm@69e0000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b968>;
+ reg = <0x69e0000 0x1000>;
+ reg-names = "tpdm-base";
+
+ coresight-name = "coresight-tpdm-ddr";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ qcom,msr-fix-req;
+
+ port {
+ tpdm_ddr_out_funnel_ddr_0: endpoint {
+ remote-endpoint = <&funnel_ddr_0_in_tpdm_ddr>;
+ };
+ };
+ };
+
+ funnel_in1: funnel@6042000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
+ reg = <0x6042000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-in1";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ funnel_in1_out_funnel_merg: endpoint {
+ remote-endpoint =
+ <&funnel_merg_in_funnel_in1>;
+ };
+ };
+
+ port@1 {
+ reg = <2>;
+ funnel_in1_in_funnel_swao: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&funnel_swao_out_funnel_in1>;
+ };
+ };
+
+ port@2 {
+ reg = <3>;
+ funnel_in1_in_modem_etm0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&modem_etm0_out_funnel_in1>;
+ };
+ };
+
+ port@3 {
+ reg = <7>;
+ funnel_in1_in_tpda_modem: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpda_modem_out_funnel_in1>;
+ };
+ };
+ };
+ };
+
+ modem_etm0 {
+ compatible = "qcom,coresight-remote-etm";
+
+ coresight-name = "coresight-modem-etm0";
+ qcom,inst-id = <2>;
+
+ port {
+ modem_etm0_out_funnel_in1: endpoint {
+ remote-endpoint =
+ <&funnel_in1_in_modem_etm0>;
+ };
+ };
+ };
+
+ funnel_swao:funnel@6b08000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b908>;
+
+ reg = <0x6b08000 0x1000>;
+ reg-names = "funnel-base";
+
+ coresight-name = "coresight-funnel-swao";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ funnel_swao_out_funnel_in1: endpoint {
+ remote-endpoint =
+ <&funnel_in1_in_funnel_swao>;
+ };
+ };
+
+ port@1 {
+ reg = <7>;
+ funnel_swao_in_tpda_swao: endpoint {
+ slave-mode;
+ remote-endpoint=
+ <&tpda_swao_out_funnel_swao>;
+ };
+ };
+ };
+ };
+
+ tpda_modem: tpda@6832000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b969>;
+ reg = <0x6832000 0x1000>;
+ reg-names = "tpda-base";
+
+ coresight-name = "coresight-tpda-modem";
+
+ qcom,tpda-atid = <67>;
+ qcom,dsb-elem-size = <0 32>;
+ qcom,cmb-elem-size = <0 64>;
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+ tpda_modem_out_funnel_in1: endpoint {
+ remote-endpoint =
+ <&funnel_in1_in_tpda_modem>;
+ };
+ };
+
+ port@1 {
+ reg = <0>;
+ tpda_modem_in_tpdm_modem: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpdm_modem_out_tpda_modem>;
+ };
+ };
+ };
+ };
+
+ tpdm_modem: tpdm@6830000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b968>;
+ reg = <0x6830000 0x1000>;
+ reg-names = "tpdm-base";
+
+ coresight-name = "coresight-tpdm-modem";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ port {
+ tpdm_modem_out_tpda_modem: endpoint {
+ remote-endpoint = <&tpda_modem_in_tpdm_modem>;
+ };
+ };
+ };
+
+ tpda_swao: tpda@6b01000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b969>;
+ reg = <0x6b01000 0x1000>;
+ reg-names = "tpda-base";
+
+ coresight-name = "coresight-tpda-swao";
+
+ qcom,tpda-atid = <71>;
+ qcom,dsb-elem-size = <1 32>;
+ qcom,cmb-elem-size = <0 64>;
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ tpda_swao_out_funnel_swao: endpoint {
+ remote-endpoint =
+ <&funnel_swao_in_tpda_swao>;
+ };
+
+ };
+
+ port@1 {
+ reg = <0>;
+ tpda_swao_in_tpdm_swao0: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpdm_swao0_out_tpda_swao>;
+ };
+ };
+
+ port@2 {
+ reg = <1>;
+ tpda_swao_in_tpdm_swao1: endpoint {
+ slave-mode;
+ remote-endpoint =
+ <&tpdm_swao1_out_tpda_swao>;
+ };
+
+ };
+ };
+ };
+
+ tpdm_swao0: tpdm@6b02000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b968>;
+
+ reg = <0x6b02000 0x1000>;
+ reg-names = "tpdm-base";
+
+ coresight-name = "coresight-tpdm-swao-0";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ port {
+ tpdm_swao0_out_tpda_swao: endpoint {
+ remote-endpoint = <&tpda_swao_in_tpdm_swao0>;
+ };
+ };
+ };
+
+ tpdm_swao1: tpdm@6b03000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b968>;
+ reg = <0x6b03000 0x1000>;
+ reg-names = "tpdm-base";
+
+ coresight-name="coresight-tpdm-swao-1";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ qcom,msr-fix-req;
+
+ port {
+ tpdm_swao1_out_tpda_swao: endpoint {
+ remote-endpoint = <&tpda_swao_in_tpdm_swao1>;
+ };
+ };
+ };
+
+ ipcb_tgu: tgu@6b0c000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b999>;
+ reg = <0x6b0c000 0x1000>;
+ reg-names = "tgu-base";
+ tgu-steps = <3>;
+ tgu-conditions = <4>;
+ tgu-regs = <4>;
+ tgu-timer-counters = <8>;
+
+ coresight-name = "coresight-tgu-ipcb";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+ };
+
+ cti0: cti@6010000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x6010000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti0";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ };
+
+ cti1: cti@6011000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x6011000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti1";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ };
+
+ cti2: cti@6012000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x6012000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti2";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+ };
+
+ cti3: cti@6013000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x6013000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti3";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ };
+
+ cti4: cti@6014000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x6014000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti4";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ };
+
+ cti5: cti@6015000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x6015000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti5";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ };
+
+ cti6: cti@6016000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x6016000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti6";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ };
+
+ cti7: cti@6017000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x6017000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti7";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ };
+
+ cti8: cti@6018000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x6018000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti8";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ };
+
+ cti9: cti@6019000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x6019000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti9";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ };
+
+ cti10: cti@601a000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x601a000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti10";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ };
+
+ cti11: cti@601b000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x601b000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti11";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ };
+
+ cti12: cti@601c000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x601c000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti12";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ };
+
+ cti13: cti@601d000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x601d000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti13";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ };
+
+ cti14: cti@601e000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x601e000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti14";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ };
+
+ cti15: cti@601f000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x601f000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti15";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ };
+
+ cti_cpu0: cti@7003000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x7003000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-cpu0";
+ cpu = <&CPU0>;
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ };
+
+ cti_modem_cpu0:cti@6837000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x6837000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-modem-cpu0";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+ };
+
+ cti_modem_cpu1:cti@683b000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x683b000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-modem-cpu1";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+ };
+
+ cti0_swao:cti@6b04000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x6b04000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-swao_cti0";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+ };
+
+ cti1_swao:cti@6b05000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x6b05000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-swao_cti1";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+ };
+
+ cti2_swao:cti@6b06000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x6b06000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-swao_cti2";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+ };
+
+ cti3_swao:cti@6b07000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x6b07000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-swao_cti3";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+ };
+
+ cti0_ddr0: cti@69e1000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x69e1000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-ddr_dl_0_cti";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+ };
+
+ cti0_ddr1: cti@69e4000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x69e4000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-ddr_dl_1_cti0";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+ };
+
+ cti1_ddr1: cti@69e5000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x69e5000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-ddr_dl_1_cti1";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+ };
+
+ cti2_ddr1: cti@69e6000 {
+ compatible = "arm,primecell";
+ arm,primecell-periphid = <0x0003b966>;
+ reg = <0x69e6000 0x1000>;
+ reg-names = "cti-base";
+
+ coresight-name = "coresight-cti-ddr_dl_1_cti2";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+ };
+
+ hwevent: hwevent@0x014066f0 {
+ compatible = "qcom,coresight-hwevent";
+ reg = <0x14066f0 0x4>,
+ <0x14166f0 0x4>,
+ <0x1406038 0x4>,
+ <0x1416038 0x4>;
+ reg-names = "ddr-ch0-cfg", "ddr-ch23-cfg", "ddr-ch0-ctrl",
+ "ddr-ch23-ctrl";
+
+ coresight-name = "coresight-hwevent";
+ coresight-csr = <&csr>;
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-mtp-audio-overlay.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp-audio-overlay.dtsi
new file mode 100644
index 0000000..5b3e0b5
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp-audio-overlay.dtsi
@@ -0,0 +1,22 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdxpoorwills-audio-overlay.dtsi"
+
+&soc {
+ sound-tavil {
+ qcom,wsa-max-devs = <1>;
+ qcom,wsa-devs = <&wsa881x_0214>;
+ qcom,wsa-aux-dev-prefix = "SpkrRight";
+ };
+};
+
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts
index 575febe..cba6262 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -15,6 +15,7 @@
#include "sdxpoorwills.dtsi"
#include "sdxpoorwills-pinctrl.dtsi"
+#include "sdxpoorwills-mtp-audio-overlay.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDXPOORWILLS MTP";
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie.dtsi
index 0b94534..e939bd2 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie.dtsi
@@ -139,6 +139,8 @@
qcom,slv-addr-space-size = <0x40000000>;
+ qcom,phy-status-offset = <0x814>;
+
qcom,cpl-timeout = <0x2>;
qcom,boot-option = <0x1>;
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi
index 1e212b7..deed94d 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi
@@ -13,11 +13,13 @@
&soc {
tlmm: pinctrl@3900000 {
compatible = "qcom,sdxpoorwills-pinctrl";
- reg = <0x3900000 0x300000>;
+ reg = <0x3900000 0x300000>,
+ <0xB204900 0x280>;
interrupts = <0 212 0>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
+ interrupt-parent = <&pdc>;
#interrupt-cells = <2>;
uart2_console_active: uart2_console_active {
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pm.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pm.dtsi
new file mode 100644
index 0000000..eab887c
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pm.dtsi
@@ -0,0 +1,95 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+&soc {
+
+ qcom,lpm-levels {
+ compatible = "qcom,lpm-levels";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,pm-cluster@0{
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ label = "system";
+ qcom,psci-mode-shift = <0>;
+ qcom,psci-mode-mask = <0xf>;
+
+ qcom,pm-cluster-level@0 {
+ reg = <0>;
+ label = "cx_active";
+ qcom,psci-mode = <0x0>;
+ qcom,latency-us = <270>;
+ qcom,ss-power = <455>;
+ qcom,energy-overhead = <270621>;
+ qcom,time-overhead = <500>;
+ };
+
+ qcom,pm-cluster-level@1 {
+ reg = <1>;
+ label = "cx_min";
+ qcom,psci-mode = <0x0>;
+ qcom,latency-us = <285>;
+ qcom,ss-power = <442>;
+ qcom,energy-overhead = <306621>;
+ qcom,time-overhead = <540>;
+ qcom,min-child-idx = <2>;
+ qcom,notify-rpm;
+ qcom,is-reset;
+ };
+
+ qcom,pm-cpu@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ qcom,psci-mode-shift = <0>;
+ qcom,psci-mode-mask = <0xf>;
+ qcom,cpu = <&CPU0>;
+
+ qcom,pm-cpu-level@0{
+ reg = <0>;
+ label = "wfi";
+ qcom,psci-cpu-mode = <0x1>;
+ qcom,latency-us = <1>;
+ qcom,ss-power = <473>;
+ qcom,energy-overhead = <100000>;
+ qcom,time-overhead = <25>;
+ };
+
+ qcom,pm-cpu-level@1 {
+ reg = <1>;
+ label ="standalone_pc";
+ qcom,psci-cpu-mode = <0x4>;
+ qcom,latency-us = <240>;
+ qcom,ss-power = <467>;
+ qcom,energy-overhead = <202781>;
+ qcom,time-overhead = <420>;
+ qcom,use-broadcast-timer;
+ qcom,is-reset;
+ };
+
+ qcom,pm-cpu-level@2 {
+ reg = <2>;
+ label = "system-pc";
+ qcom,psci-cpu-mode = <0x8>;
+ qcom,latency-us = <270>;
+ qcom,ss-power = <455>;
+ qcom,energy-overhead = <270621>;
+ qcom,time-overhead = <500>;
+ qcom,use-broadcast-timer;
+ qcom,is-reset;
+ };
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-usb.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-usb.dtsi
index 77e1763..16b057d 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-usb.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-usb.dtsi
@@ -139,9 +139,11 @@
usb3_qmp_phy: ssphy@ff0000 {
compatible = "qcom,usb-ssphy-qmp-v2";
reg = <0xff0000 0x1000>,
- <0x01fcb244 0x4>;
+ <0x01fcb244 0x4>,
+ <0x00ff088c 0x4>;
reg-names = "qmp_phy_base",
- "vls_clamp_reg";
+ "vls_clamp_reg",
+ "pcs_clamp_enable_reg";
vdd-supply = <&pmxpoorwills_l4>;
core-supply = <&pmxpoorwills_l1>;
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index 7d5ee77..2755df2 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -17,12 +17,13 @@
#include <dt-bindings/clock/qcom,gcc-sdxpoorwills.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
+#include <dt-bindings/clock/qcom,aop-qmp.h>
/ {
model = "Qualcomm Technologies, Inc. SDX POORWILLS";
compatible = "qcom,sdxpoorwills";
qcom,msm-id = <334 0x0>, <335 0x0>;
- interrupt-parent = <&intc>;
+ interrupt-parent = <&pdc>;
reserved-memory {
#address-cells = <1>;
@@ -69,6 +70,7 @@
CPU0: cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a7";
+ enable-method = "psci";
reg = <0x0>;
#cooling-cells = <2>;
};
@@ -80,6 +82,11 @@
sdhc1 = &sdhc_1; /* SDC1 eMMC/SD/SDIO slot */
};
+ psci {
+ compatible = "arm,psci-1.0";
+ method = "smc";
+ };
+
soc: soc { };
};
@@ -95,6 +102,15 @@
#interrupt-cells = <3>;
reg = <0x17800000 0x1000>,
<0x17802000 0x1000>;
+ interrupt-parent = <&intc>;
+ };
+
+ pdc: interrupt-controller@b210000{
+ compatible = "qcom,pdc-sdxpoorwills";
+ reg = <0xb210000 0x30000>;
+ #interrupt-cells = <3>;
+ interrupt-parent = <&intc>;
+ interrupt-controller;
};
timer {
@@ -223,6 +239,13 @@
mbox-names = "apps";
};
+ clock_aop: qcom,aopclk {
+ compatible = "qcom,aop-qmp-clk-v1";
+ #clock-cells = <1>;
+ mboxes = <&qmp_aop 0>;
+ mbox-names = "qdss_clk";
+ };
+
snoc_cnoc_keepalive: qcom,snoc_cnoc_keepalive {
compatible = "qcom,devbw";
governor = "powersave";
@@ -583,6 +606,8 @@
qcom,mhi-event-ring-id-limits = <9 10>; /* start and end */
qcom,modem-cfg-emb-pipe-flt;
qcom,use-ipa-pm;
+ qcom,arm-smmu;
+ qcom,smmu-fast-map;
qcom,bandwidth-vote-for-ipa;
qcom,msm-bus,name = "ipa";
qcom,msm-bus,num-cases = <5>;
@@ -721,6 +746,29 @@
compatible = "qcom,smp2pgpio-map-ipa-1-in";
gpios = <&smp2pgpio_ipa_1_in 0 0>;
};
+
+ ipa_smmu_ap: ipa_smmu_ap {
+ compatible = "qcom,ipa-smmu-ap-cb";
+ iommus = <&apps_smmu 0x5E0 0x0>;
+ qcom,iova-mapping = <0x20000000 0x40000000>;
+ qcom,additional-mapping =
+ /* modem tables in IMEM */
+ <0x14686000 0x14686000 0x3000>;
+ };
+
+ ipa_smmu_wlan: ipa_smmu_wlan {
+ compatible = "qcom,ipa-smmu-wlan-cb";
+ iommus = <&apps_smmu 0x5E1 0x0>;
+ qcom,additional-mapping =
+ /* ipa-uc ram */
+ <0x1E60000 0x1E60000 0xA000>;
+ };
+
+ ipa_smmu_uc: ipa_smmu_uc {
+ compatible = "qcom,ipa-smmu-uc-cb";
+ iommus = <&apps_smmu 0x5E2 0x0>;
+ qcom,iova-mapping = <0x40000000 0x20000000>;
+ };
};
qmp_aop: qcom,qmp-aop@c300000 {
@@ -743,6 +791,15 @@
interrupt-names = "vbus_det_irq";
status = "disabled";
};
+
+ qcom,wdt@17817000{
+ compatible = "qcom,msm-watchdog";
+ reg = <0x17817000 0x1000>;
+ reg-names = "wdt-base";
+ interrupts = <1 3 0>, <1 2 0>;
+ qcom,bark-time = <11000>;
+ qcom,pet-time = <10000>;
+ };
};
#include "pmxpoorwills.dtsi"
@@ -756,13 +813,15 @@
#include "sdxpoorwills-audio.dtsi"
#include "sdxpoorwills-ion.dtsi"
#include "msm-arm-smmu-sdxpoorwills.dtsi"
+#include "sdxpoorwills-coresight.dtsi"
&soc {
emac_hw: qcom,emac@00020000 {
compatible = "qcom,emac-dwc-eqos";
reg = <0x20000 0x10000>,
- <0x36000 0x100>;
- reg-names = "emac-base", "rgmii-base";
+ <0x36000 0x100>,
+ <0x3900000 0x300000>;
+ reg-names = "emac-base", "rgmii-base", "tlmm-central-base";
interrupts = <0 62 4>, <0 60 4>,
<0 45 4>, <0 49 4>,
<0 50 4>, <0 51 4>,
@@ -801,3 +860,16 @@
};
};
};
+
+#include "pmxpoorwills.dtsi"
+#include "sdxpoorwills-blsp.dtsi"
+#include "sdxpoorwills-regulator.dtsi"
+#include "sdxpoorwills-smp2p.dtsi"
+#include "sdxpoorwills-usb.dtsi"
+#include "sdxpoorwills-pcie.dtsi"
+#include "sdxpoorwills-bus.dtsi"
+#include "sdxpoorwills-thermal.dtsi"
+#include "sdxpoorwills-audio.dtsi"
+#include "sdxpoorwills-ion.dtsi"
+#include "msm-arm-smmu-sdxpoorwills.dtsi"
+#include "sdxpoorwills-pm.dtsi"
diff --git a/arch/arm/configs/msm8953-perf_defconfig b/arch/arm/configs/msm8953-perf_defconfig
index 9d710e3..89ed48a 100644
--- a/arch/arm/configs/msm8953-perf_defconfig
+++ b/arch/arm/configs/msm8953-perf_defconfig
@@ -314,6 +314,7 @@
CONFIG_SMB1351_USB_CHARGER=y
CONFIG_QPNP_SMBCHARGER=y
CONFIG_QPNP_TYPEC=y
+CONFIG_MSM_APM=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_THERMAL=y
CONFIG_THERMAL_QPNP=y
@@ -326,11 +327,15 @@
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_PROXY_CONSUMER=y
CONFIG_REGULATOR_CPR4_APSS=y
+CONFIG_REGULATOR_CPRH_KBSS=y
CONFIG_REGULATOR_MEM_ACC=y
CONFIG_REGULATOR_MSM_GFX_LDO=y
CONFIG_REGULATOR_QPNP_LABIBB=y
CONFIG_REGULATOR_QPNP_LCDB=y
CONFIG_REGULATOR_QPNP=y
+CONFIG_REGULATOR_RPM_SMD=y
+CONFIG_REGULATOR_SPM=y
+CONFIG_REGULATOR_STUB=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CAMERA_SUPPORT=y
CONFIG_MEDIA_CONTROLLER=y
@@ -405,12 +410,18 @@
CONFIG_USB_BAM=y
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_MAILBOX=y
+CONFIG_MSM_SPM=y
+CONFIG_MSM_L2_SPM=y
CONFIG_MSM_BOOT_STATS=y
CONFIG_QCOM_WATCHDOG_V2=y
CONFIG_QCOM_MEMORY_DUMP_V2=y
+CONFIG_MSM_RPM_SMD=y
+CONFIG_QCOM_BUS_SCALING=y
CONFIG_QCOM_SECURE_BUFFER=y
CONFIG_QCOM_EARLY_RANDOM=y
CONFIG_MSM_SMEM=y
+CONFIG_MSM_SMD=y
+CONFIG_MSM_SMD_DEBUG=y
CONFIG_MSM_GLINK=y
CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
@@ -429,6 +440,7 @@
CONFIG_MSM_PM=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
CONFIG_ANDROID=y
diff --git a/arch/arm/configs/msm8953_defconfig b/arch/arm/configs/msm8953_defconfig
index 03f297f..1e9d7ae 100644
--- a/arch/arm/configs/msm8953_defconfig
+++ b/arch/arm/configs/msm8953_defconfig
@@ -324,6 +324,7 @@
CONFIG_SMB1351_USB_CHARGER=y
CONFIG_QPNP_SMBCHARGER=y
CONFIG_QPNP_TYPEC=y
+CONFIG_MSM_APM=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_THERMAL=y
CONFIG_THERMAL_QPNP=y
@@ -336,11 +337,15 @@
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_PROXY_CONSUMER=y
CONFIG_REGULATOR_CPR4_APSS=y
+CONFIG_REGULATOR_CPRH_KBSS=y
CONFIG_REGULATOR_MEM_ACC=y
CONFIG_REGULATOR_MSM_GFX_LDO=y
CONFIG_REGULATOR_QPNP_LABIBB=y
CONFIG_REGULATOR_QPNP_LCDB=y
CONFIG_REGULATOR_QPNP=y
+CONFIG_REGULATOR_RPM_SMD=y
+CONFIG_REGULATOR_SPM=y
+CONFIG_REGULATOR_STUB=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CAMERA_SUPPORT=y
CONFIG_MEDIA_CONTROLLER=y
@@ -419,14 +424,20 @@
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_MAILBOX=y
# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_MSM_SPM=y
+CONFIG_MSM_L2_SPM=y
CONFIG_MSM_BOOT_STATS=y
CONFIG_MSM_CORE_HANG_DETECT=y
CONFIG_MSM_GLADIATOR_HANG_DETECT=y
CONFIG_QCOM_WATCHDOG_V2=y
CONFIG_QCOM_MEMORY_DUMP_V2=y
+CONFIG_MSM_RPM_SMD=y
+CONFIG_QCOM_BUS_SCALING=y
CONFIG_QCOM_SECURE_BUFFER=y
CONFIG_QCOM_EARLY_RANDOM=y
CONFIG_MSM_SMEM=y
+CONFIG_MSM_SMD=y
+CONFIG_MSM_SMD_DEBUG=y
CONFIG_MSM_GLINK=y
CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
@@ -446,6 +457,7 @@
CONFIG_MSM_PM=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_PWM=y
CONFIG_PWM_QPNP=y
CONFIG_ANDROID=y
@@ -491,6 +503,7 @@
CONFIG_DEBUG_STACK_USAGE=y
CONFIG_DEBUG_MEMORY_INIT=y
CONFIG_LOCKUP_DETECTOR=y
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
CONFIG_WQ_WATCHDOG=y
CONFIG_PANIC_TIMEOUT=5
CONFIG_PANIC_ON_SCHED_BUG=y
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index b9628eb..29e6335 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -25,6 +25,7 @@
CONFIG_PARTITION_ADVANCED=y
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_SDXPOORWILLS=y
+CONFIG_PCI_MSM=y
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_CMA=y
@@ -74,6 +75,8 @@
CONFIG_NF_CONNTRACK_TFTP=y
CONFIG_NF_CT_NETLINK=y
CONFIG_NF_CT_NETLINK_TIMEOUT=y
+CONFIG_NF_CT_NETLINK_HELPER=y
+CONFIG_NETFILTER_NETLINK_GLUE_CT=y
CONFIG_NETFILTER_XT_TARGET_LOG=y
CONFIG_NETFILTER_XT_TARGET_MARK=y
CONFIG_NETFILTER_XT_TARGET_NFLOG=y
@@ -81,6 +84,7 @@
CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
CONFIG_NETFILTER_XT_TARGET_TPROXY=y
CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
CONFIG_NETFILTER_XT_MATCH_CONNLABEL=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
@@ -104,6 +108,7 @@
CONFIG_IP_NF_TARGET_REJECT=y
CONFIG_IP_NF_NAT=y
CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NATTYPE_MODULE=y
CONFIG_IP_NF_TARGET_NETMAP=y
CONFIG_IP_NF_TARGET_REDIRECT=y
CONFIG_IP_NF_MANGLE=y
@@ -281,6 +286,7 @@
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_VBUS_DRAW=500
CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_SERIAL=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_FS=y
CONFIG_USB_CONFIGFS_UEVENT=y
@@ -316,6 +322,7 @@
CONFIG_GPIO_USB_DETECT=y
CONFIG_USB_BAM=y
CONFIG_MSM_CLK_RPMH=y
+CONFIG_MSM_CLK_AOP_QMP=y
CONFIG_MDM_GCC_SDXPOORWILLS=y
CONFIG_MDM_CLOCK_CPU_SDXPOORWILLS=y
CONFIG_REMOTE_SPINLOCK_MSM=y
@@ -328,6 +335,7 @@
CONFIG_IOMMU_TESTS=y
CONFIG_QCOM_SCM=y
CONFIG_MSM_BOOT_STATS=y
+CONFIG_QCOM_WATCHDOG_V2=y
CONFIG_MSM_SMEM=y
CONFIG_MSM_GLINK=y
CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
@@ -366,4 +374,13 @@
# CONFIG_DEBUG_PREEMPT is not set
CONFIG_IPC_LOGGING=y
CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_REMOTE_ETM=y
+CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_TPDA=y
+CONFIG_CORESIGHT_TPDM=y
+CONFIG_CORESIGHT_CTI=y
+CONFIG_CORESIGHT_EVENT=y
+CONFIG_CORESIGHT_HWEVENT=y
CONFIG_QMI_ENCDEC=y
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
index b5268d6..865406f 100644
--- a/arch/arm/configs/sdxpoorwills_defconfig
+++ b/arch/arm/configs/sdxpoorwills_defconfig
@@ -27,6 +27,7 @@
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_SDXPOORWILLS=y
# CONFIG_VDSO is not set
+CONFIG_PCI_MSM=y
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_CMA=y
@@ -76,6 +77,8 @@
CONFIG_NF_CONNTRACK_TFTP=y
CONFIG_NF_CT_NETLINK=y
CONFIG_NF_CT_NETLINK_TIMEOUT=y
+CONFIG_NF_CT_NETLINK_HELPER=y
+CONFIG_NETFILTER_NETLINK_GLUE_CT=y
CONFIG_NETFILTER_XT_TARGET_LOG=y
CONFIG_NETFILTER_XT_TARGET_MARK=y
CONFIG_NETFILTER_XT_TARGET_NFLOG=y
@@ -83,6 +86,7 @@
CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
CONFIG_NETFILTER_XT_TARGET_TPROXY=y
CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
CONFIG_NETFILTER_XT_MATCH_CONNLABEL=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
@@ -106,6 +110,7 @@
CONFIG_IP_NF_TARGET_REJECT=y
CONFIG_IP_NF_NAT=y
CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NATTYPE_MODULE=y
CONFIG_IP_NF_TARGET_NETMAP=y
CONFIG_IP_NF_TARGET_REDIRECT=y
CONFIG_IP_NF_MANGLE=y
@@ -280,6 +285,7 @@
CONFIG_USB_GADGET_DEBUG_FS=y
CONFIG_USB_GADGET_VBUS_DRAW=500
CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_SERIAL=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_FS=y
CONFIG_USB_CONFIGFS_UEVENT=y
@@ -315,6 +321,7 @@
CONFIG_GPIO_USB_DETECT=y
CONFIG_USB_BAM=y
CONFIG_MSM_CLK_RPMH=y
+CONFIG_MSM_CLK_AOP_QMP=y
CONFIG_MDM_GCC_SDXPOORWILLS=y
CONFIG_MDM_CLOCK_CPU_SDXPOORWILLS=y
CONFIG_REMOTE_SPINLOCK_MSM=y
@@ -327,6 +334,7 @@
CONFIG_IOMMU_TESTS=y
CONFIG_QCOM_SCM=y
CONFIG_MSM_BOOT_STATS=y
+CONFIG_QCOM_WATCHDOG_V2=y
CONFIG_QCOM_BUS_SCALING=y
CONFIG_QCOM_BUS_CONFIG_RPMH=y
CONFIG_MSM_SMEM=y
@@ -350,7 +358,6 @@
CONFIG_PWM_QPNP=y
CONFIG_QCOM_SHOW_RESUME_IRQ=y
CONFIG_ANDROID=y
-CONFIG_STM=y
CONFIG_EXT3_FS=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_VFAT_FS=y
@@ -382,6 +389,19 @@
CONFIG_IPC_LOGGING=y
CONFIG_BLK_DEV_IO_TRACE=y
CONFIG_DEBUG_USER=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
+CONFIG_CORESIGHT_REMOTE_ETM=y
+CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_QCOM_REPLICATOR=y
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_TPDA=y
+CONFIG_CORESIGHT_TPDM=y
+CONFIG_CORESIGHT_CTI=y
+CONFIG_CORESIGHT_EVENT=y
+CONFIG_CORESIGHT_TGU=y
+CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_CORESIGHT_DUMMY=y
CONFIG_CRYPTO_ECB=y
CONFIG_CRYPTO_CMAC=y
CONFIG_CRYPTO_SHA256=y
diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig
index 714da33..5d49b60 100644
--- a/arch/arm/configs/sunxi_defconfig
+++ b/arch/arm/configs/sunxi_defconfig
@@ -11,6 +11,7 @@
CONFIG_NR_CPUS=8
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
+CONFIG_CMA=y
CONFIG_ARM_APPENDED_DTB=y
CONFIG_ARM_ATAG_DTB_COMPAT=y
CONFIG_CPU_FREQ=y
@@ -35,6 +36,7 @@
# CONFIG_WIRELESS is not set
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DMA_CMA=y
CONFIG_BLK_DEV_SD=y
CONFIG_ATA=y
CONFIG_AHCI_SUNXI=y
diff --git a/arch/arm/kvm/mmio.c b/arch/arm/kvm/mmio.c
index b6e715f..dac7ceb 100644
--- a/arch/arm/kvm/mmio.c
+++ b/arch/arm/kvm/mmio.c
@@ -112,7 +112,7 @@
}
trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
- data);
+ &data);
data = vcpu_data_host_to_guest(vcpu, data, len);
vcpu_set_reg(vcpu, vcpu->arch.mmio_decode.rt, data);
}
@@ -182,14 +182,14 @@
data = vcpu_data_guest_to_host(vcpu, vcpu_get_reg(vcpu, rt),
len);
- trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, fault_ipa, data);
+ trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, fault_ipa, &data);
kvm_mmio_write_buf(data_buf, len, data);
ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, fault_ipa, len,
data_buf);
} else {
trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, len,
- fault_ipa, 0);
+ fault_ipa, NULL);
ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, fault_ipa, len,
data_buf);
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index 2206e0e..2a35c19 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -1284,7 +1284,7 @@
return -EFAULT;
}
- if (is_vm_hugetlb_page(vma) && !logging_active) {
+ if (vma_kernel_pagesize(vma) && !logging_active) {
hugetlb = true;
gfn = (fault_ipa & PMD_MASK) >> PAGE_SHIFT;
} else {
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
index f9dfe80..405e34d 100644
--- a/arch/arm/mach-qcom/Kconfig
+++ b/arch/arm/mach-qcom/Kconfig
@@ -44,12 +44,14 @@
select HAVE_ARM_ARCH_TIMER
select MSM_CORTEX_A7
select PINCTRL
+ select PCI
select QCOM_SCM if SMP
select MSM_JTAG_MM if CORESIGHT_ETM
select PM_DEVFREQ
select COMMON_CLK
select COMMON_CLK_QCOM
select QCOM_GDSC
+ select GENERIC_CLOCKEVENTS_BROADCAST
config ARCH_MSM8953
bool "Enable support for MSM8953"
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index d35cecb..b7640d3 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -154,6 +154,15 @@
This enables support for the MSM8953 chipset. If you do not
wish to build a kernel that runs on this chipset, say 'N' here.
+config ARCH_MSM8937
+ bool "Enable Support for Qualcomm Technologies Inc. MSM8937"
+ depends on ARCH_QCOM
+ select CPU_FREQ_QCOM
+ select COMMON_CLK_MSM
+ help
+ This enables support for the MSM8937 chipset. If you do not
+ wish to build a kernel that runs on this chipset, say 'N' here.
+
config ARCH_SDM450
bool "Enable Support for Qualcomm Technologies Inc. SDM450"
depends on ARCH_QCOM
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index 80d3e82c..9877324 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -220,7 +220,15 @@
msm8953-cdp-overlay.dtbo \
msm8953-rcm-overlay.dtbo \
msm8953-qrd-overlay.dtbo \
- msm8953-iot-mtp-overlay.dtbo
+ msm8953-iot-mtp-overlay.dtbo \
+ sdm450-cdp-s2-overlay.dtbo \
+ sdm450-mtp-s3-overlay.dtbo \
+ sdm450-qrd-sku4-overlay.dtbo
+
+dtbo-$(CONFIG_ARCH_SDM632) += sdm632-rumi-overlay.dtbo \
+ sdm450-cdp-s2-overlay.dtbo \
+ sdm450-mtp-s3-overlay.dtbo \
+ sdm450-qrd-sku4-overlay.dtbo
msm8953-mtp-overlay.dtbo-base := sdm450.dtb \
msm8953.dtb \
@@ -251,6 +259,14 @@
msm8953-ext-codec-rcm-overlay.dtbo-base := msm8953.dtb \
apq8053.dtb
msm8953-cdp-1200p-overlay.dtbo-base := msm8953.dtb
+sdm450-cdp-s2-overlay.dtbo-base := sdm450-pmi632.dtb \
+ sdm632.dtb \
+ msm8953-pmi632.dtb
+sdm450-mtp-s3-overlay.dtbo-base := sdm450-pmi632.dtb \
+ sdm632.dtb
+sdm450-qrd-sku4-overlay.dtbo-base := sdm450-pmi632.dtb \
+ sdm632.dtb
+sdm632-rumi-overlay.dtbo-base := sdm632.dtb
else
dtb-$(CONFIG_ARCH_MSM8953) += msm8953-cdp.dtb \
@@ -274,7 +290,8 @@
msm8953-pmi8937-cdp.dtb \
msm8953-pmi8937-mtp.dtb \
msm8953-pmi8940-ext-codec-mtp.dtb \
- msm8953-pmi8937-ext-codec-mtp.dtb
+ msm8953-pmi8937-ext-codec-mtp.dtb \
+ msm8953-pmi632-cdp-s2.dtb
dtb-$(CONFIG_ARCH_SDM450) += sdm450-rcm.dtb \
sdm450-cdp.dtb \
@@ -287,7 +304,10 @@
sdm450-pmi632-cdp-s2.dtb \
sdm450-pmi632-mtp-s3.dtb
-dtb-$(CONFIG_ARCH_SDM632) += sdm632-rumi.dtb
+dtb-$(CONFIG_ARCH_SDM632) += sdm632-rumi.dtb \
+ sdm632-cdp-s2.dtb \
+ sdm632-mtp-s3.dtb \
+ sdm632-qrd-sku4.dtb
endif
diff --git a/arch/arm64/boot/dts/qcom/dsi-adv7533-1080p.dtsi b/arch/arm64/boot/dts/qcom/dsi-adv7533-1080p.dtsi
new file mode 100644
index 0000000..7994285
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-adv7533-1080p.dtsi
@@ -0,0 +1,75 @@
+/* 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.
+ */
+
+&mdss_mdp {
+ dsi_adv7533_1080p: qcom,mdss_dsi_adv7533_1080p {
+ label = "adv7533 1080p video mode dsi panel";
+ qcom,mdss-dsi-panel-name = "dsi_adv7533_1080p";
+ qcom,mdss-dsi-panel-controller = <&mdss_dsi0>;
+ qcom,mdss-dsi-panel-type = "dsi_video_mode";
+ qcom,mdss-dsi-panel-destination = "display_1";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <1920>;
+ qcom,mdss-dsi-panel-height = <1080>;
+ qcom,mdss-dsi-h-front-porch = <88>;
+ qcom,mdss-dsi-h-back-porch = <148>;
+ qcom,mdss-dsi-h-pulse-width = <44>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <36>;
+ qcom,mdss-dsi-v-front-porch = <4>;
+ qcom,mdss-dsi-v-pulse-width = <5>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-on-command = [
+ 05 01 00 00 c8 00 02 11 00
+ 05 01 00 00 0a 00 02 29 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00 00 00 02 28 00
+ 05 01 00 00 00 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-h-sync-pulse = <1>;
+ qcom,mdss-dsi-traffic-mode = "non_burst_sync_pulse";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-dsi-panel-timings = [
+ E6 38 26 00 68 6C 2A 3A 2C 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x02>;
+ qcom,mdss-dsi-t-clk-pre = <0x2B>;
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-reset-sequence = <1 20>, <0 1>, <1 20>;
+ qcom,mdss-pan-physical-width-dimension = <160>;
+ qcom,mdss-pan-physical-height-dimension = <90>;
+ qcom,mdss-dsi-force-clock-lane-hs;
+ qcom,mdss-dsi-always-on;
+ qcom,mdss-dsi-panel-timings-phy-v2 = [1d 1a 03 05 01 03 04 a0
+ 1d 1a 03 05 01 03 04 a0
+ 1d 1a 03 05 01 03 04 a0
+ 1d 1a 03 05 01 03 04 a0
+ 1d 1a 03 05 01 03 04 a0];
+ qcom,dba-panel;
+ qcom,bridge-name = "adv7533";
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-adv7533-720p.dtsi b/arch/arm64/boot/dts/qcom/dsi-adv7533-720p.dtsi
new file mode 100644
index 0000000..b84488c0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-adv7533-720p.dtsi
@@ -0,0 +1,74 @@
+/* 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.
+ */
+
+&mdss_mdp {
+dsi_adv7533_720p: qcom,mdss_dsi_adv7533_720p {
+ label = "adv7533 720p video mode dsi panel";
+ qcom,mdss-dsi-panel-name = "dsi_adv7533_720p";
+ qcom,mdss-dsi-panel-controller = <&mdss_dsi0>;
+ qcom,mdss-dsi-panel-type = "dsi_video_mode";
+ qcom,mdss-dsi-panel-destination = "display_1";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <1280>;
+ qcom,mdss-dsi-panel-height = <720>;
+ qcom,mdss-dsi-h-front-porch = <110>;
+ qcom,mdss-dsi-h-back-porch = <220>;
+ qcom,mdss-dsi-h-pulse-width = <40>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <20>;
+ qcom,mdss-dsi-v-front-porch = <5>;
+ qcom,mdss-dsi-v-pulse-width = <5>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-on-command = [
+ 05 01 00 00 c8 00 02 11 00
+ 05 01 00 00 0a 00 02 29 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00 00 00 02 28 00
+ 05 01 00 00 00 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-h-sync-pulse = <1>;
+ qcom,mdss-dsi-traffic-mode = "non_burst_sync_pulse";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-panel-timings = [
+ A4 24 18 00 4E 52 1C 28 1C 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x03>;
+ qcom,mdss-dsi-t-clk-pre = <0x20>;
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-reset-sequence = <1 20>, <0 1>, <1 20>;
+ qcom,mdss-pan-physical-width-dimension = <160>;
+ qcom,mdss-pan-physical-height-dimension = <90>;
+ qcom,mdss-dsi-force-clock-lane-hs;
+ qcom,mdss-dsi-always-on;
+ qcom,mdss-dsi-panel-timings-phy-v2 = [1c 19 02 03 01 03 04 a0
+ 1c 19 02 03 01 03 04 a0
+ 1c 19 02 03 01 03 04 a0
+ 1c 19 02 03 01 03 04 a0
+ 1c 08 02 03 01 03 04 a0];
+ qcom,dba-panel;
+ qcom,bridge-name = "adv7533";
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-adv7533-1080p-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-adv7533-1080p-video.dtsi
new file mode 100644
index 0000000..cbf82af
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-adv7533-1080p-video.dtsi
@@ -0,0 +1,67 @@
+/* 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.
+ */
+
+&mdss_mdp {
+ dsi_adv7533_1080p: qcom,mdss_dsi_adv7533_1080p {
+ label = "adv7533 720p video mode dsi panel";
+ qcom,mdss-dsi-panel-name = "dsi_adv7533_1080p";
+ qcom,mdss-dsi-panel-type = "dsi_video_mode";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <1920>;
+ qcom,mdss-dsi-panel-height = <1080>;
+ qcom,mdss-dsi-h-front-porch = <88>;
+ qcom,mdss-dsi-h-back-porch = <148>;
+ qcom,mdss-dsi-h-pulse-width = <44>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <36>;
+ qcom,mdss-dsi-v-front-porch = <4>;
+ qcom,mdss-dsi-v-pulse-width = <5>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-on-command = [
+ 05 01 00 00 c8 00 02 11 00
+ 05 01 00 00 0a 00 02 29 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00 00 00 02 28 00
+ 05 01 00 00 00 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-h-sync-pulse = <1>;
+ qcom,mdss-dsi-traffic-mode = "non_burst_sync_pulse";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-dsi-panel-timings = [e6 38 26 00 68 6c 2a 3a
+ 2c 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x02>;
+ qcom,mdss-dsi-t-clk-pre = <0x2B>;
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-reset-sequence = <1 20>, <0 1>, <1 20>;
+ qcom,mdss-pan-physical-width-dimension = <160>;
+ qcom,mdss-pan-physical-height-dimension = <90>;
+ qcom,mdss-dsi-force-clock-lane-hs;
+ qcom,mdss-dsi-always-on;
+ qcom,dba-panel;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-adv7533-720p-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-adv7533-720p-video.dtsi
new file mode 100644
index 0000000..55ce9f7
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-adv7533-720p-video.dtsi
@@ -0,0 +1,66 @@
+/* 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.
+ */
+
+&mdss_mdp {
+ dsi_adv7533_720p: qcom,mdss_dsi_adv7533_720p {
+ label = "adv7533 720p video mode dsi panel";
+ qcom,mdss-dsi-panel-name = "dsi_adv7533_720p";
+ qcom,mdss-dsi-panel-type = "dsi_video_mode";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <1280>;
+ qcom,mdss-dsi-panel-height = <720>;
+ qcom,mdss-dsi-h-front-porch = <110>;
+ qcom,mdss-dsi-h-back-porch = <220>;
+ qcom,mdss-dsi-h-pulse-width = <40>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <20>;
+ qcom,mdss-dsi-v-front-porch = <5>;
+ qcom,mdss-dsi-v-pulse-width = <5>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-on-command = [
+ 05 01 00 00 c8 00 02 11 00
+ 05 01 00 00 0a 00 02 29 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00 00 00 02 28 00
+ 05 01 00 00 00 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-h-sync-pulse = <1>;
+ qcom,mdss-dsi-traffic-mode = "non_burst_sync_pulse";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-panel-timings = [a4 24 18 00 4e 52 1c 28
+ 1c 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x03>;
+ qcom,mdss-dsi-t-clk-pre = <0x20>;
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-reset-sequence = <1 20>, <0 1>, <1 20>;
+ qcom,mdss-pan-physical-width-dimension = <160>;
+ qcom,mdss-pan-physical-height-dimension = <90>;
+ qcom,mdss-dsi-force-clock-lane-hs;
+ qcom,mdss-dsi-always-on;
+ qcom,dba-panel;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-lt8912-1080p-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-lt8912-1080p-video.dtsi
new file mode 100644
index 0000000..7297d2a
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-lt8912-1080p-video.dtsi
@@ -0,0 +1,69 @@
+/* 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.
+ */
+
+&mdss_mdp {
+ dsi_lt8912_1080_vid: qcom,mdss_dsi_lt8912_1080p_video {
+ qcom,mdss-dsi-panel-name = "lt8912 1080p video mode dsi panel";
+ qcom,mdss-dsi-panel-type = "dsi_video_mode";
+ qcom,mdss-dsi-panel-destination = "display_1";
+ qcom,mdss-dsi-color-order = "rgb_swap_rgb";
+ qcom,mdss-dsi-lane-map = "lane_map_0123";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <1920>;
+ qcom,mdss-dsi-panel-height = <1080>;
+ qcom,mdss-dsi-h-front-porch = <88>;
+ qcom,mdss-dsi-h-back-porch = <148>;
+ qcom,mdss-dsi-h-pulse-width = <44>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <36>;
+ qcom,mdss-dsi-v-front-porch = <4>;
+ qcom,mdss-dsi-v-pulse-width = <5>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-on-command = [
+ 05 01 00 00 a0 00 02 11 00
+ 05 01 00 00 a0 00 02 29 00];
+ qcom,mdss-dsi-off-command = [
+ 05 01 00 00 78 00 02 28 00
+ 05 01 00 00 78 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-traffic-mode = "non_burst_sync_event";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-panel-timings-phy-v2 = [
+ 23 1e 08 09 05 03 04 a0
+ 23 1e 08 09 05 03 04 a0
+ 23 1e 08 09 05 03 04 a0
+ 23 1e 08 09 05 03 04 a0
+ 23 1a 08 09 05 03 04 a0];
+ qcom,mdss-dsi-panel-timings = [
+ e6 38 26 00 68 6c 2a 3a 2c 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x02>;
+ qcom,mdss-dsi-t-clk-pre = <0x2b>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-reset-sequence = <1 20>, <0 2>, <1 20>;
+ qcom,mdss-dsi-post-init-delay = <1>;
+ qcom,mdss-dsi-force-clock-lane-hs;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-lt8912-480p-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-lt8912-480p-video.dtsi
new file mode 100644
index 0000000..cde7fb4
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-lt8912-480p-video.dtsi
@@ -0,0 +1,67 @@
+/* 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.
+ */
+
+&mdss_mdp {
+ dsi_lt8912_480_vid: qcom,mdss_dsi_lt8912_480p_video {
+ qcom,mdss-dsi-panel-name = "lt8912 480p video mode dsi panel";
+ qcom,mdss-dsi-panel-type = "dsi_video_mode";
+ qcom,mdss-dsi-panel-destination = "display_1";
+ qcom,mdss-dsi-color-order = "rgb_swap_rgb";
+ qcom,mdss-dsi-lane-map = "lane_map_0123";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <640>;
+ qcom,mdss-dsi-panel-height = <480>;
+ qcom,mdss-dsi-h-front-porch = <16>;
+ qcom,mdss-dsi-h-back-porch = <48>;
+ qcom,mdss-dsi-h-pulse-width = <96>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <32>;
+ qcom,mdss-dsi-v-front-porch = <15>;
+ qcom,mdss-dsi-v-pulse-width = <2>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-on-command = [
+ 05 01 00 00 a0 00 02 11 00
+ 05 01 00 00 a0 00 02 29 00];
+ qcom,mdss-dsi-off-command = [
+ 05 01 00 00 78 00 02 28 00
+ 05 01 00 00 78 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-traffic-mode = "non_burst_sync_event";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-panel-timings-phy-v2 = [
+ 1D 1A 03 05 01 03 04 a0
+ 1D 1A 03 05 01 03 04 a0
+ 1D 0A 03 04 01 03 04 a0];
+ qcom,mdss-dsi-panel-timings = [
+ 65 12 0C 00 34 38 10 16 0F 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x02>;
+ qcom,mdss-dsi-t-clk-pre = <0x2B>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-reset-sequence = <1 20>, <0 2>, <1 20>;
+ qcom,mdss-dsi-post-init-delay = <1>;
+ qcom,mdss-dsi-force-clock-lane-hs;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi
index c059443..1b38d06 100644
--- a/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -38,17 +38,13 @@
qcom,mdss-dsi-te-dcs-command = <1>;
qcom,mdss-dsi-te-check-enable;
qcom,mdss-dsi-te-using-te-pin;
- qcom,mdss-dsi-panel-timings =
- [da 34 24 00 64 68 28 38 2a 03 04 00];
- qcom,mdss-dsi-t-clk-pre = <0x29>;
- qcom,mdss-dsi-t-clk-post = <0x03>;
qcom,mdss-dsi-dma-trigger = "trigger_sw";
qcom,mdss-dsi-mdp-trigger = "none";
qcom,mdss-dsi-lp11-init;
qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
qcom,mdss-dsi-bl-min-level = <1>;
qcom,mdss-dsi-bl-max-level = <4095>;
- qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+ qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 50>;
qcom,mdss-dsi-display-timings {
timing@0 {
qcom,mdss-dsi-panel-framerate = <60>;
@@ -77,11 +73,11 @@
05 01 00 00 0a 00 02 20 00
15 01 00 00 00 00 02 bb 10
05 01 00 00 78 00 02 11 00
- 05 01 00 00 14 00 02 29 00
+ 05 01 00 00 78 00 02 29 00
];
qcom,mdss-dsi-off-command = [
- 05 01 00 00 14 00 02
- 28 00 05 01 00 00 78 00 02 10 00
+ 05 01 00 00 78 00 02 28 00
+ 05 01 00 00 78 00 02 10 00
];
qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-r69006-1080p-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-r69006-1080p-cmd.dtsi
new file mode 100644
index 0000000..90d42a9
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-r69006-1080p-cmd.dtsi
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*---------------------------------------------------------------------------
+ * This file is autogenerated file using gcdb parser. Please do not edit it.
+ * Update input XML file to add a new entry or update variable in this file
+ * VERSION = "1.0"
+ *---------------------------------------------------------------------------
+ */
+
+&mdss_mdp {
+ dsi_r69006_1080p_cmd: qcom,mdss_dsi_r69006_1080p_cmd {
+ qcom,mdss-dsi-panel-name = "r69006 1080p cmd mode dsi panel";
+ qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <1080>;
+ qcom,mdss-dsi-panel-height = <1920>;
+ qcom,mdss-dsi-h-front-porch = <100>;
+ qcom,mdss-dsi-h-back-porch = <82>;
+ qcom,mdss-dsi-h-pulse-width = <20>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <9>;
+ qcom,mdss-dsi-v-front-porch = <3>;
+ qcom,mdss-dsi-v-pulse-width = <15>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-on-command = [23 01 00 00 00 00 02 B0 00
+ 29 01 00 00 00 00 06
+ B3 04 10 00 00 00
+ 29 01 00 00 00 00 03
+ B4 0C 00
+ 29 01 00 00 00 00 04
+ B6 3B D3 00
+ 23 01 00 00 00 00
+ 02 C0 00
+ 15 01 00 00 00 00
+ 02 36 98
+ 23 01 00 00 00 00
+ 02 CC 04
+ 29 01 00 00 00 00 20
+ C1 84 00 10 EF 8B F1 FF
+ FF DF 9C C5 9A 73 8D AD
+ 63 FE FF FF CB F8 01 00
+ AA 40 02 C2 01 08 00 01
+ 29 01 00 00 00 00 0A
+ CB 0D FE 1F 2C 00 00 00
+ 00 00
+ 29 01 00 00 00 00 0B
+ C2 01 F7 80 04 63 00 60
+ 00 01 30
+ 29 01 00 00 00 00 07
+ C3 55 01 00 01 00 00
+ 29 01 00 00 00 00 12
+ C4 70 00 00 00 00 00 00
+ 00 00 02 01 00 05 01 00
+ 00 00
+ 29 01 00 00 00 00 0F
+ C6 57 07 4A 07 4A 01 0E
+ 01 02 01 02 09 15 07
+ 29 01 00 00 00 00 1F
+ C7 00 06 0C 16 27 35 3F
+ 4D 33 3C 49 5B 64 66 67
+ 00 06 0C 16 27 35 3F 4D
+ 33 3C 49 5B 64 66 67
+ 29 01 00 00 00 00 14
+ C8 00 00 FE 01 08 E7 00
+ 00 FD 02 03 A8 00 00 FC
+ E7 E9 C9 00
+ 29 01 00 00 00 00 09
+ C9 1F 68 1F 68 4C 4C C4
+ 11
+ 29 01 00 00 00 00 11
+ D0 11 01 91 0B D9 19 19
+ 00 00 00 19 99 00 00 00
+ 00
+ 29 01 00 00 00 00 1D
+ D3 1B 3B BB AD A5 33 33
+ 33 00 80 AD A8 37 33 33
+ 33 33 F7 F2 1F 7D 7C FF
+ 0F 99 00 FF FF
+ 29 01 00 00 00 00 04
+ D4 57 33 03
+ 29 01 00 00 00 00 0C
+ D5 66 00 00 01 32 01 32
+ 00 0b 00 0b
+ 29 01 00 00 00 00 02 BE 04
+ 29 01 00 00 00 00 11
+ CF 40 10 00 00 00 00 32
+ 00 00 00 00 00 00 00 00
+ 00
+ 29 01 00 00 00 00 06
+ DE 00 00 3F FF 10
+ 29 01 00 00 00 00 02 E9 00
+ 29 01 00 00 00 00 02 F2 00
+ 23 01 00 00 00 00 02 D6 01
+ 39 01 00 00 00 00 02 35 00
+ 39 01 00 00 00 00 02 51 FF
+ 39 01 00 00 00 00 02 53 2C
+ 39 01 00 00 00 00 02 55 00
+ 05 01 00 00 78 00 02 11 00
+ 05 01 00 00 14 00 02 29 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00 0A 00 02 28 00
+ 29 01 00 00 00 00 1d D3 13 3B BB A5 A5 33 33 33
+ 00 80 A4 A8 37 33 33 33 33 F7 F2 1F 7D
+ 7C FF 0F 99 00 FF FF
+ 05 01 00 00 5A 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-traffic-mode = "burst_mode";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-dsi-te-pin-select = <1>;
+ qcom,mdss-dsi-wr-mem-start = <0x2c>;
+ qcom,mdss-dsi-wr-mem-continue = <0x3c>;
+ qcom,mdss-dsi-te-dcs-command = <1>;
+ qcom,mdss-dsi-te-check-enable;
+ qcom,mdss-dsi-te-using-te-pin;
+ qcom,mdss-dsi-panel-timings = [6E 3F 36 00 5A 4F 38 41 54
+ 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x1e>;
+ qcom,mdss-dsi-t-clk-pre = <0x30>;
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 05 00 01 0A];
+ qcom,mdss-dsi-panel-status-command-mode = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-status-value = <0x1C>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+ qcom,mdss-dsi-rx-eot-ignore;
+ qcom,mdss-dsi-tx-eot-append;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-r69006-1080p-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-r69006-1080p-video.dtsi
new file mode 100644
index 0000000..4cbc922
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-r69006-1080p-video.dtsi
@@ -0,0 +1,137 @@
+/* Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*---------------------------------------------------------------------------
+ * This file is autogenerated file using gcdb parser. Please do not edit it.
+ * Update input XML file to add a new entry or update variable in this file
+ * VERSION = "1.0"
+ *---------------------------------------------------------------------------
+ */
+
+&mdss_mdp {
+ dsi_r69006_1080p_video: qcom,mdss_dsi_r69006_1080p_video {
+ qcom,mdss-dsi-panel-name = "r69006 1080p video mode dsi panel";
+ qcom,mdss-dsi-panel-type = "dsi_video_mode";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <1080>;
+ qcom,mdss-dsi-panel-height = <1920>;
+ qcom,mdss-dsi-h-front-porch = <100>;
+ qcom,mdss-dsi-h-back-porch = <82>;
+ qcom,mdss-dsi-h-pulse-width = <20>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <9>;
+ qcom,mdss-dsi-v-front-porch = <3>;
+ qcom,mdss-dsi-v-pulse-width = <15>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-on-command = [23 01 00 00 00 00 02 B0 00
+ 29 01 00 00 00 00 06
+ B3 05 10 00 00 00
+ 29 01 00 00 00 00 03 B4 0c 00
+ 29 01 00 00 00 00 04 B6 3b c3 00
+ 23 01 00 00 00 00 02 C0 00
+ 15 01 00 00 00 00 02 36 98
+ 23 01 00 00 00 00 02 CC 04
+ 29 01 00 00 00 00 20
+ C1 84 00 10 EF 8B
+ F1 FF FF DF 9C C5
+ 9A 73 8D AD 63 FE
+ FF FF CB F8 01 00
+ AA 40 00 C2 01 08
+ 00 01
+ 29 01 00 00 00 00 0A
+ CB 0D FE 1F 2C 00
+ 00 00 00 00
+ 29 01 00 00 00 00 0B
+ C2 01 F7 80 04 63
+ 00 60 00 01 30
+ 29 01 00 00 00 00 07
+ C3 55 01 00 01 00
+ 00
+ 29 01 00 00 00 00 12
+ C4 70 00 00 00 00
+ 00 00 00 00 02 01
+ 00 05 01 00 00 00
+ 29 01 00 00 00 00 0F
+ C6 59 07 4a 07 4a
+ 01 0E 01 02 01 02
+ 09 15 07
+ 29 01 00 00 00 00 1F
+ C7 00 30 32 34 42
+ 4E 56 62 44 4A 54
+ 62 6B 73 7F 08 30
+ 32 34 42 4E 56 62
+ 44 4A 54 62 6B 73
+ 7F
+ 29 01 00 00 00 00 14
+ C8 00 00 00 00 00
+ FC 00 00 00 00 00
+ FC 00 00 00 00 00
+ FC 00
+ 29 01 00 00 00 00 09
+ C9 1F 68 1F 68 4C
+ 4C C4 11
+ 29 01 00 00 00 00 11
+ D0 33 01 91 0B D9
+ 19 19 00 00 00 19
+ 99 00 00 00 00
+ 29 01 00 00 00 00 1D
+ D3 1B 3B BB AD A5
+ 33 33 33 00 80 AD
+ A8 6f 6f 33 33 33
+ F7 F2 1F 7D 7C FF
+ 0F 99 00 FF FF
+ 29 01 00 00 00 00 04
+ D4 57 33 03
+ 29 01 00 00 00 00 0C
+ D5 66 00 00 01 27
+ 01 27 00 6D 00 6D
+ 23 01 00 00 00 00 02 D6 81
+ 05 01 00 00 78 00 02 11 00
+ 05 01 00 00 78 00 02 29 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00 78 00 02 28 00
+ 05 01 00 00 96 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-h-sync-pulse = <1>;
+ qcom,mdss-dsi-traffic-mode = "burst_mode";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-dsi-panel-timings = [7d 25 1d 00 37 33
+ 22 27 1e 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x20>;
+ qcom,mdss-dsi-t-clk-pre = <0x2c>;
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-reset-sequence = <1 20>, <0 2>, <1 20>;
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 05 00 01 0A];
+ qcom,mdss-dsi-panel-status-command-mode = "dsi_lp_mode";
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-read-length = <1>;
+ qcom,mdss-dsi-panel-status-value = <0x1C>;
+ qcom,mdss-dsi-panel-max-error-count = <3>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-truly-1080p-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-truly-1080p-cmd.dtsi
new file mode 100644
index 0000000..83b8ca0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-truly-1080p-cmd.dtsi
@@ -0,0 +1,96 @@
+/* Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&mdss_mdp {
+ dsi_truly_1080_cmd: qcom,mdss_dsi_truly_1080p_cmd {
+ qcom,mdss-dsi-panel-name = "truly 1080p cmd mode dsi panel";
+ qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <1080>;
+ qcom,mdss-dsi-panel-height = <1920>;
+ qcom,mdss-dsi-h-front-porch = <96>;
+ qcom,mdss-dsi-h-back-porch = <64>;
+ qcom,mdss-dsi-h-pulse-width = <16>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <16>;
+ qcom,mdss-dsi-v-front-porch = <4>;
+ qcom,mdss-dsi-v-pulse-width = <1>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-te-pin-select = <1>;
+ qcom,mdss-dsi-te-dcs-command = <1>;
+ qcom,mdss-dsi-te-check-enable;
+ qcom,mdss-dsi-te-using-te-pin;
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-traffic-mode = "burst_mode";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-dsi-panel-timings =
+ [e6 38 26 00 68 6e 2a 3c 44 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x02>;
+ qcom,mdss-dsi-t-clk-pre = <0x2d>;
+ qcom,mdss-dsi-tx-eot-append;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-on-command = [23 01 00 00 00 00 02 d6 01
+ 15 01 00 00 00 00 02 35 00
+ 15 01 00 00 00 00 02 51 ff
+ 15 01 00 00 00 00 02 53 2c
+ 15 01 00 00 00 00 02 55 00
+ 05 01 00 00 78 00 02 11 00
+ 23 01 00 00 00 00 02 b0 04
+ 29 01 00 00 00 00 07 b3 04 00 00 00 00 00
+ 29 01 00 00 00 00 03 b6 3a d3
+ 29 01 00 00 00 00 03 c0 00 00
+ 29 01 00 00 00 00 23 c1 84 60 10 eb ff 6f ce ff ff 17 02
+ 58 73 ae b1 20 c6 ff ff 1f f3 ff 5f 10 10 10 10
+ 00 02 01 22 22 00 01
+ 29 01 00 00 00 00 08 c2 31 f7 80 06 08 00 00
+ 29 01 00 00 00 00 17 c4 70 00 00 00 00 04 00 00 00 0c 06
+ 00 00 00 00 00 04 00 00 00 0c 06
+ 29 01 00 00 00 00 29 c6 78 69 00 69 00 69 00 00 00 00 00
+ 69 00 69 00 69 10 19 07 00 78 00 69 00 69 00 69
+ 00 00 00 00 00 69 00 69 00 69 10 19 07
+ 29 01 00 00 00 00 0a cb 31 fc 3f 8c 00 00 00 00 c0
+ 23 01 00 00 00 00 02 cc 0b
+ 29 01 00 00 00 00 0b d0 11 81 bb 1e 1e 4c 19 19 0c 00
+ 29 01 00 00 00 00 1a d3 1b 33 bb bb b3 33 33 33 00 01 00
+ a0 d8 a0 0d 4e 4e 33 3b 22 72 07 3d bf 33
+ 29 01 00 00 00 00 08 d5 06 00 00 01 51 01 32
+ 29 01 00 00 00 00 1f c7 01 0a 11 18 26 33 3e 50 38 42 52
+ 60 67 6e 77 01 0a 11 18 26 33 3e 50 38 42 52 60
+ 67 6e 77
+ 29 01 00 00 14 00 14 c8 01 00 00 00 00 fc 00 00 00 00
+ 00 fc 00 00 00 00 00 fc 00
+ 05 01 00 00 14 00 02 29 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00 14 00 02 28 00
+ 05 01 00 00 78 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+ qcom,mdss-dsi-post-init-delay = <1>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-truly-1080p-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-truly-1080p-video.dtsi
new file mode 100644
index 0000000..b8a85d9
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-truly-1080p-video.dtsi
@@ -0,0 +1,91 @@
+/* Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&mdss_mdp {
+ dsi_truly_1080_vid: qcom,mdss_dsi_truly_1080p_video {
+ qcom,mdss-dsi-panel-name = "truly 1080p video mode dsi panel";
+ qcom,mdss-dsi-panel-type = "dsi_video_mode";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <1080>;
+ qcom,mdss-dsi-panel-height = <1920>;
+ qcom,mdss-dsi-h-front-porch = <96>;
+ qcom,mdss-dsi-h-back-porch = <64>;
+ qcom,mdss-dsi-h-pulse-width = <16>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <16>;
+ qcom,mdss-dsi-v-front-porch = <4>;
+ qcom,mdss-dsi-v-pulse-width = <1>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-traffic-mode = "burst_mode";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-dsi-panel-timings =
+ [e6 38 26 00 68 6e 2a 3c 44 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x02>;
+ qcom,mdss-dsi-t-clk-pre = <0x2d>;
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-on-command = [15 01 00 00 00 00 02 35 00
+ 15 01 00 00 00 00 02 51 ff
+ 15 01 00 00 00 00 02 53 2c
+ 15 01 00 00 00 00 02 55 00
+ 05 01 00 00 78 00 02 11 00
+ 23 01 00 00 00 00 02 b0 00
+ 29 01 00 00 00 00 07 b3 14 00 00 00 00 00
+ 29 01 00 00 00 00 03 b6 3a d3
+ 29 01 00 00 00 00 03 c0 00 00
+ 29 01 00 00 00 00 23 c1 84 60 10 eb ff 6f ce ff ff 17 02
+ 58 73 ae b1 20 c6 ff ff 1f f3 ff 5f 10 10 10 10
+ 00 02 01 22 22 00 01
+ 29 01 00 00 00 00 08 c2 31 f7 80 06 08 00 00
+ 29 01 00 00 00 00 17 c4 70 00 00 00 00 04 00 00 00 0c 06
+ 00 00 00 00 00 04 00 00 00 0c 06
+ 29 01 00 00 00 00 29 c6 00 69 00 69 00 69 00 00 00 00 00
+ 69 00 69 00 69 10 19 07 00 01 00 69 00 69 00 69
+ 00 00 00 00 00 69 00 69 00 69 10 19 07
+ 29 01 00 00 00 00 0a cb 31 fc 3f 8c 00 00 00 00 c0
+ 23 01 00 00 00 00 02 cc 0b
+ 29 01 00 00 00 00 0b d0 11 81 bb 1e 1e 4c 19 19 0c 00
+ 29 01 00 00 00 00 1a d3 1b 33 bb bb b3 33 33 33 00 01 00
+ a0 d8 a0 0d 4e 4e 33 3b 22 72 07 3d bf 33
+ 29 01 00 00 00 00 08 d5 06 00 00 01 51 01 32
+ 29 01 00 00 00 00 1f c7 01 0a 11 18 26 33 3e 50 38 42 52
+ 60 67 6e 77 01 0a 11 18 26 33 3e 50 38 42 52 60
+ 67 6e 77
+ 29 01 00 00 14 00 14 c8 01 00 00 00 00 fc 00 00 00 00
+ 00 fc 00 00 00 00 00 fc 00
+ 05 01 00 00 14 00 02 29 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00 14 00 02 28 00
+ 05 01 00 00 78 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+ qcom,mdss-dsi-tx-eot-append;
+ qcom,mdss-dsi-post-init-delay = <1>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-truly-wuxga-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-truly-wuxga-video.dtsi
new file mode 100644
index 0000000..b0d13d0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-truly-wuxga-video.dtsi
@@ -0,0 +1,59 @@
+/* Copyright (c) 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.
+ */
+
+&mdss_mdp {
+ dsi_truly_wuxga_vid: qcom,mdss_dsi_truly_wuxga_video {
+ qcom,mdss-dsi-panel-name = "truly wuxga video mode dsi panel";
+ qcom,mdss-dsi-panel-type = "dsi_video_mode";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <1920>;
+ qcom,mdss-dsi-panel-height = <1200>;
+ qcom,mdss-dsi-h-front-porch = <96>;
+ qcom,mdss-dsi-h-back-porch = <64>;
+ qcom,mdss-dsi-h-pulse-width = <16>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <16>;
+ qcom,mdss-dsi-v-front-porch = <4>;
+ qcom,mdss-dsi-v-pulse-width = <1>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-traffic-mode = "burst_mode";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-dsi-panel-timings = [f3 3a 26 00 6c 6e
+ 2c 3e 2f 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x02>;
+ qcom,mdss-dsi-t-clk-pre = <0x2d>;
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-on-command = [32 01 00 00 00 00 02 00 00];
+ qcom,mdss-dsi-off-command = [22 01 00 00 00 00 02 00 00];
+ qcom,mdss-dsi-on-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-reset-sequence = <1 200>, <0 200>, <1 200>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi b/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
index 87b8c74..8212cc8 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -74,3 +74,51 @@
status = "ok";
};
+
+#include "msm8953-mdss-panels.dtsi"
+
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_truly_1080_vid>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+
+ qcom,platform-te-gpio = <&tlmm 24 0>;
+ qcom,platform-reset-gpio = <&tlmm 61 0>;
+ qcom,platform-bklight-en-gpio = <&tlmm 59 0>;
+};
+
+&mdss_dsi1 {
+ status = "disabled";
+ qcom,dsi-pref-prim-pan = <&dsi_adv7533_1080p>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+
+ qcom,pluggable;
+ qcom,platform-te-gpio = <&tlmm 24 0>;
+ qcom,platform-reset-gpio = <&tlmm 61 0>;
+ qcom,platform-bklight-en-gpio = <&tlmm 59 0>;
+};
+
+&dsi_truly_1080_vid {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
+};
+
+&dsi_truly_1080_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,ulps-enabled;
+ qcom,partial-update-enabled;
+ qcom,panel-roi-alignment = <2 2 4 2 1080 2>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mdss-panels.dtsi b/arch/arm64/boot/dts/qcom/msm8953-mdss-panels.dtsi
new file mode 100644
index 0000000..4fa5cd1
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-mdss-panels.dtsi
@@ -0,0 +1,106 @@
+/* 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 "dsi-panel-sim-video.dtsi"
+#include "dsi-panel-sim-dualmipi-video.dtsi"
+#include "dsi-panel-sim-cmd.dtsi"
+#include "dsi-panel-sim-dualmipi-cmd.dtsi"
+#include "dsi-panel-truly-1080p-video.dtsi"
+#include "dsi-panel-truly-1080p-cmd.dtsi"
+#include "dsi-adv7533-1080p.dtsi"
+#include "dsi-adv7533-720p.dtsi"
+#include "dsi-panel-r69006-1080p-video.dtsi"
+#include "dsi-panel-r69006-1080p-cmd.dtsi"
+#include "dsi-panel-truly-wuxga-video.dtsi"
+#include "dsi-panel-lt8912-480p-video.dtsi"
+#include "dsi-panel-lt8912-1080p-video.dtsi"
+
+&soc {
+ dsi_panel_pwr_supply: dsi_panel_pwr_supply {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,panel-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdd";
+ qcom,supply-min-voltage = <2850000>;
+ qcom,supply-max-voltage = <2850000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@1 {
+ reg = <1>;
+ qcom,supply-name = "vddio";
+ qcom,supply-min-voltage = <1800000>;
+ qcom,supply-max-voltage = <1800000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+ };
+};
+
+&dsi_truly_1080_vid {
+ qcom,mdss-dsi-panel-timings-phy-v2 = [23 1e 08 09 05 03 04 a0
+ 23 1e 08 09 05 03 04 a0
+ 23 1e 08 09 05 03 04 a0
+ 23 1e 08 09 05 03 04 a0
+ 23 1a 08 09 05 03 04 a0];
+};
+
+&dsi_truly_1080_cmd {
+ qcom,mdss-dsi-panel-timings-phy-v2 = [23 1e 08 09 05 03 04 a0
+ 23 1e 08 09 05 03 04 a0
+ 23 1e 08 09 05 03 04 a0
+ 23 1e 08 09 05 03 04 a0
+ 23 1a 08 09 05 03 04 a0];
+};
+
+&dsi_r69006_1080p_video {
+ qcom,mdss-dsi-panel-timings-phy-v2 = [24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1b 08 09 05 03 04 a0];
+};
+
+&dsi_r69006_1080p_cmd{
+ qcom,mdss-dsi-panel-timings-phy-v2 = [24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1b 08 09 05 03 04 a0];
+};
+
+&dsi_adv7533_1080p {
+ qcom,mdss-dsi-panel-timings-phy-v2 = [24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1b 08 09 05 03 04 a0];
+};
+
+&dsi_adv7533_720p {
+ qcom,mdss-dsi-panel-timings-phy-v2 = [1e 1b 04 06 02 03 04 a0
+ 1e 1b 04 06 02 03 04 a0
+ 1e 1b 04 06 02 03 04 a0
+ 1e 1b 04 06 02 03 04 a0
+ 1e 0e 04 05 02 03 04 a0];
+};
+
+&dsi_truly_wuxga_vid {
+ qcom,mdss-dsi-panel-timings-phy-v2 = [24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1f 08 09 05 03 04 a0
+ 24 1c 08 09 05 03 04 a0];
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mdss-pll.dtsi b/arch/arm64/boot/dts/qcom/msm8953-mdss-pll.dtsi
new file mode 100644
index 0000000..a279453
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-mdss-pll.dtsi
@@ -0,0 +1,84 @@
+/* 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.
+ */
+
+&soc {
+ mdss_dsi0_pll: qcom,mdss_dsi_pll@994400 {
+ compatible = "qcom,mdss_dsi_pll_8953";
+ label = "MDSS DSI 0 PLL";
+ cell-index = <0>;
+ #clock-cells = <1>;
+
+ reg = <0x01a94400 0x588>,
+ <0x0184d074 0x8>,
+ <0x01a94200 0x98>;
+ reg-names = "pll_base", "gdsc_base", "dynamic_pll_base";
+
+ gdsc-supply = <&gdsc_mdss>;
+
+ clocks = <&clock_gcc clk_gcc_mdss_ahb_clk>;
+ clock-names = "iface_clk";
+ clock-rate = <0>;
+
+ qcom,dsi-pll-ssc-en;
+ qcom,dsi-pll-ssc-mode = "down-spread";
+ /* Memory region for passing dynamic refresh pll codes */
+ memory-region = <&dfps_data_mem>;
+
+ qcom,platform-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,platform-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "gdsc";
+ qcom,supply-min-voltage = <0>;
+ qcom,supply-max-voltage = <0>;
+ qcom,supply-enable-load = <0>;
+ qcom,supply-disable-load = <0>;
+ };
+ };
+ };
+
+ mdss_dsi1_pll: qcom,mdss_dsi_pll@996400 {
+ compatible = "qcom,mdss_dsi_pll_8953";
+ label = "MDSS DSI 1 PLL";
+ cell-index = <1>;
+ #clock-cells = <1>;
+
+ reg = <0x01a96400 0x588>,
+ <0x0184d074 0x8>,
+ <0x01a96200 0x98>;
+ reg-names = "pll_base", "gdsc_base", "dynamic_pll_base";
+
+ gdsc-supply = <&gdsc_mdss>;
+
+ qcom,dsi-pll-ssc-en;
+ qcom,dsi-pll-ssc-mode = "down-spread";
+ clocks = <&clock_gcc clk_gcc_mdss_ahb_clk>;
+ clock-names = "iface_clk";
+ clock-rate = <0>;
+
+ qcom,platform-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,platform-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "gdsc";
+ qcom,supply-min-voltage = <0>;
+ qcom,supply-max-voltage = <0>;
+ qcom,supply-enable-load = <0>;
+ qcom,supply-disable-load = <0>;
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mdss.dtsi b/arch/arm64/boot/dts/qcom/msm8953-mdss.dtsi
new file mode 100644
index 0000000..310da1f
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-mdss.dtsi
@@ -0,0 +1,434 @@
+/* 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.
+ */
+
+&soc {
+ mdss_mdp: qcom,mdss_mdp@1a00000 {
+ compatible = "qcom,mdss_mdp";
+ reg = <0x01a00000 0x90000>,
+ <0x01ab0000 0x1040>;
+ reg-names = "mdp_phys", "vbif_phys";
+ interrupts = <0 72 0>;
+ vdd-supply = <&gdsc_mdss>;
+
+ /* Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_mdp";
+ qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>,
+ <22 512 0 6400000>,
+ <22 512 0 6400000>;
+
+ /* Fudge factors */
+ qcom,mdss-ab-factor = <1 1>; /* 1 time */
+ qcom,mdss-ib-factor = <1 1>; /* 1 time */
+ qcom,mdss-clk-factor = <105 100>; /* 1.05 times */
+
+ qcom,max-mixer-width = <2048>;
+ qcom,max-pipe-width = <2048>;
+
+ /* VBIF QoS remapper settings*/
+ qcom,mdss-vbif-qos-rt-setting = <1 2 2 2>;
+ qcom,mdss-vbif-qos-nrt-setting = <1 1 1 1>;
+
+ qcom,mdss-has-panic-ctrl;
+ qcom,mdss-per-pipe-panic-luts = <0x000f>,
+ <0xffff>,
+ <0xfffc>,
+ <0xff00>;
+
+ qcom,mdss-mdp-reg-offset = <0x00001000>;
+ qcom,max-bandwidth-low-kbps = <3400000>;
+ qcom,max-bandwidth-high-kbps = <3400000>;
+ qcom,max-bandwidth-per-pipe-kbps = <2300000>;
+ qcom,max-clk-rate = <400000000>;
+ qcom,mdss-default-ot-rd-limit = <32>;
+ qcom,mdss-default-ot-wr-limit = <16>;
+
+ /* Bandwidth limit settings */
+ qcom,max-bw-settings = <1 3400000>, /* Default */
+ <2 3100000>; /* Camera */
+
+ qcom,mdss-pipe-vig-off = <0x00005000>;
+ qcom,mdss-pipe-rgb-off = <0x00015000 0x00017000>;
+ qcom,mdss-pipe-dma-off = <0x00025000>;
+ qcom,mdss-pipe-cursor-off = <0x00035000>;
+
+ qcom,mdss-pipe-vig-xin-id = <0>;
+ qcom,mdss-pipe-rgb-xin-id = <1 5>;
+ qcom,mdss-pipe-dma-xin-id = <2>;
+ qcom,mdss-pipe-cursor-xin-id = <7>;
+
+ /* Offsets relative to "mdp_phys + mdp-reg-offset" address */
+ qcom,mdss-pipe-vig-clk-ctrl-offsets = <0x2aC 0 0>;
+ qcom,mdss-pipe-rgb-clk-ctrl-offsets = <0x2aC 4 8>,
+ <0x2b4 4 8>;
+ qcom,mdss-pipe-dma-clk-ctrl-offsets = <0x2ac 8 12>;
+ qcom,mdss-pipe-cursor-clk-ctrl-offsets = <0x3a8 16 15>;
+
+
+ qcom,mdss-ctl-off = <0x00002000 0x00002200 0x00002400>;
+ qcom,mdss-mixer-intf-off = <0x00045000 0x00046000>;
+ qcom,mdss-dspp-off = <0x00055000>;
+ qcom,mdss-wb-off = <0x00065000 0x00066000>;
+ qcom,mdss-intf-off = <0x0006b000 0x0006b800 0x0006c000>;
+ qcom,mdss-pingpong-off = <0x00071000 0x00071800>;
+ qcom,mdss-slave-pingpong-off = <0x00073000>;
+ qcom,mdss-cdm-off = <0x0007a200>;
+ qcom,mdss-wfd-mode = "intf";
+ qcom,mdss-highest-bank-bit = <0x1>;
+ qcom,mdss-has-decimation;
+ qcom,mdss-has-non-scalar-rgb;
+ qcom,mdss-has-rotator-downscale;
+ qcom,mdss-rot-downscale-min = <2>;
+ qcom,mdss-rot-downscale-max = <16>;
+ qcom,mdss-idle-power-collapse-enabled;
+ qcom,mdss-rot-block-size = <64>;
+ qcom,mdss-ppb-off = <0x00000330>;
+ qcom,mdss-has-pingpong-split;
+
+ clocks = <&clock_gcc clk_gcc_mdss_ahb_clk>,
+ <&clock_gcc clk_gcc_mdss_axi_clk>,
+ <&clock_gcc clk_mdp_clk_src>,
+ <&clock_gcc_mdss clk_mdss_mdp_vote_clk>,
+ <&clock_gcc clk_gcc_mdss_vsync_clk>;
+ clock-names = "iface_clk", "bus_clk", "core_clk_src",
+ "core_clk", "vsync_clk";
+
+ qcom,mdp-settings = <0x0506c 0x00000000>,
+ <0x1506c 0x00000000>,
+ <0x1706c 0x00000000>,
+ <0x2506c 0x00000000>;
+
+ qcom,vbif-settings = <0x0d0 0x00000010>;
+
+ qcom,regs-dump-mdp = <0x01000 0x01454>,
+ <0x02000 0x02064>,
+ <0x02200 0x02264>,
+ <0x02400 0x02464>,
+ <0x05000 0x05150>,
+ <0x05200 0x05230>,
+ <0x15000 0x15150>,
+ <0x17000 0x17150>,
+ <0x25000 0x25150>,
+ <0x35000 0x35150>,
+ <0x45000 0x452bc>,
+ <0x46000 0x462bc>,
+ <0x55000 0x5522c>,
+ <0x65000 0x652c0>,
+ <0x66000 0x662c0>,
+ <0x6b800 0x6ba68>,
+ <0x6c000 0x6c268>,
+ <0x71000 0x710d4>,
+ <0x71800 0x718d4>;
+
+ qcom,regs-dump-names-mdp = "MDP",
+ "CTL_0", "CTL_1", "CTL_2",
+ "VIG0_SSPP", "VIG0",
+ "RGB0_SSPP", "RGB1_SSPP",
+ "DMA0_SSPP",
+ "CURSOR0_SSPP",
+ "LAYER_0", "LAYER_1",
+ "DSPP_0",
+ "WB_0", "WB_2",
+ "INTF_1", "INTF_2",
+ "PP_0", "PP_1";
+
+ /* buffer parameters to calculate prefill bandwidth */
+ qcom,mdss-prefill-outstanding-buffer-bytes = <0>;
+ qcom,mdss-prefill-y-buffer-bytes = <0>;
+ qcom,mdss-prefill-scaler-buffer-lines-bilinear = <2>;
+ qcom,mdss-prefill-scaler-buffer-lines-caf = <4>;
+ qcom,mdss-prefill-post-scaler-buffer-pixels = <2048>;
+ qcom,mdss-prefill-pingpong-buffer-pixels = <4096>;
+
+ qcom,mdss-pp-offsets {
+ qcom,mdss-sspp-mdss-igc-lut-off = <0x2000>;
+ qcom,mdss-sspp-vig-pcc-off = <0x1780>;
+ qcom,mdss-sspp-rgb-pcc-off = <0x380>;
+ qcom,mdss-sspp-dma-pcc-off = <0x380>;
+ qcom,mdss-lm-pgc-off = <0x3c0>;
+ qcom,mdss-dspp-pcc-off = <0x1700>;
+ qcom,mdss-dspp-pgc-off = <0x17c0>;
+ };
+
+ qcom,mdss-reg-bus {
+ /* Reg Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_reg";
+ qcom,msm-bus,num-cases = <4>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,vectors-KBps =
+ <1 590 0 0>,
+ <1 590 0 76800>,
+ <1 590 0 160000>,
+ <1 590 0 320000>;
+ };
+
+ qcom,mdss-hw-rt-bus {
+ /* Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_hw_rt";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>,
+ <22 512 0 1000>;
+ };
+
+ smmu_mdp_unsec: qcom,smmu_mdp_unsec_cb {
+ compatible = "qcom,smmu_mdp_unsec";
+ iommus = <&apps_iommu 0xC00 0>; /* For NS ctx bank */
+ };
+ smmu_mdp_sec: qcom,smmu_mdp_sec_cb {
+ compatible = "qcom,smmu_mdp_sec";
+ iommus = <&apps_iommu 0xC01 0>; /* For SEC Ctx Bank */
+ };
+
+ mdss_fb0: qcom,mdss_fb_primary {
+ cell-index = <0>;
+ compatible = "qcom,mdss-fb";
+ qcom,cont-splash-memory {
+ linux,contiguous-region = <&cont_splash_mem>;
+ };
+ };
+
+ mdss_fb1: qcom,mdss_fb_wfd {
+ cell-index = <1>;
+ compatible = "qcom,mdss-fb";
+ };
+
+ mdss_fb2: qcom,mdss_fb_secondary {
+ cell-index = <2>;
+ compatible = "qcom,mdss-fb";
+ };
+ };
+
+ mdss_dsi: qcom,mdss_dsi@0 {
+ compatible = "qcom,mdss-dsi";
+ hw-config = "single_dsi";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ gdsc-supply = <&gdsc_mdss>;
+ vdda-supply = <&pm8953_s3>;
+ vcca-supply = <&pm8953_l3>;
+
+ /* Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_dsi";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>,
+ <22 512 0 1000>;
+
+ ranges = <0x1a94000 0x1a94000 0x400
+ 0x1a94400 0x1a94400 0x588
+ 0x193e000 0x193e000 0x30
+ 0x1a96000 0x1a96000 0x400
+ 0x1a96400 0x1a96400 0x588
+ 0x193e000 0x193e000 0x30>;
+
+ clocks = <&clock_gcc_mdss clk_mdss_mdp_vote_clk>,
+ <&clock_gcc clk_gcc_mdss_ahb_clk>,
+ <&clock_gcc clk_gcc_mdss_axi_clk>,
+ <&clock_gcc_mdss clk_ext_byte0_clk_src>,
+ <&clock_gcc_mdss clk_ext_byte1_clk_src>,
+ <&clock_gcc_mdss clk_ext_pclk0_clk_src>,
+ <&clock_gcc_mdss clk_ext_pclk1_clk_src>;
+ clock-names = "mdp_core_clk", "iface_clk", "bus_clk",
+ "ext_byte0_clk", "ext_byte1_clk", "ext_pixel0_clk",
+ "ext_pixel1_clk";
+
+ qcom,mmss-ulp-clamp-ctrl-offset = <0x20>;
+ qcom,mmss-phyreset-ctrl-offset = <0x24>;
+
+ qcom,mdss-fb-map-prim = <&mdss_fb0>;
+ qcom,mdss-fb-map-sec = <&mdss_fb2>;
+
+ qcom,core-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,core-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "gdsc";
+ qcom,supply-min-voltage = <0>;
+ qcom,supply-max-voltage = <0>;
+ qcom,supply-enable-load = <0>;
+ qcom,supply-disable-load = <0>;
+ };
+ };
+
+ qcom,ctrl-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,ctrl-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vdda";
+ qcom,supply-min-voltage = <1225000>;
+ qcom,supply-max-voltage = <1225000>;
+ qcom,supply-enable-load = <18160>;
+ qcom,supply-disable-load = <1>;
+ };
+ };
+
+ qcom,phy-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,phy-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "vcca";
+ qcom,supply-min-voltage = <925000>;
+ qcom,supply-max-voltage = <925000>;
+ qcom,supply-enable-load = <17000>;
+ qcom,supply-disable-load = <32>;
+ };
+ };
+
+ mdss_dsi0: qcom,mdss_dsi_ctrl0@1a94000 {
+ compatible = "qcom,mdss-dsi-ctrl";
+ label = "MDSS DSI CTRL->0";
+ cell-index = <0>;
+ reg = <0x1a94000 0x400>,
+ <0x1a94400 0x580>,
+ <0x193e000 0x30>;
+ reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
+
+ qcom,timing-db-mode;
+ qcom,mdss-mdp = <&mdss_mdp>;
+ vdd-supply = <&pm8953_l17>;
+ vddio-supply = <&pm8953_l6>;
+
+ clocks = <&clock_gcc_mdss clk_gcc_mdss_byte0_clk>,
+ <&clock_gcc_mdss clk_gcc_mdss_pclk0_clk>,
+ <&clock_gcc clk_gcc_mdss_esc0_clk>,
+ <&clock_gcc_mdss clk_byte0_clk_src>,
+ <&clock_gcc_mdss clk_pclk0_clk_src>,
+ <&mdss_dsi0_pll clk_dsi0pll_byte_clk_mux>,
+ <&mdss_dsi0_pll clk_dsi0pll_pixel_clk_mux>,
+ <&mdss_dsi0_pll clk_dsi0pll_byte_clk_src>,
+ <&mdss_dsi0_pll clk_dsi0pll_pixel_clk_src>,
+ <&mdss_dsi0_pll
+ clk_dsi0pll_shadow_byte_clk_src>,
+ <&mdss_dsi0_pll
+ clk_dsi0pll_shadow_pixel_clk_src>;
+ clock-names = "byte_clk", "pixel_clk", "core_clk",
+ "byte_clk_rcg", "pixel_clk_rcg",
+ "pll_byte_clk_mux", "pll_pixel_clk_mux",
+ "pll_byte_clk_src", "pll_pixel_clk_src",
+ "pll_shadow_byte_clk_src",
+ "pll_shadow_pixel_clk_src";
+
+ qcom,platform-strength-ctrl = [ff 06
+ ff 06
+ ff 06
+ ff 06
+ ff 00];
+ qcom,platform-regulator-settings = [1d
+ 1d 1d 1d 1d];
+ qcom,platform-lane-config = [00 00 10 0f
+ 00 00 10 0f
+ 00 00 10 0f
+ 00 00 10 0f
+ 00 00 10 8f];
+ };
+
+ mdss_dsi1: qcom,mdss_dsi_ctrl1@1a96000 {
+ compatible = "qcom,mdss-dsi-ctrl";
+ label = "MDSS DSI CTRL->1";
+ cell-index = <1>;
+ reg = <0x1a96000 0x400>,
+ <0x1a96400 0x588>,
+ <0x193e000 0x30>;
+ reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
+
+ qcom,mdss-mdp = <&mdss_mdp>;
+ vdd-supply = <&pm8953_l17>;
+ vddio-supply = <&pm8953_l6>;
+
+ clocks = <&clock_gcc_mdss clk_gcc_mdss_byte1_clk>,
+ <&clock_gcc_mdss clk_gcc_mdss_pclk1_clk>,
+ <&clock_gcc clk_gcc_mdss_esc1_clk>,
+ <&clock_gcc_mdss clk_byte1_clk_src>,
+ <&clock_gcc_mdss clk_pclk1_clk_src>,
+ <&mdss_dsi1_pll clk_dsi1pll_byte_clk_mux>,
+ <&mdss_dsi1_pll clk_dsi1pll_pixel_clk_mux>,
+ <&mdss_dsi1_pll clk_dsi1pll_byte_clk_src>,
+ <&mdss_dsi1_pll clk_dsi1pll_pixel_clk_src>,
+ <&mdss_dsi1_pll
+ clk_dsi1pll_shadow_byte_clk_src>,
+ <&mdss_dsi1_pll
+ clk_dsi1pll_shadow_pixel_clk_src>;
+ clock-names = "byte_clk", "pixel_clk", "core_clk",
+ "byte_clk_rcg", "pixel_clk_rcg",
+ "pll_byte_clk_mux", "pll_pixel_clk_mux",
+ "pll_byte_clk_src", "pll_pixel_clk_src",
+ "pll_shadow_byte_clk_src",
+ "pll_shadow_pixel_clk_src";
+
+ qcom,timing-db-mode;
+ qcom,platform-strength-ctrl = [ff 06
+ ff 06
+ ff 06
+ ff 06
+ ff 00];
+ qcom,platform-regulator-settings = [1d
+ 1d 1d 1d 1d];
+ qcom,platform-lane-config = [00 00 10 0f
+ 00 00 10 0f
+ 00 00 10 0f
+ 00 00 10 0f
+ 00 00 10 8f];
+ };
+ };
+
+ qcom,mdss_wb_panel {
+ compatible = "qcom,mdss_wb";
+ qcom,mdss_pan_res = <640 640>;
+ qcom,mdss_pan_bpp = <24>;
+ qcom,mdss-fb-map = <&mdss_fb1>;
+ };
+
+ mdss_rotator: qcom,mdss_rotator {
+ compatible = "qcom,mdss_rotator";
+ qcom,mdss-wb-count = <1>;
+ qcom,mdss-has-downscale;
+ qcom,mdss-has-ubwc;
+ /* Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_rotator";
+ qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>,
+ <22 512 0 6400000>,
+ <22 512 0 6400000>;
+
+ rot-vdd-supply = <&gdsc_mdss>;
+ qcom,supply-names = "rot-vdd";
+ qcom,mdss-has-reg-bus;
+ clocks = <&clock_gcc clk_gcc_mdss_ahb_clk>,
+ <&clock_gcc_mdss clk_mdss_rotator_vote_clk>;
+ clock-names = "iface_clk", "rot_core_clk";
+
+ qcom,mdss-rot-reg-bus {
+ /* Reg Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_rot_reg";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,vectors-KBps =
+ <1 590 0 0>,
+ <1 590 0 76800>;
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi b/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
index 87b8c74..8212cc8 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -74,3 +74,51 @@
status = "ok";
};
+
+#include "msm8953-mdss-panels.dtsi"
+
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_truly_1080_vid>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+
+ qcom,platform-te-gpio = <&tlmm 24 0>;
+ qcom,platform-reset-gpio = <&tlmm 61 0>;
+ qcom,platform-bklight-en-gpio = <&tlmm 59 0>;
+};
+
+&mdss_dsi1 {
+ status = "disabled";
+ qcom,dsi-pref-prim-pan = <&dsi_adv7533_1080p>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+
+ qcom,pluggable;
+ qcom,platform-te-gpio = <&tlmm 24 0>;
+ qcom,platform-reset-gpio = <&tlmm 61 0>;
+ qcom,platform-bklight-en-gpio = <&tlmm 59 0>;
+};
+
+&dsi_truly_1080_vid {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
+};
+
+&dsi_truly_1080_cmd {
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,ulps-enabled;
+ qcom,partial-update-enabled;
+ qcom,panel-roi-alignment = <2 2 4 2 1080 2>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pmi632-cdp-s2.dts b/arch/arm64/boot/dts/qcom/msm8953-pmi632-cdp-s2.dts
new file mode 100644
index 0000000..78ff97f
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi632-cdp-s2.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8953.dtsi"
+#include "sdm450-pmi632-cdp-s2.dtsi"
+#include "sdm450-pmi632.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. msm8953 + PMI632 CDP S2";
+ compatible = "qcom,msm8953-cdp", "qcom,msm8953", "qcom,cdp";
+ qcom,board-id = <1 2>;
+ qcom,pmic-id = <0x010016 0x25 0xC 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pmi632.dts b/arch/arm64/boot/dts/qcom/msm8953-pmi632.dts
new file mode 100644
index 0000000..2ffb0ab
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi632.dts
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8953.dtsi"
+#include "sdm450-pmi632.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. msm8953 + PMI632 SOC";
+ compatible = "qcom,msm8953";
+ qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
+ qcom,pmic-name = "PMI632";
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pmi8937.dtsi b/arch/arm64/boot/dts/qcom/msm8953-pmi8937.dtsi
index a178aa1..a208e1a 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-pmi8937.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi8937.dtsi
@@ -24,6 +24,7 @@
&usb3 {
vbus_dwc3-supply = <&smbcharger_charger_otg>;
+ extcon = <&pmi8937_charger>;
};
&pmi8937_charger {
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pmi8940.dtsi b/arch/arm64/boot/dts/qcom/msm8953-pmi8940.dtsi
index 71d6941..28fc0d7 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-pmi8940.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi8940.dtsi
@@ -24,6 +24,7 @@
&usb3 {
vbus_dwc3-supply = <&smbcharger_charger_otg>;
+ extcon = <&pmi8940_charger>;
};
&labibb {
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pmi8950.dtsi b/arch/arm64/boot/dts/qcom/msm8953-pmi8950.dtsi
index b9c2a18..139ef1e 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-pmi8950.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi8950.dtsi
@@ -29,6 +29,7 @@
&usb3 {
vbus_dwc3-supply = <&smbcharger_charger_otg>;
+ extcon = <&pmi8950_charger>;
};
&pmi8950_charger {
@@ -36,3 +37,32 @@
qcom,typec-psy-name = "typec";
};
+&mdss_dsi0 {
+ lab-supply = <&lab_regulator>;
+ ibb-supply = <&ibb_regulator>;
+};
+
+&mdss_dsi1 {
+ lab-supply = <&lab_regulator>;
+ ibb-supply = <&ibb_regulator>;
+};
+
+&dsi_panel_pwr_supply {
+ qcom,panel-supply-entry@2 {
+ reg = <2>;
+ qcom,supply-name = "lab";
+ qcom,supply-min-voltage = <4600000>;
+ qcom,supply-max-voltage = <6000000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+ qcom,panel-supply-entry@3 {
+ reg = <3>;
+ qcom,supply-name = "ibb";
+ qcom,supply-min-voltage = <4600000>;
+ qcom,supply-max-voltage = <6000000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ qcom,supply-post-on-sleep = <10>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi b/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
index 87b8c74..b29e447 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -11,6 +11,45 @@
* GNU General Public License for more details.
*/
+
+&soc {
+ i2c@78b7000 { /* BLSP1 QUP3 */
+ status = "okay";
+ synaptics@4b {
+ compatible = "synaptics,dsx-i2c";
+ reg = <0x4b>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <65 0x2008>;
+ vdd_ana-supply = <&vdd_vreg>;
+ vcc_i2c-supply = <&pm8953_l6>;
+ synaptics,pwr-reg-name = "vdd_ana";
+ synaptics,bus-reg-name = "vcc_i2c";
+ synaptics,irq-gpio = <&tlmm 65 0x2008>;
+ synaptics,irq-on-state = <0>;
+ synaptics,irq-flags = <0x2008>;
+ synaptics,power-delay-ms = <200>;
+ synaptics,reset-delay-ms = <200>;
+ synaptics,max-y-for-2d = <1919>;
+ synaptics,cap-button-codes = <139 158 172>;
+ synaptics,vir-button-codes = <139 180 2000 320 160
+ 158 540 2000 320 160
+ 172 900 2000 320 160>;
+ synaptics,resume-in-workqueue;
+ /* Underlying clocks used by secure touch */
+ clock-names = "iface_clk", "core_clk";
+ clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>,
+ <&clock_gcc clk_gcc_blsp1_qup3_i2c_apps_clk>;
+ };
+ };
+
+ vdd_vreg: vdd_vreg {
+ compatible = "regulator-fixed";
+ status = "ok";
+ regulator-name = "vdd_vreg";
+ };
+
+};
+
&blsp1_uart0 {
status = "ok";
pinctrl-names = "default";
diff --git a/arch/arm64/boot/dts/qcom/msm8953.dtsi b/arch/arm64/boot/dts/qcom/msm8953.dtsi
index a142840..2861fd5 100644
--- a/arch/arm64/boot/dts/qcom/msm8953.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953.dtsi
@@ -113,8 +113,9 @@
};
dfps_data_mem: dfps_data_mem@90000000 {
- reg = <0 0x90000000 0 0x1000>;
- label = "dfps_data_mem";
+ reg = <0 0x90000000 0 0x1000>;
+ label = "dfps_data_mem";
+ status = "disabled";
};
cont_splash_mem: splash_region@0x90001000 {
@@ -164,6 +165,8 @@
#include "msm8953-ion.dtsi"
#include "msm-arm-smmu-8953.dtsi"
#include "msm8953-gpu.dtsi"
+#include "msm8953-mdss.dtsi"
+#include "msm8953-mdss-pll.dtsi"
&soc {
#address-cells = <1>;
@@ -651,6 +654,19 @@
status = "disabled";
};
+ clock_gcc_mdss: qcom,gcc-mdss@1800000 {
+ compatible = "qcom,gcc-mdss-8953";
+ reg = <0x1800000 0x80000>;
+ reg-names = "cc_base";
+ clock-names = "pclk0_src", "pclk1_src",
+ "byte0_src", "byte1_src";
+ clocks = <&mdss_dsi0_pll clk_dsi0pll_pixel_clk_mux>,
+ <&mdss_dsi1_pll clk_dsi1pll_pixel_clk_mux>,
+ <&mdss_dsi0_pll clk_dsi0pll_byte_clk_mux>,
+ <&mdss_dsi1_pll clk_dsi1pll_byte_clk_mux>;
+ #clock-cells = <1>;
+ };
+
clock_gcc: qcom,gcc@1800000 {
compatible = "qcom,gcc-8953";
reg = <0x1800000 0x80000>,
@@ -675,6 +691,8 @@
reg = <0x1800000 0x80000>;
reg-names = "cc_base";
vdd_gfx-supply = <&gfx_vreg_corner>;
+ clocks = <&clock_gcc clk_xo_clk_src>;
+ clock-names = "xo";
qcom,gfxfreq-corner =
< 0 0 >,
< 133330000 1 >, /* Min SVS */
@@ -1162,10 +1180,11 @@
label = "modem";
};
- mem_client_3_size: qcom,client_3 {
+ qcom,client_3 {
compatible = "qcom,memshare-peripheral";
- qcom,peripheral-size = <0x0>;
+ qcom,peripheral-size = <0x500000>;
qcom,client-id = <1>;
+ qcom,allocate-boot-time;
label = "modem";
};
};
@@ -1279,6 +1298,22 @@
status = "disabled";
};
+ qcom,msm-adsprpc-mem {
+ compatible = "qcom,msm-adsprpc-mem-region";
+ memory-region = <&adsp_mem>;
+ };
+
+ qcom,msm_fastrpc {
+ compatible = "qcom,msm-fastrpc-legacy-compute";
+ qcom,msm_fastrpc_compute_cb {
+ compatible = "qcom,msm-fastrpc-legacy-compute-cb";
+ label = "adsprpc-smd";
+ iommus = <&apps_iommu 0x2408 0x7>;
+ sids = <0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf>;
+ };
+ };
+
+
ipa_hw: qcom,ipa@07900000 {
compatible = "qcom,ipa";
reg = <0x07900000 0x4effc>, <0x07904000 0x26934>;
@@ -1727,6 +1762,7 @@
vdd-supply = <&gdsc_venus>;
qcom,proxy-reg-names = "vdd";
+ qcom,mas-crypto = <&mas_crypto>;
clocks = <&clock_gcc clk_gcc_venus0_vcodec0_clk>,
<&clock_gcc clk_gcc_venus0_ahb_clk>,
@@ -1757,6 +1793,89 @@
qcom,firmware-name = "venus";
memory-region = <&venus_mem>;
};
+
+ qcom,wcnss-wlan@0a000000 {
+ compatible = "qcom,wcnss_wlan";
+ reg = <0x0a000000 0x280000>,
+ <0x0b011008 0x04>,
+ <0x0a21b000 0x3000>,
+ <0x03204000 0x00000100>,
+ <0x03200800 0x00000200>,
+ <0x0a100400 0x00000200>,
+ <0x0a205050 0x00000200>,
+ <0x0a219000 0x00000020>,
+ <0x0a080488 0x00000008>,
+ <0x0a080fb0 0x00000008>,
+ <0x0a08040c 0x00000008>,
+ <0x0a0120a8 0x00000008>,
+ <0x0a012448 0x00000008>,
+ <0x0a080c00 0x00000001>;
+
+ reg-names = "wcnss_mmio", "wcnss_fiq",
+ "pronto_phy_base", "riva_phy_base",
+ "riva_ccu_base", "pronto_a2xb_base",
+ "pronto_ccpu_base", "pronto_saw2_base",
+ "wlan_tx_phy_aborts","wlan_brdg_err_source",
+ "wlan_tx_status", "alarms_txctl",
+ "alarms_tactl", "pronto_mcu_base";
+
+ interrupts = <0 145 0 0 146 0>;
+ interrupt-names = "wcnss_wlantx_irq", "wcnss_wlanrx_irq";
+
+ qcom,pronto-vddmx-supply = <&pm8953_s7_level_ao>;
+ qcom,pronto-vddcx-supply = <&pm8953_s2_level>;
+ qcom,pronto-vddpx-supply = <&pm8953_l5>;
+ qcom,iris-vddxo-supply = <&pm8953_l7>;
+ qcom,iris-vddrfa-supply = <&pm8953_l19>;
+ qcom,iris-vddpa-supply = <&pm8953_l9>;
+ qcom,iris-vdddig-supply = <&pm8953_l5>;
+
+ qcom,iris-vddxo-voltage-level = <1800000 0 1800000>;
+ qcom,iris-vddrfa-voltage-level = <1300000 0 1300000>;
+ qcom,iris-vddpa-voltage-level = <3300000 0 3300000>;
+ qcom,iris-vdddig-voltage-level = <1800000 0 1800000>;
+
+ qcom,vddmx-voltage-level = <RPM_SMD_REGULATOR_LEVEL_TURBO
+ RPM_SMD_REGULATOR_LEVEL_NONE
+ RPM_SMD_REGULATOR_LEVEL_TURBO>;
+ qcom,vddcx-voltage-level = <RPM_SMD_REGULATOR_LEVEL_NOM
+ RPM_SMD_REGULATOR_LEVEL_NONE
+ RPM_SMD_REGULATOR_LEVEL_TURBO>;
+ qcom,vddpx-voltage-level = <1800000 0 1800000>;
+
+ qcom,iris-vddxo-current = <10000>;
+ qcom,iris-vddrfa-current = <100000>;
+ qcom,iris-vddpa-current = <515000>;
+ qcom,iris-vdddig-current = <10000>;
+
+ qcom,pronto-vddmx-current = <0>;
+ qcom,pronto-vddcx-current = <0>;
+ qcom,pronto-vddpx-current = <0>;
+
+ pinctrl-names = "wcnss_default", "wcnss_sleep",
+ "wcnss_gpio_default";
+ pinctrl-0 = <&wcnss_default>;
+ pinctrl-1 = <&wcnss_sleep>;
+ pinctrl-2 = <&wcnss_gpio_default>;
+
+ gpios = <&tlmm 76 0>, <&tlmm 77 0>, <&tlmm 78 0>,
+ <&tlmm 79 0>, <&tlmm 80 0>;
+
+ clocks = <&clock_gcc clk_xo_wlan_clk>,
+ <&clock_gcc clk_rf_clk2>,
+ <&clock_debug clk_gcc_debug_mux>,
+ <&clock_gcc clk_wcnss_m_clk>;
+
+ clock-names = "xo", "rf_clk", "measure", "wcnss_debug";
+
+ qcom,has-autodetect-xo;
+ qcom,is-pronto-v3;
+ qcom,has-pronto-hw;
+ qcom,has-vsys-adc-channel;
+ qcom,has-a2xb-split-reg;
+ qcom,wcnss-adc_tm = <&pm8953_adc_tm>;
+ };
+
};
#include "pm8953-rpm-regulator.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/pm660.dtsi b/arch/arm64/boot/dts/qcom/pm660.dtsi
index 502b2fe..903f170a 100644
--- a/arch/arm64/boot/dts/qcom/pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm660.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -218,7 +218,6 @@
qcom,scale-function = <2>;
qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
- qcom,vadc-thermal-node;
};
chan@4f {
@@ -230,7 +229,6 @@
qcom,scale-function = <2>;
qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <0>;
- qcom,vadc-thermal-node;
};
chan@1d {
@@ -376,7 +374,7 @@
#clock-cells = <1>;
qcom,cxo-freq = <19200000>;
qcom,clkdiv-id = <1>;
- qcom,clkdiv-init-freq = <19200000>;
+ qcom,clkdiv-init-freq = <9600000>;
status = "disabled";
};
};
diff --git a/arch/arm64/boot/dts/qcom/pmi632.dtsi b/arch/arm64/boot/dts/qcom/pmi632.dtsi
index 074b7da..e5963ef 100644
--- a/arch/arm64/boot/dts/qcom/pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi632.dtsi
@@ -225,9 +225,8 @@
pmi632_vib: qcom,vibrator@5700 {
compatible = "qcom,qpnp-vibrator-ldo";
reg = <0x5700 0x100>;
- qcom,vib-ldo-volt-uv = <1504000>;
+ qcom,vib-ldo-volt-uv = <3000000>;
qcom,vib-overdrive-volt-uv = <3544000>;
- status = "disabled";
};
pmi632_pwm_1: pwm@b300 {
diff --git a/arch/arm64/boot/dts/qcom/pmi8950.dtsi b/arch/arm64/boot/dts/qcom/pmi8950.dtsi
index 97be32de..bab5774 100644
--- a/arch/arm64/boot/dts/qcom/pmi8950.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi8950.dtsi
@@ -11,6 +11,8 @@
*/
#include <dt-bindings/msm/power-on.h>
+#include <dt-bindings/spmi/spmi.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
&spmi_bus {
qcom,pmi8950@2 {
diff --git a/arch/arm64/boot/dts/qcom/qcs605.dtsi b/arch/arm64/boot/dts/qcom/qcs605.dtsi
index 6cf9a82..fcde397 100644
--- a/arch/arm64/boot/dts/qcom/qcs605.dtsi
+++ b/arch/arm64/boot/dts/qcom/qcs605.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,6 +18,30 @@
qcom,msm-id = <347 0x0>;
};
+&pil_modem_mem {
+ reg = <0 0x8b000000 0 0x3c00000>;
+};
+
+&pil_video_mem {
+ reg = <0 0x8ec00000 0 0x500000>;
+};
+
+&wlan_msa_mem {
+ reg = <0 0x8f100000 0 0x100000>;
+};
+
+&pil_cdsp_mem {
+ reg = <0 0x8f200000 0 0x800000>;
+};
+
+&pil_mba_mem {
+ reg = <0 0x8fa00000 0 0x200000>;
+};
+
+&pil_adsp_mem {
+ reg = <0 0x8fc00000 0 0x1e00000>;
+};
+
&soc {
qcom,rmnet-ipa {
status = "disabled";
diff --git a/arch/arm64/boot/dts/qcom/sdm450-cdp-s2-overlay.dts b/arch/arm64/boot/dts/qcom/sdm450-cdp-s2-overlay.dts
new file mode 100644
index 0000000..e12ad51
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm450-cdp-s2-overlay.dts
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include "sdm450-pmi632-cdp-s2.dtsi"
+
+/ {
+ model = "CDP S2";
+ compatible = "qcom,cdp";
+ qcom,board-id = <1 2>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm450-mtp-s3-overlay.dts b/arch/arm64/boot/dts/qcom/sdm450-mtp-s3-overlay.dts
new file mode 100644
index 0000000..ae522a5
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm450-mtp-s3-overlay.dts
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include "sdm450-pmi632-mtp-s3.dtsi"
+
+/ {
+ model = "MTP S3";
+ compatible = "qcom,mtp";
+ qcom,board-id = <8 3>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dts b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dts
new file mode 100644
index 0000000..4f6e7f5
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dts
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sdm450.dtsi"
+#include "sdm450-pmi632.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM450 + PMI632 SOC";
+ compatible = "qcom,sdm450";
+ qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
+ qcom,pmic-name = "PMI632";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
index 88a4ce7..413612d 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
@@ -21,3 +21,8 @@
&pm8953_typec {
status = "disabled";
};
+
+&pmi632_pon {
+ qcom,ps-hold-hard-reset-disable;
+ qcom,ps-hold-shutdown-disable;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4-overlay.dts b/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4-overlay.dts
new file mode 100644
index 0000000..558c3c6
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4-overlay.dts
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include "sdm450-qrd-sku4.dtsi"
+
+/ {
+ model = "QRD SKU4";
+ compatible = "qcom,qrd";
+ qcom,board-id = <0xb 1>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm632-cdp-s2.dts b/arch/arm64/boot/dts/qcom/sdm632-cdp-s2.dts
new file mode 100644
index 0000000..903b432
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm632-cdp-s2.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sdm632.dtsi"
+#include "sdm450-pmi632-cdp-s2.dtsi"
+#include "sdm450-pmi632.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM632 + PMI632 + PMI8004 CDP S2";
+ compatible = "qcom,sdm632-cdp", "qcom,sdm632", "qcom,cdp";
+ qcom,board-id = <1 2>;
+ qcom,pmic-id = <0x010016 0x25 0xC 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm632-mtp-s3.dts b/arch/arm64/boot/dts/qcom/sdm632-mtp-s3.dts
new file mode 100644
index 0000000..6339c3c
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm632-mtp-s3.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sdm632.dtsi"
+#include "sdm450-pmi632-mtp-s3.dtsi"
+#include "sdm450-pmi632.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM632 + PMI632 + PMI8004 MTP S3";
+ compatible = "qcom,sdm632-mtp", "qcom,sdm632", "qcom,mtp";
+ qcom,board-id = <8 3>;
+ qcom,pmic-id = <0x010016 0x25 0xC 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm632-qrd-sku4.dts b/arch/arm64/boot/dts/qcom/sdm632-qrd-sku4.dts
new file mode 100644
index 0000000..9f33721
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm632-qrd-sku4.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sdm632.dtsi"
+#include "sdm450-qrd-sku4.dtsi"
+#include "sdm450-pmi632.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM632 + PMI632 + PMI8004 QRD SKU4";
+ compatible = "qcom,sdm632-qrd", "qcom,sdm632", "qcom,qrd";
+ qcom,board-id = <0xb 1>;
+ qcom,pmic-id = <0x010016 0x25 0xC 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm632-rumi-overlay.dts b/arch/arm64/boot/dts/qcom/sdm632-rumi-overlay.dts
new file mode 100644
index 0000000..4d8ce5c
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm632-rumi-overlay.dts
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include "sdm632-rumi.dtsi"
+
+/ {
+ model = "RUMI";
+ compatible = "qcom,rumi";
+ qcom,board-id = <15 0>;
+ qcom,pmic-id = <0 0 0 0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm632.dts b/arch/arm64/boot/dts/qcom/sdm632.dts
new file mode 100644
index 0000000..dab409c
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm632.dts
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sdm632.dtsi"
+#include "sdm450-pmi632.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. SDM632 + PMI632 SOC";
+ compatible = "qcom,sdm450";
+ qcom,pmic-id = <0x010016 0x25 0xC 0x0>;
+ qcom,pmic-name = "PMI632";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm632.dtsi b/arch/arm64/boot/dts/qcom/sdm632.dtsi
index 3ebd50e..5100f28 100644
--- a/arch/arm64/boot/dts/qcom/sdm632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm632.dtsi
@@ -18,5 +18,17 @@
model = "Qualcomm Technologies, Inc. SDM632";
compatible = "qcom,sdm632";
qcom,msm-id = <349 0x0>;
+ qcom,msm-name = "SDM632";
};
+&clock_gcc {
+ compatible = "qcom,gcc-sdm632";
+};
+
+&clock_debug {
+ compatible = "qcom,cc-debug-sdm632";
+};
+
+&clock_gcc_gfx {
+ compatible = "qcom,gcc-gfx-sdm632";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-audio-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm670-audio-overlay.dtsi
index 5dd5c0d..2b3cb39 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-audio-overlay.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-audio-overlay.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -12,6 +12,7 @@
*/
#include "sdm670-wcd.dtsi"
#include "sdm670-wsa881x.dtsi"
+#include "sdm670-lpi.dtsi"
#include <dt-bindings/clock/qcom,audio-ext-clk.h>
&tavil_snd {
@@ -59,6 +60,49 @@
"SpkrLeft", "SpkrRight";
};
+&tasha_snd {
+ qcom,msm-mi2s-master = <1>, <1>, <1>, <1>, <1>;
+ qcom,audio-routing =
+ "AIF4 VI", "MCLK",
+ "RX_BIAS", "MCLK",
+ "MADINPUT", "MCLK",
+ "hifi amp", "LINEOUT1",
+ "hifi amp", "LINEOUT2",
+ "AMIC2", "MIC BIAS2",
+ "MIC BIAS2", "Headset Mic",
+ "AMIC3", "MIC BIAS2",
+ "MIC BIAS2", "ANCRight Headset Mic",
+ "AMIC4", "MIC BIAS2",
+ "MIC BIAS2", "ANCLeft Headset Mic",
+ "AMIC5", "MIC BIAS3",
+ "MIC BIAS3", "Handset Mic",
+ "DMIC0", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic0",
+ "DMIC1", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic1",
+ "DMIC2", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic2",
+ "DMIC3", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic3",
+ "DMIC4", "MIC BIAS4",
+ "MIC BIAS4", "Digital Mic4",
+ "DMIC5", "MIC BIAS4",
+ "MIC BIAS4", "Digital Mic5",
+ "SpkrLeft IN", "SPK1 OUT",
+ "SpkrRight IN", "SPK2 OUT";
+
+ qcom,msm-mbhc-hphl-swh = <0>;
+ qcom,msm-mbhc-gnd-swh = <0>;
+ qcom,msm-mclk-freq = <9600000>;
+ asoc-codec = <&stub_codec>;
+ asoc-codec-names = "msm-stub-codec.1";
+ qcom,wsa-max-devs = <2>;
+ qcom,wsa-devs = <&wsa881x_211>, <&wsa881x_212>,
+ <&wsa881x_213>, <&wsa881x_214>;
+ qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrRight",
+ "SpkrLeft", "SpkrRight";
+};
+
&int_codec {
qcom,audio-routing =
"RX_BIAS", "INT_MCLK0",
@@ -222,6 +266,31 @@
pinctrl-0 = <&wcd_intr_default>;
};
+ clock_audio_native: audio_ext_clk_native {
+ status = "disabled";
+ compatible = "qcom,audio-ref-clk";
+ #clock-cells = <1>;
+ qcom,lpass-mclk-id = <0x116>;
+ qcom,codec-mclk-clk-freq = <11289600>;
+ qcom,audio-ref-clk-gpio = <&lpi_tlmm 19 0>;
+ pinctrl-names = "sleep", "active";
+ pinctrl-0 = <&lpi_mclk0_sleep>;
+ pinctrl-1 = <&lpi_mclk0_active>;
+ };
+
+ clock_audio: audio_ext_clk {
+ status = "disabled";
+ compatible = "qcom,audio-ref-clk";
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&tasha_mclk_default>;
+ pinctrl-1 = <&tasha_mclk_default>;
+ qcom,audio-ref-clk-gpio = <&pm660_gpios 3 0>;
+ clock-names = "osr_clk";
+ clocks = <&pm660_div_clk>;
+ qcom,node_has_rpm_clock;
+ #clock-cells = <1>;
+ };
+
clock_audio_lnbb: audio_ext_clk_lnbb {
status = "disabled";
compatible = "qcom,audio-ref-clk";
@@ -254,6 +323,40 @@
};
&slim_aud {
+ wcd9335: tasha_codec {
+ status = "disabled";
+ compatible = "qcom,tasha-slim-pgd";
+ elemental-addr = [00 01 a0 01 17 02];
+
+ interrupt-parent = <&wcd9xxx_intc>;
+ interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+ 17 18 19 20 21 22 23 24 25 26 27 28 29
+ 30>;
+
+ qcom,wcd-rst-gpio-node = <&wcd_rst_gpio>;
+
+ clock-names = "wcd_clk", "wcd_native_clk";
+ clocks = <&clock_audio AUDIO_PMI_CLK>,
+ <&clock_audio_native AUDIO_LPASS_MCLK>;
+
+ cdc-vdd-mic-bias-supply = <&pm660l_bob>;
+ qcom,cdc-vdd-mic-bias-voltage = <3312000 3312000>;
+ qcom,cdc-vdd-mic-bias-current = <30400>;
+
+ qcom,cdc-static-supplies = "cdc-vdd-mic-bias";
+
+ qcom,cdc-micbias1-mv = <1800>;
+ qcom,cdc-micbias2-mv = <1800>;
+ qcom,cdc-micbias3-mv = <1800>;
+ qcom,cdc-micbias4-mv = <1800>;
+
+ qcom,cdc-mclk-clk-rate = <9600000>;
+ qcom,cdc-slim-ifd = "tasha-slim-ifd";
+ qcom,cdc-slim-ifd-elemental-addr = [00 00 a0 01 17 02];
+ qcom,cdc-dmic-sample-rate = <4800000>;
+ qcom,cdc-mad-dmic-rate = <600000>;
+ };
+
wcd934x_cdc: tavil_codec {
status = "disabled";
compatible = "qcom,tavil-slim-pgd";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi b/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi
index bda44cc..faaf644 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -12,10 +12,6 @@
*/
#include "msm-audio-lpass.dtsi"
-#include "sdm670-wcd.dtsi"
-#include "sdm670-wsa881x.dtsi"
-#include "sdm670-lpi.dtsi"
-#include <dt-bindings/clock/qcom,audio-ext-clk.h>
&msm_audio_ion {
iommus = <&apps_smmu 0x1801 0x0>;
@@ -23,6 +19,13 @@
};
&soc {
+ audio_load_mod {
+ compatible = "qcom,audio-load-mod";
+ audio_test_mod {
+ compatible = "qcom,audio-test-mod";
+ };
+ };
+
qcom,avtimer@62cf700c {
compatible = "qcom,avtimer";
reg = <0x62cf700c 0x4>,
@@ -99,6 +102,72 @@
"msm-dai-q6-tdm.36928", "msm-dai-q6-tdm.36929";
};
+ tasha_snd: sound-tasha {
+ status = "disabled";
+ compatible = "qcom,sdm670-asoc-snd-tasha";
+ qcom,model = "sdm670-tasha-snd-card";
+ qcom,wcn-btfm;
+ qcom,mi2s-audio-intf;
+ qcom,auxpcm-audio-intf;
+ asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
+ <&loopback>, <&compress>, <&hostless>,
+ <&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>,
+ <&pcm_noirq>, <&cpe3>;
+ asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
+ "msm-pcm-dsp.2", "msm-voip-dsp",
+ "msm-pcm-voice", "msm-pcm-loopback",
+ "msm-compress-dsp", "msm-pcm-hostless",
+ "msm-pcm-afe", "msm-lsm-client",
+ "msm-pcm-routing", "msm-cpe-lsm",
+ "msm-compr-dsp", "msm-pcm-dsp-noirq",
+ "msm-cpe-lsm.3";
+ asoc-cpu = <&dai_mi2s0>, <&dai_mi2s1>,
+ <&dai_mi2s2>, <&dai_mi2s3>, <&dai_mi2s4>,
+ <&dai_pri_auxpcm>, <&dai_sec_auxpcm>,
+ <&dai_tert_auxpcm>, <&dai_quat_auxpcm>,
+ <&dai_quin_auxpcm>,
+ <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
+ <&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>,
+ <&sb_4_rx>, <&sb_4_tx>, <&sb_5_rx>, <&sb_5_tx>,
+ <&sb_6_rx>, <&sb_7_rx>, <&sb_7_tx>,
+ <&sb_8_rx>, <&sb_8_tx>,
+ <&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>,
+ <&afe_proxy_tx>, <&incall_record_rx>,
+ <&incall_record_tx>, <&incall_music_rx>,
+ <&incall_music_2_rx>,
+ <&usb_audio_rx>, <&usb_audio_tx>,
+ <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_tx_0>,
+ <&dai_sec_tdm_rx_0>, <&dai_sec_tdm_tx_0>,
+ <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_tx_0>,
+ <&dai_quat_tdm_rx_0>, <&dai_quat_tdm_tx_0>,
+ <&dai_quin_tdm_rx_0>, <&dai_quin_tdm_tx_0>;
+ asoc-cpu-names = "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
+ "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
+ "msm-dai-q6-mi2s.4",
+ "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2",
+ "msm-dai-q6-auxpcm.3", "msm-dai-q6-auxpcm.4",
+ "msm-dai-q6-auxpcm.5",
+ "msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
+ "msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
+ "msm-dai-q6-dev.16388", "msm-dai-q6-dev.16389",
+ "msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
+ "msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
+ "msm-dai-q6-dev.16394", "msm-dai-q6-dev.16395",
+ "msm-dai-q6-dev.16396",
+ "msm-dai-q6-dev.16398", "msm-dai-q6-dev.16399",
+ "msm-dai-q6-dev.16400", "msm-dai-q6-dev.16401",
+ "msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
+ "msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
+ "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
+ "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770",
+ "msm-dai-q6-dev.28672", "msm-dai-q6-dev.28673",
+ "msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36865",
+ "msm-dai-q6-tdm.36880", "msm-dai-q6-tdm.36881",
+ "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36897",
+ "msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36913",
+ "msm-dai-q6-tdm.36928", "msm-dai-q6-tdm.36929";
+ };
+
int_codec: sound {
status = "okay";
compatible = "qcom,sdm670-asoc-snd";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi b/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi
index 4f5a9b1..aa7cc97 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -669,35 +669,6 @@
qcom,bcms = <&bcm_cn0>;
};
- mas_qhm_tic: mas-qhm-tic {
- cell-id = <MSM_BUS_MASTER_TIC>;
- label = "mas-qhm-tic";
- qcom,buswidth = <4>;
- qcom,agg-ports = <1>;
- qcom,connections = <&slv_qhs_tlmm_south
- &slv_qhs_camera_cfg &slv_qhs_sdc4
- &slv_qhs_sdc2 &slv_qhs_mnoc_cfg
- &slv_qhs_ufs_mem_cfg &slv_qhs_glm
- &slv_qhs_pdm &slv_qhs_a2_noc_cfg
- &slv_qhs_qdss_cfg &slv_qhs_display_cfg
- &slv_qhs_tcsr &slv_qhs_dcc_cfg
- &slv_qhs_ddrss_cfg &slv_qns_cnoc_a2noc
- &slv_qhs_snoc_cfg &slv_qhs_phy_refgen_south
- &slv_qhs_gpuss_cfg &slv_qhs_venus_cfg
- &slv_qhs_tsif &slv_qhs_compute_dsp_cfg
- &slv_qhs_aop &slv_qhs_qupv3_north
- &slv_srvc_cnoc &slv_qhs_usb3_0
- &slv_qhs_ipa &slv_qhs_cpr_cx
- &slv_qhs_a1_noc_cfg &slv_qhs_aoss
- &slv_qhs_prng &slv_qhs_vsense_ctrl_cfg
- &slv_qhs_emmc_cfg &slv_qhs_qupv3_south
- &slv_qhs_spdm &slv_qhs_crypto0_cfg
- &slv_qhs_pimem_cfg &slv_qhs_tlmm_north
- &slv_qhs_clk_ctl &slv_qhs_imem_cfg>;
- qcom,bus-dev = <&fab_config_noc>;
- qcom,bcms = <&bcm_cn0>;
- };
-
mas_qnm_snoc: mas-qnm-snoc {
cell-id = <MSM_BUS_SNOC_CNOC_MAS>;
label = "mas-qnm-snoc";
@@ -727,36 +698,6 @@
qcom,bcms = <&bcm_cn0>;
};
- mas_xm_qdss_dap: mas-xm-qdss-dap {
- cell-id = <MSM_BUS_MASTER_QDSS_DAP>;
- label = "mas-xm-qdss-dap";
- qcom,buswidth = <8>;
- qcom,agg-ports = <1>;
- qcom,connections = <&slv_qhs_tlmm_south
- &slv_qhs_camera_cfg
- &slv_qhs_sdc4
- &slv_qhs_sdc2 &slv_qhs_mnoc_cfg
- &slv_qhs_ufs_mem_cfg &slv_qhs_glm
- &slv_qhs_pdm &slv_qhs_a2_noc_cfg
- &slv_qhs_qdss_cfg &slv_qhs_display_cfg
- &slv_qhs_tcsr &slv_qhs_dcc_cfg
- &slv_qhs_ddrss_cfg &slv_qns_cnoc_a2noc
- &slv_qhs_snoc_cfg &slv_qhs_phy_refgen_south
- &slv_qhs_gpuss_cfg &slv_qhs_venus_cfg
- &slv_qhs_tsif &slv_qhs_compute_dsp_cfg
- &slv_qhs_aop &slv_qhs_qupv3_north
- &slv_srvc_cnoc &slv_qhs_usb3_0
- &slv_qhs_ipa &slv_qhs_cpr_cx
- &slv_qhs_a1_noc_cfg &slv_qhs_aoss
- &slv_qhs_prng &slv_qhs_vsense_ctrl_cfg
- &slv_qhs_qupv3_south &slv_qhs_spdm
- &slv_qhs_crypto0_cfg &slv_qhs_pimem_cfg
- &slv_qhs_tlmm_north &slv_qhs_clk_ctl
- &slv_qhs_imem_cfg>;
- qcom,bus-dev = <&fab_config_noc>;
- qcom,bcms = <&bcm_cn0>;
- };
-
mas_qhm_cnoc: mas-qhm-cnoc {
cell-id = <MSM_BUS_MASTER_CNOC_DC_NOC>;
label = "mas-qhm-cnoc";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm670-camera.dtsi
index 715affd..96c4640 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-camera.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -892,7 +892,7 @@
<0 0 200000000 0 0 0 0 600000000>;
clock-cntl-level = "svs", "turbo";
fw_name = "CAMERA_ICP.elf";
- ubwc-cfg = <0x77 0x1DF>;
+ ubwc-cfg = <0x73 0x1CF>;
status = "ok";
};
@@ -913,12 +913,12 @@
<&clock_camcc CAM_CC_IPE_0_CLK>,
<&clock_camcc CAM_CC_IPE_0_CLK_SRC>;
- clock-rates = <0 0 0 0 240000000>,
+ clock-rates =
<0 0 0 0 404000000>,
<0 0 0 0 480000000>,
<0 0 0 0 538000000>,
<0 0 0 0 600000000>;
- clock-cntl-level = "lowsvs", "svs",
+ clock-cntl-level = "svs",
"svs_l1", "nominal", "turbo";
status = "ok";
};
@@ -940,12 +940,12 @@
<&clock_camcc CAM_CC_IPE_1_CLK>,
<&clock_camcc CAM_CC_IPE_1_CLK_SRC>;
- clock-rates = <0 0 0 0 240000000>,
+ clock-rates =
<0 0 0 0 404000000>,
<0 0 0 0 480000000>,
<0 0 0 0 538000000>,
<0 0 0 0 600000000>;
- clock-cntl-level = "lowsvs", "svs",
+ clock-cntl-level = "svs",
"svs_l1", "nominal", "turbo";
status = "ok";
};
@@ -967,12 +967,12 @@
<&clock_camcc CAM_CC_BPS_CLK>,
<&clock_camcc CAM_CC_BPS_CLK_SRC>;
- clock-rates = <0 0 0 0 200000000>,
+ clock-rates =
<0 0 0 0 404000000>,
<0 0 0 0 480000000>,
<0 0 0 0 600000000>,
<0 0 0 0 600000000>;
- clock-cntl-level = "lowsvs", "svs",
+ clock-cntl-level = "svs",
"svs_l1", "nominal", "turbo";
status = "ok";
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
index f287b21..75a2762 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -16,6 +16,7 @@
compatible = "qcom,pil-tz-generic";
qcom,pas-id = <13>;
qcom,firmware-name = "a615_zap";
+ memory-region = <&pil_gpu_mem>;
};
msm_bus: qcom,kgsl-busmon{
@@ -46,9 +47,12 @@
label = "kgsl-3d0";
compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d";
status = "ok";
- reg = <0x5000000 0x40000
- 0x780000 0x6300>;
- reg-names = "kgsl_3d0_reg_memory", "qfprom_memory";
+ reg = <0x5000000 0x40000>,
+ <0x5061000 0x800>,
+ <0x780000 0x6300>;
+ reg-names = "kgsl_3d0_reg_memory",
+ "kgsl_3d0_cx_dbgc_memory",
+ "qfprom_memory";
interrupts = <0 300 0>;
interrupt-names = "kgsl_3d0_irq";
qcom,id = <0>;
diff --git a/arch/arm64/boot/dts/qcom/sdm670-lpi.dtsi b/arch/arm64/boot/dts/qcom/sdm670-lpi.dtsi
index c76fbce..948c51d 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-lpi.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-lpi.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,6 +18,34 @@
gpio-controller;
#gpio-cells = <2>;
+ lpi_mclk0_active: lpi_mclk0_active {
+ mux {
+ pins = "gpio19";
+ function = "func1";
+ };
+
+ config {
+ pins = "gpio19";
+ drive-strength = <8>;
+ bias-disable;
+ output-low;
+ };
+ };
+
+ lpi_mclk0_sleep: lpi_mclk0_sleep {
+ mux {
+ pins = "gpio19";
+ function = "func1";
+ };
+
+ config {
+ pins = "gpio19";
+ drive-strength = <2>;
+ bias-disable;
+ bias-pull-down;
+ };
+ };
+
cdc_pdm_clk_active: cdc_pdm_clk_active {
mux {
pins = "gpio18";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
index a85060e..5684e19 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1505,6 +1505,67 @@
};
};
+ /* Tasha WSA speaker reset pins */
+ tasha_spkr_1_sd_n {
+ tasha_spkr_1_sd_n_sleep: tasha_spkr_1_sd_n_sleep {
+ mux {
+ pins = "gpio66";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio66";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down;
+ input-enable;
+ };
+ };
+
+ tasha_spkr_1_sd_n_active: tasha_spkr_1_sd_n_active {
+ mux {
+ pins = "gpio66";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio66";
+ drive-strength = <16>; /* 16 mA */
+ bias-disable;
+ output-high;
+ };
+ };
+ };
+
+ tasha_spkr_2_sd_n {
+ tasha_spkr_2_sd_n_sleep: tasha_spkr_2_sd_n_sleep {
+ mux {
+ pins = "gpio65";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio65";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down;
+ input-enable;
+ };
+ };
+
+ tasha_spkr_2_sd_n_active: tasha_spkr_2_sd_n_active {
+ mux {
+ pins = "gpio65";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio65";
+ drive-strength = <16>; /* 16 mA */
+ bias-disable;
+ output-high;
+ };
+ };
+ };
+
wcd_buck_vsel {
wcd_buck_vsel_default: wcd_buck_vsel_default{
mux {
@@ -1995,6 +2056,19 @@
};
};
+&pm660_gpios {
+ tasha_mclk {
+ tasha_mclk_default: tasha_mclk_default{
+ pins = "gpio3";
+ function = "func1";
+ qcom,drive-strength = <2>;
+ power-source = <0>;
+ bias-disable;
+ output-low;
+ };
+ };
+};
+
&pm660l_gpios {
camera_rear_dvdd_en {
camera_rear_dvdd_en_default: camera_rear_dvdd_en_default {
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-tasha-codec-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-tasha-codec-cdp-overlay.dts
index 15fadc6..b7cb820 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pm660a-tasha-codec-cdp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-tasha-codec-cdp-overlay.dts
@@ -21,7 +21,7 @@
#include "sdm670-cdp.dtsi"
#include "pm660a.dtsi"
-#include "sdm670-audio-overlay.dtsi"
+#include "sdm670-tasha-codec.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A + Tasha Codec CDP";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-tasha-codec-cdp.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-tasha-codec-cdp.dts
index ec906ee..1922b38 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pm660a-tasha-codec-cdp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-tasha-codec-cdp.dts
@@ -16,7 +16,7 @@
#include "sdm670.dtsi"
#include "sdm670-cdp.dtsi"
#include "pm660a.dtsi"
-#include "sdm670-audio-overlay.dtsi"
+#include "sdm670-tasha-codec.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A Tasha Codec CDP";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
index 5d3975c..6652ba5 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -175,8 +175,8 @@
io-channel-names = "rradc_batt_id",
"rradc_die_temp";
qcom,rradc-base = <0x4500>;
- qcom,fg-esr-timer-awake = <96 96>;
- qcom,fg-esr-timer-asleep = <256 256>;
+ qcom,fg-esr-timer-awake = <64 96>;
+ qcom,fg-esr-timer-asleep = <224 256>;
qcom,fg-esr-timer-charging = <0 96>;
qcom,cycle-counter-en;
qcom,hold-soc-while-full;
@@ -395,27 +395,22 @@
trips {
batt_trip1: batt-trip1 {
- temperature = <40000>;
- hysteresis = <2000>;
+ temperature = <50000>;
+ hysteresis = <4000>;
type = "passive";
};
batt_trip2: batt-trip2 {
- temperature = <45000>;
+ temperature = <52000>;
hysteresis = <2000>;
type = "passive";
};
batt_trip3: batt-trip3 {
- temperature = <48000>;
- hysteresis = <3000>;
- type = "passive";
- };
- batt_trip4: batt-trip4 {
- temperature = <50000>;
+ temperature = <54000>;
hysteresis = <2000>;
type = "passive";
};
- batt_trip5: batt-trip5 {
- temperature = <52000>;
+ batt_trip4: batt-trip4 {
+ temperature = <56000>;
hysteresis = <2000>;
type = "passive";
};
@@ -432,14 +427,10 @@
};
battery_lvl3 {
trip = <&batt_trip3>;
- cooling-device = <&pm660_charger 3 3>;
+ cooling-device = <&pm660_charger 4 4>;
};
battery_lvl4 {
trip = <&batt_trip4>;
- cooling-device = <&pm660_charger 4 4>;
- };
- battery_lvl5 {
- trip = <&batt_trip5>;
cooling-device = <&pm660_charger 5 5>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
index d2a6640..9a7e742 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
@@ -307,6 +307,12 @@
qcom,dsi-display-active;
};
+&dsi_panel_pwr_supply {
+ qcom,panel-supply-entry@2 {
+ qcom,supply-post-off-sleep = <5>;
+ };
+};
+
&pm660l_wled {
status = "okay";
qcom,led-strings-list = [00 01];
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
index ce88d14..007f937 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
@@ -826,8 +826,15 @@
};
&dsi_dual_nt36850_truly_cmd {
- qcom,mdss-dsi-t-clk-post = <0x0E>;
+ qcom,mdss-dsi-t-clk-post = <0x28>;
qcom,mdss-dsi-t-clk-pre = <0x30>;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
qcom,mdss-dsi-display-timings {
timing@0{
qcom,mdss-dsi-panel-phy-timings = [00 1f 08 08 24 23 08
diff --git a/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-audio-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-audio-overlay.dtsi
new file mode 100644
index 0000000..80d3879
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-audio-overlay.dtsi
@@ -0,0 +1,80 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "sdm670-audio-overlay.dtsi"
+
+&pmic_analog_codec {
+ status = "disabled";
+};
+
+&msm_sdw_codec {
+ status = "disabled";
+};
+
+&cdc_pdm_gpios {
+ status = "disabled";
+};
+
+&cdc_comp_gpios {
+ status = "disabled";
+};
+
+&cdc_dmic_gpios {
+ status = "disabled";
+};
+
+&cdc_sdw_gpios {
+ status = "disabled";
+};
+
+&wsa_spkr_en1 {
+ status = "disabled";
+};
+
+&wsa_spkr_en2 {
+ status = "disabled";
+};
+
+&qupv3_se8_spi {
+ status = "disabled";
+};
+
+&wcd9xxx_intc {
+ status = "okay";
+};
+
+&slim_aud {
+ status = "okay";
+};
+
+&dai_slim {
+ status = "okay";
+};
+
+&wcd9335 {
+ status = "okay";
+};
+
+&clock_audio {
+ status = "okay";
+};
+
+&clock_audio_native {
+ status = "okay";
+};
+
+&wcd_rst_gpio {
+ status = "okay";
+};
+
+&wcd9xxx_intc {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-cdp-overlay.dts
index cc772cd..af8244a 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-cdp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-cdp-overlay.dts
@@ -20,7 +20,7 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include "sdm670-cdp.dtsi"
-#include "sdm670-audio-overlay.dtsi"
+#include "sdm670-tasha-codec.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L Tasha Codec CDP";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-cdp.dts b/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-cdp.dts
index e862911..55d2fc2 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-cdp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-cdp.dts
@@ -15,7 +15,7 @@
#include "sdm670.dtsi"
#include "sdm670-cdp.dtsi"
-#include "sdm670-audio-overlay.dtsi"
+#include "sdm670-tasha-codec.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L Tasha Codec CDP";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-tasha-codec.dtsi b/arch/arm64/boot/dts/qcom/sdm670-tasha-codec.dtsi
new file mode 100644
index 0000000..1fc0fd5
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-tasha-codec.dtsi
@@ -0,0 +1,33 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdm670-tasha-codec-audio-overlay.dtsi"
+
+&int_codec {
+ status = "disabled";
+};
+
+&pm660_div_clk {
+ status = "okay";
+};
+
+&tasha_snd {
+ status = "okay";
+};
+
+&slim_aud {
+ status = "okay";
+};
+
+&dai_slim {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-thermal.dtsi b/arch/arm64/boot/dts/qcom/sdm670-thermal.dtsi
index a0fa9cf..f259838 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-thermal.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-thermal.dtsi
@@ -584,12 +584,12 @@
trips {
gold_trip0: gold-trip0 {
- temperature = <45000>;
+ temperature = <47000>;
hysteresis = <0>;
type = "passive";
};
silver_trip1: silver-trip1 {
- temperature = <50000>;
+ temperature = <52000>;
hysteresis = <0>;
type = "passive";
};
@@ -645,23 +645,23 @@
trips {
modem_trip0: modem-trip0 {
- temperature = <44000>;
+ temperature = <48000>;
hysteresis = <4000>;
type = "passive";
};
modem_trip1: modem-trip1 {
- temperature = <46000>;
- hysteresis = <3000>;
+ temperature = <50000>;
+ hysteresis = <4000>;
type = "passive";
};
modem_trip2: modem-trip2 {
- temperature = <48000>;
- hysteresis = <3000>;
+ temperature = <52000>;
+ hysteresis = <2000>;
type = "passive";
};
modem_trip3: modem-trip3 {
- temperature = <55000>;
- hysteresis = <5000>;
+ temperature = <56000>;
+ hysteresis = <4000>;
type = "passive";
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-usb.dtsi b/arch/arm64/boot/dts/qcom/sdm670-usb.dtsi
index 2ce829d..3df6d09 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-usb.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-usb.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -27,8 +27,6 @@
};
&usb0 {
- /delete-property/ iommus;
- /delete-property/ qcom,smmu-s1-bypass;
qcom,pm-qos-latency = <601>; /* CPU-CLUSTER-WFI-LVL latency +1 */
extcon = <0>, <0>, <&eud>, <0>, <0>;
};
@@ -37,6 +35,28 @@
vdd-supply = <&pm660l_l1>;
vdda18-supply = <&pm660_l10>;
vdda33-supply = <&pm660l_l7>;
+ qcom,qusb-phy-init-seq =
+ /* <value reg_offset> */
+ <0x23 0x210 /* PWR_CTRL1 */
+ 0x03 0x04 /* PLL_ANALOG_CONTROLS_TWO */
+ 0x7c 0x18c /* PLL_CLOCK_INVERTERS */
+ 0x80 0x2c /* PLL_CMODE */
+ 0x0a 0x184 /* PLL_LOCK_DELAY */
+ 0x19 0xb4 /* PLL_DIGITAL_TIMERS_TWO */
+ 0x40 0x194 /* PLL_BIAS_CONTROL_1 */
+ 0x20 0x198 /* PLL_BIAS_CONTROL_2 */
+ 0x21 0x214 /* PWR_CTRL2 */
+ 0x08 0x220 /* IMP_CTRL1 */
+ 0x58 0x224 /* IMP_CTRL2 */
+ 0x45 0x240 /* TUNE1 */
+ 0x29 0x244 /* TUNE2 */
+ 0xca 0x248 /* TUNE3 */
+ 0x04 0x24c /* TUNE4 */
+ 0x03 0x250 /* TUNE5 */
+ 0x00 0x23c /* CHG_CTRL2 */
+ 0x22 0x210>; /* PWR_CTRL1 */
+ nvmem-cells = <&minor_rev>;
+ nvmem-cell-names = "minor_rev";
};
&usb_qmp_dp_phy {
diff --git a/arch/arm64/boot/dts/qcom/sdm670-wcd.dtsi b/arch/arm64/boot/dts/qcom/sdm670-wcd.dtsi
index f8d2a04..d7120d0 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-wcd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-wcd.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -164,4 +164,20 @@
pinctrl-1 = <&hph_en1_wcd_sleep>;
};
};
+
+ tasha_codec {
+ wsa_spkr_sd1: msm_cdc_pinctrll {
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&tasha_spkr_1_sd_n_active>;
+ pinctrl-1 = <&tasha_spkr_1_sd_n_sleep>;
+ };
+
+ wsa_spkr_sd2: msm_cdc_pinctrlr {
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&tasha_spkr_2_sd_n_active>;
+ pinctrl-1 = <&tasha_spkr_2_sd_n_sleep>;
+ };
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-wsa881x.dtsi b/arch/arm64/boot/dts/qcom/sdm670-wsa881x.dtsi
index c35850d..5dfe244 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-wsa881x.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-wsa881x.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -42,4 +42,36 @@
};
};
};
+
+ tasha_codec {
+ swr_master {
+ compatible = "qcom,swr-wcd";
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ wsa881x_211: wsa881x@20170211 {
+ compatible = "qcom,wsa881x";
+ reg = <0x0 0x20170211>;
+ qcom,spkr-sd-n-node = <&wsa_spkr_sd1>;
+ };
+
+ wsa881x_212: wsa881x@20170212 {
+ compatible = "qcom,wsa881x";
+ reg = <0x0 0x20170212>;
+ qcom,spkr-sd-n-node = <&wsa_spkr_sd2>;
+ };
+
+ wsa881x_213: wsa881x@21170213 {
+ compatible = "qcom,wsa881x";
+ reg = <0x0 0x21170213>;
+ qcom,spkr-sd-n-node = <&wsa_spkr_sd1>;
+ };
+
+ wsa881x_214: wsa881x@21170214 {
+ compatible = "qcom,wsa881x";
+ reg = <0x0 0x21170214>;
+ qcom,spkr-sd-n-node = <&wsa_spkr_sd2>;
+ };
+ };
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670.dtsi b/arch/arm64/boot/dts/qcom/sdm670.dtsi
index 2b391a5..47c03e2 100644
--- a/arch/arm64/boot/dts/qcom/sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670.dtsi
@@ -529,6 +529,30 @@
reg = <0 0x93e00000 0 0x1e00000>;
};
+ pil_ipa_fw_mem: ips_fw_region@0x95c00000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0 0x95c00000 0 0x10000>;
+ };
+
+ pil_ipa_gsi_mem: ipa_gsi_region@0x95c10000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0 0x95c10000 0 0x5000>;
+ };
+
+ pil_gpu_mem: gpu_region@0x95c15000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0 0x95c15000 0 0x2000>;
+ };
+
+ qseecom_mem: qseecom_region@0x9e400000 {
+ compatible = "shared-dma-pool";
+ no-map;
+ reg = <0 0x9e400000 0 0x1400000>;
+ };
+
adsp_mem: adsp_region {
compatible = "shared-dma-pool";
alloc-ranges = <0 0x00000000 0 0xffffffff>;
@@ -537,14 +561,6 @@
size = <0 0xc00000>;
};
- qseecom_mem: qseecom_region {
- compatible = "shared-dma-pool";
- alloc-ranges = <0 0x00000000 0 0xffffffff>;
- no-map;
- alignment = <0 0x400000>;
- size = <0 0x1400000>;
- };
-
qseecom_ta_mem: qseecom_ta_region {
compatible = "shared-dma-pool";
alloc-ranges = <0 0x00000000 0 0xffffffff>;
@@ -1082,7 +1098,7 @@
vdd_pwrcl_mx_ao-supply = <&pm660l_s1_level_ao>;
qcom,mx-turbo-freq = <1440000000 1708000000 3300000001>;
- l3-devs = <&l3_cpu0 &l3_cpu6>;
+ l3-devs = <&l3_cpu0 &l3_cpu6 &l3_cdsp>;
clock-names = "xo_ao";
clocks = <&clock_rpmh RPMH_CXO_CLK_A>;
@@ -1781,6 +1797,29 @@
cell-index = <0>;
};
+ ufs_ice: ufsice@1d90000 {
+ compatible = "qcom,ice";
+ reg = <0x1d90000 0x8000>;
+ qcom,enable-ice-clk;
+ clock-names = "ufs_core_clk", "bus_clk",
+ "iface_clk", "ice_core_clk";
+ clocks = <&clock_gcc GCC_UFS_PHY_AXI_CLK>,
+ <&clock_gcc GCC_UFS_MEM_CLKREF_CLK>,
+ <&clock_gcc GCC_UFS_PHY_AHB_CLK>,
+ <&clock_gcc GCC_UFS_PHY_ICE_CORE_CLK>;
+ qcom,op-freq-hz = <0>, <0>, <0>, <300000000>;
+ vdd-hba-supply = <&ufs_phy_gdsc>;
+ qcom,msm-bus,name = "ufs_ice_noc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <1 650 0 0>, /* No vote */
+ <1 650 1000 0>; /* Max. bandwidth */
+ qcom,bus-vector-names = "MIN",
+ "MAX";
+ qcom,instance-type = "ufs";
+ };
+
ufsphy_mem: ufsphy_mem@1d87000 {
reg = <0x1d87000 0xe00>; /* PHY regs */
reg-names = "phy_mem";
@@ -1804,6 +1843,7 @@
interrupts = <0 265 0>;
phys = <&ufsphy_mem>;
phy-names = "ufsphy";
+ ufs-qcom-crypto = <&ufs_ice>;
lanes-per-direction = <1>;
dev-ref-clk-freq = <0>; /* 19.2 MHz */
@@ -1956,7 +1996,7 @@
qcom,arm-smmu;
qcom,bandwidth-vote-for-ipa;
qcom,msm-bus,name = "ipa";
- qcom,msm-bus,num-cases = <4>;
+ qcom,msm-bus,num-cases = <5>;
qcom,msm-bus,num-paths = <4>;
qcom,msm-bus,vectors-KBps =
/* No vote */
@@ -1964,22 +2004,28 @@
<90 585 0 0>,
<1 676 0 0>,
<143 777 0 0>,
+ /* SVS2 */
+ <90 512 80000 600000>,
+ <90 585 80000 350000>,
+ <1 676 40000 40000>, /*gcc_config_noc_clk_src */
+ <143 777 0 75>, /* IB defined for IPA2X_clk in MHz*/
/* SVS */
<90 512 80000 640000>,
<90 585 80000 640000>,
<1 676 80000 80000>,
- <143 777 0 150>, /* IB defined for IPA clk in MHz*/
+ <143 777 0 150>, /* IB defined for IPA2X_clk in MHz*/
/* NOMINAL */
<90 512 206000 960000>,
<90 585 206000 960000>,
<1 676 206000 160000>,
- <143 777 0 300>, /* IB defined for IPA clk in MHz*/
+ <143 777 0 300>, /* IB defined for IPA2X_clk in MHz*/
/* TURBO */
<90 512 206000 3600000>,
<90 585 206000 3600000>,
<1 676 206000 300000>,
<143 777 0 355>; /* IB defined for IPA clk in MHz*/
- qcom,bus-vector-names = "MIN", "SVS", "NOMINAL", "TURBO";
+ qcom,bus-vector-names =
+ "MIN", "SVS2", "SVS", "NOMINAL", "TURBO";
/* IPA RAM mmap */
qcom,ipa-ram-mmap = <
@@ -2093,6 +2139,8 @@
compatible = "qcom,pil-tz-generic";
qcom,pas-id = <0xf>;
qcom,firmware-name = "ipa_fws";
+ qcom,pil-force-shutdown;
+ memory-region = <&pil_ipa_fw_mem>;
};
pil_modem: qcom,mss@4080000 {
@@ -2319,6 +2367,8 @@
qcom,ddr-config = <0xC3040873>;
qcom,nonremovable;
+ nvmem-cells = <&minor_rev>;
+ nvmem-cell-names = "minor_rev";
status = "disabled";
};
@@ -2392,6 +2442,7 @@
qcom,msm-adsprpc-mem {
compatible = "qcom,msm-adsprpc-mem-region";
memory-region = <&adsp_mem>;
+ restrict-access;
};
qcom,msm_fastrpc {
@@ -2763,6 +2814,13 @@
< 2457600 MHZ_TO_MBPS(1804, 4) >;
};
+ l3_cdsp: qcom,l3-cdsp {
+ compatible = "devfreq-simple-dev";
+ clock-names = "devfreq_clk";
+ clocks = <&clock_cpucc L3_MISC_VOTE_CLK>;
+ governor = "powersave";
+ };
+
cpu_pmu: cpu-pmu {
compatible = "arm,armv8-pmuv3";
qcom,irq-is-percpu;
@@ -2778,6 +2836,20 @@
compatible = "syscon";
reg = <0x5091008 0x4>;
};
+
+ qfprom: qfprom@0x780000 {
+ compatible = "qcom,qfprom";
+ reg = <0x00784000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ minor_rev: minor_rev@0x78414c {
+ reg = <0x14c 0x4>;
+ bits = <0 30>; /* Access 30 bits from bit offset 0 */
+ };
+ };
+
};
#include "pm660.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
index 35a7774..ec1e9c7 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -863,7 +863,7 @@
<0 0 200000000 0 0 0 0 600000000>;
clock-cntl-level = "svs", "turbo";
fw_name = "CAMERA_ICP.elf";
- ubwc-cfg = <0x7F 0x1FF>;
+ ubwc-cfg = <0x7B 0x1EF>;
status = "ok";
};
@@ -885,12 +885,11 @@
<&clock_camcc CAM_CC_IPE_0_CLK_SRC>;
clock-rates =
- <0 0 0 0 240000000>,
<0 0 0 0 404000000>,
<0 0 0 0 480000000>,
<0 0 0 0 538000000>,
<0 0 0 0 600000000>;
- clock-cntl-level = "lowsvs", "svs",
+ clock-cntl-level = "svs",
"svs_l1", "nominal", "turbo";
status = "ok";
};
@@ -912,12 +911,12 @@
<&clock_camcc CAM_CC_IPE_1_CLK>,
<&clock_camcc CAM_CC_IPE_1_CLK_SRC>;
- clock-rates = <0 0 0 0 240000000>,
+ clock-rates =
<0 0 0 0 404000000>,
<0 0 0 0 480000000>,
<0 0 0 0 538000000>,
<0 0 0 0 600000000>;
- clock-cntl-level = "lowsvs", "svs",
+ clock-cntl-level = "svs",
"svs_l1", "nominal", "turbo";
status = "ok";
};
@@ -939,12 +938,12 @@
<&clock_camcc CAM_CC_BPS_CLK>,
<&clock_camcc CAM_CC_BPS_CLK_SRC>;
- clock-rates = <0 0 0 0 200000000>,
+ clock-rates =
<0 0 0 0 404000000>,
<0 0 0 0 480000000>,
<0 0 0 0 600000000>,
<0 0 0 0 600000000>;
- clock-cntl-level = "lowsvs", "svs",
+ clock-cntl-level = "svs",
"svs_l1", "nominal", "turbo";
status = "ok";
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
index 5a88dc2..2c38f51 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -320,7 +320,6 @@
qcom,nq-clkreq = <&pm8998_gpios 21 0x00>;
qcom,nq-esepwr = <&tlmm 116 0x00>;
interrupt-parent = <&tlmm>;
- qcom,clk-src = "BBCLK3";
interrupts = <63 0>;
interrupt-names = "nfc_irq";
pinctrl-names = "nfc_active", "nfc_suspend";
@@ -328,8 +327,6 @@
&nfc_enable_active
&nfc_clk_default>;
pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>;
- clocks = <&clock_rpmh RPMH_LN_BB_CLK3>;
- clock-names = "ref_clk";
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi b/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
index 90e1f24..a094f65 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
@@ -18,6 +18,23 @@
reg-names = "csr-base";
coresight-name = "coresight-csr";
+ qcom,usb-bam-support;
+ qcom,hwctrl-set-support;
+ qcom,set-byte-cntr-support;
+
+ qcom,blk-size = <1>;
+ };
+
+ swao_csr: csr@6b0e000 {
+ compatible = "qcom,coresight-csr";
+ reg = <0x6b0e000 0x1000>;
+ reg-names = "csr-base";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ coresight-name = "coresight-swao-csr";
+ qcom,timestamp-support;
qcom,blk-size = <1>;
};
@@ -113,6 +130,7 @@
reg-names = "tmc-base";
coresight-name = "coresight-tmc-etf-swao";
+ coresight-csr = <&csr>;
clocks = <&clock_aop QDSS_CLK>;
clock-names = "apb_pclk";
@@ -286,6 +304,7 @@
coresight-name = "coresight-tmc-etr";
coresight-ctis = <&cti0 &cti8>;
+ coresight-csr = <&csr>;
clocks = <&clock_aop QDSS_CLK>;
clock-names = "apb_pclk";
@@ -310,6 +329,7 @@
coresight-name = "coresight-tmc-etf";
coresight-ctis = <&cti0 &cti8>;
+ coresight-csr = <&csr>;
arm,default-sink;
clocks = <&clock_aop QDSS_CLK>;
@@ -414,6 +434,7 @@
"ddr-ch23-ctrl";
coresight-name = "coresight-hwevent";
+ coresight-csr = <&csr>;
clocks = <&clock_aop QDSS_CLK>;
clock-names = "apb_pclk";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
index 33bcaa6..000f5d3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
@@ -16,6 +16,7 @@
compatible = "qcom,pil-tz-generic";
qcom,pas-id = <13>;
qcom,firmware-name = "a630_zap";
+ memory-region = <&pil_gpu_mem>;
};
msm_bus: qcom,kgsl-busmon{
diff --git a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
index fc4b674..349c4c0 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -319,7 +319,6 @@
qcom,nq-clkreq = <&pm8998_gpios 21 0x00>;
qcom,nq-esepwr = <&tlmm 116 0x00>;
interrupt-parent = <&tlmm>;
- qcom,clk-src = "BBCLK3";
interrupts = <63 0>;
interrupt-names = "nfc_irq";
pinctrl-names = "nfc_active", "nfc_suspend";
@@ -327,8 +326,6 @@
&nfc_enable_active
&nfc_clk_default>;
pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>;
- clocks = <&clock_rpmh RPMH_LN_BB_CLK3>;
- clock-names = "ref_clk";
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
index daf5687..af7feb5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -202,6 +202,8 @@
qcom,ep-latency = <10>;
+ qcom,phy-status-offset = <0x974>;
+
qcom,boot-option = <0x1>;
linux,pci-domain = <0>;
@@ -535,6 +537,8 @@
qcom,slv-addr-space-size = <0x20000000>;
+ qcom,phy-status-offset = <0x1aac>;
+
qcom,boot-option = <0x1>;
linux,pci-domain = <1>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
index 11c48bd..78be790 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
@@ -749,6 +749,123 @@
};
};
+ /* add pingrp for touchscreen */
+ pmx_ts_int_active {
+ ts_int_active: ts_int_active {
+ mux {
+ pins = "gpio122";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio122";
+ drive-strength = <8>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ pmx_ts_int_suspend {
+ ts_int_suspend1: ts_int_suspend1 {
+ mux {
+ pins = "gpio122";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio122";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+ };
+
+ pmx_ts_reset_active {
+ ts_reset_active: ts_reset_active {
+ mux {
+ pins = "gpio99";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio99";
+ drive-strength = <8>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ pmx_ts_reset_suspend {
+ ts_reset_suspend1: ts_reset_suspend1 {
+ mux {
+ pins = "gpio99";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio99";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+ };
+
+ pmx_ts_release {
+ ts_release: ts_release {
+ mux {
+ pins = "gpio122", "gpio99";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio122", "gpio99";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+ };
+
+ ts_mux {
+ ts_active: ts_active {
+ mux {
+ pins = "gpio99", "gpio122";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio99", "gpio122";
+ drive-strength = <16>;
+ bias-pull-up;
+ };
+ };
+
+ ts_reset_suspend: ts_reset_suspend {
+ mux {
+ pins = "gpio99";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio99";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+
+ ts_int_suspend: ts_int_suspend {
+ mux {
+ pins = "gpio122";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio122";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+ };
+
sec_aux_pcm {
sec_aux_pcm_sleep: sec_aux_pcm_sleep {
mux {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
index 3ee0138..bd8ae70 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -82,7 +82,6 @@
qcom,nq-clkreq = <&pm8998_gpios 21 0x00>;
qcom,nq-esepwr = <&tlmm 116 0x00>;
interrupt-parent = <&tlmm>;
- qcom,clk-src = "BBCLK3";
interrupts = <63 0>;
interrupt-names = "nfc_irq";
pinctrl-names = "nfc_active", "nfc_suspend";
@@ -90,8 +89,6 @@
&nfc_enable_active
&nfc_clk_default>;
pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>;
- clocks = <&clock_rpmh RPMH_LN_BB_CLK3>;
- clock-names = "ref_clk";
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
index d95dda0..a5c6ab5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
@@ -206,8 +206,8 @@
qcom,vdd-io-current-level = <200 22000>;
pinctrl-names = "active", "sleep";
- pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &storage_cd>;
- pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &storage_cd>;
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &storage_cd>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &storage_cd>;
cd-gpios = <&tlmm 126 GPIO_ACTIVE_HIGH>;
@@ -217,3 +217,30 @@
&wil6210 {
status = "ok";
};
+
+&qupv3_se5_i2c {
+ status = "ok";
+ synaptics_dsx@20 {
+ compatible = "synaptics,dsx-i2c";
+ reg = <0x20>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <122 0x2008>;
+ vdd-supply = <&pm8998_l14>;
+ avdd-supply = <&pm8998_l28>;
+ pinctrl-names = "pmx_ts_active", "pmx_ts_suspend",
+ "pmx_ts_release";
+ pinctrl-0 = <&ts_int_active &ts_reset_active>;
+ pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
+ pinctrl-2 = <&ts_release>;
+ synaptics,pwr-reg-name = "avdd";
+ synaptics,bus-reg-name = "vdd";
+ synaptics,ub-i2c-addr = <0x2c>;
+ synaptics,irq-gpio = <&tlmm 122 0x2008>;
+ synaptics,reset-gpio = <&tlmm 99 0x0>;
+ synaptics,irq-on-state = <0>;
+ synaptics,power-delay-ms = <200>;
+ synaptics,reset-delay-ms = <200>;
+ synaptics,reset-on-state = <0>;
+ synaptics,reset-active-ms = <20>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
index 9d7c519..ec8665b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -559,7 +559,7 @@
regulator-min-microvolt = <2704000>;
regulator-max-microvolt = <2960000>;
qcom,init-voltage = <2704000>;
- qcom,init-mode = <RPMH_REGULATOR_MODE_LPM>;
+ qcom,init-mode = <RPMH_REGULATOR_MODE_HPM>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
index 947d28b..1551952 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -51,6 +51,7 @@
compatible = "qcom,memshare-peripheral";
qcom,peripheral-size = <0x500000>;
qcom,client-id = <1>;
+ qcom,allocate-boot-time;
label = "modem";
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 213dfdb..7832165 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -2648,6 +2648,7 @@
qcom,ipa-wdi2;
qcom,use-64-bit-dma-mask;
qcom,arm-smmu;
+ qcom,smmu-fast-map;
qcom,bandwidth-vote-for-ipa;
qcom,msm-bus,name = "ipa";
qcom,msm-bus,num-cases = <5>;
@@ -2770,7 +2771,6 @@
ipa_smmu_ap: ipa_smmu_ap {
compatible = "qcom,ipa-smmu-ap-cb";
- qcom,smmu-s1-bypass;
iommus = <&apps_smmu 0x720 0x0>;
qcom,iova-mapping = <0x20000000 0x40000000>;
qcom,additional-mapping =
@@ -2780,7 +2780,6 @@
ipa_smmu_wlan: ipa_smmu_wlan {
compatible = "qcom,ipa-smmu-wlan-cb";
- qcom,smmu-s1-bypass;
iommus = <&apps_smmu 0x721 0x0>;
qcom,additional-mapping =
/* ipa-uc ram */
@@ -2789,7 +2788,6 @@
ipa_smmu_uc: ipa_smmu_uc {
compatible = "qcom,ipa-smmu-uc-cb";
- qcom,smmu-s1-bypass;
iommus = <&apps_smmu 0x722 0x0>;
qcom,iova-mapping = <0x40000000 0x20000000>;
};
@@ -3089,7 +3087,6 @@
vdd-3.3-ch0-supply = <&pm8998_l25>;
qcom,vdd-0.8-cx-mx-config = <800000 800000>;
qcom,vdd-3.3-ch0-config = <3104000 3312000>;
- qcom,smmu-s1-bypass;
};
qmi-tmd-devices {
diff --git a/arch/arm64/configs/msm8953-perf_defconfig b/arch/arm64/configs/msm8953-perf_defconfig
index 801c66a..880018c 100644
--- a/arch/arm64/configs/msm8953-perf_defconfig
+++ b/arch/arm64/configs/msm8953-perf_defconfig
@@ -52,6 +52,7 @@
# CONFIG_IOSCHED_DEADLINE is not set
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_MSM8953=y
+CONFIG_ARCH_MSM8937=y
CONFIG_ARCH_SDM450=y
CONFIG_ARCH_SDM632=y
CONFIG_SCHED_MC=y
@@ -295,6 +296,7 @@
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM_LEGACY=y
CONFIG_MSM_SMD_PKT=y
+CONFIG_MSM_ADSPRPC=y
CONFIG_MSM_RDBG=m
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_MSM_V2=y
@@ -335,6 +337,7 @@
CONFIG_REGULATOR_COOLING_DEVICE=y
CONFIG_MFD_SPMI_PMIC=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_CPR=y
CONFIG_REGULATOR_CPR4_APSS=y
CONFIG_REGULATOR_CPRH_KBSS=y
CONFIG_REGULATOR_MEM_ACC=y
@@ -495,6 +498,7 @@
CONFIG_MSM_PM=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_SPDM_SCM=y
CONFIG_DEVFREQ_SPDM=y
CONFIG_PWM=y
diff --git a/arch/arm64/configs/msm8953_defconfig b/arch/arm64/configs/msm8953_defconfig
index aaceec2..bc0265f 100644
--- a/arch/arm64/configs/msm8953_defconfig
+++ b/arch/arm64/configs/msm8953_defconfig
@@ -56,6 +56,7 @@
# CONFIG_IOSCHED_DEADLINE is not set
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_MSM8953=y
+CONFIG_ARCH_MSM8937=y
CONFIG_ARCH_SDM450=y
CONFIG_ARCH_SDM632=y
CONFIG_SCHED_MC=y
@@ -305,6 +306,7 @@
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM_LEGACY=y
CONFIG_MSM_SMD_PKT=y
+CONFIG_MSM_ADSPRPC=y
CONFIG_MSM_RDBG=m
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_MSM_V2=y
@@ -345,6 +347,7 @@
CONFIG_REGULATOR_COOLING_DEVICE=y
CONFIG_MFD_SPMI_PMIC=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_CPR=y
CONFIG_REGULATOR_CPR4_APSS=y
CONFIG_REGULATOR_CPRH_KBSS=y
CONFIG_REGULATOR_MEM_ACC=y
@@ -514,6 +517,10 @@
CONFIG_QCOM_DCC=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_WCNSS_CORE=y
+CONFIG_WCNSS_CORE_PRONTO=y
+CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y
+CONFIG_QCOM_DEVFREQ_DEVBW=y
CONFIG_SPDM_SCM=y
CONFIG_DEVFREQ_SPDM=y
CONFIG_PWM=y
@@ -562,6 +569,7 @@
CONFIG_DEBUG_STACK_USAGE=y
CONFIG_DEBUG_MEMORY_INIT=y
CONFIG_LOCKUP_DETECTOR=y
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
CONFIG_WQ_WATCHDOG=y
CONFIG_PANIC_TIMEOUT=5
CONFIG_PANIC_ON_SCHED_BUG=y
diff --git a/arch/arm64/configs/sdm670-perf_defconfig b/arch/arm64/configs/sdm670-perf_defconfig
index 1904209..8aa1e7d 100644
--- a/arch/arm64/configs/sdm670-perf_defconfig
+++ b/arch/arm64/configs/sdm670-perf_defconfig
@@ -564,6 +564,8 @@
CONFIG_ARM_GIC_V3_ACL=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_NVMEM=y
+CONFIG_QCOM_QFPROM=y
CONFIG_SENSORS_SSC=y
CONFIG_MSM_TZ_LOG=y
CONFIG_EXT2_FS=y
diff --git a/arch/arm64/configs/sdm670_defconfig b/arch/arm64/configs/sdm670_defconfig
index 0dad3f1..667377f 100644
--- a/arch/arm64/configs/sdm670_defconfig
+++ b/arch/arm64/configs/sdm670_defconfig
@@ -582,6 +582,8 @@
CONFIG_PHY_XGENE=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_NVMEM=y
+CONFIG_QCOM_QFPROM=y
CONFIG_SENSORS_SSC=y
CONFIG_MSM_TZ_LOG=y
CONFIG_EXT2_FS=y
@@ -619,7 +621,6 @@
CONFIG_DEBUG_OBJECTS_FREE=y
CONFIG_DEBUG_OBJECTS_TIMERS=y
CONFIG_DEBUG_OBJECTS_WORK=y
-CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
CONFIG_SLUB_DEBUG_ON=y
CONFIG_DEBUG_KMEMLEAK=y
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index f7ef61e..11c95ea 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -290,6 +290,11 @@
CONFIG_KEYBOARD_GPIO=y
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_TEST_REPORTING=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_QPNP_POWER_ON=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index a4f0ffa..6aa09e5 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -294,6 +294,11 @@
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_TEST_REPORTING=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_QPNP_POWER_ON=y
diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h
index 4e7eec7..ea06f3f 100644
--- a/arch/arm64/include/asm/efi.h
+++ b/arch/arm64/include/asm/efi.h
@@ -82,19 +82,21 @@
if (mm != current->active_mm) {
/*
* Update the current thread's saved ttbr0 since it is
- * restored as part of a return from exception. Set
- * the hardware TTBR0_EL1 using cpu_switch_mm()
- * directly to enable potential errata workarounds.
+ * restored as part of a return from exception. Enable
+ * access to the valid TTBR0_EL1 and invoke the errata
+ * workaround directly since there is no return from
+ * exception when invoking the EFI run-time services.
*/
update_saved_ttbr0(current, mm);
- cpu_switch_mm(mm->pgd, mm);
+ uaccess_ttbr0_enable();
+ post_ttbr_update_workaround();
} else {
/*
* Defer the switch to the current thread's TTBR0_EL1
* until uaccess_enable(). Restore the current
* thread's saved ttbr0 corresponding to its active_mm
*/
- cpu_set_reserved_ttbr0();
+ uaccess_ttbr0_disable();
update_saved_ttbr0(current, current->active_mm);
}
}
diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
index af0215a..a0fac5c 100644
--- a/arch/arm64/include/asm/mmu_context.h
+++ b/arch/arm64/include/asm/mmu_context.h
@@ -177,7 +177,7 @@
else
ttbr = virt_to_phys(mm->pgd) | ASID(mm) << 48;
- task_thread_info(tsk)->ttbr0 = ttbr;
+ WRITE_ONCE(task_thread_info(tsk)->ttbr0, ttbr);
}
#else
static inline void update_saved_ttbr0(struct task_struct *tsk,
@@ -232,5 +232,6 @@
#define activate_mm(prev,next) switch_mm(prev, next, current)
void verify_cpu_asid_bits(void);
+void post_ttbr_update_workaround(void);
#endif
diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
index 8b38b0d..9311547 100644
--- a/arch/arm64/include/asm/uaccess.h
+++ b/arch/arm64/include/asm/uaccess.h
@@ -132,16 +132,18 @@
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
static inline void __uaccess_ttbr0_disable(void)
{
- unsigned long ttbr;
+ unsigned long flags, ttbr;
+ local_irq_save(flags);
ttbr = read_sysreg(ttbr1_el1);
+ ttbr &= ~TTBR_ASID_MASK;
/* reserved_ttbr0 placed at the end of swapper_pg_dir */
write_sysreg(ttbr + SWAPPER_DIR_SIZE, ttbr0_el1);
isb();
/* Set reserved ASID */
- ttbr &= ~TTBR_ASID_MASK;
write_sysreg(ttbr, ttbr1_el1);
isb();
+ local_irq_restore(flags);
}
static inline void __uaccess_ttbr0_enable(void)
@@ -154,10 +156,11 @@
* roll-over and an update of 'ttbr0'.
*/
local_irq_save(flags);
- ttbr0 = current_thread_info()->ttbr0;
+ ttbr0 = READ_ONCE(current_thread_info()->ttbr0);
/* Restore active ASID */
ttbr1 = read_sysreg(ttbr1_el1);
+ ttbr1 &= ~TTBR_ASID_MASK; /* safety measure */
ttbr1 |= ttbr0 & TTBR_ASID_MASK;
write_sysreg(ttbr1, ttbr1_el1);
isb();
@@ -450,11 +453,11 @@
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
.macro __uaccess_ttbr0_disable, tmp1
mrs \tmp1, ttbr1_el1 // swapper_pg_dir
+ bic \tmp1, \tmp1, #TTBR_ASID_MASK
add \tmp1, \tmp1, #SWAPPER_DIR_SIZE // reserved_ttbr0 at the end of swapper_pg_dir
msr ttbr0_el1, \tmp1 // set reserved TTBR0_EL1
isb
sub \tmp1, \tmp1, #SWAPPER_DIR_SIZE
- bic \tmp1, \tmp1, #TTBR_ASID_MASK
msr ttbr1_el1, \tmp1 // set reserved ASID
isb
.endm
@@ -471,9 +474,11 @@
isb
.endm
- .macro uaccess_ttbr0_disable, tmp1
+ .macro uaccess_ttbr0_disable, tmp1, tmp2
alternative_if_not ARM64_HAS_PAN
+ save_and_disable_irq \tmp2 // avoid preemption
__uaccess_ttbr0_disable \tmp1
+ restore_irq \tmp2
alternative_else_nop_endif
.endm
@@ -485,7 +490,7 @@
alternative_else_nop_endif
.endm
#else
- .macro uaccess_ttbr0_disable, tmp1
+ .macro uaccess_ttbr0_disable, tmp1, tmp2
.endm
.macro uaccess_ttbr0_enable, tmp1, tmp2, tmp3
@@ -495,8 +500,8 @@
/*
* These macros are no-ops when UAO is present.
*/
- .macro uaccess_disable_not_uao, tmp1
- uaccess_ttbr0_disable \tmp1
+ .macro uaccess_disable_not_uao, tmp1, tmp2
+ uaccess_ttbr0_disable \tmp1, \tmp2
alternative_if ARM64_ALT_PAN_NOT_UAO
SET_PSTATE_PAN(1)
alternative_else_nop_endif
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index 8030583..3b22fa3 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -150,7 +150,7 @@
alternative_else_nop_endif
.if \el != 0
- mrs x21, ttbr1_el1
+ mrs x21, ttbr0_el1
tst x21, #TTBR_ASID_MASK // Check for the reserved ASID
orr x23, x23, #PSR_PAN_BIT // Set the emulated PAN in the saved SPSR
b.eq 1f // TTBR0 access already disabled
diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c
index 85baada..2e6e9e9 100644
--- a/arch/arm64/kvm/handle_exit.c
+++ b/arch/arm64/kvm/handle_exit.c
@@ -44,7 +44,7 @@
ret = kvm_psci_call(vcpu);
if (ret < 0) {
- kvm_inject_undefined(vcpu);
+ vcpu_set_reg(vcpu, 0, ~0UL);
return 1;
}
@@ -53,7 +53,7 @@
static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run)
{
- kvm_inject_undefined(vcpu);
+ vcpu_set_reg(vcpu, 0, ~0UL);
return 1;
}
diff --git a/arch/arm64/lib/clear_user.S b/arch/arm64/lib/clear_user.S
index dd65ca2..07c7ad9 100644
--- a/arch/arm64/lib/clear_user.S
+++ b/arch/arm64/lib/clear_user.S
@@ -50,7 +50,7 @@
b.mi 5f
uao_user_alternative 9f, strb, sttrb, wzr, x0, 0
5: mov x0, #0
- uaccess_disable_not_uao x2
+ uaccess_disable_not_uao x2, x3
ret
ENDPROC(__clear_user)
diff --git a/arch/arm64/lib/copy_from_user.S b/arch/arm64/lib/copy_from_user.S
index 7e7e687..c7a7d96 100644
--- a/arch/arm64/lib/copy_from_user.S
+++ b/arch/arm64/lib/copy_from_user.S
@@ -67,7 +67,7 @@
uaccess_enable_not_uao x3, x4, x5
add end, x0, x2
#include "copy_template.S"
- uaccess_disable_not_uao x3
+ uaccess_disable_not_uao x3, x4
mov x0, #0 // Nothing to copy
ret
ENDPROC(__arch_copy_from_user)
diff --git a/arch/arm64/lib/copy_in_user.S b/arch/arm64/lib/copy_in_user.S
index 074d52f..e8bfaf1 100644
--- a/arch/arm64/lib/copy_in_user.S
+++ b/arch/arm64/lib/copy_in_user.S
@@ -68,7 +68,7 @@
uaccess_enable_not_uao x3, x4, x5
add end, x0, x2
#include "copy_template.S"
- uaccess_disable_not_uao x3
+ uaccess_disable_not_uao x3, x4
mov x0, #0
ret
ENDPROC(__copy_in_user)
diff --git a/arch/arm64/lib/copy_to_user.S b/arch/arm64/lib/copy_to_user.S
index 6711844..f6cfcc0 100644
--- a/arch/arm64/lib/copy_to_user.S
+++ b/arch/arm64/lib/copy_to_user.S
@@ -66,7 +66,7 @@
uaccess_enable_not_uao x3, x4, x5
add end, x0, x2
#include "copy_template.S"
- uaccess_disable_not_uao x3
+ uaccess_disable_not_uao x3, x4
mov x0, #0
ret
ENDPROC(__arch_copy_to_user)
diff --git a/arch/arm64/mm/cache.S b/arch/arm64/mm/cache.S
index 9dd6d32..b98b3fc 100644
--- a/arch/arm64/mm/cache.S
+++ b/arch/arm64/mm/cache.S
@@ -145,7 +145,7 @@
isb
mov x0, #0
1:
- uaccess_ttbr0_disable x1
+ uaccess_ttbr0_disable x1, x2
ret
9:
mov x0, #-EFAULT
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index fa20d13..28bedd7 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -186,6 +186,9 @@
ENTRY(cpu_do_switch_mm)
mrs x2, ttbr1_el1
mmid x1, x1 // get mm->context.id
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+ bfi x0, x1, #48, #16 // set the ASID field in TTBR0
+#endif
bfi x2, x1, #48, #16 // set the ASID
msr ttbr1_el1, x2 // in TTBR1 (since TCR.A1 is set)
isb
diff --git a/arch/arm64/xen/hypercall.S b/arch/arm64/xen/hypercall.S
index f542252..69711f2 100644
--- a/arch/arm64/xen/hypercall.S
+++ b/arch/arm64/xen/hypercall.S
@@ -106,6 +106,6 @@
/*
* Disable userspace access from kernel once the hyp call completed.
*/
- uaccess_ttbr0_disable x6
+ uaccess_ttbr0_disable x6, x7
ret
ENDPROC(privcmd_call);
diff --git a/arch/mips/ar7/platform.c b/arch/mips/ar7/platform.c
index 3446b6f..9da4e22 100644
--- a/arch/mips/ar7/platform.c
+++ b/arch/mips/ar7/platform.c
@@ -576,7 +576,7 @@
uart_port.type = PORT_AR7;
uart_port.uartclk = clk_get_rate(bus_clk) / 2;
uart_port.iotype = UPIO_MEM32;
- uart_port.flags = UPF_FIXED_TYPE;
+ uart_port.flags = UPF_FIXED_TYPE | UPF_BOOT_AUTOCONF;
uart_port.regshift = 2;
uart_port.line = 0;
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index c558bce..6e716a5 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -683,6 +683,18 @@
struct task_struct *t;
int max_users;
+ /* If nothing to change, return right away, successfully. */
+ if (value == mips_get_process_fp_mode(task))
+ return 0;
+
+ /* Only accept a mode change if 64-bit FP enabled for o32. */
+ if (!IS_ENABLED(CONFIG_MIPS_O32_FP64_SUPPORT))
+ return -EOPNOTSUPP;
+
+ /* And only for o32 tasks. */
+ if (IS_ENABLED(CONFIG_64BIT) && !test_thread_flag(TIF_32BIT_REGS))
+ return -EOPNOTSUPP;
+
/* Check the value is valid */
if (value & ~known_bits)
return -EOPNOTSUPP;
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c
index 11890e6..0c8ae2c 100644
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -439,25 +439,38 @@
#endif /* CONFIG_64BIT */
-static int fpr_get(struct task_struct *target,
- const struct user_regset *regset,
- unsigned int pos, unsigned int count,
- void *kbuf, void __user *ubuf)
+/*
+ * Copy the floating-point context to the supplied NT_PRFPREG buffer,
+ * !CONFIG_CPU_HAS_MSA variant. FP context's general register slots
+ * correspond 1:1 to buffer slots. Only general registers are copied.
+ */
+static int fpr_get_fpa(struct task_struct *target,
+ unsigned int *pos, unsigned int *count,
+ void **kbuf, void __user **ubuf)
{
- unsigned i;
- int err;
+ return user_regset_copyout(pos, count, kbuf, ubuf,
+ &target->thread.fpu,
+ 0, NUM_FPU_REGS * sizeof(elf_fpreg_t));
+}
+
+/*
+ * Copy the floating-point context to the supplied NT_PRFPREG buffer,
+ * CONFIG_CPU_HAS_MSA variant. Only lower 64 bits of FP context's
+ * general register slots are copied to buffer slots. Only general
+ * registers are copied.
+ */
+static int fpr_get_msa(struct task_struct *target,
+ unsigned int *pos, unsigned int *count,
+ void **kbuf, void __user **ubuf)
+{
+ unsigned int i;
u64 fpr_val;
+ int err;
- /* XXX fcr31 */
-
- if (sizeof(target->thread.fpu.fpr[i]) == sizeof(elf_fpreg_t))
- return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
- &target->thread.fpu,
- 0, sizeof(elf_fpregset_t));
-
+ BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t));
for (i = 0; i < NUM_FPU_REGS; i++) {
fpr_val = get_fpr64(&target->thread.fpu.fpr[i], 0);
- err = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ err = user_regset_copyout(pos, count, kbuf, ubuf,
&fpr_val, i * sizeof(elf_fpreg_t),
(i + 1) * sizeof(elf_fpreg_t));
if (err)
@@ -467,27 +480,64 @@
return 0;
}
-static int fpr_set(struct task_struct *target,
+/*
+ * Copy the floating-point context to the supplied NT_PRFPREG buffer.
+ * Choose the appropriate helper for general registers, and then copy
+ * the FCSR register separately.
+ */
+static int fpr_get(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
- const void *kbuf, const void __user *ubuf)
+ void *kbuf, void __user *ubuf)
{
- unsigned i;
+ const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t);
int err;
+
+ if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t))
+ err = fpr_get_fpa(target, &pos, &count, &kbuf, &ubuf);
+ else
+ err = fpr_get_msa(target, &pos, &count, &kbuf, &ubuf);
+ if (err)
+ return err;
+
+ err = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.fpu.fcr31,
+ fcr31_pos, fcr31_pos + sizeof(u32));
+
+ return err;
+}
+
+/*
+ * Copy the supplied NT_PRFPREG buffer to the floating-point context,
+ * !CONFIG_CPU_HAS_MSA variant. Buffer slots correspond 1:1 to FP
+ * context's general register slots. Only general registers are copied.
+ */
+static int fpr_set_fpa(struct task_struct *target,
+ unsigned int *pos, unsigned int *count,
+ const void **kbuf, const void __user **ubuf)
+{
+ return user_regset_copyin(pos, count, kbuf, ubuf,
+ &target->thread.fpu,
+ 0, NUM_FPU_REGS * sizeof(elf_fpreg_t));
+}
+
+/*
+ * Copy the supplied NT_PRFPREG buffer to the floating-point context,
+ * CONFIG_CPU_HAS_MSA variant. Buffer slots are copied to lower 64
+ * bits only of FP context's general register slots. Only general
+ * registers are copied.
+ */
+static int fpr_set_msa(struct task_struct *target,
+ unsigned int *pos, unsigned int *count,
+ const void **kbuf, const void __user **ubuf)
+{
+ unsigned int i;
u64 fpr_val;
-
- /* XXX fcr31 */
-
- init_fp_ctx(target);
-
- if (sizeof(target->thread.fpu.fpr[i]) == sizeof(elf_fpreg_t))
- return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- &target->thread.fpu,
- 0, sizeof(elf_fpregset_t));
+ int err;
BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t));
- for (i = 0; i < NUM_FPU_REGS && count >= sizeof(elf_fpreg_t); i++) {
- err = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ for (i = 0; i < NUM_FPU_REGS && *count > 0; i++) {
+ err = user_regset_copyin(pos, count, kbuf, ubuf,
&fpr_val, i * sizeof(elf_fpreg_t),
(i + 1) * sizeof(elf_fpreg_t));
if (err)
@@ -498,6 +548,53 @@
return 0;
}
+/*
+ * Copy the supplied NT_PRFPREG buffer to the floating-point context.
+ * Choose the appropriate helper for general registers, and then copy
+ * the FCSR register separately.
+ *
+ * We optimize for the case where `count % sizeof(elf_fpreg_t) == 0',
+ * which is supposed to have been guaranteed by the kernel before
+ * calling us, e.g. in `ptrace_regset'. We enforce that requirement,
+ * so that we can safely avoid preinitializing temporaries for
+ * partial register writes.
+ */
+static int fpr_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t);
+ u32 fcr31;
+ int err;
+
+ BUG_ON(count % sizeof(elf_fpreg_t));
+
+ if (pos + count > sizeof(elf_fpregset_t))
+ return -EIO;
+
+ init_fp_ctx(target);
+
+ if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t))
+ err = fpr_set_fpa(target, &pos, &count, &kbuf, &ubuf);
+ else
+ err = fpr_set_msa(target, &pos, &count, &kbuf, &ubuf);
+ if (err)
+ return err;
+
+ if (count > 0) {
+ err = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &fcr31,
+ fcr31_pos, fcr31_pos + sizeof(u32));
+ if (err)
+ return err;
+
+ ptrace_setfcr31(target, fcr31);
+ }
+
+ return err;
+}
+
enum mips_regset {
REGSET_GPR,
REGSET_FPR,
diff --git a/arch/parisc/include/asm/ldcw.h b/arch/parisc/include/asm/ldcw.h
index 8be707e..82dea14 100644
--- a/arch/parisc/include/asm/ldcw.h
+++ b/arch/parisc/include/asm/ldcw.h
@@ -11,6 +11,7 @@
for the semaphore. */
#define __PA_LDCW_ALIGNMENT 16
+#define __PA_LDCW_ALIGN_ORDER 4
#define __ldcw_align(a) ({ \
unsigned long __ret = (unsigned long) &(a)->lock[0]; \
__ret = (__ret + __PA_LDCW_ALIGNMENT - 1) \
@@ -28,6 +29,7 @@
ldcd). */
#define __PA_LDCW_ALIGNMENT 4
+#define __PA_LDCW_ALIGN_ORDER 2
#define __ldcw_align(a) (&(a)->slock)
#define __LDCW "ldcw,co"
diff --git a/arch/parisc/kernel/entry.S b/arch/parisc/kernel/entry.S
index 4fcff2d..e3d3e8e 100644
--- a/arch/parisc/kernel/entry.S
+++ b/arch/parisc/kernel/entry.S
@@ -35,6 +35,7 @@
#include <asm/pgtable.h>
#include <asm/signal.h>
#include <asm/unistd.h>
+#include <asm/ldcw.h>
#include <asm/thread_info.h>
#include <linux/linkage.h>
@@ -46,6 +47,14 @@
#endif
.import pa_tlb_lock,data
+ .macro load_pa_tlb_lock reg
+#if __PA_LDCW_ALIGNMENT > 4
+ load32 PA(pa_tlb_lock) + __PA_LDCW_ALIGNMENT-1, \reg
+ depi 0,31,__PA_LDCW_ALIGN_ORDER, \reg
+#else
+ load32 PA(pa_tlb_lock), \reg
+#endif
+ .endm
/* space_to_prot macro creates a prot id from a space id */
@@ -457,7 +466,7 @@
.macro tlb_lock spc,ptp,pte,tmp,tmp1,fault
#ifdef CONFIG_SMP
cmpib,COND(=),n 0,\spc,2f
- load32 PA(pa_tlb_lock),\tmp
+ load_pa_tlb_lock \tmp
1: LDCW 0(\tmp),\tmp1
cmpib,COND(=) 0,\tmp1,1b
nop
@@ -480,7 +489,7 @@
/* Release pa_tlb_lock lock. */
.macro tlb_unlock1 spc,tmp
#ifdef CONFIG_SMP
- load32 PA(pa_tlb_lock),\tmp
+ load_pa_tlb_lock \tmp
tlb_unlock0 \spc,\tmp
#endif
.endm
diff --git a/arch/parisc/kernel/pacache.S b/arch/parisc/kernel/pacache.S
index adf7187..2d40c4f 100644
--- a/arch/parisc/kernel/pacache.S
+++ b/arch/parisc/kernel/pacache.S
@@ -36,6 +36,7 @@
#include <asm/assembly.h>
#include <asm/pgtable.h>
#include <asm/cache.h>
+#include <asm/ldcw.h>
#include <linux/linkage.h>
.text
@@ -333,8 +334,12 @@
.macro tlb_lock la,flags,tmp
#ifdef CONFIG_SMP
- ldil L%pa_tlb_lock,%r1
- ldo R%pa_tlb_lock(%r1),\la
+#if __PA_LDCW_ALIGNMENT > 4
+ load32 pa_tlb_lock + __PA_LDCW_ALIGNMENT-1, \la
+ depi 0,31,__PA_LDCW_ALIGN_ORDER, \la
+#else
+ load32 pa_tlb_lock, \la
+#endif
rsm PSW_SM_I,\flags
1: LDCW 0(\la),\tmp
cmpib,<>,n 0,\tmp,3f
diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c
index 7593787..c3a532a 100644
--- a/arch/parisc/kernel/process.c
+++ b/arch/parisc/kernel/process.c
@@ -39,6 +39,7 @@
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/fs.h>
+#include <linux/cpu.h>
#include <linux/module.h>
#include <linux/personality.h>
#include <linux/ptrace.h>
@@ -181,6 +182,44 @@
}
/*
+ * Idle thread support
+ *
+ * Detect when running on QEMU with SeaBIOS PDC Firmware and let
+ * QEMU idle the host too.
+ */
+
+int running_on_qemu __read_mostly;
+
+void __cpuidle arch_cpu_idle_dead(void)
+{
+ /* nop on real hardware, qemu will offline CPU. */
+ asm volatile("or %%r31,%%r31,%%r31\n":::);
+}
+
+void __cpuidle arch_cpu_idle(void)
+{
+ local_irq_enable();
+
+ /* nop on real hardware, qemu will idle sleep. */
+ asm volatile("or %%r10,%%r10,%%r10\n":::);
+}
+
+static int __init parisc_idle_init(void)
+{
+ const char *marker;
+
+ /* check QEMU/SeaBIOS marker in PAGE0 */
+ marker = (char *) &PAGE0->pad0;
+ running_on_qemu = (memcmp(marker, "SeaBIOS", 8) == 0);
+
+ if (!running_on_qemu)
+ cpu_idle_poll_ctrl(1);
+
+ return 0;
+}
+arch_initcall(parisc_idle_init);
+
+/*
* Copy architecture-specific thread state
*/
int
diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c
index 0f9cd90..f06a9a0 100644
--- a/arch/s390/kernel/compat_linux.c
+++ b/arch/s390/kernel/compat_linux.c
@@ -263,6 +263,7 @@
return retval;
}
+ groups_sort(group_info);
retval = set_current_groups(group_info);
put_group_info(group_info);
diff --git a/arch/um/Makefile b/arch/um/Makefile
index 0ca46ede..9c150cc 100644
--- a/arch/um/Makefile
+++ b/arch/um/Makefile
@@ -117,7 +117,7 @@
archprepare: include/generated/user_constants.h
LINK-$(CONFIG_LD_SCRIPT_STATIC) += -static
-LINK-$(CONFIG_LD_SCRIPT_DYN) += -Wl,-rpath,/lib
+LINK-$(CONFIG_LD_SCRIPT_DYN) += -Wl,-rpath,/lib $(call cc-option, -no-pie)
CFLAGS_NO_HARDENING := $(call cc-option, -fno-PIC,) $(call cc-option, -fno-pic,) \
$(call cc-option, -fno-stack-protector,) \
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 64e9609d..d2f5372 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -46,7 +46,7 @@
select ARCH_USE_CMPXCHG_LOCKREF if X86_64
select ARCH_USE_QUEUED_RWLOCKS
select ARCH_USE_QUEUED_SPINLOCKS
- select ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH if SMP
+ select ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH
select ARCH_WANTS_DYNAMIC_TASK_STRUCT
select ARCH_WANT_FRAME_POINTERS
select ARCH_WANT_IPC_PARSE_VERSION if X86_32
@@ -65,6 +65,7 @@
select GENERIC_CLOCKEVENTS_MIN_ADJUST
select GENERIC_CMOS_UPDATE
select GENERIC_CPU_AUTOPROBE
+ select GENERIC_CPU_VULNERABILITIES
select GENERIC_EARLY_IOREMAP
select GENERIC_FIND_FIRST_BIT
select GENERIC_IOMAP
@@ -408,6 +409,19 @@
def_bool y
depends on X86_GOLDFISH
+config RETPOLINE
+ bool "Avoid speculative indirect branches in kernel"
+ default y
+ ---help---
+ Compile kernel with the retpoline compiler options to guard against
+ kernel-to-user data leaks by avoiding speculative indirect
+ branches. Requires a compiler with -mindirect-branch=thunk-extern
+ support for full protection. The kernel may run slower.
+
+ Without compiler support, at least indirect branches in assembler
+ code are eliminated. Since this includes the syscall entry path,
+ it is not entirely pointless.
+
if X86_32
config X86_EXTENDED_PLATFORM
bool "Support for extended (non-PC) x86 platforms"
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index 75725dc..10a7d0f 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -184,6 +184,14 @@
KBUILD_CFLAGS += $(mflags-y)
KBUILD_AFLAGS += $(mflags-y)
+# Avoid indirect branches in kernel to deal with Spectre
+ifdef CONFIG_RETPOLINE
+ RETPOLINE_CFLAGS += $(call cc-option,-mindirect-branch=thunk-extern -mindirect-branch-register)
+ ifneq ($(RETPOLINE_CFLAGS),)
+ KBUILD_CFLAGS += $(RETPOLINE_CFLAGS) -DRETPOLINE
+ endif
+endif
+
archscripts: scripts_basic
$(Q)$(MAKE) $(build)=arch/x86/tools relocs
diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h
index 766a521..2728e1b 100644
--- a/arch/x86/boot/compressed/misc.h
+++ b/arch/x86/boot/compressed/misc.h
@@ -9,6 +9,7 @@
*/
#undef CONFIG_PARAVIRT
#undef CONFIG_PARAVIRT_SPINLOCKS
+#undef CONFIG_PAGE_TABLE_ISOLATION
#undef CONFIG_KASAN
#include <linux/linkage.h>
diff --git a/arch/x86/crypto/aesni-intel_asm.S b/arch/x86/crypto/aesni-intel_asm.S
index 383a6f8..fa8801b 100644
--- a/arch/x86/crypto/aesni-intel_asm.S
+++ b/arch/x86/crypto/aesni-intel_asm.S
@@ -32,6 +32,7 @@
#include <linux/linkage.h>
#include <asm/inst.h>
#include <asm/frame.h>
+#include <asm/nospec-branch.h>
/*
* The following macros are used to move an (un)aligned 16 byte value to/from
@@ -2734,7 +2735,7 @@
pxor INC, STATE4
movdqu IV, 0x30(OUTP)
- call *%r11
+ CALL_NOSPEC %r11
movdqu 0x00(OUTP), INC
pxor INC, STATE1
@@ -2779,7 +2780,7 @@
_aesni_gf128mul_x_ble()
movups IV, (IVP)
- call *%r11
+ CALL_NOSPEC %r11
movdqu 0x40(OUTP), INC
pxor INC, STATE1
diff --git a/arch/x86/crypto/aesni-intel_glue.c b/arch/x86/crypto/aesni-intel_glue.c
index aa8b067..d9ae404 100644
--- a/arch/x86/crypto/aesni-intel_glue.c
+++ b/arch/x86/crypto/aesni-intel_glue.c
@@ -906,7 +906,7 @@
if (sg_is_last(req->src) &&
req->src->offset + req->src->length <= PAGE_SIZE &&
- sg_is_last(req->dst) &&
++ sg_is_last(req->dst) && req->dst->length &&
req->dst->offset + req->dst->length <= PAGE_SIZE) {
one_entry_in_sg = 1;
scatterwalk_start(&src_sg_walk, req->src);
diff --git a/arch/x86/crypto/camellia-aesni-avx-asm_64.S b/arch/x86/crypto/camellia-aesni-avx-asm_64.S
index aa9e8bd..77ff4de 100644
--- a/arch/x86/crypto/camellia-aesni-avx-asm_64.S
+++ b/arch/x86/crypto/camellia-aesni-avx-asm_64.S
@@ -17,6 +17,7 @@
#include <linux/linkage.h>
#include <asm/frame.h>
+#include <asm/nospec-branch.h>
#define CAMELLIA_TABLE_BYTE_LEN 272
@@ -1224,7 +1225,7 @@
vpxor 14 * 16(%rax), %xmm15, %xmm14;
vpxor 15 * 16(%rax), %xmm15, %xmm15;
- call *%r9;
+ CALL_NOSPEC %r9;
addq $(16 * 16), %rsp;
diff --git a/arch/x86/crypto/camellia-aesni-avx2-asm_64.S b/arch/x86/crypto/camellia-aesni-avx2-asm_64.S
index 16186c1..7384342 100644
--- a/arch/x86/crypto/camellia-aesni-avx2-asm_64.S
+++ b/arch/x86/crypto/camellia-aesni-avx2-asm_64.S
@@ -12,6 +12,7 @@
#include <linux/linkage.h>
#include <asm/frame.h>
+#include <asm/nospec-branch.h>
#define CAMELLIA_TABLE_BYTE_LEN 272
@@ -1337,7 +1338,7 @@
vpxor 14 * 32(%rax), %ymm15, %ymm14;
vpxor 15 * 32(%rax), %ymm15, %ymm15;
- call *%r9;
+ CALL_NOSPEC %r9;
addq $(16 * 32), %rsp;
diff --git a/arch/x86/crypto/crc32c-pcl-intel-asm_64.S b/arch/x86/crypto/crc32c-pcl-intel-asm_64.S
index dc05f01..174fd41 100644
--- a/arch/x86/crypto/crc32c-pcl-intel-asm_64.S
+++ b/arch/x86/crypto/crc32c-pcl-intel-asm_64.S
@@ -45,6 +45,7 @@
#include <asm/inst.h>
#include <linux/linkage.h>
+#include <asm/nospec-branch.h>
## ISCSI CRC 32 Implementation with crc32 and pclmulqdq Instruction
@@ -172,7 +173,7 @@
movzxw (bufp, %rax, 2), len
lea crc_array(%rip), bufp
lea (bufp, len, 1), bufp
- jmp *bufp
+ JMP_NOSPEC bufp
################################################################
## 2a) PROCESS FULL BLOCKS:
diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S
index edba860..a76dc73 100644
--- a/arch/x86/entry/entry_32.S
+++ b/arch/x86/entry/entry_32.S
@@ -45,6 +45,7 @@
#include <asm/asm.h>
#include <asm/smap.h>
#include <asm/export.h>
+#include <asm/nospec-branch.h>
.section .entry.text, "ax"
@@ -228,6 +229,17 @@
movl %ebx, PER_CPU_VAR(stack_canary)+stack_canary_offset
#endif
+#ifdef CONFIG_RETPOLINE
+ /*
+ * When switching from a shallower to a deeper call stack
+ * the RSB may either underflow or use entries populated
+ * with userspace addresses. On CPUs where those concerns
+ * exist, overwrite the RSB with entries which capture
+ * speculative execution to prevent attack.
+ */
+ FILL_RETURN_BUFFER %ebx, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW
+#endif
+
/* restore callee-saved registers */
popl %esi
popl %edi
@@ -260,7 +272,7 @@
/* kernel thread */
1: movl %edi, %eax
- call *%ebx
+ CALL_NOSPEC %ebx
/*
* A kernel thread is allowed to return here after successfully
* calling do_execve(). Exit to userspace to complete the execve()
@@ -984,7 +996,8 @@
movl 0x4(%ebp), %edx
subl $MCOUNT_INSN_SIZE, %eax
- call *ftrace_trace_function
+ movl ftrace_trace_function, %ecx
+ CALL_NOSPEC %ecx
popl %edx
popl %ecx
@@ -1020,7 +1033,7 @@
movl %eax, %ecx
popl %edx
popl %eax
- jmp *%ecx
+ JMP_NOSPEC %ecx
#endif
#ifdef CONFIG_TRACING
@@ -1062,7 +1075,7 @@
movl %ecx, %es
TRACE_IRQS_OFF
movl %esp, %eax # pt_regs pointer
- call *%edi
+ CALL_NOSPEC %edi
jmp ret_from_exception
END(page_fault)
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index e7b0e7f..e729e15 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -36,6 +36,8 @@
#include <asm/smap.h>
#include <asm/pgtable_types.h>
#include <asm/export.h>
+#include <asm/kaiser.h>
+#include <asm/nospec-branch.h>
#include <linux/err.h>
/* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this. */
@@ -146,6 +148,7 @@
* it is too small to ever cause noticeable irq latency.
*/
SWAPGS_UNSAFE_STACK
+ SWITCH_KERNEL_CR3_NO_STACK
/*
* A hypervisor implementation might want to use a label
* after the swapgs, so that it can do the swapgs
@@ -206,7 +209,12 @@
* It might end up jumping to the slow path. If it jumps, RAX
* and all argument registers are clobbered.
*/
+#ifdef CONFIG_RETPOLINE
+ movq sys_call_table(, %rax, 8), %rax
+ call __x86_indirect_thunk_rax
+#else
call *sys_call_table(, %rax, 8)
+#endif
.Lentry_SYSCALL_64_after_fastpath_call:
movq %rax, RAX(%rsp)
@@ -228,6 +236,14 @@
movq RIP(%rsp), %rcx
movq EFLAGS(%rsp), %r11
RESTORE_C_REGS_EXCEPT_RCX_R11
+ /*
+ * This opens a window where we have a user CR3, but are
+ * running in the kernel. This makes using the CS
+ * register useless for telling whether or not we need to
+ * switch CR3 in NMIs. Normal interrupts are OK because
+ * they are off here.
+ */
+ SWITCH_USER_CR3
movq RSP(%rsp), %rsp
USERGS_SYSRET64
@@ -323,10 +339,26 @@
syscall_return_via_sysret:
/* rcx and r11 are already restored (see code above) */
RESTORE_C_REGS_EXCEPT_RCX_R11
+ /*
+ * This opens a window where we have a user CR3, but are
+ * running in the kernel. This makes using the CS
+ * register useless for telling whether or not we need to
+ * switch CR3 in NMIs. Normal interrupts are OK because
+ * they are off here.
+ */
+ SWITCH_USER_CR3
movq RSP(%rsp), %rsp
USERGS_SYSRET64
opportunistic_sysret_failed:
+ /*
+ * This opens a window where we have a user CR3, but are
+ * running in the kernel. This makes using the CS
+ * register useless for telling whether or not we need to
+ * switch CR3 in NMIs. Normal interrupts are OK because
+ * they are off here.
+ */
+ SWITCH_USER_CR3
SWAPGS
jmp restore_c_regs_and_iret
END(entry_SYSCALL_64)
@@ -354,7 +386,7 @@
jmp entry_SYSCALL64_slow_path
1:
- jmp *%rax /* Called from C */
+ JMP_NOSPEC %rax /* Called from C */
END(stub_ptregs_64)
.macro ptregs_stub func
@@ -395,6 +427,17 @@
movq %rbx, PER_CPU_VAR(irq_stack_union)+stack_canary_offset
#endif
+#ifdef CONFIG_RETPOLINE
+ /*
+ * When switching from a shallower to a deeper call stack
+ * the RSB may either underflow or use entries populated
+ * with userspace addresses. On CPUs where those concerns
+ * exist, overwrite the RSB with entries which capture
+ * speculative execution to prevent attack.
+ */
+ FILL_RETURN_BUFFER %r12, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW
+#endif
+
/* restore callee-saved registers */
popq %r15
popq %r14
@@ -424,13 +467,14 @@
movq %rsp, %rdi
call syscall_return_slowpath /* returns with IRQs disabled */
TRACE_IRQS_ON /* user mode is traced as IRQS on */
+ SWITCH_USER_CR3
SWAPGS
jmp restore_regs_and_iret
1:
/* kernel thread */
movq %r12, %rdi
- call *%rbx
+ CALL_NOSPEC %rbx
/*
* A kernel thread is allowed to return here after successfully
* calling do_execve(). Exit to userspace to complete the execve()
@@ -478,6 +522,7 @@
* tracking that we're in kernel mode.
*/
SWAPGS
+ SWITCH_KERNEL_CR3
/*
* We need to tell lockdep that IRQs are off. We can't do this until
@@ -535,6 +580,7 @@
mov %rsp,%rdi
call prepare_exit_to_usermode
TRACE_IRQS_IRETQ
+ SWITCH_USER_CR3
SWAPGS
jmp restore_regs_and_iret
@@ -612,6 +658,7 @@
pushq %rdi /* Stash user RDI */
SWAPGS
+ SWITCH_KERNEL_CR3
movq PER_CPU_VAR(espfix_waddr), %rdi
movq %rax, (0*8)(%rdi) /* user RAX */
movq (1*8)(%rsp), %rax /* user RIP */
@@ -638,6 +685,7 @@
* still points to an RO alias of the ESPFIX stack.
*/
orq PER_CPU_VAR(espfix_stack), %rax
+ SWITCH_USER_CR3
SWAPGS
movq %rax, %rsp
@@ -1016,13 +1064,17 @@
#endif
#ifdef CONFIG_X86_MCE
-idtentry machine_check has_error_code=0 paranoid=1 do_sym=*machine_check_vector(%rip)
+idtentry machine_check do_mce has_error_code=0 paranoid=1
#endif
/*
* Save all registers in pt_regs, and switch gs if needed.
* Use slow, but surefire "are we in kernel?" check.
- * Return: ebx=0: need swapgs on exit, ebx=1: otherwise
+ *
+ * Return: ebx=0: needs swapgs but not SWITCH_USER_CR3 in paranoid_exit
+ * ebx=1: needs neither swapgs nor SWITCH_USER_CR3 in paranoid_exit
+ * ebx=2: needs both swapgs and SWITCH_USER_CR3 in paranoid_exit
+ * ebx=3: needs SWITCH_USER_CR3 but not swapgs in paranoid_exit
*/
ENTRY(paranoid_entry)
cld
@@ -1035,7 +1087,26 @@
js 1f /* negative -> in kernel */
SWAPGS
xorl %ebx, %ebx
-1: ret
+1:
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ /*
+ * We might have come in between a swapgs and a SWITCH_KERNEL_CR3
+ * on entry, or between a SWITCH_USER_CR3 and a swapgs on exit.
+ * Do a conditional SWITCH_KERNEL_CR3: this could safely be done
+ * unconditionally, but we need to find out whether the reverse
+ * should be done on return (conveyed to paranoid_exit in %ebx).
+ */
+ ALTERNATIVE "jmp 2f", "movq %cr3, %rax", X86_FEATURE_KAISER
+ testl $KAISER_SHADOW_PGD_OFFSET, %eax
+ jz 2f
+ orl $2, %ebx
+ andq $(~(X86_CR3_PCID_ASID_MASK | KAISER_SHADOW_PGD_OFFSET)), %rax
+ /* If PCID enabled, set X86_CR3_PCID_NOFLUSH_BIT */
+ ALTERNATIVE "", "bts $63, %rax", X86_FEATURE_PCID
+ movq %rax, %cr3
+2:
+#endif
+ ret
END(paranoid_entry)
/*
@@ -1048,19 +1119,26 @@
* be complicated. Fortunately, we there's no good reason
* to try to handle preemption here.
*
- * On entry, ebx is "no swapgs" flag (1: don't need swapgs, 0: need it)
+ * On entry: ebx=0: needs swapgs but not SWITCH_USER_CR3
+ * ebx=1: needs neither swapgs nor SWITCH_USER_CR3
+ * ebx=2: needs both swapgs and SWITCH_USER_CR3
+ * ebx=3: needs SWITCH_USER_CR3 but not swapgs
*/
ENTRY(paranoid_exit)
DISABLE_INTERRUPTS(CLBR_NONE)
TRACE_IRQS_OFF_DEBUG
- testl %ebx, %ebx /* swapgs needed? */
- jnz paranoid_exit_no_swapgs
- TRACE_IRQS_IRETQ
- SWAPGS_UNSAFE_STACK
- jmp paranoid_exit_restore
-paranoid_exit_no_swapgs:
TRACE_IRQS_IRETQ_DEBUG
-paranoid_exit_restore:
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ /* No ALTERNATIVE for X86_FEATURE_KAISER: paranoid_entry sets %ebx */
+ testl $2, %ebx /* SWITCH_USER_CR3 needed? */
+ jz paranoid_exit_no_switch
+ SWITCH_USER_CR3
+paranoid_exit_no_switch:
+#endif
+ testl $1, %ebx /* swapgs needed? */
+ jnz paranoid_exit_no_swapgs
+ SWAPGS_UNSAFE_STACK
+paranoid_exit_no_swapgs:
RESTORE_EXTRA_REGS
RESTORE_C_REGS
REMOVE_PT_GPREGS_FROM_STACK 8
@@ -1075,6 +1153,13 @@
cld
SAVE_C_REGS 8
SAVE_EXTRA_REGS 8
+ /*
+ * error_entry() always returns with a kernel gsbase and
+ * CR3. We must also have a kernel CR3/gsbase before
+ * calling TRACE_IRQS_*. Just unconditionally switch to
+ * the kernel CR3 here.
+ */
+ SWITCH_KERNEL_CR3
xorl %ebx, %ebx
testb $3, CS+8(%rsp)
jz .Lerror_kernelspace
@@ -1235,6 +1320,10 @@
*/
SWAPGS_UNSAFE_STACK
+ /*
+ * percpu variables are mapped with user CR3, so no need
+ * to switch CR3 here.
+ */
cld
movq %rsp, %rdx
movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp
@@ -1268,12 +1357,34 @@
movq %rsp, %rdi
movq $-1, %rsi
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ /* Unconditionally use kernel CR3 for do_nmi() */
+ /* %rax is saved above, so OK to clobber here */
+ ALTERNATIVE "jmp 2f", "movq %cr3, %rax", X86_FEATURE_KAISER
+ /* If PCID enabled, NOFLUSH now and NOFLUSH on return */
+ ALTERNATIVE "", "bts $63, %rax", X86_FEATURE_PCID
+ pushq %rax
+ /* mask off "user" bit of pgd address and 12 PCID bits: */
+ andq $(~(X86_CR3_PCID_ASID_MASK | KAISER_SHADOW_PGD_OFFSET)), %rax
+ movq %rax, %cr3
+2:
+#endif
call do_nmi
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ /*
+ * Unconditionally restore CR3. I know we return to
+ * kernel code that needs user CR3, but do we ever return
+ * to "user mode" where we need the kernel CR3?
+ */
+ ALTERNATIVE "", "popq %rax; movq %rax, %cr3", X86_FEATURE_KAISER
+#endif
+
/*
* Return back to user mode. We must *not* do the normal exit
- * work, because we don't want to enable interrupts. Fortunately,
- * do_nmi doesn't modify pt_regs.
+ * work, because we don't want to enable interrupts. Do not
+ * switch to user CR3: we might be going back to kernel code
+ * that had a user CR3 set.
*/
SWAPGS
jmp restore_c_regs_and_iret
@@ -1470,22 +1581,55 @@
ALLOC_PT_GPREGS_ON_STACK
/*
- * Use paranoid_entry to handle SWAPGS, but no need to use paranoid_exit
- * as we should not be calling schedule in NMI context.
- * Even with normal interrupts enabled. An NMI should not be
- * setting NEED_RESCHED or anything that normal interrupts and
- * exceptions might do.
+ * Use the same approach as paranoid_entry to handle SWAPGS, but
+ * without CR3 handling since we do that differently in NMIs. No
+ * need to use paranoid_exit as we should not be calling schedule
+ * in NMI context. Even with normal interrupts enabled. An NMI
+ * should not be setting NEED_RESCHED or anything that normal
+ * interrupts and exceptions might do.
*/
- call paranoid_entry
-
- /* paranoidentry do_nmi, 0; without TRACE_IRQS_OFF */
+ cld
+ SAVE_C_REGS
+ SAVE_EXTRA_REGS
+ movl $1, %ebx
+ movl $MSR_GS_BASE, %ecx
+ rdmsr
+ testl %edx, %edx
+ js 1f /* negative -> in kernel */
+ SWAPGS
+ xorl %ebx, %ebx
+1:
movq %rsp, %rdi
movq $-1, %rsi
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ /* Unconditionally use kernel CR3 for do_nmi() */
+ /* %rax is saved above, so OK to clobber here */
+ ALTERNATIVE "jmp 2f", "movq %cr3, %rax", X86_FEATURE_KAISER
+ /* If PCID enabled, NOFLUSH now and NOFLUSH on return */
+ ALTERNATIVE "", "bts $63, %rax", X86_FEATURE_PCID
+ pushq %rax
+ /* mask off "user" bit of pgd address and 12 PCID bits: */
+ andq $(~(X86_CR3_PCID_ASID_MASK | KAISER_SHADOW_PGD_OFFSET)), %rax
+ movq %rax, %cr3
+2:
+#endif
+
+ /* paranoidentry do_nmi, 0; without TRACE_IRQS_OFF */
call do_nmi
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ /*
+ * Unconditionally restore CR3. We might be returning to
+ * kernel code that needs user CR3, like just just before
+ * a sysret.
+ */
+ ALTERNATIVE "", "popq %rax; movq %rax, %cr3", X86_FEATURE_KAISER
+#endif
+
testl %ebx, %ebx /* swapgs needed? */
jnz nmi_restore
nmi_swapgs:
+ /* We fixed up CR3 above, so no need to switch it here */
SWAPGS_UNSAFE_STACK
nmi_restore:
RESTORE_EXTRA_REGS
diff --git a/arch/x86/entry/entry_64_compat.S b/arch/x86/entry/entry_64_compat.S
index e1721da..d76a976 100644
--- a/arch/x86/entry/entry_64_compat.S
+++ b/arch/x86/entry/entry_64_compat.S
@@ -13,6 +13,8 @@
#include <asm/irqflags.h>
#include <asm/asm.h>
#include <asm/smap.h>
+#include <asm/pgtable_types.h>
+#include <asm/kaiser.h>
#include <linux/linkage.h>
#include <linux/err.h>
@@ -48,6 +50,7 @@
ENTRY(entry_SYSENTER_compat)
/* Interrupts are off on entry. */
SWAPGS_UNSAFE_STACK
+ SWITCH_KERNEL_CR3_NO_STACK
movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp
/*
@@ -184,6 +187,7 @@
ENTRY(entry_SYSCALL_compat)
/* Interrupts are off on entry. */
SWAPGS_UNSAFE_STACK
+ SWITCH_KERNEL_CR3_NO_STACK
/* Stash user ESP and switch to the kernel stack. */
movl %esp, %r8d
@@ -259,6 +263,7 @@
xorq %r8, %r8
xorq %r9, %r9
xorq %r10, %r10
+ SWITCH_USER_CR3
movq RSP-ORIG_RAX(%rsp), %rsp
swapgs
sysretl
@@ -297,7 +302,7 @@
PARAVIRT_ADJUST_EXCEPTION_FRAME
ASM_CLAC /* Do this early to minimize exposure */
SWAPGS
-
+ SWITCH_KERNEL_CR3_NO_STACK
/*
* User tracing code (ptrace or signal handlers) might assume that
* the saved RAX contains a 32-bit number when we're invoking a 32-bit
@@ -338,6 +343,7 @@
/* Go back to user mode. */
TRACE_IRQS_ON
+ SWITCH_USER_CR3
SWAPGS
jmp restore_regs_and_iret
END(entry_INT80_compat)
diff --git a/arch/x86/entry/vsyscall/vsyscall_64.c b/arch/x86/entry/vsyscall/vsyscall_64.c
index 636c4b3..0174290 100644
--- a/arch/x86/entry/vsyscall/vsyscall_64.c
+++ b/arch/x86/entry/vsyscall/vsyscall_64.c
@@ -46,6 +46,7 @@
#else
EMULATE;
#endif
+unsigned long vsyscall_pgprot = __PAGE_KERNEL_VSYSCALL;
static int __init vsyscall_setup(char *str)
{
@@ -66,6 +67,11 @@
}
early_param("vsyscall", vsyscall_setup);
+bool vsyscall_enabled(void)
+{
+ return vsyscall_mode != NONE;
+}
+
static void warn_bad_vsyscall(const char *level, struct pt_regs *regs,
const char *message)
{
@@ -331,11 +337,11 @@
extern char __vsyscall_page;
unsigned long physaddr_vsyscall = __pa_symbol(&__vsyscall_page);
+ if (vsyscall_mode != NATIVE)
+ vsyscall_pgprot = __PAGE_KERNEL_VVAR;
if (vsyscall_mode != NONE)
__set_fixmap(VSYSCALL_PAGE, physaddr_vsyscall,
- vsyscall_mode == NATIVE
- ? PAGE_KERNEL_VSYSCALL
- : PAGE_KERNEL_VVAR);
+ __pgprot(vsyscall_pgprot));
BUILD_BUG_ON((unsigned long)__fix_to_virt(VSYSCALL_PAGE) !=
(unsigned long)VSYSCALL_ADDR);
diff --git a/arch/x86/events/amd/power.c b/arch/x86/events/amd/power.c
index 9842270..21a4e41 100644
--- a/arch/x86/events/amd/power.c
+++ b/arch/x86/events/amd/power.c
@@ -277,7 +277,7 @@
int ret;
if (!x86_match_cpu(cpu_match))
- return 0;
+ return -ENODEV;
if (!boot_cpu_has(X86_FEATURE_ACC_POWER))
return -ENODEV;
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index 9dfeeec..8e7a3f1 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -2,11 +2,15 @@
#include <linux/types.h>
#include <linux/slab.h>
+#include <asm/kaiser.h>
#include <asm/perf_event.h>
#include <asm/insn.h>
#include "../perf_event.h"
+static
+DEFINE_PER_CPU_SHARED_ALIGNED_USER_MAPPED(struct debug_store, cpu_debug_store);
+
/* The size of a BTS record in bytes: */
#define BTS_RECORD_SIZE 24
@@ -268,6 +272,39 @@
static DEFINE_PER_CPU(void *, insn_buffer);
+static void *dsalloc(size_t size, gfp_t flags, int node)
+{
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ unsigned int order = get_order(size);
+ struct page *page;
+ unsigned long addr;
+
+ page = __alloc_pages_node(node, flags | __GFP_ZERO, order);
+ if (!page)
+ return NULL;
+ addr = (unsigned long)page_address(page);
+ if (kaiser_add_mapping(addr, size, __PAGE_KERNEL) < 0) {
+ __free_pages(page, order);
+ addr = 0;
+ }
+ return (void *)addr;
+#else
+ return kmalloc_node(size, flags | __GFP_ZERO, node);
+#endif
+}
+
+static void dsfree(const void *buffer, size_t size)
+{
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ if (!buffer)
+ return;
+ kaiser_remove_mapping((unsigned long)buffer, size);
+ free_pages((unsigned long)buffer, get_order(size));
+#else
+ kfree(buffer);
+#endif
+}
+
static int alloc_pebs_buffer(int cpu)
{
struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds;
@@ -278,7 +315,7 @@
if (!x86_pmu.pebs)
return 0;
- buffer = kzalloc_node(x86_pmu.pebs_buffer_size, GFP_KERNEL, node);
+ buffer = dsalloc(x86_pmu.pebs_buffer_size, GFP_KERNEL, node);
if (unlikely(!buffer))
return -ENOMEM;
@@ -289,7 +326,7 @@
if (x86_pmu.intel_cap.pebs_format < 2) {
ibuffer = kzalloc_node(PEBS_FIXUP_SIZE, GFP_KERNEL, node);
if (!ibuffer) {
- kfree(buffer);
+ dsfree(buffer, x86_pmu.pebs_buffer_size);
return -ENOMEM;
}
per_cpu(insn_buffer, cpu) = ibuffer;
@@ -315,7 +352,8 @@
kfree(per_cpu(insn_buffer, cpu));
per_cpu(insn_buffer, cpu) = NULL;
- kfree((void *)(unsigned long)ds->pebs_buffer_base);
+ dsfree((void *)(unsigned long)ds->pebs_buffer_base,
+ x86_pmu.pebs_buffer_size);
ds->pebs_buffer_base = 0;
}
@@ -329,7 +367,7 @@
if (!x86_pmu.bts)
return 0;
- buffer = kzalloc_node(BTS_BUFFER_SIZE, GFP_KERNEL | __GFP_NOWARN, node);
+ buffer = dsalloc(BTS_BUFFER_SIZE, GFP_KERNEL | __GFP_NOWARN, node);
if (unlikely(!buffer)) {
WARN_ONCE(1, "%s: BTS buffer allocation failure\n", __func__);
return -ENOMEM;
@@ -355,19 +393,15 @@
if (!ds || !x86_pmu.bts)
return;
- kfree((void *)(unsigned long)ds->bts_buffer_base);
+ dsfree((void *)(unsigned long)ds->bts_buffer_base, BTS_BUFFER_SIZE);
ds->bts_buffer_base = 0;
}
static int alloc_ds_buffer(int cpu)
{
- int node = cpu_to_node(cpu);
- struct debug_store *ds;
+ struct debug_store *ds = per_cpu_ptr(&cpu_debug_store, cpu);
- ds = kzalloc_node(sizeof(*ds), GFP_KERNEL, node);
- if (unlikely(!ds))
- return -ENOMEM;
-
+ memset(ds, 0, sizeof(*ds));
per_cpu(cpu_hw_events, cpu).ds = ds;
return 0;
@@ -381,7 +415,6 @@
return;
per_cpu(cpu_hw_events, cpu).ds = NULL;
- kfree(ds);
}
void release_ds_buffers(void)
diff --git a/arch/x86/include/asm/alternative.h b/arch/x86/include/asm/alternative.h
index d4aea31..deca9b9 100644
--- a/arch/x86/include/asm/alternative.h
+++ b/arch/x86/include/asm/alternative.h
@@ -139,7 +139,7 @@
".popsection\n" \
".pushsection .altinstr_replacement, \"ax\"\n" \
ALTINSTR_REPLACEMENT(newinstr, feature, 1) \
- ".popsection"
+ ".popsection\n"
#define ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2)\
OLDINSTR_2(oldinstr, 1, 2) \
@@ -150,7 +150,7 @@
".pushsection .altinstr_replacement, \"ax\"\n" \
ALTINSTR_REPLACEMENT(newinstr1, feature1, 1) \
ALTINSTR_REPLACEMENT(newinstr2, feature2, 2) \
- ".popsection"
+ ".popsection\n"
/*
* Alternative instructions for different CPU types or capabilities.
diff --git a/arch/x86/include/asm/asm-prototypes.h b/arch/x86/include/asm/asm-prototypes.h
index 44b8762..b15aa40 100644
--- a/arch/x86/include/asm/asm-prototypes.h
+++ b/arch/x86/include/asm/asm-prototypes.h
@@ -10,7 +10,32 @@
#include <asm/pgtable.h>
#include <asm/special_insns.h>
#include <asm/preempt.h>
+#include <asm/asm.h>
#ifndef CONFIG_X86_CMPXCHG64
extern void cmpxchg8b_emu(void);
#endif
+
+#ifdef CONFIG_RETPOLINE
+#ifdef CONFIG_X86_32
+#define INDIRECT_THUNK(reg) extern asmlinkage void __x86_indirect_thunk_e ## reg(void);
+#else
+#define INDIRECT_THUNK(reg) extern asmlinkage void __x86_indirect_thunk_r ## reg(void);
+INDIRECT_THUNK(8)
+INDIRECT_THUNK(9)
+INDIRECT_THUNK(10)
+INDIRECT_THUNK(11)
+INDIRECT_THUNK(12)
+INDIRECT_THUNK(13)
+INDIRECT_THUNK(14)
+INDIRECT_THUNK(15)
+#endif
+INDIRECT_THUNK(ax)
+INDIRECT_THUNK(bx)
+INDIRECT_THUNK(cx)
+INDIRECT_THUNK(dx)
+INDIRECT_THUNK(si)
+INDIRECT_THUNK(di)
+INDIRECT_THUNK(bp)
+INDIRECT_THUNK(sp)
+#endif /* CONFIG_RETPOLINE */
diff --git a/arch/x86/include/asm/asm.h b/arch/x86/include/asm/asm.h
index 7acb51c..0052352 100644
--- a/arch/x86/include/asm/asm.h
+++ b/arch/x86/include/asm/asm.h
@@ -125,4 +125,15 @@
/* For C file, we already have NOKPROBE_SYMBOL macro */
#endif
+#ifndef __ASSEMBLY__
+/*
+ * This output constraint should be used for any inline asm which has a "call"
+ * instruction. Otherwise the asm may be inserted before the frame pointer
+ * gets set up by the containing function. If you forget to do this, objtool
+ * may print a "call without frame pointer save/setup" warning.
+ */
+register unsigned long current_stack_pointer asm(_ASM_SP);
+#define ASM_CALL_CONSTRAINT "+r" (current_stack_pointer)
+#endif
+
#endif /* _ASM_X86_ASM_H */
diff --git a/arch/x86/include/asm/cmdline.h b/arch/x86/include/asm/cmdline.h
index e01f7f7..84ae170 100644
--- a/arch/x86/include/asm/cmdline.h
+++ b/arch/x86/include/asm/cmdline.h
@@ -2,5 +2,7 @@
#define _ASM_X86_CMDLINE_H
int cmdline_find_option_bool(const char *cmdline_ptr, const char *option);
+int cmdline_find_option(const char *cmdline_ptr, const char *option,
+ char *buffer, int bufsize);
#endif /* _ASM_X86_CMDLINE_H */
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h
index 1d2b69f..9ea67a0 100644
--- a/arch/x86/include/asm/cpufeature.h
+++ b/arch/x86/include/asm/cpufeature.h
@@ -135,6 +135,8 @@
set_bit(bit, (unsigned long *)cpu_caps_set); \
} while (0)
+#define setup_force_cpu_bug(bit) setup_force_cpu_cap(bit)
+
#if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_X86_FAST_FEATURE_TESTS)
/*
* Static testing of CPU features. Used the same as boot_cpu_has().
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index ed10b5b..8537a21 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -189,13 +189,20 @@
#define X86_FEATURE_CPB ( 7*32+ 2) /* AMD Core Performance Boost */
#define X86_FEATURE_EPB ( 7*32+ 3) /* IA32_ENERGY_PERF_BIAS support */
+#define X86_FEATURE_INVPCID_SINGLE ( 7*32+ 4) /* Effectively INVPCID && CR4.PCIDE=1 */
#define X86_FEATURE_HW_PSTATE ( 7*32+ 8) /* AMD HW-PState */
#define X86_FEATURE_PROC_FEEDBACK ( 7*32+ 9) /* AMD ProcFeedbackInterface */
-#define X86_FEATURE_INTEL_PT ( 7*32+15) /* Intel Processor Trace */
+#define X86_FEATURE_RETPOLINE ( 7*32+12) /* Generic Retpoline mitigation for Spectre variant 2 */
+#define X86_FEATURE_RETPOLINE_AMD ( 7*32+13) /* AMD Retpoline mitigation for Spectre variant 2 */
+
#define X86_FEATURE_AVX512_4VNNIW (7*32+16) /* AVX-512 Neural Network Instructions */
#define X86_FEATURE_AVX512_4FMAPS (7*32+17) /* AVX-512 Multiply Accumulation Single precision */
+#define X86_FEATURE_RSB_CTXSW ( 7*32+19) /* Fill RSB on context switches */
+
+/* Because the ALTERNATIVE scheme is for members of the X86_FEATURE club... */
+#define X86_FEATURE_KAISER ( 7*32+31) /* CONFIG_PAGE_TABLE_ISOLATION w/o nokaiser */
/* Virtualization flags: Linux defined, word 8 */
#define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */
@@ -228,6 +235,7 @@
#define X86_FEATURE_SMAP ( 9*32+20) /* Supervisor Mode Access Prevention */
#define X86_FEATURE_CLFLUSHOPT ( 9*32+23) /* CLFLUSHOPT instruction */
#define X86_FEATURE_CLWB ( 9*32+24) /* CLWB instruction */
+#define X86_FEATURE_INTEL_PT ( 9*32+25) /* Intel Processor Trace */
#define X86_FEATURE_AVX512PF ( 9*32+26) /* AVX-512 Prefetch */
#define X86_FEATURE_AVX512ER ( 9*32+27) /* AVX-512 Exponential and Reciprocal */
#define X86_FEATURE_AVX512CD ( 9*32+28) /* AVX-512 Conflict Detection */
@@ -312,5 +320,8 @@
#define X86_BUG_SWAPGS_FENCE X86_BUG(11) /* SWAPGS without input dep on GS */
#define X86_BUG_MONITOR X86_BUG(12) /* IPI required to wake up remote CPU */
#define X86_BUG_AMD_E400 X86_BUG(13) /* CPU is among the affected by Erratum 400 */
+#define X86_BUG_CPU_MELTDOWN X86_BUG(14) /* CPU is affected by meltdown attack and needs kernel page table isolation */
+#define X86_BUG_SPECTRE_V1 X86_BUG(15) /* CPU is affected by Spectre variant 1 attack with conditional branches */
+#define X86_BUG_SPECTRE_V2 X86_BUG(16) /* CPU is affected by Spectre variant 2 attack with indirect branches */
#endif /* _ASM_X86_CPUFEATURES_H */
diff --git a/arch/x86/include/asm/desc.h b/arch/x86/include/asm/desc.h
index 12080d8..2ed5a2b 100644
--- a/arch/x86/include/asm/desc.h
+++ b/arch/x86/include/asm/desc.h
@@ -43,7 +43,7 @@
struct desc_struct gdt[GDT_ENTRIES];
} __attribute__((aligned(PAGE_SIZE)));
-DECLARE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page);
+DECLARE_PER_CPU_PAGE_ALIGNED_USER_MAPPED(struct gdt_page, gdt_page);
static inline struct desc_struct *get_cpu_gdt_table(unsigned int cpu)
{
diff --git a/arch/x86/include/asm/disabled-features.h b/arch/x86/include/asm/disabled-features.h
index 85599ad..21c5ac1 100644
--- a/arch/x86/include/asm/disabled-features.h
+++ b/arch/x86/include/asm/disabled-features.h
@@ -21,11 +21,13 @@
# define DISABLE_K6_MTRR (1<<(X86_FEATURE_K6_MTRR & 31))
# define DISABLE_CYRIX_ARR (1<<(X86_FEATURE_CYRIX_ARR & 31))
# define DISABLE_CENTAUR_MCR (1<<(X86_FEATURE_CENTAUR_MCR & 31))
+# define DISABLE_PCID 0
#else
# define DISABLE_VME 0
# define DISABLE_K6_MTRR 0
# define DISABLE_CYRIX_ARR 0
# define DISABLE_CENTAUR_MCR 0
+# define DISABLE_PCID (1<<(X86_FEATURE_PCID & 31))
#endif /* CONFIG_X86_64 */
#ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS
@@ -43,7 +45,7 @@
#define DISABLED_MASK1 0
#define DISABLED_MASK2 0
#define DISABLED_MASK3 (DISABLE_CYRIX_ARR|DISABLE_CENTAUR_MCR|DISABLE_K6_MTRR)
-#define DISABLED_MASK4 0
+#define DISABLED_MASK4 (DISABLE_PCID)
#define DISABLED_MASK5 0
#define DISABLED_MASK6 0
#define DISABLED_MASK7 0
diff --git a/arch/x86/include/asm/hardirq.h b/arch/x86/include/asm/hardirq.h
index 59405a2..9b76cd3 100644
--- a/arch/x86/include/asm/hardirq.h
+++ b/arch/x86/include/asm/hardirq.h
@@ -22,8 +22,8 @@
#ifdef CONFIG_SMP
unsigned int irq_resched_count;
unsigned int irq_call_count;
- unsigned int irq_tlb_count;
#endif
+ unsigned int irq_tlb_count;
#ifdef CONFIG_X86_THERMAL_VECTOR
unsigned int irq_thermal_count;
#endif
diff --git a/arch/x86/include/asm/hw_irq.h b/arch/x86/include/asm/hw_irq.h
index b90e105..0817d63 100644
--- a/arch/x86/include/asm/hw_irq.h
+++ b/arch/x86/include/asm/hw_irq.h
@@ -178,7 +178,7 @@
#define VECTOR_RETRIGGERED ((void *)~0UL)
typedef struct irq_desc* vector_irq_t[NR_VECTORS];
-DECLARE_PER_CPU(vector_irq_t, vector_irq);
+DECLARE_PER_CPU_USER_MAPPED(vector_irq_t, vector_irq);
#endif /* !ASSEMBLY_ */
diff --git a/arch/x86/include/asm/kaiser.h b/arch/x86/include/asm/kaiser.h
new file mode 100644
index 0000000..802bbbd
--- /dev/null
+++ b/arch/x86/include/asm/kaiser.h
@@ -0,0 +1,141 @@
+#ifndef _ASM_X86_KAISER_H
+#define _ASM_X86_KAISER_H
+
+#include <uapi/asm/processor-flags.h> /* For PCID constants */
+
+/*
+ * This file includes the definitions for the KAISER feature.
+ * KAISER is a counter measure against x86_64 side channel attacks on
+ * the kernel virtual memory. It has a shadow pgd for every process: the
+ * shadow pgd has a minimalistic kernel-set mapped, but includes the whole
+ * user memory. Within a kernel context switch, or when an interrupt is handled,
+ * the pgd is switched to the normal one. When the system switches to user mode,
+ * the shadow pgd is enabled. By this, the virtual memory caches are freed,
+ * and the user may not attack the whole kernel memory.
+ *
+ * A minimalistic kernel mapping holds the parts needed to be mapped in user
+ * mode, such as the entry/exit functions of the user space, or the stacks.
+ */
+
+#define KAISER_SHADOW_PGD_OFFSET 0x1000
+
+#ifdef __ASSEMBLY__
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+
+.macro _SWITCH_TO_KERNEL_CR3 reg
+movq %cr3, \reg
+andq $(~(X86_CR3_PCID_ASID_MASK | KAISER_SHADOW_PGD_OFFSET)), \reg
+/* If PCID enabled, set X86_CR3_PCID_NOFLUSH_BIT */
+ALTERNATIVE "", "bts $63, \reg", X86_FEATURE_PCID
+movq \reg, %cr3
+.endm
+
+.macro _SWITCH_TO_USER_CR3 reg regb
+/*
+ * regb must be the low byte portion of reg: because we have arranged
+ * for the low byte of the user PCID to serve as the high byte of NOFLUSH
+ * (0x80 for each when PCID is enabled, or 0x00 when PCID and NOFLUSH are
+ * not enabled): so that the one register can update both memory and cr3.
+ */
+movq %cr3, \reg
+orq PER_CPU_VAR(x86_cr3_pcid_user), \reg
+js 9f
+/* If PCID enabled, FLUSH this time, reset to NOFLUSH for next time */
+movb \regb, PER_CPU_VAR(x86_cr3_pcid_user+7)
+9:
+movq \reg, %cr3
+.endm
+
+.macro SWITCH_KERNEL_CR3
+ALTERNATIVE "jmp 8f", "pushq %rax", X86_FEATURE_KAISER
+_SWITCH_TO_KERNEL_CR3 %rax
+popq %rax
+8:
+.endm
+
+.macro SWITCH_USER_CR3
+ALTERNATIVE "jmp 8f", "pushq %rax", X86_FEATURE_KAISER
+_SWITCH_TO_USER_CR3 %rax %al
+popq %rax
+8:
+.endm
+
+.macro SWITCH_KERNEL_CR3_NO_STACK
+ALTERNATIVE "jmp 8f", \
+ __stringify(movq %rax, PER_CPU_VAR(unsafe_stack_register_backup)), \
+ X86_FEATURE_KAISER
+_SWITCH_TO_KERNEL_CR3 %rax
+movq PER_CPU_VAR(unsafe_stack_register_backup), %rax
+8:
+.endm
+
+#else /* CONFIG_PAGE_TABLE_ISOLATION */
+
+.macro SWITCH_KERNEL_CR3
+.endm
+.macro SWITCH_USER_CR3
+.endm
+.macro SWITCH_KERNEL_CR3_NO_STACK
+.endm
+
+#endif /* CONFIG_PAGE_TABLE_ISOLATION */
+
+#else /* __ASSEMBLY__ */
+
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+/*
+ * Upon kernel/user mode switch, it may happen that the address
+ * space has to be switched before the registers have been
+ * stored. To change the address space, another register is
+ * needed. A register therefore has to be stored/restored.
+*/
+DECLARE_PER_CPU_USER_MAPPED(unsigned long, unsafe_stack_register_backup);
+
+DECLARE_PER_CPU(unsigned long, x86_cr3_pcid_user);
+
+extern char __per_cpu_user_mapped_start[], __per_cpu_user_mapped_end[];
+
+extern int kaiser_enabled;
+extern void __init kaiser_check_boottime_disable(void);
+#else
+#define kaiser_enabled 0
+static inline void __init kaiser_check_boottime_disable(void) {}
+#endif /* CONFIG_PAGE_TABLE_ISOLATION */
+
+/*
+ * Kaiser function prototypes are needed even when CONFIG_PAGE_TABLE_ISOLATION is not set,
+ * so as to build with tests on kaiser_enabled instead of #ifdefs.
+ */
+
+/**
+ * kaiser_add_mapping - map a virtual memory part to the shadow (user) mapping
+ * @addr: the start address of the range
+ * @size: the size of the range
+ * @flags: The mapping flags of the pages
+ *
+ * The mapping is done on a global scope, so no bigger
+ * synchronization has to be done. the pages have to be
+ * manually unmapped again when they are not needed any longer.
+ */
+extern int kaiser_add_mapping(unsigned long addr, unsigned long size, unsigned long flags);
+
+/**
+ * kaiser_remove_mapping - unmap a virtual memory part of the shadow mapping
+ * @addr: the start address of the range
+ * @size: the size of the range
+ */
+extern void kaiser_remove_mapping(unsigned long start, unsigned long size);
+
+/**
+ * kaiser_init - Initialize the shadow mapping
+ *
+ * Most parts of the shadow mapping can be mapped upon boot
+ * time. Only per-process things like the thread stacks
+ * or a new LDT have to be mapped at runtime. These boot-
+ * time mappings are permanent and never unmapped.
+ */
+extern void kaiser_init(void);
+
+#endif /* __ASSEMBLY */
+
+#endif /* _ASM_X86_KAISER_H */
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index cbd1d44..20cfeeb 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1113,7 +1113,8 @@
static inline int emulate_instruction(struct kvm_vcpu *vcpu,
int emulation_type)
{
- return x86_emulate_instruction(vcpu, 0, emulation_type, NULL, 0);
+ return x86_emulate_instruction(vcpu, 0,
+ emulation_type | EMULTYPE_NO_REEXECUTE, NULL, 0);
}
void kvm_enable_efer_bits(u64);
diff --git a/arch/x86/include/asm/mmu.h b/arch/x86/include/asm/mmu.h
index 72198c6..8b272a0 100644
--- a/arch/x86/include/asm/mmu.h
+++ b/arch/x86/include/asm/mmu.h
@@ -33,12 +33,6 @@
#endif
} mm_context_t;
-#ifdef CONFIG_SMP
void leave_mm(int cpu);
-#else
-static inline void leave_mm(int cpu)
-{
-}
-#endif
#endif /* _ASM_X86_MMU_H */
diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h
index f9dd224..d23e355 100644
--- a/arch/x86/include/asm/mmu_context.h
+++ b/arch/x86/include/asm/mmu_context.h
@@ -99,10 +99,8 @@
static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
{
-#ifdef CONFIG_SMP
if (this_cpu_read(cpu_tlbstate.state) == TLBSTATE_OK)
this_cpu_write(cpu_tlbstate.state, TLBSTATE_LAZY);
-#endif
}
static inline int init_new_context(struct task_struct *tsk,
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index b601dda..b11c4c0 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -330,6 +330,9 @@
#define FAM10H_MMIO_CONF_BASE_MASK 0xfffffffULL
#define FAM10H_MMIO_CONF_BASE_SHIFT 20
#define MSR_FAM10H_NODE_ID 0xc001100c
+#define MSR_F10H_DECFG 0xc0011029
+#define MSR_F10H_DECFG_LFENCE_SERIALIZE_BIT 1
+#define MSR_F10H_DECFG_LFENCE_SERIALIZE BIT_ULL(MSR_F10H_DECFG_LFENCE_SERIALIZE_BIT)
/* K8 MSRs */
#define MSR_K8_TOP_MEM1 0xc001001a
diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h
new file mode 100644
index 0000000..4ad4108
--- /dev/null
+++ b/arch/x86/include/asm/nospec-branch.h
@@ -0,0 +1,222 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __NOSPEC_BRANCH_H__
+#define __NOSPEC_BRANCH_H__
+
+#include <asm/alternative.h>
+#include <asm/alternative-asm.h>
+#include <asm/cpufeatures.h>
+
+/*
+ * Fill the CPU return stack buffer.
+ *
+ * Each entry in the RSB, if used for a speculative 'ret', contains an
+ * infinite 'pause; lfence; jmp' loop to capture speculative execution.
+ *
+ * This is required in various cases for retpoline and IBRS-based
+ * mitigations for the Spectre variant 2 vulnerability. Sometimes to
+ * eliminate potentially bogus entries from the RSB, and sometimes
+ * purely to ensure that it doesn't get empty, which on some CPUs would
+ * allow predictions from other (unwanted!) sources to be used.
+ *
+ * We define a CPP macro such that it can be used from both .S files and
+ * inline assembly. It's possible to do a .macro and then include that
+ * from C via asm(".include <asm/nospec-branch.h>") but let's not go there.
+ */
+
+#define RSB_CLEAR_LOOPS 32 /* To forcibly overwrite all entries */
+#define RSB_FILL_LOOPS 16 /* To avoid underflow */
+
+/*
+ * Google experimented with loop-unrolling and this turned out to be
+ * the optimal version — two calls, each with their own speculation
+ * trap should their return address end up getting used, in a loop.
+ */
+#define __FILL_RETURN_BUFFER(reg, nr, sp) \
+ mov $(nr/2), reg; \
+771: \
+ call 772f; \
+773: /* speculation trap */ \
+ pause; \
+ lfence; \
+ jmp 773b; \
+772: \
+ call 774f; \
+775: /* speculation trap */ \
+ pause; \
+ lfence; \
+ jmp 775b; \
+774: \
+ dec reg; \
+ jnz 771b; \
+ add $(BITS_PER_LONG/8) * nr, sp;
+
+#ifdef __ASSEMBLY__
+
+/*
+ * This should be used immediately before a retpoline alternative. It tells
+ * objtool where the retpolines are so that it can make sense of the control
+ * flow by just reading the original instruction(s) and ignoring the
+ * alternatives.
+ */
+.macro ANNOTATE_NOSPEC_ALTERNATIVE
+ .Lannotate_\@:
+ .pushsection .discard.nospec
+ .long .Lannotate_\@ - .
+ .popsection
+.endm
+
+/*
+ * These are the bare retpoline primitives for indirect jmp and call.
+ * Do not use these directly; they only exist to make the ALTERNATIVE
+ * invocation below less ugly.
+ */
+.macro RETPOLINE_JMP reg:req
+ call .Ldo_rop_\@
+.Lspec_trap_\@:
+ pause
+ lfence
+ jmp .Lspec_trap_\@
+.Ldo_rop_\@:
+ mov \reg, (%_ASM_SP)
+ ret
+.endm
+
+/*
+ * This is a wrapper around RETPOLINE_JMP so the called function in reg
+ * returns to the instruction after the macro.
+ */
+.macro RETPOLINE_CALL reg:req
+ jmp .Ldo_call_\@
+.Ldo_retpoline_jmp_\@:
+ RETPOLINE_JMP \reg
+.Ldo_call_\@:
+ call .Ldo_retpoline_jmp_\@
+.endm
+
+/*
+ * JMP_NOSPEC and CALL_NOSPEC macros can be used instead of a simple
+ * indirect jmp/call which may be susceptible to the Spectre variant 2
+ * attack.
+ */
+.macro JMP_NOSPEC reg:req
+#ifdef CONFIG_RETPOLINE
+ ANNOTATE_NOSPEC_ALTERNATIVE
+ ALTERNATIVE_2 __stringify(jmp *\reg), \
+ __stringify(RETPOLINE_JMP \reg), X86_FEATURE_RETPOLINE, \
+ __stringify(lfence; jmp *\reg), X86_FEATURE_RETPOLINE_AMD
+#else
+ jmp *\reg
+#endif
+.endm
+
+.macro CALL_NOSPEC reg:req
+#ifdef CONFIG_RETPOLINE
+ ANNOTATE_NOSPEC_ALTERNATIVE
+ ALTERNATIVE_2 __stringify(call *\reg), \
+ __stringify(RETPOLINE_CALL \reg), X86_FEATURE_RETPOLINE,\
+ __stringify(lfence; call *\reg), X86_FEATURE_RETPOLINE_AMD
+#else
+ call *\reg
+#endif
+.endm
+
+ /*
+ * A simpler FILL_RETURN_BUFFER macro. Don't make people use the CPP
+ * monstrosity above, manually.
+ */
+.macro FILL_RETURN_BUFFER reg:req nr:req ftr:req
+#ifdef CONFIG_RETPOLINE
+ ANNOTATE_NOSPEC_ALTERNATIVE
+ ALTERNATIVE "jmp .Lskip_rsb_\@", \
+ __stringify(__FILL_RETURN_BUFFER(\reg,\nr,%_ASM_SP)) \
+ \ftr
+.Lskip_rsb_\@:
+#endif
+.endm
+
+#else /* __ASSEMBLY__ */
+
+#define ANNOTATE_NOSPEC_ALTERNATIVE \
+ "999:\n\t" \
+ ".pushsection .discard.nospec\n\t" \
+ ".long 999b - .\n\t" \
+ ".popsection\n\t"
+
+#if defined(CONFIG_X86_64) && defined(RETPOLINE)
+
+/*
+ * Since the inline asm uses the %V modifier which is only in newer GCC,
+ * the 64-bit one is dependent on RETPOLINE not CONFIG_RETPOLINE.
+ */
+# define CALL_NOSPEC \
+ ANNOTATE_NOSPEC_ALTERNATIVE \
+ ALTERNATIVE( \
+ "call *%[thunk_target]\n", \
+ "call __x86_indirect_thunk_%V[thunk_target]\n", \
+ X86_FEATURE_RETPOLINE)
+# define THUNK_TARGET(addr) [thunk_target] "r" (addr)
+
+#elif defined(CONFIG_X86_32) && defined(CONFIG_RETPOLINE)
+/*
+ * For i386 we use the original ret-equivalent retpoline, because
+ * otherwise we'll run out of registers. We don't care about CET
+ * here, anyway.
+ */
+# define CALL_NOSPEC ALTERNATIVE("call *%[thunk_target]\n", \
+ " jmp 904f;\n" \
+ " .align 16\n" \
+ "901: call 903f;\n" \
+ "902: pause;\n" \
+ " lfence;\n" \
+ " jmp 902b;\n" \
+ " .align 16\n" \
+ "903: addl $4, %%esp;\n" \
+ " pushl %[thunk_target];\n" \
+ " ret;\n" \
+ " .align 16\n" \
+ "904: call 901b;\n", \
+ X86_FEATURE_RETPOLINE)
+
+# define THUNK_TARGET(addr) [thunk_target] "rm" (addr)
+#else /* No retpoline for C / inline asm */
+# define CALL_NOSPEC "call *%[thunk_target]\n"
+# define THUNK_TARGET(addr) [thunk_target] "rm" (addr)
+#endif
+
+/* The Spectre V2 mitigation variants */
+enum spectre_v2_mitigation {
+ SPECTRE_V2_NONE,
+ SPECTRE_V2_RETPOLINE_MINIMAL,
+ SPECTRE_V2_RETPOLINE_MINIMAL_AMD,
+ SPECTRE_V2_RETPOLINE_GENERIC,
+ SPECTRE_V2_RETPOLINE_AMD,
+ SPECTRE_V2_IBRS,
+};
+
+extern char __indirect_thunk_start[];
+extern char __indirect_thunk_end[];
+
+/*
+ * On VMEXIT we must ensure that no RSB predictions learned in the guest
+ * can be followed in the host, by overwriting the RSB completely. Both
+ * retpoline and IBRS mitigations for Spectre v2 need this; only on future
+ * CPUs with IBRS_ATT *might* it be avoided.
+ */
+static inline void vmexit_fill_RSB(void)
+{
+#ifdef CONFIG_RETPOLINE
+ unsigned long loops;
+
+ asm volatile (ANNOTATE_NOSPEC_ALTERNATIVE
+ ALTERNATIVE("jmp 910f",
+ __stringify(__FILL_RETURN_BUFFER(%0, RSB_CLEAR_LOOPS, %1)),
+ X86_FEATURE_RETPOLINE)
+ "910:"
+ : "=r" (loops), ASM_CALL_CONSTRAINT
+ : : "memory" );
+#endif
+}
+
+#endif /* __ASSEMBLY__ */
+#endif /* __NOSPEC_BRANCH_H__ */
diff --git a/arch/x86/include/asm/pgalloc.h b/arch/x86/include/asm/pgalloc.h
index b6d4259..1178a51 100644
--- a/arch/x86/include/asm/pgalloc.h
+++ b/arch/x86/include/asm/pgalloc.h
@@ -27,6 +27,17 @@
*/
extern gfp_t __userpte_alloc_gfp;
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+/*
+ * Instead of one PGD, we acquire two PGDs. Being order-1, it is
+ * both 8k in size and 8k-aligned. That lets us just flip bit 12
+ * in a pointer to swap between the two 4k halves.
+ */
+#define PGD_ALLOCATION_ORDER 1
+#else
+#define PGD_ALLOCATION_ORDER 0
+#endif
+
/*
* Allocate and free page tables.
*/
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index 437feb4..2536f90 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -18,6 +18,12 @@
#ifndef __ASSEMBLY__
#include <asm/x86_init.h>
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+extern int kaiser_enabled;
+#else
+#define kaiser_enabled 0
+#endif
+
void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd);
void ptdump_walk_pgd_level_checkwx(void);
@@ -690,7 +696,17 @@
static inline int pgd_bad(pgd_t pgd)
{
- return (pgd_flags(pgd) & ~_PAGE_USER) != _KERNPG_TABLE;
+ pgdval_t ignore_flags = _PAGE_USER;
+ /*
+ * We set NX on KAISER pgds that map userspace memory so
+ * that userspace can not meaningfully use the kernel
+ * page table by accident; it will fault on the first
+ * instruction it tries to run. See native_set_pgd().
+ */
+ if (kaiser_enabled)
+ ignore_flags |= _PAGE_NX;
+
+ return (pgd_flags(pgd) & ~ignore_flags) != _KERNPG_TABLE;
}
static inline int pgd_none(pgd_t pgd)
@@ -903,7 +919,15 @@
*/
static inline void clone_pgd_range(pgd_t *dst, pgd_t *src, int count)
{
- memcpy(dst, src, count * sizeof(pgd_t));
+ memcpy(dst, src, count * sizeof(pgd_t));
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ if (kaiser_enabled) {
+ /* Clone the shadow pgd part as well */
+ memcpy(native_get_shadow_pgd(dst),
+ native_get_shadow_pgd(src),
+ count * sizeof(pgd_t));
+ }
+#endif
}
#define PTE_SHIFT ilog2(PTRS_PER_PTE)
diff --git a/arch/x86/include/asm/pgtable_64.h b/arch/x86/include/asm/pgtable_64.h
index 1cc82ec..ce97c8c6 100644
--- a/arch/x86/include/asm/pgtable_64.h
+++ b/arch/x86/include/asm/pgtable_64.h
@@ -106,9 +106,32 @@
native_set_pud(pud, native_make_pud(0));
}
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+extern pgd_t kaiser_set_shadow_pgd(pgd_t *pgdp, pgd_t pgd);
+
+static inline pgd_t *native_get_shadow_pgd(pgd_t *pgdp)
+{
+#ifdef CONFIG_DEBUG_VM
+ /* linux/mmdebug.h may not have been included at this point */
+ BUG_ON(!kaiser_enabled);
+#endif
+ return (pgd_t *)((unsigned long)pgdp | (unsigned long)PAGE_SIZE);
+}
+#else
+static inline pgd_t kaiser_set_shadow_pgd(pgd_t *pgdp, pgd_t pgd)
+{
+ return pgd;
+}
+static inline pgd_t *native_get_shadow_pgd(pgd_t *pgdp)
+{
+ BUILD_BUG_ON(1);
+ return NULL;
+}
+#endif /* CONFIG_PAGE_TABLE_ISOLATION */
+
static inline void native_set_pgd(pgd_t *pgdp, pgd_t pgd)
{
- *pgdp = pgd;
+ *pgdp = kaiser_set_shadow_pgd(pgdp, pgd);
}
static inline void native_pgd_clear(pgd_t *pgd)
diff --git a/arch/x86/include/asm/pgtable_types.h b/arch/x86/include/asm/pgtable_types.h
index 8b4de22..f1c8ac4 100644
--- a/arch/x86/include/asm/pgtable_types.h
+++ b/arch/x86/include/asm/pgtable_types.h
@@ -119,7 +119,7 @@
#define _PAGE_DEVMAP (_AT(pteval_t, 0))
#endif
-#define _PAGE_PROTNONE (_AT(pteval_t, 1) << _PAGE_BIT_PROTNONE)
+#define _PAGE_PROTNONE (_AT(pteval_t, 1) << _PAGE_BIT_PROTNONE)
#define _PAGE_TABLE (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | \
_PAGE_ACCESSED | _PAGE_DIRTY)
@@ -137,6 +137,33 @@
_PAGE_SOFT_DIRTY)
#define _HPAGE_CHG_MASK (_PAGE_CHG_MASK | _PAGE_PSE)
+/* The ASID is the lower 12 bits of CR3 */
+#define X86_CR3_PCID_ASID_MASK (_AC((1<<12)-1,UL))
+
+/* Mask for all the PCID-related bits in CR3: */
+#define X86_CR3_PCID_MASK (X86_CR3_PCID_NOFLUSH | X86_CR3_PCID_ASID_MASK)
+#define X86_CR3_PCID_ASID_KERN (_AC(0x0,UL))
+
+#if defined(CONFIG_PAGE_TABLE_ISOLATION) && defined(CONFIG_X86_64)
+/* Let X86_CR3_PCID_ASID_USER be usable for the X86_CR3_PCID_NOFLUSH bit */
+#define X86_CR3_PCID_ASID_USER (_AC(0x80,UL))
+
+#define X86_CR3_PCID_KERN_FLUSH (X86_CR3_PCID_ASID_KERN)
+#define X86_CR3_PCID_USER_FLUSH (X86_CR3_PCID_ASID_USER)
+#define X86_CR3_PCID_KERN_NOFLUSH (X86_CR3_PCID_NOFLUSH | X86_CR3_PCID_ASID_KERN)
+#define X86_CR3_PCID_USER_NOFLUSH (X86_CR3_PCID_NOFLUSH | X86_CR3_PCID_ASID_USER)
+#else
+#define X86_CR3_PCID_ASID_USER (_AC(0x0,UL))
+/*
+ * PCIDs are unsupported on 32-bit and none of these bits can be
+ * set in CR3:
+ */
+#define X86_CR3_PCID_KERN_FLUSH (0)
+#define X86_CR3_PCID_USER_FLUSH (0)
+#define X86_CR3_PCID_KERN_NOFLUSH (0)
+#define X86_CR3_PCID_USER_NOFLUSH (0)
+#endif
+
/*
* The cache modes defined here are used to translate between pure SW usage
* and the HW defined cache mode bits and/or PAT entries.
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index 83db0ea..353f038 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -156,8 +156,8 @@
extern struct cpuinfo_x86 new_cpu_data;
extern struct tss_struct doublefault_tss;
-extern __u32 cpu_caps_cleared[NCAPINTS];
-extern __u32 cpu_caps_set[NCAPINTS];
+extern __u32 cpu_caps_cleared[NCAPINTS + NBUGINTS];
+extern __u32 cpu_caps_set[NCAPINTS + NBUGINTS];
#ifdef CONFIG_SMP
DECLARE_PER_CPU_READ_MOSTLY(struct cpuinfo_x86, cpu_info);
@@ -308,7 +308,7 @@
} ____cacheline_aligned;
-DECLARE_PER_CPU_SHARED_ALIGNED(struct tss_struct, cpu_tss);
+DECLARE_PER_CPU_SHARED_ALIGNED_USER_MAPPED(struct tss_struct, cpu_tss);
#ifdef CONFIG_X86_32
DECLARE_PER_CPU(unsigned long, cpu_current_top_of_stack);
@@ -596,7 +596,7 @@
{
int tmp;
-#ifdef CONFIG_M486
+#ifdef CONFIG_X86_32
/*
* Do a CPUID if available, otherwise do a jump. The jump
* can conveniently enough be the jump around CPUID.
diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h
index ad6f5eb0..bdf9c4c 100644
--- a/arch/x86/include/asm/thread_info.h
+++ b/arch/x86/include/asm/thread_info.h
@@ -152,17 +152,6 @@
*/
#ifndef __ASSEMBLY__
-static inline unsigned long current_stack_pointer(void)
-{
- unsigned long sp;
-#ifdef CONFIG_X86_64
- asm("mov %%rsp,%0" : "=g" (sp));
-#else
- asm("mov %%esp,%0" : "=g" (sp));
-#endif
- return sp;
-}
-
/*
* Walks up the stack frames to make sure that the specified object is
* entirely contained by a single stack frame.
diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h
index fc5abff..94146f6 100644
--- a/arch/x86/include/asm/tlbflush.h
+++ b/arch/x86/include/asm/tlbflush.h
@@ -7,6 +7,7 @@
#include <asm/processor.h>
#include <asm/cpufeature.h>
#include <asm/special_insns.h>
+#include <asm/smp.h>
static inline void __invpcid(unsigned long pcid, unsigned long addr,
unsigned long type)
@@ -65,10 +66,8 @@
#endif
struct tlb_state {
-#ifdef CONFIG_SMP
struct mm_struct *active_mm;
int state;
-#endif
/*
* Access to this CR4 shadow and to H/W CR4 is protected by
@@ -133,6 +132,24 @@
cr4_set_bits(mask);
}
+/*
+ * Declare a couple of kaiser interfaces here for convenience,
+ * to avoid the need for asm/kaiser.h in unexpected places.
+ */
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+extern int kaiser_enabled;
+extern void kaiser_setup_pcid(void);
+extern void kaiser_flush_tlb_on_return_to_user(void);
+#else
+#define kaiser_enabled 0
+static inline void kaiser_setup_pcid(void)
+{
+}
+static inline void kaiser_flush_tlb_on_return_to_user(void)
+{
+}
+#endif
+
static inline void __native_flush_tlb(void)
{
/*
@@ -141,6 +158,8 @@
* back:
*/
preempt_disable();
+ if (kaiser_enabled)
+ kaiser_flush_tlb_on_return_to_user();
native_write_cr3(native_read_cr3());
preempt_enable();
}
@@ -150,20 +169,27 @@
unsigned long cr4;
cr4 = this_cpu_read(cpu_tlbstate.cr4);
- /* clear PGE */
- native_write_cr4(cr4 & ~X86_CR4_PGE);
- /* write old PGE again and flush TLBs */
- native_write_cr4(cr4);
+ if (cr4 & X86_CR4_PGE) {
+ /* clear PGE and flush TLB of all entries */
+ native_write_cr4(cr4 & ~X86_CR4_PGE);
+ /* restore PGE as it was before */
+ native_write_cr4(cr4);
+ } else {
+ /* do it with cr3, letting kaiser flush user PCID */
+ __native_flush_tlb();
+ }
}
static inline void __native_flush_tlb_global(void)
{
unsigned long flags;
- if (static_cpu_has(X86_FEATURE_INVPCID)) {
+ if (this_cpu_has(X86_FEATURE_INVPCID)) {
/*
* Using INVPCID is considerably faster than a pair of writes
* to CR4 sandwiched inside an IRQ flag save/restore.
+ *
+ * Note, this works with CR4.PCIDE=0 or 1.
*/
invpcid_flush_all();
return;
@@ -175,23 +201,52 @@
* be called from deep inside debugging code.)
*/
raw_local_irq_save(flags);
-
__native_flush_tlb_global_irq_disabled();
-
raw_local_irq_restore(flags);
}
static inline void __native_flush_tlb_single(unsigned long addr)
{
- asm volatile("invlpg (%0)" ::"r" (addr) : "memory");
+ /*
+ * SIMICS #GP's if you run INVPCID with type 2/3
+ * and X86_CR4_PCIDE clear. Shame!
+ *
+ * The ASIDs used below are hard-coded. But, we must not
+ * call invpcid(type=1/2) before CR4.PCIDE=1. Just call
+ * invlpg in the case we are called early.
+ */
+
+ if (!this_cpu_has(X86_FEATURE_INVPCID_SINGLE)) {
+ if (kaiser_enabled)
+ kaiser_flush_tlb_on_return_to_user();
+ asm volatile("invlpg (%0)" ::"r" (addr) : "memory");
+ return;
+ }
+ /* Flush the address out of both PCIDs. */
+ /*
+ * An optimization here might be to determine addresses
+ * that are only kernel-mapped and only flush the kernel
+ * ASID. But, userspace flushes are probably much more
+ * important performance-wise.
+ *
+ * Make sure to do only a single invpcid when KAISER is
+ * disabled and we have only a single ASID.
+ */
+ if (kaiser_enabled)
+ invpcid_flush_one(X86_CR3_PCID_ASID_USER, addr);
+ invpcid_flush_one(X86_CR3_PCID_ASID_KERN, addr);
}
static inline void __flush_tlb_all(void)
{
- if (boot_cpu_has(X86_FEATURE_PGE))
- __flush_tlb_global();
- else
- __flush_tlb();
+ __flush_tlb_global();
+ /*
+ * Note: if we somehow had PCID but not PGE, then this wouldn't work --
+ * we'd end up flushing kernel translations for the current ASID but
+ * we might fail to flush kernel translations for other cached ASIDs.
+ *
+ * To avoid this issue, we force PCID off if PGE is off.
+ */
}
static inline void __flush_tlb_one(unsigned long addr)
@@ -205,7 +260,6 @@
/*
* TLB flushing:
*
- * - flush_tlb() flushes the current mm struct TLBs
* - flush_tlb_all() flushes all processes TLBs
* - flush_tlb_mm(mm) flushes the specified mm context TLB's
* - flush_tlb_page(vma, vmaddr) flushes one page
@@ -217,84 +271,6 @@
* and page-granular flushes are available only on i486 and up.
*/
-#ifndef CONFIG_SMP
-
-/* "_up" is for UniProcessor.
- *
- * This is a helper for other header functions. *Not* intended to be called
- * directly. All global TLB flushes need to either call this, or to bump the
- * vm statistics themselves.
- */
-static inline void __flush_tlb_up(void)
-{
- count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ALL);
- __flush_tlb();
-}
-
-static inline void flush_tlb_all(void)
-{
- count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ALL);
- __flush_tlb_all();
-}
-
-static inline void flush_tlb(void)
-{
- __flush_tlb_up();
-}
-
-static inline void local_flush_tlb(void)
-{
- __flush_tlb_up();
-}
-
-static inline void flush_tlb_mm(struct mm_struct *mm)
-{
- if (mm == current->active_mm)
- __flush_tlb_up();
-}
-
-static inline void flush_tlb_page(struct vm_area_struct *vma,
- unsigned long addr)
-{
- if (vma->vm_mm == current->active_mm)
- __flush_tlb_one(addr);
-}
-
-static inline void flush_tlb_range(struct vm_area_struct *vma,
- unsigned long start, unsigned long end)
-{
- if (vma->vm_mm == current->active_mm)
- __flush_tlb_up();
-}
-
-static inline void flush_tlb_mm_range(struct mm_struct *mm,
- unsigned long start, unsigned long end, unsigned long vmflag)
-{
- if (mm == current->active_mm)
- __flush_tlb_up();
-}
-
-static inline void native_flush_tlb_others(const struct cpumask *cpumask,
- struct mm_struct *mm,
- unsigned long start,
- unsigned long end)
-{
-}
-
-static inline void reset_lazy_tlbstate(void)
-{
-}
-
-static inline void flush_tlb_kernel_range(unsigned long start,
- unsigned long end)
-{
- flush_tlb_all();
-}
-
-#else /* SMP */
-
-#include <asm/smp.h>
-
#define local_flush_tlb() __flush_tlb()
#define flush_tlb_mm(mm) flush_tlb_mm_range(mm, 0UL, TLB_FLUSH_ALL, 0UL)
@@ -303,13 +279,14 @@
flush_tlb_mm_range(vma->vm_mm, start, end, vma->vm_flags)
extern void flush_tlb_all(void);
-extern void flush_tlb_current_task(void);
-extern void flush_tlb_page(struct vm_area_struct *, unsigned long);
extern void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start,
unsigned long end, unsigned long vmflag);
extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
-#define flush_tlb() flush_tlb_current_task()
+static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long a)
+{
+ flush_tlb_mm_range(vma->vm_mm, a, a + PAGE_SIZE, VM_NONE);
+}
void native_flush_tlb_others(const struct cpumask *cpumask,
struct mm_struct *mm,
@@ -324,8 +301,6 @@
this_cpu_write(cpu_tlbstate.active_mm, &init_mm);
}
-#endif /* SMP */
-
#ifndef CONFIG_PARAVIRT
#define flush_tlb_others(mask, mm, start, end) \
native_flush_tlb_others(mask, mm, start, end)
diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h
index 01fd0a7..688315b 100644
--- a/arch/x86/include/asm/traps.h
+++ b/arch/x86/include/asm/traps.h
@@ -92,6 +92,7 @@
#ifdef CONFIG_X86_32
dotraplinkage void do_iret_error(struct pt_regs *, long);
#endif
+dotraplinkage void do_mce(struct pt_regs *, long);
static inline int get_si_code(unsigned long condition)
{
diff --git a/arch/x86/include/asm/vsyscall.h b/arch/x86/include/asm/vsyscall.h
index 6ba66ee..9ee8506 100644
--- a/arch/x86/include/asm/vsyscall.h
+++ b/arch/x86/include/asm/vsyscall.h
@@ -12,12 +12,15 @@
* Returns true if handled.
*/
extern bool emulate_vsyscall(struct pt_regs *regs, unsigned long address);
+extern bool vsyscall_enabled(void);
+extern unsigned long vsyscall_pgprot;
#else
static inline void map_vsyscall(void) {}
static inline bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
{
return false;
}
+static inline bool vsyscall_enabled(void) { return false; }
#endif
#endif /* _ASM_X86_VSYSCALL_H */
diff --git a/arch/x86/include/asm/xen/hypercall.h b/arch/x86/include/asm/xen/hypercall.h
index 8b678af..ccdc23d 100644
--- a/arch/x86/include/asm/xen/hypercall.h
+++ b/arch/x86/include/asm/xen/hypercall.h
@@ -44,6 +44,7 @@
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/smap.h>
+#include <asm/nospec-branch.h>
#include <xen/interface/xen.h>
#include <xen/interface/sched.h>
@@ -216,9 +217,9 @@
__HYPERCALL_5ARG(a1, a2, a3, a4, a5);
stac();
- asm volatile("call *%[call]"
+ asm volatile(CALL_NOSPEC
: __HYPERCALL_5PARAM
- : [call] "a" (&hypercall_page[call])
+ : [thunk_target] "a" (&hypercall_page[call])
: __HYPERCALL_CLOBBER5);
clac();
diff --git a/arch/x86/include/uapi/asm/processor-flags.h b/arch/x86/include/uapi/asm/processor-flags.h
index 567de50..6768d13 100644
--- a/arch/x86/include/uapi/asm/processor-flags.h
+++ b/arch/x86/include/uapi/asm/processor-flags.h
@@ -77,7 +77,8 @@
#define X86_CR3_PWT _BITUL(X86_CR3_PWT_BIT)
#define X86_CR3_PCD_BIT 4 /* Page Cache Disable */
#define X86_CR3_PCD _BITUL(X86_CR3_PCD_BIT)
-#define X86_CR3_PCID_MASK _AC(0x00000fff,UL) /* PCID Mask */
+#define X86_CR3_PCID_NOFLUSH_BIT 63 /* Preserve old PCID */
+#define X86_CR3_PCID_NOFLUSH _BITULL(X86_CR3_PCID_NOFLUSH_BIT)
/*
* Intel CPU features in CR4
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index 11cc600..0a1e8a6 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -335,13 +335,12 @@
#ifdef CONFIG_X86_IO_APIC
#define MP_ISA_BUS 0
+static int __init mp_register_ioapic_irq(u8 bus_irq, u8 polarity,
+ u8 trigger, u32 gsi);
+
static void __init mp_override_legacy_irq(u8 bus_irq, u8 polarity, u8 trigger,
u32 gsi)
{
- int ioapic;
- int pin;
- struct mpc_intsrc mp_irq;
-
/*
* Check bus_irq boundary.
*/
@@ -351,14 +350,6 @@
}
/*
- * Convert 'gsi' to 'ioapic.pin'.
- */
- ioapic = mp_find_ioapic(gsi);
- if (ioapic < 0)
- return;
- pin = mp_find_ioapic_pin(ioapic, gsi);
-
- /*
* TBD: This check is for faulty timer entries, where the override
* erroneously sets the trigger to level, resulting in a HUGE
* increase of timer interrupts!
@@ -366,16 +357,8 @@
if ((bus_irq == 0) && (trigger == 3))
trigger = 1;
- mp_irq.type = MP_INTSRC;
- mp_irq.irqtype = mp_INT;
- mp_irq.irqflag = (trigger << 2) | polarity;
- mp_irq.srcbus = MP_ISA_BUS;
- mp_irq.srcbusirq = bus_irq; /* IRQ */
- mp_irq.dstapic = mpc_ioapic_id(ioapic); /* APIC ID */
- mp_irq.dstirq = pin; /* INTIN# */
-
- mp_save_irq(&mp_irq);
-
+ if (mp_register_ioapic_irq(bus_irq, polarity, trigger, gsi) < 0)
+ return;
/*
* Reset default identity mapping if gsi is also an legacy IRQ,
* otherwise there will be more than one entry with the same GSI
@@ -422,6 +405,34 @@
return 0;
}
+static int __init mp_register_ioapic_irq(u8 bus_irq, u8 polarity,
+ u8 trigger, u32 gsi)
+{
+ struct mpc_intsrc mp_irq;
+ int ioapic, pin;
+
+ /* Convert 'gsi' to 'ioapic.pin'(INTIN#) */
+ ioapic = mp_find_ioapic(gsi);
+ if (ioapic < 0) {
+ pr_warn("Failed to find ioapic for gsi : %u\n", gsi);
+ return ioapic;
+ }
+
+ pin = mp_find_ioapic_pin(ioapic, gsi);
+
+ mp_irq.type = MP_INTSRC;
+ mp_irq.irqtype = mp_INT;
+ mp_irq.irqflag = (trigger << 2) | polarity;
+ mp_irq.srcbus = MP_ISA_BUS;
+ mp_irq.srcbusirq = bus_irq;
+ mp_irq.dstapic = mpc_ioapic_id(ioapic);
+ mp_irq.dstirq = pin;
+
+ mp_save_irq(&mp_irq);
+
+ return 0;
+}
+
static int __init
acpi_parse_ioapic(struct acpi_subtable_header * header, const unsigned long end)
{
@@ -466,7 +477,11 @@
if (acpi_sci_flags & ACPI_MADT_POLARITY_MASK)
polarity = acpi_sci_flags & ACPI_MADT_POLARITY_MASK;
- mp_override_legacy_irq(bus_irq, polarity, trigger, gsi);
+ if (bus_irq < NR_IRQS_LEGACY)
+ mp_override_legacy_irq(bus_irq, polarity, trigger, gsi);
+ else
+ mp_register_ioapic_irq(bus_irq, polarity, trigger, gsi);
+
acpi_penalize_sci_irq(bus_irq, trigger, polarity);
/*
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index 5cb272a..10d5a3d 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -340,9 +340,12 @@
static void __init_or_module optimize_nops(struct alt_instr *a, u8 *instr)
{
unsigned long flags;
+ int i;
- if (instr[0] != 0x90)
- return;
+ for (i = 0; i < a->padlen; i++) {
+ if (instr[i] != 0x90)
+ return;
+ }
local_irq_save(flags);
add_nops(instr + (a->instrlen - a->padlen), a->padlen);
diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c
index f3557a1..b5229ab 100644
--- a/arch/x86/kernel/apic/vector.c
+++ b/arch/x86/kernel/apic/vector.c
@@ -361,14 +361,17 @@
irq_data->chip_data = data;
irq_data->hwirq = virq + i;
err = assign_irq_vector_policy(virq + i, node, data, info);
- if (err)
+ if (err) {
+ irq_data->chip_data = NULL;
+ free_apic_chip_data(data);
goto error;
+ }
}
return 0;
error:
- x86_vector_free_irqs(domain, virq, i + 1);
+ x86_vector_free_irqs(domain, virq, i);
return err;
}
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile
index 4a8697f..33b6367 100644
--- a/arch/x86/kernel/cpu/Makefile
+++ b/arch/x86/kernel/cpu/Makefile
@@ -20,13 +20,11 @@
obj-y += common.o
obj-y += rdrand.o
obj-y += match.o
+obj-y += bugs.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_X86_FEATURE_NAMES) += capflags.o powerflags.o
-obj-$(CONFIG_X86_32) += bugs.o
-obj-$(CONFIG_X86_64) += bugs_64.o
-
obj-$(CONFIG_CPU_SUP_INTEL) += intel.o
obj-$(CONFIG_CPU_SUP_AMD) += amd.o
obj-$(CONFIG_CPU_SUP_CYRIX_32) += cyrix.o
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index 2b4cf04..1b89f0c 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -782,8 +782,32 @@
set_cpu_cap(c, X86_FEATURE_K8);
if (cpu_has(c, X86_FEATURE_XMM2)) {
- /* MFENCE stops RDTSC speculation */
- set_cpu_cap(c, X86_FEATURE_MFENCE_RDTSC);
+ unsigned long long val;
+ int ret;
+
+ /*
+ * A serializing LFENCE has less overhead than MFENCE, so
+ * use it for execution serialization. On families which
+ * don't have that MSR, LFENCE is already serializing.
+ * msr_set_bit() uses the safe accessors, too, even if the MSR
+ * is not present.
+ */
+ msr_set_bit(MSR_F10H_DECFG,
+ MSR_F10H_DECFG_LFENCE_SERIALIZE_BIT);
+
+ /*
+ * Verify that the MSR write was successful (could be running
+ * under a hypervisor) and only then assume that LFENCE is
+ * serializing.
+ */
+ ret = rdmsrl_safe(MSR_F10H_DECFG, &val);
+ if (!ret && (val & MSR_F10H_DECFG_LFENCE_SERIALIZE)) {
+ /* A serializing LFENCE stops RDTSC speculation */
+ set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC);
+ } else {
+ /* MFENCE stops RDTSC speculation */
+ set_cpu_cap(c, X86_FEATURE_MFENCE_RDTSC);
+ }
}
/*
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
index bd17db1..8cacf62 100644
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -9,6 +9,10 @@
*/
#include <linux/init.h>
#include <linux/utsname.h>
+#include <linux/cpu.h>
+
+#include <asm/nospec-branch.h>
+#include <asm/cmdline.h>
#include <asm/bugs.h>
#include <asm/processor.h>
#include <asm/processor-flags.h>
@@ -16,15 +20,25 @@
#include <asm/msr.h>
#include <asm/paravirt.h>
#include <asm/alternative.h>
+#include <asm/pgtable.h>
+#include <asm/cacheflush.h>
+#include <asm/intel-family.h>
+
+static void __init spectre_v2_select_mitigation(void);
void __init check_bugs(void)
{
identify_boot_cpu();
-#ifndef CONFIG_SMP
- pr_info("CPU: ");
- print_cpu_info(&boot_cpu_data);
-#endif
+ if (!IS_ENABLED(CONFIG_SMP)) {
+ pr_info("CPU: ");
+ print_cpu_info(&boot_cpu_data);
+ }
+
+ /* Select the proper spectre mitigation before patching alternatives */
+ spectre_v2_select_mitigation();
+
+#ifdef CONFIG_X86_32
/*
* Check whether we are able to run this kernel safely on SMP.
*
@@ -40,4 +54,229 @@
alternative_instructions();
fpu__init_check_bugs();
+#else /* CONFIG_X86_64 */
+ alternative_instructions();
+
+ /*
+ * Make sure the first 2MB area is not mapped by huge pages
+ * There are typically fixed size MTRRs in there and overlapping
+ * MTRRs into large pages causes slow downs.
+ *
+ * Right now we don't do that with gbpages because there seems
+ * very little benefit for that case.
+ */
+ if (!direct_gbpages)
+ set_memory_4k((unsigned long)__va(0), 1);
+#endif
}
+
+/* The kernel command line selection */
+enum spectre_v2_mitigation_cmd {
+ SPECTRE_V2_CMD_NONE,
+ SPECTRE_V2_CMD_AUTO,
+ SPECTRE_V2_CMD_FORCE,
+ SPECTRE_V2_CMD_RETPOLINE,
+ SPECTRE_V2_CMD_RETPOLINE_GENERIC,
+ SPECTRE_V2_CMD_RETPOLINE_AMD,
+};
+
+static const char *spectre_v2_strings[] = {
+ [SPECTRE_V2_NONE] = "Vulnerable",
+ [SPECTRE_V2_RETPOLINE_MINIMAL] = "Vulnerable: Minimal generic ASM retpoline",
+ [SPECTRE_V2_RETPOLINE_MINIMAL_AMD] = "Vulnerable: Minimal AMD ASM retpoline",
+ [SPECTRE_V2_RETPOLINE_GENERIC] = "Mitigation: Full generic retpoline",
+ [SPECTRE_V2_RETPOLINE_AMD] = "Mitigation: Full AMD retpoline",
+};
+
+#undef pr_fmt
+#define pr_fmt(fmt) "Spectre V2 mitigation: " fmt
+
+static enum spectre_v2_mitigation spectre_v2_enabled = SPECTRE_V2_NONE;
+
+static void __init spec2_print_if_insecure(const char *reason)
+{
+ if (boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
+ pr_info("%s\n", reason);
+}
+
+static void __init spec2_print_if_secure(const char *reason)
+{
+ if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
+ pr_info("%s\n", reason);
+}
+
+static inline bool retp_compiler(void)
+{
+ return __is_defined(RETPOLINE);
+}
+
+static inline bool match_option(const char *arg, int arglen, const char *opt)
+{
+ int len = strlen(opt);
+
+ return len == arglen && !strncmp(arg, opt, len);
+}
+
+static enum spectre_v2_mitigation_cmd __init spectre_v2_parse_cmdline(void)
+{
+ char arg[20];
+ int ret;
+
+ ret = cmdline_find_option(boot_command_line, "spectre_v2", arg,
+ sizeof(arg));
+ if (ret > 0) {
+ if (match_option(arg, ret, "off")) {
+ goto disable;
+ } else if (match_option(arg, ret, "on")) {
+ spec2_print_if_secure("force enabled on command line.");
+ return SPECTRE_V2_CMD_FORCE;
+ } else if (match_option(arg, ret, "retpoline")) {
+ spec2_print_if_insecure("retpoline selected on command line.");
+ return SPECTRE_V2_CMD_RETPOLINE;
+ } else if (match_option(arg, ret, "retpoline,amd")) {
+ if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) {
+ pr_err("retpoline,amd selected but CPU is not AMD. Switching to AUTO select\n");
+ return SPECTRE_V2_CMD_AUTO;
+ }
+ spec2_print_if_insecure("AMD retpoline selected on command line.");
+ return SPECTRE_V2_CMD_RETPOLINE_AMD;
+ } else if (match_option(arg, ret, "retpoline,generic")) {
+ spec2_print_if_insecure("generic retpoline selected on command line.");
+ return SPECTRE_V2_CMD_RETPOLINE_GENERIC;
+ } else if (match_option(arg, ret, "auto")) {
+ return SPECTRE_V2_CMD_AUTO;
+ }
+ }
+
+ if (!cmdline_find_option_bool(boot_command_line, "nospectre_v2"))
+ return SPECTRE_V2_CMD_AUTO;
+disable:
+ spec2_print_if_insecure("disabled on command line.");
+ return SPECTRE_V2_CMD_NONE;
+}
+
+/* Check for Skylake-like CPUs (for RSB handling) */
+static bool __init is_skylake_era(void)
+{
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL &&
+ boot_cpu_data.x86 == 6) {
+ switch (boot_cpu_data.x86_model) {
+ case INTEL_FAM6_SKYLAKE_MOBILE:
+ case INTEL_FAM6_SKYLAKE_DESKTOP:
+ case INTEL_FAM6_SKYLAKE_X:
+ case INTEL_FAM6_KABYLAKE_MOBILE:
+ case INTEL_FAM6_KABYLAKE_DESKTOP:
+ return true;
+ }
+ }
+ return false;
+}
+
+static void __init spectre_v2_select_mitigation(void)
+{
+ enum spectre_v2_mitigation_cmd cmd = spectre_v2_parse_cmdline();
+ enum spectre_v2_mitigation mode = SPECTRE_V2_NONE;
+
+ /*
+ * If the CPU is not affected and the command line mode is NONE or AUTO
+ * then nothing to do.
+ */
+ if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2) &&
+ (cmd == SPECTRE_V2_CMD_NONE || cmd == SPECTRE_V2_CMD_AUTO))
+ return;
+
+ switch (cmd) {
+ case SPECTRE_V2_CMD_NONE:
+ return;
+
+ case SPECTRE_V2_CMD_FORCE:
+ /* FALLTRHU */
+ case SPECTRE_V2_CMD_AUTO:
+ goto retpoline_auto;
+
+ case SPECTRE_V2_CMD_RETPOLINE_AMD:
+ if (IS_ENABLED(CONFIG_RETPOLINE))
+ goto retpoline_amd;
+ break;
+ case SPECTRE_V2_CMD_RETPOLINE_GENERIC:
+ if (IS_ENABLED(CONFIG_RETPOLINE))
+ goto retpoline_generic;
+ break;
+ case SPECTRE_V2_CMD_RETPOLINE:
+ if (IS_ENABLED(CONFIG_RETPOLINE))
+ goto retpoline_auto;
+ break;
+ }
+ pr_err("kernel not compiled with retpoline; no mitigation available!");
+ return;
+
+retpoline_auto:
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) {
+ retpoline_amd:
+ if (!boot_cpu_has(X86_FEATURE_LFENCE_RDTSC)) {
+ pr_err("LFENCE not serializing. Switching to generic retpoline\n");
+ goto retpoline_generic;
+ }
+ mode = retp_compiler() ? SPECTRE_V2_RETPOLINE_AMD :
+ SPECTRE_V2_RETPOLINE_MINIMAL_AMD;
+ setup_force_cpu_cap(X86_FEATURE_RETPOLINE_AMD);
+ setup_force_cpu_cap(X86_FEATURE_RETPOLINE);
+ } else {
+ retpoline_generic:
+ mode = retp_compiler() ? SPECTRE_V2_RETPOLINE_GENERIC :
+ SPECTRE_V2_RETPOLINE_MINIMAL;
+ setup_force_cpu_cap(X86_FEATURE_RETPOLINE);
+ }
+
+ spectre_v2_enabled = mode;
+ pr_info("%s\n", spectre_v2_strings[mode]);
+
+ /*
+ * If neither SMEP or KPTI are available, there is a risk of
+ * hitting userspace addresses in the RSB after a context switch
+ * from a shallow call stack to a deeper one. To prevent this fill
+ * the entire RSB, even when using IBRS.
+ *
+ * Skylake era CPUs have a separate issue with *underflow* of the
+ * RSB, when they will predict 'ret' targets from the generic BTB.
+ * The proper mitigation for this is IBRS. If IBRS is not supported
+ * or deactivated in favour of retpolines the RSB fill on context
+ * switch is required.
+ */
+ if ((!boot_cpu_has(X86_FEATURE_KAISER) &&
+ !boot_cpu_has(X86_FEATURE_SMEP)) || is_skylake_era()) {
+ setup_force_cpu_cap(X86_FEATURE_RSB_CTXSW);
+ pr_info("Filling RSB on context switch\n");
+ }
+}
+
+#undef pr_fmt
+
+#ifdef CONFIG_SYSFS
+ssize_t cpu_show_meltdown(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (!boot_cpu_has_bug(X86_BUG_CPU_MELTDOWN))
+ return sprintf(buf, "Not affected\n");
+ if (boot_cpu_has(X86_FEATURE_KAISER))
+ return sprintf(buf, "Mitigation: PTI\n");
+ return sprintf(buf, "Vulnerable\n");
+}
+
+ssize_t cpu_show_spectre_v1(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V1))
+ return sprintf(buf, "Not affected\n");
+ return sprintf(buf, "Vulnerable\n");
+}
+
+ssize_t cpu_show_spectre_v2(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
+ return sprintf(buf, "Not affected\n");
+
+ return sprintf(buf, "%s\n", spectre_v2_strings[spectre_v2_enabled]);
+}
+#endif
diff --git a/arch/x86/kernel/cpu/bugs_64.c b/arch/x86/kernel/cpu/bugs_64.c
deleted file mode 100644
index a972ac4..0000000
--- a/arch/x86/kernel/cpu/bugs_64.c
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 1994 Linus Torvalds
- * Copyright (C) 2000 SuSE
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <asm/alternative.h>
-#include <asm/bugs.h>
-#include <asm/processor.h>
-#include <asm/mtrr.h>
-#include <asm/cacheflush.h>
-
-void __init check_bugs(void)
-{
- identify_boot_cpu();
-#if !defined(CONFIG_SMP)
- pr_info("CPU: ");
- print_cpu_info(&boot_cpu_data);
-#endif
- alternative_instructions();
-
- /*
- * Make sure the first 2MB area is not mapped by huge pages
- * There are typically fixed size MTRRs in there and overlapping
- * MTRRs into large pages causes slow downs.
- *
- * Right now we don't do that with gbpages because there seems
- * very little benefit for that case.
- */
- if (!direct_gbpages)
- set_memory_4k((unsigned long)__va(0), 1);
-}
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 4eece91..d198ae0 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -93,7 +93,7 @@
static const struct cpu_dev *this_cpu = &default_cpu;
-DEFINE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page) = { .gdt = {
+DEFINE_PER_CPU_PAGE_ALIGNED_USER_MAPPED(struct gdt_page, gdt_page) = { .gdt = {
#ifdef CONFIG_X86_64
/*
* We need valid kernel segments for data and code in long mode too
@@ -163,6 +163,24 @@
}
__setup("nompx", x86_mpx_setup);
+#ifdef CONFIG_X86_64
+static int __init x86_pcid_setup(char *s)
+{
+ /* require an exact match without trailing characters */
+ if (strlen(s))
+ return 0;
+
+ /* do not emit a message if the feature is not present */
+ if (!boot_cpu_has(X86_FEATURE_PCID))
+ return 1;
+
+ setup_clear_cpu_cap(X86_FEATURE_PCID);
+ pr_info("nopcid: PCID feature disabled\n");
+ return 1;
+}
+__setup("nopcid", x86_pcid_setup);
+#endif
+
static int __init x86_noinvpcid_setup(char *s)
{
/* noinvpcid doesn't accept parameters */
@@ -306,6 +324,39 @@
}
}
+static void setup_pcid(struct cpuinfo_x86 *c)
+{
+ if (cpu_has(c, X86_FEATURE_PCID)) {
+ if (cpu_has(c, X86_FEATURE_PGE) || kaiser_enabled) {
+ cr4_set_bits(X86_CR4_PCIDE);
+ /*
+ * INVPCID has two "groups" of types:
+ * 1/2: Invalidate an individual address
+ * 3/4: Invalidate all contexts
+ *
+ * 1/2 take a PCID, but 3/4 do not. So, 3/4
+ * ignore the PCID argument in the descriptor.
+ * But, we have to be careful not to call 1/2
+ * with an actual non-zero PCID in them before
+ * we do the above cr4_set_bits().
+ */
+ if (cpu_has(c, X86_FEATURE_INVPCID))
+ set_cpu_cap(c, X86_FEATURE_INVPCID_SINGLE);
+ } else {
+ /*
+ * flush_tlb_all(), as currently implemented, won't
+ * work if PCID is on but PGE is not. Since that
+ * combination doesn't exist on real hardware, there's
+ * no reason to try to fully support it, but it's
+ * polite to avoid corrupting data if we're on
+ * an improperly configured VM.
+ */
+ clear_cpu_cap(c, X86_FEATURE_PCID);
+ }
+ }
+ kaiser_setup_pcid();
+}
+
/*
* Protection Keys are not available in 32-bit mode.
*/
@@ -429,8 +480,8 @@
return NULL; /* Not found */
}
-__u32 cpu_caps_cleared[NCAPINTS];
-__u32 cpu_caps_set[NCAPINTS];
+__u32 cpu_caps_cleared[NCAPINTS + NBUGINTS];
+__u32 cpu_caps_set[NCAPINTS + NBUGINTS];
void load_percpu_segment(int cpu)
{
@@ -655,6 +706,16 @@
}
}
+static void apply_forced_caps(struct cpuinfo_x86 *c)
+{
+ int i;
+
+ for (i = 0; i < NCAPINTS + NBUGINTS; i++) {
+ c->x86_capability[i] &= ~cpu_caps_cleared[i];
+ c->x86_capability[i] |= cpu_caps_set[i];
+ }
+}
+
void get_cpu_cap(struct cpuinfo_x86 *c)
{
u32 eax, ebx, ecx, edx;
@@ -821,7 +882,22 @@
}
setup_force_cpu_cap(X86_FEATURE_ALWAYS);
+
+ if (c->x86_vendor != X86_VENDOR_AMD)
+ setup_force_cpu_bug(X86_BUG_CPU_MELTDOWN);
+
+ setup_force_cpu_bug(X86_BUG_SPECTRE_V1);
+ setup_force_cpu_bug(X86_BUG_SPECTRE_V2);
+
fpu__init_system(c);
+
+#ifdef CONFIG_X86_32
+ /*
+ * Regardless of whether PCID is enumerated, the SDM says
+ * that it can't be enabled in 32-bit mode.
+ */
+ setup_clear_cpu_cap(X86_FEATURE_PCID);
+#endif
}
void __init early_cpu_init(void)
@@ -1035,10 +1111,7 @@
this_cpu->c_identify(c);
/* Clear/Set all flags overridden by options, after probe */
- for (i = 0; i < NCAPINTS; i++) {
- c->x86_capability[i] &= ~cpu_caps_cleared[i];
- c->x86_capability[i] |= cpu_caps_set[i];
- }
+ apply_forced_caps(c);
#ifdef CONFIG_X86_64
c->apicid = apic->phys_pkg_id(c->initial_apicid, 0);
@@ -1064,6 +1137,9 @@
setup_smep(c);
setup_smap(c);
+ /* Set up PCID */
+ setup_pcid(c);
+
/*
* The vendor-specific functions might have changed features.
* Now we do "generic changes."
@@ -1097,10 +1173,7 @@
* Clear/Set all flags overridden by options, need do it
* before following smp all cpus cap AND.
*/
- for (i = 0; i < NCAPINTS; i++) {
- c->x86_capability[i] &= ~cpu_caps_cleared[i];
- c->x86_capability[i] |= cpu_caps_set[i];
- }
+ apply_forced_caps(c);
/*
* On SMP, boot_cpu_data holds the common feature set between
@@ -1325,7 +1398,7 @@
[DEBUG_STACK - 1] = DEBUG_STKSZ
};
-static DEFINE_PER_CPU_PAGE_ALIGNED(char, exception_stacks
+DEFINE_PER_CPU_PAGE_ALIGNED_USER_MAPPED(char, exception_stacks
[(N_EXCEPTION_STACKS - 1) * EXCEPTION_STKSZ + DEBUG_STKSZ]);
/* May not be marked __init: used by software suspend */
@@ -1483,6 +1556,14 @@
* try to read it.
*/
cr4_init_shadow();
+ if (!kaiser_enabled) {
+ /*
+ * secondary_startup_64() deferred setting PGE in cr4:
+ * probe_page_size_mask() sets it on the boot cpu,
+ * but it needs to be set on each secondary cpu.
+ */
+ cr4_set_bits(X86_CR4_PGE);
+ }
/*
* Load microcode on this cpu if a valid microcode is available.
diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c
index de6626c..be63371 100644
--- a/arch/x86/kernel/cpu/intel_cacheinfo.c
+++ b/arch/x86/kernel/cpu/intel_cacheinfo.c
@@ -934,6 +934,8 @@
ci_leaf_init(this_leaf++, &id4_regs);
__cache_cpumap_setup(cpu, idx, &id4_regs);
}
+ this_cpu_ci->cpu_map_populated = true;
+
return 0;
}
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c
index 8ca5f8a..fe5cd6e 100644
--- a/arch/x86/kernel/cpu/mcheck/mce.c
+++ b/arch/x86/kernel/cpu/mcheck/mce.c
@@ -1754,6 +1754,11 @@
void (*machine_check_vector)(struct pt_regs *, long error_code) =
unexpected_machine_check;
+dotraplinkage void do_mce(struct pt_regs *regs, long error_code)
+{
+ machine_check_vector(regs, error_code);
+}
+
/*
* Called for each booted CPU to set up machine checks.
* Must be called with preempt off:
diff --git a/arch/x86/kernel/cpu/microcode/amd.c b/arch/x86/kernel/cpu/microcode/amd.c
index 017bda1..b74bb29 100644
--- a/arch/x86/kernel/cpu/microcode/amd.c
+++ b/arch/x86/kernel/cpu/microcode/amd.c
@@ -592,6 +592,7 @@
#define F14H_MPB_MAX_SIZE 1824
#define F15H_MPB_MAX_SIZE 4096
#define F16H_MPB_MAX_SIZE 3458
+#define F17H_MPB_MAX_SIZE 3200
switch (family) {
case 0x14:
@@ -603,6 +604,9 @@
case 0x16:
max_size = F16H_MPB_MAX_SIZE;
break;
+ case 0x17:
+ max_size = F17H_MPB_MAX_SIZE;
+ break;
default:
max_size = F1XH_MPB_MAX_SIZE;
break;
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
index 13dbcc0..f90f176 100644
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -40,6 +40,9 @@
#include <asm/setup.h>
#include <asm/msr.h>
+/* last level cache size per core */
+static int llc_size_per_core;
+
/*
* Temporary microcode blobs pointers storage. We note here during early load
* the pointers to microcode blobs we've got from whatever storage (detached
@@ -1051,8 +1054,19 @@
{
struct cpuinfo_x86 *c = &cpu_data(cpu);
- if (c->x86 == 6 && c->x86_model == INTEL_FAM6_BROADWELL_X) {
- pr_err_once("late loading on model 79 is disabled.\n");
+ /*
+ * Late loading on model 79 with microcode revision less than 0x0b000021
+ * and LLC size per core bigger than 2.5MB may result in a system hang.
+ * This behavior is documented in item BDF90, #334165 (Intel Xeon
+ * Processor E7-8800/4800 v4 Product Family).
+ */
+ if (c->x86 == 6 &&
+ c->x86_model == INTEL_FAM6_BROADWELL_X &&
+ c->x86_mask == 0x01 &&
+ llc_size_per_core > 2621440 &&
+ c->microcode < 0x0b000021) {
+ pr_err_once("Erratum BDF90: late loading with revision < 0x0b000021 (0x%x) disabled.\n", c->microcode);
+ pr_err_once("Please consider either early loading through initrd/built-in or a potential BIOS update.\n");
return true;
}
@@ -1116,6 +1130,15 @@
.microcode_fini_cpu = microcode_fini_cpu,
};
+static int __init calc_llc_size_per_core(struct cpuinfo_x86 *c)
+{
+ u64 llc_size = c->x86_cache_size * 1024;
+
+ do_div(llc_size, c->x86_max_cores);
+
+ return (int)llc_size;
+}
+
struct microcode_ops * __init init_intel_microcode(void)
{
struct cpuinfo_x86 *c = &boot_cpu_data;
@@ -1126,6 +1149,8 @@
return NULL;
}
+ llc_size_per_core = calc_llc_size_per_core(c);
+
return µcode_intel_ops;
}
diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c
index 1db8dc4..b0dd9ae 100644
--- a/arch/x86/kernel/cpu/scattered.c
+++ b/arch/x86/kernel/cpu/scattered.c
@@ -31,7 +31,6 @@
const struct cpuid_bit *cb;
static const struct cpuid_bit cpuid_bits[] = {
- { X86_FEATURE_INTEL_PT, CR_EBX,25, 0x00000007, 0 },
{ X86_FEATURE_AVX512_4VNNIW, CR_EDX, 2, 0x00000007, 0 },
{ X86_FEATURE_AVX512_4FMAPS, CR_EDX, 3, 0x00000007, 0 },
{ X86_FEATURE_APERFMPERF, CR_ECX, 0, 0x00000006, 0 },
diff --git a/arch/x86/kernel/espfix_64.c b/arch/x86/kernel/espfix_64.c
index 04f89ca..e33b385 100644
--- a/arch/x86/kernel/espfix_64.c
+++ b/arch/x86/kernel/espfix_64.c
@@ -41,6 +41,7 @@
#include <asm/pgalloc.h>
#include <asm/setup.h>
#include <asm/espfix.h>
+#include <asm/kaiser.h>
/*
* Note: we only need 6*8 = 48 bytes for the espfix stack, but round
@@ -126,6 +127,15 @@
/* Install the espfix pud into the kernel page directory */
pgd_p = &init_level4_pgt[pgd_index(ESPFIX_BASE_ADDR)];
pgd_populate(&init_mm, pgd_p, (pud_t *)espfix_pud_page);
+ /*
+ * Just copy the top-level PGD that is mapping the espfix
+ * area to ensure it is mapped into the shadow user page
+ * tables.
+ */
+ if (kaiser_enabled) {
+ set_pgd(native_get_shadow_pgd(pgd_p),
+ __pgd(_KERNPG_TABLE | __pa((pud_t *)espfix_pud_page)));
+ }
/* Randomize the locations */
init_espfix_random();
diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S
index b4421cc..67cd7c1 100644
--- a/arch/x86/kernel/head_64.S
+++ b/arch/x86/kernel/head_64.S
@@ -190,8 +190,8 @@
movq $(init_level4_pgt - __START_KERNEL_map), %rax
1:
- /* Enable PAE mode and PGE */
- movl $(X86_CR4_PAE | X86_CR4_PGE), %ecx
+ /* Enable PAE and PSE, but defer PGE until kaiser_enabled is decided */
+ movl $(X86_CR4_PAE | X86_CR4_PSE), %ecx
movq %rcx, %cr4
/* Setup early boot stage 4 level pagetables. */
@@ -405,6 +405,27 @@
.balign PAGE_SIZE; \
GLOBAL(name)
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+/*
+ * Each PGD needs to be 8k long and 8k aligned. We do not
+ * ever go out to userspace with these, so we do not
+ * strictly *need* the second page, but this allows us to
+ * have a single set_pgd() implementation that does not
+ * need to worry about whether it has 4k or 8k to work
+ * with.
+ *
+ * This ensures PGDs are 8k long:
+ */
+#define KAISER_USER_PGD_FILL 512
+/* This ensures they are 8k-aligned: */
+#define NEXT_PGD_PAGE(name) \
+ .balign 2 * PAGE_SIZE; \
+GLOBAL(name)
+#else
+#define NEXT_PGD_PAGE(name) NEXT_PAGE(name)
+#define KAISER_USER_PGD_FILL 0
+#endif
+
/* Automate the creation of 1 to 1 mapping pmd entries */
#define PMDS(START, PERM, COUNT) \
i = 0 ; \
@@ -414,9 +435,10 @@
.endr
__INITDATA
-NEXT_PAGE(early_level4_pgt)
+NEXT_PGD_PAGE(early_level4_pgt)
.fill 511,8,0
.quad level3_kernel_pgt - __START_KERNEL_map + _PAGE_TABLE
+ .fill KAISER_USER_PGD_FILL,8,0
NEXT_PAGE(early_dynamic_pgts)
.fill 512*EARLY_DYNAMIC_PAGE_TABLES,8,0
@@ -424,16 +446,18 @@
.data
#ifndef CONFIG_XEN
-NEXT_PAGE(init_level4_pgt)
+NEXT_PGD_PAGE(init_level4_pgt)
.fill 512,8,0
+ .fill KAISER_USER_PGD_FILL,8,0
#else
-NEXT_PAGE(init_level4_pgt)
+NEXT_PGD_PAGE(init_level4_pgt)
.quad level3_ident_pgt - __START_KERNEL_map + _KERNPG_TABLE
.org init_level4_pgt + L4_PAGE_OFFSET*8, 0
.quad level3_ident_pgt - __START_KERNEL_map + _KERNPG_TABLE
.org init_level4_pgt + L4_START_KERNEL*8, 0
/* (2^48-(2*1024*1024*1024))/(2^39) = 511 */
.quad level3_kernel_pgt - __START_KERNEL_map + _PAGE_TABLE
+ .fill KAISER_USER_PGD_FILL,8,0
NEXT_PAGE(level3_ident_pgt)
.quad level2_ident_pgt - __START_KERNEL_map + _KERNPG_TABLE
@@ -444,6 +468,7 @@
*/
PMDS(0, __PAGE_KERNEL_IDENT_LARGE_EXEC, PTRS_PER_PMD)
#endif
+ .fill KAISER_USER_PGD_FILL,8,0
NEXT_PAGE(level3_kernel_pgt)
.fill L3_START_KERNEL,8,0
diff --git a/arch/x86/kernel/irq_32.c b/arch/x86/kernel/irq_32.c
index 1f38d9a..2763573 100644
--- a/arch/x86/kernel/irq_32.c
+++ b/arch/x86/kernel/irq_32.c
@@ -19,6 +19,7 @@
#include <linux/mm.h>
#include <asm/apic.h>
+#include <asm/nospec-branch.h>
#ifdef CONFIG_DEBUG_STACKOVERFLOW
@@ -54,17 +55,17 @@
static void call_on_stack(void *func, void *stack)
{
asm volatile("xchgl %%ebx,%%esp \n"
- "call *%%edi \n"
+ CALL_NOSPEC
"movl %%ebx,%%esp \n"
: "=b" (stack)
: "0" (stack),
- "D"(func)
+ [thunk_target] "D"(func)
: "memory", "cc", "edx", "ecx", "eax");
}
static inline void *current_stack(void)
{
- return (void *)(current_stack_pointer() & ~(THREAD_SIZE - 1));
+ return (void *)(current_stack_pointer & ~(THREAD_SIZE - 1));
}
static inline int execute_on_irq_stack(int overflow, struct irq_desc *desc)
@@ -88,17 +89,17 @@
/* Save the next esp at the bottom of the stack */
prev_esp = (u32 *)irqstk;
- *prev_esp = current_stack_pointer();
+ *prev_esp = current_stack_pointer;
if (unlikely(overflow))
call_on_stack(print_stack_overflow, isp);
asm volatile("xchgl %%ebx,%%esp \n"
- "call *%%edi \n"
+ CALL_NOSPEC
"movl %%ebx,%%esp \n"
: "=a" (arg1), "=b" (isp)
: "0" (desc), "1" (isp),
- "D" (desc->handle_irq)
+ [thunk_target] "D" (desc->handle_irq)
: "memory", "cc", "ecx");
return 1;
}
@@ -139,7 +140,7 @@
/* Push the previous esp onto the stack */
prev_esp = (u32 *)irqstk;
- *prev_esp = current_stack_pointer();
+ *prev_esp = current_stack_pointer;
call_on_stack(__do_softirq, isp);
}
diff --git a/arch/x86/kernel/irqinit.c b/arch/x86/kernel/irqinit.c
index 1423ab1..f480b38 100644
--- a/arch/x86/kernel/irqinit.c
+++ b/arch/x86/kernel/irqinit.c
@@ -51,7 +51,7 @@
.flags = IRQF_NO_THREAD,
};
-DEFINE_PER_CPU(vector_irq_t, vector_irq) = {
+DEFINE_PER_CPU_USER_MAPPED(vector_irq_t, vector_irq) = {
[0 ... NR_VECTORS - 1] = VECTOR_UNUSED,
};
diff --git a/arch/x86/kernel/kprobes/opt.c b/arch/x86/kernel/kprobes/opt.c
index 4d74f73..dc20da1c 100644
--- a/arch/x86/kernel/kprobes/opt.c
+++ b/arch/x86/kernel/kprobes/opt.c
@@ -37,6 +37,7 @@
#include <asm/alternative.h>
#include <asm/insn.h>
#include <asm/debugreg.h>
+#include <asm/nospec-branch.h>
#include "common.h"
@@ -192,7 +193,7 @@
}
/* Check whether insn is indirect jump */
-static int insn_is_indirect_jump(struct insn *insn)
+static int __insn_is_indirect_jump(struct insn *insn)
{
return ((insn->opcode.bytes[0] == 0xff &&
(X86_MODRM_REG(insn->modrm.value) & 6) == 4) || /* Jump */
@@ -226,6 +227,26 @@
return (start <= target && target <= start + len);
}
+static int insn_is_indirect_jump(struct insn *insn)
+{
+ int ret = __insn_is_indirect_jump(insn);
+
+#ifdef CONFIG_RETPOLINE
+ /*
+ * Jump to x86_indirect_thunk_* is treated as an indirect jump.
+ * Note that even with CONFIG_RETPOLINE=y, the kernel compiled with
+ * older gcc may use indirect jump. So we add this check instead of
+ * replace indirect-jump check.
+ */
+ if (!ret)
+ ret = insn_jump_into_range(insn,
+ (unsigned long)__indirect_thunk_start,
+ (unsigned long)__indirect_thunk_end -
+ (unsigned long)__indirect_thunk_start);
+#endif
+ return ret;
+}
+
/* Decode whole function to ensure any instructions don't jump into target */
static int can_optimize(unsigned long paddr)
{
diff --git a/arch/x86/kernel/ldt.c b/arch/x86/kernel/ldt.c
index 5f70014..8bc68cf 100644
--- a/arch/x86/kernel/ldt.c
+++ b/arch/x86/kernel/ldt.c
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/uaccess.h>
+#include <linux/kaiser.h>
#include <asm/ldt.h>
#include <asm/desc.h>
@@ -34,11 +35,21 @@
set_ldt(pc->ldt->entries, pc->ldt->size);
}
+static void __free_ldt_struct(struct ldt_struct *ldt)
+{
+ if (ldt->size * LDT_ENTRY_SIZE > PAGE_SIZE)
+ vfree(ldt->entries);
+ else
+ free_page((unsigned long)ldt->entries);
+ kfree(ldt);
+}
+
/* The caller must call finalize_ldt_struct on the result. LDT starts zeroed. */
static struct ldt_struct *alloc_ldt_struct(int size)
{
struct ldt_struct *new_ldt;
int alloc_size;
+ int ret;
if (size > LDT_ENTRIES)
return NULL;
@@ -66,7 +77,13 @@
return NULL;
}
+ ret = kaiser_add_mapping((unsigned long)new_ldt->entries, alloc_size,
+ __PAGE_KERNEL);
new_ldt->size = size;
+ if (ret) {
+ __free_ldt_struct(new_ldt);
+ return NULL;
+ }
return new_ldt;
}
@@ -92,12 +109,10 @@
if (likely(!ldt))
return;
+ kaiser_remove_mapping((unsigned long)ldt->entries,
+ ldt->size * LDT_ENTRY_SIZE);
paravirt_free_ldt(ldt->entries, ldt->size);
- if (ldt->size * LDT_ENTRY_SIZE > PAGE_SIZE)
- vfree(ldt->entries);
- else
- free_page((unsigned long)ldt->entries);
- kfree(ldt);
+ __free_ldt_struct(ldt);
}
/*
diff --git a/arch/x86/kernel/mcount_64.S b/arch/x86/kernel/mcount_64.S
index 7b0d3da..287ec3b 100644
--- a/arch/x86/kernel/mcount_64.S
+++ b/arch/x86/kernel/mcount_64.S
@@ -8,7 +8,7 @@
#include <asm/ptrace.h>
#include <asm/ftrace.h>
#include <asm/export.h>
-
+#include <asm/nospec-branch.h>
.code64
.section .entry.text, "ax"
@@ -290,8 +290,9 @@
* ip and parent ip are used and the list function is called when
* function tracing is enabled.
*/
- call *ftrace_trace_function
+ movq ftrace_trace_function, %r8
+ CALL_NOSPEC %r8
restore_mcount_regs
jmp fgraph_trace
@@ -334,5 +335,5 @@
movq 8(%rsp), %rdx
movq (%rsp), %rax
addq $24, %rsp
- jmp *%rdi
+ JMP_NOSPEC %rdi
#endif
diff --git a/arch/x86/kernel/paravirt_patch_64.c b/arch/x86/kernel/paravirt_patch_64.c
index bb3840c..ee43b36 100644
--- a/arch/x86/kernel/paravirt_patch_64.c
+++ b/arch/x86/kernel/paravirt_patch_64.c
@@ -9,7 +9,6 @@
DEF_NATIVE(pv_mmu_ops, read_cr2, "movq %cr2, %rax");
DEF_NATIVE(pv_mmu_ops, read_cr3, "movq %cr3, %rax");
DEF_NATIVE(pv_mmu_ops, write_cr3, "movq %rdi, %cr3");
-DEF_NATIVE(pv_mmu_ops, flush_tlb_single, "invlpg (%rdi)");
DEF_NATIVE(pv_cpu_ops, clts, "clts");
DEF_NATIVE(pv_cpu_ops, wbinvd, "wbinvd");
@@ -59,7 +58,6 @@
PATCH_SITE(pv_mmu_ops, read_cr3);
PATCH_SITE(pv_mmu_ops, write_cr3);
PATCH_SITE(pv_cpu_ops, clts);
- PATCH_SITE(pv_mmu_ops, flush_tlb_single);
PATCH_SITE(pv_cpu_ops, wbinvd);
#if defined(CONFIG_PARAVIRT_SPINLOCKS)
case PARAVIRT_PATCH(pv_lock_ops.queued_spin_unlock):
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index fc7cf64..54b2711 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -41,7 +41,7 @@
* section. Since TSS's are completely CPU-local, we want them
* on exact cacheline boundaries, to eliminate cacheline ping-pong.
*/
-__visible DEFINE_PER_CPU_SHARED_ALIGNED(struct tss_struct, cpu_tss) = {
+__visible DEFINE_PER_CPU_SHARED_ALIGNED_USER_MAPPED(struct tss_struct, cpu_tss) = {
.x86_tss = {
.sp0 = TOP_OF_INIT_STACK,
#ifdef CONFIG_X86_32
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index 067f981..ce020a6 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -106,6 +106,10 @@
load_cr3(initial_page_table);
#else
write_cr3(real_mode_header->trampoline_pgd);
+
+ /* Exiting long mode will fail if CR4.PCIDE is set. */
+ if (static_cpu_has(X86_FEATURE_PCID))
+ cr4_clear_bits(X86_CR4_PCIDE);
#endif
/* Jump to the identity-mapped low memory code */
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index feaab07..6b55012 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -114,6 +114,7 @@
#include <asm/microcode.h>
#include <asm/mmu_context.h>
#include <asm/kaslr.h>
+#include <asm/kaiser.h>
/*
* max_low_pfn_mapped: highest direct mapped pfn under 4GB
@@ -1019,6 +1020,12 @@
*/
init_hypervisor_platform();
+ /*
+ * This needs to happen right after XENPV is set on xen and
+ * kaiser_enabled is checked below in cleanup_highmap().
+ */
+ kaiser_check_boottime_disable();
+
x86_init.resources.probe_roms();
/* after parse_early_param, so could debug it */
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 9fe7b9e..e803d72 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -115,14 +115,10 @@
spin_lock_irqsave(&rtc_lock, flags);
CMOS_WRITE(0xa, 0xf);
spin_unlock_irqrestore(&rtc_lock, flags);
- local_flush_tlb();
- pr_debug("1.\n");
*((volatile unsigned short *)phys_to_virt(TRAMPOLINE_PHYS_HIGH)) =
start_eip >> 4;
- pr_debug("2.\n");
*((volatile unsigned short *)phys_to_virt(TRAMPOLINE_PHYS_LOW)) =
start_eip & 0xf;
- pr_debug("3.\n");
}
static inline void smpboot_restore_warm_reset_vector(void)
@@ -130,11 +126,6 @@
unsigned long flags;
/*
- * Install writable page 0 entry to set BIOS data area.
- */
- local_flush_tlb();
-
- /*
* Paranoid: Set warm reset code and vector here back
* to default values.
*/
diff --git a/arch/x86/kernel/tracepoint.c b/arch/x86/kernel/tracepoint.c
index 1c113db..2bb5ee4 100644
--- a/arch/x86/kernel/tracepoint.c
+++ b/arch/x86/kernel/tracepoint.c
@@ -9,10 +9,12 @@
#include <linux/atomic.h>
atomic_t trace_idt_ctr = ATOMIC_INIT(0);
+__aligned(PAGE_SIZE)
struct desc_ptr trace_idt_descr = { NR_VECTORS * 16 - 1,
(unsigned long) trace_idt_table };
/* No need to be aligned, but done to keep all IDTs defined the same way. */
+__aligned(PAGE_SIZE)
gate_desc trace_idt_table[NR_VECTORS] __page_aligned_bss;
static int trace_irq_vector_refcount;
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index bd4e3d4..322f433 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -153,7 +153,7 @@
* from double_fault.
*/
BUG_ON((unsigned long)(current_top_of_stack() -
- current_stack_pointer()) >= THREAD_SIZE);
+ current_stack_pointer) >= THREAD_SIZE);
preempt_enable_no_resched();
}
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index 44bf5cf..d07a9390 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -693,7 +693,6 @@
case INTEL_FAM6_KABYLAKE_DESKTOP:
crystal_khz = 24000; /* 24.0 MHz */
break;
- case INTEL_FAM6_SKYLAKE_X:
case INTEL_FAM6_ATOM_DENVERTON:
crystal_khz = 25000; /* 25.0 MHz */
break;
diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c
index 01f30e5..4b30128 100644
--- a/arch/x86/kernel/vm86_32.c
+++ b/arch/x86/kernel/vm86_32.c
@@ -191,7 +191,7 @@
pte_unmap_unlock(pte, ptl);
out:
up_write(&mm->mmap_sem);
- flush_tlb();
+ flush_tlb_mm_range(mm, 0xA0000, 0xA0000 + 32*PAGE_SIZE, 0UL);
}
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index dbf67f6..c7194e9 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -105,6 +105,13 @@
SOFTIRQENTRY_TEXT
*(.fixup)
*(.gnu.warning)
+
+#ifdef CONFIG_RETPOLINE
+ __indirect_thunk_start = .;
+ *(.text.__x86.indirect_thunk)
+ __indirect_thunk_end = .;
+#endif
+
/* End of text section */
_etext = .;
} :text = 0x9090
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index c8f8dd8..6f5a3b0 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -4990,6 +4990,8 @@
bool op_prefix = false;
bool has_seg_override = false;
struct opcode opcode;
+ u16 dummy;
+ struct desc_struct desc;
ctxt->memop.type = OP_NONE;
ctxt->memopp = NULL;
@@ -5008,6 +5010,11 @@
switch (mode) {
case X86EMUL_MODE_REAL:
case X86EMUL_MODE_VM86:
+ def_op_bytes = def_ad_bytes = 2;
+ ctxt->ops->get_segment(ctxt, &dummy, &desc, NULL, VCPU_SREG_CS);
+ if (desc.d)
+ def_op_bytes = def_ad_bytes = 4;
+ break;
case X86EMUL_MODE_PROT16:
def_op_bytes = def_ad_bytes = 2;
break;
diff --git a/arch/x86/kvm/ioapic.c b/arch/x86/kvm/ioapic.c
index 6e219e5..5f810bb 100644
--- a/arch/x86/kvm/ioapic.c
+++ b/arch/x86/kvm/ioapic.c
@@ -257,8 +257,7 @@
index == RTC_GSI) {
if (kvm_apic_match_dest(vcpu, NULL, 0,
e->fields.dest_id, e->fields.dest_mode) ||
- (e->fields.trig_mode == IOAPIC_EDGE_TRIG &&
- kvm_apic_pending_eoi(vcpu, e->fields.vector)))
+ kvm_apic_pending_eoi(vcpu, e->fields.vector))
__set_bit(e->fields.vector,
ioapic_handled_vectors);
}
@@ -279,6 +278,7 @@
{
unsigned index;
bool mask_before, mask_after;
+ int old_remote_irr, old_delivery_status;
union kvm_ioapic_redirect_entry *e;
switch (ioapic->ioregsel) {
@@ -301,14 +301,28 @@
return;
e = &ioapic->redirtbl[index];
mask_before = e->fields.mask;
+ /* Preserve read-only fields */
+ old_remote_irr = e->fields.remote_irr;
+ old_delivery_status = e->fields.delivery_status;
if (ioapic->ioregsel & 1) {
e->bits &= 0xffffffff;
e->bits |= (u64) val << 32;
} else {
e->bits &= ~0xffffffffULL;
e->bits |= (u32) val;
- e->fields.remote_irr = 0;
}
+ e->fields.remote_irr = old_remote_irr;
+ e->fields.delivery_status = old_delivery_status;
+
+ /*
+ * Some OSes (Linux, Xen) assume that Remote IRR bit will
+ * be cleared by IOAPIC hardware when the entry is configured
+ * as edge-triggered. This behavior is used to simulate an
+ * explicit EOI on IOAPICs that don't have the EOI register.
+ */
+ if (e->fields.trig_mode == IOAPIC_EDGE_TRIG)
+ e->fields.remote_irr = 0;
+
mask_after = e->fields.mask;
if (mask_before != mask_after)
kvm_fire_mask_notifiers(ioapic->kvm, KVM_IRQCHIP_IOAPIC, index, mask_after);
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 8148d8c..24af898 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -44,6 +44,7 @@
#include <asm/debugreg.h>
#include <asm/kvm_para.h>
#include <asm/irq_remapping.h>
+#include <asm/nospec-branch.h>
#include <asm/virtext.h>
#include "trace.h"
@@ -4869,6 +4870,25 @@
"mov %%r14, %c[r14](%[svm]) \n\t"
"mov %%r15, %c[r15](%[svm]) \n\t"
#endif
+ /*
+ * Clear host registers marked as clobbered to prevent
+ * speculative use.
+ */
+ "xor %%" _ASM_BX ", %%" _ASM_BX " \n\t"
+ "xor %%" _ASM_CX ", %%" _ASM_CX " \n\t"
+ "xor %%" _ASM_DX ", %%" _ASM_DX " \n\t"
+ "xor %%" _ASM_SI ", %%" _ASM_SI " \n\t"
+ "xor %%" _ASM_DI ", %%" _ASM_DI " \n\t"
+#ifdef CONFIG_X86_64
+ "xor %%r8, %%r8 \n\t"
+ "xor %%r9, %%r9 \n\t"
+ "xor %%r10, %%r10 \n\t"
+ "xor %%r11, %%r11 \n\t"
+ "xor %%r12, %%r12 \n\t"
+ "xor %%r13, %%r13 \n\t"
+ "xor %%r14, %%r14 \n\t"
+ "xor %%r15, %%r15 \n\t"
+#endif
"pop %%" _ASM_BP
:
: [svm]"a"(svm),
@@ -4898,6 +4918,9 @@
#endif
);
+ /* Eliminate branch target predictions from guest mode */
+ vmexit_fill_RSB();
+
#ifdef CONFIG_X86_64
wrmsrl(MSR_GS_BASE, svm->host.gs_base);
#else
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 263e560..178a344 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -48,6 +48,7 @@
#include <asm/kexec.h>
#include <asm/apic.h>
#include <asm/irq_remapping.h>
+#include <asm/nospec-branch.h>
#include "trace.h"
#include "pmu.h"
@@ -857,8 +858,16 @@
{
BUILD_BUG_ON(ARRAY_SIZE(vmcs_field_to_offset_table) > SHRT_MAX);
- if (field >= ARRAY_SIZE(vmcs_field_to_offset_table) ||
- vmcs_field_to_offset_table[field] == 0)
+ if (field >= ARRAY_SIZE(vmcs_field_to_offset_table))
+ return -ENOENT;
+
+ /*
+ * FIXME: Mitigation for CVE-2017-5753. To be replaced with a
+ * generic mechanism.
+ */
+ asm("lfence");
+
+ if (vmcs_field_to_offset_table[field] == 0)
return -ENOENT;
return vmcs_field_to_offset_table[field];
@@ -5185,7 +5194,7 @@
vmcs_write64(GUEST_IA32_DEBUGCTL, 0);
}
- vmcs_writel(GUEST_RFLAGS, 0x02);
+ kvm_set_rflags(vcpu, X86_EFLAGS_FIXED);
kvm_rip_write(vcpu, 0xfff0);
vmcs_writel(GUEST_GDTR_BASE, 0);
@@ -6248,7 +6257,7 @@
if (test_bit(KVM_REQ_EVENT, &vcpu->requests))
return 1;
- err = emulate_instruction(vcpu, EMULTYPE_NO_REEXECUTE);
+ err = emulate_instruction(vcpu, 0);
if (err == EMULATE_USER_EXIT) {
++vcpu->stat.mmio_exits;
@@ -8948,6 +8957,7 @@
/* Save guest registers, load host registers, keep flags */
"mov %0, %c[wordsize](%%" _ASM_SP ") \n\t"
"pop %0 \n\t"
+ "setbe %c[fail](%0)\n\t"
"mov %%" _ASM_AX ", %c[rax](%0) \n\t"
"mov %%" _ASM_BX ", %c[rbx](%0) \n\t"
__ASM_SIZE(pop) " %c[rcx](%0) \n\t"
@@ -8964,12 +8974,23 @@
"mov %%r13, %c[r13](%0) \n\t"
"mov %%r14, %c[r14](%0) \n\t"
"mov %%r15, %c[r15](%0) \n\t"
+ "xor %%r8d, %%r8d \n\t"
+ "xor %%r9d, %%r9d \n\t"
+ "xor %%r10d, %%r10d \n\t"
+ "xor %%r11d, %%r11d \n\t"
+ "xor %%r12d, %%r12d \n\t"
+ "xor %%r13d, %%r13d \n\t"
+ "xor %%r14d, %%r14d \n\t"
+ "xor %%r15d, %%r15d \n\t"
#endif
"mov %%cr2, %%" _ASM_AX " \n\t"
"mov %%" _ASM_AX ", %c[cr2](%0) \n\t"
+ "xor %%eax, %%eax \n\t"
+ "xor %%ebx, %%ebx \n\t"
+ "xor %%esi, %%esi \n\t"
+ "xor %%edi, %%edi \n\t"
"pop %%" _ASM_BP "; pop %%" _ASM_DX " \n\t"
- "setbe %c[fail](%0) \n\t"
".pushsection .rodata \n\t"
".global vmx_return \n\t"
"vmx_return: " _ASM_PTR " 2b \n\t"
@@ -9006,6 +9027,9 @@
#endif
);
+ /* Eliminate branch target predictions from guest mode */
+ vmexit_fill_RSB();
+
/* MSR_IA32_DEBUGCTLMSR is zeroed on vmexit. Restore it if needed */
if (debugctlmsr)
update_debugctlmsr(debugctlmsr);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 7e28e6c..e023ef9 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -773,7 +773,8 @@
return 1;
/* PCID can not be enabled when cr3[11:0]!=000H or EFER.LMA=0 */
- if ((kvm_read_cr3(vcpu) & X86_CR3_PCID_MASK) || !is_long_mode(vcpu))
+ if ((kvm_read_cr3(vcpu) & X86_CR3_PCID_ASID_MASK) ||
+ !is_long_mode(vcpu))
return 1;
}
@@ -1750,10 +1751,13 @@
/* both __this_cpu_read() and rdtsc() should be on the same cpu */
get_cpu();
- kvm_get_time_scale(NSEC_PER_SEC, __this_cpu_read(cpu_tsc_khz) * 1000LL,
- &hv_clock.tsc_shift,
- &hv_clock.tsc_to_system_mul);
- ret = __pvclock_read_cycles(&hv_clock, rdtsc());
+ if (__this_cpu_read(cpu_tsc_khz)) {
+ kvm_get_time_scale(NSEC_PER_SEC, __this_cpu_read(cpu_tsc_khz) * 1000LL,
+ &hv_clock.tsc_shift,
+ &hv_clock.tsc_to_system_mul);
+ ret = __pvclock_read_cycles(&hv_clock, rdtsc());
+ } else
+ ret = ktime_get_boot_ns() + ka->kvmclock_offset;
put_cpu();
@@ -4263,7 +4267,7 @@
addr, n, v))
&& kvm_io_bus_read(vcpu, KVM_MMIO_BUS, addr, n, v))
break;
- trace_kvm_mmio(KVM_TRACE_MMIO_READ, n, addr, *(u64 *)v);
+ trace_kvm_mmio(KVM_TRACE_MMIO_READ, n, addr, v);
handled += n;
addr += n;
len -= n;
@@ -4516,7 +4520,7 @@
{
if (vcpu->mmio_read_completed) {
trace_kvm_mmio(KVM_TRACE_MMIO_READ, bytes,
- vcpu->mmio_fragments[0].gpa, *(u64 *)val);
+ vcpu->mmio_fragments[0].gpa, val);
vcpu->mmio_read_completed = 0;
return 1;
}
@@ -4538,14 +4542,14 @@
static int write_mmio(struct kvm_vcpu *vcpu, gpa_t gpa, int bytes, void *val)
{
- trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, *(u64 *)val);
+ trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, val);
return vcpu_mmio_write(vcpu, gpa, bytes, val);
}
static int read_exit_mmio(struct kvm_vcpu *vcpu, gpa_t gpa,
void *val, int bytes)
{
- trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, bytes, gpa, 0);
+ trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, bytes, gpa, NULL);
return X86EMUL_IO_NEEDED;
}
@@ -5307,7 +5311,7 @@
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
vcpu->run->internal.ndata = 0;
- r = EMULATE_FAIL;
+ r = EMULATE_USER_EXIT;
}
kvm_queue_exception(vcpu, UD_VECTOR);
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index 34a7413..6bf1898 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -25,6 +25,7 @@
lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
lib-$(CONFIG_INSTRUCTION_DECODER) += insn.o inat.o
lib-$(CONFIG_RANDOMIZE_BASE) += kaslr.o
+lib-$(CONFIG_RETPOLINE) += retpoline.o
obj-y += msr.o msr-reg.o msr-reg-export.o hweight.o
diff --git a/arch/x86/lib/checksum_32.S b/arch/x86/lib/checksum_32.S
index 4d34bb5..46e71a7 100644
--- a/arch/x86/lib/checksum_32.S
+++ b/arch/x86/lib/checksum_32.S
@@ -29,7 +29,8 @@
#include <asm/errno.h>
#include <asm/asm.h>
#include <asm/export.h>
-
+#include <asm/nospec-branch.h>
+
/*
* computes a partial checksum, e.g. for TCP/UDP fragments
*/
@@ -156,7 +157,7 @@
negl %ebx
lea 45f(%ebx,%ebx,2), %ebx
testl %esi, %esi
- jmp *%ebx
+ JMP_NOSPEC %ebx
# Handle 2-byte-aligned regions
20: addw (%esi), %ax
@@ -439,7 +440,7 @@
andl $-32,%edx
lea 3f(%ebx,%ebx), %ebx
testl %esi, %esi
- jmp *%ebx
+ JMP_NOSPEC %ebx
1: addl $64,%esi
addl $64,%edi
SRC(movb -32(%edx),%bl) ; SRC(movb (%edx),%bl)
diff --git a/arch/x86/lib/cmdline.c b/arch/x86/lib/cmdline.c
index 5cc78bf..3261abb 100644
--- a/arch/x86/lib/cmdline.c
+++ b/arch/x86/lib/cmdline.c
@@ -104,7 +104,112 @@
return 0; /* Buffer overrun */
}
+/*
+ * Find a non-boolean option (i.e. option=argument). In accordance with
+ * standard Linux practice, if this option is repeated, this returns the
+ * last instance on the command line.
+ *
+ * @cmdline: the cmdline string
+ * @max_cmdline_size: the maximum size of cmdline
+ * @option: option string to look for
+ * @buffer: memory buffer to return the option argument
+ * @bufsize: size of the supplied memory buffer
+ *
+ * Returns the length of the argument (regardless of if it was
+ * truncated to fit in the buffer), or -1 on not found.
+ */
+static int
+__cmdline_find_option(const char *cmdline, int max_cmdline_size,
+ const char *option, char *buffer, int bufsize)
+{
+ char c;
+ int pos = 0, len = -1;
+ const char *opptr = NULL;
+ char *bufptr = buffer;
+ enum {
+ st_wordstart = 0, /* Start of word/after whitespace */
+ st_wordcmp, /* Comparing this word */
+ st_wordskip, /* Miscompare, skip */
+ st_bufcpy, /* Copying this to buffer */
+ } state = st_wordstart;
+
+ if (!cmdline)
+ return -1; /* No command line */
+
+ /*
+ * This 'pos' check ensures we do not overrun
+ * a non-NULL-terminated 'cmdline'
+ */
+ while (pos++ < max_cmdline_size) {
+ c = *(char *)cmdline++;
+ if (!c)
+ break;
+
+ switch (state) {
+ case st_wordstart:
+ if (myisspace(c))
+ break;
+
+ state = st_wordcmp;
+ opptr = option;
+ /* fall through */
+
+ case st_wordcmp:
+ if ((c == '=') && !*opptr) {
+ /*
+ * We matched all the way to the end of the
+ * option we were looking for, prepare to
+ * copy the argument.
+ */
+ len = 0;
+ bufptr = buffer;
+ state = st_bufcpy;
+ break;
+ } else if (c == *opptr++) {
+ /*
+ * We are currently matching, so continue
+ * to the next character on the cmdline.
+ */
+ break;
+ }
+ state = st_wordskip;
+ /* fall through */
+
+ case st_wordskip:
+ if (myisspace(c))
+ state = st_wordstart;
+ break;
+
+ case st_bufcpy:
+ if (myisspace(c)) {
+ state = st_wordstart;
+ } else {
+ /*
+ * Increment len, but don't overrun the
+ * supplied buffer and leave room for the
+ * NULL terminator.
+ */
+ if (++len < bufsize)
+ *bufptr++ = c;
+ }
+ break;
+ }
+ }
+
+ if (bufsize)
+ *bufptr = '\0';
+
+ return len;
+}
+
int cmdline_find_option_bool(const char *cmdline, const char *option)
{
return __cmdline_find_option_bool(cmdline, COMMAND_LINE_SIZE, option);
}
+
+int cmdline_find_option(const char *cmdline, const char *option, char *buffer,
+ int bufsize)
+{
+ return __cmdline_find_option(cmdline, COMMAND_LINE_SIZE, option,
+ buffer, bufsize);
+}
diff --git a/arch/x86/lib/delay.c b/arch/x86/lib/delay.c
index 073d1f1..9758524 100644
--- a/arch/x86/lib/delay.c
+++ b/arch/x86/lib/delay.c
@@ -93,6 +93,13 @@
{
u64 start, end, delay, loops = __loops;
+ /*
+ * Timer value of 0 causes MWAITX to wait indefinitely, unless there
+ * is a store on the memory monitored by MONITORX.
+ */
+ if (loops == 0)
+ return;
+
start = rdtsc_ordered();
for (;;) {
diff --git a/arch/x86/lib/retpoline.S b/arch/x86/lib/retpoline.S
new file mode 100644
index 0000000..dfb2ba9
--- /dev/null
+++ b/arch/x86/lib/retpoline.S
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <linux/stringify.h>
+#include <linux/linkage.h>
+#include <asm/dwarf2.h>
+#include <asm/cpufeatures.h>
+#include <asm/alternative-asm.h>
+#include <asm/export.h>
+#include <asm/nospec-branch.h>
+
+.macro THUNK reg
+ .section .text.__x86.indirect_thunk
+
+ENTRY(__x86_indirect_thunk_\reg)
+ CFI_STARTPROC
+ JMP_NOSPEC %\reg
+ CFI_ENDPROC
+ENDPROC(__x86_indirect_thunk_\reg)
+.endm
+
+/*
+ * Despite being an assembler file we can't just use .irp here
+ * because __KSYM_DEPS__ only uses the C preprocessor and would
+ * only see one instance of "__x86_indirect_thunk_\reg" rather
+ * than one per register with the correct names. So we do it
+ * the simple and nasty way...
+ */
+#define __EXPORT_THUNK(sym) _ASM_NOKPROBE(sym); EXPORT_SYMBOL(sym)
+#define EXPORT_THUNK(reg) __EXPORT_THUNK(__x86_indirect_thunk_ ## reg)
+#define GENERATE_THUNK(reg) THUNK reg ; EXPORT_THUNK(reg)
+
+GENERATE_THUNK(_ASM_AX)
+GENERATE_THUNK(_ASM_BX)
+GENERATE_THUNK(_ASM_CX)
+GENERATE_THUNK(_ASM_DX)
+GENERATE_THUNK(_ASM_SI)
+GENERATE_THUNK(_ASM_DI)
+GENERATE_THUNK(_ASM_BP)
+GENERATE_THUNK(_ASM_SP)
+#ifdef CONFIG_64BIT
+GENERATE_THUNK(r8)
+GENERATE_THUNK(r9)
+GENERATE_THUNK(r10)
+GENERATE_THUNK(r11)
+GENERATE_THUNK(r12)
+GENERATE_THUNK(r13)
+GENERATE_THUNK(r14)
+GENERATE_THUNK(r15)
+#endif
diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile
index 96d2b84..c548b46 100644
--- a/arch/x86/mm/Makefile
+++ b/arch/x86/mm/Makefile
@@ -37,5 +37,5 @@
obj-$(CONFIG_X86_INTEL_MPX) += mpx.o
obj-$(CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS) += pkeys.o
-obj-$(CONFIG_RANDOMIZE_MEMORY) += kaslr.o
-
+obj-$(CONFIG_RANDOMIZE_MEMORY) += kaslr.o
+obj-$(CONFIG_PAGE_TABLE_ISOLATION) += kaiser.o
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 8b5ff88..74dea7f 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -191,14 +191,15 @@
* 6. T1 : reaches here, sees vma_pkey(vma)=5, when we really
* faulted on a pte with its pkey=4.
*/
-static void fill_sig_info_pkey(int si_code, siginfo_t *info, u32 *pkey)
+static void fill_sig_info_pkey(int si_signo, int si_code, siginfo_t *info,
+ u32 *pkey)
{
/* This is effectively an #ifdef */
if (!boot_cpu_has(X86_FEATURE_OSPKE))
return;
/* Fault not from Protection Keys: nothing to do */
- if (si_code != SEGV_PKUERR)
+ if ((si_code != SEGV_PKUERR) || (si_signo != SIGSEGV))
return;
/*
* force_sig_info_fault() is called from a number of
@@ -237,7 +238,7 @@
lsb = PAGE_SHIFT;
info.si_addr_lsb = lsb;
- fill_sig_info_pkey(si_code, &info, pkey);
+ fill_sig_info_pkey(si_signo, si_code, &info, pkey);
force_sig_info(si_signo, &info, tsk);
}
diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c
index 889e761..f92bdb9 100644
--- a/arch/x86/mm/init.c
+++ b/arch/x86/mm/init.c
@@ -177,7 +177,7 @@
cr4_set_bits_and_update_boot(X86_CR4_PSE);
/* Enable PGE if available */
- if (boot_cpu_has(X86_FEATURE_PGE)) {
+ if (boot_cpu_has(X86_FEATURE_PGE) && !kaiser_enabled) {
cr4_set_bits_and_update_boot(X86_CR4_PGE);
__supported_pte_mask |= _PAGE_GLOBAL;
} else
@@ -764,13 +764,11 @@
}
DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate) = {
-#ifdef CONFIG_SMP
.active_mm = &init_mm,
.state = 0,
-#endif
.cr4 = ~0UL, /* fail hard if we screw up cr4 shadow initialization */
};
-EXPORT_SYMBOL_GPL(cpu_tlbstate);
+EXPORT_PER_CPU_SYMBOL(cpu_tlbstate);
void update_cache_mode_entry(unsigned entry, enum page_cache_mode cache)
{
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index 3e27ded..7df8e3a 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -324,6 +324,16 @@
continue;
if (vaddr < (unsigned long) _text || vaddr > end)
set_pmd(pmd, __pmd(0));
+ else if (kaiser_enabled) {
+ /*
+ * level2_kernel_pgt is initialized with _PAGE_GLOBAL:
+ * clear that now. This is not important, so long as
+ * CR4.PGE remains clear, but it removes an anomaly.
+ * Physical mapping setup below avoids _PAGE_GLOBAL
+ * by use of massage_pgprot() inside pfn_pte() etc.
+ */
+ set_pmd(pmd, pmd_clear_flags(*pmd, _PAGE_GLOBAL));
+ }
}
}
diff --git a/arch/x86/mm/kaiser.c b/arch/x86/mm/kaiser.c
new file mode 100644
index 0000000..ec678aa
--- /dev/null
+++ b/arch/x86/mm/kaiser.c
@@ -0,0 +1,483 @@
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/bug.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/uaccess.h>
+
+#undef pr_fmt
+#define pr_fmt(fmt) "Kernel/User page tables isolation: " fmt
+
+#include <asm/kaiser.h>
+#include <asm/tlbflush.h> /* to verify its kaiser declarations */
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/desc.h>
+#include <asm/cmdline.h>
+#include <asm/vsyscall.h>
+
+int kaiser_enabled __read_mostly = 1;
+EXPORT_SYMBOL(kaiser_enabled); /* for inlined TLB flush functions */
+
+__visible
+DEFINE_PER_CPU_USER_MAPPED(unsigned long, unsafe_stack_register_backup);
+
+/*
+ * These can have bit 63 set, so we can not just use a plain "or"
+ * instruction to get their value or'd into CR3. It would take
+ * another register. So, we use a memory reference to these instead.
+ *
+ * This is also handy because systems that do not support PCIDs
+ * just end up or'ing a 0 into their CR3, which does no harm.
+ */
+DEFINE_PER_CPU(unsigned long, x86_cr3_pcid_user);
+
+/*
+ * At runtime, the only things we map are some things for CPU
+ * hotplug, and stacks for new processes. No two CPUs will ever
+ * be populating the same addresses, so we only need to ensure
+ * that we protect between two CPUs trying to allocate and
+ * populate the same page table page.
+ *
+ * Only take this lock when doing a set_p[4um]d(), but it is not
+ * needed for doing a set_pte(). We assume that only the *owner*
+ * of a given allocation will be doing this for _their_
+ * allocation.
+ *
+ * This ensures that once a system has been running for a while
+ * and there have been stacks all over and these page tables
+ * are fully populated, there will be no further acquisitions of
+ * this lock.
+ */
+static DEFINE_SPINLOCK(shadow_table_allocation_lock);
+
+/*
+ * Returns -1 on error.
+ */
+static inline unsigned long get_pa_from_mapping(unsigned long vaddr)
+{
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ pgd = pgd_offset_k(vaddr);
+ /*
+ * We made all the kernel PGDs present in kaiser_init().
+ * We expect them to stay that way.
+ */
+ BUG_ON(pgd_none(*pgd));
+ /*
+ * PGDs are either 512GB or 128TB on all x86_64
+ * configurations. We don't handle these.
+ */
+ BUG_ON(pgd_large(*pgd));
+
+ pud = pud_offset(pgd, vaddr);
+ if (pud_none(*pud)) {
+ WARN_ON_ONCE(1);
+ return -1;
+ }
+
+ if (pud_large(*pud))
+ return (pud_pfn(*pud) << PAGE_SHIFT) | (vaddr & ~PUD_PAGE_MASK);
+
+ pmd = pmd_offset(pud, vaddr);
+ if (pmd_none(*pmd)) {
+ WARN_ON_ONCE(1);
+ return -1;
+ }
+
+ if (pmd_large(*pmd))
+ return (pmd_pfn(*pmd) << PAGE_SHIFT) | (vaddr & ~PMD_PAGE_MASK);
+
+ pte = pte_offset_kernel(pmd, vaddr);
+ if (pte_none(*pte)) {
+ WARN_ON_ONCE(1);
+ return -1;
+ }
+
+ return (pte_pfn(*pte) << PAGE_SHIFT) | (vaddr & ~PAGE_MASK);
+}
+
+/*
+ * This is a relatively normal page table walk, except that it
+ * also tries to allocate page tables pages along the way.
+ *
+ * Returns a pointer to a PTE on success, or NULL on failure.
+ */
+static pte_t *kaiser_pagetable_walk(unsigned long address, bool user)
+{
+ pmd_t *pmd;
+ pud_t *pud;
+ pgd_t *pgd = native_get_shadow_pgd(pgd_offset_k(address));
+ gfp_t gfp = (GFP_KERNEL | __GFP_NOTRACK | __GFP_ZERO);
+ unsigned long prot = _KERNPG_TABLE;
+
+ if (pgd_none(*pgd)) {
+ WARN_ONCE(1, "All shadow pgds should have been populated");
+ return NULL;
+ }
+ BUILD_BUG_ON(pgd_large(*pgd) != 0);
+
+ if (user) {
+ /*
+ * The vsyscall page is the only page that will have
+ * _PAGE_USER set. Catch everything else.
+ */
+ BUG_ON(address != VSYSCALL_ADDR);
+
+ set_pgd(pgd, __pgd(pgd_val(*pgd) | _PAGE_USER));
+ prot = _PAGE_TABLE;
+ }
+
+ pud = pud_offset(pgd, address);
+ /* The shadow page tables do not use large mappings: */
+ if (pud_large(*pud)) {
+ WARN_ON(1);
+ return NULL;
+ }
+ if (pud_none(*pud)) {
+ unsigned long new_pmd_page = __get_free_page(gfp);
+ if (!new_pmd_page)
+ return NULL;
+ spin_lock(&shadow_table_allocation_lock);
+ if (pud_none(*pud)) {
+ set_pud(pud, __pud(prot | __pa(new_pmd_page)));
+ __inc_zone_page_state(virt_to_page((void *)
+ new_pmd_page), NR_KAISERTABLE);
+ } else
+ free_page(new_pmd_page);
+ spin_unlock(&shadow_table_allocation_lock);
+ }
+
+ pmd = pmd_offset(pud, address);
+ /* The shadow page tables do not use large mappings: */
+ if (pmd_large(*pmd)) {
+ WARN_ON(1);
+ return NULL;
+ }
+ if (pmd_none(*pmd)) {
+ unsigned long new_pte_page = __get_free_page(gfp);
+ if (!new_pte_page)
+ return NULL;
+ spin_lock(&shadow_table_allocation_lock);
+ if (pmd_none(*pmd)) {
+ set_pmd(pmd, __pmd(prot | __pa(new_pte_page)));
+ __inc_zone_page_state(virt_to_page((void *)
+ new_pte_page), NR_KAISERTABLE);
+ } else
+ free_page(new_pte_page);
+ spin_unlock(&shadow_table_allocation_lock);
+ }
+
+ return pte_offset_kernel(pmd, address);
+}
+
+static int kaiser_add_user_map(const void *__start_addr, unsigned long size,
+ unsigned long flags)
+{
+ int ret = 0;
+ pte_t *pte;
+ unsigned long start_addr = (unsigned long )__start_addr;
+ unsigned long address = start_addr & PAGE_MASK;
+ unsigned long end_addr = PAGE_ALIGN(start_addr + size);
+ unsigned long target_address;
+
+ /*
+ * It is convenient for callers to pass in __PAGE_KERNEL etc,
+ * and there is no actual harm from setting _PAGE_GLOBAL, so
+ * long as CR4.PGE is not set. But it is nonetheless troubling
+ * to see Kaiser itself setting _PAGE_GLOBAL (now that "nokaiser"
+ * requires that not to be #defined to 0): so mask it off here.
+ */
+ flags &= ~_PAGE_GLOBAL;
+ if (!(__supported_pte_mask & _PAGE_NX))
+ flags &= ~_PAGE_NX;
+
+ for (; address < end_addr; address += PAGE_SIZE) {
+ target_address = get_pa_from_mapping(address);
+ if (target_address == -1) {
+ ret = -EIO;
+ break;
+ }
+ pte = kaiser_pagetable_walk(address, flags & _PAGE_USER);
+ if (!pte) {
+ ret = -ENOMEM;
+ break;
+ }
+ if (pte_none(*pte)) {
+ set_pte(pte, __pte(flags | target_address));
+ } else {
+ pte_t tmp;
+ set_pte(&tmp, __pte(flags | target_address));
+ WARN_ON_ONCE(!pte_same(*pte, tmp));
+ }
+ }
+ return ret;
+}
+
+static int kaiser_add_user_map_ptrs(const void *start, const void *end, unsigned long flags)
+{
+ unsigned long size = end - start;
+
+ return kaiser_add_user_map(start, size, flags);
+}
+
+/*
+ * Ensure that the top level of the (shadow) page tables are
+ * entirely populated. This ensures that all processes that get
+ * forked have the same entries. This way, we do not have to
+ * ever go set up new entries in older processes.
+ *
+ * Note: we never free these, so there are no updates to them
+ * after this.
+ */
+static void __init kaiser_init_all_pgds(void)
+{
+ pgd_t *pgd;
+ int i = 0;
+
+ pgd = native_get_shadow_pgd(pgd_offset_k((unsigned long )0));
+ for (i = PTRS_PER_PGD / 2; i < PTRS_PER_PGD; i++) {
+ pgd_t new_pgd;
+ pud_t *pud = pud_alloc_one(&init_mm,
+ PAGE_OFFSET + i * PGDIR_SIZE);
+ if (!pud) {
+ WARN_ON(1);
+ break;
+ }
+ inc_zone_page_state(virt_to_page(pud), NR_KAISERTABLE);
+ new_pgd = __pgd(_KERNPG_TABLE |__pa(pud));
+ /*
+ * Make sure not to stomp on some other pgd entry.
+ */
+ if (!pgd_none(pgd[i])) {
+ WARN_ON(1);
+ continue;
+ }
+ set_pgd(pgd + i, new_pgd);
+ }
+}
+
+#define kaiser_add_user_map_early(start, size, flags) do { \
+ int __ret = kaiser_add_user_map(start, size, flags); \
+ WARN_ON(__ret); \
+} while (0)
+
+#define kaiser_add_user_map_ptrs_early(start, end, flags) do { \
+ int __ret = kaiser_add_user_map_ptrs(start, end, flags); \
+ WARN_ON(__ret); \
+} while (0)
+
+void __init kaiser_check_boottime_disable(void)
+{
+ bool enable = true;
+ char arg[5];
+ int ret;
+
+ if (boot_cpu_has(X86_FEATURE_XENPV))
+ goto silent_disable;
+
+ ret = cmdline_find_option(boot_command_line, "pti", arg, sizeof(arg));
+ if (ret > 0) {
+ if (!strncmp(arg, "on", 2))
+ goto enable;
+
+ if (!strncmp(arg, "off", 3))
+ goto disable;
+
+ if (!strncmp(arg, "auto", 4))
+ goto skip;
+ }
+
+ if (cmdline_find_option_bool(boot_command_line, "nopti"))
+ goto disable;
+
+skip:
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
+ goto disable;
+
+enable:
+ if (enable)
+ setup_force_cpu_cap(X86_FEATURE_KAISER);
+
+ return;
+
+disable:
+ pr_info("disabled\n");
+
+silent_disable:
+ kaiser_enabled = 0;
+ setup_clear_cpu_cap(X86_FEATURE_KAISER);
+}
+
+/*
+ * If anything in here fails, we will likely die on one of the
+ * first kernel->user transitions and init will die. But, we
+ * will have most of the kernel up by then and should be able to
+ * get a clean warning out of it. If we BUG_ON() here, we run
+ * the risk of being before we have good console output.
+ */
+void __init kaiser_init(void)
+{
+ int cpu;
+
+ if (!kaiser_enabled)
+ return;
+
+ kaiser_init_all_pgds();
+
+ /*
+ * Note that this sets _PAGE_USER and it needs to happen when the
+ * pagetable hierarchy gets created, i.e., early. Otherwise
+ * kaiser_pagetable_walk() will encounter initialized PTEs in the
+ * hierarchy and not set the proper permissions, leading to the
+ * pagefaults with page-protection violations when trying to read the
+ * vsyscall page. For example.
+ */
+ if (vsyscall_enabled())
+ kaiser_add_user_map_early((void *)VSYSCALL_ADDR,
+ PAGE_SIZE,
+ vsyscall_pgprot);
+
+ for_each_possible_cpu(cpu) {
+ void *percpu_vaddr = __per_cpu_user_mapped_start +
+ per_cpu_offset(cpu);
+ unsigned long percpu_sz = __per_cpu_user_mapped_end -
+ __per_cpu_user_mapped_start;
+ kaiser_add_user_map_early(percpu_vaddr, percpu_sz,
+ __PAGE_KERNEL);
+ }
+
+ /*
+ * Map the entry/exit text section, which is needed at
+ * switches from user to and from kernel.
+ */
+ kaiser_add_user_map_ptrs_early(__entry_text_start, __entry_text_end,
+ __PAGE_KERNEL_RX);
+
+#if defined(CONFIG_FUNCTION_GRAPH_TRACER) || defined(CONFIG_KASAN)
+ kaiser_add_user_map_ptrs_early(__irqentry_text_start,
+ __irqentry_text_end,
+ __PAGE_KERNEL_RX);
+#endif
+ kaiser_add_user_map_early((void *)idt_descr.address,
+ sizeof(gate_desc) * NR_VECTORS,
+ __PAGE_KERNEL_RO);
+#ifdef CONFIG_TRACING
+ kaiser_add_user_map_early(&trace_idt_descr,
+ sizeof(trace_idt_descr),
+ __PAGE_KERNEL);
+ kaiser_add_user_map_early(&trace_idt_table,
+ sizeof(gate_desc) * NR_VECTORS,
+ __PAGE_KERNEL);
+#endif
+ kaiser_add_user_map_early(&debug_idt_descr, sizeof(debug_idt_descr),
+ __PAGE_KERNEL);
+ kaiser_add_user_map_early(&debug_idt_table,
+ sizeof(gate_desc) * NR_VECTORS,
+ __PAGE_KERNEL);
+
+ pr_info("enabled\n");
+}
+
+/* Add a mapping to the shadow mapping, and synchronize the mappings */
+int kaiser_add_mapping(unsigned long addr, unsigned long size, unsigned long flags)
+{
+ if (!kaiser_enabled)
+ return 0;
+ return kaiser_add_user_map((const void *)addr, size, flags);
+}
+
+void kaiser_remove_mapping(unsigned long start, unsigned long size)
+{
+ extern void unmap_pud_range_nofree(pgd_t *pgd,
+ unsigned long start, unsigned long end);
+ unsigned long end = start + size;
+ unsigned long addr, next;
+ pgd_t *pgd;
+
+ if (!kaiser_enabled)
+ return;
+ pgd = native_get_shadow_pgd(pgd_offset_k(start));
+ for (addr = start; addr < end; pgd++, addr = next) {
+ next = pgd_addr_end(addr, end);
+ unmap_pud_range_nofree(pgd, addr, next);
+ }
+}
+
+/*
+ * Page table pages are page-aligned. The lower half of the top
+ * level is used for userspace and the top half for the kernel.
+ * This returns true for user pages that need to get copied into
+ * both the user and kernel copies of the page tables, and false
+ * for kernel pages that should only be in the kernel copy.
+ */
+static inline bool is_userspace_pgd(pgd_t *pgdp)
+{
+ return ((unsigned long)pgdp % PAGE_SIZE) < (PAGE_SIZE / 2);
+}
+
+pgd_t kaiser_set_shadow_pgd(pgd_t *pgdp, pgd_t pgd)
+{
+ if (!kaiser_enabled)
+ return pgd;
+ /*
+ * Do we need to also populate the shadow pgd? Check _PAGE_USER to
+ * skip cases like kexec and EFI which make temporary low mappings.
+ */
+ if (pgd.pgd & _PAGE_USER) {
+ if (is_userspace_pgd(pgdp)) {
+ native_get_shadow_pgd(pgdp)->pgd = pgd.pgd;
+ /*
+ * Even if the entry is *mapping* userspace, ensure
+ * that userspace can not use it. This way, if we
+ * get out to userspace running on the kernel CR3,
+ * userspace will crash instead of running.
+ */
+ if (__supported_pte_mask & _PAGE_NX)
+ pgd.pgd |= _PAGE_NX;
+ }
+ } else if (!pgd.pgd) {
+ /*
+ * pgd_clear() cannot check _PAGE_USER, and is even used to
+ * clear corrupted pgd entries: so just rely on cases like
+ * kexec and EFI never to be using pgd_clear().
+ */
+ if (!WARN_ON_ONCE((unsigned long)pgdp & PAGE_SIZE) &&
+ is_userspace_pgd(pgdp))
+ native_get_shadow_pgd(pgdp)->pgd = pgd.pgd;
+ }
+ return pgd;
+}
+
+void kaiser_setup_pcid(void)
+{
+ unsigned long user_cr3 = KAISER_SHADOW_PGD_OFFSET;
+
+ if (this_cpu_has(X86_FEATURE_PCID))
+ user_cr3 |= X86_CR3_PCID_USER_NOFLUSH;
+ /*
+ * These variables are used by the entry/exit
+ * code to change PCID and pgd and TLB flushing.
+ */
+ this_cpu_write(x86_cr3_pcid_user, user_cr3);
+}
+
+/*
+ * Make a note that this cpu will need to flush USER tlb on return to user.
+ * If cpu does not have PCID, then the NOFLUSH bit will never have been set.
+ */
+void kaiser_flush_tlb_on_return_to_user(void)
+{
+ if (this_cpu_has(X86_FEATURE_PCID))
+ this_cpu_write(x86_cr3_pcid_user,
+ X86_CR3_PCID_USER_FLUSH | KAISER_SHADOW_PGD_OFFSET);
+}
+EXPORT_SYMBOL(kaiser_flush_tlb_on_return_to_user);
diff --git a/arch/x86/mm/kaslr.c b/arch/x86/mm/kaslr.c
index aed2064..319183d 100644
--- a/arch/x86/mm/kaslr.c
+++ b/arch/x86/mm/kaslr.c
@@ -189,6 +189,6 @@
*pud_tramp = *pud;
}
- set_pgd(&trampoline_pgd_entry,
- __pgd(_KERNPG_TABLE | __pa(pud_page_tramp)));
+ /* Avoid set_pgd(), in case it's complicated by CONFIG_PAGE_TABLE_ISOLATION */
+ trampoline_pgd_entry = __pgd(_KERNPG_TABLE | __pa(pud_page_tramp));
}
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c
index e3353c9..73dcb0e1 100644
--- a/arch/x86/mm/pageattr.c
+++ b/arch/x86/mm/pageattr.c
@@ -52,6 +52,7 @@
#define CPA_FLUSHTLB 1
#define CPA_ARRAY 2
#define CPA_PAGES_ARRAY 4
+#define CPA_FREE_PAGETABLES 8
#ifdef CONFIG_PROC_FS
static unsigned long direct_pages_count[PG_LEVEL_NUM];
@@ -729,10 +730,13 @@
return 0;
}
-static bool try_to_free_pte_page(pte_t *pte)
+static bool try_to_free_pte_page(struct cpa_data *cpa, pte_t *pte)
{
int i;
+ if (!(cpa->flags & CPA_FREE_PAGETABLES))
+ return false;
+
for (i = 0; i < PTRS_PER_PTE; i++)
if (!pte_none(pte[i]))
return false;
@@ -741,10 +745,13 @@
return true;
}
-static bool try_to_free_pmd_page(pmd_t *pmd)
+static bool try_to_free_pmd_page(struct cpa_data *cpa, pmd_t *pmd)
{
int i;
+ if (!(cpa->flags & CPA_FREE_PAGETABLES))
+ return false;
+
for (i = 0; i < PTRS_PER_PMD; i++)
if (!pmd_none(pmd[i]))
return false;
@@ -753,7 +760,9 @@
return true;
}
-static bool unmap_pte_range(pmd_t *pmd, unsigned long start, unsigned long end)
+static bool unmap_pte_range(struct cpa_data *cpa, pmd_t *pmd,
+ unsigned long start,
+ unsigned long end)
{
pte_t *pte = pte_offset_kernel(pmd, start);
@@ -764,22 +773,23 @@
pte++;
}
- if (try_to_free_pte_page((pte_t *)pmd_page_vaddr(*pmd))) {
+ if (try_to_free_pte_page(cpa, (pte_t *)pmd_page_vaddr(*pmd))) {
pmd_clear(pmd);
return true;
}
return false;
}
-static void __unmap_pmd_range(pud_t *pud, pmd_t *pmd,
+static void __unmap_pmd_range(struct cpa_data *cpa, pud_t *pud, pmd_t *pmd,
unsigned long start, unsigned long end)
{
- if (unmap_pte_range(pmd, start, end))
- if (try_to_free_pmd_page((pmd_t *)pud_page_vaddr(*pud)))
+ if (unmap_pte_range(cpa, pmd, start, end))
+ if (try_to_free_pmd_page(cpa, (pmd_t *)pud_page_vaddr(*pud)))
pud_clear(pud);
}
-static void unmap_pmd_range(pud_t *pud, unsigned long start, unsigned long end)
+static void unmap_pmd_range(struct cpa_data *cpa, pud_t *pud,
+ unsigned long start, unsigned long end)
{
pmd_t *pmd = pmd_offset(pud, start);
@@ -790,7 +800,7 @@
unsigned long next_page = (start + PMD_SIZE) & PMD_MASK;
unsigned long pre_end = min_t(unsigned long, end, next_page);
- __unmap_pmd_range(pud, pmd, start, pre_end);
+ __unmap_pmd_range(cpa, pud, pmd, start, pre_end);
start = pre_end;
pmd++;
@@ -803,7 +813,8 @@
if (pmd_large(*pmd))
pmd_clear(pmd);
else
- __unmap_pmd_range(pud, pmd, start, start + PMD_SIZE);
+ __unmap_pmd_range(cpa, pud, pmd,
+ start, start + PMD_SIZE);
start += PMD_SIZE;
pmd++;
@@ -813,17 +824,19 @@
* 4K leftovers?
*/
if (start < end)
- return __unmap_pmd_range(pud, pmd, start, end);
+ return __unmap_pmd_range(cpa, pud, pmd, start, end);
/*
* Try again to free the PMD page if haven't succeeded above.
*/
if (!pud_none(*pud))
- if (try_to_free_pmd_page((pmd_t *)pud_page_vaddr(*pud)))
+ if (try_to_free_pmd_page(cpa, (pmd_t *)pud_page_vaddr(*pud)))
pud_clear(pud);
}
-static void unmap_pud_range(pgd_t *pgd, unsigned long start, unsigned long end)
+static void __unmap_pud_range(struct cpa_data *cpa, pgd_t *pgd,
+ unsigned long start,
+ unsigned long end)
{
pud_t *pud = pud_offset(pgd, start);
@@ -834,7 +847,7 @@
unsigned long next_page = (start + PUD_SIZE) & PUD_MASK;
unsigned long pre_end = min_t(unsigned long, end, next_page);
- unmap_pmd_range(pud, start, pre_end);
+ unmap_pmd_range(cpa, pud, start, pre_end);
start = pre_end;
pud++;
@@ -848,7 +861,7 @@
if (pud_large(*pud))
pud_clear(pud);
else
- unmap_pmd_range(pud, start, start + PUD_SIZE);
+ unmap_pmd_range(cpa, pud, start, start + PUD_SIZE);
start += PUD_SIZE;
pud++;
@@ -858,7 +871,7 @@
* 2M leftovers?
*/
if (start < end)
- unmap_pmd_range(pud, start, end);
+ unmap_pmd_range(cpa, pud, start, end);
/*
* No need to try to free the PUD page because we'll free it in
@@ -866,6 +879,24 @@
*/
}
+static void unmap_pud_range(pgd_t *pgd, unsigned long start, unsigned long end)
+{
+ struct cpa_data cpa = {
+ .flags = CPA_FREE_PAGETABLES,
+ };
+
+ __unmap_pud_range(&cpa, pgd, start, end);
+}
+
+void unmap_pud_range_nofree(pgd_t *pgd, unsigned long start, unsigned long end)
+{
+ struct cpa_data cpa = {
+ .flags = 0,
+ };
+
+ __unmap_pud_range(&cpa, pgd, start, end);
+}
+
static int alloc_pte_page(pmd_t *pmd)
{
pte_t *pte = (pte_t *)get_zeroed_page(GFP_KERNEL | __GFP_NOTRACK);
diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c
index 3feec5a..209b946 100644
--- a/arch/x86/mm/pgtable.c
+++ b/arch/x86/mm/pgtable.c
@@ -344,14 +344,15 @@
kmem_cache_free(pgd_cache, pgd);
}
#else
+
static inline pgd_t *_pgd_alloc(void)
{
- return (pgd_t *)__get_free_page(PGALLOC_GFP);
+ return (pgd_t *)__get_free_pages(PGALLOC_GFP, PGD_ALLOCATION_ORDER);
}
static inline void _pgd_free(pgd_t *pgd)
{
- free_page((unsigned long)pgd);
+ free_pages((unsigned long)pgd, PGD_ALLOCATION_ORDER);
}
#endif /* CONFIG_X86_PAE */
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index 75fb011..578973a 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -6,16 +6,17 @@
#include <linux/interrupt.h>
#include <linux/export.h>
#include <linux/cpu.h>
+#include <linux/debugfs.h>
#include <asm/tlbflush.h>
#include <asm/mmu_context.h>
#include <asm/cache.h>
#include <asm/apic.h>
#include <asm/uv/uv.h>
-#include <linux/debugfs.h>
+#include <asm/kaiser.h>
/*
- * Smarter SMP flushing macros.
+ * TLB flushing, formerly SMP-only
* c/o Linus Torvalds.
*
* These mean you can really definitely utterly forget about
@@ -28,14 +29,42 @@
* Implement flush IPI by CALL_FUNCTION_VECTOR, Alex Shi
*/
-#ifdef CONFIG_SMP
-
struct flush_tlb_info {
struct mm_struct *flush_mm;
unsigned long flush_start;
unsigned long flush_end;
};
+static void load_new_mm_cr3(pgd_t *pgdir)
+{
+ unsigned long new_mm_cr3 = __pa(pgdir);
+
+ if (kaiser_enabled) {
+ /*
+ * We reuse the same PCID for different tasks, so we must
+ * flush all the entries for the PCID out when we change tasks.
+ * Flush KERN below, flush USER when returning to userspace in
+ * kaiser's SWITCH_USER_CR3 (_SWITCH_TO_USER_CR3) macro.
+ *
+ * invpcid_flush_single_context(X86_CR3_PCID_ASID_USER) could
+ * do it here, but can only be used if X86_FEATURE_INVPCID is
+ * available - and many machines support pcid without invpcid.
+ *
+ * If X86_CR3_PCID_KERN_FLUSH actually added something, then it
+ * would be needed in the write_cr3() below - if PCIDs enabled.
+ */
+ BUILD_BUG_ON(X86_CR3_PCID_KERN_FLUSH);
+ kaiser_flush_tlb_on_return_to_user();
+ }
+
+ /*
+ * Caution: many callers of this function expect
+ * that load_cr3() is serializing and orders TLB
+ * fills with respect to the mm_cpumask writes.
+ */
+ write_cr3(new_mm_cr3);
+}
+
/*
* We cannot call mmdrop() because we are in interrupt context,
* instead update mm->cpu_vm_mask.
@@ -47,7 +76,7 @@
BUG();
if (cpumask_test_cpu(cpu, mm_cpumask(active_mm))) {
cpumask_clear_cpu(cpu, mm_cpumask(active_mm));
- load_cr3(swapper_pg_dir);
+ load_new_mm_cr3(swapper_pg_dir);
/*
* This gets called in the idle path where RCU
* functions differently. Tracing normally
@@ -59,8 +88,6 @@
}
EXPORT_SYMBOL_GPL(leave_mm);
-#endif /* CONFIG_SMP */
-
void switch_mm(struct mm_struct *prev, struct mm_struct *next,
struct task_struct *tsk)
{
@@ -83,7 +110,7 @@
* mapped in the new pgd, we'll double-fault. Forcibly
* map it.
*/
- unsigned int stack_pgd_index = pgd_index(current_stack_pointer());
+ unsigned int stack_pgd_index = pgd_index(current_stack_pointer);
pgd_t *pgd = next->pgd + stack_pgd_index;
@@ -91,10 +118,8 @@
set_pgd(pgd, init_mm.pgd[stack_pgd_index]);
}
-#ifdef CONFIG_SMP
this_cpu_write(cpu_tlbstate.state, TLBSTATE_OK);
this_cpu_write(cpu_tlbstate.active_mm, next);
-#endif
cpumask_set_cpu(cpu, mm_cpumask(next));
@@ -126,7 +151,7 @@
* ordering guarantee we need.
*
*/
- load_cr3(next->pgd);
+ load_new_mm_cr3(next->pgd);
trace_tlb_flush(TLB_FLUSH_ON_TASK_SWITCH, TLB_FLUSH_ALL);
@@ -152,9 +177,7 @@
if (unlikely(prev->context.ldt != next->context.ldt))
load_mm_ldt(next);
#endif
- }
-#ifdef CONFIG_SMP
- else {
+ } else {
this_cpu_write(cpu_tlbstate.state, TLBSTATE_OK);
BUG_ON(this_cpu_read(cpu_tlbstate.active_mm) != next);
@@ -175,17 +198,14 @@
* As above, load_cr3() is serializing and orders TLB
* fills with respect to the mm_cpumask write.
*/
- load_cr3(next->pgd);
+ load_new_mm_cr3(next->pgd);
trace_tlb_flush(TLB_FLUSH_ON_TASK_SWITCH, TLB_FLUSH_ALL);
load_mm_cr4(next);
load_mm_ldt(next);
}
}
-#endif
}
-#ifdef CONFIG_SMP
-
/*
* The flush IPI assumes that a thread switch happens in this order:
* [cpu0: the cpu that switches]
@@ -287,23 +307,6 @@
smp_call_function_many(cpumask, flush_tlb_func, &info, 1);
}
-void flush_tlb_current_task(void)
-{
- struct mm_struct *mm = current->mm;
-
- preempt_disable();
-
- count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ALL);
-
- /* This is an implicit full barrier that synchronizes with switch_mm. */
- local_flush_tlb();
-
- trace_tlb_flush(TLB_LOCAL_SHOOTDOWN, TLB_FLUSH_ALL);
- if (cpumask_any_but(mm_cpumask(mm), smp_processor_id()) < nr_cpu_ids)
- flush_tlb_others(mm_cpumask(mm), mm, 0UL, TLB_FLUSH_ALL);
- preempt_enable();
-}
-
/*
* See Documentation/x86/tlb.txt for details. We choose 33
* because it is large enough to cover the vast majority (at
@@ -324,6 +327,12 @@
unsigned long base_pages_to_flush = TLB_FLUSH_ALL;
preempt_disable();
+
+ if ((end != TLB_FLUSH_ALL) && !(vmflag & VM_HUGETLB))
+ base_pages_to_flush = (end - start) >> PAGE_SHIFT;
+ if (base_pages_to_flush > tlb_single_page_flush_ceiling)
+ base_pages_to_flush = TLB_FLUSH_ALL;
+
if (current->active_mm != mm) {
/* Synchronize with switch_mm. */
smp_mb();
@@ -340,15 +349,11 @@
goto out;
}
- if ((end != TLB_FLUSH_ALL) && !(vmflag & VM_HUGETLB))
- base_pages_to_flush = (end - start) >> PAGE_SHIFT;
-
/*
* Both branches below are implicit full barriers (MOV to CR or
* INVLPG) that synchronize with switch_mm.
*/
- if (base_pages_to_flush > tlb_single_page_flush_ceiling) {
- base_pages_to_flush = TLB_FLUSH_ALL;
+ if (base_pages_to_flush == TLB_FLUSH_ALL) {
count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ALL);
local_flush_tlb();
} else {
@@ -369,33 +374,6 @@
preempt_enable();
}
-void flush_tlb_page(struct vm_area_struct *vma, unsigned long start)
-{
- struct mm_struct *mm = vma->vm_mm;
-
- preempt_disable();
-
- if (current->active_mm == mm) {
- if (current->mm) {
- /*
- * Implicit full barrier (INVLPG) that synchronizes
- * with switch_mm.
- */
- __flush_tlb_one(start);
- } else {
- leave_mm(smp_processor_id());
-
- /* Synchronize with switch_mm. */
- smp_mb();
- }
- }
-
- if (cpumask_any_but(mm_cpumask(mm), smp_processor_id()) < nr_cpu_ids)
- flush_tlb_others(mm_cpumask(mm), mm, start, start + PAGE_SIZE);
-
- preempt_enable();
-}
-
static void do_flush_tlb_all(void *info)
{
count_vm_tlb_event(NR_TLB_REMOTE_FLUSH_RECEIVED);
@@ -480,5 +458,3 @@
return 0;
}
late_initcall(create_tlb_single_page_flush_ceiling);
-
-#endif /* CONFIG_SMP */
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index 15f7436..7840331 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -278,10 +278,10 @@
/* if (index >= array->map.max_entries)
* goto out;
*/
- EMIT4(0x48, 0x8B, 0x46, /* mov rax, qword ptr [rsi + 16] */
+ EMIT2(0x89, 0xD2); /* mov edx, edx */
+ EMIT3(0x39, 0x56, /* cmp dword ptr [rsi + 16], edx */
offsetof(struct bpf_array, map.max_entries));
- EMIT3(0x48, 0x39, 0xD0); /* cmp rax, rdx */
-#define OFFSET1 47 /* number of bytes to jump */
+#define OFFSET1 43 /* number of bytes to jump */
EMIT2(X86_JBE, OFFSET1); /* jbe out */
label1 = cnt;
@@ -290,21 +290,20 @@
*/
EMIT2_off32(0x8B, 0x85, -STACKSIZE + 36); /* mov eax, dword ptr [rbp - 516] */
EMIT3(0x83, 0xF8, MAX_TAIL_CALL_CNT); /* cmp eax, MAX_TAIL_CALL_CNT */
-#define OFFSET2 36
+#define OFFSET2 32
EMIT2(X86_JA, OFFSET2); /* ja out */
label2 = cnt;
EMIT3(0x83, 0xC0, 0x01); /* add eax, 1 */
EMIT2_off32(0x89, 0x85, -STACKSIZE + 36); /* mov dword ptr [rbp - 516], eax */
/* prog = array->ptrs[index]; */
- EMIT4_off32(0x48, 0x8D, 0x84, 0xD6, /* lea rax, [rsi + rdx * 8 + offsetof(...)] */
+ EMIT4_off32(0x48, 0x8B, 0x84, 0xD6, /* mov rax, [rsi + rdx * 8 + offsetof(...)] */
offsetof(struct bpf_array, ptrs));
- EMIT3(0x48, 0x8B, 0x00); /* mov rax, qword ptr [rax] */
/* if (prog == NULL)
* goto out;
*/
- EMIT4(0x48, 0x83, 0xF8, 0x00); /* cmp rax, 0 */
+ EMIT3(0x48, 0x85, 0xC0); /* test rax,rax */
#define OFFSET3 10
EMIT2(X86_JE, OFFSET3); /* je out */
label3 = cnt;
diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c
index 2f25a36..dcb2d9d 100644
--- a/arch/x86/platform/efi/efi_64.c
+++ b/arch/x86/platform/efi/efi_64.c
@@ -142,7 +142,7 @@
return 0;
gfp_mask = GFP_KERNEL | __GFP_NOTRACK | __GFP_ZERO;
- efi_pgd = (pgd_t *)__get_free_page(gfp_mask);
+ efi_pgd = (pgd_t *)__get_free_pages(gfp_mask, PGD_ALLOCATION_ORDER);
if (!efi_pgd)
return -ENOMEM;
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index 8f1f7ef..2bea87c 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -444,6 +444,12 @@
~((1 << X86_FEATURE_MTRR) | /* disable MTRR */
(1 << X86_FEATURE_ACC)); /* thermal monitoring */
+ /*
+ * Xen PV would need some work to support PCID: CR3 handling as well
+ * as xen_flush_tlb_others() would need updating.
+ */
+ cpuid_leaf1_ecx_mask &= ~(1 << (X86_FEATURE_PCID % 32)); /* disable PCID */
+
if (!xen_initial_domain())
cpuid_leaf1_edx_mask &=
~((1 << X86_FEATURE_ACPI)); /* disable ACPI */
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index b08ccbb..8ba0af7 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -185,7 +185,8 @@
}
wb_congested = wb_congested_get_create(&q->backing_dev_info,
- blkcg->css.id, GFP_NOWAIT);
+ blkcg->css.id,
+ GFP_NOWAIT | __GFP_NOWARN);
if (!wb_congested) {
ret = -ENOMEM;
goto err_put_css;
@@ -193,7 +194,7 @@
/* allocate */
if (!new_blkg) {
- new_blkg = blkg_alloc(blkcg, q, GFP_NOWAIT);
+ new_blkg = blkg_alloc(blkcg, q, GFP_NOWAIT | __GFP_NOWARN);
if (unlikely(!new_blkg)) {
ret = -ENOMEM;
goto err_put_congested;
@@ -1022,7 +1023,7 @@
}
spin_lock_init(&blkcg->lock);
- INIT_RADIX_TREE(&blkcg->blkg_tree, GFP_NOWAIT);
+ INIT_RADIX_TREE(&blkcg->blkg_tree, GFP_NOWAIT | __GFP_NOWARN);
INIT_HLIST_HEAD(&blkcg->blkg_list);
#ifdef CONFIG_CGROUP_WRITEBACK
INIT_LIST_HEAD(&blkcg->cgwb_list);
@@ -1240,7 +1241,7 @@
if (blkg->pd[pol->plid])
continue;
- pd = pol->pd_alloc_fn(GFP_NOWAIT, q->node);
+ pd = pol->pd_alloc_fn(GFP_NOWAIT | __GFP_NOWARN, q->node);
if (!pd)
swap(pd, pd_prealloc);
if (!pd) {
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 4ac4910..6a90155 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -3868,7 +3868,8 @@
goto out;
}
- cfqq = kmem_cache_alloc_node(cfq_pool, GFP_NOWAIT | __GFP_ZERO,
+ cfqq = kmem_cache_alloc_node(cfq_pool,
+ GFP_NOWAIT | __GFP_ZERO | __GFP_NOWARN,
cfqd->queue->node);
if (!cfqq) {
cfqq = &cfqd->oom_cfqq;
diff --git a/crypto/Kconfig b/crypto/Kconfig
index 84d7148..ab0d93a 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -120,7 +120,7 @@
config CRYPTO_ECDH
tristate "ECDH algorithm"
- select CRYTPO_KPP
+ select CRYPTO_KPP
help
Generic implementation of the ECDH algorithm
diff --git a/crypto/af_alg.c b/crypto/af_alg.c
index f5e18c2..ca50eeb 100644
--- a/crypto/af_alg.c
+++ b/crypto/af_alg.c
@@ -149,7 +149,7 @@
static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
- const u32 forbidden = CRYPTO_ALG_INTERNAL;
+ const u32 allowed = CRYPTO_ALG_KERN_DRIVER_ONLY;
struct sock *sk = sock->sk;
struct alg_sock *ask = alg_sk(sk);
struct sockaddr_alg *sa = (void *)uaddr;
@@ -157,6 +157,10 @@
void *private;
int err;
+ /* If caller uses non-allowed flag, return error. */
+ if ((sa->salg_feat & ~allowed) || (sa->salg_mask & ~allowed))
+ return -EINVAL;
+
if (sock->state == SS_CONNECTED)
return -EINVAL;
@@ -175,9 +179,7 @@
if (IS_ERR(type))
return PTR_ERR(type);
- private = type->bind(sa->salg_name,
- sa->salg_feat & ~forbidden,
- sa->salg_mask & ~forbidden);
+ private = type->bind(sa->salg_name, sa->salg_feat, sa->salg_mask);
if (IS_ERR(private)) {
module_put(type->owner);
return PTR_ERR(private);
diff --git a/crypto/algapi.c b/crypto/algapi.c
index 1fad2a6..5c098ff 100644
--- a/crypto/algapi.c
+++ b/crypto/algapi.c
@@ -167,6 +167,18 @@
spawn->alg = NULL;
spawns = &inst->alg.cra_users;
+
+ /*
+ * We may encounter an unregistered instance here, since
+ * an instance's spawns are set up prior to the instance
+ * being registered. An unregistered instance will have
+ * NULL ->cra_users.next, since ->cra_users isn't
+ * properly initialized until registration. But an
+ * unregistered instance cannot have any users, so treat
+ * it the same as ->cra_users being empty.
+ */
+ if (spawns->next == NULL)
+ break;
}
} while ((spawns = crypto_more_spawns(alg, &stack, &top,
&secondary_spawns)));
diff --git a/crypto/chacha20poly1305.c b/crypto/chacha20poly1305.c
index e899ef5..cb1c3a3 100644
--- a/crypto/chacha20poly1305.c
+++ b/crypto/chacha20poly1305.c
@@ -610,6 +610,11 @@
algt->mask));
if (IS_ERR(poly))
return PTR_ERR(poly);
+ poly_hash = __crypto_hash_alg_common(poly);
+
+ err = -EINVAL;
+ if (poly_hash->digestsize != POLY1305_DIGEST_SIZE)
+ goto out_put_poly;
err = -ENOMEM;
inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
@@ -618,7 +623,6 @@
ctx = aead_instance_ctx(inst);
ctx->saltlen = CHACHAPOLY_IV_SIZE - ivsize;
- poly_hash = __crypto_hash_alg_common(poly);
err = crypto_init_ahash_spawn(&ctx->poly, poly_hash,
aead_crypto_instance(inst));
if (err)
diff --git a/crypto/pcrypt.c b/crypto/pcrypt.c
index ee9cfb9..f8ec3d4 100644
--- a/crypto/pcrypt.c
+++ b/crypto/pcrypt.c
@@ -254,6 +254,14 @@
crypto_free_aead(ctx->child);
}
+static void pcrypt_free(struct aead_instance *inst)
+{
+ struct pcrypt_instance_ctx *ctx = aead_instance_ctx(inst);
+
+ crypto_drop_aead(&ctx->spawn);
+ kfree(inst);
+}
+
static int pcrypt_init_instance(struct crypto_instance *inst,
struct crypto_alg *alg)
{
@@ -319,6 +327,8 @@
inst->alg.encrypt = pcrypt_aead_encrypt;
inst->alg.decrypt = pcrypt_aead_decrypt;
+ inst->free = pcrypt_free;
+
err = aead_register_instance(tmpl, inst);
if (err)
goto out_drop_aead;
@@ -349,14 +359,6 @@
return -EINVAL;
}
-static void pcrypt_free(struct crypto_instance *inst)
-{
- struct pcrypt_instance_ctx *ctx = crypto_instance_ctx(inst);
-
- crypto_drop_aead(&ctx->spawn);
- kfree(inst);
-}
-
static int pcrypt_cpumask_change_notify(struct notifier_block *self,
unsigned long val, void *data)
{
@@ -469,7 +471,6 @@
static struct crypto_template pcrypt_tmpl = {
.name = "pcrypt",
.create = pcrypt_create,
- .free = pcrypt_free,
.module = THIS_MODULE,
};
diff --git a/crypto/sha3_generic.c b/crypto/sha3_generic.c
index 7e8ed96..a68be62 100644
--- a/crypto/sha3_generic.c
+++ b/crypto/sha3_generic.c
@@ -18,6 +18,7 @@
#include <linux/types.h>
#include <crypto/sha3.h>
#include <asm/byteorder.h>
+#include <asm/unaligned.h>
#define KECCAK_ROUNDS 24
@@ -149,7 +150,7 @@
unsigned int i;
for (i = 0; i < sctx->rsizw; i++)
- sctx->st[i] ^= ((u64 *) src)[i];
+ sctx->st[i] ^= get_unaligned_le64(src + 8 * i);
keccakf(sctx->st);
done += sctx->rsiz;
@@ -174,7 +175,7 @@
sctx->buf[sctx->rsiz - 1] |= 0x80;
for (i = 0; i < sctx->rsizw; i++)
- sctx->st[i] ^= ((u64 *) sctx->buf)[i];
+ sctx->st[i] ^= get_unaligned_le64(sctx->buf + 8 * i);
keccakf(sctx->st);
diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c
index 691814d..943702d 100644
--- a/drivers/acpi/acpica/nsutils.c
+++ b/drivers/acpi/acpica/nsutils.c
@@ -594,25 +594,20 @@
void acpi_ns_terminate(void)
{
acpi_status status;
+ union acpi_operand_object *prev;
+ union acpi_operand_object *next;
ACPI_FUNCTION_TRACE(ns_terminate);
-#ifdef ACPI_EXEC_APP
- {
- union acpi_operand_object *prev;
- union acpi_operand_object *next;
+ /* Delete any module-level code blocks */
- /* Delete any module-level code blocks */
-
- next = acpi_gbl_module_code_list;
- while (next) {
- prev = next;
- next = next->method.mutex;
- prev->method.mutex = NULL; /* Clear the Mutex (cheated) field */
- acpi_ut_remove_reference(prev);
- }
+ next = acpi_gbl_module_code_list;
+ while (next) {
+ prev = next;
+ next = next->method.mutex;
+ prev->method.mutex = NULL; /* Clear the Mutex (cheated) field */
+ acpi_ut_remove_reference(prev);
}
-#endif
/*
* Free the entire namespace -- all nodes and all objects
diff --git a/drivers/acpi/device_sysfs.c b/drivers/acpi/device_sysfs.c
index 7b2c48f..201c7ce 100644
--- a/drivers/acpi/device_sysfs.c
+++ b/drivers/acpi/device_sysfs.c
@@ -146,6 +146,10 @@
int count;
struct acpi_hardware_id *id;
+ /* Avoid unnecessarily loading modules for non present devices. */
+ if (!acpi_device_is_present(acpi_dev))
+ return 0;
+
/*
* Since we skip ACPI_DT_NAMESPACE_HID from the modalias below, 0 should
* be returned if ACPI_DT_NAMESPACE_HID is the only ACPI/PNP ID in the
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index 73c9c7f..f06317d 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -99,13 +99,13 @@
return -ENODEV;
/*
- * If the device has a _HID (or _CID) returning a valid ACPI/PNP
- * device ID, it is better to make it look less attractive here, so that
- * the other device with the same _ADR value (that may not have a valid
- * device ID) can be matched going forward. [This means a second spec
- * violation in a row, so whatever we do here is best effort anyway.]
+ * If the device has a _HID returning a valid ACPI/PNP device ID, it is
+ * better to make it look less attractive here, so that the other device
+ * with the same _ADR value (that may not have a valid device ID) can be
+ * matched going forward. [This means a second spec violation in a row,
+ * so whatever we do here is best effort anyway.]
*/
- return sta_present && list_empty(&adev->pnp.ids) ?
+ return sta_present && !adev->pnp.type.platform_id ?
FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE;
}
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 33e363d..aee3952 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4322,6 +4322,7 @@
* https://bugzilla.kernel.org/show_bug.cgi?id=121671
*/
{ "LITEON CX1-JB*-HP", NULL, ATA_HORKAGE_MAX_SEC_1024 },
+ { "LITEON EP1-*", NULL, ATA_HORKAGE_MAX_SEC_1024 },
/* Devices we expect to fail diagnostics */
diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index 10e1b9e..f03cf1d 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -121,6 +121,7 @@
config IMG_ASCII_LCD
tristate "Imagination Technologies ASCII LCD Display"
+ depends on HAS_IOMEM
default y if MIPS_MALTA || MIPS_SEAD3
select SYSCON
help
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index d02e7c0..0651010 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -235,6 +235,9 @@
config GENERIC_CPU_AUTOPROBE
bool
+config GENERIC_CPU_VULNERABILITIES
+ bool
+
config SOC_BUS
bool
diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c
index e9fd32e..70e13cf 100644
--- a/drivers/base/cacheinfo.c
+++ b/drivers/base/cacheinfo.c
@@ -16,6 +16,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/acpi.h>
#include <linux/bitops.h>
#include <linux/cacheinfo.h>
#include <linux/compiler.h>
@@ -104,9 +105,16 @@
struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
struct cacheinfo *this_leaf, *sib_leaf;
unsigned int index;
- int ret;
+ int ret = 0;
- ret = cache_setup_of_node(cpu);
+ if (this_cpu_ci->cpu_map_populated)
+ return 0;
+
+ if (of_have_populated_dt())
+ ret = cache_setup_of_node(cpu);
+ else if (!acpi_disabled)
+ /* No cache property/hierarchy support yet in ACPI */
+ ret = -ENOTSUPP;
if (ret)
return ret;
@@ -203,8 +211,7 @@
*/
ret = cache_shared_cpu_map_setup(cpu);
if (ret) {
- pr_warn("Unable to detect cache hierarchy from DT for CPU %d\n",
- cpu);
+ pr_warn("Unable to detect cache hierarchy for CPU %d\n", cpu);
goto free_ci;
}
return 0;
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index e17ad53..4fe86f7 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -590,10 +590,58 @@
#endif
}
+#ifdef CONFIG_GENERIC_CPU_VULNERABILITIES
+
+ssize_t __weak cpu_show_meltdown(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "Not affected\n");
+}
+
+ssize_t __weak cpu_show_spectre_v1(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "Not affected\n");
+}
+
+ssize_t __weak cpu_show_spectre_v2(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "Not affected\n");
+}
+
+static DEVICE_ATTR(meltdown, 0444, cpu_show_meltdown, NULL);
+static DEVICE_ATTR(spectre_v1, 0444, cpu_show_spectre_v1, NULL);
+static DEVICE_ATTR(spectre_v2, 0444, cpu_show_spectre_v2, NULL);
+
+static struct attribute *cpu_root_vulnerabilities_attrs[] = {
+ &dev_attr_meltdown.attr,
+ &dev_attr_spectre_v1.attr,
+ &dev_attr_spectre_v2.attr,
+ NULL
+};
+
+static const struct attribute_group cpu_root_vulnerabilities_group = {
+ .name = "vulnerabilities",
+ .attrs = cpu_root_vulnerabilities_attrs,
+};
+
+static void __init cpu_register_vulnerabilities(void)
+{
+ if (sysfs_create_group(&cpu_subsys.dev_root->kobj,
+ &cpu_root_vulnerabilities_group))
+ pr_err("Unable to register CPU vulnerabilities\n");
+}
+
+#else
+static inline void cpu_register_vulnerabilities(void) { }
+#endif
+
void __init cpu_dev_init(void)
{
if (subsys_system_register(&cpu_subsys, cpu_root_attr_groups))
panic("Failed to register CPU subsystem");
cpu_dev_register_generic();
+ cpu_register_vulnerabilities();
}
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 24d6cef..402254d 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1558,9 +1558,8 @@
return err;
}
-static void lo_release(struct gendisk *disk, fmode_t mode)
+static void __lo_release(struct loop_device *lo)
{
- struct loop_device *lo = disk->private_data;
int err;
if (atomic_dec_return(&lo->lo_refcnt))
@@ -1586,6 +1585,13 @@
mutex_unlock(&lo->lo_ctl_mutex);
}
+static void lo_release(struct gendisk *disk, fmode_t mode)
+{
+ mutex_lock(&loop_index_mutex);
+ __lo_release(disk->private_data);
+ mutex_unlock(&loop_index_mutex);
+}
+
static const struct block_device_operations lo_fops = {
.owner = THIS_MODULE,
.open = lo_open,
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 7d506cb..4d30da2 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -272,6 +272,7 @@
int result, flags;
struct nbd_request request;
unsigned long size = blk_rq_bytes(req);
+ struct bio *bio;
u32 type;
if (req->cmd_type == REQ_TYPE_DRV_PRIV)
@@ -305,16 +306,20 @@
return -EIO;
}
- if (type == NBD_CMD_WRITE) {
- struct req_iterator iter;
+ if (type != NBD_CMD_WRITE)
+ return 0;
+
+ flags = 0;
+ bio = req->bio;
+ while (bio) {
+ struct bio *next = bio->bi_next;
+ struct bvec_iter iter;
struct bio_vec bvec;
- /*
- * we are really probing at internals to determine
- * whether to set MSG_MORE or not...
- */
- rq_for_each_segment(bvec, req, iter) {
- flags = 0;
- if (!rq_iter_last(bvec, iter))
+
+ bio_for_each_segment(bvec, bio, iter) {
+ bool is_last = !next && bio_iter_last(bvec, iter);
+
+ if (is_last)
flags = MSG_MORE;
dev_dbg(nbd_to_dev(nbd), "request %p: sending %d bytes data\n",
cmd, bvec.bv_len);
@@ -325,7 +330,16 @@
result);
return -EIO;
}
+ /*
+ * The completion might already have come in,
+ * so break for the last one instead of letting
+ * the iterator do it. This prevents use-after-free
+ * of the bio.
+ */
+ if (is_last)
+ break;
}
+ bio = next;
}
return 0;
}
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 24f4b54..e32badd 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -4511,7 +4511,7 @@
segment_size = rbd_obj_bytes(&rbd_dev->header);
blk_queue_max_hw_sectors(q, segment_size / SECTOR_SIZE);
q->limits.max_sectors = queue_max_hw_sectors(q);
- blk_queue_max_segments(q, segment_size / SECTOR_SIZE);
+ blk_queue_max_segments(q, USHRT_MAX);
blk_queue_max_segment_size(q, segment_size);
blk_queue_io_min(q, segment_size);
blk_queue_io_opt(q, segment_size);
diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c
index 795c9d9..2051d92 100644
--- a/drivers/bus/sunxi-rsb.c
+++ b/drivers/bus/sunxi-rsb.c
@@ -178,6 +178,7 @@
.match = sunxi_rsb_device_match,
.probe = sunxi_rsb_device_probe,
.remove = sunxi_rsb_device_remove,
+ .uevent = of_device_uevent_modalias,
};
static void sunxi_rsb_dev_release(struct device *dev)
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 1ea2053..b0d0181 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -607,7 +607,7 @@
config MSM_ADSPRPC
tristate "QTI ADSP RPC driver"
- depends on MSM_GLINK
+ depends on MSM_GLINK || MSM_SMD
help
Provides a communication mechanism that allows for clients to
make remote method invocations across processor boundary to
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 177fb3d..223c9b6 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -26,8 +26,11 @@
#include <linux/msm_ion.h>
#include <soc/qcom/secure_buffer.h>
#include <soc/qcom/glink.h>
+#include <soc/qcom/smd.h>
#include <soc/qcom/subsystem_notif.h>
#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/service-notifier.h>
+#include <soc/qcom/service-locator.h>
#include <linux/scatterlist.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
@@ -58,6 +61,9 @@
#define VMID_ADSP_Q6 6
#define DEBUGFS_SIZE 1024
+#define AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME "audio_pdr_adsprpc"
+#define AUDIO_PDR_ADSP_SERVICE_NAME "avs/audio"
+
#define RPC_TIMEOUT (5 * HZ)
#define BALIGN 128
#define NUM_CHANNELS 4 /* adsp, mdsp, slpi, cdsp*/
@@ -110,6 +116,9 @@
static int fastrpc_glink_open(int cid);
static void fastrpc_glink_close(void *chan, int cid);
+static int fastrpc_audio_pdr_notifier_cb(struct notifier_block *nb,
+ unsigned long code,
+ void *data);
static struct dentry *debugfs_root;
static struct dentry *debugfs_global_file;
@@ -190,7 +199,6 @@
remote_arg_t *lpra;
remote_arg64_t *rpra;
int *fds;
- unsigned int *attrs;
struct fastrpc_mmap **maps;
struct fastrpc_buf *buf;
size_t used;
@@ -199,8 +207,9 @@
struct overlap *overs;
struct overlap **overps;
struct smq_msg msg;
- uint32_t *crc;
unsigned int magic;
+ unsigned int *attrs;
+ uint32_t *crc;
};
struct fastrpc_ctx_lst {
@@ -224,6 +233,16 @@
int used;
};
+struct fastrpc_static_pd {
+ char *spdname;
+ struct notifier_block pdrnb;
+ struct notifier_block get_service_nb;
+ void *pdrhandle;
+ int pdrcount;
+ int prevpdrcount;
+ int ispdup;
+};
+
struct fastrpc_glink_info {
int link_state;
int port_state;
@@ -238,10 +257,12 @@
void *chan;
struct device *dev;
struct fastrpc_session_ctx session[NUM_SESSIONS];
+ struct fastrpc_static_pd spd[NUM_SESSIONS];
struct completion work;
struct completion workport;
struct notifier_block nb;
struct kref kref;
+ int channel;
int sesscount;
int ssrcount;
void *handle;
@@ -269,6 +290,8 @@
struct ion_client *client;
struct device *dev;
unsigned int latency;
+ bool glink;
+ bool legacy;
};
struct fastrpc_mmap {
@@ -334,6 +357,7 @@
int cid;
int ssrcount;
int pd;
+ char *spdname;
int file_close;
struct fastrpc_apps *apps;
struct hlist_head perf;
@@ -342,6 +366,7 @@
struct pm_qos_request pm_qos_req;
int qos_request;
struct mutex map_mutex;
+ struct mutex fl_map_mutex;
};
static struct fastrpc_apps gfa;
@@ -350,18 +375,29 @@
{
.name = "adsprpc-smd",
.subsys = "adsp",
+ .channel = SMD_APPS_QDSP,
.link.link_info.edge = "lpass",
.link.link_info.transport = "smem",
+ .spd = {
+ {
+ .spdname =
+ AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME,
+ .pdrnb.notifier_call =
+ fastrpc_audio_pdr_notifier_cb,
+ }
+ },
},
{
.name = "mdsprpc-smd",
.subsys = "modem",
+ .channel = SMD_APPS_MODEM,
.link.link_info.edge = "mpss",
.link.link_info.transport = "smem",
},
{
.name = "sdsprpc-smd",
.subsys = "slpi",
+ .channel = SMD_APPS_DSPS,
.link.link_info.edge = "dsps",
.link.link_info.transport = "smem",
},
@@ -491,9 +527,7 @@
} else {
struct fastrpc_file *fl = map->fl;
- spin_lock(&fl->hlock);
hlist_add_head(&map->hn, &fl->maps);
- spin_unlock(&fl->hlock);
}
}
@@ -522,7 +556,6 @@
}
spin_unlock(&me->hlock);
} else {
- spin_lock(&fl->hlock);
hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
if (va >= map->va &&
va + len <= map->va + map->len &&
@@ -533,7 +566,6 @@
break;
}
}
- spin_unlock(&fl->hlock);
}
if (match) {
*ppmap = match;
@@ -581,7 +613,6 @@
*ppmap = match;
return 0;
}
- spin_lock(&fl->hlock);
hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
if (map->raddr == va &&
map->raddr + map->len == va + len &&
@@ -591,7 +622,6 @@
break;
}
}
- spin_unlock(&fl->hlock);
if (match) {
*ppmap = match;
return 0;
@@ -619,11 +649,9 @@
if (map->refs > 0)
return;
} else {
- spin_lock(&fl->hlock);
map->refs--;
if (!map->refs)
hlist_del_init(&map->hn);
- spin_unlock(&fl->hlock);
if (map->refs > 0 && !flags)
return;
}
@@ -786,13 +814,24 @@
goto bail;
}
map->phys = sg_dma_address(map->table->sgl);
+
if (sess->smmu.cb) {
map->phys += ((uint64_t)sess->smmu.cb << 32);
map->size = sg_dma_len(map->table->sgl);
} else {
map->size = buf_page_size(len);
}
+
vmid = fl->apps->channel[fl->cid].vmid;
+ if (!sess->smmu.enabled && !vmid) {
+ VERIFY(err, map->phys >= me->range.addr &&
+ map->phys + map->size <=
+ me->range.addr + me->range.size);
+ if (err) {
+ pr_err("adsprpc: mmap fail out of range\n");
+ goto bail;
+ }
+ }
if (vmid) {
int srcVM[1] = {VMID_HLOS};
int destVM[2] = {VMID_HLOS, vmid};
@@ -997,6 +1036,7 @@
struct fastrpc_ioctl_invoke_crc *invokefd,
struct smq_invoke_ctx **po)
{
+ struct fastrpc_apps *me = &gfa;
int err = 0, bufs, size = 0;
struct smq_invoke_ctx *ctx = NULL;
struct fastrpc_ctx_lst *clst = &fl->clst;
@@ -1019,9 +1059,14 @@
ctx->maps = (struct fastrpc_mmap **)(&ctx[1]);
ctx->lpra = (remote_arg_t *)(&ctx->maps[bufs]);
ctx->fds = (int *)(&ctx->lpra[bufs]);
- ctx->attrs = (unsigned int *)(&ctx->fds[bufs]);
- ctx->overs = (struct overlap *)(&ctx->attrs[bufs]);
- ctx->overps = (struct overlap **)(&ctx->overs[bufs]);
+ if (me->legacy) {
+ ctx->overs = (struct overlap *)(&ctx->fds[bufs]);
+ ctx->overps = (struct overlap **)(&ctx->overs[bufs]);
+ } else {
+ ctx->attrs = (unsigned int *)(&ctx->fds[bufs]);
+ ctx->overs = (struct overlap *)(&ctx->attrs[bufs]);
+ ctx->overps = (struct overlap **)(&ctx->overs[bufs]);
+ }
K_COPY_FROM_USER(err, kernel, (void *)ctx->lpra, invoke->pra,
bufs * sizeof(*ctx->lpra));
@@ -1084,8 +1129,11 @@
spin_lock(&ctx->fl->hlock);
hlist_del_init(&ctx->hn);
spin_unlock(&ctx->fl->hlock);
+ mutex_lock(&ctx->fl->fl_map_mutex);
for (i = 0; i < nbufs; ++i)
fastrpc_mmap_free(ctx->maps[i], 0);
+
+ mutex_unlock(&ctx->fl->fl_map_mutex);
fastrpc_buf_free(ctx->buf, 1);
ctx->magic = 0;
kfree(ctx);
@@ -1127,6 +1175,21 @@
spin_unlock(&me->hlock);
}
+
+static void fastrpc_notify_pdr_drivers(struct fastrpc_apps *me, char *spdname)
+{
+ struct fastrpc_file *fl;
+ struct hlist_node *n;
+
+ spin_lock(&me->hlock);
+ hlist_for_each_entry_safe(fl, n, &me->drivers, hn) {
+ if (fl->spdname && !strcmp(spdname, fl->spdname))
+ fastrpc_notify_users(fl);
+ }
+ spin_unlock(&me->hlock);
+
+}
+
static void context_list_ctor(struct fastrpc_ctx_lst *me)
{
INIT_HLIST_HEAD(&me->interrupted);
@@ -1187,6 +1250,7 @@
static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx)
{
+ struct fastrpc_apps *me = &gfa;
remote_arg64_t *rpra;
remote_arg_t *lpra = ctx->lpra;
struct smq_invoke_buf *list;
@@ -1215,23 +1279,40 @@
uintptr_t buf = (uintptr_t)lpra[i].buf.pv;
size_t len = lpra[i].buf.len;
- if (ctx->fds[i] && (ctx->fds[i] != -1))
+ mutex_lock(&ctx->fl->fl_map_mutex);
+ if (ctx->fds[i] && (ctx->fds[i] != -1)) {
+ unsigned int attrs = 0;
+
+ if (ctx->attrs)
+ attrs = ctx->attrs[i];
+
fastrpc_mmap_create(ctx->fl, ctx->fds[i],
- ctx->attrs[i], buf, len,
+ attrs, buf, len,
mflags, &ctx->maps[i]);
+ }
+ mutex_unlock(&ctx->fl->fl_map_mutex);
ipage += 1;
}
PERF_END);
handles = REMOTE_SCALARS_INHANDLES(sc) + REMOTE_SCALARS_OUTHANDLES(sc);
+ mutex_lock(&ctx->fl->fl_map_mutex);
for (i = bufs; i < bufs + handles; i++) {
VERIFY(err, !fastrpc_mmap_create(ctx->fl, ctx->fds[i],
FASTRPC_ATTR_NOVA, 0, 0, 0, &ctx->maps[i]));
- if (err)
+ if (err) {
+ mutex_unlock(&ctx->fl->fl_map_mutex);
goto bail;
+ }
ipage += 1;
}
- metalen = copylen = (size_t)&ipage[0] + (sizeof(uint64_t) * M_FDLIST) +
- (sizeof(uint32_t) * M_CRCLIST);
+ mutex_unlock(&ctx->fl->fl_map_mutex);
+ if (!me->legacy) {
+ metalen = copylen = (size_t)&ipage[0] +
+ (sizeof(uint64_t) * M_FDLIST) +
+ (sizeof(uint32_t) * M_CRCLIST);
+ } else {
+ metalen = copylen = (size_t)&ipage[0];
+ }
/* calculate len requreed for copying */
for (oix = 0; oix < inbufs + outbufs; ++oix) {
@@ -1327,16 +1408,18 @@
pages[i].addr = map->phys;
pages[i].size = map->size;
}
- fdlist = (uint64_t *)&pages[bufs + handles];
- for (i = 0; i < M_FDLIST; i++)
- fdlist[i] = 0;
- crclist = (uint32_t *)&fdlist[M_FDLIST];
- memset(crclist, 0, sizeof(uint32_t)*M_CRCLIST);
+ if (!me->legacy) {
+ fdlist = (uint64_t *)&pages[bufs + handles];
+ for (i = 0; i < M_FDLIST; i++)
+ fdlist[i] = 0;
+ crclist = (uint32_t *)&fdlist[M_FDLIST];
+ memset(crclist, 0, sizeof(uint32_t)*M_CRCLIST);
+ }
/* copy non ion buffers */
PERF(ctx->fl->profile, GET_COUNTER(perf_counter, PERF_COPY),
rlen = copylen - metalen;
- for (oix = 0; oix < inbufs + outbufs; ++oix) {
+ for (oix = 0; rpra && oix < inbufs + outbufs; ++oix) {
int i = ctx->overps[oix]->raix;
struct fastrpc_mmap *map = ctx->maps[i];
size_t mlen;
@@ -1387,7 +1470,7 @@
if (map && (map->attr & FASTRPC_ATTR_COHERENT))
continue;
- if (rpra[i].buf.len && ctx->overps[oix]->mstart)
+ if (rpra && rpra[i].buf.len && ctx->overps[oix]->mstart)
dmac_flush_range(uint64_to_ptr(rpra[i].buf.pv),
uint64_to_ptr(rpra[i].buf.pv + rpra[i].buf.len));
}
@@ -1410,11 +1493,12 @@
static int put_args(uint32_t kernel, struct smq_invoke_ctx *ctx,
remote_arg_t *upra)
{
+ struct fastrpc_apps *me = &gfa;
uint32_t sc = ctx->sc;
struct smq_invoke_buf *list;
struct smq_phy_page *pages;
struct fastrpc_mmap *mmap;
- uint64_t *fdlist;
+ uint64_t *fdlist = NULL;
uint32_t *crclist = NULL;
remote_arg64_t *rpra = ctx->rpra;
@@ -1426,8 +1510,10 @@
handles = REMOTE_SCALARS_INHANDLES(sc) + REMOTE_SCALARS_OUTHANDLES(sc);
list = smq_invoke_buf_start(ctx->rpra, sc);
pages = smq_phy_page_start(sc, list);
- fdlist = (uint64_t *)(pages + inbufs + outbufs + handles);
- crclist = (uint32_t *)(fdlist + M_FDLIST);
+ if (!me->legacy) {
+ fdlist = (uint64_t *)(pages + inbufs + outbufs + handles);
+ crclist = (uint32_t *)(fdlist + M_FDLIST);
+ }
for (i = inbufs; i < inbufs + outbufs; ++i) {
if (!ctx->maps[i]) {
@@ -1438,11 +1524,14 @@
if (err)
goto bail;
} else {
+ mutex_lock(&ctx->fl->fl_map_mutex);
fastrpc_mmap_free(ctx->maps[i], 0);
+ mutex_unlock(&ctx->fl->fl_map_mutex);
ctx->maps[i] = NULL;
}
}
- if (inbufs + outbufs + handles) {
+ mutex_lock(&ctx->fl->fl_map_mutex);
+ if (fdlist && (inbufs + outbufs + handles)) {
for (i = 0; i < M_FDLIST; i++) {
if (!fdlist[i])
break;
@@ -1451,6 +1540,7 @@
fastrpc_mmap_free(mmap, 0);
}
}
+ mutex_unlock(&ctx->fl->fl_map_mutex);
if (ctx->crc && crclist && rpra)
K_COPY_TO_USER(err, kernel, ctx->crc,
crclist, M_CRCLIST*sizeof(uint32_t));
@@ -1541,7 +1631,7 @@
struct smq_msg *msg = &ctx->msg;
struct fastrpc_file *fl = ctx->fl;
struct fastrpc_channel_ctx *channel_ctx = &fl->apps->channel[fl->cid];
- int err = 0;
+ int err = 0, len;
VERIFY(err, NULL != channel_ctx->chan);
if (err)
@@ -1557,22 +1647,73 @@
msg->invoke.header.sc = ctx->sc;
msg->invoke.page.addr = ctx->buf ? ctx->buf->phys : 0;
msg->invoke.page.size = buf_page_size(ctx->used);
-
- if (fl->ssrcount != channel_ctx->ssrcount) {
- err = -ECONNRESET;
- goto bail;
- }
- VERIFY(err, channel_ctx->link.port_state ==
+ if (fl->apps->glink) {
+ if (fl->ssrcount != channel_ctx->ssrcount) {
+ err = -ECONNRESET;
+ goto bail;
+ }
+ VERIFY(err, channel_ctx->link.port_state ==
FASTRPC_LINK_CONNECTED);
- if (err)
- goto bail;
- err = glink_tx(channel_ctx->chan,
- (void *)&fl->apps->channel[fl->cid], msg, sizeof(*msg),
- GLINK_TX_REQ_INTENT);
+ if (err)
+ goto bail;
+ err = glink_tx(channel_ctx->chan,
+ (void *)&fl->apps->channel[fl->cid], msg, sizeof(*msg),
+ GLINK_TX_REQ_INTENT);
+ } else {
+ spin_lock(&fl->apps->hlock);
+ len = smd_write((smd_channel_t *)
+ channel_ctx->chan,
+ msg, sizeof(*msg));
+ spin_unlock(&fl->apps->hlock);
+ VERIFY(err, len == sizeof(*msg));
+ }
bail:
return err;
}
+static void fastrpc_smd_read_handler(int cid)
+{
+ struct fastrpc_apps *me = &gfa;
+ struct smq_invoke_rsp rsp = {0};
+ struct smq_invoke_ctx *ctx;
+ int ret = 0, err = 0;
+
+ do {
+ ret = smd_read_from_cb(me->channel[cid].chan, &rsp,
+ sizeof(rsp));
+ if (ret != sizeof(rsp))
+ break;
+ ctx = (struct smq_invoke_ctx *)(uint64_to_ptr(rsp.ctx & ~1));
+ VERIFY(err, (ctx && ctx->magic == FASTRPC_CTX_MAGIC));
+ if (err)
+ goto bail;
+ context_notify_user(uint64_to_ptr(rsp.ctx & ~1), rsp.retval);
+ } while (ret == sizeof(rsp));
+bail:
+ if (err)
+ pr_err("adsprpc: invalid response or context\n");
+
+}
+
+static void smd_event_handler(void *priv, unsigned int event)
+{
+ struct fastrpc_apps *me = &gfa;
+ int cid = (int)(uintptr_t)priv;
+
+ switch (event) {
+ case SMD_EVENT_OPEN:
+ complete(&me->channel[cid].workport);
+ break;
+ case SMD_EVENT_CLOSE:
+ fastrpc_notify_drivers(me, cid);
+ break;
+ case SMD_EVENT_DATA:
+ fastrpc_smd_read_handler(cid);
+ break;
+ }
+}
+
+
static void fastrpc_init(struct fastrpc_apps *me)
{
int i;
@@ -1700,7 +1841,28 @@
return err;
}
+static int fastrpc_get_adsp_session(char *name, int *session)
+{
+ struct fastrpc_apps *me = &gfa;
+ int err = 0, i;
+
+ for (i = 0; i < NUM_SESSIONS; i++) {
+ if (!me->channel[0].spd[i].spdname)
+ continue;
+ if (!strcmp(name, me->channel[0].spd[i].spdname))
+ break;
+ }
+ VERIFY(err, i < NUM_SESSIONS);
+ if (err)
+ goto bail;
+ *session = i;
+bail:
+ return err;
+}
+
+static int fastrpc_mmap_remove_pdr(struct fastrpc_file *fl);
static int fastrpc_channel_open(struct fastrpc_file *fl);
+static int fastrpc_mmap_remove_ssr(struct fastrpc_file *fl);
static int fastrpc_init_process(struct fastrpc_file *fl,
struct fastrpc_ioctl_init_attrs *uproc)
{
@@ -1755,8 +1917,10 @@
if (err)
goto bail;
if (init->filelen) {
+ mutex_lock(&fl->fl_map_mutex);
VERIFY(err, !fastrpc_mmap_create(fl, init->filefd, 0,
init->file, init->filelen, mflags, &file));
+ mutex_unlock(&fl->fl_map_mutex);
if (err)
goto bail;
}
@@ -1765,8 +1929,10 @@
init->memlen));
if (err)
goto bail;
+ mutex_lock(&fl->fl_map_mutex);
VERIFY(err, !fastrpc_mmap_create(fl, init->memfd, 0,
init->mem, init->memlen, mflags, &mem));
+ mutex_unlock(&fl->fl_map_mutex);
if (err)
goto bail;
inbuf.pageslen = 1;
@@ -1836,11 +2002,19 @@
inbuf.pgid = current->tgid;
inbuf.namelen = init->filelen;
inbuf.pageslen = 0;
+
+ if (!strcmp(proc_name, "audiopd")) {
+ fl->spdname = AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME;
+ VERIFY(err, !fastrpc_mmap_remove_pdr(fl));
+ }
+
if (!me->staticpd_flags) {
inbuf.pageslen = 1;
+ mutex_lock(&fl->fl_map_mutex);
VERIFY(err, !fastrpc_mmap_create(fl, -1, 0, init->mem,
init->memlen, ADSP_MMAP_REMOTE_HEAP_ADDR,
&mem));
+ mutex_unlock(&fl->fl_map_mutex);
if (err)
goto bail;
phys = mem->phys;
@@ -1897,10 +2071,15 @@
me->channel[fl->cid].rhvm.vmid,
me->channel[fl->cid].rhvm.vmcount,
hlosvm, hlosvmperm, 1);
+ mutex_lock(&fl->fl_map_mutex);
fastrpc_mmap_free(mem, 0);
+ mutex_unlock(&fl->fl_map_mutex);
}
- if (file)
+ if (file) {
+ mutex_lock(&fl->fl_map_mutex);
fastrpc_mmap_free(file, 0);
+ mutex_unlock(&fl->fl_map_mutex);
+ }
return err;
}
@@ -2140,6 +2319,33 @@
return err;
}
+static int fastrpc_mmap_remove_pdr(struct fastrpc_file *fl)
+{
+ struct fastrpc_apps *me = &gfa;
+ int session = 0, err = 0;
+
+ VERIFY(err, !fastrpc_get_adsp_session(
+ AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME, &session));
+ if (err)
+ goto bail;
+ if (me->channel[fl->cid].spd[session].pdrcount !=
+ me->channel[fl->cid].spd[session].prevpdrcount) {
+ if (fastrpc_mmap_remove_ssr(fl))
+ pr_err("ADSPRPC: SSR: Failed to unmap remote heap\n");
+ me->channel[fl->cid].spd[session].prevpdrcount =
+ me->channel[fl->cid].spd[session].pdrcount;
+ }
+ if (!me->channel[fl->cid].spd[session].ispdup) {
+ VERIFY(err, 0);
+ if (err) {
+ err = -ENOTCONN;
+ goto bail;
+ }
+ }
+bail:
+ return err;
+}
+
static int fastrpc_mmap_remove(struct fastrpc_file *fl, uintptr_t va,
size_t len, struct fastrpc_mmap **ppmap);
@@ -2152,16 +2358,23 @@
struct fastrpc_mmap *map = NULL;
mutex_lock(&fl->map_mutex);
+ mutex_lock(&fl->fl_map_mutex);
VERIFY(err, !fastrpc_mmap_remove(fl, ud->vaddrout, ud->size, &map));
+ mutex_unlock(&fl->fl_map_mutex);
if (err)
goto bail;
VERIFY(err, !fastrpc_munmap_on_dsp(fl, map));
if (err)
goto bail;
+ mutex_lock(&fl->fl_map_mutex);
fastrpc_mmap_free(map, 0);
+ mutex_unlock(&fl->fl_map_mutex);
bail:
- if (err && map)
+ if (err && map) {
+ mutex_lock(&fl->fl_map_mutex);
fastrpc_mmap_add(map);
+ mutex_unlock(&fl->fl_map_mutex);
+ }
mutex_unlock(&fl->map_mutex);
return err;
}
@@ -2174,16 +2387,18 @@
VERIFY(err, (fl && ud));
if (err)
goto bail;
-
+ mutex_lock(&fl->fl_map_mutex);
if (!fastrpc_mmap_find(fl, ud->fd, ud->va, ud->len, 0, 0, &map)) {
pr_err("mapping not found to unamp %x va %llx %x\n",
ud->fd, (unsigned long long)ud->va,
(unsigned int)ud->len);
err = -1;
+ mutex_unlock(&fl->fl_map_mutex);
goto bail;
}
if (map)
- fastrpc_mmap_free(map, 0);
+ fastrpc_mmap_free(map, 0);
+ mutex_unlock(&fl->fl_map_mutex);
bail:
return err;
}
@@ -2197,14 +2412,17 @@
int err = 0;
mutex_lock(&fl->map_mutex);
+ mutex_lock(&fl->fl_map_mutex);
if (!fastrpc_mmap_find(fl, ud->fd, (uintptr_t)ud->vaddrin,
- ud->size, ud->flags, 1, &map)){
+ ud->size, ud->flags, 1, &map)) {
+ mutex_unlock(&fl->fl_map_mutex);
mutex_unlock(&fl->map_mutex);
return 0;
}
VERIFY(err, !fastrpc_mmap_create(fl, ud->fd, 0,
(uintptr_t)ud->vaddrin, ud->size,
ud->flags, &map));
+ mutex_unlock(&fl->fl_map_mutex);
if (err)
goto bail;
VERIFY(err, 0 == fastrpc_mmap_on_dsp(fl, ud->flags, map));
@@ -2212,8 +2430,11 @@
goto bail;
ud->vaddrout = map->raddr;
bail:
- if (err && map)
+ if (err && map) {
+ mutex_lock(&fl->fl_map_mutex);
fastrpc_mmap_free(map, 0);
+ mutex_unlock(&fl->fl_map_mutex);
+ }
mutex_unlock(&fl->map_mutex);
return err;
}
@@ -2226,10 +2447,11 @@
ctx = container_of(kref, struct fastrpc_channel_ctx, kref);
cid = ctx - &gcinfo[0];
- fastrpc_glink_close(ctx->chan, cid);
+ if (!me->glink)
+ smd_close(ctx->chan);
+ else
+ fastrpc_glink_close(ctx->chan, cid);
ctx->chan = NULL;
- glink_unregister_link_state_cb(ctx->link.link_notify_handle);
- ctx->link.link_notify_handle = NULL;
mutex_unlock(&me->smd_mutex);
pr_info("'closed /dev/%s c %d %d'\n", gcinfo[cid].name,
MAJOR(me->dev_no), cid);
@@ -2378,9 +2600,11 @@
spin_unlock(&fl->hlock);
fastrpc_context_list_dtor(fl);
fastrpc_buf_list_free(fl);
+ mutex_lock(&fl->fl_map_mutex);
hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
fastrpc_mmap_free(map, 1);
}
+ mutex_unlock(&fl->fl_map_mutex);
if (fl->ssrcount == fl->apps->channel[cid].ssrcount)
kref_put_mutex(&fl->apps->channel[cid].kref,
fastrpc_channel_close, &fl->apps->smd_mutex);
@@ -2403,6 +2627,7 @@
} while (fperf);
mutex_unlock(&fl->perf_mutex);
mutex_destroy(&fl->perf_mutex);
+ mutex_destroy(&fl->fl_map_mutex);
kfree(fl);
return 0;
}
@@ -2675,13 +2900,18 @@
fl->ssrcount = me->channel[cid].ssrcount;
if ((kref_get_unless_zero(&me->channel[cid].kref) == 0) ||
(me->channel[cid].chan == NULL)) {
- VERIFY(err, 0 == fastrpc_glink_register(cid, me));
- if (err)
- goto bail;
- VERIFY(err, 0 == fastrpc_glink_open(cid));
- if (err)
- goto bail;
-
+ if (me->glink) {
+ VERIFY(err, 0 == fastrpc_glink_register(cid, me));
+ if (err)
+ goto bail;
+ VERIFY(err, 0 == fastrpc_glink_open(cid));
+ } else {
+ VERIFY(err, !smd_named_open_on_edge(FASTRPC_SMD_GUID,
+ gcinfo[cid].channel,
+ (smd_channel_t **)&me->channel[cid].chan,
+ (void *)(uintptr_t)cid,
+ smd_event_handler));
+ }
VERIFY(err,
wait_for_completion_timeout(&me->channel[cid].workport,
RPC_TIMEOUT));
@@ -2692,13 +2922,15 @@
kref_init(&me->channel[cid].kref);
pr_info("'opened /dev/%s c %d %d'\n", gcinfo[cid].name,
MAJOR(me->dev_no), cid);
- err = glink_queue_rx_intent(me->channel[cid].chan, NULL,
- FASTRPC_GLINK_INTENT_LEN);
- err |= glink_queue_rx_intent(me->channel[cid].chan, NULL,
- FASTRPC_GLINK_INTENT_LEN);
- if (err)
- pr_warn("adsprpc: initial intent fail for %d err %d\n",
- cid, err);
+ if (me->glink) {
+ err = glink_queue_rx_intent(me->channel[cid].chan, NULL,
+ FASTRPC_GLINK_INTENT_LEN);
+ err |= glink_queue_rx_intent(me->channel[cid].chan,
+ NULL, FASTRPC_GLINK_INTENT_LEN);
+ if (err)
+ pr_warn("adsprpc: initial intent fail for %d err %d\n",
+ cid, err);
+ }
if (cid == 0 && me->channel[cid].ssrcount !=
me->channel[cid].prevssrcount) {
if (fastrpc_mmap_remove_ssr(fl))
@@ -2738,10 +2970,10 @@
fl->cid = -1;
if (debugfs_file != NULL)
fl->debugfs_file = debugfs_file;
- memset(&fl->perf, 0, sizeof(fl->perf));
fl->qos_request = 0;
filp->private_data = fl;
mutex_init(&fl->map_mutex);
+ mutex_init(&fl->fl_map_mutex);
spin_lock(&me->hlock);
hlist_add_head(&fl->hn, &me->drivers);
spin_unlock(&me->hlock);
@@ -3020,7 +3252,10 @@
ctx->ssrcount++;
ctx->issubsystemup = 0;
if (ctx->chan) {
- fastrpc_glink_close(ctx->chan, cid);
+ if (me->glink)
+ fastrpc_glink_close(ctx->chan, cid);
+ else
+ smd_close(ctx->chan);
ctx->chan = NULL;
pr_info("'restart notifier: closed /dev/%s c %d %d'\n",
gcinfo[cid].name, MAJOR(me->dev_no), cid);
@@ -3041,6 +3276,64 @@
return NOTIFY_DONE;
}
+static int fastrpc_audio_pdr_notifier_cb(struct notifier_block *pdrnb,
+ unsigned long code,
+ void *data)
+{
+ struct fastrpc_apps *me = &gfa;
+ struct fastrpc_static_pd *spd;
+ struct notif_data *notifdata = data;
+
+ spd = container_of(pdrnb, struct fastrpc_static_pd, pdrnb);
+ if (code == SERVREG_NOTIF_SERVICE_STATE_DOWN_V01) {
+ mutex_lock(&me->smd_mutex);
+ spd->pdrcount++;
+ spd->ispdup = 0;
+ pr_info("ADSPRPC: Audio PDR notifier %d %s\n",
+ MAJOR(me->dev_no), spd->spdname);
+ mutex_unlock(&me->smd_mutex);
+ if (!strcmp(spd->spdname,
+ AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME))
+ me->staticpd_flags = 0;
+ fastrpc_notify_pdr_drivers(me, spd->spdname);
+ } else if (code == SUBSYS_RAMDUMP_NOTIFICATION) {
+ if (me->channel[0].remoteheap_ramdump_dev &&
+ notifdata->enable_ramdump) {
+ me->channel[0].ramdumpenabled = 1;
+ }
+ } else if (code == SERVREG_NOTIF_SERVICE_STATE_UP_V01) {
+ spd->ispdup = 1;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int fastrpc_get_service_location_notify(struct notifier_block *nb,
+ unsigned long opcode, void *data)
+{
+ struct fastrpc_static_pd *spd;
+ struct pd_qmi_client_data *pdr = data;
+ int curr_state = 0;
+
+ spd = container_of(nb, struct fastrpc_static_pd, get_service_nb);
+ if (opcode == LOCATOR_DOWN) {
+ pr_err("ADSPRPC: Audio PD restart notifier locator down\n");
+ return NOTIFY_DONE;
+ }
+
+ if (pdr->total_domains == 1) {
+ spd->pdrhandle = service_notif_register_notifier(
+ pdr->domain_list[0].name,
+ pdr->domain_list[0].instance_id,
+ &spd->pdrnb, &curr_state);
+ if (IS_ERR(spd->pdrhandle))
+ pr_err("ADSPRPC: Unable to register notifier\n");
+ } else
+ pr_err("ADSPRPC: Service returned invalid domains\n");
+
+ return NOTIFY_DONE;
+}
+
static const struct file_operations fops = {
.open = fastrpc_device_open,
.release = fastrpc_device_release,
@@ -3052,6 +3345,8 @@
{ .compatible = "qcom,msm-fastrpc-adsp", },
{ .compatible = "qcom,msm-fastrpc-compute", },
{ .compatible = "qcom,msm-fastrpc-compute-cb", },
+ { .compatible = "qcom,msm-fastrpc-legacy-compute", },
+ { .compatible = "qcom,msm-fastrpc-legacy-compute-cb", },
{ .compatible = "qcom,msm-adsprpc-mem-region", },
{}
};
@@ -3120,6 +3415,85 @@
return err;
}
+static int fastrpc_cb_legacy_probe(struct device *dev)
+{
+ struct fastrpc_apps *me = &gfa;
+ struct fastrpc_channel_ctx *chan;
+ struct fastrpc_session_ctx *first_sess = NULL, *sess = NULL;
+ const char *name;
+ unsigned int *sids = NULL, sids_size = 0;
+ int err = 0, ret = 0, i;
+
+ unsigned int start = 0x80000000;
+
+ VERIFY(err, NULL != (name = of_get_property(dev->of_node,
+ "label", NULL)));
+ if (err)
+ goto bail;
+
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ if (!gcinfo[i].name)
+ continue;
+ if (!strcmp(name, gcinfo[i].name))
+ break;
+ }
+ VERIFY(err, i < NUM_CHANNELS);
+ if (err)
+ goto bail;
+
+ chan = &gcinfo[i];
+ VERIFY(err, chan->sesscount < NUM_SESSIONS);
+ if (err)
+ goto bail;
+
+ first_sess = &chan->session[chan->sesscount];
+
+ VERIFY(err, NULL != of_get_property(dev->of_node,
+ "sids", &sids_size));
+ if (err)
+ goto bail;
+
+ VERIFY(err, NULL != (sids = kzalloc(sids_size, GFP_KERNEL)));
+ if (err)
+ goto bail;
+ ret = of_property_read_u32_array(dev->of_node, "sids", sids,
+ sids_size/sizeof(unsigned int));
+ if (ret)
+ goto bail;
+
+ VERIFY(err, !IS_ERR_OR_NULL(first_sess->smmu.mapping =
+ arm_iommu_create_mapping(&platform_bus_type,
+ start, 0x78000000)));
+ if (err)
+ goto bail;
+
+ VERIFY(err, !arm_iommu_attach_device(dev, first_sess->smmu.mapping));
+ if (err)
+ goto bail;
+
+
+ for (i = 0; i < sids_size/sizeof(unsigned int); i++) {
+ VERIFY(err, chan->sesscount < NUM_SESSIONS);
+ if (err)
+ goto bail;
+ sess = &chan->session[chan->sesscount];
+ sess->smmu.cb = sids[i];
+ sess->smmu.dev = dev;
+ sess->smmu.mapping = first_sess->smmu.mapping;
+ sess->smmu.enabled = 1;
+ sess->used = 0;
+ sess->smmu.coherent = false;
+ sess->smmu.secure = false;
+ chan->sesscount++;
+ }
+ me->legacy = 1;
+bail:
+ kfree(sids);
+ return err;
+}
+
+
+
static void init_secure_vmid_list(struct device *dev, char *prop_name,
struct secure_vm *destvm)
{
@@ -3170,6 +3544,7 @@
struct platform_device *ion_pdev;
struct cma *cma;
uint32_t val;
+ int ret = 0;
if (of_device_is_compatible(dev->of_node,
@@ -3186,6 +3561,16 @@
return fastrpc_cb_probe(dev);
if (of_device_is_compatible(dev->of_node,
+ "qcom,msm-fastrpc-legacy-compute")) {
+ me->glink = false;
+ }
+
+ if (of_device_is_compatible(dev->of_node,
+ "qcom,msm-fastrpc-legacy-compute-cb")){
+ return fastrpc_cb_legacy_probe(dev);
+ }
+
+ if (of_device_is_compatible(dev->of_node,
"qcom,msm-adsprpc-mem-region")) {
me->dev = dev;
range.addr = 0;
@@ -3207,7 +3592,8 @@
break;
}
}
- if (range.addr) {
+ if (range.addr && !of_property_read_bool(dev->of_node,
+ "restrict-access")) {
int srcVM[1] = {VMID_HLOS};
int destVM[4] = {VMID_HLOS, VMID_MSS_MSA, VMID_SSC_Q6,
VMID_ADSP_Q6};
@@ -3221,10 +3607,31 @@
srcVM, 1, destVM, destVMperm, 4));
if (err)
goto bail;
+ me->range.addr = range.addr;
+ me->range.size = range.size;
}
return 0;
}
+ if (of_property_read_bool(dev->of_node,
+ "qcom,fastrpc-adsp-audio-pdr")) {
+ int session;
+ VERIFY(err, !fastrpc_get_adsp_session(
+ AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME, &session));
+ if (err)
+ goto spdbail;
+ me->channel[0].spd[session].get_service_nb.notifier_call =
+ fastrpc_get_service_location_notify;
+ ret = get_service_location(
+ AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME,
+ AUDIO_PDR_ADSP_SERVICE_NAME,
+ &me->channel[0].spd[session].get_service_nb);
+ if (ret)
+ pr_err("ADSPRPC: Get service location failed: %d\n",
+ ret);
+ }
+spdbail:
+ err = 0;
VERIFY(err, !of_platform_populate(pdev->dev.of_node,
fastrpc_match_table,
NULL, &pdev->dev));
@@ -3281,6 +3688,7 @@
fastrpc_init(me);
me->dev = NULL;
+ me->glink = true;
VERIFY(err, 0 == platform_driver_register(&fastrpc_driver));
if (err)
goto register_bail;
diff --git a/drivers/char/diag/Kconfig b/drivers/char/diag/Kconfig
index e309241..93c164b 100644
--- a/drivers/char/diag/Kconfig
+++ b/drivers/char/diag/Kconfig
@@ -34,4 +34,11 @@
become available, this bridge driver enables DIAG traffic over MHI
and SMUX.
+config DIAG_USES_SMD
+ bool "Enable diag internal interface over SMD"
+ depends on DIAG_CHAR && MSM_SMD
+ help
+ Diag over SMD enables exchanging diagnostic information between
+ application processor and peripherals over SDM.
+
endmenu
diff --git a/drivers/char/diag/Makefile b/drivers/char/diag/Makefile
index b61aae8..897375e 100644
--- a/drivers/char/diag/Makefile
+++ b/drivers/char/diag/Makefile
@@ -3,4 +3,5 @@
obj-$(CONFIG_USB_QCOM_DIAG_BRIDGE) += diagfwd_hsic.o
obj-$(CONFIG_USB_QCOM_DIAG_BRIDGE) += diagfwd_smux.o
obj-$(CONFIG_MSM_MHI) += diagfwd_mhi.o
+obj-$(CONFIG_DIAG_USES_SMD) += diagfwd_smd.o
diagchar-objs := diagchar_core.o diagchar_hdlc.o diagfwd.o diagfwd_glink.o diagfwd_peripheral.o diagfwd_socket.o diag_mux.o diag_memorydevice.o diag_usb.o diagmem.o diagfwd_cntl.o diag_dci.o diag_masks.o diag_debugfs.o
diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c
index 0a3faba..a7f29e6 100644
--- a/drivers/char/diag/diag_debugfs.c
+++ b/drivers/char/diag/diag_debugfs.c
@@ -32,6 +32,7 @@
#include "diag_dci.h"
#include "diag_usb.h"
#include "diagfwd_peripheral.h"
+#include "diagfwd_smd.h"
#include "diagfwd_socket.h"
#include "diagfwd_glink.h"
#include "diag_debugfs.h"
@@ -42,6 +43,7 @@
static int diag_dbgfs_table_index;
static int diag_dbgfs_mempool_index;
static int diag_dbgfs_usbinfo_index;
+static int diag_dbgfs_smdinfo_index;
static int diag_dbgfs_socketinfo_index;
static int diag_dbgfs_glinkinfo_index;
static int diag_dbgfs_hsicinfo_index;
@@ -479,6 +481,112 @@
return ret;
}
+#ifdef CONFIG_DIAG_USES_SMD
+static ssize_t diag_dbgfs_read_smdinfo(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char *buf = NULL;
+ int ret = 0;
+ int i = 0;
+ int j = 0;
+ unsigned int buf_size;
+ unsigned int bytes_remaining = 0;
+ unsigned int bytes_written = 0;
+ unsigned int bytes_in_buffer = 0;
+ struct diag_smd_info *smd_info = NULL;
+ struct diagfwd_info *fwd_ctxt = NULL;
+
+ if (diag_dbgfs_smdinfo_index >= NUM_PERIPHERALS) {
+ /* Done. Reset to prepare for future requests */
+ diag_dbgfs_smdinfo_index = 0;
+ return 0;
+ }
+
+ buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf_size = DEBUG_BUF_SIZE;
+ bytes_remaining = buf_size;
+ for (i = 0; i < NUM_TYPES; i++) {
+ for (j = 0; j < NUM_PERIPHERALS; j++) {
+ switch (i) {
+ case TYPE_DATA:
+ smd_info = &smd_data[j];
+ break;
+ case TYPE_CNTL:
+ smd_info = &smd_cntl[j];
+ break;
+ case TYPE_DCI:
+ smd_info = &smd_dci[j];
+ break;
+ case TYPE_CMD:
+ smd_info = &smd_cmd[j];
+ break;
+ case TYPE_DCI_CMD:
+ smd_info = &smd_dci_cmd[j];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ fwd_ctxt = (struct diagfwd_info *)(smd_info->fwd_ctxt);
+
+ bytes_written = scnprintf(buf+bytes_in_buffer,
+ bytes_remaining,
+ "name\t\t:\t%s\n"
+ "hdl\t\t:\t%pK\n"
+ "inited\t\t:\t%d\n"
+ "opened\t\t:\t%d\n"
+ "diag_state\t:\t%d\n"
+ "fifo size\t:\t%d\n"
+ "open pending\t:\t%d\n"
+ "close pending\t:\t%d\n"
+ "read pending\t:\t%d\n"
+ "buf_1 busy\t:\t%d\n"
+ "buf_2 busy\t:\t%d\n"
+ "bytes read\t:\t%lu\n"
+ "bytes written\t:\t%lu\n"
+ "fwd inited\t:\t%d\n"
+ "fwd opened\t:\t%d\n"
+ "fwd ch_open\t:\t%d\n\n",
+ smd_info->name,
+ smd_info->hdl,
+ smd_info->inited,
+ atomic_read(&smd_info->opened),
+ atomic_read(&smd_info->diag_state),
+ smd_info->fifo_size,
+ work_pending(&smd_info->open_work),
+ work_pending(&smd_info->close_work),
+ work_pending(&smd_info->read_work),
+ (fwd_ctxt && fwd_ctxt->buf_1) ?
+ atomic_read(&fwd_ctxt->buf_1->in_busy) : -1,
+ (fwd_ctxt && fwd_ctxt->buf_2) ?
+ atomic_read(&fwd_ctxt->buf_2->in_busy) : -1,
+ (fwd_ctxt) ? fwd_ctxt->read_bytes : 0,
+ (fwd_ctxt) ? fwd_ctxt->write_bytes : 0,
+ (fwd_ctxt) ? fwd_ctxt->inited : -1,
+ (fwd_ctxt) ?
+ atomic_read(&fwd_ctxt->opened) : -1,
+ (fwd_ctxt) ? fwd_ctxt->ch_open : -1);
+ bytes_in_buffer += bytes_written;
+
+ /* Check if there is room to add another table entry */
+ bytes_remaining = buf_size - bytes_in_buffer;
+
+ if (bytes_remaining < bytes_written)
+ break;
+ }
+ }
+ diag_dbgfs_smdinfo_index = i+1;
+ *ppos = 0;
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, bytes_in_buffer);
+
+ kfree(buf);
+ return ret;
+}
+#endif
+
static ssize_t diag_dbgfs_read_socketinfo(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
@@ -944,6 +1052,12 @@
.read = diag_dbgfs_read_status,
};
+#ifdef CONFIG_DIAG_USES_SMD
+static const struct file_operations diag_dbgfs_smdinfo_ops = {
+ .read = diag_dbgfs_read_smdinfo,
+};
+#endif
+
const struct file_operations diag_dbgfs_socketinfo_ops = {
.read = diag_dbgfs_read_socketinfo,
};
@@ -989,6 +1103,13 @@
if (!entry)
goto err;
+#ifdef CONFIG_DIAG_USES_SMD
+ entry = debugfs_create_file("smdinfo", 0444, diag_dbgfs_dent, NULL,
+ &diag_dbgfs_smdinfo_ops);
+ if (!entry)
+ goto err;
+#endif
+
entry = debugfs_create_file("socketinfo", 0444, diag_dbgfs_dent, 0,
&diag_dbgfs_socketinfo_ops);
if (!entry)
@@ -1050,6 +1171,7 @@
diag_dbgfs_table_index = 0;
diag_dbgfs_mempool_index = 0;
diag_dbgfs_usbinfo_index = 0;
+ diag_dbgfs_smdinfo_index = 0;
diag_dbgfs_socketinfo_index = 0;
diag_dbgfs_hsicinfo_index = 0;
diag_dbgfs_bridgeinfo_index = 0;
diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c
index f510c14..223bc03 100644
--- a/drivers/char/diag/diag_masks.c
+++ b/drivers/char/diag/diag_masks.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -536,8 +536,7 @@
}
static int diag_cmd_get_ssid_range(unsigned char *src_buf, int src_len,
- unsigned char *dest_buf, int dest_len,
- struct diag_md_session_t *info)
+ unsigned char *dest_buf, int dest_len, int pid)
{
int i;
int write_len = 0;
@@ -545,23 +544,30 @@
struct diag_msg_ssid_query_t rsp;
struct diag_ssid_range_t ssid_range;
struct diag_mask_info *mask_info = NULL;
+ struct diag_md_session_t *info = NULL;
+ mutex_lock(&driver->md_session_lock);
+ info = diag_md_session_get_pid(pid);
mask_info = (!info) ? &msg_mask : info->msg_mask;
if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 ||
!mask_info) {
pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n",
__func__, src_buf, src_len, dest_buf, dest_len,
mask_info);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
if (!mask_info->ptr) {
pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
__func__, mask_info->ptr);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
- if (!diag_apps_responds())
+ if (!diag_apps_responds()) {
+ mutex_unlock(&driver->md_session_lock);
return 0;
+ }
mutex_lock(&driver->msg_mask_lock);
rsp.cmd_code = DIAG_CMD_MSG_CONFIG;
rsp.sub_cmd = DIAG_CMD_OP_GET_SSID_RANGE;
@@ -583,12 +589,12 @@
write_len += sizeof(ssid_range);
}
mutex_unlock(&driver->msg_mask_lock);
+ mutex_unlock(&driver->md_session_lock);
return write_len;
}
static int diag_cmd_get_build_mask(unsigned char *src_buf, int src_len,
- unsigned char *dest_buf, int dest_len,
- struct diag_md_session_t *info)
+ unsigned char *dest_buf, int dest_len, int pid)
{
int i = 0;
int write_len = 0;
@@ -641,8 +647,7 @@
}
static int diag_cmd_get_msg_mask(unsigned char *src_buf, int src_len,
- unsigned char *dest_buf, int dest_len,
- struct diag_md_session_t *info)
+ unsigned char *dest_buf, int dest_len, int pid)
{
int i;
int write_len = 0;
@@ -651,6 +656,10 @@
struct diag_build_mask_req_t *req = NULL;
struct diag_msg_build_mask_t rsp;
struct diag_mask_info *mask_info = NULL;
+ struct diag_md_session_t *info = NULL;
+
+ mutex_lock(&driver->md_session_lock);
+ info = diag_md_session_get_pid(pid);
mask_info = (!info) ? &msg_mask : info->msg_mask;
if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 ||
@@ -658,15 +667,19 @@
pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n",
__func__, src_buf, src_len, dest_buf, dest_len,
mask_info);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
if (!mask_info->ptr) {
pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
__func__, mask_info->ptr);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
- if (!diag_apps_responds())
+ if (!diag_apps_responds()) {
+ mutex_unlock(&driver->md_session_lock);
return 0;
+ }
mutex_lock(&driver->msg_mask_lock);
req = (struct diag_build_mask_req_t *)src_buf;
@@ -681,6 +694,7 @@
pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
__func__, mask->ptr);
mutex_unlock(&driver->msg_mask_lock);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) {
@@ -700,12 +714,12 @@
memcpy(dest_buf, &rsp, sizeof(rsp));
write_len += sizeof(rsp);
mutex_unlock(&driver->msg_mask_lock);
+ mutex_unlock(&driver->md_session_lock);
return write_len;
}
static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len,
- unsigned char *dest_buf, int dest_len,
- struct diag_md_session_t *info)
+ unsigned char *dest_buf, int dest_len, int pid)
{
uint32_t mask_size = 0, offset = 0;
uint32_t *temp = NULL;
@@ -716,6 +730,10 @@
struct diag_msg_build_mask_t rsp;
struct diag_mask_info *mask_info = NULL;
struct diag_msg_mask_t *mask_next = NULL;
+ struct diag_md_session_t *info = NULL;
+
+ mutex_lock(&driver->md_session_lock);
+ info = diag_md_session_get_pid(pid);
mask_info = (!info) ? &msg_mask : info->msg_mask;
if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 ||
@@ -723,11 +741,13 @@
pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n",
__func__, src_buf, src_len, dest_buf, dest_len,
mask_info);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
if (!mask_info->ptr) {
pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
__func__, mask_info->ptr);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
@@ -740,6 +760,7 @@
__func__, mask->ptr);
mutex_unlock(&driver->msg_mask_lock);
mutex_unlock(&mask_info->lock);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) {
@@ -782,6 +803,7 @@
mutex_unlock(&mask->lock);
mutex_unlock(&driver->msg_mask_lock);
mutex_unlock(&mask_info->lock);
+ mutex_unlock(&driver->md_session_lock);
return -ENOMEM;
}
mask->ptr = temp;
@@ -802,6 +824,7 @@
}
mutex_unlock(&driver->msg_mask_lock);
mutex_unlock(&mask_info->lock);
+ mutex_unlock(&driver->md_session_lock);
if (diag_check_update(APPS_DATA))
diag_update_userspace_clients(MSG_MASKS_TYPE);
@@ -842,8 +865,7 @@
}
static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len,
- unsigned char *dest_buf, int dest_len,
- struct diag_md_session_t *info)
+ unsigned char *dest_buf, int dest_len, int pid)
{
int i, write_len = 0, peripheral;
int header_len = sizeof(struct diag_msg_config_rsp_t);
@@ -851,6 +873,10 @@
struct diag_msg_config_rsp_t *req = NULL;
struct diag_msg_mask_t *mask = NULL;
struct diag_mask_info *mask_info = NULL;
+ struct diag_md_session_t *info = NULL;
+
+ mutex_lock(&driver->md_session_lock);
+ info = diag_md_session_get_pid(pid);
mask_info = (!info) ? &msg_mask : info->msg_mask;
if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 ||
@@ -858,11 +884,13 @@
pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n",
__func__, src_buf, src_len, dest_buf, dest_len,
mask_info);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
if (!mask_info->ptr) {
pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
__func__, mask_info->ptr);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
@@ -877,6 +905,7 @@
__func__, mask->ptr);
mutex_unlock(&driver->msg_mask_lock);
mutex_unlock(&mask_info->lock);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
mask_info->status = (req->rt_mask) ? DIAG_CTRL_MASK_ALL_ENABLED :
@@ -889,7 +918,7 @@
}
mutex_unlock(&driver->msg_mask_lock);
mutex_unlock(&mask_info->lock);
-
+ mutex_unlock(&driver->md_session_lock);
if (diag_check_update(APPS_DATA))
diag_update_userspace_clients(MSG_MASKS_TYPE);
@@ -923,8 +952,7 @@
}
static int diag_cmd_get_event_mask(unsigned char *src_buf, int src_len,
- unsigned char *dest_buf, int dest_len,
- struct diag_md_session_t *info)
+ unsigned char *dest_buf, int dest_len, int pid)
{
int write_len = 0;
uint32_t mask_size;
@@ -959,26 +987,30 @@
}
static int diag_cmd_update_event_mask(unsigned char *src_buf, int src_len,
- unsigned char *dest_buf, int dest_len,
- struct diag_md_session_t *info)
+ unsigned char *dest_buf, int dest_len, int pid)
{
int i, write_len = 0, mask_len = 0, peripheral;
int header_len = sizeof(struct diag_event_mask_config_t);
struct diag_event_mask_config_t rsp;
struct diag_event_mask_config_t *req;
struct diag_mask_info *mask_info = NULL;
+ struct diag_md_session_t *info = NULL;
+ mutex_lock(&driver->md_session_lock);
+ info = diag_md_session_get_pid(pid);
mask_info = (!info) ? &event_mask : info->event_mask;
if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 ||
!mask_info) {
pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n",
__func__, src_buf, src_len, dest_buf, dest_len,
mask_info);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
if (!mask_info->ptr) {
pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
__func__, mask_info->ptr);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
req = (struct diag_event_mask_config_t *)src_buf;
@@ -986,6 +1018,7 @@
if (mask_len <= 0 || mask_len > event_mask.mask_len) {
pr_err("diag: In %s, invalid event mask len: %d\n", __func__,
mask_len);
+ mutex_unlock(&driver->md_session_lock);
return -EIO;
}
@@ -993,6 +1026,7 @@
memcpy(mask_info->ptr, src_buf + header_len, mask_len);
mask_info->status = DIAG_CTRL_MASK_VALID;
mutex_unlock(&mask_info->lock);
+ mutex_unlock(&driver->md_session_lock);
if (diag_check_update(APPS_DATA))
diag_update_userspace_clients(EVENT_MASKS_TYPE);
@@ -1027,25 +1061,29 @@
}
static int diag_cmd_toggle_events(unsigned char *src_buf, int src_len,
- unsigned char *dest_buf, int dest_len,
- struct diag_md_session_t *info)
+ unsigned char *dest_buf, int dest_len, int pid)
{
int write_len = 0, i, peripheral;
uint8_t toggle = 0;
struct diag_event_report_t header;
struct diag_mask_info *mask_info = NULL;
+ struct diag_md_session_t *info = NULL;
+ mutex_lock(&driver->md_session_lock);
+ info = diag_md_session_get_pid(pid);
mask_info = (!info) ? &event_mask : info->event_mask;
if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 ||
!mask_info) {
pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n",
__func__, src_buf, src_len, dest_buf, dest_len,
mask_info);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
if (!mask_info->ptr) {
pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
__func__, mask_info->ptr);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
@@ -1059,6 +1097,7 @@
memset(mask_info->ptr, 0, mask_info->mask_len);
}
mutex_unlock(&mask_info->lock);
+ mutex_unlock(&driver->md_session_lock);
if (diag_check_update(APPS_DATA))
diag_update_userspace_clients(EVENT_MASKS_TYPE);
@@ -1088,8 +1127,7 @@
}
static int diag_cmd_get_log_mask(unsigned char *src_buf, int src_len,
- unsigned char *dest_buf, int dest_len,
- struct diag_md_session_t *info)
+ unsigned char *dest_buf, int dest_len, int pid)
{
int i;
int status = LOG_STATUS_INVALID;
@@ -1102,6 +1140,10 @@
struct diag_log_config_req_t *req;
struct diag_log_config_rsp_t rsp;
struct diag_mask_info *mask_info = NULL;
+ struct diag_md_session_t *info = NULL;
+
+ mutex_lock(&driver->md_session_lock);
+ info = diag_md_session_get_pid(pid);
mask_info = (!info) ? &log_mask : info->log_mask;
if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 ||
@@ -1109,16 +1151,20 @@
pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n",
__func__, src_buf, src_len, dest_buf, dest_len,
mask_info);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
if (!mask_info->ptr) {
pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
__func__, mask_info->ptr);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
- if (!diag_apps_responds())
+ if (!diag_apps_responds()) {
+ mutex_unlock(&driver->md_session_lock);
return 0;
+ }
req = (struct diag_log_config_req_t *)src_buf;
read_len += req_header_len;
@@ -1138,6 +1184,7 @@
if (!log_item->ptr) {
pr_err("diag: Invalid input in %s, mask: %pK\n",
__func__, log_item);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
for (i = 0; i < MAX_EQUIP_ID; i++, log_item++) {
@@ -1179,28 +1226,27 @@
rsp.status = status;
memcpy(dest_buf, &rsp, rsp_header_len);
+ mutex_unlock(&driver->md_session_lock);
return write_len;
}
static int diag_cmd_get_log_range(unsigned char *src_buf, int src_len,
- unsigned char *dest_buf, int dest_len,
- struct diag_md_session_t *info)
+ unsigned char *dest_buf, int dest_len, int pid)
{
int i;
int write_len = 0;
struct diag_log_config_rsp_t rsp;
- struct diag_mask_info *mask_info = NULL;
struct diag_log_mask_t *mask = (struct diag_log_mask_t *)log_mask.ptr;
+ if (!mask)
+ return -EINVAL;
+
if (!diag_apps_responds())
return 0;
- mask_info = (!info) ? &log_mask : info->log_mask;
- if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 ||
- !mask_info) {
- pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n",
- __func__, src_buf, src_len, dest_buf, dest_len,
- mask_info);
+ if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0) {
+ pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d\n",
+ __func__, src_buf, src_len, dest_buf, dest_len);
return -EINVAL;
}
@@ -1223,7 +1269,7 @@
static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len,
unsigned char *dest_buf, int dest_len,
- struct diag_md_session_t *info)
+ int pid)
{
int i, peripheral, write_len = 0;
int status = LOG_STATUS_SUCCESS;
@@ -1236,6 +1282,10 @@
struct diag_log_mask_t *mask = NULL;
struct diag_mask_info *mask_info = NULL;
unsigned char *temp_buf = NULL;
+ struct diag_md_session_t *info = NULL;
+
+ mutex_lock(&driver->md_session_lock);
+ info = diag_md_session_get_pid(pid);
mask_info = (!info) ? &log_mask : info->log_mask;
if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 ||
@@ -1243,11 +1293,13 @@
pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n",
__func__, src_buf, src_len, dest_buf, dest_len,
mask_info);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
if (!mask_info->ptr) {
pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
__func__, mask_info->ptr);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
@@ -1257,6 +1309,7 @@
if (!mask->ptr) {
pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
__func__, mask->ptr);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
if (req->equip_id >= MAX_EQUIP_ID) {
@@ -1319,6 +1372,7 @@
break;
}
mutex_unlock(&mask_info->lock);
+ mutex_unlock(&driver->md_session_lock);
if (diag_check_update(APPS_DATA))
diag_update_userspace_clients(LOG_MASKS_TYPE);
@@ -1365,13 +1419,16 @@
}
static int diag_cmd_disable_log_mask(unsigned char *src_buf, int src_len,
- unsigned char *dest_buf, int dest_len,
- struct diag_md_session_t *info)
+ unsigned char *dest_buf, int dest_len, int pid)
{
struct diag_mask_info *mask_info = NULL;
struct diag_log_mask_t *mask = NULL;
struct diag_log_config_rsp_t header;
int write_len = 0, i, peripheral;
+ struct diag_md_session_t *info = NULL;
+
+ mutex_lock(&driver->md_session_lock);
+ info = diag_md_session_get_pid(pid);
mask_info = (!info) ? &log_mask : info->log_mask;
if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 ||
@@ -1379,17 +1436,20 @@
pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n",
__func__, src_buf, src_len, dest_buf, dest_len,
mask_info);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
if (!mask_info->ptr) {
pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
__func__, mask_info->ptr);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
mask = (struct diag_log_mask_t *)mask_info->ptr;
if (!mask->ptr) {
pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
__func__, mask->ptr);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
for (i = 0; i < MAX_EQUIP_ID; i++, mask++) {
@@ -1398,6 +1458,7 @@
mutex_unlock(&mask->lock);
}
mask_info->status = DIAG_CTRL_MASK_ALL_DISABLED;
+ mutex_unlock(&driver->md_session_lock);
if (diag_check_update(APPS_DATA))
diag_update_userspace_clients(LOG_MASKS_TYPE);
@@ -2144,13 +2205,11 @@
}
}
-int diag_process_apps_masks(unsigned char *buf, int len,
- struct diag_md_session_t *info)
+int diag_process_apps_masks(unsigned char *buf, int len, int pid)
{
int size = 0, sub_cmd = 0;
int (*hdlr)(unsigned char *src_buf, int src_len,
- unsigned char *dest_buf, int dest_len,
- struct diag_md_session_t *info) = NULL;
+ unsigned char *dest_buf, int dest_len, int pid) = NULL;
if (!buf || len <= 0)
return -EINVAL;
@@ -2200,7 +2259,7 @@
if (hdlr)
size = hdlr(buf, len, driver->apps_rsp_buf,
- DIAG_MAX_RSP_SIZE, info);
+ DIAG_MAX_RSP_SIZE, pid);
return (size > 0) ? size : 0;
}
diff --git a/drivers/char/diag/diag_masks.h b/drivers/char/diag/diag_masks.h
index 1a52f94..6edeee9 100644
--- a/drivers/char/diag/diag_masks.h
+++ b/drivers/char/diag/diag_masks.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2015, 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -167,8 +167,7 @@
void diag_log_mask_free(struct diag_mask_info *mask_info);
void diag_msg_mask_free(struct diag_mask_info *mask_info);
void diag_event_mask_free(struct diag_mask_info *mask_info);
-int diag_process_apps_masks(unsigned char *buf, int len,
- struct diag_md_session_t *info);
+int diag_process_apps_masks(unsigned char *buf, int len, int pid);
void diag_send_updates_peripheral(uint8_t peripheral);
extern int diag_create_msg_mask_table_entry(struct diag_msg_mask_t *msg_mask,
diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c
index 9cecb03..ce0c7bb 100644
--- a/drivers/char/diag/diag_memorydevice.c
+++ b/drivers/char/diag/diag_memorydevice.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -129,11 +129,10 @@
int diag_md_write(int id, unsigned char *buf, int len, int ctx)
{
- int i;
+ int i, peripheral, pid = 0;
uint8_t found = 0;
unsigned long flags;
struct diag_md_info *ch = NULL;
- int peripheral;
struct diag_md_session_t *session_info = NULL;
if (id < 0 || id >= NUM_DIAG_MD_DEV || id >= DIAG_NUM_PROC)
@@ -146,10 +145,14 @@
if (peripheral < 0)
return -EINVAL;
- session_info =
- diag_md_session_get_peripheral(peripheral);
- if (!session_info)
+ mutex_lock(&driver->md_session_lock);
+ session_info = diag_md_session_get_peripheral(peripheral);
+ if (!session_info) {
+ mutex_unlock(&driver->md_session_lock);
return -EIO;
+ }
+ pid = session_info->pid;
+ mutex_unlock(&driver->md_session_lock);
ch = &diag_md[id];
if (!ch)
@@ -192,8 +195,7 @@
found = 0;
for (i = 0; i < driver->num_clients && !found; i++) {
- if ((driver->client_map[i].pid !=
- session_info->pid) ||
+ if ((driver->client_map[i].pid != pid) ||
(driver->client_map[i].pid == 0))
continue;
diff --git a/drivers/char/diag/diag_usb.c b/drivers/char/diag/diag_usb.c
index 1cf7f52..060f03f 100644
--- a/drivers/char/diag/diag_usb.c
+++ b/drivers/char/diag/diag_usb.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2016, 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -221,7 +221,7 @@
if (!atomic_read(&ch->connected) &&
driver->usb_connected && diag_mask_param())
- diag_clear_masks(NULL);
+ diag_clear_masks(0);
if (ch && ch->ops && ch->ops->close)
ch->ops->close(ch->ctxt, DIAG_USB_MODE);
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 9de40b0..badd12c 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -22,6 +22,7 @@
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/device.h>
+#include <soc/qcom/smd.h>
#include <linux/atomic.h>
#include "diagfwd_bridge.h"
@@ -714,7 +715,7 @@
void diag_cmd_remove_reg_by_proc(int proc);
int diag_cmd_chk_polling(struct diag_cmd_reg_entry_t *entry);
int diag_mask_param(void);
-void diag_clear_masks(struct diag_md_session_t *info);
+void diag_clear_masks(int pid);
uint8_t diag_mask_to_pd_value(uint32_t peripheral_mask);
int diag_query_pd(char *process_name);
int diag_search_peripheral_by_pd(uint8_t pd_val);
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 0158549..694b483 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -167,7 +167,7 @@
void *diag_ipc_log;
#endif
-static void diag_md_session_close(struct diag_md_session_t *session_info);
+static void diag_md_session_close(int pid);
/*
* Returns the next delayed rsp id. If wrapping is enabled,
@@ -243,12 +243,13 @@
timer_in_progress = 0;
mutex_lock(&apps_data_mutex);
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_peripheral(APPS_DATA);
if (session_info)
hdlc_disabled = session_info->hdlc_disabled;
else
hdlc_disabled = driver->hdlc_disabled;
-
+ mutex_unlock(&driver->md_session_lock);
if (!hdlc_disabled)
diag_drain_apps_data(&hdlc_data);
else
@@ -422,7 +423,7 @@
{
return diag_mask_clear_param;
}
-void diag_clear_masks(struct diag_md_session_t *info)
+void diag_clear_masks(int pid)
{
int ret;
char cmd_disable_log_mask[] = { 0x73, 0, 0, 0, 0, 0, 0, 0};
@@ -431,14 +432,14 @@
DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
"diag: %s: masks clear request upon %s\n", __func__,
- ((info) ? "ODL exit" : "USB Disconnection"));
+ ((pid) ? "ODL exit" : "USB Disconnection"));
ret = diag_process_apps_masks(cmd_disable_log_mask,
- sizeof(cmd_disable_log_mask), info);
+ sizeof(cmd_disable_log_mask), pid);
ret = diag_process_apps_masks(cmd_disable_msg_mask,
- sizeof(cmd_disable_msg_mask), info);
+ sizeof(cmd_disable_msg_mask), pid);
ret = diag_process_apps_masks(cmd_disable_event_mask,
- sizeof(cmd_disable_event_mask), info);
+ sizeof(cmd_disable_event_mask), pid);
DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
"diag:%s: masks cleared successfully\n", __func__);
}
@@ -451,21 +452,23 @@
struct diag_md_session_t *session_info = NULL;
struct diag_logging_mode_param_t params;
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_pid(pid);
- if (!session_info)
+ if (!session_info) {
+ mutex_unlock(&driver->md_session_lock);
return;
+ }
+ session_mask = session_info->peripheral_mask;
+ mutex_unlock(&driver->md_session_lock);
if (diag_mask_clear_param)
- diag_clear_masks(session_info);
+ diag_clear_masks(pid);
mutex_lock(&driver->diag_maskclear_mutex);
driver->mask_clear = 1;
mutex_unlock(&driver->diag_maskclear_mutex);
mutex_lock(&driver->diagchar_mutex);
- session_mask = session_info->peripheral_mask;
- diag_md_session_close(session_info);
-
p_mask =
diag_translate_kernel_to_user_mask(session_mask);
@@ -489,7 +492,9 @@
}
}
}
-
+ mutex_lock(&driver->md_session_lock);
+ diag_md_session_close(pid);
+ mutex_unlock(&driver->md_session_lock);
diag_switch_logging(¶ms);
mutex_unlock(&driver->diagchar_mutex);
}
@@ -1024,11 +1029,13 @@
if (driver->hdlc_encode_buf_len != 0)
return -EAGAIN;
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_peripheral(APPS_DATA);
if (session_info)
hdlc_disabled = session_info->hdlc_disabled;
else
hdlc_disabled = driver->hdlc_disabled;
+ mutex_unlock(&driver->md_session_lock);
if (hdlc_disabled) {
if (len < 4) {
pr_err("diag: In %s, invalid len: %d of non_hdlc pkt",
@@ -1386,15 +1393,16 @@
return err;
}
-static void diag_md_session_close(struct diag_md_session_t *session_info)
+static void diag_md_session_close(int pid)
{
int i;
uint8_t found = 0;
+ struct diag_md_session_t *session_info = NULL;
+ session_info = diag_md_session_get_pid(pid);
if (!session_info)
return;
- mutex_lock(&driver->md_session_lock);
for (i = 0; i < NUM_MD_SESSIONS; i++) {
if (driver->md_session_map[i] != session_info)
continue;
@@ -1420,13 +1428,14 @@
driver->md_session_mode = (found) ? DIAG_MD_PERIPHERAL : DIAG_MD_NONE;
kfree(session_info);
session_info = NULL;
- mutex_unlock(&driver->md_session_lock);
DIAG_LOG(DIAG_DEBUG_USERSPACE, "cleared up session\n");
}
struct diag_md_session_t *diag_md_session_get_pid(int pid)
{
int i;
+ if (pid <= 0)
+ return NULL;
for (i = 0; i < NUM_MD_SESSIONS; i++) {
if (driver->md_session_map[i] &&
driver->md_session_map[i]->pid == pid)
@@ -1442,10 +1451,12 @@
return driver->md_session_map[peripheral];
}
-static int diag_md_peripheral_switch(struct diag_md_session_t *session_info,
+static int diag_md_peripheral_switch(int pid,
int peripheral_mask, int req_mode) {
int i, bit = 0;
+ struct diag_md_session_t *session_info = NULL;
+ session_info = diag_md_session_get_pid(pid);
if (!session_info)
return -EINVAL;
if (req_mode != DIAG_USB_MODE || req_mode != DIAG_MEMORY_DEVICE_MODE)
@@ -1455,25 +1466,20 @@
* check that md_session_map for i == session_info,
* if not then race condition occurred and bail
*/
- mutex_lock(&driver->md_session_lock);
for (i = 0; i < NUM_MD_SESSIONS; i++) {
bit = MD_PERIPHERAL_MASK(i) & peripheral_mask;
if (!bit)
continue;
if (req_mode == DIAG_USB_MODE) {
- if (driver->md_session_map[i] != session_info) {
- mutex_unlock(&driver->md_session_lock);
+ if (driver->md_session_map[i] != session_info)
return -EINVAL;
- }
driver->md_session_map[i] = NULL;
driver->md_session_mask &= ~bit;
session_info->peripheral_mask &= ~bit;
} else {
- if (driver->md_session_map[i] != NULL) {
- mutex_unlock(&driver->md_session_lock);
+ if (driver->md_session_map[i] != NULL)
return -EINVAL;
- }
driver->md_session_map[i] = session_info;
driver->md_session_mask |= bit;
session_info->peripheral_mask |= bit;
@@ -1482,7 +1488,6 @@
}
driver->md_session_mode = DIAG_MD_PERIPHERAL;
- mutex_unlock(&driver->md_session_lock);
DIAG_LOG(DIAG_DEBUG_USERSPACE, "Changed Peripherals:0x%x to mode:%d\n",
peripheral_mask, req_mode);
}
@@ -1491,7 +1496,7 @@
const struct diag_logging_mode_param_t *param,
uint8_t *change_mode)
{
- int i, bit = 0, err = 0;
+ int i, bit = 0, err = 0, peripheral_mask = 0;
int change_mask = 0;
struct diag_md_session_t *session_info = NULL;
@@ -1515,12 +1520,13 @@
if (req_mode == DIAG_USB_MODE) {
if (curr_mode == DIAG_USB_MODE)
return 0;
+ mutex_lock(&driver->md_session_lock);
if (driver->md_session_mode == DIAG_MD_NONE
&& driver->md_session_mask == 0 && driver->logging_mask) {
*change_mode = 1;
+ mutex_unlock(&driver->md_session_lock);
return 0;
}
-
/*
* curr_mode is either DIAG_MULTI_MODE or DIAG_MD_MODE
* Check if requested peripherals are already in usb mode
@@ -1532,8 +1538,10 @@
if (bit & driver->logging_mask)
change_mask |= bit;
}
- if (!change_mask)
+ if (!change_mask) {
+ mutex_unlock(&driver->md_session_lock);
return 0;
+ }
/*
* Change is needed. Check if this md_session has set all the
@@ -1542,29 +1550,29 @@
* If this session owns all the requested peripherals, then
* call function to switch the modes/masks for the md_session
*/
- mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_pid(current->tgid);
- mutex_unlock(&driver->md_session_lock);
-
if (!session_info) {
*change_mode = 1;
+ mutex_unlock(&driver->md_session_lock);
return 0;
}
- if ((change_mask & session_info->peripheral_mask)
+ peripheral_mask = session_info->peripheral_mask;
+ if ((change_mask & peripheral_mask)
!= change_mask) {
DIAG_LOG(DIAG_DEBUG_USERSPACE,
"Another MD Session owns a requested peripheral\n");
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
*change_mode = 1;
/* If all peripherals are being set to USB Mode, call close */
- if (~change_mask & session_info->peripheral_mask) {
- err = diag_md_peripheral_switch(session_info,
+ if (~change_mask & peripheral_mask) {
+ err = diag_md_peripheral_switch(current->tgid,
change_mask, DIAG_USB_MODE);
} else
- diag_md_session_close(session_info);
-
+ diag_md_session_close(current->tgid);
+ mutex_unlock(&driver->md_session_lock);
return err;
} else if (req_mode == DIAG_MEMORY_DEVICE_MODE) {
@@ -1573,21 +1581,23 @@
* been set. Check that requested peripherals already set are
* owned by this md session
*/
- change_mask = driver->md_session_mask & param->peripheral_mask;
mutex_lock(&driver->md_session_lock);
+ change_mask = driver->md_session_mask & param->peripheral_mask;
session_info = diag_md_session_get_pid(current->tgid);
- mutex_unlock(&driver->md_session_lock);
if (session_info) {
if ((session_info->peripheral_mask & change_mask)
!= change_mask) {
DIAG_LOG(DIAG_DEBUG_USERSPACE,
"Another MD Session owns a requested peripheral\n");
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
- err = diag_md_peripheral_switch(session_info,
+ err = diag_md_peripheral_switch(current->tgid,
change_mask, DIAG_USB_MODE);
+ mutex_unlock(&driver->md_session_lock);
} else {
+ mutex_unlock(&driver->md_session_lock);
if (change_mask) {
DIAG_LOG(DIAG_DEBUG_USERSPACE,
"Another MD Session owns a requested peripheral\n");
@@ -2047,19 +2057,17 @@
{
uint8_t hdlc_support;
struct diag_md_session_t *session_info = NULL;
- mutex_lock(&driver->md_session_lock);
- session_info = diag_md_session_get_pid(current->tgid);
- mutex_unlock(&driver->md_session_lock);
if (copy_from_user(&hdlc_support, (void __user *)ioarg,
sizeof(uint8_t)))
return -EFAULT;
mutex_lock(&driver->hdlc_disable_mutex);
- if (session_info) {
- mutex_lock(&driver->md_session_lock);
+ mutex_lock(&driver->md_session_lock);
+ session_info = diag_md_session_get_pid(current->tgid);
+ if (session_info)
session_info->hdlc_disabled = hdlc_support;
- mutex_unlock(&driver->md_session_lock);
- } else
+ else
driver->hdlc_disabled = hdlc_support;
+ mutex_unlock(&driver->md_session_lock);
mutex_unlock(&driver->hdlc_disable_mutex);
diag_update_md_clients(HDLC_SUPPORT_TYPE);
@@ -2885,7 +2893,6 @@
int remote_proc = 0;
const int mempool = POOL_TYPE_COPY;
unsigned char *user_space_data = NULL;
- struct diag_md_session_t *info = NULL;
if (!buf || len <= 0 || len > CALLBACK_BUF_SIZE) {
pr_err_ratelimited("diag: In %s, invalid buf %pK len: %d\n",
@@ -2936,13 +2943,11 @@
} else {
wait_event_interruptible(driver->wait_q,
(driver->in_busy_pktdata == 0));
- mutex_lock(&driver->md_session_lock);
- info = diag_md_session_get_pid(current->tgid);
- mutex_unlock(&driver->md_session_lock);
- ret = diag_process_apps_pkt(user_space_data, len, info);
+ ret = diag_process_apps_pkt(user_space_data, len,
+ current->tgid);
if (ret == 1)
diag_send_error_rsp((void *)(user_space_data), len,
- info);
+ current->tgid);
}
fail:
diagmem_free(driver, user_space_data, mempool);
@@ -3008,24 +3013,25 @@
if (!remote_proc) {
mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_pid(current->tgid);
- mutex_unlock(&driver->md_session_lock);
if (!session_info) {
pr_err("diag:In %s request came from invalid md session pid:%d",
__func__, current->tgid);
+ mutex_unlock(&driver->md_session_lock);
return -EINVAL;
}
if (session_info)
hdlc_disabled = session_info->hdlc_disabled;
else
hdlc_disabled = driver->hdlc_disabled;
+ mutex_unlock(&driver->md_session_lock);
if (!hdlc_disabled)
diag_process_hdlc_pkt((void *)
(driver->user_space_data_buf),
- len, session_info);
+ len, current->tgid);
else
diag_process_non_hdlc_pkt((char *)
(driver->user_space_data_buf),
- len, session_info);
+ len, current->tgid);
return 0;
}
@@ -3102,11 +3108,13 @@
mutex_lock(&apps_data_mutex);
mutex_lock(&driver->hdlc_disable_mutex);
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_peripheral(APPS_DATA);
if (session_info)
hdlc_disabled = session_info->hdlc_disabled;
else
hdlc_disabled = driver->hdlc_disabled;
+ mutex_unlock(&driver->md_session_lock);
if (hdlc_disabled)
ret = diag_process_apps_data_non_hdlc(user_space_data, len,
pkt_type);
@@ -3177,9 +3185,9 @@
ret += sizeof(int);
mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_pid(current->tgid);
- mutex_unlock(&driver->md_session_lock);
exit_stat = diag_md_copy_to_user(buf, &ret, count,
session_info);
+ mutex_unlock(&driver->md_session_lock);
goto exit;
} else if (driver->data_ready[index] & USER_SPACE_DATA_TYPE) {
/* In case, the thread wakes up and the logging mode is not
@@ -3199,14 +3207,16 @@
mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_pid(current->tgid);
- mutex_unlock(&driver->md_session_lock);
if (session_info) {
COPY_USER_SPACE_OR_ERR(buf+4,
session_info->hdlc_disabled,
sizeof(uint8_t));
- if (ret == -EFAULT)
+ if (ret == -EFAULT) {
+ mutex_unlock(&driver->md_session_lock);
goto exit;
+ }
}
+ mutex_unlock(&driver->md_session_lock);
goto exit;
}
@@ -3226,12 +3236,16 @@
if (driver->data_ready[index] & MSG_MASKS_TYPE) {
/*Copy the type of data being passed*/
data_type = driver->data_ready[index] & MSG_MASKS_TYPE;
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_peripheral(APPS_DATA);
COPY_USER_SPACE_OR_ERR(buf, data_type, sizeof(int));
- if (ret == -EFAULT)
+ if (ret == -EFAULT) {
+ mutex_unlock(&driver->md_session_lock);
goto exit;
+ }
write_len = diag_copy_to_user_msg_mask(buf + ret, count,
session_info);
+ mutex_unlock(&driver->md_session_lock);
if (write_len > 0)
ret += write_len;
driver->data_ready[index] ^= MSG_MASKS_TYPE;
@@ -3242,25 +3256,32 @@
if (driver->data_ready[index] & EVENT_MASKS_TYPE) {
/*Copy the type of data being passed*/
data_type = driver->data_ready[index] & EVENT_MASKS_TYPE;
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_peripheral(APPS_DATA);
COPY_USER_SPACE_OR_ERR(buf, data_type, 4);
- if (ret == -EFAULT)
+ if (ret == -EFAULT) {
+ mutex_unlock(&driver->md_session_lock);
goto exit;
-
+ }
if (session_info && session_info->event_mask &&
session_info->event_mask->ptr) {
COPY_USER_SPACE_OR_ERR(buf + sizeof(int),
*(session_info->event_mask->ptr),
session_info->event_mask->mask_len);
- if (ret == -EFAULT)
+ if (ret == -EFAULT) {
+ mutex_unlock(&driver->md_session_lock);
goto exit;
+ }
} else {
COPY_USER_SPACE_OR_ERR(buf + sizeof(int),
*(event_mask.ptr),
event_mask.mask_len);
- if (ret == -EFAULT)
+ if (ret == -EFAULT) {
+ mutex_unlock(&driver->md_session_lock);
goto exit;
+ }
}
+ mutex_unlock(&driver->md_session_lock);
driver->data_ready[index] ^= EVENT_MASKS_TYPE;
atomic_dec(&driver->data_ready_notif[index]);
goto exit;
@@ -3269,13 +3290,17 @@
if (driver->data_ready[index] & LOG_MASKS_TYPE) {
/*Copy the type of data being passed*/
data_type = driver->data_ready[index] & LOG_MASKS_TYPE;
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_peripheral(APPS_DATA);
COPY_USER_SPACE_OR_ERR(buf, data_type, sizeof(int));
- if (ret == -EFAULT)
+ if (ret == -EFAULT) {
+ mutex_unlock(&driver->md_session_lock);
goto exit;
+ }
write_len = diag_copy_to_user_log_mask(buf + ret, count,
session_info);
+ mutex_unlock(&driver->md_session_lock);
if (write_len > 0)
ret += write_len;
driver->data_ready[index] ^= LOG_MASKS_TYPE;
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 83f44ce..33048e1 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -242,7 +242,7 @@
}
static void pack_rsp_and_send(unsigned char *buf, int len,
- struct diag_md_session_t *info)
+ int pid)
{
int err;
int retry_count = 0, i, rsp_ctxt;
@@ -250,6 +250,7 @@
unsigned long flags;
unsigned char *rsp_ptr = driver->encoded_rsp_buf;
struct diag_pkt_frame_t header;
+ struct diag_md_session_t *session_info = NULL, *info = NULL;
if (!rsp_ptr || !buf)
return;
@@ -260,6 +261,11 @@
return;
}
+ mutex_lock(&driver->md_session_lock);
+ session_info = diag_md_session_get_pid(pid);
+ info = (session_info) ? session_info :
+ diag_md_session_get_peripheral(APPS_DATA);
+
/*
* Explicitly check for the Peripheral Modem here
* is necessary till a way to identify a peripheral
@@ -279,6 +285,7 @@
}
} else
rsp_ctxt = driver->rsp_buf_ctxt;
+ mutex_unlock(&driver->md_session_lock);
/*
* Keep trying till we get the buffer back. It should probably
@@ -302,8 +309,11 @@
* draining responses when we are in Memory Device Mode.
*/
if (driver->logging_mode == DIAG_MEMORY_DEVICE_MODE ||
- driver->logging_mode == DIAG_MULTI_MODE)
+ driver->logging_mode == DIAG_MULTI_MODE) {
+ mutex_lock(&driver->md_session_lock);
chk_logging_wakeup();
+ mutex_unlock(&driver->md_session_lock);
+ }
}
if (driver->rsp_buf_busy) {
pr_err("diag: unable to get hold of response buffer\n");
@@ -332,13 +342,14 @@
}
static void encode_rsp_and_send(unsigned char *buf, int len,
- struct diag_md_session_t *info)
+ int pid)
{
struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 };
struct diag_hdlc_dest_type enc = { NULL, NULL, 0 };
unsigned char *rsp_ptr = driver->encoded_rsp_buf;
int err, i, rsp_ctxt, retry_count = 0;
unsigned long flags;
+ struct diag_md_session_t *session_info = NULL, *info = NULL;
if (!rsp_ptr || !buf)
return;
@@ -349,6 +360,11 @@
return;
}
+ mutex_lock(&driver->md_session_lock);
+ session_info = diag_md_session_get_pid(pid);
+ info = (session_info) ? session_info :
+ diag_md_session_get_peripheral(APPS_DATA);
+
/*
* Explicitly check for the Peripheral Modem here
* is necessary till a way to identify a peripheral
@@ -368,7 +384,7 @@
}
} else
rsp_ctxt = driver->rsp_buf_ctxt;
-
+ mutex_unlock(&driver->md_session_lock);
/*
* Keep trying till we get the buffer back. It should probably
* take one or two iterations. When this loops till UINT_MAX, it
@@ -391,8 +407,11 @@
* draining responses when we are in Memory Device Mode.
*/
if (driver->logging_mode == DIAG_MEMORY_DEVICE_MODE ||
- driver->logging_mode == DIAG_MULTI_MODE)
+ driver->logging_mode == DIAG_MULTI_MODE) {
+ mutex_lock(&driver->md_session_lock);
chk_logging_wakeup();
+ mutex_unlock(&driver->md_session_lock);
+ }
}
if (driver->rsp_buf_busy) {
@@ -424,22 +443,23 @@
}
static void diag_send_rsp(unsigned char *buf, int len,
- struct diag_md_session_t *info)
+ int pid)
{
- struct diag_md_session_t *session_info = NULL;
+ struct diag_md_session_t *session_info = NULL, *info = NULL;
uint8_t hdlc_disabled;
-
+ mutex_lock(&driver->md_session_lock);
+ info = diag_md_session_get_pid(pid);
session_info = (info) ? info :
diag_md_session_get_peripheral(APPS_DATA);
if (session_info)
hdlc_disabled = session_info->hdlc_disabled;
else
hdlc_disabled = driver->hdlc_disabled;
-
+ mutex_unlock(&driver->md_session_lock);
if (hdlc_disabled)
- pack_rsp_and_send(buf, len, session_info);
+ pack_rsp_and_send(buf, len, pid);
else
- encode_rsp_and_send(buf, len, session_info);
+ encode_rsp_and_send(buf, len, pid);
}
void diag_update_pkt_buffer(unsigned char *buf, uint32_t len, int type)
@@ -506,13 +526,14 @@
int i, j;
mutex_lock(&driver->diagchar_mutex);
+ mutex_lock(&driver->md_session_lock);
for (i = 0; i < NUM_MD_SESSIONS; i++) {
if (driver->md_session_map[i] != NULL)
for (j = 0; j < driver->num_clients; j++) {
if (driver->client_map[j].pid != 0 &&
driver->client_map[j].pid ==
driver->md_session_map[i]->pid) {
- if (!(driver->data_ready[i] & type)) {
+ if (!(driver->data_ready[j] & type)) {
driver->data_ready[j] |= type;
atomic_inc(
&driver->data_ready_notif[j]);
@@ -521,6 +542,7 @@
}
}
}
+ mutex_unlock(&driver->md_session_lock);
wake_up_interruptible(&driver->wait_q);
mutex_unlock(&driver->diagchar_mutex);
}
@@ -594,14 +616,15 @@
* Check if command is valid. If the command is asking for
* status, then the processor mask field is to be ignored.
*/
- if ((version != 2) || (cmd > STATUS_STM) ||
- ((cmd != STATUS_STM) && ((mask == 0) || (0 != (mask >> 4))))) {
+ if ((version != 2) || (cmd > STM_AUTO_QUERY) ||
+ ((cmd != STATUS_STM && cmd != STM_AUTO_QUERY) &&
+ ((mask == 0) || (0 != (mask >> 4))))) {
/* Command is invalid. Send bad param message response */
dest_buf[0] = BAD_PARAM_RESPONSE_MESSAGE;
for (i = 0; i < STM_CMD_NUM_BYTES; i++)
dest_buf[i+1] = *(buf + i);
return STM_CMD_NUM_BYTES+1;
- } else if (cmd != STATUS_STM) {
+ } else if (cmd != STATUS_STM && cmd != STM_AUTO_QUERY) {
if (mask & DIAG_STM_MODEM)
diag_process_stm_mask(cmd, DIAG_STM_MODEM,
PERIPHERAL_MODEM);
@@ -981,7 +1004,7 @@
}
void diag_send_error_rsp(unsigned char *buf, int len,
- struct diag_md_session_t *info)
+ int pid)
{
/* -1 to accommodate the first byte 0x13 */
if (len > (DIAG_MAX_RSP_SIZE - 1)) {
@@ -991,13 +1014,12 @@
*(uint8_t *)driver->apps_rsp_buf = DIAG_CMD_ERROR;
memcpy((driver->apps_rsp_buf + sizeof(uint8_t)), buf, len);
- diag_send_rsp(driver->apps_rsp_buf, len + 1, info);
+ diag_send_rsp(driver->apps_rsp_buf, len + 1, pid);
}
-int diag_process_apps_pkt(unsigned char *buf, int len,
- struct diag_md_session_t *info)
+int diag_process_apps_pkt(unsigned char *buf, int len, int pid)
{
- int i;
+ int i, p_mask = 0;
int mask_ret;
int write_len = 0;
unsigned char *temp = NULL;
@@ -1006,14 +1028,15 @@
struct diag_cmd_reg_t *reg_item = NULL;
struct diagfwd_info *fwd_info = NULL;
uint32_t pd_mask = 0;
+ struct diag_md_session_t *info = NULL;
if (!buf)
return -EIO;
/* Check if the command is a supported mask command */
- mask_ret = diag_process_apps_masks(buf, len, info);
+ mask_ret = diag_process_apps_masks(buf, len, pid);
if (mask_ret > 0) {
- diag_send_rsp(driver->apps_rsp_buf, mask_ret, info);
+ diag_send_rsp(driver->apps_rsp_buf, mask_ret, pid);
return 0;
}
@@ -1035,7 +1058,7 @@
driver->apps_rsp_buf,
DIAG_MAX_RSP_SIZE);
if (write_len > 0)
- diag_send_rsp(driver->apps_rsp_buf, write_len, info);
+ diag_send_rsp(driver->apps_rsp_buf, write_len, pid);
return 0;
}
@@ -1044,18 +1067,22 @@
if (temp_entry) {
reg_item = container_of(temp_entry, struct diag_cmd_reg_t,
entry);
+ mutex_lock(&driver->md_session_lock);
+ info = diag_md_session_get_pid(pid);
if (info) {
+ p_mask = info->peripheral_mask;
+ mutex_unlock(&driver->md_session_lock);
MD_PERIPHERAL_PD_MASK(TYPE_CMD, reg_item->proc,
pd_mask);
if ((MD_PERIPHERAL_MASK(reg_item->proc) &
- info->peripheral_mask) ||
- (pd_mask & info->peripheral_mask))
+ p_mask) || (pd_mask & p_mask))
write_len = diag_send_data(reg_item, buf, len);
} else {
+ mutex_unlock(&driver->md_session_lock);
if (MD_PERIPHERAL_MASK(reg_item->proc) &
driver->logging_mask) {
mutex_unlock(&driver->cmd_reg_mutex);
- diag_send_error_rsp(buf, len, info);
+ diag_send_error_rsp(buf, len, pid);
return write_len;
}
else
@@ -1073,13 +1100,13 @@
for (i = 0; i < 4; i++)
*(driver->apps_rsp_buf+i) = *(buf+i);
*(uint32_t *)(driver->apps_rsp_buf+4) = DIAG_MAX_REQ_SIZE;
- diag_send_rsp(driver->apps_rsp_buf, 8, info);
+ diag_send_rsp(driver->apps_rsp_buf, 8, pid);
return 0;
} else if ((*buf == 0x4b) && (*(buf+1) == 0x12) &&
(*(uint16_t *)(buf+2) == DIAG_DIAG_STM)) {
len = diag_process_stm_cmd(buf, driver->apps_rsp_buf);
if (len > 0) {
- diag_send_rsp(driver->apps_rsp_buf, len, info);
+ diag_send_rsp(driver->apps_rsp_buf, len, pid);
return 0;
}
return len;
@@ -1092,7 +1119,7 @@
driver->apps_rsp_buf,
DIAG_MAX_RSP_SIZE);
if (write_len > 0)
- diag_send_rsp(driver->apps_rsp_buf, write_len, info);
+ diag_send_rsp(driver->apps_rsp_buf, write_len, pid);
return 0;
}
/* Check for time sync switch command */
@@ -1103,7 +1130,7 @@
driver->apps_rsp_buf,
DIAG_MAX_RSP_SIZE);
if (write_len > 0)
- diag_send_rsp(driver->apps_rsp_buf, write_len, info);
+ diag_send_rsp(driver->apps_rsp_buf, write_len, pid);
return 0;
}
/* Check for diag id command */
@@ -1114,14 +1141,14 @@
driver->apps_rsp_buf,
DIAG_MAX_RSP_SIZE);
if (write_len > 0)
- diag_send_rsp(driver->apps_rsp_buf, write_len, info);
+ diag_send_rsp(driver->apps_rsp_buf, write_len, pid);
return 0;
}
/* Check for download command */
else if ((chk_apps_master()) && (*buf == 0x3A)) {
/* send response back */
driver->apps_rsp_buf[0] = *buf;
- diag_send_rsp(driver->apps_rsp_buf, 1, info);
+ diag_send_rsp(driver->apps_rsp_buf, 1, pid);
msleep(5000);
/* call download API */
msm_set_restart_mode(RESTART_DLOAD);
@@ -1141,7 +1168,7 @@
for (i = 0; i < 13; i++)
driver->apps_rsp_buf[i+3] = 0;
- diag_send_rsp(driver->apps_rsp_buf, 16, info);
+ diag_send_rsp(driver->apps_rsp_buf, 16, pid);
return 0;
}
}
@@ -1150,7 +1177,7 @@
(*(buf+2) == 0x04) && (*(buf+3) == 0x0)) {
memcpy(driver->apps_rsp_buf, buf, 4);
driver->apps_rsp_buf[4] = wrap_enabled;
- diag_send_rsp(driver->apps_rsp_buf, 5, info);
+ diag_send_rsp(driver->apps_rsp_buf, 5, pid);
return 0;
}
/* Wrap the Delayed Rsp ID */
@@ -1159,7 +1186,7 @@
wrap_enabled = true;
memcpy(driver->apps_rsp_buf, buf, 4);
driver->apps_rsp_buf[4] = wrap_count;
- diag_send_rsp(driver->apps_rsp_buf, 6, info);
+ diag_send_rsp(driver->apps_rsp_buf, 6, pid);
return 0;
}
/* Mobile ID Rsp */
@@ -1170,7 +1197,7 @@
driver->apps_rsp_buf,
DIAG_MAX_RSP_SIZE);
if (write_len > 0) {
- diag_send_rsp(driver->apps_rsp_buf, write_len, info);
+ diag_send_rsp(driver->apps_rsp_buf, write_len, pid);
return 0;
}
}
@@ -1190,7 +1217,7 @@
for (i = 0; i < 55; i++)
driver->apps_rsp_buf[i] = 0;
- diag_send_rsp(driver->apps_rsp_buf, 55, info);
+ diag_send_rsp(driver->apps_rsp_buf, 55, pid);
return 0;
}
/* respond to 0x7c command */
@@ -1203,14 +1230,14 @@
chk_config_get_id();
*(unsigned char *)(driver->apps_rsp_buf + 12) = '\0';
*(unsigned char *)(driver->apps_rsp_buf + 13) = '\0';
- diag_send_rsp(driver->apps_rsp_buf, 14, info);
+ diag_send_rsp(driver->apps_rsp_buf, 14, pid);
return 0;
}
}
write_len = diag_cmd_chk_stats(buf, len, driver->apps_rsp_buf,
DIAG_MAX_RSP_SIZE);
if (write_len > 0) {
- diag_send_rsp(driver->apps_rsp_buf, write_len, info);
+ diag_send_rsp(driver->apps_rsp_buf, write_len, pid);
return 0;
}
write_len = diag_cmd_disable_hdlc(buf, len, driver->apps_rsp_buf,
@@ -1222,7 +1249,7 @@
* before disabling HDLC encoding on Apps processor.
*/
mutex_lock(&driver->hdlc_disable_mutex);
- diag_send_rsp(driver->apps_rsp_buf, write_len, info);
+ diag_send_rsp(driver->apps_rsp_buf, write_len, pid);
/*
* Set the value of hdlc_disabled after sending the response to
* the tools. This is required since the tools is expecting a
@@ -1230,10 +1257,13 @@
*/
pr_debug("diag: In %s, disabling HDLC encoding\n",
__func__);
+ mutex_lock(&driver->md_session_lock);
+ info = diag_md_session_get_pid(pid);
if (info)
info->hdlc_disabled = 1;
else
driver->hdlc_disabled = 1;
+ mutex_unlock(&driver->md_session_lock);
diag_update_md_clients(HDLC_SUPPORT_TYPE);
mutex_unlock(&driver->hdlc_disable_mutex);
return 0;
@@ -1242,13 +1272,12 @@
/* We have now come to the end of the function. */
if (chk_apps_only())
- diag_send_error_rsp(buf, len, info);
+ diag_send_error_rsp(buf, len, pid);
return 0;
}
-void diag_process_hdlc_pkt(void *data, unsigned int len,
- struct diag_md_session_t *info)
+void diag_process_hdlc_pkt(void *data, unsigned int len, int pid)
{
int err = 0;
int ret = 0;
@@ -1308,7 +1337,7 @@
}
err = diag_process_apps_pkt(driver->hdlc_buf,
- driver->hdlc_buf_len, info);
+ driver->hdlc_buf_len, pid);
if (err < 0)
goto fail;
} else {
@@ -1325,7 +1354,7 @@
* recovery algorithm. Send an error response if the
* packet is not in expected format.
*/
- diag_send_error_rsp(driver->hdlc_buf, driver->hdlc_buf_len, info);
+ diag_send_error_rsp(driver->hdlc_buf, driver->hdlc_buf_len, pid);
driver->hdlc_buf_len = 0;
end:
mutex_unlock(&driver->diag_hdlc_mutex);
@@ -1422,9 +1451,11 @@
static uint8_t hdlc_reset;
-static void hdlc_reset_timer_start(struct diag_md_session_t *info)
+static void hdlc_reset_timer_start(int pid)
{
+ struct diag_md_session_t *info = NULL;
mutex_lock(&driver->md_session_lock);
+ info = diag_md_session_get_pid(pid);
if (!hdlc_timer_in_progress) {
hdlc_timer_in_progress = 1;
if (info)
@@ -1466,15 +1497,16 @@
}
static void diag_hdlc_start_recovery(unsigned char *buf, int len,
- struct diag_md_session_t *info)
+ int pid)
{
int i;
static uint32_t bad_byte_counter;
unsigned char *start_ptr = NULL;
struct diag_pkt_frame_t *actual_pkt = NULL;
+ struct diag_md_session_t *info = NULL;
hdlc_reset = 1;
- hdlc_reset_timer_start(info);
+ hdlc_reset_timer_start(pid);
actual_pkt = (struct diag_pkt_frame_t *)buf;
for (i = 0; i < len; i++) {
@@ -1494,10 +1526,13 @@
pr_err("diag: In %s, re-enabling HDLC encoding\n",
__func__);
mutex_lock(&driver->hdlc_disable_mutex);
+ mutex_lock(&driver->md_session_lock);
+ info = diag_md_session_get_pid(pid);
if (info)
info->hdlc_disabled = 0;
else
driver->hdlc_disabled = 0;
+ mutex_unlock(&driver->md_session_lock);
mutex_unlock(&driver->hdlc_disable_mutex);
diag_update_md_clients(HDLC_SUPPORT_TYPE);
@@ -1510,12 +1545,11 @@
mutex_lock(&driver->hdlc_recovery_mutex);
driver->incoming_pkt.processing = 0;
mutex_unlock(&driver->hdlc_recovery_mutex);
- diag_process_non_hdlc_pkt(start_ptr, len - i, info);
+ diag_process_non_hdlc_pkt(start_ptr, len - i, pid);
}
}
-void diag_process_non_hdlc_pkt(unsigned char *buf, int len,
- struct diag_md_session_t *info)
+void diag_process_non_hdlc_pkt(unsigned char *buf, int len, int pid)
{
int err = 0;
uint16_t pkt_len = 0;
@@ -1571,11 +1605,11 @@
if (*(uint8_t *)(data_ptr + actual_pkt->length) !=
CONTROL_CHAR) {
mutex_unlock(&driver->hdlc_recovery_mutex);
- diag_hdlc_start_recovery(buf, len, info);
+ diag_hdlc_start_recovery(buf, len, pid);
mutex_lock(&driver->hdlc_recovery_mutex);
}
err = diag_process_apps_pkt(data_ptr,
- actual_pkt->length, info);
+ actual_pkt->length, pid);
if (err) {
pr_err("diag: In %s, unable to process incoming data packet, err: %d\n",
__func__, err);
@@ -1597,8 +1631,8 @@
pkt_len = actual_pkt->length;
if (actual_pkt->start != CONTROL_CHAR) {
- diag_hdlc_start_recovery(buf, len, info);
- diag_send_error_rsp(buf, len, info);
+ diag_hdlc_start_recovery(buf, len, pid);
+ diag_send_error_rsp(buf, len, pid);
goto end;
}
mutex_lock(&driver->hdlc_recovery_mutex);
@@ -1606,7 +1640,7 @@
pr_err("diag: In %s, incoming data is too large for the request buffer %d\n",
__func__, pkt_len);
mutex_unlock(&driver->hdlc_recovery_mutex);
- diag_hdlc_start_recovery(buf, len, info);
+ diag_hdlc_start_recovery(buf, len, pid);
break;
}
if ((pkt_len + header_len) > (len - read_bytes)) {
@@ -1623,13 +1657,13 @@
if (*(uint8_t *)(data_ptr + actual_pkt->length) !=
CONTROL_CHAR) {
mutex_unlock(&driver->hdlc_recovery_mutex);
- diag_hdlc_start_recovery(buf, len, info);
+ diag_hdlc_start_recovery(buf, len, pid);
mutex_lock(&driver->hdlc_recovery_mutex);
}
else
hdlc_reset = 0;
err = diag_process_apps_pkt(data_ptr,
- actual_pkt->length, info);
+ actual_pkt->length, pid);
if (err) {
mutex_unlock(&driver->hdlc_recovery_mutex);
break;
@@ -1648,9 +1682,9 @@
return -EINVAL;
if (!driver->hdlc_disabled)
- diag_process_hdlc_pkt(buf, len, NULL);
+ diag_process_hdlc_pkt(buf, len, 0);
else
- diag_process_non_hdlc_pkt(buf, len, NULL);
+ diag_process_non_hdlc_pkt(buf, len, 0);
diag_mux_queue_read(ctxt);
return 0;
diff --git a/drivers/char/diag/diagfwd.h b/drivers/char/diag/diagfwd.h
index 0e0bf2d..687aeb7 100644
--- a/drivers/char/diag/diagfwd.h
+++ b/drivers/char/diag/diagfwd.h
@@ -30,10 +30,8 @@
int diagfwd_init(void);
void diagfwd_exit(void);
-void diag_process_hdlc_pkt(void *data, unsigned int len,
- struct diag_md_session_t *info);
-void diag_process_non_hdlc_pkt(unsigned char *data, int len,
- struct diag_md_session_t *info);
+void diag_process_hdlc_pkt(void *data, unsigned int len, int pid);
+void diag_process_non_hdlc_pkt(unsigned char *data, int len, int pid);
int chk_config_get_id(void);
int chk_apps_only(void);
int chk_apps_master(void);
@@ -45,10 +43,8 @@
int diag_check_common_cmd(struct diag_pkt_header_t *header);
void diag_update_userspace_clients(unsigned int type);
void diag_update_sleeping_process(int process_id, int data_type);
-int diag_process_apps_pkt(unsigned char *buf, int len,
- struct diag_md_session_t *info);
-void diag_send_error_rsp(unsigned char *buf, int len,
- struct diag_md_session_t *info);
+int diag_process_apps_pkt(unsigned char *buf, int len, int pid);
+void diag_send_error_rsp(unsigned char *buf, int len, int pid);
void diag_update_pkt_buffer(unsigned char *buf, uint32_t len, int type);
int diag_process_stm_cmd(unsigned char *buf, unsigned char *dest_buf);
void diag_md_hdlc_reset_timer_func(unsigned long pid);
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index 162d53f..8d47ee38 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -333,8 +333,13 @@
if (!(driver->close_transport & PERIPHERAL_MASK(peripheral)))
continue;
driver->close_transport ^= PERIPHERAL_MASK(peripheral);
+#ifdef CONFIG_DIAG_USES_SMD
+ transport = driver->feature[peripheral].sockets_enabled ?
+ TRANSPORT_SMD : TRANSPORT_SOCKET;
+#else
transport = driver->feature[peripheral].sockets_enabled ?
TRANSPORT_GLINK : TRANSPORT_SOCKET;
+#endif
diagfwd_close_transport(transport, peripheral);
}
mutex_unlock(&driver->cntl_lock);
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
index 848ad87..4a0ee11 100644
--- a/drivers/char/diag/diagfwd_cntl.h
+++ b/drivers/char/diag/diagfwd_cntl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -77,7 +77,7 @@
#define DISABLE_STM 0
#define ENABLE_STM 1
#define STATUS_STM 2
-
+#define STM_AUTO_QUERY 3
#define UPDATE_PERIPHERAL_STM_STATE 1
#define CLEAR_PERIPHERAL_STM_STATE 2
diff --git a/drivers/char/diag/diagfwd_mhi.c b/drivers/char/diag/diagfwd_mhi.c
index f27f358..f8c3fde 100644
--- a/drivers/char/diag/diagfwd_mhi.c
+++ b/drivers/char/diag/diagfwd_mhi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -552,6 +552,8 @@
struct mhi_result *result = NULL;
struct diag_mhi_ch_t *ch = NULL;
void *buf = NULL;
+ struct diag_mhi_info *mhi_info = NULL;
+ unsigned long flags;
if (!cb_info)
return;
@@ -603,13 +605,6 @@
queue_work(diag_mhi[index].mhi_wq,
&(diag_mhi[index].open_work));
break;
- case MHI_CB_MHI_DISABLED:
- DIAG_LOG(DIAG_DEBUG_BRIDGE,
- "received mhi disabled notifiation port: %d ch: %d\n",
- index, ch->type);
- atomic_set(&(ch->opened), 0);
- __mhi_close(&diag_mhi[index], CHANNELS_CLOSED);
- break;
case MHI_CB_XFER:
/*
* If the channel is a read channel, this is a read
@@ -636,6 +631,24 @@
result->bytes_xferd,
diag_mhi[index].id);
break;
+ case MHI_CB_MHI_DISABLED:
+ case MHI_CB_SYS_ERROR:
+ case MHI_CB_MHI_SHUTDOWN:
+ DIAG_LOG(DIAG_DEBUG_BRIDGE,
+ "received mhi link down cb: %d port: %d ch: %d\n",
+ cb_info->cb_reason, index, ch->type);
+ mhi_info = &diag_mhi[index];
+ if (!mhi_info->enabled)
+ return;
+ spin_lock_irqsave(&mhi_info->lock, flags);
+ mhi_info->enabled = 0;
+ spin_unlock_irqrestore(&mhi_info->lock, flags);
+ atomic_set(&(mhi_info->read_ch.opened), 0);
+ atomic_set(&(mhi_info->write_ch.opened), 0);
+ flush_workqueue(mhi_info->mhi_wq);
+ mhi_buf_tbl_clear(mhi_info);
+ diag_remote_dev_close(mhi_info->dev_id);
+ break;
default:
pr_err("diag: In %s, invalid cb reason 0x%x\n", __func__,
cb_info->cb_reason);
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index c7bd2205..7908b82 100644
--- a/drivers/char/diag/diagfwd_peripheral.c
+++ b/drivers/char/diag/diagfwd_peripheral.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -26,6 +26,7 @@
#include "diag_masks.h"
#include "diag_dci.h"
#include "diagfwd.h"
+#include "diagfwd_smd.h"
#include "diagfwd_socket.h"
#include "diag_mux.h"
#include "diag_ipc_logging.h"
@@ -343,14 +344,13 @@
diag_ws_release();
return;
}
-
- session_info =
- diag_md_session_get_peripheral(peripheral);
+ mutex_lock(&driver->md_session_lock);
+ session_info = diag_md_session_get_peripheral(peripheral);
if (session_info)
hdlc_disabled = session_info->hdlc_disabled;
else
hdlc_disabled = driver->hdlc_disabled;
-
+ mutex_unlock(&driver->md_session_lock);
if (hdlc_disabled) {
/* The data is raw and and on APPS side HDLC is disabled */
if (!buf) {
@@ -633,12 +633,13 @@
mutex_lock(&driver->hdlc_disable_mutex);
mutex_lock(&fwd_info->data_mutex);
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_peripheral(fwd_info->peripheral);
if (session_info)
hdlc_disabled = session_info->hdlc_disabled;
else
hdlc_disabled = driver->hdlc_disabled;
-
+ mutex_unlock(&driver->md_session_lock);
if (!driver->feature[fwd_info->peripheral].encode_hdlc) {
if (fwd_info->buf_1 && fwd_info->buf_1->data == buf) {
temp_buf = fwd_info->buf_1;
@@ -905,6 +906,8 @@
&peripheral_info[TYPE_DCI_CMD][peripheral];
}
+ _diag_smd_init();
+
if (driver->supports_sockets)
diag_socket_init();
diag_glink_init();
@@ -919,6 +922,8 @@
struct diagfwd_info *fwd_info = NULL;
int transport = 0;
+ _diag_smd_exit();
+
diag_socket_exit();
for (peripheral = 0; peripheral < NUM_PERIPHERALS; peripheral++) {
@@ -1049,6 +1054,74 @@
}
}
+#ifdef CONFIG_DIAG_USES_SMD
+void diagfwd_close_transport(uint8_t transport, uint8_t peripheral)
+{
+ struct diagfwd_info *fwd_info = NULL;
+ struct diagfwd_info *dest_info = NULL;
+ int (*init_fn)(uint8_t) = NULL;
+ void (*invalidate_fn)(void *, struct diagfwd_info *) = NULL;
+ int (*check_channel_state)(void *) = NULL;
+ uint8_t transport_open = 0;
+ int i = 0;
+
+ if (peripheral >= NUM_PERIPHERALS)
+ return;
+
+ switch (transport) {
+ case TRANSPORT_SMD:
+ transport_open = TRANSPORT_SOCKET;
+ init_fn = diag_socket_init_peripheral;
+ invalidate_fn = diag_socket_invalidate;
+ check_channel_state = diag_socket_check_state;
+ break;
+ case TRANSPORT_SOCKET:
+ if (peripheral == PERIPHERAL_WDSP) {
+ transport_open = TRANSPORT_GLINK;
+ init_fn = diag_glink_init_peripheral;
+ invalidate_fn = diag_glink_invalidate;
+ check_channel_state = diag_glink_check_state;
+ } else {
+ transport_open = TRANSPORT_SMD;
+ init_fn = diag_smd_init_peripheral;
+ invalidate_fn = diag_smd_invalidate;
+ check_channel_state = diag_smd_check_state;
+ }
+ break;
+ default:
+ return;
+
+ }
+
+ mutex_lock(&driver->diagfwd_channel_mutex[peripheral]);
+ fwd_info = &early_init_info[transport][peripheral];
+ if (fwd_info->p_ops && fwd_info->p_ops->close)
+ fwd_info->p_ops->close(fwd_info->ctxt);
+ fwd_info = &early_init_info[transport_open][peripheral];
+ dest_info = &peripheral_info[TYPE_CNTL][peripheral];
+ dest_info->inited = 1;
+ dest_info->ctxt = fwd_info->ctxt;
+ dest_info->p_ops = fwd_info->p_ops;
+ dest_info->c_ops = fwd_info->c_ops;
+ dest_info->ch_open = fwd_info->ch_open;
+ dest_info->read_bytes = fwd_info->read_bytes;
+ dest_info->write_bytes = fwd_info->write_bytes;
+ dest_info->inited = fwd_info->inited;
+ dest_info->buf_1 = fwd_info->buf_1;
+ dest_info->buf_2 = fwd_info->buf_2;
+ dest_info->transport = fwd_info->transport;
+ invalidate_fn(dest_info->ctxt, dest_info);
+ for (i = 0; i < NUM_WRITE_BUFFERS; i++)
+ dest_info->buf_ptr[i] = fwd_info->buf_ptr[i];
+ if (!check_channel_state(dest_info->ctxt))
+ diagfwd_late_open(dest_info);
+ diagfwd_cntl_open(dest_info);
+ init_fn(peripheral);
+ mutex_unlock(&driver->diagfwd_channel_mutex[peripheral]);
+ diagfwd_queue_read(&peripheral_info[TYPE_DATA][peripheral]);
+ diagfwd_queue_read(&peripheral_info[TYPE_CMD][peripheral]);
+}
+#else
void diagfwd_close_transport(uint8_t transport, uint8_t peripheral)
{
struct diagfwd_info *fwd_info = NULL;
@@ -1108,6 +1181,7 @@
diagfwd_queue_read(&peripheral_info[TYPE_DATA][peripheral]);
diagfwd_queue_read(&peripheral_info[TYPE_CMD][peripheral]);
}
+#endif
void *diagfwd_request_write_buf(struct diagfwd_info *fwd_info)
{
diff --git a/drivers/char/diag/diagfwd_peripheral.h b/drivers/char/diag/diagfwd_peripheral.h
index 6ddce32..4124b17 100644
--- a/drivers/char/diag/diagfwd_peripheral.h
+++ b/drivers/char/diag/diagfwd_peripheral.h
@@ -18,9 +18,16 @@
#define MAX_PERIPHERAL_HDLC_BUF_SZ 65539
#define TRANSPORT_UNKNOWN -1
+#ifdef CONFIG_DIAG_USES_SMD
+#define TRANSPORT_SMD 0
+#define TRANSPORT_SOCKET 1
+#define TRANSPORT_GLINK 2
+#define NUM_TRANSPORT 3
+#else
#define TRANSPORT_SOCKET 0
#define TRANSPORT_GLINK 1
#define NUM_TRANSPORT 2
+#endif
#define NUM_WRITE_BUFFERS 2
#define PERIPHERAL_MASK(x) \
((x == PERIPHERAL_MODEM) ? DIAG_CON_MPSS : \
diff --git a/drivers/char/diag/diagfwd_smd.c b/drivers/char/diag/diagfwd_smd.c
new file mode 100644
index 0000000..27433a7
--- /dev/null
+++ b/drivers/char/diag/diagfwd_smd.c
@@ -0,0 +1,923 @@
+/* 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/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/ratelimit.h>
+#include <linux/workqueue.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <linux/atomic.h>
+#include <linux/diagchar.h>
+#include <linux/of.h>
+#include <linux/kmemleak.h>
+#include "diagchar.h"
+#include "diagfwd.h"
+#include "diagfwd_peripheral.h"
+#include "diagfwd_smd.h"
+#include "diag_ipc_logging.h"
+
+struct diag_smd_info smd_data[NUM_PERIPHERALS] = {
+ {
+ .peripheral = PERIPHERAL_MODEM,
+ .type = TYPE_DATA,
+ .name = "MODEM_DATA"
+ },
+ {
+ .peripheral = PERIPHERAL_LPASS,
+ .type = TYPE_DATA,
+ .name = "LPASS_DATA"
+ },
+ {
+ .peripheral = PERIPHERAL_WCNSS,
+ .type = TYPE_DATA,
+ .name = "WCNSS_DATA"
+ },
+ {
+ .peripheral = PERIPHERAL_SENSORS,
+ .type = TYPE_DATA,
+ .name = "SENSORS_DATA"
+ },
+ {
+ .peripheral = PERIPHERAL_WDSP,
+ .type = TYPE_DATA,
+ .name = "DIAG_DATA"
+ },
+ {
+ .peripheral = PERIPHERAL_CDSP,
+ .type = TYPE_DATA,
+ .name = "CDSP_DATA"
+ }
+};
+
+struct diag_smd_info smd_cntl[NUM_PERIPHERALS] = {
+ {
+ .peripheral = PERIPHERAL_MODEM,
+ .type = TYPE_CNTL,
+ .name = "MODEM_CNTL"
+ },
+ {
+ .peripheral = PERIPHERAL_LPASS,
+ .type = TYPE_CNTL,
+ .name = "LPASS_CNTL"
+ },
+ {
+ .peripheral = PERIPHERAL_WCNSS,
+ .type = TYPE_CNTL,
+ .name = "WCNSS_CNTL"
+ },
+ {
+ .peripheral = PERIPHERAL_SENSORS,
+ .type = TYPE_CNTL,
+ .name = "SENSORS_CNTL"
+ },
+ {
+ .peripheral = PERIPHERAL_WDSP,
+ .type = TYPE_CNTL,
+ .name = "DIAG_CTRL"
+ },
+ {
+ .peripheral = PERIPHERAL_CDSP,
+ .type = TYPE_CNTL,
+ .name = "CDSP_CNTL"
+ }
+};
+
+struct diag_smd_info smd_dci[NUM_PERIPHERALS] = {
+ {
+ .peripheral = PERIPHERAL_MODEM,
+ .type = TYPE_DCI,
+ .name = "MODEM_DCI"
+ },
+ {
+ .peripheral = PERIPHERAL_LPASS,
+ .type = TYPE_DCI,
+ .name = "LPASS_DCI"
+ },
+ {
+ .peripheral = PERIPHERAL_WCNSS,
+ .type = TYPE_DCI,
+ .name = "WCNSS_DCI"
+ },
+ {
+ .peripheral = PERIPHERAL_SENSORS,
+ .type = TYPE_DCI,
+ .name = "SENSORS_DCI"
+ },
+ {
+ .peripheral = PERIPHERAL_WDSP,
+ .type = TYPE_DCI,
+ .name = "DIAG_DCI_DATA"
+ },
+ {
+ .peripheral = PERIPHERAL_CDSP,
+ .type = TYPE_DCI,
+ .name = "CDSP_DCI"
+ }
+};
+
+struct diag_smd_info smd_cmd[NUM_PERIPHERALS] = {
+ {
+ .peripheral = PERIPHERAL_MODEM,
+ .type = TYPE_CMD,
+ .name = "MODEM_CMD"
+ },
+ {
+ .peripheral = PERIPHERAL_LPASS,
+ .type = TYPE_CMD,
+ .name = "LPASS_CMD"
+ },
+ {
+ .peripheral = PERIPHERAL_WCNSS,
+ .type = TYPE_CMD,
+ .name = "WCNSS_CMD"
+ },
+ {
+ .peripheral = PERIPHERAL_SENSORS,
+ .type = TYPE_CMD,
+ .name = "SENSORS_CMD"
+ },
+ {
+ .peripheral = PERIPHERAL_WDSP,
+ .type = TYPE_CMD,
+ .name = "DIAG_CMD"
+ },
+ {
+ .peripheral = PERIPHERAL_CDSP,
+ .type = TYPE_CMD,
+ .name = "CDSP_CMD"
+ }
+};
+
+struct diag_smd_info smd_dci_cmd[NUM_PERIPHERALS] = {
+ {
+ .peripheral = PERIPHERAL_MODEM,
+ .type = TYPE_DCI_CMD,
+ .name = "MODEM_DCI_CMD"
+ },
+ {
+ .peripheral = PERIPHERAL_LPASS,
+ .type = TYPE_DCI_CMD,
+ .name = "LPASS_DCI_CMD"
+ },
+ {
+ .peripheral = PERIPHERAL_WCNSS,
+ .type = TYPE_DCI_CMD,
+ .name = "WCNSS_DCI_CMD"
+ },
+ {
+ .peripheral = PERIPHERAL_SENSORS,
+ .type = TYPE_DCI_CMD,
+ .name = "SENSORS_DCI_CMD"
+ },
+ {
+ .peripheral = PERIPHERAL_WDSP,
+ .type = TYPE_DCI_CMD,
+ .name = "DIAG_DCI_CMD"
+ },
+ {
+ .peripheral = PERIPHERAL_CDSP,
+ .type = TYPE_DCI_CMD,
+ .name = "CDSP_DCI_CMD"
+ }
+};
+
+static void diag_state_open_smd(void *ctxt);
+static void diag_state_close_smd(void *ctxt);
+static void smd_notify(void *ctxt, unsigned int event);
+static int diag_smd_write(void *ctxt, unsigned char *buf, int len);
+static int diag_smd_read(void *ctxt, unsigned char *buf, int buf_len);
+static void diag_smd_queue_read(void *ctxt);
+
+static struct diag_peripheral_ops smd_ops = {
+ .open = diag_state_open_smd,
+ .close = diag_state_close_smd,
+ .write = diag_smd_write,
+ .read = diag_smd_read,
+ .queue_read = diag_smd_queue_read
+};
+
+static void diag_state_open_smd(void *ctxt)
+{
+ struct diag_smd_info *smd_info = NULL;
+
+ if (!ctxt)
+ return;
+
+ smd_info = (struct diag_smd_info *)(ctxt);
+ atomic_set(&smd_info->diag_state, 1);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "%s setting diag state to 1", smd_info->name);
+}
+
+static void diag_state_close_smd(void *ctxt)
+{
+ struct diag_smd_info *smd_info = NULL;
+
+ if (!ctxt)
+ return;
+
+ smd_info = (struct diag_smd_info *)(ctxt);
+ atomic_set(&smd_info->diag_state, 0);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "%s setting diag state to 0", smd_info->name);
+ wake_up_interruptible(&smd_info->read_wait_q);
+ flush_workqueue(smd_info->wq);
+}
+
+static int smd_channel_probe(struct platform_device *pdev, uint8_t type)
+{
+ int r = 0;
+ int index = -1;
+ const char *channel_name = NULL;
+ struct diag_smd_info *smd_info = NULL;
+
+ switch (pdev->id) {
+ case SMD_APPS_MODEM:
+ index = PERIPHERAL_MODEM;
+ break;
+ case SMD_APPS_QDSP:
+ index = PERIPHERAL_LPASS;
+ break;
+ case SMD_APPS_WCNSS:
+ index = PERIPHERAL_WCNSS;
+ break;
+ case SMD_APPS_DSPS:
+ index = PERIPHERAL_SENSORS;
+ break;
+ default:
+ pr_debug("diag: In %s Received probe for invalid index %d",
+ __func__, pdev->id);
+ return -EINVAL;
+ }
+
+ switch (type) {
+ case TYPE_DATA:
+ smd_info = &smd_data[index];
+ channel_name = "DIAG";
+ break;
+ case TYPE_CNTL:
+ smd_info = &smd_cntl[index];
+ channel_name = "DIAG_CNTL";
+ break;
+ case TYPE_CMD:
+ smd_info = &smd_cmd[index];
+ channel_name = "DIAG_CMD";
+ break;
+ case TYPE_DCI:
+ smd_info = &smd_dci[index];
+ channel_name = "DIAG_2";
+ break;
+ case TYPE_DCI_CMD:
+ smd_info = &smd_dci_cmd[index];
+ channel_name = "DIAG_2_CMD";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (index == PERIPHERAL_WCNSS && type == TYPE_DATA)
+ channel_name = "APPS_RIVA_DATA";
+ else if (index == PERIPHERAL_WCNSS && type == TYPE_CNTL)
+ channel_name = "APPS_RIVA_CTRL";
+
+ if (!channel_name || !smd_info)
+ return -EIO;
+
+ r = smd_named_open_on_edge(channel_name, pdev->id, &smd_info->hdl,
+ smd_info, smd_notify);
+
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pr_debug("diag: In %s, SMD port probed %s, id = %d, r = %d\n",
+ __func__, smd_info->name, pdev->id, r);
+
+ return 0;
+}
+
+static int smd_data_probe(struct platform_device *pdev)
+{
+ return smd_channel_probe(pdev, TYPE_DATA);
+}
+
+static int smd_cntl_probe(struct platform_device *pdev)
+{
+ return smd_channel_probe(pdev, TYPE_CNTL);
+}
+
+static int smd_cmd_probe(struct platform_device *pdev)
+{
+ return smd_channel_probe(pdev, TYPE_CMD);
+}
+
+static int smd_dci_probe(struct platform_device *pdev)
+{
+ return smd_channel_probe(pdev, TYPE_DCI);
+}
+
+static int smd_dci_cmd_probe(struct platform_device *pdev)
+{
+ return smd_channel_probe(pdev, TYPE_DCI_CMD);
+}
+
+static int smd_runtime_suspend(struct device *dev)
+{
+ dev_dbg(dev, "pm_runtime: suspending...\n");
+ return 0;
+}
+
+static int smd_runtime_resume(struct device *dev)
+{
+ dev_dbg(dev, "pm_runtime: resuming...\n");
+ return 0;
+}
+
+static const struct dev_pm_ops smd_dev_pm_ops = {
+ .runtime_suspend = smd_runtime_suspend,
+ .runtime_resume = smd_runtime_resume,
+};
+
+static struct platform_driver diag_smd_ch_driver = {
+ .probe = smd_data_probe,
+ .driver = {
+ .name = "DIAG",
+ .owner = THIS_MODULE,
+ .pm = &smd_dev_pm_ops,
+ },
+};
+
+static struct platform_driver diag_smd_lite_driver = {
+ .probe = smd_data_probe,
+ .driver = {
+ .name = "APPS_RIVA_DATA",
+ .owner = THIS_MODULE,
+ .pm = &smd_dev_pm_ops,
+ },
+};
+
+static struct platform_driver diag_smd_cntl_driver = {
+ .probe = smd_cntl_probe,
+ .driver = {
+ .name = "DIAG_CNTL",
+ .owner = THIS_MODULE,
+ .pm = &smd_dev_pm_ops,
+ },
+};
+
+static struct platform_driver diag_smd_lite_cntl_driver = {
+ .probe = smd_cntl_probe,
+ .driver = {
+ .name = "APPS_RIVA_CTRL",
+ .owner = THIS_MODULE,
+ .pm = &smd_dev_pm_ops,
+ },
+};
+
+static struct platform_driver diag_smd_lite_cmd_driver = {
+ .probe = smd_cmd_probe,
+ .driver = {
+ .name = "DIAG_CMD",
+ .owner = THIS_MODULE,
+ .pm = &smd_dev_pm_ops,
+ }
+};
+
+static struct platform_driver diag_smd_dci_driver = {
+ .probe = smd_dci_probe,
+ .driver = {
+ .name = "DIAG_2",
+ .owner = THIS_MODULE,
+ .pm = &smd_dev_pm_ops,
+ },
+};
+
+static struct platform_driver diag_smd_dci_cmd_driver = {
+ .probe = smd_dci_cmd_probe,
+ .driver = {
+ .name = "DIAG_2_CMD",
+ .owner = THIS_MODULE,
+ .pm = &smd_dev_pm_ops,
+ },
+};
+
+static void smd_open_work_fn(struct work_struct *work)
+{
+ struct diag_smd_info *smd_info = container_of(work,
+ struct diag_smd_info,
+ open_work);
+ if (!smd_info->inited)
+ return;
+
+ diagfwd_channel_open(smd_info->fwd_ctxt);
+ diagfwd_late_open(smd_info->fwd_ctxt);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s exiting\n",
+ smd_info->name);
+}
+
+static void smd_close_work_fn(struct work_struct *work)
+{
+ struct diag_smd_info *smd_info = container_of(work,
+ struct diag_smd_info,
+ close_work);
+ if (!smd_info->inited)
+ return;
+
+ diagfwd_channel_close(smd_info->fwd_ctxt);
+ wake_up_interruptible(&smd_info->read_wait_q);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s exiting\n",
+ smd_info->name);
+}
+
+static void smd_read_work_fn(struct work_struct *work)
+{
+ struct diag_smd_info *smd_info = container_of(work,
+ struct diag_smd_info,
+ read_work);
+ if (!smd_info->inited) {
+ diag_ws_release();
+ return;
+ }
+
+ diagfwd_channel_read(smd_info->fwd_ctxt);
+}
+
+static void diag_smd_late_init_work_fn(struct work_struct *work)
+{
+ struct diag_smd_info *smd_info = container_of(work,
+ struct diag_smd_info,
+ late_init_work);
+ if (!smd_info || !smd_info->hdl)
+ return;
+ diagfwd_channel_open(smd_info->fwd_ctxt);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "smd late init p: %d t: %d\n",
+ smd_info->peripheral, smd_info->type);
+}
+
+static void diag_smd_queue_read(void *ctxt)
+{
+ struct diag_smd_info *smd_info = NULL;
+
+ if (!ctxt)
+ return;
+
+ smd_info = (struct diag_smd_info *)ctxt;
+ if (smd_info->inited && atomic_read(&smd_info->opened) &&
+ smd_info->hdl) {
+ wake_up_interruptible(&smd_info->read_wait_q);
+ queue_work(smd_info->wq, &(smd_info->read_work));
+ }
+}
+int diag_smd_check_state(void *ctxt)
+{
+ struct diag_smd_info *info = NULL;
+
+ if (!ctxt)
+ return 0;
+
+ info = (struct diag_smd_info *)ctxt;
+ return (int)(atomic_read(&info->diag_state));
+}
+void diag_smd_invalidate(void *ctxt, struct diagfwd_info *fwd_ctxt)
+{
+ struct diag_smd_info *smd_info = NULL;
+ void *prev = NULL;
+
+ if (!ctxt || !fwd_ctxt)
+ return;
+
+ smd_info = (struct diag_smd_info *)ctxt;
+ prev = smd_info->fwd_ctxt;
+ smd_info->fwd_ctxt = fwd_ctxt;
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s prev: %pK fwd_ctxt: %pK\n",
+ smd_info->name, prev, smd_info->fwd_ctxt);
+}
+
+static void __diag_smd_init(struct diag_smd_info *smd_info)
+{
+ char wq_name[DIAG_SMD_NAME_SZ + 10];
+
+ if (!smd_info)
+ return;
+
+ init_waitqueue_head(&smd_info->read_wait_q);
+ mutex_init(&smd_info->lock);
+ strlcpy(wq_name, "DIAG_SMD_", 10);
+ strlcat(wq_name, smd_info->name, sizeof(smd_info->name));
+ smd_info->wq = create_singlethread_workqueue(wq_name);
+ if (!smd_info->wq) {
+ pr_err("diag: In %s, unable to create workqueue for smd channel %s\n",
+ __func__, smd_info->name);
+ return;
+ }
+ INIT_WORK(&(smd_info->open_work), smd_open_work_fn);
+ INIT_WORK(&(smd_info->close_work), smd_close_work_fn);
+ INIT_WORK(&(smd_info->read_work), smd_read_work_fn);
+ INIT_WORK(&(smd_info->late_init_work), diag_smd_late_init_work_fn);
+ smd_info->fifo_size = 0;
+ smd_info->hdl = NULL;
+ smd_info->fwd_ctxt = NULL;
+ atomic_set(&smd_info->opened, 0);
+ atomic_set(&smd_info->diag_state, 0);
+
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s initialized fwd_ctxt: %pK\n",
+ smd_info->name, smd_info->fwd_ctxt);
+}
+
+int diag_smd_init(void)
+{
+ uint8_t peripheral;
+ struct diag_smd_info *smd_info = NULL;
+
+ for (peripheral = 0; peripheral < NUM_PERIPHERALS; peripheral++) {
+ if (peripheral == PERIPHERAL_WDSP)
+ continue;
+ smd_info = &smd_cntl[peripheral];
+ __diag_smd_init(smd_info);
+ diagfwd_cntl_register(TRANSPORT_SMD, smd_info->peripheral,
+ (void *)smd_info, &smd_ops,
+ &smd_info->fwd_ctxt);
+ smd_info->inited = 1;
+ __diag_smd_init(&smd_data[peripheral]);
+ __diag_smd_init(&smd_cmd[peripheral]);
+ __diag_smd_init(&smd_dci[peripheral]);
+ __diag_smd_init(&smd_dci_cmd[peripheral]);
+ }
+
+ platform_driver_register(&diag_smd_cntl_driver);
+ platform_driver_register(&diag_smd_lite_cntl_driver);
+ platform_driver_register(&diag_smd_ch_driver);
+ platform_driver_register(&diag_smd_lite_driver);
+ platform_driver_register(&diag_smd_lite_cmd_driver);
+ platform_driver_register(&diag_smd_dci_driver);
+ platform_driver_register(&diag_smd_dci_cmd_driver);
+
+ return 0;
+}
+
+static void smd_late_init(struct diag_smd_info *smd_info)
+{
+ struct diagfwd_info *fwd_info = NULL;
+
+ if (!smd_info)
+ return;
+
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s entering\n",
+ smd_info->name);
+
+ diagfwd_register(TRANSPORT_SMD, smd_info->peripheral, smd_info->type,
+ (void *)smd_info, &smd_ops, &smd_info->fwd_ctxt);
+ fwd_info = smd_info->fwd_ctxt;
+ smd_info->inited = 1;
+ /*
+ * The channel is already open by the probe call as a result of other
+ * peripheral. Inform the diag fwd layer that the channel is open.
+ */
+ if (atomic_read(&smd_info->opened))
+ queue_work(smd_info->wq, &(smd_info->late_init_work));
+
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s exiting\n",
+ smd_info->name);
+}
+
+int diag_smd_init_peripheral(uint8_t peripheral)
+{
+ if (peripheral >= NUM_PERIPHERALS) {
+ pr_err("diag: In %s, invalid peripheral %d\n",
+ __func__, peripheral);
+ return -EINVAL;
+ }
+
+ smd_late_init(&smd_data[peripheral]);
+ smd_late_init(&smd_dci[peripheral]);
+ smd_late_init(&smd_cmd[peripheral]);
+ smd_late_init(&smd_dci_cmd[peripheral]);
+
+ return 0;
+}
+
+static void __diag_smd_exit(struct diag_smd_info *smd_info)
+{
+ if (!smd_info)
+ return;
+
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s entering\n",
+ smd_info->name);
+
+ diagfwd_deregister(smd_info->peripheral, smd_info->type,
+ (void *)smd_info);
+ smd_info->fwd_ctxt = NULL;
+ smd_info->hdl = NULL;
+ if (smd_info->wq)
+ destroy_workqueue(smd_info->wq);
+
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s exiting\n",
+ smd_info->name);
+}
+
+void diag_smd_early_exit(void)
+{
+ int i = 0;
+
+ for (i = 0; i < NUM_PERIPHERALS; i++) {
+ if (i == PERIPHERAL_WDSP)
+ continue;
+ __diag_smd_exit(&smd_cntl[i]);
+ }
+
+ platform_driver_unregister(&diag_smd_cntl_driver);
+ platform_driver_unregister(&diag_smd_lite_cntl_driver);
+}
+
+void diag_smd_exit(void)
+{
+ int i = 0;
+
+ for (i = 0; i < NUM_PERIPHERALS; i++) {
+ if (i == PERIPHERAL_WDSP)
+ continue;
+ __diag_smd_exit(&smd_data[i]);
+ __diag_smd_exit(&smd_cmd[i]);
+ __diag_smd_exit(&smd_dci[i]);
+ __diag_smd_exit(&smd_dci_cmd[i]);
+ }
+
+ platform_driver_unregister(&diag_smd_ch_driver);
+ platform_driver_unregister(&diag_smd_lite_driver);
+ platform_driver_unregister(&diag_smd_lite_cmd_driver);
+ platform_driver_unregister(&diag_smd_dci_driver);
+ platform_driver_unregister(&diag_smd_dci_cmd_driver);
+}
+
+static int diag_smd_write_ext(struct diag_smd_info *smd_info,
+ unsigned char *buf, int len)
+{
+ int err = 0;
+ int offset = 0;
+ int write_len = 0;
+ int retry_count = 0;
+ int max_retries = 3;
+ uint8_t avail = 0;
+
+ if (!smd_info || !buf || len <= 0) {
+ pr_err_ratelimited("diag: In %s, invalid params, smd_info: %pK, buf: %pK, len: %d\n",
+ __func__, smd_info, buf, len);
+ return -EINVAL;
+ }
+
+ if (!smd_info->inited || !smd_info->hdl ||
+ !atomic_read(&smd_info->opened))
+ return -ENODEV;
+
+ mutex_lock(&smd_info->lock);
+ err = smd_write_start(smd_info->hdl, len);
+ if (err) {
+ pr_err_ratelimited("diag: In %s, error calling smd_write_start, peripheral: %d, err: %d\n",
+ __func__, smd_info->peripheral, err);
+ goto fail;
+ }
+
+ while (offset < len) {
+ retry_count = 0;
+ do {
+ if (smd_write_segment_avail(smd_info->hdl)) {
+ avail = 1;
+ break;
+ }
+ /*
+ * The channel maybe busy - the FIFO can be full. Retry
+ * after sometime. The value of 10000 was chosen
+ * emprically as the optimal value for the peripherals
+ * to read data from the SMD channel.
+ */
+ usleep_range(10000, 10100);
+ retry_count++;
+ } while (retry_count < max_retries);
+
+ if (!avail) {
+ err = -EAGAIN;
+ goto fail;
+ }
+
+ write_len = smd_write_segment(smd_info->hdl, buf + offset,
+ (len - offset));
+ offset += write_len;
+ write_len = 0;
+ }
+
+ err = smd_write_end(smd_info->hdl);
+ if (err) {
+ pr_err_ratelimited("diag: In %s, error calling smd_write_end, peripheral: %d, err: %d\n",
+ __func__, smd_info->peripheral, err);
+ goto fail;
+ }
+
+fail:
+ mutex_unlock(&smd_info->lock);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "%s wrote to channel, write_len: %d, err: %d\n",
+ smd_info->name, offset, err);
+ return err;
+}
+
+static int diag_smd_write(void *ctxt, unsigned char *buf, int len)
+{
+ int write_len = 0;
+ int retry_count = 0;
+ int max_retries = 3;
+ struct diag_smd_info *smd_info = NULL;
+
+ if (!ctxt || !buf)
+ return -EIO;
+
+ smd_info = (struct diag_smd_info *)ctxt;
+ if (!smd_info || !buf || len <= 0) {
+ pr_err_ratelimited("diag: In %s, invalid params, smd_info: %pK, buf: %pK, len: %d\n",
+ __func__, smd_info, buf, len);
+ return -EINVAL;
+ }
+
+ if (!smd_info->inited || !smd_info->hdl ||
+ !atomic_read(&smd_info->opened))
+ return -ENODEV;
+
+ if (len > smd_info->fifo_size)
+ return diag_smd_write_ext(smd_info, buf, len);
+
+ do {
+ mutex_lock(&smd_info->lock);
+ write_len = smd_write(smd_info->hdl, buf, len);
+ mutex_unlock(&smd_info->lock);
+ if (write_len == len)
+ break;
+ /*
+ * The channel maybe busy - the FIFO can be full. Retry after
+ * sometime. The value of 10000 was chosen emprically as the
+ * optimal value for the peripherals to read data from the SMD
+ * channel.
+ */
+ usleep_range(10000, 10100);
+ retry_count++;
+ } while (retry_count < max_retries);
+
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s wrote to channel, write_len: %d\n",
+ smd_info->name, write_len);
+
+ if (write_len != len)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int diag_smd_read(void *ctxt, unsigned char *buf, int buf_len)
+{
+ int pkt_len = 0;
+ int err = 0;
+ int total_recd_partial = 0;
+ int total_recd = 0;
+ uint8_t buf_full = 0;
+ unsigned char *temp_buf = NULL;
+ uint32_t read_len = 0;
+ struct diag_smd_info *smd_info = NULL;
+
+ if (!ctxt || !buf || buf_len <= 0)
+ return -EIO;
+
+ smd_info = (struct diag_smd_info *)ctxt;
+ if (!smd_info->hdl || !smd_info->inited ||
+ !atomic_read(&smd_info->opened))
+ return -EIO;
+
+ /*
+ * Always try to read the data if notification is received from smd
+ * In case if packet size is 0 release the wake source hold earlier
+ */
+ err = wait_event_interruptible(smd_info->read_wait_q,
+ (smd_info->hdl != NULL) &&
+ (atomic_read(&smd_info->opened) == 1));
+ if (err) {
+ diagfwd_channel_read_done(smd_info->fwd_ctxt, buf, 0);
+ return -ERESTARTSYS;
+ }
+
+ /*
+ * Reset the buffers. Also release the wake source hold earlier.
+ */
+ if (atomic_read(&smd_info->diag_state) == 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "%s closing read thread. diag state is closed\n",
+ smd_info->name);
+ diagfwd_channel_read_done(smd_info->fwd_ctxt, buf, 0);
+ return 0;
+ }
+
+ if (!smd_info->hdl || !atomic_read(&smd_info->opened)) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "%s stopping read, hdl: %pK, opened: %d\n",
+ smd_info->name, smd_info->hdl,
+ atomic_read(&smd_info->opened));
+ goto fail_return;
+ }
+
+ do {
+ total_recd_partial = 0;
+ temp_buf = buf + total_recd;
+ pkt_len = smd_cur_packet_size(smd_info->hdl);
+ if (pkt_len <= 0)
+ break;
+
+ if (total_recd + pkt_len > buf_len) {
+ buf_full = 1;
+ break;
+ }
+
+ while (total_recd_partial < pkt_len) {
+ read_len = smd_read_avail(smd_info->hdl);
+ if (!read_len) {
+ err = wait_event_interruptible(smd_info->
+ read_wait_q,
+ ((atomic_read(&smd_info->opened)) &&
+ smd_read_avail(smd_info->hdl)));
+ if (err)
+ goto fail_return;
+
+ if (!smd_info->hdl ||
+ !atomic_read(&smd_info->opened)) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+ "%s exiting from wait",
+ smd_info->name);
+ goto fail_return;
+ }
+ }
+
+ if (pkt_len < read_len)
+ goto fail_return;
+
+ smd_read(smd_info->hdl, temp_buf, read_len);
+ total_recd_partial += read_len;
+ total_recd += read_len;
+ temp_buf += read_len;
+ }
+ } while (pkt_len > 0);
+
+ if ((smd_info->type == TYPE_DATA && pkt_len) || buf_full)
+ err = queue_work(smd_info->wq, &(smd_info->read_work));
+
+ if (total_recd > 0) {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s read total bytes: %d\n",
+ smd_info->name, total_recd);
+ diagfwd_channel_read_done(smd_info->fwd_ctxt, buf, total_recd);
+ } else {
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s error in read, err: %d\n",
+ smd_info->name, total_recd);
+ goto fail_return;
+ }
+ return 0;
+
+fail_return:
+ diagfwd_channel_read_done(smd_info->fwd_ctxt, buf, 0);
+ return -EINVAL;
+}
+
+static void smd_notify(void *ctxt, unsigned int event)
+{
+ struct diag_smd_info *smd_info = NULL;
+
+ smd_info = (struct diag_smd_info *)ctxt;
+ if (!smd_info)
+ return;
+
+ switch (event) {
+ case SMD_EVENT_OPEN:
+ atomic_set(&smd_info->opened, 1);
+ smd_info->fifo_size = smd_write_avail(smd_info->hdl);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s channel opened\n",
+ smd_info->name);
+ queue_work(smd_info->wq, &(smd_info->open_work));
+ break;
+ case SMD_EVENT_CLOSE:
+ atomic_set(&smd_info->opened, 0);
+ DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s channel closed\n",
+ smd_info->name);
+ queue_work(smd_info->wq, &(smd_info->close_work));
+ break;
+ case SMD_EVENT_DATA:
+ diag_ws_on_notify();
+ queue_work(smd_info->wq, &(smd_info->read_work));
+ break;
+ }
+
+ wake_up_interruptible(&smd_info->read_wait_q);
+}
+
diff --git a/drivers/char/diag/diagfwd_smd.h b/drivers/char/diag/diagfwd_smd.h
new file mode 100644
index 0000000..388014e
--- /dev/null
+++ b/drivers/char/diag/diagfwd_smd.h
@@ -0,0 +1,59 @@
+/* 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 DIAGFWD_SMD_H
+#define DIAGFWD_SMD_H
+
+#define DIAG_SMD_NAME_SZ 24
+#define SMD_DRAIN_BUF_SIZE 4096
+
+struct diag_smd_info {
+ uint8_t peripheral;
+ uint8_t type;
+ uint8_t inited;
+ atomic_t opened;
+ atomic_t diag_state;
+ uint32_t fifo_size;
+ smd_channel_t *hdl;
+ char name[DIAG_SMD_NAME_SZ];
+ struct mutex lock;
+ wait_queue_head_t read_wait_q;
+ struct workqueue_struct *wq;
+ struct work_struct open_work;
+ struct work_struct close_work;
+ struct work_struct read_work;
+ struct work_struct late_init_work;
+ struct diagfwd_info *fwd_ctxt;
+};
+
+extern struct diag_smd_info smd_data[NUM_PERIPHERALS];
+extern struct diag_smd_info smd_cntl[NUM_PERIPHERALS];
+extern struct diag_smd_info smd_dci[NUM_PERIPHERALS];
+extern struct diag_smd_info smd_cmd[NUM_PERIPHERALS];
+extern struct diag_smd_info smd_dci_cmd[NUM_PERIPHERALS];
+
+int diag_smd_init_peripheral(uint8_t peripheral);
+void diag_smd_exit(void);
+int diag_smd_init(void);
+void diag_smd_early_exit(void);
+void diag_smd_invalidate(void *ctxt, struct diagfwd_info *fwd_ctxt);
+int diag_smd_check_state(void *ctxt);
+
+#ifndef CONFIG_DIAG_USES_SMD
+#define _diag_smd_exit(void)
+#define _diag_smd_init(void)
+#else
+#define _diag_smd_exit(void) diag_smd_exit(void)
+#define _diag_smd_init(void) diag_smd_init(void)
+#endif
+
+#endif
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 020e8ad..929aef0 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
* Copyright (C) 2011-2012 Linaro Ltd <mturquette@linaro.org>
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -1872,6 +1872,8 @@
if (core->flags & CLK_RECALC_NEW_RATES)
(void)clk_calc_new_rates(core, core->new_rate);
+ if (core->flags & CLK_CHILD_NO_RATE_PROP)
+ return rc;
/*
* Use safe iteration, as change_rate can actually swap parents
* for certain clock types.
diff --git a/drivers/clk/msm/Kconfig b/drivers/clk/msm/Kconfig
index 16f8c32..d881a5d 100644
--- a/drivers/clk/msm/Kconfig
+++ b/drivers/clk/msm/Kconfig
@@ -16,3 +16,5 @@
Generate clock data structures from definitions found in
device tree.
+source "drivers/clk/msm/mdss/Kconfig"
+
diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile
index 4176553..d264e79 100644
--- a/drivers/clk/msm/Makefile
+++ b/drivers/clk/msm/Makefile
@@ -17,3 +17,6 @@
obj-$(CONFIG_ARCH_MSM8953) += clock-cpu-8953.o
obj-$(CONFIG_ARCH_MSM8953) += clock-rcgwr.o
endif
+
+obj-y += mdss/
+
diff --git a/drivers/clk/msm/clock-gcc-8953.c b/drivers/clk/msm/clock-gcc-8953.c
index e25da83..fd80b56 100644
--- a/drivers/clk/msm/clock-gcc-8953.c
+++ b/drivers/clk/msm/clock-gcc-8953.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -3822,6 +3822,7 @@
static const struct of_device_id msm_clock_gcc_match_table[] = {
{ .compatible = "qcom,gcc-8953" },
+ { .compatible = "qcom,gcc-sdm632" },
{},
};
@@ -3871,6 +3872,7 @@
static const struct of_device_id msm_clock_debug_match_table[] = {
{ .compatible = "qcom,cc-debug-8953" },
+ { .compatible = "qcom,cc-debug-sdm632" },
{}
};
@@ -3983,6 +3985,7 @@
static const struct of_device_id msm_clock_mdss_match_table[] = {
{ .compatible = "qcom,gcc-mdss-8953" },
+ { .compatible = "qcom,gcc-mdss-sdm632" },
{}
};
@@ -4072,8 +4075,17 @@
struct resource *res;
int ret;
u32 regval;
+ struct clk *xo_clk;
bool compat_bin = false;
+ /* Require the GCC-RPM-XO clock to be registered first */
+ xo_clk = devm_clk_get(&pdev->dev, "xo");
+ if (IS_ERR(xo_clk)) {
+ if (PTR_ERR(xo_clk) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Unable to get xo clock\n");
+ return PTR_ERR(xo_clk);
+ }
+
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cc_base");
if (!res) {
dev_err(&pdev->dev, "Register base not defined\n");
@@ -4122,6 +4134,7 @@
static const struct of_device_id msm_clock_gfx_match_table[] = {
{ .compatible = "qcom,gcc-gfx-8953" },
{ .compatible = "qcom,gcc-gfx-sdm450" },
+ { .compatible = "qcom,gcc-gfx-sdm632" },
{}
};
diff --git a/drivers/clk/msm/mdss/Kconfig b/drivers/clk/msm/mdss/Kconfig
new file mode 100644
index 0000000..229780e
--- /dev/null
+++ b/drivers/clk/msm/mdss/Kconfig
@@ -0,0 +1,6 @@
+config MSM_MDSS_PLL
+ bool "MDSS pll programming"
+ ---help---
+ It provides support for DSI, eDP and HDMI interface pll programming on MDSS
+ hardware. It also handles the pll specific resources and turn them on/off when
+ mdss pll client tries to enable/disable pll clocks.
diff --git a/drivers/clk/msm/mdss/Makefile b/drivers/clk/msm/mdss/Makefile
new file mode 100644
index 0000000..6285714
--- /dev/null
+++ b/drivers/clk/msm/mdss/Makefile
@@ -0,0 +1,7 @@
+obj-$(CONFIG_MSM_MDSS_PLL) += mdss-pll-util.o
+obj-$(CONFIG_MSM_MDSS_PLL) += mdss-pll.o
+obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-util.o
+obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-28lpm.o
+obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-8996.o
+obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-8996-util.o
+obj-$(CONFIG_MSM_MDSS_PLL) += mdss-hdmi-pll-8996.o
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-28lpm.c b/drivers/clk/msm/mdss/mdss-dsi-pll-28lpm.c
new file mode 100644
index 0000000..17ff52e
--- /dev/null
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-28lpm.c
@@ -0,0 +1,522 @@
+/* Copyright (c) 2012-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/clk/msm-clk.h>
+#include <linux/clk/msm-clock-generic.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <dt-bindings/clock/msm-clocks-8952.h>
+
+#include "mdss-pll.h"
+#include "mdss-dsi-pll.h"
+
+#define VCO_DELAY_USEC 1000
+
+static struct clk_div_ops fixed_2div_ops;
+static struct clk_ops byte_mux_clk_ops;
+static struct clk_ops pixel_clk_src_ops;
+static struct clk_ops byte_clk_src_ops;
+static struct clk_ops analog_postdiv_clk_ops;
+static struct lpfr_cfg lpfr_lut_struct[] = {
+ {479500000, 8},
+ {480000000, 11},
+ {575500000, 8},
+ {576000000, 12},
+ {610500000, 8},
+ {659500000, 9},
+ {671500000, 10},
+ {672000000, 14},
+ {708500000, 10},
+ {750000000, 11},
+};
+
+static int vco_set_rate_lpm(struct clk *c, unsigned long rate)
+{
+ int rc;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *dsi_pll_res = vco->priv;
+
+ rc = mdss_pll_resource_enable(dsi_pll_res, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ /*
+ * DSI PLL software reset. Add HW recommended delays after toggling
+ * the software reset bit off and back on.
+ */
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x01);
+ udelay(1000);
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x00);
+ udelay(1000);
+
+ rc = vco_set_rate(vco, rate);
+
+ mdss_pll_resource_enable(dsi_pll_res, false);
+ return rc;
+}
+
+static void dsi_pll_sw_reset_8916(struct mdss_pll_resources *dsi_pll_res)
+{
+ /*
+ * DSI PLL software reset. Add HW recommended delays after toggling
+ * the software reset bit off and back on.
+ */
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x01);
+ ndelay(500);
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG, 0x00);
+}
+
+static void dsi_pll_toggle_lock_detect_8916(
+ struct mdss_pll_resources *dsi_pll_res)
+{
+ /* DSI PLL toggle lock detect setting */
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x04);
+ ndelay(500);
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x05);
+ udelay(512);
+}
+
+static int dsi_pll_check_lock_status_8916(
+ struct mdss_pll_resources *dsi_pll_res)
+{
+ int rc = 0;
+
+ rc = dsi_pll_lock_status(dsi_pll_res);
+ if (rc)
+ pr_debug("PLL Locked\n");
+ else
+ pr_err("PLL failed to lock\n");
+
+ return rc;
+}
+
+
+static int gf_2_dsi_pll_enable_seq_8916(struct mdss_pll_resources *dsi_pll_res)
+{
+ int pll_locked = 0;
+
+ dsi_pll_sw_reset_8916(dsi_pll_res);
+
+ /*
+ * GF PART 2 PLL power up sequence.
+ * Add necessary delays recommended by hardware.
+ */
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x04);
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
+ udelay(3);
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
+ udelay(500);
+
+ dsi_pll_toggle_lock_detect_8916(dsi_pll_res);
+
+ pll_locked = dsi_pll_check_lock_status_8916(dsi_pll_res);
+ return pll_locked ? 0 : -EINVAL;
+}
+
+static int gf_1_dsi_pll_enable_seq_8916(struct mdss_pll_resources *dsi_pll_res)
+{
+ int pll_locked = 0;
+
+ dsi_pll_sw_reset_8916(dsi_pll_res);
+ /*
+ * GF PART 1 PLL power up sequence.
+ * Add necessary delays recommended by hardware.
+ */
+
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x14);
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
+ udelay(3);
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
+ udelay(500);
+
+ dsi_pll_toggle_lock_detect_8916(dsi_pll_res);
+
+ pll_locked = dsi_pll_check_lock_status_8916(dsi_pll_res);
+ return pll_locked ? 0 : -EINVAL;
+}
+
+static int tsmc_dsi_pll_enable_seq_8916(struct mdss_pll_resources *dsi_pll_res)
+{
+ int pll_locked = 0;
+
+ dsi_pll_sw_reset_8916(dsi_pll_res);
+ /*
+ * TSMC PLL power up sequence.
+ * Add necessary delays recommended by hardware.
+ */
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x34);
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
+ udelay(500);
+
+ dsi_pll_toggle_lock_detect_8916(dsi_pll_res);
+
+ pll_locked = dsi_pll_check_lock_status_8916(dsi_pll_res);
+ return pll_locked ? 0 : -EINVAL;
+}
+
+/* Op structures */
+
+static const struct clk_ops clk_ops_dsi_vco = {
+ .set_rate = vco_set_rate_lpm,
+ .round_rate = vco_round_rate,
+ .handoff = vco_handoff,
+ .prepare = vco_prepare,
+ .unprepare = vco_unprepare,
+};
+
+
+static struct clk_div_ops fixed_4div_ops = {
+ .set_div = fixed_4div_set_div,
+ .get_div = fixed_4div_get_div,
+};
+
+static struct clk_div_ops analog_postdiv_ops = {
+ .set_div = analog_set_div,
+ .get_div = analog_get_div,
+};
+
+static struct clk_div_ops digital_postdiv_ops = {
+ .set_div = digital_set_div,
+ .get_div = digital_get_div,
+};
+
+static struct clk_mux_ops byte_mux_ops = {
+ .set_mux_sel = set_byte_mux_sel,
+ .get_mux_sel = get_byte_mux_sel,
+};
+
+/* DSI PLL0 clock structures */
+static struct dsi_pll_vco_clk dsi_pll0_vco_clk = {
+ .ref_clk_rate = 19200000,
+ .min_rate = 350000000,
+ .max_rate = 750000000,
+ .pll_en_seq_cnt = 9,
+ .pll_enable_seqs[0] = tsmc_dsi_pll_enable_seq_8916,
+ .pll_enable_seqs[1] = tsmc_dsi_pll_enable_seq_8916,
+ .pll_enable_seqs[2] = tsmc_dsi_pll_enable_seq_8916,
+ .pll_enable_seqs[3] = gf_1_dsi_pll_enable_seq_8916,
+ .pll_enable_seqs[4] = gf_1_dsi_pll_enable_seq_8916,
+ .pll_enable_seqs[5] = gf_1_dsi_pll_enable_seq_8916,
+ .pll_enable_seqs[6] = gf_2_dsi_pll_enable_seq_8916,
+ .pll_enable_seqs[7] = gf_2_dsi_pll_enable_seq_8916,
+ .pll_enable_seqs[8] = gf_2_dsi_pll_enable_seq_8916,
+ .lpfr_lut_size = 10,
+ .lpfr_lut = lpfr_lut_struct,
+ .c = {
+ .dbg_name = "dsi_pll0_vco_clk",
+ .ops = &clk_ops_dsi_vco,
+ CLK_INIT(dsi_pll0_vco_clk.c),
+ },
+};
+
+static struct div_clk dsi_pll0_analog_postdiv_clk = {
+ .data = {
+ .max_div = 255,
+ .min_div = 1,
+ },
+ .ops = &analog_postdiv_ops,
+ .c = {
+ .parent = &dsi_pll0_vco_clk.c,
+ .dbg_name = "dsi_pll0_analog_postdiv_clk",
+ .ops = &analog_postdiv_clk_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi_pll0_analog_postdiv_clk.c),
+ },
+};
+
+static struct div_clk dsi_pll0_indirect_path_div2_clk = {
+ .ops = &fixed_2div_ops,
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi_pll0_analog_postdiv_clk.c,
+ .dbg_name = "dsi_pll0_indirect_path_div2_clk",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi_pll0_indirect_path_div2_clk.c),
+ },
+};
+
+static struct div_clk dsi_pll0_pixel_clk_src = {
+ .data = {
+ .max_div = 255,
+ .min_div = 1,
+ },
+ .ops = &digital_postdiv_ops,
+ .c = {
+ .parent = &dsi_pll0_vco_clk.c,
+ .dbg_name = "dsi_pll0_pixel_clk_src",
+ .ops = &pixel_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi_pll0_pixel_clk_src.c),
+ },
+};
+
+static struct mux_clk dsi_pll0_byte_mux = {
+ .num_parents = 2,
+ .parents = (struct clk_src[]){
+ {&dsi_pll0_vco_clk.c, 0},
+ {&dsi_pll0_indirect_path_div2_clk.c, 1},
+ },
+ .ops = &byte_mux_ops,
+ .c = {
+ .parent = &dsi_pll0_vco_clk.c,
+ .dbg_name = "dsi_pll0_byte_mux",
+ .ops = &byte_mux_clk_ops,
+ CLK_INIT(dsi_pll0_byte_mux.c),
+ },
+};
+
+static struct div_clk dsi_pll0_byte_clk_src = {
+ .ops = &fixed_4div_ops,
+ .data = {
+ .min_div = 4,
+ .max_div = 4,
+ },
+ .c = {
+ .parent = &dsi_pll0_byte_mux.c,
+ .dbg_name = "dsi_pll0_byte_clk_src",
+ .ops = &byte_clk_src_ops,
+ CLK_INIT(dsi_pll0_byte_clk_src.c),
+ },
+};
+
+/* DSI PLL1 clock structures */
+static struct dsi_pll_vco_clk dsi_pll1_vco_clk = {
+ .ref_clk_rate = 19200000,
+ .min_rate = 350000000,
+ .max_rate = 750000000,
+ .pll_en_seq_cnt = 9,
+ .pll_enable_seqs[0] = tsmc_dsi_pll_enable_seq_8916,
+ .pll_enable_seqs[1] = tsmc_dsi_pll_enable_seq_8916,
+ .pll_enable_seqs[2] = tsmc_dsi_pll_enable_seq_8916,
+ .pll_enable_seqs[3] = gf_1_dsi_pll_enable_seq_8916,
+ .pll_enable_seqs[4] = gf_1_dsi_pll_enable_seq_8916,
+ .pll_enable_seqs[5] = gf_1_dsi_pll_enable_seq_8916,
+ .pll_enable_seqs[6] = gf_2_dsi_pll_enable_seq_8916,
+ .pll_enable_seqs[7] = gf_2_dsi_pll_enable_seq_8916,
+ .pll_enable_seqs[8] = gf_2_dsi_pll_enable_seq_8916,
+ .lpfr_lut_size = 10,
+ .lpfr_lut = lpfr_lut_struct,
+ .c = {
+ .dbg_name = "dsi_pll1_vco_clk",
+ .ops = &clk_ops_dsi_vco,
+ CLK_INIT(dsi_pll1_vco_clk.c),
+ },
+};
+
+static struct div_clk dsi_pll1_analog_postdiv_clk = {
+ .data = {
+ .max_div = 255,
+ .min_div = 1,
+ },
+ .ops = &analog_postdiv_ops,
+ .c = {
+ .parent = &dsi_pll1_vco_clk.c,
+ .dbg_name = "dsi_pll1_analog_postdiv_clk",
+ .ops = &analog_postdiv_clk_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi_pll1_analog_postdiv_clk.c),
+ },
+};
+
+static struct div_clk dsi_pll1_indirect_path_div2_clk = {
+ .ops = &fixed_2div_ops,
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi_pll1_analog_postdiv_clk.c,
+ .dbg_name = "dsi_pll1_indirect_path_div2_clk",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi_pll1_indirect_path_div2_clk.c),
+ },
+};
+
+static struct div_clk dsi_pll1_pixel_clk_src = {
+ .data = {
+ .max_div = 255,
+ .min_div = 1,
+ },
+ .ops = &digital_postdiv_ops,
+ .c = {
+ .parent = &dsi_pll1_vco_clk.c,
+ .dbg_name = "dsi_pll1_pixel_clk_src",
+ .ops = &pixel_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi_pll1_pixel_clk_src.c),
+ },
+};
+
+static struct mux_clk dsi_pll1_byte_mux = {
+ .num_parents = 2,
+ .parents = (struct clk_src[]){
+ {&dsi_pll1_vco_clk.c, 0},
+ {&dsi_pll1_indirect_path_div2_clk.c, 1},
+ },
+ .ops = &byte_mux_ops,
+ .c = {
+ .parent = &dsi_pll1_vco_clk.c,
+ .dbg_name = "dsi_pll1_byte_mux",
+ .ops = &byte_mux_clk_ops,
+ CLK_INIT(dsi_pll1_byte_mux.c),
+ },
+};
+
+static struct div_clk dsi_pll1_byte_clk_src = {
+ .ops = &fixed_4div_ops,
+ .data = {
+ .min_div = 4,
+ .max_div = 4,
+ },
+ .c = {
+ .parent = &dsi_pll1_byte_mux.c,
+ .dbg_name = "dsi_pll1_byte_clk_src",
+ .ops = &byte_clk_src_ops,
+ CLK_INIT(dsi_pll1_byte_clk_src.c),
+ },
+};
+
+static struct clk_lookup dsi_pll0_cc[] = {
+ CLK_LIST(dsi_pll0_pixel_clk_src),
+ CLK_LIST(dsi_pll0_byte_clk_src),
+};
+
+static struct clk_lookup dsi_pll1_cc[] = {
+ CLK_LIST(dsi_pll1_pixel_clk_src),
+ CLK_LIST(dsi_pll1_byte_clk_src),
+};
+
+int dsi_pll_clock_register_lpm(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ int rc;
+ int const ssc_freq_min = 30000; /* min. recommended freq. value */
+ int const ssc_freq_max = 33000; /* max. recommended freq. value */
+ int const ssc_ppm_max = 5000; /* max. recommended ppm */
+
+ if (!pdev || !pdev->dev.of_node) {
+ pr_err("Invalid input parameters\n");
+ return -EINVAL;
+ }
+
+ if (!pll_res || !pll_res->pll_base) {
+ pr_err("Invalid PLL resources\n");
+ return -EPROBE_DEFER;
+ }
+
+ /* Set client data to mux, div and vco clocks */
+ if (!pll_res->index) {
+ dsi_pll0_byte_clk_src.priv = pll_res;
+ dsi_pll0_pixel_clk_src.priv = pll_res;
+ dsi_pll0_byte_mux.priv = pll_res;
+ dsi_pll0_indirect_path_div2_clk.priv = pll_res;
+ dsi_pll0_analog_postdiv_clk.priv = pll_res;
+ dsi_pll0_vco_clk.priv = pll_res;
+ } else {
+ dsi_pll1_byte_clk_src.priv = pll_res;
+ dsi_pll1_pixel_clk_src.priv = pll_res;
+ dsi_pll1_byte_mux.priv = pll_res;
+ dsi_pll1_indirect_path_div2_clk.priv = pll_res;
+ dsi_pll1_analog_postdiv_clk.priv = pll_res;
+ dsi_pll1_vco_clk.priv = pll_res;
+ }
+
+ pll_res->vco_delay = VCO_DELAY_USEC;
+
+ /* Set clock source operations */
+ pixel_clk_src_ops = clk_ops_slave_div;
+ pixel_clk_src_ops.prepare = dsi_pll_div_prepare;
+
+ analog_postdiv_clk_ops = clk_ops_div;
+ analog_postdiv_clk_ops.prepare = dsi_pll_div_prepare;
+
+ byte_clk_src_ops = clk_ops_div;
+ byte_clk_src_ops.prepare = dsi_pll_div_prepare;
+
+ byte_mux_clk_ops = clk_ops_gen_mux;
+ byte_mux_clk_ops.prepare = dsi_pll_mux_prepare;
+
+ if (pll_res->ssc_en) {
+ if (!pll_res->ssc_freq || (pll_res->ssc_freq < ssc_freq_min) ||
+ (pll_res->ssc_freq > ssc_freq_max)) {
+ pll_res->ssc_freq = ssc_freq_min;
+ pr_debug("SSC frequency out of recommended range. Set to default=%d\n",
+ pll_res->ssc_freq);
+ }
+
+ if (!pll_res->ssc_ppm || (pll_res->ssc_ppm > ssc_ppm_max)) {
+ pll_res->ssc_ppm = ssc_ppm_max;
+ pr_debug("SSC PPM out of recommended range. Set to default=%d\n",
+ pll_res->ssc_ppm);
+ }
+ }
+
+ if ((pll_res->target_id == MDSS_PLL_TARGET_8952) ||
+ (pll_res->target_id == MDSS_PLL_TARGET_8937) ||
+ (pll_res->target_id == MDSS_PLL_TARGET_8909)) {
+ if (!pll_res->index)
+ rc = of_msm_clock_register(pdev->dev.of_node,
+ dsi_pll0_cc, ARRAY_SIZE(dsi_pll0_cc));
+ else
+ rc = of_msm_clock_register(pdev->dev.of_node,
+ dsi_pll1_cc, ARRAY_SIZE(dsi_pll1_cc));
+ if (rc) {
+ pr_err("Clock register failed\n");
+ rc = -EPROBE_DEFER;
+ }
+ } else {
+ pr_err("Invalid target ID\n");
+ rc = -EINVAL;
+ }
+
+ if (!rc)
+ pr_info("Registered DSI PLL:%d clocks successfully\n",
+ pll_res->index);
+
+ return rc;
+}
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-8996-util.c b/drivers/clk/msm/mdss/mdss-dsi-pll-8996-util.c
new file mode 100644
index 0000000..c5d12e5
--- /dev/null
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-8996-util.c
@@ -0,0 +1,1239 @@
+/* Copyright (c) 2015-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/delay.h>
+#include <linux/clk/msm-clock-generic.h>
+
+#include "mdss-pll.h"
+#include "mdss-dsi-pll.h"
+#include "mdss-dsi-pll-8996.h"
+
+#define DSI_PLL_POLL_MAX_READS 15
+#define DSI_PLL_POLL_TIMEOUT_US 1000
+#define MSM8996_DSI_PLL_REVISION_2 2
+
+#define DSI_PHY_SPARE_VAL 0x6a
+#define DSI_PLL_DEFAULT_POSTDIV 1
+
+#define CEIL(x, y) (((x) + ((y)-1)) / (y))
+static void pll_db_commit_8996(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb);
+
+int set_mdss_byte_mux_sel_8996(struct mux_clk *clk, int sel)
+{
+ return 0;
+}
+
+int get_mdss_byte_mux_sel_8996(struct mux_clk *clk)
+{
+ return 0;
+}
+
+int set_mdss_pixel_mux_sel_8996(struct mux_clk *clk, int sel)
+{
+ return 0;
+}
+
+int get_mdss_pixel_mux_sel_8996(struct mux_clk *clk)
+{
+ return 0;
+}
+
+int post_n1_div_set_div(struct div_clk *clk, int div)
+{
+ struct mdss_pll_resources *pll = clk->priv;
+ struct dsi_pll_db *pdb;
+ struct dsi_pll_output *pout;
+ int rc;
+ u32 n1div = 0;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+ pout = &pdb->out;
+
+ /*
+ * vco rate = bit_clk * postdiv * n1div
+ * vco range from 1300 to 2600 Mhz
+ * postdiv = 1
+ * n1div = 1 to 15
+ * n1div = roundup(1300Mhz / bit_clk)
+ * support bit_clk above 86.67Mhz
+ */
+
+ /* this is for vco/bit clock */
+ pout->pll_postdiv = DSI_PLL_DEFAULT_POSTDIV;
+ pout->pll_n1div = div;
+
+ n1div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_CMN_CLK_CFG0);
+ n1div &= ~0xf;
+ n1div |= (div & 0xf);
+ MDSS_PLL_REG_W(pll->pll_base, DSIPHY_CMN_CLK_CFG0, n1div);
+ /* ensure n1 divider is programed */
+ wmb();
+ pr_debug("ndx=%d div=%d postdiv=%x n1div=%x\n",
+ pll->index, div, pout->pll_postdiv, pout->pll_n1div);
+
+ mdss_pll_resource_enable(pll, false);
+
+ return 0;
+}
+
+int post_n1_div_get_div(struct div_clk *clk)
+{
+ u32 div;
+ int rc;
+ struct mdss_pll_resources *pll = clk->priv;
+
+ if (is_gdsc_disabled(pll))
+ return 0;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ /*
+ * postdiv = 1/2/4/8
+ * n1div = 1 - 15
+ * fot the time being, assume postdiv = 1
+ */
+
+ div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_CMN_CLK_CFG0);
+ div &= 0xF;
+ pr_debug("n1 div = %d\n", div);
+
+ mdss_pll_resource_enable(pll, false);
+
+ return div;
+}
+
+int n2_div_set_div(struct div_clk *clk, int div)
+{
+ int rc;
+ u32 n2div;
+ struct mdss_pll_resources *pll = clk->priv;
+ struct dsi_pll_db *pdb;
+ struct dsi_pll_output *pout;
+ struct mdss_pll_resources *slave;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+ pout = &pdb->out;
+
+ /* this is for pixel_clock */
+ n2div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_CMN_CLK_CFG0);
+ n2div &= ~0xf0; /* bits 4 to 7 */
+ n2div |= (div << 4);
+ MDSS_PLL_REG_W(pll->pll_base, DSIPHY_CMN_CLK_CFG0, n2div);
+
+ /* commit slave if split display is enabled */
+ slave = pll->slave;
+ if (slave)
+ MDSS_PLL_REG_W(slave->pll_base, DSIPHY_CMN_CLK_CFG0, n2div);
+
+ pout->pll_n2div = div;
+
+ /* set dsiclk_sel=1 so that n2div *= 2 */
+ MDSS_PLL_REG_W(pll->pll_base, DSIPHY_CMN_CLK_CFG1, 1);
+ pr_debug("ndx=%d div=%d n2div=%x\n", pll->index, div, n2div);
+
+ mdss_pll_resource_enable(pll, false);
+
+ return rc;
+}
+
+int shadow_n2_div_set_div(struct div_clk *clk, int div)
+{
+ struct mdss_pll_resources *pll = clk->priv;
+ struct dsi_pll_db *pdb;
+ struct dsi_pll_output *pout;
+ u32 data;
+
+ pdb = pll->priv;
+ pout = &pdb->out;
+
+ pout->pll_n2div = div;
+
+ data = (pout->pll_n1div | (pout->pll_n2div << 4));
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL19,
+ DSIPHY_CMN_CLK_CFG0, DSIPHY_CMN_CLK_CFG1,
+ data, 1);
+ return 0;
+}
+
+int n2_div_get_div(struct div_clk *clk)
+{
+ int rc;
+ u32 n2div;
+ struct mdss_pll_resources *pll = clk->priv;
+
+ if (is_gdsc_disabled(pll))
+ return 0;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll=%d resources\n",
+ pll->index);
+ return rc;
+ }
+
+ n2div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_CMN_CLK_CFG0);
+ n2div >>= 4;
+ n2div &= 0x0f;
+
+ mdss_pll_resource_enable(pll, false);
+
+ pr_debug("ndx=%d div=%d\n", pll->index, n2div);
+
+ return n2div;
+}
+
+static bool pll_is_pll_locked_8996(struct mdss_pll_resources *pll)
+{
+ u32 status;
+ bool pll_locked;
+
+ /* poll for PLL ready status */
+ if (readl_poll_timeout_atomic((pll->pll_base +
+ DSIPHY_PLL_RESET_SM_READY_STATUS),
+ status,
+ ((status & BIT(5)) > 0),
+ DSI_PLL_POLL_MAX_READS,
+ DSI_PLL_POLL_TIMEOUT_US)) {
+ pr_err("DSI PLL ndx=%d status=%x failed to Lock\n",
+ pll->index, status);
+ pll_locked = false;
+ } else if (readl_poll_timeout_atomic((pll->pll_base +
+ DSIPHY_PLL_RESET_SM_READY_STATUS),
+ status,
+ ((status & BIT(0)) > 0),
+ DSI_PLL_POLL_MAX_READS,
+ DSI_PLL_POLL_TIMEOUT_US)) {
+ pr_err("DSI PLL ndx=%d status=%x PLl not ready\n",
+ pll->index, status);
+ pll_locked = false;
+ } else {
+ pll_locked = true;
+ }
+
+ return pll_locked;
+}
+
+static void dsi_pll_start_8996(void __iomem *pll_base)
+{
+ pr_debug("start PLL at base=%pk\n", pll_base);
+
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VREF_CFG1, 0x10);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_PLL_CNTRL, 1);
+ wmb(); /* make sure register committed */
+}
+
+static void dsi_pll_stop_8996(void __iomem *pll_base)
+{
+ pr_debug("stop PLL at base=%pk\n", pll_base);
+
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_PLL_CNTRL, 0);
+ wmb(); /* make sure register committed */
+}
+
+static inline bool pll_use_precal(struct mdss_pll_resources *pll)
+{
+ bool ret = true;
+ u32 spare = MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_CMN_GLBL_DIGTOP_SPARE2);
+
+ if (!pll->cache_pll_trim_codes[0] || /* kvco code */
+ !pll->cache_pll_trim_codes[1] || /* vco tune */
+ !pll->cache_pll_trim_codes_rate ||
+ (pll->cache_pll_trim_codes_rate != pll->vco_current_rate) ||
+ (spare != DSI_PHY_SPARE_VAL)) /* phy reset */
+ ret = false;
+
+ pr_debug("ndx:%d kvco:%d vco_tune:%d spare:0x%x rate:%llu old:%llu ret:%d\n",
+ pll->index, pll->cache_pll_trim_codes[0],
+ pll->cache_pll_trim_codes[1], spare,
+ pll->cache_pll_trim_codes_rate,
+ pll->vco_current_rate, ret);
+
+ return ret;
+}
+
+int dsi_pll_enable_seq_8996(struct mdss_pll_resources *pll)
+{
+ int rc = 0;
+ struct dsi_pll_db *pdb;
+ struct mdss_pll_resources *slave;
+
+ if (!pll) {
+ pr_err("Invalid PLL resources\n");
+ return -EINVAL;
+ }
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+ if (!pdb) {
+ pr_err("No priv found\n");
+ return -EINVAL;
+ }
+
+ dsi_pll_start_8996(pll->pll_base);
+
+ /*
+ * both DSIPHY_PLL_CLKBUFLR_EN and DSIPHY_CMN_GLBL_TEST_CTRL
+ * enabled at mdss_dsi_8996_phy_config()
+ */
+
+ if (!pll_is_pll_locked_8996(pll)) {
+ pr_err("DSI PLL ndx=%d lock failed, retry full sequence!\n",
+ pll->index);
+ slave = pll->slave;
+
+ /* commit slave if split display is enabled */
+ if (slave)
+ pll_db_commit_8996(slave, pdb);
+
+ /* commit master itself */
+ pll_db_commit_8996(pll, pdb);
+
+ dsi_pll_start_8996(pll->pll_base);
+ if (!pll_is_pll_locked_8996(pll)) {
+ pr_err("DSI PLL ndx=%d lock failed!!!\n",
+ pll->index);
+ rc = -EINVAL;
+ goto init_lock_err;
+ }
+ }
+
+ if (!pll_use_precal(pll)) {
+ /* cache vco settings */
+ pll->cache_pll_trim_codes[0] = MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_CORE_KVCO_CODE_STATUS);
+ pll->cache_pll_trim_codes[1] = MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_CORE_VCO_TUNE_STATUS);
+ pll->cache_pll_trim_codes_rate = pll->vco_current_rate;
+
+ /* write spare */
+ MDSS_PLL_REG_W(pll->pll_base, DSIPHY_CMN_GLBL_DIGTOP_SPARE2,
+ DSI_PHY_SPARE_VAL);
+ }
+
+ pr_debug("DSI PLL ndx:%d Locked! kvco=0x%x vco_tune=0x%x rate=%llu\n",
+ pll->index, pll->cache_pll_trim_codes[0],
+ pll->cache_pll_trim_codes[1],
+ pll->cache_pll_trim_codes_rate);
+
+init_lock_err:
+ return rc;
+}
+
+static int dsi_pll_enable(struct clk *c)
+{
+ int i, rc = 0;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+
+ /* Try all enable sequences until one succeeds */
+ for (i = 0; i < vco->pll_en_seq_cnt; i++) {
+ rc = vco->pll_enable_seqs[i](pll);
+ pr_debug("DSI PLL %s after sequence #%d\n",
+ rc ? "unlocked" : "locked", i + 1);
+ if (!rc)
+ break;
+ }
+
+ if (rc)
+ pr_err("ndx=%d DSI PLL failed to lock\n", pll->index);
+ else
+ pll->pll_on = true;
+
+ return rc;
+}
+
+static void dsi_pll_disable(struct clk *c)
+{
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+ struct mdss_pll_resources *slave;
+
+ if (!pll->pll_on &&
+ mdss_pll_resource_enable(pll, true)) {
+ pr_err("Failed to enable mdss dsi pll=%d\n", pll->index);
+ return;
+ }
+
+ pll->handoff_resources = false;
+ slave = pll->slave;
+
+ dsi_pll_stop_8996(pll->pll_base);
+
+ mdss_pll_resource_enable(pll, false);
+
+ pll->pll_on = false;
+
+ pr_debug("DSI PLL ndx=%d Disabled\n", pll->index);
+}
+
+static void mdss_dsi_pll_8996_input_init(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ pdb->in.fref = 19200000; /* 19.2 Mhz*/
+ pdb->in.fdata = 0; /* bit clock rate */
+ pdb->in.dsiclk_sel = 1; /* 1, reg: 0x0014 */
+ pdb->in.ssc_en = pll->ssc_en; /* 1, reg: 0x0494, bit 0 */
+ pdb->in.ldo_en = 0; /* 0, reg: 0x004c, bit 0 */
+
+ /* fixed input */
+ pdb->in.refclk_dbler_en = 0; /* 0, reg: 0x04c0, bit 1 */
+ pdb->in.vco_measure_time = 5; /* 5, unknown */
+ pdb->in.kvco_measure_time = 5; /* 5, unknown */
+ pdb->in.bandgap_timer = 4; /* 4, reg: 0x0430, bit 3 - 5 */
+ pdb->in.pll_wakeup_timer = 5; /* 5, reg: 0x043c, bit 0 - 2 */
+ pdb->in.plllock_cnt = 1; /* 1, reg: 0x0488, bit 1 - 2 */
+ pdb->in.plllock_rng = 0; /* 0, reg: 0x0488, bit 3 - 4 */
+ pdb->in.ssc_center = pll->ssc_center;/* 0, reg: 0x0494, bit 1 */
+ pdb->in.ssc_adj_period = 37; /* 37, reg: 0x498, bit 0 - 9 */
+ pdb->in.ssc_spread = pll->ssc_ppm / 1000;
+ pdb->in.ssc_freq = pll->ssc_freq;
+
+ pdb->in.pll_ie_trim = 4; /* 4, reg: 0x0400 */
+ pdb->in.pll_ip_trim = 4; /* 4, reg: 0x0404 */
+ pdb->in.pll_cpcset_cur = 1; /* 1, reg: 0x04f0, bit 0 - 2 */
+ pdb->in.pll_cpmset_cur = 1; /* 1, reg: 0x04f0, bit 3 - 5 */
+ pdb->in.pll_icpmset = 4; /* 4, reg: 0x04fc, bit 3 - 5 */
+ pdb->in.pll_icpcset = 4; /* 4, reg: 0x04fc, bit 0 - 2 */
+ pdb->in.pll_icpmset_p = 0; /* 0, reg: 0x04f4, bit 0 - 2 */
+ pdb->in.pll_icpmset_m = 0; /* 0, reg: 0x04f4, bit 3 - 5 */
+ pdb->in.pll_icpcset_p = 0; /* 0, reg: 0x04f8, bit 0 - 2 */
+ pdb->in.pll_icpcset_m = 0; /* 0, reg: 0x04f8, bit 3 - 5 */
+ pdb->in.pll_lpf_res1 = 3; /* 3, reg: 0x0504, bit 0 - 3 */
+ pdb->in.pll_lpf_cap1 = 11; /* 11, reg: 0x0500, bit 0 - 3 */
+ pdb->in.pll_lpf_cap2 = 1; /* 1, reg: 0x0500, bit 4 - 7 */
+ pdb->in.pll_iptat_trim = 7;
+ pdb->in.pll_c3ctrl = 2; /* 2 */
+ pdb->in.pll_r3ctrl = 1; /* 1 */
+}
+
+static void pll_8996_ssc_calc(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ u32 period, ssc_period;
+ u32 ref, rem;
+ s64 step_size;
+
+ pr_debug("%s: vco=%lld ref=%lld\n", __func__,
+ pll->vco_current_rate, pll->vco_ref_clk_rate);
+
+ ssc_period = pdb->in.ssc_freq / 500;
+ period = (unsigned long)pll->vco_ref_clk_rate / 1000;
+ ssc_period = CEIL(period, ssc_period);
+ ssc_period -= 1;
+ pdb->out.ssc_period = ssc_period;
+
+ pr_debug("%s: ssc, freq=%d spread=%d period=%d\n", __func__,
+ pdb->in.ssc_freq, pdb->in.ssc_spread, pdb->out.ssc_period);
+
+ step_size = (u32)pll->vco_current_rate;
+ ref = pll->vco_ref_clk_rate;
+ ref /= 1000;
+ step_size = div_s64(step_size, ref);
+ step_size <<= 20;
+ step_size = div_s64(step_size, 1000);
+ step_size *= pdb->in.ssc_spread;
+ step_size = div_s64(step_size, 1000);
+ step_size *= (pdb->in.ssc_adj_period + 1);
+
+ rem = 0;
+ step_size = div_s64_rem(step_size, ssc_period + 1, &rem);
+ if (rem)
+ step_size++;
+
+ pr_debug("%s: step_size=%lld\n", __func__, step_size);
+
+ step_size &= 0x0ffff; /* take lower 16 bits */
+
+ pdb->out.ssc_step_size = step_size;
+}
+
+static void pll_8996_dec_frac_calc(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ struct dsi_pll_input *pin = &pdb->in;
+ struct dsi_pll_output *pout = &pdb->out;
+ s64 multiplier = BIT(20);
+ s64 dec_start_multiple, dec_start, pll_comp_val;
+ s32 duration, div_frac_start;
+ s64 vco_clk_rate = pll->vco_current_rate;
+ s64 fref = pll->vco_ref_clk_rate;
+
+ pr_debug("vco_clk_rate=%lld ref_clk_rate=%lld\n",
+ vco_clk_rate, fref);
+
+ dec_start_multiple = div_s64(vco_clk_rate * multiplier, fref);
+ div_s64_rem(dec_start_multiple, multiplier, &div_frac_start);
+
+ dec_start = div_s64(dec_start_multiple, multiplier);
+
+ pout->dec_start = (u32)dec_start;
+ pout->div_frac_start = div_frac_start;
+
+ if (pin->plllock_cnt == 0)
+ duration = 1024;
+ else if (pin->plllock_cnt == 1)
+ duration = 256;
+ else if (pin->plllock_cnt == 2)
+ duration = 128;
+ else
+ duration = 32;
+
+ pll_comp_val = duration * dec_start_multiple;
+ pll_comp_val = div_s64(pll_comp_val, multiplier);
+ do_div(pll_comp_val, 10);
+
+ pout->plllock_cmp = (u32)pll_comp_val;
+
+ pout->pll_txclk_en = 1;
+ if (pll->revision == MSM8996_DSI_PLL_REVISION_2)
+ pout->cmn_ldo_cntrl = 0x3c;
+ else
+ pout->cmn_ldo_cntrl = 0x1c;
+}
+
+static u32 pll_8996_kvco_slop(u32 vrate)
+{
+ u32 slop = 0;
+
+ if (vrate > 1300000000UL && vrate <= 1800000000UL)
+ slop = 600;
+ else if (vrate > 1800000000UL && vrate < 2300000000UL)
+ slop = 400;
+ else if (vrate > 2300000000UL && vrate < 2600000000UL)
+ slop = 280;
+
+ return slop;
+}
+
+static inline u32 pll_8996_calc_kvco_code(s64 vco_clk_rate)
+{
+ u32 kvco_code;
+
+ if ((vco_clk_rate >= 2300000000ULL) &&
+ (vco_clk_rate <= 2600000000ULL))
+ kvco_code = 0x2f;
+ else if ((vco_clk_rate >= 1800000000ULL) &&
+ (vco_clk_rate < 2300000000ULL))
+ kvco_code = 0x2c;
+ else
+ kvco_code = 0x28;
+
+ pr_debug("rate: %llu kvco_code: 0x%x\n",
+ vco_clk_rate, kvco_code);
+ return kvco_code;
+}
+
+static void pll_8996_calc_vco_count(struct dsi_pll_db *pdb,
+ s64 vco_clk_rate, s64 fref)
+{
+ struct dsi_pll_input *pin = &pdb->in;
+ struct dsi_pll_output *pout = &pdb->out;
+ s64 data;
+ u32 cnt;
+
+ data = fref * pin->vco_measure_time;
+ do_div(data, 1000000);
+ data &= 0x03ff; /* 10 bits */
+ data -= 2;
+ pout->pll_vco_div_ref = data;
+
+ data = (unsigned long)vco_clk_rate / 1000000; /* unit is Mhz */
+ data *= pin->vco_measure_time;
+ do_div(data, 10);
+ pout->pll_vco_count = data; /* reg: 0x0474, 0x0478 */
+
+ data = fref * pin->kvco_measure_time;
+ do_div(data, 1000000);
+ data &= 0x03ff; /* 10 bits */
+ data -= 1;
+ pout->pll_kvco_div_ref = data;
+
+ cnt = pll_8996_kvco_slop(vco_clk_rate);
+ cnt *= 2;
+ do_div(cnt, 100);
+ cnt *= pin->kvco_measure_time;
+ pout->pll_kvco_count = cnt;
+
+ pout->pll_misc1 = 16;
+ pout->pll_resetsm_cntrl = 48;
+ pout->pll_resetsm_cntrl2 = pin->bandgap_timer << 3;
+ pout->pll_resetsm_cntrl5 = pin->pll_wakeup_timer;
+ pout->pll_kvco_code = pll_8996_calc_kvco_code(vco_clk_rate);
+}
+
+static void pll_db_commit_ssc(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ void __iomem *pll_base = pll->pll_base;
+ struct dsi_pll_input *pin = &pdb->in;
+ struct dsi_pll_output *pout = &pdb->out;
+ char data;
+
+ data = pin->ssc_adj_period;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_ADJ_PER1, data);
+ data = (pin->ssc_adj_period >> 8);
+ data &= 0x03;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_ADJ_PER2, data);
+
+ data = pout->ssc_period;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_PER1, data);
+ data = (pout->ssc_period >> 8);
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_PER2, data);
+
+ data = pout->ssc_step_size;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_STEP_SIZE1, data);
+ data = (pout->ssc_step_size >> 8);
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_STEP_SIZE2, data);
+
+ data = (pin->ssc_center & 0x01);
+ data <<= 1;
+ data |= 0x01; /* enable */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_EN_CENTER, data);
+
+ wmb(); /* make sure register committed */
+}
+
+static int pll_precal_commit_8996(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ void __iomem *pll_base = pll->pll_base;
+ struct dsi_pll_output *pout = &pdb->out;
+ char data;
+
+ /*
+ * if pre-calibrated values cannot be used, return
+ * error, so we use full sequence.
+ */
+ if (!pll_use_precal(pll)) {
+ pr_debug("cannot use precal sequence ndx:%d\n", pll->index);
+ return -EINVAL;
+ }
+
+ data = pout->cmn_ldo_cntrl;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_LDO_CNTRL, data);
+
+ /* stop pll */
+ data = 0;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_PLL_CNTRL, data);
+
+ data = 0x7f;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_0, data);
+
+ data = 0x20;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_1, data);
+
+ data = 0x38;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_RESETSM_CNTRL, data);
+
+ data = BIT(7);
+ data |= pll->cache_pll_trim_codes[1]; /* vco tune */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_VCO_TUNE, data);
+
+ data = BIT(5);
+ data |= pll->cache_pll_trim_codes[0]; /* kvco code */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_CODE, data);
+
+ data = 0xff; /* data, clk, pll normal operation */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_0, data);
+
+ data = 0x0;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_1, data);
+ wmb(); /* make sure register committed */
+
+ return 0;
+}
+
+static void pll_db_commit_common(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ void __iomem *pll_base = pll->pll_base;
+ struct dsi_pll_input *pin = &pdb->in;
+ struct dsi_pll_output *pout = &pdb->out;
+ char data;
+
+ /* confgiure the non frequency dependent pll registers */
+ data = 0;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SYSCLK_EN_RESET, data);
+
+ /* DSIPHY_PLL_CLKBUFLR_EN updated at dsi phy */
+
+ data = pout->pll_txclk_en;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_TXCLK_EN, data);
+
+ data = pout->pll_resetsm_cntrl;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_RESETSM_CNTRL, data);
+ data = pout->pll_resetsm_cntrl2;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_RESETSM_CNTRL2, data);
+ data = pout->pll_resetsm_cntrl5;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_RESETSM_CNTRL5, data);
+
+ data = pout->pll_vco_div_ref;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_DIV_REF1, data);
+ data = (pout->pll_vco_div_ref >> 8);
+ data &= 0x03;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_DIV_REF2, data);
+
+ data = pout->pll_kvco_div_ref;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_DIV_REF1, data);
+ data = (pout->pll_kvco_div_ref >> 8);
+ data &= 0x03;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_DIV_REF2, data);
+
+ data = pout->pll_misc1;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_MISC1, data);
+
+ data = pin->pll_ie_trim;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_IE_TRIM, data);
+
+ data = pin->pll_ip_trim;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_IP_TRIM, data);
+
+ data = ((pin->pll_cpmset_cur << 3) | pin->pll_cpcset_cur);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_CP_SET_CUR, data);
+
+ data = ((pin->pll_icpcset_p << 3) | pin->pll_icpcset_m);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_ICPCSET, data);
+
+ data = ((pin->pll_icpmset_p << 3) | pin->pll_icpcset_m);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_ICPMSET, data);
+
+ data = ((pin->pll_icpmset << 3) | pin->pll_icpcset);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_ICP_SET, data);
+
+ data = ((pdb->in.pll_lpf_cap2 << 4) | pdb->in.pll_lpf_cap1);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_LPF1, data);
+
+ data = pin->pll_iptat_trim;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_IPTAT_TRIM, data);
+
+ data = (pdb->in.pll_c3ctrl | (pdb->in.pll_r3ctrl << 4));
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_CRCTRL, data);
+}
+
+static void pll_db_commit_8996(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb)
+{
+ void __iomem *pll_base = pll->pll_base;
+ struct dsi_pll_input *pin = &pdb->in;
+ struct dsi_pll_output *pout = &pdb->out;
+ char data;
+
+ data = pout->cmn_ldo_cntrl;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_LDO_CNTRL, data);
+
+ pll_db_commit_common(pll, pdb);
+
+ /* de assert pll start and apply pll sw reset */
+ /* stop pll */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_PLL_CNTRL, 0);
+
+ /* pll sw reset */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_1, 0x20);
+ wmb(); /* make sure register committed */
+ udelay(10);
+
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_1, 0);
+ wmb(); /* make sure register committed */
+
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_VCO_TUNE, 0);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_CODE, 0);
+ wmb(); /* make sure register committed */
+
+ data = pdb->in.dsiclk_sel; /* set dsiclk_sel = 1 */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CLK_CFG1, data);
+
+ data = 0xff; /* data, clk, pll normal operation */
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_0, data);
+
+ /* configure the frequency dependent pll registers */
+ data = pout->dec_start;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_DEC_START, data);
+
+ data = pout->div_frac_start;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_DIV_FRAC_START1, data);
+ data = (pout->div_frac_start >> 8);
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_DIV_FRAC_START2, data);
+ data = (pout->div_frac_start >> 16);
+ data &= 0x0f;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_DIV_FRAC_START3, data);
+
+ data = pout->plllock_cmp;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLLLOCK_CMP1, data);
+ data = (pout->plllock_cmp >> 8);
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLLLOCK_CMP2, data);
+ data = (pout->plllock_cmp >> 16);
+ data &= 0x03;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLLLOCK_CMP3, data);
+
+ data = ((pin->plllock_cnt << 1) | (pin->plllock_rng << 3));
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLLLOCK_CMP_EN, data);
+
+ data = pout->pll_vco_count;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_COUNT1, data);
+ data = (pout->pll_vco_count >> 8);
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_COUNT2, data);
+
+ data = pout->pll_kvco_count;
+ data &= 0x0ff;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_COUNT1, data);
+ data = (pout->pll_kvco_count >> 8);
+ data &= 0x03;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_COUNT2, data);
+
+ data = (((pout->pll_postdiv - 1) << 4) | pdb->in.pll_lpf_res1);
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_LPF2_POSTDIV, data);
+
+ data = pout->pll_kvco_code;
+ MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_CODE, data);
+ pr_debug("kvco_code:0x%x\n", data);
+
+ data = (pout->pll_n1div | (pout->pll_n2div << 4));
+ MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CLK_CFG0, data);
+
+ if (pll->ssc_en)
+ pll_db_commit_ssc(pll, pdb);
+
+ pr_debug("pll:%d\n", pll->index);
+ wmb(); /* make sure register committed */
+}
+
+/*
+ * pll_source_finding:
+ * Both GLBL_TEST_CTRL and CLKBUFLR_EN are configured
+ * at mdss_dsi_8996_phy_config()
+ */
+static int pll_source_finding(struct mdss_pll_resources *pll)
+{
+ u32 clk_buf_en;
+ u32 glbl_test_ctrl;
+
+ glbl_test_ctrl = MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_CMN_GLBL_TEST_CTRL);
+ clk_buf_en = MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_CLKBUFLR_EN);
+
+ glbl_test_ctrl &= BIT(2);
+ glbl_test_ctrl >>= 2;
+
+ pr_debug("%s: pll=%d clk_buf_en=%x glbl_test_ctrl=%x\n",
+ __func__, pll->index, clk_buf_en, glbl_test_ctrl);
+
+ clk_buf_en &= (PLL_OUTPUT_RIGHT | PLL_OUTPUT_LEFT);
+
+ if ((glbl_test_ctrl == PLL_SOURCE_FROM_LEFT) &&
+ (clk_buf_en == PLL_OUTPUT_BOTH))
+ return PLL_MASTER;
+
+ if ((glbl_test_ctrl == PLL_SOURCE_FROM_RIGHT) &&
+ (clk_buf_en == PLL_OUTPUT_NONE))
+ return PLL_SLAVE;
+
+ if ((glbl_test_ctrl == PLL_SOURCE_FROM_LEFT) &&
+ (clk_buf_en == PLL_OUTPUT_RIGHT))
+ return PLL_STANDALONE;
+
+ pr_debug("%s: Error pll setup, clk_buf_en=%x glbl_test_ctrl=%x\n",
+ __func__, clk_buf_en, glbl_test_ctrl);
+
+ return PLL_UNKNOWN;
+}
+
+static void pll_source_setup(struct mdss_pll_resources *pll)
+{
+ int status;
+ struct dsi_pll_db *pdb = (struct dsi_pll_db *)pll->priv;
+ struct mdss_pll_resources *other;
+
+ if (pdb->source_setup_done)
+ return;
+
+ pdb->source_setup_done++;
+
+ status = pll_source_finding(pll);
+
+ if (status == PLL_STANDALONE || status == PLL_UNKNOWN)
+ return;
+
+ other = pdb->next->pll;
+ if (!other)
+ return;
+
+ pr_debug("%s: status=%d pll=%d other=%d\n", __func__,
+ status, pll->index, other->index);
+
+ if (status == PLL_MASTER)
+ pll->slave = other;
+ else
+ other->slave = pll;
+}
+
+int pll_vco_set_rate_8996(struct clk *c, unsigned long rate)
+{
+ int rc;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+ struct mdss_pll_resources *slave;
+ struct dsi_pll_db *pdb;
+
+ pdb = (struct dsi_pll_db *)pll->priv;
+ if (!pdb) {
+ pr_err("No prov found\n");
+ return -EINVAL;
+ }
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi plla=%d\n", pll->index);
+ return rc;
+ }
+
+ pll_source_setup(pll);
+
+ pr_debug("%s: ndx=%d base=%pk rate=%lu slave=%pk\n", __func__,
+ pll->index, pll->pll_base, rate, pll->slave);
+
+ pll->vco_current_rate = rate;
+ pll->vco_ref_clk_rate = vco->ref_clk_rate;
+
+ mdss_dsi_pll_8996_input_init(pll, pdb);
+ /*
+ * tx_band = pll_postdiv
+ * 0: divided by 1 <== for now
+ * 1: divided by 2
+ * 2: divided by 4
+ * 3: divided by 8
+ */
+ pdb->out.pll_postdiv = DSI_PLL_DEFAULT_POSTDIV;
+
+ pll_8996_dec_frac_calc(pll, pdb);
+
+ if (pll->ssc_en)
+ pll_8996_ssc_calc(pll, pdb);
+
+ pll_8996_calc_vco_count(pdb, pll->vco_current_rate,
+ pll->vco_ref_clk_rate);
+
+ /* precal sequence, only for the master */
+ if (pll_precal_commit_8996(pll, pdb)) {
+ pr_debug("retry full sequence\n");
+ slave = pll->slave;
+
+ /* commit slave if split display is enabled */
+ if (slave)
+ pll_db_commit_8996(slave, pdb);
+
+ /* commit master itself */
+ pll_db_commit_8996(pll, pdb);
+ }
+
+ mdss_pll_resource_enable(pll, false);
+
+ return rc;
+}
+
+static void shadow_pll_dynamic_refresh_8996(struct mdss_pll_resources *pll,
+ struct dsi_pll_db *pdb, int *pll_trim_codes)
+{
+ struct dsi_pll_output *pout = &pdb->out;
+
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL20,
+ DSIPHY_CMN_CTRL_0, DSIPHY_PLL_SYSCLK_EN_RESET,
+ 0xFF, 0x0);
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL21,
+ DSIPHY_PLL_DEC_START, DSIPHY_PLL_DIV_FRAC_START1,
+ pout->dec_start, (pout->div_frac_start & 0x0FF));
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL22,
+ DSIPHY_PLL_DIV_FRAC_START2, DSIPHY_PLL_DIV_FRAC_START3,
+ ((pout->div_frac_start >> 8) & 0x0FF),
+ ((pout->div_frac_start >> 16) & 0x0F));
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL23,
+ DSIPHY_PLL_PLLLOCK_CMP1, DSIPHY_PLL_PLLLOCK_CMP2,
+ (pout->plllock_cmp & 0x0FF),
+ ((pout->plllock_cmp >> 8) & 0x0FF));
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL24,
+ DSIPHY_PLL_PLLLOCK_CMP3, DSIPHY_PLL_PLL_VCO_TUNE,
+ ((pout->plllock_cmp >> 16) & 0x03),
+ (pll_trim_codes[1] | BIT(7))); /* VCO tune*/
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL25,
+ DSIPHY_PLL_KVCO_CODE, DSIPHY_PLL_RESETSM_CNTRL,
+ (pll_trim_codes[0] | BIT(5)), 0x38);
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL26,
+ DSIPHY_PLL_PLL_LPF2_POSTDIV, DSIPHY_CMN_PLL_CNTRL,
+ (((pout->pll_postdiv - 1) << 4) | pdb->in.pll_lpf_res1), 0x01);
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL27,
+ DSIPHY_CMN_PLL_CNTRL, DSIPHY_CMN_PLL_CNTRL,
+ 0x01, 0x01);
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL28,
+ DSIPHY_CMN_PLL_CNTRL, DSIPHY_CMN_PLL_CNTRL,
+ 0x01, 0x01);
+ MDSS_DYN_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_CTRL29,
+ DSIPHY_CMN_PLL_CNTRL, DSIPHY_CMN_PLL_CNTRL,
+ 0x01, 0x01);
+ MDSS_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR, 0x0000001E);
+ MDSS_PLL_REG_W(pll->dyn_pll_base,
+ DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR2, 0x001FFE00);
+
+ pr_debug("core_kvco_code=0x%x core_vco_tune=0x%x\n",
+ pll_trim_codes[0], pll_trim_codes[1]);
+
+ /*
+ * Ensure all the dynamic refresh registers are written before
+ * dynamic refresh to change the fps is triggered
+ */
+ wmb();
+}
+
+int shadow_pll_vco_set_rate_8996(struct clk *c, unsigned long rate)
+{
+ int rc;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+ struct dsi_pll_db *pdb;
+ int pll_trim_codes[2] = {0, 0};
+
+ if (!pll) {
+ pr_err("PLL data not found\n");
+ return -EINVAL;
+ }
+
+ pdb = pll->priv;
+ if (!pdb) {
+ pr_err("No priv data found\n");
+ return -EINVAL;
+ }
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi plla=%d\n", pll->index);
+ return rc;
+ }
+
+ pr_debug("%s: ndx=%d base=%pk rate=%lu\n", __func__,
+ pll->index, pll->pll_base, rate);
+
+ pll->vco_current_rate = rate;
+ pll->vco_ref_clk_rate = vco->ref_clk_rate;
+
+ mdss_dsi_pll_8996_input_init(pll, pdb);
+
+ pll_8996_dec_frac_calc(pll, pdb);
+
+ pll_8996_calc_vco_count(pdb, pll->vco_current_rate,
+ pll->vco_ref_clk_rate);
+
+ shadow_pll_dynamic_refresh_8996(pll, pdb, pll_trim_codes);
+
+ rc = mdss_pll_resource_enable(pll, false);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi plla=%d\n", pll->index);
+ return rc;
+ }
+
+ return rc;
+}
+
+static unsigned long pll_vco_get_rate_8996(struct clk *c)
+{
+ u64 vco_rate, multiplier = BIT(20);
+ s32 div_frac_start;
+ u32 dec_start;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ u64 ref_clk = vco->ref_clk_rate;
+ int rc;
+ struct mdss_pll_resources *pll = vco->priv;
+
+ if (is_gdsc_disabled(pll))
+ return 0;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll=%d\n", pll->index);
+ return rc;
+ }
+
+ dec_start = MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_DEC_START);
+ dec_start &= 0x0ff;
+ pr_debug("dec_start = 0x%x\n", dec_start);
+
+ div_frac_start = (MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_DIV_FRAC_START3) & 0x0f) << 16;
+ div_frac_start |= (MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_DIV_FRAC_START2) & 0x0ff) << 8;
+ div_frac_start |= MDSS_PLL_REG_R(pll->pll_base,
+ DSIPHY_PLL_DIV_FRAC_START1) & 0x0ff;
+ pr_debug("div_frac_start = 0x%x\n", div_frac_start);
+
+ vco_rate = ref_clk * dec_start;
+ vco_rate += ((ref_clk * div_frac_start) / multiplier);
+
+ pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate);
+
+ mdss_pll_resource_enable(pll, false);
+
+ return (unsigned long)vco_rate;
+}
+
+long pll_vco_round_rate_8996(struct clk *c, unsigned long rate)
+{
+ unsigned long rrate = rate;
+ u32 div;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+
+ div = vco->min_rate / rate;
+ if (div > 15) {
+ /* rate < 86.67 Mhz */
+ pr_err("rate=%lu NOT supportted\n", rate);
+ return -EINVAL;
+ }
+
+ if (rate < vco->min_rate)
+ rrate = vco->min_rate;
+ if (rate > vco->max_rate)
+ rrate = vco->max_rate;
+
+ return rrate;
+}
+
+enum handoff pll_vco_handoff_8996(struct clk *c)
+{
+ int rc;
+ enum handoff ret = HANDOFF_DISABLED_CLK;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+
+ if (is_gdsc_disabled(pll))
+ return HANDOFF_DISABLED_CLK;
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll=%d\n", pll->index);
+ return ret;
+ }
+
+ if (pll_is_pll_locked_8996(pll)) {
+ pll->handoff_resources = true;
+ pll->pll_on = true;
+ c->rate = pll_vco_get_rate_8996(c);
+ ret = HANDOFF_ENABLED_CLK;
+ } else {
+ mdss_pll_resource_enable(pll, false);
+ }
+
+ return ret;
+}
+
+enum handoff shadow_pll_vco_handoff_8996(struct clk *c)
+{
+ return HANDOFF_DISABLED_CLK;
+}
+
+int pll_vco_prepare_8996(struct clk *c)
+{
+ int rc = 0;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+
+ if (!pll) {
+ pr_err("Dsi pll resources are not available\n");
+ return -EINVAL;
+ }
+
+ rc = mdss_pll_resource_enable(pll, true);
+ if (rc) {
+ pr_err("ndx=%d Failed to enable mdss dsi pll resources\n",
+ pll->index);
+ return rc;
+ }
+
+ if ((pll->vco_cached_rate != 0)
+ && (pll->vco_cached_rate == c->rate)) {
+ rc = c->ops->set_rate(c, pll->vco_cached_rate);
+ if (rc) {
+ pr_err("index=%d vco_set_rate failed. rc=%d\n",
+ rc, pll->index);
+ mdss_pll_resource_enable(pll, false);
+ goto error;
+ }
+ }
+
+ rc = dsi_pll_enable(c);
+
+ if (rc) {
+ mdss_pll_resource_enable(pll, false);
+ pr_err("ndx=%d failed to enable dsi pll\n", pll->index);
+ }
+
+error:
+ return rc;
+}
+
+void pll_vco_unprepare_8996(struct clk *c)
+{
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *pll = vco->priv;
+
+ if (!pll) {
+ pr_err("Dsi pll resources are not available\n");
+ return;
+ }
+
+ pll->vco_cached_rate = c->rate;
+ dsi_pll_disable(c);
+}
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-8996.c b/drivers/clk/msm/mdss/mdss-dsi-pll-8996.c
new file mode 100644
index 0000000..6423342
--- /dev/null
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-8996.c
@@ -0,0 +1,572 @@
+/* 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.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <linux/clk/msm-clk.h>
+#include <linux/workqueue.h>
+#include <linux/clk/msm-clock-generic.h>
+#include <dt-bindings/clock/msm-clocks-8996.h>
+
+#include "mdss-pll.h"
+#include "mdss-dsi-pll.h"
+#include "mdss-dsi-pll-8996.h"
+
+#define VCO_DELAY_USEC 1
+
+static struct dsi_pll_db pll_db[DSI_PLL_NUM];
+
+static struct clk_ops n2_clk_src_ops;
+static struct clk_ops shadow_n2_clk_src_ops;
+static struct clk_ops byte_clk_src_ops;
+static struct clk_ops post_n1_div_clk_src_ops;
+static struct clk_ops shadow_post_n1_div_clk_src_ops;
+
+static struct clk_ops clk_ops_gen_mux_dsi;
+
+/* Op structures */
+static const struct clk_ops clk_ops_dsi_vco = {
+ .set_rate = pll_vco_set_rate_8996,
+ .round_rate = pll_vco_round_rate_8996,
+ .handoff = pll_vco_handoff_8996,
+ .prepare = pll_vco_prepare_8996,
+ .unprepare = pll_vco_unprepare_8996,
+};
+
+static struct clk_div_ops post_n1_div_ops = {
+ .set_div = post_n1_div_set_div,
+ .get_div = post_n1_div_get_div,
+};
+
+static struct clk_div_ops n2_div_ops = { /* hr_oclk3 */
+ .set_div = n2_div_set_div,
+ .get_div = n2_div_get_div,
+};
+
+static struct clk_mux_ops mdss_byte_mux_ops = {
+ .set_mux_sel = set_mdss_byte_mux_sel_8996,
+ .get_mux_sel = get_mdss_byte_mux_sel_8996,
+};
+
+static struct clk_mux_ops mdss_pixel_mux_ops = {
+ .set_mux_sel = set_mdss_pixel_mux_sel_8996,
+ .get_mux_sel = get_mdss_pixel_mux_sel_8996,
+};
+
+/* Shadow ops for dynamic refresh */
+static const struct clk_ops clk_ops_shadow_dsi_vco = {
+ .set_rate = shadow_pll_vco_set_rate_8996,
+ .round_rate = pll_vco_round_rate_8996,
+ .handoff = shadow_pll_vco_handoff_8996,
+};
+
+static struct clk_div_ops shadow_post_n1_div_ops = {
+ .set_div = post_n1_div_set_div,
+};
+
+static struct clk_div_ops shadow_n2_div_ops = {
+ .set_div = shadow_n2_div_set_div,
+};
+
+static struct dsi_pll_vco_clk dsi0pll_vco_clk = {
+ .ref_clk_rate = 19200000UL,
+ .min_rate = 1300000000UL,
+ .max_rate = 2600000000UL,
+ .pll_en_seq_cnt = 1,
+ .pll_enable_seqs[0] = dsi_pll_enable_seq_8996,
+ .c = {
+ .dbg_name = "dsi0pll_vco_clk_8996",
+ .ops = &clk_ops_dsi_vco,
+ CLK_INIT(dsi0pll_vco_clk.c),
+ },
+};
+
+static struct dsi_pll_vco_clk dsi0pll_shadow_vco_clk = {
+ .ref_clk_rate = 19200000u,
+ .min_rate = 1300000000u,
+ .max_rate = 2600000000u,
+ .c = {
+ .dbg_name = "dsi0pll_shadow_vco_clk",
+ .ops = &clk_ops_shadow_dsi_vco,
+ CLK_INIT(dsi0pll_shadow_vco_clk.c),
+ },
+};
+
+static struct dsi_pll_vco_clk dsi1pll_vco_clk = {
+ .ref_clk_rate = 19200000UL,
+ .min_rate = 1300000000UL,
+ .max_rate = 2600000000UL,
+ .pll_en_seq_cnt = 1,
+ .pll_enable_seqs[0] = dsi_pll_enable_seq_8996,
+ .c = {
+ .dbg_name = "dsi1pll_vco_clk_8996",
+ .ops = &clk_ops_dsi_vco,
+ CLK_INIT(dsi1pll_vco_clk.c),
+ },
+};
+
+static struct dsi_pll_vco_clk dsi1pll_shadow_vco_clk = {
+ .ref_clk_rate = 19200000u,
+ .min_rate = 1300000000u,
+ .max_rate = 2600000000u,
+ .pll_en_seq_cnt = 1,
+ .pll_enable_seqs[0] = dsi_pll_enable_seq_8996,
+ .c = {
+ .dbg_name = "dsi1pll_shadow_vco_clk",
+ .ops = &clk_ops_shadow_dsi_vco,
+ CLK_INIT(dsi1pll_shadow_vco_clk.c),
+ },
+};
+
+static struct div_clk dsi0pll_post_n1_div_clk = {
+ .data = {
+ .max_div = 15,
+ .min_div = 1,
+ },
+ .ops = &post_n1_div_ops,
+ .c = {
+ .parent = &dsi0pll_vco_clk.c,
+ .dbg_name = "dsi0pll_post_n1_div_clk",
+ .ops = &post_n1_div_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_post_n1_div_clk.c),
+ },
+};
+
+static struct div_clk dsi0pll_shadow_post_n1_div_clk = {
+ .data = {
+ .max_div = 15,
+ .min_div = 1,
+ },
+ .ops = &shadow_post_n1_div_ops,
+ .c = {
+ .parent = &dsi0pll_shadow_vco_clk.c,
+ .dbg_name = "dsi0pll_shadow_post_n1_div_clk",
+ .ops = &shadow_post_n1_div_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_shadow_post_n1_div_clk.c),
+ },
+};
+
+static struct div_clk dsi1pll_post_n1_div_clk = {
+ .data = {
+ .max_div = 15,
+ .min_div = 1,
+ },
+ .ops = &post_n1_div_ops,
+ .c = {
+ .parent = &dsi1pll_vco_clk.c,
+ .dbg_name = "dsi1pll_post_n1_div_clk",
+ .ops = &post_n1_div_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_post_n1_div_clk.c),
+ },
+};
+
+static struct div_clk dsi1pll_shadow_post_n1_div_clk = {
+ .data = {
+ .max_div = 15,
+ .min_div = 1,
+ },
+ .ops = &shadow_post_n1_div_ops,
+ .c = {
+ .parent = &dsi1pll_shadow_vco_clk.c,
+ .dbg_name = "dsi1pll_shadow_post_n1_div_clk",
+ .ops = &shadow_post_n1_div_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_shadow_post_n1_div_clk.c),
+ },
+};
+
+static struct div_clk dsi0pll_n2_div_clk = {
+ .data = {
+ .max_div = 15,
+ .min_div = 1,
+ },
+ .ops = &n2_div_ops,
+ .c = {
+ .parent = &dsi0pll_post_n1_div_clk.c,
+ .dbg_name = "dsi0pll_n2_div_clk",
+ .ops = &n2_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_n2_div_clk.c),
+ },
+};
+
+static struct div_clk dsi0pll_shadow_n2_div_clk = {
+ .data = {
+ .max_div = 15,
+ .min_div = 1,
+ },
+ .ops = &shadow_n2_div_ops,
+ .c = {
+ .parent = &dsi0pll_shadow_post_n1_div_clk.c,
+ .dbg_name = "dsi0pll_shadow_n2_div_clk",
+ .ops = &shadow_n2_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_shadow_n2_div_clk.c),
+ },
+};
+
+static struct div_clk dsi1pll_n2_div_clk = {
+ .data = {
+ .max_div = 15,
+ .min_div = 1,
+ },
+ .ops = &n2_div_ops,
+ .c = {
+ .parent = &dsi1pll_post_n1_div_clk.c,
+ .dbg_name = "dsi1pll_n2_div_clk",
+ .ops = &n2_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_n2_div_clk.c),
+ },
+};
+
+static struct div_clk dsi1pll_shadow_n2_div_clk = {
+ .data = {
+ .max_div = 15,
+ .min_div = 1,
+ },
+ .ops = &shadow_n2_div_ops,
+ .c = {
+ .parent = &dsi1pll_shadow_post_n1_div_clk.c,
+ .dbg_name = "dsi1pll_shadow_n2_div_clk",
+ .ops = &shadow_n2_clk_src_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_shadow_n2_div_clk.c),
+ },
+};
+
+static struct div_clk dsi0pll_pixel_clk_src = {
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi0pll_n2_div_clk.c,
+ .dbg_name = "dsi0pll_pixel_clk_src",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_pixel_clk_src.c),
+ },
+};
+
+static struct div_clk dsi0pll_shadow_pixel_clk_src = {
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi0pll_shadow_n2_div_clk.c,
+ .dbg_name = "dsi0pll_shadow_pixel_clk_src",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_shadow_pixel_clk_src.c),
+ },
+};
+
+static struct div_clk dsi1pll_pixel_clk_src = {
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi1pll_n2_div_clk.c,
+ .dbg_name = "dsi1pll_pixel_clk_src",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_pixel_clk_src.c),
+ },
+};
+
+static struct div_clk dsi1pll_shadow_pixel_clk_src = {
+ .data = {
+ .div = 2,
+ .min_div = 2,
+ .max_div = 2,
+ },
+ .c = {
+ .parent = &dsi1pll_shadow_n2_div_clk.c,
+ .dbg_name = "dsi1pll_shadow_pixel_clk_src",
+ .ops = &clk_ops_div,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_shadow_pixel_clk_src.c),
+ },
+};
+
+static struct mux_clk dsi0pll_pixel_clk_mux = {
+ .num_parents = 2,
+ .parents = (struct clk_src[]) {
+ {&dsi0pll_pixel_clk_src.c, 0},
+ {&dsi0pll_shadow_pixel_clk_src.c, 1},
+ },
+ .ops = &mdss_pixel_mux_ops,
+ .c = {
+ .parent = &dsi0pll_pixel_clk_src.c,
+ .dbg_name = "dsi0pll_pixel_clk_mux",
+ .ops = &clk_ops_gen_mux_dsi,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_pixel_clk_mux.c),
+ }
+};
+
+static struct mux_clk dsi1pll_pixel_clk_mux = {
+ .num_parents = 2,
+ .parents = (struct clk_src[]) {
+ {&dsi1pll_pixel_clk_src.c, 0},
+ {&dsi1pll_shadow_pixel_clk_src.c, 1},
+ },
+ .ops = &mdss_pixel_mux_ops,
+ .c = {
+ .parent = &dsi1pll_pixel_clk_src.c,
+ .dbg_name = "dsi1pll_pixel_clk_mux",
+ .ops = &clk_ops_gen_mux_dsi,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_pixel_clk_mux.c),
+ }
+};
+
+static struct div_clk dsi0pll_byte_clk_src = {
+ .data = {
+ .div = 8,
+ .min_div = 8,
+ .max_div = 8,
+ },
+ .c = {
+ .parent = &dsi0pll_post_n1_div_clk.c,
+ .dbg_name = "dsi0pll_byte_clk_src",
+ .ops = &clk_ops_div,
+ CLK_INIT(dsi0pll_byte_clk_src.c),
+ },
+};
+
+static struct div_clk dsi0pll_shadow_byte_clk_src = {
+ .data = {
+ .div = 8,
+ .min_div = 8,
+ .max_div = 8,
+ },
+ .c = {
+ .parent = &dsi0pll_shadow_post_n1_div_clk.c,
+ .dbg_name = "dsi0pll_shadow_byte_clk_src",
+ .ops = &clk_ops_div,
+ CLK_INIT(dsi0pll_shadow_byte_clk_src.c),
+ },
+};
+
+static struct div_clk dsi1pll_byte_clk_src = {
+ .data = {
+ .div = 8,
+ .min_div = 8,
+ .max_div = 8,
+ },
+ .c = {
+ .parent = &dsi1pll_post_n1_div_clk.c,
+ .dbg_name = "dsi1pll_byte_clk_src",
+ .ops = &clk_ops_div,
+ CLK_INIT(dsi1pll_byte_clk_src.c),
+ },
+};
+
+static struct div_clk dsi1pll_shadow_byte_clk_src = {
+ .data = {
+ .div = 8,
+ .min_div = 8,
+ .max_div = 8,
+ },
+ .c = {
+ .parent = &dsi1pll_shadow_post_n1_div_clk.c,
+ .dbg_name = "dsi1pll_shadow_byte_clk_src",
+ .ops = &clk_ops_div,
+ CLK_INIT(dsi1pll_shadow_byte_clk_src.c),
+ },
+};
+
+static struct mux_clk dsi0pll_byte_clk_mux = {
+ .num_parents = 2,
+ .parents = (struct clk_src[]) {
+ {&dsi0pll_byte_clk_src.c, 0},
+ {&dsi0pll_shadow_byte_clk_src.c, 1},
+ },
+ .ops = &mdss_byte_mux_ops,
+ .c = {
+ .parent = &dsi0pll_byte_clk_src.c,
+ .dbg_name = "dsi0pll_byte_clk_mux",
+ .ops = &clk_ops_gen_mux_dsi,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi0pll_byte_clk_mux.c),
+ }
+};
+static struct mux_clk dsi1pll_byte_clk_mux = {
+ .num_parents = 2,
+ .parents = (struct clk_src[]) {
+ {&dsi1pll_byte_clk_src.c, 0},
+ {&dsi1pll_shadow_byte_clk_src.c, 1},
+ },
+ .ops = &mdss_byte_mux_ops,
+ .c = {
+ .parent = &dsi1pll_byte_clk_src.c,
+ .dbg_name = "dsi1pll_byte_clk_mux",
+ .ops = &clk_ops_gen_mux_dsi,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(dsi1pll_byte_clk_mux.c),
+ }
+};
+
+static struct clk_lookup mdss_dsi_pllcc_8996[] = {
+ CLK_LIST(dsi0pll_byte_clk_mux),
+ CLK_LIST(dsi0pll_byte_clk_src),
+ CLK_LIST(dsi0pll_pixel_clk_mux),
+ CLK_LIST(dsi0pll_pixel_clk_src),
+ CLK_LIST(dsi0pll_n2_div_clk),
+ CLK_LIST(dsi0pll_post_n1_div_clk),
+ CLK_LIST(dsi0pll_vco_clk),
+ CLK_LIST(dsi0pll_shadow_byte_clk_src),
+ CLK_LIST(dsi0pll_shadow_pixel_clk_src),
+ CLK_LIST(dsi0pll_shadow_n2_div_clk),
+ CLK_LIST(dsi0pll_shadow_post_n1_div_clk),
+ CLK_LIST(dsi0pll_shadow_vco_clk),
+};
+
+static struct clk_lookup mdss_dsi_pllcc_8996_1[] = {
+ CLK_LIST(dsi1pll_byte_clk_mux),
+ CLK_LIST(dsi1pll_byte_clk_src),
+ CLK_LIST(dsi1pll_pixel_clk_mux),
+ CLK_LIST(dsi1pll_pixel_clk_src),
+ CLK_LIST(dsi1pll_n2_div_clk),
+ CLK_LIST(dsi1pll_post_n1_div_clk),
+ CLK_LIST(dsi1pll_vco_clk),
+ CLK_LIST(dsi1pll_shadow_byte_clk_src),
+ CLK_LIST(dsi1pll_shadow_pixel_clk_src),
+ CLK_LIST(dsi1pll_shadow_n2_div_clk),
+ CLK_LIST(dsi1pll_shadow_post_n1_div_clk),
+ CLK_LIST(dsi1pll_shadow_vco_clk),
+};
+
+int dsi_pll_clock_register_8996(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ int rc = 0, ndx;
+ int const ssc_freq_default = 31500; /* default h/w recommended value */
+ int const ssc_ppm_default = 5000; /* default h/w recommended value */
+ struct dsi_pll_db *pdb;
+
+ if (!pdev || !pdev->dev.of_node) {
+ pr_err("Invalid input parameters\n");
+ return -EINVAL;
+ }
+
+ if (!pll_res || !pll_res->pll_base) {
+ pr_err("Invalid PLL resources\n");
+ return -EPROBE_DEFER;
+ }
+
+ if (pll_res->index >= DSI_PLL_NUM) {
+ pr_err("pll ndx=%d is NOT supported\n", pll_res->index);
+ return -EINVAL;
+ }
+
+ ndx = pll_res->index;
+ pdb = &pll_db[ndx];
+ pll_res->priv = pdb;
+ pdb->pll = pll_res;
+ ndx++;
+ ndx %= DSI_PLL_NUM;
+ pdb->next = &pll_db[ndx];
+
+ /* Set clock source operations */
+
+ /* hr_oclk3, pixel_clock */
+ n2_clk_src_ops = clk_ops_slave_div;
+ n2_clk_src_ops.prepare = dsi_pll_div_prepare;
+
+ shadow_n2_clk_src_ops = clk_ops_slave_div;
+
+ /* hr_ockl2, byte, vco pll */
+ post_n1_div_clk_src_ops = clk_ops_div;
+ post_n1_div_clk_src_ops.prepare = dsi_pll_div_prepare;
+
+ shadow_post_n1_div_clk_src_ops = clk_ops_div;
+
+ byte_clk_src_ops = clk_ops_div;
+ byte_clk_src_ops.prepare = dsi_pll_div_prepare;
+
+ clk_ops_gen_mux_dsi = clk_ops_gen_mux;
+ clk_ops_gen_mux_dsi.round_rate = parent_round_rate;
+ clk_ops_gen_mux_dsi.set_rate = parent_set_rate;
+
+ if (pll_res->ssc_en) {
+ if (!pll_res->ssc_freq)
+ pll_res->ssc_freq = ssc_freq_default;
+ if (!pll_res->ssc_ppm)
+ pll_res->ssc_ppm = ssc_ppm_default;
+ }
+
+ /* Set client data to mux, div and vco clocks. */
+ if (pll_res->index == DSI_PLL_1) {
+ dsi1pll_byte_clk_src.priv = pll_res;
+ dsi1pll_pixel_clk_src.priv = pll_res;
+ dsi1pll_post_n1_div_clk.priv = pll_res;
+ dsi1pll_n2_div_clk.priv = pll_res;
+ dsi1pll_vco_clk.priv = pll_res;
+
+ dsi1pll_shadow_byte_clk_src.priv = pll_res;
+ dsi1pll_shadow_pixel_clk_src.priv = pll_res;
+ dsi1pll_shadow_post_n1_div_clk.priv = pll_res;
+ dsi1pll_shadow_n2_div_clk.priv = pll_res;
+ dsi1pll_shadow_vco_clk.priv = pll_res;
+
+ pll_res->vco_delay = VCO_DELAY_USEC;
+ if ((pll_res->target_id == MDSS_PLL_TARGET_8996) ||
+ (pll_res->target_id == MDSS_PLL_TARGET_8953)) {
+ rc = of_msm_clock_register(pdev->dev.of_node,
+ mdss_dsi_pllcc_8996_1,
+ ARRAY_SIZE(mdss_dsi_pllcc_8996_1));
+ }
+ } else {
+ dsi0pll_byte_clk_src.priv = pll_res;
+ dsi0pll_pixel_clk_src.priv = pll_res;
+ dsi0pll_post_n1_div_clk.priv = pll_res;
+ dsi0pll_n2_div_clk.priv = pll_res;
+ dsi0pll_vco_clk.priv = pll_res;
+
+ dsi0pll_shadow_byte_clk_src.priv = pll_res;
+ dsi0pll_shadow_pixel_clk_src.priv = pll_res;
+ dsi0pll_shadow_post_n1_div_clk.priv = pll_res;
+ dsi0pll_shadow_n2_div_clk.priv = pll_res;
+ dsi0pll_shadow_vco_clk.priv = pll_res;
+
+ pll_res->vco_delay = VCO_DELAY_USEC;
+ if ((pll_res->target_id == MDSS_PLL_TARGET_8996) ||
+ (pll_res->target_id == MDSS_PLL_TARGET_8953)) {
+ rc = of_msm_clock_register(pdev->dev.of_node,
+ mdss_dsi_pllcc_8996,
+ ARRAY_SIZE(mdss_dsi_pllcc_8996));
+ }
+ }
+
+ if (!rc) {
+ pr_info("Registered DSI PLL ndx=%d clocks successfully\n",
+ pll_res->index);
+ }
+
+ return rc;
+}
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-8996.h b/drivers/clk/msm/mdss/mdss-dsi-pll-8996.h
new file mode 100644
index 0000000..57700e8
--- /dev/null
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-8996.h
@@ -0,0 +1,224 @@
+/* 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 MDSS_DSI_PLL_8996_H
+#define MDSS_DSI_PLL_8996_H
+
+#define DSIPHY_CMN_CLK_CFG0 0x0010
+#define DSIPHY_CMN_CLK_CFG1 0x0014
+#define DSIPHY_CMN_GLBL_TEST_CTRL 0x0018
+
+#define DSIPHY_CMN_PLL_CNTRL 0x0048
+#define DSIPHY_CMN_CTRL_0 0x001c
+#define DSIPHY_CMN_CTRL_1 0x0020
+
+#define DSIPHY_CMN_LDO_CNTRL 0x004c
+#define DSIPHY_CMN_GLBL_DIGTOP_SPARE2 0x005c
+
+#define DSIPHY_PLL_IE_TRIM 0x0400
+#define DSIPHY_PLL_IP_TRIM 0x0404
+
+#define DSIPHY_PLL_IPTAT_TRIM 0x0410
+
+#define DSIPHY_PLL_CLKBUFLR_EN 0x041c
+
+#define DSIPHY_PLL_SYSCLK_EN_RESET 0x0428
+#define DSIPHY_PLL_RESETSM_CNTRL 0x042c
+#define DSIPHY_PLL_RESETSM_CNTRL2 0x0430
+#define DSIPHY_PLL_RESETSM_CNTRL3 0x0434
+#define DSIPHY_PLL_RESETSM_CNTRL4 0x0438
+#define DSIPHY_PLL_RESETSM_CNTRL5 0x043c
+#define DSIPHY_PLL_KVCO_DIV_REF1 0x0440
+#define DSIPHY_PLL_KVCO_DIV_REF2 0x0444
+#define DSIPHY_PLL_KVCO_COUNT1 0x0448
+#define DSIPHY_PLL_KVCO_COUNT2 0x044c
+#define DSIPHY_PLL_VREF_CFG1 0x045c
+
+#define DSIPHY_PLL_KVCO_CODE 0x0458
+#define DSIPHY_PLL_CORE_VCO_TUNE_STATUS 0x4D0
+#define DSIPHY_PLL_CORE_KVCO_CODE_STATUS 0x4D4
+
+#define DSIPHY_PLL_VCO_DIV_REF1 0x046c
+#define DSIPHY_PLL_VCO_DIV_REF2 0x0470
+#define DSIPHY_PLL_VCO_COUNT1 0x0474
+#define DSIPHY_PLL_VCO_COUNT2 0x0478
+#define DSIPHY_PLL_PLLLOCK_CMP1 0x047c
+#define DSIPHY_PLL_PLLLOCK_CMP2 0x0480
+#define DSIPHY_PLL_PLLLOCK_CMP3 0x0484
+#define DSIPHY_PLL_PLLLOCK_CMP_EN 0x0488
+#define DSIPHY_PLL_PLL_VCO_TUNE 0x048C
+#define DSIPHY_PLL_DEC_START 0x0490
+#define DSIPHY_PLL_SSC_EN_CENTER 0x0494
+#define DSIPHY_PLL_SSC_ADJ_PER1 0x0498
+#define DSIPHY_PLL_SSC_ADJ_PER2 0x049c
+#define DSIPHY_PLL_SSC_PER1 0x04a0
+#define DSIPHY_PLL_SSC_PER2 0x04a4
+#define DSIPHY_PLL_SSC_STEP_SIZE1 0x04a8
+#define DSIPHY_PLL_SSC_STEP_SIZE2 0x04ac
+#define DSIPHY_PLL_DIV_FRAC_START1 0x04b4
+#define DSIPHY_PLL_DIV_FRAC_START2 0x04b8
+#define DSIPHY_PLL_DIV_FRAC_START3 0x04bc
+#define DSIPHY_PLL_TXCLK_EN 0x04c0
+#define DSIPHY_PLL_PLL_CRCTRL 0x04c4
+
+#define DSIPHY_PLL_RESET_SM_READY_STATUS 0x04cc
+
+#define DSIPHY_PLL_PLL_MISC1 0x04e8
+
+#define DSIPHY_PLL_CP_SET_CUR 0x04f0
+#define DSIPHY_PLL_PLL_ICPMSET 0x04f4
+#define DSIPHY_PLL_PLL_ICPCSET 0x04f8
+#define DSIPHY_PLL_PLL_ICP_SET 0x04fc
+#define DSIPHY_PLL_PLL_LPF1 0x0500
+#define DSIPHY_PLL_PLL_LPF2_POSTDIV 0x0504
+#define DSIPHY_PLL_PLL_BANDGAP 0x0508
+
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL15 0x050
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL19 0x060
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL20 0x064
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL21 0x068
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL22 0x06C
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL23 0x070
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL24 0x074
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL25 0x078
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL26 0x07C
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL27 0x080
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL28 0x084
+#define DSI_DYNAMIC_REFRESH_PLL_CTRL29 0x088
+#define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR 0x094
+#define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR2 0x098
+
+struct dsi_pll_input {
+ u32 fref; /* 19.2 Mhz, reference clk */
+ u32 fdata; /* bit clock rate */
+ u32 dsiclk_sel; /* 1, reg: 0x0014 */
+ u32 n2div; /* 1, reg: 0x0010, bit 4-7 */
+ u32 ssc_en; /* 1, reg: 0x0494, bit 0 */
+ u32 ldo_en; /* 0, reg: 0x004c, bit 0 */
+
+ /* fixed */
+ u32 refclk_dbler_en; /* 0, reg: 0x04c0, bit 1 */
+ u32 vco_measure_time; /* 5, unknown */
+ u32 kvco_measure_time; /* 5, unknown */
+ u32 bandgap_timer; /* 4, reg: 0x0430, bit 3 - 5 */
+ u32 pll_wakeup_timer; /* 5, reg: 0x043c, bit 0 - 2 */
+ u32 plllock_cnt; /* 1, reg: 0x0488, bit 1 - 2 */
+ u32 plllock_rng; /* 1, reg: 0x0488, bit 3 - 4 */
+ u32 ssc_center; /* 0, reg: 0x0494, bit 1 */
+ u32 ssc_adj_period; /* 37, reg: 0x498, bit 0 - 9 */
+ u32 ssc_spread; /* 0.005 */
+ u32 ssc_freq; /* unknown */
+ u32 pll_ie_trim; /* 4, reg: 0x0400 */
+ u32 pll_ip_trim; /* 4, reg: 0x0404 */
+ u32 pll_iptat_trim; /* reg: 0x0410 */
+ u32 pll_cpcset_cur; /* 1, reg: 0x04f0, bit 0 - 2 */
+ u32 pll_cpmset_cur; /* 1, reg: 0x04f0, bit 3 - 5 */
+
+ u32 pll_icpmset; /* 4, reg: 0x04fc, bit 3 - 5 */
+ u32 pll_icpcset; /* 4, reg: 0x04fc, bit 0 - 2 */
+
+ u32 pll_icpmset_p; /* 0, reg: 0x04f4, bit 0 - 2 */
+ u32 pll_icpmset_m; /* 0, reg: 0x04f4, bit 3 - 5 */
+
+ u32 pll_icpcset_p; /* 0, reg: 0x04f8, bit 0 - 2 */
+ u32 pll_icpcset_m; /* 0, reg: 0x04f8, bit 3 - 5 */
+
+ u32 pll_lpf_res1; /* 3, reg: 0x0504, bit 0 - 3 */
+ u32 pll_lpf_cap1; /* 11, reg: 0x0500, bit 0 - 3 */
+ u32 pll_lpf_cap2; /* 1, reg: 0x0500, bit 4 - 7 */
+ u32 pll_c3ctrl; /* 2, reg: 0x04c4 */
+ u32 pll_r3ctrl; /* 1, reg: 0x04c4 */
+};
+
+struct dsi_pll_output {
+ u32 pll_txclk_en; /* reg: 0x04c0 */
+ u32 dec_start; /* reg: 0x0490 */
+ u32 div_frac_start; /* reg: 0x04b4, 0x4b8, 0x04bc */
+ u32 ssc_period; /* reg: 0x04a0, 0x04a4 */
+ u32 ssc_step_size; /* reg: 0x04a8, 0x04ac */
+ u32 plllock_cmp; /* reg: 0x047c, 0x0480, 0x0484 */
+ u32 pll_vco_div_ref; /* reg: 0x046c, 0x0470 */
+ u32 pll_vco_count; /* reg: 0x0474, 0x0478 */
+ u32 pll_kvco_div_ref; /* reg: 0x0440, 0x0444 */
+ u32 pll_kvco_count; /* reg: 0x0448, 0x044c */
+ u32 pll_misc1; /* reg: 0x04e8 */
+ u32 pll_lpf2_postdiv; /* reg: 0x0504 */
+ u32 pll_resetsm_cntrl; /* reg: 0x042c */
+ u32 pll_resetsm_cntrl2; /* reg: 0x0430 */
+ u32 pll_resetsm_cntrl5; /* reg: 0x043c */
+ u32 pll_kvco_code; /* reg: 0x0458 */
+
+ u32 cmn_clk_cfg0; /* reg: 0x0010 */
+ u32 cmn_clk_cfg1; /* reg: 0x0014 */
+ u32 cmn_ldo_cntrl; /* reg: 0x004c */
+
+ u32 pll_postdiv; /* vco */
+ u32 pll_n1div; /* vco */
+ u32 pll_n2div; /* hr_oclk3, pixel_clock */
+ u32 fcvo;
+};
+
+enum {
+ DSI_PLL_0,
+ DSI_PLL_1,
+ DSI_PLL_NUM
+};
+
+struct dsi_pll_db {
+ struct dsi_pll_db *next;
+ struct mdss_pll_resources *pll;
+ struct dsi_pll_input in;
+ struct dsi_pll_output out;
+ int source_setup_done;
+};
+
+enum {
+ PLL_OUTPUT_NONE,
+ PLL_OUTPUT_RIGHT,
+ PLL_OUTPUT_LEFT,
+ PLL_OUTPUT_BOTH
+};
+
+enum {
+ PLL_SOURCE_FROM_LEFT,
+ PLL_SOURCE_FROM_RIGHT
+};
+
+enum {
+ PLL_UNKNOWN,
+ PLL_STANDALONE,
+ PLL_SLAVE,
+ PLL_MASTER
+};
+
+int pll_vco_set_rate_8996(struct clk *c, unsigned long rate);
+long pll_vco_round_rate_8996(struct clk *c, unsigned long rate);
+enum handoff pll_vco_handoff_8996(struct clk *c);
+enum handoff shadow_pll_vco_handoff_8996(struct clk *c);
+int shadow_post_n1_div_set_div(struct div_clk *clk, int div);
+int shadow_post_n1_div_get_div(struct div_clk *clk);
+int shadow_n2_div_set_div(struct div_clk *clk, int div);
+int shadow_n2_div_get_div(struct div_clk *clk);
+int shadow_pll_vco_set_rate_8996(struct clk *c, unsigned long rate);
+int pll_vco_prepare_8996(struct clk *c);
+void pll_vco_unprepare_8996(struct clk *c);
+int set_mdss_byte_mux_sel_8996(struct mux_clk *clk, int sel);
+int get_mdss_byte_mux_sel_8996(struct mux_clk *clk);
+int set_mdss_pixel_mux_sel_8996(struct mux_clk *clk, int sel);
+int get_mdss_pixel_mux_sel_8996(struct mux_clk *clk);
+int post_n1_div_set_div(struct div_clk *clk, int div);
+int post_n1_div_get_div(struct div_clk *clk);
+int n2_div_set_div(struct div_clk *clk, int div);
+int n2_div_get_div(struct div_clk *clk);
+int dsi_pll_enable_seq_8996(struct mdss_pll_resources *pll);
+
+#endif /* MDSS_DSI_PLL_8996_H */
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-util.c b/drivers/clk/msm/mdss/mdss-dsi-pll-util.c
new file mode 100644
index 0000000..3bc7564
--- /dev/null
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-util.c
@@ -0,0 +1,654 @@
+/* Copyright (c) 2012-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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/delay.h>
+#include <linux/clk/msm-clock-generic.h>
+
+#include "mdss-pll.h"
+#include "mdss-dsi-pll.h"
+
+#define DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG (0x0)
+#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG (0x0004)
+#define DSI_PHY_PLL_UNIPHY_PLL_CHGPUMP_CFG (0x0008)
+#define DSI_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG (0x000C)
+#define DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG (0x0010)
+#define DSI_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG (0x0014)
+#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG (0x0024)
+#define DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG (0x0028)
+#define DSI_PHY_PLL_UNIPHY_PLL_LPFR_CFG (0x002C)
+#define DSI_PHY_PLL_UNIPHY_PLL_LPFC1_CFG (0x0030)
+#define DSI_PHY_PLL_UNIPHY_PLL_LPFC2_CFG (0x0034)
+#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0 (0x0038)
+#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1 (0x003C)
+#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2 (0x0040)
+#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3 (0x0044)
+#define DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG4 (0x0048)
+#define DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG0 (0x004C)
+#define DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG1 (0x0050)
+#define DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG2 (0x0054)
+#define DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG3 (0x0058)
+#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG0 (0x006C)
+#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG2 (0x0074)
+#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG3 (0x0078)
+#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG4 (0x007C)
+#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG5 (0x0080)
+#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG6 (0x0084)
+#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG7 (0x0088)
+#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG8 (0x008C)
+#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG9 (0x0090)
+#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG10 (0x0094)
+#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG11 (0x0098)
+#define DSI_PHY_PLL_UNIPHY_PLL_EFUSE_CFG (0x009C)
+#define DSI_PHY_PLL_UNIPHY_PLL_STATUS (0x00C0)
+
+#define DSI_PLL_POLL_DELAY_US 50
+#define DSI_PLL_POLL_TIMEOUT_US 500
+
+int set_byte_mux_sel(struct mux_clk *clk, int sel)
+{
+ struct mdss_pll_resources *dsi_pll_res = clk->priv;
+
+ pr_debug("byte mux set to %s mode\n", sel ? "indirect" : "direct");
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG, (sel << 1));
+
+ return 0;
+}
+
+int get_byte_mux_sel(struct mux_clk *clk)
+{
+ int mux_mode, rc;
+ struct mdss_pll_resources *dsi_pll_res = clk->priv;
+
+ if (is_gdsc_disabled(dsi_pll_res))
+ return 0;
+
+ rc = mdss_pll_resource_enable(dsi_pll_res, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ mux_mode = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_VREG_CFG) & BIT(1);
+
+ pr_debug("byte mux mode = %s", mux_mode ? "indirect" : "direct");
+ mdss_pll_resource_enable(dsi_pll_res, false);
+
+ return !!mux_mode;
+}
+
+int dsi_pll_div_prepare(struct clk *c)
+{
+ struct div_clk *div = to_div_clk(c);
+ /* Restore the divider's value */
+ return div->ops->set_div(div, div->data.div);
+}
+
+int dsi_pll_mux_prepare(struct clk *c)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ int i, rc, sel = 0;
+ struct mdss_pll_resources *dsi_pll_res = mux->priv;
+
+ rc = mdss_pll_resource_enable(dsi_pll_res, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ for (i = 0; i < mux->num_parents; i++)
+ if (mux->parents[i].src == c->parent) {
+ sel = mux->parents[i].sel;
+ break;
+ }
+
+ if (i == mux->num_parents) {
+ pr_err("Failed to select the parent clock\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ /* Restore the mux source select value */
+ rc = mux->ops->set_mux_sel(mux, sel);
+
+error:
+ mdss_pll_resource_enable(dsi_pll_res, false);
+ return rc;
+}
+
+int fixed_4div_set_div(struct div_clk *clk, int div)
+{
+ int rc;
+ struct mdss_pll_resources *dsi_pll_res = clk->priv;
+
+ rc = mdss_pll_resource_enable(dsi_pll_res, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG, (div - 1));
+
+ mdss_pll_resource_enable(dsi_pll_res, false);
+ return rc;
+}
+
+int fixed_4div_get_div(struct div_clk *clk)
+{
+ int div = 0, rc;
+ struct mdss_pll_resources *dsi_pll_res = clk->priv;
+
+ if (is_gdsc_disabled(dsi_pll_res))
+ return 0;
+
+ rc = mdss_pll_resource_enable(dsi_pll_res, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ div = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_POSTDIV2_CFG);
+
+ mdss_pll_resource_enable(dsi_pll_res, false);
+ return div + 1;
+}
+
+int digital_set_div(struct div_clk *clk, int div)
+{
+ int rc;
+ struct mdss_pll_resources *dsi_pll_res = clk->priv;
+
+ rc = mdss_pll_resource_enable(dsi_pll_res, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG, (div - 1));
+
+ mdss_pll_resource_enable(dsi_pll_res, false);
+ return rc;
+}
+
+int digital_get_div(struct div_clk *clk)
+{
+ int div = 0, rc;
+ struct mdss_pll_resources *dsi_pll_res = clk->priv;
+
+ if (is_gdsc_disabled(dsi_pll_res))
+ return 0;
+
+ rc = mdss_pll_resource_enable(dsi_pll_res, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ div = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_POSTDIV3_CFG);
+
+ mdss_pll_resource_enable(dsi_pll_res, false);
+ return div + 1;
+}
+
+int analog_set_div(struct div_clk *clk, int div)
+{
+ int rc;
+ struct mdss_pll_resources *dsi_pll_res = clk->priv;
+
+ rc = mdss_pll_resource_enable(dsi_pll_res, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG, div - 1);
+
+ mdss_pll_resource_enable(dsi_pll_res, false);
+ return rc;
+}
+
+int analog_get_div(struct div_clk *clk)
+{
+ int div = 0, rc;
+ struct mdss_pll_resources *dsi_pll_res = clk->priv;
+
+ if (is_gdsc_disabled(dsi_pll_res))
+ return 0;
+
+ rc = mdss_pll_resource_enable(clk->priv, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ div = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_POSTDIV1_CFG) + 1;
+
+ mdss_pll_resource_enable(dsi_pll_res, false);
+
+ return div;
+}
+
+int dsi_pll_lock_status(struct mdss_pll_resources *dsi_pll_res)
+{
+ u32 status;
+ int pll_locked;
+
+ /* poll for PLL ready status */
+ if (readl_poll_timeout_atomic((dsi_pll_res->pll_base +
+ DSI_PHY_PLL_UNIPHY_PLL_STATUS),
+ status,
+ ((status & BIT(0)) == 1),
+ DSI_PLL_POLL_DELAY_US,
+ DSI_PLL_POLL_TIMEOUT_US)) {
+ pr_debug("DSI PLL status=%x failed to Lock\n", status);
+ pll_locked = 0;
+ } else {
+ pll_locked = 1;
+ }
+
+ return pll_locked;
+}
+
+static int pll_28nm_vco_rate_calc(struct dsi_pll_vco_clk *vco,
+ struct mdss_dsi_vco_calc *vco_calc, unsigned long vco_clk_rate)
+{
+ s32 rem;
+ s64 frac_n_mode, ref_doubler_en_b;
+ s64 ref_clk_to_pll, div_fb, frac_n_value;
+ int i;
+
+ /* Configure the Loop filter resistance */
+ for (i = 0; i < vco->lpfr_lut_size; i++)
+ if (vco_clk_rate <= vco->lpfr_lut[i].vco_rate)
+ break;
+ if (i == vco->lpfr_lut_size) {
+ pr_err("unable to get loop filter resistance. vco=%ld\n",
+ vco_clk_rate);
+ return -EINVAL;
+ }
+ vco_calc->lpfr_lut_res = vco->lpfr_lut[i].r;
+
+ div_s64_rem(vco_clk_rate, vco->ref_clk_rate, &rem);
+ if (rem) {
+ vco_calc->refclk_cfg = 0x1;
+ frac_n_mode = 1;
+ ref_doubler_en_b = 0;
+ } else {
+ vco_calc->refclk_cfg = 0x0;
+ frac_n_mode = 0;
+ ref_doubler_en_b = 1;
+ }
+
+ pr_debug("refclk_cfg = %lld\n", vco_calc->refclk_cfg);
+
+ ref_clk_to_pll = ((vco->ref_clk_rate * 2 * (vco_calc->refclk_cfg))
+ + (ref_doubler_en_b * vco->ref_clk_rate));
+
+ div_fb = div_s64_rem(vco_clk_rate, ref_clk_to_pll, &rem);
+ frac_n_value = div_s64(((s64)rem * (1 << 16)), ref_clk_to_pll);
+ vco_calc->gen_vco_clk = vco_clk_rate;
+
+ pr_debug("ref_clk_to_pll = %lld\n", ref_clk_to_pll);
+ pr_debug("div_fb = %lld\n", div_fb);
+ pr_debug("frac_n_value = %lld\n", frac_n_value);
+
+ pr_debug("Generated VCO Clock: %lld\n", vco_calc->gen_vco_clk);
+ rem = 0;
+ if (frac_n_mode) {
+ vco_calc->sdm_cfg0 = 0;
+ vco_calc->sdm_cfg1 = (div_fb & 0x3f) - 1;
+ vco_calc->sdm_cfg3 = div_s64_rem(frac_n_value, 256, &rem);
+ vco_calc->sdm_cfg2 = rem;
+ } else {
+ vco_calc->sdm_cfg0 = (0x1 << 5);
+ vco_calc->sdm_cfg0 |= (div_fb & 0x3f) - 1;
+ vco_calc->sdm_cfg1 = 0;
+ vco_calc->sdm_cfg2 = 0;
+ vco_calc->sdm_cfg3 = 0;
+ }
+
+ pr_debug("sdm_cfg0=%lld\n", vco_calc->sdm_cfg0);
+ pr_debug("sdm_cfg1=%lld\n", vco_calc->sdm_cfg1);
+ pr_debug("sdm_cfg2=%lld\n", vco_calc->sdm_cfg2);
+ pr_debug("sdm_cfg3=%lld\n", vco_calc->sdm_cfg3);
+
+ vco_calc->cal_cfg11 = div_s64_rem(vco_calc->gen_vco_clk,
+ 256 * 1000000, &rem);
+ vco_calc->cal_cfg10 = rem / 1000000;
+ pr_debug("cal_cfg10=%lld, cal_cfg11=%lld\n",
+ vco_calc->cal_cfg10, vco_calc->cal_cfg11);
+
+ return 0;
+}
+
+static void pll_28nm_ssc_param_calc(struct dsi_pll_vco_clk *vco,
+ struct mdss_dsi_vco_calc *vco_calc)
+{
+ struct mdss_pll_resources *dsi_pll_res = vco->priv;
+ s64 ppm_freq, incr, spread_freq, div_rf, frac_n_value;
+ s32 rem;
+
+ if (!dsi_pll_res->ssc_en) {
+ pr_debug("DSI PLL SSC not enabled\n");
+ return;
+ }
+
+ vco_calc->ssc.kdiv = DIV_ROUND_CLOSEST(vco->ref_clk_rate,
+ 1000000) - 1;
+ vco_calc->ssc.triang_steps = DIV_ROUND_CLOSEST(vco->ref_clk_rate,
+ dsi_pll_res->ssc_freq * (vco_calc->ssc.kdiv + 1));
+ ppm_freq = div_s64(vco_calc->gen_vco_clk * dsi_pll_res->ssc_ppm,
+ 1000000);
+ incr = div64_s64(ppm_freq * 65536, vco->ref_clk_rate * 2 *
+ vco_calc->ssc.triang_steps);
+
+ vco_calc->ssc.triang_inc_7_0 = incr & 0xff;
+ vco_calc->ssc.triang_inc_9_8 = (incr >> 8) & 0x3;
+
+ if (!dsi_pll_res->ssc_center)
+ spread_freq = vco_calc->gen_vco_clk - ppm_freq;
+ else
+ spread_freq = vco_calc->gen_vco_clk - (ppm_freq / 2);
+
+ div_rf = div_s64(spread_freq, 2 * vco->ref_clk_rate);
+ vco_calc->ssc.dc_offset = (div_rf - 1);
+
+ div_s64_rem(spread_freq, 2 * vco->ref_clk_rate, &rem);
+ frac_n_value = div_s64((s64)rem * 65536, 2 * vco->ref_clk_rate);
+
+ vco_calc->ssc.freq_seed_7_0 = frac_n_value & 0xff;
+ vco_calc->ssc.freq_seed_15_8 = (frac_n_value >> 8) & 0xff;
+}
+
+static void pll_28nm_vco_config(void __iomem *pll_base,
+ struct mdss_dsi_vco_calc *vco_calc,
+ u32 vco_delay_us, bool ssc_en)
+{
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_LPFR_CFG,
+ vco_calc->lpfr_lut_res);
+
+ /* Loop filter capacitance values : c1 and c2 */
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_LPFC1_CFG, 0x70);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_LPFC2_CFG, 0x15);
+
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CHGPUMP_CFG, 0x02);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG3, 0x2b);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG4, 0x66);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2, 0x0d);
+
+ if (!ssc_en) {
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1,
+ (u32)(vco_calc->sdm_cfg1 & 0xff));
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2,
+ (u32)(vco_calc->sdm_cfg2 & 0xff));
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3,
+ (u32)(vco_calc->sdm_cfg3 & 0xff));
+ } else {
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1,
+ (u32)vco_calc->ssc.dc_offset);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2,
+ (u32)vco_calc->ssc.freq_seed_7_0);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3,
+ (u32)vco_calc->ssc.freq_seed_15_8);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG0,
+ (u32)vco_calc->ssc.kdiv);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG1,
+ (u32)vco_calc->ssc.triang_inc_7_0);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG2,
+ (u32)vco_calc->ssc.triang_inc_9_8);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SSC_CFG3,
+ (u32)vco_calc->ssc.triang_steps);
+ }
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG4, 0x00);
+
+ /* Add hardware recommended delay for correct PLL configuration */
+ if (vco_delay_us)
+ udelay(vco_delay_us);
+
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG,
+ (u32)vco_calc->refclk_cfg);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_PWRGEN_CFG, 0x00);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_VCOLPF_CFG, 0x71);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0,
+ (u32)vco_calc->sdm_cfg0);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG0, 0x12);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG6, 0x30);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG7, 0x00);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG8, 0x60);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG9, 0x00);
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG10,
+ (u32)(vco_calc->cal_cfg10 & 0xff));
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG11,
+ (u32)(vco_calc->cal_cfg11 & 0xff));
+ MDSS_PLL_REG_W(pll_base, DSI_PHY_PLL_UNIPHY_PLL_EFUSE_CFG, 0x20);
+}
+
+int vco_set_rate(struct dsi_pll_vco_clk *vco, unsigned long rate)
+{
+ struct mdss_dsi_vco_calc vco_calc = {0};
+ struct mdss_pll_resources *dsi_pll_res = vco->priv;
+ int rc = 0;
+
+ rc = pll_28nm_vco_rate_calc(vco, &vco_calc, rate);
+ if (rc) {
+ pr_err("vco rate calculation failed\n");
+ return rc;
+ }
+
+ pll_28nm_ssc_param_calc(vco, &vco_calc);
+ pll_28nm_vco_config(dsi_pll_res->pll_base, &vco_calc,
+ dsi_pll_res->vco_delay, dsi_pll_res->ssc_en);
+
+ return 0;
+}
+
+unsigned long vco_get_rate(struct clk *c)
+{
+ u32 sdm0, doubler, sdm_byp_div;
+ u64 vco_rate;
+ u32 sdm_dc_off, sdm_freq_seed, sdm2, sdm3;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ u64 ref_clk = vco->ref_clk_rate;
+ int rc;
+ struct mdss_pll_resources *dsi_pll_res = vco->priv;
+
+ if (is_gdsc_disabled(dsi_pll_res))
+ return 0;
+
+ rc = mdss_pll_resource_enable(dsi_pll_res, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ /* Check to see if the ref clk doubler is enabled */
+ doubler = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_REFCLK_CFG) & BIT(0);
+ ref_clk += (doubler * vco->ref_clk_rate);
+
+ /* see if it is integer mode or sdm mode */
+ sdm0 = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0);
+ if (sdm0 & BIT(6)) {
+ /* integer mode */
+ sdm_byp_div = (MDSS_PLL_REG_R(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG0) & 0x3f) + 1;
+ vco_rate = ref_clk * sdm_byp_div;
+ } else {
+ /* sdm mode */
+ sdm_dc_off = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG1) & 0xFF;
+ pr_debug("sdm_dc_off = %d\n", sdm_dc_off);
+ sdm2 = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG2) & 0xFF;
+ sdm3 = MDSS_PLL_REG_R(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_SDM_CFG3) & 0xFF;
+ sdm_freq_seed = (sdm3 << 8) | sdm2;
+ pr_debug("sdm_freq_seed = %d\n", sdm_freq_seed);
+
+ vco_rate = (ref_clk * (sdm_dc_off + 1)) +
+ mult_frac(ref_clk, sdm_freq_seed, BIT(16));
+ pr_debug("vco rate = %lld", vco_rate);
+ }
+
+ pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate);
+
+ mdss_pll_resource_enable(dsi_pll_res, false);
+
+ return (unsigned long)vco_rate;
+}
+
+static int dsi_pll_enable(struct clk *c)
+{
+ int i, rc;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *dsi_pll_res = vco->priv;
+
+ rc = mdss_pll_resource_enable(dsi_pll_res, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return rc;
+ }
+
+ /* Try all enable sequences until one succeeds */
+ for (i = 0; i < vco->pll_en_seq_cnt; i++) {
+ rc = vco->pll_enable_seqs[i](dsi_pll_res);
+ pr_debug("DSI PLL %s after sequence #%d\n",
+ rc ? "unlocked" : "locked", i + 1);
+ if (!rc)
+ break;
+ }
+
+ if (rc) {
+ mdss_pll_resource_enable(dsi_pll_res, false);
+ pr_err("DSI PLL failed to lock\n");
+ }
+ dsi_pll_res->pll_on = true;
+
+ return rc;
+}
+
+static void dsi_pll_disable(struct clk *c)
+{
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *dsi_pll_res = vco->priv;
+
+ if (!dsi_pll_res->pll_on &&
+ mdss_pll_resource_enable(dsi_pll_res, true)) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return;
+ }
+
+ dsi_pll_res->handoff_resources = false;
+
+ MDSS_PLL_REG_W(dsi_pll_res->pll_base,
+ DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x00);
+
+ mdss_pll_resource_enable(dsi_pll_res, false);
+ dsi_pll_res->pll_on = false;
+
+ pr_debug("DSI PLL Disabled\n");
+}
+
+long vco_round_rate(struct clk *c, unsigned long rate)
+{
+ unsigned long rrate = rate;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+
+ if (rate < vco->min_rate)
+ rrate = vco->min_rate;
+ if (rate > vco->max_rate)
+ rrate = vco->max_rate;
+
+ return rrate;
+}
+
+enum handoff vco_handoff(struct clk *c)
+{
+ int rc;
+ enum handoff ret = HANDOFF_DISABLED_CLK;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *dsi_pll_res = vco->priv;
+
+ if (is_gdsc_disabled(dsi_pll_res))
+ return HANDOFF_DISABLED_CLK;
+
+ rc = mdss_pll_resource_enable(dsi_pll_res, true);
+ if (rc) {
+ pr_err("Failed to enable mdss dsi pll resources\n");
+ return ret;
+ }
+
+ if (dsi_pll_lock_status(dsi_pll_res)) {
+ dsi_pll_res->handoff_resources = true;
+ dsi_pll_res->pll_on = true;
+ c->rate = vco_get_rate(c);
+ ret = HANDOFF_ENABLED_CLK;
+ } else {
+ mdss_pll_resource_enable(dsi_pll_res, false);
+ }
+
+ return ret;
+}
+
+int vco_prepare(struct clk *c)
+{
+ int rc = 0;
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *dsi_pll_res = vco->priv;
+
+ if (!dsi_pll_res) {
+ pr_err("Dsi pll resources are not available\n");
+ return -EINVAL;
+ }
+
+ if ((dsi_pll_res->vco_cached_rate != 0)
+ && (dsi_pll_res->vco_cached_rate == c->rate)) {
+ rc = c->ops->set_rate(c, dsi_pll_res->vco_cached_rate);
+ if (rc) {
+ pr_err("vco_set_rate failed. rc=%d\n", rc);
+ goto error;
+ }
+ }
+
+ rc = dsi_pll_enable(c);
+
+error:
+ return rc;
+}
+
+void vco_unprepare(struct clk *c)
+{
+ struct dsi_pll_vco_clk *vco = to_vco_clk(c);
+ struct mdss_pll_resources *dsi_pll_res = vco->priv;
+
+ if (!dsi_pll_res) {
+ pr_err("Dsi pll resources are not available\n");
+ return;
+ }
+
+ dsi_pll_res->vco_cached_rate = c->rate;
+ dsi_pll_disable(c);
+}
+
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll.h b/drivers/clk/msm/mdss/mdss-dsi-pll.h
new file mode 100644
index 0000000..4a9bb64
--- /dev/null
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll.h
@@ -0,0 +1,132 @@
+/* Copyright (c) 2012-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.
+ */
+
+#ifndef __MDSS_DSI_PLL_H
+#define __MDSS_DSI_PLL_H
+
+#define MAX_DSI_PLL_EN_SEQS 10
+
+#define DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG (0x0020)
+#define DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2 (0x0064)
+#define DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG (0x0068)
+#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1 (0x0070)
+
+/* Register offsets for 20nm PHY PLL */
+#define MMSS_DSI_PHY_PLL_PLL_CNTRL (0x0014)
+#define MMSS_DSI_PHY_PLL_PLL_BKG_KVCO_CAL_EN (0x002C)
+#define MMSS_DSI_PHY_PLL_PLLLOCK_CMP_EN (0x009C)
+
+struct lpfr_cfg {
+ unsigned long vco_rate;
+ u32 r;
+};
+
+struct dsi_pll_vco_clk {
+ unsigned long ref_clk_rate;
+ unsigned long min_rate;
+ unsigned long max_rate;
+ u32 pll_en_seq_cnt;
+ struct lpfr_cfg *lpfr_lut;
+ u32 lpfr_lut_size;
+ void *priv;
+
+ struct clk c;
+
+ int (*pll_enable_seqs[MAX_DSI_PLL_EN_SEQS])
+ (struct mdss_pll_resources *dsi_pll_Res);
+};
+
+struct ssc_params {
+ s32 kdiv;
+ s64 triang_inc_7_0;
+ s64 triang_inc_9_8;
+ s64 triang_steps;
+ s64 dc_offset;
+ s64 freq_seed_7_0;
+ s64 freq_seed_15_8;
+};
+
+struct mdss_dsi_vco_calc {
+ s64 sdm_cfg0;
+ s64 sdm_cfg1;
+ s64 sdm_cfg2;
+ s64 sdm_cfg3;
+ s64 cal_cfg10;
+ s64 cal_cfg11;
+ s64 refclk_cfg;
+ s64 gen_vco_clk;
+ u32 lpfr_lut_res;
+ struct ssc_params ssc;
+};
+
+static inline struct dsi_pll_vco_clk *to_vco_clk(struct clk *clk)
+{
+ return container_of(clk, struct dsi_pll_vco_clk, c);
+}
+
+int dsi_pll_clock_register_hpm(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+int dsi_pll_clock_register_20nm(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+int dsi_pll_clock_register_lpm(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+int dsi_pll_clock_register_8996(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+
+int set_byte_mux_sel(struct mux_clk *clk, int sel);
+int get_byte_mux_sel(struct mux_clk *clk);
+int dsi_pll_div_prepare(struct clk *c);
+int dsi_pll_mux_prepare(struct clk *c);
+int fixed_4div_set_div(struct div_clk *clk, int div);
+int fixed_4div_get_div(struct div_clk *clk);
+int digital_set_div(struct div_clk *clk, int div);
+int digital_get_div(struct div_clk *clk);
+int analog_set_div(struct div_clk *clk, int div);
+int analog_get_div(struct div_clk *clk);
+int dsi_pll_lock_status(struct mdss_pll_resources *dsi_pll_res);
+int vco_set_rate(struct dsi_pll_vco_clk *vco, unsigned long rate);
+unsigned long vco_get_rate(struct clk *c);
+long vco_round_rate(struct clk *c, unsigned long rate);
+enum handoff vco_handoff(struct clk *c);
+int vco_prepare(struct clk *c);
+void vco_unprepare(struct clk *c);
+
+/* APIs for 20nm PHY PLL */
+int pll_20nm_vco_set_rate(struct dsi_pll_vco_clk *vco, unsigned long rate);
+int shadow_pll_20nm_vco_set_rate(struct dsi_pll_vco_clk *vco,
+ unsigned long rate);
+long pll_20nm_vco_round_rate(struct clk *c, unsigned long rate);
+enum handoff pll_20nm_vco_handoff(struct clk *c);
+int pll_20nm_vco_prepare(struct clk *c);
+void pll_20nm_vco_unprepare(struct clk *c);
+int pll_20nm_vco_enable_seq(struct mdss_pll_resources *dsi_pll_res);
+
+int set_bypass_lp_div_mux_sel(struct mux_clk *clk, int sel);
+int set_shadow_bypass_lp_div_mux_sel(struct mux_clk *clk, int sel);
+int get_bypass_lp_div_mux_sel(struct mux_clk *clk);
+int fixed_hr_oclk2_set_div(struct div_clk *clk, int div);
+int shadow_fixed_hr_oclk2_set_div(struct div_clk *clk, int div);
+int fixed_hr_oclk2_get_div(struct div_clk *clk);
+int hr_oclk3_set_div(struct div_clk *clk, int div);
+int shadow_hr_oclk3_set_div(struct div_clk *clk, int div);
+int hr_oclk3_get_div(struct div_clk *clk);
+int ndiv_set_div(struct div_clk *clk, int div);
+int shadow_ndiv_set_div(struct div_clk *clk, int div);
+int ndiv_get_div(struct div_clk *clk);
+void __dsi_pll_disable(void __iomem *pll_base);
+
+int set_mdss_pixel_mux_sel(struct mux_clk *clk, int sel);
+int get_mdss_pixel_mux_sel(struct mux_clk *clk);
+int set_mdss_byte_mux_sel(struct mux_clk *clk, int sel);
+int get_mdss_byte_mux_sel(struct mux_clk *clk);
+
+#endif
diff --git a/drivers/clk/msm/mdss/mdss-hdmi-pll-8996.c b/drivers/clk/msm/mdss/mdss-hdmi-pll-8996.c
new file mode 100644
index 0000000..0f2d61e
--- /dev/null
+++ b/drivers/clk/msm/mdss/mdss-hdmi-pll-8996.c
@@ -0,0 +1,2689 @@
+/* Copyright (c) 2014-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <linux/clk/msm-clk.h>
+#include <linux/clk/msm-clock-generic.h>
+#include <dt-bindings/clock/msm-clocks-8996.h>
+
+#include "mdss-pll.h"
+#include "mdss-hdmi-pll.h"
+
+/* CONSTANTS */
+#define HDMI_BIT_CLK_TO_PIX_CLK_RATIO 10
+#define HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD 3400000000UL
+#define HDMI_DIG_FREQ_BIT_CLK_THRESHOLD 1500000000UL
+#define HDMI_MID_FREQ_BIT_CLK_THRESHOLD 750000000
+#define HDMI_CLKS_PLL_DIVSEL 0
+#define HDMI_CORECLK_DIV 5
+#define HDMI_REF_CLOCK 19200000
+#define HDMI_64B_ERR_VAL 0xFFFFFFFFFFFFFFFFULL
+#define HDMI_VERSION_8996_V1 1
+#define HDMI_VERSION_8996_V2 2
+#define HDMI_VERSION_8996_V3 3
+#define HDMI_VERSION_8996_V3_1_8 4
+
+#define HDMI_VCO_MAX_FREQ 12000000000UL
+#define HDMI_VCO_MIN_FREQ 8000000000UL
+#define HDMI_2400MHZ_BIT_CLK_HZ 2400000000UL
+#define HDMI_2250MHZ_BIT_CLK_HZ 2250000000UL
+#define HDMI_2000MHZ_BIT_CLK_HZ 2000000000UL
+#define HDMI_1700MHZ_BIT_CLK_HZ 1700000000UL
+#define HDMI_1200MHZ_BIT_CLK_HZ 1200000000UL
+#define HDMI_1334MHZ_BIT_CLK_HZ 1334000000UL
+#define HDMI_1000MHZ_BIT_CLK_HZ 1000000000UL
+#define HDMI_850MHZ_BIT_CLK_HZ 850000000
+#define HDMI_667MHZ_BIT_CLK_HZ 667000000
+#define HDMI_600MHZ_BIT_CLK_HZ 600000000
+#define HDMI_500MHZ_BIT_CLK_HZ 500000000
+#define HDMI_450MHZ_BIT_CLK_HZ 450000000
+#define HDMI_334MHZ_BIT_CLK_HZ 334000000
+#define HDMI_300MHZ_BIT_CLK_HZ 300000000
+#define HDMI_282MHZ_BIT_CLK_HZ 282000000
+#define HDMI_250MHZ_BIT_CLK_HZ 250000000
+#define HDMI_KHZ_TO_HZ 1000
+
+/* PLL REGISTERS */
+#define QSERDES_COM_ATB_SEL1 (0x000)
+#define QSERDES_COM_ATB_SEL2 (0x004)
+#define QSERDES_COM_FREQ_UPDATE (0x008)
+#define QSERDES_COM_BG_TIMER (0x00C)
+#define QSERDES_COM_SSC_EN_CENTER (0x010)
+#define QSERDES_COM_SSC_ADJ_PER1 (0x014)
+#define QSERDES_COM_SSC_ADJ_PER2 (0x018)
+#define QSERDES_COM_SSC_PER1 (0x01C)
+#define QSERDES_COM_SSC_PER2 (0x020)
+#define QSERDES_COM_SSC_STEP_SIZE1 (0x024)
+#define QSERDES_COM_SSC_STEP_SIZE2 (0x028)
+#define QSERDES_COM_POST_DIV (0x02C)
+#define QSERDES_COM_POST_DIV_MUX (0x030)
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN (0x034)
+#define QSERDES_COM_CLK_ENABLE1 (0x038)
+#define QSERDES_COM_SYS_CLK_CTRL (0x03C)
+#define QSERDES_COM_SYSCLK_BUF_ENABLE (0x040)
+#define QSERDES_COM_PLL_EN (0x044)
+#define QSERDES_COM_PLL_IVCO (0x048)
+#define QSERDES_COM_LOCK_CMP1_MODE0 (0x04C)
+#define QSERDES_COM_LOCK_CMP2_MODE0 (0x050)
+#define QSERDES_COM_LOCK_CMP3_MODE0 (0x054)
+#define QSERDES_COM_LOCK_CMP1_MODE1 (0x058)
+#define QSERDES_COM_LOCK_CMP2_MODE1 (0x05C)
+#define QSERDES_COM_LOCK_CMP3_MODE1 (0x060)
+#define QSERDES_COM_LOCK_CMP1_MODE2 (0x064)
+#define QSERDES_COM_CMN_RSVD0 (0x064)
+#define QSERDES_COM_LOCK_CMP2_MODE2 (0x068)
+#define QSERDES_COM_EP_CLOCK_DETECT_CTRL (0x068)
+#define QSERDES_COM_LOCK_CMP3_MODE2 (0x06C)
+#define QSERDES_COM_SYSCLK_DET_COMP_STATUS (0x06C)
+#define QSERDES_COM_BG_TRIM (0x070)
+#define QSERDES_COM_CLK_EP_DIV (0x074)
+#define QSERDES_COM_CP_CTRL_MODE0 (0x078)
+#define QSERDES_COM_CP_CTRL_MODE1 (0x07C)
+#define QSERDES_COM_CP_CTRL_MODE2 (0x080)
+#define QSERDES_COM_CMN_RSVD1 (0x080)
+#define QSERDES_COM_PLL_RCTRL_MODE0 (0x084)
+#define QSERDES_COM_PLL_RCTRL_MODE1 (0x088)
+#define QSERDES_COM_PLL_RCTRL_MODE2 (0x08C)
+#define QSERDES_COM_CMN_RSVD2 (0x08C)
+#define QSERDES_COM_PLL_CCTRL_MODE0 (0x090)
+#define QSERDES_COM_PLL_CCTRL_MODE1 (0x094)
+#define QSERDES_COM_PLL_CCTRL_MODE2 (0x098)
+#define QSERDES_COM_CMN_RSVD3 (0x098)
+#define QSERDES_COM_PLL_CNTRL (0x09C)
+#define QSERDES_COM_PHASE_SEL_CTRL (0x0A0)
+#define QSERDES_COM_PHASE_SEL_DC (0x0A4)
+#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL (0x0A8)
+#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM (0x0A8)
+#define QSERDES_COM_SYSCLK_EN_SEL (0x0AC)
+#define QSERDES_COM_CML_SYSCLK_SEL (0x0B0)
+#define QSERDES_COM_RESETSM_CNTRL (0x0B4)
+#define QSERDES_COM_RESETSM_CNTRL2 (0x0B8)
+#define QSERDES_COM_RESTRIM_CTRL (0x0BC)
+#define QSERDES_COM_RESTRIM_CTRL2 (0x0C0)
+#define QSERDES_COM_RESCODE_DIV_NUM (0x0C4)
+#define QSERDES_COM_LOCK_CMP_EN (0x0C8)
+#define QSERDES_COM_LOCK_CMP_CFG (0x0CC)
+#define QSERDES_COM_DEC_START_MODE0 (0x0D0)
+#define QSERDES_COM_DEC_START_MODE1 (0x0D4)
+#define QSERDES_COM_DEC_START_MODE2 (0x0D8)
+#define QSERDES_COM_VCOCAL_DEADMAN_CTRL (0x0D8)
+#define QSERDES_COM_DIV_FRAC_START1_MODE0 (0x0DC)
+#define QSERDES_COM_DIV_FRAC_START2_MODE0 (0x0E0)
+#define QSERDES_COM_DIV_FRAC_START3_MODE0 (0x0E4)
+#define QSERDES_COM_DIV_FRAC_START1_MODE1 (0x0E8)
+#define QSERDES_COM_DIV_FRAC_START2_MODE1 (0x0EC)
+#define QSERDES_COM_DIV_FRAC_START3_MODE1 (0x0F0)
+#define QSERDES_COM_DIV_FRAC_START1_MODE2 (0x0F4)
+#define QSERDES_COM_VCO_TUNE_MINVAL1 (0x0F4)
+#define QSERDES_COM_DIV_FRAC_START2_MODE2 (0x0F8)
+#define QSERDES_COM_VCO_TUNE_MINVAL2 (0x0F8)
+#define QSERDES_COM_DIV_FRAC_START3_MODE2 (0x0FC)
+#define QSERDES_COM_CMN_RSVD4 (0x0FC)
+#define QSERDES_COM_INTEGLOOP_INITVAL (0x100)
+#define QSERDES_COM_INTEGLOOP_EN (0x104)
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 (0x108)
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 (0x10C)
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 (0x110)
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 (0x114)
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE2 (0x118)
+#define QSERDES_COM_VCO_TUNE_MAXVAL1 (0x118)
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE2 (0x11C)
+#define QSERDES_COM_VCO_TUNE_MAXVAL2 (0x11C)
+#define QSERDES_COM_RES_TRIM_CONTROL2 (0x120)
+#define QSERDES_COM_VCO_TUNE_CTRL (0x124)
+#define QSERDES_COM_VCO_TUNE_MAP (0x128)
+#define QSERDES_COM_VCO_TUNE1_MODE0 (0x12C)
+#define QSERDES_COM_VCO_TUNE2_MODE0 (0x130)
+#define QSERDES_COM_VCO_TUNE1_MODE1 (0x134)
+#define QSERDES_COM_VCO_TUNE2_MODE1 (0x138)
+#define QSERDES_COM_VCO_TUNE1_MODE2 (0x13C)
+#define QSERDES_COM_VCO_TUNE_INITVAL1 (0x13C)
+#define QSERDES_COM_VCO_TUNE2_MODE2 (0x140)
+#define QSERDES_COM_VCO_TUNE_INITVAL2 (0x140)
+#define QSERDES_COM_VCO_TUNE_TIMER1 (0x144)
+#define QSERDES_COM_VCO_TUNE_TIMER2 (0x148)
+#define QSERDES_COM_SAR (0x14C)
+#define QSERDES_COM_SAR_CLK (0x150)
+#define QSERDES_COM_SAR_CODE_OUT_STATUS (0x154)
+#define QSERDES_COM_SAR_CODE_READY_STATUS (0x158)
+#define QSERDES_COM_CMN_STATUS (0x15C)
+#define QSERDES_COM_RESET_SM_STATUS (0x160)
+#define QSERDES_COM_RESTRIM_CODE_STATUS (0x164)
+#define QSERDES_COM_PLLCAL_CODE1_STATUS (0x168)
+#define QSERDES_COM_PLLCAL_CODE2_STATUS (0x16C)
+#define QSERDES_COM_BG_CTRL (0x170)
+#define QSERDES_COM_CLK_SELECT (0x174)
+#define QSERDES_COM_HSCLK_SEL (0x178)
+#define QSERDES_COM_INTEGLOOP_BINCODE_STATUS (0x17C)
+#define QSERDES_COM_PLL_ANALOG (0x180)
+#define QSERDES_COM_CORECLK_DIV (0x184)
+#define QSERDES_COM_SW_RESET (0x188)
+#define QSERDES_COM_CORE_CLK_EN (0x18C)
+#define QSERDES_COM_C_READY_STATUS (0x190)
+#define QSERDES_COM_CMN_CONFIG (0x194)
+#define QSERDES_COM_CMN_RATE_OVERRIDE (0x198)
+#define QSERDES_COM_SVS_MODE_CLK_SEL (0x19C)
+#define QSERDES_COM_DEBUG_BUS0 (0x1A0)
+#define QSERDES_COM_DEBUG_BUS1 (0x1A4)
+#define QSERDES_COM_DEBUG_BUS2 (0x1A8)
+#define QSERDES_COM_DEBUG_BUS3 (0x1AC)
+#define QSERDES_COM_DEBUG_BUS_SEL (0x1B0)
+#define QSERDES_COM_CMN_MISC1 (0x1B4)
+#define QSERDES_COM_CMN_MISC2 (0x1B8)
+#define QSERDES_COM_CORECLK_DIV_MODE1 (0x1BC)
+#define QSERDES_COM_CORECLK_DIV_MODE2 (0x1C0)
+#define QSERDES_COM_CMN_RSVD5 (0x1C0)
+
+/* Tx Channel base addresses */
+#define HDMI_TX_L0_BASE_OFFSET (0x400)
+#define HDMI_TX_L1_BASE_OFFSET (0x600)
+#define HDMI_TX_L2_BASE_OFFSET (0x800)
+#define HDMI_TX_L3_BASE_OFFSET (0xA00)
+
+/* Tx Channel PHY registers */
+#define QSERDES_TX_L0_BIST_MODE_LANENO (0x000)
+#define QSERDES_TX_L0_BIST_INVERT (0x004)
+#define QSERDES_TX_L0_CLKBUF_ENABLE (0x008)
+#define QSERDES_TX_L0_CMN_CONTROL_ONE (0x00C)
+#define QSERDES_TX_L0_CMN_CONTROL_TWO (0x010)
+#define QSERDES_TX_L0_CMN_CONTROL_THREE (0x014)
+#define QSERDES_TX_L0_TX_EMP_POST1_LVL (0x018)
+#define QSERDES_TX_L0_TX_POST2_EMPH (0x01C)
+#define QSERDES_TX_L0_TX_BOOST_LVL_UP_DN (0x020)
+#define QSERDES_TX_L0_HP_PD_ENABLES (0x024)
+#define QSERDES_TX_L0_TX_IDLE_LVL_LARGE_AMP (0x028)
+#define QSERDES_TX_L0_TX_DRV_LVL (0x02C)
+#define QSERDES_TX_L0_TX_DRV_LVL_OFFSET (0x030)
+#define QSERDES_TX_L0_RESET_TSYNC_EN (0x034)
+#define QSERDES_TX_L0_PRE_STALL_LDO_BOOST_EN (0x038)
+#define QSERDES_TX_L0_TX_BAND (0x03C)
+#define QSERDES_TX_L0_SLEW_CNTL (0x040)
+#define QSERDES_TX_L0_INTERFACE_SELECT (0x044)
+#define QSERDES_TX_L0_LPB_EN (0x048)
+#define QSERDES_TX_L0_RES_CODE_LANE_TX (0x04C)
+#define QSERDES_TX_L0_RES_CODE_LANE_RX (0x050)
+#define QSERDES_TX_L0_RES_CODE_LANE_OFFSET (0x054)
+#define QSERDES_TX_L0_PERL_LENGTH1 (0x058)
+#define QSERDES_TX_L0_PERL_LENGTH2 (0x05C)
+#define QSERDES_TX_L0_SERDES_BYP_EN_OUT (0x060)
+#define QSERDES_TX_L0_DEBUG_BUS_SEL (0x064)
+#define QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN (0x068)
+#define QSERDES_TX_L0_TX_POL_INV (0x06C)
+#define QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN (0x070)
+#define QSERDES_TX_L0_BIST_PATTERN1 (0x074)
+#define QSERDES_TX_L0_BIST_PATTERN2 (0x078)
+#define QSERDES_TX_L0_BIST_PATTERN3 (0x07C)
+#define QSERDES_TX_L0_BIST_PATTERN4 (0x080)
+#define QSERDES_TX_L0_BIST_PATTERN5 (0x084)
+#define QSERDES_TX_L0_BIST_PATTERN6 (0x088)
+#define QSERDES_TX_L0_BIST_PATTERN7 (0x08C)
+#define QSERDES_TX_L0_BIST_PATTERN8 (0x090)
+#define QSERDES_TX_L0_LANE_MODE (0x094)
+#define QSERDES_TX_L0_IDAC_CAL_LANE_MODE (0x098)
+#define QSERDES_TX_L0_IDAC_CAL_LANE_MODE_CONFIGURATION (0x09C)
+#define QSERDES_TX_L0_ATB_SEL1 (0x0A0)
+#define QSERDES_TX_L0_ATB_SEL2 (0x0A4)
+#define QSERDES_TX_L0_RCV_DETECT_LVL (0x0A8)
+#define QSERDES_TX_L0_RCV_DETECT_LVL_2 (0x0AC)
+#define QSERDES_TX_L0_PRBS_SEED1 (0x0B0)
+#define QSERDES_TX_L0_PRBS_SEED2 (0x0B4)
+#define QSERDES_TX_L0_PRBS_SEED3 (0x0B8)
+#define QSERDES_TX_L0_PRBS_SEED4 (0x0BC)
+#define QSERDES_TX_L0_RESET_GEN (0x0C0)
+#define QSERDES_TX_L0_RESET_GEN_MUXES (0x0C4)
+#define QSERDES_TX_L0_TRAN_DRVR_EMP_EN (0x0C8)
+#define QSERDES_TX_L0_TX_INTERFACE_MODE (0x0CC)
+#define QSERDES_TX_L0_PWM_CTRL (0x0D0)
+#define QSERDES_TX_L0_PWM_ENCODED_OR_DATA (0x0D4)
+#define QSERDES_TX_L0_PWM_GEAR_1_DIVIDER_BAND2 (0x0D8)
+#define QSERDES_TX_L0_PWM_GEAR_2_DIVIDER_BAND2 (0x0DC)
+#define QSERDES_TX_L0_PWM_GEAR_3_DIVIDER_BAND2 (0x0E0)
+#define QSERDES_TX_L0_PWM_GEAR_4_DIVIDER_BAND2 (0x0E4)
+#define QSERDES_TX_L0_PWM_GEAR_1_DIVIDER_BAND0_1 (0x0E8)
+#define QSERDES_TX_L0_PWM_GEAR_2_DIVIDER_BAND0_1 (0x0EC)
+#define QSERDES_TX_L0_PWM_GEAR_3_DIVIDER_BAND0_1 (0x0F0)
+#define QSERDES_TX_L0_PWM_GEAR_4_DIVIDER_BAND0_1 (0x0F4)
+#define QSERDES_TX_L0_VMODE_CTRL1 (0x0F8)
+#define QSERDES_TX_L0_VMODE_CTRL2 (0x0FC)
+#define QSERDES_TX_L0_TX_ALOG_INTF_OBSV_CNTL (0x100)
+#define QSERDES_TX_L0_BIST_STATUS (0x104)
+#define QSERDES_TX_L0_BIST_ERROR_COUNT1 (0x108)
+#define QSERDES_TX_L0_BIST_ERROR_COUNT2 (0x10C)
+#define QSERDES_TX_L0_TX_ALOG_INTF_OBSV (0x110)
+
+/* HDMI PHY REGISTERS */
+#define HDMI_PHY_BASE_OFFSET (0xC00)
+
+#define HDMI_PHY_CFG (0x00)
+#define HDMI_PHY_PD_CTL (0x04)
+#define HDMI_PHY_MODE (0x08)
+#define HDMI_PHY_MISR_CLEAR (0x0C)
+#define HDMI_PHY_TX0_TX1_BIST_CFG0 (0x10)
+#define HDMI_PHY_TX0_TX1_BIST_CFG1 (0x14)
+#define HDMI_PHY_TX0_TX1_PRBS_SEED_BYTE0 (0x18)
+#define HDMI_PHY_TX0_TX1_PRBS_SEED_BYTE1 (0x1C)
+#define HDMI_PHY_TX0_TX1_BIST_PATTERN0 (0x20)
+#define HDMI_PHY_TX0_TX1_BIST_PATTERN1 (0x24)
+#define HDMI_PHY_TX2_TX3_BIST_CFG0 (0x28)
+#define HDMI_PHY_TX2_TX3_BIST_CFG1 (0x2C)
+#define HDMI_PHY_TX2_TX3_PRBS_SEED_BYTE0 (0x30)
+#define HDMI_PHY_TX2_TX3_PRBS_SEED_BYTE1 (0x34)
+#define HDMI_PHY_TX2_TX3_BIST_PATTERN0 (0x38)
+#define HDMI_PHY_TX2_TX3_BIST_PATTERN1 (0x3C)
+#define HDMI_PHY_DEBUG_BUS_SEL (0x40)
+#define HDMI_PHY_TXCAL_CFG0 (0x44)
+#define HDMI_PHY_TXCAL_CFG1 (0x48)
+#define HDMI_PHY_TX0_TX1_LANE_CTL (0x4C)
+#define HDMI_PHY_TX2_TX3_LANE_CTL (0x50)
+#define HDMI_PHY_LANE_BIST_CONFIG (0x54)
+#define HDMI_PHY_CLOCK (0x58)
+#define HDMI_PHY_MISC1 (0x5C)
+#define HDMI_PHY_MISC2 (0x60)
+#define HDMI_PHY_TX0_TX1_BIST_STATUS0 (0x64)
+#define HDMI_PHY_TX0_TX1_BIST_STATUS1 (0x68)
+#define HDMI_PHY_TX0_TX1_BIST_STATUS2 (0x6C)
+#define HDMI_PHY_TX2_TX3_BIST_STATUS0 (0x70)
+#define HDMI_PHY_TX2_TX3_BIST_STATUS1 (0x74)
+#define HDMI_PHY_TX2_TX3_BIST_STATUS2 (0x78)
+#define HDMI_PHY_PRE_MISR_STATUS0 (0x7C)
+#define HDMI_PHY_PRE_MISR_STATUS1 (0x80)
+#define HDMI_PHY_PRE_MISR_STATUS2 (0x84)
+#define HDMI_PHY_PRE_MISR_STATUS3 (0x88)
+#define HDMI_PHY_POST_MISR_STATUS0 (0x8C)
+#define HDMI_PHY_POST_MISR_STATUS1 (0x90)
+#define HDMI_PHY_POST_MISR_STATUS2 (0x94)
+#define HDMI_PHY_POST_MISR_STATUS3 (0x98)
+#define HDMI_PHY_STATUS (0x9C)
+#define HDMI_PHY_MISC3_STATUS (0xA0)
+#define HDMI_PHY_MISC4_STATUS (0xA4)
+#define HDMI_PHY_DEBUG_BUS0 (0xA8)
+#define HDMI_PHY_DEBUG_BUS1 (0xAC)
+#define HDMI_PHY_DEBUG_BUS2 (0xB0)
+#define HDMI_PHY_DEBUG_BUS3 (0xB4)
+#define HDMI_PHY_PHY_REVISION_ID0 (0xB8)
+#define HDMI_PHY_PHY_REVISION_ID1 (0xBC)
+#define HDMI_PHY_PHY_REVISION_ID2 (0xC0)
+#define HDMI_PHY_PHY_REVISION_ID3 (0xC4)
+
+#define HDMI_PLL_POLL_MAX_READS 100
+#define HDMI_PLL_POLL_TIMEOUT_US 1500
+
+enum hdmi_pll_freqs {
+ HDMI_PCLK_25200_KHZ,
+ HDMI_PCLK_27027_KHZ,
+ HDMI_PCLK_27000_KHZ,
+ HDMI_PCLK_74250_KHZ,
+ HDMI_PCLK_148500_KHZ,
+ HDMI_PCLK_154000_KHZ,
+ HDMI_PCLK_268500_KHZ,
+ HDMI_PCLK_297000_KHZ,
+ HDMI_PCLK_594000_KHZ,
+ HDMI_PCLK_MAX
+};
+
+struct hdmi_8996_phy_pll_reg_cfg {
+ u32 tx_l0_lane_mode;
+ u32 tx_l2_lane_mode;
+ u32 tx_l0_tx_band;
+ u32 tx_l1_tx_band;
+ u32 tx_l2_tx_band;
+ u32 tx_l3_tx_band;
+ u32 com_svs_mode_clk_sel;
+ u32 com_hsclk_sel;
+ u32 com_pll_cctrl_mode0;
+ u32 com_pll_rctrl_mode0;
+ u32 com_cp_ctrl_mode0;
+ u32 com_dec_start_mode0;
+ u32 com_div_frac_start1_mode0;
+ u32 com_div_frac_start2_mode0;
+ u32 com_div_frac_start3_mode0;
+ u32 com_integloop_gain0_mode0;
+ u32 com_integloop_gain1_mode0;
+ u32 com_lock_cmp_en;
+ u32 com_lock_cmp1_mode0;
+ u32 com_lock_cmp2_mode0;
+ u32 com_lock_cmp3_mode0;
+ u32 com_core_clk_en;
+ u32 com_coreclk_div;
+ u32 com_restrim_ctrl;
+ u32 com_vco_tune_ctrl;
+
+ u32 tx_l0_tx_drv_lvl;
+ u32 tx_l0_tx_emp_post1_lvl;
+ u32 tx_l1_tx_drv_lvl;
+ u32 tx_l1_tx_emp_post1_lvl;
+ u32 tx_l2_tx_drv_lvl;
+ u32 tx_l2_tx_emp_post1_lvl;
+ u32 tx_l3_tx_drv_lvl;
+ u32 tx_l3_tx_emp_post1_lvl;
+ u32 tx_l0_vmode_ctrl1;
+ u32 tx_l0_vmode_ctrl2;
+ u32 tx_l1_vmode_ctrl1;
+ u32 tx_l1_vmode_ctrl2;
+ u32 tx_l2_vmode_ctrl1;
+ u32 tx_l2_vmode_ctrl2;
+ u32 tx_l3_vmode_ctrl1;
+ u32 tx_l3_vmode_ctrl2;
+ u32 tx_l0_res_code_lane_tx;
+ u32 tx_l1_res_code_lane_tx;
+ u32 tx_l2_res_code_lane_tx;
+ u32 tx_l3_res_code_lane_tx;
+
+ u32 phy_mode;
+};
+
+struct hdmi_8996_v3_post_divider {
+ u64 vco_freq;
+ u64 hsclk_divsel;
+ u64 vco_ratio;
+ u64 tx_band_sel;
+ u64 half_rate_mode;
+};
+
+static inline struct hdmi_pll_vco_clk *to_hdmi_8996_vco_clk(struct clk *clk)
+{
+ return container_of(clk, struct hdmi_pll_vco_clk, c);
+}
+
+static inline u64 hdmi_8996_v1_get_post_div_lt_2g(u64 bclk)
+{
+ if (bclk >= HDMI_2400MHZ_BIT_CLK_HZ)
+ return 2;
+ else if (bclk >= HDMI_1700MHZ_BIT_CLK_HZ)
+ return 3;
+ else if (bclk >= HDMI_1200MHZ_BIT_CLK_HZ)
+ return 4;
+ else if (bclk >= HDMI_850MHZ_BIT_CLK_HZ)
+ return 3;
+ else if (bclk >= HDMI_600MHZ_BIT_CLK_HZ)
+ return 4;
+ else if (bclk >= HDMI_450MHZ_BIT_CLK_HZ)
+ return 3;
+ else if (bclk >= HDMI_300MHZ_BIT_CLK_HZ)
+ return 4;
+
+ return HDMI_64B_ERR_VAL;
+}
+
+static inline u64 hdmi_8996_v2_get_post_div_lt_2g(u64 bclk, u64 vco_range)
+{
+ u64 hdmi_8ghz = vco_range;
+ u64 tmp_calc;
+
+ hdmi_8ghz <<= 2;
+ tmp_calc = hdmi_8ghz;
+ do_div(tmp_calc, 6U);
+
+ if (bclk >= vco_range)
+ return 2;
+ else if (bclk >= tmp_calc)
+ return 3;
+ else if (bclk >= vco_range >> 1)
+ return 4;
+
+ tmp_calc = hdmi_8ghz;
+ do_div(tmp_calc, 12U);
+ if (bclk >= tmp_calc)
+ return 3;
+ else if (bclk >= vco_range >> 2)
+ return 4;
+
+ tmp_calc = hdmi_8ghz;
+ do_div(tmp_calc, 24U);
+ if (bclk >= tmp_calc)
+ return 3;
+ else if (bclk >= vco_range >> 3)
+ return 4;
+
+ return HDMI_64B_ERR_VAL;
+}
+
+static inline u64 hdmi_8996_v2_get_post_div_gt_2g(u64 hsclk)
+{
+ if (hsclk >= 0 && hsclk <= 3)
+ return hsclk + 1;
+
+ return HDMI_64B_ERR_VAL;
+}
+
+static inline u64 hdmi_8996_get_coreclk_div_lt_2g(u64 bclk)
+{
+ if (bclk >= HDMI_1334MHZ_BIT_CLK_HZ)
+ return 1;
+ else if (bclk >= HDMI_1000MHZ_BIT_CLK_HZ)
+ return 1;
+ else if (bclk >= HDMI_667MHZ_BIT_CLK_HZ)
+ return 2;
+ else if (bclk >= HDMI_500MHZ_BIT_CLK_HZ)
+ return 2;
+ else if (bclk >= HDMI_334MHZ_BIT_CLK_HZ)
+ return 3;
+ else if (bclk >= HDMI_250MHZ_BIT_CLK_HZ)
+ return 3;
+
+ return HDMI_64B_ERR_VAL;
+}
+
+static inline u64 hdmi_8996_get_coreclk_div_ratio(u64 clks_pll_divsel,
+ u64 coreclk_div)
+{
+ if (clks_pll_divsel == 0)
+ return coreclk_div*2;
+ else if (clks_pll_divsel == 1)
+ return coreclk_div*4;
+
+ return HDMI_64B_ERR_VAL;
+}
+
+static inline u64 hdmi_8996_v1_get_tx_band(u64 bclk)
+{
+ if (bclk >= 2400000000UL)
+ return 0;
+ if (bclk >= 1200000000UL)
+ return 1;
+ if (bclk >= 600000000UL)
+ return 2;
+ if (bclk >= 300000000UL)
+ return 3;
+
+ return HDMI_64B_ERR_VAL;
+}
+
+static inline u64 hdmi_8996_v2_get_tx_band(u64 bclk, u64 vco_range)
+{
+ if (bclk >= vco_range)
+ return 0;
+ else if (bclk >= vco_range >> 1)
+ return 1;
+ else if (bclk >= vco_range >> 2)
+ return 2;
+ else if (bclk >= vco_range >> 3)
+ return 3;
+
+ return HDMI_64B_ERR_VAL;
+}
+
+static inline u64 hdmi_8996_v1_get_hsclk(u64 fdata)
+{
+ if (fdata >= 9600000000UL)
+ return 0;
+ else if (fdata >= 4800000000UL)
+ return 1;
+ else if (fdata >= 3200000000UL)
+ return 2;
+ else if (fdata >= 2400000000UL)
+ return 3;
+
+ return HDMI_64B_ERR_VAL;
+}
+
+static inline u64 hdmi_8996_v2_get_hsclk(u64 fdata, u64 vco_range)
+{
+ u64 tmp_calc = vco_range;
+
+ tmp_calc <<= 2;
+ do_div(tmp_calc, 3U);
+ if (fdata >= (vco_range << 2))
+ return 0;
+ else if (fdata >= (vco_range << 1))
+ return 1;
+ else if (fdata >= tmp_calc)
+ return 2;
+ else if (fdata >= vco_range)
+ return 3;
+
+ return HDMI_64B_ERR_VAL;
+
+}
+
+static inline u64 hdmi_8996_v2_get_vco_freq(u64 bclk, u64 vco_range)
+{
+ u64 tx_band_div_ratio = 1U << hdmi_8996_v2_get_tx_band(bclk, vco_range);
+ u64 pll_post_div_ratio;
+
+ if (bclk >= vco_range) {
+ u64 hsclk = hdmi_8996_v2_get_hsclk(bclk, vco_range);
+
+ pll_post_div_ratio = hdmi_8996_v2_get_post_div_gt_2g(hsclk);
+ } else {
+ pll_post_div_ratio = hdmi_8996_v2_get_post_div_lt_2g(bclk,
+ vco_range);
+ }
+
+ return bclk * (pll_post_div_ratio * tx_band_div_ratio);
+}
+
+static inline u64 hdmi_8996_v2_get_fdata(u64 bclk, u64 vco_range)
+{
+ if (bclk >= vco_range)
+ return bclk;
+
+ {
+ u64 tmp_calc = hdmi_8996_v2_get_vco_freq(bclk, vco_range);
+ u64 pll_post_div_ratio_lt_2g = hdmi_8996_v2_get_post_div_lt_2g(
+ bclk, vco_range);
+ if (pll_post_div_ratio_lt_2g == HDMI_64B_ERR_VAL)
+ return HDMI_64B_ERR_VAL;
+
+ do_div(tmp_calc, pll_post_div_ratio_lt_2g);
+ return tmp_calc;
+ }
+}
+
+static inline u64 hdmi_8996_get_cpctrl(u64 frac_start, bool gen_ssc)
+{
+ if ((frac_start != 0) ||
+ (gen_ssc == true))
+ /*
+ * This should be ROUND(11/(19.2/20))).
+ * Since ref clock does not change, hardcoding to 11
+ */
+ return 0xB;
+
+ return 0x23;
+}
+
+static inline u64 hdmi_8996_get_rctrl(u64 frac_start, bool gen_ssc)
+{
+ if ((frac_start != 0) || (gen_ssc == true))
+ return 0x16;
+
+ return 0x10;
+}
+
+static inline u64 hdmi_8996_get_cctrl(u64 frac_start, bool gen_ssc)
+{
+ if ((frac_start != 0) || (gen_ssc == true))
+ return 0x28;
+
+ return 0x1;
+}
+
+static inline u64 hdmi_8996_get_integloop_gain(u64 frac_start, bool gen_ssc)
+{
+ if ((frac_start != 0) || (gen_ssc == true))
+ return 0x80;
+
+ return 0xC4;
+}
+
+static inline u64 hdmi_8996_v3_get_integloop_gain(u64 frac_start, u64 bclk,
+ bool gen_ssc)
+{
+ u64 digclk_divsel = bclk >= HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2;
+ u64 base = ((frac_start != 0) || (gen_ssc == true)) ? 0x40 : 0xC4;
+
+ base <<= digclk_divsel;
+
+ return (base <= 2046 ? base : 0x7FE);
+}
+
+static inline u64 hdmi_8996_get_vco_tune(u64 fdata, u64 div)
+{
+ u64 vco_tune;
+
+ vco_tune = fdata * div;
+ do_div(vco_tune, 1000000);
+ vco_tune = 13000 - vco_tune - 256;
+ do_div(vco_tune, 5);
+
+ return vco_tune;
+}
+
+static inline u64 hdmi_8996_get_pll_cmp(u64 pll_cmp_cnt, u64 core_clk)
+{
+ u64 pll_cmp;
+ u64 rem;
+
+ pll_cmp = pll_cmp_cnt * core_clk;
+ rem = do_div(pll_cmp, HDMI_REF_CLOCK);
+ if (rem > (HDMI_REF_CLOCK >> 1))
+ pll_cmp++;
+ pll_cmp -= 1;
+
+ return pll_cmp;
+}
+
+static inline u64 hdmi_8996_v3_get_pll_cmp(u64 pll_cmp_cnt, u64 fdata)
+{
+ u64 dividend = pll_cmp_cnt * fdata;
+ u64 divisor = HDMI_REF_CLOCK * 10;
+ u64 rem;
+
+ rem = do_div(dividend, divisor);
+ if (rem > (divisor >> 1))
+ dividend++;
+
+ return dividend - 1;
+}
+
+static int hdmi_8996_v3_get_post_div(struct hdmi_8996_v3_post_divider *pd,
+ u64 bclk)
+{
+ u32 ratio[] = {2, 3, 4, 5, 6, 9, 10, 12, 14, 15, 20, 21, 25, 28, 35};
+ u32 tx_band_sel[] = {0, 1, 2, 3};
+ u64 vco_freq[60];
+ u64 vco, vco_optimal, half_rate_mode = 0;
+ int vco_optimal_index, vco_freq_index;
+ int i, j, k, x;
+
+ for (i = 0; i <= 1; i++) {
+ vco_optimal = HDMI_VCO_MAX_FREQ;
+ vco_optimal_index = -1;
+ vco_freq_index = 0;
+ for (j = 0; j < 15; j++) {
+ for (k = 0; k < 4; k++) {
+ u64 ratio_mult = ratio[j] << tx_band_sel[k];
+
+ vco = bclk >> half_rate_mode;
+ vco *= ratio_mult;
+ vco_freq[vco_freq_index++] = vco;
+ }
+ }
+
+ for (x = 0; x < 60; x++) {
+ u64 vco_tmp = vco_freq[x];
+
+ if ((vco_tmp >= HDMI_VCO_MIN_FREQ) &&
+ (vco_tmp <= vco_optimal)) {
+ vco_optimal = vco_tmp;
+ vco_optimal_index = x;
+ }
+ }
+
+ if (vco_optimal_index == -1) {
+ if (!half_rate_mode)
+ half_rate_mode++;
+ else
+ return -EINVAL;
+ } else {
+ pd->vco_freq = vco_optimal;
+ pd->tx_band_sel = tx_band_sel[vco_optimal_index % 4];
+ pd->vco_ratio = ratio[vco_optimal_index / 4];
+ break;
+ }
+ }
+
+ switch (pd->vco_ratio) {
+ case 2:
+ pd->hsclk_divsel = 0;
+ break;
+ case 3:
+ pd->hsclk_divsel = 4;
+ break;
+ case 4:
+ pd->hsclk_divsel = 8;
+ break;
+ case 5:
+ pd->hsclk_divsel = 12;
+ break;
+ case 6:
+ pd->hsclk_divsel = 1;
+ break;
+ case 9:
+ pd->hsclk_divsel = 5;
+ break;
+ case 10:
+ pd->hsclk_divsel = 2;
+ break;
+ case 12:
+ pd->hsclk_divsel = 9;
+ break;
+ case 14:
+ pd->hsclk_divsel = 3;
+ break;
+ case 15:
+ pd->hsclk_divsel = 13;
+ break;
+ case 20:
+ pd->hsclk_divsel = 10;
+ break;
+ case 21:
+ pd->hsclk_divsel = 7;
+ break;
+ case 25:
+ pd->hsclk_divsel = 14;
+ break;
+ case 28:
+ pd->hsclk_divsel = 11;
+ break;
+ case 35:
+ pd->hsclk_divsel = 15;
+ break;
+ };
+
+ return 0;
+}
+
+static int hdmi_8996_v1_calculate(u32 pix_clk,
+ struct hdmi_8996_phy_pll_reg_cfg *cfg)
+{
+ int rc = -EINVAL;
+ u64 fdata, clk_divtx, tmds_clk;
+ u64 bclk;
+ u64 post_div_gt_2g;
+ u64 post_div_lt_2g;
+ u64 coreclk_div1_lt_2g;
+ u64 core_clk_div_ratio;
+ u64 core_clk;
+ u64 pll_cmp;
+ u64 tx_band;
+ u64 tx_band_div_ratio;
+ u64 hsclk;
+ u64 dec_start;
+ u64 frac_start;
+ u64 pll_divisor = 4 * HDMI_REF_CLOCK;
+ u64 cpctrl;
+ u64 rctrl;
+ u64 cctrl;
+ u64 integloop_gain;
+ u64 vco_tune;
+ u64 vco_freq;
+ u64 rem;
+
+ /* FDATA, CLK_DIVTX, PIXEL_CLK, TMDS_CLK */
+ bclk = ((u64)pix_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO;
+
+ if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD)
+ tmds_clk = bclk/4;
+ else
+ tmds_clk = bclk;
+
+ post_div_lt_2g = hdmi_8996_v1_get_post_div_lt_2g(bclk);
+ if (post_div_lt_2g == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ coreclk_div1_lt_2g = hdmi_8996_get_coreclk_div_lt_2g(bclk);
+
+ core_clk_div_ratio = hdmi_8996_get_coreclk_div_ratio(
+ HDMI_CLKS_PLL_DIVSEL, HDMI_CORECLK_DIV);
+
+ tx_band = hdmi_8996_v1_get_tx_band(bclk);
+ if (tx_band == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ tx_band_div_ratio = 1 << tx_band;
+
+ if (bclk >= HDMI_2400MHZ_BIT_CLK_HZ) {
+ fdata = bclk;
+ hsclk = hdmi_8996_v1_get_hsclk(fdata);
+ if (hsclk == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ post_div_gt_2g = (hsclk <= 3) ? (hsclk + 1) : HDMI_64B_ERR_VAL;
+ if (post_div_gt_2g == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ vco_freq = bclk * (post_div_gt_2g * tx_band_div_ratio);
+ clk_divtx = vco_freq;
+ do_div(clk_divtx, post_div_gt_2g);
+ } else {
+ vco_freq = bclk * (post_div_lt_2g * tx_band_div_ratio);
+ fdata = vco_freq;
+ do_div(fdata, post_div_lt_2g);
+ hsclk = hdmi_8996_v1_get_hsclk(fdata);
+ if (hsclk == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ clk_divtx = vco_freq;
+ do_div(clk_divtx, post_div_lt_2g);
+ post_div_gt_2g = (hsclk <= 3) ? (hsclk + 1) : HDMI_64B_ERR_VAL;
+ if (post_div_gt_2g == HDMI_64B_ERR_VAL)
+ goto fail;
+ }
+
+ /* Decimal and fraction values */
+ dec_start = fdata * post_div_gt_2g;
+ do_div(dec_start, pll_divisor);
+ frac_start = ((pll_divisor - (((dec_start + 1) * pll_divisor) -
+ (fdata * post_div_gt_2g))) * (1 << 20));
+ rem = do_div(frac_start, pll_divisor);
+ /* Round off frac_start to closest integer */
+ if (rem >= (pll_divisor >> 1))
+ frac_start++;
+
+ cpctrl = hdmi_8996_get_cpctrl(frac_start, false);
+ rctrl = hdmi_8996_get_rctrl(frac_start, false);
+ cctrl = hdmi_8996_get_cctrl(frac_start, false);
+ integloop_gain = hdmi_8996_get_integloop_gain(frac_start, false);
+ vco_tune = hdmi_8996_get_vco_tune(fdata, post_div_gt_2g);
+
+ core_clk = clk_divtx;
+ do_div(core_clk, core_clk_div_ratio);
+ pll_cmp = hdmi_8996_get_pll_cmp(1024, core_clk);
+
+ /* Debug dump */
+ DEV_DBG("%s: VCO freq: %llu\n", __func__, vco_freq);
+ DEV_DBG("%s: fdata: %llu\n", __func__, fdata);
+ DEV_DBG("%s: CLK_DIVTX: %llu\n", __func__, clk_divtx);
+ DEV_DBG("%s: pix_clk: %d\n", __func__, pix_clk);
+ DEV_DBG("%s: tmds clk: %llu\n", __func__, tmds_clk);
+ DEV_DBG("%s: HSCLK_SEL: %llu\n", __func__, hsclk);
+ DEV_DBG("%s: DEC_START: %llu\n", __func__, dec_start);
+ DEV_DBG("%s: DIV_FRAC_START: %llu\n", __func__, frac_start);
+ DEV_DBG("%s: PLL_CPCTRL: %llu\n", __func__, cpctrl);
+ DEV_DBG("%s: PLL_RCTRL: %llu\n", __func__, rctrl);
+ DEV_DBG("%s: PLL_CCTRL: %llu\n", __func__, cctrl);
+ DEV_DBG("%s: INTEGLOOP_GAIN: %llu\n", __func__, integloop_gain);
+ DEV_DBG("%s: VCO_TUNE: %llu\n", __func__, vco_tune);
+ DEV_DBG("%s: TX_BAND: %llu\n", __func__, tx_band);
+ DEV_DBG("%s: PLL_CMP: %llu\n", __func__, pll_cmp);
+
+ /* Convert these values to register specific values */
+ cfg->tx_l0_lane_mode = 0x3;
+ cfg->tx_l2_lane_mode = 0x3;
+ cfg->tx_l0_tx_band = tx_band + 4;
+ cfg->tx_l1_tx_band = tx_band + 4;
+ cfg->tx_l2_tx_band = tx_band + 4;
+ cfg->tx_l3_tx_band = tx_band + 4;
+ cfg->tx_l0_res_code_lane_tx = 0x33;
+ cfg->tx_l1_res_code_lane_tx = 0x33;
+ cfg->tx_l2_res_code_lane_tx = 0x33;
+ cfg->tx_l3_res_code_lane_tx = 0x33;
+ cfg->com_restrim_ctrl = 0x0;
+ cfg->com_vco_tune_ctrl = 0x1C;
+
+ cfg->com_svs_mode_clk_sel =
+ (bclk >= HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2);
+ cfg->com_hsclk_sel = (0x28 | hsclk);
+ cfg->com_pll_cctrl_mode0 = cctrl;
+ cfg->com_pll_rctrl_mode0 = rctrl;
+ cfg->com_cp_ctrl_mode0 = cpctrl;
+ cfg->com_dec_start_mode0 = dec_start;
+ cfg->com_div_frac_start1_mode0 = (frac_start & 0xFF);
+ cfg->com_div_frac_start2_mode0 = ((frac_start & 0xFF00) >> 8);
+ cfg->com_div_frac_start3_mode0 = ((frac_start & 0xF0000) >> 16);
+ cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xFF);
+ cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xF00) >> 8);
+ cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xFF);
+ cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xFF00) >> 8);
+ cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16);
+ cfg->com_core_clk_en = (0x6C | (HDMI_CLKS_PLL_DIVSEL << 4));
+ cfg->com_coreclk_div = HDMI_CORECLK_DIV;
+
+ if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) {
+ cfg->tx_l0_tx_drv_lvl = 0x25;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l1_tx_drv_lvl = 0x25;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l2_tx_drv_lvl = 0x25;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l3_tx_drv_lvl = 0x22;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x27;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0D;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0D;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0D;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x00;
+ cfg->com_restrim_ctrl = 0x0;
+ } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) {
+ cfg->tx_l0_tx_drv_lvl = 0x25;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l1_tx_drv_lvl = 0x25;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l2_tx_drv_lvl = 0x25;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l3_tx_drv_lvl = 0x25;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0D;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0D;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0D;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x00;
+ cfg->com_restrim_ctrl = 0x0;
+ } else {
+ cfg->tx_l0_tx_drv_lvl = 0x20;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l1_tx_drv_lvl = 0x20;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l2_tx_drv_lvl = 0x20;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l3_tx_drv_lvl = 0x20;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0E;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0E;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0E;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x0E;
+ cfg->com_restrim_ctrl = 0xD8;
+ }
+
+ cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0;
+ DEV_DBG("HDMI 8996 PLL: PLL Settings\n");
+ DEV_DBG("PLL PARAM: tx_l0_lane_mode = 0x%x\n", cfg->tx_l0_lane_mode);
+ DEV_DBG("PLL PARAM: tx_l2_lane_mode = 0x%x\n", cfg->tx_l2_lane_mode);
+ DEV_DBG("PLL PARAM: tx_l0_tx_band = 0x%x\n", cfg->tx_l0_tx_band);
+ DEV_DBG("PLL PARAM: tx_l1_tx_band = 0x%x\n", cfg->tx_l1_tx_band);
+ DEV_DBG("PLL PARAM: tx_l2_tx_band = 0x%x\n", cfg->tx_l2_tx_band);
+ DEV_DBG("PLL PARAM: tx_l3_tx_band = 0x%x\n", cfg->tx_l3_tx_band);
+ DEV_DBG("PLL PARAM: com_svs_mode_clk_sel = 0x%x\n",
+ cfg->com_svs_mode_clk_sel);
+ DEV_DBG("PLL PARAM: com_hsclk_sel = 0x%x\n", cfg->com_hsclk_sel);
+ DEV_DBG("PLL PARAM: com_pll_cctrl_mode0 = 0x%x\n",
+ cfg->com_pll_cctrl_mode0);
+ DEV_DBG("PLL PARAM: com_pll_rctrl_mode0 = 0x%x\n",
+ cfg->com_pll_rctrl_mode0);
+ DEV_DBG("PLL PARAM: com_cp_ctrl_mode0 = 0x%x\n",
+ cfg->com_cp_ctrl_mode0);
+ DEV_DBG("PLL PARAM: com_dec_start_mode0 = 0x%x\n",
+ cfg->com_dec_start_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start1_mode0 = 0x%x\n",
+ cfg->com_div_frac_start1_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start2_mode0 = 0x%x\n",
+ cfg->com_div_frac_start2_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start3_mode0 = 0x%x\n",
+ cfg->com_div_frac_start3_mode0);
+ DEV_DBG("PLL PARAM: com_integloop_gain0_mode0 = 0x%x\n",
+ cfg->com_integloop_gain0_mode0);
+ DEV_DBG("PLL PARAM: com_integloop_gain1_mode0 = 0x%x\n",
+ cfg->com_integloop_gain1_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp1_mode0 = 0x%x\n",
+ cfg->com_lock_cmp1_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp2_mode0 = 0x%x\n",
+ cfg->com_lock_cmp2_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp3_mode0 = 0x%x\n",
+ cfg->com_lock_cmp3_mode0);
+ DEV_DBG("PLL PARAM: com_core_clk_en = 0x%x\n", cfg->com_core_clk_en);
+ DEV_DBG("PLL PARAM: com_coreclk_div = 0x%x\n", cfg->com_coreclk_div);
+ DEV_DBG("PLL PARAM: com_restrim_ctrl = 0x%x\n", cfg->com_restrim_ctrl);
+
+ DEV_DBG("PLL PARAM: l0_tx_drv_lvl = 0x%x\n", cfg->tx_l0_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l0_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l0_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l1_tx_drv_lvl = 0x%x\n", cfg->tx_l1_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l1_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l1_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l2_tx_drv_lvl = 0x%x\n", cfg->tx_l2_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l2_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l2_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l3_tx_drv_lvl = 0x%x\n", cfg->tx_l3_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l3_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l3_tx_emp_post1_lvl);
+
+ DEV_DBG("PLL PARAM: l0_vmode_ctrl1 = 0x%x\n", cfg->tx_l0_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l0_vmode_ctrl2 = 0x%x\n", cfg->tx_l0_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l1_vmode_ctrl1 = 0x%x\n", cfg->tx_l1_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l1_vmode_ctrl2 = 0x%x\n", cfg->tx_l1_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l2_vmode_ctrl1 = 0x%x\n", cfg->tx_l2_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l2_vmode_ctrl2 = 0x%x\n", cfg->tx_l2_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l3_vmode_ctrl1 = 0x%x\n", cfg->tx_l3_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l3_vmode_ctrl2 = 0x%x\n", cfg->tx_l3_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: tx_l0_res_code_lane_tx = 0x%x\n",
+ cfg->tx_l0_res_code_lane_tx);
+ DEV_DBG("PLL PARAM: tx_l1_res_code_lane_tx = 0x%x\n",
+ cfg->tx_l1_res_code_lane_tx);
+ DEV_DBG("PLL PARAM: tx_l2_res_code_lane_tx = 0x%x\n",
+ cfg->tx_l2_res_code_lane_tx);
+ DEV_DBG("PLL PARAM: tx_l3_res_code_lane_tx = 0x%x\n",
+ cfg->tx_l3_res_code_lane_tx);
+
+ DEV_DBG("PLL PARAM: phy_mode = 0x%x\n", cfg->phy_mode);
+ rc = 0;
+fail:
+ return rc;
+}
+
+static int hdmi_8996_v2_calculate(u32 pix_clk,
+ struct hdmi_8996_phy_pll_reg_cfg *cfg)
+{
+ int rc = -EINVAL;
+ u64 fdata, clk_divtx, tmds_clk;
+ u64 bclk;
+ u64 post_div;
+ u64 core_clk_div;
+ u64 core_clk_div_ratio;
+ u64 core_clk;
+ u64 pll_cmp;
+ u64 tx_band;
+ u64 tx_band_div_ratio;
+ u64 hsclk;
+ u64 dec_start;
+ u64 frac_start;
+ u64 pll_divisor = 4 * HDMI_REF_CLOCK;
+ u64 cpctrl;
+ u64 rctrl;
+ u64 cctrl;
+ u64 integloop_gain;
+ u64 vco_tune;
+ u64 vco_freq;
+ u64 vco_range;
+ u64 rem;
+
+ /* FDATA, CLK_DIVTX, PIXEL_CLK, TMDS_CLK */
+ bclk = ((u64)pix_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO;
+
+ if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD)
+ tmds_clk = pix_clk >> 2;
+ else
+ tmds_clk = pix_clk;
+
+ vco_range = bclk < HDMI_282MHZ_BIT_CLK_HZ ? HDMI_2000MHZ_BIT_CLK_HZ :
+ HDMI_2250MHZ_BIT_CLK_HZ;
+
+ fdata = hdmi_8996_v2_get_fdata(bclk, vco_range);
+ if (fdata == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ hsclk = hdmi_8996_v2_get_hsclk(fdata, vco_range);
+ if (hsclk == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ if (bclk >= vco_range)
+ post_div = hdmi_8996_v2_get_post_div_gt_2g(hsclk);
+ else
+ post_div = hdmi_8996_v2_get_post_div_lt_2g(bclk, vco_range);
+
+ if (post_div == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ core_clk_div = 5;
+ core_clk_div_ratio = core_clk_div * 2;
+
+ tx_band = hdmi_8996_v2_get_tx_band(bclk, vco_range);
+ if (tx_band == HDMI_64B_ERR_VAL)
+ goto fail;
+
+ tx_band_div_ratio = 1 << tx_band;
+
+ vco_freq = hdmi_8996_v2_get_vco_freq(bclk, vco_range);
+ clk_divtx = vco_freq;
+ do_div(clk_divtx, post_div);
+
+ /* Decimal and fraction values */
+ dec_start = fdata * post_div;
+ do_div(dec_start, pll_divisor);
+ frac_start = ((pll_divisor - (((dec_start + 1) * pll_divisor) -
+ (fdata * post_div))) * (1 << 20));
+ rem = do_div(frac_start, pll_divisor);
+ /* Round off frac_start to closest integer */
+ if (rem >= (pll_divisor >> 1))
+ frac_start++;
+
+ cpctrl = hdmi_8996_get_cpctrl(frac_start, false);
+ rctrl = hdmi_8996_get_rctrl(frac_start, false);
+ cctrl = hdmi_8996_get_cctrl(frac_start, false);
+ integloop_gain = hdmi_8996_get_integloop_gain(frac_start, false);
+ vco_tune = hdmi_8996_get_vco_tune(fdata, post_div);
+
+ core_clk = clk_divtx;
+ do_div(core_clk, core_clk_div_ratio);
+ pll_cmp = hdmi_8996_get_pll_cmp(1024, core_clk);
+
+ /* Debug dump */
+ DEV_DBG("%s: VCO freq: %llu\n", __func__, vco_freq);
+ DEV_DBG("%s: fdata: %llu\n", __func__, fdata);
+ DEV_DBG("%s: CLK_DIVTX: %llu\n", __func__, clk_divtx);
+ DEV_DBG("%s: pix_clk: %d\n", __func__, pix_clk);
+ DEV_DBG("%s: tmds clk: %llu\n", __func__, tmds_clk);
+ DEV_DBG("%s: HSCLK_SEL: %llu\n", __func__, hsclk);
+ DEV_DBG("%s: DEC_START: %llu\n", __func__, dec_start);
+ DEV_DBG("%s: DIV_FRAC_START: %llu\n", __func__, frac_start);
+ DEV_DBG("%s: PLL_CPCTRL: %llu\n", __func__, cpctrl);
+ DEV_DBG("%s: PLL_RCTRL: %llu\n", __func__, rctrl);
+ DEV_DBG("%s: PLL_CCTRL: %llu\n", __func__, cctrl);
+ DEV_DBG("%s: INTEGLOOP_GAIN: %llu\n", __func__, integloop_gain);
+ DEV_DBG("%s: VCO_TUNE: %llu\n", __func__, vco_tune);
+ DEV_DBG("%s: TX_BAND: %llu\n", __func__, tx_band);
+ DEV_DBG("%s: PLL_CMP: %llu\n", __func__, pll_cmp);
+
+ /* Convert these values to register specific values */
+ cfg->tx_l0_lane_mode = 0x3;
+ cfg->tx_l2_lane_mode = 0x3;
+ cfg->tx_l0_tx_band = tx_band + 4;
+ cfg->tx_l1_tx_band = tx_band + 4;
+ cfg->tx_l2_tx_band = tx_band + 4;
+ cfg->tx_l3_tx_band = tx_band + 4;
+
+ if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD)
+ cfg->com_svs_mode_clk_sel = 1;
+ else
+ cfg->com_svs_mode_clk_sel = 2;
+
+ cfg->com_hsclk_sel = (0x28 | hsclk);
+ cfg->com_pll_cctrl_mode0 = cctrl;
+ cfg->com_pll_rctrl_mode0 = rctrl;
+ cfg->com_cp_ctrl_mode0 = cpctrl;
+ cfg->com_dec_start_mode0 = dec_start;
+ cfg->com_div_frac_start1_mode0 = (frac_start & 0xFF);
+ cfg->com_div_frac_start2_mode0 = ((frac_start & 0xFF00) >> 8);
+ cfg->com_div_frac_start3_mode0 = ((frac_start & 0xF0000) >> 16);
+ cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xFF);
+ cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xF00) >> 8);
+ cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xFF);
+ cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xFF00) >> 8);
+ cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16);
+ cfg->com_core_clk_en = (0x6C | (HDMI_CLKS_PLL_DIVSEL << 4));
+ cfg->com_coreclk_div = HDMI_CORECLK_DIV;
+ cfg->com_vco_tune_ctrl = 0x0;
+
+ if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) {
+ cfg->tx_l0_tx_drv_lvl = 0x25;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l1_tx_drv_lvl = 0x25;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l2_tx_drv_lvl = 0x25;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l3_tx_drv_lvl = 0x22;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x27;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0D;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0D;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0D;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x00;
+ cfg->tx_l0_res_code_lane_tx = 0x3F;
+ cfg->tx_l1_res_code_lane_tx = 0x3F;
+ cfg->tx_l2_res_code_lane_tx = 0x3F;
+ cfg->tx_l3_res_code_lane_tx = 0x3F;
+ cfg->com_restrim_ctrl = 0x0;
+ } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) {
+ cfg->tx_l0_tx_drv_lvl = 0x25;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l1_tx_drv_lvl = 0x25;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l2_tx_drv_lvl = 0x25;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l3_tx_drv_lvl = 0x25;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0D;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0D;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0D;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x00;
+ cfg->tx_l0_res_code_lane_tx = 0x39;
+ cfg->tx_l1_res_code_lane_tx = 0x39;
+ cfg->tx_l2_res_code_lane_tx = 0x39;
+ cfg->tx_l3_res_code_lane_tx = 0x39;
+ cfg->com_restrim_ctrl = 0x0;
+ } else {
+ cfg->tx_l0_tx_drv_lvl = 0x20;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l1_tx_drv_lvl = 0x20;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l2_tx_drv_lvl = 0x20;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l3_tx_drv_lvl = 0x20;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0E;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0E;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0E;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x0E;
+ cfg->tx_l0_res_code_lane_tx = 0x3F;
+ cfg->tx_l1_res_code_lane_tx = 0x3F;
+ cfg->tx_l2_res_code_lane_tx = 0x3F;
+ cfg->tx_l3_res_code_lane_tx = 0x3F;
+ cfg->com_restrim_ctrl = 0xD8;
+ }
+
+ cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0;
+ DEV_DBG("HDMI 8996 PLL: PLL Settings\n");
+ DEV_DBG("PLL PARAM: tx_l0_lane_mode = 0x%x\n", cfg->tx_l0_lane_mode);
+ DEV_DBG("PLL PARAM: tx_l2_lane_mode = 0x%x\n", cfg->tx_l2_lane_mode);
+ DEV_DBG("PLL PARAM: tx_l0_tx_band = 0x%x\n", cfg->tx_l0_tx_band);
+ DEV_DBG("PLL PARAM: tx_l1_tx_band = 0x%x\n", cfg->tx_l1_tx_band);
+ DEV_DBG("PLL PARAM: tx_l2_tx_band = 0x%x\n", cfg->tx_l2_tx_band);
+ DEV_DBG("PLL PARAM: tx_l3_tx_band = 0x%x\n", cfg->tx_l3_tx_band);
+ DEV_DBG("PLL PARAM: com_svs_mode_clk_sel = 0x%x\n",
+ cfg->com_svs_mode_clk_sel);
+ DEV_DBG("PLL PARAM: com_vco_tune_ctrl = 0x%x\n",
+ cfg->com_vco_tune_ctrl);
+ DEV_DBG("PLL PARAM: com_hsclk_sel = 0x%x\n", cfg->com_hsclk_sel);
+ DEV_DBG("PLL PARAM: com_lock_cmp_en = 0x%x\n", cfg->com_lock_cmp_en);
+ DEV_DBG("PLL PARAM: com_pll_cctrl_mode0 = 0x%x\n",
+ cfg->com_pll_cctrl_mode0);
+ DEV_DBG("PLL PARAM: com_pll_rctrl_mode0 = 0x%x\n",
+ cfg->com_pll_rctrl_mode0);
+ DEV_DBG("PLL PARAM: com_cp_ctrl_mode0 = 0x%x\n",
+ cfg->com_cp_ctrl_mode0);
+ DEV_DBG("PLL PARAM: com_dec_start_mode0 = 0x%x\n",
+ cfg->com_dec_start_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start1_mode0 = 0x%x\n",
+ cfg->com_div_frac_start1_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start2_mode0 = 0x%x\n",
+ cfg->com_div_frac_start2_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start3_mode0 = 0x%x\n",
+ cfg->com_div_frac_start3_mode0);
+ DEV_DBG("PLL PARAM: com_integloop_gain0_mode0 = 0x%x\n",
+ cfg->com_integloop_gain0_mode0);
+ DEV_DBG("PLL PARAM: com_integloop_gain1_mode0 = 0x%x\n",
+ cfg->com_integloop_gain1_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp1_mode0 = 0x%x\n",
+ cfg->com_lock_cmp1_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp2_mode0 = 0x%x\n",
+ cfg->com_lock_cmp2_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp3_mode0 = 0x%x\n",
+ cfg->com_lock_cmp3_mode0);
+ DEV_DBG("PLL PARAM: com_core_clk_en = 0x%x\n", cfg->com_core_clk_en);
+ DEV_DBG("PLL PARAM: com_coreclk_div = 0x%x\n", cfg->com_coreclk_div);
+
+ DEV_DBG("PLL PARAM: l0_tx_drv_lvl = 0x%x\n", cfg->tx_l0_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l0_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l0_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l1_tx_drv_lvl = 0x%x\n", cfg->tx_l1_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l1_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l1_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l2_tx_drv_lvl = 0x%x\n", cfg->tx_l2_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l2_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l2_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l3_tx_drv_lvl = 0x%x\n", cfg->tx_l3_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l3_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l3_tx_emp_post1_lvl);
+
+ DEV_DBG("PLL PARAM: l0_vmode_ctrl1 = 0x%x\n", cfg->tx_l0_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l0_vmode_ctrl2 = 0x%x\n", cfg->tx_l0_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l1_vmode_ctrl1 = 0x%x\n", cfg->tx_l1_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l1_vmode_ctrl2 = 0x%x\n", cfg->tx_l1_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l2_vmode_ctrl1 = 0x%x\n", cfg->tx_l2_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l2_vmode_ctrl2 = 0x%x\n", cfg->tx_l2_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l3_vmode_ctrl1 = 0x%x\n", cfg->tx_l3_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l3_vmode_ctrl2 = 0x%x\n", cfg->tx_l3_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: tx_l0_res_code_lane_tx = 0x%x\n",
+ cfg->tx_l0_res_code_lane_tx);
+ DEV_DBG("PLL PARAM: tx_l1_res_code_lane_tx = 0x%x\n",
+ cfg->tx_l1_res_code_lane_tx);
+ DEV_DBG("PLL PARAM: tx_l2_res_code_lane_tx = 0x%x\n",
+ cfg->tx_l2_res_code_lane_tx);
+ DEV_DBG("PLL PARAM: tx_l3_res_code_lane_tx = 0x%x\n",
+ cfg->tx_l3_res_code_lane_tx);
+ DEV_DBG("PLL PARAM: com_restrim_ctrl = 0x%x\n", cfg->com_restrim_ctrl);
+
+ DEV_DBG("PLL PARAM: phy_mode = 0x%x\n", cfg->phy_mode);
+ rc = 0;
+fail:
+ return rc;
+}
+
+static int hdmi_8996_v3_calculate(u32 pix_clk,
+ struct hdmi_8996_phy_pll_reg_cfg *cfg)
+{
+ int rc = -EINVAL;
+ struct hdmi_8996_v3_post_divider pd;
+ u64 fdata, tmds_clk;
+ u64 bclk;
+ u64 pll_cmp;
+ u64 tx_band;
+ u64 hsclk;
+ u64 dec_start;
+ u64 frac_start;
+ u64 pll_divisor = 4 * HDMI_REF_CLOCK;
+ u64 cpctrl;
+ u64 rctrl;
+ u64 cctrl;
+ u64 integloop_gain;
+ u64 vco_freq;
+ u64 rem;
+
+ /* FDATA, HSCLK, PIXEL_CLK, TMDS_CLK */
+ bclk = ((u64)pix_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO;
+
+ if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD)
+ tmds_clk = pix_clk >> 2;
+ else
+ tmds_clk = pix_clk;
+
+ if (hdmi_8996_v3_get_post_div(&pd, bclk) || pd.vco_ratio <= 0 ||
+ pd.vco_freq <= 0)
+ goto fail;
+
+ vco_freq = pd.vco_freq;
+ fdata = pd.vco_freq;
+ do_div(fdata, pd.vco_ratio);
+
+ hsclk = pd.hsclk_divsel;
+ dec_start = vco_freq;
+ do_div(dec_start, pll_divisor);
+
+ frac_start = vco_freq * (1 << 20);
+ rem = do_div(frac_start, pll_divisor);
+ frac_start -= dec_start * (1 << 20);
+ if (rem > (pll_divisor >> 1))
+ frac_start++;
+
+ cpctrl = hdmi_8996_get_cpctrl(frac_start, false);
+ rctrl = hdmi_8996_get_rctrl(frac_start, false);
+ cctrl = hdmi_8996_get_cctrl(frac_start, false);
+ integloop_gain = hdmi_8996_v3_get_integloop_gain(frac_start, bclk,
+ false);
+ pll_cmp = hdmi_8996_v3_get_pll_cmp(1024, fdata);
+ tx_band = pd.tx_band_sel;
+
+ /* Debug dump */
+ DEV_DBG("%s: VCO freq: %llu\n", __func__, vco_freq);
+ DEV_DBG("%s: fdata: %llu\n", __func__, fdata);
+ DEV_DBG("%s: pix_clk: %d\n", __func__, pix_clk);
+ DEV_DBG("%s: tmds clk: %llu\n", __func__, tmds_clk);
+ DEV_DBG("%s: HSCLK_SEL: %llu\n", __func__, hsclk);
+ DEV_DBG("%s: DEC_START: %llu\n", __func__, dec_start);
+ DEV_DBG("%s: DIV_FRAC_START: %llu\n", __func__, frac_start);
+ DEV_DBG("%s: PLL_CPCTRL: %llu\n", __func__, cpctrl);
+ DEV_DBG("%s: PLL_RCTRL: %llu\n", __func__, rctrl);
+ DEV_DBG("%s: PLL_CCTRL: %llu\n", __func__, cctrl);
+ DEV_DBG("%s: INTEGLOOP_GAIN: %llu\n", __func__, integloop_gain);
+ DEV_DBG("%s: TX_BAND: %llu\n", __func__, tx_band);
+ DEV_DBG("%s: PLL_CMP: %llu\n", __func__, pll_cmp);
+
+ /* Convert these values to register specific values */
+ cfg->tx_l0_tx_band = tx_band + 4;
+ cfg->tx_l1_tx_band = tx_band + 4;
+ cfg->tx_l2_tx_band = tx_band + 4;
+ cfg->tx_l3_tx_band = tx_band + 4;
+
+ if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD)
+ cfg->com_svs_mode_clk_sel = 1;
+ else
+ cfg->com_svs_mode_clk_sel = 2;
+
+ cfg->com_hsclk_sel = (0x20 | hsclk);
+ cfg->com_pll_cctrl_mode0 = cctrl;
+ cfg->com_pll_rctrl_mode0 = rctrl;
+ cfg->com_cp_ctrl_mode0 = cpctrl;
+ cfg->com_dec_start_mode0 = dec_start;
+ cfg->com_div_frac_start1_mode0 = (frac_start & 0xFF);
+ cfg->com_div_frac_start2_mode0 = ((frac_start & 0xFF00) >> 8);
+ cfg->com_div_frac_start3_mode0 = ((frac_start & 0xF0000) >> 16);
+ cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xFF);
+ cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xF00) >> 8);
+ cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xFF);
+ cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xFF00) >> 8);
+ cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16);
+ cfg->com_lock_cmp_en = 0x04;
+ cfg->com_core_clk_en = 0x2C;
+ cfg->com_coreclk_div = HDMI_CORECLK_DIV;
+ cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0;
+ cfg->com_vco_tune_ctrl = 0x0;
+
+ cfg->tx_l0_lane_mode = 0x43;
+ cfg->tx_l2_lane_mode = 0x43;
+
+ if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) {
+ cfg->tx_l0_tx_drv_lvl = 0x25;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l1_tx_drv_lvl = 0x25;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l2_tx_drv_lvl = 0x25;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l3_tx_drv_lvl = 0x22;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x27;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0D;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0D;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0D;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x00;
+ } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) {
+ cfg->tx_l0_tx_drv_lvl = 0x25;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l1_tx_drv_lvl = 0x25;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l2_tx_drv_lvl = 0x25;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l3_tx_drv_lvl = 0x25;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x23;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0D;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0D;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0D;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x00;
+ } else {
+ cfg->tx_l0_tx_drv_lvl = 0x20;
+ cfg->tx_l0_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l1_tx_drv_lvl = 0x20;
+ cfg->tx_l1_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l2_tx_drv_lvl = 0x20;
+ cfg->tx_l2_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l3_tx_drv_lvl = 0x20;
+ cfg->tx_l3_tx_emp_post1_lvl = 0x20;
+ cfg->tx_l0_vmode_ctrl1 = 0x00;
+ cfg->tx_l0_vmode_ctrl2 = 0x0E;
+ cfg->tx_l1_vmode_ctrl1 = 0x00;
+ cfg->tx_l1_vmode_ctrl2 = 0x0E;
+ cfg->tx_l2_vmode_ctrl1 = 0x00;
+ cfg->tx_l2_vmode_ctrl2 = 0x0E;
+ cfg->tx_l3_vmode_ctrl1 = 0x00;
+ cfg->tx_l3_vmode_ctrl2 = 0x0E;
+ }
+
+ DEV_DBG("HDMI 8996 PLL: PLL Settings\n");
+ DEV_DBG("PLL PARAM: tx_l0_tx_band = 0x%x\n", cfg->tx_l0_tx_band);
+ DEV_DBG("PLL PARAM: tx_l1_tx_band = 0x%x\n", cfg->tx_l1_tx_band);
+ DEV_DBG("PLL PARAM: tx_l2_tx_band = 0x%x\n", cfg->tx_l2_tx_band);
+ DEV_DBG("PLL PARAM: tx_l3_tx_band = 0x%x\n", cfg->tx_l3_tx_band);
+ DEV_DBG("PLL PARAM: com_svs_mode_clk_sel = 0x%x\n",
+ cfg->com_svs_mode_clk_sel);
+ DEV_DBG("PLL PARAM: com_hsclk_sel = 0x%x\n", cfg->com_hsclk_sel);
+ DEV_DBG("PLL PARAM: com_lock_cmp_en = 0x%x\n", cfg->com_lock_cmp_en);
+ DEV_DBG("PLL PARAM: com_pll_cctrl_mode0 = 0x%x\n",
+ cfg->com_pll_cctrl_mode0);
+ DEV_DBG("PLL PARAM: com_pll_rctrl_mode0 = 0x%x\n",
+ cfg->com_pll_rctrl_mode0);
+ DEV_DBG("PLL PARAM: com_cp_ctrl_mode0 = 0x%x\n",
+ cfg->com_cp_ctrl_mode0);
+ DEV_DBG("PLL PARAM: com_dec_start_mode0 = 0x%x\n",
+ cfg->com_dec_start_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start1_mode0 = 0x%x\n",
+ cfg->com_div_frac_start1_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start2_mode0 = 0x%x\n",
+ cfg->com_div_frac_start2_mode0);
+ DEV_DBG("PLL PARAM: com_div_frac_start3_mode0 = 0x%x\n",
+ cfg->com_div_frac_start3_mode0);
+ DEV_DBG("PLL PARAM: com_integloop_gain0_mode0 = 0x%x\n",
+ cfg->com_integloop_gain0_mode0);
+ DEV_DBG("PLL PARAM: com_integloop_gain1_mode0 = 0x%x\n",
+ cfg->com_integloop_gain1_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp1_mode0 = 0x%x\n",
+ cfg->com_lock_cmp1_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp2_mode0 = 0x%x\n",
+ cfg->com_lock_cmp2_mode0);
+ DEV_DBG("PLL PARAM: com_lock_cmp3_mode0 = 0x%x\n",
+ cfg->com_lock_cmp3_mode0);
+ DEV_DBG("PLL PARAM: com_core_clk_en = 0x%x\n", cfg->com_core_clk_en);
+ DEV_DBG("PLL PARAM: com_coreclk_div = 0x%x\n", cfg->com_coreclk_div);
+ DEV_DBG("PLL PARAM: phy_mode = 0x%x\n", cfg->phy_mode);
+
+ DEV_DBG("PLL PARAM: tx_l0_lane_mode = 0x%x\n", cfg->tx_l0_lane_mode);
+ DEV_DBG("PLL PARAM: tx_l2_lane_mode = 0x%x\n", cfg->tx_l2_lane_mode);
+ DEV_DBG("PLL PARAM: l0_tx_drv_lvl = 0x%x\n", cfg->tx_l0_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l0_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l0_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l1_tx_drv_lvl = 0x%x\n", cfg->tx_l1_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l1_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l1_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l2_tx_drv_lvl = 0x%x\n", cfg->tx_l2_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l2_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l2_tx_emp_post1_lvl);
+ DEV_DBG("PLL PARAM: l3_tx_drv_lvl = 0x%x\n", cfg->tx_l3_tx_drv_lvl);
+ DEV_DBG("PLL PARAM: l3_tx_emp_post1_lvl = 0x%x\n",
+ cfg->tx_l3_tx_emp_post1_lvl);
+
+ DEV_DBG("PLL PARAM: l0_vmode_ctrl1 = 0x%x\n", cfg->tx_l0_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l0_vmode_ctrl2 = 0x%x\n", cfg->tx_l0_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l1_vmode_ctrl1 = 0x%x\n", cfg->tx_l1_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l1_vmode_ctrl2 = 0x%x\n", cfg->tx_l1_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l2_vmode_ctrl1 = 0x%x\n", cfg->tx_l2_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l2_vmode_ctrl2 = 0x%x\n", cfg->tx_l2_vmode_ctrl2);
+ DEV_DBG("PLL PARAM: l3_vmode_ctrl1 = 0x%x\n", cfg->tx_l3_vmode_ctrl1);
+ DEV_DBG("PLL PARAM: l3_vmode_ctrl2 = 0x%x\n", cfg->tx_l3_vmode_ctrl2);
+ rc = 0;
+fail:
+ return rc;
+}
+
+static int hdmi_8996_calculate(u32 pix_clk,
+ struct hdmi_8996_phy_pll_reg_cfg *cfg, u32 ver)
+{
+ switch (ver) {
+ case HDMI_VERSION_8996_V3:
+ case HDMI_VERSION_8996_V3_1_8:
+ return hdmi_8996_v3_calculate(pix_clk, cfg);
+ case HDMI_VERSION_8996_V2:
+ return hdmi_8996_v2_calculate(pix_clk, cfg);
+ default:
+ return hdmi_8996_v1_calculate(pix_clk, cfg);
+ }
+}
+
+static int hdmi_8996_phy_pll_set_clk_rate(struct clk *c, u32 tmds_clk, u32 ver)
+{
+ int rc = 0;
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+ struct hdmi_8996_phy_pll_reg_cfg cfg = {0};
+
+ rc = hdmi_8996_calculate(tmds_clk, &cfg, ver);
+ if (rc) {
+ DEV_ERR("%s: PLL calculation failed\n", __func__);
+ return rc;
+ }
+
+ /* Initially shut down PHY */
+ DEV_DBG("%s: Disabling PHY\n", __func__);
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_PD_CTL, 0x0);
+ udelay(500);
+
+ /* Power up sequence */
+ switch (ver) {
+ case HDMI_VERSION_8996_V2:
+ case HDMI_VERSION_8996_V3:
+ case HDMI_VERSION_8996_V3_1_8:
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_CTRL, 0x04);
+ break;
+ };
+
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_PD_CTL, 0x1);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RESETSM_CNTRL, 0x20);
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TX0_TX1_LANE_CTL, 0x0F);
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TX2_TX3_LANE_CTL, 0x0F);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_CLKBUF_ENABLE, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_CLKBUF_ENABLE, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_CLKBUF_ENABLE, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_CLKBUF_ENABLE, 0x03);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_LANE_MODE, cfg.tx_l0_lane_mode);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_LANE_MODE, cfg.tx_l2_lane_mode);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_TX_BAND, cfg.tx_l0_tx_band);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_TX_BAND, cfg.tx_l1_tx_band);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_TX_BAND, cfg.tx_l2_tx_band);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_TX_BAND, cfg.tx_l3_tx_band);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_RESET_TSYNC_EN, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_RESET_TSYNC_EN, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_RESET_TSYNC_EN, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_RESET_TSYNC_EN, 0x03);
+
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1E);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x07);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SYSCLK_EN_SEL, 0x37);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SYS_CLK_CTRL, 0x02);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CLK_ENABLE1, 0x0E);
+ if (ver == HDMI_VERSION_8996_V1)
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_CTRL, 0x06);
+
+ /* Bypass VCO calibration */
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SVS_MODE_CLK_SEL,
+ cfg.com_svs_mode_clk_sel);
+
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_TRIM, 0x0F);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_IVCO, 0x0F);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE_CTRL,
+ cfg.com_vco_tune_ctrl);
+
+ switch (ver) {
+ case HDMI_VERSION_8996_V1:
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SVS_MODE_CLK_SEL,
+ cfg.com_svs_mode_clk_sel);
+ break;
+ default:
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_CTRL, 0x06);
+ }
+
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CLK_SELECT, 0x30);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_HSCLK_SEL,
+ cfg.com_hsclk_sel);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_EN,
+ cfg.com_lock_cmp_en);
+
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_CCTRL_MODE0,
+ cfg.com_pll_cctrl_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_RCTRL_MODE0,
+ cfg.com_pll_rctrl_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CP_CTRL_MODE0,
+ cfg.com_cp_ctrl_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DEC_START_MODE0,
+ cfg.com_dec_start_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START1_MODE0,
+ cfg.com_div_frac_start1_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START2_MODE0,
+ cfg.com_div_frac_start2_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START3_MODE0,
+ cfg.com_div_frac_start3_mode0);
+
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_INTEGLOOP_GAIN0_MODE0,
+ cfg.com_integloop_gain0_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_INTEGLOOP_GAIN1_MODE0,
+ cfg.com_integloop_gain1_mode0);
+
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP1_MODE0,
+ cfg.com_lock_cmp1_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP2_MODE0,
+ cfg.com_lock_cmp2_mode0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP3_MODE0,
+ cfg.com_lock_cmp3_mode0);
+
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE_MAP, 0x00);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CORE_CLK_EN,
+ cfg.com_core_clk_en);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CORECLK_DIV,
+ cfg.com_coreclk_div);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_CONFIG, 0x02);
+
+ if (ver == HDMI_VERSION_8996_V3 || ver == HDMI_VERSION_8996_V3_1_8)
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RESCODE_DIV_NUM, 0x15);
+
+ /* TX lanes setup (TX 0/1/2/3) */
+ if (ver == HDMI_VERSION_8996_V3_1_8) {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL,
+ 0x00000023);
+ } else {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL,
+ cfg.tx_l0_tx_drv_lvl);
+ }
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_TX_EMP_POST1_LVL,
+ cfg.tx_l0_tx_emp_post1_lvl);
+
+ if (ver == HDMI_VERSION_8996_V3_1_8) {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL,
+ 0x00000023);
+ } else {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL,
+ cfg.tx_l1_tx_drv_lvl);
+ }
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_TX_EMP_POST1_LVL,
+ cfg.tx_l1_tx_emp_post1_lvl);
+
+ if (ver == HDMI_VERSION_8996_V3_1_8) {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL,
+ 0x00000023);
+ } else {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL,
+ cfg.tx_l2_tx_drv_lvl);
+ }
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_TX_EMP_POST1_LVL,
+ cfg.tx_l2_tx_emp_post1_lvl);
+
+ if (ver == HDMI_VERSION_8996_V3_1_8) {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL,
+ 0x00000020);
+ } else {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL,
+ cfg.tx_l3_tx_drv_lvl);
+ }
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_TX_EMP_POST1_LVL,
+ cfg.tx_l3_tx_emp_post1_lvl);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL1,
+ cfg.tx_l0_vmode_ctrl1);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL2,
+ cfg.tx_l0_vmode_ctrl2);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL1,
+ cfg.tx_l1_vmode_ctrl1);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL2,
+ cfg.tx_l1_vmode_ctrl2);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL1,
+ cfg.tx_l2_vmode_ctrl1);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL2,
+ cfg.tx_l2_vmode_ctrl2);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL1,
+ cfg.tx_l3_vmode_ctrl1);
+ if (ver == HDMI_VERSION_8996_V3_1_8) {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL2,
+ 0x0000000D);
+ } else {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_VMODE_CTRL2,
+ cfg.tx_l3_vmode_ctrl2);
+ }
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00);
+
+ if (ver < HDMI_VERSION_8996_V3) {
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_RES_CODE_LANE_TX,
+ cfg.tx_l0_res_code_lane_tx);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_RES_CODE_LANE_TX,
+ cfg.tx_l1_res_code_lane_tx);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_RES_CODE_LANE_TX,
+ cfg.tx_l2_res_code_lane_tx);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_RES_CODE_LANE_TX,
+ cfg.tx_l3_res_code_lane_tx);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RESTRIM_CTRL,
+ cfg.com_restrim_ctrl);
+
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TXCAL_CFG0, 0x00);
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TXCAL_CFG1, 0x05);
+ }
+
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_MODE, cfg.phy_mode);
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_PD_CTL, 0x1F);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40);
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_HP_PD_ENABLES, 0x0C);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_HP_PD_ENABLES, 0x0C);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_HP_PD_ENABLES, 0x0C);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_HP_PD_ENABLES, 0x03);
+
+ if (ver == HDMI_VERSION_8996_V2) {
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL1, 0x01);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL2, 0x01);
+ }
+ /*
+ * Ensure that vco configuration gets flushed to hardware before
+ * enabling the PLL
+ */
+ wmb();
+ return 0;
+}
+
+static int hdmi_8996_phy_ready_status(struct mdss_pll_resources *io)
+{
+ u32 status = 0;
+ int phy_ready = 0;
+ int rc;
+ u32 read_count = 0;
+
+ rc = mdss_pll_resource_enable(io, true);
+ if (rc) {
+ DEV_ERR("%s: pll resource can't be enabled\n", __func__);
+ return rc;
+ }
+
+ DEV_DBG("%s: Waiting for PHY Ready\n", __func__);
+
+ /* Poll for PHY read status */
+ while (read_count < HDMI_PLL_POLL_MAX_READS) {
+ status = MDSS_PLL_REG_R(io->phy_base, HDMI_PHY_STATUS);
+ if ((status & BIT(0)) == 1) {
+ phy_ready = 1;
+ DEV_DBG("%s: PHY READY\n", __func__);
+ break;
+ }
+ udelay(HDMI_PLL_POLL_TIMEOUT_US);
+ read_count++;
+ }
+
+ if (read_count == HDMI_PLL_POLL_MAX_READS) {
+ phy_ready = 0;
+ DEV_DBG("%s: PHY READY TIMEOUT\n", __func__);
+ }
+
+ mdss_pll_resource_enable(io, false);
+
+ return phy_ready;
+}
+
+static int hdmi_8996_pll_lock_status(struct mdss_pll_resources *io)
+{
+ u32 status;
+ int pll_locked = 0;
+ int rc;
+ u32 read_count = 0;
+
+ rc = mdss_pll_resource_enable(io, true);
+ if (rc) {
+ DEV_ERR("%s: pll resource can't be enabled\n", __func__);
+ return rc;
+ }
+
+ DEV_DBG("%s: Waiting for PLL lock\n", __func__);
+
+ while (read_count < HDMI_PLL_POLL_MAX_READS) {
+ status = MDSS_PLL_REG_R(io->pll_base,
+ QSERDES_COM_C_READY_STATUS);
+ if ((status & BIT(0)) == 1) {
+ pll_locked = 1;
+ DEV_DBG("%s: C READY\n", __func__);
+ break;
+ }
+ udelay(HDMI_PLL_POLL_TIMEOUT_US);
+ read_count++;
+ }
+
+ if (read_count == HDMI_PLL_POLL_MAX_READS) {
+ pll_locked = 0;
+ DEV_DBG("%s: C READY TIMEOUT\n", __func__);
+ }
+
+ mdss_pll_resource_enable(io, false);
+
+ return pll_locked;
+}
+
+static int hdmi_8996_v1_perform_sw_calibration(struct clk *c)
+{
+ int rc = 0;
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+
+ u32 max_code = 0x190;
+ u32 min_code = 0x0;
+ u32 max_cnt = 0;
+ u32 min_cnt = 0;
+ u32 expected_counter_value = 0;
+ u32 step = 0;
+ u32 dbus_all = 0;
+ u32 dbus_sel = 0;
+ u32 vco_code = 0;
+ u32 val = 0;
+
+ vco_code = 0xC8;
+
+ DEV_DBG("%s: Starting SW calibration with vco_code = %d\n", __func__,
+ vco_code);
+
+ expected_counter_value =
+ (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP3_MODE0) << 16) |
+ (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP2_MODE0) << 8) |
+ (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP1_MODE0));
+
+ DEV_DBG("%s: expected_counter_value = %d\n", __func__,
+ expected_counter_value);
+
+ val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1);
+ val |= BIT(4);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val);
+
+ val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1);
+ val |= BIT(3);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val);
+
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DEBUG_BUS_SEL, 0x4);
+
+ val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG);
+ val |= BIT(1);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val);
+
+ udelay(60);
+
+ while (1) {
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE1_MODE0,
+ vco_code & 0xFF);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE2_MODE0,
+ (vco_code >> 8) & 0x3);
+
+ udelay(20);
+
+ val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG);
+ val &= ~BIT(1);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val);
+
+ udelay(60);
+
+ val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG);
+ val |= BIT(1);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val);
+
+ udelay(60);
+
+ dbus_all =
+ (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS3) << 24) |
+ (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS2) << 16) |
+ (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS1) << 8) |
+ (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS0));
+
+ dbus_sel = (dbus_all >> 9) & 0x3FFFF;
+ DEV_DBG("%s: loop[%d], dbus_all = 0x%x, dbus_sel = 0x%x\n",
+ __func__, step, dbus_all, dbus_sel);
+ if (dbus_sel == 0)
+ DEV_ERR("%s: CHECK HDMI REF CLK\n", __func__);
+
+ if (dbus_sel == expected_counter_value) {
+ max_code = vco_code;
+ max_cnt = dbus_sel;
+ min_code = vco_code;
+ min_cnt = dbus_sel;
+ } else if (dbus_sel == 0) {
+ max_code = vco_code;
+ max_cnt = dbus_sel;
+ vco_code = (max_code + min_code)/2;
+ } else if (dbus_sel > expected_counter_value) {
+ min_code = vco_code;
+ min_cnt = dbus_sel;
+ vco_code = (max_code + min_code)/2;
+ } else if (dbus_sel < expected_counter_value) {
+ max_code = vco_code;
+ max_cnt = dbus_sel;
+ vco_code = (max_code + min_code)/2;
+ }
+
+ step++;
+
+ if ((vco_code == 0) || (vco_code == 0x3FF) || (step > 0x3FF)) {
+ DEV_ERR("%s: VCO tune code search failed\n", __func__);
+ rc = -ENOTSUPP;
+ break;
+ }
+ if ((max_code - min_code) <= 1) {
+ if ((max_code - min_code) == 1) {
+ if (abs((int)(max_cnt - expected_counter_value))
+ < abs((int)(min_cnt - expected_counter_value
+ ))) {
+ vco_code = max_code;
+ } else {
+ vco_code = min_code;
+ }
+ }
+ break;
+ }
+ DEV_DBG("%s: loop[%d], new vco_code = %d\n", __func__, step,
+ vco_code);
+ }
+
+ DEV_DBG("%s: CALIB done. vco_code = %d\n", __func__, vco_code);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE1_MODE0,
+ vco_code & 0xFF);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE2_MODE0,
+ (vco_code >> 8) & 0x3);
+ val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG);
+ val &= ~BIT(1);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val);
+
+ val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1);
+ val |= BIT(4);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val);
+
+ val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1);
+ val &= ~BIT(3);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val);
+
+ return rc;
+}
+
+static int hdmi_8996_v2_perform_sw_calibration(struct clk *c)
+{
+ int rc = 0;
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+ u32 vco_code1, vco_code2, integral_loop, ready_poll;
+ u32 read_count = 0;
+
+ while (read_count < (HDMI_PLL_POLL_MAX_READS << 1)) {
+ ready_poll = MDSS_PLL_REG_R(io->pll_base,
+ QSERDES_COM_C_READY_STATUS);
+ if ((ready_poll & BIT(0)) == 1) {
+ ready_poll = 1;
+ DEV_DBG("%s: C READY\n", __func__);
+ break;
+ }
+ udelay(HDMI_PLL_POLL_TIMEOUT_US);
+ read_count++;
+ }
+
+ if (read_count == (HDMI_PLL_POLL_MAX_READS << 1)) {
+ ready_poll = 0;
+ DEV_DBG("%s: C READY TIMEOUT, TRYING SW CALIBRATION\n",
+ __func__);
+ }
+
+ vco_code1 = MDSS_PLL_REG_R(io->pll_base,
+ QSERDES_COM_PLLCAL_CODE1_STATUS);
+ vco_code2 = MDSS_PLL_REG_R(io->pll_base,
+ QSERDES_COM_PLLCAL_CODE2_STATUS);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DEBUG_BUS_SEL, 0x5);
+ integral_loop = MDSS_PLL_REG_R(io->pll_base,
+ QSERDES_COM_DEBUG_BUS0);
+
+ if (((ready_poll & 0x1) == 0) || (((ready_poll & 1) == 1) &&
+ (vco_code1 == 0xFF) && ((vco_code2 & 0x3) == 0x1) &&
+ (integral_loop > 0xC0))) {
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL1, 0x04);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL2, 0x00);
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x17);
+ udelay(100);
+
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x11);
+ udelay(100);
+
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x19);
+ }
+ return rc;
+}
+
+static int hdmi_8996_perform_sw_calibration(struct clk *c, u32 ver)
+{
+ switch (ver) {
+ case HDMI_VERSION_8996_V1:
+ return hdmi_8996_v1_perform_sw_calibration(c);
+ case HDMI_VERSION_8996_V2:
+ return hdmi_8996_v2_perform_sw_calibration(c);
+ }
+ return 0;
+}
+
+static int hdmi_8996_vco_enable(struct clk *c, u32 ver)
+{
+ int rc = 0;
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x1);
+ udelay(100);
+
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x19);
+ udelay(100);
+
+ rc = hdmi_8996_perform_sw_calibration(c, ver);
+ if (rc) {
+ DEV_ERR("%s: software calibration failed\n", __func__);
+ return rc;
+ }
+
+ rc = hdmi_8996_pll_lock_status(io);
+ if (!rc) {
+ DEV_ERR("%s: PLL not locked\n", __func__);
+ return rc;
+ }
+
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN,
+ 0x6F);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET,
+ QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN,
+ 0x6F);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET,
+ QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN,
+ 0x6F);
+ MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET,
+ QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN,
+ 0x6F);
+
+ /* Disable SSC */
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_PER1, 0x0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_PER2, 0x0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_STEP_SIZE1, 0x0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_STEP_SIZE2, 0x0);
+ MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_EN_CENTER, 0x2);
+
+ rc = hdmi_8996_phy_ready_status(io);
+ if (!rc) {
+ DEV_ERR("%s: PHY not READY\n", __func__);
+ return rc;
+ }
+
+ /* Restart the retiming buffer */
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x18);
+ udelay(1);
+ MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x19);
+
+ io->pll_on = true;
+ return 0;
+}
+
+static int hdmi_8996_v1_vco_enable(struct clk *c)
+{
+ return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V1);
+}
+
+static int hdmi_8996_v2_vco_enable(struct clk *c)
+{
+ return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V2);
+}
+
+static int hdmi_8996_v3_vco_enable(struct clk *c)
+{
+ return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V3);
+}
+
+static int hdmi_8996_v3_1p8_vco_enable(struct clk *c)
+{
+ return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V3_1_8);
+}
+
+static int hdmi_8996_vco_get_lock_range(struct clk *c, unsigned long pixel_clk)
+{
+ u32 rng = 64, cmp_cnt = 1024;
+ u32 coreclk_div = 5, clks_pll_divsel = 2;
+ u32 vco_freq, vco_ratio, ppm_range;
+ u64 bclk;
+ struct hdmi_8996_v3_post_divider pd;
+
+ bclk = ((u64)pixel_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO;
+
+ DEV_DBG("%s: rate=%ld\n", __func__, pixel_clk);
+
+ if (hdmi_8996_v3_get_post_div(&pd, bclk) ||
+ pd.vco_ratio <= 0 || pd.vco_freq <= 0) {
+ DEV_ERR("%s: couldn't get post div\n", __func__);
+ return -EINVAL;
+ }
+
+ do_div(pd.vco_freq, HDMI_KHZ_TO_HZ * HDMI_KHZ_TO_HZ);
+
+ vco_freq = (u32) pd.vco_freq;
+ vco_ratio = (u32) pd.vco_ratio;
+
+ DEV_DBG("%s: freq %d, ratio %d\n", __func__,
+ vco_freq, vco_ratio);
+
+ ppm_range = (rng * HDMI_REF_CLOCK) / cmp_cnt;
+ ppm_range /= vco_freq / vco_ratio;
+ ppm_range *= coreclk_div * clks_pll_divsel;
+
+ DEV_DBG("%s: ppm range: %d\n", __func__, ppm_range);
+
+ return ppm_range;
+}
+
+static int hdmi_8996_vco_rate_atomic_update(struct clk *c,
+ unsigned long rate, u32 ver)
+{
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+ void __iomem *pll;
+ struct hdmi_8996_phy_pll_reg_cfg cfg = {0};
+ int rc = 0;
+
+ rc = hdmi_8996_calculate(rate, &cfg, ver);
+ if (rc) {
+ DEV_ERR("%s: PLL calculation failed\n", __func__);
+ goto end;
+ }
+
+ pll = io->pll_base;
+
+ MDSS_PLL_REG_W(pll, QSERDES_COM_DEC_START_MODE0,
+ cfg.com_dec_start_mode0);
+ MDSS_PLL_REG_W(pll, QSERDES_COM_DIV_FRAC_START1_MODE0,
+ cfg.com_div_frac_start1_mode0);
+ MDSS_PLL_REG_W(pll, QSERDES_COM_DIV_FRAC_START2_MODE0,
+ cfg.com_div_frac_start2_mode0);
+ MDSS_PLL_REG_W(pll, QSERDES_COM_DIV_FRAC_START3_MODE0,
+ cfg.com_div_frac_start3_mode0);
+
+ MDSS_PLL_REG_W(pll, QSERDES_COM_FREQ_UPDATE, 0x01);
+ MDSS_PLL_REG_W(pll, QSERDES_COM_FREQ_UPDATE, 0x00);
+
+ DEV_DBG("%s: updated to rate %ld\n", __func__, rate);
+end:
+ return rc;
+}
+
+static int hdmi_8996_vco_set_rate(struct clk *c, unsigned long rate, u32 ver)
+{
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+ unsigned int set_power_dwn = 0;
+ bool atomic_update = false;
+ int rc, pll_lock_range;
+
+ rc = mdss_pll_resource_enable(io, true);
+ if (rc) {
+ DEV_ERR("pll resource can't be enabled\n");
+ return rc;
+ }
+
+ DEV_DBG("%s: rate %ld\n", __func__, rate);
+
+ if (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_C_READY_STATUS) & BIT(0) &&
+ MDSS_PLL_REG_R(io->phy_base, HDMI_PHY_STATUS) & BIT(0)) {
+ pll_lock_range = hdmi_8996_vco_get_lock_range(c, vco->rate);
+
+ if (pll_lock_range > 0 && vco->rate) {
+ u32 range_limit;
+
+ range_limit = vco->rate *
+ (pll_lock_range / HDMI_KHZ_TO_HZ);
+ range_limit /= HDMI_KHZ_TO_HZ;
+
+ DEV_DBG("%s: range limit %d\n", __func__, range_limit);
+
+ if (abs(rate - vco->rate) < range_limit)
+ atomic_update = true;
+ }
+ }
+
+ if (io->pll_on && !atomic_update)
+ set_power_dwn = 1;
+
+ if (atomic_update) {
+ hdmi_8996_vco_rate_atomic_update(c, rate, ver);
+ } else {
+ rc = hdmi_8996_phy_pll_set_clk_rate(c, rate, ver);
+ if (rc)
+ DEV_ERR("%s: Failed to set clk rate\n", __func__);
+ }
+
+ mdss_pll_resource_enable(io, false);
+
+ if (set_power_dwn)
+ hdmi_8996_vco_enable(c, ver);
+
+ vco->rate = rate;
+ vco->rate_set = true;
+
+ return 0;
+}
+
+static int hdmi_8996_v1_vco_set_rate(struct clk *c, unsigned long rate)
+{
+ return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V1);
+}
+
+static int hdmi_8996_v2_vco_set_rate(struct clk *c, unsigned long rate)
+{
+ return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V2);
+}
+
+static int hdmi_8996_v3_vco_set_rate(struct clk *c, unsigned long rate)
+{
+ return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V3);
+}
+
+static int hdmi_8996_v3_1p8_vco_set_rate(struct clk *c, unsigned long rate)
+{
+ return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V3_1_8);
+}
+
+static unsigned long hdmi_get_hsclk_sel_divisor(unsigned long hsclk_sel)
+{
+ unsigned long divisor;
+
+ switch (hsclk_sel) {
+ case 0:
+ divisor = 2;
+ break;
+ case 1:
+ divisor = 6;
+ break;
+ case 2:
+ divisor = 10;
+ break;
+ case 3:
+ divisor = 14;
+ break;
+ case 4:
+ divisor = 3;
+ break;
+ case 5:
+ divisor = 9;
+ break;
+ case 6:
+ case 13:
+ divisor = 15;
+ break;
+ case 7:
+ divisor = 21;
+ break;
+ case 8:
+ divisor = 4;
+ break;
+ case 9:
+ divisor = 12;
+ break;
+ case 10:
+ divisor = 20;
+ break;
+ case 11:
+ divisor = 28;
+ break;
+ case 12:
+ divisor = 5;
+ break;
+ case 14:
+ divisor = 25;
+ break;
+ case 15:
+ divisor = 35;
+ break;
+ default:
+ divisor = 1;
+ DEV_ERR("%s: invalid hsclk_sel value = %lu",
+ __func__, hsclk_sel);
+ break;
+ }
+
+ return divisor;
+}
+
+static unsigned long hdmi_8996_vco_get_rate(struct clk *c)
+{
+ unsigned long freq = 0, hsclk_sel = 0, tx_band = 0, dec_start = 0,
+ div_frac_start = 0, vco_clock_freq = 0;
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+
+ if (mdss_pll_resource_enable(io, true)) {
+ DEV_ERR("%s: pll resource can't be enabled\n", __func__);
+ return freq;
+ }
+
+ dec_start = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEC_START_MODE0);
+
+ div_frac_start =
+ MDSS_PLL_REG_R(io->pll_base,
+ QSERDES_COM_DIV_FRAC_START1_MODE0) |
+ MDSS_PLL_REG_R(io->pll_base,
+ QSERDES_COM_DIV_FRAC_START2_MODE0) << 8 |
+ MDSS_PLL_REG_R(io->pll_base,
+ QSERDES_COM_DIV_FRAC_START3_MODE0) << 16;
+
+ vco_clock_freq = (dec_start + (div_frac_start / (1 << 20)))
+ * 4 * (HDMI_REF_CLOCK);
+
+ hsclk_sel = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_HSCLK_SEL) & 0x15;
+ hsclk_sel = hdmi_get_hsclk_sel_divisor(hsclk_sel);
+ tx_band = MDSS_PLL_REG_R(io->pll_base + HDMI_TX_L0_BASE_OFFSET,
+ QSERDES_TX_L0_TX_BAND) & 0x3;
+
+ freq = vco_clock_freq / (10 * hsclk_sel * (1 << tx_band));
+
+ mdss_pll_resource_enable(io, false);
+
+ DEV_DBG("%s: freq = %lu\n", __func__, freq);
+
+ return freq;
+}
+
+static long hdmi_8996_vco_round_rate(struct clk *c, unsigned long rate)
+{
+ unsigned long rrate = rate;
+
+ DEV_DBG("rrate=%ld\n", rrate);
+
+ return rrate;
+}
+
+static int hdmi_8996_vco_prepare(struct clk *c, u32 ver)
+{
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+ int ret = 0;
+
+ DEV_DBG("rate=%ld\n", vco->rate);
+
+ if (!vco->rate_set && vco->rate)
+ ret = hdmi_8996_vco_set_rate(c, vco->rate, ver);
+
+ if (!ret) {
+ ret = mdss_pll_resource_enable(io, true);
+ if (ret)
+ DEV_ERR("pll resource can't be enabled\n");
+ }
+
+ return ret;
+}
+
+static int hdmi_8996_v1_vco_prepare(struct clk *c)
+{
+ return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V1);
+}
+
+static int hdmi_8996_v2_vco_prepare(struct clk *c)
+{
+ return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V2);
+}
+
+static int hdmi_8996_v3_vco_prepare(struct clk *c)
+{
+ return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V3);
+}
+
+static int hdmi_8996_v3_1p8_vco_prepare(struct clk *c)
+{
+ return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V3_1_8);
+}
+
+static void hdmi_8996_vco_unprepare(struct clk *c)
+{
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+
+ vco->rate_set = false;
+
+ if (!io) {
+ DEV_ERR("Invalid input parameter\n");
+ return;
+ }
+
+ if (!io->pll_on &&
+ mdss_pll_resource_enable(io, true)) {
+ DEV_ERR("pll resource can't be enabled\n");
+ return;
+ }
+
+ io->handoff_resources = false;
+ mdss_pll_resource_enable(io, false);
+ io->pll_on = false;
+}
+
+static enum handoff hdmi_8996_vco_handoff(struct clk *c)
+{
+ enum handoff ret = HANDOFF_DISABLED_CLK;
+ struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c);
+ struct mdss_pll_resources *io = vco->priv;
+
+ if (is_gdsc_disabled(io))
+ return HANDOFF_DISABLED_CLK;
+
+ if (mdss_pll_resource_enable(io, true)) {
+ DEV_ERR("pll resource can't be enabled\n");
+ return ret;
+ }
+
+ io->handoff_resources = true;
+
+ if (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_C_READY_STATUS) & BIT(0)) {
+ if (MDSS_PLL_REG_R(io->phy_base, HDMI_PHY_STATUS) & BIT(0)) {
+ io->pll_on = true;
+ c->rate = hdmi_8996_vco_get_rate(c);
+ vco->rate = c->rate;
+ ret = HANDOFF_ENABLED_CLK;
+ } else {
+ io->handoff_resources = false;
+ mdss_pll_resource_enable(io, false);
+ DEV_DBG("%s: PHY not ready\n", __func__);
+ }
+ } else {
+ io->handoff_resources = false;
+ mdss_pll_resource_enable(io, false);
+ DEV_DBG("%s: PLL not locked\n", __func__);
+ }
+
+ DEV_DBG("done, ret=%d\n", ret);
+ return ret;
+}
+
+static const struct clk_ops hdmi_8996_v1_vco_clk_ops = {
+ .enable = hdmi_8996_v1_vco_enable,
+ .set_rate = hdmi_8996_v1_vco_set_rate,
+ .get_rate = hdmi_8996_vco_get_rate,
+ .round_rate = hdmi_8996_vco_round_rate,
+ .prepare = hdmi_8996_v1_vco_prepare,
+ .unprepare = hdmi_8996_vco_unprepare,
+ .handoff = hdmi_8996_vco_handoff,
+};
+
+static const struct clk_ops hdmi_8996_v2_vco_clk_ops = {
+ .enable = hdmi_8996_v2_vco_enable,
+ .set_rate = hdmi_8996_v2_vco_set_rate,
+ .get_rate = hdmi_8996_vco_get_rate,
+ .round_rate = hdmi_8996_vco_round_rate,
+ .prepare = hdmi_8996_v2_vco_prepare,
+ .unprepare = hdmi_8996_vco_unprepare,
+ .handoff = hdmi_8996_vco_handoff,
+};
+
+static const struct clk_ops hdmi_8996_v3_vco_clk_ops = {
+ .enable = hdmi_8996_v3_vco_enable,
+ .set_rate = hdmi_8996_v3_vco_set_rate,
+ .get_rate = hdmi_8996_vco_get_rate,
+ .round_rate = hdmi_8996_vco_round_rate,
+ .prepare = hdmi_8996_v3_vco_prepare,
+ .unprepare = hdmi_8996_vco_unprepare,
+ .handoff = hdmi_8996_vco_handoff,
+};
+
+static const struct clk_ops hdmi_8996_v3_1p8_vco_clk_ops = {
+ .enable = hdmi_8996_v3_1p8_vco_enable,
+ .set_rate = hdmi_8996_v3_1p8_vco_set_rate,
+ .get_rate = hdmi_8996_vco_get_rate,
+ .round_rate = hdmi_8996_vco_round_rate,
+ .prepare = hdmi_8996_v3_1p8_vco_prepare,
+ .unprepare = hdmi_8996_vco_unprepare,
+ .handoff = hdmi_8996_vco_handoff,
+};
+
+
+static struct hdmi_pll_vco_clk hdmi_vco_clk = {
+ .c = {
+ .dbg_name = "hdmi_8996_vco_clk",
+ .ops = &hdmi_8996_v1_vco_clk_ops,
+ .flags = CLKFLAG_NO_RATE_CACHE,
+ CLK_INIT(hdmi_vco_clk.c),
+ },
+};
+
+static struct clk_lookup hdmipllcc_8996[] = {
+ CLK_LIST(hdmi_vco_clk),
+};
+
+int hdmi_8996_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res, u32 ver)
+{
+ int rc = -ENOTSUPP;
+
+ if (!pll_res || !pll_res->phy_base || !pll_res->pll_base) {
+ DEV_ERR("%s: Invalid input parameters\n", __func__);
+ return -EPROBE_DEFER;
+ }
+
+ /* Set client data for vco, mux and div clocks */
+ hdmi_vco_clk.priv = pll_res;
+
+ switch (ver) {
+ case HDMI_VERSION_8996_V2:
+ hdmi_vco_clk.c.ops = &hdmi_8996_v2_vco_clk_ops;
+ break;
+ case HDMI_VERSION_8996_V3:
+ hdmi_vco_clk.c.ops = &hdmi_8996_v3_vco_clk_ops;
+ break;
+ case HDMI_VERSION_8996_V3_1_8:
+ hdmi_vco_clk.c.ops = &hdmi_8996_v3_1p8_vco_clk_ops;
+ break;
+ default:
+ hdmi_vco_clk.c.ops = &hdmi_8996_v1_vco_clk_ops;
+ break;
+ };
+
+ rc = of_msm_clock_register(pdev->dev.of_node, hdmipllcc_8996,
+ ARRAY_SIZE(hdmipllcc_8996));
+ if (rc) {
+ DEV_ERR("%s: Clock register failed rc=%d\n", __func__, rc);
+ rc = -EPROBE_DEFER;
+ } else {
+ DEV_DBG("%s SUCCESS\n", __func__);
+ }
+
+ return rc;
+}
+
+int hdmi_8996_v1_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ return hdmi_8996_pll_clock_register(pdev, pll_res,
+ HDMI_VERSION_8996_V1);
+}
+
+int hdmi_8996_v2_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ return hdmi_8996_pll_clock_register(pdev, pll_res,
+ HDMI_VERSION_8996_V2);
+}
+
+int hdmi_8996_v3_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ return hdmi_8996_pll_clock_register(pdev, pll_res,
+ HDMI_VERSION_8996_V3);
+}
+
+int hdmi_8996_v3_1p8_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ return hdmi_8996_pll_clock_register(pdev, pll_res,
+ HDMI_VERSION_8996_V3_1_8);
+}
diff --git a/drivers/clk/msm/mdss/mdss-hdmi-pll.h b/drivers/clk/msm/mdss/mdss-hdmi-pll.h
new file mode 100644
index 0000000..1f21d79
--- /dev/null
+++ b/drivers/clk/msm/mdss/mdss-hdmi-pll.h
@@ -0,0 +1,53 @@
+/* Copyright (c) 2012-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.
+ */
+
+#ifndef __MDSS_HDMI_PLL_H
+#define __MDSS_HDMI_PLL_H
+
+struct hdmi_pll_cfg {
+ unsigned long vco_rate;
+ u32 reg;
+};
+
+struct hdmi_pll_vco_clk {
+ unsigned long rate; /* current vco rate */
+ unsigned long min_rate; /* min vco rate */
+ unsigned long max_rate; /* max vco rate */
+ bool rate_set;
+ struct hdmi_pll_cfg *ip_seti;
+ struct hdmi_pll_cfg *cp_seti;
+ struct hdmi_pll_cfg *ip_setp;
+ struct hdmi_pll_cfg *cp_setp;
+ struct hdmi_pll_cfg *crctrl;
+ void *priv;
+
+ struct clk c;
+};
+
+int hdmi_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+
+int hdmi_20nm_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+
+int hdmi_8996_v1_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+
+int hdmi_8996_v2_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+
+int hdmi_8996_v3_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+
+int hdmi_8996_v3_1p8_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+#endif
diff --git a/drivers/clk/msm/mdss/mdss-pll-util.c b/drivers/clk/msm/mdss/mdss-pll-util.c
new file mode 100644
index 0000000..7f7da9b
--- /dev/null
+++ b/drivers/clk/msm/mdss/mdss-pll-util.c
@@ -0,0 +1,364 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/clk/msm-clock-generic.h>
+#include <linux/vmalloc.h>
+#include <linux/memblock.h>
+
+#include "mdss-pll.h"
+
+int mdss_pll_util_resource_init(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ int rc = 0;
+ struct mdss_module_power *mp = &pll_res->mp;
+
+ rc = msm_mdss_config_vreg(&pdev->dev,
+ mp->vreg_config, mp->num_vreg, 1);
+ if (rc) {
+ pr_err("Vreg config failed rc=%d\n", rc);
+ goto vreg_err;
+ }
+
+ rc = msm_mdss_get_clk(&pdev->dev, mp->clk_config, mp->num_clk);
+ if (rc) {
+ pr_err("Clock get failed rc=%d\n", rc);
+ goto clk_err;
+ }
+
+ return rc;
+
+clk_err:
+ msm_mdss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg, 0);
+vreg_err:
+ return rc;
+}
+
+/**
+ * mdss_pll_get_mp_by_reg_name() -- Find power module by regulator name
+ *@pll_res: Pointer to the PLL resource
+ *@name: Regulator name as specified in the pll dtsi
+ *
+ * This is a helper function to retrieve the regulator information
+ * for each pll resource.
+ */
+struct mdss_vreg *mdss_pll_get_mp_by_reg_name(struct mdss_pll_resources *pll_res
+ , char *name)
+{
+
+ struct mdss_vreg *regulator = NULL;
+ int i;
+
+ if ((pll_res == NULL) || (pll_res->mp.vreg_config == NULL)) {
+ pr_err("%s Invalid PLL resource\n", __func__);
+ goto error;
+ }
+
+ regulator = pll_res->mp.vreg_config;
+
+ for (i = 0; i < pll_res->mp.num_vreg; i++) {
+ if (!strcmp(name, regulator->vreg_name)) {
+ pr_debug("Found regulator match for %s\n", name);
+ break;
+ }
+ regulator++;
+ }
+
+error:
+ return regulator;
+}
+
+void mdss_pll_util_resource_deinit(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ struct mdss_module_power *mp = &pll_res->mp;
+
+ msm_mdss_put_clk(mp->clk_config, mp->num_clk);
+
+ msm_mdss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg, 0);
+}
+
+void mdss_pll_util_resource_release(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ struct mdss_module_power *mp = &pll_res->mp;
+
+ devm_kfree(&pdev->dev, mp->clk_config);
+ devm_kfree(&pdev->dev, mp->vreg_config);
+ mp->num_vreg = 0;
+ mp->num_clk = 0;
+}
+
+int mdss_pll_util_resource_enable(struct mdss_pll_resources *pll_res,
+ bool enable)
+{
+ int rc = 0;
+ struct mdss_module_power *mp = &pll_res->mp;
+
+ if (enable) {
+ rc = msm_mdss_enable_vreg(mp->vreg_config, mp->num_vreg,
+ enable);
+ if (rc) {
+ pr_err("Failed to enable vregs rc=%d\n", rc);
+ goto vreg_err;
+ }
+
+ rc = msm_mdss_clk_set_rate(mp->clk_config, mp->num_clk);
+ if (rc) {
+ pr_err("Failed to set clock rate rc=%d\n", rc);
+ goto clk_err;
+ }
+
+ rc = msm_mdss_enable_clk(mp->clk_config, mp->num_clk, enable);
+ if (rc) {
+ pr_err("clock enable failed rc:%d\n", rc);
+ goto clk_err;
+ }
+ } else {
+ msm_mdss_enable_clk(mp->clk_config, mp->num_clk, enable);
+
+ msm_mdss_enable_vreg(mp->vreg_config, mp->num_vreg, enable);
+ }
+
+ return rc;
+
+clk_err:
+ msm_mdss_enable_vreg(mp->vreg_config, mp->num_vreg, 0);
+vreg_err:
+ return rc;
+}
+
+static int mdss_pll_util_parse_dt_supply(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ int i = 0, rc = 0;
+ u32 tmp = 0;
+ struct device_node *of_node = NULL, *supply_root_node = NULL;
+ struct device_node *supply_node = NULL;
+ struct mdss_module_power *mp = &pll_res->mp;
+
+ of_node = pdev->dev.of_node;
+
+ mp->num_vreg = 0;
+ supply_root_node = of_get_child_by_name(of_node,
+ "qcom,platform-supply-entries");
+ if (!supply_root_node) {
+ pr_err("no supply entry present\n");
+ return rc;
+ }
+
+ for_each_child_of_node(supply_root_node, supply_node) {
+ mp->num_vreg++;
+ }
+
+ if (mp->num_vreg == 0) {
+ pr_debug("no vreg\n");
+ return rc;
+ }
+ pr_debug("vreg found. count=%d\n", mp->num_vreg);
+
+ mp->vreg_config = devm_kzalloc(&pdev->dev, sizeof(struct mdss_vreg) *
+ mp->num_vreg, GFP_KERNEL);
+ if (!mp->vreg_config) {
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ for_each_child_of_node(supply_root_node, supply_node) {
+
+ const char *st = NULL;
+
+ rc = of_property_read_string(supply_node,
+ "qcom,supply-name", &st);
+ if (rc) {
+ pr_err(":error reading name. rc=%d\n", rc);
+ goto error;
+ }
+
+ strlcpy(mp->vreg_config[i].vreg_name, st,
+ sizeof(mp->vreg_config[i].vreg_name));
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-min-voltage", &tmp);
+ if (rc) {
+ pr_err(": error reading min volt. rc=%d\n", rc);
+ goto error;
+ }
+ mp->vreg_config[i].min_voltage = tmp;
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-max-voltage", &tmp);
+ if (rc) {
+ pr_err(": error reading max volt. rc=%d\n", rc);
+ goto error;
+ }
+ mp->vreg_config[i].max_voltage = tmp;
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-enable-load", &tmp);
+ if (rc) {
+ pr_err(": error reading enable load. rc=%d\n", rc);
+ goto error;
+ }
+ mp->vreg_config[i].load[DSS_REG_MODE_ENABLE] = tmp;
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-disable-load", &tmp);
+ if (rc) {
+ pr_err(": error reading disable load. rc=%d\n", rc);
+ goto error;
+ }
+ mp->vreg_config[i].load[DSS_REG_MODE_DISABLE] = tmp;
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-ulp-load", &tmp);
+ if (rc)
+ pr_warn(": error reading ulp load. rc=%d\n", rc);
+
+ mp->vreg_config[i].load[DSS_REG_MODE_ULP] = (!rc ? tmp :
+ mp->vreg_config[i].load[DSS_REG_MODE_ENABLE]);
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-pre-on-sleep", &tmp);
+ if (rc)
+ pr_debug("error reading supply pre sleep value. rc=%d\n",
+ rc);
+
+ mp->vreg_config[i].pre_on_sleep = (!rc ? tmp : 0);
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-pre-off-sleep", &tmp);
+ if (rc)
+ pr_debug("error reading supply pre sleep value. rc=%d\n",
+ rc);
+
+ mp->vreg_config[i].pre_off_sleep = (!rc ? tmp : 0);
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-post-on-sleep", &tmp);
+ if (rc)
+ pr_debug("error reading supply post sleep value. rc=%d\n",
+ rc);
+
+ mp->vreg_config[i].post_on_sleep = (!rc ? tmp : 0);
+
+ rc = of_property_read_u32(supply_node,
+ "qcom,supply-post-off-sleep", &tmp);
+ if (rc)
+ pr_debug("error reading supply post sleep value. rc=%d\n",
+ rc);
+
+ mp->vreg_config[i].post_off_sleep = (!rc ? tmp : 0);
+
+ pr_debug("%s min=%d, max=%d, enable=%d, disable=%d, ulp=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d\n",
+ mp->vreg_config[i].vreg_name,
+ mp->vreg_config[i].min_voltage,
+ mp->vreg_config[i].max_voltage,
+ mp->vreg_config[i].load[DSS_REG_MODE_ENABLE],
+ mp->vreg_config[i].load[DSS_REG_MODE_DISABLE],
+ mp->vreg_config[i].load[DSS_REG_MODE_ULP],
+ mp->vreg_config[i].pre_on_sleep,
+ mp->vreg_config[i].post_on_sleep,
+ mp->vreg_config[i].pre_off_sleep,
+ mp->vreg_config[i].post_off_sleep);
+ ++i;
+
+ rc = 0;
+ }
+
+ return rc;
+
+error:
+ if (mp->vreg_config) {
+ devm_kfree(&pdev->dev, mp->vreg_config);
+ mp->vreg_config = NULL;
+ mp->num_vreg = 0;
+ }
+
+ return rc;
+}
+
+static int mdss_pll_util_parse_dt_clock(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ u32 i = 0, rc = 0;
+ struct mdss_module_power *mp = &pll_res->mp;
+ const char *clock_name;
+ u32 clock_rate;
+
+ mp->num_clk = of_property_count_strings(pdev->dev.of_node,
+ "clock-names");
+ if (mp->num_clk <= 0) {
+ pr_err("clocks are not defined\n");
+ goto clk_err;
+ }
+
+ mp->clk_config = devm_kzalloc(&pdev->dev,
+ sizeof(struct mdss_clk) * mp->num_clk, GFP_KERNEL);
+ if (!mp->clk_config) {
+ rc = -ENOMEM;
+ mp->num_clk = 0;
+ goto clk_err;
+ }
+
+ for (i = 0; i < mp->num_clk; i++) {
+ of_property_read_string_index(pdev->dev.of_node, "clock-names",
+ i, &clock_name);
+ strlcpy(mp->clk_config[i].clk_name, clock_name,
+ sizeof(mp->clk_config[i].clk_name));
+
+ of_property_read_u32_index(pdev->dev.of_node, "clock-rate",
+ i, &clock_rate);
+ mp->clk_config[i].rate = clock_rate;
+
+ if (!clock_rate)
+ mp->clk_config[i].type = DSS_CLK_AHB;
+ else
+ mp->clk_config[i].type = DSS_CLK_PCLK;
+ }
+
+clk_err:
+ return rc;
+}
+
+int mdss_pll_util_resource_parse(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ int rc = 0;
+ struct mdss_module_power *mp = &pll_res->mp;
+
+ rc = mdss_pll_util_parse_dt_supply(pdev, pll_res);
+ if (rc) {
+ pr_err("vreg parsing failed rc=%d\n", rc);
+ goto end;
+ }
+
+ rc = mdss_pll_util_parse_dt_clock(pdev, pll_res);
+ if (rc) {
+ pr_err("clock name parsing failed rc=%d", rc);
+ goto clk_err;
+ }
+
+ return rc;
+
+clk_err:
+ devm_kfree(&pdev->dev, mp->vreg_config);
+ mp->num_vreg = 0;
+end:
+ return rc;
+}
diff --git a/drivers/clk/msm/mdss/mdss-pll.c b/drivers/clk/msm/mdss/mdss-pll.c
new file mode 100644
index 0000000..49f3d7b
--- /dev/null
+++ b/drivers/clk/msm/mdss/mdss-pll.c
@@ -0,0 +1,439 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/clk/msm-clock-generic.h>
+
+#include "mdss-pll.h"
+#include "mdss-dsi-pll.h"
+#include "mdss-hdmi-pll.h"
+
+int mdss_pll_resource_enable(struct mdss_pll_resources *pll_res, bool enable)
+{
+ int rc = 0;
+ int changed = 0;
+
+ if (!pll_res) {
+ pr_err("Invalid input parameters\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Don't turn off resources during handoff or add more than
+ * 1 refcount.
+ */
+ if (pll_res->handoff_resources &&
+ (!enable || (enable & pll_res->resource_enable))) {
+ pr_debug("Do not turn on/off pll resources during handoff case\n");
+ return rc;
+ }
+
+ if (enable) {
+ if (pll_res->resource_ref_cnt == 0)
+ changed++;
+ pll_res->resource_ref_cnt++;
+ } else {
+ if (pll_res->resource_ref_cnt) {
+ pll_res->resource_ref_cnt--;
+ if (pll_res->resource_ref_cnt == 0)
+ changed++;
+ } else {
+ pr_err("PLL Resources already OFF\n");
+ }
+ }
+
+ if (changed) {
+ rc = mdss_pll_util_resource_enable(pll_res, enable);
+ if (rc)
+ pr_err("Resource update failed rc=%d\n", rc);
+ else
+ pll_res->resource_enable = enable;
+ }
+
+ return rc;
+}
+
+static int mdss_pll_resource_init(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ if (!pdev || !pll_res) {
+ pr_err("Invalid input parameters\n");
+ return -EINVAL;
+ }
+
+ return mdss_pll_util_resource_init(pdev, pll_res);
+}
+
+static void mdss_pll_resource_deinit(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ if (!pdev || !pll_res) {
+ pr_err("Invalid input parameters\n");
+ return;
+ }
+
+ mdss_pll_util_resource_deinit(pdev, pll_res);
+}
+
+static void mdss_pll_resource_release(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ if (!pdev || !pll_res) {
+ pr_err("Invalid input parameters\n");
+ return;
+ }
+
+ mdss_pll_util_resource_release(pdev, pll_res);
+}
+
+static int mdss_pll_resource_parse(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ int rc = 0;
+ const char *compatible_stream;
+
+ if (!pdev || !pll_res) {
+ pr_err("Invalid input parameters\n");
+ return -EINVAL;
+ }
+
+ rc = mdss_pll_util_resource_parse(pdev, pll_res);
+ if (rc) {
+ pr_err("Failed to parse the resources rc=%d\n", rc);
+ goto end;
+ }
+
+ compatible_stream = of_get_property(pdev->dev.of_node,
+ "compatible", NULL);
+ if (!compatible_stream) {
+ pr_err("Failed to parse the compatible stream\n");
+ goto err;
+ }
+
+ if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8952")) {
+ pll_res->pll_interface_type = MDSS_DSI_PLL_LPM;
+ pll_res->target_id = MDSS_PLL_TARGET_8952;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8937")) {
+ pll_res->pll_interface_type = MDSS_DSI_PLL_LPM;
+ pll_res->target_id = MDSS_PLL_TARGET_8937;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8909")) {
+ pll_res->pll_interface_type = MDSS_DSI_PLL_LPM;
+ pll_res->target_id = MDSS_PLL_TARGET_8909;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8996")) {
+ pll_res->pll_interface_type = MDSS_DSI_PLL_8996;
+ pll_res->target_id = MDSS_PLL_TARGET_8996;
+ pll_res->revision = 1;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8996_v2")) {
+ pll_res->pll_interface_type = MDSS_DSI_PLL_8996;
+ pll_res->target_id = MDSS_PLL_TARGET_8996;
+ pll_res->revision = 2;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8953")) {
+ pll_res->pll_interface_type = MDSS_DSI_PLL_8996;
+ pll_res->target_id = MDSS_PLL_TARGET_8953;
+ pll_res->revision = 2;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996")) {
+ pll_res->pll_interface_type = MDSS_HDMI_PLL_8996;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996_v2")) {
+ pll_res->pll_interface_type = MDSS_HDMI_PLL_8996_V2;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996_v3")) {
+ pll_res->pll_interface_type = MDSS_HDMI_PLL_8996_V3;
+ } else if (!strcmp(compatible_stream,
+ "qcom,mdss_hdmi_pll_8996_v3_1p8")) {
+ pll_res->pll_interface_type = MDSS_HDMI_PLL_8996_V3_1_8;
+ } else {
+ goto err;
+ }
+
+ return rc;
+
+err:
+ mdss_pll_resource_release(pdev, pll_res);
+end:
+ return rc;
+}
+
+static int mdss_pll_clock_register(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res)
+{
+ int rc;
+
+ if (!pdev || !pll_res) {
+ pr_err("Invalid input parameters\n");
+ return -EINVAL;
+ }
+
+ switch (pll_res->pll_interface_type) {
+ case MDSS_DSI_PLL_LPM:
+ rc = dsi_pll_clock_register_lpm(pdev, pll_res);
+ break;
+ case MDSS_DSI_PLL_8996:
+ rc = dsi_pll_clock_register_8996(pdev, pll_res);
+ break;
+ case MDSS_HDMI_PLL_8996:
+ rc = hdmi_8996_v1_pll_clock_register(pdev, pll_res);
+ break;
+ case MDSS_HDMI_PLL_8996_V2:
+ rc = hdmi_8996_v2_pll_clock_register(pdev, pll_res);
+ break;
+ case MDSS_HDMI_PLL_8996_V3:
+ rc = hdmi_8996_v3_pll_clock_register(pdev, pll_res);
+ break;
+ case MDSS_HDMI_PLL_8996_V3_1_8:
+ rc = hdmi_8996_v3_1p8_pll_clock_register(pdev, pll_res);
+ break;
+ case MDSS_UNKNOWN_PLL:
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ if (rc) {
+ pr_err("Pll ndx=%d clock register failed rc=%d\n",
+ pll_res->index, rc);
+ }
+
+ return rc;
+}
+
+static int mdss_pll_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ const char *label;
+ struct resource *pll_base_reg;
+ struct resource *phy_base_reg;
+ struct resource *dynamic_pll_base_reg;
+ struct resource *gdsc_base_reg;
+ struct mdss_pll_resources *pll_res;
+
+ if (!pdev->dev.of_node) {
+ pr_err("MDSS pll driver only supports device tree probe\n");
+ rc = -ENOTSUPP;
+ goto error;
+ }
+
+ label = of_get_property(pdev->dev.of_node, "label", NULL);
+ if (!label)
+ pr_info("%d: MDSS pll label not specified\n", __LINE__);
+ else
+ pr_info("MDSS pll label = %s\n", label);
+
+ pll_res = devm_kzalloc(&pdev->dev, sizeof(struct mdss_pll_resources),
+ GFP_KERNEL);
+ if (!pll_res) {
+ rc = -ENOMEM;
+ goto error;
+ }
+ platform_set_drvdata(pdev, pll_res);
+
+ rc = of_property_read_u32(pdev->dev.of_node, "cell-index",
+ &pll_res->index);
+ if (rc) {
+ pr_err("Unable to get the cell-index rc=%d\n", rc);
+ pll_res->index = 0;
+ }
+
+ pll_res->ssc_en = of_property_read_bool(pdev->dev.of_node,
+ "qcom,dsi-pll-ssc-en");
+
+ if (pll_res->ssc_en) {
+ pr_info("%s: label=%s PLL SSC enabled\n", __func__, label);
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,ssc-frequency-hz", &pll_res->ssc_freq);
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,ssc-ppm", &pll_res->ssc_ppm);
+
+ pll_res->ssc_center = false;
+
+ label = of_get_property(pdev->dev.of_node,
+ "qcom,dsi-pll-ssc-mode", NULL);
+
+ if (label && !strcmp(label, "center-spread"))
+ pll_res->ssc_center = true;
+ }
+
+ pll_base_reg = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "pll_base");
+ if (!pll_base_reg) {
+ pr_err("Unable to get the pll base resources\n");
+ rc = -ENOMEM;
+ goto io_error;
+ }
+
+ pll_res->pll_base = ioremap(pll_base_reg->start,
+ resource_size(pll_base_reg));
+ if (!pll_res->pll_base) {
+ pr_err("Unable to remap pll base resources\n");
+ rc = -ENOMEM;
+ goto io_error;
+ }
+
+ pr_debug("%s: ndx=%d base=%p\n", __func__,
+ pll_res->index, pll_res->pll_base);
+
+ rc = mdss_pll_resource_parse(pdev, pll_res);
+ if (rc) {
+ pr_err("Pll resource parsing from dt failed rc=%d\n", rc);
+ goto res_parse_error;
+ }
+
+ phy_base_reg = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "phy_base");
+ if (phy_base_reg) {
+ pll_res->phy_base = ioremap(phy_base_reg->start,
+ resource_size(phy_base_reg));
+ if (!pll_res->phy_base) {
+ pr_err("Unable to remap pll phy base resources\n");
+ rc = -ENOMEM;
+ goto phy_io_error;
+ }
+ }
+
+ dynamic_pll_base_reg = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "dynamic_pll_base");
+ if (dynamic_pll_base_reg) {
+ pll_res->dyn_pll_base = ioremap(dynamic_pll_base_reg->start,
+ resource_size(dynamic_pll_base_reg));
+ if (!pll_res->dyn_pll_base) {
+ pr_err("Unable to remap dynamic pll base resources\n");
+ rc = -ENOMEM;
+ goto dyn_pll_io_error;
+ }
+ }
+
+ gdsc_base_reg = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "gdsc_base");
+ if (!gdsc_base_reg) {
+ pr_err("Unable to get the gdsc base resource\n");
+ rc = -ENOMEM;
+ goto gdsc_io_error;
+ }
+ pll_res->gdsc_base = ioremap(gdsc_base_reg->start,
+ resource_size(gdsc_base_reg));
+ if (!pll_res->gdsc_base) {
+ pr_err("Unable to remap gdsc base resources\n");
+ rc = -ENOMEM;
+ goto gdsc_io_error;
+ }
+
+ rc = mdss_pll_resource_init(pdev, pll_res);
+ if (rc) {
+ pr_err("Pll ndx=%d resource init failed rc=%d\n",
+ pll_res->index, rc);
+ goto res_init_error;
+ }
+
+ rc = mdss_pll_clock_register(pdev, pll_res);
+ if (rc) {
+ pr_err("Pll ndx=%d clock register failed rc=%d\n",
+ pll_res->index, rc);
+ goto clock_register_error;
+ }
+
+ return rc;
+
+clock_register_error:
+ mdss_pll_resource_deinit(pdev, pll_res);
+res_init_error:
+ if (pll_res->gdsc_base)
+ iounmap(pll_res->gdsc_base);
+gdsc_io_error:
+ if (pll_res->dyn_pll_base)
+ iounmap(pll_res->dyn_pll_base);
+dyn_pll_io_error:
+ if (pll_res->phy_base)
+ iounmap(pll_res->phy_base);
+phy_io_error:
+ mdss_pll_resource_release(pdev, pll_res);
+res_parse_error:
+ iounmap(pll_res->pll_base);
+io_error:
+ devm_kfree(&pdev->dev, pll_res);
+error:
+ return rc;
+}
+
+static int mdss_pll_remove(struct platform_device *pdev)
+{
+ struct mdss_pll_resources *pll_res;
+
+ pll_res = platform_get_drvdata(pdev);
+ if (!pll_res) {
+ pr_err("Invalid PLL resource data");
+ return 0;
+ }
+
+ mdss_pll_resource_deinit(pdev, pll_res);
+ if (pll_res->phy_base)
+ iounmap(pll_res->phy_base);
+ if (pll_res->gdsc_base)
+ iounmap(pll_res->gdsc_base);
+ mdss_pll_resource_release(pdev, pll_res);
+ iounmap(pll_res->pll_base);
+ devm_kfree(&pdev->dev, pll_res);
+ return 0;
+}
+
+static const struct of_device_id mdss_pll_dt_match[] = {
+ {.compatible = "qcom,mdss_dsi_pll_8996"},
+ {.compatible = "qcom,mdss_dsi_pll_8996_v2"},
+ {.compatible = "qcom,mdss_hdmi_pll_8996"},
+ {.compatible = "qcom,mdss_hdmi_pll_8996_v2"},
+ {.compatible = "qcom,mdss_hdmi_pll_8996_v3"},
+ {.compatible = "qcom,mdss_hdmi_pll_8996_v3_1p8"},
+ {.compatible = "qcom,mdss_dsi_pll_8952"},
+ {.compatible = "qcom,mdss_dsi_pll_8937"},
+ {.compatible = "qcom,mdss_dsi_pll_8909"},
+ {.compatible = "qcom,mdss_dsi_pll_8953"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, mdss_clock_dt_match);
+
+static struct platform_driver mdss_pll_driver = {
+ .probe = mdss_pll_probe,
+ .remove = mdss_pll_remove,
+ .driver = {
+ .name = "mdss_pll",
+ .of_match_table = mdss_pll_dt_match,
+ },
+};
+
+static int __init mdss_pll_driver_init(void)
+{
+ int rc;
+
+ rc = platform_driver_register(&mdss_pll_driver);
+ if (rc)
+ pr_err("mdss_register_pll_driver() failed!\n");
+
+ return rc;
+}
+subsys_initcall(mdss_pll_driver_init);
+
+static void __exit mdss_pll_driver_deinit(void)
+{
+ platform_driver_unregister(&mdss_pll_driver);
+}
+module_exit(mdss_pll_driver_deinit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("mdss pll driver");
diff --git a/drivers/clk/msm/mdss/mdss-pll.h b/drivers/clk/msm/mdss/mdss-pll.h
new file mode 100644
index 0000000..1fa5cff
--- /dev/null
+++ b/drivers/clk/msm/mdss/mdss-pll.h
@@ -0,0 +1,197 @@
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MDSS_PLL_H
+#define __MDSS_PLL_H
+
+#include <linux/mdss_io_util.h>
+#include <linux/io.h>
+
+#define MDSS_PLL_REG_W(base, offset, data) \
+ writel_relaxed((data), (base) + (offset))
+#define MDSS_PLL_REG_R(base, offset) readl_relaxed((base) + (offset))
+
+#define PLL_CALC_DATA(addr0, addr1, data0, data1) \
+ (((data1) << 24) | ((((addr1) / 4) & 0xFF) << 16) | \
+ ((data0) << 8) | (((addr0) / 4) & 0xFF))
+
+#define MDSS_DYN_PLL_REG_W(base, offset, addr0, addr1, data0, data1) \
+ writel_relaxed(PLL_CALC_DATA(addr0, addr1, data0, data1), \
+ (base) + (offset))
+
+enum {
+ MDSS_DSI_PLL_LPM,
+ MDSS_DSI_PLL_8996,
+ MDSS_HDMI_PLL_8996,
+ MDSS_HDMI_PLL_8996_V2,
+ MDSS_HDMI_PLL_8996_V3,
+ MDSS_HDMI_PLL_8996_V3_1_8,
+ MDSS_UNKNOWN_PLL,
+};
+
+enum {
+ MDSS_PLL_TARGET_8996,
+ MDSS_PLL_TARGET_8952,
+ MDSS_PLL_TARGET_8937,
+ MDSS_PLL_TARGET_8953,
+ MDSS_PLL_TARGET_8909,
+};
+
+struct mdss_pll_resources {
+
+ /* Pll specific resources like GPIO, power supply, clocks, etc*/
+ struct mdss_module_power mp;
+
+ /*
+ * dsi/edp/hmdi plls' base register, phy, gdsc and dynamic refresh
+ * register mapping
+ */
+ void __iomem *pll_base;
+ void __iomem *phy_base;
+ void __iomem *gdsc_base;
+ void __iomem *dyn_pll_base;
+
+ bool is_init_locked;
+ s64 vco_current_rate;
+ s64 vco_locking_rate;
+ s64 vco_ref_clk_rate;
+
+ /*
+ * Certain pll's needs to update the same vco rate after resume in
+ * suspend/resume scenario. Cached the vco rate for such plls.
+ */
+ unsigned long vco_cached_rate;
+
+ /* dsi/edp/hmdi pll interface type */
+ u32 pll_interface_type;
+
+ /*
+ * Target ID. Used in pll_register API for valid target check before
+ * registering the PLL clocks.
+ */
+ u32 target_id;
+
+ /* HW recommended delay during configuration of vco clock rate */
+ u32 vco_delay;
+
+ /* Ref-count of the PLL resources */
+ u32 resource_ref_cnt;
+
+ /*
+ * Keep track to resource status to avoid updating same status for the
+ * pll from different paths
+ */
+ bool resource_enable;
+
+ /*
+ * Certain plls' do not allow vco rate update if it is on. Keep track of
+ * status for them to turn on/off after set rate success.
+ */
+ bool pll_on;
+
+ /*
+ * handoff_status is true of pll is already enabled by bootloader with
+ * continuous splash enable case. Clock API will call the handoff API
+ * to enable the status. It is disabled if continuous splash
+ * feature is disabled.
+ */
+ bool handoff_resources;
+
+ /*
+ * caching the pll trim codes in the case of dynamic refresh
+ * or cmd mode idle screen.
+ */
+ int cache_pll_trim_codes[2];
+
+ /*
+ * caching the pll trim codes rate
+ */
+ s64 cache_pll_trim_codes_rate;
+
+ /*
+ * for maintaining the status of saving trim codes
+ */
+ bool reg_upd;
+
+ /*
+ * Notifier callback for MDSS gdsc regulator events
+ */
+ struct notifier_block gdsc_cb;
+
+ /*
+ * Worker function to call PLL off event
+ */
+ struct work_struct pll_off;
+
+ /*
+ * PLL index if multiple index are available. Eg. in case of
+ * DSI we have 2 plls.
+ */
+ uint32_t index;
+
+ bool ssc_en; /* share pll with master */
+ bool ssc_center; /* default is down spread */
+ u32 ssc_freq;
+ u32 ssc_ppm;
+
+ struct mdss_pll_resources *slave;
+
+ /*
+ * target pll revision information
+ */
+ int revision;
+
+ void *priv;
+
+ /*
+ * dynamic refresh pll codes stored in this structure
+ */
+ struct dfps_info *dfps;
+
+};
+
+struct mdss_pll_vco_calc {
+ s32 div_frac_start1;
+ s32 div_frac_start2;
+ s32 div_frac_start3;
+ s64 dec_start1;
+ s64 dec_start2;
+ s64 pll_plllock_cmp1;
+ s64 pll_plllock_cmp2;
+ s64 pll_plllock_cmp3;
+};
+
+static inline bool is_gdsc_disabled(struct mdss_pll_resources *pll_res)
+{
+ if (!pll_res->gdsc_base) {
+ WARN(1, "gdsc_base register is not defined\n");
+ return true;
+ }
+
+ return ((readl_relaxed(pll_res->gdsc_base + 0x4) & BIT(31)) &&
+ (!(readl_relaxed(pll_res->gdsc_base) & BIT(0)))) ? false : true;
+}
+
+int mdss_pll_resource_enable(struct mdss_pll_resources *pll_res, bool enable);
+int mdss_pll_util_resource_init(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+void mdss_pll_util_resource_deinit(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+void mdss_pll_util_resource_release(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+int mdss_pll_util_resource_enable(struct mdss_pll_resources *pll_res,
+ bool enable);
+int mdss_pll_util_resource_parse(struct platform_device *pdev,
+ struct mdss_pll_resources *pll_res);
+struct mdss_vreg *mdss_pll_get_mp_by_reg_name(struct mdss_pll_resources *pll_res
+ , char *name);
+#endif
diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c
index b6204cb..bf4df55 100644
--- a/drivers/clk/qcom/clk-cpu-osm.c
+++ b/drivers/clk/qcom/clk-cpu-osm.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -31,6 +31,7 @@
#include <linux/sched.h>
#include <linux/cpufreq.h>
#include <linux/slab.h>
+#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include <dt-bindings/clock/qcom,cpucc-sdm845.h>
#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
@@ -47,6 +48,7 @@
#define MAX_CLUSTER_CNT 3
#define MAX_MEM_ACC_VAL_PER_LEVEL 3
#define CORE_COUNT_VAL(val) ((val & GENMASK(18, 16)) >> 16)
+#define CURRENT_LVAL(val) ((val & GENMASK(23, 16)) >> 16)
#define OSM_REG_SIZE 32
@@ -54,6 +56,7 @@
#define FREQ_REG 0x110
#define VOLT_REG 0x114
#define CORE_DCVS_CTRL 0xbc
+#define PSTATE_STATUS 0x700
#define DCVS_PERF_STATE_DESIRED_REG_0_V1 0x780
#define DCVS_PERF_STATE_DESIRED_REG_0_V2 0x920
@@ -74,6 +77,7 @@
u16 virtual_corner;
u16 open_loop_volt;
long frequency;
+ u32 lval;
u16 ccount;
};
@@ -205,7 +209,7 @@
struct clk_osm *c = to_clk_osm(hw);
struct clk_hw *p_hw = clk_hw_get_parent(hw);
struct clk_osm *parent = to_clk_osm(p_hw);
- int index = 0;
+ int core_num, index = 0;
if (!c || !parent)
return -EINVAL;
@@ -217,8 +221,9 @@
return -EINVAL;
}
+ core_num = parent->per_core_dcvs ? c->core_num : 0;
clk_osm_write_reg(parent, index,
- DCVS_PERF_STATE_DESIRED_REG(c->core_num,
+ DCVS_PERF_STATE_DESIRED_REG(core_num,
is_sdm845v1));
/* Make sure the write goes through before proceeding */
@@ -227,19 +232,52 @@
return 0;
}
+static int clk_pwrcl_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_hw *p_hw = clk_hw_get_parent(hw);
+ struct clk_osm *parent = to_clk_osm(p_hw);
+ int ret, index = 0, count = 40;
+ u32 curr_lval;
+
+ ret = clk_cpu_set_rate(hw, rate, parent_rate);
+ if (ret)
+ return ret;
+
+ index = clk_osm_search_table(parent->osm_table,
+ parent->num_entries, rate);
+ if (index < 0)
+ return -EINVAL;
+
+ /*
+ * Poll the CURRENT_FREQUENCY value of the PSTATE_STATUS register to
+ * check if the L_VAL has been updated.
+ */
+ while (count-- > 0) {
+ curr_lval = CURRENT_LVAL(clk_osm_read_reg(parent,
+ PSTATE_STATUS));
+ if (curr_lval <= parent->osm_table[index].lval)
+ return 0;
+ udelay(50);
+ }
+ pr_err("cannot set %s to %lu\n", clk_hw_get_name(hw), rate);
+ return -ETIMEDOUT;
+}
+
static unsigned long clk_cpu_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_osm *c = to_clk_osm(hw);
struct clk_hw *p_hw = clk_hw_get_parent(hw);
struct clk_osm *parent = to_clk_osm(p_hw);
- int index = 0;
+ int core_num, index = 0;
if (!c || !parent)
return -EINVAL;
+ core_num = parent->per_core_dcvs ? c->core_num : 0;
index = clk_osm_read_reg(parent,
- DCVS_PERF_STATE_DESIRED_REG(c->core_num,
+ DCVS_PERF_STATE_DESIRED_REG(core_num,
is_sdm845v1));
return parent->osm_table[index].frequency;
}
@@ -262,6 +300,8 @@
struct clk_osm *cpuclk = to_clk_osm(hw);
int index = 0;
unsigned long r_rate;
+ int count = 40;
+ u32 curr_lval;
if (!cpuclk)
return -EINVAL;
@@ -288,6 +328,25 @@
/* Make sure the write goes through before proceeding */
clk_osm_mb(cpuclk);
+ /*
+ * Poll the CURRENT_FREQUENCY value of the PSTATE_STATUS register to
+ * check if the L_VAL has been updated.
+ */
+ if (cpuclk->rate >= cpuclk->mx_turbo_freq &&
+ rate < cpuclk->mx_turbo_freq) {
+ while (count-- > 0) {
+ curr_lval = CURRENT_LVAL(clk_osm_read_reg(cpuclk,
+ PSTATE_STATUS));
+ if (curr_lval <= cpuclk->osm_table[index].lval) {
+ cpuclk->rate = rate;
+ return 0;
+ }
+ udelay(50);
+ }
+ pr_err("cannot set %s to %lu\n", clk_hw_get_name(hw), rate);
+ return -ETIMEDOUT;
+ }
+ cpuclk->rate = rate;
return 0;
}
@@ -318,7 +377,14 @@
.debug_init = clk_debug_measure_add,
};
-static const struct clk_ops clk_ops_core = {
+static const struct clk_ops clk_ops_pwrcl_core = {
+ .set_rate = clk_pwrcl_set_rate,
+ .round_rate = clk_cpu_round_rate,
+ .recalc_rate = clk_cpu_recalc_rate,
+ .debug_init = clk_debug_measure_add,
+};
+
+static const struct clk_ops clk_ops_perfcl_core = {
.set_rate = clk_cpu_set_rate,
.round_rate = clk_cpu_round_rate,
.recalc_rate = clk_cpu_recalc_rate,
@@ -338,6 +404,7 @@
.name = "l3_clk",
.parent_names = (const char *[]){ "bi_tcxo_ao" },
.num_parents = 1,
+ .flags = CLK_CHILD_NO_RATE_PROP,
.ops = &clk_ops_l3_osm,
.vdd_class = &vdd_l3_mx_ao,
},
@@ -381,7 +448,7 @@
.parent_names = (const char *[]){ "pwrcl_clk" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
- .ops = &clk_ops_core,
+ .ops = &clk_ops_pwrcl_core,
},
};
@@ -394,7 +461,7 @@
.parent_names = (const char *[]){ "pwrcl_clk" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
- .ops = &clk_ops_core,
+ .ops = &clk_ops_pwrcl_core,
},
};
@@ -407,7 +474,7 @@
.parent_names = (const char *[]){ "pwrcl_clk" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
- .ops = &clk_ops_core,
+ .ops = &clk_ops_pwrcl_core,
},
};
@@ -420,7 +487,7 @@
.parent_names = (const char *[]){ "pwrcl_clk" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
- .ops = &clk_ops_core,
+ .ops = &clk_ops_pwrcl_core,
},
};
@@ -433,7 +500,7 @@
.parent_names = (const char *[]){ "pwrcl_clk" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
- .ops = &clk_ops_core,
+ .ops = &clk_ops_pwrcl_core,
},
};
@@ -446,7 +513,7 @@
.parent_names = (const char *[]){ "pwrcl_clk" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
- .ops = &clk_ops_core,
+ .ops = &clk_ops_pwrcl_core,
},
};
@@ -466,7 +533,7 @@
.parent_names = (const char *[]){ "perfcl_clk" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
- .ops = &clk_ops_core,
+ .ops = &clk_ops_perfcl_core,
},
};
@@ -479,7 +546,7 @@
.parent_names = (const char *[]){ "perfcl_clk" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
- .ops = &clk_ops_core,
+ .ops = &clk_ops_perfcl_core,
},
};
@@ -492,7 +559,7 @@
.parent_names = (const char *[]){ "perfcl_clk" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
- .ops = &clk_ops_core,
+ .ops = &clk_ops_perfcl_core,
},
};
@@ -505,7 +572,7 @@
.parent_names = (const char *[]){ "perfcl_clk" },
.num_parents = 1,
.flags = CLK_SET_RATE_PARENT,
- .ops = &clk_ops_core,
+ .ops = &clk_ops_perfcl_core,
},
};
@@ -974,6 +1041,7 @@
src = ((data & GENMASK(31, 30)) >> 30);
lval = (data & GENMASK(7, 0));
c->osm_table[i].ccount = CORE_COUNT_VAL(data);
+ c->osm_table[i].lval = lval;
if (!src)
c->osm_table[i].frequency = OSM_INIT_RATE;
diff --git a/drivers/clk/qcom/gcc-sdxpoorwills.c b/drivers/clk/qcom/gcc-sdxpoorwills.c
index 696d7fb..c6e8faa 100644
--- a/drivers/clk/qcom/gcc-sdxpoorwills.c
+++ b/drivers/clk/qcom/gcc-sdxpoorwills.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1316,33 +1316,6 @@
},
};
-static struct clk_branch gcc_mss_cfg_ahb_clk = {
- .halt_reg = 0x40000,
- .halt_check = BRANCH_HALT,
- .hwcg_reg = 0x40000,
- .hwcg_bit = 1,
- .clkr = {
- .enable_reg = 0x40000,
- .enable_mask = BIT(0),
- .hw.init = &(struct clk_init_data){
- .name = "gcc_mss_cfg_ahb_clk",
- .ops = &clk_branch2_ops,
- },
- },
-};
-
-static struct clk_gate2 gcc_mss_gpll0_div_clk_src = {
- .udelay = 500,
- .clkr = {
- .enable_reg = 0x6d004,
- .enable_mask = BIT(17),
- .hw.init = &(struct clk_init_data){
- .name = "gcc_mss_gpll0_div_clk_src",
- .ops = &clk_gate2_ops,
- },
- },
-};
-
static struct clk_branch gcc_pcie_0_clkref_clk = {
.halt_reg = 0x88004,
.halt_check = BRANCH_HALT,
@@ -1794,8 +1767,6 @@
[GCC_GP2_CLK_SRC] = &gcc_gp2_clk_src.clkr,
[GCC_GP3_CLK] = &gcc_gp3_clk.clkr,
[GCC_GP3_CLK_SRC] = &gcc_gp3_clk_src.clkr,
- [GCC_MSS_CFG_AHB_CLK] = &gcc_mss_cfg_ahb_clk.clkr,
- [GCC_MSS_GPLL0_DIV_CLK_SRC] = &gcc_mss_gpll0_div_clk_src.clkr,
[GCC_PCIE_0_CLKREF_CLK] = &gcc_pcie_0_clkref_clk.clkr,
[GCC_PCIE_AUX_CLK] = &gcc_pcie_aux_clk.clkr,
[GCC_PCIE_AUX_PHY_CLK_SRC] = &gcc_pcie_aux_phy_clk_src.clkr,
diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c b/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
index 3f9fcd9..7290205 100644
--- a/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
+++ b/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -20,6 +20,8 @@
#include "mdss-dsi-pll.h"
#include "mdss-pll.h"
#include <dt-bindings/clock/mdss-10nm-pll-clk.h>
+#define CREATE_TRACE_POINTS
+#include "mdss_pll_trace.h"
#define VCO_DELAY_USEC 1
@@ -890,8 +892,13 @@
MDSS_PLL_REG_W(pll->pll_base, PLL_PLL_OUTDIV_RATE,
pll->cached_outdiv);
}
-
+ MDSS_PLL_ATRACE_BEGIN("pll_lock");
+ trace_mdss_pll_lock_start((u64)pll->vco_cached_rate,
+ pll->vco_current_rate,
+ pll->cached_cfg0, pll->cached_cfg1,
+ pll->cached_outdiv, pll->resource_ref_cnt);
rc = dsi_pll_enable(vco);
+ MDSS_PLL_ATRACE_END("pll_lock");
if (rc) {
mdss_pll_resource_enable(pll, false);
pr_err("pll(%d) enable failed, rc=%d\n", pll->index, rc);
diff --git a/drivers/clk/qcom/mdss/mdss_pll_trace.h b/drivers/clk/qcom/mdss/mdss_pll_trace.h
new file mode 100644
index 0000000..cd4fda6
--- /dev/null
+++ b/drivers/clk/qcom/mdss/mdss_pll_trace.h
@@ -0,0 +1,116 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#if !defined(_MDSS_PLL_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _MDSS_PLL_TRACE_H_
+
+#include <linux/stringify.h>
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM mdss_pll
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE mdss_pll_trace
+
+
+TRACE_EVENT(mdss_pll_lock_start,
+ TP_PROTO(
+ u64 vco_cached_rate,
+ s64 vco_current_rate,
+ u32 cached_cfg0,
+ u32 cached_cfg1,
+ u32 cached_outdiv,
+ u32 resource_ref_cnt),
+ TP_ARGS(
+ vco_cached_rate,
+ vco_current_rate,
+ cached_cfg0,
+ cached_cfg1,
+ cached_outdiv,
+ resource_ref_cnt),
+ TP_STRUCT__entry(
+ __field(u64, vco_cached_rate)
+ __field(s64, vco_current_rate)
+ __field(u32, cached_cfg0)
+ __field(u32, cached_cfg1)
+ __field(u32, cached_outdiv)
+ __field(u32, resource_ref_cnt)
+
+ ),
+ TP_fast_assign(
+ __entry->vco_cached_rate = vco_cached_rate;
+ __entry->vco_current_rate = vco_current_rate;
+ __entry->cached_cfg0 = cached_cfg0;
+ __entry->cached_cfg1 = cached_cfg1;
+ __entry->cached_outdiv = cached_outdiv;
+ __entry->resource_ref_cnt = resource_ref_cnt;
+ ),
+ TP_printk(
+ "vco_cached_rate=%llu vco_current_rate=%lld cached_cfg0=%d cached_cfg1=%d cached_outdiv=%d resource_ref_cnt=%d",
+ __entry->vco_cached_rate,
+ __entry->vco_current_rate,
+ __entry->cached_cfg0,
+ __entry->cached_cfg1,
+ __entry->cached_outdiv,
+ __entry->resource_ref_cnt)
+);
+
+TRACE_EVENT(pll_tracing_mark_write,
+ TP_PROTO(int pid, const char *name, bool trace_begin),
+ TP_ARGS(pid, name, trace_begin),
+ TP_STRUCT__entry(
+ __field(int, pid)
+ __string(trace_name, name)
+ __field(bool, trace_begin)
+ ),
+ TP_fast_assign(
+ __entry->pid = pid;
+ __assign_str(trace_name, name);
+ __entry->trace_begin = trace_begin;
+ ),
+ TP_printk("%s|%d|%s", __entry->trace_begin ? "B" : "E",
+ __entry->pid, __get_str(trace_name))
+)
+
+TRACE_EVENT(mdss_pll_trace_counter,
+ TP_PROTO(int pid, char *name, int value),
+ TP_ARGS(pid, name, value),
+ TP_STRUCT__entry(
+ __field(int, pid)
+ __string(counter_name, name)
+ __field(int, value)
+ ),
+ TP_fast_assign(
+ __entry->pid = current->tgid;
+ __assign_str(counter_name, name);
+ __entry->value = value;
+ ),
+ TP_printk("%d|%s|%d", __entry->pid,
+ __get_str(counter_name), __entry->value)
+)
+
+#define MDSS_PLL_ATRACE_END(name) trace_pll_tracing_mark_write(current->tgid,\
+ name, 0)
+#define MDSS_PLL_ATRACE_BEGIN(name) trace_pll_tracing_mark_write(current->tgid,\
+ name, 1)
+#define MDSS_PLL_ATRACE_FUNC() MDSS_PLL_ATRACE_BEGIN(__func__)
+#define MDSS_PLL_ATRACE_INT(name, value) \
+ trace_mdss_pll_trace_counter(current->tgid, name, value)
+
+
+#endif /* _MDSS_PLL_TRACE_H_ */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 84b6efe..cd6d307 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -305,6 +305,14 @@
This must be disabled for hardware validation purposes to detect any
hardware anomalies of missing events.
+config ARM_ARCH_TIMER_VCT_ACCESS
+ bool "Support for ARM architected timer virtual counter access in userspace"
+ default !ARM64
+ depends on ARM_ARCH_TIMER
+ help
+ This option enables support for reading the ARM architected timer's
+ virtual counter in userspace.
+
config FSL_ERRATUM_A008585
bool "Workaround for Freescale/NXP Erratum A-008585"
default y
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 4a4ee0f..5aa9914 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -443,13 +443,12 @@
{
u32 cntkctl = arch_timer_get_cntkctl();
- /* Disable user access to the timers */
+ /* Disable user access to the timers and the physical counter */
/* Also disable virtual event stream */
cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN
| ARCH_TIMER_USR_VT_ACCESS_EN
- | ARCH_TIMER_VIRT_EVT_EN);
-
- cntkctl |= ARCH_TIMER_USR_PCT_ACCESS_EN;
+ | ARCH_TIMER_VIRT_EVT_EN
+ | ARCH_TIMER_USR_PCT_ACCESS_EN);
/* Enable user access to the virtual counter */
if (IS_ENABLED(CONFIG_ARM_ARCH_TIMER_VCT_ACCESS))
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index f18dccf..bb540a5 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -331,6 +331,7 @@
if MIPS
config LOONGSON2_CPUFREQ
tristate "Loongson2 CPUFreq Driver"
+ depends on LEMOTE_MACH2F
help
This option adds a CPUFreq driver for loongson processors which
support software configurable cpu frequency.
@@ -343,6 +344,7 @@
config LOONGSON1_CPUFREQ
tristate "Loongson1 CPUFreq Driver"
+ depends on LOONGSON1_LS1B
help
This option adds a CPUFreq driver for loongson1 processors which
support software configurable cpu frequency.
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 1d5dba9..ff72d8a 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -730,6 +730,9 @@
\
memcpy(&new_policy, policy, sizeof(*policy)); \
\
+ new_policy.min = new_policy.user_policy.min; \
+ new_policy.max = new_policy.user_policy.max; \
+ \
ret = sscanf(buf, "%u", &new_policy.object); \
if (ret != 1) \
return -EINVAL; \
diff --git a/drivers/cpufreq/qcom-cpufreq.c b/drivers/cpufreq/qcom-cpufreq.c
index 90fac32..8a5ad70 100644
--- a/drivers/cpufreq/qcom-cpufreq.c
+++ b/drivers/cpufreq/qcom-cpufreq.c
@@ -27,10 +27,13 @@
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/cpu_cooling.h>
#include <trace/events/power.h>
static DEFINE_MUTEX(l2bw_lock);
+static struct thermal_cooling_device *cdev[NR_CPUS];
static struct clk *cpu_clk[NR_CPUS];
static struct clk *l2_clk;
static DEFINE_PER_CPU(struct cpufreq_frequency_table *, freq_table);
@@ -308,6 +311,52 @@
NULL,
};
+static void msm_cpufreq_ready(struct cpufreq_policy *policy)
+{
+ struct device_node *np, *lmh_node;
+ unsigned int cpu = 0;
+
+ if (cdev[policy->cpu])
+ return;
+
+ np = of_cpu_device_node_get(policy->cpu);
+ if (WARN_ON(!np))
+ return;
+
+ /*
+ * For now, just loading the cooling device;
+ * thermal DT code takes care of matching them.
+ */
+ if (of_find_property(np, "#cooling-cells", NULL)) {
+ lmh_node = of_parse_phandle(np, "qcom,lmh-dcvs", 0);
+ if (lmh_node) {
+ of_node_put(lmh_node);
+ goto ready_exit;
+ }
+
+ for_each_cpu(cpu, policy->related_cpus) {
+ cpumask_t cpu_mask = CPU_MASK_NONE;
+
+ of_node_put(np);
+ np = of_cpu_device_node_get(cpu);
+ if (WARN_ON(!np))
+ return;
+
+ cpumask_set_cpu(cpu, &cpu_mask);
+ cdev[cpu] = of_cpufreq_cooling_register(np, &cpu_mask);
+ if (IS_ERR(cdev[cpu])) {
+ pr_err(
+ "running cpufreq for CPU%d without cooling dev: %ld\n",
+ cpu, PTR_ERR(cdev[cpu]));
+ cdev[cpu] = NULL;
+ }
+ }
+ }
+
+ready_exit:
+ of_node_put(np);
+}
+
static struct cpufreq_driver msm_cpufreq_driver = {
/* lps calculations are handled here. */
.flags = CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
@@ -318,6 +367,7 @@
.get = msm_cpufreq_get_freq,
.name = "msm",
.attr = msm_freq_attr,
+ .ready = msm_cpufreq_ready,
};
static struct cpufreq_frequency_table *cpufreq_parse_dt(struct device *dev,
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index 19fe223..6c66b7f 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -1251,7 +1251,6 @@
state_id |= (level->psci_id & cluster->psci_mode_mask)
<< cluster->psci_mode_shift;
- (*aff_lvl)++;
/*
* We may have updated the broadcast timers, update
@@ -1259,6 +1258,8 @@
*/
if (level->notify_rpm)
system_sleep_update_wakeup();
+ if (level->psci_id)
+ (*aff_lvl)++;
}
unlock_and_return:
spin_unlock(&cluster->sync_lock);
@@ -1731,6 +1732,18 @@
{
int rc;
+#ifdef CONFIG_ARM
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ rc = arm_cpuidle_init(smp_processor_id());
+ if (rc) {
+ pr_err("CPU%d ARM CPUidle init failed (%d)\n", cpu, rc);
+ return rc;
+ }
+ }
+#endif
+
rc = platform_driver_register(&lpm_driver);
if (rc) {
pr_info("Error registering %s\n", lpm_driver.driver.name);
diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c
index c5aac25..b365ad7 100644
--- a/drivers/crypto/n2_core.c
+++ b/drivers/crypto/n2_core.c
@@ -1620,6 +1620,7 @@
CWQ_ENTRY_SIZE, 0, NULL);
if (!queue_cache[HV_NCS_QTYPE_CWQ - 1]) {
kmem_cache_destroy(queue_cache[HV_NCS_QTYPE_MAU - 1]);
+ queue_cache[HV_NCS_QTYPE_MAU - 1] = NULL;
return -ENOMEM;
}
return 0;
@@ -1629,6 +1630,8 @@
{
kmem_cache_destroy(queue_cache[HV_NCS_QTYPE_MAU - 1]);
kmem_cache_destroy(queue_cache[HV_NCS_QTYPE_CWQ - 1]);
+ queue_cache[HV_NCS_QTYPE_MAU - 1] = NULL;
+ queue_cache[HV_NCS_QTYPE_CWQ - 1] = NULL;
}
static int spu_queue_register(struct spu_queue *p, unsigned long q_type)
diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
index 2b6b112..3d50bae 100644
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -249,8 +249,9 @@
}
#ifdef CONFIG_CPU_IDLE
-static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
+static __maybe_unused DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
+#ifdef CONFIG_DT_IDLE_STATES
static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu)
{
int i, ret, count = 0;
@@ -303,6 +304,10 @@
kfree(psci_states);
return ret;
}
+#else
+static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu)
+{ return 0; }
+#endif
#ifdef CONFIG_ACPI
#include <acpi/processor.h>
diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c
index dc37dbe..a83e97e 100644
--- a/drivers/gpio/gpio-ath79.c
+++ b/drivers/gpio/gpio-ath79.c
@@ -323,3 +323,6 @@
};
module_platform_driver(ath79_gpio_driver);
+
+MODULE_DESCRIPTION("Atheros AR71XX/AR724X/AR913X GPIO API support");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-iop.c b/drivers/gpio/gpio-iop.c
index 98c7ff2..8d62db4 100644
--- a/drivers/gpio/gpio-iop.c
+++ b/drivers/gpio/gpio-iop.c
@@ -58,3 +58,7 @@
return platform_driver_register(&iop3xx_gpio_driver);
}
arch_initcall(iop3xx_gpio_init);
+
+MODULE_DESCRIPTION("GPIO handling for Intel IOP3xx processors");
+MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c
index adba614..abb5a27 100644
--- a/drivers/gpio/gpio-stmpe.c
+++ b/drivers/gpio/gpio-stmpe.c
@@ -190,6 +190,16 @@
};
int i, j;
+ /*
+ * STMPE1600: to be able to get IRQ from pins,
+ * a read must be done on GPMR register, or a write in
+ * GPSR or GPCR registers
+ */
+ if (stmpe->partnum == STMPE1600) {
+ stmpe_reg_read(stmpe, stmpe->regs[STMPE_IDX_GPMR_LSB]);
+ stmpe_reg_read(stmpe, stmpe->regs[STMPE_IDX_GPMR_CSB]);
+ }
+
for (i = 0; i < CACHE_NR_REGS; i++) {
/* STMPE801 and STMPE1600 don't have RE and FE registers */
if ((stmpe->partnum == STMPE801 ||
@@ -227,21 +237,11 @@
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(gc);
- struct stmpe *stmpe = stmpe_gpio->stmpe;
int offset = d->hwirq;
int regoffset = offset / 8;
int mask = BIT(offset % 8);
stmpe_gpio->regs[REG_IE][regoffset] |= mask;
-
- /*
- * STMPE1600 workaround: to be able to get IRQ from pins,
- * a read must be done on GPMR register, or a write in
- * GPSR or GPCR registers
- */
- if (stmpe->partnum == STMPE1600)
- stmpe_reg_read(stmpe,
- stmpe->regs[STMPE_IDX_GPMR_LSB + regoffset]);
}
static void stmpe_dbg_show_one(struct seq_file *s,
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 063d176..f3c3680 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -705,6 +705,9 @@
struct gpioevent_data ge;
int ret, level;
+ /* Do not leak kernel stack to userspace */
+ memset(&ge, 0, sizeof(ge));
+
ge.timestamp = ktime_get_real_ns();
level = gpiod_get_value_cansleep(le->desc);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v7.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v7.c
index 1a0a5f7..47951f4 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v7.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v7.c
@@ -367,29 +367,50 @@
{
struct amdgpu_device *adev = get_amdgpu_device(kgd);
struct cik_sdma_rlc_registers *m;
+ unsigned long end_jiffies;
uint32_t sdma_base_addr;
+ uint32_t data;
m = get_sdma_mqd(mqd);
sdma_base_addr = get_sdma_base_addr(m);
- WREG32(sdma_base_addr + mmSDMA0_RLC0_VIRTUAL_ADDR,
- m->sdma_rlc_virtual_addr);
+ WREG32(sdma_base_addr + mmSDMA0_RLC0_RB_CNTL,
+ m->sdma_rlc_rb_cntl & (~SDMA0_RLC0_RB_CNTL__RB_ENABLE_MASK));
- WREG32(sdma_base_addr + mmSDMA0_RLC0_RB_BASE,
- m->sdma_rlc_rb_base);
-
- WREG32(sdma_base_addr + mmSDMA0_RLC0_RB_BASE_HI,
- m->sdma_rlc_rb_base_hi);
-
- WREG32(sdma_base_addr + mmSDMA0_RLC0_RB_RPTR_ADDR_LO,
- m->sdma_rlc_rb_rptr_addr_lo);
-
- WREG32(sdma_base_addr + mmSDMA0_RLC0_RB_RPTR_ADDR_HI,
- m->sdma_rlc_rb_rptr_addr_hi);
+ end_jiffies = msecs_to_jiffies(2000) + jiffies;
+ while (true) {
+ data = RREG32(sdma_base_addr + mmSDMA0_RLC0_CONTEXT_STATUS);
+ if (data & SDMA0_RLC0_CONTEXT_STATUS__IDLE_MASK)
+ break;
+ if (time_after(jiffies, end_jiffies))
+ return -ETIME;
+ usleep_range(500, 1000);
+ }
+ if (m->sdma_engine_id) {
+ data = RREG32(mmSDMA1_GFX_CONTEXT_CNTL);
+ data = REG_SET_FIELD(data, SDMA1_GFX_CONTEXT_CNTL,
+ RESUME_CTX, 0);
+ WREG32(mmSDMA1_GFX_CONTEXT_CNTL, data);
+ } else {
+ data = RREG32(mmSDMA0_GFX_CONTEXT_CNTL);
+ data = REG_SET_FIELD(data, SDMA0_GFX_CONTEXT_CNTL,
+ RESUME_CTX, 0);
+ WREG32(mmSDMA0_GFX_CONTEXT_CNTL, data);
+ }
WREG32(sdma_base_addr + mmSDMA0_RLC0_DOORBELL,
- m->sdma_rlc_doorbell);
-
+ m->sdma_rlc_doorbell);
+ WREG32(sdma_base_addr + mmSDMA0_RLC0_RB_RPTR, 0);
+ WREG32(sdma_base_addr + mmSDMA0_RLC0_RB_WPTR, 0);
+ WREG32(sdma_base_addr + mmSDMA0_RLC0_VIRTUAL_ADDR,
+ m->sdma_rlc_virtual_addr);
+ WREG32(sdma_base_addr + mmSDMA0_RLC0_RB_BASE, m->sdma_rlc_rb_base);
+ WREG32(sdma_base_addr + mmSDMA0_RLC0_RB_BASE_HI,
+ m->sdma_rlc_rb_base_hi);
+ WREG32(sdma_base_addr + mmSDMA0_RLC0_RB_RPTR_ADDR_LO,
+ m->sdma_rlc_rb_rptr_addr_lo);
+ WREG32(sdma_base_addr + mmSDMA0_RLC0_RB_RPTR_ADDR_HI,
+ m->sdma_rlc_rb_rptr_addr_hi);
WREG32(sdma_base_addr + mmSDMA0_RLC0_RB_CNTL,
m->sdma_rlc_rb_cntl);
@@ -493,9 +514,9 @@
}
WREG32(sdma_base_addr + mmSDMA0_RLC0_DOORBELL, 0);
- WREG32(sdma_base_addr + mmSDMA0_RLC0_RB_RPTR, 0);
- WREG32(sdma_base_addr + mmSDMA0_RLC0_RB_WPTR, 0);
- WREG32(sdma_base_addr + mmSDMA0_RLC0_RB_BASE, 0);
+ WREG32(sdma_base_addr + mmSDMA0_RLC0_RB_CNTL,
+ RREG32(sdma_base_addr + mmSDMA0_RLC0_RB_CNTL) |
+ SDMA0_RLC0_RB_CNTL__RB_ENABLE_MASK);
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index f26d1fd..cb505f6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -416,6 +416,10 @@
if (candidate == lobj)
break;
+ /* We can't move pinned BOs here */
+ if (bo->pin_count)
+ continue;
+
other = amdgpu_mem_type_to_domain(bo->tbo.mem.mem_type);
/* Check if this BO is in one of the domains we need space for */
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c
index d83de98..8577a56 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c
@@ -215,8 +215,8 @@
BUG_ON(!mm || !mqd || !q);
m = get_sdma_mqd(mqd);
- m->sdma_rlc_rb_cntl = ffs(q->queue_size / sizeof(unsigned int)) <<
- SDMA0_RLC0_RB_CNTL__RB_SIZE__SHIFT |
+ m->sdma_rlc_rb_cntl = (ffs(q->queue_size / sizeof(unsigned int)) - 1)
+ << SDMA0_RLC0_RB_CNTL__RB_SIZE__SHIFT |
q->vmid << SDMA0_RLC0_RB_CNTL__RB_VMID__SHIFT |
1 << SDMA0_RLC0_RB_CNTL__RPTR_WRITEBACK_ENABLE__SHIFT |
6 << SDMA0_RLC0_RB_CNTL__RPTR_WRITEBACK_TIMER__SHIFT;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
index e1fb40b..5425c68 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
@@ -205,6 +205,24 @@
switch (type) {
case KFD_QUEUE_TYPE_SDMA:
+ if (dev->dqm->queue_count >=
+ CIK_SDMA_QUEUES_PER_ENGINE * CIK_SDMA_ENGINE_NUM) {
+ pr_err("Over-subscription is not allowed for SDMA.\n");
+ retval = -EPERM;
+ goto err_create_queue;
+ }
+
+ retval = create_cp_queue(pqm, dev, &q, properties, f, *qid);
+ if (retval != 0)
+ goto err_create_queue;
+ pqn->q = q;
+ pqn->kq = NULL;
+ retval = dev->dqm->ops.create_queue(dev->dqm, q, &pdd->qpd,
+ &q->properties.vmid);
+ pr_debug("DQM returned %d for create_queue\n", retval);
+ print_queue(q);
+ break;
+
case KFD_QUEUE_TYPE_COMPUTE:
/* check if there is over subscription */
if ((sched_policy == KFD_SCHED_POLICY_HWS_NO_OVERSUBSCRIPTION) &&
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c
index 44d476e..f64f35c 100644
--- a/drivers/gpu/drm/bridge/tc358767.c
+++ b/drivers/gpu/drm/bridge/tc358767.c
@@ -97,7 +97,7 @@
#define DP0_ACTIVEVAL 0x0650
#define DP0_SYNCVAL 0x0654
#define DP0_MISC 0x0658
-#define TU_SIZE_RECOMMENDED (0x3f << 16) /* LSCLK cycles per TU */
+#define TU_SIZE_RECOMMENDED (63) /* LSCLK cycles per TU */
#define BPC_6 (0 << 5)
#define BPC_8 (1 << 5)
@@ -318,7 +318,7 @@
tmp = (tmp << 8) | buf[i];
i++;
if (((i % 4) == 0) || (i == size)) {
- tc_write(DP0_AUXWDATA(i >> 2), tmp);
+ tc_write(DP0_AUXWDATA((i - 1) >> 2), tmp);
tmp = 0;
}
}
@@ -603,8 +603,15 @@
ret = drm_dp_link_probe(&tc->aux, &tc->link.base);
if (ret < 0)
goto err_dpcd_read;
- if ((tc->link.base.rate != 162000) && (tc->link.base.rate != 270000))
- goto err_dpcd_inval;
+ if (tc->link.base.rate != 162000 && tc->link.base.rate != 270000) {
+ dev_dbg(tc->dev, "Falling to 2.7 Gbps rate\n");
+ tc->link.base.rate = 270000;
+ }
+
+ if (tc->link.base.num_lanes > 2) {
+ dev_dbg(tc->dev, "Falling to 2 lanes\n");
+ tc->link.base.num_lanes = 2;
+ }
ret = drm_dp_dpcd_readb(&tc->aux, DP_MAX_DOWNSPREAD, tmp);
if (ret < 0)
@@ -637,9 +644,6 @@
err_dpcd_read:
dev_err(tc->dev, "failed to read DPCD: %d\n", ret);
return ret;
-err_dpcd_inval:
- dev_err(tc->dev, "invalid DPCD\n");
- return -EINVAL;
}
static int tc_set_video_mode(struct tc_data *tc, struct drm_display_mode *mode)
@@ -655,6 +659,14 @@
int lower_margin = mode->vsync_start - mode->vdisplay;
int vsync_len = mode->vsync_end - mode->vsync_start;
+ /*
+ * Recommended maximum number of symbols transferred in a transfer unit:
+ * DIV_ROUND_UP((input active video bandwidth in bytes) * tu_size,
+ * (output active video bandwidth in bytes))
+ * Must be less than tu_size.
+ */
+ max_tu_symbol = TU_SIZE_RECOMMENDED - 1;
+
dev_dbg(tc->dev, "set mode %dx%d\n",
mode->hdisplay, mode->vdisplay);
dev_dbg(tc->dev, "H margin %d,%d sync %d\n",
@@ -664,13 +676,18 @@
dev_dbg(tc->dev, "total: %dx%d\n", mode->htotal, mode->vtotal);
- /* LCD Ctl Frame Size */
- tc_write(VPCTRL0, (0x40 << 20) /* VSDELAY */ |
+ /*
+ * LCD Ctl Frame Size
+ * datasheet is not clear of vsdelay in case of DPI
+ * assume we do not need any delay when DPI is a source of
+ * sync signals
+ */
+ tc_write(VPCTRL0, (0 << 20) /* VSDELAY */ |
OPXLFMT_RGB888 | FRMSYNC_DISABLED | MSF_DISABLED);
- tc_write(HTIM01, (left_margin << 16) | /* H back porch */
- (hsync_len << 0)); /* Hsync */
- tc_write(HTIM02, (right_margin << 16) | /* H front porch */
- (mode->hdisplay << 0)); /* width */
+ tc_write(HTIM01, (ALIGN(left_margin, 2) << 16) | /* H back porch */
+ (ALIGN(hsync_len, 2) << 0)); /* Hsync */
+ tc_write(HTIM02, (ALIGN(right_margin, 2) << 16) | /* H front porch */
+ (ALIGN(mode->hdisplay, 2) << 0)); /* width */
tc_write(VTIM01, (upper_margin << 16) | /* V back porch */
(vsync_len << 0)); /* Vsync */
tc_write(VTIM02, (lower_margin << 16) | /* V front porch */
@@ -689,7 +706,7 @@
/* DP Main Stream Attributes */
vid_sync_dly = hsync_len + left_margin + mode->hdisplay;
tc_write(DP0_VIDSYNCDELAY,
- (0x003e << 16) | /* thresh_dly */
+ (max_tu_symbol << 16) | /* thresh_dly */
(vid_sync_dly << 0));
tc_write(DP0_TOTALVAL, (mode->vtotal << 16) | (mode->htotal));
@@ -705,14 +722,8 @@
tc_write(DPIPXLFMT, VS_POL_ACTIVE_LOW | HS_POL_ACTIVE_LOW |
DE_POL_ACTIVE_HIGH | SUB_CFG_TYPE_CONFIG1 | DPI_BPP_RGB888);
- /*
- * Recommended maximum number of symbols transferred in a transfer unit:
- * DIV_ROUND_UP((input active video bandwidth in bytes) * tu_size,
- * (output active video bandwidth in bytes))
- * Must be less than tu_size.
- */
- max_tu_symbol = TU_SIZE_RECOMMENDED - 1;
- tc_write(DP0_MISC, (max_tu_symbol << 23) | TU_SIZE_RECOMMENDED | BPC_8);
+ tc_write(DP0_MISC, (max_tu_symbol << 23) | (TU_SIZE_RECOMMENDED << 16) |
+ BPC_8);
return 0;
err:
@@ -808,8 +819,6 @@
unsigned int rate;
u32 dp_phy_ctrl;
int timeout;
- bool aligned;
- bool ready;
u32 value;
int ret;
u8 tmp[8];
@@ -954,16 +963,15 @@
ret = drm_dp_dpcd_read_link_status(aux, tmp + 2);
if (ret < 0)
goto err_dpcd_read;
- ready = (tmp[2] == ((DP_CHANNEL_EQ_BITS << 4) | /* Lane1 */
- DP_CHANNEL_EQ_BITS)); /* Lane0 */
- aligned = tmp[4] & DP_INTERLANE_ALIGN_DONE;
- } while ((--timeout) && !(ready && aligned));
+ } while ((--timeout) &&
+ !(drm_dp_channel_eq_ok(tmp + 2, tc->link.base.num_lanes)));
if (timeout == 0) {
/* Read DPCD 0x200-0x201 */
ret = drm_dp_dpcd_read(aux, DP_SINK_COUNT, tmp, 2);
if (ret < 0)
goto err_dpcd_read;
+ dev_err(dev, "channel(s) EQ not ok\n");
dev_info(dev, "0x0200 SINK_COUNT: 0x%02x\n", tmp[0]);
dev_info(dev, "0x0201 DEVICE_SERVICE_IRQ_VECTOR: 0x%02x\n",
tmp[1]);
@@ -974,10 +982,6 @@
dev_info(dev, "0x0206 ADJUST_REQUEST_LANE0_1: 0x%02x\n",
tmp[6]);
- if (!ready)
- dev_err(dev, "Lane0/1 not ready\n");
- if (!aligned)
- dev_err(dev, "Lane0/1 not aligned\n");
return -EAGAIN;
}
@@ -1105,7 +1109,10 @@
static int tc_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
- /* Accept any mode */
+ /* DPI interface clock limitation: upto 154 MHz */
+ if (mode->clock > 154000)
+ return MODE_CLOCK_HIGH;
+
return MODE_OK;
}
diff --git a/drivers/gpu/drm/msm/dp/dp_aux.c b/drivers/gpu/drm/msm/dp/dp_aux.c
index 79f2ec9..630b5fb 100644
--- a/drivers/gpu/drm/msm/dp/dp_aux.c
+++ b/drivers/gpu/drm/msm/dp/dp_aux.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,24 +29,54 @@
struct dp_aux dp_aux;
struct dp_catalog_aux *catalog;
struct dp_aux_cfg *cfg;
-
struct mutex mutex;
struct completion comp;
+ struct drm_dp_aux drm_aux;
- u32 aux_error_num;
- u32 retry_cnt;
bool cmd_busy;
bool native;
bool read;
bool no_send_addr;
bool no_send_stop;
+
u32 offset;
u32 segment;
+ u32 aux_error_num;
+ u32 retry_cnt;
+
atomic_t aborted;
- struct drm_dp_aux drm_aux;
+ u8 *dpcd;
+ u8 *edid;
};
+#ifdef CONFIG_DYNAMIC_DEBUG
+static void dp_aux_hex_dump(struct drm_dp_aux *drm_aux,
+ struct drm_dp_aux_msg *msg)
+{
+ DEFINE_DYNAMIC_DEBUG_METADATA(ddm, "dp aux tracker");
+
+ if (unlikely(ddm.flags & _DPRINTK_FLAGS_PRINT)) {
+ u8 buf[SZ_64];
+ struct dp_aux_private *aux = container_of(drm_aux,
+ struct dp_aux_private, drm_aux);
+
+ snprintf(buf, SZ_64, "[drm-dp] %5s %5s %5xh(%2zu): ",
+ aux->native ? "NATIVE" : "I2C",
+ aux->read ? "READ" : "WRITE",
+ msg->address, msg->size);
+
+ print_hex_dump(KERN_DEBUG, buf, DUMP_PREFIX_NONE,
+ 8, 1, msg->buffer, msg->size, false);
+ }
+}
+#else
+static void dp_aux_hex_dump(struct drm_dp_aux *drm_aux,
+ struct drm_dp_aux_msg *msg)
+{
+}
+#endif
+
static char *dp_aux_get_error(u32 aux_error)
{
switch (aux_error) {
@@ -320,6 +350,7 @@
*
* @aux: DP AUX private structure
* @input_msg: input message from DRM upstream APIs
+ * @send_seg: send the seg to sink
*
* return: void
*
@@ -327,7 +358,7 @@
* sinks that do not handle the i2c middle-of-transaction flag correctly.
*/
static void dp_aux_transfer_helper(struct dp_aux_private *aux,
- struct drm_dp_aux_msg *input_msg)
+ struct drm_dp_aux_msg *input_msg, bool send_seg)
{
struct drm_dp_aux_msg helper_msg;
u32 const message_size = 0x10;
@@ -346,7 +377,7 @@
* duplicate AUX transactions related to this while reading the
* first 16 bytes of each block.
*/
- if (!(aux->offset % edid_block_length))
+ if (!(aux->offset % edid_block_length) || !send_seg)
goto end;
aux->read = false;
@@ -388,6 +419,110 @@
aux->segment = 0x0; /* reset segment at end of block */
}
+static int dp_aux_transfer_ready(struct dp_aux_private *aux,
+ struct drm_dp_aux_msg *msg, bool send_seg)
+{
+ int ret = 0;
+ int const aux_cmd_native_max = 16;
+ int const aux_cmd_i2c_max = 128;
+
+ if (atomic_read(&aux->aborted)) {
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ aux->native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
+
+ /* Ignore address only message */
+ if ((msg->size == 0) || (msg->buffer == NULL)) {
+ msg->reply = aux->native ?
+ DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
+ goto error;
+ }
+
+ /* msg sanity check */
+ if ((aux->native && (msg->size > aux_cmd_native_max)) ||
+ (msg->size > aux_cmd_i2c_max)) {
+ pr_err("%s: invalid msg: size(%zu), request(%x)\n",
+ __func__, msg->size, msg->request);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ dp_aux_update_offset_and_segment(aux, msg);
+
+ dp_aux_transfer_helper(aux, msg, send_seg);
+
+ aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
+
+ if (aux->read) {
+ aux->no_send_addr = true;
+ aux->no_send_stop = false;
+ } else {
+ aux->no_send_addr = true;
+ aux->no_send_stop = true;
+ }
+
+ aux->cmd_busy = true;
+error:
+ return ret;
+}
+
+static ssize_t dp_aux_transfer_debug(struct drm_dp_aux *drm_aux,
+ struct drm_dp_aux_msg *msg)
+{
+ u32 timeout;
+ ssize_t ret;
+ struct dp_aux_private *aux = container_of(drm_aux,
+ struct dp_aux_private, drm_aux);
+
+ ret = dp_aux_transfer_ready(aux, msg, false);
+ if (ret)
+ goto end;
+
+ aux->aux_error_num = DP_AUX_ERR_NONE;
+
+ if (aux->native) {
+ if (aux->read && ((msg->address + msg->size) < SZ_1K)) {
+ aux->dp_aux.reg = msg->address;
+
+ reinit_completion(&aux->comp);
+ timeout = wait_for_completion_timeout(&aux->comp, HZ);
+ if (!timeout)
+ pr_err("aux timeout for 0x%x\n", msg->address);
+
+ aux->dp_aux.reg = 0xFFFF;
+
+ memcpy(msg->buffer, aux->dpcd + msg->address,
+ msg->size);
+ aux->aux_error_num = DP_AUX_ERR_NONE;
+ } else {
+ memset(msg->buffer, 0, msg->size);
+ }
+ } else {
+ if (aux->read && msg->address == 0x50) {
+ memcpy(msg->buffer,
+ aux->edid + aux->offset - 16,
+ msg->size);
+ }
+ }
+
+ if (aux->aux_error_num == DP_AUX_ERR_NONE) {
+ dp_aux_hex_dump(drm_aux, msg);
+
+ msg->reply = aux->native ?
+ DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
+ } else {
+ /* Reply defer to retry */
+ msg->reply = aux->native ?
+ DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;
+ }
+
+ ret = msg->size;
+end:
+ return ret;
+}
+
/*
* This function does the real job to process an AUX transaction.
* It will call aux_reset() function to reset the AUX channel,
@@ -397,52 +532,21 @@
struct drm_dp_aux_msg *msg)
{
ssize_t ret;
- int const aux_cmd_native_max = 16;
- int const aux_cmd_i2c_max = 128;
int const retry_count = 5;
struct dp_aux_private *aux = container_of(drm_aux,
struct dp_aux_private, drm_aux);
mutex_lock(&aux->mutex);
- if (atomic_read(&aux->aborted)) {
- ret = -ETIMEDOUT;
+ ret = dp_aux_transfer_ready(aux, msg, true);
+ if (ret)
goto unlock_exit;
- }
- aux->native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
-
- /* Ignore address only message */
- if ((msg->size == 0) || (msg->buffer == NULL)) {
- msg->reply = aux->native ?
- DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
+ if (!aux->cmd_busy) {
ret = msg->size;
goto unlock_exit;
}
- /* msg sanity check */
- if ((aux->native && (msg->size > aux_cmd_native_max)) ||
- (msg->size > aux_cmd_i2c_max)) {
- pr_err("%s: invalid msg: size(%zu), request(%x)\n",
- __func__, msg->size, msg->request);
- ret = -EINVAL;
- goto unlock_exit;
- }
-
- dp_aux_update_offset_and_segment(aux, msg);
- dp_aux_transfer_helper(aux, msg);
-
- aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
- aux->cmd_busy = true;
-
- if (aux->read) {
- aux->no_send_addr = true;
- aux->no_send_stop = false;
- } else {
- aux->no_send_addr = true;
- aux->no_send_stop = true;
- }
-
ret = dp_aux_cmd_fifo_tx(aux, msg);
if ((ret < 0) && aux->native && !atomic_read(&aux->aborted)) {
aux->retry_cnt++;
@@ -459,6 +563,8 @@
if (aux->read)
dp_aux_cmd_fifo_rx(aux, msg);
+ dp_aux_hex_dump(drm_aux, msg);
+
msg->reply = aux->native ?
DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
} else {
@@ -558,6 +664,41 @@
drm_dp_aux_unregister(&aux->drm_aux);
}
+static void dp_aux_dpcd_updated(struct dp_aux *dp_aux)
+{
+ struct dp_aux_private *aux;
+
+ if (!dp_aux) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+ complete(&aux->comp);
+}
+
+static void dp_aux_set_sim_mode(struct dp_aux *dp_aux, bool en,
+ u8 *edid, u8 *dpcd)
+{
+ struct dp_aux_private *aux;
+
+ if (!dp_aux) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+ aux->edid = edid;
+ aux->dpcd = dpcd;
+
+ if (en)
+ aux->drm_aux.transfer = dp_aux_transfer_debug;
+ else
+ aux->drm_aux.transfer = dp_aux_transfer;
+}
+
struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
struct dp_aux_cfg *aux_cfg)
{
@@ -586,6 +727,7 @@
aux->cfg = aux_cfg;
dp_aux = &aux->dp_aux;
aux->retry_cnt = 0;
+ aux->dp_aux.reg = 0xFFFF;
dp_aux->isr = dp_aux_isr;
dp_aux->init = dp_aux_init;
@@ -594,6 +736,8 @@
dp_aux->drm_aux_deregister = dp_aux_deregister;
dp_aux->reconfig = dp_aux_reconfig;
dp_aux->abort = dp_aux_abort_transaction;
+ dp_aux->dpcd_updated = dp_aux_dpcd_updated;
+ dp_aux->set_sim_mode = dp_aux_set_sim_mode;
return dp_aux;
error:
diff --git a/drivers/gpu/drm/msm/dp/dp_aux.h b/drivers/gpu/drm/msm/dp/dp_aux.h
index e8cb1cc..bf52d57 100644
--- a/drivers/gpu/drm/msm/dp/dp_aux.h
+++ b/drivers/gpu/drm/msm/dp/dp_aux.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,6 +18,19 @@
#include "dp_catalog.h"
#include "drm_dp_helper.h"
+#define DP_STATE_NOTIFICATION_SENT BIT(0)
+#define DP_STATE_TRAIN_1_STARTED BIT(1)
+#define DP_STATE_TRAIN_1_SUCCEEDED BIT(2)
+#define DP_STATE_TRAIN_1_FAILED BIT(3)
+#define DP_STATE_TRAIN_2_STARTED BIT(4)
+#define DP_STATE_TRAIN_2_SUCCEEDED BIT(5)
+#define DP_STATE_TRAIN_2_FAILED BIT(6)
+#define DP_STATE_CTRL_POWERED_ON BIT(7)
+#define DP_STATE_CTRL_POWERED_OFF BIT(8)
+#define DP_STATE_LINK_MAINTENANCE_STARTED BIT(9)
+#define DP_STATE_LINK_MAINTENANCE_COMPLETED BIT(10)
+#define DP_STATE_LINK_MAINTENANCE_FAILED BIT(11)
+
enum dp_aux_error {
DP_AUX_ERR_NONE = 0,
DP_AUX_ERR_ADDR = -1,
@@ -29,6 +42,9 @@
};
struct dp_aux {
+ u32 reg;
+ u32 state;
+
struct drm_dp_aux *drm_aux;
int (*drm_aux_register)(struct dp_aux *aux);
void (*drm_aux_deregister)(struct dp_aux *aux);
@@ -37,6 +53,8 @@
void (*deinit)(struct dp_aux *aux);
void (*reconfig)(struct dp_aux *aux);
void (*abort)(struct dp_aux *aux);
+ void (*dpcd_updated)(struct dp_aux *aux);
+ void (*set_sim_mode)(struct dp_aux *aux, bool en, u8 *edid, u8 *dpcd);
};
struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index cfb4436..56a41b5 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -23,15 +23,12 @@
#define DP_GET_MSB(x) (x >> 8)
#define DP_GET_LSB(x) (x & 0xff)
-#define dp_read(offset) readl_relaxed((offset))
-#define dp_write(offset, data) writel_relaxed((data), (offset))
-
-#define dp_catalog_get_priv(x) { \
+#define dp_catalog_get_priv(x) ({ \
struct dp_catalog *dp_catalog; \
dp_catalog = container_of(x, struct dp_catalog, x); \
- catalog = container_of(dp_catalog, struct dp_catalog_private, \
+ container_of(dp_catalog, struct dp_catalog_private, \
dp_catalog); \
-}
+})
#define DP_INTERRUPT_STATUS1 \
(DP_INTR_AUX_I2C_DONE| \
@@ -48,6 +45,14 @@
#define DP_INTR_MASK2 (DP_INTERRUPT_STATUS2 << 2)
+#define dp_catalog_fill_io(x) { \
+ catalog->io.x = parser->get_io(parser, #x); \
+}
+
+#define dp_catalog_fill_io_buf(x) { \
+ parser->get_io_buf(parser, #x); \
+}
+
static u8 const vm_pre_emphasis[4][4] = {
{0x00, 0x0B, 0x12, 0xFF}, /* pe0, 0 db */
{0x00, 0x0A, 0x12, 0xFF}, /* pe1, 3.5 db */
@@ -63,30 +68,77 @@
{0xFF, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */
};
+struct dp_catalog_io {
+ struct dp_io_data *dp_ahb;
+ struct dp_io_data *dp_aux;
+ struct dp_io_data *dp_link;
+ struct dp_io_data *dp_p0;
+ struct dp_io_data *dp_phy;
+ struct dp_io_data *dp_ln_tx0;
+ struct dp_io_data *dp_ln_tx1;
+ struct dp_io_data *dp_mmss_cc;
+ struct dp_io_data *dp_pll;
+ struct dp_io_data *usb3_dp_com;
+ struct dp_io_data *hdcp_physical;
+};
+
/* audio related catalog functions */
struct dp_catalog_private {
struct device *dev;
- struct dp_io *io;
+ struct dp_catalog_io io;
+ struct dp_parser *parser;
u32 (*audio_map)[DP_AUDIO_SDP_HEADER_MAX];
struct dp_catalog dp_catalog;
+
+ char exe_mode[SZ_4];
};
+static u32 dp_read(struct dp_catalog_private *catalog,
+ struct dp_io_data *io_data, u32 offset)
+{
+ u32 data = 0;
+
+ if (!strcmp(catalog->exe_mode, "hw") ||
+ !strcmp(catalog->exe_mode, "all")) {
+ data = readl_relaxed(io_data->io.base + offset);
+ } else if (!strcmp(catalog->exe_mode, "sw")) {
+ if (io_data->buf)
+ memcpy(&data, io_data->buf + offset, sizeof(offset));
+ }
+
+ return data;
+}
+
+static void dp_write(struct dp_catalog_private *catalog,
+ struct dp_io_data *io_data, u32 offset, u32 data)
+{
+ if (!strcmp(catalog->exe_mode, "hw") ||
+ !strcmp(catalog->exe_mode, "all"))
+ writel_relaxed(data, io_data->io.base + offset);
+
+ if (!strcmp(catalog->exe_mode, "sw") ||
+ !strcmp(catalog->exe_mode, "all")) {
+ if (io_data->buf)
+ memcpy(io_data->buf + offset, &data, sizeof(data));
+ }
+}
+
/* aux related catalog functions */
static u32 dp_catalog_aux_read_data(struct dp_catalog_aux *aux)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!aux) {
pr_err("invalid input\n");
goto end;
}
- dp_catalog_get_priv(aux);
- base = catalog->io->dp_aux.base;
+ catalog = dp_catalog_get_priv(aux);
+ io_data = catalog->io.dp_aux;
- return dp_read(base + DP_AUX_DATA);
+ return dp_read(catalog, io_data, DP_AUX_DATA);
end:
return 0;
}
@@ -95,7 +147,7 @@
{
int rc = 0;
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!aux) {
pr_err("invalid input\n");
@@ -103,10 +155,10 @@
goto end;
}
- dp_catalog_get_priv(aux);
- base = catalog->io->dp_aux.base;
+ catalog = dp_catalog_get_priv(aux);
+ io_data = catalog->io.dp_aux;
- dp_write(base + DP_AUX_DATA, aux->data);
+ dp_write(catalog, io_data, DP_AUX_DATA, aux->data);
end:
return rc;
}
@@ -115,7 +167,7 @@
{
int rc = 0;
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!aux) {
pr_err("invalid input\n");
@@ -123,10 +175,10 @@
goto end;
}
- dp_catalog_get_priv(aux);
- base = catalog->io->dp_aux.base;
+ catalog = dp_catalog_get_priv(aux);
+ io_data = catalog->io.dp_aux;
- dp_write(base + DP_AUX_TRANS_CTRL, aux->data);
+ dp_write(catalog, io_data, DP_AUX_TRANS_CTRL, aux->data);
end:
return rc;
}
@@ -136,7 +188,7 @@
int rc = 0;
u32 data = 0;
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!aux) {
pr_err("invalid input\n");
@@ -144,15 +196,15 @@
goto end;
}
- dp_catalog_get_priv(aux);
- base = catalog->io->dp_aux.base;
+ catalog = dp_catalog_get_priv(aux);
+ io_data = catalog->io.dp_aux;
if (read) {
- data = dp_read(base + DP_AUX_TRANS_CTRL);
+ data = dp_read(catalog, io_data, DP_AUX_TRANS_CTRL);
data &= ~BIT(9);
- dp_write(base + DP_AUX_TRANS_CTRL, data);
+ dp_write(catalog, io_data, DP_AUX_TRANS_CTRL, data);
} else {
- dp_write(base + DP_AUX_TRANS_CTRL, 0);
+ dp_write(catalog, io_data, DP_AUX_TRANS_CTRL, 0);
}
end:
return rc;
@@ -161,7 +213,7 @@
static void dp_catalog_aux_clear_hw_interrupts(struct dp_catalog_aux *aux)
{
struct dp_catalog_private *catalog;
- void __iomem *phy_base;
+ struct dp_io_data *io_data;
u32 data = 0;
if (!aux) {
@@ -169,17 +221,16 @@
return;
}
- dp_catalog_get_priv(aux);
- phy_base = catalog->io->phy_io.base;
+ catalog = dp_catalog_get_priv(aux);
+ io_data = catalog->io.dp_phy;
- data = dp_read(phy_base + DP_PHY_AUX_INTERRUPT_STATUS);
- pr_debug("PHY_AUX_INTERRUPT_STATUS=0x%08x\n", data);
+ data = dp_read(catalog, io_data, DP_PHY_AUX_INTERRUPT_STATUS);
- dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0x1f);
+ dp_write(catalog, io_data, DP_PHY_AUX_INTERRUPT_CLEAR, 0x1f);
wmb(); /* make sure 0x1f is written before next write */
- dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0x9f);
+ dp_write(catalog, io_data, DP_PHY_AUX_INTERRUPT_CLEAR, 0x9f);
wmb(); /* make sure 0x9f is written before next write */
- dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0);
+ dp_write(catalog, io_data, DP_PHY_AUX_INTERRUPT_CLEAR, 0);
wmb(); /* make sure register is cleared */
}
@@ -187,24 +238,25 @@
{
u32 aux_ctrl;
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!aux) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(aux);
- base = catalog->io->dp_aux.base;
+ catalog = dp_catalog_get_priv(aux);
+ io_data = catalog->io.dp_aux;
- aux_ctrl = dp_read(base + DP_AUX_CTRL);
+ aux_ctrl = dp_read(catalog, io_data, DP_AUX_CTRL);
aux_ctrl |= BIT(1);
- dp_write(base + DP_AUX_CTRL, aux_ctrl);
+ dp_write(catalog, io_data, DP_AUX_CTRL, aux_ctrl);
usleep_range(1000, 1010); /* h/w recommended delay */
aux_ctrl &= ~BIT(1);
- dp_write(base + DP_AUX_CTRL, aux_ctrl);
+
+ dp_write(catalog, io_data, DP_AUX_CTRL, aux_ctrl);
wmb(); /* make sure AUX reset is done here */
}
@@ -212,27 +264,27 @@
{
u32 aux_ctrl;
struct dp_catalog_private *catalog;
- void __iomem *base;
-
+ struct dp_io_data *io_data;
if (!aux) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(aux);
- base = catalog->io->dp_aux.base;
+ catalog = dp_catalog_get_priv(aux);
+ io_data = catalog->io.dp_aux;
- aux_ctrl = dp_read(base + DP_AUX_CTRL);
+ aux_ctrl = dp_read(catalog, io_data, DP_AUX_CTRL);
if (enable) {
aux_ctrl |= BIT(0);
- dp_write(base + DP_AUX_CTRL, aux_ctrl);
+ dp_write(catalog, io_data, DP_AUX_CTRL, aux_ctrl);
wmb(); /* make sure AUX module is enabled */
- dp_write(base + DP_TIMEOUT_COUNT, 0xffff);
- dp_write(base + DP_AUX_LIMITS, 0xffff);
+
+ dp_write(catalog, io_data, DP_TIMEOUT_COUNT, 0xffff);
+ dp_write(catalog, io_data, DP_AUX_LIMITS, 0xffff);
} else {
aux_ctrl &= ~BIT(0);
- dp_write(base + DP_AUX_CTRL, aux_ctrl);
+ dp_write(catalog, io_data, DP_AUX_CTRL, aux_ctrl);
}
}
@@ -241,13 +293,16 @@
{
struct dp_catalog_private *catalog;
u32 new_index = 0, current_index = 0;
+ struct dp_io_data *io_data;
if (!aux || !cfg || (type >= PHY_AUX_CFG_MAX)) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(aux);
+ catalog = dp_catalog_get_priv(aux);
+
+ io_data = catalog->io.dp_phy;
current_index = cfg[type].current_index;
new_index = (current_index + 1) % cfg[type].cfg_cnt;
@@ -255,8 +310,7 @@
dp_phy_aux_config_type_to_string(type),
cfg[type].lut[current_index], cfg[type].lut[new_index]);
- dp_write(catalog->io->phy_io.base + cfg[type].offset,
- cfg[type].lut[new_index]);
+ dp_write(catalog, io_data, cfg[type].offset, cfg[type].lut[new_index]);
cfg[type].current_index = new_index;
}
@@ -264,6 +318,7 @@
struct dp_aux_cfg *cfg)
{
struct dp_catalog_private *catalog;
+ struct dp_io_data *io_data;
int i = 0;
if (!aux || !cfg) {
@@ -271,25 +326,32 @@
return;
}
- dp_catalog_get_priv(aux);
+ catalog = dp_catalog_get_priv(aux);
- dp_write(catalog->io->phy_io.base + DP_PHY_PD_CTL, 0x65);
+ io_data = catalog->io.dp_phy;
+ dp_write(catalog, io_data, DP_PHY_PD_CTL, 0x65);
wmb(); /* make sure PD programming happened */
/* Turn on BIAS current for PHY/PLL */
- dp_write(catalog->io->dp_pll_io.base +
- QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1b);
+ io_data = catalog->io.dp_pll;
+ dp_write(catalog, io_data, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1b);
+
+ io_data = catalog->io.dp_phy;
+ dp_write(catalog, io_data, DP_PHY_PD_CTL, 0x02);
+ wmb(); /* make sure PD programming happened */
+ dp_write(catalog, io_data, DP_PHY_PD_CTL, 0x7d);
+
+ /* Turn on BIAS current for PHY/PLL */
+ io_data = catalog->io.dp_pll;
+ dp_write(catalog, io_data, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3f);
/* DP AUX CFG register programming */
- for (i = 0; i < PHY_AUX_CFG_MAX; i++) {
- pr_debug("%s: offset=0x%08x, value=0x%08x\n",
- dp_phy_aux_config_type_to_string(i),
- cfg[i].offset, cfg[i].lut[cfg[i].current_index]);
- dp_write(catalog->io->phy_io.base + cfg[i].offset,
- cfg[i].lut[cfg[i].current_index]);
- }
+ io_data = catalog->io.dp_phy;
+ for (i = 0; i < PHY_AUX_CFG_MAX; i++)
+ dp_write(catalog, io_data, cfg[i].offset,
+ cfg[i].lut[cfg[i].current_index]);
- dp_write(catalog->io->phy_io.base + DP_PHY_AUX_INTERRUPT_MASK, 0x1F);
+ dp_write(catalog, io_data, DP_PHY_AUX_INTERRUPT_MASK, 0x1F);
wmb(); /* make sure AUX configuration is done before enabling it */
}
@@ -297,46 +359,46 @@
{
u32 ack;
struct dp_catalog_private *catalog;
- void __iomem *ahb_base;
+ struct dp_io_data *io_data;
if (!aux) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(aux);
- ahb_base = catalog->io->dp_ahb.base;
+ catalog = dp_catalog_get_priv(aux);
+ io_data = catalog->io.dp_ahb;
- aux->isr = dp_read(ahb_base + DP_INTR_STATUS);
+ aux->isr = dp_read(catalog, io_data, DP_INTR_STATUS);
aux->isr &= ~DP_INTR_MASK1;
ack = aux->isr & DP_INTERRUPT_STATUS1;
ack <<= 1;
ack |= DP_INTR_MASK1;
- dp_write(ahb_base + DP_INTR_STATUS, ack);
+ dp_write(catalog, io_data, DP_INTR_STATUS, ack);
}
/* controller related catalog functions */
static u32 dp_catalog_ctrl_read_hdcp_status(struct dp_catalog_ctrl *ctrl)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!ctrl) {
pr_err("invalid input\n");
return -EINVAL;
}
- dp_catalog_get_priv(ctrl);
- base = catalog->io->dp_ahb.base;
+ catalog = dp_catalog_get_priv(ctrl);
+ io_data = catalog->io.dp_ahb;
- return dp_read(base + DP_HDCP_STATUS);
+ return dp_read(catalog, io_data, DP_HDCP_STATUS);
}
static void dp_catalog_panel_setup_infoframe_sdp(struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
struct drm_msm_ext_hdr_metadata *hdr;
- void __iomem *base;
+ struct dp_io_data *io_data;
u32 header, parity, data;
u8 buf[SZ_128], off = 0;
@@ -345,16 +407,16 @@
return;
}
- dp_catalog_get_priv(panel);
+ catalog = dp_catalog_get_priv(panel);
hdr = &panel->hdr_data.hdr_meta;
- base = catalog->io->dp_link.base;
+ io_data = catalog->io.dp_link;
/* HEADER BYTE 1 */
header = panel->hdr_data.vscext_header_byte1;
parity = dp_header_get_parity(header);
data = ((header << HEADER_BYTE_1_BIT)
| (parity << PARITY_BYTE_1_BIT));
- dp_write(base + MMSS_DP_VSCEXT_0, data);
+ dp_write(catalog, io_data, MMSS_DP_VSCEXT_0, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
@@ -363,22 +425,22 @@
parity = dp_header_get_parity(header);
data = ((header << HEADER_BYTE_2_BIT)
| (parity << PARITY_BYTE_2_BIT));
- dp_write(base + MMSS_DP_VSCEXT_1, data);
+ dp_write(catalog, io_data, MMSS_DP_VSCEXT_1, data);
/* HEADER BYTE 3 */
header = panel->hdr_data.vscext_header_byte3;
parity = dp_header_get_parity(header);
data = ((header << HEADER_BYTE_3_BIT)
| (parity << PARITY_BYTE_3_BIT));
- data |= dp_read(base + MMSS_DP_VSCEXT_1);
- dp_write(base + MMSS_DP_VSCEXT_1, data);
+ data |= dp_read(catalog, io_data, MMSS_DP_VSCEXT_1);
+ dp_write(catalog, io_data, MMSS_DP_VSCEXT_1, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
data = panel->hdr_data.version;
data |= panel->hdr_data.length << 8;
data |= hdr->eotf << 16;
- dp_write(base + MMSS_DP_VSCEXT_2, data);
+ dp_write(catalog, io_data, MMSS_DP_VSCEXT_2, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
@@ -386,7 +448,7 @@
(DP_GET_MSB(hdr->display_primaries_x[0]) << 8) |
(DP_GET_LSB(hdr->display_primaries_y[0]) << 16) |
(DP_GET_MSB(hdr->display_primaries_y[0]) << 24));
- dp_write(base + MMSS_DP_VSCEXT_3, data);
+ dp_write(catalog, io_data, MMSS_DP_VSCEXT_3, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
@@ -394,7 +456,7 @@
(DP_GET_MSB(hdr->display_primaries_x[1]) << 8) |
(DP_GET_LSB(hdr->display_primaries_y[1]) << 16) |
(DP_GET_MSB(hdr->display_primaries_y[1]) << 24));
- dp_write(base + MMSS_DP_VSCEXT_4, data);
+ dp_write(catalog, io_data, MMSS_DP_VSCEXT_4, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
@@ -402,7 +464,7 @@
(DP_GET_MSB(hdr->display_primaries_x[2]) << 8) |
(DP_GET_LSB(hdr->display_primaries_y[2]) << 16) |
(DP_GET_MSB(hdr->display_primaries_y[2]) << 24));
- dp_write(base + MMSS_DP_VSCEXT_5, data);
+ dp_write(catalog, io_data, MMSS_DP_VSCEXT_5, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
@@ -410,7 +472,7 @@
(DP_GET_MSB(hdr->white_point_x) << 8) |
(DP_GET_LSB(hdr->white_point_y) << 16) |
(DP_GET_MSB(hdr->white_point_y) << 24));
- dp_write(base + MMSS_DP_VSCEXT_6, data);
+ dp_write(catalog, io_data, MMSS_DP_VSCEXT_6, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
@@ -418,7 +480,7 @@
(DP_GET_MSB(hdr->max_luminance) << 8) |
(DP_GET_LSB(hdr->min_luminance) << 16) |
(DP_GET_MSB(hdr->min_luminance) << 24));
- dp_write(base + MMSS_DP_VSCEXT_7, data);
+ dp_write(catalog, io_data, MMSS_DP_VSCEXT_7, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
@@ -426,12 +488,12 @@
(DP_GET_MSB(hdr->max_content_light_level) << 8) |
(DP_GET_LSB(hdr->max_average_light_level) << 16) |
(DP_GET_MSB(hdr->max_average_light_level) << 24));
- dp_write(base + MMSS_DP_VSCEXT_8, data);
+ dp_write(catalog, io_data, MMSS_DP_VSCEXT_8, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
data = 0;
- dp_write(base + MMSS_DP_VSCEXT_9, data);
+ dp_write(catalog, io_data, MMSS_DP_VSCEXT_9, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
@@ -442,7 +504,7 @@
static void dp_catalog_panel_setup_vsc_sdp(struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
u32 header, parity, data;
u8 bpc, off = 0;
u8 buf[SZ_128];
@@ -452,15 +514,15 @@
return;
}
- dp_catalog_get_priv(panel);
- base = catalog->io->dp_link.base;
+ catalog = dp_catalog_get_priv(panel);
+ io_data = catalog->io.dp_link;
/* HEADER BYTE 1 */
header = panel->hdr_data.vsc_header_byte1;
parity = dp_header_get_parity(header);
data = ((header << HEADER_BYTE_1_BIT)
| (parity << PARITY_BYTE_1_BIT));
- dp_write(base + MMSS_DP_GENERIC0_0, data);
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_0, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
@@ -469,32 +531,32 @@
parity = dp_header_get_parity(header);
data = ((header << HEADER_BYTE_2_BIT)
| (parity << PARITY_BYTE_2_BIT));
- dp_write(base + MMSS_DP_GENERIC0_1, data);
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_1, data);
/* HEADER BYTE 3 */
header = panel->hdr_data.vsc_header_byte3;
parity = dp_header_get_parity(header);
data = ((header << HEADER_BYTE_3_BIT)
| (parity << PARITY_BYTE_3_BIT));
- data |= dp_read(base + MMSS_DP_GENERIC0_1);
- dp_write(base + MMSS_DP_GENERIC0_1, data);
+ data |= dp_read(catalog, io_data, MMSS_DP_GENERIC0_1);
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_1, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
data = 0;
- dp_write(base + MMSS_DP_GENERIC0_2, data);
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_2, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
- dp_write(base + MMSS_DP_GENERIC0_3, data);
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_3, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
- dp_write(base + MMSS_DP_GENERIC0_4, data);
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_4, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
- dp_write(base + MMSS_DP_GENERIC0_5, data);
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_5, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
@@ -517,20 +579,20 @@
((panel->hdr_data.dynamic_range & 0x1) << 15) |
((panel->hdr_data.content_type & 0x7) << 16);
- dp_write(base + MMSS_DP_GENERIC0_6, data);
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_6, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
data = 0;
- dp_write(base + MMSS_DP_GENERIC0_7, data);
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_7, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
- dp_write(base + MMSS_DP_GENERIC0_8, data);
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_8, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
- dp_write(base + MMSS_DP_GENERIC0_9, data);
+ dp_write(catalog, io_data, MMSS_DP_GENERIC0_9, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
@@ -541,7 +603,7 @@
static void dp_catalog_panel_config_hdr(struct dp_catalog_panel *panel, bool en)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
u32 cfg, cfg2, misc;
if (!panel) {
@@ -549,21 +611,21 @@
return;
}
- dp_catalog_get_priv(panel);
- base = catalog->io->dp_link.base;
+ catalog = dp_catalog_get_priv(panel);
+ io_data = catalog->io.dp_link;
- cfg = dp_read(base + MMSS_DP_SDP_CFG);
- cfg2 = dp_read(base + MMSS_DP_SDP_CFG2);
- misc = dp_read(base + DP_MISC1_MISC0);
+ cfg = dp_read(catalog, io_data, MMSS_DP_SDP_CFG);
+ cfg2 = dp_read(catalog, io_data, MMSS_DP_SDP_CFG2);
+ misc = dp_read(catalog, io_data, DP_MISC1_MISC0);
if (en) {
/* VSCEXT_SDP_EN, GEN0_SDP_EN */
cfg |= BIT(16) | BIT(17);
- dp_write(base + MMSS_DP_SDP_CFG, cfg);
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG, cfg);
/* EXTN_SDPSIZE GENERIC0_SDPSIZE */
cfg2 |= BIT(15) | BIT(16);
- dp_write(base + MMSS_DP_SDP_CFG2, cfg2);
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG2, cfg2);
dp_catalog_panel_setup_vsc_sdp(panel);
dp_catalog_panel_setup_infoframe_sdp(panel);
@@ -578,11 +640,11 @@
} else {
/* VSCEXT_SDP_EN, GEN0_SDP_EN */
cfg &= ~BIT(16) & ~BIT(17);
- dp_write(base + MMSS_DP_SDP_CFG, cfg);
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG, cfg);
/* EXTN_SDPSIZE GENERIC0_SDPSIZE */
cfg2 &= ~BIT(15) & ~BIT(16);
- dp_write(base + MMSS_DP_SDP_CFG2, cfg2);
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG2, cfg2);
/* switch back to MSA */
misc &= ~BIT(14);
@@ -590,78 +652,78 @@
pr_debug("Disabled\n");
}
- dp_write(base + DP_MISC1_MISC0, misc);
+ dp_write(catalog, io_data, DP_MISC1_MISC0, misc);
- dp_write(base + MMSS_DP_SDP_CFG3, 0x01);
- dp_write(base + MMSS_DP_SDP_CFG3, 0x00);
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG3, 0x01);
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG3, 0x00);
}
static void dp_catalog_ctrl_update_transfer_unit(struct dp_catalog_ctrl *ctrl)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!ctrl) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(ctrl);
- base = catalog->io->dp_link.base;
+ catalog = dp_catalog_get_priv(ctrl);
+ io_data = catalog->io.dp_link;
- dp_write(base + DP_VALID_BOUNDARY, ctrl->valid_boundary);
- dp_write(base + DP_TU, ctrl->dp_tu);
- dp_write(base + DP_VALID_BOUNDARY_2, ctrl->valid_boundary2);
+ dp_write(catalog, io_data, DP_VALID_BOUNDARY, ctrl->valid_boundary);
+ dp_write(catalog, io_data, DP_TU, ctrl->dp_tu);
+ dp_write(catalog, io_data, DP_VALID_BOUNDARY_2, ctrl->valid_boundary2);
}
static void dp_catalog_ctrl_state_ctrl(struct dp_catalog_ctrl *ctrl, u32 state)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!ctrl) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(ctrl);
- base = catalog->io->dp_link.base;
+ catalog = dp_catalog_get_priv(ctrl);
+ io_data = catalog->io.dp_link;
- dp_write(base + DP_STATE_CTRL, state);
+ dp_write(catalog, io_data, DP_STATE_CTRL, state);
}
static void dp_catalog_ctrl_config_ctrl(struct dp_catalog_ctrl *ctrl, u32 cfg)
{
struct dp_catalog_private *catalog;
- void __iomem *link_base;
+ struct dp_io_data *io_data;
if (!ctrl) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(ctrl);
- link_base = catalog->io->dp_link.base;
+ catalog = dp_catalog_get_priv(ctrl);
+ io_data = catalog->io.dp_link;
pr_debug("DP_CONFIGURATION_CTRL=0x%x\n", cfg);
- dp_write(link_base + DP_CONFIGURATION_CTRL, cfg);
+ dp_write(catalog, io_data, DP_CONFIGURATION_CTRL, cfg);
}
static void dp_catalog_ctrl_lane_mapping(struct dp_catalog_ctrl *ctrl)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!ctrl) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(ctrl);
- base = catalog->io->dp_link.base;
+ catalog = dp_catalog_get_priv(ctrl);
+ io_data = catalog->io.dp_link;
- dp_write(base + DP_LOGICAL2PHYSICAL_LANE_MAPPING, 0xe4);
+ dp_write(catalog, io_data, DP_LOGICAL2PHYSICAL_LANE_MAPPING, 0xe4);
}
static void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog_ctrl *ctrl,
@@ -669,29 +731,29 @@
{
u32 mainlink_ctrl;
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!ctrl) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(ctrl);
- base = catalog->io->dp_link.base;
+ catalog = dp_catalog_get_priv(ctrl);
+ io_data = catalog->io.dp_link;
if (enable) {
- dp_write(base + DP_MAINLINK_CTRL, 0x02000000);
+ dp_write(catalog, io_data, DP_MAINLINK_CTRL, 0x02000000);
wmb(); /* make sure mainlink is turned off before reset */
- dp_write(base + DP_MAINLINK_CTRL, 0x02000002);
+ dp_write(catalog, io_data, DP_MAINLINK_CTRL, 0x02000002);
wmb(); /* make sure mainlink entered reset */
- dp_write(base + DP_MAINLINK_CTRL, 0x02000000);
+ dp_write(catalog, io_data, DP_MAINLINK_CTRL, 0x02000000);
wmb(); /* make sure mainlink reset done */
- dp_write(base + DP_MAINLINK_CTRL, 0x02000001);
+ dp_write(catalog, io_data, DP_MAINLINK_CTRL, 0x02000001);
wmb(); /* make sure mainlink turned on */
} else {
- mainlink_ctrl = dp_read(base + DP_MAINLINK_CTRL);
+ mainlink_ctrl = dp_read(catalog, io_data, DP_MAINLINK_CTRL);
mainlink_ctrl &= ~BIT(0);
- dp_write(base + DP_MAINLINK_CTRL, mainlink_ctrl);
+ dp_write(catalog, io_data, DP_MAINLINK_CTRL, mainlink_ctrl);
}
}
@@ -700,21 +762,23 @@
{
u32 misc_val = cc;
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!ctrl) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(ctrl);
- base = catalog->io->dp_link.base;
+ catalog = dp_catalog_get_priv(ctrl);
+ io_data = catalog->io.dp_link;
+ misc_val = dp_read(catalog, io_data, DP_MISC1_MISC0);
+ misc_val |= cc;
misc_val |= (tb << 5);
misc_val |= BIT(0); /* Configure clock to synchronous mode */
pr_debug("misc settings = 0x%x\n", misc_val);
- dp_write(base + DP_MISC1_MISC0, misc_val);
+ dp_write(catalog, io_data, DP_MISC1_MISC0, misc_val);
}
static void dp_catalog_ctrl_config_msa(struct dp_catalog_ctrl *ctrl,
@@ -728,14 +792,14 @@
u32 const link_rate_hbr2 = 540000;
u32 const link_rate_hbr3 = 810000;
struct dp_catalog_private *catalog;
- void __iomem *base_cc, *base_ctrl;
+ struct dp_io_data *io_data;
if (!ctrl) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(ctrl);
+ catalog = dp_catalog_get_priv(ctrl);
if (fixed_nvid) {
pr_debug("use fixed NVID=0x%x\n", nvid_fixed);
nvid = nvid_fixed;
@@ -756,10 +820,10 @@
*/
mvid = (u32) mvid_calc;
} else {
- base_cc = catalog->io->dp_cc_io.base;
+ io_data = catalog->io.dp_mmss_cc;
- pixel_m = dp_read(base_cc + MMSS_DP_PIXEL_M);
- pixel_n = dp_read(base_cc + MMSS_DP_PIXEL_N);
+ pixel_m = dp_read(catalog, io_data, MMSS_DP_PIXEL_M);
+ pixel_n = dp_read(catalog, io_data, MMSS_DP_PIXEL_N);
pr_debug("pixel_m=0x%x, pixel_n=0x%x\n", pixel_m, pixel_n);
mvid = (pixel_m & 0xFFFF) * 5;
@@ -774,10 +838,10 @@
nvid *= 3;
}
- base_ctrl = catalog->io->dp_link.base;
+ io_data = catalog->io.dp_link;
pr_debug("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
- dp_write(base_ctrl + DP_SOFTWARE_MVID, mvid);
- dp_write(base_ctrl + DP_SOFTWARE_NVID, nvid);
+ dp_write(catalog, io_data, DP_SOFTWARE_MVID, mvid);
+ dp_write(catalog, io_data, DP_SOFTWARE_NVID, nvid);
}
static void dp_catalog_ctrl_set_pattern(struct dp_catalog_ctrl *ctrl,
@@ -786,26 +850,26 @@
int bit, cnt = 10;
u32 data;
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!ctrl) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(ctrl);
- base = catalog->io->dp_link.base;
+ catalog = dp_catalog_get_priv(ctrl);
+ io_data = catalog->io.dp_link;
bit = 1;
bit <<= (pattern - 1);
pr_debug("hw: bit=%d train=%d\n", bit, pattern);
- dp_write(base + DP_STATE_CTRL, bit);
+ dp_write(catalog, io_data, DP_STATE_CTRL, bit);
bit = 8;
bit <<= (pattern - 1);
while (cnt--) {
- data = dp_read(base + DP_MAINLINK_READY);
+ data = dp_read(catalog, io_data, DP_MAINLINK_READY);
if (data & bit)
break;
}
@@ -817,35 +881,35 @@
static void dp_catalog_ctrl_usb_reset(struct dp_catalog_ctrl *ctrl, bool flip)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!ctrl) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(ctrl);
+ catalog = dp_catalog_get_priv(ctrl);
- base = catalog->io->usb3_dp_com.base;
+ io_data = catalog->io.usb3_dp_com;
- dp_write(base + USB3_DP_COM_RESET_OVRD_CTRL, 0x0a);
- dp_write(base + USB3_DP_COM_PHY_MODE_CTRL, 0x02);
- dp_write(base + USB3_DP_COM_SW_RESET, 0x01);
+ dp_write(catalog, io_data, USB3_DP_COM_RESET_OVRD_CTRL, 0x0a);
+ dp_write(catalog, io_data, USB3_DP_COM_PHY_MODE_CTRL, 0x02);
+ dp_write(catalog, io_data, USB3_DP_COM_SW_RESET, 0x01);
/* make sure usb3 com phy software reset is done */
wmb();
if (!flip) /* CC1 */
- dp_write(base + USB3_DP_COM_TYPEC_CTRL, 0x02);
+ dp_write(catalog, io_data, USB3_DP_COM_TYPEC_CTRL, 0x02);
else /* CC2 */
- dp_write(base + USB3_DP_COM_TYPEC_CTRL, 0x03);
+ dp_write(catalog, io_data, USB3_DP_COM_TYPEC_CTRL, 0x03);
- dp_write(base + USB3_DP_COM_SWI_CTRL, 0x00);
- dp_write(base + USB3_DP_COM_SW_RESET, 0x00);
+ dp_write(catalog, io_data, USB3_DP_COM_SWI_CTRL, 0x00);
+ dp_write(catalog, io_data, USB3_DP_COM_SW_RESET, 0x00);
/* make sure the software reset is done */
wmb();
- dp_write(base + USB3_DP_COM_POWER_DOWN_CTRL, 0x01);
- dp_write(base + USB3_DP_COM_RESET_OVRD_CTRL, 0x00);
+ dp_write(catalog, io_data, USB3_DP_COM_POWER_DOWN_CTRL, 0x01);
+ dp_write(catalog, io_data, USB3_DP_COM_RESET_OVRD_CTRL, 0x00);
/* make sure phy is brought out of reset */
wmb();
}
@@ -854,50 +918,53 @@
bool enable)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!panel) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(panel);
- base = catalog->io->dp_p0.base;
+ catalog = dp_catalog_get_priv(panel);
+ io_data = catalog->io.dp_p0;
if (!enable) {
- dp_write(base + MMSS_DP_TPG_MAIN_CONTROL, 0x0);
- dp_write(base + MMSS_DP_BIST_ENABLE, 0x0);
- dp_write(base + MMSS_DP_TIMING_ENGINE_EN, 0x0);
+ dp_write(catalog, io_data, MMSS_DP_TPG_MAIN_CONTROL, 0x0);
+ dp_write(catalog, io_data, MMSS_DP_BIST_ENABLE, 0x0);
+ dp_write(catalog, io_data, MMSS_DP_TIMING_ENGINE_EN, 0x0);
wmb(); /* ensure Timing generator is turned off */
return;
}
- dp_write(base + MMSS_DP_INTF_CONFIG, 0x0);
- dp_write(base + MMSS_DP_INTF_HSYNC_CTL, panel->hsync_ctl);
- dp_write(base + MMSS_DP_INTF_VSYNC_PERIOD_F0, panel->vsync_period *
- panel->hsync_period);
- dp_write(base + MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0, panel->v_sync_width *
- panel->hsync_period);
- dp_write(base + MMSS_DP_INTF_VSYNC_PERIOD_F1, 0);
- dp_write(base + MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1, 0);
- dp_write(base + MMSS_DP_INTF_DISPLAY_HCTL, panel->display_hctl);
- dp_write(base + MMSS_DP_INTF_ACTIVE_HCTL, 0);
- dp_write(base + MMSS_INTF_DISPLAY_V_START_F0, panel->display_v_start);
- dp_write(base + MMSS_DP_INTF_DISPLAY_V_END_F0, panel->display_v_end);
- dp_write(base + MMSS_INTF_DISPLAY_V_START_F1, 0);
- dp_write(base + MMSS_DP_INTF_DISPLAY_V_END_F1, 0);
- dp_write(base + MMSS_DP_INTF_ACTIVE_V_START_F0, 0);
- dp_write(base + MMSS_DP_INTF_ACTIVE_V_END_F0, 0);
- dp_write(base + MMSS_DP_INTF_ACTIVE_V_START_F1, 0);
- dp_write(base + MMSS_DP_INTF_ACTIVE_V_END_F1, 0);
- dp_write(base + MMSS_DP_INTF_POLARITY_CTL, 0);
+ dp_write(catalog, io_data, MMSS_DP_INTF_CONFIG, 0x0);
+ dp_write(catalog, io_data, MMSS_DP_INTF_HSYNC_CTL, panel->hsync_ctl);
+ dp_write(catalog, io_data, MMSS_DP_INTF_VSYNC_PERIOD_F0,
+ panel->vsync_period * panel->hsync_period);
+ dp_write(catalog, io_data, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0,
+ panel->v_sync_width * panel->hsync_period);
+ dp_write(catalog, io_data, MMSS_DP_INTF_VSYNC_PERIOD_F1, 0);
+ dp_write(catalog, io_data, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1, 0);
+ dp_write(catalog, io_data, MMSS_DP_INTF_DISPLAY_HCTL,
+ panel->display_hctl);
+ dp_write(catalog, io_data, MMSS_DP_INTF_ACTIVE_HCTL, 0);
+ dp_write(catalog, io_data, MMSS_INTF_DISPLAY_V_START_F0,
+ panel->display_v_start);
+ dp_write(catalog, io_data, MMSS_DP_INTF_DISPLAY_V_END_F0,
+ panel->display_v_end);
+ dp_write(catalog, io_data, MMSS_INTF_DISPLAY_V_START_F1, 0);
+ dp_write(catalog, io_data, MMSS_DP_INTF_DISPLAY_V_END_F1, 0);
+ dp_write(catalog, io_data, MMSS_DP_INTF_ACTIVE_V_START_F0, 0);
+ dp_write(catalog, io_data, MMSS_DP_INTF_ACTIVE_V_END_F0, 0);
+ dp_write(catalog, io_data, MMSS_DP_INTF_ACTIVE_V_START_F1, 0);
+ dp_write(catalog, io_data, MMSS_DP_INTF_ACTIVE_V_END_F1, 0);
+ dp_write(catalog, io_data, MMSS_DP_INTF_POLARITY_CTL, 0);
wmb(); /* ensure TPG registers are programmed */
- dp_write(base + MMSS_DP_TPG_MAIN_CONTROL, 0x100);
- dp_write(base + MMSS_DP_TPG_VIDEO_CONFIG, 0x5);
+ dp_write(catalog, io_data, MMSS_DP_TPG_MAIN_CONTROL, 0x100);
+ dp_write(catalog, io_data, MMSS_DP_TPG_VIDEO_CONFIG, 0x5);
wmb(); /* ensure TPG config is programmed */
- dp_write(base + MMSS_DP_BIST_ENABLE, 0x1);
- dp_write(base + MMSS_DP_TIMING_ENGINE_EN, 0x1);
+ dp_write(catalog, io_data, MMSS_DP_BIST_ENABLE, 0x1);
+ dp_write(catalog, io_data, MMSS_DP_TIMING_ENGINE_EN, 0x1);
wmb(); /* ensure Timing generator is turned on */
}
@@ -905,24 +972,24 @@
{
u32 sw_reset;
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!ctrl) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(ctrl);
- base = catalog->io->dp_ahb.base;
+ catalog = dp_catalog_get_priv(ctrl);
+ io_data = catalog->io.dp_ahb;
- sw_reset = dp_read(base + DP_SW_RESET);
+ sw_reset = dp_read(catalog, io_data, DP_SW_RESET);
sw_reset |= BIT(0);
- dp_write(base + DP_SW_RESET, sw_reset);
+ dp_write(catalog, io_data, DP_SW_RESET, sw_reset);
usleep_range(1000, 1010); /* h/w recommended delay */
sw_reset &= ~BIT(0);
- dp_write(base + DP_SW_RESET, sw_reset);
+ dp_write(catalog, io_data, DP_SW_RESET, sw_reset);
}
static bool dp_catalog_ctrl_mainlink_ready(struct dp_catalog_ctrl *ctrl)
@@ -930,19 +997,19 @@
u32 data;
int cnt = 10;
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!ctrl) {
pr_err("invalid input\n");
goto end;
}
- dp_catalog_get_priv(ctrl);
- base = catalog->io->dp_link.base;
+ catalog = dp_catalog_get_priv(ctrl);
+ io_data = catalog->io.dp_link;
while (--cnt) {
/* DP_MAINLINK_READY */
- data = dp_read(base + DP_MAINLINK_READY);
+ data = dp_read(catalog, io_data, DP_MAINLINK_READY);
if (data & BIT(0))
return true;
@@ -957,52 +1024,51 @@
bool enable)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!ctrl) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(ctrl);
- base = catalog->io->dp_ahb.base;
+ catalog = dp_catalog_get_priv(ctrl);
+ io_data = catalog->io.dp_ahb;
if (enable) {
- dp_write(base + DP_INTR_STATUS, DP_INTR_MASK1);
- dp_write(base + DP_INTR_STATUS2, DP_INTR_MASK2);
+ dp_write(catalog, io_data, DP_INTR_STATUS, DP_INTR_MASK1);
+ dp_write(catalog, io_data, DP_INTR_STATUS2, DP_INTR_MASK2);
} else {
- dp_write(base + DP_INTR_STATUS, 0x00);
- dp_write(base + DP_INTR_STATUS2, 0x00);
+ dp_write(catalog, io_data, DP_INTR_STATUS, 0x00);
+ dp_write(catalog, io_data, DP_INTR_STATUS2, 0x00);
}
}
static void dp_catalog_ctrl_hpd_config(struct dp_catalog_ctrl *ctrl, bool en)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!ctrl) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(ctrl);
- base = catalog->io->dp_aux.base;
+ catalog = dp_catalog_get_priv(ctrl);
+ io_data = catalog->io.dp_aux;
if (en) {
- u32 reftimer = dp_read(base + DP_DP_HPD_REFTIMER);
+ u32 reftimer = dp_read(catalog, io_data, DP_DP_HPD_REFTIMER);
- dp_write(base + DP_DP_HPD_INT_ACK, 0xF);
- dp_write(base + DP_DP_HPD_INT_MASK, 0xF);
-
+ dp_write(catalog, io_data, DP_DP_HPD_INT_ACK, 0xF);
+ dp_write(catalog, io_data, DP_DP_HPD_INT_MASK, 0xF);
/* Enabling REFTIMER */
reftimer |= BIT(16);
- dp_write(base + DP_DP_HPD_REFTIMER, 0xF);
+ dp_write(catalog, io_data, DP_DP_HPD_REFTIMER, 0xF);
/* Enable HPD */
- dp_write(base + DP_DP_HPD_CTRL, 0x1);
+ dp_write(catalog, io_data, DP_DP_HPD_CTRL, 0x1);
} else {
/*Disable HPD */
- dp_write(base + DP_DP_HPD_CTRL, 0x0);
+ dp_write(catalog, io_data, DP_DP_HPD_CTRL, 0x0);
}
}
@@ -1010,40 +1076,40 @@
{
u32 ack = 0;
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!ctrl) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(ctrl);
- base = catalog->io->dp_ahb.base;
+ catalog = dp_catalog_get_priv(ctrl);
+ io_data = catalog->io.dp_ahb;
- ctrl->isr = dp_read(base + DP_INTR_STATUS2);
+ ctrl->isr = dp_read(catalog, io_data, DP_INTR_STATUS2);
ctrl->isr &= ~DP_INTR_MASK2;
ack = ctrl->isr & DP_INTERRUPT_STATUS2;
ack <<= 1;
ack |= DP_INTR_MASK2;
- dp_write(base + DP_INTR_STATUS2, ack);
+ dp_write(catalog, io_data, DP_INTR_STATUS2, ack);
}
static void dp_catalog_ctrl_phy_reset(struct dp_catalog_ctrl *ctrl)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!ctrl) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(ctrl);
- base = catalog->io->dp_ahb.base;
+ catalog = dp_catalog_get_priv(ctrl);
+ io_data = catalog->io.dp_ahb;
- dp_write(base + DP_PHY_CTRL, 0x5); /* bit 0 & 2 */
+ dp_write(catalog, io_data, DP_PHY_CTRL, 0x5); /* bit 0 & 2 */
usleep_range(1000, 1010); /* h/w recommended delay */
- dp_write(base + DP_PHY_CTRL, 0x0);
+ dp_write(catalog, io_data, DP_PHY_CTRL, 0x0);
wmb(); /* make sure PHY reset done */
}
@@ -1052,6 +1118,7 @@
{
u32 info = 0x0;
struct dp_catalog_private *catalog;
+ struct dp_io_data *io_data;
u8 orientation = BIT(!!flipped);
if (!ctrl) {
@@ -1059,20 +1126,22 @@
return;
}
- dp_catalog_get_priv(ctrl);
+ catalog = dp_catalog_get_priv(ctrl);
+
+ io_data = catalog->io.dp_phy;
info |= (ln_cnt & 0x0F);
info |= ((orientation & 0x0F) << 4);
pr_debug("Shared Info = 0x%x\n", info);
- dp_write(catalog->io->phy_io.base + DP_PHY_SPARE0, info);
+ dp_write(catalog, io_data, DP_PHY_SPARE0, info);
}
static void dp_catalog_ctrl_update_vx_px(struct dp_catalog_ctrl *ctrl,
u8 v_level, u8 p_level)
{
struct dp_catalog_private *catalog;
- void __iomem *base0, *base1;
+ struct dp_io_data *io_data;
u8 value0, value1;
if (!ctrl) {
@@ -1080,9 +1149,7 @@
return;
}
- dp_catalog_get_priv(ctrl);
- base0 = catalog->io->ln_tx0_io.base;
- base1 = catalog->io->ln_tx1_io.base;
+ catalog = dp_catalog_get_priv(ctrl);
pr_debug("hw: v=%d p=%d\n", v_level, p_level);
@@ -1090,10 +1157,14 @@
value1 = vm_pre_emphasis[v_level][p_level];
/* program default setting first */
- dp_write(base0 + TXn_TX_DRV_LVL, 0x2A);
- dp_write(base1 + TXn_TX_DRV_LVL, 0x2A);
- dp_write(base0 + TXn_TX_EMP_POST1_LVL, 0x20);
- dp_write(base1 + TXn_TX_EMP_POST1_LVL, 0x20);
+
+ io_data = catalog->io.dp_ln_tx0;
+ dp_write(catalog, io_data, TXn_TX_DRV_LVL, 0x2A);
+ dp_write(catalog, io_data, TXn_TX_EMP_POST1_LVL, 0x20);
+
+ io_data = catalog->io.dp_ln_tx1;
+ dp_write(catalog, io_data, TXn_TX_DRV_LVL, 0x2A);
+ dp_write(catalog, io_data, TXn_TX_EMP_POST1_LVL, 0x20);
/* Enable MUX to use Cursor values from these registers */
value0 |= BIT(5);
@@ -1101,10 +1172,13 @@
/* Configure host and panel only if both values are allowed */
if (value0 != 0xFF && value1 != 0xFF) {
- dp_write(base0 + TXn_TX_DRV_LVL, value0);
- dp_write(base1 + TXn_TX_DRV_LVL, value0);
- dp_write(base0 + TXn_TX_EMP_POST1_LVL, value1);
- dp_write(base1 + TXn_TX_EMP_POST1_LVL, value1);
+ io_data = catalog->io.dp_ln_tx0;
+ dp_write(catalog, io_data, TXn_TX_DRV_LVL, value0);
+ dp_write(catalog, io_data, TXn_TX_EMP_POST1_LVL, value1);
+
+ io_data = catalog->io.dp_ln_tx1;
+ dp_write(catalog, io_data, TXn_TX_DRV_LVL, value0);
+ dp_write(catalog, io_data, TXn_TX_EMP_POST1_LVL, value1);
pr_debug("hw: vx_value=0x%x px_value=0x%x\n",
value0, value1);
@@ -1119,54 +1193,61 @@
{
struct dp_catalog_private *catalog;
u32 value = 0x0;
- void __iomem *base = NULL;
+ struct dp_io_data *io_data = NULL;
if (!ctrl) {
pr_err("invalid input\n");
return;
}
- dp_catalog_get_priv(ctrl);
+ catalog = dp_catalog_get_priv(ctrl);
- base = catalog->io->dp_link.base;
+ io_data = catalog->io.dp_link;
- dp_write(base + DP_STATE_CTRL, 0x0);
+ dp_write(catalog, io_data, DP_STATE_CTRL, 0x0);
switch (pattern) {
case DP_TEST_PHY_PATTERN_D10_2_NO_SCRAMBLING:
- dp_write(base + DP_STATE_CTRL, 0x1);
+ dp_write(catalog, io_data, DP_STATE_CTRL, 0x1);
break;
case DP_TEST_PHY_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
value &= ~(1 << 16);
- dp_write(base + DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
+ dp_write(catalog, io_data, DP_HBR2_COMPLIANCE_SCRAMBLER_RESET,
+ value);
value |= 0xFC;
- dp_write(base + DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
- dp_write(base + DP_MAINLINK_LEVELS, 0x2);
- dp_write(base + DP_STATE_CTRL, 0x10);
+ dp_write(catalog, io_data, DP_HBR2_COMPLIANCE_SCRAMBLER_RESET,
+ value);
+ dp_write(catalog, io_data, DP_MAINLINK_LEVELS, 0x2);
+ dp_write(catalog, io_data, DP_STATE_CTRL, 0x10);
break;
case DP_TEST_PHY_PATTERN_PRBS7:
- dp_write(base + DP_STATE_CTRL, 0x20);
+ dp_write(catalog, io_data, DP_STATE_CTRL, 0x20);
break;
case DP_TEST_PHY_PATTERN_80_BIT_CUSTOM_PATTERN:
- dp_write(base + DP_STATE_CTRL, 0x40);
+ dp_write(catalog, io_data, DP_STATE_CTRL, 0x40);
/* 00111110000011111000001111100000 */
- dp_write(base + DP_TEST_80BIT_CUSTOM_PATTERN_REG0, 0x3E0F83E0);
+ dp_write(catalog, io_data, DP_TEST_80BIT_CUSTOM_PATTERN_REG0,
+ 0x3E0F83E0);
/* 00001111100000111110000011111000 */
- dp_write(base + DP_TEST_80BIT_CUSTOM_PATTERN_REG1, 0x0F83E0F8);
+ dp_write(catalog, io_data, DP_TEST_80BIT_CUSTOM_PATTERN_REG1,
+ 0x0F83E0F8);
/* 1111100000111110 */
- dp_write(base + DP_TEST_80BIT_CUSTOM_PATTERN_REG2, 0x0000F83E);
+ dp_write(catalog, io_data, DP_TEST_80BIT_CUSTOM_PATTERN_REG2,
+ 0x0000F83E);
break;
case DP_TEST_PHY_PATTERN_CP2520_PATTERN_1:
value = BIT(16);
- dp_write(base + DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
+ dp_write(catalog, io_data, DP_HBR2_COMPLIANCE_SCRAMBLER_RESET,
+ value);
value |= 0xFC;
- dp_write(base + DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
- dp_write(base + DP_MAINLINK_LEVELS, 0x2);
- dp_write(base + DP_STATE_CTRL, 0x10);
+ dp_write(catalog, io_data, DP_HBR2_COMPLIANCE_SCRAMBLER_RESET,
+ value);
+ dp_write(catalog, io_data, DP_MAINLINK_LEVELS, 0x2);
+ dp_write(catalog, io_data, DP_STATE_CTRL, 0x10);
break;
case DP_TEST_PHY_PATTERN_CP2520_PATTERN_3:
- dp_write(base + DP_MAINLINK_CTRL, 0x11);
- dp_write(base + DP_STATE_CTRL, 0x8);
+ dp_write(catalog, io_data, DP_MAINLINK_CTRL, 0x11);
+ dp_write(catalog, io_data, DP_STATE_CTRL, 0x8);
break;
default:
pr_debug("No valid test pattern requested: 0x%x\n", pattern);
@@ -1180,38 +1261,101 @@
static u32 dp_catalog_ctrl_read_phy_pattern(struct dp_catalog_ctrl *ctrl)
{
struct dp_catalog_private *catalog;
- void __iomem *base = NULL;
+ struct dp_io_data *io_data = NULL;
if (!ctrl) {
pr_err("invalid input\n");
return 0;
}
- dp_catalog_get_priv(ctrl);
+ catalog = dp_catalog_get_priv(ctrl);
- base = catalog->io->dp_link.base;
+ io_data = catalog->io.dp_link;
- return dp_read(base + DP_MAINLINK_READY);
+ return dp_read(catalog, io_data, DP_MAINLINK_READY);
+}
+
+static int dp_catalog_reg_dump(struct dp_catalog *dp_catalog,
+ char *name, u8 **out_buf, u32 *out_buf_len)
+{
+ int ret = 0;
+ u8 *buf;
+ u32 len;
+ struct dp_io_data *io_data;
+ struct dp_catalog_private *catalog;
+ struct dp_parser *parser;
+
+ if (!dp_catalog) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ catalog = container_of(dp_catalog, struct dp_catalog_private,
+ dp_catalog);
+
+ parser = catalog->parser;
+ parser->get_io_buf(parser, name);
+ io_data = parser->get_io(parser, name);
+ if (!io_data) {
+ pr_err("IO %s not found\n", name);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ buf = io_data->buf;
+ len = io_data->io.len;
+
+ if (!buf || !len) {
+ pr_err("no buffer available\n");
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ if (!strcmp(catalog->exe_mode, "hw") ||
+ !strcmp(catalog->exe_mode, "all")) {
+ u32 i, data;
+ u32 const rowsize = 4;
+ void __iomem *addr = io_data->io.base;
+
+ memset(buf, 0, len);
+
+ for (i = 0; i < len / rowsize; i++) {
+ data = readl_relaxed(addr);
+ memcpy(buf + (rowsize * i), &data, sizeof(u32));
+
+ addr += rowsize;
+ }
+ }
+
+ *out_buf = buf;
+ *out_buf_len = len;
+end:
+ if (ret)
+ parser->clear_io_buf(parser);
+
+ return ret;
}
/* panel related catalog functions */
static int dp_catalog_panel_timing_cfg(struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
if (!panel) {
pr_err("invalid input\n");
goto end;
}
- dp_catalog_get_priv(panel);
- base = catalog->io->dp_link.base;
+ catalog = dp_catalog_get_priv(panel);
+ io_data = catalog->io.dp_link;
- dp_write(base + DP_TOTAL_HOR_VER, panel->total);
- dp_write(base + DP_START_HOR_VER_FROM_SYNC, panel->sync_start);
- dp_write(base + DP_HSYNC_VSYNC_WIDTH_POLARITY, panel->width_blanking);
- dp_write(base + DP_ACTIVE_HOR_VER, panel->dp_active);
+ dp_write(catalog, io_data, DP_TOTAL_HOR_VER, panel->total);
+ dp_write(catalog, io_data, DP_START_HOR_VER_FROM_SYNC,
+ panel->sync_start);
+ dp_write(catalog, io_data, DP_HSYNC_VSYNC_WIDTH_POLARITY,
+ panel->width_blanking);
+ dp_write(catalog, io_data, DP_ACTIVE_HOR_VER, panel->dp_active);
end:
return 0;
}
@@ -1250,7 +1394,7 @@
if (!audio)
return;
- dp_catalog_get_priv(audio);
+ catalog = dp_catalog_get_priv(audio);
catalog->audio_map = sdp_map;
}
@@ -1258,17 +1402,17 @@
static void dp_catalog_audio_config_sdp(struct dp_catalog_audio *audio)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
u32 sdp_cfg = 0;
u32 sdp_cfg2 = 0;
if (!audio)
return;
- dp_catalog_get_priv(audio);
- base = catalog->io->dp_link.base;
+ catalog = dp_catalog_get_priv(audio);
+ io_data = catalog->io.dp_link;
- sdp_cfg = dp_read(base + MMSS_DP_SDP_CFG);
+ sdp_cfg = dp_read(catalog, io_data, MMSS_DP_SDP_CFG);
/* AUDIO_TIMESTAMP_SDP_EN */
sdp_cfg |= BIT(1);
@@ -1282,44 +1426,44 @@
sdp_cfg |= BIT(20);
pr_debug("sdp_cfg = 0x%x\n", sdp_cfg);
- dp_write(base + MMSS_DP_SDP_CFG, sdp_cfg);
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG, sdp_cfg);
- sdp_cfg2 = dp_read(base + MMSS_DP_SDP_CFG2);
+ sdp_cfg2 = dp_read(catalog, io_data, MMSS_DP_SDP_CFG2);
/* IFRM_REGSRC -> Do not use reg values */
sdp_cfg2 &= ~BIT(0);
/* AUDIO_STREAM_HB3_REGSRC-> Do not use reg values */
sdp_cfg2 &= ~BIT(1);
pr_debug("sdp_cfg2 = 0x%x\n", sdp_cfg2);
- dp_write(base + MMSS_DP_SDP_CFG2, sdp_cfg2);
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG2, sdp_cfg2);
}
static void dp_catalog_audio_get_header(struct dp_catalog_audio *audio)
{
struct dp_catalog_private *catalog;
u32 (*sdp_map)[DP_AUDIO_SDP_HEADER_MAX];
- void __iomem *base;
+ struct dp_io_data *io_data;
enum dp_catalog_audio_sdp_type sdp;
enum dp_catalog_audio_header_type header;
if (!audio)
return;
- dp_catalog_get_priv(audio);
+ catalog = dp_catalog_get_priv(audio);
- base = catalog->io->dp_link.base;
+ io_data = catalog->io.dp_link;
sdp_map = catalog->audio_map;
sdp = audio->sdp_type;
header = audio->sdp_header;
- audio->data = dp_read(base + sdp_map[sdp][header]);
+ audio->data = dp_read(catalog, io_data, sdp_map[sdp][header]);
}
static void dp_catalog_audio_set_header(struct dp_catalog_audio *audio)
{
struct dp_catalog_private *catalog;
u32 (*sdp_map)[DP_AUDIO_SDP_HEADER_MAX];
- void __iomem *base;
+ struct dp_io_data *io_data;
enum dp_catalog_audio_sdp_type sdp;
enum dp_catalog_audio_header_type header;
u32 data;
@@ -1327,69 +1471,69 @@
if (!audio)
return;
- dp_catalog_get_priv(audio);
+ catalog = dp_catalog_get_priv(audio);
- base = catalog->io->dp_link.base;
+ io_data = catalog->io.dp_link;
sdp_map = catalog->audio_map;
sdp = audio->sdp_type;
header = audio->sdp_header;
data = audio->data;
- dp_write(base + sdp_map[sdp][header], data);
+ dp_write(catalog, io_data, sdp_map[sdp][header], data);
}
static void dp_catalog_audio_config_acr(struct dp_catalog_audio *audio)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
u32 acr_ctrl, select;
- dp_catalog_get_priv(audio);
+ catalog = dp_catalog_get_priv(audio);
select = audio->data;
- base = catalog->io->dp_link.base;
+ io_data = catalog->io.dp_link;
acr_ctrl = select << 4 | BIT(31) | BIT(8) | BIT(14);
pr_debug("select = 0x%x, acr_ctrl = 0x%x\n", select, acr_ctrl);
- dp_write(base + MMSS_DP_AUDIO_ACR_CTRL, acr_ctrl);
+ dp_write(catalog, io_data, MMSS_DP_AUDIO_ACR_CTRL, acr_ctrl);
}
static void dp_catalog_audio_safe_to_exit_level(struct dp_catalog_audio *audio)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
u32 mainlink_levels, safe_to_exit_level;
- dp_catalog_get_priv(audio);
+ catalog = dp_catalog_get_priv(audio);
- base = catalog->io->dp_link.base;
+ io_data = catalog->io.dp_link;
safe_to_exit_level = audio->data;
- mainlink_levels = dp_read(base + DP_MAINLINK_LEVELS);
+ mainlink_levels = dp_read(catalog, io_data, DP_MAINLINK_LEVELS);
mainlink_levels &= 0xFE0;
mainlink_levels |= safe_to_exit_level;
pr_debug("mainlink_level = 0x%x, safe_to_exit_level = 0x%x\n",
mainlink_levels, safe_to_exit_level);
- dp_write(base + DP_MAINLINK_LEVELS, mainlink_levels);
+ dp_write(catalog, io_data, DP_MAINLINK_LEVELS, mainlink_levels);
}
static void dp_catalog_audio_enable(struct dp_catalog_audio *audio)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
bool enable;
u32 audio_ctrl;
- dp_catalog_get_priv(audio);
+ catalog = dp_catalog_get_priv(audio);
- base = catalog->io->dp_link.base;
+ io_data = catalog->io.dp_link;
enable = !!audio->data;
- audio_ctrl = dp_read(base + MMSS_DP_AUDIO_CFG);
+ audio_ctrl = dp_read(catalog, io_data, MMSS_DP_AUDIO_CFG);
if (enable)
audio_ctrl |= BIT(0);
@@ -1397,7 +1541,7 @@
audio_ctrl &= ~BIT(0);
pr_debug("dp_audio_cfg = 0x%x\n", audio_ctrl);
- dp_write(base + MMSS_DP_AUDIO_CFG, audio_ctrl);
+ dp_write(catalog, io_data, MMSS_DP_AUDIO_CFG, audio_ctrl);
/* make sure audio engine is disabled */
wmb();
@@ -1406,18 +1550,18 @@
static void dp_catalog_config_spd_header(struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
u32 value, new_value;
u8 parity_byte;
if (!panel)
return;
- dp_catalog_get_priv(panel);
- base = catalog->io->dp_link.base;
+ catalog = dp_catalog_get_priv(panel);
+ io_data = catalog->io.dp_link;
/* Config header and parity byte 1 */
- value = dp_read(base + MMSS_DP_GENERIC1_0);
+ value = dp_read(catalog, io_data, MMSS_DP_GENERIC1_0);
new_value = 0x83;
parity_byte = dp_header_get_parity(new_value);
@@ -1425,10 +1569,10 @@
| (parity_byte << PARITY_BYTE_1_BIT));
pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
value, parity_byte);
- dp_write(base + MMSS_DP_GENERIC1_0, value);
+ dp_write(catalog, io_data, MMSS_DP_GENERIC1_0, value);
/* Config header and parity byte 2 */
- value = dp_read(base + MMSS_DP_GENERIC1_1);
+ value = dp_read(catalog, io_data, MMSS_DP_GENERIC1_1);
new_value = 0x1b;
parity_byte = dp_header_get_parity(new_value);
@@ -1436,10 +1580,10 @@
| (parity_byte << PARITY_BYTE_2_BIT));
pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
value, parity_byte);
- dp_write(base + MMSS_DP_GENERIC1_1, value);
+ dp_write(catalog, io_data, MMSS_DP_GENERIC1_1, value);
/* Config header and parity byte 3 */
- value = dp_read(base + MMSS_DP_GENERIC1_1);
+ value = dp_read(catalog, io_data, MMSS_DP_GENERIC1_1);
new_value = (0x0 | (0x12 << 2));
parity_byte = dp_header_get_parity(new_value);
@@ -1447,13 +1591,13 @@
| (parity_byte << PARITY_BYTE_3_BIT));
pr_debug("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
new_value, parity_byte);
- dp_write(base + MMSS_DP_GENERIC1_1, value);
+ dp_write(catalog, io_data, MMSS_DP_GENERIC1_1, value);
}
static void dp_catalog_panel_config_spd(struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
- void __iomem *base;
+ struct dp_io_data *io_data;
u32 spd_cfg = 0, spd_cfg2 = 0;
u8 *vendor = NULL, *product = NULL;
/*
@@ -1479,56 +1623,110 @@
if (!panel)
return;
- dp_catalog_get_priv(panel);
- base = catalog->io->dp_link.base;
+ catalog = dp_catalog_get_priv(panel);
+ io_data = catalog->io.dp_link;
dp_catalog_config_spd_header(panel);
vendor = panel->spd_vendor_name;
product = panel->spd_product_description;
- dp_write(base + MMSS_DP_GENERIC1_2, ((vendor[0] & 0x7f) |
+ dp_write(catalog, io_data, MMSS_DP_GENERIC1_2, ((vendor[0] & 0x7f) |
((vendor[1] & 0x7f) << 8) |
((vendor[2] & 0x7f) << 16) |
((vendor[3] & 0x7f) << 24)));
- dp_write(base + MMSS_DP_GENERIC1_3, ((vendor[4] & 0x7f) |
+ dp_write(catalog, io_data, MMSS_DP_GENERIC1_3, ((vendor[4] & 0x7f) |
((vendor[5] & 0x7f) << 8) |
((vendor[6] & 0x7f) << 16) |
((vendor[7] & 0x7f) << 24)));
- dp_write(base + MMSS_DP_GENERIC1_4, ((product[0] & 0x7f) |
+ dp_write(catalog, io_data, MMSS_DP_GENERIC1_4, ((product[0] & 0x7f) |
((product[1] & 0x7f) << 8) |
((product[2] & 0x7f) << 16) |
((product[3] & 0x7f) << 24)));
- dp_write(base + MMSS_DP_GENERIC1_5, ((product[4] & 0x7f) |
+ dp_write(catalog, io_data, MMSS_DP_GENERIC1_5, ((product[4] & 0x7f) |
((product[5] & 0x7f) << 8) |
((product[6] & 0x7f) << 16) |
((product[7] & 0x7f) << 24)));
- dp_write(base + MMSS_DP_GENERIC1_6, ((product[8] & 0x7f) |
+ dp_write(catalog, io_data, MMSS_DP_GENERIC1_6, ((product[8] & 0x7f) |
((product[9] & 0x7f) << 8) |
((product[10] & 0x7f) << 16) |
((product[11] & 0x7f) << 24)));
- dp_write(base + MMSS_DP_GENERIC1_7, ((product[12] & 0x7f) |
+ dp_write(catalog, io_data, MMSS_DP_GENERIC1_7, ((product[12] & 0x7f) |
((product[13] & 0x7f) << 8) |
((product[14] & 0x7f) << 16) |
((product[15] & 0x7f) << 24)));
- dp_write(base + MMSS_DP_GENERIC1_8, device_type);
- dp_write(base + MMSS_DP_GENERIC1_9, 0x00);
+ dp_write(catalog, io_data, MMSS_DP_GENERIC1_8, device_type);
+ dp_write(catalog, io_data, MMSS_DP_GENERIC1_9, 0x00);
- spd_cfg = dp_read(base + MMSS_DP_SDP_CFG);
+ spd_cfg = dp_read(catalog, io_data, MMSS_DP_SDP_CFG);
/* GENERIC1_SDP for SPD Infoframe */
spd_cfg |= BIT(18);
- dp_write(base + MMSS_DP_SDP_CFG, spd_cfg);
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG, spd_cfg);
- spd_cfg2 = dp_read(base + MMSS_DP_SDP_CFG2);
+ spd_cfg2 = dp_read(catalog, io_data, MMSS_DP_SDP_CFG2);
/* 28 data bytes for SPD Infoframe with GENERIC1 set */
spd_cfg2 |= BIT(17);
- dp_write(base + MMSS_DP_SDP_CFG2, spd_cfg2);
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG2, spd_cfg2);
- dp_write(base + MMSS_DP_SDP_CFG3, 0x1);
- dp_write(base + MMSS_DP_SDP_CFG3, 0x0);
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG3, 0x1);
+ dp_write(catalog, io_data, MMSS_DP_SDP_CFG3, 0x0);
}
-struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io)
+static void dp_catalog_get_io_buf(struct dp_catalog_private *catalog)
+{
+ struct dp_parser *parser = catalog->parser;
+
+ dp_catalog_fill_io_buf(dp_ahb);
+ dp_catalog_fill_io_buf(dp_aux);
+ dp_catalog_fill_io_buf(dp_link);
+ dp_catalog_fill_io_buf(dp_p0);
+ dp_catalog_fill_io_buf(dp_phy);
+ dp_catalog_fill_io_buf(dp_ln_tx0);
+ dp_catalog_fill_io_buf(dp_ln_tx1);
+ dp_catalog_fill_io_buf(dp_pll);
+ dp_catalog_fill_io_buf(usb3_dp_com);
+ dp_catalog_fill_io_buf(dp_mmss_cc);
+ dp_catalog_fill_io_buf(hdcp_physical);
+}
+
+static void dp_catalog_get_io(struct dp_catalog_private *catalog)
+{
+ struct dp_parser *parser = catalog->parser;
+
+ dp_catalog_fill_io(dp_ahb);
+ dp_catalog_fill_io(dp_aux);
+ dp_catalog_fill_io(dp_link);
+ dp_catalog_fill_io(dp_p0);
+ dp_catalog_fill_io(dp_phy);
+ dp_catalog_fill_io(dp_ln_tx0);
+ dp_catalog_fill_io(dp_ln_tx1);
+ dp_catalog_fill_io(dp_pll);
+ dp_catalog_fill_io(usb3_dp_com);
+ dp_catalog_fill_io(dp_mmss_cc);
+ dp_catalog_fill_io(hdcp_physical);
+}
+
+static void dp_catalog_set_exe_mode(struct dp_catalog *dp_catalog, char *mode)
+{
+ struct dp_catalog_private *catalog;
+
+ if (!dp_catalog) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ catalog = container_of(dp_catalog, struct dp_catalog_private,
+ dp_catalog);
+
+ strlcpy(catalog->exe_mode, mode, sizeof(catalog->exe_mode));
+
+ if (!strcmp(catalog->exe_mode, "hw"))
+ catalog->parser->clear_io_buf(catalog->parser);
+ else
+ dp_catalog_get_io_buf(catalog);
+}
+
+struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser)
{
int rc = 0;
struct dp_catalog *dp_catalog;
@@ -1583,7 +1781,7 @@
.config_spd = dp_catalog_panel_config_spd,
};
- if (!io) {
+ if (!dev || !parser) {
pr_err("invalid input\n");
rc = -EINVAL;
goto error;
@@ -1596,7 +1794,11 @@
}
catalog->dev = dev;
- catalog->io = io;
+ catalog->parser = parser;
+
+ dp_catalog_get_io(catalog);
+
+ strlcpy(catalog->exe_mode, "hw", sizeof(catalog->exe_mode));
dp_catalog = &catalog->dp_catalog;
@@ -1605,6 +1807,9 @@
dp_catalog->audio = audio;
dp_catalog->panel = panel;
+ dp_catalog->set_exe_mode = dp_catalog_set_exe_mode;
+ dp_catalog->get_reg_dump = dp_catalog_reg_dump;
+
return dp_catalog;
error:
return ERR_PTR(rc);
@@ -1620,5 +1825,6 @@
catalog = container_of(dp_catalog, struct dp_catalog_private,
dp_catalog);
+ catalog->parser->clear_io_buf(catalog->parser);
devm_kfree(catalog->dev, catalog);
}
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index d03be6a..743468d 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -181,6 +181,10 @@
struct dp_catalog_ctrl ctrl;
struct dp_catalog_audio audio;
struct dp_catalog_panel panel;
+
+ void (*set_exe_mode)(struct dp_catalog *dp_catalog, char *mode);
+ int (*get_reg_dump)(struct dp_catalog *dp_catalog,
+ char *mode, u8 **out_buf, u32 *out_buf_len);
};
static inline u8 dp_ecc_get_g0_value(u8 data)
@@ -248,7 +252,7 @@
return parity_byte;
}
-struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io);
+struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser);
void dp_catalog_put(struct dp_catalog *catalog);
#endif /* _DP_CATALOG_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 006f723..2e2887e 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -71,6 +71,8 @@
struct completion video_comp;
bool orientation;
+ bool power_on;
+
atomic_t aborted;
u32 pixel_rate;
@@ -128,6 +130,11 @@
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+ if (!ctrl->power_on || atomic_read(&ctrl->aborted)) {
+ pr_err("CTRL off, return\n");
+ return;
+ }
+
reinit_completion(&ctrl->idle_comp);
dp_ctrl_state_ctrl(ctrl, ST_PUSH_IDLE);
@@ -813,6 +820,10 @@
u8 link_status[DP_LINK_STATUS_SIZE];
int const maximum_retries = 5;
+ ctrl->aux->state &= ~DP_STATE_TRAIN_1_FAILED;
+ ctrl->aux->state &= ~DP_STATE_TRAIN_1_SUCCEEDED;
+ ctrl->aux->state |= DP_STATE_TRAIN_1_STARTED;
+
dp_ctrl_state_ctrl(ctrl, 0);
/* Make sure to clear the current pattern before starting a new one */
wmb();
@@ -822,18 +833,18 @@
DP_LINK_SCRAMBLING_DISABLE); /* train_1 */
if (ret <= 0) {
ret = -EINVAL;
- return ret;
+ goto end;
}
ret = dp_ctrl_update_vx_px(ctrl);
if (ret <= 0) {
ret = -EINVAL;
- return ret;
+ goto end;
}
tries = 0;
old_v_level = ctrl->link->phy_params.v_level;
- while (1) {
+ while (!atomic_read(&ctrl->aborted)) {
drm_dp_link_train_clock_recovery_delay(ctrl->panel->dpcd);
ret = dp_ctrl_read_link_status(ctrl, link_status);
@@ -872,6 +883,13 @@
break;
}
}
+end:
+ ctrl->aux->state &= ~DP_STATE_TRAIN_1_STARTED;
+
+ if (ret)
+ ctrl->aux->state |= DP_STATE_TRAIN_1_FAILED;
+ else
+ ctrl->aux->state |= DP_STATE_TRAIN_1_SUCCEEDED;
return ret;
}
@@ -915,6 +933,10 @@
int const maximum_retries = 5;
u8 link_status[DP_LINK_STATUS_SIZE];
+ ctrl->aux->state &= ~DP_STATE_TRAIN_2_FAILED;
+ ctrl->aux->state &= ~DP_STATE_TRAIN_2_SUCCEEDED;
+ ctrl->aux->state |= DP_STATE_TRAIN_2_STARTED;
+
dp_ctrl_state_ctrl(ctrl, 0);
/* Make sure to clear the current pattern before starting a new one */
wmb();
@@ -927,14 +949,14 @@
ret = dp_ctrl_update_vx_px(ctrl);
if (ret <= 0) {
ret = -EINVAL;
- return ret;
+ goto end;
}
ctrl->catalog->set_pattern(ctrl->catalog, pattern);
ret = dp_ctrl_train_pattern_set(ctrl,
pattern | DP_RECOVERED_CLOCK_OUT_EN);
if (ret <= 0) {
ret = -EINVAL;
- return ret;
+ goto end;
}
do {
@@ -960,8 +982,14 @@
ret = -EINVAL;
break;
}
- } while (1);
+ } while (!atomic_read(&ctrl->aborted));
+end:
+ ctrl->aux->state &= ~DP_STATE_TRAIN_2_STARTED;
+ if (ret)
+ ctrl->aux->state |= DP_STATE_TRAIN_2_FAILED;
+ else
+ ctrl->aux->state |= DP_STATE_TRAIN_2_SUCCEEDED;
return ret;
}
@@ -1102,8 +1130,7 @@
return ctrl->power->clk_enable(ctrl->power, DP_CTRL_PM, false);
}
-static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl,
- bool flip, bool multi_func)
+static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset)
{
struct dp_ctrl_private *ctrl;
struct dp_catalog_ctrl *catalog;
@@ -1118,7 +1145,7 @@
ctrl->orientation = flip;
catalog = ctrl->catalog;
- if (!multi_func) {
+ if (reset) {
catalog->usb_reset(ctrl->catalog, flip);
catalog->phy_reset(ctrl->catalog);
}
@@ -1180,6 +1207,15 @@
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+ if (!ctrl->power_on || atomic_read(&ctrl->aborted)) {
+ pr_err("CTRL off, return\n");
+ return -EINVAL;
+ }
+
+ ctrl->aux->state &= ~DP_STATE_LINK_MAINTENANCE_COMPLETED;
+ ctrl->aux->state &= ~DP_STATE_LINK_MAINTENANCE_FAILED;
+ ctrl->aux->state |= DP_STATE_LINK_MAINTENANCE_STARTED;
+
ctrl->dp_ctrl.push_idle(&ctrl->dp_ctrl);
ctrl->dp_ctrl.reset(&ctrl->dp_ctrl);
@@ -1219,6 +1255,13 @@
ret = dp_ctrl_setup_main_link(ctrl, true);
} while (ret == -EAGAIN);
+ ctrl->aux->state &= ~DP_STATE_LINK_MAINTENANCE_STARTED;
+
+ if (ret)
+ ctrl->aux->state |= DP_STATE_LINK_MAINTENANCE_FAILED;
+ else
+ ctrl->aux->state |= DP_STATE_LINK_MAINTENANCE_COMPLETED;
+
return ret;
}
@@ -1341,7 +1384,6 @@
atomic_set(&ctrl->aborted, 0);
rate = ctrl->panel->link_info.rate;
- ctrl->power->clk_enable(ctrl->power, DP_CORE_PM, true);
ctrl->catalog->hpd_config(ctrl->catalog, true);
if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
@@ -1396,6 +1438,7 @@
if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN)
dp_ctrl_send_phy_test_pattern(ctrl);
+ ctrl->power_on = true;
pr_debug("End-\n");
end:
@@ -1419,6 +1462,7 @@
dp_ctrl_disable_mainlink_clocks(ctrl);
+ ctrl->power_on = false;
pr_debug("DP off done\n");
}
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index 229c779..31d8f07 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -23,7 +23,7 @@
#include "dp_catalog.h"
struct dp_ctrl {
- int (*init)(struct dp_ctrl *dp_ctrl, bool flip, bool multi_func);
+ int (*init)(struct dp_ctrl *dp_ctrl, bool flip, bool reset);
void (*deinit)(struct dp_ctrl *dp_ctrl);
int (*on)(struct dp_ctrl *dp_ctrl);
void (*off)(struct dp_ctrl *dp_ctrl);
diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c
index 0b3d903..78bea02 100644
--- a/drivers/gpu/drm/msm/dp/dp_debug.c
+++ b/drivers/gpu/drm/msm/dp/dp_debug.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -16,7 +16,6 @@
#include <linux/debugfs.h>
-#include "dp_parser.h"
#include "dp_power.h"
#include "dp_catalog.h"
#include "dp_aux.h"
@@ -36,15 +35,56 @@
u8 *dpcd;
u32 dpcd_size;
+ int vdo;
+
+ char exe_mode[SZ_32];
+ char reg_dump[SZ_32];
+
struct dp_usbpd *usbpd;
struct dp_link *link;
struct dp_panel *panel;
+ struct dp_aux *aux;
+ struct dp_catalog *catalog;
struct drm_connector **connector;
struct device *dev;
-
+ struct work_struct sim_work;
struct dp_debug dp_debug;
};
+static int dp_debug_get_edid_buf(struct dp_debug_private *debug)
+{
+ int rc = 0;
+
+ if (!debug->edid) {
+ debug->edid = devm_kzalloc(debug->dev, SZ_256, GFP_KERNEL);
+ if (!debug->edid) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ debug->edid_size = SZ_256;
+ }
+end:
+ return rc;
+}
+
+static int dp_debug_get_dpcd_buf(struct dp_debug_private *debug)
+{
+ int rc = 0;
+
+ if (!debug->dpcd) {
+ debug->dpcd = devm_kzalloc(debug->dev, SZ_1K, GFP_KERNEL);
+ if (!debug->dpcd) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ debug->dpcd_size = SZ_1K;
+ }
+end:
+ return rc;
+}
+
static ssize_t dp_debug_write_edid(struct file *file,
const char __user *user_buff, size_t count, loff_t *ppos)
{
@@ -75,7 +115,8 @@
edid_size = size / char_to_nib;
buf_t = buf;
- memset(debug->edid, 0, debug->edid_size);
+ if (dp_debug_get_edid_buf(debug))
+ goto bail;
if (edid_size != debug->edid_size) {
pr_debug("clearing debug edid\n");
@@ -100,13 +141,13 @@
buf_t += char_to_nib;
}
- print_hex_dump(KERN_DEBUG, "DEBUG EDID: ", DUMP_PREFIX_NONE,
- 16, 1, debug->edid, debug->edid_size, false);
-
edid = debug->edid;
bail:
kfree(buf);
- debug->panel->set_edid(debug->panel, edid);
+
+ if (!debug->dp_debug.sim_mode)
+ debug->panel->set_edid(debug->panel, edid);
+
return rc;
}
@@ -119,8 +160,8 @@
size_t dpcd_size = 0;
size_t size = 0, dpcd_buf_index = 0;
ssize_t rc = count;
-
- pr_debug("count=%zu\n", count);
+ char offset_ch[5];
+ u32 offset;
if (!debug)
return -ENODEV;
@@ -128,7 +169,7 @@
if (*ppos)
goto bail;
- size = min_t(size_t, count, SZ_32);
+ size = min_t(size_t, count, SZ_2K);
buf = kzalloc(size, GFP_KERNEL);
if (!buf) {
@@ -139,16 +180,30 @@
if (copy_from_user(buf, user_buff, size))
goto bail;
- dpcd_size = size / char_to_nib;
- buf_t = buf;
+ memcpy(offset_ch, buf, 4);
+ offset_ch[4] = '\0';
- memset(debug->dpcd, 0, debug->dpcd_size);
-
- if (dpcd_size != debug->dpcd_size) {
- pr_debug("clearing debug dpcd\n");
+ if (kstrtoint(offset_ch, 16, &offset)) {
+ pr_err("offset kstrtoint error\n");
goto bail;
}
+ if (dp_debug_get_dpcd_buf(debug))
+ goto bail;
+
+ if (offset == 0xFFFF) {
+ pr_err("clearing dpcd\n");
+ memset(debug->dpcd, 0, debug->dpcd_size);
+ goto bail;
+ }
+
+ size -= 4;
+
+ dpcd_size = size / char_to_nib;
+ buf_t = buf + 4;
+
+ dpcd_buf_index = offset;
+
while (dpcd_size--) {
char t[3];
int d;
@@ -167,16 +222,39 @@
buf_t += char_to_nib;
}
- print_hex_dump(KERN_DEBUG, "DEBUG DPCD: ", DUMP_PREFIX_NONE,
- 8, 1, debug->dpcd, debug->dpcd_size, false);
-
dpcd = debug->dpcd;
bail:
kfree(buf);
- debug->panel->set_dpcd(debug->panel, dpcd);
+ if (debug->dp_debug.sim_mode)
+ debug->aux->dpcd_updated(debug->aux);
+ else
+ debug->panel->set_dpcd(debug->panel, dpcd);
+
return rc;
}
+static ssize_t dp_debug_read_dpcd(struct file *file,
+ char __user *user_buff, size_t count, loff_t *ppos)
+{
+ struct dp_debug_private *debug = file->private_data;
+ char buf[SZ_8];
+ u32 len = 0;
+
+ if (!debug)
+ return -ENODEV;
+
+ if (*ppos)
+ return 0;
+
+ len += snprintf(buf, SZ_8, "0x%x\n", debug->aux->reg);
+
+ if (copy_to_user(user_buff, buf, len))
+ return -EFAULT;
+
+ *ppos += len;
+ return len;
+}
+
static ssize_t dp_debug_write_hpd(struct file *file,
const char __user *user_buff, size_t count, loff_t *ppos)
{
@@ -323,6 +401,36 @@
return len;
}
+static ssize_t dp_debug_write_exe_mode(struct file *file,
+ const char __user *user_buff, size_t count, loff_t *ppos)
+{
+ struct dp_debug_private *debug = file->private_data;
+ char *buf;
+ size_t len = 0;
+
+ if (!debug)
+ return -ENODEV;
+
+ if (*ppos)
+ return 0;
+
+ len = min_t(size_t, count, SZ_32 - 1);
+ buf = memdup_user(user_buff, len);
+ buf[len] = '\0';
+
+ if (sscanf(buf, "%3s", debug->exe_mode) != 1)
+ goto end;
+
+ if (strcmp(debug->exe_mode, "hw") &&
+ strcmp(debug->exe_mode, "sw") &&
+ strcmp(debug->exe_mode, "all"))
+ goto end;
+
+ debug->catalog->set_exe_mode(debug->catalog, debug->exe_mode);
+end:
+ return len;
+}
+
static ssize_t dp_debug_read_connected(struct file *file,
char __user *user_buff, size_t count, loff_t *ppos)
{
@@ -421,7 +529,6 @@
struct dp_debug_private *debug = file->private_data;
char *buf;
u32 len = 0, rc = 0;
- u64 lclk = 0;
u32 max_size = SZ_4K;
if (!debug)
@@ -434,124 +541,60 @@
if (!buf)
return -ENOMEM;
- rc = snprintf(buf + len, max_size, "\tname = %s\n", DEBUG_NAME);
+ rc = snprintf(buf + len, max_size, "\tstate=0x%x\n", debug->aux->state);
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
goto error;
- rc = snprintf(buf + len, max_size,
- "\tdp_panel\n\t\tmax_pclk_khz = %d\n",
- debug->panel->max_pclk_khz);
- if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
- goto error;
-
- rc = snprintf(buf + len, max_size,
- "\tdrm_dp_link\n\t\trate = %u\n",
+ rc = snprintf(buf + len, max_size, "\tlink_rate=%u\n",
debug->panel->link_info.rate);
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
goto error;
- rc = snprintf(buf + len, max_size,
- "\t\tnum_lanes = %u\n",
+ rc = snprintf(buf + len, max_size, "\tnum_lanes=%u\n",
debug->panel->link_info.num_lanes);
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
goto error;
- rc = snprintf(buf + len, max_size,
- "\t\tcapabilities = %lu\n",
- debug->panel->link_info.capabilities);
- if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
- goto error;
-
- rc = snprintf(buf + len, max_size,
- "\tdp_panel_info:\n\t\tactive = %dx%d\n",
+ rc = snprintf(buf + len, max_size, "\tresolution=%dx%d@%dHz\n",
debug->panel->pinfo.h_active,
- debug->panel->pinfo.v_active);
- if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
- goto error;
-
- rc = snprintf(buf + len, max_size,
- "\t\tback_porch = %dx%d\n",
- debug->panel->pinfo.h_back_porch,
- debug->panel->pinfo.v_back_porch);
- if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
- goto error;
-
- rc = snprintf(buf + len, max_size,
- "\t\tfront_porch = %dx%d\n",
- debug->panel->pinfo.h_front_porch,
- debug->panel->pinfo.v_front_porch);
- if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
- goto error;
-
- rc = snprintf(buf + len, max_size,
- "\t\tsync_width = %dx%d\n",
- debug->panel->pinfo.h_sync_width,
- debug->panel->pinfo.v_sync_width);
- if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
- goto error;
-
- rc = snprintf(buf + len, max_size,
- "\t\tactive_low = %dx%d\n",
- debug->panel->pinfo.h_active_low,
- debug->panel->pinfo.v_active_low);
- if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
- goto error;
-
- rc = snprintf(buf + len, max_size,
- "\t\th_skew = %d\n",
- debug->panel->pinfo.h_skew);
- if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
- goto error;
-
- rc = snprintf(buf + len, max_size,
- "\t\trefresh rate = %d\n",
+ debug->panel->pinfo.v_active,
debug->panel->pinfo.refresh_rate);
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
goto error;
- rc = snprintf(buf + len, max_size,
- "\t\tpixel clock khz = %d\n",
+ rc = snprintf(buf + len, max_size, "\tpclock=%dKHz\n",
debug->panel->pinfo.pixel_clk_khz);
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
goto error;
- rc = snprintf(buf + len, max_size,
- "\t\tbpp = %d\n",
+ rc = snprintf(buf + len, max_size, "\tbpp=%d\n",
debug->panel->pinfo.bpp);
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
goto error;
/* Link Information */
- rc = snprintf(buf + len, max_size,
- "\tdp_link:\n\t\ttest_requested = %d\n",
- debug->link->sink_request);
+ rc = snprintf(buf + len, max_size, "\ttest_req=%s\n",
+ dp_link_get_test_name(debug->link->sink_request));
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
goto error;
rc = snprintf(buf + len, max_size,
- "\t\tlane_count = %d\n", debug->link->link_params.lane_count);
+ "\tlane_count=%d\n", debug->link->link_params.lane_count);
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
goto error;
rc = snprintf(buf + len, max_size,
- "\t\tbw_code = %d\n", debug->link->link_params.bw_code);
- if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
- goto error;
-
- lclk = drm_dp_bw_code_to_link_rate(
- debug->link->link_params.bw_code) * 1000;
- rc = snprintf(buf + len, max_size,
- "\t\tlclk = %lld\n", lclk);
+ "\tbw_code=%d\n", debug->link->link_params.bw_code);
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
goto error;
rc = snprintf(buf + len, max_size,
- "\t\tv_level = %d\n", debug->link->phy_params.v_level);
+ "\tv_level=%d\n", debug->link->phy_params.v_level);
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
goto error;
rc = snprintf(buf + len, max_size,
- "\t\tp_level = %d\n", debug->link->phy_params.p_level);
+ "\tp_level=%d\n", debug->link->phy_params.p_level);
if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
goto error;
@@ -665,6 +708,8 @@
pr_err("invalid input\n");
len = -EINVAL;
}
+
+ debug->panel->setup_hdr(debug->panel, &c_state->hdr_meta);
end:
return len;
}
@@ -814,6 +859,155 @@
return rc;
}
+static ssize_t dp_debug_write_sim(struct file *file,
+ const char __user *user_buff, size_t count, loff_t *ppos)
+{
+ struct dp_debug_private *debug = file->private_data;
+ char buf[SZ_8];
+ size_t len = 0;
+ int sim;
+
+ if (!debug)
+ return -ENODEV;
+
+ if (*ppos)
+ return 0;
+
+ /* Leave room for termination char */
+ len = min_t(size_t, count, SZ_8 - 1);
+ if (copy_from_user(buf, user_buff, len))
+ goto end;
+
+ buf[len] = '\0';
+
+ if (kstrtoint(buf, 10, &sim) != 0)
+ goto end;
+
+ if (sim) {
+ if (dp_debug_get_edid_buf(debug))
+ goto end;
+
+ if (dp_debug_get_dpcd_buf(debug))
+ goto error;
+ } else {
+ if (debug->edid) {
+ devm_kfree(debug->dev, debug->edid);
+ debug->edid = NULL;
+ }
+
+ if (debug->dpcd) {
+ devm_kfree(debug->dev, debug->dpcd);
+ debug->dpcd = NULL;
+ }
+ }
+
+ debug->dp_debug.sim_mode = !!sim;
+
+ debug->aux->set_sim_mode(debug->aux, debug->dp_debug.sim_mode,
+ debug->edid, debug->dpcd);
+end:
+ return len;
+error:
+ devm_kfree(debug->dev, debug->edid);
+ return len;
+}
+
+static ssize_t dp_debug_write_attention(struct file *file,
+ const char __user *user_buff, size_t count, loff_t *ppos)
+{
+ struct dp_debug_private *debug = file->private_data;
+ char buf[SZ_8];
+ size_t len = 0;
+ int vdo;
+
+ if (!debug)
+ return -ENODEV;
+
+ if (*ppos)
+ return 0;
+
+ /* Leave room for termination char */
+ len = min_t(size_t, count, SZ_8 - 1);
+ if (copy_from_user(buf, user_buff, len))
+ goto end;
+
+ buf[len] = '\0';
+
+ if (kstrtoint(buf, 10, &vdo) != 0)
+ goto end;
+
+ debug->vdo = vdo;
+
+ schedule_work(&debug->sim_work);
+end:
+ return len;
+}
+
+static ssize_t dp_debug_write_dump(struct file *file,
+ const char __user *user_buff, size_t count, loff_t *ppos)
+{
+ struct dp_debug_private *debug = file->private_data;
+ char buf[SZ_32];
+ size_t len = 0;
+
+ if (!debug)
+ return -ENODEV;
+
+ if (*ppos)
+ return 0;
+
+ /* Leave room for termination char */
+ len = min_t(size_t, count, SZ_32 - 1);
+ if (copy_from_user(buf, user_buff, len))
+ goto end;
+
+ buf[len] = '\0';
+
+ if (sscanf(buf, "%31s", debug->reg_dump) != 1)
+ goto end;
+
+ /* qfprom register dump not supported */
+ if (!strcmp(debug->reg_dump, "qfprom_physical"))
+ strlcpy(debug->reg_dump, "clear", sizeof(debug->reg_dump));
+end:
+ return len;
+}
+
+static ssize_t dp_debug_read_dump(struct file *file,
+ char __user *user_buff, size_t count, loff_t *ppos)
+{
+ int rc = 0;
+ struct dp_debug_private *debug = file->private_data;
+ u8 *buf = NULL;
+ u32 len = 0;
+ char prefix[SZ_32];
+
+ if (!debug)
+ return -ENODEV;
+
+ if (*ppos)
+ return 0;
+
+ if (!debug->usbpd->hpd_high || !strlen(debug->reg_dump))
+ goto end;
+
+ rc = debug->catalog->get_reg_dump(debug->catalog,
+ debug->reg_dump, &buf, &len);
+ if (rc)
+ goto end;
+
+ snprintf(prefix, sizeof(prefix), "%s: ", debug->reg_dump);
+ print_hex_dump(KERN_DEBUG, prefix, DUMP_PREFIX_NONE,
+ 16, 4, buf, len, false);
+
+ if (copy_to_user(user_buff, buf, len))
+ return -EFAULT;
+
+ *ppos += len;
+end:
+ return len;
+}
+
static const struct file_operations dp_debug_fops = {
.open = simple_open,
.read = dp_debug_read_info,
@@ -838,6 +1032,7 @@
static const struct file_operations dpcd_fops = {
.open = simple_open,
.write = dp_debug_write_dpcd,
+ .read = dp_debug_read_dpcd,
};
static const struct file_operations connected_fops = {
@@ -850,6 +1045,10 @@
.read = dp_debug_bw_code_read,
.write = dp_debug_bw_code_write,
};
+static const struct file_operations exe_mode_fops = {
+ .open = simple_open,
+ .write = dp_debug_write_exe_mode,
+};
static const struct file_operations tpg_fops = {
.open = simple_open,
@@ -863,6 +1062,22 @@
.read = dp_debug_read_hdr,
};
+static const struct file_operations sim_fops = {
+ .open = simple_open,
+ .write = dp_debug_write_sim,
+};
+
+static const struct file_operations attention_fops = {
+ .open = simple_open,
+ .write = dp_debug_write_attention,
+};
+
+static const struct file_operations dump_fops = {
+ .open = simple_open,
+ .write = dp_debug_write_dump,
+ .read = dp_debug_read_dump,
+};
+
static int dp_debug_init(struct dp_debug *dp_debug)
{
int rc = 0;
@@ -925,7 +1140,14 @@
rc = PTR_ERR(file);
pr_err("[%s] debugfs max_bw_code failed, rc=%d\n",
DEBUG_NAME, rc);
- goto error_remove_dir;
+ }
+
+ file = debugfs_create_file("exe_mode", 0644, dir,
+ debug, &exe_mode_fops);
+ if (IS_ERR_OR_NULL(file)) {
+ rc = PTR_ERR(file);
+ pr_err("[%s] debugfs register failed, rc=%d\n",
+ DEBUG_NAME, rc);
}
file = debugfs_create_file("edid", 0644, dir,
@@ -965,6 +1187,36 @@
goto error_remove_dir;
}
+ file = debugfs_create_file("sim", 0644, dir,
+ debug, &sim_fops);
+
+ if (IS_ERR_OR_NULL(file)) {
+ rc = PTR_ERR(file);
+ pr_err("[%s] debugfs sim failed, rc=%d\n",
+ DEBUG_NAME, rc);
+ goto error_remove_dir;
+ }
+
+ file = debugfs_create_file("attention", 0644, dir,
+ debug, &attention_fops);
+
+ if (IS_ERR_OR_NULL(file)) {
+ rc = PTR_ERR(file);
+ pr_err("[%s] debugfs attention failed, rc=%d\n",
+ DEBUG_NAME, rc);
+ goto error_remove_dir;
+ }
+
+ file = debugfs_create_file("dump", 0644, dir,
+ debug, &dump_fops);
+
+ if (IS_ERR_OR_NULL(file)) {
+ rc = PTR_ERR(file);
+ pr_err("[%s] debugfs dump failed, rc=%d\n",
+ DEBUG_NAME, rc);
+ goto error_remove_dir;
+ }
+
return 0;
error_remove_dir:
@@ -975,15 +1227,24 @@
return rc;
}
+static void dp_debug_sim_work(struct work_struct *work)
+{
+ struct dp_debug_private *debug =
+ container_of(work, typeof(*debug), sim_work);
+
+ debug->usbpd->simulate_attention(debug->usbpd, debug->vdo);
+}
+
struct dp_debug *dp_debug_get(struct device *dev, struct dp_panel *panel,
struct dp_usbpd *usbpd, struct dp_link *link,
- struct drm_connector **connector)
+ struct dp_aux *aux, struct drm_connector **connector,
+ struct dp_catalog *catalog)
{
int rc = 0;
struct dp_debug_private *debug;
struct dp_debug *dp_debug;
- if (!dev || !panel || !usbpd || !link) {
+ if (!dev || !panel || !usbpd || !link || !catalog) {
pr_err("invalid input\n");
rc = -EINVAL;
goto error;
@@ -995,30 +1256,16 @@
goto error;
}
- debug->edid = devm_kzalloc(dev, SZ_256, GFP_KERNEL);
- if (!debug->edid) {
- rc = -ENOMEM;
- kfree(debug);
- goto error;
- }
-
- debug->edid_size = SZ_256;
-
- debug->dpcd = devm_kzalloc(dev, SZ_16, GFP_KERNEL);
- if (!debug->dpcd) {
- rc = -ENOMEM;
- kfree(debug);
- goto error;
- }
-
- debug->dpcd_size = SZ_16;
+ INIT_WORK(&debug->sim_work, dp_debug_sim_work);
debug->dp_debug.debug_en = false;
debug->usbpd = usbpd;
debug->link = link;
debug->panel = panel;
+ debug->aux = aux;
debug->dev = dev;
debug->connector = connector;
+ debug->catalog = catalog;
dp_debug = &debug->dp_debug;
dp_debug->vdisplay = 0;
@@ -1061,7 +1308,11 @@
dp_debug_deinit(dp_debug);
- devm_kfree(debug->dev, debug->edid);
- devm_kfree(debug->dev, debug->dpcd);
+ if (debug->edid)
+ devm_kfree(debug->dev, debug->edid);
+
+ if (debug->dpcd)
+ devm_kfree(debug->dev, debug->dpcd);
+
devm_kfree(debug->dev, debug);
}
diff --git a/drivers/gpu/drm/msm/dp/dp_debug.h b/drivers/gpu/drm/msm/dp/dp_debug.h
index 3b2d23e..2643f70 100644
--- a/drivers/gpu/drm/msm/dp/dp_debug.h
+++ b/drivers/gpu/drm/msm/dp/dp_debug.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,6 +18,7 @@
#include "dp_panel.h"
#include "dp_link.h"
#include "dp_usbpd.h"
+#include "dp_aux.h"
/**
* struct dp_debug
@@ -29,6 +30,7 @@
*/
struct dp_debug {
bool debug_en;
+ bool sim_mode;
bool psm_enabled;
int aspect_ratio;
int vdisplay;
@@ -45,6 +47,7 @@
* @usbpd: instance of usbpd module
* @link: instance of link module
* @connector: double pointer to display connector
+ * @catalog: instance of catalog module
* return: pointer to allocated debug module data
*
* This function sets up the debug module and provides a way
@@ -52,7 +55,8 @@
*/
struct dp_debug *dp_debug_get(struct device *dev, struct dp_panel *panel,
struct dp_usbpd *usbpd, struct dp_link *link,
- struct drm_connector **connector);
+ struct dp_aux *aux, struct drm_connector **connector,
+ struct dp_catalog *catalog);
/**
* dp_debug_put()
*
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index f2c0a0e..b1d03d6 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -22,6 +22,8 @@
#include <linux/of_irq.h>
#include <linux/hdcp_qseecom.h>
+#include "sde_connector.h"
+
#include "msm_drv.h"
#include "dp_usbpd.h"
#include "dp_parser.h"
@@ -87,11 +89,12 @@
struct workqueue_struct *wq;
struct delayed_work hdcp_cb_work;
- struct work_struct connect_work;
+ struct delayed_work connect_work;
struct work_struct attention_work;
struct mutex hdcp_mutex;
struct mutex session_lock;
int hdcp_status;
+ unsigned long audio_status;
};
static const struct of_device_id dp_dt_match[] = {
@@ -99,6 +102,11 @@
{}
};
+static bool dp_display_framework_ready(struct dp_display_private *dp)
+{
+ return dp->dp_display.post_open ? false : true;
+}
+
static inline bool dp_display_is_hdcp_enabled(struct dp_display_private *dp)
{
return dp->hdcp.feature_enabled &&
@@ -266,6 +274,7 @@
static int dp_display_initialize_hdcp(struct dp_display_private *dp)
{
struct sde_hdcp_init_data hdcp_init_data;
+ struct dp_parser *parser;
int rc = 0;
if (!dp) {
@@ -273,6 +282,8 @@
return -EINVAL;
}
+ parser = dp->parser;
+
mutex_init(&dp->hdcp_mutex);
hdcp_init_data.client_id = HDCP_CLIENT_DP;
@@ -282,13 +293,14 @@
hdcp_init_data.mutex = &dp->hdcp_mutex;
hdcp_init_data.sec_access = true;
hdcp_init_data.notify_status = dp_display_notify_hdcp_status_cb;
- hdcp_init_data.core_io = &dp->parser->io.ctrl_io;
- hdcp_init_data.dp_ahb = &dp->parser->io.dp_ahb;
- hdcp_init_data.dp_aux = &dp->parser->io.dp_aux;
- hdcp_init_data.dp_link = &dp->parser->io.dp_link;
- hdcp_init_data.dp_p0 = &dp->parser->io.dp_p0;
- hdcp_init_data.qfprom_io = &dp->parser->io.qfprom_io;
- hdcp_init_data.hdcp_io = &dp->parser->io.hdcp_io;
+ hdcp_init_data.dp_ahb = &parser->get_io(parser, "dp_ahb")->io;
+ hdcp_init_data.dp_aux = &parser->get_io(parser, "dp_aux")->io;
+ hdcp_init_data.dp_link = &parser->get_io(parser, "dp_link")->io;
+ hdcp_init_data.dp_p0 = &parser->get_io(parser, "dp_p0")->io;
+ hdcp_init_data.qfprom_io = &parser->get_io(parser,
+ "qfprom_physical")->io;
+ hdcp_init_data.hdcp_io = &parser->get_io(parser,
+ "hdcp_physical")->io;
hdcp_init_data.revision = &dp->panel->link_info.revision;
dp->hdcp.hdcp1 = sde_hdcp_1x_init(&hdcp_init_data);
@@ -448,35 +460,39 @@
}
/* if cable is already connected, send notification */
- if (dp_display->is_connected)
- dp_display_send_hpd_event(dp);
+ if (dp->usbpd->hpd_high)
+ queue_delayed_work(dp->wq, &dp->connect_work, HZ * 10);
else
dp_display->post_open = NULL;
-
}
static int dp_display_send_hpd_notification(struct dp_display_private *dp,
bool hpd)
{
+ u32 timeout_sec;
+ int ret = 0;
+
dp->dp_display.is_connected = hpd;
- /* in case, framework is not yet up, don't notify hpd */
- if (dp->dp_display.post_open)
- return 0;
+ if (dp_display_framework_ready(dp))
+ timeout_sec = 5;
+ else
+ timeout_sec = 10;
+
+ dp->aux->state |= DP_STATE_NOTIFICATION_SENT;
reinit_completion(&dp->notification_comp);
dp_display_send_hpd_event(dp);
- if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 5)) {
+ if (!wait_for_completion_timeout(&dp->notification_comp,
+ HZ * timeout_sec)) {
pr_warn("%s timeout\n", hpd ? "connect" : "disconnect");
- /* cancel any pending request */
- dp->ctrl->abort(dp->ctrl);
- dp->aux->abort(dp->aux);
-
- return -EINVAL;
+ ret = -EINVAL;
}
- return 0;
+ dp->aux->state &= ~DP_STATE_NOTIFICATION_SENT;
+
+ return ret;
}
static int dp_display_process_hpd_high(struct dp_display_private *dp)
@@ -497,26 +513,21 @@
rc = dp->panel->read_sink_caps(dp->panel,
dp->dp_display.connector, dp->usbpd->multi_func);
if (rc) {
- if (rc == -ETIMEDOUT) {
- pr_err("Sink cap read failed, skip notification\n");
+ /*
+ * ETIMEDOUT --> cable may have been removed
+ * ENOTCONN --> no downstream device connected
+ */
+ if (rc == -ETIMEDOUT || rc == -ENOTCONN)
goto end;
- } else {
+ else
goto notify;
- }
- }
-
- dp->link->process_request(dp->link);
-
- if (dp_display_is_sink_count_zero(dp)) {
- pr_debug("no downstream devices connected\n");
- rc = -EINVAL;
- goto end;
}
edid = dp->panel->edid_ctrl->edid;
dp->audio_supported = drm_detect_monitor_audio(edid);
+ dp->link->process_request(dp->link);
dp->panel->handle_sink_request(dp->panel);
dp->dp_display.max_pclk_khz = dp->parser->max_pclk_khz;
@@ -530,6 +541,7 @@
static void dp_display_host_init(struct dp_display_private *dp)
{
bool flip = false;
+ bool reset;
if (dp->core_initialized) {
pr_debug("DP core already initialized\n");
@@ -539,8 +551,10 @@
if (dp->usbpd->orientation == ORIENTATION_CC2)
flip = true;
+ reset = dp->debug->sim_mode ? false : !dp->usbpd->multi_func;
+
dp->power->init(dp->power, flip);
- dp->ctrl->init(dp->ctrl, flip, dp->usbpd->multi_func);
+ dp->ctrl->init(dp->ctrl, flip, reset);
enable_irq(dp->irq);
dp->core_initialized = true;
}
@@ -556,6 +570,7 @@
dp->power->deinit(dp->power);
disable_irq(dp->irq);
dp->core_initialized = false;
+ dp->aux->state = 0;
}
static int dp_display_process_hpd_low(struct dp_display_private *dp)
@@ -573,9 +588,9 @@
if (dp->audio_supported)
dp->audio->off(dp->audio);
- rc = dp_display_send_hpd_notification(dp, false);
+ dp->audio_status = -ENODEV;
- dp->aux->deinit(dp->aux);
+ rc = dp_display_send_hpd_notification(dp, false);
dp->panel->video_test = false;
@@ -602,8 +617,9 @@
dp_display_host_init(dp);
- if (dp->usbpd->hpd_high)
- queue_work(dp->wq, &dp->connect_work);
+ /* check for hpd high and framework ready */
+ if (dp->usbpd->hpd_high && dp_display_framework_ready(dp))
+ queue_delayed_work(dp->wq, &dp->connect_work, 0);
end:
return rc;
}
@@ -620,6 +636,8 @@
dp->ctrl->push_idle(dp->ctrl);
dp->ctrl->off(dp->ctrl);
+ dp->panel->deinit(dp->panel);
+ dp->aux->deinit(dp->aux);
dp->power_on = false;
}
@@ -659,6 +677,13 @@
goto end;
}
+ /*
+ * In case cable/dongle is disconnected during adb shell stop,
+ * reset psm_enabled flag to false since it is no more needed
+ */
+ if (dp->dp_display.post_open)
+ dp->debug->psm_enabled = false;
+
if (dp->debug->psm_enabled)
dp->link->psm_config(dp->link, &dp->panel->link_info, true);
@@ -667,6 +692,7 @@
dp->aux->abort(dp->aux);
/* wait for idle state */
+ cancel_delayed_work(&dp->connect_work);
flush_workqueue(dp->wq);
dp_display_handle_disconnect(dp);
@@ -678,13 +704,13 @@
{
mutex_lock(&dp->audio->ops_lock);
- if (dp->audio_supported)
+ if (dp->audio_supported && !IS_ERR_VALUE(dp->audio_status))
dp->audio->off(dp->audio);
dp->ctrl->link_maintenance(dp->ctrl);
- if (dp->audio_supported)
- dp->audio->on(dp->audio);
+ if (dp->audio_supported && !IS_ERR_VALUE(dp->audio_status))
+ dp->audio_status = dp->audio->on(dp->audio);
mutex_unlock(&dp->audio->ops_lock);
}
@@ -707,7 +733,7 @@
return;
}
- queue_work(dp->wq, &dp->connect_work);
+ queue_delayed_work(dp->wq, &dp->connect_work, 0);
return;
}
@@ -753,17 +779,19 @@
return -ENODEV;
}
- if (dp->usbpd->hpd_irq && dp->usbpd->hpd_high) {
+ if (dp->usbpd->hpd_irq && dp->usbpd->hpd_high &&
+ dp->power_on) {
dp->link->process_request(dp->link);
queue_work(dp->wq, &dp->attention_work);
} else if (dp->usbpd->hpd_high) {
- queue_work(dp->wq, &dp->connect_work);
+ queue_delayed_work(dp->wq, &dp->connect_work, 0);
} else {
/* cancel any pending request */
dp->ctrl->abort(dp->ctrl);
dp->aux->abort(dp->aux);
/* wait for idle state */
+ cancel_delayed_work(&dp->connect_work);
flush_workqueue(dp->wq);
dp_display_handle_disconnect(dp);
@@ -774,7 +802,8 @@
static void dp_display_connect_work(struct work_struct *work)
{
- struct dp_display_private *dp = container_of(work,
+ struct delayed_work *dw = to_delayed_work(work);
+ struct dp_display_private *dp = container_of(dw,
struct dp_display_private, connect_work);
if (dp->dp_display.is_connected) {
@@ -828,7 +857,7 @@
goto error_catalog;
}
- dp->catalog = dp_catalog_get(dev, &dp->parser->io);
+ dp->catalog = dp_catalog_get(dev, dp->parser);
if (IS_ERR(dp->catalog)) {
rc = PTR_ERR(dp->catalog);
pr_err("failed to initialize catalog, rc = %d\n", rc);
@@ -920,7 +949,8 @@
}
dp->debug = dp_debug_get(dev, dp->panel, dp->usbpd,
- dp->link, &dp->dp_display.connector);
+ dp->link, dp->aux, &dp->dp_display.connector,
+ dp->catalog);
if (IS_ERR(dp->debug)) {
rc = PTR_ERR(dp->debug);
pr_err("failed to initialize debug, rc = %d\n", rc);
@@ -1070,7 +1100,7 @@
if (dp->audio_supported) {
dp->audio->bw_code = dp->link->link_params.bw_code;
dp->audio->lane_count = dp->link->link_params.lane_count;
- dp->audio->on(dp->audio);
+ dp->audio_status = dp->audio->on(dp->audio);
}
dp_display_update_hdcp_info(dp);
@@ -1081,9 +1111,12 @@
dp->hdcp_status = HDCP_STATE_AUTHENTICATING;
queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ / 2);
}
+
+ dp->panel->setup_hdr(dp->panel, NULL);
end:
/* clear framework event notifier */
dp_display->post_open = NULL;
+ dp->aux->state |= DP_STATE_CTRL_POWERED_ON;
complete_all(&dp->notification_comp);
mutex_unlock(&dp->session_lock);
@@ -1116,6 +1149,14 @@
dp->hdcp.ops->off(dp->hdcp.data);
}
+ if (dp->usbpd->hpd_high && dp->usbpd->alt_mode_cfg_done) {
+ if (dp->audio_supported)
+ dp->audio->off(dp->audio);
+
+ dp->link->psm_config(dp->link, &dp->panel->link_info, true);
+ dp->debug->psm_enabled = true;
+ }
+
dp->ctrl->push_idle(dp->ctrl);
end:
mutex_unlock(&dp->session_lock);
@@ -1125,6 +1166,8 @@
static int dp_display_disable(struct dp_display *dp_display)
{
struct dp_display_private *dp;
+ struct drm_connector *connector;
+ struct sde_connector_state *c_state;
if (!dp_display) {
pr_err("invalid input\n");
@@ -1132,6 +1175,8 @@
}
dp = container_of(dp_display, struct dp_display_private, dp_display);
+ connector = dp->dp_display.connector;
+ c_state = to_sde_connector_state(connector->state);
mutex_lock(&dp->session_lock);
@@ -1142,9 +1187,27 @@
dp->ctrl->off(dp->ctrl);
dp->panel->deinit(dp->panel);
+ dp->aux->deinit(dp->aux);
+ connector->hdr_eotf = 0;
+ connector->hdr_metadata_type_one = 0;
+ connector->hdr_max_luminance = 0;
+ connector->hdr_avg_luminance = 0;
+ connector->hdr_min_luminance = 0;
+
+ memset(&c_state->hdr_meta, 0, sizeof(c_state->hdr_meta));
+
+ /*
+ * In case of framework reboot, the DP off sequence is executed without
+ * any notification from driver. Initialize post_open callback to notify
+ * DP connection once framework restarts.
+ */
+ if (dp->usbpd->hpd_high && dp->usbpd->alt_mode_cfg_done) {
+ dp_display->post_open = dp_display_post_open;
+ dp->dp_display.is_connected = false;
+ }
dp->power_on = false;
-
+ dp->aux->state = DP_STATE_CTRL_POWERED_OFF;
end:
complete_all(&dp->notification_comp);
mutex_unlock(&dp->session_lock);
@@ -1252,8 +1315,7 @@
return ret;
}
-
-static int dp_display_pre_kickoff(struct dp_display *dp_display,
+static int dp_display_config_hdr(struct dp_display *dp_display,
struct drm_msm_ext_hdr_metadata *hdr)
{
int rc = 0;
@@ -1266,8 +1328,7 @@
dp = container_of(dp_display, struct dp_display_private, dp_display);
- if (hdr->hdr_supported && dp->panel->hdr_supported(dp->panel))
- rc = dp->panel->setup_hdr(dp->panel, hdr);
+ rc = dp->panel->setup_hdr(dp->panel, hdr);
return rc;
}
@@ -1281,7 +1342,7 @@
}
INIT_DELAYED_WORK(&dp->hdcp_cb_work, dp_display_hdcp_cb_work);
- INIT_WORK(&dp->connect_work, dp_display_connect_work);
+ INIT_DELAYED_WORK(&dp->connect_work, dp_display_connect_work);
INIT_WORK(&dp->attention_work, dp_display_attention_work);
return 0;
@@ -1308,6 +1369,7 @@
dp->pdev = pdev;
dp->name = "drm_dp";
+ dp->audio_status = -ENODEV;
rc = dp_display_create_workqueue(dp);
if (rc) {
@@ -1332,7 +1394,7 @@
g_dp_display->get_debug = dp_get_debug;
g_dp_display->post_open = dp_display_post_open;
g_dp_display->post_init = dp_display_post_init;
- g_dp_display->pre_kickoff = dp_display_pre_kickoff;
+ g_dp_display->config_hdr = dp_display_config_hdr;
rc = component_add(&pdev->dev, &dp_display_comp_ops);
if (rc) {
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index c55e6c8..266de5f 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -43,7 +43,7 @@
int (*request_irq)(struct dp_display *dp_display);
struct dp_debug *(*get_debug)(struct dp_display *dp_display);
void (*post_open)(struct dp_display *dp_display);
- int (*pre_kickoff)(struct dp_display *dp_display,
+ int (*config_hdr)(struct dp_display *dp_display,
struct drm_msm_ext_hdr_metadata *hdr_meta);
void (*post_init)(struct dp_display *dp_display);
};
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index 7746b8e..b834230 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -276,18 +276,17 @@
.mode_set = dp_bridge_mode_set,
};
-int dp_connector_pre_kickoff(struct drm_connector *connector,
- void *display,
- struct msm_display_kickoff_params *params)
+int dp_connector_config_hdr(void *display,
+ struct sde_connector_state *c_state)
{
struct dp_display *dp = display;
- if (!connector || !display || !params) {
+ if (!display || !c_state) {
pr_err("invalid params\n");
return -EINVAL;
}
- return dp->pre_kickoff(dp, params->hdr_meta);
+ return dp->config_hdr(dp, &c_state->hdr_meta);
}
int dp_connector_post_init(struct drm_connector *connector, void *display)
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h
index 89b0a7e..3ca10c2 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_drm.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -32,15 +32,13 @@
};
/**
- * dp_connector_pre_kickoff - callback to perform pre kickoff initialization
- * @connector: Pointer to drm connector structure
+ * dp_connector_config_hdr - callback to configure HDR
* @display: Pointer to private display handle
- * @params: Pointer to kickoff parameters
+ * @c_state: connect state data
* Returns: Zero on success
*/
-int dp_connector_pre_kickoff(struct drm_connector *connector,
- void *display,
- struct msm_display_kickoff_params *params);
+int dp_connector_config_hdr(void *display,
+ struct sde_connector_state *c_state);
/**
* dp_connector_post_init - callback to perform additional initialization steps
diff --git a/drivers/gpu/drm/msm/dp/dp_link.c b/drivers/gpu/drm/msm/dp/dp_link.c
index 3ca247c..05629dd 100644
--- a/drivers/gpu/drm/msm/dp/dp_link.c
+++ b/drivers/gpu/drm/msm/dp/dp_link.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -724,24 +724,6 @@
return ret;
}
-static char *dp_link_get_test_name(u32 test_requested)
-{
- switch (test_requested) {
- case DP_TEST_LINK_TRAINING:
- return DP_LINK_ENUM_STR(DP_TEST_LINK_TRAINING);
- case DP_TEST_LINK_VIDEO_PATTERN:
- return DP_LINK_ENUM_STR(DP_TEST_LINK_VIDEO_PATTERN);
- case DP_TEST_LINK_EDID_READ:
- return DP_LINK_ENUM_STR(DP_TEST_LINK_EDID_READ);
- case DP_TEST_LINK_PHY_TEST_PATTERN:
- return DP_LINK_ENUM_STR(DP_TEST_LINK_PHY_TEST_PATTERN);
- case DP_TEST_LINK_AUDIO_PATTERN:
- return DP_LINK_ENUM_STR(DP_TEST_LINK_AUDIO_PATTERN);
- default:
- return "unknown";
- }
-}
-
/**
* dp_link_is_video_audio_test_requested() - checks for audio/video link request
* @link: link requested by the sink
diff --git a/drivers/gpu/drm/msm/dp/dp_link.h b/drivers/gpu/drm/msm/dp/dp_link.h
index 6f79b6a..46d30a7 100644
--- a/drivers/gpu/drm/msm/dp/dp_link.h
+++ b/drivers/gpu/drm/msm/dp/dp_link.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -83,6 +83,24 @@
u32 bw_code;
};
+static inline char *dp_link_get_test_name(u32 test_requested)
+{
+ switch (test_requested) {
+ case DP_TEST_LINK_TRAINING:
+ return DP_LINK_ENUM_STR(DP_TEST_LINK_TRAINING);
+ case DP_TEST_LINK_VIDEO_PATTERN:
+ return DP_LINK_ENUM_STR(DP_TEST_LINK_VIDEO_PATTERN);
+ case DP_TEST_LINK_EDID_READ:
+ return DP_LINK_ENUM_STR(DP_TEST_LINK_EDID_READ);
+ case DP_TEST_LINK_PHY_TEST_PATTERN:
+ return DP_LINK_ENUM_STR(DP_TEST_LINK_PHY_TEST_PATTERN);
+ case DP_TEST_LINK_AUDIO_PATTERN:
+ return DP_LINK_ENUM_STR(DP_TEST_LINK_AUDIO_PATTERN);
+ default:
+ return "unknown";
+ }
+}
+
struct dp_link {
u32 sink_request;
u32 test_response;
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index 0401760..7132699 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -136,9 +136,6 @@
goto end;
}
-
- print_hex_dump(KERN_DEBUG, "[drm-dp] SINK DPCD: ",
- DUMP_PREFIX_NONE, 8, 1, dp_panel->dpcd, rlen, false);
}
rlen = drm_dp_dpcd_read(panel->aux->drm_aux,
@@ -275,46 +272,44 @@
static int dp_panel_read_edid(struct dp_panel *dp_panel,
struct drm_connector *connector)
{
+ int ret = 0;
struct dp_panel_private *panel;
if (!dp_panel) {
pr_err("invalid input\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto end;
}
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
if (panel->custom_edid) {
pr_debug("skip edid read in debug mode\n");
- return 0;
+ goto end;
}
sde_get_edid(connector, &panel->aux->drm_aux->ddc,
(void **)&dp_panel->edid_ctrl);
if (!dp_panel->edid_ctrl->edid) {
pr_err("EDID read failed\n");
- } else {
- u8 *buf = (u8 *)dp_panel->edid_ctrl->edid;
- u32 size = buf[0x7E] ? 256 : 128;
-
- print_hex_dump(KERN_DEBUG, "[drm-dp] SINK EDID: ",
- DUMP_PREFIX_NONE, 16, 1, buf, size, false);
-
- return 0;
+ ret = -EINVAL;
+ goto end;
}
-
- return -EINVAL;
+end:
+ return ret;
}
static int dp_panel_read_sink_caps(struct dp_panel *dp_panel,
struct drm_connector *connector, bool multi_func)
{
- int rc = 0;
+ int rc = 0, rlen, count, downstream_ports;
+ const int count_len = 1;
struct dp_panel_private *panel;
if (!dp_panel || !connector) {
pr_err("invalid input\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto end;
}
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
@@ -327,19 +322,35 @@
dp_panel->max_bw_code)) {
if ((rc == -ETIMEDOUT) || (rc == -ENODEV)) {
pr_err("DPCD read failed, return early\n");
- return rc;
+ goto end;
}
pr_err("panel dpcd read failed/incorrect, set default params\n");
dp_panel_set_default_link_params(dp_panel);
}
+ downstream_ports = dp_panel->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+ DP_DWN_STRM_PORT_PRESENT;
+
+ if (downstream_ports) {
+ rlen = drm_dp_dpcd_read(panel->aux->drm_aux, DP_SINK_COUNT,
+ &count, count_len);
+ if (rlen == count_len) {
+ count = DP_GET_SINK_COUNT(count);
+ if (!count) {
+ pr_err("no downstream ports connected\n");
+ rc = -ENOTCONN;
+ goto end;
+ }
+ }
+ }
+
rc = dp_panel_read_edid(dp_panel, connector);
if (rc) {
pr_err("panel edid read failed, set failsafe mode\n");
return rc;
}
-
- return 0;
+end:
+ return rc;
}
static u32 dp_panel_get_supported_bpp(struct dp_panel *dp_panel,
@@ -648,6 +659,7 @@
{
int rc = 0;
struct dp_panel_private *panel;
+ struct dp_catalog_hdr_data *hdr;
if (!dp_panel) {
pr_err("invalid input\n");
@@ -655,11 +667,13 @@
}
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+ hdr = &panel->catalog->hdr_data;
if (!panel->custom_edid)
sde_free_edid((void **)&dp_panel->edid_ctrl);
memset(&dp_panel->pinfo, 0, sizeof(dp_panel->pinfo));
+ memset(&hdr->hdr_meta, 0, sizeof(hdr->hdr_meta));
panel->panel_on = false;
return rc;
@@ -706,30 +720,6 @@
(panel->minor >= 4 || panel->vscext_supported);
}
-static bool dp_panel_is_validate_hdr_state(struct dp_panel_private *panel,
- struct drm_msm_ext_hdr_metadata *hdr_meta)
-{
- struct drm_msm_ext_hdr_metadata *panel_hdr_meta =
- &panel->catalog->hdr_data.hdr_meta;
-
- if (!hdr_meta)
- goto end;
-
- /* bail out if HDR not active */
- if (hdr_meta->hdr_state == HDR_DISABLED &&
- panel->hdr_state == HDR_DISABLED)
- goto end;
-
- /* bail out if same meta data is received */
- if (hdr_meta->hdr_state == HDR_ENABLED &&
- panel_hdr_meta->eotf == hdr_meta->eotf)
- goto end;
-
- return true;
-end:
- return false;
-}
-
static int dp_panel_setup_hdr(struct dp_panel *dp_panel,
struct drm_msm_ext_hdr_metadata *hdr_meta)
{
@@ -744,14 +734,18 @@
}
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+ hdr = &panel->catalog->hdr_data;
- if (!dp_panel_is_validate_hdr_state(panel, hdr_meta))
- goto end;
+ /* use cached meta data in case meta data not provided */
+ if (!hdr_meta) {
+ if (hdr->hdr_meta.hdr_state)
+ goto cached;
+ else
+ goto end;
+ }
panel->hdr_state = hdr_meta->hdr_state;
- hdr = &panel->catalog->hdr_data;
-
hdr->ext_header_byte0 = 0x00;
hdr->ext_header_byte1 = 0x04;
hdr->ext_header_byte2 = 0x1F;
@@ -786,8 +780,9 @@
memcpy(&hdr->hdr_meta, hdr_meta, sizeof(hdr->hdr_meta));
else
memset(&hdr->hdr_meta, 0, sizeof(hdr->hdr_meta));
-
- panel->catalog->config_hdr(panel->catalog, panel->hdr_state);
+cached:
+ if (panel->panel_on)
+ panel->catalog->config_hdr(panel->catalog, panel->hdr_state);
end:
return rc;
}
diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c
index c112cdc..adcc762 100644
--- a/drivers/gpu/drm/msm/dp/dp_parser.c
+++ b/drivers/gpu/drm/msm/dp/dp_parser.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -20,101 +20,45 @@
static void dp_parser_unmap_io_resources(struct dp_parser *parser)
{
+ int i = 0;
struct dp_io *io = &parser->io;
- msm_dss_iounmap(&io->dp_ahb);
- msm_dss_iounmap(&io->dp_aux);
- msm_dss_iounmap(&io->dp_link);
- msm_dss_iounmap(&io->dp_p0);
- msm_dss_iounmap(&io->phy_io);
- msm_dss_iounmap(&io->ln_tx0_io);
- msm_dss_iounmap(&io->ln_tx0_io);
- msm_dss_iounmap(&io->dp_pll_io);
- msm_dss_iounmap(&io->dp_cc_io);
- msm_dss_iounmap(&io->usb3_dp_com);
- msm_dss_iounmap(&io->qfprom_io);
- msm_dss_iounmap(&io->hdcp_io);
+ for (i = 0; i < io->len; i++)
+ msm_dss_iounmap(&io->data[i].io);
}
-static int dp_parser_ctrl_res(struct dp_parser *parser)
+static int dp_parser_reg(struct dp_parser *parser)
{
- int rc = 0;
- u32 index;
+ int rc = 0, i = 0;
+ u32 reg_count;
struct platform_device *pdev = parser->pdev;
- struct device_node *of_node = parser->pdev->dev.of_node;
struct dp_io *io = &parser->io;
+ struct device *dev = &pdev->dev;
- rc = of_property_read_u32(of_node, "cell-index", &index);
- if (rc) {
- pr_err("cell-index not specified, rc=%d\n", rc);
- goto err;
+ reg_count = of_property_count_strings(dev->of_node, "reg-names");
+ if (reg_count <= 0) {
+ pr_err("no reg defined\n");
+ return -EINVAL;
}
- rc = msm_dss_ioremap_byname(pdev, &io->dp_ahb, "dp_ahb");
- if (rc) {
- pr_err("unable to remap dp io resources\n");
- goto err;
+ io->len = reg_count;
+ io->data = devm_kzalloc(dev, sizeof(struct dp_io_data) * reg_count,
+ GFP_KERNEL);
+ if (!io->data)
+ return -ENOMEM;
+
+ for (i = 0; i < reg_count; i++) {
+ of_property_read_string_index(dev->of_node,
+ "reg-names", i, &io->data[i].name);
+ rc = msm_dss_ioremap_byname(pdev, &io->data[i].io,
+ io->data[i].name);
+ if (rc) {
+ pr_err("unable to remap %s resources\n",
+ io->data[i].name);
+ goto err;
+ }
}
- rc = msm_dss_ioremap_byname(pdev, &io->dp_aux, "dp_aux");
- if (rc) {
- pr_err("unable to remap dp io resources\n");
- goto err;
- }
-
- rc = msm_dss_ioremap_byname(pdev, &io->dp_link, "dp_link");
- if (rc) {
- pr_err("unable to remap dp io resources\n");
- goto err;
- }
-
- rc = msm_dss_ioremap_byname(pdev, &io->dp_p0, "dp_p0");
- if (rc) {
- pr_err("unable to remap dp io resources\n");
- goto err;
- }
-
- rc = msm_dss_ioremap_byname(pdev, &io->phy_io, "dp_phy");
- if (rc) {
- pr_err("unable to remap dp PHY resources\n");
- goto err;
- }
-
- rc = msm_dss_ioremap_byname(pdev, &io->ln_tx0_io, "dp_ln_tx0");
- if (rc) {
- pr_err("unable to remap dp TX0 resources\n");
- goto err;
- }
-
- rc = msm_dss_ioremap_byname(pdev, &io->ln_tx1_io, "dp_ln_tx1");
- if (rc) {
- pr_err("unable to remap dp TX1 resources\n");
- goto err;
- }
-
- rc = msm_dss_ioremap_byname(pdev, &io->dp_pll_io, "dp_pll");
- if (rc) {
- pr_err("unable to remap DP PLL resources\n");
- goto err;
- }
-
- rc = msm_dss_ioremap_byname(pdev, &io->usb3_dp_com, "usb3_dp_com");
- if (rc) {
- pr_err("unable to remap USB3 DP com resources\n");
- goto err;
- }
-
- if (msm_dss_ioremap_byname(pdev, &io->dp_cc_io, "dp_mmss_cc")) {
- pr_err("unable to remap dp MMSS_CC resources\n");
- goto err;
- }
-
- if (msm_dss_ioremap_byname(pdev, &io->qfprom_io, "qfprom_physical"))
- pr_warn("unable to remap dp qfprom resources\n");
-
- if (msm_dss_ioremap_byname(pdev, &io->hdcp_io, "hdcp_physical"))
- pr_warn("unable to remap dp hdcp resources\n");
-
return 0;
err:
dp_parser_unmap_io_resources(parser);
@@ -618,7 +562,7 @@
goto err;
}
- rc = dp_parser_ctrl_res(parser);
+ rc = dp_parser_reg(parser);
if (rc)
goto err;
@@ -647,6 +591,74 @@
return rc;
}
+static struct dp_io_data *dp_parser_get_io(struct dp_parser *dp_parser,
+ char *name)
+{
+ int i = 0;
+ struct dp_io *io;
+
+ if (!dp_parser) {
+ pr_err("invalid input\n");
+ goto err;
+ }
+
+ io = &dp_parser->io;
+
+ for (i = 0; i < io->len; i++) {
+ struct dp_io_data *data = &io->data[i];
+
+ if (!strcmp(data->name, name))
+ return data;
+ }
+err:
+ return NULL;
+}
+
+static void dp_parser_get_io_buf(struct dp_parser *dp_parser, char *name)
+{
+ int i = 0;
+ struct dp_io *io;
+
+ if (!dp_parser) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ io = &dp_parser->io;
+
+ for (i = 0; i < io->len; i++) {
+ struct dp_io_data *data = &io->data[i];
+
+ if (!strcmp(data->name, name)) {
+ if (!data->buf)
+ data->buf = devm_kzalloc(&dp_parser->pdev->dev,
+ data->io.len, GFP_KERNEL);
+ }
+ }
+}
+
+static void dp_parser_clear_io_buf(struct dp_parser *dp_parser)
+{
+ int i = 0;
+ struct dp_io *io;
+
+ if (!dp_parser) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ io = &dp_parser->io;
+
+ for (i = 0; i < io->len; i++) {
+ struct dp_io_data *data = &io->data[i];
+
+ if (data->buf)
+ devm_kfree(&dp_parser->pdev->dev, data->buf);
+
+ data->buf = NULL;
+ }
+}
+
struct dp_parser *dp_parser_get(struct platform_device *pdev)
{
struct dp_parser *parser;
@@ -656,6 +668,9 @@
return ERR_PTR(-ENOMEM);
parser->parse = dp_parser_parse;
+ parser->get_io = dp_parser_get_io;
+ parser->get_io_buf = dp_parser_get_io_buf;
+ parser->clear_io_buf = dp_parser_clear_io_buf;
parser->pdev = pdev;
return parser;
@@ -679,5 +694,7 @@
dp_parser_put_gpio_data(&parser->pdev->dev, &power[i]);
}
+ dp_parser_clear_io_buf(parser);
+ devm_kfree(&parser->pdev->dev, parser->io.data);
devm_kfree(&parser->pdev->dev, parser);
}
diff --git a/drivers/gpu/drm/msm/dp/dp_parser.h b/drivers/gpu/drm/msm/dp/dp_parser.h
index 72da381..6e78db2 100644
--- a/drivers/gpu/drm/msm/dp/dp_parser.h
+++ b/drivers/gpu/drm/msm/dp/dp_parser.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -56,35 +56,25 @@
};
/**
- * struct dp_ctrl_resource - controller's IO related data
- *
- * @dp_ahb: controller's ahb mapped memory address
- * @dp_aux: controller's aux mapped memory address
- * @dp_link: controller's link mapped memory address
- * @dp_p0: controller's p0 mapped memory address
- * @phy_io: phy's mapped memory address
- * @ln_tx0_io: USB-DP lane TX0's mapped memory address
- * @ln_tx1_io: USB-DP lane TX1's mapped memory address
- * @dp_cc_io: DP cc's mapped memory address
- * @qfprom_io: qfprom's mapped memory address
- * @dp_pll_io: DP PLL mapped memory address
- * @usb3_dp_com: USB3 DP PHY combo mapped memory address
- * @hdcp_io: hdcp's mapped memory address
+ * struct dp_io_data - data structure to store DP IO related info
+ * @name: name of the IO
+ * @buf: buffer corresponding to IO for debugging
+ * @io: io data which give len and mapped address
+ */
+struct dp_io_data {
+ const char *name;
+ u8 *buf;
+ struct dss_io_data io;
+};
+
+/**
+ * struct dp_io - data struct to store array of DP IO info
+ * @len: total number of IOs
+ * @data: pointer to an array of DP IO data structures.
*/
struct dp_io {
- struct dss_io_data ctrl_io;
- struct dss_io_data dp_ahb;
- struct dss_io_data dp_aux;
- struct dss_io_data dp_link;
- struct dss_io_data dp_p0;
- struct dss_io_data phy_io;
- struct dss_io_data ln_tx0_io;
- struct dss_io_data ln_tx1_io;
- struct dss_io_data dp_cc_io;
- struct dss_io_data qfprom_io;
- struct dss_io_data dp_pll_io;
- struct dss_io_data usb3_dp_com;
- struct dss_io_data hdcp_io;
+ u32 len;
+ struct dp_io_data *data;
};
/**
@@ -171,6 +161,9 @@
* @ctrl_resouce: controller's register address realated data
* @disp_data: controller's display related data
* @parse: function to be called by client to parse device tree.
+ * @get_io: function to be called by client to get io data.
+ * @get_io_buf: function to be called by client to get io buffers.
+ * @clear_io_buf: function to be called by client to clear io buffers.
*/
struct dp_parser {
struct platform_device *pdev;
@@ -184,6 +177,9 @@
u32 max_pclk_khz;
int (*parse)(struct dp_parser *parser);
+ struct dp_io_data *(*get_io)(struct dp_parser *parser, char *name);
+ void (*get_io_buf)(struct dp_parser *parser, char *name);
+ void (*clear_io_buf)(struct dp_parser *parser);
};
/**
diff --git a/drivers/gpu/drm/msm/dp/dp_usbpd.c b/drivers/gpu/drm/msm/dp/dp_usbpd.c
index 2bd3bd4..42eb9b0 100644
--- a/drivers/gpu/drm/msm/dp/dp_usbpd.c
+++ b/drivers/gpu/drm/msm/dp/dp_usbpd.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -426,6 +426,28 @@
return rc;
}
+static int dp_usbpd_simulate_attention(struct dp_usbpd *dp_usbpd, int vdo)
+{
+ int rc = 0;
+ struct dp_usbpd_private *pd;
+
+ if (!dp_usbpd) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ pd = container_of(dp_usbpd, struct dp_usbpd_private, dp_usbpd);
+
+ pd->vdo = vdo;
+ dp_usbpd_get_status(pd);
+
+ if (pd->dp_cb && pd->dp_cb->attention)
+ pd->dp_cb->attention(pd->dev);
+error:
+ return rc;
+}
+
struct dp_usbpd *dp_usbpd_get(struct device *dev, struct dp_usbpd_cb *cb)
{
int rc = 0;
@@ -475,6 +497,7 @@
dp_usbpd = &usbpd->dp_usbpd;
dp_usbpd->simulate_connect = dp_usbpd_simulate_connect;
+ dp_usbpd->simulate_attention = dp_usbpd_simulate_attention;
return dp_usbpd;
error:
diff --git a/drivers/gpu/drm/msm/dp/dp_usbpd.h b/drivers/gpu/drm/msm/dp/dp_usbpd.h
index e70ad7d..0a7efd9 100644
--- a/drivers/gpu/drm/msm/dp/dp_usbpd.h
+++ b/drivers/gpu/drm/msm/dp/dp_usbpd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -50,6 +50,7 @@
* @alt_mode_cfg_done: bool to specify alt mode status
* @debug_en: bool to specify debug mode
* @simulate_connect: simulate disconnect or connect for debug mode
+ * @simulate_attention: simulate attention messages for debug mode
*/
struct dp_usbpd {
enum dp_usbpd_port port;
@@ -65,6 +66,7 @@
bool debug_en;
int (*simulate_connect)(struct dp_usbpd *dp_usbpd, bool hpd);
+ int (*simulate_attention)(struct dp_usbpd *dp_usbpd, int vdo);
};
/**
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
index bfbcf54..6fb7105 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -2087,11 +2087,14 @@
}
mutex_lock(&dsi_ctrl->ctrl_lock);
- if (!dsi_rect_is_equal(&dsi_ctrl->roi, roi)) {
+ if ((!dsi_rect_is_equal(&dsi_ctrl->roi, roi)) ||
+ dsi_ctrl->modeupdated) {
*changed = true;
memcpy(&dsi_ctrl->roi, roi, sizeof(dsi_ctrl->roi));
+ dsi_ctrl->modeupdated = false;
} else
*changed = false;
+
mutex_unlock(&dsi_ctrl->ctrl_lock);
return rc;
}
@@ -2647,6 +2650,7 @@
ctrl->mode_bounds.w = ctrl->host_config.video_timing.h_active;
ctrl->mode_bounds.h = ctrl->host_config.video_timing.v_active;
memcpy(&ctrl->roi, &ctrl->mode_bounds, sizeof(ctrl->mode_bounds));
+ ctrl->modeupdated = true;
ctrl->roi.x = 0;
error:
mutex_unlock(&ctrl->ctrl_lock);
@@ -2673,9 +2677,6 @@
return -EINVAL;
}
- mutex_lock(&dsi_ctrl->ctrl_lock);
- mutex_unlock(&dsi_ctrl->ctrl_lock);
-
return rc;
}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
index ca58896..537bdc3 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
@@ -211,6 +211,7 @@
* dsi controller and run only dsi controller.
* @null_insertion_enabled: A boolean property to allow dsi controller to
* insert null packet.
+ * @modeupdated: Boolean to send new roi if mode is updated.
*/
struct dsi_ctrl {
struct platform_device *pdev;
@@ -258,6 +259,7 @@
bool phy_isolation_enabled;
bool null_insertion_enabled;
+ bool modeupdated;
};
/**
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
index 0ffece3..eaeeb52 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -2960,11 +2960,7 @@
return -EINVAL;
}
- mutex_lock(&panel->panel_lock);
-
memcpy(phy_props, &panel->phy_props, sizeof(*phy_props));
-
- mutex_unlock(&panel->panel_lock);
return rc;
}
@@ -2978,11 +2974,7 @@
return -EINVAL;
}
- mutex_lock(&panel->panel_lock);
-
memcpy(dfps_caps, &panel->dfps_caps, sizeof(*dfps_caps));
-
- mutex_unlock(&panel->panel_lock);
return rc;
}
@@ -3329,7 +3321,7 @@
set->cmds[0].msg.rx_len = 0;
set->cmds[0].msg.rx_buf = 0;
set->cmds[0].last_command = 0;
- set->cmds[0].post_wait_ms = 1;
+ set->cmds[0].post_wait_ms = 0;
set->cmds[1].msg.channel = 0;
set->cmds[1].msg.type = MIPI_DSI_DCS_LONG_WRITE;
@@ -3340,7 +3332,7 @@
set->cmds[1].msg.rx_len = 0;
set->cmds[1].msg.rx_buf = 0;
set->cmds[1].last_command = 1;
- set->cmds[1].post_wait_ms = 1;
+ set->cmds[1].post_wait_ms = 0;
goto exit;
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c
index 42aea7e..3e084d5 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.c
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
@@ -613,6 +613,7 @@
if (!node)
return;
+ spin_lock_irqsave(&node->state_lock, flags);
if (node->state == IRQ_DISABLED) {
ret = sde_core_irq_enable(kms, &irq_idx, 1);
if (ret)
@@ -620,6 +621,7 @@
else
node->state = IRQ_ENABLED;
}
+ spin_unlock_irqrestore(&node->state_lock, flags);
}
static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node,
@@ -1623,6 +1625,7 @@
struct sde_crtc *crtc;
int i;
int irq_idx, ret;
+ unsigned long flags;
struct sde_cp_node prop_node;
struct sde_crtc_irq_info *node = NULL;
@@ -1673,6 +1676,7 @@
if (!en) {
if (node) {
+ spin_lock_irqsave(&node->state_lock, flags);
if (node->state == IRQ_ENABLED) {
ret = sde_core_irq_disable(kms, &irq_idx, 1);
if (ret)
@@ -1683,6 +1687,7 @@
} else {
node->state = IRQ_NOINIT;
}
+ spin_unlock_irqrestore(&node->state_lock, flags);
} else {
DRM_ERROR("failed to get node from crtc event list\n");
}
@@ -1701,6 +1706,7 @@
if (node) {
/* device resume or resume from IPC cases */
+ spin_lock_irqsave(&node->state_lock, flags);
if (node->state == IRQ_DISABLED || node->state == IRQ_NOINIT) {
ret = sde_core_irq_enable(kms, &irq_idx, 1);
if (ret) {
@@ -1712,6 +1718,7 @@
node->state = IRQ_ENABLED;
}
}
+ spin_unlock_irqrestore(&node->state_lock, flags);
} else {
/* request from userspace to register the event
* in this case, node has not been added into the event list
@@ -1807,14 +1814,17 @@
return;
}
+ spin_lock_irqsave(&node->state_lock, flags);
if (node->state == IRQ_ENABLED) {
if (sde_core_irq_disable_nolock(kms, irq_idx)) {
DRM_ERROR("failed to disable irq %d, ret %d\n",
irq_idx, ret);
+ spin_unlock_irqrestore(&node->state_lock, flags);
return;
}
node->state = IRQ_DISABLED;
}
+ spin_unlock_irqrestore(&node->state_lock, flags);
/* lock histogram buffer */
for (i = 0; i < crtc->num_mixers; i++) {
@@ -1886,6 +1896,7 @@
struct sde_crtc *crtc;
struct sde_crtc_irq_info *node = NULL;
int i, irq_idx, ret = 0;
+ unsigned long flags;
if (!crtc_drm || !hist_irq) {
DRM_ERROR("invalid crtc %pK irq %pK\n", crtc_drm, hist_irq);
@@ -1928,6 +1939,7 @@
if (!en) {
if (node) {
/* device suspend case or suspend to IPC cases */
+ spin_lock_irqsave(&node->state_lock, flags);
if (node->state == IRQ_ENABLED) {
ret = sde_core_irq_disable(kms, &irq_idx, 1);
if (ret)
@@ -1938,6 +1950,7 @@
} else {
node->state = IRQ_NOINIT;
}
+ spin_unlock_irqrestore(&node->state_lock, flags);
} else {
DRM_ERROR("failed to get node from crtc event list\n");
}
@@ -1957,6 +1970,7 @@
if (node) {
/* device resume or resume from IPC cases */
+ spin_lock_irqsave(&node->state_lock, flags);
if (node->state == IRQ_DISABLED || node->state == IRQ_NOINIT) {
ret = sde_core_irq_enable(kms, &irq_idx, 1);
if (ret) {
@@ -1968,6 +1982,7 @@
node->state = IRQ_ENABLED;
}
}
+ spin_unlock_irqrestore(&node->state_lock, flags);
} else {
/* request from userspace to register the event
* in this case, node has not been added into the event list
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index d5c4386..655390b 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -471,8 +471,11 @@
}
c_conn->last_panel_power_mode = mode;
- if (mode != SDE_MODE_DPMS_ON)
+ if (mode != SDE_MODE_DPMS_ON) {
+ mutex_unlock(&c_conn->lock);
sde_connector_schedule_status_work(connector, false);
+ mutex_lock(&c_conn->lock);
+ }
return rc;
}
@@ -939,34 +942,38 @@
struct sde_connector_state *c_state,
void *usr_ptr)
{
+ int rc = 0;
struct drm_connector *connector;
struct drm_msm_ext_hdr_metadata *hdr_meta;
int i;
if (!c_conn || !c_state) {
SDE_ERROR_CONN(c_conn, "invalid args\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto end;
}
connector = &c_conn->base;
if (!connector->hdr_supported) {
SDE_ERROR_CONN(c_conn, "sink doesn't support HDR\n");
- return -ENOTSUPP;
+ rc = -ENOTSUPP;
+ goto end;
}
memset(&c_state->hdr_meta, 0, sizeof(c_state->hdr_meta));
if (!usr_ptr) {
SDE_DEBUG_CONN(c_conn, "hdr metadata cleared\n");
- return 0;
+ goto end;
}
if (copy_from_user(&c_state->hdr_meta,
(void __user *)usr_ptr,
sizeof(*hdr_meta))) {
SDE_ERROR_CONN(c_conn, "failed to copy hdr metadata\n");
- return -EFAULT;
+ rc = -EFAULT;
+ goto end;
}
hdr_meta = &c_state->hdr_meta;
@@ -989,7 +996,10 @@
hdr_meta->display_primaries_y[i]);
}
- return 0;
+ if (c_conn->ops.config_hdr)
+ rc = c_conn->ops.config_hdr(c_conn->display, c_state);
+end:
+ return rc;
}
static int sde_connector_atomic_set_property(struct drm_connector *connector,
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index 7cf09b7..9c37869 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -241,6 +241,16 @@
*/
int (*cmd_transfer)(void *display, const char *cmd_buf,
u32 cmd_buf_len);
+
+ /**
+ * config_hdr - configure HDR
+ * @display: Pointer to private display handle
+ * @c_state: Pointer to connector state
+ * Returns: Zero on success, negative error code for failures
+ */
+ int (*config_hdr)(void *display,
+ struct sde_connector_state *c_state);
+
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.c b/drivers/gpu/drm/msm/sde/sde_core_irq.c
index a6f22c9..442104b 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -513,6 +513,7 @@
struct msm_drm_private *priv;
int i;
int rc;
+ unsigned long irq_flags;
if (!sde_kms) {
SDE_ERROR("invalid sde_kms\n");
@@ -543,6 +544,7 @@
sde_disable_all_irqs(sde_kms);
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
+ spin_lock_irqsave(&sde_kms->irq_obj.cb_lock, irq_flags);
kfree(sde_kms->irq_obj.irq_cb_tbl);
kfree(sde_kms->irq_obj.enable_counts);
kfree(sde_kms->irq_obj.irq_counts);
@@ -550,6 +552,7 @@
sde_kms->irq_obj.enable_counts = NULL;
sde_kms->irq_obj.irq_counts = NULL;
sde_kms->irq_obj.total_irqs = 0;
+ spin_unlock_irqrestore(&sde_kms->irq_obj.cb_lock, irq_flags);
}
static void sde_core_irq_mask(struct irq_data *irqd)
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index b3eb101..1ee75c4 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -2511,9 +2511,6 @@
SDE_EVTLOG_FUNC_CASE3);
}
- if (fevent->event & SDE_ENCODER_FRAME_EVENT_DONE)
- sde_core_perf_crtc_update(crtc, 0, false);
-
if (fevent->event & (SDE_ENCODER_FRAME_EVENT_DONE
| SDE_ENCODER_FRAME_EVENT_ERROR))
frame_done = true;
@@ -2597,6 +2594,8 @@
SDE_EVT32_VERBOSE(DRMID(crtc));
smmu_state = &sde_crtc->smmu_state;
+ sde_core_perf_crtc_update(crtc, 0, false);
+
/* complete secure transitions if any */
if (smmu_state->transition_type == POST_COMMIT)
sde_crtc_secure_ctrl(crtc, true);
@@ -3276,7 +3275,8 @@
* smmu state is attached,
*/
if ((smmu_state->state != DETACHED) &&
- (smmu_state->state != DETACH_ALL_REQ))
+ (smmu_state->state != DETACH_ALL_REQ) &&
+ sde_crtc->enabled)
sde_cp_crtc_apply_properties(crtc);
/*
@@ -4487,28 +4487,41 @@
}
static int _sde_crtc_excl_rect_overlap_check(struct plane_state pstates[],
- int cnt, int curr_cnt, struct sde_rect *excl_rect, int z_pos)
+ int cnt, int curr_cnt, struct sde_rect *excl_rect)
{
struct sde_rect dst_rect, intersect;
int i, rc = -EINVAL;
const struct drm_plane_state *pstate;
- /* start checking from next plane */
- for (i = curr_cnt; i < cnt; i++) {
+ for (i = 0; i < cnt; i++) {
+ if (i == curr_cnt)
+ continue;
+
pstate = pstates[i].drm_pstate;
POPULATE_RECT(&dst_rect, pstate->crtc_x, pstate->crtc_y,
pstate->crtc_w, pstate->crtc_h, false);
sde_kms_rect_intersect(&dst_rect, excl_rect, &intersect);
+ /* complete intersection of excl_rect is required */
if (intersect.w == excl_rect->w && intersect.h == excl_rect->h
- /* next plane may be on same z-order */
- && z_pos != pstates[i].stage) {
+ /* intersecting rect should be in another z_order */
+ && pstates[curr_cnt].stage != pstates[i].stage) {
rc = 0;
goto end;
}
}
- SDE_ERROR("excl rect does not find top overlapping rect\n");
+ SDE_ERROR(
+ "no overlapping rect for [%d] z_pos:%d, excl_rect:{%d,%d,%d,%d}\n",
+ i, pstates[curr_cnt].stage,
+ excl_rect->x, excl_rect->y, excl_rect->w, excl_rect->h);
+ for (i = 0; i < cnt; i++) {
+ pstate = pstates[i].drm_pstate;
+ SDE_ERROR("[%d] p:%d, z_pos:%d, src:{%d,%d,%d,%d}\n",
+ i, pstate->plane->base.id, pstates[i].stage,
+ pstate->crtc_x, pstate->crtc_y,
+ pstate->crtc_w, pstate->crtc_h);
+ }
end:
return rc;
}
@@ -4550,9 +4563,9 @@
pstate = pstates[i].drm_pstate;
sde_pstate = to_sde_plane_state(pstate);
if (sde_pstate->excl_rect.w && sde_pstate->excl_rect.h) {
- /* check overlap on all top z-order */
+ /* check overlap on any other z-order */
rc = _sde_crtc_excl_rect_overlap_check(pstates, cnt,
- i + 1, &sde_pstate->excl_rect, pstates[i].stage);
+ i, &sde_pstate->excl_rect);
if (rc)
goto end;
}
@@ -6120,6 +6133,7 @@
INIT_LIST_HEAD(&node->list);
node->func = custom_events[i].func;
node->event = event;
+ spin_lock_init(&node->state_lock);
break;
}
}
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index 33d2b8fa..78f15ec 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -445,7 +445,8 @@
* @event: event type of the interrupt
* @func: function pointer to enable/disable the interrupt
* @list: list of user customized event in crtc
- * @ref_count: reference count for the interrupt
+ * @state: state of the interrupt
+ * @state_lock: spin lock for interrupt state
*/
struct sde_crtc_irq_info {
struct sde_irq_callback irq;
@@ -454,6 +455,7 @@
struct sde_irq_callback *irq);
struct list_head list;
enum sde_crtc_irq_state state;
+ spinlock_t state_lock;
};
#define to_sde_crtc_state(x) \
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 3b5e3f5..92ab669 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <robdclark@gmail.com>
*
@@ -3739,6 +3739,7 @@
SDE_ATRACE_BEGIN("enc_prepare_for_kickoff");
for (i = 0; i < sde_enc->num_phys_encs; i++) {
phys = sde_enc->phys_encs[i];
+ params->is_primary = sde_enc->disp_info.is_primary;
if (phys) {
if (phys->ops.prepare_for_kickoff) {
rc = phys->ops.prepare_for_kickoff(
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index 8038eb6..2c84e20 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <robdclark@gmail.com>
*
@@ -54,11 +54,13 @@
/**
* sde_encoder_kickoff_params - info encoder requires at kickoff
* @inline_rotate_prefill: number of lines to prefill for inline rotation
+ * @is_primary: set to true if the display is primary display
* @affected_displays: bitmask, bit set means the ROI of the commit lies within
* the bounds of the physical display at the bit index
*/
struct sde_encoder_kickoff_params {
u32 inline_rotate_prefill;
+ u32 is_primary;
unsigned long affected_displays;
};
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index 4a15e6f..a3f09b6 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -245,9 +245,10 @@
* HW layer requires VSYNC counter of first pixel of tgt VFP line.
* @phys_enc: Pointer to physical encoder
* @rot_fetch_lines: number of line to prefill, or 0 to disable
+ * @is_primary: set true if the display is primary display
*/
static void programmable_rot_fetch_config(struct sde_encoder_phys *phys_enc,
- u32 rot_fetch_lines)
+ u32 rot_fetch_lines, u32 is_primary)
{
struct sde_encoder_phys_vid *vid_enc =
to_sde_encoder_phys_vid(phys_enc);
@@ -264,7 +265,8 @@
!phys_enc->hw_ctl->ops.get_bitmask_intf ||
!phys_enc->hw_ctl->ops.update_pending_flush ||
!vid_enc->hw_intf->ops.setup_rot_start ||
- !phys_enc->sde_kms)
+ !phys_enc->sde_kms ||
+ !is_primary)
return;
timing = &vid_enc->timing_params;
@@ -403,7 +405,7 @@
to_sde_encoder_phys_vid(phys_enc);
struct sde_hw_ctl *hw_ctl;
unsigned long lock_flags;
- u32 flush_register = 0;
+ u32 flush_register = ~0;
u32 reset_status = 0;
int new_cnt = -1, old_cnt = -1;
u32 event = 0;
@@ -417,42 +419,45 @@
SDE_ATRACE_BEGIN("vblank_irq");
- /* signal only for master, where there is a pending kickoff */
- if (sde_encoder_phys_vid_is_master(phys_enc)
- && atomic_add_unless(
- &phys_enc->pending_retire_fence_cnt, -1, 0)) {
- event = SDE_ENCODER_FRAME_EVENT_SIGNAL_RELEASE_FENCE
- | SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE;
-
- if (phys_enc->parent_ops.handle_frame_done)
- phys_enc->parent_ops.handle_frame_done(phys_enc->parent,
- phys_enc, event);
- }
-
- if (phys_enc->parent_ops.handle_vblank_virt)
- phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent,
- phys_enc);
-
- old_cnt = atomic_read(&phys_enc->pending_kickoff_cnt);
-
/*
* only decrement the pending flush count if we've actually flushed
* hardware. due to sw irq latency, vblank may have already happened
* so we need to double-check with hw that it accepted the flush bits
*/
spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
+
+ old_cnt = atomic_read(&phys_enc->pending_kickoff_cnt);
+
if (hw_ctl && hw_ctl->ops.get_flush_register)
flush_register = hw_ctl->ops.get_flush_register(hw_ctl);
- if (flush_register == 0)
- new_cnt = atomic_add_unless(&phys_enc->pending_kickoff_cnt,
- -1, 0);
+ if (flush_register)
+ goto not_flushed;
+ new_cnt = atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
+
+ /* signal only for master, where there is a pending kickoff */
+ if (sde_encoder_phys_vid_is_master(phys_enc)) {
+ if (atomic_add_unless(&phys_enc->pending_retire_fence_cnt,
+ -1, 0))
+ event |= SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE |
+ SDE_ENCODER_FRAME_EVENT_SIGNAL_RELEASE_FENCE;
+ }
+
+not_flushed:
if (hw_ctl && hw_ctl->ops.get_reset)
reset_status = hw_ctl->ops.get_reset(hw_ctl);
spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
+ if (event && phys_enc->parent_ops.handle_frame_done)
+ phys_enc->parent_ops.handle_frame_done(phys_enc->parent,
+ phys_enc, event);
+
+ if (phys_enc->parent_ops.handle_vblank_virt)
+ phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent,
+ phys_enc);
+
SDE_EVT32_IRQ(DRMID(phys_enc->parent), vid_enc->hw_intf->idx - INTF_0,
old_cnt, new_cnt, reset_status ? SDE_EVTLOG_ERROR : 0,
flush_register, event);
@@ -873,7 +878,8 @@
vid_enc->error_count = 0;
}
- programmable_rot_fetch_config(phys_enc, params->inline_rotate_prefill);
+ programmable_rot_fetch_config(phys_enc,
+ params->inline_rotate_prefill, params->is_primary);
return rc;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ad4.c b/drivers/gpu/drm/msm/sde/sde_hw_ad4.c
index 994bf3d..593e972 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ad4.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ad4.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -786,6 +786,7 @@
blk_offset += 4;
val = (ad_cfg->cfg_param_027 & (BIT(16) - 1));
val |= ((ad_cfg->cfg_param_028 & (BIT(16) - 1)) << 16);
+ SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
blk_offset += 4;
val = (ad_cfg->cfg_param_029 & (BIT(16) - 1));
SDE_REG_WRITE(&dspp->hw, dspp->cap->sblk->ad.base + blk_offset, val);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index 545ed65..cdc6a9c 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1681,6 +1681,8 @@
if (sde_cfg->has_wb_ubwc)
set_bit(SDE_WB_UBWC, &wb->features);
+ set_bit(SDE_WB_XY_ROI_OFFSET, &wb->features);
+
for (j = 0; j < sde_cfg->mdp_count; j++) {
sde_cfg->mdp[j].clk_ctrls[wb->clk_ctrl].reg_off =
PROP_BITVALUE_ACCESS(prop_value,
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 2b0aa37..5d3835c 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -949,7 +949,7 @@
.get_mode_info = dp_connector_get_mode_info,
.post_open = dp_connector_post_open,
.check_status = NULL,
- .pre_kickoff = dp_connector_pre_kickoff,
+ .config_hdr = dp_connector_config_hdr,
.cmd_transfer = NULL,
};
struct msm_display_info info;
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index ed1079d..4c281666 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -2379,6 +2379,29 @@
(u64) &rstate->rot_hw->base);
rstate->out_fbo = NULL;
}
+
+ /*
+ * For video mode, reject any downscale factor greater than or
+ * equal to 1.1x
+ *
+ * Check the downscale factor first to avoid querying the
+ * interface mode unnecessarily.
+ */
+ if ((rstate->out_src_h >> 16) * 10 >= state->crtc_h * 11 &&
+ sde_crtc_get_intf_mode(state->crtc) ==
+ INTF_MODE_VIDEO) {
+ SDE_DEBUG_PLANE(psde,
+ "inline %d with invalid scale, %dx%d, %dx%d\n",
+ rstate->sequence_id,
+ rstate->out_src_w, rstate->out_src_h,
+ state->crtc_w, state->crtc_h);
+ SDE_EVT32(DRMID(plane), rstate->sequence_id,
+ rstate->out_src_w >> 16,
+ rstate->out_src_h >> 16,
+ state->crtc_w, state->crtc_h,
+ SDE_EVTLOG_ERROR);
+ return -EINVAL;
+ }
} else {
SDE_DEBUG("plane%d.%d bypass rotator\n", plane->base.id,
@@ -3257,7 +3280,8 @@
if (!fb || !old_fb) {
SDE_DEBUG_PLANE(psde, "can't compare fb handles\n");
- } else if (fb->pixel_format != old_fb->pixel_format) {
+ } else if ((fb->pixel_format != old_fb->pixel_format) ||
+ pstate->const_alpha_en != old_pstate->const_alpha_en) {
SDE_DEBUG_PLANE(psde, "format change\n");
pstate->dirty |= SDE_PLANE_DIRTY_FORMAT | SDE_PLANE_DIRTY_RECTS;
} else {
@@ -3569,7 +3593,8 @@
pstate->const_alpha_en = fmt->alpha_enable &&
(SDE_DRM_BLEND_OP_OPAQUE !=
- sde_plane_get_property(pstate, PLANE_PROP_BLEND_OP));
+ sde_plane_get_property(pstate, PLANE_PROP_BLEND_OP)) &&
+ (pstate->stage != SDE_STAGE_0);
modeset_update:
if (!ret)
diff --git a/drivers/gpu/drm/msm/sde/sde_rm.c b/drivers/gpu/drm/msm/sde/sde_rm.c
index f9092e2..93304e16 100644
--- a/drivers/gpu/drm/msm/sde/sde_rm.c
+++ b/drivers/gpu/drm/msm/sde/sde_rm.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1129,17 +1129,29 @@
return ret;
}
-/**
- * poll_intr_status - Gets HW interrupt status based on
- * given lookup IRQ index.
- * @intr: HW interrupt handle
- * @irq_idx: Lookup irq index return from irq_idx_lookup
- * @msec: Maximum delay allowed to check intr status
- * return: return zero on success.
- */
-static u32 _sde_rm_poll_intr_status_for_cont_splash
- (struct sde_hw_intr *intr,
- int irq_idx, u32 const msec)
+static void _sde_rm_clear_irq_status(struct sde_hw_intr *hw_intr,
+ int irq_idx_pp_done, int irq_idx_autorefresh)
+{
+ u32 intr_value = 0;
+
+ if ((irq_idx_pp_done >= 0) && (hw_intr->ops.get_intr_status_nomask)) {
+ intr_value = hw_intr->ops.get_intr_status_nomask(hw_intr,
+ irq_idx_pp_done, false);
+ hw_intr->ops.clear_intr_status_force_mask(hw_intr,
+ irq_idx_pp_done, intr_value);
+ }
+
+ if ((irq_idx_autorefresh >= 0) &&
+ (hw_intr->ops.get_intr_status_nomask)) {
+ intr_value = hw_intr->ops.get_intr_status_nomask(hw_intr,
+ irq_idx_autorefresh, false);
+ hw_intr->ops.clear_intr_status_force_mask(hw_intr,
+ irq_idx_autorefresh, intr_value);
+ }
+}
+
+static u32 _sde_rm_poll_intr_status_for_cont_splash(struct sde_hw_intr *intr,
+ int irq_idx_pp_done, int irq_idx_autorefresh, u32 const msec)
{
int i;
u32 status = 0;
@@ -1153,19 +1165,112 @@
for (i = 0; i < loop; i++) {
status = intr->ops.get_intr_status_nomask
- (intr, irq_idx, false);
+ (intr, irq_idx_pp_done, false);
- if (status & BIT(irq_idx)) {
- SDE_DEBUG(" Poll success. i=%d, status=0x%x\n",
+ if (status & BIT(irq_idx_pp_done)) {
+ SDE_DEBUG("pp_done received i=%d, status=0x%x\n",
i, status);
- return 0;
+ SDE_EVT32(status, i, irq_idx_pp_done);
+
+ if (status & BIT(irq_idx_autorefresh))
+ _sde_rm_clear_irq_status(intr,
+ irq_idx_pp_done, irq_idx_autorefresh);
+ else
+ return 0;
}
usleep_range(delay_us, delay_us + 10);
}
+
+ SDE_EVT32(status, irq_idx_pp_done, SDE_EVTLOG_ERROR);
SDE_ERROR("polling timed out. status = 0x%x\n", status);
return -ETIMEDOUT;
}
+static int _sde_rm_autorefresh_disable(struct sde_hw_pingpong *pp,
+ struct sde_hw_intr *hw_intr)
+{
+ u32 const timeout_ms = 35; /* Max two vsyncs delay */
+ int rc = 0, i, loop = 3;
+ struct sde_hw_pp_vsync_info info;
+ int irq_idx_pp_done = -1, irq_idx_autorefresh = -1;
+ struct sde_hw_autorefresh cfg = {0};
+
+ if (!pp->ops.get_autorefresh || !pp->ops.setup_autorefresh ||
+ !pp->ops.connect_external_te || !pp->ops.get_vsync_info) {
+ SDE_ERROR("autorefresh update api not supported\n");
+ return 0;
+ }
+
+ /* read default autorefresh configuration */
+ pp->ops.get_autorefresh(pp, &cfg);
+ if (!cfg.enable) {
+ SDE_DEBUG("autorefresh already disabled\n");
+ SDE_EVT32(pp->idx - PINGPONG_0, SDE_EVTLOG_FUNC_CASE1);
+ return 0;
+ }
+
+ /* disable external TE first */
+ pp->ops.connect_external_te(pp, false);
+
+ /* get all IRQ indexes */
+ if (hw_intr->ops.irq_idx_lookup) {
+ irq_idx_pp_done = hw_intr->ops.irq_idx_lookup(
+ SDE_IRQ_TYPE_PING_PONG_COMP, pp->idx);
+ irq_idx_autorefresh = hw_intr->ops.irq_idx_lookup(
+ SDE_IRQ_TYPE_PING_PONG_AUTO_REF, pp->idx);
+ SDE_DEBUG("pp_done itr_idx = %d autorefresh irq_idx:%d\n",
+ irq_idx_pp_done, irq_idx_autorefresh);
+ }
+
+ /* disable autorefresh */
+ cfg.enable = false;
+ pp->ops.setup_autorefresh(pp, &cfg);
+
+ SDE_EVT32(pp->idx - PINGPONG_0, irq_idx_pp_done, irq_idx_autorefresh);
+ _sde_rm_clear_irq_status(hw_intr, irq_idx_pp_done, irq_idx_autorefresh);
+
+ /*
+ * Check the line count again if
+ * the line count is equal to the active
+ * height to make sure their is no
+ * additional frame updates
+ */
+ for (i = 0; i < loop; i++) {
+ info.wr_ptr_line_count = 0;
+ info.rd_ptr_init_val = 0;
+ pp->ops.get_vsync_info(pp, &info);
+
+ SDE_EVT32(pp->idx - PINGPONG_0, info.wr_ptr_line_count,
+ info.rd_ptr_init_val, SDE_EVTLOG_FUNC_CASE1);
+
+ /* wait for read ptr intr */
+ rc = _sde_rm_poll_intr_status_for_cont_splash(hw_intr,
+ irq_idx_pp_done, irq_idx_autorefresh, timeout_ms);
+
+ info.wr_ptr_line_count = 0;
+ info.rd_ptr_init_val = 0;
+ pp->ops.get_vsync_info(pp, &info);
+ SDE_DEBUG("i=%d, line count=%d\n", i, info.wr_ptr_line_count);
+
+ SDE_EVT32(pp->idx - PINGPONG_0, info.wr_ptr_line_count,
+ info.rd_ptr_init_val, SDE_EVTLOG_FUNC_CASE2);
+
+ /* log line count and return */
+ if (!rc)
+ break;
+ /*
+ * Wait for few milli seconds for line count
+ * to increase if any frame transfer is
+ * pending.
+ */
+ usleep_range(3000, 4000);
+ }
+
+ pp->ops.connect_external_te(pp, true);
+
+ return rc;
+}
+
/**
* sde_rm_get_pp_dsc_for_cont_splash - retrieve the current dsc enabled blocks
* and disable autorefresh if enabled.
@@ -1181,9 +1286,7 @@
{
int index = 0;
int value, dsc_cnt = 0;
- struct sde_hw_autorefresh cfg;
struct sde_rm_hw_iter iter_pp;
- int irq_idx_pp_done = -1;
if (!rm || !sde_kms || !dsc_ids) {
SDE_ERROR("invalid input parameters\n");
@@ -1195,11 +1298,7 @@
while (_sde_rm_get_hw_locked(rm, &iter_pp)) {
struct sde_hw_pingpong *pp =
to_sde_hw_pingpong(iter_pp.blk->hw);
- u32 intr_value = 0;
- u32 const timeout_ms = 35; /* Max two vsyncs delay */
- int rc = 0, i, loop = 2;
struct sde_hw_intr *hw_intr = NULL;
- struct sde_hw_pp_vsync_info info;
if (!pp->ops.get_dsc_status) {
SDE_ERROR("get_dsc_status ops not initialized\n");
@@ -1219,70 +1318,7 @@
}
index++;
- if (!pp->ops.get_autorefresh) {
- SDE_ERROR("get_autorefresh api not supported\n");
- return 0;
- }
- memset(&cfg, 0, sizeof(cfg));
- if (!pp->ops.get_autorefresh(pp, &cfg)
- && (cfg.enable)
- && (pp->ops.setup_autorefresh)) {
- if (hw_intr->ops.irq_idx_lookup) {
- irq_idx_pp_done = hw_intr->ops.irq_idx_lookup
- (SDE_IRQ_TYPE_PING_PONG_COMP,
- pp->idx);
- SDE_DEBUG(" itr_idx = %d\n", irq_idx_pp_done);
- }
-
- if ((irq_idx_pp_done >= 0) &&
- (hw_intr->ops.get_intr_status_nomask)) {
- intr_value = hw_intr->ops.get_intr_status_nomask
- (hw_intr, irq_idx_pp_done, false);
- hw_intr->ops.clear_intr_status_force_mask
- (hw_intr, irq_idx_pp_done, intr_value);
- }
- cfg.enable = false;
- SDE_DEBUG("Disabling autorefresh\n");
- pp->ops.setup_autorefresh(pp, &cfg);
-
- /*
- * Check the line count again if
- * the line count is equal to the active
- * height to make sure their is no
- * additional frame updates
- */
- for (i = 0; i < loop; i++) {
- info.wr_ptr_line_count = 0;
- info.rd_ptr_init_val = 0;
- if (pp->ops.get_vsync_info)
- pp->ops.get_vsync_info(pp, &info);
- /*
- * For cmd-mode using external-TE logic,
- * the rd_ptr_init_val is equal to
- * active-height. Use this init_val to
- * compare that with lane count. Need
- * to implement a different check
- * if external-TE is not used.
- */
- if (info.wr_ptr_line_count
- < info.rd_ptr_init_val) {
- /* wait for read ptr intr */
- rc =
- _sde_rm_poll_intr_status_for_cont_splash
- (hw_intr, irq_idx_pp_done, timeout_ms);
- if (!rc)
- break;
- }
- SDE_DEBUG("i=%d, line count=%d\n",
- i, info.wr_ptr_line_count);
- /*
- * Wait for few milli seconds for line count
- * to increase if any frame transfer is
- * pending.
- */
- usleep_range(3000, 4000);
- }
- }
+ _sde_rm_autorefresh_disable(pp, hw_intr);
}
return dsc_cnt;
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
index 4ceed7a9..4b83e9e 100644
--- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
@@ -638,7 +638,8 @@
match = of_match_node(dmm_of_match, dev->dev.of_node);
if (!match) {
dev_err(&dev->dev, "failed to find matching device node\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto fail;
}
omap_dmm->plat_data = match->data;
diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c
index 094bc6a..d96c084 100644
--- a/drivers/gpu/drm/vc4/vc4_irq.c
+++ b/drivers/gpu/drm/vc4/vc4_irq.c
@@ -225,6 +225,9 @@
/* Clear any pending interrupts we might have left. */
V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
+ /* Finish any interrupt handler still in flight. */
+ disable_irq(dev->irq);
+
cancel_work_sync(&vc4->overflow_mem_work);
}
diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c
index 7cc346a..ce7c21d 100644
--- a/drivers/gpu/drm/vc4/vc4_v3d.c
+++ b/drivers/gpu/drm/vc4/vc4_v3d.c
@@ -173,6 +173,9 @@
struct vc4_dev *vc4 = v3d->vc4;
vc4_v3d_init_hw(vc4->dev);
+
+ /* We disabled the IRQ as part of vc4_irq_uninstall in suspend. */
+ enable_irq(vc4->dev->irq);
vc4_irq_postinstall(vc4->dev);
return 0;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index fefb9d9..81f5a55 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -2729,6 +2729,8 @@
}
view_type = vmw_view_cmd_to_type(header->id);
+ if (view_type == vmw_view_max)
+ return -EINVAL;
cmd = container_of(header, typeof(*cmd), header);
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface,
user_surface_converter,
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index c01640c..c734123 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -364,7 +364,7 @@
{ A6XX_CP_AHB_CNTL, 0x0 },
};
-static struct reg_list_pair a615_ifpc_pwrup_reglist[] = {
+static struct reg_list_pair a615_pwrup_reglist[] = {
{ A6XX_UCHE_GBIF_GX_CONFIG, 0x0 },
};
@@ -604,7 +604,6 @@
uint32_t i;
struct cpu_gpu_lock *lock;
struct reg_list_pair *r;
- uint16_t a615_list_size = 0;
/* Set up the register values */
for (i = 0; i < ARRAY_SIZE(a6xx_ifpc_pwrup_reglist); i++) {
@@ -617,19 +616,6 @@
kgsl_regread(KGSL_DEVICE(adreno_dev), r->offset, &r->val);
}
- if (adreno_is_a615(adreno_dev)) {
- for (i = 0; i < ARRAY_SIZE(a615_ifpc_pwrup_reglist); i++) {
- r = &a615_ifpc_pwrup_reglist[i];
- kgsl_regread(KGSL_DEVICE(adreno_dev),
- r->offset, &r->val);
- }
-
- a615_list_size = sizeof(a615_ifpc_pwrup_reglist);
-
- memcpy(adreno_dev->pwrup_reglist.hostptr + sizeof(*lock),
- a615_ifpc_pwrup_reglist, a615_list_size);
- }
-
lock = (struct cpu_gpu_lock *) adreno_dev->pwrup_reglist.hostptr;
lock->flag_ucode = 0;
lock->flag_kmd = 0;
@@ -648,16 +634,29 @@
* of the static IFPC-only register list.
*/
lock->list_length = (sizeof(a6xx_ifpc_pwrup_reglist) +
- sizeof(a6xx_pwrup_reglist) + a615_list_size) >> 2;
- lock->list_offset = (sizeof(a6xx_ifpc_pwrup_reglist) +
- a615_list_size) >> 2;
+ sizeof(a6xx_pwrup_reglist)) >> 2;
+ lock->list_offset = sizeof(a6xx_ifpc_pwrup_reglist) >> 2;
- memcpy(adreno_dev->pwrup_reglist.hostptr + sizeof(*lock)
- + a615_list_size,
+ memcpy(adreno_dev->pwrup_reglist.hostptr + sizeof(*lock),
a6xx_ifpc_pwrup_reglist, sizeof(a6xx_ifpc_pwrup_reglist));
memcpy(adreno_dev->pwrup_reglist.hostptr + sizeof(*lock)
- + sizeof(a6xx_ifpc_pwrup_reglist) + a615_list_size,
- a6xx_pwrup_reglist, sizeof(a6xx_pwrup_reglist));
+ + sizeof(a6xx_ifpc_pwrup_reglist), a6xx_pwrup_reglist,
+ sizeof(a6xx_pwrup_reglist));
+
+ if (adreno_is_a615(adreno_dev)) {
+ for (i = 0; i < ARRAY_SIZE(a615_pwrup_reglist); i++) {
+ r = &a615_pwrup_reglist[i];
+ kgsl_regread(KGSL_DEVICE(adreno_dev),
+ r->offset, &r->val);
+ }
+
+ memcpy(adreno_dev->pwrup_reglist.hostptr + sizeof(*lock)
+ + sizeof(a6xx_ifpc_pwrup_reglist)
+ + sizeof(a6xx_pwrup_reglist), a615_pwrup_reglist,
+ sizeof(a615_pwrup_reglist));
+
+ lock->list_length += sizeof(a615_pwrup_reglist);
+ }
}
/*
@@ -2032,17 +2031,17 @@
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
struct gmu_device *gmu = &device->gmu;
- unsigned int status, status2;
+ unsigned int status2;
+ uint64_t ts1;
+ ts1 = read_AO_counter(device);
if (timed_poll_check(device, A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS,
0, GMU_START_TIMEOUT, CXGXCPUBUSYIGNAHB)) {
kgsl_gmu_regread(device,
- A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS, &status);
- kgsl_gmu_regread(device,
A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS2, &status2);
dev_err(&gmu->pdev->dev,
- "GMU not idling: status=0x%x, status2=0x%x\n",
- status, status2);
+ "GMU not idling: status2=0x%x %llx %llx\n",
+ status2, ts1, read_AO_counter(device));
return -ETIMEDOUT;
}
diff --git a/drivers/gpu/msm/adreno_a6xx_snapshot.c b/drivers/gpu/msm/adreno_a6xx_snapshot.c
index 97f2c58..afd1be5 100644
--- a/drivers/gpu/msm/adreno_a6xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a6xx_snapshot.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -376,6 +376,7 @@
A6XX_DBGBUS_CX = 0x17,
A6XX_DBGBUS_GMU_GX = 0x18,
A6XX_DBGBUS_TPFCHE = 0x19,
+ A6XX_DBGBUS_GBIF_GX = 0x1a,
A6XX_DBGBUS_GPC = 0x1d,
A6XX_DBGBUS_LARC = 0x1e,
A6XX_DBGBUS_HLSQ_SPTP = 0x1f,
@@ -1161,11 +1162,14 @@
}
header->id = block->block_id;
+ if ((block->block_id == A6XX_DBGBUS_VBIF) &&
+ adreno_has_gbif(adreno_dev))
+ header->id = A6XX_DBGBUS_GBIF_GX;
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) &&
+ if ((adreno_is_a630(adreno_dev) || adreno_is_a615(adreno_dev)) &&
(block_id == A6XX_DBGBUS_GMU_GX))
block_id = A6XX_DBGBUS_GMU_CX;
@@ -1428,18 +1432,18 @@
KGSL_SNAPSHOT_SECTION_DEBUGBUS,
snapshot, a6xx_snapshot_cx_dbgc_debugbus_block,
(void *) &a6xx_cx_dbgc_debugbus_blocks[i]);
- /*
- * Get debugbus for GBIF CX part if GPU has GBIF block
- * GBIF uses exactly same ID as of VBIF so use
- * it as it is.
- */
- if (adreno_has_gbif(adreno_dev))
- kgsl_snapshot_add_section(device,
- KGSL_SNAPSHOT_SECTION_DEBUGBUS,
- snapshot,
- a6xx_snapshot_cx_dbgc_debugbus_block,
- (void *) &a6xx_vbif_debugbus_blocks);
}
+ /*
+ * Get debugbus for GBIF CX part if GPU has GBIF block
+ * GBIF uses exactly same ID as of VBIF so use
+ * it as it is.
+ */
+ if (adreno_has_gbif(adreno_dev))
+ kgsl_snapshot_add_section(device,
+ KGSL_SNAPSHOT_SECTION_DEBUGBUS,
+ snapshot,
+ a6xx_snapshot_cx_dbgc_debugbus_block,
+ (void *) &a6xx_vbif_debugbus_blocks);
}
}
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index f57fbb6..5039a06 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -2998,7 +2998,7 @@
long ret = 0;
bool full_flush = false;
uint64_t size = 0;
- int i, count = 0;
+ int i;
void __user *ptr;
if (param->count == 0 || param->count > 128)
@@ -3010,8 +3010,8 @@
entries = kcalloc(param->count, sizeof(*entries), GFP_KERNEL);
if (entries == NULL) {
- ret = -ENOMEM;
- goto out;
+ kfree(objs);
+ return -ENOMEM;
}
ptr = to_user_ptr(param->objs);
@@ -3028,8 +3028,6 @@
if (entries[i] == NULL)
continue;
- count++;
-
if (!(objs[i].op & KGSL_GPUMEM_CACHE_RANGE))
size += entries[i]->memdesc.size;
else if (objs[i].offset < entries[i]->memdesc.size)
@@ -3038,25 +3036,23 @@
full_flush = check_full_flush(size, objs[i].op);
if (full_flush) {
trace_kgsl_mem_sync_full_cache(i, size);
- break;
+ goto out;
}
ptr += sizeof(*objs);
}
- if (!full_flush) {
- for (i = 0; !ret && i < param->count; i++)
- if (entries[i])
- ret = _kgsl_gpumem_sync_cache(entries[i],
- objs[i].offset, objs[i].length,
- objs[i].op);
- }
+ for (i = 0; !ret && i < param->count; i++)
+ if (entries[i])
+ ret = _kgsl_gpumem_sync_cache(entries[i],
+ objs[i].offset, objs[i].length,
+ objs[i].op);
+out:
for (i = 0; i < param->count; i++)
if (entries[i])
kgsl_mem_entry_put(entries[i]);
-out:
kfree(entries);
kfree(objs);
diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index 66fc011..10446f7 100644
--- a/drivers/gpu/msm/kgsl_gmu.c
+++ b/drivers/gpu/msm/kgsl_gmu.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1307,10 +1307,13 @@
do {
if (!regulator_is_enabled(gmu->cx_gdsc))
return 0;
- cond_resched();
+ usleep_range(10, 100);
} while (!(time_after(jiffies, t)));
+ if (!regulator_is_enabled(gmu->cx_gdsc))
+ return 0;
+
dev_err(&gmu->pdev->dev, "GMU CX gdsc off timeout");
return -ETIMEDOUT;
}
@@ -1635,6 +1638,8 @@
unsigned int fence_mask)
{
unsigned int status, i;
+ struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
+ unsigned int reg_offset = gpudev->reg_offsets->offsets[offset];
adreno_writereg(adreno_dev, offset, val);
@@ -1659,6 +1664,6 @@
}
dev_err(adreno_dev->dev.dev,
- "GMU fenced register write timed out: reg %x\n", offset);
+ "GMU fenced register write timed out: reg 0x%x\n", reg_offset);
return -ETIMEDOUT;
}
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 4b11bbe..60c56a06 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -165,14 +165,19 @@
}
void kgsl_iommu_unmap_global_secure_pt_entry(struct kgsl_device *device,
- struct kgsl_memdesc *entry)
+ struct kgsl_memdesc *memdesc)
{
- if (!kgsl_mmu_is_secured(&device->mmu))
+ if (!kgsl_mmu_is_secured(&device->mmu) || memdesc == NULL)
return;
- if (entry != NULL && entry->pagetable->name == KGSL_MMU_SECURE_PT)
- kgsl_mmu_unmap(entry->pagetable, entry);
+ /* Check if an empty memdesc got passed in */
+ if ((memdesc->gpuaddr == 0) || (memdesc->size == 0))
+ return;
+ if (memdesc->pagetable) {
+ if (memdesc->pagetable->name == KGSL_MMU_SECURE_PT)
+ kgsl_mmu_unmap(memdesc->pagetable, memdesc);
+ }
}
int kgsl_iommu_map_global_secure_pt_entry(struct kgsl_device *device,
diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h
index 462ff3b..65460f7 100644
--- a/drivers/gpu/msm/kgsl_iommu.h
+++ b/drivers/gpu/msm/kgsl_iommu.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -25,7 +25,7 @@
*/
#define KGSL_IOMMU_GLOBAL_MEM_SIZE (20 * SZ_1M)
#define KGSL_IOMMU_GLOBAL_MEM_BASE32 0xf8000000
-#define KGSL_IOMMU_GLOBAL_MEM_BASE64 TASK_SIZE_32
+#define KGSL_IOMMU_GLOBAL_MEM_BASE64 0xfc000000
#define KGSL_IOMMU_GLOBAL_MEM_BASE(__mmu) \
(MMU_FEATURE(__mmu, KGSL_MMU_64BIT) ? \
diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c
index c4ff22f..3f7ea18 100644
--- a/drivers/gpu/msm/kgsl_pwrscale.c
+++ b/drivers/gpu/msm/kgsl_pwrscale.c
@@ -910,12 +910,12 @@
min_level = pwr->thermal_pwrlevel_floor;
/* Thermal limit cannot be lower than lowest non-zero operating freq */
- for (level = 0; level < (pwr->num_pwrlevels - 1); level++)
+ for (level = 0; level < (pwr->num_pwrlevels - 1); level++) {
if (pwr->pwrlevels[level].gpu_freq == max_freq)
max_level = level;
if (pwr->pwrlevels[level].gpu_freq == min_freq)
min_level = level;
-
+ }
pwr->thermal_pwrlevel = max_level;
pwr->thermal_pwrlevel_floor = min_level;
diff --git a/drivers/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c
index d4165b3..a4de6a0 100644
--- a/drivers/gpu/msm/kgsl_sync.c
+++ b/drivers/gpu/msm/kgsl_sync.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -734,7 +734,8 @@
fput(sync_file->file);
else if (sfence)
fence_put(&sfence->fence);
- kgsl_syncsource_put(syncsource);
+ else
+ kgsl_syncsource_put(syncsource);
}
return ret;
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index d72dfb2..7a4d39c 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -2192,23 +2192,23 @@
int i;
unsigned long flags;
- spin_lock_irqsave(&remote->remote_lock, flags);
- remote->remotes[index].registered = false;
- spin_unlock_irqrestore(&remote->remote_lock, flags);
-
- if (remote->remotes[index].battery.battery)
- devres_release_group(&wacom->hdev->dev,
- &remote->remotes[index].battery.bat_desc);
-
- if (remote->remotes[index].group.name)
- devres_release_group(&wacom->hdev->dev,
- &remote->remotes[index]);
-
for (i = 0; i < WACOM_MAX_REMOTES; i++) {
if (remote->remotes[i].serial == serial) {
+
+ spin_lock_irqsave(&remote->remote_lock, flags);
+ remote->remotes[i].registered = false;
+ spin_unlock_irqrestore(&remote->remote_lock, flags);
+
+ if (remote->remotes[i].battery.battery)
+ devres_release_group(&wacom->hdev->dev,
+ &remote->remotes[i].battery.bat_desc);
+
+ if (remote->remotes[i].group.name)
+ devres_release_group(&wacom->hdev->dev,
+ &remote->remotes[i]);
+
remote->remotes[i].serial = 0;
remote->remotes[i].group.name = NULL;
- remote->remotes[i].registered = false;
remote->remotes[i].battery.battery = NULL;
wacom->led.groups[i].select = WACOM_STATUS_UNKNOWN;
}
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
index e0a8216..13c32eb4 100644
--- a/drivers/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -31,6 +31,7 @@
#include <linux/clockchips.h>
#include <asm/hyperv.h>
#include <asm/mshyperv.h>
+#include <asm/nospec-branch.h>
#include "hyperv_vmbus.h"
/* The one and only */
@@ -103,9 +104,10 @@
return (u64)ULLONG_MAX;
__asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8");
- __asm__ __volatile__("call *%3" : "=a" (hv_status) :
+ __asm__ __volatile__(CALL_NOSPEC :
+ "=a" (hv_status) :
"c" (control), "d" (input_address),
- "m" (hypercall_page));
+ THUNK_TARGET(hypercall_page));
return hv_status;
@@ -123,11 +125,12 @@
if (!hypercall_page)
return (u64)ULLONG_MAX;
- __asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi),
+ __asm__ __volatile__ (CALL_NOSPEC : "=d"(hv_status_hi),
"=a"(hv_status_lo) : "d" (control_hi),
"a" (control_lo), "b" (input_address_hi),
"c" (input_address_lo), "D"(output_address_hi),
- "S"(output_address_lo), "m" (hypercall_page));
+ "S"(output_address_lo),
+ THUNK_TARGET(hypercall_page));
return hv_status_lo | ((u64)hv_status_hi << 32);
#endif /* !x86_64 */
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index ba59eae..d013acf 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -20,6 +20,7 @@
*/
#include <linux/kernel.h>
+#include <linux/math64.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
@@ -476,8 +477,8 @@
static long pmbus_reg2data_direct(struct pmbus_data *data,
struct pmbus_sensor *sensor)
{
- long val = (s16) sensor->data;
- long m, b, R;
+ s64 b, val = (s16)sensor->data;
+ s32 m, R;
m = data->info->m[sensor->class];
b = data->info->b[sensor->class];
@@ -505,11 +506,12 @@
R--;
}
while (R < 0) {
- val = DIV_ROUND_CLOSEST(val, 10);
+ val = div_s64(val + 5LL, 10L); /* round closest */
R++;
}
- return (val - b) / m;
+ val = div_s64(val - b, m);
+ return clamp_val(val, LONG_MIN, LONG_MAX);
}
/*
@@ -629,7 +631,8 @@
static u16 pmbus_data2reg_direct(struct pmbus_data *data,
struct pmbus_sensor *sensor, long val)
{
- long m, b, R;
+ s64 b, val64 = val;
+ s32 m, R;
m = data->info->m[sensor->class];
b = data->info->b[sensor->class];
@@ -646,18 +649,18 @@
R -= 3; /* Adjust R and b for data in milli-units */
b *= 1000;
}
- val = val * m + b;
+ val64 = val64 * m + b;
while (R > 0) {
- val *= 10;
+ val64 *= 10;
R--;
}
while (R < 0) {
- val = DIV_ROUND_CLOSEST(val, 10);
+ val64 = div_s64(val64 + 5LL, 10L); /* round closest */
R++;
}
- return val;
+ return (u16)clamp_val(val64, S16_MIN, S16_MAX);
}
static u16 pmbus_data2reg_vid(struct pmbus_data *data,
diff --git a/drivers/hwtracing/coresight/coresight-byte-cntr.c b/drivers/hwtracing/coresight/coresight-byte-cntr.c
index 7ef2710..81889b6 100644
--- a/drivers/hwtracing/coresight/coresight-byte-cntr.c
+++ b/drivers/hwtracing/coresight/coresight-byte-cntr.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -23,17 +23,23 @@
static struct tmc_drvdata *tmcdrvdata;
static void tmc_etr_read_bytes(struct byte_cntr *byte_cntr_data, loff_t *ppos,
- size_t bytes, size_t *len)
+ size_t bytes, size_t *len, char **bufp)
{
- if (*len >= bytes) {
- atomic_dec(&byte_cntr_data->irq_cnt);
+
+ if (*bufp >= (char *)(tmcdrvdata->vaddr + tmcdrvdata->size))
+ *bufp = tmcdrvdata->vaddr;
+
+ if (*len >= bytes)
*len = bytes;
- } else {
- if (((uint32_t)*ppos % bytes) + *len > bytes)
- *len = bytes - ((uint32_t)*ppos % bytes);
- if ((*len + (uint32_t)*ppos) % bytes == 0)
- atomic_dec(&byte_cntr_data->irq_cnt);
- }
+ else if (((uint32_t)*ppos % bytes) + *len > bytes)
+ *len = bytes - ((uint32_t)*ppos % bytes);
+
+ if ((*bufp + *len) > (char *)(tmcdrvdata->vaddr +
+ tmcdrvdata->size))
+ *len = (char *)(tmcdrvdata->vaddr + tmcdrvdata->size) -
+ *bufp;
+ if (*len == bytes || (*len + (uint32_t)*ppos) % bytes == 0)
+ atomic_dec(&byte_cntr_data->irq_cnt);
}
static void tmc_etr_sg_read_pos(loff_t *ppos,
@@ -96,7 +102,7 @@
if (*len >= (bytes - ((uint32_t)*ppos % bytes)))
*len = bytes - ((uint32_t)*ppos % bytes);
- if ((*len + (uint32_t)*ppos) % bytes == 0)
+ if (*len == bytes || (*len + (uint32_t)*ppos) % bytes == 0)
atomic_dec(&tmcdrvdata->byte_cntr->irq_cnt);
}
@@ -153,11 +159,12 @@
if (!byte_cntr_data->read_active)
goto err0;
}
- bufp = (char *)(tmcdrvdata->vaddr + *ppos);
+ bufp = (char *)(tmcdrvdata->buf + *ppos);
if (tmcdrvdata->mem_type == TMC_ETR_MEM_TYPE_CONTIG)
tmc_etr_read_bytes(byte_cntr_data, ppos,
- byte_cntr_data->block_size, &len);
+ byte_cntr_data->block_size, &len,
+ &bufp);
else
tmc_etr_sg_read_pos(ppos, byte_cntr_data->block_size, 0,
&len, &bufp);
@@ -179,7 +186,7 @@
if (tmcdrvdata->mem_type == TMC_ETR_MEM_TYPE_CONTIG)
tmc_etr_read_bytes(byte_cntr_data, ppos,
byte_cntr_data->block_size,
- &len);
+ &len, &bufp);
else
tmc_etr_sg_read_pos(ppos,
byte_cntr_data->block_size,
@@ -229,7 +236,7 @@
mutex_lock(&byte_cntr_data->byte_cntr_lock);
byte_cntr_data->enable = false;
- coresight_csr_set_byte_cntr(0);
+ coresight_csr_set_byte_cntr(byte_cntr_data->csr, 0);
mutex_unlock(&byte_cntr_data->byte_cntr_lock);
}
@@ -243,7 +250,7 @@
mutex_lock(&byte_cntr_data->byte_cntr_lock);
byte_cntr_data->read_active = false;
- coresight_csr_set_byte_cntr(0);
+ coresight_csr_set_byte_cntr(byte_cntr_data->csr, 0);
mutex_unlock(&byte_cntr_data->byte_cntr_lock);
return 0;
@@ -261,7 +268,8 @@
return -EINVAL;
}
- coresight_csr_set_byte_cntr(byte_cntr_data->block_size);
+ coresight_csr_set_byte_cntr(byte_cntr_data->csr,
+ byte_cntr_data->block_size);
fp->private_data = byte_cntr_data;
nonseekable_open(in, fp);
byte_cntr_data->enable = true;
@@ -364,6 +372,7 @@
tmcdrvdata = drvdata;
byte_cntr_data->byte_cntr_irq = byte_cntr_irq;
+ byte_cntr_data->csr = drvdata->csr;
atomic_set(&byte_cntr_data->irq_cnt, 0);
init_waitqueue_head(&byte_cntr_data->wq);
mutex_init(&byte_cntr_data->byte_cntr_lock);
diff --git a/drivers/hwtracing/coresight/coresight-byte-cntr.h b/drivers/hwtracing/coresight/coresight-byte-cntr.h
index 94e9089..b104d92 100644
--- a/drivers/hwtracing/coresight/coresight-byte-cntr.h
+++ b/drivers/hwtracing/coresight/coresight-byte-cntr.h
@@ -16,6 +16,7 @@
atomic_t irq_cnt;
wait_queue_head_t wq;
struct mutex byte_cntr_lock;
+ struct coresight_csr *csr;
};
extern void tmc_etr_byte_cntr_start(struct byte_cntr *byte_cntr_data);
diff --git a/drivers/hwtracing/coresight/coresight-csr.c b/drivers/hwtracing/coresight/coresight-csr.c
index 1ec73a5..9069530 100644
--- a/drivers/hwtracing/coresight/coresight-csr.c
+++ b/drivers/hwtracing/coresight/coresight-csr.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, 2015-2016 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 2015-2016,2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -20,6 +20,7 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/coresight.h>
+#include <linux/clk.h>
#include "coresight-priv.h"
@@ -77,15 +78,32 @@
struct device *dev;
struct coresight_device *csdev;
uint32_t blksize;
+ struct coresight_csr csr;
+ struct clk *clk;
+ spinlock_t spin_lock;
+ bool usb_bam_support;
+ bool hwctrl_set_support;
+ bool set_byte_cntr_support;
+ bool timestamp_support;
};
-static struct csr_drvdata *csrdrvdata;
+static LIST_HEAD(csr_list);
+#define to_csr_drvdata(c) container_of(c, struct csr_drvdata, csr)
-void msm_qdss_csr_enable_bam_to_usb(void)
+void msm_qdss_csr_enable_bam_to_usb(struct coresight_csr *csr)
{
- struct csr_drvdata *drvdata = csrdrvdata;
+ struct csr_drvdata *drvdata;
uint32_t usbbamctrl, usbflshctrl;
+ unsigned long flags;
+ if (csr == NULL)
+ return;
+
+ drvdata = to_csr_drvdata(csr);
+ if (IS_ERR_OR_NULL(drvdata) || !drvdata->usb_bam_support)
+ return;
+
+ spin_lock_irqsave(&drvdata->spin_lock, flags);
CSR_UNLOCK(drvdata);
usbbamctrl = csr_readl(drvdata, CSR_USBBAMCTRL);
@@ -102,14 +120,24 @@
csr_writel(drvdata, usbbamctrl, CSR_USBBAMCTRL);
CSR_LOCK(drvdata);
+ spin_unlock_irqrestore(&drvdata->spin_lock, flags);
}
EXPORT_SYMBOL(msm_qdss_csr_enable_bam_to_usb);
-void msm_qdss_csr_disable_bam_to_usb(void)
+void msm_qdss_csr_disable_bam_to_usb(struct coresight_csr *csr)
{
- struct csr_drvdata *drvdata = csrdrvdata;
+ struct csr_drvdata *drvdata;
uint32_t usbbamctrl;
+ unsigned long flags;
+ if (csr == NULL)
+ return;
+
+ drvdata = to_csr_drvdata(csr);
+ if (IS_ERR_OR_NULL(drvdata) || !drvdata->usb_bam_support)
+ return;
+
+ spin_lock_irqsave(&drvdata->spin_lock, flags);
CSR_UNLOCK(drvdata);
usbbamctrl = csr_readl(drvdata, CSR_USBBAMCTRL);
@@ -117,14 +145,24 @@
csr_writel(drvdata, usbbamctrl, CSR_USBBAMCTRL);
CSR_LOCK(drvdata);
+ spin_unlock_irqrestore(&drvdata->spin_lock, flags);
}
EXPORT_SYMBOL(msm_qdss_csr_disable_bam_to_usb);
-void msm_qdss_csr_disable_flush(void)
+void msm_qdss_csr_disable_flush(struct coresight_csr *csr)
{
- struct csr_drvdata *drvdata = csrdrvdata;
+ struct csr_drvdata *drvdata;
uint32_t usbflshctrl;
+ unsigned long flags;
+ if (csr == NULL)
+ return;
+
+ drvdata = to_csr_drvdata(csr);
+ if (IS_ERR_OR_NULL(drvdata) || !drvdata->usb_bam_support)
+ return;
+
+ spin_lock_irqsave(&drvdata->spin_lock, flags);
CSR_UNLOCK(drvdata);
usbflshctrl = csr_readl(drvdata, CSR_USBFLSHCTRL);
@@ -132,14 +170,25 @@
csr_writel(drvdata, usbflshctrl, CSR_USBFLSHCTRL);
CSR_LOCK(drvdata);
+ spin_unlock_irqrestore(&drvdata->spin_lock, flags);
}
EXPORT_SYMBOL(msm_qdss_csr_disable_flush);
-int coresight_csr_hwctrl_set(uint64_t addr, uint32_t val)
+int coresight_csr_hwctrl_set(struct coresight_csr *csr, uint64_t addr,
+ uint32_t val)
{
- struct csr_drvdata *drvdata = csrdrvdata;
+ struct csr_drvdata *drvdata;
int ret = 0;
+ unsigned long flags;
+ if (csr == NULL)
+ return -EINVAL;
+
+ drvdata = to_csr_drvdata(csr);
+ if (IS_ERR_OR_NULL(drvdata) || !drvdata->hwctrl_set_support)
+ return -EINVAL;
+
+ spin_lock_irqsave(&drvdata->spin_lock, flags);
CSR_UNLOCK(drvdata);
if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL0))
@@ -154,15 +203,24 @@
ret = -EINVAL;
CSR_LOCK(drvdata);
-
+ spin_unlock_irqrestore(&drvdata->spin_lock, flags);
return ret;
}
EXPORT_SYMBOL(coresight_csr_hwctrl_set);
-void coresight_csr_set_byte_cntr(uint32_t count)
+void coresight_csr_set_byte_cntr(struct coresight_csr *csr, uint32_t count)
{
- struct csr_drvdata *drvdata = csrdrvdata;
+ struct csr_drvdata *drvdata;
+ unsigned long flags;
+ if (csr == NULL)
+ return;
+
+ drvdata = to_csr_drvdata(csr);
+ if (IS_ERR_OR_NULL(drvdata) || !drvdata->set_byte_cntr_support)
+ return;
+
+ spin_lock_irqsave(&drvdata->spin_lock, flags);
CSR_UNLOCK(drvdata);
csr_writel(drvdata, count, CSR_BYTECNTVAL);
@@ -171,9 +229,85 @@
mb();
CSR_LOCK(drvdata);
+ spin_unlock_irqrestore(&drvdata->spin_lock, flags);
}
EXPORT_SYMBOL(coresight_csr_set_byte_cntr);
+struct coresight_csr *coresight_csr_get(const char *name)
+{
+ struct coresight_csr *csr;
+
+ list_for_each_entry(csr, &csr_list, link) {
+ if (!strcmp(csr->name, name))
+ return csr;
+ }
+
+ return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL(coresight_csr_get);
+
+static ssize_t csr_show_timestamp(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ ssize_t size = 0;
+ uint64_t time_tick = 0;
+ uint32_t val, time_val0, time_val1;
+ int ret;
+ unsigned long flags;
+
+ struct csr_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+ if (IS_ERR_OR_NULL(drvdata) || !drvdata->timestamp_support) {
+ dev_err(dev, "Invalid param\n");
+ return 0;
+ }
+
+ ret = clk_prepare_enable(drvdata->clk);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&drvdata->spin_lock, flags);
+ CSR_UNLOCK(drvdata);
+
+ val = csr_readl(drvdata, CSR_TIMESTAMPCTRL);
+
+ val = val & ~BIT(0);
+ csr_writel(drvdata, val, CSR_TIMESTAMPCTRL);
+
+ val = val | BIT(0);
+ csr_writel(drvdata, val, CSR_TIMESTAMPCTRL);
+
+ time_val0 = csr_readl(drvdata, CSR_QDSSTIMEVAL0);
+ time_val1 = csr_readl(drvdata, CSR_QDSSTIMEVAL1);
+
+ CSR_LOCK(drvdata);
+ spin_unlock_irqrestore(&drvdata->spin_lock, flags);
+
+ clk_disable_unprepare(drvdata->clk);
+
+ time_tick |= (uint64_t)time_val1 << 32;
+ time_tick |= (uint64_t)time_val0;
+ size = scnprintf(buf, PAGE_SIZE, "%llu\n", time_tick);
+ dev_dbg(dev, "timestamp : %s\n", buf);
+ return size;
+}
+
+static DEVICE_ATTR(timestamp, 0444, csr_show_timestamp, NULL);
+
+static struct attribute *csr_attrs[] = {
+ &dev_attr_timestamp.attr,
+ NULL,
+};
+
+static struct attribute_group csr_attr_grp = {
+ .attrs = csr_attrs,
+};
+static const struct attribute_group *csr_attr_grps[] = {
+ &csr_attr_grp,
+ NULL,
+};
+
static int csr_probe(struct platform_device *pdev)
{
int ret;
@@ -194,6 +328,10 @@
drvdata->dev = &pdev->dev;
platform_set_drvdata(pdev, drvdata);
+ drvdata->clk = devm_clk_get(dev, "apb_pclk");
+ if (IS_ERR(drvdata->clk))
+ dev_dbg(dev, "csr not config clk\n");
+
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr-base");
if (!res)
return -ENODEV;
@@ -208,27 +346,65 @@
if (ret)
drvdata->blksize = BLKSIZE_256;
+ drvdata->usb_bam_support = of_property_read_bool(pdev->dev.of_node,
+ "qcom,usb-bam-support");
+ if (!drvdata->usb_bam_support)
+ dev_dbg(dev, "usb_bam support handled by other subsystem\n");
+ else
+ dev_dbg(dev, "usb_bam operation supported\n");
+
+ drvdata->hwctrl_set_support = of_property_read_bool(pdev->dev.of_node,
+ "qcom,hwctrl-set-support");
+ if (!drvdata->hwctrl_set_support)
+ dev_dbg(dev, "hwctrl_set_support handled by other subsystem\n");
+ else
+ dev_dbg(dev, "hwctrl_set_support operation supported\n");
+
+ drvdata->set_byte_cntr_support = of_property_read_bool(
+ pdev->dev.of_node, "qcom,set-byte-cntr-support");
+ if (!drvdata->set_byte_cntr_support)
+ dev_dbg(dev, "set byte_cntr_support handled by other subsystem\n");
+ else
+ dev_dbg(dev, "set_byte_cntr_support operation supported\n");
+
+ drvdata->timestamp_support = of_property_read_bool(pdev->dev.of_node,
+ "qcom,timestamp-support");
+ if (!drvdata->timestamp_support)
+ dev_dbg(dev, "timestamp_support handled by other subsystem\n");
+ else
+ dev_dbg(dev, "timestamp_support operation supported\n");
+
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
desc->type = CORESIGHT_DEV_TYPE_NONE;
desc->pdata = pdev->dev.platform_data;
desc->dev = &pdev->dev;
+ if (drvdata->timestamp_support)
+ desc->groups = csr_attr_grps;
+
drvdata->csdev = coresight_register(desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
/* Store the driver data pointer for use in exported functions */
- csrdrvdata = drvdata;
- dev_info(dev, "CSR initialized\n");
+ spin_lock_init(&drvdata->spin_lock);
+ drvdata->csr.name = ((struct coresight_platform_data *)
+ (pdev->dev.platform_data))->name;
+ list_add_tail(&drvdata->csr.link, &csr_list);
+
+ dev_info(dev, "CSR initialized: %s\n", drvdata->csr.name);
return 0;
}
static int csr_remove(struct platform_device *pdev)
{
+ unsigned long flags;
struct csr_drvdata *drvdata = platform_get_drvdata(pdev);
+ spin_lock_irqsave(&drvdata->spin_lock, flags);
coresight_unregister(drvdata->csdev);
+ spin_unlock_irqrestore(&drvdata->spin_lock, flags);
return 0;
}
diff --git a/drivers/hwtracing/coresight/coresight-hwevent.c b/drivers/hwtracing/coresight/coresight-hwevent.c
index 22e9d6f..1e8872b 100644
--- a/drivers/hwtracing/coresight/coresight-hwevent.c
+++ b/drivers/hwtracing/coresight/coresight-hwevent.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -42,6 +42,8 @@
struct regulator **hreg;
int nr_hmux;
struct hwevent_mux *hmux;
+ struct coresight_csr *csr;
+ const char *csr_name;
};
static int hwevent_enable(struct hwevent_drvdata *drvdata)
@@ -132,7 +134,7 @@
}
if (i == drvdata->nr_hmux) {
- ret = coresight_csr_hwctrl_set(addr, val);
+ ret = coresight_csr_hwctrl_set(drvdata->csr, addr, val);
if (ret) {
dev_err(dev, "invalid mux control register address\n");
ret = -EINVAL;
@@ -185,6 +187,17 @@
drvdata->dev = &pdev->dev;
platform_set_drvdata(pdev, drvdata);
+ ret = of_get_coresight_csr_name(dev->of_node, &drvdata->csr_name);
+ if (ret) {
+ dev_err(dev, "No csr data\n");
+ } else{
+ drvdata->csr = coresight_csr_get(drvdata->csr_name);
+ if (IS_ERR(drvdata->csr)) {
+ dev_err(dev, "failed to get csr, defer probe\n");
+ return -EPROBE_DEFER;
+ }
+ }
+
drvdata->nr_hmux = of_property_count_strings(pdev->dev.of_node,
"reg-names");
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index afe9f3d..ba721fd 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -1,5 +1,4 @@
-/* Copyright (c) 2011-2012, 2016-2017, The Linux Foundation.
- * All rights reserved.
+/* Copyright (c) 2011-2012, 2016-2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -73,6 +72,11 @@
CS_MODE_PERF,
};
+struct coresight_csr {
+ const char *name;
+ struct list_head link;
+};
+
/**
* struct cs_buffer - keep track of a recording session' specifics
* @cur: index of the current buffer
@@ -149,18 +153,24 @@
#endif
#ifdef CONFIG_CORESIGHT_CSR
-extern void msm_qdss_csr_enable_bam_to_usb(void);
-extern void msm_qdss_csr_disable_bam_to_usb(void);
-extern void msm_qdss_csr_disable_flush(void);
-extern int coresight_csr_hwctrl_set(uint64_t addr, uint32_t val);
-extern void coresight_csr_set_byte_cntr(uint32_t count);
+extern void msm_qdss_csr_enable_bam_to_usb(struct coresight_csr *csr);
+extern void msm_qdss_csr_disable_bam_to_usb(struct coresight_csr *csr);
+extern void msm_qdss_csr_disable_flush(struct coresight_csr *csr);
+extern int coresight_csr_hwctrl_set(struct coresight_csr *csr, uint64_t addr,
+ uint32_t val);
+extern void coresight_csr_set_byte_cntr(struct coresight_csr *csr,
+ uint32_t count);
+extern struct coresight_csr *coresight_csr_get(const char *name);
#else
-static inline void msm_qdss_csr_enable_bam_to_usb(void) {}
-static inline void msm_qdss_csr_disable_bam_to_usb(void) {}
-static inline void msm_qdss_csr_disable_flush(void) {}
-static inline int coresight_csr_hwctrl_set(uint64_t addr,
- uint32_t val) { return -EINVAL; }
-static inline void coresight_csr_set_byte_cntr(uint32_t count) {}
+static inline void msm_qdss_csr_enable_bam_to_usb(struct coresight_csr *csr) {}
+static inline void msm_qdss_csr_disable_bam_to_usb(struct coresight_csr *csr) {}
+static inline void msm_qdss_csr_disable_flush(struct coresight_csr *csr) {}
+static inline int coresight_csr_hwctrl_set(struct coresight_csr *csr,
+ uint64_t addr, uint32_t val) { return -EINVAL; }
+static inline void coresight_csr_set_byte_cntr(struct coresight_csr *csr,
+ uint32_t count) {}
+static inline struct coresight_csr *coresight_csr_get(const char *name)
+ { return NULL; }
#endif
#endif
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index eb70e7a..dcdc3f2 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
* Copyright(C) 2016 Linaro Limited. All rights reserved.
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
*
@@ -579,7 +579,7 @@
return;
/* Configure and enable required CSR registers */
- msm_qdss_csr_enable_bam_to_usb();
+ msm_qdss_csr_enable_bam_to_usb(drvdata->csr);
/* Configure and enable ETR for usb bam output */
@@ -675,7 +675,7 @@
return;
/* Ensure periodic flush is disabled in CSR block */
- msm_qdss_csr_disable_flush();
+ msm_qdss_csr_disable_flush(drvdata->csr);
CS_UNLOCK(drvdata->base);
@@ -685,7 +685,7 @@
CS_LOCK(drvdata);
/* Disable CSR configuration */
- msm_qdss_csr_disable_bam_to_usb();
+ msm_qdss_csr_disable_bam_to_usb(drvdata->csr);
drvdata->enable_to_bam = false;
}
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 6f13eb3..802d4f1 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2017-2018, The Linux Foundation. All rights reserved.
*
* Description: CoreSight Trace Memory Controller driver
*
@@ -611,6 +611,17 @@
dev_err(dev, "failed to get reset cti\n");
}
+ ret = of_get_coresight_csr_name(adev->dev.of_node, &drvdata->csr_name);
+ if (ret) {
+ dev_err(dev, "No csr data\n");
+ } else{
+ drvdata->csr = coresight_csr_get(drvdata->csr_name);
+ if (IS_ERR(drvdata->csr)) {
+ dev_err(dev, "failed to get csr, defer probe\n");
+ return -EPROBE_DEFER;
+ }
+ }
+
desc.pdata = pdata;
desc.dev = dev;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index fe6bc76..36117ec 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -189,6 +189,8 @@
bool sticky_enable;
struct coresight_cti *cti_flush;
struct coresight_cti *cti_reset;
+ struct coresight_csr *csr;
+ const char *csr_name;
struct byte_cntr *byte_cntr;
};
diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c
index 5473fcf..be810fe 100644
--- a/drivers/hwtracing/coresight/of_coresight.c
+++ b/drivers/hwtracing/coresight/of_coresight.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -238,3 +238,21 @@
return ctidata;
}
EXPORT_SYMBOL(of_get_coresight_cti_data);
+
+int of_get_coresight_csr_name(struct device_node *node, const char **csr_name)
+{
+ int ret;
+ struct device_node *csr_node;
+
+ csr_node = of_parse_phandle(node, "coresight-csr", 0);
+ if (!csr_node)
+ return -EINVAL;
+
+ ret = of_property_read_string(csr_node, "coresight-name", csr_name);
+ of_node_put(csr_node);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL(of_get_coresight_csr_name);
diff --git a/drivers/infiniband/hw/cxgb4/cq.c b/drivers/infiniband/hw/cxgb4/cq.c
index 19c6477..a856371 100644
--- a/drivers/infiniband/hw/cxgb4/cq.c
+++ b/drivers/infiniband/hw/cxgb4/cq.c
@@ -575,10 +575,10 @@
ret = -EAGAIN;
goto skip_cqe;
}
- if (unlikely((CQE_WRID_MSN(hw_cqe) != (wq->rq.msn)))) {
+ if (unlikely(!CQE_STATUS(hw_cqe) &&
+ CQE_WRID_MSN(hw_cqe) != wq->rq.msn)) {
t4_set_wq_in_error(wq);
- hw_cqe->header |= htonl(CQE_STATUS_V(T4_ERR_MSN));
- goto proc_cqe;
+ hw_cqe->header |= cpu_to_be32(CQE_STATUS_V(T4_ERR_MSN));
}
goto proc_cqe;
}
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index a2120ff..5e29fbd 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -2575,6 +2575,18 @@
return ret;
}
+static u8 mlx5_get_umr_fence(u8 umr_fence_cap)
+{
+ switch (umr_fence_cap) {
+ case MLX5_CAP_UMR_FENCE_NONE:
+ return MLX5_FENCE_MODE_NONE;
+ case MLX5_CAP_UMR_FENCE_SMALL:
+ return MLX5_FENCE_MODE_INITIATOR_SMALL;
+ default:
+ return MLX5_FENCE_MODE_STRONG_ORDERING;
+ }
+}
+
static int create_dev_resources(struct mlx5_ib_resources *devr)
{
struct ib_srq_init_attr attr;
@@ -3101,6 +3113,8 @@
mlx5_ib_internal_fill_odp_caps(dev);
+ dev->umr_fence = mlx5_get_umr_fence(MLX5_CAP_GEN(mdev, umr_fence));
+
if (MLX5_CAP_GEN(mdev, imaicl)) {
dev->ib_dev.alloc_mw = mlx5_ib_alloc_mw;
dev->ib_dev.dealloc_mw = mlx5_ib_dealloc_mw;
diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
index 86e1e081..d5cc954 100644
--- a/drivers/infiniband/hw/mlx5/mlx5_ib.h
+++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
@@ -345,7 +345,7 @@
struct mlx5_ib_wq rq;
u8 sq_signal_bits;
- u8 fm_cache;
+ u8 next_fence;
struct mlx5_ib_wq sq;
/* serialize qp state modifications
@@ -643,6 +643,7 @@
struct list_head qp_list;
/* Array with num_ports elements */
struct mlx5_ib_port *port;
+ u8 umr_fence;
};
static inline struct mlx5_ib_cq *to_mibcq(struct mlx5_core_cq *mcq)
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
index 2665414..fdd1561 100644
--- a/drivers/infiniband/hw/mlx5/qp.c
+++ b/drivers/infiniband/hw/mlx5/qp.c
@@ -3755,24 +3755,6 @@
}
}
-static u8 get_fence(u8 fence, struct ib_send_wr *wr)
-{
- if (unlikely(wr->opcode == IB_WR_LOCAL_INV &&
- wr->send_flags & IB_SEND_FENCE))
- return MLX5_FENCE_MODE_STRONG_ORDERING;
-
- if (unlikely(fence)) {
- if (wr->send_flags & IB_SEND_FENCE)
- return MLX5_FENCE_MODE_SMALL_AND_FENCE;
- else
- return fence;
- } else if (unlikely(wr->send_flags & IB_SEND_FENCE)) {
- return MLX5_FENCE_MODE_FENCE;
- }
-
- return 0;
-}
-
static int begin_wqe(struct mlx5_ib_qp *qp, void **seg,
struct mlx5_wqe_ctrl_seg **ctrl,
struct ib_send_wr *wr, unsigned *idx,
@@ -3801,8 +3783,7 @@
static void finish_wqe(struct mlx5_ib_qp *qp,
struct mlx5_wqe_ctrl_seg *ctrl,
u8 size, unsigned idx, u64 wr_id,
- int nreq, u8 fence, u8 next_fence,
- u32 mlx5_opcode)
+ int nreq, u8 fence, u32 mlx5_opcode)
{
u8 opmod = 0;
@@ -3810,7 +3791,6 @@
mlx5_opcode | ((u32)opmod << 24));
ctrl->qpn_ds = cpu_to_be32(size | (qp->trans_qp.base.mqp.qpn << 8));
ctrl->fm_ce_se |= fence;
- qp->fm_cache = next_fence;
if (unlikely(qp->wq_sig))
ctrl->signature = wq_sig(ctrl);
@@ -3870,7 +3850,6 @@
goto out;
}
- fence = qp->fm_cache;
num_sge = wr->num_sge;
if (unlikely(num_sge > qp->sq.max_gs)) {
mlx5_ib_warn(dev, "\n");
@@ -3887,6 +3866,19 @@
goto out;
}
+ if (wr->opcode == IB_WR_LOCAL_INV ||
+ wr->opcode == IB_WR_REG_MR) {
+ fence = dev->umr_fence;
+ next_fence = MLX5_FENCE_MODE_INITIATOR_SMALL;
+ } else if (wr->send_flags & IB_SEND_FENCE) {
+ if (qp->next_fence)
+ fence = MLX5_FENCE_MODE_SMALL_AND_FENCE;
+ else
+ fence = MLX5_FENCE_MODE_FENCE;
+ } else {
+ fence = qp->next_fence;
+ }
+
switch (ibqp->qp_type) {
case IB_QPT_XRC_INI:
xrc = seg;
@@ -3913,7 +3905,6 @@
goto out;
case IB_WR_LOCAL_INV:
- next_fence = MLX5_FENCE_MODE_INITIATOR_SMALL;
qp->sq.wr_data[idx] = IB_WR_LOCAL_INV;
ctrl->imm = cpu_to_be32(wr->ex.invalidate_rkey);
set_linv_wr(qp, &seg, &size);
@@ -3921,7 +3912,6 @@
break;
case IB_WR_REG_MR:
- next_fence = MLX5_FENCE_MODE_INITIATOR_SMALL;
qp->sq.wr_data[idx] = IB_WR_REG_MR;
ctrl->imm = cpu_to_be32(reg_wr(wr)->key);
err = set_reg_wr(qp, reg_wr(wr), &seg, &size);
@@ -3944,9 +3934,8 @@
goto out;
}
- finish_wqe(qp, ctrl, size, idx, wr->wr_id,
- nreq, get_fence(fence, wr),
- next_fence, MLX5_OPCODE_UMR);
+ finish_wqe(qp, ctrl, size, idx, wr->wr_id, nreq,
+ fence, MLX5_OPCODE_UMR);
/*
* SET_PSV WQEs are not signaled and solicited
* on error
@@ -3971,9 +3960,8 @@
goto out;
}
- finish_wqe(qp, ctrl, size, idx, wr->wr_id,
- nreq, get_fence(fence, wr),
- next_fence, MLX5_OPCODE_SET_PSV);
+ finish_wqe(qp, ctrl, size, idx, wr->wr_id, nreq,
+ fence, MLX5_OPCODE_SET_PSV);
err = begin_wqe(qp, &seg, &ctrl, wr,
&idx, &size, nreq);
if (err) {
@@ -3983,7 +3971,6 @@
goto out;
}
- next_fence = MLX5_FENCE_MODE_INITIATOR_SMALL;
err = set_psv_wr(&sig_handover_wr(wr)->sig_attrs->wire,
mr->sig->psv_wire.psv_idx, &seg,
&size);
@@ -3993,9 +3980,9 @@
goto out;
}
- finish_wqe(qp, ctrl, size, idx, wr->wr_id,
- nreq, get_fence(fence, wr),
- next_fence, MLX5_OPCODE_SET_PSV);
+ finish_wqe(qp, ctrl, size, idx, wr->wr_id, nreq,
+ fence, MLX5_OPCODE_SET_PSV);
+ qp->next_fence = MLX5_FENCE_MODE_INITIATOR_SMALL;
num_sge = 0;
goto skip_psv;
@@ -4100,8 +4087,8 @@
}
}
- finish_wqe(qp, ctrl, size, idx, wr->wr_id, nreq,
- get_fence(fence, wr), next_fence,
+ qp->next_fence = next_fence;
+ finish_wqe(qp, ctrl, size, idx, wr->wr_id, nreq, fence,
mlx5_ib_opcode[wr->opcode]);
skip_psv:
if (0)
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
index 39d2837..0983470 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.c
+++ b/drivers/infiniband/ulp/isert/ib_isert.c
@@ -747,6 +747,7 @@
{
struct isert_conn *isert_conn = cma_id->qp->qp_context;
+ ib_drain_qp(isert_conn->qp);
list_del_init(&isert_conn->node);
isert_conn->cm_id = NULL;
isert_put_conn(isert_conn);
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index b974897..29ab814 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -992,8 +992,7 @@
return -ENOMEM;
attr->qp_state = IB_QPS_INIT;
- attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE | IB_ACCESS_REMOTE_READ |
- IB_ACCESS_REMOTE_WRITE;
+ attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE;
attr->port_num = ch->sport->port;
attr->pkey_index = 0;
diff --git a/drivers/input/misc/qpnp-power-on.c b/drivers/input/misc/qpnp-power-on.c
index febcd9c..5fa0d4b 100644
--- a/drivers/input/misc/qpnp-power-on.c
+++ b/drivers/input/misc/qpnp-power-on.c
@@ -212,6 +212,9 @@
int warm_reset_poff_type;
int hard_reset_poff_type;
int shutdown_poff_type;
+ int resin_warm_reset_type;
+ int resin_hard_reset_type;
+ int resin_shutdown_type;
u16 base;
u8 subtype;
u8 pon_ver;
@@ -219,7 +222,12 @@
u8 warm_reset_reason2;
bool is_spon;
bool store_hard_reset_reason;
+ bool resin_hard_reset_disable;
+ bool resin_shutdown_disable;
+ bool ps_hold_hard_reset_disable;
+ bool ps_hold_shutdown_disable;
bool kpdpwr_dbc_enable;
+ bool resin_pon_reset;
ktime_t kpdpwr_last_release_time;
};
@@ -478,6 +486,7 @@
enum pon_power_off_type type)
{
int rc;
+ bool disable = false;
u16 rst_en_reg;
if (pon->pon_ver == QPNP_PON_GEN1_V1)
@@ -497,10 +506,12 @@
case PON_POWER_OFF_HARD_RESET:
if (pon->hard_reset_poff_type != -EINVAL)
type = pon->hard_reset_poff_type;
+ disable = pon->ps_hold_hard_reset_disable;
break;
case PON_POWER_OFF_SHUTDOWN:
if (pon->shutdown_poff_type != -EINVAL)
type = pon->shutdown_poff_type;
+ disable = pon->ps_hold_shutdown_disable;
break;
default:
break;
@@ -513,6 +524,13 @@
rst_en_reg, rc);
/*
+ * Check if ps-hold power off configuration needs to be disabled.
+ * If yes, then return without configuring.
+ */
+ if (disable)
+ return rc;
+
+ /*
* We need 10 sleep clock cycles here. But since the clock is
* internally generated, we need to add 50% tolerance to be
* conservative.
@@ -533,7 +551,80 @@
"Unable to write to addr=%hx, rc(%d)\n",
rst_en_reg, rc);
- dev_dbg(&pon->pdev->dev, "power off type = 0x%02X\n", type);
+ dev_dbg(&pon->pdev->dev, "ps_hold power off type = 0x%02X\n", type);
+ return rc;
+}
+
+static int qpnp_resin_pon_reset_config(struct qpnp_pon *pon,
+ enum pon_power_off_type type)
+{
+ int rc;
+ bool disable = false;
+ u16 rst_en_reg;
+
+ if (pon->pon_ver == QPNP_PON_GEN1_V1)
+ rst_en_reg = QPNP_PON_RESIN_S2_CNTL(pon);
+ else
+ rst_en_reg = QPNP_PON_RESIN_S2_CNTL2(pon);
+
+ /*
+ * Based on the poweroff type set for a PON device through device tree
+ * change the type being configured into PON_RESIN_S2_CTL.
+ */
+ switch (type) {
+ case PON_POWER_OFF_WARM_RESET:
+ if (pon->resin_warm_reset_type != -EINVAL)
+ type = pon->resin_warm_reset_type;
+ break;
+ case PON_POWER_OFF_HARD_RESET:
+ if (pon->resin_hard_reset_type != -EINVAL)
+ type = pon->resin_hard_reset_type;
+ disable = pon->resin_hard_reset_disable;
+ break;
+ case PON_POWER_OFF_SHUTDOWN:
+ if (pon->resin_shutdown_type != -EINVAL)
+ type = pon->resin_shutdown_type;
+ disable = pon->resin_shutdown_disable;
+ break;
+ default:
+ break;
+ }
+
+ rc = qpnp_pon_masked_write(pon, rst_en_reg, QPNP_PON_S2_CNTL_EN, 0);
+ if (rc)
+ dev_err(&pon->pdev->dev,
+ "Unable to write to addr=%hx, rc(%d)\n",
+ rst_en_reg, rc);
+
+ /*
+ * Check if resin power off configuration needs to be disabled.
+ * If yes, then return without configuring.
+ */
+ if (disable)
+ return rc;
+
+ /*
+ * We need 10 sleep clock cycles here. But since the clock is
+ * internally generated, we need to add 50% tolerance to be
+ * conservative.
+ */
+ udelay(500);
+
+ rc = qpnp_pon_masked_write(pon, QPNP_PON_RESIN_S2_CNTL(pon),
+ QPNP_PON_S2_CNTL_TYPE_MASK, type);
+ if (rc)
+ dev_err(&pon->pdev->dev,
+ "Unable to write to addr=%x, rc(%d)\n",
+ QPNP_PON_RESIN_S2_CNTL(pon), rc);
+
+ rc = qpnp_pon_masked_write(pon, rst_en_reg, QPNP_PON_S2_CNTL_EN,
+ QPNP_PON_S2_CNTL_EN);
+ if (rc)
+ dev_err(&pon->pdev->dev,
+ "Unable to write to addr=%hx, rc(%d)\n",
+ rst_en_reg, rc);
+
+ dev_dbg(&pon->pdev->dev, "resin power off type = 0x%02X\n", type);
return rc;
}
@@ -588,6 +679,15 @@
rc);
goto out;
}
+ if (pon->resin_pon_reset) {
+ rc = qpnp_resin_pon_reset_config(pon, type);
+ if (rc) {
+ dev_err(&pon->pdev->dev,
+ "Error configuring secondary PON resin rc: %d\n",
+ rc);
+ goto out;
+ }
+ }
}
/* Set ship mode here if it has been requested */
if (!!pon_ship_mode_en) {
@@ -2337,6 +2437,69 @@
pon->shutdown_poff_type = -EINVAL;
}
+ pon->ps_hold_hard_reset_disable =
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,ps-hold-hard-reset-disable");
+ pon->ps_hold_shutdown_disable = of_property_read_bool(pdev->dev.of_node,
+ "qcom,ps-hold-shutdown-disable");
+
+
+ pon->resin_pon_reset = of_property_read_bool(pdev->dev.of_node,
+ "qcom,resin-pon-reset");
+
+ rc = of_property_read_u32(pon->pdev->dev.of_node,
+ "qcom,resin-warm-reset-type",
+ &pon->resin_warm_reset_type);
+ if (rc) {
+ if (rc != -EINVAL) {
+ dev_err(&pdev->dev, "Unable to read resin warm reset poweroff type rc: %d\n",
+ rc);
+ goto err_out;
+ }
+ pon->resin_warm_reset_type = -EINVAL;
+ } else if (pon->resin_warm_reset_type <= PON_POWER_OFF_RESERVED ||
+ pon->resin_warm_reset_type >= PON_POWER_OFF_MAX_TYPE) {
+ dev_err(&pdev->dev, "Invalid resin-warm-reset-type\n");
+ pon->resin_warm_reset_type = -EINVAL;
+ }
+
+ rc = of_property_read_u32(pon->pdev->dev.of_node,
+ "qcom,resin-hard-reset-type",
+ &pon->resin_hard_reset_type);
+ if (rc) {
+ if (rc != -EINVAL) {
+ dev_err(&pdev->dev, "Unable to read resin hard reset poweroff type rc: %d\n",
+ rc);
+ goto err_out;
+ }
+ pon->resin_hard_reset_type = -EINVAL;
+ } else if (pon->resin_hard_reset_type <= PON_POWER_OFF_RESERVED ||
+ pon->resin_hard_reset_type >= PON_POWER_OFF_MAX_TYPE) {
+ dev_err(&pdev->dev, "Invalid resin-hard-reset-type\n");
+ pon->resin_hard_reset_type = -EINVAL;
+ }
+
+ rc = of_property_read_u32(pon->pdev->dev.of_node,
+ "qcom,resin-shutdown-type",
+ &pon->resin_shutdown_type);
+ if (rc) {
+ if (rc != -EINVAL) {
+ dev_err(&pdev->dev, "Unable to read resin shutdown poweroff type rc: %d\n",
+ rc);
+ goto err_out;
+ }
+ pon->resin_shutdown_type = -EINVAL;
+ } else if (pon->resin_shutdown_type <= PON_POWER_OFF_RESERVED ||
+ pon->resin_shutdown_type >= PON_POWER_OFF_MAX_TYPE) {
+ dev_err(&pdev->dev, "Invalid resin-shutdown-type\n");
+ pon->resin_shutdown_type = -EINVAL;
+ }
+
+ pon->resin_hard_reset_disable = of_property_read_bool(pdev->dev.of_node,
+ "qcom,resin-hard-reset-disable");
+ pon->resin_shutdown_disable = of_property_read_bool(pdev->dev.of_node,
+ "qcom,resin-shutdown-disable");
+
rc = device_create_file(&pdev->dev, &dev_attr_debounce_us);
if (rc) {
dev_err(&pdev->dev, "sys file creation failed rc: %d\n", rc);
@@ -2365,7 +2528,8 @@
return 0;
err_out:
- sys_reset_dev = NULL;
+ if (sys_reset)
+ sys_reset_dev = NULL;
return rc;
}
diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c
index caa5a62..15929d8 100644
--- a/drivers/input/misc/twl4030-vibra.c
+++ b/drivers/input/misc/twl4030-vibra.c
@@ -178,12 +178,14 @@
twl4030_vibra_suspend, twl4030_vibra_resume);
static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata,
- struct device_node *node)
+ struct device_node *parent)
{
+ struct device_node *node;
+
if (pdata && pdata->coexist)
return true;
- node = of_find_node_by_name(node, "codec");
+ node = of_get_child_by_name(parent, "codec");
if (node) {
of_node_put(node);
return true;
diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c
index 5690eb7..15e0d35 100644
--- a/drivers/input/misc/twl6040-vibra.c
+++ b/drivers/input/misc/twl6040-vibra.c
@@ -248,8 +248,7 @@
int vddvibr_uV = 0;
int error;
- of_node_get(twl6040_core_dev->of_node);
- twl6040_core_node = of_find_node_by_name(twl6040_core_dev->of_node,
+ twl6040_core_node = of_get_child_by_name(twl6040_core_dev->of_node,
"vibra");
if (!twl6040_core_node) {
dev_err(&pdev->dev, "parent of node is missing?\n");
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index f26807c..af83d2e 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -1247,29 +1247,32 @@
case SS4_PACKET_ID_MULTI:
if (priv->flags & ALPS_BUTTONPAD) {
if (IS_SS4PLUS_DEV(priv->dev_id)) {
- f->mt[0].x = SS4_PLUS_BTL_MF_X_V2(p, 0);
- f->mt[1].x = SS4_PLUS_BTL_MF_X_V2(p, 1);
+ f->mt[2].x = SS4_PLUS_BTL_MF_X_V2(p, 0);
+ f->mt[3].x = SS4_PLUS_BTL_MF_X_V2(p, 1);
+ no_data_x = SS4_PLUS_MFPACKET_NO_AX_BL;
} else {
f->mt[2].x = SS4_BTL_MF_X_V2(p, 0);
f->mt[3].x = SS4_BTL_MF_X_V2(p, 1);
+ no_data_x = SS4_MFPACKET_NO_AX_BL;
}
+ no_data_y = SS4_MFPACKET_NO_AY_BL;
f->mt[2].y = SS4_BTL_MF_Y_V2(p, 0);
f->mt[3].y = SS4_BTL_MF_Y_V2(p, 1);
- no_data_x = SS4_MFPACKET_NO_AX_BL;
- no_data_y = SS4_MFPACKET_NO_AY_BL;
} else {
if (IS_SS4PLUS_DEV(priv->dev_id)) {
- f->mt[0].x = SS4_PLUS_STD_MF_X_V2(p, 0);
- f->mt[1].x = SS4_PLUS_STD_MF_X_V2(p, 1);
+ f->mt[2].x = SS4_PLUS_STD_MF_X_V2(p, 0);
+ f->mt[3].x = SS4_PLUS_STD_MF_X_V2(p, 1);
+ no_data_x = SS4_PLUS_MFPACKET_NO_AX;
} else {
- f->mt[0].x = SS4_STD_MF_X_V2(p, 0);
- f->mt[1].x = SS4_STD_MF_X_V2(p, 1);
+ f->mt[2].x = SS4_STD_MF_X_V2(p, 0);
+ f->mt[3].x = SS4_STD_MF_X_V2(p, 1);
+ no_data_x = SS4_MFPACKET_NO_AX;
}
+ no_data_y = SS4_MFPACKET_NO_AY;
+
f->mt[2].y = SS4_STD_MF_Y_V2(p, 0);
f->mt[3].y = SS4_STD_MF_Y_V2(p, 1);
- no_data_x = SS4_MFPACKET_NO_AX;
- no_data_y = SS4_MFPACKET_NO_AY;
}
f->first_mp = 0;
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
index 7931237..9bc2bab 100644
--- a/drivers/input/mouse/alps.h
+++ b/drivers/input/mouse/alps.h
@@ -120,10 +120,12 @@
#define SS4_IS_5F_DETECTED(_b) ((_b[2] & 0x10) == 0x10)
-#define SS4_MFPACKET_NO_AX 8160 /* X-Coordinate value */
-#define SS4_MFPACKET_NO_AY 4080 /* Y-Coordinate value */
-#define SS4_MFPACKET_NO_AX_BL 8176 /* Buttonless X-Coordinate value */
-#define SS4_MFPACKET_NO_AY_BL 4088 /* Buttonless Y-Coordinate value */
+#define SS4_MFPACKET_NO_AX 8160 /* X-Coordinate value */
+#define SS4_MFPACKET_NO_AY 4080 /* Y-Coordinate value */
+#define SS4_MFPACKET_NO_AX_BL 8176 /* Buttonless X-Coord value */
+#define SS4_MFPACKET_NO_AY_BL 4088 /* Buttonless Y-Coord value */
+#define SS4_PLUS_MFPACKET_NO_AX 4080 /* SS4 PLUS, X */
+#define SS4_PLUS_MFPACKET_NO_AX_BL 4088 /* Buttonless SS4 PLUS, X */
/*
* enum V7_PACKET_ID - defines the packet type for V7
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index cd834da..59603a5 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1609,7 +1609,7 @@
case 5:
etd->hw_version = 3;
break;
- case 6 ... 14:
+ case 6 ... 15:
etd->hw_version = 4;
break;
default:
diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c
index 7e2dc5e..0b49f29 100644
--- a/drivers/input/mouse/trackpoint.c
+++ b/drivers/input/mouse/trackpoint.c
@@ -383,6 +383,9 @@
if (trackpoint_read(&psmouse->ps2dev, TP_EXT_BTN, &button_info)) {
psmouse_warn(psmouse, "failed to get extended button data, assuming 3 buttons\n");
button_info = 0x33;
+ } else if (!button_info) {
+ psmouse_warn(psmouse, "got 0 in extended button data, assuming 3 buttons\n");
+ button_info = 0x33;
}
psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
diff --git a/drivers/input/touchscreen/88pm860x-ts.c b/drivers/input/touchscreen/88pm860x-ts.c
index 251ff2a..7a0dbce 100644
--- a/drivers/input/touchscreen/88pm860x-ts.c
+++ b/drivers/input/touchscreen/88pm860x-ts.c
@@ -126,7 +126,7 @@
int data, n, ret;
if (!np)
return -ENODEV;
- np = of_find_node_by_name(np, "touch");
+ np = of_get_child_by_name(np, "touch");
if (!np) {
dev_err(&pdev->dev, "Can't find touch node\n");
return -EINVAL;
@@ -144,13 +144,13 @@
if (data) {
ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
if (ret < 0)
- return -EINVAL;
+ goto err_put_node;
}
/* set tsi prebias time */
if (!of_property_read_u32(np, "marvell,88pm860x-tsi-prebias", &data)) {
ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
if (ret < 0)
- return -EINVAL;
+ goto err_put_node;
}
/* set prebias & prechg time of pen detect */
data = 0;
@@ -161,10 +161,18 @@
if (data) {
ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
if (ret < 0)
- return -EINVAL;
+ goto err_put_node;
}
of_property_read_u32(np, "marvell,88pm860x-resistor-X", res_x);
+
+ of_node_put(np);
+
return 0;
+
+err_put_node:
+ of_node_put(np);
+
+ return -EINVAL;
}
#else
#define pm860x_touch_dt_init(x, y, z) (-1)
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 36777b3..86168b9 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -11,6 +11,8 @@
if INPUT_TOUCHSCREEN
+source "drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig"
+
config TOUCHSCREEN_PROPERTIES
def_tristate INPUT
depends on INPUT
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 0caab59..7ac5a98 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -72,6 +72,7 @@
obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o
obj-$(CONFIG_TOUCHSCREEN_SURFACE3_SPI) += surface3_spi.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX) += synaptics_dsx/
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_v26) += synaptics_dsx_2.6/
obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
diff --git a/drivers/input/touchscreen/synaptics_dsx/Kconfig b/drivers/input/touchscreen/synaptics_dsx/Kconfig
index b2fa115..b54e792 100644
--- a/drivers/input/touchscreen/synaptics_dsx/Kconfig
+++ b/drivers/input/touchscreen/synaptics_dsx/Kconfig
@@ -59,6 +59,16 @@
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_fw_update.
+config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
+ bool "Synaptics DSX firmware update sysfs attributes"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE
+ help
+ Say Y here to enable support for sysfs attributes for
+ performing firmware update in a development environment.
+ This does not affect the core or other subsystem attributes.
+
+ If unsure, say N.
+
config TOUCHSCREEN_SYNAPTICS_DSX_TEST_REPORTING
tristate "Synaptics DSX test reporting module"
depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
index 7f62e01..395def9 100644
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
@@ -137,6 +137,7 @@
static int fwu_recovery_check_status(void);
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static ssize_t fwu_sysfs_show_image(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count);
@@ -201,6 +202,8 @@
struct device_attribute *attr, char *buf);
#endif
+#endif
+
enum f34_version {
F34_V0 = 0,
F34_V1,
@@ -757,6 +760,7 @@
struct work_struct fwu_work;
};
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static struct bin_attribute dev_attr_data = {
.attr = {
.name = "data",
@@ -766,8 +770,10 @@
.read = fwu_sysfs_show_image,
.write = fwu_sysfs_store_image,
};
+#endif
static struct device_attribute attrs[] = {
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
__ATTR(dorecovery, 0220,
synaptics_rmi4_show_error,
fwu_sysfs_do_recovery_store),
@@ -821,13 +827,16 @@
fwu_sysfs_read_lockdown_code_show,
fwu_sysfs_write_lockdown_code_store),
#endif
+#endif
};
static struct synaptics_rmi4_fwu_handle *fwu;
DECLARE_COMPLETION(fwu_remove_complete);
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
DEFINE_MUTEX(fwu_sysfs_mutex);
+#endif
static void calculate_checksum(unsigned short *data, unsigned long len,
unsigned long *result)
@@ -3061,6 +3070,7 @@
return 0;
}
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static int fwu_check_pm_configuration_size(void)
{
unsigned short block_count;
@@ -3077,6 +3087,7 @@
return 0;
}
+#endif
static int fwu_check_bl_configuration_size(void)
{
@@ -3444,6 +3455,7 @@
return fwu_write_configuration();
}
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static int fwu_write_pm_configuration(void)
{
fwu->config_area = PM_CONFIG_AREA;
@@ -3469,6 +3481,7 @@
return 0;
}
#endif
+#endif
static int fwu_write_flash_configuration(void)
{
@@ -3757,6 +3770,7 @@
return retval;
}
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static int fwu_do_read_config(void)
{
int retval;
@@ -3984,6 +3998,7 @@
return retval;
}
#endif
+#endif
static int fwu_do_lockdown_v7(void)
{
@@ -4134,6 +4149,7 @@
}
#endif
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static int fwu_start_write_guest_code(void)
{
int retval;
@@ -4339,6 +4355,7 @@
return retval;
}
+#endif
static int fwu_start_reflash(void)
{
@@ -4970,6 +4987,7 @@
}
#endif
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static ssize_t fwu_sysfs_show_image(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count)
@@ -5566,6 +5584,7 @@
return count;
}
#endif
+#endif
static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data,
unsigned char intr_mask)
{
@@ -5668,6 +5687,7 @@
if (ENABLE_SYS_REFLASH == false)
return 0;
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj,
&dev_attr_data);
if (retval < 0) {
@@ -5676,6 +5696,7 @@
__func__);
goto exit_free_mem;
}
+#endif
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
@@ -5697,7 +5718,9 @@
&attrs[attr_count].attr);
}
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
+#endif
exit_free_mem:
kfree(fwu->image_name);
@@ -5739,7 +5762,9 @@
&attrs[attr_count].attr);
}
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
+#endif
exit:
complete(&fwu_remove_complete);
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c
index 8776d4a..7725cd3 100644
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c
@@ -402,11 +402,11 @@
struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
struct i2c_msg msg[2];
+ mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
+
retval = synaptics_rmi4_i2c_alloc_buf(rmi4_data, length + 1);
if (retval < 0)
- return retval;
-
- mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
+ goto exit;
retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr);
if (retval != PAGE_SELECT_LEN) {
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c
index 61cf979..331274e 100644
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c
@@ -567,16 +567,22 @@
return -EBADF;
}
- if (count == 0)
- return 0;
+ mutex_lock(&(dev_data->file_mutex));
+
+ if (*f_pos > REG_ADDR_LIMIT) {
+ retval = -EFAULT;
+ goto clean_up;
+ }
if (count > (REG_ADDR_LIMIT - *f_pos))
count = REG_ADDR_LIMIT - *f_pos;
+ if (count == 0) {
+ retval = 0;
+ goto clean_up;
+ }
address = (unsigned short)(*f_pos);
- mutex_lock(&(dev_data->file_mutex));
-
rmidev_allocate_buffer(count);
retval = synaptics_rmi4_reg_read(rmidev->rmi4_data,
@@ -638,18 +644,26 @@
return -EBADF;
}
- if (count == 0)
- return 0;
+ mutex_lock(&(dev_data->file_mutex));
+
+ if (*f_pos > REG_ADDR_LIMIT) {
+ retval = -EFAULT;
+ goto unlock;
+ }
if (count > (REG_ADDR_LIMIT - *f_pos))
count = REG_ADDR_LIMIT - *f_pos;
- mutex_lock(&(dev_data->file_mutex));
-
+ if (count == 0) {
+ retval = 0;
+ goto unlock;
+ }
rmidev_allocate_buffer(count);
- if (copy_from_user(rmidev->tmpbuf, buf, count))
+ if (copy_from_user(rmidev->tmpbuf, buf, count)) {
return -EFAULT;
+ goto unlock;
+ }
retval = synaptics_rmi4_reg_write(rmidev->rmi4_data,
*f_pos,
@@ -658,6 +672,7 @@
if (retval >= 0)
*f_pos += retval;
+unlock:
mutex_unlock(&(dev_data->file_mutex));
return retval;
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig b/drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig
index b40f26e..5389628 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig
@@ -1,33 +1,33 @@
#
-# Synaptics DSX touchscreen driver configuration
+# Synaptics DSX v2.6 touchscreen driver configuration
#
-menuconfig TOUCHSCREEN_SYNAPTICS_DSX
- bool "Synaptics DSX touchscreen"
- default N
+menuconfig TOUCHSCREEN_SYNAPTICS_DSX_v26
+ bool "Synaptics DSX v2.6 touchscreen"
+ default y
help
Say Y here if you have a Synaptics DSX touchscreen connected
to your system.
If unsure, say N.
-if TOUCHSCREEN_SYNAPTICS_DSX
+if TOUCHSCREEN_SYNAPTICS_DSX_v26
choice
- default TOUCHSCREEN_SYNAPTICS_DSX_I2C
- prompt "Synaptics DSX bus interface"
-config TOUCHSCREEN_SYNAPTICS_DSX_I2C
+ default TOUCHSCREEN_SYNAPTICS_DSX_I2C_v26
+ prompt "Synaptics DSX v2.6 bus interface"
+config TOUCHSCREEN_SYNAPTICS_DSX_I2C_v26
bool "RMI over I2C"
depends on I2C
-config TOUCHSCREEN_SYNAPTICS_DSX_SPI
+config TOUCHSCREEN_SYNAPTICS_DSX_SPI_v26
bool "RMI over SPI"
depends on SPI_MASTER
-config TOUCHSCREEN_SYNAPTICS_DSX_RMI_HID_I2C
+config TOUCHSCREEN_SYNAPTICS_DSX_RMI_HID_I2C_v26
bool "HID over I2C"
depends on I2C
endchoice
-config TOUCHSCREEN_SYNAPTICS_DSX_CORE
- tristate "Synaptics DSX core driver module"
+config TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
+ tristate "Synaptics DSX v2.6 core driver module"
depends on I2C || SPI_MASTER
help
Say Y here to enable basic touch reporting functionality.
@@ -37,9 +37,9 @@
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_core.
-config TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV
- tristate "Synaptics DSX RMI device module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
+config TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v26
+ tristate "Synaptics DSX v2.6 RMI device module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
help
Say Y here to enable support for direct RMI register access.
@@ -48,9 +48,9 @@
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_rmi_dev.
-config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE
- tristate "Synaptics DSX firmware update module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
+config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v26
+ tristate "Synaptics DSX v2.6 firmware update module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
help
Say Y here to enable support for doing firmware update.
@@ -59,9 +59,9 @@
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_fw_update.
-config TOUCHSCREEN_SYNAPTICS_DSX_TEST_REPORTING
- tristate "Synaptics DSX test reporting module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
+config TOUCHSCREEN_SYNAPTICS_DSX_TEST_REPORTING_v26
+ tristate "Synaptics DSX v2.6 test reporting module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
help
Say Y here to enable support for retrieving production test reports.
@@ -70,9 +70,9 @@
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_test_reporting.
-config TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY
- tristate "Synaptics DSX proximity module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
+config TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY_v26
+ tristate "Synaptics DSX v2.6 proximity module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
help
Say Y here to enable support for proximity functionality.
@@ -81,9 +81,9 @@
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_proximity.
-config TOUCHSCREEN_SYNAPTICS_DSX_ACTIVE_PEN
- tristate "Synaptics DSX active pen module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
+config TOUCHSCREEN_SYNAPTICS_DSX_ACTIVE_PEN_v26
+ tristate "Synaptics DSX v2.6 active pen module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
help
Say Y here to enable support for active pen functionality.
@@ -92,9 +92,9 @@
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_active_pen.
-config TOUCHSCREEN_SYNAPTICS_DSX_GESTURE
- tristate "Synaptics DSX user defined gesture module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
+config TOUCHSCREEN_SYNAPTICS_DSX_GESTURE_v26
+ tristate "Synaptics DSX v2.6 user defined gesture module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
help
Say Y here to enable support for user defined gesture functionality.
@@ -103,9 +103,9 @@
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_gesture.
-config TOUCHSCREEN_SYNAPTICS_DSX_VIDEO
- tristate "Synaptics DSX video module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
+config TOUCHSCREEN_SYNAPTICS_DSX_VIDEO_v26
+ tristate "Synaptics DSX v2.6 video module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
help
Say Y here to enable support for video communication functionality.
@@ -114,4 +114,14 @@
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_video.
+config SECURE_TOUCH_SYNAPTICS_DSX_V26
+ bool "Secure Touch support for Synaptics V2.6 Touchscreen"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_I2C_v26
+ help
+ Say Y here
+ -Synaptics DSX V2.6 touch driver is connected
+ -To enable secure touch for Synaptics DSX V2.6 touch driver
+
+ If unsure, say N.
+
endif
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/Makefile b/drivers/input/touchscreen/synaptics_dsx_2.6/Makefile
index 515bc17..e5e7215 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/Makefile
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/Makefile
@@ -4,14 +4,14 @@
# Each configuration option enables a list of files.
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_I2C) += synaptics_dsx_i2c.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_SPI) += synaptics_dsx_spi.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_HID_I2C) += synaptics_dsx_rmi_hid_i2c.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE) += synaptics_dsx_core.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV) += synaptics_dsx_rmi_dev.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE) += synaptics_dsx_fw_update.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_TEST_REPORTING) += synaptics_dsx_test_reporting.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY) += synaptics_dsx_proximity.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_ACTIVE_PEN) += synaptics_dsx_active_pen.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_GESTURE) += synaptics_dsx_gesture.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_VIDEO) += synaptics_dsx_video.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_I2C_v26) += synaptics_dsx_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_SPI_v26) += synaptics_dsx_spi.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_HID_I2C_v26) += synaptics_dsx_rmi_hid_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26) += synaptics_dsx_core.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v26) += synaptics_dsx_rmi_dev.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v26) += synaptics_dsx_fw_update.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_TEST_REPORTING_v26) += synaptics_dsx_test_reporting.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY_v26) += synaptics_dsx_proximity.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_ACTIVE_PEN_v26) += synaptics_dsx_active_pen.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_GESTURE_v26) += synaptics_dsx_gesture.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_VIDEO_v26) += synaptics_dsx_video.o
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_active_pen.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_active_pen.c
index 0e61708..db5324a 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_active_pen.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_active_pen.c
@@ -38,7 +38,7 @@
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define APEN_PHYS_NAME "synaptics_dsx/active_pen"
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c
index 1583299..7633767 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c
@@ -5,6 +5,7 @@
*
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -40,7 +41,7 @@
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#ifdef KERNEL_ABOVE_2_6_38
#include <linux/input/mt.h>
@@ -67,11 +68,11 @@
#define F12_DATA_15_WORKAROUND
#define IGNORE_FN_INIT_FAILURE
-/*
+
#define FB_READY_RESET
#define FB_READY_WAIT_MS 100
#define FB_READY_TIMEOUT_S 30
-*/
+
#define RPT_TYPE (1 << 0)
#define RPT_X_LSB (1 << 1)
#define RPT_X_MSB (1 << 2)
@@ -117,11 +118,11 @@
static int synaptics_rmi4_check_status(struct synaptics_rmi4_data *rmi4_data,
bool *was_in_bl_mode);
static int synaptics_rmi4_free_fingers(struct synaptics_rmi4_data *rmi4_data);
-static int synaptics_rmi4_reinit_device(struct synaptics_rmi4_data *rmi4_data);
static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data,
bool rebuild);
#ifdef CONFIG_FB
+static void synaptics_rmi4_fb_notify_resume_work(struct work_struct *work);
static int synaptics_rmi4_fb_notifier_cb(struct notifier_block *self,
unsigned long event, void *data);
#endif
@@ -172,6 +173,19 @@
static ssize_t synaptics_rmi4_virtual_key_map_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf);
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+static ssize_t synaptics_rmi4_secure_touch_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t synaptics_rmi4_secure_touch_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t synaptics_rmi4_secure_touch_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+#endif
+
+static irqreturn_t synaptics_rmi4_irq(int irq, void *data);
+
struct synaptics_rmi4_f01_device_status {
union {
struct {
@@ -596,27 +610,35 @@
static struct synaptics_dsx_button_map *vir_button_map;
static struct device_attribute attrs[] = {
- __ATTR(reset, S_IWUGO,
- synaptics_rmi4_show_error,
+ __ATTR(reset, 0220,
+ NULL,
synaptics_rmi4_f01_reset_store),
- __ATTR(productinfo, S_IRUGO,
+ __ATTR(productinfo, 0444,
synaptics_rmi4_f01_productinfo_show,
- synaptics_rmi4_store_error),
- __ATTR(buildid, S_IRUGO,
+ NULL),
+ __ATTR(buildid, 0444,
synaptics_rmi4_f01_buildid_show,
- synaptics_rmi4_store_error),
- __ATTR(flashprog, S_IRUGO,
+ NULL),
+ __ATTR(flashprog, 0444,
synaptics_rmi4_f01_flashprog_show,
- synaptics_rmi4_store_error),
- __ATTR(0dbutton, (S_IRUGO | S_IWUGO),
+ NULL),
+ __ATTR(0dbutton, 0664,
synaptics_rmi4_0dbutton_show,
synaptics_rmi4_0dbutton_store),
- __ATTR(suspend, S_IWUGO,
- synaptics_rmi4_show_error,
+ __ATTR(suspend, 0220,
+ NULL,
synaptics_rmi4_suspend_store),
- __ATTR(wake_gesture, (S_IRUGO | S_IWUGO),
+ __ATTR(wake_gesture, 0664,
synaptics_rmi4_wake_gesture_show,
synaptics_rmi4_wake_gesture_store),
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+ __ATTR(secure_touch_enable, 0664,
+ synaptics_rmi4_secure_touch_enable_show,
+ synaptics_rmi4_secure_touch_enable_store),
+ __ATTR(secure_touch, 0444,
+ synaptics_rmi4_secure_touch_show,
+ NULL),
+#endif
};
static struct kobj_attribute virtual_key_map_attr = {
@@ -627,6 +649,205 @@
.show = synaptics_rmi4_virtual_key_map_show,
};
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+static void synaptics_secure_touch_init(struct synaptics_rmi4_data *data)
+{
+ data->st_initialized = 0;
+ init_completion(&data->st_powerdown);
+ init_completion(&data->st_irq_processed);
+
+ /* Get clocks */
+ data->core_clk = devm_clk_get(data->pdev->dev.parent, "core_clk");
+ if (IS_ERR(data->core_clk)) {
+ dev_warn(data->pdev->dev.parent,
+ "%s: error on clk_get(core_clk): %ld\n", __func__,
+ PTR_ERR(data->core_clk));
+ data->core_clk = NULL;
+ }
+
+ data->iface_clk = devm_clk_get(data->pdev->dev.parent, "iface_clk");
+ if (IS_ERR(data->iface_clk)) {
+ dev_warn(data->pdev->dev.parent,
+ "%s: error on clk_get(iface_clk): %ld\n", __func__,
+ PTR_ERR(data->iface_clk));
+ data->iface_clk = NULL;
+ }
+
+ data->st_initialized = 1;
+}
+
+static void synaptics_secure_touch_notify(struct synaptics_rmi4_data *rmi4_data)
+{
+ sysfs_notify(&rmi4_data->input_dev->dev.kobj, NULL, "secure_touch");
+}
+
+static irqreturn_t synaptics_filter_interrupt(
+ struct synaptics_rmi4_data *rmi4_data)
+{
+ if (atomic_read(&rmi4_data->st_enabled)) {
+ if (atomic_cmpxchg(&rmi4_data->st_pending_irqs, 0, 1) == 0) {
+ reinit_completion(&rmi4_data->st_irq_processed);
+ synaptics_secure_touch_notify(rmi4_data);
+ wait_for_completion_interruptible(
+ &rmi4_data->st_irq_processed);
+ }
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+/*
+ * 'blocking' variable will have value 'true' when we want to prevent the driver
+ * from accessing the xPU/SMMU protected HW resources while the session is
+ * active.
+ */
+static void synaptics_secure_touch_stop(struct synaptics_rmi4_data *rmi4_data,
+ bool blocking)
+{
+ if (atomic_read(&rmi4_data->st_enabled)) {
+ atomic_set(&rmi4_data->st_pending_irqs, -1);
+ synaptics_secure_touch_notify(rmi4_data);
+ if (blocking)
+ wait_for_completion_interruptible(
+ &rmi4_data->st_powerdown);
+ }
+}
+
+#else
+static void synaptics_secure_touch_init(struct synaptics_rmi4_data *rmi4_data)
+{
+}
+
+static irqreturn_t synaptics_filter_interrupt(
+ struct synaptics_rmi4_data *rmi4_data)
+{
+ return IRQ_NONE;
+}
+
+static void synaptics_secure_touch_stop(struct synaptics_rmi4_data *rmi4_data,
+ bool blocking)
+{
+}
+#endif
+
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+static ssize_t synaptics_rmi4_secure_touch_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%d",
+ atomic_read(&rmi4_data->st_enabled));
+}
+/*
+ * Accept only "0" and "1" valid values.
+ * "0" will reset the st_enabled flag, then wake up the reading process and
+ * the interrupt handler.
+ * The bus driver is notified via pm_runtime that it is not required to stay
+ * awake anymore.
+ * It will also make sure the queue of events is emptied in the controller,
+ * in case a touch happened in between the secure touch being disabled and
+ * the local ISR being ungated.
+ * "1" will set the st_enabled flag and clear the st_pending_irqs flag.
+ * The bus driver is requested via pm_runtime to stay awake.
+ */
+static ssize_t synaptics_rmi4_secure_touch_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+ unsigned long value;
+ int err = 0;
+
+ if (count > 2)
+ return -EINVAL;
+
+ err = kstrtoul(buf, 10, &value);
+ if (err != 0)
+ return err;
+
+ if (!rmi4_data->st_initialized)
+ return -EIO;
+
+ err = count;
+
+ switch (value) {
+ case 0:
+ if (atomic_read(&rmi4_data->st_enabled) == 0)
+ break;
+
+ synaptics_rmi4_bus_put(rmi4_data);
+ atomic_set(&rmi4_data->st_enabled, 0);
+ synaptics_secure_touch_notify(rmi4_data);
+ complete(&rmi4_data->st_irq_processed);
+ synaptics_rmi4_irq(rmi4_data->irq, rmi4_data);
+ complete(&rmi4_data->st_powerdown);
+
+ break;
+ case 1:
+ if (atomic_read(&rmi4_data->st_enabled)) {
+ err = -EBUSY;
+ break;
+ }
+
+ synchronize_irq(rmi4_data->irq);
+
+ if (synaptics_rmi4_bus_get(rmi4_data) < 0) {
+ dev_err(
+ rmi4_data->pdev->dev.parent,
+ "synaptics_rmi4_bus_get failed\n");
+ err = -EIO;
+ break;
+ }
+ reinit_completion(&rmi4_data->st_powerdown);
+ reinit_completion(&rmi4_data->st_irq_processed);
+ atomic_set(&rmi4_data->st_enabled, 1);
+ atomic_set(&rmi4_data->st_pending_irqs, 0);
+ break;
+ default:
+ dev_err(
+ rmi4_data->pdev->dev.parent,
+ "unsupported value: %lu\n", value);
+ err = -EINVAL;
+ break;
+ }
+ return err;
+}
+
+/*
+ * This function returns whether there are pending interrupts, or
+ * other error conditions that need to be signaled to the userspace library,
+ * according tot he following logic:
+ * - st_enabled is 0 if secure touch is not enabled, returning -EBADF
+ * - st_pending_irqs is -1 to signal that secure touch is in being stopped,
+ * returning -EINVAL
+ * - st_pending_irqs is 1 to signal that there is a pending irq, returning
+ * the value "1" to the sysfs read operation
+ * - st_pending_irqs is 0 (only remaining case left) if the pending interrupt
+ * has been processed, so the interrupt handler can be allowed to continue.
+ */
+static ssize_t synaptics_rmi4_secure_touch_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+ int val = 0;
+
+ if (atomic_read(&rmi4_data->st_enabled) == 0)
+ return -EBADF;
+
+ if (atomic_cmpxchg(&rmi4_data->st_pending_irqs, -1, 0) == -1)
+ return -EINVAL;
+
+ if (atomic_cmpxchg(&rmi4_data->st_pending_irqs, 1, 0) == 1)
+ val = 1;
+ else
+ complete(&rmi4_data->st_irq_processed);
+
+ return scnprintf(buf, PAGE_SIZE, "%u", val);
+
+}
+#endif
+
static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
@@ -1173,7 +1394,6 @@
#ifndef TYPE_B_PROTOCOL
input_mt_sync(rmi4_data->input_dev);
#endif
- input_sync(rmi4_data->input_dev);
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Finger %d: status = 0x%02x, x = %d, y = %d, wx = %d, wy = %d\n",
@@ -1245,7 +1465,6 @@
#ifndef TYPE_B_PROTOCOL
input_mt_sync(rmi4_data->input_dev);
#endif
- input_sync(rmi4_data->input_dev);
if (rmi4_data->stylus_enable) {
stylus_presence = 0;
@@ -1261,6 +1480,8 @@
}
}
+ input_sync(rmi4_data->input_dev);
+
mutex_unlock(&(rmi4_data->rmi4_report_mutex));
return touch_count;
@@ -1422,6 +1643,11 @@
rmi = &(rmi4_data->rmi4_mod_info);
+ if (rmi4_data->stay_awake) {
+ msleep(30);
+ return;
+ }
+
/*
* Get interrupt status information from F01 Data1 register to
* determine the source(s) that are flagging the interrupt.
@@ -1460,12 +1686,6 @@
}
if (status.unconfigured && !status.flash_prog) {
pr_notice("%s: spontaneous reset detected\n", __func__);
- retval = synaptics_rmi4_reinit_device(rmi4_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to reinit device\n",
- __func__);
- }
}
if (!report)
@@ -1507,6 +1727,9 @@
const struct synaptics_dsx_board_data *bdata =
rmi4_data->hw_if->board_data;
+ if (synaptics_filter_interrupt(data) == IRQ_HANDLED)
+ return IRQ_HANDLED;
+
if (gpio_get_value(bdata->irq_gpio) != bdata->irq_on_state)
goto exit;
@@ -2906,7 +3129,9 @@
unsigned char buf[16];
if (config) {
- snprintf(buf, PAGE_SIZE, "dsx_gpio_%u\n", gpio);
+ retval = snprintf(buf, ARRAY_SIZE(buf), "dsx_gpio_%u\n", gpio);
+ if (retval >= 16)
+ return -EINVAL;
retval = gpio_request(gpio, buf);
if (retval) {
@@ -3429,49 +3654,6 @@
return;
}
-static int synaptics_rmi4_reinit_device(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- struct synaptics_rmi4_fn *fhandler;
- struct synaptics_rmi4_exp_fhandler *exp_fhandler;
- struct synaptics_rmi4_device_info *rmi;
-
- rmi = &(rmi4_data->rmi4_mod_info);
-
- mutex_lock(&(rmi4_data->rmi4_reset_mutex));
-
- synaptics_rmi4_free_fingers(rmi4_data);
-
- if (!list_empty(&rmi->support_fn_list)) {
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->fn_number == SYNAPTICS_RMI4_F12) {
- synaptics_rmi4_f12_set_enables(rmi4_data, 0);
- break;
- }
- }
- }
-
- retval = synaptics_rmi4_int_enable(rmi4_data, true);
- if (retval < 0)
- goto exit;
-
- mutex_lock(&exp_data.mutex);
- if (!list_empty(&exp_data.list)) {
- list_for_each_entry(exp_fhandler, &exp_data.list, link)
- if (exp_fhandler->exp_fn->reinit != NULL)
- exp_fhandler->exp_fn->reinit(rmi4_data);
- }
- mutex_unlock(&exp_data.mutex);
-
- synaptics_rmi4_set_configured(rmi4_data);
-
- retval = 0;
-
-exit:
- mutex_unlock(&(rmi4_data->rmi4_reset_mutex));
- return retval;
-}
-
static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data,
bool rebuild)
{
@@ -3683,6 +3865,57 @@
}
EXPORT_SYMBOL(synaptics_rmi4_new_function);
+static int synaptics_dsx_pinctrl_init(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+
+ /* Get pinctrl if target uses pinctrl */
+ rmi4_data->ts_pinctrl = devm_pinctrl_get((rmi4_data->pdev->dev.parent));
+ if (IS_ERR_OR_NULL(rmi4_data->ts_pinctrl)) {
+ retval = PTR_ERR(rmi4_data->ts_pinctrl);
+ dev_err(rmi4_data->pdev->dev.parent,
+ "Target does not use pinctrl %d\n", retval);
+ goto err_pinctrl_get;
+ }
+
+ rmi4_data->pinctrl_state_active
+ = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_active");
+ if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_active)) {
+ retval = PTR_ERR(rmi4_data->pinctrl_state_active);
+ dev_err(rmi4_data->pdev->dev.parent,
+ "Can not lookup %s pinstate %d\n",
+ PINCTRL_STATE_ACTIVE, retval);
+ goto err_pinctrl_lookup;
+ }
+
+ rmi4_data->pinctrl_state_suspend
+ = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_suspend");
+ if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_suspend)) {
+ retval = PTR_ERR(rmi4_data->pinctrl_state_suspend);
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "Can not lookup %s pinstate %d\n",
+ PINCTRL_STATE_SUSPEND, retval);
+ goto err_pinctrl_lookup;
+ }
+
+ rmi4_data->pinctrl_state_release
+ = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_release");
+ if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) {
+ retval = PTR_ERR(rmi4_data->pinctrl_state_release);
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "Can not lookup %s pinstate %d\n",
+ PINCTRL_STATE_RELEASE, retval);
+ }
+
+ return 0;
+
+err_pinctrl_lookup:
+ devm_pinctrl_put(rmi4_data->ts_pinctrl);
+err_pinctrl_get:
+ rmi4_data->ts_pinctrl = NULL;
+ return retval;
+}
+
static int synaptics_rmi4_probe(struct platform_device *pdev)
{
int retval;
@@ -3752,6 +3985,21 @@
goto err_enable_reg;
}
+ retval = synaptics_dsx_pinctrl_init(rmi4_data);
+ if (!retval && rmi4_data->ts_pinctrl) {
+ /*
+ * Pinctrl handle is optional. If pinctrl handle is found
+ * let pins to be configured in active state. If not
+ * found continue further without error.
+ */
+ retval = pinctrl_select_state(rmi4_data->ts_pinctrl,
+ rmi4_data->pinctrl_state_active);
+ if (retval < 0) {
+ dev_err(&pdev->dev,
+ "%s: Failed to select %s pinstate %d\n",
+ __func__, PINCTRL_STATE_ACTIVE, retval);
+ }
+ }
retval = synaptics_rmi4_set_gpio(rmi4_data);
if (retval < 0) {
dev_err(&pdev->dev,
@@ -3779,6 +4027,8 @@
}
#ifdef CONFIG_FB
+ INIT_WORK(&rmi4_data->fb_notify_work,
+ synaptics_rmi4_fb_notify_resume_work);
rmi4_data->fb_notifier.notifier_call = synaptics_rmi4_fb_notifier_cb;
retval = fb_register_client(&rmi4_data->fb_notifier);
if (retval < 0) {
@@ -3844,25 +4094,52 @@
rmi4_data->rb_workqueue =
create_singlethread_workqueue("dsx_rebuild_workqueue");
+ if (!rmi4_data->rb_workqueue) {
+ retval = -ENOMEM;
+ goto err_rb_workqueue;
+ }
INIT_DELAYED_WORK(&rmi4_data->rb_work, synaptics_rmi4_rebuild_work);
exp_data.workqueue = create_singlethread_workqueue("dsx_exp_workqueue");
+ if (!exp_data.workqueue) {
+ retval = -ENOMEM;
+ goto err_exp_data_workqueue;
+ }
INIT_DELAYED_WORK(&exp_data.work, synaptics_rmi4_exp_fn_work);
exp_data.rmi4_data = rmi4_data;
exp_data.queue_work = true;
- queue_delayed_work(exp_data.workqueue,
- &exp_data.work,
- 0);
+ queue_delayed_work(exp_data.workqueue, &exp_data.work, 0);
#ifdef FB_READY_RESET
rmi4_data->reset_workqueue =
create_singlethread_workqueue("dsx_reset_workqueue");
+ if (!rmi4_data->reset_workqueue) {
+ retval = -ENOMEM;
+ goto err_reset_workqueue;
+ }
INIT_WORK(&rmi4_data->reset_work, synaptics_rmi4_reset_work);
queue_work(rmi4_data->reset_workqueue, &rmi4_data->reset_work);
#endif
+ /* Initialize secure touch */
+ synaptics_secure_touch_init(rmi4_data);
+ synaptics_secure_touch_stop(rmi4_data, true);
+
return retval;
+#ifdef FB_READY_RESET
+err_reset_workqueue:
+#endif
+ cancel_delayed_work_sync(&exp_data.work);
+ flush_workqueue(exp_data.workqueue);
+ destroy_workqueue(exp_data.workqueue);
+
+err_exp_data_workqueue:
+ cancel_delayed_work_sync(&rmi4_data->rb_work);
+ flush_workqueue(rmi4_data->rb_workqueue);
+ destroy_workqueue(rmi4_data->rb_workqueue);
+
+err_rb_workqueue:
err_sysfs:
for (attr_count--; attr_count >= 0; attr_count--) {
sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
@@ -3908,6 +4185,21 @@
err_set_gpio:
synaptics_rmi4_enable_reg(rmi4_data, false);
+ if (rmi4_data->ts_pinctrl) {
+ if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) {
+ devm_pinctrl_put(rmi4_data->ts_pinctrl);
+ rmi4_data->ts_pinctrl = NULL;
+ } else {
+ retval = pinctrl_select_state(
+ rmi4_data->ts_pinctrl,
+ rmi4_data->pinctrl_state_release);
+ if (retval)
+ dev_err(&pdev->dev,
+ "%s: Failed to create sysfs attributes\n",
+ __func__);
+ }
+ }
+
err_enable_reg:
synaptics_rmi4_get_reg(rmi4_data, false);
@@ -3920,6 +4212,7 @@
static int synaptics_rmi4_remove(struct platform_device *pdev)
{
unsigned char attr_count;
+ int err;
struct synaptics_rmi4_data *rmi4_data = platform_get_drvdata(pdev);
const struct synaptics_dsx_board_data *bdata =
rmi4_data->hw_if->board_data;
@@ -3975,6 +4268,22 @@
if (bdata->power_gpio >= 0)
synaptics_rmi4_gpio_setup(bdata->power_gpio, false, 0, 0);
+
+ if (rmi4_data->ts_pinctrl) {
+ if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) {
+ devm_pinctrl_put(rmi4_data->ts_pinctrl);
+ rmi4_data->ts_pinctrl = NULL;
+ } else {
+ err = pinctrl_select_state(
+ rmi4_data->ts_pinctrl,
+ rmi4_data->pinctrl_state_release);
+ if (err)
+ dev_err(&pdev->dev,
+ "Failed to select release pinctrl state %d\n",
+ err);
+ }
+ }
+
synaptics_rmi4_enable_reg(rmi4_data, false);
synaptics_rmi4_get_reg(rmi4_data, false);
@@ -4091,6 +4400,14 @@
}
#ifdef CONFIG_FB
+static void synaptics_rmi4_fb_notify_resume_work(struct work_struct *work)
+{
+ struct synaptics_rmi4_data *rmi4_data =
+ container_of(work, struct synaptics_rmi4_data, fb_notify_work);
+ synaptics_rmi4_resume(&(rmi4_data->input_dev->dev));
+ rmi4_data->fb_ready = true;
+}
+
static int synaptics_rmi4_fb_notifier_cb(struct notifier_block *self,
unsigned long event, void *data)
{
@@ -4101,14 +4418,36 @@
fb_notifier);
if (evdata && evdata->data && rmi4_data) {
- if (event == FB_EVENT_BLANK) {
- transition = evdata->data;
- if (*transition == FB_BLANK_POWERDOWN) {
- synaptics_rmi4_suspend(&rmi4_data->pdev->dev);
- rmi4_data->fb_ready = false;
- } else if (*transition == FB_BLANK_UNBLANK) {
- synaptics_rmi4_resume(&rmi4_data->pdev->dev);
- rmi4_data->fb_ready = true;
+ if (rmi4_data->hw_if->board_data->resume_in_workqueue) {
+ if (event == FB_EARLY_EVENT_BLANK) {
+ synaptics_secure_touch_stop(rmi4_data, false);
+ } else if (event == FB_EVENT_BLANK) {
+ transition = evdata->data;
+ if (*transition == FB_BLANK_POWERDOWN) {
+ flush_work(
+ &(rmi4_data->fb_notify_work));
+ synaptics_rmi4_suspend(
+ &rmi4_data->pdev->dev);
+ rmi4_data->fb_ready = false;
+ } else if (*transition == FB_BLANK_UNBLANK) {
+ schedule_work(
+ &(rmi4_data->fb_notify_work));
+ }
+ }
+ } else {
+ if (event == FB_EARLY_EVENT_BLANK) {
+ synaptics_secure_touch_stop(rmi4_data, false);
+ } else if (event == FB_EVENT_BLANK) {
+ transition = evdata->data;
+ if (*transition == FB_BLANK_POWERDOWN) {
+ synaptics_rmi4_suspend(
+ &rmi4_data->pdev->dev);
+ rmi4_data->fb_ready = false;
+ } else if (*transition == FB_BLANK_UNBLANK) {
+ synaptics_rmi4_resume(
+ &rmi4_data->pdev->dev);
+ rmi4_data->fb_ready = true;
+ }
}
}
}
@@ -4128,6 +4467,14 @@
if (rmi4_data->stay_awake)
return;
+ /*
+ * During early suspend/late resume, the driver doesn't access xPU/SMMU
+ * protected HW resources. So, there is no compelling need to block,
+ * but notifying the userspace that a power event has occurred is
+ * enough. Hence 'blocking' variable can be set to false.
+ */
+ synaptics_secure_touch_stop(rmi4_data, false);
+
if (rmi4_data->enable_wakeup_gesture) {
synaptics_rmi4_wakeup_gesture(rmi4_data, true);
enable_irq_wake(rmi4_data->irq);
@@ -4165,6 +4512,8 @@
if (rmi4_data->stay_awake)
return;
+ synaptics_secure_touch_stop(rmi4_data, false);
+
if (rmi4_data->enable_wakeup_gesture) {
synaptics_rmi4_wakeup_gesture(rmi4_data, false);
disable_irq_wake(rmi4_data->irq);
@@ -4207,10 +4556,13 @@
{
struct synaptics_rmi4_exp_fhandler *exp_fhandler;
struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+ int retval;
if (rmi4_data->stay_awake)
return 0;
+ synaptics_secure_touch_stop(rmi4_data, true);
+
if (rmi4_data->enable_wakeup_gesture) {
synaptics_rmi4_wakeup_gesture(rmi4_data, true);
enable_irq_wake(rmi4_data->irq);
@@ -4223,6 +4575,13 @@
synaptics_rmi4_free_fingers(rmi4_data);
}
+ if (rmi4_data->ts_pinctrl) {
+ retval = pinctrl_select_state(rmi4_data->ts_pinctrl,
+ rmi4_data->pinctrl_state_suspend);
+ if (retval < 0)
+ dev_err(dev, "Cannot get idle pinctrl state\n");
+ goto err_pinctrl;
+ }
exit:
mutex_lock(&exp_data.mutex);
if (!list_empty(&exp_data.list)) {
@@ -4232,9 +4591,19 @@
}
mutex_unlock(&exp_data.mutex);
+ if (!rmi4_data->suspend) {
+ synaptics_rmi4_enable_reg(rmi4_data, false);
+ synaptics_rmi4_get_reg(rmi4_data, false);
+ }
rmi4_data->suspend = true;
return 0;
+
+err_pinctrl:
+ synaptics_rmi4_sleep_enable(rmi4_data, false);
+ synaptics_rmi4_irq_enable(rmi4_data, true, false);
+ return retval;
+
}
static int synaptics_rmi4_resume(struct device *dev)
@@ -4248,6 +4617,8 @@
if (rmi4_data->stay_awake)
return 0;
+ synaptics_secure_touch_stop(rmi4_data, true);
+
if (rmi4_data->enable_wakeup_gesture) {
synaptics_rmi4_wakeup_gesture(rmi4_data, false);
disable_irq_wake(rmi4_data->irq);
@@ -4256,8 +4627,19 @@
rmi4_data->current_page = MASK_8BIT;
+ if (rmi4_data->suspend) {
+ synaptics_rmi4_get_reg(rmi4_data, true);
+ synaptics_rmi4_enable_reg(rmi4_data, true);
+ }
+
synaptics_rmi4_sleep_enable(rmi4_data, false);
synaptics_rmi4_irq_enable(rmi4_data, true, false);
+ if (rmi4_data->ts_pinctrl) {
+ retval = pinctrl_select_state(rmi4_data->ts_pinctrl,
+ rmi4_data->pinctrl_state_active);
+ if (retval < 0)
+ dev_err(dev, "Cannot get default pinctrl state\n");
+ }
exit:
#ifdef FB_READY_RESET
@@ -4306,7 +4688,7 @@
{
int retval;
- retval = synaptics_rmi4_bus_init();
+ retval = synaptics_rmi4_bus_init_v26();
if (retval)
return retval;
@@ -4317,7 +4699,7 @@
{
platform_driver_unregister(&synaptics_rmi4_driver);
- synaptics_rmi4_bus_exit();
+ synaptics_rmi4_bus_exit_v26();
return;
}
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h
index 1b4079c..39fec9a 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h
@@ -5,6 +5,7 @@
*
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -48,6 +49,13 @@
#include <linux/earlysuspend.h>
#endif
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+#include <linux/completion.h>
+#include <linux/atomic.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#endif
+
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38))
#define KERNEL_ABOVE_2_6_38
#endif
@@ -115,6 +123,9 @@
#define MASK_2BIT 0x03
#define MASK_1BIT 0x01
+#define PINCTRL_STATE_ACTIVE "pmx_ts_active"
+#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend"
+#define PINCTRL_STATE_RELEASE "pmx_ts_release"
enum exp_fn {
RMI_DEV = 0,
RMI_FW_UPDATER,
@@ -324,6 +335,7 @@
struct delayed_work rb_work;
struct workqueue_struct *rb_workqueue;
#ifdef CONFIG_FB
+ struct work_struct fb_notify_work;
struct notifier_block fb_notifier;
struct work_struct reset_work;
struct workqueue_struct *reset_workqueue;
@@ -374,6 +386,19 @@
bool enable);
void (*report_touch)(struct synaptics_rmi4_data *rmi4_data,
struct synaptics_rmi4_fn *fhandler);
+ struct pinctrl *ts_pinctrl;
+ struct pinctrl_state *pinctrl_state_active;
+ struct pinctrl_state *pinctrl_state_suspend;
+ struct pinctrl_state *pinctrl_state_release;
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+ atomic_t st_enabled;
+ atomic_t st_pending_irqs;
+ struct completion st_powerdown;
+ struct completion st_irq_processed;
+ bool st_initialized;
+ struct clk *core_clk;
+ struct clk *iface_clk;
+#endif
};
struct synaptics_dsx_bus_access {
@@ -382,6 +407,10 @@
unsigned char *data, unsigned short length);
int (*write)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr,
unsigned char *data, unsigned short length);
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+ int (*get)(struct synaptics_rmi4_data *rmi4_data);
+ void (*put)(struct synaptics_rmi4_data *rmi4_data);
+#endif
};
struct synaptics_dsx_hw_interface {
@@ -405,9 +434,9 @@
unsigned char intr_mask);
};
-int synaptics_rmi4_bus_init(void);
+int synaptics_rmi4_bus_init_v26(void);
-void synaptics_rmi4_bus_exit(void);
+void synaptics_rmi4_bus_exit_v26(void);
void synaptics_rmi4_new_function(struct synaptics_rmi4_exp_fn *exp_fn_module,
bool insert);
@@ -432,21 +461,16 @@
return rmi4_data->hw_if->bus_access->write(rmi4_data, addr, data, len);
}
-static inline ssize_t synaptics_rmi4_show_error(struct device *dev,
- struct device_attribute *attr, char *buf)
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+static inline int synaptics_rmi4_bus_get(struct synaptics_rmi4_data *rmi4_data)
{
- dev_warn(dev, "%s Attempted to read from write-only attribute %s\n",
- __func__, attr->attr.name);
- return -EPERM;
+ return rmi4_data->hw_if->bus_access->get(rmi4_data);
}
-
-static inline ssize_t synaptics_rmi4_store_error(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
+static inline void synaptics_rmi4_bus_put(struct synaptics_rmi4_data *rmi4_data)
{
- dev_warn(dev, "%s Attempted to write to read-only attribute %s\n",
- __func__, attr->attr.name);
- return -EPERM;
+ rmi4_data->hw_if->bus_access->put(rmi4_data);
}
+#endif
static inline int secure_memcpy(unsigned char *dest, unsigned int dest_size,
const unsigned char *src, unsigned int src_size,
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c
index 5dd0ec4..344f4c3 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c
@@ -5,6 +5,7 @@
*
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -39,7 +40,7 @@
#include <linux/input.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define FW_IMAGE_NAME "synaptics/startup_fw_update.img"
@@ -127,6 +128,7 @@
static int fwu_recovery_check_status(void);
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static ssize_t fwu_sysfs_show_image(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count);
@@ -179,6 +181,7 @@
static ssize_t fwu_sysfs_write_guest_code_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
+#endif
enum f34_version {
F34_V0 = 0,
@@ -650,6 +653,7 @@
struct work_struct fwu_work;
};
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static struct bin_attribute dev_attr_data = {
.attr = {
.name = "data",
@@ -659,53 +663,56 @@
.read = fwu_sysfs_show_image,
.write = fwu_sysfs_store_image,
};
+#endif
static struct device_attribute attrs[] = {
- __ATTR(dorecovery, S_IWUGO,
- synaptics_rmi4_show_error,
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
+ __ATTR(dorecovery, 0220,
+ NULL,
fwu_sysfs_do_recovery_store),
- __ATTR(doreflash, S_IWUGO,
- synaptics_rmi4_show_error,
+ __ATTR(doreflash, 0220,
+ NULL,
fwu_sysfs_do_reflash_store),
- __ATTR(writeconfig, S_IWUGO,
- synaptics_rmi4_show_error,
+ __ATTR(writeconfig, 0220,
+ NULL,
fwu_sysfs_write_config_store),
- __ATTR(readconfig, S_IWUGO,
- synaptics_rmi4_show_error,
+ __ATTR(readconfig, 0220,
+ NULL,
fwu_sysfs_read_config_store),
- __ATTR(configarea, S_IWUGO,
- synaptics_rmi4_show_error,
+ __ATTR(configarea, 0220,
+ NULL,
fwu_sysfs_config_area_store),
- __ATTR(imagename, S_IWUGO,
- synaptics_rmi4_show_error,
+ __ATTR(imagename, 0220,
+ NULL,
fwu_sysfs_image_name_store),
- __ATTR(imagesize, S_IWUGO,
- synaptics_rmi4_show_error,
+ __ATTR(imagesize, 0220,
+ NULL,
fwu_sysfs_image_size_store),
- __ATTR(blocksize, S_IRUGO,
+ __ATTR(blocksize, 0444,
fwu_sysfs_block_size_show,
- synaptics_rmi4_store_error),
- __ATTR(fwblockcount, S_IRUGO,
+ NULL),
+ __ATTR(fwblockcount, 0444,
fwu_sysfs_firmware_block_count_show,
- synaptics_rmi4_store_error),
- __ATTR(configblockcount, S_IRUGO,
+ NULL),
+ __ATTR(configblockcount, 0444,
fwu_sysfs_configuration_block_count_show,
- synaptics_rmi4_store_error),
- __ATTR(dispconfigblockcount, S_IRUGO,
+ NULL),
+ __ATTR(dispconfigblockcount, 0444,
fwu_sysfs_disp_config_block_count_show,
- synaptics_rmi4_store_error),
- __ATTR(permconfigblockcount, S_IRUGO,
+ NULL),
+ __ATTR(permconfigblockcount, 0444,
fwu_sysfs_perm_config_block_count_show,
- synaptics_rmi4_store_error),
- __ATTR(blconfigblockcount, S_IRUGO,
+ NULL),
+ __ATTR(blconfigblockcount, 0444,
fwu_sysfs_bl_config_block_count_show,
- synaptics_rmi4_store_error),
- __ATTR(guestcodeblockcount, S_IRUGO,
+ NULL),
+ __ATTR(guestcodeblockcount, 0444,
fwu_sysfs_guest_code_block_count_show,
- synaptics_rmi4_store_error),
- __ATTR(writeguestcode, S_IWUGO,
- synaptics_rmi4_show_error,
+ NULL),
+ __ATTR(writeguestcode, 0220,
+ NULL,
fwu_sysfs_write_guest_code_store),
+#endif
};
static struct synaptics_rmi4_fwu_handle *fwu;
@@ -2218,10 +2225,12 @@
__func__);
return -ENOMEM;
}
- while (strptr[index] >= '0' && strptr[index] <= '9') {
+ while ((index < MAX_FIRMWARE_ID_LEN - 1) && strptr[index] >= '0'
+ && strptr[index] <= '9') {
firmware_id[index] = strptr[index];
index++;
}
+ firmware_id[index] = '\0';
retval = sstrtoul(firmware_id, 10, (unsigned long *)fw_id);
kfree(firmware_id);
@@ -2608,6 +2617,7 @@
return 0;
}
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static int fwu_check_pm_configuration_size(void)
{
unsigned short block_count;
@@ -2624,6 +2634,7 @@
return 0;
}
+#endif
static int fwu_check_bl_configuration_size(void)
{
@@ -2823,6 +2834,7 @@
return fwu_write_configuration();
}
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static int fwu_write_pm_configuration(void)
{
fwu->config_area = PM_CONFIG_AREA;
@@ -2832,6 +2844,7 @@
return fwu_write_configuration();
}
+#endif
static int fwu_write_flash_configuration(void)
{
@@ -3039,6 +3052,7 @@
return retval;
}
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static int fwu_do_read_config(void)
{
int retval;
@@ -3116,6 +3130,7 @@
return retval;
}
+#endif
static int fwu_do_lockdown_v7(void)
{
@@ -3190,6 +3205,7 @@
return retval;
}
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static int fwu_start_write_guest_code(void)
{
int retval;
@@ -3395,6 +3411,7 @@
return retval;
}
+#endif
static int fwu_start_reflash(void)
{
@@ -3596,6 +3613,7 @@
return 0;
}
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static int fwu_recovery_erase_all(void)
{
int retval;
@@ -3789,6 +3807,7 @@
return retval;
}
+#endif
int synaptics_fw_updater(const unsigned char *fw_data)
{
@@ -3847,6 +3866,7 @@
}
#endif
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static ssize_t fwu_sysfs_show_image(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count)
@@ -4218,6 +4238,7 @@
fwu->image = NULL;
return retval;
}
+#endif
static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data,
unsigned char intr_mask)
@@ -4231,6 +4252,28 @@
return;
}
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
+static int synaptics_create_fwu_bin_file(struct synaptics_rmi4_data *rmi4_data)
+{
+ return sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj,
+ &dev_attr_data);
+}
+
+static void synaptics_remove_fwu_bin_file(struct synaptics_rmi4_data *rmi4_data)
+{
+ sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
+}
+#else
+static int synaptics_create_fwu_bin_file(struct synaptics_rmi4_data *rmi4_data)
+{
+ return 0;
+}
+
+static void synaptics_remove_fwu_bin_file(struct synaptics_rmi4_data *rmi4_data)
+{
+}
+#endif
+
static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
{
int retval;
@@ -4302,8 +4345,7 @@
fwu->do_lockdown = DO_LOCKDOWN;
fwu->initialized = true;
- retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj,
- &dev_attr_data);
+ retval = synaptics_create_fwu_bin_file(rmi4_data);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to create sysfs bin file\n",
@@ -4338,7 +4380,7 @@
&attrs[attr_count].attr);
}
- sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
+ synaptics_remove_fwu_bin_file(rmi4_data);
exit_free_mem:
kfree(fwu->image_name);
@@ -4369,7 +4411,7 @@
&attrs[attr_count].attr);
}
- sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
+ synaptics_remove_fwu_bin_file(rmi4_data);
kfree(fwu->read_config_buf);
kfree(fwu->image_name);
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_gesture.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_gesture.c
index 7630a60..ae1a55af 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_gesture.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_gesture.c
@@ -5,6 +5,7 @@
*
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -38,7 +39,7 @@
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define GESTURE_PHYS_NAME "synaptics_dsx/gesture"
@@ -388,54 +389,54 @@
};
static struct device_attribute attrs[] = {
- __ATTR(engine_enable, S_IWUGO,
- synaptics_rmi4_show_error,
+ __ATTR(engine_enable, 0220,
+ NULL,
udg_sysfs_engine_enable_store),
- __ATTR(detection_enable, S_IWUGO,
- synaptics_rmi4_show_error,
+ __ATTR(detection_enable, 0220,
+ NULL,
udg_sysfs_detection_enable_store),
- __ATTR(detection_score, S_IRUGO,
+ __ATTR(detection_score, 0444,
udg_sysfs_detection_score_show,
- synaptics_rmi4_store_error),
- __ATTR(detection_index, S_IRUGO,
+ NULL),
+ __ATTR(detection_index, 0444,
udg_sysfs_detection_index_show,
- synaptics_rmi4_store_error),
- __ATTR(registration_enable, S_IWUGO,
- synaptics_rmi4_show_error,
+ NULL),
+ __ATTR(registration_enable, 0220,
+ NULL,
udg_sysfs_registration_enable_store),
- __ATTR(registration_begin, S_IWUGO,
- synaptics_rmi4_show_error,
+ __ATTR(registration_begin, 0220,
+ NULL,
udg_sysfs_registration_begin_store),
- __ATTR(registration_status, S_IRUGO,
+ __ATTR(registration_status, 0444,
udg_sysfs_registration_status_show,
- synaptics_rmi4_store_error),
- __ATTR(template_size, S_IRUGO,
+ NULL),
+ __ATTR(template_size, 0444,
udg_sysfs_template_size_show,
- synaptics_rmi4_store_error),
- __ATTR(template_max_index, S_IRUGO,
+ NULL),
+ __ATTR(template_max_index, 0444,
udg_sysfs_template_max_index_show,
- synaptics_rmi4_store_error),
- __ATTR(template_detection, S_IRUGO,
+ NULL),
+ __ATTR(template_detection, 0444,
udg_sysfs_template_detection_show,
- synaptics_rmi4_store_error),
- __ATTR(template_index, S_IWUGO,
- synaptics_rmi4_show_error,
+ NULL),
+ __ATTR(template_index, 0220,
+ NULL,
udg_sysfs_template_index_store),
- __ATTR(template_valid, (S_IRUGO | S_IWUGO),
+ __ATTR(template_valid, 0664,
udg_sysfs_template_valid_show,
udg_sysfs_template_valid_store),
- __ATTR(template_clear, S_IWUGO,
- synaptics_rmi4_show_error,
+ __ATTR(template_clear, 0220,
+ NULL,
udg_sysfs_template_clear_store),
- __ATTR(trace_size, S_IRUGO,
+ __ATTR(trace_size, 0444,
udg_sysfs_trace_size_show,
- synaptics_rmi4_store_error),
+ NULL),
};
static struct bin_attribute template_data = {
.attr = {
.name = "template_data",
- .mode = (S_IRUGO | S_IWUGO),
+ .mode = 0664,
},
.size = 0,
.read = udg_sysfs_template_data_show,
@@ -445,7 +446,7 @@
static struct bin_attribute trace_data = {
.attr = {
.name = "trace_data",
- .mode = S_IRUGO,
+ .mode = 0444,
},
.size = 0,
.read = udg_sysfs_trace_data_show,
@@ -453,22 +454,22 @@
};
static struct device_attribute params[] = {
- __ATTR(template_displacement, (S_IRUGO | S_IWUGO),
+ __ATTR(template_displacement, 0664,
udg_sysfs_template_displacement_show,
udg_sysfs_template_displacement_store),
- __ATTR(rotation_invariance, (S_IRUGO | S_IWUGO),
+ __ATTR(rotation_invariance, 0664,
udg_sysfs_rotation_invariance_show,
udg_sysfs_rotation_invariance_store),
- __ATTR(scale_invariance, (S_IRUGO | S_IWUGO),
+ __ATTR(scale_invariance, 0664,
udg_sysfs_scale_invariance_show,
udg_sysfs_scale_invariance_store),
- __ATTR(threshold_factor, (S_IRUGO | S_IWUGO),
+ __ATTR(threshold_factor, 0664,
udg_sysfs_threshold_factor_show,
udg_sysfs_threshold_factor_store),
- __ATTR(match_metric_threshold, (S_IRUGO | S_IWUGO),
+ __ATTR(match_metric_threshold, 0664,
udg_sysfs_match_metric_threshold_show,
udg_sysfs_match_metric_threshold_store),
- __ATTR(max_inter_stroke_time, (S_IRUGO | S_IWUGO),
+ __ATTR(max_inter_stroke_time, 0664,
udg_sysfs_max_inter_stroke_time_show,
udg_sysfs_max_inter_stroke_time_store),
};
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c
index 3be3bca..df17a0b 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c
@@ -5,6 +5,7 @@
*
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -40,7 +41,7 @@
#include <linux/types.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define SYN_I2C_RETRY_TIMES 10
@@ -48,9 +49,8 @@
/*
#define I2C_BURST_LIMIT 255
*/
-/*
+
#define XFER_MSGS_LIMIT 8
-*/
static unsigned char *wr_buf;
@@ -78,6 +78,9 @@
else
bdata->irq_on_state = value;
+ bdata->resume_in_workqueue = of_property_read_bool(np,
+ "synaptics,resume-in-workqueue");
+
retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name);
if (retval < 0)
bdata->pwr_reg_name = NULL;
@@ -178,14 +181,9 @@
bdata->max_y_for_2d = -1;
}
- prop = of_find_property(np, "synaptics,swap-axes", NULL);
- bdata->swap_axes = prop > 0 ? true : false;
-
- prop = of_find_property(np, "synaptics,x-flip", NULL);
- bdata->x_flip = prop > 0 ? true : false;
-
- prop = of_find_property(np, "synaptics,y-flip", NULL);
- bdata->y_flip = prop > 0 ? true : false;
+ bdata->swap_axes = of_property_read_bool(np, "synaptics,swap-axes");
+ bdata->x_flip = of_property_read_bool(np, "synaptics,x-flip");
+ bdata->y_flip = of_property_read_bool(np, "synaptics,y-flip");
prop = of_find_property(np, "synaptics,ub-i2c-addr", NULL);
if (prop && prop->length) {
@@ -288,7 +286,7 @@
static int synaptics_rmi4_i2c_set_page(struct synaptics_rmi4_data *rmi4_data,
unsigned short addr)
{
- int retval;
+ int retval = 0;
unsigned char retry;
unsigned char buf[PAGE_SELECT_LEN];
unsigned char page;
@@ -306,7 +304,7 @@
if (page != rmi4_data->current_page) {
for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
- if (i2c_transfer(i2c->adapter, msg, 1) == 1) {
+ if (i2c_transfer(i2c->adapter, &msg[0], 1) == 1) {
rmi4_data->current_page = page;
retval = PAGE_SELECT_LEN;
break;
@@ -348,7 +346,7 @@
unsigned short remaining_length = length;
struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
struct i2c_adapter *adap = i2c->adapter;
- struct i2c_msg msg[rd_msgs + 1];
+ struct i2c_msg msg[XFER_MSGS_LIMIT + 1];
mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
@@ -384,14 +382,10 @@
remaining_msgs = rd_msgs + 1;
while (remaining_msgs) {
-#ifdef XFER_MSGS_LIMIT
if (remaining_msgs > XFER_MSGS_LIMIT)
xfer_msgs = XFER_MSGS_LIMIT;
else
xfer_msgs = remaining_msgs;
-#else
- xfer_msgs = remaining_msgs;
-#endif
for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
retval = i2c_transfer(adap, &msg[index], xfer_msgs);
if (retval == xfer_msgs)
@@ -442,11 +436,11 @@
struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
struct i2c_msg msg[1];
+ mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
+
retval = synaptics_rmi4_i2c_alloc_buf(rmi4_data, length + 1);
if (retval < 0)
- return retval;
-
- mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
+ goto exit;
retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr);
if (retval != PAGE_SELECT_LEN) {
@@ -469,7 +463,7 @@
}
for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
- if (i2c_transfer(i2c->adapter, msg, 1) == 1) {
+ if (i2c_transfer(i2c->adapter, &msg[0], 1) == 1) {
retval = length;
break;
}
@@ -497,10 +491,73 @@
return retval;
}
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+static int synaptics_rmi4_clk_prepare_enable(
+ struct synaptics_rmi4_data *rmi4_data)
+{
+ int ret;
+
+ ret = clk_prepare_enable(rmi4_data->iface_clk);
+ if (ret) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "error on clk_prepare_enable(iface_clk):%d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(rmi4_data->core_clk);
+ if (ret) {
+ clk_disable_unprepare(rmi4_data->iface_clk);
+ dev_err(rmi4_data->pdev->dev.parent,
+ "error clk_prepare_enable(core_clk):%d\n", ret);
+ }
+ return ret;
+}
+
+static void synaptics_rmi4_clk_disable_unprepare(
+ struct synaptics_rmi4_data *rmi4_data)
+{
+ clk_disable_unprepare(rmi4_data->core_clk);
+ clk_disable_unprepare(rmi4_data->iface_clk);
+}
+
+static int synaptics_rmi4_i2c_get(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
+
+ mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
+ retval = pm_runtime_get_sync(i2c->adapter->dev.parent);
+ if (retval >= 0 && rmi4_data->core_clk != NULL &&
+ rmi4_data->iface_clk != NULL) {
+ retval = synaptics_rmi4_clk_prepare_enable(rmi4_data);
+ if (retval)
+ pm_runtime_put_sync(i2c->adapter->dev.parent);
+ }
+ mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
+
+ return retval;
+}
+
+static void synaptics_rmi4_i2c_put(struct synaptics_rmi4_data *rmi4_data)
+{
+ struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
+
+ mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
+ if (rmi4_data->core_clk != NULL && rmi4_data->iface_clk != NULL)
+ synaptics_rmi4_clk_disable_unprepare(rmi4_data);
+ pm_runtime_put_sync(i2c->adapter->dev.parent);
+ mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
+}
+#endif
+
static struct synaptics_dsx_bus_access bus_access = {
.type = BUS_I2C,
.read = synaptics_rmi4_i2c_read,
.write = synaptics_rmi4_i2c_write,
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
+ .get = synaptics_rmi4_i2c_get,
+ .put = synaptics_rmi4_i2c_put,
+#endif
};
static void synaptics_rmi4_i2c_dev_release(struct device *dev)
@@ -625,13 +682,13 @@
.id_table = synaptics_rmi4_id_table,
};
-int synaptics_rmi4_bus_init(void)
+int synaptics_rmi4_bus_init_v26(void)
{
return i2c_add_driver(&synaptics_rmi4_i2c_driver);
}
-EXPORT_SYMBOL(synaptics_rmi4_bus_init);
+EXPORT_SYMBOL(synaptics_rmi4_bus_init_v26);
-void synaptics_rmi4_bus_exit(void)
+void synaptics_rmi4_bus_exit_v26(void)
{
kfree(wr_buf);
@@ -639,7 +696,7 @@
return;
}
-EXPORT_SYMBOL(synaptics_rmi4_bus_exit);
+EXPORT_SYMBOL(synaptics_rmi4_bus_exit_v26);
MODULE_AUTHOR("Synaptics, Inc.");
MODULE_DESCRIPTION("Synaptics DSX I2C Bus Support Module");
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_proximity.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_proximity.c
index 2507c77..ce8979c 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_proximity.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_proximity.c
@@ -38,7 +38,7 @@
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define PROX_PHYS_NAME "synaptics_dsx/proximity"
@@ -54,7 +54,7 @@
struct device_attribute *attr, const char *buf, size_t count);
static struct device_attribute attrs[] = {
- __ATTR(hover_finger_en, (S_IRUGO | S_IWUGO),
+ __ATTR(hover_finger_en, 0664,
synaptics_rmi4_hover_finger_en_show,
synaptics_rmi4_hover_finger_en_store),
};
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_dev.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_dev.c
index 9d19bda..4392374 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_dev.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_dev.c
@@ -5,6 +5,7 @@
*
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -43,7 +44,7 @@
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define CHAR_DEVICE_NAME "rmi"
@@ -118,7 +119,7 @@
static struct bin_attribute attr_data = {
.attr = {
.name = "data",
- .mode = (S_IRUGO | S_IWUGO),
+ .mode = 0664,
},
.size = 0,
.read = rmidev_sysfs_data_show,
@@ -126,25 +127,25 @@
};
static struct device_attribute attrs[] = {
- __ATTR(open, S_IWUGO,
- synaptics_rmi4_show_error,
+ __ATTR(open, 0220,
+ NULL,
rmidev_sysfs_open_store),
- __ATTR(release, S_IWUGO,
- synaptics_rmi4_show_error,
+ __ATTR(release, 0220,
+ NULL,
rmidev_sysfs_release_store),
- __ATTR(attn_state, S_IRUGO,
+ __ATTR(attn_state, 0444,
rmidev_sysfs_attn_state_show,
- synaptics_rmi4_store_error),
- __ATTR(pid, S_IRUGO | S_IWUGO,
+ NULL),
+ __ATTR(pid, 0664,
rmidev_sysfs_pid_show,
rmidev_sysfs_pid_store),
- __ATTR(term, S_IWUGO,
- synaptics_rmi4_show_error,
+ __ATTR(term, 0220,
+ NULL,
rmidev_sysfs_term_store),
- __ATTR(intr_mask, S_IRUGO | S_IWUGO,
+ __ATTR(intr_mask, 0444,
rmidev_sysfs_intr_mask_show,
rmidev_sysfs_intr_mask_store),
- __ATTR(concurrent, S_IRUGO | S_IWUGO,
+ __ATTR(concurrent, 0444,
rmidev_sysfs_concurrent_show,
rmidev_sysfs_concurrent_store),
};
@@ -155,7 +156,7 @@
static struct rmidev_handle *rmidev;
-DECLARE_COMPLETION(rmidev_remove_complete);
+DECLARE_COMPLETION(rmidev_remove_complete_v26);
static irqreturn_t rmidev_sysfs_irq(int irq, void *data)
{
@@ -562,18 +563,24 @@
return -EBADF;
}
- if (count == 0)
- return 0;
+ mutex_lock(&(dev_data->file_mutex));
+
+ if (*f_pos > REG_ADDR_LIMIT) {
+ retval = -EFAULT;
+ goto clean_up;
+ }
if (count > (REG_ADDR_LIMIT - *f_pos))
count = REG_ADDR_LIMIT - *f_pos;
+ if (count == 0) {
+ retval = 0;
+ goto clean_up;
+ }
address = (unsigned short)(*f_pos);
rmidev_allocate_buffer(count);
- mutex_lock(&(dev_data->file_mutex));
-
retval = synaptics_rmi4_reg_read(rmidev->rmi4_data,
*f_pos,
rmidev->tmpbuf,
@@ -633,18 +640,26 @@
return -EBADF;
}
- if (count == 0)
- return 0;
+ mutex_lock(&(dev_data->file_mutex));
+
+ if (*f_pos > REG_ADDR_LIMIT) {
+ retval = -EFAULT;
+ goto unlock;
+ }
if (count > (REG_ADDR_LIMIT - *f_pos))
count = REG_ADDR_LIMIT - *f_pos;
+ if (count == 0) {
+ retval = 0;
+ goto unlock;
+ }
rmidev_allocate_buffer(count);
- if (copy_from_user(rmidev->tmpbuf, buf, count))
+ if (copy_from_user(rmidev->tmpbuf, buf, count)) {
return -EFAULT;
-
- mutex_lock(&(dev_data->file_mutex));
+ goto unlock;
+ }
retval = synaptics_rmi4_reg_write(rmidev->rmi4_data,
*f_pos,
@@ -653,6 +668,7 @@
if (retval >= 0)
*f_pos += retval;
+unlock:
mutex_unlock(&(dev_data->file_mutex));
return retval;
@@ -1016,7 +1032,7 @@
rmidev = NULL;
exit:
- complete(&rmidev_remove_complete);
+ complete(&rmidev_remove_complete_v26);
return;
}
@@ -1045,7 +1061,7 @@
{
synaptics_rmi4_new_function(&rmidev_module, false);
- wait_for_completion(&rmidev_remove_complete);
+ wait_for_completion(&rmidev_remove_complete_v26);
return;
}
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_hid_i2c.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_hid_i2c.c
index df6e357..7e02487 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_hid_i2c.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_hid_i2c.c
@@ -42,7 +42,7 @@
#include <linux/types.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define SYN_I2C_RETRY_TIMES 10
@@ -987,19 +987,19 @@
.id_table = synaptics_rmi4_id_table,
};
-int synaptics_rmi4_bus_init(void)
+int synaptics_rmi4_bus_init_v26(void)
{
return i2c_add_driver(&synaptics_rmi4_i2c_driver);
}
-EXPORT_SYMBOL(synaptics_rmi4_bus_init);
+EXPORT_SYMBOL(synaptics_rmi4_bus_init_v26);
-void synaptics_rmi4_bus_exit(void)
+void synaptics_rmi4_bus_exit_v26(void)
{
i2c_del_driver(&synaptics_rmi4_i2c_driver);
return;
}
-EXPORT_SYMBOL(synaptics_rmi4_bus_exit);
+EXPORT_SYMBOL(synaptics_rmi4_bus_exit_v26);
MODULE_AUTHOR("Synaptics, Inc.");
MODULE_DESCRIPTION("Synaptics DSX I2C Bus Support Module");
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_spi.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_spi.c
index d5d9c3c..382a3dd 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_spi.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_spi.c
@@ -40,7 +40,7 @@
#include <linux/types.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define SPI_READ 0x80
@@ -615,19 +615,19 @@
};
-int synaptics_rmi4_bus_init(void)
+int synaptics_rmi4_bus_init_v26(void)
{
return spi_register_driver(&synaptics_rmi4_spi_driver);
}
-EXPORT_SYMBOL(synaptics_rmi4_bus_init);
+EXPORT_SYMBOL(synaptics_rmi4_bus_init_v26);
-void synaptics_rmi4_bus_exit(void)
+void synaptics_rmi4_bus_exit_v26(void)
{
spi_unregister_driver(&synaptics_rmi4_spi_driver);
return;
}
-EXPORT_SYMBOL(synaptics_rmi4_bus_exit);
+EXPORT_SYMBOL(synaptics_rmi4_bus_exit_v26);
MODULE_AUTHOR("Synaptics, Inc.");
MODULE_DESCRIPTION("Synaptics DSX SPI Bus Support Module");
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_test_reporting.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_test_reporting.c
index 6ed7bd6..49bec56 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_test_reporting.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_test_reporting.c
@@ -5,6 +5,7 @@
*
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -40,7 +41,7 @@
#include <linux/ctype.h>
#include <linux/hrtimer.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define SYSFS_FOLDER_NAME "f54"
@@ -198,23 +199,13 @@
static ssize_t concat(test_sysfs, _##propname##_show)(\
struct device *dev,\
struct device_attribute *attr,\
- char *buf);\
-\
-static struct device_attribute dev_attr_##propname =\
- __ATTR(propname, S_IRUGO,\
- concat(test_sysfs, _##propname##_show),\
- synaptics_rmi4_store_error);
+ char *buf);
#define store_prototype(propname)\
static ssize_t concat(test_sysfs, _##propname##_store)(\
struct device *dev,\
struct device_attribute *attr,\
- const char *buf, size_t count);\
-\
-static struct device_attribute dev_attr_##propname =\
- __ATTR(propname, S_IWUGO,\
- synaptics_rmi4_show_error,\
- concat(test_sysfs, _##propname##_store));
+ const char *buf, size_t count);
#define show_store_prototype(propname)\
static ssize_t concat(test_sysfs, _##propname##_show)(\
@@ -228,7 +219,7 @@
const char *buf, size_t count);\
\
static struct device_attribute dev_attr_##propname =\
- __ATTR(propname, (S_IRUGO | S_IWUGO),\
+ __ATTR(propname, 0664,\
concat(test_sysfs, _##propname##_show),\
concat(test_sysfs, _##propname##_store));
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_video.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_video.c
index 493410e..bfd03cf 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_video.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_video.c
@@ -5,6 +5,7 @@
*
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -37,7 +38,7 @@
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define SYSFS_FOLDER_NAME "video"
@@ -112,11 +113,11 @@
#endif
static struct device_attribute attrs[] = {
- __ATTR(dcs_write, S_IWUGO,
- synaptics_rmi4_show_error,
+ __ATTR(dcs_write, 0220,
+ NULL,
video_sysfs_dcs_write_store),
- __ATTR(param, S_IWUGO,
- synaptics_rmi4_show_error,
+ __ATTR(param, 0220,
+ NULL,
video_sysfs_param_store),
};
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index d3d975a..7f294f7 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -1547,13 +1547,15 @@
domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
domain->geometry.aperture_end = (1UL << ias) - 1;
domain->geometry.force_aperture = true;
- smmu_domain->pgtbl_ops = pgtbl_ops;
ret = finalise_stage_fn(smmu_domain, &pgtbl_cfg);
- if (ret < 0)
+ if (ret < 0) {
free_io_pgtable_ops(pgtbl_ops);
+ return ret;
+ }
- return ret;
+ smmu_domain->pgtbl_ops = pgtbl_ops;
+ return 0;
}
static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
@@ -1580,7 +1582,7 @@
static int arm_smmu_install_ste_for_dev(struct iommu_fwspec *fwspec)
{
- int i;
+ int i, j;
struct arm_smmu_master_data *master = fwspec->iommu_priv;
struct arm_smmu_device *smmu = master->smmu;
@@ -1588,6 +1590,13 @@
u32 sid = fwspec->ids[i];
__le64 *step = arm_smmu_get_step_for_sid(smmu, sid);
+ /* Bridged PCI devices may end up with duplicated IDs */
+ for (j = 0; j < i; j++)
+ if (fwspec->ids[j] == sid)
+ break;
+ if (j < i)
+ continue;
+
arm_smmu_write_strtab_ent(smmu, sid, step, &master->ste);
}
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index f0e510f..85df514 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -56,6 +56,7 @@
#include <linux/ktime.h>
#include <trace/events/iommu.h>
#include <linux/notifier.h>
+#include <dt-bindings/arm/arm-smmu.h>
#include <linux/amba/bus.h>
#include <soc/qcom/msm_tz_smmu.h>
@@ -355,13 +356,11 @@
enum arm_smmu_s2cr_privcfg privcfg;
u8 cbndx;
bool cb_handoff;
- bool write_protected;
};
#define s2cr_init_val (struct arm_smmu_s2cr){ \
.type = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS, \
.cb_handoff = false, \
- .write_protected = false, \
}
struct arm_smmu_smr {
@@ -433,16 +432,6 @@
#define ARM_SMMU_FEAT_FMT_AARCH32_S (1 << 11)
u32 features;
-#define ARM_SMMU_OPT_SECURE_CFG_ACCESS (1 << 0)
-#define ARM_SMMU_OPT_FATAL_ASF (1 << 1)
-#define ARM_SMMU_OPT_SKIP_INIT (1 << 2)
-#define ARM_SMMU_OPT_DYNAMIC (1 << 3)
-#define ARM_SMMU_OPT_3LVL_TABLES (1 << 4)
-#define ARM_SMMU_OPT_NO_ASID_RETENTION (1 << 5)
-#define ARM_SMMU_OPT_DISABLE_ATOS (1 << 6)
-#define ARM_SMMU_OPT_MMU500_ERRATA1 (1 << 7)
-#define ARM_SMMU_OPT_STATIC_CB (1 << 8)
-#define ARM_SMMU_OPT_HALT (1 << 9)
u32 options;
enum arm_smmu_arch_version version;
enum arm_smmu_implementation model;
@@ -450,6 +439,7 @@
u32 num_context_banks;
u32 num_s2_context_banks;
DECLARE_BITMAP(context_map, ARM_SMMU_MAX_CBS);
+ DECLARE_BITMAP(secure_context_map, ARM_SMMU_MAX_CBS);
atomic_t irptndx;
u32 num_mapping_groups;
@@ -2052,6 +2042,11 @@
arm_smmu_unassign_table(smmu_domain);
arm_smmu_secure_domain_unlock(smmu_domain);
__arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx);
+ /* As the nonsecure context bank index is any way set to zero,
+ * so, directly clearing up the secure cb bitmap.
+ */
+ if (arm_smmu_is_slave_side_secure(smmu_domain))
+ __arm_smmu_free_bitmap(smmu->secure_context_map, cfg->cbndx);
arm_smmu_power_off(smmu->pwr);
arm_smmu_domain_reinit(smmu_domain);
@@ -2777,8 +2772,7 @@
bool arm_smmu_skip_write(void __iomem *addr)
{
struct arm_smmu_device *smmu;
- unsigned long cb;
- int i;
+ int cb;
smmu = arm_smmu_get_by_addr(addr);
@@ -2792,11 +2786,8 @@
/* Finally skip writing to secure CB */
cb = ((unsigned long)addr & ((smmu->size >> 1) - 1)) >> PAGE_SHIFT;
- for (i = 0; i < smmu->num_mapping_groups; i++) {
- if ((smmu->s2crs[i].cbndx == cb) &&
- (smmu->s2crs[i].write_protected))
- return true;
- }
+ if (test_bit(cb, smmu->secure_context_map))
+ return true;
return false;
}
@@ -3736,8 +3727,7 @@
smmu_domain->slave_side_secure = true;
if (arm_smmu_is_slave_side_secure(smmu_domain))
- for_each_cfg_sme(fwspec, i, idx)
- smmu->s2crs[idx].write_protected = true;
+ bitmap_set(smmu->secure_context_map, cb, 1);
}
if (cb < 0 && !arm_smmu_is_static_cb(smmu)) {
@@ -4553,7 +4543,8 @@
if (arm_smmu_power_on(smmu->pwr))
return -EINVAL;
- if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS))
+ if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS) ||
+ !bitmap_empty(smmu->secure_context_map, ARM_SMMU_MAX_CBS))
dev_err(&pdev->dev, "removing device with active domains!\n");
idr_destroy(&smmu->asid_idr);
diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c
index 22a708e..25b85ab 100644
--- a/drivers/iommu/iommu-debug.c
+++ b/drivers/iommu/iommu-debug.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -28,7 +28,7 @@
#include <asm/cacheflush.h>
#include <asm/dma-iommu.h>
-#if defined(CONFIG_IOMMU_DEBUG_TRACKING) || defined(CONFIG_IOMMU_TESTS)
+#if defined(CONFIG_IOMMU_TESTS)
static const char *iommu_debug_attr_to_string(enum iommu_attr attr)
{
@@ -170,6 +170,8 @@
u64 phys;
size_t len;
struct list_head list;
+ struct mutex clk_lock;
+ unsigned int clk_count;
};
static int iommu_debug_build_phoney_sg_table(struct device *dev,
@@ -1195,6 +1197,7 @@
return -ENOMEM;
}
+ val = VMID_CP_CAMERA;
if (is_secure && iommu_domain_set_attr(ddev->domain,
DOMAIN_ATTR_SECURE_VMID,
&val)) {
@@ -1485,6 +1488,10 @@
ssize_t retval;
size_t buflen;
+ if (kptr_restrict != 0) {
+ pr_err("kptr_restrict needs to be disabled.\n");
+ return -EPERM;
+ }
if (!dev->archdata.mapping) {
pr_err("No mapping. Did you already attach?\n");
return -EINVAL;
@@ -1552,6 +1559,10 @@
ssize_t retval;
size_t buflen;
+ if (kptr_restrict != 0) {
+ pr_err("kptr_restrict needs to be disabled.\n");
+ return -EPERM;
+ }
if (!ddev->domain) {
pr_err("No domain. Did you already attach?\n");
return -EINVAL;
@@ -1600,6 +1611,10 @@
ssize_t retval;
size_t buflen;
+ if (kptr_restrict != 0) {
+ pr_err("kptr_restrict needs to be disabled.\n");
+ return -EPERM;
+ }
if (!dev->archdata.mapping) {
pr_err("No mapping. Did you already attach?\n");
return -EINVAL;
@@ -2046,20 +2061,34 @@
return -EFAULT;
}
+ mutex_lock(&ddev->clk_lock);
switch (buf) {
case '0':
+ if (ddev->clk_count == 0) {
+ dev_err(dev, "Config clocks already disabled\n");
+ break;
+ }
+
+ if (--ddev->clk_count > 0)
+ break;
+
dev_err(dev, "Disabling config clocks\n");
iommu_disable_config_clocks(ddev->domain);
break;
case '1':
+ if (ddev->clk_count++ > 0)
+ break;
+
dev_err(dev, "Enabling config clocks\n");
if (iommu_enable_config_clocks(ddev->domain))
dev_err(dev, "Failed!\n");
break;
default:
dev_err(dev, "Invalid value. Should be 0 or 1.\n");
+ mutex_unlock(&ddev->clk_lock);
return -EINVAL;
}
+ mutex_unlock(&ddev->clk_lock);
return count;
}
@@ -2109,6 +2138,9 @@
if (!of_find_property(dev->of_node, "iommus", NULL))
return 0;
+ if (!of_device_is_compatible(dev->of_node, "iommu-debug-test"))
+ return 0;
+
/* Hold a reference count */
if (!iommu_group_get(dev))
return 0;
@@ -2116,6 +2148,7 @@
ddev = kzalloc(sizeof(*ddev), GFP_KERNEL);
if (!ddev)
return -ENODEV;
+ mutex_init(&ddev->clk_lock);
ddev->dev = dev;
dir = debugfs_create_dir(dev_name(dev), debugfs_tests_dir);
if (!dir) {
diff --git a/drivers/irqchip/qcom/Kconfig b/drivers/irqchip/qcom/Kconfig
index b892109..0038047 100644
--- a/drivers/irqchip/qcom/Kconfig
+++ b/drivers/irqchip/qcom/Kconfig
@@ -20,3 +20,10 @@
default y if ARCH_SDM670
help
QTI Power Domain Controller for SDM670
+
+config QTI_PDC_SDXPOORWILLS
+ bool "QTI PDC SDxPOORWILLS"
+ select QTI_PDC
+ default y if ARCH_SDXPOORWILLS
+ help
+ QTI Power Domain Controller for SDxPoorwills
diff --git a/drivers/irqchip/qcom/Makefile b/drivers/irqchip/qcom/Makefile
index 5e99040..c4ff9ef 100644
--- a/drivers/irqchip/qcom/Makefile
+++ b/drivers/irqchip/qcom/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_QTI_PDC) += pdc.o
obj-$(CONFIG_QTI_PDC_SDM845) += pdc-sdm845.o
obj-$(CONFIG_QTI_PDC_SDM670) += pdc-sdm670.o
+obj-$(CONFIG_QTI_PDC_SDXPOORWILLS) += pdc-sdxpoorwills.o
diff --git a/drivers/irqchip/qcom/pdc-sdxpoorwills.c b/drivers/irqchip/qcom/pdc-sdxpoorwills.c
new file mode 100644
index 0000000..5bbca03
--- /dev/null
+++ b/drivers/irqchip/qcom/pdc-sdxpoorwills.c
@@ -0,0 +1,76 @@
+/* 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/irqchip.h>
+#include "pdc.h"
+
+static struct pdc_pin sdxpoorwills_data[] = {
+ {0, 179}, /* rpmh_wake */
+ {1, 180}, /* ee0_apps_hlos_spmi_periph_irq */
+ {2, 181}, /* ee1_apps_trustzone_spmi_periph_irq */
+ {3, 182}, /* secure_wdog_expired */
+ {4, 183}, /* secure_wdog_bark_irq */
+ {5, 184}, /* aop_wdog_expired_irq */
+ {8, 187}, /* aoss_pmic_arb_mpu_xpu_summary_irq */
+ {9, 188}, /* rpmh_wake */
+ {12, 191}, /* pdc_apps_epcb_timeout_summary_irq */
+ {13, 192}, /* spmi_protocol_irq */
+ {14, 193}, /* tsense0_tsense_max_min_int */
+ {15, 194}, /* apps_pdc_irq_in_15 */
+ {16, 195}, /* tsense0_upper_lower_intr */
+ {17, 196}, /* apps_pdc_irq_in_17 */
+ {18, 197}, /* tsense0_critical_intr */
+ {19, 198}, /* apps_pdc_irq_in_19 */
+ {20, 199}, /* apps_pdc.gp_irq_mux[0] */
+ {21, 200}, /* apps_pdc.gp_irq_mux[1] */
+ {22, 201}, /* apps_pdc.gp_irq_mux[2] */
+ {23, 202}, /* apps_pdc.gp_irq_mux[3] */
+ {24, 203}, /* apps_pdc.gp_irq_mux[4] */
+ {25, 204}, /* apps_pdc.gp_irq_mux[5] */
+ {26, 205}, /* apps_pdc.gp_irq_mux[6] */
+ {27, 206}, /* apps_pdc.gp_irq_mux[7] */
+ {28, 207}, /* apps_pdc.gp_irq_mux[8] */
+ {29, 208}, /* apps_pdc.gp_irq_mux[9] */
+ {30, 209}, /* apps_pdc.gp_irq_mux[10] */
+ {31, 210}, /* apps_pdc.gp_irq_mux[11] */
+ {32, 211}, /* apps_pdc.gp_irq_mux[12] */
+ {33, 212}, /* apps_pdc.gp_irq_mux[13] */
+ {34, 213}, /* apps_pdc.gp_irq_mux[14] */
+ {35, 214}, /* apps_pdc.gp_irq_mux[15] */
+ {36, 215}, /* apps_pdc.gp_irq_mux[16] */
+ {37, 216}, /* apps_pdc.gp_irq_mux[17] */
+ {38, 217}, /* apps_pdc.gp_irq_mux[18] */
+ {39, 218}, /* apps_pdc.gp_irq_mux[19] */
+ {40, 219}, /* apps_pdc.gp_irq_mux[20] */
+ {41, 220}, /* apps_pdc.gp_irq_mux[21] */
+ {42, 221}, /* apps_pdc.gp_irq_mux[22] */
+ {43, 222}, /* apps_pdc.gp_irq_mux[23] */
+ {44, 223}, /* apps_pdc.gp_irq_mux[24] */
+ {45, 224}, /* apps_pdc.gp_irq_mux[25] */
+ {46, 225}, /* apps_pdc.gp_irq_mux[26] */
+ {47, 226}, /* apps_pdc.gp_irq_mux[27] */
+ {48, 227}, /* apps_pdc.gp_irq_mux[28] */
+ {49, 228}, /* apps_pdc.gp_irq_mux[29] */
+ {50, 229}, /* apps_pdc.gp_irq_mux[30] */
+ {51, 230}, /* apps_pdc.gp_irq_mux[31] */
+ {-1}
+};
+
+static int __init qcom_pdc_gic_init(struct device_node *node,
+ struct device_node *parent)
+{
+ pr_info("PDC sdxpoowills initialized\n");
+ return qcom_pdc_init(node, parent, sdxpoorwills_data);
+}
+
+IRQCHIP_DECLARE(pdc_sdxpoorwills, "qcom,pdc-sdxpoorwills", qcom_pdc_gic_init);
diff --git a/drivers/irqchip/qcom/pdc.c b/drivers/irqchip/qcom/pdc.c
index 923552f..f4e9799 100644
--- a/drivers/irqchip/qcom/pdc.c
+++ b/drivers/irqchip/qcom/pdc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
+#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/io.h>
#include <linux/kernel.h>
@@ -95,6 +96,20 @@
return 0;
}
+static int qcom_pdc_gic_get_irqchip_state(struct irq_data *d,
+ enum irqchip_irq_state which, bool *state)
+{
+ return d->parent_data->chip->irq_get_irqchip_state(d->parent_data,
+ which, state);
+}
+
+static int qcom_pdc_gic_set_irqchip_state(struct irq_data *d,
+ enum irqchip_irq_state which, bool value)
+{
+ return d->parent_data->chip->irq_set_irqchip_state(d->parent_data,
+ which, value);
+}
+
static void qcom_pdc_gic_mask(struct irq_data *d)
{
pdc_enable_intr(d, false);
@@ -220,6 +235,8 @@
#ifdef CONFIG_SMP
.irq_set_affinity = irq_chip_set_affinity_parent,
#endif
+ .irq_get_irqchip_state = qcom_pdc_gic_get_irqchip_state,
+ .irq_set_irqchip_state = qcom_pdc_gic_set_irqchip_state,
};
static int qcom_pdc_translate(struct irq_domain *d,
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 26e03ba..787bda3 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -625,6 +625,15 @@
To compile this driver as a module, choose 'm' here: the module
will be called leds-powernv.
+config LEDS_QTI_TRI_LED
+ tristate "LED support for Qualcomm Technologies, Inc. TRI_LED"
+ depends on LEDS_CLASS && MFD_SPMI_PMIC && PWM && OF
+ help
+ This driver supports the TRI_LED module found in Qualcomm
+ Technologies, Inc. PMIC chips. TRI_LED supports 3 LED drivers
+ at max and each is controlled by a PWM channel used for dimming
+ or blinking.
+
config LEDS_SYSCON
bool "LED support for LEDs on system controllers"
depends on LEDS_CLASS=y
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 5514391..e9eaa50 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -61,6 +61,7 @@
obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
+obj-$(CONFIG_LEDS_QTI_TRI_LED) += leds-qti-tri-led.o
obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o
obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o
obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
@@ -72,8 +73,8 @@
obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o
obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o
obj-$(CONFIG_LEDS_QPNP) += leds-qpnp.o
-obj-$(CONFIG_LEDS_QPNP_FLASH) += leds-qpnp-flash.o
-obj-$(CONFIG_LEDS_QPNP_FLASH_V2) += leds-qpnp-flash-v2.o
+obj-$(CONFIG_LEDS_QPNP_FLASH) += leds-qpnp-flash.o leds-qpnp-flash-common.o
+obj-$(CONFIG_LEDS_QPNP_FLASH_V2) += leds-qpnp-flash-v2.o leds-qpnp-flash-common.o
obj-$(CONFIG_LEDS_QPNP_WLED) += leds-qpnp-wled.o
obj-$(CONFIG_LEDS_QPNP_HAPTICS) += leds-qpnp-haptics.o
obj-$(CONFIG_LEDS_QPNP_VIBRATOR_LDO) += leds-qpnp-vibrator-ldo.o
diff --git a/drivers/leds/leds-qpnp-flash-common.c b/drivers/leds/leds-qpnp-flash-common.c
new file mode 100644
index 0000000..5aed910
--- /dev/null
+++ b/drivers/leds/leds-qpnp-flash-common.c
@@ -0,0 +1,16 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/leds-qpnp-flash.h>
+
+int (*qpnp_flash_led_prepare)(struct led_trigger *trig, int options,
+ int *max_current);
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index 1a2aea9..f3f9a1a 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -99,11 +99,8 @@
#define VPH_DROOP_DEBOUNCE_US_TO_VAL(val_us) (val_us / 8)
#define VPH_DROOP_HYST_MV_TO_VAL(val_mv) (val_mv / 25)
-#define VPH_DROOP_THRESH_MV_TO_VAL(val_mv) ((val_mv / 100) - 25)
#define VPH_DROOP_THRESH_VAL_TO_UV(val) ((val + 25) * 100000)
#define MITIGATION_THRSH_MA_TO_VAL(val_ma) (val_ma / 100)
-#define CURRENT_MA_TO_REG_VAL(curr_ma, ires_ua) ((curr_ma * 1000) / ires_ua - 1)
-#define SAFETY_TMR_TO_REG_VAL(duration_ms) ((duration_ms / 10) - 1)
#define THERMAL_HYST_TEMP_TO_VAL(val, divisor) (val / divisor)
#define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6
@@ -317,6 +314,14 @@
FLASH_LED_IRES7P5_MAX_CURR_MA, FLASH_LED_IRES5P0_MAX_CURR_MA
};
+static inline int get_current_reg_code(int target_curr_ma, int ires_ua)
+{
+ if (!ires_ua || !target_curr_ma || (target_curr_ma < (ires_ua / 1000)))
+ return 0;
+
+ return DIV_ROUND_UP(target_curr_ma * 1000, ires_ua) - 1;
+}
+
static int qpnp_flash_led_read(struct qpnp_flash_led *led, u16 addr, u8 *data)
{
int rc;
@@ -542,7 +547,7 @@
return rc;
if (led->pdata->led1n2_iclamp_low_ma) {
- val = CURRENT_MA_TO_REG_VAL(led->pdata->led1n2_iclamp_low_ma,
+ val = get_current_reg_code(led->pdata->led1n2_iclamp_low_ma,
led->fnode[LED1].ires_ua);
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_LED1N2_ICLAMP_LOW(led->base),
@@ -552,7 +557,7 @@
}
if (led->pdata->led1n2_iclamp_mid_ma) {
- val = CURRENT_MA_TO_REG_VAL(led->pdata->led1n2_iclamp_mid_ma,
+ val = get_current_reg_code(led->pdata->led1n2_iclamp_mid_ma,
led->fnode[LED1].ires_ua);
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_LED1N2_ICLAMP_MID(led->base),
@@ -562,7 +567,7 @@
}
if (led->pdata->led3_iclamp_low_ma) {
- val = CURRENT_MA_TO_REG_VAL(led->pdata->led3_iclamp_low_ma,
+ val = get_current_reg_code(led->pdata->led3_iclamp_low_ma,
led->fnode[LED3].ires_ua);
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_LED3_ICLAMP_LOW(led->base),
@@ -572,7 +577,7 @@
}
if (led->pdata->led3_iclamp_mid_ma) {
- val = CURRENT_MA_TO_REG_VAL(led->pdata->led3_iclamp_mid_ma,
+ val = get_current_reg_code(led->pdata->led3_iclamp_mid_ma,
led->fnode[LED3].ires_ua);
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_LED3_ICLAMP_MID(led->base),
@@ -992,7 +997,7 @@
}
fnode->current_ma = prgm_current_ma;
fnode->cdev.brightness = prgm_current_ma;
- fnode->current_reg_val = CURRENT_MA_TO_REG_VAL(prgm_current_ma,
+ fnode->current_reg_val = get_current_reg_code(prgm_current_ma,
fnode->ires_ua);
fnode->led_on = prgm_current_ma != 0;
@@ -1103,10 +1108,11 @@
return rc;
}
- /* Iterate over all leds for this switch node */
+ /* Iterate over all active leds for this switch node */
val = 0;
for (i = 0; i < led->num_fnodes; i++)
- if (snode->led_mask & BIT(led->fnode[i].id))
+ if (led->fnode[i].led_on &&
+ snode->led_mask & BIT(led->fnode[i].id))
val |= led->fnode[i].ires_idx << (led->fnode[i].id * 2);
rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_IRES(led->base),
@@ -1210,7 +1216,7 @@
return 0;
}
-int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
+static int qpnp_flash_led_prepare_v2(struct led_trigger *trig, int options,
int *max_current)
{
struct led_classdev *led_cdev;
@@ -1430,6 +1436,22 @@
return atomic_notifier_chain_unregister(&irq_notifier_list, nb);
}
+static inline u8 get_safety_timer_code(u32 duration_ms)
+{
+ if (!duration_ms)
+ return 0;
+
+ return (duration_ms / 10) - 1;
+}
+
+static inline u8 get_vph_droop_thresh_code(u32 val_mv)
+{
+ if (!val_mv)
+ return 0;
+
+ return (val_mv / 100) - 25;
+}
+
static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
struct flash_node_data *fnode, struct device_node *node)
{
@@ -1521,8 +1543,9 @@
fnode->duration = FLASH_LED_SAFETY_TMR_DISABLED;
rc = of_property_read_u32(node, "qcom,duration-ms", &val);
if (!rc) {
- fnode->duration = (u8)(SAFETY_TMR_TO_REG_VAL(val) |
- FLASH_LED_SAFETY_TMR_ENABLE);
+ fnode->duration = get_safety_timer_code(val);
+ if (fnode->duration)
+ fnode->duration |= FLASH_LED_SAFETY_TMR_ENABLE;
} else if (rc == -EINVAL) {
if (fnode->type == FLASH_LED_TYPE_FLASH) {
pr_err("Timer duration is required for flash LED\n");
@@ -1968,7 +1991,7 @@
rc = of_property_read_u32(node, "qcom,vph-droop-threshold-mv", &val);
if (!rc) {
led->pdata->vph_droop_threshold =
- VPH_DROOP_THRESH_MV_TO_VAL(val);
+ get_vph_droop_thresh_code(val);
} else if (rc != -EINVAL) {
pr_err("Unable to read VPH droop threshold, rc=%d\n", rc);
return rc;
@@ -2194,6 +2217,7 @@
if (!led->pdata)
return -ENOMEM;
+ qpnp_flash_led_prepare = qpnp_flash_led_prepare_v2;
rc = qpnp_flash_led_parse_common_dt(led, node);
if (rc < 0) {
pr_err("Failed to parse common flash LED device tree\n");
diff --git a/drivers/leds/leds-qpnp-flash.c b/drivers/leds/leds-qpnp-flash.c
index 3b07af8..ce2b055 100644
--- a/drivers/leds/leds-qpnp-flash.c
+++ b/drivers/leds/leds-qpnp-flash.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1207,7 +1207,7 @@
return rc;
}
-int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
+static int qpnp_flash_led_prepare_v1(struct led_trigger *trig, int options,
int *max_current)
{
struct led_classdev *led_cdev = trigger_to_lcdev(trig);
@@ -2468,6 +2468,7 @@
led->pdev = pdev;
led->current_addr = FLASH_LED0_CURRENT(led->base);
led->current2_addr = FLASH_LED1_CURRENT(led->base);
+ qpnp_flash_led_prepare = qpnp_flash_led_prepare_v1;
led->pdata = devm_kzalloc(&pdev->dev, sizeof(*led->pdata), GFP_KERNEL);
if (!led->pdata)
diff --git a/drivers/leds/leds-qpnp-haptics.c b/drivers/leds/leds-qpnp-haptics.c
index ebdff87..fad36ea 100644
--- a/drivers/leds/leds-qpnp-haptics.c
+++ b/drivers/leds/leds-qpnp-haptics.c
@@ -1,5 +1,4 @@
-/* Copyright (c) 2014-2015, 2017-2018, The Linux Foundation.
- * All rights reserved.
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -275,6 +274,7 @@
* @ last_rate_cfg - Last rate config updated
* @ wave_rep_cnt - waveform repeat count
* @ wave_s_rep_cnt - waveform sample repeat count
+ * @ wf_samp_len - waveform sample length
* @ ext_pwm_freq_khz - external pwm frequency in KHz
* @ ext_pwm_dtest_line - DTEST line for external pwm
* @ status_flags - status
@@ -330,6 +330,7 @@
u16 last_rate_cfg;
u32 wave_rep_cnt;
u32 wave_s_rep_cnt;
+ u32 wf_samp_len;
u32 ext_pwm_freq_khz;
u8 ext_pwm_dtest_line;
u32 status_flags;
@@ -445,6 +446,19 @@
return rc;
}
+static inline int get_buffer_mode_duration(struct hap_chip *chip)
+{
+ int sample_count, sample_duration;
+
+ sample_count = chip->wave_rep_cnt * chip->wave_s_rep_cnt *
+ chip->wf_samp_len;
+ sample_duration = sample_count * chip->wave_play_rate_us;
+ pr_debug("sample_count: %d sample_duration: %d\n", sample_count,
+ sample_duration);
+
+ return (sample_duration / 1000);
+}
+
static bool is_sw_lra_auto_resonance_control(struct hap_chip *chip)
{
if (chip->act_type != HAP_LRA)
@@ -735,11 +749,12 @@
goto out;
}
- if (chip->play_mode != HAP_BUFFER)
- hrtimer_start(&chip->stop_timer,
- ktime_set(time_ms / MSEC_PER_SEC,
- (time_ms % MSEC_PER_SEC) * NSEC_PER_MSEC),
- HRTIMER_MODE_REL);
+ if (chip->play_mode == HAP_BUFFER)
+ time_ms = get_buffer_mode_duration(chip);
+ hrtimer_start(&chip->stop_timer,
+ ktime_set(time_ms / MSEC_PER_SEC,
+ (time_ms % MSEC_PER_SEC) * NSEC_PER_MSEC),
+ HRTIMER_MODE_REL);
rc = qpnp_haptics_auto_res_enable(chip, true);
if (rc < 0) {
@@ -766,6 +781,9 @@
if (chip->play_mode == HAP_PWM)
pwm_disable(chip->pwm_data.pwm_dev);
+
+ if (chip->play_mode == HAP_BUFFER)
+ chip->wave_samp_idx = 0;
}
out:
@@ -1182,8 +1200,11 @@
if (time_ms <= 20) {
wave_samp[0] = HAP_WF_SAMP_MAX;
wave_samp[1] = HAP_WF_SAMP_MAX;
- if (time_ms > 15)
+ chip->wf_samp_len = 2;
+ if (time_ms > 15) {
wave_samp[2] = HAP_WF_SAMP_MAX;
+ chip->wf_samp_len = 3;
+ }
/* short pattern */
rc = qpnp_haptics_parse_buffer_dt(chip);
@@ -1302,33 +1323,20 @@
chip->wave_samp_idx += HAP_WAVE_SAMP_LEN;
if (chip->wave_samp_idx >= ARRAY_SIZE(chip->wave_samp)) {
pr_debug("Samples over\n");
- /* fall through to stop playing */
} else {
pr_debug("moving to next sample set %d\n",
chip->wave_samp_idx);
+ /* Moving to next set of wave sample */
rc = qpnp_haptics_buffer_config(chip, NULL, false);
if (rc < 0) {
pr_err("Error in configuring buffer, rc=%d\n",
rc);
goto irq_handled;
}
-
- /*
- * Moving to next set of wave sample. No need to stop
- * or change the play control. Just return.
- */
- goto irq_handled;
}
}
- rc = qpnp_haptics_play_control(chip, HAP_STOP);
- if (rc < 0) {
- pr_err("Error in disabling play, rc=%d\n", rc);
- goto irq_handled;
- }
- chip->wave_samp_idx = 0;
-
irq_handled:
return IRQ_HANDLED;
}
@@ -1638,6 +1646,7 @@
pos += bytes_read;
}
+ chip->wf_samp_len = i;
for (i = 0; i < ARRAY_SIZE(chip->wave_samp); i++)
chip->wave_samp[i] = samp[i];
@@ -1986,7 +1995,10 @@
/* Use default values */
for (i = 0; i < HAP_WAVE_SAMP_LEN; i++)
chip->wave_samp[i] = HAP_WF_SAMP_MAX;
+
+ wf_samp_len = HAP_WAVE_SAMP_LEN;
}
+ chip->wf_samp_len = wf_samp_len;
return 0;
}
diff --git a/drivers/leds/leds-qti-tri-led.c b/drivers/leds/leds-qti-tri-led.c
new file mode 100644
index 0000000..ab5e876
--- /dev/null
+++ b/drivers/leds/leds-qti-tri-led.c
@@ -0,0 +1,512 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#define TRILED_REG_TYPE 0x04
+#define TRILED_REG_SUBTYPE 0x05
+#define TRILED_REG_EN_CTL 0x46
+
+/* TRILED_REG_EN_CTL */
+#define TRILED_EN_CTL_MASK GENMASK(7, 5)
+#define TRILED_EN_CTL_MAX_BIT 7
+
+#define TRILED_TYPE 0x19
+#define TRILED_SUBTYPE_LED3H0L12 0x02
+#define TRILED_SUBTYPE_LED2H0L12 0x03
+#define TRILED_SUBTYPE_LED1H2L12 0x04
+
+#define TRILED_NUM_MAX 3
+
+#define PWM_PERIOD_DEFAULT_NS 1000000
+#define LED_BLINK_ON_MS 125
+#define LED_BLINK_OFF_MS 875
+
+struct pwm_setting {
+ u32 pre_period_ns;
+ u32 period_ns;
+ u32 duty_ns;
+};
+
+struct led_setting {
+ u32 on_ms;
+ u32 off_ms;
+ enum led_brightness brightness;
+ bool blink;
+};
+
+struct qpnp_led_dev {
+ struct led_classdev cdev;
+ struct pwm_device *pwm_dev;
+ struct pwm_setting pwm_setting;
+ struct led_setting led_setting;
+ struct qpnp_tri_led_chip *chip;
+ struct mutex lock;
+ const char *label;
+ const char *default_trigger;
+ u8 id;
+ bool blinking;
+};
+
+struct qpnp_tri_led_chip {
+ struct device *dev;
+ struct regmap *regmap;
+ struct qpnp_led_dev *leds;
+ struct mutex bus_lock;
+ int num_leds;
+ u16 reg_base;
+ u8 subtype;
+};
+
+static int qpnp_tri_led_read(struct qpnp_tri_led_chip *chip, u16 addr, u8 *val)
+{
+ int rc;
+ unsigned int tmp;
+
+ mutex_lock(&chip->bus_lock);
+ rc = regmap_read(chip->regmap, chip->reg_base + addr, &tmp);
+ if (rc < 0)
+ dev_err(chip->dev, "Read addr 0x%x failed, rc=%d\n", addr, rc);
+ else
+ *val = (u8)tmp;
+ mutex_unlock(&chip->bus_lock);
+
+ return rc;
+}
+
+static int qpnp_tri_led_masked_write(struct qpnp_tri_led_chip *chip,
+ u16 addr, u8 mask, u8 val)
+{
+ int rc;
+
+ mutex_lock(&chip->bus_lock);
+ rc = regmap_update_bits(chip->regmap, chip->reg_base + addr, mask, val);
+ if (rc < 0)
+ dev_err(chip->dev, "Update addr 0x%x to val 0x%x with mask 0x%x failed, rc=%d\n",
+ addr, val, mask, rc);
+ mutex_unlock(&chip->bus_lock);
+
+ return rc;
+}
+
+static int __tri_led_config_pwm(struct qpnp_led_dev *led,
+ struct pwm_setting *pwm)
+{
+ struct pwm_state pstate;
+ int rc;
+
+ pwm_get_state(led->pwm_dev, &pstate);
+ pstate.enabled = !!(pwm->duty_ns != 0);
+ pstate.period = pwm->period_ns;
+ pstate.duty_cycle = pwm->duty_ns;
+ rc = pwm_apply_state(led->pwm_dev, &pstate);
+
+ if (rc < 0)
+ dev_err(led->chip->dev, "Apply PWM state for %s led failed, rc=%d\n",
+ led->cdev.name, rc);
+
+ return rc;
+}
+
+static int __tri_led_set(struct qpnp_led_dev *led)
+{
+ int rc = 0;
+ u8 val = 0, mask = 0;
+
+ rc = __tri_led_config_pwm(led, &led->pwm_setting);
+ if (rc < 0) {
+ dev_err(led->chip->dev, "Configure PWM for %s led failed, rc=%d\n",
+ led->cdev.name, rc);
+ return rc;
+ }
+
+ mask |= 1 << (TRILED_EN_CTL_MAX_BIT - led->id);
+
+ if (led->pwm_setting.duty_ns == 0)
+ val = 0;
+ else
+ val = mask;
+
+ rc = qpnp_tri_led_masked_write(led->chip, TRILED_REG_EN_CTL,
+ mask, val);
+ if (rc < 0)
+ dev_err(led->chip->dev, "Update addr 0x%x failed, rc=%d\n",
+ TRILED_REG_EN_CTL, rc);
+
+ return rc;
+}
+
+static int qpnp_tri_led_set(struct qpnp_led_dev *led)
+{
+ u32 on_ms, off_ms, period_ns, duty_ns;
+ enum led_brightness brightness = led->led_setting.brightness;
+ int rc = 0;
+
+ if (led->led_setting.blink) {
+ on_ms = led->led_setting.on_ms;
+ off_ms = led->led_setting.off_ms;
+ if (on_ms > INT_MAX / NSEC_PER_MSEC)
+ duty_ns = INT_MAX - 1;
+ else
+ duty_ns = on_ms * NSEC_PER_MSEC;
+
+ if (on_ms + off_ms > INT_MAX / NSEC_PER_MSEC) {
+ period_ns = INT_MAX;
+ duty_ns = (period_ns / (on_ms + off_ms)) * on_ms;
+ } else {
+ period_ns = (on_ms + off_ms) * NSEC_PER_MSEC;
+ }
+
+ if (period_ns < duty_ns && duty_ns != 0)
+ period_ns = duty_ns + 1;
+ } else {
+ /* Use initial period if no blinking is required */
+ period_ns = led->pwm_setting.pre_period_ns;
+
+ if (period_ns > INT_MAX / brightness)
+ duty_ns = (period_ns / LED_FULL) * brightness;
+ else
+ duty_ns = (period_ns * brightness) / LED_FULL;
+
+ if (period_ns < duty_ns && duty_ns != 0)
+ period_ns = duty_ns + 1;
+ }
+ dev_dbg(led->chip->dev, "PWM settings for %s led: period = %dns, duty = %dns\n",
+ led->cdev.name, period_ns, duty_ns);
+
+ led->pwm_setting.duty_ns = duty_ns;
+ led->pwm_setting.period_ns = period_ns;
+
+ rc = __tri_led_set(led);
+ if (rc < 0) {
+ dev_err(led->chip->dev, "__tri_led_set %s failed, rc=%d\n",
+ led->cdev.name, rc);
+ return rc;
+ }
+
+ if (led->led_setting.blink) {
+ led->cdev.brightness = LED_FULL;
+ led->blinking = true;
+ } else {
+ led->cdev.brightness = led->led_setting.brightness;
+ led->blinking = false;
+ }
+
+ return rc;
+}
+
+static int qpnp_tri_led_set_brightness(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct qpnp_led_dev *led =
+ container_of(led_cdev, struct qpnp_led_dev, cdev);
+ int rc = 0;
+
+ mutex_lock(&led->lock);
+ if (brightness > LED_FULL)
+ brightness = LED_FULL;
+
+ if (brightness == led->led_setting.brightness &&
+ !led->blinking) {
+ mutex_unlock(&led->lock);
+ return 0;
+ }
+
+ led->led_setting.brightness = brightness;
+ if (!!brightness)
+ led->led_setting.off_ms = 0;
+ else
+ led->led_setting.on_ms = 0;
+ led->led_setting.blink = false;
+
+ rc = qpnp_tri_led_set(led);
+ if (rc)
+ dev_err(led->chip->dev, "Set led failed for %s, rc=%d\n",
+ led->label, rc);
+
+ mutex_unlock(&led->lock);
+
+ return rc;
+}
+
+static enum led_brightness qpnp_tri_led_get_brightness(
+ struct led_classdev *led_cdev)
+{
+ return led_cdev->brightness;
+}
+
+static int qpnp_tri_led_set_blink(struct led_classdev *led_cdev,
+ unsigned long *on_ms, unsigned long *off_ms)
+{
+ struct qpnp_led_dev *led =
+ container_of(led_cdev, struct qpnp_led_dev, cdev);
+ int rc = 0;
+
+ mutex_lock(&led->lock);
+ if (led->blinking && *on_ms == led->led_setting.on_ms &&
+ *off_ms == led->led_setting.off_ms) {
+ dev_dbg(led_cdev->dev, "Ignore, on/off setting is not changed: on %lums, off %lums\n",
+ *on_ms, *off_ms);
+ mutex_unlock(&led->lock);
+ return 0;
+ }
+
+ if (*on_ms == 0) {
+ led->led_setting.blink = false;
+ led->led_setting.brightness = LED_OFF;
+ } else if (*off_ms == 0) {
+ led->led_setting.blink = false;
+ led->led_setting.brightness = led->cdev.brightness;
+ } else {
+ led->led_setting.on_ms = *on_ms;
+ led->led_setting.off_ms = *off_ms;
+ led->led_setting.blink = true;
+ }
+
+ rc = qpnp_tri_led_set(led);
+ if (rc)
+ dev_err(led->chip->dev, "Set led failed for %s, rc=%d\n",
+ led->label, rc);
+
+ mutex_unlock(&led->lock);
+ return rc;
+}
+
+static int qpnp_tri_led_register(struct qpnp_tri_led_chip *chip)
+{
+ struct qpnp_led_dev *led;
+ int rc, i, j;
+
+ for (i = 0; i < chip->num_leds; i++) {
+ led = &chip->leds[i];
+ mutex_init(&led->lock);
+ led->cdev.name = led->label;
+ led->cdev.max_brightness = LED_FULL;
+ led->cdev.brightness_set_blocking = qpnp_tri_led_set_brightness;
+ led->cdev.brightness_get = qpnp_tri_led_get_brightness;
+ led->cdev.blink_set = qpnp_tri_led_set_blink;
+ led->cdev.default_trigger = led->default_trigger;
+ led->cdev.brightness = LED_OFF;
+ led->cdev.blink_delay_on = LED_BLINK_ON_MS;
+ led->cdev.blink_delay_off = LED_BLINK_OFF_MS;
+
+ rc = devm_led_classdev_register(chip->dev, &led->cdev);
+ if (rc < 0) {
+ dev_err(chip->dev, "%s led class device registering failed, rc=%d\n",
+ led->label, rc);
+ goto destroy;
+ }
+ }
+
+ return 0;
+destroy:
+ for (j = 0; j <= i; j++)
+ mutex_destroy(&chip->leds[i].lock);
+
+ return rc;
+}
+
+static int qpnp_tri_led_hw_init(struct qpnp_tri_led_chip *chip)
+{
+ int rc = 0;
+ u8 val;
+
+ rc = qpnp_tri_led_read(chip, TRILED_REG_TYPE, &val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Read REG_TYPE failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (val != TRILED_TYPE) {
+ dev_err(chip->dev, "invalid subtype(%d)\n", val);
+ return -ENODEV;
+ }
+
+ rc = qpnp_tri_led_read(chip, TRILED_REG_SUBTYPE, &val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Read REG_SUBTYPE failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ chip->subtype = val;
+
+ return 0;
+}
+
+static int qpnp_tri_led_parse_dt(struct qpnp_tri_led_chip *chip)
+{
+ struct device_node *node = chip->dev->of_node, *child_node;
+ struct qpnp_led_dev *led;
+ struct pwm_args pargs;
+ const __be32 *addr;
+ int rc, id, i = 0;
+
+ addr = of_get_address(chip->dev->of_node, 0, NULL, NULL);
+ if (!addr) {
+ dev_err(chip->dev, "Getting address failed\n");
+ return -EINVAL;
+ }
+ chip->reg_base = be32_to_cpu(addr[0]);
+
+ chip->num_leds = of_get_available_child_count(node);
+ if (chip->num_leds == 0) {
+ dev_err(chip->dev, "No led child node defined\n");
+ return -ENODEV;
+ }
+
+ if (chip->num_leds > TRILED_NUM_MAX) {
+ dev_err(chip->dev, "can't support %d leds(max %d)\n",
+ chip->num_leds, TRILED_NUM_MAX);
+ return -EINVAL;
+ }
+
+ chip->leds = devm_kcalloc(chip->dev, chip->num_leds,
+ sizeof(struct qpnp_led_dev), GFP_KERNEL);
+ if (!chip->leds)
+ return -ENOMEM;
+
+ for_each_available_child_of_node(node, child_node) {
+ rc = of_property_read_u32(child_node, "led-sources", &id);
+ if (rc) {
+ dev_err(chip->dev, "Get led-sources failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ if (id >= TRILED_NUM_MAX) {
+ dev_err(chip->dev, "only support 0~%d current source\n",
+ TRILED_NUM_MAX - 1);
+ return -EINVAL;
+ }
+
+ led = &chip->leds[i++];
+ led->chip = chip;
+ led->id = id;
+ led->label =
+ of_get_property(child_node, "label", NULL) ? :
+ child_node->name;
+
+ led->pwm_dev =
+ devm_of_pwm_get(chip->dev, child_node, NULL);
+ if (IS_ERR(led->pwm_dev)) {
+ rc = PTR_ERR(led->pwm_dev);
+ if (rc != -EPROBE_DEFER)
+ dev_err(chip->dev, "Get pwm device for %s led failed, rc=%d\n",
+ led->label, rc);
+ return rc;
+ }
+
+ pwm_get_args(led->pwm_dev, &pargs);
+ if (pargs.period == 0)
+ led->pwm_setting.pre_period_ns = PWM_PERIOD_DEFAULT_NS;
+ else
+ led->pwm_setting.pre_period_ns = pargs.period;
+
+ led->default_trigger = of_get_property(child_node,
+ "linux,default-trigger", NULL);
+ }
+
+ return rc;
+}
+
+static int qpnp_tri_led_probe(struct platform_device *pdev)
+{
+ struct qpnp_tri_led_chip *chip;
+ int rc = 0;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = &pdev->dev;
+ chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
+ if (!chip->regmap) {
+ dev_err(chip->dev, "Getting regmap failed\n");
+ return -EINVAL;
+ }
+
+ rc = qpnp_tri_led_parse_dt(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ mutex_init(&chip->bus_lock);
+
+ rc = qpnp_tri_led_hw_init(chip);
+ if (rc) {
+ dev_err(chip->dev, "HW initialization failed, rc=%d\n", rc);
+ goto destroy;
+ }
+
+ dev_set_drvdata(chip->dev, chip);
+ rc = qpnp_tri_led_register(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Registering LED class devices failed, rc=%d\n",
+ rc);
+ goto destroy;
+ }
+
+ dev_dbg(chip->dev, "Tri-led module with subtype 0x%x is detected\n",
+ chip->subtype);
+ return 0;
+destroy:
+ mutex_destroy(&chip->bus_lock);
+ dev_set_drvdata(chip->dev, NULL);
+
+ return rc;
+}
+
+static int qpnp_tri_led_remove(struct platform_device *pdev)
+{
+ int i;
+ struct qpnp_tri_led_chip *chip = dev_get_drvdata(&pdev->dev);
+
+ mutex_destroy(&chip->bus_lock);
+ for (i = 0; i < chip->num_leds; i++)
+ mutex_destroy(&chip->leds[i].lock);
+ dev_set_drvdata(chip->dev, NULL);
+ return 0;
+}
+
+static const struct of_device_id qpnp_tri_led_of_match[] = {
+ { .compatible = "qcom,tri-led",},
+ { },
+};
+
+static struct platform_driver qpnp_tri_led_driver = {
+ .driver = {
+ .name = "qcom,tri-led",
+ .of_match_table = qpnp_tri_led_of_match,
+ },
+ .probe = qpnp_tri_led_probe,
+ .remove = qpnp_tri_led_remove,
+};
+module_platform_driver(qpnp_tri_led_driver);
+
+MODULE_DESCRIPTION("QTI TRI_LED driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("leds:qpnp-tri-led");
diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c
index 2efdce0..cac297f 100644
--- a/drivers/md/bcache/btree.c
+++ b/drivers/md/bcache/btree.c
@@ -803,7 +803,10 @@
c->shrink.scan_objects = bch_mca_scan;
c->shrink.seeks = 4;
c->shrink.batch = c->btree_pages * 2;
- register_shrinker(&c->shrink);
+
+ if (register_shrinker(&c->shrink))
+ pr_warn("bcache: %s: could not register shrinker",
+ __func__);
return 0;
}
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index 7643f72..3ec647e 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -1554,7 +1554,8 @@
int l;
struct dm_buffer *b, *tmp;
unsigned long freed = 0;
- unsigned long count = nr_to_scan;
+ unsigned long count = c->n_buffers[LIST_CLEAN] +
+ c->n_buffers[LIST_DIRTY];
unsigned long retain_target = get_retain_buffers(c);
for (l = 0; l < LIST_SIZE; l++) {
@@ -1591,6 +1592,7 @@
{
struct dm_bufio_client *c;
unsigned long count;
+ unsigned long retain_target;
c = container_of(shrink, struct dm_bufio_client, shrinker);
if (sc->gfp_mask & __GFP_FS)
@@ -1599,8 +1601,9 @@
return 0;
count = c->n_buffers[LIST_CLEAN] + c->n_buffers[LIST_DIRTY];
+ retain_target = get_retain_buffers(c);
dm_bufio_unlock(c);
- return count;
+ return (count < retain_target) ? 0 : (count - retain_target);
}
/*
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c
index 4477bf9..e976f4f 100644
--- a/drivers/md/dm-thin-metadata.c
+++ b/drivers/md/dm-thin-metadata.c
@@ -81,10 +81,14 @@
#define SECTOR_TO_BLOCK_SHIFT 3
/*
+ * For btree insert:
* 3 for btree insert +
* 2 for btree lookup used within space map
+ * For btree remove:
+ * 2 for shadow spine +
+ * 4 for rebalance 3 child node
*/
-#define THIN_MAX_CONCURRENT_LOCKS 5
+#define THIN_MAX_CONCURRENT_LOCKS 6
/* This should be plenty */
#define SPACE_MAP_ROOT_SIZE 128
diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c
index 7a75b50..e4ececd 100644
--- a/drivers/md/persistent-data/dm-btree.c
+++ b/drivers/md/persistent-data/dm-btree.c
@@ -678,23 +678,8 @@
pn->keys[1] = rn->keys[0];
memcpy_disk(value_ptr(pn, 1), &val, sizeof(__le64));
- /*
- * rejig the spine. This is ugly, since it knows too
- * much about the spine
- */
- if (s->nodes[0] != new_parent) {
- unlock_block(s->info, s->nodes[0]);
- s->nodes[0] = new_parent;
- }
- if (key < le64_to_cpu(rn->keys[0])) {
- unlock_block(s->info, right);
- s->nodes[1] = left;
- } else {
- unlock_block(s->info, left);
- s->nodes[1] = right;
- }
- s->count = 2;
-
+ unlock_block(s->info, left);
+ unlock_block(s->info, right);
return 0;
}
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context.c b/drivers/media/platform/msm/camera/cam_core/cam_context.c
index 7e6d999..d1222aa 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -196,6 +196,30 @@
return rc;
}
+int cam_context_handle_crm_process_evt(struct cam_context *ctx,
+ struct cam_req_mgr_link_evt_data *process_evt)
+{
+ int rc = 0;
+
+ if (!ctx->state_machine) {
+ CAM_ERR(CAM_CORE, "Context is not ready");
+ return -EINVAL;
+ }
+
+ mutex_lock(&ctx->ctx_mutex);
+ if (ctx->state_machine[ctx->state].crm_ops.process_evt) {
+ rc = ctx->state_machine[ctx->state].crm_ops.process_evt(ctx,
+ process_evt);
+ } else {
+ /* handling of this message is optional */
+ CAM_DBG(CAM_CORE, "No crm process evt in dev %d, state %d",
+ ctx->dev_hdl, ctx->state);
+ }
+ mutex_unlock(&ctx->ctx_mutex);
+
+ return rc;
+}
+
int cam_context_handle_acquire_dev(struct cam_context *ctx,
struct cam_acquire_dev_cmd *cmd)
{
@@ -257,10 +281,10 @@
int cam_context_handle_flush_dev(struct cam_context *ctx,
struct cam_flush_dev_cmd *cmd)
{
- int rc;
+ int rc = 0;
if (!ctx->state_machine) {
- CAM_ERR(CAM_CORE, "context is not ready");
+ CAM_ERR(CAM_CORE, "Context is not ready");
return -EINVAL;
}
@@ -274,9 +298,8 @@
rc = ctx->state_machine[ctx->state].ioctl_ops.flush_dev(
ctx, cmd);
} else {
- CAM_ERR(CAM_CORE, "No flush device in dev %d, state %d",
+ CAM_WARN(CAM_CORE, "No flush device in dev %d, state %d",
ctx->dev_hdl, ctx->state);
- rc = -EPROTO;
}
mutex_unlock(&ctx->ctx_mutex);
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context.h b/drivers/media/platform/msm/camera/cam_core/cam_context.h
index c823b7a..af92b7e 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context.h
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -110,6 +110,7 @@
* @unlink: Unlink the context
* @apply_req: Apply setting for the context
* @flush_req: Flush request to remove request ids
+ * @process_evt: Handle event notification from CRM.(optional)
*
*/
struct cam_ctx_crm_ops {
@@ -123,6 +124,8 @@
struct cam_req_mgr_apply_request *apply);
int (*flush_req)(struct cam_context *ctx,
struct cam_req_mgr_flush_request *flush);
+ int (*process_evt)(struct cam_context *ctx,
+ struct cam_req_mgr_link_evt_data *evt_data);
};
@@ -273,6 +276,18 @@
struct cam_req_mgr_flush_request *apply);
/**
+ * cam_context_handle_crm_process_evt()
+ *
+ * @brief: Handle process event command
+ *
+ * @ctx: Object pointer for cam_context
+ * @process_evt: process event command payload
+ *
+ */
+int cam_context_handle_crm_process_evt(struct cam_context *ctx,
+ struct cam_req_mgr_link_evt_data *process_evt);
+
+/**
* cam_context_handle_acquire_dev()
*
* @brief: Handle acquire device command
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
index aab1a1a..8ea920d 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -430,6 +430,8 @@
uint32_t i;
int rc = 0;
+ CAM_DBG(CAM_CTXT, "E: NRT flush ctx");
+
/*
* flush pending requests, take the sync lock to synchronize with the
* sync callback thread so that the sync cb thread does not try to
@@ -444,23 +446,33 @@
while (!list_empty(&temp_list)) {
req = list_first_entry(&temp_list,
struct cam_ctx_request, list);
+
list_del_init(&req->list);
req->flushed = 1;
+
flush_args.flush_req_pending[flush_args.num_req_pending++] =
req->req_priv;
for (i = 0; i < req->num_out_map_entries; i++)
- if (req->out_map_entries[i].sync_id != -1)
- cam_sync_signal(req->out_map_entries[i].sync_id,
+ if (req->out_map_entries[i].sync_id != -1) {
+ rc = cam_sync_signal(
+ req->out_map_entries[i].sync_id,
CAM_SYNC_STATE_SIGNALED_ERROR);
+ if (rc == -EALREADY) {
+ CAM_ERR(CAM_CTXT,
+ "Req: %llu already signalled, sync_id:%d",
+ req->request_id,
+ req->out_map_entries[i].
+ sync_id);
+ break;
+ }
+ }
}
mutex_unlock(&ctx->sync_mutex);
if (ctx->hw_mgr_intf->hw_flush) {
flush_args.num_req_active = 0;
spin_lock(&ctx->lock);
- INIT_LIST_HEAD(&temp_list);
- list_splice_init(&ctx->active_req_list, &temp_list);
- list_for_each_entry(req, &temp_list, list) {
+ list_for_each_entry(req, &ctx->active_req_list, list) {
flush_args.flush_req_active[flush_args.num_req_active++]
= req->req_priv;
}
@@ -474,24 +486,42 @@
}
}
+ INIT_LIST_HEAD(&temp_list);
+ spin_lock(&ctx->lock);
+ list_splice_init(&ctx->active_req_list, &temp_list);
+ INIT_LIST_HEAD(&ctx->active_req_list);
+ spin_unlock(&ctx->lock);
+
while (!list_empty(&temp_list)) {
req = list_first_entry(&temp_list,
struct cam_ctx_request, list);
list_del_init(&req->list);
- for (i = 0; i < req->num_out_map_entries; i++)
+ for (i = 0; i < req->num_out_map_entries; i++) {
if (req->out_map_entries[i].sync_id != -1) {
- cam_sync_signal(req->out_map_entries[i].sync_id,
+ rc = cam_sync_signal(
+ req->out_map_entries[i].sync_id,
CAM_SYNC_STATE_SIGNALED_ERROR);
+ if (rc == -EALREADY) {
+ CAM_ERR(CAM_CTXT,
+ "Req: %llu already signalled ctx: %pK dev_name: %s dev_handle: %d ctx_state: %d",
+ req->request_id, req->ctx,
+ req->ctx->dev_name,
+ req->ctx->dev_hdl,
+ req->ctx->state);
+ break;
+ }
}
+ }
spin_lock(&ctx->lock);
list_add_tail(&req->list, &ctx->free_req_list);
spin_unlock(&ctx->lock);
req->ctx = NULL;
}
- INIT_LIST_HEAD(&ctx->active_req_list);
- return rc;
+ CAM_DBG(CAM_CTXT, "X: NRT flush ctx");
+
+ return 0;
}
int32_t cam_context_flush_req_to_hw(struct cam_context *ctx,
@@ -502,6 +532,8 @@
uint32_t i;
int rc = 0;
+ CAM_DBG(CAM_CTXT, "E: NRT flush req");
+
flush_args.num_req_pending = 0;
flush_args.num_req_active = 0;
mutex_lock(&ctx->sync_mutex);
@@ -510,7 +542,9 @@
if (req->request_id != cmd->req_id)
continue;
+ list_del_init(&req->list);
req->flushed = 1;
+
flush_args.flush_req_pending[flush_args.num_req_pending++] =
req->req_priv;
break;
@@ -525,6 +559,8 @@
if (req->request_id != cmd->req_id)
continue;
+ list_del_init(&req->list);
+
flush_args.flush_req_active[
flush_args.num_req_active++] =
req->req_priv;
@@ -543,20 +579,31 @@
if (req) {
if (flush_args.num_req_pending || flush_args.num_req_active) {
- list_del_init(&req->list);
for (i = 0; i < req->num_out_map_entries; i++)
- if (req->out_map_entries[i].sync_id != -1)
- cam_sync_signal(
+ if (req->out_map_entries[i].sync_id != -1) {
+ rc = cam_sync_signal(
req->out_map_entries[i].sync_id,
CAM_SYNC_STATE_SIGNALED_ERROR);
- spin_lock(&ctx->lock);
- list_add_tail(&req->list, &ctx->free_req_list);
- spin_unlock(&ctx->lock);
- req->ctx = NULL;
+ if (rc == -EALREADY) {
+ CAM_ERR(CAM_CTXT,
+ "Req: %llu already signalled, sync_id:%d",
+ req->request_id,
+ req->out_map_entries[i].
+ sync_id);
+ break;
+ }
+ }
+ if (flush_args.num_req_active) {
+ spin_lock(&ctx->lock);
+ list_add_tail(&req->list, &ctx->free_req_list);
+ spin_unlock(&ctx->lock);
+ req->ctx = NULL;
+ }
}
}
+ CAM_DBG(CAM_CTXT, "X: NRT flush req");
- return rc;
+ return 0;
}
int32_t cam_context_flush_dev_to_hw(struct cam_context *ctx,
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_node.c b/drivers/media/platform/msm/camera/cam_core/cam_node.c
index a5977b3..4e9034e 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_node.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_node.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -220,7 +220,7 @@
rc = cam_context_handle_flush_dev(ctx, flush);
if (rc)
- CAM_ERR(CAM_CORE, "FLush failure for node %s", node->name);
+ CAM_ERR(CAM_CORE, "Flush failure for node %s", node->name);
return rc;
}
@@ -342,6 +342,25 @@
return cam_context_handle_crm_flush_req(ctx, flush);
}
+static int __cam_node_crm_process_evt(
+ struct cam_req_mgr_link_evt_data *evt_data)
+{
+ struct cam_context *ctx = NULL;
+
+ if (!evt_data) {
+ CAM_ERR(CAM_CORE, "Invalid process event request payload");
+ return -EINVAL;
+ }
+
+ ctx = (struct cam_context *) cam_get_device_priv(evt_data->dev_hdl);
+ if (!ctx) {
+ CAM_ERR(CAM_CORE, "Can not get context for handle %d",
+ evt_data->dev_hdl);
+ return -EINVAL;
+ }
+ return cam_context_handle_crm_process_evt(ctx, evt_data);
+}
+
int cam_node_deinit(struct cam_node *node)
{
if (node)
@@ -394,6 +413,7 @@
node->crm_node_intf.get_dev_info = __cam_node_crm_get_dev_info;
node->crm_node_intf.link_setup = __cam_node_crm_link_setup;
node->crm_node_intf.flush_req = __cam_node_crm_flush_req;
+ node->crm_node_intf.process_evt = __cam_node_crm_process_evt;
mutex_init(&node->list_mutex);
INIT_LIST_HEAD(&node->free_ctx_list);
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
index 3ebe7a1..b04bc23 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -16,12 +16,19 @@
#include <linux/msm-bus.h>
#include <linux/pm_opp.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include "cam_cpas_hw.h"
#include "cam_cpas_hw_intf.h"
#include "cam_cpas_soc.h"
-#define CAM_CPAS_AXI_MIN_BW (2048 * 1024)
+#define CAM_CPAS_AXI_MIN_MNOC_AB_BW (2048 * 1024)
+#define CAM_CPAS_AXI_MIN_MNOC_IB_BW (2048 * 1024)
+#define CAM_CPAS_AXI_MIN_CAMNOC_AB_BW (2048 * 1024)
+#define CAM_CPAS_AXI_MIN_CAMNOC_IB_BW (3000000000L)
+
+static uint cam_min_camnoc_ib_bw;
+module_param(cam_min_camnoc_ib_bw, uint, 0644);
int cam_cpas_util_reg_update(struct cam_hw_info *cpas_hw,
enum cam_cpas_reg_base reg_base, struct cam_cpas_reg *reg_info)
@@ -84,11 +91,19 @@
}
static int cam_cpas_util_vote_bus_client_bw(
- struct cam_cpas_bus_client *bus_client, uint64_t ab, uint64_t ib)
+ struct cam_cpas_bus_client *bus_client, uint64_t ab, uint64_t ib,
+ bool camnoc_bw)
{
struct msm_bus_paths *path;
struct msm_bus_scale_pdata *pdata;
int idx = 0;
+ uint64_t min_camnoc_ib_bw = CAM_CPAS_AXI_MIN_CAMNOC_IB_BW;
+
+ if (cam_min_camnoc_ib_bw > 0)
+ min_camnoc_ib_bw = (uint64_t)cam_min_camnoc_ib_bw * 1000000L;
+
+ CAM_DBG(CAM_CPAS, "cam_min_camnoc_ib_bw = %d, min_camnoc_ib_bw=%llu",
+ cam_min_camnoc_ib_bw, min_camnoc_ib_bw);
if (!bus_client->valid) {
CAM_ERR(CAM_CPAS, "bus client not valid");
@@ -118,11 +133,19 @@
bus_client->curr_vote_level = idx;
mutex_unlock(&bus_client->lock);
- if ((ab > 0) && (ab < CAM_CPAS_AXI_MIN_BW))
- ab = CAM_CPAS_AXI_MIN_BW;
+ if (camnoc_bw == true) {
+ if ((ab > 0) && (ab < CAM_CPAS_AXI_MIN_CAMNOC_AB_BW))
+ ab = CAM_CPAS_AXI_MIN_CAMNOC_AB_BW;
- if ((ib > 0) && (ib < CAM_CPAS_AXI_MIN_BW))
- ib = CAM_CPAS_AXI_MIN_BW;
+ if ((ib > 0) && (ib < min_camnoc_ib_bw))
+ ib = min_camnoc_ib_bw;
+ } else {
+ if ((ab > 0) && (ab < CAM_CPAS_AXI_MIN_MNOC_AB_BW))
+ ab = CAM_CPAS_AXI_MIN_MNOC_AB_BW;
+
+ if ((ib > 0) && (ib < CAM_CPAS_AXI_MIN_MNOC_IB_BW))
+ ib = CAM_CPAS_AXI_MIN_MNOC_IB_BW;
+ }
pdata = bus_client->pdata;
path = &(pdata->usecase[idx]);
@@ -205,7 +228,7 @@
return -EINVAL;
if (bus_client->dyn_vote)
- cam_cpas_util_vote_bus_client_bw(bus_client, 0, 0);
+ cam_cpas_util_vote_bus_client_bw(bus_client, 0, 0, false);
else
cam_cpas_util_vote_bus_client_level(bus_client, 0);
@@ -370,7 +393,7 @@
list_for_each_entry_safe(curr_port, temp_port,
&cpas_core->axi_ports_list_head, sibling_port) {
rc = cam_cpas_util_vote_bus_client_bw(&curr_port->mnoc_bus,
- mnoc_bw, mnoc_bw);
+ mnoc_bw, mnoc_bw, false);
if (rc) {
CAM_ERR(CAM_CPAS,
"Failed in mnoc vote, enable=%d, rc=%d",
@@ -380,13 +403,13 @@
if (soc_private->axi_camnoc_based) {
cam_cpas_util_vote_bus_client_bw(
- &curr_port->camnoc_bus, 0, camnoc_bw);
+ &curr_port->camnoc_bus, 0, camnoc_bw, true);
if (rc) {
CAM_ERR(CAM_CPAS,
"Failed in mnoc vote, enable=%d, %d",
enable, rc);
cam_cpas_util_vote_bus_client_bw(
- &curr_port->mnoc_bus, 0, 0);
+ &curr_port->mnoc_bus, 0, 0, false);
goto remove_ahb_vote;
}
}
@@ -571,7 +594,7 @@
camnoc_bw, mnoc_bw);
rc = cam_cpas_util_vote_bus_client_bw(&axi_port->mnoc_bus,
- mnoc_bw, mnoc_bw);
+ mnoc_bw, mnoc_bw, false);
if (rc) {
CAM_ERR(CAM_CPAS,
"Failed in mnoc vote ab[%llu] ib[%llu] rc=%d",
@@ -581,7 +604,7 @@
if (soc_private->axi_camnoc_based) {
rc = cam_cpas_util_vote_bus_client_bw(&axi_port->camnoc_bus,
- 0, camnoc_bw);
+ 0, camnoc_bw, true);
if (rc) {
CAM_ERR(CAM_CPAS,
"Failed camnoc vote ab[%llu] ib[%llu] rc=%d",
@@ -880,9 +903,11 @@
goto done;
if (cpas_core->streamon_clients == 0) {
+ atomic_set(&cpas_core->irq_count, 1);
rc = cam_cpas_soc_enable_resources(&cpas_hw->soc_info,
applied_level);
if (rc) {
+ atomic_set(&cpas_core->irq_count, 0);
CAM_ERR(CAM_CPAS, "enable_resorce failed, rc=%d", rc);
goto done;
}
@@ -890,14 +915,17 @@
if (cpas_core->internal_ops.power_on) {
rc = cpas_core->internal_ops.power_on(cpas_hw);
if (rc) {
+ atomic_set(&cpas_core->irq_count, 0);
cam_cpas_soc_disable_resources(
- &cpas_hw->soc_info);
+ &cpas_hw->soc_info, true, true);
CAM_ERR(CAM_CPAS,
"failed in power_on settings rc=%d",
rc);
goto done;
}
}
+ CAM_DBG(CAM_CPAS, "irq_count=%d\n",
+ atomic_read(&cpas_core->irq_count));
cpas_hw->hw_state = CAM_HW_STATE_POWER_UP;
}
@@ -912,6 +940,10 @@
return rc;
}
+static int _check_irq_count(struct cam_cpas *cpas_core)
+{
+ return (atomic_read(&cpas_core->irq_count) > 0) ? 0 : 1;
+}
static int cam_cpas_hw_stop(void *hw_priv, void *stop_args,
uint32_t arg_size)
@@ -924,6 +956,7 @@
struct cam_ahb_vote ahb_vote;
struct cam_axi_vote axi_vote;
int rc = 0;
+ long result;
if (!hw_priv || !stop_args) {
CAM_ERR(CAM_CPAS, "Invalid arguments %pK %pK",
@@ -972,11 +1005,29 @@
}
}
- rc = cam_cpas_soc_disable_resources(&cpas_hw->soc_info);
+ rc = cam_cpas_soc_disable_irq(&cpas_hw->soc_info);
+ if (rc) {
+ CAM_ERR(CAM_CPAS, "disable_irq failed, rc=%d", rc);
+ goto done;
+ }
+
+ /* Wait for any IRQs still being handled */
+ atomic_dec(&cpas_core->irq_count);
+ result = wait_event_timeout(cpas_core->irq_count_wq,
+ _check_irq_count(cpas_core), HZ);
+ if (result == 0) {
+ CAM_ERR(CAM_CPAS, "Wait failed: irq_count=%d",
+ atomic_read(&cpas_core->irq_count));
+ }
+
+ rc = cam_cpas_soc_disable_resources(&cpas_hw->soc_info,
+ true, false);
if (rc) {
CAM_ERR(CAM_CPAS, "disable_resorce failed, rc=%d", rc);
goto done;
}
+ CAM_DBG(CAM_CPAS, "Disabled all the resources: irq_count=%d\n",
+ atomic_read(&cpas_core->irq_count));
cpas_hw->hw_state = CAM_HW_STATE_POWER_DOWN;
}
@@ -1427,6 +1478,8 @@
soc_private = (struct cam_cpas_private_soc *)
cpas_hw->soc_info.soc_private;
cpas_core->num_clients = soc_private->num_clients;
+ atomic_set(&cpas_core->irq_count, 0);
+ init_waitqueue_head(&cpas_core->irq_count_wq);
if (internal_ops->setup_regbase) {
rc = internal_ops->setup_regbase(&cpas_hw->soc_info,
@@ -1482,7 +1535,7 @@
if (rc)
goto disable_soc_res;
- rc = cam_cpas_soc_disable_resources(&cpas_hw->soc_info);
+ rc = cam_cpas_soc_disable_resources(&cpas_hw->soc_info, true, true);
if (rc) {
CAM_ERR(CAM_CPAS, "failed in soc_disable_resources, rc=%d", rc);
goto remove_default_vote;
@@ -1500,7 +1553,7 @@
return 0;
disable_soc_res:
- cam_cpas_soc_disable_resources(&cpas_hw->soc_info);
+ cam_cpas_soc_disable_resources(&cpas_hw->soc_info, true, true);
remove_default_vote:
cam_cpas_util_vote_default_ahb_axi(cpas_hw, false);
axi_cleanup:
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
index aa3663d..05840bb 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -187,6 +187,8 @@
struct list_head axi_ports_list_head;
struct cam_cpas_internal_ops internal_ops;
struct workqueue_struct *work_queue;
+ atomic_t irq_count;
+ wait_queue_head_t irq_count_wq;
};
int cam_camsstop_get_internal_ops(struct cam_cpas_internal_ops *internal_ops);
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c
index d5108f6..0187a64 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -366,7 +366,6 @@
switch (cmd->op_code) {
case CAM_QUERY_CAP: {
struct cam_cpas_query_cap query;
- uint32_t cam_cpas;
rc = copy_from_user(&query, (void __user *) cmd->handle,
sizeof(query));
@@ -377,7 +376,8 @@
}
rc = cam_cpas_get_hw_info(&query.camera_family,
- &query.camera_version, &query.cpas_version, &cam_cpas);
+ &query.camera_version, &query.cpas_version,
+ &query.reserved);
if (rc)
break;
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
index f85f461..b18af0a 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -209,13 +209,26 @@
return rc;
}
-int cam_cpas_soc_disable_resources(struct cam_hw_soc_info *soc_info)
+int cam_cpas_soc_disable_resources(struct cam_hw_soc_info *soc_info,
+ bool disable_clocks, bool disble_irq)
{
int rc = 0;
- rc = cam_soc_util_disable_platform_resource(soc_info, true, true);
+ rc = cam_soc_util_disable_platform_resource(soc_info,
+ disable_clocks, disble_irq);
if (rc)
CAM_ERR(CAM_CPAS, "disable platform failed, rc=%d", rc);
return rc;
}
+
+int cam_cpas_soc_disable_irq(struct cam_hw_soc_info *soc_info)
+{
+ int rc = 0;
+
+ rc = cam_soc_util_irq_disable(soc_info);
+ if (rc)
+ CAM_ERR(CAM_CPAS, "disable irq failed, rc=%d", rc);
+
+ return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
index d4fc039..fe0187e 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -61,5 +61,7 @@
int cam_cpas_soc_deinit_resources(struct cam_hw_soc_info *soc_info);
int cam_cpas_soc_enable_resources(struct cam_hw_soc_info *soc_info,
enum cam_vote_level default_level);
-int cam_cpas_soc_disable_resources(struct cam_hw_soc_info *soc_info);
+int cam_cpas_soc_disable_resources(struct cam_hw_soc_info *soc_info,
+ bool disable_clocks, bool disble_irq);
+int cam_cpas_soc_disable_irq(struct cam_hw_soc_info *soc_info);
#endif /* _CAM_CPAS_SOC_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
index 0e5ce85..0533ed8 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -354,6 +354,11 @@
cpas_core = (struct cam_cpas *) cpas_hw->core_info;
soc_info = &cpas_hw->soc_info;
+ if (!atomic_inc_not_zero(&cpas_core->irq_count)) {
+ CAM_ERR(CAM_CPAS, "CPAS off");
+ return;
+ }
+
for (i = 0; i < camnoc_info->irq_err_size; i++) {
if ((payload->irq_status & camnoc_info->irq_err[i].sbm_port) &&
(camnoc_info->irq_err[i].enable)) {
@@ -398,6 +403,9 @@
~camnoc_info->irq_err[i].sbm_port;
}
}
+ atomic_dec(&cpas_core->irq_count);
+ wake_up(&cpas_core->irq_count_wq);
+ CAM_DBG(CAM_CPAS, "irq_count=%d\n", atomic_read(&cpas_core->irq_count));
if (payload->irq_status)
CAM_ERR(CAM_CPAS, "IRQ not handled irq_status=0x%x",
@@ -414,9 +422,14 @@
int camnoc_index = cpas_core->regbase_index[CAM_CPAS_REG_CAMNOC];
struct cam_cpas_work_payload *payload;
+ if (!atomic_inc_not_zero(&cpas_core->irq_count)) {
+ CAM_ERR(CAM_CPAS, "CPAS off");
+ return IRQ_HANDLED;
+ }
+
payload = kzalloc(sizeof(struct cam_cpas_work_payload), GFP_ATOMIC);
if (!payload)
- return IRQ_HANDLED;
+ goto done;
payload->irq_status = cam_io_r_mb(
soc_info->reg_map[camnoc_index].mem_base +
@@ -433,6 +446,9 @@
cam_cpastop_reset_irq(cpas_hw);
queue_work(cpas_core->work_queue, &payload->work);
+done:
+ atomic_dec(&cpas_core->irq_count);
+ wake_up(&cpas_core->irq_count_wq);
return IRQ_HANDLED;
}
diff --git a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_intf.h b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_intf.h
index b9b59a1..178e734 100644
--- a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_intf.h
+++ b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_intf.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -110,7 +110,7 @@
/**
* cam_hfi_deinit() - cleanup HFI
*/
-void cam_hfi_deinit(void);
+void cam_hfi_deinit(void __iomem *icp_base);
/**
* hfi_set_debug_level() - set debug level
* @lvl: FW debug message level
diff --git a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_session_defs.h b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_session_defs.h
index 0412b8a..7b2cb8b 100644
--- a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_session_defs.h
+++ b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_session_defs.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -35,6 +35,30 @@
#define HFI_IPEBPS_HANDLE_TYPE_IPE_NON_RT 0x3
/**
+ * struct abort_data
+ * @num_req_ids: Number of req ids
+ * @num_req_id: point to specific req id
+ *
+ * create abort data
+ */
+struct abort_data {
+ uint32_t num_req_ids;
+ uint32_t num_req_id[1];
+};
+
+/**
+ * struct hfi_cmd_data
+ * @abort: abort data
+ * @user data: user supplied argument
+ *
+ * create session abort data
+ */
+struct hfi_cmd_abort {
+ struct abort_data abort;
+ uint64_t user_data;
+} __packed;
+
+/**
* struct hfi_cmd_abort_destroy
* @user_data: user supplied data
*
diff --git a/drivers/media/platform/msm/camera/cam_icp/hfi.c b/drivers/media/platform/msm/camera/cam_icp/hfi.c
index 77f33d0..f95f8eb 100644
--- a/drivers/media/platform/msm/camera/cam_icp/hfi.c
+++ b/drivers/media/platform/msm/camera/cam_icp/hfi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -124,7 +124,7 @@
* firmware to process
*/
wmb();
- cam_io_w((uint32_t)INTR_ENABLE,
+ cam_io_w_mb((uint32_t)INTR_ENABLE,
g_hfi->csr_base + HFI_REG_A5_CSR_HOST2ICPINT);
err:
mutex_unlock(&hfi_cmd_q_mutex);
@@ -222,6 +222,10 @@
q->qhdr_read_idx = new_read_idx;
*words_read = size_in_words;
+ /* Memory Barrier to make sure message
+ * queue parameters are updated after read
+ */
+ wmb();
err:
mutex_unlock(&hfi_msg_q_mutex);
return rc;
@@ -445,17 +449,17 @@
val = cam_io_r(icp_base + HFI_REG_A5_CSR_A5_CONTROL);
val &= ~(ICP_FLAG_CSR_A5_EN | ICP_FLAG_CSR_WAKE_UP_EN);
- cam_io_w(val, icp_base + HFI_REG_A5_CSR_A5_CONTROL);
+ cam_io_w_mb(val, icp_base + HFI_REG_A5_CSR_A5_CONTROL);
val = cam_io_r(icp_base + HFI_REG_A5_CSR_NSEC_RESET);
- cam_io_w(val, icp_base + HFI_REG_A5_CSR_NSEC_RESET);
+ cam_io_w_mb(val, icp_base + HFI_REG_A5_CSR_NSEC_RESET);
}
void cam_hfi_enable_cpu(void __iomem *icp_base)
{
- cam_io_w((uint32_t)ICP_FLAG_CSR_A5_EN,
+ cam_io_w_mb((uint32_t)ICP_FLAG_CSR_A5_EN,
icp_base + HFI_REG_A5_CSR_A5_CONTROL);
- cam_io_w((uint32_t)0x10, icp_base + HFI_REG_A5_CSR_NSEC_RESET);
+ cam_io_w_mb((uint32_t)0x10, icp_base + HFI_REG_A5_CSR_NSEC_RESET);
}
int cam_hfi_resume(struct hfi_mem_info *hfi_mem,
@@ -464,23 +468,11 @@
int rc = 0;
uint32_t data;
uint32_t fw_version, status = 0;
+ uint32_t retry_cnt = 0;
cam_hfi_enable_cpu(icp_base);
g_hfi->csr_base = icp_base;
- rc = readw_poll_timeout((icp_base + HFI_REG_ICP_HOST_INIT_RESPONSE),
- status, status != ICP_INIT_RESP_SUCCESS, 15, 200);
-
- if (rc) {
- CAM_ERR(CAM_HFI, "timed out , status = %u", status);
- return -EINVAL;
- }
-
- fw_version = cam_io_r(icp_base + HFI_REG_FW_VERSION);
- CAM_DBG(CAM_HFI, "fw version : [%x]", fw_version);
-
- cam_io_w((uint32_t)INTR_ENABLE, icp_base + HFI_REG_A5_CSR_A2HOSTINTEN);
-
if (debug) {
cam_io_w_mb(ICP_FLAG_A5_CTRL_DBG_EN,
(icp_base + HFI_REG_A5_CSR_A5_CONTROL));
@@ -499,20 +491,54 @@
icp_base + HFI_REG_A5_CSR_A5_CONTROL);
}
+ while (retry_cnt < HFI_MAX_POLL_TRY) {
+ readw_poll_timeout((icp_base + HFI_REG_ICP_HOST_INIT_RESPONSE),
+ status, (status == ICP_INIT_RESP_SUCCESS), 100, 10000);
+
+ CAM_DBG(CAM_HFI, "1: status = %u", status);
+ status = cam_io_r_mb(icp_base + HFI_REG_ICP_HOST_INIT_RESPONSE);
+ CAM_DBG(CAM_HFI, "2: status = %u", status);
+ if (status == ICP_INIT_RESP_SUCCESS)
+ break;
+
+ if (status == ICP_INIT_RESP_FAILED) {
+ CAM_ERR(CAM_HFI, "ICP Init Failed. status = %u",
+ status);
+ fw_version = cam_io_r(icp_base + HFI_REG_FW_VERSION);
+ CAM_ERR(CAM_HFI, "fw version : [%x]", fw_version);
+ return -EINVAL;
+ }
+ retry_cnt++;
+ }
+
+ if ((retry_cnt == HFI_MAX_POLL_TRY) &&
+ (status == ICP_INIT_RESP_RESET)) {
+ CAM_ERR(CAM_HFI, "Reached Max retries. status = %u",
+ status);
+ fw_version = cam_io_r(icp_base + HFI_REG_FW_VERSION);
+ CAM_ERR(CAM_HFI, "fw version : [%x]", fw_version);
+ return -EINVAL;
+ }
+
+ cam_io_w_mb((uint32_t)INTR_ENABLE,
+ icp_base + HFI_REG_A5_CSR_A2HOSTINTEN);
+
+ fw_version = cam_io_r(icp_base + HFI_REG_FW_VERSION);
+ CAM_DBG(CAM_HFI, "fw version : [%x]", fw_version);
+
data = cam_io_r(icp_base + HFI_REG_A5_CSR_A5_STATUS);
CAM_DBG(CAM_HFI, "wfi status = %x", (int)data);
- cam_io_w((uint32_t)hfi_mem->qtbl.iova, icp_base + HFI_REG_QTBL_PTR);
- cam_io_w((uint32_t)hfi_mem->shmem.iova,
+ cam_io_w_mb((uint32_t)hfi_mem->qtbl.iova, icp_base + HFI_REG_QTBL_PTR);
+ cam_io_w_mb((uint32_t)hfi_mem->shmem.iova,
icp_base + HFI_REG_SHARED_MEM_PTR);
- cam_io_w((uint32_t)hfi_mem->shmem.len,
+ cam_io_w_mb((uint32_t)hfi_mem->shmem.len,
icp_base + HFI_REG_SHARED_MEM_SIZE);
- cam_io_w((uint32_t)hfi_mem->sec_heap.iova,
+ cam_io_w_mb((uint32_t)hfi_mem->sec_heap.iova,
icp_base + HFI_REG_UNCACHED_HEAP_PTR);
- cam_io_w((uint32_t)hfi_mem->sec_heap.len,
+ cam_io_w_mb((uint32_t)hfi_mem->sec_heap.len,
icp_base + HFI_REG_UNCACHED_HEAP_SIZE);
- cam_io_w((uint32_t)INTR_ENABLE, icp_base + HFI_REG_A5_CSR_A2HOSTINTEN);
return rc;
}
@@ -524,6 +550,7 @@
struct hfi_qtbl_hdr *qtbl_hdr;
struct hfi_q_hdr *cmd_q_hdr, *msg_q_hdr, *dbg_q_hdr;
uint32_t hw_version, soc_version, fw_version, status = 0;
+ uint32_t retry_cnt = 0;
mutex_lock(&hfi_cmd_q_mutex);
mutex_lock(&hfi_msg_q_mutex);
@@ -560,7 +587,7 @@
* disabling the clock gating on both V1 and V2 until the
* hardware team root causes this
*/
- cam_io_w((uint32_t)ICP_FLAG_CSR_A5_EN |
+ cam_io_w_mb((uint32_t)ICP_FLAG_CSR_A5_EN |
ICP_FLAG_CSR_WAKE_UP_EN |
ICP_CSR_EN_CLKGATE_WFI,
icp_base + HFI_REG_A5_CSR_A5_CONTROL);
@@ -677,24 +704,48 @@
break;
}
- cam_io_w((uint32_t)hfi_mem->qtbl.iova, icp_base + HFI_REG_QTBL_PTR);
- cam_io_w((uint32_t)hfi_mem->shmem.iova,
+ cam_io_w_mb((uint32_t)hfi_mem->qtbl.iova, icp_base + HFI_REG_QTBL_PTR);
+ cam_io_w_mb((uint32_t)hfi_mem->shmem.iova,
icp_base + HFI_REG_SHARED_MEM_PTR);
- cam_io_w((uint32_t)hfi_mem->shmem.len,
+ cam_io_w_mb((uint32_t)hfi_mem->shmem.len,
icp_base + HFI_REG_SHARED_MEM_SIZE);
- cam_io_w((uint32_t)hfi_mem->sec_heap.iova,
+ cam_io_w_mb((uint32_t)hfi_mem->sec_heap.iova,
icp_base + HFI_REG_UNCACHED_HEAP_PTR);
- cam_io_w((uint32_t)hfi_mem->sec_heap.len,
+ cam_io_w_mb((uint32_t)hfi_mem->sec_heap.len,
icp_base + HFI_REG_UNCACHED_HEAP_SIZE);
- cam_io_w((uint32_t)ICP_INIT_REQUEST_SET,
+ cam_io_w_mb((uint32_t)ICP_INIT_REQUEST_SET,
icp_base + HFI_REG_HOST_ICP_INIT_REQUEST);
hw_version = cam_io_r(icp_base + HFI_REG_A5_HW_VERSION);
- rc = readw_poll_timeout((icp_base + HFI_REG_ICP_HOST_INIT_RESPONSE),
- status, status != ICP_INIT_RESP_SUCCESS, 15, 200);
- if (rc) {
- CAM_ERR(CAM_HFI, "timed out , status = %u", status);
+ while (retry_cnt < HFI_MAX_POLL_TRY) {
+ readw_poll_timeout((icp_base + HFI_REG_ICP_HOST_INIT_RESPONSE),
+ status, (status == ICP_INIT_RESP_SUCCESS), 100, 10000);
+
+ CAM_DBG(CAM_HFI, "1: status = %u rc = %d", status, rc);
+ status = cam_io_r_mb(icp_base + HFI_REG_ICP_HOST_INIT_RESPONSE);
+ CAM_DBG(CAM_HFI, "2: status = %u rc = %d", status, rc);
+ if (status == ICP_INIT_RESP_SUCCESS)
+ break;
+
+ if (status == ICP_INIT_RESP_FAILED) {
+ CAM_ERR(CAM_HFI, "ICP Init Failed. status = %u",
+ status);
+ fw_version = cam_io_r(icp_base + HFI_REG_FW_VERSION);
+ CAM_ERR(CAM_HFI, "fw version : [%x]", fw_version);
+ goto regions_fail;
+ }
+ retry_cnt++;
+ }
+
+ if ((retry_cnt == HFI_MAX_POLL_TRY) &&
+ (status == ICP_INIT_RESP_RESET)) {
+ CAM_ERR(CAM_HFI, "Reached Max retries. status = %u",
+ status);
+ fw_version = cam_io_r(icp_base + HFI_REG_FW_VERSION);
+ CAM_ERR(CAM_HFI,
+ "hw version : : [%x], fw version : [%x]",
+ hw_version, fw_version);
goto regions_fail;
}
@@ -706,7 +757,8 @@
g_hfi->hfi_state = HFI_READY;
g_hfi->cmd_q_state = true;
g_hfi->msg_q_state = true;
- cam_io_w((uint32_t)INTR_ENABLE, icp_base + HFI_REG_A5_CSR_A2HOSTINTEN);
+ cam_io_w_mb((uint32_t)INTR_ENABLE,
+ icp_base + HFI_REG_A5_CSR_A2HOSTINTEN);
mutex_unlock(&hfi_cmd_q_mutex);
mutex_unlock(&hfi_msg_q_mutex);
@@ -714,14 +766,14 @@
return rc;
regions_fail:
kfree(g_hfi);
+ g_hfi = NULL;
alloc_fail:
mutex_unlock(&hfi_cmd_q_mutex);
mutex_unlock(&hfi_msg_q_mutex);
return rc;
}
-
-void cam_hfi_deinit(void)
+void cam_hfi_deinit(void __iomem *icp_base)
{
mutex_lock(&hfi_cmd_q_mutex);
mutex_lock(&hfi_msg_q_mutex);
@@ -734,7 +786,10 @@
g_hfi->cmd_q_state = false;
g_hfi->msg_q_state = false;
- cam_io_w((uint32_t)INTR_DISABLE,
+ cam_io_w_mb((uint32_t)ICP_INIT_REQUEST_RESET,
+ icp_base + HFI_REG_HOST_ICP_INIT_REQUEST);
+
+ cam_io_w_mb((uint32_t)INTR_DISABLE,
g_hfi->csr_base + HFI_REG_A5_CSR_A2HOSTINTEN);
kzfree(g_hfi);
g_hfi = NULL;
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_soc.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_soc.c
index b7b636c..d2314c4 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_soc.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_soc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -138,9 +138,22 @@
int cam_bps_update_clk_rate(struct cam_hw_soc_info *soc_info,
uint32_t clk_rate)
{
+ int32_t src_clk_idx;
+
if (!soc_info)
return -EINVAL;
+ src_clk_idx = soc_info->src_clk_idx;
+
+ if ((soc_info->clk_level_valid[CAM_TURBO_VOTE] == true) &&
+ (soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx] != 0) &&
+ (clk_rate > soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx])) {
+ CAM_DBG(CAM_ICP, "clk_rate %d greater than max, reset to %d",
+ clk_rate,
+ soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx]);
+ clk_rate = soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx];
+ }
+
return cam_soc_util_set_clk_rate(soc_info->clk[soc_info->src_clk_idx],
soc_info->clk_name[soc_info->src_clk_idx], clk_rate);
}
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
index 0012b34..5dfb1bc 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,6 +13,7 @@
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/io.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
@@ -116,7 +117,12 @@
if (ctx_data->clk_info.clk_rate[i] >= base_clk)
return i;
- return 0;
+ /*
+ * Caller has to ensure returned index is within array
+ * size bounds while accessing that index.
+ */
+
+ return i;
}
static bool cam_icp_is_over_clk(struct cam_icp_hw_mgr *hw_mgr,
@@ -132,7 +138,7 @@
curr_clk_idx = cam_icp_get_actual_clk_rate_idx(ctx_data,
hw_mgr_clk_info->curr_clk);
- CAM_DBG(CAM_ICP, "bc_idx = %d cc_idx = %d %lld %lld",
+ CAM_DBG(CAM_ICP, "bc_idx = %d cc_idx = %d %d %d",
base_clk_idx, curr_clk_idx, hw_mgr_clk_info->base_clk,
hw_mgr_clk_info->curr_clk);
@@ -192,9 +198,9 @@
struct cam_hw_info *dev = NULL;
if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS)
- dev_intf = hw_mgr->devices[CAM_ICP_DEV_BPS][0];
+ dev_intf = hw_mgr->bps_dev_intf;
else
- dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][0];
+ dev_intf = hw_mgr->ipe0_dev_intf;
if (!dev_intf) {
CAM_ERR(CAM_ICP, "dev_intf is invalid");
@@ -254,9 +260,9 @@
struct cam_hw_intf *dev_intf = NULL;
struct cam_a5_clk_update_cmd clk_upd_cmd;
- ipe0_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][0];
- ipe1_dev_intf = hw_mgr->devices[CAM_ICP_DEV_IPE][1];
- bps_dev_intf = hw_mgr->devices[CAM_ICP_DEV_BPS][0];
+ ipe0_dev_intf = hw_mgr->ipe0_dev_intf;
+ ipe1_dev_intf = hw_mgr->ipe1_dev_intf;
+ bps_dev_intf = hw_mgr->bps_dev_intf;
clk_info->base_clk = 0;
clk_info->curr_clk = 0;
@@ -305,7 +311,120 @@
return 0;
}
-static void cam_icp_timer_cb(unsigned long data)
+static int32_t cam_icp_ctx_timer(void *priv, void *data)
+{
+ struct clk_work_data *task_data = (struct clk_work_data *)data;
+ struct cam_icp_hw_ctx_data *ctx_data =
+ (struct cam_icp_hw_ctx_data *)task_data->data;
+ struct cam_icp_hw_mgr *hw_mgr = &icp_hw_mgr;
+ uint32_t id;
+ struct cam_hw_intf *ipe0_dev_intf = NULL;
+ struct cam_hw_intf *ipe1_dev_intf = NULL;
+ struct cam_hw_intf *bps_dev_intf = NULL;
+ struct cam_hw_intf *dev_intf = NULL;
+ struct cam_icp_clk_info *clk_info;
+ struct cam_icp_cpas_vote clk_update;
+
+ if (!ctx_data) {
+ CAM_ERR(CAM_ICP, "ctx_data is NULL, failed to update clk");
+ return -EINVAL;
+ }
+
+ mutex_lock(&ctx_data->ctx_mutex);
+ if ((ctx_data->state != CAM_ICP_CTX_STATE_ACQUIRED) ||
+ (ctx_data->watch_dog_reset_counter == 0)) {
+ CAM_DBG(CAM_ICP, "state %d, counter=%d",
+ ctx_data->state, ctx_data->watch_dog_reset_counter);
+ mutex_unlock(&ctx_data->ctx_mutex);
+ return 0;
+ }
+
+ CAM_DBG(CAM_ICP,
+ "E :ctx_id = %d ubw = %lld cbw = %lld curr_fc = %u bc = %u",
+ ctx_data->ctx_id,
+ ctx_data->clk_info.uncompressed_bw,
+ ctx_data->clk_info.compressed_bw,
+ ctx_data->clk_info.curr_fc, ctx_data->clk_info.base_clk);
+
+ ipe0_dev_intf = hw_mgr->ipe0_dev_intf;
+ ipe1_dev_intf = hw_mgr->ipe1_dev_intf;
+ bps_dev_intf = hw_mgr->bps_dev_intf;
+
+ if ((!ipe0_dev_intf) || (!bps_dev_intf)) {
+ CAM_ERR(CAM_ICP, "dev intfs are wrong, failed to update clk");
+ mutex_unlock(&ctx_data->ctx_mutex);
+ return -EINVAL;
+ }
+
+ if (!ctx_data->icp_dev_acquire_info) {
+ CAM_WARN(CAM_ICP, "NULL acquire info");
+ mutex_unlock(&ctx_data->ctx_mutex);
+ return -EINVAL;
+ }
+
+ if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS) {
+ dev_intf = bps_dev_intf;
+ clk_info = &hw_mgr->clk_info[ICP_CLK_HW_BPS];
+ id = CAM_ICP_BPS_CMD_VOTE_CPAS;
+ } else {
+ dev_intf = ipe0_dev_intf;
+ clk_info = &hw_mgr->clk_info[ICP_CLK_HW_IPE];
+ id = CAM_ICP_IPE_CMD_VOTE_CPAS;
+ }
+
+ clk_info->compressed_bw -= ctx_data->clk_info.compressed_bw;
+ clk_info->uncompressed_bw -= ctx_data->clk_info.uncompressed_bw;
+ ctx_data->clk_info.uncompressed_bw = 0;
+ ctx_data->clk_info.compressed_bw = 0;
+ ctx_data->clk_info.curr_fc = 0;
+ ctx_data->clk_info.base_clk = 0;
+
+ clk_update.ahb_vote.type = CAM_VOTE_DYNAMIC;
+ clk_update.ahb_vote.vote.freq = clk_info->curr_clk;
+ clk_update.ahb_vote_valid = true;
+ clk_update.axi_vote.compressed_bw = clk_info->compressed_bw;
+ clk_update.axi_vote.uncompressed_bw = clk_info->uncompressed_bw;
+ clk_update.axi_vote_valid = true;
+ dev_intf->hw_ops.process_cmd(dev_intf->hw_priv, id,
+ &clk_update, sizeof(clk_update));
+
+ CAM_DBG(CAM_ICP,
+ "X :ctx_id = %d ubw = %lld cbw = %lld curr_fc = %u bc = %u",
+ ctx_data->ctx_id,
+ ctx_data->clk_info.uncompressed_bw,
+ ctx_data->clk_info.compressed_bw,
+ ctx_data->clk_info.curr_fc, ctx_data->clk_info.base_clk);
+
+ mutex_unlock(&ctx_data->ctx_mutex);
+
+ return 0;
+}
+
+static void cam_icp_ctx_timer_cb(unsigned long data)
+{
+ unsigned long flags;
+ struct crm_workq_task *task;
+ struct clk_work_data *task_data;
+ struct cam_req_mgr_timer *timer = (struct cam_req_mgr_timer *)data;
+
+ spin_lock_irqsave(&icp_hw_mgr.hw_mgr_lock, flags);
+ task = cam_req_mgr_workq_get_task(icp_hw_mgr.msg_work);
+ if (!task) {
+ CAM_ERR(CAM_ICP, "no empty task");
+ spin_unlock_irqrestore(&icp_hw_mgr.hw_mgr_lock, flags);
+ return;
+ }
+
+ task_data = (struct clk_work_data *)task->payload;
+ task_data->data = timer->parent;
+ task_data->type = ICP_WORKQ_TASK_MSG_TYPE;
+ task->process_cb = cam_icp_ctx_timer;
+ cam_req_mgr_workq_enqueue_task(task, &icp_hw_mgr,
+ CRM_TASK_PRIORITY_0);
+ spin_unlock_irqrestore(&icp_hw_mgr.hw_mgr_lock, flags);
+}
+
+static void cam_icp_device_timer_cb(unsigned long data)
{
unsigned long flags;
struct crm_workq_task *task;
@@ -342,13 +461,29 @@
hw_mgr->clk_info[i].uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
hw_mgr->clk_info[i].compressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
hw_mgr->clk_info[i].hw_type = i;
+ hw_mgr->clk_info[i].watch_dog_reset_counter = 0;
}
hw_mgr->icp_default_clk = ICP_CLK_SVS_HZ;
return 0;
}
-static int cam_icp_timer_start(struct cam_icp_hw_mgr *hw_mgr)
+static int cam_icp_ctx_timer_start(struct cam_icp_hw_ctx_data *ctx_data)
+{
+ int rc = 0;
+
+ rc = crm_timer_init(&ctx_data->watch_dog,
+ 2000, ctx_data, &cam_icp_ctx_timer_cb);
+ if (rc)
+ CAM_ERR(CAM_ICP, "Failed to start timer");
+
+ ctx_data->watch_dog_reset_counter = 0;
+
+ CAM_DBG(CAM_ICP, "stop timer : ctx_id = %d", ctx_data->ctx_id);
+ return rc;
+}
+
+static int cam_icp_device_timer_start(struct cam_icp_hw_mgr *hw_mgr)
{
int rc = 0;
int i;
@@ -356,21 +491,70 @@
for (i = 0; i < ICP_CLK_HW_MAX; i++) {
if (!hw_mgr->clk_info[i].watch_dog) {
rc = crm_timer_init(&hw_mgr->clk_info[i].watch_dog,
- 3000, &hw_mgr->clk_info[i], &cam_icp_timer_cb);
+ 3000, &hw_mgr->clk_info[i],
+ &cam_icp_device_timer_cb);
+
if (rc)
CAM_ERR(CAM_ICP, "Failed to start timer %d", i);
+
+ hw_mgr->clk_info[i].watch_dog_reset_counter = 0;
}
}
return rc;
}
-static void cam_icp_timer_stop(struct cam_icp_hw_mgr *hw_mgr)
+static int cam_icp_ctx_timer_stop(struct cam_icp_hw_ctx_data *ctx_data)
{
- if (!hw_mgr->bps_ctxt_cnt)
+ if (ctx_data->watch_dog) {
+ CAM_DBG(CAM_ICP, "stop timer : ctx_id = %d", ctx_data->ctx_id);
+ ctx_data->watch_dog_reset_counter = 0;
+ crm_timer_exit(&ctx_data->watch_dog);
+ ctx_data->watch_dog = NULL;
+ }
+
+ return 0;
+}
+
+static void cam_icp_device_timer_stop(struct cam_icp_hw_mgr *hw_mgr)
+{
+ if (!hw_mgr->bps_ctxt_cnt &&
+ hw_mgr->clk_info[ICP_CLK_HW_BPS].watch_dog) {
+ hw_mgr->clk_info[ICP_CLK_HW_BPS].watch_dog_reset_counter = 0;
crm_timer_exit(&hw_mgr->clk_info[ICP_CLK_HW_BPS].watch_dog);
- else if (!hw_mgr->ipe_ctxt_cnt)
+ hw_mgr->clk_info[ICP_CLK_HW_BPS].watch_dog = NULL;
+ }
+
+ if (!hw_mgr->ipe_ctxt_cnt &&
+ hw_mgr->clk_info[ICP_CLK_HW_IPE].watch_dog) {
+ hw_mgr->clk_info[ICP_CLK_HW_IPE].watch_dog_reset_counter = 0;
crm_timer_exit(&hw_mgr->clk_info[ICP_CLK_HW_IPE].watch_dog);
+ hw_mgr->clk_info[ICP_CLK_HW_IPE].watch_dog = NULL;
+ }
+}
+
+static int cam_icp_ctx_timer_reset(struct cam_icp_hw_ctx_data *ctx_data)
+{
+ if (ctx_data && ctx_data->watch_dog) {
+ ctx_data->watch_dog_reset_counter++;
+ CAM_DBG(CAM_ICP, "reset timer : ctx_id = %d, counter=%d",
+ ctx_data->ctx_id, ctx_data->watch_dog_reset_counter);
+ crm_timer_reset(ctx_data->watch_dog);
+ }
+
+ return 0;
+}
+
+static void cam_icp_device_timer_reset(struct cam_icp_hw_mgr *hw_mgr,
+ int device_index)
+{
+ if ((device_index >= ICP_CLK_HW_MAX) || (!hw_mgr))
+ return;
+
+ if (hw_mgr->clk_info[device_index].watch_dog) {
+ crm_timer_reset(hw_mgr->clk_info[device_index].watch_dog);
+ hw_mgr->clk_info[device_index].watch_dog_reset_counter++;
+ }
}
static uint32_t cam_icp_mgr_calc_base_clk(uint32_t frame_cycles,
@@ -420,7 +604,9 @@
for (i = 0; i < CAM_ICP_CTX_MAX; i++) {
ctx_data = &hw_mgr->ctx_data[i];
if (ctx_data->state == CAM_ICP_CTX_STATE_ACQUIRED &&
- ctx_data->icp_dev_acquire_info->dev_type == dev_type)
+ ICP_DEV_TYPE_TO_CLK_TYPE(
+ ctx_data->icp_dev_acquire_info->dev_type) ==
+ ICP_DEV_TYPE_TO_CLK_TYPE(dev_type))
hw_mgr_clk_info->base_clk +=
ctx_data->clk_info.base_clk;
}
@@ -575,7 +761,8 @@
hw_mgr_clk_info->over_clked = 0;
rc = false;
} else if (hw_mgr_clk_info->curr_clk < hw_mgr_clk_info->base_clk) {
- hw_mgr_clk_info->curr_clk = hw_mgr_clk_info->base_clk;
+ hw_mgr_clk_info->curr_clk = cam_icp_get_actual_clk_rate(hw_mgr,
+ ctx_data, hw_mgr_clk_info->base_clk);
rc = true;
}
mutex_unlock(&hw_mgr->hw_mgr_mutex);
@@ -633,7 +820,13 @@
* recalculate bandwidth of all contexts of same hardware and update
* voting of bandwidth
*/
- if (clk_info->uncompressed_bw == ctx_data->clk_info.uncompressed_bw)
+ CAM_DBG(CAM_ICP, "ubw ctx = %lld clk_info ubw = %lld busy = %d",
+ ctx_data->clk_info.uncompressed_bw,
+ clk_info->uncompressed_bw, busy);
+
+ if ((clk_info->uncompressed_bw == ctx_data->clk_info.uncompressed_bw) &&
+ (ctx_data->clk_info.uncompressed_bw ==
+ hw_mgr_clk_info->uncompressed_bw))
return false;
if (busy &&
@@ -647,13 +840,18 @@
for (i = 0; i < CAM_ICP_CTX_MAX; i++) {
ctx = &hw_mgr->ctx_data[i];
if (ctx->state == CAM_ICP_CTX_STATE_ACQUIRED &&
- ctx->icp_dev_acquire_info->dev_type ==
- ctx_data->icp_dev_acquire_info->dev_type) {
+ ICP_DEV_TYPE_TO_CLK_TYPE(
+ ctx->icp_dev_acquire_info->dev_type) ==
+ ICP_DEV_TYPE_TO_CLK_TYPE(
+ ctx_data->icp_dev_acquire_info->dev_type)) {
mutex_lock(&hw_mgr->hw_mgr_mutex);
hw_mgr_clk_info->uncompressed_bw +=
ctx->clk_info.uncompressed_bw;
hw_mgr_clk_info->compressed_bw +=
ctx->clk_info.compressed_bw;
+ CAM_DBG(CAM_ICP, "ubw = %lld, cbw = %lld",
+ hw_mgr_clk_info->uncompressed_bw,
+ hw_mgr_clk_info->compressed_bw);
mutex_unlock(&hw_mgr->hw_mgr_mutex);
}
}
@@ -671,12 +869,13 @@
uint64_t req_id;
struct cam_icp_clk_info *hw_mgr_clk_info;
+ cam_icp_ctx_timer_reset(ctx_data);
if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS) {
- crm_timer_reset(hw_mgr->clk_info[ICP_CLK_HW_BPS].watch_dog);
+ cam_icp_device_timer_reset(hw_mgr, ICP_CLK_HW_BPS);
hw_mgr_clk_info = &hw_mgr->clk_info[ICP_CLK_HW_BPS];
CAM_DBG(CAM_ICP, "Reset bps timer");
} else {
- crm_timer_reset(hw_mgr->clk_info[ICP_CLK_HW_IPE].watch_dog);
+ cam_icp_device_timer_reset(hw_mgr, ICP_CLK_HW_IPE);
hw_mgr_clk_info = &hw_mgr->clk_info[ICP_CLK_HW_IPE];
CAM_DBG(CAM_ICP, "Reset ipe timer");
}
@@ -827,11 +1026,13 @@
dev_intf->hw_ops.process_cmd(dev_intf->hw_priv, id,
&clk_update, sizeof(clk_update));
- if (ctx_data->icp_dev_acquire_info->dev_type != CAM_ICP_RES_TYPE_BPS)
- if (ipe1_dev_intf)
- ipe1_dev_intf->hw_ops.process_cmd(
- ipe1_dev_intf->hw_priv, id,
- &clk_update, sizeof(clk_update));
+ /*
+ * Consolidated bw needs to be voted on only one IPE client. Otherwise
+ * total bw that we vote at bus client would be doubled. So either
+ * remove voting on IPE1 or divide the vote for each IPE client
+ * and vote to cpas - cpas will add up and vote full bw to sf client
+ * anyway.
+ */
return 0;
}
@@ -870,7 +1071,11 @@
if (ctx_data->icp_dev_acquire_info->dev_type == CAM_ICP_RES_TYPE_BPS) {
if (hw_mgr->bps_ctxt_cnt++)
goto end;
- bps_dev_intf->hw_ops.init(bps_dev_intf->hw_priv, NULL, 0);
+ if (!hw_mgr->bps_clk_state) {
+ bps_dev_intf->hw_ops.init(
+ bps_dev_intf->hw_priv, NULL, 0);
+ hw_mgr->bps_clk_state = true;
+ }
if (icp_hw_mgr.ipe_bps_pc_flag) {
bps_dev_intf->hw_ops.process_cmd(
bps_dev_intf->hw_priv,
@@ -880,15 +1085,18 @@
} else {
if (hw_mgr->ipe_ctxt_cnt++)
goto end;
-
- ipe0_dev_intf->hw_ops.init(ipe0_dev_intf->hw_priv, NULL, 0);
+ if (!hw_mgr->ipe_clk_state)
+ ipe0_dev_intf->hw_ops.init(
+ ipe0_dev_intf->hw_priv, NULL, 0);
if (icp_hw_mgr.ipe_bps_pc_flag) {
ipe0_dev_intf->hw_ops.process_cmd(
ipe0_dev_intf->hw_priv,
CAM_ICP_IPE_CMD_POWER_RESUME, NULL, 0);
}
- if ((icp_hw_mgr.ipe1_enable) && (ipe1_dev_intf)) {
+ if ((icp_hw_mgr.ipe1_enable) &&
+ (ipe1_dev_intf) &&
+ (!hw_mgr->ipe_clk_state)) {
ipe1_dev_intf->hw_ops.init(ipe1_dev_intf->hw_priv,
NULL, 0);
@@ -899,6 +1107,7 @@
NULL, 0);
}
}
+ hw_mgr->ipe_clk_state = true;
if (icp_hw_mgr.ipe_bps_pc_flag) {
hw_mgr->core_info = hw_mgr->core_info |
(ICP_PWR_CLP_IPE0 | ICP_PWR_CLP_IPE1);
@@ -908,6 +1117,8 @@
CAM_DBG(CAM_ICP, "core_info %X", hw_mgr->core_info);
if (icp_hw_mgr.ipe_bps_pc_flag)
rc = hfi_enable_ipe_bps_pc(true, hw_mgr->core_info);
+ else if (icp_hw_mgr.icp_pc_flag)
+ rc = hfi_enable_ipe_bps_pc(false, hw_mgr->core_info);
else
rc = hfi_enable_ipe_bps_pc(false, hw_mgr->core_info);
end:
@@ -953,7 +1164,11 @@
hw_mgr->core_info & (~ICP_PWR_CLP_BPS);
}
- bps_dev_intf->hw_ops.deinit(bps_dev_intf->hw_priv, NULL, 0);
+ if (hw_mgr->bps_clk_state) {
+ bps_dev_intf->hw_ops.deinit
+ (bps_dev_intf->hw_priv, NULL, 0);
+ hw_mgr->bps_clk_state = false;
+ }
} else {
CAM_DBG(CAM_ICP, "ipe ctx cnt %d", hw_mgr->ipe_ctxt_cnt);
if (ctx_data)
@@ -968,7 +1183,10 @@
CAM_ICP_IPE_CMD_POWER_COLLAPSE, NULL, 0);
}
- ipe0_dev_intf->hw_ops.deinit(ipe0_dev_intf->hw_priv, NULL, 0);
+
+ if (hw_mgr->ipe_clk_state)
+ ipe0_dev_intf->hw_ops.deinit(
+ ipe0_dev_intf->hw_priv, NULL, 0);
if (ipe1_dev_intf) {
if (icp_hw_mgr.ipe_bps_pc_flag) {
@@ -978,9 +1196,12 @@
NULL, 0);
}
+ if (hw_mgr->ipe_clk_state)
ipe1_dev_intf->hw_ops.deinit(ipe1_dev_intf->hw_priv,
NULL, 0);
}
+
+ hw_mgr->ipe_clk_state = false;
if (icp_hw_mgr.ipe_bps_pc_flag) {
hw_mgr->core_info = hw_mgr->core_info &
(~(ICP_PWR_CLP_IPE0 | ICP_PWR_CLP_IPE1));
@@ -1131,7 +1352,7 @@
ctx_data->hfi_frame_process.in_resource[i]);
cam_sync_destroy(
ctx_data->hfi_frame_process.in_resource[i]);
- ctx_data->hfi_frame_process.in_resource[i] = 0;
+ ctx_data->hfi_frame_process.in_free_resource[i] = 0;
}
hfi_frame_process->fw_process_flag[i] = false;
clear_bit(i, ctx_data->hfi_frame_process.bitmap);
@@ -1141,7 +1362,7 @@
if (!hfi_frame_process->in_free_resource[i])
continue;
- CAM_INFO(CAM_ICP, "Delete merged sync in object: %d",
+ CAM_DBG(CAM_ICP, "Delete merged sync in object: %d",
ctx_data->hfi_frame_process.in_free_resource[i]);
cam_sync_destroy(
ctx_data->hfi_frame_process.in_free_resource[i]);
@@ -1174,9 +1395,10 @@
clk_type = ICP_DEV_TYPE_TO_CLK_TYPE(ctx_data->icp_dev_acquire_info->
dev_type);
- crm_timer_reset(icp_hw_mgr.clk_info[clk_type].watch_dog);
+ cam_icp_device_timer_reset(&icp_hw_mgr, clk_type);
mutex_lock(&ctx_data->ctx_mutex);
+ cam_icp_ctx_timer_reset(ctx_data);
if (ctx_data->state != CAM_ICP_CTX_STATE_ACQUIRED) {
CAM_DBG(CAM_ICP, "ctx %u is in %d state",
ctx_data->ctx_id, ctx_data->state);
@@ -1301,6 +1523,7 @@
{
struct hfi_msg_create_handle_ack *create_handle_ack = NULL;
struct cam_icp_hw_ctx_data *ctx_data = NULL;
+ int rc = 0;
create_handle_ack = (struct hfi_msg_create_handle_ack *)msg_ptr;
if (!create_handle_ack) {
@@ -1317,11 +1540,15 @@
if (ctx_data->state == CAM_ICP_CTX_STATE_IN_USE) {
ctx_data->fw_handle = create_handle_ack->fw_handle;
CAM_DBG(CAM_ICP, "fw_handle = %x", ctx_data->fw_handle);
- complete(&ctx_data->wait_complete);
- } else
- CAM_WARN(CAM_ICP, "Timeout failed to create fw handle");
-
- return 0;
+ } else {
+ CAM_WARN(CAM_ICP,
+ "This ctx is no longer in use current state: %d",
+ ctx_data->state);
+ ctx_data->fw_handle = 0;
+ rc = -EPERM;
+ }
+ complete(&ctx_data->wait_complete);
+ return rc;
}
static int cam_icp_mgr_process_msg_ping_ack(uint32_t *msg_ptr)
@@ -1385,21 +1612,38 @@
{
struct cam_icp_hw_ctx_data *ctx_data = NULL;
struct hfi_msg_ipebps_async_ack *ioconfig_ack = NULL;
+ struct cam_hw_intf *a5_dev_intf = NULL;
+ struct cam_hw_info *a5_dev = NULL;
int rc = 0;
+ a5_dev_intf = icp_hw_mgr.a5_dev_intf;
+ if (!a5_dev_intf) {
+ CAM_ERR(CAM_ICP, "a5_dev_intf is invalid");
+ return -EINVAL;
+ }
+ a5_dev = (struct cam_hw_info *)a5_dev_intf->hw_priv;
switch (msg_ptr[ICP_PACKET_OPCODE]) {
- case HFI_IPEBPS_CMD_OPCODE_IPE_DESTROY:
- case HFI_IPEBPS_CMD_OPCODE_BPS_DESTROY:
case HFI_IPEBPS_CMD_OPCODE_IPE_ABORT:
case HFI_IPEBPS_CMD_OPCODE_BPS_ABORT:
- CAM_DBG(CAM_ICP, "received IPE/BPS_DESTROY/ABORT:");
+ ioconfig_ack = (struct hfi_msg_ipebps_async_ack *)msg_ptr;
+ ctx_data =
+ (struct cam_icp_hw_ctx_data *)ioconfig_ack->user_data1;
+ if (ctx_data->state != CAM_ICP_CTX_STATE_FREE)
+ complete(&ctx_data->wait_complete);
+ CAM_DBG(CAM_ICP, "received IPE/BPS/ ABORT: ctx_state =%d",
+ ctx_data->state);
+ break;
+ case HFI_IPEBPS_CMD_OPCODE_IPE_DESTROY:
+ case HFI_IPEBPS_CMD_OPCODE_BPS_DESTROY:
ioconfig_ack = (struct hfi_msg_ipebps_async_ack *)msg_ptr;
ctx_data =
(struct cam_icp_hw_ctx_data *)ioconfig_ack->user_data1;
if ((ctx_data->state == CAM_ICP_CTX_STATE_RELEASE) ||
- (ctx_data->state == CAM_ICP_CTX_STATE_IN_USE))
+ (ctx_data->state == CAM_ICP_CTX_STATE_IN_USE)) {
complete(&ctx_data->wait_complete);
-
+ }
+ CAM_DBG(CAM_ICP, "received IPE/BPS/ DESTROY: ctx_state =%d",
+ ctx_data->state);
break;
default:
CAM_ERR(CAM_ICP, "Invalid opcode : %u",
@@ -1407,7 +1651,6 @@
rc = -EINVAL;
break;
}
-
return rc;
}
@@ -1808,12 +2051,20 @@
return 0;
}
- if (ipe1_dev_intf) {
+ if (ipe1_dev_intf && hw_mgr->ipe_clk_state) {
ipe1_dev_intf->hw_ops.deinit(ipe1_dev_intf->hw_priv,
NULL, 0);
}
- ipe0_dev_intf->hw_ops.deinit(ipe0_dev_intf->hw_priv, NULL, 0);
- bps_dev_intf->hw_ops.deinit(bps_dev_intf->hw_priv, NULL, 0);
+
+ if (hw_mgr->ipe_clk_state)
+ ipe0_dev_intf->hw_ops.deinit(ipe0_dev_intf->hw_priv, NULL, 0);
+ if (hw_mgr->bps_clk_state)
+ bps_dev_intf->hw_ops.deinit(bps_dev_intf->hw_priv, NULL, 0);
+
+
+ hw_mgr->bps_clk_state = false;
+ hw_mgr->ipe_clk_state = false;
+
return 0;
}
static int cam_icp_mgr_icp_power_collapse(struct cam_icp_hw_mgr *hw_mgr)
@@ -1831,12 +2082,15 @@
}
a5_dev = (struct cam_hw_info *)a5_dev_intf->hw_priv;
- if (!hw_mgr->icp_pc_flag)
+ if (!hw_mgr->icp_pc_flag) {
+ cam_hfi_disable_cpu(
+ a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base);
rc = cam_icp_mgr_hw_close(hw_mgr, NULL);
- else
+ } else {
rc = cam_icp_mgr_send_pc_prep(hw_mgr);
-
- cam_hfi_disable_cpu(a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base);
+ cam_hfi_disable_cpu(
+ a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base);
+ }
a5_dev_intf->hw_ops.deinit(a5_dev_intf->hw_priv, NULL, 0);
CAM_DBG(CAM_ICP, "EXIT");
@@ -1897,7 +2151,7 @@
int rc = 0;
unsigned long rem_jiffies;
size_t packet_size;
- int timeout = 5000;
+ int timeout = 100;
struct hfi_cmd_work_data *task_data;
struct hfi_cmd_ipebps_async *abort_cmd;
struct crm_workq_task *task;
@@ -1908,9 +2162,10 @@
packet_size =
sizeof(struct hfi_cmd_ipebps_async) +
- sizeof(struct hfi_cmd_abort_destroy) -
+ sizeof(struct hfi_cmd_abort) -
sizeof(((struct hfi_cmd_ipebps_async *)0)->payload.direct);
abort_cmd = kzalloc(packet_size, GFP_KERNEL);
+ CAM_DBG(CAM_ICP, "abort pkt size = %d", (int) packet_size);
if (!abort_cmd) {
rc = -ENOMEM;
return rc;
@@ -1928,8 +2183,6 @@
abort_cmd->fw_handles[0] = ctx_data->fw_handle;
abort_cmd->user_data1 = (uint64_t)ctx_data;
abort_cmd->user_data2 = (uint64_t)0x0;
- memcpy(abort_cmd->payload.direct, &ctx_data->temp_payload,
- sizeof(uint64_t));
task_data = (struct hfi_cmd_work_data *)task->payload;
task_data->data = (void *)abort_cmd;
@@ -1960,7 +2213,7 @@
struct cam_icp_hw_ctx_data *ctx_data)
{
int rc = 0;
- int timeout = 5000;
+ int timeout = 100;
unsigned long rem_jiffies;
size_t packet_size;
struct hfi_cmd_work_data *task_data;
@@ -2047,6 +2300,7 @@
&hw_mgr->ctx_data[ctx_id], 0);
hw_mgr->ctx_data[ctx_id].state = CAM_ICP_CTX_STATE_RELEASE;
CAM_DBG(CAM_ICP, "E: ctx_id = %d", ctx_id);
+ cam_icp_mgr_abort_handle(&hw_mgr->ctx_data[ctx_id]);
cam_icp_mgr_destroy_handle(&hw_mgr->ctx_data[ctx_id]);
cam_icp_mgr_cleanup_ctx(&hw_mgr->ctx_data[ctx_id]);
@@ -2063,6 +2317,7 @@
kfree(hw_mgr->ctx_data[ctx_id].icp_dev_acquire_info);
hw_mgr->ctx_data[ctx_id].icp_dev_acquire_info = NULL;
hw_mgr->ctx_data[ctx_id].state = CAM_ICP_CTX_STATE_FREE;
+ cam_icp_ctx_timer_stop(&hw_mgr->ctx_data[ctx_id]);
mutex_unlock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
CAM_DBG(CAM_ICP, "X: ctx_id = %d", ctx_id);
@@ -2091,12 +2346,15 @@
ipe0_dev_intf->hw_ops.deinit(ipe0_dev_intf->hw_priv, NULL, 0);
bps_dev_intf->hw_ops.deinit(bps_dev_intf->hw_priv, NULL, 0);
a5_dev_intf->hw_ops.deinit(a5_dev_intf->hw_priv, NULL, 0);
+ hw_mgr->bps_clk_state = false;
+ hw_mgr->ipe_clk_state = false;
}
static int cam_icp_mgr_hw_close(void *hw_priv, void *hw_close_args)
{
struct cam_icp_hw_mgr *hw_mgr = hw_priv;
struct cam_hw_intf *a5_dev_intf = NULL;
+ struct cam_hw_info *a5_dev = NULL;
struct cam_icp_a5_set_irq_cb irq_cb;
struct cam_icp_a5_set_fw_buf_info fw_buf_info;
int rc = 0;
@@ -2108,14 +2366,13 @@
mutex_unlock(&hw_mgr->hw_mgr_mutex);
return 0;
}
-
a5_dev_intf = hw_mgr->a5_dev_intf;
if (!a5_dev_intf) {
CAM_DBG(CAM_ICP, "a5_dev_intf is NULL");
mutex_unlock(&hw_mgr->hw_mgr_mutex);
return -EINVAL;
}
-
+ a5_dev = (struct cam_hw_info *)a5_dev_intf->hw_priv;
fw_buf_info.kva = 0;
fw_buf_info.iova = 0;
fw_buf_info.len = 0;
@@ -2126,9 +2383,8 @@
sizeof(fw_buf_info));
if (rc)
CAM_ERR(CAM_ICP, "nullify the fw buf failed");
-
- cam_hfi_deinit();
-
+ cam_hfi_deinit(
+ a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base);
irq_cb.icp_hw_mgr_cb = NULL;
irq_cb.data = NULL;
rc = a5_dev_intf->hw_ops.process_cmd(
@@ -2142,7 +2398,6 @@
hw_mgr->fw_download = false;
hw_mgr->secure_mode = CAM_SECURE_MODE_NON_SECURE;
mutex_unlock(&hw_mgr->hw_mgr_mutex);
-
CAM_DBG(CAM_ICP, "Exit");
return rc;
}
@@ -2184,11 +2439,16 @@
goto ipe1_dev_init_failed;
}
+ hw_mgr->bps_clk_state = true;
+ hw_mgr->ipe_clk_state = true;
+
return rc;
ipe1_dev_init_failed:
ipe0_dev_intf->hw_ops.deinit(ipe0_dev_intf->hw_priv, NULL, 0);
+ hw_mgr->ipe_clk_state = false;
ipe0_dev_init_failed:
bps_dev_intf->hw_ops.deinit(bps_dev_intf->hw_priv, NULL, 0);
+ hw_mgr->bps_clk_state = false;
bps_dev_init_failed:
a5_dev_intf->hw_ops.deinit(a5_dev_intf->hw_priv, NULL, 0);
a5_dev_init_failed:
@@ -2337,10 +2597,10 @@
if (hw_mgr->fw_download == false) {
CAM_DBG(CAM_ICP, "Downloading FW");
mutex_unlock(&hw_mgr->hw_mgr_mutex);
- cam_icp_mgr_hw_open(hw_mgr, &downloadFromResume);
+ rc = cam_icp_mgr_hw_open(hw_mgr, &downloadFromResume);
mutex_lock(&hw_mgr->hw_mgr_mutex);
CAM_DBG(CAM_ICP, "FW Download Done Exit");
- return 0;
+ return rc;
}
rc = a5_dev_intf->hw_ops.init(a5_dev_intf->hw_priv, NULL, 0);
@@ -2386,34 +2646,24 @@
}
a5_dev = (struct cam_hw_info *)a5_dev_intf->hw_priv;
rc = cam_icp_allocate_hfi_mem();
- if (rc) {
- mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ if (rc)
goto alloc_hfi_mem_failed;
- }
rc = cam_icp_mgr_device_init(hw_mgr);
- if (rc) {
- mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ if (rc)
goto dev_init_fail;
- }
rc = cam_icp_mgr_fw_download(hw_mgr);
- if (rc) {
- mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ if (rc)
goto fw_download_failed;
- }
rc = cam_icp_mgr_hfi_init(hw_mgr);
- if (rc) {
- mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ if (rc)
goto hfi_init_failed;
- }
rc = cam_icp_mgr_send_fw_init(hw_mgr);
- if (rc) {
- mutex_unlock(&hw_mgr->hw_mgr_mutex);
+ if (rc)
goto fw_init_failed;
- }
hw_mgr->ctxt_cnt = 0;
hw_mgr->fw_download = true;
@@ -2428,22 +2678,32 @@
if (download_fw_args)
icp_pc = *((bool *)download_fw_args);
+ if (download_fw_args && icp_pc == true && hw_mgr->icp_pc_flag) {
+ rc = cam_ipe_bps_deint(hw_mgr);
+ CAM_DBG(CAM_ICP, "deinit all clocks");
+ }
+
if (download_fw_args && icp_pc == true)
return rc;
+ rc = cam_ipe_bps_deint(hw_mgr);
rc = cam_icp_mgr_icp_power_collapse(hw_mgr);
+ CAM_DBG(CAM_ICP, "deinit all clocks at boot up");
return rc;
fw_init_failed:
- cam_hfi_deinit();
+ cam_hfi_deinit(
+ a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base);
hfi_init_failed:
- cam_hfi_disable_cpu(a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base);
+ cam_hfi_disable_cpu(
+ a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base);
fw_download_failed:
cam_icp_mgr_device_deinit(hw_mgr);
dev_init_fail:
cam_icp_free_hfi_mem();
alloc_hfi_mem_failed:
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
return rc;
}
@@ -3173,11 +3433,12 @@
mutex_lock(&hw_mgr->hw_mgr_mutex);
cam_icp_hw_mgr_reset_clk_info(hw_mgr);
hw_mgr->secure_mode = CAM_SECURE_MODE_NON_SECURE;
+ rc = cam_ipe_bps_deint(hw_mgr);
}
mutex_unlock(&hw_mgr->hw_mgr_mutex);
if (!hw_mgr->bps_ctxt_cnt || !hw_mgr->ipe_ctxt_cnt)
- cam_icp_timer_stop(hw_mgr);
+ cam_icp_device_timer_stop(hw_mgr);
CAM_DBG(CAM_ICP, "Exit");
return rc;
@@ -3266,6 +3527,11 @@
CAM_ERR(CAM_ICP, "FW response timed out %d", rc);
}
+ if (ctx_data->fw_handle == 0) {
+ CAM_ERR(CAM_ICP, "Invalid handle created");
+ rc = -EINVAL;
+ }
+
return rc;
}
@@ -3429,12 +3695,13 @@
}
ctx_data = &hw_mgr->ctx_data[ctx_id];
ctx_data->ctx_id = ctx_id;
- mutex_unlock(&hw_mgr->hw_mgr_mutex);
mutex_lock(&ctx_data->ctx_mutex);
rc = cam_icp_get_acquire_info(hw_mgr, args, ctx_data);
- if (rc)
+ if (rc) {
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
goto acquire_info_failed;
+ }
icp_dev_acquire_info = ctx_data->icp_dev_acquire_info;
rc = cam_mem_get_io_buf(
@@ -3443,6 +3710,7 @@
&io_buf_addr, &io_buf_size);
if (rc) {
CAM_ERR(CAM_ICP, "unable to get src buf info from io desc");
+ mutex_unlock(&hw_mgr->hw_mgr_mutex);
goto get_io_buf_failed;
}
@@ -3450,7 +3718,6 @@
icp_dev_acquire_info->io_config_cmd_handle,
(void *)io_buf_addr, io_buf_size);
- mutex_lock(&hw_mgr->hw_mgr_mutex);
if (!hw_mgr->ctxt_cnt) {
rc = cam_icp_clk_info_init(hw_mgr, ctx_data);
if (rc) {
@@ -3464,6 +3731,9 @@
goto get_io_buf_failed;
}
+ if (icp_hw_mgr.a5_debug_q)
+ hfi_set_debug_level(icp_hw_mgr.a5_dbg_lvl);
+
rc = cam_icp_send_ubwc_cfg(hw_mgr);
if (rc) {
mutex_unlock(&hw_mgr->hw_mgr_mutex);
@@ -3472,7 +3742,9 @@
}
if (!hw_mgr->bps_ctxt_cnt || !hw_mgr->ipe_ctxt_cnt)
- cam_icp_timer_start(hw_mgr);
+ cam_icp_device_timer_start(hw_mgr);
+
+ cam_icp_ctx_timer_start(ctx_data);
rc = cam_icp_mgr_ipe_bps_resume(hw_mgr, ctx_data);
if (rc) {
@@ -3539,6 +3811,7 @@
send_ping_failed:
cam_icp_mgr_ipe_bps_power_collapse(hw_mgr, ctx_data, 0);
ipe_bps_resume_failed:
+ cam_icp_ctx_timer_stop(&hw_mgr->ctx_data[ctx_id]);
ubwc_cfg_failed:
if (!hw_mgr->ctxt_cnt)
cam_icp_mgr_icp_power_collapse(hw_mgr);
@@ -3843,7 +4116,6 @@
goto icp_wq_create_failed;
init_completion(&icp_hw_mgr.a5_complete);
-
return rc;
icp_wq_create_failed:
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
index 85f5b550..aac4a5e 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -183,6 +183,8 @@
* @temp_payload: Payload for destroy handle data
* @ctx_id: Context Id
* @clk_info: Current clock info of a context
+ * @watch_dog: watchdog timer handle
+ * @watch_dog_reset_counter: Counter for watch dog reset
*/
struct cam_icp_hw_ctx_data {
void *context_priv;
@@ -200,6 +202,8 @@
struct ipe_bps_destroy temp_payload;
uint32_t ctx_id;
struct cam_ctx_clk_info clk_info;
+ struct cam_req_mgr_timer *watch_dog;
+ uint32_t watch_dog_reset_counter;
};
/**
@@ -222,6 +226,7 @@
* @compressed_bw: Current compressed bandwidth voting
* @hw_type: IPE/BPS device type
* @watch_dog: watchdog timer handle
+ * @watch_dog_reset_counter: Counter for watch dog reset
*/
struct cam_icp_clk_info {
uint32_t base_clk;
@@ -232,6 +237,7 @@
uint64_t compressed_bw;
uint32_t hw_type;
struct cam_req_mgr_timer *watch_dog;
+ uint32_t watch_dog_reset_counter;
};
/**
@@ -271,6 +277,12 @@
* @ipe1_enable: Flag for IPE1
* @bps_enable: Flag for BPS
* @core_info: 32 bit value , tells IPE0/1 and BPS
+ * @a5_dev_intf : Device interface for A5
+ * @ipe0_dev_intf: Device interface for IPE0
+ * @ipe1_dev_intf: Device interface for IPE1
+ * @bps_dev_intf: Device interface for BPS
+ * @ipe_clk_state: IPE clock state flag
+ * @bps_clk_state: BPS clock state flag
*/
struct cam_icp_hw_mgr {
struct mutex hw_mgr_mutex;
@@ -313,6 +325,8 @@
struct cam_hw_intf *ipe0_dev_intf;
struct cam_hw_intf *ipe1_dev_intf;
struct cam_hw_intf *bps_dev_intf;
+ bool ipe_clk_state;
+ bool bps_clk_state;
};
static int cam_icp_mgr_hw_close(void *hw_priv, void *hw_close_args);
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h b/drivers/media/platform/msm/camera/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h
index d2e04ef..771c4ed 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -21,7 +21,7 @@
#define ICP_CLK_TURBO_HZ 600000000
#define ICP_CLK_SVS_HZ 400000000
-#define CAM_ICP_A5_BW_BYTES_VOTE 100000000
+#define CAM_ICP_A5_BW_BYTES_VOTE 40000000
#define CAM_ICP_CTX_MAX 36
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_soc.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_soc.c
index 289d7d4..d24305a 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_soc.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_soc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -141,9 +141,22 @@
int cam_ipe_update_clk_rate(struct cam_hw_soc_info *soc_info,
uint32_t clk_rate)
{
+ int32_t src_clk_idx;
+
if (!soc_info)
return -EINVAL;
+ src_clk_idx = soc_info->src_clk_idx;
+
+ if ((soc_info->clk_level_valid[CAM_TURBO_VOTE] == true) &&
+ (soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx] != 0) &&
+ (clk_rate > soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx])) {
+ CAM_DBG(CAM_ICP, "clk_rate %d greater than max, reset to %d",
+ clk_rate,
+ soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx]);
+ clk_rate = soc_info->clk_rate[CAM_TURBO_VOTE][src_clk_idx];
+ }
+
return cam_soc_util_set_clk_rate(soc_info->clk[soc_info->src_clk_idx],
soc_info->clk_name[soc_info->src_clk_idx], clk_rate);
}
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
index d1153ba..d62344d 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -86,7 +86,8 @@
struct cam_ctx_request, list);
req_isp_old = (struct cam_isp_ctx_req *) req_old->req_priv;
req_isp_new = (struct cam_isp_ctx_req *) req->req_priv;
- if (req_isp_old->packet_opcode_type == CAM_ISP_PACKET_INIT_DEV) {
+ if (req_isp_old->hw_update_data.packet_opcode_type ==
+ CAM_ISP_PACKET_INIT_DEV) {
if ((req_isp_old->num_cfg + req_isp_new->num_cfg) >=
CAM_ISP_CTX_CFG_MAX) {
CAM_WARN(CAM_ISP, "Can not merge INIT pkt");
@@ -896,6 +897,29 @@
return rc;
}
+static int __cam_isp_ctx_sof_in_flush(
+ struct cam_isp_context *ctx_isp, void *evt_data)
+{
+ int rc = 0;
+ struct cam_isp_hw_sof_event_data *sof_event_data = evt_data;
+
+ if (!evt_data) {
+ CAM_ERR(CAM_ISP, "in valid sof event data");
+ return -EINVAL;
+ }
+ ctx_isp->frame_id++;
+ ctx_isp->sof_timestamp_val = sof_event_data->timestamp;
+ CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx",
+ ctx_isp->frame_id, ctx_isp->sof_timestamp_val);
+
+ if (--ctx_isp->frame_skip_count == 0)
+ ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF;
+ else
+ CAM_ERR(CAM_ISP, "Skip currect SOF");
+
+ return rc;
+}
+
static struct cam_isp_ctx_irq_ops
cam_isp_ctx_activated_state_machine_irq[CAM_ISP_CTX_ACTIVATED_MAX] = {
/* SOF */
@@ -967,6 +991,17 @@
/* HALT */
{
},
+ /* FLUSH */
+ {
+ .irq_ops = {
+ NULL,
+ __cam_isp_ctx_sof_in_flush,
+ NULL,
+ NULL,
+ NULL,
+ __cam_isp_ctx_buf_done_in_applied,
+ },
+ },
};
static int __cam_isp_ctx_apply_req_in_activated_state(
@@ -1037,6 +1072,7 @@
cfg.ctxt_to_hw_map = ctx_isp->hw_ctx;
cfg.hw_update_entries = req_isp->cfg;
cfg.num_hw_update_entries = req_isp->num_cfg;
+ cfg.priv = &req_isp->hw_update_data;
rc = ctx->hw_mgr_intf->hw_config(ctx->hw_mgr_intf->hw_mgr_priv, &cfg);
if (rc) {
@@ -1170,6 +1206,24 @@
return rc;
}
+static int __cam_isp_ctx_flush_req_in_activated(
+ struct cam_context *ctx,
+ struct cam_req_mgr_flush_request *flush_req)
+{
+ int rc = 0;
+ struct cam_isp_context *ctx_isp;
+
+ ctx_isp = (struct cam_isp_context *) ctx->ctx_priv;
+ spin_lock_bh(&ctx->lock);
+ ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_FLUSH;
+ ctx_isp->frame_skip_count = 2;
+ spin_unlock_bh(&ctx->lock);
+
+ CAM_DBG(CAM_ISP, "Flush request in state %d", ctx->state);
+ rc = __cam_isp_ctx_flush_req(ctx, &ctx->pending_req_list, flush_req);
+ return rc;
+}
+
static int __cam_isp_ctx_flush_req_in_ready(
struct cam_context *ctx,
struct cam_req_mgr_flush_request *flush_req)
@@ -1230,12 +1284,24 @@
.crm_ops = {},
.irq_ops = NULL,
},
+ /* HW ERROR */
+ {
+ .ioctl_ops = {},
+ .crm_ops = {},
+ .irq_ops = NULL,
+ },
/* HALT */
{
.ioctl_ops = {},
.crm_ops = {},
.irq_ops = NULL,
},
+ /* FLUSH */
+ {
+ .ioctl_ops = {},
+ .crm_ops = {},
+ .irq_ops = NULL,
+ },
};
static int __cam_isp_ctx_rdi_only_sof_in_top_state(
@@ -1601,10 +1667,23 @@
__cam_isp_ctx_buf_done_in_bubble_applied,
},
},
-
+ /* HW ERROR */
+ {
+ },
/* HALT */
{
},
+ /* FLUSH */
+ {
+ .irq_ops = {
+ NULL,
+ __cam_isp_ctx_sof_in_flush,
+ NULL,
+ NULL,
+ NULL,
+ __cam_isp_ctx_buf_done_in_applied,
+ },
+ },
};
static int __cam_isp_ctx_rdi_only_apply_req_top_state(
@@ -1660,15 +1739,26 @@
.crm_ops = {},
.irq_ops = NULL,
},
+ /* HW ERROR */
+ {
+ .ioctl_ops = {},
+ .crm_ops = {},
+ .irq_ops = NULL,
+ },
/* HALT */
{
.ioctl_ops = {},
.crm_ops = {},
.irq_ops = NULL,
},
+ /* FLUSHED */
+ {
+ .ioctl_ops = {},
+ .crm_ops = {},
+ .irq_ops = NULL,
+ },
};
-
/* top level state machine */
static int __cam_isp_ctx_release_dev_in_top_state(struct cam_context *ctx,
struct cam_release_dev_cmd *cmd)
@@ -1729,7 +1819,6 @@
struct cam_req_mgr_add_request add_req;
struct cam_isp_context *ctx_isp =
(struct cam_isp_context *) ctx->ctx_priv;
- struct cam_isp_prepare_hw_update_data hw_update_data;
CAM_DBG(CAM_ISP, "get free request object......");
@@ -1780,7 +1869,7 @@
cfg.max_in_map_entries = CAM_ISP_CTX_RES_MAX;
cfg.out_map_entries = req_isp->fence_map_out;
cfg.in_map_entries = req_isp->fence_map_in;
- cfg.priv = &hw_update_data;
+ cfg.priv = &req_isp->hw_update_data;
CAM_DBG(CAM_ISP, "try to prepare config packet......");
@@ -1795,7 +1884,6 @@
req_isp->num_fence_map_out = cfg.num_out_map_entries;
req_isp->num_fence_map_in = cfg.num_in_map_entries;
req_isp->num_acked = 0;
- req_isp->packet_opcode_type = hw_update_data.packet_opcode_type;
CAM_DBG(CAM_ISP, "num_entry: %d, num fence out: %d, num fence in: %d",
req_isp->num_cfg, req_isp->num_fence_map_out,
@@ -1805,9 +1893,11 @@
req->status = 1;
CAM_DBG(CAM_ISP, "Packet request id 0x%llx packet opcode:%d",
- packet->header.request_id, req_isp->packet_opcode_type);
+ packet->header.request_id,
+ req_isp->hw_update_data.packet_opcode_type);
- if (req_isp->packet_opcode_type == CAM_ISP_PACKET_INIT_DEV) {
+ if (req_isp->hw_update_data.packet_opcode_type ==
+ CAM_ISP_PACKET_INIT_DEV) {
if (ctx->state < CAM_CTX_ACTIVATED) {
rc = __cam_isp_ctx_enqueue_init_request(ctx, req);
if (rc)
@@ -2052,7 +2142,7 @@
struct cam_start_stop_dev_cmd *cmd)
{
int rc = 0;
- struct cam_hw_start_args arg;
+ struct cam_hw_config_args arg;
struct cam_ctx_request *req;
struct cam_isp_ctx_req *req_isp;
struct cam_isp_context *ctx_isp =
@@ -2080,9 +2170,11 @@
rc = -EFAULT;
goto end;
}
+
arg.ctxt_to_hw_map = ctx_isp->hw_ctx;
arg.hw_update_entries = req_isp->cfg;
arg.num_hw_update_entries = req_isp->num_cfg;
+ arg.priv = &req_isp->hw_update_data;
ctx_isp->frame_id = 0;
ctx_isp->active_req_cnt = 0;
@@ -2212,6 +2304,58 @@
return rc;
}
+static int __cam_isp_ctx_link_pause(struct cam_context *ctx)
+{
+ int rc = 0;
+ struct cam_isp_hw_cmd_args hw_cmd_args;
+ struct cam_isp_context *ctx_isp =
+ (struct cam_isp_context *) ctx->ctx_priv;
+
+ hw_cmd_args.ctxt_to_hw_map = ctx_isp->hw_ctx;
+ hw_cmd_args.cmd_type = CAM_ISP_HW_MGR_CMD_PAUSE_HW;
+ rc = ctx->hw_mgr_intf->hw_cmd(ctx->hw_mgr_intf->hw_mgr_priv,
+ &hw_cmd_args);
+
+ return rc;
+}
+
+static int __cam_isp_ctx_link_resume(struct cam_context *ctx)
+{
+ int rc = 0;
+ struct cam_isp_hw_cmd_args hw_cmd_args;
+ struct cam_isp_context *ctx_isp =
+ (struct cam_isp_context *) ctx->ctx_priv;
+
+ hw_cmd_args.ctxt_to_hw_map = ctx_isp->hw_ctx;
+ hw_cmd_args.cmd_type = CAM_ISP_HW_MGR_CMD_RESUME_HW;
+ rc = ctx->hw_mgr_intf->hw_cmd(ctx->hw_mgr_intf->hw_mgr_priv,
+ &hw_cmd_args);
+
+ return rc;
+}
+
+static int __cam_isp_ctx_process_evt(struct cam_context *ctx,
+ struct cam_req_mgr_link_evt_data *link_evt_data)
+{
+ int rc = 0;
+
+ switch (link_evt_data->evt_type) {
+ case CAM_REQ_MGR_LINK_EVT_ERR:
+ /* No need to handle this message now */
+ break;
+ case CAM_REQ_MGR_LINK_EVT_PAUSE:
+ __cam_isp_ctx_link_pause(ctx);
+ break;
+ case CAM_REQ_MGR_LINK_EVT_RESUME:
+ __cam_isp_ctx_link_resume(ctx);
+ break;
+ default:
+ CAM_WARN(CAM_ISP, "Unknown event from CRM");
+ break;
+ }
+ return rc;
+}
+
static int __cam_isp_ctx_unlink_in_activated(struct cam_context *ctx,
struct cam_req_mgr_core_dev_link_setup *unlink)
{
@@ -2343,7 +2487,8 @@
.crm_ops = {
.unlink = __cam_isp_ctx_unlink_in_activated,
.apply_req = __cam_isp_ctx_apply_req,
- .flush_req = __cam_isp_ctx_flush_req_in_top_state,
+ .flush_req = __cam_isp_ctx_flush_req_in_activated,
+ .process_evt = __cam_isp_ctx_process_evt,
},
.irq_ops = __cam_isp_ctx_handle_irq_in_activated,
},
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
index 88ebc03..f1f3137d 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -52,6 +52,7 @@
CAM_ISP_CTX_ACTIVATED_BUBBLE_APPLIED,
CAM_ISP_CTX_ACTIVATED_HW_ERROR,
CAM_ISP_CTX_ACTIVATED_HALT,
+ CAM_ISP_CTX_ACTIVATED_FLUSH,
CAM_ISP_CTX_ACTIVATED_MAX,
};
@@ -81,22 +82,22 @@
* the request has been completed.
* @bubble_report: Flag to track if bubble report is active on
* current request
- * @packet_opcode_type: Request packet opcode type,
- * ie INIT packet or update packet
+ * @hw_update_data: HW update data for this request
*
*/
struct cam_isp_ctx_req {
- struct cam_ctx_request *base;
+ struct cam_ctx_request *base;
- struct cam_hw_update_entry cfg[CAM_ISP_CTX_CFG_MAX];
- uint32_t num_cfg;
- struct cam_hw_fence_map_entry fence_map_out[CAM_ISP_CTX_RES_MAX];
- uint32_t num_fence_map_out;
- struct cam_hw_fence_map_entry fence_map_in[CAM_ISP_CTX_RES_MAX];
- uint32_t num_fence_map_in;
- uint32_t num_acked;
- int32_t bubble_report;
- uint32_t packet_opcode_type;
+ struct cam_hw_update_entry cfg[CAM_ISP_CTX_CFG_MAX];
+ uint32_t num_cfg;
+ struct cam_hw_fence_map_entry fence_map_out
+ [CAM_ISP_CTX_RES_MAX];
+ uint32_t num_fence_map_out;
+ struct cam_hw_fence_map_entry fence_map_in[CAM_ISP_CTX_RES_MAX];
+ uint32_t num_fence_map_in;
+ uint32_t num_acked;
+ int32_t bubble_report;
+ struct cam_isp_prepare_hw_update_data hw_update_data;
};
/**
@@ -116,6 +117,7 @@
* @subscribe_event: The irq event mask that CRM subscribes to, IFE will
* invoke CRM cb at those event.
* @last_applied_req_id: Last applied request id
+ * @frame_skip_count: Number of frame to skip before change state
*
*/
struct cam_isp_context {
@@ -135,6 +137,7 @@
int64_t reported_req_id;
uint32_t subscribe_event;
int64_t last_applied_req_id;
+ uint32_t frame_skip_count;
};
/**
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
index 8d41ae7..a6f1cf5 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -860,10 +860,95 @@
return rc;
}
-static int cam_ife_hw_mgr_acquire_res_ife_csid_ipp(
+static int cam_ife_mgr_acquire_cid_res(
struct cam_ife_hw_mgr_ctx *ife_ctx,
struct cam_isp_in_port_info *in_port,
- uint32_t cid_res_id)
+ uint32_t *cid_res_id,
+ enum cam_ife_pix_path_res_id csid_path)
+{
+ int rc = -1;
+ int i, j;
+ struct cam_ife_hw_mgr *ife_hw_mgr;
+ struct cam_ife_hw_mgr_res *cid_res;
+ struct cam_hw_intf *hw_intf;
+ struct cam_csid_hw_reserve_resource_args csid_acquire;
+
+ ife_hw_mgr = ife_ctx->hw_mgr;
+
+ rc = cam_ife_hw_mgr_get_res(&ife_ctx->free_res_list, &cid_res);
+ if (rc) {
+ CAM_ERR(CAM_ISP, "No more free hw mgr resource");
+ goto err;
+ }
+ cam_ife_hw_mgr_put_res(&ife_ctx->res_list_ife_cid, &cid_res);
+
+ csid_acquire.res_type = CAM_ISP_RESOURCE_CID;
+ csid_acquire.in_port = in_port;
+ csid_acquire.res_id = csid_path;
+
+ for (i = 0; i < CAM_IFE_CSID_HW_NUM_MAX; i++) {
+ if (!ife_hw_mgr->csid_devices[i])
+ continue;
+
+ hw_intf = ife_hw_mgr->csid_devices[i];
+ rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv, &csid_acquire,
+ sizeof(csid_acquire));
+ if (rc)
+ continue;
+ else
+ break;
+ }
+
+ if (i == CAM_IFE_CSID_HW_NUM_MAX || !csid_acquire.node_res) {
+ CAM_ERR(CAM_ISP, "Can not acquire ife csid rdi resource");
+ goto err;
+ }
+
+ cid_res->res_type = CAM_IFE_HW_MGR_RES_CID;
+ cid_res->res_id = csid_acquire.node_res->res_id;
+ cid_res->is_dual_vfe = in_port->usage_type;
+ cid_res->hw_res[0] = csid_acquire.node_res;
+ cid_res->hw_res[1] = NULL;
+ /* CID(DT_ID) value of acquire device, require for path */
+ *cid_res_id = csid_acquire.node_res->res_id;
+
+ if (cid_res->is_dual_vfe) {
+ csid_acquire.node_res = NULL;
+ csid_acquire.res_type = CAM_ISP_RESOURCE_CID;
+ csid_acquire.in_port = in_port;
+ for (j = i + 1; j < CAM_IFE_CSID_HW_NUM_MAX; j++) {
+ if (!ife_hw_mgr->csid_devices[j])
+ continue;
+
+ hw_intf = ife_hw_mgr->csid_devices[j];
+ rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv,
+ &csid_acquire, sizeof(csid_acquire));
+ if (rc)
+ continue;
+ else
+ break;
+ }
+
+ if (j == CAM_IFE_CSID_HW_NUM_MAX) {
+ CAM_ERR(CAM_ISP,
+ "Can not acquire ife csid rdi resource");
+ goto err;
+ }
+ cid_res->hw_res[1] = csid_acquire.node_res;
+ }
+ cid_res->parent = &ife_ctx->res_list_ife_in;
+ ife_ctx->res_list_ife_in.child[
+ ife_ctx->res_list_ife_in.num_children++] = cid_res;
+
+ return 0;
+err:
+ return rc;
+
+}
+
+static int cam_ife_hw_mgr_acquire_res_ife_csid_ipp(
+ struct cam_ife_hw_mgr_ctx *ife_ctx,
+ struct cam_isp_in_port_info *in_port)
{
int rc = -1;
int i;
@@ -872,8 +957,17 @@
struct cam_ife_hw_mgr_res *csid_res;
struct cam_ife_hw_mgr_res *cid_res;
struct cam_hw_intf *hw_intf;
+ uint32_t cid_res_id;
struct cam_csid_hw_reserve_resource_args csid_acquire;
+ /* get cid resource */
+ rc = cam_ife_mgr_acquire_cid_res(ife_ctx, in_port, &cid_res_id,
+ CAM_IFE_PIX_PATH_RES_IPP);
+ if (rc) {
+ CAM_ERR(CAM_ISP, "Acquire IFE CID resource Failed");
+ goto err;
+ }
+
ife_hw_mgr = ife_ctx->hw_mgr;
rc = cam_ife_hw_mgr_get_res(&ife_ctx->free_res_list, &csid_res);
@@ -985,8 +1079,7 @@
static int cam_ife_hw_mgr_acquire_res_ife_csid_rdi(
struct cam_ife_hw_mgr_ctx *ife_ctx,
- struct cam_isp_in_port_info *in_port,
- uint32_t cid_res_id)
+ struct cam_isp_in_port_info *in_port)
{
int rc = -1;
int i, j;
@@ -996,6 +1089,7 @@
struct cam_ife_hw_mgr_res *cid_res;
struct cam_hw_intf *hw_intf;
struct cam_isp_out_port_info *out_port;
+ uint32_t cid_res_id;
struct cam_csid_hw_reserve_resource_args csid_acquire;
ife_hw_mgr = ife_ctx->hw_mgr;
@@ -1005,6 +1099,15 @@
if (!cam_ife_hw_mgr_is_rdi_res(out_port->res_type))
continue;
+ /* get cid resource */
+ rc = cam_ife_mgr_acquire_cid_res(ife_ctx, in_port, &cid_res_id,
+ cam_ife_hw_mgr_get_ife_csid_rdi_res_type(
+ out_port->res_type));
+ if (rc) {
+ CAM_ERR(CAM_ISP, "Acquire IFE CID resource Failed");
+ goto err;
+ }
+
rc = cam_ife_hw_mgr_get_res(&ife_ctx->free_res_list,
&csid_res);
if (rc) {
@@ -1135,91 +1238,6 @@
return 0;
}
-static int cam_ife_mgr_acquire_cid_res(
- struct cam_ife_hw_mgr_ctx *ife_ctx,
- struct cam_isp_in_port_info *in_port,
- uint32_t *cid_res_id,
- int pixel_count)
-{
- int rc = -1;
- int i, j;
- struct cam_ife_hw_mgr *ife_hw_mgr;
- struct cam_ife_hw_mgr_res *cid_res;
- struct cam_hw_intf *hw_intf;
- struct cam_csid_hw_reserve_resource_args csid_acquire;
-
- ife_hw_mgr = ife_ctx->hw_mgr;
-
- rc = cam_ife_hw_mgr_get_res(&ife_ctx->free_res_list, &cid_res);
- if (rc) {
- CAM_ERR(CAM_ISP, "No more free hw mgr resource");
- goto err;
- }
- cam_ife_hw_mgr_put_res(&ife_ctx->res_list_ife_cid, &cid_res);
-
- csid_acquire.res_type = CAM_ISP_RESOURCE_CID;
- csid_acquire.in_port = in_port;
- csid_acquire.pixel_count = pixel_count;
-
- for (i = 0; i < CAM_IFE_CSID_HW_NUM_MAX; i++) {
- if (!ife_hw_mgr->csid_devices[i])
- continue;
-
- hw_intf = ife_hw_mgr->csid_devices[i];
- rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv, &csid_acquire,
- sizeof(csid_acquire));
- if (rc)
- continue;
- else
- break;
- }
-
- if (i == CAM_IFE_CSID_HW_NUM_MAX || !csid_acquire.node_res) {
- CAM_ERR(CAM_ISP, "Can not acquire ife csid rdi resource");
- goto err;
- }
-
- cid_res->res_type = CAM_IFE_HW_MGR_RES_CID;
- cid_res->res_id = csid_acquire.node_res->res_id;
- cid_res->is_dual_vfe = in_port->usage_type;
- cid_res->hw_res[0] = csid_acquire.node_res;
- cid_res->hw_res[1] = NULL;
- /* CID(DT_ID) value of acquire device, require for path */
- *cid_res_id = csid_acquire.node_res->res_id;
-
- if (cid_res->is_dual_vfe) {
- csid_acquire.node_res = NULL;
- csid_acquire.res_type = CAM_ISP_RESOURCE_CID;
- csid_acquire.in_port = in_port;
- for (j = i + 1; j < CAM_IFE_CSID_HW_NUM_MAX; j++) {
- if (!ife_hw_mgr->csid_devices[j])
- continue;
-
- hw_intf = ife_hw_mgr->csid_devices[j];
- rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv,
- &csid_acquire, sizeof(csid_acquire));
- if (rc)
- continue;
- else
- break;
- }
-
- if (j == CAM_IFE_CSID_HW_NUM_MAX) {
- CAM_ERR(CAM_ISP,
- "Can not acquire ife csid rdi resource");
- goto err;
- }
- cid_res->hw_res[1] = csid_acquire.node_res;
- }
- cid_res->parent = &ife_ctx->res_list_ife_in;
- ife_ctx->res_list_ife_in.child[
- ife_ctx->res_list_ife_in.num_children++] = cid_res;
-
- return 0;
-err:
- return rc;
-
-}
static int cam_ife_mgr_acquire_hw_for_ctx(
struct cam_ife_hw_mgr_ctx *ife_ctx,
struct cam_isp_in_port_info *in_port,
@@ -1229,7 +1247,6 @@
int is_dual_vfe = 0;
int pixel_count = 0;
int rdi_count = 0;
- uint32_t cid_res_id = 0;
is_dual_vfe = in_port->usage_type;
@@ -1248,18 +1265,9 @@
return -EINVAL;
}
- /* get cid resource */
- rc = cam_ife_mgr_acquire_cid_res(ife_ctx, in_port, &cid_res_id,
- pixel_count);
- if (rc) {
- CAM_ERR(CAM_ISP, "Acquire IFE CID resource Failed");
- goto err;
- }
-
if (pixel_count) {
/* get ife csid IPP resrouce */
- rc = cam_ife_hw_mgr_acquire_res_ife_csid_ipp(ife_ctx, in_port,
- cid_res_id);
+ rc = cam_ife_hw_mgr_acquire_res_ife_csid_ipp(ife_ctx, in_port);
if (rc) {
CAM_ERR(CAM_ISP,
"Acquire IFE CSID IPP resource Failed");
@@ -1269,8 +1277,7 @@
if (rdi_count) {
/* get ife csid rdi resource */
- rc = cam_ife_hw_mgr_acquire_res_ife_csid_rdi(ife_ctx, in_port,
- cid_res_id);
+ rc = cam_ife_hw_mgr_acquire_res_ife_csid_rdi(ife_ctx, in_port);
if (rc) {
CAM_ERR(CAM_ISP,
"Acquire IFE CSID RDI resource Failed");
@@ -1437,15 +1444,97 @@
return rc;
}
+static int cam_isp_blob_bw_update(
+ struct cam_isp_bw_config *bw_config,
+ struct cam_ife_hw_mgr_ctx *ctx)
+{
+ struct cam_ife_hw_mgr_res *hw_mgr_res;
+ struct cam_hw_intf *hw_intf;
+ struct cam_vfe_bw_update_args bw_upd_args;
+ uint64_t cam_bw_bps = 0;
+ uint64_t ext_bw_bps = 0;
+ int rc = -EINVAL;
+ uint32_t i;
+
+ CAM_DBG(CAM_ISP,
+ "usage=%u left cam_bw_bps=%llu ext_bw_bps=%llu\n"
+ "right cam_bw_bps=%llu ext_bw_bps=%llu",
+ bw_config->usage_type,
+ bw_config->left_pix_vote.cam_bw_bps,
+ bw_config->left_pix_vote.ext_bw_bps,
+ bw_config->right_pix_vote.cam_bw_bps,
+ bw_config->right_pix_vote.ext_bw_bps);
+
+ list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) {
+ for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
+ if (!hw_mgr_res->hw_res[i])
+ continue;
+
+ if (hw_mgr_res->res_id == CAM_ISP_HW_VFE_IN_CAMIF)
+ if (i == CAM_ISP_HW_SPLIT_LEFT) {
+ cam_bw_bps =
+ bw_config->left_pix_vote.cam_bw_bps;
+ ext_bw_bps =
+ bw_config->left_pix_vote.ext_bw_bps;
+ } else {
+ cam_bw_bps =
+ bw_config->right_pix_vote.cam_bw_bps;
+ ext_bw_bps =
+ bw_config->right_pix_vote.ext_bw_bps;
+ }
+ else if ((hw_mgr_res->res_id >= CAM_ISP_HW_VFE_IN_RDI0)
+ && (hw_mgr_res->res_id <=
+ CAM_ISP_HW_VFE_IN_RDI3)) {
+ uint32_t idx = hw_mgr_res->res_id -
+ CAM_ISP_HW_VFE_IN_RDI0;
+ if (idx >= bw_config->num_rdi)
+ continue;
+
+ cam_bw_bps =
+ bw_config->rdi_vote[idx].cam_bw_bps;
+ ext_bw_bps =
+ bw_config->rdi_vote[idx].ext_bw_bps;
+ } else
+ if (hw_mgr_res->hw_res[i]) {
+ CAM_ERR(CAM_ISP, "Invalid res_id %u",
+ hw_mgr_res->res_id);
+ rc = -EINVAL;
+ return rc;
+ }
+
+ hw_intf = hw_mgr_res->hw_res[i]->hw_intf;
+ if (hw_intf && hw_intf->hw_ops.process_cmd) {
+ bw_upd_args.node_res =
+ hw_mgr_res->hw_res[i];
+
+ bw_upd_args.camnoc_bw_bytes = cam_bw_bps;
+ bw_upd_args.external_bw_bytes = ext_bw_bps;
+
+ rc = hw_intf->hw_ops.process_cmd(
+ hw_intf->hw_priv,
+ CAM_ISP_HW_CMD_BW_UPDATE,
+ &bw_upd_args,
+ sizeof(struct cam_vfe_bw_update_args));
+ if (rc)
+ CAM_ERR(CAM_ISP, "BW Update failed");
+ } else
+ CAM_WARN(CAM_ISP, "NULL hw_intf!");
+ }
+ }
+
+ return rc;
+}
+
/* entry function: config_hw */
static int cam_ife_mgr_config_hw(void *hw_mgr_priv,
void *config_hw_args)
{
int rc = -1, i;
- struct cam_hw_start_args *cfg;
+ struct cam_hw_config_args *cfg;
struct cam_hw_update_entry *cmd;
struct cam_cdm_bl_request *cdm_cmd;
struct cam_ife_hw_mgr_ctx *ctx;
+ struct cam_isp_prepare_hw_update_data *hw_update_data;
CAM_DBG(CAM_ISP, "Enter");
if (!hw_mgr_priv || !config_hw_args) {
@@ -1467,6 +1556,18 @@
if (atomic_read(&ctx->overflow_pending))
return -EINVAL;
+ hw_update_data = (struct cam_isp_prepare_hw_update_data *) cfg->priv;
+
+ for (i = 0; i < CAM_IFE_HW_NUM_MAX; i++) {
+ if (hw_update_data->bw_config_valid[i] == true) {
+ rc = cam_isp_blob_bw_update(
+ (struct cam_isp_bw_config *)
+ &hw_update_data->bw_config[i], ctx);
+ if (rc)
+ CAM_ERR(CAM_ISP, "Bandwidth Update Failed");
+ }
+ }
+
CAM_DBG(CAM_ISP, "Enter ctx id:%d num_hw_upd_entries %d",
ctx->ctx_index, cfg->num_hw_update_entries);
@@ -1798,7 +1899,7 @@
static int cam_ife_mgr_start_hw(void *hw_mgr_priv, void *start_hw_args)
{
int rc = -1;
- struct cam_hw_start_args *start_args = start_hw_args;
+ struct cam_hw_config_args *start_args = start_hw_args;
struct cam_ife_hw_mgr_ctx *ctx;
struct cam_ife_hw_mgr_res *hw_mgr_res;
uint32_t i;
@@ -2204,92 +2305,6 @@
return rc;
}
-static int cam_isp_blob_bw_update(
- uint32_t blob_type,
- struct cam_isp_generic_blob_info *blob_info,
- struct cam_isp_bw_config *bw_config,
- struct cam_hw_prepare_update_args *prepare)
-{
- struct cam_ife_hw_mgr_ctx *ctx = NULL;
- struct cam_ife_hw_mgr_res *hw_mgr_res;
- struct cam_hw_intf *hw_intf;
- struct cam_vfe_bw_update_args bw_upd_args;
- uint64_t cam_bw_bps = 0;
- uint64_t ext_bw_bps = 0;
- int rc = -EINVAL;
- uint32_t i;
-
- ctx = prepare->ctxt_to_hw_map;
-
- CAM_DBG(CAM_ISP,
- "usage=%u left cam_bw_bps=%llu ext_bw_bps=%llu\n"
- "right cam_bw_bps=%llu ext_bw_bps=%llu",
- bw_config->usage_type,
- bw_config->left_pix_vote.cam_bw_bps,
- bw_config->left_pix_vote.ext_bw_bps,
- bw_config->right_pix_vote.cam_bw_bps,
- bw_config->right_pix_vote.ext_bw_bps);
-
- list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) {
- for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
- if (!hw_mgr_res->hw_res[i])
- continue;
-
- if (hw_mgr_res->res_id == CAM_ISP_HW_VFE_IN_CAMIF)
- if (i == CAM_ISP_HW_SPLIT_LEFT) {
- cam_bw_bps =
- bw_config->left_pix_vote.cam_bw_bps;
- ext_bw_bps =
- bw_config->left_pix_vote.ext_bw_bps;
- } else {
- cam_bw_bps =
- bw_config->right_pix_vote.cam_bw_bps;
- ext_bw_bps =
- bw_config->right_pix_vote.ext_bw_bps;
- }
- else if ((hw_mgr_res->res_id >= CAM_ISP_HW_VFE_IN_RDI0)
- && (hw_mgr_res->res_id <=
- CAM_ISP_HW_VFE_IN_RDI3)) {
- uint32_t idx = hw_mgr_res->res_id -
- CAM_ISP_HW_VFE_IN_RDI0;
- if (idx >= bw_config->num_rdi)
- continue;
-
- cam_bw_bps =
- bw_config->rdi_vote[idx].cam_bw_bps;
- ext_bw_bps =
- bw_config->rdi_vote[idx].ext_bw_bps;
- } else
- if (hw_mgr_res->hw_res[i]) {
- CAM_ERR(CAM_ISP, "Invalid res_id %u",
- hw_mgr_res->res_id);
- rc = -EINVAL;
- return rc;
- }
-
- hw_intf = hw_mgr_res->hw_res[i]->hw_intf;
- if (hw_intf && hw_intf->hw_ops.process_cmd) {
- bw_upd_args.node_res =
- hw_mgr_res->hw_res[i];
-
- bw_upd_args.camnoc_bw_bytes = cam_bw_bps;
- bw_upd_args.external_bw_bytes = ext_bw_bps;
-
- rc = hw_intf->hw_ops.process_cmd(
- hw_intf->hw_priv,
- CAM_ISP_HW_CMD_BW_UPDATE,
- &bw_upd_args,
- sizeof(struct cam_vfe_bw_update_args));
- if (rc)
- CAM_ERR(CAM_ISP, "BW Update failed");
- } else
- CAM_WARN(CAM_ISP, "NULL hw_intf!");
- }
- }
-
- return rc;
-}
-
static int cam_isp_packet_generic_blob_handler(void *user_data,
uint32_t blob_type, uint32_t blob_size, uint8_t *blob_data)
{
@@ -2340,11 +2355,22 @@
case CAM_ISP_GENERIC_BLOB_TYPE_BW_CONFIG: {
struct cam_isp_bw_config *bw_config =
(struct cam_isp_bw_config *)blob_data;
+ struct cam_isp_prepare_hw_update_data *prepare_hw_data;
- rc = cam_isp_blob_bw_update(blob_type, blob_info,
- bw_config, prepare);
- if (rc)
- CAM_ERR(CAM_ISP, "Bandwidth Update Failed");
+ if (!prepare || !prepare->priv ||
+ (bw_config->usage_type >= CAM_IFE_HW_NUM_MAX)) {
+ CAM_ERR(CAM_ISP, "Invalid inputs");
+ rc = -EINVAL;
+ break;
+ }
+
+ prepare_hw_data = (struct cam_isp_prepare_hw_update_data *)
+ prepare->priv;
+
+ memcpy(&prepare_hw_data->bw_config[bw_config->usage_type],
+ bw_config, sizeof(prepare_hw_data->bw_config[0]));
+ prepare_hw_data->bw_config_valid[bw_config->usage_type] = true;
+
}
break;
default:
@@ -2375,6 +2401,9 @@
CAM_DBG(CAM_ISP, "enter");
+ prepare_hw_data = (struct cam_isp_prepare_hw_update_data *)
+ prepare->priv;
+
ctx = (struct cam_ife_hw_mgr_ctx *) prepare->ctxt_to_hw_map;
hw_mgr = (struct cam_ife_hw_mgr *)hw_mgr_priv;
@@ -2399,6 +2428,13 @@
prepare->num_in_map_entries = 0;
prepare->num_out_map_entries = 0;
+ memset(&prepare_hw_data->bw_config[0], 0x0,
+ sizeof(prepare_hw_data->bw_config[0]) *
+ CAM_IFE_HW_NUM_MAX);
+ memset(&prepare_hw_data->bw_config_valid[0], 0x0,
+ sizeof(prepare_hw_data->bw_config_valid[0]) *
+ CAM_IFE_HW_NUM_MAX);
+
for (i = 0; i < ctx->num_base; i++) {
CAM_DBG(CAM_ISP, "process cmd buffer for device %d", i);
@@ -2452,8 +2488,6 @@
* bits to get the type of operation since UMD definition
* of op_code has some difference from KMD.
*/
- prepare_hw_data = (struct cam_isp_prepare_hw_update_data *)
- prepare->priv;
if (((prepare->packet->header.op_code + 1) & 0xF) ==
CAM_ISP_PACKET_INIT_DEV) {
prepare_hw_data->packet_opcode_type = CAM_ISP_PACKET_INIT_DEV;
@@ -2488,6 +2522,53 @@
return rc;
}
+static int cam_ife_mgr_bw_control(struct cam_ife_hw_mgr_ctx *ctx,
+ enum cam_vfe_bw_control_action action)
+{
+ struct cam_ife_hw_mgr_res *hw_mgr_res;
+ struct cam_hw_intf *hw_intf;
+ struct cam_vfe_bw_control_args bw_ctrl_args;
+ int rc = -EINVAL;
+ uint32_t i;
+
+ CAM_DBG(CAM_ISP, "Enter...ctx id:%d", ctx->ctx_index);
+
+ list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) {
+ for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
+ if (!hw_mgr_res->hw_res[i])
+ continue;
+
+ hw_intf = hw_mgr_res->hw_res[i]->hw_intf;
+ if (hw_intf && hw_intf->hw_ops.process_cmd) {
+ bw_ctrl_args.node_res =
+ hw_mgr_res->hw_res[i];
+ bw_ctrl_args.action = action;
+
+ rc = hw_intf->hw_ops.process_cmd(
+ hw_intf->hw_priv,
+ CAM_ISP_HW_CMD_BW_CONTROL,
+ &bw_ctrl_args,
+ sizeof(struct cam_vfe_bw_control_args));
+ if (rc)
+ CAM_ERR(CAM_ISP, "BW Update failed");
+ } else
+ CAM_WARN(CAM_ISP, "NULL hw_intf!");
+ }
+ }
+
+ return rc;
+}
+
+static int cam_ife_mgr_pause_hw(struct cam_ife_hw_mgr_ctx *ctx)
+{
+ return cam_ife_mgr_bw_control(ctx, CAM_VFE_BW_CONTROL_EXCLUDE);
+}
+
+static int cam_ife_mgr_resume_hw(struct cam_ife_hw_mgr_ctx *ctx)
+{
+ return cam_ife_mgr_bw_control(ctx, CAM_VFE_BW_CONTROL_INCLUDE);
+}
+
static int cam_ife_mgr_cmd(void *hw_mgr_priv, void *cmd_args)
{
int rc = 0;
@@ -2513,6 +2594,12 @@
hw_cmd_args->u.is_rdi_only_context = 0;
break;
+ case CAM_ISP_HW_MGR_CMD_PAUSE_HW:
+ cam_ife_mgr_pause_hw(ctx);
+ break;
+ case CAM_ISP_HW_MGR_CMD_RESUME_HW:
+ cam_ife_mgr_resume_hw(ctx);
+ break;
default:
CAM_ERR(CAM_ISP, "Invalid HW mgr command:0x%x",
hw_cmd_args->cmd_type);
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h
index 4d26138..c418a41 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,9 +18,6 @@
#include "cam_ife_csid_hw_intf.h"
#include "cam_tasklet_util.h"
-/* MAX IFE instance */
-#define CAM_IFE_HW_NUM_MAX 4
-
/* enum cam_ife_hw_mgr_res_type - manager resource node type */
enum cam_ife_hw_mgr_res_type {
CAM_IFE_HW_MGR_RES_UNINIT,
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h
index cf044eb..78336d2 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,6 +19,10 @@
#include <uapi/media/cam_isp.h>
#include "cam_hw_mgr_intf.h"
+/* MAX IFE instance */
+#define CAM_IFE_HW_NUM_MAX 4
+#define CAM_IFE_RDI_NUM_MAX 4
+
/**
* enum cam_isp_hw_event_type - Collection of the ISP hardware events
*/
@@ -47,15 +51,38 @@
};
/**
+ * struct cam_isp_bw_config_internal - Internal Bandwidth configuration
+ *
+ * @usage_type: Usage type (Single/Dual)
+ * @num_rdi: Number of RDI votes
+ * @left_pix_vote: Bandwidth vote for left ISP
+ * @right_pix_vote: Bandwidth vote for right ISP
+ * @rdi_vote: RDI bandwidth requirements
+ */
+
+struct cam_isp_bw_config_internal {
+ uint32_t usage_type;
+ uint32_t num_rdi;
+ struct cam_isp_bw_vote left_pix_vote;
+ struct cam_isp_bw_vote right_pix_vote;
+ struct cam_isp_bw_vote rdi_vote[CAM_IFE_RDI_NUM_MAX];
+};
+
+/**
* struct cam_isp_prepare_hw_update_data - hw prepare data
*
* @packet_opcode_type: Packet header opcode in the packet header
- * this opcode defines, packet is init packet or
- * update packet
+ * this opcode defines, packet is init packet or
+ * update packet
+ * @bw_config: BW config information
+ * @bw_config_valid: Flag indicating whether the bw_config at the index
+ * is valid or not
*
*/
struct cam_isp_prepare_hw_update_data {
- uint32_t packet_opcode_type;
+ uint32_t packet_opcode_type;
+ struct cam_isp_bw_config_internal bw_config[CAM_IFE_HW_NUM_MAX];
+ bool bw_config_valid[CAM_IFE_HW_NUM_MAX];
};
@@ -130,6 +157,8 @@
/* enum cam_isp_hw_mgr_command - Hardware manager command type */
enum cam_isp_hw_mgr_command {
CAM_ISP_HW_MGR_CMD_IS_RDI_ONLY_CONTEXT,
+ CAM_ISP_HW_MGR_CMD_PAUSE_HW,
+ CAM_ISP_HW_MGR_CMD_RESUME_HW,
CAM_ISP_HW_MGR_CMD_MAX,
};
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
index bce0374..ff0c91f 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
@@ -287,7 +287,7 @@
static int cam_ife_csid_cid_get(struct cam_ife_csid_hw *csid_hw,
struct cam_isp_resource_node **res, int32_t vc, uint32_t dt,
- uint32_t res_type, int pixel_count)
+ uint32_t res_type)
{
int rc = 0;
struct cam_ife_csid_cid_data *cid_data;
@@ -305,8 +305,7 @@
break;
}
} else {
- if (cid_data->vc == vc && cid_data->dt == dt &&
- cid_data->pixel_count == pixel_count) {
+ if (cid_data->vc == vc && cid_data->dt == dt) {
cid_data->cnt++;
*res = &csid_hw->cid_res[i];
break;
@@ -330,7 +329,6 @@
cid_data->vc = vc;
cid_data->dt = dt;
cid_data->cnt = 1;
- cid_data->pixel_count = pixel_count;
csid_hw->cid_res[j].res_state =
CAM_ISP_RESOURCE_STATE_RESERVED;
*res = &csid_hw->cid_res[j];
@@ -570,7 +568,6 @@
struct cam_csid_hw_reserve_resource_args *cid_reserv)
{
int rc = 0;
- uint32_t i;
struct cam_ife_csid_cid_data *cid_data;
CAM_DBG(CAM_ISP,
@@ -728,7 +725,6 @@
cid_data->vc = cid_reserv->in_port->vc;
cid_data->dt = cid_reserv->in_port->dt;
cid_data->cnt = 1;
- cid_data->pixel_count = cid_reserv->pixel_count;
cid_reserv->node_res = &csid_hw->cid_res[0];
csid_hw->csi2_reserve_cnt++;
@@ -737,27 +733,43 @@
csid_hw->hw_intf->hw_idx,
cid_reserv->node_res->res_id);
} else {
- if (cid_reserv->pixel_count > 0) {
- for (i = 0; i < CAM_IFE_CSID_CID_RES_MAX; i++) {
- cid_data = (struct cam_ife_csid_cid_data *)
- csid_hw->cid_res[i].res_priv;
- if ((csid_hw->cid_res[i].res_state >=
- CAM_ISP_RESOURCE_STATE_RESERVED) &&
- cid_data->pixel_count > 0) {
- CAM_DBG(CAM_ISP,
- "CSID:%d IPP resource is full");
- rc = -EINVAL;
- goto end;
- }
+ switch (cid_reserv->res_id) {
+ case CAM_IFE_PIX_PATH_RES_IPP:
+ if (csid_hw->ipp_res.res_state !=
+ CAM_ISP_RESOURCE_STATE_AVAILABLE) {
+ CAM_DBG(CAM_ISP,
+ "CSID:%d IPP resource not available",
+ csid_hw->hw_intf->hw_idx);
+ rc = -EINVAL;
+ goto end;
}
+ break;
+ case CAM_IFE_PIX_PATH_RES_RDI_0:
+ case CAM_IFE_PIX_PATH_RES_RDI_1:
+ case CAM_IFE_PIX_PATH_RES_RDI_2:
+ case CAM_IFE_PIX_PATH_RES_RDI_3:
+ if (csid_hw->rdi_res[cid_reserv->res_id].res_state !=
+ CAM_ISP_RESOURCE_STATE_AVAILABLE) {
+ CAM_DBG(CAM_ISP,
+ "CSID:%d RDI:%d resource not available",
+ csid_hw->hw_intf->hw_idx,
+ cid_reserv->res_id);
+ rc = -EINVAL;
+ goto end;
+ }
+ break;
+ default:
+ CAM_ERR(CAM_ISP, "CSID%d: Invalid csid path",
+ csid_hw->hw_intf->hw_idx);
+ rc = -EINVAL;
+ goto end;
}
rc = cam_ife_csid_cid_get(csid_hw,
&cid_reserv->node_res,
cid_reserv->in_port->vc,
cid_reserv->in_port->dt,
- cid_reserv->in_port->res_type,
- cid_reserv->pixel_count);
+ cid_reserv->in_port->res_type);
/* if success then increment the reserve count */
if (!rc) {
if (csid_hw->csi2_reserve_cnt == UINT_MAX) {
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h
index b400d14..4b546ea 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -368,7 +368,6 @@
uint32_t dt;
uint32_t cnt;
uint32_t tpg_set;
- int pixel_count;
};
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h
index df97bd6..ceeacbe 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -75,7 +75,6 @@
* @cid: cid (DT_ID) value for path, this is applicable for CSID path
* reserve
* @node_res : Reserved resource structure pointer
- * @pixel_count: Number of pixel resources
*
*/
struct cam_csid_hw_reserve_resource_args {
@@ -87,7 +86,6 @@
uint32_t master_idx;
uint32_t cid;
struct cam_isp_resource_node *node_res;
- int pixel_count;
};
/**
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h
index 257a5ac..b9f6d77 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -92,6 +92,7 @@
CAM_ISP_HW_CMD_STRIPE_UPDATE,
CAM_ISP_HW_CMD_CLOCK_UPDATE,
CAM_ISP_HW_CMD_BW_UPDATE,
+ CAM_ISP_HW_CMD_BW_CONTROL,
CAM_ISP_HW_CMD_MAX,
};
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h
index b771ec6..8927d6a 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -185,6 +185,22 @@
uint64_t external_bw_bytes;
};
+enum cam_vfe_bw_control_action {
+ CAM_VFE_BW_CONTROL_EXCLUDE = 0,
+ CAM_VFE_BW_CONTROL_INCLUDE = 1
+};
+
+/*
+ * struct cam_vfe_bw_control_args:
+ *
+ * @node_res: Resource to get the time stamp
+ * @action: Bandwidth control action
+ */
+struct cam_vfe_bw_control_args {
+ struct cam_isp_resource_node *node_res;
+ enum cam_vfe_bw_control_action action;
+};
+
/*
* struct cam_vfe_top_irq_evt_payload:
*
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
index 02fec28..2c4fe9d 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -697,6 +697,7 @@
case CAM_ISP_HW_CMD_GET_REG_UPDATE:
case CAM_ISP_HW_CMD_CLOCK_UPDATE:
case CAM_ISP_HW_CMD_BW_UPDATE:
+ case CAM_ISP_HW_CMD_BW_CONTROL:
rc = core_info->vfe_top->hw_ops.process_cmd(
core_info->vfe_top->top_priv, cmd_type, cmd_args,
arg_size);
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c
index f166025..f4aa5c3 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -20,8 +20,9 @@
#include "cam_cpas_api.h"
#include "cam_vfe_soc.h"
-#define CAM_VFE_HW_RESET_HW_AND_REG_VAL 0x00003F9F
-#define CAM_VFE_HW_RESET_HW_VAL 0x00003F87
+#define CAM_VFE_HW_RESET_HW_AND_REG_VAL 0x00003F9F
+#define CAM_VFE_HW_RESET_HW_VAL 0x00003F87
+#define CAM_VFE_DELAY_BW_REDUCTION_NUM_FRAMES 3
struct cam_vfe_top_ver2_common_data {
struct cam_hw_soc_info *soc_info;
@@ -33,7 +34,11 @@
struct cam_vfe_top_ver2_common_data common_data;
struct cam_isp_resource_node mux_rsrc[CAM_VFE_TOP_VER2_MUX_MAX];
unsigned long hw_clk_rate;
- struct cam_axi_vote hw_axi_vote;
+ enum cam_vfe_bw_control_action axi_vote_control[
+ CAM_VFE_TOP_VER2_MUX_MAX];
+ struct cam_axi_vote to_be_applied_axi_vote;
+ struct cam_axi_vote applied_axi_vote;
+ uint32_t counter_to_update_axi_vote;
struct cam_axi_vote req_axi_vote[CAM_VFE_TOP_VER2_MUX_MAX];
unsigned long req_clk_rate[CAM_VFE_TOP_VER2_MUX_MAX];
};
@@ -119,7 +124,8 @@
}
static int cam_vfe_top_set_axi_bw_vote(
- struct cam_vfe_top_ver2_priv *top_priv)
+ struct cam_vfe_top_ver2_priv *top_priv,
+ bool start_stop)
{
struct cam_axi_vote sum = {0, 0};
int i, rc = 0;
@@ -127,6 +133,7 @@
top_priv->common_data.soc_info;
struct cam_vfe_soc_private *soc_private =
soc_info->soc_private;
+ bool apply_bw_update = false;
if (!soc_private) {
CAM_ERR(CAM_ISP, "Error soc_private NULL");
@@ -134,30 +141,98 @@
}
for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) {
- sum.uncompressed_bw +=
- top_priv->req_axi_vote[i].uncompressed_bw;
- sum.compressed_bw +=
- top_priv->req_axi_vote[i].compressed_bw;
+ if (top_priv->axi_vote_control[i] ==
+ CAM_VFE_BW_CONTROL_INCLUDE) {
+ sum.uncompressed_bw +=
+ top_priv->req_axi_vote[i].uncompressed_bw;
+ sum.compressed_bw +=
+ top_priv->req_axi_vote[i].compressed_bw;
+ }
}
- CAM_DBG(CAM_ISP, "BW Vote: u=%lld c=%lld",
+ CAM_DBG(CAM_ISP, "Updating BW from (%llu %llu) to (%llu %llu)",
+ top_priv->applied_axi_vote.uncompressed_bw,
+ top_priv->applied_axi_vote.compressed_bw,
sum.uncompressed_bw,
sum.compressed_bw);
- if ((top_priv->hw_axi_vote.uncompressed_bw ==
+ if ((top_priv->applied_axi_vote.uncompressed_bw ==
sum.uncompressed_bw) &&
- (top_priv->hw_axi_vote.compressed_bw ==
- sum.compressed_bw))
+ (top_priv->applied_axi_vote.compressed_bw ==
+ sum.compressed_bw)) {
+ CAM_DBG(CAM_ISP, "BW config unchanged %llu %llu",
+ top_priv->applied_axi_vote.uncompressed_bw,
+ top_priv->applied_axi_vote.compressed_bw);
+ top_priv->counter_to_update_axi_vote = 0;
return 0;
+ }
- rc = cam_cpas_update_axi_vote(
+ if ((top_priv->to_be_applied_axi_vote.uncompressed_bw !=
+ sum.uncompressed_bw) ||
+ (top_priv->to_be_applied_axi_vote.compressed_bw !=
+ sum.compressed_bw)) {
+ // we got a new bw value to apply
+ top_priv->counter_to_update_axi_vote = 0;
+
+ top_priv->to_be_applied_axi_vote.uncompressed_bw =
+ sum.uncompressed_bw;
+ top_priv->to_be_applied_axi_vote.compressed_bw =
+ sum.compressed_bw;
+ }
+
+ if (start_stop == true) {
+ CAM_DBG(CAM_ISP,
+ "New bw in start/stop, applying bw now, counter=%d",
+ top_priv->counter_to_update_axi_vote);
+ top_priv->counter_to_update_axi_vote = 0;
+ apply_bw_update = true;
+ } else if ((top_priv->to_be_applied_axi_vote.uncompressed_bw <
+ top_priv->applied_axi_vote.uncompressed_bw) ||
+ (top_priv->to_be_applied_axi_vote.compressed_bw <
+ top_priv->applied_axi_vote.compressed_bw)) {
+ if (top_priv->counter_to_update_axi_vote >=
+ (CAM_VFE_TOP_VER2_MUX_MAX *
+ CAM_VFE_DELAY_BW_REDUCTION_NUM_FRAMES)) {
+ CAM_DBG(CAM_ISP,
+ "New bw is less, applying bw now, counter=%d",
+ top_priv->counter_to_update_axi_vote);
+ top_priv->counter_to_update_axi_vote = 0;
+ apply_bw_update = true;
+ } else {
+ CAM_DBG(CAM_ISP,
+ "New bw is less, Defer applying bw, counter=%d",
+ top_priv->counter_to_update_axi_vote);
+
+ top_priv->counter_to_update_axi_vote++;
+ apply_bw_update = false;
+ }
+ } else {
+ CAM_DBG(CAM_ISP,
+ "New bw is more, applying bw now, counter=%d",
+ top_priv->counter_to_update_axi_vote);
+ top_priv->counter_to_update_axi_vote = 0;
+ apply_bw_update = true;
+ }
+
+ CAM_DBG(CAM_ISP,
+ "counter=%d, apply_bw_update=%d",
+ top_priv->counter_to_update_axi_vote,
+ apply_bw_update);
+
+ if (apply_bw_update == true) {
+ rc = cam_cpas_update_axi_vote(
soc_private->cpas_handle,
- &sum);
- if (!rc) {
- top_priv->hw_axi_vote.uncompressed_bw = sum.uncompressed_bw;
- top_priv->hw_axi_vote.compressed_bw = sum.compressed_bw;
- } else
- CAM_ERR(CAM_ISP, "BW request failed, rc=%d", rc);
+ &top_priv->to_be_applied_axi_vote);
+ if (!rc) {
+ top_priv->applied_axi_vote.uncompressed_bw =
+ top_priv->to_be_applied_axi_vote.uncompressed_bw;
+ top_priv->applied_axi_vote.compressed_bw =
+ top_priv->to_be_applied_axi_vote.compressed_bw;
+ } else {
+ CAM_ERR(CAM_ISP, "BW request failed, rc=%d", rc);
+ }
+ top_priv->counter_to_update_axi_vote = 0;
+ }
return rc;
}
@@ -239,6 +314,8 @@
bw_update->camnoc_bw_bytes;
top_priv->req_axi_vote[i].compressed_bw =
bw_update->external_bw_bytes;
+ top_priv->axi_vote_control[i] =
+ CAM_VFE_BW_CONTROL_INCLUDE;
break;
}
}
@@ -248,7 +325,51 @@
res->hw_intf->hw_idx,
hw_info->hw_state);
} else
- rc = cam_vfe_top_set_axi_bw_vote(top_priv);
+ rc = cam_vfe_top_set_axi_bw_vote(top_priv, false);
+
+ return rc;
+}
+
+static int cam_vfe_top_bw_control(
+ struct cam_vfe_top_ver2_priv *top_priv,
+ void *cmd_args, uint32_t arg_size)
+{
+ struct cam_vfe_bw_control_args *bw_ctrl = NULL;
+ struct cam_isp_resource_node *res = NULL;
+ struct cam_hw_info *hw_info = NULL;
+ int rc = 0;
+ int i;
+
+ bw_ctrl = (struct cam_vfe_bw_control_args *)cmd_args;
+ res = bw_ctrl->node_res;
+
+ if (!res || !res->hw_intf->hw_priv)
+ return -EINVAL;
+
+ hw_info = res->hw_intf->hw_priv;
+
+ if (res->res_type != CAM_ISP_RESOURCE_VFE_IN ||
+ res->res_id >= CAM_ISP_HW_VFE_IN_MAX) {
+ CAM_ERR(CAM_ISP, "VFE:%d Invalid res_type:%d res id%d",
+ res->hw_intf->hw_idx, res->res_type,
+ res->res_id);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) {
+ if (top_priv->mux_rsrc[i].res_id == res->res_id) {
+ top_priv->axi_vote_control[i] = bw_ctrl->action;
+ break;
+ }
+ }
+
+ if (hw_info->hw_state != CAM_HW_STATE_POWER_UP) {
+ CAM_DBG(CAM_ISP, "VFE:%d Not ready to set BW yet :%d",
+ res->hw_intf->hw_idx,
+ hw_info->hw_state);
+ } else {
+ rc = cam_vfe_top_set_axi_bw_vote(top_priv, true);
+ }
return rc;
}
@@ -412,7 +533,7 @@
return rc;
}
- rc = cam_vfe_top_set_axi_bw_vote(top_priv);
+ rc = cam_vfe_top_set_axi_bw_vote(top_priv, true);
if (rc) {
CAM_ERR(CAM_ISP, "set_axi_bw_vote failed, rc=%d", rc);
return rc;
@@ -458,6 +579,8 @@
top_priv->req_clk_rate[i] = 0;
top_priv->req_axi_vote[i].compressed_bw = 0;
top_priv->req_axi_vote[i].uncompressed_bw = 0;
+ top_priv->axi_vote_control[i] =
+ CAM_VFE_BW_CONTROL_EXCLUDE;
break;
}
}
@@ -468,7 +591,7 @@
return rc;
}
- rc = cam_vfe_top_set_axi_bw_vote(top_priv);
+ rc = cam_vfe_top_set_axi_bw_vote(top_priv, true);
if (rc) {
CAM_ERR(CAM_ISP, "set_axi_bw_vote failed, rc=%d", rc);
return rc;
@@ -518,6 +641,9 @@
rc = cam_vfe_top_bw_update(top_priv, cmd_args,
arg_size);
break;
+ case CAM_ISP_HW_CMD_BW_CONTROL:
+ rc = cam_vfe_top_bw_control(top_priv, cmd_args, arg_size);
+ break;
default:
rc = -EINVAL;
CAM_ERR(CAM_ISP, "Error! Invalid cmd:%d", cmd_type);
@@ -554,8 +680,11 @@
}
vfe_top->top_priv = top_priv;
top_priv->hw_clk_rate = 0;
- top_priv->hw_axi_vote.compressed_bw = 0;
- top_priv->hw_axi_vote.uncompressed_bw = 0;
+ top_priv->to_be_applied_axi_vote.compressed_bw = 0;
+ top_priv->to_be_applied_axi_vote.uncompressed_bw = 0;
+ top_priv->applied_axi_vote.compressed_bw = 0;
+ top_priv->applied_axi_vote.uncompressed_bw = 0;
+ top_priv->counter_to_update_axi_vote = 0;
for (i = 0, j = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) {
top_priv->mux_rsrc[i].res_type = CAM_ISP_RESOURCE_VFE_IN;
@@ -565,6 +694,8 @@
top_priv->req_clk_rate[i] = 0;
top_priv->req_axi_vote[i].compressed_bw = 0;
top_priv->req_axi_vote[i].uncompressed_bw = 0;
+ top_priv->axi_vote_control[i] = CAM_VFE_BW_CONTROL_EXCLUDE;
+
if (ver2_hw_info->mux_type[i] == CAM_VFE_CAMIF_VER_2_0) {
top_priv->mux_rsrc[i].res_id =
diff --git a/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.c b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.c
index bb952e6..21e66a2 100644
--- a/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.c
+++ b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.c
@@ -68,6 +68,10 @@
cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd,
hw_info->bus_rd_reg.bus_client_reg[index].unpack_cfg_0,
0x1);
+ else if (io_buf->io_cfg->format == CAM_FORMAT_PLAIN16_10)
+ cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd,
+ hw_info->bus_rd_reg.bus_client_reg[index].unpack_cfg_0,
+ 0x22);
else
CAM_ERR(CAM_LRME, "Unsupported format %d",
io_buf->io_cfg->format);
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c
index 9689698..02f03ea 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -761,7 +761,7 @@
"Buffer inactive at idx=%d, continuing", i);
continue;
} else {
- CAM_INFO(CAM_CRM,
+ CAM_DBG(CAM_CRM,
"Active buffer at idx=%d, possible leak needs unmapping",
i);
cam_mem_mgr_unmap_active_buf(i);
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
index d7662f1..3100f91 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -2487,6 +2487,12 @@
return -EINVAL;
}
+ if ((!sync_info->link_hdls[0]) || (!sync_info->link_hdls[1])) {
+ CAM_WARN(CAM_CRM, "Invalid link handles 0x%x 0x%x",
+ sync_info->link_hdls[0], sync_info->link_hdls[1]);
+ return -EINVAL;
+ }
+
mutex_lock(&g_crm_core_dev->crm_lock);
/* session hdl's priv data is cam session struct */
cam_session = (struct cam_req_mgr_core_session *)
@@ -2607,9 +2613,12 @@
int cam_req_mgr_link_control(struct cam_req_mgr_link_control *control)
{
int rc = 0;
- int i;
+ int i, j;
struct cam_req_mgr_core_link *link = NULL;
+ struct cam_req_mgr_connected_device *dev = NULL;
+ struct cam_req_mgr_link_evt_data evt_data;
+
if (!control) {
CAM_ERR(CAM_CRM, "Control command is NULL");
rc = -EINVAL;
@@ -2639,9 +2648,29 @@
link->link_hdl);
rc = -EFAULT;
}
+ /* notify nodes */
+ for (j = 0; j < link->num_devs; j++) {
+ dev = &link->l_dev[j];
+ evt_data.evt_type = CAM_REQ_MGR_LINK_EVT_RESUME;
+ evt_data.link_hdl = link->link_hdl;
+ evt_data.dev_hdl = dev->dev_hdl;
+ evt_data.req_id = 0;
+ if (dev->ops && dev->ops->process_evt)
+ dev->ops->process_evt(&evt_data);
+ }
} else if (control->ops == CAM_REQ_MGR_LINK_DEACTIVATE) {
/* Destroy SOF watchdog timer */
crm_timer_exit(&link->watchdog);
+ /* notify nodes */
+ for (j = 0; j < link->num_devs; j++) {
+ dev = &link->l_dev[j];
+ evt_data.evt_type = CAM_REQ_MGR_LINK_EVT_PAUSE;
+ evt_data.link_hdl = link->link_hdl;
+ evt_data.dev_hdl = dev->dev_hdl;
+ evt_data.req_id = 0;
+ if (dev->ops && dev->ops->process_evt)
+ dev->ops->process_evt(&evt_data);
+ }
} else {
CAM_ERR(CAM_CRM, "Invalid link control command");
rc = -EINVAL;
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_interface.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_interface.h
index ce8dfa7..45ebc69 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_interface.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_interface.h
@@ -81,9 +81,9 @@
* @process_evt : payload to generic event
*/
struct cam_req_mgr_kmd_ops {
- cam_req_mgr_get_dev_info get_dev_info;
- cam_req_mgr_link_setup link_setup;
- cam_req_mgr_apply_req apply_req;
+ cam_req_mgr_get_dev_info get_dev_info;
+ cam_req_mgr_link_setup link_setup;
+ cam_req_mgr_apply_req apply_req;
cam_req_mgr_flush_req flush_req;
cam_req_mgr_process_evt process_evt;
};
@@ -182,6 +182,8 @@
*/
enum cam_req_mgr_link_evt_type {
CAM_REQ_MGR_LINK_EVT_ERR,
+ CAM_REQ_MGR_LINK_EVT_PAUSE,
+ CAM_REQ_MGR_LINK_EVT_RESUME,
CAM_REQ_MGR_LINK_EVT_MAX,
};
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
index 9894ca3..d5bb1b0 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -191,14 +191,16 @@
CAM_ERR(CAM_SENSOR,
"Already some pkt in offset req : %lld",
csl_packet->header.request_id);
- rc = delete_request(i2c_reg_settings);
- if (rc < 0) {
- CAM_ERR(CAM_SENSOR,
- "Failed in Deleting the err: %d", rc);
- return rc;
- }
+ /*
+ * Update req mgr even in case of failure.
+ * This will help not to wait indefinitely
+ * and freeze. If this log is triggered then
+ * fix it.
+ */
+ cam_sensor_update_req_mgr(s_ctrl, csl_packet);
+ return 0;
}
- break;
+ break;
}
case CAM_SENSOR_PACKET_OPCODE_SENSOR_NOP: {
if ((s_ctrl->sensor_state == CAM_SENSOR_INIT) ||
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
index 7a6d7fd..89aad4e 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -98,6 +98,11 @@
return -EINVAL;
}
+ if (!write_setting->reg_setting) {
+ CAM_ERR(CAM_SENSOR, "Invalid Register Settings");
+ return -EINVAL;
+ }
+
if (io_master_info->master_type == CCI_MASTER) {
return cam_cci_i2c_write_table(io_master_info,
write_setting);
@@ -125,6 +130,11 @@
return -EINVAL;
}
+ if (!write_setting->reg_setting) {
+ CAM_ERR(CAM_SENSOR, "Invalid Register Settings");
+ return -EINVAL;
+ }
+
if (io_master_info->master_type == CCI_MASTER) {
return cam_cci_i2c_write_continuous_table(io_master_info,
write_setting, cam_sensor_i2c_write_flag);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h
index 72ca737..622dae6 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -26,7 +26,7 @@
#define MAX_REGULATOR 5
#define MAX_POWER_CONFIG 12
-#define MAX_PER_FRAME_ARRAY 8
+#define MAX_PER_FRAME_ARRAY 32
#define CAM_SENSOR_NAME "cam-sensor"
#define CAM_ACTUATOR_NAME "cam-actuator"
diff --git a/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.c b/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.c
index c757315..e04c6b9 100644
--- a/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.c
+++ b/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -140,6 +140,7 @@
struct work_struct smmu_work;
struct mutex payload_list_lock;
struct list_head payload_list;
+ u32 non_fatal_fault;
};
static const struct of_device_id msm_cam_smmu_dt_match[] = {
@@ -2902,6 +2903,15 @@
rc = -ENODEV;
goto end;
}
+
+ iommu_cb_set.non_fatal_fault = 1;
+ if (iommu_domain_set_attr(cb->mapping->domain,
+ DOMAIN_ATTR_NON_FATAL_FAULTS,
+ &iommu_cb_set.non_fatal_fault) < 0) {
+ CAM_ERR(CAM_SMMU,
+ "Error: failed to set non fatal fault attribute");
+ }
+
} else {
CAM_ERR(CAM_SMMU, "Context bank does not have IO region");
rc = -ENODEV;
diff --git a/drivers/media/platform/msm/camera/cam_utils/cam_io_util.c b/drivers/media/platform/msm/camera/cam_utils/cam_io_util.c
index 1b5fd9f..8d5f96a 100644
--- a/drivers/media/platform/msm/camera/cam_utils/cam_io_util.c
+++ b/drivers/media/platform/msm/camera/cam_utils/cam_io_util.c
@@ -1,4 +1,5 @@
-/* Copyright (c) 2011-2014, 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2014, 2017-2018, The Linux Foundation.
+ * All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -36,6 +37,8 @@
/* Ensure previous writes are done */
wmb();
writel_relaxed_no_log(data, addr);
+ /* Ensure previous writes are done */
+ wmb();
return 0;
}
@@ -68,6 +71,8 @@
rmb();
data = readl_relaxed(addr);
CAM_DBG(CAM_UTIL, "0x%pK %08x", addr, data);
+ /* Ensure previous read is done */
+ rmb();
return data;
}
@@ -113,6 +118,8 @@
CAM_DBG(CAM_UTIL, "0x%pK %08x", d, *s);
writel_relaxed(*s++, d++);
}
+ /* Ensure previous writes are done */
+ wmb();
return 0;
}
diff --git a/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.c b/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.c
index bd56310..0b1896f 100644
--- a/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.c
+++ b/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -859,7 +859,7 @@
count = of_property_count_strings(of_node, "reg-names");
if (count <= 0) {
- CAM_WARN(CAM_UTIL, "no reg-names found for: %s",
+ CAM_DBG(CAM_UTIL, "no reg-names found for: %s",
soc_info->dev_name);
count = 0;
}
@@ -896,7 +896,7 @@
rc = of_property_read_string_index(of_node, "interrupt-names", 0,
&soc_info->irq_name);
if (rc) {
- CAM_WARN(CAM_UTIL, "No interrupt line preset for: %s",
+ CAM_DBG(CAM_UTIL, "No interrupt line preset for: %s",
soc_info->dev_name);
rc = 0;
} else {
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index 03dfde6..44cc7dc 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -39,6 +39,9 @@
case HFI_ERR_SYS_FATAL:
vidc_err = VIDC_ERR_HW_FATAL;
break;
+ case HFI_ERR_SYS_NOC_ERROR:
+ vidc_err = VIDC_ERR_NOC_ERROR;
+ break;
case HFI_ERR_SYS_VERSION_MISMATCH:
case HFI_ERR_SYS_INVALID_PARAMETER:
case HFI_ERR_SYS_SESSION_ID_OUT_OF_RANGE:
@@ -316,11 +319,14 @@
return 0;
}
-static int hfi_process_sys_error(u32 device_id, struct msm_vidc_cb_info *info)
+static int hfi_process_sys_error(u32 device_id,
+ struct hfi_msg_event_notify_packet *pkt,
+ struct msm_vidc_cb_info *info)
{
struct msm_vidc_cb_cmd_done cmd_done = {0};
cmd_done.device_id = device_id;
+ cmd_done.status = hfi_map_err_status(pkt->event_data1);
info->response_type = HAL_SYS_ERROR;
info->response.cmd = cmd_done;
@@ -373,7 +379,7 @@
case HFI_EVENT_SYS_ERROR:
dprintk(VIDC_ERR, "HFI_EVENT_SYS_ERROR: %d, %#x\n",
pkt->event_data1, pkt->event_data2);
- return hfi_process_sys_error(device_id, info);
+ return hfi_process_sys_error(device_id, pkt, info);
case HFI_EVENT_SESSION_ERROR:
dprintk(VIDC_INFO, "HFI_EVENT_SESSION_ERROR[%#x]\n",
pkt->session_id);
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
index fa40091..42bf1ba 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -39,6 +39,8 @@
static inline struct msm_vidc_inst *get_vidc_inst(struct file *filp, void *fh)
{
+ if (!filp->private_data)
+ return NULL;
return container_of(filp->private_data,
struct msm_vidc_inst, event_handler);
}
@@ -74,6 +76,7 @@
vidc_inst = get_vidc_inst(filp, NULL);
rc = msm_vidc_close(vidc_inst);
+ filp->private_data = NULL;
trace_msm_v4l2_vidc_close_end("msm_v4l2_close end");
return rc;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 360d2df..f3ab5be 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -35,6 +35,8 @@
V4L2_EVENT_MSM_VIDC_RELEASE_BUFFER_REFERENCE
#define L_MODE V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY
+#define TRIGGER_SSR_LOCK_RETRIES 5
+
const char *const mpeg_video_vidc_extradata[] = {
"Extradata none",
"Extradata MB Quantization",
@@ -2207,6 +2209,10 @@
}
/* handle the hw error before core released to get full debug info */
msm_vidc_handle_hw_error(core);
+ if (response->status == VIDC_ERR_NOC_ERROR) {
+ dprintk(VIDC_WARN, "Got NOC error");
+ MSM_VIDC_ERROR(true);
+ }
dprintk(VIDC_DBG, "Calling core_release\n");
rc = call_hfi_op(hdev, core_release, hdev->hfi_device_data);
if (rc) {
@@ -3913,13 +3919,17 @@
struct eos_buf *binfo = NULL;
u32 smem_flags = 0;
- get_inst(inst->core, inst);
+ if (inst->state != MSM_VIDC_START_DONE) {
+ dprintk(VIDC_DBG,
+ "Inst = %pK is not ready for EOS\n", inst);
+ break;
+ }
binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
if (!binfo) {
dprintk(VIDC_ERR, "%s: Out of memory\n", __func__);
rc = -ENOMEM;
- goto exit;
+ break;
}
if (inst->flags & VIDC_SECURE)
@@ -3929,26 +3939,25 @@
SZ_4K, 1, smem_flags,
HAL_BUFFER_INPUT, 0, &binfo->smem);
if (rc) {
+ kfree(binfo);
dprintk(VIDC_ERR,
"Failed to allocate output memory\n");
rc = -ENOMEM;
- goto exit;
+ break;
}
mutex_lock(&inst->eosbufs.lock);
list_add_tail(&binfo->list, &inst->eosbufs.list);
mutex_unlock(&inst->eosbufs.lock);
- if (inst->state != MSM_VIDC_START_DONE) {
- dprintk(VIDC_DBG,
- "Inst = %pK is not ready for EOS\n", inst);
- goto exit;
- }
-
rc = msm_vidc_send_pending_eos_buffers(inst);
-
-exit:
- put_inst(inst);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed pending_eos_buffers sending\n");
+ list_del(&binfo->list);
+ kfree(binfo);
+ break;
+ }
break;
}
default:
@@ -5305,6 +5314,7 @@
{
int rc = 0;
struct hfi_device *hdev;
+ int try_lock_counter = TRIGGER_SSR_LOCK_RETRIES;
if (!core || !core->device) {
dprintk(VIDC_WARN, "Invalid parameters: %pK\n", core);
@@ -5312,7 +5322,13 @@
}
hdev = core->device;
- mutex_lock(&core->lock);
+ while (try_lock_counter) {
+ if (mutex_trylock(&core->lock))
+ break;
+ try_lock_counter--;
+ if (!try_lock_counter)
+ return -EBUSY;
+ }
if (core->state == VIDC_CORE_INIT_DONE) {
/*
* In current implementation user-initiated SSR triggers
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
index 215bb78..74fa3d5 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -131,7 +131,7 @@
static ssize_t trigger_ssr_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos) {
unsigned long ssr_trigger_val = 0;
- int rc = 0;
+ int rc = 0, ret = 0;
struct msm_vidc_core *core = filp->private_data;
size_t size = MAX_SSR_STRING_LEN;
char kbuf[MAX_SSR_STRING_LEN + 1] = {0};
@@ -156,8 +156,8 @@
dprintk(VIDC_WARN, "returning error err %d\n", rc);
rc = -EINVAL;
} else {
- msm_vidc_trigger_ssr(core, ssr_trigger_val);
- rc = count;
+ ret = msm_vidc_trigger_ssr(core, ssr_trigger_val);
+ rc = (ret == -EBUSY ? ret : count);
}
exit:
return rc;
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 b1a240d..530fe3a 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -992,10 +992,11 @@
if (core->smmu_fault_handled) {
if (core->resources.non_fatal_pagefaults) {
- msm_vidc_noc_error_info(core);
- MSM_VIDC_ERROR(true);
+ dprintk(VIDC_ERR,
+ "%s: non-fatal pagefault address: %lx\n",
+ __func__, iova);
+ return 0;
}
- return -ENOSYS;
}
dprintk(VIDC_ERR, "%s - faulting address: %lx\n", __func__, iova);
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 2260b55..54cbdfc 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -88,6 +88,7 @@
VIDC_ERR_TIMEOUT,
VIDC_ERR_CMDQFULL,
VIDC_ERR_START_CODE_NOT_FOUND,
+ VIDC_ERR_NOC_ERROR,
VIDC_ERR_CLIENT_PRESENT = 0x90000001,
VIDC_ERR_CLIENT_FATAL,
VIDC_ERR_CMD_QUEUE_FULL,
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index ca6d803..ea8cf1a 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -45,7 +45,7 @@
#define HFI_ERR_SYS_SESSION_IN_USE (HFI_COMMON_BASE + 0x7)
#define HFI_ERR_SYS_SESSION_ID_OUT_OF_RANGE (HFI_COMMON_BASE + 0x8)
#define HFI_ERR_SYS_UNSUPPORTED_DOMAIN (HFI_COMMON_BASE + 0x9)
-
+#define HFI_ERR_SYS_NOC_ERROR (HFI_COMMON_BASE + 0x11)
#define HFI_ERR_SESSION_FATAL (HFI_COMMON_BASE + 0x1001)
#define HFI_ERR_SESSION_INVALID_PARAMETER (HFI_COMMON_BASE + 0x1002)
#define HFI_ERR_SESSION_BAD_POINTER (HFI_COMMON_BASE + 0x1003)
diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c
index dc76fd4..0324633 100644
--- a/drivers/media/usb/usbtv/usbtv-core.c
+++ b/drivers/media/usb/usbtv/usbtv-core.c
@@ -141,6 +141,7 @@
static struct usb_device_id usbtv_id_table[] = {
{ USB_DEVICE(0x1b71, 0x3002) },
+ { USB_DEVICE(0x1f71, 0x3301) },
{}
};
MODULE_DEVICE_TABLE(usb, usbtv_id_table);
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 146ca6f..b5ad125 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -1,7 +1,7 @@
/*
* QTI Secure Execution Environment Communicator (QSEECOM) driver
*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1859,6 +1859,8 @@
struct qseecom_command_scm_resp continue_resp;
bool found_app = false;
unsigned long flags;
+ sigset_t new_sigset;
+ sigset_t old_sigset;
if (!resp || !data) {
pr_err("invalid resp or data pointer\n");
@@ -1900,23 +1902,23 @@
ptr_app->blocked_on_listener_id = resp->data;
/* sleep until listener is available */
+ sigfillset(&new_sigset);
+ sigprocmask(SIG_SETMASK, &new_sigset, &old_sigset);
+
do {
qseecom.app_block_ref_cnt++;
ptr_app->app_blocked = true;
mutex_unlock(&app_access_lock);
- if (wait_event_freezable(
+ wait_event_freezable(
list_ptr->listener_block_app_wq,
- !list_ptr->listener_in_use)) {
- pr_err("Interrupted: listener_id %d, app_id %d\n",
- resp->data, ptr_app->app_id);
- ret = -ERESTARTSYS;
- goto exit;
- }
+ !list_ptr->listener_in_use);
mutex_lock(&app_access_lock);
ptr_app->app_blocked = false;
qseecom.app_block_ref_cnt--;
} while (list_ptr->listener_in_use);
+ sigprocmask(SIG_SETMASK, &old_sigset, NULL);
+
ptr_app->blocked_on_listener_id = 0;
/* notify the blocked app that listener is available */
pr_warn("Lsntr %d is available, unblock app(%d) %s in TZ\n",
@@ -1950,6 +1952,8 @@
struct qseecom_continue_blocked_request_ireq ireq;
struct qseecom_command_scm_resp continue_resp;
unsigned int session_id;
+ sigset_t new_sigset;
+ sigset_t old_sigset;
if (!resp) {
pr_err("invalid resp pointer\n");
@@ -1965,22 +1969,23 @@
}
pr_debug("lsntr %d in_use = %d\n",
resp->data, list_ptr->listener_in_use);
+
/* sleep until listener is available */
+ sigfillset(&new_sigset);
+ sigprocmask(SIG_SETMASK, &new_sigset, &old_sigset);
+
do {
qseecom.app_block_ref_cnt++;
mutex_unlock(&app_access_lock);
- if (wait_event_freezable(
+ wait_event_freezable(
list_ptr->listener_block_app_wq,
- !list_ptr->listener_in_use)) {
- pr_err("Interrupted: listener_id %d, session_id %d\n",
- resp->data, session_id);
- ret = -ERESTARTSYS;
- goto exit;
- }
+ !list_ptr->listener_in_use);
mutex_lock(&app_access_lock);
qseecom.app_block_ref_cnt--;
} while (list_ptr->listener_in_use);
+ sigprocmask(SIG_SETMASK, &old_sigset, NULL);
+
/* notify TZ that listener is available */
pr_warn("Lsntr %d is available, unblock session(%d) in TZ\n",
resp->data, session_id);
diff --git a/drivers/mmc/host/cmdq_hci.c b/drivers/mmc/host/cmdq_hci.c
index 55ce946..01811d9 100644
--- a/drivers/mmc/host/cmdq_hci.c
+++ b/drivers/mmc/host/cmdq_hci.c
@@ -357,7 +357,7 @@
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",
+ pr_debug("desc-base: 0x%pK trans-base: 0x%pK\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);
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 367c84f..5ed9b72 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-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -39,6 +39,7 @@
#include <linux/iopoll.h>
#include <linux/msm-bus.h>
#include <linux/pm_runtime.h>
+#include <linux/nvmem-consumer.h>
#include <trace/events/mmc.h>
#include "sdhci-msm.h"
@@ -1885,6 +1886,65 @@
}
}
+#ifdef CONFIG_NVMEM
+/* Parse qfprom data for deciding on errata work-arounds */
+static long qfprom_read(struct device *dev, const char *name)
+{
+ struct nvmem_cell *cell;
+ ssize_t len = 0;
+ u32 *buf, val = 0;
+ long err = 0;
+
+ cell = nvmem_cell_get(dev, name);
+ if (IS_ERR(cell)) {
+ err = PTR_ERR(cell);
+ dev_err(dev, "failed opening nvmem cell err : %ld\n", err);
+ /* If entry does not exist, then that is not an error */
+ if (err == -ENOENT)
+ err = 0;
+ return err;
+ }
+
+ buf = (u32 *)nvmem_cell_read(cell, &len);
+ if (IS_ERR(buf) || !len) {
+ dev_err(dev, "Failed reading nvmem cell, err: %u, bytes fetched: %zd\n",
+ *buf, len);
+ if (!IS_ERR(buf)) {
+ kfree(buf);
+ err = -EINVAL;
+ } else {
+ err = PTR_ERR(buf);
+ }
+ } else {
+ val = *buf;
+ kfree(buf);
+ }
+
+ nvmem_cell_put(cell);
+ return err ? err : (long) val;
+}
+
+/* Reads the SoC version */
+static int sdhci_msm_get_socrev(struct device *dev,
+ struct sdhci_msm_host *msm_host)
+{
+
+ msm_host->soc_min_rev = qfprom_read(dev, "minor_rev");
+
+ if (msm_host->soc_min_rev < 0)
+ dev_err(dev, "failed getting soc_min_rev, err : %d\n",
+ msm_host->soc_min_rev);
+ return msm_host->soc_min_rev;
+}
+#else
+/* Reads the SoC version */
+static int sdhci_msm_get_socrev(struct device *dev,
+ struct sdhci_msm_host *msm_host)
+{
+ return 0;
+}
+#endif
+
/* Parse platform data */
static
struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev,
@@ -2062,6 +2122,13 @@
if (!of_property_read_u32(np, "qcom,ddr-config", &pdata->ddr_config))
pdata->rclk_wa = true;
+ /*
+ * rclk_wa is not required if soc version is mentioned and
+ * is not base version.
+ */
+ if (msm_host->soc_min_rev != 0)
+ pdata->rclk_wa = false;
+
return pdata;
out:
return NULL;
@@ -4170,11 +4237,10 @@
group->latency = PM_QOS_DEFAULT_VALUE;
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",
+ pr_info("%s (): voted for group #%d (mask=0x%lx) latency=%d\n",
__func__, i,
group->req.cpus_affine.bits[0],
- group->latency,
- &latency[i].latency[SDHCI_PERFORMANCE_MODE]);
+ group->latency);
}
msm_host->pm_qos_prev_cpu = -1;
msm_host->pm_qos_group_enable = true;
@@ -4530,6 +4596,12 @@
msm_host->mmc = host->mmc;
msm_host->pdev = pdev;
+ ret = sdhci_msm_get_socrev(&pdev->dev, msm_host);
+ if (ret == -EPROBE_DEFER) {
+ dev_err(&pdev->dev, "SoC version rd: fail: defer for now\n");
+ goto pltfm_free;
+ }
+
/* get the ice device vops if present */
ret = sdhci_msm_ice_get_dev(host);
if (ret == -EPROBE_DEFER) {
@@ -4735,8 +4807,6 @@
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));
}
/*
diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h
index 7c737cc..9c2442d 100644
--- a/drivers/mmc/host/sdhci-msm.h
+++ b/drivers/mmc/host/sdhci-msm.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -255,6 +255,7 @@
bool core_3_0v_support;
bool pltfm_init_done;
struct sdhci_msm_regs_restore regs_restore;
+ int soc_min_rev;
};
extern char *saved_command_line;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 566be69..b674b38 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -3068,13 +3068,13 @@
struct sdhci_adma2_64_desc *dma_desc = desc;
if (host->flags & SDHCI_USE_64_BIT_DMA)
- DBG("%s: %p: DMA 0x%08x%08x, LEN 0x%04x, Attr=0x%02x\n",
+ DBG("%s: %pK: DMA 0x%08x%08x, LEN 0x%04x,Attr=0x%02x\n",
name, desc, le32_to_cpu(dma_desc->addr_hi),
le32_to_cpu(dma_desc->addr_lo),
le16_to_cpu(dma_desc->len),
le16_to_cpu(dma_desc->cmd));
else
- DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n",
+ DBG("%s: %pK: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n",
name, desc, le32_to_cpu(dma_desc->addr_lo),
le16_to_cpu(dma_desc->len),
le16_to_cpu(dma_desc->cmd));
diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c
index de31514..d38527e 100644
--- a/drivers/mtd/nand/denali_pci.c
+++ b/drivers/mtd/nand/denali_pci.c
@@ -119,3 +119,7 @@
};
module_pci_driver(denali_pci_driver);
+
+MODULE_DESCRIPTION("PCI driver for Denali NAND controller");
+MODULE_AUTHOR("Intel Corporation and its suppliers");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index b121bf4..3b8911c 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -950,6 +950,7 @@
switch (command) {
case NAND_CMD_READ0:
+ case NAND_CMD_READOOB:
case NAND_CMD_PAGEPROG:
info->use_ecc = 1;
break;
diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c
index eea9aea..5d50123 100644
--- a/drivers/net/can/usb/gs_usb.c
+++ b/drivers/net/can/usb/gs_usb.c
@@ -449,7 +449,7 @@
dev_err(netdev->dev.parent, "Couldn't set bittimings (err=%d)",
rc);
- return rc;
+ return (rc > 0) ? 0 : rc;
}
static void gs_usb_xmit_callback(struct urb *urb)
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
index 3047325..7f5ec40 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c
@@ -184,7 +184,7 @@
void *cmd_head = pcan_usb_fd_cmd_buffer(dev);
int err = 0;
u8 *packet_ptr;
- int i, n = 1, packet_len;
+ int packet_len;
ptrdiff_t cmd_len;
/* usb device unregistered? */
@@ -201,17 +201,13 @@
}
packet_ptr = cmd_head;
+ packet_len = cmd_len;
/* firmware is not able to re-assemble 512 bytes buffer in full-speed */
- if ((dev->udev->speed != USB_SPEED_HIGH) &&
- (cmd_len > PCAN_UFD_LOSPD_PKT_SIZE)) {
- packet_len = PCAN_UFD_LOSPD_PKT_SIZE;
- n += cmd_len / packet_len;
- } else {
- packet_len = cmd_len;
- }
+ if (unlikely(dev->udev->speed != USB_SPEED_HIGH))
+ packet_len = min(packet_len, PCAN_UFD_LOSPD_PKT_SIZE);
- for (i = 0; i < n; i++) {
+ do {
err = usb_bulk_msg(dev->udev,
usb_sndbulkpipe(dev->udev,
PCAN_USBPRO_EP_CMDOUT),
@@ -224,7 +220,12 @@
}
packet_ptr += packet_len;
- }
+ cmd_len -= packet_len;
+
+ if (cmd_len < PCAN_UFD_LOSPD_PKT_SIZE)
+ packet_len = cmd_len;
+
+ } while (packet_len > 0);
return err;
}
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
index a7e04ff..cde4b96 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
@@ -1843,8 +1843,8 @@
/* Read A2 portion of the EEPROM */
if (length) {
start -= ETH_MODULE_SFF_8436_LEN;
- bnxt_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A2, 1, start,
- length, data);
+ rc = bnxt_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A2, 1,
+ start, length, data);
}
return rc;
}
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index edae2dc..bb22d32 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -14226,7 +14226,9 @@
/* Reset PHY, otherwise the read DMA engine will be in a mode that
* breaks all requests to 256 bytes.
*/
- if (tg3_asic_rev(tp) == ASIC_REV_57766)
+ if (tg3_asic_rev(tp) == ASIC_REV_57766 ||
+ tg3_asic_rev(tp) == ASIC_REV_5717 ||
+ tg3_asic_rev(tp) == ASIC_REV_5719)
reset_phy = true;
err = tg3_restart_hw(tp, reset_phy);
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index 1644896..b2eeecb 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -4733,6 +4733,15 @@
be_schedule_worker(adapter);
+ /*
+ * The IF was destroyed and re-created. We need to clear
+ * all promiscuous flags valid for the destroyed IF.
+ * Without this promisc mode is not restored during
+ * be_open() because the driver thinks that it is
+ * already enabled in HW.
+ */
+ adapter->if_flags &= ~BE_IF_FLAGS_ALL_PROMISCUOUS;
+
if (netif_running(netdev))
status = be_open(netdev);
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 849b871..9170918 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -172,10 +172,12 @@
#endif /* CONFIG_M5272 */
/* The FEC stores dest/src/type/vlan, data, and checksum for receive packets.
+ *
+ * 2048 byte skbufs are allocated. However, alignment requirements
+ * varies between FEC variants. Worst case is 64, so round down by 64.
*/
-#define PKT_MAXBUF_SIZE 1522
+#define PKT_MAXBUF_SIZE (round_down(2048 - 64, 64))
#define PKT_MINBUF_SIZE 64
-#define PKT_MAXBLR_SIZE 1536
/* FEC receive acceleration */
#define FEC_RACC_IPDIS (1 << 1)
@@ -813,6 +815,12 @@
for (i = 0; i < txq->bd.ring_size; i++) {
/* Initialize the BD for every fragment in the page. */
bdp->cbd_sc = cpu_to_fec16(0);
+ if (bdp->cbd_bufaddr &&
+ !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr)))
+ dma_unmap_single(&fep->pdev->dev,
+ fec32_to_cpu(bdp->cbd_bufaddr),
+ fec16_to_cpu(bdp->cbd_datlen),
+ DMA_TO_DEVICE);
if (txq->tx_skbuff[i]) {
dev_kfree_skb_any(txq->tx_skbuff[i]);
txq->tx_skbuff[i] = NULL;
@@ -847,7 +855,7 @@
for (i = 0; i < fep->num_rx_queues; i++) {
rxq = fep->rx_queue[i];
writel(rxq->bd.dma, fep->hwp + FEC_R_DES_START(i));
- writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i));
+ writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i));
/* enable DMA1/2 */
if (i)
diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c
index f3aaca7..8a48656 100644
--- a/drivers/net/ethernet/intel/e1000e/ich8lan.c
+++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c
@@ -1364,6 +1364,9 @@
* Checks to see of the link status of the hardware has changed. If a
* change in link status has been detected, then we read the PHY registers
* to get the current speed/duplex if link exists.
+ *
+ * Returns a negative error code (-E1000_ERR_*) or 0 (link down) or 1 (link
+ * up).
**/
static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
{
@@ -1379,7 +1382,7 @@
* Change or Rx Sequence Error interrupt.
*/
if (!mac->get_link_status)
- return 0;
+ return 1;
/* First we want to see if the MII Status Register reports
* link. If so, then we want to get the current speed/duplex
@@ -1611,10 +1614,12 @@
* different link partner.
*/
ret_val = e1000e_config_fc_after_link_up(hw);
- if (ret_val)
+ if (ret_val) {
e_dbg("Error configuring flow control\n");
+ return ret_val;
+ }
- return ret_val;
+ return 1;
}
static s32 e1000_get_variants_ich8lan(struct e1000_adapter *adapter)
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index ca54f76..3a61491 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -3273,7 +3273,7 @@
int igb_close(struct net_device *netdev)
{
- if (netif_device_present(netdev))
+ if (netif_device_present(netdev) || netdev->dismantle)
return __igb_close(netdev, false);
return 0;
}
diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c
index a0d1b08..7aeb7fe 100644
--- a/drivers/net/ethernet/marvell/mvmdio.c
+++ b/drivers/net/ethernet/marvell/mvmdio.c
@@ -232,7 +232,8 @@
dev->regs + MVMDIO_ERR_INT_MASK);
} else if (dev->err_interrupt == -EPROBE_DEFER) {
- return -EPROBE_DEFER;
+ ret = -EPROBE_DEFER;
+ goto out_mdio;
}
mutex_init(&dev->lock);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
index f7fabec..4c3f1cb 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
@@ -367,7 +367,7 @@
case MLX5_CMD_OP_QUERY_VPORT_COUNTER:
case MLX5_CMD_OP_ALLOC_Q_COUNTER:
case MLX5_CMD_OP_QUERY_Q_COUNTER:
- case MLX5_CMD_OP_SET_RATE_LIMIT:
+ case MLX5_CMD_OP_SET_PP_RATE_LIMIT:
case MLX5_CMD_OP_QUERY_RATE_LIMIT:
case MLX5_CMD_OP_ALLOC_PD:
case MLX5_CMD_OP_ALLOC_UAR:
@@ -502,7 +502,7 @@
MLX5_COMMAND_STR_CASE(ALLOC_Q_COUNTER);
MLX5_COMMAND_STR_CASE(DEALLOC_Q_COUNTER);
MLX5_COMMAND_STR_CASE(QUERY_Q_COUNTER);
- MLX5_COMMAND_STR_CASE(SET_RATE_LIMIT);
+ MLX5_COMMAND_STR_CASE(SET_PP_RATE_LIMIT);
MLX5_COMMAND_STR_CASE(QUERY_RATE_LIMIT);
MLX5_COMMAND_STR_CASE(ALLOC_PD);
MLX5_COMMAND_STR_CASE(DEALLOC_PD);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 9d37229..38981db 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -3038,6 +3038,7 @@
struct sk_buff *skb,
netdev_features_t features)
{
+ unsigned int offset = 0;
struct udphdr *udph;
u16 proto;
u16 port = 0;
@@ -3047,7 +3048,7 @@
proto = ip_hdr(skb)->protocol;
break;
case htons(ETH_P_IPV6):
- proto = ipv6_hdr(skb)->nexthdr;
+ proto = ipv6_find_hdr(skb, &offset, -1, NULL, NULL);
break;
default:
goto out;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qp.c b/drivers/net/ethernet/mellanox/mlx5/core/qp.c
index d0a4005..9346f39 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/qp.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/qp.c
@@ -303,8 +303,8 @@
err_cmd:
memset(din, 0, sizeof(din));
memset(dout, 0, sizeof(dout));
- MLX5_SET(destroy_qp_in, in, opcode, MLX5_CMD_OP_DESTROY_QP);
- MLX5_SET(destroy_qp_in, in, qpn, qp->qpn);
+ MLX5_SET(destroy_qp_in, din, opcode, MLX5_CMD_OP_DESTROY_QP);
+ MLX5_SET(destroy_qp_in, din, qpn, qp->qpn);
mlx5_cmd_exec(dev, din, sizeof(din), dout, sizeof(dout));
return err;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/rl.c b/drivers/net/ethernet/mellanox/mlx5/core/rl.c
index 104902a..2be9ec5 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/rl.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/rl.c
@@ -60,16 +60,16 @@
return ret_entry;
}
-static int mlx5_set_rate_limit_cmd(struct mlx5_core_dev *dev,
+static int mlx5_set_pp_rate_limit_cmd(struct mlx5_core_dev *dev,
u32 rate, u16 index)
{
- u32 in[MLX5_ST_SZ_DW(set_rate_limit_in)] = {0};
- u32 out[MLX5_ST_SZ_DW(set_rate_limit_out)] = {0};
+ u32 in[MLX5_ST_SZ_DW(set_pp_rate_limit_in)] = {0};
+ u32 out[MLX5_ST_SZ_DW(set_pp_rate_limit_out)] = {0};
- MLX5_SET(set_rate_limit_in, in, opcode,
- MLX5_CMD_OP_SET_RATE_LIMIT);
- MLX5_SET(set_rate_limit_in, in, rate_limit_index, index);
- MLX5_SET(set_rate_limit_in, in, rate_limit, rate);
+ MLX5_SET(set_pp_rate_limit_in, in, opcode,
+ MLX5_CMD_OP_SET_PP_RATE_LIMIT);
+ MLX5_SET(set_pp_rate_limit_in, in, rate_limit_index, index);
+ MLX5_SET(set_pp_rate_limit_in, in, rate_limit, rate);
return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
}
@@ -108,7 +108,7 @@
entry->refcount++;
} else {
/* new rate limit */
- err = mlx5_set_rate_limit_cmd(dev, rate, entry->index);
+ err = mlx5_set_pp_rate_limit_cmd(dev, rate, entry->index);
if (err) {
mlx5_core_err(dev, "Failed configuring rate: %u (%d)\n",
rate, err);
@@ -144,7 +144,7 @@
entry->refcount--;
if (!entry->refcount) {
/* need to remove rate */
- mlx5_set_rate_limit_cmd(dev, 0, entry->index);
+ mlx5_set_pp_rate_limit_cmd(dev, 0, entry->index);
entry->rate = 0;
}
@@ -197,8 +197,8 @@
/* Clear all configured rates */
for (i = 0; i < table->max_size; i++)
if (table->rl_entry[i].rate)
- mlx5_set_rate_limit_cmd(dev, 0,
- table->rl_entry[i].index);
+ mlx5_set_pp_rate_limit_cmd(dev, 0,
+ table->rl_entry[i].index);
kfree(dev->priv.rl_table.rl_entry);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c
index 07a9ba6..2f74953 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c
@@ -71,9 +71,9 @@
struct mlx5e_vxlan_db *vxlan_db = &priv->vxlan;
struct mlx5e_vxlan *vxlan;
- spin_lock(&vxlan_db->lock);
+ spin_lock_bh(&vxlan_db->lock);
vxlan = radix_tree_lookup(&vxlan_db->tree, port);
- spin_unlock(&vxlan_db->lock);
+ spin_unlock_bh(&vxlan_db->lock);
return vxlan;
}
@@ -88,8 +88,12 @@
struct mlx5e_vxlan *vxlan;
int err;
- if (mlx5e_vxlan_lookup_port(priv, port))
+ mutex_lock(&priv->state_lock);
+ vxlan = mlx5e_vxlan_lookup_port(priv, port);
+ if (vxlan) {
+ atomic_inc(&vxlan->refcount);
goto free_work;
+ }
if (mlx5e_vxlan_core_add_port_cmd(priv->mdev, port))
goto free_work;
@@ -99,10 +103,11 @@
goto err_delete_port;
vxlan->udp_port = port;
+ atomic_set(&vxlan->refcount, 1);
- spin_lock_irq(&vxlan_db->lock);
+ spin_lock_bh(&vxlan_db->lock);
err = radix_tree_insert(&vxlan_db->tree, vxlan->udp_port, vxlan);
- spin_unlock_irq(&vxlan_db->lock);
+ spin_unlock_bh(&vxlan_db->lock);
if (err)
goto err_free;
@@ -113,35 +118,39 @@
err_delete_port:
mlx5e_vxlan_core_del_port_cmd(priv->mdev, port);
free_work:
+ mutex_unlock(&priv->state_lock);
kfree(vxlan_work);
}
-static void __mlx5e_vxlan_core_del_port(struct mlx5e_priv *priv, u16 port)
-{
- struct mlx5e_vxlan_db *vxlan_db = &priv->vxlan;
- struct mlx5e_vxlan *vxlan;
-
- spin_lock_irq(&vxlan_db->lock);
- vxlan = radix_tree_delete(&vxlan_db->tree, port);
- spin_unlock_irq(&vxlan_db->lock);
-
- if (!vxlan)
- return;
-
- mlx5e_vxlan_core_del_port_cmd(priv->mdev, vxlan->udp_port);
-
- kfree(vxlan);
-}
-
static void mlx5e_vxlan_del_port(struct work_struct *work)
{
struct mlx5e_vxlan_work *vxlan_work =
container_of(work, struct mlx5e_vxlan_work, work);
- struct mlx5e_priv *priv = vxlan_work->priv;
+ struct mlx5e_priv *priv = vxlan_work->priv;
+ struct mlx5e_vxlan_db *vxlan_db = &priv->vxlan;
u16 port = vxlan_work->port;
+ struct mlx5e_vxlan *vxlan;
+ bool remove = false;
- __mlx5e_vxlan_core_del_port(priv, port);
+ mutex_lock(&priv->state_lock);
+ spin_lock_bh(&vxlan_db->lock);
+ vxlan = radix_tree_lookup(&vxlan_db->tree, port);
+ if (!vxlan)
+ goto out_unlock;
+ if (atomic_dec_and_test(&vxlan->refcount)) {
+ radix_tree_delete(&vxlan_db->tree, port);
+ remove = true;
+ }
+
+out_unlock:
+ spin_unlock_bh(&vxlan_db->lock);
+
+ if (remove) {
+ mlx5e_vxlan_core_del_port_cmd(priv->mdev, port);
+ kfree(vxlan);
+ }
+ mutex_unlock(&priv->state_lock);
kfree(vxlan_work);
}
@@ -171,12 +180,11 @@
struct mlx5e_vxlan *vxlan;
unsigned int port = 0;
- spin_lock_irq(&vxlan_db->lock);
+ /* Lockless since we are the only radix-tree consumers, wq is disabled */
while (radix_tree_gang_lookup(&vxlan_db->tree, (void **)&vxlan, port, 1)) {
port = vxlan->udp_port;
- spin_unlock_irq(&vxlan_db->lock);
- __mlx5e_vxlan_core_del_port(priv, (u16)port);
- spin_lock_irq(&vxlan_db->lock);
+ radix_tree_delete(&vxlan_db->tree, port);
+ mlx5e_vxlan_core_del_port_cmd(priv->mdev, port);
+ kfree(vxlan);
}
- spin_unlock_irq(&vxlan_db->lock);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h
index 5def12c..5ef6ae7 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h
@@ -36,6 +36,7 @@
#include "en.h"
struct mlx5e_vxlan {
+ atomic_t refcount;
u16 udp_port;
};
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index 9e31a33..1655601 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -765,11 +765,8 @@
dipn = htonl(dip);
dev = mlxsw_sp->rifs[rif]->dev;
n = neigh_lookup(&arp_tbl, &dipn, dev);
- if (!n) {
- netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
- &dip);
+ if (!n)
return;
- }
netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
neigh_event_send(n, NULL);
@@ -1328,9 +1325,9 @@
static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
bool removing)
{
- if (!removing && !nh->should_offload)
+ if (!removing)
nh->should_offload = 1;
- else if (removing && nh->offloaded)
+ else
nh->should_offload = 0;
nh->update = 1;
}
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 2c4350a..298b74e 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -2222,19 +2222,14 @@
void __iomem *ioaddr = tp->mmio_addr;
dma_addr_t paddr = tp->counters_phys_addr;
u32 cmd;
- bool ret;
RTL_W32(CounterAddrHigh, (u64)paddr >> 32);
+ RTL_R32(CounterAddrHigh);
cmd = (u64)paddr & DMA_BIT_MASK(32);
RTL_W32(CounterAddrLow, cmd);
RTL_W32(CounterAddrLow, cmd | counter_cmd);
- ret = rtl_udelay_loop_wait_low(tp, &rtl_counters_cond, 10, 1000);
-
- RTL_W32(CounterAddrLow, 0);
- RTL_W32(CounterAddrHigh, 0);
-
- return ret;
+ return rtl_udelay_loop_wait_low(tp, &rtl_counters_cond, 10, 1000);
}
static bool rtl8169_reset_counters(struct net_device *dev)
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index 2140ded..b6816ae 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -3087,18 +3087,37 @@
/* ioremap the TSU registers */
if (mdp->cd->tsu) {
struct resource *rtsu;
+
rtsu = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- mdp->tsu_addr = devm_ioremap_resource(&pdev->dev, rtsu);
- if (IS_ERR(mdp->tsu_addr)) {
- ret = PTR_ERR(mdp->tsu_addr);
+ if (!rtsu) {
+ dev_err(&pdev->dev, "no TSU resource\n");
+ ret = -ENODEV;
+ goto out_release;
+ }
+ /* We can only request the TSU region for the first port
+ * of the two sharing this TSU for the probe to succeed...
+ */
+ if (devno % 2 == 0 &&
+ !devm_request_mem_region(&pdev->dev, rtsu->start,
+ resource_size(rtsu),
+ dev_name(&pdev->dev))) {
+ dev_err(&pdev->dev, "can't request TSU resource.\n");
+ ret = -EBUSY;
+ goto out_release;
+ }
+ mdp->tsu_addr = devm_ioremap(&pdev->dev, rtsu->start,
+ resource_size(rtsu));
+ if (!mdp->tsu_addr) {
+ dev_err(&pdev->dev, "TSU region ioremap() failed.\n");
+ ret = -ENOMEM;
goto out_release;
}
mdp->port = devno % 2;
ndev->features = NETIF_F_HW_VLAN_CTAG_FILTER;
}
- /* initialize first or needed device */
- if (!devno || pd->needs_init) {
+ /* Need to init only the first port of the two sharing a TSU */
+ if (devno % 2 == 0) {
if (mdp->cd->chip_reset)
mdp->cd->chip_reset(ndev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index adf61a7..98bbb91 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -280,8 +280,14 @@
bool stmmac_eee_init(struct stmmac_priv *priv)
{
unsigned long flags;
+ int interface = priv->plat->interface;
bool ret = false;
+ if ((interface != PHY_INTERFACE_MODE_MII) &&
+ (interface != PHY_INTERFACE_MODE_GMII) &&
+ !phy_interface_mode_is_rgmii(interface))
+ goto out;
+
/* Using PCS we cannot dial with the phy registers at this stage
* so we do not support extra feature like EEE.
*/
diff --git a/drivers/net/ethernet/xilinx/Kconfig b/drivers/net/ethernet/xilinx/Kconfig
index 6d68c8a..da4ec57 100644
--- a/drivers/net/ethernet/xilinx/Kconfig
+++ b/drivers/net/ethernet/xilinx/Kconfig
@@ -34,6 +34,7 @@
config XILINX_LL_TEMAC
tristate "Xilinx LL TEMAC (LocalLink Tri-mode Ethernet MAC) driver"
depends on (PPC || MICROBLAZE)
+ depends on !64BIT || BROKEN
select PHYLIB
---help---
This driver supports the Xilinx 10/100/1000 LocalLink TEMAC
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index fbf5945..2032a6d 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -624,6 +624,7 @@
phydev->link = 0;
if (phydev->drv->config_intr && phy_interrupt_is_valid(phydev))
phydev->drv->config_intr(phydev);
+ return genphy_config_aneg(phydev);
}
return 0;
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index b883af9..fc4c2cc 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -1002,17 +1002,18 @@
if (!ifname_is_set)
snprintf(ppp->dev->name, IFNAMSIZ, "ppp%i", ppp->file.index);
+ mutex_unlock(&pn->all_ppp_mutex);
+
ret = register_netdevice(ppp->dev);
if (ret < 0)
goto err_unit;
atomic_inc(&ppp_unit_count);
- mutex_unlock(&pn->all_ppp_mutex);
-
return 0;
err_unit:
+ mutex_lock(&pn->all_ppp_mutex);
unit_put(&pn->units_idr, ppp->file.index);
err:
mutex_unlock(&pn->all_ppp_mutex);
diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
index 4ddae81..dc36c2e 100644
--- a/drivers/net/ppp/pppoe.c
+++ b/drivers/net/ppp/pppoe.c
@@ -842,6 +842,7 @@
struct pppoe_hdr *ph;
struct net_device *dev;
char *start;
+ int hlen;
lock_sock(sk);
if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) {
@@ -860,16 +861,16 @@
if (total_len > (dev->mtu + dev->hard_header_len))
goto end;
-
- skb = sock_wmalloc(sk, total_len + dev->hard_header_len + 32,
- 0, GFP_KERNEL);
+ hlen = LL_RESERVED_SPACE(dev);
+ skb = sock_wmalloc(sk, hlen + sizeof(*ph) + total_len +
+ dev->needed_tailroom, 0, GFP_KERNEL);
if (!skb) {
error = -ENOMEM;
goto end;
}
/* Reserve space for headers. */
- skb_reserve(skb, dev->hard_header_len);
+ skb_reserve(skb, hlen);
skb_reset_network_header(skb);
skb->dev = dev;
@@ -930,7 +931,7 @@
/* Copy the data if there is no space for the header or if it's
* read-only.
*/
- if (skb_cow_head(skb, sizeof(*ph) + dev->hard_header_len))
+ if (skb_cow_head(skb, LL_RESERVED_SPACE(dev) + sizeof(*ph)))
goto abort;
__skb_push(skb, sizeof(*ph));
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 6f9fc27..9babe04 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -525,6 +525,14 @@
skb_queue_purge(&tfile->sk.sk_error_queue);
}
+static void tun_cleanup_tx_array(struct tun_file *tfile)
+{
+ if (tfile->tx_array.ring.queue) {
+ skb_array_cleanup(&tfile->tx_array);
+ memset(&tfile->tx_array, 0, sizeof(tfile->tx_array));
+ }
+}
+
static void __tun_detach(struct tun_file *tfile, bool clean)
{
struct tun_file *ntfile;
@@ -566,8 +574,7 @@
tun->dev->reg_state == NETREG_REGISTERED)
unregister_netdevice(tun->dev);
}
- if (tun)
- skb_array_cleanup(&tfile->tx_array);
+ tun_cleanup_tx_array(tfile);
sock_put(&tfile->sk);
}
}
@@ -606,11 +613,13 @@
/* Drop read queue */
tun_queue_purge(tfile);
sock_put(&tfile->sk);
+ tun_cleanup_tx_array(tfile);
}
list_for_each_entry_safe(tfile, tmp, &tun->disabled, next) {
tun_enable_queue(tfile);
tun_queue_purge(tfile);
sock_put(&tfile->sk);
+ tun_cleanup_tx_array(tfile);
}
BUG_ON(tun->numdisabled != 0);
@@ -2373,6 +2382,8 @@
sock_set_flag(&tfile->sk, SOCK_ZEROCOPY);
+ memset(&tfile->tx_array, 0, sizeof(tfile->tx_array));
+
return 0;
}
diff --git a/drivers/net/usb/cx82310_eth.c b/drivers/net/usb/cx82310_eth.c
index e221bfc..947bea8 100644
--- a/drivers/net/usb/cx82310_eth.c
+++ b/drivers/net/usb/cx82310_eth.c
@@ -293,12 +293,9 @@
{
int len = skb->len;
- if (skb_headroom(skb) < 2) {
- struct sk_buff *skb2 = skb_copy_expand(skb, 2, 0, flags);
+ if (skb_cow_head(skb, 2)) {
dev_kfree_skb_any(skb);
- skb = skb2;
- if (!skb)
- return NULL;
+ return NULL;
}
skb_push(skb, 2);
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index f33460c..c53385a 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -2197,6 +2197,7 @@
buf = DEFAULT_BURST_CAP_SIZE / FS_USB_PKT_SIZE;
dev->rx_urb_size = DEFAULT_BURST_CAP_SIZE;
dev->rx_qlen = 4;
+ dev->tx_qlen = 4;
}
ret = lan78xx_write_reg(dev, BURST_CAP, buf);
@@ -2419,14 +2420,9 @@
{
u32 tx_cmd_a, tx_cmd_b;
- if (skb_headroom(skb) < TX_OVERHEAD) {
- struct sk_buff *skb2;
-
- skb2 = skb_copy_expand(skb, TX_OVERHEAD, 0, flags);
+ if (skb_cow_head(skb, TX_OVERHEAD)) {
dev_kfree_skb_any(skb);
- skb = skb2;
- if (!skb)
- return NULL;
+ return NULL;
}
if (lan78xx_linearize(skb) < 0)
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 105fbfb..db65d9a 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -907,6 +907,7 @@
{QMI_FIXED_INTF(0x1199, 0x9079, 10)}, /* Sierra Wireless EM74xx */
{QMI_FIXED_INTF(0x1199, 0x907b, 8)}, /* Sierra Wireless EM74xx */
{QMI_FIXED_INTF(0x1199, 0x907b, 10)}, /* Sierra Wireless EM74xx */
+ {QMI_FIXED_INTF(0x1199, 0x9091, 8)}, /* Sierra Wireless EM7565 */
{QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */
{QMI_FIXED_INTF(0x1bbb, 0x0203, 2)}, /* Alcatel L800MA */
{QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */
diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c
index 9af9799..4cb9b11 100644
--- a/drivers/net/usb/smsc75xx.c
+++ b/drivers/net/usb/smsc75xx.c
@@ -2205,13 +2205,9 @@
{
u32 tx_cmd_a, tx_cmd_b;
- if (skb_headroom(skb) < SMSC75XX_TX_OVERHEAD) {
- struct sk_buff *skb2 =
- skb_copy_expand(skb, SMSC75XX_TX_OVERHEAD, 0, flags);
+ if (skb_cow_head(skb, SMSC75XX_TX_OVERHEAD)) {
dev_kfree_skb_any(skb);
- skb = skb2;
- if (!skb)
- return NULL;
+ return NULL;
}
tx_cmd_a = (u32)(skb->len & TX_CMD_A_LEN) | TX_CMD_A_FCS;
diff --git a/drivers/net/usb/sr9700.c b/drivers/net/usb/sr9700.c
index 4a1e9c4..aadfe1d 100644
--- a/drivers/net/usb/sr9700.c
+++ b/drivers/net/usb/sr9700.c
@@ -456,14 +456,9 @@
len = skb->len;
- if (skb_headroom(skb) < SR_TX_OVERHEAD) {
- struct sk_buff *skb2;
-
- skb2 = skb_copy_expand(skb, SR_TX_OVERHEAD, 0, flags);
+ if (skb_cow_head(skb, SR_TX_OVERHEAD)) {
dev_kfree_skb_any(skb);
- skb = skb2;
- if (!skb)
- return NULL;
+ return NULL;
}
__skb_push(skb, SR_TX_OVERHEAD);
diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c
index ef83ae3..4afba17 100644
--- a/drivers/net/vmxnet3/vmxnet3_drv.c
+++ b/drivers/net/vmxnet3/vmxnet3_drv.c
@@ -1616,7 +1616,6 @@
rq->rx_ring[i].basePA);
rq->rx_ring[i].base = NULL;
}
- rq->buf_info[i] = NULL;
}
if (rq->data_ring.base) {
@@ -1638,6 +1637,7 @@
(rq->rx_ring[0].size + rq->rx_ring[1].size);
dma_free_coherent(&adapter->pdev->dev, sz, rq->buf_info[0],
rq->buf_info_pa);
+ rq->buf_info[0] = rq->buf_info[1] = NULL;
}
}
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 0b4c156..ba1fe61 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -548,6 +548,11 @@
return IEEE80211_TKIP_IV_LEN;
case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
return IEEE80211_CCMP_HDR_LEN;
+ case HTT_RX_MPDU_ENCRYPT_AES_CCM256_WPA2:
+ return IEEE80211_CCMP_256_HDR_LEN;
+ case HTT_RX_MPDU_ENCRYPT_AES_GCMP_WPA2:
+ case HTT_RX_MPDU_ENCRYPT_AES_GCMP256_WPA2:
+ return IEEE80211_GCMP_HDR_LEN;
case HTT_RX_MPDU_ENCRYPT_WEP128:
case HTT_RX_MPDU_ENCRYPT_WAPI:
break;
@@ -573,6 +578,11 @@
return IEEE80211_TKIP_ICV_LEN;
case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
return IEEE80211_CCMP_MIC_LEN;
+ case HTT_RX_MPDU_ENCRYPT_AES_CCM256_WPA2:
+ return IEEE80211_CCMP_256_MIC_LEN;
+ case HTT_RX_MPDU_ENCRYPT_AES_GCMP_WPA2:
+ case HTT_RX_MPDU_ENCRYPT_AES_GCMP256_WPA2:
+ return IEEE80211_GCMP_MIC_LEN;
case HTT_RX_MPDU_ENCRYPT_WEP128:
case HTT_RX_MPDU_ENCRYPT_WAPI:
break;
@@ -1024,9 +1034,21 @@
hdr = (void *)msdu->data;
/* Tail */
- if (status->flag & RX_FLAG_IV_STRIPPED)
+ if (status->flag & RX_FLAG_IV_STRIPPED) {
skb_trim(msdu, msdu->len -
ath10k_htt_rx_crypto_tail_len(ar, enctype));
+ } else {
+ /* MIC */
+ if ((status->flag & RX_FLAG_MIC_STRIPPED) &&
+ enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2)
+ skb_trim(msdu, msdu->len - 8);
+
+ /* ICV */
+ if (status->flag & RX_FLAG_ICV_STRIPPED &&
+ enctype != HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2)
+ skb_trim(msdu, msdu->len -
+ ath10k_htt_rx_crypto_tail_len(ar, enctype));
+ }
/* MMIC */
if ((status->flag & RX_FLAG_MMIC_STRIPPED) &&
@@ -1048,7 +1070,8 @@
static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
struct sk_buff *msdu,
struct ieee80211_rx_status *status,
- const u8 first_hdr[64])
+ const u8 first_hdr[64],
+ enum htt_rx_mpdu_encrypt_type enctype)
{
struct ieee80211_hdr *hdr;
struct htt_rx_desc *rxd;
@@ -1056,6 +1079,7 @@
u8 da[ETH_ALEN];
u8 sa[ETH_ALEN];
int l3_pad_bytes;
+ int bytes_aligned = ar->hw_params.decap_align_bytes;
/* Delivered decapped frame:
* [nwifi 802.11 header] <-- replaced with 802.11 hdr
@@ -1084,6 +1108,14 @@
/* push original 802.11 header */
hdr = (struct ieee80211_hdr *)first_hdr;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
+
+ if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
+ memcpy(skb_push(msdu,
+ ath10k_htt_rx_crypto_param_len(ar, enctype)),
+ (void *)hdr + round_up(hdr_len, bytes_aligned),
+ ath10k_htt_rx_crypto_param_len(ar, enctype));
+ }
+
memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
/* original 802.11 header has a different DA and in
@@ -1144,6 +1176,7 @@
u8 sa[ETH_ALEN];
int l3_pad_bytes;
struct htt_rx_desc *rxd;
+ int bytes_aligned = ar->hw_params.decap_align_bytes;
/* Delivered decapped frame:
* [eth header] <-- replaced with 802.11 hdr & rfc1042/llc
@@ -1172,6 +1205,14 @@
/* push original 802.11 header */
hdr = (struct ieee80211_hdr *)first_hdr;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
+
+ if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
+ memcpy(skb_push(msdu,
+ ath10k_htt_rx_crypto_param_len(ar, enctype)),
+ (void *)hdr + round_up(hdr_len, bytes_aligned),
+ ath10k_htt_rx_crypto_param_len(ar, enctype));
+ }
+
memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
/* original 802.11 header has a different DA and in
@@ -1185,12 +1226,14 @@
static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar,
struct sk_buff *msdu,
struct ieee80211_rx_status *status,
- const u8 first_hdr[64])
+ const u8 first_hdr[64],
+ enum htt_rx_mpdu_encrypt_type enctype)
{
struct ieee80211_hdr *hdr;
size_t hdr_len;
int l3_pad_bytes;
struct htt_rx_desc *rxd;
+ int bytes_aligned = ar->hw_params.decap_align_bytes;
/* Delivered decapped frame:
* [amsdu header] <-- replaced with 802.11 hdr
@@ -1206,6 +1249,14 @@
hdr = (struct ieee80211_hdr *)first_hdr;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
+
+ if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
+ memcpy(skb_push(msdu,
+ ath10k_htt_rx_crypto_param_len(ar, enctype)),
+ (void *)hdr + round_up(hdr_len, bytes_aligned),
+ ath10k_htt_rx_crypto_param_len(ar, enctype));
+ }
+
memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
}
@@ -1240,13 +1291,15 @@
is_decrypted);
break;
case RX_MSDU_DECAP_NATIVE_WIFI:
- ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr);
+ ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr,
+ enctype);
break;
case RX_MSDU_DECAP_ETHERNET2_DIX:
ath10k_htt_rx_h_undecap_eth(ar, msdu, status, first_hdr, enctype);
break;
case RX_MSDU_DECAP_8023_SNAP_LLC:
- ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr);
+ ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr,
+ enctype);
break;
}
}
@@ -1289,7 +1342,8 @@
static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
struct sk_buff_head *amsdu,
- struct ieee80211_rx_status *status)
+ struct ieee80211_rx_status *status,
+ bool fill_crypt_header)
{
struct sk_buff *first;
struct sk_buff *last;
@@ -1299,7 +1353,6 @@
enum htt_rx_mpdu_encrypt_type enctype;
u8 first_hdr[64];
u8 *qos;
- size_t hdr_len;
bool has_fcs_err;
bool has_crypto_err;
bool has_tkip_err;
@@ -1324,15 +1377,17 @@
* decapped header. It'll be used for undecapping of each MSDU.
*/
hdr = (void *)rxd->rx_hdr_status;
- hdr_len = ieee80211_hdrlen(hdr->frame_control);
- memcpy(first_hdr, hdr, hdr_len);
+ memcpy(first_hdr, hdr, RX_HTT_HDR_STATUS_LEN);
/* Each A-MSDU subframe will use the original header as the base and be
* reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl.
*/
hdr = (void *)first_hdr;
- qos = ieee80211_get_qos_ctl(hdr);
- qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ qos = ieee80211_get_qos_ctl(hdr);
+ qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+ }
/* Some attention flags are valid only in the last MSDU. */
last = skb_peek_tail(amsdu);
@@ -1379,9 +1434,14 @@
status->flag |= RX_FLAG_DECRYPTED;
if (likely(!is_mgmt))
- status->flag |= RX_FLAG_IV_STRIPPED |
- RX_FLAG_MMIC_STRIPPED;
-}
+ status->flag |= RX_FLAG_MMIC_STRIPPED;
+
+ if (fill_crypt_header)
+ status->flag |= RX_FLAG_MIC_STRIPPED |
+ RX_FLAG_ICV_STRIPPED;
+ else
+ status->flag |= RX_FLAG_IV_STRIPPED;
+ }
skb_queue_walk(amsdu, msdu) {
ath10k_htt_rx_h_csum_offload(msdu);
@@ -1397,6 +1457,9 @@
if (is_mgmt)
continue;
+ if (fill_crypt_header)
+ continue;
+
hdr = (void *)msdu->data;
hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
}
@@ -1407,6 +1470,9 @@
struct ieee80211_rx_status *status)
{
struct sk_buff *msdu;
+ struct sk_buff *first_subframe;
+
+ first_subframe = skb_peek(amsdu);
while ((msdu = __skb_dequeue(amsdu))) {
/* Setup per-MSDU flags */
@@ -1415,6 +1481,13 @@
else
status->flag |= RX_FLAG_AMSDU_MORE;
+ if (msdu == first_subframe) {
+ first_subframe = NULL;
+ status->flag &= ~RX_FLAG_ALLOW_SAME_PN;
+ } else {
+ status->flag |= RX_FLAG_ALLOW_SAME_PN;
+ }
+
ath10k_process_rx(ar, status, msdu);
}
}
@@ -1557,7 +1630,7 @@
ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status, 0xffff);
ath10k_htt_rx_h_unchain(ar, &amsdu, ret > 0);
ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
- ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
+ ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true);
ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
return num_msdus;
@@ -1892,7 +1965,7 @@
num_msdus += skb_queue_len(&amsdu);
ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id);
ath10k_htt_rx_h_filter(ar, &amsdu, status);
- ath10k_htt_rx_h_mpdu(ar, &amsdu, status);
+ ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false);
ath10k_htt_rx_h_deliver(ar, &amsdu, status);
break;
case -EAGAIN:
diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h
index 034e7a5..e4878d0 100644
--- a/drivers/net/wireless/ath/ath10k/rx_desc.h
+++ b/drivers/net/wireless/ath/ath10k/rx_desc.h
@@ -239,6 +239,9 @@
HTT_RX_MPDU_ENCRYPT_WAPI = 5,
HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2 = 6,
HTT_RX_MPDU_ENCRYPT_NONE = 7,
+ HTT_RX_MPDU_ENCRYPT_AES_CCM256_WPA2 = 8,
+ HTT_RX_MPDU_ENCRYPT_AES_GCMP_WPA2 = 9,
+ HTT_RX_MPDU_ENCRYPT_AES_GCMP256_WPA2 = 10,
};
#define RX_MPDU_START_INFO0_PEER_IDX_MASK 0x000007ff
diff --git a/drivers/net/wireless/ath/wil6210/boot_loader.h b/drivers/net/wireless/ath/wil6210/boot_loader.h
index c131b5e..d32c1f4 100644
--- a/drivers/net/wireless/ath/wil6210/boot_loader.h
+++ b/drivers/net/wireless/ath/wil6210/boot_loader.h
@@ -1,4 +1,5 @@
/* Copyright (c) 2015 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -39,7 +40,8 @@
/* valid only for version 2 and above */
__le32 bl_assert_code; /* 0x880A58 BL Assert code */
__le32 bl_assert_blink; /* 0x880A5C BL Assert Branch */
- __le32 bl_reserved[22]; /* 0x880A60 - 0x880AB4 */
+ __le32 bl_shutdown_handshake; /* 0x880A60 BL cleaner shutdown */
+ __le32 bl_reserved[21]; /* 0x880A64 - 0x880AB4 */
__le32 bl_magic_number; /* 0x880AB8 BL Magic number */
} __packed;
@@ -58,4 +60,9 @@
u8 mac_address[6]; /* 0x880A4c BL mac address */
} __packed;
+/* bits for bl_shutdown_handshake */
+#define BL_SHUTDOWN_HS_GRTD BIT(0)
+#define BL_SHUTDOWN_HS_RTD BIT(1)
+#define BL_SHUTDOWN_HS_PROT_VER(x) WIL_GET_BITS(x, 28, 31)
+
#endif /* BOOT_LOADER_EXPORT_H_ */
diff --git a/drivers/net/wireless/ath/wil6210/fw.h b/drivers/net/wireless/ath/wil6210/fw.h
index 2f2b910..2c7b24f 100644
--- a/drivers/net/wireless/ath/wil6210/fw.h
+++ b/drivers/net/wireless/ath/wil6210/fw.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2014,2016 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -58,15 +59,30 @@
u8 data[0]; /* free-form data [data_size], see above */
} __packed;
+/* Comment header - common for all comment record types */
+struct wil_fw_record_comment_hdr {
+ __le32 magic;
+};
+
/* FW capabilities encoded inside a comment record */
#define WIL_FW_CAPABILITIES_MAGIC (0xabcddcba)
struct wil_fw_record_capabilities { /* type == wil_fw_type_comment */
/* identifies capabilities record */
- __le32 magic;
+ struct wil_fw_record_comment_hdr hdr;
/* capabilities (variable size), see enum wmi_fw_capability */
u8 capabilities[0];
};
+/* brd file info encoded inside a comment record */
+#define WIL_BRD_FILE_MAGIC (0xabcddcbb)
+struct wil_fw_record_brd_file { /* type == wil_fw_type_comment */
+ /* identifies brd file record */
+ struct wil_fw_record_comment_hdr hdr;
+ __le32 version;
+ __le32 base_addr;
+ __le32 max_size_bytes;
+} __packed;
+
/* perform action
* data_size = @head.size - offsetof(struct wil_fw_record_action, data)
*/
diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c
index 77d1902..914c010 100644
--- a/drivers/net/wireless/ath/wil6210/fw_inc.c
+++ b/drivers/net/wireless/ath/wil6210/fw_inc.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -128,14 +129,13 @@
}
static int
-fw_handle_comment(struct wil6210_priv *wil, const void *data,
- size_t size)
+fw_handle_capabilities(struct wil6210_priv *wil, const void *data,
+ size_t size)
{
const struct wil_fw_record_capabilities *rec = data;
size_t capa_size;
- if (size < sizeof(*rec) ||
- le32_to_cpu(rec->magic) != WIL_FW_CAPABILITIES_MAGIC) {
+ if (size < sizeof(*rec)) {
wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1,
data, size, true);
return 0;
@@ -151,8 +151,56 @@
return 0;
}
-static int fw_handle_data(struct wil6210_priv *wil, const void *data,
- size_t size)
+static int
+fw_handle_brd_file(struct wil6210_priv *wil, const void *data,
+ size_t size)
+{
+ const struct wil_fw_record_brd_file *rec = data;
+
+ if (size < sizeof(*rec)) {
+ wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1,
+ data, size, true);
+ return 0;
+ }
+
+ wil->brd_file_addr = le32_to_cpu(rec->base_addr);
+ wil->brd_file_max_size = le32_to_cpu(rec->max_size_bytes);
+
+ wil_dbg_fw(wil, "brd_file_addr 0x%x, brd_file_max_size %d\n",
+ wil->brd_file_addr, wil->brd_file_max_size);
+
+ return 0;
+}
+
+static int
+fw_handle_comment(struct wil6210_priv *wil, const void *data,
+ size_t size)
+{
+ const struct wil_fw_record_comment_hdr *hdr = data;
+ u32 magic;
+ int rc = 0;
+
+ if (size < sizeof(*hdr))
+ return 0;
+
+ magic = le32_to_cpu(hdr->magic);
+
+ switch (magic) {
+ case WIL_FW_CAPABILITIES_MAGIC:
+ wil_dbg_fw(wil, "magic is WIL_FW_CAPABILITIES_MAGIC\n");
+ rc = fw_handle_capabilities(wil, data, size);
+ break;
+ case WIL_BRD_FILE_MAGIC:
+ wil_dbg_fw(wil, "magic is WIL_BRD_FILE_MAGIC\n");
+ rc = fw_handle_brd_file(wil, data, size);
+ break;
+ }
+
+ return rc;
+}
+
+static int __fw_handle_data(struct wil6210_priv *wil, const void *data,
+ size_t size, __le32 addr)
{
const struct wil_fw_record_data *d = data;
void __iomem *dst;
@@ -163,16 +211,23 @@
return -EINVAL;
}
- if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address"))
+ if (!wil_fw_addr_check(wil, &dst, addr, s, "address"))
return -EINVAL;
- wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(d->addr),
- s);
+ wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(addr), s);
wil_memcpy_toio_32(dst, d->data, s);
wmb(); /* finish before processing next record */
return 0;
}
+static int fw_handle_data(struct wil6210_priv *wil, const void *data,
+ size_t size)
+{
+ const struct wil_fw_record_data *d = data;
+
+ return __fw_handle_data(wil, data, size, d->addr);
+}
+
static int fw_handle_fill(struct wil6210_priv *wil, const void *data,
size_t size)
{
@@ -552,6 +607,100 @@
}
/**
+ * wil_brd_process - process section from BRD file
+ *
+ * Return error code
+ */
+static int wil_brd_process(struct wil6210_priv *wil, const void *data,
+ size_t size)
+{
+ int rc = 0;
+ const struct wil_fw_record_head *hdr = data;
+ size_t s, hdr_sz;
+ u16 type;
+
+ /* Assuming the board file includes only one header record and one data
+ * record. Each record starts with wil_fw_record_head.
+ */
+ if (size < sizeof(*hdr))
+ return -EINVAL;
+ s = sizeof(*hdr) + le32_to_cpu(hdr->size);
+ if (s > size)
+ return -EINVAL;
+
+ /* Skip the header record and handle the data record */
+ hdr = (const void *)hdr + s;
+ size -= s;
+ if (size < sizeof(*hdr))
+ return -EINVAL;
+ hdr_sz = le32_to_cpu(hdr->size);
+
+ if (wil->brd_file_max_size && hdr_sz > wil->brd_file_max_size)
+ return -EINVAL;
+ if (sizeof(*hdr) + hdr_sz > size)
+ return -EINVAL;
+ if (hdr_sz % 4) {
+ wil_err_fw(wil, "unaligned record size: %zu\n",
+ hdr_sz);
+ return -EINVAL;
+ }
+ type = le16_to_cpu(hdr->type);
+ if (type != wil_fw_type_data) {
+ wil_err_fw(wil, "invalid record type for board file: %d\n",
+ type);
+ return -EINVAL;
+ }
+ if (hdr_sz < sizeof(struct wil_fw_record_data)) {
+ wil_err_fw(wil, "data record too short: %zu\n", hdr_sz);
+ return -EINVAL;
+ }
+
+ wil_dbg_fw(wil, "using addr from fw file: [0x%08x]\n",
+ wil->brd_file_addr);
+
+ rc = __fw_handle_data(wil, &hdr[1], hdr_sz,
+ cpu_to_le32(wil->brd_file_addr));
+
+ return rc;
+}
+
+/**
+ * wil_request_board - Request board file
+ *
+ * Request board image from the file
+ * board file address and max size are read from FW file
+ * during initialization.
+ * brd file shall include one header and one data section.
+ *
+ * Return error code
+ */
+int wil_request_board(struct wil6210_priv *wil, const char *name)
+{
+ int rc, dlen;
+ const struct firmware *brd;
+
+ rc = request_firmware(&brd, name, wil_to_dev(wil));
+ if (rc) {
+ wil_err_fw(wil, "Failed to load brd %s\n", name);
+ return rc;
+ }
+ wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, brd->size);
+
+ /* Verify the header */
+ dlen = wil_fw_verify(wil, brd->data, brd->size);
+ if (dlen < 0) {
+ rc = dlen;
+ goto out;
+ }
+ /* Process the data record */
+ rc = wil_brd_process(wil, brd->data, dlen);
+
+out:
+ release_firmware(brd);
+ return rc;
+}
+
+/**
* wil_fw_verify_file_exists - checks if firmware file exist
*
* @wil: driver context
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index dcf87a7..1835187 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -395,8 +396,9 @@
wil6210_mask_irq_misc(wil, false);
if (isr & ISR_MISC_FW_ERROR) {
- u32 fw_assert_code = wil_r(wil, RGF_FW_ASSERT_CODE);
- u32 ucode_assert_code = wil_r(wil, RGF_UCODE_ASSERT_CODE);
+ u32 fw_assert_code = wil_r(wil, wil->rgf_fw_assert_code_addr);
+ u32 ucode_assert_code =
+ wil_r(wil, wil->rgf_ucode_assert_code_addr);
wil_err(wil,
"Firmware error detected, assert codes FW 0x%08x, UCODE 0x%08x\n",
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 8e13f24..9cef0f0 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -642,6 +643,98 @@
destroy_workqueue(wil->wmi_wq);
}
+static void wil_shutdown_bl(struct wil6210_priv *wil)
+{
+ u32 val;
+
+ wil_s(wil, RGF_USER_BL +
+ offsetof(struct bl_dedicated_registers_v1,
+ bl_shutdown_handshake), BL_SHUTDOWN_HS_GRTD);
+
+ usleep_range(100, 150);
+
+ val = wil_r(wil, RGF_USER_BL +
+ offsetof(struct bl_dedicated_registers_v1,
+ bl_shutdown_handshake));
+ if (val & BL_SHUTDOWN_HS_RTD) {
+ wil_dbg_misc(wil, "BL is ready for halt\n");
+ return;
+ }
+
+ wil_err(wil, "BL did not report ready for halt\n");
+}
+
+/* this format is used by ARC embedded CPU for instruction memory */
+static inline u32 ARC_me_imm32(u32 d)
+{
+ return ((d & 0xffff0000) >> 16) | ((d & 0x0000ffff) << 16);
+}
+
+/* defines access to interrupt vectors for wil_freeze_bl */
+#define ARC_IRQ_VECTOR_OFFSET(N) ((N) * 8)
+/* ARC long jump instruction */
+#define ARC_JAL_INST (0x20200f80)
+
+static void wil_freeze_bl(struct wil6210_priv *wil)
+{
+ u32 jal, upc, saved;
+ u32 ivt3 = ARC_IRQ_VECTOR_OFFSET(3);
+
+ jal = wil_r(wil, wil->iccm_base + ivt3);
+ if (jal != ARC_me_imm32(ARC_JAL_INST)) {
+ wil_dbg_misc(wil, "invalid IVT entry found, skipping\n");
+ return;
+ }
+
+ /* prevent the target from entering deep sleep
+ * and disabling memory access
+ */
+ saved = wil_r(wil, RGF_USER_USAGE_8);
+ wil_w(wil, RGF_USER_USAGE_8, saved | BIT_USER_PREVENT_DEEP_SLEEP);
+ usleep_range(20, 25); /* let the BL process the bit */
+
+ /* redirect to endless loop in the INT_L1 context and let it trap */
+ wil_w(wil, wil->iccm_base + ivt3 + 4, ARC_me_imm32(ivt3));
+ usleep_range(20, 25); /* let the BL get into the trap */
+
+ /* verify the BL is frozen */
+ upc = wil_r(wil, RGF_USER_CPU_PC);
+ if (upc < ivt3 || (upc > (ivt3 + 8)))
+ wil_dbg_misc(wil, "BL freeze failed, PC=0x%08X\n", upc);
+
+ wil_w(wil, RGF_USER_USAGE_8, saved);
+}
+
+static void wil_bl_prepare_halt(struct wil6210_priv *wil)
+{
+ u32 tmp, ver;
+
+ /* before halting device CPU driver must make sure BL is not accessing
+ * host memory. This is done differently depending on BL version:
+ * 1. For very old BL versions the procedure is skipped
+ * (not supported).
+ * 2. For old BL version we use a special trick to freeze the BL
+ * 3. For new BL versions we shutdown the BL using handshake procedure.
+ */
+ tmp = wil_r(wil, RGF_USER_BL +
+ offsetof(struct bl_dedicated_registers_v0,
+ boot_loader_struct_version));
+ if (!tmp) {
+ wil_dbg_misc(wil, "old BL, skipping halt preperation\n");
+ return;
+ }
+
+ tmp = wil_r(wil, RGF_USER_BL +
+ offsetof(struct bl_dedicated_registers_v1,
+ bl_shutdown_handshake));
+ ver = BL_SHUTDOWN_HS_PROT_VER(tmp);
+
+ if (ver > 0)
+ wil_shutdown_bl(wil);
+ else
+ wil_freeze_bl(wil);
+}
+
static inline void wil_halt_cpu(struct wil6210_priv *wil)
{
wil_w(wil, RGF_USER_USER_CPU_0, BIT_USER_USER_CPU_MAN_RST);
@@ -675,7 +768,7 @@
}
}
-static int wil_target_reset(struct wil6210_priv *wil)
+static int wil_target_reset(struct wil6210_priv *wil, int no_flash)
{
int delay = 0;
u32 x, x1 = 0;
@@ -689,9 +782,16 @@
wil_halt_cpu(wil);
- /* clear all boot loader "ready" bits */
- wil_w(wil, RGF_USER_BL +
- offsetof(struct bl_dedicated_registers_v0, boot_loader_ready), 0);
+ if (!no_flash) {
+ /* clear all boot loader "ready" bits */
+ wil_w(wil, RGF_USER_BL +
+ offsetof(struct bl_dedicated_registers_v0,
+ boot_loader_ready), 0);
+ /* this should be safe to write even with old BLs */
+ wil_w(wil, RGF_USER_BL +
+ offsetof(struct bl_dedicated_registers_v1,
+ bl_shutdown_handshake), 0);
+ }
/* Clear Fw Download notification */
wil_c(wil, RGF_USER_USAGE_6, BIT(0));
@@ -732,21 +832,33 @@
wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
/* wait until device ready. typical time is 20..80 msec */
- do {
- msleep(RST_DELAY);
- x = wil_r(wil, RGF_USER_BL +
- offsetof(struct bl_dedicated_registers_v0,
- boot_loader_ready));
- if (x1 != x) {
- wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n", x1, x);
- x1 = x;
- }
- if (delay++ > RST_COUNT) {
- wil_err(wil, "Reset not completed, bl.ready 0x%08x\n",
- x);
- return -ETIME;
- }
- } while (x != BL_READY);
+ if (no_flash)
+ do {
+ msleep(RST_DELAY);
+ x = wil_r(wil, USER_EXT_USER_PMU_3);
+ if (delay++ > RST_COUNT) {
+ wil_err(wil, "Reset not completed, PMU_3 0x%08x\n",
+ x);
+ return -ETIME;
+ }
+ } while ((x & BIT_PMU_DEVICE_RDY) == 0);
+ else
+ do {
+ msleep(RST_DELAY);
+ x = wil_r(wil, RGF_USER_BL +
+ offsetof(struct bl_dedicated_registers_v0,
+ boot_loader_ready));
+ if (x1 != x) {
+ wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n",
+ x1, x);
+ x1 = x;
+ }
+ if (delay++ > RST_COUNT) {
+ wil_err(wil, "Reset not completed, bl.ready 0x%08x\n",
+ x);
+ return -ETIME;
+ }
+ } while (x != BL_READY);
wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
@@ -754,6 +866,21 @@
wil_s(wil, RGF_DMA_OFUL_NID_0, BIT_DMA_OFUL_NID_0_RX_EXT_TR_EN |
BIT_DMA_OFUL_NID_0_RX_EXT_A3_SRC);
+ if (no_flash) {
+ /* Reset OTP HW vectors to fit 40MHz */
+ wil_w(wil, RGF_USER_XPM_IFC_RD_TIME1, 0x60001);
+ wil_w(wil, RGF_USER_XPM_IFC_RD_TIME2, 0x20027);
+ wil_w(wil, RGF_USER_XPM_IFC_RD_TIME3, 0x1);
+ wil_w(wil, RGF_USER_XPM_IFC_RD_TIME4, 0x20027);
+ wil_w(wil, RGF_USER_XPM_IFC_RD_TIME5, 0x30003);
+ wil_w(wil, RGF_USER_XPM_IFC_RD_TIME6, 0x20002);
+ wil_w(wil, RGF_USER_XPM_IFC_RD_TIME7, 0x60001);
+ wil_w(wil, RGF_USER_XPM_IFC_RD_TIME8, 0x60001);
+ wil_w(wil, RGF_USER_XPM_IFC_RD_TIME9, 0x60001);
+ wil_w(wil, RGF_USER_XPM_IFC_RD_TIME10, 0x60001);
+ wil_w(wil, RGF_USER_XPM_RD_DOUT_SAMPLE_TIME, 0x57);
+ }
+
wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY);
return 0;
}
@@ -911,6 +1038,27 @@
}
}
+static int wil_get_otp_info(struct wil6210_priv *wil)
+{
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct wiphy *wiphy = wil_to_wiphy(wil);
+ u8 mac[8];
+
+ wil_memcpy_fromio_32(mac, wil->csr + HOSTADDR(RGF_OTP_MAC),
+ sizeof(mac));
+ if (!is_valid_ether_addr(mac)) {
+ wil_err(wil, "Invalid MAC %pM\n", mac);
+ return -EINVAL;
+ }
+
+ ether_addr_copy(ndev->perm_addr, mac);
+ ether_addr_copy(wiphy->perm_addr, mac);
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ ether_addr_copy(ndev->dev_addr, mac);
+
+ return 0;
+}
+
static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
{
ulong to = msecs_to_jiffies(1000);
@@ -1004,6 +1152,7 @@
{
int rc;
unsigned long status_flags = BIT(wil_status_resetting);
+ int no_flash;
wil_dbg_misc(wil, "reset\n");
@@ -1082,20 +1231,28 @@
flush_workqueue(wil->wq_service);
flush_workqueue(wil->wmi_wq);
- wil_bl_crash_info(wil, false);
+ no_flash = test_bit(hw_capa_no_flash, wil->hw_capa);
+ if (!no_flash)
+ wil_bl_crash_info(wil, false);
wil_disable_irq(wil);
- rc = wil_target_reset(wil);
+ rc = wil_target_reset(wil, no_flash);
wil6210_clear_irq(wil);
wil_enable_irq(wil);
wil_rx_fini(wil);
if (rc) {
- wil_bl_crash_info(wil, true);
+ if (!no_flash)
+ wil_bl_crash_info(wil, true);
goto out;
}
- rc = wil_get_bl_info(wil);
- if (rc == -EAGAIN && !load_fw) /* ignore RF error if not going up */
- rc = 0;
+ if (no_flash) {
+ rc = wil_get_otp_info(wil);
+ } else {
+ rc = wil_get_bl_info(wil);
+ if (rc == -EAGAIN && !load_fw)
+ /* ignore RF error if not going up */
+ rc = 0;
+ }
if (rc)
goto out;
@@ -1104,13 +1261,21 @@
wil_info(wil, "Use firmware <%s> + board <%s>\n",
wil->wil_fw_name, WIL_BOARD_FILE_NAME);
+ if (!no_flash)
+ wil_bl_prepare_halt(wil);
+
wil_halt_cpu(wil);
memset(wil->fw_version, 0, sizeof(wil->fw_version));
/* Loading f/w from the file */
rc = wil_request_firmware(wil, wil->wil_fw_name, true);
if (rc)
goto out;
- rc = wil_request_firmware(wil, WIL_BOARD_FILE_NAME, true);
+ if (wil->brd_file_addr)
+ rc = wil_request_board(wil, WIL_BOARD_FILE_NAME);
+ else
+ rc = wil_request_firmware(wil,
+ WIL_BOARD_FILE_NAME,
+ true);
if (rc)
goto out;
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 025bdd3..1875387 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -39,15 +40,16 @@
#endif /* CONFIG_PM */
static
-void wil_set_capabilities(struct wil6210_priv *wil)
+int wil_set_capabilities(struct wil6210_priv *wil)
{
const char *wil_fw_name;
u32 jtag_id = wil_r(wil, RGF_USER_JTAG_DEV_ID);
u8 chip_revision = (wil_r(wil, RGF_USER_REVISION_ID) &
RGF_USER_REVISION_ID_MASK);
int platform_capa;
+ struct fw_map *iccm_section, *sct;
- bitmap_zero(wil->hw_capabilities, hw_capability_last);
+ bitmap_zero(wil->hw_capa, hw_capa_last);
bitmap_zero(wil->fw_capabilities, WMI_FW_CAPABILITY_MAX);
bitmap_zero(wil->platform_capa, WIL_PLATFORM_CAPA_MAX);
wil->wil_fw_name = ftm_mode ? WIL_FW_NAME_FTM_DEFAULT :
@@ -56,6 +58,8 @@
switch (jtag_id) {
case JTAG_DEV_ID_SPARROW:
+ memcpy(fw_mapping, sparrow_fw_mapping,
+ sizeof(sparrow_fw_mapping));
switch (chip_revision) {
case REVISION_ID_SPARROW_D0:
wil->hw_name = "Sparrow D0";
@@ -65,6 +69,12 @@
if (wil_fw_verify_file_exists(wil, wil_fw_name))
wil->wil_fw_name = wil_fw_name;
+ sct = wil_find_fw_mapping("mac_rgf_ext");
+ if (!sct) {
+ wil_err(wil, "mac_rgf_ext section not found in fw_mapping\n");
+ return -EINVAL;
+ }
+ memcpy(sct, &sparrow_d0_mac_rgf_ext, sizeof(*sct));
break;
case REVISION_ID_SPARROW_B0:
wil->hw_name = "Sparrow B0";
@@ -75,15 +85,36 @@
wil->hw_version = HW_VER_UNKNOWN;
break;
}
+ wil->rgf_fw_assert_code_addr = SPARROW_RGF_FW_ASSERT_CODE;
+ wil->rgf_ucode_assert_code_addr = SPARROW_RGF_UCODE_ASSERT_CODE;
+ break;
+ case JTAG_DEV_ID_TALYN:
+ wil->hw_name = "Talyn";
+ wil->hw_version = HW_VER_TALYN;
+ memcpy(fw_mapping, talyn_fw_mapping, sizeof(talyn_fw_mapping));
+ wil->rgf_fw_assert_code_addr = TALYN_RGF_FW_ASSERT_CODE;
+ wil->rgf_ucode_assert_code_addr = TALYN_RGF_UCODE_ASSERT_CODE;
+ if (wil_r(wil, RGF_USER_OTP_HW_RD_MACHINE_1) &
+ BIT_NO_FLASH_INDICATION)
+ set_bit(hw_capa_no_flash, wil->hw_capa);
break;
default:
wil_err(wil, "Unknown board hardware, chip_id 0x%08x, chip_revision 0x%08x\n",
jtag_id, chip_revision);
wil->hw_name = "Unknown";
wil->hw_version = HW_VER_UNKNOWN;
+ return -EINVAL;
}
- wil_info(wil, "Board hardware is %s\n", wil->hw_name);
+ iccm_section = wil_find_fw_mapping("fw_code");
+ if (!iccm_section) {
+ wil_err(wil, "fw_code section not found in fw_mapping\n");
+ return -EINVAL;
+ }
+ wil->iccm_base = iccm_section->host;
+
+ wil_info(wil, "Board hardware is %s, flash %sexist\n", wil->hw_name,
+ test_bit(hw_capa_no_flash, wil->hw_capa) ? "doesn't " : "");
/* Get platform capabilities */
if (wil->platform_ops.get_capa) {
@@ -96,6 +127,8 @@
/* extract FW capabilities from file without loading the FW */
wil_request_firmware(wil, wil->wil_fw_name, false);
wil_refresh_fw_capabilities(wil);
+
+ return 0;
}
void wil_disable_irq(struct wil6210_priv *wil)
@@ -299,7 +332,11 @@
/* rollback to err_iounmap */
wil_info(wil, "CSR at %pR -> 0x%p\n", &pdev->resource[0], wil->csr);
- wil_set_capabilities(wil);
+ rc = wil_set_capabilities(wil);
+ if (rc) {
+ wil_err(wil, "wil_set_capabilities failed, rc %d\n", rc);
+ goto err_iounmap;
+ }
wil6210_clear_irq(wil);
/* FW should raise IRQ when ready */
@@ -385,6 +422,7 @@
static const struct pci_device_id wil6210_pcie_ids[] = {
{ PCI_DEVICE(0x1ae9, 0x0310) },
{ PCI_DEVICE(0x1ae9, 0x0302) }, /* same as above, firmware broken */
+ { PCI_DEVICE(0x17cb, 0x1201) }, /* Talyn */
{ /* end: all zeroes */ },
};
MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 33df230..2f6d6c9 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -170,6 +171,7 @@
#define HW_MACHINE_BOOT_DONE (0x3fffffd)
#define RGF_USER_USER_CPU_0 (0x8801e0)
#define BIT_USER_USER_CPU_MAN_RST BIT(1) /* user_cpu_man_rst */
+#define RGF_USER_CPU_PC (0x8801e8)
#define RGF_USER_MAC_CPU_0 (0x8801fc)
#define BIT_USER_MAC_CPU_MAN_RST BIT(1) /* mac_cpu_man_rst */
#define RGF_USER_USER_SCRATCH_PAD (0x8802bc)
@@ -195,6 +197,19 @@
#define RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1 (0x880c2c)
#define RGF_USER_SPARROW_M_4 (0x880c50) /* Sparrow */
#define BIT_SPARROW_M_4_SEL_SLEEP_OR_REF BIT(2)
+#define RGF_USER_OTP_HW_RD_MACHINE_1 (0x880ce0)
+ #define BIT_NO_FLASH_INDICATION BIT(8)
+#define RGF_USER_XPM_IFC_RD_TIME1 (0x880cec)
+#define RGF_USER_XPM_IFC_RD_TIME2 (0x880cf0)
+#define RGF_USER_XPM_IFC_RD_TIME3 (0x880cf4)
+#define RGF_USER_XPM_IFC_RD_TIME4 (0x880cf8)
+#define RGF_USER_XPM_IFC_RD_TIME5 (0x880cfc)
+#define RGF_USER_XPM_IFC_RD_TIME6 (0x880d00)
+#define RGF_USER_XPM_IFC_RD_TIME7 (0x880d04)
+#define RGF_USER_XPM_IFC_RD_TIME8 (0x880d08)
+#define RGF_USER_XPM_IFC_RD_TIME9 (0x880d0c)
+#define RGF_USER_XPM_IFC_RD_TIME10 (0x880d10)
+#define RGF_USER_XPM_RD_DOUT_SAMPLE_TIME (0x880d64)
#define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */
#define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0)
@@ -285,22 +300,33 @@
#define RGF_CAF_PLL_LOCK_STATUS (0x88afec)
#define BIT_CAF_OSC_DIG_XTAL_STABLE BIT(0)
+#define USER_EXT_USER_PMU_3 (0x88d00c)
+ #define BIT_PMU_DEVICE_RDY BIT(0)
+
#define RGF_USER_JTAG_DEV_ID (0x880b34) /* device ID */
#define JTAG_DEV_ID_SPARROW (0x2632072f)
+ #define JTAG_DEV_ID_TALYN (0x7e0e1)
#define RGF_USER_REVISION_ID (0x88afe4)
#define RGF_USER_REVISION_ID_MASK (3)
#define REVISION_ID_SPARROW_B0 (0x0)
#define REVISION_ID_SPARROW_D0 (0x3)
+#define RGF_OTP_MAC (0x8a0620)
+
/* crash codes for FW/Ucode stored here */
-#define RGF_FW_ASSERT_CODE (0x91f020)
-#define RGF_UCODE_ASSERT_CODE (0x91f028)
+
+/* ASSERT RGFs */
+#define SPARROW_RGF_FW_ASSERT_CODE (0x91f020)
+#define SPARROW_RGF_UCODE_ASSERT_CODE (0x91f028)
+#define TALYN_RGF_FW_ASSERT_CODE (0xa37020)
+#define TALYN_RGF_UCODE_ASSERT_CODE (0xa37028)
enum {
HW_VER_UNKNOWN,
HW_VER_SPARROW_B0, /* REVISION_ID_SPARROW_B0 */
HW_VER_SPARROW_D0, /* REVISION_ID_SPARROW_D0 */
+ HW_VER_TALYN, /* JTAG_DEV_ID_TALYN */
};
/* popular locations */
@@ -316,6 +342,10 @@
#define WIL_DATA_COMPLETION_TO_MS 200
/* Hardware definitions end */
+#define SPARROW_FW_MAPPING_TABLE_SIZE 10
+#define TALYN_FW_MAPPING_TABLE_SIZE 13
+#define MAX_FW_MAPPING_TABLE_SIZE 13
+
struct fw_map {
u32 from; /* linker address - from, inclusive */
u32 to; /* linker address - to, exclusive */
@@ -325,7 +355,10 @@
};
/* array size should be in sync with actual definition in the wmi.c */
-extern const struct fw_map fw_mapping[10];
+extern const struct fw_map sparrow_fw_mapping[SPARROW_FW_MAPPING_TABLE_SIZE];
+extern const struct fw_map sparrow_d0_mac_rgf_ext;
+extern const struct fw_map talyn_fw_mapping[TALYN_FW_MAPPING_TABLE_SIZE];
+extern struct fw_map fw_mapping[MAX_FW_MAPPING_TABLE_SIZE];
/**
* mk_cidxtid - construct @cidxtid field
@@ -572,7 +605,8 @@
};
enum {
- hw_capability_last
+ hw_capa_no_flash,
+ hw_capa_last
};
struct wil_probe_client_req {
@@ -648,7 +682,10 @@
u8 chip_revision;
const char *hw_name;
const char *wil_fw_name;
- DECLARE_BITMAP(hw_capabilities, hw_capability_last);
+ char *board_file;
+ u32 brd_file_addr;
+ u32 brd_file_max_size;
+ DECLARE_BITMAP(hw_capa, hw_capa_last);
DECLARE_BITMAP(fw_capabilities, WMI_FW_CAPABILITY_MAX);
DECLARE_BITMAP(platform_capa, WIL_PLATFORM_CAPA_MAX);
u8 n_mids; /* number of additional MIDs as reported by FW */
@@ -722,7 +759,7 @@
atomic_t isr_count_rx, isr_count_tx;
/* debugfs */
struct dentry *debug;
- struct wil_blob_wrapper blobs[ARRAY_SIZE(fw_mapping)];
+ struct wil_blob_wrapper blobs[MAX_FW_MAPPING_TABLE_SIZE];
u8 discovery_mode;
u8 abft_len;
u8 wakeup_trigger;
@@ -770,6 +807,10 @@
bool suspend_resp_comp;
u32 bus_request_kbps;
u32 bus_request_kbps_pre_suspend;
+
+ u32 rgf_fw_assert_code_addr;
+ u32 rgf_ucode_assert_code_addr;
+ u32 iccm_base;
};
#define wil_to_wiphy(i) (i->wdev->wiphy)
@@ -895,6 +936,7 @@
int wil_find_cid(struct wil6210_priv *wil, const u8 *mac);
void wil_set_ethtoolops(struct net_device *ndev);
+struct fw_map *wil_find_fw_mapping(const char *section);
void __iomem *wmi_buffer_block(struct wil6210_priv *wil, __le32 ptr, u32 size);
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr);
void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr);
@@ -1034,6 +1076,7 @@
int wil_ioctl(struct wil6210_priv *wil, void __user *data, int cmd);
int wil_request_firmware(struct wil6210_priv *wil, const char *name,
bool load);
+int wil_request_board(struct wil6210_priv *wil, const char *name);
bool wil_fw_verify_file_exists(struct wil6210_priv *wil, const char *name);
void wil_pm_runtime_allow(struct wil6210_priv *wil);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 07659b12..f2dba31 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -71,23 +72,23 @@
* On the PCI bus, there is one BAR (BAR0) of 2Mb size, exposing
* AHB addresses starting from 0x880000
*
- * Internally, firmware uses addresses that allows faster access but
+ * Internally, firmware uses addresses that allow faster access but
* are invisible from the host. To read from these addresses, alternative
* AHB address must be used.
- *
- * Memory mapping
- * Linker address PCI/Host address
- * 0x880000 .. 0xa80000 2Mb BAR0
- * 0x800000 .. 0x807000 0x900000 .. 0x907000 28k DCCM
- * 0x840000 .. 0x857000 0x908000 .. 0x91f000 92k PERIPH
*/
/**
- * @fw_mapping provides memory remapping table
+ * @sparrow_fw_mapping provides memory remapping table for sparrow
*
* array size should be in sync with the declaration in the wil6210.h
+ *
+ * Sparrow memory mapping:
+ * Linker address PCI/Host address
+ * 0x880000 .. 0xa80000 2Mb BAR0
+ * 0x800000 .. 0x808000 0x900000 .. 0x908000 32k DCCM
+ * 0x840000 .. 0x860000 0x908000 .. 0x928000 128k PERIPH
*/
-const struct fw_map fw_mapping[] = {
+const struct fw_map sparrow_fw_mapping[] = {
/* FW code RAM 256k */
{0x000000, 0x040000, 0x8c0000, "fw_code", true},
/* FW data RAM 32k */
@@ -113,6 +114,59 @@
{0x800000, 0x804000, 0x940000, "uc_data", false},
};
+/**
+ * @sparrow_d0_mac_rgf_ext - mac_rgf_ext section for Sparrow D0
+ * it is a bit larger to support extra features
+ */
+const struct fw_map sparrow_d0_mac_rgf_ext = {
+ 0x88c000, 0x88c500, 0x88c000, "mac_rgf_ext", true
+};
+
+/**
+ * @talyn_fw_mapping provides memory remapping table for Talyn
+ *
+ * array size should be in sync with the declaration in the wil6210.h
+ *
+ * Talyn memory mapping:
+ * Linker address PCI/Host address
+ * 0x880000 .. 0xc80000 4Mb BAR0
+ * 0x800000 .. 0x820000 0xa00000 .. 0xa20000 128k DCCM
+ * 0x840000 .. 0x858000 0xa20000 .. 0xa38000 96k PERIPH
+ */
+const struct fw_map talyn_fw_mapping[] = {
+ /* FW code RAM 1M */
+ {0x000000, 0x100000, 0x900000, "fw_code", true},
+ /* FW data RAM 128k */
+ {0x800000, 0x820000, 0xa00000, "fw_data", true},
+ /* periph. data RAM 96k */
+ {0x840000, 0x858000, 0xa20000, "fw_peri", true},
+ /* various RGF 40k */
+ {0x880000, 0x88a000, 0x880000, "rgf", true},
+ /* AGC table 4k */
+ {0x88a000, 0x88b000, 0x88a000, "AGC_tbl", true},
+ /* Pcie_ext_rgf 4k */
+ {0x88b000, 0x88c000, 0x88b000, "rgf_ext", true},
+ /* mac_ext_rgf 1344b */
+ {0x88c000, 0x88c540, 0x88c000, "mac_rgf_ext", true},
+ /* ext USER RGF 4k */
+ {0x88d000, 0x88e000, 0x88d000, "ext_user_rgf", true},
+ /* OTP 4k */
+ {0x8a0000, 0x8a1000, 0x8a0000, "otp", true},
+ /* DMA EXT RGF 64k */
+ {0x8b0000, 0x8c0000, 0x8b0000, "dma_ext_rgf", true},
+ /* upper area 1536k */
+ {0x900000, 0xa80000, 0x900000, "upper", true},
+ /* UCODE areas - accessible by debugfs blobs but not by
+ * wmi_addr_remap. UCODE areas MUST be added AFTER FW areas!
+ */
+ /* ucode code RAM 256k */
+ {0x000000, 0x040000, 0xa38000, "uc_code", false},
+ /* ucode data RAM 32k */
+ {0x800000, 0x808000, 0xa78000, "uc_data", false},
+};
+
+struct fw_map fw_mapping[MAX_FW_MAPPING_TABLE_SIZE];
+
struct blink_on_off_time led_blink_time[] = {
{WIL_LED_BLINK_ON_SLOW_MS, WIL_LED_BLINK_OFF_SLOW_MS},
{WIL_LED_BLINK_ON_MED_MS, WIL_LED_BLINK_OFF_MED_MS},
@@ -140,6 +194,24 @@
}
/**
+ * find fw_mapping entry by section name
+ * @section - section name
+ *
+ * Return pointer to section or NULL if not found
+ */
+struct fw_map *wil_find_fw_mapping(const char *section)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fw_mapping); i++)
+ if (fw_mapping[i].name &&
+ !strcmp(section, fw_mapping[i].name))
+ return &fw_mapping[i];
+
+ return NULL;
+}
+
+/**
* Check address validity for WMI buffer; remap if needed
* @ptr - internal (linker) fw/ucode address
* @size - if non zero, validate the block does not
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
index d04babd..ff5ce1e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
@@ -1040,6 +1040,8 @@
return le32_to_cpu(txq_timer->p2p_go);
case NL80211_IFTYPE_P2P_DEVICE:
return le32_to_cpu(txq_timer->p2p_device);
+ case NL80211_IFTYPE_MONITOR:
+ return default_timeout;
default:
WARN_ON(1);
return mvm->cfg->base_params->wd_timeout;
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index 8d498a9..1a9dadf 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -86,6 +86,8 @@
/* IRQ name is queue name with "-tx" or "-rx" appended */
#define IRQ_NAME_SIZE (QUEUE_NAME_SIZE + 3)
+static DECLARE_WAIT_QUEUE_HEAD(module_unload_q);
+
struct netfront_stats {
u64 packets;
u64 bytes;
@@ -2051,10 +2053,12 @@
break;
case XenbusStateClosed:
+ wake_up_all(&module_unload_q);
if (dev->state == XenbusStateClosed)
break;
/* Missed the backend's CLOSING state -- fallthrough */
case XenbusStateClosing:
+ wake_up_all(&module_unload_q);
xenbus_frontend_closed(dev);
break;
}
@@ -2160,6 +2164,20 @@
dev_dbg(&dev->dev, "%s\n", dev->nodename);
+ if (xenbus_read_driver_state(dev->otherend) != XenbusStateClosed) {
+ xenbus_switch_state(dev, XenbusStateClosing);
+ wait_event(module_unload_q,
+ xenbus_read_driver_state(dev->otherend) ==
+ XenbusStateClosing);
+
+ xenbus_switch_state(dev, XenbusStateClosed);
+ wait_event(module_unload_q,
+ xenbus_read_driver_state(dev->otherend) ==
+ XenbusStateClosed ||
+ xenbus_read_driver_state(dev->otherend) ==
+ XenbusStateUnknown);
+ }
+
xennet_disconnect_backend(info);
unregister_netdev(info->netdev);
diff --git a/drivers/nfc/nq-nci.c b/drivers/nfc/nq-nci.c
index ea4bedf..0280d42 100644
--- a/drivers/nfc/nq-nci.c
+++ b/drivers/nfc/nq-nci.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -36,6 +36,8 @@
unsigned int firm_gpio;
unsigned int ese_gpio;
const char *clk_src_name;
+ /* NFC_CLK pin voting state */
+ bool clk_pin_voting;
};
static const struct of_device_id msm_match_table[] = {
@@ -469,39 +471,47 @@
dev_dbg(&nqx_dev->client->dev,
"gpio_set_value disable: %s: info: %p\n",
__func__, nqx_dev);
- if (gpio_is_valid(nqx_dev->firm_gpio))
+ if (gpio_is_valid(nqx_dev->firm_gpio)) {
gpio_set_value(nqx_dev->firm_gpio, 0);
+ usleep_range(10000, 10100);
+ }
if (gpio_is_valid(nqx_dev->ese_gpio)) {
if (!gpio_get_value(nqx_dev->ese_gpio)) {
dev_dbg(&nqx_dev->client->dev, "disabling en_gpio\n");
gpio_set_value(nqx_dev->en_gpio, 0);
+ usleep_range(10000, 10100);
} else {
dev_dbg(&nqx_dev->client->dev, "keeping en_gpio high\n");
}
} else {
dev_dbg(&nqx_dev->client->dev, "ese_gpio invalid, set en_gpio to low\n");
gpio_set_value(nqx_dev->en_gpio, 0);
+ usleep_range(10000, 10100);
}
- r = nqx_clock_deselect(nqx_dev);
- if (r < 0)
- dev_err(&nqx_dev->client->dev, "unable to disable clock\n");
+ if (nqx_dev->pdata->clk_pin_voting) {
+ r = nqx_clock_deselect(nqx_dev);
+ if (r < 0)
+ dev_err(&nqx_dev->client->dev, "unable to disable clock\n");
+ }
nqx_dev->nfc_ven_enabled = false;
- /* hardware dependent delay */
- msleep(100);
} else if (arg == 1) {
nqx_enable_irq(nqx_dev);
dev_dbg(&nqx_dev->client->dev,
"gpio_set_value enable: %s: info: %p\n",
__func__, nqx_dev);
- if (gpio_is_valid(nqx_dev->firm_gpio))
+ if (gpio_is_valid(nqx_dev->firm_gpio)) {
gpio_set_value(nqx_dev->firm_gpio, 0);
+ usleep_range(10000, 10100);
+ }
gpio_set_value(nqx_dev->en_gpio, 1);
- r = nqx_clock_select(nqx_dev);
- if (r < 0)
- dev_err(&nqx_dev->client->dev, "unable to enable clock\n");
+ usleep_range(10000, 10100);
+ if (nqx_dev->pdata->clk_pin_voting) {
+ r = nqx_clock_select(nqx_dev);
+ if (r < 0)
+ dev_err(&nqx_dev->client->dev, "unable to enable clock\n");
+ }
nqx_dev->nfc_ven_enabled = true;
- msleep(20);
} else if (arg == 2) {
/*
* We are switching to Dowload Mode, toggle the enable pin
@@ -515,14 +525,15 @@
}
}
gpio_set_value(nqx_dev->en_gpio, 1);
- msleep(20);
- if (gpio_is_valid(nqx_dev->firm_gpio))
+ usleep_range(10000, 10100);
+ if (gpio_is_valid(nqx_dev->firm_gpio)) {
gpio_set_value(nqx_dev->firm_gpio, 1);
- msleep(20);
+ usleep_range(10000, 10100);
+ }
gpio_set_value(nqx_dev->en_gpio, 0);
- msleep(100);
+ usleep_range(10000, 10100);
gpio_set_value(nqx_dev->en_gpio, 1);
- msleep(20);
+ usleep_range(10000, 10100);
} else {
r = -ENOIOCTLCMD;
}
@@ -648,13 +659,14 @@
unsigned char nci_reset_rsp[6];
unsigned char init_rsp_len = 0;
unsigned int enable_gpio = nqx_dev->en_gpio;
+
/* making sure that the NFCC starts in a clean state. */
gpio_set_value(enable_gpio, 0);/* ULPM: Disable */
/* hardware dependent delay */
- msleep(20);
+ usleep_range(10000, 10100);
gpio_set_value(enable_gpio, 1);/* HPD : Enable*/
/* hardware dependent delay */
- msleep(20);
+ usleep_range(10000, 10100);
/* send NCI CORE RESET CMD with Keep Config parameters */
ret = i2c_master_send(client, raw_nci_reset_cmd,
@@ -670,21 +682,17 @@
/* Read Response of RESET command */
ret = i2c_master_recv(client, nci_reset_rsp,
sizeof(nci_reset_rsp));
- dev_err(&client->dev,
- "%s: - nq - reset cmd answer : NfcNciRx %x %x %x\n",
- __func__, nci_reset_rsp[0],
- nci_reset_rsp[1], nci_reset_rsp[2]);
if (ret < 0) {
dev_err(&client->dev,
"%s: - i2c_master_recv Error\n", __func__);
goto err_nfcc_hw_check;
}
- ret = i2c_master_send(client, raw_nci_init_cmd,
- sizeof(raw_nci_init_cmd));
+ ret = nqx_standby_write(nqx_dev, raw_nci_init_cmd,
+ sizeof(raw_nci_init_cmd));
if (ret < 0) {
dev_err(&client->dev,
"%s: - i2c_master_send Error\n", __func__);
- goto err_nfcc_hw_check;
+ goto err_nfcc_core_init_fail;
}
/* hardware dependent delay */
msleep(30);
@@ -694,7 +702,7 @@
if (ret < 0) {
dev_err(&client->dev,
"%s: - i2c_master_recv Error\n", __func__);
- goto err_nfcc_hw_check;
+ goto err_nfcc_core_init_fail;
}
init_rsp_len = 2 + nci_init_rsp[2]; /*payload + len*/
if (init_rsp_len > PAYLOAD_HEADER_LENGTH) {
@@ -707,6 +715,11 @@
nqx_dev->nqx_info.info.fw_minor =
nci_init_rsp[init_rsp_len];
}
+ dev_dbg(&client->dev,
+ "%s: - nq - reset cmd answer : NfcNciRx %x %x %x\n",
+ __func__, nci_reset_rsp[0],
+ nci_reset_rsp[1], nci_reset_rsp[2]);
+
dev_dbg(&nqx_dev->client->dev, "NQ NFCC chip_type = %x\n",
nqx_dev->nqx_info.info.chip_type);
dev_dbg(&nqx_dev->client->dev, "NQ fw version = %x.%x.%x\n",
@@ -746,6 +759,12 @@
ret = 0;
goto done;
+err_nfcc_core_init_fail:
+ dev_err(&client->dev,
+ "%s: - nq - reset cmd answer : NfcNciRx %x %x %x\n",
+ __func__, nci_reset_rsp[0],
+ nci_reset_rsp[1], nci_reset_rsp[2]);
+
err_nfcc_hw_check:
ret = -ENXIO;
dev_err(&client->dev,
@@ -828,12 +847,13 @@
pdata->ese_gpio = -EINVAL;
}
- r = of_property_read_string(np, "qcom,clk-src", &pdata->clk_src_name);
+ if (of_property_read_string(np, "qcom,clk-src", &pdata->clk_src_name))
+ pdata->clk_pin_voting = false;
+ else
+ pdata->clk_pin_voting = true;
pdata->clkreq_gpio = of_get_named_gpio(np, "qcom,nq-clkreq", 0);
- if (r)
- return -EINVAL;
return r;
}
@@ -1213,6 +1233,7 @@
.owner = THIS_MODULE,
.name = "nq-nci",
.of_match_table = msm_match_table,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &nfc_pm_ops,
},
};
diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
index 94733f7..7121453 100644
--- a/drivers/nvdimm/btt.c
+++ b/drivers/nvdimm/btt.c
@@ -183,13 +183,13 @@
return ret;
}
-static int btt_log_read_pair(struct arena_info *arena, u32 lane,
- struct log_entry *ent)
+static int btt_log_group_read(struct arena_info *arena, u32 lane,
+ struct log_group *log)
{
- WARN_ON(!ent);
+ WARN_ON(!log);
return arena_read_bytes(arena,
- arena->logoff + (2 * lane * LOG_ENT_SIZE), ent,
- 2 * LOG_ENT_SIZE);
+ arena->logoff + (lane * LOG_GRP_SIZE), log,
+ LOG_GRP_SIZE);
}
static struct dentry *debugfs_root;
@@ -229,6 +229,8 @@
debugfs_create_x64("logoff", S_IRUGO, d, &a->logoff);
debugfs_create_x64("info2off", S_IRUGO, d, &a->info2off);
debugfs_create_x32("flags", S_IRUGO, d, &a->flags);
+ debugfs_create_u32("log_index_0", S_IRUGO, d, &a->log_index[0]);
+ debugfs_create_u32("log_index_1", S_IRUGO, d, &a->log_index[1]);
}
static void btt_debugfs_init(struct btt *btt)
@@ -247,6 +249,11 @@
}
}
+static u32 log_seq(struct log_group *log, int log_idx)
+{
+ return le32_to_cpu(log->ent[log_idx].seq);
+}
+
/*
* This function accepts two log entries, and uses the
* sequence number to find the 'older' entry.
@@ -256,8 +263,10 @@
*
* TODO The logic feels a bit kludge-y. make it better..
*/
-static int btt_log_get_old(struct log_entry *ent)
+static int btt_log_get_old(struct arena_info *a, struct log_group *log)
{
+ int idx0 = a->log_index[0];
+ int idx1 = a->log_index[1];
int old;
/*
@@ -265,23 +274,23 @@
* the next time, the following logic works out to put this
* (next) entry into [1]
*/
- if (ent[0].seq == 0) {
- ent[0].seq = cpu_to_le32(1);
+ if (log_seq(log, idx0) == 0) {
+ log->ent[idx0].seq = cpu_to_le32(1);
return 0;
}
- if (ent[0].seq == ent[1].seq)
+ if (log_seq(log, idx0) == log_seq(log, idx1))
return -EINVAL;
- if (le32_to_cpu(ent[0].seq) + le32_to_cpu(ent[1].seq) > 5)
+ if (log_seq(log, idx0) + log_seq(log, idx1) > 5)
return -EINVAL;
- if (le32_to_cpu(ent[0].seq) < le32_to_cpu(ent[1].seq)) {
- if (le32_to_cpu(ent[1].seq) - le32_to_cpu(ent[0].seq) == 1)
+ if (log_seq(log, idx0) < log_seq(log, idx1)) {
+ if ((log_seq(log, idx1) - log_seq(log, idx0)) == 1)
old = 0;
else
old = 1;
} else {
- if (le32_to_cpu(ent[0].seq) - le32_to_cpu(ent[1].seq) == 1)
+ if ((log_seq(log, idx0) - log_seq(log, idx1)) == 1)
old = 1;
else
old = 0;
@@ -306,17 +315,18 @@
{
int ret;
int old_ent, ret_ent;
- struct log_entry log[2];
+ struct log_group log;
- ret = btt_log_read_pair(arena, lane, log);
+ ret = btt_log_group_read(arena, lane, &log);
if (ret)
return -EIO;
- old_ent = btt_log_get_old(log);
+ old_ent = btt_log_get_old(arena, &log);
if (old_ent < 0 || old_ent > 1) {
dev_info(to_dev(arena),
"log corruption (%d): lane %d seq [%d, %d]\n",
- old_ent, lane, log[0].seq, log[1].seq);
+ old_ent, lane, log.ent[arena->log_index[0]].seq,
+ log.ent[arena->log_index[1]].seq);
/* TODO set error state? */
return -EIO;
}
@@ -324,7 +334,7 @@
ret_ent = (old_flag ? old_ent : (1 - old_ent));
if (ent != NULL)
- memcpy(ent, &log[ret_ent], LOG_ENT_SIZE);
+ memcpy(ent, &log.ent[arena->log_index[ret_ent]], LOG_ENT_SIZE);
return ret_ent;
}
@@ -338,17 +348,13 @@
u32 sub, struct log_entry *ent)
{
int ret;
- /*
- * Ignore the padding in log_entry for calculating log_half.
- * The entry is 'committed' when we write the sequence number,
- * and we want to ensure that that is the last thing written.
- * We don't bother writing the padding as that would be extra
- * media wear and write amplification
- */
- unsigned int log_half = (LOG_ENT_SIZE - 2 * sizeof(u64)) / 2;
- u64 ns_off = arena->logoff + (((2 * lane) + sub) * LOG_ENT_SIZE);
+ u32 group_slot = arena->log_index[sub];
+ unsigned int log_half = LOG_ENT_SIZE / 2;
void *src = ent;
+ u64 ns_off;
+ ns_off = arena->logoff + (lane * LOG_GRP_SIZE) +
+ (group_slot * LOG_ENT_SIZE);
/* split the 16B write into atomic, durable halves */
ret = arena_write_bytes(arena, ns_off, src, log_half);
if (ret)
@@ -419,16 +425,16 @@
{
int ret;
u32 i;
- struct log_entry log, zerolog;
+ struct log_entry ent, zerolog;
memset(&zerolog, 0, sizeof(zerolog));
for (i = 0; i < arena->nfree; i++) {
- log.lba = cpu_to_le32(i);
- log.old_map = cpu_to_le32(arena->external_nlba + i);
- log.new_map = cpu_to_le32(arena->external_nlba + i);
- log.seq = cpu_to_le32(LOG_SEQ_INIT);
- ret = __btt_log_write(arena, i, 0, &log);
+ ent.lba = cpu_to_le32(i);
+ ent.old_map = cpu_to_le32(arena->external_nlba + i);
+ ent.new_map = cpu_to_le32(arena->external_nlba + i);
+ ent.seq = cpu_to_le32(LOG_SEQ_INIT);
+ ret = __btt_log_write(arena, i, 0, &ent);
if (ret)
return ret;
ret = __btt_log_write(arena, i, 1, &zerolog);
@@ -490,6 +496,123 @@
return 0;
}
+static bool ent_is_padding(struct log_entry *ent)
+{
+ return (ent->lba == 0) && (ent->old_map == 0) && (ent->new_map == 0)
+ && (ent->seq == 0);
+}
+
+/*
+ * Detecting valid log indices: We read a log group (see the comments in btt.h
+ * for a description of a 'log_group' and its 'slots'), and iterate over its
+ * four slots. We expect that a padding slot will be all-zeroes, and use this
+ * to detect a padding slot vs. an actual entry.
+ *
+ * If a log_group is in the initial state, i.e. hasn't been used since the
+ * creation of this BTT layout, it will have three of the four slots with
+ * zeroes. We skip over these log_groups for the detection of log_index. If
+ * all log_groups are in the initial state (i.e. the BTT has never been
+ * written to), it is safe to assume the 'new format' of log entries in slots
+ * (0, 1).
+ */
+static int log_set_indices(struct arena_info *arena)
+{
+ bool idx_set = false, initial_state = true;
+ int ret, log_index[2] = {-1, -1};
+ u32 i, j, next_idx = 0;
+ struct log_group log;
+ u32 pad_count = 0;
+
+ for (i = 0; i < arena->nfree; i++) {
+ ret = btt_log_group_read(arena, i, &log);
+ if (ret < 0)
+ return ret;
+
+ for (j = 0; j < 4; j++) {
+ if (!idx_set) {
+ if (ent_is_padding(&log.ent[j])) {
+ pad_count++;
+ continue;
+ } else {
+ /* Skip if index has been recorded */
+ if ((next_idx == 1) &&
+ (j == log_index[0]))
+ continue;
+ /* valid entry, record index */
+ log_index[next_idx] = j;
+ next_idx++;
+ }
+ if (next_idx == 2) {
+ /* two valid entries found */
+ idx_set = true;
+ } else if (next_idx > 2) {
+ /* too many valid indices */
+ return -ENXIO;
+ }
+ } else {
+ /*
+ * once the indices have been set, just verify
+ * that all subsequent log groups are either in
+ * their initial state or follow the same
+ * indices.
+ */
+ if (j == log_index[0]) {
+ /* entry must be 'valid' */
+ if (ent_is_padding(&log.ent[j]))
+ return -ENXIO;
+ } else if (j == log_index[1]) {
+ ;
+ /*
+ * log_index[1] can be padding if the
+ * lane never got used and it is still
+ * in the initial state (three 'padding'
+ * entries)
+ */
+ } else {
+ /* entry must be invalid (padding) */
+ if (!ent_is_padding(&log.ent[j]))
+ return -ENXIO;
+ }
+ }
+ }
+ /*
+ * If any of the log_groups have more than one valid,
+ * non-padding entry, then the we are no longer in the
+ * initial_state
+ */
+ if (pad_count < 3)
+ initial_state = false;
+ pad_count = 0;
+ }
+
+ if (!initial_state && !idx_set)
+ return -ENXIO;
+
+ /*
+ * If all the entries in the log were in the initial state,
+ * assume new padding scheme
+ */
+ if (initial_state)
+ log_index[1] = 1;
+
+ /*
+ * Only allow the known permutations of log/padding indices,
+ * i.e. (0, 1), and (0, 2)
+ */
+ if ((log_index[0] == 0) && ((log_index[1] == 1) || (log_index[1] == 2)))
+ ; /* known index possibilities */
+ else {
+ dev_err(to_dev(arena), "Found an unknown padding scheme\n");
+ return -ENXIO;
+ }
+
+ arena->log_index[0] = log_index[0];
+ arena->log_index[1] = log_index[1];
+ dev_dbg(to_dev(arena), "log_index_0 = %d\n", log_index[0]);
+ dev_dbg(to_dev(arena), "log_index_1 = %d\n", log_index[1]);
+ return 0;
+}
+
static int btt_rtt_init(struct arena_info *arena)
{
arena->rtt = kcalloc(arena->nfree, sizeof(u32), GFP_KERNEL);
@@ -545,8 +668,7 @@
available -= 2 * BTT_PG_SIZE;
/* The log takes a fixed amount of space based on nfree */
- logsize = roundup(2 * arena->nfree * sizeof(struct log_entry),
- BTT_PG_SIZE);
+ logsize = roundup(arena->nfree * LOG_GRP_SIZE, BTT_PG_SIZE);
available -= logsize;
/* Calculate optimal split between map and data area */
@@ -563,6 +685,10 @@
arena->mapoff = arena->dataoff + datasize;
arena->logoff = arena->mapoff + mapsize;
arena->info2off = arena->logoff + logsize;
+
+ /* Default log indices are (0,1) */
+ arena->log_index[0] = 0;
+ arena->log_index[1] = 1;
return arena;
}
@@ -653,6 +779,13 @@
arena->external_lba_start = cur_nlba;
parse_arena_meta(arena, super, cur_off);
+ ret = log_set_indices(arena);
+ if (ret) {
+ dev_err(to_dev(arena),
+ "Unable to deduce log/padding indices\n");
+ goto out;
+ }
+
ret = btt_freelist_init(arena);
if (ret)
goto out;
diff --git a/drivers/nvdimm/btt.h b/drivers/nvdimm/btt.h
index b2f8651..0f80b6b 100644
--- a/drivers/nvdimm/btt.h
+++ b/drivers/nvdimm/btt.h
@@ -26,6 +26,7 @@
#define MAP_ERR_MASK (1 << MAP_ERR_SHIFT)
#define MAP_LBA_MASK (~((1 << MAP_TRIM_SHIFT) | (1 << MAP_ERR_SHIFT)))
#define MAP_ENT_NORMAL 0xC0000000
+#define LOG_GRP_SIZE sizeof(struct log_group)
#define LOG_ENT_SIZE sizeof(struct log_entry)
#define ARENA_MIN_SIZE (1UL << 24) /* 16 MB */
#define ARENA_MAX_SIZE (1ULL << 39) /* 512 GB */
@@ -44,12 +45,52 @@
INIT_READY
};
+/*
+ * A log group represents one log 'lane', and consists of four log entries.
+ * Two of the four entries are valid entries, and the remaining two are
+ * padding. Due to an old bug in the padding location, we need to perform a
+ * test to determine the padding scheme being used, and use that scheme
+ * thereafter.
+ *
+ * In kernels prior to 4.15, 'log group' would have actual log entries at
+ * indices (0, 2) and padding at indices (1, 3), where as the correct/updated
+ * format has log entries at indices (0, 1) and padding at indices (2, 3).
+ *
+ * Old (pre 4.15) format:
+ * +-----------------+-----------------+
+ * | ent[0] | ent[1] |
+ * | 16B | 16B |
+ * | lba/old/new/seq | pad |
+ * +-----------------------------------+
+ * | ent[2] | ent[3] |
+ * | 16B | 16B |
+ * | lba/old/new/seq | pad |
+ * +-----------------+-----------------+
+ *
+ * New format:
+ * +-----------------+-----------------+
+ * | ent[0] | ent[1] |
+ * | 16B | 16B |
+ * | lba/old/new/seq | lba/old/new/seq |
+ * +-----------------------------------+
+ * | ent[2] | ent[3] |
+ * | 16B | 16B |
+ * | pad | pad |
+ * +-----------------+-----------------+
+ *
+ * We detect during start-up which format is in use, and set
+ * arena->log_index[(0, 1)] with the detected format.
+ */
+
struct log_entry {
__le32 lba;
__le32 old_map;
__le32 new_map;
__le32 seq;
- __le64 padding[2];
+};
+
+struct log_group {
+ struct log_entry ent[4];
};
struct btt_sb {
@@ -117,6 +158,7 @@
* @list: List head for list of arenas
* @debugfs_dir: Debugfs dentry
* @flags: Arena flags - may signify error states.
+ * @log_index: Indices of the valid log entries in a log_group
*
* arena_info is a per-arena handle. Once an arena is narrowed down for an
* IO, this struct is passed around for the duration of the IO.
@@ -147,6 +189,7 @@
struct dentry *debugfs_dir;
/* Arena flags */
u32 flags;
+ int log_index[2];
};
/**
diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c
index b5305f0..c25d7dc 100644
--- a/drivers/nvmem/qfprom.c
+++ b/drivers/nvmem/qfprom.c
@@ -98,7 +98,26 @@
.of_match_table = qfprom_of_match,
},
};
-module_platform_driver(qfprom_driver);
+
+static int __init qfprom_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&qfprom_driver);
+ if (ret != 0)
+ pr_err("Failed to register qfprom driver\n");
+
+ return ret;
+}
+
+static void __exit qfprom_exit(void)
+{
+ return platform_driver_unregister(&qfprom_driver);
+}
+
+subsys_initcall(qfprom_init);
+module_exit(qfprom_exit);
+
MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>");
MODULE_DESCRIPTION("Qualcomm QFPROM driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index 69c8cb0..b897813 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -50,8 +50,7 @@
#include <linux/ipc_logging.h>
#include <linux/msm_pcie.h>
-#define PCIE_VENDOR_ID_RCP 0x17cb
-#define PCIE_DEVICE_ID_RCP 0x0106
+#define PCIE_VENDOR_ID_QCOM 0x17cb
#define PCIE20_L1SUB_CONTROL1 0x1E4
#define PCIE20_PARF_DBI_BASE_ADDR 0x350
@@ -63,7 +62,6 @@
#define PCIE_N_SW_RESET(n) (PCS_PORT(n) + 0x00)
#define PCIE_N_POWER_DOWN_CONTROL(n) (PCS_PORT(n) + 0x04)
-#define PCIE_N_PCS_STATUS(n) (PCS_PORT(n) + 0x174)
#define PCIE_GEN3_COM_INTEGLOOP_GAIN1_MODE0 0x0154
#define PCIE_GEN3_L0_DRVR_CTRL0 0x080c
@@ -71,7 +69,6 @@
#define PCIE_GEN3_L0_BIST_ERR_CNT1_STATUS 0x08a8
#define PCIE_GEN3_L0_BIST_ERR_CNT2_STATUS 0x08ac
#define PCIE_GEN3_L0_DEBUG_BUS_STATUS4 0x08bc
-#define PCIE_GEN3_PCIE_PHY_PCS_STATUS 0x1aac
#define PCIE20_PARF_SYS_CTRL 0x00
#define PCIE20_PARF_PM_CTRL 0x20
@@ -590,6 +587,7 @@
uint32_t switch_latency;
uint32_t wr_halt_size;
uint32_t slv_addr_space_size;
+ uint32_t phy_status_offset;
uint32_t cpl_timeout;
uint32_t current_bdf;
uint32_t perst_delay_us_min;
@@ -996,11 +994,7 @@
static bool pcie_phy_is_ready(struct msm_pcie_dev_t *dev)
{
- u32 pos = (dev->max_link_speed == GEN2_SPEED) ?
- PCIE_N_PCS_STATUS(dev->rc_idx) :
- PCIE_GEN3_PCIE_PHY_PCS_STATUS;
-
- if (readl_relaxed(dev->phy + pos) & BIT(6))
+ if (readl_relaxed(dev->phy + dev->phy_status_offset) & BIT(6))
return false;
else
return true;
@@ -1258,6 +1252,8 @@
dev->wr_halt_size);
PCIE_DBG_FS(dev, "slv_addr_space_size: 0x%x\n",
dev->slv_addr_space_size);
+ PCIE_DBG_FS(dev, "phy_status_offset: 0x%x\n",
+ dev->phy_status_offset);
PCIE_DBG_FS(dev, "cpl_timeout: 0x%x\n",
dev->cpl_timeout);
PCIE_DBG_FS(dev, "current_bdf: 0x%x\n",
@@ -5812,6 +5808,21 @@
"RC%d: slv-addr-space-size: 0x%x.\n",
rc_idx, msm_pcie_dev[rc_idx].slv_addr_space_size);
+ msm_pcie_dev[rc_idx].phy_status_offset = 0;
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,phy-status-offset",
+ &msm_pcie_dev[rc_idx].phy_status_offset);
+ if (ret) {
+ PCIE_ERR(&msm_pcie_dev[rc_idx],
+ "RC%d: failed to get PCIe PHY status offset.\n",
+ rc_idx);
+ goto decrease_rc_num;
+ } else {
+ PCIE_DBG(&msm_pcie_dev[rc_idx],
+ "RC%d: phy-status-offset: 0x%x.\n",
+ rc_idx, msm_pcie_dev[rc_idx].phy_status_offset);
+ }
+
msm_pcie_dev[rc_idx].cpl_timeout = 0;
ret = of_property_read_u32((&pdev->dev)->of_node,
"qcom,cpl-timeout",
@@ -6208,10 +6219,10 @@
struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev->bus);
PCIE_DBG(pcie_dev, "hdr_type %d\n", dev->hdr_type);
- if (dev->hdr_type == 1)
+ if (pci_is_root_bus(dev->bus))
dev->class = (dev->class & 0xff) | (PCI_CLASS_BRIDGE_PCI << 8);
}
-DECLARE_PCI_FIXUP_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
+DECLARE_PCI_FIXUP_EARLY(PCIE_VENDOR_ID_QCOM, PCI_ANY_ID,
msm_pcie_fixup_early);
/* Suspend the PCIe link */
@@ -6294,7 +6305,8 @@
PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx);
- if (pcie_dev->link_status != MSM_PCIE_LINK_ENABLED)
+ if (pcie_dev->link_status != MSM_PCIE_LINK_ENABLED ||
+ !pci_is_root_bus(dev->bus))
return;
spin_lock_irqsave(&pcie_dev->cfg_lock,
@@ -6319,7 +6331,7 @@
mutex_unlock(&pcie_dev->recovery_lock);
}
-DECLARE_PCI_FIXUP_SUSPEND(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
+DECLARE_PCI_FIXUP_SUSPEND(PCIE_VENDOR_ID_QCOM, PCI_ANY_ID,
msm_pcie_fixup_suspend);
/* Resume the PCIe link */
@@ -6393,7 +6405,7 @@
PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx);
if ((pcie_dev->link_status != MSM_PCIE_LINK_DISABLED) ||
- pcie_dev->user_suspend)
+ pcie_dev->user_suspend || !pci_is_root_bus(dev->bus))
return;
mutex_lock(&pcie_dev->recovery_lock);
@@ -6405,7 +6417,7 @@
mutex_unlock(&pcie_dev->recovery_lock);
}
-DECLARE_PCI_FIXUP_RESUME(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
+DECLARE_PCI_FIXUP_RESUME(PCIE_VENDOR_ID_QCOM, PCI_ANY_ID,
msm_pcie_fixup_resume);
static void msm_pcie_fixup_resume_early(struct pci_dev *dev)
@@ -6416,7 +6428,7 @@
PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx);
if ((pcie_dev->link_status != MSM_PCIE_LINK_DISABLED) ||
- pcie_dev->user_suspend)
+ pcie_dev->user_suspend || !pci_is_root_bus(dev->bus))
return;
mutex_lock(&pcie_dev->recovery_lock);
@@ -6427,7 +6439,7 @@
mutex_unlock(&pcie_dev->recovery_lock);
}
-DECLARE_PCI_FIXUP_RESUME_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCIE_VENDOR_ID_QCOM, PCI_ANY_ID,
msm_pcie_fixup_resume_early);
int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user,
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index a268f4d..48a365e 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -395,6 +395,10 @@
if (ret)
return ERR_PTR(-ENODEV);
+ /* This phy type handled by the usb-phy subsystem for now */
+ if (of_device_is_compatible(args.np, "usb-nop-xceiv"))
+ return ERR_PTR(-ENODEV);
+
mutex_lock(&phy_provider_mutex);
phy_provider = of_phy_provider_lookup(args.np);
if (IS_ERR(phy_provider) || !try_module_get(phy_provider->owner)) {
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index d39a17f..6515ce4 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -89,6 +89,16 @@
Technologies Inc MSM8953 platform.
If unsure say N.
+config PINCTRL_MSM8937
+ tristate "Qualcomm Technologies Inc MSM8937 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 MSM8937 platform.
+ If unsure say N.
+
config PINCTRL_SDM845
tristate "Qualcomm Technologies Inc SDM845 pin controller driver"
depends on GPIOLIB && OF
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index d92db11..db71677 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -12,6 +12,7 @@
obj-$(CONFIG_PINCTRL_QDF2XXX) += pinctrl-qdf2xxx.o
obj-$(CONFIG_PINCTRL_MDM9615) += pinctrl-mdm9615.o
obj-$(CONFIG_PINCTRL_MSM8953) += pinctrl-msm8953.o
+obj-$(CONFIG_PINCTRL_MSM8937) += pinctrl-msm8937.o
obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-gpio.o
obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-mpp.o
obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-gpio.o
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c
index bf95849..1441678 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.c
+++ b/drivers/pinctrl/qcom/pinctrl-msm.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2013, Sony Mobile Communications AB.
- * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -70,6 +70,7 @@
const struct msm_pinctrl_soc_data *soc;
void __iomem *regs;
+ void __iomem *pdc_regs;
};
static int msm_get_groups_count(struct pinctrl_dev *pctldev)
@@ -777,6 +778,102 @@
.irq_set_wake = msm_gpio_irq_set_wake,
};
+static struct irq_chip msm_dirconn_irq_chip;
+
+static void msm_gpio_dirconn_handler(struct irq_desc *desc)
+{
+ struct irq_data *irqd = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+
+ chained_irq_enter(chip, desc);
+ generic_handle_irq(irqd->irq);
+ chained_irq_exit(chip, desc);
+}
+
+static void setup_pdc_gpio(struct irq_domain *domain,
+ unsigned int parent_irq, unsigned int gpio)
+{
+ int irq;
+
+ if (gpio != 0) {
+ irq = irq_find_mapping(domain, gpio);
+ irq_set_parent(irq, parent_irq);
+ irq_set_chip(irq, &msm_dirconn_irq_chip);
+ irq_set_handler_data(parent_irq, irq_get_irq_data(irq));
+ }
+
+ __irq_set_handler(parent_irq, msm_gpio_dirconn_handler, false, NULL);
+}
+
+static void request_dc_interrupt(struct irq_domain *domain,
+ struct irq_domain *parent, irq_hw_number_t hwirq,
+ unsigned int gpio)
+{
+ struct irq_fwspec fwspec;
+ unsigned int parent_irq;
+
+ fwspec.fwnode = parent->fwnode;
+ fwspec.param[0] = 0; /* SPI */
+ fwspec.param[1] = hwirq;
+ fwspec.param[2] = IRQ_TYPE_NONE;
+ fwspec.param_count = 3;
+
+ parent_irq = irq_create_fwspec_mapping(&fwspec);
+
+ setup_pdc_gpio(domain, parent_irq, gpio);
+}
+
+/**
+ * gpio_muxed_to_pdc: Mux the GPIO to a PDC IRQ
+ *
+ * @pdc_domain: the PDC's domain
+ * @d: the GPIO's IRQ data
+ *
+ * Find a free PDC port for the GPIO and map the GPIO's mux information to the
+ * PDC registers; so the GPIO can be used a wakeup source.
+ */
+static void gpio_muxed_to_pdc(struct irq_domain *pdc_domain, struct irq_data *d)
+{
+ int i, j;
+ unsigned int mux;
+ struct irq_desc *desc = irq_data_to_desc(d);
+ struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ unsigned int gpio = d->hwirq;
+ struct msm_pinctrl *pctrl;
+ unsigned int irq;
+
+ if (!gc || !parent_data)
+ return;
+
+ pctrl = gpiochip_get_data(gc);
+
+ for (i = 0; i < pctrl->soc->n_gpio_mux_in; i++) {
+ if (gpio != pctrl->soc->gpio_mux_in[i].gpio)
+ continue;
+ mux = pctrl->soc->gpio_mux_in[i].mux;
+ for (j = 0; j < pctrl->soc->n_pdc_mux_out; j++) {
+ struct msm_pdc_mux_output *pdc_out =
+ &pctrl->soc->pdc_mux_out[j];
+
+ if (pdc_out->mux == mux)
+ break;
+ if (pdc_out->mux)
+ continue;
+ pdc_out->mux = gpio;
+ irq = irq_find_mapping(pdc_domain, pdc_out->hwirq + 32);
+ /* setup the IRQ parent for the GPIO */
+ setup_pdc_gpio(pctrl->chip.irqdomain, irq, gpio);
+ /* program pdc select grp register */
+ writel_relaxed((mux & 0x3F), pctrl->pdc_regs +
+ (0x14 * j));
+ break;
+ }
+ /* We have no more PDC port available */
+ WARN_ON(j == pctrl->soc->n_pdc_mux_out);
+ }
+}
+
static bool is_gpio_dual_edge(struct irq_data *d, irq_hw_number_t *dir_conn_irq)
{
struct irq_desc *desc = irq_data_to_desc(d);
@@ -797,6 +894,17 @@
return true;
}
}
+
+ for (i = 0; i < pctrl->soc->n_pdc_mux_out; i++) {
+ struct msm_pdc_mux_output *dir_conn =
+ &pctrl->soc->pdc_mux_out[i];
+
+ if (dir_conn->mux == d->hwirq && (dir_conn->hwirq + 32)
+ != parent_data->hwirq) {
+ *dir_conn_irq = dir_conn->hwirq + 32;
+ return true;
+ }
+ }
return false;
}
@@ -814,13 +922,48 @@
irq_get_irq_data(irq_find_mapping(parent_data->domain,
dir_conn_irq));
- if (dir_conn_data && dir_conn_data->chip->irq_mask)
+ if (!dir_conn_data)
+ return;
+ if (dir_conn_data->chip->irq_mask)
dir_conn_data->chip->irq_mask(dir_conn_data);
}
+
if (parent_data->chip->irq_mask)
parent_data->chip->irq_mask(parent_data);
}
+static void msm_dirconn_irq_enable(struct irq_data *d)
+{
+ struct irq_desc *desc = irq_data_to_desc(d);
+ struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+ irq_hw_number_t dir_conn_irq = 0;
+
+ if (!parent_data)
+ return;
+
+ if (is_gpio_dual_edge(d, &dir_conn_irq)) {
+ struct irq_data *dir_conn_data =
+ irq_get_irq_data(irq_find_mapping(parent_data->domain,
+ dir_conn_irq));
+
+ if (dir_conn_data &&
+ dir_conn_data->chip->irq_set_irqchip_state)
+ dir_conn_data->chip->irq_set_irqchip_state(
+ dir_conn_data,
+ IRQCHIP_STATE_PENDING, 0);
+
+ if (dir_conn_data && dir_conn_data->chip->irq_unmask)
+ dir_conn_data->chip->irq_unmask(dir_conn_data);
+ }
+
+ if (parent_data->chip->irq_set_irqchip_state)
+ parent_data->chip->irq_set_irqchip_state(parent_data,
+ IRQCHIP_STATE_PENDING, 0);
+
+ if (parent_data->chip->irq_unmask)
+ parent_data->chip->irq_unmask(parent_data);
+}
+
static void msm_dirconn_irq_unmask(struct irq_data *d)
{
struct irq_desc *desc = irq_data_to_desc(d);
@@ -835,7 +978,9 @@
irq_get_irq_data(irq_find_mapping(parent_data->domain,
dir_conn_irq));
- if (dir_conn_data && dir_conn_data->chip->irq_unmask)
+ if (!dir_conn_data)
+ return;
+ if (dir_conn_data->chip->irq_unmask)
dir_conn_data->chip->irq_unmask(dir_conn_data);
}
if (parent_data->chip->irq_unmask)
@@ -1058,6 +1203,7 @@
static struct irq_chip msm_dirconn_irq_chip = {
.name = "msmgpio-dc",
.irq_mask = msm_dirconn_irq_mask,
+ .irq_enable = msm_dirconn_irq_enable,
.irq_unmask = msm_dirconn_irq_unmask,
.irq_eoi = msm_dirconn_irq_eoi,
.irq_ack = msm_dirconn_irq_ack,
@@ -1103,57 +1249,53 @@
chained_irq_exit(chip, desc);
}
-static void msm_gpio_dirconn_handler(struct irq_desc *desc)
-{
- struct irq_data *irqd = irq_desc_get_handler_data(desc);
- struct irq_chip *chip = irq_desc_get_chip(desc);
-
- chained_irq_enter(chip, desc);
- generic_handle_irq(irqd->irq);
- chained_irq_exit(chip, desc);
-}
-
static void msm_gpio_setup_dir_connects(struct msm_pinctrl *pctrl)
{
struct device_node *parent_node;
- struct irq_domain *parent_domain;
- struct irq_fwspec fwspec;
+ struct irq_domain *pdc_domain;
unsigned int i;
parent_node = of_irq_find_parent(pctrl->dev->of_node);
-
if (!parent_node)
return;
- parent_domain = irq_find_host(parent_node);
- if (!parent_domain)
+ pdc_domain = irq_find_host(parent_node);
+ if (!pdc_domain)
return;
- fwspec.fwnode = parent_domain->fwnode;
for (i = 0; i < pctrl->soc->n_dir_conns; i++) {
const struct msm_dir_conn *dirconn = &pctrl->soc->dir_conn[i];
- unsigned int parent_irq;
- int irq;
- fwspec.param[0] = 0; /* SPI */
- fwspec.param[1] = dirconn->hwirq;
- fwspec.param[2] = IRQ_TYPE_NONE;
- fwspec.param_count = 3;
- parent_irq = irq_create_fwspec_mapping(&fwspec);
+ request_dc_interrupt(pctrl->chip.irqdomain, pdc_domain,
+ dirconn->hwirq, dirconn->gpio);
+ }
- if (dirconn->gpio != 0) {
- irq = irq_find_mapping(pctrl->chip.irqdomain,
- dirconn->gpio);
+ for (i = 0; i < pctrl->soc->n_pdc_mux_out; i++) {
+ struct msm_pdc_mux_output *pdc_out =
+ &pctrl->soc->pdc_mux_out[i];
- irq_set_parent(irq, parent_irq);
- irq_set_chip(irq, &msm_dirconn_irq_chip);
- __irq_set_handler(parent_irq, msm_gpio_dirconn_handler,
- false, NULL);
- irq_set_handler_data(parent_irq, irq_get_irq_data(irq));
- } else {
- __irq_set_handler(parent_irq, msm_gpio_dirconn_handler,
- false, NULL);
- }
+ request_dc_interrupt(pctrl->chip.irqdomain, pdc_domain,
+ pdc_out->hwirq, 0);
+ }
+
+ /*
+ * Statically choose the GPIOs for mapping to PDC. Dynamic mux mapping
+ * is very difficult.
+ */
+ for (i = 0; i < pctrl->soc->n_pdc_mux_out; i++) {
+ unsigned int irq;
+ struct irq_data *d;
+ struct msm_gpio_mux_input *gpio_in =
+ &pctrl->soc->gpio_mux_in[i];
+ if (!gpio_in->init)
+ continue;
+
+ irq = irq_find_mapping(pctrl->chip.irqdomain, gpio_in->gpio);
+ d = irq_get_irq_data(irq);
+ if (!d)
+ continue;
+
+ gpio_muxed_to_pdc(pdc_domain, d);
}
}
@@ -1263,6 +1405,9 @@
if (IS_ERR(pctrl->regs))
return PTR_ERR(pctrl->regs);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ pctrl->pdc_regs = devm_ioremap_resource(&pdev->dev, res);
+
msm_pinctrl_setup_pm_reset(pctrl);
pctrl->irq = platform_get_irq(pdev, 0);
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h
index 1c6df2f..9fc6660 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.h
+++ b/drivers/pinctrl/qcom/pinctrl-msm.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2013, Sony Mobile Communications AB.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -99,13 +100,35 @@
unsigned intr_detection_bit:5;
unsigned intr_detection_width:5;
unsigned dir_conn_en_bit:8;
-}
+};
+
+/**
+ * struct msm_gpio_mux_input - Map GPIO to Mux pin
+ * @mux:: The mux pin to which the GPIO is connected to
+ * @gpio: GPIO pin number
+ * @init: Setup PDC connection at probe
+ */
+struct msm_gpio_mux_input {
+ unsigned int mux;
+ unsigned int gpio;
+ bool init;
+};
+
+/**
+ * struct msm_pdc_mux_output - GPIO mux pin to PDC port
+ * @mux: GPIO mux pin number
+ * @hwirq: The PDC port (hwirq) that GPIO is connected to
+ */
+struct msm_pdc_mux_output {
+ unsigned int mux;
+ irq_hw_number_t hwirq;
+};
/**
* struct msm_dir_conn - Direct GPIO connect configuration
* @gpio: GPIO pin number
* @hwirq: The GIC interrupt that the pin is connected to
- */;
+ */
struct msm_dir_conn {
unsigned int gpio;
irq_hw_number_t hwirq;
@@ -122,8 +145,12 @@
* @ngpio: The number of pingroups the driver should expose as GPIOs.
* @dir_conn: An array describing all the pins directly connected to GIC.
* @ndirconns: The number of pins directly connected to GIC
- * @dir_conn_offsets: Direct connect register offsets for each tile.
* @dir_conn_irq_base: Direct connect interrupt base register for kpss.
+ * @gpio_mux_in: Map of GPIO pin to the hwirq.
+ * @n_gpioc_mux_in: The number of entries in @pdc_mux_in.
+ * @pdc_mux_out: Map of GPIO mux to PDC port.
+ * @n_pdc_mux_out: The number of entries in @pdc_mux_out.
+ * @n_pdc_offset: The offset for the PDC mux pins
*/
struct msm_pinctrl_soc_data {
const struct pinctrl_pin_desc *pins;
@@ -136,6 +163,11 @@
const struct msm_dir_conn *dir_conn;
unsigned int n_dir_conns;
unsigned int dir_conn_irq_base;
+ struct msm_pdc_mux_output *pdc_mux_out;
+ unsigned int n_pdc_mux_out;
+ struct msm_gpio_mux_input *gpio_mux_in;
+ unsigned int n_gpio_mux_in;
+ unsigned int n_pdc_mux_offset;
};
int msm_pinctrl_probe(struct platform_device *pdev,
diff --git a/drivers/pinctrl/qcom/pinctrl-msm8937.c b/drivers/pinctrl/qcom/pinctrl-msm8937.c
new file mode 100644
index 0000000..2b72c54
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-msm8937.c
@@ -0,0 +1,1479 @@
+/* Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/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 = 4, \
+ .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_raw_status_bit = -1, \
+ .intr_polarity_bit = -1, \
+ .intr_detection_bit = -1, \
+ .intr_detection_width = -1, \
+ }
+static const struct pinctrl_pin_desc msm8937_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, "GPIO_100"),
+ PINCTRL_PIN(101, "GPIO_101"),
+ PINCTRL_PIN(102, "GPIO_102"),
+ PINCTRL_PIN(103, "GPIO_103"),
+ PINCTRL_PIN(104, "GPIO_104"),
+ PINCTRL_PIN(105, "GPIO_105"),
+ PINCTRL_PIN(106, "GPIO_106"),
+ PINCTRL_PIN(107, "GPIO_107"),
+ PINCTRL_PIN(108, "GPIO_108"),
+ PINCTRL_PIN(109, "GPIO_109"),
+ PINCTRL_PIN(110, "GPIO_110"),
+ PINCTRL_PIN(111, "GPIO_111"),
+ PINCTRL_PIN(112, "GPIO_112"),
+ PINCTRL_PIN(113, "GPIO_113"),
+ PINCTRL_PIN(114, "GPIO_114"),
+ PINCTRL_PIN(115, "GPIO_115"),
+ PINCTRL_PIN(116, "GPIO_116"),
+ PINCTRL_PIN(117, "GPIO_117"),
+ PINCTRL_PIN(118, "GPIO_118"),
+ PINCTRL_PIN(119, "GPIO_119"),
+ PINCTRL_PIN(120, "GPIO_120"),
+ PINCTRL_PIN(121, "GPIO_121"),
+ PINCTRL_PIN(122, "GPIO_122"),
+ PINCTRL_PIN(123, "GPIO_123"),
+ PINCTRL_PIN(124, "GPIO_124"),
+ PINCTRL_PIN(125, "GPIO_125"),
+ PINCTRL_PIN(126, "GPIO_126"),
+ PINCTRL_PIN(127, "GPIO_127"),
+ PINCTRL_PIN(128, "GPIO_128"),
+ PINCTRL_PIN(129, "GPIO_129"),
+ PINCTRL_PIN(130, "GPIO_130"),
+ PINCTRL_PIN(131, "GPIO_131"),
+ PINCTRL_PIN(132, "GPIO_132"),
+ PINCTRL_PIN(133, "GPIO_133"),
+ PINCTRL_PIN(134, "SDC1_CLK"),
+ PINCTRL_PIN(135, "SDC1_CMD"),
+ PINCTRL_PIN(136, "SDC1_DATA"),
+ PINCTRL_PIN(137, "SDC1_RCLK"),
+ PINCTRL_PIN(138, "SDC2_CLK"),
+ PINCTRL_PIN(139, "SDC2_CMD"),
+ PINCTRL_PIN(140, "SDC2_DATA"),
+ PINCTRL_PIN(141, "QDSD_CLK"),
+ PINCTRL_PIN(142, "QDSD_CMD"),
+ PINCTRL_PIN(143, "QDSD_DATA0"),
+ PINCTRL_PIN(144, "QDSD_DATA1"),
+ PINCTRL_PIN(145, "QDSD_DATA2"),
+ PINCTRL_PIN(146, "QDSD_DATA3"),
+};
+
+#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);
+DECLARE_MSM_GPIO_PINS(100);
+DECLARE_MSM_GPIO_PINS(101);
+DECLARE_MSM_GPIO_PINS(102);
+DECLARE_MSM_GPIO_PINS(103);
+DECLARE_MSM_GPIO_PINS(104);
+DECLARE_MSM_GPIO_PINS(105);
+DECLARE_MSM_GPIO_PINS(106);
+DECLARE_MSM_GPIO_PINS(107);
+DECLARE_MSM_GPIO_PINS(108);
+DECLARE_MSM_GPIO_PINS(109);
+DECLARE_MSM_GPIO_PINS(110);
+DECLARE_MSM_GPIO_PINS(111);
+DECLARE_MSM_GPIO_PINS(112);
+DECLARE_MSM_GPIO_PINS(113);
+DECLARE_MSM_GPIO_PINS(114);
+DECLARE_MSM_GPIO_PINS(115);
+DECLARE_MSM_GPIO_PINS(116);
+DECLARE_MSM_GPIO_PINS(117);
+DECLARE_MSM_GPIO_PINS(118);
+DECLARE_MSM_GPIO_PINS(119);
+DECLARE_MSM_GPIO_PINS(120);
+DECLARE_MSM_GPIO_PINS(121);
+DECLARE_MSM_GPIO_PINS(122);
+DECLARE_MSM_GPIO_PINS(123);
+DECLARE_MSM_GPIO_PINS(124);
+DECLARE_MSM_GPIO_PINS(125);
+DECLARE_MSM_GPIO_PINS(126);
+DECLARE_MSM_GPIO_PINS(127);
+DECLARE_MSM_GPIO_PINS(128);
+DECLARE_MSM_GPIO_PINS(129);
+DECLARE_MSM_GPIO_PINS(130);
+DECLARE_MSM_GPIO_PINS(131);
+DECLARE_MSM_GPIO_PINS(132);
+DECLARE_MSM_GPIO_PINS(133);
+
+static const unsigned int sdc1_clk_pins[] = { 134 };
+static const unsigned int sdc1_cmd_pins[] = { 135 };
+static const unsigned int sdc1_data_pins[] = { 136 };
+static const unsigned int sdc1_rclk_pins[] = { 137 };
+static const unsigned int sdc2_clk_pins[] = { 138 };
+static const unsigned int sdc2_cmd_pins[] = { 139 };
+static const unsigned int sdc2_data_pins[] = { 140 };
+static const unsigned int qdsd_clk_pins[] = { 141 };
+static const unsigned int qdsd_cmd_pins[] = { 142 };
+static const unsigned int qdsd_data0_pins[] = { 143 };
+static const unsigned int qdsd_data1_pins[] = { 144 };
+static const unsigned int qdsd_data2_pins[] = { 145 };
+static const unsigned int qdsd_data3_pins[] = { 146 };
+
+enum msm8937_functions {
+ msm_mux_qdss_tracedata_b,
+ msm_mux_blsp_uart1,
+ msm_mux_gpio,
+ msm_mux_blsp_spi1,
+ msm_mux_adsp_ext,
+ msm_mux_blsp_i2c1,
+ msm_mux_prng_rosc,
+ msm_mux_qdss_cti_trig_out_b0,
+ msm_mux_blsp_spi2,
+ msm_mux_blsp_uart2,
+ msm_mux_blsp_uart3,
+ msm_mux_pbs0,
+ msm_mux_pbs1,
+ msm_mux_pwr_modem_enabled_b,
+ msm_mux_blsp_i2c3,
+ msm_mux_gcc_gp2_clk_b,
+ msm_mux_ldo_update,
+ msm_mux_atest_combodac_to_gpio_native,
+ msm_mux_ldo_en,
+ msm_mux_blsp_i2c2,
+ msm_mux_gcc_gp1_clk_b,
+ msm_mux_pbs2,
+ msm_mux_atest_gpsadc_dtest0_native,
+ msm_mux_blsp_spi3,
+ msm_mux_gcc_gp3_clk_b,
+ msm_mux_blsp_spi4,
+ msm_mux_blsp_uart4,
+ msm_mux_sec_mi2s,
+ msm_mux_pwr_nav_enabled_b,
+ msm_mux_codec_mad,
+ msm_mux_pwr_crypto_enabled_b,
+ msm_mux_blsp_i2c4,
+ msm_mux_blsp_spi5,
+ msm_mux_blsp_uart5,
+ msm_mux_qdss_traceclk_a,
+ msm_mux_atest_bbrx1,
+ msm_mux_m_voc,
+ msm_mux_qdss_cti_trig_in_a0,
+ msm_mux_qdss_cti_trig_in_b0,
+ msm_mux_blsp_i2c6,
+ msm_mux_qdss_traceclk_b,
+ msm_mux_atest_wlan0,
+ msm_mux_atest_wlan1,
+ msm_mux_atest_bbrx0,
+ msm_mux_blsp_i2c5,
+ msm_mux_qdss_tracectl_a,
+ msm_mux_atest_gpsadc_dtest1_native,
+ msm_mux_qdss_tracedata_a,
+ msm_mux_blsp_spi6,
+ msm_mux_blsp_uart6,
+ msm_mux_qdss_tracectl_b,
+ msm_mux_mdp_vsync,
+ msm_mux_pri_mi2s_mclk_a,
+ msm_mux_sec_mi2s_mclk_a,
+ msm_mux_cam_mclk,
+ msm_mux_cci_i2c,
+ msm_mux_pwr_modem_enabled_a,
+ msm_mux_cci_timer0,
+ msm_mux_cci_timer1,
+ msm_mux_cam1_standby,
+ msm_mux_pwr_nav_enabled_a,
+ msm_mux_cam1_rst,
+ msm_mux_pwr_crypto_enabled_a,
+ msm_mux_forced_usb,
+ msm_mux_qdss_cti_trig_out_b1,
+ msm_mux_cam2_rst,
+ msm_mux_webcam_standby,
+ msm_mux_cci_async,
+ msm_mux_webcam_rst,
+ msm_mux_ov_ldo,
+ msm_mux_sd_write,
+ msm_mux_accel_int,
+ msm_mux_gcc_gp1_clk_a,
+ msm_mux_alsp_int,
+ msm_mux_gcc_gp2_clk_a,
+ msm_mux_mag_int,
+ msm_mux_gcc_gp3_clk_a,
+ msm_mux_blsp6_spi,
+ msm_mux_fp_int,
+ msm_mux_qdss_cti_trig_in_b1,
+ msm_mux_uim_batt,
+ msm_mux_cam2_standby,
+ msm_mux_uim1_data,
+ msm_mux_uim1_clk,
+ msm_mux_uim1_reset,
+ msm_mux_uim1_present,
+ msm_mux_uim2_data,
+ msm_mux_uim2_clk,
+ msm_mux_uim2_reset,
+ msm_mux_uim2_present,
+ msm_mux_sensor_rst,
+ msm_mux_mipi_dsi0,
+ msm_mux_smb_int,
+ msm_mux_cam0_ldo,
+ msm_mux_us_euro,
+ msm_mux_atest_char3,
+ msm_mux_dbg_out,
+ msm_mux_bimc_dte0,
+ msm_mux_ts_resout,
+ msm_mux_ts_sample,
+ msm_mux_sec_mi2s_mclk_b,
+ msm_mux_pri_mi2s,
+ msm_mux_sdcard_det,
+ msm_mux_atest_char1,
+ msm_mux_ebi_cdc,
+ msm_mux_audio_reset,
+ msm_mux_atest_char0,
+ msm_mux_audio_ref,
+ msm_mux_cdc_pdm0,
+ msm_mux_pri_mi2s_mclk_b,
+ msm_mux_lpass_slimbus,
+ msm_mux_lpass_slimbus0,
+ msm_mux_lpass_slimbus1,
+ msm_mux_codec_int1,
+ msm_mux_codec_int2,
+ msm_mux_wcss_bt,
+ msm_mux_atest_char2,
+ msm_mux_ebi_ch0,
+ msm_mux_wcss_wlan2,
+ msm_mux_wcss_wlan1,
+ msm_mux_wcss_wlan0,
+ msm_mux_wcss_wlan,
+ msm_mux_wcss_fm,
+ msm_mux_ext_lpass,
+ msm_mux_cri_trng,
+ msm_mux_cri_trng1,
+ msm_mux_cri_trng0,
+ msm_mux_blsp_spi7,
+ msm_mux_blsp_uart7,
+ msm_mux_pri_mi2s_ws,
+ msm_mux_blsp_i2c7,
+ msm_mux_gcc_tlmm,
+ msm_mux_dmic0_clk,
+ msm_mux_dmic0_data,
+ msm_mux_key_volp,
+ msm_mux_qdss_cti_trig_in_a1,
+ msm_mux_us_emitter,
+ msm_mux_wsa_irq,
+ msm_mux_wsa_io,
+ msm_mux_wsa_reset,
+ msm_mux_blsp_spi8,
+ msm_mux_blsp_uart8,
+ msm_mux_blsp_i2c8,
+ msm_mux_gcc_plltest,
+ msm_mux_nav_pps_in_a,
+ msm_mux_pa_indicator,
+ msm_mux_modem_tsync,
+ msm_mux_nav_tsync,
+ msm_mux_nav_pps_in_b,
+ msm_mux_nav_pps,
+ msm_mux_gsm0_tx,
+ msm_mux_atest_char,
+ msm_mux_atest_tsens,
+ msm_mux_bimc_dte1,
+ msm_mux_ssbi_wtr1,
+ msm_mux_fp_gpio,
+ msm_mux_coex_uart,
+ msm_mux_key_snapshot,
+ msm_mux_key_focus,
+ msm_mux_nfc_pwr,
+ msm_mux_blsp8_spi,
+ msm_mux_qdss_cti_trig_out_a0,
+ msm_mux_qdss_cti_trig_out_a1,
+ msm_mux_NA,
+};
+
+static const char * const qdss_tracedata_b_groups[] = {
+ "gpio0", "gpio1", "gpio6", "gpio7", "gpio12", "gpio13", "gpio23",
+ "gpio42", "gpio43", "gpio44", "gpio47", "gpio66", "gpio86", "gpio87",
+ "gpio88", "gpio92",
+};
+static const char * const blsp_uart1_groups[] = {
+ "gpio0", "gpio1", "gpio2", "gpio3",
+};
+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", "gpio25", "gpio26", "gpio27", "gpio28",
+ "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35",
+ "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42",
+ "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49",
+ "gpio50", "gpio51", "gpio52", "gpio53", "gpio54", "gpio55", "gpio56",
+ "gpio57", "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", "gpio100", "gpio101", "gpio102", "gpio103", "gpio104",
+ "gpio105", "gpio106", "gpio107", "gpio108", "gpio109", "gpio110",
+ "gpio111", "gpio112", "gpio113", "gpio114", "gpio115", "gpio116",
+ "gpio117", "gpio118", "gpio119", "gpio120", "gpio121", "gpio122",
+ "gpio123", "gpio124", "gpio125", "gpio126", "gpio127", "gpio128",
+ "gpio129", "gpio130", "gpio131", "gpio132", "gpio133",
+};
+static const char * const blsp_spi1_groups[] = {
+ "gpio0", "gpio1", "gpio2", "gpio3",
+};
+static const char * const adsp_ext_groups[] = {
+ "gpio1",
+};
+static const char * const blsp_i2c1_groups[] = {
+ "gpio2", "gpio3",
+};
+static const char * const prng_rosc_groups[] = {
+ "gpio2",
+};
+static const char * const qdss_cti_trig_out_b0_groups[] = {
+ "gpio2",
+};
+static const char * const blsp_spi2_groups[] = {
+ "gpio4", "gpio5", "gpio6", "gpio7",
+};
+static const char * const blsp_uart2_groups[] = {
+ "gpio4", "gpio5", "gpio6", "gpio7",
+};
+static const char * const blsp_uart3_groups[] = {
+ "gpio8", "gpio9", "gpio10", "gpio11",
+};
+static const char * const pbs0_groups[] = {
+ "gpio8",
+};
+static const char * const pbs1_groups[] = {
+ "gpio9",
+};
+static const char * const pwr_modem_enabled_b_groups[] = {
+ "gpio9",
+};
+static const char * const blsp_i2c3_groups[] = {
+ "gpio10", "gpio11",
+};
+static const char * const gcc_gp2_clk_b_groups[] = {
+ "gpio10",
+};
+static const char * const ldo_update_groups[] = {
+ "gpio4",
+};
+static const char * const atest_combodac_to_gpio_native_groups[] = {
+ "gpio4", "gpio12", "gpio13", "gpio20", "gpio21", "gpio28", "gpio29",
+ "gpio30", "gpio39", "gpio40", "gpio41", "gpio42", "gpio43", "gpio44",
+ "gpio45", "gpio46", "gpio47", "gpio48", "gpio67", "gpio115",
+};
+static const char * const ldo_en_groups[] = {
+ "gpio5",
+};
+static const char * const blsp_i2c2_groups[] = {
+ "gpio6", "gpio7",
+};
+static const char * const gcc_gp1_clk_b_groups[] = {
+ "gpio6",
+};
+static const char * const pbs2_groups[] = {
+ "gpio7",
+};
+static const char * const atest_gpsadc_dtest0_native_groups[] = {
+ "gpio7",
+};
+static const char * const blsp_spi3_groups[] = {
+ "gpio8", "gpio9", "gpio10", "gpio11",
+};
+static const char * const gcc_gp3_clk_b_groups[] = {
+ "gpio11",
+};
+static const char * const blsp_spi4_groups[] = {
+ "gpio12", "gpio13", "gpio14", "gpio15",
+};
+static const char * const blsp_uart4_groups[] = {
+ "gpio12", "gpio13", "gpio14", "gpio15",
+};
+static const char * const sec_mi2s_groups[] = {
+ "gpio12", "gpio13", "gpio94", "gpio95",
+};
+static const char * const pwr_nav_enabled_b_groups[] = {
+ "gpio12",
+};
+static const char * const codec_mad_groups[] = {
+ "gpio13",
+};
+static const char * const pwr_crypto_enabled_b_groups[] = {
+ "gpio13",
+};
+static const char * const blsp_i2c4_groups[] = {
+ "gpio14", "gpio15",
+};
+static const char * const blsp_spi5_groups[] = {
+ "gpio16", "gpio17", "gpio18", "gpio19",
+};
+static const char * const blsp_uart5_groups[] = {
+ "gpio16", "gpio17", "gpio18", "gpio19",
+};
+static const char * const qdss_traceclk_a_groups[] = {
+ "gpio16",
+};
+static const char * const atest_bbrx1_groups[] = {
+ "gpio16",
+};
+static const char * const m_voc_groups[] = {
+ "gpio17", "gpio21",
+};
+static const char * const qdss_cti_trig_in_a0_groups[] = {
+ "gpio17",
+};
+static const char * const qdss_cti_trig_in_b0_groups[] = {
+ "gpio21",
+};
+static const char * const blsp_i2c6_groups[] = {
+ "gpio22", "gpio23",
+};
+static const char * const qdss_traceclk_b_groups[] = {
+ "gpio22",
+};
+static const char * const atest_wlan0_groups[] = {
+ "gpio22",
+};
+static const char * const atest_wlan1_groups[] = {
+ "gpio23",
+};
+static const char * const atest_bbrx0_groups[] = {
+ "gpio17",
+};
+static const char * const blsp_i2c5_groups[] = {
+ "gpio18", "gpio19",
+};
+static const char * const qdss_tracectl_a_groups[] = {
+ "gpio18",
+};
+static const char * const atest_gpsadc_dtest1_native_groups[] = {
+ "gpio18",
+};
+static const char * const qdss_tracedata_a_groups[] = {
+ "gpio19", "gpio26", "gpio27", "gpio28", "gpio29", "gpio30", "gpio31",
+ "gpio32", "gpio33", "gpio34", "gpio35", "gpio36", "gpio38", "gpio39",
+ "gpio40", "gpio50",
+};
+static const char * const blsp_spi6_groups[] = {
+ "gpio20", "gpio21", "gpio22", "gpio23",
+};
+static const char * const blsp_uart6_groups[] = {
+ "gpio20", "gpio21", "gpio22", "gpio23",
+};
+static const char * const qdss_tracectl_b_groups[] = {
+ "gpio20",
+};
+static const char * const mdp_vsync_groups[] = {
+ "gpio24", "gpio25",
+};
+static const char * const pri_mi2s_mclk_a_groups[] = {
+ "gpio25",
+};
+static const char * const sec_mi2s_mclk_a_groups[] = {
+ "gpio25",
+};
+static const char * const cam_mclk_groups[] = {
+ "gpio26", "gpio27", "gpio28",
+};
+static const char * const cci_i2c_groups[] = {
+ "gpio29", "gpio30", "gpio31", "gpio32",
+};
+static const char * const pwr_modem_enabled_a_groups[] = {
+ "gpio29",
+};
+static const char * const cci_timer0_groups[] = {
+ "gpio33",
+};
+static const char * const cci_timer1_groups[] = {
+ "gpio34",
+};
+static const char * const cam1_standby_groups[] = {
+ "gpio35",
+};
+static const char * const pwr_nav_enabled_a_groups[] = {
+ "gpio35",
+};
+static const char * const cam1_rst_groups[] = {
+ "gpio36",
+};
+static const char * const pwr_crypto_enabled_a_groups[] = {
+ "gpio36",
+};
+static const char * const forced_usb_groups[] = {
+ "gpio37",
+};
+static const char * const qdss_cti_trig_out_b1_groups[] = {
+ "gpio37",
+};
+static const char * const cam2_rst_groups[] = {
+ "gpio38",
+};
+static const char * const webcam_standby_groups[] = {
+ "gpio39",
+};
+static const char * const cci_async_groups[] = {
+ "gpio39",
+};
+static const char * const webcam_rst_groups[] = {
+ "gpio40",
+};
+static const char * const ov_ldo_groups[] = {
+ "gpio41",
+};
+static const char * const sd_write_groups[] = {
+ "gpio41",
+};
+static const char * const accel_int_groups[] = {
+ "gpio42",
+};
+static const char * const gcc_gp1_clk_a_groups[] = {
+ "gpio42",
+};
+static const char * const alsp_int_groups[] = {
+ "gpio43",
+};
+static const char * const gcc_gp2_clk_a_groups[] = {
+ "gpio43",
+};
+static const char * const mag_int_groups[] = {
+ "gpio44",
+};
+static const char * const gcc_gp3_clk_a_groups[] = {
+ "gpio44",
+};
+static const char * const blsp6_spi_groups[] = {
+ "gpio47",
+};
+static const char * const fp_int_groups[] = {
+ "gpio48",
+};
+static const char * const qdss_cti_trig_in_b1_groups[] = {
+ "gpio48",
+};
+static const char * const uim_batt_groups[] = {
+ "gpio49",
+};
+static const char * const cam2_standby_groups[] = {
+ "gpio50",
+};
+static const char * const uim1_data_groups[] = {
+ "gpio51",
+};
+static const char * const uim1_clk_groups[] = {
+ "gpio52",
+};
+static const char * const uim1_reset_groups[] = {
+ "gpio53",
+};
+static const char * const uim1_present_groups[] = {
+ "gpio54",
+};
+static const char * const uim2_data_groups[] = {
+ "gpio55",
+};
+static const char * const uim2_clk_groups[] = {
+ "gpio56",
+};
+static const char * const uim2_reset_groups[] = {
+ "gpio57",
+};
+static const char * const uim2_present_groups[] = {
+ "gpio58",
+};
+static const char * const sensor_rst_groups[] = {
+ "gpio59",
+};
+static const char * const mipi_dsi0_groups[] = {
+ "gpio60",
+};
+static const char * const smb_int_groups[] = {
+ "gpio61",
+};
+static const char * const cam0_ldo_groups[] = {
+ "gpio62",
+};
+static const char * const us_euro_groups[] = {
+ "gpio63",
+};
+static const char * const atest_char3_groups[] = {
+ "gpio63",
+};
+static const char * const dbg_out_groups[] = {
+ "gpio63",
+};
+static const char * const bimc_dte0_groups[] = {
+ "gpio63", "gpio65",
+};
+static const char * const ts_resout_groups[] = {
+ "gpio64",
+};
+static const char * const ts_sample_groups[] = {
+ "gpio65",
+};
+static const char * const sec_mi2s_mclk_b_groups[] = {
+ "gpio66",
+};
+static const char * const pri_mi2s_groups[] = {
+ "gpio66", "gpio85", "gpio86", "gpio88", "gpio94", "gpio95",
+};
+static const char * const sdcard_det_groups[] = {
+ "gpio67",
+};
+static const char * const atest_char1_groups[] = {
+ "gpio67",
+};
+static const char * const ebi_cdc_groups[] = {
+ "gpio67", "gpio69", "gpio118", "gpio119", "gpio120", "gpio123",
+};
+static const char * const audio_reset_groups[] = {
+ "gpio68",
+};
+static const char * const atest_char0_groups[] = {
+ "gpio68",
+};
+static const char * const audio_ref_groups[] = {
+ "gpio69",
+};
+static const char * const cdc_pdm0_groups[] = {
+ "gpio69", "gpio70", "gpio71", "gpio72", "gpio73", "gpio74",
+};
+static const char * const pri_mi2s_mclk_b_groups[] = {
+ "gpio69",
+};
+static const char * const lpass_slimbus_groups[] = {
+ "gpio70",
+};
+static const char * const lpass_slimbus0_groups[] = {
+ "gpio71",
+};
+static const char * const lpass_slimbus1_groups[] = {
+ "gpio72",
+};
+static const char * const codec_int1_groups[] = {
+ "gpio73",
+};
+static const char * const codec_int2_groups[] = {
+ "gpio74",
+};
+static const char * const wcss_bt_groups[] = {
+ "gpio75", "gpio83", "gpio84",
+};
+static const char * const atest_char2_groups[] = {
+ "gpio75",
+};
+static const char * const ebi_ch0_groups[] = {
+ "gpio75",
+};
+static const char * const wcss_wlan2_groups[] = {
+ "gpio76",
+};
+static const char * const wcss_wlan1_groups[] = {
+ "gpio77",
+};
+static const char * const wcss_wlan0_groups[] = {
+ "gpio78",
+};
+static const char * const wcss_wlan_groups[] = {
+ "gpio79", "gpio80",
+};
+static const char * const wcss_fm_groups[] = {
+ "gpio81", "gpio82",
+};
+static const char * const ext_lpass_groups[] = {
+ "gpio81",
+};
+static const char * const cri_trng_groups[] = {
+ "gpio82",
+};
+static const char * const cri_trng1_groups[] = {
+ "gpio83",
+};
+static const char * const cri_trng0_groups[] = {
+ "gpio84",
+};
+static const char * const blsp_spi7_groups[] = {
+ "gpio85", "gpio86", "gpio87", "gpio88",
+};
+static const char * const blsp_uart7_groups[] = {
+ "gpio85", "gpio86", "gpio87", "gpio88",
+};
+static const char * const pri_mi2s_ws_groups[] = {
+ "gpio87",
+};
+static const char * const blsp_i2c7_groups[] = {
+ "gpio87", "gpio88",
+};
+static const char * const gcc_tlmm_groups[] = {
+ "gpio87",
+};
+static const char * const dmic0_clk_groups[] = {
+ "gpio89",
+};
+static const char * const dmic0_data_groups[] = {
+ "gpio90",
+};
+static const char * const key_volp_groups[] = {
+ "gpio91",
+};
+static const char * const qdss_cti_trig_in_a1_groups[] = {
+ "gpio91",
+};
+static const char * const us_emitter_groups[] = {
+ "gpio92",
+};
+static const char * const wsa_irq_groups[] = {
+ "gpio93",
+};
+static const char * const wsa_io_groups[] = {
+ "gpio94", "gpio95",
+};
+static const char * const wsa_reset_groups[] = {
+ "gpio96",
+};
+static const char * const blsp_spi8_groups[] = {
+ "gpio96", "gpio97", "gpio98", "gpio99",
+};
+static const char * const blsp_uart8_groups[] = {
+ "gpio96", "gpio97", "gpio98", "gpio99",
+};
+static const char * const blsp_i2c8_groups[] = {
+ "gpio98", "gpio99",
+};
+static const char * const gcc_plltest_groups[] = {
+ "gpio98", "gpio99",
+};
+static const char * const nav_pps_in_a_groups[] = {
+ "gpio115",
+};
+static const char * const pa_indicator_groups[] = {
+ "gpio116",
+};
+static const char * const modem_tsync_groups[] = {
+ "gpio117",
+};
+static const char * const nav_tsync_groups[] = {
+ "gpio117",
+};
+static const char * const nav_pps_in_b_groups[] = {
+ "gpio117",
+};
+static const char * const nav_pps_groups[] = {
+ "gpio117",
+};
+static const char * const gsm0_tx_groups[] = {
+ "gpio119",
+};
+static const char * const atest_char_groups[] = {
+ "gpio120",
+};
+static const char * const atest_tsens_groups[] = {
+ "gpio120",
+};
+static const char * const bimc_dte1_groups[] = {
+ "gpio121", "gpio122",
+};
+static const char * const ssbi_wtr1_groups[] = {
+ "gpio122", "gpio123",
+};
+static const char * const fp_gpio_groups[] = {
+ "gpio124",
+};
+static const char * const coex_uart_groups[] = {
+ "gpio124", "gpio127",
+};
+static const char * const key_snapshot_groups[] = {
+ "gpio127",
+};
+static const char * const key_focus_groups[] = {
+ "gpio128",
+};
+static const char * const nfc_pwr_groups[] = {
+ "gpio129",
+};
+static const char * const blsp8_spi_groups[] = {
+ "gpio130",
+};
+static const char * const qdss_cti_trig_out_a0_groups[] = {
+ "gpio132",
+};
+static const char * const qdss_cti_trig_out_a1_groups[] = {
+ "gpio133",
+};
+
+static const struct msm_function msm8937_functions[] = {
+ FUNCTION(qdss_tracedata_b),
+ FUNCTION(blsp_uart1),
+ FUNCTION(gpio),
+ FUNCTION(blsp_spi1),
+ FUNCTION(adsp_ext),
+ FUNCTION(blsp_i2c1),
+ FUNCTION(prng_rosc),
+ FUNCTION(qdss_cti_trig_out_b0),
+ FUNCTION(blsp_spi2),
+ FUNCTION(blsp_uart2),
+ FUNCTION(blsp_uart3),
+ FUNCTION(pbs0),
+ FUNCTION(pbs1),
+ FUNCTION(pwr_modem_enabled_b),
+ FUNCTION(blsp_i2c3),
+ FUNCTION(gcc_gp2_clk_b),
+ FUNCTION(ldo_update),
+ FUNCTION(atest_combodac_to_gpio_native),
+ FUNCTION(ldo_en),
+ FUNCTION(blsp_i2c2),
+ FUNCTION(gcc_gp1_clk_b),
+ FUNCTION(pbs2),
+ FUNCTION(atest_gpsadc_dtest0_native),
+ FUNCTION(blsp_spi3),
+ FUNCTION(gcc_gp3_clk_b),
+ FUNCTION(blsp_spi4),
+ FUNCTION(blsp_uart4),
+ FUNCTION(sec_mi2s),
+ FUNCTION(pwr_nav_enabled_b),
+ FUNCTION(codec_mad),
+ FUNCTION(pwr_crypto_enabled_b),
+ FUNCTION(blsp_i2c4),
+ FUNCTION(blsp_spi5),
+ FUNCTION(blsp_uart5),
+ FUNCTION(qdss_traceclk_a),
+ FUNCTION(atest_bbrx1),
+ FUNCTION(m_voc),
+ FUNCTION(qdss_cti_trig_in_a0),
+ FUNCTION(qdss_cti_trig_in_b0),
+ FUNCTION(blsp_i2c6),
+ FUNCTION(qdss_traceclk_b),
+ FUNCTION(atest_wlan0),
+ FUNCTION(atest_wlan1),
+ FUNCTION(atest_bbrx0),
+ FUNCTION(blsp_i2c5),
+ FUNCTION(qdss_tracectl_a),
+ FUNCTION(atest_gpsadc_dtest1_native),
+ FUNCTION(qdss_tracedata_a),
+ FUNCTION(blsp_spi6),
+ FUNCTION(blsp_uart6),
+ FUNCTION(qdss_tracectl_b),
+ FUNCTION(mdp_vsync),
+ FUNCTION(pri_mi2s_mclk_a),
+ FUNCTION(sec_mi2s_mclk_a),
+ FUNCTION(cam_mclk),
+ FUNCTION(cci_i2c),
+ FUNCTION(pwr_modem_enabled_a),
+ FUNCTION(cci_timer0),
+ FUNCTION(cci_timer1),
+ FUNCTION(cam1_standby),
+ FUNCTION(pwr_nav_enabled_a),
+ FUNCTION(cam1_rst),
+ FUNCTION(pwr_crypto_enabled_a),
+ FUNCTION(forced_usb),
+ FUNCTION(qdss_cti_trig_out_b1),
+ FUNCTION(cam2_rst),
+ FUNCTION(webcam_standby),
+ FUNCTION(cci_async),
+ FUNCTION(webcam_rst),
+ FUNCTION(ov_ldo),
+ FUNCTION(sd_write),
+ FUNCTION(accel_int),
+ FUNCTION(gcc_gp1_clk_a),
+ FUNCTION(alsp_int),
+ FUNCTION(gcc_gp2_clk_a),
+ FUNCTION(mag_int),
+ FUNCTION(gcc_gp3_clk_a),
+ FUNCTION(blsp6_spi),
+ FUNCTION(fp_int),
+ FUNCTION(qdss_cti_trig_in_b1),
+ FUNCTION(uim_batt),
+ FUNCTION(cam2_standby),
+ FUNCTION(uim1_data),
+ FUNCTION(uim1_clk),
+ FUNCTION(uim1_reset),
+ FUNCTION(uim1_present),
+ FUNCTION(uim2_data),
+ FUNCTION(uim2_clk),
+ FUNCTION(uim2_reset),
+ FUNCTION(uim2_present),
+ FUNCTION(sensor_rst),
+ FUNCTION(mipi_dsi0),
+ FUNCTION(smb_int),
+ FUNCTION(cam0_ldo),
+ FUNCTION(us_euro),
+ FUNCTION(atest_char3),
+ FUNCTION(dbg_out),
+ FUNCTION(bimc_dte0),
+ FUNCTION(ts_resout),
+ FUNCTION(ts_sample),
+ FUNCTION(sec_mi2s_mclk_b),
+ FUNCTION(pri_mi2s),
+ FUNCTION(sdcard_det),
+ FUNCTION(atest_char1),
+ FUNCTION(ebi_cdc),
+ FUNCTION(audio_reset),
+ FUNCTION(atest_char0),
+ FUNCTION(audio_ref),
+ FUNCTION(cdc_pdm0),
+ FUNCTION(pri_mi2s_mclk_b),
+ FUNCTION(lpass_slimbus),
+ FUNCTION(lpass_slimbus0),
+ FUNCTION(lpass_slimbus1),
+ FUNCTION(codec_int1),
+ FUNCTION(codec_int2),
+ FUNCTION(wcss_bt),
+ FUNCTION(atest_char2),
+ FUNCTION(ebi_ch0),
+ FUNCTION(wcss_wlan2),
+ FUNCTION(wcss_wlan1),
+ FUNCTION(wcss_wlan0),
+ FUNCTION(wcss_wlan),
+ FUNCTION(wcss_fm),
+ FUNCTION(ext_lpass),
+ FUNCTION(cri_trng),
+ FUNCTION(cri_trng1),
+ FUNCTION(cri_trng0),
+ FUNCTION(blsp_spi7),
+ FUNCTION(blsp_uart7),
+ FUNCTION(pri_mi2s_ws),
+ FUNCTION(blsp_i2c7),
+ FUNCTION(gcc_tlmm),
+ FUNCTION(dmic0_clk),
+ FUNCTION(dmic0_data),
+ FUNCTION(key_volp),
+ FUNCTION(qdss_cti_trig_in_a1),
+ FUNCTION(us_emitter),
+ FUNCTION(wsa_irq),
+ FUNCTION(wsa_io),
+ FUNCTION(wsa_reset),
+ FUNCTION(blsp_spi8),
+ FUNCTION(blsp_uart8),
+ FUNCTION(blsp_i2c8),
+ FUNCTION(gcc_plltest),
+ FUNCTION(nav_pps_in_a),
+ FUNCTION(pa_indicator),
+ FUNCTION(modem_tsync),
+ FUNCTION(nav_tsync),
+ FUNCTION(nav_pps_in_b),
+ FUNCTION(nav_pps),
+ FUNCTION(gsm0_tx),
+ FUNCTION(atest_char),
+ FUNCTION(atest_tsens),
+ FUNCTION(bimc_dte1),
+ FUNCTION(ssbi_wtr1),
+ FUNCTION(fp_gpio),
+ FUNCTION(coex_uart),
+ FUNCTION(key_snapshot),
+ FUNCTION(key_focus),
+ FUNCTION(nfc_pwr),
+ FUNCTION(blsp8_spi),
+ FUNCTION(qdss_cti_trig_out_a0),
+ FUNCTION(qdss_cti_trig_out_a1),
+};
+
+static const struct msm_pingroup msm8937_groups[] = {
+ PINGROUP(0, blsp_spi1, blsp_uart1, qdss_tracedata_b, NA, NA, NA, NA,
+ NA, NA),
+ PINGROUP(1, blsp_spi1, blsp_uart1, adsp_ext, NA, NA, NA, NA, NA,
+ qdss_tracedata_b),
+ PINGROUP(2, blsp_spi1, blsp_uart1, blsp_i2c1, prng_rosc, NA, NA, NA,
+ NA, NA),
+ PINGROUP(3, blsp_spi1, blsp_uart1, blsp_i2c1, NA, NA, NA, NA, NA, NA),
+ PINGROUP(4, blsp_spi2, blsp_uart2, ldo_update, NA,
+ atest_combodac_to_gpio_native, NA, NA, NA, NA),
+ PINGROUP(5, blsp_spi2, blsp_uart2, ldo_en, NA, NA, NA, NA, NA, NA),
+ PINGROUP(6, blsp_spi2, blsp_uart2, blsp_i2c2, gcc_gp1_clk_b,
+ qdss_tracedata_b, NA, NA, NA, NA),
+ PINGROUP(7, blsp_spi2, blsp_uart2, blsp_i2c2, pbs2, NA,
+ qdss_tracedata_b, NA, atest_gpsadc_dtest0_native, NA),
+ PINGROUP(8, blsp_spi3, blsp_uart3, pbs0, NA, NA, NA, NA, NA, NA),
+ PINGROUP(9, blsp_spi3, blsp_uart3, pbs1, pwr_modem_enabled_b, NA, NA,
+ NA, NA, NA),
+ PINGROUP(10, blsp_spi3, blsp_uart3, blsp_i2c3, gcc_gp2_clk_b, NA, NA,
+ NA, NA, NA),
+ PINGROUP(11, blsp_spi3, blsp_uart3, blsp_i2c3, gcc_gp3_clk_b, NA, NA,
+ NA, NA, NA),
+ PINGROUP(12, blsp_spi4, blsp_uart4, sec_mi2s, pwr_nav_enabled_b, NA,
+ NA, NA, NA, NA),
+ PINGROUP(13, blsp_spi4, blsp_uart4, sec_mi2s, pwr_crypto_enabled_b, NA,
+ NA, NA, NA, NA),
+ PINGROUP(14, blsp_spi4, blsp_uart4, blsp_i2c4, NA, NA, NA, NA, NA, NA),
+ PINGROUP(15, blsp_spi4, blsp_uart4, blsp_i2c4, NA, NA, NA, NA, NA, NA),
+ PINGROUP(16, blsp_spi5, blsp_uart5, NA, NA, NA, NA, qdss_traceclk_a,
+ NA, atest_bbrx1),
+ PINGROUP(17, blsp_spi5, blsp_uart5, m_voc, qdss_cti_trig_in_a0, NA,
+ atest_bbrx0, NA, NA, NA),
+ PINGROUP(18, blsp_spi5, blsp_uart5, blsp_i2c5, qdss_tracectl_a, NA,
+ atest_gpsadc_dtest1_native, NA, NA, NA),
+ PINGROUP(19, blsp_spi5, blsp_uart5, blsp_i2c5, qdss_tracedata_a, NA,
+ NA, NA, NA, NA),
+ PINGROUP(20, blsp_spi6, blsp_uart6, NA, NA, NA, NA, NA, NA,
+ qdss_tracectl_b),
+ PINGROUP(21, blsp_spi6, blsp_uart6, m_voc, NA, NA, NA, NA, NA,
+ qdss_cti_trig_in_b0),
+ PINGROUP(22, blsp_spi6, blsp_uart6, blsp_i2c6, qdss_traceclk_b, NA,
+ atest_wlan0, NA, NA, NA),
+ PINGROUP(23, blsp_spi6, blsp_uart6, blsp_i2c6, qdss_tracedata_b, NA,
+ atest_wlan1, NA, NA, NA),
+ PINGROUP(24, mdp_vsync, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(25, mdp_vsync, pri_mi2s_mclk_a, sec_mi2s_mclk_a, NA, NA, NA,
+ NA, NA, NA),
+ PINGROUP(26, cam_mclk, NA, NA, NA, NA, NA, qdss_tracedata_a, NA, NA),
+ PINGROUP(27, cam_mclk, NA, NA, NA, NA, NA, NA, NA, qdss_tracedata_a),
+ PINGROUP(28, cam_mclk, NA, NA, NA, NA, NA, qdss_tracedata_a, NA,
+ atest_combodac_to_gpio_native),
+ PINGROUP(29, cci_i2c, pwr_modem_enabled_a, NA, NA, NA, NA, NA,
+ qdss_tracedata_a, NA),
+ PINGROUP(30, cci_i2c, NA, NA, NA, NA, NA, NA, NA, qdss_tracedata_a),
+ PINGROUP(31, cci_i2c, NA, NA, NA, NA, NA, NA, NA, qdss_tracedata_a),
+ PINGROUP(32, cci_i2c, NA, NA, NA, NA, NA, NA, NA, qdss_tracedata_a),
+ PINGROUP(33, cci_timer0, NA, NA, NA, NA, NA, NA, NA, qdss_tracedata_a),
+ PINGROUP(34, cci_timer1, NA, NA, NA, NA, NA, NA, NA, qdss_tracedata_a),
+ PINGROUP(35, pwr_nav_enabled_a, NA, NA, NA, NA, NA, NA, NA,
+ qdss_tracedata_a),
+ PINGROUP(36, pwr_crypto_enabled_a, NA, NA, NA, NA, NA, NA, NA,
+ qdss_tracedata_a),
+ PINGROUP(37, NA, NA, NA, NA, NA, qdss_cti_trig_out_b1, NA, NA, NA),
+ PINGROUP(38, NA, qdss_tracedata_a, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(39, cci_async, NA, NA, NA, NA, NA, qdss_tracedata_a, NA,
+ atest_combodac_to_gpio_native),
+ PINGROUP(40, NA, NA, NA, NA, qdss_tracedata_a, NA,
+ atest_combodac_to_gpio_native, NA, NA),
+ PINGROUP(41, sd_write, NA, NA, NA, NA, NA, NA, NA,
+ atest_combodac_to_gpio_native),
+ PINGROUP(42, gcc_gp1_clk_a, qdss_tracedata_b, NA,
+ atest_combodac_to_gpio_native, NA, NA, NA, NA, NA),
+ PINGROUP(43, gcc_gp2_clk_a, qdss_tracedata_b, NA,
+ atest_combodac_to_gpio_native, NA, NA, NA, NA, NA),
+ PINGROUP(44, gcc_gp3_clk_a, qdss_tracedata_b, NA,
+ atest_combodac_to_gpio_native, NA, NA, NA, NA, NA),
+ PINGROUP(45, NA, NA, atest_combodac_to_gpio_native, NA, NA, NA, NA, NA,
+ NA),
+ PINGROUP(46, NA, NA, atest_combodac_to_gpio_native, NA, NA, NA, NA, NA,
+ NA),
+ PINGROUP(47, blsp6_spi, NA, qdss_tracedata_b, NA,
+ atest_combodac_to_gpio_native, NA, NA, NA, NA),
+ PINGROUP(48, NA, qdss_cti_trig_in_b1, NA,
+ atest_combodac_to_gpio_native, NA, NA, NA, NA, NA),
+ PINGROUP(49, uim_batt, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(50, qdss_tracedata_a, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(51, uim1_data, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(52, uim1_clk, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(53, uim1_reset, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(54, uim1_present, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(55, uim2_data, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(56, uim2_clk, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(57, uim2_reset, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(58, uim2_present, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(59, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(60, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(61, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(62, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(63, atest_char3, dbg_out, bimc_dte0, NA, NA, NA, NA, NA, NA),
+ PINGROUP(64, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(65, bimc_dte0, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(66, sec_mi2s_mclk_b, pri_mi2s, NA, qdss_tracedata_b, NA, NA,
+ NA, NA, NA),
+ PINGROUP(67, atest_char1, ebi_cdc, NA, atest_combodac_to_gpio_native,
+ NA, NA, NA, NA, NA),
+ PINGROUP(68, atest_char0, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(69, audio_ref, cdc_pdm0, pri_mi2s_mclk_b, ebi_cdc, NA, NA, NA,
+ NA, NA),
+ PINGROUP(70, lpass_slimbus, cdc_pdm0, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(71, lpass_slimbus0, cdc_pdm0, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(72, lpass_slimbus1, cdc_pdm0, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(73, cdc_pdm0, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(74, cdc_pdm0, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(75, wcss_bt, atest_char2, NA, ebi_ch0, NA, NA, NA, NA, NA),
+ PINGROUP(76, wcss_wlan2, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(77, wcss_wlan1, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(78, wcss_wlan0, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(79, wcss_wlan, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(80, wcss_wlan, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(81, wcss_fm, ext_lpass, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(82, wcss_fm, cri_trng, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(83, wcss_bt, cri_trng1, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(84, wcss_bt, cri_trng0, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(85, pri_mi2s, blsp_spi7, blsp_uart7, NA, NA, NA, NA, NA, NA),
+ PINGROUP(86, pri_mi2s, blsp_spi7, blsp_uart7, qdss_tracedata_b, NA, NA,
+ NA, NA, NA),
+ PINGROUP(87, pri_mi2s_ws, blsp_spi7, blsp_uart7, blsp_i2c7,
+ qdss_tracedata_b, gcc_tlmm, NA, NA, NA),
+ PINGROUP(88, pri_mi2s, blsp_spi7, blsp_uart7, blsp_i2c7, NA, NA, NA,
+ NA, NA),
+ PINGROUP(89, dmic0_clk, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(90, dmic0_data, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(91, NA, NA, NA, NA, NA, qdss_cti_trig_in_a1, NA, NA, NA),
+ PINGROUP(92, NA, NA, NA, NA, NA, qdss_tracedata_b, NA, NA, NA),
+ PINGROUP(93, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(94, wsa_io, sec_mi2s, pri_mi2s, NA, NA, NA, NA, NA, NA),
+ PINGROUP(95, wsa_io, sec_mi2s, pri_mi2s, NA, NA, NA, NA, NA, NA),
+ PINGROUP(96, blsp_spi8, blsp_uart8, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(97, blsp_spi8, blsp_uart8, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(98, blsp_spi8, blsp_uart8, blsp_i2c8, gcc_plltest, NA, NA, NA,
+ NA, NA),
+ PINGROUP(99, blsp_spi8, blsp_uart8, blsp_i2c8, gcc_plltest, NA, NA, NA,
+ NA, NA),
+ PINGROUP(100, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(101, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(102, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(103, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(104, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(105, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(106, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(107, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(108, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(109, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(110, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(111, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(112, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(113, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(114, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(115, NA, NA, nav_pps_in_a, NA, atest_combodac_to_gpio_native,
+ NA, NA, NA, NA),
+ PINGROUP(116, NA, pa_indicator, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(117, NA, modem_tsync, nav_tsync, nav_pps_in_b, nav_pps, NA,
+ NA, NA, NA),
+ PINGROUP(118, NA, ebi_cdc, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(119, gsm0_tx, NA, ebi_cdc, NA, NA, NA, NA, NA, NA),
+ PINGROUP(120, NA, atest_char, ebi_cdc, NA, atest_tsens, NA, NA, NA, NA),
+ PINGROUP(121, NA, NA, NA, bimc_dte1, NA, NA, NA, NA, NA),
+ PINGROUP(122, NA, ssbi_wtr1, NA, NA, bimc_dte1, NA, NA, NA, NA),
+ PINGROUP(123, NA, ssbi_wtr1, ebi_cdc, NA, NA, NA, NA, NA, NA),
+ PINGROUP(124, coex_uart, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(125, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(126, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(127, coex_uart, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(128, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(129, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(130, blsp8_spi, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(131, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(132, qdss_cti_trig_out_a0, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(133, qdss_cti_trig_out_a1, NA, NA, NA, NA, NA, NA, NA, NA),
+ SDC_QDSD_PINGROUP(sdc1_clk, 0x10a000, 13, 6),
+ SDC_QDSD_PINGROUP(sdc1_cmd, 0x10a000, 11, 3),
+ SDC_QDSD_PINGROUP(sdc1_data, 0x10a000, 9, 0),
+ SDC_QDSD_PINGROUP(sdc1_rclk, 0x10a000, 15, 0),
+ SDC_QDSD_PINGROUP(sdc2_clk, 0x109000, 14, 6),
+ SDC_QDSD_PINGROUP(sdc2_cmd, 0x109000, 11, 3),
+ SDC_QDSD_PINGROUP(sdc2_data, 0x109000, 9, 0),
+ SDC_QDSD_PINGROUP(qdsd_clk, 0x19c000, 3, 0),
+ SDC_QDSD_PINGROUP(qdsd_cmd, 0x19c000, 8, 5),
+ SDC_QDSD_PINGROUP(qdsd_data0, 0x19c000, 13, 10),
+ SDC_QDSD_PINGROUP(qdsd_data1, 0x19c000, 18, 15),
+ SDC_QDSD_PINGROUP(qdsd_data2, 0x19c000, 23, 20),
+ SDC_QDSD_PINGROUP(qdsd_data3, 0x19c000, 28, 25),
+};
+
+static const struct msm_pinctrl_soc_data msm8937_pinctrl = {
+ .pins = msm8937_pins,
+ .npins = ARRAY_SIZE(msm8937_pins),
+ .functions = msm8937_functions,
+ .nfunctions = ARRAY_SIZE(msm8937_functions),
+ .groups = msm8937_groups,
+ .ngroups = ARRAY_SIZE(msm8937_groups),
+ .ngpios = 134,
+};
+
+static int msm8937_pinctrl_probe(struct platform_device *pdev)
+{
+ return msm_pinctrl_probe(pdev, &msm8937_pinctrl);
+}
+
+static const struct of_device_id msm8937_pinctrl_of_match[] = {
+ { .compatible = "qcom,msm8937-pinctrl", },
+ { },
+};
+
+static struct platform_driver msm8937_pinctrl_driver = {
+ .driver = {
+ .name = "msm8937-pinctrl",
+ .owner = THIS_MODULE,
+ .of_match_table = msm8937_pinctrl_of_match,
+ },
+ .probe = msm8937_pinctrl_probe,
+ .remove = msm_pinctrl_remove,
+};
+
+static int __init msm8937_pinctrl_init(void)
+{
+ return platform_driver_register(&msm8937_pinctrl_driver);
+}
+arch_initcall(msm8937_pinctrl_init);
+
+static void __exit msm8937_pinctrl_exit(void)
+{
+ platform_driver_unregister(&msm8937_pinctrl_driver);
+}
+module_exit(msm8937_pinctrl_exit);
+
+MODULE_DESCRIPTION("QTI msm8937 pinctrl driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, msm8937_pinctrl_of_match);
diff --git a/drivers/pinctrl/qcom/pinctrl-sdxpoorwills.c b/drivers/pinctrl/qcom/pinctrl-sdxpoorwills.c
index 6ceb39a..c5f1307 100644
--- a/drivers/pinctrl/qcom/pinctrl-sdxpoorwills.c
+++ b/drivers/pinctrl/qcom/pinctrl-sdxpoorwills.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1105,6 +1105,97 @@
[106] = SDC_QDSD_PINGROUP(sdc2_data, 0x0, 9, 0),
};
+static struct msm_gpio_mux_input sdxpoorwills_mux_in[] = {
+ {0, 1},
+ {1, 2},
+ {2, 5},
+ {3, 6},
+ {4, 9},
+ {5, 10},
+ {6, 11},
+ {7, 12},
+ {8, 13},
+ {9, 14},
+ {10, 15},
+ {11, 16},
+ {12, 17},
+ {13, 18},
+ {14, 19},
+ {15, 21},
+ {16, 22},
+ {17, 24},
+ {18, 25},
+ {19, 35},
+ {20, 42, 1},
+ {21, 43},
+ {22, 45},
+ {23, 46},
+ {24, 48},
+ {25, 50},
+ {26, 52},
+ {27, 53},
+ {28, 54},
+ {29, 55},
+ {30, 56},
+ {31, 57},
+ {32, 60},
+ {33, 61},
+ {34, 64},
+ {35, 65},
+ {36, 68},
+ {37, 71},
+ {38, 75},
+ {39, 76},
+ {40, 78},
+ {41, 79},
+ {42, 80},
+ {43, 82},
+ {44, 83},
+ {45, 84},
+ {46, 86},
+ {47, 87},
+ {48, 88},
+ {49, 89},
+ {50, 90},
+ {51, 93},
+ {52, 94},
+ {53, 95},
+ {54, 97},
+ {55, 98},
+};
+
+static struct msm_pdc_mux_output sdxpoorwills_mux_out[] = {
+ {0, 167},
+ {0, 168},
+ {0, 169},
+ {0, 170},
+ {0, 171},
+ {0, 172},
+ {0, 173},
+ {0, 174},
+ {0, 175},
+ {0, 176},
+ {0, 177},
+ {0, 178},
+ {0, 179},
+ {0, 180},
+ {0, 181},
+ {0, 182},
+ {0, 183},
+ {0, 184},
+ {0, 185},
+ {0, 186},
+ {0, 187},
+ {0, 188},
+ {0, 189},
+ {0, 190},
+ {0, 191},
+ {0, 192},
+ {0, 193},
+ {0, 194},
+ {0, 195},
+};
+
static const struct msm_pinctrl_soc_data sdxpoorwills_pinctrl = {
.pins = sdxpoorwills_pins,
.npins = ARRAY_SIZE(sdxpoorwills_pins),
@@ -1112,6 +1203,11 @@
.nfunctions = ARRAY_SIZE(sdxpoorwills_functions),
.groups = sdxpoorwills_groups,
.ngroups = ARRAY_SIZE(sdxpoorwills_groups),
+ .gpio_mux_in = sdxpoorwills_mux_in,
+ .n_gpio_mux_in = ARRAY_SIZE(sdxpoorwills_mux_in),
+ .pdc_mux_out = sdxpoorwills_mux_out,
+ .n_pdc_mux_out = ARRAY_SIZE(sdxpoorwills_mux_out),
+ .n_pdc_mux_offset = 20,
.ngpios = 100,
};
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
index 1335c45..e23541c 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
@@ -72,6 +72,11 @@
IPA_USB_DRV_NAME " %s:%d " fmt, ## args); \
} while (0)
+enum ipa_usb_direction {
+ IPA_USB_DIR_UL,
+ IPA_USB_DIR_DL,
+};
+
struct ipa_usb_xdci_connect_params_internal {
enum ipa_usb_max_usb_packet_size max_pkt_size;
u32 ipa_to_usb_clnt_hdl;
@@ -167,7 +172,8 @@
int (*ipa_usb_notify_cb)(enum ipa_usb_notify_event, void *user_data);
void *user_data;
enum ipa3_usb_state state;
- struct ipa_usb_xdci_chan_params ch_params;
+ struct ipa_usb_xdci_chan_params ul_ch_params;
+ struct ipa_usb_xdci_chan_params dl_ch_params;
struct ipa3_usb_teth_prot_conn_params teth_conn_params;
};
@@ -741,6 +747,10 @@
&ipa3_usb_ctx->ttype_ctx[ttype];
int result;
+ /* there is one PM resource for teth and one for DPL */
+ if (!IPA3_USB_IS_TTYPE_DPL(ttype) && ipa3_usb_ctx->num_init_prot > 0)
+ return 0;
+
memset(&ttype_ctx->pm_ctx.reg_params, 0,
sizeof(ttype_ctx->pm_ctx.reg_params));
ttype_ctx->pm_ctx.reg_params.name = (ttype == IPA_USB_TRANSPORT_DPL) ?
@@ -1020,11 +1030,11 @@
return 0;
teth_prot_init_fail:
- if (ipa_pm_is_used()) {
- ipa3_usb_deregister_pm(ttype);
- } else {
- if ((IPA3_USB_IS_TTYPE_DPL(ttype))
- || (ipa3_usb_ctx->num_init_prot == 0)) {
+ if ((IPA3_USB_IS_TTYPE_DPL(ttype))
+ || (ipa3_usb_ctx->num_init_prot == 0)) {
+ if (ipa_pm_is_used()) {
+ ipa3_usb_deregister_pm(ttype);
+ } else {
ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.prod_valid =
false;
ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.cons_valid =
@@ -1069,8 +1079,6 @@
params->gevntcount_hi_addr);
IPA_USB_DBG_LOW("dir = %d\n", params->dir);
IPA_USB_DBG_LOW("xfer_ring_len = %d\n", params->xfer_ring_len);
- IPA_USB_DBG_LOW("xfer_ring_base_addr = %llx\n",
- params->xfer_ring_base_addr);
IPA_USB_DBG_LOW("last_trb_addr_iova = %x\n",
params->xfer_scratch.last_trb_addr_iova);
IPA_USB_DBG_LOW("const_buffer_size = %d\n",
@@ -1174,15 +1182,16 @@
ipa3_usb_ctx->smmu_reg_map.cnt--;
}
+
result = ipa3_smmu_map_peer_buff(params->xfer_ring_base_addr_iova,
- params->xfer_ring_base_addr, params->xfer_ring_len, map);
+ params->xfer_ring_len, map, params->sgt_xfer_rings);
if (result) {
IPA_USB_ERR("failed to map Xfer ring %d\n", result);
return result;
}
result = ipa3_smmu_map_peer_buff(params->data_buff_base_addr_iova,
- params->data_buff_base_addr, params->data_buff_base_len, map);
+ params->data_buff_base_len, map, params->sgt_data_buff);
if (result) {
IPA_USB_ERR("failed to map TRBs buff %d\n", result);
return result;
@@ -1191,13 +1200,52 @@
return 0;
}
+static int ipa3_usb_smmu_store_sgt(struct sg_table **out_ch_ptr,
+ struct sg_table *in_sgt_ptr)
+{
+ unsigned int nents;
+
+ if (in_sgt_ptr != NULL) {
+ *out_ch_ptr = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (*out_ch_ptr == NULL)
+ return -ENOMEM;
+
+ nents = in_sgt_ptr->nents;
+
+ (*out_ch_ptr)->sgl =
+ kcalloc(nents, sizeof(struct scatterlist),
+ GFP_KERNEL);
+ if ((*out_ch_ptr)->sgl == NULL)
+ return -ENOMEM;
+
+ memcpy((*out_ch_ptr)->sgl, in_sgt_ptr->sgl,
+ nents*sizeof((*out_ch_ptr)->sgl));
+ (*out_ch_ptr)->nents = nents;
+ (*out_ch_ptr)->orig_nents = in_sgt_ptr->orig_nents;
+ }
+ return 0;
+}
+
+static int ipa3_usb_smmu_free_sgt(struct sg_table **out_sgt_ptr)
+{
+ if (*out_sgt_ptr != NULL) {
+ kfree((*out_sgt_ptr)->sgl);
+ (*out_sgt_ptr)->sgl = NULL;
+ kfree(*out_sgt_ptr);
+ *out_sgt_ptr = NULL;
+ }
+ return 0;
+}
+
static int ipa3_usb_request_xdci_channel(
struct ipa_usb_xdci_chan_params *params,
+ enum ipa_usb_direction dir,
struct ipa_req_chan_out_params *out_params)
{
int result = -EFAULT;
struct ipa_request_gsi_channel_params chan_params;
enum ipa3_usb_transport_type ttype;
+ struct ipa_usb_xdci_chan_params *xdci_ch_params;
IPA_USB_DBG_LOW("entry\n");
if (params == NULL || out_params == NULL ||
@@ -1273,8 +1321,26 @@
}
/* store channel params for SMMU unmap */
- ipa3_usb_ctx->ttype_ctx[ttype].ch_params = *params;
+ if (dir == IPA_USB_DIR_UL)
+ xdci_ch_params = &ipa3_usb_ctx->ttype_ctx[ttype].ul_ch_params;
+ else
+ xdci_ch_params = &ipa3_usb_ctx->ttype_ctx[ttype].dl_ch_params;
+ *xdci_ch_params = *params;
+ result = ipa3_usb_smmu_store_sgt(
+ &xdci_ch_params->sgt_xfer_rings,
+ params->sgt_xfer_rings);
+ if (result) {
+ ipa3_usb_smmu_free_sgt(&xdci_ch_params->sgt_xfer_rings);
+ return result;
+ }
+ result = ipa3_usb_smmu_store_sgt(
+ &xdci_ch_params->sgt_data_buff,
+ params->sgt_data_buff);
+ if (result) {
+ ipa3_usb_smmu_free_sgt(&xdci_ch_params->sgt_data_buff);
+ return result;
+ }
chan_params.keep_ipa_awake = params->keep_ipa_awake;
chan_params.evt_ring_params.intf = GSI_EVT_CHTYPE_XDCI_EV;
chan_params.evt_ring_params.intr = GSI_INTR_IRQ;
@@ -1282,7 +1348,7 @@
chan_params.evt_ring_params.ring_len = params->xfer_ring_len -
chan_params.evt_ring_params.re_size;
chan_params.evt_ring_params.ring_base_addr =
- params->xfer_ring_base_addr;
+ params->xfer_ring_base_addr_iova;
chan_params.evt_ring_params.ring_base_vaddr = NULL;
chan_params.evt_ring_params.int_modt = 0;
chan_params.evt_ring_params.int_modt = 0;
@@ -1302,7 +1368,7 @@
chan_params.chan_params.re_size = GSI_CHAN_RE_SIZE_16B;
chan_params.chan_params.ring_len = params->xfer_ring_len;
chan_params.chan_params.ring_base_addr =
- params->xfer_ring_base_addr;
+ params->xfer_ring_base_addr_iova;
chan_params.chan_params.ring_base_vaddr = NULL;
chan_params.chan_params.use_db_eng = GSI_CHAN_DB_MODE;
chan_params.chan_params.max_prefetch = GSI_ONE_PREFETCH_SEG;
@@ -1345,9 +1411,11 @@
}
static int ipa3_usb_release_xdci_channel(u32 clnt_hdl,
+ enum ipa_usb_direction dir,
enum ipa3_usb_transport_type ttype)
{
int result = 0;
+ struct ipa_usb_xdci_chan_params *xdci_ch_params;
IPA_USB_DBG_LOW("entry\n");
if (ttype < 0 || ttype >= IPA_USB_TRANSPORT_MAX) {
@@ -1367,8 +1435,17 @@
return result;
}
- result = ipa3_usb_smmu_map_xdci_channel(
- &ipa3_usb_ctx->ttype_ctx[ttype].ch_params, false);
+ if (dir == IPA_USB_DIR_UL)
+ xdci_ch_params = &ipa3_usb_ctx->ttype_ctx[ttype].ul_ch_params;
+ else
+ xdci_ch_params = &ipa3_usb_ctx->ttype_ctx[ttype].dl_ch_params;
+
+ result = ipa3_usb_smmu_map_xdci_channel(xdci_ch_params, false);
+
+ if (xdci_ch_params->sgt_xfer_rings != NULL)
+ ipa3_usb_smmu_free_sgt(&xdci_ch_params->sgt_xfer_rings);
+ if (xdci_ch_params->sgt_data_buff != NULL)
+ ipa3_usb_smmu_free_sgt(&xdci_ch_params->sgt_data_buff);
/* Change ipa_usb state to INITIALIZED */
if (!ipa3_usb_set_state(IPA_USB_INITIALIZED, false, ttype))
@@ -2131,14 +2208,15 @@
if (connect_params->teth_prot != IPA_USB_DIAG) {
result = ipa3_usb_request_xdci_channel(ul_chan_params,
- ul_out_params);
+ IPA_USB_DIR_UL, ul_out_params);
if (result) {
IPA_USB_ERR("failed to allocate UL channel.\n");
goto bad_params;
}
}
- result = ipa3_usb_request_xdci_channel(dl_chan_params, dl_out_params);
+ result = ipa3_usb_request_xdci_channel(dl_chan_params, IPA_USB_DIR_DL,
+ dl_out_params);
if (result) {
IPA_USB_ERR("failed to allocate DL/DPL channel.\n");
goto alloc_dl_chan_fail;
@@ -2174,11 +2252,12 @@
return 0;
connect_fail:
- ipa3_usb_release_xdci_channel(dl_out_params->clnt_hdl,
+ ipa3_usb_release_xdci_channel(dl_out_params->clnt_hdl, IPA_USB_DIR_DL,
IPA3_USB_GET_TTYPE(dl_chan_params->teth_prot));
alloc_dl_chan_fail:
if (connect_params->teth_prot != IPA_USB_DIAG)
ipa3_usb_release_xdci_channel(ul_out_params->clnt_hdl,
+ IPA_USB_DIR_UL,
IPA3_USB_GET_TTYPE(ul_chan_params->teth_prot));
bad_params:
mutex_unlock(&ipa3_usb_ctx->general_mutex);
@@ -2250,14 +2329,16 @@
IPA_USB_ERR("failed to change state to stopped\n");
if (!IPA3_USB_IS_TTYPE_DPL(ttype)) {
- result = ipa3_usb_release_xdci_channel(ul_clnt_hdl, ttype);
+ result = ipa3_usb_release_xdci_channel(ul_clnt_hdl,
+ IPA_USB_DIR_UL, ttype);
if (result) {
IPA_USB_ERR("failed to release UL channel.\n");
return result;
}
}
- result = ipa3_usb_release_xdci_channel(dl_clnt_hdl, ttype);
+ result = ipa3_usb_release_xdci_channel(dl_clnt_hdl,
+ IPA_USB_DIR_DL, ttype);
if (result) {
IPA_USB_ERR("failed to release DL channel.\n");
return result;
@@ -2462,14 +2543,15 @@
goto bad_params;
}
- if (ipa_pm_is_used()) {
- ipa3_usb_deregister_pm(ttype);
- } else {
- if (IPA3_USB_IS_TTYPE_DPL(ttype) ||
- (ipa3_usb_ctx->num_init_prot == 0)) {
- if (!ipa3_usb_set_state(IPA_USB_INVALID, false, ttype))
- IPA_USB_ERR(
- "failed to change state to invalid\n");
+ if (IPA3_USB_IS_TTYPE_DPL(ttype) ||
+ (ipa3_usb_ctx->num_init_prot == 0)) {
+ if (!ipa3_usb_set_state(IPA_USB_INVALID, false, ttype))
+ IPA_USB_ERR(
+ "failed to change state to invalid\n");
+ if (ipa_pm_is_used()) {
+ ipa3_usb_deregister_pm(ttype);
+ ipa3_usb_ctx->ttype_ctx[ttype].ipa_usb_notify_cb = NULL;
+ } else {
ipa_rm_delete_resource(
ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.prod_params.name);
ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.prod_valid =
@@ -2869,9 +2951,8 @@
pr_debug("entry\n");
ipa3_usb_ctx = kzalloc(sizeof(struct ipa3_usb_context), GFP_KERNEL);
if (ipa3_usb_ctx == NULL) {
- pr_err("failed to allocate memory\n");
pr_err(":ipa_usb init failed\n");
- return -EFAULT;
+ return -ENOMEM;
}
memset(ipa3_usb_ctx, 0, sizeof(struct ipa3_usb_context));
diff --git a/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c b/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c
index 613bed3..2723a35 100644
--- a/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c
+++ b/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -140,7 +140,7 @@
ipa_rm_it_handles[resource_name].work_in_progress = false;
pwlock = &(ipa_rm_it_handles[resource_name].w_lock);
name = ipa_rm_it_handles[resource_name].w_lock_name;
- snprintf(name, MAX_WS_NAME, "IPA_RM%d\n", resource_name);
+ snprintf(name, MAX_WS_NAME, "IPA_RM%d", resource_name);
wakeup_source_init(pwlock, name);
INIT_DELAYED_WORK(&ipa_rm_it_handles[resource_name].work,
ipa_rm_inactivity_timer_func);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
index 37614cc..f062ed2 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1455,7 +1455,11 @@
pr_err("Table Size:%d\n",
ipa_ctx->nat_mem.size_base_tables);
- pr_err("Expansion Table Size:%d\n",
+ if (!ipa_ctx->nat_mem.size_expansion_tables)
+ pr_err("Expansion Table Size:%d\n",
+ ipa_ctx->nat_mem.size_expansion_tables);
+ else
+ pr_err("Expansion Table Size:%d\n",
ipa_ctx->nat_mem.size_expansion_tables-1);
if (!ipa_ctx->nat_mem.is_sys_mem)
@@ -1470,6 +1474,8 @@
pr_err("\nBase Table:\n");
} else {
+ if (!ipa_ctx->nat_mem.size_expansion_tables)
+ continue;
tbl_size = ipa_ctx->nat_mem.size_expansion_tables-1;
base_tbl =
(u32 *)ipa_ctx->nat_mem.ipv4_expansion_rules_addr;
@@ -1569,6 +1575,8 @@
pr_err("\nIndex Table:\n");
} else {
+ if (!ipa_ctx->nat_mem.size_expansion_tables)
+ continue;
tbl_size = ipa_ctx->nat_mem.size_expansion_tables-1;
indx_tbl =
(u32 *)ipa_ctx->nat_mem.index_table_expansion_addr;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
index 227a12a..2b55c2d 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1145,6 +1145,15 @@
return -EINVAL;
}
+ if (!strcmp(entry->tbl->name, IPA_DFLT_RT_TBL_NAME)) {
+ IPADBG("Deleting rule from default rt table idx=%u\n",
+ entry->tbl->idx);
+ if (entry->tbl->rule_cnt == 1) {
+ IPAERR_RL("Default tbl last rule cannot be deleted\n");
+ return -EINVAL;
+ }
+ }
+
if (entry->hdr)
__ipa_release_hdr(entry->hdr->id);
else if (entry->proc_ctx)
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c
index a454382..cf8f0b8 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -583,7 +583,8 @@
{
IPADBG("--res_idx=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", res_idx,
&pa, iova, len);
- wdi_res[res_idx].res = kzalloc(sizeof(struct ipa_wdi_res), GFP_KERNEL);
+ wdi_res[res_idx].res = kzalloc(sizeof(*wdi_res[res_idx].res),
+ GFP_KERNEL);
if (!wdi_res[res_idx].res)
BUG();
wdi_res[res_idx].nents = 1;
@@ -609,8 +610,8 @@
return;
}
- wdi_res[res_idx].res = kcalloc(sgt->nents, sizeof(struct ipa_wdi_res),
- GFP_KERNEL);
+ wdi_res[res_idx].res = kcalloc(sgt->nents,
+ sizeof(*wdi_res[res_idx].res), GFP_KERNEL);
if (!wdi_res[res_idx].res)
BUG();
wdi_res[res_idx].nents = sgt->nents;
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index 5b2d13a..068c6c5 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -1661,6 +1661,7 @@
IPAWANERR("Failed to allocate memory.\n");
return -ENOMEM;
}
+ extend_ioctl_data.u.if_name[IFNAMSIZ-1] = '\0';
len = sizeof(wan_msg->upstream_ifname) >
sizeof(extend_ioctl_data.u.if_name) ?
sizeof(extend_ioctl_data.u.if_name) :
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index ae24675..01c0736 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -4394,6 +4394,8 @@
/* Prevent consequent calls from trying to load the FW again. */
if (ipa3_ctx->ipa_initialization_complete)
return 0;
+ /* move proxy vote for modem on ipa3_post_init */
+ IPA_ACTIVE_CLIENTS_INC_SPECIAL("PROXY_CLK_VOTE");
/*
* indication whether working in MHI config or non MHI config is given
@@ -4858,7 +4860,6 @@
int result = 0;
int i;
struct ipa3_rt_tbl_set *rset;
- struct ipa_active_client_logging_info log_info;
IPADBG("IPA Driver initialization started\n");
@@ -5049,8 +5050,7 @@
}
mutex_init(&ipa3_ctx->ipa3_active_clients.mutex);
- IPA_ACTIVE_CLIENTS_PREP_SPECIAL(log_info, "PROXY_CLK_VOTE");
- ipa3_active_clients_log_inc(&log_info, false);
+ /* move proxy vote for modem to ipa3_post_init() */
atomic_set(&ipa3_ctx->ipa3_active_clients.cnt, 1);
/* Create workqueues for power management */
@@ -5296,6 +5296,8 @@
IPADBG("ipa cdev added successful. major:%d minor:%d\n",
MAJOR(ipa3_ctx->dev_num),
MINOR(ipa3_ctx->dev_num));
+ /* proxy vote for modem is added in ipa3_post_init() phase */
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
return 0;
fail_cdev_add:
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index 901b51b..17e4838 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
@@ -548,10 +548,17 @@
return 0;
}
-int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr, u32 size, bool map)
+int ipa3_smmu_map_peer_buff(u64 iova, u32 size, bool map, struct sg_table *sgt)
{
struct iommu_domain *smmu_domain;
int res;
+ phys_addr_t phys;
+ unsigned long va;
+ struct scatterlist *sg;
+ int count = 0;
+ size_t len;
+ int i;
+ struct page *page;
if (ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_AP])
return 0;
@@ -562,33 +569,53 @@
return -EINVAL;
}
+ /*
+ * USB GSI driver would update sgt irrespective of USB S1
+ * is enable or bypass.
+ * If USB S1 is enabled using IOMMU, iova != pa.
+ * If USB S1 is bypass, iova == pa.
+ */
if (map) {
- res = ipa3_iommu_map(smmu_domain,
- rounddown(iova, PAGE_SIZE),
- rounddown(phys_addr, PAGE_SIZE),
- roundup(size + iova - rounddown(iova, PAGE_SIZE),
- PAGE_SIZE),
- IOMMU_READ | IOMMU_WRITE);
- if (res) {
- IPAERR("Fail to map 0x%llx->0x%pa\n", iova, &phys_addr);
- return -EINVAL;
+ if (sgt != NULL) {
+ va = rounddown(iova, PAGE_SIZE);
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ page = sg_page(sg);
+ phys = page_to_phys(page);
+ len = PAGE_ALIGN(sg->offset + sg->length);
+ res = ipa3_iommu_map(smmu_domain, va, phys,
+ len, IOMMU_READ | IOMMU_WRITE);
+ if (res) {
+ IPAERR("Fail to map pa=%pa\n", &phys);
+ return -EINVAL;
+ }
+ va += len;
+ count++;
+ }
+ } else {
+ res = ipa3_iommu_map(smmu_domain,
+ rounddown(iova, PAGE_SIZE),
+ rounddown(iova, PAGE_SIZE),
+ roundup(size + iova -
+ rounddown(iova, PAGE_SIZE),
+ PAGE_SIZE),
+ IOMMU_READ | IOMMU_WRITE);
+ if (res) {
+ IPAERR("Fail to map 0x%llx\n", iova);
+ return -EINVAL;
+ }
}
} else {
res = iommu_unmap(smmu_domain,
- rounddown(iova, PAGE_SIZE),
- roundup(size + iova - rounddown(iova, PAGE_SIZE),
- PAGE_SIZE));
+ rounddown(iova, PAGE_SIZE),
+ roundup(size + iova - rounddown(iova, PAGE_SIZE),
+ PAGE_SIZE));
if (res != roundup(size + iova - rounddown(iova, PAGE_SIZE),
PAGE_SIZE)) {
- IPAERR("Fail to unmap 0x%llx->0x%pa\n",
- iova, &phys_addr);
+ IPAERR("Fail to unmap 0x%llx\n", iova);
return -EINVAL;
}
}
-
- IPADBG("Peer buff %s 0x%llx->0x%pa\n", map ? "map" : "unmap",
- iova, &phys_addr);
-
+ IPADBG("Peer buff %s 0x%llx\n", map ? "map" : "unmap", iova);
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index c0fe730..90edd2b 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -222,7 +222,13 @@
tx_pkt->no_unmap_dma = true;
tx_pkt->sys = sys;
spin_lock_bh(&sys->spinlock);
+ if (unlikely(!sys->nop_pending)) {
+ spin_unlock_bh(&sys->spinlock);
+ kmem_cache_free(ipa3_ctx->tx_pkt_wrapper_cache, tx_pkt);
+ return;
+ }
list_add_tail(&tx_pkt->link, &sys->head_desc_list);
+ sys->nop_pending = false;
spin_unlock_bh(&sys->spinlock);
memset(&nop_xfer, 0, sizeof(nop_xfer));
@@ -273,6 +279,7 @@
int result;
u32 mem_flag = GFP_ATOMIC;
const struct ipa_gsi_ep_config *gsi_ep_cfg;
+ bool send_nop = false;
if (unlikely(!in_atomic))
mem_flag = GFP_KERNEL;
@@ -410,10 +417,14 @@
}
kfree(gsi_xfer_elem_array);
+ if (sys->use_comm_evt_ring && !sys->nop_pending) {
+ sys->nop_pending = true;
+ send_nop = true;
+ }
spin_unlock_bh(&sys->spinlock);
/* set the timer for sending the NOP descriptor */
- if (sys->use_comm_evt_ring && !hrtimer_active(&sys->db_timer)) {
+ if (send_nop) {
ktime_t time = ktime_set(0, IPA_TX_SEND_COMPL_NOP_DELAY_NS);
IPADBG_LOW("scheduling timer for ch %lu\n",
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index d7d74a3..7bd1731 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -626,6 +626,7 @@
struct delayed_work switch_to_intr_work;
enum ipa3_sys_pipe_policy policy;
bool use_comm_evt_ring;
+ bool nop_pending;
int (*pyld_hdlr)(struct sk_buff *skb, struct ipa3_sys_context *sys);
struct sk_buff * (*get_skb)(unsigned int len, gfp_t flags);
void (*free_skb)(struct sk_buff *skb);
@@ -2300,8 +2301,7 @@
int ipa3_rx_poll(u32 clnt_hdl, int budget);
void ipa3_recycle_wan_skb(struct sk_buff *skb);
int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map);
-int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr,
- u32 size, bool map);
+int ipa3_smmu_map_peer_buff(u64 iova, u32 size, bool map, struct sg_table *sgt);
void ipa3_reset_freeze_vote(void);
int ipa3_ntn_init(void);
int ipa3_get_ntn_stats(struct Ipa3HwStatsNTNInfoData_t *stats);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c
index 80a39b5..140afa8 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c
@@ -156,6 +156,7 @@
* @activate_work: work for activate (blocking case)
* @deactivate work: delayed work for deferred_deactivate function
* @complete: generic wait-for-completion handler
+ * @wlock: wake source to prevent AP suspend
*/
struct ipa_pm_client {
char name[IPA_PM_MAX_EX_CL];
@@ -170,6 +171,7 @@
struct work_struct activate_work;
struct delayed_work deactivate_work;
struct completion complete;
+ struct wakeup_source wlock;
};
/*
@@ -395,8 +397,11 @@
unsigned long flags;
client = container_of(work, struct ipa_pm_client, activate_work);
- if (!client->skip_clk_vote)
+ if (!client->skip_clk_vote) {
IPA_ACTIVE_CLIENTS_INC_SPECIAL(client->name);
+ if (client->group == IPA_PM_GROUP_APPS)
+ __pm_stay_awake(&client->wlock);
+ }
spin_lock_irqsave(&client->state_lock, flags);
IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
@@ -414,8 +419,11 @@
complete_all(&client->complete);
if (dec_clk) {
- if (!client->skip_clk_vote)
+ if (!client->skip_clk_vote) {
IPA_ACTIVE_CLIENTS_DEC_SPECIAL(client->name);
+ if (client->group == IPA_PM_GROUP_APPS)
+ __pm_relax(&client->wlock);
+ }
IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
return;
@@ -464,8 +472,11 @@
client->state = IPA_PM_DEACTIVATED;
IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
spin_unlock_irqrestore(&client->state_lock, flags);
- if (!client->skip_clk_vote)
+ if (!client->skip_clk_vote) {
IPA_ACTIVE_CLIENTS_DEC_SPECIAL(client->name);
+ if (client->group == IPA_PM_GROUP_APPS)
+ __pm_relax(&client->wlock);
+ }
deactivate_client(client->hdl);
do_clk_scaling();
@@ -669,7 +680,7 @@
}
/**
- * ipa_rm_delete_register() - register an IPA PM client with the PM
+ * ipa_pm_register() - register an IPA PM client with the PM
* @register_params: params for a client like throughput, callback, etc.
* @hdl: int pointer that will be used as an index to access the client
*
@@ -681,6 +692,7 @@
int ipa_pm_register(struct ipa_pm_register_params *params, u32 *hdl)
{
struct ipa_pm_client *client;
+ struct wakeup_source *wlock;
if (ipa_pm_ctx == NULL) {
IPA_PM_ERR("PM_ctx is null\n");
@@ -729,6 +741,8 @@
client->group = params->group;
client->hdl = *hdl;
client->skip_clk_vote = params->skip_clk_vote;
+ wlock = &client->wlock;
+ wakeup_source_init(wlock, client->name);
/* add client to exception list */
if (add_client_to_exception_list(*hdl)) {
@@ -792,6 +806,7 @@
if (ipa_pm_ctx->clients_by_pipe[i] == ipa_pm_ctx->clients[hdl])
ipa_pm_ctx->clients_by_pipe[i] = NULL;
}
+ wakeup_source_trash(&client->wlock);
kfree(client);
ipa_pm_ctx->clients[hdl] = NULL;
@@ -909,6 +924,8 @@
/* we got the clocks */
if (result == 0) {
client->state = IPA_PM_ACTIVATED;
+ if (client->group == IPA_PM_GROUP_APPS)
+ __pm_stay_awake(&client->wlock);
spin_unlock_irqrestore(&client->state_lock, flags);
activate_client(client->hdl);
if (sync)
@@ -1072,8 +1089,11 @@
IPA_PM_DBG_STATE(client->hdl, client->name,
client->state);
spin_unlock_irqrestore(&client->state_lock, flags);
- if (!client->skip_clk_vote)
+ if (!client->skip_clk_vote) {
IPA_ACTIVE_CLIENTS_DEC_SPECIAL(client->name);
+ if (client->group == IPA_PM_GROUP_APPS)
+ __pm_relax(&client->wlock);
+ }
deactivate_client(client->hdl);
} else /* if activated or deactivated, we do nothing */
spin_unlock_irqrestore(&client->state_lock, flags);
@@ -1124,8 +1144,11 @@
spin_unlock_irqrestore(&client->state_lock, flags);
/* else case (Deactivates all Activated cases)*/
- if (!client->skip_clk_vote)
+ if (!client->skip_clk_vote) {
IPA_ACTIVE_CLIENTS_DEC_SPECIAL(client->name);
+ if (client->group == IPA_PM_GROUP_APPS)
+ __pm_relax(&client->wlock);
+ }
spin_lock_irqsave(&client->state_lock, flags);
client->state = IPA_PM_DEACTIVATED;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
index fc76604..1bdc0fb 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1282,6 +1282,15 @@
return -EINVAL;
}
+ if (!strcmp(entry->tbl->name, IPA_DFLT_RT_TBL_NAME)) {
+ IPADBG("Deleting rule from default rt table idx=%u\n",
+ entry->tbl->idx);
+ if (entry->tbl->rule_cnt == 1) {
+ IPAERR_RL("Default tbl last rule cannot be deleted\n");
+ return -EINVAL;
+ }
+ }
+
if (entry->hdr)
__ipa3_release_hdr(entry->hdr->id);
else if (entry->proc_ctx)
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
index 941e489..648db5e 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1209,8 +1209,6 @@
IPADBG("Skipping endpoint configuration.\n");
}
- ipa3_enable_data_path(ipa_ep_idx);
-
out->clnt_hdl = ipa_ep_idx;
if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(in->sys.client))
@@ -1316,6 +1314,7 @@
struct ipa3_ep_context *ep;
union IpaHwWdiCommonChCmdData_t enable;
struct ipa_ep_cfg_holb holb_cfg;
+ struct ipahal_reg_endp_init_rsrc_grp rsrc_grp;
if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
ipa3_ctx->ep[clnt_hdl].valid == 0) {
@@ -1348,6 +1347,20 @@
goto uc_timeout;
}
+ /* Assign the resource group for pipe */
+ memset(&rsrc_grp, 0, sizeof(rsrc_grp));
+ rsrc_grp.rsrc_grp = ipa_get_ep_group(ep->client);
+ if (rsrc_grp.rsrc_grp == -1) {
+ IPAERR("invalid group for client %d\n", ep->client);
+ WARN_ON(1);
+ return -EFAULT;
+ }
+
+ IPADBG("Setting group %d for pipe %d\n",
+ rsrc_grp.rsrc_grp, clnt_hdl);
+ ipahal_write_reg_n_fields(IPA_ENDP_INIT_RSRC_GRP_n, clnt_hdl,
+ &rsrc_grp);
+
if (IPA_CLIENT_IS_CONS(ep->client)) {
memset(&holb_cfg, 0, sizeof(holb_cfg));
holb_cfg.en = IPA_HOLB_TMR_DIS;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index 1782fdc..9974b87 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -1117,7 +1117,7 @@
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 7, 0, 8, 16, IPA_EE_UC } },
+ { 6, 2, 8, 16, IPA_EE_UC } },
[IPA_4_0][IPA_CLIENT_USB_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
@@ -1153,13 +1153,7 @@
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 9, 1, 8, 16, IPA_EE_UC } },
- [IPA_4_0][IPA_CLIENT_Q6_LAN_PROD] = {
- true, IPA_v4_0_GROUP_UL_DL,
- true,
- IPA_DPS_HPS_SEQ_TYPE_PKT_PROCESS_NO_DEC_UCP,
- QMB_MASTER_SELECT_DDR,
- { 6, 2, 12, 24, IPA_EE_Q6 } },
+ { 9, 0, 8, 16, IPA_EE_UC } },
[IPA_4_0][IPA_CLIENT_Q6_WAN_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
@@ -1196,13 +1190,13 @@
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- {7, 0, 8, 16, IPA_EE_UC } },
+ { 7, 9, 8, 16, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST4_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 8, 10, 8, 16, IPA_EE_AP } },
+ {8, 10, 8, 16, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_WLAN1_CONS] = {
@@ -1210,7 +1204,7 @@
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 18, 2, 6, 9, IPA_EE_UC } },
+ { 18, 3, 6, 9, IPA_EE_UC } },
[IPA_4_0][IPA_CLIENT_WLAN2_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
@@ -1258,7 +1252,7 @@
true,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 22, 3, 17, 17, IPA_EE_UC } },
+ { 22, 1, 17, 17, IPA_EE_UC } },
[IPA_4_0][IPA_CLIENT_Q6_LAN_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
@@ -1284,25 +1278,25 @@
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_PCIE,
- { 12, 2, 5, 5, IPA_EE_AP } },
+ { 11, 6, 9, 9, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST1_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { 12, 2, 5, 5, IPA_EE_AP } },
+ QMB_MASTER_SELECT_PCIE,
+ { 11, 6, 9, 9, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST2_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_PCIE,
- { 18, 2, 6, 9, IPA_EE_UC } },
+ { 12, 2, 5, 5, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST3_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { 20, 13, 9, 9, IPA_EE_AP } },
+ QMB_MASTER_SELECT_PCIE,
+ { 19, 12, 9, 9, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST4_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
@@ -1318,12 +1312,6 @@
{ 31, 31, 8, 8, IPA_EE_AP } },
/* IPA_4_0_MHI */
- [IPA_4_0_MHI][IPA_CLIENT_USB_PROD] = {
- false, IPA_EP_NOT_ALLOCATED,
- true,
- IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { -1, -1, -1, -1, -1 } },
[IPA_4_0_MHI][IPA_CLIENT_APPS_WAN_PROD] = {
true, IPA_v4_0_MHI_GROUP_DDR,
true,
@@ -1342,12 +1330,6 @@
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_PCIE,
{ 1, 0, 8, 16, IPA_EE_AP } },
- [IPA_4_0_MHI][IPA_CLIENT_Q6_LAN_PROD] = {
- true, IPA_v4_0_MHI_GROUP_DDR,
- true,
- IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
- QMB_MASTER_SELECT_DDR,
- { 6, 2, 12, 24, IPA_EE_Q6 } },
[IPA_4_0_MHI][IPA_CLIENT_Q6_WAN_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
@@ -1404,18 +1386,7 @@
QMB_MASTER_SELECT_DDR,
{ 8, 10, 8, 16, IPA_EE_AP } },
- [IPA_4_0_MHI][IPA_CLIENT_USB_CONS] = {
- false, IPA_EP_NOT_ALLOCATED,
- false,
- IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { -1, -1, -1, -1, -1 } },
- [IPA_4_0_MHI][IPA_CLIENT_USB_DPL_CONS] = {
- false, IPA_EP_NOT_ALLOCATED,
- false,
- IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { -1, -1, -1, -1, -1 } },
+
[IPA_4_0_MHI][IPA_CLIENT_APPS_LAN_CONS] = {
true, IPA_v4_0_MHI_GROUP_DDR,
false,
@@ -1470,25 +1441,25 @@
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_PCIE,
- { 12, 2, 5, 5, IPA_EE_AP } },
+ { 11, 6, 9, 9, IPA_EE_AP } },
[IPA_4_0_MHI][IPA_CLIENT_TEST1_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { 12, 2, 5, 5, IPA_EE_AP } },
+ QMB_MASTER_SELECT_PCIE,
+ { 11, 6, 9, 9, IPA_EE_AP } },
[IPA_4_0_MHI][IPA_CLIENT_TEST2_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_PCIE,
- { 18, 11, 6, 9, IPA_EE_AP } },
+ QMB_MASTER_SELECT_DDR,
+ { 12, 2, 5, 5, IPA_EE_AP } },
[IPA_4_0_MHI][IPA_CLIENT_TEST3_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { 20, 13, 9, 9, IPA_EE_AP } },
+ QMB_MASTER_SELECT_PCIE,
+ { 19, 12, 9, 9, IPA_EE_AP } },
[IPA_4_0_MHI][IPA_CLIENT_TEST4_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
index 4920942..48e7d7c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
@@ -2129,15 +2129,13 @@
return;
}
- valmask->val = (1 & IPA_ENDP_INIT_AGGR_n_AGGR_FORCE_CLOSE_BMSK) <<
- IPA_ENDP_INIT_AGGR_n_AGGR_FORCE_CLOSE_SHFT;
- valmask->mask = IPA_ENDP_INIT_AGGR_n_AGGR_FORCE_CLOSE_BMSK <<
- IPA_ENDP_INIT_AGGR_n_AGGR_FORCE_CLOSE_SHFT;
+ valmask->val = (1 << IPA_ENDP_INIT_AGGR_n_AGGR_FORCE_CLOSE_SHFT) &&
+ IPA_ENDP_INIT_AGGR_n_AGGR_FORCE_CLOSE_BMSK;
+ valmask->mask = IPA_ENDP_INIT_AGGR_n_AGGR_FORCE_CLOSE_BMSK;
- valmask->val |= ((0 & IPA_ENDP_INIT_AGGR_n_AGGR_EN_BMSK) <<
- IPA_ENDP_INIT_AGGR_n_AGGR_EN_SHFT);
- valmask->mask |= ((IPA_ENDP_INIT_AGGR_n_AGGR_EN_BMSK <<
- IPA_ENDP_INIT_AGGR_n_AGGR_EN_SHFT));
+ valmask->val |= ((0 << IPA_ENDP_INIT_AGGR_n_AGGR_EN_SHFT) &&
+ IPA_ENDP_INIT_AGGR_n_AGGR_EN_BMSK);
+ valmask->mask |= IPA_ENDP_INIT_AGGR_n_AGGR_EN_BMSK;
}
u32 ipahal_aggr_get_max_byte_limit(void)
@@ -2188,7 +2186,7 @@
return;
}
IPA_SETFIELD_IN_REG(valmask->val, 1 << ep_idx, shft, bmsk);
- valmask->mask = bmsk << shft;
+ valmask->mask = bmsk;
}
void ipahal_get_fltrt_hash_flush_valmask(
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index 8285ae7..2d86200 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -1740,6 +1740,7 @@
IPAWANERR("Failed to allocate memory.\n");
return -ENOMEM;
}
+ extend_ioctl_data.u.if_name[IFNAMSIZ-1] = '\0';
len = sizeof(wan_msg->upstream_ifname) >
sizeof(extend_ioctl_data.u.if_name) ?
sizeof(extend_ioctl_data.u.if_name) :
@@ -4025,6 +4026,11 @@
ipa3_qmi_init();
/* Register for Modem SSR */
+ if (ipa3_ctx != NULL)
+ /* SSR is not supported yet on IPA 4.0 */
+ if (ipa3_ctx->ipa_hw_type == IPA_HW_v4_0)
+ return platform_driver_register(&rmnet_ipa_driver);
+
rmnet_ipa3_ctx->subsys_notify_handle = subsys_notif_register_notifier(
SUBSYS_MODEM,
&ipa3_ssr_notifier);
diff --git a/drivers/platform/msm/ipa/test/ipa_ut_framework.c b/drivers/platform/msm/ipa/test/ipa_ut_framework.c
index 85699bb..bcbcd87 100644
--- a/drivers/platform/msm/ipa/test/ipa_ut_framework.c
+++ b/drivers/platform/msm/ipa/test/ipa_ut_framework.c
@@ -971,6 +971,7 @@
IS_ERR(ipa_ut_ctx->test_dbgfs_root)) {
IPA_UT_ERR("failed to create test debugfs dir\n");
ret = -EFAULT;
+ destroy_workqueue(ipa_ut_ctx->wq);
goto unlock_mutex;
}
@@ -980,6 +981,7 @@
if (!dfile_enable || IS_ERR(dfile_enable)) {
IPA_UT_ERR("failed to create enable debugfs file\n");
ret = -EFAULT;
+ destroy_workqueue(ipa_ut_ctx->wq);
goto fail_clean_dbgfs;
}
diff --git a/drivers/platform/msm/msm_ext_display.c b/drivers/platform/msm/msm_ext_display.c
index bc4df04..7a93e0e 100644
--- a/drivers/platform/msm/msm_ext_display.c
+++ b/drivers/platform/msm/msm_ext_display.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -44,6 +44,20 @@
EXTCON_NONE,
};
+static int msm_ext_disp_find_index(struct extcon_dev *edev,
+ enum msm_ext_disp_type id)
+{
+ int i;
+
+ /* Find the the index of extcon cable in edev->supported_cable */
+ for (i = 0; i < edev->max_supported; i++) {
+ if (edev->supported_cable[i] == id)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
static int msm_ext_disp_extcon_register(struct msm_ext_disp *ext_disp)
{
int ret = 0;
@@ -145,7 +159,8 @@
enum msm_ext_disp_cable_state new_state)
{
int ret = 0;
- int state;
+ int state, index;
+ enum msm_ext_disp_cable_state current_state;
if (!ext_disp->ops) {
pr_err("codec not registered, skip notification\n");
@@ -154,13 +169,27 @@
}
state = ext_disp->audio_sdev.state;
- ret = extcon_set_state_sync(&ext_disp->audio_sdev,
- ext_disp->current_disp, !!new_state);
- pr_debug("Audio state %s %d\n",
- ext_disp->audio_sdev.state == state ?
- "is same" : "switched to",
- ext_disp->audio_sdev.state);
+ index = msm_ext_disp_find_index(&ext_disp->audio_sdev, type);
+ if (index < 0 || index >= ext_disp->audio_sdev.max_supported) {
+ pr_err("invalid index\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (state & BIT(index))
+ current_state = EXT_DISPLAY_CABLE_CONNECT;
+ else
+ current_state = EXT_DISPLAY_CABLE_DISCONNECT;
+
+ if (current_state == new_state) {
+ ret = -EEXIST;
+ pr_debug("same state\n");
+ } else {
+ ret = extcon_set_state_sync(&ext_disp->audio_sdev,
+ ext_disp->current_disp, !!new_state);
+ pr_debug("state changed to %d\n", new_state);
+ }
end:
return ret;
}
diff --git a/drivers/platform/msm/qcom-geni-se.c b/drivers/platform/msm/qcom-geni-se.c
index 5d094d2..348b287 100644
--- a/drivers/platform/msm/qcom-geni-se.c
+++ b/drivers/platform/msm/qcom-geni-se.c
@@ -31,6 +31,14 @@
#define GENI_SE_IOMMU_VA_START (0x40000000)
#define GENI_SE_IOMMU_VA_SIZE (0xC0000000)
+#ifdef CONFIG_ARM64
+#define GENI_SE_DMA_PTR_L(ptr) ((u32)ptr)
+#define GENI_SE_DMA_PTR_H(ptr) ((u32)(ptr >> 32))
+#else
+#define GENI_SE_DMA_PTR_L(ptr) ((u32)ptr)
+#define GENI_SE_DMA_PTR_H(ptr) 0
+#endif
+
#define NUM_LOG_PAGES 2
#define MAX_CLK_PERF_LEVEL 32
static unsigned long default_bus_bw_set[] = {0, 19200000, 50000000, 100000000};
@@ -999,8 +1007,8 @@
return ret;
geni_write_reg(7, base, SE_DMA_TX_IRQ_EN_SET);
- geni_write_reg((u32)(*tx_dma), base, SE_DMA_TX_PTR_L);
- geni_write_reg((u32)((*tx_dma) >> 32), base, SE_DMA_TX_PTR_H);
+ geni_write_reg(GENI_SE_DMA_PTR_L(*tx_dma), base, SE_DMA_TX_PTR_L);
+ geni_write_reg(GENI_SE_DMA_PTR_H(*tx_dma), base, SE_DMA_TX_PTR_H);
geni_write_reg(1, base, SE_DMA_TX_ATTR);
geni_write_reg(tx_len, base, SE_DMA_TX_LEN);
return 0;
@@ -1033,8 +1041,8 @@
return ret;
geni_write_reg(7, base, SE_DMA_RX_IRQ_EN_SET);
- geni_write_reg((u32)(*rx_dma), base, SE_DMA_RX_PTR_L);
- geni_write_reg((u32)((*rx_dma) >> 32), base, SE_DMA_RX_PTR_H);
+ geni_write_reg(GENI_SE_DMA_PTR_L(*rx_dma), base, SE_DMA_RX_PTR_L);
+ geni_write_reg(GENI_SE_DMA_PTR_H(*rx_dma), base, SE_DMA_RX_PTR_H);
/* RX does not have EOT bit */
geni_write_reg(0, base, SE_DMA_RX_ATTR);
geni_write_reg(rx_len, base, SE_DMA_RX_LEN);
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index 95e3782..dec698f 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -3139,7 +3139,8 @@
}
dev = &ctx->usb_bam_pdev->dev;
- if (dev && dev->parent && !device_property_present(dev->parent,
+ if (dev && dev->parent && device_property_present(dev->parent, "iommus")
+ && !device_property_present(dev->parent,
"qcom,smmu-s1-bypass")) {
pr_info("%s: setting SPS_BAM_SMMU_EN flag with (%s)\n",
__func__, dev_name(dev));
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index ceeb8c1..00d82e8 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -848,5 +848,5 @@
pr_info("Mapper unloaded\n");
}
-subsys_initcall(acpi_wmi_init);
+subsys_initcall_sync(acpi_wmi_init);
module_exit(acpi_wmi_exit);
diff --git a/drivers/power/reset/zx-reboot.c b/drivers/power/reset/zx-reboot.c
index b0b1eb3..76153ac 100644
--- a/drivers/power/reset/zx-reboot.c
+++ b/drivers/power/reset/zx-reboot.c
@@ -81,3 +81,7 @@
},
};
module_platform_driver(zx_reboot_driver);
+
+MODULE_DESCRIPTION("ZTE SoCs reset driver");
+MODULE_AUTHOR("Jun Nie <jun.nie@linaro.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h
index 99120f4..a2a35c6 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -170,6 +170,7 @@
FG_SRAM_SYS_TERM_CURR,
FG_SRAM_CHG_TERM_CURR,
FG_SRAM_CHG_TERM_BASE_CURR,
+ FG_SRAM_CUTOFF_CURR,
FG_SRAM_DELTA_MSOC_THR,
FG_SRAM_DELTA_BSOC_THR,
FG_SRAM_RECHARGE_SOC_THR,
@@ -262,6 +263,7 @@
int chg_term_curr_ma;
int chg_term_base_curr_ma;
int sys_term_curr_ma;
+ int cutoff_curr_ma;
int delta_soc_thr;
int recharge_soc_thr;
int recharge_volt_thr_mv;
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index 2c62218..0894f37 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -35,6 +35,8 @@
#define ESR_PULSE_THRESH_OFFSET 3
#define SLOPE_LIMIT_WORD 3
#define SLOPE_LIMIT_OFFSET 0
+#define CUTOFF_CURR_WORD 4
+#define CUTOFF_CURR_OFFSET 0
#define CUTOFF_VOLT_WORD 5
#define CUTOFF_VOLT_OFFSET 0
#define SYS_TERM_CURR_WORD 6
@@ -208,6 +210,8 @@
1000000, 122070, 0, fg_encode_current, NULL),
PARAM(CHG_TERM_CURR, CHG_TERM_CURR_WORD, CHG_TERM_CURR_OFFSET, 1,
100000, 390625, 0, fg_encode_current, NULL),
+ PARAM(CUTOFF_CURR, CUTOFF_CURR_WORD, CUTOFF_CURR_OFFSET, 3,
+ 1000000, 122070, 0, fg_encode_current, NULL),
PARAM(DELTA_MSOC_THR, DELTA_MSOC_THR_WORD, DELTA_MSOC_THR_OFFSET, 1,
2048, 100, 0, fg_encode_default, NULL),
PARAM(DELTA_BSOC_THR, DELTA_BSOC_THR_WORD, DELTA_BSOC_THR_OFFSET, 1,
@@ -284,6 +288,8 @@
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(CUTOFF_CURR, CUTOFF_CURR_WORD, CUTOFF_CURR_OFFSET, 3,
+ 1000000, 122070, 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,
@@ -3974,6 +3980,16 @@
return rc;
}
+ fg_encode(chip->sp, FG_SRAM_CUTOFF_CURR, chip->dt.cutoff_curr_ma,
+ buf);
+ rc = fg_sram_write(chip, chip->sp[FG_SRAM_CUTOFF_CURR].addr_word,
+ chip->sp[FG_SRAM_CUTOFF_CURR].addr_byte, buf,
+ chip->sp[FG_SRAM_CUTOFF_CURR].len, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing cutoff_curr, rc=%d\n", rc);
+ 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);
@@ -4697,6 +4713,7 @@
#define DEFAULT_CHG_TERM_CURR_MA 100
#define DEFAULT_CHG_TERM_BASE_CURR_MA 75
#define DEFAULT_SYS_TERM_CURR_MA -125
+#define DEFAULT_CUTOFF_CURR_MA 500
#define DEFAULT_DELTA_SOC_THR 1
#define DEFAULT_RECHARGE_SOC_THR 95
#define DEFAULT_BATT_TEMP_COLD 0
@@ -4860,6 +4877,12 @@
else
chip->dt.chg_term_base_curr_ma = temp;
+ rc = of_property_read_u32(node, "qcom,fg-cutoff-current", &temp);
+ if (rc < 0)
+ chip->dt.cutoff_curr_ma = DEFAULT_CUTOFF_CURR_MA;
+ else
+ chip->dt.cutoff_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;
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index e7d13ae..8e367c5 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -350,6 +350,16 @@
To compile this driver as a module, choose M here: the module
will be called pwm-rcar.
+config PWM_QTI_LPG
+ tristate "Qualcomm Technologies, Inc. LPG driver"
+ depends on MFD_SPMI_PMIC && OF
+ help
+ This driver supports the LPG (Light Pulse Generator) module found in
+ Qualcomm Technologies, Inc. PMIC chips. Each LPG channel can be
+ configured to operate in PWM mode to output a fixed amplitude with
+ variable duty cycle or in LUT (Look up table) mode to output PWM
+ signal with a modulated amplitude.
+
config PWM_RENESAS_TPU
tristate "Renesas TPU PWM support"
depends on ARCH_RENESAS || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 24c1baf..9453eb0 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -33,6 +33,7 @@
obj-$(CONFIG_PWM_PXA) += pwm-pxa.o
obj-$(CONFIG_PWM_QPNP) += pwm-qpnp.o
obj-$(CONFIG_PWM_RCAR) += pwm-rcar.o
+obj-$(CONFIG_PWM_QTI_LPG) += pwm-qti-lpg.o
obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o
obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
diff --git a/drivers/pwm/pwm-qti-lpg.c b/drivers/pwm/pwm-qti-lpg.c
new file mode 100644
index 0000000..328f4b6
--- /dev/null
+++ b/drivers/pwm/pwm-qti-lpg.c
@@ -0,0 +1,570 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#define REG_SIZE_PER_LPG 0x100
+
+#define REG_LPG_PWM_SIZE_CLK 0x41
+#define REG_LPG_PWM_FREQ_PREDIV_CLK 0x42
+#define REG_LPG_PWM_TYPE_CONFIG 0x43
+#define REG_LPG_PWM_VALUE_LSB 0x44
+#define REG_LPG_PWM_VALUE_MSB 0x45
+#define REG_LPG_ENABLE_CONTROL 0x46
+#define REG_LPG_PWM_SYNC 0x47
+
+/* REG_LPG_PWM_SIZE_CLK */
+#define LPG_PWM_SIZE_MASK BIT(4)
+#define LPG_PWM_SIZE_SHIFT 4
+#define LPG_PWM_CLK_FREQ_SEL_MASK GENMASK(1, 0)
+
+/* REG_LPG_PWM_FREQ_PREDIV_CLK */
+#define LPG_PWM_FREQ_PREDIV_MASK GENMASK(6, 5)
+#define LPG_PWM_FREQ_PREDIV_SHIFT 5
+#define LPG_PWM_FREQ_EXPONENT_MASK GENMASK(2, 0)
+
+/* REG_LPG_PWM_TYPE_CONFIG */
+#define LPG_PWM_EN_GLITCH_REMOVAL_MASK BIT(5)
+
+/* REG_LPG_PWM_VALUE_LSB */
+#define LPG_PWM_VALUE_LSB_MASK GENMASK(7, 0)
+
+/* REG_LPG_PWM_VALUE_MSB */
+#define LPG_PWM_VALUE_MSB_MASK BIT(0)
+
+/* REG_LPG_ENABLE_CONTROL */
+#define LPG_EN_LPG_OUT_BIT BIT(7)
+#define LPG_PWM_SRC_SELECT_MASK BIT(2)
+#define LPG_PWM_SRC_SELECT_SHIFT 2
+#define LPG_EN_RAMP_GEN_MASK BIT(1)
+#define LPG_EN_RAMP_GEN_SHIFT 1
+
+/* REG_LPG_PWM_SYNC */
+#define LPG_PWM_VALUE_SYNC BIT(0)
+
+#define NUM_PWM_SIZE 2
+#define NUM_PWM_CLK 3
+#define NUM_CLK_PREDIV 4
+#define NUM_PWM_EXP 8
+
+enum {
+ LUT_PATTERN = 0,
+ PWM_OUTPUT,
+};
+
+static const int pwm_size[NUM_PWM_SIZE] = {6, 9};
+static const int clk_freq_hz[NUM_PWM_CLK] = {1024, 32768, 19200000};
+static const int clk_prediv[NUM_CLK_PREDIV] = {1, 3, 5, 6};
+static const int pwm_exponent[NUM_PWM_EXP] = {0, 1, 2, 3, 4, 5, 6, 7};
+
+struct lpg_pwm_config {
+ u32 pwm_size;
+ u32 pwm_clk;
+ u32 prediv;
+ u32 clk_exp;
+ u16 pwm_value;
+ u32 best_period_ns;
+};
+
+struct qpnp_lpg_channel {
+ struct qpnp_lpg_chip *chip;
+ struct lpg_pwm_config pwm_config;
+ u32 lpg_idx;
+ u32 reg_base;
+ u8 src_sel;
+ int current_period_ns;
+ int current_duty_ns;
+};
+
+struct qpnp_lpg_chip {
+ struct pwm_chip pwm_chip;
+ struct regmap *regmap;
+ struct device *dev;
+ struct qpnp_lpg_channel *lpgs;
+ struct mutex bus_lock;
+ u32 num_lpgs;
+};
+
+static int qpnp_lpg_write(struct qpnp_lpg_channel *lpg, u16 addr, u8 val)
+{
+ int rc;
+
+ mutex_lock(&lpg->chip->bus_lock);
+ rc = regmap_write(lpg->chip->regmap, lpg->reg_base + addr, val);
+ if (rc < 0)
+ dev_err(lpg->chip->dev, "Write addr 0x%x with value %d failed, rc=%d\n",
+ lpg->reg_base + addr, val, rc);
+ mutex_unlock(&lpg->chip->bus_lock);
+
+ return rc;
+}
+
+static int qpnp_lpg_masked_write(struct qpnp_lpg_channel *lpg,
+ u16 addr, u8 mask, u8 val)
+{
+ int rc;
+
+ mutex_lock(&lpg->chip->bus_lock);
+ rc = regmap_update_bits(lpg->chip->regmap, lpg->reg_base + addr,
+ mask, val);
+ if (rc < 0)
+ dev_err(lpg->chip->dev, "Update addr 0x%x to val 0x%x with mask 0x%x failed, rc=%d\n",
+ lpg->reg_base + addr, val, mask, rc);
+ mutex_unlock(&lpg->chip->bus_lock);
+
+ return rc;
+}
+
+static struct qpnp_lpg_channel *pwm_dev_to_qpnp_lpg(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm) {
+
+ struct qpnp_lpg_chip *chip = container_of(pwm_chip,
+ struct qpnp_lpg_chip, pwm_chip);
+ u32 hw_idx = pwm->hwpwm;
+
+ if (hw_idx >= chip->num_lpgs) {
+ dev_err(chip->dev, "hw index %d out of range [0-%d]\n",
+ hw_idx, chip->num_lpgs - 1);
+ return NULL;
+ }
+
+ return &chip->lpgs[hw_idx];
+}
+
+static int __find_index_in_array(int member, const int array[], int length)
+{
+ int i;
+
+ for (i = 0; i < length; i++) {
+ if (member == array[i])
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int qpnp_lpg_set_pwm_config(struct qpnp_lpg_channel *lpg)
+{
+ int rc;
+ u8 val, mask;
+ int pwm_size_idx, pwm_clk_idx, prediv_idx, clk_exp_idx;
+
+ pwm_size_idx = __find_index_in_array(lpg->pwm_config.pwm_size,
+ pwm_size, ARRAY_SIZE(pwm_size));
+ pwm_clk_idx = __find_index_in_array(lpg->pwm_config.pwm_clk,
+ clk_freq_hz, ARRAY_SIZE(clk_freq_hz));
+ prediv_idx = __find_index_in_array(lpg->pwm_config.prediv,
+ clk_prediv, ARRAY_SIZE(clk_prediv));
+ clk_exp_idx = __find_index_in_array(lpg->pwm_config.clk_exp,
+ pwm_exponent, ARRAY_SIZE(pwm_exponent));
+
+ if (pwm_size_idx < 0 || pwm_clk_idx < 0
+ || prediv_idx < 0 || clk_exp_idx < 0)
+ return -EINVAL;
+
+ /* pwm_clk_idx is 1 bit lower than the register value */
+ pwm_clk_idx += 1;
+ val = pwm_size_idx << LPG_PWM_SIZE_SHIFT | pwm_clk_idx;
+ mask = LPG_PWM_SIZE_MASK | LPG_PWM_CLK_FREQ_SEL_MASK;
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_PWM_SIZE_CLK, mask, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_SIZE_CLK failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = prediv_idx << LPG_PWM_FREQ_PREDIV_SHIFT | clk_exp_idx;
+ mask = LPG_PWM_FREQ_PREDIV_MASK | LPG_PWM_FREQ_EXPONENT_MASK;
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_PWM_FREQ_PREDIV_CLK, mask, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_FREQ_PREDIV_CLK failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = lpg->pwm_config.pwm_value & LPG_PWM_VALUE_LSB_MASK;
+ rc = qpnp_lpg_write(lpg, REG_LPG_PWM_VALUE_LSB, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_VALUE_LSB failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = lpg->pwm_config.pwm_value >> 8;
+ mask = LPG_PWM_VALUE_MSB_MASK;
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_PWM_VALUE_MSB, mask, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_VALUE_MSB failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = LPG_PWM_VALUE_SYNC;
+ rc = qpnp_lpg_write(lpg, REG_LPG_PWM_SYNC, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_SYNC failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static void __qpnp_lpg_calc_pwm_period(int period_ns,
+ struct lpg_pwm_config *pwm_config)
+{
+ struct lpg_pwm_config configs[NUM_PWM_SIZE];
+ int i, j, m, n;
+ int tmp1, tmp2;
+ int clk_period_ns = 0, pwm_clk_period_ns;
+ int clk_delta_ns = INT_MAX, min_clk_delta_ns = INT_MAX;
+ int pwm_period_delta = INT_MAX, min_pwm_period_delta = INT_MAX;
+ int pwm_size_step;
+
+ /*
+ * (2^pwm_size) * (2^pwm_exp) * prediv * NSEC_PER_SEC
+ * pwm_period = ---------------------------------------------------
+ * clk_freq_hz
+ *
+ * Searching the closest settings for the requested PWM period.
+ */
+ for (n = 0; n < ARRAY_SIZE(pwm_size); n++) {
+ pwm_clk_period_ns = period_ns >> pwm_size[n];
+ for (i = ARRAY_SIZE(clk_freq_hz) - 1; i >= 0; i--) {
+ for (j = 0; j < ARRAY_SIZE(clk_prediv); j++) {
+ for (m = 0; m < ARRAY_SIZE(pwm_exponent); m++) {
+ tmp1 = 1 << pwm_exponent[m];
+ tmp1 *= clk_prediv[j];
+ tmp2 = NSEC_PER_SEC / clk_freq_hz[i];
+
+ clk_period_ns = tmp1 * tmp2;
+
+ clk_delta_ns = abs(pwm_clk_period_ns
+ - clk_period_ns);
+ /*
+ * Find the closest setting for
+ * PWM frequency predivide value
+ */
+ if (clk_delta_ns < min_clk_delta_ns) {
+ min_clk_delta_ns
+ = clk_delta_ns;
+ configs[n].pwm_clk
+ = clk_freq_hz[i];
+ configs[n].prediv
+ = clk_prediv[j];
+ configs[n].clk_exp
+ = pwm_exponent[m];
+ configs[n].pwm_size
+ = pwm_size[n];
+ configs[n].best_period_ns
+ = clk_period_ns;
+ }
+ }
+ }
+ }
+
+ configs[n].best_period_ns *= 1 << pwm_size[n];
+ /* Find the closest setting for PWM period */
+ if (min_clk_delta_ns < INT_MAX >> pwm_size[n])
+ pwm_period_delta = min_clk_delta_ns << pwm_size[n];
+ else
+ pwm_period_delta = INT_MAX;
+ if (pwm_period_delta < min_pwm_period_delta) {
+ min_pwm_period_delta = pwm_period_delta;
+ memcpy(pwm_config, &configs[n],
+ sizeof(struct lpg_pwm_config));
+ }
+ }
+
+ /* Larger PWM size can achieve better resolution for PWM duty */
+ for (n = ARRAY_SIZE(pwm_size) - 1; n > 0; n--) {
+ if (pwm_config->pwm_size >= pwm_size[n])
+ break;
+ pwm_size_step = pwm_size[n] - pwm_config->pwm_size;
+ if (pwm_config->clk_exp >= pwm_size_step) {
+ pwm_config->pwm_size = pwm_size[n];
+ pwm_config->clk_exp -= pwm_size_step;
+ }
+ }
+ pr_debug("PWM setting for period_ns %d: pwm_clk = %dHZ, prediv = %d, exponent = %d, pwm_size = %d\n",
+ period_ns, pwm_config->pwm_clk, pwm_config->prediv,
+ pwm_config->clk_exp, pwm_config->pwm_size);
+ pr_debug("Actual period: %dns\n", pwm_config->best_period_ns);
+}
+
+static void __qpnp_lpg_calc_pwm_duty(int period_ns, int duty_ns,
+ struct lpg_pwm_config *pwm_config)
+{
+ u16 pwm_value, max_pwm_value;
+
+ if ((1 << pwm_config->pwm_size) > (INT_MAX / duty_ns))
+ pwm_value = duty_ns / (period_ns >> pwm_config->pwm_size);
+ else
+ pwm_value = (duty_ns << pwm_config->pwm_size) / period_ns;
+
+ max_pwm_value = (1 << pwm_config->pwm_size) - 1;
+ if (pwm_value > max_pwm_value)
+ pwm_value = max_pwm_value;
+ pwm_config->pwm_value = pwm_value;
+}
+
+static int qpnp_lpg_pwm_config(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+ struct qpnp_lpg_channel *lpg;
+ int rc = 0;
+
+ lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return -ENODEV;
+ }
+
+ if (duty_ns > period_ns) {
+ dev_err(pwm_chip->dev, "Duty %dns is larger than period %dns\n",
+ duty_ns, period_ns);
+ return -EINVAL;
+ }
+
+ if (period_ns != lpg->current_period_ns)
+ __qpnp_lpg_calc_pwm_period(period_ns, &lpg->pwm_config);
+
+ if (period_ns != lpg->current_period_ns ||
+ duty_ns != lpg->current_duty_ns)
+ __qpnp_lpg_calc_pwm_duty(period_ns, duty_ns, &lpg->pwm_config);
+
+ rc = qpnp_lpg_set_pwm_config(lpg);
+ if (rc < 0)
+ dev_err(pwm_chip->dev, "Config PWM failed for channel %d, rc=%d\n",
+ lpg->lpg_idx, rc);
+
+ return rc;
+}
+
+static int qpnp_lpg_pwm_enable(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm)
+{
+ struct qpnp_lpg_channel *lpg;
+ int rc = 0;
+ u8 mask, val;
+
+ lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return -ENODEV;
+ }
+
+ mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT;
+ val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT | LPG_EN_LPG_OUT_BIT;
+
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
+ if (rc < 0)
+ dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n",
+ lpg->lpg_idx, rc);
+
+ return rc;
+}
+
+static void qpnp_lpg_pwm_disable(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm)
+{
+ struct qpnp_lpg_channel *lpg;
+ int rc;
+ u8 mask, val;
+
+ lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return;
+ }
+
+ mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT;
+ val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT;
+
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
+ if (rc < 0)
+ dev_err(pwm_chip->dev, "Disable PWM output failed for channel %d, rc=%d\n",
+ lpg->lpg_idx, rc);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void qpnp_lpg_pwm_dbg_show(struct pwm_chip *pwm_chip, struct seq_file *s)
+{
+ struct qpnp_lpg_channel *lpg;
+ struct lpg_pwm_config *cfg;
+ struct pwm_device *pwm;
+ int i;
+
+ for (i = 0; i < pwm_chip->npwm; i++) {
+ pwm = &pwm_chip->pwms[i];
+
+ lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return;
+ }
+
+ if (test_bit(PWMF_REQUESTED, &pwm->flags)) {
+ seq_printf(s, "LPG %d is requested by %s\n",
+ lpg->lpg_idx + 1, pwm->label);
+ } else {
+ seq_printf(s, "LPG %d is free\n",
+ lpg->lpg_idx + 1);
+ continue;
+ }
+
+ if (pwm_is_enabled(pwm)) {
+ seq_puts(s, " enabled\n");
+ } else {
+ seq_puts(s, " disabled\n");
+ continue;
+ }
+
+ cfg = &lpg->pwm_config;
+ seq_printf(s, " clk = %dHz\n", cfg->pwm_clk);
+ seq_printf(s, " pwm_size = %d\n", cfg->pwm_size);
+ seq_printf(s, " prediv = %d\n", cfg->prediv);
+ seq_printf(s, " exponent = %d\n", cfg->clk_exp);
+ seq_printf(s, " pwm_value = %d\n", cfg->pwm_value);
+ seq_printf(s, " Requested period: %dns, best period = %dns\n",
+ pwm_get_period(pwm), cfg->best_period_ns);
+ }
+}
+#endif
+
+static const struct pwm_ops qpnp_lpg_pwm_ops = {
+ .config = qpnp_lpg_pwm_config,
+ .enable = qpnp_lpg_pwm_enable,
+ .disable = qpnp_lpg_pwm_disable,
+#ifdef CONFIG_DEBUG_FS
+ .dbg_show = qpnp_lpg_pwm_dbg_show,
+#endif
+ .owner = THIS_MODULE,
+};
+
+static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip)
+{
+ int rc = 0, i;
+ u64 base, length;
+ const __be32 *addr;
+
+ addr = of_get_address(chip->dev->of_node, 0, NULL, NULL);
+ if (!addr) {
+ dev_err(chip->dev, "Getting address failed\n");
+ return -EINVAL;
+ }
+ base = be32_to_cpu(addr[0]);
+ length = be32_to_cpu(addr[1]);
+
+ chip->num_lpgs = length / REG_SIZE_PER_LPG;
+ chip->lpgs = devm_kcalloc(chip->dev, chip->num_lpgs,
+ sizeof(*chip->lpgs), GFP_KERNEL);
+ if (!chip->lpgs)
+ return -ENOMEM;
+
+ for (i = 0; i < chip->num_lpgs; i++) {
+ chip->lpgs[i].chip = chip;
+ chip->lpgs[i].lpg_idx = i;
+ chip->lpgs[i].reg_base = base + i * REG_SIZE_PER_LPG;
+ chip->lpgs[i].src_sel = PWM_OUTPUT;
+ }
+
+ return rc;
+}
+
+static int qpnp_lpg_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct qpnp_lpg_chip *chip;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = &pdev->dev;
+ chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
+ if (!chip->regmap) {
+ dev_err(chip->dev, "Getting regmap failed\n");
+ return -EINVAL;
+ }
+
+ rc = qpnp_lpg_parse_dt(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ dev_set_drvdata(chip->dev, chip);
+
+ mutex_init(&chip->bus_lock);
+ chip->pwm_chip.dev = chip->dev;
+ chip->pwm_chip.base = -1;
+ chip->pwm_chip.npwm = chip->num_lpgs;
+ chip->pwm_chip.ops = &qpnp_lpg_pwm_ops;
+
+ rc = pwmchip_add(&chip->pwm_chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Add pwmchip failed, rc=%d\n", rc);
+ mutex_destroy(&chip->bus_lock);
+ }
+
+ return rc;
+}
+
+static int qpnp_lpg_remove(struct platform_device *pdev)
+{
+ struct qpnp_lpg_chip *chip = dev_get_drvdata(&pdev->dev);
+ int rc = 0;
+
+ rc = pwmchip_remove(&chip->pwm_chip);
+ if (rc < 0)
+ dev_err(chip->dev, "Remove pwmchip failed, rc=%d\n", rc);
+
+ mutex_destroy(&chip->bus_lock);
+ dev_set_drvdata(chip->dev, NULL);
+
+ return rc;
+}
+
+static const struct of_device_id qpnp_lpg_of_match[] = {
+ { .compatible = "qcom,pwm-lpg",},
+ { },
+};
+
+static struct platform_driver qpnp_lpg_driver = {
+ .driver = {
+ .name = "qcom,pwm-lpg",
+ .of_match_table = qpnp_lpg_of_match,
+ },
+ .probe = qpnp_lpg_probe,
+ .remove = qpnp_lpg_remove,
+};
+module_platform_driver(qpnp_lpg_driver);
+
+MODULE_DESCRIPTION("QTI LPG driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("pwm:pwm-lpg");
diff --git a/drivers/regulator/cpr-regulator.c b/drivers/regulator/cpr-regulator.c
index 9c47e82..cabcf7f 100644
--- a/drivers/regulator/cpr-regulator.c
+++ b/drivers/regulator/cpr-regulator.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -38,8 +38,6 @@
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/cpr-regulator.h>
-#include <linux/msm_thermal.h>
-#include <linux/msm_tsens.h>
#include <soc/qcom/scm.h>
/* Register Offsets for RB-CPR and Bit Definitions */
@@ -287,7 +285,6 @@
int corner;
int ceiling_max;
struct dentry *debugfs;
- struct device *dev;
/* eFuse parameters */
phys_addr_t efuse_addr;
@@ -319,14 +316,6 @@
/* mem-acc regulator */
struct regulator *mem_acc_vreg;
- /* thermal monitor */
- int tsens_id;
- int cpr_disable_temp_threshold;
- int cpr_enable_temp_threshold;
- bool cpr_disable_on_temperature;
- bool cpr_thermal_disable;
- struct threshold_info tsens_threshold_config;
-
/* CPR parameters */
u32 num_fuse_corners;
u64 cpr_fuse_bits;
@@ -565,8 +554,7 @@
static bool cpr_is_allowed(struct cpr_regulator *cpr_vreg)
{
- if (cpr_vreg->cpr_fuse_disable || !cpr_vreg->enable ||
- cpr_vreg->cpr_thermal_disable)
+ if (cpr_vreg->cpr_fuse_disable || !cpr_vreg->enable)
return false;
else
return true;
@@ -5102,145 +5090,6 @@
return rc;
}
-static int cpr_disable_on_temp(struct cpr_regulator *cpr_vreg, bool disable)
-{
- int rc = 0;
-
- mutex_lock(&cpr_vreg->cpr_mutex);
-
- if (cpr_vreg->cpr_fuse_disable ||
- (cpr_vreg->cpr_thermal_disable == disable))
- goto out;
-
- cpr_vreg->cpr_thermal_disable = disable;
-
- if (cpr_vreg->enable && cpr_vreg->corner) {
- if (disable) {
- cpr_debug(cpr_vreg, "Disabling CPR - below temperature threshold [%d]\n",
- cpr_vreg->cpr_disable_temp_threshold);
- /* disable CPR and force open-loop */
- cpr_ctl_disable(cpr_vreg);
- rc = cpr_regulator_set_voltage(cpr_vreg->rdev,
- cpr_vreg->corner, false);
- if (rc < 0)
- cpr_err(cpr_vreg, "Failed to set voltage, rc=%d\n",
- rc);
- } else {
- /* enable CPR */
- cpr_debug(cpr_vreg, "Enabling CPR - above temperature thresold [%d]\n",
- cpr_vreg->cpr_enable_temp_threshold);
- rc = cpr_regulator_set_voltage(cpr_vreg->rdev,
- cpr_vreg->corner, true);
- if (rc < 0)
- cpr_err(cpr_vreg, "Failed to set voltage, rc=%d\n",
- rc);
- }
- }
-out:
- mutex_unlock(&cpr_vreg->cpr_mutex);
- return rc;
-}
-
-static void tsens_threshold_notify(struct therm_threshold *tsens_cb_data)
-{
- struct threshold_info *info = tsens_cb_data->parent;
- struct cpr_regulator *cpr_vreg = container_of(info,
- struct cpr_regulator, tsens_threshold_config);
- int rc = 0;
-
- cpr_debug(cpr_vreg, "Triggered tsens-notification trip_type=%d for thermal_zone_id=%d\n",
- tsens_cb_data->trip_triggered, tsens_cb_data->sensor_id);
-
- switch (tsens_cb_data->trip_triggered) {
- case THERMAL_TRIP_CONFIGURABLE_HI:
- rc = cpr_disable_on_temp(cpr_vreg, false);
- if (rc < 0)
- cpr_err(cpr_vreg, "Failed to enable CPR, rc=%d\n", rc);
- break;
- case THERMAL_TRIP_CONFIGURABLE_LOW:
- rc = cpr_disable_on_temp(cpr_vreg, true);
- if (rc < 0)
- cpr_err(cpr_vreg, "Failed to disable CPR, rc=%d\n", rc);
- break;
- default:
- cpr_debug(cpr_vreg, "trip-type %d not supported\n",
- tsens_cb_data->trip_triggered);
- break;
- }
-
- if (tsens_cb_data->cur_state != tsens_cb_data->trip_triggered) {
- rc = sensor_mgr_set_threshold(tsens_cb_data->sensor_id,
- tsens_cb_data->threshold);
- if (rc < 0)
- cpr_err(cpr_vreg,
- "Failed to set temp. threshold, rc=%d\n", rc);
- else
- tsens_cb_data->cur_state =
- tsens_cb_data->trip_triggered;
- }
-}
-
-static int cpr_check_tsens(struct cpr_regulator *cpr_vreg)
-{
- int rc = 0;
- struct tsens_device tsens_dev;
- unsigned long temp = 0;
- bool disable;
-
- if (tsens_is_ready() > 0) {
- tsens_dev.sensor_num = cpr_vreg->tsens_id;
- rc = tsens_get_temp(&tsens_dev, &temp);
- if (rc < 0) {
- cpr_err(cpr_vreg, "Faled to read tsens, rc=%d\n", rc);
- return rc;
- }
-
- disable = (int) temp <= cpr_vreg->cpr_disable_temp_threshold;
- rc = cpr_disable_on_temp(cpr_vreg, disable);
- if (rc)
- cpr_err(cpr_vreg, "Failed to %s CPR, rc=%d\n",
- disable ? "disable" : "enable", rc);
- }
-
- return rc;
-}
-
-static int cpr_thermal_init(struct cpr_regulator *cpr_vreg)
-{
- int rc;
- struct device_node *of_node = cpr_vreg->dev->of_node;
-
- if (!of_find_property(of_node, "qcom,cpr-thermal-sensor-id", NULL))
- return 0;
-
- CPR_PROP_READ_U32(cpr_vreg, of_node, "cpr-thermal-sensor-id",
- &cpr_vreg->tsens_id, rc);
- if (rc < 0)
- return rc;
-
- CPR_PROP_READ_U32(cpr_vreg, of_node, "cpr-disable-temp-threshold",
- &cpr_vreg->cpr_disable_temp_threshold, rc);
- if (rc < 0)
- return rc;
-
- CPR_PROP_READ_U32(cpr_vreg, of_node, "cpr-enable-temp-threshold",
- &cpr_vreg->cpr_enable_temp_threshold, rc);
- if (rc < 0)
- return rc;
-
- if (cpr_vreg->cpr_disable_temp_threshold >=
- cpr_vreg->cpr_enable_temp_threshold) {
- cpr_err(cpr_vreg, "Invalid temperature threshold cpr_disable_temp[%d] >= cpr_enable_temp[%d]\n",
- cpr_vreg->cpr_disable_temp_threshold,
- cpr_vreg->cpr_enable_temp_threshold);
- return -EINVAL;
- }
-
- cpr_vreg->cpr_disable_on_temperature = true;
-
- return 0;
-}
-
static int cpr_init_cpr(struct platform_device *pdev,
struct cpr_regulator *cpr_vreg)
{
@@ -6067,7 +5916,13 @@
return -EINVAL;
}
- init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node);
+ cpr_vreg = devm_kzalloc(&pdev->dev, sizeof(struct cpr_regulator),
+ GFP_KERNEL);
+ if (!cpr_vreg)
+ return -ENOMEM;
+
+ init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node,
+ &cpr_vreg->rdesc);
if (!init_data) {
dev_err(dev, "regulator init data is missing\n");
return -EINVAL;
@@ -6078,14 +5933,6 @@
|= REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS;
}
- cpr_vreg = devm_kzalloc(&pdev->dev, sizeof(struct cpr_regulator),
- GFP_KERNEL);
- if (!cpr_vreg) {
- dev_err(dev, "Can't allocate cpr_regulator memory\n");
- return -ENOMEM;
- }
-
- cpr_vreg->dev = &pdev->dev;
cpr_vreg->rdesc.name = init_data->constraints.name;
if (cpr_vreg->rdesc.name == NULL) {
dev_err(dev, "regulator-name missing\n");
@@ -6182,12 +6029,6 @@
return rc;
}
- rc = cpr_thermal_init(cpr_vreg);
- if (rc) {
- cpr_err(cpr_vreg, "Thermal intialization failed rc=%d\n", rc);
- return rc;
- }
-
if (of_property_read_bool(pdev->dev.of_node,
"qcom,disable-closed-loop-in-pc")) {
rc = cpr_init_pm_notification(cpr_vreg);
@@ -6247,17 +6088,6 @@
platform_set_drvdata(pdev, cpr_vreg);
cpr_debugfs_init(cpr_vreg);
- if (cpr_vreg->cpr_disable_on_temperature) {
- rc = cpr_check_tsens(cpr_vreg);
- if (rc < 0) {
- cpr_err(cpr_vreg, "Unable to config CPR on tsens, rc=%d\n",
- rc);
- cpr_apc_exit(cpr_vreg);
- cpr_debugfs_remove(cpr_vreg);
- return rc;
- }
- }
-
/* Register panic notification call back */
cpr_vreg->panic_notifier.notifier_call = cpr_panic_callback;
atomic_notifier_chain_register(&panic_notifier_list,
@@ -6293,10 +6123,6 @@
if (cpr_vreg->cpu_notifier.notifier_call)
unregister_hotcpu_notifier(&cpr_vreg->cpu_notifier);
- if (cpr_vreg->cpr_disable_on_temperature)
- sensor_mgr_remove_threshold(
- &cpr_vreg->tsens_threshold_config);
-
atomic_notifier_chain_unregister(&panic_notifier_list,
&cpr_vreg->panic_notifier);
@@ -6325,56 +6151,6 @@
.resume = cpr_regulator_resume,
};
-static int initialize_tsens_monitor(struct cpr_regulator *cpr_vreg)
-{
- int rc;
-
- rc = cpr_check_tsens(cpr_vreg);
- if (rc < 0) {
- cpr_err(cpr_vreg, "Unable to check tsens, rc=%d\n", rc);
- return rc;
- }
-
- rc = sensor_mgr_init_threshold(&cpr_vreg->tsens_threshold_config,
- cpr_vreg->tsens_id,
- cpr_vreg->cpr_enable_temp_threshold, /* high */
- cpr_vreg->cpr_disable_temp_threshold, /* low */
- tsens_threshold_notify);
- if (rc < 0) {
- cpr_err(cpr_vreg, "Failed to init tsens monitor, rc=%d\n", rc);
- return rc;
- }
-
- rc = sensor_mgr_convert_id_and_set_threshold(
- &cpr_vreg->tsens_threshold_config);
- if (rc < 0)
- cpr_err(cpr_vreg, "Failed to set tsens threshold, rc=%d\n",
- rc);
-
- return rc;
-}
-
-int __init cpr_regulator_late_init(void)
-{
- int rc;
- struct cpr_regulator *cpr_vreg;
-
- mutex_lock(&cpr_regulator_list_mutex);
-
- list_for_each_entry(cpr_vreg, &cpr_regulator_list, list) {
- if (cpr_vreg->cpr_disable_on_temperature) {
- rc = initialize_tsens_monitor(cpr_vreg);
- if (rc)
- cpr_err(cpr_vreg, "Failed to initialize temperature monitor, rc=%d\n",
- rc);
- }
- }
-
- mutex_unlock(&cpr_regulator_list_mutex);
- return 0;
-}
-late_initcall(cpr_regulator_late_init);
-
/**
* cpr_regulator_init() - register cpr-regulator driver
*
diff --git a/drivers/regulator/qpnp-labibb-regulator.c b/drivers/regulator/qpnp-labibb-regulator.c
index f457eea..88c5697 100644
--- a/drivers/regulator/qpnp-labibb-regulator.c
+++ b/drivers/regulator/qpnp-labibb-regulator.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -2840,8 +2840,11 @@
return true;
if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE &&
- labibb->mode == QPNP_LABIBB_LCD_MODE)
+ labibb->mode == QPNP_LABIBB_LCD_MODE) {
+ if (labibb->ttw_en)
+ return false;
return true;
+ }
return false;
}
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index e72234e..9b5fc50 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -576,9 +576,9 @@
};
struct qeth_ipato {
- int enabled;
- int invert4;
- int invert6;
+ bool enabled;
+ bool invert4;
+ bool invert6;
struct list_head entries;
};
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 838ed62..df8f74c 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -1475,9 +1475,9 @@
qeth_set_intial_options(card);
/* IP address takeover */
INIT_LIST_HEAD(&card->ipato.entries);
- card->ipato.enabled = 0;
- card->ipato.invert4 = 0;
- card->ipato.invert6 = 0;
+ card->ipato.enabled = false;
+ card->ipato.invert4 = false;
+ card->ipato.invert6 = false;
/* init QDIO stuff */
qeth_init_qdio_info(card);
INIT_DELAYED_WORK(&card->buffer_reclaim_work, qeth_buffer_reclaim_work);
diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h
index 26f7953..eedf9b0 100644
--- a/drivers/s390/net/qeth_l3.h
+++ b/drivers/s390/net/qeth_l3.h
@@ -80,7 +80,7 @@
int qeth_l3_add_rxip(struct qeth_card *, enum qeth_prot_versions, const u8 *);
void qeth_l3_del_rxip(struct qeth_card *card, enum qeth_prot_versions,
const u8 *);
-int qeth_l3_is_addr_covered_by_ipato(struct qeth_card *, struct qeth_ipaddr *);
+void qeth_l3_update_ipato(struct qeth_card *card);
struct qeth_ipaddr *qeth_l3_get_addr_buffer(enum qeth_prot_versions);
int qeth_l3_add_ip(struct qeth_card *, struct qeth_ipaddr *);
int qeth_l3_delete_ip(struct qeth_card *, struct qeth_ipaddr *);
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index f91e70c..1487f8a 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -168,8 +168,8 @@
}
}
-int qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card,
- struct qeth_ipaddr *addr)
+static bool qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card,
+ struct qeth_ipaddr *addr)
{
struct qeth_ipato_entry *ipatoe;
u8 addr_bits[128] = {0, };
@@ -178,6 +178,8 @@
if (!card->ipato.enabled)
return 0;
+ if (addr->type != QETH_IP_TYPE_NORMAL)
+ return 0;
qeth_l3_convert_addr_to_bits((u8 *) &addr->u, addr_bits,
(addr->proto == QETH_PROT_IPV4)? 4:16);
@@ -293,8 +295,7 @@
memcpy(addr, tmp_addr, sizeof(struct qeth_ipaddr));
addr->ref_counter = 1;
- if (addr->type == QETH_IP_TYPE_NORMAL &&
- qeth_l3_is_addr_covered_by_ipato(card, addr)) {
+ if (qeth_l3_is_addr_covered_by_ipato(card, addr)) {
QETH_CARD_TEXT(card, 2, "tkovaddr");
addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
}
@@ -607,6 +608,27 @@
/*
* IP address takeover related functions
*/
+
+/**
+ * qeth_l3_update_ipato() - Update 'takeover' property, for all NORMAL IPs.
+ *
+ * Caller must hold ip_lock.
+ */
+void qeth_l3_update_ipato(struct qeth_card *card)
+{
+ struct qeth_ipaddr *addr;
+ unsigned int i;
+
+ hash_for_each(card->ip_htable, i, addr, hnode) {
+ if (addr->type != QETH_IP_TYPE_NORMAL)
+ continue;
+ if (qeth_l3_is_addr_covered_by_ipato(card, addr))
+ addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
+ else
+ addr->set_flags &= ~QETH_IPA_SETIP_TAKEOVER_FLAG;
+ }
+}
+
static void qeth_l3_clear_ipato_list(struct qeth_card *card)
{
struct qeth_ipato_entry *ipatoe, *tmp;
@@ -618,6 +640,7 @@
kfree(ipatoe);
}
+ qeth_l3_update_ipato(card);
spin_unlock_bh(&card->ip_lock);
}
@@ -642,8 +665,10 @@
}
}
- if (!rc)
+ if (!rc) {
list_add_tail(&new->entry, &card->ipato.entries);
+ qeth_l3_update_ipato(card);
+ }
spin_unlock_bh(&card->ip_lock);
@@ -666,6 +691,7 @@
(proto == QETH_PROT_IPV4)? 4:16) &&
(ipatoe->mask_bits == mask_bits)) {
list_del(&ipatoe->entry);
+ qeth_l3_update_ipato(card);
kfree(ipatoe);
}
}
diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c
index cffe42f..d6bdfc6 100644
--- a/drivers/s390/net/qeth_l3_sys.c
+++ b/drivers/s390/net/qeth_l3_sys.c
@@ -372,8 +372,8 @@
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
- struct qeth_ipaddr *addr;
- int i, rc = 0;
+ bool enable;
+ int rc = 0;
if (!card)
return -EINVAL;
@@ -386,25 +386,18 @@
}
if (sysfs_streq(buf, "toggle")) {
- card->ipato.enabled = (card->ipato.enabled)? 0 : 1;
- } else if (sysfs_streq(buf, "1")) {
- card->ipato.enabled = 1;
- hash_for_each(card->ip_htable, i, addr, hnode) {
- if ((addr->type == QETH_IP_TYPE_NORMAL) &&
- qeth_l3_is_addr_covered_by_ipato(card, addr))
- addr->set_flags |=
- QETH_IPA_SETIP_TAKEOVER_FLAG;
- }
- } else if (sysfs_streq(buf, "0")) {
- card->ipato.enabled = 0;
- hash_for_each(card->ip_htable, i, addr, hnode) {
- if (addr->set_flags &
- QETH_IPA_SETIP_TAKEOVER_FLAG)
- addr->set_flags &=
- ~QETH_IPA_SETIP_TAKEOVER_FLAG;
- }
- } else
+ enable = !card->ipato.enabled;
+ } else if (kstrtobool(buf, &enable)) {
rc = -EINVAL;
+ goto out;
+ }
+
+ if (card->ipato.enabled != enable) {
+ card->ipato.enabled = enable;
+ spin_lock_bh(&card->ip_lock);
+ qeth_l3_update_ipato(card);
+ spin_unlock_bh(&card->ip_lock);
+ }
out:
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
@@ -430,20 +423,27 @@
const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
+ bool invert;
int rc = 0;
if (!card)
return -EINVAL;
mutex_lock(&card->conf_mutex);
- if (sysfs_streq(buf, "toggle"))
- card->ipato.invert4 = (card->ipato.invert4)? 0 : 1;
- else if (sysfs_streq(buf, "1"))
- card->ipato.invert4 = 1;
- else if (sysfs_streq(buf, "0"))
- card->ipato.invert4 = 0;
- else
+ if (sysfs_streq(buf, "toggle")) {
+ invert = !card->ipato.invert4;
+ } else if (kstrtobool(buf, &invert)) {
rc = -EINVAL;
+ goto out;
+ }
+
+ if (card->ipato.invert4 != invert) {
+ card->ipato.invert4 = invert;
+ spin_lock_bh(&card->ip_lock);
+ qeth_l3_update_ipato(card);
+ spin_unlock_bh(&card->ip_lock);
+ }
+out:
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
@@ -609,20 +609,27 @@
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
+ bool invert;
int rc = 0;
if (!card)
return -EINVAL;
mutex_lock(&card->conf_mutex);
- if (sysfs_streq(buf, "toggle"))
- card->ipato.invert6 = (card->ipato.invert6)? 0 : 1;
- else if (sysfs_streq(buf, "1"))
- card->ipato.invert6 = 1;
- else if (sysfs_streq(buf, "0"))
- card->ipato.invert6 = 0;
- else
+ if (sysfs_streq(buf, "toggle")) {
+ invert = !card->ipato.invert6;
+ } else if (kstrtobool(buf, &invert)) {
rc = -EINVAL;
+ goto out;
+ }
+
+ if (card->ipato.invert6 != invert) {
+ card->ipato.invert6 = invert;
+ spin_lock_bh(&card->ip_lock);
+ qeth_l3_update_ipato(card);
+ spin_unlock_bh(&card->ip_lock);
+ }
+out:
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
index 0aeecec..e2962f1 100644
--- a/drivers/scsi/aacraid/commsup.c
+++ b/drivers/scsi/aacraid/commsup.c
@@ -1416,13 +1416,13 @@
* will ensure that i/o is queisced and the card is flushed in that
* case.
*/
+ aac_free_irq(aac);
aac_fib_map_free(aac);
pci_free_consistent(aac->pdev, aac->comm_size, aac->comm_addr, aac->comm_phys);
aac->comm_addr = NULL;
aac->comm_phys = 0;
kfree(aac->queues);
aac->queues = NULL;
- aac_free_irq(aac);
kfree(aac->fsa_dev);
aac->fsa_dev = NULL;
quirks = aac_get_driver_ident(index)->quirks;
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 9962370..0b8db8a 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -3857,6 +3857,7 @@
if (h->fw_support & MISC_FW_RAID_OFFLOAD_BASIC)
hpsa_get_ioaccel_status(h, scsi3addr, this_device);
volume_offline = hpsa_volume_offline(h, scsi3addr);
+ this_device->volume_offline = volume_offline;
if (volume_offline == HPSA_LV_FAILED) {
rc = HPSA_LV_FAILED;
dev_err(&h->pdev->dev,
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index a530f08..4abd3fc 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -1727,7 +1727,7 @@
if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx)) {
reason = FAILURE_SESSION_IN_RECOVERY;
- sc->result = DID_REQUEUE;
+ sc->result = DID_REQUEUE << 16;
goto fault;
}
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 9965135..3a7a86d 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -149,7 +149,6 @@
struct list_head rq_list; /* head of request list */
struct fasync_struct *async_qp; /* used by asynchronous notification */
Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */
- char low_dma; /* as in parent but possibly overridden to 1 */
char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */
char cmd_q; /* 1 -> allow command queuing, 0 -> don't */
unsigned char next_cmd_len; /* 0: automatic, >0: use on next write() */
@@ -928,26 +927,14 @@
/* strange ..., for backward compatibility */
return sfp->timeout_user;
case SG_SET_FORCE_LOW_DMA:
- result = get_user(val, ip);
- if (result)
- return result;
- if (val) {
- sfp->low_dma = 1;
- if ((0 == sfp->low_dma) && !sfp->res_in_use) {
- val = (int) sfp->reserve.bufflen;
- mutex_lock(&sfp->parentdp->open_rel_lock);
- sg_remove_scat(sfp, &sfp->reserve);
- sg_build_reserve(sfp, val);
- mutex_unlock(&sfp->parentdp->open_rel_lock);
- }
- } else {
- if (atomic_read(&sdp->detaching))
- return -ENODEV;
- sfp->low_dma = sdp->device->host->unchecked_isa_dma;
- }
+ /*
+ * N.B. This ioctl never worked properly, but failed to
+ * return an error value. So returning '0' to keep compability
+ * with legacy applications.
+ */
return 0;
case SG_GET_LOW_DMA:
- return put_user((int) sfp->low_dma, ip);
+ return put_user((int) sdp->device->host->unchecked_isa_dma, ip);
case SG_GET_SCSI_ID:
if (!access_ok(VERIFY_WRITE, p, sizeof (sg_scsi_id_t)))
return -EFAULT;
@@ -1866,6 +1853,7 @@
int sg_tablesize = sfp->parentdp->sg_tablesize;
int blk_size = buff_size, order;
gfp_t gfp_mask = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN;
+ struct sg_device *sdp = sfp->parentdp;
if (blk_size < 0)
return -EFAULT;
@@ -1891,7 +1879,7 @@
scatter_elem_sz_prev = num;
}
- if (sfp->low_dma)
+ if (sdp->device->host->unchecked_isa_dma)
gfp_mask |= GFP_DMA;
if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
@@ -2154,8 +2142,6 @@
sfp->timeout = SG_DEFAULT_TIMEOUT;
sfp->timeout_user = SG_DEFAULT_TIMEOUT_USER;
sfp->force_packid = SG_DEF_FORCE_PACK_ID;
- sfp->low_dma = (SG_DEF_FORCE_LOW_DMA == 0) ?
- sdp->device->host->unchecked_isa_dma : 1;
sfp->cmd_q = SG_DEF_COMMAND_Q;
sfp->keep_orphan = SG_DEF_KEEP_ORPHAN;
sfp->parentdp = sdp;
@@ -2614,7 +2600,7 @@
jiffies_to_msecs(fp->timeout),
fp->reserve.bufflen,
(int) fp->reserve.k_use_sg,
- (int) fp->low_dma);
+ (int) sdp->device->host->unchecked_isa_dma);
seq_printf(s, " cmd_q=%d f_packid=%d k_orphan=%d closed=0\n",
(int) fp->cmd_q, (int) fp->force_packid,
(int) fp->keep_orphan);
diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
index db4e7bb..1b283b2 100644
--- a/drivers/scsi/ufs/Kconfig
+++ b/drivers/scsi/ufs/Kconfig
@@ -83,8 +83,6 @@
tristate "QCOM specific hooks to UFS controller platform driver"
depends on SCSI_UFSHCD_PLATFORM && ARCH_QCOM
select PHY_QCOM_UFS
- select EXTCON
- select EXTCON_GPIO
help
This selects the QCOM specific additions to UFSHCD platform driver.
UFS host on QCOM needs some vendor specific configuration before
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 1a93164..02dfbcc 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -8517,12 +8517,15 @@
struct ufs_vreg *vreg, bool on)
{
int ret = 0;
- struct regulator *reg = vreg->reg;
- const char *name = vreg->name;
+ struct regulator *reg;
+ const char *name;
int min_uV, uA_load;
BUG_ON(!vreg);
+ reg = vreg->reg;
+ name = vreg->name;
+
if (regulator_count_voltages(reg) > 0) {
min_uV = on ? vreg->min_uV : 0;
ret = regulator_set_voltage(reg, min_uV, vreg->max_uV);
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c
index d8cc2c4..59897ea 100644
--- a/drivers/soc/qcom/glink.c
+++ b/drivers/soc/qcom/glink.c
@@ -376,7 +376,7 @@
static struct channel_ctx *ch_name_to_ch_ctx_create(
struct glink_core_xprt_ctx *xprt_ctx,
- const char *name);
+ const char *name, bool local);
static void ch_push_remote_rx_intent(struct channel_ctx *ctx, size_t size,
uint32_t riid, void *cookie);
@@ -1836,13 +1836,14 @@
* it is not found and get reference of context.
* @xprt_ctx: Transport to search for a matching channel.
* @name: Name of the desired channel.
+ * @local: If called from local open or not
*
* Return: The channel corresponding to @name, NULL if a matching channel was
* not found AND a new channel could not be created.
*/
static struct channel_ctx *ch_name_to_ch_ctx_create(
struct glink_core_xprt_ctx *xprt_ctx,
- const char *name)
+ const char *name, bool local)
{
struct channel_ctx *entry;
struct channel_ctx *ctx;
@@ -1886,10 +1887,23 @@
list_for_each_entry_safe(entry, temp, &xprt_ctx->channels,
port_list_node)
if (!strcmp(entry->name, name) && !entry->pending_delete) {
+ rwref_get(&entry->ch_state_lhb2);
+ /* port already exists */
+ if (entry->local_open_state != GLINK_CHANNEL_CLOSED
+ && local) {
+ /* not ready to be re-opened */
+ GLINK_INFO_CH_XPRT(entry, xprt_ctx,
+ "%s: Ch not ready. State: %u\n",
+ __func__, entry->local_open_state);
+ rwref_put(&entry->ch_state_lhb2);
+ entry = NULL;
+ } else if (local) {
+ entry->local_open_state =
+ GLINK_CHANNEL_OPENING;
+ }
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;
}
@@ -1919,6 +1933,8 @@
ctx->transport_ptr = xprt_ctx;
rwref_get(&ctx->ch_state_lhb2);
+ if (local)
+ ctx->local_open_state = GLINK_CHANNEL_OPENING;
list_add_tail(&ctx->port_list_node, &xprt_ctx->channels);
GLINK_INFO_PERF_CH_XPRT(ctx, xprt_ctx,
@@ -2604,23 +2620,13 @@
* look for an existing port structure which can occur in
* reopen and remote-open-first cases
*/
- ctx = ch_name_to_ch_ctx_create(transport_ptr, cfg->name);
+ ctx = ch_name_to_ch_ctx_create(transport_ptr, cfg->name, true);
if (ctx == NULL) {
GLINK_ERR("%s:%s %s: Error - unable to allocate new channel\n",
cfg->transport, cfg->edge, __func__);
return ERR_PTR(-ENOMEM);
}
- /* port already exists */
- if (ctx->local_open_state != GLINK_CHANNEL_CLOSED) {
- /* not ready to be re-opened */
- 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);
- }
-
/* initialize port structure */
ctx->user_priv = cfg->priv;
ctx->rx_intent_req_timeout_jiffies =
@@ -2651,7 +2657,6 @@
ctx->local_xprt_req = best_id;
ctx->no_migrate = cfg->transport &&
!(cfg->options & GLINK_OPT_INITIAL_XPORT);
- ctx->local_open_state = GLINK_CHANNEL_OPENING;
GLINK_INFO_PERF_CH(ctx,
"%s: local:GLINK_CHANNEL_CLOSED->GLINK_CHANNEL_OPENING\n",
__func__);
@@ -4912,7 +4917,7 @@
bool do_migrate;
glink_core_migration_edge_lock(if_ptr->glink_core_priv);
- ctx = ch_name_to_ch_ctx_create(if_ptr->glink_core_priv, name);
+ ctx = ch_name_to_ch_ctx_create(if_ptr->glink_core_priv, name, false);
if (ctx == NULL) {
GLINK_ERR_XPRT(if_ptr->glink_core_priv,
"%s: invalid rcid %u received, name '%s'\n",
@@ -5015,6 +5020,7 @@
struct channel_ctx *ctx;
bool is_ch_fully_closed;
struct glink_core_xprt_ctx *xprt_ptr = if_ptr->glink_core_priv;
+ unsigned long flags;
ctx = xprt_rcid_to_ch_ctx_get(if_ptr->glink_core_priv, rcid);
if (!ctx) {
@@ -5032,11 +5038,13 @@
rwref_put(&ctx->ch_state_lhb2);
return;
}
+ spin_lock_irqsave(&ctx->transport_ptr->xprt_ctx_lock_lhb1, flags);
+ ctx->pending_delete = true;
+ spin_unlock_irqrestore(&ctx->transport_ptr->xprt_ctx_lock_lhb1, flags);
GLINK_INFO_CH(ctx, "%s: remote: OPENED->CLOSED\n", __func__);
is_ch_fully_closed = glink_core_remote_close_common(ctx, false);
- ctx->pending_delete = true;
if_ptr->tx_cmd_ch_remote_close_ack(if_ptr, rcid);
if (is_ch_fully_closed) {
diff --git a/drivers/soc/qcom/glink_ssr.c b/drivers/soc/qcom/glink_ssr.c
index 614b53a..239f2c1 100644
--- a/drivers/soc/qcom/glink_ssr.c
+++ b/drivers/soc/qcom/glink_ssr.c
@@ -29,6 +29,7 @@
#define GLINK_SSR_EVENT_INIT ~0
#define NUM_LOG_PAGES 3
+#define GLINK_SSR_PRIORITY 1
#define GLINK_SSR_LOG(x...) do { \
if (glink_ssr_log_ctx) \
ipc_log_string(glink_ssr_log_ctx, x); \
@@ -945,6 +946,7 @@
nb->subsystem = subsys_name;
nb->nb.notifier_call = glink_ssr_restart_notifier_cb;
+ nb->nb.priority = GLINK_SSR_PRIORITY;
handle = subsys_notif_register_notifier(nb->subsystem, &nb->nb);
if (IS_ERR_OR_NULL(handle)) {
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index a28644d..e3a50e3 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -2262,8 +2262,6 @@
set_bit(ICNSS_FW_READY, &penv->state);
- icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_READY, NULL);
-
icnss_pr_info("WLAN FW is ready: 0x%lx\n", penv->state);
icnss_hw_power_off(penv);
@@ -2356,29 +2354,6 @@
return 0;
}
-static int icnss_call_driver_remove(struct icnss_priv *priv)
-{
- icnss_pr_dbg("Calling driver remove state: 0x%lx\n", priv->state);
-
- clear_bit(ICNSS_FW_READY, &priv->state);
-
- if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
- return 0;
-
- if (!priv->ops || !priv->ops->remove)
- return 0;
-
- set_bit(ICNSS_DRIVER_UNLOADING, &penv->state);
- penv->ops->remove(&priv->pdev->dev);
-
- clear_bit(ICNSS_DRIVER_UNLOADING, &penv->state);
- clear_bit(ICNSS_DRIVER_PROBED, &priv->state);
-
- icnss_hw_power_off(penv);
-
- return 0;
-}
-
static int icnss_fw_crashed(struct icnss_priv *priv,
struct icnss_event_pd_service_down_data *event_data)
{
@@ -2418,10 +2393,7 @@
if (priv->force_err_fatal)
ICNSS_ASSERT(0);
- if (event_data->crashed)
- icnss_fw_crashed(priv, event_data);
- else
- icnss_call_driver_remove(priv);
+ icnss_fw_crashed(priv, event_data);
out:
kfree(data);
@@ -3104,6 +3076,12 @@
if (!dev)
return -ENODEV;
+ if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
+ icnss_pr_err("FW down, ignoring fw_log_mode state: 0x%lx\n",
+ penv->state);
+ return -EINVAL;
+ }
+
icnss_pr_dbg("FW log mode: %u\n", fw_log_mode);
ret = wlfw_ini_send_sync_msg(fw_log_mode);
@@ -3197,6 +3175,12 @@
if (!dev)
return -ENODEV;
+ if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
+ icnss_pr_err("FW down, ignoring wlan_enable state: 0x%lx\n",
+ penv->state);
+ return -EINVAL;
+ }
+
icnss_pr_dbg("Mode: %d, config: %p, host_version: %s\n",
mode, config, host_version);
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_bimc_adhoc.c b/drivers/soc/qcom/msm_bus/msm_bus_bimc_adhoc.c
index 95c127d..974f74e 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_bimc_adhoc.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_bimc_adhoc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -243,7 +243,7 @@
(M_BKE_GC_GC_BMSK >> \
(M_BKE_GC_GC_SHFT + 1))
-static int bimc_div(int64_t *a, uint32_t b)
+static int bimc_div(uint64_t *a, uint32_t b)
{
if ((*a > 0) && (*a < b)) {
*a = 0;
diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c
index 1bc9239..3b6c0bd 100644
--- a/drivers/soc/qcom/peripheral-loader.c
+++ b/drivers/soc/qcom/peripheral-loader.c
@@ -161,7 +161,7 @@
region_info = (struct md_ss_region __iomem *)subsys_segtable_base;
if (!region_info)
return -EINVAL;
- pr_debug("Segments in minidump 0x%x\n", ss_mdump_seg_cnt);
+ pr_info("Minidump : Segments in minidump 0x%x\n", ss_mdump_seg_cnt);
ramdump_segs = kcalloc(ss_mdump_seg_cnt,
sizeof(*ramdump_segs), GFP_KERNEL);
if (!ramdump_segs)
@@ -184,7 +184,7 @@
offset = offset +
sizeof(region_info->region_base_address);
s->size = __raw_readl(offset);
- pr_debug("Minidump : Dumping segment %s with address 0x%lx and size 0x%x\n",
+ pr_info("Minidump : Dumping segment %s with address 0x%lx and size 0x%x\n",
s->name, s->address, (unsigned int)s->size);
} else
ss_valid_seg_cnt--;
@@ -220,15 +220,15 @@
int count = 0, ret;
if (desc->minidump) {
- pr_debug("Minidump : md_ss_toc->md_ss_toc_init is 0x%x\n",
+ pr_info("Minidump : md_ss_toc->md_ss_toc_init is 0x%x\n",
(unsigned int)desc->minidump->md_ss_toc_init);
- pr_debug("Minidump : md_ss_toc->md_ss_enable_status is 0x%x\n",
+ pr_info("Minidump : md_ss_toc->md_ss_enable_status is 0x%x\n",
(unsigned int)desc->minidump->md_ss_enable_status);
- pr_debug("Minidump : md_ss_toc->encryption_status is 0x%x\n",
+ pr_info("Minidump : md_ss_toc->encryption_status is 0x%x\n",
(unsigned int)desc->minidump->encryption_status);
- pr_debug("Minidump : md_ss_toc->ss_region_count is 0x%x\n",
+ pr_info("Minidump : md_ss_toc->ss_region_count is 0x%x\n",
(unsigned int)desc->minidump->ss_region_count);
- pr_debug("Minidump : md_ss_toc->md_ss_smem_regions_baseptr is 0x%x\n",
+ pr_info("Minidump : md_ss_toc->md_ss_smem_regions_baseptr is 0x%x\n",
(unsigned int)
desc->minidump->md_ss_smem_regions_baseptr);
/**
@@ -241,11 +241,11 @@
MD_SS_ENABLED)) {
if (desc->minidump->encryption_status ==
MD_SS_ENCR_DONE) {
- pr_debug("Dumping Minidump for %s\n",
+ pr_info("Minidump : Dumping for %s\n",
desc->name);
return pil_do_minidump(desc, minidump_dev);
}
- pr_debug("Minidump aborted for %s\n", desc->name);
+ pr_info("Minidump : aborted for %s\n", desc->name);
return -EINVAL;
}
}
diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c
index 68ff0f3..bc47a95 100644
--- a/drivers/soc/qcom/pil-msa.c
+++ b/drivers/soc/qcom/pil-msa.c
@@ -557,7 +557,7 @@
{
struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
phys_addr_t start_addr = pil_get_entry_addr(pil);
- u32 debug_val;
+ u32 debug_val = 0;
int ret;
trace_pil_func(__func__);
@@ -576,8 +576,10 @@
if (ret)
goto err_clks;
- /* Save state of modem debug register before full reset */
- debug_val = readl_relaxed(drv->reg_base + QDSP6SS_DBG_CFG);
+ if (!pil->minidump || !pil->modem_ssr) {
+ /* Save state of modem debug register before full reset */
+ debug_val = readl_relaxed(drv->reg_base + QDSP6SS_DBG_CFG);
+ }
/* Assert reset to subsystem */
pil_mss_assert_resets(drv);
@@ -587,9 +589,12 @@
if (ret)
goto err_restart;
- writel_relaxed(debug_val, drv->reg_base + QDSP6SS_DBG_CFG);
- if (modem_dbg_cfg)
- writel_relaxed(modem_dbg_cfg, drv->reg_base + QDSP6SS_DBG_CFG);
+ if (!pil->minidump || !pil->modem_ssr) {
+ writel_relaxed(debug_val, drv->reg_base + QDSP6SS_DBG_CFG);
+ if (modem_dbg_cfg)
+ writel_relaxed(modem_dbg_cfg,
+ drv->reg_base + QDSP6SS_DBG_CFG);
+ }
/* Program Image Address */
if (drv->self_auth) {
@@ -819,8 +824,8 @@
* Need to Wait for timeout for debug reset sequence to
* complete before returning
*/
- pr_debug("Minidump: waiting encryption to complete\n");
- msleep(30000);
+ pr_info("Minidump: waiting encryption to complete\n");
+ msleep(10000);
if (pil->minidump) {
writel_relaxed(0x2, drv->reg_base + QDSP6SS_NMI_CFG);
/* Let write complete before proceeding */
diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c
index 721124c..ac322f8 100644
--- a/drivers/soc/qcom/pil-q6v5-mss.c
+++ b/drivers/soc/qcom/pil-q6v5-mss.c
@@ -167,6 +167,11 @@
if (ret)
return ret;
+ pil_mss_remove_proxy_votes(&drv->q6->desc);
+ ret = pil_mss_make_proxy_votes(&drv->q6->desc);
+ if (ret)
+ return ret;
+
ret = pil_mss_reset_load_mba(&drv->q6->desc);
if (ret)
return ret;
diff --git a/drivers/soc/qcom/scm.c b/drivers/soc/qcom/scm.c
index cab7178..fec6f17 100644
--- a/drivers/soc/qcom/scm.c
+++ b/drivers/soc/qcom/scm.c
@@ -185,9 +185,8 @@
case SCM_ENOMEM:
return -ENOMEM;
case SCM_EBUSY:
- return SCM_EBUSY;
case SCM_V2_EBUSY:
- return SCM_V2_EBUSY;
+ return -EBUSY;
}
return -EINVAL;
}
@@ -338,13 +337,13 @@
do {
ret = scm_call_common(svc_id, cmd_id, cmd_buf, cmd_len,
resp_buf, resp_len, cmd, len);
- if (ret == SCM_EBUSY)
+ if (ret == -EBUSY)
msleep(SCM_EBUSY_WAIT_MS);
if (retry_count == 33)
pr_warn("scm: secure world has been busy for 1 second!\n");
- } while (ret == SCM_EBUSY && (retry_count++ < SCM_EBUSY_MAX_RETRY));
+ } while (ret == -EBUSY && (retry_count++ < SCM_EBUSY_MAX_RETRY));
- if (ret == SCM_EBUSY)
+ if (ret == -EBUSY)
pr_err("scm: secure world busy (rc = SCM_EBUSY)\n");
return ret;
@@ -799,7 +798,7 @@
ret = scm_call_common(svc_id, cmd_id, cmd_buf, cmd_len, resp_buf,
resp_len, cmd, len);
- if (unlikely(ret == SCM_EBUSY))
+ if (unlikely(ret == -EBUSY))
ret = _scm_call_retry(svc_id, cmd_id, cmd_buf, cmd_len,
resp_buf, resp_len, cmd, PAGE_ALIGN(len));
kfree(cmd);
diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c
index 556882c..685b384 100644
--- a/drivers/soc/qcom/socinfo.c
+++ b/drivers/soc/qcom/socinfo.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -591,6 +591,11 @@
[349] = {MSM_CPU_SDM632, "SDM632"},
[350] = {MSM_CPU_SDA632, "SDA632"},
+ /*MSM8937 ID */
+ [294] = {MSM_CPU_8937, "MSM8937"},
+ [295] = {MSM_CPU_8937, "APQ8937"},
+
+
/* Uninitialized IDs are not known to run Linux.
* MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are
* considered as unknown CPU.
@@ -1518,6 +1523,10 @@
dummy_socinfo.id = 293;
strlcpy(dummy_socinfo.build_id, "msm8953 - ",
sizeof(dummy_socinfo.build_id));
+ } else if (early_machine_is_msm8937()) {
+ dummy_socinfo.id = 294;
+ strlcpy(dummy_socinfo.build_id, "msm8937 - ",
+ sizeof(dummy_socinfo.build_id));
} else if (early_machine_is_sdm450()) {
dummy_socinfo.id = 338;
strlcpy(dummy_socinfo.build_id, "sdm450 - ",
diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c
index f5e76e0..6d58d6b 100644
--- a/drivers/soc/qcom/watchdog_v2.c
+++ b/drivers/soc/qcom/watchdog_v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -691,7 +691,7 @@
wdog_dd->user_pet_complete = true;
wdog_dd->user_pet_enabled = false;
wake_up_process(wdog_dd->watchdog_task);
- init_timer(&wdog_dd->pet_timer);
+ init_timer_deferrable(&wdog_dd->pet_timer);
wdog_dd->pet_timer.data = (unsigned long)wdog_dd;
wdog_dd->pet_timer.function = pet_task_wakeup;
wdog_dd->pet_timer.expires = jiffies + delay_time;
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index 7b24c4d..a074763 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -28,6 +28,7 @@
#define SPI_NUM_CHIPSELECT (4)
#define SPI_XFER_TIMEOUT_MS (250)
+#define SPI_AUTO_SUSPEND_DELAY (250)
/* SPI SE specific registers */
#define SE_SPI_CPHA (0x224)
#define SE_SPI_LOOPBACK (0x22C)
@@ -153,6 +154,7 @@
int num_rx_eot;
int num_xfers;
void *ipc;
+ bool shared_se;
};
static struct spi_master *get_spi_master(struct device *dev)
@@ -757,9 +759,22 @@
/* Adjust the AB/IB based on the max speed of the slave.*/
rsc->ib = max_speed * DEFAULT_BUS_WIDTH;
rsc->ab = max_speed * DEFAULT_BUS_WIDTH;
+ if (mas->shared_se) {
+ struct se_geni_rsc *rsc;
+ int ret = 0;
+
+ rsc = &mas->spi_rsc;
+ ret = pinctrl_select_state(rsc->geni_pinctrl,
+ rsc->geni_gpio_active);
+ if (ret)
+ GENI_SE_ERR(mas->ipc, false, NULL,
+ "%s: Error %d pinctrl_select_state\n", __func__, ret);
+ }
+
ret = pm_runtime_get_sync(mas->dev);
if (ret < 0) {
- dev_err(mas->dev, "Error enabling SE resources\n");
+ dev_err(mas->dev, "%s:Error enabling SE resources %d\n",
+ __func__, ret);
pm_runtime_put_noidle(mas->dev);
goto exit_prepare_transfer_hardware;
} else {
@@ -851,6 +866,9 @@
"%s:Major:%d Minor:%d step:%dos%d\n",
__func__, major, minor, step, mas->oversampling);
}
+ mas->shared_se =
+ (geni_read_reg(mas->base, GENI_IF_FIFO_DISABLE_RO) &
+ FIFO_IF_DISABLE);
}
exit_prepare_transfer_hardware:
return ret;
@@ -860,7 +878,20 @@
{
struct spi_geni_master *mas = spi_master_get_devdata(spi);
- pm_runtime_put_sync(mas->dev);
+ if (mas->shared_se) {
+ struct se_geni_rsc *rsc;
+ int ret = 0;
+
+ rsc = &mas->spi_rsc;
+ ret = pinctrl_select_state(rsc->geni_pinctrl,
+ rsc->geni_gpio_sleep);
+ if (ret)
+ GENI_SE_ERR(mas->ipc, false, NULL,
+ "%s: Error %d pinctrl_select_state\n", __func__, ret);
+ }
+
+ pm_runtime_mark_last_busy(mas->dev);
+ pm_runtime_put_autosuspend(mas->dev);
return 0;
}
@@ -1333,6 +1364,9 @@
init_completion(&geni_mas->xfer_done);
init_completion(&geni_mas->tx_cb);
init_completion(&geni_mas->rx_cb);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTO_SUSPEND_DELAY);
+ pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_enable(&pdev->dev);
ret = spi_register_master(spi);
if (ret) {
@@ -1366,7 +1400,14 @@
struct spi_master *spi = get_spi_master(dev);
struct spi_geni_master *geni_mas = spi_master_get_devdata(spi);
- ret = se_geni_resources_off(&geni_mas->spi_rsc);
+ if (geni_mas->shared_se) {
+ ret = se_geni_clks_off(&geni_mas->spi_rsc);
+ if (ret)
+ GENI_SE_ERR(geni_mas->ipc, false, NULL,
+ "%s: Error %d turning off clocks\n", __func__, ret);
+ } else {
+ ret = se_geni_resources_off(&geni_mas->spi_rsc);
+ }
return ret;
}
@@ -1376,7 +1417,14 @@
struct spi_master *spi = get_spi_master(dev);
struct spi_geni_master *geni_mas = spi_master_get_devdata(spi);
- ret = se_geni_resources_on(&geni_mas->spi_rsc);
+ if (geni_mas->shared_se) {
+ ret = se_geni_clks_on(&geni_mas->spi_rsc);
+ if (ret)
+ GENI_SE_ERR(geni_mas->ipc, false, NULL,
+ "%s: Error %d turning on clocks\n", __func__, ret);
+ } else {
+ ret = se_geni_resources_on(&geni_mas->spi_rsc);
+ }
return ret;
}
@@ -1387,9 +1435,29 @@
static int spi_geni_suspend(struct device *dev)
{
- if (!pm_runtime_status_suspended(dev))
- return -EBUSY;
- return 0;
+ int ret = 0;
+
+ if (!pm_runtime_status_suspended(dev)) {
+ struct spi_master *spi = get_spi_master(dev);
+ struct spi_geni_master *geni_mas = spi_master_get_devdata(spi);
+
+ if (list_empty(&spi->queue) && !spi->cur_msg) {
+ GENI_SE_ERR(geni_mas->ipc, true, dev,
+ "%s: Force suspend", __func__);
+ ret = spi_geni_runtime_suspend(dev);
+ if (ret) {
+ GENI_SE_ERR(geni_mas->ipc, true, dev,
+ "Force suspend Failed:%d", ret);
+ } else {
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_enable(dev);
+ }
+ } else {
+ ret = -EBUSY;
+ }
+ }
+ return ret;
}
#else
static int spi_geni_runtime_suspend(struct device *dev)
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index deb782f..a6e34f0 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -1307,12 +1307,23 @@
{
struct spi_master *master = platform_get_drvdata(pdev);
struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
+ int ret;
spi_bitbang_stop(&spi_imx->bitbang);
+ ret = clk_enable(spi_imx->clk_per);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(spi_imx->clk_ipg);
+ if (ret) {
+ clk_disable(spi_imx->clk_per);
+ return ret;
+ }
+
writel(0, spi_imx->base + MXC_CSPICTRL);
- clk_unprepare(spi_imx->clk_ipg);
- clk_unprepare(spi_imx->clk_per);
+ clk_disable_unprepare(spi_imx->clk_ipg);
+ clk_disable_unprepare(spi_imx->clk_per);
spi_imx_sdma_exit(spi_imx);
spi_master_put(master);
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index 41f1a19..83b46d4 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -561,13 +561,15 @@
task_lock(selected);
send_sig(SIGKILL, selected, 0);
- if (selected->mm)
+ if (selected->mm) {
task_set_lmk_waiting(selected);
- if (oom_reaper)
- mark_lmk_victim(selected);
+ if (!test_bit(MMF_OOM_SKIP, &selected->mm->flags) &&
+ oom_reaper) {
+ mark_lmk_victim(selected);
+ wake_oom_reaper(selected);
+ }
+ }
task_unlock(selected);
- if (oom_reaper)
- wake_oom_reaper(selected);
trace_lowmemory_kill(selected, cache_size, cache_limit, free);
lowmem_print(1, "Killing '%s' (%d) (tgid %d), adj %hd,\n"
"to free %ldkB on behalf of '%s' (%d) because\n"
diff --git a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c
index 9e88021..e8d9db4 100644
--- a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c
+++ b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c
@@ -824,14 +824,15 @@
return conn;
failed_2:
- kiblnd_destroy_conn(conn, true);
+ kiblnd_destroy_conn(conn);
+ LIBCFS_FREE(conn, sizeof(*conn));
failed_1:
LIBCFS_FREE(init_qp_attr, sizeof(*init_qp_attr));
failed_0:
return NULL;
}
-void kiblnd_destroy_conn(struct kib_conn *conn, bool free_conn)
+void kiblnd_destroy_conn(struct kib_conn *conn)
{
struct rdma_cm_id *cmid = conn->ibc_cmid;
struct kib_peer *peer = conn->ibc_peer;
@@ -894,8 +895,6 @@
rdma_destroy_id(cmid);
atomic_dec(&net->ibn_nconns);
}
-
- LIBCFS_FREE(conn, sizeof(*conn));
}
int kiblnd_close_peer_conns_locked(struct kib_peer *peer, int why)
diff --git a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.h b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.h
index 1457697..30cb2f5 100644
--- a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.h
+++ b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.h
@@ -1018,7 +1018,7 @@
struct kib_conn *kiblnd_create_conn(struct kib_peer *peer,
struct rdma_cm_id *cmid,
int state, int version);
-void kiblnd_destroy_conn(struct kib_conn *conn, bool free_conn);
+void kiblnd_destroy_conn(struct kib_conn *conn);
void kiblnd_close_conn(struct kib_conn *conn, int error);
void kiblnd_close_conn_locked(struct kib_conn *conn, int error);
diff --git a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c
index 995f2da..ea9a0c2 100644
--- a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c
+++ b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c
@@ -3323,11 +3323,13 @@
spin_unlock_irqrestore(lock, flags);
dropped_lock = 1;
- kiblnd_destroy_conn(conn, !peer);
+ kiblnd_destroy_conn(conn);
spin_lock_irqsave(lock, flags);
- if (!peer)
+ if (!peer) {
+ kfree(conn);
continue;
+ }
conn->ibc_peer = peer;
if (peer->ibp_reconnected < KIB_RECONN_HIGH_RACE)
diff --git a/drivers/staging/rtl8188eu/os_dep/ioctl_linux.c b/drivers/staging/rtl8188eu/os_dep/ioctl_linux.c
index 4de9dbc..c7bf8ab 100644
--- a/drivers/staging/rtl8188eu/os_dep/ioctl_linux.c
+++ b/drivers/staging/rtl8188eu/os_dep/ioctl_linux.c
@@ -1397,19 +1397,13 @@
if ((check_fwstate(pmlmepriv, _FW_LINKED)) ||
(check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE))) {
len = pcur_bss->Ssid.SsidLength;
-
- wrqu->essid.length = len;
-
memcpy(extra, pcur_bss->Ssid.Ssid, len);
-
- wrqu->essid.flags = 1;
} else {
- ret = -1;
- goto exit;
+ len = 0;
+ *extra = 0;
}
-
-exit:
-
+ wrqu->essid.length = len;
+ wrqu->essid.flags = 1;
return ret;
}
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index 72e926d..04d2b6e 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -1940,7 +1940,6 @@
struct iscsi_tmr_req *tmr_req;
struct iscsi_tm *hdr;
int out_of_order_cmdsn = 0, ret;
- bool sess_ref = false;
u8 function, tcm_function = TMR_UNKNOWN;
hdr = (struct iscsi_tm *) buf;
@@ -1982,18 +1981,17 @@
buf);
}
+ transport_init_se_cmd(&cmd->se_cmd, &iscsi_ops,
+ conn->sess->se_sess, 0, DMA_NONE,
+ TCM_SIMPLE_TAG, cmd->sense_buffer + 2);
+
+ target_get_sess_cmd(&cmd->se_cmd, true);
+
/*
* TASK_REASSIGN for ERL=2 / connection stays inside of
* LIO-Target $FABRIC_MOD
*/
if (function != ISCSI_TM_FUNC_TASK_REASSIGN) {
- transport_init_se_cmd(&cmd->se_cmd, &iscsi_ops,
- conn->sess->se_sess, 0, DMA_NONE,
- TCM_SIMPLE_TAG, cmd->sense_buffer + 2);
-
- target_get_sess_cmd(&cmd->se_cmd, true);
- sess_ref = true;
-
switch (function) {
case ISCSI_TM_FUNC_ABORT_TASK:
tcm_function = TMR_ABORT_TASK;
@@ -2132,12 +2130,8 @@
* For connection recovery, this is also the default action for
* TMR TASK_REASSIGN.
*/
- if (sess_ref) {
- pr_debug("Handle TMR, using sess_ref=true check\n");
- target_put_sess_cmd(&cmd->se_cmd);
- }
-
iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+ target_put_sess_cmd(&cmd->se_cmd);
return 0;
}
EXPORT_SYMBOL(iscsit_handle_task_mgt_cmd);
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
index 27dd1e1..14bb2db 100644
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -133,6 +133,15 @@
spin_unlock(&se_cmd->t_state_lock);
return false;
}
+ if (se_cmd->transport_state & CMD_T_PRE_EXECUTE) {
+ if (se_cmd->scsi_status) {
+ pr_debug("Attempted to abort io tag: %llu early failure"
+ " status: 0x%02x\n", se_cmd->tag,
+ se_cmd->scsi_status);
+ spin_unlock(&se_cmd->t_state_lock);
+ return false;
+ }
+ }
if (sess->sess_tearing_down || se_cmd->cmd_wait_set) {
pr_debug("Attempted to abort io tag: %llu already shutdown,"
" skipping\n", se_cmd->tag);
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 4c0782c..6f3eccf 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -1939,6 +1939,7 @@
}
cmd->t_state = TRANSPORT_PROCESSING;
+ cmd->transport_state &= ~CMD_T_PRE_EXECUTE;
cmd->transport_state |= CMD_T_ACTIVE|CMD_T_BUSY|CMD_T_SENT;
spin_unlock_irq(&cmd->t_state_lock);
@@ -2592,6 +2593,7 @@
ret = -ESHUTDOWN;
goto out;
}
+ se_cmd->transport_state |= CMD_T_PRE_EXECUTE;
list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list);
out:
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index f25bade..9e96f8a 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -330,7 +330,7 @@
unsigned long event, void *data)
{
struct cpufreq_policy *policy = data;
- unsigned long clipped_freq, floor_freq;
+ unsigned long clipped_freq = ULONG_MAX, floor_freq = 0;
struct cpufreq_cooling_device *cpufreq_dev;
if (event != CPUFREQ_ADJUST)
@@ -338,31 +338,30 @@
mutex_lock(&cooling_list_lock);
list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) {
- if (!cpumask_test_cpu(policy->cpu, &cpufreq_dev->allowed_cpus))
+ if (!cpumask_intersects(&cpufreq_dev->allowed_cpus,
+ policy->related_cpus))
continue;
-
- /*
- * policy->max is the maximum allowed frequency defined by user
- * and clipped_freq is the maximum that thermal constraints
- * allow.
- *
- * If clipped_freq is lower than policy->max, then we need to
- * readjust policy->max.
- *
- * But, if clipped_freq is greater than policy->max, we don't
- * need to do anything.
- *
- * Similarly, if policy minimum set by the user is less than
- * the floor_frequency, then adjust the policy->min.
- */
- clipped_freq = cpufreq_dev->clipped_freq;
- floor_freq = cpufreq_dev->floor_freq;
-
- if (policy->max > clipped_freq || policy->min < floor_freq)
- cpufreq_verify_within_limits(policy, floor_freq,
- clipped_freq);
- break;
+ if (cpufreq_dev->clipped_freq < clipped_freq)
+ clipped_freq = cpufreq_dev->clipped_freq;
+ if (cpufreq_dev->floor_freq > floor_freq)
+ floor_freq = cpufreq_dev->floor_freq;
}
+ /*
+ * policy->max is the maximum allowed frequency defined by user
+ * and clipped_freq is the maximum that thermal constraints
+ * allow.
+ *
+ * If clipped_freq is lower than policy->max, then we need to
+ * readjust policy->max.
+ *
+ * But, if clipped_freq is greater than policy->max, we don't
+ * need to do anything.
+ *
+ * Similarly, if policy minimum set by the user is less than
+ * the floor_frequency, then adjust the policy->min.
+ */
+ if (policy->max > clipped_freq || policy->min < floor_freq)
+ cpufreq_verify_within_limits(policy, floor_freq, clipped_freq);
mutex_unlock(&cooling_list_lock);
return NOTIFY_OK;
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index bdf0e6e..faf50df 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -1764,7 +1764,7 @@
{
struct n_tty_data *ldata = tty->disc_data;
- if (!old || (old->c_lflag ^ tty->termios.c_lflag) & ICANON) {
+ if (!old || (old->c_lflag ^ tty->termios.c_lflag) & (ICANON | EXTPROC)) {
bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
ldata->line_start = ldata->read_tail;
if (!L_ICANON(tty) || !read_cnt(ldata)) {
@@ -2427,7 +2427,7 @@
return put_user(tty_chars_in_buffer(tty), (int __user *) arg);
case TIOCINQ:
down_write(&tty->termios_rwsem);
- if (L_ICANON(tty))
+ if (L_ICANON(tty) && !L_EXTPROC(tty))
retval = inq_canon(ldata);
else
retval = read_cnt(ldata);
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index a70356d..521a6e4 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -2239,12 +2239,14 @@
val &= ~UCR3_AWAKEN;
writel(val, sport->port.membase + UCR3);
- val = readl(sport->port.membase + UCR1);
- if (on)
- val |= UCR1_RTSDEN;
- else
- val &= ~UCR1_RTSDEN;
- writel(val, sport->port.membase + UCR1);
+ if (sport->have_rtscts) {
+ val = readl(sport->port.membase + UCR1);
+ if (on)
+ val |= UCR1_RTSDEN;
+ else
+ val &= ~UCR1_RTSDEN;
+ writel(val, sport->port.membase + UCR1);
+ }
}
static int imx_serial_port_suspend_noirq(struct device *dev)
diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index d79b95764..185a9e2 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -894,7 +894,6 @@
msm_geni_serial_prep_dma_tx(uport);
}
- IPC_LOG_MSG(msm_port->ipc_log_misc, "%s\n", __func__);
return;
check_flow_ctrl:
geni_ios = geni_read_reg_nolog(uport->membase, SE_GENI_IOS);
@@ -963,7 +962,18 @@
SE_GENI_M_IRQ_CLEAR);
}
geni_write_reg_nolog(M_CMD_CANCEL_EN, uport, SE_GENI_M_IRQ_CLEAR);
- IPC_LOG_MSG(port->ipc_log_misc, "%s\n", __func__);
+ /*
+ * If we end up having to cancel an on-going Tx for non-console usecase
+ * then it means there was some unsent data in the Tx FIFO, consequently
+ * it means that there is a vote imbalance as we put in a vote during
+ * start_tx() that is removed only as part of a "done" ISR. To balance
+ * this out, remove the vote put in during start_tx().
+ */
+ if (!uart_console(uport)) {
+ IPC_LOG_MSG(port->ipc_log_misc, "%s:Removing vote\n", __func__);
+ msm_geni_serial_power_off(uport);
+ }
+ IPC_LOG_MSG(port->ipc_log_misc, "%s:\n", __func__);
}
static void msm_geni_serial_stop_tx(struct uart_port *uport)
@@ -2529,6 +2539,8 @@
* doing a stop_rx else we could end up flowing off the peer.
*/
mb();
+ IPC_LOG_MSG(port->ipc_log_pwr, "%s: Manual Flow ON 0x%x\n",
+ __func__, uart_manual_rfr);
}
stop_rx_sequencer(&port->uport);
if ((geni_status & M_GENI_CMD_ACTIVE))
@@ -2610,6 +2622,7 @@
mutex_unlock(&tty_port->mutex);
return -EBUSY;
}
+ IPC_LOG_MSG(port->ipc_log_pwr, "%s\n", __func__);
mutex_unlock(&tty_port->mutex);
}
return 0;
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index c220c2c..e99f1c5 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -446,7 +446,7 @@
* Callers other than flush_to_ldisc() need to exclude the kworker
* from concurrent use of the line discipline, see paste_selection().
*
- * Returns the number of bytes not processed
+ * Returns the number of bytes processed
*/
int tty_ldisc_receive_buf(struct tty_ldisc *ld, unsigned char *p,
char *f, int count)
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 734a635..8d9f9a8 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -1543,6 +1543,9 @@
"%s: %s driver does not set tty->port. This will crash the kernel later. Fix the driver!\n",
__func__, tty->driver->name);
+ retval = tty_ldisc_lock(tty, 5 * HZ);
+ if (retval)
+ goto err_release_lock;
tty->port->itty = tty;
/*
@@ -1553,6 +1556,7 @@
retval = tty_ldisc_setup(tty, tty->link);
if (retval)
goto err_release_tty;
+ tty_ldisc_unlock(tty);
/* Return the tty locked so that it cannot vanish under the caller */
return tty;
@@ -1565,9 +1569,11 @@
/* call the tty release_tty routine to clean out this slot */
err_release_tty:
- tty_unlock(tty);
+ tty_ldisc_unlock(tty);
tty_info_ratelimited(tty, "ldisc open failed (%d), clearing slot %d\n",
retval, idx);
+err_release_lock:
+ tty_unlock(tty);
release_tty(tty, idx);
return ERR_PTR(retval);
}
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index b0500a0..3a9e2a2 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -336,7 +336,7 @@
ldsem_up_write(&tty->ldisc_sem);
}
-static int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
+int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
{
int ret;
@@ -347,7 +347,7 @@
return 0;
}
-static void tty_ldisc_unlock(struct tty_struct *tty)
+void tty_ldisc_unlock(struct tty_struct *tty)
{
clear_bit(TTY_LDISC_HALTED, &tty->flags);
__tty_ldisc_unlock(tty);
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index ea20b2c..34d23cc 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -375,7 +375,7 @@
res = usb_submit_urb(acm->read_urbs[index], mem_flags);
if (res) {
- if (res != -EPERM) {
+ if (res != -EPERM && res != -ENODEV) {
dev_err(&acm->data->dev,
"urb %d failed submission with %d\n",
index, res);
@@ -1706,6 +1706,9 @@
{ USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
.driver_info = SINGLE_RX_URB, /* firmware bug */
},
+ { USB_DEVICE(0x11ca, 0x0201), /* VeriFone Mx870 Gadget Serial */
+ .driver_info = SINGLE_RX_URB,
+ },
{ USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 23b7823..0dce6ab 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -1002,7 +1002,7 @@
case USB_SSP_CAP_TYPE:
ssp_cap = (struct usb_ssp_cap_descriptor *)buffer;
ssac = (le32_to_cpu(ssp_cap->bmAttributes) &
- USB_SSP_SUBLINK_SPEED_ATTRIBS) + 1;
+ USB_SSP_SUBLINK_SPEED_ATTRIBS);
if (length >= USB_DT_USB_SSP_CAP_SIZE(ssac))
dev->bos->ssp_cap = ssp_cap;
break;
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 5001028..c05c4f8 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -57,10 +57,11 @@
/* Microsoft LifeCam-VX700 v2.0 */
{ USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME },
- /* Logitech HD Pro Webcams C920, C920-C and C930e */
+ /* Logitech HD Pro Webcams C920, C920-C, C925e and C930e */
{ USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT },
{ USB_DEVICE(0x046d, 0x0841), .driver_info = USB_QUIRK_DELAY_INIT },
{ USB_DEVICE(0x046d, 0x0843), .driver_info = USB_QUIRK_DELAY_INIT },
+ { USB_DEVICE(0x046d, 0x085b), .driver_info = USB_QUIRK_DELAY_INIT },
/* Logitech ConferenceCam CC3000e */
{ USB_DEVICE(0x046d, 0x0847), .driver_info = USB_QUIRK_DELAY_INIT },
@@ -154,6 +155,9 @@
/* Genesys Logic hub, internally used by KY-688 USB 3.1 Type-C Hub */
{ USB_DEVICE(0x05e3, 0x0612), .driver_info = USB_QUIRK_NO_LPM },
+ /* ELSA MicroLink 56K */
+ { USB_DEVICE(0x05cc, 0x2267), .driver_info = USB_QUIRK_RESET_RESUME },
+
/* Genesys Logic hub, internally used by Moshi USB to Ethernet Adapter */
{ USB_DEVICE(0x05e3, 0x0616), .driver_info = USB_QUIRK_NO_LPM },
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 465a1c6..e0321a1 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -903,6 +903,16 @@
dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
}
+ /*
+ * Enable hardware control of sending remote wakeup in HS when
+ * the device is in the L1 state.
+ */
+ if (dwc->revision >= DWC3_REVISION_290A) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
+ reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
+ dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
+ }
+
return 0;
err2:
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index e1dc7c8..b91642a 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -217,6 +217,9 @@
/* Global Debug LTSSM Register */
#define DWC3_GDBGLTSSM_LINKSTATE_MASK (0xF << 22)
+/* Global User Control 1 Register */
+#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW (1 << 24)
+
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
#define DWC3_GUSB2PHYCFG_ENBLSLPM (1 << 8)
@@ -1036,6 +1039,7 @@
#define DWC3_REVISION_260A 0x5533260a
#define DWC3_REVISION_270A 0x5533270a
#define DWC3_REVISION_280A 0x5533280a
+#define DWC3_REVISION_290A 0x5533290a
#define DWC3_REVISION_300A 0x5533300a
#define DWC3_REVISION_310A 0x5533310a
#define DWC3_REVISION_320A 0x5533320a
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index cc239b0..89bf6b7 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -1061,6 +1061,8 @@
struct dwc3_trb *trb;
int num_trbs = (dep->direction) ? (2 * (req->num_bufs) + 2)
: (req->num_bufs + 2);
+ struct scatterlist *sg;
+ struct sg_table *sgt;
dep->trb_pool = dma_zalloc_coherent(dwc->sysdev,
num_trbs * sizeof(struct dwc3_trb),
@@ -1073,6 +1075,19 @@
}
dep->num_trbs = num_trbs;
+ dma_get_sgtable(dwc->sysdev, &req->sgt_trb_xfer_ring, dep->trb_pool,
+ dep->trb_pool_dma, num_trbs * sizeof(struct dwc3_trb));
+
+ sgt = &req->sgt_trb_xfer_ring;
+ dev_dbg(dwc->dev, "%s(): trb_pool:%pK trb_pool_dma:%lx\n",
+ __func__, dep->trb_pool, (unsigned long)dep->trb_pool_dma);
+
+ for_each_sg(sgt->sgl, sg, sgt->nents, i)
+ dev_dbg(dwc->dev,
+ "%i: page_link:%lx offset:%x length:%x address:%lx\n",
+ i, sg->page_link, sg->offset, sg->length,
+ (unsigned long)sg->dma_address);
+
/* IN direction */
if (dep->direction) {
for (i = 0; i < num_trbs ; i++) {
@@ -1138,11 +1153,13 @@
}
}
- pr_debug("%s: Initialized TRB Ring for %s\n", __func__, dep->name);
+ dev_dbg(dwc->dev, "%s: Initialized TRB Ring for %s\n",
+ __func__, dep->name);
trb = &dep->trb_pool[0];
if (trb) {
for (i = 0; i < num_trbs; i++) {
- pr_debug("TRB(%d): ADDRESS:%lx bpl:%x bph:%x size:%x ctrl:%x\n",
+ dev_dbg(dwc->dev,
+ "TRB %d: ADDR:%lx bpl:%x bph:%x sz:%x ctl:%x\n",
i, (unsigned long)dwc3_trb_dma_offset(dep,
&dep->trb_pool[i]), trb->bpl, trb->bph,
trb->size, trb->ctrl);
@@ -1159,7 +1176,7 @@
* @usb_ep - pointer to usb_ep instance.
*
*/
-static void gsi_free_trbs(struct usb_ep *ep)
+static void gsi_free_trbs(struct usb_ep *ep, struct usb_gsi_request *req)
{
struct dwc3_ep *dep = to_dwc3_ep(ep);
struct dwc3 *dwc = dep->dwc;
@@ -1176,6 +1193,7 @@
dep->trb_pool = NULL;
dep->trb_pool_dma = 0;
}
+ sg_free_table(&req->sgt_trb_xfer_ring);
}
/*
* Configures GSI EPs. For GSI EPs we need to set interrupter numbers.
@@ -1365,7 +1383,8 @@
break;
case GSI_EP_OP_FREE_TRBS:
dev_dbg(mdwc->dev, "EP_OP_FREE_TRBS for %s\n", ep->name);
- gsi_free_trbs(ep);
+ request = (struct usb_gsi_request *)op_data;
+ gsi_free_trbs(ep, request);
break;
case GSI_EP_OP_CONFIG:
request = (struct usb_gsi_request *)op_data;
@@ -3331,6 +3350,8 @@
* turning on usb gdsc regulator clk is stuck off.
*/
dwc3_msm_config_gdsc(mdwc, 1);
+ clk_prepare_enable(mdwc->iface_clk);
+ clk_prepare_enable(mdwc->core_clk);
clk_prepare_enable(mdwc->cfg_ahb_clk);
/* Configure AHB2PHY for one wait state read/write*/
val = readl_relaxed(mdwc->ahb2phy_base +
@@ -3343,6 +3364,8 @@
mb();
}
clk_disable_unprepare(mdwc->cfg_ahb_clk);
+ clk_disable_unprepare(mdwc->core_clk);
+ clk_disable_unprepare(mdwc->iface_clk);
dwc3_msm_config_gdsc(mdwc, 0);
}
}
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index cbce880..986c97c 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -816,6 +816,16 @@
if (!dwc->gadget_driver)
goto out;
+ /*
+ * Workaround for SNPS STAR: 9001046257 which affects dwc3 core
+ * 3.10a or earlier. LPM Not rejected during control transfer. Device
+ * is programmed to reject LPM when SETUP packet is received and
+ * ACK LPM after completing STATUS stage.
+ */
+ if (dwc->has_lpm_erratum && dwc->revision <= DWC3_REVISION_310A)
+ dwc3_masked_write_readback(dwc->regs, DWC3_DCTL,
+ DWC3_DCTL_LPM_ERRATA_MASK, DWC3_DCTL_LPM_ERRATA(0));
+
trace_dwc3_ctrl_req(ctrl);
len = le16_to_cpu(ctrl->wLength);
@@ -990,6 +1000,11 @@
dbg_print(dep->number, "DONE", status, "STATUS");
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
+
+ if (dwc->has_lpm_erratum && dwc->revision <= DWC3_REVISION_310A)
+ dwc3_masked_write_readback(dwc->regs, DWC3_DCTL,
+ DWC3_DCTL_LPM_ERRATA_MASK,
+ DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold));
}
static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index a06f9a8..1f75b58 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -64,4 +64,28 @@
base - DWC3_GLOBALS_REGS_START + offset, value);
}
+static inline void dwc3_masked_write_readback(void __iomem *base,
+ u32 offset, const u32 mask, u32 value)
+{
+ u32 write_val, tmp;
+
+ tmp = readl_relaxed(base + offset - DWC3_GLOBALS_REGS_START);
+ tmp &= ~mask; /* retain other bits */
+ write_val = tmp | value;
+
+ writel_relaxed(write_val, base + offset - DWC3_GLOBALS_REGS_START);
+
+ /* Read back to see if value was written */
+ tmp = readl_relaxed(base + offset - DWC3_GLOBALS_REGS_START);
+
+ dwc3_trace(trace_dwc3_masked_write_readback,
+ "addr %p readback val %08x",
+ base - DWC3_GLOBALS_REGS_START + offset, tmp);
+
+ tmp &= mask; /* clear other bits */
+ if (tmp != value)
+ pr_err("%s: write: %x to %x FAILED\n",
+ __func__, value, offset);
+}
+
#endif /* __DRIVERS_USB_DWC3_IO_H */
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
index d24cefd..88f5fb8 100644
--- a/drivers/usb/dwc3/trace.h
+++ b/drivers/usb/dwc3/trace.h
@@ -47,6 +47,11 @@
TP_ARGS(vaf)
);
+DEFINE_EVENT(dwc3_log_msg, dwc3_masked_write_readback,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
DEFINE_EVENT(dwc3_log_msg, dwc3_gadget,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf)
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 598a67d..2bde573 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -10,3 +10,5 @@
libcomposite-y += composite.o functions.o configfs.o u_f.o
obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/
+
+obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o
diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c
new file mode 100644
index 0000000..a9c073b
--- /dev/null
+++ b/drivers/usb/gadget/ci13xxx_msm.c
@@ -0,0 +1,378 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This 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.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/usb/msm_hsusb_hw.h>
+#include <linux/usb/ulpi.h>
+#include <linux/gpio.h>
+
+#include "ci13xxx_udc.c"
+
+#define MSM_USB_BASE (udc->regs)
+
+#define CI13XXX_MSM_MAX_LOG2_ITC 7
+
+struct ci13xxx_udc_context {
+ int irq;
+ void __iomem *regs;
+ int wake_gpio;
+ int wake_irq;
+ bool wake_irq_state;
+};
+
+static struct ci13xxx_udc_context _udc_ctxt;
+
+static irqreturn_t msm_udc_irq(int irq, void *data)
+{
+ return udc_irq();
+}
+
+static void ci13xxx_msm_suspend(void)
+{
+ struct device *dev = _udc->gadget.dev.parent;
+ dev_dbg(dev, "ci13xxx_msm_suspend\n");
+
+ if (_udc_ctxt.wake_irq && !_udc_ctxt.wake_irq_state) {
+ enable_irq_wake(_udc_ctxt.wake_irq);
+ enable_irq(_udc_ctxt.wake_irq);
+ _udc_ctxt.wake_irq_state = true;
+ }
+}
+
+static void ci13xxx_msm_resume(void)
+{
+ struct device *dev = _udc->gadget.dev.parent;
+ dev_dbg(dev, "ci13xxx_msm_resume\n");
+
+ if (_udc_ctxt.wake_irq && _udc_ctxt.wake_irq_state) {
+ disable_irq_wake(_udc_ctxt.wake_irq);
+ disable_irq_nosync(_udc_ctxt.wake_irq);
+ _udc_ctxt.wake_irq_state = false;
+ }
+}
+
+static void ci13xxx_msm_disconnect(void)
+{
+ struct ci13xxx *udc = _udc;
+ struct usb_phy *phy = udc->transceiver;
+
+ if (phy && (phy->flags & ENABLE_DP_MANUAL_PULLUP))
+ usb_phy_io_write(phy,
+ ULPI_MISC_A_VBUSVLDEXT |
+ ULPI_MISC_A_VBUSVLDEXTSEL,
+ ULPI_CLR(ULPI_MISC_A));
+}
+
+/* Link power management will reduce power consumption by
+ * short time HW suspend/resume.
+ */
+static void ci13xxx_msm_set_l1(struct ci13xxx *udc)
+{
+ int temp;
+ struct device *dev = udc->gadget.dev.parent;
+
+ dev_dbg(dev, "Enable link power management\n");
+
+ /* Enable remote wakeup and L1 for IN EPs */
+ writel_relaxed(0xffff0000, USB_L1_EP_CTRL);
+
+ temp = readl_relaxed(USB_L1_CONFIG);
+ temp |= L1_CONFIG_LPM_EN | L1_CONFIG_REMOTE_WAKEUP |
+ L1_CONFIG_GATE_SYS_CLK | L1_CONFIG_PHY_LPM |
+ L1_CONFIG_PLL;
+ writel_relaxed(temp, USB_L1_CONFIG);
+}
+
+static void ci13xxx_msm_connect(void)
+{
+ struct ci13xxx *udc = _udc;
+ struct usb_phy *phy = udc->transceiver;
+
+ if (phy && (phy->flags & ENABLE_DP_MANUAL_PULLUP)) {
+ int temp;
+
+ usb_phy_io_write(phy,
+ ULPI_MISC_A_VBUSVLDEXT |
+ ULPI_MISC_A_VBUSVLDEXTSEL,
+ ULPI_SET(ULPI_MISC_A));
+
+ temp = readl_relaxed(USB_GENCONFIG2);
+ temp |= GENCFG2_SESS_VLD_CTRL_EN;
+ writel_relaxed(temp, USB_GENCONFIG2);
+
+ temp = readl_relaxed(USB_USBCMD);
+ temp |= USBCMD_SESS_VLD_CTRL;
+ writel_relaxed(temp, USB_USBCMD);
+
+ /*
+ * Add memory barrier as it is must to complete
+ * above USB PHY and Link register writes before
+ * moving ahead with USB peripheral mode enumeration,
+ * otherwise USB peripheral mode may not work.
+ */
+ mb();
+ }
+}
+
+static void ci13xxx_msm_reset(void)
+{
+ struct ci13xxx *udc = _udc;
+ struct usb_phy *phy = udc->transceiver;
+ struct device *dev = udc->gadget.dev.parent;
+
+ writel_relaxed(0, USB_AHBBURST);
+ writel_relaxed(0x08, USB_AHBMODE);
+
+ if (udc->gadget.l1_supported)
+ ci13xxx_msm_set_l1(udc);
+
+ if (phy && (phy->flags & ENABLE_SECONDARY_PHY)) {
+ int temp;
+
+ dev_dbg(dev, "using secondary hsphy\n");
+ temp = readl_relaxed(USB_PHY_CTRL2);
+ temp |= (1<<16);
+ writel_relaxed(temp, USB_PHY_CTRL2);
+
+ /*
+ * Add memory barrier to make sure above LINK writes are
+ * complete before moving ahead with USB peripheral mode
+ * enumeration.
+ */
+ mb();
+ }
+}
+
+static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event)
+{
+ struct device *dev = udc->gadget.dev.parent;
+
+ switch (event) {
+ case CI13XXX_CONTROLLER_RESET_EVENT:
+ dev_info(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n");
+ ci13xxx_msm_reset();
+ break;
+ case CI13XXX_CONTROLLER_DISCONNECT_EVENT:
+ dev_info(dev, "CI13XXX_CONTROLLER_DISCONNECT_EVENT received\n");
+ ci13xxx_msm_disconnect();
+ ci13xxx_msm_resume();
+ break;
+ case CI13XXX_CONTROLLER_CONNECT_EVENT:
+ dev_info(dev, "CI13XXX_CONTROLLER_CONNECT_EVENT received\n");
+ ci13xxx_msm_connect();
+ break;
+ case CI13XXX_CONTROLLER_SUSPEND_EVENT:
+ dev_info(dev, "CI13XXX_CONTROLLER_SUSPEND_EVENT received\n");
+ ci13xxx_msm_suspend();
+ break;
+ case CI13XXX_CONTROLLER_RESUME_EVENT:
+ dev_info(dev, "CI13XXX_CONTROLLER_RESUME_EVENT received\n");
+ ci13xxx_msm_resume();
+ break;
+
+ default:
+ dev_dbg(dev, "unknown ci13xxx_udc event\n");
+ break;
+ }
+}
+
+static irqreturn_t ci13xxx_msm_resume_irq(int irq, void *data)
+{
+ struct ci13xxx *udc = _udc;
+
+ if (udc->transceiver && udc->vbus_active && udc->suspended)
+ usb_phy_set_suspend(udc->transceiver, 0);
+ else if (!udc->suspended)
+ ci13xxx_msm_resume();
+
+ return IRQ_HANDLED;
+}
+
+static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = {
+ .name = "ci13xxx_msm",
+ .flags = CI13XXX_REGS_SHARED |
+ CI13XXX_REQUIRE_TRANSCEIVER |
+ CI13XXX_PULLUP_ON_VBUS |
+ CI13XXX_ZERO_ITC |
+ CI13XXX_DISABLE_STREAMING |
+ CI13XXX_IS_OTG,
+ .nz_itc = 0,
+ .notify_event = ci13xxx_msm_notify_event,
+};
+
+static int ci13xxx_msm_install_wake_gpio(struct platform_device *pdev,
+ struct resource *res)
+{
+ int wake_irq;
+ int ret;
+
+ dev_dbg(&pdev->dev, "ci13xxx_msm_install_wake_gpio\n");
+
+ _udc_ctxt.wake_gpio = res->start;
+ gpio_request(_udc_ctxt.wake_gpio, "USB_RESUME");
+ gpio_direction_input(_udc_ctxt.wake_gpio);
+ wake_irq = gpio_to_irq(_udc_ctxt.wake_gpio);
+ if (wake_irq < 0) {
+ dev_err(&pdev->dev, "could not register USB_RESUME GPIO.\n");
+ return -ENXIO;
+ }
+
+ dev_dbg(&pdev->dev, "_udc_ctxt.gpio_irq = %d and irq = %d\n",
+ _udc_ctxt.wake_gpio, wake_irq);
+ ret = request_irq(wake_irq, ci13xxx_msm_resume_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT, "usb resume", NULL);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not register USB_RESUME IRQ.\n");
+ goto gpio_free;
+ }
+ disable_irq(wake_irq);
+ _udc_ctxt.wake_irq = wake_irq;
+
+ return 0;
+
+gpio_free:
+ gpio_free(_udc_ctxt.wake_gpio);
+ _udc_ctxt.wake_gpio = 0;
+ return ret;
+}
+
+static void ci13xxx_msm_uninstall_wake_gpio(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "ci13xxx_msm_uninstall_wake_gpio\n");
+
+ if (_udc_ctxt.wake_gpio) {
+ gpio_free(_udc_ctxt.wake_gpio);
+ _udc_ctxt.wake_gpio = 0;
+ }
+}
+
+static int ci13xxx_msm_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret;
+ struct ci13xxx_platform_data *pdata = pdev->dev.platform_data;
+ bool is_l1_supported = false;
+
+ dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n");
+
+ if (pdata) {
+ /* Acceptable values for nz_itc are: 0,1,2,4,8,16,32,64 */
+ if (pdata->log2_itc > CI13XXX_MSM_MAX_LOG2_ITC ||
+ pdata->log2_itc <= 0)
+ ci13xxx_msm_udc_driver.nz_itc = 0;
+ else
+ ci13xxx_msm_udc_driver.nz_itc =
+ 1 << (pdata->log2_itc-1);
+
+ is_l1_supported = pdata->l1_supported;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get platform resource mem\n");
+ return -ENXIO;
+ }
+
+ _udc_ctxt.regs = ioremap(res->start, resource_size(res));
+ if (!_udc_ctxt.regs) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ return -ENOMEM;
+ }
+
+ ret = udc_probe(&ci13xxx_msm_udc_driver, &pdev->dev, _udc_ctxt.regs);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "udc_probe failed\n");
+ goto iounmap;
+ }
+
+ _udc->gadget.l1_supported = is_l1_supported;
+
+ _udc_ctxt.irq = platform_get_irq(pdev, 0);
+ if (_udc_ctxt.irq < 0) {
+ dev_err(&pdev->dev, "IRQ not found\n");
+ ret = -ENXIO;
+ goto udc_remove;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IO, "USB_RESUME");
+ if (res) {
+ ret = ci13xxx_msm_install_wake_gpio(pdev, res);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "gpio irq install failed\n");
+ goto udc_remove;
+ }
+ }
+
+ ret = request_irq(_udc_ctxt.irq, msm_udc_irq, IRQF_SHARED, pdev->name,
+ pdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "request_irq failed\n");
+ goto gpio_uninstall;
+ }
+
+ pm_runtime_no_callbacks(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ return 0;
+
+gpio_uninstall:
+ ci13xxx_msm_uninstall_wake_gpio(pdev);
+udc_remove:
+ udc_remove();
+iounmap:
+ iounmap(_udc_ctxt.regs);
+
+ return ret;
+}
+
+int ci13xxx_msm_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+ free_irq(_udc_ctxt.irq, pdev);
+ ci13xxx_msm_uninstall_wake_gpio(pdev);
+ udc_remove();
+ iounmap(_udc_ctxt.regs);
+ return 0;
+}
+
+void msm_hw_bam_disable(bool bam_disable)
+{
+ u32 val;
+ struct ci13xxx *udc = _udc;
+
+ if (bam_disable)
+ val = readl_relaxed(USB_GENCONFIG) | GENCONFIG_BAM_DISABLE;
+ else
+ val = readl_relaxed(USB_GENCONFIG) & ~GENCONFIG_BAM_DISABLE;
+
+ writel_relaxed(val, USB_GENCONFIG);
+}
+
+static struct platform_driver ci13xxx_msm_driver = {
+ .probe = ci13xxx_msm_probe,
+ .driver = {
+ .name = "msm_hsusb",
+ },
+ .remove = ci13xxx_msm_remove,
+};
+MODULE_ALIAS("platform:msm_hsusb");
+
+static int __init ci13xxx_msm_init(void)
+{
+ return platform_driver_register(&ci13xxx_msm_driver);
+}
+module_init(ci13xxx_msm_init);
+
+static void __exit ci13xxx_msm_exit(void)
+{
+ platform_driver_unregister(&ci13xxx_msm_driver);
+}
+module_exit(ci13xxx_msm_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
new file mode 100644
index 0000000..b8389e2
--- /dev/null
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -0,0 +1,3876 @@
+/*
+ * ci13xxx_udc.c - MIPS USB IP core family device controller
+ *
+ * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
+ *
+ * Author: David Lopo
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * Description: MIPS USB IP core family device controller
+ * Currently it only supports IP part number CI13412
+ *
+ * This driver is composed of several blocks:
+ * - HW: hardware interface
+ * - DBG: debug facilities (optional)
+ * - UTIL: utilities
+ * - ISR: interrupts handling
+ * - ENDPT: endpoint operations (Gadget API)
+ * - GADGET: gadget operations (Gadget API)
+ * - BUS: bus glue code, bus abstraction layer
+ *
+ * Compile Options
+ * - CONFIG_USB_GADGET_DEBUG_FILES: enable debug facilities
+ * - STALL_IN: non-empty bulk-in pipes cannot be halted
+ * if defined mass storage compliance succeeds but with warnings
+ * => case 4: Hi > Dn
+ * => case 5: Hi > Di
+ * => case 8: Hi <> Do
+ * if undefined usbtest 13 fails
+ * - TRACE: enable function tracing (depends on DEBUG)
+ *
+ * Main Features
+ * - Chapter 9 & Mass Storage Compliance with Gadget File Storage
+ * - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined)
+ * - Normal & LPM support
+ *
+ * USBTEST Report
+ * - OK: 0-12, 13 (STALL_IN defined) & 14
+ * - Not Supported: 15 & 16 (ISO)
+ *
+ * TODO List
+ * - OTG
+ * - Isochronous & Interrupt Traffic
+ * - Handle requests which spawns into several TDs
+ * - GET_STATUS(device) - always reports 0
+ * - Gadget API (majority of optional features)
+ */
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/ratelimit.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/msm_hsusb.h>
+#include <linux/tracepoint.h>
+#include <mach/usb_trace.h>
+#include "ci13xxx_udc.h"
+
+/* Turns on streaming. overrides CI13XXX_DISABLE_STREAMING */
+static unsigned int streaming;
+module_param(streaming, uint, S_IRUGO | S_IWUSR);
+
+/******************************************************************************
+ * DEFINE
+ *****************************************************************************/
+
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+#define USB_MAX_TIMEOUT 25 /* 25msec timeout */
+#define EP_PRIME_CHECK_DELAY (jiffies + msecs_to_jiffies(1000))
+#define MAX_PRIME_CHECK_RETRY 3 /*Wait for 3sec for EP prime failure */
+
+/* ctrl register bank access */
+static DEFINE_SPINLOCK(udc_lock);
+
+/* control endpoint description */
+static const struct usb_endpoint_descriptor
+ctrl_endpt_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+ .wMaxPacketSize = cpu_to_le16(CTRL_PAYLOAD_MAX),
+};
+
+static const struct usb_endpoint_descriptor
+ctrl_endpt_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+ .wMaxPacketSize = cpu_to_le16(CTRL_PAYLOAD_MAX),
+};
+
+/* UDC descriptor */
+static struct ci13xxx *_udc;
+
+/* Interrupt statistics */
+#define ISR_MASK 0x1F
+static struct {
+ u32 test;
+ u32 ui;
+ u32 uei;
+ u32 pci;
+ u32 uri;
+ u32 sli;
+ u32 none;
+ struct {
+ u32 cnt;
+ u32 buf[ISR_MASK+1];
+ u32 idx;
+ } hndl;
+} isr_statistics;
+
+/**
+ * ffs_nr: find first (least significant) bit set
+ * @x: the word to search
+ *
+ * This function returns bit number (instead of position)
+ */
+static int ffs_nr(u32 x)
+{
+ int n = ffs(x);
+
+ return n ? n-1 : 32;
+}
+
+struct ci13xxx_ebi_err_entry {
+ u32 *usb_req_buf;
+ u32 usb_req_length;
+ u32 ep_info;
+ struct ci13xxx_ebi_err_entry *next;
+};
+
+struct ci13xxx_ebi_err_data {
+ u32 ebi_err_addr;
+ u32 apkt0;
+ u32 apkt1;
+ struct ci13xxx_ebi_err_entry *ebi_err_entry;
+};
+static struct ci13xxx_ebi_err_data *ebi_err_data;
+
+/******************************************************************************
+ * HW block
+ *****************************************************************************/
+/* register bank descriptor */
+static struct {
+ unsigned lpm; /* is LPM? */
+ void __iomem *abs; /* bus map offset */
+ void __iomem *cap; /* bus map offset + CAP offset + CAP data */
+ size_t size; /* bank size */
+} hw_bank;
+
+/* MSM specific */
+#define ABS_AHBBURST (0x0090UL)
+#define ABS_AHBMODE (0x0098UL)
+/* UDC register map */
+#define ABS_CAPLENGTH (0x100UL)
+#define ABS_HCCPARAMS (0x108UL)
+#define ABS_DCCPARAMS (0x124UL)
+#define ABS_TESTMODE (hw_bank.lpm ? 0x0FCUL : 0x138UL)
+/* offset to CAPLENTGH (addr + data) */
+#define CAP_USBCMD (0x000UL)
+#define CAP_USBSTS (0x004UL)
+#define CAP_USBINTR (0x008UL)
+#define CAP_DEVICEADDR (0x014UL)
+#define CAP_ENDPTLISTADDR (0x018UL)
+#define CAP_PORTSC (0x044UL)
+#define CAP_DEVLC (0x084UL)
+#define CAP_ENDPTPIPEID (0x0BCUL)
+#define CAP_USBMODE (hw_bank.lpm ? 0x0C8UL : 0x068UL)
+#define CAP_ENDPTSETUPSTAT (hw_bank.lpm ? 0x0D8UL : 0x06CUL)
+#define CAP_ENDPTPRIME (hw_bank.lpm ? 0x0DCUL : 0x070UL)
+#define CAP_ENDPTFLUSH (hw_bank.lpm ? 0x0E0UL : 0x074UL)
+#define CAP_ENDPTSTAT (hw_bank.lpm ? 0x0E4UL : 0x078UL)
+#define CAP_ENDPTCOMPLETE (hw_bank.lpm ? 0x0E8UL : 0x07CUL)
+#define CAP_ENDPTCTRL (hw_bank.lpm ? 0x0ECUL : 0x080UL)
+#define CAP_LAST (hw_bank.lpm ? 0x12CUL : 0x0C0UL)
+
+#define REMOTE_WAKEUP_DELAY msecs_to_jiffies(200)
+
+/* maximum number of enpoints: valid only after hw_device_reset() */
+static unsigned hw_ep_max;
+static void dbg_usb_op_fail(u8 addr, const char *name,
+ const struct ci13xxx_ep *mep);
+/**
+ * hw_ep_bit: calculates the bit number
+ * @num: endpoint number
+ * @dir: endpoint direction
+ *
+ * This function returns bit number
+ */
+static inline int hw_ep_bit(int num, int dir)
+{
+ return num + (dir ? 16 : 0);
+}
+
+static int ep_to_bit(int n)
+{
+ int fill = 16 - hw_ep_max / 2;
+
+ if (n >= hw_ep_max / 2)
+ n += fill;
+
+ return n;
+}
+
+/**
+ * hw_aread: reads from register bitfield
+ * @addr: address relative to bus map
+ * @mask: bitfield mask
+ *
+ * This function returns register bitfield data
+ */
+static u32 hw_aread(u32 addr, u32 mask)
+{
+ return ioread32(addr + hw_bank.abs) & mask;
+}
+
+/**
+ * hw_awrite: writes to register bitfield
+ * @addr: address relative to bus map
+ * @mask: bitfield mask
+ * @data: new data
+ */
+static void hw_awrite(u32 addr, u32 mask, u32 data)
+{
+ iowrite32(hw_aread(addr, ~mask) | (data & mask),
+ addr + hw_bank.abs);
+}
+
+/**
+ * hw_cread: reads from register bitfield
+ * @addr: address relative to CAP offset plus content
+ * @mask: bitfield mask
+ *
+ * This function returns register bitfield data
+ */
+static u32 hw_cread(u32 addr, u32 mask)
+{
+ return ioread32(addr + hw_bank.cap) & mask;
+}
+
+/**
+ * hw_cwrite: writes to register bitfield
+ * @addr: address relative to CAP offset plus content
+ * @mask: bitfield mask
+ * @data: new data
+ */
+static void hw_cwrite(u32 addr, u32 mask, u32 data)
+{
+ iowrite32(hw_cread(addr, ~mask) | (data & mask),
+ addr + hw_bank.cap);
+}
+
+/**
+ * hw_ctest_and_clear: tests & clears register bitfield
+ * @addr: address relative to CAP offset plus content
+ * @mask: bitfield mask
+ *
+ * This function returns register bitfield data
+ */
+static u32 hw_ctest_and_clear(u32 addr, u32 mask)
+{
+ u32 reg = hw_cread(addr, mask);
+
+ iowrite32(reg, addr + hw_bank.cap);
+ return reg;
+}
+
+/**
+ * hw_ctest_and_write: tests & writes register bitfield
+ * @addr: address relative to CAP offset plus content
+ * @mask: bitfield mask
+ * @data: new data
+ *
+ * This function returns register bitfield data
+ */
+static u32 hw_ctest_and_write(u32 addr, u32 mask, u32 data)
+{
+ u32 reg = hw_cread(addr, ~0);
+
+ iowrite32((reg & ~mask) | (data & mask), addr + hw_bank.cap);
+ return (reg & mask) >> ffs_nr(mask);
+}
+
+static int hw_device_init(void __iomem *base)
+{
+ u32 reg;
+
+ /* bank is a module variable */
+ hw_bank.abs = base;
+
+ hw_bank.cap = hw_bank.abs;
+ hw_bank.cap += ABS_CAPLENGTH;
+ hw_bank.cap += ioread8(hw_bank.cap);
+
+ reg = hw_aread(ABS_HCCPARAMS, HCCPARAMS_LEN) >> ffs_nr(HCCPARAMS_LEN);
+ hw_bank.lpm = reg;
+ hw_bank.size = hw_bank.cap - hw_bank.abs;
+ hw_bank.size += CAP_LAST;
+ hw_bank.size /= sizeof(u32);
+
+ reg = hw_aread(ABS_DCCPARAMS, DCCPARAMS_DEN) >> ffs_nr(DCCPARAMS_DEN);
+ hw_ep_max = reg * 2; /* cache hw ENDPT_MAX */
+
+ if (hw_ep_max == 0 || hw_ep_max > ENDPT_MAX)
+ return -ENODEV;
+
+ /* setup lock mode ? */
+
+ /* ENDPTSETUPSTAT is '0' by default */
+
+ /* HCSPARAMS.bf.ppc SHOULD BE zero for device */
+
+ return 0;
+}
+/**
+ * hw_device_reset: resets chip (execute without interruption)
+ * @base: register base address
+ *
+ * This function returns an error code
+ */
+static int hw_device_reset(struct ci13xxx *udc)
+{
+ int delay_count = 25; /* 250 usec */
+
+ /* should flush & stop before reset */
+ hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0);
+ hw_cwrite(CAP_USBCMD, USBCMD_RS, 0);
+
+ hw_cwrite(CAP_USBCMD, USBCMD_RST, USBCMD_RST);
+ while (delay_count-- && hw_cread(CAP_USBCMD, USBCMD_RST))
+ udelay(10);
+ if (delay_count < 0)
+ pr_err("USB controller reset failed\n");
+
+ if (udc->udc_driver->notify_event)
+ udc->udc_driver->notify_event(udc,
+ CI13XXX_CONTROLLER_RESET_EVENT);
+
+ /* USBMODE should be configured step by step */
+ hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
+ hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE);
+ hw_cwrite(CAP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); /* HW >= 2.3 */
+
+ /*
+ * ITC (Interrupt Threshold Control) field is to set the maximum
+ * rate at which the device controller will issue interrupts.
+ * The maximum interrupt interval measured in micro frames.
+ * Valid values are 0, 1, 2, 4, 8, 16, 32, 64. The default value is
+ * 8 micro frames. If CPU can handle interrupts at faster rate, ITC
+ * can be set to lesser value to gain performance.
+ */
+ if (udc->udc_driver->nz_itc)
+ hw_cwrite(CAP_USBCMD, USBCMD_ITC_MASK,
+ USBCMD_ITC(udc->udc_driver->nz_itc));
+ else if (udc->udc_driver->flags & CI13XXX_ZERO_ITC)
+ hw_cwrite(CAP_USBCMD, USBCMD_ITC_MASK, USBCMD_ITC(0));
+
+ if (hw_cread(CAP_USBMODE, USBMODE_CM) != USBMODE_CM_DEVICE) {
+ pr_err("cannot enter in device mode");
+ pr_err("lpm = %i", hw_bank.lpm);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/**
+ * hw_device_state: enables/disables interrupts & starts/stops device (execute
+ * without interruption)
+ * @dma: 0 => disable, !0 => enable and set dma engine
+ *
+ * This function returns an error code
+ */
+static int hw_device_state(u32 dma)
+{
+ struct ci13xxx *udc = _udc;
+
+ if (dma) {
+ if (streaming || !(udc->udc_driver->flags &
+ CI13XXX_DISABLE_STREAMING))
+ hw_cwrite(CAP_USBMODE, USBMODE_SDIS, 0);
+ else
+ hw_cwrite(CAP_USBMODE, USBMODE_SDIS, USBMODE_SDIS);
+
+ hw_cwrite(CAP_ENDPTLISTADDR, ~0, dma);
+
+ if (udc->udc_driver->notify_event)
+ udc->udc_driver->notify_event(udc,
+ CI13XXX_CONTROLLER_CONNECT_EVENT);
+
+ /* interrupt, error, port change, reset, sleep/suspend */
+ hw_cwrite(CAP_USBINTR, ~0,
+ USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
+ hw_cwrite(CAP_USBCMD, USBCMD_RS, USBCMD_RS);
+ } else {
+ hw_cwrite(CAP_USBCMD, USBCMD_RS, 0);
+ hw_cwrite(CAP_USBINTR, ~0, 0);
+ }
+ return 0;
+}
+
+static void debug_ept_flush_info(int ep_num, int dir)
+{
+ struct ci13xxx *udc = _udc;
+ struct ci13xxx_ep *mep;
+
+ if (dir)
+ mep = &udc->ci13xxx_ep[ep_num + hw_ep_max/2];
+ else
+ mep = &udc->ci13xxx_ep[ep_num];
+
+ pr_err_ratelimited("USB Registers\n");
+ pr_err_ratelimited("USBCMD:%x\n", hw_cread(CAP_USBCMD, ~0));
+ pr_err_ratelimited("USBSTS:%x\n", hw_cread(CAP_USBSTS, ~0));
+ pr_err_ratelimited("ENDPTLISTADDR:%x\n",
+ hw_cread(CAP_ENDPTLISTADDR, ~0));
+ pr_err_ratelimited("PORTSC:%x\n", hw_cread(CAP_PORTSC, ~0));
+ pr_err_ratelimited("USBMODE:%x\n", hw_cread(CAP_USBMODE, ~0));
+ pr_err_ratelimited("ENDPTSTAT:%x\n", hw_cread(CAP_ENDPTSTAT, ~0));
+
+ dbg_usb_op_fail(0xFF, "FLUSHF", mep);
+}
+/**
+ * hw_ep_flush: flush endpoint fifo (execute without interruption)
+ * @num: endpoint number
+ * @dir: endpoint direction
+ *
+ * This function returns an error code
+ */
+static int hw_ep_flush(int num, int dir)
+{
+ ktime_t start, diff;
+ int n = hw_ep_bit(num, dir);
+ struct ci13xxx_ep *mEp = &_udc->ci13xxx_ep[n];
+
+ /* Flush ep0 even when queue is empty */
+ if (_udc->skip_flush || (num && list_empty(&mEp->qh.queue)))
+ return 0;
+
+ start = ktime_get();
+ do {
+ /* flush any pending transfer */
+ hw_cwrite(CAP_ENDPTFLUSH, BIT(n), BIT(n));
+ while (hw_cread(CAP_ENDPTFLUSH, BIT(n))) {
+ cpu_relax();
+ diff = ktime_sub(ktime_get(), start);
+ if (ktime_to_ms(diff) > USB_MAX_TIMEOUT) {
+ printk_ratelimited(KERN_ERR
+ "%s: Failed to flush ep#%d %s\n",
+ __func__, num,
+ dir ? "IN" : "OUT");
+ debug_ept_flush_info(num, dir);
+ _udc->skip_flush = true;
+ return 0;
+ }
+ }
+ } while (hw_cread(CAP_ENDPTSTAT, BIT(n)));
+
+ return 0;
+}
+
+/**
+ * hw_ep_disable: disables endpoint (execute without interruption)
+ * @num: endpoint number
+ * @dir: endpoint direction
+ *
+ * This function returns an error code
+ */
+static int hw_ep_disable(int num, int dir)
+{
+ hw_cwrite(CAP_ENDPTCTRL + num * sizeof(u32),
+ dir ? ENDPTCTRL_TXE : ENDPTCTRL_RXE, 0);
+ return 0;
+}
+
+/**
+ * hw_ep_enable: enables endpoint (execute without interruption)
+ * @num: endpoint number
+ * @dir: endpoint direction
+ * @type: endpoint type
+ *
+ * This function returns an error code
+ */
+static int hw_ep_enable(int num, int dir, int type)
+{
+ u32 mask, data;
+
+ if (dir) {
+ mask = ENDPTCTRL_TXT; /* type */
+ data = type << ffs_nr(mask);
+
+ mask |= ENDPTCTRL_TXS; /* unstall */
+ mask |= ENDPTCTRL_TXR; /* reset data toggle */
+ data |= ENDPTCTRL_TXR;
+ mask |= ENDPTCTRL_TXE; /* enable */
+ data |= ENDPTCTRL_TXE;
+ } else {
+ mask = ENDPTCTRL_RXT; /* type */
+ data = type << ffs_nr(mask);
+
+ mask |= ENDPTCTRL_RXS; /* unstall */
+ mask |= ENDPTCTRL_RXR; /* reset data toggle */
+ data |= ENDPTCTRL_RXR;
+ mask |= ENDPTCTRL_RXE; /* enable */
+ data |= ENDPTCTRL_RXE;
+ }
+ hw_cwrite(CAP_ENDPTCTRL + num * sizeof(u32), mask, data);
+
+ /* make sure endpoint is enabled before returning */
+ mb();
+
+ return 0;
+}
+
+/**
+ * hw_ep_get_halt: return endpoint halt status
+ * @num: endpoint number
+ * @dir: endpoint direction
+ *
+ * This function returns 1 if endpoint halted
+ */
+static int hw_ep_get_halt(int num, int dir)
+{
+ u32 mask = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS;
+
+ return hw_cread(CAP_ENDPTCTRL + num * sizeof(u32), mask) ? 1 : 0;
+}
+
+/**
+ * hw_test_and_clear_setup_status: test & clear setup status (execute without
+ * interruption)
+ * @n: endpoint number
+ *
+ * This function returns setup status
+ */
+static int hw_test_and_clear_setup_status(int n)
+{
+ n = ep_to_bit(n);
+ return hw_ctest_and_clear(CAP_ENDPTSETUPSTAT, BIT(n));
+}
+
+/**
+ * hw_ep_prime: primes endpoint (execute without interruption)
+ * @num: endpoint number
+ * @dir: endpoint direction
+ * @is_ctrl: true if control endpoint
+ *
+ * This function returns an error code
+ */
+static int hw_ep_prime(int num, int dir, int is_ctrl)
+{
+ int n = hw_ep_bit(num, dir);
+
+ if (is_ctrl && dir == RX && hw_cread(CAP_ENDPTSETUPSTAT, BIT(num)))
+ return -EAGAIN;
+
+ hw_cwrite(CAP_ENDPTPRIME, BIT(n), BIT(n));
+
+ if (is_ctrl && dir == RX && hw_cread(CAP_ENDPTSETUPSTAT, BIT(num)))
+ return -EAGAIN;
+
+ /* status shoult be tested according with manual but it doesn't work */
+ return 0;
+}
+
+/**
+ * hw_ep_set_halt: configures ep halt & resets data toggle after clear (execute
+ * without interruption)
+ * @num: endpoint number
+ * @dir: endpoint direction
+ * @value: true => stall, false => unstall
+ *
+ * This function returns an error code
+ */
+static int hw_ep_set_halt(int num, int dir, int value)
+{
+ u32 addr, mask_xs, mask_xr;
+
+ if (value != 0 && value != 1)
+ return -EINVAL;
+
+ do {
+ if (hw_cread(CAP_ENDPTSETUPSTAT, BIT(num)))
+ return 0;
+
+ addr = CAP_ENDPTCTRL + num * sizeof(u32);
+ mask_xs = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS;
+ mask_xr = dir ? ENDPTCTRL_TXR : ENDPTCTRL_RXR;
+
+ /* data toggle - reserved for EP0 but it's in ESS */
+ hw_cwrite(addr, mask_xs|mask_xr, value ? mask_xs : mask_xr);
+
+ } while (value != hw_ep_get_halt(num, dir));
+
+ return 0;
+}
+
+/**
+ * hw_intr_clear: disables interrupt & clears interrupt status (execute without
+ * interruption)
+ * @n: interrupt bit
+ *
+ * This function returns an error code
+ */
+static int hw_intr_clear(int n)
+{
+ if (n >= REG_BITS)
+ return -EINVAL;
+
+ hw_cwrite(CAP_USBINTR, BIT(n), 0);
+ hw_cwrite(CAP_USBSTS, BIT(n), BIT(n));
+ return 0;
+}
+
+/**
+ * hw_intr_force: enables interrupt & forces interrupt status (execute without
+ * interruption)
+ * @n: interrupt bit
+ *
+ * This function returns an error code
+ */
+static int hw_intr_force(int n)
+{
+ if (n >= REG_BITS)
+ return -EINVAL;
+
+ hw_awrite(ABS_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE);
+ hw_cwrite(CAP_USBINTR, BIT(n), BIT(n));
+ hw_cwrite(CAP_USBSTS, BIT(n), BIT(n));
+ hw_awrite(ABS_TESTMODE, TESTMODE_FORCE, 0);
+ return 0;
+}
+
+/**
+ * hw_is_port_high_speed: test if port is high speed
+ *
+ * This function returns true if high speed port
+ */
+static int hw_port_is_high_speed(void)
+{
+ return hw_bank.lpm ? hw_cread(CAP_DEVLC, DEVLC_PSPD) :
+ hw_cread(CAP_PORTSC, PORTSC_HSP);
+}
+
+/**
+ * hw_port_test_get: reads port test mode value
+ *
+ * This function returns port test mode value
+ */
+static u8 hw_port_test_get(void)
+{
+ return hw_cread(CAP_PORTSC, PORTSC_PTC) >> ffs_nr(PORTSC_PTC);
+}
+
+/**
+ * hw_port_test_set: writes port test mode (execute without interruption)
+ * @mode: new value
+ *
+ * This function returns an error code
+ */
+static int hw_port_test_set(u8 mode)
+{
+ const u8 TEST_MODE_MAX = 7;
+
+ if (mode > TEST_MODE_MAX)
+ return -EINVAL;
+
+ hw_cwrite(CAP_PORTSC, PORTSC_PTC, mode << ffs_nr(PORTSC_PTC));
+ return 0;
+}
+
+/**
+ * hw_read_intr_enable: returns interrupt enable register
+ *
+ * This function returns register data
+ */
+static u32 hw_read_intr_enable(void)
+{
+ return hw_cread(CAP_USBINTR, ~0);
+}
+
+/**
+ * hw_read_intr_status: returns interrupt status register
+ *
+ * This function returns register data
+ */
+static u32 hw_read_intr_status(void)
+{
+ return hw_cread(CAP_USBSTS, ~0);
+}
+
+/**
+ * hw_register_read: reads all device registers (execute without interruption)
+ * @buf: destination buffer
+ * @size: buffer size
+ *
+ * This function returns number of registers read
+ */
+static size_t hw_register_read(u32 *buf, size_t size)
+{
+ unsigned i;
+
+ if (size > hw_bank.size)
+ size = hw_bank.size;
+
+ for (i = 0; i < size; i++)
+ buf[i] = hw_aread(i * sizeof(u32), ~0);
+
+ return size;
+}
+
+/**
+ * hw_register_write: writes to register
+ * @addr: register address
+ * @data: register value
+ *
+ * This function returns an error code
+ */
+static int hw_register_write(u16 addr, u32 data)
+{
+ /* align */
+ addr /= sizeof(u32);
+
+ if (addr >= hw_bank.size)
+ return -EINVAL;
+
+ /* align */
+ addr *= sizeof(u32);
+
+ hw_awrite(addr, ~0, data);
+ return 0;
+}
+
+/**
+ * hw_test_and_clear_complete: test & clear complete status (execute without
+ * interruption)
+ * @n: endpoint number
+ *
+ * This function returns complete status
+ */
+static int hw_test_and_clear_complete(int n)
+{
+ n = ep_to_bit(n);
+ return hw_ctest_and_clear(CAP_ENDPTCOMPLETE, BIT(n));
+}
+
+/**
+ * hw_test_and_clear_intr_active: test & clear active interrupts (execute
+ * without interruption)
+ *
+ * This function returns active interrutps
+ */
+static u32 hw_test_and_clear_intr_active(void)
+{
+ u32 reg = hw_read_intr_status() & hw_read_intr_enable();
+
+ hw_cwrite(CAP_USBSTS, ~0, reg);
+ return reg;
+}
+
+/**
+ * hw_test_and_clear_setup_guard: test & clear setup guard (execute without
+ * interruption)
+ *
+ * This function returns guard value
+ */
+static int hw_test_and_clear_setup_guard(void)
+{
+ return hw_ctest_and_write(CAP_USBCMD, USBCMD_SUTW, 0);
+}
+
+/**
+ * hw_test_and_set_setup_guard: test & set setup guard (execute without
+ * interruption)
+ *
+ * This function returns guard value
+ */
+static int hw_test_and_set_setup_guard(void)
+{
+ return hw_ctest_and_write(CAP_USBCMD, USBCMD_SUTW, USBCMD_SUTW);
+}
+
+/**
+ * hw_usb_set_address: configures USB address (execute without interruption)
+ * @value: new USB address
+ *
+ * This function returns an error code
+ */
+static int hw_usb_set_address(u8 value)
+{
+ /* advance */
+ hw_cwrite(CAP_DEVICEADDR, DEVICEADDR_USBADR | DEVICEADDR_USBADRA,
+ value << ffs_nr(DEVICEADDR_USBADR) | DEVICEADDR_USBADRA);
+ return 0;
+}
+
+/**
+ * hw_usb_reset: restart device after a bus reset (execute without
+ * interruption)
+ *
+ * This function returns an error code
+ */
+static int hw_usb_reset(void)
+{
+ int delay_count = 10; /* 100 usec delay */
+
+ hw_usb_set_address(0);
+
+ /* ESS flushes only at end?!? */
+ hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0); /* flush all EPs */
+
+ /* clear setup token semaphores */
+ hw_cwrite(CAP_ENDPTSETUPSTAT, 0, 0); /* writes its content */
+
+ /* clear complete status */
+ hw_cwrite(CAP_ENDPTCOMPLETE, 0, 0); /* writes its content */
+
+ /* wait until all bits cleared */
+ while (delay_count-- && hw_cread(CAP_ENDPTPRIME, ~0))
+ udelay(10);
+ if (delay_count < 0)
+ pr_err("ENDPTPRIME is not cleared during bus reset\n");
+
+ /* reset all endpoints ? */
+
+ /* reset internal status and wait for further instructions
+ no need to verify the port reset status (ESS does it) */
+
+ return 0;
+}
+
+/******************************************************************************
+ * DBG block
+ *****************************************************************************/
+/**
+ * show_device: prints information about device capabilities and status
+ *
+ * Check "device.h" for details
+ */
+static ssize_t show_device(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+ struct usb_gadget *gadget = &udc->gadget;
+ int n = 0;
+
+ dbg_trace("[%s] %p\n", __func__, buf);
+ if (attr == NULL || buf == NULL) {
+ dev_err(dev, "[%s] EINVAL\n", __func__);
+ return 0;
+ }
+
+ n += scnprintf(buf + n, PAGE_SIZE - n, "speed = %d\n",
+ gadget->speed);
+ n += scnprintf(buf + n, PAGE_SIZE - n, "max_speed = %d\n",
+ gadget->max_speed);
+ /* TODO: Scheduled for removal in 3.8. */
+ n += scnprintf(buf + n, PAGE_SIZE - n, "is_dualspeed = %d\n",
+ gadget_is_dualspeed(gadget));
+ n += scnprintf(buf + n, PAGE_SIZE - n, "is_otg = %d\n",
+ gadget->is_otg);
+ n += scnprintf(buf + n, PAGE_SIZE - n, "is_a_peripheral = %d\n",
+ gadget->is_a_peripheral);
+ n += scnprintf(buf + n, PAGE_SIZE - n, "b_hnp_enable = %d\n",
+ gadget->b_hnp_enable);
+ n += scnprintf(buf + n, PAGE_SIZE - n, "a_hnp_support = %d\n",
+ gadget->a_hnp_support);
+ n += scnprintf(buf + n, PAGE_SIZE - n, "a_alt_hnp_support = %d\n",
+ gadget->a_alt_hnp_support);
+ n += scnprintf(buf + n, PAGE_SIZE - n, "name = %s\n",
+ (gadget->name ? gadget->name : ""));
+
+ return n;
+}
+static DEVICE_ATTR(device, S_IRUSR, show_device, NULL);
+
+/**
+ * show_driver: prints information about attached gadget (if any)
+ *
+ * Check "device.h" for details
+ */
+static ssize_t show_driver(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+ struct usb_gadget_driver *driver = udc->driver;
+ int n = 0;
+
+ dbg_trace("[%s] %p\n", __func__, buf);
+ if (attr == NULL || buf == NULL) {
+ dev_err(dev, "[%s] EINVAL\n", __func__);
+ return 0;
+ }
+
+ if (driver == NULL)
+ return scnprintf(buf, PAGE_SIZE,
+ "There is no gadget attached!\n");
+
+ n += scnprintf(buf + n, PAGE_SIZE - n, "function = %s\n",
+ (driver->function ? driver->function : ""));
+ n += scnprintf(buf + n, PAGE_SIZE - n, "max speed = %d\n",
+ driver->max_speed);
+
+ return n;
+}
+static DEVICE_ATTR(driver, S_IRUSR, show_driver, NULL);
+
+/* Maximum event message length */
+#define DBG_DATA_MSG 64UL
+
+/* Maximum event messages */
+#define DBG_DATA_MAX 128UL
+
+/* Event buffer descriptor */
+static struct {
+ char (buf[DBG_DATA_MAX])[DBG_DATA_MSG]; /* buffer */
+ unsigned idx; /* index */
+ unsigned tty; /* print to console? */
+ rwlock_t lck; /* lock */
+} dbg_data = {
+ .idx = 0,
+ .tty = 0,
+ .lck = __RW_LOCK_UNLOCKED(lck)
+};
+
+/**
+ * dbg_dec: decrements debug event index
+ * @idx: buffer index
+ */
+static void dbg_dec(unsigned *idx)
+{
+ *idx = (*idx - 1) & (DBG_DATA_MAX-1);
+}
+
+/**
+ * dbg_inc: increments debug event index
+ * @idx: buffer index
+ */
+static void dbg_inc(unsigned *idx)
+{
+ *idx = (*idx + 1) & (DBG_DATA_MAX-1);
+}
+
+
+static unsigned int ep_addr_txdbg_mask;
+module_param(ep_addr_txdbg_mask, uint, S_IRUGO | S_IWUSR);
+static unsigned int ep_addr_rxdbg_mask;
+module_param(ep_addr_rxdbg_mask, uint, S_IRUGO | S_IWUSR);
+
+static int allow_dbg_print(u8 addr)
+{
+ int dir, num;
+
+ /* allow bus wide events */
+ if (addr == 0xff)
+ return 1;
+
+ dir = addr & USB_ENDPOINT_DIR_MASK ? TX : RX;
+ num = addr & ~USB_ENDPOINT_DIR_MASK;
+ num = 1 << num;
+
+ if ((dir == TX) && (num & ep_addr_txdbg_mask))
+ return 1;
+ if ((dir == RX) && (num & ep_addr_rxdbg_mask))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * dbg_print: prints the common part of the event
+ * @addr: endpoint address
+ * @name: event name
+ * @status: status
+ * @extra: extra information
+ */
+static void dbg_print(u8 addr, const char *name, int status, const char *extra)
+{
+ struct timeval tval;
+ unsigned int stamp;
+ unsigned long flags;
+
+ if (!allow_dbg_print(addr))
+ return;
+
+ write_lock_irqsave(&dbg_data.lck, flags);
+
+ do_gettimeofday(&tval);
+ stamp = tval.tv_sec & 0xFFFF; /* 2^32 = 4294967296. Limit to 4096s */
+ stamp = stamp * 1000000 + tval.tv_usec;
+
+ scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG,
+ "%04X\t? %02X %-7.7s %4i ?\t%s\n",
+ stamp, addr, name, status, extra);
+
+ dbg_inc(&dbg_data.idx);
+
+ write_unlock_irqrestore(&dbg_data.lck, flags);
+
+ if (dbg_data.tty != 0)
+ pr_notice("%04X\t? %02X %-7.7s %4i ?\t%s\n",
+ stamp, addr, name, status, extra);
+}
+
+/**
+ * dbg_done: prints a DONE event
+ * @addr: endpoint address
+ * @td: transfer descriptor
+ * @status: status
+ */
+static void dbg_done(u8 addr, const u32 token, int status)
+{
+ char msg[DBG_DATA_MSG];
+
+ scnprintf(msg, sizeof(msg), "%d %02X",
+ (int)(token & TD_TOTAL_BYTES) >> ffs_nr(TD_TOTAL_BYTES),
+ (int)(token & TD_STATUS) >> ffs_nr(TD_STATUS));
+ dbg_print(addr, "DONE", status, msg);
+}
+
+/**
+ * dbg_event: prints a generic event
+ * @addr: endpoint address
+ * @name: event name
+ * @status: status
+ */
+static void dbg_event(u8 addr, const char *name, int status)
+{
+ if (name != NULL)
+ dbg_print(addr, name, status, "");
+}
+
+/*
+ * dbg_queue: prints a QUEUE event
+ * @addr: endpoint address
+ * @req: USB request
+ * @status: status
+ */
+static void dbg_queue(u8 addr, const struct usb_request *req, int status)
+{
+ char msg[DBG_DATA_MSG];
+
+ if (req != NULL) {
+ scnprintf(msg, sizeof(msg),
+ "%d %d", !req->no_interrupt, req->length);
+ dbg_print(addr, "QUEUE", status, msg);
+ }
+}
+
+/**
+ * dbg_setup: prints a SETUP event
+ * @addr: endpoint address
+ * @req: setup request
+ */
+static void dbg_setup(u8 addr, const struct usb_ctrlrequest *req)
+{
+ char msg[DBG_DATA_MSG];
+
+ if (req != NULL) {
+ scnprintf(msg, sizeof(msg),
+ "%02X %02X %04X %04X %d", req->bRequestType,
+ req->bRequest, le16_to_cpu(req->wValue),
+ le16_to_cpu(req->wIndex), le16_to_cpu(req->wLength));
+ dbg_print(addr, "SETUP", 0, msg);
+ }
+}
+
+/**
+ * dbg_usb_op_fail: prints USB Operation FAIL event
+ * @addr: endpoint address
+ * @mEp: endpoint structure
+ */
+static void dbg_usb_op_fail(u8 addr, const char *name,
+ const struct ci13xxx_ep *mep)
+{
+ char msg[DBG_DATA_MSG];
+ struct ci13xxx_req *req;
+ struct list_head *ptr = NULL;
+
+ if (mep != NULL) {
+ scnprintf(msg, sizeof(msg),
+ "%s Fail EP%d%s QH:%08X",
+ name, mep->num,
+ mep->dir ? "IN" : "OUT", mep->qh.ptr->cap);
+ dbg_print(addr, name, 0, msg);
+ scnprintf(msg, sizeof(msg),
+ "cap:%08X %08X %08X\n",
+ mep->qh.ptr->curr, mep->qh.ptr->td.next,
+ mep->qh.ptr->td.token);
+ dbg_print(addr, "QHEAD", 0, msg);
+
+ list_for_each(ptr, &mep->qh.queue) {
+ req = list_entry(ptr, struct ci13xxx_req, queue);
+ scnprintf(msg, sizeof(msg),
+ "%08X:%08X:%08X\n",
+ req->dma, req->ptr->next,
+ req->ptr->token);
+ dbg_print(addr, "REQ", 0, msg);
+ scnprintf(msg, sizeof(msg), "%08X:%d\n",
+ req->ptr->page[0],
+ req->req.status);
+ dbg_print(addr, "REQPAGE", 0, msg);
+ }
+ }
+}
+
+/**
+ * show_events: displays the event buffer
+ *
+ * Check "device.h" for details
+ */
+static ssize_t show_events(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ unsigned long flags;
+ unsigned i, j, n = 0;
+
+ dbg_trace("[%s] %p\n", __func__, buf);
+ if (attr == NULL || buf == NULL) {
+ dev_err(dev, "[%s] EINVAL\n", __func__);
+ return 0;
+ }
+
+ read_lock_irqsave(&dbg_data.lck, flags);
+
+ i = dbg_data.idx;
+ for (dbg_dec(&i); i != dbg_data.idx; dbg_dec(&i)) {
+ n += strlen(dbg_data.buf[i]);
+ if (n >= PAGE_SIZE) {
+ n -= strlen(dbg_data.buf[i]);
+ break;
+ }
+ }
+ for (j = 0, dbg_inc(&i); j < n; dbg_inc(&i))
+ j += scnprintf(buf + j, PAGE_SIZE - j,
+ "%s", dbg_data.buf[i]);
+
+ read_unlock_irqrestore(&dbg_data.lck, flags);
+
+ return n;
+}
+
+/**
+ * store_events: configure if events are going to be also printed to console
+ *
+ * Check "device.h" for details
+ */
+static ssize_t store_events(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned tty;
+
+ dbg_trace("[%s] %p, %d\n", __func__, buf, count);
+ if (attr == NULL || buf == NULL) {
+ dev_err(dev, "[%s] EINVAL\n", __func__);
+ goto done;
+ }
+
+ if (sscanf(buf, "%u", &tty) != 1 || tty > 1) {
+ dev_err(dev, "<1|0>: enable|disable console log\n");
+ goto done;
+ }
+
+ dbg_data.tty = tty;
+ dev_info(dev, "tty = %u", dbg_data.tty);
+
+ done:
+ return count;
+}
+static DEVICE_ATTR(events, S_IRUSR | S_IWUSR, show_events, store_events);
+
+/**
+ * show_inters: interrupt status, enable status and historic
+ *
+ * Check "device.h" for details
+ */
+static ssize_t show_inters(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+ unsigned long flags;
+ u32 intr;
+ unsigned i, j, n = 0;
+
+ dbg_trace("[%s] %p\n", __func__, buf);
+ if (attr == NULL || buf == NULL) {
+ dev_err(dev, "[%s] EINVAL\n", __func__);
+ return 0;
+ }
+
+ spin_lock_irqsave(udc->lock, flags);
+
+ n += scnprintf(buf + n, PAGE_SIZE - n,
+ "status = %08x\n", hw_read_intr_status());
+ n += scnprintf(buf + n, PAGE_SIZE - n,
+ "enable = %08x\n", hw_read_intr_enable());
+
+ n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n",
+ isr_statistics.test);
+ n += scnprintf(buf + n, PAGE_SIZE - n, "? ui = %d\n",
+ isr_statistics.ui);
+ n += scnprintf(buf + n, PAGE_SIZE - n, "? uei = %d\n",
+ isr_statistics.uei);
+ n += scnprintf(buf + n, PAGE_SIZE - n, "? pci = %d\n",
+ isr_statistics.pci);
+ n += scnprintf(buf + n, PAGE_SIZE - n, "? uri = %d\n",
+ isr_statistics.uri);
+ n += scnprintf(buf + n, PAGE_SIZE - n, "? sli = %d\n",
+ isr_statistics.sli);
+ n += scnprintf(buf + n, PAGE_SIZE - n, "*none = %d\n",
+ isr_statistics.none);
+ n += scnprintf(buf + n, PAGE_SIZE - n, "*hndl = %d\n",
+ isr_statistics.hndl.cnt);
+
+ for (i = isr_statistics.hndl.idx, j = 0; j <= ISR_MASK; j++, i++) {
+ i &= ISR_MASK;
+ intr = isr_statistics.hndl.buf[i];
+
+ if (USBi_UI & intr)
+ n += scnprintf(buf + n, PAGE_SIZE - n, "ui ");
+ intr &= ~USBi_UI;
+ if (USBi_UEI & intr)
+ n += scnprintf(buf + n, PAGE_SIZE - n, "uei ");
+ intr &= ~USBi_UEI;
+ if (USBi_PCI & intr)
+ n += scnprintf(buf + n, PAGE_SIZE - n, "pci ");
+ intr &= ~USBi_PCI;
+ if (USBi_URI & intr)
+ n += scnprintf(buf + n, PAGE_SIZE - n, "uri ");
+ intr &= ~USBi_URI;
+ if (USBi_SLI & intr)
+ n += scnprintf(buf + n, PAGE_SIZE - n, "sli ");
+ intr &= ~USBi_SLI;
+ if (intr)
+ n += scnprintf(buf + n, PAGE_SIZE - n, "??? ");
+ if (isr_statistics.hndl.buf[i])
+ n += scnprintf(buf + n, PAGE_SIZE - n, "\n");
+ }
+
+ spin_unlock_irqrestore(udc->lock, flags);
+
+ return n;
+}
+
+/**
+ * store_inters: enable & force or disable an individual interrutps
+ * (to be used for test purposes only)
+ *
+ * Check "device.h" for details
+ */
+static ssize_t store_inters(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+ unsigned long flags;
+ unsigned en, bit;
+
+ dbg_trace("[%s] %p, %d\n", __func__, buf, count);
+ if (attr == NULL || buf == NULL) {
+ dev_err(dev, "[%s] EINVAL\n", __func__);
+ goto done;
+ }
+
+ if (sscanf(buf, "%u %u", &en, &bit) != 2 || en > 1) {
+ dev_err(dev, "<1|0> <bit>: enable|disable interrupt");
+ goto done;
+ }
+
+ spin_lock_irqsave(udc->lock, flags);
+ if (en) {
+ if (hw_intr_force(bit))
+ dev_err(dev, "invalid bit number\n");
+ else
+ isr_statistics.test++;
+ } else {
+ if (hw_intr_clear(bit))
+ dev_err(dev, "invalid bit number\n");
+ }
+ spin_unlock_irqrestore(udc->lock, flags);
+
+ done:
+ return count;
+}
+static DEVICE_ATTR(inters, S_IRUSR | S_IWUSR, show_inters, store_inters);
+
+/**
+ * show_port_test: reads port test mode
+ *
+ * Check "device.h" for details
+ */
+static ssize_t show_port_test(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+ unsigned long flags;
+ unsigned mode;
+
+ dbg_trace("[%s] %p\n", __func__, buf);
+ if (attr == NULL || buf == NULL) {
+ dev_err(dev, "[%s] EINVAL\n", __func__);
+ return 0;
+ }
+
+ spin_lock_irqsave(udc->lock, flags);
+ mode = hw_port_test_get();
+ spin_unlock_irqrestore(udc->lock, flags);
+
+ return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode);
+}
+
+/**
+ * store_port_test: writes port test mode
+ *
+ * Check "device.h" for details
+ */
+static ssize_t store_port_test(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+ unsigned long flags;
+ unsigned mode;
+
+ dbg_trace("[%s] %p, %d\n", __func__, buf, count);
+ if (attr == NULL || buf == NULL) {
+ dev_err(dev, "[%s] EINVAL\n", __func__);
+ goto done;
+ }
+
+ if (sscanf(buf, "%u", &mode) != 1) {
+ dev_err(dev, "<mode>: set port test mode");
+ goto done;
+ }
+
+ spin_lock_irqsave(udc->lock, flags);
+ if (hw_port_test_set(mode))
+ dev_err(dev, "invalid mode\n");
+ spin_unlock_irqrestore(udc->lock, flags);
+
+ done:
+ return count;
+}
+static DEVICE_ATTR(port_test, S_IRUSR | S_IWUSR,
+ show_port_test, store_port_test);
+
+/**
+ * show_qheads: DMA contents of all queue heads
+ *
+ * Check "device.h" for details
+ */
+static ssize_t show_qheads(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+ unsigned long flags;
+ unsigned i, j, n = 0;
+
+ dbg_trace("[%s] %p\n", __func__, buf);
+ if (attr == NULL || buf == NULL) {
+ dev_err(dev, "[%s] EINVAL\n", __func__);
+ return 0;
+ }
+
+ spin_lock_irqsave(udc->lock, flags);
+ for (i = 0; i < hw_ep_max/2; i++) {
+ struct ci13xxx_ep *mEpRx = &udc->ci13xxx_ep[i];
+ struct ci13xxx_ep *mEpTx = &udc->ci13xxx_ep[i + hw_ep_max/2];
+ n += scnprintf(buf + n, PAGE_SIZE - n,
+ "EP=%02i: RX=%08X TX=%08X\n",
+ i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma);
+ for (j = 0; j < (sizeof(struct ci13xxx_qh)/sizeof(u32)); j++) {
+ n += scnprintf(buf + n, PAGE_SIZE - n,
+ " %04X: %08X %08X\n", j,
+ *((u32 *)mEpRx->qh.ptr + j),
+ *((u32 *)mEpTx->qh.ptr + j));
+ }
+ }
+ spin_unlock_irqrestore(udc->lock, flags);
+
+ return n;
+}
+static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL);
+
+/**
+ * show_registers: dumps all registers
+ *
+ * Check "device.h" for details
+ */
+#define DUMP_ENTRIES 512
+static ssize_t show_registers(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+ unsigned long flags;
+ u32 *dump;
+ unsigned i, k, n = 0;
+
+ dbg_trace("[%s] %p\n", __func__, buf);
+ if (attr == NULL || buf == NULL) {
+ dev_err(dev, "[%s] EINVAL\n", __func__);
+ return 0;
+ }
+
+ dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL);
+ if (!dump) {
+ dev_err(dev, "%s: out of memory\n", __func__);
+ return 0;
+ }
+
+ spin_lock_irqsave(udc->lock, flags);
+ k = hw_register_read(dump, DUMP_ENTRIES);
+ spin_unlock_irqrestore(udc->lock, flags);
+
+ for (i = 0; i < k; i++) {
+ n += scnprintf(buf + n, PAGE_SIZE - n,
+ "reg[0x%04X] = 0x%08X\n",
+ i * (unsigned)sizeof(u32), dump[i]);
+ }
+ kfree(dump);
+
+ return n;
+}
+
+/**
+ * store_registers: writes value to register address
+ *
+ * Check "device.h" for details
+ */
+static ssize_t store_registers(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+ unsigned long addr, data, flags;
+
+ dbg_trace("[%s] %p, %d\n", __func__, buf, count);
+ if (attr == NULL || buf == NULL) {
+ dev_err(dev, "[%s] EINVAL\n", __func__);
+ goto done;
+ }
+
+ if (sscanf(buf, "%li %li", &addr, &data) != 2) {
+ dev_err(dev, "<addr> <data>: write data to register address");
+ goto done;
+ }
+
+ spin_lock_irqsave(udc->lock, flags);
+ if (hw_register_write(addr, data))
+ dev_err(dev, "invalid address range\n");
+ spin_unlock_irqrestore(udc->lock, flags);
+
+ done:
+ return count;
+}
+static DEVICE_ATTR(registers, S_IRUSR | S_IWUSR,
+ show_registers, store_registers);
+
+/**
+ * show_requests: DMA contents of all requests currently queued (all endpts)
+ *
+ * Check "device.h" for details
+ */
+static ssize_t show_requests(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+ unsigned long flags;
+ struct list_head *ptr = NULL;
+ struct ci13xxx_req *req = NULL;
+ unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32);
+
+ dbg_trace("[%s] %p\n", __func__, buf);
+ if (attr == NULL || buf == NULL) {
+ dev_err(dev, "[%s] EINVAL\n", __func__);
+ return 0;
+ }
+
+ spin_lock_irqsave(udc->lock, flags);
+ for (i = 0; i < hw_ep_max; i++)
+ list_for_each(ptr, &udc->ci13xxx_ep[i].qh.queue)
+ {
+ req = list_entry(ptr, struct ci13xxx_req, queue);
+
+ n += scnprintf(buf + n, PAGE_SIZE - n,
+ "EP=%02i: TD=%08X %s\n",
+ i % hw_ep_max/2, (u32)req->dma,
+ ((i < hw_ep_max/2) ? "RX" : "TX"));
+
+ for (j = 0; j < qSize; j++)
+ n += scnprintf(buf + n, PAGE_SIZE - n,
+ " %04X: %08X\n", j,
+ *((u32 *)req->ptr + j));
+ }
+ spin_unlock_irqrestore(udc->lock, flags);
+
+ return n;
+}
+static DEVICE_ATTR(requests, S_IRUSR, show_requests, NULL);
+
+/* EP# and Direction */
+static ssize_t prime_ept(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+ struct ci13xxx_ep *mEp;
+ unsigned int ep_num, dir;
+ int n;
+ struct ci13xxx_req *mReq = NULL;
+
+ if (sscanf(buf, "%u %u", &ep_num, &dir) != 2) {
+ dev_err(dev, "<ep_num> <dir>: prime the ep");
+ goto done;
+ }
+
+ if (dir)
+ mEp = &udc->ci13xxx_ep[ep_num + hw_ep_max/2];
+ else
+ mEp = &udc->ci13xxx_ep[ep_num];
+
+ n = hw_ep_bit(mEp->num, mEp->dir);
+ mReq = list_entry(mEp->qh.queue.next, struct ci13xxx_req, queue);
+ mEp->qh.ptr->td.next = mReq->dma;
+ mEp->qh.ptr->td.token &= ~TD_STATUS;
+
+ wmb();
+
+ hw_cwrite(CAP_ENDPTPRIME, BIT(n), BIT(n));
+ while (hw_cread(CAP_ENDPTPRIME, BIT(n)))
+ cpu_relax();
+
+ pr_info("%s: prime:%08x stat:%08x ep#%d dir:%s\n", __func__,
+ hw_cread(CAP_ENDPTPRIME, ~0),
+ hw_cread(CAP_ENDPTSTAT, ~0),
+ mEp->num, mEp->dir ? "IN" : "OUT");
+done:
+ return count;
+
+}
+static DEVICE_ATTR(prime, S_IWUSR, NULL, prime_ept);
+
+/* EP# and Direction */
+static ssize_t print_dtds(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+ struct ci13xxx_ep *mEp;
+ unsigned int ep_num, dir;
+ int n;
+ struct list_head *ptr = NULL;
+ struct ci13xxx_req *req = NULL;
+
+ if (sscanf(buf, "%u %u", &ep_num, &dir) != 2) {
+ dev_err(dev, "<ep_num> <dir>: to print dtds");
+ goto done;
+ }
+
+ if (dir)
+ mEp = &udc->ci13xxx_ep[ep_num + hw_ep_max/2];
+ else
+ mEp = &udc->ci13xxx_ep[ep_num];
+
+ n = hw_ep_bit(mEp->num, mEp->dir);
+ pr_info("%s: prime:%08x stat:%08x ep#%d dir:%s"
+ "dTD_update_fail_count: %lu "
+ "mEp->dTD_update_fail_count: %lu"
+ "mEp->prime_fail_count: %lu\n", __func__,
+ hw_cread(CAP_ENDPTPRIME, ~0),
+ hw_cread(CAP_ENDPTSTAT, ~0),
+ mEp->num, mEp->dir ? "IN" : "OUT",
+ udc->dTD_update_fail_count,
+ mEp->dTD_update_fail_count,
+ mEp->prime_fail_count);
+
+ pr_info("QH: cap:%08x cur:%08x next:%08x token:%08x\n",
+ mEp->qh.ptr->cap, mEp->qh.ptr->curr,
+ mEp->qh.ptr->td.next, mEp->qh.ptr->td.token);
+
+ list_for_each(ptr, &mEp->qh.queue) {
+ req = list_entry(ptr, struct ci13xxx_req, queue);
+
+ pr_info("\treq:%08x next:%08x token:%08x page0:%08x status:%d\n",
+ req->dma, req->ptr->next, req->ptr->token,
+ req->ptr->page[0], req->req.status);
+ }
+done:
+ return count;
+
+}
+static DEVICE_ATTR(dtds, S_IWUSR, NULL, print_dtds);
+
+static int ci13xxx_wakeup(struct usb_gadget *_gadget)
+{
+ struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
+ unsigned long flags;
+ int ret = 0;
+
+ trace();
+
+ spin_lock_irqsave(udc->lock, flags);
+ if (!udc->remote_wakeup) {
+ ret = -EOPNOTSUPP;
+ dbg_trace("remote wakeup feature is not enabled\n");
+ goto out;
+ }
+ spin_unlock_irqrestore(udc->lock, flags);
+
+ udc->udc_driver->notify_event(udc,
+ CI13XXX_CONTROLLER_REMOTE_WAKEUP_EVENT);
+
+ if (udc->transceiver)
+ usb_phy_set_suspend(udc->transceiver, 0);
+
+ spin_lock_irqsave(udc->lock, flags);
+ if (!hw_cread(CAP_PORTSC, PORTSC_SUSP)) {
+ ret = -EINVAL;
+ dbg_trace("port is not suspended\n");
+ goto out;
+ }
+ hw_cwrite(CAP_PORTSC, PORTSC_FPR, PORTSC_FPR);
+out:
+ spin_unlock_irqrestore(udc->lock, flags);
+ return ret;
+}
+
+static void usb_do_remote_wakeup(struct work_struct *w)
+{
+ struct ci13xxx *udc = _udc;
+ unsigned long flags;
+ bool do_wake;
+
+ /*
+ * This work can not be canceled from interrupt handler. Check
+ * if wakeup conditions are still met.
+ */
+ spin_lock_irqsave(udc->lock, flags);
+ do_wake = udc->suspended && udc->remote_wakeup;
+ spin_unlock_irqrestore(udc->lock, flags);
+
+ if (do_wake)
+ ci13xxx_wakeup(&udc->gadget);
+}
+
+static ssize_t usb_remote_wakeup(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev);
+
+ ci13xxx_wakeup(&udc->gadget);
+
+ return count;
+}
+static DEVICE_ATTR(wakeup, S_IWUSR, 0, usb_remote_wakeup);
+
+/**
+ * dbg_create_files: initializes the attribute interface
+ * @dev: device
+ *
+ * This function returns an error code
+ */
+__maybe_unused static int dbg_create_files(struct device *dev)
+{
+ int retval = 0;
+
+ if (dev == NULL)
+ return -EINVAL;
+ retval = device_create_file(dev, &dev_attr_device);
+ if (retval)
+ goto done;
+ retval = device_create_file(dev, &dev_attr_driver);
+ if (retval)
+ goto rm_device;
+ retval = device_create_file(dev, &dev_attr_events);
+ if (retval)
+ goto rm_driver;
+ retval = device_create_file(dev, &dev_attr_inters);
+ if (retval)
+ goto rm_events;
+ retval = device_create_file(dev, &dev_attr_port_test);
+ if (retval)
+ goto rm_inters;
+ retval = device_create_file(dev, &dev_attr_qheads);
+ if (retval)
+ goto rm_port_test;
+ retval = device_create_file(dev, &dev_attr_registers);
+ if (retval)
+ goto rm_qheads;
+ retval = device_create_file(dev, &dev_attr_requests);
+ if (retval)
+ goto rm_registers;
+ retval = device_create_file(dev, &dev_attr_wakeup);
+ if (retval)
+ goto rm_remote_wakeup;
+ retval = device_create_file(dev, &dev_attr_prime);
+ if (retval)
+ goto rm_prime;
+ retval = device_create_file(dev, &dev_attr_dtds);
+ if (retval)
+ goto rm_dtds;
+
+ return 0;
+
+rm_dtds:
+ device_remove_file(dev, &dev_attr_dtds);
+rm_prime:
+ device_remove_file(dev, &dev_attr_prime);
+rm_remote_wakeup:
+ device_remove_file(dev, &dev_attr_wakeup);
+ rm_registers:
+ device_remove_file(dev, &dev_attr_registers);
+ rm_qheads:
+ device_remove_file(dev, &dev_attr_qheads);
+ rm_port_test:
+ device_remove_file(dev, &dev_attr_port_test);
+ rm_inters:
+ device_remove_file(dev, &dev_attr_inters);
+ rm_events:
+ device_remove_file(dev, &dev_attr_events);
+ rm_driver:
+ device_remove_file(dev, &dev_attr_driver);
+ rm_device:
+ device_remove_file(dev, &dev_attr_device);
+ done:
+ return retval;
+}
+
+/**
+ * dbg_remove_files: destroys the attribute interface
+ * @dev: device
+ *
+ * This function returns an error code
+ */
+__maybe_unused static int dbg_remove_files(struct device *dev)
+{
+ if (dev == NULL)
+ return -EINVAL;
+ device_remove_file(dev, &dev_attr_requests);
+ device_remove_file(dev, &dev_attr_registers);
+ device_remove_file(dev, &dev_attr_qheads);
+ device_remove_file(dev, &dev_attr_port_test);
+ device_remove_file(dev, &dev_attr_inters);
+ device_remove_file(dev, &dev_attr_events);
+ device_remove_file(dev, &dev_attr_driver);
+ device_remove_file(dev, &dev_attr_device);
+ device_remove_file(dev, &dev_attr_wakeup);
+ return 0;
+}
+
+static void dump_usb_info(void *ignore, unsigned int ebi_addr,
+ unsigned int ebi_apacket0, unsigned int ebi_apacket1)
+{
+ struct ci13xxx *udc = _udc;
+ unsigned long flags;
+ struct list_head *ptr = NULL;
+ struct ci13xxx_req *req = NULL;
+ struct ci13xxx_ep *mEp;
+ unsigned i;
+ struct ci13xxx_ebi_err_entry *temp_dump;
+ static int count;
+ u32 epdir = 0;
+
+ if (count)
+ return;
+ count++;
+
+ pr_info("%s: USB EBI error detected\n", __func__);
+
+ ebi_err_data = kmalloc(sizeof(struct ci13xxx_ebi_err_data),
+ GFP_ATOMIC);
+ if (!ebi_err_data) {
+ pr_err("%s: memory alloc failed for ebi_err_data\n", __func__);
+ return;
+ }
+
+ ebi_err_data->ebi_err_entry = kmalloc(
+ sizeof(struct ci13xxx_ebi_err_entry),
+ GFP_ATOMIC);
+ if (!ebi_err_data->ebi_err_entry) {
+ kfree(ebi_err_data);
+ pr_err("%s: memory alloc failed for ebi_err_entry\n", __func__);
+ return;
+ }
+
+ ebi_err_data->ebi_err_addr = ebi_addr;
+ ebi_err_data->apkt0 = ebi_apacket0;
+ ebi_err_data->apkt1 = ebi_apacket1;
+
+ temp_dump = ebi_err_data->ebi_err_entry;
+ pr_info("\n DUMPING USB Requests Information\n");
+ spin_lock_irqsave(udc->lock, flags);
+ for (i = 0; i < hw_ep_max; i++) {
+ list_for_each(ptr, &udc->ci13xxx_ep[i].qh.queue) {
+ mEp = &udc->ci13xxx_ep[i];
+ req = list_entry(ptr, struct ci13xxx_req, queue);
+
+ temp_dump->usb_req_buf = req->req.buf;
+ temp_dump->usb_req_length = req->req.length;
+ epdir = mEp->dir;
+ temp_dump->ep_info = mEp->num | (epdir << 15);
+
+ temp_dump->next = kmalloc(
+ sizeof(struct ci13xxx_ebi_err_entry),
+ GFP_ATOMIC);
+ if (!temp_dump->next) {
+ pr_err("%s: memory alloc failed\n", __func__);
+ spin_unlock_irqrestore(udc->lock, flags);
+ return;
+ }
+ temp_dump = temp_dump->next;
+ }
+ }
+ spin_unlock_irqrestore(udc->lock, flags);
+}
+
+/******************************************************************************
+ * UTIL block
+ *****************************************************************************/
+/**
+ * _usb_addr: calculates endpoint address from direction & number
+ * @ep: endpoint
+ */
+static inline u8 _usb_addr(struct ci13xxx_ep *ep)
+{
+ return ((ep->dir == TX) ? USB_ENDPOINT_DIR_MASK : 0) | ep->num;
+}
+
+static void ep_prime_timer_func(unsigned long data)
+{
+ struct ci13xxx_ep *mep = (struct ci13xxx_ep *)data;
+ struct ci13xxx_req *req;
+ struct list_head *ptr = NULL;
+ int n = hw_ep_bit(mep->num, mep->dir);
+ unsigned long flags;
+
+
+ spin_lock_irqsave(mep->lock, flags);
+
+ if (_udc && (!_udc->vbus_active || _udc->suspended)) {
+ pr_debug("ep%d%s prime timer when vbus_active=%d,suspend=%d\n",
+ mep->num, mep->dir ? "IN" : "OUT",
+ _udc->vbus_active, _udc->suspended);
+ goto out;
+ }
+
+ if (!hw_cread(CAP_ENDPTPRIME, BIT(n)))
+ goto out;
+
+ if (list_empty(&mep->qh.queue))
+ goto out;
+
+ req = list_entry(mep->qh.queue.next, struct ci13xxx_req, queue);
+
+ mb();
+ if (!(TD_STATUS_ACTIVE & req->ptr->token))
+ goto out;
+
+ mep->prime_timer_count++;
+ if (mep->prime_timer_count == MAX_PRIME_CHECK_RETRY) {
+ mep->prime_timer_count = 0;
+ pr_info("ep%d dir:%s QH:cap:%08x cur:%08x next:%08x tkn:%08x\n",
+ mep->num, mep->dir ? "IN" : "OUT",
+ mep->qh.ptr->cap, mep->qh.ptr->curr,
+ mep->qh.ptr->td.next, mep->qh.ptr->td.token);
+ list_for_each(ptr, &mep->qh.queue) {
+ req = list_entry(ptr, struct ci13xxx_req, queue);
+ pr_info("\treq:%08xnext:%08xtkn:%08xpage0:%08xsts:%d\n",
+ req->dma, req->ptr->next,
+ req->ptr->token, req->ptr->page[0],
+ req->req.status);
+ }
+ dbg_usb_op_fail(0xFF, "PRIMEF", mep);
+ mep->prime_fail_count++;
+ } else {
+ mod_timer(&mep->prime_timer, EP_PRIME_CHECK_DELAY);
+ }
+
+ spin_unlock_irqrestore(mep->lock, flags);
+ return;
+
+out:
+ mep->prime_timer_count = 0;
+ spin_unlock_irqrestore(mep->lock, flags);
+
+}
+
+/**
+ * _hardware_queue: configures a request at hardware level
+ * @gadget: gadget
+ * @mEp: endpoint
+ *
+ * This function returns an error code
+ */
+static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
+{
+ unsigned i;
+ int ret = 0;
+ unsigned length = mReq->req.length;
+ struct ci13xxx *udc = _udc;
+
+ trace("%p, %p", mEp, mReq);
+
+ /* don't queue twice */
+ if (mReq->req.status == -EALREADY)
+ return -EALREADY;
+
+ mReq->req.status = -EALREADY;
+ if (length && mReq->req.dma == DMA_ADDR_INVALID) {
+ mReq->req.dma = \
+ dma_map_single(mEp->device, mReq->req.buf,
+ length, mEp->dir ? DMA_TO_DEVICE :
+ DMA_FROM_DEVICE);
+ if (mReq->req.dma == 0)
+ return -ENOMEM;
+
+ mReq->map = 1;
+ }
+
+ if (mReq->req.zero && length && (length % mEp->ep.maxpacket == 0)) {
+ mReq->zptr = dma_pool_alloc(mEp->td_pool, GFP_ATOMIC,
+ &mReq->zdma);
+ if (mReq->zptr == NULL) {
+ if (mReq->map) {
+ dma_unmap_single(mEp->device, mReq->req.dma,
+ length, mEp->dir ? DMA_TO_DEVICE :
+ DMA_FROM_DEVICE);
+ mReq->req.dma = DMA_ADDR_INVALID;
+ mReq->map = 0;
+ }
+ return -ENOMEM;
+ }
+ memset(mReq->zptr, 0, sizeof(*mReq->zptr));
+ mReq->zptr->next = TD_TERMINATE;
+ mReq->zptr->token = TD_STATUS_ACTIVE;
+ if (!mReq->req.no_interrupt)
+ mReq->zptr->token |= TD_IOC;
+ }
+ /*
+ * TD configuration
+ * TODO - handle requests which spawns into several TDs
+ */
+ memset(mReq->ptr, 0, sizeof(*mReq->ptr));
+ mReq->ptr->token = length << ffs_nr(TD_TOTAL_BYTES);
+ mReq->ptr->token &= TD_TOTAL_BYTES;
+ mReq->ptr->token |= TD_STATUS_ACTIVE;
+ if (mReq->zptr) {
+ mReq->ptr->next = mReq->zdma;
+ } else {
+ mReq->ptr->next = TD_TERMINATE;
+ if (!mReq->req.no_interrupt)
+ mReq->ptr->token |= TD_IOC;
+ }
+
+ /* MSM Specific: updating the request as required for
+ * SPS mode. Enable MSM DMA engine acording
+ * to the UDC private data in the request.
+ */
+ if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
+ if (mReq->req.udc_priv & MSM_SPS_MODE) {
+ mReq->ptr->token = TD_STATUS_ACTIVE;
+ if (mReq->req.udc_priv & MSM_IS_FINITE_TRANSFER)
+ mReq->ptr->next = TD_TERMINATE;
+ else
+ mReq->ptr->next = MSM_ETD_TYPE | mReq->dma;
+ if (!mReq->req.no_interrupt)
+ mReq->ptr->token |= MSM_ETD_IOC;
+ }
+ mReq->req.dma = 0;
+ }
+
+ mReq->ptr->page[0] = mReq->req.dma;
+ for (i = 1; i < 5; i++)
+ mReq->ptr->page[i] = (mReq->req.dma + i * CI13XXX_PAGE_SIZE) &
+ ~TD_RESERVED_MASK;
+ wmb();
+
+ /* Remote Wakeup */
+ if (udc->suspended) {
+ if (!udc->remote_wakeup) {
+ mReq->req.status = -EAGAIN;
+ dev_dbg(mEp->device, "%s: queue failed (suspend) ept #%d\n",
+ __func__, mEp->num);
+ return -EAGAIN;
+ }
+ usb_phy_set_suspend(udc->transceiver, 0);
+ schedule_delayed_work(&udc->rw_work, REMOTE_WAKEUP_DELAY);
+ }
+
+ if (!list_empty(&mEp->qh.queue)) {
+ struct ci13xxx_req *mReqPrev;
+ int n = hw_ep_bit(mEp->num, mEp->dir);
+ int tmp_stat;
+ ktime_t start, diff;
+
+ mReqPrev = list_entry(mEp->qh.queue.prev,
+ struct ci13xxx_req, queue);
+ if (mReqPrev->zptr)
+ mReqPrev->zptr->next = mReq->dma & TD_ADDR_MASK;
+ else
+ mReqPrev->ptr->next = mReq->dma & TD_ADDR_MASK;
+ wmb();
+ if (hw_cread(CAP_ENDPTPRIME, BIT(n)))
+ goto done;
+ start = ktime_get();
+ do {
+ hw_cwrite(CAP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW);
+ tmp_stat = hw_cread(CAP_ENDPTSTAT, BIT(n));
+ diff = ktime_sub(ktime_get(), start);
+ /* poll for max. 100ms */
+ if (ktime_to_ms(diff) > USB_MAX_TIMEOUT) {
+ if (hw_cread(CAP_USBCMD, USBCMD_ATDTW))
+ break;
+ printk_ratelimited(KERN_ERR
+ "%s:queue failed ep#%d %s\n",
+ __func__, mEp->num, mEp->dir ? "IN" : "OUT");
+ return -EAGAIN;
+ }
+ } while (!hw_cread(CAP_USBCMD, USBCMD_ATDTW));
+ hw_cwrite(CAP_USBCMD, USBCMD_ATDTW, 0);
+ if (tmp_stat)
+ goto done;
+ }
+
+ /* QH configuration */
+ if (!list_empty(&mEp->qh.queue)) {
+ struct ci13xxx_req *mReq = \
+ list_entry(mEp->qh.queue.next,
+ struct ci13xxx_req, queue);
+
+ if (TD_STATUS_ACTIVE & mReq->ptr->token) {
+ mEp->qh.ptr->td.next = mReq->dma;
+ mEp->qh.ptr->td.token &= ~TD_STATUS;
+ goto prime;
+ }
+ }
+
+ mEp->qh.ptr->td.next = mReq->dma; /* TERMINATE = 0 */
+
+ if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
+ if (mReq->req.udc_priv & MSM_SPS_MODE) {
+ mEp->qh.ptr->td.next |= MSM_ETD_TYPE;
+ i = hw_cread(CAP_ENDPTPIPEID +
+ mEp->num * sizeof(u32), ~0);
+ /* Read current value of this EPs pipe id */
+ i = (mEp->dir == TX) ?
+ ((i >> MSM_TX_PIPE_ID_OFS) & MSM_PIPE_ID_MASK) :
+ (i & MSM_PIPE_ID_MASK);
+ /* If requested pipe id is different from current,
+ then write it */
+ if (i != (mReq->req.udc_priv & MSM_PIPE_ID_MASK)) {
+ if (mEp->dir == TX)
+ hw_cwrite(
+ CAP_ENDPTPIPEID +
+ mEp->num * sizeof(u32),
+ MSM_PIPE_ID_MASK <<
+ MSM_TX_PIPE_ID_OFS,
+ (mReq->req.udc_priv &
+ MSM_PIPE_ID_MASK)
+ << MSM_TX_PIPE_ID_OFS);
+ else
+ hw_cwrite(
+ CAP_ENDPTPIPEID +
+ mEp->num * sizeof(u32),
+ MSM_PIPE_ID_MASK,
+ mReq->req.udc_priv &
+ MSM_PIPE_ID_MASK);
+ }
+ }
+ }
+
+ mEp->qh.ptr->td.token &= ~TD_STATUS; /* clear status */
+ mEp->qh.ptr->cap |= QH_ZLT;
+
+prime:
+ wmb(); /* synchronize before ep prime */
+
+ ret = hw_ep_prime(mEp->num, mEp->dir,
+ mEp->type == USB_ENDPOINT_XFER_CONTROL);
+ if (!ret)
+ mod_timer(&mEp->prime_timer, EP_PRIME_CHECK_DELAY);
+done:
+ return ret;
+}
+
+/**
+ * _hardware_dequeue: handles a request at hardware level
+ * @gadget: gadget
+ * @mEp: endpoint
+ *
+ * This function returns an error code
+ */
+static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
+{
+ trace("%p, %p", mEp, mReq);
+
+ if (mReq->req.status != -EALREADY)
+ return -EINVAL;
+
+ /* clean speculative fetches on req->ptr->token */
+ mb();
+
+ if ((TD_STATUS_ACTIVE & mReq->ptr->token) != 0)
+ return -EBUSY;
+
+ if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID)
+ if ((mReq->req.udc_priv & MSM_SPS_MODE) &&
+ (mReq->req.udc_priv & MSM_IS_FINITE_TRANSFER))
+ return -EBUSY;
+ if (mReq->zptr) {
+ if ((TD_STATUS_ACTIVE & mReq->zptr->token) != 0)
+ return -EBUSY;
+
+ /* The controller may access this dTD one more time.
+ * Defer freeing this to next zero length dTD completion.
+ * It is safe to assume that controller will no longer
+ * access the previous dTD after next dTD completion.
+ */
+ if (mEp->last_zptr)
+ dma_pool_free(mEp->td_pool, mEp->last_zptr,
+ mEp->last_zdma);
+ mEp->last_zptr = mReq->zptr;
+ mEp->last_zdma = mReq->zdma;
+
+ mReq->zptr = NULL;
+ }
+
+ mReq->req.status = 0;
+
+ if (mReq->map) {
+ dma_unmap_single(mEp->device, mReq->req.dma, mReq->req.length,
+ mEp->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ mReq->req.dma = DMA_ADDR_INVALID;
+ mReq->map = 0;
+ }
+
+ mReq->req.status = mReq->ptr->token & TD_STATUS;
+ if ((TD_STATUS_HALTED & mReq->req.status) != 0)
+ mReq->req.status = -1;
+ else if ((TD_STATUS_DT_ERR & mReq->req.status) != 0)
+ mReq->req.status = -1;
+ else if ((TD_STATUS_TR_ERR & mReq->req.status) != 0)
+ mReq->req.status = -1;
+
+ mReq->req.actual = mReq->ptr->token & TD_TOTAL_BYTES;
+ mReq->req.actual >>= ffs_nr(TD_TOTAL_BYTES);
+ mReq->req.actual = mReq->req.length - mReq->req.actual;
+ mReq->req.actual = mReq->req.status ? 0 : mReq->req.actual;
+
+ return mReq->req.actual;
+}
+
+/**
+ * restore_original_req: Restore original req's attributes
+ * @mReq: Request
+ *
+ * This function restores original req's attributes. Call
+ * this function before completing the large req (>16K).
+ */
+static void restore_original_req(struct ci13xxx_req *mReq)
+{
+ mReq->req.buf = mReq->multi.buf;
+ mReq->req.length = mReq->multi.len;
+ if (!mReq->req.status)
+ mReq->req.actual = mReq->multi.actual;
+
+ mReq->multi.len = 0;
+ mReq->multi.actual = 0;
+ mReq->multi.buf = NULL;
+}
+
+/**
+ * _ep_nuke: dequeues all endpoint requests
+ * @mEp: endpoint
+ *
+ * This function returns an error code
+ * Caller must hold lock
+ */
+static int _ep_nuke(struct ci13xxx_ep *mEp)
+__releases(mEp->lock)
+__acquires(mEp->lock)
+{
+ struct ci13xxx_ep *mEpTemp = mEp;
+ unsigned val;
+
+ trace("%p", mEp);
+
+ if (mEp == NULL)
+ return -EINVAL;
+
+ del_timer(&mEp->prime_timer);
+ mEp->prime_timer_count = 0;
+
+ hw_ep_flush(mEp->num, mEp->dir);
+
+ while (!list_empty(&mEp->qh.queue)) {
+
+ /* pop oldest request */
+ struct ci13xxx_req *mReq = \
+ list_entry(mEp->qh.queue.next,
+ struct ci13xxx_req, queue);
+ list_del_init(&mReq->queue);
+
+ /* MSM Specific: Clear end point specific register */
+ if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
+ if (mReq->req.udc_priv & MSM_SPS_MODE) {
+ val = hw_cread(CAP_ENDPTPIPEID +
+ mEp->num * sizeof(u32),
+ ~0);
+
+ if (val != MSM_EP_PIPE_ID_RESET_VAL)
+ hw_cwrite(
+ CAP_ENDPTPIPEID +
+ mEp->num * sizeof(u32),
+ ~0, MSM_EP_PIPE_ID_RESET_VAL);
+ }
+ }
+ mReq->req.status = -ESHUTDOWN;
+
+ if (mReq->map) {
+ dma_unmap_single(mEp->device, mReq->req.dma,
+ mReq->req.length,
+ mEp->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ mReq->req.dma = DMA_ADDR_INVALID;
+ mReq->map = 0;
+ }
+
+ if (mEp->multi_req) {
+ restore_original_req(mReq);
+ mEp->multi_req = false;
+ }
+
+ if (mReq->req.complete != NULL) {
+ spin_unlock(mEp->lock);
+ if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) &&
+ mReq->req.length)
+ mEpTemp = &_udc->ep0in;
+ mReq->req.complete(&mEpTemp->ep, &mReq->req);
+ if (mEp->type == USB_ENDPOINT_XFER_CONTROL)
+ mReq->req.complete = NULL;
+ spin_lock(mEp->lock);
+ }
+ }
+ return 0;
+}
+
+/**
+ * _gadget_stop_activity: stops all USB activity, flushes & disables all endpts
+ * @gadget: gadget
+ *
+ * This function returns an error code
+ */
+static int _gadget_stop_activity(struct usb_gadget *gadget)
+{
+ struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget);
+ unsigned long flags;
+
+ trace("%p", gadget);
+
+ if (gadget == NULL)
+ return -EINVAL;
+
+ spin_lock_irqsave(udc->lock, flags);
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->remote_wakeup = 0;
+ udc->suspended = 0;
+ udc->configured = 0;
+ spin_unlock_irqrestore(udc->lock, flags);
+
+ gadget->b_hnp_enable = 0;
+ gadget->a_hnp_support = 0;
+ gadget->host_request = 0;
+ gadget->otg_srp_reqd = 0;
+
+ udc->driver->disconnect(gadget);
+
+ spin_lock_irqsave(udc->lock, flags);
+ _ep_nuke(&udc->ep0out);
+ _ep_nuke(&udc->ep0in);
+ spin_unlock_irqrestore(udc->lock, flags);
+
+ if (udc->ep0in.last_zptr) {
+ dma_pool_free(udc->ep0in.td_pool, udc->ep0in.last_zptr,
+ udc->ep0in.last_zdma);
+ udc->ep0in.last_zptr = NULL;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ * ISR block
+ *****************************************************************************/
+/**
+ * isr_reset_handler: USB reset interrupt handler
+ * @udc: UDC device
+ *
+ * This function resets USB engine after a bus reset occurred
+ */
+static void isr_reset_handler(struct ci13xxx *udc)
+__releases(udc->lock)
+__acquires(udc->lock)
+{
+ int retval;
+
+ trace("%p", udc);
+
+ if (udc == NULL) {
+ err("EINVAL");
+ return;
+ }
+
+ dbg_event(0xFF, "BUS RST", 0);
+
+ spin_unlock(udc->lock);
+
+ if (udc->suspended) {
+ if (udc->udc_driver->notify_event)
+ udc->udc_driver->notify_event(udc,
+ CI13XXX_CONTROLLER_RESUME_EVENT);
+ if (udc->transceiver)
+ usb_phy_set_suspend(udc->transceiver, 0);
+ udc->driver->resume(&udc->gadget);
+ udc->suspended = 0;
+ }
+
+ /*stop charging upon reset */
+ if (udc->transceiver)
+ usb_phy_set_power(udc->transceiver, 100);
+
+ retval = _gadget_stop_activity(&udc->gadget);
+ if (retval)
+ goto done;
+
+ _udc->skip_flush = false;
+ retval = hw_usb_reset();
+ if (retval)
+ goto done;
+
+ spin_lock(udc->lock);
+
+ done:
+ if (retval)
+ err("error: %i", retval);
+}
+
+/**
+ * isr_resume_handler: USB PCI interrupt handler
+ * @udc: UDC device
+ *
+ */
+static void isr_resume_handler(struct ci13xxx *udc)
+{
+ udc->gadget.speed = hw_port_is_high_speed() ?
+ USB_SPEED_HIGH : USB_SPEED_FULL;
+ if (udc->suspended) {
+ spin_unlock(udc->lock);
+ if (udc->udc_driver->notify_event)
+ udc->udc_driver->notify_event(udc,
+ CI13XXX_CONTROLLER_RESUME_EVENT);
+ if (udc->transceiver)
+ usb_phy_set_suspend(udc->transceiver, 0);
+ udc->driver->resume(&udc->gadget);
+ spin_lock(udc->lock);
+ udc->suspended = 0;
+ }
+}
+
+/**
+ * isr_resume_handler: USB SLI interrupt handler
+ * @udc: UDC device
+ *
+ */
+static void isr_suspend_handler(struct ci13xxx *udc)
+{
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN &&
+ udc->vbus_active) {
+ if (udc->suspended == 0) {
+ spin_unlock(udc->lock);
+ udc->driver->suspend(&udc->gadget);
+ if (udc->udc_driver->notify_event)
+ udc->udc_driver->notify_event(udc,
+ CI13XXX_CONTROLLER_SUSPEND_EVENT);
+ if (udc->transceiver)
+ usb_phy_set_suspend(udc->transceiver, 1);
+ spin_lock(udc->lock);
+ udc->suspended = 1;
+ }
+ }
+}
+
+/**
+ * isr_get_status_complete: get_status request complete function
+ * @ep: endpoint
+ * @req: request handled
+ *
+ * Caller must release lock
+ */
+static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ trace("%p, %p", ep, req);
+
+ if (ep == NULL || req == NULL) {
+ err("EINVAL");
+ return;
+ }
+
+ if (req->status)
+ err("GET_STATUS failed");
+}
+
+/**
+ * isr_get_status_response: get_status request response
+ * @udc: udc struct
+ * @setup: setup request packet
+ *
+ * This function returns an error code
+ */
+static int isr_get_status_response(struct ci13xxx *udc,
+ struct usb_ctrlrequest *setup)
+__releases(mEp->lock)
+__acquires(mEp->lock)
+{
+ struct ci13xxx_ep *mEp = &udc->ep0in;
+ struct usb_request *req = udc->status;
+ int dir, num, retval;
+
+ trace("%p, %p", mEp, setup);
+
+ if (mEp == NULL || setup == NULL)
+ return -EINVAL;
+
+ req->complete = isr_get_status_complete;
+ req->length = 2;
+ req->buf = udc->status_buf;
+
+ if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
+ if (setup->wIndex == OTG_STATUS_SELECTOR) {
+ *((u8 *)req->buf) = _udc->gadget.host_request <<
+ HOST_REQUEST_FLAG;
+ req->length = 1;
+ } else {
+ /* Assume that device is bus powered for now. */
+ *((u16 *)req->buf) = _udc->remote_wakeup << 1;
+ }
+ /* TODO: D1 - Remote Wakeup; D0 - Self Powered */
+ retval = 0;
+ } else if ((setup->bRequestType & USB_RECIP_MASK) \
+ == USB_RECIP_ENDPOINT) {
+ dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ?
+ TX : RX;
+ num = le16_to_cpu(setup->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+ *((u16 *)req->buf) = hw_ep_get_halt(num, dir);
+ }
+ /* else do nothing; reserved for future use */
+
+ spin_unlock(mEp->lock);
+ retval = usb_ep_queue(&mEp->ep, req, GFP_ATOMIC);
+ spin_lock(mEp->lock);
+ return retval;
+}
+
+/**
+ * isr_setup_status_complete: setup_status request complete function
+ * @ep: endpoint
+ * @req: request handled
+ *
+ * Caller must release lock. Put the port in test mode if test mode
+ * feature is selected.
+ */
+static void
+isr_setup_status_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct ci13xxx *udc = req->context;
+ unsigned long flags;
+
+ trace("%p, %p", ep, req);
+
+ spin_lock_irqsave(udc->lock, flags);
+ if (udc->test_mode)
+ hw_port_test_set(udc->test_mode);
+ spin_unlock_irqrestore(udc->lock, flags);
+}
+
+/**
+ * isr_setup_status_phase: queues the status phase of a setup transation
+ * @udc: udc struct
+ *
+ * This function returns an error code
+ */
+static int isr_setup_status_phase(struct ci13xxx *udc)
+__releases(mEp->lock)
+__acquires(mEp->lock)
+{
+ int retval;
+ struct ci13xxx_ep *mEp;
+
+ trace("%p", udc);
+
+ mEp = (udc->ep0_dir == TX) ? &udc->ep0out : &udc->ep0in;
+ udc->status->context = udc;
+ udc->status->complete = isr_setup_status_complete;
+ udc->status->length = 0;
+
+ spin_unlock(mEp->lock);
+ retval = usb_ep_queue(&mEp->ep, udc->status, GFP_ATOMIC);
+ spin_lock(mEp->lock);
+
+ return retval;
+}
+
+/**
+ * isr_tr_complete_low: transaction complete low level handler
+ * @mEp: endpoint
+ *
+ * This function returns an error code
+ * Caller must hold lock
+ */
+static int isr_tr_complete_low(struct ci13xxx_ep *mEp)
+__releases(mEp->lock)
+__acquires(mEp->lock)
+{
+ struct ci13xxx_req *mReq, *mReqTemp;
+ struct ci13xxx_ep *mEpTemp = mEp;
+ int uninitialized_var(retval);
+ int req_dequeue = 1;
+ struct ci13xxx *udc = _udc;
+
+ trace("%p", mEp);
+
+ if (list_empty(&mEp->qh.queue))
+ return 0;
+
+ del_timer(&mEp->prime_timer);
+ mEp->prime_timer_count = 0;
+ list_for_each_entry_safe(mReq, mReqTemp, &mEp->qh.queue,
+ queue) {
+dequeue:
+ retval = _hardware_dequeue(mEp, mReq);
+ if (retval < 0) {
+ /*
+ * FIXME: don't know exact delay
+ * required for HW to update dTD status
+ * bits. This is a temporary workaround till
+ * HW designers come back on this.
+ */
+ if (retval == -EBUSY && req_dequeue &&
+ (mEp->dir == 0 || mEp->num == 0)) {
+ req_dequeue = 0;
+ udc->dTD_update_fail_count++;
+ mEp->dTD_update_fail_count++;
+ udelay(10);
+ goto dequeue;
+ }
+ break;
+ }
+ req_dequeue = 0;
+
+ if (mEp->multi_req) { /* Large request in progress */
+ unsigned remain_len;
+
+ mReq->multi.actual += mReq->req.actual;
+ remain_len = mReq->multi.len - mReq->multi.actual;
+ if (mReq->req.status || !remain_len ||
+ (mReq->req.actual != mReq->req.length)) {
+ restore_original_req(mReq);
+ mEp->multi_req = false;
+ } else {
+ mReq->req.buf = mReq->multi.buf +
+ mReq->multi.actual;
+ mReq->req.length = min_t(unsigned, remain_len,
+ (4 * CI13XXX_PAGE_SIZE));
+
+ mReq->req.status = -EINPROGRESS;
+ mReq->req.actual = 0;
+ list_del_init(&mReq->queue);
+ retval = _hardware_enqueue(mEp, mReq);
+ if (retval) {
+ err("Large req failed in middle");
+ mReq->req.status = retval;
+ restore_original_req(mReq);
+ mEp->multi_req = false;
+ goto done;
+ } else {
+ list_add_tail(&mReq->queue,
+ &mEp->qh.queue);
+ return 0;
+ }
+ }
+ }
+ list_del_init(&mReq->queue);
+done:
+
+ dbg_done(_usb_addr(mEp), mReq->ptr->token, retval);
+
+ if (mReq->req.complete != NULL) {
+ spin_unlock(mEp->lock);
+ if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) &&
+ mReq->req.length)
+ mEpTemp = &_udc->ep0in;
+ mReq->req.complete(&mEpTemp->ep, &mReq->req);
+ spin_lock(mEp->lock);
+ }
+ }
+
+ if (retval == -EBUSY)
+ retval = 0;
+ if (retval < 0)
+ dbg_event(_usb_addr(mEp), "DONE", retval);
+
+ return retval;
+}
+
+/**
+ * isr_tr_complete_handler: transaction complete interrupt handler
+ * @udc: UDC descriptor
+ *
+ * This function handles traffic events
+ */
+static void isr_tr_complete_handler(struct ci13xxx *udc)
+__releases(udc->lock)
+__acquires(udc->lock)
+{
+ unsigned i;
+ u8 tmode = 0;
+
+ trace("%p", udc);
+
+ if (udc == NULL) {
+ err("EINVAL");
+ return;
+ }
+
+ for (i = 0; i < hw_ep_max; i++) {
+ struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i];
+ int type, num, dir, err = -EINVAL;
+ struct usb_ctrlrequest req;
+
+ if (mEp->desc == NULL)
+ continue; /* not configured */
+
+ if (hw_test_and_clear_complete(i)) {
+ err = isr_tr_complete_low(mEp);
+ if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
+ if (err > 0) /* needs status phase */
+ err = isr_setup_status_phase(udc);
+ if (err < 0) {
+ dbg_event(_usb_addr(mEp),
+ "ERROR", err);
+ spin_unlock(udc->lock);
+ if (usb_ep_set_halt(&mEp->ep))
+ err("error: ep_set_halt");
+ spin_lock(udc->lock);
+ }
+ }
+ }
+
+ if (mEp->type != USB_ENDPOINT_XFER_CONTROL ||
+ !hw_test_and_clear_setup_status(i))
+ continue;
+
+ if (i != 0) {
+ warn("ctrl traffic received at endpoint");
+ continue;
+ }
+
+ /*
+ * Flush data and handshake transactions of previous
+ * setup packet.
+ */
+ _ep_nuke(&udc->ep0out);
+ _ep_nuke(&udc->ep0in);
+
+ /* read_setup_packet */
+ do {
+ hw_test_and_set_setup_guard();
+ memcpy(&req, &mEp->qh.ptr->setup, sizeof(req));
+ /* Ensure buffer is read before acknowledging to h/w */
+ mb();
+ } while (!hw_test_and_clear_setup_guard());
+
+ type = req.bRequestType;
+
+ udc->ep0_dir = (type & USB_DIR_IN) ? TX : RX;
+
+ dbg_setup(_usb_addr(mEp), &req);
+
+ switch (req.bRequest) {
+ case USB_REQ_CLEAR_FEATURE:
+ if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
+ le16_to_cpu(req.wValue) ==
+ USB_ENDPOINT_HALT) {
+ if (req.wLength != 0)
+ break;
+ num = le16_to_cpu(req.wIndex);
+ dir = num & USB_ENDPOINT_DIR_MASK;
+ num &= USB_ENDPOINT_NUMBER_MASK;
+ if (dir) /* TX */
+ num += hw_ep_max/2;
+ if (!udc->ci13xxx_ep[num].wedge) {
+ spin_unlock(udc->lock);
+ err = usb_ep_clear_halt(
+ &udc->ci13xxx_ep[num].ep);
+ spin_lock(udc->lock);
+ if (err)
+ break;
+ }
+ err = isr_setup_status_phase(udc);
+ } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) &&
+ le16_to_cpu(req.wValue) ==
+ USB_DEVICE_REMOTE_WAKEUP) {
+ if (req.wLength != 0)
+ break;
+ udc->remote_wakeup = 0;
+ err = isr_setup_status_phase(udc);
+ } else {
+ goto delegate;
+ }
+ break;
+ case USB_REQ_GET_STATUS:
+ if (type != (USB_DIR_IN|USB_RECIP_DEVICE) &&
+ type != (USB_DIR_IN|USB_RECIP_ENDPOINT) &&
+ type != (USB_DIR_IN|USB_RECIP_INTERFACE))
+ goto delegate;
+ if (le16_to_cpu(req.wValue) != 0)
+ break;
+ err = isr_get_status_response(udc, &req);
+ break;
+ case USB_REQ_SET_ADDRESS:
+ if (type != (USB_DIR_OUT|USB_RECIP_DEVICE))
+ goto delegate;
+ if (le16_to_cpu(req.wLength) != 0 ||
+ le16_to_cpu(req.wIndex) != 0)
+ break;
+ err = hw_usb_set_address((u8)le16_to_cpu(req.wValue));
+ if (err)
+ break;
+ err = isr_setup_status_phase(udc);
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ if (type == (USB_DIR_OUT|USB_TYPE_STANDARD))
+ udc->configured = !!req.wValue;
+ goto delegate;
+ case USB_REQ_SET_FEATURE:
+ if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
+ le16_to_cpu(req.wValue) ==
+ USB_ENDPOINT_HALT) {
+ if (req.wLength != 0)
+ break;
+ num = le16_to_cpu(req.wIndex);
+ dir = num & USB_ENDPOINT_DIR_MASK;
+ num &= USB_ENDPOINT_NUMBER_MASK;
+ if (dir) /* TX */
+ num += hw_ep_max/2;
+
+ spin_unlock(udc->lock);
+ err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep);
+ spin_lock(udc->lock);
+ if (!err)
+ isr_setup_status_phase(udc);
+ } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) {
+ if (req.wLength != 0)
+ break;
+ switch (le16_to_cpu(req.wValue)) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ udc->remote_wakeup = 1;
+ err = isr_setup_status_phase(udc);
+ break;
+ case USB_DEVICE_B_HNP_ENABLE:
+ udc->gadget.b_hnp_enable = 1;
+ err = isr_setup_status_phase(udc);
+ break;
+ case USB_DEVICE_A_HNP_SUPPORT:
+ udc->gadget.a_hnp_support = 1;
+ err = isr_setup_status_phase(udc);
+ break;
+ case USB_DEVICE_A_ALT_HNP_SUPPORT:
+ break;
+ case USB_DEVICE_TEST_MODE:
+ tmode = le16_to_cpu(req.wIndex) >> 8;
+ switch (tmode) {
+ case TEST_J:
+ case TEST_K:
+ case TEST_SE0_NAK:
+ case TEST_PACKET:
+ case TEST_FORCE_EN:
+ udc->test_mode = tmode;
+ err = isr_setup_status_phase(
+ udc);
+ break;
+ case TEST_OTG_SRP_REQD:
+ udc->gadget.otg_srp_reqd = 1;
+ err = isr_setup_status_phase(
+ udc);
+ break;
+ case TEST_OTG_HNP_REQD:
+ udc->gadget.host_request = 1;
+ err = isr_setup_status_phase(
+ udc);
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+ } else {
+ goto delegate;
+ }
+ break;
+ default:
+delegate:
+ if (req.wLength == 0) /* no data phase */
+ udc->ep0_dir = TX;
+
+ spin_unlock(udc->lock);
+ err = udc->driver->setup(&udc->gadget, &req);
+ spin_lock(udc->lock);
+ break;
+ }
+
+ if (err < 0) {
+ dbg_event(_usb_addr(mEp), "ERROR", err);
+
+ spin_unlock(udc->lock);
+ if (usb_ep_set_halt(&mEp->ep))
+ err("error: ep_set_halt");
+ spin_lock(udc->lock);
+ }
+ }
+}
+
+/******************************************************************************
+ * ENDPT block
+ *****************************************************************************/
+/**
+ * ep_enable: configure endpoint, making it usable
+ *
+ * Check usb_ep_enable() at "usb_gadget.h" for details
+ */
+static int ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
+ int retval = 0;
+ unsigned long flags;
+ unsigned mult = 0;
+
+ trace("ep = %p, desc = %p", ep, desc);
+
+ if (ep == NULL || desc == NULL)
+ return -EINVAL;
+
+ spin_lock_irqsave(mEp->lock, flags);
+
+ /* only internal SW should enable ctrl endpts */
+
+ mEp->desc = desc;
+
+ if (!list_empty(&mEp->qh.queue))
+ warn("enabling a non-empty endpoint!");
+
+ mEp->dir = usb_endpoint_dir_in(desc) ? TX : RX;
+ mEp->num = usb_endpoint_num(desc);
+ mEp->type = usb_endpoint_type(desc);
+
+ mEp->ep.maxpacket = usb_endpoint_maxp(desc);
+
+ dbg_event(_usb_addr(mEp), "ENABLE", 0);
+
+ mEp->qh.ptr->cap = 0;
+
+ if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
+ mEp->qh.ptr->cap |= QH_IOS;
+ } else if (mEp->type == USB_ENDPOINT_XFER_ISOC) {
+ mEp->qh.ptr->cap &= ~QH_MULT;
+ mult = ((mEp->ep.maxpacket >> QH_MULT_SHIFT) + 1) & 0x03;
+ mEp->qh.ptr->cap |= (mult << ffs_nr(QH_MULT));
+ } else {
+ mEp->qh.ptr->cap |= QH_ZLT;
+ }
+
+ mEp->qh.ptr->cap |=
+ (mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT;
+ mEp->qh.ptr->td.next |= TD_TERMINATE; /* needed? */
+
+ /* complete all the updates to ept->head before enabling endpoint*/
+ mb();
+
+ /*
+ * Enable endpoints in the HW other than ep0 as ep0
+ * is always enabled
+ */
+ if (mEp->num)
+ retval |= hw_ep_enable(mEp->num, mEp->dir, mEp->type);
+
+ spin_unlock_irqrestore(mEp->lock, flags);
+ return retval;
+}
+
+/**
+ * ep_disable: endpoint is no longer usable
+ *
+ * Check usb_ep_disable() at "usb_gadget.h" for details
+ */
+static int ep_disable(struct usb_ep *ep)
+{
+ struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
+ int direction, retval = 0;
+ unsigned long flags;
+
+ trace("%p", ep);
+
+ if (ep == NULL)
+ return -EINVAL;
+ else if (mEp->desc == NULL)
+ return -EBUSY;
+
+ spin_lock_irqsave(mEp->lock, flags);
+
+ /* only internal SW should disable ctrl endpts */
+
+ direction = mEp->dir;
+ do {
+ dbg_event(_usb_addr(mEp), "DISABLE", 0);
+
+ retval |= _ep_nuke(mEp);
+ retval |= hw_ep_disable(mEp->num, mEp->dir);
+
+ if (mEp->type == USB_ENDPOINT_XFER_CONTROL)
+ mEp->dir = (mEp->dir == TX) ? RX : TX;
+
+ } while (mEp->dir != direction);
+
+ if (mEp->last_zptr) {
+ dma_pool_free(mEp->td_pool, mEp->last_zptr,
+ mEp->last_zdma);
+ mEp->last_zptr = NULL;
+ }
+
+ mEp->desc = NULL;
+ mEp->ep.desc = NULL;
+ mEp->ep.maxpacket = USHRT_MAX;
+
+ spin_unlock_irqrestore(mEp->lock, flags);
+ return retval;
+}
+
+/**
+ * ep_alloc_request: allocate a request object to use with this endpoint
+ *
+ * Check usb_ep_alloc_request() at "usb_gadget.h" for details
+ */
+static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
+{
+ struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
+ struct ci13xxx_req *mReq = NULL;
+
+ trace("%p, %i", ep, gfp_flags);
+
+ if (ep == NULL) {
+ err("EINVAL");
+ return NULL;
+ }
+
+ mReq = kzalloc(sizeof(struct ci13xxx_req), gfp_flags);
+ if (mReq != NULL) {
+ INIT_LIST_HEAD(&mReq->queue);
+ mReq->req.dma = DMA_ADDR_INVALID;
+
+ mReq->ptr = dma_pool_alloc(mEp->td_pool, gfp_flags,
+ &mReq->dma);
+ if (mReq->ptr == NULL) {
+ kfree(mReq);
+ mReq = NULL;
+ }
+ }
+
+ dbg_event(_usb_addr(mEp), "ALLOC", mReq == NULL);
+
+ return (mReq == NULL) ? NULL : &mReq->req;
+}
+
+/**
+ * ep_free_request: frees a request object
+ *
+ * Check usb_ep_free_request() at "usb_gadget.h" for details
+ */
+static void ep_free_request(struct usb_ep *ep, struct usb_request *req)
+{
+ struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
+ struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req);
+ unsigned long flags;
+
+ trace("%p, %p", ep, req);
+
+ if (ep == NULL || req == NULL) {
+ err("EINVAL");
+ return;
+ } else if (!list_empty(&mReq->queue)) {
+ err("EBUSY");
+ return;
+ }
+
+ spin_lock_irqsave(mEp->lock, flags);
+
+ if (mReq->ptr)
+ dma_pool_free(mEp->td_pool, mReq->ptr, mReq->dma);
+ kfree(mReq);
+
+ dbg_event(_usb_addr(mEp), "FREE", 0);
+
+ spin_unlock_irqrestore(mEp->lock, flags);
+}
+
+/**
+ * ep_queue: queues (submits) an I/O request to an endpoint
+ *
+ * Check usb_ep_queue()* at usb_gadget.h" for details
+ */
+static int ep_queue(struct usb_ep *ep, struct usb_request *req,
+ gfp_t __maybe_unused gfp_flags)
+{
+ struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
+ struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req);
+ int retval = 0;
+ unsigned long flags;
+ struct ci13xxx *udc = _udc;
+
+ trace("%p, %p, %X", ep, req, gfp_flags);
+
+ spin_lock_irqsave(mEp->lock, flags);
+ if (ep == NULL || req == NULL || mEp->desc == NULL) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+ if (!udc->softconnect) {
+ retval = -ENODEV;
+ goto done;
+ }
+
+ if (!udc->configured && mEp->type !=
+ USB_ENDPOINT_XFER_CONTROL) {
+ trace("usb is not configured"
+ "ept #%d, ept name#%s\n",
+ mEp->num, mEp->ep.name);
+ retval = -ESHUTDOWN;
+ goto done;
+ }
+
+ if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
+ if (req->length)
+ mEp = (_udc->ep0_dir == RX) ?
+ &_udc->ep0out : &_udc->ep0in;
+ if (!list_empty(&mEp->qh.queue)) {
+ _ep_nuke(mEp);
+ retval = -EOVERFLOW;
+ warn("endpoint ctrl %X nuked", _usb_addr(mEp));
+ }
+ }
+
+ /* first nuke then test link, e.g. previous status has not sent */
+ if (!list_empty(&mReq->queue)) {
+ retval = -EBUSY;
+ err("request already in queue");
+ goto done;
+ }
+ if (mEp->multi_req) {
+ retval = -EAGAIN;
+ err("Large request is in progress. come again");
+ goto done;
+ }
+
+ if (req->length > (4 * CI13XXX_PAGE_SIZE)) {
+ if (!list_empty(&mEp->qh.queue)) {
+ retval = -EAGAIN;
+ err("Queue is busy. Large req is not allowed");
+ goto done;
+ }
+ if ((mEp->type != USB_ENDPOINT_XFER_BULK) ||
+ (mEp->dir != RX)) {
+ retval = -EINVAL;
+ err("Larger req is supported only for Bulk OUT");
+ goto done;
+ }
+ mEp->multi_req = true;
+ mReq->multi.len = req->length;
+ mReq->multi.buf = req->buf;
+ req->length = (4 * CI13XXX_PAGE_SIZE);
+ }
+
+ dbg_queue(_usb_addr(mEp), req, retval);
+
+ /* push request */
+ mReq->req.status = -EINPROGRESS;
+ mReq->req.actual = 0;
+
+ retval = _hardware_enqueue(mEp, mReq);
+
+ if (retval == -EALREADY) {
+ dbg_event(_usb_addr(mEp), "QUEUE", retval);
+ retval = 0;
+ }
+ if (!retval)
+ list_add_tail(&mReq->queue, &mEp->qh.queue);
+ else if (mEp->multi_req)
+ mEp->multi_req = false;
+
+ done:
+ spin_unlock_irqrestore(mEp->lock, flags);
+ return retval;
+}
+
+/**
+ * ep_dequeue: dequeues (cancels, unlinks) an I/O request from an endpoint
+ *
+ * Check usb_ep_dequeue() at "usb_gadget.h" for details
+ */
+static int ep_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+ struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
+ struct ci13xxx_ep *mEpTemp = mEp;
+ struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req);
+ unsigned long flags;
+
+ trace("%p, %p", ep, req);
+
+ spin_lock_irqsave(mEp->lock, flags);
+ /*
+ * Only ep0 IN is exposed to composite. When a req is dequeued
+ * on ep0, check both ep0 IN and ep0 OUT queues.
+ */
+ if (ep == NULL || req == NULL || mReq->req.status != -EALREADY ||
+ mEp->desc == NULL || list_empty(&mReq->queue) ||
+ (list_empty(&mEp->qh.queue) && ((mEp->type !=
+ USB_ENDPOINT_XFER_CONTROL) ||
+ list_empty(&_udc->ep0out.qh.queue)))) {
+ spin_unlock_irqrestore(mEp->lock, flags);
+ return -EINVAL;
+ }
+
+ dbg_event(_usb_addr(mEp), "DEQUEUE", 0);
+
+ if ((mEp->type == USB_ENDPOINT_XFER_CONTROL)) {
+ hw_ep_flush(_udc->ep0out.num, RX);
+ hw_ep_flush(_udc->ep0in.num, TX);
+ } else {
+ hw_ep_flush(mEp->num, mEp->dir);
+ }
+
+ /* pop request */
+ list_del_init(&mReq->queue);
+ if (mReq->map) {
+ dma_unmap_single(mEp->device, mReq->req.dma, mReq->req.length,
+ mEp->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ mReq->req.dma = DMA_ADDR_INVALID;
+ mReq->map = 0;
+ }
+ req->status = -ECONNRESET;
+ if (mEp->multi_req) {
+ restore_original_req(mReq);
+ mEp->multi_req = false;
+ }
+
+ if (mReq->req.complete != NULL) {
+ spin_unlock(mEp->lock);
+ if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) &&
+ mReq->req.length)
+ mEpTemp = &_udc->ep0in;
+ mReq->req.complete(&mEpTemp->ep, &mReq->req);
+ if (mEp->type == USB_ENDPOINT_XFER_CONTROL)
+ mReq->req.complete = NULL;
+ spin_lock(mEp->lock);
+ }
+
+ spin_unlock_irqrestore(mEp->lock, flags);
+ return 0;
+}
+
+static int is_sps_req(struct ci13xxx_req *mReq)
+{
+ return (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID &&
+ mReq->req.udc_priv & MSM_SPS_MODE);
+}
+
+/**
+ * ep_set_halt: sets the endpoint halt feature
+ *
+ * Check usb_ep_set_halt() at "usb_gadget.h" for details
+ */
+static int ep_set_halt(struct usb_ep *ep, int value)
+{
+ struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
+ int direction, retval = 0;
+ unsigned long flags;
+
+ trace("%p, %i", ep, value);
+
+ if (ep == NULL || mEp->desc == NULL)
+ return -EINVAL;
+
+ spin_lock_irqsave(mEp->lock, flags);
+
+#ifndef STALL_IN
+ /* g_file_storage MS compliant but g_zero fails chapter 9 compliance */
+ if (value && mEp->type == USB_ENDPOINT_XFER_BULK && mEp->dir == TX &&
+ !list_empty(&mEp->qh.queue) &&
+ !is_sps_req(list_entry(mEp->qh.queue.next, struct ci13xxx_req,
+ queue))){
+ spin_unlock_irqrestore(mEp->lock, flags);
+ return -EAGAIN;
+ }
+#endif
+
+ direction = mEp->dir;
+ do {
+ dbg_event(_usb_addr(mEp), "HALT", value);
+ retval |= hw_ep_set_halt(mEp->num, mEp->dir, value);
+
+ if (!value)
+ mEp->wedge = 0;
+
+ if (mEp->type == USB_ENDPOINT_XFER_CONTROL)
+ mEp->dir = (mEp->dir == TX) ? RX : TX;
+
+ } while (mEp->dir != direction);
+
+ spin_unlock_irqrestore(mEp->lock, flags);
+ return retval;
+}
+
+/**
+ * ep_set_wedge: sets the halt feature and ignores clear requests
+ *
+ * Check usb_ep_set_wedge() at "usb_gadget.h" for details
+ */
+static int ep_set_wedge(struct usb_ep *ep)
+{
+ struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
+ unsigned long flags;
+
+ trace("%p", ep);
+
+ if (ep == NULL || mEp->desc == NULL)
+ return -EINVAL;
+
+ spin_lock_irqsave(mEp->lock, flags);
+
+ dbg_event(_usb_addr(mEp), "WEDGE", 0);
+ mEp->wedge = 1;
+
+ spin_unlock_irqrestore(mEp->lock, flags);
+
+ return usb_ep_set_halt(ep);
+}
+
+/**
+ * ep_fifo_flush: flushes contents of a fifo
+ *
+ * Check usb_ep_fifo_flush() at "usb_gadget.h" for details
+ */
+static void ep_fifo_flush(struct usb_ep *ep)
+{
+ struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
+ unsigned long flags;
+
+ trace("%p", ep);
+
+ if (ep == NULL) {
+ err("%02X: -EINVAL", _usb_addr(mEp));
+ return;
+ }
+
+ spin_lock_irqsave(mEp->lock, flags);
+
+ dbg_event(_usb_addr(mEp), "FFLUSH", 0);
+ /*
+ * _ep_nuke() takes care of flushing the endpoint.
+ * some function drivers expect udc to retire all
+ * pending requests upon flushing an endpoint. There
+ * is no harm in doing it.
+ */
+ _ep_nuke(mEp);
+
+ spin_unlock_irqrestore(mEp->lock, flags);
+}
+
+/**
+ * Endpoint-specific part of the API to the USB controller hardware
+ * Check "usb_gadget.h" for details
+ */
+static const struct usb_ep_ops usb_ep_ops = {
+ .enable = ep_enable,
+ .disable = ep_disable,
+ .alloc_request = ep_alloc_request,
+ .free_request = ep_free_request,
+ .queue = ep_queue,
+ .dequeue = ep_dequeue,
+ .set_halt = ep_set_halt,
+ .set_wedge = ep_set_wedge,
+ .fifo_flush = ep_fifo_flush,
+};
+
+/******************************************************************************
+ * GADGET block
+ *****************************************************************************/
+static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+ struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
+ unsigned long flags;
+ int gadget_ready = 0;
+
+ if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS))
+ return -EOPNOTSUPP;
+
+ spin_lock_irqsave(udc->lock, flags);
+ udc->vbus_active = is_active;
+ if (udc->driver)
+ gadget_ready = 1;
+ spin_unlock_irqrestore(udc->lock, flags);
+
+ if (gadget_ready) {
+ if (is_active) {
+ pm_runtime_get_sync(&_gadget->dev);
+ hw_device_reset(udc);
+ if (udc->softconnect)
+ hw_device_state(udc->ep0out.qh.dma);
+ } else {
+ hw_device_state(0);
+ _gadget_stop_activity(&udc->gadget);
+ if (udc->udc_driver->notify_event)
+ udc->udc_driver->notify_event(udc,
+ CI13XXX_CONTROLLER_DISCONNECT_EVENT);
+ pm_runtime_put_sync(&_gadget->dev);
+ }
+ }
+
+ return 0;
+}
+
+static int ci13xxx_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
+{
+ struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
+
+ if (udc->transceiver)
+ return usb_phy_set_power(udc->transceiver, mA);
+ return -ENOTSUPP;
+}
+
+static int ci13xxx_pullup(struct usb_gadget *_gadget, int is_active)
+{
+ struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(udc->lock, flags);
+ udc->softconnect = is_active;
+ if (((udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) &&
+ !udc->vbus_active) || !udc->driver) {
+ spin_unlock_irqrestore(udc->lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(udc->lock, flags);
+
+ if (is_active)
+ hw_device_state(udc->ep0out.qh.dma);
+ else
+ hw_device_state(0);
+
+ return 0;
+}
+
+static int ci13xxx_start(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *));
+static int ci13xxx_stop(struct usb_gadget_driver *driver);
+
+/**
+ * Device operations part of the API to the USB controller hardware,
+ * which don't involve endpoints (or i/o)
+ * Check "usb_gadget.h" for details
+ */
+static const struct usb_gadget_ops usb_gadget_ops = {
+ .vbus_session = ci13xxx_vbus_session,
+ .wakeup = ci13xxx_wakeup,
+ .vbus_draw = ci13xxx_vbus_draw,
+ .pullup = ci13xxx_pullup,
+ .start = ci13xxx_start,
+ .stop = ci13xxx_stop,
+};
+
+/**
+ * ci13xxx_start: register a gadget driver
+ * @driver: the driver being registered
+ * @bind: the driver's bind callback
+ *
+ * Check ci13xxx_start() at <linux/usb/gadget.h> for details.
+ * Interrupts are enabled here.
+ */
+static int ci13xxx_start(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *))
+{
+ struct ci13xxx *udc = _udc;
+ unsigned long flags;
+ int i, j;
+ int retval = -ENOMEM;
+ bool put = false;
+
+ trace("%p", driver);
+
+ if (driver == NULL ||
+ bind == NULL ||
+ driver->setup == NULL ||
+ driver->disconnect == NULL)
+ return -EINVAL;
+ else if (udc == NULL)
+ return -ENODEV;
+ else if (udc->driver != NULL)
+ return -EBUSY;
+
+ /* alloc resources */
+ udc->qh_pool = dma_pool_create("ci13xxx_qh", &udc->gadget.dev,
+ sizeof(struct ci13xxx_qh),
+ 64, CI13XXX_PAGE_SIZE);
+ if (udc->qh_pool == NULL)
+ return -ENOMEM;
+
+ udc->td_pool = dma_pool_create("ci13xxx_td", &udc->gadget.dev,
+ sizeof(struct ci13xxx_td),
+ 64, CI13XXX_PAGE_SIZE);
+ if (udc->td_pool == NULL) {
+ dma_pool_destroy(udc->qh_pool);
+ udc->qh_pool = NULL;
+ return -ENOMEM;
+ }
+
+ spin_lock_irqsave(udc->lock, flags);
+
+ info("hw_ep_max = %d", hw_ep_max);
+
+ udc->gadget.dev.driver = NULL;
+
+ retval = 0;
+ for (i = 0; i < hw_ep_max/2; i++) {
+ for (j = RX; j <= TX; j++) {
+ int k = i + j * hw_ep_max/2;
+ struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[k];
+
+ scnprintf(mEp->name, sizeof(mEp->name), "ep%i%s", i,
+ (j == TX) ? "in" : "out");
+
+ mEp->lock = udc->lock;
+ mEp->device = &udc->gadget.dev;
+ mEp->td_pool = udc->td_pool;
+
+ mEp->ep.name = mEp->name;
+ mEp->ep.ops = &usb_ep_ops;
+ mEp->ep.maxpacket =
+ k ? USHRT_MAX : CTRL_PAYLOAD_MAX;
+
+ INIT_LIST_HEAD(&mEp->qh.queue);
+ spin_unlock_irqrestore(udc->lock, flags);
+ mEp->qh.ptr = dma_pool_alloc(udc->qh_pool, GFP_KERNEL,
+ &mEp->qh.dma);
+ spin_lock_irqsave(udc->lock, flags);
+ if (mEp->qh.ptr == NULL)
+ retval = -ENOMEM;
+ else
+ memset(mEp->qh.ptr, 0, sizeof(*mEp->qh.ptr));
+
+ /* skip ep0 out and in endpoints */
+ if (i == 0)
+ continue;
+
+ list_add_tail(&mEp->ep.ep_list, &udc->gadget.ep_list);
+ }
+ }
+ if (retval)
+ goto done;
+ spin_unlock_irqrestore(udc->lock, flags);
+ udc->ep0out.ep.desc = &ctrl_endpt_out_desc;
+ retval = usb_ep_enable(&udc->ep0out.ep);
+ if (retval)
+ return retval;
+
+ udc->ep0in.ep.desc = &ctrl_endpt_in_desc;
+ retval = usb_ep_enable(&udc->ep0in.ep);
+ if (retval)
+ return retval;
+ udc->status = usb_ep_alloc_request(&udc->ep0in.ep, GFP_KERNEL);
+ if (!udc->status)
+ return -ENOMEM;
+ udc->status_buf = kzalloc(2, GFP_KERNEL); /* for GET_STATUS */
+ if (!udc->status_buf) {
+ usb_ep_free_request(&udc->ep0in.ep, udc->status);
+ return -ENOMEM;
+ }
+ spin_lock_irqsave(udc->lock, flags);
+
+ udc->gadget.ep0 = &udc->ep0in.ep;
+ /* bind gadget */
+ driver->driver.bus = NULL;
+ udc->gadget.dev.driver = &driver->driver;
+ udc->softconnect = 1;
+
+ spin_unlock_irqrestore(udc->lock, flags);
+ pm_runtime_get_sync(&udc->gadget.dev);
+ retval = bind(&udc->gadget); /* MAY SLEEP */
+ spin_lock_irqsave(udc->lock, flags);
+
+ if (retval) {
+ udc->gadget.dev.driver = NULL;
+ goto done;
+ }
+
+ udc->driver = driver;
+ if (udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) {
+ if (udc->vbus_active) {
+ if (udc->udc_driver->flags & CI13XXX_REGS_SHARED)
+ hw_device_reset(udc);
+ } else {
+ put = true;
+ goto done;
+ }
+ }
+
+ if (!udc->softconnect) {
+ put = true;
+ goto done;
+ }
+
+ retval = hw_device_state(udc->ep0out.qh.dma);
+
+ done:
+ spin_unlock_irqrestore(udc->lock, flags);
+ if (retval || put)
+ pm_runtime_put_sync(&udc->gadget.dev);
+
+ if (udc->udc_driver->notify_event)
+ udc->udc_driver->notify_event(udc,
+ CI13XXX_CONTROLLER_UDC_STARTED_EVENT);
+
+ return retval;
+}
+
+/**
+ * ci13xxx_stop: unregister a gadget driver
+ *
+ * Check usb_gadget_unregister_driver() at "usb_gadget.h" for details
+ */
+static int ci13xxx_stop(struct usb_gadget_driver *driver)
+{
+ struct ci13xxx *udc = _udc;
+ unsigned long i, flags;
+
+ trace("%p", driver);
+
+ if (driver == NULL ||
+ driver->unbind == NULL ||
+ driver->setup == NULL ||
+ driver->disconnect == NULL ||
+ driver != udc->driver)
+ return -EINVAL;
+
+ spin_lock_irqsave(udc->lock, flags);
+
+ if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) ||
+ udc->vbus_active) {
+ hw_device_state(0);
+ spin_unlock_irqrestore(udc->lock, flags);
+ _gadget_stop_activity(&udc->gadget);
+ spin_lock_irqsave(udc->lock, flags);
+ pm_runtime_put(&udc->gadget.dev);
+ }
+
+ /* unbind gadget */
+ spin_unlock_irqrestore(udc->lock, flags);
+ driver->unbind(&udc->gadget); /* MAY SLEEP */
+ spin_lock_irqsave(udc->lock, flags);
+
+ usb_ep_free_request(&udc->ep0in.ep, udc->status);
+ kfree(udc->status_buf);
+
+ udc->gadget.dev.driver = NULL;
+
+ /* free resources */
+ for (i = 0; i < hw_ep_max; i++) {
+ struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i];
+
+ if (!list_empty(&mEp->ep.ep_list))
+ list_del_init(&mEp->ep.ep_list);
+
+ if (mEp->qh.ptr != NULL)
+ dma_pool_free(udc->qh_pool, mEp->qh.ptr, mEp->qh.dma);
+ }
+
+ udc->gadget.ep0 = NULL;
+ udc->driver = NULL;
+
+ spin_unlock_irqrestore(udc->lock, flags);
+
+ if (udc->td_pool != NULL) {
+ dma_pool_destroy(udc->td_pool);
+ udc->td_pool = NULL;
+ }
+ if (udc->qh_pool != NULL) {
+ dma_pool_destroy(udc->qh_pool);
+ udc->qh_pool = NULL;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ * BUS block
+ *****************************************************************************/
+/**
+ * udc_irq: global interrupt handler
+ *
+ * This function returns IRQ_HANDLED if the IRQ has been handled
+ * It locks access to registers
+ */
+static irqreturn_t udc_irq(void)
+{
+ struct ci13xxx *udc = _udc;
+ irqreturn_t retval;
+ u32 intr;
+
+ trace();
+
+ if (udc == NULL) {
+ err("ENODEV");
+ return IRQ_HANDLED;
+ }
+
+ spin_lock(udc->lock);
+
+ if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) {
+ if (hw_cread(CAP_USBMODE, USBMODE_CM) !=
+ USBMODE_CM_DEVICE) {
+ spin_unlock(udc->lock);
+ return IRQ_NONE;
+ }
+ }
+ intr = hw_test_and_clear_intr_active();
+ if (intr) {
+ isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intr;
+ isr_statistics.hndl.idx &= ISR_MASK;
+ isr_statistics.hndl.cnt++;
+
+ /* order defines priority - do NOT change it */
+ if (USBi_URI & intr) {
+ isr_statistics.uri++;
+ isr_reset_handler(udc);
+ }
+ if (USBi_PCI & intr) {
+ isr_statistics.pci++;
+ isr_resume_handler(udc);
+ }
+ if (USBi_UEI & intr)
+ isr_statistics.uei++;
+ if (USBi_UI & intr) {
+ isr_statistics.ui++;
+ isr_tr_complete_handler(udc);
+ }
+ if (USBi_SLI & intr) {
+ isr_suspend_handler(udc);
+ isr_statistics.sli++;
+ }
+ retval = IRQ_HANDLED;
+ } else {
+ isr_statistics.none++;
+ retval = IRQ_NONE;
+ }
+ spin_unlock(udc->lock);
+
+ return retval;
+}
+
+/**
+ * udc_release: driver release function
+ * @dev: device
+ *
+ * Currently does nothing
+ */
+static void udc_release(struct device *dev)
+{
+ trace("%p", dev);
+
+ if (dev == NULL)
+ err("EINVAL");
+}
+
+/**
+ * udc_probe: parent probe must call this to initialize UDC
+ * @dev: parent device
+ * @regs: registers base address
+ * @name: driver name
+ *
+ * This function returns an error code
+ * No interrupts active, the IRQ has not been requested yet
+ * Kernel assumes 32-bit DMA operations by default, no need to dma_set_mask
+ */
+static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
+ void __iomem *regs)
+{
+ struct ci13xxx *udc;
+ struct ci13xxx_platform_data *pdata;
+ int retval = 0, i;
+
+ trace("%p, %p, %p", dev, regs, driver->name);
+
+ if (dev == NULL || regs == NULL || driver == NULL ||
+ driver->name == NULL)
+ return -EINVAL;
+
+ udc = kzalloc(sizeof(struct ci13xxx), GFP_KERNEL);
+ if (udc == NULL)
+ return -ENOMEM;
+
+ udc->lock = &udc_lock;
+ udc->regs = regs;
+ udc->udc_driver = driver;
+
+ udc->gadget.ops = &usb_gadget_ops;
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->gadget.max_speed = USB_SPEED_HIGH;
+ if (udc->udc_driver->flags & CI13XXX_IS_OTG)
+ udc->gadget.is_otg = 1;
+ else
+ udc->gadget.is_otg = 0;
+ udc->gadget.name = driver->name;
+
+ INIT_LIST_HEAD(&udc->gadget.ep_list);
+ udc->gadget.ep0 = NULL;
+
+ pdata = dev->platform_data;
+ if (pdata)
+ udc->gadget.usb_core_id = pdata->usb_core_id;
+
+ dev_set_name(&udc->gadget.dev, "gadget");
+ udc->gadget.dev.dma_mask = dev->dma_mask;
+ udc->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask;
+ udc->gadget.dev.parent = dev;
+ udc->gadget.dev.release = udc_release;
+
+ if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) {
+ udc->transceiver = usb_get_transceiver();
+ if (udc->transceiver == NULL) {
+ retval = -ENODEV;
+ goto free_udc;
+ }
+ }
+
+ INIT_DELAYED_WORK(&udc->rw_work, usb_do_remote_wakeup);
+
+ retval = hw_device_init(regs);
+ if (retval < 0)
+ goto put_transceiver;
+
+ for (i = 0; i < hw_ep_max; i++) {
+ struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i];
+ INIT_LIST_HEAD(&mEp->ep.ep_list);
+ setup_timer(&mEp->prime_timer, ep_prime_timer_func,
+ (unsigned long) mEp);
+ }
+
+ if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) {
+ retval = hw_device_reset(udc);
+ if (retval)
+ goto put_transceiver;
+ }
+
+ retval = device_register(&udc->gadget.dev);
+ if (retval) {
+ put_device(&udc->gadget.dev);
+ goto put_transceiver;
+ }
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+ retval = dbg_create_files(&udc->gadget.dev);
+#endif
+ if (retval)
+ goto unreg_device;
+
+ if (udc->transceiver) {
+ retval = otg_set_peripheral(udc->transceiver->otg,
+ &udc->gadget);
+ if (retval)
+ goto remove_dbg;
+ }
+
+ retval = usb_add_gadget_udc(dev, &udc->gadget);
+ if (retval)
+ goto remove_trans;
+
+ pm_runtime_no_callbacks(&udc->gadget.dev);
+ pm_runtime_enable(&udc->gadget.dev);
+
+ if (register_trace_usb_daytona_invalid_access(dump_usb_info, NULL))
+ pr_err("Registering trace failed\n");
+
+ _udc = udc;
+ return retval;
+
+remove_trans:
+ if (udc->transceiver) {
+ otg_set_peripheral(udc->transceiver->otg, &udc->gadget);
+ usb_put_transceiver(udc->transceiver);
+ }
+
+ err("error = %i", retval);
+remove_dbg:
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+ dbg_remove_files(&udc->gadget.dev);
+#endif
+unreg_device:
+ device_unregister(&udc->gadget.dev);
+put_transceiver:
+ if (udc->transceiver)
+ usb_put_transceiver(udc->transceiver);
+free_udc:
+ kfree(udc);
+ _udc = NULL;
+ return retval;
+}
+
+/**
+ * udc_remove: parent remove must call this to remove UDC
+ *
+ * No interrupts active, the IRQ has been released
+ */
+static void udc_remove(void)
+{
+ struct ci13xxx *udc = _udc;
+ int retval;
+
+ if (udc == NULL) {
+ err("EINVAL");
+ return;
+ }
+ retval = unregister_trace_usb_daytona_invalid_access(dump_usb_info,
+ NULL);
+ if (retval)
+ pr_err("Unregistering trace failed\n");
+
+ usb_del_gadget_udc(&udc->gadget);
+
+ if (udc->transceiver) {
+ otg_set_peripheral(udc->transceiver->otg, &udc->gadget);
+ usb_put_transceiver(udc->transceiver);
+ }
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+ dbg_remove_files(&udc->gadget.dev);
+#endif
+ device_unregister(&udc->gadget.dev);
+
+ kfree(udc);
+ _udc = NULL;
+}
diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h
new file mode 100644
index 0000000..f90ea86
--- /dev/null
+++ b/drivers/usb/gadget/ci13xxx_udc.h
@@ -0,0 +1,274 @@
+/*
+ * ci13xxx_udc.h - structures, registers, and macros MIPS USB IP core
+ *
+ * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
+ *
+ * Author: David Lopo
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Description: MIPS USB IP core family device controller
+ * Structures, registers and logging macros
+ */
+
+#ifndef _CI13XXX_h_
+#define _CI13XXX_h_
+
+/******************************************************************************
+ * DEFINE
+ *****************************************************************************/
+#define CI13XXX_PAGE_SIZE 4096ul /* page size for TD's */
+#define ENDPT_MAX (32)
+#define CTRL_PAYLOAD_MAX (64)
+#define RX (0) /* similar to USB_DIR_OUT but can be used as an index */
+#define TX (1) /* similar to USB_DIR_IN but can be used as an index */
+
+/* UDC private data:
+ * 16MSb - Vendor ID | 16 LSb Vendor private data
+ */
+#define CI13XX_REQ_VENDOR_ID(id) (id & 0xFFFF0000UL)
+
+#define MSM_ETD_TYPE BIT(1)
+#define MSM_EP_PIPE_ID_RESET_VAL 0x1F001F
+
+/******************************************************************************
+ * STRUCTURES
+ *****************************************************************************/
+/* DMA layout of transfer descriptors */
+struct ci13xxx_td {
+ /* 0 */
+ u32 next;
+#define TD_TERMINATE BIT(0)
+#define TD_ADDR_MASK (0xFFFFFFEUL << 5)
+ /* 1 */
+ u32 token;
+#define TD_STATUS (0x00FFUL << 0)
+#define TD_STATUS_TR_ERR BIT(3)
+#define TD_STATUS_DT_ERR BIT(5)
+#define TD_STATUS_HALTED BIT(6)
+#define TD_STATUS_ACTIVE BIT(7)
+#define TD_MULTO (0x0003UL << 10)
+#define TD_IOC BIT(15)
+#define TD_TOTAL_BYTES (0x7FFFUL << 16)
+ /* 2 */
+ u32 page[5];
+#define TD_CURR_OFFSET (0x0FFFUL << 0)
+#define TD_FRAME_NUM (0x07FFUL << 0)
+#define TD_RESERVED_MASK (0x0FFFUL << 0)
+} __attribute__ ((packed, aligned(4)));
+
+/* DMA layout of queue heads */
+struct ci13xxx_qh {
+ /* 0 */
+ u32 cap;
+#define QH_IOS BIT(15)
+#define QH_MAX_PKT (0x07FFUL << 16)
+#define QH_ZLT BIT(29)
+#define QH_MULT (0x0003UL << 30)
+#define QH_MULT_SHIFT 11
+ /* 1 */
+ u32 curr;
+ /* 2 - 8 */
+ struct ci13xxx_td td;
+ /* 9 */
+ u32 RESERVED;
+ struct usb_ctrlrequest setup;
+} __attribute__ ((packed, aligned(4)));
+
+/* cache of larger request's original attributes */
+struct ci13xxx_multi_req {
+ unsigned len;
+ unsigned actual;
+ void *buf;
+};
+
+/* Extension of usb_request */
+struct ci13xxx_req {
+ struct usb_request req;
+ unsigned map;
+ struct list_head queue;
+ struct ci13xxx_td *ptr;
+ dma_addr_t dma;
+ struct ci13xxx_td *zptr;
+ dma_addr_t zdma;
+ struct ci13xxx_multi_req multi;
+};
+
+/* Extension of usb_ep */
+struct ci13xxx_ep {
+ struct usb_ep ep;
+ const struct usb_endpoint_descriptor *desc;
+ u8 dir;
+ u8 num;
+ u8 type;
+ char name[16];
+ struct {
+ struct list_head queue;
+ struct ci13xxx_qh *ptr;
+ dma_addr_t dma;
+ } qh;
+ int wedge;
+
+ /* global resources */
+ spinlock_t *lock;
+ struct device *device;
+ struct dma_pool *td_pool;
+ struct ci13xxx_td *last_zptr;
+ dma_addr_t last_zdma;
+ unsigned long dTD_update_fail_count;
+ unsigned long prime_fail_count;
+ int prime_timer_count;
+ struct timer_list prime_timer;
+
+ bool multi_req;
+};
+
+struct ci13xxx;
+struct ci13xxx_udc_driver {
+ const char *name;
+ unsigned long flags;
+ unsigned int nz_itc;
+#define CI13XXX_REGS_SHARED BIT(0)
+#define CI13XXX_REQUIRE_TRANSCEIVER BIT(1)
+#define CI13XXX_PULLUP_ON_VBUS BIT(2)
+#define CI13XXX_DISABLE_STREAMING BIT(3)
+#define CI13XXX_ZERO_ITC BIT(4)
+#define CI13XXX_IS_OTG BIT(5)
+
+#define CI13XXX_CONTROLLER_RESET_EVENT 0
+#define CI13XXX_CONTROLLER_CONNECT_EVENT 1
+#define CI13XXX_CONTROLLER_SUSPEND_EVENT 2
+#define CI13XXX_CONTROLLER_REMOTE_WAKEUP_EVENT 3
+#define CI13XXX_CONTROLLER_RESUME_EVENT 4
+#define CI13XXX_CONTROLLER_DISCONNECT_EVENT 5
+#define CI13XXX_CONTROLLER_UDC_STARTED_EVENT 6
+
+ void (*notify_event) (struct ci13xxx *udc, unsigned event);
+};
+
+/* CI13XXX UDC descriptor & global resources */
+struct ci13xxx {
+ spinlock_t *lock; /* ctrl register bank access */
+ void __iomem *regs; /* registers address space */
+
+ struct dma_pool *qh_pool; /* DMA pool for queue heads */
+ struct dma_pool *td_pool; /* DMA pool for transfer descs */
+ struct usb_request *status; /* ep0 status request */
+ void *status_buf;/* GET_STATUS buffer */
+
+ struct usb_gadget gadget; /* USB slave device */
+ struct ci13xxx_ep ci13xxx_ep[ENDPT_MAX]; /* extended endpts */
+ u32 ep0_dir; /* ep0 direction */
+#define ep0out ci13xxx_ep[0]
+#define ep0in ci13xxx_ep[hw_ep_max / 2]
+ u8 remote_wakeup; /* Is remote wakeup feature
+ enabled by the host? */
+ u8 suspended; /* suspended by the host */
+ u8 configured; /* is device configured */
+ u8 test_mode; /* the selected test mode */
+
+ struct delayed_work rw_work; /* remote wakeup delayed work */
+ struct usb_gadget_driver *driver; /* 3rd party gadget driver */
+ struct ci13xxx_udc_driver *udc_driver; /* device controller driver */
+ int vbus_active; /* is VBUS active */
+ int softconnect; /* is pull-up enable allowed */
+ unsigned long dTD_update_fail_count;
+ struct usb_phy *transceiver; /* Transceiver struct */
+ bool skip_flush; /* skip flushing remaining EP
+ upon flush timeout for the
+ first EP. */
+};
+
+/******************************************************************************
+ * REGISTERS
+ *****************************************************************************/
+/* register size */
+#define REG_BITS (32)
+
+/* HCCPARAMS */
+#define HCCPARAMS_LEN BIT(17)
+
+/* DCCPARAMS */
+#define DCCPARAMS_DEN (0x1F << 0)
+#define DCCPARAMS_DC BIT(7)
+
+/* TESTMODE */
+#define TESTMODE_FORCE BIT(0)
+
+/* USBCMD */
+#define USBCMD_RS BIT(0)
+#define USBCMD_RST BIT(1)
+#define USBCMD_SUTW BIT(13)
+#define USBCMD_ATDTW BIT(14)
+
+/* USBSTS & USBINTR */
+#define USBi_UI BIT(0)
+#define USBi_UEI BIT(1)
+#define USBi_PCI BIT(2)
+#define USBi_URI BIT(6)
+#define USBi_SLI BIT(8)
+
+/* DEVICEADDR */
+#define DEVICEADDR_USBADRA BIT(24)
+#define DEVICEADDR_USBADR (0x7FUL << 25)
+
+/* PORTSC */
+#define PORTSC_FPR BIT(6)
+#define PORTSC_SUSP BIT(7)
+#define PORTSC_HSP BIT(9)
+#define PORTSC_PTC (0x0FUL << 16)
+
+/* DEVLC */
+#define DEVLC_PSPD (0x03UL << 25)
+#define DEVLC_PSPD_HS (0x02UL << 25)
+
+/* USBMODE */
+#define USBMODE_CM (0x03UL << 0)
+#define USBMODE_CM_IDLE (0x00UL << 0)
+#define USBMODE_CM_DEVICE (0x02UL << 0)
+#define USBMODE_CM_HOST (0x03UL << 0)
+#define USBMODE_SLOM BIT(3)
+#define USBMODE_SDIS BIT(4)
+#define USBCMD_ITC(n) (n << 16) /* n = 0, 1, 2, 4, 8, 16, 32, 64 */
+#define USBCMD_ITC_MASK (0xFF << 16)
+
+/* ENDPTCTRL */
+#define ENDPTCTRL_RXS BIT(0)
+#define ENDPTCTRL_RXT (0x03UL << 2)
+#define ENDPTCTRL_RXR BIT(6) /* reserved for port 0 */
+#define ENDPTCTRL_RXE BIT(7)
+#define ENDPTCTRL_TXS BIT(16)
+#define ENDPTCTRL_TXT (0x03UL << 18)
+#define ENDPTCTRL_TXR BIT(22) /* reserved for port 0 */
+#define ENDPTCTRL_TXE BIT(23)
+
+/******************************************************************************
+ * LOGGING
+ *****************************************************************************/
+#define ci13xxx_printk(level, format, args...) \
+do { \
+ if (_udc == NULL) \
+ printk(level "[%s] " format "\n", __func__, ## args); \
+ else \
+ dev_printk(level, _udc->gadget.dev.parent, \
+ "[%s] " format "\n", __func__, ## args); \
+} while (0)
+
+#ifndef err
+#define err(format, args...) ci13xxx_printk(KERN_ERR, format, ## args)
+#endif
+
+#define warn(format, args...) ci13xxx_printk(KERN_WARNING, format, ## args)
+#define info(format, args...) ci13xxx_printk(KERN_INFO, format, ## args)
+
+#ifdef TRACE
+#define trace(format, args...) ci13xxx_printk(KERN_DEBUG, format, ## args)
+#define dbg_trace(format, args...) dev_dbg(dev, format, ##args)
+#else
+#define trace(format, args...) do {} while (0)
+#define dbg_trace(format, args...) do {} while (0)
+#endif
+
+#endif /* _CI13XXX_h_ */
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 4aee8c8..0fec59c 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -167,7 +167,6 @@
struct usb_function *f,
struct usb_ep *_ep)
{
- struct usb_composite_dev *cdev = get_gadget_data(g);
struct usb_endpoint_descriptor *chosen_desc = NULL;
struct usb_descriptor_header **speed_desc = NULL;
@@ -246,8 +245,12 @@
_ep->maxburst = comp_desc->bMaxBurst + 1;
break;
default:
- if (comp_desc->bMaxBurst != 0)
+ if (comp_desc->bMaxBurst != 0) {
+ struct usb_composite_dev *cdev;
+
+ cdev = get_gadget_data(g);
ERROR(cdev, "ep0 bMaxBurst must be 0\n");
+ }
_ep->maxburst = 1;
break;
}
diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c
index 9f7a29a..05a66b2 100644
--- a/drivers/usb/gadget/function/f_audio_source.c
+++ b/drivers/usb/gadget/function/f_audio_source.c
@@ -158,6 +158,13 @@
.bInterval = 4, /* poll 1 per millisecond */
};
+static struct usb_ss_ep_comp_descriptor ss_as_in_comp_desc = {
+ .bLength = sizeof(ss_as_in_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ .wBytesPerInterval = cpu_to_le16(IN_EP_MAX_PACKET_SIZE),
+};
+
/* Standard ISO IN Endpoint Descriptor for highspeed */
static struct usb_endpoint_descriptor fs_as_in_ep_desc = {
.bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
@@ -198,6 +205,26 @@
NULL,
};
+static struct usb_descriptor_header *ss_audio_desc[] = {
+ (struct usb_descriptor_header *)&ac_interface_desc,
+ (struct usb_descriptor_header *)&ac_header_desc,
+
+ (struct usb_descriptor_header *)&input_terminal_desc,
+ (struct usb_descriptor_header *)&output_terminal_desc,
+ (struct usb_descriptor_header *)&feature_unit_desc,
+
+ (struct usb_descriptor_header *)&as_interface_alt_0_desc,
+ (struct usb_descriptor_header *)&as_interface_alt_1_desc,
+ (struct usb_descriptor_header *)&as_header_desc,
+
+ (struct usb_descriptor_header *)&as_type_i_desc,
+
+ (struct usb_descriptor_header *)&hs_as_in_ep_desc,
+ (struct usb_descriptor_header *)&ss_as_in_comp_desc,
+ (struct usb_descriptor_header *)&as_iso_in_desc,
+ NULL,
+};
+
static struct usb_descriptor_header *fs_audio_desc[] = {
(struct usb_descriptor_header *)&ac_interface_desc,
(struct usb_descriptor_header *)&ac_header_desc,
@@ -673,6 +700,7 @@
f->fs_descriptors = fs_audio_desc;
f->hs_descriptors = hs_audio_desc;
+ f->ss_descriptors = ss_audio_desc;
for (i = 0, status = 0; i < IN_EP_REQ_COUNT && status == 0; i++) {
req = audio_request_new(ep, IN_EP_MAX_PACKET_SIZE);
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index dc368c7..4bdfadf 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -300,15 +300,17 @@
gsi_channel_info.gevntcount_hi_addr;
in_params->dir = GSI_CHAN_DIR_FROM_GSI;
in_params->xfer_ring_len = gsi_channel_info.xfer_ring_len;
- in_params->xfer_ring_base_addr = gsi_channel_info.xfer_ring_base_addr;
in_params->xfer_scratch.last_trb_addr_iova =
gsi_channel_info.last_trb_addr;
- in_params->xfer_ring_base_addr = in_params->xfer_ring_base_addr_iova =
+ in_params->xfer_ring_base_addr_iova =
gsi_channel_info.xfer_ring_base_addr;
in_params->data_buff_base_len = d_port->in_request.buf_len *
d_port->in_request.num_bufs;
- in_params->data_buff_base_addr = in_params->data_buff_base_addr_iova =
- d_port->in_request.dma;
+ in_params->data_buff_base_addr_iova = d_port->in_request.dma;
+ in_params->sgt_xfer_rings = &d_port->in_request.sgt_trb_xfer_ring;
+ in_params->sgt_data_buff = &d_port->in_request.sgt_data_buff;
+ log_event_dbg("%s(): IN: sgt_xfer_rings:%pK sgt_data_buff:%pK\n",
+ __func__, in_params->sgt_xfer_rings, in_params->sgt_data_buff);
in_params->xfer_scratch.const_buffer_size =
gsi_channel_info.const_buffer_size;
in_params->xfer_scratch.depcmd_low_addr =
@@ -340,14 +342,19 @@
out_params->dir = GSI_CHAN_DIR_TO_GSI;
out_params->xfer_ring_len =
gsi_channel_info.xfer_ring_len;
- out_params->xfer_ring_base_addr =
- out_params->xfer_ring_base_addr_iova =
+ out_params->xfer_ring_base_addr_iova =
gsi_channel_info.xfer_ring_base_addr;
out_params->data_buff_base_len = d_port->out_request.buf_len *
d_port->out_request.num_bufs;
- out_params->data_buff_base_addr =
- out_params->data_buff_base_addr_iova =
+ out_params->data_buff_base_addr_iova =
d_port->out_request.dma;
+ out_params->sgt_xfer_rings =
+ &d_port->out_request.sgt_trb_xfer_ring;
+ out_params->sgt_data_buff = &d_port->out_request.sgt_data_buff;
+ log_event_dbg("%s(): OUT: sgt_xfer_rings:%pK sgt_data_buff:%pK\n",
+ __func__, out_params->sgt_xfer_rings,
+ out_params->sgt_data_buff);
+
out_params->xfer_scratch.last_trb_addr_iova =
gsi_channel_info.last_trb_addr;
out_params->xfer_scratch.const_buffer_size =
@@ -501,10 +508,12 @@
gsi->d_port.in_channel_handle = -EINVAL;
gsi->d_port.out_channel_handle = -EINVAL;
- usb_gsi_ep_op(gsi->d_port.in_ep, NULL, GSI_EP_OP_FREE_TRBS);
+ usb_gsi_ep_op(gsi->d_port.in_ep, &gsi->d_port.in_request,
+ GSI_EP_OP_FREE_TRBS);
if (gsi->d_port.out_ep)
- usb_gsi_ep_op(gsi->d_port.out_ep, NULL, GSI_EP_OP_FREE_TRBS);
+ usb_gsi_ep_op(gsi->d_port.out_ep, &gsi->d_port.out_request,
+ GSI_EP_OP_FREE_TRBS);
/* free buffers allocated with each TRB */
gsi_free_trb_buffer(gsi);
@@ -1949,6 +1958,11 @@
ret = -ENOMEM;
goto fail1;
}
+
+ dma_get_sgtable(dev->parent,
+ &gsi->d_port.in_request.sgt_data_buff,
+ gsi->d_port.in_request.buf_base_addr,
+ gsi->d_port.in_request.dma, len_in);
}
if (gsi->d_port.out_ep && !gsi->d_port.out_request.buf_base_addr) {
@@ -1968,6 +1982,11 @@
ret = -ENOMEM;
goto fail;
}
+
+ dma_get_sgtable(dev->parent,
+ &gsi->d_port.out_request.sgt_data_buff,
+ gsi->d_port.out_request.buf_base_addr,
+ gsi->d_port.out_request.dma, len_out);
}
log_event_dbg("finished allocating trb's buffer\n");
@@ -1998,6 +2017,7 @@
gsi->d_port.out_request.buf_base_addr,
gsi->d_port.out_request.dma);
gsi->d_port.out_request.buf_base_addr = NULL;
+ sg_free_table(&gsi->d_port.out_request.sgt_data_buff);
}
if (gsi->d_port.in_ep &&
@@ -2008,6 +2028,7 @@
gsi->d_port.in_request.buf_base_addr,
gsi->d_port.in_request.dma);
gsi->d_port.in_request.buf_base_addr = NULL;
+ sg_free_table(&gsi->d_port.in_request.sgt_data_buff);
}
}
@@ -3069,7 +3090,7 @@
ipa_chnl_params->xfer_ring_len);
len += scnprintf(buf + len, PAGE_SIZE - len,
"%25s %10x\n", "IN TRB Base Addr: ", (unsigned int)
- ipa_chnl_params->xfer_ring_base_addr);
+ ipa_chnl_params->xfer_ring_base_addr_iova);
len += scnprintf(buf + len, PAGE_SIZE - len,
"%25s %10x\n", "GEVENTCNTLO IN Addr: ",
ipa_chnl_params->gevntcount_low_addr);
@@ -3103,7 +3124,7 @@
ipa_chnl_params->xfer_ring_len);
len += scnprintf(buf + len, PAGE_SIZE - len,
"%25s %10x\n", "OUT TRB Base Addr: ", (unsigned int)
- ipa_chnl_params->xfer_ring_base_addr);
+ ipa_chnl_params->xfer_ring_base_addr_iova);
len += scnprintf(buf + len, PAGE_SIZE - len,
"%25s %10x\n", "GEVENTCNTLO OUT Addr: ",
ipa_chnl_params->gevntcount_low_addr);
diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c
index a0fecb2..6ae2693 100644
--- a/drivers/usb/gadget/function/f_qdss.c
+++ b/drivers/usb/gadget/function/f_qdss.c
@@ -183,15 +183,28 @@
}
/*----------------------------------------------------------------------*/
-static void qdss_ctrl_write_complete(struct usb_ep *ep,
+static void qdss_write_complete(struct usb_ep *ep,
struct usb_request *req)
{
struct f_qdss *qdss = ep->driver_data;
struct qdss_request *d_req = req->context;
+ struct usb_ep *in;
+ struct list_head *list_pool;
+ enum qdss_state state;
unsigned long flags;
pr_debug("qdss_ctrl_write_complete\n");
+ if (qdss->debug_inface_enabled) {
+ in = qdss->port.ctrl_in;
+ list_pool = &qdss->ctrl_write_pool;
+ state = USB_QDSS_CTRL_WRITE_DONE;
+ } else {
+ in = qdss->port.data;
+ list_pool = &qdss->data_write_pool;
+ state = USB_QDSS_DATA_WRITE_DONE;
+ }
+
if (!req->status) {
/* send zlp */
if ((req->length >= ep->maxpacket) &&
@@ -199,13 +212,13 @@
req->length = 0;
d_req->actual = req->actual;
d_req->status = req->status;
- if (!usb_ep_queue(qdss->port.ctrl_in, req, GFP_ATOMIC))
+ if (!usb_ep_queue(in, req, GFP_ATOMIC))
return;
}
}
spin_lock_irqsave(&qdss->lock, flags);
- list_add_tail(&req->list, &qdss->ctrl_write_pool);
+ list_add_tail(&req->list, list_pool);
if (req->length != 0) {
d_req->actual = req->actual;
d_req->status = req->status;
@@ -213,8 +226,7 @@
spin_unlock_irqrestore(&qdss->lock, flags);
if (qdss->ch.notify)
- qdss->ch.notify(qdss->ch.priv, USB_QDSS_CTRL_WRITE_DONE, d_req,
- NULL);
+ qdss->ch.notify(qdss->ch.priv, state, d_req, NULL);
}
static void qdss_ctrl_read_complete(struct usb_ep *ep,
@@ -252,6 +264,12 @@
return;
}
+ list_for_each_safe(act, tmp, &qdss->data_write_pool) {
+ req = list_entry(act, struct usb_request, list);
+ list_del(&req->list);
+ usb_ep_free_request(qdss->port.data, req);
+ }
+
list_for_each_safe(act, tmp, &qdss->ctrl_write_pool) {
req = list_entry(act, struct usb_request, list);
list_del(&req->list);
@@ -271,23 +289,41 @@
{
struct f_qdss *qdss = ch->priv_usb;
struct usb_request *req;
+ struct usb_ep *in;
+ struct list_head *list_pool;
int i;
pr_debug("usb_qdss_alloc_req\n");
- if (no_write_buf <= 0 || no_read_buf <= 0 || !qdss) {
+ if (!qdss) {
+ pr_err("usb_qdss_alloc_req: channel %s closed\n", ch->name);
+ return -ENODEV;
+ }
+
+ if ((qdss->debug_inface_enabled &&
+ (no_write_buf <= 0 || no_read_buf <= 0)) ||
+ (!qdss->debug_inface_enabled &&
+ (no_write_buf <= 0 || no_read_buf))) {
pr_err("usb_qdss_alloc_req: missing params\n");
return -ENODEV;
}
+ if (qdss->debug_inface_enabled) {
+ in = qdss->port.ctrl_in;
+ list_pool = &qdss->ctrl_write_pool;
+ } else {
+ in = qdss->port.data;
+ list_pool = &qdss->data_write_pool;
+ }
+
for (i = 0; i < no_write_buf; i++) {
- req = usb_ep_alloc_request(qdss->port.ctrl_in, GFP_ATOMIC);
+ req = usb_ep_alloc_request(in, GFP_ATOMIC);
if (!req) {
pr_err("usb_qdss_alloc_req: ctrl_in allocation err\n");
goto fail;
}
- req->complete = qdss_ctrl_write_complete;
- list_add_tail(&req->list, &qdss->ctrl_write_pool);
+ req->complete = qdss_write_complete;
+ list_add_tail(&req->list, list_pool);
}
for (i = 0; i < no_read_buf; i++) {
@@ -378,6 +414,10 @@
qdss_ctrl_intf_desc.iInterface = id;
}
+ /* for non-accelerated path keep tx fifo size 1k */
+ if (!strcmp(qdss->ch.name, USB_QDSS_CH_MDM))
+ qdss_data_ep_comp_desc.bMaxBurst = 0;
+
ep = usb_ep_autoconfig_ss(gadget, &qdss_ss_data_desc,
&qdss_data_ep_comp_desc);
if (!ep) {
@@ -490,21 +530,20 @@
qdss = container_of(work, struct f_qdss, disconnect_w);
pr_debug("usb_qdss_disconnect_work\n");
- /*
- * Uninitialized init data i.e. ep specific operation.
- * Notify qdss to cancel all active transfers.
- */
- if (qdss->ch.app_conn) {
+
+ /* Notify qdss to cancel all active transfers */
+ if (qdss->ch.notify)
+ qdss->ch.notify(qdss->ch.priv,
+ USB_QDSS_DISCONNECT,
+ NULL,
+ NULL);
+
+ /* Uninitialized init data i.e. ep specific operation */
+ if (qdss->ch.app_conn && !strcmp(qdss->ch.name, USB_QDSS_CH_MSM)) {
status = uninit_data(qdss->port.data);
if (status)
pr_err("%s: uninit_data error\n", __func__);
- if (qdss->ch.notify)
- qdss->ch.notify(qdss->ch.priv,
- USB_QDSS_DISCONNECT,
- NULL,
- NULL);
-
status = set_qdss_data_connection(qdss, 0);
if (status)
pr_err("qdss_disconnect error");
@@ -561,15 +600,16 @@
}
pr_debug("usb_qdss_connect_work\n");
+
+ if (!strcmp(qdss->ch.name, USB_QDSS_CH_MDM))
+ goto notify;
+
status = set_qdss_data_connection(qdss, 1);
if (status) {
pr_err("set_qdss_data_connection error(%d)", status);
return;
}
- if (qdss->ch.notify)
- qdss->ch.notify(qdss->ch.priv, USB_QDSS_CONNECT,
- NULL, &qdss->ch);
spin_lock_irqsave(&qdss->lock, flags);
req = qdss->endless_req;
spin_unlock_irqrestore(&qdss->lock, flags);
@@ -577,8 +617,15 @@
return;
status = usb_ep_queue(qdss->port.data, req, GFP_ATOMIC);
- if (status)
+ if (status) {
pr_err("%s: usb_ep_queue error (%d)\n", __func__, status);
+ return;
+ }
+
+notify:
+ if (qdss->ch.notify)
+ qdss->ch.notify(qdss->ch.priv, USB_QDSS_CONNECT,
+ NULL, &qdss->ch);
}
static int qdss_set_alt(struct usb_function *f, unsigned int intf,
@@ -718,6 +765,7 @@
spin_lock_init(&qdss->lock);
INIT_LIST_HEAD(&qdss->ctrl_read_pool);
INIT_LIST_HEAD(&qdss->ctrl_write_pool);
+ INIT_LIST_HEAD(&qdss->data_write_pool);
INIT_WORK(&qdss->connect_w, usb_qdss_connect_work);
INIT_WORK(&qdss->disconnect_w, usb_qdss_disconnect_work);
@@ -813,6 +861,50 @@
}
EXPORT_SYMBOL(usb_qdss_ctrl_write);
+int usb_qdss_write(struct usb_qdss_ch *ch, struct qdss_request *d_req)
+{
+ struct f_qdss *qdss = ch->priv_usb;
+ unsigned long flags;
+ struct usb_request *req = NULL;
+
+ pr_debug("usb_qdss_ctrl_write\n");
+
+ if (!qdss)
+ return -ENODEV;
+
+ spin_lock_irqsave(&qdss->lock, flags);
+
+ if (qdss->usb_connected == 0) {
+ spin_unlock_irqrestore(&qdss->lock, flags);
+ return -EIO;
+ }
+
+ if (list_empty(&qdss->data_write_pool)) {
+ pr_err("error: usb_qdss_data_write list is empty\n");
+ spin_unlock_irqrestore(&qdss->lock, flags);
+ return -EAGAIN;
+ }
+
+ req = list_first_entry(&qdss->data_write_pool, struct usb_request,
+ list);
+ list_del(&req->list);
+ spin_unlock_irqrestore(&qdss->lock, flags);
+
+ req->buf = d_req->buf;
+ req->length = d_req->length;
+ req->context = d_req;
+ if (usb_ep_queue(qdss->port.data, req, GFP_ATOMIC)) {
+ spin_lock_irqsave(&qdss->lock, flags);
+ list_add_tail(&req->list, &qdss->data_write_pool);
+ spin_unlock_irqrestore(&qdss->lock, flags);
+ pr_err("qdss usb_ep_queue failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_qdss_write);
+
struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv,
void (*notify)(void *priv, unsigned int event,
struct qdss_request *d_req, struct usb_qdss_ch *))
@@ -870,7 +962,9 @@
pr_debug("usb_qdss_close\n");
spin_lock_irqsave(&qdss_lock, flags);
- if (!qdss || !qdss->usb_connected) {
+ ch->priv_usb = NULL;
+ if (!qdss || !qdss->usb_connected ||
+ !strcmp(qdss->ch.name, USB_QDSS_CH_MDM)) {
ch->app_conn = 0;
spin_unlock_irqrestore(&qdss_lock, flags);
return;
diff --git a/drivers/usb/gadget/function/f_qdss.h b/drivers/usb/gadget/function/f_qdss.h
index 72edb90..57c76f8 100644
--- a/drivers/usb/gadget/function/f_qdss.h
+++ b/drivers/usb/gadget/function/f_qdss.h
@@ -59,6 +59,10 @@
struct usb_qdss_ch ch;
struct list_head ctrl_read_pool;
struct list_head ctrl_write_pool;
+
+ /* for mdm channel SW path */
+ struct list_head data_write_pool;
+
struct work_struct connect_w;
struct work_struct disconnect_w;
spinlock_t lock;
diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index cb00ada..9625248 100644
--- a/drivers/usb/gadget/function/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
@@ -31,13 +31,43 @@
struct gserial port;
u8 data_id;
u8 port_num;
+ spinlock_t lock;
+
+ struct usb_ep *notify;
+ struct usb_request *notify_req;
+
+ u8 online;
+ u8 pending;
+ struct usb_cdc_line_coding port_line_coding;
+
+ /* SetControlLineState request */
+ u16 port_handshake_bits;
+#define ACM_CTRL_RTS (1 << 1) /* unused with full duplex */
+#define ACM_CTRL_DTR (1 << 0) /* host is ready for data r/w */
+
+ /* SerialState notification */
+ u16 serial_state;
+#define ACM_CTRL_OVERRUN (1 << 6)
+#define ACM_CTRL_PARITY (1 << 5)
+#define ACM_CTRL_FRAMING (1 << 4)
+#define ACM_CTRL_RI (1 << 3)
+#define ACM_CTRL_BRK (1 << 2)
+#define ACM_CTRL_DSR (1 << 1)
+#define ACM_CTRL_DCD (1 << 0)
};
+static inline struct f_gser *port_to_gser(struct gserial *p)
+{
+ return container_of(p, struct f_gser, port);
+}
+
static inline struct f_gser *func_to_gser(struct usb_function *f)
{
return container_of(f, struct f_gser, port.func);
}
+#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */
+#define GS_NOTIFY_MAXPACKET 10 /* notification + 2 bytes */
/*-------------------------------------------------------------------------*/
/* interface descriptor: */
@@ -46,15 +76,55 @@
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
- .bNumEndpoints = 2,
+ .bNumEndpoints = 3,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
/* .iInterface = DYNAMIC */
};
+static struct usb_cdc_header_desc gser_header_desc = {
+ .bLength = sizeof(gser_header_desc),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_HEADER_TYPE,
+ .bcdCDC = cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_call_mgmt_descriptor
+gser_call_mgmt_descriptor = {
+ .bLength = sizeof(gser_call_mgmt_descriptor),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
+ .bmCapabilities = 0,
+ /* .bDataInterface = DYNAMIC */
+};
+
+static struct usb_cdc_acm_descriptor gser_descriptor = {
+ .bLength = sizeof(gser_descriptor),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_ACM_TYPE,
+ .bmCapabilities = USB_CDC_CAP_LINE,
+};
+
+static struct usb_cdc_union_desc gser_union_desc = {
+ .bLength = sizeof(gser_union_desc),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_UNION_TYPE,
+ /* .bMasterInterface0 = DYNAMIC */
+ /* .bSlaveInterface0 = DYNAMIC */
+};
+
/* full speed support: */
+static struct usb_endpoint_descriptor gser_fs_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
+ .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL,
+};
+
static struct usb_endpoint_descriptor gser_fs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -71,12 +141,25 @@
static struct usb_descriptor_header *gser_fs_function[] = {
(struct usb_descriptor_header *) &gser_interface_desc,
+ (struct usb_descriptor_header *) &gser_header_desc,
+ (struct usb_descriptor_header *) &gser_call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &gser_descriptor,
+ (struct usb_descriptor_header *) &gser_union_desc,
+ (struct usb_descriptor_header *) &gser_fs_notify_desc,
(struct usb_descriptor_header *) &gser_fs_in_desc,
(struct usb_descriptor_header *) &gser_fs_out_desc,
NULL,
};
/* high speed support: */
+static struct usb_endpoint_descriptor gser_hs_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
+ .bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
+};
static struct usb_endpoint_descriptor gser_hs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
@@ -94,6 +177,11 @@
static struct usb_descriptor_header *gser_hs_function[] = {
(struct usb_descriptor_header *) &gser_interface_desc,
+ (struct usb_descriptor_header *) &gser_header_desc,
+ (struct usb_descriptor_header *) &gser_call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &gser_descriptor,
+ (struct usb_descriptor_header *) &gser_union_desc,
+ (struct usb_descriptor_header *) &gser_hs_notify_desc,
(struct usb_descriptor_header *) &gser_hs_in_desc,
(struct usb_descriptor_header *) &gser_hs_out_desc,
NULL,
@@ -118,8 +206,33 @@
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
};
+static struct usb_endpoint_descriptor gser_ss_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
+ .bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
+};
+
+static struct usb_ss_ep_comp_descriptor gser_ss_notify_comp_desc = {
+ .bLength = sizeof(gser_ss_notify_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 2 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+ .wBytesPerInterval = cpu_to_le16(GS_NOTIFY_MAXPACKET),
+};
+
static struct usb_descriptor_header *gser_ss_function[] = {
(struct usb_descriptor_header *) &gser_interface_desc,
+ (struct usb_descriptor_header *) &gser_header_desc,
+ (struct usb_descriptor_header *) &gser_call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &gser_descriptor,
+ (struct usb_descriptor_header *) &gser_union_desc,
+ (struct usb_descriptor_header *) &gser_ss_notify_desc,
+ (struct usb_descriptor_header *) &gser_ss_notify_comp_desc,
(struct usb_descriptor_header *) &gser_ss_in_desc,
(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
(struct usb_descriptor_header *) &gser_ss_out_desc,
@@ -130,7 +243,7 @@
/* string descriptors: */
static struct usb_string gser_string_defs[] = {
- [0].s = "Generic Serial",
+ [0].s = "DUN over Serial",
{ } /* end of list */
};
@@ -145,13 +258,131 @@
};
/*-------------------------------------------------------------------------*/
+static void gser_complete_set_line_coding(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct f_gser *gser = ep->driver_data;
+ struct usb_composite_dev *cdev = gser->port.func.config->cdev;
+
+ if (req->status != 0) {
+ dev_dbg(&cdev->gadget->dev, "gser ttyGS%d completion, err %d\n",
+ gser->port_num, req->status);
+ return;
+ }
+
+ /* normal completion */
+ if (req->actual != sizeof(gser->port_line_coding)) {
+ dev_dbg(&cdev->gadget->dev, "gser ttyGS%d short resp, len %d\n",
+ gser->port_num, req->actual);
+ usb_ep_set_halt(ep);
+ } else {
+ struct usb_cdc_line_coding *value = req->buf;
+
+ gser->port_line_coding = *value;
+ }
+}
+
+static int
+gser_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+ struct f_gser *gser = func_to_gser(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = cdev->req;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+
+ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+ /* SET_LINE_CODING ... just read and save what the host sends */
+ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_REQ_SET_LINE_CODING:
+ if (w_length != sizeof(struct usb_cdc_line_coding))
+ goto invalid;
+
+ value = w_length;
+ cdev->gadget->ep0->driver_data = gser;
+ req->complete = gser_complete_set_line_coding;
+ break;
+
+ /* GET_LINE_CODING ... return what host sent, or initial value */
+ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_REQ_GET_LINE_CODING:
+ value = min_t(unsigned int, w_length,
+ sizeof(struct usb_cdc_line_coding));
+ memcpy(req->buf, &gser->port_line_coding, value);
+ break;
+
+ /* SET_CONTROL_LINE_STATE ... save what the host sent */
+ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+
+ value = 0;
+ gser->port_handshake_bits = w_value;
+ pr_debug("%s: USB_CDC_REQ_SET_CONTROL_LINE_STATE: DTR:%d RST:%d\n",
+ __func__, w_value & ACM_CTRL_DTR ? 1 : 0,
+ w_value & ACM_CTRL_RTS ? 1 : 0);
+
+ if (gser->port.notify_modem)
+ gser->port.notify_modem(&gser->port, 0, w_value);
+
+ break;
+
+ default:
+invalid:
+ dev_dbg(&cdev->gadget->dev,
+ "invalid control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ dev_dbg(&cdev->gadget->dev,
+ "gser ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
+ gser->port_num, ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ req->zero = 0;
+ req->length = value;
+ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0)
+ ERROR(cdev, "gser response on ttyGS%d, err %d\n",
+ gser->port_num, value);
+ }
+
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_gser *gser = func_to_gser(f);
struct usb_composite_dev *cdev = f->config->cdev;
+ int rc = 0;
/* we know alt == 0, so this is an activation or a reset */
+ if (gser->notify->driver_data) {
+ dev_dbg(&cdev->gadget->dev,
+ "reset generic ctl ttyGS%d\n", gser->port_num);
+ usb_ep_disable(gser->notify);
+ }
+
+ if (!gser->notify->desc) {
+ if (config_ep_by_speed(cdev->gadget, f, gser->notify)) {
+ gser->notify->desc = NULL;
+ return -EINVAL;
+ }
+ }
+
+ rc = usb_ep_enable(gser->notify);
+ if (rc) {
+ ERROR(cdev, "can't enable %s, result %d\n",
+ gser->notify->name, rc);
+ return rc;
+ }
+ gser->notify->driver_data = gser;
if (gser->port.in->enabled) {
dev_dbg(&cdev->gadget->dev,
@@ -169,7 +400,9 @@
}
}
gserial_connect(&gser->port, gser->port_num);
- return 0;
+ gser->online = 1;
+
+ return rc;
}
static void gser_disable(struct usb_function *f)
@@ -180,6 +413,178 @@
dev_dbg(&cdev->gadget->dev,
"generic ttyGS%d deactivated\n", gser->port_num);
gserial_disconnect(&gser->port);
+ usb_ep_disable(gser->notify);
+ gser->notify->driver_data = NULL;
+ gser->online = 0;
+}
+
+static int gser_notify(struct f_gser *gser, u8 type, u16 value,
+ void *data, unsigned int length)
+{
+ struct usb_ep *ep = gser->notify;
+ struct usb_request *req;
+ struct usb_cdc_notification *notify;
+ const unsigned int len = sizeof(*notify) + length;
+ void *buf;
+ int status;
+ struct usb_composite_dev *cdev = gser->port.func.config->cdev;
+
+ req = gser->notify_req;
+ gser->notify_req = NULL;
+ gser->pending = false;
+
+ req->length = len;
+ notify = req->buf;
+ buf = notify + 1;
+
+ notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+ | USB_RECIP_INTERFACE;
+ notify->bNotificationType = type;
+ notify->wValue = cpu_to_le16(value);
+ notify->wIndex = cpu_to_le16(gser->data_id);
+ notify->wLength = cpu_to_le16(length);
+ memcpy(buf, data, length);
+
+ status = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (status < 0) {
+ ERROR(cdev, "gser ttyGS%d can't notify serial state, %d\n",
+ gser->port_num, status);
+ gser->notify_req = req;
+ }
+
+ return status;
+}
+
+static int gser_notify_serial_state(struct f_gser *gser)
+{
+ int status;
+ unsigned long flags;
+ struct usb_composite_dev *cdev = gser->port.func.config->cdev;
+
+ spin_lock_irqsave(&gser->lock, flags);
+ if (gser->notify_req) {
+ DBG(cdev, "gser ttyGS%d serial state %04x\n",
+ gser->port_num, gser->serial_state);
+ status = gser_notify(gser, USB_CDC_NOTIFY_SERIAL_STATE,
+ 0, &gser->serial_state,
+ sizeof(gser->serial_state));
+ } else {
+ gser->pending = true;
+ status = 0;
+ }
+
+ spin_unlock_irqrestore(&gser->lock, flags);
+ return status;
+}
+
+static void gser_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_gser *gser = req->context;
+ u8 doit = false;
+ unsigned long flags;
+
+ /* on this call path we do NOT hold the port spinlock,
+ * which is why ACM needs its own spinlock
+ */
+
+ spin_lock_irqsave(&gser->lock, flags);
+ if (req->status != -ESHUTDOWN)
+ doit = gser->pending;
+
+ gser->notify_req = req;
+ spin_unlock_irqrestore(&gser->lock, flags);
+
+ if (doit && gser->online)
+ gser_notify_serial_state(gser);
+}
+
+static void gser_connect(struct gserial *port)
+{
+ struct f_gser *gser = port_to_gser(port);
+
+ gser->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;
+ gser_notify_serial_state(gser);
+}
+
+static unsigned int gser_get_dtr(struct gserial *port)
+{
+ struct f_gser *gser = port_to_gser(port);
+
+ if (gser->port_handshake_bits & ACM_CTRL_DTR)
+ return 1;
+ else
+ return 0;
+}
+
+static unsigned int gser_get_rts(struct gserial *port)
+{
+ struct f_gser *gser = port_to_gser(port);
+
+ if (gser->port_handshake_bits & ACM_CTRL_RTS)
+ return 1;
+ else
+ return 0;
+}
+
+static unsigned int gser_send_carrier_detect(struct gserial *port,
+ unsigned int yes)
+{
+ u16 state;
+ struct f_gser *gser = port_to_gser(port);
+
+ state = gser->serial_state;
+ state &= ~ACM_CTRL_DCD;
+ if (yes)
+ state |= ACM_CTRL_DCD;
+
+ gser->serial_state = state;
+ return gser_notify_serial_state(gser);
+}
+
+static unsigned int gser_send_ring_indicator(struct gserial *port,
+ unsigned int yes)
+{
+ u16 state;
+ struct f_gser *gser = port_to_gser(port);
+
+ state = gser->serial_state;
+ state &= ~ACM_CTRL_RI;
+ if (yes)
+ state |= ACM_CTRL_RI;
+
+ gser->serial_state = state;
+ return gser_notify_serial_state(gser);
+}
+
+static void gser_disconnect(struct gserial *port)
+{
+ struct f_gser *gser = port_to_gser(port);
+
+ gser->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD);
+ gser_notify_serial_state(gser);
+}
+
+static int gser_send_break(struct gserial *port, int duration)
+{
+ u16 state;
+ struct f_gser *gser = port_to_gser(port);
+
+ state = gser->serial_state;
+ state &= ~ACM_CTRL_BRK;
+ if (duration)
+ state |= ACM_CTRL_BRK;
+
+ gser->serial_state = state;
+ return gser_notify_serial_state(gser);
+}
+
+static int gser_send_modem_ctrl_bits(struct gserial *port, int ctrl_bits)
+{
+ struct f_gser *gser = port_to_gser(port);
+
+ gser->serial_state = ctrl_bits;
+
+ return gser_notify_serial_state(gser);
}
/*-------------------------------------------------------------------------*/
@@ -225,6 +630,21 @@
goto fail;
gser->port.out = ep;
+ ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_notify_desc);
+ if (!ep)
+ goto fail;
+ gser->notify = ep;
+ ep->driver_data = cdev; /* claim */
+ /* allocate notification */
+ gser->notify_req = gs_alloc_req(ep,
+ sizeof(struct usb_cdc_notification) + 2,
+ GFP_KERNEL);
+ if (!gser->notify_req)
+ goto fail;
+
+ gser->notify_req->complete = gser_notify_complete;
+ gser->notify_req->context = gser;
+
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
@@ -235,6 +655,15 @@
gser_ss_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress;
gser_ss_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ gser_hs_notify_desc.bEndpointAddress =
+ gser_fs_notify_desc.bEndpointAddress;
+ }
+ if (gadget_is_superspeed(c->cdev->gadget)) {
+ gser_ss_notify_desc.bEndpointAddress =
+ gser_fs_notify_desc.bEndpointAddress;
+ }
+
status = usb_assign_descriptors(f, gser_fs_function, gser_hs_function,
gser_ss_function, NULL);
if (status)
@@ -247,6 +676,18 @@
return 0;
fail:
+ if (gser->notify_req)
+ gs_free_req(gser->notify, gser->notify_req);
+
+ /* we might as well release our claims on endpoints */
+ if (gser->notify)
+ gser->notify->driver_data = NULL;
+ /* we might as well release our claims on endpoints */
+ if (gser->port.out)
+ gser->port.out->driver_data = NULL;
+ if (gser->port.in)
+ gser->port.in->driver_data = NULL;
+
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
return status;
@@ -327,7 +768,10 @@
static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
{
+ struct f_gser *gser = func_to_gser(f);
+
usb_free_all_descriptors(f);
+ gs_free_req(gser->notify, gser->notify_req);
}
static struct usb_function *gser_alloc(struct usb_function_instance *fi)
@@ -342,6 +786,7 @@
opts = container_of(fi, struct f_serial_opts, func_inst);
+ spin_lock_init(&gser->lock);
gser->port_num = opts->port_num;
gser->port.func.name = "gser";
@@ -351,6 +796,15 @@
gser->port.func.set_alt = gser_set_alt;
gser->port.func.disable = gser_disable;
gser->port.func.free_func = gser_free;
+ gser->port.func.setup = gser_setup;
+ gser->port.connect = gser_connect;
+ gser->port.get_dtr = gser_get_dtr;
+ gser->port.get_rts = gser_get_rts;
+ gser->port.send_carrier_detect = gser_send_carrier_detect;
+ gser->port.send_ring_indicator = gser_send_ring_indicator;
+ gser->port.send_modem_ctrl_bits = gser_send_modem_ctrl_bits;
+ gser->port.disconnect = gser_disconnect;
+ gser->port.send_break = gser_send_break;
return &gser->port.func;
}
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index cb1ecfa..54220a5 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -382,15 +382,20 @@
static int alloc_requests(struct eth_dev *dev, struct gether *link, unsigned n)
{
- int status;
+ int status = 0;
spin_lock(&dev->req_lock);
- status = prealloc(&dev->tx_reqs, link->in_ep, n);
- if (status < 0)
- goto fail;
- status = prealloc(&dev->rx_reqs, link->out_ep, n);
- if (status < 0)
- goto fail;
+ if (link->in_ep) {
+ status = prealloc(&dev->tx_reqs, link->in_ep, n);
+ if (status < 0)
+ goto fail;
+ }
+
+ if (link->out_ep) {
+ status = prealloc(&dev->rx_reqs, link->out_ep, n);
+ if (status < 0)
+ goto fail;
+ }
goto done;
fail:
DBG(dev, "can't alloc requests\n");
@@ -838,16 +843,24 @@
* their own pace; the network stack can handle old packets.
* For the moment we leave this here, since it works.
*/
- in = link->in_ep->desc;
- out = link->out_ep->desc;
- usb_ep_disable(link->in_ep);
- usb_ep_disable(link->out_ep);
- if (netif_carrier_ok(net)) {
- DBG(dev, "host still using in/out endpoints\n");
- link->in_ep->desc = in;
- link->out_ep->desc = out;
- usb_ep_enable(link->in_ep);
- usb_ep_enable(link->out_ep);
+ if (link->in_ep) {
+ in = link->in_ep->desc;
+ usb_ep_disable(link->in_ep);
+ if (netif_carrier_ok(net)) {
+ DBG(dev, "host still using in endpoints\n");
+ link->in_ep->desc = in;
+ usb_ep_enable(link->in_ep);
+ }
+ }
+
+ if (link->out_ep) {
+ out = link->out_ep->desc;
+ usb_ep_disable(link->out_ep);
+ if (netif_carrier_ok(net)) {
+ DBG(dev, "host still using out endpoints\n");
+ link->out_ep->desc = out;
+ usb_ep_enable(link->out_ep);
+ }
}
}
spin_unlock_irqrestore(&dev->lock, flags);
@@ -1201,20 +1214,24 @@
if (!dev)
return ERR_PTR(-EINVAL);
- link->in_ep->driver_data = dev;
- result = usb_ep_enable(link->in_ep);
- if (result != 0) {
- DBG(dev, "enable %s --> %d\n",
- link->in_ep->name, result);
- goto fail0;
+ if (link->in_ep) {
+ link->in_ep->driver_data = dev;
+ result = usb_ep_enable(link->in_ep);
+ if (result != 0) {
+ DBG(dev, "enable %s --> %d\n",
+ link->in_ep->name, result);
+ goto fail0;
+ }
}
- link->out_ep->driver_data = dev;
- result = usb_ep_enable(link->out_ep);
- if (result != 0) {
- DBG(dev, "enable %s --> %d\n",
- link->out_ep->name, result);
- goto fail1;
+ if (link->out_ep) {
+ link->out_ep->driver_data = dev;
+ result = usb_ep_enable(link->out_ep);
+ if (result != 0) {
+ DBG(dev, "enable %s --> %d\n",
+ link->out_ep->name, result);
+ goto fail1;
+ }
}
if (result == 0)
@@ -1252,9 +1269,11 @@
/* on error, disable any endpoints */
} else {
- (void) usb_ep_disable(link->out_ep);
+ if (link->out_ep)
+ (void) usb_ep_disable(link->out_ep);
fail1:
- (void) usb_ep_disable(link->in_ep);
+ if (link->in_ep)
+ (void) usb_ep_disable(link->in_ep);
}
fail0:
/* caller is responsible for cleanup on error */
@@ -1295,41 +1314,45 @@
* of all pending i/o. then free the request objects
* and forget about the endpoints.
*/
- usb_ep_disable(link->in_ep);
- spin_lock(&dev->req_lock);
- while (!list_empty(&dev->tx_reqs)) {
- req = container_of(dev->tx_reqs.next,
- struct usb_request, list);
- list_del(&req->list);
-
- spin_unlock(&dev->req_lock);
- if (link->multi_pkt_xfer)
- kfree(req->buf);
- usb_ep_free_request(link->in_ep, req);
+ if (link->in_ep) {
+ usb_ep_disable(link->in_ep);
spin_lock(&dev->req_lock);
- }
- spin_unlock(&dev->req_lock);
- link->in_ep->desc = NULL;
+ while (!list_empty(&dev->tx_reqs)) {
+ req = container_of(dev->tx_reqs.next,
+ struct usb_request, list);
+ list_del(&req->list);
- usb_ep_disable(link->out_ep);
- spin_lock(&dev->req_lock);
- while (!list_empty(&dev->rx_reqs)) {
- req = container_of(dev->rx_reqs.next,
- struct usb_request, list);
- list_del(&req->list);
-
+ spin_unlock(&dev->req_lock);
+ if (link->multi_pkt_xfer)
+ kfree(req->buf);
+ usb_ep_free_request(link->in_ep, req);
+ spin_lock(&dev->req_lock);
+ }
spin_unlock(&dev->req_lock);
- usb_ep_free_request(link->out_ep, req);
- spin_lock(&dev->req_lock);
+ link->in_ep->desc = NULL;
}
- spin_unlock(&dev->req_lock);
- spin_lock(&dev->rx_frames.lock);
- while ((skb = __skb_dequeue(&dev->rx_frames)))
- dev_kfree_skb_any(skb);
- spin_unlock(&dev->rx_frames.lock);
+ if (link->out_ep) {
+ usb_ep_disable(link->out_ep);
+ spin_lock(&dev->req_lock);
+ while (!list_empty(&dev->rx_reqs)) {
+ req = container_of(dev->rx_reqs.next,
+ struct usb_request, list);
+ list_del(&req->list);
- link->out_ep->desc = NULL;
+ spin_unlock(&dev->req_lock);
+ usb_ep_free_request(link->out_ep, req);
+ spin_lock(&dev->req_lock);
+ }
+ spin_unlock(&dev->req_lock);
+
+ spin_lock(&dev->rx_frames.lock);
+ while ((skb = __skb_dequeue(&dev->rx_frames)))
+ dev_kfree_skb_any(skb);
+ spin_unlock(&dev->rx_frames.lock);
+
+ link->out_ep->desc = NULL;
+ }
/* finish forgetting about this USB link episode */
dev->header_len = 0;
diff --git a/drivers/usb/gadget/function/u_serial.h b/drivers/usb/gadget/function/u_serial.h
index c20210c..f367dc5 100644
--- a/drivers/usb/gadget/function/u_serial.h
+++ b/drivers/usb/gadget/function/u_serial.h
@@ -45,11 +45,23 @@
/* REVISIT avoid this CDC-ACM support harder ... */
struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */
+ u16 serial_state;
+
+ /* control signal callbacks*/
+ unsigned int (*get_dtr)(struct gserial *p);
+ unsigned int (*get_rts)(struct gserial *p);
/* notification callbacks */
void (*connect)(struct gserial *p);
void (*disconnect)(struct gserial *p);
int (*send_break)(struct gserial *p, int duration);
+ unsigned int (*send_carrier_detect)(struct gserial *p,
+ unsigned int yes);
+ unsigned int (*send_ring_indicator)(struct gserial *p,
+ unsigned int yes);
+ int (*send_modem_ctrl_bits)(struct gserial *p, int ctrl_bits);
+ /* notification changes to modem */
+ void (*notify_modem)(void *gser, u8 portno, int ctrl_bits);
};
/* utilities to allocate/free request and buffer */
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index 658b8da..f472b2b 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -389,6 +389,22 @@
dynamically linked module called "udc-xilinx" and force all
gadget drivers to also be dynamically linked.
+config USB_CI13XXX_MSM
+ tristate "MIPS USB CI13xxx for MSM"
+ depends on ARCH_MSM
+ select USB_MSM_OTG
+ help
+ MSM SoC has chipidea USB controller. This driver uses
+ ci13xxx_udc core.
+ This driver depends on OTG driver for PHY initialization,
+ clock management, powering up VBUS, and power management.
+ This driver is not supported on boards like trout which
+ has an external PHY.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "ci13xxx_msm" and force all
+ gadget drivers to also be dynamically linked.
+
#
# LAST -- dummy/emulated controller
#
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 26bfa73..72176a8 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -964,7 +964,7 @@
return 0;
/* "high bandwidth" works only at high speed */
- if (!gadget_is_dualspeed(gadget) && usb_endpoint_maxp(desc) & (3<<11))
+ if (!gadget_is_dualspeed(gadget) && usb_endpoint_maxp_mult(desc) > 1)
return 0;
switch (type) {
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index b59d214..2911b72 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1086,7 +1086,8 @@
return 1;
fail:
-
+ if (dev->eps[0].ring)
+ xhci_ring_free(xhci, dev->eps[0].ring);
if (dev->in_ctx)
xhci_free_container_ctx(xhci, dev->in_ctx);
if (dev->out_ctx)
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index c87ef38..f6782a3 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -190,6 +190,9 @@
xhci->quirks |= XHCI_BROKEN_STREAMS;
}
if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
+ pdev->device == 0x0014)
+ xhci->quirks |= XHCI_TRUST_TX_LENGTH;
+ if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
pdev->device == 0x0015)
xhci->quirks |= XHCI_RESET_ON_RESUME;
if (pdev->vendor == PCI_VENDOR_ID_VIA)
diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c
index 8e7737d..03be5d5 100644
--- a/drivers/usb/misc/usb3503.c
+++ b/drivers/usb/misc/usb3503.c
@@ -292,6 +292,8 @@
if (gpio_is_valid(hub->gpio_reset)) {
err = devm_gpio_request_one(dev, hub->gpio_reset,
GPIOF_OUT_INIT_LOW, "usb3503 reset");
+ /* Datasheet defines a hardware reset to be at least 100us */
+ usleep_range(100, 10000);
if (err) {
dev_err(dev,
"unable to request GPIO %d as reset pin (%d)\n",
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index 1a874a1..80b37d2 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -1002,7 +1002,9 @@
break;
case MON_IOCQ_RING_SIZE:
+ mutex_lock(&rp->fetch_lock);
ret = rp->b_size;
+ mutex_unlock(&rp->fetch_lock);
break;
case MON_IOCT_RING_SIZE:
@@ -1229,12 +1231,16 @@
unsigned long offset, chunk_idx;
struct page *pageptr;
+ mutex_lock(&rp->fetch_lock);
offset = vmf->pgoff << PAGE_SHIFT;
- if (offset >= rp->b_size)
+ if (offset >= rp->b_size) {
+ mutex_unlock(&rp->fetch_lock);
return VM_FAULT_SIGBUS;
+ }
chunk_idx = offset / CHUNK_SIZE;
pageptr = rp->b_vec[chunk_idx].pg;
get_page(pageptr);
+ mutex_unlock(&rp->fetch_lock);
vmf->page = pageptr;
return 0;
}
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 17e8edb..4e223f5 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -253,4 +253,15 @@
the high-speed PHY which is usually paired with either the ChipIdea or
Synopsys DWC3 USB IPs on MSM SOCs. This driver expects to configure the
PHY with a dedicated register I/O memory region.
+
+config USB_MSM_OTG
+ tristate "Qualcomm Technologies, Inc. on-chip USB OTG controller support"
+ depends on (USB || USB_GADGET) && (ARCH_QCOM || COMPILE_TEST)
+ select USB_PHY
+ help
+ Enable this to support the USB OTG transceiver on Qualcomm chips. It
+ handles PHY initialization, clock management, and workarounds
+ required after resetting the hardware and power management.
+ This driver is required even for peripheral only or host only
+ mode configurations.
endmenu
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index 285659d..7e9ffa0 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -31,3 +31,4 @@
obj-$(CONFIG_USB_MSM_SSPHY_QMP) += phy-msm-ssusb-qmp.o
obj-$(CONFIG_MSM_QUSB_PHY) += phy-msm-qusb.o phy-msm-qusb-v2.o
obj-$(CONFIG_MSM_HSUSB_PHY) += phy-msm-snps-hs.o
+obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o
diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c
index cce17e0..cc1a0ea 100644
--- a/drivers/usb/phy/phy-msm-qusb-v2.c
+++ b/drivers/usb/phy/phy-msm-qusb-v2.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -26,6 +26,7 @@
#include <linux/regulator/machine.h>
#include <linux/usb/phy.h>
#include <linux/reset.h>
+#include <linux/nvmem-consumer.h>
#include <linux/debugfs.h>
#include <linux/hrtimer.h>
@@ -146,8 +147,65 @@
u8 tune[5];
struct hrtimer timer;
+ int soc_min_rev;
};
+#ifdef CONFIG_NVMEM
+/* Parse qfprom data for deciding on errata work-arounds */
+static long qfprom_read(struct device *dev, const char *name)
+{
+ struct nvmem_cell *cell;
+ ssize_t len = 0;
+ u32 *buf, val = 0;
+ long err = 0;
+
+ cell = nvmem_cell_get(dev, name);
+ if (IS_ERR(cell)) {
+ err = PTR_ERR(cell);
+ dev_err(dev, "failed opening nvmem cell err : %ld\n", err);
+ /* If entry does not exist, then that is not an error */
+ if (err == -ENOENT)
+ err = 0;
+ return err;
+ }
+
+ buf = (u32 *)nvmem_cell_read(cell, &len);
+ if (IS_ERR(buf) || !len) {
+ dev_err(dev, "Failed reading nvmem cell, err: %u, bytes fetched: %zd\n",
+ *buf, len);
+ if (!IS_ERR(buf)) {
+ kfree(buf);
+ err = -EINVAL;
+ } else {
+ err = PTR_ERR(buf);
+ }
+ } else {
+ val = *buf;
+ kfree(buf);
+ }
+
+ nvmem_cell_put(cell);
+ return err ? err : (long) val;
+}
+
+/* Reads the SoC version */
+static int qusb_phy_get_socrev(struct device *dev, struct qusb_phy *qphy)
+{
+ qphy->soc_min_rev = qfprom_read(dev, "minor_rev");
+ if (qphy->soc_min_rev < 0)
+ dev_err(dev, "failed getting soc_min_rev, err : %d\n",
+ qphy->soc_min_rev);
+
+ return qphy->soc_min_rev;
+};
+#else
+/* Reads the SoC version */
+static int qusb_phy_get_socrev(struct device *dev, struct qusb_phy *qphy)
+{
+ return 0;
+}
+#endif
+
static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on)
{
dev_dbg(qphy->phy.dev, "%s(): clocks_enabled:%d on:%d\n",
@@ -1125,6 +1183,11 @@
return PTR_ERR(qphy->vdda18);
}
+ ret = qusb_phy_get_socrev(&pdev->dev, qphy);
+ if (ret == -EPROBE_DEFER) {
+ dev_err(&pdev->dev, "SoC version rd: fail: defer for now\n");
+ return ret;
+ }
qphy->pinctrl = devm_pinctrl_get(dev);
if (IS_ERR(qphy->pinctrl)) {
ret = PTR_ERR(qphy->pinctrl);
@@ -1159,7 +1222,14 @@
qphy->phy.type = USB_PHY_TYPE_USB2;
qphy->phy.notify_connect = qusb_phy_notify_connect;
qphy->phy.notify_disconnect = qusb_phy_notify_disconnect;
- qphy->phy.disable_chirp = qusb_phy_disable_chirp;
+
+ /*
+ * qusb_phy_disable_chirp is not required if soc version is
+ * mentioned and is not base version.
+ */
+ if (qphy->soc_min_rev == 0)
+ qphy->phy.disable_chirp = qusb_phy_disable_chirp;
+
qphy->phy.start_port_reset = qusb_phy_enable_ext_pulldown;
ret = usb_add_phy_dev(&qphy->phy);
diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c
index 8134579..ad3bbf7 100644
--- a/drivers/usb/phy/phy-msm-ssusb-qmp.c
+++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -112,6 +112,7 @@
struct usb_phy phy;
void __iomem *base;
void __iomem *vls_clamp_reg;
+ void __iomem *pcs_clamp_enable_reg;
void __iomem *tcsr_usb3_dp_phymode;
struct regulator *vdd;
@@ -187,6 +188,8 @@
case USB_PHY_TYPE_USB3_OR_DP:
case USB_PHY_TYPE_USB3:
writel_relaxed(!!val, phy->vls_clamp_reg);
+ if (phy->pcs_clamp_enable_reg)
+ writel_relaxed(!val, phy->pcs_clamp_enable_reg);
break;
default:
break;
@@ -892,6 +895,16 @@
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "pcs_clamp_enable_reg");
+ if (res) {
+ phy->pcs_clamp_enable_reg = devm_ioremap_resource(dev, res);
+ if (IS_ERR(phy->pcs_clamp_enable_reg)) {
+ dev_err(dev, "err getting pcs_clamp_enable_reg address.\n");
+ return PTR_ERR(phy->pcs_clamp_enable_reg);
+ }
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"tcsr_usb3_dp_phymode");
if (res) {
phy->tcsr_usb3_dp_phymode = devm_ioremap_resource(dev, res);
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
new file mode 100644
index 0000000..c5cdddc
--- /dev/null
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -0,0 +1,5473 @@
+/* Copyright (c) 2009-2018, Linux Foundation. All rights reserved.
+ *
+ * This 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/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/pm_runtime.h>
+#include <linux/suspend.h>
+#include <linux/of.h>
+#include <linux/dma-mapping.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/irqchip/msm-mpm-irq.h>
+#include <linux/pm_wakeup.h>
+#include <linux/reset.h>
+#include <linux/extcon.h>
+#include <soc/qcom/scm.h>
+
+#include <linux/usb.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/ulpi.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/msm_hsusb.h>
+#include <linux/usb/msm_hsusb_hw.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/qpnp/qpnp-adc.h>
+
+#include <linux/msm-bus.h>
+
+/**
+ * Requested USB votes for BUS bandwidth
+ *
+ * USB_NO_PERF_VOTE BUS Vote for inactive USB session or disconnect
+ * USB_MAX_PERF_VOTE Maximum BUS bandwidth vote
+ * USB_MIN_PERF_VOTE Minimum BUS bandwidth vote (for some hw same as NO_PERF)
+ *
+ */
+enum usb_bus_vote {
+ USB_NO_PERF_VOTE = 0,
+ USB_MAX_PERF_VOTE,
+ USB_MIN_PERF_VOTE,
+};
+
+/**
+ * Supported USB modes
+ *
+ * USB_PERIPHERAL Only peripheral mode is supported.
+ * USB_HOST Only host mode is supported.
+ * USB_OTG OTG mode is supported.
+ *
+ */
+enum usb_mode_type {
+ USB_NONE = 0,
+ USB_PERIPHERAL,
+ USB_HOST,
+ USB_OTG,
+};
+
+/**
+ * OTG control
+ *
+ * OTG_NO_CONTROL Id/VBUS notifications not required. Useful in host
+ * only configuration.
+ * OTG_PHY_CONTROL Id/VBUS notifications comes form USB PHY.
+ * OTG_PMIC_CONTROL Id/VBUS notifications comes from PMIC hardware.
+ * OTG_USER_CONTROL Id/VBUS notifcations comes from User via sysfs.
+ *
+ */
+enum otg_control_type {
+ OTG_NO_CONTROL = 0,
+ OTG_PHY_CONTROL,
+ OTG_PMIC_CONTROL,
+ OTG_USER_CONTROL,
+};
+
+/**
+ * PHY used in
+ *
+ * INVALID_PHY Unsupported PHY
+ * CI_PHY Chipidea PHY
+ * SNPS_PICO_PHY Synopsis Pico PHY
+ * SNPS_FEMTO_PHY Synopsis Femto PHY
+ * QUSB_ULPI_PHY
+ *
+ */
+enum msm_usb_phy_type {
+ INVALID_PHY = 0,
+ CI_PHY, /* not supported */
+ SNPS_PICO_PHY,
+ SNPS_FEMTO_PHY,
+ QUSB_ULPI_PHY,
+};
+
+#define IDEV_CHG_MAX 1500
+#define IUNIT 100
+#define IDEV_HVDCP_CHG_MAX 1800
+
+/**
+ * Used different VDDCX voltage values
+ */
+enum usb_vdd_value {
+ VDD_NONE = 0,
+ VDD_MIN,
+ VDD_MAX,
+ VDD_VAL_MAX,
+};
+
+/**
+ * struct msm_otg_platform_data - platform device data
+ * for msm_otg driver.
+ * @phy_init_seq: PHY configuration sequence values. Value of -1 is reserved as
+ * "do not overwrite default value at this address".
+ * @vbus_power: VBUS power on/off routine.It should return result
+ * as success(zero value) or failure(non-zero value).
+ * @power_budget: VBUS power budget in mA (0 will be treated as 500mA).
+ * @mode: Supported mode (OTG/peripheral/host).
+ * @otg_control: OTG switch controlled by user/Id pin
+ * @default_mode: Default operational mode. Applicable only if
+ * OTG switch is controller by user.
+ * @pmic_id_irq: IRQ number assigned for PMIC USB ID line.
+ * @mpm_otgsessvld_int: MPM wakeup pin assigned for OTG SESSVLD
+ * interrupt. Used when .otg_control == OTG_PHY_CONTROL.
+ * @mpm_dpshv_int: MPM wakeup pin assigned for DP SHV interrupt.
+ * Used during host bus suspend.
+ * @mpm_dmshv_int: MPM wakeup pin assigned for DM SHV interrupt.
+ * Used during host bus suspend.
+ * @disable_reset_on_disconnect: perform USB PHY and LINK reset
+ * on USB cable disconnection.
+ * @pnoc_errata_fix: workaround needed for PNOC hardware bug that
+ * affects USB performance.
+ * @enable_lpm_on_suspend: Enable the USB core to go into Low
+ * Power Mode, when USB bus is suspended but cable
+ * is connected.
+ * @core_clk_always_on_workaround: Don't disable core_clk when
+ * USB enters LPM.
+ * @delay_lpm_on_disconnect: Use a delay before entering LPM
+ * upon USB cable disconnection.
+ * @enable_sec_phy: Use second HSPHY with USB2 core
+ * @bus_scale_table: parameters for bus bandwidth requirements
+ * @log2_itc: value of 2^(log2_itc-1) will be used as the
+ * interrupt threshold (ITC), when log2_itc is
+ * between 1 to 7.
+ * @l1_supported: enable link power management support.
+ * @dpdm_pulldown_added: Indicates whether pull down resistors are
+ * connected on data lines or not.
+ * @vddmin_gpio: dedictaed gpio in the platform that is used for
+ * pullup the D+ line in case of bus suspend with
+ * phy retention.
+ * @enable_ahb2ahb_bypass: Indicates whether enable AHB2AHB BYPASS
+ * mode with controller in device mode.
+ * @bool disable_retention_with_vdd_min: Indicates whether to enable
+ allowing VDDmin without putting PHY into retention.
+ * @bool enable_phy_id_pullup: Indicates whether phy id pullup is
+ enabled or not.
+ * @usb_id_gpio: Gpio used for USB ID detection.
+ * @hub_reset_gpio: Gpio used for hub reset.
+ * @switch_sel_gpio: Gpio used for controlling switch that
+ routing D+/D- from the USB HUB to the USB jack type B
+ for peripheral mode.
+ * @bool phy_dvdd_always_on: PHY DVDD is supplied by always on PMIC LDO.
+ * @bool emulation: Indicates whether we are running on emulation platform.
+ * @bool enable_streaming: Indicates whether streaming to be enabled by default.
+ * @bool enable_axi_prefetch: Indicates whether AXI Prefetch interface is used
+ for improving data performance.
+ * @bool enable_sdp_typec_current_limit: Indicates whether type-c current for
+ sdp charger to be limited.
+ * @usbeth_reset_gpio: Gpio used for external usb-to-eth reset.
+ */
+struct msm_otg_platform_data {
+ int *phy_init_seq;
+ int phy_init_sz;
+ int (*vbus_power)(bool on);
+ unsigned int power_budget;
+ enum usb_mode_type mode;
+ enum otg_control_type otg_control;
+ enum usb_mode_type default_mode;
+ enum msm_usb_phy_type phy_type;
+ int pmic_id_irq;
+ unsigned int mpm_otgsessvld_int;
+ unsigned int mpm_dpshv_int;
+ unsigned int mpm_dmshv_int;
+ bool disable_reset_on_disconnect;
+ bool pnoc_errata_fix;
+ bool enable_lpm_on_dev_suspend;
+ bool core_clk_always_on_workaround;
+ bool delay_lpm_on_disconnect;
+ bool dp_manual_pullup;
+ bool enable_sec_phy;
+ struct msm_bus_scale_pdata *bus_scale_table;
+ int log2_itc;
+ bool l1_supported;
+ bool dpdm_pulldown_added;
+ int vddmin_gpio;
+ bool enable_ahb2ahb_bypass;
+ bool disable_retention_with_vdd_min;
+ bool enable_phy_id_pullup;
+ int usb_id_gpio;
+ int hub_reset_gpio;
+ int usbeth_reset_gpio;
+ int switch_sel_gpio;
+ bool phy_dvdd_always_on;
+ bool emulation;
+ bool enable_streaming;
+ bool enable_axi_prefetch;
+ bool enable_sdp_typec_current_limit;
+ bool vbus_low_as_hostmode;
+};
+
+#define USB_CHG_BLOCK_ULPI 1
+
+#define USB_REQUEST_5V 1
+#define USB_REQUEST_9V 2
+/**
+ * struct msm_usb_chg_info - MSM USB charger block details.
+ * @chg_block_type: The type of charger block. QSCRATCH/ULPI.
+ * @page_offset: USB charger register base may not be aligned to
+ * PAGE_SIZE. The kernel driver aligns the base
+ * address and use it for memory mapping. This
+ * page_offset is used by user space to calaculate
+ * the corret charger register base address.
+ * @length: The length of the charger register address space.
+ */
+struct msm_usb_chg_info {
+ uint32_t chg_block_type;
+ __kernel_off_t page_offset;
+ size_t length;
+};
+
+/* Get the MSM USB charger block information */
+#define MSM_USB_EXT_CHG_INFO _IOW('M', 0, struct msm_usb_chg_info)
+
+/* Vote against USB hardware low power mode */
+#define MSM_USB_EXT_CHG_BLOCK_LPM _IOW('M', 1, int)
+
+/* To tell kernel about voltage being voted */
+#define MSM_USB_EXT_CHG_VOLTAGE_INFO _IOW('M', 2, int)
+
+/* To tell kernel about voltage request result */
+#define MSM_USB_EXT_CHG_RESULT _IOW('M', 3, int)
+
+/* To tell kernel whether charger connected is external charger or not */
+#define MSM_USB_EXT_CHG_TYPE _IOW('M', 4, int)
+
+#define MSM_USB_BASE (motg->regs)
+#define MSM_USB_PHY_CSR_BASE (motg->phy_csr_regs)
+
+#define DRIVER_NAME "msm_otg"
+
+#define CHG_RECHECK_DELAY (jiffies + msecs_to_jiffies(2000))
+#define ULPI_IO_TIMEOUT_USEC (10 * 1000)
+#define USB_PHY_3P3_VOL_MIN 3050000 /* uV */
+#define USB_PHY_3P3_VOL_MAX 3300000 /* uV */
+#define USB_PHY_3P3_HPM_LOAD 50000 /* uA */
+#define USB_PHY_3P3_LPM_LOAD 4000 /* uA */
+
+#define USB_PHY_1P8_VOL_MIN 1800000 /* uV */
+#define USB_PHY_1P8_VOL_MAX 1800000 /* uV */
+#define USB_PHY_1P8_HPM_LOAD 50000 /* uA */
+#define USB_PHY_1P8_LPM_LOAD 4000 /* uA */
+
+#define USB_PHY_VDD_DIG_VOL_NONE 0 /*uV */
+#define USB_PHY_VDD_DIG_VOL_MIN 1045000 /* uV */
+#define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */
+
+#define USB_SUSPEND_DELAY_TIME (500 * HZ/1000) /* 500 msec */
+
+#define USB_DEFAULT_SYSTEM_CLOCK 80000000 /* 80 MHz */
+
+#define PM_QOS_SAMPLE_SEC 2
+#define PM_QOS_THRESHOLD 400
+
+#define MICRO_5V 5000000
+#define MICRO_9V 9000000
+
+#define SDP_CURRENT_UA 500000
+#define CDP_CURRENT_UA 1500000
+#define DCP_CURRENT_UA 1500000
+#define HVDCP_CURRENT_UA 3000000
+
+enum msm_otg_phy_reg_mode {
+ USB_PHY_REG_OFF,
+ USB_PHY_REG_ON,
+ USB_PHY_REG_LPM_ON,
+ USB_PHY_REG_LPM_OFF,
+ USB_PHY_REG_3P3_ON,
+ USB_PHY_REG_3P3_OFF,
+};
+
+static char *override_phy_init;
+module_param(override_phy_init, charp, 0644);
+MODULE_PARM_DESC(override_phy_init,
+ "Override HSUSB PHY Init Settings");
+
+unsigned int lpm_disconnect_thresh = 1000;
+module_param(lpm_disconnect_thresh, uint, 0644);
+MODULE_PARM_DESC(lpm_disconnect_thresh,
+ "Delay before entering LPM on USB disconnect");
+
+static bool floated_charger_enable;
+module_param(floated_charger_enable, bool, 0644);
+MODULE_PARM_DESC(floated_charger_enable,
+ "Whether to enable floated charger");
+
+/* by default debugging is enabled */
+static unsigned int enable_dbg_log = 1;
+module_param(enable_dbg_log, uint, 0644);
+MODULE_PARM_DESC(enable_dbg_log, "Debug buffer events");
+
+/* Max current to be drawn for HVDCP charger */
+static int hvdcp_max_current = IDEV_HVDCP_CHG_MAX;
+module_param(hvdcp_max_current, int, 0644);
+MODULE_PARM_DESC(hvdcp_max_current, "max current drawn for HVDCP charger");
+
+/* Max current to be drawn for DCP charger */
+static int dcp_max_current = IDEV_CHG_MAX;
+module_param(dcp_max_current, int, 0644);
+MODULE_PARM_DESC(dcp_max_current, "max current drawn for DCP charger");
+
+static DECLARE_COMPLETION(pmic_vbus_init);
+static struct msm_otg *the_msm_otg;
+static bool debug_bus_voting_enabled;
+
+static struct regulator *hsusb_3p3;
+static struct regulator *hsusb_1p8;
+static struct regulator *hsusb_vdd;
+static struct regulator *vbus_otg;
+static struct power_supply *psy;
+
+static int vdd_val[VDD_VAL_MAX];
+static u32 bus_freqs[USB_NOC_NUM_VOTE][USB_NUM_BUS_CLOCKS] /*bimc,snoc,pcnoc*/;
+static char bus_clkname[USB_NUM_BUS_CLOCKS][20] = {"bimc_clk", "snoc_clk",
+ "pcnoc_clk"};
+static bool bus_clk_rate_set;
+
+static void dbg_inc(unsigned int *idx)
+{
+ *idx = (*idx + 1) & (DEBUG_MAX_MSG-1);
+}
+
+static void
+msm_otg_dbg_log_event(struct usb_phy *phy, char *event, int d1, int d2)
+{
+ struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+ unsigned long flags;
+ unsigned long long t;
+ unsigned long nanosec;
+
+ if (!enable_dbg_log)
+ return;
+
+ write_lock_irqsave(&motg->dbg_lock, flags);
+ t = cpu_clock(smp_processor_id());
+ nanosec = do_div(t, 1000000000)/1000;
+ scnprintf(motg->buf[motg->dbg_idx], DEBUG_MSG_LEN,
+ "[%5lu.%06lu]: %s :%d:%d",
+ (unsigned long)t, nanosec, event, d1, d2);
+
+ motg->dbg_idx++;
+ motg->dbg_idx = motg->dbg_idx % DEBUG_MAX_MSG;
+ write_unlock_irqrestore(&motg->dbg_lock, flags);
+}
+
+static int msm_hsusb_ldo_init(struct msm_otg *motg, int init)
+{
+ int rc = 0;
+
+ if (init) {
+ hsusb_3p3 = devm_regulator_get(motg->phy.dev, "HSUSB_3p3");
+ if (IS_ERR(hsusb_3p3)) {
+ dev_err(motg->phy.dev, "unable to get hsusb 3p3\n");
+ return PTR_ERR(hsusb_3p3);
+ }
+
+ rc = regulator_set_voltage(hsusb_3p3, USB_PHY_3P3_VOL_MIN,
+ USB_PHY_3P3_VOL_MAX);
+ if (rc) {
+ dev_err(motg->phy.dev, "unable to set voltage level for hsusb 3p3\n"
+ );
+ return rc;
+ }
+ hsusb_1p8 = devm_regulator_get(motg->phy.dev, "HSUSB_1p8");
+ if (IS_ERR(hsusb_1p8)) {
+ dev_err(motg->phy.dev, "unable to get hsusb 1p8\n");
+ rc = PTR_ERR(hsusb_1p8);
+ goto put_3p3_lpm;
+ }
+ rc = regulator_set_voltage(hsusb_1p8, USB_PHY_1P8_VOL_MIN,
+ USB_PHY_1P8_VOL_MAX);
+ if (rc) {
+ dev_err(motg->phy.dev, "unable to set voltage level for hsusb 1p8\n"
+ );
+ goto put_1p8;
+ }
+
+ return 0;
+ }
+
+put_1p8:
+ regulator_set_voltage(hsusb_1p8, 0, USB_PHY_1P8_VOL_MAX);
+put_3p3_lpm:
+ regulator_set_voltage(hsusb_3p3, 0, USB_PHY_3P3_VOL_MAX);
+ return rc;
+}
+
+static int msm_hsusb_config_vddcx(int high)
+{
+ struct msm_otg *motg = the_msm_otg;
+ int max_vol = vdd_val[VDD_MAX];
+ int min_vol;
+ int ret;
+
+ min_vol = vdd_val[!!high];
+ ret = regulator_set_voltage(hsusb_vdd, min_vol, max_vol);
+ if (ret) {
+ pr_err("%s: unable to set the voltage for regulator HSUSB_VDDCX\n",
+ __func__);
+ return ret;
+ }
+
+ pr_debug("%s: min_vol:%d max_vol:%d\n", __func__, min_vol, max_vol);
+ msm_otg_dbg_log_event(&motg->phy, "CONFIG VDDCX", min_vol, max_vol);
+
+ return ret;
+}
+
+static int msm_hsusb_ldo_enable(struct msm_otg *motg,
+ enum msm_otg_phy_reg_mode mode)
+{
+ int ret = 0;
+
+ if (IS_ERR(hsusb_1p8)) {
+ pr_err("%s: HSUSB_1p8 is not initialized\n", __func__);
+ return -ENODEV;
+ }
+
+ if (IS_ERR(hsusb_3p3)) {
+ pr_err("%s: HSUSB_3p3 is not initialized\n", __func__);
+ return -ENODEV;
+ }
+
+ switch (mode) {
+ case USB_PHY_REG_ON:
+ ret = regulator_set_load(hsusb_1p8, USB_PHY_1P8_HPM_LOAD);
+ if (ret < 0) {
+ pr_err("%s: Unable to set HPM of the regulator HSUSB_1p8\n",
+ __func__);
+ return ret;
+ }
+
+ ret = regulator_enable(hsusb_1p8);
+ if (ret) {
+ dev_err(motg->phy.dev, "%s: unable to enable the hsusb 1p8\n",
+ __func__);
+ regulator_set_load(hsusb_1p8, 0);
+ return ret;
+ }
+
+ /* fall through */
+ case USB_PHY_REG_3P3_ON:
+ ret = regulator_set_load(hsusb_3p3, USB_PHY_3P3_HPM_LOAD);
+ if (ret < 0) {
+ pr_err("%s: Unable to set HPM of the regulator HSUSB_3p3\n",
+ __func__);
+ if (mode == USB_PHY_REG_ON) {
+ regulator_set_load(hsusb_1p8, 0);
+ regulator_disable(hsusb_1p8);
+ }
+ return ret;
+ }
+
+ ret = regulator_enable(hsusb_3p3);
+ if (ret) {
+ dev_err(motg->phy.dev, "%s: unable to enable the hsusb 3p3\n",
+ __func__);
+ regulator_set_load(hsusb_3p3, 0);
+ if (mode == USB_PHY_REG_ON) {
+ regulator_set_load(hsusb_1p8, 0);
+ regulator_disable(hsusb_1p8);
+ }
+ return ret;
+ }
+
+ break;
+
+ case USB_PHY_REG_OFF:
+ ret = regulator_disable(hsusb_1p8);
+ if (ret) {
+ dev_err(motg->phy.dev, "%s: unable to disable the hsusb 1p8\n",
+ __func__);
+ return ret;
+ }
+
+ ret = regulator_set_load(hsusb_1p8, 0);
+ if (ret < 0)
+ pr_err("%s: Unable to set LPM of the regulator HSUSB_1p8\n",
+ __func__);
+
+ /* fall through */
+ case USB_PHY_REG_3P3_OFF:
+ ret = regulator_disable(hsusb_3p3);
+ if (ret) {
+ dev_err(motg->phy.dev, "%s: unable to disable the hsusb 3p3\n",
+ __func__);
+ return ret;
+ }
+ ret = regulator_set_load(hsusb_3p3, 0);
+ if (ret < 0)
+ pr_err("%s: Unable to set LPM of the regulator HSUSB_3p3\n",
+ __func__);
+
+ break;
+
+ case USB_PHY_REG_LPM_ON:
+ ret = regulator_set_load(hsusb_1p8, USB_PHY_1P8_LPM_LOAD);
+ if (ret < 0) {
+ pr_err("%s: Unable to set LPM of the regulator: HSUSB_1p8\n",
+ __func__);
+ return ret;
+ }
+
+ ret = regulator_set_load(hsusb_3p3, USB_PHY_3P3_LPM_LOAD);
+ if (ret < 0) {
+ pr_err("%s: Unable to set LPM of the regulator: HSUSB_3p3\n",
+ __func__);
+ regulator_set_load(hsusb_1p8, USB_PHY_REG_ON);
+ return ret;
+ }
+
+ break;
+
+ case USB_PHY_REG_LPM_OFF:
+ ret = regulator_set_load(hsusb_1p8, USB_PHY_1P8_HPM_LOAD);
+ if (ret < 0) {
+ pr_err("%s: Unable to set HPM of the regulator: HSUSB_1p8\n",
+ __func__);
+ return ret;
+ }
+
+ ret = regulator_set_load(hsusb_3p3, USB_PHY_3P3_HPM_LOAD);
+ if (ret < 0) {
+ pr_err("%s: Unable to set HPM of the regulator: HSUSB_3p3\n",
+ __func__);
+ regulator_set_load(hsusb_1p8, USB_PHY_REG_ON);
+ return ret;
+ }
+
+ break;
+
+ default:
+ pr_err("%s: Unsupported mode (%d).", __func__, mode);
+ return -ENOTSUPP;
+ }
+
+ pr_debug("%s: USB reg mode (%d) (OFF/HPM/LPM)\n", __func__, mode);
+ msm_otg_dbg_log_event(&motg->phy, "USB REG MODE", mode, ret);
+ return ret < 0 ? ret : 0;
+}
+
+static int ulpi_read(struct usb_phy *phy, u32 reg)
+{
+ struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+ int cnt = 0;
+
+ if (motg->pdata->emulation)
+ return 0;
+
+ if (motg->pdata->phy_type == QUSB_ULPI_PHY && reg > 0x3F) {
+ pr_debug("%s: ULPI vendor-specific reg 0x%02x not supported\n",
+ __func__, reg);
+ return 0;
+ }
+
+ /* initiate read operation */
+ writel_relaxed(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg),
+ USB_ULPI_VIEWPORT);
+
+ /* wait for completion */
+ while (cnt < ULPI_IO_TIMEOUT_USEC) {
+ if (!(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN))
+ break;
+ udelay(1);
+ cnt++;
+ }
+
+ if (cnt >= ULPI_IO_TIMEOUT_USEC) {
+ dev_err(phy->dev, "ulpi_read: timeout %08x\n",
+ readl_relaxed(USB_ULPI_VIEWPORT));
+ dev_err(phy->dev, "PORTSC: %08x USBCMD: %08x\n",
+ readl_relaxed(USB_PORTSC), readl_relaxed(USB_USBCMD));
+ return -ETIMEDOUT;
+ }
+ return ULPI_DATA_READ(readl_relaxed(USB_ULPI_VIEWPORT));
+}
+
+static int ulpi_write(struct usb_phy *phy, u32 val, u32 reg)
+{
+ struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+ int cnt = 0;
+
+ if (motg->pdata->emulation)
+ return 0;
+
+ if (motg->pdata->phy_type == QUSB_ULPI_PHY && reg > 0x3F) {
+ pr_debug("%s: ULPI vendor-specific reg 0x%02x not supported\n",
+ __func__, reg);
+ return 0;
+ }
+
+ /* initiate write operation */
+ writel_relaxed(ULPI_RUN | ULPI_WRITE |
+ ULPI_ADDR(reg) | ULPI_DATA(val),
+ USB_ULPI_VIEWPORT);
+
+ /* wait for completion */
+ while (cnt < ULPI_IO_TIMEOUT_USEC) {
+ if (!(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN))
+ break;
+ udelay(1);
+ cnt++;
+ }
+
+ if (cnt >= ULPI_IO_TIMEOUT_USEC) {
+ dev_err(phy->dev, "ulpi_write: timeout\n");
+ dev_err(phy->dev, "PORTSC: %08x USBCMD: %08x\n",
+ readl_relaxed(USB_PORTSC), readl_relaxed(USB_USBCMD));
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+static struct usb_phy_io_ops msm_otg_io_ops = {
+ .read = ulpi_read,
+ .write = ulpi_write,
+};
+
+static void ulpi_init(struct msm_otg *motg)
+{
+ struct msm_otg_platform_data *pdata = motg->pdata;
+ int aseq[10];
+ int *seq = NULL;
+
+ if (override_phy_init) {
+ pr_debug("%s(): HUSB PHY Init:%s\n", __func__,
+ override_phy_init);
+ get_options(override_phy_init, ARRAY_SIZE(aseq), aseq);
+ seq = &aseq[1];
+ } else {
+ seq = pdata->phy_init_seq;
+ }
+
+ if (!seq)
+ return;
+
+ while (seq[0] >= 0) {
+ if (override_phy_init)
+ pr_debug("ulpi: write 0x%02x to 0x%02x\n",
+ seq[0], seq[1]);
+
+ dev_vdbg(motg->phy.dev, "ulpi: write 0x%02x to 0x%02x\n",
+ seq[0], seq[1]);
+ msm_otg_dbg_log_event(&motg->phy, "ULPI WRITE", seq[0], seq[1]);
+ ulpi_write(&motg->phy, seq[0], seq[1]);
+ seq += 2;
+ }
+}
+
+static int msm_otg_phy_clk_reset(struct msm_otg *motg)
+{
+ int ret;
+
+ if (!motg->phy_reset_clk)
+ return 0;
+
+ if (motg->sleep_clk)
+ clk_disable_unprepare(motg->sleep_clk);
+ if (motg->phy_csr_clk)
+ clk_disable_unprepare(motg->phy_csr_clk);
+
+ ret = reset_control_assert(motg->phy_reset);
+ if (ret) {
+ pr_err("phy_reset_clk assert failed %d\n", ret);
+ return ret;
+ }
+ /*
+ * As per databook, 10 usec delay is required between
+ * PHY POR assert and de-assert.
+ */
+ usleep_range(10, 15);
+ ret = reset_control_deassert(motg->phy_reset);
+ if (ret) {
+ pr_err("phy_reset_clk de-assert failed %d\n", ret);
+ return ret;
+ }
+ /*
+ * As per databook, it takes 75 usec for PHY to stabilize
+ * after the reset.
+ */
+ usleep_range(80, 100);
+
+ if (motg->phy_csr_clk)
+ clk_prepare_enable(motg->phy_csr_clk);
+ if (motg->sleep_clk)
+ clk_prepare_enable(motg->sleep_clk);
+
+ return 0;
+}
+
+static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert)
+{
+ int ret;
+
+ if (assert) {
+ /* Using asynchronous block reset to the hardware */
+ dev_dbg(motg->phy.dev, "block_reset ASSERT\n");
+ clk_disable_unprepare(motg->pclk);
+ clk_disable_unprepare(motg->core_clk);
+ ret = reset_control_assert(motg->core_reset);
+ if (ret)
+ dev_err(motg->phy.dev, "usb hs_clk assert failed\n");
+ } else {
+ dev_dbg(motg->phy.dev, "block_reset DEASSERT\n");
+ ret = reset_control_deassert(motg->core_reset);
+ ndelay(200);
+ ret = clk_prepare_enable(motg->core_clk);
+ WARN(ret, "USB core_clk enable failed\n");
+ ret = clk_prepare_enable(motg->pclk);
+ WARN(ret, "USB pclk enable failed\n");
+ if (ret)
+ dev_err(motg->phy.dev, "usb hs_clk deassert failed\n");
+ }
+ return ret;
+}
+
+static int msm_otg_phy_reset(struct msm_otg *motg)
+{
+ u32 val;
+ int ret;
+ struct msm_otg_platform_data *pdata = motg->pdata;
+
+ /*
+ * AHB2AHB Bypass mode shouldn't be enable before doing
+ * async clock reset. If it is enable, disable the same.
+ */
+ val = readl_relaxed(USB_AHBMODE);
+ if (val & AHB2AHB_BYPASS) {
+ pr_err("%s(): AHB2AHB_BYPASS SET: AHBMODE:%x\n",
+ __func__, val);
+ val &= ~AHB2AHB_BYPASS_BIT_MASK;
+ writel_relaxed(val | AHB2AHB_BYPASS_CLEAR, USB_AHBMODE);
+ pr_err("%s(): AHBMODE: %x\n", __func__,
+ readl_relaxed(USB_AHBMODE));
+ }
+
+ ret = msm_otg_link_clk_reset(motg, 1);
+ if (ret)
+ return ret;
+
+ msm_otg_phy_clk_reset(motg);
+
+ /* wait for 1ms delay as suggested in HPG. */
+ usleep_range(1000, 1200);
+
+ ret = msm_otg_link_clk_reset(motg, 0);
+ if (ret)
+ return ret;
+
+ if (pdata && pdata->enable_sec_phy)
+ writel_relaxed(readl_relaxed(USB_PHY_CTRL2) | (1<<16),
+ USB_PHY_CTRL2);
+ val = readl_relaxed(USB_PORTSC) & ~PORTSC_PTS_MASK;
+ writel_relaxed(val | PORTSC_PTS_ULPI, USB_PORTSC);
+
+ dev_info(motg->phy.dev, "phy_reset: success\n");
+ msm_otg_dbg_log_event(&motg->phy, "PHY RESET SUCCESS",
+ motg->inputs, motg->phy.otg->state);
+ return 0;
+}
+
+#define LINK_RESET_TIMEOUT_USEC (250 * 1000)
+static int msm_otg_link_reset(struct msm_otg *motg)
+{
+ int cnt = 0;
+ struct msm_otg_platform_data *pdata = motg->pdata;
+
+ writel_relaxed(USBCMD_RESET, USB_USBCMD);
+ while (cnt < LINK_RESET_TIMEOUT_USEC) {
+ if (!(readl_relaxed(USB_USBCMD) & USBCMD_RESET))
+ break;
+ udelay(1);
+ cnt++;
+ }
+ if (cnt >= LINK_RESET_TIMEOUT_USEC)
+ return -ETIMEDOUT;
+
+ /* select ULPI phy */
+ writel_relaxed(0x80000000, USB_PORTSC);
+ writel_relaxed(0x0, USB_AHBBURST);
+ writel_relaxed(0x08, USB_AHBMODE);
+
+ if (pdata && pdata->enable_sec_phy)
+ writel_relaxed(readl_relaxed(USB_PHY_CTRL2) | (1<<16),
+ USB_PHY_CTRL2);
+ return 0;
+}
+
+#define QUSB2PHY_PORT_POWERDOWN 0xB4
+#define QUSB2PHY_PORT_UTMI_CTRL2 0xC4
+
+static void msm_usb_phy_reset(struct msm_otg *motg)
+{
+ u32 val;
+ int ret, *seq;
+
+ switch (motg->pdata->phy_type) {
+ case SNPS_PICO_PHY:
+ /* Assert USB PHY_PON */
+ val = readl_relaxed(motg->usb_phy_ctrl_reg);
+ val &= ~PHY_POR_BIT_MASK;
+ val |= PHY_POR_ASSERT;
+ writel_relaxed(val, motg->usb_phy_ctrl_reg);
+
+ /* wait for minimum 10 microseconds as
+ * suggested in HPG.
+ */
+ usleep_range(10, 15);
+
+ /* Deassert USB PHY_PON */
+ val = readl_relaxed(motg->usb_phy_ctrl_reg);
+ val &= ~PHY_POR_BIT_MASK;
+ val |= PHY_POR_DEASSERT;
+ writel_relaxed(val, motg->usb_phy_ctrl_reg);
+ break;
+ case QUSB_ULPI_PHY:
+ ret = reset_control_assert(motg->phy_reset);
+ if (ret) {
+ pr_err("phy_reset_clk assert failed %d\n", ret);
+ break;
+ }
+
+ /* need to delay 10us for PHY to reset */
+ usleep_range(10, 20);
+
+ ret = reset_control_deassert(motg->phy_reset);
+ if (ret) {
+ pr_err("phy_reset_clk de-assert failed %d\n", ret);
+ break;
+ }
+
+ /* Ensure that RESET operation is completed. */
+ mb();
+
+ writel_relaxed(0x23,
+ motg->phy_csr_regs + QUSB2PHY_PORT_POWERDOWN);
+ writel_relaxed(0x0,
+ motg->phy_csr_regs + QUSB2PHY_PORT_UTMI_CTRL2);
+
+ /* Program tuning parameters for PHY */
+ seq = motg->pdata->phy_init_seq;
+ if (seq) {
+ while (seq[0] >= 0) {
+ writel_relaxed(seq[1],
+ motg->phy_csr_regs + seq[0]);
+ seq += 2;
+ }
+ }
+
+ /* ensure above writes are completed before re-enabling PHY */
+ wmb();
+ writel_relaxed(0x22,
+ motg->phy_csr_regs + QUSB2PHY_PORT_POWERDOWN);
+ break;
+ case SNPS_FEMTO_PHY:
+ if (!motg->phy_por_clk) {
+ pr_err("phy_por_clk missing\n");
+ break;
+ }
+ ret = reset_control_assert(motg->phy_por_reset);
+ if (ret) {
+ pr_err("phy_por_clk assert failed %d\n", ret);
+ break;
+ }
+ /*
+ * The Femto PHY is POR reset in the following scenarios.
+ *
+ * 1. After overriding the parameter registers.
+ * 2. Low power mode exit from PHY retention.
+ *
+ * Ensure that SIDDQ is cleared before bringing the PHY
+ * out of reset.
+ *
+ */
+
+ val = readb_relaxed(USB_PHY_CSR_PHY_CTRL_COMMON0);
+ val &= ~SIDDQ;
+ writeb_relaxed(val, USB_PHY_CSR_PHY_CTRL_COMMON0);
+
+ /*
+ * As per databook, 10 usec delay is required between
+ * PHY POR assert and de-assert.
+ */
+ usleep_range(10, 20);
+ ret = reset_control_deassert(motg->phy_por_reset);
+ if (ret) {
+ pr_err("phy_por_clk de-assert failed %d\n", ret);
+ break;
+ }
+ /*
+ * As per databook, it takes 75 usec for PHY to stabilize
+ * after the reset.
+ */
+ usleep_range(80, 100);
+ break;
+ default:
+ break;
+ }
+ /* Ensure that RESET operation is completed. */
+ mb();
+}
+
+static int msm_otg_reset(struct usb_phy *phy)
+{
+ struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+ struct msm_otg_platform_data *pdata = motg->pdata;
+ int ret;
+ u32 val = 0;
+ u32 ulpi_val = 0;
+
+ msm_otg_dbg_log_event(&motg->phy, "USB RESET", phy->otg->state,
+ get_pm_runtime_counter(phy->dev));
+ /*
+ * USB PHY and Link reset also reset the USB BAM.
+ * Thus perform reset operation only once to avoid
+ * USB BAM reset on other cases e.g. USB cable disconnections.
+ * If hardware reported error then it must be reset for recovery.
+ */
+ if (motg->err_event_seen)
+ dev_info(phy->dev, "performing USB h/w reset for recovery\n");
+ else if (pdata->disable_reset_on_disconnect && motg->reset_counter)
+ return 0;
+
+ motg->reset_counter++;
+
+ disable_irq(motg->irq);
+ if (motg->phy_irq)
+ disable_irq(motg->phy_irq);
+
+ ret = msm_otg_phy_reset(motg);
+ if (ret) {
+ dev_err(phy->dev, "phy_reset failed\n");
+ if (motg->phy_irq)
+ enable_irq(motg->phy_irq);
+
+ enable_irq(motg->irq);
+ return ret;
+ }
+
+ if (motg->phy_irq)
+ enable_irq(motg->phy_irq);
+
+ enable_irq(motg->irq);
+ ret = msm_otg_link_reset(motg);
+ if (ret) {
+ dev_err(phy->dev, "link reset failed\n");
+ return ret;
+ }
+
+ msleep(100);
+
+ /* Reset USB PHY after performing USB Link RESET */
+ msm_usb_phy_reset(motg);
+
+ /* Program USB PHY Override registers. */
+ ulpi_init(motg);
+
+ /*
+ * It is required to reset USB PHY after programming
+ * the USB PHY Override registers to get the new
+ * values into effect.
+ */
+ msm_usb_phy_reset(motg);
+
+ if (pdata->otg_control == OTG_PHY_CONTROL) {
+ val = readl_relaxed(USB_OTGSC);
+ if (pdata->mode == USB_OTG) {
+ ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID;
+ val |= OTGSC_IDIE | OTGSC_BSVIE;
+ } else if (pdata->mode == USB_PERIPHERAL) {
+ ulpi_val = ULPI_INT_SESS_VALID;
+ val |= OTGSC_BSVIE;
+ }
+ writel_relaxed(val, USB_OTGSC);
+ ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_RISE);
+ ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL);
+ } else if (pdata->otg_control == OTG_PMIC_CONTROL) {
+ ulpi_write(phy, OTG_COMP_DISABLE,
+ ULPI_SET(ULPI_PWR_CLK_MNG_REG));
+ if (motg->phy_irq)
+ writeb_relaxed(USB_PHY_ID_MASK,
+ USB2_PHY_USB_PHY_INTERRUPT_MASK1);
+ }
+
+ if (motg->caps & ALLOW_VDD_MIN_WITH_RETENTION_DISABLED)
+ writel_relaxed(readl_relaxed(USB_OTGSC) & ~(OTGSC_IDPU),
+ USB_OTGSC);
+
+ msm_otg_dbg_log_event(&motg->phy, "USB RESET DONE", phy->otg->state,
+ get_pm_runtime_counter(phy->dev));
+
+ if (pdata->enable_axi_prefetch)
+ writel_relaxed(readl_relaxed(USB_HS_APF_CTRL) | (APF_CTRL_EN),
+ USB_HS_APF_CTRL);
+
+ /*
+ * Disable USB BAM as block reset resets USB BAM registers.
+ */
+ msm_usb_bam_enable(CI_CTRL, false);
+
+ return 0;
+}
+
+static void msm_otg_kick_sm_work(struct msm_otg *motg)
+{
+ if (atomic_read(&motg->in_lpm))
+ motg->resume_pending = true;
+
+ /* For device mode, resume now. Let pm_resume handle other cases */
+ if (atomic_read(&motg->pm_suspended) &&
+ motg->phy.otg->state != OTG_STATE_B_SUSPEND) {
+ motg->sm_work_pending = true;
+ } else if (!motg->sm_work_pending) {
+ /* process event only if previous one is not pending */
+ queue_work(motg->otg_wq, &motg->sm_work);
+ }
+}
+
+/*
+ * UDC calls usb_phy_set_suspend() to notify during bus suspend/resume.
+ * Update relevant state-machine inputs and queue sm_work.
+ * LPM enter/exit doesn't happen directly from this routine.
+ */
+
+static int msm_otg_set_suspend(struct usb_phy *phy, int suspend)
+{
+ struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+
+ pr_debug("%s(%d) in %s state\n", __func__, suspend,
+ usb_otg_state_string(phy->otg->state));
+ msm_otg_dbg_log_event(phy, "SET SUSPEND", suspend, phy->otg->state);
+
+ if (!(motg->caps & ALLOW_LPM_ON_DEV_SUSPEND))
+ return 0;
+
+ if (suspend) {
+ /* called in suspend interrupt context */
+ pr_debug("peripheral bus suspend\n");
+ msm_otg_dbg_log_event(phy, "PERIPHERAL BUS SUSPEND",
+ motg->inputs, phy->otg->state);
+
+ set_bit(A_BUS_SUSPEND, &motg->inputs);
+ } else {
+ /* host resume or remote-wakeup */
+ pr_debug("peripheral bus resume\n");
+ msm_otg_dbg_log_event(phy, "PERIPHERAL BUS RESUME",
+ motg->inputs, phy->otg->state);
+
+ clear_bit(A_BUS_SUSPEND, &motg->inputs);
+ }
+ /* use kick_sm_work to handle race with pm_resume */
+ msm_otg_kick_sm_work(motg);
+
+ return 0;
+}
+
+static int msm_otg_bus_freq_set(struct msm_otg *motg, enum usb_noc_mode mode)
+{
+ int i, ret;
+ long rate;
+
+ for (i = 0; i < USB_NUM_BUS_CLOCKS; i++) {
+ rate = bus_freqs[mode][i];
+ if (!rate) {
+ pr_debug("%s rate not available\n", bus_clkname[i]);
+ continue;
+ }
+
+ ret = clk_set_rate(motg->bus_clks[i], rate);
+ if (ret) {
+ pr_err("%s set rate failed: %d\n", bus_clkname[i], ret);
+ return ret;
+ }
+ pr_debug("%s set to %lu Hz\n", bus_clkname[i],
+ clk_get_rate(motg->bus_clks[i]));
+ msm_otg_dbg_log_event(&motg->phy, "OTG BUS FREQ SET", i, rate);
+ }
+
+ bus_clk_rate_set = true;
+
+ return 0;
+}
+
+static int msm_otg_bus_freq_get(struct msm_otg *motg)
+{
+ struct device *dev = motg->phy.dev;
+ struct device_node *np = dev->of_node;
+ int len = 0, i, count = USB_NUM_BUS_CLOCKS;
+
+ if (!np)
+ return -EINVAL;
+
+ /* SVS requires extra set of frequencies for perf_mode sysfs node */
+ if (motg->default_noc_mode == USB_NOC_SVS_VOTE)
+ count *= 2;
+
+ len = of_property_count_elems_of_size(np, "qcom,bus-clk-rate",
+ sizeof(len));
+ if (!len || (len != count)) {
+ pr_err("Invalid bus rate:%d %u\n", len, motg->default_noc_mode);
+ return -EINVAL;
+ }
+ of_property_read_u32_array(np, "qcom,bus-clk-rate", bus_freqs[0],
+ count);
+ for (i = 0; i < USB_NUM_BUS_CLOCKS; i++) {
+ if (bus_freqs[0][i] == 0) {
+ motg->bus_clks[i] = NULL;
+ pr_debug("%s not available\n", bus_clkname[i]);
+ continue;
+ }
+
+ motg->bus_clks[i] = devm_clk_get(dev, bus_clkname[i]);
+ if (IS_ERR(motg->bus_clks[i])) {
+ pr_err("%s get failed\n", bus_clkname[i]);
+ return PTR_ERR(motg->bus_clks[i]);
+ }
+ }
+ return 0;
+}
+
+static void msm_otg_bus_clks_enable(struct msm_otg *motg)
+{
+ int i;
+ int ret;
+
+ if (!bus_clk_rate_set || motg->bus_clks_enabled)
+ return;
+
+ for (i = 0; i < USB_NUM_BUS_CLOCKS; i++) {
+ if (motg->bus_clks[i] == NULL)
+ continue;
+ ret = clk_prepare_enable(motg->bus_clks[i]);
+ if (ret) {
+ pr_err("%s enable rate failed: %d\n", bus_clkname[i],
+ ret);
+ goto err_clk_en;
+ }
+ }
+ motg->bus_clks_enabled = true;
+ return;
+err_clk_en:
+ for (--i; i >= 0; --i) {
+ if (motg->bus_clks[i] != NULL)
+ clk_disable_unprepare(motg->bus_clks[i]);
+ }
+}
+
+static void msm_otg_bus_clks_disable(struct msm_otg *motg)
+{
+ int i;
+
+ if (!bus_clk_rate_set || !motg->bus_clks_enabled)
+ return;
+
+ for (i = 0; i < USB_NUM_BUS_CLOCKS; i++) {
+ if (motg->bus_clks[i] != NULL)
+ clk_disable_unprepare(motg->bus_clks[i]);
+ }
+ motg->bus_clks_enabled = false;
+}
+
+static void msm_otg_bus_vote(struct msm_otg *motg, enum usb_bus_vote vote)
+{
+ int ret;
+ struct msm_otg_platform_data *pdata = motg->pdata;
+
+ msm_otg_dbg_log_event(&motg->phy, "BUS VOTE", vote,
+ motg->phy.otg->state);
+ /* Check if target allows min_vote to be same as no_vote */
+ if (pdata->bus_scale_table &&
+ vote >= pdata->bus_scale_table->num_usecases)
+ vote = USB_NO_PERF_VOTE;
+
+ if (motg->bus_perf_client) {
+ ret = msm_bus_scale_client_update_request(
+ motg->bus_perf_client, vote);
+ if (ret)
+ dev_err(motg->phy.dev, "%s: Failed to vote (%d)\n"
+ "for bus bw %d\n", __func__, vote, ret);
+ }
+
+ if (vote == USB_MAX_PERF_VOTE)
+ msm_otg_bus_clks_enable(motg);
+ else
+ msm_otg_bus_clks_disable(motg);
+}
+
+static void msm_otg_enable_phy_hv_int(struct msm_otg *motg)
+{
+ bool bsv_id_hv_int = false;
+ bool dp_dm_hv_int = false;
+ u32 val;
+
+ if (motg->pdata->otg_control == OTG_PHY_CONTROL ||
+ motg->phy_irq)
+ bsv_id_hv_int = true;
+ if (motg->host_bus_suspend || motg->device_bus_suspend)
+ dp_dm_hv_int = true;
+
+ if (!bsv_id_hv_int && !dp_dm_hv_int)
+ return;
+
+ switch (motg->pdata->phy_type) {
+ case SNPS_PICO_PHY:
+ val = readl_relaxed(motg->usb_phy_ctrl_reg);
+ if (bsv_id_hv_int)
+ val |= (PHY_IDHV_INTEN | PHY_OTGSESSVLDHV_INTEN);
+ if (dp_dm_hv_int)
+ val |= PHY_CLAMP_DPDMSE_EN;
+ writel_relaxed(val, motg->usb_phy_ctrl_reg);
+ break;
+ case SNPS_FEMTO_PHY:
+ if (bsv_id_hv_int) {
+ val = readb_relaxed(USB_PHY_CSR_PHY_CTRL1);
+ val |= ID_HV_CLAMP_EN_N;
+ writeb_relaxed(val, USB_PHY_CSR_PHY_CTRL1);
+ }
+
+ if (dp_dm_hv_int) {
+ val = readb_relaxed(USB_PHY_CSR_PHY_CTRL3);
+ val |= CLAMP_MPM_DPSE_DMSE_EN_N;
+ writeb_relaxed(val, USB_PHY_CSR_PHY_CTRL3);
+ }
+ break;
+ default:
+ break;
+ }
+ pr_debug("%s: bsv_id_hv = %d dp_dm_hv_int = %d\n",
+ __func__, bsv_id_hv_int, dp_dm_hv_int);
+ msm_otg_dbg_log_event(&motg->phy, "PHY HV INTR ENABLED",
+ bsv_id_hv_int, dp_dm_hv_int);
+}
+
+static void msm_otg_disable_phy_hv_int(struct msm_otg *motg)
+{
+ bool bsv_id_hv_int = false;
+ bool dp_dm_hv_int = false;
+ u32 val;
+
+ if (motg->pdata->otg_control == OTG_PHY_CONTROL ||
+ motg->phy_irq)
+ bsv_id_hv_int = true;
+ if (motg->host_bus_suspend || motg->device_bus_suspend)
+ dp_dm_hv_int = true;
+
+ if (!bsv_id_hv_int && !dp_dm_hv_int)
+ return;
+
+ switch (motg->pdata->phy_type) {
+ case SNPS_PICO_PHY:
+ val = readl_relaxed(motg->usb_phy_ctrl_reg);
+ if (bsv_id_hv_int)
+ val &= ~(PHY_IDHV_INTEN | PHY_OTGSESSVLDHV_INTEN);
+ if (dp_dm_hv_int)
+ val &= ~PHY_CLAMP_DPDMSE_EN;
+ writel_relaxed(val, motg->usb_phy_ctrl_reg);
+ break;
+ case SNPS_FEMTO_PHY:
+ if (bsv_id_hv_int) {
+ val = readb_relaxed(USB_PHY_CSR_PHY_CTRL1);
+ val &= ~ID_HV_CLAMP_EN_N;
+ writeb_relaxed(val, USB_PHY_CSR_PHY_CTRL1);
+ }
+
+ if (dp_dm_hv_int) {
+ val = readb_relaxed(USB_PHY_CSR_PHY_CTRL3);
+ val &= ~CLAMP_MPM_DPSE_DMSE_EN_N;
+ writeb_relaxed(val, USB_PHY_CSR_PHY_CTRL3);
+ }
+ break;
+ default:
+ break;
+ }
+ pr_debug("%s: bsv_id_hv = %d dp_dm_hv_int = %d\n",
+ __func__, bsv_id_hv_int, dp_dm_hv_int);
+ msm_otg_dbg_log_event(&motg->phy, "PHY HV INTR DISABLED",
+ bsv_id_hv_int, dp_dm_hv_int);
+}
+
+static void msm_otg_enter_phy_retention(struct msm_otg *motg)
+{
+ u32 val;
+
+ switch (motg->pdata->phy_type) {
+ case SNPS_PICO_PHY:
+ val = readl_relaxed(motg->usb_phy_ctrl_reg);
+ val &= ~PHY_RETEN;
+ writel_relaxed(val, motg->usb_phy_ctrl_reg);
+ break;
+ case SNPS_FEMTO_PHY:
+ /* Retention is supported via SIDDQ */
+ val = readb_relaxed(USB_PHY_CSR_PHY_CTRL_COMMON0);
+ val |= SIDDQ;
+ writeb_relaxed(val, USB_PHY_CSR_PHY_CTRL_COMMON0);
+ break;
+ default:
+ break;
+ }
+ pr_debug("USB PHY is in retention\n");
+ msm_otg_dbg_log_event(&motg->phy, "USB PHY ENTER RETENTION",
+ motg->pdata->phy_type, 0);
+}
+
+static void msm_otg_exit_phy_retention(struct msm_otg *motg)
+{
+ int val;
+
+ switch (motg->pdata->phy_type) {
+ case SNPS_PICO_PHY:
+ val = readl_relaxed(motg->usb_phy_ctrl_reg);
+ val |= PHY_RETEN;
+ writel_relaxed(val, motg->usb_phy_ctrl_reg);
+ break;
+ case SNPS_FEMTO_PHY:
+ /*
+ * It is required to do USB block reset to bring Femto PHY out
+ * of retention.
+ */
+ msm_otg_reset(&motg->phy);
+ break;
+ default:
+ break;
+ }
+ pr_debug("USB PHY is exited from retention\n");
+ msm_otg_dbg_log_event(&motg->phy, "USB PHY EXIT RETENTION",
+ motg->pdata->phy_type, 0);
+}
+
+static void msm_id_status_w(struct work_struct *w);
+static irqreturn_t msm_otg_phy_irq_handler(int irq, void *data)
+{
+ struct msm_otg *motg = data;
+
+ msm_otg_dbg_log_event(&motg->phy, "PHY ID IRQ",
+ atomic_read(&motg->in_lpm), motg->phy.otg->state);
+ if (atomic_read(&motg->in_lpm)) {
+ pr_debug("PHY ID IRQ in LPM\n");
+ motg->phy_irq_pending = true;
+ msm_otg_kick_sm_work(motg);
+ } else {
+ pr_debug("PHY ID IRQ outside LPM\n");
+ msm_id_status_w(&motg->id_status_work.work);
+ }
+
+ return IRQ_HANDLED;
+}
+
+#define PHY_SUSPEND_TIMEOUT_USEC (5 * 1000)
+#define PHY_DEVICE_BUS_SUSPEND_TIMEOUT_USEC 100
+#define PHY_RESUME_TIMEOUT_USEC (100 * 1000)
+
+#define PHY_SUSPEND_RETRIES_MAX 3
+
+static void msm_otg_set_vbus_state(int online);
+static void msm_otg_perf_vote_update(struct msm_otg *motg, bool perf_mode);
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_otg_suspend(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+ struct usb_bus *bus = phy->otg->host;
+ struct msm_otg_platform_data *pdata = motg->pdata;
+ int cnt;
+ bool host_bus_suspend, device_bus_suspend, dcp, prop_charger;
+ bool floated_charger, sm_work_busy;
+ u32 cmd_val;
+ u32 portsc, config2;
+ u32 func_ctrl;
+ int phcd_retry_cnt = 0, ret;
+ unsigned int phy_suspend_timeout;
+
+ cnt = 0;
+ msm_otg_dbg_log_event(phy, "LPM ENTER START",
+ motg->inputs, phy->otg->state);
+
+ if (atomic_read(&motg->in_lpm))
+ return 0;
+
+ cancel_delayed_work_sync(&motg->perf_vote_work);
+
+ disable_irq(motg->irq);
+ if (motg->phy_irq)
+ disable_irq(motg->phy_irq);
+lpm_start:
+ host_bus_suspend = phy->otg->host && !test_bit(ID, &motg->inputs);
+ device_bus_suspend = phy->otg->gadget && test_bit(ID, &motg->inputs) &&
+ test_bit(A_BUS_SUSPEND, &motg->inputs) &&
+ motg->caps & ALLOW_LPM_ON_DEV_SUSPEND;
+
+ if (host_bus_suspend)
+ msm_otg_perf_vote_update(motg, false);
+ /*
+ * Allow putting PHY into SIDDQ with wall charger connected in
+ * case of external charger detection.
+ */
+ dcp = (motg->chg_type == USB_DCP_CHARGER) && !motg->is_ext_chg_dcp;
+ prop_charger = motg->chg_type == USB_NONCOMPLIANT_CHARGER;
+ floated_charger = motg->chg_type == USB_FLOATED_CHARGER;
+
+ /* !BSV, but its handling is in progress by otg sm_work */
+ sm_work_busy = !test_bit(B_SESS_VLD, &motg->inputs) &&
+ phy->otg->state == OTG_STATE_B_PERIPHERAL;
+
+ /* Perform block reset to recover from UDC error events on disconnect */
+ if (motg->err_event_seen)
+ msm_otg_reset(phy);
+
+ /* Enable line state difference wakeup fix for only device and host
+ * bus suspend scenarios. Otherwise PHY can not be suspended when
+ * a charger that pulls DP/DM high is connected.
+ */
+ config2 = readl_relaxed(USB_GENCONFIG_2);
+ if (device_bus_suspend)
+ config2 |= GENCONFIG_2_LINESTATE_DIFF_WAKEUP_EN;
+ else
+ config2 &= ~GENCONFIG_2_LINESTATE_DIFF_WAKEUP_EN;
+ writel_relaxed(config2, USB_GENCONFIG_2);
+
+ /*
+ * Abort suspend when,
+ * 1. charging detection in progress due to cable plug-in
+ * 2. host mode activation in progress due to Micro-A cable insertion
+ * 3. !BSV, but its handling is in progress by otg sm_work
+ * Don't abort suspend in case of dcp detected by PMIC
+ */
+
+ if ((test_bit(B_SESS_VLD, &motg->inputs) && !device_bus_suspend &&
+ !dcp && !motg->is_ext_chg_dcp && !prop_charger &&
+ !floated_charger) || sm_work_busy) {
+ msm_otg_dbg_log_event(phy, "LPM ENTER ABORTED",
+ motg->inputs, motg->chg_type);
+ enable_irq(motg->irq);
+ if (motg->phy_irq)
+ enable_irq(motg->phy_irq);
+ return -EBUSY;
+ }
+
+ if (motg->caps & ALLOW_VDD_MIN_WITH_RETENTION_DISABLED) {
+ /* put the controller in non-driving mode */
+ func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL);
+ func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+ func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
+ ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL);
+ ulpi_write(phy, ULPI_IFC_CTRL_AUTORESUME,
+ ULPI_CLR(ULPI_IFC_CTRL));
+ }
+
+ /*
+ * PHY suspend sequence as mentioned in the databook.
+ *
+ * Device bus suspend: The controller may abort PHY suspend if
+ * there is an incoming reset or resume from the host. If PHCD
+ * is not set within 100 usec. Abort the LPM sequence.
+ *
+ * Host bus suspend: If the peripheral is attached, PHY is already
+ * put into suspend along with the peripheral bus suspend. poll for
+ * PHCD upto 5 msec. If the peripheral is not attached i.e entering
+ * LPM with Micro-A cable, set the PHCD and poll for it for 5 msec.
+ *
+ * No cable connected: Set the PHCD to suspend the PHY. Poll for PHCD
+ * upto 5 msec.
+ *
+ * The controller aborts PHY suspend only in device bus suspend case.
+ * In other cases, it is observed that PHCD may not get set within
+ * the timeout. If so, set the PHCD again and poll for it before
+ * reset recovery.
+ */
+
+phcd_retry:
+ if (device_bus_suspend)
+ phy_suspend_timeout = PHY_DEVICE_BUS_SUSPEND_TIMEOUT_USEC;
+ else
+ phy_suspend_timeout = PHY_SUSPEND_TIMEOUT_USEC;
+
+ cnt = 0;
+ portsc = readl_relaxed(USB_PORTSC);
+ if (!(portsc & PORTSC_PHCD)) {
+ writel_relaxed(portsc | PORTSC_PHCD,
+ USB_PORTSC);
+ while (cnt < phy_suspend_timeout) {
+ if (readl_relaxed(USB_PORTSC) & PORTSC_PHCD)
+ break;
+ udelay(1);
+ cnt++;
+ }
+ }
+
+ if (cnt >= phy_suspend_timeout) {
+ if (phcd_retry_cnt > PHY_SUSPEND_RETRIES_MAX) {
+ msm_otg_dbg_log_event(phy, "PHY SUSPEND FAILED",
+ phcd_retry_cnt, phy->otg->state);
+ dev_err(phy->dev, "PHY suspend failed\n");
+ ret = -EBUSY;
+ goto phy_suspend_fail;
+ }
+
+ if (device_bus_suspend) {
+ dev_dbg(phy->dev, "PHY suspend aborted\n");
+ ret = -EBUSY;
+ goto phy_suspend_fail;
+ } else {
+ if (phcd_retry_cnt++ < PHY_SUSPEND_RETRIES_MAX) {
+ dev_dbg(phy->dev, "PHY suspend retry\n");
+ goto phcd_retry;
+ } else {
+ dev_err(phy->dev, "reset attempt during PHY suspend\n");
+ phcd_retry_cnt++;
+ motg->reset_counter = 0;
+ msm_otg_reset(phy);
+ goto lpm_start;
+ }
+ }
+ }
+
+ /*
+ * PHY has capability to generate interrupt asynchronously in low
+ * power mode (LPM). This interrupt is level triggered. So USB IRQ
+ * line must be disabled till async interrupt enable bit is cleared
+ * in USBCMD register. Assert STP (ULPI interface STOP signal) to
+ * block data communication from PHY.
+ *
+ * PHY retention mode is disallowed while entering to LPM with wall
+ * charger connected. But PHY is put into suspend mode. Hence
+ * enable asynchronous interrupt to detect charger disconnection when
+ * PMIC notifications are unavailable.
+ */
+ cmd_val = readl_relaxed(USB_USBCMD);
+ if (host_bus_suspend || device_bus_suspend ||
+ (motg->pdata->otg_control == OTG_PHY_CONTROL))
+ cmd_val |= ASYNC_INTR_CTRL | ULPI_STP_CTRL;
+ else
+ cmd_val |= ULPI_STP_CTRL;
+ writel_relaxed(cmd_val, USB_USBCMD);
+
+ /*
+ * BC1.2 spec mandates PD to enable VDP_SRC when charging from DCP.
+ * PHY retention and collapse can not happen with VDP_SRC enabled.
+ */
+
+
+ /*
+ * We come here in 3 scenarios.
+ *
+ * (1) No cable connected (out of session):
+ * - BSV/ID HV interrupts are enabled for PHY based detection.
+ * - PHY is put in retention.
+ * - If allowed (PMIC based detection), PHY is power collapsed.
+ * - DVDD (CX/MX) minimization and XO shutdown are allowed.
+ * - The wakeup is through VBUS/ID interrupt from PHY/PMIC/user.
+ * (2) USB wall charger:
+ * - BSV/ID HV interrupts are enabled for PHY based detection.
+ * - For BC1.2 compliant charger, retention is not allowed to
+ * keep VDP_SRC on. XO shutdown is allowed.
+ * - The wakeup is through VBUS/ID interrupt from PHY/PMIC/user.
+ * (3) Device/Host Bus suspend (if LPM is enabled):
+ * - BSV/ID HV interrupts are enabled for PHY based detection.
+ * - D+/D- MPM pin are configured to wakeup from line state
+ * change through PHY HV interrupts. PHY HV interrupts are
+ * also enabled. If MPM pins are not available, retention and
+ * XO is not allowed.
+ * - PHY is put into retention only if a gpio is used to keep
+ * the D+ pull-up. ALLOW_BUS_SUSPEND_WITHOUT_REWORK capability
+ * is set means, PHY can enable D+ pull-up or D+/D- pull-down
+ * without any re-work and PHY should not be put into retention.
+ * - DVDD (CX/MX) minimization and XO shutdown is allowed if
+ * ALLOW_BUS_SUSPEND_WITHOUT_REWORK is set (PHY DVDD is supplied
+ * via PMIC LDO) or board level re-work is present.
+ * - The wakeup is through VBUS/ID interrupt from PHY/PMIC/user
+ * or USB link asynchronous interrupt for line state change.
+ *
+ */
+ motg->host_bus_suspend = host_bus_suspend;
+ motg->device_bus_suspend = device_bus_suspend;
+
+ if (motg->caps & ALLOW_PHY_RETENTION && !device_bus_suspend && !dcp &&
+ (!host_bus_suspend || (motg->caps &
+ ALLOW_BUS_SUSPEND_WITHOUT_REWORK) ||
+ ((motg->caps & ALLOW_HOST_PHY_RETENTION)
+ && (pdata->dpdm_pulldown_added || !(portsc & PORTSC_CCS))))) {
+ msm_otg_enable_phy_hv_int(motg);
+ if ((!host_bus_suspend || !(motg->caps &
+ ALLOW_BUS_SUSPEND_WITHOUT_REWORK)) &&
+ !(motg->caps & ALLOW_VDD_MIN_WITH_RETENTION_DISABLED)) {
+ msm_otg_enter_phy_retention(motg);
+ motg->lpm_flags |= PHY_RETENTIONED;
+ }
+ } else if (device_bus_suspend && !dcp &&
+ (pdata->mpm_dpshv_int || pdata->mpm_dmshv_int)) {
+ /* DP DM HV interrupts are used for bus resume from XO off */
+ msm_otg_enable_phy_hv_int(motg);
+ if (motg->caps & ALLOW_PHY_RETENTION && pdata->vddmin_gpio) {
+
+ /*
+ * This is HW WA needed when PHY_CLAMP_DPDMSE_EN is
+ * enabled and we put the phy in retention mode.
+ * Without this WA, the async_irq will be fired right
+ * after suspending whithout any bus resume.
+ */
+ config2 = readl_relaxed(USB_GENCONFIG_2);
+ config2 &= ~GENCONFIG_2_DPSE_DMSE_HV_INTR_EN;
+ writel_relaxed(config2, USB_GENCONFIG_2);
+
+ msm_otg_enter_phy_retention(motg);
+ motg->lpm_flags |= PHY_RETENTIONED;
+ gpio_direction_output(pdata->vddmin_gpio, 1);
+ }
+ }
+
+ /* Ensure that above operation is completed before turning off clocks */
+ mb();
+ /* Consider clocks on workaround flag only in case of bus suspend */
+ if (!(phy->otg->state == OTG_STATE_B_PERIPHERAL &&
+ test_bit(A_BUS_SUSPEND, &motg->inputs)) ||
+ !motg->pdata->core_clk_always_on_workaround) {
+ clk_disable_unprepare(motg->pclk);
+ clk_disable_unprepare(motg->core_clk);
+ if (motg->phy_csr_clk)
+ clk_disable_unprepare(motg->phy_csr_clk);
+ motg->lpm_flags |= CLOCKS_DOWN;
+ }
+
+ /* usb phy no more require TCXO clock, hence vote for TCXO disable */
+ if (!host_bus_suspend || (motg->caps &
+ ALLOW_BUS_SUSPEND_WITHOUT_REWORK) ||
+ ((motg->caps & ALLOW_HOST_PHY_RETENTION) &&
+ (pdata->dpdm_pulldown_added || !(portsc & PORTSC_CCS)))) {
+ if (motg->xo_clk) {
+ clk_disable_unprepare(motg->xo_clk);
+ motg->lpm_flags |= XO_SHUTDOWN;
+ }
+ }
+
+ if (motg->caps & ALLOW_PHY_POWER_COLLAPSE &&
+ !host_bus_suspend && !dcp && !device_bus_suspend) {
+ msm_hsusb_ldo_enable(motg, USB_PHY_REG_OFF);
+ motg->lpm_flags |= PHY_PWR_COLLAPSED;
+ } else if (motg->caps & ALLOW_PHY_REGULATORS_LPM &&
+ !host_bus_suspend && !device_bus_suspend && !dcp) {
+ msm_hsusb_ldo_enable(motg, USB_PHY_REG_LPM_ON);
+ motg->lpm_flags |= PHY_REGULATORS_LPM;
+ }
+
+ if (motg->lpm_flags & PHY_RETENTIONED ||
+ (motg->caps & ALLOW_VDD_MIN_WITH_RETENTION_DISABLED)) {
+ regulator_disable(hsusb_vdd);
+ msm_hsusb_config_vddcx(0);
+ }
+
+ if (device_may_wakeup(phy->dev)) {
+ if (host_bus_suspend || device_bus_suspend) {
+ enable_irq_wake(motg->async_irq);
+ enable_irq_wake(motg->irq);
+ }
+
+ if (motg->phy_irq)
+ enable_irq_wake(motg->phy_irq);
+ if (motg->pdata->pmic_id_irq)
+ enable_irq_wake(motg->pdata->pmic_id_irq);
+ if (motg->ext_id_irq)
+ enable_irq_wake(motg->ext_id_irq);
+ if (pdata->otg_control == OTG_PHY_CONTROL &&
+ pdata->mpm_otgsessvld_int)
+ msm_mpm_set_pin_wake(pdata->mpm_otgsessvld_int, 1);
+ if ((host_bus_suspend || device_bus_suspend) &&
+ pdata->mpm_dpshv_int)
+ msm_mpm_set_pin_wake(pdata->mpm_dpshv_int, 1);
+ if ((host_bus_suspend || device_bus_suspend) &&
+ pdata->mpm_dmshv_int)
+ msm_mpm_set_pin_wake(pdata->mpm_dmshv_int, 1);
+ }
+ if (bus)
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags);
+
+ msm_otg_bus_vote(motg, USB_NO_PERF_VOTE);
+
+ atomic_set(&motg->in_lpm, 1);
+
+ /* Enable ASYNC IRQ during LPM */
+ enable_irq(motg->async_irq);
+ if (motg->phy_irq)
+ enable_irq(motg->phy_irq);
+
+ enable_irq(motg->irq);
+ pm_relax(&motg->pdev->dev);
+
+ dev_dbg(phy->dev, "LPM caps = %lu flags = %lu\n",
+ motg->caps, motg->lpm_flags);
+ dev_info(phy->dev, "USB in low power mode\n");
+ msm_otg_dbg_log_event(phy, "LPM ENTER DONE",
+ motg->caps, motg->lpm_flags);
+
+ if (motg->err_event_seen) {
+ motg->err_event_seen = false;
+ if (motg->vbus_state != test_bit(B_SESS_VLD, &motg->inputs))
+ msm_otg_set_vbus_state(motg->vbus_state);
+ if (motg->id_state != test_bit(ID, &motg->inputs))
+ msm_id_status_w(&motg->id_status_work.work);
+ }
+
+ return 0;
+
+phy_suspend_fail:
+ enable_irq(motg->irq);
+ if (motg->phy_irq)
+ enable_irq(motg->phy_irq);
+ return ret;
+}
+
+static int msm_otg_resume(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+ struct usb_bus *bus = phy->otg->host;
+ struct usb_hcd *hcd = bus_to_hcd(phy->otg->host);
+ struct msm_otg_platform_data *pdata = motg->pdata;
+ int cnt = 0;
+ unsigned int temp;
+ unsigned int ret;
+ u32 func_ctrl;
+
+ msm_otg_dbg_log_event(phy, "LPM EXIT START", motg->inputs,
+ phy->otg->state);
+ if (!atomic_read(&motg->in_lpm)) {
+ msm_otg_dbg_log_event(phy, "USB NOT IN LPM",
+ atomic_read(&motg->in_lpm), phy->otg->state);
+ return 0;
+ }
+
+ disable_irq(motg->irq);
+ pm_stay_awake(&motg->pdev->dev);
+
+ /*
+ * If we are resuming from the device bus suspend, restore
+ * the max performance bus vote. Otherwise put a minimum
+ * bus vote to satisfy the requirement for enabling clocks.
+ */
+
+ if (motg->device_bus_suspend && debug_bus_voting_enabled)
+ msm_otg_bus_vote(motg, USB_MAX_PERF_VOTE);
+ else
+ msm_otg_bus_vote(motg, USB_MIN_PERF_VOTE);
+
+ /* Vote for TCXO when waking up the phy */
+ if (motg->lpm_flags & XO_SHUTDOWN) {
+ if (motg->xo_clk)
+ clk_prepare_enable(motg->xo_clk);
+ motg->lpm_flags &= ~XO_SHUTDOWN;
+ }
+
+ if (motg->lpm_flags & CLOCKS_DOWN) {
+ if (motg->phy_csr_clk) {
+ ret = clk_prepare_enable(motg->phy_csr_clk);
+ WARN(ret, "USB phy_csr_clk enable failed\n");
+ }
+ ret = clk_prepare_enable(motg->core_clk);
+ WARN(ret, "USB core_clk enable failed\n");
+ ret = clk_prepare_enable(motg->pclk);
+ WARN(ret, "USB pclk enable failed\n");
+ motg->lpm_flags &= ~CLOCKS_DOWN;
+ }
+
+ if (motg->lpm_flags & PHY_PWR_COLLAPSED) {
+ msm_hsusb_ldo_enable(motg, USB_PHY_REG_ON);
+ motg->lpm_flags &= ~PHY_PWR_COLLAPSED;
+ } else if (motg->lpm_flags & PHY_REGULATORS_LPM) {
+ msm_hsusb_ldo_enable(motg, USB_PHY_REG_LPM_OFF);
+ motg->lpm_flags &= ~PHY_REGULATORS_LPM;
+ }
+
+ if (motg->lpm_flags & PHY_RETENTIONED ||
+ (motg->caps & ALLOW_VDD_MIN_WITH_RETENTION_DISABLED)) {
+ msm_hsusb_config_vddcx(1);
+ ret = regulator_enable(hsusb_vdd);
+ WARN(ret, "hsusb_vdd LDO enable failed\n");
+ msm_otg_disable_phy_hv_int(motg);
+ msm_otg_exit_phy_retention(motg);
+ motg->lpm_flags &= ~PHY_RETENTIONED;
+ if (pdata->vddmin_gpio && motg->device_bus_suspend)
+ gpio_direction_input(pdata->vddmin_gpio);
+ } else if (motg->device_bus_suspend) {
+ msm_otg_disable_phy_hv_int(motg);
+ }
+
+ temp = readl_relaxed(USB_USBCMD);
+ temp &= ~ASYNC_INTR_CTRL;
+ temp &= ~ULPI_STP_CTRL;
+ writel_relaxed(temp, USB_USBCMD);
+
+ /*
+ * PHY comes out of low power mode (LPM) in case of wakeup
+ * from asynchronous interrupt.
+ */
+ if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD))
+ goto skip_phy_resume;
+
+ writel_relaxed(readl_relaxed(USB_PORTSC) & ~PORTSC_PHCD, USB_PORTSC);
+
+ while (cnt < PHY_RESUME_TIMEOUT_USEC) {
+ if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD))
+ break;
+ udelay(1);
+ cnt++;
+ }
+
+ if (cnt >= PHY_RESUME_TIMEOUT_USEC) {
+ /*
+ * This is a fatal error. Reset the link and
+ * PHY. USB state can not be restored. Re-insertion
+ * of USB cable is the only way to get USB working.
+ */
+ dev_err(phy->dev, "Unable to resume USB. Re-plugin the cable\n"
+ );
+ msm_otg_reset(phy);
+ }
+
+skip_phy_resume:
+ if (motg->caps & ALLOW_VDD_MIN_WITH_RETENTION_DISABLED) {
+ /* put the controller in normal mode */
+ func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL);
+ func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+ func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
+ ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL);
+ }
+
+ if (device_may_wakeup(phy->dev)) {
+ if (motg->host_bus_suspend || motg->device_bus_suspend) {
+ disable_irq_wake(motg->async_irq);
+ disable_irq_wake(motg->irq);
+ }
+
+ if (motg->phy_irq)
+ disable_irq_wake(motg->phy_irq);
+ if (motg->pdata->pmic_id_irq)
+ disable_irq_wake(motg->pdata->pmic_id_irq);
+ if (motg->ext_id_irq)
+ disable_irq_wake(motg->ext_id_irq);
+ if (pdata->otg_control == OTG_PHY_CONTROL &&
+ pdata->mpm_otgsessvld_int)
+ msm_mpm_set_pin_wake(pdata->mpm_otgsessvld_int, 0);
+ if ((motg->host_bus_suspend || motg->device_bus_suspend) &&
+ pdata->mpm_dpshv_int)
+ msm_mpm_set_pin_wake(pdata->mpm_dpshv_int, 0);
+ if ((motg->host_bus_suspend || motg->device_bus_suspend) &&
+ pdata->mpm_dmshv_int)
+ msm_mpm_set_pin_wake(pdata->mpm_dmshv_int, 0);
+ }
+ if (bus)
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags);
+
+ atomic_set(&motg->in_lpm, 0);
+
+ if (motg->async_int) {
+ /* Match the disable_irq call from ISR */
+ enable_irq(motg->async_int);
+ motg->async_int = 0;
+ }
+ enable_irq(motg->irq);
+
+ /* Enable ASYNC_IRQ only during LPM */
+ disable_irq(motg->async_irq);
+
+ if (motg->phy_irq_pending) {
+ motg->phy_irq_pending = false;
+ msm_id_status_w(&motg->id_status_work.work);
+ }
+
+ if (motg->host_bus_suspend) {
+ usb_hcd_resume_root_hub(hcd);
+ schedule_delayed_work(&motg->perf_vote_work,
+ msecs_to_jiffies(1000 * PM_QOS_SAMPLE_SEC));
+ }
+
+ dev_info(phy->dev, "USB exited from low power mode\n");
+ msm_otg_dbg_log_event(phy, "LPM EXIT DONE",
+ motg->caps, motg->lpm_flags);
+
+ return 0;
+}
+#endif
+
+static void msm_otg_notify_host_mode(struct msm_otg *motg, bool host_mode)
+{
+ if (!psy) {
+ pr_err("No USB power supply registered!\n");
+ return;
+ }
+
+ motg->host_mode = host_mode;
+ power_supply_changed(psy);
+}
+
+static int msm_otg_notify_chg_type(struct msm_otg *motg)
+{
+ static int charger_type;
+ union power_supply_propval pval = {0};
+
+ /*
+ * TODO
+ * Unify OTG driver charger types and power supply charger types
+ */
+ if (charger_type == motg->chg_type)
+ return 0;
+
+ if (motg->chg_type == USB_SDP_CHARGER)
+ charger_type = POWER_SUPPLY_TYPE_USB;
+ else if (motg->chg_type == USB_CDP_CHARGER)
+ charger_type = POWER_SUPPLY_TYPE_USB_CDP;
+ else if (motg->chg_type == USB_DCP_CHARGER ||
+ motg->chg_type == USB_NONCOMPLIANT_CHARGER ||
+ motg->chg_type == USB_FLOATED_CHARGER)
+ charger_type = POWER_SUPPLY_TYPE_USB_DCP;
+ else
+ charger_type = POWER_SUPPLY_TYPE_UNKNOWN;
+
+ if (!psy) {
+ pr_err("No USB power supply registered!\n");
+ return -EINVAL;
+ }
+
+ pr_debug("setting usb power supply type %d\n", charger_type);
+ msm_otg_dbg_log_event(&motg->phy, "SET USB PWR SUPPLY TYPE",
+ motg->chg_type, charger_type);
+ pval.intval = charger_type;
+ power_supply_set_property(psy, POWER_SUPPLY_PROP_TYPE, &pval);
+ return 0;
+}
+
+static int msm_otg_notify_power_supply(struct msm_otg *motg, unsigned int mA)
+{
+ union power_supply_propval pval = {0};
+ bool enable;
+ int limit;
+
+ if (!psy) {
+ dev_dbg(motg->phy.dev, "no usb power supply registered\n");
+ goto psy_error;
+ }
+
+ if (motg->cur_power == 0 && mA > 2) {
+ /* Enable charging */
+ enable = true;
+ limit = 1000 * mA;
+ } else if (motg->cur_power >= 0 && (mA == 0 || mA == 2)) {
+ /* Disable charging */
+ enable = false;
+ /* Set max current limit in uA */
+ limit = 1000 * mA;
+ } else {
+ enable = true;
+ /* Current has changed (100/2 --> 500) */
+ limit = 1000 * mA;
+ }
+
+ pval.intval = enable;
+ if (power_supply_set_property(psy, POWER_SUPPLY_PROP_ONLINE, &pval))
+ goto psy_error;
+
+ pval.intval = limit;
+ if (power_supply_set_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX,
+ &pval))
+ goto psy_error;
+
+ power_supply_changed(psy);
+ return 0;
+
+psy_error:
+ dev_dbg(motg->phy.dev, "power supply error when setting property\n");
+ return -ENXIO;
+}
+
+static void msm_otg_set_online_status(struct msm_otg *motg)
+{
+ union power_supply_propval pval = {0};
+
+ if (!psy) {
+ dev_dbg(motg->phy.dev, "no usb power supply registered\n");
+ return;
+ }
+
+ /* Set power supply online status to false */
+ pval.intval = false;
+ if (power_supply_set_property(psy, POWER_SUPPLY_PROP_ONLINE, &pval))
+ dev_dbg(motg->phy.dev, "error setting power supply property\n");
+}
+
+static void msm_otg_notify_charger(struct msm_otg *motg, unsigned int mA)
+{
+ struct usb_gadget *g = motg->phy.otg->gadget;
+ struct msm_otg_platform_data *pdata = motg->pdata;
+
+ if (g && g->is_a_peripheral)
+ return;
+
+ dev_dbg(motg->phy.dev, "Requested curr from USB = %u, max-type-c:%u\n",
+ mA, motg->typec_current_max);
+ /* Save bc1.2 max_curr if type-c charger later moves to diff mode */
+ motg->bc1p2_current_max = mA;
+
+ /*
+ * Limit type-c charger current to 500 for SDP charger to avoid more
+ * current drawn than 500 with Hosts that don't support type C due to
+ * non compliant type-c to standard A cables.
+ */
+ if (pdata->enable_sdp_typec_current_limit &&
+ (motg->chg_type == USB_SDP_CHARGER) &&
+ motg->typec_current_max > 500)
+ motg->typec_current_max = 500;
+
+ /* Override mA if type-c charger used (use hvdcp/bc1.2 if it is 500) */
+ if (motg->typec_current_max > 500 && mA < motg->typec_current_max)
+ mA = motg->typec_current_max;
+
+ if (msm_otg_notify_chg_type(motg))
+ dev_err(motg->phy.dev,
+ "Failed notifying %d charger type to PMIC\n",
+ motg->chg_type);
+
+ /*
+ * This condition will be true when usb cable is disconnected
+ * during bootup before enumeration. Check charger type also
+ * to avoid clearing online flag in case of valid charger.
+ */
+ if (motg->online && motg->cur_power == 0 && mA == 0 &&
+ (motg->chg_type == USB_INVALID_CHARGER))
+ msm_otg_set_online_status(motg);
+
+ if (motg->cur_power == mA)
+ return;
+
+ dev_info(motg->phy.dev, "Avail curr from USB = %u\n", mA);
+ msm_otg_dbg_log_event(&motg->phy, "AVAIL CURR FROM USB",
+ mA, motg->chg_type);
+
+ msm_otg_notify_power_supply(motg, mA);
+
+ motg->cur_power = mA;
+}
+
+static int msm_otg_set_power(struct usb_phy *phy, unsigned int mA)
+{
+ struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+
+ /*
+ * Gadget driver uses set_power method to notify about the
+ * available current based on suspend/configured states.
+ *
+ * IDEV_CHG can be drawn irrespective of suspend/un-configured
+ * states when CDP/ACA is connected.
+ */
+ if (motg->chg_type == USB_SDP_CHARGER)
+ msm_otg_notify_charger(motg, mA);
+
+ return 0;
+}
+
+static void msm_hsusb_vbus_power(struct msm_otg *motg, bool on);
+
+static void msm_otg_perf_vote_update(struct msm_otg *motg, bool perf_mode)
+{
+ static bool curr_perf_mode;
+ int ret, latency = motg->pm_qos_latency;
+ long clk_rate;
+
+ if (curr_perf_mode == perf_mode)
+ return;
+
+ if (perf_mode) {
+ if (latency)
+ pm_qos_update_request(&motg->pm_qos_req_dma, latency);
+ msm_otg_bus_vote(motg, USB_MAX_PERF_VOTE);
+ clk_rate = motg->core_clk_rate;
+ } else {
+ if (latency)
+ pm_qos_update_request(&motg->pm_qos_req_dma,
+ PM_QOS_DEFAULT_VALUE);
+ msm_otg_bus_vote(motg, USB_MIN_PERF_VOTE);
+ clk_rate = motg->core_clk_svs_rate;
+ }
+
+ if (clk_rate) {
+ ret = clk_set_rate(motg->core_clk, clk_rate);
+ if (ret)
+ dev_err(motg->phy.dev, "sys_clk set_rate fail:%d %ld\n",
+ ret, clk_rate);
+ }
+ curr_perf_mode = perf_mode;
+ pr_debug("%s: latency updated to: %d, core_freq to: %ld\n", __func__,
+ latency, clk_rate);
+}
+
+static void msm_otg_perf_vote_work(struct work_struct *w)
+{
+ struct msm_otg *motg = container_of(w, struct msm_otg,
+ perf_vote_work.work);
+ unsigned int curr_sample_int_count;
+ bool in_perf_mode = false;
+
+ curr_sample_int_count = motg->usb_irq_count;
+ motg->usb_irq_count = 0;
+
+ if (curr_sample_int_count >= PM_QOS_THRESHOLD)
+ in_perf_mode = true;
+
+ msm_otg_perf_vote_update(motg, in_perf_mode);
+ pr_debug("%s: in_perf_mode:%u, interrupts in last sample:%u\n",
+ __func__, in_perf_mode, curr_sample_int_count);
+
+ schedule_delayed_work(&motg->perf_vote_work,
+ msecs_to_jiffies(1000 * PM_QOS_SAMPLE_SEC));
+}
+
+static void msm_otg_start_host(struct usb_otg *otg, int on)
+{
+ struct msm_otg *motg = container_of(otg->usb_phy, struct msm_otg, phy);
+ struct msm_otg_platform_data *pdata = motg->pdata;
+ struct usb_hcd *hcd;
+ u32 val;
+
+ if (!otg->host)
+ return;
+
+ hcd = bus_to_hcd(otg->host);
+
+ msm_otg_dbg_log_event(&motg->phy, "PM RT: StartHost GET",
+ get_pm_runtime_counter(motg->phy.dev), 0);
+ pm_runtime_get_sync(otg->usb_phy->dev);
+ if (on) {
+ dev_dbg(otg->usb_phy->dev, "host on\n");
+ msm_otg_dbg_log_event(&motg->phy, "HOST ON",
+ motg->inputs, otg->state);
+ msm_hsusb_vbus_power(motg, 1);
+ msm_otg_reset(&motg->phy);
+
+ if (pdata->otg_control == OTG_PHY_CONTROL)
+ ulpi_write(otg->usb_phy, OTG_COMP_DISABLE,
+ ULPI_SET(ULPI_PWR_CLK_MNG_REG));
+
+ if (pdata->enable_axi_prefetch) {
+ val = readl_relaxed(USB_HS_APF_CTRL);
+ val &= ~APF_CTRL_EN;
+ writel_relaxed(val, USB_HS_APF_CTRL);
+ }
+ usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+#ifdef CONFIG_SMP
+ motg->pm_qos_req_dma.type = PM_QOS_REQ_AFFINE_IRQ;
+ motg->pm_qos_req_dma.irq = motg->irq;
+#endif
+ pm_qos_add_request(&motg->pm_qos_req_dma,
+ PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
+ /* start in perf mode for better performance initially */
+ msm_otg_perf_vote_update(motg, true);
+ schedule_delayed_work(&motg->perf_vote_work,
+ msecs_to_jiffies(1000 * PM_QOS_SAMPLE_SEC));
+ } else {
+ dev_dbg(otg->usb_phy->dev, "host off\n");
+ msm_otg_dbg_log_event(&motg->phy, "HOST OFF",
+ motg->inputs, otg->state);
+ msm_hsusb_vbus_power(motg, 0);
+
+ cancel_delayed_work_sync(&motg->perf_vote_work);
+ msm_otg_perf_vote_update(motg, false);
+ pm_qos_remove_request(&motg->pm_qos_req_dma);
+
+ pm_runtime_disable(&hcd->self.root_hub->dev);
+ pm_runtime_barrier(&hcd->self.root_hub->dev);
+ usb_remove_hcd(hcd);
+ msm_otg_reset(&motg->phy);
+
+ if (pdata->enable_axi_prefetch)
+ writel_relaxed(readl_relaxed(USB_HS_APF_CTRL)
+ | (APF_CTRL_EN), USB_HS_APF_CTRL);
+
+ /* HCD core reset all bits of PORTSC. select ULPI phy */
+ writel_relaxed(0x80000000, USB_PORTSC);
+
+ if (pdata->otg_control == OTG_PHY_CONTROL)
+ ulpi_write(otg->usb_phy, OTG_COMP_DISABLE,
+ ULPI_CLR(ULPI_PWR_CLK_MNG_REG));
+ }
+ msm_otg_dbg_log_event(&motg->phy, "PM RT: StartHost PUT",
+ get_pm_runtime_counter(motg->phy.dev), 0);
+
+ pm_runtime_mark_last_busy(otg->usb_phy->dev);
+ pm_runtime_put_autosuspend(otg->usb_phy->dev);
+}
+
+static void msm_hsusb_vbus_power(struct msm_otg *motg, bool on)
+{
+ int ret;
+ static bool vbus_is_on;
+
+ msm_otg_dbg_log_event(&motg->phy, "VBUS POWER", on, vbus_is_on);
+ if (vbus_is_on == on)
+ return;
+
+ if (motg->pdata->vbus_power) {
+ ret = motg->pdata->vbus_power(on);
+ if (!ret)
+ vbus_is_on = on;
+ return;
+ }
+
+ if (!vbus_otg) {
+ pr_err("vbus_otg is NULL.");
+ return;
+ }
+
+ /*
+ * if entering host mode tell the charger to not draw any current
+ * from usb before turning on the boost.
+ * if exiting host mode disable the boost before enabling to draw
+ * current from the source.
+ */
+ if (on) {
+ msm_otg_notify_host_mode(motg, on);
+ ret = regulator_enable(vbus_otg);
+ if (ret) {
+ pr_err("unable to enable vbus_otg\n");
+ return;
+ }
+ vbus_is_on = true;
+ } else {
+ ret = regulator_disable(vbus_otg);
+ if (ret) {
+ pr_err("unable to disable vbus_otg\n");
+ return;
+ }
+ msm_otg_notify_host_mode(motg, on);
+ vbus_is_on = false;
+ }
+}
+
+static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+ struct msm_otg *motg = container_of(otg->usb_phy, struct msm_otg, phy);
+ struct usb_hcd *hcd;
+
+ /*
+ * Fail host registration if this board can support
+ * only peripheral configuration.
+ */
+ if (motg->pdata->mode == USB_PERIPHERAL) {
+ dev_info(otg->usb_phy->dev, "Host mode is not supported\n");
+ return -ENODEV;
+ }
+
+ if (!motg->pdata->vbus_power && host) {
+ vbus_otg = devm_regulator_get(motg->phy.dev, "vbus_otg");
+ if (IS_ERR(vbus_otg)) {
+ msm_otg_dbg_log_event(&motg->phy,
+ "UNABLE TO GET VBUS_OTG",
+ otg->state, 0);
+ pr_err("Unable to get vbus_otg\n");
+ return PTR_ERR(vbus_otg);
+ }
+ }
+
+ if (!host) {
+ if (otg->state == OTG_STATE_A_HOST) {
+ msm_otg_start_host(otg, 0);
+ otg->host = NULL;
+ otg->state = OTG_STATE_UNDEFINED;
+ queue_work(motg->otg_wq, &motg->sm_work);
+ } else {
+ otg->host = NULL;
+ }
+
+ return 0;
+ }
+
+ hcd = bus_to_hcd(host);
+ hcd->power_budget = motg->pdata->power_budget;
+
+ otg->host = host;
+ dev_dbg(otg->usb_phy->dev, "host driver registered w/ tranceiver\n");
+ msm_otg_dbg_log_event(&motg->phy, "HOST DRIVER REGISTERED",
+ hcd->power_budget, motg->pdata->mode);
+
+ /*
+ * Kick the state machine work, if peripheral is not supported
+ * or peripheral is already registered with us.
+ */
+ if (motg->pdata->mode == USB_HOST || otg->gadget)
+ queue_work(motg->otg_wq, &motg->sm_work);
+
+ return 0;
+}
+
+static void msm_otg_start_peripheral(struct usb_otg *otg, int on)
+{
+ struct msm_otg *motg = container_of(otg->usb_phy, struct msm_otg, phy);
+ struct msm_otg_platform_data *pdata = motg->pdata;
+ struct pinctrl_state *set_state;
+ int ret;
+
+ if (!otg->gadget)
+ return;
+
+ msm_otg_dbg_log_event(&motg->phy, "PM RT: StartPeri GET",
+ get_pm_runtime_counter(motg->phy.dev), 0);
+ pm_runtime_get_sync(otg->usb_phy->dev);
+ if (on) {
+ dev_dbg(otg->usb_phy->dev, "gadget on\n");
+ msm_otg_dbg_log_event(&motg->phy, "GADGET ON",
+ motg->inputs, otg->state);
+
+ /* Configure BUS performance parameters for MAX bandwidth */
+ if (debug_bus_voting_enabled)
+ msm_otg_bus_vote(motg, USB_MAX_PERF_VOTE);
+ /* bump up usb core_clk to default */
+ clk_set_rate(motg->core_clk, motg->core_clk_rate);
+
+ usb_gadget_vbus_connect(otg->gadget);
+
+ /*
+ * Request VDD min gpio, if need to support VDD
+ * minimazation during peripheral bus suspend.
+ */
+ if (pdata->vddmin_gpio) {
+ if (motg->phy_pinctrl) {
+ set_state =
+ pinctrl_lookup_state(motg->phy_pinctrl,
+ "hsusb_active");
+ if (IS_ERR(set_state)) {
+ pr_err("cannot get phy pinctrl active state\n");
+ } else {
+ pinctrl_select_state(motg->phy_pinctrl,
+ set_state);
+ }
+ }
+
+ ret = gpio_request(pdata->vddmin_gpio,
+ "MSM_OTG_VDD_MIN_GPIO");
+ if (ret < 0) {
+ dev_err(otg->usb_phy->dev, "gpio req failed for vdd min:%d\n",
+ ret);
+ pdata->vddmin_gpio = 0;
+ }
+ }
+ } else {
+ dev_dbg(otg->usb_phy->dev, "gadget off\n");
+ msm_otg_dbg_log_event(&motg->phy, "GADGET OFF",
+ motg->inputs, otg->state);
+ usb_gadget_vbus_disconnect(otg->gadget);
+ clear_bit(A_BUS_SUSPEND, &motg->inputs);
+ /* Configure BUS performance parameters to default */
+ msm_otg_bus_vote(motg, USB_MIN_PERF_VOTE);
+
+ if (pdata->vddmin_gpio) {
+ gpio_free(pdata->vddmin_gpio);
+ if (motg->phy_pinctrl) {
+ set_state =
+ pinctrl_lookup_state(motg->phy_pinctrl,
+ "hsusb_sleep");
+ if (IS_ERR(set_state))
+ pr_err("cannot get phy pinctrl sleep state\n");
+ else
+ pinctrl_select_state(motg->phy_pinctrl,
+ set_state);
+ }
+ }
+ }
+ msm_otg_dbg_log_event(&motg->phy, "PM RT: StartPeri PUT",
+ get_pm_runtime_counter(motg->phy.dev), 0);
+ pm_runtime_mark_last_busy(otg->usb_phy->dev);
+ pm_runtime_put_autosuspend(otg->usb_phy->dev);
+}
+
+static int msm_otg_set_peripheral(struct usb_otg *otg,
+ struct usb_gadget *gadget)
+{
+ struct msm_otg *motg = container_of(otg->usb_phy, struct msm_otg, phy);
+
+ /*
+ * Fail peripheral registration if this board can support
+ * only host configuration.
+ */
+ if (motg->pdata->mode == USB_HOST) {
+ dev_info(otg->usb_phy->dev, "Peripheral mode is not supported\n");
+ return -ENODEV;
+ }
+
+ if (!gadget) {
+ if (otg->state == OTG_STATE_B_PERIPHERAL) {
+ msm_otg_dbg_log_event(&motg->phy,
+ "PM RUNTIME: PERIPHERAL GET1",
+ get_pm_runtime_counter(otg->usb_phy->dev), 0);
+ msm_otg_start_peripheral(otg, 0);
+ otg->gadget = NULL;
+ otg->state = OTG_STATE_UNDEFINED;
+ queue_work(motg->otg_wq, &motg->sm_work);
+ } else {
+ otg->gadget = NULL;
+ }
+
+ return 0;
+ }
+ otg->gadget = gadget;
+ dev_dbg(otg->usb_phy->dev, "peripheral driver registered w/ tranceiver\n");
+ msm_otg_dbg_log_event(&motg->phy, "PERIPHERAL DRIVER REGISTERED",
+ otg->state, motg->pdata->mode);
+
+ /*
+ * Kick the state machine work, if host is not supported
+ * or host is already registered with us.
+ */
+ if (motg->pdata->mode == USB_PERIPHERAL || otg->host)
+ queue_work(motg->otg_wq, &motg->sm_work);
+
+ return 0;
+}
+
+static bool msm_otg_read_pmic_id_state(struct msm_otg *motg)
+{
+ unsigned long flags;
+ bool id;
+ int ret;
+
+ if (!motg->pdata->pmic_id_irq)
+ return -ENODEV;
+
+ local_irq_save(flags);
+ ret = irq_get_irqchip_state(motg->pdata->pmic_id_irq,
+ IRQCHIP_STATE_LINE_LEVEL, &id);
+ local_irq_restore(flags);
+
+ /*
+ * If we can not read ID line state for some reason, treat
+ * it as float. This would prevent MHL discovery and kicking
+ * host mode unnecessarily.
+ */
+ if (ret < 0)
+ return true;
+
+ return !!id;
+}
+
+static bool msm_otg_read_phy_id_state(struct msm_otg *motg)
+{
+ u8 val;
+
+ /*
+ * clear the pending/outstanding interrupts and
+ * read the ID status from the SRC_STATUS register.
+ */
+ writeb_relaxed(USB_PHY_ID_MASK, USB2_PHY_USB_PHY_INTERRUPT_CLEAR1);
+
+ writeb_relaxed(0x1, USB2_PHY_USB_PHY_IRQ_CMD);
+ /*
+ * Databook says 200 usec delay is required for
+ * clearing the interrupts.
+ */
+ udelay(200);
+ writeb_relaxed(0x0, USB2_PHY_USB_PHY_IRQ_CMD);
+
+ val = readb_relaxed(USB2_PHY_USB_PHY_INTERRUPT_SRC_STATUS);
+ if (val & USB_PHY_IDDIG_1_0)
+ return false; /* ID is grounded */
+ else
+ return true;
+}
+
+static void msm_otg_chg_check_timer_func(unsigned long data)
+{
+ struct msm_otg *motg = (struct msm_otg *) data;
+ struct usb_otg *otg = motg->phy.otg;
+
+ if (atomic_read(&motg->in_lpm) ||
+ !test_bit(B_SESS_VLD, &motg->inputs) ||
+ otg->state != OTG_STATE_B_PERIPHERAL ||
+ otg->gadget->speed != USB_SPEED_UNKNOWN) {
+ dev_dbg(otg->usb_phy->dev, "Nothing to do in chg_check_timer\n");
+ return;
+ }
+
+ if ((readl_relaxed(USB_PORTSC) & PORTSC_LS) == PORTSC_LS) {
+ dev_dbg(otg->usb_phy->dev, "DCP is detected as SDP\n");
+ msm_otg_dbg_log_event(&motg->phy, "DCP IS DETECTED AS SDP",
+ otg->state, 0);
+ set_bit(B_FALSE_SDP, &motg->inputs);
+ queue_work(motg->otg_wq, &motg->sm_work);
+ }
+}
+
+static bool msm_chg_check_secondary_det(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+ u32 chg_det;
+ bool ret = false;
+
+ switch (motg->pdata->phy_type) {
+ case SNPS_PICO_PHY:
+ case SNPS_FEMTO_PHY:
+ chg_det = ulpi_read(phy, 0x87);
+ ret = chg_det & 1;
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static void msm_chg_enable_secondary_det(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+
+ switch (motg->pdata->phy_type) {
+ case SNPS_PICO_PHY:
+ case SNPS_FEMTO_PHY:
+ /*
+ * Configure DM as current source, DP as current sink
+ * and enable battery charging comparators.
+ */
+ ulpi_write(phy, 0x8, 0x85);
+ ulpi_write(phy, 0x2, 0x85);
+ ulpi_write(phy, 0x1, 0x85);
+ break;
+ default:
+ break;
+ }
+}
+
+static bool msm_chg_check_primary_det(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+ u32 chg_det;
+ bool ret = false;
+
+ switch (motg->pdata->phy_type) {
+ case SNPS_PICO_PHY:
+ case SNPS_FEMTO_PHY:
+ chg_det = ulpi_read(phy, 0x87);
+ ret = chg_det & 1;
+ /* Turn off VDP_SRC */
+ ulpi_write(phy, 0x3, 0x86);
+ msleep(20);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static void msm_chg_enable_primary_det(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+
+ switch (motg->pdata->phy_type) {
+ case SNPS_PICO_PHY:
+ case SNPS_FEMTO_PHY:
+ /*
+ * Configure DP as current source, DM as current sink
+ * and enable battery charging comparators.
+ */
+ ulpi_write(phy, 0x2, 0x85);
+ ulpi_write(phy, 0x1, 0x85);
+ break;
+ default:
+ break;
+ }
+}
+
+static bool msm_chg_check_dcd(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+ u32 line_state;
+ bool ret = false;
+
+ switch (motg->pdata->phy_type) {
+ case SNPS_PICO_PHY:
+ case SNPS_FEMTO_PHY:
+ line_state = ulpi_read(phy, 0x87);
+ ret = line_state & 2;
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static void msm_chg_disable_dcd(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+
+ switch (motg->pdata->phy_type) {
+ case SNPS_PICO_PHY:
+ ulpi_write(phy, 0x10, 0x86);
+ break;
+ case SNPS_FEMTO_PHY:
+ ulpi_write(phy, 0x10, 0x86);
+ /*
+ * Disable the Rdm_down after
+ * the DCD is completed.
+ */
+ ulpi_write(phy, 0x04, 0x0C);
+ break;
+ default:
+ break;
+ }
+}
+
+static void msm_chg_enable_dcd(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+
+ switch (motg->pdata->phy_type) {
+ case SNPS_PICO_PHY:
+ /* Data contact detection enable */
+ ulpi_write(phy, 0x10, 0x85);
+ break;
+ case SNPS_FEMTO_PHY:
+ /*
+ * Idp_src and Rdm_down are de-coupled
+ * on Femto PHY. If Idp_src alone is
+ * enabled, DCD timeout is observed with
+ * wall charger. But a genuine DCD timeout
+ * may be incorrectly interpreted. Also
+ * BC1.2 compliance testers expect Rdm_down
+ * to enabled during DCD. Enable Rdm_down
+ * explicitly before enabling the DCD.
+ */
+ ulpi_write(phy, 0x04, 0x0B);
+ ulpi_write(phy, 0x10, 0x85);
+ break;
+ default:
+ break;
+ }
+}
+
+static void msm_chg_block_on(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+ u32 func_ctrl;
+
+ /* put the controller in non-driving mode */
+ func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL);
+ func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+ func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
+ ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL);
+
+ switch (motg->pdata->phy_type) {
+ case SNPS_PICO_PHY:
+ case SNPS_FEMTO_PHY:
+ /* disable DP and DM pull down resistors */
+ ulpi_write(phy, 0x6, 0xC);
+ /* Clear charger detecting control bits */
+ ulpi_write(phy, 0x1F, 0x86);
+ /* Clear alt interrupt latch and enable bits */
+ ulpi_write(phy, 0x1F, 0x92);
+ ulpi_write(phy, 0x1F, 0x95);
+ udelay(100);
+ break;
+ default:
+ break;
+ }
+}
+
+static void msm_chg_block_off(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+ u32 func_ctrl;
+
+ switch (motg->pdata->phy_type) {
+ case SNPS_PICO_PHY:
+ case SNPS_FEMTO_PHY:
+ /* Clear charger detecting control bits */
+ ulpi_write(phy, 0x3F, 0x86);
+ /* Clear alt interrupt latch and enable bits */
+ ulpi_write(phy, 0x1F, 0x92);
+ ulpi_write(phy, 0x1F, 0x95);
+ /* re-enable DP and DM pull down resistors */
+ ulpi_write(phy, 0x6, 0xB);
+ break;
+ default:
+ break;
+ }
+
+ /* put the controller in normal mode */
+ func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL);
+ func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+ func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
+ ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL);
+}
+
+static const char *chg_to_string(enum usb_chg_type chg_type)
+{
+ switch (chg_type) {
+ case USB_SDP_CHARGER: return "USB_SDP_CHARGER";
+ case USB_DCP_CHARGER: return "USB_DCP_CHARGER";
+ case USB_CDP_CHARGER: return "USB_CDP_CHARGER";
+ case USB_NONCOMPLIANT_CHARGER: return "USB_NONCOMPLIANT_CHARGER";
+ case USB_FLOATED_CHARGER: return "USB_FLOATED_CHARGER";
+ default: return "INVALID_CHARGER";
+ }
+}
+
+#define MSM_CHG_DCD_TIMEOUT (750 * HZ/1000) /* 750 msec */
+#define MSM_CHG_DCD_POLL_TIME (50 * HZ/1000) /* 50 msec */
+#define MSM_CHG_PRIMARY_DET_TIME (50 * HZ/1000) /* TVDPSRC_ON */
+#define MSM_CHG_SECONDARY_DET_TIME (50 * HZ/1000) /* TVDMSRC_ON */
+static void msm_chg_detect_work(struct work_struct *w)
+{
+ struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work);
+ struct usb_phy *phy = &motg->phy;
+ bool is_dcd = false, tmout, vout;
+ static bool dcd;
+ u32 line_state, dm_vlgc;
+ unsigned long delay;
+
+ dev_dbg(phy->dev, "chg detection work\n");
+ msm_otg_dbg_log_event(phy, "CHG DETECTION WORK",
+ motg->chg_state, get_pm_runtime_counter(phy->dev));
+
+ switch (motg->chg_state) {
+ case USB_CHG_STATE_UNDEFINED:
+ case USB_CHG_STATE_IN_PROGRESS:
+ msm_chg_block_on(motg);
+ msm_chg_enable_dcd(motg);
+ motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
+ motg->dcd_time = 0;
+ delay = MSM_CHG_DCD_POLL_TIME;
+ break;
+ case USB_CHG_STATE_WAIT_FOR_DCD:
+ is_dcd = msm_chg_check_dcd(motg);
+ motg->dcd_time += MSM_CHG_DCD_POLL_TIME;
+ tmout = motg->dcd_time >= MSM_CHG_DCD_TIMEOUT;
+ if (is_dcd || tmout) {
+ if (is_dcd)
+ dcd = true;
+ else
+ dcd = false;
+ msm_chg_disable_dcd(motg);
+ msm_chg_enable_primary_det(motg);
+ delay = MSM_CHG_PRIMARY_DET_TIME;
+ motg->chg_state = USB_CHG_STATE_DCD_DONE;
+ } else {
+ delay = MSM_CHG_DCD_POLL_TIME;
+ }
+ break;
+ case USB_CHG_STATE_DCD_DONE:
+ vout = msm_chg_check_primary_det(motg);
+ line_state = readl_relaxed(USB_PORTSC) & PORTSC_LS;
+ dm_vlgc = line_state & PORTSC_LS_DM;
+ if (vout && !dm_vlgc) { /* VDAT_REF < DM < VLGC */
+ if (line_state) { /* DP > VLGC */
+ motg->chg_type = USB_NONCOMPLIANT_CHARGER;
+ motg->chg_state = USB_CHG_STATE_DETECTED;
+ delay = 0;
+ } else {
+ msm_chg_enable_secondary_det(motg);
+ delay = MSM_CHG_SECONDARY_DET_TIME;
+ motg->chg_state = USB_CHG_STATE_PRIMARY_DONE;
+ }
+ } else { /* DM < VDAT_REF || DM > VLGC */
+ if (line_state) /* DP > VLGC or/and DM > VLGC */
+ motg->chg_type = USB_NONCOMPLIANT_CHARGER;
+ else if (!dcd && floated_charger_enable)
+ motg->chg_type = USB_FLOATED_CHARGER;
+ else
+ motg->chg_type = USB_SDP_CHARGER;
+
+ motg->chg_state = USB_CHG_STATE_DETECTED;
+ delay = 0;
+ goto state_detected;
+ }
+ break;
+ case USB_CHG_STATE_PRIMARY_DONE:
+ vout = msm_chg_check_secondary_det(motg);
+ if (vout)
+ motg->chg_type = USB_DCP_CHARGER;
+ else
+ motg->chg_type = USB_CDP_CHARGER;
+ motg->chg_state = USB_CHG_STATE_SECONDARY_DONE;
+ /* fall through */
+ case USB_CHG_STATE_SECONDARY_DONE:
+ motg->chg_state = USB_CHG_STATE_DETECTED;
+ case USB_CHG_STATE_DETECTED:
+state_detected:
+ /*
+ * Notify the charger type to power supply
+ * owner as soon as we determine the charger.
+ */
+ if (motg->chg_type == USB_DCP_CHARGER && motg->ext_chg_opened) {
+ init_completion(&motg->ext_chg_wait);
+ motg->ext_chg_active = DEFAULT;
+ }
+ msm_otg_notify_chg_type(motg);
+ msm_chg_block_off(motg);
+
+ /* Enable VDP_SRC in case of DCP charger */
+ if (motg->chg_type == USB_DCP_CHARGER)
+ ulpi_write(phy, 0x2, 0x85);
+
+ dev_dbg(phy->dev, "chg_type = %s\n",
+ chg_to_string(motg->chg_type));
+ msm_otg_dbg_log_event(phy, "CHG WORK PUT: CHG_TYPE",
+ motg->chg_type, get_pm_runtime_counter(phy->dev));
+ /* to match _get from sm_work before starting chg_det_work */
+ pm_runtime_mark_last_busy(phy->dev);
+ pm_runtime_put_autosuspend(phy->dev);
+
+ queue_work(motg->otg_wq, &motg->sm_work);
+ return;
+ default:
+ return;
+ }
+
+ msm_otg_dbg_log_event(phy, "CHG WORK: QUEUE", motg->chg_type, delay);
+ queue_delayed_work(motg->otg_wq, &motg->chg_work, delay);
+}
+
+#define VBUS_INIT_TIMEOUT msecs_to_jiffies(5000)
+
+/*
+ * We support OTG, Peripheral only and Host only configurations. In case
+ * of OTG, mode switch (host-->peripheral/peripheral-->host) can happen
+ * via Id pin status or user request (debugfs). Id/BSV interrupts are not
+ * enabled when switch is controlled by user and default mode is supplied
+ * by board file, which can be changed by userspace later.
+ */
+static void msm_otg_init_sm(struct msm_otg *motg)
+{
+ struct msm_otg_platform_data *pdata = motg->pdata;
+ u32 otgsc = readl_relaxed(USB_OTGSC);
+ int ret;
+
+ switch (pdata->mode) {
+ case USB_OTG:
+ if (pdata->otg_control == OTG_USER_CONTROL) {
+ if (pdata->default_mode == USB_HOST) {
+ clear_bit(ID, &motg->inputs);
+ } else if (pdata->default_mode == USB_PERIPHERAL) {
+ set_bit(ID, &motg->inputs);
+ set_bit(B_SESS_VLD, &motg->inputs);
+ } else {
+ set_bit(ID, &motg->inputs);
+ clear_bit(B_SESS_VLD, &motg->inputs);
+ }
+ } else if (pdata->otg_control == OTG_PHY_CONTROL) {
+ if (otgsc & OTGSC_ID)
+ set_bit(ID, &motg->inputs);
+ else
+ clear_bit(ID, &motg->inputs);
+ if (otgsc & OTGSC_BSV)
+ set_bit(B_SESS_VLD, &motg->inputs);
+ else
+ clear_bit(B_SESS_VLD, &motg->inputs);
+ } else if (pdata->otg_control == OTG_PMIC_CONTROL) {
+ if (pdata->pmic_id_irq) {
+ if (msm_otg_read_pmic_id_state(motg))
+ set_bit(ID, &motg->inputs);
+ else
+ clear_bit(ID, &motg->inputs);
+ } else if (motg->ext_id_irq) {
+ if (gpio_get_value(pdata->usb_id_gpio))
+ set_bit(ID, &motg->inputs);
+ else
+ clear_bit(ID, &motg->inputs);
+ } else if (motg->phy_irq) {
+ if (msm_otg_read_phy_id_state(motg))
+ set_bit(ID, &motg->inputs);
+ else
+ clear_bit(ID, &motg->inputs);
+ }
+ /*
+ * VBUS initial state is reported after PMIC
+ * driver initialization. Wait for it.
+ */
+ ret = wait_for_completion_timeout(&pmic_vbus_init,
+ VBUS_INIT_TIMEOUT);
+ if (!ret) {
+ dev_dbg(motg->phy.dev, "%s: timeout waiting for PMIC VBUS\n",
+ __func__);
+ msm_otg_dbg_log_event(&motg->phy,
+ "PMIC VBUS WAIT TMOUT", motg->inputs,
+ motg->phy.otg->state);
+ clear_bit(B_SESS_VLD, &motg->inputs);
+ pmic_vbus_init.done = 1;
+ }
+ }
+ break;
+ case USB_HOST:
+ clear_bit(ID, &motg->inputs);
+ break;
+ case USB_PERIPHERAL:
+ set_bit(ID, &motg->inputs);
+ if (pdata->otg_control == OTG_PHY_CONTROL) {
+ if (otgsc & OTGSC_BSV)
+ set_bit(B_SESS_VLD, &motg->inputs);
+ else
+ clear_bit(B_SESS_VLD, &motg->inputs);
+ } else if (pdata->otg_control == OTG_PMIC_CONTROL) {
+ /*
+ * VBUS initial state is reported after PMIC
+ * driver initialization. Wait for it.
+ */
+ ret = wait_for_completion_timeout(&pmic_vbus_init,
+ VBUS_INIT_TIMEOUT);
+ if (!ret) {
+ dev_dbg(motg->phy.dev, "%s: timeout waiting for PMIC VBUS\n",
+ __func__);
+ msm_otg_dbg_log_event(&motg->phy,
+ "PMIC VBUS WAIT TMOUT", motg->inputs,
+ motg->phy.otg->state);
+ clear_bit(B_SESS_VLD, &motg->inputs);
+ pmic_vbus_init.done = 1;
+ }
+ } else if (pdata->otg_control == OTG_USER_CONTROL) {
+ set_bit(ID, &motg->inputs);
+ set_bit(B_SESS_VLD, &motg->inputs);
+ }
+ break;
+ default:
+ break;
+ }
+ msm_otg_dbg_log_event(&motg->phy, "SM INIT", pdata->mode, motg->inputs);
+ if (motg->id_state != USB_ID_GROUND)
+ motg->id_state = (test_bit(ID, &motg->inputs)) ? USB_ID_FLOAT :
+ USB_ID_GROUND;
+}
+
+static void msm_otg_wait_for_ext_chg_done(struct msm_otg *motg)
+{
+ struct usb_phy *phy = &motg->phy;
+ unsigned long t;
+
+ /*
+ * Defer next cable connect event till external charger
+ * detection is completed.
+ */
+
+ if (motg->ext_chg_active == ACTIVE) {
+
+do_wait:
+ pr_debug("before msm_otg ext chg wait\n");
+ msm_otg_dbg_log_event(&motg->phy, "EXT CHG: WAIT", 0, 0);
+
+ t = wait_for_completion_timeout(&motg->ext_chg_wait,
+ msecs_to_jiffies(3000));
+ msm_otg_dbg_log_event(&motg->phy, "EXT CHG: DONE", t, 0);
+
+ if (!t)
+ pr_err("msm_otg ext chg wait timeout\n");
+ else if (motg->ext_chg_active == ACTIVE)
+ goto do_wait;
+ else
+ pr_debug("msm_otg ext chg wait done\n");
+ }
+
+ if (motg->ext_chg_opened) {
+ if (phy->flags & ENABLE_DP_MANUAL_PULLUP) {
+ ulpi_write(phy, ULPI_MISC_A_VBUSVLDEXT |
+ ULPI_MISC_A_VBUSVLDEXTSEL,
+ ULPI_CLR(ULPI_MISC_A));
+ }
+ /* clear charging register bits */
+ ulpi_write(phy, 0x3F, 0x86);
+ /* re-enable DP and DM pull-down resistors*/
+ ulpi_write(phy, 0x6, 0xB);
+ }
+}
+
+static void msm_otg_sm_work(struct work_struct *w)
+{
+ struct msm_otg *motg = container_of(w, struct msm_otg, sm_work);
+ struct usb_otg *otg = motg->phy.otg;
+ struct device *dev = otg->usb_phy->dev;
+ bool work = 0, dcp;
+ int ret;
+
+ pr_debug("%s work\n", usb_otg_state_string(otg->state));
+ msm_otg_dbg_log_event(&motg->phy, "SM WORK:",
+ otg->state, motg->inputs);
+
+ /* Just resume h/w if reqd, pm_count is handled based on state/inputs */
+ if (motg->resume_pending) {
+ pm_runtime_get_sync(otg->usb_phy->dev);
+ if (atomic_read(&motg->in_lpm)) {
+ dev_err(dev, "SM WORK: USB is in LPM\n");
+ msm_otg_dbg_log_event(&motg->phy,
+ "SM WORK: USB IS IN LPM",
+ otg->state, motg->inputs);
+ msm_otg_resume(motg);
+ }
+ motg->resume_pending = false;
+ pm_runtime_put_noidle(otg->usb_phy->dev);
+ }
+
+ switch (otg->state) {
+ case OTG_STATE_UNDEFINED:
+ pm_runtime_get_sync(otg->usb_phy->dev);
+ msm_otg_reset(otg->usb_phy);
+ /* Add child device only after block reset */
+ ret = of_platform_populate(motg->pdev->dev.of_node, NULL, NULL,
+ &motg->pdev->dev);
+ if (ret)
+ dev_dbg(&motg->pdev->dev, "failed to add BAM core\n");
+
+ msm_otg_init_sm(motg);
+ otg->state = OTG_STATE_B_IDLE;
+ if (!test_bit(B_SESS_VLD, &motg->inputs) &&
+ test_bit(ID, &motg->inputs)) {
+ msm_otg_dbg_log_event(&motg->phy,
+ "PM RUNTIME: UNDEF PUT",
+ get_pm_runtime_counter(otg->usb_phy->dev), 0);
+ pm_runtime_put_sync(otg->usb_phy->dev);
+ break;
+ }
+ pm_runtime_put(otg->usb_phy->dev);
+ /* FALL THROUGH */
+ case OTG_STATE_B_IDLE:
+ if (!test_bit(ID, &motg->inputs) && otg->host) {
+ pr_debug("!id\n");
+ msm_otg_dbg_log_event(&motg->phy, "!ID",
+ motg->inputs, otg->state);
+
+ msm_otg_start_host(otg, 1);
+ otg->state = OTG_STATE_A_HOST;
+ } else if (test_bit(B_SESS_VLD, &motg->inputs)) {
+ pr_debug("b_sess_vld\n");
+ msm_otg_dbg_log_event(&motg->phy, "B_SESS_VLD",
+ motg->inputs, otg->state);
+ switch (motg->chg_state) {
+ case USB_CHG_STATE_UNDEFINED:
+ /* put at the end of chg_det or disconnect */
+ pm_runtime_get_sync(otg->usb_phy->dev);
+ msm_otg_dbg_log_event(&motg->phy, "PM CHG GET",
+ get_pm_runtime_counter(dev), 0);
+ motg->chg_state = USB_CHG_STATE_IN_PROGRESS;
+ msm_chg_detect_work(&motg->chg_work.work);
+ break;
+ case USB_CHG_STATE_DETECTED:
+ switch (motg->chg_type) {
+ case USB_DCP_CHARGER:
+ /* fall through */
+ case USB_NONCOMPLIANT_CHARGER:
+ msm_otg_notify_charger(motg,
+ dcp_max_current);
+ if (!motg->is_ext_chg_dcp)
+ otg->state =
+ OTG_STATE_B_CHARGER;
+ break;
+ case USB_FLOATED_CHARGER:
+ msm_otg_notify_charger(motg,
+ IDEV_CHG_MAX);
+ otg->state = OTG_STATE_B_CHARGER;
+ break;
+ case USB_CDP_CHARGER:
+ msm_otg_notify_charger(motg,
+ IDEV_CHG_MAX);
+ /* fall through */
+ case USB_SDP_CHARGER:
+ pm_runtime_get_sync(otg->usb_phy->dev);
+ msm_otg_start_peripheral(otg, 1);
+ otg->state =
+ OTG_STATE_B_PERIPHERAL;
+ mod_timer(&motg->chg_check_timer,
+ CHG_RECHECK_DELAY);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ pr_debug("chg_work cancel");
+ msm_otg_dbg_log_event(&motg->phy, "CHG_WORK CANCEL",
+ motg->inputs, otg->state);
+ del_timer_sync(&motg->chg_check_timer);
+ clear_bit(B_FALSE_SDP, &motg->inputs);
+ cancel_delayed_work_sync(&motg->chg_work);
+ /*
+ * Find out whether chg_w couldn't start or finished.
+ * In both the cases, runtime ref_count vote is missing
+ */
+ if (motg->chg_state == USB_CHG_STATE_UNDEFINED ||
+ motg->chg_state == USB_CHG_STATE_DETECTED) {
+ msm_otg_dbg_log_event(&motg->phy, "RT !CHG GET",
+ get_pm_runtime_counter(otg->usb_phy->dev), 0);
+ pm_runtime_get_sync(dev);
+ }
+
+ dcp = (motg->chg_type == USB_DCP_CHARGER);
+ motg->chg_state = USB_CHG_STATE_UNDEFINED;
+ motg->chg_type = USB_INVALID_CHARGER;
+ msm_otg_notify_charger(motg, 0);
+ if (dcp) {
+ if (motg->ext_chg_active == DEFAULT)
+ motg->ext_chg_active = INACTIVE;
+ msm_otg_wait_for_ext_chg_done(motg);
+ /* Turn off VDP_SRC */
+ ulpi_write(otg->usb_phy, 0x2, 0x86);
+ }
+ msm_chg_block_off(motg);
+ msm_otg_dbg_log_event(&motg->phy, "RT: CHG A PUT",
+ get_pm_runtime_counter(otg->usb_phy->dev), 0);
+ /* Delay used only if autosuspend enabled */
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ }
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ if (test_bit(B_SESS_VLD, &motg->inputs) &&
+ test_bit(B_FALSE_SDP, &motg->inputs)) {
+ pr_debug("B_FALSE_SDP\n");
+ msm_otg_start_peripheral(otg, 0);
+ motg->chg_type = USB_DCP_CHARGER;
+ clear_bit(B_FALSE_SDP, &motg->inputs);
+ otg->state = OTG_STATE_B_IDLE;
+ msm_otg_dbg_log_event(&motg->phy, "B_FALSE_SDP PUT",
+ get_pm_runtime_counter(dev), motg->inputs);
+ pm_runtime_put_sync(dev);
+ /* schedule work to update charging current */
+ work = 1;
+ } else if (!test_bit(B_SESS_VLD, &motg->inputs)) {
+ msm_otg_start_peripheral(otg, 0);
+ msm_otg_dbg_log_event(&motg->phy, "RT PM: B_PERI A PUT",
+ get_pm_runtime_counter(dev), 0);
+ /* _put for _get done on cable connect in B_IDLE */
+ pm_runtime_put_noidle(dev);
+ /* Schedule work to finish cable disconnect processing*/
+ otg->state = OTG_STATE_B_IDLE;
+ work = 1;
+ } else if (test_bit(A_BUS_SUSPEND, &motg->inputs)) {
+ pr_debug("a_bus_suspend\n");
+ msm_otg_dbg_log_event(&motg->phy,
+ "BUS_SUSPEND: PM RT PUT",
+ get_pm_runtime_counter(dev), 0);
+ otg->state = OTG_STATE_B_SUSPEND;
+ /* _get on connect in B_IDLE or host resume in B_SUSP */
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ }
+ break;
+ case OTG_STATE_B_SUSPEND:
+ if (!test_bit(B_SESS_VLD, &motg->inputs)) {
+ msm_otg_start_peripheral(otg, 0);
+ otg->state = OTG_STATE_B_IDLE;
+ /* Schedule work to finish cable disconnect processing*/
+ work = 1;
+ } else if (!test_bit(A_BUS_SUSPEND, &motg->inputs)) {
+ pr_debug("!a_bus_suspend\n");
+ otg->state = OTG_STATE_B_PERIPHERAL;
+ msm_otg_dbg_log_event(&motg->phy,
+ "BUS_RESUME: PM RT GET",
+ get_pm_runtime_counter(dev), 0);
+ pm_runtime_get_sync(dev);
+ }
+ break;
+
+ case OTG_STATE_B_CHARGER:
+ if (test_bit(B_SESS_VLD, &motg->inputs)) {
+ pr_debug("BSV set again\n");
+ msm_otg_dbg_log_event(&motg->phy, "BSV SET AGAIN",
+ motg->inputs, otg->state);
+ } else if (!test_bit(B_SESS_VLD, &motg->inputs)) {
+ otg->state = OTG_STATE_B_IDLE;
+ work = 1;
+ }
+ break;
+ case OTG_STATE_A_HOST:
+ if (test_bit(ID, &motg->inputs)) {
+ msm_otg_start_host(otg, 0);
+ otg->state = OTG_STATE_B_IDLE;
+ work = 1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (work)
+ queue_work(motg->otg_wq, &motg->sm_work);
+}
+
+static irqreturn_t msm_otg_irq(int irq, void *data)
+{
+ struct msm_otg *motg = data;
+ struct usb_otg *otg = motg->phy.otg;
+ u32 otgsc = 0;
+ bool work = 0;
+
+ if (atomic_read(&motg->in_lpm)) {
+ pr_debug("OTG IRQ: %d in LPM\n", irq);
+ msm_otg_dbg_log_event(&motg->phy, "OTG IRQ IS IN LPM",
+ irq, otg->state);
+ /*Ignore interrupt if one interrupt already seen in LPM*/
+ if (motg->async_int)
+ return IRQ_HANDLED;
+
+ disable_irq_nosync(irq);
+ motg->async_int = irq;
+ msm_otg_kick_sm_work(motg);
+
+ return IRQ_HANDLED;
+ }
+ motg->usb_irq_count++;
+
+ otgsc = readl_relaxed(USB_OTGSC);
+ if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS)))
+ return IRQ_NONE;
+
+ if ((otgsc & OTGSC_IDIS) && (otgsc & OTGSC_IDIE)) {
+ if (otgsc & OTGSC_ID) {
+ dev_dbg(otg->usb_phy->dev, "ID set\n");
+ msm_otg_dbg_log_event(&motg->phy, "ID SET",
+ motg->inputs, otg->state);
+ set_bit(ID, &motg->inputs);
+ } else {
+ dev_dbg(otg->usb_phy->dev, "ID clear\n");
+ msm_otg_dbg_log_event(&motg->phy, "ID CLEAR",
+ motg->inputs, otg->state);
+ clear_bit(ID, &motg->inputs);
+ }
+ work = 1;
+ } else if ((otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {
+ if (otgsc & OTGSC_BSV) {
+ dev_dbg(otg->usb_phy->dev, "BSV set\n");
+ msm_otg_dbg_log_event(&motg->phy, "BSV SET",
+ motg->inputs, otg->state);
+ set_bit(B_SESS_VLD, &motg->inputs);
+ } else {
+ dev_dbg(otg->usb_phy->dev, "BSV clear\n");
+ msm_otg_dbg_log_event(&motg->phy, "BSV CLEAR",
+ motg->inputs, otg->state);
+ clear_bit(B_SESS_VLD, &motg->inputs);
+ clear_bit(A_BUS_SUSPEND, &motg->inputs);
+ }
+ work = 1;
+ }
+ if (work)
+ queue_work(motg->otg_wq, &motg->sm_work);
+
+ writel_relaxed(otgsc, USB_OTGSC);
+
+ return IRQ_HANDLED;
+}
+
+static void msm_otg_set_vbus_state(int online)
+{
+ struct msm_otg *motg = the_msm_otg;
+ static bool init;
+
+ motg->vbus_state = online;
+
+ if (motg->err_event_seen)
+ return;
+
+ if (online) {
+ pr_debug("PMIC: BSV set\n");
+ msm_otg_dbg_log_event(&motg->phy, "PMIC: BSV SET",
+ init, motg->inputs);
+ if (test_and_set_bit(B_SESS_VLD, &motg->inputs) && init)
+ return;
+ } else {
+ pr_debug("PMIC: BSV clear\n");
+ msm_otg_dbg_log_event(&motg->phy, "PMIC: BSV CLEAR",
+ init, motg->inputs);
+ motg->is_ext_chg_dcp = false;
+ if (!test_and_clear_bit(B_SESS_VLD, &motg->inputs) && init)
+ return;
+ }
+
+ /* do not queue state m/c work if id is grounded */
+ if (!test_bit(ID, &motg->inputs) &&
+ !motg->pdata->vbus_low_as_hostmode) {
+ /*
+ * state machine work waits for initial VBUS
+ * completion in UNDEFINED state. Process
+ * the initial VBUS event in ID_GND state.
+ */
+ if (init)
+ return;
+ }
+
+ if (!init) {
+ init = true;
+ if (pmic_vbus_init.done &&
+ test_bit(B_SESS_VLD, &motg->inputs)) {
+ pr_debug("PMIC: BSV came late\n");
+ msm_otg_dbg_log_event(&motg->phy, "PMIC: BSV CAME LATE",
+ init, motg->inputs);
+ goto out;
+ }
+
+ if (motg->pdata->vbus_low_as_hostmode &&
+ !test_bit(B_SESS_VLD, &motg->inputs)) {
+ motg->id_state = USB_ID_GROUND;
+ clear_bit(ID, &motg->inputs);
+ }
+ complete(&pmic_vbus_init);
+ pr_debug("PMIC: BSV init complete\n");
+ msm_otg_dbg_log_event(&motg->phy, "PMIC: BSV INIT COMPLETE",
+ init, motg->inputs);
+ return;
+ }
+
+out:
+ if (motg->is_ext_chg_dcp) {
+ if (test_bit(B_SESS_VLD, &motg->inputs)) {
+ msm_otg_notify_charger(motg, IDEV_CHG_MAX);
+ } else {
+ motg->is_ext_chg_dcp = false;
+ motg->chg_state = USB_CHG_STATE_UNDEFINED;
+ motg->chg_type = USB_INVALID_CHARGER;
+ msm_otg_notify_charger(motg, 0);
+ }
+ return;
+ }
+
+ msm_otg_dbg_log_event(&motg->phy, "CHECK VBUS EVENT DURING SUSPEND",
+ atomic_read(&motg->pm_suspended),
+ motg->sm_work_pending);
+
+ /* Move to host mode on vbus low if required */
+ if (motg->pdata->vbus_low_as_hostmode) {
+ if (!test_bit(B_SESS_VLD, &motg->inputs))
+ clear_bit(ID, &motg->inputs);
+ else
+ set_bit(ID, &motg->inputs);
+ }
+ msm_otg_kick_sm_work(motg);
+}
+
+static void msm_id_status_w(struct work_struct *w)
+{
+ struct msm_otg *motg = container_of(w, struct msm_otg,
+ id_status_work.work);
+ int work = 0;
+
+ dev_dbg(motg->phy.dev, "ID status_w\n");
+
+ if (motg->pdata->pmic_id_irq)
+ motg->id_state = msm_otg_read_pmic_id_state(motg);
+ else if (motg->ext_id_irq)
+ motg->id_state = gpio_get_value(motg->pdata->usb_id_gpio);
+ else if (motg->phy_irq)
+ motg->id_state = msm_otg_read_phy_id_state(motg);
+
+ if (motg->err_event_seen)
+ return;
+
+ if (motg->id_state) {
+ if (gpio_is_valid(motg->pdata->switch_sel_gpio))
+ gpio_direction_input(motg->pdata->switch_sel_gpio);
+ if (!test_and_set_bit(ID, &motg->inputs)) {
+ pr_debug("ID set\n");
+ msm_otg_dbg_log_event(&motg->phy, "ID SET",
+ motg->inputs, motg->phy.otg->state);
+ work = 1;
+ }
+ } else {
+ if (gpio_is_valid(motg->pdata->switch_sel_gpio))
+ gpio_direction_output(motg->pdata->switch_sel_gpio, 1);
+ if (test_and_clear_bit(ID, &motg->inputs)) {
+ pr_debug("ID clear\n");
+ msm_otg_dbg_log_event(&motg->phy, "ID CLEAR",
+ motg->inputs, motg->phy.otg->state);
+ work = 1;
+ }
+ }
+
+ if (work && (motg->phy.otg->state != OTG_STATE_UNDEFINED)) {
+ msm_otg_dbg_log_event(&motg->phy,
+ "CHECK ID EVENT DURING SUSPEND",
+ atomic_read(&motg->pm_suspended),
+ motg->sm_work_pending);
+ msm_otg_kick_sm_work(motg);
+ }
+}
+
+#define MSM_ID_STATUS_DELAY 5 /* 5msec */
+static irqreturn_t msm_id_irq(int irq, void *data)
+{
+ struct msm_otg *motg = data;
+
+ /*schedule delayed work for 5msec for ID line state to settle*/
+ queue_delayed_work(motg->otg_wq, &motg->id_status_work,
+ msecs_to_jiffies(MSM_ID_STATUS_DELAY));
+
+ return IRQ_HANDLED;
+}
+
+int msm_otg_pm_notify(struct notifier_block *notify_block,
+ unsigned long mode, void *unused)
+{
+ struct msm_otg *motg = container_of(
+ notify_block, struct msm_otg, pm_notify);
+
+ dev_dbg(motg->phy.dev, "OTG PM notify:%lx, sm_pending:%u\n", mode,
+ motg->sm_work_pending);
+ msm_otg_dbg_log_event(&motg->phy, "PM NOTIFY",
+ mode, motg->sm_work_pending);
+
+ switch (mode) {
+ case PM_POST_SUSPEND:
+ /* OTG sm_work can be armed now */
+ atomic_set(&motg->pm_suspended, 0);
+
+ /* Handle any deferred wakeup events from USB during suspend */
+ if (motg->sm_work_pending) {
+ motg->sm_work_pending = false;
+ queue_work(motg->otg_wq, &motg->sm_work);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static int msm_otg_mode_show(struct seq_file *s, void *unused)
+{
+ struct msm_otg *motg = s->private;
+ struct usb_otg *otg = motg->phy.otg;
+
+ switch (otg->state) {
+ case OTG_STATE_A_HOST:
+ seq_puts(s, "host\n");
+ break;
+ case OTG_STATE_B_IDLE:
+ case OTG_STATE_B_PERIPHERAL:
+ case OTG_STATE_B_SUSPEND:
+ seq_puts(s, "peripheral\n");
+ break;
+ default:
+ seq_puts(s, "none\n");
+ break;
+ }
+
+ return 0;
+}
+
+static int msm_otg_mode_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, msm_otg_mode_show, inode->i_private);
+}
+
+static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ struct msm_otg *motg = s->private;
+ char buf[16];
+ struct usb_phy *phy = &motg->phy;
+ int status = count;
+ enum usb_mode_type req_mode;
+
+ memset(buf, 0x00, sizeof(buf));
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) {
+ status = -EFAULT;
+ goto out;
+ }
+
+ if (!strncmp(buf, "host", 4)) {
+ req_mode = USB_HOST;
+ } else if (!strncmp(buf, "peripheral", 10)) {
+ req_mode = USB_PERIPHERAL;
+ } else if (!strncmp(buf, "none", 4)) {
+ req_mode = USB_NONE;
+ } else {
+ status = -EINVAL;
+ goto out;
+ }
+
+ switch (req_mode) {
+ case USB_NONE:
+ switch (phy->otg->state) {
+ case OTG_STATE_A_HOST:
+ case OTG_STATE_B_PERIPHERAL:
+ case OTG_STATE_B_SUSPEND:
+ set_bit(ID, &motg->inputs);
+ clear_bit(B_SESS_VLD, &motg->inputs);
+ break;
+ default:
+ goto out;
+ }
+ break;
+ case USB_PERIPHERAL:
+ switch (phy->otg->state) {
+ case OTG_STATE_B_IDLE:
+ case OTG_STATE_A_HOST:
+ set_bit(ID, &motg->inputs);
+ set_bit(B_SESS_VLD, &motg->inputs);
+ break;
+ default:
+ goto out;
+ }
+ break;
+ case USB_HOST:
+ switch (phy->otg->state) {
+ case OTG_STATE_B_IDLE:
+ case OTG_STATE_B_PERIPHERAL:
+ case OTG_STATE_B_SUSPEND:
+ clear_bit(ID, &motg->inputs);
+ break;
+ default:
+ goto out;
+ }
+ break;
+ default:
+ goto out;
+ }
+
+ motg->id_state = (test_bit(ID, &motg->inputs)) ? USB_ID_FLOAT :
+ USB_ID_GROUND;
+ queue_work(motg->otg_wq, &motg->sm_work);
+out:
+ return status;
+}
+
+const struct file_operations msm_otg_mode_fops = {
+ .open = msm_otg_mode_open,
+ .read = seq_read,
+ .write = msm_otg_mode_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int msm_otg_show_otg_state(struct seq_file *s, void *unused)
+{
+ struct msm_otg *motg = s->private;
+ struct usb_phy *phy = &motg->phy;
+
+ seq_printf(s, "%s\n", usb_otg_state_string(phy->otg->state));
+ return 0;
+}
+
+static int msm_otg_otg_state_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, msm_otg_show_otg_state, inode->i_private);
+}
+
+const struct file_operations msm_otg_state_fops = {
+ .open = msm_otg_otg_state_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int msm_otg_show_chg_type(struct seq_file *s, void *unused)
+{
+ struct msm_otg *motg = s->private;
+
+ seq_printf(s, "%s\n", chg_to_string(motg->chg_type));
+ return 0;
+}
+
+static int msm_otg_chg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, msm_otg_show_chg_type, inode->i_private);
+}
+
+const struct file_operations msm_otg_chg_fops = {
+ .open = msm_otg_chg_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int msm_otg_bus_show(struct seq_file *s, void *unused)
+{
+ if (debug_bus_voting_enabled)
+ seq_puts(s, "enabled\n");
+ else
+ seq_puts(s, "disabled\n");
+
+ return 0;
+}
+
+static int msm_otg_bus_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, msm_otg_bus_show, inode->i_private);
+}
+
+static ssize_t msm_otg_bus_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char buf[8];
+ struct seq_file *s = file->private_data;
+ struct msm_otg *motg = s->private;
+
+ memset(buf, 0x00, sizeof(buf));
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ if (!strncmp(buf, "enable", 6)) {
+ /* Do not vote here. Let OTG statemachine decide when to vote */
+ debug_bus_voting_enabled = true;
+ } else {
+ debug_bus_voting_enabled = false;
+ msm_otg_bus_vote(motg, USB_MIN_PERF_VOTE);
+ }
+
+ return count;
+}
+
+static int msm_otg_dbg_buff_show(struct seq_file *s, void *unused)
+{
+ struct msm_otg *motg = s->private;
+ unsigned long flags;
+ unsigned int i;
+
+ read_lock_irqsave(&motg->dbg_lock, flags);
+
+ i = motg->dbg_idx;
+ if (strnlen(motg->buf[i], DEBUG_MSG_LEN))
+ seq_printf(s, "%s\n", motg->buf[i]);
+ for (dbg_inc(&i); i != motg->dbg_idx; dbg_inc(&i)) {
+ if (!strnlen(motg->buf[i], DEBUG_MSG_LEN))
+ continue;
+ seq_printf(s, "%s\n", motg->buf[i]);
+ }
+ read_unlock_irqrestore(&motg->dbg_lock, flags);
+
+ return 0;
+}
+
+static int msm_otg_dbg_buff_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, msm_otg_dbg_buff_show, inode->i_private);
+}
+
+const struct file_operations msm_otg_dbg_buff_fops = {
+ .open = msm_otg_dbg_buff_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int msm_otg_dpdm_regulator_enable(struct regulator_dev *rdev)
+{
+ int ret = 0;
+ struct msm_otg *motg = rdev_get_drvdata(rdev);
+
+ if (!motg->rm_pulldown) {
+ ret = msm_hsusb_ldo_enable(motg, USB_PHY_REG_3P3_ON);
+ if (!ret) {
+ motg->rm_pulldown = true;
+ msm_otg_dbg_log_event(&motg->phy, "RM Pulldown",
+ motg->rm_pulldown, 0);
+ }
+ }
+
+ return ret;
+}
+
+static int msm_otg_dpdm_regulator_disable(struct regulator_dev *rdev)
+{
+ int ret = 0;
+ struct msm_otg *motg = rdev_get_drvdata(rdev);
+
+ if (motg->rm_pulldown) {
+ ret = msm_hsusb_ldo_enable(motg, USB_PHY_REG_3P3_OFF);
+ if (!ret) {
+ motg->rm_pulldown = false;
+ msm_otg_dbg_log_event(&motg->phy, "RM Pulldown",
+ motg->rm_pulldown, 0);
+ }
+ }
+
+ return ret;
+}
+
+static int msm_otg_dpdm_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct msm_otg *motg = rdev_get_drvdata(rdev);
+
+ return motg->rm_pulldown;
+}
+
+static struct regulator_ops msm_otg_dpdm_regulator_ops = {
+ .enable = msm_otg_dpdm_regulator_enable,
+ .disable = msm_otg_dpdm_regulator_disable,
+ .is_enabled = msm_otg_dpdm_regulator_is_enabled,
+};
+
+static int usb_phy_regulator_init(struct msm_otg *motg)
+{
+ struct device *dev = motg->phy.dev;
+ struct regulator_config cfg = {};
+ struct regulator_init_data *init_data;
+
+ init_data = devm_kzalloc(dev, sizeof(*init_data), GFP_KERNEL);
+ if (!init_data)
+ return -ENOMEM;
+
+ init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_STATUS;
+ motg->dpdm_rdesc.owner = THIS_MODULE;
+ motg->dpdm_rdesc.type = REGULATOR_VOLTAGE;
+ motg->dpdm_rdesc.ops = &msm_otg_dpdm_regulator_ops;
+ motg->dpdm_rdesc.name = kbasename(dev->of_node->full_name);
+
+ cfg.dev = dev;
+ cfg.init_data = init_data;
+ cfg.driver_data = motg;
+ cfg.of_node = dev->of_node;
+
+ motg->dpdm_rdev = devm_regulator_register(dev, &motg->dpdm_rdesc, &cfg);
+ if (IS_ERR(motg->dpdm_rdev))
+ return PTR_ERR(motg->dpdm_rdev);
+
+ return 0;
+}
+
+const struct file_operations msm_otg_bus_fops = {
+ .open = msm_otg_bus_open,
+ .read = seq_read,
+ .write = msm_otg_bus_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct dentry *msm_otg_dbg_root;
+
+static int msm_otg_debugfs_init(struct msm_otg *motg)
+{
+ struct dentry *msm_otg_dentry;
+ struct msm_otg_platform_data *pdata = motg->pdata;
+
+ msm_otg_dbg_root = debugfs_create_dir("msm_otg", NULL);
+
+ if (!msm_otg_dbg_root || IS_ERR(msm_otg_dbg_root))
+ return -ENODEV;
+
+ if ((pdata->mode == USB_OTG || pdata->mode == USB_PERIPHERAL) &&
+ pdata->otg_control == OTG_USER_CONTROL) {
+
+ msm_otg_dentry = debugfs_create_file("mode", 0644,
+ msm_otg_dbg_root, motg, &msm_otg_mode_fops);
+
+ if (!msm_otg_dentry) {
+ debugfs_remove(msm_otg_dbg_root);
+ msm_otg_dbg_root = NULL;
+ return -ENODEV;
+ }
+ }
+
+ msm_otg_dentry = debugfs_create_file("chg_type", 0444, msm_otg_dbg_root,
+ motg, &msm_otg_chg_fops);
+
+ if (!msm_otg_dentry) {
+ debugfs_remove_recursive(msm_otg_dbg_root);
+ return -ENODEV;
+ }
+
+ msm_otg_dentry = debugfs_create_file("bus_voting", 0644,
+ msm_otg_dbg_root, motg, &msm_otg_bus_fops);
+
+ if (!msm_otg_dentry) {
+ debugfs_remove_recursive(msm_otg_dbg_root);
+ return -ENODEV;
+ }
+
+ msm_otg_dentry = debugfs_create_file("otg_state", 0444,
+ msm_otg_dbg_root, motg, &msm_otg_state_fops);
+
+ if (!msm_otg_dentry) {
+ debugfs_remove_recursive(msm_otg_dbg_root);
+ return -ENODEV;
+ }
+
+ msm_otg_dentry = debugfs_create_file("dbg_buff", 0444,
+ msm_otg_dbg_root, motg, &msm_otg_dbg_buff_fops);
+
+ if (!msm_otg_dentry) {
+ debugfs_remove_recursive(msm_otg_dbg_root);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void msm_otg_debugfs_cleanup(void)
+{
+ debugfs_remove_recursive(msm_otg_dbg_root);
+}
+
+static ssize_t
+set_msm_otg_perf_mode(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct msm_otg *motg = the_msm_otg;
+ int ret;
+ long clk_rate;
+
+ pr_debug("%s: enable:%d\n", __func__, !strncasecmp(buf, "enable", 6));
+
+ if (!strncasecmp(buf, "enable", 6)) {
+ clk_rate = motg->core_clk_nominal_rate;
+ msm_otg_bus_freq_set(motg, USB_NOC_NOM_VOTE);
+ } else {
+ clk_rate = motg->core_clk_svs_rate;
+ msm_otg_bus_freq_set(motg, USB_NOC_SVS_VOTE);
+ }
+
+ if (clk_rate) {
+ pr_debug("Set usb sys_clk rate:%ld\n", clk_rate);
+ ret = clk_set_rate(motg->core_clk, clk_rate);
+ if (ret)
+ pr_err("sys_clk set_rate fail:%d %ld\n", ret, clk_rate);
+ msm_otg_dbg_log_event(&motg->phy, "OTG PERF SET",
+ clk_rate, ret);
+ } else {
+ pr_err("usb sys_clk rate is undefined\n");
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(perf_mode, 0200, NULL, set_msm_otg_perf_mode);
+
+#define MSM_OTG_CMD_ID 0x09
+#define MSM_OTG_DEVICE_ID 0x04
+#define MSM_OTG_VMID_IDX 0xFF
+#define MSM_OTG_MEM_TYPE 0x02
+struct msm_otg_scm_cmd_buf {
+ unsigned int device_id;
+ unsigned int vmid_idx;
+ unsigned int mem_type;
+} __attribute__ ((__packed__));
+
+static void msm_otg_pnoc_errata_fix(struct msm_otg *motg)
+{
+ int ret;
+ struct msm_otg_platform_data *pdata = motg->pdata;
+ struct msm_otg_scm_cmd_buf cmd_buf;
+
+ if (!pdata->pnoc_errata_fix)
+ return;
+
+ dev_dbg(motg->phy.dev, "applying fix for pnoc h/w issue\n");
+
+ cmd_buf.device_id = MSM_OTG_DEVICE_ID;
+ cmd_buf.vmid_idx = MSM_OTG_VMID_IDX;
+ cmd_buf.mem_type = MSM_OTG_MEM_TYPE;
+
+ ret = scm_call(SCM_SVC_MP, MSM_OTG_CMD_ID, &cmd_buf,
+ sizeof(cmd_buf), NULL, 0);
+
+ if (ret)
+ dev_err(motg->phy.dev, "scm command failed to update VMIDMT\n");
+}
+
+static u64 msm_otg_dma_mask = DMA_BIT_MASK(32);
+static struct platform_device *msm_otg_add_pdev(
+ struct platform_device *ofdev, const char *name)
+{
+ struct platform_device *pdev;
+ const struct resource *res = ofdev->resource;
+ unsigned int num = ofdev->num_resources;
+ int retval;
+ struct ci13xxx_platform_data ci_pdata;
+ struct msm_otg_platform_data *otg_pdata;
+ struct msm_otg *motg;
+
+ pdev = platform_device_alloc(name, -1);
+ if (!pdev) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+ pdev->dev.dma_mask = &msm_otg_dma_mask;
+ pdev->dev.parent = &ofdev->dev;
+
+ if (num) {
+ retval = platform_device_add_resources(pdev, res, num);
+ if (retval)
+ goto error;
+ }
+
+ if (!strcmp(name, "msm_hsusb")) {
+ otg_pdata =
+ (struct msm_otg_platform_data *)
+ ofdev->dev.platform_data;
+ motg = platform_get_drvdata(ofdev);
+ ci_pdata.log2_itc = otg_pdata->log2_itc;
+ ci_pdata.usb_core_id = 0;
+ ci_pdata.l1_supported = otg_pdata->l1_supported;
+ ci_pdata.enable_ahb2ahb_bypass =
+ otg_pdata->enable_ahb2ahb_bypass;
+ ci_pdata.enable_streaming = otg_pdata->enable_streaming;
+ ci_pdata.enable_axi_prefetch = otg_pdata->enable_axi_prefetch;
+ retval = platform_device_add_data(pdev, &ci_pdata,
+ sizeof(ci_pdata));
+ if (retval)
+ goto error;
+ }
+
+ retval = platform_device_add(pdev);
+ if (retval)
+ goto error;
+
+ return pdev;
+
+error:
+ platform_device_put(pdev);
+ return ERR_PTR(retval);
+}
+
+static int msm_otg_setup_devices(struct platform_device *ofdev,
+ enum usb_mode_type mode, bool init)
+{
+ const char *gadget_name = "msm_hsusb";
+ const char *host_name = "msm_hsusb_host";
+ static struct platform_device *gadget_pdev;
+ static struct platform_device *host_pdev;
+ int retval = 0;
+
+ if (!init) {
+ if (gadget_pdev) {
+ platform_device_unregister(gadget_pdev);
+ device_remove_file(&gadget_pdev->dev,
+ &dev_attr_perf_mode);
+ }
+ if (host_pdev)
+ platform_device_unregister(host_pdev);
+ return 0;
+ }
+
+ switch (mode) {
+ case USB_OTG:
+ /* fall through */
+ case USB_PERIPHERAL:
+ gadget_pdev = msm_otg_add_pdev(ofdev, gadget_name);
+ if (IS_ERR(gadget_pdev)) {
+ retval = PTR_ERR(gadget_pdev);
+ break;
+ }
+ if (device_create_file(&gadget_pdev->dev, &dev_attr_perf_mode))
+ dev_err(&gadget_pdev->dev, "perf_mode file failed\n");
+ if (mode == USB_PERIPHERAL)
+ break;
+ /* fall through */
+ case USB_HOST:
+ host_pdev = msm_otg_add_pdev(ofdev, host_name);
+ if (IS_ERR(host_pdev)) {
+ retval = PTR_ERR(host_pdev);
+ if (mode == USB_OTG) {
+ platform_device_unregister(gadget_pdev);
+ device_remove_file(&gadget_pdev->dev,
+ &dev_attr_perf_mode);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return retval;
+}
+
+static int msm_otg_ext_chg_open(struct inode *inode, struct file *file)
+{
+ struct msm_otg *motg = the_msm_otg;
+
+ pr_debug("msm_otg ext chg open\n");
+ msm_otg_dbg_log_event(&motg->phy, "EXT CHG: OPEN",
+ motg->inputs, motg->phy.otg->state);
+
+ motg->ext_chg_opened = true;
+ file->private_data = (void *)motg;
+ return 0;
+}
+
+static long
+msm_otg_ext_chg_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct msm_otg *motg = file->private_data;
+ struct msm_usb_chg_info info = {0};
+ int ret = 0, val;
+
+ msm_otg_dbg_log_event(&motg->phy, "EXT CHG: IOCTL", cmd, 0);
+ switch (cmd) {
+ case MSM_USB_EXT_CHG_INFO:
+ info.chg_block_type = USB_CHG_BLOCK_ULPI;
+ info.page_offset = motg->io_res->start & ~PAGE_MASK;
+ /* mmap() works on PAGE granularity */
+ info.length = PAGE_SIZE;
+
+ if (copy_to_user((void __user *)arg, &info, sizeof(info))) {
+ pr_err("%s: copy to user failed\n\n", __func__);
+ ret = -EFAULT;
+ }
+ break;
+ case MSM_USB_EXT_CHG_BLOCK_LPM:
+ if (get_user(val, (int __user *)arg)) {
+ pr_err("%s: get_user failed\n\n", __func__);
+ ret = -EFAULT;
+ break;
+ }
+ pr_debug("%s: LPM block request %d\n", __func__, val);
+ msm_otg_dbg_log_event(&motg->phy, "LPM BLOCK REQ", val, 0);
+ if (val) { /* block LPM */
+ if (motg->chg_type == USB_DCP_CHARGER) {
+ motg->ext_chg_active = ACTIVE;
+ msm_otg_dbg_log_event(&motg->phy,
+ "PM RUNTIME: EXT_CHG GET",
+ get_pm_runtime_counter(motg->phy.dev), 0);
+ pm_runtime_get_sync(motg->phy.dev);
+ } else {
+ motg->ext_chg_active = INACTIVE;
+ complete(&motg->ext_chg_wait);
+ ret = -ENODEV;
+ }
+ } else {
+ motg->ext_chg_active = INACTIVE;
+ complete(&motg->ext_chg_wait);
+ /*
+ * If usb cable is disconnected and then userspace
+ * calls ioctl to unblock low power mode, make sure
+ * otg_sm work for usb disconnect is processed first
+ * followed by decrementing the PM usage counters.
+ */
+ flush_work(&motg->sm_work);
+ msm_otg_dbg_log_event(&motg->phy,
+ "PM RUNTIME: EXT_CHG PUT",
+ get_pm_runtime_counter(motg->phy.dev), 0);
+ pm_runtime_put_sync(motg->phy.dev);
+ }
+ break;
+ case MSM_USB_EXT_CHG_VOLTAGE_INFO:
+ if (get_user(val, (int __user *)arg)) {
+ pr_err("%s: get_user failed\n\n", __func__);
+ ret = -EFAULT;
+ break;
+ }
+ msm_otg_dbg_log_event(&motg->phy, "EXT CHG: VOL REQ", cmd, val);
+
+ if (val == USB_REQUEST_5V)
+ pr_debug("%s:voting 5V voltage request\n", __func__);
+ else if (val == USB_REQUEST_9V)
+ pr_debug("%s:voting 9V voltage request\n", __func__);
+ break;
+ case MSM_USB_EXT_CHG_RESULT:
+ if (get_user(val, (int __user *)arg)) {
+ pr_err("%s: get_user failed\n\n", __func__);
+ ret = -EFAULT;
+ break;
+ }
+ msm_otg_dbg_log_event(&motg->phy, "EXT CHG: VOL REQ", cmd, val);
+
+ if (!val)
+ pr_debug("%s:voltage request successful\n", __func__);
+ else
+ pr_debug("%s:voltage request failed\n", __func__);
+ break;
+ case MSM_USB_EXT_CHG_TYPE:
+ if (get_user(val, (int __user *)arg)) {
+ pr_err("%s: get_user failed\n\n", __func__);
+ ret = -EFAULT;
+ break;
+ }
+ msm_otg_dbg_log_event(&motg->phy, "EXT CHG: VOL REQ", cmd, val);
+
+ if (val)
+ pr_debug("%s:charger is external charger\n", __func__);
+ else
+ pr_debug("%s:charger is not ext charger\n", __func__);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int msm_otg_ext_chg_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct msm_otg *motg = file->private_data;
+ unsigned long vsize = vma->vm_end - vma->vm_start;
+ int ret;
+
+ if (vma->vm_pgoff || vsize > PAGE_SIZE)
+ return -EINVAL;
+
+ vma->vm_pgoff = __phys_to_pfn(motg->io_res->start);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ ret = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vsize, vma->vm_page_prot);
+ if (ret < 0) {
+ pr_err("%s: failed with return val %d\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int msm_otg_ext_chg_release(struct inode *inode, struct file *file)
+{
+ struct msm_otg *motg = file->private_data;
+
+ pr_debug("msm_otg ext chg release\n");
+ msm_otg_dbg_log_event(&motg->phy, "EXT CHG: RELEASE",
+ motg->inputs, motg->phy.otg->state);
+
+ motg->ext_chg_opened = false;
+
+ return 0;
+}
+
+static const struct file_operations msm_otg_ext_chg_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_otg_ext_chg_open,
+ .unlocked_ioctl = msm_otg_ext_chg_ioctl,
+ .mmap = msm_otg_ext_chg_mmap,
+ .release = msm_otg_ext_chg_release,
+};
+
+static int msm_otg_setup_ext_chg_cdev(struct msm_otg *motg)
+{
+ int ret;
+
+ if (motg->pdata->enable_sec_phy || motg->pdata->mode == USB_HOST ||
+ motg->pdata->otg_control != OTG_PMIC_CONTROL) {
+ pr_debug("usb ext chg is not supported by msm otg\n");
+ return -ENODEV;
+ }
+
+ ret = alloc_chrdev_region(&motg->ext_chg_dev, 0, 1, "usb_ext_chg");
+ if (ret < 0) {
+ pr_err("Fail to allocate usb ext char dev region\n");
+ return ret;
+ }
+ motg->ext_chg_class = class_create(THIS_MODULE, "msm_ext_chg");
+ if (ret < 0) {
+ pr_err("Fail to create usb ext chg class\n");
+ goto unreg_chrdev;
+ }
+ cdev_init(&motg->ext_chg_cdev, &msm_otg_ext_chg_fops);
+ motg->ext_chg_cdev.owner = THIS_MODULE;
+
+ ret = cdev_add(&motg->ext_chg_cdev, motg->ext_chg_dev, 1);
+ if (ret < 0) {
+ pr_err("Fail to add usb ext chg cdev\n");
+ goto destroy_class;
+ }
+ motg->ext_chg_device = device_create(motg->ext_chg_class,
+ NULL, motg->ext_chg_dev, NULL,
+ "usb_ext_chg");
+ if (IS_ERR(motg->ext_chg_device)) {
+ pr_err("Fail to create usb ext chg device\n");
+ ret = PTR_ERR(motg->ext_chg_device);
+ motg->ext_chg_device = NULL;
+ goto del_cdev;
+ }
+
+ init_completion(&motg->ext_chg_wait);
+ pr_debug("msm otg ext chg cdev setup success\n");
+ return 0;
+
+del_cdev:
+ cdev_del(&motg->ext_chg_cdev);
+destroy_class:
+ class_destroy(motg->ext_chg_class);
+unreg_chrdev:
+ unregister_chrdev_region(motg->ext_chg_dev, 1);
+
+ return ret;
+}
+
+static ssize_t dpdm_pulldown_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct msm_otg *motg = the_msm_otg;
+ struct msm_otg_platform_data *pdata = motg->pdata;
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", pdata->dpdm_pulldown_added ?
+ "enabled" : "disabled");
+}
+
+static ssize_t dpdm_pulldown_enable_store(struct device *dev,
+ struct device_attribute *attr, const char
+ *buf, size_t size)
+{
+ struct msm_otg *motg = the_msm_otg;
+ struct msm_otg_platform_data *pdata = motg->pdata;
+
+ if (!strncasecmp(buf, "enable", 6)) {
+ pdata->dpdm_pulldown_added = true;
+ return size;
+ } else if (!strncasecmp(buf, "disable", 7)) {
+ pdata->dpdm_pulldown_added = false;
+ return size;
+ }
+
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(dpdm_pulldown_enable, 0644,
+ dpdm_pulldown_enable_show, dpdm_pulldown_enable_store);
+
+static int msm_otg_vbus_notifier(struct notifier_block *nb, unsigned long event,
+ void *ptr)
+{
+ struct msm_otg *motg = container_of(nb, struct msm_otg, vbus_nb);
+
+ if (event)
+ set_bit(B_SESS_VLD, &motg->inputs);
+ else
+ clear_bit(B_SESS_VLD, &motg->inputs);
+
+ queue_work(motg->otg_wq, &motg->sm_work);
+
+ return NOTIFY_DONE;
+}
+
+static int msm_otg_id_notifier(struct notifier_block *nb, unsigned long event,
+ void *ptr)
+{
+ struct msm_otg *motg = container_of(nb, struct msm_otg, id_nb);
+
+ if (event)
+ clear_bit(ID, &motg->inputs);
+ else
+ set_bit(ID, &motg->inputs);
+
+ queue_work(motg->otg_wq, &motg->sm_work);
+
+ return NOTIFY_DONE;
+}
+
+static int msm_otg_extcon_register(struct msm_otg *motg)
+{
+ struct device_node *node = motg->pdev->dev.of_node;
+ struct extcon_dev *edev;
+ int ret = 0;
+
+ if (!of_property_read_bool(node, "extcon"))
+ return 0;
+
+ edev = extcon_get_edev_by_phandle(&motg->pdev->dev, 0);
+ if (IS_ERR(edev) && PTR_ERR(edev) != -ENODEV)
+ return PTR_ERR(edev);
+
+ if (!IS_ERR(edev)) {
+ motg->extcon_vbus = edev;
+ motg->vbus_nb.notifier_call = msm_otg_vbus_notifier;
+ ret = extcon_register_notifier(edev, EXTCON_USB,
+ &motg->vbus_nb);
+ if (ret < 0) {
+ dev_err(&motg->pdev->dev, "failed to register notifier for USB\n");
+ return ret;
+ }
+ }
+
+ if (of_count_phandle_with_args(node, "extcon", NULL) > 1) {
+ edev = extcon_get_edev_by_phandle(&motg->pdev->dev, 1);
+ if (IS_ERR(edev) && PTR_ERR(edev) != -ENODEV) {
+ ret = PTR_ERR(edev);
+ goto err;
+ }
+ }
+
+ if (!IS_ERR(edev)) {
+ motg->extcon_id = edev;
+ motg->id_nb.notifier_call = msm_otg_id_notifier;
+ ret = extcon_register_notifier(edev, EXTCON_USB_HOST,
+ &motg->id_nb);
+ if (ret < 0) {
+ dev_err(&motg->pdev->dev, "failed to register notifier for USB-HOST\n");
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ if (motg->extcon_vbus)
+ extcon_unregister_notifier(motg->extcon_vbus, EXTCON_USB,
+ &motg->vbus_nb);
+
+ return ret;
+}
+
+struct msm_otg_platform_data *msm_otg_dt_to_pdata(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct msm_otg_platform_data *pdata;
+ int len = 0;
+ int res_gpio;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ len = of_property_count_elems_of_size(node,
+ "qcom,hsusb-otg-phy-init-seq", sizeof(len));
+ if (len > 0) {
+ pdata->phy_init_seq = devm_kzalloc(&pdev->dev,
+ len * sizeof(len), GFP_KERNEL);
+ if (!pdata->phy_init_seq)
+ return NULL;
+ of_property_read_u32_array(node, "qcom,hsusb-otg-phy-init-seq",
+ pdata->phy_init_seq, len);
+ }
+ of_property_read_u32(node, "qcom,hsusb-otg-power-budget",
+ &pdata->power_budget);
+ of_property_read_u32(node, "qcom,hsusb-otg-mode",
+ &pdata->mode);
+ of_property_read_u32(node, "qcom,hsusb-otg-otg-control",
+ &pdata->otg_control);
+ of_property_read_u32(node, "qcom,hsusb-otg-default-mode",
+ &pdata->default_mode);
+ of_property_read_u32(node, "qcom,hsusb-otg-phy-type",
+ &pdata->phy_type);
+ pdata->disable_reset_on_disconnect = of_property_read_bool(node,
+ "qcom,hsusb-otg-disable-reset");
+ pdata->pnoc_errata_fix = of_property_read_bool(node,
+ "qcom,hsusb-otg-pnoc-errata-fix");
+ pdata->enable_lpm_on_dev_suspend = of_property_read_bool(node,
+ "qcom,hsusb-otg-lpm-on-dev-suspend");
+ pdata->core_clk_always_on_workaround = of_property_read_bool(node,
+ "qcom,hsusb-otg-clk-always-on-workaround");
+ pdata->delay_lpm_on_disconnect = of_property_read_bool(node,
+ "qcom,hsusb-otg-delay-lpm");
+ pdata->dp_manual_pullup = of_property_read_bool(node,
+ "qcom,dp-manual-pullup");
+ pdata->enable_sec_phy = of_property_read_bool(node,
+ "qcom,usb2-enable-hsphy2");
+ of_property_read_u32(node, "qcom,hsusb-log2-itc",
+ &pdata->log2_itc);
+
+ of_property_read_u32(node, "qcom,hsusb-otg-mpm-dpsehv-int",
+ &pdata->mpm_dpshv_int);
+ of_property_read_u32(node, "qcom,hsusb-otg-mpm-dmsehv-int",
+ &pdata->mpm_dmshv_int);
+ pdata->pmic_id_irq = platform_get_irq_byname(pdev, "pmic_id_irq");
+ if (pdata->pmic_id_irq < 0)
+ pdata->pmic_id_irq = 0;
+
+ pdata->hub_reset_gpio = of_get_named_gpio(
+ node, "qcom,hub-reset-gpio", 0);
+ if (!gpio_is_valid(pdata->hub_reset_gpio))
+ pr_debug("hub_reset_gpio is not available\n");
+
+ pdata->usbeth_reset_gpio = of_get_named_gpio(
+ node, "qcom,usbeth-reset-gpio", 0);
+ if (!gpio_is_valid(pdata->usbeth_reset_gpio))
+ pr_debug("usbeth_reset_gpio is not available\n");
+
+ pdata->switch_sel_gpio =
+ of_get_named_gpio(node, "qcom,sw-sel-gpio", 0);
+ if (!gpio_is_valid(pdata->switch_sel_gpio))
+ pr_debug("switch_sel_gpio is not available\n");
+
+ pdata->usb_id_gpio =
+ of_get_named_gpio(node, "qcom,usbid-gpio", 0);
+ if (!gpio_is_valid(pdata->usb_id_gpio))
+ pr_debug("usb_id_gpio is not available\n");
+
+ pdata->l1_supported = of_property_read_bool(node,
+ "qcom,hsusb-l1-supported");
+ pdata->enable_ahb2ahb_bypass = of_property_read_bool(node,
+ "qcom,ahb-async-bridge-bypass");
+ pdata->disable_retention_with_vdd_min = of_property_read_bool(node,
+ "qcom,disable-retention-with-vdd-min");
+ pdata->enable_phy_id_pullup = of_property_read_bool(node,
+ "qcom,enable-phy-id-pullup");
+ pdata->phy_dvdd_always_on = of_property_read_bool(node,
+ "qcom,phy-dvdd-always-on");
+
+ res_gpio = of_get_named_gpio(node, "qcom,hsusb-otg-vddmin-gpio", 0);
+ if (!gpio_is_valid(res_gpio))
+ res_gpio = 0;
+ pdata->vddmin_gpio = res_gpio;
+
+ pdata->emulation = of_property_read_bool(node,
+ "qcom,emulation");
+
+ pdata->enable_streaming = of_property_read_bool(node,
+ "qcom,boost-sysclk-with-streaming");
+
+ pdata->enable_axi_prefetch = of_property_read_bool(node,
+ "qcom,axi-prefetch-enable");
+
+ pdata->enable_sdp_typec_current_limit = of_property_read_bool(node,
+ "qcom,enable-sdp-typec-current-limit");
+ pdata->vbus_low_as_hostmode = of_property_read_bool(node,
+ "qcom,vbus-low-as-hostmode");
+ return pdata;
+}
+
+static int msm_otg_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ int len = 0;
+ u32 tmp[3];
+ struct resource *res;
+ struct msm_otg *motg;
+ struct usb_phy *phy;
+ struct msm_otg_platform_data *pdata;
+ void __iomem *tcsr;
+ int id_irq = 0;
+
+ dev_info(&pdev->dev, "msm_otg probe\n");
+
+ motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL);
+ if (!motg) {
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ /*
+ * USB Core is running its protocol engine based on CORE CLK,
+ * CORE CLK must be running at >55Mhz for correct HSUSB
+ * operation and USB core cannot tolerate frequency changes on
+ * CORE CLK. For such USB cores, vote for maximum clk frequency
+ * on pclk source
+ */
+ motg->core_clk = clk_get(&pdev->dev, "core_clk");
+ if (IS_ERR(motg->core_clk)) {
+ ret = PTR_ERR(motg->core_clk);
+ motg->core_clk = NULL;
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "failed to get core_clk\n");
+ goto free_motg;
+ }
+
+ motg->core_reset = devm_reset_control_get(&pdev->dev, "core_reset");
+ if (IS_ERR(motg->core_reset)) {
+ dev_err(&pdev->dev, "failed to get core_reset\n");
+ ret = PTR_ERR(motg->core_reset);
+ goto put_core_clk;
+ }
+
+ /*
+ * USB Core CLK can run at max freq if streaming is enabled. Hence,
+ * get Max supported clk frequency for USB Core CLK and request to set
+ * the same. Otherwise set USB Core CLK to defined default value.
+ */
+ if (of_property_read_u32(pdev->dev.of_node,
+ "qcom,max-nominal-sysclk-rate", &ret)) {
+ ret = -EINVAL;
+ goto put_core_clk;
+ } else {
+ motg->core_clk_nominal_rate = clk_round_rate(motg->core_clk,
+ ret);
+ }
+
+ if (of_property_read_u32(pdev->dev.of_node,
+ "qcom,max-svs-sysclk-rate", &ret)) {
+ dev_dbg(&pdev->dev, "core_clk svs freq not specified\n");
+ } else {
+ motg->core_clk_svs_rate = clk_round_rate(motg->core_clk, ret);
+ }
+
+ motg->default_noc_mode = USB_NOC_NOM_VOTE;
+ if (of_property_read_bool(pdev->dev.of_node, "qcom,default-mode-svs")) {
+ motg->core_clk_rate = motg->core_clk_svs_rate;
+ motg->default_noc_mode = USB_NOC_SVS_VOTE;
+ } else if (of_property_read_bool(pdev->dev.of_node,
+ "qcom,boost-sysclk-with-streaming")) {
+ motg->core_clk_rate = motg->core_clk_nominal_rate;
+ } else {
+ motg->core_clk_rate = clk_round_rate(motg->core_clk,
+ USB_DEFAULT_SYSTEM_CLOCK);
+ }
+
+ if (IS_ERR_VALUE(motg->core_clk_rate)) {
+ dev_err(&pdev->dev, "fail to get core clk max freq.\n");
+ } else {
+ ret = clk_set_rate(motg->core_clk, motg->core_clk_rate);
+ if (ret)
+ dev_err(&pdev->dev, "fail to set core_clk freq:%d\n",
+ ret);
+ }
+
+ motg->pclk = clk_get(&pdev->dev, "iface_clk");
+ if (IS_ERR(motg->pclk)) {
+ ret = PTR_ERR(motg->pclk);
+ motg->pclk = NULL;
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "failed to get iface_clk\n");
+ goto put_core_clk;
+ }
+
+ motg->xo_clk = clk_get(&pdev->dev, "xo");
+ if (IS_ERR(motg->xo_clk)) {
+ ret = PTR_ERR(motg->xo_clk);
+ motg->xo_clk = NULL;
+ if (ret == -EPROBE_DEFER)
+ goto put_pclk;
+ }
+
+ /*
+ * On few platforms USB PHY is fed with sleep clk.
+ * Hence don't fail probe.
+ */
+ motg->sleep_clk = devm_clk_get(&pdev->dev, "sleep_clk");
+ if (IS_ERR(motg->sleep_clk)) {
+ ret = PTR_ERR(motg->sleep_clk);
+ motg->sleep_clk = NULL;
+ if (ret == -EPROBE_DEFER)
+ goto put_xo_clk;
+ else
+ dev_dbg(&pdev->dev, "failed to get sleep_clk\n");
+ } else {
+ ret = clk_prepare_enable(motg->sleep_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "%s failed to vote sleep_clk%d\n",
+ __func__, ret);
+ goto put_xo_clk;
+ }
+ }
+
+ /*
+ * If present, phy_reset_clk is used to reset the PHY, ULPI bridge
+ * and CSR Wrapper. This is a reset only clock.
+ */
+
+ if (of_property_match_string(pdev->dev.of_node,
+ "clock-names", "phy_reset_clk") >= 0) {
+ motg->phy_reset_clk = devm_clk_get(&pdev->dev, "phy_reset_clk");
+ if (IS_ERR(motg->phy_reset_clk)) {
+ ret = PTR_ERR(motg->phy_reset_clk);
+ goto disable_sleep_clk;
+ }
+
+ motg->phy_reset = devm_reset_control_get(&pdev->dev,
+ "phy_reset");
+ if (IS_ERR(motg->phy_reset)) {
+ dev_err(&pdev->dev, "failed to get phy_reset\n");
+ ret = PTR_ERR(motg->phy_reset);
+ goto disable_sleep_clk;
+ }
+ }
+
+ /*
+ * If present, phy_por_clk is used to assert/de-assert phy POR
+ * input. This is a reset only clock. phy POR must be asserted
+ * after overriding the parameter registers via CSR wrapper or
+ * ULPI bridge.
+ */
+ if (of_property_match_string(pdev->dev.of_node,
+ "clock-names", "phy_por_clk") >= 0) {
+ motg->phy_por_clk = devm_clk_get(&pdev->dev, "phy_por_clk");
+ if (IS_ERR(motg->phy_por_clk)) {
+ ret = PTR_ERR(motg->phy_por_clk);
+ goto disable_sleep_clk;
+ }
+
+ motg->phy_por_reset = devm_reset_control_get(&pdev->dev,
+ "phy_por_reset");
+ if (IS_ERR(motg->phy_por_reset)) {
+ dev_err(&pdev->dev, "failed to get phy_por_reset\n");
+ ret = PTR_ERR(motg->phy_por_reset);
+ goto disable_sleep_clk;
+ }
+ }
+
+ /*
+ * If present, phy_csr_clk is required for accessing PHY
+ * CSR registers via AHB2PHY interface.
+ */
+ if (of_property_match_string(pdev->dev.of_node,
+ "clock-names", "phy_csr_clk") >= 0) {
+ motg->phy_csr_clk = devm_clk_get(&pdev->dev, "phy_csr_clk");
+ if (IS_ERR(motg->phy_csr_clk)) {
+ ret = PTR_ERR(motg->phy_csr_clk);
+ goto disable_sleep_clk;
+ } else {
+ ret = clk_prepare_enable(motg->phy_csr_clk);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "fail to enable phy csr clk %d\n", ret);
+ goto disable_sleep_clk;
+ }
+ }
+ }
+
+ of_property_read_u32(pdev->dev.of_node, "qcom,pm-qos-latency",
+ &motg->pm_qos_latency);
+
+ pdata = msm_otg_dt_to_pdata(pdev);
+ if (!pdata) {
+ ret = -ENOMEM;
+ goto disable_phy_csr_clk;
+ }
+ pdev->dev.platform_data = pdata;
+
+ pdata->bus_scale_table = msm_bus_cl_get_pdata(pdev);
+ if (!pdata->bus_scale_table)
+ dev_dbg(&pdev->dev, "bus scaling is disabled\n");
+
+ if (pdata->phy_type == QUSB_ULPI_PHY) {
+ if (of_property_match_string(pdev->dev.of_node,
+ "clock-names", "phy_ref_clk") >= 0) {
+ motg->phy_ref_clk = devm_clk_get(&pdev->dev,
+ "phy_ref_clk");
+ if (IS_ERR(motg->phy_ref_clk)) {
+ ret = PTR_ERR(motg->phy_ref_clk);
+ goto disable_phy_csr_clk;
+ } else {
+ ret = clk_prepare_enable(motg->phy_ref_clk);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "fail to enable phy ref clk %d\n",
+ ret);
+ goto disable_phy_csr_clk;
+ }
+ }
+ }
+ }
+
+ motg->phy.otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg),
+ GFP_KERNEL);
+ if (!motg->phy.otg) {
+ ret = -ENOMEM;
+ goto disable_phy_csr_clk;
+ }
+
+ the_msm_otg = motg;
+ motg->pdata = pdata;
+ phy = &motg->phy;
+ phy->dev = &pdev->dev;
+ motg->pdev = pdev;
+ motg->dbg_idx = 0;
+ motg->dbg_lock = __RW_LOCK_UNLOCKED(lck);
+
+ if (motg->pdata->bus_scale_table) {
+ motg->bus_perf_client =
+ msm_bus_scale_register_client(motg->pdata->bus_scale_table);
+ if (!motg->bus_perf_client) {
+ dev_err(motg->phy.dev, "%s: Failed to register BUS\n"
+ "scaling client!!\n", __func__);
+ } else {
+ debug_bus_voting_enabled = true;
+ /* Some platforms require BUS vote to control clocks */
+ msm_otg_bus_vote(motg, USB_MIN_PERF_VOTE);
+ }
+ }
+
+ ret = msm_otg_bus_freq_get(motg);
+ if (ret) {
+ pr_err("failed to get noc clocks: %d\n", ret);
+ } else {
+ ret = msm_otg_bus_freq_set(motg, motg->default_noc_mode);
+ if (ret)
+ pr_err("failed to vote explicit noc rates: %d\n", ret);
+ }
+
+ /* initialize reset counter */
+ motg->reset_counter = 0;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get core iomem resource\n");
+ ret = -ENODEV;
+ goto devote_bus_bw;
+ }
+
+ motg->io_res = res;
+ motg->regs = ioremap(res->start, resource_size(res));
+ if (!motg->regs) {
+ dev_err(&pdev->dev, "core iomem ioremap failed\n");
+ ret = -ENOMEM;
+ goto devote_bus_bw;
+ }
+ dev_info(&pdev->dev, "OTG regs = %pK\n", motg->regs);
+
+ if (pdata->enable_sec_phy) {
+ res = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "tcsr");
+ if (!res) {
+ dev_dbg(&pdev->dev, "missing TCSR memory resource\n");
+ } else {
+ tcsr = devm_ioremap_nocache(&pdev->dev, res->start,
+ resource_size(res));
+ if (!tcsr) {
+ dev_dbg(&pdev->dev, "tcsr ioremap failed\n");
+ } else {
+ /* Enable USB2 on secondary HSPHY. */
+ writel_relaxed(0x1, tcsr);
+ /*
+ * Ensure that TCSR write is completed before
+ * USB registers initialization.
+ */
+ mb();
+ }
+ }
+ }
+
+ if (pdata->enable_sec_phy)
+ motg->usb_phy_ctrl_reg = USB_PHY_CTRL2;
+ else
+ motg->usb_phy_ctrl_reg = USB_PHY_CTRL;
+
+ /*
+ * The USB PHY wrapper provides a register interface
+ * through AHB2PHY for performing PHY related operations
+ * like retention, HV interrupts and overriding parameter
+ * registers etc. The registers start at 4 byte boundary
+ * but only the first byte is valid and remaining are not
+ * used. Relaxed versions of readl/writel should be used.
+ *
+ * The link does not have any PHY specific registers.
+ * Hence set motg->usb_phy_ctrl_reg to.
+ */
+ if (motg->pdata->phy_type == SNPS_FEMTO_PHY ||
+ pdata->phy_type == QUSB_ULPI_PHY) {
+ res = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "phy_csr");
+ if (!res) {
+ dev_err(&pdev->dev, "PHY CSR IOMEM missing!\n");
+ ret = -ENODEV;
+ goto free_regs;
+ }
+ motg->phy_csr_regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(motg->phy_csr_regs)) {
+ ret = PTR_ERR(motg->phy_csr_regs);
+ dev_err(&pdev->dev, "PHY CSR ioremap failed!\n");
+ goto free_regs;
+ }
+ motg->usb_phy_ctrl_reg = 0;
+ }
+
+ motg->irq = platform_get_irq(pdev, 0);
+ if (!motg->irq) {
+ dev_err(&pdev->dev, "platform_get_irq failed\n");
+ ret = -ENODEV;
+ goto free_regs;
+ }
+
+ motg->async_irq = platform_get_irq_byname(pdev, "async_irq");
+ if (motg->async_irq < 0) {
+ dev_err(&pdev->dev, "platform_get_irq for async_int failed\n");
+ motg->async_irq = 0;
+ goto free_regs;
+ }
+
+ if (motg->xo_clk) {
+ ret = clk_prepare_enable(motg->xo_clk);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s failed to vote for TCXO %d\n",
+ __func__, ret);
+ goto free_xo_handle;
+ }
+ }
+
+
+ clk_prepare_enable(motg->pclk);
+
+ hsusb_vdd = devm_regulator_get(motg->phy.dev, "hsusb_vdd_dig");
+ if (IS_ERR(hsusb_vdd)) {
+ hsusb_vdd = devm_regulator_get(motg->phy.dev, "HSUSB_VDDCX");
+ if (IS_ERR(hsusb_vdd)) {
+ dev_err(motg->phy.dev, "unable to get hsusb vddcx\n");
+ ret = PTR_ERR(hsusb_vdd);
+ goto devote_xo_handle;
+ }
+ }
+
+ len = of_property_count_elems_of_size(pdev->dev.of_node,
+ "qcom,vdd-voltage-level", sizeof(len));
+ if (len > 0) {
+ if (len == sizeof(tmp) / sizeof(len)) {
+ of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,vdd-voltage-level",
+ tmp, len);
+ vdd_val[0] = tmp[0];
+ vdd_val[1] = tmp[1];
+ vdd_val[2] = tmp[2];
+ } else {
+ dev_dbg(&pdev->dev,
+ "Using default hsusb vdd config.\n");
+ goto devote_xo_handle;
+ }
+ } else {
+ goto devote_xo_handle;
+ }
+
+ ret = msm_hsusb_config_vddcx(1);
+ if (ret) {
+ dev_err(&pdev->dev, "hsusb vddcx configuration failed\n");
+ goto devote_xo_handle;
+ }
+
+ ret = regulator_enable(hsusb_vdd);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable the hsusb vddcx\n");
+ goto free_config_vddcx;
+ }
+
+ ret = msm_hsusb_ldo_init(motg, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "hsusb vreg configuration failed\n");
+ goto free_hsusb_vdd;
+ }
+
+ /* Get pinctrl if target uses pinctrl */
+ motg->phy_pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (IS_ERR(motg->phy_pinctrl)) {
+ if (of_property_read_bool(pdev->dev.of_node, "pinctrl-names")) {
+ dev_err(&pdev->dev, "Error encountered while getting pinctrl");
+ ret = PTR_ERR(motg->phy_pinctrl);
+ goto free_ldo_init;
+ }
+ dev_dbg(&pdev->dev, "Target does not use pinctrl\n");
+ motg->phy_pinctrl = NULL;
+ }
+
+ ret = msm_hsusb_ldo_enable(motg, USB_PHY_REG_ON);
+ if (ret) {
+ dev_err(&pdev->dev, "hsusb vreg enable failed\n");
+ goto free_ldo_init;
+ }
+ clk_prepare_enable(motg->core_clk);
+
+ /* Check if USB mem_type change is needed to workaround PNOC hw issue */
+ msm_otg_pnoc_errata_fix(motg);
+
+ writel_relaxed(0, USB_USBINTR);
+ writel_relaxed(0, USB_OTGSC);
+ /* Ensure that above STOREs are completed before enabling interrupts */
+ mb();
+
+ motg->id_state = USB_ID_FLOAT;
+ set_bit(ID, &motg->inputs);
+ INIT_WORK(&motg->sm_work, msm_otg_sm_work);
+ INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work);
+ INIT_DELAYED_WORK(&motg->id_status_work, msm_id_status_w);
+ INIT_DELAYED_WORK(&motg->perf_vote_work, msm_otg_perf_vote_work);
+ setup_timer(&motg->chg_check_timer, msm_otg_chg_check_timer_func,
+ (unsigned long) motg);
+ motg->otg_wq = alloc_ordered_workqueue("k_otg", 0);
+ if (!motg->otg_wq) {
+ pr_err("%s: Unable to create workqueue otg_wq\n",
+ __func__);
+ goto disable_core_clk;
+ }
+
+ ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED,
+ "msm_otg", motg);
+ if (ret) {
+ dev_err(&pdev->dev, "request irq failed\n");
+ goto destroy_wq;
+ }
+
+ motg->phy_irq = platform_get_irq_byname(pdev, "phy_irq");
+ if (motg->phy_irq < 0) {
+ dev_dbg(&pdev->dev, "phy_irq is not present\n");
+ motg->phy_irq = 0;
+ } else {
+
+ /* clear all interrupts before enabling the IRQ */
+ writeb_relaxed(0xFF, USB2_PHY_USB_PHY_INTERRUPT_CLEAR0);
+ writeb_relaxed(0xFF, USB2_PHY_USB_PHY_INTERRUPT_CLEAR1);
+
+ writeb_relaxed(0x1, USB2_PHY_USB_PHY_IRQ_CMD);
+ /*
+ * Databook says 200 usec delay is required for
+ * clearing the interrupts.
+ */
+ udelay(200);
+ writeb_relaxed(0x0, USB2_PHY_USB_PHY_IRQ_CMD);
+
+ ret = request_irq(motg->phy_irq, msm_otg_phy_irq_handler,
+ IRQF_TRIGGER_RISING, "msm_otg_phy_irq", motg);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "phy_irq request fail %d\n", ret);
+ goto free_irq;
+ }
+ }
+
+ ret = request_irq(motg->async_irq, msm_otg_irq,
+ IRQF_TRIGGER_RISING, "msm_otg", motg);
+ if (ret) {
+ dev_err(&pdev->dev, "request irq failed (ASYNC INT)\n");
+ goto free_phy_irq;
+ }
+ disable_irq(motg->async_irq);
+
+ if (pdata->otg_control == OTG_PHY_CONTROL && pdata->mpm_otgsessvld_int)
+ msm_mpm_enable_pin(pdata->mpm_otgsessvld_int, 1);
+
+ if (pdata->mpm_dpshv_int)
+ msm_mpm_enable_pin(pdata->mpm_dpshv_int, 1);
+ if (pdata->mpm_dmshv_int)
+ msm_mpm_enable_pin(pdata->mpm_dmshv_int, 1);
+
+ phy->init = msm_otg_reset;
+ phy->set_power = msm_otg_set_power;
+ phy->set_suspend = msm_otg_set_suspend;
+ phy->dbg_event = msm_otg_dbg_log_event;
+
+ phy->io_ops = &msm_otg_io_ops;
+
+ phy->otg->usb_phy = &motg->phy;
+ phy->otg->set_host = msm_otg_set_host;
+ phy->otg->set_peripheral = msm_otg_set_peripheral;
+ if (pdata->dp_manual_pullup)
+ phy->flags |= ENABLE_DP_MANUAL_PULLUP;
+
+ if (pdata->enable_sec_phy)
+ phy->flags |= ENABLE_SECONDARY_PHY;
+
+ ret = usb_add_phy(&motg->phy, USB_PHY_TYPE_USB2);
+ if (ret) {
+ dev_err(&pdev->dev, "usb_add_phy failed\n");
+ goto free_async_irq;
+ }
+
+ ret = usb_phy_regulator_init(motg);
+ if (ret) {
+ dev_err(&pdev->dev, "usb_phy_regulator_init failed\n");
+ goto remove_phy;
+ }
+
+ if (motg->pdata->mode == USB_OTG &&
+ motg->pdata->otg_control == OTG_PMIC_CONTROL &&
+ !motg->phy_irq) {
+
+ if (gpio_is_valid(motg->pdata->usb_id_gpio)) {
+ /* usb_id_gpio request */
+ ret = gpio_request(motg->pdata->usb_id_gpio,
+ "USB_ID_GPIO");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "gpio req failed for id\n");
+ motg->pdata->usb_id_gpio = 0;
+ goto remove_phy;
+ }
+
+ /*
+ * The following code implements switch between the HOST
+ * mode to device mode when used different HW components
+ * on the same port: USB HUB and the usb jack type B
+ * for device mode In this case HUB should be gone
+ * only once out of reset at the boot time and after
+ * that always stay on
+ */
+ if (gpio_is_valid(motg->pdata->hub_reset_gpio)) {
+ ret = devm_gpio_request(&pdev->dev,
+ motg->pdata->hub_reset_gpio,
+ "qcom,hub-reset-gpio");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "gpio req failed for hub reset\n");
+ goto remove_phy;
+ }
+ gpio_direction_output(
+ motg->pdata->hub_reset_gpio, 1);
+ }
+
+ if (gpio_is_valid(motg->pdata->switch_sel_gpio)) {
+ ret = devm_gpio_request(&pdev->dev,
+ motg->pdata->switch_sel_gpio,
+ "qcom,sw-sel-gpio");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "gpio req failed for switch sel\n");
+ goto remove_phy;
+ }
+ if (gpio_get_value(motg->pdata->usb_id_gpio))
+ gpio_direction_input(
+ motg->pdata->switch_sel_gpio);
+
+ else
+ gpio_direction_output(
+ motg->pdata->switch_sel_gpio,
+ 1);
+ }
+
+ /* usb_id_gpio to irq */
+ id_irq = gpio_to_irq(motg->pdata->usb_id_gpio);
+ motg->ext_id_irq = id_irq;
+ } else if (motg->pdata->pmic_id_irq) {
+ id_irq = motg->pdata->pmic_id_irq;
+ }
+
+ if (id_irq) {
+ ret = request_irq(id_irq,
+ msm_id_irq,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING,
+ "msm_otg", motg);
+ if (ret) {
+ dev_err(&pdev->dev, "request irq failed for ID\n");
+ goto remove_phy;
+ }
+ } else {
+ /* PMIC does USB ID detection and notifies through
+ * USB_OTG property of USB powersupply.
+ */
+ dev_dbg(&pdev->dev, "PMIC does ID detection\n");
+ }
+ }
+
+ platform_set_drvdata(pdev, motg);
+ device_init_wakeup(&pdev->dev, 1);
+
+ ret = msm_otg_debugfs_init(motg);
+ if (ret)
+ dev_dbg(&pdev->dev, "mode debugfs file is not available\n");
+
+ if (motg->pdata->otg_control == OTG_PMIC_CONTROL &&
+ (!(motg->pdata->mode == USB_OTG) ||
+ motg->pdata->pmic_id_irq || motg->ext_id_irq ||
+ !motg->phy_irq))
+ motg->caps = ALLOW_PHY_POWER_COLLAPSE | ALLOW_PHY_RETENTION;
+
+ if (motg->pdata->otg_control == OTG_PHY_CONTROL || motg->phy_irq ||
+ motg->pdata->enable_phy_id_pullup)
+ motg->caps = ALLOW_PHY_RETENTION | ALLOW_PHY_REGULATORS_LPM;
+
+ if (motg->pdata->mpm_dpshv_int || motg->pdata->mpm_dmshv_int)
+ motg->caps |= ALLOW_HOST_PHY_RETENTION;
+
+ device_create_file(&pdev->dev, &dev_attr_dpdm_pulldown_enable);
+
+ if (motg->pdata->enable_lpm_on_dev_suspend)
+ motg->caps |= ALLOW_LPM_ON_DEV_SUSPEND;
+
+ if (motg->pdata->disable_retention_with_vdd_min)
+ motg->caps |= ALLOW_VDD_MIN_WITH_RETENTION_DISABLED;
+
+ /*
+ * PHY DVDD is supplied by a always on PMIC LDO (unlike
+ * vddcx/vddmx). PHY can keep D+ pull-up and D+/D-
+ * pull-down during suspend without any additional
+ * hardware re-work.
+ */
+ if (motg->pdata->phy_type == SNPS_FEMTO_PHY)
+ motg->caps |= ALLOW_BUS_SUSPEND_WITHOUT_REWORK;
+
+ pm_stay_awake(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ if (motg->pdata->delay_lpm_on_disconnect) {
+ pm_runtime_set_autosuspend_delay(&pdev->dev,
+ lpm_disconnect_thresh);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ }
+
+ ret = msm_otg_setup_ext_chg_cdev(motg);
+ if (ret)
+ dev_dbg(&pdev->dev, "fail to setup cdev\n");
+
+ if (pdev->dev.of_node) {
+ ret = msm_otg_setup_devices(pdev, pdata->mode, true);
+ if (ret) {
+ dev_err(&pdev->dev, "devices setup failed\n");
+ goto remove_cdev;
+ }
+ }
+
+ psy = power_supply_get_by_name("usb");
+ if (!psy) {
+ dev_dbg(&pdev->dev, "Could not get usb power_supply\n");
+ ret = -EPROBE_DEFER;
+ goto otg_remove_devices;
+ }
+
+
+ ret = msm_otg_extcon_register(motg);
+ if (ret)
+ goto put_psy;
+
+ if (motg->extcon_vbus) {
+ ret = extcon_get_cable_state_(motg->extcon_vbus, EXTCON_USB);
+ if (ret)
+ set_bit(B_SESS_VLD, &motg->inputs);
+ else
+ clear_bit(B_SESS_VLD, &motg->inputs);
+ }
+
+ if (motg->extcon_id) {
+ ret = extcon_get_cable_state_(motg->extcon_id, EXTCON_USB_HOST);
+ if (ret)
+ clear_bit(ID, &motg->inputs);
+ else
+ set_bit(ID, &motg->inputs);
+ }
+
+ if (gpio_is_valid(motg->pdata->hub_reset_gpio)) {
+ ret = devm_gpio_request(&pdev->dev,
+ motg->pdata->hub_reset_gpio,
+ "HUB_RESET");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "gpio req failed for hub_reset\n");
+ } else {
+ gpio_direction_output(
+ motg->pdata->hub_reset_gpio, 0);
+ /* 5 microsecs reset signaling to usb hub */
+ usleep_range(5, 10);
+ gpio_direction_output(
+ motg->pdata->hub_reset_gpio, 1);
+ }
+ }
+
+ if (gpio_is_valid(motg->pdata->usbeth_reset_gpio)) {
+ ret = devm_gpio_request(&pdev->dev,
+ motg->pdata->usbeth_reset_gpio,
+ "ETH_RESET");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "gpio req failed for usbeth_reset\n");
+ } else {
+ gpio_direction_output(
+ motg->pdata->usbeth_reset_gpio, 0);
+ /* 100 microsecs reset signaling to usb-to-eth */
+ usleep_range(100, 110);
+ gpio_direction_output(
+ motg->pdata->usbeth_reset_gpio, 1);
+ }
+ }
+
+ motg->pm_notify.notifier_call = msm_otg_pm_notify;
+ register_pm_notifier(&motg->pm_notify);
+ msm_otg_dbg_log_event(phy, "OTG PROBE", motg->caps, motg->lpm_flags);
+
+ return 0;
+
+put_psy:
+ if (psy)
+ power_supply_put(psy);
+otg_remove_devices:
+ if (pdev->dev.of_node)
+ msm_otg_setup_devices(pdev, motg->pdata->mode, false);
+remove_cdev:
+ if (!motg->ext_chg_device) {
+ device_destroy(motg->ext_chg_class, motg->ext_chg_dev);
+ cdev_del(&motg->ext_chg_cdev);
+ class_destroy(motg->ext_chg_class);
+ unregister_chrdev_region(motg->ext_chg_dev, 1);
+ }
+remove_phy:
+ usb_remove_phy(&motg->phy);
+free_async_irq:
+ free_irq(motg->async_irq, motg);
+free_phy_irq:
+ if (motg->phy_irq)
+ free_irq(motg->phy_irq, motg);
+free_irq:
+ free_irq(motg->irq, motg);
+destroy_wq:
+ destroy_workqueue(motg->otg_wq);
+disable_core_clk:
+ clk_disable_unprepare(motg->core_clk);
+ msm_hsusb_ldo_enable(motg, USB_PHY_REG_OFF);
+free_ldo_init:
+ msm_hsusb_ldo_init(motg, 0);
+free_hsusb_vdd:
+ regulator_disable(hsusb_vdd);
+free_config_vddcx:
+ regulator_set_voltage(hsusb_vdd,
+ vdd_val[VDD_NONE],
+ vdd_val[VDD_MAX]);
+devote_xo_handle:
+ clk_disable_unprepare(motg->pclk);
+ if (motg->xo_clk)
+ clk_disable_unprepare(motg->xo_clk);
+free_xo_handle:
+ if (motg->xo_clk) {
+ clk_put(motg->xo_clk);
+ motg->xo_clk = NULL;
+ }
+free_regs:
+ iounmap(motg->regs);
+devote_bus_bw:
+ if (motg->bus_perf_client) {
+ msm_otg_bus_vote(motg, USB_NO_PERF_VOTE);
+ msm_bus_scale_unregister_client(motg->bus_perf_client);
+ }
+disable_phy_csr_clk:
+ if (motg->phy_csr_clk)
+ clk_disable_unprepare(motg->phy_csr_clk);
+disable_sleep_clk:
+ if (motg->sleep_clk)
+ clk_disable_unprepare(motg->sleep_clk);
+put_xo_clk:
+ if (motg->xo_clk)
+ clk_put(motg->xo_clk);
+put_pclk:
+ if (motg->pclk)
+ clk_put(motg->pclk);
+put_core_clk:
+ if (motg->core_clk)
+ clk_put(motg->core_clk);
+free_motg:
+ kfree(motg);
+ return ret;
+}
+
+static int msm_otg_remove(struct platform_device *pdev)
+{
+ struct msm_otg *motg = platform_get_drvdata(pdev);
+ struct usb_phy *phy = &motg->phy;
+ int cnt = 0;
+
+ if (phy->otg->host || phy->otg->gadget)
+ return -EBUSY;
+
+ unregister_pm_notifier(&motg->pm_notify);
+
+ extcon_unregister_notifier(motg->extcon_id, EXTCON_USB_HOST,
+ &motg->id_nb);
+ extcon_unregister_notifier(motg->extcon_vbus, EXTCON_USB,
+ &motg->vbus_nb);
+
+ if (!motg->ext_chg_device) {
+ device_destroy(motg->ext_chg_class, motg->ext_chg_dev);
+ cdev_del(&motg->ext_chg_cdev);
+ class_destroy(motg->ext_chg_class);
+ unregister_chrdev_region(motg->ext_chg_dev, 1);
+ }
+
+ if (pdev->dev.of_node)
+ msm_otg_setup_devices(pdev, motg->pdata->mode, false);
+ if (psy)
+ power_supply_put(psy);
+ msm_otg_debugfs_cleanup();
+ cancel_delayed_work_sync(&motg->chg_work);
+ cancel_delayed_work_sync(&motg->id_status_work);
+ cancel_delayed_work_sync(&motg->perf_vote_work);
+ msm_otg_perf_vote_update(motg, false);
+ cancel_work_sync(&motg->sm_work);
+ destroy_workqueue(motg->otg_wq);
+
+ pm_runtime_resume(&pdev->dev);
+
+ device_init_wakeup(&pdev->dev, 0);
+ pm_runtime_disable(&pdev->dev);
+
+ if (motg->phy_irq)
+ free_irq(motg->phy_irq, motg);
+ if (motg->pdata->pmic_id_irq)
+ free_irq(motg->pdata->pmic_id_irq, motg);
+ usb_remove_phy(phy);
+ free_irq(motg->irq, motg);
+
+ if (motg->pdata->mpm_dpshv_int || motg->pdata->mpm_dmshv_int)
+ device_remove_file(&pdev->dev,
+ &dev_attr_dpdm_pulldown_enable);
+ if (motg->pdata->otg_control == OTG_PHY_CONTROL &&
+ motg->pdata->mpm_otgsessvld_int)
+ msm_mpm_enable_pin(motg->pdata->mpm_otgsessvld_int, 0);
+
+ if (motg->pdata->mpm_dpshv_int)
+ msm_mpm_enable_pin(motg->pdata->mpm_dpshv_int, 0);
+ if (motg->pdata->mpm_dmshv_int)
+ msm_mpm_enable_pin(motg->pdata->mpm_dmshv_int, 0);
+
+ /*
+ * Put PHY in low power mode.
+ */
+ ulpi_read(phy, 0x14);
+ ulpi_write(phy, 0x08, 0x09);
+
+ writel_relaxed(readl_relaxed(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC);
+ while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
+ if (readl_relaxed(USB_PORTSC) & PORTSC_PHCD)
+ break;
+ udelay(1);
+ cnt++;
+ }
+ if (cnt >= PHY_SUSPEND_TIMEOUT_USEC)
+ dev_err(phy->dev, "Unable to suspend PHY\n");
+
+ clk_disable_unprepare(motg->pclk);
+ clk_disable_unprepare(motg->core_clk);
+ if (motg->phy_csr_clk)
+ clk_disable_unprepare(motg->phy_csr_clk);
+ if (motg->xo_clk) {
+ clk_disable_unprepare(motg->xo_clk);
+ clk_put(motg->xo_clk);
+ }
+
+ if (!IS_ERR(motg->sleep_clk))
+ clk_disable_unprepare(motg->sleep_clk);
+
+ msm_hsusb_ldo_enable(motg, USB_PHY_REG_OFF);
+ msm_hsusb_ldo_init(motg, 0);
+ regulator_disable(hsusb_vdd);
+ regulator_set_voltage(hsusb_vdd,
+ vdd_val[VDD_NONE],
+ vdd_val[VDD_MAX]);
+
+ iounmap(motg->regs);
+ pm_runtime_set_suspended(&pdev->dev);
+
+ clk_put(motg->pclk);
+ clk_put(motg->core_clk);
+
+ if (motg->bus_perf_client) {
+ msm_otg_bus_vote(motg, USB_NO_PERF_VOTE);
+ msm_bus_scale_unregister_client(motg->bus_perf_client);
+ }
+
+ return 0;
+}
+
+static void msm_otg_shutdown(struct platform_device *pdev)
+{
+ struct msm_otg *motg = platform_get_drvdata(pdev);
+
+ dev_dbg(&pdev->dev, "OTG shutdown\n");
+ msm_hsusb_vbus_power(motg, 0);
+}
+
+#ifdef CONFIG_PM
+static int msm_otg_runtime_idle(struct device *dev)
+{
+ struct msm_otg *motg = dev_get_drvdata(dev);
+ struct usb_phy *phy = &motg->phy;
+
+ dev_dbg(dev, "OTG runtime idle\n");
+ msm_otg_dbg_log_event(phy, "RUNTIME IDLE",
+ phy->otg->state, motg->ext_chg_active);
+
+ if (phy->otg->state == OTG_STATE_UNDEFINED)
+ return -EAGAIN;
+
+ if (motg->ext_chg_active == DEFAULT) {
+ dev_dbg(dev, "Deferring LPM\n");
+ /*
+ * Charger detection may happen in user space.
+ * Delay entering LPM by 3 sec. Otherwise we
+ * have to exit LPM when user space begins
+ * charger detection.
+ *
+ * This timer will be canceled when user space
+ * votes against LPM by incrementing PM usage
+ * counter. We enter low power mode when
+ * PM usage counter is decremented.
+ */
+ pm_schedule_suspend(dev, 3000);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int msm_otg_runtime_suspend(struct device *dev)
+{
+ struct msm_otg *motg = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "OTG runtime suspend\n");
+ msm_otg_dbg_log_event(&motg->phy, "RUNTIME SUSPEND",
+ get_pm_runtime_counter(dev), 0);
+ return msm_otg_suspend(motg);
+}
+
+static int msm_otg_runtime_resume(struct device *dev)
+{
+ struct msm_otg *motg = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "OTG runtime resume\n");
+ msm_otg_dbg_log_event(&motg->phy, "RUNTIME RESUME",
+ get_pm_runtime_counter(dev), 0);
+
+ return msm_otg_resume(motg);
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_otg_pm_suspend(struct device *dev)
+{
+ struct msm_otg *motg = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "OTG PM suspend\n");
+ msm_otg_dbg_log_event(&motg->phy, "PM SUSPEND START",
+ get_pm_runtime_counter(dev),
+ atomic_read(&motg->pm_suspended));
+
+ /* flush any pending sm_work first */
+ flush_work(&motg->sm_work);
+ if (!atomic_read(&motg->in_lpm)) {
+ dev_err(dev, "Abort PM suspend!! (USB is outside LPM)\n");
+ return -EBUSY;
+ }
+ atomic_set(&motg->pm_suspended, 1);
+
+ return 0;
+}
+
+static int msm_otg_pm_resume(struct device *dev)
+{
+ int ret = 0;
+ struct msm_otg *motg = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "OTG PM resume\n");
+ msm_otg_dbg_log_event(&motg->phy, "PM RESUME START",
+ get_pm_runtime_counter(dev), pm_runtime_suspended(dev));
+
+ if (motg->resume_pending || motg->phy_irq_pending) {
+ msm_otg_dbg_log_event(&motg->phy, "PM RESUME BY USB",
+ motg->async_int, motg->resume_pending);
+ /* sm work if pending will start in pm notify to exit LPM */
+ }
+
+ return ret;
+}
+#endif
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops msm_otg_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(msm_otg_pm_suspend, msm_otg_pm_resume)
+ SET_RUNTIME_PM_OPS(msm_otg_runtime_suspend, msm_otg_runtime_resume,
+ msm_otg_runtime_idle)
+};
+#endif
+
+static const struct of_device_id msm_otg_dt_match[] = {
+ { .compatible = "qcom,hsusb-otg",
+ },
+ {}
+};
+
+static struct platform_driver msm_otg_driver = {
+ .probe = msm_otg_probe,
+ .remove = msm_otg_remove,
+ .shutdown = msm_otg_shutdown,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &msm_otg_dev_pm_ops,
+#endif
+ .of_match_table = msm_otg_dt_match,
+ },
+};
+
+module_platform_driver(msm_otg_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM USB transceiver driver");
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 56ecb8b..584ae8c 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -63,6 +63,7 @@
- Google USB serial devices
- HP4x calculators
- a number of Motorola phones
+ - Motorola Tetra devices
- Novatel Wireless GPS receivers
- Siemens USB/MPI adapter.
- ViVOtech ViVOpay USB device.
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 11ee55e..3178d8a 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -121,6 +121,7 @@
{ USB_DEVICE(0x10C4, 0x8470) }, /* Juniper Networks BX Series System Console */
{ USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */
{ USB_DEVICE(0x10C4, 0x84B6) }, /* Starizona Hyperion */
+ { USB_DEVICE(0x10C4, 0x85A7) }, /* LifeScan OneTouch Verio IQ */
{ USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */
{ USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */
{ USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */
@@ -171,6 +172,7 @@
{ USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */
{ USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */
{ USB_DEVICE(0x18EF, 0xE025) }, /* ELV Marble Sound Board 1 */
+ { USB_DEVICE(0x18EF, 0xE030) }, /* ELV ALC 8xxx Battery Charger */
{ USB_DEVICE(0x18EF, 0xE032) }, /* ELV TFD500 Data Logger */
{ USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */
{ USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 3249f42..0c743e4 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1017,6 +1017,7 @@
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_BT_USB_PID) },
{ USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_WL_USB_PID) },
+ { USB_DEVICE(AIRBUS_DS_VID, AIRBUS_DS_P8GR) },
{ } /* Terminating entry */
};
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index f9d15bd..543d280 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -914,6 +914,12 @@
#define ICPDAS_I7563U_PID 0x0105
/*
+ * Airbus Defence and Space
+ */
+#define AIRBUS_DS_VID 0x1e8e /* Vendor ID */
+#define AIRBUS_DS_P8GR 0x6001 /* Tetra P8GR */
+
+/*
* RT Systems programming cables for various ham radios
*/
#define RTSYSTEMS_VID 0x2100 /* Vendor ID */
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 464db17..de61271 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -2215,7 +2215,6 @@
/* something went wrong */
dev_err(dev, "%s - usb_submit_urb(write command) failed, status = %d\n",
__func__, status);
- usb_kill_urb(urb);
usb_free_urb(urb);
atomic_dec(&CmdUrbs);
return status;
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index ffa8ec9..1799aa0 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -236,6 +236,8 @@
/* These Quectel products use Qualcomm's vendor ID */
#define QUECTEL_PRODUCT_UC20 0x9003
#define QUECTEL_PRODUCT_UC15 0x9090
+/* These Yuga products use Qualcomm's vendor ID */
+#define YUGA_PRODUCT_CLM920_NC5 0x9625
#define QUECTEL_VENDOR_ID 0x2c7c
/* These Quectel products use Quectel's vendor ID */
@@ -283,6 +285,7 @@
#define TELIT_PRODUCT_LE922_USBCFG3 0x1043
#define TELIT_PRODUCT_LE922_USBCFG5 0x1045
#define TELIT_PRODUCT_ME910 0x1100
+#define TELIT_PRODUCT_ME910_DUAL_MODEM 0x1101
#define TELIT_PRODUCT_LE920 0x1200
#define TELIT_PRODUCT_LE910 0x1201
#define TELIT_PRODUCT_LE910_USBCFG4 0x1206
@@ -380,6 +383,9 @@
#define FOUR_G_SYSTEMS_PRODUCT_W14 0x9603
#define FOUR_G_SYSTEMS_PRODUCT_W100 0x9b01
+/* Fujisoft products */
+#define FUJISOFT_PRODUCT_FS040U 0x9b02
+
/* iBall 3.5G connect wireless modem */
#define IBALL_3_5G_CONNECT 0x9605
@@ -648,6 +654,11 @@
.reserved = BIT(1) | BIT(3),
};
+static const struct option_blacklist_info telit_me910_dual_modem_blacklist = {
+ .sendsetup = BIT(0),
+ .reserved = BIT(3),
+};
+
static const struct option_blacklist_info telit_le910_blacklist = {
.sendsetup = BIT(0),
.reserved = BIT(1) | BIT(2),
@@ -677,6 +688,10 @@
.reserved = BIT(4) | BIT(5),
};
+static const struct option_blacklist_info yuga_clm920_nc5_blacklist = {
+ .reserved = BIT(1) | BIT(4),
+};
+
static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
@@ -1181,6 +1196,9 @@
{ USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC15)},
{ USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC20),
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ /* Yuga products use Qualcomm vendor ID */
+ { USB_DEVICE(QUALCOMM_VENDOR_ID, YUGA_PRODUCT_CLM920_NC5),
+ .driver_info = (kernel_ulong_t)&yuga_clm920_nc5_blacklist },
/* Quectel products using Quectel vendor ID */
{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC21),
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
@@ -1247,6 +1265,8 @@
.driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910),
.driver_info = (kernel_ulong_t)&telit_me910_blacklist },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910_DUAL_MODEM),
+ .driver_info = (kernel_ulong_t)&telit_me910_dual_modem_blacklist },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910),
.driver_info = (kernel_ulong_t)&telit_le910_blacklist },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910_USBCFG4),
@@ -1880,6 +1900,8 @@
{ USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W100),
.driver_info = (kernel_ulong_t)&four_g_w100_blacklist
},
+ {USB_DEVICE(LONGCHEER_VENDOR_ID, FUJISOFT_PRODUCT_FS040U),
+ .driver_info = (kernel_ulong_t)&net_intf3_blacklist},
{ USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, SPEEDUP_PRODUCT_SU9800, 0xff) },
{ USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, 0x9801, 0xff),
.driver_info = (kernel_ulong_t)&net_intf3_blacklist },
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index a51b283..3da25ad 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -39,6 +39,7 @@
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_DCU11) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ3) },
+ { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_CHILITAG) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ALDIGA) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MMX) },
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index 3b5a15d..1232890 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -17,6 +17,7 @@
#define PL2303_PRODUCT_ID_DCU11 0x1234
#define PL2303_PRODUCT_ID_PHAROS 0xaaa0
#define PL2303_PRODUCT_ID_RSAQ3 0xaaa2
+#define PL2303_PRODUCT_ID_CHILITAG 0xaaa8
#define PL2303_PRODUCT_ID_ALDIGA 0x0611
#define PL2303_PRODUCT_ID_MMX 0x0612
#define PL2303_PRODUCT_ID_GPRS 0x0609
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 4516291..fb6dc16 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -166,6 +166,8 @@
{DEVICE_SWI(0x1199, 0x9079)}, /* Sierra Wireless EM74xx */
{DEVICE_SWI(0x1199, 0x907a)}, /* Sierra Wireless EM74xx QDL */
{DEVICE_SWI(0x1199, 0x907b)}, /* Sierra Wireless EM74xx */
+ {DEVICE_SWI(0x1199, 0x9090)}, /* Sierra Wireless EM7565 QDL */
+ {DEVICE_SWI(0x1199, 0x9091)}, /* Sierra Wireless EM7565 */
{DEVICE_SWI(0x413c, 0x81a2)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a3)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a4)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
@@ -346,6 +348,7 @@
break;
case 2:
dev_dbg(dev, "NMEA GPS interface found\n");
+ sendsetup = true;
break;
case 3:
dev_dbg(dev, "Modem port found\n");
diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c
index e98b6e5..6aa7ff2 100644
--- a/drivers/usb/serial/usb-serial-simple.c
+++ b/drivers/usb/serial/usb-serial-simple.c
@@ -80,6 +80,11 @@
{ USB_DEVICE(0x22b8, 0x2c64) } /* Motorola V950 phone */
DEVICE(moto_modem, MOTO_IDS);
+/* Motorola Tetra driver */
+#define MOTOROLA_TETRA_IDS() \
+ { USB_DEVICE(0x0cad, 0x9011) } /* Motorola Solutions TETRA PEI */
+DEVICE(motorola_tetra, MOTOROLA_TETRA_IDS);
+
/* Novatel Wireless GPS driver */
#define NOVATEL_IDS() \
{ USB_DEVICE(0x09d7, 0x0100) } /* NovAtel FlexPack GPS */
@@ -110,6 +115,7 @@
&google_device,
&vivopay_device,
&moto_modem_device,
+ &motorola_tetra_device,
&novatel_gps_device,
&hp4x_device,
&suunto_device,
@@ -125,6 +131,7 @@
GOOGLE_IDS(),
VIVOPAY_IDS(),
MOTO_IDS(),
+ MOTOROLA_TETRA_IDS(),
NOVATEL_IDS(),
HP4X_IDS(),
SUUNTO_IDS(),
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 9876af4..6891e90 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -1076,20 +1076,19 @@
return 0;
err = uas_configure_endpoints(devinfo);
- if (err) {
+ if (err && err != ENODEV)
shost_printk(KERN_ERR, shost,
"%s: alloc streams error %d after reset",
__func__, err);
- return 1;
- }
+ /* we must unblock the host in every case lest we deadlock */
spin_lock_irqsave(shost->host_lock, flags);
scsi_report_bus_reset(shost, 0);
spin_unlock_irqrestore(shost->host_lock, flags);
scsi_unblock_requests(shost);
- return 0;
+ return err ? 1 : 0;
}
static int uas_suspend(struct usb_interface *intf, pm_message_t message)
diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h
index 9f356f7..719ec68 100644
--- a/drivers/usb/storage/unusual_uas.h
+++ b/drivers/usb/storage/unusual_uas.h
@@ -156,6 +156,13 @@
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_ATA_1X),
+/* Reported-by: Icenowy Zheng <icenowy@aosc.io> */
+UNUSUAL_DEV(0x2537, 0x1068, 0x0000, 0x9999,
+ "Norelsys",
+ "NS1068X",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_IGNORE_UAS),
+
/* Reported-by: Takeo Nakayama <javhera@gmx.com> */
UNUSUAL_DEV(0x357d, 0x7788, 0x0000, 0x9999,
"JMicron",
diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c
index c653ce5..1886d8e 100644
--- a/drivers/usb/usbip/stub_dev.c
+++ b/drivers/usb/usbip/stub_dev.c
@@ -163,8 +163,7 @@
* step 1?
*/
if (ud->tcp_socket) {
- dev_dbg(&sdev->udev->dev, "shutdown tcp_socket %p\n",
- ud->tcp_socket);
+ dev_dbg(&sdev->udev->dev, "shutdown sockfd\n");
kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
}
diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c
index af10f7b..325b4c0 100644
--- a/drivers/usb/usbip/stub_main.c
+++ b/drivers/usb/usbip/stub_main.c
@@ -252,11 +252,12 @@
struct stub_priv *priv;
struct urb *urb;
- dev_dbg(&sdev->udev->dev, "free sdev %p\n", sdev);
+ dev_dbg(&sdev->udev->dev, "Stub device cleaning up urbs\n");
while ((priv = stub_priv_pop(sdev))) {
urb = priv->urb;
- dev_dbg(&sdev->udev->dev, "free urb %p\n", urb);
+ dev_dbg(&sdev->udev->dev, "free urb seqnum %lu\n",
+ priv->seqnum);
usb_kill_urb(urb);
kmem_cache_free(stub_priv_cache, priv);
diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c
index 283a9be..5b80718 100644
--- a/drivers/usb/usbip/stub_rx.c
+++ b/drivers/usb/usbip/stub_rx.c
@@ -225,9 +225,6 @@
if (priv->seqnum != pdu->u.cmd_unlink.seqnum)
continue;
- dev_info(&priv->urb->dev->dev, "unlink urb %p\n",
- priv->urb);
-
/*
* This matched urb is not completed yet (i.e., be in
* flight in usb hcd hardware/driver). Now we are
@@ -266,8 +263,8 @@
ret = usb_unlink_urb(priv->urb);
if (ret != -EINPROGRESS)
dev_err(&priv->urb->dev->dev,
- "failed to unlink a urb %p, ret %d\n",
- priv->urb, ret);
+ "failed to unlink a urb # %lu, ret %d\n",
+ priv->seqnum, ret);
return 0;
}
diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c
index 87ff94b..96aa375 100644
--- a/drivers/usb/usbip/stub_tx.c
+++ b/drivers/usb/usbip/stub_tx.c
@@ -102,7 +102,7 @@
/* link a urb to the queue of tx. */
spin_lock_irqsave(&sdev->priv_lock, flags);
if (sdev->ud.tcp_socket == NULL) {
- usbip_dbg_stub_tx("ignore urb for closed connection %p", urb);
+ usbip_dbg_stub_tx("ignore urb for closed connection\n");
/* It will be freed in stub_device_cleanup_urbs(). */
} else if (priv->unlinking) {
stub_enqueue_ret_unlink(sdev, priv->seqnum, urb->status);
@@ -204,8 +204,8 @@
/* 1. setup usbip_header */
setup_ret_submit_pdu(&pdu_header, urb);
- usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n",
- pdu_header.base.seqnum, urb);
+ usbip_dbg_stub_tx("setup txdata seqnum: %d\n",
+ pdu_header.base.seqnum);
usbip_header_correct_endian(&pdu_header, 1);
iov[iovnum].iov_base = &pdu_header;
diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c
index 8b23229..2a5d318 100644
--- a/drivers/usb/usbip/usbip_common.c
+++ b/drivers/usb/usbip/usbip_common.c
@@ -105,7 +105,7 @@
dev_dbg(dev, " devnum(%d) devpath(%s) usb speed(%s)",
udev->devnum, udev->devpath, usb_speed_string(udev->speed));
- pr_debug("tt %p, ttport %d\n", udev->tt, udev->ttport);
+ pr_debug("tt hub ttport %d\n", udev->ttport);
dev_dbg(dev, " ");
for (i = 0; i < 16; i++)
@@ -138,12 +138,8 @@
}
pr_debug("\n");
- dev_dbg(dev, "parent %p, bus %p\n", udev->parent, udev->bus);
-
- dev_dbg(dev,
- "descriptor %p, config %p, actconfig %p, rawdescriptors %p\n",
- &udev->descriptor, udev->config,
- udev->actconfig, udev->rawdescriptors);
+ dev_dbg(dev, "parent %s, bus %s\n", dev_name(&udev->parent->dev),
+ udev->bus->bus_name);
dev_dbg(dev, "have_langid %d, string_langid %d\n",
udev->have_langid, udev->string_langid);
@@ -251,9 +247,6 @@
dev = &urb->dev->dev;
- dev_dbg(dev, " urb :%p\n", urb);
- dev_dbg(dev, " dev :%p\n", urb->dev);
-
usbip_dump_usb_device(urb->dev);
dev_dbg(dev, " pipe :%08x ", urb->pipe);
@@ -262,11 +255,9 @@
dev_dbg(dev, " status :%d\n", urb->status);
dev_dbg(dev, " transfer_flags :%08X\n", urb->transfer_flags);
- dev_dbg(dev, " transfer_buffer :%p\n", urb->transfer_buffer);
dev_dbg(dev, " transfer_buffer_length:%d\n",
urb->transfer_buffer_length);
dev_dbg(dev, " actual_length :%d\n", urb->actual_length);
- dev_dbg(dev, " setup_packet :%p\n", urb->setup_packet);
if (urb->setup_packet && usb_pipetype(urb->pipe) == PIPE_CONTROL)
usbip_dump_usb_ctrlrequest(
@@ -276,8 +267,6 @@
dev_dbg(dev, " number_of_packets :%d\n", urb->number_of_packets);
dev_dbg(dev, " interval :%d\n", urb->interval);
dev_dbg(dev, " error_count :%d\n", urb->error_count);
- dev_dbg(dev, " context :%p\n", urb->context);
- dev_dbg(dev, " complete :%p\n", urb->complete);
}
EXPORT_SYMBOL_GPL(usbip_dump_urb);
@@ -335,13 +324,10 @@
char *bp = buf;
int osize = size;
- usbip_dbg_xmit("enter\n");
-
- if (!sock || !buf || !size) {
- pr_err("invalid arg, sock %p buff %p size %d\n", sock, buf,
- size);
+ if (!sock || !buf || !size)
return -EINVAL;
- }
+
+ usbip_dbg_xmit("enter\n");
do {
sock->sk->sk_allocation = GFP_NOIO;
@@ -354,11 +340,8 @@
msg.msg_flags = MSG_NOSIGNAL;
result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL);
- if (result <= 0) {
- pr_debug("receive sock %p buf %p size %u ret %d total %d\n",
- sock, buf, size, result, total);
+ if (result <= 0)
goto err;
- }
size -= result;
buf += result;
diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h
index 9f49037..f0b955f 100644
--- a/drivers/usb/usbip/usbip_common.h
+++ b/drivers/usb/usbip/usbip_common.h
@@ -271,6 +271,7 @@
/* lock for status */
spinlock_t lock;
+ int sockfd;
struct socket *tcp_socket;
struct task_struct *tcp_rx;
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index d6dc165..dbe615b 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -300,7 +300,7 @@
case USB_PORT_FEAT_POWER:
usbip_dbg_vhci_rh(
" ClearPortFeature: USB_PORT_FEAT_POWER\n");
- dum->port_status[rhport] = 0;
+ dum->port_status[rhport] &= ~USB_PORT_STAT_POWER;
dum->resuming = 0;
break;
case USB_PORT_FEAT_C_RESET:
@@ -506,9 +506,6 @@
struct vhci_device *vdev;
unsigned long flags;
- usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n",
- hcd, urb, mem_flags);
-
if (portnum > VHCI_HC_PORTS) {
pr_err("invalid port number %d\n", portnum);
return -ENODEV;
@@ -671,8 +668,6 @@
struct vhci_device *vdev;
unsigned long flags;
- pr_info("dequeue a urb %p\n", urb);
-
spin_lock_irqsave(&vhci->lock, flags);
priv = urb->hcpriv;
@@ -700,7 +695,6 @@
/* tcp connection is closed */
spin_lock(&vdev->priv_lock);
- pr_info("device %p seems to be disconnected\n", vdev);
list_del(&priv->list);
kfree(priv);
urb->hcpriv = NULL;
@@ -712,8 +706,6 @@
* vhci_rx will receive RET_UNLINK and give back the URB.
* Otherwise, we give back it here.
*/
- pr_info("gives back urb %p\n", urb);
-
usb_hcd_unlink_urb_from_ep(hcd, urb);
spin_unlock_irqrestore(&vhci->lock, flags);
@@ -741,8 +733,6 @@
unlink->unlink_seqnum = priv->seqnum;
- pr_info("device %p seems to be still connected\n", vdev);
-
/* send cmd_unlink and try to cancel the pending URB in the
* peer */
list_add_tail(&unlink->list, &vdev->unlink_tx);
@@ -823,7 +813,7 @@
/* need this? see stub_dev.c */
if (ud->tcp_socket) {
- pr_debug("shutdown tcp_socket %p\n", ud->tcp_socket);
+ pr_debug("shutdown tcp_socket\n");
kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
}
diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c
index fc2d319..5943dee 100644
--- a/drivers/usb/usbip/vhci_rx.c
+++ b/drivers/usb/usbip/vhci_rx.c
@@ -37,24 +37,23 @@
urb = priv->urb;
status = urb->status;
- usbip_dbg_vhci_rx("find urb %p vurb %p seqnum %u\n",
- urb, priv, seqnum);
+ usbip_dbg_vhci_rx("find urb seqnum %u\n", seqnum);
switch (status) {
case -ENOENT:
/* fall through */
case -ECONNRESET:
- dev_info(&urb->dev->dev,
- "urb %p was unlinked %ssynchronuously.\n", urb,
- status == -ENOENT ? "" : "a");
+ dev_dbg(&urb->dev->dev,
+ "urb seq# %u was unlinked %ssynchronuously\n",
+ seqnum, status == -ENOENT ? "" : "a");
break;
case -EINPROGRESS:
/* no info output */
break;
default:
- dev_info(&urb->dev->dev,
- "urb %p may be in a error, status %d\n", urb,
- status);
+ dev_dbg(&urb->dev->dev,
+ "urb seq# %u may be in a error, status %d\n",
+ seqnum, status);
}
list_del(&priv->list);
@@ -80,8 +79,8 @@
spin_unlock_irqrestore(&vdev->priv_lock, flags);
if (!urb) {
- pr_err("cannot find a urb of seqnum %u\n", pdu->base.seqnum);
- pr_info("max seqnum %d\n",
+ pr_err("cannot find a urb of seqnum %u max seqnum %d\n",
+ pdu->base.seqnum,
atomic_read(&vhci->seqnum));
usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
return;
@@ -104,7 +103,7 @@
if (usbip_dbg_flag_vhci_rx)
usbip_dump_urb(urb);
- usbip_dbg_vhci_rx("now giveback urb %p\n", urb);
+ usbip_dbg_vhci_rx("now giveback urb %u\n", pdu->base.seqnum);
spin_lock_irqsave(&vhci->lock, flags);
usb_hcd_unlink_urb_from_ep(vhci_to_hcd(vhci), urb);
@@ -170,7 +169,7 @@
pr_info("the urb (seqnum %d) was already given back\n",
pdu->base.seqnum);
} else {
- usbip_dbg_vhci_rx("now giveback urb %p\n", urb);
+ usbip_dbg_vhci_rx("now giveback urb %d\n", pdu->base.seqnum);
/* If unlink is successful, status is -ECONNRESET */
urb->status = pdu->u.ret_unlink.status;
diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c
index c404017..c287ccc 100644
--- a/drivers/usb/usbip/vhci_sysfs.c
+++ b/drivers/usb/usbip/vhci_sysfs.c
@@ -49,13 +49,17 @@
/*
* output example:
- * port sta spd dev socket local_busid
- * 0000 004 000 00000000 c5a7bb80 1-2.3
- * 0001 004 000 00000000 d8cee980 2-3.4
+ * port sta spd dev sockfd local_busid
+ * 0000 004 000 00000000 000003 1-2.3
+ * 0001 004 000 00000000 000004 2-3.4
*
- * IP address can be retrieved from a socket pointer address by looking
- * up /proc/net/{tcp,tcp6}. Also, a userland program may remember a
- * port number and its peer IP address.
+ * Output includes socket fd instead of socket pointer address to
+ * avoid leaking kernel memory address in:
+ * /sys/devices/platform/vhci_hcd.0/status and in debug output.
+ * The socket pointer address is not used at the moment and it was
+ * made visible as a convenient way to find IP address from socket
+ * pointer address by looking up /proc/net/{tcp,tcp6}. As this opens
+ * a security hole, the change is made to use sockfd instead.
*/
for (i = 0; i < VHCI_HC_PORTS; i++) {
struct vhci_device *vdev = &vhci->vdev[i];
@@ -68,13 +72,13 @@
if (vdev->ud.status == VDEV_ST_USED) {
out += sprintf(out, "%03u %08x ",
vdev->speed, vdev->devid);
- out += sprintf(out, "%16p %s",
- vdev->ud.tcp_socket,
+ out += sprintf(out, "%06u %s",
+ vdev->ud.sockfd,
dev_name(&vdev->udev->dev));
} else {
out += sprintf(out, "000 00000000 ");
- out += sprintf(out, "0000000000000000 0-0");
+ out += sprintf(out, "000000 0-0");
}
out += sprintf(out, "\n");
@@ -125,7 +129,7 @@
int pdev_nr;
out += sprintf(out,
- "port sta spd dev socket local_busid\n");
+ "port sta spd dev sockfd local_busid\n");
pdev_nr = status_name_to_id(attr->attr.name);
if (pdev_nr < 0)
@@ -324,6 +328,7 @@
vdev->devid = devid;
vdev->speed = speed;
+ vdev->ud.sockfd = sockfd;
vdev->ud.tcp_socket = socket;
vdev->ud.status = VDEV_ST_NOTASSIGNED;
@@ -361,6 +366,7 @@
status->attr.attr.name = status->name;
status->attr.attr.mode = S_IRUGO;
status->attr.show = status_show;
+ sysfs_attr_init(&status->attr.attr);
}
static int init_status_attrs(void)
diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c
index 3e7878f..a9a663a 100644
--- a/drivers/usb/usbip/vhci_tx.c
+++ b/drivers/usb/usbip/vhci_tx.c
@@ -83,7 +83,8 @@
memset(&msg, 0, sizeof(msg));
memset(&iov, 0, sizeof(iov));
- usbip_dbg_vhci_tx("setup txdata urb %p\n", urb);
+ usbip_dbg_vhci_tx("setup txdata urb seqnum %lu\n",
+ priv->seqnum);
/* 1. setup usbip_header */
setup_cmd_submit_pdu(&pdu_header, urb);
diff --git a/drivers/usb/usbip/vudc_rx.c b/drivers/usb/usbip/vudc_rx.c
index e429b59..d020e72 100644
--- a/drivers/usb/usbip/vudc_rx.c
+++ b/drivers/usb/usbip/vudc_rx.c
@@ -132,6 +132,25 @@
urb_p->new = 1;
urb_p->seqnum = pdu->base.seqnum;
+ if (urb_p->ep->type == USB_ENDPOINT_XFER_ISOC) {
+ /* validate packet size and number of packets */
+ unsigned int maxp, packets, bytes;
+
+ maxp = usb_endpoint_maxp(urb_p->ep->desc);
+ maxp *= usb_endpoint_maxp_mult(urb_p->ep->desc);
+ bytes = pdu->u.cmd_submit.transfer_buffer_length;
+ packets = DIV_ROUND_UP(bytes, maxp);
+
+ if (pdu->u.cmd_submit.number_of_packets < 0 ||
+ pdu->u.cmd_submit.number_of_packets > packets) {
+ dev_err(&udc->gadget.dev,
+ "CMD_SUBMIT: isoc invalid num packets %d\n",
+ pdu->u.cmd_submit.number_of_packets);
+ ret = -EMSGSIZE;
+ goto free_urbp;
+ }
+ }
+
ret = alloc_urb_from_cmd(&urb_p->urb, pdu, urb_p->ep->type);
if (ret) {
usbip_event_add(&udc->ud, VUDC_EVENT_ERROR_MALLOC);
diff --git a/drivers/usb/usbip/vudc_tx.c b/drivers/usb/usbip/vudc_tx.c
index 2346617..3ab4c86 100644
--- a/drivers/usb/usbip/vudc_tx.c
+++ b/drivers/usb/usbip/vudc_tx.c
@@ -97,6 +97,13 @@
memset(&pdu_header, 0, sizeof(pdu_header));
memset(&msg, 0, sizeof(msg));
+ if (urb->actual_length > 0 && !urb->transfer_buffer) {
+ dev_err(&udc->gadget.dev,
+ "urb: actual_length %d transfer_buffer null\n",
+ urb->actual_length);
+ return -1;
+ }
+
if (urb_p->type == USB_ENDPOINT_XFER_ISOC)
iovnum = 2 + urb->number_of_packets;
else
@@ -112,8 +119,8 @@
/* 1. setup usbip_header */
setup_ret_submit_pdu(&pdu_header, urb_p);
- usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n",
- pdu_header.base.seqnum, urb);
+ usbip_dbg_stub_tx("setup txdata seqnum: %d\n",
+ pdu_header.base.seqnum);
usbip_header_correct_endian(&pdu_header, 1);
iov[iovnum].iov_base = &pdu_header;
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index 5d3b0db..eb44e99 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -2328,6 +2328,20 @@
Select this option if display contents should be inherited as set by
the bootloader.
+config FB_MSM
+ tristate "MSM Framebuffer support"
+ depends on FB && ARCH_QCOM
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ select SYNC
+ select SW_SYNC
+ ---help---
+ The MSM driver implements a frame buffer interface to
+ provide access to the display hardware and provide
+ a way for users to display graphics
+ on connected display panels.
+
config FB_MX3
tristate "MX3 Framebuffer support"
depends on FB && MX3_IPU
@@ -2448,6 +2462,7 @@
source "drivers/video/fbdev/omap/Kconfig"
source "drivers/video/fbdev/omap2/Kconfig"
source "drivers/video/fbdev/mmp/Kconfig"
+source "drivers/video/fbdev/msm/Kconfig"
config FB_SH_MOBILE_MERAM
tristate "SuperH Mobile MERAM read ahead support"
diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile
index ee8c814..c16b198 100644
--- a/drivers/video/fbdev/Makefile
+++ b/drivers/video/fbdev/Makefile
@@ -130,6 +130,11 @@
obj-$(CONFIG_FB_OPENCORES) += ocfb.o
obj-$(CONFIG_FB_SM712) += sm712fb.o
+ifeq ($(CONFIG_FB_MSM),y)
+obj-y += msm/
+else
+obj-$(CONFIG_MSM_DBA) += msm/msm_dba/
+endif
# Platform or fallback drivers go here
obj-$(CONFIG_FB_UVESA) += uvesafb.o
obj-$(CONFIG_FB_VESA) += vesafb.o
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
index f8a3839..2ef33d4 100644
--- a/drivers/video/fbdev/core/fbmem.c
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -1085,7 +1085,7 @@
EXPORT_SYMBOL(fb_blank);
static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
- unsigned long arg)
+ unsigned long arg, struct file *file)
{
struct fb_ops *fb;
struct fb_var_screeninfo var;
@@ -1222,7 +1222,9 @@
if (!lock_fb_info(info))
return -ENODEV;
fb = info->fbops;
- if (fb->fb_ioctl)
+ if (fb->fb_ioctl_v2)
+ ret = fb->fb_ioctl_v2(info, cmd, arg, file);
+ else if (fb->fb_ioctl)
ret = fb->fb_ioctl(info, cmd, arg);
else
ret = -ENOTTY;
@@ -1237,7 +1239,7 @@
if (!info)
return -ENODEV;
- return do_fb_ioctl(info, cmd, arg);
+ return do_fb_ioctl(info, cmd, arg, file);
}
#ifdef CONFIG_COMPAT
@@ -1268,7 +1270,7 @@
};
static int fb_getput_cmap(struct fb_info *info, unsigned int cmd,
- unsigned long arg)
+ unsigned long arg, struct file *file)
{
struct fb_cmap_user __user *cmap;
struct fb_cmap32 __user *cmap32;
@@ -1291,7 +1293,7 @@
put_user(compat_ptr(data), &cmap->transp))
return -EFAULT;
- err = do_fb_ioctl(info, cmd, (unsigned long) cmap);
+ err = do_fb_ioctl(info, cmd, (unsigned long) cmap, file);
if (!err) {
if (copy_in_user(&cmap32->start,
@@ -1336,7 +1338,7 @@
}
static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd,
- unsigned long arg)
+ unsigned long arg, struct file *file)
{
mm_segment_t old_fs;
struct fb_fix_screeninfo fix;
@@ -1347,7 +1349,7 @@
old_fs = get_fs();
set_fs(KERNEL_DS);
- err = do_fb_ioctl(info, cmd, (unsigned long) &fix);
+ err = do_fb_ioctl(info, cmd, (unsigned long) &fix, file);
set_fs(old_fs);
if (!err)
@@ -1374,20 +1376,22 @@
case FBIOPUT_CON2FBMAP:
arg = (unsigned long) compat_ptr(arg);
case FBIOBLANK:
- ret = do_fb_ioctl(info, cmd, arg);
+ ret = do_fb_ioctl(info, cmd, arg, file);
break;
case FBIOGET_FSCREENINFO:
- ret = fb_get_fscreeninfo(info, cmd, arg);
+ ret = fb_get_fscreeninfo(info, cmd, arg, file);
break;
case FBIOGETCMAP:
case FBIOPUTCMAP:
- ret = fb_getput_cmap(info, cmd, arg);
+ ret = fb_getput_cmap(info, cmd, arg, file);
break;
default:
- if (fb->fb_compat_ioctl)
+ if (fb->fb_compat_ioctl_v2)
+ ret = fb->fb_compat_ioctl_v2(info, cmd, arg, file);
+ else if (fb->fb_compat_ioctl)
ret = fb->fb_compat_ioctl(info, cmd, arg);
break;
}
diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile
index ed3ff87..e09dcdb 100644
--- a/drivers/video/fbdev/msm/Makefile
+++ b/drivers/video/fbdev/msm/Makefile
@@ -1,6 +1,7 @@
ccflags-y += -I$(src)
obj-$(CONFIG_FB_MSM_MDSS_MHL3) += mhl3/
+obj-$(CONFIG_MSM_DBA) += msm_dba/
mdss-mdp3-objs = mdp3.o mdp3_layer.o mdp3_dma.o mdp3_ctrl.o dsi_status_v2.o
mdss-mdp3-objs += mdp3_ppp.o mdp3_ppp_hwio.o mdp3_ppp_data.o
@@ -9,7 +10,7 @@
ccflags-y += -DTARGET_HW_MDSS_MDP3
endif
mdss-mdp-objs := mdss_mdp.o mdss_mdp_ctl.o mdss_mdp_pipe.o mdss_mdp_util.o dsi_status_6g.o
-mdss-mdp-objs += mdss_mdp_pp.o mdss_mdp_pp_debug.o mdss_mdp_pp_cache_config.o
+mdss-mdp-objs += mdss_mdp_pp.o mdss_mdp_pp_debug.o mdss_mdp_pp_cache_config.o mdss_sync.o
mdss-mdp-objs += mdss_mdp_intf_video.o
mdss-mdp-objs += mdss_mdp_intf_cmd.o
mdss-mdp-objs += mdss_mdp_intf_writeback.o
@@ -20,6 +21,7 @@
mdss-mdp-objs += mdss_mdp_cdm.o
mdss-mdp-objs += mdss_smmu.o
mdss-mdp-objs += mdss_mdp_wfd.o
+mdss-mdp-objs += mdss_io_util.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_mdp_debug.o
diff --git a/drivers/video/fbdev/msm/dsi_host_v2.c b/drivers/video/fbdev/msm/dsi_host_v2.c
index 2782702..33775ec 100644
--- a/drivers/video/fbdev/msm/dsi_host_v2.c
+++ b/drivers/video/fbdev/msm/dsi_host_v2.c
@@ -1114,7 +1114,7 @@
if (!pdata->panel_info.dynamic_switch_pending) {
for (i = 0; !ret && (i < DSI_MAX_PM); i++) {
- ret = msm_dss_enable_vreg(
+ ret = msm_mdss_enable_vreg(
ctrl_pdata->power_data[i].vreg_config,
ctrl_pdata->power_data[i].num_vreg, 1);
if (ret) {
@@ -1215,7 +1215,7 @@
error_vreg:
if (ret) {
for (; i >= 0; i--)
- msm_dss_enable_vreg(
+ msm_mdss_enable_vreg(
ctrl_pdata->power_data[i].vreg_config,
ctrl_pdata->power_data[i].num_vreg, 0);
}
@@ -1250,7 +1250,7 @@
if (!pdata->panel_info.dynamic_switch_pending) {
for (i = DSI_MAX_PM - 1; i >= 0; i--) {
- ret = msm_dss_enable_vreg(
+ ret = msm_mdss_enable_vreg(
ctrl_pdata->power_data[i].vreg_config,
ctrl_pdata->power_data[i].num_vreg, 0);
if (ret)
@@ -1287,7 +1287,7 @@
pinfo = &pdata->panel_info;
mutex_lock(&ctrl_pdata->mutex);
for (i = 0; !ret && (i < DSI_MAX_PM); i++) {
- ret = msm_dss_enable_vreg(
+ ret = msm_mdss_enable_vreg(
ctrl_pdata->power_data[i].vreg_config,
ctrl_pdata->power_data[i].num_vreg, 1);
if (ret) {
@@ -1314,7 +1314,7 @@
error_vreg:
if (ret) {
for (; i >= 0; i--)
- msm_dss_enable_vreg(
+ msm_mdss_enable_vreg(
ctrl_pdata->power_data[i].vreg_config,
ctrl_pdata->power_data[i].num_vreg, 0);
}
diff --git a/drivers/video/fbdev/msm/dsi_io_v2.c b/drivers/video/fbdev/msm/dsi_io_v2.c
index dd2e308..28441b6 100644
--- a/drivers/video/fbdev/msm/dsi_io_v2.c
+++ b/drivers/video/fbdev/msm/dsi_io_v2.c
@@ -50,7 +50,7 @@
}
}
-int msm_dsi_io_init(struct platform_device *pdev, struct dss_module_power *mp)
+int msm_dsi_io_init(struct platform_device *pdev, struct mdss_module_power *mp)
{
int rc;
@@ -67,7 +67,7 @@
return rc;
}
- rc = msm_dss_config_vreg(&pdev->dev, mp->vreg_config,
+ rc = msm_mdss_config_vreg(&pdev->dev, mp->vreg_config,
mp->num_vreg, 1);
if (rc) {
pr_err("fail to initialize DSI regulator\n");
@@ -78,11 +78,11 @@
}
void msm_dsi_io_deinit(struct platform_device *pdev,
- struct dss_module_power *mp)
+ struct mdss_module_power *mp)
{
if (dsi_io_private) {
msm_dsi_clk_deinit();
- msm_dss_config_vreg(&pdev->dev, mp->vreg_config,
+ msm_mdss_config_vreg(&pdev->dev, mp->vreg_config,
mp->num_vreg, 0);
kfree(dsi_io_private);
dsi_io_private = NULL;
diff --git a/drivers/video/fbdev/msm/dsi_io_v2.h b/drivers/video/fbdev/msm/dsi_io_v2.h
index dd9adf9..d0227ec 100644
--- a/drivers/video/fbdev/msm/dsi_io_v2.h
+++ b/drivers/video/fbdev/msm/dsi_io_v2.h
@@ -18,10 +18,10 @@
void msm_dsi_ahb_ctrl(int enable);
int msm_dsi_io_init(struct platform_device *dev,
- struct dss_module_power *mp);
+ struct mdss_module_power *mp);
void msm_dsi_io_deinit(struct platform_device *dev,
- struct dss_module_power *mp);
+ struct mdss_module_power *mp);
int msm_dsi_clk_init(struct platform_device *dev);
diff --git a/drivers/video/fbdev/msm/dsi_v2.c b/drivers/video/fbdev/msm/dsi_v2.c
index 92d512a..74c0726 100644
--- a/drivers/video/fbdev/msm/dsi_v2.c
+++ b/drivers/video/fbdev/msm/dsi_v2.c
@@ -237,7 +237,7 @@
}
static void mdss_dsi_put_dt_vreg_data(struct device *dev,
- struct dss_module_power *module_power)
+ struct mdss_module_power *module_power)
{
if (!module_power) {
pr_err("%s: invalid input\n", __func__);
@@ -252,7 +252,7 @@
}
static int mdss_dsi_get_dt_vreg_data(struct device *dev,
- struct dss_module_power *mp, enum dsi_pm_type module)
+ struct mdss_module_power *mp, enum dsi_pm_type module)
{
int i = 0, rc = 0;
u32 tmp = 0;
@@ -287,7 +287,7 @@
pr_debug("%s: vreg found. count=%d\n", __func__, mp->num_vreg);
}
- mp->vreg_config = devm_kzalloc(dev, sizeof(struct dss_vreg) *
+ mp->vreg_config = devm_kzalloc(dev, sizeof(struct mdss_vreg) *
mp->num_vreg, GFP_KERNEL);
if (!mp->vreg_config) {
rc = -ENOMEM;
diff --git a/drivers/video/fbdev/msm/mdp3.c b/drivers/video/fbdev/msm/mdp3.c
index 308af51..5cf439c 100644
--- a/drivers/video/fbdev/msm/mdp3.c
+++ b/drivers/video/fbdev/msm/mdp3.c
@@ -45,7 +45,6 @@
#include <linux/msm-bus.h>
#include <linux/msm-bus-board.h>
#include <linux/qcom_iommu.h>
-#include <linux/msm_iommu_domains.h>
#include <linux/msm_dma_iommu_mapping.h>
diff --git a/drivers/video/fbdev/msm/mdp3.h b/drivers/video/fbdev/msm/mdp3.h
index 3f0d979..6fb39a7 100644
--- a/drivers/video/fbdev/msm/mdp3.h
+++ b/drivers/video/fbdev/msm/mdp3.h
@@ -19,8 +19,6 @@
#include <linux/platform_device.h>
#include <linux/io.h>
-#include <linux/msm_iommu_domains.h>
-
#include "mdss_dsi_clk.h"
#include "mdp3_dma.h"
#include "mdss_fb.h"
@@ -111,7 +109,6 @@
struct mdp3_iommu_domain_map {
u32 domain_type;
char *client_name;
- struct msm_iova_partition partitions[1];
int npartitions;
int domain_idx;
struct iommu_domain *domain;
diff --git a/drivers/video/fbdev/msm/mdp3_ctrl.c b/drivers/video/fbdev/msm/mdp3_ctrl.c
index 889c302..17dadf4 100644
--- a/drivers/video/fbdev/msm/mdp3_ctrl.c
+++ b/drivers/video/fbdev/msm/mdp3_ctrl.c
@@ -271,7 +271,7 @@
struct mdp3_session_data *session = (struct mdp3_session_data *)arg;
atomic_inc(&session->dma_done_cnt);
- queue_kthread_work(&session->worker, &session->dma_done_work);
+ kthread_queue_work(&session->worker, &session->dma_done_work);
complete_all(&session->dma_completion);
}
diff --git a/drivers/video/fbdev/msm/mdp3_ppp.c b/drivers/video/fbdev/msm/mdp3_ppp.c
index 7964cf0..3b72b2d 100644
--- a/drivers/video/fbdev/msm/mdp3_ppp.c
+++ b/drivers/video/fbdev/msm/mdp3_ppp.c
@@ -1661,7 +1661,7 @@
mdp3_ppp_req_push(req_q, req);
mutex_unlock(&ppp_stat->req_mutex);
- queue_kthread_work(&ppp_stat->kworker, &ppp_stat->blit_work);
+ kthread_queue_work(&ppp_stat->kworker, &ppp_stat->blit_work);
if (!async) {
/* wait for release fence */
rc = sync_fence_wait(fence,
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h
index 5d9e612..17bad06 100644
--- a/drivers/video/fbdev/msm/mdss.h
+++ b/drivers/video/fbdev/msm/mdss.h
@@ -209,7 +209,7 @@
struct mdss_smmu_client {
struct device *dev;
struct dma_iommu_mapping *mmu_mapping;
- struct dss_module_power mp;
+ struct mdss_module_power mp;
struct reg_bus_client *reg_bus_clt;
bool domain_attached;
bool handoff_pending;
@@ -283,9 +283,9 @@
struct mdss_panel_data *pdata;
struct platform_device *pdev;
- struct dss_io_data mdss_io;
- struct dss_io_data vbif_io;
- struct dss_io_data vbif_nrt_io;
+ struct mdss_io_data mdss_io;
+ struct mdss_io_data vbif_io;
+ struct mdss_io_data vbif_nrt_io;
char __iomem *mdp_base;
struct mdss_smmu_client mdss_smmu[MDSS_IOMMU_MAX_DOMAIN];
@@ -590,14 +590,14 @@
}
#define MDSS_VBIF_WRITE(mdata, offset, value, nrt_vbif) \
- (nrt_vbif ? dss_reg_w(&mdata->vbif_nrt_io, offset, value, 0) :\
- dss_reg_w(&mdata->vbif_io, offset, value, 0))
+ (nrt_vbif ? mdss_reg_w(&mdata->vbif_nrt_io, offset, value, 0) :\
+ mdss_reg_w(&mdata->vbif_io, offset, value, 0))
#define MDSS_VBIF_READ(mdata, offset, nrt_vbif) \
- (nrt_vbif ? dss_reg_r(&mdata->vbif_nrt_io, offset, 0) :\
- dss_reg_r(&mdata->vbif_io, offset, 0))
+ (nrt_vbif ? mdss_reg_r(&mdata->vbif_nrt_io, offset, 0) :\
+ mdss_reg_r(&mdata->vbif_io, offset, 0))
#define MDSS_REG_WRITE(mdata, offset, value) \
- dss_reg_w(&mdata->mdss_io, offset, value, 0)
+ mdss_reg_w(&mdata->mdss_io, offset, value, 0)
#define MDSS_REG_READ(mdata, offset) \
- dss_reg_r(&mdata->mdss_io, offset, 0)
+ mdss_reg_r(&mdata->mdss_io, offset, 0)
#endif /* MDSS_H */
diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c
index 06da395..9a9f5e4 100644
--- a/drivers/video/fbdev/msm/mdss_compat_utils.c
+++ b/drivers/video/fbdev/msm/mdss_compat_utils.c
@@ -4307,7 +4307,7 @@
break;
}
- if (ret == -ENOTSUP)
+ if (ret == -ENOTSUPP)
pr_err("%s: unsupported ioctl\n", __func__);
else if (ret)
pr_debug("%s: ioctl err cmd=%u ret=%d\n", __func__, cmd, ret);
diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.h b/drivers/video/fbdev/msm/mdss_compat_utils.h
index 9dcf6d4..ebae393 100644
--- a/drivers/video/fbdev/msm/mdss_compat_utils.h
+++ b/drivers/video/fbdev/msm/mdss_compat_utils.h
@@ -18,11 +18,13 @@
/*
* To allow proper structure padding for 64bit/32bit target
*/
+#ifndef MDP_LAYER_COMMIT_V1_PAD
#ifdef __LP64
#define MDP_LAYER_COMMIT_V1_PAD 2
#else
#define MDP_LAYER_COMMIT_V1_PAD 3
#endif
+#endif
struct mdp_buf_sync32 {
u32 flags;
diff --git a/drivers/video/fbdev/msm/mdss_dba_utils.c b/drivers/video/fbdev/msm/mdss_dba_utils.c
index 9edf2a8..2758a5a 100644
--- a/drivers/video/fbdev/msm/mdss_dba_utils.c
+++ b/drivers/video/fbdev/msm/mdss_dba_utils.c
@@ -14,7 +14,7 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <video/msm_dba.h>
-#include <linux/switch.h>
+#include <linux/extcon.h>
#include "mdss_dba_utils.h"
#include "mdss_hdmi_edid.h"
@@ -32,8 +32,8 @@
bool hpd_state;
bool audio_switch_registered;
bool display_switch_registered;
- struct switch_dev sdev_display;
- struct switch_dev sdev_audio;
+ struct extcon_dev sdev_display;
+ struct extcon_dev sdev_audio;
struct kobject *kobj;
struct mdss_panel_info *pinfo;
void *dba_data;
@@ -103,7 +103,7 @@
state = udata->sdev_display.state;
- switch_set_state(&udata->sdev_display, val);
+ extcon_set_state_sync(&udata->sdev_display, 0, val);
pr_debug("cable state %s %d\n",
udata->sdev_display.state == state ?
@@ -128,7 +128,7 @@
state = udata->sdev_audio.state;
- switch_set_state(&udata->sdev_audio, val);
+ extcon_set_state_sync(&udata->sdev_audio, 0, val);
pr_debug("audio state %s %d\n",
udata->sdev_audio.state == state ?
@@ -485,7 +485,7 @@
/* create switch device to update display modules */
udata->sdev_display.name = "hdmi";
- rc = switch_dev_register(&udata->sdev_display);
+ rc = extcon_dev_register(&udata->sdev_display);
if (rc) {
pr_err("display switch registration failed\n");
goto end;
@@ -495,7 +495,7 @@
/* create switch device to update audio modules */
udata->sdev_audio.name = "hdmi_audio";
- ret = switch_dev_register(&udata->sdev_audio);
+ ret = extcon_dev_register(&udata->sdev_audio);
if (ret) {
pr_err("audio switch registration failed\n");
goto end;
@@ -895,10 +895,10 @@
}
if (udata->audio_switch_registered)
- switch_dev_unregister(&udata->sdev_audio);
+ extcon_dev_unregister(&udata->sdev_audio);
if (udata->display_switch_registered)
- switch_dev_unregister(&udata->sdev_display);
+ extcon_dev_unregister(&udata->sdev_display);
if (udata->kobj)
mdss_dba_utils_sysfs_remove(udata->kobj);
diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c
index 19335772..f38d40c 100644
--- a/drivers/video/fbdev/msm/mdss_debug.c
+++ b/drivers/video/fbdev/msm/mdss_debug.c
@@ -886,7 +886,7 @@
buf[count] = 0; /* end of string */
- if (kstrtoint(buf, "%d", &perf_mode) != 1)
+ if (kstrtoint(buf, 10, &perf_mode) != 1)
return -EFAULT;
if (perf_mode) {
@@ -1023,7 +1023,7 @@
buf[count] = 0; /* end of string */
- if (kstrtoint(buf, "%d", &disable_panic) != 1)
+ if (kstrtoint(buf, 10, &disable_panic) != 1)
return -EFAULT;
if (disable_panic) {
@@ -1169,10 +1169,10 @@
(struct mdss_data_type *)mdata, &mdss_perf_panic_enable);
debugfs_create_bool("enable_bw_release", 0644, mdd->perf,
- (u32 *)&mdata->enable_bw_release);
+ (bool *)&mdata->enable_bw_release);
debugfs_create_bool("enable_rotator_bw_release", 0644, mdd->perf,
- (u32 *)&mdata->enable_rotator_bw_release);
+ (bool *)&mdata->enable_rotator_bw_release);
debugfs_create_file("ab_factor", 0644, mdd->perf,
&mdata->ab_factor, &mdss_factor_fops);
@@ -1685,7 +1685,7 @@
resp->crc_op_mode = map->crc_op_mode;
break;
default:
- ret = -ENOTSUP;
+ ret = -ENOTSUPP;
break;
}
diff --git a/drivers/video/fbdev/msm/mdss_debug.h b/drivers/video/fbdev/msm/mdss_debug.h
index 01d300e..0d482c0 100644
--- a/drivers/video/fbdev/msm/mdss_debug.h
+++ b/drivers/video/fbdev/msm/mdss_debug.h
@@ -78,8 +78,8 @@
#define MDSS_XLOG_IOMMU(...) mdss_xlog(__func__, __LINE__, MDSS_XLOG_IOMMU, \
##__VA_ARGS__, DATA_LIMITER)
-#define ATRACE_END(name) trace_tracing_mark_write(current->tgid, name, 0)
-#define ATRACE_BEGIN(name) trace_tracing_mark_write(current->tgid, name, 1)
+#define ATRACE_END(name) trace_mdss_mark_write(current->tgid, name, 0)
+#define ATRACE_BEGIN(name) trace_mdss_mark_write(current->tgid, name, 1)
#define ATRACE_FUNC() ATRACE_BEGIN(__func__)
#define ATRACE_INT(name, value) \
@@ -225,7 +225,7 @@
int mdss_dump_misr_data(char **buf, u32 size);
static inline int mdss_debug_register_io(const char *name,
- struct dss_io_data *io_data, struct mdss_debug_base **dbg_blk)
+ struct mdss_io_data *io_data, struct mdss_debug_base **dbg_blk)
{
return mdss_debug_register_base(name, io_data->base, io_data->len,
dbg_blk);
diff --git a/drivers/video/fbdev/msm/mdss_debug_xlog.c b/drivers/video/fbdev/msm/mdss_debug_xlog.c
index e493dcd..49684f4 100644
--- a/drivers/video/fbdev/msm/mdss_debug_xlog.c
+++ b/drivers/video/fbdev/msm/mdss_debug_xlog.c
@@ -734,7 +734,7 @@
debugfs_create_u32("enable", 0644, mdss_dbg_xlog.xlog,
&mdss_dbg_xlog.xlog_enable);
debugfs_create_bool("panic", 0644, mdss_dbg_xlog.xlog,
- &mdss_dbg_xlog.panic_on_err);
+ (bool *)&mdss_dbg_xlog.panic_on_err);
debugfs_create_u32("reg_dump", 0644, mdss_dbg_xlog.xlog,
&mdss_dbg_xlog.enable_reg_dump);
debugfs_create_u32("dbgbus_dump", 0644, mdss_dbg_xlog.xlog,
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index bc6d568..d70c1e8 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -26,6 +26,7 @@
#include <linux/uaccess.h>
#include <linux/msm-bus.h>
#include <linux/pm_qos.h>
+#include <linux/mdss_io_util.h>
#include "mdss.h"
#include "mdss_panel.h"
@@ -253,14 +254,14 @@
}
for (i = DSI_CORE_PM; !rc && (i < DSI_MAX_PM); i++) {
- rc = msm_dss_config_vreg(&pdev->dev,
+ rc = msm_mdss_config_vreg(&pdev->dev,
sdata->power_data[i].vreg_config,
sdata->power_data[i].num_vreg, 1);
if (rc) {
pr_err("%s: failed to init vregs for %s\n",
__func__, __mdss_dsi_pm_name(i));
for (j = i-1; j >= DSI_CORE_PM; j--) {
- msm_dss_config_vreg(&pdev->dev,
+ msm_mdss_config_vreg(&pdev->dev,
sdata->power_data[j].vreg_config,
sdata->power_data[j].num_vreg, 0);
}
@@ -293,7 +294,7 @@
if (mdss_dsi_pinctrl_set_state(ctrl_pdata, false))
pr_debug("reset disable: pinctrl not enabled\n");
- ret = msm_dss_enable_vreg(
+ ret = msm_mdss_enable_vreg(
ctrl_pdata->panel_power_data.vreg_config,
ctrl_pdata->panel_power_data.num_vreg, 0);
if (ret)
@@ -317,7 +318,7 @@
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
- ret = msm_dss_enable_vreg(
+ ret = msm_mdss_enable_vreg(
ctrl_pdata->panel_power_data.vreg_config,
ctrl_pdata->panel_power_data.num_vreg, 1);
if (ret) {
@@ -378,11 +379,11 @@
if (i == DSI_CORE_PM)
continue;
if (i == DSI_PANEL_PM)
- ret = msm_dss_config_vreg_opt_mode(
+ ret = msm_mdss_config_vreg_opt_mode(
ctrl_pdata->panel_power_data.vreg_config,
ctrl_pdata->panel_power_data.num_vreg, mode);
else
- ret = msm_dss_config_vreg_opt_mode(
+ ret = msm_mdss_config_vreg_opt_mode(
sdata->power_data[i].vreg_config,
sdata->power_data[i].num_vreg, mode);
if (ret) {
@@ -395,7 +396,7 @@
if (ret) {
mode = enable ? DSS_REG_MODE_ENABLE : DSS_REG_MODE_ULP;
for (; i >= 0; i--)
- msm_dss_config_vreg_opt_mode(
+ msm_mdss_config_vreg_opt_mode(
ctrl_pdata->power_data[i].vreg_config,
ctrl_pdata->power_data[i].num_vreg, mode);
}
@@ -405,7 +406,7 @@
int mdss_dsi_panel_power_ctrl(struct mdss_panel_data *pdata,
int power_state)
{
- int ret;
+ int ret = 0;
struct mdss_panel_info *pinfo;
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
@@ -478,7 +479,7 @@
}
static void mdss_dsi_put_dt_vreg_data(struct device *dev,
- struct dss_module_power *module_power)
+ struct mdss_module_power *module_power)
{
if (!module_power) {
pr_err("%s: invalid input\n", __func__);
@@ -493,7 +494,7 @@
}
static int mdss_dsi_get_dt_vreg_data(struct device *dev,
- struct device_node *of_node, struct dss_module_power *mp,
+ struct device_node *of_node, struct mdss_module_power *mp,
enum dsi_pm_type module)
{
int i = 0, rc = 0;
@@ -535,7 +536,7 @@
pr_debug("%s: vreg found. count=%d\n", __func__, mp->num_vreg);
}
- mp->vreg_config = devm_kzalloc(dev, sizeof(struct dss_vreg) *
+ mp->vreg_config = devm_kzalloc(dev, sizeof(struct mdss_vreg) *
mp->num_vreg, GFP_KERNEL);
if (!mp->vreg_config) {
rc = -ENOMEM;
@@ -1033,9 +1034,9 @@
&dfs->override_flag);
debugfs_create_bool("cmd_sync_wait_broadcast", 0644, dfs->root,
- (u32 *)&dfs_ctrl->cmd_sync_wait_broadcast);
+ &dfs_ctrl->cmd_sync_wait_broadcast);
debugfs_create_bool("cmd_sync_wait_trigger", 0644, dfs->root,
- (u32 *)&dfs_ctrl->cmd_sync_wait_trigger);
+ &dfs_ctrl->cmd_sync_wait_trigger);
debugfs_create_file("dsi_on_cmd_state", 0644, dfs->root,
&dfs_ctrl->on_cmds.link_state, &mdss_dsi_cmd_state_fop);
@@ -2028,7 +2029,7 @@
{
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
struct mdss_dsi_ctrl_pdata *sctrl_pdata = NULL;
- struct mdss_panel_info *pinfo, *spinfo;
+ struct mdss_panel_info *pinfo, *spinfo = NULL;
int rc = 0;
if (pdata == NULL) {
@@ -3452,7 +3453,7 @@
goto res_release;
for (i = (DSI_MAX_PM - 1); i >= DSI_CORE_PM; i--) {
- if (msm_dss_config_vreg(&pdev->dev,
+ if (msm_mdss_config_vreg(&pdev->dev,
sdata->power_data[i].vreg_config,
sdata->power_data[i].num_vreg, 1) < 0)
pr_err("%s: failed to de-init vregs for %s\n",
@@ -3748,12 +3749,6 @@
return -EPROBE_DEFER;
}
- if (util->display_disabled) {
- pr_info("%s: Display is disabled, not progressing with dsi probe\n",
- __func__);
- return -ENOTSUPP;
- }
-
if (!pdev || !pdev->dev.of_node) {
pr_err("%s: DSI driver only supports device tree probe\n",
__func__);
@@ -3824,7 +3819,7 @@
mdss_dsi_pm_qos_remove_request(ctrl_pdata->shared_data);
- if (msm_dss_config_vreg(&pdev->dev,
+ if (msm_mdss_config_vreg(&pdev->dev,
ctrl_pdata->panel_power_data.vreg_config,
ctrl_pdata->panel_power_data.num_vreg, 1) < 0)
pr_err("%s: failed to de-init vregs for %s\n",
@@ -3832,9 +3827,9 @@
mdss_dsi_put_dt_vreg_data(&pdev->dev, &ctrl_pdata->panel_power_data);
mfd = platform_get_drvdata(pdev);
- msm_dss_iounmap(&ctrl_pdata->mmss_misc_io);
- msm_dss_iounmap(&ctrl_pdata->phy_io);
- msm_dss_iounmap(&ctrl_pdata->ctrl_io);
+ msm_mdss_iounmap(&ctrl_pdata->mmss_misc_io);
+ msm_mdss_iounmap(&ctrl_pdata->phy_io);
+ msm_mdss_iounmap(&ctrl_pdata->ctrl_io);
mdss_dsi_debugfs_cleanup(ctrl_pdata);
if (ctrl_pdata->workq)
@@ -3877,7 +3872,7 @@
return -EPERM;
}
- rc = msm_dss_ioremap_byname(pdev, &ctrl->ctrl_io, "dsi_ctrl");
+ rc = msm_mdss_ioremap_byname(pdev, &ctrl->ctrl_io, "dsi_ctrl");
if (rc) {
pr_err("%s:%d unable to remap dsi ctrl resources\n",
__func__, __LINE__);
@@ -3887,14 +3882,14 @@
ctrl->ctrl_base = ctrl->ctrl_io.base;
ctrl->reg_size = ctrl->ctrl_io.len;
- rc = msm_dss_ioremap_byname(pdev, &ctrl->phy_io, "dsi_phy");
+ rc = msm_mdss_ioremap_byname(pdev, &ctrl->phy_io, "dsi_phy");
if (rc) {
pr_err("%s:%d unable to remap dsi phy resources\n",
__func__, __LINE__);
return rc;
}
- rc = msm_dss_ioremap_byname(pdev, &ctrl->phy_regulator_io,
+ rc = msm_mdss_ioremap_byname(pdev, &ctrl->phy_regulator_io,
"dsi_phy_regulator");
if (rc)
pr_debug("%s:%d unable to remap dsi phy regulator resources\n",
@@ -3908,7 +3903,7 @@
__func__, ctrl->ctrl_base, ctrl->reg_size, ctrl->phy_io.base,
ctrl->phy_io.len);
- rc = msm_dss_ioremap_byname(pdev, &ctrl->mmss_misc_io,
+ rc = msm_mdss_ioremap_byname(pdev, &ctrl->mmss_misc_io,
"mmss_misc_phys");
if (rc) {
pr_debug("%s:%d mmss_misc IO remap failed\n",
@@ -3924,7 +3919,7 @@
int ret;
ret = devm_request_irq(dev, irq_no, mdss_dsi_isr,
- IRQF_DISABLED, "DSI", ctrl);
+ 0, "DSI", ctrl);
if (ret) {
pr_err("msm_dsi_irq_init request_irq() failed!\n");
return ret;
@@ -4181,7 +4176,7 @@
return rc;
}
- rc = msm_dss_config_vreg(&ctrl_pdev->dev,
+ rc = msm_mdss_config_vreg(&ctrl_pdev->dev,
ctrl_pdata->panel_power_data.vreg_config,
ctrl_pdata->panel_power_data.num_vreg, 1);
if (rc) {
@@ -4263,7 +4258,7 @@
sdata = ctrl_pdata->shared_data;
if (pinfo->ulps_suspend_enabled) {
- rc = msm_dss_enable_vreg(
+ rc = msm_mdss_enable_vreg(
sdata->power_data[DSI_PHY_PM].vreg_config,
sdata->power_data[DSI_PHY_PM].num_vreg, 1);
if (rc) {
diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h
index ea38c0d..60bc455 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.h
+++ b/drivers/video/fbdev/msm/mdss_dsi.h
@@ -272,7 +272,7 @@
struct clk *pixel1_parent;
/* DSI core regulators */
- struct dss_module_power power_data[DSI_MAX_PM];
+ struct mdss_module_power power_data[DSI_MAX_PM];
/* Shared mutex for DSI PHY regulator */
struct mutex phy_reg_lock;
@@ -407,10 +407,10 @@
void (*switch_mode)(struct mdss_panel_data *pdata, int mode);
struct mdss_panel_data panel_data;
unsigned char *ctrl_base;
- struct dss_io_data ctrl_io;
- struct dss_io_data mmss_misc_io;
- struct dss_io_data phy_io;
- struct dss_io_data phy_regulator_io;
+ struct mdss_io_data ctrl_io;
+ struct mdss_io_data mmss_misc_io;
+ struct mdss_io_data phy_io;
+ struct mdss_io_data phy_regulator_io;
int reg_size;
u32 flags;
struct clk *byte_clk;
@@ -459,8 +459,8 @@
u32 pclk_rate_bkp;
u32 byte_clk_rate_bkp;
bool refresh_clk_rate; /* flag to recalculate clk_rate */
- struct dss_module_power panel_power_data;
- struct dss_module_power power_data[DSI_MAX_PM]; /* for 8x10 */
+ struct mdss_module_power panel_power_data;
+ struct mdss_module_power power_data[DSI_MAX_PM]; /* for 8x10 */
u32 dsi_irq_mask;
struct mdss_hw *dsi_hw;
struct mdss_intf_recovery *recovery;
diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c
index 14ac3e1..988c7a9 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_host.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_host.c
@@ -1656,7 +1656,7 @@
len = mdss_dsi_cmd_dma_tpg_tx(ctrl, tp);
else
len = mdss_dsi_cmd_dma_tx(ctrl, tp);
- if (IS_ERR_VALUE(len)) {
+ if (IS_ERR_VALUE((unsigned long)len)) {
mdss_dsi_disable_irq(ctrl, DSI_CMD_TERM);
pr_err("%s: failed to call cmd_dma_tx for cmd = 0x%x\n",
__func__, cm->payload[0]);
@@ -1886,7 +1886,7 @@
ret = mdss_dsi_cmd_dma_tpg_tx(ctrl, tp);
else
ret = mdss_dsi_cmd_dma_tx(ctrl, tp);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
mdss_dsi_disable_irq(ctrl, DSI_CMD_TERM);
pr_err("%s: failed to tx max_pkt_size\n",
__func__);
@@ -1924,7 +1924,7 @@
ret = mdss_dsi_cmd_dma_tpg_tx(ctrl, tp);
else
ret = mdss_dsi_cmd_dma_tx(ctrl, tp);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
mdss_dsi_disable_irq(ctrl, DSI_CMD_TERM);
pr_err("%s: failed to tx cmd = 0x%x\n",
__func__, cmds->payload[0]);
@@ -2043,7 +2043,7 @@
if (ctrl->mdss_util->iommu_attached()) {
ret = mdss_smmu_dsi_map_buffer(tp->dmap, domain, ctrl->dma_size,
&(ctrl->dma_addr), tp->start, DMA_TO_DEVICE);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("unable to map dma memory to iommu(%d)\n", ret);
ctrl->mdss_util->iommu_unlock();
return -ENOMEM;
@@ -2116,7 +2116,7 @@
}
}
- if (!IS_ERR_VALUE(ret))
+ if (!IS_ERR_VALUE((unsigned long)ret))
ret = tp->len;
if (mctrl && mctrl->dma_addr) {
@@ -2678,7 +2678,7 @@
if (ctrl->mdss_util->iommu_ctrl) {
rc = ctrl->mdss_util->iommu_ctrl(1);
- if (IS_ERR_VALUE(rc)) {
+ if (IS_ERR_VALUE((unsigned long)rc)) {
pr_err("IOMMU attach failed\n");
mutex_unlock(&ctrl->cmd_mutex);
return rc;
diff --git a/drivers/video/fbdev/msm/mdss_dsi_status.c b/drivers/video/fbdev/msm/mdss_dsi_status.c
index 7b6be11..992d687 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_status.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_status.c
@@ -227,17 +227,6 @@
int __init mdss_dsi_status_init(void)
{
int rc = 0;
- struct mdss_util_intf *util = mdss_get_util_intf();
-
- if (!util) {
- pr_err("%s: Failed to get utility functions\n", __func__);
- return -ENODEV;
- }
-
- if (util->display_disabled) {
- pr_info("Display is disabled, not progressing with dsi_init\n");
- return -ENOTSUPP;
- }
pstatus_data = kzalloc(sizeof(struct dsi_status_data), GFP_KERNEL);
if (!pstatus_data)
diff --git a/drivers/video/fbdev/msm/mdss_edp.c b/drivers/video/fbdev/msm/mdss_edp.c
index 55d4ab3..79eae8b 100644
--- a/drivers/video/fbdev/msm/mdss_edp.c
+++ b/drivers/video/fbdev/msm/mdss_edp.c
@@ -27,12 +27,9 @@
#include <linux/clk.h>
#include <linux/spinlock_types.h>
#include <linux/kthread.h>
-#include <mach/hardware.h>
-#include <mach/dma.h>
#include "mdss.h"
#include "mdss_edp.h"
-#include "mdss_debug.h"
#define RGB_COMPONENTS 3
#define VDDA_MIN_UV 1800000 /* uV units */
diff --git a/drivers/video/fbdev/msm/mdss_edp_aux.c b/drivers/video/fbdev/msm/mdss_edp_aux.c
index dc12f3b..8ba715d 100644
--- a/drivers/video/fbdev/msm/mdss_edp_aux.c
+++ b/drivers/video/fbdev/msm/mdss_edp_aux.c
@@ -28,10 +28,6 @@
#include <linux/of_gpio.h>
#include <linux/clk/msm-clk.h>
-#include <mach/hardware.h>
-#include <mach/gpio.h>
-#include <mach/dma.h>
-
#include "mdss_panel.h"
#include "mdss_edp.h"
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index 64f462f..65b689f 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -43,8 +43,6 @@
#include <linux/uaccess.h>
#include <linux/version.h>
#include <linux/vmalloc.h>
-#include <linux/sync.h>
-#include <linux/sw_sync.h>
#include <linux/file.h>
#include <linux/kthread.h>
#include <linux/dma-buf.h>
@@ -55,6 +53,7 @@
#include "mdss_smmu.h"
#include "mdss_mdp.h"
#include "mdp3_ctrl.h"
+#include "mdss_sync.h"
#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER
#define MDSS_FB_NUM 3
@@ -945,9 +944,6 @@
{
struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
- if (!mfd)
- return;
-
mfd->shutdown_pending = true;
/* wake up threads waiting on idle or kickoff queues */
@@ -1314,12 +1310,20 @@
mfd->mdp_sync_pt_data.fence_name = "mdp-fence";
if (mfd->mdp_sync_pt_data.timeline == NULL) {
- char timeline_name[16];
+ char timeline_name[32];
snprintf(timeline_name, sizeof(timeline_name),
"mdss_fb_%d", mfd->index);
mfd->mdp_sync_pt_data.timeline =
- sw_sync_timeline_create(timeline_name);
+ mdss_create_timeline(timeline_name);
+ if (mfd->mdp_sync_pt_data.timeline == NULL) {
+ pr_err("cannot create release fence time line\n");
+ return -ENOMEM;
+ }
+ snprintf(timeline_name, sizeof(timeline_name),
+ "mdss_fb_%d_retire", mfd->index);
+ mfd->mdp_sync_pt_data.timeline_retire =
+ mdss_create_timeline(timeline_name);
if (mfd->mdp_sync_pt_data.timeline == NULL) {
pr_err("cannot create release fence time line\n");
return -ENOMEM;
@@ -1864,7 +1868,7 @@
/* Start Display thread */
if (mfd->disp_thread == NULL) {
ret = mdss_fb_start_disp_thread(mfd);
- if (IS_ERR_VALUE(ret))
+ if (IS_ERR_VALUE((unsigned long)ret))
return ret;
}
@@ -2290,7 +2294,7 @@
pr_debug("vm_start=%x vm_end=%x vm_page_prot=%ld\n",
(unsigned int)vma->vm_start,
(unsigned int)vma->vm_end,
- (unsigned long int)vma->vm_page_prot);
+ (unsigned long int)vma->vm_page_prot.pgprot);
io_remap_pfn_range(vma, addr, page_to_pfn(page), len,
vma->vm_page_prot);
@@ -2874,7 +2878,7 @@
}
static void __mdss_fb_copy_fence(struct msm_sync_pt_data *sync_pt_data,
- struct sync_fence **fences, u32 *fence_cnt)
+ struct mdss_fence **fences, u32 *fence_cnt)
{
pr_debug("%s: wait for fences\n", sync_pt_data->fence_name);
@@ -2887,12 +2891,12 @@
sync_pt_data->acq_fen_cnt = 0;
if (*fence_cnt)
memcpy(fences, sync_pt_data->acq_fen,
- *fence_cnt * sizeof(struct sync_fence *));
+ *fence_cnt * sizeof(struct mdss_fence *));
mutex_unlock(&sync_pt_data->sync_mutex);
}
static int __mdss_fb_wait_for_fence_sub(struct msm_sync_pt_data *sync_pt_data,
- struct sync_fence **fences, int fence_cnt)
+ struct mdss_fence **fences, int fence_cnt)
{
int i, ret = 0;
unsigned long max_wait = msecs_to_jiffies(WAIT_MAX_FENCE_TIMEOUT);
@@ -2916,7 +2920,7 @@
wait_ms = min_t(long, WAIT_FENCE_FIRST_TIMEOUT,
wait_ms);
- ret = sync_fence_wait(fences[i], wait_ms);
+ ret = mdss_wait_sync_fence(fences[i], wait_ms);
if (ret == -ETIME) {
wait_jf = timeout - jiffies;
@@ -2928,31 +2932,31 @@
wait_ms);
pr_warn("%s: sync_fence_wait timed out! ",
- fences[i]->name);
+ mdss_get_sync_fence_name(fences[i]));
pr_cont("Waiting %ld.%ld more seconds\n",
(wait_ms/MSEC_PER_SEC), (wait_ms%MSEC_PER_SEC));
MDSS_XLOG(sync_pt_data->timeline_value);
MDSS_XLOG_TOUT_HANDLER("mdp");
- ret = sync_fence_wait(fences[i], wait_ms);
+ ret = mdss_wait_sync_fence(fences[i], wait_ms);
if (ret == -ETIME)
break;
}
- sync_fence_put(fences[i]);
+ mdss_put_sync_fence(fences[i]);
}
if (ret < 0) {
pr_err("%s: sync_fence_wait failed! ret = %x\n",
sync_pt_data->fence_name, ret);
for (; i < fence_cnt; i++)
- sync_fence_put(fences[i]);
+ mdss_put_sync_fence(fences[i]);
}
return ret;
}
int mdss_fb_wait_for_fence(struct msm_sync_pt_data *sync_pt_data)
{
- struct sync_fence *fences[MDP_MAX_FENCE_FD];
+ struct mdss_fence *fences[MDP_MAX_FENCE_FD];
int fence_cnt = 0;
__mdss_fb_copy_fence(sync_pt_data, fences, &fence_cnt);
@@ -2977,7 +2981,8 @@
mutex_lock(&sync_pt_data->sync_mutex);
if (atomic_add_unless(&sync_pt_data->commit_cnt, -1, 0) &&
sync_pt_data->timeline) {
- sw_sync_timeline_inc(sync_pt_data->timeline, 1);
+ mdss_inc_timeline(sync_pt_data->timeline, 1);
+ mdss_inc_timeline(sync_pt_data->timeline_retire, 1);
MDSS_XLOG(sync_pt_data->timeline_value);
sync_pt_data->timeline_value++;
@@ -3009,7 +3014,7 @@
if (sync_pt_data->timeline) {
val = sync_pt_data->threshold +
atomic_read(&sync_pt_data->commit_cnt);
- sw_sync_timeline_inc(sync_pt_data->timeline, val);
+ mdss_inc_timeline(sync_pt_data->timeline, val);
sync_pt_data->timeline_value += val;
atomic_set(&sync_pt_data->commit_cnt, 0);
}
@@ -3182,7 +3187,7 @@
if (var->yoffset > (info->var.yres_virtual - info->var.yres))
return -EINVAL;
- ret = mdss_fb_pan_idle(mfd);
+ ret = mdss_fb_wait_for_kickoff(mfd);
if (ret) {
pr_err("wait_for_kick failed. rc=%d\n", ret);
return ret;
@@ -3513,7 +3518,7 @@
if (var->grayscale > 1) {
format = mdss_grayscale_to_mdp_format(var->grayscale);
- if (!IS_ERR_VALUE(format))
+ if (!IS_ERR_VALUE((unsigned long)format))
pinfo->out_format = format;
else
pr_warn("Failed to map grayscale value (%d) to an MDP format\n",
@@ -3593,7 +3598,7 @@
{
struct msm_sync_pt_data *sync_pt_data = &mfd->mdp_sync_pt_data;
struct msm_fb_backup_type *fb_backup = &mfd->msm_fb_backup;
- int ret = -ENOTSUP;
+ int ret = -ENOTSUPP;
u32 new_dsi_mode, dynamic_dsi_switch = 0;
if (!sync_pt_data->async_wait_fences)
@@ -3648,7 +3653,7 @@
if (!ret)
mdss_fb_update_backlight(mfd);
- if (IS_ERR_VALUE(ret) || !sync_pt_data->flushed) {
+ if (IS_ERR_VALUE((unsigned long)ret) || !sync_pt_data->flushed) {
mdss_fb_release_kickoff(mfd);
mdss_fb_signal_timeline(sync_pt_data);
if ((mfd->panel.type == MIPI_CMD_PANEL) &&
@@ -3823,7 +3828,7 @@
mdss_fb_var_to_panelinfo(var, panel_info);
rc = mdss_fb_send_panel_event(mfd, MDSS_EVENT_CHECK_PARAMS,
panel_info);
- if (IS_ERR_VALUE(rc)) {
+ if (IS_ERR_VALUE((unsigned long)rc)) {
kfree(panel_info);
return rc;
}
@@ -3984,7 +3989,7 @@
mfd->fbi->var.yres) * mfd->fb_page;
old_format = mdss_grayscale_to_mdp_format(var->grayscale);
- if (!IS_ERR_VALUE(old_format)) {
+ if (!IS_ERR_VALUE((unsigned long)old_format)) {
if (old_format != mfd->panel_info->out_format)
mfd->panel_reconfig = true;
}
@@ -4186,24 +4191,16 @@
* Function returns a fence on the timeline given with the name provided.
* The fence created will be signaled when the timeline is advanced.
*/
-struct sync_fence *mdss_fb_sync_get_fence(struct sw_sync_timeline *timeline,
+struct mdss_fence *mdss_fb_sync_get_fence(struct mdss_timeline *timeline,
const char *fence_name, int val)
{
- struct sync_pt *sync_pt;
- struct sync_fence *fence;
+ struct mdss_fence *fence;
- pr_debug("%s: buf sync fence timeline=%d\n", fence_name, val);
- sync_pt = sw_sync_pt_create(timeline, val);
- if (sync_pt == NULL) {
- pr_err("%s: cannot create sync point\n", fence_name);
- return NULL;
- }
-
- /* create fence */
- fence = sync_fence_create(fence_name, sync_pt);
+ fence = mdss_get_sync_fence(timeline, fence_name, NULL, val);
+ pr_debug("%s: buf sync fence timeline=%d\n",
+ mdss_get_sync_fence_name(fence), val);
if (fence == NULL) {
- sync_pt_free(sync_pt);
pr_err("%s: cannot create fence\n", fence_name);
return NULL;
}
@@ -4216,7 +4213,7 @@
{
int i, ret = 0;
int acq_fen_fd[MDP_MAX_FENCE_FD];
- struct sync_fence *fence, *rel_fence, *retire_fence;
+ struct mdss_fence *fence, *rel_fence, *retire_fence;
int rel_fen_fd;
int retire_fen_fd;
int val;
@@ -4240,7 +4237,7 @@
mutex_lock(&sync_pt_data->sync_mutex);
for (i = 0; i < buf_sync->acq_fen_fd_cnt; i++) {
- fence = sync_fence_fdget(acq_fen_fd[i]);
+ fence = mdss_get_fd_sync_fence(acq_fen_fd[i]);
if (fence == NULL) {
pr_err("%s: null fence! i=%d fd=%d\n",
sync_pt_data->fence_name, i,
@@ -4254,7 +4251,7 @@
if (ret)
goto buf_sync_err_1;
- val = sync_pt_data->timeline_value + sync_pt_data->threshold +
+ val = sync_pt_data->threshold +
atomic_read(&sync_pt_data->commit_cnt);
MDSS_XLOG(sync_pt_data->timeline_value, val,
@@ -4273,7 +4270,7 @@
}
/* create fd */
- rel_fen_fd = get_unused_fd_flags(0);
+ rel_fen_fd = mdss_get_sync_fence_fd(rel_fence);
if (rel_fen_fd < 0) {
pr_err("%s: get_unused_fd_flags failed error:0x%x\n",
sync_pt_data->fence_name, rel_fen_fd);
@@ -4307,13 +4304,13 @@
ret = retire_fence ? PTR_ERR(rel_fence) : -ENOMEM;
goto buf_sync_err_3;
}
- retire_fen_fd = get_unused_fd_flags(0);
+ retire_fen_fd = mdss_get_sync_fence_fd(retire_fence);
if (retire_fen_fd < 0) {
pr_err("%s: get_unused_fd_flags failed for retire fence error:0x%x\n",
sync_pt_data->fence_name, retire_fen_fd);
ret = retire_fen_fd;
- sync_fence_put(retire_fence);
+ mdss_put_sync_fence(retire_fence);
goto buf_sync_err_3;
}
@@ -4323,14 +4320,12 @@
pr_err("%s: copy_to_user failed for retire fence\n",
sync_pt_data->fence_name);
put_unused_fd(retire_fen_fd);
- sync_fence_put(retire_fence);
+ mdss_put_sync_fence(retire_fence);
goto buf_sync_err_3;
}
- sync_fence_install(retire_fence, retire_fen_fd);
-
skip_retire_fence:
- sync_fence_install(rel_fence, rel_fen_fd);
+ mdss_get_sync_fence_fd(rel_fence);
mutex_unlock(&sync_pt_data->sync_mutex);
if (buf_sync->flags & MDP_BUF_SYNC_FLAG_WAIT)
@@ -4340,10 +4335,10 @@
buf_sync_err_3:
put_unused_fd(rel_fen_fd);
buf_sync_err_2:
- sync_fence_put(rel_fence);
+ mdss_put_sync_fence(rel_fence);
buf_sync_err_1:
for (i = 0; i < sync_pt_data->acq_fen_cnt; i++)
- sync_fence_put(sync_pt_data->acq_fen[i]);
+ mdss_put_sync_fence(sync_pt_data->acq_fen[i]);
sync_pt_data->acq_fen_cnt = 0;
mutex_unlock(&sync_pt_data->sync_mutex);
return ret;
@@ -4796,7 +4791,7 @@
{
struct msm_fb_data_type *mfd;
void __user *argp = (void __user *)arg;
- int ret = -ENOTSUP;
+ int ret = -ENOTSUPP;
struct mdp_buf_sync buf_sync;
unsigned int dsi_mode = 0;
struct mdss_panel_data *pdata = NULL;
@@ -4884,7 +4879,7 @@
break;
}
- if (ret == -ENOTSUP)
+ if (ret == -ENOTSUPP)
pr_err("unsupported ioctl (%x)\n", cmd);
exit:
diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h
index f8ef48a..19e6299 100644
--- a/drivers/video/fbdev/msm/mdss_fb.h
+++ b/drivers/video/fbdev/msm/mdss_fb.h
@@ -171,11 +171,12 @@
struct msm_sync_pt_data {
char *fence_name;
u32 acq_fen_cnt;
- struct sync_fence *acq_fen[MDP_MAX_FENCE_FD];
+ struct mdss_fence *acq_fen[MDP_MAX_FENCE_FD];
u32 temp_fen_cnt;
- struct sync_fence *temp_fen[MDP_MAX_FENCE_FD];
+ struct mdss_fence *temp_fen[MDP_MAX_FENCE_FD];
- struct sw_sync_timeline *timeline;
+ struct mdss_timeline *timeline;
+ struct mdss_timeline *timeline_retire;
int timeline_value;
u32 threshold;
u32 retire_threshold;
@@ -186,7 +187,7 @@
struct mutex sync_mutex;
struct notifier_block notifier;
- struct sync_fence *(*get_retire_fence)
+ struct mdss_fence *(*get_retire_fence)
(struct msm_sync_pt_data *sync_pt_data);
};
@@ -452,7 +453,7 @@
void mdss_fb_update_backlight(struct msm_fb_data_type *mfd);
int mdss_fb_wait_for_fence(struct msm_sync_pt_data *sync_pt_data);
void mdss_fb_signal_timeline(struct msm_sync_pt_data *sync_pt_data);
-struct sync_fence *mdss_fb_sync_get_fence(struct sw_sync_timeline *timeline,
+struct mdss_fence *mdss_fb_sync_get_fence(struct mdss_timeline *timeline,
const char *fence_name, int val);
int mdss_fb_register_mdp_instance(struct msm_mdp_interface *mdp);
int mdss_fb_dcm(struct msm_fb_data_type *mfd, int req_state);
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_audio.c b/drivers/video/fbdev/msm/mdss_hdmi_audio.c
index e4dc530..446e8b4 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_audio.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_audio.c
@@ -19,7 +19,7 @@
#include <linux/mutex.h>
#include <linux/iopoll.h>
#include <linux/types.h>
-#include <linux/switch.h>
+#include <linux/extcon.h>
#include <linux/gcd.h>
#include "mdss_hdmi_audio.h"
@@ -63,9 +63,9 @@
};
struct hdmi_audio {
- struct dss_io_data *io;
+ struct mdss_io_data *io;
struct msm_hdmi_audio_setup_params params;
- struct switch_dev sdev;
+ struct extcon_dev sdev;
u32 pclk;
bool ack_enabled;
bool audio_ack_enabled;
@@ -143,7 +143,7 @@
static void hdmi_audio_acr_enable(struct hdmi_audio *audio)
{
- struct dss_io_data *io;
+ struct mdss_io_data *io;
struct hdmi_audio_acr acr;
struct msm_hdmi_audio_setup_params *params;
u32 pclk, layout, multiplier = 1, sample_rate;
@@ -260,7 +260,7 @@
static void hdmi_audio_infoframe_setup(struct hdmi_audio *audio, bool enabled)
{
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
u32 channels, channel_allocation, level_shift, down_mix, layout;
u32 hdmi_debug_reg = 0, audio_info_0_reg = 0, audio_info_1_reg = 0;
u32 audio_info_ctrl_reg, aud_pck_ctrl_2_reg;
@@ -393,7 +393,7 @@
return;
}
- switch_set_state(&audio->sdev, val);
+ extcon_set_state_sync(&audio->sdev, 0, val);
switched = audio->sdev.state != state;
if (audio->ack_enabled && switched)
@@ -490,7 +490,7 @@
goto end;
audio->sdev.name = "hdmi_audio";
- rc = switch_dev_register(&audio->sdev);
+ rc = extcon_dev_register(&audio->sdev);
if (rc) {
pr_err("audio switch registration failed\n");
kzfree(audio);
@@ -520,7 +520,7 @@
struct hdmi_audio *audio = ctx;
if (audio) {
- switch_dev_unregister(&audio->sdev);
+ extcon_dev_unregister(&audio->sdev);
kfree(ctx);
}
}
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_audio.h b/drivers/video/fbdev/msm/mdss_hdmi_audio.h
index 7b33cb8..2449123 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_audio.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_audio.h
@@ -62,7 +62,7 @@
* Defines the data needed to be provided while initializing audio module
*/
struct hdmi_audio_init_data {
- struct dss_io_data *io;
+ struct mdss_io_data *io;
struct hdmi_audio_ops *ops;
};
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_cec.c b/drivers/video/fbdev/msm/mdss_hdmi_cec.c
index f1be313..f15272e 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_cec.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_cec.c
@@ -52,7 +52,7 @@
u32 frame_retransmit = RETRANSMIT_MAX_NUM;
bool frame_type;
unsigned long flags;
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)data;
if (!cec_ctrl || !cec_ctrl->init_data.io || !msg) {
@@ -169,7 +169,7 @@
int i;
u32 data;
struct hdmi_cec_ctrl *cec_ctrl = NULL;
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
struct cec_msg msg;
struct cec_cbs *cbs;
@@ -262,7 +262,7 @@
int rc = 0;
u32 cec_intr, cec_status;
unsigned long flags;
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)input;
if (!cec_ctrl || !cec_ctrl->init_data.io) {
@@ -368,7 +368,7 @@
{
int ret = 0;
u32 hdmi_hw_version, reg_val;
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
struct hdmi_cec_ctrl *cec_ctrl = (struct hdmi_cec_ctrl *)input;
struct mdss_panel_info *pinfo;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_cec.h b/drivers/video/fbdev/msm/mdss_hdmi_cec.h
index 57a7664..de4bb35 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_cec.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_cec.h
@@ -30,7 +30,7 @@
*/
struct hdmi_cec_init_data {
struct workqueue_struct *workq;
- struct dss_io_data *io;
+ struct mdss_io_data *io;
struct mdss_panel_info *pinfo;
struct cec_cbs *cbs;
struct cec_ops *ops;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
index d6e37a1..ab8491d 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
@@ -738,7 +738,7 @@
}
rc = sscanf(buf,
- "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
+ "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %u",
(unsigned long *) &timing.active_h,
(unsigned long *) &timing.front_porch_h,
(unsigned long *) &timing.pulse_width_h,
@@ -753,7 +753,7 @@
(unsigned long *) &timing.refresh_rate,
(unsigned long *) &timing.interlaced,
(unsigned long *) &timing.supported,
- (unsigned long *) &timing.ar);
+ (unsigned int *) &timing.ar);
if (rc != 15) {
DEV_ERR("%s: error reading buf\n", __func__);
@@ -762,7 +762,7 @@
rc = hdmi_set_resv_timing_info(&timing);
- if (!IS_ERR_VALUE(rc)) {
+ if (!IS_ERR_VALUE((unsigned long)rc)) {
DEV_DBG("%s: added new res %d\n", __func__, rc);
} else {
DEV_ERR("%s: error adding new res %d\n", __func__, rc);
@@ -1499,7 +1499,7 @@
rc = -EINVAL;
}
- if (!IS_ERR_VALUE(rc)) {
+ if (!IS_ERR_VALUE((unsigned long)rc)) {
*disp_mode = rc;
DEV_DBG("%s: DTD mode found: %d\n", __func__, *disp_mode);
} else {
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp.c b/drivers/video/fbdev/msm/mdss_hdmi_hdcp.c
index bbdf485..41c6844 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp.c
@@ -87,7 +87,7 @@
int hdcp_ddc_status;
int failure;
int nack0;
- struct dss_io_data *io;
+ struct mdss_io_data *io;
if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -166,7 +166,7 @@
static void hdmi_hdcp_hw_ddc_clean(struct hdmi_hdcp_ctrl *hdcp_ctrl)
{
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
u32 hdcp_ddc_status, ddc_hw_status;
u32 ddc_xfer_done, ddc_xfer_req;
u32 ddc_hw_req, ddc_hw_not_idle;
@@ -254,8 +254,8 @@
u32 ksv_lsb_addr, ksv_msb_addr;
u32 aksv_lsb, aksv_msb;
u8 aksv[5];
- struct dss_io_data *io;
- struct dss_io_data *qfprom_io;
+ struct mdss_io_data *io;
+ struct mdss_io_data *qfprom_io;
struct hdmi_hdcp_ctrl *hdcp_ctrl = input;
if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io ||
@@ -352,8 +352,8 @@
u32 link0_an_0, link0_an_1;
u32 timeout_count;
bool is_match;
- struct dss_io_data *io;
- struct dss_io_data *hdcp_io;
+ struct mdss_io_data *io;
+ struct mdss_io_data *hdcp_io;
u8 aksv[5], *bksv = NULL;
u8 an[8];
u8 bcaps = 0;
@@ -676,30 +676,34 @@
return rc;
} /* hdmi_hdcp_authentication_part1 */
-static int read_write_v_h(dss_io_data *io, int off. char *name, u32 reg,
- bool wr)
+static int read_write_v_h(struct hdmi_hdcp_ctrl *hdcp_ctrl,
+ struct hdmi_tx_ddc_data ddc_data,
+ struct mdss_io_data *io, int off, char *name,
+ u32 reg, bool wr)
{
- char what[20];
int rc = 0;
do {
ddc_data.offset = off;
- memset(what, 0, sizeof(what));
- snprintf(what, 20, name);
+ memset(ddc_data.what, 0, 20);
+ snprintf(ddc_data.what, 20, name);
hdcp_ctrl->init_data.ddc_ctrl->ddc_data = ddc_data;
rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl);
if (rc) {
DEV_ERR("%s: %s: Read %s failed\n", __func__,
- HDCP_STATE_NAME, what);
+ HDCP_STATE_NAME, ddc_data.what);
return rc;
}
DEV_DBG("%s: %s: %s: buf[0]=%x, [1]=%x,[2]=%x, [3]=%x\n",
- __func__, HDCP_STATE_NAME, what, buf[0], buf[1],
- buf[2], buf[3]);
+ __func__, HDCP_STATE_NAME, ddc_data.what,
+ ddc_data.data_buf[0], ddc_data.data_buf[1],
+ ddc_data.data_buf[2], ddc_data.data_buf[3]);
if (wr) {
DSS_REG_W((io), (reg),
- (buf[3] << 24 | buf[2] << 16 |
- buf[1] << 8 | buf[0]));
+ (ddc_data.data_buf[3] << 24 |
+ ddc_data.data_buf[2] << 16 |
+ ddc_data.data_buf[1] << 8 |
+ ddc_data.data_buf[0]));
}
} while (0);
return rc;
@@ -711,7 +715,7 @@
int rc = 0;
u8 buf[4];
struct hdmi_tx_ddc_data ddc_data;
- struct dss_io_data *io;
+ struct mdss_io_data *io;
struct scm_hdcp_req scm_buf[SCM_HDCP_MAX_REG];
u32 phy_addr;
@@ -728,14 +732,6 @@
u32 ret = 0;
u32 resp = 0;
- if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) {
- DEV_ERR("%s: invalid input\n", __func__);
- return -EINVAL;
- }
-
- phy_addr = hdcp_ctrl->init_data.phy_addr;
-
- io = hdcp_ctrl->init_data.core_io;
memset(&ddc_data, 0, sizeof(ddc_data));
ddc_data.dev_addr = 0x74;
ddc_data.data_buf = buf;
@@ -744,13 +740,23 @@
ddc_data.retry = 5;
ddc_data.what = what;
+ if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ phy_addr = hdcp_ctrl->init_data.phy_addr;
+
+ io = hdcp_ctrl->init_data.core_io;
+
if (hdcp_ctrl->tz_hdcp) {
memset(scm_buf, 0x00, sizeof(scm_buf));
for (iter = 0; iter < size && iter < SCM_HDCP_MAX_REG; iter++) {
struct hdmi_hdcp_reg_data *rd = reg_data + iter;
- if (read_write_v_h(io, rd->off, rd->name, 0, false))
+ if (read_write_v_h(hdcp_ctrl, ddc_data, io, rd->off,
+ rd->name, 0, false))
goto error;
rd->reg_val = buf[3] << 24 | buf[2] << 16 |
@@ -768,56 +774,56 @@
goto error;
}
} else if (hdcp_ctrl->hdmi_tx_ver_4) {
- struct dss_io_data *hdcp_io = hdcp_ctrl->init_data.hdcp_io;
+ struct mdss_io_data *hdcp_io = hdcp_ctrl->init_data.hdcp_io;
/* Read V'.HO 4 Byte at offset 0x20 */
- if (read_write_v_h(hdcp_io, 0x20, "V' H0",
+ if (read_write_v_h(hdcp_ctrl, ddc_data, hdcp_io, 0x20, "V' H0",
HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA7, true))
goto error;
/* Read V'.H1 4 Byte at offset 0x24 */
- if (read_write_v_h(hdcp_io, 0x24, "V' H1",
+ if (read_write_v_h(hdcp_ctrl, ddc_data, hdcp_io, 0x24, "V' H1",
HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA8, true))
goto error;
/* Read V'.H2 4 Byte at offset 0x28 */
- if (read_write_v_h(hdcp_io, 0x28, "V' H2",
+ if (read_write_v_h(hdcp_ctrl, ddc_data, hdcp_io, 0x28, "V' H2",
HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA9, true))
goto error;
/* Read V'.H3 4 Byte at offset 0x2C */
- if (read_write_v_h(hdcp_io, 0x2C, "V' H3",
+ if (read_write_v_h(hdcp_ctrl, ddc_data, hdcp_io, 0x2C, "V' H3",
HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA10, true))
goto error;
/* Read V'.H4 4 Byte at offset 0x30 */
- if (read_write_v_h(hdcp_io, 0x30, "V' H4",
+ if (read_write_v_h(hdcp_ctrl, ddc_data, hdcp_io, 0x30, "V' H4",
HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA11, true))
goto error;
} else {
/* Read V'.HO 4 Byte at offset 0x20 */
- if (read_write_v_h(io, 0x20, "V' H0", HDMI_HDCP_RCVPORT_DATA7,
- true))
+ if (read_write_v_h(hdcp_ctrl, ddc_data, io, 0x20, "V' H0",
+ HDMI_HDCP_RCVPORT_DATA7, true))
goto error;
/* Read V'.H1 4 Byte at offset 0x24 */
- if (read_write_v_h(io, 0x24, "V' H1", HDMI_HDCP_RCVPORT_DATA8,
- true))
+ if (read_write_v_h(hdcp_ctrl, ddc_data, io, 0x24, "V' H1",
+ HDMI_HDCP_RCVPORT_DATA8, true))
goto error;
/* Read V'.H2 4 Byte at offset 0x28 */
- if (read_write_v_h(io, 0x28, "V' H2", HDMI_HDCP_RCVPORT_DATA9,
- true))
+ if (read_write_v_h(hdcp_ctrl, ddc_data, io, 0x28, "V' H2",
+ HDMI_HDCP_RCVPORT_DATA9, true))
goto error;
/* Read V'.H3 4 Byte at offset 0x2C */
- if (read_write_v_h(io, 0x2C, "V' H3", HDMI_HDCP_RCVPORT_DATA10,
- true))
+ if (read_write_v_h(hdcp_ctrl, ddc_data, io, 0x2C, "V' H3",
+ HDMI_HDCP_RCVPORT_DATA10, true))
goto error;
/* Read V'.H4 4 Byte at offset 0x30 */
- if (read_write_v_h(io, 0x30, "V' H4", HDMI_HDCP_RCVPORT_DATA11,
- true))
+ if (read_write_v_h(hdcp_ctrl, ddc_data, io, 0x30, "V' H4",
+ HDMI_HDCP_RCVPORT_DATA11, true))
goto error;
}
@@ -837,7 +843,7 @@
u16 bstatus, max_devs_exceeded = 0, max_cascade_exceeded = 0;
u32 link0_status;
u32 ksv_bytes;
- struct dss_io_data *io;
+ struct mdss_io_data *io;
struct scm_hdcp_req scm_buf[SCM_HDCP_MAX_REG];
u32 phy_addr;
@@ -1241,7 +1247,7 @@
struct delayed_work *dw = to_delayed_work(work);
struct hdmi_hdcp_ctrl *hdcp_ctrl = container_of(dw,
struct hdmi_hdcp_ctrl, hdcp_auth_work);
- struct dss_io_data *io;
+ struct mdss_io_data *io;
if (!hdcp_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -1347,7 +1353,7 @@
int hdmi_hdcp_reauthenticate(void *input)
{
struct hdmi_hdcp_ctrl *hdcp_ctrl = (struct hdmi_hdcp_ctrl *)input;
- struct dss_io_data *io;
+ struct mdss_io_data *io;
u32 hdmi_hw_version;
u32 ret = 0;
@@ -1394,7 +1400,7 @@
void hdmi_hdcp_off(void *input)
{
struct hdmi_hdcp_ctrl *hdcp_ctrl = (struct hdmi_hdcp_ctrl *)input;
- struct dss_io_data *io;
+ struct mdss_io_data *io;
int rc = 0;
if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) {
@@ -1447,7 +1453,7 @@
{
struct hdmi_hdcp_ctrl *hdcp_ctrl = (struct hdmi_hdcp_ctrl *)input;
int rc = 0;
- struct dss_io_data *io;
+ struct mdss_io_data *io;
u32 hdcp_int_val;
if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) {
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h b/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h
index 2098943..2276009 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h
@@ -28,9 +28,9 @@
};
struct hdmi_hdcp_init_data {
- struct dss_io_data *core_io;
- struct dss_io_data *qfprom_io;
- struct dss_io_data *hdcp_io;
+ struct mdss_io_data *core_io;
+ struct mdss_io_data *qfprom_io;
+ struct mdss_io_data *hdcp_io;
struct mutex *mutex;
struct kobject *sysfs_kobj;
struct workqueue_struct *workq;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c
index fc0c878..8dce151 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c
@@ -64,7 +64,7 @@
void *lib_ctx; /* Handle to HDCP 2.2 Trustzone library */
struct hdcp_txmtr_ops *lib; /* Ops for driver to call into TZ */
- enum hdmi_hdcp_wakeup_cmd wakeup_cmd;
+ enum hdcp_wakeup_cmd wakeup_cmd;
enum hdmi_auth_status auth_status;
char *send_msg_buf;
uint32_t send_msg_len;
@@ -89,7 +89,7 @@
static inline bool hdmi_hdcp2p2_is_valid_state(struct hdmi_hdcp2p2_ctrl *ctrl)
{
- if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_AUTHENTICATE)
+ if (ctrl->wakeup_cmd == HDCP_WKUP_CMD_AUTHENTICATE)
return true;
if (atomic_read(&ctrl->auth_state) != HDCP_STATE_INACTIVE)
@@ -99,7 +99,7 @@
}
static int hdmi_hdcp2p2_copy_buf(struct hdmi_hdcp2p2_ctrl *ctrl,
- struct hdmi_hdcp_wakeup_data *data)
+ struct hdcp_wakeup_data *data)
{
mutex_lock(&ctrl->msg_lock);
@@ -126,7 +126,7 @@
return 0;
}
-static int hdmi_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data)
+static int hdmi_hdcp2p2_wakeup(struct hdcp_wakeup_data *data)
{
struct hdmi_hdcp2p2_ctrl *ctrl;
@@ -144,7 +144,7 @@
mutex_lock(&ctrl->wakeup_mutex);
pr_debug("cmd: %s, timeout %dms, tethered %d\n",
- hdmi_hdcp_cmd_to_str(data->cmd),
+ hdcp_cmd_to_str(data->cmd),
data->timeout, ctrl->tethered);
ctrl->wakeup_cmd = data->cmd;
@@ -162,30 +162,30 @@
if (hdmi_hdcp2p2_copy_buf(ctrl, data))
goto exit;
- if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS)
+ if (ctrl->wakeup_cmd == HDCP_WKUP_CMD_STATUS_SUCCESS)
ctrl->auth_status = HDMI_HDCP_AUTH_STATUS_SUCCESS;
- else if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_FAILED)
+ else if (ctrl->wakeup_cmd == HDCP_WKUP_CMD_STATUS_FAILED)
ctrl->auth_status = HDMI_HDCP_AUTH_STATUS_FAILURE;
if (ctrl->tethered)
goto exit;
switch (ctrl->wakeup_cmd) {
- case HDMI_HDCP_WKUP_CMD_SEND_MESSAGE:
- queue_kthread_work(&ctrl->worker, &ctrl->send_msg);
+ case HDCP_WKUP_CMD_SEND_MESSAGE:
+ kthread_queue_work(&ctrl->worker, &ctrl->send_msg);
break;
- case HDMI_HDCP_WKUP_CMD_RECV_MESSAGE:
- queue_kthread_work(&ctrl->worker, &ctrl->recv_msg);
+ case HDCP_WKUP_CMD_RECV_MESSAGE:
+ kthread_queue_work(&ctrl->worker, &ctrl->recv_msg);
break;
- case HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS:
- case HDMI_HDCP_WKUP_CMD_STATUS_FAILED:
- queue_kthread_work(&ctrl->worker, &ctrl->status);
+ case HDCP_WKUP_CMD_STATUS_SUCCESS:
+ case HDCP_WKUP_CMD_STATUS_FAILED:
+ kthread_queue_work(&ctrl->worker, &ctrl->status);
break;
- case HDMI_HDCP_WKUP_CMD_LINK_POLL:
- queue_kthread_work(&ctrl->worker, &ctrl->poll);
+ case HDCP_WKUP_CMD_LINK_POLL:
+ kthread_queue_work(&ctrl->worker, &ctrl->poll);
break;
- case HDMI_HDCP_WKUP_CMD_AUTHENTICATE:
- queue_kthread_work(&ctrl->worker, &ctrl->auth);
+ case HDCP_WKUP_CMD_AUTHENTICATE:
+ kthread_queue_work(&ctrl->worker, &ctrl->auth);
break;
default:
pr_err("invalid wakeup command %d\n", ctrl->wakeup_cmd);
@@ -220,19 +220,19 @@
while (1) {
switch (ctrl->wakeup_cmd) {
- case HDMI_HDCP_WKUP_CMD_SEND_MESSAGE:
- ctrl->wakeup_cmd = HDMI_HDCP_WKUP_CMD_INVALID;
+ case HDCP_WKUP_CMD_SEND_MESSAGE:
+ ctrl->wakeup_cmd = HDCP_WKUP_CMD_INVALID;
hdmi_hdcp2p2_send_msg(ctrl);
break;
- case HDMI_HDCP_WKUP_CMD_RECV_MESSAGE:
- ctrl->wakeup_cmd = HDMI_HDCP_WKUP_CMD_INVALID;
+ case HDCP_WKUP_CMD_RECV_MESSAGE:
+ ctrl->wakeup_cmd = HDCP_WKUP_CMD_INVALID;
hdmi_hdcp2p2_recv_msg(ctrl);
break;
- case HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS:
- case HDMI_HDCP_WKUP_CMD_STATUS_FAILED:
+ case HDCP_WKUP_CMD_STATUS_SUCCESS:
+ case HDCP_WKUP_CMD_STATUS_FAILED:
hdmi_hdcp2p2_auth_status(ctrl);
goto exit;
- case HDMI_HDCP_WKUP_CMD_LINK_POLL:
+ case HDCP_WKUP_CMD_LINK_POLL:
hdmi_hdcp2p2_link_check(ctrl);
goto exit;
default:
@@ -240,7 +240,7 @@
}
}
exit:
- ctrl->wakeup_cmd = HDMI_HDCP_WKUP_CMD_INVALID;
+ ctrl->wakeup_cmd = HDCP_WKUP_CMD_INVALID;
}
int hdmi_hdcp2p2_authenticate_tethered(struct hdmi_hdcp2p2_ctrl *ctrl)
@@ -278,7 +278,7 @@
static void hdmi_hdcp2p2_off(void *input)
{
struct hdmi_hdcp2p2_ctrl *ctrl = (struct hdmi_hdcp2p2_ctrl *)input;
- struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_AUTHENTICATE};
+ struct hdcp_wakeup_data cdata = {HDCP_WKUP_CMD_AUTHENTICATE};
if (!ctrl) {
pr_err("invalid input\n");
@@ -287,7 +287,7 @@
hdmi_hdcp2p2_reset(ctrl);
- flush_kthread_worker(&ctrl->worker);
+ kthread_flush_worker(&ctrl->worker);
hdmi_hdcp2p2_ddc_disable(ctrl->init_data.ddc_ctrl);
@@ -302,7 +302,7 @@
static int hdmi_hdcp2p2_authenticate(void *input)
{
struct hdmi_hdcp2p2_ctrl *ctrl = input;
- struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_AUTHENTICATE};
+ struct hdcp_wakeup_data cdata = {HDCP_WKUP_CMD_AUTHENTICATE};
u32 regval;
int rc = 0;
@@ -312,7 +312,7 @@
DSS_REG_W(ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2, regval);
- flush_kthread_worker(&ctrl->worker);
+ kthread_flush_worker(&ctrl->worker);
ctrl->sink_status = SINK_CONNECTED;
atomic_set(&ctrl->auth_state, HDCP_STATE_AUTHENTICATING);
@@ -385,8 +385,8 @@
ctrl->tethered = !!tethered;
- if (ctrl->lib && ctrl->lib->update_exec_type && ctrl->lib_ctx)
- ctrl->lib->update_exec_type(ctrl->lib_ctx, ctrl->tethered);
+ //if (ctrl->lib && ctrl->lib->update_exec_type && ctrl->lib_ctx)
+ // ctrl->lib->update_exec_type(ctrl->lib_ctx, ctrl->tethered);
exit:
mutex_unlock(&ctrl->mutex);
@@ -661,7 +661,7 @@
}
if (atomic_read(&ctrl->auth_state) != HDCP_STATE_INACTIVE)
- queue_kthread_work(&ctrl->worker, &ctrl->link);
+ kthread_queue_work(&ctrl->worker, &ctrl->link);
}
static void hdmi_hdcp2p2_recv_msg(struct hdmi_hdcp2p2_ctrl *ctrl)
@@ -1042,7 +1042,7 @@
register_data.client_ops = &client_ops;
register_data.txmtr_ops = &txmtr_ops;
register_data.client_ctx = ctrl;
- register_data.tethered = ctrl->tethered;
+ //register_data.tethered = ctrl->tethered;
rc = hdcp_library_register(®ister_data);
if (rc) {
@@ -1050,14 +1050,14 @@
goto error;
}
- init_kthread_worker(&ctrl->worker);
+ kthread_init_worker(&ctrl->worker);
- init_kthread_work(&ctrl->auth, hdmi_hdcp2p2_auth_work);
- init_kthread_work(&ctrl->send_msg, hdmi_hdcp2p2_send_msg_work);
- init_kthread_work(&ctrl->recv_msg, hdmi_hdcp2p2_recv_msg_work);
- init_kthread_work(&ctrl->status, hdmi_hdcp2p2_auth_status_work);
- init_kthread_work(&ctrl->link, hdmi_hdcp2p2_link_work);
- init_kthread_work(&ctrl->poll, hdmi_hdcp2p2_poll_work);
+ kthread_init_work(&ctrl->auth, hdmi_hdcp2p2_auth_work);
+ kthread_init_work(&ctrl->send_msg, hdmi_hdcp2p2_send_msg_work);
+ kthread_init_work(&ctrl->recv_msg, hdmi_hdcp2p2_recv_msg_work);
+ kthread_init_work(&ctrl->status, hdmi_hdcp2p2_auth_status_work);
+ kthread_init_work(&ctrl->link, hdmi_hdcp2p2_link_work);
+ kthread_init_work(&ctrl->poll, hdmi_hdcp2p2_poll_work);
ctrl->thread = kthread_run(kthread_worker_fn,
&ctrl->worker, "hdmi_hdcp2p2");
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_panel.c b/drivers/video/fbdev/msm/mdss_hdmi_panel.c
index 9e082b3a..3823d3b 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_panel.c
@@ -108,7 +108,7 @@
};
struct hdmi_panel {
- struct dss_io_data *io;
+ struct mdss_io_data *io;
struct hdmi_util_ds_data *ds_data;
struct hdmi_panel_data *data;
struct hdmi_video_config vid_cfg;
@@ -275,7 +275,7 @@
u32 total_h, start_h, end_h;
u32 total_v, start_v, end_v;
u32 div = 0;
- struct dss_io_data *io = panel->io;
+ struct mdss_io_data *io = panel->io;
struct msm_hdmi_mode_timing_info *timing;
timing = panel->vid_cfg.timing;
@@ -342,7 +342,7 @@
u8 avi_iframe[AVI_MAX_DATA_BYTES] = {0};
u8 checksum;
u32 sum, reg_val;
- struct dss_io_data *io = panel->io;
+ struct mdss_io_data *io = panel->io;
struct hdmi_avi_infoframe_config *avi;
struct msm_hdmi_mode_timing_info *timing;
@@ -477,7 +477,7 @@
u32 sum, reg_val;
u32 hdmi_vic, hdmi_video_format, s3d_struct = 0;
struct hdmi_panel *panel = input;
- struct dss_io_data *io = panel->io;
+ struct mdss_io_data *io = panel->io;
/* HDMI Spec 1.4a Table 8-10 */
vs_iframe[0] = 0x81; /* type */
@@ -564,7 +564,7 @@
u32 packet_control = 0;
u8 *vendor_name = NULL;
u8 *product_description = NULL;
- struct dss_io_data *io = panel->io;
+ struct mdss_io_data *io = panel->io;
vendor_name = panel->spd_vendor_name;
product_description = panel->spd_product_description;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_panel.h b/drivers/video/fbdev/msm/mdss_hdmi_panel.h
index 4685b4e..50e168a 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_panel.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_panel.h
@@ -73,7 +73,7 @@
* @version: hardware version of the hdmi tx
*/
struct hdmi_panel_init_data {
- struct dss_io_data *io;
+ struct mdss_io_data *io;
struct hdmi_util_ds_data *ds_data;
struct hdmi_panel_data *panel_data;
struct hdmi_tx_ddc_ctrl *ddc;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
index 12aba84..4f2bb09 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
@@ -117,23 +117,23 @@
.irq_handler = hdmi_tx_isr,
};
-static struct dss_gpio hpd_gpio_config[] = {
+static struct mdss_gpio hpd_gpio_config[] = {
{0, 1, COMPATIBLE_NAME "-hpd"},
{0, 1, COMPATIBLE_NAME "-mux-en"},
{0, 0, COMPATIBLE_NAME "-mux-sel"},
{0, 1, COMPATIBLE_NAME "-mux-lpm"}
};
-static struct dss_gpio ddc_gpio_config[] = {
+static struct mdss_gpio ddc_gpio_config[] = {
{0, 1, COMPATIBLE_NAME "-ddc-mux-sel"},
{0, 1, COMPATIBLE_NAME "-ddc-clk"},
{0, 1, COMPATIBLE_NAME "-ddc-data"}
};
-static struct dss_gpio core_gpio_config[] = {
+static struct mdss_gpio core_gpio_config[] = {
};
-static struct dss_gpio cec_gpio_config[] = {
+static struct mdss_gpio cec_gpio_config[] = {
{0, 1, COMPATIBLE_NAME "-cec"}
};
@@ -152,7 +152,7 @@
{
int rc;
int reg_val;
- struct dss_io_data *io;
+ struct mdss_io_data *io;
rc = hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_HPD_PM, true);
if (rc) {
@@ -369,7 +369,7 @@
}
state = hdmi_ctrl->sdev.state;
- switch_set_state(&hdmi_ctrl->sdev, val);
+ extcon_set_state_sync(&hdmi_ctrl->sdev, EXTCON_DISP_HDMI, state);
DEV_INFO("%s: cable state %s %d\n", __func__,
hdmi_ctrl->sdev.state == state ?
@@ -392,7 +392,7 @@
{
u64 status = 0;
u32 wait_for_vote = 50;
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
if (!hdmi_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -483,7 +483,7 @@
static int hdmi_tx_config_5v(struct hdmi_tx_ctrl *hdmi_ctrl, bool enable)
{
- struct dss_module_power *pd = NULL;
+ struct mdss_module_power *pd = NULL;
int ret = 0;
if (!hdmi_ctrl) {
@@ -644,7 +644,7 @@
static int hdmi_tx_update_pixel_clk(struct hdmi_tx_ctrl *hdmi_ctrl)
{
- struct dss_module_power *power_data = NULL;
+ struct mdss_module_power *power_data = NULL;
struct mdss_panel_info *pinfo;
int rc = 0;
@@ -675,7 +675,7 @@
DEV_DBG("%s: rate %ld\n", __func__, power_data->clk_config->rate);
- msm_dss_clk_set_rate(power_data->clk_config, power_data->num_clk);
+ msm_mdss_clk_set_rate(power_data->clk_config, power_data->num_clk);
end:
return rc;
}
@@ -736,7 +736,7 @@
{
int sim_mode, rc;
struct hdmi_tx_ctrl *hdmi_ctrl = NULL;
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
hdmi_ctrl = hdmi_tx_get_drvdata_from_sysfs_dev(dev);
@@ -1222,7 +1222,7 @@
{
int read, ret;
struct hdmi_tx_ctrl *hdmi_ctrl = NULL;
- struct dss_module_power *pd = NULL;
+ struct mdss_module_power *pd = NULL;
hdmi_ctrl = hdmi_tx_get_drvdata_from_sysfs_dev(dev);
if (!hdmi_ctrl) {
@@ -1333,7 +1333,7 @@
static int hdmi_tx_config_avmute(struct hdmi_tx_ctrl *hdmi_ctrl, bool set)
{
- struct dss_io_data *io;
+ struct mdss_io_data *io;
u32 av_mute_status;
bool av_pkt_en = false;
@@ -1374,7 +1374,7 @@
static bool hdmi_tx_is_encryption_set(struct hdmi_tx_ctrl *hdmi_ctrl)
{
- struct dss_io_data *io;
+ struct mdss_io_data *io;
bool enc_en = true;
u32 reg_val;
@@ -1960,7 +1960,7 @@
static inline u32 hdmi_tx_is_controller_on(struct hdmi_tx_ctrl *hdmi_ctrl)
{
- struct dss_io_data *io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
+ struct mdss_io_data *io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
return DSS_REG_R_ND(io, HDMI_CTRL) & BIT(0);
} /* hdmi_tx_is_controller_on */
@@ -2096,7 +2096,7 @@
static void hdmi_tx_hpd_int_work(struct work_struct *work)
{
struct hdmi_tx_ctrl *hdmi_ctrl = NULL;
- struct dss_io_data *io;
+ struct mdss_io_data *io;
int rc = -EINVAL;
int retry = MAX_EDID_READ_RETRY;
@@ -2149,7 +2149,7 @@
static int hdmi_tx_check_capability(struct hdmi_tx_ctrl *hdmi_ctrl)
{
u32 hdmi_disabled, hdcp_disabled, reg_val;
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
int ret = 0;
if (!hdmi_ctrl) {
@@ -2208,7 +2208,7 @@
static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on)
{
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
/* Defaults: Disable block, HDMI mode */
u32 reg_val = BIT(1);
@@ -2260,7 +2260,7 @@
{
struct pinctrl_state *pin_state = NULL;
int rc = -EFAULT;
- struct dss_module_power *power_data = NULL;
+ struct mdss_module_power *power_data = NULL;
u64 cur_pin_states;
if (!hdmi_ctrl) {
@@ -2357,7 +2357,7 @@
enum hdmi_tx_power_module_type module, int config)
{
int rc = 0;
- struct dss_module_power *power_data = NULL;
+ struct mdss_module_power *power_data = NULL;
char name[MAX_CLIENT_NAME_LEN];
if (!hdmi_ctrl || module >= HDMI_TX_MAX_PM) {
@@ -2374,7 +2374,7 @@
}
if (config) {
- rc = msm_dss_config_vreg(&hdmi_ctrl->pdev->dev,
+ rc = msm_mdss_config_vreg(&hdmi_ctrl->pdev->dev,
power_data->vreg_config, power_data->num_vreg, 1);
if (rc) {
DEV_ERR("%s: Failed to config %s vreg. Err=%d\n",
@@ -2387,13 +2387,13 @@
mdss_reg_bus_vote_client_create(name);
if (IS_ERR(hdmi_ctrl->pdata.reg_bus_clt[module])) {
pr_err("reg bus client create failed\n");
- msm_dss_config_vreg(&hdmi_ctrl->pdev->dev,
+ msm_mdss_config_vreg(&hdmi_ctrl->pdev->dev,
power_data->vreg_config, power_data->num_vreg, 0);
rc = PTR_ERR(hdmi_ctrl->pdata.reg_bus_clt[module]);
goto exit;
}
- rc = msm_dss_get_clk(&hdmi_ctrl->pdev->dev,
+ rc = msm_mdss_get_clk(&hdmi_ctrl->pdev->dev,
power_data->clk_config, power_data->num_clk);
if (rc) {
DEV_ERR("%s: Failed to get %s clk. Err=%d\n",
@@ -2402,16 +2402,16 @@
mdss_reg_bus_vote_client_destroy(
hdmi_ctrl->pdata.reg_bus_clt[module]);
hdmi_ctrl->pdata.reg_bus_clt[module] = NULL;
- msm_dss_config_vreg(&hdmi_ctrl->pdev->dev,
+ msm_mdss_config_vreg(&hdmi_ctrl->pdev->dev,
power_data->vreg_config, power_data->num_vreg, 0);
}
} else {
- msm_dss_put_clk(power_data->clk_config, power_data->num_clk);
+ msm_mdss_put_clk(power_data->clk_config, power_data->num_clk);
mdss_reg_bus_vote_client_destroy(
hdmi_ctrl->pdata.reg_bus_clt[module]);
hdmi_ctrl->pdata.reg_bus_clt[module] = NULL;
- rc = msm_dss_config_vreg(&hdmi_ctrl->pdev->dev,
+ rc = msm_mdss_config_vreg(&hdmi_ctrl->pdev->dev,
power_data->vreg_config, power_data->num_vreg, 0);
if (rc)
DEV_ERR("%s: Fail to deconfig %s vreg. Err=%d\n",
@@ -2427,7 +2427,7 @@
{
int i;
int rc = 0;
- struct dss_module_power *pd = NULL;
+ struct mdss_module_power *pd = NULL;
if (!hdmi_ctrl || module >= HDMI_TX_MAX_PM) {
DEV_ERR("%s: Error: invalid input\n", __func__);
@@ -2473,7 +2473,7 @@
enum hdmi_tx_power_module_type module, int enable)
{
int rc = 0;
- struct dss_module_power *power_data = NULL;
+ struct mdss_module_power *power_data = NULL;
if (!hdmi_ctrl || module >= HDMI_TX_MAX_PM) {
DEV_ERR("%s: Error: invalid input\n", __func__);
@@ -2495,7 +2495,7 @@
}
if (enable && !hdmi_ctrl->power_data_enable[module]) {
- rc = msm_dss_enable_vreg(power_data->vreg_config,
+ rc = msm_mdss_enable_vreg(power_data->vreg_config,
power_data->num_vreg, 1);
if (rc) {
DEV_ERR("%s: Failed to enable %s vreg. Error=%d\n",
@@ -2510,7 +2510,7 @@
goto error;
}
- rc = msm_dss_enable_gpio(power_data->gpio_config,
+ rc = msm_mdss_enable_gpio(power_data->gpio_config,
power_data->num_gpio, 1);
if (rc) {
DEV_ERR("%s: Failed to enable %s gpio. Error=%d\n",
@@ -2520,7 +2520,7 @@
mdss_update_reg_bus_vote(hdmi_ctrl->pdata.reg_bus_clt[module],
VOTE_INDEX_LOW);
- rc = msm_dss_clk_set_rate(power_data->clk_config,
+ rc = msm_mdss_clk_set_rate(power_data->clk_config,
power_data->num_clk);
if (rc) {
DEV_ERR("%s: failed to set clks rate for %s. err=%d\n",
@@ -2528,7 +2528,7 @@
goto disable_gpio;
}
- rc = msm_dss_enable_clk(power_data->clk_config,
+ rc = msm_mdss_enable_clk(power_data->clk_config,
power_data->num_clk, 1);
if (rc) {
DEV_ERR("%s: Failed to enable clks for %s. Error=%d\n",
@@ -2539,14 +2539,14 @@
} else if (!enable && hdmi_ctrl->power_data_enable[module] &&
(!hdmi_tx_is_cec_wakeup_en(hdmi_ctrl) ||
((module != HDMI_TX_HPD_PM) && (module != HDMI_TX_CEC_PM)))) {
- msm_dss_enable_clk(power_data->clk_config,
+ msm_mdss_enable_clk(power_data->clk_config,
power_data->num_clk, 0);
mdss_update_reg_bus_vote(hdmi_ctrl->pdata.reg_bus_clt[module],
VOTE_INDEX_DISABLE);
- msm_dss_enable_gpio(power_data->gpio_config,
+ msm_mdss_enable_gpio(power_data->gpio_config,
power_data->num_gpio, 0);
hdmi_tx_pinctrl_set_state(hdmi_ctrl, module, 0);
- msm_dss_enable_vreg(power_data->vreg_config,
+ msm_mdss_enable_vreg(power_data->vreg_config,
power_data->num_vreg, 0);
hdmi_ctrl->power_data_enable[module] = false;
}
@@ -2556,9 +2556,9 @@
disable_gpio:
mdss_update_reg_bus_vote(hdmi_ctrl->pdata.reg_bus_clt[module],
VOTE_INDEX_DISABLE);
- msm_dss_enable_gpio(power_data->gpio_config, power_data->num_gpio, 0);
+ msm_mdss_enable_gpio(power_data->gpio_config, power_data->num_gpio, 0);
disable_vreg:
- msm_dss_enable_vreg(power_data->vreg_config, power_data->num_vreg, 0);
+ msm_mdss_enable_vreg(power_data->vreg_config, power_data->num_vreg, 0);
error:
return rc;
} /* hdmi_tx_enable_power */
@@ -2607,7 +2607,7 @@
unsigned int phy_reset_polarity = 0x0;
unsigned int pll_reset_polarity = 0x0;
unsigned int val;
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
if (!hdmi_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -2803,7 +2803,7 @@
return hpd;
}
-int msm_hdmi_register_audio_codec(struct platform_device *pdev,
+int msm_mdss_hdmi_register_audio_codec(struct platform_device *pdev,
struct msm_hdmi_audio_codec_ops *ops)
{
struct hdmi_tx_ctrl *hdmi_ctrl = platform_get_drvdata(pdev);
@@ -2819,7 +2819,7 @@
return 0;
} /* hdmi_tx_audio_register */
-EXPORT_SYMBOL(msm_hdmi_register_audio_codec);
+EXPORT_SYMBOL(msm_mdss_hdmi_register_audio_codec);
static int hdmi_tx_setup_tmds_clk_rate(struct hdmi_tx_ctrl *hdmi_ctrl)
{
@@ -2865,7 +2865,7 @@
static void hdmi_tx_hpd_polarity_setup(struct hdmi_tx_ctrl *hdmi_ctrl,
bool polarity)
{
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
bool cable_sense;
if (!hdmi_ctrl) {
@@ -2913,7 +2913,7 @@
static int hdmi_tx_power_off(struct hdmi_tx_ctrl *hdmi_ctrl)
{
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
void *pdata = NULL;
if (!hdmi_ctrl) {
@@ -3041,7 +3041,7 @@
static void hdmi_tx_hpd_off(struct hdmi_tx_ctrl *hdmi_ctrl)
{
int rc = 0;
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
unsigned long flags;
if (!hdmi_ctrl) {
@@ -3091,7 +3091,7 @@
{
u32 reg_val;
int rc = 0;
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
if (!hdmi_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -3114,7 +3114,7 @@
return rc;
}
- dss_reg_dump(io->base, io->len, "HDMI-INIT: ", REG_DUMP);
+ mdss_reg_dump(io->base, io->len, "HDMI-INIT: ", REG_DUMP);
if (!hdmi_ctrl->panel_data.panel_info.cont_splash_enabled) {
hdmi_tx_set_mode(hdmi_ctrl, false);
@@ -3213,7 +3213,7 @@
static irqreturn_t hdmi_tx_isr(int irq, void *data)
{
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
struct hdmi_tx_ctrl *hdmi_ctrl = (struct hdmi_tx_ctrl *)data;
unsigned long flags;
u32 hpd_current_state;
@@ -3304,7 +3304,7 @@
hdmi_ctrl->hdcp_ops = NULL;
hdmi_ctrl->hdcp_data = NULL;
- switch_dev_unregister(&hdmi_ctrl->sdev);
+ extcon_dev_unregister(&hdmi_ctrl->sdev);
if (hdmi_ctrl->workq)
destroy_workqueue(hdmi_ctrl->workq);
mutex_destroy(&hdmi_ctrl->tx_lock);
@@ -3407,7 +3407,7 @@
}
hdmi_ctrl->sdev.name = "hdmi";
- rc = switch_dev_register(&hdmi_ctrl->sdev);
+ rc = extcon_set_state_sync(&hdmi_ctrl->sdev, EXTCON_DISP_HDMI, false);
if (rc) {
DEV_ERR("%s: display switch registration failed\n", __func__);
goto end;
@@ -3581,7 +3581,7 @@
return 0;
primary_err:
- switch_dev_unregister(&hdmi_ctrl->sdev);
+ extcon_dev_unregister(&hdmi_ctrl->sdev);
switch_err:
hdmi_tx_deinit_features(hdmi_ctrl, HDMI_TX_FEAT_MAX);
init_err:
@@ -3854,7 +3854,7 @@
/* IO */
for (i = HDMI_TX_MAX_IO - 1; i >= 0; i--) {
if (hdmi_ctrl->pdata.io[i].base)
- msm_dss_iounmap(&hdmi_ctrl->pdata.io[i]);
+ msm_mdss_iounmap(&hdmi_ctrl->pdata.io[i]);
}
} /* hdmi_tx_deinit_resource */
@@ -3874,7 +3874,7 @@
/* IO */
for (i = 0; i < HDMI_TX_MAX_IO; i++) {
- rc = msm_dss_ioremap_byname(hdmi_ctrl->pdev, &pdata->io[i],
+ rc = msm_mdss_ioremap_byname(hdmi_ctrl->pdev, &pdata->io[i],
hdmi_tx_io_name(i));
if (rc) {
DEV_DBG("%s: '%s' remap failed or not available\n",
@@ -3903,7 +3903,7 @@
} /* hdmi_tx_init_resource */
static void hdmi_tx_put_dt_clk_data(struct device *dev,
- struct dss_module_power *module_power)
+ struct mdss_module_power *module_power)
{
if (!module_power) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -3919,7 +3919,7 @@
/* todo: once clk are moved to device tree then change this implementation */
static int hdmi_tx_get_dt_clk_data(struct device *dev,
- struct dss_module_power *mp, u32 module_type)
+ struct mdss_module_power *mp, u32 module_type)
{
int rc = 0;
@@ -3933,7 +3933,7 @@
switch (module_type) {
case HDMI_TX_HPD_PM:
mp->num_clk = 4;
- mp->clk_config = devm_kzalloc(dev, sizeof(struct dss_clk) *
+ mp->clk_config = devm_kzalloc(dev, sizeof(struct mdss_clk) *
mp->num_clk, GFP_KERNEL);
if (!mp->clk_config) {
DEV_ERR("%s: can't alloc '%s' clk mem\n", __func__,
@@ -3966,7 +3966,7 @@
case HDMI_TX_CORE_PM:
mp->num_clk = 1;
- mp->clk_config = devm_kzalloc(dev, sizeof(struct dss_clk) *
+ mp->clk_config = devm_kzalloc(dev, sizeof(struct mdss_clk) *
mp->num_clk, GFP_KERNEL);
if (!mp->clk_config) {
DEV_ERR("%s: can't alloc '%s' clk mem\n", __func__,
@@ -4005,7 +4005,7 @@
} /* hdmi_tx_get_dt_clk_data */
static void hdmi_tx_put_dt_vreg_data(struct device *dev,
- struct dss_module_power *module_power)
+ struct mdss_module_power *module_power)
{
if (!module_power) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -4020,7 +4020,7 @@
} /* hdmi_tx_put_dt_vreg_data */
static int hdmi_tx_get_dt_vreg_data(struct device *dev,
- struct dss_module_power *mp, u32 module_type)
+ struct mdss_module_power *mp, u32 module_type)
{
int i, j, rc = 0;
int dt_vreg_total = 0, mod_vreg_total = 0;
@@ -4085,7 +4085,7 @@
if (mod_vreg_total > 0) {
mp->num_vreg = mod_vreg_total;
- mp->vreg_config = devm_kzalloc(dev, sizeof(struct dss_vreg) *
+ mp->vreg_config = devm_kzalloc(dev, sizeof(struct mdss_vreg) *
mod_vreg_total, GFP_KERNEL);
if (!mp->vreg_config) {
DEV_ERR("%s: can't alloc '%s' vreg mem\n", __func__,
@@ -4198,7 +4198,7 @@
} /* hdmi_tx_get_dt_vreg_data */
static void hdmi_tx_put_dt_gpio_data(struct device *dev,
- struct dss_module_power *module_power)
+ struct mdss_module_power *module_power)
{
if (!module_power) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -4213,11 +4213,11 @@
} /* hdmi_tx_put_dt_gpio_data */
static int hdmi_tx_get_dt_gpio_data(struct device *dev,
- struct dss_module_power *mp, u32 module_type)
+ struct mdss_module_power *mp, u32 module_type)
{
int i, j;
int mp_gpio_cnt = 0, gpio_list_size = 0;
- struct dss_gpio *gpio_list = NULL;
+ struct mdss_gpio *gpio_list = NULL;
struct device_node *of_node = NULL;
DEV_DBG("%s: module: '%s'\n", __func__, hdmi_tx_pm_name(module_type));
@@ -4264,7 +4264,7 @@
DEV_DBG("%s: mp_gpio_cnt = %d\n", __func__, mp_gpio_cnt);
mp->num_gpio = mp_gpio_cnt;
- mp->gpio_config = devm_kzalloc(dev, sizeof(struct dss_gpio) *
+ mp->gpio_config = devm_kzalloc(dev, sizeof(struct mdss_gpio) *
mp_gpio_cnt, GFP_KERNEL);
if (!mp->gpio_config) {
DEV_ERR("%s: can't alloc '%s' gpio mem\n", __func__,
@@ -4283,7 +4283,7 @@
continue;
}
memcpy(&mp->gpio_config[j], &gpio_list[i],
- sizeof(struct dss_gpio));
+ sizeof(struct mdss_gpio));
mp->gpio_config[j].gpio = (unsigned int)gpio;
@@ -4531,17 +4531,17 @@
if (hdmi_ctrl->panel_data.panel_info.cont_splash_enabled) {
for (i = 0; i < HDMI_TX_MAX_PM; i++) {
- msm_dss_enable_vreg(
+ msm_mdss_enable_vreg(
hdmi_ctrl->pdata.power_data[i].vreg_config,
hdmi_ctrl->pdata.power_data[i].num_vreg, 1);
hdmi_tx_pinctrl_set_state(hdmi_ctrl, i, 1);
- msm_dss_enable_gpio(
+ msm_mdss_enable_gpio(
hdmi_ctrl->pdata.power_data[i].gpio_config,
hdmi_ctrl->pdata.power_data[i].num_gpio, 1);
- msm_dss_enable_clk(
+ msm_mdss_enable_clk(
hdmi_ctrl->pdata.power_data[i].clk_config,
hdmi_ctrl->pdata.power_data[i].num_clk, 1);
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.h b/drivers/video/fbdev/msm/mdss_hdmi_tx.h
index e55aaea..6a13c75 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_tx.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.h
@@ -13,7 +13,7 @@
#ifndef __MDSS_HDMI_TX_H__
#define __MDSS_HDMI_TX_H__
-#include <linux/switch.h>
+#include <linux/extcon.h>
#include "mdss_hdmi_util.h"
#include "mdss_hdmi_panel.h"
#include "mdss_cec_core.h"
@@ -41,8 +41,8 @@
bool primary;
bool cont_splash_enabled;
bool cond_power_on;
- struct dss_io_data io[HDMI_TX_MAX_IO];
- struct dss_module_power power_data[HDMI_TX_MAX_PM];
+ struct mdss_io_data io[HDMI_TX_MAX_IO];
+ struct mdss_module_power power_data[HDMI_TX_MAX_PM];
struct reg_bus_client *reg_bus_clt[HDMI_TX_MAX_PM];
/* bitfield representing each module's pin state */
u64 pin_states;
@@ -72,7 +72,7 @@
struct mutex tx_lock;
struct list_head cable_notify_handlers;
struct kobject *kobj;
- struct switch_dev sdev;
+ struct extcon_dev sdev;
struct workqueue_struct *workq;
struct hdmi_util_ds_data ds_data;
struct completion hpd_int_done;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.c b/drivers/video/fbdev/msm/mdss_hdmi_util.c
index 8942e71..b5bedfa 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_util.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_util.c
@@ -186,7 +186,7 @@
{
u32 reg_val;
int rc;
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
if (!ctrl || !ctrl->io) {
pr_err("invalid input\n");
@@ -655,7 +655,7 @@
enum trigger_mode mode, bool seg)
{
struct hdmi_tx_ddc_data *ddc_data = &ddc_ctrl->ddc_data;
- struct dss_io_data *io = ddc_ctrl->io;
+ struct mdss_io_data *io = ddc_ctrl->io;
u32 const seg_addr = 0x60, seg_num = 0x01;
u32 ddc_ctrl_reg_val;
@@ -738,7 +738,7 @@
u32 reg_val, ndx, time_out_count, wait_time;
struct hdmi_tx_ddc_data *ddc_data;
int status;
- int busy_wait_us;
+ int busy_wait_us = 0;
if (!ddc_ctrl || !ddc_ctrl->io) {
pr_err("invalid input\n");
@@ -886,7 +886,7 @@
static int hdmi_ddc_hdcp2p2_isr(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
{
- struct dss_io_data *io = NULL;
+ struct mdss_io_data *io = NULL;
struct hdmi_tx_hdcp2p2_ddc_data *data;
u32 intr0, intr2, intr5;
u32 msg_size;
@@ -1022,7 +1022,7 @@
static int hdmi_ddc_scrambling_isr(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
{
- struct dss_io_data *io;
+ struct mdss_io_data *io;
bool scrambler_timer_off = false;
u32 intr2, intr5;
@@ -1216,7 +1216,7 @@
u32 time_out_count;
struct hdmi_tx_ddc_data *ddc_data;
u32 wait_time;
- int busy_wait_us;
+ int busy_wait_us = 0;
if (!ddc_ctrl || !ddc_ctrl->io) {
pr_err("invalid input\n");
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.h b/drivers/video/fbdev/msm/mdss_hdmi_util.h
index d26be99..ecab9d5 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_util.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_util.h
@@ -447,7 +447,7 @@
atomic_t write_busy_wait_done;
atomic_t read_busy_wait_done;
atomic_t rxstatus_busy_wait_done;
- struct dss_io_data *io;
+ struct mdss_io_data *io;
struct completion ddc_sw_done;
struct hdmi_tx_ddc_data ddc_data;
struct hdmi_tx_hdcp2p2_ddc_data hdcp2p2_ddc_data;
diff --git a/drivers/video/fbdev/msm/mdss_io_util.c b/drivers/video/fbdev/msm/mdss_io_util.c
index bd96c605..3117793 100644
--- a/drivers/video/fbdev/msm/mdss_io_util.c
+++ b/drivers/video/fbdev/msm/mdss_io_util.c
@@ -17,7 +17,7 @@
#include <linux/mdss_io_util.h>
#define MAX_I2C_CMDS 16
-void dss_reg_w(struct dss_io_data *io, u32 offset, u32 value, u32 debug)
+void mdss_reg_w(struct mdss_io_data *io, u32 offset, u32 value, u32 debug)
{
u32 in_val;
@@ -41,10 +41,10 @@
value, in_val);
}
-} /* dss_reg_w */
-EXPORT_SYMBOL(dss_reg_w);
+} /* mdss_reg_w */
+EXPORT_SYMBOL(mdss_reg_w);
-u32 dss_reg_r(struct dss_io_data *io, u32 offset, u32 debug)
+u32 mdss_reg_r(struct mdss_io_data *io, u32 offset, u32 debug)
{
u32 value;
@@ -66,19 +66,19 @@
(u32)(unsigned long)(io->base + offset), value);
return value;
-} /* dss_reg_r */
-EXPORT_SYMBOL(dss_reg_r);
+} /* mdss_reg_r */
+EXPORT_SYMBOL(mdss_reg_r);
-void dss_reg_dump(void __iomem *base, u32 length, const char *prefix,
+void mdss_reg_dump(void __iomem *base, u32 length, const char *prefix,
u32 debug)
{
if (debug)
print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_OFFSET, 32, 4,
(void *)base, length, false);
-} /* dss_reg_dump */
-EXPORT_SYMBOL(dss_reg_dump);
+} /* mdss_reg_dump */
+EXPORT_SYMBOL(mdss_reg_dump);
-static struct resource *msm_dss_get_res_byname(struct platform_device *pdev,
+static struct resource *msm_mdss_get_res_byname(struct platform_device *pdev,
unsigned int type, const char *name)
{
struct resource *res = NULL;
@@ -88,11 +88,11 @@
DEV_ERR("%s: '%s' resource not found\n", __func__, name);
return res;
-} /* msm_dss_get_res_byname */
-EXPORT_SYMBOL(msm_dss_get_res_byname);
+} /* msm_mdss_get_res_byname */
+EXPORT_SYMBOL(msm_mdss_get_res_byname);
-int msm_dss_ioremap_byname(struct platform_device *pdev,
- struct dss_io_data *io_data, const char *name)
+int msm_mdss_ioremap_byname(struct platform_device *pdev,
+ struct mdss_io_data *io_data, const char *name)
{
struct resource *res = NULL;
@@ -102,9 +102,9 @@
return -EINVAL;
}
- res = msm_dss_get_res_byname(pdev, IORESOURCE_MEM, name);
+ res = msm_mdss_get_res_byname(pdev, IORESOURCE_MEM, name);
if (!res) {
- DEV_ERR("%pS->%s: '%s' msm_dss_get_res_byname failed\n",
+ DEV_ERR("%pS->%s: '%s' msm_mdss_get_res_byname failed\n",
__builtin_return_address(0), __func__, name);
return -ENODEV;
}
@@ -118,10 +118,10 @@
}
return 0;
-} /* msm_dss_ioremap_byname */
-EXPORT_SYMBOL(msm_dss_ioremap_byname);
+} /* msm_mdss_ioremap_byname */
+EXPORT_SYMBOL(msm_mdss_ioremap_byname);
-void msm_dss_iounmap(struct dss_io_data *io_data)
+void msm_mdss_iounmap(struct mdss_io_data *io_data)
{
if (!io_data) {
DEV_ERR("%pS->%s: invalid input\n",
@@ -134,15 +134,15 @@
io_data->base = NULL;
}
io_data->len = 0;
-} /* msm_dss_iounmap */
-EXPORT_SYMBOL(msm_dss_iounmap);
+} /* msm_mdss_iounmap */
+EXPORT_SYMBOL(msm_mdss_iounmap);
-int msm_dss_config_vreg(struct device *dev, struct dss_vreg *in_vreg,
+int msm_mdss_config_vreg(struct device *dev, struct mdss_vreg *in_vreg,
int num_vreg, int config)
{
int i = 0, rc = 0;
- struct dss_vreg *curr_vreg = NULL;
- enum dss_vreg_type type;
+ struct mdss_vreg *curr_vreg = NULL;
+ enum mdss_vreg_type type;
if (!in_vreg || !num_vreg)
return rc;
@@ -196,7 +196,7 @@
vreg_unconfig:
if (type == DSS_REG_LDO)
- regulator_set_optimum_mode(curr_vreg->vreg, 0);
+ regulator_set_load(curr_vreg->vreg, 0);
vreg_set_voltage_fail:
regulator_put(curr_vreg->vreg);
@@ -210,11 +210,11 @@
goto vreg_unconfig;
}
return rc;
-} /* msm_dss_config_vreg */
-EXPORT_SYMBOL(msm_dss_config_vreg);
+} /* msm_mdss_config_vreg */
+EXPORT_SYMBOL(msm_mdss_config_vreg);
-int msm_dss_config_vreg_opt_mode(struct dss_vreg *in_vreg, int num_vreg,
- enum dss_vreg_mode mode)
+int msm_mdss_config_vreg_opt_mode(struct mdss_vreg *in_vreg, int num_vreg,
+ enum mdss_vreg_mode mode)
{
int i = 0, rc = 0;
@@ -237,7 +237,7 @@
DEV_DBG("%s: Setting optimum mode %d for %s (load=%d)\n",
__func__, mode, in_vreg[i].vreg_name,
in_vreg[i].load[mode]);
- rc = regulator_set_optimum_mode(in_vreg[i].vreg,
+ rc = regulator_set_load(in_vreg[i].vreg,
in_vreg[i].load[mode]);
if (rc < 0) {
DEV_ERR("%pS->%s: %s set opt mode failed. rc=%d\n",
@@ -246,7 +246,7 @@
goto error;
} else {
/*
- * regulator_set_optimum_mode can return non-zero
+ * regulator_set_load can return non-zero
* value for success. However, this API is expected
* to return 0 for success.
*/
@@ -257,9 +257,9 @@
error:
return rc;
}
-EXPORT_SYMBOL(msm_dss_config_vreg_opt_mode);
+EXPORT_SYMBOL(msm_mdss_config_vreg_opt_mode);
-int msm_dss_enable_vreg(struct dss_vreg *in_vreg, int num_vreg, int enable)
+int msm_mdss_enable_vreg(struct mdss_vreg *in_vreg, int num_vreg, int enable)
{
int i = 0, rc = 0;
bool need_sleep;
@@ -277,7 +277,7 @@
if (in_vreg[i].pre_on_sleep && need_sleep)
usleep_range(in_vreg[i].pre_on_sleep * 1000,
in_vreg[i].pre_on_sleep * 1000);
- rc = regulator_set_optimum_mode(in_vreg[i].vreg,
+ rc = regulator_set_load(in_vreg[i].vreg,
in_vreg[i].load[DSS_REG_MODE_ENABLE]);
if (rc < 0) {
DEV_ERR("%pS->%s: %s set opt m fail\n",
@@ -301,7 +301,7 @@
if (in_vreg[i].pre_off_sleep)
usleep_range(in_vreg[i].pre_off_sleep * 1000,
in_vreg[i].pre_off_sleep * 1000);
- regulator_set_optimum_mode(in_vreg[i].vreg,
+ regulator_set_load(in_vreg[i].vreg,
in_vreg[i].load[DSS_REG_MODE_DISABLE]);
if (regulator_is_enabled(in_vreg[i].vreg))
@@ -315,7 +315,7 @@
return rc;
disable_vreg:
- regulator_set_optimum_mode(in_vreg[i].vreg,
+ regulator_set_load(in_vreg[i].vreg,
in_vreg[i].load[DSS_REG_MODE_DISABLE]);
vreg_set_opt_mode_fail:
@@ -323,7 +323,7 @@
if (in_vreg[i].pre_off_sleep)
usleep_range(in_vreg[i].pre_off_sleep * 1000,
in_vreg[i].pre_off_sleep * 1000);
- regulator_set_optimum_mode(in_vreg[i].vreg,
+ regulator_set_load(in_vreg[i].vreg,
in_vreg[i].load[DSS_REG_MODE_DISABLE]);
regulator_disable(in_vreg[i].vreg);
if (in_vreg[i].post_off_sleep)
@@ -332,10 +332,10 @@
}
return rc;
-} /* msm_dss_enable_vreg */
-EXPORT_SYMBOL(msm_dss_enable_vreg);
+} /* msm_mdss_enable_vreg */
+EXPORT_SYMBOL(msm_mdss_enable_vreg);
-int msm_dss_enable_gpio(struct dss_gpio *in_gpio, int num_gpio, int enable)
+int msm_mdss_enable_gpio(struct mdss_gpio *in_gpio, int num_gpio, int enable)
{
int i = 0, rc = 0;
@@ -372,10 +372,10 @@
gpio_free(in_gpio[i].gpio);
return rc;
-} /* msm_dss_enable_gpio */
-EXPORT_SYMBOL(msm_dss_enable_gpio);
+} /* msm_mdss_enable_gpio */
+EXPORT_SYMBOL(msm_mdss_enable_gpio);
-void msm_dss_put_clk(struct dss_clk *clk_arry, int num_clk)
+void msm_mdss_put_clk(struct mdss_clk *clk_arry, int num_clk)
{
int i;
@@ -384,10 +384,10 @@
clk_put(clk_arry[i].clk);
clk_arry[i].clk = NULL;
}
-} /* msm_dss_put_clk */
-EXPORT_SYMBOL(msm_dss_put_clk);
+} /* msm_mdss_put_clk */
+EXPORT_SYMBOL(msm_mdss_put_clk);
-int msm_dss_get_clk(struct device *dev, struct dss_clk *clk_arry, int num_clk)
+int msm_mdss_get_clk(struct device *dev, struct mdss_clk *clk_arry, int num_clk)
{
int i, rc = 0;
@@ -405,13 +405,13 @@
return rc;
error:
- msm_dss_put_clk(clk_arry, num_clk);
+ msm_mdss_put_clk(clk_arry, num_clk);
return rc;
-} /* msm_dss_get_clk */
-EXPORT_SYMBOL(msm_dss_get_clk);
+} /* msm_mdss_get_clk */
+EXPORT_SYMBOL(msm_mdss_get_clk);
-int msm_dss_clk_set_rate(struct dss_clk *clk_arry, int num_clk)
+int msm_mdss_clk_set_rate(struct mdss_clk *clk_arry, int num_clk)
{
int i, rc = 0;
@@ -442,10 +442,10 @@
}
return rc;
-} /* msm_dss_clk_set_rate */
-EXPORT_SYMBOL(msm_dss_clk_set_rate);
+} /* msm_mdss_clk_set_rate */
+EXPORT_SYMBOL(msm_mdss_clk_set_rate);
-int msm_dss_enable_clk(struct dss_clk *clk_arry, int num_clk, int enable)
+int msm_mdss_enable_clk(struct mdss_clk *clk_arry, int num_clk, int enable)
{
int i, rc = 0;
@@ -469,7 +469,7 @@
}
if (rc) {
- msm_dss_enable_clk(&clk_arry[i],
+ msm_mdss_enable_clk(&clk_arry[i],
i, false);
break;
}
@@ -490,8 +490,8 @@
}
return rc;
-} /* msm_dss_enable_clk */
-EXPORT_SYMBOL(msm_dss_enable_clk);
+} /* msm_mdss_enable_clk */
+EXPORT_SYMBOL(msm_mdss_enable_clk);
int mdss_i2c_byte_read(struct i2c_client *client, uint8_t slave_addr,
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index 73324489..a9a5d8f 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -114,7 +114,7 @@
.irq_handler = NULL,
};
-#ifdef CONFIG_MSM_BUS_SCALING
+#ifdef CONFIG_QCOM_BUS_SCALING
#define MDP_REG_BUS_VECTOR_ENTRY(ab_val, ib_val) \
{ \
.src = MSM_BUS_MASTER_AMPSS_M0, \
@@ -354,7 +354,6 @@
/* check here if virq is a valid interrupt line */
irq_set_chip_and_handler(virq, &mdss_irq_chip, handle_level_irq);
irq_set_chip_data(virq, mdata);
- set_irq_flags(virq, IRQF_VALID);
return 0;
}
@@ -418,7 +417,7 @@
return IRQ_HANDLED;
}
-#ifdef CONFIG_MSM_BUS_SCALING
+#ifdef CONFIG_QCOM_BUS_SCALING
static int mdss_mdp_bus_scale_register(struct mdss_data_type *mdata)
{
struct msm_bus_scale_pdata *reg_bus_pdata;
@@ -812,7 +811,7 @@
int mdss_mdp_irq_enable(u32 intr_type, u32 intf_num)
{
- int irq_idx, idx;
+ int irq_idx;
unsigned long irq_flags;
int ret = 0;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
@@ -831,7 +830,7 @@
spin_lock_irqsave(&mdp_lock, irq_flags);
if (mdata->mdp_irq_mask[irq.reg_idx] & irq.irq_mask) {
pr_warn("MDSS MDP IRQ-0x%x is already set, mask=%x\n",
- irq.irq_mask, mdata->mdp_irq_mask[idx]);
+ irq.irq_mask, mdata->mdp_irq_mask[irq.reg_idx]);
ret = -EBUSY;
} else {
pr_debug("MDP IRQ mask old=%x new=%x\n",
@@ -1182,7 +1181,8 @@
if (IS_ERR_VALUE(clk_rate)) {
pr_err("unable to round rate err=%ld\n", clk_rate);
} else if (clk_rate != clk_get_rate(clk)) {
- if (IS_ERR_VALUE(clk_set_rate(clk, clk_rate)))
+ if (IS_ERR_VALUE((unsigned long)
+ clk_set_rate(clk, clk_rate)))
pr_err("clk_set_rate failed\n");
else
pr_debug("mdp clk rate=%lu\n", clk_rate);
@@ -1374,7 +1374,7 @@
}
mutex_unlock(&mdp_iommu_ref_cnt_lock);
- if (IS_ERR_VALUE(rc))
+ if (IS_ERR_VALUE((unsigned long)rc))
return rc;
else
return mdata->iommu_ref_cnt;
@@ -1431,7 +1431,7 @@
pr_debug("called from %pS\n", __builtin_return_address(0));
rc = mdss_iommu_ctrl(1);
- if (IS_ERR_VALUE(rc)) {
+ if (IS_ERR_VALUE((unsigned long)rc)) {
pr_err("mdss iommu attach failed rc=%d\n", rc);
goto end;
}
@@ -1546,7 +1546,7 @@
VOTE_INDEX_LOW);
rc = mdss_iommu_ctrl(1);
- if (IS_ERR_VALUE(rc))
+ if (IS_ERR_VALUE((unsigned long)rc))
pr_err("IOMMU attach failed\n");
/* Active+Sleep */
@@ -1665,7 +1665,7 @@
pr_debug("max mdp clk rate=%d\n", mdata->max_mdp_clk_rate);
ret = devm_request_irq(&mdata->pdev->dev, mdss_mdp_hw.irq_info->irq,
- mdss_irq_handler, IRQF_DISABLED, "MDSS", mdata);
+ mdss_irq_handler, 0, "MDSS", mdata);
if (ret) {
pr_err("mdp request_irq() failed!\n");
return ret;
@@ -2630,13 +2630,7 @@
mdss_res->mdss_util->panel_intf_type = mdss_panel_intf_type;
mdss_res->mdss_util->panel_intf_status = mdss_panel_get_intf_status;
- if (mdss_res->mdss_util->param_check(mdss_mdp_panel)) {
- mdss_res->mdss_util->display_disabled = true;
- mdss_res->mdss_util->mdp_probe_done = true;
- return 0;
- }
-
- rc = msm_dss_ioremap_byname(pdev, &mdata->mdss_io, "mdp_phys");
+ rc = msm_mdss_ioremap_byname(pdev, &mdata->mdss_io, "mdp_phys");
if (rc) {
pr_err("unable to map MDP base\n");
goto probe_done;
@@ -2645,7 +2639,7 @@
(int) (unsigned long) mdata->mdss_io.base,
mdata->mdss_io.len);
- rc = msm_dss_ioremap_byname(pdev, &mdata->vbif_io, "vbif_phys");
+ rc = msm_mdss_ioremap_byname(pdev, &mdata->vbif_io, "vbif_phys");
if (rc) {
pr_err("unable to map MDSS VBIF base\n");
goto probe_done;
@@ -2654,7 +2648,8 @@
(int) (unsigned long) mdata->vbif_io.base,
mdata->vbif_io.len);
- rc = msm_dss_ioremap_byname(pdev, &mdata->vbif_nrt_io, "vbif_nrt_phys");
+ rc = msm_mdss_ioremap_byname(pdev, &mdata->vbif_nrt_io,
+ "vbif_nrt_phys");
if (rc)
pr_debug("unable to map MDSS VBIF non-realtime base\n");
else
@@ -2834,7 +2829,7 @@
num_of_display_on, intf_sel);
probe_done:
- if (IS_ERR_VALUE(rc)) {
+ if (IS_ERR_VALUE((unsigned long)rc)) {
if (!num_of_display_on)
mdss_mdp_footswitch_ctrl_splash(false);
@@ -2850,8 +2845,8 @@
return rc;
}
-static void mdss_mdp_parse_dt_regs_array(const u32 *arr, struct dss_io_data *io,
- struct mdss_hw_settings *hws, int count)
+static void mdss_mdp_parse_dt_regs_array(const u32 *arr,
+ struct mdss_io_data *io, struct mdss_hw_settings *hws, int count)
{
u32 len, reg;
int i;
@@ -3261,28 +3256,28 @@
rc = mdss_mdp_parse_dt_pipe_helper(pdev, MDSS_MDP_PIPE_TYPE_VIG, "vig",
&mdata->vig_pipes, mdata->nvig_pipes, 0);
- if (IS_ERR_VALUE(rc))
+ if (IS_ERR_VALUE((unsigned long)rc))
goto parse_fail;
mdata->nvig_pipes = rc;
rc = mdss_mdp_parse_dt_pipe_helper(pdev, MDSS_MDP_PIPE_TYPE_RGB, "rgb",
&mdata->rgb_pipes, mdata->nrgb_pipes,
mdata->nvig_pipes);
- if (IS_ERR_VALUE(rc))
+ if (IS_ERR_VALUE((unsigned long)rc))
goto parse_fail;
mdata->nrgb_pipes = rc;
rc = mdss_mdp_parse_dt_pipe_helper(pdev, MDSS_MDP_PIPE_TYPE_DMA, "dma",
&mdata->dma_pipes, mdata->ndma_pipes,
mdata->nvig_pipes + mdata->nrgb_pipes);
- if (IS_ERR_VALUE(rc))
+ if (IS_ERR_VALUE((unsigned long)rc))
goto parse_fail;
mdata->ndma_pipes = rc;
rc = mdss_mdp_parse_dt_pipe_helper(pdev, MDSS_MDP_PIPE_TYPE_CURSOR,
"cursor", &mdata->cursor_pipes, mdata->ncursor_pipes,
0);
- if (IS_ERR_VALUE(rc))
+ if (IS_ERR_VALUE((unsigned long)rc))
goto parse_fail;
mdata->ncursor_pipes = rc;
@@ -4282,7 +4277,7 @@
return 0;
}
-#ifdef CONFIG_MSM_BUS_SCALING
+#ifdef CONFIG_QCOM_BUS_SCALING
static int mdss_mdp_parse_dt_bus_scale(struct platform_device *pdev)
{
int rc, paths;
@@ -4358,6 +4353,7 @@
return rc;
}
#else
+__maybe_unused
static int mdss_mdp_parse_dt_bus_scale(struct platform_device *pdev)
{
return 0;
@@ -4975,7 +4971,7 @@
#define mdss_mdp_resume NULL
#endif
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
static int mdss_mdp_runtime_resume(struct device *dev)
{
struct mdss_data_type *mdata = dev_get_drvdata(dev);
@@ -5033,9 +5029,11 @@
static const struct dev_pm_ops mdss_mdp_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(mdss_mdp_pm_suspend, mdss_mdp_pm_resume)
+#ifdef CONFIG_PM
SET_RUNTIME_PM_OPS(mdss_mdp_runtime_suspend,
mdss_mdp_runtime_resume,
mdss_mdp_runtime_idle)
+#endif
};
static int mdss_mdp_remove(struct platform_device *pdev)
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index eb85ceb..4307119 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -648,7 +648,6 @@
/* vsync handler for FRC */
struct mdss_mdp_vsync_handler frc_vsync_handler;
- bool commit_in_progress;
};
struct mdss_mdp_mixer {
@@ -1010,7 +1009,7 @@
u32 splash_mem_size;
u32 sd_enabled;
- struct sw_sync_timeline *vsync_timeline;
+ struct mdss_timeline *vsync_timeline;
struct mdss_mdp_vsync_handler vsync_retire_handler;
int retire_cnt;
bool kickoff_released;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index 6e4a6b3..4f17310 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -942,6 +942,7 @@
if (!ctl || !ctl->mdata)
return 0;
+ mdata = ctl->mdata;
mixer = pipe->mixer_left;
if (!mixer)
return -EINVAL;
@@ -2670,7 +2671,7 @@
ktime_t current_time = ktime_get();
if (!ctl->ops.read_line_cnt_fnc)
- return -ENOTSUP;
+ return -ENOTSUPP;
pinfo = &ctl->panel_data->panel_info;
if (!pinfo)
@@ -5524,9 +5525,7 @@
} else {
sctl_flush_bits = sctl->flush_bits;
}
- sctl->commit_in_progress = true;
}
- ctl->commit_in_progress = true;
ctl_flush_bits = ctl->flush_bits;
ATRACE_END("postproc_programming");
@@ -5666,15 +5665,11 @@
ATRACE_BEGIN("flush_kickoff");
mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, ctl_flush_bits);
- if (sctl) {
- if (sctl_flush_bits) {
- mdss_mdp_ctl_write(sctl, MDSS_MDP_REG_CTL_FLUSH,
- sctl_flush_bits);
- sctl->flush_bits = 0;
- }
- sctl->commit_in_progress = false;
+ if (sctl && sctl_flush_bits) {
+ mdss_mdp_ctl_write(sctl, MDSS_MDP_REG_CTL_FLUSH,
+ sctl_flush_bits);
+ sctl->flush_bits = 0;
}
- ctl->commit_in_progress = false;
MDSS_XLOG(ctl->intf_num, ctl_flush_bits, sctl_flush_bits,
split_lm_valid);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_debug.c b/drivers/video/fbdev/msm/mdss_mdp_debug.c
index b641ccb..d24ff53 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_debug.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_debug.c
@@ -1495,11 +1495,11 @@
debugfs_create_file("safe_stat", 0644, mdd->root, mdata,
&mdss_debugfs_safe_stats_fops);
debugfs_create_bool("serialize_wait4pp", 0644, mdd->root,
- (u32 *)&mdata->serialize_wait4pp);
+ (bool *)&mdata->serialize_wait4pp);
debugfs_create_bool("wait4autorefresh", 0644, mdd->root,
- (u32 *)&mdata->wait4autorefresh);
+ (bool *)&mdata->wait4autorefresh);
debugfs_create_bool("enable_gate", 0644, mdd->root,
- (u32 *)&mdata->enable_gate);
+ (bool *)&mdata->enable_gate);
debugfs_create_u32("color0", 0644, mdd->bordercolor,
(u32 *)&mdata->bcolor0);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
index 84218e3..7a0542a 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
@@ -1206,7 +1206,6 @@
atomic_read(&ctx->koff_cnt));
if (sync_ppdone) {
atomic_inc(&ctx->pp_done_cnt);
- if (!ctl->commit_in_progress)
schedule_work(&ctx->pp_done_work);
mdss_mdp_resource_control(ctl,
@@ -3080,7 +3079,7 @@
pr_debug("%s: turn off interface clocks\n", __func__);
ret = mdss_mdp_cmd_stop_sub(ctl, panel_power_state);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("%s: unable to stop interface: %d\n",
__func__, ret);
goto end;
@@ -3088,7 +3087,7 @@
if (sctl) {
mdss_mdp_cmd_stop_sub(sctl, panel_power_state);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("%s: unable to stop slave intf: %d\n",
__func__, ret);
goto end;
@@ -3127,7 +3126,7 @@
ctl->ops.reconfigure = NULL;
end:
- if (!IS_ERR_VALUE(ret)) {
+ if (!IS_ERR_VALUE((unsigned long)ret)) {
struct mdss_mdp_cmd_ctx *sctx = NULL;
ctx->panel_power_state = panel_power_state;
@@ -3505,7 +3504,7 @@
/* Command mode is supported only starting at INTF1 */
session = ctl->intf_num - MDSS_MDP_INTF1;
ret = mdss_mdp_cmd_intfs_setup(ctl, session);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("unable to set cmd interface: %d\n", ret);
return ret;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
index 453fe28..7b69aa3 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
@@ -926,7 +926,7 @@
intfs_num = ctl->intf_num - MDSS_MDP_INTF0;
ret = mdss_mdp_video_intfs_stop(ctl, ctl->panel_data, intfs_num);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("unable to stop video interface: %d\n", ret);
return ret;
}
@@ -1512,7 +1512,7 @@
}
rc = mdss_iommu_ctrl(1);
- if (IS_ERR_VALUE(rc)) {
+ if (IS_ERR_VALUE((unsigned long)rc)) {
pr_err("IOMMU attach failed\n");
return rc;
}
@@ -2166,7 +2166,7 @@
intfs_num = ctl->intf_num - MDSS_MDP_INTF0;
ret = mdss_mdp_video_intfs_setup(ctl, ctl->panel_data, intfs_num);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("unable to set video interface: %d\n", ret);
return ret;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c b/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
index b155870..46f25dd 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
@@ -794,7 +794,7 @@
mdss_mdp_irq_enable(ctx->intr_type, ctx->intf_num);
ret = mdss_iommu_ctrl(1);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("IOMMU attach failed\n");
return ret;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c
index 49a7bf4..b1c8041 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_layer.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c
@@ -21,8 +21,6 @@
#include <linux/delay.h>
#include <linux/msm_mdp.h>
#include <linux/memblock.h>
-#include <linux/sync.h>
-#include <linux/sw_sync.h>
#include <linux/file.h>
#include <soc/qcom/event_timer.h>
@@ -31,6 +29,7 @@
#include "mdss_fb.h"
#include "mdss_mdp.h"
#include "mdss_mdp_wfd.h"
+#include "mdss_sync.h"
#define CHECK_LAYER_BOUNDS(offset, size, max_size) \
(((size) > (max_size)) || ((offset) > ((max_size) - (size))))
@@ -704,13 +703,13 @@
return ret;
}
-static struct sync_fence *__create_fence(struct msm_fb_data_type *mfd,
+static struct mdss_fence *__create_fence(struct msm_fb_data_type *mfd,
struct msm_sync_pt_data *sync_pt_data, u32 fence_type,
int *fence_fd, int value)
{
struct mdss_overlay_private *mdp5_data;
struct mdss_mdp_ctl *ctl;
- struct sync_fence *sync_fence = NULL;
+ struct mdss_fence *sync_fence = NULL;
char fence_name[32];
mdp5_data = mfd_to_mdp5_data(mfd);
@@ -736,16 +735,23 @@
if ((fence_type == MDSS_MDP_RETIRE_FENCE) &&
(mfd->panel.type == MIPI_CMD_PANEL)) {
if (mdp5_data->vsync_timeline) {
- value = mdp5_data->vsync_timeline->value + 1 +
- mdp5_data->retire_cnt++;
+ value = 1 + mdp5_data->retire_cnt++;
sync_fence = mdss_fb_sync_get_fence(
- mdp5_data->vsync_timeline, fence_name, value);
+ mdp5_data->vsync_timeline, fence_name,
+ value);
} else {
return ERR_PTR(-EPERM);
}
} else {
- sync_fence = mdss_fb_sync_get_fence(sync_pt_data->timeline,
- fence_name, value);
+ if (fence_type == MDSS_MDP_RETIRE_FENCE)
+ sync_fence = mdss_fb_sync_get_fence(
+ sync_pt_data->timeline_retire,
+ fence_name, value);
+ else
+ sync_fence = mdss_fb_sync_get_fence(
+ sync_pt_data->timeline,
+ fence_name, value);
+
}
if (IS_ERR_OR_NULL(sync_fence)) {
@@ -754,14 +760,15 @@
}
/* get fence fd */
- *fence_fd = get_unused_fd_flags(0);
+ *fence_fd = mdss_get_sync_fence_fd(sync_fence);
if (*fence_fd < 0) {
pr_err("%s: get_unused_fd_flags failed error:0x%x\n",
fence_name, *fence_fd);
- sync_fence_put(sync_fence);
+ mdss_put_sync_fence(sync_fence);
sync_fence = NULL;
goto end;
}
+ pr_debug("%s:val=%d\n", mdss_get_sync_fence_name(sync_fence), value);
end:
return sync_fence;
@@ -777,7 +784,7 @@
static int __handle_buffer_fences(struct msm_fb_data_type *mfd,
struct mdp_layer_commit_v1 *commit, struct mdp_input_layer *layer_list)
{
- struct sync_fence *fence, *release_fence, *retire_fence;
+ struct mdss_fence *fence, *release_fence, *retire_fence;
struct msm_sync_pt_data *sync_pt_data = NULL;
struct mdp_input_layer *layer;
int value;
@@ -803,7 +810,7 @@
if (layer->buffer.fence < 0)
continue;
- fence = sync_fence_fdget(layer->buffer.fence);
+ fence = mdss_get_fd_sync_fence(layer->buffer.fence);
if (!fence) {
pr_err("%s: sync fence get failed! fd=%d\n",
sync_pt_data->fence_name, layer->buffer.fence);
@@ -816,7 +823,7 @@
if (ret)
goto sync_fence_err;
- value = sync_pt_data->timeline_value + sync_pt_data->threshold +
+ value = sync_pt_data->threshold +
atomic_read(&sync_pt_data->commit_cnt);
release_fence = __create_fence(mfd, sync_pt_data,
@@ -835,21 +842,18 @@
goto retire_fence_err;
}
- sync_fence_install(release_fence, commit->release_fence);
- sync_fence_install(retire_fence, commit->retire_fence);
-
mutex_unlock(&sync_pt_data->sync_mutex);
return ret;
retire_fence_err:
put_unused_fd(commit->release_fence);
- sync_fence_put(release_fence);
+ mdss_put_sync_fence(release_fence);
release_fence_err:
commit->retire_fence = -1;
commit->release_fence = -1;
sync_fence_err:
for (i = 0; i < sync_pt_data->acq_fen_cnt; i++)
- sync_fence_put(sync_pt_data->acq_fen[i]);
+ mdss_put_sync_fence(sync_pt_data->acq_fen[i]);
sync_pt_data->acq_fen_cnt = 0;
mutex_unlock(&sync_pt_data->sync_mutex);
@@ -1522,7 +1526,7 @@
int cnt = 1;
mode = __multirect_layer_flags_to_mode(layer_list[ndx].flags);
- if (IS_ERR_VALUE(mode))
+ if (IS_ERR_VALUE((unsigned long)mode))
return mode;
pr_debug("layer #%d pipe_ndx=%d multirect mode=%d\n",
@@ -1557,7 +1561,7 @@
mode = __multirect_layer_flags_to_mode(
layer_list[i].flags);
- if (IS_ERR_VALUE(mode))
+ if (IS_ERR_VALUE((unsigned long)mode))
return mode;
if (mode != vinfo[0]->multirect.mode) {
@@ -1598,7 +1602,7 @@
cnt = __update_multirect_info(mfd, validate_info_list,
layer_list, ndx, layer_cnt);
- if (IS_ERR_VALUE(cnt))
+ if (IS_ERR_VALUE((unsigned long)cnt))
return cnt;
if (cnt <= 1) {
@@ -1619,7 +1623,7 @@
}
rc = __multirect_validate_mode(mfd, layers, cnt);
- if (IS_ERR_VALUE(rc))
+ if (IS_ERR_VALUE((unsigned long)rc))
return rc;
return 0;
@@ -1647,7 +1651,7 @@
u32 mixer_mux, dst_x;
int layer_count = commit->input_layer_cnt;
- struct mdss_mdp_pipe *pipe, *tmp, *left_blend_pipe;
+ struct mdss_mdp_pipe *pipe = NULL, *tmp, *left_blend_pipe;
struct mdss_mdp_pipe *right_plist[MAX_PIPES_PER_LM] = {0};
struct mdss_mdp_pipe *left_plist[MAX_PIPES_PER_LM] = {0};
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
@@ -1867,7 +1871,7 @@
rec_destroy_ndx[rect_num] |= pipe->ndx;
ret = mdss_mdp_pipe_map(pipe);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("Unable to map used pipe%d ndx=%x\n",
pipe->num, pipe->ndx);
layer->error_code = ret;
@@ -1937,7 +1941,7 @@
rec_destroy_ndx[0], rec_destroy_ndx[1], ret);
mutex_lock(&mdp5_data->list_lock);
list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_used, list) {
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
if (((pipe->ndx & rec_release_ndx[0]) &&
(pipe->multirect.num == 0)) ||
((pipe->ndx & rec_release_ndx[1]) &&
@@ -2062,7 +2066,7 @@
if (!validate_info_list[i].layer) {
ret = __update_multirect_info(mfd, validate_info_list,
layer_list, i, layer_count);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("error updating multirect config. ret=%d i=%d\n",
ret, i);
goto end;
@@ -2180,7 +2184,7 @@
struct mdss_mdp_wfd *wfd = NULL;
struct mdp_output_layer *output_layer = NULL;
struct mdss_mdp_wfd_data *data = NULL;
- struct sync_fence *fence = NULL;
+ struct mdss_fence *fence = NULL;
struct msm_sync_pt_data *sync_pt_data = NULL;
if (!mfd || !commit)
@@ -2208,7 +2212,8 @@
return PTR_ERR(data);
if (output_layer->buffer.fence >= 0) {
- fence = sync_fence_fdget(output_layer->buffer.fence);
+ fence = mdss_get_fd_sync_fence(
+ output_layer->buffer.fence);
if (!fence) {
pr_err("fail to get output buffer fence\n");
rc = -EINVAL;
@@ -2249,7 +2254,7 @@
input_layer_err:
if (fence)
- sync_fence_put(fence);
+ mdss_put_sync_fence(fence);
fence_get_err:
if (data)
mdss_mdp_wfd_remove_data(wfd, data);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
index f69f5b3..3144b6c 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -25,8 +25,8 @@
#include <linux/msm_mdp.h>
#include <linux/memblock.h>
#include <linux/sort.h>
-#include <linux/sw_sync.h>
#include <linux/kmemleak.h>
+#include <linux/kthread.h>
#include <asm/div64.h>
#include <soc/qcom/event_timer.h>
@@ -38,6 +38,7 @@
#include "mdss_smmu.h"
#include "mdss_mdp_wfd.h"
#include "mdss_dsi_clk.h"
+#include "mdss_sync.h"
#define VSYNC_PERIOD 16
#define BORDERFILL_NDX 0x0BF000BF
@@ -760,7 +761,7 @@
}
ret = mdss_mdp_pipe_map(pipe);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("Unable to map used pipe%d ndx=%x\n",
pipe->num, pipe->ndx);
return ret;
@@ -1387,7 +1388,7 @@
if (!mdp5_data->mdata->idle_pc_enabled ||
(mfd->panel_info->type != MIPI_CMD_PANEL)) {
rc = pm_runtime_get_sync(&mfd->pdev->dev);
- if (IS_ERR_VALUE(rc)) {
+ if (IS_ERR_VALUE((unsigned long)rc)) {
pr_err("unable to resume with pm_runtime_get_sync rc=%d\n",
rc);
goto end;
@@ -1404,7 +1405,7 @@
*/
if (!mfd->panel_info->cont_splash_enabled) {
rc = mdss_iommu_ctrl(1);
- if (IS_ERR_VALUE(rc)) {
+ if (IS_ERR_VALUE((unsigned long)rc)) {
pr_err("iommu attach failed rc=%d\n", rc);
goto end;
}
@@ -1591,10 +1592,10 @@
* if we reach here without errors and buf == NULL
* then solid fill will be set
*/
- if (!IS_ERR_VALUE(ret))
+ if (!IS_ERR_VALUE((unsigned long)ret))
ret = mdss_mdp_pipe_queue_data(pipe, buf);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_warn("Unable to queue data for pnum=%d rect=%d\n",
pipe->num, pipe->multirect.num);
@@ -2231,7 +2232,7 @@
if (frc_info->video_stat.last_delta) {
video_fps_changed =
- abs64(delta_t - frc_info->video_stat.last_delta)
+ abs(delta_t - frc_info->video_stat.last_delta)
> (FRC_VIDEO_FPS_CHANGE_THRESHOLD_US *
FRC_VIDEO_FPS_DETECT_WINDOW);
@@ -2883,7 +2884,7 @@
}
ret = mdss_iommu_ctrl(1);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("iommu attach failed rc=%d\n", ret);
mutex_unlock(&mdp5_data->ov_lock);
if (ctl->shared_lock)
@@ -2906,7 +2907,7 @@
sd_transition_state = mdp5_data->sd_transition_state;
if (sd_transition_state != SD_TRANSITION_NONE) {
ret = __config_secure_display(mdp5_data);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("Secure session config failed\n");
goto commit_fail;
}
@@ -2963,7 +2964,7 @@
if (!mdp5_data->kickoff_released)
mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_CTX_DONE);
- if (IS_ERR_VALUE(ret))
+ if (IS_ERR_VALUE((unsigned long)ret))
goto commit_fail;
mutex_unlock(&mdp5_data->ov_lock);
@@ -2985,7 +2986,7 @@
ret = mdss_mdp_ctl_update_fps(ctl);
ATRACE_END("fps_update");
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("failed to update fps!\n");
goto commit_fail;
}
@@ -3166,7 +3167,7 @@
}
ret = mdss_mdp_pipe_map(pipe);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("Unable to map used pipe%d ndx=%x\n",
pipe->num, pipe->ndx);
return ret;
@@ -3192,7 +3193,7 @@
ret = mdss_mdp_data_get_and_validate_size(src_data, &req->data,
1, flags, &mfd->pdev->dev, false, DMA_TO_DEVICE,
&buffer);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
mdss_mdp_overlay_buf_free(mfd, src_data);
pr_err("src_data pmem error\n");
}
@@ -3435,7 +3436,7 @@
}
ret = mdss_iommu_ctrl(1);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("IOMMU attach failed\n");
goto clk_disable;
}
@@ -5095,7 +5096,7 @@
static int mdss_mdp_histo_ioctl(struct msm_fb_data_type *mfd, u32 cmd,
void __user *argp)
{
- int ret = -ENOTSUP;
+ int ret = -ENOTSUPP;
struct mdp_histogram_data hist;
struct mdp_histogram_start_req hist_req;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
@@ -5493,7 +5494,7 @@
left_blend_pipe, is_single_layer);
req->z_order -= MDSS_MDP_STAGE_0;
- if (IS_ERR_VALUE(ret))
+ if (IS_ERR_VALUE((unsigned long)ret))
goto validate_exit;
pr_debug("pnum:%d id:0x%x flags:0x%x dst_x:%d l_blend_pnum%d\n",
@@ -5533,7 +5534,7 @@
else
ovlist->processed_overlays = i;
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_debug("err=%d total_ovs:%d processed:%d left:%d right:%d\n",
ret, num_ovs, ovlist->processed_overlays, left_lm_ovs,
right_lm_ovs);
@@ -5586,7 +5587,7 @@
}
ret = __handle_overlay_prepare(mfd, &ovlist, overlays);
- if (!IS_ERR_VALUE(ret)) {
+ if (!IS_ERR_VALUE((unsigned long)ret)) {
for (i = 0; i < ovlist.num_overlays; i++) {
if (copy_to_user(req_list[i], overlays + i,
sizeof(struct mdp_overlay))) {
@@ -5609,7 +5610,7 @@
u32 cmd, void __user *argp)
{
struct mdp_overlay *req = NULL;
- int val, ret = -ENOTSUP;
+ int val, ret = -ENOTSUPP;
struct msmfb_metadata metadata;
struct mdp_pp_feature_version pp_feature_version;
struct msmfb_overlay_data data;
@@ -5653,7 +5654,7 @@
if (!ret) {
ret = mdss_mdp_overlay_get(mfd, req);
- if (!IS_ERR_VALUE(ret))
+ if (!IS_ERR_VALUE((unsigned long)ret))
ret = copy_to_user(argp, req, sizeof(*req));
}
@@ -5669,7 +5670,7 @@
if (!ret) {
ret = mdss_mdp_overlay_set(mfd, req);
- if (!IS_ERR_VALUE(ret))
+ if (!IS_ERR_VALUE((unsigned long)ret))
ret = copy_to_user(argp, req, sizeof(*req));
}
if (ret)
@@ -5949,7 +5950,7 @@
}
panel_on:
- if (IS_ERR_VALUE(rc)) {
+ if (IS_ERR_VALUE((unsigned long)rc)) {
pr_err("Failed to turn on fb%d\n", mfd->index);
mdss_mdp_overlay_off(mfd);
goto end;
@@ -6110,7 +6111,7 @@
* retire_signal api checks for retire_cnt with sync_mutex lock.
*/
- flush_kthread_work(&mdp5_data->vsync_work);
+ kthread_flush_work(&mdp5_data->vsync_work);
}
ctl_stop:
@@ -6316,7 +6317,7 @@
}
mdp5_data = mfd_to_mdp5_data(mfd);
- queue_kthread_work(&mdp5_data->worker, &mdp5_data->vsync_work);
+ kthread_queue_work(&mdp5_data->worker, &mdp5_data->vsync_work);
}
static void __vsync_retire_work_handler(struct kthread_work *work)
@@ -6339,11 +6340,11 @@
mutex_lock(&mfd->mdp_sync_pt_data.sync_mutex);
if (mdp5_data->retire_cnt > 0) {
- sw_sync_timeline_inc(mdp5_data->vsync_timeline, val);
+ mdss_inc_timeline(mdp5_data->vsync_timeline, val);
mdp5_data->retire_cnt -= min(val, mdp5_data->retire_cnt);
pr_debug("Retire signaled! timeline val=%d remaining=%d\n",
- mdp5_data->vsync_timeline->value,
- mdp5_data->retire_cnt);
+ mdss_get_timeline_retire_ts(mdp5_data->vsync_timeline),
+ mdp5_data->retire_cnt);
if (mdp5_data->retire_cnt == 0) {
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
@@ -6355,7 +6356,7 @@
mutex_unlock(&mfd->mdp_sync_pt_data.sync_mutex);
}
-static struct sync_fence *
+static struct mdss_fence *
__vsync_retire_get_fence(struct msm_sync_pt_data *sync_pt_data)
{
struct msm_fb_data_type *mfd;
@@ -6378,7 +6379,7 @@
return ERR_PTR(-EPERM);
}
- value = mdp5_data->vsync_timeline->value + 1 + mdp5_data->retire_cnt;
+ value = 1 + mdp5_data->retire_cnt;
mdp5_data->retire_cnt++;
return mdss_fb_sync_get_fence(mdp5_data->vsync_timeline,
@@ -6419,14 +6420,14 @@
struct sched_param param = { .sched_priority = 5 };
snprintf(name, sizeof(name), "mdss_fb%d_retire", mfd->index);
- mdp5_data->vsync_timeline = sw_sync_timeline_create(name);
+ mdp5_data->vsync_timeline = mdss_create_timeline(name);
if (mdp5_data->vsync_timeline == NULL) {
pr_err("cannot vsync create time line");
return -ENOMEM;
}
- init_kthread_worker(&mdp5_data->worker);
- init_kthread_work(&mdp5_data->vsync_work, __vsync_retire_work_handler);
+ kthread_init_worker(&mdp5_data->worker);
+ kthread_init_work(&mdp5_data->vsync_work, __vsync_retire_work_handler);
mdp5_data->thread = kthread_run(kthread_worker_fn,
&mdp5_data->worker,
@@ -6717,7 +6718,7 @@
if (mfd->panel_info->mipi.dms_mode ||
mfd->panel_info->type == MIPI_CMD_PANEL) {
rc = __vsync_retire_setup(mfd);
- if (IS_ERR_VALUE(rc)) {
+ if (IS_ERR_VALUE((unsigned long)rc)) {
pr_err("unable to create vsync timeline\n");
goto init_fail;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c
index 6131ed1..6ac2c4b 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c
@@ -1717,7 +1717,7 @@
lut_offset = mdata->scaler_off->dest_base +
mdata->scaler_off->dest_scaler_lut_off[id];
/*TODO : set pixel fmt to RGB101010 */
- return -ENOTSUP;
+ return -ENOTSUPP;
} else {
return -EINVAL;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c b/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c
index f17cf6f..02ddced 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c
@@ -89,7 +89,7 @@
}
sinfo->size = buf_size;
- dma_buf_begin_cpu_access(sinfo->dma_buf, 0, size, DMA_BIDIRECTIONAL);
+ dma_buf_begin_cpu_access(sinfo->dma_buf, DMA_BIDIRECTIONAL);
sinfo->splash_buffer = dma_buf_kmap(sinfo->dma_buf, 0);
if (IS_ERR(sinfo->splash_buffer)) {
pr_err("ion kernel memory mapping failed\n");
@@ -133,8 +133,7 @@
if (!mdata || !mdata->iclient || !sinfo->dma_buf)
return;
- dma_buf_end_cpu_access(sinfo->dma_buf, 0, sinfo->size,
- DMA_BIDIRECTIONAL);
+ dma_buf_end_cpu_access(sinfo->dma_buf, DMA_BIDIRECTIONAL);
dma_buf_kunmap(sinfo->dma_buf, 0, sinfo->splash_buffer);
mdss_smmu_unmap_dma_buf(sinfo->table, MDSS_IOMMU_DOMAIN_UNSECURE, 0,
@@ -177,7 +176,7 @@
pr_debug("iommu memory mapping failed rc=%d\n", rc);
} else {
ret = mdss_iommu_ctrl(1);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("mdss iommu attach failed\n");
mdss_smmu_unmap(MDSS_IOMMU_DOMAIN_UNSECURE,
mdp5_data->splash_mem_addr,
@@ -288,7 +287,7 @@
*/
if (mdp5_data->handoff && ctl && ctl->is_video_mode) {
rc = mdss_mdp_display_commit(ctl, NULL, NULL);
- if (!IS_ERR_VALUE(rc)) {
+ if (!IS_ERR_VALUE((unsigned long)rc)) {
mdss_mdp_display_wait4comp(ctl);
} else {
/*
diff --git a/drivers/video/fbdev/msm/mdss_mdp_trace.h b/drivers/video/fbdev/msm/mdss_mdp_trace.h
index c100e9c..f8a6baf 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_trace.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_trace.h
@@ -376,7 +376,7 @@
__entry->kickoff_cnt)
);
-TRACE_EVENT(tracing_mark_write,
+TRACE_EVENT(mdss_mark_write,
TP_PROTO(int pid, const char *name, bool trace_begin),
TP_ARGS(pid, name, trace_begin),
TP_STRUCT__entry(
diff --git a/drivers/video/fbdev/msm/mdss_mdp_util.c b/drivers/video/fbdev/msm/mdss_mdp_util.c
index 6c28fe9..44f53a7 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_util.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_util.c
@@ -420,8 +420,8 @@
if (fmt->format == MDP_Y_CBCR_H2V2_UBWC ||
fmt->format == MDP_Y_CBCR_H2V2_TP10_UBWC) {
- uint32_t y_stride_alignment, uv_stride_alignment;
- uint32_t y_height_alignment, uv_height_alignment;
+ uint32_t y_stride_alignment = 0, uv_stride_alignment = 0;
+ uint32_t y_height_alignment = 0, uv_height_alignment = 0;
uint32_t y_tile_width = fmt_ubwc->micro.tile_width;
uint32_t y_tile_height = fmt_ubwc->micro.tile_height;
uint32_t uv_tile_width = y_tile_width / 2;
@@ -517,12 +517,11 @@
if (ps == NULL)
return -EINVAL;
- memset(ps, 0, sizeof(struct mdss_mdp_plane_sizes));
-
if ((w > MAX_IMG_WIDTH) || (h > MAX_IMG_HEIGHT))
return -ERANGE;
bpp = fmt->bpp;
+ memset(ps, 0, sizeof(struct mdss_mdp_plane_sizes));
if (mdss_mdp_is_ubwc_format(fmt)) {
rc = mdss_mdp_get_ubwc_plane_size(fmt, w, h, ps);
@@ -1002,8 +1001,8 @@
}
data->srcp_f = f;
- if (MAJOR(f.file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
- fb_num = MINOR(f.file->f_dentry->d_inode->i_rdev);
+ if (MAJOR(f.file->f_path.dentry->d_inode->i_rdev) == FB_MAJOR) {
+ fb_num = MINOR(f.file->f_path.dentry->d_inode->i_rdev);
ret = mdss_fb_get_phys_info(start, len, fb_num);
if (ret)
pr_err("mdss_fb_get_phys_info() failed\n");
@@ -1051,7 +1050,7 @@
struct sg_table *sg_ptr = NULL;
do {
- ihandle = ion_import_dma_buf(iclient,
+ ihandle = ion_import_dma_buf_fd(iclient,
img->memory_id);
if (IS_ERR_OR_NULL(ihandle)) {
ret = -EINVAL;
@@ -1140,7 +1139,7 @@
ret = mdss_smmu_map_dma_buf(data->srcp_dma_buf,
data->srcp_table, domain,
&data->addr, &data->len, dir);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("smmu map dma buf failed: (%d)\n", ret);
goto err_unmap;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_wfd.h b/drivers/video/fbdev/msm/mdss_mdp_wfd.h
index e603c48..b35feb7 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_wfd.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_wfd.h
@@ -14,8 +14,6 @@
#ifndef __MDSS_MDP_WFD_H__
#define __MDSS_MDP_WFD_H__
-#include <linux/sync.h>
-#include <linux/sw_sync.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/msm_mdp_ext.h>
diff --git a/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c
index 73676d0..2dc9a1f 100644
--- a/drivers/video/fbdev/msm/mdss_rotator.c
+++ b/drivers/video/fbdev/msm/mdss_rotator.c
@@ -17,7 +17,6 @@
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/file.h>
-#include <linux/sync.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/clk.h>
@@ -28,6 +27,7 @@
#include "mdss_rotator_internal.h"
#include "mdss_mdp.h"
#include "mdss_debug.h"
+#include "mdss_sync.h"
/* waiting for hw time out, 3 vsync for 30fps*/
#define ROT_HW_ACQUIRE_TIMEOUT_IN_MS 100
@@ -230,7 +230,7 @@
pr_err("unable to round rate err=%ld\n", clk_rate);
} else if (clk_rate != clk_get_rate(clk)) {
ret = clk_set_rate(clk, clk_rate);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
pr_err("clk_set_rate failed, err:%d\n", ret);
} else {
pr_debug("rotator clk rate=%lu\n", clk_rate);
@@ -253,7 +253,7 @@
}
pr_debug("%s: rotator regulators", on ? "Enable" : "Disable");
- ret = msm_dss_enable_vreg(mgr->module_power.vreg_config,
+ ret = msm_mdss_enable_vreg(mgr->module_power.vreg_config,
mgr->module_power.num_vreg, on);
if (ret) {
pr_warn("Rotator regulator failed to %s\n",
@@ -374,21 +374,11 @@
return false;
}
-static void mdss_rotator_install_fence_fd(struct mdss_rot_entry_container *req)
-{
- int i = 0;
-
- for (i = 0; i < req->count; i++)
- sync_fence_install(req->entries[i].output_fence,
- req->entries[i].output_fence_fd);
-}
-
static int mdss_rotator_create_fence(struct mdss_rot_entry *entry)
{
int ret = 0, fd;
u32 val;
- struct sync_pt *sync_pt;
- struct sync_fence *fence;
+ struct mdss_fence *fence;
struct mdss_rot_timeline *rot_timeline;
if (!entry->queue)
@@ -397,24 +387,15 @@
rot_timeline = &entry->queue->timeline;
mutex_lock(&rot_timeline->lock);
- val = rot_timeline->next_value + 1;
+ val = 1;
- sync_pt = sw_sync_pt_create(rot_timeline->timeline, val);
- if (sync_pt == NULL) {
+ fence = mdss_get_sync_fence(rot_timeline->timeline,
+ rot_timeline->fence_name, NULL, val);
+ if (fence == NULL) {
pr_err("cannot create sync point\n");
goto sync_pt_create_err;
}
-
- /* create fence */
- fence = sync_fence_create(rot_timeline->fence_name, sync_pt);
- if (fence == NULL) {
- pr_err("%s: cannot create fence\n", rot_timeline->fence_name);
- sync_pt_free(sync_pt);
- ret = -ENOMEM;
- goto sync_pt_create_err;
- }
-
- fd = get_unused_fd_flags(0);
+ fd = mdss_get_sync_fence_fd(fence);
if (fd < 0) {
pr_err("get_unused_fd_flags failed error:0x%x\n", fd);
ret = fd;
@@ -426,12 +407,13 @@
entry->output_fence_fd = fd;
entry->output_fence = fence;
- pr_debug("output sync point created at val=%u\n", val);
+ pr_debug("output sync point created at %s:val=%u\n",
+ mdss_get_sync_fence_name(fence), val);
return 0;
get_fd_err:
- sync_fence_put(fence);
+ mdss_put_sync_fence(fence);
sync_pt_create_err:
mutex_unlock(&rot_timeline->lock);
return ret;
@@ -442,7 +424,7 @@
struct mdss_rot_timeline *rot_timeline;
if (entry->input_fence) {
- sync_fence_put(entry->input_fence);
+ mdss_put_sync_fence(entry->input_fence);
entry->input_fence = NULL;
}
@@ -450,7 +432,7 @@
/* fence failed to copy to user space */
if (entry->output_fence) {
- sync_fence_put(entry->output_fence);
+ mdss_put_sync_fence(entry->output_fence);
entry->output_fence = NULL;
put_unused_fd(entry->output_fence_fd);
@@ -475,7 +457,7 @@
}
mutex_lock(&rot_timeline->lock);
- sw_sync_timeline_inc(rot_timeline->timeline, 1);
+ mdss_inc_timeline(rot_timeline->timeline, 1);
mutex_unlock(&rot_timeline->lock);
entry->output_signaled = true;
@@ -492,8 +474,8 @@
return 0;
}
- ret = sync_fence_wait(entry->input_fence, ROT_FENCE_WAIT_TIMEOUT);
- sync_fence_put(entry->input_fence);
+ ret = mdss_wait_sync_fence(entry->input_fence, ROT_FENCE_WAIT_TIMEOUT);
+ mdss_put_sync_fence(entry->input_fence);
entry->input_fence = NULL;
return ret;
}
@@ -545,7 +527,7 @@
ATRACE_BEGIN(__func__);
ret = mdss_iommu_ctrl(1);
- if (IS_ERR_VALUE(ret)) {
+ if (IS_ERR_VALUE((unsigned long)ret)) {
ATRACE_END(__func__);
return ret;
}
@@ -697,7 +679,7 @@
struct mdss_rot_hw_resource *hw;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
u32 pipe_ndx, offset = mdss_mdp_get_wb_ctl_support(mdata, true);
- int ret;
+ int ret = 0;
hw = devm_kzalloc(&mgr->pdev->dev, sizeof(struct mdss_rot_hw_resource),
GFP_KERNEL);
@@ -867,7 +849,7 @@
snprintf(name, sizeof(name), "rot_timeline_%d", i);
pr_debug("timeline name=%s\n", name);
mgr->queues[i].timeline.timeline =
- sw_sync_timeline_create(name);
+ mdss_create_timeline(name);
if (!mgr->queues[i].timeline.timeline) {
ret = -EPERM;
break;
@@ -896,11 +878,11 @@
destroy_workqueue(mgr->queues[i].rot_work_queue);
if (mgr->queues[i].timeline.timeline) {
- struct sync_timeline *obj;
+ struct mdss_timeline *obj;
- obj = (struct sync_timeline *)
+ obj = (struct mdss_timeline *)
mgr->queues[i].timeline.timeline;
- sync_timeline_destroy(obj);
+ mdss_destroy_timeline(obj);
}
}
devm_kfree(&mgr->pdev->dev, mgr->queues);
@@ -1524,8 +1506,8 @@
}
if (item->input.fence >= 0) {
- entry->input_fence =
- sync_fence_fdget(item->input.fence);
+ entry->input_fence = mdss_get_fd_sync_fence(
+ item->input.fence);
if (!entry->input_fence) {
pr_err("invalid input fence fd\n");
return -EINVAL;
@@ -2263,7 +2245,6 @@
goto handle_request_err1;
}
- mdss_rotator_install_fence_fd(req);
mdss_rotator_queue_request(mgr, private, req);
mutex_unlock(&mgr->lock);
@@ -2423,7 +2404,6 @@
goto handle_request32_err1;
}
- mdss_rotator_install_fence_fd(req);
mdss_rotator_queue_request(mgr, private, req);
mutex_unlock(&mgr->lock);
@@ -2677,14 +2657,14 @@
}
static void mdss_rotator_put_dt_vreg_data(struct device *dev,
- struct dss_module_power *mp)
+ struct mdss_module_power *mp)
{
if (!mp) {
DEV_ERR("%s: invalid input\n", __func__);
return;
}
- msm_dss_config_vreg(dev, mp->vreg_config, mp->num_vreg, 0);
+ msm_mdss_config_vreg(dev, mp->vreg_config, mp->num_vreg, 0);
if (mp->vreg_config) {
devm_kfree(dev, mp->vreg_config);
mp->vreg_config = NULL;
@@ -2693,7 +2673,7 @@
}
static int mdss_rotator_get_dt_vreg_data(struct device *dev,
- struct dss_module_power *mp)
+ struct mdss_module_power *mp)
{
const char *st = NULL;
struct device_node *of_node = NULL;
@@ -2715,7 +2695,7 @@
return 0;
}
mp->num_vreg = dt_vreg_total;
- mp->vreg_config = devm_kzalloc(dev, sizeof(struct dss_vreg) *
+ mp->vreg_config = devm_kzalloc(dev, sizeof(struct mdss_vreg) *
dt_vreg_total, GFP_KERNEL);
if (!mp->vreg_config) {
DEV_ERR("%s: can't alloc vreg mem\n", __func__);
@@ -2733,7 +2713,7 @@
}
snprintf(mp->vreg_config[i].vreg_name, 32, "%s", st);
}
- msm_dss_config_vreg(dev, mp->vreg_config, mp->num_vreg, 1);
+ msm_mdss_config_vreg(dev, mp->vreg_config, mp->num_vreg, 1);
for (i = 0; i < dt_vreg_total; i++) {
DEV_DBG("%s: %s min=%d, max=%d, enable=%d disable=%d\n",
diff --git a/drivers/video/fbdev/msm/mdss_rotator_internal.h b/drivers/video/fbdev/msm/mdss_rotator_internal.h
index 8dd400e..88f530a 100644
--- a/drivers/video/fbdev/msm/mdss_rotator_internal.h
+++ b/drivers/video/fbdev/msm/mdss_rotator_internal.h
@@ -15,10 +15,8 @@
#define MDSS_MDP_ROTATOR_INTERNAL_H
#include <linux/list.h>
-#include <linux/sync.h>
#include <linux/file.h>
#include <linux/mdss_rotator.h>
-#include <linux/sw_sync.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/cdev.h>
@@ -52,7 +50,7 @@
struct mdss_rot_timeline {
struct mutex lock;
- struct sw_sync_timeline *timeline;
+ struct mdss_timeline *timeline;
u32 next_value;
char fence_name[32];
};
@@ -95,10 +93,10 @@
struct mdss_mdp_data src_buf;
struct mdss_mdp_data dst_buf;
- struct sync_fence *input_fence;
+ struct mdss_fence *input_fence;
int output_fence_fd;
- struct sync_fence *output_fence;
+ struct mdss_fence *output_fence;
bool output_signaled;
u32 dnsc_factor_w;
@@ -174,7 +172,7 @@
struct mdss_rot_bus_data_type reg_bus;
/* Module power is only used for regulator management */
- struct dss_module_power module_power;
+ struct mdss_module_power module_power;
bool regulator_enable;
struct mutex clk_lock;
diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c
index 05353a0..7a44824 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.c
+++ b/drivers/video/fbdev/msm/mdss_smmu.c
@@ -17,17 +17,15 @@
#include <linux/debugfs.h>
#include <linux/kernel.h>
#include <linux/iommu.h>
-#include <linux/qcom_iommu.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/clk/msm-clk.h>
-
+#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/dma-buf.h>
#include <linux/of_platform.h>
#include <linux/msm_dma_iommu_mapping.h>
-#include <linux/qcom_iommu.h>
#include <asm/dma-iommu.h>
#include "soc/qcom/secure_buffer.h"
@@ -38,6 +36,27 @@
#define SZ_4G 0xF0000000
+#ifdef CONFIG_QCOM_IOMMU
+#include <linux/qcom_iommu.h>
+static inline struct bus_type *mdss_mmu_get_bus(struct device *dev)
+{
+ return msm_iommu_get_bus(dev);
+}
+static inline struct device *mdss_mmu_get_ctx(const char *name)
+{
+ return msm_iommu_get_ctx(name);
+}
+#else
+static inline struct bus_type *mdss_mmu_get_bus(struct device *dev)
+{
+ return &platform_bus_type;
+}
+static inline struct device *mdss_mmu_get_ctx(const char *name)
+{
+ return ERR_PTR(-ENODEV);
+}
+#endif
+
static DEFINE_MUTEX(mdp_iommu_lock);
void mdss_iommu_lock(void)
@@ -51,7 +70,7 @@
}
static int mdss_smmu_util_parse_dt_clock(struct platform_device *pdev,
- struct dss_module_power *mp)
+ struct mdss_module_power *mp)
{
u32 i = 0, rc = 0;
const char *clock_name;
@@ -67,7 +86,7 @@
mp->num_clk = num_clk;
mp->clk_config = devm_kzalloc(&pdev->dev,
- sizeof(struct dss_clk) * mp->num_clk, GFP_KERNEL);
+ sizeof(struct mdss_clk) * mp->num_clk, GFP_KERNEL);
if (!mp->clk_config) {
rc = -ENOMEM;
mp->num_clk = 0;
@@ -95,7 +114,7 @@
}
static int mdss_smmu_clk_register(struct platform_device *pdev,
- struct dss_module_power *mp)
+ struct mdss_module_power *mp)
{
int i, ret;
struct clk *clk;
@@ -123,7 +142,7 @@
bool enable)
{
int rc = 0;
- struct dss_module_power *mp;
+ struct mdss_module_power *mp;
if (!mdss_smmu)
return -EINVAL;
@@ -134,27 +153,27 @@
return 0;
if (enable) {
- rc = msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, true);
+ rc = msm_mdss_enable_vreg(mp->vreg_config, mp->num_vreg, true);
if (rc) {
pr_err("vreg enable failed - rc:%d\n", rc);
goto end;
}
mdss_update_reg_bus_vote(mdss_smmu->reg_bus_clt,
VOTE_INDEX_LOW);
- rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, true);
+ rc = msm_mdss_enable_clk(mp->clk_config, mp->num_clk, true);
if (rc) {
pr_err("clock enable failed - rc:%d\n", rc);
mdss_update_reg_bus_vote(mdss_smmu->reg_bus_clt,
VOTE_INDEX_DISABLE);
- msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg,
+ msm_mdss_enable_vreg(mp->vreg_config, mp->num_vreg,
false);
goto end;
}
} else {
- msm_dss_enable_clk(mp->clk_config, mp->num_clk, false);
+ msm_mdss_enable_clk(mp->clk_config, mp->num_clk, false);
mdss_update_reg_bus_vote(mdss_smmu->reg_bus_clt,
VOTE_INDEX_DISABLE);
- msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, false);
+ msm_mdss_enable_vreg(mp->vreg_config, mp->num_vreg, false);
}
end:
return rc;
@@ -655,7 +674,10 @@
parent = dev->of_node;
for_each_child_of_node(parent, child) {
- if (is_mdss_smmu_compatible_device(child->name))
+ char name[MDSS_SMMU_COMPAT_STR_LEN] = {};
+
+ strlcpy(name, child->name, sizeof(name));
+ if (is_mdss_smmu_compatible_device(name))
of_platform_device_create(child, NULL, dev);
}
}
@@ -704,8 +726,7 @@
int rc = 0;
struct mdss_smmu_domain smmu_domain;
const struct of_device_id *match;
- struct dss_module_power *mp;
- int disable_htw = 1;
+ struct mdss_module_power *mp;
char name[MAX_CLIENT_NAME_LEN];
const __be32 *address = NULL, *size = NULL;
@@ -733,7 +754,7 @@
* For old iommu driver we query the context bank device
* rather than getting it from dt.
*/
- dev = msm_iommu_get_ctx(smmu_domain.ctx_name);
+ dev = mdss_mmu_get_ctx(smmu_domain.ctx_name);
if (!dev) {
pr_err("Invalid SMMU ctx for domain:%d\n",
smmu_domain.domain);
@@ -743,13 +764,13 @@
mdss_smmu = &mdata->mdss_smmu[smmu_domain.domain];
mp = &mdss_smmu->mp;
- memset(mp, 0, sizeof(struct dss_module_power));
+ memset(mp, 0, sizeof(struct mdss_module_power));
if (of_find_property(pdev->dev.of_node,
"gdsc-mmagic-mdss-supply", NULL)) {
mp->vreg_config = devm_kzalloc(&pdev->dev,
- sizeof(struct dss_vreg), GFP_KERNEL);
+ sizeof(struct mdss_vreg), GFP_KERNEL);
if (!mp->vreg_config)
return -ENOMEM;
@@ -758,7 +779,7 @@
mp->num_vreg = 1;
}
- rc = msm_dss_config_vreg(&pdev->dev, mp->vreg_config,
+ rc = msm_mdss_config_vreg(&pdev->dev, mp->vreg_config,
mp->num_vreg, true);
if (rc) {
pr_err("vreg config failed rc=%d\n", rc);
@@ -769,7 +790,7 @@
if (rc) {
pr_err("smmu clk register failed for domain[%d] with err:%d\n",
smmu_domain.domain, rc);
- msm_dss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg,
+ msm_mdss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg,
false);
return rc;
}
@@ -778,7 +799,7 @@
mdss_smmu->reg_bus_clt = mdss_reg_bus_vote_client_create(name);
if (IS_ERR(mdss_smmu->reg_bus_clt)) {
pr_err("mdss bus client register failed\n");
- msm_dss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg,
+ msm_mdss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg,
false);
return PTR_ERR(mdss_smmu->reg_bus_clt);
}
@@ -791,7 +812,7 @@
}
mdss_smmu->mmu_mapping = arm_iommu_create_mapping(
- msm_iommu_get_bus(dev), smmu_domain.start, smmu_domain.size);
+ mdss_mmu_get_bus(dev), smmu_domain.start, smmu_domain.size);
if (IS_ERR(mdss_smmu->mmu_mapping)) {
pr_err("iommu create mapping failed for domain[%d]\n",
smmu_domain.domain);
@@ -799,13 +820,6 @@
goto disable_power;
}
- rc = iommu_domain_set_attr(mdss_smmu->mmu_mapping->domain,
- DOMAIN_ATTR_COHERENT_HTW_DISABLE, &disable_htw);
- if (rc) {
- pr_err("couldn't disable coherent HTW\n");
- goto release_mapping;
- }
-
if (smmu_domain.domain == MDSS_IOMMU_DOMAIN_SECURE ||
smmu_domain.domain == MDSS_IOMMU_DOMAIN_ROT_SECURE) {
int secure_vmid = VMID_CP_PIXEL;
@@ -848,7 +862,7 @@
bus_client_destroy:
mdss_reg_bus_vote_client_destroy(mdss_smmu->reg_bus_clt);
mdss_smmu->reg_bus_clt = NULL;
- msm_dss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg,
+ msm_mdss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg,
false);
return rc;
}
diff --git a/drivers/video/fbdev/msm/mdss_smmu.h b/drivers/video/fbdev/msm/mdss_smmu.h
index be2a55f..091af3b 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.h
+++ b/drivers/video/fbdev/msm/mdss_smmu.h
@@ -23,6 +23,7 @@
#include "mdss_debug.h"
#define MDSS_SMMU_COMPATIBLE "qcom,smmu"
+#define MDSS_SMMU_COMPAT_STR_LEN 10
#define SMMU_CBN_FSYNR1 0x6c
struct mdss_iommu_map_type {
diff --git a/drivers/video/fbdev/msm/mdss_sync.c b/drivers/video/fbdev/msm/mdss_sync.c
new file mode 100644
index 0000000..ed611e7
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_sync.c
@@ -0,0 +1,453 @@
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/fence.h>
+#include <linux/sync_file.h>
+
+#include "mdss_sync.h"
+
+#define MDSS_SYNC_NAME_SIZE 64
+#define MDSS_SYNC_DRIVER_NAME "mdss"
+
+/**
+ * struct mdss_fence - sync fence context
+ * @base: base sync fence object
+ * @name: name of this sync fence
+ * @fence_list: linked list of outstanding sync fence
+ */
+struct mdss_fence {
+ struct fence base;
+ char name[MDSS_SYNC_NAME_SIZE];
+ struct list_head fence_list;
+};
+
+/**
+ * struct mdss_timeline - sync timeline context
+ * @kref: reference count of timeline
+ * @lock: serialization lock for timeline and fence update
+ * @name: name of timeline
+ * @fence_name: fence name prefix
+ * @next_value: next commit sequence number
+ * @value: current retired sequence number
+ * @context: fence context identifier
+ * @fence_list_head: linked list of outstanding sync fence
+ */
+struct mdss_timeline {
+ struct kref kref;
+ spinlock_t lock;
+ char name[MDSS_SYNC_NAME_SIZE];
+ u32 next_value;
+ u32 value;
+ u64 context;
+ struct list_head fence_list_head;
+};
+
+/*
+ * to_mdss_fence - get mdss fence from fence base object
+ * @fence: Pointer to fence base object
+ */
+static struct mdss_fence *to_mdss_fence(struct fence *fence)
+{
+ return container_of(fence, struct mdss_fence, base);
+}
+
+/*
+ * to_mdss_timeline - get mdss timeline from fence base object
+ * @fence: Pointer to fence base object
+ */
+static struct mdss_timeline *to_mdss_timeline(struct fence *fence)
+{
+ return container_of(fence->lock, struct mdss_timeline, lock);
+}
+
+/*
+ * mdss_free_timeline - Free the given timeline object
+ * @kref: Pointer to timeline kref object.
+ */
+static void mdss_free_timeline(struct kref *kref)
+{
+ struct mdss_timeline *tl =
+ container_of(kref, struct mdss_timeline, kref);
+
+ kfree(tl);
+}
+
+/*
+ * mdss_put_timeline - Put the given timeline object
+ * @tl: Pointer to timeline object.
+ */
+static void mdss_put_timeline(struct mdss_timeline *tl)
+{
+ if (!tl) {
+ pr_err("invalid parameters\n");
+ return;
+ }
+
+ kref_put(&tl->kref, mdss_free_timeline);
+}
+
+/*
+ * mdss_get_timeline - Get the given timeline object
+ * @tl: Pointer to timeline object.
+ */
+static void mdss_get_timeline(struct mdss_timeline *tl)
+{
+ if (!tl) {
+ pr_err("invalid parameters\n");
+ return;
+ }
+
+ kref_get(&tl->kref);
+}
+
+static const char *mdss_fence_get_driver_name(struct fence *fence)
+{
+ return MDSS_SYNC_DRIVER_NAME;
+}
+
+static const char *mdss_fence_get_timeline_name(struct fence *fence)
+{
+ struct mdss_timeline *tl = to_mdss_timeline(fence);
+
+ return tl->name;
+}
+
+static bool mdss_fence_enable_signaling(struct fence *fence)
+{
+ return true;
+}
+
+static bool mdss_fence_signaled(struct fence *fence)
+{
+ struct mdss_timeline *tl = to_mdss_timeline(fence);
+ bool status;
+
+ status = ((s32) (tl->value - fence->seqno)) >= 0;
+ pr_debug("status:%d fence seq:%d and timeline:%s:%d next %d\n",
+ status, fence->seqno, tl->name,
+ tl->value, tl->next_value);
+ return status;
+}
+
+static void mdss_fence_release(struct fence *fence)
+{
+ struct mdss_fence *f = to_mdss_fence(fence);
+ unsigned long flags;
+
+ spin_lock_irqsave(fence->lock, flags);
+ if (!list_empty(&f->fence_list))
+ list_del(&f->fence_list);
+ spin_unlock_irqrestore(fence->lock, flags);
+ mdss_put_timeline(to_mdss_timeline(fence));
+ kfree_rcu(f, base.rcu);
+}
+
+static void mdss_fence_value_str(struct fence *fence, char *str, int size)
+{
+ snprintf(str, size, "%u", fence->seqno);
+}
+
+static void mdss_fence_timeline_value_str(struct fence *fence, char *str,
+ int size)
+{
+ struct mdss_timeline *tl = to_mdss_timeline(fence);
+
+ snprintf(str, size, "%u", tl->value);
+}
+
+static struct fence_ops mdss_fence_ops = {
+ .get_driver_name = mdss_fence_get_driver_name,
+ .get_timeline_name = mdss_fence_get_timeline_name,
+ .enable_signaling = mdss_fence_enable_signaling,
+ .signaled = mdss_fence_signaled,
+ .wait = fence_default_wait,
+ .release = mdss_fence_release,
+ .fence_value_str = mdss_fence_value_str,
+ .timeline_value_str = mdss_fence_timeline_value_str,
+};
+
+/*
+ * mdss_create_timeline - Create timeline object with the given name
+ * @name: Pointer to name character string.
+ */
+struct mdss_timeline *mdss_create_timeline(const char *name)
+{
+ struct mdss_timeline *tl;
+
+ if (!name) {
+ pr_err("invalid parameters\n");
+ return NULL;
+ }
+
+ tl = kzalloc(sizeof(struct mdss_timeline), GFP_KERNEL);
+ if (!tl)
+ return NULL;
+
+ kref_init(&tl->kref);
+ snprintf(tl->name, sizeof(tl->name), "%s", name);
+ spin_lock_init(&tl->lock);
+ tl->context = fence_context_alloc(1);
+ INIT_LIST_HEAD(&tl->fence_list_head);
+
+ return tl;
+}
+
+/*
+ * mdss_destroy_timeline - Destroy the given timeline object
+ * @tl: Pointer to timeline object.
+ */
+void mdss_destroy_timeline(struct mdss_timeline *tl)
+{
+ mdss_put_timeline(tl);
+}
+
+/*
+ * mdss_inc_timeline_locked - Increment timeline by given amount
+ * @tl: Pointer to timeline object.
+ * @increment: the amount to increase the timeline by.
+ */
+static int mdss_inc_timeline_locked(struct mdss_timeline *tl,
+ int increment)
+{
+ struct mdss_fence *f, *next;
+
+ tl->value += increment;
+ list_for_each_entry_safe(f, next, &tl->fence_list_head, fence_list) {
+ if (fence_is_signaled_locked(&f->base)) {
+ pr_debug("%s signaled\n", f->name);
+ list_del_init(&f->fence_list);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * mdss_resync_timeline - Resync timeline to last committed value
+ * @tl: Pointer to timeline object.
+ */
+void mdss_resync_timeline(struct mdss_timeline *tl)
+{
+ unsigned long flags;
+ s32 val;
+
+ if (!tl) {
+ pr_err("invalid parameters\n");
+ return;
+ }
+
+ spin_lock_irqsave(&tl->lock, flags);
+ val = tl->next_value - tl->value;
+ if (val > 0) {
+ pr_warn("flush %s:%d\n", tl->name, val);
+ mdss_inc_timeline_locked(tl, val);
+ }
+ spin_unlock_irqrestore(&tl->lock, flags);
+}
+
+/*
+ * mdss_get_sync_fence - Create fence object from the given timeline
+ * @tl: Pointer to timeline object
+ * @timestamp: Pointer to timestamp of the returned fence. Null if not required.
+ * Return: pointer fence created on give time line.
+ */
+struct mdss_fence *mdss_get_sync_fence(
+ struct mdss_timeline *tl, const char *fence_name,
+ u32 *timestamp, int offset)
+{
+ struct mdss_fence *f;
+ unsigned long flags;
+ u32 val;
+
+ if (!tl) {
+ pr_err("invalid parameters\n");
+ return NULL;
+ }
+
+ f = kzalloc(sizeof(struct mdss_fence), GFP_KERNEL);
+ if (!f)
+ return NULL;
+
+ INIT_LIST_HEAD(&f->fence_list);
+ spin_lock_irqsave(&tl->lock, flags);
+ val = tl->next_value + offset;
+ tl->next_value += 1;
+ fence_init(&f->base, &mdss_fence_ops, &tl->lock, tl->context, val);
+ list_add_tail(&f->fence_list, &tl->fence_list_head);
+ mdss_get_timeline(tl);
+ spin_unlock_irqrestore(&tl->lock, flags);
+ snprintf(f->name, sizeof(f->name), "%s_%u", fence_name, val);
+
+ if (timestamp)
+ *timestamp = val;
+
+ pr_debug("fence created at val=%u tl->name %s next_value %d value %d offset %d\n",
+ val, tl->name, tl->next_value, tl->value, offset);
+
+ return (struct mdss_fence *) &f->base;
+}
+
+/*
+ * mdss_inc_timeline - Increment timeline by given amount
+ * @tl: Pointer to timeline object.
+ * @increment: the amount to increase the timeline by.
+ */
+int mdss_inc_timeline(struct mdss_timeline *tl, int increment)
+{
+ unsigned long flags;
+ int rc;
+
+ if (!tl) {
+ pr_err("invalid parameters\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&tl->lock, flags);
+ rc = mdss_inc_timeline_locked(tl, increment);
+ spin_unlock_irqrestore(&tl->lock, flags);
+
+ return rc;
+}
+
+/*
+ * mdss_get_timeline_commit_ts - Return commit tick of given timeline
+ * @tl: Pointer to timeline object.
+ */
+u32 mdss_get_timeline_commit_ts(struct mdss_timeline *tl)
+{
+ if (!tl) {
+ pr_err("invalid parameters\n");
+ return 0;
+ }
+
+ return tl->next_value;
+}
+
+/*
+ * mdss_get_timeline_retire_ts - Return retire tick of given timeline
+ * @tl: Pointer to timeline object.
+ */
+u32 mdss_get_timeline_retire_ts(struct mdss_timeline *tl)
+{
+ if (!tl) {
+ pr_err("invalid parameters\n");
+ return 0;
+ }
+
+ return tl->value;
+}
+
+/*
+ * mdss_put_sync_fence - Destroy given fence object
+ * @fence: Pointer to fence object.
+ */
+void mdss_put_sync_fence(struct mdss_fence *fence)
+{
+ if (!fence) {
+ pr_err("invalid parameters\n");
+ return;
+ }
+
+ fence_put((struct fence *) fence);
+}
+
+/*
+ * mdss_wait_sync_fence - Wait until fence signal or timeout
+ * @fence: Pointer to fence object.
+ * @timeout: maximum wait time, in msec, for fence to signal.
+ */
+int mdss_wait_sync_fence(struct mdss_fence *fence,
+ long timeout)
+{
+ int rc;
+
+ if (!fence) {
+ pr_err("invalid parameters\n");
+ return -EINVAL;
+ }
+
+ rc = fence_wait_timeout((struct fence *) fence, false,
+ msecs_to_jiffies(timeout));
+ if (rc > 0) {
+ pr_debug("fence signaled\n");
+ rc = 0;
+ } else if (rc == 0) {
+ pr_debug("fence timeout\n");
+ rc = -ETIMEDOUT;
+ }
+
+ return rc;
+}
+
+/*
+ * mdss_get_fd_sync_fence - Get fence object of given file descriptor
+ * @fd: File description of fence object.
+ */
+struct mdss_fence *mdss_get_fd_sync_fence(int fd)
+{
+ return (struct mdss_fence *) sync_file_get_fence(fd);
+}
+
+/*
+ * mdss_get_sync_fence_fd - Get file descriptor of given fence object
+ * @fence: Pointer to fence object.
+ * Return: File descriptor on success, or error code on error
+ */
+int mdss_get_sync_fence_fd(struct mdss_fence *fence)
+{
+ int fd;
+ struct sync_file *sync_file;
+
+ if (!fence) {
+ pr_err("invalid parameters\n");
+ return -EINVAL;
+ }
+
+ fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fd < 0) {
+ pr_err("fail to get unused fd\n");
+ return fd;
+ }
+
+ sync_file = sync_file_create((struct fence *) fence);
+ if (!sync_file) {
+ put_unused_fd(fd);
+ pr_err("failed to create sync file\n");
+ return -ENOMEM;
+ }
+
+ fd_install(fd, sync_file->file);
+
+ return fd;
+}
+
+/*
+ * mdss_put_sync_fence - Destroy given fence object
+ * @fence: Pointer to fence object.
+ * Return: fence name
+ */
+const char *mdss_get_sync_fence_name(struct mdss_fence *fence)
+{
+ if (!fence) {
+ pr_err("invalid parameters\n");
+ return NULL;
+ }
+
+ return fence->name;
+}
diff --git a/drivers/video/fbdev/msm/mdss_sync.h b/drivers/video/fbdev/msm/mdss_sync.h
new file mode 100644
index 0000000..39a1aa7b
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_sync.h
@@ -0,0 +1,122 @@
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MDSS_SYNC_H
+#define MDSS_SYNC_H
+
+#include <linux/types.h>
+#include <linux/errno.h>
+
+struct mdss_fence;
+struct mdss_timeline;
+
+#if defined(CONFIG_SYNC_FILE)
+struct mdss_timeline *mdss_create_timeline(const char *name);
+
+void mdss_destroy_timeline(struct mdss_timeline *tl);
+
+struct mdss_fence *mdss_get_sync_fence(
+ struct mdss_timeline *tl, const char *fence_name,
+ u32 *timestamp, int offset);
+
+void mdss_resync_timeline(struct mdss_timeline *tl);
+
+u32 mdss_get_timeline_commit_ts(struct mdss_timeline *tl);
+
+u32 mdss_get_timeline_retire_ts(struct mdss_timeline *tl);
+
+int mdss_inc_timeline(struct mdss_timeline *tl, int increment);
+
+void mdss_put_sync_fence(struct mdss_fence *fence);
+
+int mdss_wait_sync_fence(struct mdss_fence *fence,
+ long timeout);
+
+struct mdss_fence *mdss_get_fd_sync_fence(int fd);
+
+int mdss_get_sync_fence_fd(struct mdss_fence *fence);
+const char *mdss_get_sync_fence_name(struct mdss_fence *fence);
+#else
+static inline
+struct mdss_timeline *mdss_create_timeline(const char *name)
+{
+ return NULL;
+}
+
+static inline
+void mdss_destroy_timeline(struct mdss_timeline *tl)
+{
+}
+
+static inline
+struct mdss_fence *mdss_get_sync_fence(
+ struct mdss_timeline *tl, const char *fence_name,
+ u32 *timestamp, int offset)
+{
+ return NULL;
+}
+
+static inline
+void mdss_resync_timeline(struct mdss_timeline *tl)
+{
+}
+
+static inline
+int mdss_inc_timeline(struct mdss_timeline *tl, int increment)
+{
+ return 0;
+}
+
+static inline
+u32 mdss_get_timeline_commit_ts(struct mdss_timeline *tl)
+{
+ return 0;
+}
+
+static inline
+u32 mdss_get_timeline_retire_ts(struct mdss_timeline *tl)
+{
+ return 0;
+}
+
+static inline
+void mdss_put_sync_fence(struct mdss_fence *fence)
+{
+}
+
+static inline
+int mdss_wait_sync_fence(struct mdss_fence *fence,
+ long timeout)
+{
+ return 0;
+}
+
+static inline
+struct mdss_fence *mdss_get_fd_sync_fence(int fd)
+{
+ return NULL;
+}
+
+static inline
+int mdss_get_sync_fence_fd(struct mdss_fence *fence)
+{
+ return -EBADF;
+}
+const char *mdss_get_sync_fence_name(struct mdss_fence *fence)
+{
+ return NULL;
+}
+}
+#endif
+
+#endif /* MDSS_SYNC_H */
diff --git a/drivers/video/fbdev/msm/mdss_wb.c b/drivers/video/fbdev/msm/mdss_wb.c
index 4b509ec..c8c5d47 100644
--- a/drivers/video/fbdev/msm/mdss_wb.c
+++ b/drivers/video/fbdev/msm/mdss_wb.c
@@ -22,7 +22,8 @@
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/version.h>
-#include <linux/switch.h>
+#include <linux/extcon.h>
+#include <linux/module.h>
#include "mdss_panel.h"
#include "mdss_wb.h"
@@ -94,6 +95,11 @@
return 0;
}
+static const unsigned int mdss_wb_disp_supported_cable[] = {
+ EXTCON_DISP_HMD + 1, /* For WFD */
+ EXTCON_NONE,
+};
+
static int mdss_wb_dev_init(struct mdss_wb_ctrl *wb_ctrl)
{
int rc = 0;
@@ -103,8 +109,11 @@
return -ENODEV;
}
+ memset(&wb_ctrl->sdev, 0x0, sizeof(wb_ctrl->sdev));
+ wb_ctrl->sdev.supported_cable = mdss_wb_disp_supported_cable;
+ wb_ctrl->sdev.dev.parent = &wb_ctrl->pdev->dev;
wb_ctrl->sdev.name = "wfd";
- rc = switch_dev_register(&wb_ctrl->sdev);
+ rc = extcon_dev_register(&wb_ctrl->sdev);
if (rc) {
pr_err("Failed to setup switch dev for writeback panel");
return rc;
@@ -120,7 +129,7 @@
return -ENODEV;
}
- switch_dev_unregister(&wb_ctrl->sdev);
+ extcon_dev_unregister(&wb_ctrl->sdev);
return 0;
}
diff --git a/drivers/video/fbdev/msm/mdss_wb.h b/drivers/video/fbdev/msm/mdss_wb.h
index 9cc88b6..010a123 100644
--- a/drivers/video/fbdev/msm/mdss_wb.h
+++ b/drivers/video/fbdev/msm/mdss_wb.h
@@ -14,12 +14,12 @@
#ifndef MDSS_WB_H
#define MDSS_WB_H
-#include <linux/switch.h>
+#include <linux/extcon.h>
struct mdss_wb_ctrl {
struct platform_device *pdev;
struct mdss_panel_data pdata;
- struct switch_dev sdev;
+ struct extcon_dev sdev;
};
#endif
diff --git a/drivers/video/fbdev/msm/mhl_sii8334.c b/drivers/video/fbdev/msm/mhl_sii8334.c
index 5d0ac99..cf45eb6 100644
--- a/drivers/video/fbdev/msm/mhl_sii8334.c
+++ b/drivers/video/fbdev/msm/mhl_sii8334.c
@@ -240,7 +240,7 @@
{
int i, rc = 0;
struct device_node *of_node = NULL;
- struct dss_gpio *temp_gpio = NULL;
+ struct mdss_gpio *temp_gpio = NULL;
struct platform_device *hdmi_pdev = NULL;
struct device_node *hdmi_tx_node = NULL;
int dt_gpio;
@@ -262,7 +262,7 @@
/* GPIOs */
temp_gpio = NULL;
- temp_gpio = devm_kzalloc(dev, sizeof(struct dss_gpio), GFP_KERNEL);
+ temp_gpio = devm_kzalloc(dev, sizeof(struct mdss_gpio), GFP_KERNEL);
pr_debug("%s: gpios allocd\n", __func__);
if (!(temp_gpio)) {
pr_err("%s: can't alloc %d gpio mem\n", __func__, i);
@@ -283,7 +283,7 @@
/* PWR */
temp_gpio = NULL;
- temp_gpio = devm_kzalloc(dev, sizeof(struct dss_gpio), GFP_KERNEL);
+ temp_gpio = devm_kzalloc(dev, sizeof(struct mdss_gpio), GFP_KERNEL);
pr_debug("%s: gpios allocd\n", __func__);
if (!(temp_gpio)) {
pr_err("%s: can't alloc %d gpio mem\n", __func__, i);
@@ -303,7 +303,7 @@
/* INTR */
temp_gpio = NULL;
- temp_gpio = devm_kzalloc(dev, sizeof(struct dss_gpio), GFP_KERNEL);
+ temp_gpio = devm_kzalloc(dev, sizeof(struct mdss_gpio), GFP_KERNEL);
pr_debug("%s: gpios allocd\n", __func__);
if (!(temp_gpio)) {
pr_err("%s: can't alloc %d gpio mem\n", __func__, i);
@@ -1716,7 +1716,7 @@
static int mhl_gpio_config(struct mhl_tx_ctrl *mhl_ctrl, int on)
{
int ret;
- struct dss_gpio *temp_reset_gpio, *temp_intr_gpio;
+ struct mdss_gpio *temp_reset_gpio, *temp_intr_gpio;
/* caused too many line spills */
temp_reset_gpio = mhl_ctrl->pdata->gpios[MHL_TX_RESET_GPIO];
diff --git a/drivers/video/fbdev/msm/msm_dba/adv7533.c b/drivers/video/fbdev/msm/msm_dba/adv7533.c
index b628e1a..5b44a49 100644
--- a/drivers/video/fbdev/msm/msm_dba/adv7533.c
+++ b/drivers/video/fbdev/msm/msm_dba/adv7533.c
@@ -125,7 +125,7 @@
struct pinctrl_state *pinctrl_state_suspend;
bool audio;
bool disable_gpios;
- struct dss_module_power power_data;
+ struct mdss_module_power power_data;
bool hdcp_enabled;
bool cec_enabled;
bool is_power_on;
@@ -433,7 +433,7 @@
}
static void adv7533_parse_vreg_dt(struct device *dev,
- struct dss_module_power *mp)
+ struct mdss_module_power *mp)
{
int i, rc = 0;
int dt_vreg_total = 0;
@@ -449,7 +449,7 @@
goto end;
}
mp->num_vreg = dt_vreg_total;
- mp->vreg_config = devm_kzalloc(dev, sizeof(struct dss_vreg) *
+ mp->vreg_config = devm_kzalloc(dev, sizeof(struct mdss_vreg) *
dt_vreg_total, GFP_KERNEL);
if (!mp->vreg_config)
goto end;
@@ -1471,7 +1471,7 @@
static int adv7533_config_vreg(struct adv7533 *pdata, int enable)
{
int rc = 0;
- struct dss_module_power *power_data = NULL;
+ struct mdss_module_power *power_data = NULL;
if (!pdata) {
pr_err("invalid input\n");
@@ -1486,7 +1486,7 @@
}
if (enable) {
- rc = msm_dss_config_vreg(&pdata->i2c_client->dev,
+ rc = msm_mdss_config_vreg(&pdata->i2c_client->dev,
power_data->vreg_config,
power_data->num_vreg, 1);
if (rc) {
@@ -1495,7 +1495,7 @@
goto exit;
}
} else {
- rc = msm_dss_config_vreg(&pdata->i2c_client->dev,
+ rc = msm_mdss_config_vreg(&pdata->i2c_client->dev,
power_data->vreg_config,
power_data->num_vreg, 0);
if (rc) {
@@ -1512,7 +1512,7 @@
static int adv7533_enable_vreg(struct adv7533 *pdata, int enable)
{
int rc = 0;
- struct dss_module_power *power_data = NULL;
+ struct mdss_module_power *power_data = NULL;
if (!pdata) {
pr_err("invalid input\n");
@@ -1527,7 +1527,7 @@
}
if (enable) {
- rc = msm_dss_enable_vreg(power_data->vreg_config,
+ rc = msm_mdss_enable_vreg(power_data->vreg_config,
power_data->num_vreg, 1);
if (rc) {
pr_err("%s: Failed to enable vreg. Err=%d\n",
@@ -1535,7 +1535,7 @@
goto exit;
}
} else {
- rc = msm_dss_enable_vreg(power_data->vreg_config,
+ rc = msm_mdss_enable_vreg(power_data->vreg_config,
power_data->num_vreg, 0);
if (rc) {
pr_err("%s: Failed to disable vreg. Err=%d\n",
diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
index 05a7fc0..39d26a4 100644
--- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c
+++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
@@ -2314,7 +2314,7 @@
if ((ctrl->ctrl_state & CTRL_STATE_DSI_ACTIVE) &&
(i != DSI_CORE_PM))
continue;
- rc = msm_dss_enable_vreg(
+ rc = msm_mdss_enable_vreg(
sdata->power_data[i].vreg_config,
sdata->power_data[i].num_vreg, 0);
if (rc) {
@@ -2374,7 +2374,7 @@
(!pdata->panel_info.cont_splash_enabled) &&
(i != DSI_CORE_PM))
continue;
- rc = msm_dss_enable_vreg(
+ rc = msm_mdss_enable_vreg(
sdata->power_data[i].vreg_config,
sdata->power_data[i].num_vreg, 1);
if (rc) {
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index e4b48f3..c56253a 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -1253,7 +1253,7 @@
/* Lock all pages first so we can lock the extent safely. */
ret = io_ctl_prepare_pages(io_ctl, inode, 0);
if (ret)
- goto out;
+ goto out_unlock;
lock_extent_bits(&BTRFS_I(inode)->io_tree, 0, i_size_read(inode) - 1,
&cached_state);
@@ -1346,6 +1346,7 @@
out_nospc:
cleanup_write_cache_enospc(inode, io_ctl, &cached_state, &bitmap_list);
+out_unlock:
if (block_group && (block_group->flags & BTRFS_BLOCK_GROUP_DATA))
up_write(&block_group->data_rwsem);
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 1493ceb..ec03cf6 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -114,6 +114,10 @@
int who = arg;
type = PIDTYPE_PID;
if (who < 0) {
+ /* avoid overflow below */
+ if (who == INT_MIN)
+ return;
+
type = PIDTYPE_PGID;
who = -who;
}
diff --git a/fs/nfs_common/grace.c b/fs/nfs_common/grace.c
index fd8c9a5..77d136a 100644
--- a/fs/nfs_common/grace.c
+++ b/fs/nfs_common/grace.c
@@ -30,7 +30,11 @@
struct list_head *grace_list = net_generic(net, grace_net_id);
spin_lock(&grace_lock);
- list_add(&lm->list, grace_list);
+ if (list_empty(&lm->list))
+ list_add(&lm->list, grace_list);
+ else
+ WARN(1, "double list_add attempt detected in net %x %s\n",
+ net->ns.inum, (net == &init_net) ? "(init_net)" : "");
spin_unlock(&grace_lock);
}
EXPORT_SYMBOL_GPL(locks_start_grace);
@@ -104,7 +108,9 @@
{
struct list_head *grace_list = net_generic(net, grace_net_id);
- BUG_ON(!list_empty(grace_list));
+ WARN_ONCE(!list_empty(grace_list),
+ "net %x %s: grace_list is not empty\n",
+ net->ns.inum, __func__);
}
static struct pernet_operations grace_net_ops = {
diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c
index 62469c6..81c018e 100644
--- a/fs/nfsd/auth.c
+++ b/fs/nfsd/auth.c
@@ -60,6 +60,9 @@
else
gi->gid[i] = rqgi->gid[i];
}
+
+ /* Each thread allocates its own gi, no race */
+ groups_sort(gi);
} else {
gi = get_group_info(rqgi);
}
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 9ebb2d7..f463c4e 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -63,12 +63,16 @@
static const stateid_t currentstateid = {
.si_generation = 1,
};
+static const stateid_t close_stateid = {
+ .si_generation = 0xffffffffU,
+};
static u64 current_sessionid = 1;
#define ZERO_STATEID(stateid) (!memcmp((stateid), &zero_stateid, sizeof(stateid_t)))
#define ONE_STATEID(stateid) (!memcmp((stateid), &one_stateid, sizeof(stateid_t)))
#define CURRENT_STATEID(stateid) (!memcmp((stateid), ¤tstateid, sizeof(stateid_t)))
+#define CLOSE_STATEID(stateid) (!memcmp((stateid), &close_stateid, sizeof(stateid_t)))
/* forward declarations */
static bool check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner);
@@ -4866,7 +4870,8 @@
struct nfs4_stid *s;
__be32 status = nfserr_bad_stateid;
- if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
+ if (ZERO_STATEID(stateid) || ONE_STATEID(stateid) ||
+ CLOSE_STATEID(stateid))
return status;
/* Client debugging aid. */
if (!same_clid(&stateid->si_opaque.so_clid, &cl->cl_clientid)) {
@@ -4924,7 +4929,8 @@
else if (typemask & NFS4_DELEG_STID)
typemask |= NFS4_REVOKED_DELEG_STID;
- if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
+ if (ZERO_STATEID(stateid) || ONE_STATEID(stateid) ||
+ CLOSE_STATEID(stateid))
return nfserr_bad_stateid;
status = lookup_clientid(&stateid->si_opaque.so_clid, cstate, nn);
if (status == nfserr_stale_clientid) {
@@ -5175,15 +5181,9 @@
status = nfsd4_check_seqid(cstate, sop, seqid);
if (status)
return status;
- if (stp->st_stid.sc_type == NFS4_CLOSED_STID
- || stp->st_stid.sc_type == NFS4_REVOKED_DELEG_STID)
- /*
- * "Closed" stateid's exist *only* to return
- * nfserr_replay_me from the previous step, and
- * revoked delegations are kept only for free_stateid.
- */
- return nfserr_bad_stateid;
- mutex_lock(&stp->st_mutex);
+ status = nfsd4_lock_ol_stateid(stp);
+ if (status != nfs_ok)
+ return status;
status = check_stateid_generation(stateid, &stp->st_stid.sc_stateid, nfsd4_has_session(cstate));
if (status == nfs_ok)
status = nfs4_check_fh(current_fh, &stp->st_stid);
@@ -5407,6 +5407,11 @@
nfsd4_close_open_stateid(stp);
mutex_unlock(&stp->st_mutex);
+ /* See RFC5661 sectionm 18.2.4 */
+ if (stp->st_stid.sc_client->cl_minorversion)
+ memcpy(&close->cl_stateid, &close_stateid,
+ sizeof(close->cl_stateid));
+
/* put reference from nfs4_preprocess_seqid_op */
nfs4_put_stid(&stp->st_stid);
out:
@@ -7007,6 +7012,10 @@
INIT_LIST_HEAD(&nn->sessionid_hashtbl[i]);
nn->conf_name_tree = RB_ROOT;
nn->unconf_name_tree = RB_ROOT;
+ nn->boot_time = get_seconds();
+ nn->grace_ended = false;
+ nn->nfsd4_manager.block_opens = true;
+ INIT_LIST_HEAD(&nn->nfsd4_manager.list);
INIT_LIST_HEAD(&nn->client_lru);
INIT_LIST_HEAD(&nn->close_lru);
INIT_LIST_HEAD(&nn->del_recall_lru);
@@ -7064,9 +7073,6 @@
ret = nfs4_state_create_net(net);
if (ret)
return ret;
- nn->boot_time = get_seconds();
- nn->grace_ended = false;
- nn->nfsd4_manager.block_opens = true;
locks_start_grace(net, &nn->nfsd4_manager);
nfsd4_client_tracking_init(net);
printk(KERN_INFO "NFSD: starting %ld-second grace period (net %p)\n",
diff --git a/fs/orangefs/devorangefs-req.c b/fs/orangefs/devorangefs-req.c
index fe2cbeb..939aa06 100644
--- a/fs/orangefs/devorangefs-req.c
+++ b/fs/orangefs/devorangefs-req.c
@@ -161,7 +161,7 @@
struct orangefs_kernel_op_s *op, *temp;
__s32 proto_ver = ORANGEFS_KERNEL_PROTO_VERSION;
static __s32 magic = ORANGEFS_DEVREQ_MAGIC;
- struct orangefs_kernel_op_s *cur_op = NULL;
+ struct orangefs_kernel_op_s *cur_op;
unsigned long ret;
/* We do not support blocking IO. */
@@ -181,6 +181,7 @@
}
restart:
+ cur_op = NULL;
/* Get next op (if any) from top of list. */
spin_lock(&orangefs_request_list_lock);
list_for_each_entry_safe(op, temp, &orangefs_request_list, list) {
diff --git a/fs/orangefs/file.c b/fs/orangefs/file.c
index 02cc613..5b2cbe5 100644
--- a/fs/orangefs/file.c
+++ b/fs/orangefs/file.c
@@ -446,7 +446,7 @@
static ssize_t orangefs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
struct file *file = iocb->ki_filp;
- loff_t pos = *(&iocb->ki_pos);
+ loff_t pos = iocb->ki_pos;
ssize_t rc = 0;
BUG_ON(iocb->private);
@@ -485,9 +485,6 @@
}
}
- if (file->f_pos > i_size_read(file->f_mapping->host))
- orangefs_i_size_write(file->f_mapping->host, file->f_pos);
-
rc = generic_write_checks(iocb, iter);
if (rc <= 0) {
@@ -501,7 +498,7 @@
* pos to the end of the file, so we will wait till now to set
* pos...
*/
- pos = *(&iocb->ki_pos);
+ pos = iocb->ki_pos;
rc = do_readv_writev(ORANGEFS_IO_WRITE,
file,
diff --git a/fs/orangefs/orangefs-kernel.h b/fs/orangefs/orangefs-kernel.h
index 45dd8f2..f28381a 100644
--- a/fs/orangefs/orangefs-kernel.h
+++ b/fs/orangefs/orangefs-kernel.h
@@ -570,17 +570,6 @@
sys_attr.mask = ORANGEFS_ATTR_SYS_ALL_SETABLE; \
} while (0)
-static inline void orangefs_i_size_write(struct inode *inode, loff_t i_size)
-{
-#if BITS_PER_LONG == 32 && defined(CONFIG_SMP)
- inode_lock(inode);
-#endif
- i_size_write(inode, i_size);
-#if BITS_PER_LONG == 32 && defined(CONFIG_SMP)
- inode_unlock(inode);
-#endif
-}
-
static inline void orangefs_set_timeout(struct dentry *dentry)
{
unsigned long time = jiffies + orangefs_dcache_timeout_msecs*HZ/1000;
diff --git a/fs/orangefs/waitqueue.c b/fs/orangefs/waitqueue.c
index abcfa3f..f61b008 100644
--- a/fs/orangefs/waitqueue.c
+++ b/fs/orangefs/waitqueue.c
@@ -28,10 +28,10 @@
*/
void purge_waiting_ops(void)
{
- struct orangefs_kernel_op_s *op;
+ struct orangefs_kernel_op_s *op, *tmp;
spin_lock(&orangefs_request_list_lock);
- list_for_each_entry(op, &orangefs_request_list, list) {
+ list_for_each_entry_safe(op, tmp, &orangefs_request_list, list) {
gossip_debug(GOSSIP_WAIT_DEBUG,
"pvfs2-client-core: purging op tag %llu %s\n",
llu(op->tag),
diff --git a/fs/pipe.c b/fs/pipe.c
index 8e0d9f2..9faecf1 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -1018,13 +1018,19 @@
/*
* Currently we rely on the pipe array holding a power-of-2 number
- * of pages.
+ * of pages. Returns 0 on error.
*/
static inline unsigned int round_pipe_size(unsigned int size)
{
unsigned long nr_pages;
+ if (size < pipe_min_size)
+ size = pipe_min_size;
+
nr_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ if (nr_pages == 0)
+ return 0;
+
return roundup_pow_of_two(nr_pages) << PAGE_SHIFT;
}
@@ -1040,6 +1046,8 @@
long ret = 0;
size = round_pipe_size(arg);
+ if (size == 0)
+ return -EINVAL;
nr_pages = size >> PAGE_SHIFT;
if (!nr_pages)
@@ -1123,13 +1131,18 @@
int pipe_proc_fn(struct ctl_table *table, int write, void __user *buf,
size_t *lenp, loff_t *ppos)
{
+ unsigned int rounded_pipe_max_size;
int ret;
ret = proc_dointvec_minmax(table, write, buf, lenp, ppos);
if (ret < 0 || !write)
return ret;
- pipe_max_size = round_pipe_size(pipe_max_size);
+ rounded_pipe_max_size = round_pipe_size(pipe_max_size);
+ if (rounded_pipe_max_size == 0)
+ return -EINVAL;
+
+ pipe_max_size = rounded_pipe_max_size;
return ret;
}
diff --git a/fs/proc/array.c b/fs/proc/array.c
index c932ec4..794b52a 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -423,8 +423,11 @@
* safe because the task has stopped executing permanently.
*/
if (permitted && (task->flags & PF_DUMPCORE)) {
- eip = KSTK_EIP(task);
- esp = KSTK_ESP(task);
+ if (try_get_task_stack(task)) {
+ eip = KSTK_EIP(task);
+ esp = KSTK_ESP(task);
+ put_task_stack(task);
+ }
}
}
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index 1bfac28..f9246ac 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -2985,7 +2985,8 @@
pr_info("VFS: Dquot-cache hash table entries: %ld (order %ld,"
" %ld bytes)\n", nr_hash, order, (PAGE_SIZE << order));
- register_shrinker(&dqcache_shrinker);
+ if (register_shrinker(&dqcache_shrinker))
+ panic("Cannot register dquot shrinker");
return 0;
}
diff --git a/fs/reiserfs/bitmap.c b/fs/reiserfs/bitmap.c
index dc198bc..edc8ef7 100644
--- a/fs/reiserfs/bitmap.c
+++ b/fs/reiserfs/bitmap.c
@@ -513,9 +513,17 @@
"inode has negative prealloc blocks count.");
#endif
while (ei->i_prealloc_count > 0) {
- reiserfs_free_prealloc_block(th, inode, ei->i_prealloc_block);
- ei->i_prealloc_block++;
+ b_blocknr_t block_to_free;
+
+ /*
+ * reiserfs_free_prealloc_block can drop the write lock,
+ * which could allow another caller to free the same block.
+ * We can protect against it by modifying the prealloc
+ * state before calling it.
+ */
+ block_to_free = ei->i_prealloc_block++;
ei->i_prealloc_count--;
+ reiserfs_free_prealloc_block(th, inode, block_to_free);
dirty = 1;
}
if (dirty)
@@ -1128,7 +1136,7 @@
hint->prealloc_size = 0;
if (!hint->formatted_node && hint->preallocate) {
- if (S_ISREG(hint->inode->i_mode)
+ if (S_ISREG(hint->inode->i_mode) && !IS_PRIVATE(hint->inode)
&& hint->inode->i_size >=
REISERFS_SB(hint->th->t_super)->s_alloc_options.
preallocmin * hint->inode->i_sb->s_blocksize)
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
index 0a6ad4e..e101d70 100644
--- a/fs/reiserfs/super.c
+++ b/fs/reiserfs/super.c
@@ -2521,7 +2521,6 @@
return err;
if (inode->i_size < off + len - towrite)
i_size_write(inode, off + len - towrite);
- inode->i_version++;
inode->i_mtime = inode->i_ctime = current_time(inode);
mark_inode_dirty(inode);
return len - towrite;
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index d31cd1e..f3acecf 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -391,7 +391,7 @@
(ip->i_df.if_flags & XFS_IFEXTENTS));
ASSERT(offset <= mp->m_super->s_maxbytes);
- if (offset + count > mp->m_super->s_maxbytes)
+ if ((xfs_ufsize_t)offset + count > mp->m_super->s_maxbytes)
count = mp->m_super->s_maxbytes - offset;
end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + count);
offset_fsb = XFS_B_TO_FSBT(mp, offset);
@@ -1295,7 +1295,7 @@
if (mapping_size > size)
mapping_size = size;
if (offset < i_size_read(inode) &&
- offset + mapping_size >= i_size_read(inode)) {
+ (xfs_ufsize_t)offset + mapping_size >= i_size_read(inode)) {
/* limit mapping to block that spans EOF */
mapping_size = roundup_64(i_size_read(inode) - offset,
i_blocksize(inode));
@@ -1347,7 +1347,7 @@
lockmode = xfs_ilock_data_map_shared(ip);
ASSERT(offset <= mp->m_super->s_maxbytes);
- if (offset + size > mp->m_super->s_maxbytes)
+ if ((xfs_ufsize_t)offset + size > mp->m_super->s_maxbytes)
size = mp->m_super->s_maxbytes - offset;
end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + size);
offset_fsb = XFS_B_TO_FSBT(mp, offset);
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index eca7bae..3f45d98 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -1785,22 +1785,27 @@
btp->bt_bdi = blk_get_backing_dev_info(bdev);
if (xfs_setsize_buftarg_early(btp, bdev))
- goto error;
+ goto error_free;
if (list_lru_init(&btp->bt_lru))
- goto error;
+ goto error_free;
if (percpu_counter_init(&btp->bt_io_count, 0, GFP_KERNEL))
- goto error;
+ goto error_lru;
btp->bt_shrinker.count_objects = xfs_buftarg_shrink_count;
btp->bt_shrinker.scan_objects = xfs_buftarg_shrink_scan;
btp->bt_shrinker.seeks = DEFAULT_SEEKS;
btp->bt_shrinker.flags = SHRINKER_NUMA_AWARE;
- register_shrinker(&btp->bt_shrinker);
+ if (register_shrinker(&btp->bt_shrinker))
+ goto error_pcpu;
return btp;
-error:
+error_pcpu:
+ percpu_counter_destroy(&btp->bt_io_count);
+error_lru:
+ list_lru_destroy(&btp->bt_lru);
+error_free:
kmem_free(btp);
return NULL;
}
diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c
index 9d06cc3..7a7b3cc 100644
--- a/fs/xfs/xfs_dquot.c
+++ b/fs/xfs/xfs_dquot.c
@@ -1004,14 +1004,22 @@
* holding the lock before removing the dquot from the AIL.
*/
if ((lip->li_flags & XFS_LI_IN_AIL) &&
- lip->li_lsn == qip->qli_flush_lsn) {
+ ((lip->li_lsn == qip->qli_flush_lsn) ||
+ (lip->li_flags & XFS_LI_FAILED))) {
/* xfs_trans_ail_delete() drops the AIL lock. */
spin_lock(&ailp->xa_lock);
- if (lip->li_lsn == qip->qli_flush_lsn)
+ if (lip->li_lsn == qip->qli_flush_lsn) {
xfs_trans_ail_delete(ailp, lip, SHUTDOWN_CORRUPT_INCORE);
- else
+ } else {
+ /*
+ * Clear the failed state since we are about to drop the
+ * flush lock
+ */
+ if (lip->li_flags & XFS_LI_FAILED)
+ xfs_clear_li_failed(lip);
spin_unlock(&ailp->xa_lock);
+ }
}
/*
diff --git a/fs/xfs/xfs_dquot_item.c b/fs/xfs/xfs_dquot_item.c
index 2c7a162..664dea1 100644
--- a/fs/xfs/xfs_dquot_item.c
+++ b/fs/xfs/xfs_dquot_item.c
@@ -137,6 +137,26 @@
wait_event(dqp->q_pinwait, (atomic_read(&dqp->q_pincount) == 0));
}
+/*
+ * Callback used to mark a buffer with XFS_LI_FAILED when items in the buffer
+ * have been failed during writeback
+ *
+ * this informs the AIL that the dquot is already flush locked on the next push,
+ * and acquires a hold on the buffer to ensure that it isn't reclaimed before
+ * dirty data makes it to disk.
+ */
+STATIC void
+xfs_dquot_item_error(
+ struct xfs_log_item *lip,
+ struct xfs_buf *bp)
+{
+ struct xfs_dquot *dqp;
+
+ dqp = DQUOT_ITEM(lip)->qli_dquot;
+ ASSERT(!completion_done(&dqp->q_flush));
+ xfs_set_li_failed(lip, bp);
+}
+
STATIC uint
xfs_qm_dquot_logitem_push(
struct xfs_log_item *lip,
@@ -144,13 +164,28 @@
__acquires(&lip->li_ailp->xa_lock)
{
struct xfs_dquot *dqp = DQUOT_ITEM(lip)->qli_dquot;
- struct xfs_buf *bp = NULL;
+ struct xfs_buf *bp = lip->li_buf;
uint rval = XFS_ITEM_SUCCESS;
int error;
if (atomic_read(&dqp->q_pincount) > 0)
return XFS_ITEM_PINNED;
+ /*
+ * The buffer containing this item failed to be written back
+ * previously. Resubmit the buffer for IO
+ */
+ if (lip->li_flags & XFS_LI_FAILED) {
+ if (!xfs_buf_trylock(bp))
+ return XFS_ITEM_LOCKED;
+
+ if (!xfs_buf_resubmit_failed_buffers(bp, lip, buffer_list))
+ rval = XFS_ITEM_FLUSHING;
+
+ xfs_buf_unlock(bp);
+ return rval;
+ }
+
if (!xfs_dqlock_nowait(dqp))
return XFS_ITEM_LOCKED;
@@ -242,7 +277,8 @@
.iop_unlock = xfs_qm_dquot_logitem_unlock,
.iop_committed = xfs_qm_dquot_logitem_committed,
.iop_push = xfs_qm_dquot_logitem_push,
- .iop_committing = xfs_qm_dquot_logitem_committing
+ .iop_committing = xfs_qm_dquot_logitem_committing,
+ .iop_error = xfs_dquot_item_error
};
/*
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 98ca9f1..c5f2f1e 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -2430,6 +2430,24 @@
}
/*
+ * Free any local-format buffers sitting around before we reset to
+ * extents format.
+ */
+static inline void
+xfs_ifree_local_data(
+ struct xfs_inode *ip,
+ int whichfork)
+{
+ struct xfs_ifork *ifp;
+
+ if (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL)
+ return;
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ xfs_idata_realloc(ip, -ifp->if_bytes, whichfork);
+}
+
+/*
* This is called to return an inode to the inode free list.
* The inode should already be truncated to 0 length and have
* no pages associated with it. This routine also assumes that
@@ -2466,6 +2484,9 @@
if (error)
return error;
+ xfs_ifree_local_data(ip, XFS_DATA_FORK);
+ xfs_ifree_local_data(ip, XFS_ATTR_FORK);
+
VFS_I(ip)->i_mode = 0; /* mark incore inode as free */
ip->i_d.di_flags = 0;
ip->i_d.di_dmevmask = 0;
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index dc81e52..2e6000a 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -778,7 +778,14 @@
*/
#define PERCPU_INPUT(cacheline) \
VMLINUX_SYMBOL(__per_cpu_start) = .; \
+ VMLINUX_SYMBOL(__per_cpu_user_mapped_start) = .; \
*(.data..percpu..first) \
+ . = ALIGN(cacheline); \
+ *(.data..percpu..user_mapped) \
+ *(.data..percpu..user_mapped..shared_aligned) \
+ . = ALIGN(PAGE_SIZE); \
+ *(.data..percpu..user_mapped..page_aligned) \
+ VMLINUX_SYMBOL(__per_cpu_user_mapped_end) = .; \
. = ALIGN(PAGE_SIZE); \
*(.data..percpu..page_aligned) \
. = ALIGN(cacheline); \
diff --git a/include/dt-bindings/arm/arm-smmu.h b/include/dt-bindings/arm/arm-smmu.h
new file mode 100644
index 0000000..3a1dbd3
--- /dev/null
+++ b/include/dt-bindings/arm/arm-smmu.h
@@ -0,0 +1,27 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DT_ARM_SMMU_H__
+#define __DT_ARM_SMMU_H__
+
+#define ARM_SMMU_OPT_SECURE_CFG_ACCESS (1 << 0)
+#define ARM_SMMU_OPT_FATAL_ASF (1 << 1)
+#define ARM_SMMU_OPT_SKIP_INIT (1 << 2)
+#define ARM_SMMU_OPT_DYNAMIC (1 << 3)
+#define ARM_SMMU_OPT_3LVL_TABLES (1 << 4)
+#define ARM_SMMU_OPT_NO_ASID_RETENTION (1 << 5)
+#define ARM_SMMU_OPT_DISABLE_ATOS (1 << 6)
+#define ARM_SMMU_OPT_MMU500_ERRATA1 (1 << 7)
+#define ARM_SMMU_OPT_STATIC_CB (1 << 8)
+#define ARM_SMMU_OPT_HALT (1 << 9)
+
+#endif
diff --git a/include/dt-bindings/clock/msm-clocks-8952.h b/include/dt-bindings/clock/msm-clocks-8952.h
new file mode 100644
index 0000000..80a95d9
--- /dev/null
+++ b/include/dt-bindings/clock/msm-clocks-8952.h
@@ -0,0 +1,344 @@
+/* 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.
+ */
+
+#ifndef __MSM_CLOCKS_8952_H
+#define __MSM_CLOCKS_8952_H
+
+/* clock_gcc controlled clocks */
+
+/* GPLLs */
+#define clk_gpll0_clk_src_8952 0x1617c790
+#define clk_gpll0_ao_clk_src_8952 0x9b4db4e8
+#define clk_gpll0_clk_src_8937 0x94350fc4
+#define clk_gpll0_ao_clk_src_8937 0x923c7546
+#define clk_gpll0_clk_src 0x5933b69f
+#define clk_gpll0_ao_clk_src 0x6b2fb034
+#define clk_gpll0_sleep_clk_src 0x4f89fcf0
+#define clk_gpll0_out_main 0x850fecec
+#define clk_gpll0_out_aux 0x64e55d63
+#define clk_gpll0_misc 0xe06ee816
+#define clk_gpll3_clk_src 0x5b1eccd5
+#define clk_gpll3_out_main 0xf5fc71ab
+#define clk_gpll3_out_aux 0xe72bea1a
+#define clk_gpll4_clk_src 0x10525d57
+#define clk_gpll4_out_main 0xdca8db2a
+#define clk_gpll6_clk_src 0x17dceaad
+#define clk_gpll6_out_main 0x27b8b7be
+#define clk_a53ss_c0_pll 0xf761da94
+#define clk_a53ss_c1_pll 0xfbc57bbd
+#define clk_a53ss_cci_pll 0x17d32f1e
+
+/* SRCs */
+#define clk_apss_ahb_clk_src 0x36f8495f
+#define clk_blsp1_qup1_i2c_apps_clk_src 0x17f78f5e
+#define clk_blsp1_qup1_spi_apps_clk_src 0xf534c4fa
+#define clk_blsp1_qup2_i2c_apps_clk_src 0x8de71c79
+#define clk_blsp1_qup2_spi_apps_clk_src 0x33cf809a
+#define clk_blsp1_qup3_i2c_apps_clk_src 0xf161b902
+#define clk_blsp1_qup3_spi_apps_clk_src 0x5e95683f
+#define clk_blsp1_qup4_i2c_apps_clk_src 0xb2ecce68
+#define clk_gcc_blsp2_ahb_clk 0x8f283c1d
+#define clk_gcc_blsp2_sleep_clk 0x429ca5d2
+#define clk_gcc_blsp2_qup1_spi_apps_clk 0xa32604cc
+#define clk_gcc_blsp2_qup1_i2c_apps_clk 0x9ace11dd
+#define clk_blsp2_qup1_spi_apps_clk_src 0xcc1b8365
+#define clk_blsp2_qup1_i2c_apps_clk_src 0xd6d1e95d
+#define clk_gcc_blsp2_uart1_apps_clk 0x8c3512ff
+#define clk_gcc_blsp2_uart1_sim_clk 0x2ea81633
+#define clk_blsp2_uart1_apps_clk_src 0x562c66dc
+#define clk_gcc_blsp2_qup2_spi_apps_clk 0xbf54ca6d
+#define clk_gcc_blsp2_qup2_i2c_apps_clk 0x1bf9a57e
+#define clk_blsp2_qup2_spi_apps_clk_src 0xd577dc44
+#define clk_blsp2_qup2_i2c_apps_clk_src 0x603b5c51
+#define clk_gcc_blsp2_uart2_apps_clk 0x1e1965a3
+#define clk_gcc_blsp2_uart2_sim_clk 0xca05dfe2
+#define clk_blsp2_uart2_apps_clk_src 0xdd448080
+#define clk_gcc_blsp2_qup3_spi_apps_clk 0xc68509d6
+#define clk_gcc_blsp2_qup3_i2c_apps_clk 0x336d4170
+#define clk_blsp2_qup3_spi_apps_clk_src 0xd04b1e92
+#define clk_blsp2_qup3_i2c_apps_clk_src 0xea82959c
+#define clk_gcc_blsp2_qup4_spi_apps_clk 0x01a72b93
+#define clk_gcc_blsp2_qup4_i2c_apps_clk 0xbd22539d
+#define clk_blsp2_qup4_spi_apps_clk_src 0x25d4a2b1
+#define clk_blsp2_qup4_i2c_apps_clk_src 0x73dc968c
+#define clk_blsp1_qup4_spi_apps_clk_src 0xddb5bbdb
+#define clk_blsp1_uart1_apps_clk_src 0xf8146114
+#define clk_blsp1_uart2_apps_clk_src 0xfc9c2f73
+#define clk_byte0_clk_src 0x75cc885b
+#define clk_cci_clk_src 0x822f3d97
+#define clk_camss_top_ahb_clk_src 0xf92304fb
+#define clk_camss_gp0_clk_src 0x43b063e9
+#define clk_camss_gp1_clk_src 0xa3315f1b
+#define clk_crypto_clk_src 0x37a21414
+#define clk_csi0_clk_src 0x227e65bc
+#define clk_csi1_clk_src 0x6a2a6c36
+#define clk_csi2_clk_src 0x4113589f
+#define clk_csi0phytimer_clk_src 0xc8a309be
+#define clk_csi1phytimer_clk_src 0x7c0fe23a
+#define clk_esc0_clk_src 0xb41d7c38
+#define clk_gfx3d_clk_src 0x917f76ef
+#define clk_gp1_clk_src 0xad85b97a
+#define clk_gp2_clk_src 0xfb1f0065
+#define clk_gp3_clk_src 0x63b693d6
+#define clk_jpeg0_clk_src 0x9a0a0ac3
+#define clk_mdp_clk_src 0x6dc1f8f1
+#define clk_mclk0_clk_src 0x266b3853
+#define clk_mclk1_clk_src 0xa73cad0c
+#define clk_mclk2_clk_src 0x42545468
+#define clk_pclk0_clk_src 0xccac1f35
+#define clk_pdm2_clk_src 0x31e494fd
+#define clk_sdcc1_apps_clk_src 0xd4975db2
+#define clk_sdcc1_ice_core_clk_src 0xfd6a4301
+#define clk_sdcc2_apps_clk_src 0xfc46c821
+#define clk_usb_hs_system_clk_src 0x28385546
+#define clk_usb_fs_system_clk_src 0x06ee1762
+#define clk_usb_fs_ic_clk_src 0x25d4acc8
+#define clk_usb_fs_ic_clk_src 0x25d4acc8
+#define clk_gcc_qusb2_phy_clk 0x996884d5
+#define clk_gcc_usb2_hs_phy_only_clk 0x0047179d
+#define clk_vsync_clk_src 0xecb43940
+#define clk_vfe0_clk_src 0xa0c2bd8f
+#define clk_vcodec0_clk_src 0xbc193019
+#define clk_gcc_blsp1_ahb_clk 0x8caa5b4f
+#define clk_gcc_boot_rom_ahb_clk 0xde2adeb1
+#define clk_gcc_crypto_ahb_clk 0x94de4919
+#define clk_gcc_crypto_axi_clk 0xd4415c9b
+#define clk_gcc_crypto_clk 0x00d390d2
+#define clk_gcc_prng_ahb_clk 0x397e7eaa
+#define clk_gcc_qdss_dap_clk 0x7fa9aa73
+#define clk_gcc_apss_tcu_clk 0xaf56a329
+#define clk_gcc_ipa_tbu_clk 0x75bbfb5c
+#define clk_gcc_gfx_tbu_clk 0x18bb9a90
+#define clk_gcc_gtcu_ahb_clk 0xb432168e
+#define clk_gcc_jpeg_tbu_clk 0xcf8fd944
+#define clk_gcc_mdp_tbu_clk 0x82287f76
+#define clk_gcc_smmu_cfg_clk 0x75eaefa5
+#define clk_gcc_venus_tbu_clk 0x7e0b97ce
+#define clk_gcc_vfe_tbu_clk 0x061f2f95
+#define clk_gcc_vfe1_tbu_clk 0x4888e70f
+#define clk_gcc_cpp_tbu_clk 0xab6f19ab
+#define clk_gcc_blsp1_qup1_i2c_apps_clk 0xc303fae9
+#define clk_gcc_blsp1_qup1_spi_apps_clk 0x759a76b0
+#define clk_gcc_blsp1_qup2_i2c_apps_clk 0x1076f220
+#define clk_gcc_blsp1_qup2_spi_apps_clk 0x3e77d48f
+#define clk_gcc_blsp1_qup3_i2c_apps_clk 0x9e25ac82
+#define clk_gcc_blsp1_qup3_spi_apps_clk 0xfb978880
+#define clk_gcc_blsp1_qup4_i2c_apps_clk 0xd7f40f6f
+#define clk_gcc_blsp1_qup4_spi_apps_clk 0x80f8722f
+#define clk_gcc_blsp1_uart1_apps_clk 0xc7c62f90
+#define clk_gcc_blsp1_uart2_apps_clk 0xf8a61c96
+#define clk_gcc_camss_cci_ahb_clk 0xa81c11ba
+#define clk_gcc_camss_cci_clk 0xb7dd8824
+#define clk_gcc_camss_csi0_ahb_clk 0x175d672a
+#define clk_gcc_camss_csi0_clk 0x6b01b3e1
+#define clk_gcc_camss_csi0phy_clk 0x06a41ff7
+#define clk_gcc_camss_csi0pix_clk 0x61a8a930
+#define clk_gcc_camss_csi0rdi_clk 0x7053c7ae
+#define clk_gcc_camss_csi1_ahb_clk 0x2c2dc261
+#define clk_gcc_camss_csi1_clk 0x1aba4a8c
+#define clk_gcc_camss_csi1phy_clk 0x0fd1d1fa
+#define clk_gcc_camss_csi1pix_clk 0x87fc98d8
+#define clk_gcc_camss_csi1rdi_clk 0x6ac996fe
+#define clk_gcc_camss_csi2_ahb_clk 0xf3f25940
+#define clk_gcc_camss_csi2_clk 0xb6857fa2
+#define clk_gcc_camss_csi2phy_clk 0xbeeffbcd
+#define clk_gcc_camss_csi2pix_clk 0xa619561a
+#define clk_gcc_camss_csi2rdi_clk 0x019fd3f1
+#define clk_vfe1_clk_src 0x4e357366
+#define clk_gcc_camss_vfe1_clk 0xcaf20d99
+#define clk_gcc_camss_vfe1_ahb_clk 0x634a738a
+#define clk_gcc_camss_vfe1_axi_clk 0xaf7463b3
+#define clk_gcc_vfe1_qdss_at_clk 0xfff1e0be
+#define clk_cpp_clk_src 0x8382f56d
+#define clk_gcc_camss_cpp_clk 0x7118a0de
+#define clk_gcc_camss_cpp_ahb_clk 0x4ac95e14
+#define clk_gcc_camss_cpp_axi_clk 0xbbf73861
+#define clk_gcc_cpp_qdss_at_clk 0x05805d0d
+#define clk_gcc_cpp_qdss_tsctr_div8_clk 0xebd2c356
+#define clk_gcc_camss_csi_vfe0_clk 0xcc73453c
+#define clk_gcc_camss_csi_vfe1_clk 0xb1ef6e8b
+#define clk_gcc_camss_gp0_clk 0xd2bc3892
+#define clk_gcc_camss_gp1_clk 0xe4c013e1
+#define clk_gcc_camss_ispif_ahb_clk 0x3c0a858f
+#define clk_gcc_camss_jpeg0_clk 0x1ed3f032
+#define clk_gcc_camss_jpeg_ahb_clk 0x3bfa7603
+#define clk_gcc_camss_jpeg_axi_clk 0x3e278896
+#define clk_gcc_camss_mclk0_clk 0x80902deb
+#define clk_gcc_camss_mclk1_clk 0x5002d85f
+#define clk_gcc_camss_mclk2_clk 0x222f8fff
+#define clk_gcc_camss_micro_ahb_clk 0xfbbee8cf
+#define clk_gcc_camss_csi0phytimer_clk 0xf8897589
+#define clk_gcc_camss_csi1phytimer_clk 0x4d26438f
+#define clk_gcc_camss_ahb_clk 0x9894b414
+#define clk_gcc_camss_top_ahb_clk 0x4e814a78
+#define clk_gcc_camss_vfe0_clk 0xaaa3cd97
+#define clk_gcc_camss_vfe_ahb_clk 0x4050f47a
+#define clk_gcc_camss_vfe_axi_clk 0x77fe2384
+#define clk_gcc_sys_mm_noc_axi_clk 0xb75a7187
+#define clk_gcc_oxili_gmem_clk 0x5620913a
+#define clk_gcc_gp1_clk 0x057f7b69
+#define clk_gcc_gp2_clk 0x9bf83ffd
+#define clk_gcc_gp3_clk 0xec6539ee
+#define clk_gcc_mdss_ahb_clk 0xbfb92ed3
+#define clk_gcc_mdss_axi_clk 0x668f51de
+#define clk_gcc_mdss_byte0_clk 0x35da7862
+#define clk_gcc_mdss_esc0_clk 0xaec5cb25
+#define clk_gcc_mdss_mdp_clk 0x22f3521f
+#define clk_gcc_mdss_pclk0_clk 0xcc5c5c77
+#define clk_gcc_mdss_vsync_clk 0x32a09f1f
+#define clk_gcc_mss_cfg_ahb_clk 0x111cde81
+#define clk_gcc_mss_q6_bimc_axi_clk 0x67544d62
+#define clk_gcc_oxili_ahb_clk 0xd15c8a00
+#define clk_gcc_oxili_gfx3d_clk 0x49a51fd9
+#define clk_gcc_oxili_timer_clk 0x1180db06
+#define clk_gcc_oxili_aon_clk 0xae18e54d
+#define clk_gcc_pdm2_clk 0x99d55711
+#define clk_gcc_pdm_ahb_clk 0x365664f6
+#define clk_gcc_sdcc1_ahb_clk 0x691e0caa
+#define clk_gcc_sdcc1_apps_clk 0x9ad6fb96
+#define clk_gcc_sdcc1_ice_core_clk 0x0fd5680a
+#define clk_gcc_sdcc2_ahb_clk 0x23d5727f
+#define clk_gcc_sdcc2_apps_clk 0x861b20ac
+#define clk_gcc_usb2a_phy_sleep_clk 0x6caa736f
+#define clk_gcc_usb_hs_phy_cfg_ahb_clk 0xe13808fd
+#define clk_gcc_usb_hs_ahb_clk 0x72ce8032
+#define clk_gcc_usb_fs_ahb_clk 0x00e31116
+#define clk_gcc_usb_fs_ic_clk 0xbd533d37
+#define clk_gcc_usb_hs_system_clk 0xa11972e5
+#define clk_gcc_usb_fs_system_clk 0xea3b114c
+#define clk_gcc_venus0_ahb_clk 0x08d778c6
+#define clk_gcc_venus0_axi_clk 0xcdf4c8f6
+#define clk_gcc_venus0_vcodec0_clk 0xf76a02bb
+#define clk_gcc_venus0_core0_vcodec0_clk 0x83a7f549
+#define clk_gcc_venus0_core1_vcodec0_clk 0xa0813de6
+#define clk_gcc_gfx_tcu_clk 0x59505e55
+#define clk_gcc_gtcu_ahb_bridge_clk 0x19d2c5fe
+#define clk_gcc_bimc_gpu_clk 0x19922503
+#define clk_gcc_bimc_gfx_clk 0x3edd69ad
+#define clk_ipa_clk 0xfa685cda
+#define clk_ipa_a_clk 0xeeec2919
+#define clk_mdss_mdp_vote_clk 0x588460a4
+#define clk_mdss_rotator_vote_clk 0x5b1f675e
+
+#define clk_pixel_clk_src 0x8b6f83d8
+#define clk_byte_clk_src 0x3a911c53
+#define clk_ext_pclk0_clk_src 0x087c1612
+#define clk_ext_byte0_clk_src 0xfb32f31e
+
+#define clk_dsi_pll0_byte_clk_src 0x44539836
+#define clk_dsi_pll0_pixel_clk_src 0x5767c287
+#define clk_dsi_pll1_byte_clk_src 0x73e88d02
+#define clk_dsi_pll1_pixel_clk_src 0xce233fcf
+#define clk_ext_pclk1_clk_src 0x8067c5a3
+#define clk_ext_byte1_clk_src 0x585ef6d4
+#define clk_byte1_clk_src 0x63c2c955
+#define clk_esc1_clk_src 0x3b0afa42
+#define clk_pclk1_clk_src 0x090f68ac
+#define clk_gcc_mdss_pclk1_clk 0x9a9c430d
+#define clk_gcc_mdss_byte1_clk 0x41f97fd8
+#define clk_gcc_mdss_esc1_clk 0x34653cc7
+#define clk_gcc_dcc_clk 0xd1000c50
+#define clk_gcc_debug_mux_8937 0x917968c2
+
+/* clock_rpm controlled clocks */
+#define clk_pnoc_clk 0xc1296d0f
+#define clk_pnoc_a_clk 0x9bcffee4
+#define clk_pnoc_msmbus_clk 0x2b53b688
+#define clk_pnoc_msmbus_a_clk 0x9753a54f
+#define clk_pnoc_keepalive_a_clk 0x9464f720
+#define clk_pnoc_sps_clk 0x23d3f584
+#define clk_pnoc_usb_a_clk 0x11d6a74e
+#define clk_pnoc_usb_clk 0x266d8376
+#define clk_snoc_clk 0x2c341aa0
+#define clk_snoc_a_clk 0x8fcef2af
+#define clk_snoc_usb_a_clk 0x34b7821b
+#define clk_snoc_wcnss_a_clk 0xd3949ebc
+#define clk_snoc_usb_clk 0x29f9d73d
+#define clk_snoc_msmbus_clk 0xe6900bb6
+#define clk_snoc_msmbus_a_clk 0x5d4683bd
+#define clk_snoc_mmnoc_axi_clk 0xfedd4bd5
+#define clk_snoc_mmnoc_ahb_clk 0xd2149dbb
+#define clk_sysmmnoc_clk 0xebb1df78
+#define clk_sysmmnoc_a_clk 0x6ca682a2
+#define clk_sysmmnoc_msmbus_clk 0xd61e5721
+#define clk_sysmmnoc_msmbus_a_clk 0x50600f1b
+#define clk_bimc_clk 0x4b80bf00
+#define clk_bimc_a_clk 0x4b25668a
+#define clk_bimc_acpu_a_clk 0x4446311b
+#define clk_bimc_msmbus_clk 0xd212feea
+#define clk_bimc_msmbus_a_clk 0x71d1a499
+#define clk_bimc_usb_a_clk 0xea410834
+#define clk_bimc_wcnss_a_clk 0x5a6df715
+#define clk_bimc_usb_clk 0x9bd2b2bf
+#define clk_bimc_gpu_clk 0xd3e0a327
+#define clk_bimc_gpu_a_clk 0x67f0e9a5
+#define clk_qdss_clk 0x1492202a
+#define clk_qdss_a_clk 0xdd121669
+#define clk_xo_clk_src 0x23f5649f
+#define clk_xo_a_clk_src 0x2fdd2c7c
+#define clk_xo_otg_clk 0x79bca5cc
+#define clk_xo_a2 0xeba5a83d
+#define clk_xo_dwc3_clk 0xfad488ce
+#define clk_xo_ehci_host_clk 0xc7c340b1
+#define clk_xo_lpm_clk 0x2be48257
+#define clk_xo_pil_mss_clk 0xe97a8354
+#define clk_xo_pil_pronto_clk 0x89dae6d0
+#define clk_xo_wlan_clk 0x0116b76f
+#define clk_xo_pil_lpass_clk 0xb72aa4c9
+#define clk_bb_clk1 0xf5304268
+#define clk_bb_clk1_a 0xfa113810
+#define clk_bb_clk1_pin 0x6dd0a779
+#define clk_bb_clk1_a_pin 0x9b637772
+#define clk_bb_clk2 0xfe15cb87
+#define clk_bb_clk2_a 0x59682706
+#define clk_bb_clk2_pin 0x498938e5
+#define clk_bb_clk2_a_pin 0x52513787
+#define clk_rf_clk1 0xaabeea5a
+#define clk_rf_clk1_a 0x72a10cb8
+#define clk_rf_clk1_pin 0x8f463562
+#define clk_rf_clk1_a_pin 0x62549ff6
+#define clk_rf_clk2 0x24a30992
+#define clk_rf_clk2_a 0x944d8bbd
+#define clk_rf_clk2_pin 0xa7c5602a
+#define clk_rf_clk2_a_pin 0x2d75eb4d
+#define clk_div_clk1 0xaa1157a6
+#define clk_div_clk1_a 0x6b943d68
+#define clk_div_clk2 0xd454019f
+#define clk_div_clk2_a 0x4bd7bfa8
+#define clk_ln_bb_clk 0x3ab0b36d
+#define clk_ln_bb_a_clk 0xc7257ea8
+
+
+/* clock_debug controlled clocks */
+#define clk_gcc_debug_mux 0x8121ac15
+#define clk_rpm_debug_mux 0x25cd1f3a
+#define clk_wcnss_m_clk 0x709f430b
+#define clk_apss_debug_pri_mux 0xc691ff55
+#define clk_apss_debug_sec_mux 0xc0b680f9
+#define clk_apss_debug_ter_mux 0x32041c48
+#define clk_apc0_m_clk 0xce1e9473
+#define clk_apc1_m_clk 0x990fbaf7
+#define clk_cci_m_clk 0xec7e8afc
+
+#define clk_a53ssmux_lc 0x71a9377b
+#define clk_a53ssmux_bc 0xb5983c42
+#define clk_a53ssmux_cci 0x15560bd5
+
+#define clk_a53_lc_clk 0xc69f0878
+#define clk_a53_bc_clk 0xcf28e63a
+#define clk_cci_clk 0x96854074
+
+#define clk_audio_ap_clk 0x312ac429
+#define clk_audio_pmi_clk 0xb7ba2274
+#define clk_audio_lpass_mclk 0x575ec22b
+
+#endif
diff --git a/include/dt-bindings/clock/msm-clocks-8996.h b/include/dt-bindings/clock/msm-clocks-8996.h
new file mode 100644
index 0000000..1f515f2
--- /dev/null
+++ b/include/dt-bindings/clock/msm-clocks-8996.h
@@ -0,0 +1,548 @@
+/* Copyright (c) 2014-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 __MSM_CLOCKS_8996_H
+#define __MSM_CLOCKS_8996_H
+
+/* clock_gcc controlled clocks */
+#define clk_cxo_clk_src 0x79e95308
+#define clk_pnoc_clk 0x4325d220
+#define clk_pnoc_a_clk 0x2808c12b
+#define clk_bimc_clk 0x4b80bf00
+#define clk_bimc_a_clk 0x4b25668a
+#define clk_cnoc_clk 0xd5ccb7f4
+#define clk_cnoc_a_clk 0xd8fe2ccc
+#define clk_snoc_clk 0x2c341aa0
+#define clk_snoc_a_clk 0x8fcef2af
+#define clk_bb_clk1 0xf5304268
+#define clk_bb_clk1_ao 0xfa113810
+#define clk_bb_clk1_pin 0x6dd0a779
+#define clk_bb_clk1_pin_ao 0x9b637772
+#define clk_bb_clk2 0xfe15cb87
+#define clk_bb_clk2_ao 0x59682706
+#define clk_bb_clk2_pin 0x498938e5
+#define clk_bb_clk2_pin_ao 0x52513787
+#define clk_bimc_msmbus_clk 0xd212feea
+#define clk_bimc_msmbus_a_clk 0x71d1a499
+#define clk_ce1_a_clk 0x44a833fe
+#define clk_cnoc_msmbus_clk 0x62228b5d
+#define clk_cnoc_msmbus_a_clk 0x67442955
+#define clk_cxo_clk_src_ao 0x64eb6004
+#define clk_cxo_dwc3_clk 0xf79c19f6
+#define clk_cxo_lpm_clk 0x94adbf3d
+#define clk_cxo_otg_clk 0x4eec0bb9
+#define clk_cxo_pil_lpass_clk 0xe17f0ff6
+#define clk_cxo_pil_ssc_clk 0x81832015
+#define clk_div_clk1 0xaa1157a6
+#define clk_div_clk1_ao 0x6b943d68
+#define clk_div_clk2 0xd454019f
+#define clk_div_clk2_ao 0x53f9e788
+#define clk_div_clk3 0xa9a55a68
+#define clk_div_clk3_ao 0x3d6725a8
+#define clk_ipa_a_clk 0xeeec2919
+#define clk_ipa_clk 0xfa685cda
+#define clk_ln_bb_clk 0x3ab0b36d
+#define clk_ln_bb_a_clk 0xc7257ea8
+#define clk_ln_bb_clk_pin 0x1b1c476a
+#define clk_ln_bb_a_clk_pin 0x9cbb5411
+#define clk_mcd_ce1_clk 0xbb615d26
+#define clk_pnoc_keepalive_a_clk 0xf8f91f0b
+#define clk_pnoc_msmbus_clk 0x38b95c77
+#define clk_pnoc_msmbus_a_clk 0x8c9b4e93
+#define clk_pnoc_pm_clk 0xd6f7dfb9
+#define clk_pnoc_sps_clk 0xd482ecc7
+#define clk_qdss_a_clk 0xdd121669
+#define clk_qdss_clk 0x1492202a
+#define clk_rf_clk1 0xaabeea5a
+#define clk_rf_clk1_ao 0x72a10cb8
+#define clk_rf_clk1_pin 0x8f463562
+#define clk_rf_clk1_pin_ao 0x62549ff6
+#define clk_rf_clk2 0x24a30992
+#define clk_rf_clk2_ao 0x944d8bbd
+#define clk_rf_clk2_pin 0xa7c5602a
+#define clk_rf_clk2_pin_ao 0x2d75eb4d
+#define clk_snoc_msmbus_clk 0xe6900bb6
+#define clk_snoc_msmbus_a_clk 0x5d4683bd
+#define clk_mcd_ce1_clk 0xbb615d26
+#define clk_qcedev_ce1_clk 0x293f97b0
+#define clk_qcrypto_ce1_clk 0xa6ac14df
+#define clk_qseecom_ce1_clk 0xaa858373
+#define clk_scm_ce1_clk 0xd8ebcc62
+#define clk_ce1_clk 0x42229c55
+#define clk_gcc_ce1_ahb_m_clk 0x2eb28c01
+#define clk_gcc_ce1_axi_m_clk 0xc174dfba
+#define clk_measure_only_bimc_hmss_axi_clk 0xc1cc4f11
+#define clk_aggre1_noc_clk 0x049abba8
+#define clk_aggre1_noc_a_clk 0xc12e4220
+#define clk_aggre2_noc_clk 0xaa681404
+#define clk_aggre2_noc_a_clk 0xcab67089
+#define clk_mmssnoc_axi_rpm_clk 0x4d7f8cdc
+#define clk_mmssnoc_axi_rpm_a_clk 0xfbea899b
+#define clk_mmssnoc_axi_clk 0xdb4b31e6
+#define clk_mmssnoc_axi_a_clk 0xd4970614
+#define clk_mmssnoc_gds_clk 0x06a22afa
+
+#define clk_gpll0 0x1ebe3bc4
+#define clk_gpll0_ao 0xa1368304
+#define clk_gpll0_out_main 0xe9374de7
+#define clk_gpll4 0xb3b5d85b
+#define clk_gpll4_out_main 0xa9a0ab9d
+#define clk_ufs_axi_clk_src 0x297ca380
+#define clk_pcie_aux_clk_src 0xebc50566
+#define clk_usb30_master_clk_src 0xc6262f89
+#define clk_usb20_master_clk_src 0x5680ac83
+#define clk_ufs_ice_core_clk_src 0xda8e7119
+#define clk_blsp1_qup1_i2c_apps_clk_src 0x17f78f5e
+#define clk_blsp1_qup1_spi_apps_clk_src 0xf534c4fa
+#define clk_blsp1_qup2_i2c_apps_clk_src 0x8de71c79
+#define clk_blsp1_qup2_spi_apps_clk_src 0x33cf809a
+#define clk_blsp1_qup3_i2c_apps_clk_src 0xf161b902
+#define clk_blsp1_qup3_spi_apps_clk_src 0x5e95683f
+#define clk_blsp1_qup4_i2c_apps_clk_src 0xb2ecce68
+#define clk_blsp1_qup4_spi_apps_clk_src 0xddb5bbdb
+#define clk_blsp1_qup5_i2c_apps_clk_src 0x71ea7804
+#define clk_blsp1_qup5_spi_apps_clk_src 0x9752f35f
+#define clk_blsp1_qup6_i2c_apps_clk_src 0x28806803
+#define clk_blsp1_qup6_spi_apps_clk_src 0x44a1edc4
+#define clk_blsp1_uart1_apps_clk_src 0xf8146114
+#define clk_blsp1_uart2_apps_clk_src 0xfc9c2f73
+#define clk_blsp1_uart3_apps_clk_src 0x600497f2
+#define clk_blsp1_uart4_apps_clk_src 0x56bff15c
+#define clk_blsp1_uart5_apps_clk_src 0x218ef697
+#define clk_blsp1_uart6_apps_clk_src 0x8fbdbe4c
+#define clk_blsp2_qup1_i2c_apps_clk_src 0xd6d1e95d
+#define clk_blsp2_qup1_spi_apps_clk_src 0xcc1b8365
+#define clk_blsp2_qup2_i2c_apps_clk_src 0x603b5c51
+#define clk_blsp2_qup2_spi_apps_clk_src 0xd577dc44
+#define clk_blsp2_qup3_i2c_apps_clk_src 0xea82959c
+#define clk_blsp2_qup3_spi_apps_clk_src 0xd04b1e92
+#define clk_blsp2_qup4_i2c_apps_clk_src 0x73dc968c
+#define clk_blsp2_qup4_spi_apps_clk_src 0x25d4a2b1
+#define clk_blsp2_qup5_i2c_apps_clk_src 0xcc3698bd
+#define clk_blsp2_qup5_spi_apps_clk_src 0xfa0cf45e
+#define clk_blsp2_qup6_i2c_apps_clk_src 0x2fa53151
+#define clk_blsp2_qup6_spi_apps_clk_src 0x5ca86755
+#define clk_blsp2_uart1_apps_clk_src 0x562c66dc
+#define clk_blsp2_uart2_apps_clk_src 0xdd448080
+#define clk_blsp2_uart3_apps_clk_src 0x46b2e90f
+#define clk_blsp2_uart4_apps_clk_src 0x23a093d2
+#define clk_blsp2_uart5_apps_clk_src 0xe067616a
+#define clk_blsp2_uart6_apps_clk_src 0xe02d2829
+#define clk_gp1_clk_src 0xad85b97a
+#define clk_gp2_clk_src 0xfb1f0065
+#define clk_gp3_clk_src 0x63b693d6
+#define clk_hmss_rbcpr_clk_src 0xedd9a474
+#define clk_pdm2_clk_src 0x31e494fd
+#define clk_sdcc1_apps_clk_src 0xd4975db2
+#define clk_sdcc2_apps_clk_src 0xfc46c821
+#define clk_sdcc3_apps_clk_src 0xea34c7f4
+#define clk_sdcc4_apps_clk_src 0x7aaaaa0c
+#define clk_tsif_ref_clk_src 0x4e9042d1
+#define clk_usb20_mock_utmi_clk_src 0xc3aaeecb
+#define clk_usb30_mock_utmi_clk_src 0xa024a976
+#define clk_usb3_phy_aux_clk_src 0x15eec63c
+#define clk_gcc_qusb2phy_prim_reset 0x07550fa1
+#define clk_gcc_qusb2phy_sec_reset 0x3f3a87d0
+#define clk_gcc_periph_noc_usb20_ahb_clk 0xfb9f26e9
+#define clk_gcc_mmss_gcc_dbg_clk 0xe89d461c
+#define clk_cpu_dbg_clk 0x6550dfa9
+#define clk_gcc_blsp1_ahb_clk 0x8caa5b4f
+#define clk_gcc_blsp1_qup1_i2c_apps_clk 0xc303fae9
+#define clk_gcc_blsp1_qup1_spi_apps_clk 0x759a76b0
+#define clk_gcc_blsp1_qup2_i2c_apps_clk 0x1076f220
+#define clk_gcc_blsp1_qup2_spi_apps_clk 0x3e77d48f
+#define clk_gcc_blsp1_qup3_i2c_apps_clk 0x9e25ac82
+#define clk_gcc_blsp1_qup3_spi_apps_clk 0xfb978880
+#define clk_gcc_blsp1_qup4_i2c_apps_clk 0xd7f40f6f
+#define clk_gcc_blsp1_qup4_spi_apps_clk 0x80f8722f
+#define clk_gcc_blsp1_qup5_i2c_apps_clk 0xacae5604
+#define clk_gcc_blsp1_qup5_spi_apps_clk 0xbf3e15d7
+#define clk_gcc_blsp1_qup6_i2c_apps_clk 0x5c6ad820
+#define clk_gcc_blsp1_qup6_spi_apps_clk 0x780d9f85
+#define clk_gcc_blsp1_uart1_apps_clk 0xc7c62f90
+#define clk_gcc_blsp1_uart2_apps_clk 0xf8a61c96
+#define clk_gcc_blsp1_uart3_apps_clk 0xc3298bd7
+#define clk_gcc_blsp1_uart4_apps_clk 0x26be16c0
+#define clk_gcc_blsp1_uart5_apps_clk 0x28a6bc74
+#define clk_gcc_blsp1_uart6_apps_clk 0x28fd3466
+#define clk_gcc_blsp2_ahb_clk 0x8f283c1d
+#define clk_gcc_blsp2_qup1_i2c_apps_clk 0x9ace11dd
+#define clk_gcc_blsp2_qup1_spi_apps_clk 0xa32604cc
+#define clk_gcc_blsp2_qup2_i2c_apps_clk 0x1bf9a57e
+#define clk_gcc_blsp2_qup2_spi_apps_clk 0xbf54ca6d
+#define clk_gcc_blsp2_qup3_i2c_apps_clk 0x336d4170
+#define clk_gcc_blsp2_qup3_spi_apps_clk 0xc68509d6
+#define clk_gcc_blsp2_qup4_i2c_apps_clk 0xbd22539d
+#define clk_gcc_blsp2_qup4_spi_apps_clk 0x01a72b93
+#define clk_gcc_blsp2_qup5_i2c_apps_clk 0xe2b2ce1d
+#define clk_gcc_blsp2_qup5_spi_apps_clk 0xf40999cd
+#define clk_gcc_blsp2_qup6_i2c_apps_clk 0x894bcea4
+#define clk_gcc_blsp2_qup6_spi_apps_clk 0xfe1bd34a
+#define clk_gcc_blsp2_uart1_apps_clk 0x8c3512ff
+#define clk_gcc_blsp2_uart2_apps_clk 0x1e1965a3
+#define clk_gcc_blsp2_uart3_apps_clk 0x382415ab
+#define clk_gcc_blsp2_uart4_apps_clk 0x87a44b42
+#define clk_gcc_blsp2_uart5_apps_clk 0x5cd30649
+#define clk_gcc_blsp2_uart6_apps_clk 0x8feee5ab
+#define clk_gcc_boot_rom_ahb_clk 0xde2adeb1
+#define clk_gcc_gp1_clk 0x057f7b69
+#define clk_gcc_gp2_clk 0x9bf83ffd
+#define clk_gcc_gp3_clk 0xec6539ee
+#define clk_gcc_hmss_rbcpr_clk 0x699183be
+#define clk_gcc_mmss_noc_cfg_ahb_clk 0xb41a9d99
+#define clk_gcc_pcie_0_aux_clk 0x3d2e3ece
+#define clk_gcc_pcie_0_cfg_ahb_clk 0x4dd325c3
+#define clk_gcc_pcie_0_mstr_axi_clk 0x3f85285b
+#define clk_gcc_pcie_0_slv_axi_clk 0xd69638a1
+#define clk_gcc_pcie_0_pipe_clk 0x4f37621e
+#define clk_gcc_pcie_0_phy_reset 0xdc3201c1
+#define clk_gcc_pcie_1_aux_clk 0xc9bb962c
+#define clk_gcc_pcie_1_cfg_ahb_clk 0xb6338658
+#define clk_gcc_pcie_1_mstr_axi_clk 0xc20f6269
+#define clk_gcc_pcie_1_slv_axi_clk 0xd54e40d6
+#define clk_gcc_pcie_1_pipe_clk 0xc1627422
+#define clk_gcc_pcie_1_phy_reset 0x674481bb
+#define clk_gcc_pcie_2_aux_clk 0xa4dc7ae8
+#define clk_gcc_pcie_2_cfg_ahb_clk 0x4f1d3121
+#define clk_gcc_pcie_2_mstr_axi_clk 0x9e81724a
+#define clk_gcc_pcie_2_slv_axi_clk 0x7990d8b2
+#define clk_gcc_pcie_2_pipe_clk 0xa757a834
+#define clk_gcc_pcie_2_phy_reset 0x82634880
+#define clk_gcc_pcie_phy_reset 0x9bc3c959
+#define clk_gcc_pcie_phy_com_reset 0x8bf513e6
+#define clk_gcc_pcie_phy_nocsr_com_phy_reset 0x0c16a2da
+#define clk_gcc_pcie_phy_aux_clk 0x4746e74f
+#define clk_gcc_pcie_phy_cfg_ahb_clk 0x8533671a
+#define clk_gcc_pdm2_clk 0x99d55711
+#define clk_gcc_pdm_ahb_clk 0x365664f6
+#define clk_gcc_prng_ahb_clk 0x397e7eaa
+#define clk_gcc_sdcc1_ahb_clk 0x691e0caa
+#define clk_gcc_sdcc1_apps_clk 0x9ad6fb96
+#define clk_gcc_sdcc2_ahb_clk 0x23d5727f
+#define clk_gcc_sdcc2_apps_clk 0x861b20ac
+#define clk_gcc_sdcc3_ahb_clk 0x565b2c03
+#define clk_gcc_sdcc3_apps_clk 0x0b27aeac
+#define clk_gcc_sdcc4_ahb_clk 0x64f3e6a8
+#define clk_gcc_sdcc4_apps_clk 0xbf7c4dc8
+#define clk_gcc_tsif_ahb_clk 0x88d2822c
+#define clk_gcc_tsif_ref_clk 0x8f1ed2c2
+#define clk_gcc_ufs_ahb_clk 0x1914bb84
+#define clk_gcc_ufs_axi_clk 0x47c743a7
+#define clk_gcc_ufs_ice_core_clk 0x310b0710
+#define clk_gcc_ufs_rx_cfg_clk 0xa6747786
+#define clk_gcc_ufs_rx_symbol_0_clk 0x7f43251c
+#define clk_gcc_ufs_rx_symbol_1_clk 0x03182fde
+#define clk_gcc_ufs_tx_cfg_clk 0xba2cf8b5
+#define clk_gcc_ufs_tx_symbol_0_clk 0x6a9f747a
+#define clk_gcc_ufs_unipro_core_clk 0x2daf7fd2
+#define clk_gcc_ufs_sys_clk_core_clk 0x360e5ac8
+#define clk_gcc_ufs_tx_symbol_clk_core_clk 0xf6fb0df7
+#define clk_gcc_usb20_master_clk 0x24c3b66a
+#define clk_gcc_usb20_mock_utmi_clk 0xe8db8203
+#define clk_gcc_usb20_sleep_clk 0x6e8cb4b2
+#define clk_gcc_usb30_master_clk 0xb3b4e2cb
+#define clk_gcc_usb30_mock_utmi_clk 0xa800b65a
+#define clk_gcc_usb30_sleep_clk 0xd0b65c92
+#define clk_gcc_usb3_phy_aux_clk 0x0d9a36e0
+#define clk_gcc_usb3_phy_pipe_clk 0xf279aff2
+#define clk_gcc_usb_phy_cfg_ahb2phy_clk 0xd1231a0e
+#define clk_gcc_aggre0_cnoc_ahb_clk 0x53a35559
+#define clk_gcc_aggre0_snoc_axi_clk 0x3c446400
+#define clk_gcc_aggre0_noc_qosgen_extref_clk 0x8c4356ba
+#define clk_hlos1_vote_lpass_core_smmu_clk 0x3aaa1743
+#define clk_hlos1_vote_lpass_adsp_smmu_clk 0xc76f702f
+#define clk_gcc_usb3_phy_reset 0x03d559f1
+#define clk_gcc_usb3phy_phy_reset 0xb1a4f885
+#define clk_gcc_usb3_clkref_clk 0xb6cc8f01
+#define clk_gcc_hdmi_clkref_clk 0x4d4eec04
+#define clk_gcc_edp_clkref_clk 0xa8685c3f
+#define clk_gcc_ufs_clkref_clk 0x92aa126f
+#define clk_gcc_pcie_clkref_clk 0xa2e247fa
+#define clk_gcc_rx2_usb2_clkref_clk 0x27ec24ba
+#define clk_gcc_rx1_usb2_clkref_clk 0x53351d25
+#define clk_gcc_smmu_aggre0_ahb_clk 0x47a06ce4
+#define clk_gcc_smmu_aggre0_axi_clk 0x3cac4a6c
+#define clk_gcc_sys_noc_usb3_axi_clk 0x94d26800
+#define clk_gcc_sys_noc_ufs_axi_clk 0x19d38312
+#define clk_gcc_aggre2_usb3_axi_clk 0xd5822a8e
+#define clk_gcc_aggre2_ufs_axi_clk 0xb31e5191
+#define clk_gcc_mmss_gpll0_div_clk 0xdd06848d
+#define clk_gcc_mmss_bimc_gfx_clk 0xe4f28754
+#define clk_gcc_bimc_gfx_clk 0x3edd69ad
+#define clk_gcc_qspi_ahb_clk 0x96969dc8
+#define clk_gcc_qspi_ser_clk 0xfaf1e266
+#define clk_qspi_ser_clk_src 0x426676ee
+#define clk_sdcc1_ice_core_clk_src 0xfd6a4301
+#define clk_gcc_sdcc1_ice_core_clk 0x0fd5680a
+#define clk_gcc_mss_cfg_ahb_clk 0x111cde81
+#define clk_gcc_mss_snoc_axi_clk 0x0e71de85
+#define clk_gcc_mss_q6_bimc_axi_clk 0x67544d62
+#define clk_gcc_mss_mnoc_bimc_axi_clk 0xf665d03f
+#define clk_gpll0_out_msscc 0x7d794829
+#define clk_gcc_debug_mux_v2 0xf7e749f0
+#define clk_gcc_dcc_ahb_clk 0xfa14a88c
+#define clk_gcc_aggre0_noc_mpu_cfg_ahb_clk 0x5c1bb8e2
+
+/* clock_mmss controlled clocks */
+#define clk_mmsscc_xo 0x05e63704
+#define clk_mmsscc_gpll0 0xe900c515
+#define clk_mmsscc_gpll0_div 0x73892e05
+#define clk_mmsscc_mmssnoc_ahb 0x7b4bd6f7
+#define clk_mmpll0 0xdd83b751
+#define clk_mmpll0_out_main 0x2f996a31
+#define clk_mmpll1 0x6da7fb90
+#define clk_mmpll1_out_main 0xa0d3a7da
+#define clk_mmpll4 0x22c063c1
+#define clk_mmpll4_out_main 0xfb21c2fd
+#define clk_mmpll3 0x18c76899
+#define clk_mmpll3_out_main 0x6eb6328f
+#define clk_ahb_clk_src 0x86f49203
+#define clk_mmpll2 0x1190e4d8
+#define clk_mmpll2_out_main 0x1e9e24a8
+#define clk_mmpll8 0xd06ad45e
+#define clk_mmpll8_out_main 0x75b1f386
+#define clk_mmpll9 0x1c50684c
+#define clk_mmpll9_out_main 0x16b74937
+#define clk_mmpll5 0xa41e1936
+#define clk_mmpll5_out_main 0xcc1897bf
+#define clk_csi0_clk_src 0x227e65bc
+#define clk_vfe0_clk_src 0xa0c2bd8f
+#define clk_vfe1_clk_src 0x4e357366
+#define clk_csi1_clk_src 0x6a2a6c36
+#define clk_csi2_clk_src 0x4113589f
+#define clk_csi3_clk_src 0xfd934012
+#define clk_maxi_clk_src 0x52c09777
+#define clk_cpp_clk_src 0x8382f56d
+#define clk_jpeg0_clk_src 0x9a0a0ac3
+#define clk_jpeg2_clk_src 0x5ad927f3
+#define clk_jpeg_dma_clk_src 0xb68afcea
+#define clk_mdp_clk_src 0x6dc1f8f1
+#define clk_video_core_clk_src 0x8be4c944
+#define clk_fd_core_clk_src 0xe4799ab7
+#define clk_cci_clk_src 0x822f3d97
+#define clk_csiphy0_3p_clk_src 0xd2474b12
+#define clk_csiphy1_3p_clk_src 0x46a02aff
+#define clk_csiphy2_3p_clk_src 0x1447813f
+#define clk_camss_gp0_clk_src 0x6b57cfe6
+#define clk_camss_gp1_clk_src 0xf735368a
+#define clk_jpeg_dma_clk_src 0xb68afcea
+#define clk_mclk0_clk_src 0x266b3853
+#define clk_mclk1_clk_src 0xa73cad0c
+#define clk_mclk2_clk_src 0x42545468
+#define clk_mclk3_clk_src 0x2bfbb714
+#define clk_csi0phytimer_clk_src 0xc8a309be
+#define clk_csi1phytimer_clk_src 0x7c0fe23a
+#define clk_csi2phytimer_clk_src 0x62ffea9c
+#define clk_rbbmtimer_clk_src 0x17649ecc
+#define clk_esc0_clk_src 0xb41d7c38
+#define clk_esc1_clk_src 0x3b0afa42
+#define clk_hdmi_clk_src 0xb40aeea9
+#define clk_vsync_clk_src 0xecb43940
+#define clk_rbcpr_clk_src 0x2c2e9af2
+#define clk_video_subcore0_clk_src 0x88d79636
+#define clk_video_subcore1_clk_src 0x4966930c
+#define clk_mmss_bto_ahb_clk 0xfdf8c361
+#define clk_camss_ahb_clk 0xc4ff91d4
+#define clk_camss_cci_ahb_clk 0x04c4441a
+#define clk_camss_cci_clk 0xd6cb5eb9
+#define clk_camss_cpp_ahb_clk 0x12e9a87b
+#define clk_camss_cpp_clk 0xb82f366b
+#define clk_camss_cpp_axi_clk 0x5598c804
+#define clk_camss_cpp_vbif_ahb_clk 0xb5f31be4
+#define clk_camss_csi0_ahb_clk 0x6e29c972
+#define clk_camss_csi0_clk 0x30862ddb
+#define clk_camss_csi0phy_clk 0x2cecfb84
+#define clk_camss_csi0pix_clk 0x6946f77b
+#define clk_camss_csi0rdi_clk 0x83645ef5
+#define clk_camss_csi1_ahb_clk 0xccc15f06
+#define clk_camss_csi1_clk 0xb150f052
+#define clk_camss_csi1phy_clk 0xb989f06d
+#define clk_camss_csi1pix_clk 0x58d19bf3
+#define clk_camss_csi1rdi_clk 0x4d2f3352
+#define clk_camss_csi2_ahb_clk 0x92d02d75
+#define clk_camss_csi2_clk 0x74fc92e8
+#define clk_camss_csi2phy_clk 0xda05d9d8
+#define clk_camss_csi2pix_clk 0xf8ed0731
+#define clk_camss_csi2rdi_clk 0xdc1b2081
+#define clk_camss_csi3_ahb_clk 0xee5e459c
+#define clk_camss_csi3_clk 0x39488fdd
+#define clk_camss_csi3phy_clk 0x8b6063b9
+#define clk_camss_csi3pix_clk 0xd82bd467
+#define clk_camss_csi3rdi_clk 0xb6750046
+#define clk_camss_csi_vfe0_clk 0x3023937a
+#define clk_camss_csi_vfe1_clk 0xe66fa522
+#define clk_camss_csiphy0_3p_clk 0xf2a54f5a
+#define clk_camss_csiphy1_3p_clk 0x8bf70cb2
+#define clk_camss_csiphy2_3p_clk 0x1c14c939
+#define clk_camss_gp0_clk 0xcee7e51d
+#define clk_camss_gp1_clk 0x41f1c2e3
+#define clk_camss_ispif_ahb_clk 0x9a212c6d
+#define clk_camss_jpeg0_clk 0x0b0e2db7
+#define clk_camss_jpeg2_clk 0xd7291c8d
+#define clk_camss_jpeg_ahb_clk 0x1f47fd28
+#define clk_camss_jpeg_axi_clk 0x9e5545c8
+#define clk_camss_jpeg_dma_clk 0x2336e65d
+#define clk_camss_mclk0_clk 0xcf0c61e0
+#define clk_camss_mclk1_clk 0xd1410ed4
+#define clk_camss_mclk2_clk 0x851286f2
+#define clk_camss_mclk3_clk 0x4db11c45
+#define clk_camss_micro_ahb_clk 0x33a23277
+#define clk_camss_csi0phytimer_clk 0xff93b3c8
+#define clk_camss_csi1phytimer_clk 0x6c399ab6
+#define clk_camss_csi2phytimer_clk 0x24f47f49
+#define clk_camss_top_ahb_clk 0x8f8b2d33
+#define clk_camss_vfe_ahb_clk 0x595197bc
+#define clk_camss_vfe_axi_clk 0x273d4c31
+#define clk_camss_vfe0_ahb_clk 0x4652833c
+#define clk_camss_vfe0_clk 0x1e9bb8c4
+#define clk_camss_vfe0_stream_clk 0x22835fa4
+#define clk_camss_vfe1_ahb_clk 0x6a56abd3
+#define clk_camss_vfe1_clk 0x5bffa69b
+#define clk_camss_vfe1_stream_clk 0x92f849b9
+#define clk_fd_ahb_clk 0x868a2c5c
+#define clk_fd_core_clk 0x3badcae4
+#define clk_fd_core_uar_clk 0x7e624e15
+#define clk_gpu_ahb_clk 0xf97f1d43
+#define clk_gpu_aon_isense_clk 0xa9e9b297
+#define clk_gpu_gx_gfx3d_clk 0xb7ece823
+#define clk_gpu_mx_clk 0xb80ccedf
+#define clk_gpu_gx_rbbmtimer_clk 0xdeba634e
+#define clk_mdss_ahb_clk 0x684ccb41
+#define clk_mdss_axi_clk 0xcc07d687
+#define clk_mdss_esc0_clk 0x28cafbe6
+#define clk_mdss_esc1_clk 0xc22c6883
+#define clk_mdss_hdmi_ahb_clk 0x01cef516
+#define clk_mdss_hdmi_clk 0x097a6de9
+#define clk_mdss_mdp_clk 0x618336ac
+#define clk_mdss_vsync_clk 0x42a022d3
+#define clk_mmss_misc_ahb_clk 0xea30b0e7
+#define clk_mmss_misc_cxo_clk 0xe620cd80
+#define clk_mmagic_bimc_noc_cfg_ahb_clk 0x12d5ba72
+#define clk_mmagic_camss_axi_clk 0xa8b1c16b
+#define clk_mmagic_camss_noc_cfg_ahb_clk 0x5182c819
+#define clk_mmss_mmagic_cfg_ahb_clk 0x5e94a822
+#define clk_mmagic_mdss_axi_clk 0xa0359d10
+#define clk_mmagic_mdss_noc_cfg_ahb_clk 0x9c6d5482
+#define clk_mmagic_video_axi_clk 0x7b9219c3
+#define clk_mmagic_video_noc_cfg_ahb_clk 0x5124d256
+#define clk_mmss_mmagic_ahb_clk 0x3d15f2b0
+#define clk_mmss_mmagic_maxi_clk 0xbdaf5af7
+#define clk_mmss_rbcpr_ahb_clk 0x623ba55f
+#define clk_mmss_rbcpr_clk 0x69a23a6f
+#define clk_mmss_spdm_cpp_clk 0xefe35cd2
+#define clk_mmss_spdm_jpeg_dma_clk 0xcb7bd5a0
+#define clk_smmu_cpp_ahb_clk 0x3ad82d84
+#define clk_smmu_cpp_axi_clk 0xa6bb2f4a
+#define clk_smmu_jpeg_ahb_clk 0x10c436ec
+#define clk_smmu_jpeg_axi_clk 0x41112f37
+#define clk_smmu_mdp_ahb_clk 0x04994cb2
+#define clk_smmu_mdp_axi_clk 0x7fd71687
+#define clk_smmu_rot_ahb_clk 0xa30772c9
+#define clk_smmu_rot_axi_clk 0xfed7c078
+#define clk_smmu_vfe_ahb_clk 0x4dabebe7
+#define clk_smmu_vfe_axi_clk 0xde483725
+#define clk_smmu_video_ahb_clk 0x2d738e2c
+#define clk_smmu_video_axi_clk 0xe2b5b887
+#define clk_video_ahb_clk 0x90775cfb
+#define clk_video_axi_clk 0xe6c16dba
+#define clk_video_core_clk 0x7e876ec3
+#define clk_video_maxi_clk 0x97749db6
+#define clk_video_subcore0_clk 0xb6f63e6c
+#define clk_video_subcore1_clk 0x26c29cb4
+#define clk_vmem_ahb_clk 0xab6223ff
+#define clk_vmem_maxi_clk 0x15ef32db
+#define clk_mmss_debug_mux 0xe646ffda
+#define clk_mmss_gcc_dbg_clk 0xafa4d48a
+#define clk_gfx3d_clk_src 0x917f76ef
+#define clk_extpclk_clk_src 0xb2c31abd
+#define clk_mdss_byte0_clk 0xf5a03f64
+#define clk_mdss_byte1_clk 0xb8c7067d
+#define clk_mdss_extpclk_clk 0xfa5aadb0
+#define clk_mdss_pclk0_clk 0x3487234a
+#define clk_mdss_pclk1_clk 0xd5804246
+#define clk_gpu_gcc_dbg_clk 0x0ccc42cd
+#define clk_mdss_mdp_vote_clk 0x588460a4
+#define clk_mdss_rotator_vote_clk 0x5b1f675e
+#define clk_mmpll2_postdiv_clk 0x4fdeaaba
+#define clk_mmpll8_postdiv_clk 0xedf57882
+#define clk_mmpll9_postdiv_clk 0x3064b618
+#define clk_gfx3d_clk_src_v2 0x4210acb7
+#define clk_byte0_clk_src 0x75cc885b
+#define clk_byte1_clk_src 0x63c2c955
+#define clk_pclk0_clk_src 0xccac1f35
+#define clk_pclk1_clk_src 0x090f68ac
+#define clk_ext_byte0_clk_src 0xfb32f31e
+#define clk_ext_byte1_clk_src 0x585ef6d4
+#define clk_ext_pclk0_clk_src 0x087c1612
+#define clk_ext_pclk1_clk_src 0x8067c5a3
+
+/* clock_debug controlled clocks */
+#define clk_gcc_debug_mux 0x8121ac15
+
+/* external multimedia clocks */
+#define clk_dsi0pll_pixel_clk_mux 0x792379e1
+#define clk_dsi0pll_byte_clk_mux 0x60e83f06
+#define clk_dsi0pll_byte_clk_src 0xbbaa30be
+#define clk_dsi0pll_pixel_clk_src 0x45b3260f
+#define clk_dsi0pll_n2_div_clk 0x1474c213
+#define clk_dsi0pll_post_n1_div_clk 0xdab8c389
+#define clk_dsi0pll_vco_clk 0x15940d40
+#define clk_dsi1pll_pixel_clk_mux 0x36458019
+#define clk_dsi1pll_byte_clk_mux 0xb5a42b7b
+#define clk_dsi1pll_byte_clk_src 0x63930a8f
+#define clk_dsi1pll_pixel_clk_src 0x0e4c9b56
+#define clk_dsi1pll_n2_div_clk 0x2c9d4007
+#define clk_dsi1pll_post_n1_div_clk 0x03020041
+#define clk_dsi1pll_vco_clk 0x99797b50
+#define clk_mdss_dsi1_vco_clk_src 0xfcd15658
+#define clk_hdmi_vco_clk 0x66003284
+
+#define clk_dsi0pll_shadow_byte_clk_src 0x177c029c
+#define clk_dsi0pll_shadow_pixel_clk_src 0x98ae3c92
+#define clk_dsi0pll_shadow_n2_div_clk 0xd5f0dad9
+#define clk_dsi0pll_shadow_post_n1_div_clk 0x1f7c8cf8
+#define clk_dsi0pll_shadow_vco_clk 0xb100ca83
+#define clk_dsi1pll_shadow_byte_clk_src 0xfc021ce5
+#define clk_dsi1pll_shadow_pixel_clk_src 0xdcca3ffc
+#define clk_dsi1pll_shadow_n2_div_clk 0x189541bf
+#define clk_dsi1pll_shadow_post_n1_div_clk 0x1637020e
+#define clk_dsi1pll_shadow_vco_clk 0x68d8b6f7
+
+/* CPU clocks */
+#define clk_pwrcl_clk 0xc554130e
+#define clk_pwrcl_pll 0x25454ca1
+#define clk_pwrcl_alt_pll 0xc445471b
+#define clk_pwrcl_pll_main 0x28948e22
+#define clk_pwrcl_alt_pll_main 0x25c8270e
+#define clk_pwrcl_hf_mux 0x77706ae6
+#define clk_pwrcl_lf_mux 0xd99e334d
+#define clk_perfcl_clk 0x58869997
+#define clk_perfcl_pll 0x97dcec1c
+#define clk_perfcl_alt_pll 0xfe2eaea1
+#define clk_perfcl_pll_main 0x0dbf0c0b
+#define clk_perfcl_alt_pll_main 0x0b892aab
+#define clk_perfcl_hf_mux 0x9e8bbe59
+#define clk_perfcl_lf_mux 0x2f9c278d
+#define clk_cbf_pll 0xfe2e96a3
+#define clk_cbf_pll_main 0x2b05cf95
+#define clk_cbf_hf_mux 0x71244f73
+#define clk_cbf_clk 0x48e9e16b
+#define clk_xo_ao 0x428c856d
+#define clk_sys_apcsaux_clk 0x0b0dd513
+#define clk_cpu_debug_mux 0xc7acaa31
+
+/* Audio External Clocks */
+#define clk_audio_ap_clk 0x312ac429
+#define clk_audio_pmi_clk 0xb7ba2274
+#define clk_audio_ap_clk2 0xf0fbaf5b
+#define clk_audio_lpass_mclk2 0x0122abee
+#endif
diff --git a/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h b/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h
index 7e1394c..36d34b1 100644
--- a/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h
+++ b/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -62,47 +62,44 @@
#define GCC_GP2_CLK_SRC 44
#define GCC_GP3_CLK 45
#define GCC_GP3_CLK_SRC 46
-#define GCC_MSS_CFG_AHB_CLK 47
-#define GCC_MSS_GPLL0_DIV_CLK_SRC 48
-#define GCC_MSS_SNOC_AXI_CLK 49
-#define GCC_PCIE_0_CLKREF_CLK 50
-#define GCC_PCIE_AUX_CLK 51
-#define GCC_PCIE_AUX_PHY_CLK_SRC 52
-#define GCC_PCIE_CFG_AHB_CLK 53
-#define GCC_PCIE_MSTR_AXI_CLK 54
-#define GCC_PCIE_PHY_REFGEN_CLK 55
-#define GCC_PCIE_PHY_REFGEN_CLK_SRC 56
-#define GCC_PCIE_PIPE_CLK 57
-#define GCC_PCIE_SLEEP_CLK 58
-#define GCC_PCIE_SLV_AXI_CLK 59
-#define GCC_PCIE_SLV_Q2A_AXI_CLK 60
-#define GCC_PDM2_CLK 61
-#define GCC_PDM2_CLK_SRC 62
-#define GCC_PDM_AHB_CLK 63
-#define GCC_PDM_XO4_CLK 64
-#define GCC_PRNG_AHB_CLK 65
-#define GCC_SDCC1_AHB_CLK 66
-#define GCC_SDCC1_APPS_CLK 67
-#define GCC_SDCC1_APPS_CLK_SRC 68
-#define GCC_SPMI_FETCHER_AHB_CLK 69
-#define GCC_SPMI_FETCHER_CLK 70
-#define GCC_SPMI_FETCHER_CLK_SRC 71
-#define GCC_SYS_NOC_CPUSS_AHB_CLK 72
-#define GCC_SYS_NOC_USB3_CLK 73
-#define GCC_USB30_MASTER_CLK 74
-#define GCC_USB30_MASTER_CLK_SRC 75
-#define GCC_USB30_MOCK_UTMI_CLK 76
-#define GCC_USB30_MOCK_UTMI_CLK_SRC 77
-#define GCC_USB30_SLEEP_CLK 78
-#define GCC_USB3_PHY_AUX_CLK 79
-#define GCC_USB3_PHY_AUX_CLK_SRC 80
-#define GCC_USB3_PHY_PIPE_CLK 81
-#define GCC_USB3_PRIM_CLKREF_CLK 82
-#define GCC_USB_PHY_CFG_AHB2PHY_CLK 83
-#define GPLL0 84
-#define GPLL0_OUT_EVEN 85
-#define GPLL4 86
-#define GPLL4_OUT_EVEN 87
+#define GCC_PCIE_0_CLKREF_CLK 47
+#define GCC_PCIE_AUX_CLK 48
+#define GCC_PCIE_AUX_PHY_CLK_SRC 49
+#define GCC_PCIE_CFG_AHB_CLK 50
+#define GCC_PCIE_MSTR_AXI_CLK 51
+#define GCC_PCIE_PHY_REFGEN_CLK 52
+#define GCC_PCIE_PHY_REFGEN_CLK_SRC 53
+#define GCC_PCIE_PIPE_CLK 54
+#define GCC_PCIE_SLEEP_CLK 55
+#define GCC_PCIE_SLV_AXI_CLK 56
+#define GCC_PCIE_SLV_Q2A_AXI_CLK 57
+#define GCC_PDM2_CLK 58
+#define GCC_PDM2_CLK_SRC 59
+#define GCC_PDM_AHB_CLK 60
+#define GCC_PDM_XO4_CLK 61
+#define GCC_PRNG_AHB_CLK 62
+#define GCC_SDCC1_AHB_CLK 63
+#define GCC_SDCC1_APPS_CLK 64
+#define GCC_SDCC1_APPS_CLK_SRC 65
+#define GCC_SPMI_FETCHER_AHB_CLK 66
+#define GCC_SPMI_FETCHER_CLK 67
+#define GCC_SPMI_FETCHER_CLK_SRC 68
+#define GCC_SYS_NOC_CPUSS_AHB_CLK 69
+#define GCC_SYS_NOC_USB3_CLK 70
+#define GCC_USB30_MASTER_CLK 71
+#define GCC_USB30_MASTER_CLK_SRC 72
+#define GCC_USB30_MOCK_UTMI_CLK 73
+#define GCC_USB30_MOCK_UTMI_CLK_SRC 74
+#define GCC_USB30_SLEEP_CLK 75
+#define GCC_USB3_PHY_AUX_CLK 76
+#define GCC_USB3_PHY_AUX_CLK_SRC 77
+#define GCC_USB3_PHY_PIPE_CLK 78
+#define GCC_USB3_PRIM_CLKREF_CLK 79
+#define GCC_USB_PHY_CFG_AHB2PHY_CLK 80
+#define GPLL0 81
+#define GPLL0_OUT_EVEN 82
+#define GPLL4 83
+#define GPLL4_OUT_EVEN 84
/* CPU clocks */
#define CLOCK_A7SS 0
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 97498be..7995940 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -36,17 +36,26 @@
};
struct bpf_map {
- atomic_t refcnt;
+ /* 1st cacheline with read-mostly members of which some
+ * are also accessed in fast-path (e.g. ops, max_entries).
+ */
+ const struct bpf_map_ops *ops ____cacheline_aligned;
enum bpf_map_type map_type;
u32 key_size;
u32 value_size;
u32 max_entries;
u32 map_flags;
u32 pages;
- struct user_struct *user;
- const struct bpf_map_ops *ops;
- struct work_struct work;
+ bool unpriv_array;
+ /* 7 bytes hole */
+
+ /* 2nd cacheline with misc members to avoid false sharing
+ * particularly with refcounting.
+ */
+ struct user_struct *user ____cacheline_aligned;
+ atomic_t refcnt;
atomic_t usercnt;
+ struct work_struct work;
};
struct bpf_map_type_list {
@@ -189,6 +198,7 @@
struct bpf_array {
struct bpf_map map;
u32 elem_size;
+ u32 index_mask;
/* 'ownership' of prog_array is claimed by the first program that
* is going to use this map or by the first program which FD is stored
* in the map to make sure that all callers and callees have the same
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 4c4e935..070fc49 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -67,7 +67,10 @@
};
struct bpf_insn_aux_data {
- enum bpf_reg_type ptr_type; /* pointer type for load/store insns */
+ union {
+ enum bpf_reg_type ptr_type; /* pointer type for load/store insns */
+ struct bpf_map *map_ptr; /* pointer for call insn into lookup_elem */
+ };
bool seen; /* this insn was processed by the verifier */
};
diff --git a/include/linux/cacheinfo.h b/include/linux/cacheinfo.h
index 2189935..a951fd1 100644
--- a/include/linux/cacheinfo.h
+++ b/include/linux/cacheinfo.h
@@ -71,6 +71,7 @@
struct cacheinfo *info_list;
unsigned int num_levels;
unsigned int num_leaves;
+ bool cpu_map_populated;
};
/*
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 793255d..53a061e 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -42,6 +42,8 @@
* to first consumer that enables clk
*/
#define CLK_IS_MEASURE BIT(14) /* measure clock */
+/* do not call clk_change_rate on the clock's children */
+#define CLK_CHILD_NO_RATE_PROP BIT(15)
struct clk;
struct clk_hw;
diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h
index 0d442e3..47f5ba6 100644
--- a/include/linux/clockchips.h
+++ b/include/linux/clockchips.h
@@ -199,7 +199,7 @@
extern void clockevents_resume(void);
# ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
-# ifdef CONFIG_ARCH_HAS_TICK_BROADCAST
+# if defined(CONFIG_ARCH_HAS_TICK_BROADCAST) && defined(CONFIG_SMP)
extern void tick_broadcast(const struct cpumask *mask);
# else
# define tick_broadcast NULL
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 32c3d42..fabfc0b 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -272,11 +272,16 @@
struct device *dev, struct device_node *node);
extern struct coresight_cti_data *of_get_coresight_cti_data(
struct device *dev, struct device_node *node);
+extern int of_get_coresight_csr_name(struct device_node *node,
+ const char **csr_name);
+
#else
static inline struct coresight_platform_data *of_get_coresight_platform_data(
struct device *dev, struct device_node *node) { return NULL; }
static inline struct coresight_cti_data *of_get_coresight_cti_data(
struct device *dev, struct device_node *node) { return NULL; }
+static inline int of_get_coresight_csr_name(struct device_node *node,
+ const char **csr_name){ return -EINVAL; }
#endif
#ifdef CONFIG_PID_NS
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 3484287..1f7e4ec 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -44,6 +44,13 @@
extern int cpu_add_dev_attr_group(struct attribute_group *attrs);
extern void cpu_remove_dev_attr_group(struct attribute_group *attrs);
+extern ssize_t cpu_show_meltdown(struct device *dev,
+ struct device_attribute *attr, char *buf);
+extern ssize_t cpu_show_spectre_v1(struct device *dev,
+ struct device_attribute *attr, char *buf);
+extern ssize_t cpu_show_spectre_v2(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
extern __printf(4, 5)
struct device *cpu_device_create(struct device *parent, void *drvdata,
const struct attribute_group **groups,
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index d921206..6500554 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -51,7 +51,7 @@
CPUHP_SH_SH3X_PREPARE,
CPUHP_BLK_MQ_PREPARE,
CPUHP_TOPOLOGY_PREPARE,
- CPUHP_TIMERS_DEAD,
+ CPUHP_TIMERS_PREPARE,
CPUHP_NOTF_ERR_INJ_PREPARE,
CPUHP_MIPS_SOC_PREPARE,
CPUHP_BRINGUP_CPU,
diff --git a/include/linux/cred.h b/include/linux/cred.h
index f0e70a1..cf1a5d0 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -82,6 +82,7 @@
extern void set_groups(struct cred *, struct group_info *);
extern int groups_search(const struct group_info *, kgid_t);
extern bool may_setgroups(void);
+extern void groups_sort(struct group_info *);
/*
* The security context of a task
diff --git a/include/linux/fb.h b/include/linux/fb.h
index a964d07..2b9ece8 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -303,10 +303,18 @@
int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
unsigned long arg);
+ /* perform fb specific ioctl v2 (optional) - provides file param */
+ int (*fb_ioctl_v2)(struct fb_info *info, unsigned int cmd,
+ unsigned long arg, struct file *file);
+
/* Handle 32bit compat ioctl (optional) */
- int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
+ int (*fb_compat_ioctl)(struct fb_info *info, unsigned int cmd,
unsigned long arg);
+ /* Handle 32bit compat ioctl (optional) */
+ int (*fb_compat_ioctl_v2)(struct fb_info *info, unsigned int cmd,
+ unsigned long arg, struct file *file);
+
/* perform fb specific mmap */
int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
@@ -475,6 +483,7 @@
struct fb_cmap cmap; /* Current cmap */
struct list_head modelist; /* mode list */
struct fb_videomode *mode; /* current mode */
+ struct file *file; /* current file node */
#ifdef CONFIG_FB_BACKLIGHT
/* assigned backlight device */
diff --git a/include/linux/frame.h b/include/linux/frame.h
index e6baaba..d772c61 100644
--- a/include/linux/frame.h
+++ b/include/linux/frame.h
@@ -11,7 +11,7 @@
* For more information, see tools/objtool/Documentation/stack-validation.txt.
*/
#define STACK_FRAME_NON_STANDARD(func) \
- static void __used __section(__func_stack_frame_non_standard) \
+ static void __used __section(.discard.func_stack_frame_non_standard) \
*__func_stack_frame_non_standard_##func = func
#else /* !CONFIG_STACK_VALIDATION */
diff --git a/include/linux/fscache.h b/include/linux/fscache.h
index 115bb81..94a8aae 100644
--- a/include/linux/fscache.h
+++ b/include/linux/fscache.h
@@ -764,7 +764,7 @@
{
if (fscache_cookie_valid(cookie) && PageFsCache(page))
return __fscache_maybe_release_page(cookie, page, gfp);
- return false;
+ return true;
}
/**
diff --git a/include/linux/input/synaptics_dsx_v2_6.h b/include/linux/input/synaptics_dsx_v2_6.h
index 7deef35..52241e5 100644
--- a/include/linux/input/synaptics_dsx_v2_6.h
+++ b/include/linux/input/synaptics_dsx_v2_6.h
@@ -5,6 +5,7 @@
*
* Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
* Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -34,13 +35,13 @@
#ifndef _SYNAPTICS_DSX_H_
#define _SYNAPTICS_DSX_H_
-#define PLATFORM_DRIVER_NAME "synaptics_dsx"
-#define STYLUS_DRIVER_NAME "synaptics_dsx_stylus"
-#define ACTIVE_PEN_DRIVER_NAME "synaptics_dsx_active_pen"
-#define PROXIMITY_DRIVER_NAME "synaptics_dsx_proximity"
-#define GESTURE_DRIVER_NAME "synaptics_dsx_gesture"
-#define I2C_DRIVER_NAME "synaptics_dsx_i2c"
-#define SPI_DRIVER_NAME "synaptics_dsx_spi"
+#define PLATFORM_DRIVER_NAME "synaptics_dsxv26"
+#define STYLUS_DRIVER_NAME "synaptics_dsxv26_stylus"
+#define ACTIVE_PEN_DRIVER_NAME "synaptics_dsxv26_active_pen"
+#define PROXIMITY_DRIVER_NAME "synaptics_dsxv26_proximity"
+#define GESTURE_DRIVER_NAME "synaptics_dsxv26_gesture"
+#define I2C_DRIVER_NAME "synaptics_dsxv26"
+#define SPI_DRIVER_NAME "synaptics_dsxv26"
/*
* struct synaptics_dsx_button_map - button map
@@ -57,6 +58,7 @@
* @x_flip: x flip flag
* @y_flip: y flip flag
* @swap_axes: swap axes flag
+ * @resume_in_workqueue: defer resume function to workqueue
* @irq_gpio: attention interrupt GPIO
* @irq_on_state: attention interrupt active state
* @power_gpio: power switch GPIO
@@ -84,6 +86,7 @@
bool x_flip;
bool y_flip;
bool swap_axes;
+ bool resume_in_workqueue;
int irq_gpio;
int irq_on_state;
int power_gpio;
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index bbc65ef..3562047 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -14,6 +14,7 @@
#include <linux/hrtimer.h>
#include <linux/kref.h>
#include <linux/workqueue.h>
+#include <linux/sched.h>
#include <linux/atomic.h>
#include <asm/ptrace.h>
@@ -498,6 +499,13 @@
return this_cpu_read(ksoftirqd);
}
+static inline bool ksoftirqd_running_on(int cpu)
+{
+ struct task_struct *tsk = per_cpu(ksoftirqd, cpu);
+
+ return tsk && (tsk->state == TASK_RUNNING);
+}
+
/* Tasklets --- multithreaded analogue of BHs.
Main feature differing them of generic softirqs: tasklet
diff --git a/include/linux/ipa_usb.h b/include/linux/ipa_usb.h
index de11633..cceae83 100644
--- a/include/linux/ipa_usb.h
+++ b/include/linux/ipa_usb.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -121,14 +121,16 @@
* @dir: channel direction
* @xfer_ring_len: length of transfer ring in bytes (must be integral
* multiple of transfer element size - 16B for xDCI)
- * @xfer_ring_base_addr: physical base address of transfer ring. Address must be
- * aligned to xfer_ring_len rounded to power of two
* @xfer_scratch: parameters for xDCI channel scratch
- * @xfer_ring_base_addr_iova: IO virtual address mapped to xfer_ring_base_addr
+ * @xfer_ring_base_addr_iova: IO virtual address mapped to pysical base address
* @data_buff_base_len: length of data buffer allocated by USB driver
- * @data_buff_base_addr: physical base address for the data buffer (where TRBs
- * points)
- * @data_buff_base_addr_iova: IO virtual address mapped to data_buff_base_addr
+ * @data_buff_base_addr_iova: IO virtual address mapped to pysical base address
+ * @sgt_xfer_rings: Scatter table for Xfer rings,contains valid non NULL
+ * value
+ * when USB S1-SMMU enabed, else NULL.
+ * @sgt_data_buff: Scatter table for data buffs,contains valid non NULL
+ * value
+ * when USB S1-SMMU enabed, else NULL.
*
*/
struct ipa_usb_xdci_chan_params {
@@ -143,12 +145,12 @@
/* transfer ring params */
enum gsi_chan_dir dir;
u16 xfer_ring_len;
- u64 xfer_ring_base_addr;
struct ipa_usb_xdci_chan_scratch xfer_scratch;
u64 xfer_ring_base_addr_iova;
u32 data_buff_base_len;
- u64 data_buff_base_addr;
u64 data_buff_base_addr_iova;
+ struct sg_table *sgt_xfer_rings;
+ struct sg_table *sgt_data_buff;
};
/**
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 3b94400..11ff751 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -248,7 +248,8 @@
* 100: prefer care-of address
*/
dontfrag:1,
- autoflowlabel:1;
+ autoflowlabel:1,
+ autoflowlabel_set:1;
__u8 min_hopcount;
__u8 tclass;
__be32 rcv_flowinfo;
diff --git a/include/linux/kaiser.h b/include/linux/kaiser.h
new file mode 100644
index 0000000..58c55b1
--- /dev/null
+++ b/include/linux/kaiser.h
@@ -0,0 +1,52 @@
+#ifndef _LINUX_KAISER_H
+#define _LINUX_KAISER_H
+
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+#include <asm/kaiser.h>
+
+static inline int kaiser_map_thread_stack(void *stack)
+{
+ /*
+ * Map that page of kernel stack on which we enter from user context.
+ */
+ return kaiser_add_mapping((unsigned long)stack +
+ THREAD_SIZE - PAGE_SIZE, PAGE_SIZE, __PAGE_KERNEL);
+}
+
+static inline void kaiser_unmap_thread_stack(void *stack)
+{
+ /*
+ * Note: may be called even when kaiser_map_thread_stack() failed.
+ */
+ kaiser_remove_mapping((unsigned long)stack +
+ THREAD_SIZE - PAGE_SIZE, PAGE_SIZE);
+}
+#else
+
+/*
+ * These stubs are used whenever CONFIG_PAGE_TABLE_ISOLATION is off, which
+ * includes architectures that support KAISER, but have it disabled.
+ */
+
+static inline void kaiser_init(void)
+{
+}
+static inline int kaiser_add_mapping(unsigned long addr,
+ unsigned long size, unsigned long flags)
+{
+ return 0;
+}
+static inline void kaiser_remove_mapping(unsigned long start,
+ unsigned long size)
+{
+}
+static inline int kaiser_map_thread_stack(void *stack)
+{
+ return 0;
+}
+static inline void kaiser_unmap_thread_stack(void *stack)
+{
+}
+
+#endif /* !CONFIG_PAGE_TABLE_ISOLATION */
+#endif /* _LINUX_KAISER_H */
diff --git a/include/linux/leds-qpnp-flash.h b/include/linux/leds-qpnp-flash.h
index 1fe6e17..e3b9cf1 100644
--- a/include/linux/leds-qpnp-flash.h
+++ b/include/linux/leds-qpnp-flash.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -21,7 +21,14 @@
#define FLASH_LED_PREPARE_OPTIONS_MASK GENMASK(3, 0)
-int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
+#if (defined CONFIG_LEDS_QPNP_FLASH || defined CONFIG_LEDS_QPNP_FLASH_V2)
+extern int (*qpnp_flash_led_prepare)(struct led_trigger *trig, int options,
int *max_current);
-
+#else
+static inline int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
+ int *max_current)
+{
+ return -ENODEV;
+}
+#endif
#endif
diff --git a/include/linux/mdss_io_util.h b/include/linux/mdss_io_util.h
index 028f3e3..dd0b17c 100644
--- a/include/linux/mdss_io_util.h
+++ b/include/linux/mdss_io_util.h
@@ -28,26 +28,26 @@
#define DEV_WARN(fmt, args...) pr_warn(fmt, ##args)
#define DEV_ERR(fmt, args...) pr_err(fmt, ##args)
-struct dss_io_data {
+struct mdss_io_data {
u32 len;
void __iomem *base;
};
-void dss_reg_w(struct dss_io_data *io, u32 offset, u32 value, u32 debug);
-u32 dss_reg_r(struct dss_io_data *io, u32 offset, u32 debug);
-void dss_reg_dump(void __iomem *base, u32 len, const char *prefix, u32 debug);
+void mdss_reg_w(struct mdss_io_data *io, u32 offset, u32 value, u32 debug);
+u32 mdss_reg_r(struct mdss_io_data *io, u32 offset, u32 debug);
+void mdss_reg_dump(void __iomem *base, u32 len, const char *prefix, u32 debug);
-#define DSS_REG_W_ND(io, offset, val) dss_reg_w(io, offset, val, false)
-#define DSS_REG_W(io, offset, val) dss_reg_w(io, offset, val, true)
-#define DSS_REG_R_ND(io, offset) dss_reg_r(io, offset, false)
-#define DSS_REG_R(io, offset) dss_reg_r(io, offset, true)
+#define DSS_REG_W_ND(io, offset, val) mdss_reg_w(io, offset, val, false)
+#define DSS_REG_W(io, offset, val) mdss_reg_w(io, offset, val, true)
+#define DSS_REG_R_ND(io, offset) mdss_reg_r(io, offset, false)
+#define DSS_REG_R(io, offset) mdss_reg_r(io, offset, true)
-enum dss_vreg_type {
+enum mdss_vreg_type {
DSS_REG_LDO,
DSS_REG_VS,
};
-enum dss_vreg_mode {
+enum mdss_vreg_mode {
DSS_REG_MODE_ENABLE,
DSS_REG_MODE_DISABLE,
DSS_REG_MODE_LP,
@@ -55,7 +55,7 @@
DSS_REG_MODE_MAX,
};
-struct dss_vreg {
+struct mdss_vreg {
struct regulator *vreg; /* vreg handle */
char vreg_name[32];
int min_voltage;
@@ -67,51 +67,52 @@
int post_off_sleep;
};
-struct dss_gpio {
+struct mdss_gpio {
unsigned int gpio;
unsigned int value;
char gpio_name[32];
};
-enum dss_clk_type {
+enum mdss_clk_type {
DSS_CLK_AHB, /* no set rate. rate controlled through rpm */
DSS_CLK_PCLK,
DSS_CLK_OTHER,
};
-struct dss_clk {
+struct mdss_clk {
struct clk *clk; /* clk handle */
char clk_name[32];
- enum dss_clk_type type;
+ enum mdss_clk_type type;
unsigned long rate;
};
-struct dss_module_power {
+struct mdss_module_power {
unsigned int num_vreg;
- struct dss_vreg *vreg_config;
+ struct mdss_vreg *vreg_config;
unsigned int num_gpio;
- struct dss_gpio *gpio_config;
+ struct mdss_gpio *gpio_config;
unsigned int num_clk;
- struct dss_clk *clk_config;
+ struct mdss_clk *clk_config;
};
-int msm_dss_ioremap_byname(struct platform_device *pdev,
- struct dss_io_data *io_data, const char *name);
-void msm_dss_iounmap(struct dss_io_data *io_data);
+int msm_mdss_ioremap_byname(struct platform_device *pdev,
+ struct mdss_io_data *io_data, const char *name);
+void msm_mdss_iounmap(struct mdss_io_data *io_data);
-int msm_dss_enable_gpio(struct dss_gpio *in_gpio, int num_gpio, int enable);
-int msm_dss_gpio_enable(struct dss_gpio *in_gpio, int num_gpio, int enable);
+int msm_mdss_enable_gpio(struct mdss_gpio *in_gpio, int num_gpio, int enable);
+int msm_mdss_gpio_enable(struct mdss_gpio *in_gpio, int num_gpio, int enable);
-int msm_dss_config_vreg(struct device *dev, struct dss_vreg *in_vreg,
+int msm_mdss_config_vreg(struct device *dev, struct mdss_vreg *in_vreg,
int num_vreg, int config);
-int msm_dss_enable_vreg(struct dss_vreg *in_vreg, int num_vreg, int enable);
-int msm_dss_config_vreg_opt_mode(struct dss_vreg *in_vreg, int num_vreg,
- enum dss_vreg_mode mode);
+int msm_mdss_enable_vreg(struct mdss_vreg *in_vreg, int num_vreg, int enable);
+int msm_mdss_config_vreg_opt_mode(struct mdss_vreg *in_vreg, int num_vreg,
+ enum mdss_vreg_mode mode);
-int msm_dss_get_clk(struct device *dev, struct dss_clk *clk_arry, int num_clk);
-void msm_dss_put_clk(struct dss_clk *clk_arry, int num_clk);
-int msm_dss_clk_set_rate(struct dss_clk *clk_arry, int num_clk);
-int msm_dss_enable_clk(struct dss_clk *clk_arry, int num_clk, int enable);
+int msm_mdss_get_clk(struct device *dev, struct mdss_clk *clk_arry,
+ int num_clk);
+void msm_mdss_put_clk(struct mdss_clk *clk_arry, int num_clk);
+int msm_mdss_clk_set_rate(struct mdss_clk *clk_arry, int num_clk);
+int msm_mdss_enable_clk(struct mdss_clk *clk_arry, int num_clk, int enable);
int mdss_i2c_byte_read(struct i2c_client *client, uint8_t slave_addr,
uint8_t reg_offset, uint8_t *read_buf);
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 8b35bdb..fd77f83 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -490,9 +490,21 @@
extern int do_swap_account;
#endif
-void lock_page_memcg(struct page *page);
+struct mem_cgroup *lock_page_memcg(struct page *page);
+void __unlock_page_memcg(struct mem_cgroup *memcg);
void unlock_page_memcg(struct page *page);
+static inline void __mem_cgroup_update_page_stat(struct page *page,
+ struct mem_cgroup *memcg,
+ enum mem_cgroup_stat_index idx,
+ int val)
+{
+ VM_BUG_ON(!(rcu_read_lock_held() || PageLocked(page)));
+
+ if (memcg && memcg->stat)
+ this_cpu_add(memcg->stat->count[idx], val);
+}
+
/**
* mem_cgroup_update_page_stat - update page state statistics
* @page: the page
@@ -508,13 +520,12 @@
* mem_cgroup_update_page_stat(page, state, -1);
* unlock_page(page) or unlock_page_memcg(page)
*/
+
static inline void mem_cgroup_update_page_stat(struct page *page,
enum mem_cgroup_stat_index idx, int val)
{
- VM_BUG_ON(!(rcu_read_lock_held() || PageLocked(page)));
- if (page->mem_cgroup)
- this_cpu_add(page->mem_cgroup->stat->count[idx], val);
+ __mem_cgroup_update_page_stat(page, page->mem_cgroup, idx, val);
}
static inline void mem_cgroup_inc_page_stat(struct page *page,
@@ -709,7 +720,12 @@
{
}
-static inline void lock_page_memcg(struct page *page)
+static inline struct mem_cgroup *lock_page_memcg(struct page *page)
+{
+ return NULL;
+}
+
+static inline void __unlock_page_memcg(struct mem_cgroup *memcg)
{
}
@@ -745,6 +761,13 @@
{
}
+static inline void __mem_cgroup_update_page_stat(struct page *page,
+ struct mem_cgroup *memcg,
+ enum mem_cgroup_stat_index idx,
+ int nr)
+{
+}
+
static inline void mem_cgroup_inc_page_stat(struct page *page,
enum mem_cgroup_stat_index idx)
{
diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h
index 6045d4d..20ee90c 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -143,7 +143,7 @@
MLX5_CMD_OP_ALLOC_Q_COUNTER = 0x771,
MLX5_CMD_OP_DEALLOC_Q_COUNTER = 0x772,
MLX5_CMD_OP_QUERY_Q_COUNTER = 0x773,
- MLX5_CMD_OP_SET_RATE_LIMIT = 0x780,
+ MLX5_CMD_OP_SET_PP_RATE_LIMIT = 0x780,
MLX5_CMD_OP_QUERY_RATE_LIMIT = 0x781,
MLX5_CMD_OP_ALLOC_PD = 0x800,
MLX5_CMD_OP_DEALLOC_PD = 0x801,
@@ -737,6 +737,12 @@
MLX5_CAP_PORT_TYPE_ETH = 0x1,
};
+enum {
+ MLX5_CAP_UMR_FENCE_STRONG = 0x0,
+ MLX5_CAP_UMR_FENCE_SMALL = 0x1,
+ MLX5_CAP_UMR_FENCE_NONE = 0x2,
+};
+
struct mlx5_ifc_cmd_hca_cap_bits {
u8 reserved_at_0[0x80];
@@ -838,7 +844,9 @@
u8 striding_rq[0x1];
u8 reserved_at_201[0x2];
u8 ipoib_basic_offloads[0x1];
- u8 reserved_at_205[0xa];
+ u8 reserved_at_205[0x5];
+ u8 umr_fence[0x2];
+ u8 reserved_at_20c[0x3];
u8 drain_sigerr[0x1];
u8 cmdif_checksum[0x2];
u8 sigerr_cqe[0x1];
@@ -6689,7 +6697,7 @@
u8 vxlan_udp_port[0x10];
};
-struct mlx5_ifc_set_rate_limit_out_bits {
+struct mlx5_ifc_set_pp_rate_limit_out_bits {
u8 status[0x8];
u8 reserved_at_8[0x18];
@@ -6698,7 +6706,7 @@
u8 reserved_at_40[0x40];
};
-struct mlx5_ifc_set_rate_limit_in_bits {
+struct mlx5_ifc_set_pp_rate_limit_in_bits {
u8 opcode[0x10];
u8 reserved_at_10[0x10];
@@ -6711,6 +6719,8 @@
u8 reserved_at_60[0x20];
u8 rate_limit[0x20];
+
+ u8 reserved_at_a0[0x160];
};
struct mlx5_ifc_access_register_out_bits {
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 16155d0..932e99c 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1901,6 +1901,7 @@
/* page_alloc.c */
extern int min_free_kbytes;
extern int watermark_scale_factor;
+extern int extra_free_kbytes;
/* nommu.c */
extern atomic_long_t mmap_pages_allocated;
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 90900c2..0c28c28 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -133,8 +133,9 @@
NR_SLAB_UNRECLAIMABLE,
NR_PAGETABLE, /* used for pagetables */
NR_KERNEL_STACK_KB, /* measured in KiB */
- /* Second 128 byte cacheline */
+ NR_KAISERTABLE,
NR_BOUNCE,
+ /* Second 128 byte cacheline */
#if IS_ENABLED(CONFIG_ZSMALLOC)
NR_ZSPAGES, /* allocated in zsmalloc */
#endif
diff --git a/include/linux/percpu-defs.h b/include/linux/percpu-defs.h
index 8f16299..8902f23 100644
--- a/include/linux/percpu-defs.h
+++ b/include/linux/percpu-defs.h
@@ -35,6 +35,12 @@
#endif
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+#define USER_MAPPED_SECTION "..user_mapped"
+#else
+#define USER_MAPPED_SECTION ""
+#endif
+
/*
* Base implementations of per-CPU variable declarations and definitions, where
* the section in which the variable is to be placed is provided by the
@@ -115,6 +121,12 @@
#define DEFINE_PER_CPU(type, name) \
DEFINE_PER_CPU_SECTION(type, name, "")
+#define DECLARE_PER_CPU_USER_MAPPED(type, name) \
+ DECLARE_PER_CPU_SECTION(type, name, USER_MAPPED_SECTION)
+
+#define DEFINE_PER_CPU_USER_MAPPED(type, name) \
+ DEFINE_PER_CPU_SECTION(type, name, USER_MAPPED_SECTION)
+
/*
* Declaration/definition used for per-CPU variables that must come first in
* the set of variables.
@@ -144,6 +156,14 @@
DEFINE_PER_CPU_SECTION(type, name, PER_CPU_SHARED_ALIGNED_SECTION) \
____cacheline_aligned_in_smp
+#define DECLARE_PER_CPU_SHARED_ALIGNED_USER_MAPPED(type, name) \
+ DECLARE_PER_CPU_SECTION(type, name, USER_MAPPED_SECTION PER_CPU_SHARED_ALIGNED_SECTION) \
+ ____cacheline_aligned_in_smp
+
+#define DEFINE_PER_CPU_SHARED_ALIGNED_USER_MAPPED(type, name) \
+ DEFINE_PER_CPU_SECTION(type, name, USER_MAPPED_SECTION PER_CPU_SHARED_ALIGNED_SECTION) \
+ ____cacheline_aligned_in_smp
+
#define DECLARE_PER_CPU_ALIGNED(type, name) \
DECLARE_PER_CPU_SECTION(type, name, PER_CPU_ALIGNED_SECTION) \
____cacheline_aligned
@@ -162,11 +182,21 @@
#define DEFINE_PER_CPU_PAGE_ALIGNED(type, name) \
DEFINE_PER_CPU_SECTION(type, name, "..page_aligned") \
__aligned(PAGE_SIZE)
+/*
+ * Declaration/definition used for per-CPU variables that must be page aligned and need to be mapped in user mode.
+ */
+#define DECLARE_PER_CPU_PAGE_ALIGNED_USER_MAPPED(type, name) \
+ DECLARE_PER_CPU_SECTION(type, name, USER_MAPPED_SECTION"..page_aligned") \
+ __aligned(PAGE_SIZE)
+
+#define DEFINE_PER_CPU_PAGE_ALIGNED_USER_MAPPED(type, name) \
+ DEFINE_PER_CPU_SECTION(type, name, USER_MAPPED_SECTION"..page_aligned") \
+ __aligned(PAGE_SIZE)
/*
* Declaration/definition used for per-CPU variables that must be read mostly.
*/
-#define DECLARE_PER_CPU_READ_MOSTLY(type, name) \
+#define DECLARE_PER_CPU_READ_MOSTLY(type, name) \
DECLARE_PER_CPU_SECTION(type, name, "..read_mostly")
#define DEFINE_PER_CPU_READ_MOSTLY(type, name) \
diff --git a/include/linux/phy.h b/include/linux/phy.h
index a04d69a..867110c 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -684,6 +684,17 @@
}
/**
+ * phy_interface_mode_is_rgmii - Convenience function for testing if a
+ * PHY interface mode is RGMII (all variants)
+ * @mode: the phy_interface_t enum
+ */
+static inline bool phy_interface_mode_is_rgmii(phy_interface_t mode)
+{
+ return mode >= PHY_INTERFACE_MODE_RGMII &&
+ mode <= PHY_INTERFACE_MODE_RGMII_TXID;
+};
+
+/**
* phy_interface_is_rgmii - Convenience function for testing if a PHY interface
* is RGMII (all variants)
* @phydev: the phy_device struct
diff --git a/include/linux/ptr_ring.h b/include/linux/ptr_ring.h
index b83507c..e38f471 100644
--- a/include/linux/ptr_ring.h
+++ b/include/linux/ptr_ring.h
@@ -99,12 +99,18 @@
/* Note: callers invoking this in a loop must use a compiler barrier,
* for example cpu_relax(). Callers must hold producer_lock.
+ * Callers are responsible for making sure pointer that is being queued
+ * points to a valid data.
*/
static inline int __ptr_ring_produce(struct ptr_ring *r, void *ptr)
{
if (unlikely(!r->size) || r->queue[r->producer])
return -ENOSPC;
+ /* Make sure the pointer we are storing points to a valid data. */
+ /* Pairs with smp_read_barrier_depends in __ptr_ring_consume. */
+ smp_wmb();
+
r->queue[r->producer++] = ptr;
if (unlikely(r->producer >= r->size))
r->producer = 0;
@@ -244,6 +250,9 @@
if (ptr)
__ptr_ring_discard_one(r);
+ /* Make sure anyone accessing data through the pointer is up to date. */
+ /* Pairs with smp_wmb in __ptr_ring_produce. */
+ smp_read_barrier_depends();
return ptr;
}
diff --git a/include/linux/sh_eth.h b/include/linux/sh_eth.h
index f2e27e0..01b3778 100644
--- a/include/linux/sh_eth.h
+++ b/include/linux/sh_eth.h
@@ -16,7 +16,6 @@
unsigned char mac_addr[ETH_ALEN];
unsigned no_ether_link:1;
unsigned ether_link_active_low:1;
- unsigned needs_init:1;
};
#endif
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 647532b..f50b717c 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -219,7 +219,8 @@
} rack;
u16 advmss; /* Advertised MSS */
u8 rate_app_limited:1, /* rate_{delivered,interval_us} limited? */
- unused:7;
+ is_sack_reneg:1, /* in recovery from loss with SACK reneg? */
+ unused:6;
u8 nonagle : 4,/* Disable Nagle algorithm? */
thin_lto : 1,/* Use linear timeouts for thin streams */
thin_dupack : 1,/* Fast retransmit on first dupack */
diff --git a/include/linux/timer.h b/include/linux/timer.h
index 5f5107b..c2476e2 100644
--- a/include/linux/timer.h
+++ b/include/linux/timer.h
@@ -237,9 +237,11 @@
unsigned long round_jiffies_up_relative(unsigned long j);
#ifdef CONFIG_HOTPLUG_CPU
+int timers_prepare_cpu(unsigned int cpu);
int timers_dead_cpu(unsigned int cpu);
#else
-#define timers_dead_cpu NULL
+#define timers_prepare_cpu NULL
+#define timers_dead_cpu NULL
#endif
#endif
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 40144f3..a41244f 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -394,6 +394,8 @@
/* tty_io.c */
extern int __init tty_init(void);
extern const char *tty_name(const struct tty_struct *tty);
+extern int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout);
+extern void tty_ldisc_unlock(struct tty_struct *tty);
#else
static inline void console_init(void)
{ }
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 972dabc..3fda92f 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -84,6 +84,8 @@
* @db_reg_phs_addr_lsb: IPA channel doorbell register's physical address LSB
* @mapped_db_reg_phs_addr_lsb: doorbell LSB IOVA address mapped with IOMMU
* @db_reg_phs_addr_msb: IPA channel doorbell register's physical address MSB
+ * @sgt_trb_xfer_ring: USB TRB ring related sgtable entries
+ * @sgt_data_buff: Data buffer related sgtable entries
*/
struct usb_gsi_request {
void *buf_base_addr;
@@ -93,6 +95,8 @@
u32 db_reg_phs_addr_lsb;
dma_addr_t mapped_db_reg_phs_addr_lsb;
u32 db_reg_phs_addr_msb;
+ struct sg_table sgt_trb_xfer_ring;
+ struct sg_table sgt_data_buff;
};
/*
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
new file mode 100644
index 0000000..c3e980e
--- /dev/null
+++ b/include/linux/usb/msm_hsusb.h
@@ -0,0 +1,357 @@
+/* include/linux/usb/msm_hsusb.h
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Brian Swetland <swetland@google.com>
+ * Copyright (c) 2009-2018, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __ASM_ARCH_MSM_HSUSB_H
+#define __ASM_ARCH_MSM_HSUSB_H
+
+#include <linux/types.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/clk.h>
+#include <linux/pm_qos.h>
+#include <linux/hrtimer.h>
+#include <linux/power_supply.h>
+#include <linux/cdev.h>
+#include <linux/usb_bam.h>
+#include <linux/extcon.h>
+#include <linux/regulator/driver.h>
+/**
+ * Requested USB votes for NOC frequency
+ *
+ * USB_NOC_NOM_VOTE Vote for NOM set of NOC frequencies
+ * USB_NOC_SVS_VOTE Vote for SVS set of NOC frequencies
+ *
+ */
+enum usb_noc_mode {
+ USB_NOC_NOM_VOTE = 0,
+ USB_NOC_SVS_VOTE,
+ USB_NOC_NUM_VOTE,
+};
+
+/**
+ * Different states involved in USB charger detection.
+ *
+ * USB_CHG_STATE_UNDEFINED USB charger is not connected or detection
+ * process is not yet started.
+ * USB_CHG_STATE_IN_PROGRESS Charger detection in progress
+ * USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact.
+ * USB_CHG_STATE_DCD_DONE Data pin contact is detected.
+ * USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects
+ * between SDP and DCP/CDP).
+ * USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects
+ * between DCP and CDP).
+ * USB_CHG_STATE_DETECTED USB charger type is determined.
+ *
+ */
+enum usb_chg_state {
+ USB_CHG_STATE_UNDEFINED = 0,
+ USB_CHG_STATE_IN_PROGRESS,
+ USB_CHG_STATE_WAIT_FOR_DCD,
+ USB_CHG_STATE_DCD_DONE,
+ USB_CHG_STATE_PRIMARY_DONE,
+ USB_CHG_STATE_SECONDARY_DONE,
+ USB_CHG_STATE_DETECTED,
+};
+
+/**
+ * USB charger types
+ *
+ * USB_INVALID_CHARGER Invalid USB charger.
+ * USB_SDP_CHARGER Standard downstream port. Refers to a downstream port
+ * on USB2.0 compliant host/hub.
+ * USB_DCP_CHARGER Dedicated charger port (AC charger/ Wall charger).
+ * USB_CDP_CHARGER Charging downstream port. Enumeration can happen and
+ * IDEV_CHG_MAX can be drawn irrespective of USB state.
+ * USB_NONCOMPLIANT_CHARGER A non-compliant charger pull DP and DM to specific
+ * voltages between 2.0-3.3v for identification.
+ *
+ */
+enum usb_chg_type {
+ USB_INVALID_CHARGER = 0,
+ USB_SDP_CHARGER,
+ USB_DCP_CHARGER,
+ USB_CDP_CHARGER,
+ USB_NONCOMPLIANT_CHARGER,
+ USB_FLOATED_CHARGER,
+};
+
+/**
+ * Maintain state for hvdcp external charger status
+ * DEFAULT This is used when DCP is detected
+ * ACTIVE This is used when ioctl is called to block LPM
+ * INACTIVE This is used when ioctl is called to unblock LPM
+ */
+
+enum usb_ext_chg_status {
+ DEFAULT = 1,
+ ACTIVE,
+ INACTIVE,
+};
+
+/**
+ * USB ID state
+ */
+enum usb_id_state {
+ USB_ID_GROUND = 0,
+ USB_ID_FLOAT,
+};
+
+#define USB_NUM_BUS_CLOCKS 3
+
+/**
+ * struct msm_otg: OTG driver data. Shared by HCD and DCD.
+ * @otg: USB OTG Transceiver structure.
+ * @pdata: otg device platform data.
+ * @irq: IRQ number assigned for HSUSB controller.
+ * @async_irq: IRQ number used by some controllers during low power state
+ * @phy_irq: IRQ number assigned for PHY to notify events like id and line
+ state changes.
+ * @pclk: clock struct of iface_clk.
+ * @core_clk: clock struct of core_bus_clk.
+ * @sleep_clk: clock struct of sleep_clk for USB PHY.
+ * @phy_reset_clk: clock struct of phy_reset_clk for USB PHY. This clock is
+ a reset only clock and resets the PHY, ULPI bridge and
+ CSR wrapper.
+ * @phy_por_clk: clock struct of phy_por_clk for USB PHY. This clock is
+ a reset only clock and resets only the PHY (POR).
+ * @phy_csr_clk: clock struct of phy_csr_clk for USB PHY. This clock is
+ required to access PHY CSR registers via AHB2PHY interface.
+ * @bus_clks: bimc/snoc/pcnoc clock struct.
+ * @core_reset: Reset control for core_clk
+ * @phy_reset: Reset control for phy_reset_clk
+ * @phy_por_reset: Reset control for phy_por_clk
+ * @default_noc_mode: default frequency for NOC clocks - SVS or NOM
+ * @core_clk_rate: core clk max frequency
+ * @regs: ioremapped register base address.
+ * @usb_phy_ctrl_reg: relevant PHY_CTRL_REG register base address.
+ * @inputs: OTG state machine inputs(Id, SessValid etc).
+ * @sm_work: OTG state machine work.
+ * @sm_work_pending: OTG state machine work is pending, queued post pm_resume
+ * @resume_pending: USB h/w lpm_exit pending. Done on next sm_work run
+ * @pm_suspended: OTG device is system(PM) suspended.
+ * @pm_notify: Notifier to receive system wide PM transition events.
+ It is used to defer wakeup events processing until
+ system is RESUMED.
+ * @in_lpm: indicates low power mode (LPM) state.
+ * @async_int: IRQ line on which ASYNC interrupt arrived in LPM.
+ * @cur_power: The amount of mA available from downstream port.
+ * @otg_wq: Strict order otg workqueue for OTG works (SM/ID/SUSPEND).
+ * @chg_work: Charger detection work.
+ * @chg_state: The state of charger detection process.
+ * @chg_type: The type of charger attached.
+ * @bus_perf_client: Bus performance client handle to request BUS bandwidth
+ * @host_bus_suspend: indicates host bus suspend or not.
+ * @device_bus_suspend: indicates device bus suspend or not.
+ * @bus_clks_enabled: indicates pcnoc/snoc/bimc clocks are on or not.
+ * @chg_check_timer: The timer used to implement the workaround to detect
+ * very slow plug in of wall charger.
+ * @bc1p2_current_max: Max charging current allowed as per bc1.2 chg detection
+ * @typec_current_max: Max charging current allowed as per type-c chg detection
+ * @is_ext_chg_dcp: To indicate whether charger detected by external entity
+ SMB hardware is DCP charger or not.
+ * @ext_id_irq: IRQ for ID interrupt.
+ * @phy_irq_pending: Gets set when PHY IRQ arrives in LPM.
+ * @id_state: Indicates USBID line status.
+ * @rm_pulldown: Indicates pulldown status on D+ and D- data lines.
+ * @extcon_vbus: Used for VBUS notification registration.
+ * @extcon_id: Used for ID notification registration.
+ * @vbus_nb: Notification callback for VBUS event.
+ * @id_nb: Notification callback for ID event.
+ * @dpdm_desc: Regulator descriptor for D+ and D- voting.
+ * @dpdm_rdev: Regulator class device for dpdm regulator.
+ * @dbg_idx: Dynamic debug buffer Index.
+ * @dbg_lock: Dynamic debug buffer Lock.
+ * @buf: Dynamic Debug Buffer.
+ * @max_nominal_system_clk_rate: max freq at which system clock can run in
+ nominal mode.
+ */
+struct msm_otg {
+ struct usb_phy phy;
+ struct msm_otg_platform_data *pdata;
+ struct platform_device *pdev;
+ int irq;
+ int async_irq;
+ int phy_irq;
+ struct clk *xo_clk;
+ struct clk *pclk;
+ struct clk *core_clk;
+ struct clk *sleep_clk;
+ struct clk *phy_reset_clk;
+ struct clk *phy_por_clk;
+ struct clk *phy_csr_clk;
+ struct clk *bus_clks[USB_NUM_BUS_CLOCKS];
+ struct clk *phy_ref_clk;
+ struct reset_control *core_reset;
+ struct reset_control *phy_reset;
+ struct reset_control *phy_por_reset;
+ long core_clk_rate;
+ long core_clk_svs_rate;
+ long core_clk_nominal_rate;
+ enum usb_noc_mode default_noc_mode;
+ struct resource *io_res;
+ void __iomem *regs;
+ void __iomem *phy_csr_regs;
+ void __iomem *usb_phy_ctrl_reg;
+#define ID 0
+#define B_SESS_VLD 1
+#define A_BUS_SUSPEND 14
+#define B_FALSE_SDP 18
+ unsigned long inputs;
+ struct work_struct sm_work;
+ bool sm_work_pending;
+ bool resume_pending;
+ atomic_t pm_suspended;
+ struct notifier_block pm_notify;
+ atomic_t in_lpm;
+ bool err_event_seen;
+ int async_int;
+ unsigned int cur_power;
+ struct workqueue_struct *otg_wq;
+ struct delayed_work chg_work;
+ struct delayed_work id_status_work;
+ enum usb_chg_state chg_state;
+ enum usb_chg_type chg_type;
+ unsigned int dcd_time;
+ unsigned long caps;
+ uint32_t bus_perf_client;
+ bool host_bus_suspend;
+ bool device_bus_suspend;
+ bool bus_clks_enabled;
+ struct timer_list chg_check_timer;
+ /*
+ * Allowing PHY power collpase turns off the HSUSB 3.3v and 1.8v
+ * analog regulators while going to low power mode.
+ * Currently only 28nm PHY has the support to allowing PHY
+ * power collapse since it doesn't have leakage currents while
+ * turning off the power rails.
+ */
+#define ALLOW_PHY_POWER_COLLAPSE BIT(0)
+ /*
+ * Allow PHY RETENTION mode before turning off the digital
+ * voltage regulator(VDDCX).
+ */
+#define ALLOW_PHY_RETENTION BIT(1)
+ /*
+ * Allow putting the core in Low Power mode, when
+ * USB bus is suspended but cable is connected.
+ */
+#define ALLOW_LPM_ON_DEV_SUSPEND BIT(2)
+ /*
+ * Allowing PHY regulators LPM puts the HSUSB 3.3v and 1.8v
+ * analog regulators into LPM while going to USB low power mode.
+ */
+#define ALLOW_PHY_REGULATORS_LPM BIT(3)
+ /*
+ * Allow PHY RETENTION mode before turning off the digital
+ * voltage regulator(VDDCX) during host mode.
+ */
+#define ALLOW_HOST_PHY_RETENTION BIT(4)
+ /*
+ * Allow VDD minimization without putting PHY into retention
+ * for fixing PHY current leakage issue when LDOs ar turned off.
+ */
+#define ALLOW_VDD_MIN_WITH_RETENTION_DISABLED BIT(5)
+
+ /*
+ * PHY can keep D+ pull-up during peripheral bus suspend and
+ * D+/D- pull-down during host bus suspend without any
+ * re-work. This is possible only when PHY DVDD is supplied
+ * by a PMIC LDO (unlike VDDCX/VDDMX).
+ */
+#define ALLOW_BUS_SUSPEND_WITHOUT_REWORK BIT(6)
+ unsigned long lpm_flags;
+#define PHY_PWR_COLLAPSED BIT(0)
+#define PHY_RETENTIONED BIT(1)
+#define XO_SHUTDOWN BIT(2)
+#define CLOCKS_DOWN BIT(3)
+#define PHY_REGULATORS_LPM BIT(4)
+ int reset_counter;
+ unsigned int online;
+ unsigned int host_mode;
+ unsigned int bc1p2_current_max;
+ unsigned int typec_current_max;
+
+ dev_t ext_chg_dev;
+ struct cdev ext_chg_cdev;
+ struct class *ext_chg_class;
+ struct device *ext_chg_device;
+ bool ext_chg_opened;
+ enum usb_ext_chg_status ext_chg_active;
+ struct completion ext_chg_wait;
+ struct pinctrl *phy_pinctrl;
+ bool is_ext_chg_dcp;
+ struct qpnp_vadc_chip *vadc_dev;
+ int ext_id_irq;
+ bool phy_irq_pending;
+ enum usb_id_state id_state;
+ bool rm_pulldown;
+ struct extcon_dev *extcon_vbus;
+ struct extcon_dev *extcon_id;
+ struct notifier_block vbus_nb;
+ struct notifier_block id_nb;
+ struct regulator_desc dpdm_rdesc;
+ struct regulator_dev *dpdm_rdev;
+/* Maximum debug message length */
+#define DEBUG_MSG_LEN 128UL
+/* Maximum number of messages */
+#define DEBUG_MAX_MSG 256UL
+ unsigned int dbg_idx;
+ rwlock_t dbg_lock;
+
+ char (buf[DEBUG_MAX_MSG])[DEBUG_MSG_LEN]; /* buffer */
+ unsigned int vbus_state;
+ unsigned int usb_irq_count;
+ int pm_qos_latency;
+ struct pm_qos_request pm_qos_req_dma;
+ struct delayed_work perf_vote_work;
+};
+
+struct ci13xxx_platform_data {
+ u8 usb_core_id;
+ /*
+ * value of 2^(log2_itc-1) will be used as the interrupt threshold
+ * (ITC), when log2_itc is between 1 to 7.
+ */
+ int log2_itc;
+ bool l1_supported;
+ bool enable_ahb2ahb_bypass;
+ bool enable_streaming;
+ bool enable_axi_prefetch;
+};
+
+#ifdef CONFIG_USB_BAM
+void msm_bam_set_usb_host_dev(struct device *dev);
+bool msm_usb_bam_enable(enum usb_ctrl ctrl, bool bam_enable);
+int msm_do_bam_disable_enable(enum usb_ctrl ctrl);
+#else
+static inline void msm_bam_set_usb_host_dev(struct device *dev) {}
+static inline bool msm_usb_bam_enable(enum usb_ctrl ctrl, bool bam_enable)
+{
+ return true;
+}
+int msm_do_bam_disable_enable(enum usb_ctrl ctrl) { return true; }
+#endif
+#ifdef CONFIG_USB_CI13XXX_MSM
+void msm_hw_soft_reset(void);
+#else
+static inline void msm_hw_soft_reset(void)
+{
+}
+#endif
+
+#endif
diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h
index 974c379..daa245d 100644
--- a/include/linux/usb/msm_hsusb_hw.h
+++ b/include/linux/usb/msm_hsusb_hw.h
@@ -25,6 +25,9 @@
#define ULPI_TX_PKT_EN_CLR_FIX BIT(19)
#define USB_CAPLENGTH (MSM_USB_BASE + 0x0100) /* 8 bit */
+#define USB_HS_APF_CTRL (MSM_USB_BASE + 0x0380)
+
+#define APF_CTRL_EN BIT(0)
#define USB_USBCMD (MSM_USB_BASE + 0x0140)
#define USB_PORTSC (MSM_USB_BASE + 0x0184)
@@ -34,15 +37,24 @@
#define USB_PHY_CTRL2 (MSM_USB_BASE + 0x0278)
#define GENCONFIG_2_SESS_VLD_CTRL_EN BIT(7)
+#define GENCONFIG_2_LINESTATE_DIFF_WAKEUP_EN BIT(12)
+#define GENCONFIG_2_DPSE_DMSE_HV_INTR_EN BIT(15)
#define USBCMD_SESS_VLD_CTRL BIT(25)
#define USBCMD_RESET 2
#define USB_USBINTR (MSM_USB_BASE + 0x0148)
+#define AHB2AHB_BYPASS BIT(31)
+#define AHB2AHB_BYPASS_BIT_MASK BIT(31)
+#define AHB2AHB_BYPASS_CLEAR (0 << 31)
+
#define PORTSC_PHCD (1 << 23) /* phy suspend mode */
#define PORTSC_PTS_MASK (3 << 30)
#define PORTSC_PTS_ULPI (2 << 30)
#define PORTSC_PTS_SERIAL (3 << 30)
+#define PORTSC_LS (3 << 10)
+#define PORTSC_LS_DM (1 << 10)
+#define PORTSC_CCS (1 << 0)
#define USB_ULPI_VIEWPORT (MSM_USB_BASE + 0x0170)
#define ULPI_RUN (1 << 30)
@@ -63,10 +75,16 @@
#define ASYNC_INTR_CTRL (1 << 29) /* Enable async interrupt */
#define ULPI_STP_CTRL (1 << 30) /* Block communication with PHY */
#define PHY_RETEN (1 << 1) /* PHY retention enable/disable */
+#define PHY_IDHV_INTEN (1 << 8) /* PHY ID HV interrupt */
+#define PHY_OTGSESSVLDHV_INTEN (1 << 9) /* PHY Session Valid HV int. */
+#define PHY_CLAMP_DPDMSE_EN (1 << 21) /* PHY mpm DP DM clamp enable */
+#define PHY_POR_BIT_MASK BIT(0)
#define PHY_POR_ASSERT (1 << 0) /* USB2 28nm PHY POR ASSERT */
+#define PHY_POR_DEASSERT (0 << 0) /* USB2 28nm PHY POR DEASSERT */
/* OTG definitions */
#define OTGSC_INTSTS_MASK (0x7f << 16)
+#define OTGSC_IDPU (1 << 5)
#define OTGSC_ID (1 << 8)
#define OTGSC_BSV (1 << 11)
#define OTGSC_IDIS (1 << 16)
@@ -74,4 +92,29 @@
#define OTGSC_IDIE (1 << 24)
#define OTGSC_BSVIE (1 << 27)
+/* USB PHY CSR registers and bit definitions */
+
+#define USB_PHY_CSR_PHY_CTRL_COMMON0 (MSM_USB_PHY_CSR_BASE + 0x078)
+#define SIDDQ BIT(2)
+
+#define USB_PHY_CSR_PHY_CTRL1 (MSM_USB_PHY_CSR_BASE + 0x08C)
+#define ID_HV_CLAMP_EN_N BIT(1)
+
+#define USB_PHY_CSR_PHY_CTRL3 (MSM_USB_PHY_CSR_BASE + 0x094)
+#define CLAMP_MPM_DPSE_DMSE_EN_N BIT(2)
+
+#define USB2_PHY_USB_PHY_IRQ_CMD (MSM_USB_PHY_CSR_BASE + 0x0D0)
+#define USB2_PHY_USB_PHY_INTERRUPT_SRC_STATUS (MSM_USB_PHY_CSR_BASE + 0x05C)
+
+#define USB2_PHY_USB_PHY_INTERRUPT_CLEAR0 (MSM_USB_PHY_CSR_BASE + 0x0DC)
+#define USB2_PHY_USB_PHY_INTERRUPT_CLEAR1 (MSM_USB_PHY_CSR_BASE + 0x0E0)
+
+#define USB2_PHY_USB_PHY_INTERRUPT_MASK1 (MSM_USB_PHY_CSR_BASE + 0x0D8)
+
+#define USB_PHY_IDDIG_1_0 BIT(7)
+
+#define USB_PHY_IDDIG_RISE_MASK BIT(0)
+#define USB_PHY_IDDIG_FALL_MASK BIT(1)
+#define USB_PHY_ID_MASK (USB_PHY_IDDIG_RISE_MASK | USB_PHY_IDDIG_FALL_MASK)
+
#endif /* __LINUX_USB_GADGET_MSM72K_UDC_H__ */
diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h
index 64aa52e..d999b3c 100644
--- a/include/linux/usb/phy.h
+++ b/include/linux/usb/phy.h
@@ -58,6 +58,7 @@
OTG_STATE_B_SRP_INIT,
OTG_STATE_B_PERIPHERAL,
OTG_STATE_B_SUSPEND,
+ OTG_STATE_B_CHARGER,
/* extra dual-role default-b states */
OTG_STATE_B_WAIT_ACON,
@@ -141,6 +142,10 @@
/* reset the PHY clocks */
int (*reset)(struct usb_phy *x);
+
+ /* for notification of usb_phy_dbg_events */
+ void (*dbg_event)(struct usb_phy *x,
+ char *event, int msg1, int msg2);
int (*disable_chirp)(struct usb_phy *x, bool disable);
};
diff --git a/include/linux/usb/usb_qdss.h b/include/linux/usb/usb_qdss.h
index b58d8ee..fe626c18 100644
--- a/include/linux/usb/usb_qdss.h
+++ b/include/linux/usb/usb_qdss.h
@@ -15,6 +15,9 @@
#include <linux/kernel.h>
+#define USB_QDSS_CH_MDM "qdss_mdm"
+#define USB_QDSS_CH_MSM "qdss"
+
struct qdss_request {
char *buf;
int length;
diff --git a/include/linux/vm_event_item.h b/include/linux/vm_event_item.h
index 9cc195f..a9c2e4c 100644
--- a/include/linux/vm_event_item.h
+++ b/include/linux/vm_event_item.h
@@ -89,10 +89,8 @@
#endif
#endif
#ifdef CONFIG_DEBUG_TLBFLUSH
-#ifdef CONFIG_SMP
NR_TLB_REMOTE_FLUSH, /* cpu tried to flush others' tlbs */
NR_TLB_REMOTE_FLUSH_RECEIVED,/* cpu received ipi for flush */
-#endif /* CONFIG_SMP */
NR_TLB_LOCAL_FLUSH_ALL,
NR_TLB_LOCAL_FLUSH_ONE,
#endif /* CONFIG_DEBUG_TLBFLUSH */
diff --git a/include/net/arp.h b/include/net/arp.h
index 5e0f891..1b3f869 100644
--- a/include/net/arp.h
+++ b/include/net/arp.h
@@ -19,6 +19,9 @@
static inline struct neighbour *__ipv4_neigh_lookup_noref(struct net_device *dev, u32 key)
{
+ if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
+ key = INADDR_ANY;
+
return ___neigh_lookup_noref(&arp_tbl, neigh_key_eq32, arp_hashfn, &key, dev);
}
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 63ce902..d5e79f1 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -55,6 +55,12 @@
*/
#define CFG80211_ROAMED_API_UNIFIED 1
+/* Indicate backport support for DBS scan control */
+#define CFG80211_SCAN_DBS_CONTROL_SUPPORT 1
+
+/* Indicate backport support for per chain rssi scan */
+#define CFG80211_SCAN_PER_CHAIN_RSSI_SUPPORT 1
+
/**
* DOC: Introduction
*
@@ -1742,6 +1748,8 @@
* by %parent_bssid.
* @parent_bssid: the BSS according to which %parent_tsf is set. This is set to
* the BSS that requested the scan in which the beacon/probe was received.
+ * @chains: bitmask for filled values in @chain_signal.
+ * @chain_signal: per-chain signal strength of last received BSS in dBm.
*/
struct cfg80211_inform_bss {
struct ieee80211_channel *chan;
@@ -1750,6 +1758,8 @@
u64 boottime_ns;
u64 parent_tsf;
u8 parent_bssid[ETH_ALEN] __aligned(2);
+ u8 chains;
+ s8 chain_signal[IEEE80211_MAX_CHAINS];
};
/**
@@ -1793,6 +1803,8 @@
* that holds the beacon data. @beacon_ies is still valid, of course, and
* points to the same data as hidden_beacon_bss->beacon_ies in that case.
* @signal: signal strength value (type depends on the wiphy's signal_type)
+ * @chains: bitmask for filled values in @chain_signal.
+ * @chain_signal: per-chain signal strength of last received BSS in dBm.
* @priv: private area for driver use, has at least wiphy->bss_priv_size bytes
*/
struct cfg80211_bss {
@@ -1811,6 +1823,8 @@
u16 capability;
u8 bssid[ETH_ALEN];
+ u8 chains;
+ s8 chain_signal[IEEE80211_MAX_CHAINS];
u8 priv[0] __aligned(sizeof(void *));
};
diff --git a/include/net/ip.h b/include/net/ip.h
index 4ef6792..ad065a4 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -33,6 +33,8 @@
#include <net/flow.h>
#include <net/flow_dissector.h>
+#define IPV4_MIN_MTU 68 /* RFC 791 */
+
struct sock;
struct inet_skb_parm {
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 615ce0a..e64210c 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -290,6 +290,7 @@
int flags);
int ip6_flowlabel_init(void);
void ip6_flowlabel_cleanup(void);
+bool ip6_autoflowlabel(struct net *net, const struct ipv6_pinfo *np);
static inline void fl6_sock_release(struct ip6_flowlabel *fl)
{
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 2c7d876..8fd61bc 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1007,7 +1007,7 @@
* @RX_FLAG_DECRYPTED: This frame was decrypted in hardware.
* @RX_FLAG_MMIC_STRIPPED: the Michael MIC is stripped off this frame,
* verification has been done by the hardware.
- * @RX_FLAG_IV_STRIPPED: The IV/ICV are stripped from this frame.
+ * @RX_FLAG_IV_STRIPPED: The IV and ICV are stripped from this frame.
* If this flag is set, the stack cannot do any replay detection
* hence the driver or hardware will have to do that.
* @RX_FLAG_PN_VALIDATED: Currently only valid for CCMP/GCMP frames, this
@@ -1078,6 +1078,8 @@
* @RX_FLAG_ALLOW_SAME_PN: Allow the same PN as same packet before.
* This is used for AMSDU subframes which can have the same PN as
* the first subframe.
+ * @RX_FLAG_ICV_STRIPPED: The ICV is stripped from this frame. CRC checking must
+ * be done in the hardware.
*/
enum mac80211_rx_flags {
RX_FLAG_MMIC_ERROR = BIT(0),
@@ -1113,6 +1115,7 @@
RX_FLAG_RADIOTAP_VENDOR_DATA = BIT(31),
RX_FLAG_MIC_STRIPPED = BIT_ULL(32),
RX_FLAG_ALLOW_SAME_PN = BIT_ULL(33),
+ RX_FLAG_ICV_STRIPPED = BIT_ULL(34),
};
#define RX_FLAG_STBC_SHIFT 26
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index 0940598..23102da 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -213,6 +213,11 @@
return net1 == net2;
}
+static inline int check_net(const struct net *net)
+{
+ return atomic_read(&net->count) != 0;
+}
+
void net_drop_ns(void *);
#else
@@ -237,6 +242,11 @@
return 1;
}
+static inline int check_net(const struct net *net)
+{
+ return 1;
+}
+
#define net_drop_ns NULL
#endif
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 775c3bd..448aec0 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1017,7 +1017,7 @@
void tcp_rate_skb_delivered(struct sock *sk, struct sk_buff *skb,
struct rate_sample *rs);
void tcp_rate_gen(struct sock *sk, u32 delivered, u32 lost,
- struct skb_mstamp *now, struct rate_sample *rs);
+ bool is_sack_reneg, struct skb_mstamp *now, struct rate_sample *rs);
void tcp_rate_check_app_limited(struct sock *sk);
/* These functions determine how the current flow behaves in respect of SACK
diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h
index ad38816..31b4cce 100644
--- a/include/soc/qcom/icnss.h
+++ b/include/soc/qcom/icnss.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -23,7 +23,6 @@
#endif
enum icnss_uevent {
- ICNSS_UEVENT_FW_READY,
ICNSS_UEVENT_FW_CRASHED,
ICNSS_UEVENT_FW_DOWN,
};
diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h
index 505e82b..cbfe7e4 100644
--- a/include/soc/qcom/socinfo.h
+++ b/include/soc/qcom/socinfo.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -108,6 +108,8 @@
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sda670")
#define early_machine_is_msm8953() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8953")
+#define early_machine_is_msm8937() \
+ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8937")
#define early_machine_is_sdm450() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdm450")
#define early_machine_is_sdm632() \
@@ -155,6 +157,7 @@
#define early_machine_is_qcs605() 0
#define early_machine_is_sda670() 0
#define early_machine_is_msm8953() 0
+#define early_machine_is_msm8937() 0
#define early_machine_is_sdm450() 0
#define early_machine_is_sdm632() 0
#endif
@@ -225,6 +228,7 @@
MSM_CPU_SDM450,
MSM_CPU_SDM632,
MSM_CPU_SDA632,
+ MSM_CPU_8937
};
struct msm_soc_info {
diff --git a/include/sound/wcd-dsp-mgr.h b/include/sound/wcd-dsp-mgr.h
index 7ba1817..52b52c4 100644
--- a/include/sound/wcd-dsp-mgr.h
+++ b/include/sound/wcd-dsp-mgr.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -80,6 +80,7 @@
/* Software generated signal indicating debug dumps to be collected */
WDSP_DEBUG_DUMP,
+ WDSP_DEBUG_DUMP_INTERNAL,
};
/*
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index eb3b23b..30f99ce 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -493,6 +493,7 @@
#define CMD_T_BUSY (1 << 9)
#define CMD_T_TAS (1 << 10)
#define CMD_T_FABRIC_STOP (1 << 11)
+#define CMD_T_PRE_EXECUTE (1 << 12)
spinlock_t t_state_lock;
struct kref cmd_kref;
struct completion t_transport_stop_comp;
diff --git a/include/trace/events/kvm.h b/include/trace/events/kvm.h
index 8ade3eb..90fce4d 100644
--- a/include/trace/events/kvm.h
+++ b/include/trace/events/kvm.h
@@ -208,7 +208,7 @@
{ KVM_TRACE_MMIO_WRITE, "write" }
TRACE_EVENT(kvm_mmio,
- TP_PROTO(int type, int len, u64 gpa, u64 val),
+ TP_PROTO(int type, int len, u64 gpa, void *val),
TP_ARGS(type, len, gpa, val),
TP_STRUCT__entry(
@@ -222,7 +222,10 @@
__entry->type = type;
__entry->len = len;
__entry->gpa = gpa;
- __entry->val = val;
+ __entry->val = 0;
+ if (val)
+ memcpy(&__entry->val, val,
+ min_t(u32, sizeof(__entry->val), len));
),
TP_printk("mmio %s len %u gpa 0x%llx val 0x%llx",
diff --git a/include/uapi/linux/eventpoll.h b/include/uapi/linux/eventpoll.h
index 81c464a..6016e9e 100644
--- a/include/uapi/linux/eventpoll.h
+++ b/include/uapi/linux/eventpoll.h
@@ -26,8 +26,21 @@
#define EPOLL_CTL_DEL 2
#define EPOLL_CTL_MOD 3
+/* Epoll event masks */
+#define EPOLLIN 0x00000001
+#define EPOLLPRI 0x00000002
+#define EPOLLOUT 0x00000004
+#define EPOLLERR 0x00000008
+#define EPOLLHUP 0x00000010
+#define EPOLLRDNORM 0x00000040
+#define EPOLLRDBAND 0x00000080
+#define EPOLLWRNORM 0x00000100
+#define EPOLLWRBAND 0x00000200
+#define EPOLLMSG 0x00000400
+#define EPOLLRDHUP 0x00002000
+
/* Set exclusive wakeup mode for the target file descriptor */
-#define EPOLLEXCLUSIVE (1 << 28)
+#define EPOLLEXCLUSIVE (1U << 28)
/*
* Request the handling of system wakeup events so as to prevent system suspends
@@ -39,13 +52,13 @@
*
* Requires CAP_BLOCK_SUSPEND
*/
-#define EPOLLWAKEUP (1 << 29)
+#define EPOLLWAKEUP (1U << 29)
/* Set the One Shot behaviour for the target file descriptor */
-#define EPOLLONESHOT (1 << 30)
+#define EPOLLONESHOT (1U << 30)
/* Set the Edge Triggered behaviour for the target file descriptor */
-#define EPOLLET (1 << 31)
+#define EPOLLET (1U << 31)
/*
* On x86-64 make the 64bit structure have the same alignment as the
diff --git a/include/uapi/linux/msm_mdp.h b/include/uapi/linux/msm_mdp.h
index 73f4938..8a0e4cf 100644
--- a/include/uapi/linux/msm_mdp.h
+++ b/include/uapi/linux/msm_mdp.h
@@ -1410,6 +1410,11 @@
MDP_CSC_ITU_R_709,
};
+/*
+ * These definitions are a continuation of the mdp_color_space enum above
+ */
+#define MDP_CSC_ITU_R_2020 (MDP_CSC_ITU_R_709 + 1)
+#define MDP_CSC_ITU_R_2020_FR (MDP_CSC_ITU_R_2020 + 1)
enum {
mdp_igc_v1_7 = 1,
mdp_igc_vmax,
diff --git a/include/uapi/linux/msm_mdp_ext.h b/include/uapi/linux/msm_mdp_ext.h
index 05a105b..1a2a7e2c 100644
--- a/include/uapi/linux/msm_mdp_ext.h
+++ b/include/uapi/linux/msm_mdp_ext.h
@@ -34,9 +34,9 @@
* To allow proper structure padding for 64bit/32bit target
*/
#ifdef __LP64
-#define MDP_LAYER_COMMIT_V1_PAD 3
+#define MDP_LAYER_COMMIT_V1_PAD 2
#else
-#define MDP_LAYER_COMMIT_V1_PAD 4
+#define MDP_LAYER_COMMIT_V1_PAD 3
#endif
/*
@@ -350,8 +350,11 @@
/* Buffer attached with output layer. Device uses it for commit call */
struct mdp_layer_buffer buffer;
+ /* color space of the destination */
+ enum mdp_color_space color_space;
+
/* 32bits reserved value for future usage. */
- uint32_t reserved[6];
+ uint32_t reserved[5];
};
/*
@@ -389,6 +392,18 @@
uint64_t __user scale;
};
+/* Enable Deterministic Frame Rate Control (FRC) */
+#define MDP_VIDEO_FRC_ENABLE (1 << 0)
+
+struct mdp_frc_info {
+ /* flags to control FRC feature */
+ uint32_t flags;
+ /* video frame count per frame */
+ uint32_t frame_cnt;
+ /* video timestamp per frame in millisecond unit */
+ int64_t timestamp;
+};
+
/*
* Commit structure holds layer stack send by client for validate and commit
* call. If layers are different between validate and commit call then commit
@@ -467,6 +482,9 @@
*/
uint32_t dest_scaler_cnt;
+ /* FRC info per device which contains frame count and timestamp */
+ struct mdp_frc_info __user *frc_info;
+
/* 32-bits reserved value for future usage. */
uint32_t reserved[MDP_LAYER_COMMIT_V1_PAD];
};
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 9fbdc11..3092188 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -3782,6 +3782,9 @@
* @NL80211_BSS_PARENT_BSSID. (u64).
* @NL80211_BSS_PARENT_BSSID: the BSS according to which @NL80211_BSS_PARENT_TSF
* is set.
+ * @NL80211_BSS_CHAIN_SIGNAL: per-chain signal strength of last BSS update.
+ * Contains a nested array of signal strength attributes (u8, dBm),
+ * using the nesting index as the antenna number.
* @__NL80211_BSS_AFTER_LAST: internal
* @NL80211_BSS_MAX: highest BSS attribute
*/
@@ -3805,6 +3808,7 @@
NL80211_BSS_PAD,
NL80211_BSS_PARENT_TSF,
NL80211_BSS_PARENT_BSSID,
+ NL80211_BSS_CHAIN_SIGNAL,
/* keep last */
__NL80211_BSS_AFTER_LAST,
@@ -4835,6 +4839,27 @@
* RSSI threshold values to monitor rather than exactly one threshold.
* @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD: Driver SME supports FILS shared key
* authentication with %NL80211_CMD_CONNECT.
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK: Device wants to do 4-way
+ * handshake with PSK in station mode (PSK is passed as part of the connect
+ * and associate commands), doing it in the host might not be supported.
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X: Device wants to do doing 4-way
+ * handshake with 802.1X in station mode (will pass EAP frames to the host
+ * and accept the set_pmk/del_pmk commands), doing it in the host might not
+ * be supported.
+ * @NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME: Driver is capable of overriding
+ * the max channel attribute in the FILS request params IE with the
+ * actual dwell time.
+ * @NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP: Driver accepts broadcast probe
+ * response
+ * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE: Driver supports sending
+ * the first probe request in each channel at rate of at least 5.5Mbps.
+ * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: Driver supports
+ * probe request tx deferral and suppression
+ * @NL80211_EXT_FEATURE_MFP_OPTIONAL: Driver supports the %NL80211_MFP_OPTIONAL
+ * value in %NL80211_ATTR_USE_MFP.
+ * @NL80211_EXT_FEATURE_LOW_SPAN_SCAN: Driver supports low span scan.
+ * @NL80211_EXT_FEATURE_LOW_POWER_SCAN: Driver supports low power scan.
+ * @NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN: Driver supports high accuracy scan.
*
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@@ -4855,6 +4880,16 @@
NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI,
NL80211_EXT_FEATURE_CQM_RSSI_LIST,
NL80211_EXT_FEATURE_FILS_SK_OFFLOAD,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X,
+ NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME,
+ NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION,
+ NL80211_EXT_FEATURE_MFP_OPTIONAL,
+ NL80211_EXT_FEATURE_LOW_SPAN_SCAN,
+ NL80211_EXT_FEATURE_LOW_POWER_SCAN,
+ NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN,
/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
@@ -4915,6 +4950,10 @@
* of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN
* requests.
*
+ * NL80211_SCAN_FLAG_LOW_SPAN, NL80211_SCAN_FLAG_LOW_POWER, and
+ * NL80211_SCAN_FLAG_HIGH_ACCURACY flags are exclusive of each other, i.e., only
+ * one of them can be used in the request.
+ *
* @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority
* @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning
* @NL80211_SCAN_FLAG_AP: force a scan even if the interface is configured
@@ -4931,12 +4970,29 @@
* locally administered 1, multicast 0) is assumed.
* This flag must not be requested when the feature isn't supported, check
* the nl80211 feature flags for the device.
+ * SSID and/or RSSI.
+ * @NL80211_SCAN_FLAG_LOW_SPAN: Span corresponds to the total time taken to
+ * accomplish the scan. Thus, this flag intends the driver to perform the
+ * scan request with lesser span/duration. It is specific to the driver
+ * implementations on how this is accomplished. Scan accuracy may get
+ * impacted with this flag.
+ * @NL80211_SCAN_FLAG_LOW_POWER: This flag intends the scan attempts to consume
+ * optimal possible power. Drivers can resort to their specific means to
+ * optimize the power. Scan accuracy may get impacted with this flag.
+ * @NL80211_SCAN_FLAG_HIGH_ACCURACY: Accuracy here intends to the extent of scan
+ * results obtained. Thus HIGH_ACCURACY scan flag aims to get maximum
+ * possible scan results. This flag hints the driver to use the best
+ * possible scan configuration to improve the accuracy in scanning.
+ * Latency and power use may get impacted with this flag.
*/
enum nl80211_scan_flags {
NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0,
NL80211_SCAN_FLAG_FLUSH = 1<<1,
NL80211_SCAN_FLAG_AP = 1<<2,
NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3,
+ NL80211_SCAN_FLAG_LOW_SPAN = 1<<8,
+ NL80211_SCAN_FLAG_LOW_POWER = 1<<9,
+ NL80211_SCAN_FLAG_HIGH_ACCURACY = 1<<10,
};
/**
diff --git a/include/uapi/scsi/sg.h b/include/uapi/scsi/sg.h
index 08d3beb..2ce6d73 100644
--- a/include/uapi/scsi/sg.h
+++ b/include/uapi/scsi/sg.h
@@ -198,7 +198,6 @@
#define SG_DEFAULT_RETRIES 0
/* Defaults, commented if they differ from original sg driver */
-#define SG_DEF_FORCE_LOW_DMA 0 /* was 1 -> memory below 16MB on i386 */
#define SG_DEF_FORCE_PACK_ID 0
#define SG_DEF_KEEP_ORPHAN 0
#define SG_DEF_RESERVED_SIZE SG_SCATTER_SZ /* load time option */
diff --git a/init/Kconfig b/init/Kconfig
index d4a2e32..2e57658 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1760,6 +1760,13 @@
Enable the bpf() system call that allows to manipulate eBPF
programs and maps via file descriptors.
+config BPF_JIT_ALWAYS_ON
+ bool "Permanently enable BPF JIT and remove BPF interpreter"
+ depends on BPF_SYSCALL && HAVE_EBPF_JIT && BPF_JIT
+ help
+ Enables BPF JIT and removes BPF interpreter to avoid
+ speculative execution of BPF instructions by the interpreter
+
config SHMEM
bool "Use full shmem filesystem" if EXPERT
default y
diff --git a/init/main.c b/init/main.c
index 674bc77..17e439f 100644
--- a/init/main.c
+++ b/init/main.c
@@ -80,6 +80,7 @@
#include <linux/integrity.h>
#include <linux/proc_ns.h>
#include <linux/io.h>
+#include <linux/kaiser.h>
#include <asm/io.h>
#include <asm/bugs.h>
@@ -473,6 +474,7 @@
pgtable_init();
vmalloc_init();
ioremap_huge_init();
+ kaiser_init();
}
asmlinkage __visible void __init start_kernel(void)
diff --git a/ipc/msg.c b/ipc/msg.c
index e12307d..ff10d43 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -763,7 +763,10 @@
if (*msgtyp == 0)
return SEARCH_ANY;
if (*msgtyp < 0) {
- *msgtyp = -*msgtyp;
+ if (*msgtyp == LONG_MIN) /* -LONG_MIN is undefined */
+ *msgtyp = LONG_MAX;
+ else
+ *msgtyp = -*msgtyp;
return SEARCH_LESSEQUAL;
}
if (msgflg & MSG_EXCEPT)
diff --git a/kernel/acct.c b/kernel/acct.c
index 74963d1..37f1dc6 100644
--- a/kernel/acct.c
+++ b/kernel/acct.c
@@ -99,7 +99,7 @@
{
struct kstatfs sbuf;
- if (time_is_before_jiffies(acct->needcheck))
+ if (time_is_after_jiffies(acct->needcheck))
goto out;
/* May block */
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index f3721e1..9a1e6ed 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -46,9 +46,10 @@
static struct bpf_map *array_map_alloc(union bpf_attr *attr)
{
bool percpu = attr->map_type == BPF_MAP_TYPE_PERCPU_ARRAY;
+ u32 elem_size, index_mask, max_entries;
+ bool unpriv = !capable(CAP_SYS_ADMIN);
struct bpf_array *array;
- u64 array_size;
- u32 elem_size;
+ u64 array_size, mask64;
/* check sanity of attributes */
if (attr->max_entries == 0 || attr->key_size != 4 ||
@@ -63,11 +64,32 @@
elem_size = round_up(attr->value_size, 8);
+ max_entries = attr->max_entries;
+
+ /* On 32 bit archs roundup_pow_of_two() with max_entries that has
+ * upper most bit set in u32 space is undefined behavior due to
+ * resulting 1U << 32, so do it manually here in u64 space.
+ */
+ mask64 = fls_long(max_entries - 1);
+ mask64 = 1ULL << mask64;
+ mask64 -= 1;
+
+ index_mask = mask64;
+ if (unpriv) {
+ /* round up array size to nearest power of 2,
+ * since cpu will speculate within index_mask limits
+ */
+ max_entries = index_mask + 1;
+ /* Check for overflows. */
+ if (max_entries < attr->max_entries)
+ return ERR_PTR(-E2BIG);
+ }
+
array_size = sizeof(*array);
if (percpu)
- array_size += (u64) attr->max_entries * sizeof(void *);
+ array_size += (u64) max_entries * sizeof(void *);
else
- array_size += (u64) attr->max_entries * elem_size;
+ array_size += (u64) max_entries * elem_size;
/* make sure there is no u32 overflow later in round_up() */
if (array_size >= U32_MAX - PAGE_SIZE)
@@ -77,6 +99,8 @@
array = bpf_map_area_alloc(array_size);
if (!array)
return ERR_PTR(-ENOMEM);
+ array->index_mask = index_mask;
+ array->map.unpriv_array = unpriv;
/* copy mandatory map attributes */
array->map.map_type = attr->map_type;
@@ -110,7 +134,7 @@
if (unlikely(index >= array->map.max_entries))
return NULL;
- return array->value + array->elem_size * index;
+ return array->value + array->elem_size * (index & array->index_mask);
}
/* Called from eBPF program */
@@ -122,7 +146,7 @@
if (unlikely(index >= array->map.max_entries))
return NULL;
- return this_cpu_ptr(array->pptrs[index]);
+ return this_cpu_ptr(array->pptrs[index & array->index_mask]);
}
int bpf_percpu_array_copy(struct bpf_map *map, void *key, void *value)
@@ -142,7 +166,7 @@
*/
size = round_up(map->value_size, 8);
rcu_read_lock();
- pptr = array->pptrs[index];
+ pptr = array->pptrs[index & array->index_mask];
for_each_possible_cpu(cpu) {
bpf_long_memcpy(value + off, per_cpu_ptr(pptr, cpu), size);
off += size;
@@ -190,10 +214,11 @@
return -EEXIST;
if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY)
- memcpy(this_cpu_ptr(array->pptrs[index]),
+ memcpy(this_cpu_ptr(array->pptrs[index & array->index_mask]),
value, map->value_size);
else
- memcpy(array->value + array->elem_size * index,
+ memcpy(array->value +
+ array->elem_size * (index & array->index_mask),
value, map->value_size);
return 0;
}
@@ -227,7 +252,7 @@
*/
size = round_up(map->value_size, 8);
rcu_read_lock();
- pptr = array->pptrs[index];
+ pptr = array->pptrs[index & array->index_mask];
for_each_possible_cpu(cpu) {
bpf_long_memcpy(per_cpu_ptr(pptr, cpu), value + off, size);
off += size;
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index aa6d981..879ca84 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -458,6 +458,7 @@
}
EXPORT_SYMBOL_GPL(__bpf_call_base);
+#ifndef CONFIG_BPF_JIT_ALWAYS_ON
/**
* __bpf_prog_run - run eBPF program on a given context
* @ctx: is the data we are operating on
@@ -641,7 +642,7 @@
DST = tmp;
CONT;
ALU_MOD_X:
- if (unlikely(SRC == 0))
+ if (unlikely((u32)SRC == 0))
return 0;
tmp = (u32) DST;
DST = do_div(tmp, (u32) SRC);
@@ -660,7 +661,7 @@
DST = div64_u64(DST, SRC);
CONT;
ALU_DIV_X:
- if (unlikely(SRC == 0))
+ if (unlikely((u32)SRC == 0))
return 0;
tmp = (u32) DST;
do_div(tmp, (u32) SRC);
@@ -715,7 +716,7 @@
struct bpf_map *map = (struct bpf_map *) (unsigned long) BPF_R2;
struct bpf_array *array = container_of(map, struct bpf_array, map);
struct bpf_prog *prog;
- u64 index = BPF_R3;
+ u32 index = BPF_R3;
if (unlikely(index >= array->map.max_entries))
goto out;
@@ -923,6 +924,13 @@
}
STACK_FRAME_NON_STANDARD(__bpf_prog_run); /* jump table */
+#else
+static unsigned int __bpf_prog_ret0(void *ctx, const struct bpf_insn *insn)
+{
+ return 0;
+}
+#endif
+
bool bpf_prog_array_compatible(struct bpf_array *array,
const struct bpf_prog *fp)
{
@@ -970,7 +978,11 @@
*/
struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err)
{
+#ifndef CONFIG_BPF_JIT_ALWAYS_ON
fp->bpf_func = (void *) __bpf_prog_run;
+#else
+ fp->bpf_func = (void *) __bpf_prog_ret0;
+#endif
/* eBPF JITs can rewrite the program in case constant
* blinding is active. However, in case of error during
@@ -979,6 +991,12 @@
* be JITed, but falls back to the interpreter.
*/
fp = bpf_int_jit_compile(fp);
+#ifdef CONFIG_BPF_JIT_ALWAYS_ON
+ if (!fp->jited) {
+ *err = -ENOTSUPP;
+ return fp;
+ }
+#endif
bpf_prog_lock_ro(fp);
/* The tail call compatibility check can only be done at
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index cd62aea..5b017fb 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -565,57 +565,6 @@
list_add(&tl->list_node, &bpf_prog_types);
}
-/* fixup insn->imm field of bpf_call instructions:
- * if (insn->imm == BPF_FUNC_map_lookup_elem)
- * insn->imm = bpf_map_lookup_elem - __bpf_call_base;
- * else if (insn->imm == BPF_FUNC_map_update_elem)
- * insn->imm = bpf_map_update_elem - __bpf_call_base;
- * else ...
- *
- * this function is called after eBPF program passed verification
- */
-static void fixup_bpf_calls(struct bpf_prog *prog)
-{
- const struct bpf_func_proto *fn;
- int i;
-
- for (i = 0; i < prog->len; i++) {
- struct bpf_insn *insn = &prog->insnsi[i];
-
- if (insn->code == (BPF_JMP | BPF_CALL)) {
- /* we reach here when program has bpf_call instructions
- * and it passed bpf_check(), means that
- * ops->get_func_proto must have been supplied, check it
- */
- BUG_ON(!prog->aux->ops->get_func_proto);
-
- if (insn->imm == BPF_FUNC_get_route_realm)
- prog->dst_needed = 1;
- if (insn->imm == BPF_FUNC_get_prandom_u32)
- bpf_user_rnd_init_once();
- if (insn->imm == BPF_FUNC_tail_call) {
- /* mark bpf_tail_call as different opcode
- * to avoid conditional branch in
- * interpeter for every normal call
- * and to prevent accidental JITing by
- * JIT compiler that doesn't support
- * bpf_tail_call yet
- */
- insn->imm = 0;
- insn->code |= BPF_X;
- continue;
- }
-
- fn = prog->aux->ops->get_func_proto(insn->imm);
- /* all functions that have prototype and verifier allowed
- * programs to call them, must be real in-kernel functions
- */
- BUG_ON(!fn->func);
- insn->imm = fn->func - __bpf_call_base;
- }
- }
-}
-
/* drop refcnt on maps used by eBPF program and free auxilary data */
static void free_used_maps(struct bpf_prog_aux *aux)
{
@@ -810,9 +759,6 @@
if (err < 0)
goto free_used_maps;
- /* fixup BPF_CALL->imm field */
- fixup_bpf_calls(prog);
-
/* eBPF program is ready to be JITed */
prog = bpf_prog_select_runtime(prog, &err);
if (err < 0)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index d7eeebf..076e4a0 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -702,6 +702,13 @@
return __is_pointer_value(env->allow_ptr_leaks, &env->cur_state.regs[regno]);
}
+static bool is_ctx_reg(struct bpf_verifier_env *env, int regno)
+{
+ const struct bpf_reg_state *reg = &env->cur_state.regs[regno];
+
+ return reg->type == PTR_TO_CTX;
+}
+
static int check_ptr_alignment(struct bpf_verifier_env *env,
struct bpf_reg_state *reg, int off, int size)
{
@@ -896,6 +903,12 @@
return -EACCES;
}
+ if (is_ctx_reg(env, insn->dst_reg)) {
+ verbose("BPF_XADD stores into R%d context is not allowed\n",
+ insn->dst_reg);
+ return -EACCES;
+ }
+
/* check whether atomic_add can read the memory */
err = check_mem_access(env, insn->dst_reg, insn->off,
BPF_SIZE(insn->code), BPF_READ, -1);
@@ -1187,7 +1200,7 @@
}
}
-static int check_call(struct bpf_verifier_env *env, int func_id)
+static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx)
{
struct bpf_verifier_state *state = &env->cur_state;
const struct bpf_func_proto *fn = NULL;
@@ -1238,6 +1251,13 @@
err = check_func_arg(env, BPF_REG_2, fn->arg2_type, &meta);
if (err)
return err;
+ if (func_id == BPF_FUNC_tail_call) {
+ if (meta.map_ptr == NULL) {
+ verbose("verifier bug\n");
+ return -EINVAL;
+ }
+ env->insn_aux_data[insn_idx].map_ptr = meta.map_ptr;
+ }
err = check_func_arg(env, BPF_REG_3, fn->arg3_type, &meta);
if (err)
return err;
@@ -1836,6 +1856,11 @@
return -EINVAL;
}
+ if (opcode == BPF_ARSH && BPF_CLASS(insn->code) != BPF_ALU64) {
+ verbose("BPF_ARSH not supported for 32 bit ALU\n");
+ return -EINVAL;
+ }
+
if ((opcode == BPF_LSH || opcode == BPF_RSH ||
opcode == BPF_ARSH) && BPF_SRC(insn->code) == BPF_K) {
int size = BPF_CLASS(insn->code) == BPF_ALU64 ? 64 : 32;
@@ -3000,6 +3025,12 @@
if (err)
return err;
+ if (is_ctx_reg(env, insn->dst_reg)) {
+ verbose("BPF_ST stores into R%d context is not allowed\n",
+ insn->dst_reg);
+ return -EACCES;
+ }
+
/* check that memory (dst_reg + off) is writeable */
err = check_mem_access(env, insn->dst_reg, insn->off,
BPF_SIZE(insn->code), BPF_WRITE,
@@ -3019,7 +3050,7 @@
return -EINVAL;
}
- err = check_call(env, insn->imm);
+ err = check_call(env, insn->imm, insn_idx);
if (err)
return err;
@@ -3362,6 +3393,99 @@
return 0;
}
+/* fixup insn->imm field of bpf_call instructions
+ *
+ * this function is called after eBPF program passed verification
+ */
+static int fixup_bpf_calls(struct bpf_verifier_env *env)
+{
+ struct bpf_prog *prog = env->prog;
+ struct bpf_insn *insn = prog->insnsi;
+ const struct bpf_func_proto *fn;
+ const int insn_cnt = prog->len;
+ struct bpf_insn insn_buf[16];
+ struct bpf_prog *new_prog;
+ struct bpf_map *map_ptr;
+ int i, cnt, delta = 0;
+
+
+ for (i = 0; i < insn_cnt; i++, insn++) {
+ if (insn->code == (BPF_ALU | BPF_MOD | BPF_X) ||
+ insn->code == (BPF_ALU | BPF_DIV | BPF_X)) {
+ /* due to JIT bugs clear upper 32-bits of src register
+ * before div/mod operation
+ */
+ insn_buf[0] = BPF_MOV32_REG(insn->src_reg, insn->src_reg);
+ insn_buf[1] = *insn;
+ cnt = 2;
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ if (!new_prog)
+ return -ENOMEM;
+
+ delta += cnt - 1;
+ env->prog = prog = new_prog;
+ insn = new_prog->insnsi + i + delta;
+ continue;
+ }
+
+ if (insn->code != (BPF_JMP | BPF_CALL))
+ continue;
+
+ if (insn->imm == BPF_FUNC_get_route_realm)
+ prog->dst_needed = 1;
+ if (insn->imm == BPF_FUNC_get_prandom_u32)
+ bpf_user_rnd_init_once();
+ if (insn->imm == BPF_FUNC_tail_call) {
+ /* mark bpf_tail_call as different opcode to avoid
+ * conditional branch in the interpeter for every normal
+ * call and to prevent accidental JITing by JIT compiler
+ * that doesn't support bpf_tail_call yet
+ */
+ insn->imm = 0;
+ insn->code |= BPF_X;
+
+ /* instead of changing every JIT dealing with tail_call
+ * emit two extra insns:
+ * if (index >= max_entries) goto out;
+ * index &= array->index_mask;
+ * to avoid out-of-bounds cpu speculation
+ */
+ map_ptr = env->insn_aux_data[i + delta].map_ptr;
+ if (!map_ptr->unpriv_array)
+ continue;
+ insn_buf[0] = BPF_JMP_IMM(BPF_JGE, BPF_REG_3,
+ map_ptr->max_entries, 2);
+ insn_buf[1] = BPF_ALU32_IMM(BPF_AND, BPF_REG_3,
+ container_of(map_ptr,
+ struct bpf_array,
+ map)->index_mask);
+ insn_buf[2] = *insn;
+ cnt = 3;
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ if (!new_prog)
+ return -ENOMEM;
+
+ delta += cnt - 1;
+ env->prog = prog = new_prog;
+ insn = new_prog->insnsi + i + delta;
+ continue;
+ }
+
+ fn = prog->aux->ops->get_func_proto(insn->imm);
+ /* all functions that have prototype and verifier allowed
+ * programs to call them, must be real in-kernel functions
+ */
+ if (!fn->func) {
+ verbose("kernel subsystem misconfigured func %d\n",
+ insn->imm);
+ return -EFAULT;
+ }
+ insn->imm = fn->func - __bpf_call_base;
+ }
+
+ return 0;
+}
+
static void free_states(struct bpf_verifier_env *env)
{
struct bpf_verifier_state_list *sl, *sln;
@@ -3463,6 +3587,9 @@
/* program is valid, convert *(u32*)(ctx + off) accesses */
ret = convert_ctx_accesses(env);
+ if (ret == 0)
+ ret = fixup_bpf_calls(env);
+
if (log_level && log_len >= log_size - 1) {
BUG_ON(log_len >= log_size);
/* verifier log exceeded user supplied buffer */
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 26c624e..a83771f 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -4407,7 +4407,11 @@
*/
do {
css_task_iter_start(&from->self, &it);
- task = css_task_iter_next(&it);
+
+ do {
+ task = css_task_iter_next(&it);
+ } while (task && (task->flags & PF_EXITING));
+
if (task)
get_task_struct(task);
css_task_iter_end(&it);
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 823fe5a..c68d150 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -1376,9 +1376,9 @@
* before blk_mq_queue_reinit_notify() from notify_dead(),
* otherwise a RCU stall occurs.
*/
- [CPUHP_TIMERS_DEAD] = {
+ [CPUHP_TIMERS_PREPARE] = {
.name = "timers:dead",
- .startup.single = NULL,
+ .startup.single = timers_prepare_cpu,
.teardown.single = timers_dead_cpu,
},
/* Kicks the plugged cpu into life */
diff --git a/kernel/fork.c b/kernel/fork.c
index b83adf9..79fdfd8 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -58,6 +58,7 @@
#include <linux/tsacct_kern.h>
#include <linux/cn_proc.h>
#include <linux/freezer.h>
+#include <linux/kaiser.h>
#include <linux/delayacct.h>
#include <linux/taskstats_kern.h>
#include <linux/random.h>
@@ -213,6 +214,7 @@
static inline void free_thread_stack(struct task_struct *tsk)
{
+ kaiser_unmap_thread_stack(tsk->stack);
#ifdef CONFIG_VMAP_STACK
if (task_stack_vm_area(tsk)) {
unsigned long flags;
@@ -495,6 +497,10 @@
* functions again.
*/
tsk->stack = stack;
+
+ err= kaiser_map_thread_stack(tsk->stack);
+ if (err)
+ goto free_stack;
#ifdef CONFIG_VMAP_STACK
tsk->stack_vm_area = stack_vm_area;
#endif
diff --git a/kernel/futex.c b/kernel/futex.c
index 88bad86..bb2265a 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -1711,6 +1711,9 @@
struct futex_q *this, *next;
WAKE_Q(wake_q);
+ if (nr_wake < 0 || nr_requeue < 0)
+ return -EINVAL;
+
if (requeue_pi) {
/*
* Requeue PI only works on two distinct uaddrs. This
diff --git a/kernel/groups.c b/kernel/groups.c
index 2fcadd6..94bde52 100644
--- a/kernel/groups.c
+++ b/kernel/groups.c
@@ -77,7 +77,7 @@
}
/* a simple Shell sort */
-static void groups_sort(struct group_info *group_info)
+void groups_sort(struct group_info *group_info)
{
int base, max, stride;
int gidsetsize = group_info->ngroups;
@@ -103,6 +103,7 @@
stride /= 3;
}
}
+EXPORT_SYMBOL(groups_sort);
/* a simple bsearch */
int groups_search(const struct group_info *group_info, kgid_t grp)
@@ -134,7 +135,6 @@
void set_groups(struct cred *new, struct group_info *group_info)
{
put_group_info(new->group_info);
- groups_sort(group_info);
get_group_info(group_info);
new->group_info = group_info;
}
@@ -218,6 +218,7 @@
return retval;
}
+ groups_sort(group_info);
retval = set_current_groups(group_info);
put_group_info(group_info);
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 59fcdbe..7b02ae6 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -1051,8 +1051,13 @@
*/
static struct rq *__migrate_task(struct rq *rq, struct task_struct *p, int dest_cpu)
{
- if (unlikely(!cpu_active(dest_cpu)))
- return rq;
+ if (p->flags & PF_KTHREAD) {
+ if (unlikely(!cpu_online(dest_cpu)))
+ return rq;
+ } else {
+ if (unlikely(!cpu_active(dest_cpu)))
+ return rq;
+ }
/* Affinity changed (again). */
if (!cpumask_test_cpu(dest_cpu, tsk_cpus_allowed(p)))
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index da8261d..0a295ac 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -724,6 +724,8 @@
if (unlikely(dl_se->dl_boosted || !start_dl_timer(p)))
return;
dl_se->dl_throttled = 1;
+ if (dl_se->runtime > 0)
+ dl_se->runtime = 0;
}
}
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 645b472..73f11c4 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -2178,9 +2178,8 @@
* the rt_loop_next will cause the iterator to perform another scan.
*
*/
-static int rto_next_cpu(struct rq *rq)
+static int rto_next_cpu(struct root_domain *rd)
{
- struct root_domain *rd = rq->rd;
int next;
int cpu;
@@ -2256,7 +2255,7 @@
* Otherwise it is finishing up and an ipi needs to be sent.
*/
if (rq->rd->rto_cpu < 0)
- cpu = rto_next_cpu(rq);
+ cpu = rto_next_cpu(rq->rd);
raw_spin_unlock(&rq->rd->rto_lock);
@@ -2269,6 +2268,8 @@
/* Called from hardirq context */
void rto_push_irq_work_func(struct irq_work *work)
{
+ struct root_domain *rd =
+ container_of(work, struct root_domain, rto_push_work);
struct rq *rq;
int cpu;
@@ -2284,18 +2285,18 @@
raw_spin_unlock(&rq->lock);
}
- raw_spin_lock(&rq->rd->rto_lock);
+ raw_spin_lock(&rd->rto_lock);
/* Pass the IPI to the next rt overloaded queue */
- cpu = rto_next_cpu(rq);
+ cpu = rto_next_cpu(rd);
- raw_spin_unlock(&rq->rd->rto_lock);
+ raw_spin_unlock(&rd->rto_lock);
if (cpu < 0)
return;
/* Try the next RT overloaded CPU */
- irq_work_queue_on(&rq->rd->rto_push_work, cpu);
+ irq_work_queue_on(&rd->rto_push_work, cpu);
}
#endif /* HAVE_RT_PUSH_IPI */
diff --git a/kernel/signal.c b/kernel/signal.c
index e48668c..7ebe236 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -72,7 +72,7 @@
handler = sig_handler(t, sig);
if (unlikely(t->signal->flags & SIGNAL_UNKILLABLE) &&
- handler == SIG_DFL && !force)
+ handler == SIG_DFL && !(force && sig_kernel_only(sig)))
return 1;
return sig_handler_ignored(handler, sig);
@@ -88,13 +88,15 @@
if (sigismember(&t->blocked, sig) || sigismember(&t->real_blocked, sig))
return 0;
- if (!sig_task_ignored(t, sig, force))
+ /*
+ * Tracers may want to know about even ignored signal unless it
+ * is SIGKILL which can't be reported anyway but can be ignored
+ * by SIGNAL_UNKILLABLE task.
+ */
+ if (t->ptrace && sig != SIGKILL)
return 0;
- /*
- * Tracers may want to know about even ignored signals.
- */
- return !t->ptrace;
+ return sig_task_ignored(t, sig, force);
}
/*
@@ -917,9 +919,9 @@
* then start taking the whole group down immediately.
*/
if (sig_fatal(p, sig) &&
- !(signal->flags & (SIGNAL_UNKILLABLE | SIGNAL_GROUP_EXIT)) &&
+ !(signal->flags & SIGNAL_GROUP_EXIT) &&
!sigismember(&t->real_blocked, sig) &&
- (sig == SIGKILL || !t->ptrace)) {
+ (sig == SIGKILL || !p->ptrace)) {
/*
* This signal will be fatal to the whole group.
*/
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index dea7e55..a01c821 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -127,7 +127,9 @@
static int __maybe_unused four = 4;
static unsigned long one_ul = 1;
static int one_hundred = 100;
+#ifdef CONFIG_PERF_EVENTS
static int one_thousand = 1000;
+#endif
#ifdef CONFIG_PRINTK
static int ten_thousand = 10000;
#endif
@@ -1572,8 +1574,16 @@
.maxlen = sizeof(watermark_scale_factor),
.mode = 0644,
.proc_handler = watermark_scale_factor_sysctl_handler,
- .extra1 = &one,
- .extra2 = &one_thousand,
+ .extra1 = &zero,
+ .extra2 = &zero,
+ },
+ {
+ .procname = "extra_free_kbytes",
+ .data = &extra_free_kbytes,
+ .maxlen = sizeof(extra_free_kbytes),
+ .mode = 0644,
+ .proc_handler = min_free_kbytes_sysctl_handler,
+ .extra1 = &zero,
},
{
.procname = "percpu_pagelist_fraction",
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index a01a71f..38b008e 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -652,7 +652,9 @@
static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base)
{
base->expires_next.tv64 = KTIME_MAX;
+ base->hang_detected = 0;
base->hres_active = 0;
+ base->next_timer = NULL;
}
/*
@@ -1582,6 +1584,7 @@
timerqueue_init_head(&cpu_base->clock_base[i].active);
}
+ cpu_base->active_bases = 0;
cpu_base->cpu = cpu;
hrtimer_init_hres(cpu_base);
return 0;
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index df3191e..4ce4285 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -684,6 +684,11 @@
tick_program_event(hrtimer_get_expires(&ts->sched_timer), 1);
}
+static inline bool local_timer_softirq_pending(void)
+{
+ return local_softirq_pending() & TIMER_SOFTIRQ;
+}
+
static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
ktime_t now, int cpu)
{
@@ -700,8 +705,18 @@
} while (read_seqretry(&jiffies_lock, seq));
ts->last_jiffies = basejiff;
- if (rcu_needs_cpu(basemono, &next_rcu) ||
- arch_needs_cpu() || irq_work_needs_cpu()) {
+ /*
+ * Keep the periodic tick, when RCU, architecture or irq_work
+ * requests it.
+ * Aside of that check whether the local timer softirq is
+ * pending. If so its a bad idea to call get_next_timer_interrupt()
+ * because there is an already expired timer, so it will request
+ * immeditate expiry, which rearms the hardware timer with a
+ * minimal delta which brings us back to this place
+ * immediately. Lather, rinse and repeat...
+ */
+ if (rcu_needs_cpu(basemono, &next_rcu) || arch_needs_cpu() ||
+ irq_work_needs_cpu() || local_timer_softirq_pending()) {
next_tick = basemono + TICK_NSEC;
} else {
/*
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 4c0b001..a6f8a44 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -208,7 +208,6 @@
static DEFINE_PER_CPU(struct timer_base, timer_bases[NR_BASES]);
struct timer_base timer_base_deferrable;
-static atomic_t deferrable_pending;
#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
unsigned int sysctl_timer_migration = 1;
@@ -826,11 +825,10 @@
struct timer_base *base = per_cpu_ptr(&timer_bases[BASE_STD], cpu);
/*
- * If the timer is deferrable and nohz is active then we need to use
- * the deferrable base.
+ * If the timer is deferrable and NO_HZ_COMMON is set then we need
+ * to use the deferrable base.
*/
- if (IS_ENABLED(CONFIG_NO_HZ_COMMON) && base->nohz_active &&
- (tflags & TIMER_DEFERRABLE)) {
+ if (IS_ENABLED(CONFIG_NO_HZ_COMMON) && (tflags & TIMER_DEFERRABLE)) {
base = &timer_base_deferrable;
if (tflags & TIMER_PINNED)
base = per_cpu_ptr(&timer_bases[BASE_DEF], cpu);
@@ -843,11 +841,10 @@
struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]);
/*
- * If the timer is deferrable and nohz is active then we need to use
- * the deferrable base.
+ * If the timer is deferrable and NO_HZ_COMMON is set then we need
+ * to use the deferrable base.
*/
- if (IS_ENABLED(CONFIG_NO_HZ_COMMON) && base->nohz_active &&
- (tflags & TIMER_DEFERRABLE)) {
+ if (IS_ENABLED(CONFIG_NO_HZ_COMMON) && (tflags & TIMER_DEFERRABLE)) {
base = &timer_base_deferrable;
if (tflags & TIMER_PINNED)
base = this_cpu_ptr(&timer_bases[BASE_DEF]);
@@ -1002,8 +999,6 @@
if (!ret && pending_only)
goto out_unlock;
- debug_activate(timer, expires);
-
new_base = get_target_base(base, timer->flags);
if (base != new_base) {
@@ -1027,6 +1022,8 @@
}
}
+ debug_activate(timer, expires);
+
timer->expires = expires;
/*
* If 'idx' was calculated above and the base time did not advance
@@ -1491,6 +1488,8 @@
#ifdef CONFIG_SMP
+static atomic_t deferrable_pending;
+
/*
* check_pending_deferrable_timers - Check for unbound deferrable timer expiry
* @cpu - Current CPU
@@ -1671,6 +1670,27 @@
spin_unlock_irq(&base->lock);
}
+#ifdef CONFIG_SMP
+static inline bool should_this_cpu_run_deferrable_timers(void)
+{
+ int tick_cpu = READ_ONCE(tick_do_timer_cpu);
+
+ if (atomic_cmpxchg(&deferrable_pending, 1, 0) &&
+ tick_cpu == TICK_DO_TIMER_NONE)
+ return true;
+
+ if (tick_cpu == smp_processor_id())
+ return true;
+
+ return (tick_cpu >= 0 && ksoftirqd_running_on(tick_cpu));
+}
+#else
+static inline bool should_this_cpu_run_deferrable_timers(void)
+{
+ return true;
+}
+#endif
+
/*
* This function runs timers and the timer-tq in bottom half context.
*/
@@ -1692,12 +1712,10 @@
base->must_forward_clk = false;
__run_timers(base);
- if (IS_ENABLED(CONFIG_NO_HZ_COMMON) && base->nohz_active)
+ if (IS_ENABLED(CONFIG_NO_HZ_COMMON))
__run_timers(this_cpu_ptr(&timer_bases[BASE_DEF]));
- if ((atomic_cmpxchg(&deferrable_pending, 1, 0) &&
- tick_do_timer_cpu == TICK_DO_TIMER_NONE) ||
- tick_do_timer_cpu == smp_processor_id())
+ if (should_this_cpu_run_deferrable_timers())
__run_timers(&timer_base_deferrable);
}
@@ -1711,7 +1729,7 @@
hrtimer_run_queues();
/* Raise the softirq only if required. */
if (time_before(jiffies, base->clk)) {
- if (!IS_ENABLED(CONFIG_NO_HZ_COMMON) || !base->nohz_active)
+ if (!IS_ENABLED(CONFIG_NO_HZ_COMMON))
return;
/* CPU is awake, so check the deferrable base. */
base++;
@@ -1872,6 +1890,21 @@
}
}
+int timers_prepare_cpu(unsigned int cpu)
+{
+ struct timer_base *base;
+ int b;
+
+ for (b = 0; b < NR_BASES; b++) {
+ base = per_cpu_ptr(&timer_bases[b], cpu);
+ base->clk = jiffies;
+ base->next_expiry = base->clk + NEXT_TIMER_MAX_DELTA;
+ base->is_idle = false;
+ base->must_forward_clk = true;
+ }
+ return 0;
+}
+
static void __migrate_timers(unsigned int cpu, bool remove_pinned)
{
struct timer_base *old_base;
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index f5c016e..3e1d11f 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -280,6 +280,8 @@
/* Missed count stored at end */
#define RB_MISSED_STORED (1 << 30)
+#define RB_MISSED_FLAGS (RB_MISSED_EVENTS|RB_MISSED_STORED)
+
struct buffer_data_page {
u64 time_stamp; /* page time stamp */
local_t commit; /* write committed index */
@@ -331,7 +333,9 @@
*/
size_t ring_buffer_page_len(void *page)
{
- return local_read(&((struct buffer_data_page *)page)->commit)
+ struct buffer_data_page *bpage = page;
+
+ return (local_read(&bpage->commit) & ~RB_MISSED_FLAGS)
+ BUF_PAGE_HDR_SIZE;
}
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 9e862ae..046abb0 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -6337,7 +6337,7 @@
.spd_release = buffer_spd_release,
};
struct buffer_ref *ref;
- int entries, size, i;
+ int entries, i;
ssize_t ret = 0;
#ifdef CONFIG_TRACER_MAX_TRACE
@@ -6388,14 +6388,6 @@
break;
}
- /*
- * zero out any left over data, this is going to
- * user land.
- */
- size = ring_buffer_page_len(ref->page);
- if (size < PAGE_SIZE)
- memset(ref->page + size, 0, PAGE_SIZE - size);
-
page = virt_to_page(ref->page);
spd.pages[i] = page;
@@ -7119,6 +7111,7 @@
buf->data = alloc_percpu(struct trace_array_cpu);
if (!buf->data) {
ring_buffer_free(buf->buffer);
+ buf->buffer = NULL;
return -ENOMEM;
}
@@ -7142,7 +7135,9 @@
allocate_snapshot ? size : 1);
if (WARN_ON(ret)) {
ring_buffer_free(tr->trace_buffer.buffer);
+ tr->trace_buffer.buffer = NULL;
free_percpu(tr->trace_buffer.data);
+ tr->trace_buffer.data = NULL;
return -ENOMEM;
}
tr->allocated_snapshot = allocate_snapshot;
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 72687f4..b15316a 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -2201,6 +2201,7 @@
{
struct trace_event_call *call, *p;
const char *last_system = NULL;
+ bool first = false;
int last_i;
int i;
@@ -2208,15 +2209,28 @@
list_for_each_entry_safe(call, p, &ftrace_events, list) {
/* events are usually grouped together with systems */
if (!last_system || call->class->system != last_system) {
+ first = true;
last_i = 0;
last_system = call->class->system;
}
+ /*
+ * Since calls are grouped by systems, the likelyhood that the
+ * next call in the iteration belongs to the same system as the
+ * previous call is high. As an optimization, we skip seaching
+ * for a map[] that matches the call's system if the last call
+ * was from the same system. That's what last_i is for. If the
+ * call has the same system as the previous call, then last_i
+ * will be the index of the first map[] that has a matching
+ * system.
+ */
for (i = last_i; i < len; i++) {
if (call->class->system == map[i]->system) {
/* Save the first system if need be */
- if (!last_i)
+ if (first) {
last_i = i;
+ first = false;
+ }
update_event_printk(call, map[i]);
}
}
diff --git a/kernel/uid16.c b/kernel/uid16.c
index cc40793..dcffcce 100644
--- a/kernel/uid16.c
+++ b/kernel/uid16.c
@@ -190,6 +190,7 @@
return retval;
}
+ groups_sort(group_info);
retval = set_current_groups(group_info);
put_group_info(group_info);
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 6b9af20..5ef61bd 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -50,6 +50,7 @@
#include <linux/uaccess.h>
#include <linux/bug.h>
#include <linux/delay.h>
+#include <linux/nmi.h>
#include "workqueue_internal.h"
@@ -1641,7 +1642,7 @@
mod_timer(&pool->idle_timer, jiffies + IDLE_WORKER_TIMEOUT);
/*
- * Sanity check nr_running. Because wq_unbind_fn() releases
+ * Sanity check nr_running. Because unbind_workers() releases
* pool->lock between setting %WORKER_UNBOUND and zapping
* nr_running, the warning may trigger spuriously. Check iff
* unbind is not in progress.
@@ -4431,6 +4432,12 @@
if (pwq->nr_active || !list_empty(&pwq->delayed_works))
show_pwq(pwq);
spin_unlock_irqrestore(&pwq->pool->lock, flags);
+ /*
+ * We could be printing a lot from atomic context, e.g.
+ * sysrq-t -> show_workqueue_state(). Avoid triggering
+ * hard lockup.
+ */
+ touch_nmi_watchdog();
}
}
@@ -4458,6 +4465,12 @@
pr_cont("\n");
next_pool:
spin_unlock_irqrestore(&pool->lock, flags);
+ /*
+ * We could be printing a lot from atomic context, e.g.
+ * sysrq-t -> show_workqueue_state(). Avoid triggering
+ * hard lockup.
+ */
+ touch_nmi_watchdog();
}
rcu_read_unlock_sched();
@@ -4478,9 +4491,8 @@
* cpu comes back online.
*/
-static void wq_unbind_fn(struct work_struct *work)
+static void unbind_workers(int cpu)
{
- int cpu = smp_processor_id();
struct worker_pool *pool;
struct worker *worker;
@@ -4677,12 +4689,13 @@
int workqueue_offline_cpu(unsigned int cpu)
{
- struct work_struct unbind_work;
struct workqueue_struct *wq;
/* unbinding per-cpu workers should happen on the local CPU */
- INIT_WORK_ONSTACK(&unbind_work, wq_unbind_fn);
- queue_work_on(cpu, system_highpri_wq, &unbind_work);
+ if (WARN_ON(cpu != smp_processor_id()))
+ return -1;
+
+ unbind_workers(cpu);
/* update NUMA affinity of unbound workqueues */
mutex_lock(&wq_pool_mutex);
@@ -4690,9 +4703,6 @@
wq_update_unbound_numa(wq, cpu, false);
mutex_unlock(&wq_pool_mutex);
- /* wait for per-cpu unbinding to finish */
- flush_work(&unbind_work);
- destroy_work_on_stack(&unbind_work);
return 0;
}
diff --git a/lib/test_bpf.c b/lib/test_bpf.c
index 2e38502..98da752 100644
--- a/lib/test_bpf.c
+++ b/lib/test_bpf.c
@@ -5646,9 +5646,8 @@
return NULL;
}
}
- /* We don't expect to fail. */
if (*err) {
- pr_cont("FAIL to attach err=%d len=%d\n",
+ pr_cont("FAIL to prog_create err=%d len=%d\n",
*err, fprog.len);
return NULL;
}
@@ -5671,6 +5670,10 @@
* checks.
*/
fp = bpf_prog_select_runtime(fp, err);
+ if (*err) {
+ pr_cont("FAIL to select_runtime err=%d\n", *err);
+ return NULL;
+ }
break;
}
@@ -5856,8 +5859,8 @@
pass_cnt++;
continue;
}
-
- return err;
+ err_cnt++;
+ continue;
}
pr_cont("jited:%u ", fp->jited);
diff --git a/mm/cma.c b/mm/cma.c
index 2984dac..5fc1809 100644
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -61,7 +61,7 @@
}
static unsigned long cma_bitmap_aligned_mask(const struct cma *cma,
- int align_order)
+ unsigned int align_order)
{
if (align_order <= cma->order_per_bit)
return 0;
@@ -69,17 +69,14 @@
}
/*
- * Find a PFN aligned to the specified order and return an offset represented in
- * order_per_bits.
+ * Find the offset of the base PFN from the specified align_order.
+ * The value returned is represented in order_per_bits.
*/
static unsigned long cma_bitmap_aligned_offset(const struct cma *cma,
- int align_order)
+ unsigned int align_order)
{
- if (align_order <= cma->order_per_bit)
- return 0;
-
- return (ALIGN(cma->base_pfn, (1UL << align_order))
- - cma->base_pfn) >> cma->order_per_bit;
+ return (cma->base_pfn & ((1UL << align_order) - 1))
+ >> cma->order_per_bit;
}
static unsigned long cma_bitmap_pages_to_bits(const struct cma *cma,
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index 9a20a55..9b59170 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -1454,6 +1454,8 @@
if (page_count(page) == 0)
continue;
scan_block(page, page + 1, NULL);
+ if (!(pfn % (MAX_SCAN_SIZE / sizeof(*page))))
+ cond_resched();
}
}
put_online_mems();
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index fce6c48..34eec18 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -1619,9 +1619,13 @@
* @page: the page
*
* This function protects unlocked LRU pages from being moved to
- * another cgroup and stabilizes their page->mem_cgroup binding.
+ * another cgroup.
+ *
+ * It ensures lifetime of the returned memcg. Caller is responsible
+ * for the lifetime of the page; __unlock_page_memcg() is available
+ * when @page might get freed inside the locked section.
*/
-void lock_page_memcg(struct page *page)
+struct mem_cgroup *lock_page_memcg(struct page *page)
{
struct mem_cgroup *memcg;
unsigned long flags;
@@ -1630,18 +1634,24 @@
* The RCU lock is held throughout the transaction. The fast
* path can get away without acquiring the memcg->move_lock
* because page moving starts with an RCU grace period.
- */
+ *
+ * The RCU lock also protects the memcg from being freed when
+ * the page state that is going to change is the only thing
+ * preventing the page itself from being freed. E.g. writeback
+ * doesn't hold a page reference and relies on PG_writeback to
+ * keep off truncation, migration and so forth.
+ */
rcu_read_lock();
if (mem_cgroup_disabled())
- return;
+ return NULL;
again:
memcg = page->mem_cgroup;
if (unlikely(!memcg))
- return;
+ return NULL;
if (atomic_read(&memcg->moving_account) <= 0)
- return;
+ return memcg;
spin_lock_irqsave(&memcg->move_lock, flags);
if (memcg != page->mem_cgroup) {
@@ -1657,18 +1667,18 @@
memcg->move_lock_task = current;
memcg->move_lock_flags = flags;
- return;
+ return memcg;
}
EXPORT_SYMBOL(lock_page_memcg);
/**
- * unlock_page_memcg - unlock a page->mem_cgroup binding
- * @page: the page
+ * __unlock_page_memcg - unlock and unpin a memcg
+ * @memcg: the memcg
+ *
+ * Unlock and unpin a memcg returned by lock_page_memcg().
*/
-void unlock_page_memcg(struct page *page)
+void __unlock_page_memcg(struct mem_cgroup *memcg)
{
- struct mem_cgroup *memcg = page->mem_cgroup;
-
if (memcg && memcg->move_lock_task == current) {
unsigned long flags = memcg->move_lock_flags;
@@ -1680,6 +1690,15 @@
rcu_read_unlock();
}
+
+/**
+ * unlock_page_memcg - unlock a page->mem_cgroup binding
+ * @page: the page
+ */
+void unlock_page_memcg(struct page *page)
+{
+ __unlock_page_memcg(page->mem_cgroup);
+}
EXPORT_SYMBOL(unlock_page_memcg);
/*
@@ -5541,7 +5560,7 @@
next = page->lru.next;
VM_BUG_ON_PAGE(PageLRU(page), page);
- VM_BUG_ON_PAGE(page_count(page), page);
+ VM_BUG_ON_PAGE(!PageHWPoison(page) && page_count(page), page);
if (!page->mem_cgroup)
continue;
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index b335423..43622c6 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -535,6 +535,13 @@
*/
ClearPageActive(p);
ClearPageUnevictable(p);
+
+ /*
+ * Poisoned page might never drop its ref count to 0 so we have
+ * to uncharge it manually from its memcg.
+ */
+ mem_cgroup_uncharge(p);
+
/*
* drop the page count elevated by isolate_lru_page()
*/
diff --git a/mm/mmap.c b/mm/mmap.c
index 621db7f..5df92da 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2250,7 +2250,8 @@
gap_addr = TASK_SIZE;
next = vma->vm_next;
- if (next && next->vm_start < gap_addr) {
+ if (next && next->vm_start < gap_addr &&
+ (next->vm_flags & (VM_WRITE|VM_READ|VM_EXEC))) {
if (!(next->vm_flags & VM_GROWSUP))
return -ENOMEM;
/* Check that both stack segments have the same anon_vma? */
@@ -2334,7 +2335,8 @@
if (gap_addr > address)
return -ENOMEM;
prev = vma->vm_prev;
- if (prev && prev->vm_end > gap_addr) {
+ if (prev && prev->vm_end > gap_addr &&
+ (prev->vm_flags & (VM_WRITE|VM_READ|VM_EXEC))) {
if (!(prev->vm_flags & VM_GROWSDOWN))
return -ENOMEM;
/* Check that both stack segments have the same anon_vma? */
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index 439cc63..dd7817cd3 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -2713,9 +2713,10 @@
int test_clear_page_writeback(struct page *page)
{
struct address_space *mapping = page_mapping(page);
+ struct mem_cgroup *memcg;
int ret;
- lock_page_memcg(page);
+ memcg = lock_page_memcg(page);
if (mapping && mapping_use_writeback_tags(mapping)) {
struct inode *inode = mapping->host;
struct backing_dev_info *bdi = inode_to_bdi(inode);
@@ -2743,13 +2744,20 @@
} else {
ret = TestClearPageWriteback(page);
}
+ /*
+ * NOTE: Page might be free now! Writeback doesn't hold a page
+ * reference on its own, it relies on truncation to wait for
+ * the clearing of PG_writeback. The below can only access
+ * page state that is static across allocation cycles.
+ */
if (ret) {
- mem_cgroup_dec_page_stat(page, MEM_CGROUP_STAT_WRITEBACK);
+ __mem_cgroup_update_page_stat(page, memcg,
+ MEM_CGROUP_STAT_WRITEBACK, -1);
dec_node_page_state(page, NR_WRITEBACK);
dec_zone_page_state(page, NR_ZONE_WRITE_PENDING);
inc_node_page_state(page, NR_WRITTEN);
}
- unlock_page_memcg(page);
+ __unlock_page_memcg(memcg);
return ret;
}
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 63b19a3..4a60459 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -254,9 +254,21 @@
#endif
};
+/*
+ * Try to keep at least this much lowmem free. Do not allow normal
+ * allocations below this point, only high priority ones. Automatically
+ * tuned according to the amount of memory in the system.
+ */
int min_free_kbytes = 1024;
int user_min_free_kbytes = -1;
-int watermark_scale_factor = 10;
+int watermark_scale_factor;
+
+/*
+ * Extra memory for the system to try freeing. Used to temporarily
+ * free memory, to make space for new workloads. Anyone can allocate
+ * down to the min watermarks controlled by min_free_kbytes above.
+ */
+int extra_free_kbytes;
static unsigned long __meminitdata nr_kernel_pages;
static unsigned long __meminitdata nr_all_pages;
@@ -2887,9 +2899,6 @@
if (!area->nr_free)
continue;
- if (alloc_harder)
- return true;
-
for (mt = 0; mt < MIGRATE_PCPTYPES; mt++) {
#ifdef CONFIG_CMA
/*
@@ -2909,6 +2918,9 @@
return true;
}
#endif
+ if (alloc_harder &&
+ !list_empty(&area->free_list[MIGRATE_HIGHATOMIC]))
+ return true;
}
return false;
}
@@ -6742,6 +6754,7 @@
static void __setup_per_zone_wmarks(void)
{
unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - 10);
+ unsigned long pages_low = extra_free_kbytes >> (PAGE_SHIFT - 10);
unsigned long lowmem_pages = 0;
struct zone *zone;
unsigned long flags;
@@ -6753,11 +6766,14 @@
}
for_each_zone(zone) {
- u64 tmp;
+ u64 min, low;
spin_lock_irqsave(&zone->lock, flags);
- tmp = (u64)pages_min * zone->managed_pages;
- do_div(tmp, lowmem_pages);
+ min = (u64)pages_min * zone->managed_pages;
+ do_div(min, lowmem_pages);
+ low = (u64)pages_low * zone->managed_pages;
+ do_div(low, vm_total_pages);
+
if (is_highmem(zone)) {
/*
* __GFP_HIGH and PF_MEMALLOC allocations usually don't
@@ -6778,7 +6794,7 @@
* If it's a lowmem zone, reserve a number of pages
* proportionate to the zone's size.
*/
- zone->watermark[WMARK_MIN] = tmp;
+ zone->watermark[WMARK_MIN] = min;
}
/*
@@ -6786,12 +6802,13 @@
* scale factor in proportion to available memory, but
* ensure a minimum size on small systems.
*/
- tmp = max_t(u64, tmp >> 2,
+ min = max_t(u64, min >> 2,
mult_frac(zone->managed_pages,
watermark_scale_factor, 10000));
- zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + tmp;
- zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + tmp * 2;
+ zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + low + min;
+ zone->watermark[WMARK_HIGH] =
+ min_wmark_pages(zone) + low + min * 2;
spin_unlock_irqrestore(&zone->lock, flags);
}
@@ -6872,7 +6889,7 @@
/*
* min_free_kbytes_sysctl_handler - just a wrapper around proc_dointvec() so
* that we can call two helper functions whenever min_free_kbytes
- * changes.
+ * or extra_free_kbytes changes.
*/
int min_free_kbytes_sysctl_handler(struct ctl_table *table, int write,
void __user *buffer, size_t *length, loff_t *ppos)
diff --git a/mm/vmstat.c b/mm/vmstat.c
index 2ab7973..26f0e49 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -932,6 +932,7 @@
"nr_slab_unreclaimable",
"nr_page_table_pages",
"nr_kernel_stack",
+ "nr_overhead",
"nr_bounce",
#if IS_ENABLED(CONFIG_ZSMALLOC)
"nr_zspages",
diff --git a/mm/zswap.c b/mm/zswap.c
index dbef278..ded051e 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -752,18 +752,22 @@
pool = zswap_pool_find_get(type, compressor);
if (pool) {
zswap_pool_debug("using existing", pool);
+ WARN_ON(pool == zswap_pool_current());
list_del_rcu(&pool->list);
- } else {
- spin_unlock(&zswap_pools_lock);
- pool = zswap_pool_create(type, compressor);
- spin_lock(&zswap_pools_lock);
}
+ spin_unlock(&zswap_pools_lock);
+
+ if (!pool)
+ pool = zswap_pool_create(type, compressor);
+
if (pool)
ret = param_set_charp(s, kp);
else
ret = -EINVAL;
+ spin_lock(&zswap_pools_lock);
+
if (!ret) {
put_pool = zswap_pool_current();
list_add_rcu(&pool->list, &zswap_pools);
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index 4a47074..c8ea3cf 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -111,12 +111,7 @@
vlan_gvrp_uninit_applicant(real_dev);
}
- /* Take it out of our own structures, but be sure to interlock with
- * HW accelerating devices or SW vlan input packet processing if
- * VLAN is not 0 (leave it there for 802.1p).
- */
- if (vlan_id)
- vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id);
+ vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id);
/* Get rid of the vlan's reference to real_dev */
dev_put(real_dev);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ffd09c1..2bbca23 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -3353,9 +3353,10 @@
break;
case L2CAP_CONF_EFS:
- remote_efs = 1;
- if (olen == sizeof(efs))
+ if (olen == sizeof(efs)) {
+ remote_efs = 1;
memcpy(&efs, (void *) val, olen);
+ }
break;
case L2CAP_CONF_EWS:
@@ -3574,16 +3575,17 @@
break;
case L2CAP_CONF_EFS:
- if (olen == sizeof(efs))
+ if (olen == sizeof(efs)) {
memcpy(&efs, (void *)val, olen);
- if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
- efs.stype != L2CAP_SERV_NOTRAFIC &&
- efs.stype != chan->local_stype)
- return -ECONNREFUSED;
+ if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
+ efs.stype != L2CAP_SERV_NOTRAFIC &&
+ efs.stype != chan->local_stype)
+ return -ECONNREFUSED;
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs),
- (unsigned long) &efs, endptr - ptr);
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs),
+ (unsigned long) &efs, endptr - ptr);
+ }
break;
case L2CAP_CONF_FCS:
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 5d4006e..4f83122 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -1092,19 +1092,20 @@
struct net_bridge *br = netdev_priv(dev);
int err;
+ err = register_netdevice(dev);
+ if (err)
+ return err;
+
if (tb[IFLA_ADDRESS]) {
spin_lock_bh(&br->lock);
br_stp_change_bridge_id(br, nla_data(tb[IFLA_ADDRESS]));
spin_unlock_bh(&br->lock);
}
- err = register_netdevice(dev);
- if (err)
- return err;
-
err = br_changelink(dev, tb, data);
if (err)
- unregister_netdevice(dev);
+ br_dev_delete(dev, NULL);
+
return err;
}
diff --git a/net/can/af_can.c b/net/can/af_can.c
index 5488e4a..ac1552d 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -722,13 +722,12 @@
if (unlikely(!net_eq(dev_net(dev), &init_net)))
goto drop;
- if (WARN_ONCE(dev->type != ARPHRD_CAN ||
- skb->len != CAN_MTU ||
- cfd->len > CAN_MAX_DLEN,
- "PF_CAN: dropped non conform CAN skbuf: "
- "dev type %d, len %d, datalen %d\n",
- dev->type, skb->len, cfd->len))
+ if (unlikely(dev->type != ARPHRD_CAN || skb->len != CAN_MTU ||
+ cfd->len > CAN_MAX_DLEN)) {
+ pr_warn_once("PF_CAN: dropped non conform CAN skbuf: dev type %d, len %d, datalen %d\n",
+ dev->type, skb->len, cfd->len);
goto drop;
+ }
can_receive(skb, dev);
return NET_RX_SUCCESS;
@@ -746,13 +745,12 @@
if (unlikely(!net_eq(dev_net(dev), &init_net)))
goto drop;
- if (WARN_ONCE(dev->type != ARPHRD_CAN ||
- skb->len != CANFD_MTU ||
- cfd->len > CANFD_MAX_DLEN,
- "PF_CAN: dropped non conform CAN FD skbuf: "
- "dev type %d, len %d, datalen %d\n",
- dev->type, skb->len, cfd->len))
+ if (unlikely(dev->type != ARPHRD_CAN || skb->len != CANFD_MTU ||
+ cfd->len > CANFD_MAX_DLEN)) {
+ pr_warn_once("PF_CAN: dropped non conform CAN FD skbuf: dev type %d, len %d, datalen %d\n",
+ dev->type, skb->len, cfd->len);
goto drop;
+ }
can_receive(skb, dev);
return NET_RX_SUCCESS;
diff --git a/net/core/dev.c b/net/core/dev.c
index e0217f8..ca40d8e 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3139,10 +3139,21 @@
hdr_len = skb_transport_header(skb) - skb_mac_header(skb);
/* + transport layer */
- if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))
- hdr_len += tcp_hdrlen(skb);
- else
- hdr_len += sizeof(struct udphdr);
+ if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) {
+ const struct tcphdr *th;
+ struct tcphdr _tcphdr;
+
+ th = skb_header_pointer(skb, skb_transport_offset(skb),
+ sizeof(_tcphdr), &_tcphdr);
+ if (likely(th))
+ hdr_len += __tcp_hdrlen(th);
+ } else {
+ struct udphdr _udphdr;
+
+ if (skb_header_pointer(skb, skb_transport_offset(skb),
+ sizeof(_udphdr), &_udphdr))
+ hdr_len += sizeof(struct udphdr);
+ }
if (shinfo->gso_type & SKB_GSO_DODGY)
gso_segs = DIV_ROUND_UP(skb->len - hdr_len,
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index e9989b8..7913771 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -742,15 +742,6 @@
return dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings);
}
-static void
-warn_incomplete_ethtool_legacy_settings_conversion(const char *details)
-{
- char name[sizeof(current->comm)];
-
- pr_info_once("warning: `%s' uses legacy ethtool link settings API, %s\n",
- get_task_comm(name, current), details);
-}
-
/* Query device for its ethtool_cmd settings.
*
* Backward compatibility note: for compatibility with legacy ethtool,
@@ -777,10 +768,8 @@
&link_ksettings);
if (err < 0)
return err;
- if (!convert_link_ksettings_to_legacy_settings(&cmd,
- &link_ksettings))
- warn_incomplete_ethtool_legacy_settings_conversion(
- "link modes are only partially reported");
+ convert_link_ksettings_to_legacy_settings(&cmd,
+ &link_ksettings);
/* send a sensible cmd tag back to user */
cmd.cmd = ETHTOOL_GSET;
diff --git a/net/core/filter.c b/net/core/filter.c
index 5e42e0e..c385c55 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -451,6 +451,10 @@
convert_bpf_extensions(fp, &insn))
break;
+ if (fp->code == (BPF_ALU | BPF_DIV | BPF_X) ||
+ fp->code == (BPF_ALU | BPF_MOD | BPF_X))
+ *insn++ = BPF_MOV32_REG(BPF_REG_X, BPF_REG_X);
+
*insn = BPF_RAW_INSN(fp->code, BPF_REG_A, BPF_REG_X, 0, fp->k);
break;
@@ -1015,11 +1019,9 @@
*/
goto out_err_free;
- /* We are guaranteed to never error here with cBPF to eBPF
- * transitions, since there's no issue with type compatibility
- * checks on program arrays.
- */
fp = bpf_prog_select_runtime(fp, &err);
+ if (err)
+ goto out_err_free;
kfree(old_prog);
return fp;
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index 32e4e01..862d63e 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -550,8 +550,8 @@
out_good:
ret = true;
- key_control->thoff = (u16)nhoff;
out:
+ key_control->thoff = min_t(u16, nhoff, skb ? skb->len : hlen);
key_basic->n_proto = proto;
key_basic->ip_proto = ip_proto;
@@ -559,7 +559,6 @@
out_bad:
ret = false;
- key_control->thoff = min_t(u16, nhoff, skb ? skb->len : hlen);
goto out;
}
EXPORT_SYMBOL(__skb_flow_dissect);
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index b91cecc..7d07d6b 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -497,7 +497,7 @@
if (atomic_read(&tbl->entries) > (1 << nht->hash_shift))
nht = neigh_hash_grow(tbl, nht->hash_shift + 1);
- hash_val = tbl->hash(pkey, dev, nht->hash_rnd) >> (32 - nht->hash_shift);
+ hash_val = tbl->hash(n->primary_key, dev, nht->hash_rnd) >> (32 - nht->hash_shift);
if (n->parms->dead) {
rc = ERR_PTR(-EINVAL);
@@ -509,7 +509,7 @@
n1 != NULL;
n1 = rcu_dereference_protected(n1->next,
lockdep_is_held(&tbl->lock))) {
- if (dev == n1->dev && !memcmp(n1->primary_key, pkey, key_len)) {
+ if (dev == n1->dev && !memcmp(n1->primary_key, n->primary_key, key_len)) {
if (want_ref)
neigh_hold(n1);
rc = n1;
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 7001da9..b7efe2f 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -263,7 +263,7 @@
spin_lock_irqsave(&net->nsid_lock, flags);
peer = idr_find(&net->netns_ids, id);
if (peer)
- get_net(peer);
+ peer = maybe_get_net(peer);
spin_unlock_irqrestore(&net->nsid_lock, flags);
rcu_read_unlock();
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index d7ecf40..26ef78a 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3829,7 +3829,7 @@
struct sock *sk = skb->sk;
if (!skb_may_tx_timestamp(sk, false))
- return;
+ goto err;
/* Take a reference to prevent skb_orphan() from freeing the socket,
* but only if the socket refcount is not zero.
@@ -3838,7 +3838,11 @@
*skb_hwtstamps(skb) = *hwtstamps;
__skb_complete_tx_timestamp(skb, sk, SCM_TSTAMP_SND);
sock_put(sk);
+ return;
}
+
+err:
+ kfree_skb(skb);
}
EXPORT_SYMBOL_GPL(skb_complete_tx_timestamp);
diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c
index acd2a6c..fb467db 100644
--- a/net/core/sock_diag.c
+++ b/net/core/sock_diag.c
@@ -295,7 +295,7 @@
case SKNLGRP_INET6_UDP_DESTROY:
if (!sock_diag_handlers[AF_INET6])
request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
- NETLINK_SOCK_DIAG, AF_INET);
+ NETLINK_SOCK_DIAG, AF_INET6);
break;
}
return 0;
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index a7f05f0..1b46190 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -292,7 +292,13 @@
.data = &bpf_jit_enable,
.maxlen = sizeof(int),
.mode = 0644,
+#ifndef CONFIG_BPF_JIT_ALWAYS_ON
.proc_handler = proc_dointvec
+#else
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &one,
+ .extra2 = &one,
+#endif
},
# ifdef CONFIG_HAVE_EBPF_JIT
{
diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c
index 5e3a730..7753681 100644
--- a/net/dccp/ccids/ccid2.c
+++ b/net/dccp/ccids/ccid2.c
@@ -140,6 +140,9 @@
ccid2_pr_debug("RTO_EXPIRE\n");
+ if (sk->sk_state == DCCP_CLOSED)
+ goto out;
+
/* back-off timer */
hc->tx_rto <<= 1;
if (hc->tx_rto > DCCP_RTO_MAX)
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index 51b27ae..e60517e 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -223,11 +223,16 @@
static int arp_constructor(struct neighbour *neigh)
{
- __be32 addr = *(__be32 *)neigh->primary_key;
+ __be32 addr;
struct net_device *dev = neigh->dev;
struct in_device *in_dev;
struct neigh_parms *parms;
+ u32 inaddr_any = INADDR_ANY;
+ if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
+ memcpy(neigh->primary_key, &inaddr_any, arp_tbl.key_len);
+
+ addr = *(__be32 *)neigh->primary_key;
rcu_read_lock();
in_dev = __in_dev_get_rcu(dev);
if (!in_dev) {
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index db54461..4457e5d 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -1380,7 +1380,7 @@
static bool inetdev_valid_mtu(unsigned int mtu)
{
- return mtu >= 68;
+ return mtu >= IPV4_MIN_MTU;
}
static void inetdev_send_gratuitous_arp(struct net_device *dev,
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 2ec005c..08b7260 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -1254,14 +1254,19 @@
static void ip_fib_net_exit(struct net *net)
{
- unsigned int i;
+ int i;
rtnl_lock();
#ifdef CONFIG_IP_MULTIPLE_TABLES
RCU_INIT_POINTER(net->ipv4.fib_main, NULL);
RCU_INIT_POINTER(net->ipv4.fib_default, NULL);
#endif
- for (i = 0; i < FIB_TABLE_HASHSZ; i++) {
+ /* Destroy the tables in reverse order to guarantee that the
+ * local table, ID 255, is destroyed before the main table, ID
+ * 254. This is necessary as the local table may contain
+ * references to data contained in the main table.
+ */
+ for (i = FIB_TABLE_HASHSZ - 1; i >= 0; i--) {
struct hlist_head *head = &net->ipv4.fib_table_hash[i];
struct hlist_node *tmp;
struct fib_table *tb;
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 08575e3..9c7a4ce 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -89,6 +89,7 @@
#include <linux/rtnetlink.h>
#include <linux/times.h>
#include <linux/pkt_sched.h>
+#include <linux/byteorder/generic.h>
#include <net/net_namespace.h>
#include <net/arp.h>
@@ -321,6 +322,23 @@
return scount;
}
+/* source address selection per RFC 3376 section 4.2.13 */
+static __be32 igmpv3_get_srcaddr(struct net_device *dev,
+ const struct flowi4 *fl4)
+{
+ struct in_device *in_dev = __in_dev_get_rcu(dev);
+
+ if (!in_dev)
+ return htonl(INADDR_ANY);
+
+ for_ifa(in_dev) {
+ if (fl4->saddr == ifa->ifa_local)
+ return fl4->saddr;
+ } endfor_ifa(in_dev);
+
+ return htonl(INADDR_ANY);
+}
+
static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu)
{
struct sk_buff *skb;
@@ -368,7 +386,7 @@
pip->frag_off = htons(IP_DF);
pip->ttl = 1;
pip->daddr = fl4.daddr;
- pip->saddr = fl4.saddr;
+ pip->saddr = igmpv3_get_srcaddr(dev, &fl4);
pip->protocol = IPPROTO_IGMP;
pip->tot_len = 0; /* filled in later */
ip_select_ident(net, skb, NULL);
@@ -404,16 +422,17 @@
}
static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc,
- int type, struct igmpv3_grec **ppgr)
+ int type, struct igmpv3_grec **ppgr, unsigned int mtu)
{
struct net_device *dev = pmc->interface->dev;
struct igmpv3_report *pih;
struct igmpv3_grec *pgr;
- if (!skb)
- skb = igmpv3_newpack(dev, dev->mtu);
- if (!skb)
- return NULL;
+ if (!skb) {
+ skb = igmpv3_newpack(dev, mtu);
+ if (!skb)
+ return NULL;
+ }
pgr = (struct igmpv3_grec *)skb_put(skb, sizeof(struct igmpv3_grec));
pgr->grec_type = type;
pgr->grec_auxwords = 0;
@@ -436,12 +455,17 @@
struct igmpv3_grec *pgr = NULL;
struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list;
int scount, stotal, first, isquery, truncate;
+ unsigned int mtu;
if (pmc->multiaddr == IGMP_ALL_HOSTS)
return skb;
if (ipv4_is_local_multicast(pmc->multiaddr) && !net->ipv4.sysctl_igmp_llm_reports)
return skb;
+ mtu = READ_ONCE(dev->mtu);
+ if (mtu < IPV4_MIN_MTU)
+ return skb;
+
isquery = type == IGMPV3_MODE_IS_INCLUDE ||
type == IGMPV3_MODE_IS_EXCLUDE;
truncate = type == IGMPV3_MODE_IS_EXCLUDE ||
@@ -462,7 +486,7 @@
AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) {
if (skb)
igmpv3_sendpack(skb);
- skb = igmpv3_newpack(dev, dev->mtu);
+ skb = igmpv3_newpack(dev, mtu);
}
}
first = 1;
@@ -498,12 +522,12 @@
pgr->grec_nsrcs = htons(scount);
if (skb)
igmpv3_sendpack(skb);
- skb = igmpv3_newpack(dev, dev->mtu);
+ skb = igmpv3_newpack(dev, mtu);
first = 1;
scount = 0;
}
if (first) {
- skb = add_grhead(skb, pmc, type, &pgr);
+ skb = add_grhead(skb, pmc, type, &pgr, mtu);
first = 0;
}
if (!skb)
@@ -538,7 +562,7 @@
igmpv3_sendpack(skb);
skb = NULL; /* add_grhead will get a new one */
}
- skb = add_grhead(skb, pmc, type, &pgr);
+ skb = add_grhead(skb, pmc, type, &pgr, mtu);
}
}
if (pgr)
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index bd7f183..96536a0 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -346,8 +346,8 @@
dev->needed_headroom = t_hlen + hlen;
mtu -= (dev->hard_header_len + t_hlen);
- if (mtu < 68)
- mtu = 68;
+ if (mtu < IPV4_MIN_MTU)
+ mtu = IPV4_MIN_MTU;
return mtu;
}
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index ab0bbcb..a860df2 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -502,11 +502,16 @@
int err;
struct ip_options_data opt_copy;
struct raw_frag_vec rfv;
+ int hdrincl;
err = -EMSGSIZE;
if (len > 0xFFFF)
goto out;
+ /* hdrincl should be READ_ONCE(inet->hdrincl)
+ * but READ_ONCE() doesn't work with bit fields
+ */
+ hdrincl = inet->hdrincl;
/*
* Check the flags.
*/
@@ -582,7 +587,7 @@
/* Linux does not mangle headers on raw sockets,
* so that IP options + IP_HDRINCL is non-sense.
*/
- if (inet->hdrincl)
+ if (hdrincl)
goto done;
if (ipc.opt->opt.srr) {
if (!daddr)
@@ -604,12 +609,12 @@
flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos,
RT_SCOPE_UNIVERSE,
- inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol,
+ hdrincl ? IPPROTO_RAW : sk->sk_protocol,
inet_sk_flowi_flags(sk) |
- (inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0),
+ (hdrincl ? FLOWI_FLAG_KNOWN_NH : 0),
daddr, saddr, 0, 0, sk->sk_uid);
- if (!inet->hdrincl) {
+ if (!hdrincl) {
rfv.msg = msg;
rfv.hlen = 0;
@@ -634,7 +639,7 @@
goto do_confirm;
back_from_confirm:
- if (inet->hdrincl)
+ if (hdrincl)
err = raw_send_hdrinc(sk, &fl4, msg, len,
&rt, msg->msg_flags, &ipc.sockc);
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 0a57417..cb3f08c 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2224,6 +2224,9 @@
tcp_send_active_reset(sk, GFP_ATOMIC);
__NET_INC_STATS(sock_net(sk),
LINUX_MIB_TCPABORTONMEMORY);
+ } else if (!check_net(sock_net(sk))) {
+ /* Not possible to send reset; just close */
+ tcp_set_state(sk, TCP_CLOSE);
}
}
@@ -2306,6 +2309,7 @@
tp->snd_cwnd_cnt = 0;
tp->window_clamp = 0;
tcp_set_ca_state(sk, TCP_CA_Open);
+ tp->is_sack_reneg = 0;
tcp_clear_retrans(tp);
inet_csk_delack_init(sk);
/* Initialize rcv_mss to TCP_MIN_MSS to avoid division by 0
diff --git a/net/ipv4/tcp_bbr.c b/net/ipv4/tcp_bbr.c
index cb8db34..e86a34f 100644
--- a/net/ipv4/tcp_bbr.c
+++ b/net/ipv4/tcp_bbr.c
@@ -81,7 +81,8 @@
u32 lt_last_lost; /* LT intvl start: tp->lost */
u32 pacing_gain:10, /* current gain for setting pacing rate */
cwnd_gain:10, /* current gain for setting cwnd */
- full_bw_cnt:3, /* number of rounds without large bw gains */
+ full_bw_reached:1, /* reached full bw in Startup? */
+ full_bw_cnt:2, /* number of rounds without large bw gains */
cycle_idx:3, /* current index in pacing_gain cycle array */
has_seen_rtt:1, /* have we seen an RTT sample yet? */
unused_b:5;
@@ -151,7 +152,7 @@
{
const struct bbr *bbr = inet_csk_ca(sk);
- return bbr->full_bw_cnt >= bbr_full_bw_cnt;
+ return bbr->full_bw_reached;
}
/* Return the windowed max recent bandwidth sample, in pkts/uS << BW_SCALE. */
@@ -688,6 +689,7 @@
return;
}
++bbr->full_bw_cnt;
+ bbr->full_bw_reached = bbr->full_bw_cnt >= bbr_full_bw_cnt;
}
/* If pipe is probably full, drain the queue and then enter steady-state. */
@@ -821,6 +823,7 @@
bbr->restore_cwnd = 0;
bbr->round_start = 0;
bbr->idle_restart = 0;
+ bbr->full_bw_reached = 0;
bbr->full_bw = 0;
bbr->full_bw_cnt = 0;
bbr->cycle_mstamp.v64 = 0;
@@ -840,6 +843,11 @@
*/
static u32 bbr_undo_cwnd(struct sock *sk)
{
+ struct bbr *bbr = inet_csk_ca(sk);
+
+ bbr->full_bw = 0; /* spurious slow-down; reset full pipe detection */
+ bbr->full_bw_cnt = 0;
+ bbr_reset_lt_bw_sampling(sk);
return tcp_sk(sk)->snd_cwnd;
}
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index b6a7f91..8ce03d8 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -1967,6 +1967,8 @@
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPSACKRENEGING);
tp->sacked_out = 0;
tp->fackets_out = 0;
+ /* Mark SACK reneging until we recover from this loss event. */
+ tp->is_sack_reneg = 1;
}
tcp_clear_all_retrans_hints(tp);
@@ -2464,6 +2466,7 @@
return true;
}
tcp_set_ca_state(sk, TCP_CA_Open);
+ tp->is_sack_reneg = 0;
return false;
}
@@ -2495,8 +2498,10 @@
NET_INC_STATS(sock_net(sk),
LINUX_MIB_TCPSPURIOUSRTOS);
inet_csk(sk)->icsk_retransmits = 0;
- if (frto_undo || tcp_is_sack(tp))
+ if (frto_undo || tcp_is_sack(tp)) {
tcp_set_ca_state(sk, TCP_CA_Open);
+ tp->is_sack_reneg = 0;
+ }
return true;
}
return false;
@@ -3590,6 +3595,7 @@
struct tcp_sacktag_state sack_state;
struct rate_sample rs = { .prior_delivered = 0 };
u32 prior_snd_una = tp->snd_una;
+ bool is_sack_reneg = tp->is_sack_reneg;
u32 ack_seq = TCP_SKB_CB(skb)->seq;
u32 ack = TCP_SKB_CB(skb)->ack_seq;
bool is_dupack = false;
@@ -3712,7 +3718,7 @@
tcp_schedule_loss_probe(sk);
delivered = tp->delivered - delivered; /* freshly ACKed or SACKed */
lost = tp->lost - lost; /* freshly marked lost */
- tcp_rate_gen(sk, delivered, lost, &now, &rs);
+ tcp_rate_gen(sk, delivered, lost, is_sack_reneg, &now, &rs);
tcp_cong_control(sk, ack, delivered, flag, &rs);
tcp_xmit_recovery(sk, rexmit);
return 1;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 49d32fbc..05ac17a 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -831,7 +831,7 @@
tcp_time_stamp,
req->ts_recent,
0,
- tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&ip_hdr(skb)->daddr,
+ tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&ip_hdr(skb)->saddr,
AF_INET),
inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0,
ip_hdr(skb)->tos);
diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c
index bc68da3..366b1be 100644
--- a/net/ipv4/tcp_offload.c
+++ b/net/ipv4/tcp_offload.c
@@ -32,6 +32,9 @@
static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb,
netdev_features_t features)
{
+ if (!(skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4))
+ return ERR_PTR(-EINVAL);
+
if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
return ERR_PTR(-EINVAL);
diff --git a/net/ipv4/tcp_rate.c b/net/ipv4/tcp_rate.c
index 9be1581..18309f5 100644
--- a/net/ipv4/tcp_rate.c
+++ b/net/ipv4/tcp_rate.c
@@ -106,7 +106,7 @@
/* Update the connection delivery information and generate a rate sample. */
void tcp_rate_gen(struct sock *sk, u32 delivered, u32 lost,
- struct skb_mstamp *now, struct rate_sample *rs)
+ bool is_sack_reneg, struct skb_mstamp *now, struct rate_sample *rs)
{
struct tcp_sock *tp = tcp_sk(sk);
u32 snd_us, ack_us;
@@ -124,8 +124,12 @@
rs->acked_sacked = delivered; /* freshly ACKed or SACKed */
rs->losses = lost; /* freshly marked lost */
- /* Return an invalid sample if no timing information is available. */
- if (!rs->prior_mstamp.v64) {
+ /* Return an invalid sample if no timing information is available or
+ * in recovery from loss with SACK reneging. Rate samples taken during
+ * a SACK reneging event may overestimate bw by including packets that
+ * were SACKed before the reneg.
+ */
+ if (!rs->prior_mstamp.v64 || is_sack_reneg) {
rs->delivered = -1;
rs->interval_us = -1;
return;
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index d3d3ef6..4be66e4 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -82,11 +82,19 @@
* to prevent DoS attacks. It is called when a retransmission timeout
* or zero probe timeout occurs on orphaned socket.
*
+ * Also close if our net namespace is exiting; in that case there is no
+ * hope of ever communicating again since all netns interfaces are already
+ * down (or about to be down), and we need to release our dst references,
+ * which have been moved to the netns loopback interface, so the namespace
+ * can finish exiting. This condition is only possible if we are a kernel
+ * socket, as those do not hold references to the namespace.
+ *
* Criteria is still not confirmed experimentally and may change.
* We kill the socket, if:
* 1. If number of orphaned sockets exceeds an administratively configured
* limit.
* 2. If we have strong memory pressure.
+ * 3. If our net namespace is exiting.
*/
static int tcp_out_of_resources(struct sock *sk, bool do_reset)
{
@@ -115,6 +123,13 @@
__NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONMEMORY);
return 1;
}
+
+ if (!check_net(sock_net(sk))) {
+ /* Not possible to send reset; just close */
+ tcp_done(sk);
+ return 1;
+ }
+
return 0;
}
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index 6401574..f4f616e 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -205,6 +205,9 @@
goto out;
}
+ if (!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP))
+ goto out;
+
if (!pskb_may_pull(skb, sizeof(struct udphdr)))
goto out;
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index f51f94b..55a8f68 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -225,7 +225,6 @@
np->mcast_hops = IPV6_DEFAULT_MCASTHOPS;
np->mc_loop = 1;
np->pmtudisc = IPV6_PMTUDISC_WANT;
- np->autoflowlabel = ip6_default_np_autolabel(sock_net(sk));
sk->sk_ipv6only = net->ipv6.sysctl.bindv6only;
/* Init the ipv4 part of the socket since we can have sockets
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index a7d0c01..21bd54f 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -337,11 +337,12 @@
nt->dev = dev;
nt->net = dev_net(dev);
- ip6gre_tnl_link_config(nt, 1);
if (register_netdevice(dev) < 0)
goto failed_free;
+ ip6gre_tnl_link_config(nt, 1);
+
/* Can use a lockless transmit, unless we generate output sequences */
if (!(nt->parms.o_flags & TUNNEL_SEQ))
dev->features |= NETIF_F_LLTX;
@@ -1267,7 +1268,6 @@
static int ip6gre_tap_init(struct net_device *dev)
{
- struct ip6_tnl *tunnel;
int ret;
ret = ip6gre_tunnel_init_common(dev);
@@ -1276,10 +1276,6 @@
dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
- tunnel = netdev_priv(dev);
-
- ip6gre_tnl_link_config(tunnel, 1);
-
return 0;
}
@@ -1374,7 +1370,6 @@
nt->dev = dev;
nt->net = dev_net(dev);
- ip6gre_tnl_link_config(nt, !tb[IFLA_MTU]);
dev->features |= GRE6_FEATURES;
dev->hw_features |= GRE6_FEATURES;
@@ -1400,6 +1395,11 @@
if (err)
goto out;
+ ip6gre_tnl_link_config(nt, !tb[IFLA_MTU]);
+
+ if (tb[IFLA_MTU])
+ ip6_tnl_change_mtu(dev, nla_get_u32(tb[IFLA_MTU]));
+
dev_hold(dev);
ip6gre_tunnel_link(ign, nt);
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 4e6c439..55ca65c 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -165,6 +165,14 @@
!(IP6CB(skb)->flags & IP6SKB_REROUTED));
}
+bool ip6_autoflowlabel(struct net *net, const struct ipv6_pinfo *np)
+{
+ if (!np->autoflowlabel_set)
+ return ip6_default_np_autolabel(net);
+ else
+ return np->autoflowlabel;
+}
+
/*
* xmit an sk_buff (used by TCP, SCTP and DCCP)
* Note : socket lock is not held for SYNACK packets, but might be modified
@@ -228,7 +236,7 @@
hlimit = ip6_dst_hoplimit(dst);
ip6_flow_hdr(hdr, tclass, ip6_make_flowlabel(net, skb, fl6->flowlabel,
- np->autoflowlabel, fl6));
+ ip6_autoflowlabel(net, np), fl6));
hdr->payload_len = htons(seg_len);
hdr->nexthdr = proto;
@@ -1260,14 +1268,16 @@
v6_cork->tclass = ipc6->tclass;
if (rt->dst.flags & DST_XFRM_TUNNEL)
mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ?
- rt->dst.dev->mtu : dst_mtu(&rt->dst);
+ READ_ONCE(rt->dst.dev->mtu) : dst_mtu(&rt->dst);
else
mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ?
- rt->dst.dev->mtu : dst_mtu(rt->dst.path);
+ READ_ONCE(rt->dst.dev->mtu) : dst_mtu(rt->dst.path);
if (np->frag_size < mtu) {
if (np->frag_size)
mtu = np->frag_size;
}
+ if (mtu < IPV6_MIN_MTU)
+ return -EINVAL;
cork->base.fragsize = mtu;
if (dst_allfrag(rt->dst.path))
cork->base.flags |= IPCORK_ALLFRAG;
@@ -1699,7 +1709,7 @@
ip6_flow_hdr(hdr, v6_cork->tclass,
ip6_make_flowlabel(net, skb, fl6->flowlabel,
- np->autoflowlabel, fl6));
+ ip6_autoflowlabel(net, np), fl6));
hdr->hop_limit = v6_cork->hop_limit;
hdr->nexthdr = proto;
hdr->saddr = fl6->saddr;
@@ -1798,11 +1808,13 @@
cork.base.flags = 0;
cork.base.addr = 0;
cork.base.opt = NULL;
+ cork.base.dst = NULL;
v6_cork.opt = NULL;
err = ip6_setup_cork(sk, &cork, &v6_cork, ipc6, rt, fl6);
- if (err)
+ if (err) {
+ ip6_cork_release(&cork, &v6_cork);
return ERR_PTR(err);
-
+ }
if (ipc6->dontfrag < 0)
ipc6->dontfrag = inet6_sk(sk)->dontfrag;
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 64aefc2..4c52236 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -911,7 +911,7 @@
if (t->parms.collect_md) {
tun_dst = ipv6_tun_rx_dst(skb, 0, 0, 0);
if (!tun_dst)
- return 0;
+ goto drop;
}
ret = __ip6_tnl_rcv(t, skb, tpi, tun_dst, dscp_ecn_decapsulate,
log_ecn_error);
@@ -1080,10 +1080,11 @@
memcpy(&fl6->daddr, addr6, sizeof(fl6->daddr));
neigh_release(neigh);
}
- } else if (!(t->parms.flags &
- (IP6_TNL_F_USE_ORIG_TCLASS | IP6_TNL_F_USE_ORIG_FWMARK))) {
- /* enable the cache only only if the routing decision does
- * not depend on the current inner header value
+ } else if (t->parms.proto != 0 && !(t->parms.flags &
+ (IP6_TNL_F_USE_ORIG_TCLASS |
+ IP6_TNL_F_USE_ORIG_FWMARK))) {
+ /* enable the cache only if neither the outer protocol nor the
+ * routing decision depends on the current inner header value
*/
use_cache = true;
}
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 38bee17..bcea985 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -874,6 +874,7 @@
break;
case IPV6_AUTOFLOWLABEL:
np->autoflowlabel = valbool;
+ np->autoflowlabel_set = 1;
retv = 0;
break;
}
@@ -1315,7 +1316,7 @@
break;
case IPV6_AUTOFLOWLABEL:
- val = np->autoflowlabel;
+ val = ip6_autoflowlabel(sock_net(sk), np);
break;
default:
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 1bdc703..ca8fac6 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1682,16 +1682,16 @@
}
static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc,
- int type, struct mld2_grec **ppgr)
+ int type, struct mld2_grec **ppgr, unsigned int mtu)
{
- struct net_device *dev = pmc->idev->dev;
struct mld2_report *pmr;
struct mld2_grec *pgr;
- if (!skb)
- skb = mld_newpack(pmc->idev, dev->mtu);
- if (!skb)
- return NULL;
+ if (!skb) {
+ skb = mld_newpack(pmc->idev, mtu);
+ if (!skb)
+ return NULL;
+ }
pgr = (struct mld2_grec *)skb_put(skb, sizeof(struct mld2_grec));
pgr->grec_type = type;
pgr->grec_auxwords = 0;
@@ -1714,10 +1714,15 @@
struct mld2_grec *pgr = NULL;
struct ip6_sf_list *psf, *psf_next, *psf_prev, **psf_list;
int scount, stotal, first, isquery, truncate;
+ unsigned int mtu;
if (pmc->mca_flags & MAF_NOREPORT)
return skb;
+ mtu = READ_ONCE(dev->mtu);
+ if (mtu < IPV6_MIN_MTU)
+ return skb;
+
isquery = type == MLD2_MODE_IS_INCLUDE ||
type == MLD2_MODE_IS_EXCLUDE;
truncate = type == MLD2_MODE_IS_EXCLUDE ||
@@ -1738,7 +1743,7 @@
AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) {
if (skb)
mld_sendpack(skb);
- skb = mld_newpack(idev, dev->mtu);
+ skb = mld_newpack(idev, mtu);
}
}
first = 1;
@@ -1774,12 +1779,12 @@
pgr->grec_nsrcs = htons(scount);
if (skb)
mld_sendpack(skb);
- skb = mld_newpack(idev, dev->mtu);
+ skb = mld_newpack(idev, mtu);
first = 1;
scount = 0;
}
if (first) {
- skb = add_grhead(skb, pmc, type, &pgr);
+ skb = add_grhead(skb, pmc, type, &pgr, mtu);
first = 0;
}
if (!skb)
@@ -1814,7 +1819,7 @@
mld_sendpack(skb);
skb = NULL; /* add_grhead will get a new one */
}
- skb = add_grhead(skb, pmc, type, &pgr);
+ skb = add_grhead(skb, pmc, type, &pgr, mtu);
}
}
if (pgr)
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index 5edfe66..64ec233 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -263,6 +263,7 @@
* this case. -DaveM
*/
pr_debug("end of fragment not rounded to 8 bytes.\n");
+ inet_frag_kill(&fq->q, &nf_frags);
return -EPROTO;
}
if (end > fq->q.len) {
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 9828dc2..bb84165 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -964,7 +964,7 @@
tcp_rsk(req)->rcv_nxt,
req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale,
tcp_time_stamp, req->ts_recent, sk->sk_bound_dev_if,
- tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr),
+ tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr),
0, 0);
}
diff --git a/net/ipv6/tcpv6_offload.c b/net/ipv6/tcpv6_offload.c
index d883c92..278e49c 100644
--- a/net/ipv6/tcpv6_offload.c
+++ b/net/ipv6/tcpv6_offload.c
@@ -46,6 +46,9 @@
{
struct tcphdr *th;
+ if (!(skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6))
+ return ERR_PTR(-EINVAL);
+
if (!pskb_may_pull(skb, sizeof(*th)))
return ERR_PTR(-EINVAL);
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index e7d378c..2bd2087 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -55,6 +55,9 @@
const struct ipv6hdr *ipv6h;
struct udphdr *uh;
+ if (!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP))
+ goto out;
+
if (!pskb_may_pull(skb, sizeof(struct udphdr)))
goto out;
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 94bf810..6482b00 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -401,6 +401,11 @@
#endif
int len;
+ if (sp->sadb_address_len <
+ DIV_ROUND_UP(sizeof(*sp) + offsetofend(typeof(*addr), sa_family),
+ sizeof(uint64_t)))
+ return -EINVAL;
+
switch (addr->sa_family) {
case AF_INET:
len = DIV_ROUND_UP(sizeof(*sp) + sizeof(*sin), sizeof(uint64_t));
@@ -511,6 +516,9 @@
uint16_t ext_type;
int ext_len;
+ if (len < sizeof(*ehdr))
+ return -EINVAL;
+
ext_len = ehdr->sadb_ext_len;
ext_len *= sizeof(uint64_t);
ext_type = ehdr->sadb_ext_type;
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index b747c96..fed598a 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -788,7 +788,7 @@
struct mesh_path *mpath;
u8 ttl, flags, hopcount;
const u8 *orig_addr;
- u32 orig_sn, metric, metric_txsta, interval;
+ u32 orig_sn, new_metric, orig_metric, last_hop_metric, interval;
bool root_is_gate;
ttl = rann->rann_ttl;
@@ -799,7 +799,7 @@
interval = le32_to_cpu(rann->rann_interval);
hopcount = rann->rann_hopcount;
hopcount++;
- metric = le32_to_cpu(rann->rann_metric);
+ orig_metric = le32_to_cpu(rann->rann_metric);
/* Ignore our own RANNs */
if (ether_addr_equal(orig_addr, sdata->vif.addr))
@@ -816,7 +816,10 @@
return;
}
- metric_txsta = airtime_link_metric_get(local, sta);
+ last_hop_metric = airtime_link_metric_get(local, sta);
+ new_metric = orig_metric + last_hop_metric;
+ if (new_metric < orig_metric)
+ new_metric = MAX_METRIC;
mpath = mesh_path_lookup(sdata, orig_addr);
if (!mpath) {
@@ -829,7 +832,7 @@
}
if (!(SN_LT(mpath->sn, orig_sn)) &&
- !(mpath->sn == orig_sn && metric < mpath->rann_metric)) {
+ !(mpath->sn == orig_sn && new_metric < mpath->rann_metric)) {
rcu_read_unlock();
return;
}
@@ -847,7 +850,7 @@
}
mpath->sn = orig_sn;
- mpath->rann_metric = metric + metric_txsta;
+ mpath->rann_metric = new_metric;
mpath->is_root = true;
/* Recording RANNs sender address to send individually
* addressed PREQs destined for root mesh STA */
@@ -867,7 +870,7 @@
mesh_path_sel_frame_tx(MPATH_RANN, flags, orig_addr,
orig_sn, 0, NULL, 0, broadcast_addr,
hopcount, ttl, interval,
- metric + metric_txsta, 0, sdata);
+ new_metric, 0, sdata);
}
rcu_read_unlock();
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c
index efa3f48..73e8f34 100644
--- a/net/mac80211/wep.c
+++ b/net/mac80211/wep.c
@@ -293,7 +293,8 @@
return RX_DROP_UNUSABLE;
ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
/* remove ICV */
- if (pskb_trim(rx->skb, rx->skb->len - IEEE80211_WEP_ICV_LEN))
+ if (!(status->flag & RX_FLAG_ICV_STRIPPED) &&
+ pskb_trim(rx->skb, rx->skb->len - IEEE80211_WEP_ICV_LEN))
return RX_DROP_UNUSABLE;
}
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 5c71d60..caa5986 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -295,7 +295,8 @@
return RX_DROP_UNUSABLE;
/* Trim ICV */
- skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN);
+ if (!(status->flag & RX_FLAG_ICV_STRIPPED))
+ skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN);
/* Remove IV */
memmove(skb->data + IEEE80211_TKIP_IV_LEN, skb->data, hdrlen);
diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c
index 28d0653..3f49912 100644
--- a/net/netfilter/nfnetlink_cthelper.c
+++ b/net/netfilter/nfnetlink_cthelper.c
@@ -17,6 +17,7 @@
#include <linux/types.h>
#include <linux/list.h>
#include <linux/errno.h>
+#include <linux/capability.h>
#include <net/netlink.h>
#include <net/sock.h>
@@ -392,6 +393,9 @@
struct nfnl_cthelper *nlcth;
int ret = 0;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
if (!tb[NFCTH_NAME] || !tb[NFCTH_TUPLE])
return -EINVAL;
@@ -595,6 +599,9 @@
struct nfnl_cthelper *nlcth;
bool tuple_set = false;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
if (nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = {
.dump = nfnl_cthelper_dump_table,
@@ -661,6 +668,9 @@
struct nfnl_cthelper *nlcth, *n;
int j = 0, ret;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
if (tb[NFCTH_NAME])
helper_name = nla_data(tb[NFCTH_NAME]);
diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
index 2455b69..b589a62 100644
--- a/net/netfilter/xt_osf.c
+++ b/net/netfilter/xt_osf.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/capability.h>
#include <linux/if.h>
#include <linux/inetdevice.h>
#include <linux/ip.h>
@@ -69,6 +70,9 @@
struct xt_osf_finger *kf = NULL, *sf;
int err = 0;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
if (!osf_attrs[OSF_ATTR_FINGER])
return -EINVAL;
@@ -113,6 +117,9 @@
struct xt_osf_finger *sf;
int err = -ENOENT;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
if (!osf_attrs[OSF_ATTR_FINGER])
return -EINVAL;
diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c
index ec87467..313a6dd 100644
--- a/net/netfilter/xt_socket.c
+++ b/net/netfilter/xt_socket.c
@@ -161,10 +161,13 @@
#endif
if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) {
- struct udphdr _hdr, *hp;
+ struct udphdr *hp;
+ struct tcphdr _hdr;
hp = skb_header_pointer(skb, ip_hdrlen(skb),
- sizeof(_hdr), &_hdr);
+ iph->protocol == IPPROTO_UDP ?
+ sizeof(*hp) : sizeof(_hdr),
+ &_hdr);
if (hp == NULL)
return NULL;
@@ -370,9 +373,11 @@
}
if (tproto == IPPROTO_UDP || tproto == IPPROTO_TCP) {
- struct udphdr _hdr, *hp;
+ struct udphdr *hp;
+ struct tcphdr _hdr;
- hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
+ hp = skb_header_pointer(skb, thoff, tproto == IPPROTO_UDP ?
+ sizeof(*hp) : sizeof(_hdr), &_hdr);
if (hp == NULL)
return NULL;
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 1ff497b..e1c123d 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -261,6 +261,9 @@
struct sock *sk = skb->sk;
int ret = -ENOMEM;
+ if (!net_eq(dev_net(dev), sock_net(sk)))
+ return 0;
+
dev_hold(dev);
if (is_vmalloc_addr(skb->head))
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index 0792541..1668916 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -1789,14 +1789,11 @@
#define MAX_ACTIONS_BUFSIZE (32 * 1024)
-static struct sw_flow_actions *nla_alloc_flow_actions(int size, bool log)
+static struct sw_flow_actions *nla_alloc_flow_actions(int size)
{
struct sw_flow_actions *sfa;
- if (size > MAX_ACTIONS_BUFSIZE) {
- OVS_NLERR(log, "Flow action size %u bytes exceeds max", size);
- return ERR_PTR(-EINVAL);
- }
+ WARN_ON_ONCE(size > MAX_ACTIONS_BUFSIZE);
sfa = kmalloc(sizeof(*sfa) + size, GFP_KERNEL);
if (!sfa)
@@ -1869,12 +1866,15 @@
new_acts_size = ksize(*sfa) * 2;
if (new_acts_size > MAX_ACTIONS_BUFSIZE) {
- if ((MAX_ACTIONS_BUFSIZE - next_offset) < req_size)
+ if ((MAX_ACTIONS_BUFSIZE - next_offset) < req_size) {
+ OVS_NLERR(log, "Flow action size exceeds max %u",
+ MAX_ACTIONS_BUFSIZE);
return ERR_PTR(-EMSGSIZE);
+ }
new_acts_size = MAX_ACTIONS_BUFSIZE;
}
- acts = nla_alloc_flow_actions(new_acts_size, log);
+ acts = nla_alloc_flow_actions(new_acts_size);
if (IS_ERR(acts))
return (void *)acts;
@@ -2500,7 +2500,7 @@
{
int err;
- *sfa = nla_alloc_flow_actions(nla_len(attr), log);
+ *sfa = nla_alloc_flow_actions(min(nla_len(attr), MAX_ACTIONS_BUFSIZE));
if (IS_ERR(*sfa))
return PTR_ERR(*sfa);
diff --git a/net/rds/rdma.c b/net/rds/rdma.c
index de8496e..f6027f4 100644
--- a/net/rds/rdma.c
+++ b/net/rds/rdma.c
@@ -524,6 +524,9 @@
local_vec = (struct rds_iovec __user *)(unsigned long) args->local_vec_addr;
+ if (args->nr_local == 0)
+ return -EINVAL;
+
/* figure out the number of pages in the vector */
for (i = 0; i < args->nr_local; i++) {
if (copy_from_user(&vec, &local_vec[i],
@@ -873,6 +876,7 @@
err:
if (page)
put_page(page);
+ rm->atomic.op_active = 0;
kfree(rm->atomic.op_notifier);
return ret;
diff --git a/net/rds/send.c b/net/rds/send.c
index ad247dc..ef53d164 100644
--- a/net/rds/send.c
+++ b/net/rds/send.c
@@ -1006,6 +1006,9 @@
continue;
if (cmsg->cmsg_type == RDS_CMSG_RDMA_ARGS) {
+ if (cmsg->cmsg_len <
+ CMSG_LEN(sizeof(struct rds_rdma_args)))
+ return -EINVAL;
args = CMSG_DATA(cmsg);
*rdma_bytes += args->remote_vec.bytes;
}
diff --git a/net/rmnet_data/rmnet_data_handlers.c b/net/rmnet_data/rmnet_data_handlers.c
index 8faf7a7..50dd516 100644
--- a/net/rmnet_data/rmnet_data_handlers.c
+++ b/net/rmnet_data/rmnet_data_handlers.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -453,6 +453,13 @@
}
ep = &config->muxed_ep[mux_id];
+ if (!ep->refcount) {
+ LOGD("Packet on %s:%d; has no logical endpoint config",
+ skb->dev->name, mux_id);
+
+ rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPINGRESS_MUX_NO_EP);
+ return RX_HANDLER_CONSUMED;
+ }
skb->dev = ep->egress_dev;
diff --git a/net/rmnet_data/rmnet_data_stats.h b/net/rmnet_data/rmnet_data_stats.h
index 366e486..75ed434 100644
--- a/net/rmnet_data/rmnet_data_stats.h
+++ b/net/rmnet_data/rmnet_data_stats.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014, 2016, 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -39,6 +39,7 @@
RMNET_STATS_SKBFREE_DEAGG_DATA_LEN_0,
RMNET_STATS_SKBFREE_INGRESS_BAD_MAP_CKSUM,
RMNET_STATS_SKBFREE_MAPC_UNSUPPORTED,
+ RMNET_STATS_SKBFREE_MAPINGRESS_MUX_NO_EP,
RMNET_STATS_SKBFREE_MAX
};
diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c
index e0aa30f..9617b42 100644
--- a/net/sched/act_gact.c
+++ b/net/sched/act_gact.c
@@ -161,7 +161,7 @@
if (action == TC_ACT_SHOT)
this_cpu_ptr(gact->common.cpu_qstats)->drops += packets;
- tm->lastuse = lastuse;
+ tm->lastuse = max_t(u64, tm->lastuse, lastuse);
}
static int tcf_gact_dump(struct sk_buff *skb, struct tc_action *a,
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index 6b07fba..fc3650b 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -211,7 +211,7 @@
struct tcf_t *tm = &m->tcf_tm;
_bstats_cpu_update(this_cpu_ptr(a->cpu_bstats), bytes, packets);
- tm->lastuse = lastuse;
+ tm->lastuse = max_t(u64, tm->lastuse, lastuse);
}
static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind,
diff --git a/net/sctp/offload.c b/net/sctp/offload.c
index 4f5a2b5..6300f28 100644
--- a/net/sctp/offload.c
+++ b/net/sctp/offload.c
@@ -44,6 +44,9 @@
struct sk_buff *segs = ERR_PTR(-EINVAL);
struct sctphdr *sh;
+ if (!(skb_shinfo(skb)->gso_type & SKB_GSO_SCTP))
+ goto out;
+
sh = sctp_hdr(skb);
if (!pskb_may_pull(skb, sizeof(*sh)))
goto out;
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index c2ab864..c472b83 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -83,7 +83,7 @@
static int sctp_writeable(struct sock *sk);
static void sctp_wfree(struct sk_buff *skb);
static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
- size_t msg_len, struct sock **orig_sk);
+ size_t msg_len);
static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p);
static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);
static int sctp_wait_for_accept(struct sock *sk, long timeo);
@@ -332,16 +332,14 @@
if (len < sizeof (struct sockaddr))
return NULL;
+ if (!opt->pf->af_supported(addr->sa.sa_family, opt))
+ return NULL;
+
/* V4 mapped address are really of AF_INET family */
if (addr->sa.sa_family == AF_INET6 &&
- ipv6_addr_v4mapped(&addr->v6.sin6_addr)) {
- if (!opt->pf->af_supported(AF_INET, opt))
- return NULL;
- } else {
- /* Does this PF support this AF? */
- if (!opt->pf->af_supported(addr->sa.sa_family, opt))
- return NULL;
- }
+ ipv6_addr_v4mapped(&addr->v6.sin6_addr) &&
+ !opt->pf->af_supported(AF_INET, opt))
+ return NULL;
/* If we get this far, af is valid. */
af = sctp_get_af_specific(addr->sa.sa_family);
@@ -1958,7 +1956,7 @@
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
if (!sctp_wspace(asoc)) {
/* sk can be changed by peel off when waiting for buf. */
- err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len, &sk);
+ err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len);
if (err) {
if (err == -ESRCH) {
/* asoc is already dead. */
@@ -4246,7 +4244,7 @@
SCTP_DBG_OBJCNT_INC(sock);
local_bh_disable();
- percpu_counter_inc(&sctp_sockets_allocated);
+ sk_sockets_allocated_inc(sk);
sock_prot_inuse_add(net, sk->sk_prot, 1);
/* Nothing can fail after this block, otherwise
@@ -4290,7 +4288,7 @@
}
sctp_endpoint_free(sp->ep);
local_bh_disable();
- percpu_counter_dec(&sctp_sockets_allocated);
+ sk_sockets_allocated_dec(sk);
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
local_bh_enable();
}
@@ -7441,12 +7439,12 @@
/* Helper function to wait for space in the sndbuf. */
static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
- size_t msg_len, struct sock **orig_sk)
+ size_t msg_len)
{
struct sock *sk = asoc->base.sk;
- int err = 0;
long current_timeo = *timeo_p;
DEFINE_WAIT(wait);
+ int err = 0;
pr_debug("%s: asoc:%p, timeo:%ld, msg_len:%zu\n", __func__, asoc,
*timeo_p, msg_len);
@@ -7475,17 +7473,13 @@
release_sock(sk);
current_timeo = schedule_timeout(current_timeo);
lock_sock(sk);
- if (sk != asoc->base.sk) {
- release_sock(sk);
- sk = asoc->base.sk;
- lock_sock(sk);
- }
+ if (sk != asoc->base.sk)
+ goto do_error;
*timeo_p = current_timeo;
}
out:
- *orig_sk = sk;
finish_wait(&asoc->wait, &wait);
/* Release the association's refcnt. */
diff --git a/net/socket.c b/net/socket.c
index fc0b609..bd9679f 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -2566,6 +2566,15 @@
core_initcall(sock_init); /* early initcall */
+static int __init jit_init(void)
+{
+#ifdef CONFIG_BPF_JIT_ALWAYS_ON
+ bpf_jit_enable = 1;
+#endif
+ return 0;
+}
+pure_initcall(jit_init);
+
#ifdef CONFIG_PROC_FS
void socket_seq_show(struct seq_file *seq)
{
diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c
index 25d9a9c..624c322 100644
--- a/net/sunrpc/auth_gss/gss_rpc_xdr.c
+++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c
@@ -231,6 +231,7 @@
goto out_free_groups;
creds->cr_group_info->gid[i] = kgid;
}
+ groups_sort(creds->cr_group_info);
return 0;
out_free_groups:
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 1530825..6a08bc4 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -481,6 +481,7 @@
goto out;
rsci.cred.cr_group_info->gid[i] = kgid;
}
+ groups_sort(rsci.cred.cr_group_info);
/* mech name */
len = qword_get(&mesg, buf, mlen);
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index 64af4f0..738a243 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -520,6 +520,7 @@
ug.gi->gid[i] = kgid;
}
+ groups_sort(ug.gi);
ugp = unix_gid_lookup(cd, uid);
if (ugp) {
struct cache_head *ch;
@@ -819,6 +820,7 @@
kgid_t kgid = make_kgid(&init_user_ns, svc_getnl(argv));
cred->cr_group_info->gid[i] = kgid;
}
+ groups_sort(cred->cr_group_info);
if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) {
*authp = rpc_autherr_badverf;
return SVC_DENIED;
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index e01c825..d24d14e 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -2381,6 +2381,7 @@
case -ECONNREFUSED:
case -ECONNRESET:
case -ENETUNREACH:
+ case -EHOSTUNREACH:
case -EADDRINUSE:
case -ENOBUFS:
/* retry with existing socket, after a delay */
diff --git a/net/tipc/node.c b/net/tipc/node.c
index 2775332..5b3e1ea 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -1848,36 +1848,38 @@
if (strcmp(name, tipc_bclink_name) == 0) {
err = tipc_nl_add_bc_link(net, &msg);
- if (err) {
- nlmsg_free(msg.skb);
- return err;
- }
+ if (err)
+ goto err_free;
} else {
int bearer_id;
struct tipc_node *node;
struct tipc_link *link;
node = tipc_node_find_by_name(net, name, &bearer_id);
- if (!node)
- return -EINVAL;
+ if (!node) {
+ err = -EINVAL;
+ goto err_free;
+ }
tipc_node_read_lock(node);
link = node->links[bearer_id].link;
if (!link) {
tipc_node_read_unlock(node);
- nlmsg_free(msg.skb);
- return -EINVAL;
+ err = -EINVAL;
+ goto err_free;
}
err = __tipc_nl_add_link(net, &msg, link, 0);
tipc_node_read_unlock(node);
- if (err) {
- nlmsg_free(msg.skb);
- return err;
- }
+ if (err)
+ goto err_free;
}
return genlmsg_reply(msg.skb, info);
+
+err_free:
+ nlmsg_free(msg.skb);
+ return err;
}
int tipc_nl_node_reset_link_stats(struct sk_buff *skb, struct genl_info *info)
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index ff9887f..7fe91b1 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -54,7 +54,8 @@
country AR:
(2402 - 2482 @ 40), (36)
- (5170 - 5330 @ 160), (23)
+ (5170 - 5250 @ 80), (23), AUTO-BW
+ (5250 - 5330 @ 80), (36), AUTO-BW
(5490 - 5590 @ 80), (36)
(5650 - 5730 @ 80), (36)
(5735 - 5835 @ 80), (36)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index d8387b1..8f9bd38 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -6701,8 +6701,17 @@
if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
request->flags = nla_get_u32(
info->attrs[NL80211_ATTR_SCAN_FLAGS]);
- if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
- !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
+ if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
+ !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
+ ((request->flags & NL80211_SCAN_FLAG_LOW_SPAN) &&
+ !wiphy_ext_feature_isset(wiphy,
+ NL80211_EXT_FEATURE_LOW_SPAN_SCAN)) ||
+ ((request->flags & NL80211_SCAN_FLAG_LOW_POWER) &&
+ !wiphy_ext_feature_isset(wiphy,
+ NL80211_EXT_FEATURE_LOW_POWER_SCAN)) ||
+ ((request->flags & NL80211_SCAN_FLAG_HIGH_ACCURACY) &&
+ !wiphy_ext_feature_isset(wiphy,
+ NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN))) {
err = -EOPNOTSUPP;
goto out_free;
}
@@ -7585,6 +7594,11 @@
intbss->ts_boottime, NL80211_BSS_PAD))
goto nla_put_failure;
+ if (!nl80211_put_signal(msg, intbss->pub.chains,
+ intbss->pub.chain_signal,
+ NL80211_BSS_CHAIN_SIGNAL))
+ goto nla_put_failure;
+
switch (rdev->wiphy.signal_type) {
case CFG80211_SIGNAL_TYPE_MBM:
if (nla_put_u32(msg, NL80211_BSS_SIGNAL_MBM, res->signal))
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 35ad69f..ad8611b 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -904,6 +904,9 @@
found->ts = tmp->ts;
found->ts_boottime = tmp->ts_boottime;
found->parent_tsf = tmp->parent_tsf;
+ found->pub.chains = tmp->pub.chains;
+ memcpy(found->pub.chain_signal, tmp->pub.chain_signal,
+ IEEE80211_MAX_CHAINS);
ether_addr_copy(found->parent_bssid, tmp->parent_bssid);
} else {
struct cfg80211_internal_bss *new;
@@ -1156,6 +1159,8 @@
tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
tmp.ts_boottime = data->boottime_ns;
tmp.parent_tsf = data->parent_tsf;
+ tmp.pub.chains = data->chains;
+ memcpy(tmp.pub.chain_signal, data->chain_signal, IEEE80211_MAX_CHAINS);
ether_addr_copy(tmp.parent_bssid, data->parent_bssid);
signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py
index 1bf949c..f6ab3cc 100644
--- a/scripts/gdb/linux/tasks.py
+++ b/scripts/gdb/linux/tasks.py
@@ -96,6 +96,8 @@
thread_info_addr = task.address + ia64_task_size
thread_info = thread_info_addr.cast(thread_info_ptr_type)
else:
+ if task.type.fields()[0].type == thread_info_type.get_type():
+ return task['thread_info']
thread_info = task['stack'].cast(thread_info_ptr_type)
return thread_info.dereference()
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index bd83497..845eb9b 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -838,6 +838,7 @@
".cmem*", /* EZchip */
".fmt_slot*", /* EZchip */
".gnu.lto*",
+ ".discard.*",
NULL
};
diff --git a/scripts/module-common.lds b/scripts/module-common.lds
index 53234e8..9b6e246 100644
--- a/scripts/module-common.lds
+++ b/scripts/module-common.lds
@@ -4,7 +4,10 @@
* combine them automatically.
*/
SECTIONS {
- /DISCARD/ : { *(.discard) }
+ /DISCARD/ : {
+ *(.discard)
+ *(.discard.*)
+ }
__ksymtab 0 : { *(SORT(___ksymtab+*)) }
__ksymtab_gpl 0 : { *(SORT(___ksymtab_gpl+*)) }
diff --git a/security/Kconfig b/security/Kconfig
index 4415de2..638afc8 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -50,6 +50,16 @@
bool
default n
+config PAGE_TABLE_ISOLATION
+ bool "Remove the kernel mapping in user mode"
+ default y
+ depends on X86_64 && SMP
+ help
+ This enforces a strict kernel and user space isolation, in order
+ to close hardware side channels on kernel address information.
+
+ If you are unsure how to answer this question, answer Y.
+
config SECURITYFS
bool "Enable the securityfs filesystem"
help
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index e60c79d..52f3c55 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -348,27 +348,26 @@
struct avc_xperms_decision_node *xpd_node;
struct extended_perms_decision *xpd;
- xpd_node = kmem_cache_zalloc(avc_xperms_decision_cachep,
- GFP_ATOMIC | __GFP_NOMEMALLOC);
+ xpd_node = kmem_cache_zalloc(avc_xperms_decision_cachep, GFP_NOWAIT);
if (!xpd_node)
return NULL;
xpd = &xpd_node->xpd;
if (which & XPERMS_ALLOWED) {
xpd->allowed = kmem_cache_zalloc(avc_xperms_data_cachep,
- GFP_ATOMIC | __GFP_NOMEMALLOC);
+ GFP_NOWAIT);
if (!xpd->allowed)
goto error;
}
if (which & XPERMS_AUDITALLOW) {
xpd->auditallow = kmem_cache_zalloc(avc_xperms_data_cachep,
- GFP_ATOMIC | __GFP_NOMEMALLOC);
+ GFP_NOWAIT);
if (!xpd->auditallow)
goto error;
}
if (which & XPERMS_DONTAUDIT) {
xpd->dontaudit = kmem_cache_zalloc(avc_xperms_data_cachep,
- GFP_ATOMIC | __GFP_NOMEMALLOC);
+ GFP_NOWAIT);
if (!xpd->dontaudit)
goto error;
}
@@ -396,8 +395,7 @@
{
struct avc_xperms_node *xp_node;
- xp_node = kmem_cache_zalloc(avc_xperms_cachep,
- GFP_ATOMIC|__GFP_NOMEMALLOC);
+ xp_node = kmem_cache_zalloc(avc_xperms_cachep, GFP_NOWAIT);
if (!xp_node)
return xp_node;
INIT_LIST_HEAD(&xp_node->xpd_head);
@@ -550,7 +548,7 @@
{
struct avc_node *node;
- node = kmem_cache_zalloc(avc_node_cachep, GFP_ATOMIC|__GFP_NOMEMALLOC);
+ node = kmem_cache_zalloc(avc_node_cachep, GFP_NOWAIT);
if (!node)
goto out;
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 082b20c..051ee18 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -78,8 +78,7 @@
static struct sidtab sidtab;
struct policydb policydb;
-int ss_initialized;
-
+int ss_initialized __aligned(0x1000) __attribute__((section(".bss_rtic")));
/*
* The largest sequence number that has been used when
* providing an access decision to the access vector cache.
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index ebc9fdf..3321348 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -466,7 +466,6 @@
v = snd_pcm_hw_param_last(pcm, params, var, dir);
else
v = snd_pcm_hw_param_first(pcm, params, var, dir);
- snd_BUG_ON(v < 0);
return v;
}
@@ -1370,8 +1369,11 @@
if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
return tmp;
- mutex_lock(&runtime->oss.params_lock);
while (bytes > 0) {
+ if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
+ tmp = -ERESTARTSYS;
+ break;
+ }
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
tmp = bytes;
if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes)
@@ -1415,14 +1417,18 @@
xfer += tmp;
if ((substream->f_flags & O_NONBLOCK) != 0 &&
tmp != runtime->oss.period_bytes)
- break;
+ tmp = -EAGAIN;
}
- }
- mutex_unlock(&runtime->oss.params_lock);
- return xfer;
-
err:
- mutex_unlock(&runtime->oss.params_lock);
+ mutex_unlock(&runtime->oss.params_lock);
+ if (tmp < 0)
+ break;
+ if (signal_pending(current)) {
+ tmp = -ERESTARTSYS;
+ break;
+ }
+ tmp = 0;
+ }
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
}
@@ -1470,8 +1476,11 @@
if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
return tmp;
- mutex_lock(&runtime->oss.params_lock);
while (bytes > 0) {
+ if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
+ tmp = -ERESTARTSYS;
+ break;
+ }
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
if (runtime->oss.buffer_used == 0) {
tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
@@ -1502,12 +1511,16 @@
bytes -= tmp;
xfer += tmp;
}
- }
- mutex_unlock(&runtime->oss.params_lock);
- return xfer;
-
err:
- mutex_unlock(&runtime->oss.params_lock);
+ mutex_unlock(&runtime->oss.params_lock);
+ if (tmp < 0)
+ break;
+ if (signal_pending(current)) {
+ tmp = -ERESTARTSYS;
+ break;
+ }
+ tmp = 0;
+ }
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
}
diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c
index 727ac44..a84a1d3 100644
--- a/sound/core/oss/pcm_plugin.c
+++ b/sound/core/oss/pcm_plugin.c
@@ -591,18 +591,26 @@
snd_pcm_sframes_t frames = size;
plugin = snd_pcm_plug_first(plug);
- while (plugin && frames > 0) {
+ while (plugin) {
+ if (frames <= 0)
+ return frames;
if ((next = plugin->next) != NULL) {
snd_pcm_sframes_t frames1 = frames;
- if (plugin->dst_frames)
+ if (plugin->dst_frames) {
frames1 = plugin->dst_frames(plugin, frames);
+ if (frames1 <= 0)
+ return frames1;
+ }
if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) {
return err;
}
if (err != frames1) {
frames = err;
- if (plugin->src_frames)
+ if (plugin->src_frames) {
frames = plugin->src_frames(plugin, frames1);
+ if (frames <= 0)
+ return frames;
+ }
}
} else
dst_channels = NULL;
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 2dfe772..9ccf6a5 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -582,7 +582,6 @@
{
u_int64_t n = (u_int64_t) a * b;
if (c == 0) {
- snd_BUG_ON(!n);
*r = 0;
return UINT_MAX;
}
@@ -1668,7 +1667,7 @@
return changed;
if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params);
- if (snd_BUG_ON(err < 0))
+ if (err < 0)
return err;
}
return snd_pcm_hw_param_value(params, var, dir);
@@ -1715,7 +1714,7 @@
return changed;
if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params);
- if (snd_BUG_ON(err < 0))
+ if (err < 0)
return err;
}
return snd_pcm_hw_param_value(params, var, dir);
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 45ef591..16580a8 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -221,6 +221,7 @@
rwlock_init(&client->ports_lock);
mutex_init(&client->ports_mutex);
INIT_LIST_HEAD(&client->ports_list_head);
+ mutex_init(&client->ioctl_mutex);
/* find free slot in the client table */
spin_lock_irqsave(&clients_lock, flags);
@@ -2127,7 +2128,9 @@
return -EFAULT;
}
+ mutex_lock(&client->ioctl_mutex);
err = handler->func(client, &buf);
+ mutex_unlock(&client->ioctl_mutex);
if (err >= 0) {
/* Some commands includes a bug in 'dir' field. */
if (handler->cmd == SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT ||
diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h
index c661425..0611e1e 100644
--- a/sound/core/seq/seq_clientmgr.h
+++ b/sound/core/seq/seq_clientmgr.h
@@ -61,6 +61,7 @@
struct list_head ports_list_head;
rwlock_t ports_lock;
struct mutex ports_mutex;
+ struct mutex ioctl_mutex;
int convert32; /* convert 32->64bit */
/* output pool */
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index 54f348a..cbd20cb 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -39,6 +39,7 @@
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include <sound/info.h>
#include <sound/initval.h>
@@ -305,19 +306,6 @@
return 0;
}
-static void params_change_substream(struct loopback_pcm *dpcm,
- struct snd_pcm_runtime *runtime)
-{
- struct snd_pcm_runtime *dst_runtime;
-
- if (dpcm == NULL || dpcm->substream == NULL)
- return;
- dst_runtime = dpcm->substream->runtime;
- if (dst_runtime == NULL)
- return;
- dst_runtime->hw = dpcm->cable->hw;
-}
-
static void params_change(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -329,10 +317,6 @@
cable->hw.rate_max = runtime->rate;
cable->hw.channels_min = runtime->channels;
cable->hw.channels_max = runtime->channels;
- params_change_substream(cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
- runtime);
- params_change_substream(cable->streams[SNDRV_PCM_STREAM_CAPTURE],
- runtime);
}
static int loopback_prepare(struct snd_pcm_substream *substream)
@@ -620,26 +604,29 @@
static int rule_format(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
+ struct loopback_pcm *dpcm = rule->private;
+ struct loopback_cable *cable = dpcm->cable;
+ struct snd_mask m;
- struct snd_pcm_hardware *hw = rule->private;
- struct snd_mask *maskp = hw_param_mask(params, rule->var);
-
- maskp->bits[0] &= (u_int32_t)hw->formats;
- maskp->bits[1] &= (u_int32_t)(hw->formats >> 32);
- memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */
- if (! maskp->bits[0] && ! maskp->bits[1])
- return -EINVAL;
- return 0;
+ snd_mask_none(&m);
+ mutex_lock(&dpcm->loopback->cable_lock);
+ m.bits[0] = (u_int32_t)cable->hw.formats;
+ m.bits[1] = (u_int32_t)(cable->hw.formats >> 32);
+ mutex_unlock(&dpcm->loopback->cable_lock);
+ return snd_mask_refine(hw_param_mask(params, rule->var), &m);
}
static int rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- struct snd_pcm_hardware *hw = rule->private;
+ struct loopback_pcm *dpcm = rule->private;
+ struct loopback_cable *cable = dpcm->cable;
struct snd_interval t;
- t.min = hw->rate_min;
- t.max = hw->rate_max;
+ mutex_lock(&dpcm->loopback->cable_lock);
+ t.min = cable->hw.rate_min;
+ t.max = cable->hw.rate_max;
+ mutex_unlock(&dpcm->loopback->cable_lock);
t.openmin = t.openmax = 0;
t.integer = 0;
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
@@ -648,22 +635,44 @@
static int rule_channels(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- struct snd_pcm_hardware *hw = rule->private;
+ struct loopback_pcm *dpcm = rule->private;
+ struct loopback_cable *cable = dpcm->cable;
struct snd_interval t;
- t.min = hw->channels_min;
- t.max = hw->channels_max;
+ mutex_lock(&dpcm->loopback->cable_lock);
+ t.min = cable->hw.channels_min;
+ t.max = cable->hw.channels_max;
+ mutex_unlock(&dpcm->loopback->cable_lock);
t.openmin = t.openmax = 0;
t.integer = 0;
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
}
+static void free_cable(struct snd_pcm_substream *substream)
+{
+ struct loopback *loopback = substream->private_data;
+ int dev = get_cable_index(substream);
+ struct loopback_cable *cable;
+
+ cable = loopback->cables[substream->number][dev];
+ if (!cable)
+ return;
+ if (cable->streams[!substream->stream]) {
+ /* other stream is still alive */
+ cable->streams[substream->stream] = NULL;
+ } else {
+ /* free the cable */
+ loopback->cables[substream->number][dev] = NULL;
+ kfree(cable);
+ }
+}
+
static int loopback_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm;
- struct loopback_cable *cable;
+ struct loopback_cable *cable = NULL;
int err = 0;
int dev = get_cable_index(substream);
@@ -682,7 +691,6 @@
if (!cable) {
cable = kzalloc(sizeof(*cable), GFP_KERNEL);
if (!cable) {
- kfree(dpcm);
err = -ENOMEM;
goto unlock;
}
@@ -700,19 +708,19 @@
/* are cached -> they do not reflect the actual state */
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_FORMAT,
- rule_format, &runtime->hw,
+ rule_format, dpcm,
SNDRV_PCM_HW_PARAM_FORMAT, -1);
if (err < 0)
goto unlock;
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
- rule_rate, &runtime->hw,
+ rule_rate, dpcm,
SNDRV_PCM_HW_PARAM_RATE, -1);
if (err < 0)
goto unlock;
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS,
- rule_channels, &runtime->hw,
+ rule_channels, dpcm,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (err < 0)
goto unlock;
@@ -724,6 +732,10 @@
else
runtime->hw = cable->hw;
unlock:
+ if (err < 0) {
+ free_cable(substream);
+ kfree(dpcm);
+ }
mutex_unlock(&loopback->cable_lock);
return err;
}
@@ -732,20 +744,10 @@
{
struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm = substream->runtime->private_data;
- struct loopback_cable *cable;
- int dev = get_cable_index(substream);
loopback_timer_stop(dpcm);
mutex_lock(&loopback->cable_lock);
- cable = loopback->cables[substream->number][dev];
- if (cable->streams[!substream->stream]) {
- /* other stream is still alive */
- cable->streams[substream->stream] = NULL;
- } else {
- /* free the cable */
- loopback->cables[substream->number][dev] = NULL;
- kfree(cable);
- }
+ free_cable(substream);
mutex_unlock(&loopback->cable_lock);
return 0;
}
diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c
index c9af022..47c3e97 100644
--- a/sound/hda/hdac_i915.c
+++ b/sound/hda/hdac_i915.c
@@ -319,7 +319,7 @@
*/
int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops *aops)
{
- if (WARN_ON(!hdac_acomp))
+ if (!hdac_acomp)
return -ENODEV;
hdac_acomp->audio_ops = aops;
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 80bbadc..d6e079f 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -408,6 +408,7 @@
/*SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),*/
/* codec SSID */
+ SND_PCI_QUIRK(0x106b, 0x0600, "iMac 14,1", CS420X_IMAC27_122),
SND_PCI_QUIRK(0x106b, 0x1c00, "MacBookPro 8,1", CS420X_MBP81),
SND_PCI_QUIRK(0x106b, 0x2000, "iMac 12,2", CS420X_IMAC27_122),
SND_PCI_QUIRK(0x106b, 0x2800, "MacBookPro 10,1", CS420X_MBP101),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index ba40596..71a058f 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -5617,6 +5617,7 @@
SND_PCI_QUIRK(0x1028, 0x075b, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
SND_PCI_QUIRK(0x1028, 0x075d, "Dell AIO", ALC298_FIXUP_SPK_VOLUME),
SND_PCI_QUIRK(0x1028, 0x0798, "Dell Inspiron 17 7000 Gaming", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER),
+ SND_PCI_QUIRK(0x1028, 0x082a, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
@@ -5972,6 +5973,11 @@
{0x1b, 0x01011020},
{0x21, 0x02211010}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60130},
+ {0x14, 0x90170110},
+ {0x1b, 0x01011020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60160},
{0x14, 0x90170120},
{0x21, 0x02211030}),
diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c
index c69e976..f886324 100644
--- a/sound/soc/codecs/da7218.c
+++ b/sound/soc/codecs/da7218.c
@@ -2519,7 +2519,7 @@
}
if (da7218->dev_id == DA7218_DEV_ID) {
- hpldet_np = of_find_node_by_name(np, "da7218_hpldet");
+ hpldet_np = of_get_child_by_name(np, "da7218_hpldet");
if (!hpldet_np)
return pdata;
diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h
index 5acd5b6..f9b6c5a 100644
--- a/sound/soc/codecs/tlv320aic31xx.h
+++ b/sound/soc/codecs/tlv320aic31xx.h
@@ -115,7 +115,7 @@
/* INT2 interrupt control */
#define AIC31XX_INT2CTRL AIC31XX_REG(0, 49)
/* GPIO1 control */
-#define AIC31XX_GPIO1 AIC31XX_REG(0, 50)
+#define AIC31XX_GPIO1 AIC31XX_REG(0, 51)
#define AIC31XX_DACPRB AIC31XX_REG(0, 60)
/* ADC Instruction Set Register */
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index a2104d6..26fd6a6 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -232,7 +232,7 @@
struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev);
struct device_node *twl4030_codec_node = NULL;
- twl4030_codec_node = of_find_node_by_name(codec->dev->parent->of_node,
+ twl4030_codec_node = of_get_child_by_name(codec->dev->parent->of_node,
"codec");
if (!pdata && twl4030_codec_node) {
@@ -241,9 +241,11 @@
GFP_KERNEL);
if (!pdata) {
dev_err(codec->dev, "Can not allocate memory\n");
+ of_node_put(twl4030_codec_node);
return NULL;
}
twl4030_setup_pdata_of(pdata, twl4030_codec_node);
+ of_node_put(twl4030_codec_node);
}
return pdata;
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 757af79..c03c9da 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -1465,7 +1465,7 @@
le64_to_cpu(footer->timestamp));
while (pos < firmware->size &&
- pos - firmware->size > sizeof(*region)) {
+ sizeof(*region) < firmware->size - pos) {
region = (void *)&(firmware->data[pos]);
region_name = "Unknown";
reg = 0;
@@ -1526,8 +1526,8 @@
regions, le32_to_cpu(region->len), offset,
region_name);
- if ((pos + le32_to_cpu(region->len) + sizeof(*region)) >
- firmware->size) {
+ if (le32_to_cpu(region->len) >
+ firmware->size - pos - sizeof(*region)) {
adsp_err(dsp,
"%s.%d: %s region len %d bytes exceeds file length %zu\n",
file, regions, region_name,
@@ -1992,7 +1992,7 @@
blocks = 0;
while (pos < firmware->size &&
- pos - firmware->size > sizeof(*blk)) {
+ sizeof(*blk) < firmware->size - pos) {
blk = (void *)(&firmware->data[pos]);
type = le16_to_cpu(blk->type);
@@ -2066,8 +2066,8 @@
}
if (reg) {
- if ((pos + le32_to_cpu(blk->len) + sizeof(*blk)) >
- firmware->size) {
+ if (le32_to_cpu(blk->len) >
+ firmware->size - pos - sizeof(*blk)) {
adsp_err(dsp,
"%s.%d: %s region len %d bytes exceeds file length %zu\n",
file, blocks, region_name,
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index fde08660..1c03490 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -1467,12 +1467,6 @@
sizeof(fsl_ssi_ac97_dai));
fsl_ac97_data = ssi_private;
-
- ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
- if (ret) {
- dev_err(&pdev->dev, "could not set AC'97 ops\n");
- return ret;
- }
} else {
/* Initialize this copy of the CPU DAI driver structure */
memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
@@ -1583,6 +1577,14 @@
return ret;
}
+ if (fsl_ssi_is_ac97(ssi_private)) {
+ ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "could not set AC'97 ops\n");
+ goto error_ac97_ops;
+ }
+ }
+
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
&ssi_private->cpu_dai_drv, 1);
if (ret) {
@@ -1666,6 +1668,10 @@
fsl_ssi_debugfs_remove(&ssi_private->dbg_stats);
error_asoc_register:
+ if (fsl_ssi_is_ac97(ssi_private))
+ snd_soc_set_ac97_ops(NULL);
+
+error_ac97_ops:
if (ssi_private->soc->imx)
fsl_ssi_imx_clean(pdev, ssi_private);
diff --git a/sound/usb/helper.c b/sound/usb/helper.c
index 7712e2b..4783648 100644
--- a/sound/usb/helper.c
+++ b/sound/usb/helper.c
@@ -122,7 +122,7 @@
case USB_SPEED_SUPER:
case USB_SPEED_SUPER_PLUS:
if (get_endpoint(alts, 0)->bInterval >= 1 &&
- get_endpoint(alts, 0)->bInterval <= 4)
+ get_endpoint(alts, 0)->bInterval <= 16)
return get_endpoint(alts, 0)->bInterval - 1;
break;
default:
diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h
index a396292..f79669a 100644
--- a/tools/arch/x86/include/asm/cpufeatures.h
+++ b/tools/arch/x86/include/asm/cpufeatures.h
@@ -197,6 +197,9 @@
#define X86_FEATURE_AVX512_4VNNIW (7*32+16) /* AVX-512 Neural Network Instructions */
#define X86_FEATURE_AVX512_4FMAPS (7*32+17) /* AVX-512 Multiply Accumulation Single precision */
+/* Because the ALTERNATIVE scheme is for members of the X86_FEATURE club... */
+#define X86_FEATURE_KAISER ( 7*32+31) /* CONFIG_PAGE_TABLE_ISOLATION w/o nokaiser */
+
/* Virtualization flags: Linux defined, word 8 */
#define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */
#define X86_FEATURE_VNMI ( 8*32+ 1) /* Intel Virtual NMI */
diff --git a/tools/gpio/gpio-event-mon.c b/tools/gpio/gpio-event-mon.c
index 1c14c25..4b36323 100644
--- a/tools/gpio/gpio-event-mon.c
+++ b/tools/gpio/gpio-event-mon.c
@@ -23,6 +23,7 @@
#include <getopt.h>
#include <inttypes.h>
#include <sys/ioctl.h>
+#include <sys/types.h>
#include <linux/gpio.h>
int monitor_device(const char *device_name,
diff --git a/tools/objtool/arch/x86/insn/x86-opcode-map.txt b/tools/objtool/arch/x86/insn/x86-opcode-map.txt
index 767be7c..1754e09 100644
--- a/tools/objtool/arch/x86/insn/x86-opcode-map.txt
+++ b/tools/objtool/arch/x86/insn/x86-opcode-map.txt
@@ -896,7 +896,7 @@
GrpTable: Grp3_1
0: TEST Eb,Ib
-1:
+1: TEST Eb,Ib
2: NOT Eb
3: NEG Eb
4: MUL AL,Eb
diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index b8dadb0..a688a85 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -51,7 +51,7 @@
unsigned int len, state;
unsigned char type;
unsigned long immediate;
- bool alt_group, visited;
+ bool alt_group, visited, ignore_alts;
struct symbol *call_dest;
struct instruction *jump_dest;
struct list_head alts;
@@ -353,6 +353,40 @@
}
/*
+ * FIXME: For now, just ignore any alternatives which add retpolines. This is
+ * a temporary hack, as it doesn't allow ORC to unwind from inside a retpoline.
+ * But it at least allows objtool to understand the control flow *around* the
+ * retpoline.
+ */
+static int add_nospec_ignores(struct objtool_file *file)
+{
+ struct section *sec;
+ struct rela *rela;
+ struct instruction *insn;
+
+ sec = find_section_by_name(file->elf, ".rela.discard.nospec");
+ if (!sec)
+ return 0;
+
+ list_for_each_entry(rela, &sec->rela_list, list) {
+ if (rela->sym->type != STT_SECTION) {
+ WARN("unexpected relocation symbol type in %s", sec->name);
+ return -1;
+ }
+
+ insn = find_insn(file, rela->sym->sec, rela->addend);
+ if (!insn) {
+ WARN("bad .discard.nospec entry");
+ return -1;
+ }
+
+ insn->ignore_alts = true;
+ }
+
+ return 0;
+}
+
+/*
* Find the destination instructions for all jumps.
*/
static int add_jump_destinations(struct objtool_file *file)
@@ -382,6 +416,13 @@
} else if (rela->sym->sec->idx) {
dest_sec = rela->sym->sec;
dest_off = rela->sym->sym.st_value + rela->addend + 4;
+ } else if (strstr(rela->sym->name, "_indirect_thunk_")) {
+ /*
+ * Retpoline jumps are really dynamic jumps in
+ * disguise, so convert them accordingly.
+ */
+ insn->type = INSN_JUMP_DYNAMIC;
+ continue;
} else {
/* sibling call */
insn->jump_dest = 0;
@@ -428,11 +469,18 @@
dest_off = insn->offset + insn->len + insn->immediate;
insn->call_dest = find_symbol_by_offset(insn->sec,
dest_off);
+ /*
+ * FIXME: Thanks to retpolines, it's now considered
+ * normal for a function to call within itself. So
+ * disable this warning for now.
+ */
+#if 0
if (!insn->call_dest) {
WARN_FUNC("can't find call dest symbol at offset 0x%lx",
insn->sec, insn->offset, dest_off);
return -1;
}
+#endif
} else if (rela->sym->type == STT_SECTION) {
insn->call_dest = find_symbol_by_offset(rela->sym->sec,
rela->addend+4);
@@ -594,12 +642,6 @@
return ret;
list_for_each_entry_safe(special_alt, tmp, &special_alts, list) {
- alt = malloc(sizeof(*alt));
- if (!alt) {
- WARN("malloc failed");
- ret = -1;
- goto out;
- }
orig_insn = find_insn(file, special_alt->orig_sec,
special_alt->orig_off);
@@ -610,6 +652,10 @@
goto out;
}
+ /* Ignore retpoline alternatives. */
+ if (orig_insn->ignore_alts)
+ continue;
+
new_insn = NULL;
if (!special_alt->group || special_alt->new_len) {
new_insn = find_insn(file, special_alt->new_sec,
@@ -635,6 +681,13 @@
goto out;
}
+ alt = malloc(sizeof(*alt));
+ if (!alt) {
+ WARN("malloc failed");
+ ret = -1;
+ goto out;
+ }
+
alt->insn = new_insn;
list_add_tail(&alt->list, &orig_insn->alts);
@@ -854,6 +907,10 @@
add_ignores(file);
+ ret = add_nospec_ignores(file);
+ if (ret)
+ return ret;
+
ret = add_jump_destinations(file);
if (ret)
return ret;
@@ -1173,6 +1230,14 @@
for_each_insn(file, insn) {
if (!insn->visited && insn->type == INSN_RETURN) {
+
+ /*
+ * Don't warn about call instructions in unvisited
+ * retpoline alternatives.
+ */
+ if (!strcmp(insn->sec->name, ".altinstr_replacement"))
+ continue;
+
WARN_FUNC("return instruction outside of a callable function",
insn->sec, insn->offset);
warnings++;
@@ -1229,7 +1294,7 @@
INIT_LIST_HEAD(&file.insn_list);
hash_init(file.insn_hash);
- file.whitelist = find_section_by_name(file.elf, "__func_stack_frame_non_standard");
+ file.whitelist = find_section_by_name(file.elf, ".discard.func_stack_frame_non_standard");
file.rodata = find_section_by_name(file.elf, ".rodata");
file.ignore_unreachables = false;
file.c_file = find_section_by_name(file.elf, ".comment");
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index d897702..faacf0c 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -26,6 +26,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <errno.h>
#include "elf.h"
#include "warn.h"
@@ -370,7 +371,8 @@
elf->fd = open(name, O_RDONLY);
if (elf->fd == -1) {
- perror("open");
+ fprintf(stderr, "objtool: Can't open '%s': %s\n",
+ name, strerror(errno));
goto err;
}
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index cffdd9c..ff37531 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -19,18 +19,18 @@
include $(srctree)/tools/scripts/Makefile.arch
-$(call detected_var,ARCH)
+$(call detected_var,SRCARCH)
NO_PERF_REGS := 1
# Additional ARCH settings for ppc
-ifeq ($(ARCH),powerpc)
+ifeq ($(SRCARCH),powerpc)
NO_PERF_REGS := 0
LIBUNWIND_LIBS := -lunwind -lunwind-ppc64
endif
# Additional ARCH settings for x86
-ifeq ($(ARCH),x86)
+ifeq ($(SRCARCH),x86)
$(call detected,CONFIG_X86)
ifeq (${IS_64_BIT}, 1)
CFLAGS += -DHAVE_ARCH_X86_64_SUPPORT -DHAVE_SYSCALL_TABLE -I$(OUTPUT)arch/x86/include/generated
@@ -43,12 +43,12 @@
NO_PERF_REGS := 0
endif
-ifeq ($(ARCH),arm)
+ifeq ($(SRCARCH),arm)
NO_PERF_REGS := 0
LIBUNWIND_LIBS = -lunwind -lunwind-arm
endif
-ifeq ($(ARCH),arm64)
+ifeq ($(SRCARCH),arm64)
NO_PERF_REGS := 0
LIBUNWIND_LIBS = -lunwind -lunwind-aarch64
endif
@@ -61,7 +61,7 @@
# Disable it on all other architectures in case libdw unwind
# support is detected in system. Add supported architectures
# to the check.
-ifneq ($(ARCH),$(filter $(ARCH),x86 arm))
+ifneq ($(SRCARCH),$(filter $(SRCARCH),x86 arm))
NO_LIBDW_DWARF_UNWIND := 1
endif
@@ -115,9 +115,9 @@
FEATURE_CHECK_CFLAGS-libbabeltrace := $(LIBBABELTRACE_CFLAGS)
FEATURE_CHECK_LDFLAGS-libbabeltrace := $(LIBBABELTRACE_LDFLAGS) -lbabeltrace-ctf
-FEATURE_CHECK_CFLAGS-bpf = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(ARCH)/include/uapi -I$(srctree)/tools/include/uapi
+FEATURE_CHECK_CFLAGS-bpf = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(SRCARCH)/include/uapi -I$(srctree)/tools/include/uapi
# include ARCH specific config
--include $(src-perf)/arch/$(ARCH)/Makefile
+-include $(src-perf)/arch/$(SRCARCH)/Makefile
ifdef PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET
CFLAGS += -DHAVE_ARCH_REGS_QUERY_REGISTER_OFFSET
@@ -205,12 +205,12 @@
endif
CFLAGS += -I$(src-perf)/util/include
-CFLAGS += -I$(src-perf)/arch/$(ARCH)/include
+CFLAGS += -I$(src-perf)/arch/$(SRCARCH)/include
CFLAGS += -I$(srctree)/tools/include/uapi
CFLAGS += -I$(srctree)/tools/include/
-CFLAGS += -I$(srctree)/tools/arch/$(ARCH)/include/uapi
-CFLAGS += -I$(srctree)/tools/arch/$(ARCH)/include/
-CFLAGS += -I$(srctree)/tools/arch/$(ARCH)/
+CFLAGS += -I$(srctree)/tools/arch/$(SRCARCH)/include/uapi
+CFLAGS += -I$(srctree)/tools/arch/$(SRCARCH)/include/
+CFLAGS += -I$(srctree)/tools/arch/$(SRCARCH)/
# $(obj-perf) for generated common-cmds.h
# $(obj-perf)/util for generated bison/flex headers
@@ -321,7 +321,7 @@
ifndef NO_DWARF
ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined)
- msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled);
+ msg := $(warning DWARF register mappings have not been defined for architecture $(SRCARCH), DWARF support disabled);
NO_DWARF := 1
else
CFLAGS += -DHAVE_DWARF_SUPPORT $(LIBDW_CFLAGS)
@@ -346,7 +346,7 @@
CFLAGS += -DHAVE_BPF_PROLOGUE
$(call detected,CONFIG_BPF_PROLOGUE)
else
- msg := $(warning BPF prologue is not supported by architecture $(ARCH), missing regs_query_register_offset());
+ msg := $(warning BPF prologue is not supported by architecture $(SRCARCH), missing regs_query_register_offset());
endif
else
msg := $(warning DWARF support is off, BPF prologue is disabled);
@@ -372,7 +372,7 @@
endif
endif
-ifeq ($(ARCH),powerpc)
+ifeq ($(SRCARCH),powerpc)
ifndef NO_DWARF
CFLAGS += -DHAVE_SKIP_CALLCHAIN_IDX
endif
@@ -453,7 +453,7 @@
endif
ifndef NO_LOCAL_LIBUNWIND
- ifeq ($(ARCH),$(filter $(ARCH),arm arm64))
+ ifeq ($(SRCARCH),$(filter $(SRCARCH),arm arm64))
$(call feature_check,libunwind-debug-frame)
ifneq ($(feature-libunwind-debug-frame), 1)
msg := $(warning No debug_frame support found in libunwind);
@@ -717,7 +717,7 @@
NO_PERF_READ_VDSO32 := 1
endif
endif
- ifneq ($(ARCH), x86)
+ ifneq ($(SRCARCH), x86)
NO_PERF_READ_VDSOX32 := 1
endif
ifndef NO_PERF_READ_VDSOX32
@@ -746,7 +746,7 @@
endif
ifndef NO_AUXTRACE
- ifeq ($(ARCH),x86)
+ ifeq ($(SRCARCH),x86)
ifeq ($(feature-get_cpuid), 0)
msg := $(warning Your gcc lacks the __get_cpuid() builtin, disables support for auxtrace/Intel PT, please install a newer gcc);
NO_AUXTRACE := 1
@@ -793,7 +793,7 @@
ETC_PERFCONFIG = etc/perfconfig
endif
ifndef lib
-ifeq ($(ARCH)$(IS_64_BIT), x861)
+ifeq ($(SRCARCH)$(IS_64_BIT), x861)
lib = lib64
else
lib = lib
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index ef52d1e..2b92ffe 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -192,7 +192,7 @@
ifeq ($(config),0)
include $(srctree)/tools/scripts/Makefile.arch
--include arch/$(ARCH)/Makefile
+-include arch/$(SRCARCH)/Makefile
endif
# The FEATURE_DUMP_EXPORT holds location of the actual
diff --git a/tools/perf/arch/Build b/tools/perf/arch/Build
index 109eb75..d9b6af8 100644
--- a/tools/perf/arch/Build
+++ b/tools/perf/arch/Build
@@ -1,2 +1,2 @@
libperf-y += common.o
-libperf-y += $(ARCH)/
+libperf-y += $(SRCARCH)/
diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build
index 9213a12..999a4e8 100644
--- a/tools/perf/pmu-events/Build
+++ b/tools/perf/pmu-events/Build
@@ -2,7 +2,7 @@
jevents-y += json.o jsmn.o jevents.o
pmu-events-y += pmu-events.o
-JDIR = pmu-events/arch/$(ARCH)
+JDIR = pmu-events/arch/$(SRCARCH)
JSON = $(shell [ -d $(JDIR) ] && \
find $(JDIR) -name '*.json' -o -name 'mapfile.csv')
#
@@ -10,4 +10,4 @@
# directory and create tables in pmu-events.c.
#
$(OUTPUT)pmu-events/pmu-events.c: $(JSON) $(JEVENTS)
- $(Q)$(call echo-cmd,gen)$(JEVENTS) $(ARCH) pmu-events/arch $(OUTPUT)pmu-events/pmu-events.c $(V)
+ $(Q)$(call echo-cmd,gen)$(JEVENTS) $(SRCARCH) pmu-events/arch $(OUTPUT)pmu-events/pmu-events.c $(V)
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 8a4ce49..546250a 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -71,7 +71,7 @@
$(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
$(Q)echo ';' >> $@
-ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64 powerpc))
+ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc))
perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
endif
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 5337f49..28bdb48 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -826,7 +826,7 @@
/*
* default get_cpuid(): nothing gets recorded
- * actual implementation must be in arch/$(ARCH)/util/header.c
+ * actual implementation must be in arch/$(SRCARCH)/util/header.c
*/
int __weak get_cpuid(char *buffer __maybe_unused, size_t sz __maybe_unused)
{
diff --git a/tools/power/cpupower/bench/system.c b/tools/power/cpupower/bench/system.c
index c25a74a..2bb3eef 100644
--- a/tools/power/cpupower/bench/system.c
+++ b/tools/power/cpupower/bench/system.c
@@ -61,7 +61,7 @@
dprintf("set %s as cpufreq governor\n", governor);
- if (cpupower_is_cpu_online(cpu) != 0) {
+ if (cpupower_is_cpu_online(cpu) != 1) {
perror("cpufreq_cpu_exists");
fprintf(stderr, "error: cpu %u does not exist\n", cpu);
return -1;
diff --git a/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c b/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c
index 1b5da00..5b3205f 100644
--- a/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c
+++ b/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c
@@ -130,15 +130,18 @@
{
int num;
char *tmp;
+ int this_cpu;
+
+ this_cpu = sched_getcpu();
/* Assume idle state count is the same for all CPUs */
- cpuidle_sysfs_monitor.hw_states_num = cpuidle_state_count(0);
+ cpuidle_sysfs_monitor.hw_states_num = cpuidle_state_count(this_cpu);
if (cpuidle_sysfs_monitor.hw_states_num <= 0)
return NULL;
for (num = 0; num < cpuidle_sysfs_monitor.hw_states_num; num++) {
- tmp = cpuidle_state_name(0, num);
+ tmp = cpuidle_state_name(this_cpu, num);
if (tmp == NULL)
continue;
@@ -146,7 +149,7 @@
strncpy(cpuidle_cstates[num].name, tmp, CSTATE_NAME_LEN - 1);
free(tmp);
- tmp = cpuidle_state_desc(0, num);
+ tmp = cpuidle_state_desc(this_cpu, num);
if (tmp == NULL)
continue;
strncpy(cpuidle_cstates[num].desc, tmp, CSTATE_DESC_LEN - 1);
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 6300c1a..4af37bf 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -6,7 +6,7 @@
TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_syscall test_mremap_vdso \
check_initial_reg_state sigreturn ldt_gdt iopl mpx-mini-test \
- protection_keys
+ protection_keys test_vsyscall
TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \
test_FCMOV test_FCOMI test_FISTTP \
vdso_restorer
diff --git a/tools/testing/selftests/x86/test_vsyscall.c b/tools/testing/selftests/x86/test_vsyscall.c
new file mode 100644
index 0000000..6e0bd52
--- /dev/null
+++ b/tools/testing/selftests/x86/test_vsyscall.c
@@ -0,0 +1,500 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <sys/ucontext.h>
+#include <errno.h>
+#include <err.h>
+#include <sched.h>
+#include <stdbool.h>
+#include <setjmp.h>
+
+#ifdef __x86_64__
+# define VSYS(x) (x)
+#else
+# define VSYS(x) 0
+#endif
+
+#ifndef SYS_getcpu
+# ifdef __x86_64__
+# define SYS_getcpu 309
+# else
+# define SYS_getcpu 318
+# endif
+#endif
+
+static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
+ int flags)
+{
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = handler;
+ sa.sa_flags = SA_SIGINFO | flags;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(sig, &sa, 0))
+ err(1, "sigaction");
+}
+
+/* vsyscalls and vDSO */
+bool should_read_vsyscall = false;
+
+typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz);
+gtod_t vgtod = (gtod_t)VSYS(0xffffffffff600000);
+gtod_t vdso_gtod;
+
+typedef int (*vgettime_t)(clockid_t, struct timespec *);
+vgettime_t vdso_gettime;
+
+typedef long (*time_func_t)(time_t *t);
+time_func_t vtime = (time_func_t)VSYS(0xffffffffff600400);
+time_func_t vdso_time;
+
+typedef long (*getcpu_t)(unsigned *, unsigned *, void *);
+getcpu_t vgetcpu = (getcpu_t)VSYS(0xffffffffff600800);
+getcpu_t vdso_getcpu;
+
+static void init_vdso(void)
+{
+ void *vdso = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
+ if (!vdso)
+ vdso = dlopen("linux-gate.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
+ if (!vdso) {
+ printf("[WARN]\tfailed to find vDSO\n");
+ return;
+ }
+
+ vdso_gtod = (gtod_t)dlsym(vdso, "__vdso_gettimeofday");
+ if (!vdso_gtod)
+ printf("[WARN]\tfailed to find gettimeofday in vDSO\n");
+
+ vdso_gettime = (vgettime_t)dlsym(vdso, "__vdso_clock_gettime");
+ if (!vdso_gettime)
+ printf("[WARN]\tfailed to find clock_gettime in vDSO\n");
+
+ vdso_time = (time_func_t)dlsym(vdso, "__vdso_time");
+ if (!vdso_time)
+ printf("[WARN]\tfailed to find time in vDSO\n");
+
+ vdso_getcpu = (getcpu_t)dlsym(vdso, "__vdso_getcpu");
+ if (!vdso_getcpu) {
+ /* getcpu() was never wired up in the 32-bit vDSO. */
+ printf("[%s]\tfailed to find getcpu in vDSO\n",
+ sizeof(long) == 8 ? "WARN" : "NOTE");
+ }
+}
+
+static int init_vsys(void)
+{
+#ifdef __x86_64__
+ int nerrs = 0;
+ FILE *maps;
+ char line[128];
+ bool found = false;
+
+ maps = fopen("/proc/self/maps", "r");
+ if (!maps) {
+ printf("[WARN]\tCould not open /proc/self/maps -- assuming vsyscall is r-x\n");
+ should_read_vsyscall = true;
+ return 0;
+ }
+
+ while (fgets(line, sizeof(line), maps)) {
+ char r, x;
+ void *start, *end;
+ char name[128];
+ if (sscanf(line, "%p-%p %c-%cp %*x %*x:%*x %*u %s",
+ &start, &end, &r, &x, name) != 5)
+ continue;
+
+ if (strcmp(name, "[vsyscall]"))
+ continue;
+
+ printf("\tvsyscall map: %s", line);
+
+ if (start != (void *)0xffffffffff600000 ||
+ end != (void *)0xffffffffff601000) {
+ printf("[FAIL]\taddress range is nonsense\n");
+ nerrs++;
+ }
+
+ printf("\tvsyscall permissions are %c-%c\n", r, x);
+ should_read_vsyscall = (r == 'r');
+ if (x != 'x') {
+ vgtod = NULL;
+ vtime = NULL;
+ vgetcpu = NULL;
+ }
+
+ found = true;
+ break;
+ }
+
+ fclose(maps);
+
+ if (!found) {
+ printf("\tno vsyscall map in /proc/self/maps\n");
+ should_read_vsyscall = false;
+ vgtod = NULL;
+ vtime = NULL;
+ vgetcpu = NULL;
+ }
+
+ return nerrs;
+#else
+ return 0;
+#endif
+}
+
+/* syscalls */
+static inline long sys_gtod(struct timeval *tv, struct timezone *tz)
+{
+ return syscall(SYS_gettimeofday, tv, tz);
+}
+
+static inline int sys_clock_gettime(clockid_t id, struct timespec *ts)
+{
+ return syscall(SYS_clock_gettime, id, ts);
+}
+
+static inline long sys_time(time_t *t)
+{
+ return syscall(SYS_time, t);
+}
+
+static inline long sys_getcpu(unsigned * cpu, unsigned * node,
+ void* cache)
+{
+ return syscall(SYS_getcpu, cpu, node, cache);
+}
+
+static jmp_buf jmpbuf;
+
+static void sigsegv(int sig, siginfo_t *info, void *ctx_void)
+{
+ siglongjmp(jmpbuf, 1);
+}
+
+static double tv_diff(const struct timeval *a, const struct timeval *b)
+{
+ return (double)(a->tv_sec - b->tv_sec) +
+ (double)((int)a->tv_usec - (int)b->tv_usec) * 1e-6;
+}
+
+static int check_gtod(const struct timeval *tv_sys1,
+ const struct timeval *tv_sys2,
+ const struct timezone *tz_sys,
+ const char *which,
+ const struct timeval *tv_other,
+ const struct timezone *tz_other)
+{
+ int nerrs = 0;
+ double d1, d2;
+
+ if (tz_other && (tz_sys->tz_minuteswest != tz_other->tz_minuteswest || tz_sys->tz_dsttime != tz_other->tz_dsttime)) {
+ printf("[FAIL] %s tz mismatch\n", which);
+ nerrs++;
+ }
+
+ d1 = tv_diff(tv_other, tv_sys1);
+ d2 = tv_diff(tv_sys2, tv_other);
+ printf("\t%s time offsets: %lf %lf\n", which, d1, d2);
+
+ if (d1 < 0 || d2 < 0) {
+ printf("[FAIL]\t%s time was inconsistent with the syscall\n", which);
+ nerrs++;
+ } else {
+ printf("[OK]\t%s gettimeofday()'s timeval was okay\n", which);
+ }
+
+ return nerrs;
+}
+
+static int test_gtod(void)
+{
+ struct timeval tv_sys1, tv_sys2, tv_vdso, tv_vsys;
+ struct timezone tz_sys, tz_vdso, tz_vsys;
+ long ret_vdso = -1;
+ long ret_vsys = -1;
+ int nerrs = 0;
+
+ printf("[RUN]\ttest gettimeofday()\n");
+
+ if (sys_gtod(&tv_sys1, &tz_sys) != 0)
+ err(1, "syscall gettimeofday");
+ if (vdso_gtod)
+ ret_vdso = vdso_gtod(&tv_vdso, &tz_vdso);
+ if (vgtod)
+ ret_vsys = vgtod(&tv_vsys, &tz_vsys);
+ if (sys_gtod(&tv_sys2, &tz_sys) != 0)
+ err(1, "syscall gettimeofday");
+
+ if (vdso_gtod) {
+ if (ret_vdso == 0) {
+ nerrs += check_gtod(&tv_sys1, &tv_sys2, &tz_sys, "vDSO", &tv_vdso, &tz_vdso);
+ } else {
+ printf("[FAIL]\tvDSO gettimeofday() failed: %ld\n", ret_vdso);
+ nerrs++;
+ }
+ }
+
+ if (vgtod) {
+ if (ret_vsys == 0) {
+ nerrs += check_gtod(&tv_sys1, &tv_sys2, &tz_sys, "vsyscall", &tv_vsys, &tz_vsys);
+ } else {
+ printf("[FAIL]\tvsys gettimeofday() failed: %ld\n", ret_vsys);
+ nerrs++;
+ }
+ }
+
+ return nerrs;
+}
+
+static int test_time(void) {
+ int nerrs = 0;
+
+ printf("[RUN]\ttest time()\n");
+ long t_sys1, t_sys2, t_vdso = 0, t_vsys = 0;
+ long t2_sys1 = -1, t2_sys2 = -1, t2_vdso = -1, t2_vsys = -1;
+ t_sys1 = sys_time(&t2_sys1);
+ if (vdso_time)
+ t_vdso = vdso_time(&t2_vdso);
+ if (vtime)
+ t_vsys = vtime(&t2_vsys);
+ t_sys2 = sys_time(&t2_sys2);
+ if (t_sys1 < 0 || t_sys1 != t2_sys1 || t_sys2 < 0 || t_sys2 != t2_sys2) {
+ printf("[FAIL]\tsyscall failed (ret1:%ld output1:%ld ret2:%ld output2:%ld)\n", t_sys1, t2_sys1, t_sys2, t2_sys2);
+ nerrs++;
+ return nerrs;
+ }
+
+ if (vdso_time) {
+ if (t_vdso < 0 || t_vdso != t2_vdso) {
+ printf("[FAIL]\tvDSO failed (ret:%ld output:%ld)\n", t_vdso, t2_vdso);
+ nerrs++;
+ } else if (t_vdso < t_sys1 || t_vdso > t_sys2) {
+ printf("[FAIL]\tvDSO returned the wrong time (%ld %ld %ld)\n", t_sys1, t_vdso, t_sys2);
+ nerrs++;
+ } else {
+ printf("[OK]\tvDSO time() is okay\n");
+ }
+ }
+
+ if (vtime) {
+ if (t_vsys < 0 || t_vsys != t2_vsys) {
+ printf("[FAIL]\tvsyscall failed (ret:%ld output:%ld)\n", t_vsys, t2_vsys);
+ nerrs++;
+ } else if (t_vsys < t_sys1 || t_vsys > t_sys2) {
+ printf("[FAIL]\tvsyscall returned the wrong time (%ld %ld %ld)\n", t_sys1, t_vsys, t_sys2);
+ nerrs++;
+ } else {
+ printf("[OK]\tvsyscall time() is okay\n");
+ }
+ }
+
+ return nerrs;
+}
+
+static int test_getcpu(int cpu)
+{
+ int nerrs = 0;
+ long ret_sys, ret_vdso = -1, ret_vsys = -1;
+
+ printf("[RUN]\tgetcpu() on CPU %d\n", cpu);
+
+ cpu_set_t cpuset;
+ CPU_ZERO(&cpuset);
+ CPU_SET(cpu, &cpuset);
+ if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) {
+ printf("[SKIP]\tfailed to force CPU %d\n", cpu);
+ return nerrs;
+ }
+
+ unsigned cpu_sys, cpu_vdso, cpu_vsys, node_sys, node_vdso, node_vsys;
+ unsigned node = 0;
+ bool have_node = false;
+ ret_sys = sys_getcpu(&cpu_sys, &node_sys, 0);
+ if (vdso_getcpu)
+ ret_vdso = vdso_getcpu(&cpu_vdso, &node_vdso, 0);
+ if (vgetcpu)
+ ret_vsys = vgetcpu(&cpu_vsys, &node_vsys, 0);
+
+ if (ret_sys == 0) {
+ if (cpu_sys != cpu) {
+ printf("[FAIL]\tsyscall reported CPU %hu but should be %d\n", cpu_sys, cpu);
+ nerrs++;
+ }
+
+ have_node = true;
+ node = node_sys;
+ }
+
+ if (vdso_getcpu) {
+ if (ret_vdso) {
+ printf("[FAIL]\tvDSO getcpu() failed\n");
+ nerrs++;
+ } else {
+ if (!have_node) {
+ have_node = true;
+ node = node_vdso;
+ }
+
+ if (cpu_vdso != cpu) {
+ printf("[FAIL]\tvDSO reported CPU %hu but should be %d\n", cpu_vdso, cpu);
+ nerrs++;
+ } else {
+ printf("[OK]\tvDSO reported correct CPU\n");
+ }
+
+ if (node_vdso != node) {
+ printf("[FAIL]\tvDSO reported node %hu but should be %hu\n", node_vdso, node);
+ nerrs++;
+ } else {
+ printf("[OK]\tvDSO reported correct node\n");
+ }
+ }
+ }
+
+ if (vgetcpu) {
+ if (ret_vsys) {
+ printf("[FAIL]\tvsyscall getcpu() failed\n");
+ nerrs++;
+ } else {
+ if (!have_node) {
+ have_node = true;
+ node = node_vsys;
+ }
+
+ if (cpu_vsys != cpu) {
+ printf("[FAIL]\tvsyscall reported CPU %hu but should be %d\n", cpu_vsys, cpu);
+ nerrs++;
+ } else {
+ printf("[OK]\tvsyscall reported correct CPU\n");
+ }
+
+ if (node_vsys != node) {
+ printf("[FAIL]\tvsyscall reported node %hu but should be %hu\n", node_vsys, node);
+ nerrs++;
+ } else {
+ printf("[OK]\tvsyscall reported correct node\n");
+ }
+ }
+ }
+
+ return nerrs;
+}
+
+static int test_vsys_r(void)
+{
+#ifdef __x86_64__
+ printf("[RUN]\tChecking read access to the vsyscall page\n");
+ bool can_read;
+ if (sigsetjmp(jmpbuf, 1) == 0) {
+ *(volatile int *)0xffffffffff600000;
+ can_read = true;
+ } else {
+ can_read = false;
+ }
+
+ if (can_read && !should_read_vsyscall) {
+ printf("[FAIL]\tWe have read access, but we shouldn't\n");
+ return 1;
+ } else if (!can_read && should_read_vsyscall) {
+ printf("[FAIL]\tWe don't have read access, but we should\n");
+ return 1;
+ } else {
+ printf("[OK]\tgot expected result\n");
+ }
+#endif
+
+ return 0;
+}
+
+
+#ifdef __x86_64__
+#define X86_EFLAGS_TF (1UL << 8)
+static volatile sig_atomic_t num_vsyscall_traps;
+
+static unsigned long get_eflags(void)
+{
+ unsigned long eflags;
+ asm volatile ("pushfq\n\tpopq %0" : "=rm" (eflags));
+ return eflags;
+}
+
+static void set_eflags(unsigned long eflags)
+{
+ asm volatile ("pushq %0\n\tpopfq" : : "rm" (eflags) : "flags");
+}
+
+static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
+{
+ ucontext_t *ctx = (ucontext_t *)ctx_void;
+ unsigned long ip = ctx->uc_mcontext.gregs[REG_RIP];
+
+ if (((ip ^ 0xffffffffff600000UL) & ~0xfffUL) == 0)
+ num_vsyscall_traps++;
+}
+
+static int test_native_vsyscall(void)
+{
+ time_t tmp;
+ bool is_native;
+
+ if (!vtime)
+ return 0;
+
+ printf("[RUN]\tchecking for native vsyscall\n");
+ sethandler(SIGTRAP, sigtrap, 0);
+ set_eflags(get_eflags() | X86_EFLAGS_TF);
+ vtime(&tmp);
+ set_eflags(get_eflags() & ~X86_EFLAGS_TF);
+
+ /*
+ * If vsyscalls are emulated, we expect a single trap in the
+ * vsyscall page -- the call instruction will trap with RIP
+ * pointing to the entry point before emulation takes over.
+ * In native mode, we expect two traps, since whatever code
+ * the vsyscall page contains will be more than just a ret
+ * instruction.
+ */
+ is_native = (num_vsyscall_traps > 1);
+
+ printf("\tvsyscalls are %s (%d instructions in vsyscall page)\n",
+ (is_native ? "native" : "emulated"),
+ (int)num_vsyscall_traps);
+
+ return 0;
+}
+#endif
+
+int main(int argc, char **argv)
+{
+ int nerrs = 0;
+
+ init_vdso();
+ nerrs += init_vsys();
+
+ nerrs += test_gtod();
+ nerrs += test_time();
+ nerrs += test_getcpu(0);
+ nerrs += test_getcpu(1);
+
+ sethandler(SIGSEGV, sigsegv, 0);
+ nerrs += test_vsys_r();
+
+#ifdef __x86_64__
+ nerrs += test_native_vsyscall();
+#endif
+
+ return nerrs ? 1 : 0;
+}
diff --git a/tools/usb/usbip/libsrc/usbip_common.c b/tools/usb/usbip/libsrc/usbip_common.c
index ac73710..1517a23 100644
--- a/tools/usb/usbip/libsrc/usbip_common.c
+++ b/tools/usb/usbip/libsrc/usbip_common.c
@@ -215,9 +215,16 @@
struct usbip_usb_interface *uinf)
{
char busid[SYSFS_BUS_ID_SIZE];
+ int size;
struct udev_device *sif;
- sprintf(busid, "%s:%d.%d", udev->busid, udev->bConfigurationValue, i);
+ size = snprintf(busid, sizeof(busid), "%s:%d.%d",
+ udev->busid, udev->bConfigurationValue, i);
+ if (size < 0 || (unsigned int)size >= sizeof(busid)) {
+ err("busid length %i >= %lu or < 0", size,
+ (long unsigned)sizeof(busid));
+ return -1;
+ }
sif = udev_device_new_from_subsystem_sysname(udev_context, "usb", busid);
if (!sif) {
diff --git a/tools/usb/usbip/libsrc/usbip_host_common.c b/tools/usb/usbip/libsrc/usbip_host_common.c
index 9d41522..6ff7b60 100644
--- a/tools/usb/usbip/libsrc/usbip_host_common.c
+++ b/tools/usb/usbip/libsrc/usbip_host_common.c
@@ -40,13 +40,20 @@
static int32_t read_attr_usbip_status(struct usbip_usb_device *udev)
{
char status_attr_path[SYSFS_PATH_MAX];
+ int size;
int fd;
int length;
char status;
int value = 0;
- snprintf(status_attr_path, SYSFS_PATH_MAX, "%s/usbip_status",
- udev->path);
+ size = snprintf(status_attr_path, sizeof(status_attr_path),
+ "%s/usbip_status", udev->path);
+ if (size < 0 || (unsigned int)size >= sizeof(status_attr_path)) {
+ err("usbip_status path length %i >= %lu or < 0", size,
+ (long unsigned)sizeof(status_attr_path));
+ return -1;
+ }
+
fd = open(status_attr_path, O_RDONLY);
if (fd < 0) {
@@ -218,6 +225,7 @@
{
char attr_name[] = "usbip_sockfd";
char sockfd_attr_path[SYSFS_PATH_MAX];
+ int size;
char sockfd_buff[30];
int ret;
@@ -237,10 +245,20 @@
}
/* only the first interface is true */
- snprintf(sockfd_attr_path, sizeof(sockfd_attr_path), "%s/%s",
- edev->udev.path, attr_name);
+ size = snprintf(sockfd_attr_path, sizeof(sockfd_attr_path), "%s/%s",
+ edev->udev.path, attr_name);
+ if (size < 0 || (unsigned int)size >= sizeof(sockfd_attr_path)) {
+ err("exported device path length %i >= %lu or < 0", size,
+ (long unsigned)sizeof(sockfd_attr_path));
+ return -1;
+ }
- snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd);
+ size = snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd);
+ if (size < 0 || (unsigned int)size >= sizeof(sockfd_buff)) {
+ err("socket length %i >= %lu or < 0", size,
+ (long unsigned)sizeof(sockfd_buff));
+ return -1;
+ }
ret = write_sysfs_attribute(sockfd_attr_path, sockfd_buff,
strlen(sockfd_buff));
diff --git a/tools/usb/usbip/libsrc/vhci_driver.c b/tools/usb/usbip/libsrc/vhci_driver.c
index ad92047..1274f32 100644
--- a/tools/usb/usbip/libsrc/vhci_driver.c
+++ b/tools/usb/usbip/libsrc/vhci_driver.c
@@ -55,12 +55,12 @@
while (*c != '\0') {
int port, status, speed, devid;
- unsigned long socket;
+ int sockfd;
char lbusid[SYSFS_BUS_ID_SIZE];
- ret = sscanf(c, "%d %d %d %x %lx %31s\n",
+ ret = sscanf(c, "%d %d %d %x %u %31s\n",
&port, &status, &speed,
- &devid, &socket, lbusid);
+ &devid, &sockfd, lbusid);
if (ret < 5) {
dbg("sscanf failed: %d", ret);
@@ -69,7 +69,7 @@
dbg("port %d status %d speed %d devid %x",
port, status, speed, devid);
- dbg("socket %lx lbusid %s", socket, lbusid);
+ dbg("sockfd %u lbusid %s", sockfd, lbusid);
/* if a device is connected, look at it */
diff --git a/tools/usb/usbip/src/usbip.c b/tools/usb/usbip/src/usbip.c
index d7599d9..73d8eee 100644
--- a/tools/usb/usbip/src/usbip.c
+++ b/tools/usb/usbip/src/usbip.c
@@ -176,6 +176,8 @@
break;
case '?':
printf("usbip: invalid option\n");
+ /* Terminate after printing error */
+ /* FALLTHRU */
default:
usbip_usage();
goto out;
diff --git a/tools/usb/usbip/src/usbip_bind.c b/tools/usb/usbip/src/usbip_bind.c
index fa46141..e121cfb 100644
--- a/tools/usb/usbip/src/usbip_bind.c
+++ b/tools/usb/usbip/src/usbip_bind.c
@@ -144,6 +144,7 @@
int rc;
struct udev *udev;
struct udev_device *dev;
+ const char *devpath;
/* Check whether the device with this bus ID exists. */
udev = udev_new();
@@ -152,8 +153,16 @@
err("device with the specified bus ID does not exist");
return -1;
}
+ devpath = udev_device_get_devpath(dev);
udev_unref(udev);
+ /* If the device is already attached to vhci_hcd - bail out */
+ if (strstr(devpath, USBIP_VHCI_DRV_NAME)) {
+ err("bind loop detected: device: %s is attached to %s\n",
+ devpath, USBIP_VHCI_DRV_NAME);
+ return -1;
+ }
+
rc = unbind_other(busid);
if (rc == UNBIND_ST_FAILED) {
err("could not unbind driver from device on busid %s", busid);
diff --git a/tools/usb/usbip/src/usbip_list.c b/tools/usb/usbip/src/usbip_list.c
index f1b38e8..d65a9f4 100644
--- a/tools/usb/usbip/src/usbip_list.c
+++ b/tools/usb/usbip/src/usbip_list.c
@@ -187,6 +187,7 @@
const char *busid;
char product_name[128];
int ret = -1;
+ const char *devpath;
/* Create libudev context. */
udev = udev_new();
@@ -209,6 +210,14 @@
path = udev_list_entry_get_name(dev_list_entry);
dev = udev_device_new_from_syspath(udev, path);
+ /* Ignore devices attached to vhci_hcd */
+ devpath = udev_device_get_devpath(dev);
+ if (strstr(devpath, USBIP_VHCI_DRV_NAME)) {
+ dbg("Skip the device %s already attached to %s\n",
+ devpath, USBIP_VHCI_DRV_NAME);
+ continue;
+ }
+
/* Get device information. */
idVendor = udev_device_get_sysattr_value(dev, "idVendor");
idProduct = udev_device_get_sysattr_value(dev, "idProduct");
diff --git a/tools/usb/usbip/src/utils.c b/tools/usb/usbip/src/utils.c
index 2b3d6d2..3d7b42e 100644
--- a/tools/usb/usbip/src/utils.c
+++ b/tools/usb/usbip/src/utils.c
@@ -30,6 +30,7 @@
char command[SYSFS_BUS_ID_SIZE + 4];
char match_busid_attr_path[SYSFS_PATH_MAX];
int rc;
+ int cmd_size;
snprintf(match_busid_attr_path, sizeof(match_busid_attr_path),
"%s/%s/%s/%s/%s/%s", SYSFS_MNT_PATH, SYSFS_BUS_NAME,
@@ -37,12 +38,14 @@
attr_name);
if (add)
- snprintf(command, SYSFS_BUS_ID_SIZE + 4, "add %s", busid);
+ cmd_size = snprintf(command, SYSFS_BUS_ID_SIZE + 4, "add %s",
+ busid);
else
- snprintf(command, SYSFS_BUS_ID_SIZE + 4, "del %s", busid);
+ cmd_size = snprintf(command, SYSFS_BUS_ID_SIZE + 4, "del %s",
+ busid);
rc = write_sysfs_attribute(match_busid_attr_path, command,
- sizeof(command));
+ cmd_size);
if (rc < 0) {
dbg("failed to write match_busid: %s", strerror(errno));
return -1;