Merge "Merge commit '7e23719341aaa839409af745b97379f6cc43c20f' into msm-4.9_UPSTREAM_0208_PC96"
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/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index 13d50f8..e507c4e 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -791,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"
diff --git a/arch/arm/configs/msm8953-perf_defconfig b/arch/arm/configs/msm8953-perf_defconfig
index b710528..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
diff --git a/arch/arm/configs/msm8953_defconfig b/arch/arm/configs/msm8953_defconfig
index 3371892..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
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index a515e00..29e6335 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -335,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
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
index ba1acda..865406f 100644
--- a/arch/arm/configs/sdxpoorwills_defconfig
+++ b/arch/arm/configs/sdxpoorwills_defconfig
@@ -334,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
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-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-pmi8950.dtsi b/arch/arm64/boot/dts/qcom/msm8953-pmi8950.dtsi
index 944868b..139ef1e 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-pmi8950.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi8950.dtsi
@@ -37,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.dtsi b/arch/arm64/boot/dts/qcom/msm8953.dtsi
index 1f651fd..38e4804 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>,
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-8996-util.c b/drivers/clk/msm/mdss/mdss-dsi-pll-8996-util.c
index f9a4f0e..c5d12e5 100644
--- a/drivers/clk/msm/mdss/mdss-dsi-pll-8996-util.c
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-8996-util.c
@@ -53,52 +53,6 @@
return 0;
}
-static int mdss_pll_read_stored_trim_codes(
- struct mdss_pll_resources *dsi_pll_res, s64 vco_clk_rate,
- int *pll_trim_codes)
-{
- int i;
- int rc = 0;
- bool found = false;
-
- if (!dsi_pll_res->dfps) {
- rc = -EINVAL;
- goto end_read;
- }
-
- for (i = 0; i < dsi_pll_res->dfps->panel_dfps.frame_rate_cnt; i++) {
- struct dfps_codes_info *codes_info =
- &dsi_pll_res->dfps->codes_dfps[i];
-
- pr_debug("valid=%d frame_rate=%d, vco_rate=%d, code %d %d\n",
- codes_info->is_valid, codes_info->frame_rate,
- codes_info->clk_rate, codes_info->pll_codes.pll_codes_1,
- codes_info->pll_codes.pll_codes_2);
-
- if (vco_clk_rate != codes_info->clk_rate &&
- codes_info->is_valid)
- continue;
-
- pll_trim_codes[0] =
- codes_info->pll_codes.pll_codes_1;
- pll_trim_codes[1] =
- codes_info->pll_codes.pll_codes_2;
- found = true;
- break;
- }
-
- if (!found) {
- rc = -EINVAL;
- goto end_read;
- }
-
- pr_debug("core_kvco_code=0x%x core_vco_tune=0x%x\n",
- pll_trim_codes[0], pll_trim_codes[1]);
-
-end_read:
- return rc;
-}
-
int post_n1_div_set_div(struct div_clk *clk, int div)
{
struct mdss_pll_resources *pll = clk->priv;
@@ -1091,8 +1045,7 @@
struct dsi_pll_vco_clk *vco = to_vco_clk(c);
struct mdss_pll_resources *pll = vco->priv;
struct dsi_pll_db *pdb;
- s64 vco_clk_rate = (s64)rate;
- int pll_trim_codes[2];
+ int pll_trim_codes[2] = {0, 0};
if (!pll) {
pr_err("PLL data not found\n");
@@ -1105,12 +1058,6 @@
return -EINVAL;
}
- rc = mdss_pll_read_stored_trim_codes(pll, vco_clk_rate, pll_trim_codes);
- if (rc) {
- pr_err("cannot find pll codes rate=%lld\n", vco_clk_rate);
- return -EINVAL;
- }
-
rc = mdss_pll_resource_enable(pll, true);
if (rc) {
pr_err("Failed to enable mdss dsi plla=%d\n", pll->index);
@@ -1141,7 +1088,7 @@
return rc;
}
-unsigned long pll_vco_get_rate_8996(struct clk *c)
+static unsigned long pll_vco_get_rate_8996(struct clk *c)
{
u64 vco_rate, multiplier = BIT(20);
s32 div_frac_start;
diff --git a/drivers/clk/msm/mdss/mdss-hdmi-pll-8996.c b/drivers/clk/msm/mdss/mdss-hdmi-pll-8996.c
index 717ea94..0f2d61e 100644
--- a/drivers/clk/msm/mdss/mdss-hdmi-pll-8996.c
+++ b/drivers/clk/msm/mdss/mdss-hdmi-pll-8996.c
@@ -31,14 +31,14 @@
#define HDMI_CLKS_PLL_DIVSEL 0
#define HDMI_CORECLK_DIV 5
#define HDMI_REF_CLOCK 19200000
-#define HDMI_64B_ERR_VAL 0xFFFFFFFFFFFFFFFF
+#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 12000000000
-#define HDMI_VCO_MIN_FREQ 8000000000
+#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
@@ -2566,7 +2566,7 @@
return ret;
}
-const struct clk_ops hdmi_8996_v1_vco_clk_ops = {
+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,
@@ -2576,7 +2576,7 @@
.handoff = hdmi_8996_vco_handoff,
};
-const struct clk_ops hdmi_8996_v2_vco_clk_ops = {
+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,
@@ -2586,7 +2586,7 @@
.handoff = hdmi_8996_vco_handoff,
};
-const struct clk_ops hdmi_8996_v3_vco_clk_ops = {
+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,
@@ -2596,7 +2596,7 @@
.handoff = hdmi_8996_vco_handoff,
};
-const struct clk_ops hdmi_8996_v3_1p8_vco_clk_ops = {
+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,
diff --git a/drivers/clk/msm/mdss/mdss-pll-util.c b/drivers/clk/msm/mdss/mdss-pll-util.c
index 11b69c3..7f7da9b 100644
--- a/drivers/clk/msm/mdss/mdss-pll-util.c
+++ b/drivers/clk/msm/mdss/mdss-pll-util.c
@@ -17,8 +17,6 @@
#include <linux/err.h>
#include <linux/string.h>
#include <linux/clk/msm-clock-generic.h>
-#include <linux/of_address.h>
-#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>
#include <linux/memblock.h>
@@ -28,16 +26,16 @@
struct mdss_pll_resources *pll_res)
{
int rc = 0;
- struct dss_module_power *mp = &pll_res->mp;
+ struct mdss_module_power *mp = &pll_res->mp;
- rc = msm_dss_config_vreg(&pdev->dev,
+ 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_dss_get_clk(&pdev->dev, mp->clk_config, mp->num_clk);
+ 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;
@@ -46,7 +44,7 @@
return rc;
clk_err:
- msm_dss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg, 0);
+ msm_mdss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg, 0);
vreg_err:
return rc;
}
@@ -59,11 +57,11 @@
* This is a helper function to retrieve the regulator information
* for each pll resource.
*/
-struct dss_vreg *mdss_pll_get_mp_by_reg_name(struct mdss_pll_resources *pll_res
+struct mdss_vreg *mdss_pll_get_mp_by_reg_name(struct mdss_pll_resources *pll_res
, char *name)
{
- struct dss_vreg *regulator = NULL;
+ struct mdss_vreg *regulator = NULL;
int i;
if ((pll_res == NULL) || (pll_res->mp.vreg_config == NULL)) {
@@ -88,17 +86,17 @@
void mdss_pll_util_resource_deinit(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
- struct dss_module_power *mp = &pll_res->mp;
+ struct mdss_module_power *mp = &pll_res->mp;
- msm_dss_put_clk(mp->clk_config, mp->num_clk);
+ msm_mdss_put_clk(mp->clk_config, mp->num_clk);
- msm_dss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg, 0);
+ 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 dss_module_power *mp = &pll_res->mp;
+ struct mdss_module_power *mp = &pll_res->mp;
devm_kfree(&pdev->dev, mp->clk_config);
devm_kfree(&pdev->dev, mp->vreg_config);
@@ -110,36 +108,37 @@
bool enable)
{
int rc = 0;
- struct dss_module_power *mp = &pll_res->mp;
+ struct mdss_module_power *mp = &pll_res->mp;
if (enable) {
- rc = msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, 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_dss_clk_set_rate(mp->clk_config, mp->num_clk);
+ 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_dss_enable_clk(mp->clk_config, mp->num_clk, enable);
+ 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_dss_enable_clk(mp->clk_config, mp->num_clk, enable);
+ msm_mdss_enable_clk(mp->clk_config, mp->num_clk, enable);
- msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, enable);
+ msm_mdss_enable_vreg(mp->vreg_config, mp->num_vreg, enable);
}
return rc;
clk_err:
- msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, 0);
+ msm_mdss_enable_vreg(mp->vreg_config, mp->num_vreg, 0);
vreg_err:
return rc;
}
@@ -151,7 +150,7 @@
u32 tmp = 0;
struct device_node *of_node = NULL, *supply_root_node = NULL;
struct device_node *supply_node = NULL;
- struct dss_module_power *mp = &pll_res->mp;
+ struct mdss_module_power *mp = &pll_res->mp;
of_node = pdev->dev.of_node;
@@ -173,7 +172,7 @@
}
pr_debug("vreg found. count=%d\n", mp->num_vreg);
- mp->vreg_config = devm_kzalloc(&pdev->dev, sizeof(struct dss_vreg) *
+ mp->vreg_config = devm_kzalloc(&pdev->dev, sizeof(struct mdss_vreg) *
mp->num_vreg, GFP_KERNEL);
if (!mp->vreg_config) {
rc = -ENOMEM;
@@ -298,7 +297,7 @@
struct mdss_pll_resources *pll_res)
{
u32 i = 0, rc = 0;
- struct dss_module_power *mp = &pll_res->mp;
+ struct mdss_module_power *mp = &pll_res->mp;
const char *clock_name;
u32 clock_rate;
@@ -310,7 +309,7 @@
}
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;
@@ -337,90 +336,11 @@
return rc;
}
-static void mdss_pll_free_bootmem(u32 mem_addr, u32 size)
-{
- unsigned long pfn_start, pfn_end, pfn_idx;
-
- pfn_start = mem_addr >> PAGE_SHIFT;
- pfn_end = (mem_addr + size) >> PAGE_SHIFT;
- for (pfn_idx = pfn_start; pfn_idx < pfn_end; pfn_idx++)
- free_reserved_page(pfn_to_page(pfn_idx));
-}
-
-static int mdss_pll_util_parse_dt_dfps(struct platform_device *pdev,
- struct mdss_pll_resources *pll_res)
-{
- int rc = 0;
- struct device_node *pnode;
- const u32 *addr;
- struct vm_struct *area;
- u64 size;
- u32 offsets[2];
- unsigned long virt_add;
-
- pnode = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
- if (IS_ERR_OR_NULL(pnode)) {
- rc = PTR_ERR(pnode);
- goto pnode_err;
- }
-
- addr = of_get_address(pnode, 0, &size, NULL);
- if (!addr) {
- pr_err("failed to parse the dfps memory address\n");
- rc = -EINVAL;
- goto pnode_err;
- }
- /* maintain compatibility for 32/64 bit */
- offsets[0] = (u32) of_read_ulong(addr, 2);
- offsets[1] = (u32) size;
-
- area = get_vm_area(offsets[1], VM_IOREMAP);
- if (!area) {
- rc = -ENOMEM;
- goto dfps_mem_err;
- }
-
- virt_add = (unsigned long)area->addr;
- rc = ioremap_page_range(virt_add, (virt_add + offsets[1]),
- offsets[0], PAGE_KERNEL);
- if (rc) {
- rc = -ENOMEM;
- goto ioremap_err;
- }
-
- pll_res->dfps = kzalloc(sizeof(struct dfps_info), GFP_KERNEL);
- if (IS_ERR_OR_NULL(pll_res->dfps)) {
- rc = PTR_ERR(pll_res->dfps);
- pr_err("couldn't allocate dfps kernel memory\n");
- goto addr_err;
- }
-
- /* memcopy complete dfps structure from kernel virtual memory */
- memcpy_fromio(pll_res->dfps, area->addr, sizeof(struct dfps_info));
-
-addr_err:
- if (virt_add)
- unmap_kernel_range(virt_add, (unsigned long) size);
-ioremap_err:
- if (area)
- vfree(area->addr);
-dfps_mem_err:
- /* free the dfps memory here */
- memblock_free(offsets[0], offsets[1]);
- mdss_pll_free_bootmem(offsets[0], offsets[1]);
-pnode_err:
- if (pnode)
- of_node_put(pnode);
-
- dma_release_declared_memory(&pdev->dev);
- return rc;
-}
-
int mdss_pll_util_resource_parse(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = 0;
- struct dss_module_power *mp = &pll_res->mp;
+ struct mdss_module_power *mp = &pll_res->mp;
rc = mdss_pll_util_parse_dt_supply(pdev, pll_res);
if (rc) {
@@ -434,9 +354,6 @@
goto clk_err;
}
- if (mdss_pll_util_parse_dt_dfps(pdev, pll_res))
- pr_err("dfps not enabled!\n");
-
return rc;
clk_err:
diff --git a/drivers/clk/msm/mdss/mdss-pll.h b/drivers/clk/msm/mdss/mdss-pll.h
index 35d8c74..1fa5cff 100644
--- a/drivers/clk/msm/mdss/mdss-pll.h
+++ b/drivers/clk/msm/mdss/mdss-pll.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -46,37 +46,10 @@
MDSS_PLL_TARGET_8909,
};
-#define DFPS_MAX_NUM_OF_FRAME_RATES 20
-
-struct dfps_panel_info {
- uint32_t enabled;
- uint32_t frame_rate_cnt;
- uint32_t frame_rate[DFPS_MAX_NUM_OF_FRAME_RATES]; /* hz */
-};
-
-struct dfps_pll_codes {
- uint32_t pll_codes_1;
- uint32_t pll_codes_2;
-};
-
-struct dfps_codes_info {
- uint32_t is_valid;
- uint32_t frame_rate; /* hz */
- uint32_t clk_rate; /* hz */
- struct dfps_pll_codes pll_codes;
-};
-
-struct dfps_info {
- struct dfps_panel_info panel_dfps;
- struct dfps_codes_info codes_dfps[DFPS_MAX_NUM_OF_FRAME_RATES];
- void *dfps_fb_base;
- uint32_t chip_serial;
-};
-
struct mdss_pll_resources {
/* Pll specific resources like GPIO, power supply, clocks, etc*/
- struct dss_module_power mp;
+ struct mdss_module_power mp;
/*
* dsi/edp/hmdi plls' base register, phy, gdsc and dynamic refresh
@@ -219,6 +192,6 @@
bool enable);
int mdss_pll_util_resource_parse(struct platform_device *pdev,
struct mdss_pll_resources *pll_res);
-struct dss_vreg *mdss_pll_get_mp_by_reg_name(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/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/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index fd0fd39..f3ab5be 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -3919,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)
@@ -3935,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:
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/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/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/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/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/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-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/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile
index 2c3f0ca..e09dcdb 100644
--- a/drivers/video/fbdev/msm/Makefile
+++ b/drivers/video/fbdev/msm/Makefile
@@ -10,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
@@ -21,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/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_debug.h b/drivers/video/fbdev/msm/mdss_debug.h
index 83342e4..0d482c0 100644
--- a/drivers/video/fbdev/msm/mdss_debug.h
+++ b/drivers/video/fbdev/msm/mdss_debug.h
@@ -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_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index c8d7425..d70c1e8 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -254,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);
}
@@ -294,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)
@@ -318,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) {
@@ -353,13 +353,6 @@
return 0;
}
-int msm_dss_config_vreg_opt_mode(struct dss_vreg *in_vreg,
- int num_vreg, enum dss_vreg_mode mode)
-{
-/* stub: FIX ME.Should be sourced from mdss_io_util.c */
- return 0;
-}
-
static int mdss_dsi_panel_power_ulp(struct mdss_panel_data *pdata,
int enable)
{
@@ -386,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) {
@@ -403,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);
}
@@ -486,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__);
@@ -501,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;
@@ -543,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;
@@ -3460,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",
@@ -3826,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",
@@ -3834,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)
@@ -3879,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__);
@@ -3889,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",
@@ -3910,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",
@@ -4183,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) {
@@ -4265,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_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index 6d494da..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
@@ -1311,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;
@@ -2871,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);
@@ -2884,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);
@@ -2913,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;
@@ -2925,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);
@@ -2974,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++;
@@ -3006,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);
}
@@ -4183,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;
}
@@ -4213,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;
@@ -4237,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,
@@ -4251,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,
@@ -4270,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);
@@ -4304,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;
}
@@ -4320,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)
@@ -4337,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;
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 0effbcb..446e8b4 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_audio.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_audio.c
@@ -63,7 +63,7 @@
};
struct hdmi_audio {
- struct dss_io_data *io;
+ struct mdss_io_data *io;
struct msm_hdmi_audio_setup_params params;
struct extcon_dev sdev;
u32 pclk;
@@ -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;
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_hdcp.c b/drivers/video/fbdev/msm/mdss_hdmi_hdcp.c
index e88884d..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;
@@ -678,7 +678,7 @@
static int read_write_v_h(struct hdmi_hdcp_ctrl *hdcp_ctrl,
struct hdmi_tx_ddc_data ddc_data,
- struct dss_io_data *io, int off, char *name,
+ struct mdss_io_data *io, int off, char *name,
u32 reg, bool wr)
{
int rc = 0;
@@ -715,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;
@@ -774,7 +774,7 @@
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_ctrl, ddc_data, hdcp_io, 0x20, "V' H0",
@@ -843,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;
@@ -1247,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__);
@@ -1353,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;
@@ -1400,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) {
@@ -1453,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_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 d9a84ad..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) {
@@ -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__);
@@ -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;
@@ -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 d09ca3c..6a13c75 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_tx.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.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;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.c b/drivers/video/fbdev/msm/mdss_hdmi_util.c
index 0753b89..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;
@@ -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;
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 414c075..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;
@@ -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;
@@ -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;
@@ -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 8c8d28e..a9a5d8f 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -2630,7 +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;
- 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;
@@ -2639,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;
@@ -2648,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
@@ -2844,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;
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index 7670ee0..4307119 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -1009,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_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c
index 6a292b24..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);
@@ -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 24ed7f9..3144b6c 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -25,7 +25,6 @@
#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>
@@ -39,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
@@ -6340,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);
@@ -6356,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;
@@ -6379,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,
@@ -6420,7 +6420,7 @@
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;
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 ff4dbb7..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
@@ -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;
}
@@ -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 699aacc..7a44824 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.c
+++ b/drivers/video/fbdev/msm/mdss_smmu.c
@@ -70,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;
@@ -86,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;
@@ -114,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;
@@ -142,7 +142,7 @@
bool enable)
{
int rc = 0;
- struct dss_module_power *mp;
+ struct mdss_module_power *mp;
if (!mdss_smmu)
return -EINVAL;
@@ -153,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;
@@ -726,7 +726,7 @@
int rc = 0;
struct mdss_smmu_domain smmu_domain;
const struct of_device_id *match;
- struct dss_module_power *mp;
+ struct mdss_module_power *mp;
char name[MAX_CLIENT_NAME_LEN];
const __be32 *address = NULL, *size = NULL;
@@ -764,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;
@@ -779,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);
@@ -790,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;
}
@@ -799,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);
}
@@ -862,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_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/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/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/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/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/kernel/time/timer.c b/kernel/time/timer.c
index 5b5d016..1de57ef 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;
@@ -1489,6 +1488,8 @@
#ifdef CONFIG_SMP
+static atomic_t deferrable_pending;
+
/*
* check_pending_deferrable_timers - Check for unbound deferrable timer expiry
* @cpu - Current CPU
@@ -1669,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.
*/
@@ -1693,9 +1715,7 @@
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);
}
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/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.