diff options
702 files changed, 49995 insertions, 9049 deletions
diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index 3435d138955c..45d052ad0ada 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -80,6 +80,9 @@ its hardware characteristcs. * qcom,force-reg-dump: enables TMC reg dump support + * arm,sg-enable : indicates whether scatter gather feature is enabled + by default for TMC ETR configuration. + * Required property for TPDAs: * qcom,tpda-atid: must be present. Specifies the ATID for TPDA. diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt index 2bd7653af5a3..cee9b942a9e3 100644 --- a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt +++ b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt @@ -182,6 +182,14 @@ Properties: command register for each of the two clusters managed by the OSM controller. +- qcom,apm-threshold-voltage + Usage: required + Value type: <u32> + Definition: Specifies the APM threshold voltage in microvolts. If the + VDD_APCC supply voltage is above or at this level, then the + APM is switched to use VDD_APCC. If VDD_APCC is below + this level, then the APM is switched to use VDD_MX. + - qcom,apm-mode-ctl Usage: required Value type: <prop-encoded-array> @@ -392,6 +400,8 @@ Example: qcom,apm-ctrl-status = <0x179d000c 0x179d0018>; + qcom,apm-threshold-voltage = <832000>; + qcom,pwrcl-apcs-mem-acc-cfg = <0x179d1360 0x179d1364 0x179d1364>; qcom,perfcl-apcs-mem-acc-cfg = diff --git a/Documentation/devicetree/bindings/fb/mdss-dp.txt b/Documentation/devicetree/bindings/fb/mdss-dp.txt new file mode 100644 index 000000000000..85656e312acc --- /dev/null +++ b/Documentation/devicetree/bindings/fb/mdss-dp.txt @@ -0,0 +1,138 @@ +QTI MDSS DP + +MDSS DP is a display-port driver which supports panels that are compatible with +VESA DP and EDP display interface specification. + +When configuring the optional properties for external backlight, one should also +configure the gpio that drives the pwm to it. + +Required properties +- compatible : Must be "qcom,mdss-edp". +- reg : Offset and length of the register set for the + device. +- reg-names : Names to refer to register sets related to this + device +- gdsc-supply : Phandle for gdsc regulator device node. +- vdda-1p2-supply : Phandle for 1.2V vdda regulator device node. +- vdda-0p9-supply : Phandle for 0.9V vdda regulator device node. +- status : A string that has to be set to "okay/ok" to enable + the driver. By default this property will be set to + "disable". Will be set to "ok/okay" status for + specific platforms. +- qcom,mdss-fb-map: pHandle that specifies the framebuffer to which the + interface is mapped. +- clocks: List of Phandles for clock device nodes + needed by the device. +- clock-names: List of clock names needed by the device. +- qcom,aux-en-gpio: Specifies the aux-channel enable gpio. +- qcom,aux-sel-gpio: Specifies the aux-channel select gpio. +- qcom,usbplug-cc-gpio: Specifies the usbplug orientation gpio. + +Optional properties: +- qcom,<type>-supply-entries: A node that lists the elements of the supply used by the + a particular "type" of DSI modulee. The module "types" + can be "core", "ctrl", and "phy". Within the same type, + there can be more than one instance of this binding, + in which case the entry would be appended with the + supply entry index. + e.g. qcom,ctrl-supply-entry@0 + -- qcom,supply-name: name of the supply (vdd/vdda/vddio) + -- qcom,supply-min-voltage: minimum voltage level (uV) + -- qcom,supply-max-voltage: maximum voltage level (uV) + -- qcom,supply-enable-load: load drawn (uA) from enabled supply + -- qcom,supply-disable-load: load drawn (uA) from disabled supply + -- qcom,supply-pre-on-sleep: time to sleep (ms) before turning on + -- qcom,supply-post-on-sleep: time to sleep (ms) after turning on + -- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off + -- qcom,supply-post-off-sleep: time to sleep (ms) after turning off +- qcom,hpd-gpio: Specifies the HPD gpio. +- pinctrl-names: List of names to assign mdss pin states defined in pinctrl device node + Refer to pinctrl-bindings.txt +- pinctrl-<0..n>: Lists phandles each pointing to the pin configuration node within a pin + controller. These pin configurations are installed in the pinctrl + device node. Refer to pinctrl-bindings.txt + +Example: + mdss_dp_ctrl: qcom,dp_ctrl@c990000 { + cell-index = <0>; + compatible = "qcom,mdss-dp"; + qcom,mdss-fb-map = <&mdss_fb3>; + + gdsc-supply = <&gdsc_mdss>; + vdda-1p2-supply = <&pmcobalt_l2>; + vdda-0p9-supply = <&pmcobalt_l1>; + + reg = <0xc990000 0xa84>, + <0xc011000 0x910>, + <0x1fcb200 0x050>; + reg-names = "dp_ctrl", "dp_phy", "tcsr_regs"; + + clocks = <&clock_mmss clk_mmss_mnoc_ahb_clk>, + <&clock_mmss clk_mmss_mdss_ahb_clk>, + <&clock_mmss clk_mmss_mdss_axi_clk>, + <&clock_mmss clk_mmss_mdss_mdp_clk>, + <&clock_mmss clk_mmss_mdss_hdmi_dp_ahb_clk>, + <&clock_mmss clk_mmss_mdss_dp_aux_clk>, + <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>, + <&clock_mmss clk_mmss_mdss_dp_link_clk>, + <&clock_mmss clk_mmss_mdss_dp_link_intf_clk>, + <&clock_mmss clk_mmss_mdss_dp_crypto_clk>, + <&clock_mmss clk_mmss_mdss_dp_pixel_clk>; + clock-names = "core_mnoc_clk", "core_iface_clk", "core_bus_clk", + "core_mdp_core_clk", "core_alt_iface_clk", + "core_aux_clk", "core_cfg_ahb_clk", "ctrl_link_clk", + "ctrl_link_iface_clk", "ctrl_crypto_clk", "ctrl_pixel_clk"; + + 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-1p2"; + qcom,supply-min-voltage = <1200000>; + qcom,supply-max-voltage = <1200000>; + qcom,supply-enable-load = <12560>; + qcom,supply-disable-load = <4>; + }; + }; + + qcom,phy-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,phy-supply-entry@0 { + reg = <0>; + qcom,supply-name = "vdda-0p9"; + qcom,supply-min-voltage = <880000>; + qcom,supply-max-voltage = <880000>; + qcom,supply-enable-load = <73400>; + qcom,supply-disable-load = <32>; + }; + }; + + pinctrl-names = "mdss_dp_active", "mdss_dp_sleep"; + pinctrl-0 = <&mdss_dp_aux_active &mdss_dp_usbplug_cc_active + &mdss_dp_hpd_active>; + pinctrl-1 = <&mdss_dp_aux_suspend &mdss_dp_usbplug_cc_suspend + &mdss_dp_hpd_suspend>; + qcom,aux-en-gpio = <&tlmm 77 0>; + qcom,aux-sel-gpio = <&tlmm 78 0>; + qcom,usbplug-cc-gpio = <&tlmm 38 0>; + qcom,hpd-gpio = <&tlmm 34 0>; + }; + diff --git a/Documentation/devicetree/bindings/fb/mdss-edp.txt b/Documentation/devicetree/bindings/fb/mdss-edp.txt deleted file mode 100644 index 882047a43fc9..000000000000 --- a/Documentation/devicetree/bindings/fb/mdss-edp.txt +++ /dev/null @@ -1,52 +0,0 @@ -Qualcomm MDSS EDP - -MDSS EDP is a edp driver which supports panels that are compatable with -VESA EDP display interface specification. - -When configuring the optional properties for external backlight, one should also -configure the gpio that drives the pwm to it. - -Required properties -- compatible : Must be "qcom,mdss-edp". -- reg : Offset and length of the register set for the - device. -- reg-names : Names to refer to register sets related to this - device -- vdda-supply : Phandle for vdd regulator device node. -- gpio-panel-en : GPIO for supplying power to panel and backlight - driver. -- gpio-lvl-en : GPIO to enable HPD be received by host. -- status : A string that has to be set to "okay/ok" to enable - the driver. By default this property will be set to - "disable". Will be set to "ok/okay" status for - specific platforms. -- qcom,mdss-fb-map: pHandle that specifies the framebuffer to which the - interface is mapped. -- gpio-panel-hpd : gpio pin use for edp hpd - -Optional properties -- qcom,panel-lpg-channel : LPG channel for backlight. -- qcom,panel-pwm-period : PWM period in microseconds. - - -Optional properties: -- qcom,mdss-brightness-max-level: Specifies the max brightness level supported. - 255 = default value. - -Example: - mdss_edp: qcom,mdss_edp@fd923400 { - compatible = "qcom,mdss-edp"; - reg = <0xfd923400 0x700>, - <0xfd8c2000 0x1000>; - reg-names = "edp_base", "mmss_cc_base"; - vdda-supply = <&pm8941_l12>; - gpio-panel-en = <&msmgpio 58 0>; - gpio-lvl-en = <&msmgpio 91 0>; - qcom,panel-lpg-channel = <7>; /* LPG Channel 8 */ - qcom,panel-pwm-period = <53>; - status = "disable"; - qcom,mdss-fb-map = <&mdss_fb0>; - gpio-panel-hpd = <&msmgpio 102 0>; - }; - - diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt index ce2e38b905a1..06b8c71effcc 100644 --- a/Documentation/devicetree/bindings/gpu/adreno.txt +++ b/Documentation/devicetree/bindings/gpu/adreno.txt @@ -138,6 +138,11 @@ Optional Properties: Specify the size of snapshot in bytes. This will override snapshot size defined in the driver code. +- qcom,gpu-qdss-stm: + <baseAddr size> + baseAddr - base address of the gpu channels in the qdss stm memory region + size - size of the gpu stm region + GPU Quirks: - qcom,gpu-quirk-two-pass-use-wfi: Signal the GPU to set Set TWOPASSUSEWFI bit in diff --git a/Documentation/devicetree/bindings/iio/adc/qcom-rradc.txt b/Documentation/devicetree/bindings/iio/adc/qcom-rradc.txt index 8a64ccbd66c0..721a4f72563e 100644 --- a/Documentation/devicetree/bindings/iio/adc/qcom-rradc.txt +++ b/Documentation/devicetree/bindings/iio/adc/qcom-rradc.txt @@ -36,13 +36,10 @@ Main node properties: Definition: Must be one. For details about IIO bindings see: Documentation/devicetree/bindings/iio/iio-bindings.txt -Channel subnode properties: - -- channel: - Usage: required - Value type: <u32> - Definition: ADC channel number. - See drivers/iio/adc/qcom-rradc.c for channels within rradc_channel_id +IIO client nodes need to specify the RRADC channel number while requesting ADC reads. +The channel list supported by the RRADC driver is available in the enum rradc_channel_id +located at at drivers/iio/adc/qcom-rradc.c. Clients can use this index from the enum +as the channel number while requesting ADC reads. Example: @@ -53,11 +50,6 @@ Example: #address-cells = <1>; #size-cells = <0>; #io-channel-cells = <1>; - - /* Channel node */ - batt_id { - channel = <0>; - }; }; /* IIO client node */ diff --git a/Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt b/Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt new file mode 100644 index 000000000000..832ec34dbbda --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt @@ -0,0 +1,80 @@ +ITE Tech. touch controller + +The ITE Tech. touch controller is connected to host processor +via i2c. The controller generates interrupts when the user +touches the panel. The host controller is expected to read +the touch coordinates over i2c and pass the coordinates to +the rest of the system. + +Required properties: + + - compatible : should be "ite,it7260_ts" + - reg : i2c slave address of the device + - interrupt-parent : parent of interrupt + - interrupts : touch sample interrupt to indicate presence or release + of fingers on the panel. + - ite,irq-gpio : irq gpio which is to provide interrupts to host, + same as "interrupts" node. It will also + contain active low or active high information. + - ite,reset-gpio : reset gpio to control the reset of chip + - ite,reset-delay : reset delay for controller (ms), default 20 + +Optional properties: + - avdd-supply : Analog power supply needed to power device + - vdd-supply : Power source required to pull up i2c bus + - ite,wakeup : boolean, use this to support touch-to-wake feature. + - ite,palm-detect-en : boolean, use this to send palm-detect-keycode when + palm is detected. + - ite,fw-name : Specify firmware file name in /etc/firmware + - ite,cfg-name : Specify config file name in /etc/firmware + - ite,panel-coords : touch panel min x, min y, max x and + max y resolution + - ite,display-coords : display min x, min y, max x and + max y resolution + - ite,num-fingers : number of fingers supported by the touch controller + - 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: + "pmx_ts_active" : Active configuration of pins, this should specify active + config defined in pin groups of interrupt and reset gpio. + "pmx_ts_suspend" : Disabled configuration of pins, this should specify sleep + config defined in pin groups of interrupt and reset gpio. + "pmx_ts_release" : Release configuration of pins, this should specify + release config defined in pin groups of interrupt and reset gpio. + - ite,low-reset : boolean, if the controller needs low-state of the reset gpio while + initializing, and reset gpio should be made as high-state to reset the + controller. It means the controller needs "active-high" reset gpio. + +Required properties palm-detect-en feature: + - ite,palm-detect-keycode : The keycode that is required to be sent when + palm is detected by the ITE tech driver. + +Example: + i2c@f9927000 { + it7260@46 { + compatible = "ite,it7260_ts"; + reg = <0x46>; + interrupt-parent = <&msmgpio>; + /* pins used by touchscreen */ + pinctrl-names = "pmx_ts_active","pmx_ts_suspend","pmx_ts_release"; + pinctrl-0 = <&ts_int_active &ts_reset_active>; + pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>; + pinctrl-2 = <&ts_release> + interrupts = <17 0x2>; + avdd-supply = <&pm8226_l19>; + vdd-supply = <&pm8226_lvs1>; + ite,reset-gpio = <&msmgpio 16 0x00>; + ite,irq-gpio = <&msmgpio 17 0x2008>; + ite,wakeup; + ite,palm-detect-en; + ite,palm-detect-keycode = <142>; + ite,fw-name = "ite7260_fw.bin"; + ite,cfg-name = "ite7260_cfg.bin"; + ite,panel-coords = <0 0 320 320>; + ite,display-coords = <0 0 320 320>; + ite,num-fingers = <2>; + ite,reset-delay = <20>; + ite,low-reset; + }; + }; diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt index 1102cb5d4f6f..51bcb07cbb6e 100644 --- a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt +++ b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt @@ -13,9 +13,25 @@ Required properties: - reg : Base address and size for flash LED modules Optional properties: +- interrupts : Specifies the interrupts associated with flash-led. +- interrupt-names : Specify the interrupt names associated with interrupts. - qcom,hdrm-auto-mode : Boolean type to select headroom auto mode enabled or not -- qcom,isc-delay : Integer type to specify short circuit delay. Valid values are 32, 64, - 128, 192. Unit is us. +- qcom,isc-delay-us : Integer type to specify short circuit delay. Valid values are 32, 64, + 128, 192. Unit is uS. +- qcom,warmup-delay-us : Integer type to specify warm up delay. Valid values are 32, 64, + 128, 192. Unit is uS. +- qcom,short-circuit-det : Boolean property which enables short circuit fault detection. +- qcom,open-circuit-det : Boolean property which enables open circuit fault detection. +- qcom,vph-droop-det : Boolean property which enables VPH droop detection. +- qcom,vph-droop-hys-mv : Integer property to specify VPH droop hysteresis. It is only used if + qcom,vph-droop-det is specified. Valid values are 0, 25, 50 and 75. + Unit is mV. +- qcom,vph-droop-thresh-mv : Integer property to specify VPH droop threshold. It is only used if + qcom,vph-droop-det is specified. Valid values are + 2500 to 3200 with step size of 100. Unit is mV. +- qcom,vph-droop-debounce-us : Integer property to specify VPH droop debounce time. It is only used + if qcom,vph-droop-det is specified. Valid values are 0, 8, 16 and 26. + Unit is uS. - qcom,hw-strobe-option : Integer type to specify hardware strobe option. Based on the specified value, additional GPIO configuration may be required to provide strobing support. Supported values are: @@ -93,6 +109,22 @@ Example: status = "okay"; reg = <0xd300 0x100>; label = "flash"; + interrupts = <0x3 0xd3 0x0 IRQ_TYPE_EDGE_BOTH>, + <0x3 0xd3 0x1 IRQ_TYPE_EDGE_BOTH>, + <0x3 0xd3 0x2 IRQ_TYPE_EDGE_BOTH>, + <0x3 0xd3 0x3 IRQ_TYPE_EDGE_BOTH>, + <0x3 0xd3 0x4 IRQ_TYPE_EDGE_BOTH>, + <0x3 0xd3 0x5 IRQ_TYPE_EDGE_BOTH>, + <0x3 0xd3 0x6 IRQ_TYPE_EDGE_BOTH>, + <0x3 0xd3 0x7 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "led-fault-irq", + "mitigation-irq", + "flash-timer-exp-irq", + "all-ramp-down-done-irq", + "all-ramp-up-done-irq", + "led3-ramp-up-done-irq", + "led2-ramp-up-done-irq", + "led1-ramp-up-done-irq"; qcom,hdrm-auto-mode; qcom,isc-delay = <192>; diff --git a/Documentation/devicetree/bindings/media/video/msm-vidc.txt b/Documentation/devicetree/bindings/media/video/msm-vidc.txt index 1c90f98c2954..b1869803d345 100644 --- a/Documentation/devicetree/bindings/media/video/msm-vidc.txt +++ b/Documentation/devicetree/bindings/media/video/msm-vidc.txt @@ -130,6 +130,8 @@ value is typically max(latencies of every cluster at all power levels) + 1 - qcom,max-secure-instances = An int containing max number of concurrent secure instances supported, accounting for venus and system wide limitations like memory, performance etc. +- qcom,debug-timeout = A bool indicating that FW errors such as SYS_ERROR, + SESSION_ERROR and timeouts will be treated as Fatal. [Second level nodes] Context Banks diff --git a/Documentation/devicetree/bindings/opp/opp.txt b/Documentation/devicetree/bindings/opp/opp.txt index 0cb44dc21f97..601256fe8c0d 100644 --- a/Documentation/devicetree/bindings/opp/opp.txt +++ b/Documentation/devicetree/bindings/opp/opp.txt @@ -45,21 +45,10 @@ Devices supporting OPPs must set their "operating-points-v2" property with phandle to a OPP table in their DT node. The OPP core will use this phandle to find the operating points for the device. -Devices may want to choose OPP tables at runtime and so can provide a list of -phandles here. But only *one* of them should be chosen at runtime. This must be -accompanied by a corresponding "operating-points-names" property, to uniquely -identify the OPP tables. - If required, this can be extended for SoC vendor specfic bindings. Such bindings should be documented as Documentation/devicetree/bindings/power/<vendor>-opp.txt and should have a compatible description like: "operating-points-v2-<vendor>". -Optional properties: -- operating-points-names: Names of OPP tables (required if multiple OPP - tables are present), to uniquely identify them. The same list must be present - for all the CPUs which are sharing clock/voltage rails and hence the OPP - tables. - * OPP Table Node This describes the OPPs belonging to a device. This node can have following @@ -100,6 +89,14 @@ Optional properties: Entries for multiple regulators must be present in the same order as regulators are specified in device's DT node. +- opp-microvolt-<name>: Named opp-microvolt property. This is exactly similar to + the above opp-microvolt property, but allows multiple voltage ranges to be + provided for the same OPP. At runtime, the platform can pick a <name> and + matching opp-microvolt-<name> property will be enabled for all OPPs. If the + platform doesn't pick a specific <name> or the <name> doesn't match with any + opp-microvolt-<name> properties, then opp-microvolt property shall be used, if + present. + - opp-microamp: The maximum current drawn by the device in microamperes considering system specific parameters (such as transients, process, aging, maximum operating temperature range etc.) as necessary. This may be used to @@ -112,6 +109,9 @@ Optional properties: for few regulators, then this should be marked as zero for them. If it isn't required for any regulator, then this property need not be present. +- opp-microamp-<name>: Named opp-microamp property. Similar to + opp-microvolt-<name> property, but for microamp instead. + - clock-latency-ns: Specifies the maximum possible transition latency (in nanoseconds) for switching to this OPP from any other OPP. @@ -123,6 +123,26 @@ Optional properties: - opp-suspend: Marks the OPP to be used during device suspend. Only one OPP in the table should have this. +- opp-supported-hw: This enables us to select only a subset of OPPs from the + larger OPP table, based on what version of the hardware we are running on. We + still can't have multiple nodes with the same opp-hz value in OPP table. + + It's an user defined array containing a hierarchy of hardware version numbers, + supported by the OPP. For example: a platform with hierarchy of three levels + of versions (A, B and C), this field should be like <X Y Z>, where X + corresponds to Version hierarchy A, Y corresponds to version hierarchy B and Z + corresponds to version hierarchy C. + + Each level of hierarchy is represented by a 32 bit value, and so there can be + only 32 different supported version per hierarchy. i.e. 1 bit per version. A + value of 0xFFFFFFFF will enable the OPP for all versions for that hierarchy + level. And a value of 0x00000000 will disable the OPP completely, and so we + never want that to happen. + + If 32 values aren't sufficient for a version hierarchy, than that version + hierarchy can be contained in multiple 32 bit values. i.e. <X Y Z1 Z2> in the + above example, Z1 & Z2 refer to the version hierarchy Z. + - status: Marks the node enabled/disabled. Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together. @@ -157,20 +177,20 @@ Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together. compatible = "operating-points-v2"; opp-shared; - opp00 { + opp@1000000000 { opp-hz = /bits/ 64 <1000000000>; opp-microvolt = <970000 975000 985000>; opp-microamp = <70000>; clock-latency-ns = <300000>; opp-suspend; }; - opp01 { + opp@1100000000 { opp-hz = /bits/ 64 <1100000000>; opp-microvolt = <980000 1000000 1010000>; opp-microamp = <80000>; clock-latency-ns = <310000>; }; - opp02 { + opp@1200000000 { opp-hz = /bits/ 64 <1200000000>; opp-microvolt = <1025000>; clock-latency-ns = <290000>; @@ -236,20 +256,20 @@ independently. * independently. */ - opp00 { + opp@1000000000 { opp-hz = /bits/ 64 <1000000000>; opp-microvolt = <970000 975000 985000>; opp-microamp = <70000>; clock-latency-ns = <300000>; opp-suspend; }; - opp01 { + opp@1100000000 { opp-hz = /bits/ 64 <1100000000>; opp-microvolt = <980000 1000000 1010000>; opp-microamp = <80000>; clock-latency-ns = <310000>; }; - opp02 { + opp@1200000000 { opp-hz = /bits/ 64 <1200000000>; opp-microvolt = <1025000>; opp-microamp = <90000; @@ -312,20 +332,20 @@ DVFS state together. compatible = "operating-points-v2"; opp-shared; - opp00 { + opp@1000000000 { opp-hz = /bits/ 64 <1000000000>; opp-microvolt = <970000 975000 985000>; opp-microamp = <70000>; clock-latency-ns = <300000>; opp-suspend; }; - opp01 { + opp@1100000000 { opp-hz = /bits/ 64 <1100000000>; opp-microvolt = <980000 1000000 1010000>; opp-microamp = <80000>; clock-latency-ns = <310000>; }; - opp02 { + opp@1200000000 { opp-hz = /bits/ 64 <1200000000>; opp-microvolt = <1025000>; opp-microamp = <90000>; @@ -338,20 +358,20 @@ DVFS state together. compatible = "operating-points-v2"; opp-shared; - opp10 { + opp@1300000000 { opp-hz = /bits/ 64 <1300000000>; opp-microvolt = <1045000 1050000 1055000>; opp-microamp = <95000>; clock-latency-ns = <400000>; opp-suspend; }; - opp11 { + opp@1400000000 { opp-hz = /bits/ 64 <1400000000>; opp-microvolt = <1075000>; opp-microamp = <100000>; clock-latency-ns = <400000>; }; - opp12 { + opp@1500000000 { opp-hz = /bits/ 64 <1500000000>; opp-microvolt = <1010000 1100000 1110000>; opp-microamp = <95000>; @@ -378,7 +398,7 @@ Example 4: Handling multiple regulators compatible = "operating-points-v2"; opp-shared; - opp00 { + opp@1000000000 { opp-hz = /bits/ 64 <1000000000>; opp-microvolt = <970000>, /* Supply 0 */ <960000>, /* Supply 1 */ @@ -391,7 +411,7 @@ Example 4: Handling multiple regulators /* OR */ - opp00 { + opp@1000000000 { opp-hz = /bits/ 64 <1000000000>; opp-microvolt = <970000 975000 985000>, /* Supply 0 */ <960000 965000 975000>, /* Supply 1 */ @@ -404,7 +424,7 @@ Example 4: Handling multiple regulators /* OR */ - opp00 { + opp@1000000000 { opp-hz = /bits/ 64 <1000000000>; opp-microvolt = <970000 975000 985000>, /* Supply 0 */ <960000 965000 975000>, /* Supply 1 */ @@ -417,7 +437,8 @@ Example 4: Handling multiple regulators }; }; -Example 5: Multiple OPP tables +Example 5: opp-supported-hw +(example: three level hierarchy of versions: cuts, substrate and process) / { cpus { @@ -426,40 +447,73 @@ Example 5: Multiple OPP tables ... cpu-supply = <&cpu_supply> - operating-points-v2 = <&cpu0_opp_table_slow>, <&cpu0_opp_table_fast>; - operating-points-names = "slow", "fast"; + operating-points-v2 = <&cpu0_opp_table_slow>; }; }; - cpu0_opp_table_slow: opp_table_slow { + opp_table { compatible = "operating-points-v2"; status = "okay"; opp-shared; - opp00 { + opp@600000000 { + /* + * Supports all substrate and process versions for 0xF + * cuts, i.e. only first four cuts. + */ + opp-supported-hw = <0xF 0xFFFFFFFF 0xFFFFFFFF> opp-hz = /bits/ 64 <600000000>; + opp-microvolt = <900000 915000 925000>; ... }; - opp01 { + opp@800000000 { + /* + * Supports: + * - cuts: only one, 6th cut (represented by 6th bit). + * - substrate: supports 16 different substrate versions + * - process: supports 9 different process versions + */ + opp-supported-hw = <0x20 0xff0000ff 0x0000f4f0> opp-hz = /bits/ 64 <800000000>; + opp-microvolt = <900000 915000 925000>; ... }; }; +}; + +Example 6: opp-microvolt-<name>, opp-microamp-<name>: +(example: device with two possible microvolt ranges: slow and fast) - cpu0_opp_table_fast: opp_table_fast { +/ { + cpus { + cpu@0 { + compatible = "arm,cortex-a7"; + ... + + operating-points-v2 = <&cpu0_opp_table>; + }; + }; + + cpu0_opp_table: opp_table0 { compatible = "operating-points-v2"; - status = "okay"; opp-shared; - opp10 { + opp@1000000000 { opp-hz = /bits/ 64 <1000000000>; - ... + opp-microvolt-slow = <900000 915000 925000>; + opp-microvolt-fast = <970000 975000 985000>; + opp-microamp-slow = <70000>; + opp-microamp-fast = <71000>; }; - opp11 { - opp-hz = /bits/ 64 <1100000000>; - ... + opp@1200000000 { + opp-hz = /bits/ 64 <1200000000>; + opp-microvolt-slow = <900000 915000 925000>, /* Supply vcc0 */ + <910000 925000 935000>; /* Supply vcc1 */ + opp-microvolt-fast = <970000 975000 985000>, /* Supply vcc0 */ + <960000 965000 975000>; /* Supply vcc1 */ + opp-microamp = <70000>; /* Will be used for both slow/fast */ }; }; }; diff --git a/Documentation/devicetree/bindings/pinctrl/img,pistachio-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/img,pistachio-pinctrl.txt index 08a4a32c8eb0..0326154c7925 100644 --- a/Documentation/devicetree/bindings/pinctrl/img,pistachio-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/img,pistachio-pinctrl.txt @@ -134,12 +134,12 @@ mfio80 ddr_debug, mips_trace_data, mips_debug mfio81 dreq0, mips_trace_data, eth_debug mfio82 dreq1, mips_trace_data, eth_debug mfio83 mips_pll_lock, mips_trace_data, usb_debug -mfio84 sys_pll_lock, mips_trace_data, usb_debug -mfio85 wifi_pll_lock, mips_trace_data, sdhost_debug -mfio86 bt_pll_lock, mips_trace_data, sdhost_debug -mfio87 rpu_v_pll_lock, dreq2, socif_debug -mfio88 rpu_l_pll_lock, dreq3, socif_debug -mfio89 audio_pll_lock, dreq4, dreq5 +mfio84 audio_pll_lock, mips_trace_data, usb_debug +mfio85 rpu_v_pll_lock, mips_trace_data, sdhost_debug +mfio86 rpu_l_pll_lock, mips_trace_data, sdhost_debug +mfio87 sys_pll_lock, dreq2, socif_debug +mfio88 wifi_pll_lock, dreq3, socif_debug +mfio89 bt_pll_lock, dreq4, dreq5 tck trstn tdi diff --git a/Documentation/devicetree/bindings/platform/msm/ipa.txt b/Documentation/devicetree/bindings/platform/msm/ipa.txt index 222b4fe66697..0aba9aef566a 100644 --- a/Documentation/devicetree/bindings/platform/msm/ipa.txt +++ b/Documentation/devicetree/bindings/platform/msm/ipa.txt @@ -76,6 +76,10 @@ memory allocation over a PCIe bridge - qcom,tethered-flow-control: Boolean context flag to indicate whether apps based flow control is needed for tethered call. +- qcom,rx-polling-sleep-ms: Receive Polling Timeout in millisecond, + default is 1 millisecond. +- qcom,ipa-polling-iteration: IPA Polling Iteration Count,default is 40. + IPA pipe sub nodes (A2 static pipes configurations): -label: two labels are supported, a2-to-ipa and ipa-to-a2 which @@ -119,6 +123,9 @@ IPA SMMU sub nodes - qcom,iova-mapping: specifies the start address and size of iova space. +- qcom,additional-mapping: specifies any addtional mapping needed for this + context bank. The format is <iova pa size> + IPA SMP2P sub nodes -compatible: "qcom,smp2pgpio-map-ipa-1-out" - represents the out gpio from @@ -195,7 +202,10 @@ qcom,ipa@fd4c0000 { ipa_smmu_ap: ipa_smmu_ap { compatible = "qcom,ipa-smmu-ap-cb"; iommus = <&anoc2_smmu 0x30>; - qcom,iova-mapping = <0x10000000 0x40000000>; + qcom,iova-mapping = <0x20000000 0x40000000>; + qcom,additional-mapping = + /* modem tables in IMEM */ + <0x146BD000 0x146BD000 0x2000>; }; ipa_smmu_wlan: ipa_smmu_wlan { diff --git a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt new file mode 100644 index 000000000000..9bf6d5b2bf8e --- /dev/null +++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-fg-gen3.txt @@ -0,0 +1,179 @@ +Qualcomm Techonologies, Inc. QPNP PMIC Fuel Gauge Gen3 Device + +QPNP PMIC FG Gen3 device provides interface to the clients to read properties +related to the battery. Its main function is to retrieve the State of Charge +(SOC), in percentage scale representing the amount of charge left in the +battery. + +======================= +Required Node Structure +======================= + +FG Gen3 device must be described in two levels of device nodes. The first +level describes the FG Gen3 device. The second level describes one or more +peripherals managed by FG Gen3 driver. All the peripheral specific parameters +such as base address, interrupts etc., should be under second level node. + +==================================== +First Level Node - FG Gen3 device +==================================== + +- compatible + Usage: required + Value type: <string> + Definition: Should be "qcom,fg-gen3". + +- qcom,pmic-revid + Usage: required + Value type: <phandle> + Definition: Should specify the phandle of PMIC revid module. This is + used to identify the PMIC subtype. + +- io-channels +- io-channel-names + Usage: required + Value type: <phandle> + Definition: For details about IIO bindings see: + Documentation/devicetree/bindings/iio/iio-bindings.txt + +- qcom,fg-cutoff-voltage + Usage: optional + Value type: <u32> + Definition: The voltage (in mV) where the fuel gauge will steer the SOC + to be zero. For example, if the cutoff voltage is set to + 3400mv, the fuel gauge will try to count SoC so that the + battery SOC will be 0 when it is 3400mV. If this property + is not specified, then the default value used will be + 3200mV. + +- qcom,fg-empty-voltage + Usage: optional + Value type: <u32> + Definition: The voltage threshold (in mV) based on which the empty soc + interrupt will be triggered. When the empty soc interrupt + fires, battery soc will be set to 0 and the userspace will + be notified via the power supply framework. The userspace + will read 0% soc and immediately shutdown. If this property + is not specified, then the default value used will be + 3100mV. + +- qcom,fg-vbatt-low-thr + Usage: optional + Value type: <u32> + Definition: The voltage threshold (in mV) which upon set will be used + for configuring the low battery voltage threshold. + +- qcom,fg-chg-term-current + Usage: optional + Value type: <u32> + Definition: Battery current (in mA) at which the fuel gauge will issue + an end of charge if the charger is configured to use the + fuel gauge ADC for end of charge detection. If this + property is not specified, then the default value used + will be 100mA. + +- qcom,fg-sys-term-current + Usage: optional + Value type: <u32> + Definition: Battery current (in mA) at which the fuel gauge will try to + scale towards 100%. When the charge current goes above this + the SOC should be at 100%. If this property is not + specified, then the default value used will be 125mA. + +- qcom,fg-delta-soc-thr + Usage: optional + Value type: <u32> + Definition: Percentage of monotonic SOC increase upon which the delta + SOC interrupt will be triggered. If this property is not + specified, then the default value will be 1. + +- qcom,fg-recharge-soc-thr + Usage: optional + Value type: <u32> + Definition: Percentage of monotonic SOC upon which the charging will + will be resumed once the charging is complete. If this + property is not specified, then the default value will be + 95. + +- qcom,fg-rsense-sel + Usage: optional + Value type: <u32> + Definition: Specifies the source of sense resistor. + Allowed values are: + 0 - Rsense is from Battery FET + 1 - Rsense is external + 2 - Rsense is Battery FET and SMB + Option 2 can be used only when a parallel charger is + present. If this property is not specified, then the + default value will be 2. + +- qcom,fg-jeita-thresholds + Usage: optional + Value type: <prop-encoded-array> + Definition: A list of integers which holds the jeita thresholds (degC) + in the following order. Allowed size is 4. + Element 0 - JEITA cold threshold + Element 1 - JEITA cool threshold + Element 2 - JEITA warm threshold + Element 3 - JEITA hot threshold + If these parameters are not specified, then the default + values used will be 0, 5, 45, 50. + +========================================================== +Second Level Nodes - Peripherals managed by FG Gen3 driver +========================================================== +- reg + Usage: required + Value type: <prop-encoded-array> + Definition: Addresses and sizes for the specified peripheral + +- interrupts + Usage: optional + Value type: <prop-encoded-array> + Definition: Interrupt mapping as per the interrupt encoding + +- interrupt-names + Usage: optional + Value type: <stringlist> + Definition: Interrupt names. This list must match up 1-to-1 with the + interrupts specified in the 'interrupts' property. + +======== +Example +======== + +pmicobalt_fg: qpnp,fg { + compatible = "qcom,fg-gen3"; + #address-cells = <1>; + #size-cells = <1>; + qcom,pmic-revid = <&pmicobalt_revid>; + io-channels = <&pmicobalt_rradc 3>; + io-channel-names = "rradc_batt_id"; + status = "okay"; + + qcom,fg-batt-soc@4000 { + status = "okay"; + reg = <0x4000 0x100>; + interrupts = <0x2 0x40 0x0 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x40 0x1 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x40 0x2 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x40 0x3 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "soc-update", + "soc-ready", + "bsoc-delta", + "msoc-delta"; + + }; + + qcom,fg-batt-info@4100 { + status = "okay"; + reg = <0x4100 0x100>; + interrupts = <0x2 0x41 0x3 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "batt-missing"; + }; + + qcom,fg-memif@4400 { + status = "okay"; + reg = <0x4400 0x100>; + }; +}; diff --git a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt index 58db2c2350e4..5a415d04fbcf 100644 --- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt +++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt @@ -50,6 +50,21 @@ Charger specific properties: Value type: <u32> Definition: Specifies the DC input current limit in micro-amps. +- qcom,wipower-max-uw + Usage: optional + Value type: <u32> + Definition: Specifies the DC input power limit in micro-watts. + If the value is not present, 8W is used as default. + +- qcom,thermal-mitigation + Usage: optional + Value type: Array of <u32> + Definition: Array of fast charge current limit values for + different system thermal mitigation levels. + This should be a flat array that denotes the + maximum charge current in mA for each thermal + level. + ============================================= Second Level Nodes - SMB2 Charger Peripherals ============================================= @@ -80,9 +95,7 @@ pmicobalt_charger: qcom,qpnp-smb2 { #address-cells = <1>; #size-cells = <1>; - qcom,pmic-revid = <&pmicobalt_revid>; qcom,suspend-input; - qcom,disable-charging; dpdm-supply = <&qusb_phy0>; qcom,chgr@1000 { diff --git a/Documentation/devicetree/bindings/regulator/cprh-kbss-regulator.txt b/Documentation/devicetree/bindings/regulator/cprh-kbss-regulator.txt index b9143cfc2587..833fb645b92a 100644 --- a/Documentation/devicetree/bindings/regulator/cprh-kbss-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/cprh-kbss-regulator.txt @@ -42,10 +42,21 @@ KBSS specific properties: - qcom,apm-threshold-voltage Usage: optional Value type: <u32> - Definition: Specifies the APM threshold voltage in microvolts. If the - VDD_APCC supply voltage is above this level, then the APM is - switched to use VDD_APCC. If VDD_APCC is below this level, - then the APM is switched to use VDD_MX. + Definition: Specifies the APM threshold voltage in microvolts. The + floor to ceiling range for every corner is adjusted to ensure + it does not intersect this voltage. The value of this property + must match with the APM threshold voltage defined in the OSM + device to ensure that if the VDD_APCC supply voltage is above + this level, then the APM is switched to use VDD_APCC and if + VDD_APCC is below this level, then the APM is switched to use + VDD_MX. + +- qcom,apm-crossover-voltage + Usage: required if qcom,apm-threshold-voltage is specified + Value type: <u32> + Definition: Specifies the APM crossover voltage in microvolts which + corresponds to the voltage the VDD supply must be set at + during an APM switch transition. - qcom,apm-hysteresis-voltage Usage: optional diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index b30b6b87add6..7fa51e394f5c 100755 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -351,6 +351,9 @@ Required properties: Required properties: - compatible : "qcom,msm-cpe-lsm" + - qcom,msm-cpe-lsm-id : lsm afe port ID. CPE lsm driver uses + this property to find out the input afe port ID. Currently + only supported values are 1 and 3. * wcd_us_euro_gpio @@ -1367,12 +1370,12 @@ Example: qcom,mbhc-audio-jack-type = "6-pole-jack"; asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>, <&loopback>, <&compress>, <&hostless>, - <&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>; + <&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>, <&cpe3>; asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", "msm-pcm-dsp.2", "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback", "msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe", "msm-lsm-client", "msm-pcm-routing", "msm-cpe-lsm", - "msm-compr-dsp"; + "msm-compr-dsp", "msm-cpe-lsm.3"; asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>, <&dai_mi2s>, <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>, <&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>, @@ -2139,3 +2142,35 @@ Example: asoc-codec-names = "msm-stub-codec.1"; qcom,wsa-max-devs = <0>; }; + +* WCD DSP manager driver + +Required properties: +- compatible : "qcom,wcd-dsp-mgr" +- qcom,wdsp-components : This is phandle list containing the references to the + components of the manager driver. Manager driver will + register to component framework with these phandles. +- qcom,img-filename : String property to provide the dsp image file name that is + to be read from file system and downloaded to dsp memory +Optional properties: +- qcom,wdsp-cmpnt-dev-name : Property that manager driver will parse, but defined + in the child's DT entry that is given to manager driver + with phandle. This property will be used by the manager + driver in case the manager driver cannot match child's + of_node pointer to registered phandle. + +Example: + + qcom,wcd-dsp-mgr { + compatible = "qcom,wcd-dsp-mgr"; + qcom,wdsp-components = <&wcd934x_cdc 0>, + <&wcd_spi_0 1>, + <&glink_spi 2>; + qcom,img-filename = "cpe_9340"; + }; + +Example of child node that would have qcom,wdsp-cmpnt-dev-name property + + wcd934x_cdc: tavil_codec { + qcom,wdsp-cmpnt-dev-name = "tavil_codec"; + }; diff --git a/Documentation/devicetree/bindings/sound/wcd-spi.txt b/Documentation/devicetree/bindings/sound/wcd-spi.txt new file mode 100644 index 000000000000..6ea513b026c2 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wcd-spi.txt @@ -0,0 +1,27 @@ +WCD audio codec SPI driver support + +* wcd_spi + +The wcd_spi device is child device node to the master contoller's device node +and will have properties that the SPI framework or the master controller driver +expects. The properties listed here are specific to wcd-spi driver. + +Required properties: + +- compatible : "qcom,wcd-spi-v2" + +- qcom,mem-base-addr : Defines the memory base address from the SPI + memory map. This will be used as an offset to read + and write memory. + +Example: + +&spi_10 { + status = "ok"; + wcd_spi_0: wcd_spi@0 { + compatible = "qcom,wcd-spi-v2"; + reg = <0>; + spi-max-frequency = <24000000>; + qcom,mem-base-addr = <0x100000>; + }; +}; diff --git a/Documentation/devicetree/bindings/thermal/tsens.txt b/Documentation/devicetree/bindings/thermal/tsens.txt index 9aa52dde2dd4..684bea131405 100644 --- a/Documentation/devicetree/bindings/thermal/tsens.txt +++ b/Documentation/devicetree/bindings/thermal/tsens.txt @@ -34,16 +34,22 @@ Required properties: should be "qcom,msmfalcon-tsens" for falcon TSENS driver. The compatible property is used to identify the respective fusemap to use for the corresponding SoC. -- reg : offset and length of the TSENS registers. -- reg : offset and length of the QFPROM registers used for storing - the calibration data for the individual sensors. +- reg : offset and length of the TSENS registers with associated property in reg-names + as "tsens_physical" and QFPROM registers with associated property in reg-names + as "tsens_eeprom_physical". The efuse registers are used for storing + the calibration data for the individual sensors. If the gain and offset are + programmed by the TSENS control registers then adding the QFPROM register property + is optional. - reg-names : resource names used for the physical address of the TSENS registers, the QFPROM efuse primary calibration address region, Should be "tsens_physical" for physical address of the TSENS, "tsens_eeprom_physical" for physical address where primary calibration data is stored. This includes the backup calibration address region if TSENS calibration data is stored - in the region. + in the region. The reg-name "tsens_eeprom_physical" property is + optional if the gain and offset are programmed by the TSENS + control registers and the status registers directly reports the TSENS + temperature readings. - interrupts : TSENS interrupt to notify Upper/Lower temperature threshold. - interrupt-names: Should be "tsens-upper-lower" for temperature threshold. Add "tsens-critical" for Critical temperature threshold notification @@ -53,7 +59,8 @@ Required properties: - qcom,slope : One point calibration characterized slope data for each sensor used to compute the offset. Slope is represented as ADC code/DegC and the value is multipled by a factor - of 1000. + of 1000. If gain and offset are programmed by the TSENS control + registers then this property is optional. Optional properties: - qcom,calibration-less-mode : If present the pre-characterized data for offsets diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt index a38104faf261..3136687adb57 100644 --- a/Documentation/devicetree/bindings/usb/dwc3.txt +++ b/Documentation/devicetree/bindings/usb/dwc3.txt @@ -6,6 +6,10 @@ DWC3- USB3 CONTROLLER. Complies to the generic USB binding properties Required properties: - compatible: must be "snps,dwc3" - reg : Address and length of the register set for the device + Required regs are: + - "core_base" : USB DWC3 controller register set. + - "ahb2phy_base" : AHB2PHY register base. It is used to update read/write + wait cycle for accessing PHY. - interrupts: Interrupts used by the dwc3 controller. Optional properties: @@ -61,7 +65,10 @@ This is usually a subnode to DWC3 glue to which it is connected. dwc3@4a030000 { compatible = "snps,dwc3"; - reg = <0x4a030000 0xcfff>; + reg = <0x07600000 0xfc000>, + <0x7416000 0x400>; + reg-names = "core_base", + "ahb2phy_base"; interrupts = <0 92 4> usb-phy = <&usb2_phy>, <&usb3,phy>; tx-fifo-resize; diff --git a/Documentation/devicetree/bindings/usb/msm-phy.txt b/Documentation/devicetree/bindings/usb/msm-phy.txt index 35bb94b2ef70..dd9c13b4b5ff 100644 --- a/Documentation/devicetree/bindings/usb/msm-phy.txt +++ b/Documentation/devicetree/bindings/usb/msm-phy.txt @@ -101,6 +101,10 @@ Required properties: Required "supply-name" examples are: "vdd" : vdd supply for SSPHY digital circuit operation "core" : high-voltage analog supply for SSPHY + - clocks: a list of phandles to the PHY clocks. Use as per + Documentation/devicetree/bindings/clock/clock-bindings.txt + - clock-names: Names of the clocks in 1-1 correspondence with the "clocks" + property. Required clocks are "aux_clk" and "pipe_clk". - qcom,vdd-voltage-level: This property must be a list of three integer values (no, min, max) where each value represents either a voltage in microvolts or a value corresponding to voltage corner @@ -119,6 +123,10 @@ Optional properties: - reg: Additional register set of address and length to control QMP PHY are: "tcsr_usb3_dp_phymode" : top-level CSR register to be written to select super speed usb qmp phy. + - clocks: a list of phandles to the PHY clocks. Use as per + Documentation/devicetree/bindings/clock/clock-bindings.txt + - clock-names: Names of the clocks in 1-1 correspondence with the "clocks" + property. Required clocks are "cfg_ahb_clk", "phy_reset" and "phy_phy_reset". - qcom,vbus-valid-override: If present, indicates VBUS pin is not connected to the USB PHY and the controller must rely on external VBUS notification in order to manually relay the notification to the SSPHY. @@ -138,6 +146,17 @@ Example: vdda18-supply = <&pmd9635_l8>; qcom,vdd-voltage-level = <0 900000 1050000>; qcom,vbus-valid-override; + + clocks = <&clock_gcc clk_gcc_usb3_phy_aux_clk>, + <&clock_gcc clk_gcc_usb3_phy_pipe_clk>, + <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>, + <&clock_gcc clk_gcc_usb3_phy_reset>, + <&clock_gcc clk_gcc_usb3phy_phy_reset>, + <&clock_gcc clk_ln_bb_clk1>, + <&clock_gcc clk_gcc_usb3_clkref_clk>; + + clock-names = "aux_clk", "pipe_clk", "cfg_ahb_clk", "phy_reset", + "phy_phy_reset", "ref_clk_src", "ref_clk"; }; QUSB2 High-Speed PHY @@ -157,21 +176,22 @@ Required properties: - clocks: a list of phandles to the PHY clocks. Use as per Documentation/devicetree/bindings/clock/clock-bindings.txt - clock-names: Names of the clocks in 1-1 correspondence with the "clocks" - property. Required clocks are "cfg_ahb_clk" and "phy_reset". + property. Required clock is "phy_reset". - phy_type: Should be one of "ulpi" or "utmi". ChipIdea core uses "ulpi" mode. Optional properties: - - reg: Address and length register set to control QUSB2 PHY - "qscratch_base" : QSCRATCH base register set. + - reg-names: Additional registers corresponding with the following: "tune2_efuse_addr": EFUSE based register address to read TUNE2 parameter. via the QSCRATCH interface. "emu_phy_base" : phy base address used for programming emulation target phy. "ref_clk_addr" : ref_clk bcr address used for on/off ref_clk before reset. - - reg-names: Should be "qscratch_base". The qscratch register bank - allows us to manipulate QUSB PHY bits eg. to enable D+ pull-up using s/w - control in device mode. The reg-names property is required if the - reg property is specified. + - clocks: a list of phandles to the PHY clocks. Use as per + Documentation/devicetree/bindings/clock/clock-bindings.txt + - clock-names: Names of the clocks in 1-1 correspondence with the "clocks" + property. "cfg_ahb_clk", "ref_clk_src" and "ref_clk" are optional clocks. - qcom,qusb-phy-init-seq: QUSB PHY initialization sequence with value,reg pair. + - qcom,qusb-phy-host-init-seq: QUSB PHY initialization sequence for host mode + with value,reg pair. - qcom,emu-init-seq : emulation initialization sequence with value,reg pair. - qcom,phy-pll-reset-seq : emulation PLL reset sequence with value,reg pair. - qcom,emu-dcm-reset-seq : emulation DCM reset sequence with value,reg pair. @@ -185,10 +205,8 @@ Optional properties: Example: qusb_phy: qusb@f9b39000 { compatible = "qcom,qusb2phy"; - reg = <0x00079000 0x7000>, - <0x08af8800 0x400>; - reg-names = "qusb_phy_base", - "qscratch_base"; + reg = <0x00079000 0x7000>; + reg-names = "qusb_phy_base"; vdd-supply = <&pm8994_s2_corner>; vdda18-supply = <&pm8994_l6>; vdda33-supply = <&pm8994_l24>; diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt index 7d323b91f031..2b2bfe428c79 100644 --- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt +++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt @@ -10,6 +10,11 @@ Required properties : "hs_phy_irq" : Interrupt from HS PHY for asynchronous events in LPM. "pwr_event_irq" : Interrupt to controller for asynchronous events in LPM. Used for SS-USB power events. + - clocks: a list of phandles to the controller clocks. Use as per + Documentation/devicetree/bindings/clock/clock-bindings.txt + - clock-names: Names of the clocks in 1-1 correspondence with the "clocks" + property. Required clocks are "xo", "iface_clk", "core_clk", "sleep_clk" + and "utmi_clk". Optional properties : - reg: Additional registers @@ -27,6 +32,10 @@ Optional properties : - interrupt-names : Optional interrupt resource entries are: "pmic_id_irq" : Interrupt from PMIC for external ID pin notification. "ss_phy_irq" : Interrupt from super speed phy for wake up notification. + - clocks: a list of phandles to the controller clocks. Use as per + Documentation/devicetree/bindings/clock/clock-bindings.txt + - clock-names: Names of the clocks in 1-1 correspondence with the "clocks" + property. Optional clocks are "bus_aggr_clk" and "cfg_ahb_clk". - qcom,charging-disabled: If present then battery charging using USB is disabled. - vbus_dwc3-supply: phandle to the 5V VBUS supply regulator used for host mode. @@ -77,6 +86,18 @@ Example MSM USB3.0 controller device node : qcom,msm_bus,vectors = <61 512 0 0>, <61 512 240000000 960000000>; + + clocks = <&clock_gcc clk_gcc_usb30_master_clk>, + <&clock_gcc clk_gcc_cfg_noc_usb3_axi_clk>, + <&clock_gcc clk_gcc_aggre1_usb3_axi_clk>, + <&clock_gcc clk_gcc_usb30_mock_utmi_clk>, + <&clock_gcc clk_gcc_usb30_sleep_clk>, + <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>, + <&clock_gcc clk_cxo_dwc3_clk>; + + clock-names = "core_clk", "iface_clk", "bus_aggr_clk", + "utmi_clk", "sleep_clk", "cfg_ahb_clk", "xo"; + dwc3@f9200000 { compatible = "synopsys,dwc3"; reg = <0xf9200000 0xfc000>; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 8570e6a6bfa4..4097b7cd6454 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -120,6 +120,7 @@ intercontrol Inter Control Group invensense InvenSense Inc. isee ISEE 2007 S.L. isil Intersil +ite ITE Tech. Inc. jedec JEDEC Solid State Technology Association karo Ka-Ro electronics GmbH keymile Keymile GmbH diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 65c1849f8269..024bdaf9af46 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -3936,6 +3936,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted. sector if the number is odd); i = IGNORE_DEVICE (don't bind to this device); + j = NO_REPORT_LUNS (don't use report luns + command, uas only); l = NOT_LOCKABLE (don't try to lock and unlock ejectable media); m = MAX_SECTORS_64 (don't transfer more diff --git a/MAINTAINERS b/MAINTAINERS index d826f1b9eb02..4c3e1d2ac31b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -230,13 +230,13 @@ F: kernel/sys_ni.c ABIT UGURU 1,2 HARDWARE MONITOR DRIVER M: Hans de Goede <hdegoede@redhat.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: drivers/hwmon/abituguru.c ABIT UGURU 3 HARDWARE MONITOR DRIVER M: Alistair John Strachan <alistair@devzero.co.uk> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: drivers/hwmon/abituguru3.c @@ -373,14 +373,14 @@ S: Maintained ADM1025 HARDWARE MONITOR DRIVER M: Jean Delvare <jdelvare@suse.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/adm1025 F: drivers/hwmon/adm1025.c ADM1029 HARDWARE MONITOR DRIVER M: Corentin Labbe <clabbe.montjoie@gmail.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: drivers/hwmon/adm1029.c @@ -425,7 +425,7 @@ F: drivers/video/backlight/adp8860_bl.c ADS1015 HARDWARE MONITOR DRIVER M: Dirk Eibach <eibach@gdsys.de> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/ads1015 F: drivers/hwmon/ads1015.c @@ -438,7 +438,7 @@ F: drivers/macintosh/therm_adt746x.c ADT7475 HARDWARE MONITOR DRIVER M: Jean Delvare <jdelvare@suse.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/adt7475 F: drivers/hwmon/adt7475.c @@ -615,7 +615,7 @@ F: include/linux/ccp.h AMD FAM15H PROCESSOR POWER MONITORING DRIVER M: Andreas Herrmann <herrmann.der.user@googlemail.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/fam15h_power F: drivers/hwmon/fam15h_power.c @@ -779,7 +779,7 @@ F: drivers/input/mouse/bcm5974.c APPLE SMC DRIVER M: Henrik Rydberg <rydberg@bitmath.org> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Odd fixes F: drivers/hwmon/applesmc.c @@ -1777,7 +1777,7 @@ F: include/media/as3645a.h ASC7621 HARDWARE MONITOR DRIVER M: George Joseph <george.joseph@fairview5.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/asc7621 F: drivers/hwmon/asc7621.c @@ -1864,7 +1864,7 @@ F: drivers/net/wireless/ath/carl9170/ ATK0110 HWMON DRIVER M: Luca Tettamanti <kronos.it@gmail.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: drivers/hwmon/asus_atk0110.c @@ -2984,7 +2984,7 @@ F: mm/swap_cgroup.c CORETEMP HARDWARE MONITORING DRIVER M: Fenghua Yu <fenghua.yu@intel.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/coretemp F: drivers/hwmon/coretemp.c @@ -3549,7 +3549,7 @@ T: git git://git.infradead.org/users/vkoul/slave-dma.git DME1737 HARDWARE MONITOR DRIVER M: Juerg Haefliger <juergh@gmail.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/dme1737 F: drivers/hwmon/dme1737.c @@ -4262,7 +4262,7 @@ F: include/video/exynos_mipi* F71805F HARDWARE MONITORING DRIVER M: Jean Delvare <jdelvare@suse.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/f71805f F: drivers/hwmon/f71805f.c @@ -4341,7 +4341,7 @@ F: fs/* FINTEK F75375S HARDWARE MONITOR AND FAN CONTROLLER DRIVER M: Riku Voipio <riku.voipio@iki.fi> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: drivers/hwmon/f75375s.c F: include/linux/f75375s.h @@ -4883,8 +4883,8 @@ F: drivers/media/usb/hackrf/ HARDWARE MONITORING M: Jean Delvare <jdelvare@suse.com> M: Guenter Roeck <linux@roeck-us.net> -L: lm-sensors@lm-sensors.org -W: http://www.lm-sensors.org/ +L: linux-hwmon@vger.kernel.org +W: http://hwmon.wiki.kernel.org/ T: quilt http://jdelvare.nerim.net/devel/linux/jdelvare-hwmon/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git S: Maintained @@ -5393,7 +5393,7 @@ F: drivers/usb/atm/ueagle-atm.c INA209 HARDWARE MONITOR DRIVER M: Guenter Roeck <linux@roeck-us.net> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/ina209 F: Documentation/devicetree/bindings/i2c/ina209.txt @@ -5401,7 +5401,7 @@ F: drivers/hwmon/ina209.c INA2XX HARDWARE MONITOR DRIVER M: Guenter Roeck <linux@roeck-us.net> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/ina2xx F: drivers/hwmon/ina2xx.c @@ -5884,7 +5884,7 @@ F: drivers/isdn/hardware/eicon/ IT87 HARDWARE MONITORING DRIVER M: Jean Delvare <jdelvare@suse.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/it87 F: drivers/hwmon/it87.c @@ -5920,7 +5920,7 @@ F: drivers/media/dvb-frontends/ix2505v* JC42.4 TEMPERATURE SENSOR DRIVER M: Guenter Roeck <linux@roeck-us.net> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: drivers/hwmon/jc42.c F: Documentation/hwmon/jc42 @@ -5970,14 +5970,14 @@ F: drivers/tty/serial/jsm/ K10TEMP HARDWARE MONITORING DRIVER M: Clemens Ladisch <clemens@ladisch.de> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/k10temp F: drivers/hwmon/k10temp.c K8TEMP HARDWARE MONITORING DRIVER M: Rudolf Marek <r.marek@assembler.cz> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/k8temp F: drivers/hwmon/k8temp.c @@ -6485,27 +6485,27 @@ F: net/llc/ LM73 HARDWARE MONITOR DRIVER M: Guillaume Ligneul <guillaume.ligneul@gmail.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: drivers/hwmon/lm73.c LM78 HARDWARE MONITOR DRIVER M: Jean Delvare <jdelvare@suse.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/lm78 F: drivers/hwmon/lm78.c LM83 HARDWARE MONITOR DRIVER M: Jean Delvare <jdelvare@suse.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/lm83 F: drivers/hwmon/lm83.c LM90 HARDWARE MONITOR DRIVER M: Jean Delvare <jdelvare@suse.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/lm90 F: Documentation/devicetree/bindings/hwmon/lm90.txt @@ -6513,7 +6513,7 @@ F: drivers/hwmon/lm90.c LM95234 HARDWARE MONITOR DRIVER M: Guenter Roeck <linux@roeck-us.net> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/lm95234 F: drivers/hwmon/lm95234.c @@ -6580,7 +6580,7 @@ F: drivers/scsi/sym53c8xx_2/ LTC4261 HARDWARE MONITOR DRIVER M: Guenter Roeck <linux@roeck-us.net> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/ltc4261 F: drivers/hwmon/ltc4261.c @@ -6749,28 +6749,28 @@ F: include/uapi/linux/matroxfb.h MAX16065 HARDWARE MONITOR DRIVER M: Guenter Roeck <linux@roeck-us.net> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/max16065 F: drivers/hwmon/max16065.c MAX20751 HARDWARE MONITOR DRIVER M: Guenter Roeck <linux@roeck-us.net> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/max20751 F: drivers/hwmon/max20751.c MAX6650 HARDWARE MONITOR AND FAN CONTROLLER DRIVER M: "Hans J. Koch" <hjk@hansjkoch.de> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/max6650 F: drivers/hwmon/max6650.c MAX6697 HARDWARE MONITOR DRIVER M: Guenter Roeck <linux@roeck-us.net> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/max6697 F: Documentation/devicetree/bindings/i2c/max6697.txt @@ -7303,7 +7303,7 @@ F: drivers/scsi/NCR_D700.* NCT6775 HARDWARE MONITOR DRIVER M: Guenter Roeck <linux@roeck-us.net> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/nct6775 F: drivers/hwmon/nct6775.c @@ -8064,7 +8064,7 @@ F: drivers/video/logo/logo_parisc* PC87360 HARDWARE MONITORING DRIVER M: Jim Cromie <jim.cromie@gmail.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/pc87360 F: drivers/hwmon/pc87360.c @@ -8076,7 +8076,7 @@ F: drivers/char/pc8736x_gpio.c PC87427 HARDWARE MONITORING DRIVER M: Jean Delvare <jdelvare@suse.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/pc87427 F: drivers/hwmon/pc87427.c @@ -8415,8 +8415,8 @@ F: drivers/rtc/rtc-puv3.c PMBUS HARDWARE MONITORING DRIVERS M: Guenter Roeck <linux@roeck-us.net> -L: lm-sensors@lm-sensors.org -W: http://www.lm-sensors.org/ +L: linux-hwmon@vger.kernel.org +W: http://hwmon.wiki.kernel.org/ W: http://www.roeck-us.net/linux/drivers/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git S: Maintained @@ -8610,7 +8610,7 @@ F: drivers/media/usb/pwc/* PWM FAN DRIVER M: Kamil Debski <k.debski@samsung.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Supported F: Documentation/devicetree/bindings/hwmon/pwm-fan.txt F: Documentation/hwmon/pwm-fan @@ -9882,28 +9882,28 @@ F: Documentation/devicetree/bindings/media/i2c/nokia,smia.txt SMM665 HARDWARE MONITOR DRIVER M: Guenter Roeck <linux@roeck-us.net> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/smm665 F: drivers/hwmon/smm665.c SMSC EMC2103 HARDWARE MONITOR DRIVER M: Steve Glendinning <steve.glendinning@shawell.net> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/emc2103 F: drivers/hwmon/emc2103.c SMSC SCH5627 HARDWARE MONITOR DRIVER M: Hans de Goede <hdegoede@redhat.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Supported F: Documentation/hwmon/sch5627 F: drivers/hwmon/sch5627.c SMSC47B397 HARDWARE MONITOR DRIVER M: Jean Delvare <jdelvare@suse.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/smsc47b397 F: drivers/hwmon/smsc47b397.c @@ -10830,7 +10830,7 @@ F: include/linux/mmc/sh_mobile_sdhi.h TMP401 HARDWARE MONITOR DRIVER M: Guenter Roeck <linux@roeck-us.net> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/tmp401 F: drivers/hwmon/tmp401.c @@ -11564,14 +11564,14 @@ F: Documentation/networking/vrf.txt VT1211 HARDWARE MONITOR DRIVER M: Juerg Haefliger <juergh@gmail.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/vt1211 F: drivers/hwmon/vt1211.c VT8231 HARDWARE MONITOR DRIVER M: Roger Lucas <vt8231@hiddenengine.co.uk> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: drivers/hwmon/vt8231.c @@ -11590,21 +11590,21 @@ F: drivers/w1/ W83791D HARDWARE MONITORING DRIVER M: Marc Hulsman <m.hulsman@tudelft.nl> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/w83791d F: drivers/hwmon/w83791d.c W83793 HARDWARE MONITORING DRIVER M: Rudolf Marek <r.marek@assembler.cz> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/hwmon/w83793 F: drivers/hwmon/w83793.c W83795 HARDWARE MONITORING DRIVER M: Jean Delvare <jdelvare@suse.com> -L: lm-sensors@lm-sensors.org +L: linux-hwmon@vger.kernel.org S: Maintained F: drivers/hwmon/w83795.c @@ -1,6 +1,6 @@ VERSION = 4 PATCHLEVEL = 4 -SUBLEVEL = 6 +SUBLEVEL = 8 EXTRAVERSION = NAME = Blurry Fish Butt diff --git a/arch/arc/include/asm/bitops.h b/arch/arc/include/asm/bitops.h index 57c1f33844d4..0352fb8d21b9 100644 --- a/arch/arc/include/asm/bitops.h +++ b/arch/arc/include/asm/bitops.h @@ -35,21 +35,6 @@ static inline void op##_bit(unsigned long nr, volatile unsigned long *m)\ \ m += nr >> 5; \ \ - /* \ - * ARC ISA micro-optimization: \ - * \ - * Instructions dealing with bitpos only consider lower 5 bits \ - * e.g (x << 33) is handled like (x << 1) by ASL instruction \ - * (mem pointer still needs adjustment to point to next word) \ - * \ - * Hence the masking to clamp @nr arg can be elided in general. \ - * \ - * However if @nr is a constant (above assumed in a register), \ - * and greater than 31, gcc can optimize away (x << 33) to 0, \ - * as overflow, given the 32-bit ISA. Thus masking needs to be \ - * done for const @nr, but no code is generated due to gcc \ - * const prop. \ - */ \ nr &= 0x1f; \ \ __asm__ __volatile__( \ diff --git a/arch/arc/include/asm/io.h b/arch/arc/include/asm/io.h index 694ece8a0243..27b17adea50d 100644 --- a/arch/arc/include/asm/io.h +++ b/arch/arc/include/asm/io.h @@ -129,15 +129,23 @@ static inline void __raw_writel(u32 w, volatile void __iomem *addr) #define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); }) /* - * Relaxed API for drivers which can handle any ordering themselves + * Relaxed API for drivers which can handle barrier ordering themselves + * + * Also these are defined to perform little endian accesses. + * To provide the typical device register semantics of fixed endian, + * swap the byte order for Big Endian + * + * http://lkml.kernel.org/r/201603100845.30602.arnd@arndb.de */ #define readb_relaxed(c) __raw_readb(c) -#define readw_relaxed(c) __raw_readw(c) -#define readl_relaxed(c) __raw_readl(c) +#define readw_relaxed(c) ({ u16 __r = le16_to_cpu((__force __le16) \ + __raw_readw(c)); __r; }) +#define readl_relaxed(c) ({ u32 __r = le32_to_cpu((__force __le32) \ + __raw_readl(c)); __r; }) #define writeb_relaxed(v,c) __raw_writeb(v,c) -#define writew_relaxed(v,c) __raw_writew(v,c) -#define writel_relaxed(v,c) __raw_writel(v,c) +#define writew_relaxed(v,c) __raw_writew((__force u16) cpu_to_le16(v),c) +#define writel_relaxed(v,c) __raw_writel((__force u32) cpu_to_le32(v),c) #include <asm-generic/io.h> diff --git a/arch/arm/boot/dts/at91-sama5d3_xplained.dts b/arch/arm/boot/dts/at91-sama5d3_xplained.dts index ff888d21c786..f3e2b96c06a3 100644 --- a/arch/arm/boot/dts/at91-sama5d3_xplained.dts +++ b/arch/arm/boot/dts/at91-sama5d3_xplained.dts @@ -303,6 +303,7 @@ regulator-name = "mmc0-card-supply"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; + regulator-always-on; }; gpio_keys { diff --git a/arch/arm/boot/dts/at91-sama5d4_xplained.dts b/arch/arm/boot/dts/at91-sama5d4_xplained.dts index 569026e8f96c..da84e65b56ef 100644 --- a/arch/arm/boot/dts/at91-sama5d4_xplained.dts +++ b/arch/arm/boot/dts/at91-sama5d4_xplained.dts @@ -268,5 +268,6 @@ regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; vin-supply = <&vcc_3v3_reg>; + regulator-always-on; }; }; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi new file mode 100644 index 000000000000..90df1d0c1ac0 --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-ascent-3450mah.dtsi @@ -0,0 +1,80 @@ +/* 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. + */ + +qcom,ascent_3450mah { + /* #Ascent_860_82209_0000_3450mAh_averaged_MasterSlave_Jul20th2016*/ + qcom,max-voltage-uv = <4350000>; + qcom,nom-batt-capacity-mah = <3450>; + qcom,batt-id-kohm = <60>; + qcom,battery-beta = <3435>; + qcom,battery-type = "ascent_860_82209_0000_3450mah"; + qcom,checksum = <0xD1D9>; + qcom,gui-version = "PMI8998GUI - 0.0.0.82"; + qcom,fg-profile-data = [ + 2C 1F 3F FC + E9 03 A1 FD + 58 1D FD F5 + 27 12 2C 14 + 3F 18 FF 22 + 9B 45 A3 52 + 55 00 00 00 + 0E 00 00 00 + 00 00 1C AC + F7 CD 71 B5 + 1A 00 0C 00 + 3C EB 54 E4 + EC 05 7F FA + 76 05 F5 02 + CA F3 82 3A + 2A 09 40 40 + 07 00 05 00 + 58 1F 42 06 + 85 03 35 F4 + 4D 1D 37 F2 + 23 0A 79 15 + B7 18 32 23 + 26 45 72 53 + 55 00 00 00 + 0D 00 00 00 + 00 00 13 CC + 03 00 98 BD + 16 00 00 00 + 3C EB 54 E4 + 9F FC A3 F3 + 0F FC DF FA + FF E5 A9 23 + CB 33 08 33 + 07 10 00 00 + 81 0D 99 45 + 16 00 19 00 + 75 01 0A FA + FF 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi new file mode 100644 index 000000000000..2c1edde56d6a --- /dev/null +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi @@ -0,0 +1,80 @@ +/* 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. + */ + +qcom,itech_3000mah { + /* #Itech_B00826LF_3000mAh_ver1660_averaged_MasterSlave_Jul20th2016*/ + qcom,max-voltage-uv = <4350000>; + qcom,nom-batt-capacity-mah = <3000>; + qcom,batt-id-kohm = <100>; + qcom,battery-beta = <3450>; + qcom,battery-type = "itech_b00826lf_3000mah_ver1660"; + qcom,checksum = <0xE06B>; + qcom,gui-version = "PMI8998GUI - 0.0.0.82"; + qcom,fg-profile-data = [ + A4 1F 6E 05 + 9C 0A 16 06 + 32 1D 24 E5 + 61 0B 1B 15 + AD 17 8C 22 + EB 3C 87 4A + 5B 00 00 00 + 12 00 00 00 + 00 00 62 C2 + 0C CD D8 C2 + 19 00 0C 00 + 7E 00 C7 EC + E3 05 5D FA + 97 F5 12 12 + C2 05 90 3B + 22 09 40 40 + 07 00 05 00 + 7D 1F DE 05 + 3F 0A 73 06 + 72 1D E2 F5 + 6F 12 BF 1D + 88 18 FB 22 + 8D 45 C6 52 + 54 00 00 00 + 0F 00 00 00 + 00 00 BD CD + 55 C2 5D C5 + 14 00 00 00 + 7E 00 C7 EC + 60 06 BB 00 + B3 FC 61 03 + 6A 06 78 1B + B3 33 08 33 + 07 10 00 00 + 3E 0B 99 45 + 14 00 19 00 + AE 01 0A FA + FF 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/msm-gdsc-cobalt.dtsi b/arch/arm/boot/dts/qcom/msm-gdsc-cobalt.dtsi index a2c8c89b08a3..c86351e48d5f 100644 --- a/arch/arm/boot/dts/qcom/msm-gdsc-cobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msm-gdsc-cobalt.dtsi @@ -60,6 +60,7 @@ <0xc8ce024 0x4>; reg-names = "base", "hw_ctrl_addr"; qcom,no-status-check-on-disable; + qcom,gds-timeout = <500>; status = "disabled"; }; diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi index fad834199be5..c724ce5a8ad9 100644 --- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi @@ -297,46 +297,40 @@ #address-cells = <1>; #size-cells = <0>; #io-channel-cells = <1>; + }; - /* Channel node */ - batt_id { - channel = <3>; - }; - - batt_therm { - channel = <4>; - }; - - skin_temp { - channel = <5>; - }; - - usbin_v { - channel = <6>; - }; - - usbin_i { - channel = <7>; - }; - - dcin_v { - channel = <8>; - }; - - dcin_i { - channel = <9>; - }; + pmicobalt_fg: qpnp,fg { + compatible = "qcom,fg-gen3"; + #address-cells = <1>; + #size-cells = <1>; + qcom,pmic-revid = <&pmicobalt_revid>; + io-channels = <&pmicobalt_rradc 0>; + io-channel-names = "rradc_batt_id"; + status = "okay"; - die_temp { - channel = <0xa>; + qcom,fg-batt-soc@4000 { + status = "okay"; + reg = <0x4000 0x100>; + interrupts = <0x2 0x40 0x0 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x40 0x1 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x40 0x2 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x40 0x3 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "soc-update", + "soc-ready", + "bsoc-delta", + "msoc-delta"; }; - chg_temp { - channel = <0xb>; + qcom,fg-batt-info@4100 { + status = "okay"; + reg = <0x4100 0x100>; + interrupts = <0x2 0x41 0x3 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "batt-missing"; }; - gpio { - channel = <0xc>; + qcom,fg-memif@4400 { + status = "okay"; + reg = <0x4400 0x100>; }; }; }; @@ -599,7 +593,16 @@ status = "okay"; reg = <0xd300 0x100>; label = "flash"; + interrupts = <0x3 0xd3 0x0 IRQ_TYPE_EDGE_RISING>, + <0x3 0xd3 0x3 IRQ_TYPE_EDGE_RISING>, + <0x3 0xd3 0x4 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "led-fault-irq", + "all-ramp-down-done-irq", + "all-ramp-up-done-irq"; qcom,hdrm-auto-mode; + qcom,short-circuit-det; + qcom,open-circuit-det; + qcom,vph-droop-det; qcom,isc-delay = <192>; pmicobalt_flash0: qcom,flash_0 { diff --git a/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi b/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi index 25e0d99987db..07423a601b35 100644 --- a/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-gpu.dtsi @@ -79,6 +79,8 @@ qcom,snapshot-size = <1048576>; //bytes + qcom,gpu-qdss-stm = <0x081c0000 0x40000>; // base addr, size + /* Trace bus */ coresight-id = <300>; coresight-name = "coresight-gfx"; diff --git a/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi index 55fbca7dc9a1..96279288d336 100644 --- a/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi @@ -719,7 +719,6 @@ "SpkrLeft IN", "SPK1 OUT", "SpkrRight IN", "SPK2 OUT"; - qcom,hdmi-audio-rx; asoc-codec = <&stub_codec>, <&hdmi_audio>; asoc-codec-names = "msm-stub-codec.1", "msm-hdmi-audio-codec-rx"; qcom,hph-en1-gpio = <&pmi8994_gpios 10 0>; diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi index 2da89bd9ac6e..9bcc375e275c 100644 --- a/arch/arm/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996.dtsi @@ -2059,11 +2059,9 @@ qusb_phy0: qusb@7411000 { compatible = "qcom,qusb2phy"; reg = <0x07411000 0x180>, - <0x06af8800 0x400>, <0x0007024c 0x4>, <0x00388018 0x4>; reg-names = "qusb_phy_base", - "qscratch_base", "tune2_efuse_addr", "ref_clk_addr"; vdd-supply = <&pm8994_s2_corner>; @@ -2096,11 +2094,9 @@ qusb_phy1: qusb@7412000 { compatible = "qcom,qusb2phy"; reg = <0x07412000 0x180>, - <0x076f8800 0x400>, <0x0007024c 0x4>, <0x00388014 0x4>; reg-names = "qusb_phy_base", - "qscratch_base", "tune2_efuse_addr", "ref_clk_addr"; vdd-supply = <&pm8994_s2_corner>; @@ -3004,13 +3000,14 @@ qcom,tasha-mclk-clk-freq = <9600000>; asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>, <&loopback>, <&compress>, <&hostless>, - <&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>, - <&pcmnoirq>; + <&afe>, <&lsm>, <&routing>, <&cpe>, + <&compr>, <&pcmnoirq>, <&cpe3>; asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", "msm-pcm-dsp.2", "msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback", "msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe", "msm-lsm-client", "msm-pcm-routing", "msm-cpe-lsm", - "msm-compr-dsp", "msm-pcm-dsp-noirq"; + "msm-compr-dsp", "msm-pcm-dsp-noirq", + "msm-cpe-lsm.3"; asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>, <&dai_mi2s>, <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>, <&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>, @@ -3122,6 +3119,12 @@ cpe: qcom,msm-cpe-lsm { compatible = "qcom,msm-cpe-lsm"; + qcom,msm-cpe-lsm-id = <1>; + }; + + cpe3: qcom,msm-cpe-lsm@3 { + compatible = "qcom,msm-cpe-lsm"; + qcom,msm-cpe-lsm-id = <3>; }; qcom,msm-dai-q6 { diff --git a/arch/arm/boot/dts/qcom/msmcobalt-bus.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-bus.dtsi index 3c39a61c4328..86decf438430 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-bus.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-bus.dtsi @@ -131,6 +131,16 @@ clock-names = "bus_clk", "bus_a_clk"; clocks = <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_gcc clk_mmssnoc_axi_a_clk>; + clk-mdss-axi-no-rate-supply = + <&gdsc_mdss>; + clk-mdss-ahb-no-rate-supply = + <&gdsc_mdss>; + clk-camss-ahb-no-rate-supply = + <&gdsc_camss_top>; + clk-video-ahb-no-rate-supply = + <&gdsc_venus>; + clk-video-axi-no-rate-supply = + <&gdsc_venus>; qcom,node-qos-clks { clock-names = "clk-noc-cfg-ahb-no-rate", @@ -141,6 +151,7 @@ "clk-video-ahb-no-rate", "clk-video-axi-no-rate"; clocks = + <&clock_gcc clk_mmssnoc_axi_clk>, <&clock_gcc clk_gcc_mmss_noc_cfg_ahb_clk>, <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_mdss_ahb_clk>, diff --git a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi index 3ed038069319..aeb9b1c0207f 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi @@ -16,6 +16,7 @@ cell-index = <0>; compatible = "qcom,camera-flash"; qcom,flash-source = <&pmicobalt_flash0 &pmicobalt_flash1>; + qcom,torch-source = <&pmicobalt_torch0 &pmicobalt_torch1>; qcom,switch-source = <&pmicobalt_switch0>; status = "ok"; }; @@ -24,6 +25,7 @@ cell-index = <1>; compatible = "qcom,camera-flash"; qcom,flash-source = <&pmicobalt_flash2>; + qcom,torch-source = <&pmicobalt_torch2>; qcom,switch-source = <&pmicobalt_switch1>; status = "ok"; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi index d152c0049f96..3887999e9bab 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi @@ -16,6 +16,7 @@ cell-index = <0>; compatible = "qcom,camera-flash"; qcom,flash-source = <&pmicobalt_flash0 &pmicobalt_flash1>; + qcom,torch-source = <&pmicobalt_torch0 &pmicobalt_torch1>; qcom,switch-source = <&pmicobalt_switch0>; status = "ok"; }; @@ -24,6 +25,7 @@ cell-index = <1>; compatible = "qcom,camera-flash"; qcom,flash-source = <&pmicobalt_flash2>; + qcom,torch-source = <&pmicobalt_torch2>; qcom,switch-source = <&pmicobalt_switch1>; status = "ok"; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts index aebd9a1440de..10edf71da2f3 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts +++ b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts @@ -17,7 +17,22 @@ #include "msmcobalt-cdp.dtsi" / { - model = "Qualcomm Technologies, Inc. MSM cobalt"; + model = "Qualcomm Technologies, Inc. MSM COBALT v1 CDP"; compatible = "qcom,msmcobalt-cdp", "qcom,msmcobalt", "qcom,cdp"; qcom,board-id = <1 0>; }; + +&qusb_phy0 { + qcom,qusb-phy-host-init-seq = + /* value reg_offsets> */ + <0x63 0x210 + 0x13 0x04 + 0x7c 0x18c + 0x80 0x2c + 0x0a 0x184 + 0x8c 0x21c + 0x05 0x23c + 0x03 0x240 + 0xff 0x218 + 0x62 0x210>; +}; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi index c0777a7e193a..4afaa3aa51be 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-coresight.dtsi @@ -20,6 +20,7 @@ reg-names = "tmc-base", "bam-base"; arm,buffer-size = <0x400000>; + arm,sg-enable; coresight-ctis = <&cti0 &cti8>; @@ -75,6 +76,8 @@ coresight-name = "coresight-tmc-etf"; + arm,default-sink; + clocks = <&clock_gcc clk_qdss_clk>, <&clock_gcc clk_qdss_a_clk>; clock-names = "apb_pclk", "core_a_clk"; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts index e5708fc8d743..ea4047df25f6 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts +++ b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts @@ -17,7 +17,22 @@ #include "msmcobalt-mtp.dtsi" / { - model = "Qualcomm Technologies, Inc. MSM cobalt v1"; + model = "Qualcomm Technologies, Inc. MSM COBALT v1 MTP"; compatible = "qcom,msmcobalt-mtp", "qcom,msmcobalt", "qcom,mtp"; qcom,board-id = <8 0>; }; + +&qusb_phy0 { + qcom,qusb-phy-host-init-seq = + /* value reg_offsets> */ + <0x63 0x210 + 0x13 0x04 + 0x7c 0x18c + 0x80 0x2c + 0x0a 0x184 + 0x8c 0x21c + 0x05 0x23c + 0x03 0x240 + 0xff 0x218 + 0x62 0x210>; +}; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi index 6833bd1d7f4a..50924b1667a4 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi @@ -501,3 +501,11 @@ }; }; }; + +/{ + mtp_batterydata: qcom,battery-data { + qcom,batt-id-range-pct = <15>; + #include "fg-gen3-batterydata-itech-3000mah.dtsi" + #include "fg-gen3-batterydata-ascent-3450mah.dtsi" + }; +}; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi index 1094d96bd100..4cbd17f80cb8 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi @@ -185,7 +185,7 @@ config { pins = "gpio87", "gpio88"; drive-strength = <2>; - bias-pull-up; + bias-disable; }; }; }; @@ -1245,7 +1245,7 @@ config { pins = "gpio45", "gpio46", "gpio47", "gpio48"; drive-strength = <2>; - bias-disable; + bias-pull-up; }; }; @@ -1425,6 +1425,84 @@ }; }; + mdss_dp_aux_active: mdss_dp_aux_active { + mux { + pins = "gpio77", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio77", "gpio78"; + bias-disable = <0>; /* no pull */ + drive-strength = <8>; + }; + }; + + mdss_dp_aux_suspend: mdss_dp_aux_suspend { + mux { + pins = "gpio77", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio77", "gpio78"; + bias-pull-down; + drive-strength = <2>; + }; + }; + + mdss_dp_usbplug_cc_active: mdss_dp_usbplug_cc_active { + mux { + pins = "gpio38"; + function = "usb_phy"; + }; + + config { + pins = "gpio38"; + bias-disable; + drive-strength = <16>; + }; + }; + + mdss_dp_usbplug_cc_suspend: mdss_dp_usbplug_cc_suspend { + mux { + pins = "gpio38"; + function = "usb_phy"; + }; + + config { + pins = "gpio38"; + bias-pull-down; + drive-strength = <2>; + }; + }; + + mdss_dp_hpd_active: mdss_dp_hpd_active { + mux { + pins = "gpio34"; + function = "edp_hot"; + }; + + config { + pins = "gpio34"; + bias-pull-down; + drive-strength = <16>; + }; + }; + + mdss_dp_hpd_suspend: mdss_dp_hpd_suspend { + mux { + pins = "gpio34"; + function = "edp_hot"; + }; + + config { + pins = "gpio34"; + bias-pull-down; + drive-strength = <2>; + }; + }; + blsp2_uart3_active: blsp2_uart3_active { mux { pins = "gpio49", "gpio50", "gpio51", "gpio52"; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi index 1720aca3b298..425a902568ae 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi @@ -11,3 +11,61 @@ */ #include "msmcobalt-mtp.dtsi" + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dsi { + hw-config = "split_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_dual_nt35597_truly_video>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 94 0>; + qcom,platform-te-gpio = <&tlmm 10 0>; + qcom,panel-mode-gpio = <&tlmm 91 0>; +}; + +&mdss_dsi1 { + qcom,dsi-pref-prim-pan = <&dsi_dual_nt35597_truly_video>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 94 0>; + qcom,platform-te-gpio = <&tlmm 10 0>; + qcom,panel-mode-gpio = <&tlmm 91 0>; +}; + +&dsi_sharp_4k_dsc_video { + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; +}; + +&dsi_sharp_4k_dsc_cmd { + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; +}; + +&dsi_dual_nt35597_truly_video { + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-mode-sel-gpio-state = "dual_port"; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; +}; + +&dsi_dual_nt35597_truly_cmd { + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-mode-sel-gpio-state = "dual_port"; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; +}; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi index 12ee61b34d8c..5833b30d1fd1 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-regulator.dtsi @@ -576,6 +576,7 @@ qcom,cpr-voltage-settling-time = <1760>; qcom,apm-threshold-voltage = <832000>; + qcom,apm-crossover-voltage = <880000>; qcom,apm-hysteresis-voltage = <32000>; qcom,voltage-step = <4000>; qcom,voltage-base = <352000>; @@ -737,6 +738,7 @@ qcom,cpr-voltage-settling-time = <1760>; qcom,apm-threshold-voltage = <832000>; + qcom,apm-crossover-voltage = <880000>; qcom,apm-hysteresis-voltage = <32000>; qcom,voltage-step = <4000>; qcom,voltage-base = <352000>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2-cdp.dts b/arch/arm/boot/dts/qcom/msmcobalt-v2-cdp.dts index d47213603bdf..7cb8f107abc4 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-v2-cdp.dts +++ b/arch/arm/boot/dts/qcom/msmcobalt-v2-cdp.dts @@ -17,7 +17,7 @@ #include "msmcobalt-cdp.dtsi" / { - model = "Qualcomm Technologies, Inc. MSM cobalt v2"; + model = "Qualcomm Technologies, Inc. MSM COBALT v2 CDP"; compatible = "qcom,msmcobalt-cdp", "qcom,msmcobalt", "qcom,cdp"; qcom,board-id = <1 0>; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2-mtp.dts b/arch/arm/boot/dts/qcom/msmcobalt-v2-mtp.dts index eb48e4444ef8..96e671f04df3 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-v2-mtp.dts +++ b/arch/arm/boot/dts/qcom/msmcobalt-v2-mtp.dts @@ -17,7 +17,7 @@ #include "msmcobalt-mtp.dtsi" / { - model = "Qualcomm Technologies, Inc. MSM cobalt v2"; + model = "Qualcomm Technologies, Inc. MSM COBALT v2 MTP"; compatible = "qcom,msmcobalt-mtp", "qcom,msmcobalt", "qcom,mtp"; qcom,board-id = <8 0>; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi index ee31a3059406..8311d21a262c 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi @@ -25,6 +25,7 @@ qcom,firmware-name = "venus"; qcom,never-unload-fw; qcom,sw-power-collapse; + qcom,debug-timeout; qcom,reg-presets = <0x80124 0x0002000>, <0x80550 0x0000001>, diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi index e748783b0c7d..3bbc98788b9f 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi @@ -716,6 +716,7 @@ reg = <0x100000 0xb0000>; reg-names = "cc_base"; vdd_dig-supply = <&pmcobalt_s1_level>; + vdd_dig_ao-supply = <&pmcobalt_s1_level_ao>; #clock-cells = <1>; }; @@ -760,7 +761,7 @@ vdd_gpucc-supply = <&gfx_vreg>; vdd_mx-supply = <&pmcobalt_s9_level>; vdd_gpu_mx-supply = <&pmcobalt_s9_level>; - qcom,gfx3d_clk_src-opp-store-vcorner = <&msm_gpu>; + qcom,gfx3d_clk_src-opp-handle = <&msm_gpu>; qcom,gfxfreq-speedbin0 = < 0 0 0 >, < 171000000 1 RPM_SMD_REGULATOR_LEVEL_SVS >, @@ -804,21 +805,21 @@ < 576000000 0x0504001e 0x03200020 0x1 >, < 633600000 0x05040021 0x03200020 0x1 >, < 710400000 0x05040025 0x03200020 0x1 >, - < 806400000 0x0504002a 0x04200020 0x2 >, - < 883200000 0x0404002e 0x04250025 0x2 >, - < 960000000 0x04040032 0x05280028 0x2 >, - < 1036800000 0x04040036 0x052b002b 0x3 >, - < 1113600000 0x0404003a 0x052e002e 0x3 >, - < 1190400000 0x0404003e 0x06320032 0x3 >, - < 1248000000 0x04040041 0x06340034 0x3 >, - < 1324800000 0x04040045 0x06370037 0x3 >, - < 1401600000 0x04040049 0x073a003a 0x3 >, - < 1478400000 0x0404004d 0x073e003e 0x3 >, - < 1574400000 0x04040052 0x08420042 0x4 >, - < 1651200000 0x04040056 0x08450045 0x4 >, - < 1728000000 0x0404005a 0x08480048 0x4 >, - < 1804800000 0x0404005e 0x094b004b 0x4 >, - < 1881600000 0x04040062 0x094e004e 0x4 >; + < 806400000 0x0504002a 0x04200020 0x1 >, + < 883200000 0x0404002e 0x04250025 0x1 >, + < 960000000 0x04040032 0x05280028 0x1 >, + < 1036800000 0x04040036 0x052b002b 0x2 >, + < 1113600000 0x0404003a 0x052e002e 0x2 >, + < 1190400000 0x0404003e 0x06320032 0x2 >, + < 1248000000 0x04040041 0x06340034 0x2 >, + < 1324800000 0x04040045 0x06370037 0x2 >, + < 1401600000 0x04040049 0x073a003a 0x2 >, + < 1478400000 0x0404004d 0x073e003e 0x2 >, + < 1574400000 0x04040052 0x08420042 0x2 >, + < 1651200000 0x04040056 0x08450045 0x2 >, + < 1728000000 0x0404005a 0x08480048 0x2 >, + < 1804800000 0x0404005e 0x094b004b 0x3 >, + < 1881600000 0x04040062 0x094e004e 0x3 >; qcom,perfcl-speedbin0-v0 = < 300000000 0x0004000f 0x01200020 0x1 >, @@ -839,10 +840,10 @@ < 1401600000 0x04040049 0x073a003a 0x2 >, < 1478400000 0x0404004d 0x073e003e 0x2 >, < 1536000000 0x04040050 0x07400040 0x2 >, - < 1632000000 0x04040055 0x08440044 0x3 >, - < 1708800000 0x04040059 0x08470047 0x3 >, - < 1785600000 0x0404005d 0x094a004a 0x3 >, - < 1862400000 0x04040061 0x094e004e 0x3 >, + < 1632000000 0x04040055 0x08440044 0x2 >, + < 1708800000 0x04040059 0x08470047 0x2 >, + < 1785600000 0x0404005d 0x094a004a 0x2 >, + < 1862400000 0x04040061 0x094e004e 0x2 >, < 1939200000 0x04040065 0x09510051 0x3 >, < 2016000000 0x04040069 0x0a540054 0x3 >, < 2092800000 0x0404006d 0x0a570057 0x3 >; @@ -888,6 +889,7 @@ <0x8fff0036 0x8fff003a 0x0fff0036>, <0x8fff003d 0x8fff0041 0x0fff003d>; + qcom,apm-threshold-voltage = <832000>; qcom,boost-fsm-en; qcom,safe-fsm-en; qcom,ps-fsm-en; @@ -900,13 +902,11 @@ qcom,perfcl-apcs-mem-acc-cfg = <0x179d1368 0x179d136C 0x179d1370>; qcom,pwrcl-apcs-mem-acc-val = - <0x00000000 0x10000000 0x10000000>, - <0x00000000 0x10000000 0x10000000>, + <0x00000000 0x80000000 0x80000000>, <0x00000000 0x00000000 0x00000000>, <0x00000000 0x00000001 0x00000001>; qcom,perfcl-apcs-mem-acc-val = - <0x00000000 0x00000000 0x10000000>, - <0x00000000 0x00000000 0x10000000>, + <0x00000000 0x00000000 0x80000000>, <0x00000000 0x00000000 0x00000000>, <0x00000000 0x00000000 0x00000001>; @@ -966,8 +966,12 @@ qcom,do-not-use-ch-gsi-20; qcom,ipa-wdi2; qcom,use-64-bit-dma-mask; - clock-names = "core_clk"; - clocks = <&clock_gcc clk_ipa_clk>; + clocks = <&clock_gcc clk_ipa_clk>, + <&clock_gcc clk_aggre2_noc_clk>; + clock-names = "core_clk", "smmu_clk"; + qcom,arm-smmu; + qcom,smmu-disable-htw; + qcom,smmu-s1-bypass; qcom,msm-bus,name = "ipa"; qcom,msm-bus,num-cases = <4>; qcom,msm-bus,num-paths = <3>; @@ -979,15 +983,15 @@ /* SVS */ <90 512 80000 640000>, <90 585 80000 640000>, - <1 676 80000 160000>, + <1 676 80000 80000>, /* NOMINAL */ <90 512 206000 960000>, <90 585 206000 960000>, - <1 676 206000 200000>, + <1 676 206000 160000>, /* TURBO */ <90 512 206000 3600000>, <90 585 206000 3600000>, - <1 676 206000 960000>; + <1 676 206000 300000>; qcom,bus-vector-names = "MIN", "SVS", "NOMINAL", "TURBO"; /* IPA RAM mmap */ @@ -1074,6 +1078,23 @@ compatible = "qcom,smp2pgpio-map-ipa-1-in"; gpios = <&smp2pgpio_ipa_1_in 0 0>; }; + + ipa_smmu_ap: ipa_smmu_ap { + compatible = "qcom,ipa-smmu-ap-cb"; + iommus = <&anoc2_smmu 0x18e0>; + qcom,iova-mapping = <0x10000000 0x40000000>; + }; + + ipa_smmu_wlan: ipa_smmu_wlan { + compatible = "qcom,ipa-smmu-wlan-cb"; + iommus = <&anoc2_smmu 0x18e1>; + }; + + ipa_smmu_uc: ipa_smmu_uc { + compatible = "qcom,ipa-smmu-uc-cb"; + iommus = <&anoc2_smmu 0x18e2>; + qcom,iova-mapping = <0x40000000 0x20000000>; + }; }; qcom,ipa_fws@1e08000 { @@ -1706,11 +1727,10 @@ <&clock_gcc clk_gcc_aggre1_usb3_axi_clk>, <&clock_gcc clk_gcc_usb30_mock_utmi_clk>, <&clock_gcc clk_gcc_usb30_sleep_clk>, - <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>, <&clock_gcc clk_cxo_dwc3_clk>; clock-names = "core_clk", "iface_clk", "bus_aggr_clk", - "utmi_clk", "sleep_clk", "cfg_ahb_clk", "xo"; + "utmi_clk", "sleep_clk", "xo"; dwc3@a800000 { compatible = "snps,dwc3"; @@ -1758,11 +1778,9 @@ }; qusb_phy0: qusb@c012000 { - compatible = "qcom,qusb2phy"; - reg = <0x0c012000 0x2a8>, - <0x0a8f8800 0x400>; - reg-names = "qusb_phy_base", - "qscratch_base"; + compatible = "qcom,qusb2phy-v2"; + reg = <0x0c012000 0x2a8>; + reg-names = "qusb_phy_base"; vdd-supply = <&pmcobalt_l1>; vdda18-supply = <&pmcobalt_l12>; vdda33-supply = <&pmcobalt_l24>; @@ -1778,11 +1796,9 @@ clocks = <&clock_gcc clk_ln_bb_clk1>, <&clock_gcc clk_gcc_rx1_usb2_clkref_clk>, - <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>, <&clock_gcc clk_gcc_qusb2phy_prim_reset>; - clock-names = "ref_clk_src", "ref_clk", "cfg_ahb_clk", - "phy_reset"; + clock-names = "ref_clk_src", "ref_clk", "phy_reset"; }; ssphy: ssphy@c010000 { @@ -1930,13 +1946,12 @@ clocks = <&clock_gcc clk_gcc_usb3_phy_aux_clk>, <&clock_gcc clk_gcc_usb3_phy_pipe_clk>, - <&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>, <&clock_gcc clk_gcc_usb3_phy_reset>, <&clock_gcc clk_gcc_usb3phy_phy_reset>, <&clock_gcc clk_ln_bb_clk1>, <&clock_gcc clk_gcc_usb3_clkref_clk>; - clock-names = "aux_clk", "pipe_clk", "cfg_ahb_clk", "phy_reset", + clock-names = "aux_clk", "pipe_clk", "phy_reset", "phy_phy_reset", "ref_clk_src", "ref_clk"; }; @@ -2044,27 +2059,22 @@ tsens0: tsens@10aa000 { compatible = "qcom,msmcobalt-tsens"; - reg = <0x10aa000 0x2000>, - <0x74230 0x1000>; - reg-names = "tsens_physical", "tsens_eeprom_physical"; + reg = <0x10aa000 0x2000>; + reg-names = "tsens_physical"; interrupts = <0 458 0>, <0 445 0>; interrupt-names = "tsens-upper-lower", "tsens-critical"; qcom,sensors = <14>; - qcom,slope = <2901 2846 3200 3200 3200 3200 3200 3200 3200 - 3200 3200 3200 3200 3200>; }; tsens1: tsens@10ad000 { compatible = "qcom,msmcobalt-tsens"; - reg = <0x10ad000 0x2000>, - <0x75230 0x1000>; - reg-names = "tsens_physical", "tsens_eeprom_physical"; + reg = <0x10ad000 0x2000>; + reg-names = "tsens_physical"; interrupts = <0 184 0>, <0 430 0>; interrupt-names = "tsens-upper-lower", "tsens-critical"; qcom,client-id = <14 15 16 17 18 19 20 21>; qcom,sensor-id = <0 1 3 4 5 6 7 2>; qcom,sensors = <8>; - qcom,slope = <2901 2846 3200 3200 3200 3200 3200 3200>; }; qcom,sensor-information { diff --git a/arch/arm/boot/dts/qcom/msmfalcon.dtsi b/arch/arm/boot/dts/qcom/msmfalcon.dtsi index 2b19c74bd3cb..9ccf55322de1 100644 --- a/arch/arm/boot/dts/qcom/msmfalcon.dtsi +++ b/arch/arm/boot/dts/qcom/msmfalcon.dtsi @@ -313,6 +313,185 @@ compatible = "qcom,dummycc"; #clock-cells = <1>; }; + + qcom,ipc-spinlock@1f40000 { + compatible = "qcom,ipc-spinlock-sfpb"; + reg = <0x1f40000 0x8000>; + qcom,num-locks = <8>; + }; + + qcom,smem@86000000 { + compatible = "qcom,smem"; + reg = <0x86000000 0x200000>, + <0x17911008 0x4>, + <0x778000 0x7000>, + <0x1fd4000 0x8>; + reg-names = "smem", "irq-reg-base", "aux-mem1", + "smem_targ_info_reg"; + qcom,mpu-enabled; + }; + + qcom,glink-smem-native-xprt-modem@86000000 { + compatible = "qcom,glink-smem-native-xprt"; + reg = <0x86000000 0x200000>, + <0x17911008 0x4>; + reg-names = "smem", "irq-reg-base"; + qcom,irq-mask = <0x8000>; + interrupts = <0 452 1>; + label = "mpss"; + }; + + qcom,glink-smem-native-xprt-adsp@86000000 { + compatible = "qcom,glink-smem-native-xprt"; + reg = <0x86000000 0x200000>, + <0x17911008 0x4>; + reg-names = "smem", "irq-reg-base"; + qcom,irq-mask = <0x200>; + interrupts = <0 157 1>; + label = "lpass"; + qcom,qos-config = <&glink_qos_adsp>; + qcom,ramp-time = <0xaf>; + }; + + glink_qos_adsp: qcom,glink-qos-config-adsp { + compatible = "qcom,glink-qos-config"; + qcom,flow-info = <0x3c 0x0>, + <0x3c 0x0>, + <0x3c 0x0>, + <0x3c 0x0>; + qcom,mtu-size = <0x800>; + qcom,tput-stats-cycle = <0xa>; + }; + + qcom,glink-smem-native-xprt-cdsp@86000000 { + compatible = "qcom,glink-smem-native-xprt"; + reg = <0x86000000 0x200000>, + <0x17911008 0x4>; + reg-names = "smem", "irq-reg-base"; + qcom,irq-mask = <0x20000000>; + interrupts = <0 513 1>; + label = "cdsp"; + }; + + qcom,glink-smem-native-xprt-rpm@68000 { + compatible = "qcom,glink-rpm-native-xprt"; + reg = <0x778000 0x7000>, + <0x17911008 0x4>; + reg-names = "msgram", "irq-reg-base"; + qcom,irq-mask = <0x1>; + interrupts = <0 168 1>; + label = "rpm"; + }; + + glink_mpss: qcom,glink-ssr-modem { + compatible = "qcom,glink_ssr"; + label = "modem"; + qcom,edge = "mpss"; + qcom,notify-edges = <&glink_lpass>, <&glink_rpm>, + <&glink_cdsp>; + qcom,xprt = "smem"; + }; + + glink_lpass: qcom,glink-ssr-adsp { + compatible = "qcom,glink_ssr"; + label = "adsp"; + qcom,edge = "lpass"; + qcom,notify-edges = <&glink_mpss>, <&glink_rpm>, + <&glink_cdsp>; + qcom,xprt = "smem"; + }; + + glink_rpm: qcom,glink-ssr-rpm { + compatible = "qcom,glink_ssr"; + label = "rpm"; + qcom,edge = "rpm"; + qcom,notify-edges = <&glink_lpass>, <&glink_mpss>, + <&glink_cdsp>; + qcom,xprt = "smem"; + }; + + glink_cdsp: qcom,glink-ssr-cdsp { + compatible = "qcom,glink_ssr"; + label = "cdsp"; + qcom,edge = "cdsp"; + qcom,notify-edges = <&glink_lpass>, <&glink_mpss>, + <&glink_rpm>; + qcom,xprt = "smem"; + }; + + qcom,glink_pkt { + compatible = "qcom,glinkpkt"; + + qcom,glinkpkt-at-mdm0 { + qcom,glinkpkt-transport = "smem"; + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DS"; + qcom,glinkpkt-dev-name = "at_mdm0"; + }; + + qcom,glinkpkt-loopback_cntl { + qcom,glinkpkt-transport = "lloop"; + qcom,glinkpkt-edge = "local"; + qcom,glinkpkt-ch-name = "LOCAL_LOOPBACK_CLNT"; + qcom,glinkpkt-dev-name = "glink_pkt_loopback_ctrl"; + }; + + qcom,glinkpkt-loopback_data { + qcom,glinkpkt-transport = "lloop"; + qcom,glinkpkt-edge = "local"; + qcom,glinkpkt-ch-name = "glink_pkt_lloop_CLNT"; + qcom,glinkpkt-dev-name = "glink_pkt_loopback"; + }; + + qcom,glinkpkt-apr-apps2 { + qcom,glinkpkt-transport = "smem"; + qcom,glinkpkt-edge = "adsp"; + qcom,glinkpkt-ch-name = "apr_apps2"; + qcom,glinkpkt-dev-name = "apr_apps2"; + }; + + qcom,glinkpkt-data40-cntl { + qcom,glinkpkt-transport = "smem"; + qcom,glinkpkt-edge = "mpss"; + qcom,glinkpkt-ch-name = "DATA40_CNTL"; + qcom,glinkpkt-dev-name = "smdcntl8"; + }; + }; + + qcom,ipc_router { + compatible = "qcom,ipc_router"; + qcom,node-id = <1>; + }; + + qcom,ipc_router_modem_xprt { + compatible = "qcom,ipc_router_glink_xprt"; + qcom,ch-name = "IPCRTR"; + qcom,xprt-remote = "mpss"; + qcom,glink-xprt = "smem"; + qcom,xprt-linkid = <1>; + qcom,xprt-version = <1>; + qcom,fragmented-data; + }; + + qcom,ipc_router_q6_xprt { + compatible = "qcom,ipc_router_glink_xprt"; + qcom,ch-name = "IPCRTR"; + qcom,xprt-remote = "lpass"; + qcom,glink-xprt = "smem"; + qcom,xprt-linkid = <1>; + qcom,xprt-version = <1>; + qcom,fragmented-data; + }; + + qcom,ipc_router_cdsp_xprt { + compatible = "qcom,ipc_router_glink_xprt"; + qcom,ch-name = "IPCRTR"; + qcom,xprt-remote = "cdsp"; + qcom,glink-xprt = "smem"; + qcom,xprt-linkid = <1>; + qcom,xprt-version = <1>; + qcom,fragmented-data; + }; }; #include "msmfalcon-ion.dtsi" diff --git a/arch/arm/mach-s3c64xx/dev-audio.c b/arch/arm/mach-s3c64xx/dev-audio.c index ff780a8d8366..9a42736ef4ac 100644 --- a/arch/arm/mach-s3c64xx/dev-audio.c +++ b/arch/arm/mach-s3c64xx/dev-audio.c @@ -54,12 +54,12 @@ static int s3c64xx_i2s_cfg_gpio(struct platform_device *pdev) static struct resource s3c64xx_iis0_resource[] = { [0] = DEFINE_RES_MEM(S3C64XX_PA_IIS0, SZ_256), - [1] = DEFINE_RES_DMA(DMACH_I2S0_OUT), - [2] = DEFINE_RES_DMA(DMACH_I2S0_IN), }; -static struct s3c_audio_pdata i2sv3_pdata = { +static struct s3c_audio_pdata i2s0_pdata = { .cfg_gpio = s3c64xx_i2s_cfg_gpio, + .dma_playback = DMACH_I2S0_OUT, + .dma_capture = DMACH_I2S0_IN, }; struct platform_device s3c64xx_device_iis0 = { @@ -68,15 +68,19 @@ struct platform_device s3c64xx_device_iis0 = { .num_resources = ARRAY_SIZE(s3c64xx_iis0_resource), .resource = s3c64xx_iis0_resource, .dev = { - .platform_data = &i2sv3_pdata, + .platform_data = &i2s0_pdata, }, }; EXPORT_SYMBOL(s3c64xx_device_iis0); static struct resource s3c64xx_iis1_resource[] = { [0] = DEFINE_RES_MEM(S3C64XX_PA_IIS1, SZ_256), - [1] = DEFINE_RES_DMA(DMACH_I2S1_OUT), - [2] = DEFINE_RES_DMA(DMACH_I2S1_IN), +}; + +static struct s3c_audio_pdata i2s1_pdata = { + .cfg_gpio = s3c64xx_i2s_cfg_gpio, + .dma_playback = DMACH_I2S1_OUT, + .dma_capture = DMACH_I2S1_IN, }; struct platform_device s3c64xx_device_iis1 = { @@ -85,19 +89,19 @@ struct platform_device s3c64xx_device_iis1 = { .num_resources = ARRAY_SIZE(s3c64xx_iis1_resource), .resource = s3c64xx_iis1_resource, .dev = { - .platform_data = &i2sv3_pdata, + .platform_data = &i2s1_pdata, }, }; EXPORT_SYMBOL(s3c64xx_device_iis1); static struct resource s3c64xx_iisv4_resource[] = { [0] = DEFINE_RES_MEM(S3C64XX_PA_IISV4, SZ_256), - [1] = DEFINE_RES_DMA(DMACH_HSI_I2SV40_TX), - [2] = DEFINE_RES_DMA(DMACH_HSI_I2SV40_RX), }; static struct s3c_audio_pdata i2sv4_pdata = { .cfg_gpio = s3c64xx_i2s_cfg_gpio, + .dma_playback = DMACH_HSI_I2SV40_TX, + .dma_capture = DMACH_HSI_I2SV40_RX, .type = { .i2s = { .quirks = QUIRK_PRI_6CHAN, @@ -142,12 +146,12 @@ static int s3c64xx_pcm_cfg_gpio(struct platform_device *pdev) static struct resource s3c64xx_pcm0_resource[] = { [0] = DEFINE_RES_MEM(S3C64XX_PA_PCM0, SZ_256), - [1] = DEFINE_RES_DMA(DMACH_PCM0_TX), - [2] = DEFINE_RES_DMA(DMACH_PCM0_RX), }; static struct s3c_audio_pdata s3c_pcm0_pdata = { .cfg_gpio = s3c64xx_pcm_cfg_gpio, + .dma_capture = DMACH_PCM0_RX, + .dma_playback = DMACH_PCM0_TX, }; struct platform_device s3c64xx_device_pcm0 = { @@ -163,12 +167,12 @@ EXPORT_SYMBOL(s3c64xx_device_pcm0); static struct resource s3c64xx_pcm1_resource[] = { [0] = DEFINE_RES_MEM(S3C64XX_PA_PCM1, SZ_256), - [1] = DEFINE_RES_DMA(DMACH_PCM1_TX), - [2] = DEFINE_RES_DMA(DMACH_PCM1_RX), }; static struct s3c_audio_pdata s3c_pcm1_pdata = { .cfg_gpio = s3c64xx_pcm_cfg_gpio, + .dma_playback = DMACH_PCM1_TX, + .dma_capture = DMACH_PCM1_RX, }; struct platform_device s3c64xx_device_pcm1 = { @@ -196,13 +200,14 @@ static int s3c64xx_ac97_cfg_gpe(struct platform_device *pdev) static struct resource s3c64xx_ac97_resource[] = { [0] = DEFINE_RES_MEM(S3C64XX_PA_AC97, SZ_256), - [1] = DEFINE_RES_DMA(DMACH_AC97_PCMOUT), - [2] = DEFINE_RES_DMA(DMACH_AC97_PCMIN), - [3] = DEFINE_RES_DMA(DMACH_AC97_MICIN), - [4] = DEFINE_RES_IRQ(IRQ_AC97), + [1] = DEFINE_RES_IRQ(IRQ_AC97), }; -static struct s3c_audio_pdata s3c_ac97_pdata; +static struct s3c_audio_pdata s3c_ac97_pdata = { + .dma_playback = DMACH_AC97_PCMOUT, + .dma_capture = DMACH_AC97_PCMIN, + .dma_capture_mic = DMACH_AC97_MICIN, +}; static u64 s3c64xx_ac97_dmamask = DMA_BIT_MASK(32); diff --git a/arch/arm/mach-s3c64xx/include/mach/dma.h b/arch/arm/mach-s3c64xx/include/mach/dma.h index 096e14073bd9..9c739eafe95c 100644 --- a/arch/arm/mach-s3c64xx/include/mach/dma.h +++ b/arch/arm/mach-s3c64xx/include/mach/dma.h @@ -14,38 +14,38 @@ #define S3C64XX_DMA_CHAN(name) ((unsigned long)(name)) /* DMA0/SDMA0 */ -#define DMACH_UART0 S3C64XX_DMA_CHAN("uart0_tx") -#define DMACH_UART0_SRC2 S3C64XX_DMA_CHAN("uart0_rx") -#define DMACH_UART1 S3C64XX_DMA_CHAN("uart1_tx") -#define DMACH_UART1_SRC2 S3C64XX_DMA_CHAN("uart1_rx") -#define DMACH_UART2 S3C64XX_DMA_CHAN("uart2_tx") -#define DMACH_UART2_SRC2 S3C64XX_DMA_CHAN("uart2_rx") -#define DMACH_UART3 S3C64XX_DMA_CHAN("uart3_tx") -#define DMACH_UART3_SRC2 S3C64XX_DMA_CHAN("uart3_rx") -#define DMACH_PCM0_TX S3C64XX_DMA_CHAN("pcm0_tx") -#define DMACH_PCM0_RX S3C64XX_DMA_CHAN("pcm0_rx") -#define DMACH_I2S0_OUT S3C64XX_DMA_CHAN("i2s0_tx") -#define DMACH_I2S0_IN S3C64XX_DMA_CHAN("i2s0_rx") +#define DMACH_UART0 "uart0_tx" +#define DMACH_UART0_SRC2 "uart0_rx" +#define DMACH_UART1 "uart1_tx" +#define DMACH_UART1_SRC2 "uart1_rx" +#define DMACH_UART2 "uart2_tx" +#define DMACH_UART2_SRC2 "uart2_rx" +#define DMACH_UART3 "uart3_tx" +#define DMACH_UART3_SRC2 "uart3_rx" +#define DMACH_PCM0_TX "pcm0_tx" +#define DMACH_PCM0_RX "pcm0_rx" +#define DMACH_I2S0_OUT "i2s0_tx" +#define DMACH_I2S0_IN "i2s0_rx" #define DMACH_SPI0_TX S3C64XX_DMA_CHAN("spi0_tx") #define DMACH_SPI0_RX S3C64XX_DMA_CHAN("spi0_rx") -#define DMACH_HSI_I2SV40_TX S3C64XX_DMA_CHAN("i2s2_tx") -#define DMACH_HSI_I2SV40_RX S3C64XX_DMA_CHAN("i2s2_rx") +#define DMACH_HSI_I2SV40_TX "i2s2_tx" +#define DMACH_HSI_I2SV40_RX "i2s2_rx" /* DMA1/SDMA1 */ -#define DMACH_PCM1_TX S3C64XX_DMA_CHAN("pcm1_tx") -#define DMACH_PCM1_RX S3C64XX_DMA_CHAN("pcm1_rx") -#define DMACH_I2S1_OUT S3C64XX_DMA_CHAN("i2s1_tx") -#define DMACH_I2S1_IN S3C64XX_DMA_CHAN("i2s1_rx") +#define DMACH_PCM1_TX "pcm1_tx" +#define DMACH_PCM1_RX "pcm1_rx" +#define DMACH_I2S1_OUT "i2s1_tx" +#define DMACH_I2S1_IN "i2s1_rx" #define DMACH_SPI1_TX S3C64XX_DMA_CHAN("spi1_tx") #define DMACH_SPI1_RX S3C64XX_DMA_CHAN("spi1_rx") -#define DMACH_AC97_PCMOUT S3C64XX_DMA_CHAN("ac97_out") -#define DMACH_AC97_PCMIN S3C64XX_DMA_CHAN("ac97_in") -#define DMACH_AC97_MICIN S3C64XX_DMA_CHAN("ac97_mic") -#define DMACH_PWM S3C64XX_DMA_CHAN("pwm") -#define DMACH_IRDA S3C64XX_DMA_CHAN("irda") -#define DMACH_EXTERNAL S3C64XX_DMA_CHAN("external") -#define DMACH_SECURITY_RX S3C64XX_DMA_CHAN("sec_rx") -#define DMACH_SECURITY_TX S3C64XX_DMA_CHAN("sec_tx") +#define DMACH_AC97_PCMOUT "ac97_out" +#define DMACH_AC97_PCMIN "ac97_in" +#define DMACH_AC97_MICIN "ac97_mic" +#define DMACH_PWM "pwm" +#define DMACH_IRDA "irda" +#define DMACH_EXTERNAL "external" +#define DMACH_SECURITY_RX "sec_rx" +#define DMACH_SECURITY_TX "sec_tx" enum dma_ch { DMACH_MAX = 32 diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index bf6a92504175..d41957eae6ef 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -2284,6 +2284,7 @@ void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, set_dma_ops(dev, dma_ops); } +EXPORT_SYMBOL(arch_setup_dma_ops); void arch_teardown_dma_ops(struct device *dev) { diff --git a/arch/arm/plat-samsung/devs.c b/arch/arm/plat-samsung/devs.c index 82074625de5c..e212f9d804bd 100644 --- a/arch/arm/plat-samsung/devs.c +++ b/arch/arm/plat-samsung/devs.c @@ -65,6 +65,7 @@ #include <linux/platform_data/usb-ohci-s3c2410.h> #include <plat/usb-phy.h> #include <plat/regs-spi.h> +#include <linux/platform_data/asoc-s3c.h> #include <linux/platform_data/spi-s3c64xx.h> static u64 samsung_device_dma_mask = DMA_BIT_MASK(32); @@ -74,9 +75,12 @@ static u64 samsung_device_dma_mask = DMA_BIT_MASK(32); static struct resource s3c_ac97_resource[] = { [0] = DEFINE_RES_MEM(S3C2440_PA_AC97, S3C2440_SZ_AC97), [1] = DEFINE_RES_IRQ(IRQ_S3C244X_AC97), - [2] = DEFINE_RES_DMA_NAMED(DMACH_PCM_OUT, "PCM out"), - [3] = DEFINE_RES_DMA_NAMED(DMACH_PCM_IN, "PCM in"), - [4] = DEFINE_RES_DMA_NAMED(DMACH_MIC_IN, "Mic in"), +}; + +static struct s3c_audio_pdata s3c_ac97_pdata = { + .dma_playback = (void *)DMACH_PCM_OUT, + .dma_capture = (void *)DMACH_PCM_IN, + .dma_capture_mic = (void *)DMACH_MIC_IN, }; struct platform_device s3c_device_ac97 = { @@ -87,6 +91,7 @@ struct platform_device s3c_device_ac97 = { .dev = { .dma_mask = &samsung_device_dma_mask, .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &s3c_ac97_pdata, } }; #endif /* CONFIG_CPU_S3C2440 */ diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index fc2cce36bff9..1ae97b43e110 100644 --- a/arch/arm64/configs/msm-perf_defconfig +++ b/arch/arm64/configs/msm-perf_defconfig @@ -85,6 +85,7 @@ CONFIG_IP_MULTIPLE_TABLES=y CONFIG_IP_ROUTE_VERBOSE=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y +CONFIG_INET_ESP=y # CONFIG_INET_LRO is not set CONFIG_IPV6_ROUTER_PREF=y CONFIG_IPV6_ROUTE_INFO=y @@ -95,7 +96,6 @@ CONFIG_INET6_IPCOMP=y CONFIG_IPV6_MIP6=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_SUBTREES=y -# CONFIG_NET_ACTIVITY_STATS is not set CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=y CONFIG_NF_CONNTRACK_SECMARK=y @@ -408,7 +408,10 @@ CONFIG_SND_SOC=y CONFIG_SND_SOC_MSM8996=y CONFIG_UHID=y CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_XHCI_HCD=y diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig index 38e489936895..3f941c24f299 100644 --- a/arch/arm64/configs/msm_defconfig +++ b/arch/arm64/configs/msm_defconfig @@ -98,7 +98,6 @@ CONFIG_INET6_IPCOMP=y CONFIG_IPV6_MIP6=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_SUBTREES=y -# CONFIG_NET_ACTIVITY_STATS is not set CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=y CONFIG_NF_CONNTRACK_SECMARK=y @@ -397,7 +396,10 @@ CONFIG_SND_SOC=y CONFIG_SND_SOC_MSM8996=y CONFIG_UHID=y CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y CONFIG_USB=y CONFIG_USB_XHCI_HCD=y CONFIG_USB_EHCI_HCD=y diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 72584917f930..de464a023e18 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -57,6 +57,8 @@ CONFIG_FORCE_ALLOC_FROM_DMA_ZONE=y CONFIG_SECCOMP=y CONFIG_ARMV8_DEPRECATED=y CONFIG_SWP_EMULATION=y +CONFIG_CP15_BARRIER_EMULATION=y +CONFIG_SETEND_EMULATION=y # CONFIG_EFI is not set CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set @@ -97,7 +99,6 @@ CONFIG_INET6_IPCOMP=y CONFIG_IPV6_MIP6=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_SUBTREES=y -# CONFIG_NET_ACTIVITY_STATS is not set CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=y CONFIG_NF_CONNTRACK_SECMARK=y @@ -302,6 +303,7 @@ CONFIG_MSM_PM=y CONFIG_APSS_CORE_EA=y CONFIG_MSM_APM=y CONFIG_QPNP_SMBCHARGER=y +CONFIG_QPNP_FG_GEN3=y CONFIG_SMB135X_CHARGER=y CONFIG_SMB1351_USB_CHARGER=y CONFIG_MSM_BCL_CTL=y @@ -379,6 +381,7 @@ CONFIG_FB_MSM=y CONFIG_FB_MSM_MDSS=y CONFIG_FB_MSM_MDSS_WRITEBACK=y CONFIG_FB_MSM_MDSS_HDMI_PANEL=y +CONFIG_FB_MSM_MDSS_DP_PANEL=y CONFIG_LOGO=y # CONFIG_LOGO_LINUX_MONO is not set # CONFIG_LOGO_LINUX_VGA16 is not set @@ -390,7 +393,10 @@ CONFIG_SND_SOC=y CONFIG_SND_SOC_MSMCOBALT=y CONFIG_UHID=y CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_XHCI_HCD=y @@ -464,6 +470,9 @@ CONFIG_MSM_MDSS_PLL=y CONFIG_REMOTE_SPINLOCK_MSM=y CONFIG_IOMMU_IO_PGTABLE_FAST=y CONFIG_ARM_SMMU=y +CONFIG_IOMMU_DEBUG=y +CONFIG_IOMMU_DEBUG_TRACKING=y +CONFIG_IOMMU_TESTS=y CONFIG_MSM_SMEM=y CONFIG_QPNP_HAPTIC=y CONFIG_MSM_SMD=y @@ -500,6 +509,7 @@ CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y CONFIG_MSM_MPM_OF=y CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_CORE_CTL_HELPER=y +CONFIG_QCOM_REMOTEQDSS=y CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y CONFIG_MSM_RPM_LOG=y CONFIG_MSM_RPM_STATS_LOG=y @@ -543,6 +553,19 @@ CONFIG_CPU_FREQ_SWITCH_PROFILER=y CONFIG_DEBUG_SET_MODULE_RONX=y CONFIG_DEBUG_RODATA=y CONFIG_DEBUG_ALIGN_RODATA=y +CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y +CONFIG_CORESIGHT_SOURCE_ETM4X=y +CONFIG_CORESIGHT_REMOTE_ETM=y +CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0 +CONFIG_CORESIGHT_QCOM_REPLICATOR=y +CONFIG_CORESIGHT_STM=y +CONFIG_CORESIGHT_HWEVENT=y +CONFIG_CORESIGHT_CTI=y +CONFIG_CORESIGHT_TPDA=y +CONFIG_CORESIGHT_TPDM=y +CONFIG_CORESIGHT_QPDI=y +CONFIG_CORESIGHT_SOURCE_DUMMY=y CONFIG_PFK=y CONFIG_SECURITY=y CONFIG_SECURITY_SELINUX=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index e708960cf28b..55f7eb39c70d 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -98,7 +98,6 @@ CONFIG_INET6_IPCOMP=y CONFIG_IPV6_MIP6=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_SUBTREES=y -# CONFIG_NET_ACTIVITY_STATS is not set CONFIG_NETFILTER=y CONFIG_NF_CONNTRACK=y CONFIG_NF_CONNTRACK_SECMARK=y @@ -307,6 +306,7 @@ CONFIG_MSM_PM=y CONFIG_APSS_CORE_EA=y CONFIG_MSM_APM=y CONFIG_QPNP_SMBCHARGER=y +CONFIG_QPNP_FG_GEN3=y CONFIG_SMB135X_CHARGER=y CONFIG_SMB1351_USB_CHARGER=y CONFIG_MSM_BCL_CTL=y @@ -384,6 +384,7 @@ CONFIG_FB_MSM=y CONFIG_FB_MSM_MDSS=y CONFIG_FB_MSM_MDSS_WRITEBACK=y CONFIG_FB_MSM_MDSS_HDMI_PANEL=y +CONFIG_FB_MSM_MDSS_DP_PANEL=y CONFIG_LOGO=y # CONFIG_LOGO_LINUX_MONO is not set # CONFIG_LOGO_LINUX_VGA16 is not set @@ -395,7 +396,10 @@ CONFIG_SND_SOC=y CONFIG_SND_SOC_MSMCOBALT=y CONFIG_UHID=y CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y CONFIG_USB=y CONFIG_USB_XHCI_HCD=y CONFIG_USB_EHCI_HCD=y @@ -525,6 +529,7 @@ CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y CONFIG_MSM_MPM_OF=y CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_CORE_CTL_HELPER=y +CONFIG_QCOM_REMOTEQDSS=y CONFIG_MSM_SERVICE_NOTIFIER=y CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y CONFIG_MSM_RPM_LOG=y diff --git a/arch/arm64/include/asm/opcodes.h b/arch/arm64/include/asm/opcodes.h index 4e603ea36ad3..123f45d92cd1 100644 --- a/arch/arm64/include/asm/opcodes.h +++ b/arch/arm64/include/asm/opcodes.h @@ -1 +1,5 @@ +#ifdef CONFIG_CPU_BIG_ENDIAN +#define CONFIG_CPU_ENDIAN_BE8 CONFIG_CPU_BIG_ENDIAN +#endif + #include <../../arm/include/asm/opcodes.h> diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c index 8aee3aeec3e6..c1492ba1f6d1 100644 --- a/arch/arm64/kernel/debug-monitors.c +++ b/arch/arm64/kernel/debug-monitors.c @@ -186,20 +186,21 @@ static void clear_regs_spsr_ss(struct pt_regs *regs) /* EL1 Single Step Handler hooks */ static LIST_HEAD(step_hook); -static DEFINE_RWLOCK(step_hook_lock); +static DEFINE_SPINLOCK(step_hook_lock); void register_step_hook(struct step_hook *hook) { - write_lock(&step_hook_lock); - list_add(&hook->node, &step_hook); - write_unlock(&step_hook_lock); + spin_lock(&step_hook_lock); + list_add_rcu(&hook->node, &step_hook); + spin_unlock(&step_hook_lock); } void unregister_step_hook(struct step_hook *hook) { - write_lock(&step_hook_lock); - list_del(&hook->node); - write_unlock(&step_hook_lock); + spin_lock(&step_hook_lock); + list_del_rcu(&hook->node); + spin_unlock(&step_hook_lock); + synchronize_rcu(); } /* @@ -213,15 +214,15 @@ static int call_step_hook(struct pt_regs *regs, unsigned int esr) struct step_hook *hook; int retval = DBG_HOOK_ERROR; - read_lock(&step_hook_lock); + rcu_read_lock(); - list_for_each_entry(hook, &step_hook, node) { + list_for_each_entry_rcu(hook, &step_hook, node) { retval = hook->fn(regs, esr); if (retval == DBG_HOOK_HANDLED) break; } - read_unlock(&step_hook_lock); + rcu_read_unlock(); return retval; } diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index 8fa6f87cc31d..52d046b6a920 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c @@ -674,7 +674,9 @@ static const struct of_device_id armv8_pmu_of_device_ids[] = { {.compatible = "arm,armv8-pmuv3", .data = armv8_pmuv3_init}, {.compatible = "arm,cortex-a53-pmu", .data = armv8_a53_pmu_init}, {.compatible = "arm,cortex-a57-pmu", .data = armv8_a57_pmu_init}, +#ifdef CONFIG_ARCH_MSM8996 {.compatible = "qcom,kryo-pmuv3", .data = kryo_pmu_init}, +#endif {}, }; diff --git a/arch/arm64/kernel/vdso/vdso.S b/arch/arm64/kernel/vdso/vdso.S index 60c1db54b41a..82379a70ef03 100644 --- a/arch/arm64/kernel/vdso/vdso.S +++ b/arch/arm64/kernel/vdso/vdso.S @@ -21,9 +21,8 @@ #include <linux/const.h> #include <asm/page.h> - __PAGE_ALIGNED_DATA - .globl vdso_start, vdso_end + .section .rodata .balign PAGE_SIZE vdso_start: .incbin "arch/arm64/kernel/vdso/vdso.so" diff --git a/arch/ia64/include/asm/io.h b/arch/ia64/include/asm/io.h index 9041bbe2b7b4..8fdb9c7eeb66 100644 --- a/arch/ia64/include/asm/io.h +++ b/arch/ia64/include/asm/io.h @@ -436,6 +436,7 @@ static inline void __iomem * ioremap_cache (unsigned long phys_addr, unsigned lo return ioremap(phys_addr, size); } #define ioremap_cache ioremap_cache +#define ioremap_uc ioremap_nocache /* diff --git a/arch/mips/alchemy/devboards/db1000.c b/arch/mips/alchemy/devboards/db1000.c index bdeed9d13c6f..433c4b9a9f0a 100644 --- a/arch/mips/alchemy/devboards/db1000.c +++ b/arch/mips/alchemy/devboards/db1000.c @@ -503,15 +503,15 @@ int __init db1000_dev_setup(void) if (board == BCSR_WHOAMI_DB1500) { c0 = AU1500_GPIO2_INT; c1 = AU1500_GPIO5_INT; - d0 = AU1500_GPIO0_INT; - d1 = AU1500_GPIO3_INT; + d0 = 0; /* GPIO number, NOT irq! */ + d1 = 3; /* GPIO number, NOT irq! */ s0 = AU1500_GPIO1_INT; s1 = AU1500_GPIO4_INT; } else if (board == BCSR_WHOAMI_DB1100) { c0 = AU1100_GPIO2_INT; c1 = AU1100_GPIO5_INT; - d0 = AU1100_GPIO0_INT; - d1 = AU1100_GPIO3_INT; + d0 = 0; /* GPIO number, NOT irq! */ + d1 = 3; /* GPIO number, NOT irq! */ s0 = AU1100_GPIO1_INT; s1 = AU1100_GPIO4_INT; @@ -545,15 +545,15 @@ int __init db1000_dev_setup(void) } else if (board == BCSR_WHOAMI_DB1000) { c0 = AU1000_GPIO2_INT; c1 = AU1000_GPIO5_INT; - d0 = AU1000_GPIO0_INT; - d1 = AU1000_GPIO3_INT; + d0 = 0; /* GPIO number, NOT irq! */ + d1 = 3; /* GPIO number, NOT irq! */ s0 = AU1000_GPIO1_INT; s1 = AU1000_GPIO4_INT; platform_add_devices(db1000_devs, ARRAY_SIZE(db1000_devs)); } else if ((board == BCSR_WHOAMI_PB1500) || (board == BCSR_WHOAMI_PB1500R2)) { c0 = AU1500_GPIO203_INT; - d0 = AU1500_GPIO201_INT; + d0 = 1; /* GPIO number, NOT irq! */ s0 = AU1500_GPIO202_INT; twosocks = 0; flashsize = 64; @@ -566,7 +566,7 @@ int __init db1000_dev_setup(void) */ } else if (board == BCSR_WHOAMI_PB1100) { c0 = AU1100_GPIO11_INT; - d0 = AU1100_GPIO9_INT; + d0 = 9; /* GPIO number, NOT irq! */ s0 = AU1100_GPIO10_INT; twosocks = 0; flashsize = 64; @@ -583,7 +583,6 @@ int __init db1000_dev_setup(void) } else return 0; /* unknown board, no further dev setup to do */ - irq_set_irq_type(d0, IRQ_TYPE_EDGE_BOTH); irq_set_irq_type(c0, IRQ_TYPE_LEVEL_LOW); irq_set_irq_type(s0, IRQ_TYPE_LEVEL_LOW); @@ -597,7 +596,6 @@ int __init db1000_dev_setup(void) c0, d0, /*s0*/0, 0, 0); if (twosocks) { - irq_set_irq_type(d1, IRQ_TYPE_EDGE_BOTH); irq_set_irq_type(c1, IRQ_TYPE_LEVEL_LOW); irq_set_irq_type(s1, IRQ_TYPE_LEVEL_LOW); diff --git a/arch/mips/alchemy/devboards/db1550.c b/arch/mips/alchemy/devboards/db1550.c index 5740bcfdfc7f..6c37b9326f41 100644 --- a/arch/mips/alchemy/devboards/db1550.c +++ b/arch/mips/alchemy/devboards/db1550.c @@ -514,7 +514,7 @@ static void __init db1550_devices(void) AU1000_PCMCIA_MEM_PHYS_ADDR + 0x000400000 - 1, AU1000_PCMCIA_IO_PHYS_ADDR, AU1000_PCMCIA_IO_PHYS_ADDR + 0x000010000 - 1, - AU1550_GPIO3_INT, AU1550_GPIO0_INT, + AU1550_GPIO3_INT, 0, /*AU1550_GPIO21_INT*/0, 0, 0); db1x_register_pcmcia_socket( @@ -524,7 +524,7 @@ static void __init db1550_devices(void) AU1000_PCMCIA_MEM_PHYS_ADDR + 0x004400000 - 1, AU1000_PCMCIA_IO_PHYS_ADDR + 0x004000000, AU1000_PCMCIA_IO_PHYS_ADDR + 0x004010000 - 1, - AU1550_GPIO5_INT, AU1550_GPIO1_INT, + AU1550_GPIO5_INT, 1, /*AU1550_GPIO22_INT*/0, 0, 1); platform_device_register(&db1550_nand_dev); diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c index 490cea569d57..5c62065cbf22 100644 --- a/arch/mips/kernel/unaligned.c +++ b/arch/mips/kernel/unaligned.c @@ -885,7 +885,7 @@ static void emulate_load_store_insn(struct pt_regs *regs, { union mips_instruction insn; unsigned long value; - unsigned int res; + unsigned int res, preempted; unsigned long origpc; unsigned long orig31; void __user *fault_addr = NULL; @@ -1226,27 +1226,36 @@ static void emulate_load_store_insn(struct pt_regs *regs, if (!access_ok(VERIFY_READ, addr, sizeof(*fpr))) goto sigbus; - /* - * Disable preemption to avoid a race between copying - * state from userland, migrating to another CPU and - * updating the hardware vector register below. - */ - preempt_disable(); - - res = __copy_from_user_inatomic(fpr, addr, - sizeof(*fpr)); - if (res) - goto fault; - - /* - * Update the hardware register if it is in use by the - * task in this quantum, in order to avoid having to - * save & restore the whole vector context. - */ - if (test_thread_flag(TIF_USEDMSA)) - write_msa_wr(wd, fpr, df); + do { + /* + * If we have live MSA context keep track of + * whether we get preempted in order to avoid + * the register context we load being clobbered + * by the live context as it's saved during + * preemption. If we don't have live context + * then it can't be saved to clobber the value + * we load. + */ + preempted = test_thread_flag(TIF_USEDMSA); + + res = __copy_from_user_inatomic(fpr, addr, + sizeof(*fpr)); + if (res) + goto fault; - preempt_enable(); + /* + * Update the hardware register if it is in use + * by the task in this quantum, in order to + * avoid having to save & restore the whole + * vector context. + */ + preempt_disable(); + if (test_thread_flag(TIF_USEDMSA)) { + write_msa_wr(wd, fpr, df); + preempted = 0; + } + preempt_enable(); + } while (preempted); break; case msa_st_op: diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h index 0abdd4c607ed..1960b87c1c8b 100644 --- a/arch/parisc/include/asm/uaccess.h +++ b/arch/parisc/include/asm/uaccess.h @@ -76,6 +76,7 @@ struct exception_table_entry { */ struct exception_data { unsigned long fault_ip; + unsigned long fault_gp; unsigned long fault_space; unsigned long fault_addr; }; diff --git a/arch/parisc/kernel/asm-offsets.c b/arch/parisc/kernel/asm-offsets.c index d2f62570a7b1..78d30d2ea2d8 100644 --- a/arch/parisc/kernel/asm-offsets.c +++ b/arch/parisc/kernel/asm-offsets.c @@ -299,6 +299,7 @@ int main(void) #endif BLANK(); DEFINE(EXCDATA_IP, offsetof(struct exception_data, fault_ip)); + DEFINE(EXCDATA_GP, offsetof(struct exception_data, fault_gp)); DEFINE(EXCDATA_SPACE, offsetof(struct exception_data, fault_space)); DEFINE(EXCDATA_ADDR, offsetof(struct exception_data, fault_addr)); BLANK(); diff --git a/arch/parisc/kernel/parisc_ksyms.c b/arch/parisc/kernel/parisc_ksyms.c index 568b2c61ea02..3cad8aadc69e 100644 --- a/arch/parisc/kernel/parisc_ksyms.c +++ b/arch/parisc/kernel/parisc_ksyms.c @@ -47,11 +47,11 @@ EXPORT_SYMBOL(__cmpxchg_u64); EXPORT_SYMBOL(lclear_user); EXPORT_SYMBOL(lstrnlen_user); -/* Global fixups */ -extern void fixup_get_user_skip_1(void); -extern void fixup_get_user_skip_2(void); -extern void fixup_put_user_skip_1(void); -extern void fixup_put_user_skip_2(void); +/* Global fixups - defined as int to avoid creation of function pointers */ +extern int fixup_get_user_skip_1; +extern int fixup_get_user_skip_2; +extern int fixup_put_user_skip_1; +extern int fixup_put_user_skip_2; EXPORT_SYMBOL(fixup_get_user_skip_1); EXPORT_SYMBOL(fixup_get_user_skip_2); EXPORT_SYMBOL(fixup_put_user_skip_1); diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c index 553b09855cfd..77e2262c97f6 100644 --- a/arch/parisc/kernel/traps.c +++ b/arch/parisc/kernel/traps.c @@ -798,6 +798,9 @@ void notrace handle_interruption(int code, struct pt_regs *regs) if (fault_space == 0 && !faulthandler_disabled()) { + /* Clean up and return if in exception table. */ + if (fixup_exception(regs)) + return; pdc_chassis_send_status(PDC_CHASSIS_DIRECT_PANIC); parisc_terminate("Kernel Fault", regs, code, fault_address); } diff --git a/arch/parisc/lib/fixup.S b/arch/parisc/lib/fixup.S index 536ef66bb94b..1052b747e011 100644 --- a/arch/parisc/lib/fixup.S +++ b/arch/parisc/lib/fixup.S @@ -26,6 +26,7 @@ #ifdef CONFIG_SMP .macro get_fault_ip t1 t2 + loadgp addil LT%__per_cpu_offset,%r27 LDREG RT%__per_cpu_offset(%r1),\t1 /* t2 = smp_processor_id() */ @@ -40,14 +41,19 @@ LDREG RT%exception_data(%r1),\t1 /* t1 = this_cpu_ptr(&exception_data) */ add,l \t1,\t2,\t1 + /* %r27 = t1->fault_gp - restore gp */ + LDREG EXCDATA_GP(\t1), %r27 /* t1 = t1->fault_ip */ LDREG EXCDATA_IP(\t1), \t1 .endm #else .macro get_fault_ip t1 t2 + loadgp /* t1 = this_cpu_ptr(&exception_data) */ addil LT%exception_data,%r27 LDREG RT%exception_data(%r1),\t2 + /* %r27 = t2->fault_gp - restore gp */ + LDREG EXCDATA_GP(\t2), %r27 /* t1 = t2->fault_ip */ LDREG EXCDATA_IP(\t2), \t1 .endm diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c index a762864ec92e..f9064449908a 100644 --- a/arch/parisc/mm/fault.c +++ b/arch/parisc/mm/fault.c @@ -151,6 +151,7 @@ int fixup_exception(struct pt_regs *regs) struct exception_data *d; d = this_cpu_ptr(&exception_data); d->fault_ip = regs->iaoq[0]; + d->fault_gp = regs->gr[27]; d->fault_space = regs->isr; d->fault_addr = regs->ior; diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index 9833fee493ec..807f1594701d 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -486,13 +486,13 @@ static void hugepd_free(struct mmu_gather *tlb, void *hugepte) { struct hugepd_freelist **batchp; - batchp = this_cpu_ptr(&hugepd_freelist_cur); + batchp = &get_cpu_var(hugepd_freelist_cur); if (atomic_read(&tlb->mm->mm_users) < 2 || cpumask_equal(mm_cpumask(tlb->mm), cpumask_of(smp_processor_id()))) { kmem_cache_free(hugepte_cache, hugepte); - put_cpu_var(hugepd_freelist_cur); + put_cpu_var(hugepd_freelist_cur); return; } diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index c873e682b67f..2b2ced9dc00a 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -45,7 +45,7 @@ struct zpci_fmb { u64 rpcit_ops; u64 dma_rbytes; u64 dma_wbytes; -} __packed __aligned(16); +} __packed __aligned(64); enum zpci_state { ZPCI_FN_STATE_RESERVED, diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 857b6526d298..424e6809ad07 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -1197,114 +1197,12 @@ cleanup_critical: .quad .Lpsw_idle_lpsw .Lcleanup_save_fpu_regs: - TSTMSK __LC_CPU_FLAGS,_CIF_FPU - bor %r14 - clg %r9,BASED(.Lcleanup_save_fpu_regs_done) - jhe 5f - clg %r9,BASED(.Lcleanup_save_fpu_regs_fp) - jhe 4f - clg %r9,BASED(.Lcleanup_save_fpu_regs_vx_high) - jhe 3f - clg %r9,BASED(.Lcleanup_save_fpu_regs_vx_low) - jhe 2f - clg %r9,BASED(.Lcleanup_save_fpu_fpc_end) - jhe 1f - lg %r2,__LC_CURRENT - aghi %r2,__TASK_thread -0: # Store floating-point controls - stfpc __THREAD_FPU_fpc(%r2) -1: # Load register save area and check if VX is active - lg %r3,__THREAD_FPU_regs(%r2) - TSTMSK __LC_MACHINE_FLAGS,MACHINE_FLAG_VX - jz 4f # no VX -> store FP regs -2: # Store vector registers (V0-V15) - VSTM %v0,%v15,0,%r3 # vstm 0,15,0(3) -3: # Store vector registers (V16-V31) - VSTM %v16,%v31,256,%r3 # vstm 16,31,256(3) - j 5f # -> done, set CIF_FPU flag -4: # Store floating-point registers - std 0,0(%r3) - std 1,8(%r3) - std 2,16(%r3) - std 3,24(%r3) - std 4,32(%r3) - std 5,40(%r3) - std 6,48(%r3) - std 7,56(%r3) - std 8,64(%r3) - std 9,72(%r3) - std 10,80(%r3) - std 11,88(%r3) - std 12,96(%r3) - std 13,104(%r3) - std 14,112(%r3) - std 15,120(%r3) -5: # Set CIF_FPU flag - oi __LC_CPU_FLAGS+7,_CIF_FPU - lg %r9,48(%r11) # return from save_fpu_regs + larl %r9,save_fpu_regs br %r14 -.Lcleanup_save_fpu_fpc_end: - .quad .Lsave_fpu_regs_fpc_end -.Lcleanup_save_fpu_regs_vx_low: - .quad .Lsave_fpu_regs_vx_low -.Lcleanup_save_fpu_regs_vx_high: - .quad .Lsave_fpu_regs_vx_high -.Lcleanup_save_fpu_regs_fp: - .quad .Lsave_fpu_regs_fp -.Lcleanup_save_fpu_regs_done: - .quad .Lsave_fpu_regs_done .Lcleanup_load_fpu_regs: - TSTMSK __LC_CPU_FLAGS,_CIF_FPU - bnor %r14 - clg %r9,BASED(.Lcleanup_load_fpu_regs_done) - jhe 1f - clg %r9,BASED(.Lcleanup_load_fpu_regs_fp) - jhe 2f - clg %r9,BASED(.Lcleanup_load_fpu_regs_vx_high) - jhe 3f - clg %r9,BASED(.Lcleanup_load_fpu_regs_vx) - jhe 4f - lg %r4,__LC_CURRENT - aghi %r4,__TASK_thread - lfpc __THREAD_FPU_fpc(%r4) - TSTMSK __LC_MACHINE_FLAGS,MACHINE_FLAG_VX - lg %r4,__THREAD_FPU_regs(%r4) # %r4 <- reg save area - jz 2f # -> no VX, load FP regs -4: # Load V0 ..V15 registers - VLM %v0,%v15,0,%r4 -3: # Load V16..V31 registers - VLM %v16,%v31,256,%r4 - j 1f -2: # Load floating-point registers - ld 0,0(%r4) - ld 1,8(%r4) - ld 2,16(%r4) - ld 3,24(%r4) - ld 4,32(%r4) - ld 5,40(%r4) - ld 6,48(%r4) - ld 7,56(%r4) - ld 8,64(%r4) - ld 9,72(%r4) - ld 10,80(%r4) - ld 11,88(%r4) - ld 12,96(%r4) - ld 13,104(%r4) - ld 14,112(%r4) - ld 15,120(%r4) -1: # Clear CIF_FPU bit - ni __LC_CPU_FLAGS+7,255-_CIF_FPU - lg %r9,48(%r11) # return from load_fpu_regs + larl %r9,load_fpu_regs br %r14 -.Lcleanup_load_fpu_regs_vx: - .quad .Lload_fpu_regs_vx -.Lcleanup_load_fpu_regs_vx_high: - .quad .Lload_fpu_regs_vx_high -.Lcleanup_load_fpu_regs_fp: - .quad .Lload_fpu_regs_fp -.Lcleanup_load_fpu_regs_done: - .quad .Lload_fpu_regs_done /* * Integer constants diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S index 58b719fa8067..1ad2407c7f75 100644 --- a/arch/s390/kernel/head64.S +++ b/arch/s390/kernel/head64.S @@ -16,7 +16,7 @@ __HEAD ENTRY(startup_continue) - tm __LC_STFL_FAC_LIST+6,0x80 # LPP available ? + tm __LC_STFL_FAC_LIST+5,0x80 # LPP available ? jz 0f xc __LC_LPP+1(7,0),__LC_LPP+1 # clear lpp and current_pid mvi __LC_LPP,0x80 # and set LPP_MAGIC diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index c837bcacf218..1f581eb61bc2 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -329,6 +329,7 @@ static void __init setup_lowcore(void) + PAGE_SIZE - STACK_FRAME_OVERHEAD - sizeof(struct pt_regs); lc->current_task = (unsigned long) init_thread_union.thread_info.task; lc->thread_info = (unsigned long) &init_thread_union; + lc->lpp = LPP_MAGIC; lc->machine_flags = S390_lowcore.machine_flags; lc->stfl_fac_list = S390_lowcore.stfl_fac_list; memcpy(lc->stfle_fac_list, S390_lowcore.stfle_fac_list, diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 7ef12a3ace3a..19442395f413 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -871,8 +871,11 @@ static inline int barsize(u8 size) static int zpci_mem_init(void) { + BUILD_BUG_ON(!is_power_of_2(__alignof__(struct zpci_fmb)) || + __alignof__(struct zpci_fmb) < sizeof(struct zpci_fmb)); + zdev_fmb_cache = kmem_cache_create("PCI_FMB_cache", sizeof(struct zpci_fmb), - 16, 0, NULL); + __alignof__(struct zpci_fmb), 0, NULL); if (!zdev_fmb_cache) goto error_zdev; diff --git a/arch/sh/mm/kmap.c b/arch/sh/mm/kmap.c index ec29e14ec5a8..bf25d7c79a2d 100644 --- a/arch/sh/mm/kmap.c +++ b/arch/sh/mm/kmap.c @@ -36,6 +36,7 @@ void *kmap_coherent(struct page *page, unsigned long addr) BUG_ON(!test_bit(PG_dcache_clean, &page->flags)); + preempt_disable(); pagefault_disable(); idx = FIX_CMAP_END - @@ -64,4 +65,5 @@ void kunmap_coherent(void *kvaddr) } pagefault_enable(); + preempt_enable(); } diff --git a/arch/um/configs/x86_64_defconfig b/arch/um/configs/x86_64_defconfig index 3aab117bd553..7a67b7ac1a7e 100644 --- a/arch/um/configs/x86_64_defconfig +++ b/arch/um/configs/x86_64_defconfig @@ -15,7 +15,6 @@ CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_DEVICE=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y -CONFIG_RESOURCE_COUNTERS=y CONFIG_CGROUP_SCHED=y CONFIG_BLK_CGROUP=y # CONFIG_PID_NS is not set diff --git a/arch/um/configs/x86_64_um_defconfig b/arch/um/configs/x86_64_um_defconfig index 487b68bad250..a37e38ef12fe 100644 --- a/arch/um/configs/x86_64_um_defconfig +++ b/arch/um/configs/x86_64_um_defconfig @@ -655,7 +655,6 @@ CONFIG_IPV6_MULTIPLE_TABLES=y # CONFIG_IPV6_SUBTREES is not set # CONFIG_IPV6_MROUTE is not set # CONFIG_ANDROID_PARANOID_NETWORK is not set -# CONFIG_NET_ACTIVITY_STATS is not set # CONFIG_NETWORK_SECMARK is not set # CONFIG_NET_PTP_CLASSIFY is not set # CONFIG_NETWORK_PHY_TIMESTAMPING is not set diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c index 29880c9b324e..e22e57298522 100644 --- a/arch/um/drivers/mconsole_kern.c +++ b/arch/um/drivers/mconsole_kern.c @@ -133,7 +133,7 @@ void mconsole_proc(struct mc_request *req) ptr += strlen("proc"); ptr = skip_spaces(ptr); - file = file_open_root(mnt->mnt_root, mnt, ptr, O_RDONLY); + file = file_open_root(mnt->mnt_root, mnt, ptr, O_RDONLY, 0); if (IS_ERR(file)) { mconsole_reply(req, "Failed to open file", 1, 0); printk(KERN_ERR "open /proc/%s: %ld\n", ptr, PTR_ERR(file)); diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index d57eca6b3777..737967e2cf28 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1146,22 +1146,23 @@ config MICROCODE bool "CPU microcode loading support" default y depends on CPU_SUP_AMD || CPU_SUP_INTEL - depends on BLK_DEV_INITRD select FW_LOADER ---help--- - If you say Y here, you will be able to update the microcode on - certain Intel and AMD processors. The Intel support is for the - IA32 family, e.g. Pentium Pro, Pentium II, Pentium III, Pentium 4, - Xeon etc. The AMD support is for families 0x10 and later. You will - obviously need the actual microcode binary data itself which is not - shipped with the Linux kernel. - - This option selects the general module only, you need to select - at least one vendor specific module as well. - - To compile this driver as a module, choose M here: the module - will be called microcode. + Intel and AMD processors. The Intel support is for the IA32 family, + e.g. Pentium Pro, Pentium II, Pentium III, Pentium 4, Xeon etc. The + AMD support is for families 0x10 and later. You will obviously need + the actual microcode binary data itself which is not shipped with + the Linux kernel. + + The preferred method to load microcode from a detached initrd is described + in Documentation/x86/early-microcode.txt. For that you need to enable + CONFIG_BLK_DEV_INITRD in order for the loader to be able to scan the + initrd for microcode blobs. + + In addition, you can build-in the microcode into the kernel. For that you + need to enable FIRMWARE_IN_KERNEL and add the vendor-supplied microcode + to the CONFIG_EXTRA_FIRMWARE config option. config MICROCODE_INTEL bool "Intel microcode loading support" diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c index 03663740c866..1a4477cedc49 100644 --- a/arch/x86/entry/common.c +++ b/arch/x86/entry/common.c @@ -268,6 +268,7 @@ static void exit_to_usermode_loop(struct pt_regs *regs, u32 cached_flags) /* Called with IRQs disabled. */ __visible inline void prepare_exit_to_usermode(struct pt_regs *regs) { + struct thread_info *ti = pt_regs_to_thread_info(regs); u32 cached_flags; if (IS_ENABLED(CONFIG_PROVE_LOCKING) && WARN_ON(!irqs_disabled())) @@ -275,12 +276,22 @@ __visible inline void prepare_exit_to_usermode(struct pt_regs *regs) lockdep_sys_exit(); - cached_flags = - READ_ONCE(pt_regs_to_thread_info(regs)->flags); + cached_flags = READ_ONCE(ti->flags); if (unlikely(cached_flags & EXIT_TO_USERMODE_LOOP_FLAGS)) exit_to_usermode_loop(regs, cached_flags); +#ifdef CONFIG_COMPAT + /* + * Compat syscalls set TS_COMPAT. Make sure we clear it before + * returning to user mode. We need to clear it *after* signal + * handling, because syscall restart has a fixup for compat + * syscalls. The fixup is exercised by the ptrace_syscall_32 + * selftest. + */ + ti->status &= ~TS_COMPAT; +#endif + user_enter(); } @@ -332,14 +343,6 @@ __visible inline void syscall_return_slowpath(struct pt_regs *regs) if (unlikely(cached_flags & SYSCALL_EXIT_WORK_FLAGS)) syscall_slow_exit_work(regs, cached_flags); -#ifdef CONFIG_COMPAT - /* - * Compat syscalls set TS_COMPAT. Make sure we clear it before - * returning to user mode. - */ - ti->status &= ~TS_COMPAT; -#endif - local_irq_disable(); prepare_exit_to_usermode(regs); } diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h index a30316bf801a..163769d82475 100644 --- a/arch/x86/include/asm/apic.h +++ b/arch/x86/include/asm/apic.h @@ -638,8 +638,8 @@ static inline void entering_irq(void) static inline void entering_ack_irq(void) { - ack_APIC_irq(); entering_irq(); + ack_APIC_irq(); } static inline void ipi_entering_ack_irq(void) diff --git a/arch/x86/include/asm/hw_irq.h b/arch/x86/include/asm/hw_irq.h index 1e3408e88604..59caa55fb9b5 100644 --- a/arch/x86/include/asm/hw_irq.h +++ b/arch/x86/include/asm/hw_irq.h @@ -136,6 +136,7 @@ struct irq_alloc_info { struct irq_cfg { unsigned int dest_apicid; u8 vector; + u8 old_vector; }; extern struct irq_cfg *irq_cfg(unsigned int irq); diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 30cfd64295a0..9d2abb2a41d2 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -41,7 +41,7 @@ #define KVM_PIO_PAGE_OFFSET 1 #define KVM_COALESCED_MMIO_PAGE_OFFSET 2 -#define KVM_HALT_POLL_NS_DEFAULT 500000 +#define KVM_HALT_POLL_NS_DEFAULT 400000 #define KVM_IRQCHIP_NUM_PINS KVM_IOAPIC_NUM_PINS diff --git a/arch/x86/include/asm/microcode.h b/arch/x86/include/asm/microcode.h index 34e62b1dcfce..712b24ed3a64 100644 --- a/arch/x86/include/asm/microcode.h +++ b/arch/x86/include/asm/microcode.h @@ -2,6 +2,7 @@ #define _ASM_X86_MICROCODE_H #include <linux/earlycpio.h> +#include <linux/initrd.h> #define native_rdmsr(msr, val1, val2) \ do { \ @@ -168,4 +169,29 @@ static inline void reload_early_microcode(void) { } static inline bool get_builtin_firmware(struct cpio_data *cd, const char *name) { return false; } #endif + +static inline unsigned long get_initrd_start(void) +{ +#ifdef CONFIG_BLK_DEV_INITRD + return initrd_start; +#else + return 0; +#endif +} + +static inline unsigned long get_initrd_start_addr(void) +{ +#ifdef CONFIG_BLK_DEV_INITRD +#ifdef CONFIG_X86_32 + unsigned long *initrd_start_p = (unsigned long *)__pa_nodebug(&initrd_start); + + return (unsigned long)__pa_nodebug(*initrd_start_p); +#else + return get_initrd_start(); +#endif +#else /* CONFIG_BLK_DEV_INITRD */ + return 0; +#endif +} + #endif /* _ASM_X86_MICROCODE_H */ diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index fa1195dae425..164e3f8d3c3d 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -93,6 +93,8 @@ extern raw_spinlock_t pci_config_lock; extern int (*pcibios_enable_irq)(struct pci_dev *dev); extern void (*pcibios_disable_irq)(struct pci_dev *dev); +extern bool mp_should_keep_irq(struct device *dev); + struct pci_raw_ops { int (*read)(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 *val); diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index 7bcb861a04e5..5a2ed3ed2f26 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h @@ -165,6 +165,7 @@ struct x86_pmu_capability { #define GLOBAL_STATUS_ASIF BIT_ULL(60) #define GLOBAL_STATUS_COUNTERS_FROZEN BIT_ULL(59) #define GLOBAL_STATUS_LBRS_FROZEN BIT_ULL(58) +#define GLOBAL_STATUS_TRACE_TOPAPMI BIT_ULL(55) /* * IBS cpuid feature detection diff --git a/arch/x86/include/asm/xen/hypervisor.h b/arch/x86/include/asm/xen/hypervisor.h index 8b2d4bea9962..39171b3646bb 100644 --- a/arch/x86/include/asm/xen/hypervisor.h +++ b/arch/x86/include/asm/xen/hypervisor.h @@ -62,4 +62,6 @@ void xen_arch_register_cpu(int num); void xen_arch_unregister_cpu(int num); #endif +extern void xen_set_iopl_mask(unsigned mask); + #endif /* _ASM_X86_XEN_HYPERVISOR_H */ diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c index a35f6b5473f4..7af2505f20c2 100644 --- a/arch/x86/kernel/apic/vector.c +++ b/arch/x86/kernel/apic/vector.c @@ -211,6 +211,7 @@ update: */ cpumask_and(d->old_domain, d->old_domain, cpu_online_mask); d->move_in_progress = !cpumask_empty(d->old_domain); + d->cfg.old_vector = d->move_in_progress ? d->cfg.vector : 0; d->cfg.vector = vector; cpumask_copy(d->domain, vector_cpumask); success: @@ -653,46 +654,97 @@ void irq_complete_move(struct irq_cfg *cfg) } /* - * Called with @desc->lock held and interrupts disabled. + * Called from fixup_irqs() with @desc->lock held and interrupts disabled. */ void irq_force_complete_move(struct irq_desc *desc) { struct irq_data *irqdata = irq_desc_get_irq_data(desc); struct apic_chip_data *data = apic_chip_data(irqdata); struct irq_cfg *cfg = data ? &data->cfg : NULL; + unsigned int cpu; if (!cfg) return; - __irq_complete_move(cfg, cfg->vector); - /* * This is tricky. If the cleanup of @data->old_domain has not been * done yet, then the following setaffinity call will fail with * -EBUSY. This can leave the interrupt in a stale state. * - * The cleanup cannot make progress because we hold @desc->lock. So in - * case @data->old_domain is not yet cleaned up, we need to drop the - * lock and acquire it again. @desc cannot go away, because the - * hotplug code holds the sparse irq lock. + * All CPUs are stuck in stop machine with interrupts disabled so + * calling __irq_complete_move() would be completely pointless. */ raw_spin_lock(&vector_lock); - /* Clean out all offline cpus (including ourself) first. */ + /* + * Clean out all offline cpus (including the outgoing one) from the + * old_domain mask. + */ cpumask_and(data->old_domain, data->old_domain, cpu_online_mask); - while (!cpumask_empty(data->old_domain)) { + + /* + * If move_in_progress is cleared and the old_domain mask is empty, + * then there is nothing to cleanup. fixup_irqs() will take care of + * the stale vectors on the outgoing cpu. + */ + if (!data->move_in_progress && cpumask_empty(data->old_domain)) { raw_spin_unlock(&vector_lock); - raw_spin_unlock(&desc->lock); - cpu_relax(); - raw_spin_lock(&desc->lock); + return; + } + + /* + * 1) The interrupt is in move_in_progress state. That means that we + * have not seen an interrupt since the io_apic was reprogrammed to + * the new vector. + * + * 2) The interrupt has fired on the new vector, but the cleanup IPIs + * have not been processed yet. + */ + if (data->move_in_progress) { /* - * Reevaluate apic_chip_data. It might have been cleared after - * we dropped @desc->lock. + * In theory there is a race: + * + * set_ioapic(new_vector) <-- Interrupt is raised before update + * is effective, i.e. it's raised on + * the old vector. + * + * So if the target cpu cannot handle that interrupt before + * the old vector is cleaned up, we get a spurious interrupt + * and in the worst case the ioapic irq line becomes stale. + * + * But in case of cpu hotplug this should be a non issue + * because if the affinity update happens right before all + * cpus rendevouz in stop machine, there is no way that the + * interrupt can be blocked on the target cpu because all cpus + * loops first with interrupts enabled in stop machine, so the + * old vector is not yet cleaned up when the interrupt fires. + * + * So the only way to run into this issue is if the delivery + * of the interrupt on the apic/system bus would be delayed + * beyond the point where the target cpu disables interrupts + * in stop machine. I doubt that it can happen, but at least + * there is a theroretical chance. Virtualization might be + * able to expose this, but AFAICT the IOAPIC emulation is not + * as stupid as the real hardware. + * + * Anyway, there is nothing we can do about that at this point + * w/o refactoring the whole fixup_irq() business completely. + * We print at least the irq number and the old vector number, + * so we have the necessary information when a problem in that + * area arises. */ - data = apic_chip_data(irqdata); - if (!data) - return; - raw_spin_lock(&vector_lock); + pr_warn("IRQ fixup: irq %d move in progress, old vector %d\n", + irqdata->irq, cfg->old_vector); } + /* + * If old_domain is not empty, then other cpus still have the irq + * descriptor set in their vector array. Clean it up. + */ + for_each_cpu(cpu, data->old_domain) + per_cpu(vector_irq, cpu)[cfg->old_vector] = VECTOR_UNUSED; + + /* Cleanup the left overs of the (half finished) move */ + cpumask_clear(data->old_domain); + data->move_in_progress = 0; raw_spin_unlock(&vector_lock); } #endif diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c index ce47402eb2f9..ac8975a65280 100644 --- a/arch/x86/kernel/cpu/microcode/intel.c +++ b/arch/x86/kernel/cpu/microcode/intel.c @@ -555,10 +555,14 @@ scan_microcode(struct mc_saved_data *mc_saved_data, unsigned long *initrd, cd.data = NULL; cd.size = 0; - cd = find_cpio_data(p, (void *)start, size, &offset); - if (!cd.data) { + /* try built-in microcode if no initrd */ + if (!size) { if (!load_builtin_intel_microcode(&cd)) return UCODE_ERROR; + } else { + cd = find_cpio_data(p, (void *)start, size, &offset); + if (!cd.data) + return UCODE_ERROR; } return get_matching_model_microcode(0, start, cd.data, cd.size, @@ -694,7 +698,7 @@ int __init save_microcode_in_initrd_intel(void) if (count == 0) return ret; - copy_initrd_ptrs(mc_saved, mc_saved_in_initrd, initrd_start, count); + copy_initrd_ptrs(mc_saved, mc_saved_in_initrd, get_initrd_start(), count); ret = save_microcode(&mc_saved_data, mc_saved, count); if (ret) pr_err("Cannot save microcode patches from initrd.\n"); @@ -732,16 +736,20 @@ void __init load_ucode_intel_bsp(void) struct boot_params *p; p = (struct boot_params *)__pa_nodebug(&boot_params); - start = p->hdr.ramdisk_image; size = p->hdr.ramdisk_size; - _load_ucode_intel_bsp( - (struct mc_saved_data *)__pa_nodebug(&mc_saved_data), - (unsigned long *)__pa_nodebug(&mc_saved_in_initrd), - start, size); + /* + * Set start only if we have an initrd image. We cannot use initrd_start + * because it is not set that early yet. + */ + start = (size ? p->hdr.ramdisk_image : 0); + + _load_ucode_intel_bsp((struct mc_saved_data *)__pa_nodebug(&mc_saved_data), + (unsigned long *)__pa_nodebug(&mc_saved_in_initrd), + start, size); #else - start = boot_params.hdr.ramdisk_image + PAGE_OFFSET; size = boot_params.hdr.ramdisk_size; + start = (size ? boot_params.hdr.ramdisk_image + PAGE_OFFSET : 0); _load_ucode_intel_bsp(&mc_saved_data, mc_saved_in_initrd, start, size); #endif @@ -752,20 +760,14 @@ void load_ucode_intel_ap(void) struct mc_saved_data *mc_saved_data_p; struct ucode_cpu_info uci; unsigned long *mc_saved_in_initrd_p; - unsigned long initrd_start_addr; enum ucode_state ret; #ifdef CONFIG_X86_32 - unsigned long *initrd_start_p; - mc_saved_in_initrd_p = - (unsigned long *)__pa_nodebug(mc_saved_in_initrd); + mc_saved_in_initrd_p = (unsigned long *)__pa_nodebug(mc_saved_in_initrd); mc_saved_data_p = (struct mc_saved_data *)__pa_nodebug(&mc_saved_data); - initrd_start_p = (unsigned long *)__pa_nodebug(&initrd_start); - initrd_start_addr = (unsigned long)__pa_nodebug(*initrd_start_p); #else - mc_saved_data_p = &mc_saved_data; mc_saved_in_initrd_p = mc_saved_in_initrd; - initrd_start_addr = initrd_start; + mc_saved_data_p = &mc_saved_data; #endif /* @@ -777,7 +779,7 @@ void load_ucode_intel_ap(void) collect_cpu_info_early(&uci); ret = load_microcode(mc_saved_data_p, mc_saved_in_initrd_p, - initrd_start_addr, &uci); + get_initrd_start_addr(), &uci); if (ret != UCODE_OK) return; diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 2bf79d7c97df..a3aeb2cc361e 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -593,6 +593,19 @@ void x86_pmu_disable_all(void) } } +/* + * There may be PMI landing after enabled=0. The PMI hitting could be before or + * after disable_all. + * + * If PMI hits before disable_all, the PMU will be disabled in the NMI handler. + * It will not be re-enabled in the NMI handler again, because enabled=0. After + * handling the NMI, disable_all will be called, which will not change the + * state either. If PMI hits after disable_all, the PMU is already disabled + * before entering NMI handler. The NMI handler will not change the state + * either. + * + * So either situation is harmless. + */ static void x86_pmu_disable(struct pmu *pmu) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index d0e35ebb2adb..ee70445fbb1f 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h @@ -591,6 +591,7 @@ struct x86_pmu { pebs_active :1, pebs_broken :1; int pebs_record_size; + int pebs_buffer_size; void (*drain_pebs)(struct pt_regs *regs); struct event_constraint *pebs_constraints; void (*pebs_aliases)(struct perf_event *event); @@ -907,6 +908,8 @@ void intel_pmu_lbr_init_hsw(void); void intel_pmu_lbr_init_skl(void); +void intel_pmu_pebs_data_source_nhm(void); + int intel_pmu_setup_lbr_filter(struct perf_event *event); void intel_pt_interrupt(void); diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index e2a430021e46..078de2e86b7a 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -1458,7 +1458,15 @@ static __initconst const u64 slm_hw_cache_event_ids }; /* - * Use from PMIs where the LBRs are already disabled. + * Used from PMIs where the LBRs are already disabled. + * + * This function could be called consecutively. It is required to remain in + * disabled state if called consecutively. + * + * During consecutive calls, the same disable value will be written to related + * registers, so the PMU state remains unchanged. hw.state in + * intel_bts_disable_local will remain PERF_HES_STOPPED too in consecutive + * calls. */ static void __intel_pmu_disable_all(void) { @@ -1840,6 +1848,16 @@ again: if (__test_and_clear_bit(62, (unsigned long *)&status)) { handled++; x86_pmu.drain_pebs(regs); + /* + * There are cases where, even though, the PEBS ovfl bit is set + * in GLOBAL_OVF_STATUS, the PEBS events may also have their + * overflow bits set for their counters. We must clear them + * here because they have been processed as exact samples in + * the drain_pebs() routine. They must not be processed again + * in the for_each_bit_set() loop for regular samples below. + */ + status &= ~cpuc->pebs_enabled; + status &= x86_pmu.intel_ctrl | GLOBAL_STATUS_TRACE_TOPAPMI; } /* @@ -1885,7 +1903,10 @@ again: goto again; done: - __intel_pmu_enable_all(0, true); + /* Only restore PMU state when it's active. See x86_pmu_disable(). */ + if (cpuc->enabled) + __intel_pmu_enable_all(0, true); + /* * Only unmask the NMI after the overflow counters * have been reset. This avoids spurious NMIs on @@ -3315,6 +3336,7 @@ __init int intel_pmu_init(void) intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = X86_CONFIG(.event=0xb1, .umask=0x3f, .inv=1, .cmask=1); + intel_pmu_pebs_data_source_nhm(); x86_add_quirk(intel_nehalem_quirk); pr_cont("Nehalem events, "); @@ -3377,6 +3399,7 @@ __init int intel_pmu_init(void) intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = X86_CONFIG(.event=0xb1, .umask=0x3f, .inv=1, .cmask=1); + intel_pmu_pebs_data_source_nhm(); pr_cont("Westmere events, "); break; diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 5db1c7755548..7abb2b88572e 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -51,7 +51,8 @@ union intel_x86_pebs_dse { #define OP_LH (P(OP, LOAD) | P(LVL, HIT)) #define SNOOP_NONE_MISS (P(SNOOP, NONE) | P(SNOOP, MISS)) -static const u64 pebs_data_source[] = { +/* Version for Sandy Bridge and later */ +static u64 pebs_data_source[] = { P(OP, LOAD) | P(LVL, MISS) | P(LVL, L3) | P(SNOOP, NA),/* 0x00:ukn L3 */ OP_LH | P(LVL, L1) | P(SNOOP, NONE), /* 0x01: L1 local */ OP_LH | P(LVL, LFB) | P(SNOOP, NONE), /* 0x02: LFB hit */ @@ -70,6 +71,14 @@ static const u64 pebs_data_source[] = { OP_LH | P(LVL, UNC) | P(SNOOP, NONE), /* 0x0f: uncached */ }; +/* Patch up minor differences in the bits */ +void __init intel_pmu_pebs_data_source_nhm(void) +{ + pebs_data_source[0x05] = OP_LH | P(LVL, L3) | P(SNOOP, HIT); + pebs_data_source[0x06] = OP_LH | P(LVL, L3) | P(SNOOP, HITM); + pebs_data_source[0x07] = OP_LH | P(LVL, L3) | P(SNOOP, HITM); +} + static u64 precise_store_data(u64 status) { union intel_x86_pebs_dse dse; @@ -269,7 +278,7 @@ static int alloc_pebs_buffer(int cpu) if (!x86_pmu.pebs) return 0; - buffer = kzalloc_node(PEBS_BUFFER_SIZE, GFP_KERNEL, node); + buffer = kzalloc_node(x86_pmu.pebs_buffer_size, GFP_KERNEL, node); if (unlikely(!buffer)) return -ENOMEM; @@ -286,7 +295,7 @@ static int alloc_pebs_buffer(int cpu) per_cpu(insn_buffer, cpu) = ibuffer; } - max = PEBS_BUFFER_SIZE / x86_pmu.pebs_record_size; + max = x86_pmu.pebs_buffer_size / x86_pmu.pebs_record_size; ds->pebs_buffer_base = (u64)(unsigned long)buffer; ds->pebs_index = ds->pebs_buffer_base; @@ -1296,6 +1305,7 @@ void __init intel_ds_init(void) x86_pmu.bts = boot_cpu_has(X86_FEATURE_BTS); x86_pmu.pebs = boot_cpu_has(X86_FEATURE_PEBS); + x86_pmu.pebs_buffer_size = PEBS_BUFFER_SIZE; if (x86_pmu.pebs) { char pebs_type = x86_pmu.intel_cap.pebs_trap ? '+' : '-'; int format = x86_pmu.intel_cap.pebs_format; @@ -1304,6 +1314,14 @@ void __init intel_ds_init(void) case 0: printk(KERN_CONT "PEBS fmt0%c, ", pebs_type); x86_pmu.pebs_record_size = sizeof(struct pebs_record_core); + /* + * Using >PAGE_SIZE buffers makes the WRMSR to + * PERF_GLOBAL_CTRL in intel_pmu_enable_all() + * mysteriously hang on Core2. + * + * As a workaround, we don't do this. + */ + x86_pmu.pebs_buffer_size = PAGE_SIZE; x86_pmu.drain_pebs = intel_pmu_drain_pebs_core; break; diff --git a/arch/x86/kernel/cpu/perf_event_knc.c b/arch/x86/kernel/cpu/perf_event_knc.c index 5b0c232d1ee6..b931095e86d4 100644 --- a/arch/x86/kernel/cpu/perf_event_knc.c +++ b/arch/x86/kernel/cpu/perf_event_knc.c @@ -263,7 +263,9 @@ again: goto again; done: - knc_pmu_enable_all(0); + /* Only restore PMU state when it's active. See x86_pmu_disable(). */ + if (cpuc->enabled) + knc_pmu_enable_all(0); return handled; } diff --git a/arch/x86/kernel/ioport.c b/arch/x86/kernel/ioport.c index 37dae792dbbe..589b3193f102 100644 --- a/arch/x86/kernel/ioport.c +++ b/arch/x86/kernel/ioport.c @@ -96,9 +96,14 @@ asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int turn_on) SYSCALL_DEFINE1(iopl, unsigned int, level) { struct pt_regs *regs = current_pt_regs(); - unsigned int old = (regs->flags >> 12) & 3; struct thread_struct *t = ¤t->thread; + /* + * Careful: the IOPL bits in regs->flags are undefined under Xen PV + * and changing them has no effect. + */ + unsigned int old = t->iopl >> X86_EFLAGS_IOPL_BIT; + if (level > 3) return -EINVAL; /* Trying to gain more privileges? */ @@ -106,8 +111,9 @@ SYSCALL_DEFINE1(iopl, unsigned int, level) if (!capable(CAP_SYS_RAWIO)) return -EPERM; } - regs->flags = (regs->flags & ~X86_EFLAGS_IOPL) | (level << 12); - t->iopl = level << 12; + regs->flags = (regs->flags & ~X86_EFLAGS_IOPL) | + (level << X86_EFLAGS_IOPL_BIT); + t->iopl = level << X86_EFLAGS_IOPL_BIT; set_iopl_mask(t->iopl); return 0; diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index e835d263a33b..4cbb60fbff3e 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -48,6 +48,7 @@ #include <asm/syscalls.h> #include <asm/debugreg.h> #include <asm/switch_to.h> +#include <asm/xen/hypervisor.h> asmlinkage extern void ret_from_fork(void); @@ -411,6 +412,17 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p) task_thread_info(prev_p)->flags & _TIF_WORK_CTXSW_PREV)) __switch_to_xtra(prev_p, next_p, tss); +#ifdef CONFIG_XEN + /* + * On Xen PV, IOPL bits in pt_regs->flags have no effect, and + * current_pt_regs()->flags may not match the current task's + * intended IOPL. We need to switch it manually. + */ + if (unlikely(static_cpu_has(X86_FEATURE_XENPV) && + prev->iopl != next->iopl)) + xen_set_iopl_mask(next->iopl); +#endif + if (static_cpu_has_bug(X86_BUG_SYSRET_SS_ATTRS)) { /* * AMD CPUs have a misfeature: SYSRET sets the SS selector but diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c index b0ea42b78ccd..ab5318727579 100644 --- a/arch/x86/kvm/i8254.c +++ b/arch/x86/kvm/i8254.c @@ -245,7 +245,7 @@ static void kvm_pit_ack_irq(struct kvm_irq_ack_notifier *kian) * PIC is being reset. Handle it gracefully here */ atomic_inc(&ps->pending); - else if (value > 0) + else if (value > 0 && ps->reinject) /* in this case, we had multiple outstanding pit interrupts * that we needed to inject. Reinject */ @@ -288,7 +288,9 @@ static void pit_do_work(struct kthread_work *work) * last one has been acked. */ spin_lock(&ps->inject_lock); - if (ps->irq_ack) { + if (!ps->reinject) + inject = 1; + else if (ps->irq_ack) { ps->irq_ack = 0; inject = 1; } @@ -317,10 +319,10 @@ static enum hrtimer_restart pit_timer_fn(struct hrtimer *data) struct kvm_kpit_state *ps = container_of(data, struct kvm_kpit_state, timer); struct kvm_pit *pt = ps->kvm->arch.vpit; - if (ps->reinject || !atomic_read(&ps->pending)) { + if (ps->reinject) atomic_inc(&ps->pending); - queue_kthread_work(&pt->worker, &pt->expired); - } + + queue_kthread_work(&pt->worker, &pt->expired); if (ps->is_periodic) { hrtimer_add_expires_ns(&ps->timer, ps->period); diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 0958fa2b7cb7..f34ab71dfd57 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -2637,8 +2637,15 @@ static void nested_vmx_setup_ctls_msrs(struct vcpu_vmx *vmx) } else vmx->nested.nested_vmx_ept_caps = 0; + /* + * Old versions of KVM use the single-context version without + * checking for support, so declare that it is supported even + * though it is treated as global context. The alternative is + * not failing the single-context invvpid, and it is worse. + */ if (enable_vpid) vmx->nested.nested_vmx_vpid_caps = VMX_VPID_INVVPID_BIT | + VMX_VPID_EXTENT_SINGLE_CONTEXT_BIT | VMX_VPID_EXTENT_GLOBAL_CONTEXT_BIT; else vmx->nested.nested_vmx_vpid_caps = 0; @@ -7340,6 +7347,7 @@ static int handle_invept(struct kvm_vcpu *vcpu) if (!(types & (1UL << type))) { nested_vmx_failValid(vcpu, VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID); + skip_emulated_instruction(vcpu); return 1; } @@ -7398,6 +7406,7 @@ static int handle_invvpid(struct kvm_vcpu *vcpu) if (!(types & (1UL << type))) { nested_vmx_failValid(vcpu, VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID); + skip_emulated_instruction(vcpu); return 1; } @@ -7414,12 +7423,17 @@ static int handle_invvpid(struct kvm_vcpu *vcpu) } switch (type) { + case VMX_VPID_EXTENT_SINGLE_CONTEXT: + /* + * Old versions of KVM use the single-context version so we + * have to support it; just treat it the same as all-context. + */ case VMX_VPID_EXTENT_ALL_CONTEXT: __vmx_flush_tlb(vcpu, to_vmx(vcpu)->nested.vpid02); nested_vmx_succeed(vcpu); break; default: - /* Trap single context invalidation invvpid calls */ + /* Trap individual address invalidation invvpid calls */ BUG_ON(1); break; } diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index d2945024ed33..7eb4ebd3ebea 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2736,6 +2736,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) } kvm_make_request(KVM_REQ_STEAL_UPDATE, vcpu); + vcpu->arch.switch_db_regs |= KVM_DEBUGREG_RELOAD; } void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) @@ -6023,12 +6024,10 @@ static int inject_pending_event(struct kvm_vcpu *vcpu, bool req_int_win) } /* try to inject new event if pending */ - if (vcpu->arch.nmi_pending) { - if (kvm_x86_ops->nmi_allowed(vcpu)) { - --vcpu->arch.nmi_pending; - vcpu->arch.nmi_injected = true; - kvm_x86_ops->set_nmi(vcpu); - } + if (vcpu->arch.nmi_pending && kvm_x86_ops->nmi_allowed(vcpu)) { + --vcpu->arch.nmi_pending; + vcpu->arch.nmi_injected = true; + kvm_x86_ops->set_nmi(vcpu); } else if (kvm_cpu_has_injectable_intr(vcpu)) { /* * Because interrupts can be injected asynchronously, we are @@ -6473,10 +6472,12 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) if (inject_pending_event(vcpu, req_int_win) != 0) req_immediate_exit = true; /* enable NMI/IRQ window open exits if needed */ - else if (vcpu->arch.nmi_pending) - kvm_x86_ops->enable_nmi_window(vcpu); - else if (kvm_cpu_has_injectable_intr(vcpu) || req_int_win) - kvm_x86_ops->enable_irq_window(vcpu); + else { + if (vcpu->arch.nmi_pending) + kvm_x86_ops->enable_nmi_window(vcpu); + if (kvm_cpu_has_injectable_intr(vcpu) || req_int_win) + kvm_x86_ops->enable_irq_window(vcpu); + } if (kvm_lapic_enabled(vcpu)) { update_cr8_intercept(vcpu); diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c index 8f4cc3dfac32..5fb6adaaa796 100644 --- a/arch/x86/mm/tlb.c +++ b/arch/x86/mm/tlb.c @@ -106,8 +106,6 @@ static void flush_tlb_func(void *info) if (f->flush_mm != this_cpu_read(cpu_tlbstate.active_mm)) return; - if (!f->flush_end) - f->flush_end = f->flush_start + PAGE_SIZE; count_vm_tlb_event(NR_TLB_REMOTE_FLUSH_RECEIVED); if (this_cpu_read(cpu_tlbstate.state) == TLBSTATE_OK) { @@ -135,12 +133,20 @@ void native_flush_tlb_others(const struct cpumask *cpumask, unsigned long end) { struct flush_tlb_info info; + + if (end == 0) + end = start + PAGE_SIZE; info.flush_mm = mm; info.flush_start = start; info.flush_end = end; count_vm_tlb_event(NR_TLB_REMOTE_FLUSH); - trace_tlb_flush(TLB_REMOTE_SEND_IPI, end - start); + if (end == TLB_FLUSH_ALL) + trace_tlb_flush(TLB_REMOTE_SEND_IPI, TLB_FLUSH_ALL); + else + trace_tlb_flush(TLB_REMOTE_SEND_IPI, + (end - start) >> PAGE_SHIFT); + if (is_uv_system()) { unsigned int cpu; diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index eccd4d99e6a4..8fd6f44aee83 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c @@ -673,28 +673,22 @@ int pcibios_add_device(struct pci_dev *dev) return 0; } -int pcibios_alloc_irq(struct pci_dev *dev) +int pcibios_enable_device(struct pci_dev *dev, int mask) { - /* - * If the PCI device was already claimed by core code and has - * MSI enabled, probing of the pcibios IRQ will overwrite - * dev->irq. So bail out if MSI is already enabled. - */ - if (pci_dev_msi_enabled(dev)) - return -EBUSY; + int err; - return pcibios_enable_irq(dev); -} + if ((err = pci_enable_resources(dev, mask)) < 0) + return err; -void pcibios_free_irq(struct pci_dev *dev) -{ - if (pcibios_disable_irq) - pcibios_disable_irq(dev); + if (!pci_dev_msi_enabled(dev)) + return pcibios_enable_irq(dev); + return 0; } -int pcibios_enable_device(struct pci_dev *dev, int mask) +void pcibios_disable_device (struct pci_dev *dev) { - return pci_enable_resources(dev, mask); + if (!pci_dev_msi_enabled(dev) && pcibios_disable_irq) + pcibios_disable_irq(dev); } int pci_ext_cfg_avail(void) diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c index e58565556703..0ae7e9fa348d 100644 --- a/arch/x86/pci/fixup.c +++ b/arch/x86/pci/fixup.c @@ -540,3 +540,10 @@ static void twinhead_reserve_killing_zone(struct pci_dev *dev) } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x27B9, twinhead_reserve_killing_zone); + +static void pci_bdwep_bar(struct pci_dev *dev) +{ + dev->non_compliant_bars = 1; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x6fa0, pci_bdwep_bar); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x6fc0, pci_bdwep_bar); diff --git a/arch/x86/pci/intel_mid_pci.c b/arch/x86/pci/intel_mid_pci.c index 0d24e7c10145..8b93e634af84 100644 --- a/arch/x86/pci/intel_mid_pci.c +++ b/arch/x86/pci/intel_mid_pci.c @@ -215,7 +215,7 @@ static int intel_mid_pci_irq_enable(struct pci_dev *dev) int polarity; int ret; - if (pci_has_managed_irq(dev)) + if (dev->irq_managed && dev->irq > 0) return 0; switch (intel_mid_identify_cpu()) { @@ -256,13 +256,10 @@ static int intel_mid_pci_irq_enable(struct pci_dev *dev) static void intel_mid_pci_irq_disable(struct pci_dev *dev) { - if (pci_has_managed_irq(dev)) { + if (!mp_should_keep_irq(&dev->dev) && dev->irq_managed && + dev->irq > 0) { mp_unmap_irq(dev->irq); dev->irq_managed = 0; - /* - * Don't reset dev->irq here, otherwise - * intel_mid_pci_irq_enable() will fail on next call. - */ } } diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c index 32e70343e6fd..9bd115484745 100644 --- a/arch/x86/pci/irq.c +++ b/arch/x86/pci/irq.c @@ -1202,7 +1202,7 @@ static int pirq_enable_irq(struct pci_dev *dev) struct pci_dev *temp_dev; int irq; - if (pci_has_managed_irq(dev)) + if (dev->irq_managed && dev->irq > 0) return 0; irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, @@ -1230,7 +1230,8 @@ static int pirq_enable_irq(struct pci_dev *dev) } dev = temp_dev; if (irq >= 0) { - pci_set_managed_irq(dev, irq); + dev->irq_managed = 1; + dev->irq = irq; dev_info(&dev->dev, "PCI->APIC IRQ transform: " "INT %c -> IRQ %d\n", 'A' + pin - 1, irq); return 0; @@ -1256,10 +1257,24 @@ static int pirq_enable_irq(struct pci_dev *dev) return 0; } +bool mp_should_keep_irq(struct device *dev) +{ + if (dev->power.is_prepared) + return true; +#ifdef CONFIG_PM + if (dev->power.runtime_status == RPM_SUSPENDING) + return true; +#endif + + return false; +} + static void pirq_disable_irq(struct pci_dev *dev) { - if (io_apic_assign_pci_irqs && pci_has_managed_irq(dev)) { + if (io_apic_assign_pci_irqs && !mp_should_keep_irq(&dev->dev) && + dev->irq_managed && dev->irq) { mp_unmap_irq(dev->irq); - pci_reset_managed_irq(dev); + dev->irq = 0; + dev->irq_managed = 0; } } diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index b7de78bdc09c..beab8c706ac9 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -961,7 +961,7 @@ static void xen_load_sp0(struct tss_struct *tss, tss->x86_tss.sp0 = thread->sp0; } -static void xen_set_iopl_mask(unsigned mask) +void xen_set_iopl_mask(unsigned mask) { struct physdev_set_iopl set_iopl; diff --git a/arch/xtensa/kernel/head.S b/arch/xtensa/kernel/head.S index 9ed55649ac8e..05e1df943856 100644 --- a/arch/xtensa/kernel/head.S +++ b/arch/xtensa/kernel/head.S @@ -128,7 +128,7 @@ ENTRY(_startup) wsr a0, icountlevel .set _index, 0 - .rept XCHAL_NUM_DBREAK - 1 + .rept XCHAL_NUM_DBREAK wsr a0, SREG_DBREAKC + _index .set _index, _index + 1 .endr diff --git a/arch/xtensa/mm/cache.c b/arch/xtensa/mm/cache.c index d75aa1476da7..1a804a2f9a5b 100644 --- a/arch/xtensa/mm/cache.c +++ b/arch/xtensa/mm/cache.c @@ -97,11 +97,11 @@ void clear_user_highpage(struct page *page, unsigned long vaddr) unsigned long paddr; void *kvaddr = coherent_kvaddr(page, TLBTEMP_BASE_1, vaddr, &paddr); - pagefault_disable(); + preempt_disable(); kmap_invalidate_coherent(page, vaddr); set_bit(PG_arch_1, &page->flags); clear_page_alias(kvaddr, paddr); - pagefault_enable(); + preempt_enable(); } void copy_user_highpage(struct page *dst, struct page *src, @@ -113,11 +113,11 @@ void copy_user_highpage(struct page *dst, struct page *src, void *src_vaddr = coherent_kvaddr(src, TLBTEMP_BASE_2, vaddr, &src_paddr); - pagefault_disable(); + preempt_disable(); kmap_invalidate_coherent(dst, vaddr); set_bit(PG_arch_1, &dst->flags); copy_page_alias(dst_vaddr, src_vaddr, dst_paddr, src_paddr); - pagefault_enable(); + preempt_enable(); } #endif /* DCACHE_WAY_SIZE > PAGE_SIZE */ diff --git a/arch/xtensa/platforms/iss/console.c b/arch/xtensa/platforms/iss/console.c index 70cb408bc20d..92d785fefb6d 100644 --- a/arch/xtensa/platforms/iss/console.c +++ b/arch/xtensa/platforms/iss/console.c @@ -100,21 +100,23 @@ static void rs_poll(unsigned long priv) { struct tty_port *port = (struct tty_port *)priv; int i = 0; + int rd = 1; unsigned char c; spin_lock(&timer_lock); while (simc_poll(0)) { - simc_read(0, &c, 1); + rd = simc_read(0, &c, 1); + if (rd <= 0) + break; tty_insert_flip_char(port, c, TTY_NORMAL); i++; } if (i) tty_flip_buffer_push(port); - - - mod_timer(&serial_timer, jiffies + SERIAL_TIMER_VALUE); + if (rd) + mod_timer(&serial_timer, jiffies + SERIAL_TIMER_VALUE); spin_unlock(&timer_lock); } diff --git a/block/blk-core.c b/block/blk-core.c index 22baa252213c..5101e6a7bd48 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2218,7 +2218,7 @@ int blk_insert_cloned_request(struct request_queue *q, struct request *rq) if (q->mq_ops) { if (blk_queue_io_stat(q)) blk_account_io_start(rq, true); - blk_mq_insert_request(rq, false, true, true); + blk_mq_insert_request(rq, false, true, false); return 0; } diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index 90d6d47965b0..ecdb5a2ce085 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -178,6 +178,8 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, int cached_ret = -ENOKEY; int ret; + *_trusted = false; + for (p = pkcs7->certs; p; p = p->next) p->seen = false; diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 021d39c0ba75..13c4e5a5fe8c 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -494,7 +494,7 @@ int x509_decode_time(time64_t *_t, size_t hdrlen, unsigned char tag, const unsigned char *value, size_t vlen) { - static const unsigned char month_lengths[] = { 31, 29, 31, 30, 31, 30, + static const unsigned char month_lengths[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; const unsigned char *p = value; unsigned year, mon, day, hour, min, sec, mon_len; @@ -540,9 +540,9 @@ int x509_decode_time(time64_t *_t, size_t hdrlen, if (year % 4 == 0) { mon_len = 29; if (year % 100 == 0) { - year /= 100; - if (year % 4 != 0) - mon_len = 28; + mon_len = 28; + if (year % 400 == 0) + mon_len = 29; } } } diff --git a/crypto/keywrap.c b/crypto/keywrap.c index b1d106ce55f3..72014f963ba7 100644 --- a/crypto/keywrap.c +++ b/crypto/keywrap.c @@ -212,7 +212,7 @@ static int crypto_kw_decrypt(struct blkcipher_desc *desc, SEMIBSIZE)) ret = -EBADMSG; - memzero_explicit(&block, sizeof(struct crypto_kw_block)); + memzero_explicit(block, sizeof(struct crypto_kw_block)); return ret; } @@ -297,7 +297,7 @@ static int crypto_kw_encrypt(struct blkcipher_desc *desc, /* establish the IV for the caller to pick up */ memcpy(desc->info, block->A, SEMIBSIZE); - memzero_explicit(&block, sizeof(struct crypto_kw_block)); + memzero_explicit(block, sizeof(struct crypto_kw_block)); return 0; } diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index c9336751e5e3..8a10a7ae6a8a 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -409,7 +409,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev) return 0; } - if (pci_has_managed_irq(dev)) + if (dev->irq_managed && dev->irq > 0) return 0; entry = acpi_pci_irq_lookup(dev, pin); @@ -454,7 +454,8 @@ int acpi_pci_irq_enable(struct pci_dev *dev) kfree(entry); return rc; } - pci_set_managed_irq(dev, rc); + dev->irq = rc; + dev->irq_managed = 1; if (link) snprintf(link_desc, sizeof(link_desc), " -> Link[%s]", link); @@ -477,9 +478,17 @@ void acpi_pci_irq_disable(struct pci_dev *dev) u8 pin; pin = dev->pin; - if (!pin || !pci_has_managed_irq(dev)) + if (!pin || !dev->irq_managed || dev->irq <= 0) return; + /* Keep IOAPIC pin configuration when suspending */ + if (dev->dev.power.is_prepared) + return; +#ifdef CONFIG_PM + if (dev->dev.power.runtime_status == RPM_SUSPENDING) + return; +#endif + entry = acpi_pci_irq_lookup(dev, pin); if (!entry) return; @@ -499,6 +508,6 @@ void acpi_pci_irq_disable(struct pci_dev *dev) dev_dbg(&dev->dev, "PCI INT %c disabled\n", pin_name(pin)); if (gsi >= 0) { acpi_unregister_gsi(gsi); - pci_reset_managed_irq(dev); + dev->irq_managed = 0; } } diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index cdc5c2599beb..627f8fbb5e9a 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -26,8 +26,20 @@ #ifdef CONFIG_X86 #define valid_IRQ(i) (((i) != 0) && ((i) != 2)) +static inline bool acpi_iospace_resource_valid(struct resource *res) +{ + /* On X86 IO space is limited to the [0 - 64K] IO port range */ + return res->end < 0x10003; +} #else #define valid_IRQ(i) (true) +/* + * ACPI IO descriptors on arches other than X86 contain MMIO CPU physical + * addresses mapping IO space in CPU physical address space, IO space + * resources can be placed anywhere in the 64-bit physical address space. + */ +static inline bool +acpi_iospace_resource_valid(struct resource *res) { return true; } #endif static bool acpi_dev_resource_len_valid(u64 start, u64 end, u64 len, bool io) @@ -126,7 +138,7 @@ static void acpi_dev_ioresource_flags(struct resource *res, u64 len, if (!acpi_dev_resource_len_valid(res->start, res->end, len, true)) res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; - if (res->end >= 0x10003) + if (!acpi_iospace_resource_valid(res)) res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; if (io_decode == ACPI_DECODE_16) diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 0d94621dc856..e3322adaaae0 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -714,6 +714,7 @@ static int acpi_hibernation_enter(void) static void acpi_hibernation_leave(void) { + pm_set_resume_via_firmware(); /* * If ACPI is not enabled by the BIOS and the boot kernel, we need to * enable it here. diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 4c67945ef36f..20d17906fc9b 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -379,6 +379,7 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) struct files_struct *files = proc->files; unsigned long rlim_cur; unsigned long irqs; + int ret; if (files == NULL) return -ESRCH; @@ -389,7 +390,11 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) rlim_cur = task_rlimit(proc->tsk, RLIMIT_NOFILE); unlock_task_sighand(proc->tsk, &irqs); - return __alloc_fd(files, 0, rlim_cur, flags); + preempt_enable_no_resched(); + ret = __alloc_fd(files, 0, rlim_cur, flags); + preempt_disable(); + + return ret; } /* @@ -398,8 +403,11 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) static void task_fd_install( struct binder_proc *proc, unsigned int fd, struct file *file) { - if (proc->files) + if (proc->files) { + preempt_enable_no_resched(); __fd_install(proc->files, fd, file); + preempt_disable(); + } } /* @@ -427,6 +435,7 @@ static inline void binder_lock(const char *tag) { trace_binder_lock(tag); mutex_lock(&binder_main_lock); + preempt_disable(); trace_binder_locked(tag); } @@ -434,8 +443,62 @@ static inline void binder_unlock(const char *tag) { trace_binder_unlock(tag); mutex_unlock(&binder_main_lock); + preempt_enable(); +} + +static inline void *kzalloc_preempt_disabled(size_t size) +{ + void *ptr; + + ptr = kzalloc(size, GFP_NOWAIT); + if (ptr) + return ptr; + + preempt_enable_no_resched(); + ptr = kzalloc(size, GFP_KERNEL); + preempt_disable(); + + return ptr; +} + +static inline long copy_to_user_preempt_disabled(void __user *to, const void *from, long n) +{ + long ret; + + preempt_enable_no_resched(); + ret = copy_to_user(to, from, n); + preempt_disable(); + return ret; +} + +static inline long copy_from_user_preempt_disabled(void *to, const void __user *from, long n) +{ + long ret; + + preempt_enable_no_resched(); + ret = copy_from_user(to, from, n); + preempt_disable(); + return ret; } +#define get_user_preempt_disabled(x, ptr) \ +({ \ + int __ret; \ + preempt_enable_no_resched(); \ + __ret = get_user(x, ptr); \ + preempt_disable(); \ + __ret; \ +}) + +#define put_user_preempt_disabled(x, ptr) \ +({ \ + int __ret; \ + preempt_enable_no_resched(); \ + __ret = put_user(x, ptr); \ + preempt_disable(); \ + __ret; \ +}) + static void binder_set_nice(long nice) { long min_nice; @@ -568,6 +631,8 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate, else mm = get_task_mm(proc->tsk); + preempt_enable_no_resched(); + if (mm) { down_write(&mm->mmap_sem); vma = proc->vma; @@ -622,6 +687,9 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate, up_write(&mm->mmap_sem); mmput(mm); } + + preempt_disable(); + return 0; free_range: @@ -644,6 +712,9 @@ err_no_vma: up_write(&mm->mmap_sem); mmput(mm); } + + preempt_disable(); + return -ENOMEM; } @@ -903,7 +974,7 @@ static struct binder_node *binder_new_node(struct binder_proc *proc, return NULL; } - node = kzalloc(sizeof(*node), GFP_KERNEL); + node = kzalloc_preempt_disabled(sizeof(*node)); if (node == NULL) return NULL; binder_stats_created(BINDER_STAT_NODE); @@ -1040,7 +1111,7 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, else return ref; } - new_ref = kzalloc(sizeof(*ref), GFP_KERNEL); + new_ref = kzalloc_preempt_disabled(sizeof(*ref)); if (new_ref == NULL) return NULL; binder_stats_created(BINDER_STAT_REF); @@ -1438,14 +1509,14 @@ static void binder_transaction(struct binder_proc *proc, e->to_proc = target_proc->pid; /* TODO: reuse incoming transaction for reply */ - t = kzalloc(sizeof(*t), GFP_KERNEL); + t = kzalloc_preempt_disabled(sizeof(*t)); if (t == NULL) { return_error = BR_FAILED_REPLY; goto err_alloc_t_failed; } binder_stats_created(BINDER_STAT_TRANSACTION); - tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL); + tcomplete = kzalloc_preempt_disabled(sizeof(*tcomplete)); if (tcomplete == NULL) { return_error = BR_FAILED_REPLY; goto err_alloc_tcomplete_failed; @@ -1502,14 +1573,14 @@ static void binder_transaction(struct binder_proc *proc, offp = (binder_size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *))); - if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t) + if (copy_from_user_preempt_disabled(t->buffer->data, (const void __user *)(uintptr_t) tr->data.ptr.buffer, tr->data_size)) { binder_user_error("%d:%d got transaction with invalid data ptr\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; goto err_copy_data_failed; } - if (copy_from_user(offp, (const void __user *)(uintptr_t) + if (copy_from_user_preempt_disabled(offp, (const void __user *)(uintptr_t) tr->data.ptr.offsets, tr->offsets_size)) { binder_user_error("%d:%d got transaction with invalid offsets ptr\n", proc->pid, thread->pid); @@ -1778,7 +1849,7 @@ static int binder_thread_write(struct binder_proc *proc, void __user *end = buffer + size; while (ptr < end && thread->return_error == BR_OK) { - if (get_user(cmd, (uint32_t __user *)ptr)) + if (get_user_preempt_disabled(cmd, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); trace_binder_command(cmd); @@ -1796,7 +1867,7 @@ static int binder_thread_write(struct binder_proc *proc, struct binder_ref *ref; const char *debug_string; - if (get_user(target, (uint32_t __user *)ptr)) + if (get_user_preempt_disabled(target, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); if (target == 0 && binder_context_mgr_node && @@ -1846,10 +1917,10 @@ static int binder_thread_write(struct binder_proc *proc, binder_uintptr_t cookie; struct binder_node *node; - if (get_user(node_ptr, (binder_uintptr_t __user *)ptr)) + if (get_user_preempt_disabled(node_ptr, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); - if (get_user(cookie, (binder_uintptr_t __user *)ptr)) + if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); node = binder_get_node(proc, node_ptr); @@ -1907,7 +1978,7 @@ static int binder_thread_write(struct binder_proc *proc, binder_uintptr_t data_ptr; struct binder_buffer *buffer; - if (get_user(data_ptr, (binder_uintptr_t __user *)ptr)) + if (get_user_preempt_disabled(data_ptr, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); @@ -1949,7 +2020,7 @@ static int binder_thread_write(struct binder_proc *proc, case BC_REPLY: { struct binder_transaction_data tr; - if (copy_from_user(&tr, ptr, sizeof(tr))) + if (copy_from_user_preempt_disabled(&tr, ptr, sizeof(tr))) return -EFAULT; ptr += sizeof(tr); binder_transaction(proc, thread, &tr, cmd == BC_REPLY); @@ -1999,10 +2070,10 @@ static int binder_thread_write(struct binder_proc *proc, struct binder_ref *ref; struct binder_ref_death *death; - if (get_user(target, (uint32_t __user *)ptr)) + if (get_user_preempt_disabled(target, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); - if (get_user(cookie, (binder_uintptr_t __user *)ptr)) + if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); ref = binder_get_ref(proc, target); @@ -2031,7 +2102,7 @@ static int binder_thread_write(struct binder_proc *proc, proc->pid, thread->pid); break; } - death = kzalloc(sizeof(*death), GFP_KERNEL); + death = kzalloc_preempt_disabled(sizeof(*death)); if (death == NULL) { thread->return_error = BR_ERROR; binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, @@ -2085,8 +2156,7 @@ static int binder_thread_write(struct binder_proc *proc, struct binder_work *w; binder_uintptr_t cookie; struct binder_ref_death *death = NULL; - - if (get_user(cookie, (binder_uintptr_t __user *)ptr)) + if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(cookie); @@ -2118,7 +2188,8 @@ static int binder_thread_write(struct binder_proc *proc, wake_up_interruptible(&proc->wait); } } - } break; + } + break; default: pr_err("%d:%d unknown command %d\n", @@ -2167,7 +2238,7 @@ static int binder_thread_read(struct binder_proc *proc, int wait_for_proc_work; if (*consumed == 0) { - if (put_user(BR_NOOP, (uint32_t __user *)ptr)) + if (put_user_preempt_disabled(BR_NOOP, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); } @@ -2178,7 +2249,7 @@ retry: if (thread->return_error != BR_OK && ptr < end) { if (thread->return_error2 != BR_OK) { - if (put_user(thread->return_error2, (uint32_t __user *)ptr)) + if (put_user_preempt_disabled(thread->return_error2, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); binder_stat_br(proc, thread, thread->return_error2); @@ -2186,7 +2257,7 @@ retry: goto done; thread->return_error2 = BR_OK; } - if (put_user(thread->return_error, (uint32_t __user *)ptr)) + if (put_user_preempt_disabled(thread->return_error, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); binder_stat_br(proc, thread, thread->return_error); @@ -2264,7 +2335,7 @@ retry: } break; case BINDER_WORK_TRANSACTION_COMPLETE: { cmd = BR_TRANSACTION_COMPLETE; - if (put_user(cmd, (uint32_t __user *)ptr)) + if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr)) return -EFAULT; ptr += sizeof(uint32_t); @@ -2306,14 +2377,14 @@ retry: node->has_weak_ref = 0; } if (cmd != BR_NOOP) { - if (put_user(cmd, (uint32_t __user *)ptr)) + if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr)) return -EFAULT; ptr += sizeof(uint32_t); - if (put_user(node->ptr, + if (put_user_preempt_disabled(node->ptr, (binder_uintptr_t __user *) (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); - if (put_user(node->cookie, + if (put_user_preempt_disabled(node->cookie, (binder_uintptr_t __user *) (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); @@ -2357,11 +2428,10 @@ retry: cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE; else cmd = BR_DEAD_BINDER; - if (put_user(cmd, (uint32_t __user *)ptr)) + if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr)) return -EFAULT; ptr += sizeof(uint32_t); - if (put_user(death->cookie, - (binder_uintptr_t __user *)ptr)) + if (put_user_preempt_disabled(death->cookie, (binder_uintptr_t __user *) ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); binder_stat_br(proc, thread, cmd); @@ -2428,10 +2498,10 @@ retry: ALIGN(t->buffer->data_size, sizeof(void *)); - if (put_user(cmd, (uint32_t __user *)ptr)) + if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr)) return -EFAULT; ptr += sizeof(uint32_t); - if (copy_to_user(ptr, &tr, sizeof(tr))) + if (copy_to_user_preempt_disabled(ptr, &tr, sizeof(tr))) return -EFAULT; ptr += sizeof(tr); @@ -2473,7 +2543,7 @@ done: binder_debug(BINDER_DEBUG_THREADS, "%d:%d BR_SPAWN_LOOPER\n", proc->pid, thread->pid); - if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer)) + if (put_user_preempt_disabled(BR_SPAWN_LOOPER, (uint32_t __user *) buffer)) return -EFAULT; binder_stat_br(proc, thread, BR_SPAWN_LOOPER); } @@ -2548,7 +2618,7 @@ static struct binder_thread *binder_get_thread(struct binder_proc *proc) break; } if (*p == NULL) { - thread = kzalloc(sizeof(*thread), GFP_KERNEL); + thread = kzalloc_preempt_disabled(sizeof(*thread)); if (thread == NULL) return NULL; binder_stats_created(BINDER_STAT_THREAD); @@ -2652,7 +2722,7 @@ static int binder_ioctl_write_read(struct file *filp, ret = -EINVAL; goto out; } - if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { + if (copy_from_user_preempt_disabled(&bwr, ubuf, sizeof(bwr))) { ret = -EFAULT; goto out; } @@ -2670,7 +2740,7 @@ static int binder_ioctl_write_read(struct file *filp, trace_binder_write_done(ret); if (ret < 0) { bwr.read_consumed = 0; - if (copy_to_user(ubuf, &bwr, sizeof(bwr))) + if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr))) ret = -EFAULT; goto out; } @@ -2684,7 +2754,7 @@ static int binder_ioctl_write_read(struct file *filp, if (!list_empty(&proc->todo)) wake_up_interruptible(&proc->wait); if (ret < 0) { - if (copy_to_user(ubuf, &bwr, sizeof(bwr))) + if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr))) ret = -EFAULT; goto out; } @@ -2694,7 +2764,7 @@ static int binder_ioctl_write_read(struct file *filp, proc->pid, thread->pid, (u64)bwr.write_consumed, (u64)bwr.write_size, (u64)bwr.read_consumed, (u64)bwr.read_size); - if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { + if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr))) { ret = -EFAULT; goto out; } @@ -2772,7 +2842,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) goto err; break; case BINDER_SET_MAX_THREADS: - if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) { + if (copy_from_user_preempt_disabled(&proc->max_threads, ubuf, sizeof(proc->max_threads))) { ret = -EINVAL; goto err; } @@ -2795,9 +2865,8 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ret = -EINVAL; goto err; } - if (put_user(BINDER_CURRENT_PROTOCOL_VERSION, - &ver->protocol_version)) { - ret = -EINVAL; + if (put_user_preempt_disabled(BINDER_CURRENT_PROTOCOL_VERSION, &ver->protocol_version)) { + ret = -EINVAL; goto err; } break; @@ -2858,6 +2927,7 @@ static const struct vm_operations_struct binder_vm_ops = { static int binder_mmap(struct file *filp, struct vm_area_struct *vma) { int ret; + struct vm_struct *area; struct binder_proc *proc = filp->private_data; const char *failure_string; @@ -2918,7 +2988,11 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) vma->vm_ops = &binder_vm_ops; vma->vm_private_data = proc; - if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) { + /* binder_update_page_range assumes preemption is disabled */ + preempt_disable(); + ret = binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma); + preempt_enable_no_resched(); + if (ret) { ret = -ENOMEM; failure_string = "alloc small buf"; goto err_alloc_small_buf_failed; @@ -3188,8 +3262,12 @@ static void binder_deferred_func(struct work_struct *work) int defer; do { - binder_lock(__func__); + trace_binder_lock(__func__); + mutex_lock(&binder_main_lock); + trace_binder_locked(__func__); + mutex_lock(&binder_deferred_lock); + preempt_disable(); if (!hlist_empty(&binder_deferred_list)) { proc = hlist_entry(binder_deferred_list.first, struct binder_proc, deferred_work_node); @@ -3215,7 +3293,9 @@ static void binder_deferred_func(struct work_struct *work) if (defer & BINDER_DEFERRED_RELEASE) binder_deferred_release(proc); /* frees proc */ - binder_unlock(__func__); + trace_binder_unlock(__func__); + mutex_unlock(&binder_main_lock); + preempt_enable_no_resched(); if (files) put_files_struct(files); } while (proc); diff --git a/drivers/base/power/opp/Makefile b/drivers/base/power/opp/Makefile index 33c1e18c41a4..19837ef04d8e 100644 --- a/drivers/base/power/opp/Makefile +++ b/drivers/base/power/opp/Makefile @@ -1,2 +1,3 @@ ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG obj-y += core.o cpu.o +obj-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index b8e76f75073b..433b60092972 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c @@ -13,50 +13,52 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/clk.h> #include <linux/errno.h> #include <linux/err.h> #include <linux/slab.h> #include <linux/device.h> #include <linux/of.h> #include <linux/export.h> +#include <linux/regulator/consumer.h> #include "opp.h" /* - * The root of the list of all devices. All device_opp structures branch off - * from here, with each device_opp containing the list of opp it supports in + * The root of the list of all opp-tables. All opp_table structures branch off + * from here, with each opp_table containing the list of opps it supports in * various states of availability. */ -static LIST_HEAD(dev_opp_list); +static LIST_HEAD(opp_tables); /* Lock to allow exclusive modification to the device and opp lists */ -DEFINE_MUTEX(dev_opp_list_lock); +DEFINE_MUTEX(opp_table_lock); #define opp_rcu_lockdep_assert() \ do { \ RCU_LOCKDEP_WARN(!rcu_read_lock_held() && \ - !lockdep_is_held(&dev_opp_list_lock), \ - "Missing rcu_read_lock() or " \ - "dev_opp_list_lock protection"); \ + !lockdep_is_held(&opp_table_lock), \ + "Missing rcu_read_lock() or " \ + "opp_table_lock protection"); \ } while (0) -static struct device_list_opp *_find_list_dev(const struct device *dev, - struct device_opp *dev_opp) +static struct opp_device *_find_opp_dev(const struct device *dev, + struct opp_table *opp_table) { - struct device_list_opp *list_dev; + struct opp_device *opp_dev; - list_for_each_entry(list_dev, &dev_opp->dev_list, node) - if (list_dev->dev == dev) - return list_dev; + list_for_each_entry(opp_dev, &opp_table->dev_list, node) + if (opp_dev->dev == dev) + return opp_dev; return NULL; } -static struct device_opp *_managed_opp(const struct device_node *np) +static struct opp_table *_managed_opp(const struct device_node *np) { - struct device_opp *dev_opp; + struct opp_table *opp_table; - list_for_each_entry_rcu(dev_opp, &dev_opp_list, node) { - if (dev_opp->np == np) { + list_for_each_entry_rcu(opp_table, &opp_tables, node) { + if (opp_table->np == np) { /* * Multiple devices can point to the same OPP table and * so will have same node-pointer, np. @@ -64,7 +66,7 @@ static struct device_opp *_managed_opp(const struct device_node *np) * But the OPPs will be considered as shared only if the * OPP table contains a "opp-shared" property. */ - return dev_opp->shared_opp ? dev_opp : NULL; + return opp_table->shared_opp ? opp_table : NULL; } } @@ -72,24 +74,24 @@ static struct device_opp *_managed_opp(const struct device_node *np) } /** - * _find_device_opp() - find device_opp struct using device pointer - * @dev: device pointer used to lookup device OPPs + * _find_opp_table() - find opp_table struct using device pointer + * @dev: device pointer used to lookup OPP table * - * Search list of device OPPs for one containing matching device. Does a RCU - * reader operation to grab the pointer needed. + * Search OPP table for one containing matching device. Does a RCU reader + * operation to grab the pointer needed. * - * Return: pointer to 'struct device_opp' if found, otherwise -ENODEV or + * Return: pointer to 'struct opp_table' if found, otherwise -ENODEV or * -EINVAL based on type of error. * * Locking: For readers, this function must be called under rcu_read_lock(). - * device_opp is a RCU protected pointer, which means that device_opp is valid + * opp_table is a RCU protected pointer, which means that opp_table is valid * as long as we are under RCU lock. * - * For Writers, this function must be called with dev_opp_list_lock held. + * For Writers, this function must be called with opp_table_lock held. */ -struct device_opp *_find_device_opp(struct device *dev) +struct opp_table *_find_opp_table(struct device *dev) { - struct device_opp *dev_opp; + struct opp_table *opp_table; opp_rcu_lockdep_assert(); @@ -98,9 +100,9 @@ struct device_opp *_find_device_opp(struct device *dev) return ERR_PTR(-EINVAL); } - list_for_each_entry_rcu(dev_opp, &dev_opp_list, node) - if (_find_list_dev(dev, dev_opp)) - return dev_opp; + list_for_each_entry_rcu(opp_table, &opp_tables, node) + if (_find_opp_dev(dev, opp_table)) + return opp_table; return ERR_PTR(-ENODEV); } @@ -213,16 +215,16 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo); */ unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev) { - struct device_opp *dev_opp; + struct opp_table *opp_table; unsigned long clock_latency_ns; rcu_read_lock(); - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) clock_latency_ns = 0; else - clock_latency_ns = dev_opp->clock_latency_ns_max; + clock_latency_ns = opp_table->clock_latency_ns_max; rcu_read_unlock(); return clock_latency_ns; @@ -230,6 +232,82 @@ unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev) EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency); /** + * dev_pm_opp_get_max_volt_latency() - Get max voltage latency in nanoseconds + * @dev: device for which we do this operation + * + * Return: This function returns the max voltage latency in nanoseconds. + * + * Locking: This function takes rcu_read_lock(). + */ +unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) +{ + struct opp_table *opp_table; + struct dev_pm_opp *opp; + struct regulator *reg; + unsigned long latency_ns = 0; + unsigned long min_uV = ~0, max_uV = 0; + int ret; + + rcu_read_lock(); + + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + rcu_read_unlock(); + return 0; + } + + reg = opp_table->regulator; + if (IS_ERR(reg)) { + /* Regulator may not be required for device */ + if (reg) + dev_err(dev, "%s: Invalid regulator (%ld)\n", __func__, + PTR_ERR(reg)); + rcu_read_unlock(); + return 0; + } + + list_for_each_entry_rcu(opp, &opp_table->opp_list, node) { + if (!opp->available) + continue; + + if (opp->u_volt_min < min_uV) + min_uV = opp->u_volt_min; + if (opp->u_volt_max > max_uV) + max_uV = opp->u_volt_max; + } + + rcu_read_unlock(); + + /* + * The caller needs to ensure that opp_table (and hence the regulator) + * isn't freed, while we are executing this routine. + */ + ret = regulator_set_voltage_time(reg, min_uV, max_uV); + if (ret > 0) + latency_ns = ret * 1000; + + return latency_ns; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_volt_latency); + +/** + * dev_pm_opp_get_max_transition_latency() - Get max transition latency in + * nanoseconds + * @dev: device for which we do this operation + * + * Return: This function returns the max transition latency, in nanoseconds, to + * switch from one OPP to other. + * + * Locking: This function takes rcu_read_lock(). + */ +unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev) +{ + return dev_pm_opp_get_max_volt_latency(dev) + + dev_pm_opp_get_max_clock_latency(dev); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_transition_latency); + +/** * dev_pm_opp_get_suspend_opp() - Get suspend opp * @dev: device for which we do this operation * @@ -244,21 +322,21 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency); */ struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev) { - struct device_opp *dev_opp; + struct opp_table *opp_table; opp_rcu_lockdep_assert(); - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp) || !dev_opp->suspend_opp || - !dev_opp->suspend_opp->available) + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table) || !opp_table->suspend_opp || + !opp_table->suspend_opp->available) return NULL; - return dev_opp->suspend_opp; + return opp_table->suspend_opp; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp); /** - * dev_pm_opp_get_opp_count() - Get number of opps available in the opp list + * dev_pm_opp_get_opp_count() - Get number of opps available in the opp table * @dev: device for which we do this operation * * Return: This function returns the number of available opps if there are any, @@ -268,21 +346,21 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp); */ int dev_pm_opp_get_opp_count(struct device *dev) { - struct device_opp *dev_opp; + struct opp_table *opp_table; struct dev_pm_opp *temp_opp; int count = 0; rcu_read_lock(); - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) { - count = PTR_ERR(dev_opp); - dev_err(dev, "%s: device OPP not found (%d)\n", + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + count = PTR_ERR(opp_table); + dev_err(dev, "%s: OPP table not found (%d)\n", __func__, count); goto out_unlock; } - list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { + list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) { if (temp_opp->available) count++; } @@ -299,7 +377,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count); * @freq: frequency to search for * @available: true/false - match for available opp * - * Return: Searches for exact match in the opp list and returns pointer to the + * Return: Searches for exact match in the opp table and returns pointer to the * matching opp if found, else returns ERR_PTR in case of error and should * be handled using IS_ERR. Error return values can be: * EINVAL: for bad pointer @@ -323,19 +401,20 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, bool available) { - struct device_opp *dev_opp; + struct opp_table *opp_table; struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); opp_rcu_lockdep_assert(); - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) { - int r = PTR_ERR(dev_opp); - dev_err(dev, "%s: device OPP not found (%d)\n", __func__, r); + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + int r = PTR_ERR(opp_table); + + dev_err(dev, "%s: OPP table not found (%d)\n", __func__, r); return ERR_PTR(r); } - list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { + list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) { if (temp_opp->available == available && temp_opp->rate == freq) { opp = temp_opp; @@ -371,7 +450,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, unsigned long *freq) { - struct device_opp *dev_opp; + struct opp_table *opp_table; struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); opp_rcu_lockdep_assert(); @@ -381,11 +460,11 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, return ERR_PTR(-EINVAL); } - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) - return ERR_CAST(dev_opp); + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) + return ERR_CAST(opp_table); - list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { + list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) { if (temp_opp->available && temp_opp->rate >= *freq) { opp = temp_opp; *freq = opp->rate; @@ -421,7 +500,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil); struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq) { - struct device_opp *dev_opp; + struct opp_table *opp_table; struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); opp_rcu_lockdep_assert(); @@ -431,11 +510,11 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, return ERR_PTR(-EINVAL); } - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) - return ERR_CAST(dev_opp); + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) + return ERR_CAST(opp_table); - list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { + list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) { if (temp_opp->available) { /* go to the next node, before choosing prev */ if (temp_opp->rate > *freq) @@ -451,116 +530,343 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); -/* List-dev Helpers */ -static void _kfree_list_dev_rcu(struct rcu_head *head) +/* + * The caller needs to ensure that opp_table (and hence the clk) isn't freed, + * while clk returned here is used. + */ +static struct clk *_get_opp_clk(struct device *dev) +{ + struct opp_table *opp_table; + struct clk *clk; + + rcu_read_lock(); + + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + dev_err(dev, "%s: device opp doesn't exist\n", __func__); + clk = ERR_CAST(opp_table); + goto unlock; + } + + clk = opp_table->clk; + if (IS_ERR(clk)) + dev_err(dev, "%s: No clock available for the device\n", + __func__); + +unlock: + rcu_read_unlock(); + return clk; +} + +static int _set_opp_voltage(struct device *dev, struct regulator *reg, + unsigned long u_volt, unsigned long u_volt_min, + unsigned long u_volt_max) +{ + int ret; + + /* Regulator not available for device */ + if (IS_ERR(reg)) { + dev_dbg(dev, "%s: regulator not available: %ld\n", __func__, + PTR_ERR(reg)); + return 0; + } + + dev_dbg(dev, "%s: voltages (mV): %lu %lu %lu\n", __func__, u_volt_min, + u_volt, u_volt_max); + + ret = regulator_set_voltage_triplet(reg, u_volt_min, u_volt, + u_volt_max); + if (ret) + dev_err(dev, "%s: failed to set voltage (%lu %lu %lu mV): %d\n", + __func__, u_volt_min, u_volt, u_volt_max, ret); + + return ret; +} + +/** + * dev_pm_opp_set_rate() - Configure new OPP based on frequency + * @dev: device for which we do this operation + * @target_freq: frequency to achieve + * + * This configures the power-supplies and clock source to the levels specified + * by the OPP corresponding to the target_freq. + * + * Locking: This function takes rcu_read_lock(). + */ +int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) +{ + struct opp_table *opp_table; + struct dev_pm_opp *old_opp, *opp; + struct regulator *reg; + struct clk *clk; + unsigned long freq, old_freq; + unsigned long u_volt, u_volt_min, u_volt_max; + unsigned long ou_volt, ou_volt_min, ou_volt_max; + int ret; + + if (unlikely(!target_freq)) { + dev_err(dev, "%s: Invalid target frequency %lu\n", __func__, + target_freq); + return -EINVAL; + } + + clk = _get_opp_clk(dev); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + freq = clk_round_rate(clk, target_freq); + if ((long)freq <= 0) + freq = target_freq; + + old_freq = clk_get_rate(clk); + + /* Return early if nothing to do */ + if (old_freq == freq) { + dev_dbg(dev, "%s: old/new frequencies (%lu Hz) are same, nothing to do\n", + __func__, freq); + return 0; + } + + rcu_read_lock(); + + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + dev_err(dev, "%s: device opp doesn't exist\n", __func__); + rcu_read_unlock(); + return PTR_ERR(opp_table); + } + + old_opp = dev_pm_opp_find_freq_ceil(dev, &old_freq); + if (!IS_ERR(old_opp)) { + ou_volt = old_opp->u_volt; + ou_volt_min = old_opp->u_volt_min; + ou_volt_max = old_opp->u_volt_max; + } else { + dev_err(dev, "%s: failed to find current OPP for freq %lu (%ld)\n", + __func__, old_freq, PTR_ERR(old_opp)); + } + + opp = dev_pm_opp_find_freq_ceil(dev, &freq); + if (IS_ERR(opp)) { + ret = PTR_ERR(opp); + dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n", + __func__, freq, ret); + rcu_read_unlock(); + return ret; + } + + u_volt = opp->u_volt; + u_volt_min = opp->u_volt_min; + u_volt_max = opp->u_volt_max; + + reg = opp_table->regulator; + + rcu_read_unlock(); + + /* Scaling up? Scale voltage before frequency */ + if (freq > old_freq) { + ret = _set_opp_voltage(dev, reg, u_volt, u_volt_min, + u_volt_max); + if (ret) + goto restore_voltage; + } + + /* Change frequency */ + + dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n", + __func__, old_freq, freq); + + ret = clk_set_rate(clk, freq); + if (ret) { + dev_err(dev, "%s: failed to set clock rate: %d\n", __func__, + ret); + goto restore_voltage; + } + + /* Scaling down? Scale voltage after frequency */ + if (freq < old_freq) { + ret = _set_opp_voltage(dev, reg, u_volt, u_volt_min, + u_volt_max); + if (ret) + goto restore_freq; + } + + return 0; + +restore_freq: + if (clk_set_rate(clk, old_freq)) + dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n", + __func__, old_freq); +restore_voltage: + /* This shouldn't harm even if the voltages weren't updated earlier */ + if (!IS_ERR(old_opp)) + _set_opp_voltage(dev, reg, ou_volt, ou_volt_min, ou_volt_max); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate); + +/* OPP-dev Helpers */ +static void _kfree_opp_dev_rcu(struct rcu_head *head) { - struct device_list_opp *list_dev; + struct opp_device *opp_dev; - list_dev = container_of(head, struct device_list_opp, rcu_head); - kfree_rcu(list_dev, rcu_head); + opp_dev = container_of(head, struct opp_device, rcu_head); + kfree_rcu(opp_dev, rcu_head); } -static void _remove_list_dev(struct device_list_opp *list_dev, - struct device_opp *dev_opp) +static void _remove_opp_dev(struct opp_device *opp_dev, + struct opp_table *opp_table) { - list_del(&list_dev->node); - call_srcu(&dev_opp->srcu_head.srcu, &list_dev->rcu_head, - _kfree_list_dev_rcu); + opp_debug_unregister(opp_dev, opp_table); + list_del(&opp_dev->node); + call_srcu(&opp_table->srcu_head.srcu, &opp_dev->rcu_head, + _kfree_opp_dev_rcu); } -struct device_list_opp *_add_list_dev(const struct device *dev, - struct device_opp *dev_opp) +struct opp_device *_add_opp_dev(const struct device *dev, + struct opp_table *opp_table) { - struct device_list_opp *list_dev; + struct opp_device *opp_dev; + int ret; - list_dev = kzalloc(sizeof(*list_dev), GFP_KERNEL); - if (!list_dev) + opp_dev = kzalloc(sizeof(*opp_dev), GFP_KERNEL); + if (!opp_dev) return NULL; - /* Initialize list-dev */ - list_dev->dev = dev; - list_add_rcu(&list_dev->node, &dev_opp->dev_list); + /* Initialize opp-dev */ + opp_dev->dev = dev; + list_add_rcu(&opp_dev->node, &opp_table->dev_list); + + /* Create debugfs entries for the opp_table */ + ret = opp_debug_register(opp_dev, opp_table); + if (ret) + dev_err(dev, "%s: Failed to register opp debugfs (%d)\n", + __func__, ret); - return list_dev; + return opp_dev; } /** - * _add_device_opp() - Find device OPP table or allocate a new one + * _add_opp_table() - Find OPP table or allocate a new one * @dev: device for which we do this operation * * It tries to find an existing table first, if it couldn't find one, it * allocates a new OPP table and returns that. * - * Return: valid device_opp pointer if success, else NULL. + * Return: valid opp_table pointer if success, else NULL. */ -static struct device_opp *_add_device_opp(struct device *dev) +static struct opp_table *_add_opp_table(struct device *dev) { - struct device_opp *dev_opp; - struct device_list_opp *list_dev; + struct opp_table *opp_table; + struct opp_device *opp_dev; + struct device_node *np; + int ret; - /* Check for existing list for 'dev' first */ - dev_opp = _find_device_opp(dev); - if (!IS_ERR(dev_opp)) - return dev_opp; + /* Check for existing table for 'dev' first */ + opp_table = _find_opp_table(dev); + if (!IS_ERR(opp_table)) + return opp_table; /* - * Allocate a new device OPP table. In the infrequent case where a new + * Allocate a new OPP table. In the infrequent case where a new * device is needed to be added, we pay this penalty. */ - dev_opp = kzalloc(sizeof(*dev_opp), GFP_KERNEL); - if (!dev_opp) + opp_table = kzalloc(sizeof(*opp_table), GFP_KERNEL); + if (!opp_table) return NULL; - INIT_LIST_HEAD(&dev_opp->dev_list); + INIT_LIST_HEAD(&opp_table->dev_list); - list_dev = _add_list_dev(dev, dev_opp); - if (!list_dev) { - kfree(dev_opp); + opp_dev = _add_opp_dev(dev, opp_table); + if (!opp_dev) { + kfree(opp_table); return NULL; } - srcu_init_notifier_head(&dev_opp->srcu_head); - INIT_LIST_HEAD(&dev_opp->opp_list); + /* + * Only required for backward compatibility with v1 bindings, but isn't + * harmful for other cases. And so we do it unconditionally. + */ + np = of_node_get(dev->of_node); + if (np) { + u32 val; + + if (!of_property_read_u32(np, "clock-latency", &val)) + opp_table->clock_latency_ns_max = val; + of_property_read_u32(np, "voltage-tolerance", + &opp_table->voltage_tolerance_v1); + of_node_put(np); + } + + /* Set regulator to a non-NULL error value */ + opp_table->regulator = ERR_PTR(-ENXIO); + + /* Find clk for the device */ + opp_table->clk = clk_get(dev, NULL); + if (IS_ERR(opp_table->clk)) { + ret = PTR_ERR(opp_table->clk); + if (ret != -EPROBE_DEFER) + dev_dbg(dev, "%s: Couldn't find clock: %d\n", __func__, + ret); + } + + srcu_init_notifier_head(&opp_table->srcu_head); + INIT_LIST_HEAD(&opp_table->opp_list); - /* Secure the device list modification */ - list_add_rcu(&dev_opp->node, &dev_opp_list); - return dev_opp; + /* Secure the device table modification */ + list_add_rcu(&opp_table->node, &opp_tables); + return opp_table; } /** - * _kfree_device_rcu() - Free device_opp RCU handler + * _kfree_device_rcu() - Free opp_table RCU handler * @head: RCU head */ static void _kfree_device_rcu(struct rcu_head *head) { - struct device_opp *device_opp = container_of(head, struct device_opp, rcu_head); + struct opp_table *opp_table = container_of(head, struct opp_table, + rcu_head); - kfree_rcu(device_opp, rcu_head); + kfree_rcu(opp_table, rcu_head); } /** - * _remove_device_opp() - Removes a device OPP table - * @dev_opp: device OPP table to be removed. + * _remove_opp_table() - Removes a OPP table + * @opp_table: OPP table to be removed. * - * Removes/frees device OPP table it it doesn't contain any OPPs. + * Removes/frees OPP table if it doesn't contain any OPPs. */ -static void _remove_device_opp(struct device_opp *dev_opp) +static void _remove_opp_table(struct opp_table *opp_table) { - struct device_list_opp *list_dev; + struct opp_device *opp_dev; + + if (!list_empty(&opp_table->opp_list)) + return; - if (!list_empty(&dev_opp->opp_list)) + if (opp_table->supported_hw) return; - list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp, - node); + if (opp_table->prop_name) + return; + + if (!IS_ERR(opp_table->regulator)) + return; - _remove_list_dev(list_dev, dev_opp); + /* Release clk */ + if (!IS_ERR(opp_table->clk)) + clk_put(opp_table->clk); + + opp_dev = list_first_entry(&opp_table->dev_list, struct opp_device, + node); + + _remove_opp_dev(opp_dev, opp_table); /* dev_list must be empty now */ - WARN_ON(!list_empty(&dev_opp->dev_list)); + WARN_ON(!list_empty(&opp_table->dev_list)); - list_del_rcu(&dev_opp->node); - call_srcu(&dev_opp->srcu_head.srcu, &dev_opp->rcu_head, + list_del_rcu(&opp_table->node); + call_srcu(&opp_table->srcu_head.srcu, &opp_table->rcu_head, _kfree_device_rcu); } @@ -577,17 +883,17 @@ static void _kfree_opp_rcu(struct rcu_head *head) /** * _opp_remove() - Remove an OPP from a table definition - * @dev_opp: points back to the device_opp struct this opp belongs to + * @opp_table: points back to the opp_table struct this opp belongs to * @opp: pointer to the OPP to remove * @notify: OPP_EVENT_REMOVE notification should be sent or not * - * This function removes an opp definition from the opp list. + * This function removes an opp definition from the opp table. * - * Locking: The internal device_opp and opp structures are RCU protected. + * Locking: The internal opp_table and opp structures are RCU protected. * It is assumed that the caller holds required mutex for an RCU updater * strategy. */ -static void _opp_remove(struct device_opp *dev_opp, +static void _opp_remove(struct opp_table *opp_table, struct dev_pm_opp *opp, bool notify) { /* @@ -595,21 +901,23 @@ static void _opp_remove(struct device_opp *dev_opp, * frequency/voltage list. */ if (notify) - srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp); + srcu_notifier_call_chain(&opp_table->srcu_head, + OPP_EVENT_REMOVE, opp); + opp_debug_remove_one(opp); list_del_rcu(&opp->node); - call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); + call_srcu(&opp_table->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); - _remove_device_opp(dev_opp); + _remove_opp_table(opp_table); } /** - * dev_pm_opp_remove() - Remove an OPP from OPP list + * dev_pm_opp_remove() - Remove an OPP from OPP table * @dev: device for which we do this operation * @freq: OPP to remove with matching 'freq' * - * This function removes an opp from the opp list. + * This function removes an opp from the opp table. * - * Locking: The internal device_opp and opp structures are RCU protected. + * Locking: The internal opp_table and opp structures are RCU protected. * Hence this function internally uses RCU updater strategy with mutex locks * to keep the integrity of the internal data structures. Callers should ensure * that this function is *NOT* called under RCU protection or in contexts where @@ -618,17 +926,17 @@ static void _opp_remove(struct device_opp *dev_opp, void dev_pm_opp_remove(struct device *dev, unsigned long freq) { struct dev_pm_opp *opp; - struct device_opp *dev_opp; + struct opp_table *opp_table; bool found = false; - /* Hold our list modification lock here */ - mutex_lock(&dev_opp_list_lock); + /* Hold our table modification lock here */ + mutex_lock(&opp_table_lock); - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) goto unlock; - list_for_each_entry(opp, &dev_opp->opp_list, node) { + list_for_each_entry(opp, &opp_table->opp_list, node) { if (opp->rate == freq) { found = true; break; @@ -641,14 +949,14 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq) goto unlock; } - _opp_remove(dev_opp, opp, true); + _opp_remove(opp_table, opp, true); unlock: - mutex_unlock(&dev_opp_list_lock); + mutex_unlock(&opp_table_lock); } EXPORT_SYMBOL_GPL(dev_pm_opp_remove); static struct dev_pm_opp *_allocate_opp(struct device *dev, - struct device_opp **dev_opp) + struct opp_table **opp_table) { struct dev_pm_opp *opp; @@ -659,8 +967,8 @@ static struct dev_pm_opp *_allocate_opp(struct device *dev, INIT_LIST_HEAD(&opp->node); - *dev_opp = _add_device_opp(dev); - if (!*dev_opp) { + *opp_table = _add_opp_table(dev); + if (!*opp_table) { kfree(opp); return NULL; } @@ -668,21 +976,38 @@ static struct dev_pm_opp *_allocate_opp(struct device *dev, return opp; } +static bool _opp_supported_by_regulators(struct dev_pm_opp *opp, + struct opp_table *opp_table) +{ + struct regulator *reg = opp_table->regulator; + + if (!IS_ERR(reg) && + !regulator_is_supported_voltage(reg, opp->u_volt_min, + opp->u_volt_max)) { + pr_warn("%s: OPP minuV: %lu maxuV: %lu, not supported by regulator\n", + __func__, opp->u_volt_min, opp->u_volt_max); + return false; + } + + return true; +} + static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, - struct device_opp *dev_opp) + struct opp_table *opp_table) { struct dev_pm_opp *opp; - struct list_head *head = &dev_opp->opp_list; + struct list_head *head = &opp_table->opp_list; + int ret; /* * Insert new OPP in order of increasing frequency and discard if * already present. * - * Need to use &dev_opp->opp_list in the condition part of the 'for' + * Need to use &opp_table->opp_list in the condition part of the 'for' * loop, don't replace it with head otherwise it will become an infinite * loop. */ - list_for_each_entry_rcu(opp, &dev_opp->opp_list, node) { + list_for_each_entry_rcu(opp, &opp_table->opp_list, node) { if (new_opp->rate > opp->rate) { head = &opp->node; continue; @@ -700,9 +1025,20 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, 0 : -EEXIST; } - new_opp->dev_opp = dev_opp; + new_opp->opp_table = opp_table; list_add_rcu(&new_opp->node, head); + ret = opp_debug_create_one(new_opp, opp_table); + if (ret) + dev_err(dev, "%s: Failed to register opp to debugfs (%d)\n", + __func__, ret); + + if (!_opp_supported_by_regulators(new_opp, opp_table)) { + new_opp->available = false; + dev_warn(dev, "%s: OPP not supported by regulators (%lu)\n", + __func__, new_opp->rate); + } + return 0; } @@ -713,14 +1049,14 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, * @u_volt: Voltage in uVolts for this OPP * @dynamic: Dynamically added OPPs. * - * This function adds an opp definition to the opp list and returns status. + * This function adds an opp definition to the opp table and returns status. * The opp is made available by default and it can be controlled using * dev_pm_opp_enable/disable functions and may be removed by dev_pm_opp_remove. * * NOTE: "dynamic" parameter impacts OPPs added by the dev_pm_opp_of_add_table * and freed by dev_pm_opp_of_remove_table. * - * Locking: The internal device_opp and opp structures are RCU protected. + * Locking: The internal opp_table and opp structures are RCU protected. * Hence this function internally uses RCU updater strategy with mutex locks * to keep the integrity of the internal data structures. Callers should ensure * that this function is *NOT* called under RCU protection or in contexts where @@ -736,14 +1072,15 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, static int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt, bool dynamic) { - struct device_opp *dev_opp; + struct opp_table *opp_table; struct dev_pm_opp *new_opp; + unsigned long tol; int ret; - /* Hold our list modification lock here */ - mutex_lock(&dev_opp_list_lock); + /* Hold our table modification lock here */ + mutex_lock(&opp_table_lock); - new_opp = _allocate_opp(dev, &dev_opp); + new_opp = _allocate_opp(dev, &opp_table); if (!new_opp) { ret = -ENOMEM; goto unlock; @@ -751,83 +1088,475 @@ static int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt, /* populate the opp table */ new_opp->rate = freq; + tol = u_volt * opp_table->voltage_tolerance_v1 / 100; new_opp->u_volt = u_volt; + new_opp->u_volt_min = u_volt - tol; + new_opp->u_volt_max = u_volt + tol; new_opp->available = true; new_opp->dynamic = dynamic; - ret = _opp_add(dev, new_opp, dev_opp); + ret = _opp_add(dev, new_opp, opp_table); if (ret) goto free_opp; - mutex_unlock(&dev_opp_list_lock); + mutex_unlock(&opp_table_lock); /* * Notify the changes in the availability of the operable * frequency/voltage list. */ - srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ADD, new_opp); + srcu_notifier_call_chain(&opp_table->srcu_head, OPP_EVENT_ADD, new_opp); return 0; free_opp: - _opp_remove(dev_opp, new_opp, false); + _opp_remove(opp_table, new_opp, false); unlock: - mutex_unlock(&dev_opp_list_lock); + mutex_unlock(&opp_table_lock); return ret; } /* TODO: Support multiple regulators */ -static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev) +static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, + struct opp_table *opp_table) { u32 microvolt[3] = {0}; u32 val; int count, ret; + struct property *prop = NULL; + char name[NAME_MAX]; + + /* Search for "opp-microvolt-<name>" */ + if (opp_table->prop_name) { + snprintf(name, sizeof(name), "opp-microvolt-%s", + opp_table->prop_name); + prop = of_find_property(opp->np, name, NULL); + } - /* Missing property isn't a problem, but an invalid entry is */ - if (!of_find_property(opp->np, "opp-microvolt", NULL)) - return 0; + if (!prop) { + /* Search for "opp-microvolt" */ + sprintf(name, "opp-microvolt"); + prop = of_find_property(opp->np, name, NULL); + + /* Missing property isn't a problem, but an invalid entry is */ + if (!prop) + return 0; + } - count = of_property_count_u32_elems(opp->np, "opp-microvolt"); + count = of_property_count_u32_elems(opp->np, name); if (count < 0) { - dev_err(dev, "%s: Invalid opp-microvolt property (%d)\n", - __func__, count); + dev_err(dev, "%s: Invalid %s property (%d)\n", + __func__, name, count); return count; } /* There can be one or three elements here */ if (count != 1 && count != 3) { - dev_err(dev, "%s: Invalid number of elements in opp-microvolt property (%d)\n", - __func__, count); + dev_err(dev, "%s: Invalid number of elements in %s property (%d)\n", + __func__, name, count); return -EINVAL; } - ret = of_property_read_u32_array(opp->np, "opp-microvolt", microvolt, - count); + ret = of_property_read_u32_array(opp->np, name, microvolt, count); if (ret) { - dev_err(dev, "%s: error parsing opp-microvolt: %d\n", __func__, - ret); + dev_err(dev, "%s: error parsing %s: %d\n", __func__, name, ret); return -EINVAL; } opp->u_volt = microvolt[0]; - opp->u_volt_min = microvolt[1]; - opp->u_volt_max = microvolt[2]; - if (!of_property_read_u32(opp->np, "opp-microamp", &val)) + if (count == 1) { + opp->u_volt_min = opp->u_volt; + opp->u_volt_max = opp->u_volt; + } else { + opp->u_volt_min = microvolt[1]; + opp->u_volt_max = microvolt[2]; + } + + /* Search for "opp-microamp-<name>" */ + prop = NULL; + if (opp_table->prop_name) { + snprintf(name, sizeof(name), "opp-microamp-%s", + opp_table->prop_name); + prop = of_find_property(opp->np, name, NULL); + } + + if (!prop) { + /* Search for "opp-microamp" */ + sprintf(name, "opp-microamp"); + prop = of_find_property(opp->np, name, NULL); + } + + if (prop && !of_property_read_u32(opp->np, name, &val)) opp->u_amp = val; return 0; } /** + * dev_pm_opp_set_supported_hw() - Set supported platforms + * @dev: Device for which supported-hw has to be set. + * @versions: Array of hierarchy of versions to match. + * @count: Number of elements in the array. + * + * This is required only for the V2 bindings, and it enables a platform to + * specify the hierarchy of versions it supports. OPP layer will then enable + * OPPs, which are available for those versions, based on its 'opp-supported-hw' + * property. + * + * Locking: The internal opp_table and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, + unsigned int count) +{ + struct opp_table *opp_table; + int ret = 0; + + /* Hold our table modification lock here */ + mutex_lock(&opp_table_lock); + + opp_table = _add_opp_table(dev); + if (!opp_table) { + ret = -ENOMEM; + goto unlock; + } + + /* Make sure there are no concurrent readers while updating opp_table */ + WARN_ON(!list_empty(&opp_table->opp_list)); + + /* Do we already have a version hierarchy associated with opp_table? */ + if (opp_table->supported_hw) { + dev_err(dev, "%s: Already have supported hardware list\n", + __func__); + ret = -EBUSY; + goto err; + } + + opp_table->supported_hw = kmemdup(versions, count * sizeof(*versions), + GFP_KERNEL); + if (!opp_table->supported_hw) { + ret = -ENOMEM; + goto err; + } + + opp_table->supported_hw_count = count; + mutex_unlock(&opp_table_lock); + return 0; + +err: + _remove_opp_table(opp_table); +unlock: + mutex_unlock(&opp_table_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_set_supported_hw); + +/** + * dev_pm_opp_put_supported_hw() - Releases resources blocked for supported hw + * @dev: Device for which supported-hw has to be put. + * + * This is required only for the V2 bindings, and is called for a matching + * dev_pm_opp_set_supported_hw(). Until this is called, the opp_table structure + * will not be freed. + * + * Locking: The internal opp_table and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +void dev_pm_opp_put_supported_hw(struct device *dev) +{ + struct opp_table *opp_table; + + /* Hold our table modification lock here */ + mutex_lock(&opp_table_lock); + + /* Check for existing table for 'dev' first */ + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + dev_err(dev, "Failed to find opp_table: %ld\n", + PTR_ERR(opp_table)); + goto unlock; + } + + /* Make sure there are no concurrent readers while updating opp_table */ + WARN_ON(!list_empty(&opp_table->opp_list)); + + if (!opp_table->supported_hw) { + dev_err(dev, "%s: Doesn't have supported hardware list\n", + __func__); + goto unlock; + } + + kfree(opp_table->supported_hw); + opp_table->supported_hw = NULL; + opp_table->supported_hw_count = 0; + + /* Try freeing opp_table if this was the last blocking resource */ + _remove_opp_table(opp_table); + +unlock: + mutex_unlock(&opp_table_lock); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw); + +/** + * dev_pm_opp_set_prop_name() - Set prop-extn name + * @dev: Device for which the prop-name has to be set. + * @name: name to postfix to properties. + * + * This is required only for the V2 bindings, and it enables a platform to + * specify the extn to be used for certain property names. The properties to + * which the extension will apply are opp-microvolt and opp-microamp. OPP core + * should postfix the property name with -<name> while looking for them. + * + * Locking: The internal opp_table and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +int dev_pm_opp_set_prop_name(struct device *dev, const char *name) +{ + struct opp_table *opp_table; + int ret = 0; + + /* Hold our table modification lock here */ + mutex_lock(&opp_table_lock); + + opp_table = _add_opp_table(dev); + if (!opp_table) { + ret = -ENOMEM; + goto unlock; + } + + /* Make sure there are no concurrent readers while updating opp_table */ + WARN_ON(!list_empty(&opp_table->opp_list)); + + /* Do we already have a prop-name associated with opp_table? */ + if (opp_table->prop_name) { + dev_err(dev, "%s: Already have prop-name %s\n", __func__, + opp_table->prop_name); + ret = -EBUSY; + goto err; + } + + opp_table->prop_name = kstrdup(name, GFP_KERNEL); + if (!opp_table->prop_name) { + ret = -ENOMEM; + goto err; + } + + mutex_unlock(&opp_table_lock); + return 0; + +err: + _remove_opp_table(opp_table); +unlock: + mutex_unlock(&opp_table_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_set_prop_name); + +/** + * dev_pm_opp_put_prop_name() - Releases resources blocked for prop-name + * @dev: Device for which the prop-name has to be put. + * + * This is required only for the V2 bindings, and is called for a matching + * dev_pm_opp_set_prop_name(). Until this is called, the opp_table structure + * will not be freed. + * + * Locking: The internal opp_table and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +void dev_pm_opp_put_prop_name(struct device *dev) +{ + struct opp_table *opp_table; + + /* Hold our table modification lock here */ + mutex_lock(&opp_table_lock); + + /* Check for existing table for 'dev' first */ + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + dev_err(dev, "Failed to find opp_table: %ld\n", + PTR_ERR(opp_table)); + goto unlock; + } + + /* Make sure there are no concurrent readers while updating opp_table */ + WARN_ON(!list_empty(&opp_table->opp_list)); + + if (!opp_table->prop_name) { + dev_err(dev, "%s: Doesn't have a prop-name\n", __func__); + goto unlock; + } + + kfree(opp_table->prop_name); + opp_table->prop_name = NULL; + + /* Try freeing opp_table if this was the last blocking resource */ + _remove_opp_table(opp_table); + +unlock: + mutex_unlock(&opp_table_lock); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name); + +/** + * dev_pm_opp_set_regulator() - Set regulator name for the device + * @dev: Device for which regulator name is being set. + * @name: Name of the regulator. + * + * In order to support OPP switching, OPP layer needs to know the name of the + * device's regulator, as the core would be required to switch voltages as well. + * + * This must be called before any OPPs are initialized for the device. + * + * Locking: The internal opp_table and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +int dev_pm_opp_set_regulator(struct device *dev, const char *name) +{ + struct opp_table *opp_table; + struct regulator *reg; + int ret; + + mutex_lock(&opp_table_lock); + + opp_table = _add_opp_table(dev); + if (!opp_table) { + ret = -ENOMEM; + goto unlock; + } + + /* This should be called before OPPs are initialized */ + if (WARN_ON(!list_empty(&opp_table->opp_list))) { + ret = -EBUSY; + goto err; + } + + /* Already have a regulator set */ + if (WARN_ON(!IS_ERR(opp_table->regulator))) { + ret = -EBUSY; + goto err; + } + /* Allocate the regulator */ + reg = regulator_get_optional(dev, name); + if (IS_ERR(reg)) { + ret = PTR_ERR(reg); + if (ret != -EPROBE_DEFER) + dev_err(dev, "%s: no regulator (%s) found: %d\n", + __func__, name, ret); + goto err; + } + + opp_table->regulator = reg; + + mutex_unlock(&opp_table_lock); + return 0; + +err: + _remove_opp_table(opp_table); +unlock: + mutex_unlock(&opp_table_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_set_regulator); + +/** + * dev_pm_opp_put_regulator() - Releases resources blocked for regulator + * @dev: Device for which regulator was set. + * + * Locking: The internal opp_table and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +void dev_pm_opp_put_regulator(struct device *dev) +{ + struct opp_table *opp_table; + + mutex_lock(&opp_table_lock); + + /* Check for existing table for 'dev' first */ + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + dev_err(dev, "Failed to find opp_table: %ld\n", + PTR_ERR(opp_table)); + goto unlock; + } + + if (IS_ERR(opp_table->regulator)) { + dev_err(dev, "%s: Doesn't have regulator set\n", __func__); + goto unlock; + } + + /* Make sure there are no concurrent readers while updating opp_table */ + WARN_ON(!list_empty(&opp_table->opp_list)); + + regulator_put(opp_table->regulator); + opp_table->regulator = ERR_PTR(-ENXIO); + + /* Try freeing opp_table if this was the last blocking resource */ + _remove_opp_table(opp_table); + +unlock: + mutex_unlock(&opp_table_lock); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulator); + +static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table, + struct device_node *np) +{ + unsigned int count = opp_table->supported_hw_count; + u32 version; + int ret; + + if (!opp_table->supported_hw) + return true; + + while (count--) { + ret = of_property_read_u32_index(np, "opp-supported-hw", count, + &version); + if (ret) { + dev_warn(dev, "%s: failed to read opp-supported-hw property at index %d: %d\n", + __func__, count, ret); + return false; + } + + /* Both of these are bitwise masks of the versions */ + if (!(version & opp_table->supported_hw[count])) + return false; + } + + return true; +} + +/** * _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings) * @dev: device for which we do this operation * @np: device node * - * This function adds an opp definition to the opp list and returns status. The + * This function adds an opp definition to the opp table and returns status. The * opp can be controlled using dev_pm_opp_enable/disable functions and may be * removed by dev_pm_opp_remove. * - * Locking: The internal device_opp and opp structures are RCU protected. + * Locking: The internal opp_table and opp structures are RCU protected. * Hence this function internally uses RCU updater strategy with mutex locks * to keep the integrity of the internal data structures. Callers should ensure * that this function is *NOT* called under RCU protection or in contexts where @@ -843,16 +1572,16 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev) */ static int _opp_add_static_v2(struct device *dev, struct device_node *np) { - struct device_opp *dev_opp; + struct opp_table *opp_table; struct dev_pm_opp *new_opp; u64 rate; u32 val; int ret; - /* Hold our list modification lock here */ - mutex_lock(&dev_opp_list_lock); + /* Hold our table modification lock here */ + mutex_lock(&opp_table_lock); - new_opp = _allocate_opp(dev, &dev_opp); + new_opp = _allocate_opp(dev, &opp_table); if (!new_opp) { ret = -ENOMEM; goto unlock; @@ -864,6 +1593,12 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np) goto free_opp; } + /* Check if the OPP supports hardware's hierarchy of versions or not */ + if (!_opp_is_supported(dev, opp_table, np)) { + dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate); + goto free_opp; + } + /* * Rate is defined as an unsigned long in clk API, and so casting * explicitly to its type. Must be fixed once rate is 64 bit @@ -879,28 +1614,30 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np) if (!of_property_read_u32(np, "clock-latency-ns", &val)) new_opp->clock_latency_ns = val; - ret = opp_parse_supplies(new_opp, dev); + ret = opp_parse_supplies(new_opp, dev, opp_table); if (ret) goto free_opp; - ret = _opp_add(dev, new_opp, dev_opp); + ret = _opp_add(dev, new_opp, opp_table); if (ret) goto free_opp; /* OPP to select on device suspend */ if (of_property_read_bool(np, "opp-suspend")) { - if (dev_opp->suspend_opp) + if (opp_table->suspend_opp) { dev_warn(dev, "%s: Multiple suspend OPPs found (%lu %lu)\n", - __func__, dev_opp->suspend_opp->rate, + __func__, opp_table->suspend_opp->rate, new_opp->rate); - else - dev_opp->suspend_opp = new_opp; + } else { + new_opp->suspend = true; + opp_table->suspend_opp = new_opp; + } } - if (new_opp->clock_latency_ns > dev_opp->clock_latency_ns_max) - dev_opp->clock_latency_ns_max = new_opp->clock_latency_ns; + if (new_opp->clock_latency_ns > opp_table->clock_latency_ns_max) + opp_table->clock_latency_ns_max = new_opp->clock_latency_ns; - mutex_unlock(&dev_opp_list_lock); + mutex_unlock(&opp_table_lock); pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu\n", __func__, new_opp->turbo, new_opp->rate, new_opp->u_volt, @@ -911,13 +1648,13 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np) * Notify the changes in the availability of the operable * frequency/voltage list. */ - srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ADD, new_opp); + srcu_notifier_call_chain(&opp_table->srcu_head, OPP_EVENT_ADD, new_opp); return 0; free_opp: - _opp_remove(dev_opp, new_opp, false); + _opp_remove(opp_table, new_opp, false); unlock: - mutex_unlock(&dev_opp_list_lock); + mutex_unlock(&opp_table_lock); return ret; } @@ -927,11 +1664,11 @@ unlock: * @freq: Frequency in Hz for this OPP * @u_volt: Voltage in uVolts for this OPP * - * This function adds an opp definition to the opp list and returns status. + * This function adds an opp definition to the opp table and returns status. * The opp is made available by default and it can be controlled using * dev_pm_opp_enable/disable functions. * - * Locking: The internal device_opp and opp structures are RCU protected. + * Locking: The internal opp_table and opp structures are RCU protected. * Hence this function internally uses RCU updater strategy with mutex locks * to keep the integrity of the internal data structures. Callers should ensure * that this function is *NOT* called under RCU protection or in contexts where @@ -963,7 +1700,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_add); * copy operation, returns 0 if no modification was done OR modification was * successful. * - * Locking: The internal device_opp and opp structures are RCU protected. + * Locking: The internal opp_table and opp structures are RCU protected. * Hence this function internally uses RCU updater strategy with mutex locks to * keep the integrity of the internal data structures. Callers should ensure * that this function is *NOT* called under RCU protection or in contexts where @@ -972,7 +1709,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_add); static int _opp_set_availability(struct device *dev, unsigned long freq, bool availability_req) { - struct device_opp *dev_opp; + struct opp_table *opp_table; struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV); int r = 0; @@ -981,18 +1718,18 @@ static int _opp_set_availability(struct device *dev, unsigned long freq, if (!new_opp) return -ENOMEM; - mutex_lock(&dev_opp_list_lock); + mutex_lock(&opp_table_lock); - /* Find the device_opp */ - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) { - r = PTR_ERR(dev_opp); + /* Find the opp_table */ + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + r = PTR_ERR(opp_table); dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r); goto unlock; } /* Do we have the frequency? */ - list_for_each_entry(tmp_opp, &dev_opp->opp_list, node) { + list_for_each_entry(tmp_opp, &opp_table->opp_list, node) { if (tmp_opp->rate == freq) { opp = tmp_opp; break; @@ -1013,21 +1750,21 @@ static int _opp_set_availability(struct device *dev, unsigned long freq, new_opp->available = availability_req; list_replace_rcu(&opp->node, &new_opp->node); - mutex_unlock(&dev_opp_list_lock); - call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); + mutex_unlock(&opp_table_lock); + call_srcu(&opp_table->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); /* Notify the change of the OPP availability */ if (availability_req) - srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ENABLE, - new_opp); + srcu_notifier_call_chain(&opp_table->srcu_head, + OPP_EVENT_ENABLE, new_opp); else - srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_DISABLE, - new_opp); + srcu_notifier_call_chain(&opp_table->srcu_head, + OPP_EVENT_DISABLE, new_opp); return 0; unlock: - mutex_unlock(&dev_opp_list_lock); + mutex_unlock(&opp_table_lock); kfree(new_opp); return r; } @@ -1041,7 +1778,7 @@ unlock: * corresponding error value. It is meant to be used for users an OPP available * after being temporarily made unavailable with dev_pm_opp_disable. * - * Locking: The internal device_opp and opp structures are RCU protected. + * Locking: The internal opp_table and opp structures are RCU protected. * Hence this function indirectly uses RCU and mutex locks to keep the * integrity of the internal data structures. Callers should ensure that * this function is *NOT* called under RCU protection or in contexts where @@ -1067,7 +1804,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_enable); * control by users to make this OPP not available until the circumstances are * right to make it available again (with a call to dev_pm_opp_enable). * - * Locking: The internal device_opp and opp structures are RCU protected. + * Locking: The internal opp_table and opp structures are RCU protected. * Hence this function indirectly uses RCU and mutex locks to keep the * integrity of the internal data structures. Callers should ensure that * this function is *NOT* called under RCU protection or in contexts where @@ -1085,26 +1822,26 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_disable); /** * dev_pm_opp_get_notifier() - find notifier_head of the device with opp - * @dev: device pointer used to lookup device OPPs. + * @dev: device pointer used to lookup OPP table. * * Return: pointer to notifier head if found, otherwise -ENODEV or * -EINVAL based on type of error casted as pointer. value must be checked * with IS_ERR to determine valid pointer or error result. * - * Locking: This function must be called under rcu_read_lock(). dev_opp is a RCU - * protected pointer. The reason for the same is that the opp pointer which is - * returned will remain valid for use with opp_get_{voltage, freq} only while + * Locking: This function must be called under rcu_read_lock(). opp_table is a + * RCU protected pointer. The reason for the same is that the opp pointer which + * is returned will remain valid for use with opp_get_{voltage, freq} only while * under the locked area. The pointer returned must be used prior to unlocking * with rcu_read_unlock() to maintain the integrity of the pointer. */ struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev) { - struct device_opp *dev_opp = _find_device_opp(dev); + struct opp_table *opp_table = _find_opp_table(dev); - if (IS_ERR(dev_opp)) - return ERR_CAST(dev_opp); /* matching type */ + if (IS_ERR(opp_table)) + return ERR_CAST(opp_table); /* matching type */ - return &dev_opp->srcu_head; + return &opp_table->srcu_head; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); @@ -1112,11 +1849,11 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); /** * dev_pm_opp_of_remove_table() - Free OPP table entries created from static DT * entries - * @dev: device pointer used to lookup device OPPs. + * @dev: device pointer used to lookup OPP table. * * Free OPPs created using static entries present in DT. * - * Locking: The internal device_opp and opp structures are RCU protected. + * Locking: The internal opp_table and opp structures are RCU protected. * Hence this function indirectly uses RCU updater strategy with mutex locks * to keep the integrity of the internal data structures. Callers should ensure * that this function is *NOT* called under RCU protection or in contexts where @@ -1124,38 +1861,38 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); */ void dev_pm_opp_of_remove_table(struct device *dev) { - struct device_opp *dev_opp; + struct opp_table *opp_table; struct dev_pm_opp *opp, *tmp; - /* Hold our list modification lock here */ - mutex_lock(&dev_opp_list_lock); + /* Hold our table modification lock here */ + mutex_lock(&opp_table_lock); - /* Check for existing list for 'dev' */ - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) { - int error = PTR_ERR(dev_opp); + /* Check for existing table for 'dev' */ + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + int error = PTR_ERR(opp_table); if (error != -ENODEV) - WARN(1, "%s: dev_opp: %d\n", + WARN(1, "%s: opp_table: %d\n", IS_ERR_OR_NULL(dev) ? "Invalid device" : dev_name(dev), error); goto unlock; } - /* Find if dev_opp manages a single device */ - if (list_is_singular(&dev_opp->dev_list)) { + /* Find if opp_table manages a single device */ + if (list_is_singular(&opp_table->dev_list)) { /* Free static OPPs */ - list_for_each_entry_safe(opp, tmp, &dev_opp->opp_list, node) { + list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) { if (!opp->dynamic) - _opp_remove(dev_opp, opp, true); + _opp_remove(opp_table, opp, true); } } else { - _remove_list_dev(_find_list_dev(dev, dev_opp), dev_opp); + _remove_opp_dev(_find_opp_dev(dev, opp_table), opp_table); } unlock: - mutex_unlock(&dev_opp_list_lock); + mutex_unlock(&opp_table_lock); } EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table); @@ -1176,22 +1913,22 @@ struct device_node *_of_get_opp_desc_node(struct device *dev) static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np) { struct device_node *np; - struct device_opp *dev_opp; + struct opp_table *opp_table; int ret = 0, count = 0; - mutex_lock(&dev_opp_list_lock); + mutex_lock(&opp_table_lock); - dev_opp = _managed_opp(opp_np); - if (dev_opp) { + opp_table = _managed_opp(opp_np); + if (opp_table) { /* OPPs are already managed */ - if (!_add_list_dev(dev, dev_opp)) + if (!_add_opp_dev(dev, opp_table)) ret = -ENOMEM; - mutex_unlock(&dev_opp_list_lock); + mutex_unlock(&opp_table_lock); return ret; } - mutex_unlock(&dev_opp_list_lock); + mutex_unlock(&opp_table_lock); - /* We have opp-list node now, iterate over it and add OPPs */ + /* We have opp-table node now, iterate over it and add OPPs */ for_each_available_child_of_node(opp_np, np) { count++; @@ -1207,19 +1944,19 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np) if (WARN_ON(!count)) return -ENOENT; - mutex_lock(&dev_opp_list_lock); + mutex_lock(&opp_table_lock); - dev_opp = _find_device_opp(dev); - if (WARN_ON(IS_ERR(dev_opp))) { - ret = PTR_ERR(dev_opp); - mutex_unlock(&dev_opp_list_lock); + opp_table = _find_opp_table(dev); + if (WARN_ON(IS_ERR(opp_table))) { + ret = PTR_ERR(opp_table); + mutex_unlock(&opp_table_lock); goto free_table; } - dev_opp->np = opp_np; - dev_opp->shared_opp = of_property_read_bool(opp_np, "opp-shared"); + opp_table->np = opp_np; + opp_table->shared_opp = of_property_read_bool(opp_np, "opp-shared"); - mutex_unlock(&dev_opp_list_lock); + mutex_unlock(&opp_table_lock); return 0; @@ -1248,7 +1985,7 @@ static int _of_add_opp_table_v1(struct device *dev) */ nr = prop->length / sizeof(u32); if (nr % 2) { - dev_err(dev, "%s: Invalid OPP list\n", __func__); + dev_err(dev, "%s: Invalid OPP table\n", __func__); return -EINVAL; } @@ -1268,11 +2005,11 @@ static int _of_add_opp_table_v1(struct device *dev) /** * dev_pm_opp_of_add_table() - Initialize opp table from device tree - * @dev: device pointer used to lookup device OPPs. + * @dev: device pointer used to lookup OPP table. * * Register the initial OPP table with the OPP library for given device. * - * Locking: The internal device_opp and opp structures are RCU protected. + * Locking: The internal opp_table and opp structures are RCU protected. * Hence this function indirectly uses RCU updater strategy with mutex locks * to keep the integrity of the internal data structures. Callers should ensure * that this function is *NOT* called under RCU protection or in contexts where diff --git a/drivers/base/power/opp/cpu.c b/drivers/base/power/opp/cpu.c index 7b445e88a0d5..ba2bdbd932ef 100644 --- a/drivers/base/power/opp/cpu.c +++ b/drivers/base/power/opp/cpu.c @@ -31,7 +31,7 @@ * @table: Cpufreq table returned back to caller * * Generate a cpufreq table for a provided device- this assumes that the - * opp list is already initialized and ready for usage. + * opp table is already initialized and ready for usage. * * This function allocates required memory for the cpufreq table. It is * expected that the caller does the required maintenance such as freeing @@ -44,7 +44,7 @@ * WARNING: It is important for the callers to ensure refreshing their copy of * the table if any of the mentioned functions have been invoked in the interim. * - * Locking: The internal device_opp and opp structures are RCU protected. + * Locking: The internal opp_table and opp structures are RCU protected. * Since we just use the regular accessor functions to access the internal data * structures, we use RCU read lock inside this function. As a result, users of * this function DONOT need to use explicit locks for invoking. @@ -122,15 +122,15 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table); /* Required only for V1 bindings, as v2 can manage it from DT itself */ int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) { - struct device_list_opp *list_dev; - struct device_opp *dev_opp; + struct opp_device *opp_dev; + struct opp_table *opp_table; struct device *dev; int cpu, ret = 0; - mutex_lock(&dev_opp_list_lock); + mutex_lock(&opp_table_lock); - dev_opp = _find_device_opp(cpu_dev); - if (IS_ERR(dev_opp)) { + opp_table = _find_opp_table(cpu_dev); + if (IS_ERR(opp_table)) { ret = -EINVAL; goto unlock; } @@ -146,15 +146,15 @@ int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) continue; } - list_dev = _add_list_dev(dev, dev_opp); - if (!list_dev) { - dev_err(dev, "%s: failed to add list-dev for cpu%d device\n", + opp_dev = _add_opp_dev(dev, opp_table); + if (!opp_dev) { + dev_err(dev, "%s: failed to add opp-dev for cpu%d device\n", __func__, cpu); continue; } } unlock: - mutex_unlock(&dev_opp_list_lock); + mutex_unlock(&opp_table_lock); return ret; } @@ -214,7 +214,6 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table); /* * Works only for OPP v2 bindings. * - * cpumask should be already set to mask of cpu_dev->id. * Returns -ENOENT if operating-points-v2 bindings aren't supported. */ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) @@ -230,6 +229,8 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask return -ENOENT; } + cpumask_set_cpu(cpu_dev->id, cpumask); + /* OPPs are shared ? */ if (!of_property_read_bool(np, "opp-shared")) goto put_cpu_node; diff --git a/drivers/base/power/opp/debugfs.c b/drivers/base/power/opp/debugfs.c new file mode 100644 index 000000000000..ef1ae6b52042 --- /dev/null +++ b/drivers/base/power/opp/debugfs.c @@ -0,0 +1,218 @@ +/* + * Generic OPP debugfs interface + * + * Copyright (C) 2015-2016 Viresh Kumar <viresh.kumar@linaro.org> + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/limits.h> + +#include "opp.h" + +static struct dentry *rootdir; + +static void opp_set_dev_name(const struct device *dev, char *name) +{ + if (dev->parent) + snprintf(name, NAME_MAX, "%s-%s", dev_name(dev->parent), + dev_name(dev)); + else + snprintf(name, NAME_MAX, "%s", dev_name(dev)); +} + +void opp_debug_remove_one(struct dev_pm_opp *opp) +{ + debugfs_remove_recursive(opp->dentry); +} + +int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table) +{ + struct dentry *pdentry = opp_table->dentry; + struct dentry *d; + char name[25]; /* 20 chars for 64 bit value + 5 (opp:\0) */ + + /* Rate is unique to each OPP, use it to give opp-name */ + snprintf(name, sizeof(name), "opp:%lu", opp->rate); + + /* Create per-opp directory */ + d = debugfs_create_dir(name, pdentry); + if (!d) + return -ENOMEM; + + if (!debugfs_create_bool("available", S_IRUGO, d, &opp->available)) + return -ENOMEM; + + if (!debugfs_create_bool("dynamic", S_IRUGO, d, &opp->dynamic)) + return -ENOMEM; + + if (!debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo)) + return -ENOMEM; + + if (!debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend)) + return -ENOMEM; + + if (!debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate)) + return -ENOMEM; + + if (!debugfs_create_ulong("u_volt_target", S_IRUGO, d, &opp->u_volt)) + return -ENOMEM; + + if (!debugfs_create_ulong("u_volt_min", S_IRUGO, d, &opp->u_volt_min)) + return -ENOMEM; + + if (!debugfs_create_ulong("u_volt_max", S_IRUGO, d, &opp->u_volt_max)) + return -ENOMEM; + + if (!debugfs_create_ulong("u_amp", S_IRUGO, d, &opp->u_amp)) + return -ENOMEM; + + if (!debugfs_create_ulong("clock_latency_ns", S_IRUGO, d, + &opp->clock_latency_ns)) + return -ENOMEM; + + opp->dentry = d; + return 0; +} + +static int opp_list_debug_create_dir(struct opp_device *opp_dev, + struct opp_table *opp_table) +{ + const struct device *dev = opp_dev->dev; + struct dentry *d; + + opp_set_dev_name(dev, opp_table->dentry_name); + + /* Create device specific directory */ + d = debugfs_create_dir(opp_table->dentry_name, rootdir); + if (!d) { + dev_err(dev, "%s: Failed to create debugfs dir\n", __func__); + return -ENOMEM; + } + + opp_dev->dentry = d; + opp_table->dentry = d; + + return 0; +} + +static int opp_list_debug_create_link(struct opp_device *opp_dev, + struct opp_table *opp_table) +{ + const struct device *dev = opp_dev->dev; + char name[NAME_MAX]; + struct dentry *d; + + opp_set_dev_name(opp_dev->dev, name); + + /* Create device specific directory link */ + d = debugfs_create_symlink(name, rootdir, opp_table->dentry_name); + if (!d) { + dev_err(dev, "%s: Failed to create link\n", __func__); + return -ENOMEM; + } + + opp_dev->dentry = d; + + return 0; +} + +/** + * opp_debug_register - add a device opp node to the debugfs 'opp' directory + * @opp_dev: opp-dev pointer for device + * @opp_table: the device-opp being added + * + * Dynamically adds device specific directory in debugfs 'opp' directory. If the + * device-opp is shared with other devices, then links will be created for all + * devices except the first. + * + * Return: 0 on success, otherwise negative error. + */ +int opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table) +{ + if (!rootdir) { + pr_debug("%s: Uninitialized rootdir\n", __func__); + return -EINVAL; + } + + if (opp_table->dentry) + return opp_list_debug_create_link(opp_dev, opp_table); + + return opp_list_debug_create_dir(opp_dev, opp_table); +} + +static void opp_migrate_dentry(struct opp_device *opp_dev, + struct opp_table *opp_table) +{ + struct opp_device *new_dev; + const struct device *dev; + struct dentry *dentry; + + /* Look for next opp-dev */ + list_for_each_entry(new_dev, &opp_table->dev_list, node) + if (new_dev != opp_dev) + break; + + /* new_dev is guaranteed to be valid here */ + dev = new_dev->dev; + debugfs_remove_recursive(new_dev->dentry); + + opp_set_dev_name(dev, opp_table->dentry_name); + + dentry = debugfs_rename(rootdir, opp_dev->dentry, rootdir, + opp_table->dentry_name); + if (!dentry) { + dev_err(dev, "%s: Failed to rename link from: %s to %s\n", + __func__, dev_name(opp_dev->dev), dev_name(dev)); + return; + } + + new_dev->dentry = dentry; + opp_table->dentry = dentry; +} + +/** + * opp_debug_unregister - remove a device opp node from debugfs opp directory + * @opp_dev: opp-dev pointer for device + * @opp_table: the device-opp being removed + * + * Dynamically removes device specific directory from debugfs 'opp' directory. + */ +void opp_debug_unregister(struct opp_device *opp_dev, + struct opp_table *opp_table) +{ + if (opp_dev->dentry == opp_table->dentry) { + /* Move the real dentry object under another device */ + if (!list_is_singular(&opp_table->dev_list)) { + opp_migrate_dentry(opp_dev, opp_table); + goto out; + } + opp_table->dentry = NULL; + } + + debugfs_remove_recursive(opp_dev->dentry); + +out: + opp_dev->dentry = NULL; +} + +static int __init opp_debug_init(void) +{ + /* Create /sys/kernel/debug/opp directory */ + rootdir = debugfs_create_dir("opp", NULL); + if (!rootdir) { + pr_err("%s: Failed to create root directory\n", __func__); + return -ENOMEM; + } + + return 0; +} +core_initcall(opp_debug_init); diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h index 7366b2aa8997..f67f806fcf3a 100644 --- a/drivers/base/power/opp/opp.h +++ b/drivers/base/power/opp/opp.h @@ -17,17 +17,21 @@ #include <linux/device.h> #include <linux/kernel.h> #include <linux/list.h> +#include <linux/limits.h> #include <linux/pm_opp.h> #include <linux/rculist.h> #include <linux/rcupdate.h> +struct clk; +struct regulator; + /* Lock to allow exclusive modification to the device and opp lists */ -extern struct mutex dev_opp_list_lock; +extern struct mutex opp_table_lock; /* * Internal data structure organization with the OPP layer library is as * follows: - * dev_opp_list (root) + * opp_tables (root) * |- device 1 (represents voltage domain 1) * | |- opp 1 (availability, freq, voltage) * | |- opp 2 .. @@ -36,23 +40,24 @@ extern struct mutex dev_opp_list_lock; * |- device 2 (represents the next voltage domain) * ... * `- device m (represents mth voltage domain) - * device 1, 2.. are represented by dev_opp structure while each opp + * device 1, 2.. are represented by opp_table structure while each opp * is represented by the opp structure. */ /** * struct dev_pm_opp - Generic OPP description structure - * @node: opp list node. The nodes are maintained throughout the lifetime + * @node: opp table node. The nodes are maintained throughout the lifetime * of boot. It is expected only an optimal set of OPPs are * added to the library by the SoC framework. - * RCU usage: opp list is traversed with RCU locks. node + * RCU usage: opp table is traversed with RCU locks. node * modification is possible realtime, hence the modifications - * are protected by the dev_opp_list_lock for integrity. + * are protected by the opp_table_lock for integrity. * IMPORTANT: the opp nodes should be maintained in increasing * order. - * @dynamic: not-created from static DT entries. * @available: true/false - marks if this OPP as available or not + * @dynamic: not-created from static DT entries. * @turbo: true if turbo (boost) OPP + * @suspend: true if suspend OPP * @rate: Frequency in hertz * @u_volt: Target voltage in microvolts corresponding to this OPP * @u_volt_min: Minimum voltage in microvolts corresponding to this OPP @@ -60,9 +65,10 @@ extern struct mutex dev_opp_list_lock; * @u_amp: Maximum current drawn by the device in microamperes * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's * frequency from any other OPP's frequency. - * @dev_opp: points back to the device_opp struct this opp belongs to + * @opp_table: points back to the opp_table struct this opp belongs to * @rcu_head: RCU callback head used for deferred freeing * @np: OPP's device node. + * @dentry: debugfs dentry pointer (per opp) * * This structure stores the OPP information for a given device. */ @@ -72,6 +78,7 @@ struct dev_pm_opp { bool available; bool dynamic; bool turbo; + bool suspend; unsigned long rate; unsigned long u_volt; @@ -80,40 +87,60 @@ struct dev_pm_opp { unsigned long u_amp; unsigned long clock_latency_ns; - struct device_opp *dev_opp; + struct opp_table *opp_table; struct rcu_head rcu_head; struct device_node *np; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif }; /** - * struct device_list_opp - devices managed by 'struct device_opp' + * struct opp_device - devices managed by 'struct opp_table' * @node: list node * @dev: device to which the struct object belongs * @rcu_head: RCU callback head used for deferred freeing + * @dentry: debugfs dentry pointer (per device) * - * This is an internal data structure maintaining the list of devices that are - * managed by 'struct device_opp'. + * This is an internal data structure maintaining the devices that are managed + * by 'struct opp_table'. */ -struct device_list_opp { +struct opp_device { struct list_head node; const struct device *dev; struct rcu_head rcu_head; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif }; /** - * struct device_opp - Device opp structure - * @node: list node - contains the devices with OPPs that + * struct opp_table - Device opp structure + * @node: table node - contains the devices with OPPs that * have been registered. Nodes once added are not modified in this - * list. - * RCU usage: nodes are not modified in the list of device_opp, - * however addition is possible and is secured by dev_opp_list_lock + * table. + * RCU usage: nodes are not modified in the table of opp_table, + * however addition is possible and is secured by opp_table_lock * @srcu_head: notifier head to notify the OPP availability changes. * @rcu_head: RCU callback head used for deferred freeing * @dev_list: list of devices that share these OPPs - * @opp_list: list of opps + * @opp_list: table of opps * @np: struct device_node pointer for opp's DT node. + * @clock_latency_ns_max: Max clock latency in nanoseconds. * @shared_opp: OPP is shared between multiple devices. + * @suspend_opp: Pointer to OPP to be used during device suspend. + * @supported_hw: Array of version number to support. + * @supported_hw_count: Number of elements in supported_hw array. + * @prop_name: A name to postfix to many DT properties, while parsing them. + * @clk: Device's clock handle + * @regulator: Supply regulator + * @dentry: debugfs dentry pointer of the real device directory (not links). + * @dentry_name: Name of the real dentry. + * + * @voltage_tolerance_v1: In percentage, for v1 bindings only. * * This is an internal data structure maintaining the link to opps attached to * a device. This structure is not meant to be shared to users as it is @@ -123,7 +150,7 @@ struct device_list_opp { * need to wait for the grace period of both of them before freeing any * resources. And so we have used kfree_rcu() from within call_srcu() handlers. */ -struct device_opp { +struct opp_table { struct list_head node; struct srcu_notifier_head srcu_head; @@ -133,14 +160,48 @@ struct device_opp { struct device_node *np; unsigned long clock_latency_ns_max; + + /* For backward compatibility with v1 bindings */ + unsigned int voltage_tolerance_v1; + bool shared_opp; struct dev_pm_opp *suspend_opp; + + unsigned int *supported_hw; + unsigned int supported_hw_count; + const char *prop_name; + struct clk *clk; + struct regulator *regulator; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; + char dentry_name[NAME_MAX]; +#endif }; /* Routines internal to opp core */ -struct device_opp *_find_device_opp(struct device *dev); -struct device_list_opp *_add_list_dev(const struct device *dev, - struct device_opp *dev_opp); +struct opp_table *_find_opp_table(struct device *dev); +struct opp_device *_add_opp_dev(const struct device *dev, struct opp_table *opp_table); struct device_node *_of_get_opp_desc_node(struct device *dev); +#ifdef CONFIG_DEBUG_FS +void opp_debug_remove_one(struct dev_pm_opp *opp); +int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table); +int opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table); +void opp_debug_unregister(struct opp_device *opp_dev, struct opp_table *opp_table); +#else +static inline void opp_debug_remove_one(struct dev_pm_opp *opp) {} + +static inline int opp_debug_create_one(struct dev_pm_opp *opp, + struct opp_table *opp_table) +{ return 0; } +static inline int opp_debug_register(struct opp_device *opp_dev, + struct opp_table *opp_table) +{ return 0; } + +static inline void opp_debug_unregister(struct opp_device *opp_dev, + struct opp_table *opp_table) +{ } +#endif /* DEBUG_FS */ + #endif /* __DRIVER_OPP_H__ */ diff --git a/drivers/block/brd.c b/drivers/block/brd.c index a5880f4ab40e..1914c63ca8b1 100644 --- a/drivers/block/brd.c +++ b/drivers/block/brd.c @@ -338,7 +338,7 @@ static blk_qc_t brd_make_request(struct request_queue *q, struct bio *bio) if (unlikely(bio->bi_rw & REQ_DISCARD)) { if (sector & ((PAGE_SIZE >> SECTOR_SHIFT) - 1) || - bio->bi_iter.bi_size & PAGE_MASK) + bio->bi_iter.bi_size & ~PAGE_MASK) goto io_error; discard_from_brd(brd, sector, bio->bi_iter.bi_size); goto out; diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 3457ac8c03e2..55d3d1da72de 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -173,7 +173,13 @@ static struct mtip_cmd *mtip_get_int_command(struct driver_data *dd) { struct request *rq; + if (mtip_check_surprise_removal(dd->pdev)) + return NULL; + rq = blk_mq_alloc_request(dd->queue, 0, __GFP_RECLAIM, true); + if (IS_ERR(rq)) + return NULL; + return blk_mq_rq_to_pdu(rq); } @@ -233,15 +239,9 @@ static void mtip_async_complete(struct mtip_port *port, "Command tag %d failed due to TFE\n", tag); } - /* Unmap the DMA scatter list entries */ - dma_unmap_sg(&dd->pdev->dev, cmd->sg, cmd->scatter_ents, cmd->direction); - rq = mtip_rq_from_tag(dd, tag); - if (unlikely(cmd->unaligned)) - up(&port->cmd_slot_unal); - - blk_mq_end_request(rq, status ? -EIO : 0); + blk_mq_complete_request(rq, status); } /* @@ -581,6 +581,8 @@ static void mtip_completion(struct mtip_port *port, dev_warn(&port->dd->pdev->dev, "Internal command %d completed with TFE\n", tag); + command->comp_func = NULL; + command->comp_data = NULL; complete(waiting); } @@ -618,8 +620,6 @@ static void mtip_handle_tfe(struct driver_data *dd) port = dd->port; - set_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags); - if (test_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags)) { cmd = mtip_cmd_from_tag(dd, MTIP_TAG_INTERNAL); dbg_printk(MTIP_DRV_NAME " TFE for the internal command\n"); @@ -628,7 +628,7 @@ static void mtip_handle_tfe(struct driver_data *dd) cmd->comp_func(port, MTIP_TAG_INTERNAL, cmd, PORT_IRQ_TF_ERR); } - goto handle_tfe_exit; + return; } /* clear the tag accumulator */ @@ -701,7 +701,7 @@ static void mtip_handle_tfe(struct driver_data *dd) fail_reason = "thermal shutdown"; } if (buf[288] == 0xBF) { - set_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag); + set_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag); dev_info(&dd->pdev->dev, "Drive indicates rebuild has failed. Secure erase required.\n"); fail_all_ncq_cmds = 1; @@ -771,11 +771,6 @@ static void mtip_handle_tfe(struct driver_data *dd) } } print_tags(dd, "reissued (TFE)", tagaccum, cmd_cnt); - -handle_tfe_exit: - /* clear eh_active */ - clear_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags); - wake_up_interruptible(&port->svc_wait); } /* @@ -1007,6 +1002,7 @@ static bool mtip_pause_ncq(struct mtip_port *port, (fis->features == 0x27 || fis->features == 0x72 || fis->features == 0x62 || fis->features == 0x26))) { clear_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag); + clear_bit(MTIP_DDF_REBUILD_FAILED_BIT, &port->dd->dd_flag); /* Com reset after secure erase or lowlevel format */ mtip_restart_port(port); clear_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags); @@ -1021,12 +1017,14 @@ static bool mtip_pause_ncq(struct mtip_port *port, * * @port Pointer to port data structure * @timeout Max duration to wait (ms) + * @atomic gfp_t flag to indicate blockable context or not * * return value * 0 Success * -EBUSY Commands still active */ -static int mtip_quiesce_io(struct mtip_port *port, unsigned long timeout) +static int mtip_quiesce_io(struct mtip_port *port, unsigned long timeout, + gfp_t atomic) { unsigned long to; unsigned int n; @@ -1037,16 +1035,21 @@ static int mtip_quiesce_io(struct mtip_port *port, unsigned long timeout) to = jiffies + msecs_to_jiffies(timeout); do { if (test_bit(MTIP_PF_SVC_THD_ACTIVE_BIT, &port->flags) && - test_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags)) { + test_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags) && + atomic == GFP_KERNEL) { msleep(20); continue; /* svc thd is actively issuing commands */ } - msleep(100); + if (atomic == GFP_KERNEL) + msleep(100); + else { + cpu_relax(); + udelay(100); + } + if (mtip_check_surprise_removal(port->dd->pdev)) goto err_fault; - if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &port->dd->dd_flag)) - goto err_fault; /* * Ignore s_active bit 0 of array element 0. @@ -1099,6 +1102,7 @@ static int mtip_exec_internal_command(struct mtip_port *port, struct mtip_cmd *int_cmd; struct driver_data *dd = port->dd; int rv = 0; + unsigned long start; /* Make sure the buffer is 8 byte aligned. This is asic specific. */ if (buffer & 0x00000007) { @@ -1107,6 +1111,10 @@ static int mtip_exec_internal_command(struct mtip_port *port, } int_cmd = mtip_get_int_command(dd); + if (!int_cmd) { + dbg_printk(MTIP_DRV_NAME "Unable to allocate tag for PIO cmd\n"); + return -EFAULT; + } set_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags); @@ -1119,7 +1127,7 @@ static int mtip_exec_internal_command(struct mtip_port *port, if (fis->command != ATA_CMD_STANDBYNOW1) { /* wait for io to complete if non atomic */ if (mtip_quiesce_io(port, - MTIP_QUIESCE_IO_TIMEOUT_MS) < 0) { + MTIP_QUIESCE_IO_TIMEOUT_MS, atomic) < 0) { dev_warn(&dd->pdev->dev, "Failed to quiesce IO\n"); mtip_put_int_command(dd, int_cmd); @@ -1162,6 +1170,8 @@ static int mtip_exec_internal_command(struct mtip_port *port, /* Populate the command header */ int_cmd->command_header->byte_count = 0; + start = jiffies; + /* Issue the command to the hardware */ mtip_issue_non_ncq_command(port, MTIP_TAG_INTERNAL); @@ -1170,10 +1180,12 @@ static int mtip_exec_internal_command(struct mtip_port *port, if ((rv = wait_for_completion_interruptible_timeout( &wait, msecs_to_jiffies(timeout))) <= 0) { + if (rv == -ERESTARTSYS) { /* interrupted */ dev_err(&dd->pdev->dev, - "Internal command [%02X] was interrupted after %lu ms\n", - fis->command, timeout); + "Internal command [%02X] was interrupted after %u ms\n", + fis->command, + jiffies_to_msecs(jiffies - start)); rv = -EINTR; goto exec_ic_exit; } else if (rv == 0) /* timeout */ @@ -2897,6 +2909,42 @@ static int mtip_ftl_rebuild_poll(struct driver_data *dd) return -EFAULT; } +static void mtip_softirq_done_fn(struct request *rq) +{ + struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq); + struct driver_data *dd = rq->q->queuedata; + + /* Unmap the DMA scatter list entries */ + dma_unmap_sg(&dd->pdev->dev, cmd->sg, cmd->scatter_ents, + cmd->direction); + + if (unlikely(cmd->unaligned)) + up(&dd->port->cmd_slot_unal); + + blk_mq_end_request(rq, rq->errors); +} + +static void mtip_abort_cmd(struct request *req, void *data, + bool reserved) +{ + struct driver_data *dd = data; + + dbg_printk(MTIP_DRV_NAME " Aborting request, tag = %d\n", req->tag); + + clear_bit(req->tag, dd->port->cmds_to_issue); + req->errors = -EIO; + mtip_softirq_done_fn(req); +} + +static void mtip_queue_cmd(struct request *req, void *data, + bool reserved) +{ + struct driver_data *dd = data; + + set_bit(req->tag, dd->port->cmds_to_issue); + blk_abort_request(req); +} + /* * service thread to issue queued commands * @@ -2909,7 +2957,7 @@ static int mtip_ftl_rebuild_poll(struct driver_data *dd) static int mtip_service_thread(void *data) { struct driver_data *dd = (struct driver_data *)data; - unsigned long slot, slot_start, slot_wrap; + unsigned long slot, slot_start, slot_wrap, to; unsigned int num_cmd_slots = dd->slot_groups * 32; struct mtip_port *port = dd->port; @@ -2924,9 +2972,7 @@ static int mtip_service_thread(void *data) * is in progress nor error handling is active */ wait_event_interruptible(port->svc_wait, (port->flags) && - !(port->flags & MTIP_PF_PAUSE_IO)); - - set_bit(MTIP_PF_SVC_THD_ACTIVE_BIT, &port->flags); + (port->flags & MTIP_PF_SVC_THD_WORK)); if (kthread_should_stop() || test_bit(MTIP_PF_SVC_THD_STOP_BIT, &port->flags)) @@ -2936,6 +2982,8 @@ static int mtip_service_thread(void *data) &dd->dd_flag))) goto st_out; + set_bit(MTIP_PF_SVC_THD_ACTIVE_BIT, &port->flags); + restart_eh: /* Demux bits: start with error handling */ if (test_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags)) { @@ -2946,6 +2994,32 @@ restart_eh: if (test_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags)) goto restart_eh; + if (test_bit(MTIP_PF_TO_ACTIVE_BIT, &port->flags)) { + to = jiffies + msecs_to_jiffies(5000); + + do { + mdelay(100); + } while (atomic_read(&dd->irq_workers_active) != 0 && + time_before(jiffies, to)); + + if (atomic_read(&dd->irq_workers_active) != 0) + dev_warn(&dd->pdev->dev, + "Completion workers still active!"); + + spin_lock(dd->queue->queue_lock); + blk_mq_all_tag_busy_iter(*dd->tags.tags, + mtip_queue_cmd, dd); + spin_unlock(dd->queue->queue_lock); + + set_bit(MTIP_PF_ISSUE_CMDS_BIT, &dd->port->flags); + + if (mtip_device_reset(dd)) + blk_mq_all_tag_busy_iter(*dd->tags.tags, + mtip_abort_cmd, dd); + + clear_bit(MTIP_PF_TO_ACTIVE_BIT, &dd->port->flags); + } + if (test_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags)) { slot = 1; /* used to restrict the loop to one iteration */ @@ -2978,10 +3052,8 @@ restart_eh: } if (test_bit(MTIP_PF_REBUILD_BIT, &port->flags)) { - if (mtip_ftl_rebuild_poll(dd) < 0) - set_bit(MTIP_DDF_REBUILD_FAILED_BIT, - &dd->dd_flag); - clear_bit(MTIP_PF_REBUILD_BIT, &port->flags); + if (mtip_ftl_rebuild_poll(dd) == 0) + clear_bit(MTIP_PF_REBUILD_BIT, &port->flags); } } @@ -3096,7 +3168,7 @@ static int mtip_hw_get_identify(struct driver_data *dd) if (buf[288] == 0xBF) { dev_info(&dd->pdev->dev, "Drive indicates rebuild has failed.\n"); - /* TODO */ + set_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag); } } @@ -3270,20 +3342,25 @@ out1: return rv; } -static void mtip_standby_drive(struct driver_data *dd) +static int mtip_standby_drive(struct driver_data *dd) { - if (dd->sr) - return; + int rv = 0; + if (dd->sr || !dd->port) + return -ENODEV; /* * Send standby immediate (E0h) to the drive so that it * saves its state. */ if (!test_bit(MTIP_PF_REBUILD_BIT, &dd->port->flags) && - !test_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag)) - if (mtip_standby_immediate(dd->port)) + !test_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag) && + !test_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag)) { + rv = mtip_standby_immediate(dd->port); + if (rv) dev_warn(&dd->pdev->dev, "STANDBY IMMEDIATE failed\n"); + } + return rv; } /* @@ -3296,10 +3373,6 @@ static void mtip_standby_drive(struct driver_data *dd) */ static int mtip_hw_exit(struct driver_data *dd) { - /* - * Send standby immediate (E0h) to the drive so that it - * saves its state. - */ if (!dd->sr) { /* de-initialize the port. */ mtip_deinit_port(dd->port); @@ -3341,8 +3414,7 @@ static int mtip_hw_shutdown(struct driver_data *dd) * Send standby immediate (E0h) to the drive so that it * saves its state. */ - if (!dd->sr && dd->port) - mtip_standby_immediate(dd->port); + mtip_standby_drive(dd); return 0; } @@ -3365,7 +3437,7 @@ static int mtip_hw_suspend(struct driver_data *dd) * Send standby immediate (E0h) to the drive * so that it saves its state. */ - if (mtip_standby_immediate(dd->port) != 0) { + if (mtip_standby_drive(dd) != 0) { dev_err(&dd->pdev->dev, "Failed standby-immediate command\n"); return -EFAULT; @@ -3603,6 +3675,28 @@ static int mtip_block_getgeo(struct block_device *dev, return 0; } +static int mtip_block_open(struct block_device *dev, fmode_t mode) +{ + struct driver_data *dd; + + if (dev && dev->bd_disk) { + dd = (struct driver_data *) dev->bd_disk->private_data; + + if (dd) { + if (test_bit(MTIP_DDF_REMOVAL_BIT, + &dd->dd_flag)) { + return -ENODEV; + } + return 0; + } + } + return -ENODEV; +} + +void mtip_block_release(struct gendisk *disk, fmode_t mode) +{ +} + /* * Block device operation function. * @@ -3610,6 +3704,8 @@ static int mtip_block_getgeo(struct block_device *dev, * layer. */ static const struct block_device_operations mtip_block_ops = { + .open = mtip_block_open, + .release = mtip_block_release, .ioctl = mtip_block_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = mtip_block_compat_ioctl, @@ -3671,10 +3767,9 @@ static int mtip_submit_request(struct blk_mq_hw_ctx *hctx, struct request *rq) rq_data_dir(rq))) { return -ENODATA; } - if (unlikely(test_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag))) + if (unlikely(test_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag) || + test_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag))) return -ENODATA; - if (test_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag)) - return -ENXIO; } if (rq->cmd_flags & REQ_DISCARD) { @@ -3786,11 +3881,33 @@ static int mtip_init_cmd(void *data, struct request *rq, unsigned int hctx_idx, return 0; } +static enum blk_eh_timer_return mtip_cmd_timeout(struct request *req, + bool reserved) +{ + struct driver_data *dd = req->q->queuedata; + int ret = BLK_EH_RESET_TIMER; + + if (reserved) + goto exit_handler; + + if (test_bit(req->tag, dd->port->cmds_to_issue)) + goto exit_handler; + + if (test_and_set_bit(MTIP_PF_TO_ACTIVE_BIT, &dd->port->flags)) + goto exit_handler; + + wake_up_interruptible(&dd->port->svc_wait); +exit_handler: + return ret; +} + static struct blk_mq_ops mtip_mq_ops = { .queue_rq = mtip_queue_rq, .map_queue = blk_mq_map_queue, .init_request = mtip_init_cmd, .exit_request = mtip_free_cmd, + .complete = mtip_softirq_done_fn, + .timeout = mtip_cmd_timeout, }; /* @@ -3857,7 +3974,6 @@ static int mtip_block_initialize(struct driver_data *dd) mtip_hw_debugfs_init(dd); -skip_create_disk: memset(&dd->tags, 0, sizeof(dd->tags)); dd->tags.ops = &mtip_mq_ops; dd->tags.nr_hw_queues = 1; @@ -3867,12 +3983,13 @@ skip_create_disk: dd->tags.numa_node = dd->numa_node; dd->tags.flags = BLK_MQ_F_SHOULD_MERGE; dd->tags.driver_data = dd; + dd->tags.timeout = MTIP_NCQ_CMD_TIMEOUT_MS; rv = blk_mq_alloc_tag_set(&dd->tags); if (rv) { dev_err(&dd->pdev->dev, "Unable to allocate request queue\n"); - goto block_queue_alloc_init_error; + goto block_queue_alloc_tag_error; } /* Allocate the request queue. */ @@ -3887,6 +4004,7 @@ skip_create_disk: dd->disk->queue = dd->queue; dd->queue->queuedata = dd; +skip_create_disk: /* Initialize the protocol layer. */ wait_for_rebuild = mtip_hw_get_identify(dd); if (wait_for_rebuild < 0) { @@ -3983,8 +4101,9 @@ kthread_run_error: read_capacity_error: init_hw_cmds_error: blk_cleanup_queue(dd->queue); - blk_mq_free_tag_set(&dd->tags); block_queue_alloc_init_error: + blk_mq_free_tag_set(&dd->tags); +block_queue_alloc_tag_error: mtip_hw_debugfs_exit(dd); disk_index_error: spin_lock(&rssd_index_lock); @@ -4001,6 +4120,22 @@ protocol_init_error: return rv; } +static void mtip_no_dev_cleanup(struct request *rq, void *data, bool reserv) +{ + struct driver_data *dd = (struct driver_data *)data; + struct mtip_cmd *cmd; + + if (likely(!reserv)) + blk_mq_complete_request(rq, -ENODEV); + else if (test_bit(MTIP_PF_IC_ACTIVE_BIT, &dd->port->flags)) { + + cmd = mtip_cmd_from_tag(dd, MTIP_TAG_INTERNAL); + if (cmd->comp_func) + cmd->comp_func(dd->port, MTIP_TAG_INTERNAL, + cmd, -ENODEV); + } +} + /* * Block layer deinitialization function. * @@ -4032,12 +4167,23 @@ static int mtip_block_remove(struct driver_data *dd) } } - if (!dd->sr) - mtip_standby_drive(dd); + if (!dd->sr) { + /* + * Explicitly wait here for IOs to quiesce, + * as mtip_standby_drive usually won't wait for IOs. + */ + if (!mtip_quiesce_io(dd->port, MTIP_QUIESCE_IO_TIMEOUT_MS, + GFP_KERNEL)) + mtip_standby_drive(dd); + } else dev_info(&dd->pdev->dev, "device %s surprise removal\n", dd->disk->disk_name); + blk_mq_freeze_queue_start(dd->queue); + blk_mq_stop_hw_queues(dd->queue); + blk_mq_all_tag_busy_iter(dd->tags.tags[0], mtip_no_dev_cleanup, dd); + /* * Delete our gendisk structure. This also removes the device * from /dev @@ -4047,7 +4193,8 @@ static int mtip_block_remove(struct driver_data *dd) dd->bdev = NULL; } if (dd->disk) { - del_gendisk(dd->disk); + if (test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag)) + del_gendisk(dd->disk); if (dd->disk->queue) { blk_cleanup_queue(dd->queue); blk_mq_free_tag_set(&dd->tags); @@ -4088,7 +4235,8 @@ static int mtip_block_shutdown(struct driver_data *dd) dev_info(&dd->pdev->dev, "Shutting down %s ...\n", dd->disk->disk_name); - del_gendisk(dd->disk); + if (test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag)) + del_gendisk(dd->disk); if (dd->disk->queue) { blk_cleanup_queue(dd->queue); blk_mq_free_tag_set(&dd->tags); @@ -4433,7 +4581,7 @@ static void mtip_pci_remove(struct pci_dev *pdev) struct driver_data *dd = pci_get_drvdata(pdev); unsigned long flags, to; - set_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag); + set_bit(MTIP_DDF_REMOVAL_BIT, &dd->dd_flag); spin_lock_irqsave(&dev_lock, flags); list_del_init(&dd->online_list); @@ -4450,12 +4598,17 @@ static void mtip_pci_remove(struct pci_dev *pdev) } while (atomic_read(&dd->irq_workers_active) != 0 && time_before(jiffies, to)); + if (!dd->sr) + fsync_bdev(dd->bdev); + if (atomic_read(&dd->irq_workers_active) != 0) { dev_warn(&dd->pdev->dev, "Completion workers still active!\n"); } - blk_mq_stop_hw_queues(dd->queue); + blk_set_queue_dying(dd->queue); + set_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag); + /* Clean up the block layer. */ mtip_block_remove(dd); diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h index 3274784008eb..7617888f7944 100644 --- a/drivers/block/mtip32xx/mtip32xx.h +++ b/drivers/block/mtip32xx/mtip32xx.h @@ -134,16 +134,24 @@ enum { MTIP_PF_EH_ACTIVE_BIT = 1, /* error handling */ MTIP_PF_SE_ACTIVE_BIT = 2, /* secure erase */ MTIP_PF_DM_ACTIVE_BIT = 3, /* download microcde */ + MTIP_PF_TO_ACTIVE_BIT = 9, /* timeout handling */ MTIP_PF_PAUSE_IO = ((1 << MTIP_PF_IC_ACTIVE_BIT) | (1 << MTIP_PF_EH_ACTIVE_BIT) | (1 << MTIP_PF_SE_ACTIVE_BIT) | - (1 << MTIP_PF_DM_ACTIVE_BIT)), + (1 << MTIP_PF_DM_ACTIVE_BIT) | + (1 << MTIP_PF_TO_ACTIVE_BIT)), MTIP_PF_SVC_THD_ACTIVE_BIT = 4, MTIP_PF_ISSUE_CMDS_BIT = 5, MTIP_PF_REBUILD_BIT = 6, MTIP_PF_SVC_THD_STOP_BIT = 8, + MTIP_PF_SVC_THD_WORK = ((1 << MTIP_PF_EH_ACTIVE_BIT) | + (1 << MTIP_PF_ISSUE_CMDS_BIT) | + (1 << MTIP_PF_REBUILD_BIT) | + (1 << MTIP_PF_SVC_THD_STOP_BIT) | + (1 << MTIP_PF_TO_ACTIVE_BIT)), + /* below are bit numbers in 'dd_flag' defined in driver_data */ MTIP_DDF_SEC_LOCK_BIT = 0, MTIP_DDF_REMOVE_PENDING_BIT = 1, @@ -153,6 +161,7 @@ enum { MTIP_DDF_RESUME_BIT = 6, MTIP_DDF_INIT_DONE_BIT = 7, MTIP_DDF_REBUILD_FAILED_BIT = 8, + MTIP_DDF_REMOVAL_BIT = 9, MTIP_DDF_STOP_IO = ((1 << MTIP_DDF_REMOVE_PENDING_BIT) | (1 << MTIP_DDF_SEC_LOCK_BIT) | diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 81ea69fee7ca..fbdddd6f94b8 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1955,7 +1955,7 @@ static struct ceph_osd_request *rbd_osd_req_create( osdc = &rbd_dev->rbd_client->client->osdc; osd_req = ceph_osdc_alloc_request(osdc, snapc, num_ops, false, - GFP_ATOMIC); + GFP_NOIO); if (!osd_req) return NULL; /* ENOMEM */ @@ -2004,7 +2004,7 @@ rbd_osd_req_create_copyup(struct rbd_obj_request *obj_request) rbd_dev = img_request->rbd_dev; osdc = &rbd_dev->rbd_client->client->osdc; osd_req = ceph_osdc_alloc_request(osdc, snapc, num_osd_ops, - false, GFP_ATOMIC); + false, GFP_NOIO); if (!osd_req) return NULL; /* ENOMEM */ @@ -2506,7 +2506,7 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request, bio_chain_clone_range(&bio_list, &bio_offset, clone_size, - GFP_ATOMIC); + GFP_NOIO); if (!obj_request->bio_list) goto out_unwind; } else if (type == OBJ_REQUEST_PAGES) { diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index fa893c3ec408..0beaa52df66b 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -82,6 +82,7 @@ static const struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x0489, 0xe05f) }, { USB_DEVICE(0x0489, 0xe076) }, { USB_DEVICE(0x0489, 0xe078) }, + { USB_DEVICE(0x0489, 0xe095) }, { USB_DEVICE(0x04c5, 0x1330) }, { USB_DEVICE(0x04CA, 0x3004) }, { USB_DEVICE(0x04CA, 0x3005) }, @@ -92,6 +93,7 @@ static const struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x04CA, 0x300d) }, { USB_DEVICE(0x04CA, 0x300f) }, { USB_DEVICE(0x04CA, 0x3010) }, + { USB_DEVICE(0x04CA, 0x3014) }, { USB_DEVICE(0x0930, 0x0219) }, { USB_DEVICE(0x0930, 0x021c) }, { USB_DEVICE(0x0930, 0x0220) }, @@ -113,10 +115,12 @@ static const struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x13d3, 0x3362) }, { USB_DEVICE(0x13d3, 0x3375) }, { USB_DEVICE(0x13d3, 0x3393) }, + { USB_DEVICE(0x13d3, 0x3395) }, { USB_DEVICE(0x13d3, 0x3402) }, { USB_DEVICE(0x13d3, 0x3408) }, { USB_DEVICE(0x13d3, 0x3423) }, { USB_DEVICE(0x13d3, 0x3432) }, + { USB_DEVICE(0x13d3, 0x3472) }, { USB_DEVICE(0x13d3, 0x3474) }, /* Atheros AR5BBU12 with sflash firmware */ @@ -144,6 +148,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe076), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0489, 0xe095), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, @@ -154,6 +159,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x04ca, 0x300d), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 }, @@ -175,10 +181,12 @@ static const struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x13d3, 0x3395), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x13d3, 0x3472), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3474), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU22 with sflash firmware */ diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 968897108c76..79107597a594 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -196,6 +196,7 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe076), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0489, 0xe095), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, @@ -206,6 +207,7 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x04ca, 0x300d), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 }, @@ -227,10 +229,12 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x13d3, 0x3395), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x13d3, 0x3472), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3474), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU12 with sflash firmware */ diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 45cc39aabeee..252142524ff2 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -136,11 +136,13 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev, chip->cdev.owner = chip->pdev->driver->owner; chip->cdev.kobj.parent = &chip->dev.kobj; + devm_add_action(dev, (void (*)(void *)) put_device, &chip->dev); + return chip; } EXPORT_SYMBOL_GPL(tpmm_chip_alloc); -static int tpm_dev_add_device(struct tpm_chip *chip) +static int tpm_add_char_device(struct tpm_chip *chip) { int rc; @@ -151,7 +153,6 @@ static int tpm_dev_add_device(struct tpm_chip *chip) chip->devname, MAJOR(chip->dev.devt), MINOR(chip->dev.devt), rc); - device_unregister(&chip->dev); return rc; } @@ -162,16 +163,17 @@ static int tpm_dev_add_device(struct tpm_chip *chip) chip->devname, MAJOR(chip->dev.devt), MINOR(chip->dev.devt), rc); + cdev_del(&chip->cdev); return rc; } return rc; } -static void tpm_dev_del_device(struct tpm_chip *chip) +static void tpm_del_char_device(struct tpm_chip *chip) { cdev_del(&chip->cdev); - device_unregister(&chip->dev); + device_del(&chip->dev); } static int tpm1_chip_register(struct tpm_chip *chip) @@ -222,7 +224,7 @@ int tpm_chip_register(struct tpm_chip *chip) tpm_add_ppi(chip); - rc = tpm_dev_add_device(chip); + rc = tpm_add_char_device(chip); if (rc) goto out_err; @@ -274,6 +276,6 @@ void tpm_chip_unregister(struct tpm_chip *chip) sysfs_remove_link(&chip->pdev->kobj, "ppi"); tpm1_chip_unregister(chip); - tpm_dev_del_device(chip); + tpm_del_char_device(chip); } EXPORT_SYMBOL_GPL(tpm_chip_unregister); diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 4bb9727c1047..61e64293b765 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -310,11 +310,11 @@ static int crb_acpi_remove(struct acpi_device *device) struct device *dev = &device->dev; struct tpm_chip *chip = dev_get_drvdata(dev); - tpm_chip_unregister(chip); - if (chip->flags & TPM_CHIP_FLAG_TPM2) tpm2_shutdown(chip, TPM2_SU_CLEAR); + tpm_chip_unregister(chip); + return 0; } diff --git a/drivers/char/tpm/tpm_eventlog.c b/drivers/char/tpm/tpm_eventlog.c index bd72fb04225e..4e6940acf639 100644 --- a/drivers/char/tpm/tpm_eventlog.c +++ b/drivers/char/tpm/tpm_eventlog.c @@ -232,7 +232,7 @@ static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v) { struct tcpa_event *event = v; struct tcpa_event temp_event; - char *tempPtr; + char *temp_ptr; int i; memcpy(&temp_event, event, sizeof(struct tcpa_event)); @@ -242,10 +242,16 @@ static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v) temp_event.event_type = do_endian_conversion(event->event_type); temp_event.event_size = do_endian_conversion(event->event_size); - tempPtr = (char *)&temp_event; + temp_ptr = (char *) &temp_event; - for (i = 0; i < sizeof(struct tcpa_event) + temp_event.event_size; i++) - seq_putc(m, tempPtr[i]); + for (i = 0; i < (sizeof(struct tcpa_event) - 1) ; i++) + seq_putc(m, temp_ptr[i]); + + temp_ptr = (char *) v; + + for (i = (sizeof(struct tcpa_event) - 1); + i < (sizeof(struct tcpa_event) + temp_event.event_size); i++) + seq_putc(m, temp_ptr[i]); return 0; diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 39bf5820297e..4f9830c1b121 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -1097,13 +1097,15 @@ static int bcm2835_pll_divider_set_rate(struct clk_hw *hw, struct bcm2835_pll_divider *divider = bcm2835_pll_divider_from_hw(hw); struct bcm2835_cprman *cprman = divider->cprman; const struct bcm2835_pll_divider_data *data = divider->data; - u32 cm; - int ret; + u32 cm, div, max_div = 1 << A2W_PLL_DIV_BITS; - ret = clk_divider_ops.set_rate(hw, rate, parent_rate); - if (ret) - return ret; + div = DIV_ROUND_UP_ULL(parent_rate, rate); + + div = min(div, max_div); + if (div == max_div) + div = 0; + cprman_write(cprman, data->a2w_reg, div); cm = cprman_read(cprman, data->cm_reg); cprman_write(cprman, data->cm_reg, cm | data->load_mask); cprman_write(cprman, data->cm_reg, cm & ~data->load_mask); diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile index 107af19abff2..ecf0b09bb49a 100644 --- a/drivers/clk/msm/Makefile +++ b/drivers/clk/msm/Makefile @@ -13,9 +13,9 @@ obj-$(CONFIG_COMMON_CLK_MSM) += clock-debug.o # MSM 8996 ifeq ($(CONFIG_COMMON_CLK_MSM), y) - obj-$(CONFIG_ARCH_QCOM) += clock-gcc-8996.o - obj-$(CONFIG_ARCH_QCOM) += clock-mmss-8996.o - obj-$(CONFIG_ARCH_QCOM) += clock-cpu-8996.o + obj-$(CONFIG_ARCH_MSM8996) += clock-gcc-8996.o + obj-$(CONFIG_ARCH_MSM8996) += clock-mmss-8996.o + obj-$(CONFIG_ARCH_MSM8996) += clock-cpu-8996.o endif # MSM COBALT @@ -27,4 +27,4 @@ ifeq ($(CONFIG_COMMON_CLK_MSM), y) endif obj-$(CONFIG_COMMON_CLK_MSM) += gdsc.o -obj-$(CONFIG_COMMON_CLK_MSM)-y += mdss/ +obj-$(CONFIG_COMMON_CLK_MSM) += mdss/ diff --git a/drivers/clk/msm/clock-gcc-cobalt.c b/drivers/clk/msm/clock-gcc-cobalt.c index 1524d3d8ed46..7299863ff42b 100644 --- a/drivers/clk/msm/clock-gcc-cobalt.c +++ b/drivers/clk/msm/clock-gcc-cobalt.c @@ -56,6 +56,7 @@ static void __iomem *virt_dbgbase; } static DEFINE_VDD_REGULATORS(vdd_dig, VDD_DIG_NUM, 1, vdd_corner, NULL); +static DEFINE_VDD_REGULATORS(vdd_dig_ao, VDD_DIG_NUM, 1, vdd_corner, NULL); DEFINE_CLK_RPM_SMD_BRANCH(cxo_clk_src, cxo_clk_src_ao, RPM_MISC_CLK_TYPE, CXO_CLK_SRC_ID, 19200000); @@ -211,7 +212,7 @@ static struct rcg_clk hmss_ahb_clk_src = { .c = { .dbg_name = "hmss_ahb_clk_src", .ops = &clk_ops_rcg, - VDD_DIG_FMAX_MAP3(LOWER, 19200000, LOW, 50000000, + VDD_DIG_FMAX_MAP3_AO(LOWER, 19200000, LOW, 50000000, NOMINAL, 100000000), CLK_INIT(hmss_ahb_clk_src.c), }, @@ -1029,7 +1030,7 @@ static struct rcg_clk hmss_gpll0_clk_src = { .c = { .dbg_name = "hmss_gpll0_clk_src", .ops = &clk_ops_rcg, - VDD_DIG_FMAX_MAP1(LOWER, 600000000), + VDD_DIG_FMAX_MAP1_AO(LOWER, 600000000), CLK_INIT(hmss_gpll0_clk_src.c), }, }; @@ -2184,17 +2185,6 @@ static struct reset_clk gcc_qusb2phy_sec_reset = { }, }; -static struct branch_clk gcc_usb_phy_cfg_ahb2phy_clk = { - .cbcr_reg = GCC_USB_PHY_CFG_AHB2PHY_CBCR, - .has_sibling = 1, - .base = &virt_base, - .c = { - .dbg_name = "gcc_usb_phy_cfg_ahb2phy_clk", - .ops = &clk_ops_branch, - CLK_INIT(gcc_usb_phy_cfg_ahb2phy_clk.c), - }, -}; - static struct branch_clk gcc_wcss_ahb_s0_clk = { .cbcr_reg = GCC_WCSS_AHB_S0_CBCR, .has_sibling = 1, @@ -2380,7 +2370,6 @@ static struct mux_clk gcc_debug_mux = { { &gcc_usb30_mock_utmi_clk.c, 0x0040 }, { &gcc_usb3_phy_aux_clk.c, 0x0041 }, { &gcc_usb3_phy_pipe_clk.c, 0x0042 }, - { &gcc_usb_phy_cfg_ahb2phy_clk.c, 0x0045 }, { &gcc_sdcc2_apps_clk.c, 0x0046 }, { &gcc_sdcc2_ahb_clk.c, 0x0047 }, { &gcc_sdcc4_apps_clk.c, 0x0048 }, @@ -2687,7 +2676,6 @@ static struct clk_lookup msm_clocks_gcc_cobalt[] = { CLK_LIST(gcc_usb30_sleep_clk), CLK_LIST(gcc_usb3_phy_aux_clk), CLK_LIST(gcc_usb3_phy_pipe_clk), - CLK_LIST(gcc_usb_phy_cfg_ahb2phy_clk), CLK_LIST(gcc_prng_ahb_clk), CLK_LIST(gcc_boot_rom_ahb_clk), CLK_LIST(gcc_wcss_ahb_s0_clk), @@ -2748,11 +2736,6 @@ static int msm_gcc_cobalt_probe(struct platform_device *pdev) return -ENOMEM; } - /* Set the HMSS_AHB_CLK_ENA bit to enable the hmss_ahb_clk */ - regval = readl_relaxed(virt_base + GCC_APCS_CLOCK_BRANCH_ENA_VOTE); - regval |= BIT(21); - writel_relaxed(regval, virt_base + GCC_APCS_CLOCK_BRANCH_ENA_VOTE); - /* * Set the HMSS_AHB_CLK_SLEEP_ENA bit to allow the hmss_ahb_clk to be * turned off by hardware during certain apps low power modes. @@ -2769,6 +2752,14 @@ static int msm_gcc_cobalt_probe(struct platform_device *pdev) return PTR_ERR(vdd_dig.regulator[0]); } + vdd_dig_ao.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_dig_ao"); + if (IS_ERR(vdd_dig_ao.regulator[0])) { + if (!(PTR_ERR(vdd_dig_ao.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, + "Unable to get vdd_dig_ao regulator\n"); + return PTR_ERR(vdd_dig_ao.regulator[0]); + } + bimc_clk.c.parent = &cxo_clk_src.c; ret = of_msm_clock_register(pdev->dev.of_node, msm_clocks_rpm_cobalt, ARRAY_SIZE(msm_clocks_rpm_cobalt)); diff --git a/drivers/clk/msm/clock-gpu-cobalt.c b/drivers/clk/msm/clock-gpu-cobalt.c index ce3e7916e658..7230c7a2bc04 100644 --- a/drivers/clk/msm/clock-gpu-cobalt.c +++ b/drivers/clk/msm/clock-gpu-cobalt.c @@ -109,7 +109,7 @@ static struct alpha_pll_clk gpu_pll0_pll = { .parent = &gpucc_xo.c, .dbg_name = "gpu_pll0_pll", .ops = &clk_ops_fabia_alpha_pll, - VDD_GPU_PLL_FMAX_MAP1(NOMINAL, 1300000500), + VDD_GPU_PLL_FMAX_MAP1(MIN, 1300000500), CLK_INIT(gpu_pll0_pll.c), }, }; @@ -168,7 +168,7 @@ static struct alpha_pll_clk gpu_pll1_pll = { .parent = &gpucc_xo.c, .dbg_name = "gpu_pll1_pll", .ops = &clk_ops_fabia_alpha_pll, - VDD_GPU_PLL_FMAX_MAP1(NOMINAL, 1300000500), + VDD_GPU_PLL_FMAX_MAP1(MIN, 1300000500), CLK_INIT(gpu_pll1_pll.c), }, }; @@ -670,15 +670,15 @@ static struct clk_lookup msm_clocks_gfxcc_cobalt[] = { static void msm_gfxcc_hamster_fixup(void) { - gpu_pll0_pll.c.fmax[VDD_DIG_NOMINAL] = 1420000500; - gpu_pll1_pll.c.fmax[VDD_DIG_NOMINAL] = 1420000500; + gpu_pll0_pll.c.fmax[VDD_DIG_MIN] = 1420000500; + gpu_pll1_pll.c.fmax[VDD_DIG_MIN] = 1420000500; gfx3d_clk_src.freq_tbl = ftbl_gfx3d_clk_src_vq; } static void msm_gfxcc_cobalt_v2_fixup(void) { - gpu_pll0_pll.c.fmax[VDD_DIG_NOMINAL] = 1420000500; - gpu_pll1_pll.c.fmax[VDD_DIG_NOMINAL] = 1420000500; + gpu_pll0_pll.c.fmax[VDD_DIG_MIN] = 1420000500; + gpu_pll1_pll.c.fmax[VDD_DIG_MIN] = 1420000500; gfx3d_clk_src.freq_tbl = ftbl_gfx3d_clk_src_v2; } diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c index 598e52b54c99..9d605503520f 100644 --- a/drivers/clk/msm/clock-osm.c +++ b/drivers/clk/msm/clock-osm.c @@ -75,8 +75,7 @@ enum clk_osm_trace_packet_id { #define MEM_ACC_SEQ_REG_CFG_START(n) (SEQ_REG(12 + (n))) #define MEM_ACC_SEQ_CONST(n) (n) #define MEM_ACC_INSTR_COMP(n) (0x67 + ((n) * 0x40)) -#define MEM_ACC_SEQ_REG_VAL_START(n) \ - ((n) < 8 ? SEQ_REG(4 + (n)) : SEQ_REG(60 + (n) - 8)) +#define MEM_ACC_SEQ_REG_VAL_START(n) (SEQ_REG(60 + (n))) #define OSM_TABLE_SIZE 40 #define MAX_CLUSTER_CNT 2 @@ -125,6 +124,7 @@ enum clk_osm_trace_packet_id { #define SPM_CC_CTRL 0x1028 #define SPM_CC_HYSTERESIS 0x101C #define SPM_CORE_RET_MAPPING 0x1024 +#define CFG_DELAY_VAL_3 0x12C #define LLM_FREQ_VOTE_HYSTERESIS 0x102C #define LLM_VOLT_VOTE_HYSTERESIS 0x1030 @@ -184,11 +184,11 @@ enum clk_osm_trace_packet_id { #define MAX_INSTRUCTIONS 256 #define MAX_BR_INSTRUCTIONS 49 -#define MAX_MEM_ACC_LEVELS 4 +#define MAX_MEM_ACC_LEVELS 3 #define MAX_MEM_ACC_VAL_PER_LEVEL 3 #define MAX_MEM_ACC_VALUES (MAX_MEM_ACC_LEVELS * \ MAX_MEM_ACC_VAL_PER_LEVEL) -#define MEM_ACC_READ_MASK 0x7 +#define MEM_ACC_APM_READ_MASK 0xff #define TRACE_CTRL 0x1F38 #define TRACE_CTRL_EN_MASK BIT(0) @@ -203,6 +203,11 @@ enum clk_osm_trace_packet_id { #define PERIODIC_TRACE_MAX_NS 21474836475 #define PERIODIC_TRACE_DEFAULT_NS 1000000 +#define PLL_DD_USER_CTL_LO_ENABLE 0x0f04c408 +#define PLL_DD_USER_CTL_LO_DISABLE 0x1f04c41f +#define PLL_DD_D0_USER_CTL_LO 0x17916208 +#define PLL_DD_D1_USER_CTL_LO 0x17816208 + static void __iomem *virt_base; #define lmh_lite_clk_src_source_val 1 @@ -222,50 +227,48 @@ static void __iomem *virt_base; static u32 seq_instr[] = { 0xc2005000, 0x2c9e3b21, 0xc0ab2cdc, 0xc2882525, 0x359dc491, - 0x700a500b, 0x70005001, 0x390938c8, 0xcb44c833, 0xce56cd54, - 0x341336e0, 0xadba0000, 0x10004000, 0x70005001, 0x1000500c, - 0xc792c5a1, 0x501625e1, 0x3da335a2, 0x50170006, 0x50150006, - 0xafb9c633, 0xacb31000, 0xacb41000, 0x1000c422, 0x500baefc, - 0x5001700a, 0xaefd7000, 0x700b5010, 0x700c5012, 0xadb9ad41, - 0x181b0000, 0x500f500c, 0x34135011, 0x84b9181b, 0xbd808539, - 0x2ba40003, 0x0006a001, 0x10007105, 0x1000500e, 0x1c0a500c, - 0x3b181c01, 0x3b431c06, 0x10001c07, 0x39831c06, 0x500c1c07, - 0x1c0a1c02, 0x10000000, 0x70015002, 0x10000000, 0x50038103, - 0x50047002, 0x10007003, 0x39853b44, 0x50038104, 0x40037002, - 0x70095005, 0xb1c0a146, 0x238b0003, 0x10004005, 0x848b8308, - 0x1000850c, 0x848e830d, 0x1000850c, 0x3a4c5006, 0x3a8f39cd, - 0x40063ad0, 0x50071000, 0x2c127006, 0x4007a00f, 0x71050006, - 0x1000700d, 0x1c1aa964, 0x700d4007, 0x50071000, 0x1c167006, - 0x50125010, 0x40072411, 0x4007700d, 0xa00f1000, 0x0006a821, - 0x40077105, 0x500c700d, 0x1c1591ad, 0x5011500f, 0x10000000, - 0x500c2bd4, 0x0006a00f, 0x10007105, 0xa821a00f, 0x70050006, - 0x91ad500c, 0x500f1c15, 0x10005011, 0x1c162bce, 0x50125010, - 0xa82aa022, 0x71050006, 0x1c1591a6, 0x5011500f, 0x5014500c, - 0x0006a00f, 0x00007105, 0x91a41000, 0x22175013, 0x1c1aa963, - 0x22171000, 0x1c1aa963, 0x50081000, 0x40087007, 0x1c1aa963, - 0x70085009, 0x10004009, 0x850c848e, 0x0003b1c0, 0x400d2b99, - 0x500d1000, 0xabaf1000, 0x853184b0, 0x0003bb80, 0xa0371000, - 0x71050006, 0x85481000, 0xbf8084c3, 0x2ba80003, 0xbf8084c2, - 0x2ba70003, 0xbf8084c1, 0x2ba60003, 0x8ec71000, 0xc6dd8dc3, - 0x8c1625ec, 0x8d498c97, 0x8ec61c00, 0xc6dd8dc2, 0x8c1325ec, - 0x8d158c94, 0x8ec51c00, 0xc6dd8dc1, 0x8c1025ec, 0x8d128c91, - 0x8dc01c00, 0x182cc633, 0x84c08548, 0x0003bf80, 0x84c12ba9, - 0x0003bf80, 0x84c22baa, 0x0003bf80, 0x10002bab, 0x8dc08ec4, - 0x25ecc6dd, 0x8c948c13, 0x1c008d15, 0x8dc18ec5, 0x25ecc6dd, - 0x8c978c16, 0x1c008d49, 0x8dc28ec6, 0x25ecc6dd, 0x8ccb8c4a, - 0x1c008d4c, 0xc6338dc3, 0x1000af9b, 0xa759a79a, 0x1000a718, + 0x700a500b, 0x5001aefc, 0xaefd7000, 0x390938c8, 0xcb44c833, + 0xce56cd54, 0x341336e0, 0xa4baadba, 0xb480a493, 0x10004000, + 0x70005001, 0x1000500c, 0xc792c5a1, 0x501625e1, 0x3da335a2, + 0x50170006, 0x50150006, 0x1000c633, 0x1000acb3, 0xc422acb4, + 0xaefc1000, 0x700a500b, 0x70005001, 0x5010aefd, 0x5012700b, + 0xad41700c, 0x84e5adb9, 0xb3808566, 0x239b0003, 0x856484e3, + 0xb9800007, 0x2bad0003, 0xac3aa20b, 0x0003181b, 0x0003bb40, + 0xa30d239b, 0x500c181b, 0x5011500f, 0x181b3413, 0x853984b9, + 0x0003bd80, 0xa0012ba4, 0x72050803, 0x500e1000, 0x500c1000, + 0x1c011c0a, 0x3b181c06, 0x1c073b43, 0x1c061000, 0x1c073983, + 0x1c02500c, 0x10001c0a, 0x70015002, 0x81031000, 0x70025003, + 0x70035004, 0x3b441000, 0x81553985, 0x70025003, 0x50054003, + 0xa1467009, 0x0003b1c0, 0x4005238b, 0x835a1000, 0x855c84db, + 0x1000a51f, 0x84de835d, 0xa52c855c, 0x50061000, 0x39cd3a4c, + 0x3ad03a8f, 0x10004006, 0x70065007, 0xa00f2c12, 0x08034007, + 0xaefc7205, 0xaefd700d, 0xa9641000, 0x40071c1a, 0x700daefc, + 0x1000aefd, 0x70065007, 0x50101c16, 0x40075012, 0x700daefc, + 0x2411aefd, 0xa8211000, 0x0803a00f, 0x500c7005, 0x1c1591e0, + 0x500f5014, 0x10005011, 0x500c2bd4, 0x0803a00f, 0x10007205, + 0xa00fa9d1, 0x0803a821, 0xa9d07005, 0x91e0500c, 0x500f1c15, + 0x10005011, 0x1c162bce, 0x50125010, 0xa022a82a, 0x70050803, + 0x1c1591df, 0x5011500f, 0x5014500c, 0x0803a00f, 0x10007205, + 0x501391a4, 0x22172217, 0x70075008, 0xa9634008, 0x1c1a0006, + 0x70085009, 0x10004009, 0x00008ed9, 0x3e05c8dd, 0x1c033604, + 0xabaf1000, 0x856284e1, 0x0003bb80, 0x1000239f, 0x0803a037, + 0x10007205, 0x8dc61000, 0x38a71c2a, 0x1c2a8dc4, 0x100038a6, + 0x1c2a8dc5, 0x8dc73867, 0x38681c2a, 0x8c491000, 0x8d4b8cca, + 0x10001c00, 0x8ccd8c4c, 0x1c008d4e, 0x8c4f1000, 0x8d518cd0, + 0x10001c00, 0xa759a79a, 0x1000a718, 0xbf80af9b, 0x00001000, }; static u32 seq_br_instr[] = { - 0x28c, 0x1e6, 0x238, 0xd0, 0xec, - 0xf4, 0xbc, 0xc4, 0x9c, 0xac, - 0xfc, 0xe2, 0x154, 0x174, 0x17c, - 0x10a, 0x126, 0x13a, 0x11c, 0x98, - 0x160, 0x1a6, 0x19a, 0x1ae, 0x1c0, - 0x1ce, 0x1d2, 0x30, 0x60, 0x86, - 0x7c, 0x1d8, 0x34, 0x3c, 0x56, - 0x5a, 0x1de, 0x2e, 0x222, 0x212, - 0x202, 0x254, 0x264, 0x274, 0x288, + 0x248, 0x20e, 0x21c, 0xf6, 0x112, + 0x11c, 0xe4, 0xea, 0xc6, 0xd6, + 0x126, 0x108, 0x184, 0x1a8, 0x1b0, + 0x134, 0x158, 0x16e, 0x14a, 0xc2, + 0x190, 0x1d2, 0x1cc, 0x1d4, 0x1e8, + 0x0, 0x1f6, 0x32, 0x66, 0xb0, + 0xa6, 0x1fc, 0x3c, 0x44, 0x5c, + 0x60, 0x204, 0x30, 0x22a, 0x234, + 0x23e, 0x0, 0x250, 0x0, 0x0, 0x9a, + 0x20c, }; DEFINE_EXT_CLK(xo_ao, NULL); @@ -298,6 +301,7 @@ struct clk_osm { u32 cluster_num; u32 irq; u32 apm_crossover_vc; + u32 apm_threshold_vc; u32 cycle_counter_reads; u32 cycle_counter_delay; u32 cycle_counter_factor; @@ -551,8 +555,8 @@ static void clk_osm_print_osm_table(struct clk_osm *c) lval, table[i].spare_data); } - pr_debug("APM crossover corner: %d\n", - c->apm_crossover_vc); + pr_debug("APM threshold corner=%d, crossover corner=%d\n", + c->apm_threshold_vc, c->apm_crossover_vc); } static int clk_osm_get_lut(struct platform_device *pdev, @@ -1116,10 +1120,21 @@ exit: static int clk_osm_resolve_crossover_corners(struct clk_osm *c, struct platform_device *pdev) { + struct regulator *regulator = c->vdd_reg; struct dev_pm_opp *opp; unsigned long freq = 0; - int vc, rc = 0; + int vc, i, threshold, rc = 0; + u32 corner_volt, data; + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,apm-threshold-voltage", + &threshold); + if (rc) { + pr_info("qcom,apm-threshold-voltage property not specified\n"); + return rc; + } + + /* Determine crossover virtual corner */ rcu_read_lock(); opp = dev_pm_opp_find_freq_exact(&c->vdd_dev->dev, freq, true); if (IS_ERR(opp)) { @@ -1138,6 +1153,48 @@ static int clk_osm_resolve_crossover_corners(struct clk_osm *c, vc--; c->apm_crossover_vc = vc; + /* Determine threshold virtual corner */ + for (i = 0; i < OSM_TABLE_SIZE; i++) { + freq = c->osm_table[i].frequency; + /* + * Only frequencies that are supported across all configurations + * are present in the OPP table associated with the regulator + * device. + */ + data = (c->osm_table[i].freq_data & GENMASK(18, 16)) >> 16; + if (data != MAX_CONFIG) + continue; + + rcu_read_lock(); + opp = dev_pm_opp_find_freq_exact(&c->vdd_dev->dev, freq, true); + if (IS_ERR(opp)) { + rc = PTR_ERR(opp); + if (rc == -ERANGE) + pr_err("Frequency %lu not found\n", freq); + goto exit; + } + + vc = dev_pm_opp_get_voltage(opp); + if (!vc) { + pr_err("No virtual corner found for frequency %lu\n", + freq); + rc = -ERANGE; + goto exit; + } + + rcu_read_unlock(); + + corner_volt = regulator_list_corner_voltage(regulator, vc); + + /* CPR virtual corners are zero-based numbered */ + vc--; + + if (corner_volt >= threshold) { + c->apm_threshold_vc = vc; + break; + } + } + return 0; exit: rcu_read_unlock(); @@ -1413,55 +1470,77 @@ static void clk_osm_program_apm_regs(struct clk_osm *c) */ clk_osm_write_reg(c, c->apm_mode_ctl, SEQ_REG(2)); - /* Program mode value to switch APM from VDD_APCC to VDD_MX */ - clk_osm_write_reg(c, APM_MX_MODE, SEQ_REG(22)); - - /* Program mode value to switch APM from VDD_MX to VDD_APCC */ - clk_osm_write_reg(c, APM_APC_MODE, SEQ_REG(25)); - /* Program address of controller status register */ clk_osm_write_reg(c, c->apm_ctrl_status, SEQ_REG(3)); - /* Program mask used to determine status of APM power supply switch */ - clk_osm_write_reg(c, APM_MODE_SWITCH_MASK, SEQ_REG(24)); + /* Program mode value to switch APM from VDD_APCC to VDD_MX */ + clk_osm_write_reg(c, APM_MX_MODE, SEQ_REG(77)); /* Program value used to determine current APM power supply is VDD_MX */ - clk_osm_write_reg(c, APM_MX_MODE_VAL, SEQ_REG(23)); + clk_osm_write_reg(c, APM_MX_MODE_VAL, SEQ_REG(78)); + + /* Program mask used to determine status of APM power supply switch */ + clk_osm_write_reg(c, APM_MODE_SWITCH_MASK, SEQ_REG(79)); + + /* Program mode value to switch APM from VDD_MX to VDD_APCC */ + clk_osm_write_reg(c, APM_APC_MODE, SEQ_REG(80)); /* * Program value used to determine current APM power supply * is VDD_APCC */ - clk_osm_write_reg(c, APM_APC_MODE_VAL, SEQ_REG(26)); + clk_osm_write_reg(c, APM_APC_MODE_VAL, SEQ_REG(81)); } static void clk_osm_program_mem_acc_regs(struct clk_osm *c) { - int i; + int i, curr_level, j = 0; + int mem_acc_level_map[MAX_MEM_ACC_LEVELS] = {0, 0, 0}; - if (!c->secure_init) - return; + curr_level = c->osm_table[0].spare_data; + for (i = 0; i < c->num_entries; i++) { + if (curr_level == MAX_MEM_ACC_LEVELS) + break; - clk_osm_write_reg(c, c->pbases[OSM_BASE] + SEQ_REG(50), - SEQ_REG(49)); - clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(1), SEQ_REG(50)); - clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(1), SEQ_REG(51)); - clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(2), SEQ_REG(52)); - clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(3), SEQ_REG(53)); - clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(4), SEQ_REG(54)); - clk_osm_write_reg(c, MEM_ACC_INSTR_COMP(0), SEQ_REG(55)); - clk_osm_write_reg(c, MEM_ACC_INSTR_COMP(1), SEQ_REG(56)); - clk_osm_write_reg(c, MEM_ACC_INSTR_COMP(2), SEQ_REG(57)); - clk_osm_write_reg(c, MEM_ACC_INSTR_COMP(3), SEQ_REG(58)); - clk_osm_write_reg(c, MEM_ACC_READ_MASK, SEQ_REG(59)); - - for (i = 0; i < MAX_MEM_ACC_VALUES; i++) - clk_osm_write_reg(c, c->apcs_mem_acc_val[i], - MEM_ACC_SEQ_REG_VAL_START(i)); - - for (i = 0; i < MAX_MEM_ACC_VAL_PER_LEVEL; i++) - clk_osm_write_reg(c, c->apcs_mem_acc_cfg[i], - MEM_ACC_SEQ_REG_CFG_START(i)); + if (c->osm_table[i].spare_data != curr_level) { + mem_acc_level_map[j++] = i - 1; + curr_level = c->osm_table[i].spare_data; + } + } + + if (c->secure_init) { + clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(1), SEQ_REG(51)); + clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(2), SEQ_REG(52)); + clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(3), SEQ_REG(53)); + clk_osm_write_reg(c, MEM_ACC_SEQ_CONST(4), SEQ_REG(54)); + clk_osm_write_reg(c, MEM_ACC_APM_READ_MASK, SEQ_REG(59)); + clk_osm_write_reg(c, mem_acc_level_map[0], SEQ_REG(55)); + clk_osm_write_reg(c, mem_acc_level_map[0] + 1, SEQ_REG(56)); + clk_osm_write_reg(c, mem_acc_level_map[1], SEQ_REG(57)); + clk_osm_write_reg(c, mem_acc_level_map[1] + 1, SEQ_REG(58)); + clk_osm_write_reg(c, c->pbases[OSM_BASE] + SEQ_REG(28), + SEQ_REG(49)); + + for (i = 0; i < MAX_MEM_ACC_VALUES; i++) + clk_osm_write_reg(c, c->apcs_mem_acc_val[i], + MEM_ACC_SEQ_REG_VAL_START(i)); + + for (i = 0; i < MAX_MEM_ACC_VAL_PER_LEVEL; i++) + clk_osm_write_reg(c, c->apcs_mem_acc_cfg[i], + MEM_ACC_SEQ_REG_CFG_START(i)); + } else { + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(55), + mem_acc_level_map[0]); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(56), + mem_acc_level_map[0] + 1); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(57), + mem_acc_level_map[1]); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(58), + mem_acc_level_map[1] + 1); + /* SEQ_REG(49) = SEQ_REG(28) init by TZ */ + } + + return; } void clk_osm_setup_sequencer(struct clk_osm *c) @@ -1500,10 +1579,12 @@ static void clk_osm_setup_cycle_counters(struct clk_osm *c) static void clk_osm_setup_osm_was(struct clk_osm *c) { + u32 cc_hyst; u32 val; val = clk_osm_read_reg(c, PDN_FSM_CTRL_REG); val |= IGNORE_PLL_LOCK_MASK; + cc_hyst = clk_osm_read_reg(c, SPM_CC_HYSTERESIS); if (c->secure_init) { clk_osm_write_reg(c, val, SEQ_REG(47)); @@ -1518,10 +1599,51 @@ static void clk_osm_setup_osm_was(struct clk_osm *c) clk_osm_write_reg(c, 0x0, SEQ_REG(45)); clk_osm_write_reg(c, c->pbases[OSM_BASE] + PDN_FSM_CTRL_REG, SEQ_REG(46)); + + /* C2D/C3 + D2D workaround */ + clk_osm_write_reg(c, c->pbases[OSM_BASE] + SPM_CC_HYSTERESIS, + SEQ_REG(6)); + clk_osm_write_reg(c, cc_hyst, SEQ_REG(7)); + + /* Droop detector PLL lock detect workaround */ + clk_osm_write_reg(c, PLL_DD_USER_CTL_LO_ENABLE, SEQ_REG(4)); + clk_osm_write_reg(c, PLL_DD_USER_CTL_LO_DISABLE, SEQ_REG(5)); + clk_osm_write_reg(c, c->cluster_num == 0 ? PLL_DD_D0_USER_CTL_LO + : PLL_DD_D1_USER_CTL_LO, SEQ_REG(21)); + + /* PLL lock detect and HMSS AHB clock workaround */ + clk_osm_write_reg(c, 0x640, CFG_DELAY_VAL_3); + + /* DxFSM workaround */ + clk_osm_write_reg(c, c->cluster_num == 0 ? 0x17911200 : + 0x17811200, SEQ_REG(22)); + clk_osm_write_reg(c, 0x80800, SEQ_REG(23)); + clk_osm_write_reg(c, 0x179D1100, SEQ_REG(24)); + clk_osm_write_reg(c, 0x11f, SEQ_REG(25)); + clk_osm_write_reg(c, c->cluster_num == 0 ? 0x17912000 : + 0x17811290, SEQ_REG(26)); + clk_osm_write_reg(c, c->cluster_num == 0 ? 0x17911290 : + 0x17811290, SEQ_REG(20)); + clk_osm_write_reg(c, c->cluster_num == 0 ? 0x17811290 : + 0x17911290, SEQ_REG(32)); + clk_osm_write_reg(c, 0x179D4020, SEQ_REG(35)); + clk_osm_write_reg(c, 0x11f, SEQ_REG(25)); + clk_osm_write_reg(c, 0xa, SEQ_REG(86)); + clk_osm_write_reg(c, 0xe, SEQ_REG(87)); + clk_osm_write_reg(c, 0x00400000, SEQ_REG(88)); + clk_osm_write_reg(c, 0x00700000, SEQ_REG(89)); } else { scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(47), val); val &= ~IGNORE_PLL_LOCK_MASK; scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(48), val); + + /* C2D/C3 + D2D workaround */ + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(7), + cc_hyst); + + /* Droop detector PLL lock detect workaround */ + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(4), + PLL_DD_USER_CTL_LO_ENABLE); } if (c->cluster_num == 0) { @@ -1671,18 +1793,15 @@ static void clk_osm_do_additional_setup(struct clk_osm *c, /* APM Programming */ clk_osm_program_apm_regs(c); - /* MEM-ACC Programming */ - clk_osm_program_mem_acc_regs(c); - /* GFMUX Programming */ clk_osm_write_reg(c, c->apcs_cfg_rcgr, SEQ_REG(16)); - clk_osm_write_reg(c, GPLL_SEL, SEQ_REG(17)); - clk_osm_write_reg(c, PLL_EARLY_SEL, SEQ_REG(20)); - clk_osm_write_reg(c, PLL_MAIN_SEL, SEQ_REG(32)); clk_osm_write_reg(c, c->apcs_cmd_rcgr, SEQ_REG(33)); clk_osm_write_reg(c, RCG_UPDATE, SEQ_REG(34)); - clk_osm_write_reg(c, RCG_UPDATE_SUCCESS, SEQ_REG(35)); - clk_osm_write_reg(c, RCG_UPDATE, SEQ_REG(36)); + clk_osm_write_reg(c, GPLL_SEL, SEQ_REG(17)); + clk_osm_write_reg(c, PLL_EARLY_SEL, SEQ_REG(82)); + clk_osm_write_reg(c, PLL_MAIN_SEL, SEQ_REG(83)); + clk_osm_write_reg(c, RCG_UPDATE_SUCCESS, SEQ_REG(84)); + clk_osm_write_reg(c, RCG_UPDATE, SEQ_REG(85)); pr_debug("seq_size: %lu, seqbr_size: %lu\n", ARRAY_SIZE(seq_instr), ARRAY_SIZE(seq_br_instr)); @@ -1693,17 +1812,43 @@ static void clk_osm_do_additional_setup(struct clk_osm *c, static void clk_osm_apm_vc_setup(struct clk_osm *c) { /* - * APM crossover virtual corner at which the switch - * from APC to MX and vice-versa should take place. + * APM crossover virtual corner corresponds to switching + * voltage during APM transition. APM threshold virtual + * corner is the first corner which requires switch + * sequence of APM from MX to APC. */ if (c->secure_init) { - clk_osm_write_reg(c, c->apm_crossover_vc, SEQ_REG(1)); + clk_osm_write_reg(c, c->apm_threshold_vc, SEQ_REG(1)); + clk_osm_write_reg(c, c->apm_crossover_vc, SEQ_REG(72)); + clk_osm_write_reg(c, c->pbases[OSM_BASE] + SEQ_REG(1), + SEQ_REG(8)); + clk_osm_write_reg(c, c->apm_threshold_vc, + SEQ_REG(15)); + clk_osm_write_reg(c, c->apm_threshold_vc != 0 ? + c->apm_threshold_vc - 1 : 0xff, + SEQ_REG(31)); + clk_osm_write_reg(c, 0x3b | c->apm_threshold_vc << 6, + SEQ_REG(73)); + clk_osm_write_reg(c, 0x39 | c->apm_threshold_vc << 6, + SEQ_REG(76)); /* Ensure writes complete before returning */ mb(); } else { scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(1), + c->apm_threshold_vc); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(72), c->apm_crossover_vc); + /* SEQ_REG(8) = address of SEQ_REG(1) init by TZ */ + clk_osm_write_reg(c, c->apm_threshold_vc, + SEQ_REG(15)); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(31), + c->apm_threshold_vc != 0 ? + c->apm_threshold_vc - 1 : 0xff); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(73), + 0x3b | c->apm_threshold_vc << 6); + scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(76), + 0x39 | c->apm_threshold_vc << 6); } } @@ -2412,6 +2557,10 @@ static int cpu_clock_osm_driver_probe(struct platform_device *pdev) clk_osm_do_additional_setup(&pwrcl_clk, pdev); clk_osm_do_additional_setup(&perfcl_clk, pdev); + /* MEM-ACC Programming */ + clk_osm_program_mem_acc_regs(&pwrcl_clk); + clk_osm_program_mem_acc_regs(&perfcl_clk); + /* Program APM crossover corners */ clk_osm_apm_vc_setup(&pwrcl_clk); clk_osm_apm_vc_setup(&perfcl_clk); diff --git a/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c b/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c index eac501e28d7b..d5d55a58bf7f 100644 --- a/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c +++ b/drivers/clk/msm/mdss/mdss-hdmi-pll-cobalt.c @@ -82,7 +82,7 @@ #define HDMI_HZ_TO_MHZ 1000000 #define HDMI_REF_CLOCK_MHZ 19.2 #define HDMI_REF_CLOCK_HZ (HDMI_REF_CLOCK_MHZ * 1000000) -#define HDMI_VCO_MIN_RATE_HZ 30000000 +#define HDMI_VCO_MIN_RATE_HZ 25000000 #define HDMI_VCO_MAX_RATE_HZ 600000000 struct cobalt_reg_cfg { diff --git a/drivers/clk/msm/vdd-level-cobalt.h b/drivers/clk/msm/vdd-level-cobalt.h index 2cb40afafe3f..f847a4104d4d 100644 --- a/drivers/clk/msm/vdd-level-cobalt.h +++ b/drivers/clk/msm/vdd-level-cobalt.h @@ -50,11 +50,19 @@ }, \ .num_fmax = VDD_DIG_NUM -#define VDD_DIG_FMAX_MAP2_AO(l1, f1, l2, f2) \ +#define VDD_DIG_FMAX_MAP1_AO(l1, f1) \ + .vdd_class = &vdd_dig_ao, \ + .fmax = (unsigned long[VDD_DIG_NUM]) { \ + [VDD_DIG_##l1] = (f1), \ + }, \ + .num_fmax = VDD_DIG_NUM + +#define VDD_DIG_FMAX_MAP3_AO(l1, f1, l2, f2, l3, f3) \ .vdd_class = &vdd_dig_ao, \ .fmax = (unsigned long[VDD_DIG_NUM]) { \ [VDD_DIG_##l1] = (f1), \ [VDD_DIG_##l2] = (f2), \ + [VDD_DIG_##l3] = (f3), \ }, \ .num_fmax = VDD_DIG_NUM diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 94419695cd2e..dc1b66f84af2 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -12,6 +12,7 @@ clk-qcom-y += clk-regmap-mux.o clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o clk-qcom-y += clk-hfpll.o clk-qcom-y += reset.o +clk-qcom-y += clk-dummy.o clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o diff --git a/drivers/clk/qcom/clk-dummy.c b/drivers/clk/qcom/clk-dummy.c new file mode 100644 index 000000000000..3205fbc6b8ba --- /dev/null +++ b/drivers/clk/qcom/clk-dummy.c @@ -0,0 +1,110 @@ +/* + * 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. + */ + +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +struct clk_dummy { + struct clk_hw hw; + unsigned long rrate; +}; + +#define to_clk_dummy(_hw) container_of(_hw, struct clk_dummy, hw) + +static int dummy_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_dummy *dummy = to_clk_dummy(hw); + + dummy->rrate = rate; + + pr_debug("set rate: %lu\n", rate); + + return 0; +} + +static long dummy_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + return rate; +} + +static unsigned long dummy_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_dummy *dummy = to_clk_dummy(hw); + + pr_debug("clock rate: %lu\n", dummy->rrate); + + return dummy->rrate; +} + +struct clk_ops clk_dummy_ops = { + .set_rate = dummy_clk_set_rate, + .round_rate = dummy_clk_round_rate, + .recalc_rate = dummy_clk_recalc_rate, +}; +EXPORT_SYMBOL_GPL(clk_dummy_ops); + +/** + * clk_register_dummy - register dummy clock with the + * clock framework + * @dev: device that is registering this clock + * @name: name of this clock + * @flags: framework-specific flags + */ +static struct clk *clk_register_dummy(struct device *dev, const char *name, + unsigned long flags) +{ + struct clk_dummy *dummy; + struct clk *clk; + struct clk_init_data init = {}; + + /* allocate dummy clock */ + dummy = kzalloc(sizeof(*dummy), GFP_KERNEL); + if (!dummy) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_dummy_ops; + init.flags = flags | CLK_IS_BASIC; + init.num_parents = 0; + dummy->hw.init = &init; + + /* register the clock */ + clk = clk_register(dev, &dummy->hw); + if (IS_ERR(clk)) + kfree(dummy); + + return clk; +} + +/** + * of_dummy_clk_setup() - Setup function for simple fixed rate clock + */ +static void of_dummy_clk_setup(struct device_node *node) +{ + struct clk *clk; + const char *clk_name = "dummy_clk"; + + of_property_read_string(node, "clock-output-names", &clk_name); + + clk = clk_register_dummy(NULL, clk_name, 0); + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + pr_info("%s: Dummy clock registered\n", clk_name); +} +CLK_OF_DECLARE(dummy_clk, "qcom,dummycc", of_dummy_clk_setup); diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index ae9bdeb21f29..10cabca921be 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2014, 2016, 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 @@ -48,5 +48,5 @@ extern int qcom_cc_really_probe(struct platform_device *pdev, struct regmap *regmap); extern int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc); - +extern struct clk_ops clk_dummy_ops; #endif diff --git a/drivers/clk/qcom/mdss/Kconfig b/drivers/clk/qcom/mdss/Kconfig new file mode 100644 index 000000000000..229780e45bb8 --- /dev/null +++ b/drivers/clk/qcom/mdss/Kconfig @@ -0,0 +1,6 @@ +config MSM_MDSS_PLL + bool "MDSS pll programming" + ---help--- + It provides support for DSI, eDP and HDMI interface pll programming on MDSS + hardware. It also handles the pll specific resources and turn them on/off when + mdss pll client tries to enable/disable pll clocks. diff --git a/drivers/clk/qcom/mdss/Makefile b/drivers/clk/qcom/mdss/Makefile new file mode 100644 index 000000000000..75891dc10dda --- /dev/null +++ b/drivers/clk/qcom/mdss/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-pll-util.o +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-pll.o +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-8996.o +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-dsi-pll-8996-util.o +obj-$(CONFIG_MSM_MDSS_PLL) += mdss-hdmi-pll-8996.o diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll-8996-util.c b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996-util.c new file mode 100644 index 000000000000..6d2694d5a2e9 --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996-util.c @@ -0,0 +1,1137 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/iopoll.h> +#include <linux/delay.h> +#include <linux/clk/msm-clock-generic.h> + +#include "mdss-pll.h" +#include "mdss-dsi-pll.h" +#include "mdss-dsi-pll-8996.h" + +#define DSI_PLL_POLL_MAX_READS 15 +#define DSI_PLL_POLL_TIMEOUT_US 1000 +#define MSM8996_DSI_PLL_REVISION_2 2 + +#define CEIL(x, y) (((x) + ((y)-1)) / (y)) + +int set_mdss_byte_mux_sel_8996(struct mux_clk *clk, int sel) +{ + return 0; +} + +int get_mdss_byte_mux_sel_8996(struct mux_clk *clk) +{ + return 0; +} + +int set_mdss_pixel_mux_sel_8996(struct mux_clk *clk, int sel) +{ + return 0; +} + +int get_mdss_pixel_mux_sel_8996(struct mux_clk *clk) +{ + return 0; +} + +static int mdss_pll_read_stored_trim_codes( + struct mdss_pll_resources *dsi_pll_res, s64 vco_clk_rate) +{ + 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; + + dsi_pll_res->cache_pll_trim_codes[0] = + codes_info->pll_codes.pll_codes_1; + dsi_pll_res->cache_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", + dsi_pll_res->cache_pll_trim_codes[0], + dsi_pll_res->cache_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; + struct dsi_pll_db *pdb; + struct dsi_pll_output *pout; + int rc; + u32 n1div = 0; + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + pdb = (struct dsi_pll_db *)pll->priv; + pout = &pdb->out; + + /* + * vco rate = bit_clk * postdiv * n1div + * vco range from 1300 to 2600 Mhz + * postdiv = 1 + * n1div = 1 to 15 + * n1div = roundup(1300Mhz / bit_clk) + * support bit_clk above 86.67Mhz + */ + + /* this is for vco/bit clock */ + pout->pll_postdiv = 1; /* fixed, divided by 1 */ + pout->pll_n1div = div; + + n1div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_CMN_CLK_CFG0); + n1div &= ~0xf; + n1div |= (div & 0xf); + MDSS_PLL_REG_W(pll->pll_base, DSIPHY_CMN_CLK_CFG0, n1div); + /* ensure n1 divider is programed */ + wmb(); + pr_debug("ndx=%d div=%d postdiv=%x n1div=%x\n", + pll->index, div, pout->pll_postdiv, pout->pll_n1div); + + mdss_pll_resource_enable(pll, false); + + return 0; +} + +int post_n1_div_get_div(struct div_clk *clk) +{ + u32 div; + int rc; + struct mdss_pll_resources *pll = clk->priv; + + if (is_gdsc_disabled(pll)) + return 0; + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + /* + * postdiv = 1/2/4/8 + * n1div = 1 - 15 + * fot the time being, assume postdiv = 1 + */ + + div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_CMN_CLK_CFG0); + div &= 0xF; + pr_debug("n1 div = %d\n", div); + + mdss_pll_resource_enable(pll, false); + + return div; +} + +int n2_div_set_div(struct div_clk *clk, int div) +{ + int rc; + u32 n2div; + struct mdss_pll_resources *pll = clk->priv; + struct dsi_pll_db *pdb; + struct dsi_pll_output *pout; + struct mdss_pll_resources *slave; + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll resources\n"); + return rc; + } + + pdb = (struct dsi_pll_db *)pll->priv; + pout = &pdb->out; + + /* this is for pixel clock */ + n2div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_CMN_CLK_CFG0); + n2div &= ~0xf0; /* bits 4 to 7 */ + n2div |= (div << 4); + MDSS_PLL_REG_W(pll->pll_base, DSIPHY_CMN_CLK_CFG0, n2div); + + /* commit slave if split display is enabled */ + slave = pll->slave; + if (slave) + MDSS_PLL_REG_W(slave->pll_base, DSIPHY_CMN_CLK_CFG0, n2div); + + pout->pll_n2div = div; + + /* set dsiclk_sel=1 so that n2div *= 2 */ + MDSS_PLL_REG_W(pll->pll_base, DSIPHY_CMN_CLK_CFG1, 1); + pr_debug("ndx=%d div=%d n2div=%x\n", pll->index, div, n2div); + + mdss_pll_resource_enable(pll, false); + + return rc; +} + +int shadow_n2_div_set_div(struct div_clk *clk, int div) +{ + struct mdss_pll_resources *pll = clk->priv; + struct dsi_pll_db *pdb; + struct dsi_pll_output *pout; + u32 data; + + pdb = pll->priv; + pout = &pdb->out; + + pout->pll_n2div = div; + + data = (pout->pll_n1div | (pout->pll_n2div << 4)); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL19, + DSIPHY_CMN_CLK_CFG0, DSIPHY_CMN_CLK_CFG1, + data, 1); + return 0; +} + +int n2_div_get_div(struct div_clk *clk) +{ + int rc; + u32 n2div; + struct mdss_pll_resources *pll = clk->priv; + + if (is_gdsc_disabled(pll)) + return 0; + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll=%d resources\n", + pll->index); + return rc; + } + + n2div = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_CMN_CLK_CFG0); + n2div >>= 4; + n2div &= 0x0f; + + mdss_pll_resource_enable(pll, false); + + pr_debug("ndx=%d div=%d\n", pll->index, n2div); + + return n2div; +} + +static bool pll_is_pll_locked_8996(struct mdss_pll_resources *pll) +{ + u32 status; + bool pll_locked; + + /* poll for PLL ready status */ + if (readl_poll_timeout_atomic((pll->pll_base + + DSIPHY_PLL_RESET_SM_READY_STATUS), + status, + ((status & BIT(5)) > 0), + DSI_PLL_POLL_MAX_READS, + DSI_PLL_POLL_TIMEOUT_US)) { + pr_err("DSI PLL ndx=%d status=%x failed to Lock\n", + pll->index, status); + pll_locked = false; + } else if (readl_poll_timeout_atomic((pll->pll_base + + DSIPHY_PLL_RESET_SM_READY_STATUS), + status, + ((status & BIT(0)) > 0), + DSI_PLL_POLL_MAX_READS, + DSI_PLL_POLL_TIMEOUT_US)) { + pr_err("DSI PLL ndx=%d status=%x PLl not ready\n", + pll->index, status); + pll_locked = false; + } else { + pll_locked = true; + } + + return pll_locked; +} + +static void dsi_pll_start_8996(void __iomem *pll_base) +{ + pr_debug("start PLL at base=%p\n", pll_base); + + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VREF_CFG1, 0x10); + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_PLL_CNTRL, 1); +} + +static void dsi_pll_stop_8996(void __iomem *pll_base) +{ + pr_debug("stop PLL at base=%p\n", pll_base); + + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_PLL_CNTRL, 0); +} + +int dsi_pll_enable_seq_8996(struct mdss_pll_resources *pll) +{ + int rc = 0; + + if (!pll) { + pr_err("Invalid PLL resources\n"); + return -EINVAL; + } + + dsi_pll_start_8996(pll->pll_base); + + /* + * both DSIPHY_PLL_CLKBUFLR_EN and DSIPHY_CMN_GLBL_TEST_CTRL + * enabled at mdss_dsi_8996_phy_config() + */ + + if (!pll_is_pll_locked_8996(pll)) { + pr_err("DSI PLL ndx=%d lock failed\n", pll->index); + rc = -EINVAL; + goto init_lock_err; + } + + pr_debug("DSI PLL ndx=%d Lock success\n", pll->index); + +init_lock_err: + return rc; +} + +static int dsi_pll_enable(struct clk *c) +{ + int i, rc = 0; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *pll = vco->priv; + + /* Try all enable sequences until one succeeds */ + for (i = 0; i < vco->pll_en_seq_cnt; i++) { + rc = vco->pll_enable_seqs[i](pll); + pr_debug("DSI PLL %s after sequence #%d\n", + rc ? "unlocked" : "locked", i + 1); + if (!rc) + break; + } + + if (rc) + pr_err("ndx=%d DSI PLL failed to lock\n", pll->index); + else + pll->pll_on = true; + + return rc; +} + +static void dsi_pll_disable(struct clk *c) +{ + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *pll = vco->priv; + struct mdss_pll_resources *slave; + + if (!pll->pll_on && + mdss_pll_resource_enable(pll, true)) { + pr_err("Failed to enable mdss dsi pll=%d\n", pll->index); + return; + } + + pll->handoff_resources = false; + slave = pll->slave; + + dsi_pll_stop_8996(pll->pll_base); + + mdss_pll_resource_enable(pll, false); + + pll->pll_on = false; + + pr_debug("DSI PLL ndx=%d Disabled\n", pll->index); +} + +static void mdss_dsi_pll_8996_input_init(struct mdss_pll_resources *pll, + struct dsi_pll_db *pdb) +{ + pdb->in.fref = 19200000; /* 19.2 Mhz*/ + pdb->in.fdata = 0; /* bit clock rate */ + pdb->in.dsiclk_sel = 1; /* 1, reg: 0x0014 */ + pdb->in.ssc_en = pll->ssc_en; /* 1, reg: 0x0494, bit 0 */ + pdb->in.ldo_en = 0; /* 0, reg: 0x004c, bit 0 */ + + /* fixed input */ + pdb->in.refclk_dbler_en = 0; /* 0, reg: 0x04c0, bit 1 */ + pdb->in.vco_measure_time = 5; /* 5, unknown */ + pdb->in.kvco_measure_time = 5; /* 5, unknown */ + pdb->in.bandgap_timer = 4; /* 4, reg: 0x0430, bit 3 - 5 */ + pdb->in.pll_wakeup_timer = 5; /* 5, reg: 0x043c, bit 0 - 2 */ + pdb->in.plllock_cnt = 1; /* 1, reg: 0x0488, bit 1 - 2 */ + pdb->in.plllock_rng = 0; /* 0, reg: 0x0488, bit 3 - 4 */ + pdb->in.ssc_center = pll->ssc_center;/* 0, reg: 0x0494, bit 1 */ + pdb->in.ssc_adj_period = 37; /* 37, reg: 0x498, bit 0 - 9 */ + pdb->in.ssc_spread = pll->ssc_ppm / 1000; + pdb->in.ssc_freq = pll->ssc_freq; + + pdb->in.pll_ie_trim = 4; /* 4, reg: 0x0400 */ + pdb->in.pll_ip_trim = 4; /* 4, reg: 0x0404 */ + pdb->in.pll_cpcset_cur = 1; /* 1, reg: 0x04f0, bit 0 - 2 */ + pdb->in.pll_cpmset_cur = 1; /* 1, reg: 0x04f0, bit 3 - 5 */ + pdb->in.pll_icpmset = 4; /* 4, reg: 0x04fc, bit 3 - 5 */ + pdb->in.pll_icpcset = 4; /* 4, reg: 0x04fc, bit 0 - 2 */ + pdb->in.pll_icpmset_p = 0; /* 0, reg: 0x04f4, bit 0 - 2 */ + pdb->in.pll_icpmset_m = 0; /* 0, reg: 0x04f4, bit 3 - 5 */ + pdb->in.pll_icpcset_p = 0; /* 0, reg: 0x04f8, bit 0 - 2 */ + pdb->in.pll_icpcset_m = 0; /* 0, reg: 0x04f8, bit 3 - 5 */ + pdb->in.pll_lpf_res1 = 3; /* 3, reg: 0x0504, bit 0 - 3 */ + pdb->in.pll_lpf_cap1 = 11; /* 11, reg: 0x0500, bit 0 - 3 */ + pdb->in.pll_lpf_cap2 = 1; /* 1, reg: 0x0500, bit 4 - 7 */ + pdb->in.pll_iptat_trim = 7; + pdb->in.pll_c3ctrl = 2; /* 2 */ + pdb->in.pll_r3ctrl = 1; /* 1 */ +} + +static void pll_8996_ssc_calc(struct mdss_pll_resources *pll, + struct dsi_pll_db *pdb) +{ + u32 period, ssc_period; + u32 ref, rem; + s64 step_size; + + pr_debug("%s: vco=%lld ref=%lld\n", __func__, + pll->vco_current_rate, pll->vco_ref_clk_rate); + + ssc_period = pdb->in.ssc_freq / 500; + period = (unsigned long)pll->vco_ref_clk_rate / 1000; + ssc_period = CEIL(period, ssc_period); + ssc_period -= 1; + pdb->out.ssc_period = ssc_period; + + pr_debug("%s: ssc, freq=%d spread=%d period=%d\n", __func__, + pdb->in.ssc_freq, pdb->in.ssc_spread, pdb->out.ssc_period); + + step_size = (u32)pll->vco_current_rate; + ref = pll->vco_ref_clk_rate; + ref /= 1000; + step_size = div_s64(step_size, ref); + step_size <<= 20; + step_size = div_s64(step_size, 1000); + step_size *= pdb->in.ssc_spread; + step_size = div_s64(step_size, 1000); + step_size *= (pdb->in.ssc_adj_period + 1); + + rem = 0; + step_size = div_s64_rem(step_size, ssc_period + 1, &rem); + if (rem) + step_size++; + + pr_debug("%s: step_size=%lld\n", __func__, step_size); + + step_size &= 0x0ffff; /* take lower 16 bits */ + + pdb->out.ssc_step_size = step_size; +} + +static void pll_8996_dec_frac_calc(struct mdss_pll_resources *pll, + struct dsi_pll_db *pdb) +{ + struct dsi_pll_input *pin = &pdb->in; + struct dsi_pll_output *pout = &pdb->out; + s64 multiplier = BIT(20); + s64 dec_start_multiple, dec_start, pll_comp_val; + s32 duration, div_frac_start; + s64 vco_clk_rate = pll->vco_current_rate; + s64 fref = pll->vco_ref_clk_rate; + + pr_debug("vco_clk_rate=%lld ref_clk_rate=%lld\n", + vco_clk_rate, fref); + + dec_start_multiple = div_s64(vco_clk_rate * multiplier, fref); + div_s64_rem(dec_start_multiple, multiplier, &div_frac_start); + + dec_start = div_s64(dec_start_multiple, multiplier); + + pout->dec_start = (u32)dec_start; + pout->div_frac_start = div_frac_start; + + if (pin->plllock_cnt == 0) + duration = 1024; + else if (pin->plllock_cnt == 1) + duration = 256; + else if (pin->plllock_cnt == 2) + duration = 128; + else + duration = 32; + + pll_comp_val = duration * dec_start_multiple; + pll_comp_val = div_s64(pll_comp_val, multiplier); + do_div(pll_comp_val, 10); + + pout->plllock_cmp = (u32)pll_comp_val; + + pout->pll_txclk_en = 1; + if (pll->revision == MSM8996_DSI_PLL_REVISION_2) + pout->cmn_ldo_cntrl = 0x3c; + else + pout->cmn_ldo_cntrl = 0x1c; +} + +static u32 pll_8996_kvco_slop(u32 vrate) +{ + u32 slop = 0; + + if (vrate > 1300000000UL && vrate <= 1800000000UL) + slop = 600; + else if (vrate > 1800000000UL && vrate < 2300000000UL) + slop = 400; + else if (vrate > 2300000000UL && vrate < 2600000000UL) + slop = 280; + + return slop; +} + +static void pll_8996_calc_vco_count(struct dsi_pll_db *pdb, + s64 vco_clk_rate, s64 fref) +{ + struct dsi_pll_input *pin = &pdb->in; + struct dsi_pll_output *pout = &pdb->out; + s64 data; + u32 cnt; + + data = fref * pin->vco_measure_time; + do_div(data, 1000000); + data &= 0x03ff; /* 10 bits */ + data -= 2; + pout->pll_vco_div_ref = data; + + data = (unsigned long)vco_clk_rate / 1000000; /* unit is Mhz */ + data *= pin->vco_measure_time; + do_div(data, 10); + pout->pll_vco_count = data; /* reg: 0x0474, 0x0478 */ + + data = fref * pin->kvco_measure_time; + do_div(data, 1000000); + data &= 0x03ff; /* 10 bits */ + data -= 1; + pout->pll_kvco_div_ref = data; + + cnt = pll_8996_kvco_slop(vco_clk_rate); + cnt *= 2; + do_div(cnt, 100); + cnt *= pin->kvco_measure_time; + pout->pll_kvco_count = cnt; + + pout->pll_misc1 = 16; + pout->pll_resetsm_cntrl = 48; + pout->pll_resetsm_cntrl2 = pin->bandgap_timer << 3; + pout->pll_resetsm_cntrl5 = pin->pll_wakeup_timer; + pout->pll_kvco_code = 0; +} + +static void pll_db_commit_ssc(struct mdss_pll_resources *pll, + struct dsi_pll_db *pdb) +{ + void __iomem *pll_base = pll->pll_base; + struct dsi_pll_input *pin = &pdb->in; + struct dsi_pll_output *pout = &pdb->out; + char data; + + data = pin->ssc_adj_period; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_ADJ_PER1, data); + data = (pin->ssc_adj_period >> 8); + data &= 0x03; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_ADJ_PER2, data); + + data = pout->ssc_period; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_PER1, data); + data = (pout->ssc_period >> 8); + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_PER2, data); + + data = pout->ssc_step_size; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_STEP_SIZE1, data); + data = (pout->ssc_step_size >> 8); + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_STEP_SIZE2, data); + + data = (pin->ssc_center & 0x01); + data <<= 1; + data |= 0x01; /* enable */ + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SSC_EN_CENTER, data); + + wmb(); /* make sure register committed */ +} + +static void pll_db_commit_common(struct mdss_pll_resources *pll, + struct dsi_pll_db *pdb) +{ + void __iomem *pll_base = pll->pll_base; + struct dsi_pll_input *pin = &pdb->in; + struct dsi_pll_output *pout = &pdb->out; + char data; + + /* confgiure the non frequency dependent pll registers */ + data = 0; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_SYSCLK_EN_RESET, data); + + /* DSIPHY_PLL_CLKBUFLR_EN updated at dsi phy */ + + data = pout->pll_txclk_en; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_TXCLK_EN, data); + + data = pout->pll_resetsm_cntrl; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_RESETSM_CNTRL, data); + data = pout->pll_resetsm_cntrl2; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_RESETSM_CNTRL2, data); + data = pout->pll_resetsm_cntrl5; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_RESETSM_CNTRL5, data); + + data = pout->pll_vco_div_ref; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_DIV_REF1, data); + data = (pout->pll_vco_div_ref >> 8); + data &= 0x03; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_DIV_REF2, data); + + data = pout->pll_kvco_div_ref; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_DIV_REF1, data); + data = (pout->pll_kvco_div_ref >> 8); + data &= 0x03; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_DIV_REF2, data); + + data = pout->pll_misc1; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_MISC1, data); + + data = pin->pll_ie_trim; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_IE_TRIM, data); + + data = pin->pll_ip_trim; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_IP_TRIM, data); + + data = ((pin->pll_cpmset_cur << 3) | pin->pll_cpcset_cur); + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_CP_SET_CUR, data); + + data = ((pin->pll_icpcset_p << 3) | pin->pll_icpcset_m); + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_ICPCSET, data); + + data = ((pin->pll_icpmset_p << 3) | pin->pll_icpcset_m); + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_ICPMSET, data); + + data = ((pin->pll_icpmset << 3) | pin->pll_icpcset); + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_ICP_SET, data); + + data = ((pdb->in.pll_lpf_cap2 << 4) | pdb->in.pll_lpf_cap1); + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_LPF1, data); + + data = pin->pll_iptat_trim; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_IPTAT_TRIM, data); + + data = (pdb->in.pll_c3ctrl | (pdb->in.pll_r3ctrl << 4)); + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_CRCTRL, data); +} + +static void pll_db_commit_8996(struct mdss_pll_resources *pll, + struct dsi_pll_db *pdb) +{ + void __iomem *pll_base = pll->pll_base; + struct dsi_pll_input *pin = &pdb->in; + struct dsi_pll_output *pout = &pdb->out; + char data; + + data = pout->cmn_ldo_cntrl; + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_LDO_CNTRL, data); + + pll_db_commit_common(pll, pdb); + + /* de assert pll start and apply pll sw reset */ + /* stop pll */ + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_PLL_CNTRL, 0); + + /* pll sw reset */ + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_1, 0x20); + wmb(); /* make sure register committed */ + udelay(10); + + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_1, 0); + wmb(); /* make sure register committed */ + + data = pdb->in.dsiclk_sel; /* set dsiclk_sel = 1 */ + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CLK_CFG1, data); + + data = 0xff; /* data, clk, pll normal operation */ + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CTRL_0, data); + + /* confgiure the frequency dependent pll registers */ + data = pout->dec_start; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_DEC_START, data); + + data = pout->div_frac_start; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_DIV_FRAC_START1, data); + data = (pout->div_frac_start >> 8); + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_DIV_FRAC_START2, data); + data = (pout->div_frac_start >> 16); + data &= 0x0f; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_DIV_FRAC_START3, data); + + data = pout->plllock_cmp; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLLLOCK_CMP1, data); + data = (pout->plllock_cmp >> 8); + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLLLOCK_CMP2, data); + data = (pout->plllock_cmp >> 16); + data &= 0x03; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLLLOCK_CMP3, data); + + data = ((pin->plllock_cnt << 1) | (pin->plllock_rng << 3)); + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLLLOCK_CMP_EN, data); + + data = pout->pll_vco_count; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_COUNT1, data); + data = (pout->pll_vco_count >> 8); + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_VCO_COUNT2, data); + + data = pout->pll_kvco_count; + data &= 0x0ff; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_COUNT1, data); + data = (pout->pll_kvco_count >> 8); + data &= 0x03; + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_KVCO_COUNT2, data); + + /* + * tx_band = pll_postdiv + * 0: divided by 1 <== for now + * 1: divided by 2 + * 2: divided by 4 + * 3: divided by 8 + */ + data = (((pout->pll_postdiv - 1) << 4) | pdb->in.pll_lpf_res1); + MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PLL_LPF2_POSTDIV, data); + + data = (pout->pll_n1div | (pout->pll_n2div << 4)); + MDSS_PLL_REG_W(pll_base, DSIPHY_CMN_CLK_CFG0, data); + + if (pll->ssc_en) + pll_db_commit_ssc(pll, pdb); + + wmb(); /* make sure register committed */ +} + +/* + * pll_source_finding: + * Both GLBL_TEST_CTRL and CLKBUFLR_EN are configured + * at mdss_dsi_8996_phy_config() + */ +static int pll_source_finding(struct mdss_pll_resources *pll) +{ + u32 clk_buf_en; + u32 glbl_test_ctrl; + + glbl_test_ctrl = MDSS_PLL_REG_R(pll->pll_base, + DSIPHY_CMN_GLBL_TEST_CTRL); + clk_buf_en = MDSS_PLL_REG_R(pll->pll_base, + DSIPHY_PLL_CLKBUFLR_EN); + + glbl_test_ctrl &= BIT(2); + glbl_test_ctrl >>= 2; + + pr_debug("%s: pll=%d clk_buf_en=%x glbl_test_ctrl=%x\n", + __func__, pll->index, clk_buf_en, glbl_test_ctrl); + + clk_buf_en &= (PLL_OUTPUT_RIGHT | PLL_OUTPUT_LEFT); + + if ((glbl_test_ctrl == PLL_SOURCE_FROM_LEFT) && + (clk_buf_en == PLL_OUTPUT_BOTH)) + return PLL_MASTER; + + if ((glbl_test_ctrl == PLL_SOURCE_FROM_RIGHT) && + (clk_buf_en == PLL_OUTPUT_NONE)) + return PLL_SLAVE; + + if ((glbl_test_ctrl == PLL_SOURCE_FROM_LEFT) && + (clk_buf_en == PLL_OUTPUT_RIGHT)) + return PLL_STANDALONE; + + pr_debug("%s: Error pll setup, clk_buf_en=%x glbl_test_ctrl=%x\n", + __func__, clk_buf_en, glbl_test_ctrl); + + return PLL_UNKNOWN; +} + +static void pll_source_setup(struct mdss_pll_resources *pll) +{ + int status; + struct dsi_pll_db *pdb = (struct dsi_pll_db *)pll->priv; + struct mdss_pll_resources *other; + + if (pdb->source_setup_done) + return; + + pdb->source_setup_done++; + + status = pll_source_finding(pll); + + if (status == PLL_STANDALONE || status == PLL_UNKNOWN) + return; + + other = pdb->next->pll; + if (!other) + return; + + pr_debug("%s: status=%d pll=%d other=%d\n", __func__, + status, pll->index, other->index); + + if (status == PLL_MASTER) + pll->slave = other; + else + other->slave = pll; +} + +int pll_vco_set_rate_8996(struct clk *c, unsigned long rate) +{ + int rc; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *pll = vco->priv; + struct mdss_pll_resources *slave; + struct dsi_pll_db *pdb; + + pdb = (struct dsi_pll_db *)pll->priv; + if (!pdb) { + pr_err("No prov found\n"); + return -EINVAL; + } + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("Failed to enable mdss dsi plla=%d\n", pll->index); + return rc; + } + + pll_source_setup(pll); + + pr_debug("%s: ndx=%d base=%p rate=%lu slave=%p\n", __func__, + pll->index, pll->pll_base, rate, pll->slave); + + pll->vco_current_rate = rate; + pll->vco_ref_clk_rate = vco->ref_clk_rate; + + mdss_dsi_pll_8996_input_init(pll, pdb); + + pll_8996_dec_frac_calc(pll, pdb); + + if (pll->ssc_en) + pll_8996_ssc_calc(pll, pdb); + + pll_8996_calc_vco_count(pdb, pll->vco_current_rate, + pll->vco_ref_clk_rate); + + /* commit slave if split display is enabled */ + slave = pll->slave; + if (slave) + pll_db_commit_8996(slave, pdb); + + /* commit master itself */ + pll_db_commit_8996(pll, pdb); + + mdss_pll_resource_enable(pll, false); + + return rc; +} + +static void shadow_pll_dynamic_refresh_8996(struct mdss_pll_resources *pll, + struct dsi_pll_db *pdb) +{ + struct dsi_pll_output *pout = &pdb->out; + + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL20, + DSIPHY_CMN_CTRL_0, DSIPHY_PLL_SYSCLK_EN_RESET, + 0xFF, 0x0); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL21, + DSIPHY_PLL_DEC_START, DSIPHY_PLL_DIV_FRAC_START1, + pout->dec_start, (pout->div_frac_start & 0x0FF)); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL22, + DSIPHY_PLL_DIV_FRAC_START2, DSIPHY_PLL_DIV_FRAC_START3, + ((pout->div_frac_start >> 8) & 0x0FF), + ((pout->div_frac_start >> 16) & 0x0F)); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL23, + DSIPHY_PLL_PLLLOCK_CMP1, DSIPHY_PLL_PLLLOCK_CMP2, + (pout->plllock_cmp & 0x0FF), + ((pout->plllock_cmp >> 8) & 0x0FF)); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL24, + DSIPHY_PLL_PLLLOCK_CMP3, DSIPHY_PLL_PLL_VCO_TUNE, + ((pout->plllock_cmp >> 16) & 0x03), + (pll->cache_pll_trim_codes[1] | BIT(7))); /* VCO tune*/ + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL25, + DSIPHY_PLL_KVCO_CODE, DSIPHY_PLL_RESETSM_CNTRL, + (pll->cache_pll_trim_codes[0] | BIT(5)), 0x38); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL26, + DSIPHY_PLL_PLL_LPF2_POSTDIV, DSIPHY_CMN_PLL_CNTRL, + (((pout->pll_postdiv - 1) << 4) | pdb->in.pll_lpf_res1), 0x01); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL27, + DSIPHY_CMN_PLL_CNTRL, DSIPHY_CMN_PLL_CNTRL, + 0x01, 0x01); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL28, + DSIPHY_CMN_PLL_CNTRL, DSIPHY_CMN_PLL_CNTRL, + 0x01, 0x01); + MDSS_DYN_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_CTRL29, + DSIPHY_CMN_PLL_CNTRL, DSIPHY_CMN_PLL_CNTRL, + 0x01, 0x01); + MDSS_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR, 0x0000001E); + MDSS_PLL_REG_W(pll->dyn_pll_base, + DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR2, 0x001FFE00); + + /* + * Ensure all the dynamic refresh registers are written before + * dynamic refresh to change the fps is triggered + */ + wmb(); +} + +int shadow_pll_vco_set_rate_8996(struct clk *c, unsigned long rate) +{ + int rc; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *pll = vco->priv; + struct dsi_pll_db *pdb; + s64 vco_clk_rate = (s64)rate; + + if (!pll) { + pr_err("PLL data not found\n"); + return -EINVAL; + } + + pdb = pll->priv; + if (!pdb) { + pr_err("No priv data found\n"); + return -EINVAL; + } + + rc = mdss_pll_read_stored_trim_codes(pll, vco_clk_rate); + 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); + return rc; + } + + pr_debug("%s: ndx=%d base=%p rate=%lu\n", __func__, + pll->index, pll->pll_base, rate); + + pll->vco_current_rate = rate; + pll->vco_ref_clk_rate = vco->ref_clk_rate; + + mdss_dsi_pll_8996_input_init(pll, pdb); + + pll_8996_dec_frac_calc(pll, pdb); + + pll_8996_calc_vco_count(pdb, pll->vco_current_rate, + pll->vco_ref_clk_rate); + + shadow_pll_dynamic_refresh_8996(pll, pdb); + + rc = mdss_pll_resource_enable(pll, false); + if (rc) { + pr_err("Failed to enable mdss dsi plla=%d\n", pll->index); + return rc; + } + + return rc; +} + +unsigned long pll_vco_get_rate_8996(struct clk *c) +{ + u64 vco_rate, multiplier = BIT(20); + s32 div_frac_start; + u32 dec_start; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + u64 ref_clk = vco->ref_clk_rate; + int rc; + struct mdss_pll_resources *pll = vco->priv; + + if (is_gdsc_disabled(pll)) + return 0; + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll=%d\n", pll->index); + return rc; + } + + dec_start = MDSS_PLL_REG_R(pll->pll_base, + DSIPHY_PLL_DEC_START); + dec_start &= 0x0ff; + pr_debug("dec_start = 0x%x\n", dec_start); + + div_frac_start = (MDSS_PLL_REG_R(pll->pll_base, + DSIPHY_PLL_DIV_FRAC_START3) & 0x0f) << 16; + div_frac_start |= (MDSS_PLL_REG_R(pll->pll_base, + DSIPHY_PLL_DIV_FRAC_START2) & 0x0ff) << 8; + div_frac_start |= MDSS_PLL_REG_R(pll->pll_base, + DSIPHY_PLL_DIV_FRAC_START1) & 0x0ff; + pr_debug("div_frac_start = 0x%x\n", div_frac_start); + + vco_rate = ref_clk * dec_start; + vco_rate += ((ref_clk * div_frac_start) / multiplier); + + pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate); + + mdss_pll_resource_enable(pll, false); + + return (unsigned long)vco_rate; +} + +long pll_vco_round_rate_8996(struct clk *c, unsigned long rate) +{ + unsigned long rrate = rate; + u32 div; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + + div = vco->min_rate / rate; + if (div > 15) { + /* rate < 86.67 Mhz */ + pr_err("rate=%lu NOT supportted\n", rate); + return -EINVAL; + } + + if (rate < vco->min_rate) + rrate = vco->min_rate; + if (rate > vco->max_rate) + rrate = vco->max_rate; + + return rrate; +} + +enum handoff pll_vco_handoff_8996(struct clk *c) +{ + int rc; + enum handoff ret = HANDOFF_DISABLED_CLK; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *pll = vco->priv; + + if (is_gdsc_disabled(pll)) + return HANDOFF_DISABLED_CLK; + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("Failed to enable mdss dsi pll=%d\n", pll->index); + return ret; + } + + if (pll_is_pll_locked_8996(pll)) { + pll->handoff_resources = true; + pll->pll_on = true; + c->rate = pll_vco_get_rate_8996(c); + ret = HANDOFF_ENABLED_CLK; + } else { + mdss_pll_resource_enable(pll, false); + } + + return ret; +} + +enum handoff shadow_pll_vco_handoff_8996(struct clk *c) +{ + return HANDOFF_DISABLED_CLK; +} + +int pll_vco_prepare_8996(struct clk *c) +{ + int rc = 0; + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *pll = vco->priv; + + if (!pll) { + pr_err("Dsi pll resources are not available\n"); + return -EINVAL; + } + + rc = mdss_pll_resource_enable(pll, true); + if (rc) { + pr_err("ndx=%d Failed to enable mdss dsi pll resources\n", + pll->index); + return rc; + } + + if ((pll->vco_cached_rate != 0) + && (pll->vco_cached_rate == c->rate)) { + rc = c->ops->set_rate(c, pll->vco_cached_rate); + if (rc) { + pr_err("index=%d vco_set_rate failed. rc=%d\n", + rc, pll->index); + mdss_pll_resource_enable(pll, false); + goto error; + } + } + + rc = dsi_pll_enable(c); + + if (rc) { + mdss_pll_resource_enable(pll, false); + pr_err("ndx=%d failed to enable dsi pll\n", pll->index); + } + +error: + return rc; +} + +void pll_vco_unprepare_8996(struct clk *c) +{ + struct dsi_pll_vco_clk *vco = to_vco_clk(c); + struct mdss_pll_resources *pll = vco->priv; + + if (!pll) { + pr_err("Dsi pll resources are not available\n"); + return; + } + + pll->vco_cached_rate = c->rate; + dsi_pll_disable(c); +} diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.c b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.c new file mode 100644 index 000000000000..1de1b997a041 --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.c @@ -0,0 +1,566 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/clk/msm-clk-provider.h> +#include <linux/clk/msm-clk.h> +#include <linux/workqueue.h> +#include <linux/clk/msm-clock-generic.h> +#include <dt-bindings/clock/msm-clocks-8996.h> + +#include "mdss-pll.h" +#include "mdss-dsi-pll.h" +#include "mdss-dsi-pll-8996.h" + +#define VCO_DELAY_USEC 1 + +static struct dsi_pll_db pll_db[DSI_PLL_NUM]; + +static struct clk_ops n2_clk_src_ops; +static struct clk_ops shadow_n2_clk_src_ops; +static struct clk_ops byte_clk_src_ops; +static struct clk_ops post_n1_div_clk_src_ops; +static struct clk_ops shadow_post_n1_div_clk_src_ops; + +static struct clk_ops clk_ops_gen_mux_dsi; + +/* Op structures */ +static struct clk_ops clk_ops_dsi_vco = { + .set_rate = pll_vco_set_rate_8996, + .round_rate = pll_vco_round_rate_8996, + .handoff = pll_vco_handoff_8996, + .prepare = pll_vco_prepare_8996, + .unprepare = pll_vco_unprepare_8996, +}; + +static struct clk_div_ops post_n1_div_ops = { + .set_div = post_n1_div_set_div, + .get_div = post_n1_div_get_div, +}; + +static struct clk_div_ops n2_div_ops = { /* hr_oclk3 */ + .set_div = n2_div_set_div, + .get_div = n2_div_get_div, +}; + +static struct clk_mux_ops mdss_byte_mux_ops = { + .set_mux_sel = set_mdss_byte_mux_sel_8996, + .get_mux_sel = get_mdss_byte_mux_sel_8996, +}; + +static struct clk_mux_ops mdss_pixel_mux_ops = { + .set_mux_sel = set_mdss_pixel_mux_sel_8996, + .get_mux_sel = get_mdss_pixel_mux_sel_8996, +}; + +/* Shadow ops for dynamic refresh */ +static struct clk_ops clk_ops_shadow_dsi_vco = { + .set_rate = shadow_pll_vco_set_rate_8996, + .round_rate = pll_vco_round_rate_8996, + .handoff = shadow_pll_vco_handoff_8996, +}; + +static struct clk_div_ops shadow_post_n1_div_ops = { + .set_div = post_n1_div_set_div, +}; + +static struct clk_div_ops shadow_n2_div_ops = { + .set_div = shadow_n2_div_set_div, +}; + +static struct dsi_pll_vco_clk dsi0pll_vco_clk = { + .ref_clk_rate = 19200000UL, + .min_rate = 1300000000UL, + .max_rate = 2600000000UL, + .pll_en_seq_cnt = 1, + .pll_enable_seqs[0] = dsi_pll_enable_seq_8996, + .c = { + .dbg_name = "dsi0pll_vco_clk_8996", + .ops = &clk_ops_dsi_vco, + CLK_INIT(dsi0pll_vco_clk.c), + }, +}; + +static struct dsi_pll_vco_clk dsi0pll_shadow_vco_clk = { + .ref_clk_rate = 19200000u, + .min_rate = 1300000000u, + .max_rate = 2600000000u, + .c = { + .dbg_name = "dsi0pll_shadow_vco_clk", + .ops = &clk_ops_shadow_dsi_vco, + CLK_INIT(dsi0pll_shadow_vco_clk.c), + }, +}; + +static struct dsi_pll_vco_clk dsi1pll_vco_clk = { + .ref_clk_rate = 19200000UL, + .min_rate = 1300000000UL, + .max_rate = 2600000000UL, + .pll_en_seq_cnt = 1, + .pll_enable_seqs[0] = dsi_pll_enable_seq_8996, + .c = { + .dbg_name = "dsi1pll_vco_clk_8996", + .ops = &clk_ops_dsi_vco, + CLK_INIT(dsi1pll_vco_clk.c), + }, +}; + +static struct dsi_pll_vco_clk dsi1pll_shadow_vco_clk = { + .ref_clk_rate = 19200000u, + .min_rate = 1300000000u, + .max_rate = 2600000000u, + .pll_en_seq_cnt = 1, + .pll_enable_seqs[0] = dsi_pll_enable_seq_8996, + .c = { + .dbg_name = "dsi1pll_shadow_vco_clk", + .ops = &clk_ops_shadow_dsi_vco, + CLK_INIT(dsi1pll_shadow_vco_clk.c), + }, +}; + +static struct div_clk dsi0pll_post_n1_div_clk = { + .data = { + .max_div = 15, + .min_div = 1, + }, + .ops = &post_n1_div_ops, + .c = { + .parent = &dsi0pll_vco_clk.c, + .dbg_name = "dsi0pll_post_n1_div_clk", + .ops = &post_n1_div_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_post_n1_div_clk.c), + }, +}; + +static struct div_clk dsi0pll_shadow_post_n1_div_clk = { + .data = { + .max_div = 15, + .min_div = 1, + }, + .ops = &shadow_post_n1_div_ops, + .c = { + .parent = &dsi0pll_shadow_vco_clk.c, + .dbg_name = "dsi0pll_shadow_post_n1_div_clk", + .ops = &shadow_post_n1_div_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_shadow_post_n1_div_clk.c), + }, +}; + +static struct div_clk dsi1pll_post_n1_div_clk = { + .data = { + .max_div = 15, + .min_div = 1, + }, + .ops = &post_n1_div_ops, + .c = { + .parent = &dsi1pll_vco_clk.c, + .dbg_name = "dsi1pll_post_n1_div_clk", + .ops = &post_n1_div_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_post_n1_div_clk.c), + }, +}; + +static struct div_clk dsi1pll_shadow_post_n1_div_clk = { + .data = { + .max_div = 15, + .min_div = 1, + }, + .ops = &shadow_post_n1_div_ops, + .c = { + .parent = &dsi1pll_shadow_vco_clk.c, + .dbg_name = "dsi1pll_shadow_post_n1_div_clk", + .ops = &shadow_post_n1_div_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_shadow_post_n1_div_clk.c), + }, +}; + +static struct div_clk dsi0pll_n2_div_clk = { + .data = { + .max_div = 15, + .min_div = 1, + }, + .ops = &n2_div_ops, + .c = { + .parent = &dsi0pll_post_n1_div_clk.c, + .dbg_name = "dsi0pll_n2_div_clk", + .ops = &n2_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_n2_div_clk.c), + }, +}; + +static struct div_clk dsi0pll_shadow_n2_div_clk = { + .data = { + .max_div = 15, + .min_div = 1, + }, + .ops = &shadow_n2_div_ops, + .c = { + .parent = &dsi0pll_shadow_post_n1_div_clk.c, + .dbg_name = "dsi0pll_shadow_n2_div_clk", + .ops = &shadow_n2_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_shadow_n2_div_clk.c), + }, +}; + +static struct div_clk dsi1pll_n2_div_clk = { + .data = { + .max_div = 15, + .min_div = 1, + }, + .ops = &n2_div_ops, + .c = { + .parent = &dsi1pll_post_n1_div_clk.c, + .dbg_name = "dsi1pll_n2_div_clk", + .ops = &n2_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_n2_div_clk.c), + }, +}; + +static struct div_clk dsi1pll_shadow_n2_div_clk = { + .data = { + .max_div = 15, + .min_div = 1, + }, + .ops = &shadow_n2_div_ops, + .c = { + .parent = &dsi1pll_shadow_post_n1_div_clk.c, + .dbg_name = "dsi1pll_shadow_n2_div_clk", + .ops = &shadow_n2_clk_src_ops, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_shadow_n2_div_clk.c), + }, +}; + +static struct div_clk dsi0pll_pixel_clk_src = { + .data = { + .div = 2, + .min_div = 2, + .max_div = 2, + }, + .c = { + .parent = &dsi0pll_n2_div_clk.c, + .dbg_name = "dsi0pll_pixel_clk_src", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_pixel_clk_src.c), + }, +}; + +static struct div_clk dsi0pll_shadow_pixel_clk_src = { + .data = { + .div = 2, + .min_div = 2, + .max_div = 2, + }, + .c = { + .parent = &dsi0pll_shadow_n2_div_clk.c, + .dbg_name = "dsi0pll_shadow_pixel_clk_src", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_shadow_pixel_clk_src.c), + }, +}; + +static struct div_clk dsi1pll_pixel_clk_src = { + .data = { + .div = 2, + .min_div = 2, + .max_div = 2, + }, + .c = { + .parent = &dsi1pll_n2_div_clk.c, + .dbg_name = "dsi1pll_pixel_clk_src", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_pixel_clk_src.c), + }, +}; + +static struct div_clk dsi1pll_shadow_pixel_clk_src = { + .data = { + .div = 2, + .min_div = 2, + .max_div = 2, + }, + .c = { + .parent = &dsi1pll_shadow_n2_div_clk.c, + .dbg_name = "dsi1pll_shadow_pixel_clk_src", + .ops = &clk_ops_div, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_shadow_pixel_clk_src.c), + }, +}; + +static struct mux_clk dsi0pll_pixel_clk_mux = { + .num_parents = 2, + .parents = (struct clk_src[]) { + {&dsi0pll_pixel_clk_src.c, 0}, + {&dsi0pll_shadow_pixel_clk_src.c, 1}, + }, + .ops = &mdss_pixel_mux_ops, + .c = { + .parent = &dsi0pll_pixel_clk_src.c, + .dbg_name = "dsi0pll_pixel_clk_mux", + .ops = &clk_ops_gen_mux_dsi, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_pixel_clk_mux.c), + } +}; + +static struct mux_clk dsi1pll_pixel_clk_mux = { + .num_parents = 2, + .parents = (struct clk_src[]) { + {&dsi1pll_pixel_clk_src.c, 0}, + {&dsi1pll_shadow_pixel_clk_src.c, 1}, + }, + .ops = &mdss_pixel_mux_ops, + .c = { + .parent = &dsi1pll_pixel_clk_src.c, + .dbg_name = "dsi1pll_pixel_clk_mux", + .ops = &clk_ops_gen_mux_dsi, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_pixel_clk_mux.c), + } +}; + +static struct div_clk dsi0pll_byte_clk_src = { + .data = { + .div = 8, + .min_div = 8, + .max_div = 8, + }, + .c = { + .parent = &dsi0pll_post_n1_div_clk.c, + .dbg_name = "dsi0pll_byte_clk_src", + .ops = &clk_ops_div, + CLK_INIT(dsi0pll_byte_clk_src.c), + }, +}; + +static struct div_clk dsi0pll_shadow_byte_clk_src = { + .data = { + .div = 8, + .min_div = 8, + .max_div = 8, + }, + .c = { + .parent = &dsi0pll_shadow_post_n1_div_clk.c, + .dbg_name = "dsi0pll_shadow_byte_clk_src", + .ops = &clk_ops_div, + CLK_INIT(dsi0pll_shadow_byte_clk_src.c), + }, +}; + +static struct div_clk dsi1pll_byte_clk_src = { + .data = { + .div = 8, + .min_div = 8, + .max_div = 8, + }, + .c = { + .parent = &dsi1pll_post_n1_div_clk.c, + .dbg_name = "dsi1pll_byte_clk_src", + .ops = &clk_ops_div, + CLK_INIT(dsi1pll_byte_clk_src.c), + }, +}; + +static struct div_clk dsi1pll_shadow_byte_clk_src = { + .data = { + .div = 8, + .min_div = 8, + .max_div = 8, + }, + .c = { + .parent = &dsi1pll_shadow_post_n1_div_clk.c, + .dbg_name = "dsi1pll_shadow_byte_clk_src", + .ops = &clk_ops_div, + CLK_INIT(dsi1pll_shadow_byte_clk_src.c), + }, +}; + +static struct mux_clk dsi0pll_byte_clk_mux = { + .num_parents = 2, + .parents = (struct clk_src[]) { + {&dsi0pll_byte_clk_src.c, 0}, + {&dsi0pll_shadow_byte_clk_src.c, 1}, + }, + .ops = &mdss_byte_mux_ops, + .c = { + .parent = &dsi0pll_byte_clk_src.c, + .dbg_name = "dsi0pll_byte_clk_mux", + .ops = &clk_ops_gen_mux_dsi, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi0pll_byte_clk_mux.c), + } +}; +static struct mux_clk dsi1pll_byte_clk_mux = { + .num_parents = 2, + .parents = (struct clk_src[]) { + {&dsi1pll_byte_clk_src.c, 0}, + {&dsi1pll_shadow_byte_clk_src.c, 1}, + }, + .ops = &mdss_byte_mux_ops, + .c = { + .parent = &dsi1pll_byte_clk_src.c, + .dbg_name = "dsi1pll_byte_clk_mux", + .ops = &clk_ops_gen_mux_dsi, + .flags = CLKFLAG_NO_RATE_CACHE, + CLK_INIT(dsi1pll_byte_clk_mux.c), + } +}; + +static struct clk_lookup mdss_dsi_pllcc_8996[] = { + CLK_LIST(dsi0pll_byte_clk_mux), + CLK_LIST(dsi0pll_byte_clk_src), + CLK_LIST(dsi0pll_pixel_clk_mux), + CLK_LIST(dsi0pll_pixel_clk_src), + CLK_LIST(dsi0pll_n2_div_clk), + CLK_LIST(dsi0pll_post_n1_div_clk), + CLK_LIST(dsi0pll_vco_clk), + CLK_LIST(dsi0pll_shadow_byte_clk_src), + CLK_LIST(dsi0pll_shadow_pixel_clk_src), + CLK_LIST(dsi0pll_shadow_n2_div_clk), + CLK_LIST(dsi0pll_shadow_post_n1_div_clk), + CLK_LIST(dsi0pll_shadow_vco_clk), +}; + +static struct clk_lookup mdss_dsi_pllcc_8996_1[] = { + CLK_LIST(dsi1pll_byte_clk_mux), + CLK_LIST(dsi1pll_byte_clk_src), + CLK_LIST(dsi1pll_pixel_clk_mux), + CLK_LIST(dsi1pll_pixel_clk_src), + CLK_LIST(dsi1pll_n2_div_clk), + CLK_LIST(dsi1pll_post_n1_div_clk), + CLK_LIST(dsi1pll_vco_clk), + CLK_LIST(dsi1pll_shadow_byte_clk_src), + CLK_LIST(dsi1pll_shadow_pixel_clk_src), + CLK_LIST(dsi1pll_shadow_n2_div_clk), + CLK_LIST(dsi1pll_shadow_post_n1_div_clk), + CLK_LIST(dsi1pll_shadow_vco_clk), +}; + +int dsi_pll_clock_register_8996(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc = 0, ndx; + int const ssc_freq_default = 31500; /* default h/w recommended value */ + int const ssc_ppm_default = 5000; /* default h/w recommended value */ + struct dsi_pll_db *pdb; + + if (!pdev || !pdev->dev.of_node) { + pr_err("Invalid input parameters\n"); + return -EINVAL; + } + + if (!pll_res || !pll_res->pll_base) { + pr_err("Invalid PLL resources\n"); + return -EPROBE_DEFER; + } + + if (pll_res->index >= DSI_PLL_NUM) { + pr_err("pll ndx=%d is NOT supported\n", pll_res->index); + return -EINVAL; + } + + ndx = pll_res->index; + pdb = &pll_db[ndx]; + pll_res->priv = pdb; + pdb->pll = pll_res; + ndx++; + ndx %= DSI_PLL_NUM; + pdb->next = &pll_db[ndx]; + + /* Set clock source operations */ + + /* hr_oclk3, pixel */ + n2_clk_src_ops = clk_ops_slave_div; + n2_clk_src_ops.prepare = mdss_pll_div_prepare; + + shadow_n2_clk_src_ops = clk_ops_slave_div; + + /* hr_ockl2, byte, vco pll */ + post_n1_div_clk_src_ops = clk_ops_div; + post_n1_div_clk_src_ops.prepare = mdss_pll_div_prepare; + + shadow_post_n1_div_clk_src_ops = clk_ops_div; + + byte_clk_src_ops = clk_ops_div; + byte_clk_src_ops.prepare = mdss_pll_div_prepare; + + clk_ops_gen_mux_dsi = clk_ops_gen_mux; + clk_ops_gen_mux_dsi.round_rate = parent_round_rate; + clk_ops_gen_mux_dsi.set_rate = parent_set_rate; + + if (pll_res->ssc_en) { + if (!pll_res->ssc_freq) + pll_res->ssc_freq = ssc_freq_default; + if (!pll_res->ssc_ppm) + pll_res->ssc_ppm = ssc_ppm_default; + } + + /* Set client data to mux, div and vco clocks. */ + if (pll_res->index == DSI_PLL_1) { + dsi1pll_byte_clk_src.priv = pll_res; + dsi1pll_pixel_clk_src.priv = pll_res; + dsi1pll_post_n1_div_clk.priv = pll_res; + dsi1pll_n2_div_clk.priv = pll_res; + dsi1pll_vco_clk.priv = pll_res; + + dsi1pll_shadow_byte_clk_src.priv = pll_res; + dsi1pll_shadow_pixel_clk_src.priv = pll_res; + dsi1pll_shadow_post_n1_div_clk.priv = pll_res; + dsi1pll_shadow_n2_div_clk.priv = pll_res; + dsi1pll_shadow_vco_clk.priv = pll_res; + + pll_res->vco_delay = VCO_DELAY_USEC; + rc = of_msm_clock_register(pdev->dev.of_node, + mdss_dsi_pllcc_8996_1, + ARRAY_SIZE(mdss_dsi_pllcc_8996_1)); + } else { + dsi0pll_byte_clk_src.priv = pll_res; + dsi0pll_pixel_clk_src.priv = pll_res; + dsi0pll_post_n1_div_clk.priv = pll_res; + dsi0pll_n2_div_clk.priv = pll_res; + dsi0pll_vco_clk.priv = pll_res; + + dsi0pll_shadow_byte_clk_src.priv = pll_res; + dsi0pll_shadow_pixel_clk_src.priv = pll_res; + dsi0pll_shadow_post_n1_div_clk.priv = pll_res; + dsi0pll_shadow_n2_div_clk.priv = pll_res; + dsi0pll_shadow_vco_clk.priv = pll_res; + + pll_res->vco_delay = VCO_DELAY_USEC; + rc = of_msm_clock_register(pdev->dev.of_node, + mdss_dsi_pllcc_8996, + ARRAY_SIZE(mdss_dsi_pllcc_8996)); + } + + if (!rc) { + pr_info("Registered DSI PLL ndx=%d clocks successfully\n", + pll_res->index); + } + + return rc; +} diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.h b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.h new file mode 100644 index 000000000000..611e79101d4f --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-dsi-pll-8996.h @@ -0,0 +1,221 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef MDSS_DSI_PLL_8996_H +#define MDSS_DSI_PLL_8996_H + +#define DSIPHY_CMN_CLK_CFG0 0x0010 +#define DSIPHY_CMN_CLK_CFG1 0x0014 +#define DSIPHY_CMN_GLBL_TEST_CTRL 0x0018 + +#define DSIPHY_CMN_PLL_CNTRL 0x0048 +#define DSIPHY_CMN_CTRL_0 0x001c +#define DSIPHY_CMN_CTRL_1 0x0020 + +#define DSIPHY_CMN_LDO_CNTRL 0x004c + +#define DSIPHY_PLL_IE_TRIM 0x0400 +#define DSIPHY_PLL_IP_TRIM 0x0404 + +#define DSIPHY_PLL_IPTAT_TRIM 0x0410 + +#define DSIPHY_PLL_CLKBUFLR_EN 0x041c + +#define DSIPHY_PLL_SYSCLK_EN_RESET 0x0428 +#define DSIPHY_PLL_RESETSM_CNTRL 0x042c +#define DSIPHY_PLL_RESETSM_CNTRL2 0x0430 +#define DSIPHY_PLL_RESETSM_CNTRL3 0x0434 +#define DSIPHY_PLL_RESETSM_CNTRL4 0x0438 +#define DSIPHY_PLL_RESETSM_CNTRL5 0x043c +#define DSIPHY_PLL_KVCO_DIV_REF1 0x0440 +#define DSIPHY_PLL_KVCO_DIV_REF2 0x0444 +#define DSIPHY_PLL_KVCO_COUNT1 0x0448 +#define DSIPHY_PLL_KVCO_COUNT2 0x044c +#define DSIPHY_PLL_VREF_CFG1 0x045c + +#define DSIPHY_PLL_KVCO_CODE 0x0458 + +#define DSIPHY_PLL_VCO_DIV_REF1 0x046c +#define DSIPHY_PLL_VCO_DIV_REF2 0x0470 +#define DSIPHY_PLL_VCO_COUNT1 0x0474 +#define DSIPHY_PLL_VCO_COUNT2 0x0478 +#define DSIPHY_PLL_PLLLOCK_CMP1 0x047c +#define DSIPHY_PLL_PLLLOCK_CMP2 0x0480 +#define DSIPHY_PLL_PLLLOCK_CMP3 0x0484 +#define DSIPHY_PLL_PLLLOCK_CMP_EN 0x0488 +#define DSIPHY_PLL_PLL_VCO_TUNE 0x048C +#define DSIPHY_PLL_DEC_START 0x0490 +#define DSIPHY_PLL_SSC_EN_CENTER 0x0494 +#define DSIPHY_PLL_SSC_ADJ_PER1 0x0498 +#define DSIPHY_PLL_SSC_ADJ_PER2 0x049c +#define DSIPHY_PLL_SSC_PER1 0x04a0 +#define DSIPHY_PLL_SSC_PER2 0x04a4 +#define DSIPHY_PLL_SSC_STEP_SIZE1 0x04a8 +#define DSIPHY_PLL_SSC_STEP_SIZE2 0x04ac +#define DSIPHY_PLL_DIV_FRAC_START1 0x04b4 +#define DSIPHY_PLL_DIV_FRAC_START2 0x04b8 +#define DSIPHY_PLL_DIV_FRAC_START3 0x04bc +#define DSIPHY_PLL_TXCLK_EN 0x04c0 +#define DSIPHY_PLL_PLL_CRCTRL 0x04c4 + +#define DSIPHY_PLL_RESET_SM_READY_STATUS 0x04cc + +#define DSIPHY_PLL_PLL_MISC1 0x04e8 + +#define DSIPHY_PLL_CP_SET_CUR 0x04f0 +#define DSIPHY_PLL_PLL_ICPMSET 0x04f4 +#define DSIPHY_PLL_PLL_ICPCSET 0x04f8 +#define DSIPHY_PLL_PLL_ICP_SET 0x04fc +#define DSIPHY_PLL_PLL_LPF1 0x0500 +#define DSIPHY_PLL_PLL_LPF2_POSTDIV 0x0504 +#define DSIPHY_PLL_PLL_BANDGAP 0x0508 + +#define DSI_DYNAMIC_REFRESH_PLL_CTRL15 0x050 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL19 0x060 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL20 0x064 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL21 0x068 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL22 0x06C +#define DSI_DYNAMIC_REFRESH_PLL_CTRL23 0x070 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL24 0x074 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL25 0x078 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL26 0x07C +#define DSI_DYNAMIC_REFRESH_PLL_CTRL27 0x080 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL28 0x084 +#define DSI_DYNAMIC_REFRESH_PLL_CTRL29 0x088 +#define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR 0x094 +#define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR2 0x098 + +struct dsi_pll_input { + u32 fref; /* 19.2 Mhz, reference clk */ + u32 fdata; /* bit clock rate */ + u32 dsiclk_sel; /* 1, reg: 0x0014 */ + u32 n2div; /* 1, reg: 0x0010, bit 4-7 */ + u32 ssc_en; /* 1, reg: 0x0494, bit 0 */ + u32 ldo_en; /* 0, reg: 0x004c, bit 0 */ + + /* fixed */ + u32 refclk_dbler_en; /* 0, reg: 0x04c0, bit 1 */ + u32 vco_measure_time; /* 5, unknown */ + u32 kvco_measure_time; /* 5, unknown */ + u32 bandgap_timer; /* 4, reg: 0x0430, bit 3 - 5 */ + u32 pll_wakeup_timer; /* 5, reg: 0x043c, bit 0 - 2 */ + u32 plllock_cnt; /* 1, reg: 0x0488, bit 1 - 2 */ + u32 plllock_rng; /* 1, reg: 0x0488, bit 3 - 4 */ + u32 ssc_center; /* 0, reg: 0x0494, bit 1 */ + u32 ssc_adj_period; /* 37, reg: 0x498, bit 0 - 9 */ + u32 ssc_spread; /* 0.005 */ + u32 ssc_freq; /* unknown */ + u32 pll_ie_trim; /* 4, reg: 0x0400 */ + u32 pll_ip_trim; /* 4, reg: 0x0404 */ + u32 pll_iptat_trim; /* reg: 0x0410 */ + u32 pll_cpcset_cur; /* 1, reg: 0x04f0, bit 0 - 2 */ + u32 pll_cpmset_cur; /* 1, reg: 0x04f0, bit 3 - 5 */ + + u32 pll_icpmset; /* 4, reg: 0x04fc, bit 3 - 5 */ + u32 pll_icpcset; /* 4, reg: 0x04fc, bit 0 - 2 */ + + u32 pll_icpmset_p; /* 0, reg: 0x04f4, bit 0 - 2 */ + u32 pll_icpmset_m; /* 0, reg: 0x04f4, bit 3 - 5 */ + + u32 pll_icpcset_p; /* 0, reg: 0x04f8, bit 0 - 2 */ + u32 pll_icpcset_m; /* 0, reg: 0x04f8, bit 3 - 5 */ + + u32 pll_lpf_res1; /* 3, reg: 0x0504, bit 0 - 3 */ + u32 pll_lpf_cap1; /* 11, reg: 0x0500, bit 0 - 3 */ + u32 pll_lpf_cap2; /* 1, reg: 0x0500, bit 4 - 7 */ + u32 pll_c3ctrl; /* 2, reg: 0x04c4 */ + u32 pll_r3ctrl; /* 1, reg: 0x04c4 */ +}; + +struct dsi_pll_output { + u32 pll_txclk_en; /* reg: 0x04c0 */ + u32 dec_start; /* reg: 0x0490 */ + u32 div_frac_start; /* reg: 0x04b4, 0x4b8, 0x04bc */ + u32 ssc_period; /* reg: 0x04a0, 0x04a4 */ + u32 ssc_step_size; /* reg: 0x04a8, 0x04ac */ + u32 plllock_cmp; /* reg: 0x047c, 0x0480, 0x0484 */ + u32 pll_vco_div_ref; /* reg: 0x046c, 0x0470 */ + u32 pll_vco_count; /* reg: 0x0474, 0x0478 */ + u32 pll_kvco_div_ref; /* reg: 0x0440, 0x0444 */ + u32 pll_kvco_count; /* reg: 0x0448, 0x044c */ + u32 pll_misc1; /* reg: 0x04e8 */ + u32 pll_lpf2_postdiv; /* reg: 0x0504 */ + u32 pll_resetsm_cntrl; /* reg: 0x042c */ + u32 pll_resetsm_cntrl2; /* reg: 0x0430 */ + u32 pll_resetsm_cntrl5; /* reg: 0x043c */ + u32 pll_kvco_code; /* reg: 0x0458 */ + + u32 cmn_clk_cfg0; /* reg: 0x0010 */ + u32 cmn_clk_cfg1; /* reg: 0x0014 */ + u32 cmn_ldo_cntrl; /* reg: 0x004c */ + + u32 pll_postdiv; /* vco */ + u32 pll_n1div; /* vco */ + u32 pll_n2div; /* hr_oclk3, pixel */ + u32 fcvo; +}; + +enum { + DSI_PLL_0, + DSI_PLL_1, + DSI_PLL_NUM +}; + +struct dsi_pll_db { + struct dsi_pll_db *next; + struct mdss_pll_resources *pll; + struct dsi_pll_input in; + struct dsi_pll_output out; + int source_setup_done; +}; + +enum { + PLL_OUTPUT_NONE, + PLL_OUTPUT_RIGHT, + PLL_OUTPUT_LEFT, + PLL_OUTPUT_BOTH +}; + +enum { + PLL_SOURCE_FROM_LEFT, + PLL_SOURCE_FROM_RIGHT +}; + +enum { + PLL_UNKNOWN, + PLL_STANDALONE, + PLL_SLAVE, + PLL_MASTER +}; + +int pll_vco_set_rate_8996(struct clk *c, unsigned long rate); +long pll_vco_round_rate_8996(struct clk *c, unsigned long rate); +enum handoff pll_vco_handoff_8996(struct clk *c); +enum handoff shadow_pll_vco_handoff_8996(struct clk *c); +int shadow_post_n1_div_set_div(struct div_clk *clk, int div); +int shadow_post_n1_div_get_div(struct div_clk *clk); +int shadow_n2_div_set_div(struct div_clk *clk, int div); +int shadow_n2_div_get_div(struct div_clk *clk); +int shadow_pll_vco_set_rate_8996(struct clk *c, unsigned long rate); +int pll_vco_prepare_8996(struct clk *c); +void pll_vco_unprepare_8996(struct clk *c); +int set_mdss_byte_mux_sel_8996(struct mux_clk *clk, int sel); +int get_mdss_byte_mux_sel_8996(struct mux_clk *clk); +int set_mdss_pixel_mux_sel_8996(struct mux_clk *clk, int sel); +int get_mdss_pixel_mux_sel_8996(struct mux_clk *clk); +int post_n1_div_set_div(struct div_clk *clk, int div); +int post_n1_div_get_div(struct div_clk *clk); +int n2_div_set_div(struct div_clk *clk, int div); +int n2_div_get_div(struct div_clk *clk); +int dsi_pll_enable_seq_8996(struct mdss_pll_resources *pll); + +#endif /* MDSS_DSI_PLL_8996_H */ diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll.h b/drivers/clk/qcom/mdss/mdss-dsi-pll.h new file mode 100644 index 000000000000..f88ae4d0eea1 --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-dsi-pll.h @@ -0,0 +1,110 @@ +/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MDSS_DSI_PLL_H +#define __MDSS_DSI_PLL_H + +#define MAX_DSI_PLL_EN_SEQS 10 + +#define DSI_PHY_PLL_UNIPHY_PLL_GLB_CFG (0x0020) +#define DSI_PHY_PLL_UNIPHY_PLL_LKDET_CFG2 (0x0064) +#define DSI_PHY_PLL_UNIPHY_PLL_TEST_CFG (0x0068) +#define DSI_PHY_PLL_UNIPHY_PLL_CAL_CFG1 (0x0070) + +/* Register offsets for 20nm PHY PLL */ +#define MMSS_DSI_PHY_PLL_PLL_CNTRL (0x0014) +#define MMSS_DSI_PHY_PLL_PLL_BKG_KVCO_CAL_EN (0x002C) +#define MMSS_DSI_PHY_PLL_PLLLOCK_CMP_EN (0x009C) + +struct lpfr_cfg { + unsigned long vco_rate; + u32 r; +}; + +struct dsi_pll_vco_clk { + unsigned long ref_clk_rate; + unsigned long min_rate; + unsigned long max_rate; + u32 pll_en_seq_cnt; + struct lpfr_cfg *lpfr_lut; + u32 lpfr_lut_size; + void *priv; + + struct clk c; + + int (*pll_enable_seqs[MAX_DSI_PLL_EN_SEQS]) + (struct mdss_pll_resources *dsi_pll_Res); +}; + +static inline struct dsi_pll_vco_clk *to_vco_clk(struct clk *clk) +{ + return container_of(clk, struct dsi_pll_vco_clk, c); +} + +int dsi_pll_clock_register_hpm(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +int dsi_pll_clock_register_20nm(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +int dsi_pll_clock_register_lpm(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +int dsi_pll_clock_register_8996(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +int dsi_pll_clock_register_cobalt(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); + +int set_byte_mux_sel(struct mux_clk *clk, int sel); +int get_byte_mux_sel(struct mux_clk *clk); +int dsi_pll_mux_prepare(struct clk *c); +int fixed_4div_set_div(struct div_clk *clk, int div); +int fixed_4div_get_div(struct div_clk *clk); +int digital_set_div(struct div_clk *clk, int div); +int digital_get_div(struct div_clk *clk); +int analog_set_div(struct div_clk *clk, int div); +int analog_get_div(struct div_clk *clk); +int dsi_pll_lock_status(struct mdss_pll_resources *dsi_pll_res); +int vco_set_rate(struct dsi_pll_vco_clk *vco, unsigned long rate); +unsigned long vco_get_rate(struct clk *c); +long vco_round_rate(struct clk *c, unsigned long rate); +enum handoff vco_handoff(struct clk *c); +int vco_prepare(struct clk *c); +void vco_unprepare(struct clk *c); + +/* APIs for 20nm PHY PLL */ +int pll_20nm_vco_set_rate(struct dsi_pll_vco_clk *vco, unsigned long rate); +int shadow_pll_20nm_vco_set_rate(struct dsi_pll_vco_clk *vco, + unsigned long rate); +long pll_20nm_vco_round_rate(struct clk *c, unsigned long rate); +enum handoff pll_20nm_vco_handoff(struct clk *c); +int pll_20nm_vco_prepare(struct clk *c); +void pll_20nm_vco_unprepare(struct clk *c); +int pll_20nm_vco_enable_seq(struct mdss_pll_resources *dsi_pll_res); + +int set_bypass_lp_div_mux_sel(struct mux_clk *clk, int sel); +int set_shadow_bypass_lp_div_mux_sel(struct mux_clk *clk, int sel); +int get_bypass_lp_div_mux_sel(struct mux_clk *clk); +int fixed_hr_oclk2_set_div(struct div_clk *clk, int div); +int shadow_fixed_hr_oclk2_set_div(struct div_clk *clk, int div); +int fixed_hr_oclk2_get_div(struct div_clk *clk); +int hr_oclk3_set_div(struct div_clk *clk, int div); +int shadow_hr_oclk3_set_div(struct div_clk *clk, int div); +int hr_oclk3_get_div(struct div_clk *clk); +int ndiv_set_div(struct div_clk *clk, int div); +int shadow_ndiv_set_div(struct div_clk *clk, int div); +int ndiv_get_div(struct div_clk *clk); +void __dsi_pll_disable(void __iomem *pll_base); + +int set_mdss_pixel_mux_sel(struct mux_clk *clk, int sel); +int get_mdss_pixel_mux_sel(struct mux_clk *clk); +int set_mdss_byte_mux_sel(struct mux_clk *clk, int sel); +int get_mdss_byte_mux_sel(struct mux_clk *clk); + +#endif diff --git a/drivers/clk/qcom/mdss/mdss-hdmi-pll-8996.c b/drivers/clk/qcom/mdss/mdss-hdmi-pll-8996.c new file mode 100644 index 000000000000..e79b5cc39a79 --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-hdmi-pll-8996.c @@ -0,0 +1,2686 @@ +/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/iopoll.h> +#include <linux/clk/msm-clk-provider.h> +#include <linux/clk/msm-clk.h> +#include <linux/clk/msm-clock-generic.h> +#include <dt-bindings/clock/msm-clocks-8996.h> + +#include "mdss-pll.h" +#include "mdss-hdmi-pll.h" + +/* CONSTANTS */ +#define HDMI_BIT_CLK_TO_PIX_CLK_RATIO 10 +#define HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD 3400000000UL +#define HDMI_DIG_FREQ_BIT_CLK_THRESHOLD 1500000000UL +#define HDMI_MID_FREQ_BIT_CLK_THRESHOLD 750000000 +#define HDMI_CLKS_PLL_DIVSEL 0 +#define HDMI_CORECLK_DIV 5 +#define HDMI_REF_CLOCK 19200000 +#define HDMI_64B_ERR_VAL 0xFFFFFFFFFFFFFFFF +#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_2400MHZ_BIT_CLK_HZ 2400000000UL +#define HDMI_2250MHZ_BIT_CLK_HZ 2250000000UL +#define HDMI_2000MHZ_BIT_CLK_HZ 2000000000UL +#define HDMI_1700MHZ_BIT_CLK_HZ 1700000000UL +#define HDMI_1200MHZ_BIT_CLK_HZ 1200000000UL +#define HDMI_1334MHZ_BIT_CLK_HZ 1334000000UL +#define HDMI_1000MHZ_BIT_CLK_HZ 1000000000UL +#define HDMI_850MHZ_BIT_CLK_HZ 850000000 +#define HDMI_667MHZ_BIT_CLK_HZ 667000000 +#define HDMI_600MHZ_BIT_CLK_HZ 600000000 +#define HDMI_500MHZ_BIT_CLK_HZ 500000000 +#define HDMI_450MHZ_BIT_CLK_HZ 450000000 +#define HDMI_334MHZ_BIT_CLK_HZ 334000000 +#define HDMI_300MHZ_BIT_CLK_HZ 300000000 +#define HDMI_282MHZ_BIT_CLK_HZ 282000000 +#define HDMI_250MHZ_BIT_CLK_HZ 250000000 +#define HDMI_KHZ_TO_HZ 1000 + +/* PLL REGISTERS */ +#define QSERDES_COM_ATB_SEL1 (0x000) +#define QSERDES_COM_ATB_SEL2 (0x004) +#define QSERDES_COM_FREQ_UPDATE (0x008) +#define QSERDES_COM_BG_TIMER (0x00C) +#define QSERDES_COM_SSC_EN_CENTER (0x010) +#define QSERDES_COM_SSC_ADJ_PER1 (0x014) +#define QSERDES_COM_SSC_ADJ_PER2 (0x018) +#define QSERDES_COM_SSC_PER1 (0x01C) +#define QSERDES_COM_SSC_PER2 (0x020) +#define QSERDES_COM_SSC_STEP_SIZE1 (0x024) +#define QSERDES_COM_SSC_STEP_SIZE2 (0x028) +#define QSERDES_COM_POST_DIV (0x02C) +#define QSERDES_COM_POST_DIV_MUX (0x030) +#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN (0x034) +#define QSERDES_COM_CLK_ENABLE1 (0x038) +#define QSERDES_COM_SYS_CLK_CTRL (0x03C) +#define QSERDES_COM_SYSCLK_BUF_ENABLE (0x040) +#define QSERDES_COM_PLL_EN (0x044) +#define QSERDES_COM_PLL_IVCO (0x048) +#define QSERDES_COM_LOCK_CMP1_MODE0 (0x04C) +#define QSERDES_COM_LOCK_CMP2_MODE0 (0x050) +#define QSERDES_COM_LOCK_CMP3_MODE0 (0x054) +#define QSERDES_COM_LOCK_CMP1_MODE1 (0x058) +#define QSERDES_COM_LOCK_CMP2_MODE1 (0x05C) +#define QSERDES_COM_LOCK_CMP3_MODE1 (0x060) +#define QSERDES_COM_LOCK_CMP1_MODE2 (0x064) +#define QSERDES_COM_CMN_RSVD0 (0x064) +#define QSERDES_COM_LOCK_CMP2_MODE2 (0x068) +#define QSERDES_COM_EP_CLOCK_DETECT_CTRL (0x068) +#define QSERDES_COM_LOCK_CMP3_MODE2 (0x06C) +#define QSERDES_COM_SYSCLK_DET_COMP_STATUS (0x06C) +#define QSERDES_COM_BG_TRIM (0x070) +#define QSERDES_COM_CLK_EP_DIV (0x074) +#define QSERDES_COM_CP_CTRL_MODE0 (0x078) +#define QSERDES_COM_CP_CTRL_MODE1 (0x07C) +#define QSERDES_COM_CP_CTRL_MODE2 (0x080) +#define QSERDES_COM_CMN_RSVD1 (0x080) +#define QSERDES_COM_PLL_RCTRL_MODE0 (0x084) +#define QSERDES_COM_PLL_RCTRL_MODE1 (0x088) +#define QSERDES_COM_PLL_RCTRL_MODE2 (0x08C) +#define QSERDES_COM_CMN_RSVD2 (0x08C) +#define QSERDES_COM_PLL_CCTRL_MODE0 (0x090) +#define QSERDES_COM_PLL_CCTRL_MODE1 (0x094) +#define QSERDES_COM_PLL_CCTRL_MODE2 (0x098) +#define QSERDES_COM_CMN_RSVD3 (0x098) +#define QSERDES_COM_PLL_CNTRL (0x09C) +#define QSERDES_COM_PHASE_SEL_CTRL (0x0A0) +#define QSERDES_COM_PHASE_SEL_DC (0x0A4) +#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL (0x0A8) +#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM (0x0A8) +#define QSERDES_COM_SYSCLK_EN_SEL (0x0AC) +#define QSERDES_COM_CML_SYSCLK_SEL (0x0B0) +#define QSERDES_COM_RESETSM_CNTRL (0x0B4) +#define QSERDES_COM_RESETSM_CNTRL2 (0x0B8) +#define QSERDES_COM_RESTRIM_CTRL (0x0BC) +#define QSERDES_COM_RESTRIM_CTRL2 (0x0C0) +#define QSERDES_COM_RESCODE_DIV_NUM (0x0C4) +#define QSERDES_COM_LOCK_CMP_EN (0x0C8) +#define QSERDES_COM_LOCK_CMP_CFG (0x0CC) +#define QSERDES_COM_DEC_START_MODE0 (0x0D0) +#define QSERDES_COM_DEC_START_MODE1 (0x0D4) +#define QSERDES_COM_DEC_START_MODE2 (0x0D8) +#define QSERDES_COM_VCOCAL_DEADMAN_CTRL (0x0D8) +#define QSERDES_COM_DIV_FRAC_START1_MODE0 (0x0DC) +#define QSERDES_COM_DIV_FRAC_START2_MODE0 (0x0E0) +#define QSERDES_COM_DIV_FRAC_START3_MODE0 (0x0E4) +#define QSERDES_COM_DIV_FRAC_START1_MODE1 (0x0E8) +#define QSERDES_COM_DIV_FRAC_START2_MODE1 (0x0EC) +#define QSERDES_COM_DIV_FRAC_START3_MODE1 (0x0F0) +#define QSERDES_COM_DIV_FRAC_START1_MODE2 (0x0F4) +#define QSERDES_COM_VCO_TUNE_MINVAL1 (0x0F4) +#define QSERDES_COM_DIV_FRAC_START2_MODE2 (0x0F8) +#define QSERDES_COM_VCO_TUNE_MINVAL2 (0x0F8) +#define QSERDES_COM_DIV_FRAC_START3_MODE2 (0x0FC) +#define QSERDES_COM_CMN_RSVD4 (0x0FC) +#define QSERDES_COM_INTEGLOOP_INITVAL (0x100) +#define QSERDES_COM_INTEGLOOP_EN (0x104) +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 (0x108) +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 (0x10C) +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 (0x110) +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 (0x114) +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE2 (0x118) +#define QSERDES_COM_VCO_TUNE_MAXVAL1 (0x118) +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE2 (0x11C) +#define QSERDES_COM_VCO_TUNE_MAXVAL2 (0x11C) +#define QSERDES_COM_RES_TRIM_CONTROL2 (0x120) +#define QSERDES_COM_VCO_TUNE_CTRL (0x124) +#define QSERDES_COM_VCO_TUNE_MAP (0x128) +#define QSERDES_COM_VCO_TUNE1_MODE0 (0x12C) +#define QSERDES_COM_VCO_TUNE2_MODE0 (0x130) +#define QSERDES_COM_VCO_TUNE1_MODE1 (0x134) +#define QSERDES_COM_VCO_TUNE2_MODE1 (0x138) +#define QSERDES_COM_VCO_TUNE1_MODE2 (0x13C) +#define QSERDES_COM_VCO_TUNE_INITVAL1 (0x13C) +#define QSERDES_COM_VCO_TUNE2_MODE2 (0x140) +#define QSERDES_COM_VCO_TUNE_INITVAL2 (0x140) +#define QSERDES_COM_VCO_TUNE_TIMER1 (0x144) +#define QSERDES_COM_VCO_TUNE_TIMER2 (0x148) +#define QSERDES_COM_SAR (0x14C) +#define QSERDES_COM_SAR_CLK (0x150) +#define QSERDES_COM_SAR_CODE_OUT_STATUS (0x154) +#define QSERDES_COM_SAR_CODE_READY_STATUS (0x158) +#define QSERDES_COM_CMN_STATUS (0x15C) +#define QSERDES_COM_RESET_SM_STATUS (0x160) +#define QSERDES_COM_RESTRIM_CODE_STATUS (0x164) +#define QSERDES_COM_PLLCAL_CODE1_STATUS (0x168) +#define QSERDES_COM_PLLCAL_CODE2_STATUS (0x16C) +#define QSERDES_COM_BG_CTRL (0x170) +#define QSERDES_COM_CLK_SELECT (0x174) +#define QSERDES_COM_HSCLK_SEL (0x178) +#define QSERDES_COM_INTEGLOOP_BINCODE_STATUS (0x17C) +#define QSERDES_COM_PLL_ANALOG (0x180) +#define QSERDES_COM_CORECLK_DIV (0x184) +#define QSERDES_COM_SW_RESET (0x188) +#define QSERDES_COM_CORE_CLK_EN (0x18C) +#define QSERDES_COM_C_READY_STATUS (0x190) +#define QSERDES_COM_CMN_CONFIG (0x194) +#define QSERDES_COM_CMN_RATE_OVERRIDE (0x198) +#define QSERDES_COM_SVS_MODE_CLK_SEL (0x19C) +#define QSERDES_COM_DEBUG_BUS0 (0x1A0) +#define QSERDES_COM_DEBUG_BUS1 (0x1A4) +#define QSERDES_COM_DEBUG_BUS2 (0x1A8) +#define QSERDES_COM_DEBUG_BUS3 (0x1AC) +#define QSERDES_COM_DEBUG_BUS_SEL (0x1B0) +#define QSERDES_COM_CMN_MISC1 (0x1B4) +#define QSERDES_COM_CMN_MISC2 (0x1B8) +#define QSERDES_COM_CORECLK_DIV_MODE1 (0x1BC) +#define QSERDES_COM_CORECLK_DIV_MODE2 (0x1C0) +#define QSERDES_COM_CMN_RSVD5 (0x1C0) + +/* Tx Channel base addresses */ +#define HDMI_TX_L0_BASE_OFFSET (0x400) +#define HDMI_TX_L1_BASE_OFFSET (0x600) +#define HDMI_TX_L2_BASE_OFFSET (0x800) +#define HDMI_TX_L3_BASE_OFFSET (0xA00) + +/* Tx Channel PHY registers */ +#define QSERDES_TX_L0_BIST_MODE_LANENO (0x000) +#define QSERDES_TX_L0_BIST_INVERT (0x004) +#define QSERDES_TX_L0_CLKBUF_ENABLE (0x008) +#define QSERDES_TX_L0_CMN_CONTROL_ONE (0x00C) +#define QSERDES_TX_L0_CMN_CONTROL_TWO (0x010) +#define QSERDES_TX_L0_CMN_CONTROL_THREE (0x014) +#define QSERDES_TX_L0_TX_EMP_POST1_LVL (0x018) +#define QSERDES_TX_L0_TX_POST2_EMPH (0x01C) +#define QSERDES_TX_L0_TX_BOOST_LVL_UP_DN (0x020) +#define QSERDES_TX_L0_HP_PD_ENABLES (0x024) +#define QSERDES_TX_L0_TX_IDLE_LVL_LARGE_AMP (0x028) +#define QSERDES_TX_L0_TX_DRV_LVL (0x02C) +#define QSERDES_TX_L0_TX_DRV_LVL_OFFSET (0x030) +#define QSERDES_TX_L0_RESET_TSYNC_EN (0x034) +#define QSERDES_TX_L0_PRE_STALL_LDO_BOOST_EN (0x038) +#define QSERDES_TX_L0_TX_BAND (0x03C) +#define QSERDES_TX_L0_SLEW_CNTL (0x040) +#define QSERDES_TX_L0_INTERFACE_SELECT (0x044) +#define QSERDES_TX_L0_LPB_EN (0x048) +#define QSERDES_TX_L0_RES_CODE_LANE_TX (0x04C) +#define QSERDES_TX_L0_RES_CODE_LANE_RX (0x050) +#define QSERDES_TX_L0_RES_CODE_LANE_OFFSET (0x054) +#define QSERDES_TX_L0_PERL_LENGTH1 (0x058) +#define QSERDES_TX_L0_PERL_LENGTH2 (0x05C) +#define QSERDES_TX_L0_SERDES_BYP_EN_OUT (0x060) +#define QSERDES_TX_L0_DEBUG_BUS_SEL (0x064) +#define QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN (0x068) +#define QSERDES_TX_L0_TX_POL_INV (0x06C) +#define QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN (0x070) +#define QSERDES_TX_L0_BIST_PATTERN1 (0x074) +#define QSERDES_TX_L0_BIST_PATTERN2 (0x078) +#define QSERDES_TX_L0_BIST_PATTERN3 (0x07C) +#define QSERDES_TX_L0_BIST_PATTERN4 (0x080) +#define QSERDES_TX_L0_BIST_PATTERN5 (0x084) +#define QSERDES_TX_L0_BIST_PATTERN6 (0x088) +#define QSERDES_TX_L0_BIST_PATTERN7 (0x08C) +#define QSERDES_TX_L0_BIST_PATTERN8 (0x090) +#define QSERDES_TX_L0_LANE_MODE (0x094) +#define QSERDES_TX_L0_IDAC_CAL_LANE_MODE (0x098) +#define QSERDES_TX_L0_IDAC_CAL_LANE_MODE_CONFIGURATION (0x09C) +#define QSERDES_TX_L0_ATB_SEL1 (0x0A0) +#define QSERDES_TX_L0_ATB_SEL2 (0x0A4) +#define QSERDES_TX_L0_RCV_DETECT_LVL (0x0A8) +#define QSERDES_TX_L0_RCV_DETECT_LVL_2 (0x0AC) +#define QSERDES_TX_L0_PRBS_SEED1 (0x0B0) +#define QSERDES_TX_L0_PRBS_SEED2 (0x0B4) +#define QSERDES_TX_L0_PRBS_SEED3 (0x0B8) +#define QSERDES_TX_L0_PRBS_SEED4 (0x0BC) +#define QSERDES_TX_L0_RESET_GEN (0x0C0) +#define QSERDES_TX_L0_RESET_GEN_MUXES (0x0C4) +#define QSERDES_TX_L0_TRAN_DRVR_EMP_EN (0x0C8) +#define QSERDES_TX_L0_TX_INTERFACE_MODE (0x0CC) +#define QSERDES_TX_L0_PWM_CTRL (0x0D0) +#define QSERDES_TX_L0_PWM_ENCODED_OR_DATA (0x0D4) +#define QSERDES_TX_L0_PWM_GEAR_1_DIVIDER_BAND2 (0x0D8) +#define QSERDES_TX_L0_PWM_GEAR_2_DIVIDER_BAND2 (0x0DC) +#define QSERDES_TX_L0_PWM_GEAR_3_DIVIDER_BAND2 (0x0E0) +#define QSERDES_TX_L0_PWM_GEAR_4_DIVIDER_BAND2 (0x0E4) +#define QSERDES_TX_L0_PWM_GEAR_1_DIVIDER_BAND0_1 (0x0E8) +#define QSERDES_TX_L0_PWM_GEAR_2_DIVIDER_BAND0_1 (0x0EC) +#define QSERDES_TX_L0_PWM_GEAR_3_DIVIDER_BAND0_1 (0x0F0) +#define QSERDES_TX_L0_PWM_GEAR_4_DIVIDER_BAND0_1 (0x0F4) +#define QSERDES_TX_L0_VMODE_CTRL1 (0x0F8) +#define QSERDES_TX_L0_VMODE_CTRL2 (0x0FC) +#define QSERDES_TX_L0_TX_ALOG_INTF_OBSV_CNTL (0x100) +#define QSERDES_TX_L0_BIST_STATUS (0x104) +#define QSERDES_TX_L0_BIST_ERROR_COUNT1 (0x108) +#define QSERDES_TX_L0_BIST_ERROR_COUNT2 (0x10C) +#define QSERDES_TX_L0_TX_ALOG_INTF_OBSV (0x110) + +/* HDMI PHY REGISTERS */ +#define HDMI_PHY_BASE_OFFSET (0xC00) + +#define HDMI_PHY_CFG (0x00) +#define HDMI_PHY_PD_CTL (0x04) +#define HDMI_PHY_MODE (0x08) +#define HDMI_PHY_MISR_CLEAR (0x0C) +#define HDMI_PHY_TX0_TX1_BIST_CFG0 (0x10) +#define HDMI_PHY_TX0_TX1_BIST_CFG1 (0x14) +#define HDMI_PHY_TX0_TX1_PRBS_SEED_BYTE0 (0x18) +#define HDMI_PHY_TX0_TX1_PRBS_SEED_BYTE1 (0x1C) +#define HDMI_PHY_TX0_TX1_BIST_PATTERN0 (0x20) +#define HDMI_PHY_TX0_TX1_BIST_PATTERN1 (0x24) +#define HDMI_PHY_TX2_TX3_BIST_CFG0 (0x28) +#define HDMI_PHY_TX2_TX3_BIST_CFG1 (0x2C) +#define HDMI_PHY_TX2_TX3_PRBS_SEED_BYTE0 (0x30) +#define HDMI_PHY_TX2_TX3_PRBS_SEED_BYTE1 (0x34) +#define HDMI_PHY_TX2_TX3_BIST_PATTERN0 (0x38) +#define HDMI_PHY_TX2_TX3_BIST_PATTERN1 (0x3C) +#define HDMI_PHY_DEBUG_BUS_SEL (0x40) +#define HDMI_PHY_TXCAL_CFG0 (0x44) +#define HDMI_PHY_TXCAL_CFG1 (0x48) +#define HDMI_PHY_TX0_TX1_LANE_CTL (0x4C) +#define HDMI_PHY_TX2_TX3_LANE_CTL (0x50) +#define HDMI_PHY_LANE_BIST_CONFIG (0x54) +#define HDMI_PHY_CLOCK (0x58) +#define HDMI_PHY_MISC1 (0x5C) +#define HDMI_PHY_MISC2 (0x60) +#define HDMI_PHY_TX0_TX1_BIST_STATUS0 (0x64) +#define HDMI_PHY_TX0_TX1_BIST_STATUS1 (0x68) +#define HDMI_PHY_TX0_TX1_BIST_STATUS2 (0x6C) +#define HDMI_PHY_TX2_TX3_BIST_STATUS0 (0x70) +#define HDMI_PHY_TX2_TX3_BIST_STATUS1 (0x74) +#define HDMI_PHY_TX2_TX3_BIST_STATUS2 (0x78) +#define HDMI_PHY_PRE_MISR_STATUS0 (0x7C) +#define HDMI_PHY_PRE_MISR_STATUS1 (0x80) +#define HDMI_PHY_PRE_MISR_STATUS2 (0x84) +#define HDMI_PHY_PRE_MISR_STATUS3 (0x88) +#define HDMI_PHY_POST_MISR_STATUS0 (0x8C) +#define HDMI_PHY_POST_MISR_STATUS1 (0x90) +#define HDMI_PHY_POST_MISR_STATUS2 (0x94) +#define HDMI_PHY_POST_MISR_STATUS3 (0x98) +#define HDMI_PHY_STATUS (0x9C) +#define HDMI_PHY_MISC3_STATUS (0xA0) +#define HDMI_PHY_MISC4_STATUS (0xA4) +#define HDMI_PHY_DEBUG_BUS0 (0xA8) +#define HDMI_PHY_DEBUG_BUS1 (0xAC) +#define HDMI_PHY_DEBUG_BUS2 (0xB0) +#define HDMI_PHY_DEBUG_BUS3 (0xB4) +#define HDMI_PHY_PHY_REVISION_ID0 (0xB8) +#define HDMI_PHY_PHY_REVISION_ID1 (0xBC) +#define HDMI_PHY_PHY_REVISION_ID2 (0xC0) +#define HDMI_PHY_PHY_REVISION_ID3 (0xC4) + +#define HDMI_PLL_POLL_MAX_READS 100 +#define HDMI_PLL_POLL_TIMEOUT_US 1500 + +enum hdmi_pll_freqs { + HDMI_PCLK_25200_KHZ, + HDMI_PCLK_27027_KHZ, + HDMI_PCLK_27000_KHZ, + HDMI_PCLK_74250_KHZ, + HDMI_PCLK_148500_KHZ, + HDMI_PCLK_154000_KHZ, + HDMI_PCLK_268500_KHZ, + HDMI_PCLK_297000_KHZ, + HDMI_PCLK_594000_KHZ, + HDMI_PCLK_MAX +}; + +struct hdmi_8996_phy_pll_reg_cfg { + u32 tx_l0_lane_mode; + u32 tx_l2_lane_mode; + u32 tx_l0_tx_band; + u32 tx_l1_tx_band; + u32 tx_l2_tx_band; + u32 tx_l3_tx_band; + u32 com_svs_mode_clk_sel; + u32 com_hsclk_sel; + u32 com_pll_cctrl_mode0; + u32 com_pll_rctrl_mode0; + u32 com_cp_ctrl_mode0; + u32 com_dec_start_mode0; + u32 com_div_frac_start1_mode0; + u32 com_div_frac_start2_mode0; + u32 com_div_frac_start3_mode0; + u32 com_integloop_gain0_mode0; + u32 com_integloop_gain1_mode0; + u32 com_lock_cmp_en; + u32 com_lock_cmp1_mode0; + u32 com_lock_cmp2_mode0; + u32 com_lock_cmp3_mode0; + u32 com_core_clk_en; + u32 com_coreclk_div; + u32 com_restrim_ctrl; + u32 com_vco_tune_ctrl; + + u32 tx_l0_tx_drv_lvl; + u32 tx_l0_tx_emp_post1_lvl; + u32 tx_l1_tx_drv_lvl; + u32 tx_l1_tx_emp_post1_lvl; + u32 tx_l2_tx_drv_lvl; + u32 tx_l2_tx_emp_post1_lvl; + u32 tx_l3_tx_drv_lvl; + u32 tx_l3_tx_emp_post1_lvl; + u32 tx_l0_vmode_ctrl1; + u32 tx_l0_vmode_ctrl2; + u32 tx_l1_vmode_ctrl1; + u32 tx_l1_vmode_ctrl2; + u32 tx_l2_vmode_ctrl1; + u32 tx_l2_vmode_ctrl2; + u32 tx_l3_vmode_ctrl1; + u32 tx_l3_vmode_ctrl2; + u32 tx_l0_res_code_lane_tx; + u32 tx_l1_res_code_lane_tx; + u32 tx_l2_res_code_lane_tx; + u32 tx_l3_res_code_lane_tx; + + u32 phy_mode; +}; + +struct hdmi_8996_v3_post_divider { + u64 vco_freq; + u64 hsclk_divsel; + u64 vco_ratio; + u64 tx_band_sel; + u64 half_rate_mode; +}; + +static inline struct hdmi_pll_vco_clk *to_hdmi_8996_vco_clk(struct clk *clk) +{ + return container_of(clk, struct hdmi_pll_vco_clk, c); +} + +static inline u64 hdmi_8996_v1_get_post_div_lt_2g(u64 bclk) +{ + if (bclk >= HDMI_2400MHZ_BIT_CLK_HZ) + return 2; + else if (bclk >= HDMI_1700MHZ_BIT_CLK_HZ) + return 3; + else if (bclk >= HDMI_1200MHZ_BIT_CLK_HZ) + return 4; + else if (bclk >= HDMI_850MHZ_BIT_CLK_HZ) + return 3; + else if (bclk >= HDMI_600MHZ_BIT_CLK_HZ) + return 4; + else if (bclk >= HDMI_450MHZ_BIT_CLK_HZ) + return 3; + else if (bclk >= HDMI_300MHZ_BIT_CLK_HZ) + return 4; + + return HDMI_64B_ERR_VAL; +} + +static inline u64 hdmi_8996_v2_get_post_div_lt_2g(u64 bclk, u64 vco_range) +{ + u64 hdmi_8ghz = vco_range; + u64 tmp_calc; + + hdmi_8ghz <<= 2; + tmp_calc = hdmi_8ghz; + do_div(tmp_calc, 6U); + + if (bclk >= vco_range) + return 2; + else if (bclk >= tmp_calc) + return 3; + else if (bclk >= vco_range >> 1) + return 4; + + tmp_calc = hdmi_8ghz; + do_div(tmp_calc, 12U); + if (bclk >= tmp_calc) + return 3; + else if (bclk >= vco_range >> 2) + return 4; + + tmp_calc = hdmi_8ghz; + do_div(tmp_calc, 24U); + if (bclk >= tmp_calc) + return 3; + else if (bclk >= vco_range >> 3) + return 4; + + return HDMI_64B_ERR_VAL; +} + +static inline u64 hdmi_8996_v2_get_post_div_gt_2g(u64 hsclk) +{ + if (hsclk >= 0 && hsclk <= 3) + return hsclk + 1; + + return HDMI_64B_ERR_VAL; +} + +static inline u64 hdmi_8996_get_coreclk_div_lt_2g(u64 bclk) +{ + if (bclk >= HDMI_1334MHZ_BIT_CLK_HZ) + return 1; + else if (bclk >= HDMI_1000MHZ_BIT_CLK_HZ) + return 1; + else if (bclk >= HDMI_667MHZ_BIT_CLK_HZ) + return 2; + else if (bclk >= HDMI_500MHZ_BIT_CLK_HZ) + return 2; + else if (bclk >= HDMI_334MHZ_BIT_CLK_HZ) + return 3; + else if (bclk >= HDMI_250MHZ_BIT_CLK_HZ) + return 3; + + return HDMI_64B_ERR_VAL; +} + +static inline u64 hdmi_8996_get_coreclk_div_ratio(u64 clks_pll_divsel, + u64 coreclk_div) +{ + if (clks_pll_divsel == 0) + return coreclk_div*2; + else if (clks_pll_divsel == 1) + return coreclk_div*4; + + return HDMI_64B_ERR_VAL; +} + +static inline u64 hdmi_8996_v1_get_tx_band(u64 bclk) +{ + if (bclk >= 2400000000UL) + return 0; + if (bclk >= 1200000000UL) + return 1; + if (bclk >= 600000000UL) + return 2; + if (bclk >= 300000000UL) + return 3; + + return HDMI_64B_ERR_VAL; +} + +static inline u64 hdmi_8996_v2_get_tx_band(u64 bclk, u64 vco_range) +{ + if (bclk >= vco_range) + return 0; + else if (bclk >= vco_range >> 1) + return 1; + else if (bclk >= vco_range >> 2) + return 2; + else if (bclk >= vco_range >> 3) + return 3; + + return HDMI_64B_ERR_VAL; +} + +static inline u64 hdmi_8996_v1_get_hsclk(u64 fdata) +{ + if (fdata >= 9600000000UL) + return 0; + else if (fdata >= 4800000000UL) + return 1; + else if (fdata >= 3200000000UL) + return 2; + else if (fdata >= 2400000000UL) + return 3; + + return HDMI_64B_ERR_VAL; +} + +static inline u64 hdmi_8996_v2_get_hsclk(u64 fdata, u64 vco_range) +{ + u64 tmp_calc = vco_range; + + tmp_calc <<= 2; + do_div(tmp_calc, 3U); + if (fdata >= (vco_range << 2)) + return 0; + else if (fdata >= (vco_range << 1)) + return 1; + else if (fdata >= tmp_calc) + return 2; + else if (fdata >= vco_range) + return 3; + + return HDMI_64B_ERR_VAL; + +} + +static inline u64 hdmi_8996_v2_get_vco_freq(u64 bclk, u64 vco_range) +{ + u64 tx_band_div_ratio = 1U << hdmi_8996_v2_get_tx_band(bclk, vco_range); + u64 pll_post_div_ratio; + + if (bclk >= vco_range) { + u64 hsclk = hdmi_8996_v2_get_hsclk(bclk, vco_range); + + pll_post_div_ratio = hdmi_8996_v2_get_post_div_gt_2g(hsclk); + } else { + pll_post_div_ratio = hdmi_8996_v2_get_post_div_lt_2g(bclk, + vco_range); + } + + return bclk * (pll_post_div_ratio * tx_band_div_ratio); +} + +static inline u64 hdmi_8996_v2_get_fdata(u64 bclk, u64 vco_range) +{ + if (bclk >= vco_range) + return bclk; + + u64 tmp_calc = hdmi_8996_v2_get_vco_freq(bclk, vco_range); + u64 pll_post_div_ratio_lt_2g = hdmi_8996_v2_get_post_div_lt_2g( + bclk, vco_range); + if (pll_post_div_ratio_lt_2g == HDMI_64B_ERR_VAL) + return HDMI_64B_ERR_VAL; + + do_div(tmp_calc, pll_post_div_ratio_lt_2g); + return tmp_calc; +} + +static inline u64 hdmi_8996_get_cpctrl(u64 frac_start, bool gen_ssc) +{ + if ((frac_start != 0) || + (gen_ssc == true)) + /* + * This should be ROUND(11/(19.2/20))). + * Since ref clock does not change, hardcoding to 11 + */ + return 0xB; + + return 0x23; +} + +static inline u64 hdmi_8996_get_rctrl(u64 frac_start, bool gen_ssc) +{ + if ((frac_start != 0) || (gen_ssc == true)) + return 0x16; + + return 0x10; +} + +static inline u64 hdmi_8996_get_cctrl(u64 frac_start, bool gen_ssc) +{ + if ((frac_start != 0) || (gen_ssc == true)) + return 0x28; + + return 0x1; +} + +static inline u64 hdmi_8996_get_integloop_gain(u64 frac_start, bool gen_ssc) +{ + if ((frac_start != 0) || (gen_ssc == true)) + return 0x80; + + return 0xC4; +} + +static inline u64 hdmi_8996_v3_get_integloop_gain(u64 frac_start, u64 bclk, + bool gen_ssc) +{ + u64 digclk_divsel = bclk >= HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2; + u64 base = ((frac_start != 0) || (gen_ssc == true)) ? 0x40 : 0xC4; + + base <<= digclk_divsel; + + return (base <= 2046 ? base : 0x7FE); +} + +static inline u64 hdmi_8996_get_vco_tune(u64 fdata, u64 div) +{ + u64 vco_tune; + + vco_tune = fdata * div; + do_div(vco_tune, 1000000); + vco_tune = 13000 - vco_tune - 256; + do_div(vco_tune, 5); + + return vco_tune; +} + +static inline u64 hdmi_8996_get_pll_cmp(u64 pll_cmp_cnt, u64 core_clk) +{ + u64 pll_cmp; + u64 rem; + + pll_cmp = pll_cmp_cnt * core_clk; + rem = do_div(pll_cmp, HDMI_REF_CLOCK); + if (rem > (HDMI_REF_CLOCK >> 1)) + pll_cmp++; + pll_cmp -= 1; + + return pll_cmp; +} + +static inline u64 hdmi_8996_v3_get_pll_cmp(u64 pll_cmp_cnt, u64 fdata) +{ + u64 dividend = pll_cmp_cnt * fdata; + u64 divisor = HDMI_REF_CLOCK * 10; + u64 rem; + + rem = do_div(dividend, divisor); + if (rem > (divisor >> 1)) + dividend++; + + return dividend - 1; +} + +static int hdmi_8996_v3_get_post_div(struct hdmi_8996_v3_post_divider *pd, + u64 bclk) +{ + u32 ratio[] = {2, 3, 4, 5, 6, 9, 10, 12, 14, 15, 20, 21, 25, 28, 35}; + u32 tx_band_sel[] = {0, 1, 2, 3}; + u64 vco_freq[60]; + u64 vco, vco_optimal, half_rate_mode = 0; + int vco_optimal_index, vco_freq_index; + int i, j, k, x; + + for (i = 0; i <= 1; i++) { + vco_optimal = HDMI_VCO_MAX_FREQ; + vco_optimal_index = -1; + vco_freq_index = 0; + for (j = 0; j < 15; j++) { + for (k = 0; k < 4; k++) { + u64 ratio_mult = ratio[j] << tx_band_sel[k]; + + vco = bclk >> half_rate_mode; + vco *= ratio_mult; + vco_freq[vco_freq_index++] = vco; + } + } + + for (x = 0; x < 60; x++) { + u64 vco_tmp = vco_freq[x]; + + if ((vco_tmp >= HDMI_VCO_MIN_FREQ) && + (vco_tmp <= vco_optimal)) { + vco_optimal = vco_tmp; + vco_optimal_index = x; + } + } + + if (vco_optimal_index == -1) { + if (!half_rate_mode) + half_rate_mode++; + else + return -EINVAL; + } else { + pd->vco_freq = vco_optimal; + pd->tx_band_sel = tx_band_sel[vco_optimal_index % 4]; + pd->vco_ratio = ratio[vco_optimal_index / 4]; + break; + } + } + + switch (pd->vco_ratio) { + case 2: + pd->hsclk_divsel = 0; + break; + case 3: + pd->hsclk_divsel = 4; + break; + case 4: + pd->hsclk_divsel = 8; + break; + case 5: + pd->hsclk_divsel = 12; + break; + case 6: + pd->hsclk_divsel = 1; + break; + case 9: + pd->hsclk_divsel = 5; + break; + case 10: + pd->hsclk_divsel = 2; + break; + case 12: + pd->hsclk_divsel = 9; + break; + case 14: + pd->hsclk_divsel = 3; + break; + case 15: + pd->hsclk_divsel = 13; + break; + case 20: + pd->hsclk_divsel = 10; + break; + case 21: + pd->hsclk_divsel = 7; + break; + case 25: + pd->hsclk_divsel = 14; + break; + case 28: + pd->hsclk_divsel = 11; + break; + case 35: + pd->hsclk_divsel = 15; + break; + }; + + return 0; +} + +static int hdmi_8996_v1_calculate(u32 pix_clk, + struct hdmi_8996_phy_pll_reg_cfg *cfg) +{ + int rc = -EINVAL; + u64 fdata, clk_divtx, tmds_clk; + u64 bclk; + u64 post_div_gt_2g; + u64 post_div_lt_2g; + u64 coreclk_div1_lt_2g; + u64 core_clk_div_ratio; + u64 core_clk; + u64 pll_cmp; + u64 tx_band; + u64 tx_band_div_ratio; + u64 hsclk; + u64 dec_start; + u64 frac_start; + u64 pll_divisor = 4 * HDMI_REF_CLOCK; + u64 cpctrl; + u64 rctrl; + u64 cctrl; + u64 integloop_gain; + u64 vco_tune; + u64 vco_freq; + u64 rem; + + /* FDATA, CLK_DIVTX, PIXEL_CLK, TMDS_CLK */ + bclk = ((u64)pix_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO; + + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) + tmds_clk = bclk/4; + else + tmds_clk = bclk; + + post_div_lt_2g = hdmi_8996_v1_get_post_div_lt_2g(bclk); + if (post_div_lt_2g == HDMI_64B_ERR_VAL) + goto fail; + + coreclk_div1_lt_2g = hdmi_8996_get_coreclk_div_lt_2g(bclk); + + core_clk_div_ratio = hdmi_8996_get_coreclk_div_ratio( + HDMI_CLKS_PLL_DIVSEL, HDMI_CORECLK_DIV); + + tx_band = hdmi_8996_v1_get_tx_band(bclk); + if (tx_band == HDMI_64B_ERR_VAL) + goto fail; + + tx_band_div_ratio = 1 << tx_band; + + if (bclk >= HDMI_2400MHZ_BIT_CLK_HZ) { + fdata = bclk; + hsclk = hdmi_8996_v1_get_hsclk(fdata); + if (hsclk == HDMI_64B_ERR_VAL) + goto fail; + + post_div_gt_2g = (hsclk <= 3) ? (hsclk + 1) : HDMI_64B_ERR_VAL; + if (post_div_gt_2g == HDMI_64B_ERR_VAL) + goto fail; + + vco_freq = bclk * (post_div_gt_2g * tx_band_div_ratio); + clk_divtx = vco_freq; + do_div(clk_divtx, post_div_gt_2g); + } else { + vco_freq = bclk * (post_div_lt_2g * tx_band_div_ratio); + fdata = vco_freq; + do_div(fdata, post_div_lt_2g); + hsclk = hdmi_8996_v1_get_hsclk(fdata); + if (hsclk == HDMI_64B_ERR_VAL) + goto fail; + + clk_divtx = vco_freq; + do_div(clk_divtx, post_div_lt_2g); + post_div_gt_2g = (hsclk <= 3) ? (hsclk + 1) : HDMI_64B_ERR_VAL; + if (post_div_gt_2g == HDMI_64B_ERR_VAL) + goto fail; + } + + /* Decimal and fraction values */ + dec_start = fdata * post_div_gt_2g; + do_div(dec_start, pll_divisor); + frac_start = ((pll_divisor - (((dec_start + 1) * pll_divisor) - + (fdata * post_div_gt_2g))) * (1 << 20)); + rem = do_div(frac_start, pll_divisor); + /* Round off frac_start to closest integer */ + if (rem >= (pll_divisor >> 1)) + frac_start++; + + cpctrl = hdmi_8996_get_cpctrl(frac_start, false); + rctrl = hdmi_8996_get_rctrl(frac_start, false); + cctrl = hdmi_8996_get_cctrl(frac_start, false); + integloop_gain = hdmi_8996_get_integloop_gain(frac_start, false); + vco_tune = hdmi_8996_get_vco_tune(fdata, post_div_gt_2g); + + core_clk = clk_divtx; + do_div(core_clk, core_clk_div_ratio); + pll_cmp = hdmi_8996_get_pll_cmp(1024, core_clk); + + /* Debug dump */ + DEV_DBG("%s: VCO freq: %llu\n", __func__, vco_freq); + DEV_DBG("%s: fdata: %llu\n", __func__, fdata); + DEV_DBG("%s: CLK_DIVTX: %llu\n", __func__, clk_divtx); + DEV_DBG("%s: pix_clk: %d\n", __func__, pix_clk); + DEV_DBG("%s: tmds clk: %llu\n", __func__, tmds_clk); + DEV_DBG("%s: HSCLK_SEL: %llu\n", __func__, hsclk); + DEV_DBG("%s: DEC_START: %llu\n", __func__, dec_start); + DEV_DBG("%s: DIV_FRAC_START: %llu\n", __func__, frac_start); + DEV_DBG("%s: PLL_CPCTRL: %llu\n", __func__, cpctrl); + DEV_DBG("%s: PLL_RCTRL: %llu\n", __func__, rctrl); + DEV_DBG("%s: PLL_CCTRL: %llu\n", __func__, cctrl); + DEV_DBG("%s: INTEGLOOP_GAIN: %llu\n", __func__, integloop_gain); + DEV_DBG("%s: VCO_TUNE: %llu\n", __func__, vco_tune); + DEV_DBG("%s: TX_BAND: %llu\n", __func__, tx_band); + DEV_DBG("%s: PLL_CMP: %llu\n", __func__, pll_cmp); + + /* Convert these values to register specific values */ + cfg->tx_l0_lane_mode = 0x3; + cfg->tx_l2_lane_mode = 0x3; + cfg->tx_l0_tx_band = tx_band + 4; + cfg->tx_l1_tx_band = tx_band + 4; + cfg->tx_l2_tx_band = tx_band + 4; + cfg->tx_l3_tx_band = tx_band + 4; + cfg->tx_l0_res_code_lane_tx = 0x33; + cfg->tx_l1_res_code_lane_tx = 0x33; + cfg->tx_l2_res_code_lane_tx = 0x33; + cfg->tx_l3_res_code_lane_tx = 0x33; + cfg->com_restrim_ctrl = 0x0; + cfg->com_vco_tune_ctrl = 0x1C; + + cfg->com_svs_mode_clk_sel = + (bclk >= HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2); + cfg->com_hsclk_sel = (0x28 | hsclk); + cfg->com_pll_cctrl_mode0 = cctrl; + cfg->com_pll_rctrl_mode0 = rctrl; + cfg->com_cp_ctrl_mode0 = cpctrl; + cfg->com_dec_start_mode0 = dec_start; + cfg->com_div_frac_start1_mode0 = (frac_start & 0xFF); + cfg->com_div_frac_start2_mode0 = ((frac_start & 0xFF00) >> 8); + cfg->com_div_frac_start3_mode0 = ((frac_start & 0xF0000) >> 16); + cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xFF); + cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xF00) >> 8); + cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xFF); + cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xFF00) >> 8); + cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16); + cfg->com_core_clk_en = (0x6C | (HDMI_CLKS_PLL_DIVSEL << 4)); + cfg->com_coreclk_div = HDMI_CORECLK_DIV; + + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) { + cfg->tx_l0_tx_drv_lvl = 0x25; + cfg->tx_l0_tx_emp_post1_lvl = 0x23; + cfg->tx_l1_tx_drv_lvl = 0x25; + cfg->tx_l1_tx_emp_post1_lvl = 0x23; + cfg->tx_l2_tx_drv_lvl = 0x25; + cfg->tx_l2_tx_emp_post1_lvl = 0x23; + cfg->tx_l3_tx_drv_lvl = 0x22; + cfg->tx_l3_tx_emp_post1_lvl = 0x27; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0D; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0D; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0D; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x00; + cfg->com_restrim_ctrl = 0x0; + } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) { + cfg->tx_l0_tx_drv_lvl = 0x25; + cfg->tx_l0_tx_emp_post1_lvl = 0x23; + cfg->tx_l1_tx_drv_lvl = 0x25; + cfg->tx_l1_tx_emp_post1_lvl = 0x23; + cfg->tx_l2_tx_drv_lvl = 0x25; + cfg->tx_l2_tx_emp_post1_lvl = 0x23; + cfg->tx_l3_tx_drv_lvl = 0x25; + cfg->tx_l3_tx_emp_post1_lvl = 0x23; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0D; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0D; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0D; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x00; + cfg->com_restrim_ctrl = 0x0; + } else { + cfg->tx_l0_tx_drv_lvl = 0x20; + cfg->tx_l0_tx_emp_post1_lvl = 0x20; + cfg->tx_l1_tx_drv_lvl = 0x20; + cfg->tx_l1_tx_emp_post1_lvl = 0x20; + cfg->tx_l2_tx_drv_lvl = 0x20; + cfg->tx_l2_tx_emp_post1_lvl = 0x20; + cfg->tx_l3_tx_drv_lvl = 0x20; + cfg->tx_l3_tx_emp_post1_lvl = 0x20; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0E; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0E; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0E; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x0E; + cfg->com_restrim_ctrl = 0xD8; + } + + cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0; + DEV_DBG("HDMI 8996 PLL: PLL Settings\n"); + DEV_DBG("PLL PARAM: tx_l0_lane_mode = 0x%x\n", cfg->tx_l0_lane_mode); + DEV_DBG("PLL PARAM: tx_l2_lane_mode = 0x%x\n", cfg->tx_l2_lane_mode); + DEV_DBG("PLL PARAM: tx_l0_tx_band = 0x%x\n", cfg->tx_l0_tx_band); + DEV_DBG("PLL PARAM: tx_l1_tx_band = 0x%x\n", cfg->tx_l1_tx_band); + DEV_DBG("PLL PARAM: tx_l2_tx_band = 0x%x\n", cfg->tx_l2_tx_band); + DEV_DBG("PLL PARAM: tx_l3_tx_band = 0x%x\n", cfg->tx_l3_tx_band); + DEV_DBG("PLL PARAM: com_svs_mode_clk_sel = 0x%x\n", + cfg->com_svs_mode_clk_sel); + DEV_DBG("PLL PARAM: com_hsclk_sel = 0x%x\n", cfg->com_hsclk_sel); + DEV_DBG("PLL PARAM: com_pll_cctrl_mode0 = 0x%x\n", + cfg->com_pll_cctrl_mode0); + DEV_DBG("PLL PARAM: com_pll_rctrl_mode0 = 0x%x\n", + cfg->com_pll_rctrl_mode0); + DEV_DBG("PLL PARAM: com_cp_ctrl_mode0 = 0x%x\n", + cfg->com_cp_ctrl_mode0); + DEV_DBG("PLL PARAM: com_dec_start_mode0 = 0x%x\n", + cfg->com_dec_start_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start1_mode0 = 0x%x\n", + cfg->com_div_frac_start1_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start2_mode0 = 0x%x\n", + cfg->com_div_frac_start2_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start3_mode0 = 0x%x\n", + cfg->com_div_frac_start3_mode0); + DEV_DBG("PLL PARAM: com_integloop_gain0_mode0 = 0x%x\n", + cfg->com_integloop_gain0_mode0); + DEV_DBG("PLL PARAM: com_integloop_gain1_mode0 = 0x%x\n", + cfg->com_integloop_gain1_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp1_mode0 = 0x%x\n", + cfg->com_lock_cmp1_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp2_mode0 = 0x%x\n", + cfg->com_lock_cmp2_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp3_mode0 = 0x%x\n", + cfg->com_lock_cmp3_mode0); + DEV_DBG("PLL PARAM: com_core_clk_en = 0x%x\n", cfg->com_core_clk_en); + DEV_DBG("PLL PARAM: com_coreclk_div = 0x%x\n", cfg->com_coreclk_div); + DEV_DBG("PLL PARAM: com_restrim_ctrl = 0x%x\n", cfg->com_restrim_ctrl); + + DEV_DBG("PLL PARAM: l0_tx_drv_lvl = 0x%x\n", cfg->tx_l0_tx_drv_lvl); + DEV_DBG("PLL PARAM: l0_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l0_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l1_tx_drv_lvl = 0x%x\n", cfg->tx_l1_tx_drv_lvl); + DEV_DBG("PLL PARAM: l1_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l1_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l2_tx_drv_lvl = 0x%x\n", cfg->tx_l2_tx_drv_lvl); + DEV_DBG("PLL PARAM: l2_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l2_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l3_tx_drv_lvl = 0x%x\n", cfg->tx_l3_tx_drv_lvl); + DEV_DBG("PLL PARAM: l3_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l3_tx_emp_post1_lvl); + + DEV_DBG("PLL PARAM: l0_vmode_ctrl1 = 0x%x\n", cfg->tx_l0_vmode_ctrl1); + DEV_DBG("PLL PARAM: l0_vmode_ctrl2 = 0x%x\n", cfg->tx_l0_vmode_ctrl2); + DEV_DBG("PLL PARAM: l1_vmode_ctrl1 = 0x%x\n", cfg->tx_l1_vmode_ctrl1); + DEV_DBG("PLL PARAM: l1_vmode_ctrl2 = 0x%x\n", cfg->tx_l1_vmode_ctrl2); + DEV_DBG("PLL PARAM: l2_vmode_ctrl1 = 0x%x\n", cfg->tx_l2_vmode_ctrl1); + DEV_DBG("PLL PARAM: l2_vmode_ctrl2 = 0x%x\n", cfg->tx_l2_vmode_ctrl2); + DEV_DBG("PLL PARAM: l3_vmode_ctrl1 = 0x%x\n", cfg->tx_l3_vmode_ctrl1); + DEV_DBG("PLL PARAM: l3_vmode_ctrl2 = 0x%x\n", cfg->tx_l3_vmode_ctrl2); + DEV_DBG("PLL PARAM: tx_l0_res_code_lane_tx = 0x%x\n", + cfg->tx_l0_res_code_lane_tx); + DEV_DBG("PLL PARAM: tx_l1_res_code_lane_tx = 0x%x\n", + cfg->tx_l1_res_code_lane_tx); + DEV_DBG("PLL PARAM: tx_l2_res_code_lane_tx = 0x%x\n", + cfg->tx_l2_res_code_lane_tx); + DEV_DBG("PLL PARAM: tx_l3_res_code_lane_tx = 0x%x\n", + cfg->tx_l3_res_code_lane_tx); + + DEV_DBG("PLL PARAM: phy_mode = 0x%x\n", cfg->phy_mode); + rc = 0; +fail: + return rc; +} + +static int hdmi_8996_v2_calculate(u32 pix_clk, + struct hdmi_8996_phy_pll_reg_cfg *cfg) +{ + int rc = -EINVAL; + u64 fdata, clk_divtx, tmds_clk; + u64 bclk; + u64 post_div; + u64 core_clk_div; + u64 core_clk_div_ratio; + u64 core_clk; + u64 pll_cmp; + u64 tx_band; + u64 tx_band_div_ratio; + u64 hsclk; + u64 dec_start; + u64 frac_start; + u64 pll_divisor = 4 * HDMI_REF_CLOCK; + u64 cpctrl; + u64 rctrl; + u64 cctrl; + u64 integloop_gain; + u64 vco_tune; + u64 vco_freq; + u64 vco_range; + u64 rem; + + /* FDATA, CLK_DIVTX, PIXEL_CLK, TMDS_CLK */ + bclk = ((u64)pix_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO; + + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) + tmds_clk = pix_clk >> 2; + else + tmds_clk = pix_clk; + + vco_range = bclk < HDMI_282MHZ_BIT_CLK_HZ ? HDMI_2000MHZ_BIT_CLK_HZ : + HDMI_2250MHZ_BIT_CLK_HZ; + + fdata = hdmi_8996_v2_get_fdata(bclk, vco_range); + if (fdata == HDMI_64B_ERR_VAL) + goto fail; + + hsclk = hdmi_8996_v2_get_hsclk(fdata, vco_range); + if (hsclk == HDMI_64B_ERR_VAL) + goto fail; + + if (bclk >= vco_range) + post_div = hdmi_8996_v2_get_post_div_gt_2g(hsclk); + else + post_div = hdmi_8996_v2_get_post_div_lt_2g(bclk, vco_range); + + if (post_div == HDMI_64B_ERR_VAL) + goto fail; + + core_clk_div = 5; + core_clk_div_ratio = core_clk_div * 2; + + tx_band = hdmi_8996_v2_get_tx_band(bclk, vco_range); + if (tx_band == HDMI_64B_ERR_VAL) + goto fail; + + tx_band_div_ratio = 1 << tx_band; + + vco_freq = hdmi_8996_v2_get_vco_freq(bclk, vco_range); + clk_divtx = vco_freq; + do_div(clk_divtx, post_div); + + /* Decimal and fraction values */ + dec_start = fdata * post_div; + do_div(dec_start, pll_divisor); + frac_start = ((pll_divisor - (((dec_start + 1) * pll_divisor) - + (fdata * post_div))) * (1 << 20)); + rem = do_div(frac_start, pll_divisor); + /* Round off frac_start to closest integer */ + if (rem >= (pll_divisor >> 1)) + frac_start++; + + cpctrl = hdmi_8996_get_cpctrl(frac_start, false); + rctrl = hdmi_8996_get_rctrl(frac_start, false); + cctrl = hdmi_8996_get_cctrl(frac_start, false); + integloop_gain = hdmi_8996_get_integloop_gain(frac_start, false); + vco_tune = hdmi_8996_get_vco_tune(fdata, post_div); + + core_clk = clk_divtx; + do_div(core_clk, core_clk_div_ratio); + pll_cmp = hdmi_8996_get_pll_cmp(1024, core_clk); + + /* Debug dump */ + DEV_DBG("%s: VCO freq: %llu\n", __func__, vco_freq); + DEV_DBG("%s: fdata: %llu\n", __func__, fdata); + DEV_DBG("%s: CLK_DIVTX: %llu\n", __func__, clk_divtx); + DEV_DBG("%s: pix_clk: %d\n", __func__, pix_clk); + DEV_DBG("%s: tmds clk: %llu\n", __func__, tmds_clk); + DEV_DBG("%s: HSCLK_SEL: %llu\n", __func__, hsclk); + DEV_DBG("%s: DEC_START: %llu\n", __func__, dec_start); + DEV_DBG("%s: DIV_FRAC_START: %llu\n", __func__, frac_start); + DEV_DBG("%s: PLL_CPCTRL: %llu\n", __func__, cpctrl); + DEV_DBG("%s: PLL_RCTRL: %llu\n", __func__, rctrl); + DEV_DBG("%s: PLL_CCTRL: %llu\n", __func__, cctrl); + DEV_DBG("%s: INTEGLOOP_GAIN: %llu\n", __func__, integloop_gain); + DEV_DBG("%s: VCO_TUNE: %llu\n", __func__, vco_tune); + DEV_DBG("%s: TX_BAND: %llu\n", __func__, tx_band); + DEV_DBG("%s: PLL_CMP: %llu\n", __func__, pll_cmp); + + /* Convert these values to register specific values */ + cfg->tx_l0_lane_mode = 0x3; + cfg->tx_l2_lane_mode = 0x3; + cfg->tx_l0_tx_band = tx_band + 4; + cfg->tx_l1_tx_band = tx_band + 4; + cfg->tx_l2_tx_band = tx_band + 4; + cfg->tx_l3_tx_band = tx_band + 4; + + if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD) + cfg->com_svs_mode_clk_sel = 1; + else + cfg->com_svs_mode_clk_sel = 2; + + cfg->com_hsclk_sel = (0x28 | hsclk); + cfg->com_pll_cctrl_mode0 = cctrl; + cfg->com_pll_rctrl_mode0 = rctrl; + cfg->com_cp_ctrl_mode0 = cpctrl; + cfg->com_dec_start_mode0 = dec_start; + cfg->com_div_frac_start1_mode0 = (frac_start & 0xFF); + cfg->com_div_frac_start2_mode0 = ((frac_start & 0xFF00) >> 8); + cfg->com_div_frac_start3_mode0 = ((frac_start & 0xF0000) >> 16); + cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xFF); + cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xF00) >> 8); + cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xFF); + cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xFF00) >> 8); + cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16); + cfg->com_core_clk_en = (0x6C | (HDMI_CLKS_PLL_DIVSEL << 4)); + cfg->com_coreclk_div = HDMI_CORECLK_DIV; + cfg->com_vco_tune_ctrl = 0x0; + + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) { + cfg->tx_l0_tx_drv_lvl = 0x25; + cfg->tx_l0_tx_emp_post1_lvl = 0x23; + cfg->tx_l1_tx_drv_lvl = 0x25; + cfg->tx_l1_tx_emp_post1_lvl = 0x23; + cfg->tx_l2_tx_drv_lvl = 0x25; + cfg->tx_l2_tx_emp_post1_lvl = 0x23; + cfg->tx_l3_tx_drv_lvl = 0x22; + cfg->tx_l3_tx_emp_post1_lvl = 0x27; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0D; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0D; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0D; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x00; + cfg->tx_l0_res_code_lane_tx = 0x3F; + cfg->tx_l1_res_code_lane_tx = 0x3F; + cfg->tx_l2_res_code_lane_tx = 0x3F; + cfg->tx_l3_res_code_lane_tx = 0x3F; + cfg->com_restrim_ctrl = 0x0; + } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) { + cfg->tx_l0_tx_drv_lvl = 0x25; + cfg->tx_l0_tx_emp_post1_lvl = 0x23; + cfg->tx_l1_tx_drv_lvl = 0x25; + cfg->tx_l1_tx_emp_post1_lvl = 0x23; + cfg->tx_l2_tx_drv_lvl = 0x25; + cfg->tx_l2_tx_emp_post1_lvl = 0x23; + cfg->tx_l3_tx_drv_lvl = 0x25; + cfg->tx_l3_tx_emp_post1_lvl = 0x23; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0D; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0D; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0D; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x00; + cfg->tx_l0_res_code_lane_tx = 0x39; + cfg->tx_l1_res_code_lane_tx = 0x39; + cfg->tx_l2_res_code_lane_tx = 0x39; + cfg->tx_l3_res_code_lane_tx = 0x39; + cfg->com_restrim_ctrl = 0x0; + } else { + cfg->tx_l0_tx_drv_lvl = 0x20; + cfg->tx_l0_tx_emp_post1_lvl = 0x20; + cfg->tx_l1_tx_drv_lvl = 0x20; + cfg->tx_l1_tx_emp_post1_lvl = 0x20; + cfg->tx_l2_tx_drv_lvl = 0x20; + cfg->tx_l2_tx_emp_post1_lvl = 0x20; + cfg->tx_l3_tx_drv_lvl = 0x20; + cfg->tx_l3_tx_emp_post1_lvl = 0x20; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0E; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0E; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0E; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x0E; + cfg->tx_l0_res_code_lane_tx = 0x3F; + cfg->tx_l1_res_code_lane_tx = 0x3F; + cfg->tx_l2_res_code_lane_tx = 0x3F; + cfg->tx_l3_res_code_lane_tx = 0x3F; + cfg->com_restrim_ctrl = 0xD8; + } + + cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0; + DEV_DBG("HDMI 8996 PLL: PLL Settings\n"); + DEV_DBG("PLL PARAM: tx_l0_lane_mode = 0x%x\n", cfg->tx_l0_lane_mode); + DEV_DBG("PLL PARAM: tx_l2_lane_mode = 0x%x\n", cfg->tx_l2_lane_mode); + DEV_DBG("PLL PARAM: tx_l0_tx_band = 0x%x\n", cfg->tx_l0_tx_band); + DEV_DBG("PLL PARAM: tx_l1_tx_band = 0x%x\n", cfg->tx_l1_tx_band); + DEV_DBG("PLL PARAM: tx_l2_tx_band = 0x%x\n", cfg->tx_l2_tx_band); + DEV_DBG("PLL PARAM: tx_l3_tx_band = 0x%x\n", cfg->tx_l3_tx_band); + DEV_DBG("PLL PARAM: com_svs_mode_clk_sel = 0x%x\n", + cfg->com_svs_mode_clk_sel); + DEV_DBG("PLL PARAM: com_vco_tune_ctrl = 0x%x\n", + cfg->com_vco_tune_ctrl); + DEV_DBG("PLL PARAM: com_hsclk_sel = 0x%x\n", cfg->com_hsclk_sel); + DEV_DBG("PLL PARAM: com_lock_cmp_en = 0x%x\n", cfg->com_lock_cmp_en); + DEV_DBG("PLL PARAM: com_pll_cctrl_mode0 = 0x%x\n", + cfg->com_pll_cctrl_mode0); + DEV_DBG("PLL PARAM: com_pll_rctrl_mode0 = 0x%x\n", + cfg->com_pll_rctrl_mode0); + DEV_DBG("PLL PARAM: com_cp_ctrl_mode0 = 0x%x\n", + cfg->com_cp_ctrl_mode0); + DEV_DBG("PLL PARAM: com_dec_start_mode0 = 0x%x\n", + cfg->com_dec_start_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start1_mode0 = 0x%x\n", + cfg->com_div_frac_start1_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start2_mode0 = 0x%x\n", + cfg->com_div_frac_start2_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start3_mode0 = 0x%x\n", + cfg->com_div_frac_start3_mode0); + DEV_DBG("PLL PARAM: com_integloop_gain0_mode0 = 0x%x\n", + cfg->com_integloop_gain0_mode0); + DEV_DBG("PLL PARAM: com_integloop_gain1_mode0 = 0x%x\n", + cfg->com_integloop_gain1_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp1_mode0 = 0x%x\n", + cfg->com_lock_cmp1_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp2_mode0 = 0x%x\n", + cfg->com_lock_cmp2_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp3_mode0 = 0x%x\n", + cfg->com_lock_cmp3_mode0); + DEV_DBG("PLL PARAM: com_core_clk_en = 0x%x\n", cfg->com_core_clk_en); + DEV_DBG("PLL PARAM: com_coreclk_div = 0x%x\n", cfg->com_coreclk_div); + + DEV_DBG("PLL PARAM: l0_tx_drv_lvl = 0x%x\n", cfg->tx_l0_tx_drv_lvl); + DEV_DBG("PLL PARAM: l0_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l0_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l1_tx_drv_lvl = 0x%x\n", cfg->tx_l1_tx_drv_lvl); + DEV_DBG("PLL PARAM: l1_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l1_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l2_tx_drv_lvl = 0x%x\n", cfg->tx_l2_tx_drv_lvl); + DEV_DBG("PLL PARAM: l2_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l2_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l3_tx_drv_lvl = 0x%x\n", cfg->tx_l3_tx_drv_lvl); + DEV_DBG("PLL PARAM: l3_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l3_tx_emp_post1_lvl); + + DEV_DBG("PLL PARAM: l0_vmode_ctrl1 = 0x%x\n", cfg->tx_l0_vmode_ctrl1); + DEV_DBG("PLL PARAM: l0_vmode_ctrl2 = 0x%x\n", cfg->tx_l0_vmode_ctrl2); + DEV_DBG("PLL PARAM: l1_vmode_ctrl1 = 0x%x\n", cfg->tx_l1_vmode_ctrl1); + DEV_DBG("PLL PARAM: l1_vmode_ctrl2 = 0x%x\n", cfg->tx_l1_vmode_ctrl2); + DEV_DBG("PLL PARAM: l2_vmode_ctrl1 = 0x%x\n", cfg->tx_l2_vmode_ctrl1); + DEV_DBG("PLL PARAM: l2_vmode_ctrl2 = 0x%x\n", cfg->tx_l2_vmode_ctrl2); + DEV_DBG("PLL PARAM: l3_vmode_ctrl1 = 0x%x\n", cfg->tx_l3_vmode_ctrl1); + DEV_DBG("PLL PARAM: l3_vmode_ctrl2 = 0x%x\n", cfg->tx_l3_vmode_ctrl2); + DEV_DBG("PLL PARAM: tx_l0_res_code_lane_tx = 0x%x\n", + cfg->tx_l0_res_code_lane_tx); + DEV_DBG("PLL PARAM: tx_l1_res_code_lane_tx = 0x%x\n", + cfg->tx_l1_res_code_lane_tx); + DEV_DBG("PLL PARAM: tx_l2_res_code_lane_tx = 0x%x\n", + cfg->tx_l2_res_code_lane_tx); + DEV_DBG("PLL PARAM: tx_l3_res_code_lane_tx = 0x%x\n", + cfg->tx_l3_res_code_lane_tx); + DEV_DBG("PLL PARAM: com_restrim_ctrl = 0x%x\n", cfg->com_restrim_ctrl); + + DEV_DBG("PLL PARAM: phy_mode = 0x%x\n", cfg->phy_mode); + rc = 0; +fail: + return rc; +} + +static int hdmi_8996_v3_calculate(u32 pix_clk, + struct hdmi_8996_phy_pll_reg_cfg *cfg) +{ + int rc = -EINVAL; + struct hdmi_8996_v3_post_divider pd; + u64 fdata, tmds_clk; + u64 bclk; + u64 pll_cmp; + u64 tx_band; + u64 hsclk; + u64 dec_start; + u64 frac_start; + u64 pll_divisor = 4 * HDMI_REF_CLOCK; + u64 cpctrl; + u64 rctrl; + u64 cctrl; + u64 integloop_gain; + u64 vco_freq; + u64 rem; + + /* FDATA, HSCLK, PIXEL_CLK, TMDS_CLK */ + bclk = ((u64)pix_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO; + + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) + tmds_clk = pix_clk >> 2; + else + tmds_clk = pix_clk; + + if (hdmi_8996_v3_get_post_div(&pd, bclk) || pd.vco_ratio <= 0 || + pd.vco_freq <= 0) + goto fail; + + vco_freq = pd.vco_freq; + fdata = pd.vco_freq; + do_div(fdata, pd.vco_ratio); + + hsclk = pd.hsclk_divsel; + dec_start = vco_freq; + do_div(dec_start, pll_divisor); + + frac_start = vco_freq * (1 << 20); + rem = do_div(frac_start, pll_divisor); + frac_start -= dec_start * (1 << 20); + if (rem > (pll_divisor >> 1)) + frac_start++; + + cpctrl = hdmi_8996_get_cpctrl(frac_start, false); + rctrl = hdmi_8996_get_rctrl(frac_start, false); + cctrl = hdmi_8996_get_cctrl(frac_start, false); + integloop_gain = hdmi_8996_v3_get_integloop_gain(frac_start, bclk, + false); + pll_cmp = hdmi_8996_v3_get_pll_cmp(1024, fdata); + tx_band = pd.tx_band_sel; + + /* Debug dump */ + DEV_DBG("%s: VCO freq: %llu\n", __func__, vco_freq); + DEV_DBG("%s: fdata: %llu\n", __func__, fdata); + DEV_DBG("%s: pix_clk: %d\n", __func__, pix_clk); + DEV_DBG("%s: tmds clk: %llu\n", __func__, tmds_clk); + DEV_DBG("%s: HSCLK_SEL: %llu\n", __func__, hsclk); + DEV_DBG("%s: DEC_START: %llu\n", __func__, dec_start); + DEV_DBG("%s: DIV_FRAC_START: %llu\n", __func__, frac_start); + DEV_DBG("%s: PLL_CPCTRL: %llu\n", __func__, cpctrl); + DEV_DBG("%s: PLL_RCTRL: %llu\n", __func__, rctrl); + DEV_DBG("%s: PLL_CCTRL: %llu\n", __func__, cctrl); + DEV_DBG("%s: INTEGLOOP_GAIN: %llu\n", __func__, integloop_gain); + DEV_DBG("%s: TX_BAND: %llu\n", __func__, tx_band); + DEV_DBG("%s: PLL_CMP: %llu\n", __func__, pll_cmp); + + /* Convert these values to register specific values */ + cfg->tx_l0_tx_band = tx_band + 4; + cfg->tx_l1_tx_band = tx_band + 4; + cfg->tx_l2_tx_band = tx_band + 4; + cfg->tx_l3_tx_band = tx_band + 4; + + if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD) + cfg->com_svs_mode_clk_sel = 1; + else + cfg->com_svs_mode_clk_sel = 2; + + cfg->com_hsclk_sel = (0x20 | hsclk); + cfg->com_pll_cctrl_mode0 = cctrl; + cfg->com_pll_rctrl_mode0 = rctrl; + cfg->com_cp_ctrl_mode0 = cpctrl; + cfg->com_dec_start_mode0 = dec_start; + cfg->com_div_frac_start1_mode0 = (frac_start & 0xFF); + cfg->com_div_frac_start2_mode0 = ((frac_start & 0xFF00) >> 8); + cfg->com_div_frac_start3_mode0 = ((frac_start & 0xF0000) >> 16); + cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xFF); + cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xF00) >> 8); + cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xFF); + cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xFF00) >> 8); + cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16); + cfg->com_lock_cmp_en = 0x04; + cfg->com_core_clk_en = 0x2C; + cfg->com_coreclk_div = HDMI_CORECLK_DIV; + cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0; + cfg->com_vco_tune_ctrl = 0x0; + + cfg->tx_l0_lane_mode = 0x43; + cfg->tx_l2_lane_mode = 0x43; + + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) { + cfg->tx_l0_tx_drv_lvl = 0x25; + cfg->tx_l0_tx_emp_post1_lvl = 0x23; + cfg->tx_l1_tx_drv_lvl = 0x25; + cfg->tx_l1_tx_emp_post1_lvl = 0x23; + cfg->tx_l2_tx_drv_lvl = 0x25; + cfg->tx_l2_tx_emp_post1_lvl = 0x23; + cfg->tx_l3_tx_drv_lvl = 0x22; + cfg->tx_l3_tx_emp_post1_lvl = 0x27; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0D; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0D; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0D; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x00; + } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) { + cfg->tx_l0_tx_drv_lvl = 0x25; + cfg->tx_l0_tx_emp_post1_lvl = 0x23; + cfg->tx_l1_tx_drv_lvl = 0x25; + cfg->tx_l1_tx_emp_post1_lvl = 0x23; + cfg->tx_l2_tx_drv_lvl = 0x25; + cfg->tx_l2_tx_emp_post1_lvl = 0x23; + cfg->tx_l3_tx_drv_lvl = 0x25; + cfg->tx_l3_tx_emp_post1_lvl = 0x23; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0D; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0D; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0D; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x00; + } else { + cfg->tx_l0_tx_drv_lvl = 0x20; + cfg->tx_l0_tx_emp_post1_lvl = 0x20; + cfg->tx_l1_tx_drv_lvl = 0x20; + cfg->tx_l1_tx_emp_post1_lvl = 0x20; + cfg->tx_l2_tx_drv_lvl = 0x20; + cfg->tx_l2_tx_emp_post1_lvl = 0x20; + cfg->tx_l3_tx_drv_lvl = 0x20; + cfg->tx_l3_tx_emp_post1_lvl = 0x20; + cfg->tx_l0_vmode_ctrl1 = 0x00; + cfg->tx_l0_vmode_ctrl2 = 0x0E; + cfg->tx_l1_vmode_ctrl1 = 0x00; + cfg->tx_l1_vmode_ctrl2 = 0x0E; + cfg->tx_l2_vmode_ctrl1 = 0x00; + cfg->tx_l2_vmode_ctrl2 = 0x0E; + cfg->tx_l3_vmode_ctrl1 = 0x00; + cfg->tx_l3_vmode_ctrl2 = 0x0E; + } + + DEV_DBG("HDMI 8996 PLL: PLL Settings\n"); + DEV_DBG("PLL PARAM: tx_l0_tx_band = 0x%x\n", cfg->tx_l0_tx_band); + DEV_DBG("PLL PARAM: tx_l1_tx_band = 0x%x\n", cfg->tx_l1_tx_band); + DEV_DBG("PLL PARAM: tx_l2_tx_band = 0x%x\n", cfg->tx_l2_tx_band); + DEV_DBG("PLL PARAM: tx_l3_tx_band = 0x%x\n", cfg->tx_l3_tx_band); + DEV_DBG("PLL PARAM: com_svs_mode_clk_sel = 0x%x\n", + cfg->com_svs_mode_clk_sel); + DEV_DBG("PLL PARAM: com_hsclk_sel = 0x%x\n", cfg->com_hsclk_sel); + DEV_DBG("PLL PARAM: com_lock_cmp_en = 0x%x\n", cfg->com_lock_cmp_en); + DEV_DBG("PLL PARAM: com_pll_cctrl_mode0 = 0x%x\n", + cfg->com_pll_cctrl_mode0); + DEV_DBG("PLL PARAM: com_pll_rctrl_mode0 = 0x%x\n", + cfg->com_pll_rctrl_mode0); + DEV_DBG("PLL PARAM: com_cp_ctrl_mode0 = 0x%x\n", + cfg->com_cp_ctrl_mode0); + DEV_DBG("PLL PARAM: com_dec_start_mode0 = 0x%x\n", + cfg->com_dec_start_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start1_mode0 = 0x%x\n", + cfg->com_div_frac_start1_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start2_mode0 = 0x%x\n", + cfg->com_div_frac_start2_mode0); + DEV_DBG("PLL PARAM: com_div_frac_start3_mode0 = 0x%x\n", + cfg->com_div_frac_start3_mode0); + DEV_DBG("PLL PARAM: com_integloop_gain0_mode0 = 0x%x\n", + cfg->com_integloop_gain0_mode0); + DEV_DBG("PLL PARAM: com_integloop_gain1_mode0 = 0x%x\n", + cfg->com_integloop_gain1_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp1_mode0 = 0x%x\n", + cfg->com_lock_cmp1_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp2_mode0 = 0x%x\n", + cfg->com_lock_cmp2_mode0); + DEV_DBG("PLL PARAM: com_lock_cmp3_mode0 = 0x%x\n", + cfg->com_lock_cmp3_mode0); + DEV_DBG("PLL PARAM: com_core_clk_en = 0x%x\n", cfg->com_core_clk_en); + DEV_DBG("PLL PARAM: com_coreclk_div = 0x%x\n", cfg->com_coreclk_div); + DEV_DBG("PLL PARAM: phy_mode = 0x%x\n", cfg->phy_mode); + + DEV_DBG("PLL PARAM: tx_l0_lane_mode = 0x%x\n", cfg->tx_l0_lane_mode); + DEV_DBG("PLL PARAM: tx_l2_lane_mode = 0x%x\n", cfg->tx_l2_lane_mode); + DEV_DBG("PLL PARAM: l0_tx_drv_lvl = 0x%x\n", cfg->tx_l0_tx_drv_lvl); + DEV_DBG("PLL PARAM: l0_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l0_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l1_tx_drv_lvl = 0x%x\n", cfg->tx_l1_tx_drv_lvl); + DEV_DBG("PLL PARAM: l1_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l1_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l2_tx_drv_lvl = 0x%x\n", cfg->tx_l2_tx_drv_lvl); + DEV_DBG("PLL PARAM: l2_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l2_tx_emp_post1_lvl); + DEV_DBG("PLL PARAM: l3_tx_drv_lvl = 0x%x\n", cfg->tx_l3_tx_drv_lvl); + DEV_DBG("PLL PARAM: l3_tx_emp_post1_lvl = 0x%x\n", + cfg->tx_l3_tx_emp_post1_lvl); + + DEV_DBG("PLL PARAM: l0_vmode_ctrl1 = 0x%x\n", cfg->tx_l0_vmode_ctrl1); + DEV_DBG("PLL PARAM: l0_vmode_ctrl2 = 0x%x\n", cfg->tx_l0_vmode_ctrl2); + DEV_DBG("PLL PARAM: l1_vmode_ctrl1 = 0x%x\n", cfg->tx_l1_vmode_ctrl1); + DEV_DBG("PLL PARAM: l1_vmode_ctrl2 = 0x%x\n", cfg->tx_l1_vmode_ctrl2); + DEV_DBG("PLL PARAM: l2_vmode_ctrl1 = 0x%x\n", cfg->tx_l2_vmode_ctrl1); + DEV_DBG("PLL PARAM: l2_vmode_ctrl2 = 0x%x\n", cfg->tx_l2_vmode_ctrl2); + DEV_DBG("PLL PARAM: l3_vmode_ctrl1 = 0x%x\n", cfg->tx_l3_vmode_ctrl1); + DEV_DBG("PLL PARAM: l3_vmode_ctrl2 = 0x%x\n", cfg->tx_l3_vmode_ctrl2); + rc = 0; +fail: + return rc; +} + +static int hdmi_8996_calculate(u32 pix_clk, + struct hdmi_8996_phy_pll_reg_cfg *cfg, u32 ver) +{ + switch (ver) { + case HDMI_VERSION_8996_V3: + case HDMI_VERSION_8996_V3_1_8: + return hdmi_8996_v3_calculate(pix_clk, cfg); + case HDMI_VERSION_8996_V2: + return hdmi_8996_v2_calculate(pix_clk, cfg); + default: + return hdmi_8996_v1_calculate(pix_clk, cfg); + } +} + +static int hdmi_8996_phy_pll_set_clk_rate(struct clk *c, u32 tmds_clk, u32 ver) +{ + int rc = 0; + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + struct hdmi_8996_phy_pll_reg_cfg cfg = {0}; + + rc = hdmi_8996_calculate(tmds_clk, &cfg, ver); + if (rc) { + DEV_ERR("%s: PLL calculation failed\n", __func__); + return rc; + } + + /* Initially shut down PHY */ + DEV_DBG("%s: Disabling PHY\n", __func__); + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_PD_CTL, 0x0); + udelay(500); + + /* Power up sequence */ + switch (ver) { + case HDMI_VERSION_8996_V2: + case HDMI_VERSION_8996_V3: + case HDMI_VERSION_8996_V3_1_8: + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_CTRL, 0x04); + break; + }; + + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_PD_CTL, 0x1); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RESETSM_CNTRL, 0x20); + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TX0_TX1_LANE_CTL, 0x0F); + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TX2_TX3_LANE_CTL, 0x0F); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_CLKBUF_ENABLE, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_CLKBUF_ENABLE, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_CLKBUF_ENABLE, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_CLKBUF_ENABLE, 0x03); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_LANE_MODE, cfg.tx_l0_lane_mode); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_LANE_MODE, cfg.tx_l2_lane_mode); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_TX_BAND, cfg.tx_l0_tx_band); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_TX_BAND, cfg.tx_l1_tx_band); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_TX_BAND, cfg.tx_l2_tx_band); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_TX_BAND, cfg.tx_l3_tx_band); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_RESET_TSYNC_EN, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_RESET_TSYNC_EN, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_RESET_TSYNC_EN, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_RESET_TSYNC_EN, 0x03); + + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1E); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x07); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SYSCLK_EN_SEL, 0x37); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SYS_CLK_CTRL, 0x02); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CLK_ENABLE1, 0x0E); + if (ver == HDMI_VERSION_8996_V1) + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_CTRL, 0x06); + + /* Bypass VCO calibration */ + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SVS_MODE_CLK_SEL, + cfg.com_svs_mode_clk_sel); + + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_TRIM, 0x0F); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_IVCO, 0x0F); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE_CTRL, + cfg.com_vco_tune_ctrl); + + switch (ver) { + case HDMI_VERSION_8996_V1: + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SVS_MODE_CLK_SEL, + cfg.com_svs_mode_clk_sel); + break; + default: + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_BG_CTRL, 0x06); + } + + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CLK_SELECT, 0x30); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_HSCLK_SEL, + cfg.com_hsclk_sel); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_EN, + cfg.com_lock_cmp_en); + + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_CCTRL_MODE0, + cfg.com_pll_cctrl_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_PLL_RCTRL_MODE0, + cfg.com_pll_rctrl_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CP_CTRL_MODE0, + cfg.com_cp_ctrl_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DEC_START_MODE0, + cfg.com_dec_start_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START1_MODE0, + cfg.com_div_frac_start1_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START2_MODE0, + cfg.com_div_frac_start2_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DIV_FRAC_START3_MODE0, + cfg.com_div_frac_start3_mode0); + + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_INTEGLOOP_GAIN0_MODE0, + cfg.com_integloop_gain0_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_INTEGLOOP_GAIN1_MODE0, + cfg.com_integloop_gain1_mode0); + + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP1_MODE0, + cfg.com_lock_cmp1_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP2_MODE0, + cfg.com_lock_cmp2_mode0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP3_MODE0, + cfg.com_lock_cmp3_mode0); + + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE_MAP, 0x00); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CORE_CLK_EN, + cfg.com_core_clk_en); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CORECLK_DIV, + cfg.com_coreclk_div); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_CONFIG, 0x02); + + if (ver == HDMI_VERSION_8996_V3 || ver == HDMI_VERSION_8996_V3_1_8) + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RESCODE_DIV_NUM, 0x15); + + /* TX lanes setup (TX 0/1/2/3) */ + if (ver == HDMI_VERSION_8996_V3_1_8) { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL, + 0x00000023); + } else { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL, + cfg.tx_l0_tx_drv_lvl); + } + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_TX_EMP_POST1_LVL, + cfg.tx_l0_tx_emp_post1_lvl); + + if (ver == HDMI_VERSION_8996_V3_1_8) { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL, + 0x00000023); + } else { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL, + cfg.tx_l1_tx_drv_lvl); + } + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_TX_EMP_POST1_LVL, + cfg.tx_l1_tx_emp_post1_lvl); + + if (ver == HDMI_VERSION_8996_V3_1_8) { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL, + 0x00000023); + } else { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL, + cfg.tx_l2_tx_drv_lvl); + } + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_TX_EMP_POST1_LVL, + cfg.tx_l2_tx_emp_post1_lvl); + + if (ver == HDMI_VERSION_8996_V3_1_8) { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL, + 0x00000020); + } else { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL, + cfg.tx_l3_tx_drv_lvl); + } + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_TX_EMP_POST1_LVL, + cfg.tx_l3_tx_emp_post1_lvl); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL1, + cfg.tx_l0_vmode_ctrl1); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL2, + cfg.tx_l0_vmode_ctrl2); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL1, + cfg.tx_l1_vmode_ctrl1); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL2, + cfg.tx_l1_vmode_ctrl2); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL1, + cfg.tx_l2_vmode_ctrl1); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL2, + cfg.tx_l2_vmode_ctrl2); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL1, + cfg.tx_l3_vmode_ctrl1); + if (ver == HDMI_VERSION_8996_V3_1_8) { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL2, + 0x0000000D); + } else { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_VMODE_CTRL2, + cfg.tx_l3_vmode_ctrl2); + } + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_TX_DRV_LVL_OFFSET, 0x00); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_RES_CODE_LANE_OFFSET, 0x00); + + if (ver < HDMI_VERSION_8996_V3) { + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_RES_CODE_LANE_TX, + cfg.tx_l0_res_code_lane_tx); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_RES_CODE_LANE_TX, + cfg.tx_l1_res_code_lane_tx); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_RES_CODE_LANE_TX, + cfg.tx_l2_res_code_lane_tx); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_RES_CODE_LANE_TX, + cfg.tx_l3_res_code_lane_tx); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_RESTRIM_CTRL, + cfg.com_restrim_ctrl); + + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TXCAL_CFG0, 0x00); + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_TXCAL_CFG1, 0x05); + } + + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_MODE, cfg.phy_mode); + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_PD_CTL, 0x1F); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_TRAN_DRVR_EMP_EN, 0x03); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_PARRATE_REC_DETECT_IDLE_EN, 0x40); + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_HP_PD_ENABLES, 0x0C); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_HP_PD_ENABLES, 0x0C); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_HP_PD_ENABLES, 0x0C); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_HP_PD_ENABLES, 0x03); + + if (ver == HDMI_VERSION_8996_V2) { + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL1, 0x01); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL2, 0x01); + } + /* + * Ensure that vco configuration gets flushed to hardware before + * enabling the PLL + */ + wmb(); + return 0; +} + +static int hdmi_8996_phy_ready_status(struct mdss_pll_resources *io) +{ + u32 status = 0; + int phy_ready = 0; + int rc; + u32 read_count = 0; + + rc = mdss_pll_resource_enable(io, true); + if (rc) { + DEV_ERR("%s: pll resource can't be enabled\n", __func__); + return rc; + } + + DEV_DBG("%s: Waiting for PHY Ready\n", __func__); + + /* Poll for PHY read status */ + while (read_count < HDMI_PLL_POLL_MAX_READS) { + status = MDSS_PLL_REG_R(io->phy_base, HDMI_PHY_STATUS); + if ((status & BIT(0)) == 1) { + phy_ready = 1; + DEV_DBG("%s: PHY READY\n", __func__); + break; + } + udelay(HDMI_PLL_POLL_TIMEOUT_US); + read_count++; + } + + if (read_count == HDMI_PLL_POLL_MAX_READS) { + phy_ready = 0; + DEV_DBG("%s: PHY READY TIMEOUT\n", __func__); + } + + mdss_pll_resource_enable(io, false); + + return phy_ready; +} + +static int hdmi_8996_pll_lock_status(struct mdss_pll_resources *io) +{ + u32 status; + int pll_locked = 0; + int rc; + u32 read_count = 0; + + rc = mdss_pll_resource_enable(io, true); + if (rc) { + DEV_ERR("%s: pll resource can't be enabled\n", __func__); + return rc; + } + + DEV_DBG("%s: Waiting for PLL lock\n", __func__); + + while (read_count < HDMI_PLL_POLL_MAX_READS) { + status = MDSS_PLL_REG_R(io->pll_base, + QSERDES_COM_C_READY_STATUS); + if ((status & BIT(0)) == 1) { + pll_locked = 1; + DEV_DBG("%s: C READY\n", __func__); + break; + } + udelay(HDMI_PLL_POLL_TIMEOUT_US); + read_count++; + } + + if (read_count == HDMI_PLL_POLL_MAX_READS) { + pll_locked = 0; + DEV_DBG("%s: C READY TIMEOUT\n", __func__); + } + + mdss_pll_resource_enable(io, false); + + return pll_locked; +} + +static int hdmi_8996_v1_perform_sw_calibration(struct clk *c) +{ + int rc = 0; + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + + u32 max_code = 0x190; + u32 min_code = 0x0; + u32 max_cnt = 0; + u32 min_cnt = 0; + u32 expected_counter_value = 0; + u32 step = 0; + u32 dbus_all = 0; + u32 dbus_sel = 0; + u32 vco_code = 0; + u32 val = 0; + + vco_code = 0xC8; + + DEV_DBG("%s: Starting SW calibration with vco_code = %d\n", __func__, + vco_code); + + expected_counter_value = + (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP3_MODE0) << 16) | + (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP2_MODE0) << 8) | + (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP1_MODE0)); + + DEV_DBG("%s: expected_counter_value = %d\n", __func__, + expected_counter_value); + + val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1); + val |= BIT(4); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val); + + val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1); + val |= BIT(3); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val); + + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DEBUG_BUS_SEL, 0x4); + + val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG); + val |= BIT(1); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val); + + udelay(60); + + while (1) { + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE1_MODE0, + vco_code & 0xFF); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE2_MODE0, + (vco_code >> 8) & 0x3); + + udelay(20); + + val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG); + val &= ~BIT(1); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val); + + udelay(60); + + val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG); + val |= BIT(1); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val); + + udelay(60); + + dbus_all = + (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS3) << 24) | + (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS2) << 16) | + (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS1) << 8) | + (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEBUG_BUS0)); + + dbus_sel = (dbus_all >> 9) & 0x3FFFF; + DEV_DBG("%s: loop[%d], dbus_all = 0x%x, dbus_sel = 0x%x\n", + __func__, step, dbus_all, dbus_sel); + if (dbus_sel == 0) + DEV_ERR("%s: CHECK HDMI REF CLK\n", __func__); + + if (dbus_sel == expected_counter_value) { + max_code = vco_code; + max_cnt = dbus_sel; + min_code = vco_code; + min_cnt = dbus_sel; + } else if (dbus_sel == 0) { + max_code = vco_code; + max_cnt = dbus_sel; + vco_code = (max_code + min_code)/2; + } else if (dbus_sel > expected_counter_value) { + min_code = vco_code; + min_cnt = dbus_sel; + vco_code = (max_code + min_code)/2; + } else if (dbus_sel < expected_counter_value) { + max_code = vco_code; + max_cnt = dbus_sel; + vco_code = (max_code + min_code)/2; + } + + step++; + + if ((vco_code == 0) || (vco_code == 0x3FF) || (step > 0x3FF)) { + DEV_ERR("%s: VCO tune code search failed\n", __func__); + rc = -ENOTSUPP; + break; + } + if ((max_code - min_code) <= 1) { + if ((max_code - min_code) == 1) { + if (abs((int)(max_cnt - expected_counter_value)) + < abs((int)(min_cnt - expected_counter_value + ))) { + vco_code = max_code; + } else { + vco_code = min_code; + } + } + break; + } + DEV_DBG("%s: loop[%d], new vco_code = %d\n", __func__, step, + vco_code); + } + + DEV_DBG("%s: CALIB done. vco_code = %d\n", __func__, vco_code); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE1_MODE0, + vco_code & 0xFF); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_VCO_TUNE2_MODE0, + (vco_code >> 8) & 0x3); + val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_LOCK_CMP_CFG); + val &= ~BIT(1); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_LOCK_CMP_CFG, val); + + val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1); + val |= BIT(4); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val); + + val = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_CMN_MISC1); + val &= ~BIT(3); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_CMN_MISC1, val); + + return rc; +} + +static int hdmi_8996_v2_perform_sw_calibration(struct clk *c) +{ + int rc = 0; + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + u32 vco_code1, vco_code2, integral_loop, ready_poll; + u32 read_count = 0; + + while (read_count < (HDMI_PLL_POLL_MAX_READS << 1)) { + ready_poll = MDSS_PLL_REG_R(io->pll_base, + QSERDES_COM_C_READY_STATUS); + if ((ready_poll & BIT(0)) == 1) { + ready_poll = 1; + DEV_DBG("%s: C READY\n", __func__); + break; + } + udelay(HDMI_PLL_POLL_TIMEOUT_US); + read_count++; + } + + if (read_count == (HDMI_PLL_POLL_MAX_READS << 1)) { + ready_poll = 0; + DEV_DBG("%s: C READY TIMEOUT, TRYING SW CALIBRATION\n", + __func__); + } + + vco_code1 = MDSS_PLL_REG_R(io->pll_base, + QSERDES_COM_PLLCAL_CODE1_STATUS); + vco_code2 = MDSS_PLL_REG_R(io->pll_base, + QSERDES_COM_PLLCAL_CODE2_STATUS); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_DEBUG_BUS_SEL, 0x5); + integral_loop = MDSS_PLL_REG_R(io->pll_base, + QSERDES_COM_DEBUG_BUS0); + + if (((ready_poll & 0x1) == 0) || (((ready_poll & 1) == 1) && + (vco_code1 == 0xFF) && ((vco_code2 & 0x3) == 0x1) && + (integral_loop > 0xC0))) { + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL1, 0x04); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_ATB_SEL2, 0x00); + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x17); + udelay(100); + + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x11); + udelay(100); + + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x19); + } + return rc; +} + +static int hdmi_8996_perform_sw_calibration(struct clk *c, u32 ver) +{ + switch (ver) { + case HDMI_VERSION_8996_V1: + return hdmi_8996_v1_perform_sw_calibration(c); + case HDMI_VERSION_8996_V2: + return hdmi_8996_v2_perform_sw_calibration(c); + } + return 0; +} + +static int hdmi_8996_vco_enable(struct clk *c, u32 ver) +{ + int rc = 0; + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x1); + udelay(100); + + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x19); + udelay(100); + + rc = hdmi_8996_perform_sw_calibration(c, ver); + if (rc) { + DEV_ERR("%s: software calibration failed\n", __func__); + return rc; + } + + rc = hdmi_8996_pll_lock_status(io); + if (!rc) { + DEV_ERR("%s: PLL not locked\n", __func__); + return rc; + } + + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, + 0x6F); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L1_BASE_OFFSET, + QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, + 0x6F); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L2_BASE_OFFSET, + QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, + 0x6F); + MDSS_PLL_REG_W(io->pll_base + HDMI_TX_L3_BASE_OFFSET, + QSERDES_TX_L0_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, + 0x6F); + + /* Disable SSC */ + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_PER1, 0x0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_PER2, 0x0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_STEP_SIZE1, 0x0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_STEP_SIZE2, 0x0); + MDSS_PLL_REG_W(io->pll_base, QSERDES_COM_SSC_EN_CENTER, 0x2); + + rc = hdmi_8996_phy_ready_status(io); + if (!rc) { + DEV_ERR("%s: PHY not READY\n", __func__); + return rc; + } + + /* Restart the retiming buffer */ + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x18); + udelay(1); + MDSS_PLL_REG_W(io->phy_base, HDMI_PHY_CFG, 0x19); + + io->pll_on = true; + return 0; +} + +static int hdmi_8996_v1_vco_enable(struct clk *c) +{ + return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V1); +} + +static int hdmi_8996_v2_vco_enable(struct clk *c) +{ + return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V2); +} + +static int hdmi_8996_v3_vco_enable(struct clk *c) +{ + return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V3); +} + +static int hdmi_8996_v3_1p8_vco_enable(struct clk *c) +{ + return hdmi_8996_vco_enable(c, HDMI_VERSION_8996_V3_1_8); +} + +static int hdmi_8996_vco_get_lock_range(struct clk *c, unsigned long pixel_clk) +{ + u32 rng = 64, cmp_cnt = 1024; + u32 coreclk_div = 5, clks_pll_divsel = 2; + u32 vco_freq, vco_ratio, ppm_range; + u64 bclk; + struct hdmi_8996_v3_post_divider pd; + + bclk = ((u64)pixel_clk) * HDMI_BIT_CLK_TO_PIX_CLK_RATIO; + + DEV_DBG("%s: rate=%ld\n", __func__, pixel_clk); + + if (hdmi_8996_v3_get_post_div(&pd, bclk) || + pd.vco_ratio <= 0 || pd.vco_freq <= 0) { + DEV_ERR("%s: couldn't get post div\n", __func__); + return -EINVAL; + } + + do_div(pd.vco_freq, HDMI_KHZ_TO_HZ * HDMI_KHZ_TO_HZ); + + vco_freq = (u32) pd.vco_freq; + vco_ratio = (u32) pd.vco_ratio; + + DEV_DBG("%s: freq %d, ratio %d\n", __func__, + vco_freq, vco_ratio); + + ppm_range = (rng * HDMI_REF_CLOCK) / cmp_cnt; + ppm_range /= vco_freq / vco_ratio; + ppm_range *= coreclk_div * clks_pll_divsel; + + DEV_DBG("%s: ppm range: %d\n", __func__, ppm_range); + + return ppm_range; +} + +static int hdmi_8996_vco_rate_atomic_update(struct clk *c, + unsigned long rate, u32 ver) +{ + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + void __iomem *pll; + struct hdmi_8996_phy_pll_reg_cfg cfg = {0}; + int rc = 0; + + rc = hdmi_8996_calculate(rate, &cfg, ver); + if (rc) { + DEV_ERR("%s: PLL calculation failed\n", __func__); + goto end; + } + + pll = io->pll_base; + + MDSS_PLL_REG_W(pll, QSERDES_COM_DEC_START_MODE0, + cfg.com_dec_start_mode0); + MDSS_PLL_REG_W(pll, QSERDES_COM_DIV_FRAC_START1_MODE0, + cfg.com_div_frac_start1_mode0); + MDSS_PLL_REG_W(pll, QSERDES_COM_DIV_FRAC_START2_MODE0, + cfg.com_div_frac_start2_mode0); + MDSS_PLL_REG_W(pll, QSERDES_COM_DIV_FRAC_START3_MODE0, + cfg.com_div_frac_start3_mode0); + + MDSS_PLL_REG_W(pll, QSERDES_COM_FREQ_UPDATE, 0x01); + MDSS_PLL_REG_W(pll, QSERDES_COM_FREQ_UPDATE, 0x00); + + DEV_DBG("%s: updated to rate %ld\n", __func__, rate); +end: + return rc; +} + +static int hdmi_8996_vco_set_rate(struct clk *c, unsigned long rate, u32 ver) +{ + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + unsigned int set_power_dwn = 0; + bool atomic_update = false; + int rc, pll_lock_range; + + rc = mdss_pll_resource_enable(io, true); + if (rc) { + DEV_ERR("pll resource can't be enabled\n"); + return rc; + } + + DEV_DBG("%s: rate %ld\n", __func__, rate); + + if (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_C_READY_STATUS) & BIT(0) && + MDSS_PLL_REG_R(io->phy_base, HDMI_PHY_STATUS) & BIT(0)) { + pll_lock_range = hdmi_8996_vco_get_lock_range(c, vco->rate); + + if (pll_lock_range > 0 && vco->rate) { + u32 range_limit; + + range_limit = vco->rate * + (pll_lock_range / HDMI_KHZ_TO_HZ); + range_limit /= HDMI_KHZ_TO_HZ; + + DEV_DBG("%s: range limit %d\n", __func__, range_limit); + + if (abs(rate - vco->rate) < range_limit) + atomic_update = true; + } + } + + if (io->pll_on && !atomic_update) + set_power_dwn = 1; + + if (atomic_update) { + hdmi_8996_vco_rate_atomic_update(c, rate, ver); + } else { + rc = hdmi_8996_phy_pll_set_clk_rate(c, rate, ver); + if (rc) + DEV_ERR("%s: Failed to set clk rate\n", __func__); + } + + mdss_pll_resource_enable(io, false); + + if (set_power_dwn) + hdmi_8996_vco_enable(c, ver); + + vco->rate = rate; + vco->rate_set = true; + + return 0; +} + +static int hdmi_8996_v1_vco_set_rate(struct clk *c, unsigned long rate) +{ + return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V1); +} + +static int hdmi_8996_v2_vco_set_rate(struct clk *c, unsigned long rate) +{ + return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V2); +} + +static int hdmi_8996_v3_vco_set_rate(struct clk *c, unsigned long rate) +{ + return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V3); +} + +static int hdmi_8996_v3_1p8_vco_set_rate(struct clk *c, unsigned long rate) +{ + return hdmi_8996_vco_set_rate(c, rate, HDMI_VERSION_8996_V3_1_8); +} + +static unsigned long hdmi_get_hsclk_sel_divisor(unsigned long hsclk_sel) +{ + unsigned long divisor; + + switch (hsclk_sel) { + case 0: + divisor = 2; + break; + case 1: + divisor = 6; + break; + case 2: + divisor = 10; + break; + case 3: + divisor = 14; + break; + case 4: + divisor = 3; + break; + case 5: + divisor = 9; + break; + case 6: + case 13: + divisor = 15; + break; + case 7: + divisor = 21; + break; + case 8: + divisor = 4; + break; + case 9: + divisor = 12; + break; + case 10: + divisor = 20; + break; + case 11: + divisor = 28; + break; + case 12: + divisor = 5; + break; + case 14: + divisor = 25; + break; + case 15: + divisor = 35; + break; + default: + divisor = 1; + DEV_ERR("%s: invalid hsclk_sel value = %lu", + __func__, hsclk_sel); + break; + } + + return divisor; +} + +static unsigned long hdmi_8996_vco_get_rate(struct clk *c) +{ + unsigned long freq = 0, hsclk_sel = 0, tx_band = 0, dec_start = 0, + div_frac_start = 0, vco_clock_freq = 0; + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + + if (mdss_pll_resource_enable(io, true)) { + DEV_ERR("%s: pll resource can't be enabled\n", __func__); + return freq; + } + + dec_start = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_DEC_START_MODE0); + + div_frac_start = + MDSS_PLL_REG_R(io->pll_base, + QSERDES_COM_DIV_FRAC_START1_MODE0) | + MDSS_PLL_REG_R(io->pll_base, + QSERDES_COM_DIV_FRAC_START2_MODE0) << 8 | + MDSS_PLL_REG_R(io->pll_base, + QSERDES_COM_DIV_FRAC_START3_MODE0) << 16; + + vco_clock_freq = (dec_start + (div_frac_start / (1 << 20))) + * 4 * (HDMI_REF_CLOCK); + + hsclk_sel = MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_HSCLK_SEL) & 0x15; + hsclk_sel = hdmi_get_hsclk_sel_divisor(hsclk_sel); + tx_band = MDSS_PLL_REG_R(io->pll_base + HDMI_TX_L0_BASE_OFFSET, + QSERDES_TX_L0_TX_BAND) & 0x3; + + freq = vco_clock_freq / (10 * hsclk_sel * (1 << tx_band)); + + mdss_pll_resource_enable(io, false); + + DEV_DBG("%s: freq = %lu\n", __func__, freq); + + return freq; +} + +static long hdmi_8996_vco_round_rate(struct clk *c, unsigned long rate) +{ + unsigned long rrate = rate; + + DEV_DBG("rrate=%ld\n", rrate); + + return rrate; +} + +static int hdmi_8996_vco_prepare(struct clk *c, u32 ver) +{ + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + int ret = 0; + + DEV_DBG("rate=%ld\n", vco->rate); + + if (!vco->rate_set && vco->rate) + ret = hdmi_8996_vco_set_rate(c, vco->rate, ver); + + if (!ret) { + ret = mdss_pll_resource_enable(io, true); + if (ret) + DEV_ERR("pll resource can't be enabled\n"); + } + + return ret; +} + +static int hdmi_8996_v1_vco_prepare(struct clk *c) +{ + return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V1); +} + +static int hdmi_8996_v2_vco_prepare(struct clk *c) +{ + return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V2); +} + +static int hdmi_8996_v3_vco_prepare(struct clk *c) +{ + return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V3); +} + +static int hdmi_8996_v3_1p8_vco_prepare(struct clk *c) +{ + return hdmi_8996_vco_prepare(c, HDMI_VERSION_8996_V3_1_8); +} + +static void hdmi_8996_vco_unprepare(struct clk *c) +{ + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + + vco->rate_set = false; + + if (!io) { + DEV_ERR("Invalid input parameter\n"); + return; + } + + if (!io->pll_on && + mdss_pll_resource_enable(io, true)) { + DEV_ERR("pll resource can't be enabled\n"); + return; + } + + io->handoff_resources = false; + mdss_pll_resource_enable(io, false); + io->pll_on = false; +} + +static enum handoff hdmi_8996_vco_handoff(struct clk *c) +{ + enum handoff ret = HANDOFF_DISABLED_CLK; + struct hdmi_pll_vco_clk *vco = to_hdmi_8996_vco_clk(c); + struct mdss_pll_resources *io = vco->priv; + + if (is_gdsc_disabled(io)) + return HANDOFF_DISABLED_CLK; + + if (mdss_pll_resource_enable(io, true)) { + DEV_ERR("pll resource can't be enabled\n"); + return ret; + } + + io->handoff_resources = true; + + if (MDSS_PLL_REG_R(io->pll_base, QSERDES_COM_C_READY_STATUS) & BIT(0)) { + if (MDSS_PLL_REG_R(io->phy_base, HDMI_PHY_STATUS) & BIT(0)) { + io->pll_on = true; + c->rate = hdmi_8996_vco_get_rate(c); + vco->rate = c->rate; + ret = HANDOFF_ENABLED_CLK; + } else { + io->handoff_resources = false; + mdss_pll_resource_enable(io, false); + DEV_DBG("%s: PHY not ready\n", __func__); + } + } else { + io->handoff_resources = false; + mdss_pll_resource_enable(io, false); + DEV_DBG("%s: PLL not locked\n", __func__); + } + + DEV_DBG("done, ret=%d\n", ret); + return ret; +} + +static struct clk_ops hdmi_8996_v1_vco_clk_ops = { + .enable = hdmi_8996_v1_vco_enable, + .set_rate = hdmi_8996_v1_vco_set_rate, + .get_rate = hdmi_8996_vco_get_rate, + .round_rate = hdmi_8996_vco_round_rate, + .prepare = hdmi_8996_v1_vco_prepare, + .unprepare = hdmi_8996_vco_unprepare, + .handoff = hdmi_8996_vco_handoff, +}; + +static struct clk_ops hdmi_8996_v2_vco_clk_ops = { + .enable = hdmi_8996_v2_vco_enable, + .set_rate = hdmi_8996_v2_vco_set_rate, + .get_rate = hdmi_8996_vco_get_rate, + .round_rate = hdmi_8996_vco_round_rate, + .prepare = hdmi_8996_v2_vco_prepare, + .unprepare = hdmi_8996_vco_unprepare, + .handoff = hdmi_8996_vco_handoff, +}; + +static struct clk_ops hdmi_8996_v3_vco_clk_ops = { + .enable = hdmi_8996_v3_vco_enable, + .set_rate = hdmi_8996_v3_vco_set_rate, + .get_rate = hdmi_8996_vco_get_rate, + .round_rate = hdmi_8996_vco_round_rate, + .prepare = hdmi_8996_v3_vco_prepare, + .unprepare = hdmi_8996_vco_unprepare, + .handoff = hdmi_8996_vco_handoff, +}; + +static struct clk_ops hdmi_8996_v3_1p8_vco_clk_ops = { + .enable = hdmi_8996_v3_1p8_vco_enable, + .set_rate = hdmi_8996_v3_1p8_vco_set_rate, + .get_rate = hdmi_8996_vco_get_rate, + .round_rate = hdmi_8996_vco_round_rate, + .prepare = hdmi_8996_v3_1p8_vco_prepare, + .unprepare = hdmi_8996_vco_unprepare, + .handoff = hdmi_8996_vco_handoff, +}; + + +static struct hdmi_pll_vco_clk hdmi_vco_clk = { + .c = { + .dbg_name = "hdmi_8996_vco_clk", + .ops = &hdmi_8996_v1_vco_clk_ops, + CLK_INIT(hdmi_vco_clk.c), + }, +}; + +static struct clk_lookup hdmipllcc_8996[] = { + CLK_LIST(hdmi_vco_clk), +}; + +int hdmi_8996_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res, u32 ver) +{ + int rc = -ENOTSUPP; + + if (!pll_res || !pll_res->phy_base || !pll_res->pll_base) { + DEV_ERR("%s: Invalid input parameters\n", __func__); + return -EPROBE_DEFER; + } + + /* Set client data for vco, mux and div clocks */ + hdmi_vco_clk.priv = pll_res; + + switch (ver) { + case HDMI_VERSION_8996_V2: + hdmi_vco_clk.c.ops = &hdmi_8996_v2_vco_clk_ops; + break; + case HDMI_VERSION_8996_V3: + hdmi_vco_clk.c.ops = &hdmi_8996_v3_vco_clk_ops; + break; + case HDMI_VERSION_8996_V3_1_8: + hdmi_vco_clk.c.ops = &hdmi_8996_v3_1p8_vco_clk_ops; + break; + default: + hdmi_vco_clk.c.ops = &hdmi_8996_v1_vco_clk_ops; + break; + }; + + rc = of_msm_clock_register(pdev->dev.of_node, hdmipllcc_8996, + ARRAY_SIZE(hdmipllcc_8996)); + if (rc) { + DEV_ERR("%s: Clock register failed rc=%d\n", __func__, rc); + rc = -EPROBE_DEFER; + } else { + DEV_DBG("%s SUCCESS\n", __func__); + } + + return rc; +} + +int hdmi_8996_v1_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + return hdmi_8996_pll_clock_register(pdev, pll_res, + HDMI_VERSION_8996_V1); +} + +int hdmi_8996_v2_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + return hdmi_8996_pll_clock_register(pdev, pll_res, + HDMI_VERSION_8996_V2); +} + +int hdmi_8996_v3_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + return hdmi_8996_pll_clock_register(pdev, pll_res, + HDMI_VERSION_8996_V3); +} + +int hdmi_8996_v3_1p8_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + return hdmi_8996_pll_clock_register(pdev, pll_res, + HDMI_VERSION_8996_V3_1_8); +} diff --git a/drivers/clk/qcom/mdss/mdss-hdmi-pll.h b/drivers/clk/qcom/mdss/mdss-hdmi-pll.h new file mode 100644 index 000000000000..d4226bf43e13 --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-hdmi-pll.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MDSS_HDMI_PLL_H +#define __MDSS_HDMI_PLL_H + +struct hdmi_pll_cfg { + unsigned long vco_rate; + u32 reg; +}; + +struct hdmi_pll_vco_clk { + unsigned long rate; /* current vco rate */ + unsigned long min_rate; /* min vco rate */ + unsigned long max_rate; /* max vco rate */ + bool rate_set; + struct hdmi_pll_cfg *ip_seti; + struct hdmi_pll_cfg *cp_seti; + struct hdmi_pll_cfg *ip_setp; + struct hdmi_pll_cfg *cp_setp; + struct hdmi_pll_cfg *crctrl; + void *priv; + + struct clk c; +}; + +static inline struct hdmi_pll_vco_clk *to_hdmi_vco_clk(struct clk *clk) +{ + return container_of(clk, struct hdmi_pll_vco_clk, c); +} + +int hdmi_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); + +int hdmi_20nm_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); + +int hdmi_8996_v1_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); + +int hdmi_8996_v2_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); + +int hdmi_8996_v3_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); + +int hdmi_8996_v3_1p8_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); + +int hdmi_cobalt_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +#endif diff --git a/drivers/clk/qcom/mdss/mdss-pll-util.c b/drivers/clk/qcom/mdss/mdss-pll-util.c new file mode 100644 index 000000000000..690c53f30eb7 --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-pll-util.c @@ -0,0 +1,438 @@ +/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/string.h> +#include <linux/clk/msm-clock-generic.h> +#include <linux/of_address.h> +#include <linux/dma-mapping.h> +#include <linux/vmalloc.h> +#include <linux/memblock.h> + +#include "mdss-pll.h" + +int mdss_pll_util_resource_init(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc = 0; + struct dss_module_power *mp = &pll_res->mp; + + rc = msm_dss_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); + if (rc) { + pr_err("Clock get failed rc=%d\n", rc); + goto clk_err; + } + + return rc; + +clk_err: + msm_dss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg, 0); +vreg_err: + return rc; +} + +/** + * mdss_pll_get_mp_by_reg_name() -- Find power module by regulator name + *@pll_res: Pointer to the PLL resource + *@name: Regulator name as specified in the pll dtsi + * + * This is a helper function to retrieve the regulator information + * for each pll resource. + */ +struct dss_vreg *mdss_pll_get_mp_by_reg_name(struct mdss_pll_resources *pll_res + , char *name) +{ + + struct dss_vreg *regulator = NULL; + int i; + + if ((pll_res == NULL) || (pll_res->mp.vreg_config == NULL)) { + pr_err("%s Invalid PLL resource\n", __func__); + goto error; + } + + regulator = pll_res->mp.vreg_config; + + for (i = 0; i < pll_res->mp.num_vreg; i++) { + if (!strcmp(name, regulator->vreg_name)) { + pr_debug("Found regulator match for %s\n", name); + break; + } + regulator++; + } + +error: + return regulator; +} + +void mdss_pll_util_resource_deinit(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + struct dss_module_power *mp = &pll_res->mp; + + msm_dss_put_clk(mp->clk_config, mp->num_clk); + + msm_dss_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; + + devm_kfree(&pdev->dev, mp->clk_config); + devm_kfree(&pdev->dev, mp->vreg_config); + mp->num_vreg = 0; + mp->num_clk = 0; +} + +int mdss_pll_util_resource_enable(struct mdss_pll_resources *pll_res, + bool enable) +{ + int rc = 0; + struct dss_module_power *mp = &pll_res->mp; + + if (enable) { + rc = msm_dss_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); + 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); + 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_dss_enable_vreg(mp->vreg_config, mp->num_vreg, enable); + } + + return rc; + +clk_err: + msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, 0); +vreg_err: + return rc; +} + +static int mdss_pll_util_parse_dt_supply(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int i = 0, rc = 0; + u32 tmp = 0; + struct device_node *of_node = NULL, *supply_root_node = NULL; + struct device_node *supply_node = NULL; + struct dss_module_power *mp = &pll_res->mp; + + of_node = pdev->dev.of_node; + + mp->num_vreg = 0; + supply_root_node = of_get_child_by_name(of_node, + "qcom,platform-supply-entries"); + if (!supply_root_node) { + pr_err("no supply entry present\n"); + return rc; + } + + for_each_child_of_node(supply_root_node, supply_node) { + mp->num_vreg++; + } + + if (mp->num_vreg == 0) { + pr_debug("no vreg\n"); + return rc; + } + pr_debug("vreg found. count=%d\n", mp->num_vreg); + + mp->vreg_config = devm_kzalloc(&pdev->dev, sizeof(struct dss_vreg) * + mp->num_vreg, GFP_KERNEL); + if (!mp->vreg_config) { + rc = -ENOMEM; + return rc; + } + + for_each_child_of_node(supply_root_node, supply_node) { + + const char *st = NULL; + + rc = of_property_read_string(supply_node, + "qcom,supply-name", &st); + if (rc) { + pr_err(":error reading name. rc=%d\n", rc); + goto error; + } + + strlcpy(mp->vreg_config[i].vreg_name, st, + sizeof(mp->vreg_config[i].vreg_name)); + + rc = of_property_read_u32(supply_node, + "qcom,supply-min-voltage", &tmp); + if (rc) { + pr_err(": error reading min volt. rc=%d\n", rc); + goto error; + } + mp->vreg_config[i].min_voltage = tmp; + + rc = of_property_read_u32(supply_node, + "qcom,supply-max-voltage", &tmp); + if (rc) { + pr_err(": error reading max volt. rc=%d\n", rc); + goto error; + } + mp->vreg_config[i].max_voltage = tmp; + + rc = of_property_read_u32(supply_node, + "qcom,supply-enable-load", &tmp); + if (rc) { + pr_err(": error reading enable load. rc=%d\n", rc); + goto error; + } + mp->vreg_config[i].enable_load = tmp; + + rc = of_property_read_u32(supply_node, + "qcom,supply-disable-load", &tmp); + if (rc) { + pr_err(": error reading disable load. rc=%d\n", rc); + goto error; + } + mp->vreg_config[i].disable_load = tmp; + + rc = of_property_read_u32(supply_node, + "qcom,supply-pre-on-sleep", &tmp); + if (rc) + pr_debug("error reading supply pre sleep value. rc=%d\n", + rc); + + mp->vreg_config[i].pre_on_sleep = (!rc ? tmp : 0); + + rc = of_property_read_u32(supply_node, + "qcom,supply-pre-off-sleep", &tmp); + if (rc) + pr_debug("error reading supply pre sleep value. rc=%d\n", + rc); + + mp->vreg_config[i].pre_off_sleep = (!rc ? tmp : 0); + + rc = of_property_read_u32(supply_node, + "qcom,supply-post-on-sleep", &tmp); + if (rc) + pr_debug("error reading supply post sleep value. rc=%d\n", + rc); + + mp->vreg_config[i].post_on_sleep = (!rc ? tmp : 0); + + rc = of_property_read_u32(supply_node, + "qcom,supply-post-off-sleep", &tmp); + if (rc) + pr_debug("error reading supply post sleep value. rc=%d\n", + rc); + + mp->vreg_config[i].post_off_sleep = (!rc ? tmp : 0); + + pr_debug("%s min=%d, max=%d, enable=%d, disable=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d\n", + mp->vreg_config[i].vreg_name, + mp->vreg_config[i].min_voltage, + mp->vreg_config[i].max_voltage, + mp->vreg_config[i].enable_load, + mp->vreg_config[i].disable_load, + mp->vreg_config[i].pre_on_sleep, + mp->vreg_config[i].post_on_sleep, + mp->vreg_config[i].pre_off_sleep, + mp->vreg_config[i].post_off_sleep); + ++i; + + rc = 0; + } + + return rc; + +error: + if (mp->vreg_config) { + devm_kfree(&pdev->dev, mp->vreg_config); + mp->vreg_config = NULL; + mp->num_vreg = 0; + } + + return rc; +} + +static int mdss_pll_util_parse_dt_clock(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + u32 i = 0, rc = 0; + struct dss_module_power *mp = &pll_res->mp; + const char *clock_name; + u32 clock_rate; + + mp->num_clk = of_property_count_strings(pdev->dev.of_node, + "clock-names"); + if (mp->num_clk <= 0) { + pr_err("clocks are not defined\n"); + goto clk_err; + } + + mp->clk_config = devm_kzalloc(&pdev->dev, + sizeof(struct dss_clk) * mp->num_clk, GFP_KERNEL); + if (!mp->clk_config) { + rc = -ENOMEM; + mp->num_clk = 0; + goto clk_err; + } + + for (i = 0; i < mp->num_clk; i++) { + of_property_read_string_index(pdev->dev.of_node, "clock-names", + i, &clock_name); + strlcpy(mp->clk_config[i].clk_name, clock_name, + sizeof(mp->clk_config[i].clk_name)); + + of_property_read_u32_index(pdev->dev.of_node, "clock-rate", + i, &clock_rate); + mp->clk_config[i].rate = clock_rate; + + if (!clock_rate) + mp->clk_config[i].type = DSS_CLK_AHB; + else + mp->clk_config[i].type = DSS_CLK_PCLK; + } + +clk_err: + return rc; +} + +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; + + rc = mdss_pll_util_parse_dt_supply(pdev, pll_res); + if (rc) { + pr_err("vreg parsing failed rc=%d\n", rc); + goto end; + } + + rc = mdss_pll_util_parse_dt_clock(pdev, pll_res); + if (rc) { + pr_err("clock name parsing failed rc=%d", rc); + goto clk_err; + } + + if (mdss_pll_util_parse_dt_dfps(pdev, pll_res)) + pr_err("dfps not enabled!\n"); + + return rc; + +clk_err: + devm_kfree(&pdev->dev, mp->vreg_config); + mp->num_vreg = 0; +end: + return rc; +} diff --git a/drivers/clk/qcom/mdss/mdss-pll.c b/drivers/clk/qcom/mdss/mdss-pll.c new file mode 100644 index 000000000000..e91e9c9dc768 --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-pll.c @@ -0,0 +1,437 @@ +/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/iopoll.h> +#include <linux/clk/msm-clock-generic.h> + +#include "mdss-pll.h" +#include "mdss-dsi-pll.h" +#include "mdss-hdmi-pll.h" +#include "mdss-dp-pll.h" + +int mdss_pll_resource_enable(struct mdss_pll_resources *pll_res, bool enable) +{ + int rc = 0; + int changed = 0; + + if (!pll_res) { + pr_err("Invalid input parameters\n"); + return -EINVAL; + } + + /* + * Don't turn off resources during handoff or add more than + * 1 refcount. + */ + if (pll_res->handoff_resources && + (!enable || (enable & pll_res->resource_enable))) { + pr_debug("Do not turn on/off pll resources during handoff case\n"); + return rc; + } + + if (enable) { + if (pll_res->resource_ref_cnt == 0) + changed++; + pll_res->resource_ref_cnt++; + } else { + if (pll_res->resource_ref_cnt) { + pll_res->resource_ref_cnt--; + if (pll_res->resource_ref_cnt == 0) + changed++; + } else { + pr_err("PLL Resources already OFF\n"); + } + } + + if (changed) { + rc = mdss_pll_util_resource_enable(pll_res, enable); + if (rc) + pr_err("Resource update failed rc=%d\n", rc); + else + pll_res->resource_enable = enable; + } + + return rc; +} + +static int mdss_pll_resource_init(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + if (!pdev || !pll_res) { + pr_err("Invalid input parameters\n"); + return -EINVAL; + } + + return mdss_pll_util_resource_init(pdev, pll_res); +} + +static void mdss_pll_resource_deinit(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + if (!pdev || !pll_res) { + pr_err("Invalid input parameters\n"); + return; + } + + mdss_pll_util_resource_deinit(pdev, pll_res); +} + +static void mdss_pll_resource_release(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + if (!pdev || !pll_res) { + pr_err("Invalid input parameters\n"); + return; + } + + mdss_pll_util_resource_release(pdev, pll_res); +} + +static int mdss_pll_resource_parse(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc = 0; + const char *compatible_stream; + + if (!pdev || !pll_res) { + pr_err("Invalid input parameters\n"); + return -EINVAL; + } + + rc = mdss_pll_util_resource_parse(pdev, pll_res); + if (rc) { + pr_err("Failed to parse the resources rc=%d\n", rc); + goto end; + } + + compatible_stream = of_get_property(pdev->dev.of_node, + "compatible", NULL); + if (!compatible_stream) { + pr_err("Failed to parse the compatible stream\n"); + goto err; + } + + if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8996")) { + pll_res->pll_interface_type = MDSS_DSI_PLL_8996; + pll_res->target_id = MDSS_PLL_TARGET_8996; + pll_res->revision = 1; + } else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_8996_v2")) { + pll_res->pll_interface_type = MDSS_DSI_PLL_8996; + pll_res->target_id = MDSS_PLL_TARGET_8996; + pll_res->revision = 2; + } else if (!strcmp(compatible_stream, "qcom,mdss_dsi_pll_cobalt")) { + pll_res->pll_interface_type = MDSS_DSI_PLL_COBALT; + } else if (!strcmp(compatible_stream, "qcom,mdss_dp_pll_cobalt")) { + pll_res->pll_interface_type = MDSS_DP_PLL_COBALT; + } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996")) { + pll_res->pll_interface_type = MDSS_HDMI_PLL_8996; + } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996_v2")) { + pll_res->pll_interface_type = MDSS_HDMI_PLL_8996_V2; + } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996_v3")) { + pll_res->pll_interface_type = MDSS_HDMI_PLL_8996_V3; + } else if (!strcmp(compatible_stream, + "qcom,mdss_hdmi_pll_8996_v3_1p8")) { + pll_res->pll_interface_type = MDSS_HDMI_PLL_8996_V3_1_8; + } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_cobalt")) { + pll_res->pll_interface_type = MDSS_HDMI_PLL_COBALT; + } else { + goto err; + } + + return rc; + +err: + mdss_pll_resource_release(pdev, pll_res); +end: + return rc; +} + +static int mdss_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + int rc; + + if (!pdev || !pll_res) { + pr_err("Invalid input parameters\n"); + return -EINVAL; + } + + switch (pll_res->pll_interface_type) { + case MDSS_DSI_PLL_8996: + rc = dsi_pll_clock_register_8996(pdev, pll_res); + break; + case MDSS_DSI_PLL_COBALT: + rc = dsi_pll_clock_register_cobalt(pdev, pll_res); + case MDSS_DP_PLL_COBALT: + rc = dp_pll_clock_register_cobalt(pdev, pll_res); + break; + case MDSS_HDMI_PLL_8996: + rc = hdmi_8996_v1_pll_clock_register(pdev, pll_res); + break; + case MDSS_HDMI_PLL_8996_V2: + rc = hdmi_8996_v2_pll_clock_register(pdev, pll_res); + break; + case MDSS_HDMI_PLL_8996_V3: + rc = hdmi_8996_v3_pll_clock_register(pdev, pll_res); + break; + case MDSS_HDMI_PLL_8996_V3_1_8: + rc = hdmi_8996_v3_1p8_pll_clock_register(pdev, pll_res); + break; + case MDSS_HDMI_PLL_COBALT: + rc = hdmi_cobalt_pll_clock_register(pdev, pll_res); + break; + case MDSS_UNKNOWN_PLL: + default: + rc = -EINVAL; + break; + } + + if (rc) { + pr_err("Pll ndx=%d clock register failed rc=%d\n", + pll_res->index, rc); + } + + return rc; +} + +static int mdss_pll_probe(struct platform_device *pdev) +{ + int rc = 0; + const char *label; + struct resource *pll_base_reg; + struct resource *phy_base_reg; + struct resource *dynamic_pll_base_reg; + struct resource *gdsc_base_reg; + struct mdss_pll_resources *pll_res; + + if (!pdev->dev.of_node) { + pr_err("MDSS pll driver only supports device tree probe\n"); + rc = -ENOTSUPP; + goto error; + } + + label = of_get_property(pdev->dev.of_node, "label", NULL); + if (!label) + pr_info("%d: MDSS pll label not specified\n", __LINE__); + else + pr_info("MDSS pll label = %s\n", label); + + pll_res = devm_kzalloc(&pdev->dev, sizeof(struct mdss_pll_resources), + GFP_KERNEL); + if (!pll_res) { + rc = -ENOMEM; + goto error; + } + platform_set_drvdata(pdev, pll_res); + + rc = of_property_read_u32(pdev->dev.of_node, "cell-index", + &pll_res->index); + if (rc) { + pr_err("Unable to get the cell-index rc=%d\n", rc); + pll_res->index = 0; + } + + pll_res->ssc_en = of_property_read_bool(pdev->dev.of_node, + "qcom,dsi-pll-ssc-en"); + + if (pll_res->ssc_en) { + pr_info("%s: label=%s PLL SSC enabled\n", __func__, label); + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,ssc-frequency-hz", &pll_res->ssc_freq); + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,ssc-ppm", &pll_res->ssc_ppm); + + pll_res->ssc_center = false; + + label = of_get_property(pdev->dev.of_node, + "qcom,dsi-pll-ssc-mode", NULL); + + if (label && !strcmp(label, "center-spread")) + pll_res->ssc_center = true; + } + + pll_base_reg = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "pll_base"); + if (!pll_base_reg) { + pr_err("Unable to get the pll base resources\n"); + rc = -ENOMEM; + goto io_error; + } + + pll_res->pll_base = ioremap(pll_base_reg->start, + resource_size(pll_base_reg)); + if (!pll_res->pll_base) { + pr_err("Unable to remap pll base resources\n"); + rc = -ENOMEM; + goto io_error; + } + + pr_debug("%s: ndx=%d base=%p\n", __func__, + pll_res->index, pll_res->pll_base); + + rc = mdss_pll_resource_parse(pdev, pll_res); + if (rc) { + pr_err("Pll resource parsing from dt failed rc=%d\n", rc); + goto res_parse_error; + } + + phy_base_reg = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "phy_base"); + if (phy_base_reg) { + pll_res->phy_base = ioremap(phy_base_reg->start, + resource_size(phy_base_reg)); + if (!pll_res->phy_base) { + pr_err("Unable to remap pll phy base resources\n"); + rc = -ENOMEM; + goto phy_io_error; + } + } + + dynamic_pll_base_reg = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "dynamic_pll_base"); + if (dynamic_pll_base_reg) { + pll_res->dyn_pll_base = ioremap(dynamic_pll_base_reg->start, + resource_size(dynamic_pll_base_reg)); + if (!pll_res->dyn_pll_base) { + pr_err("Unable to remap dynamic pll base resources\n"); + rc = -ENOMEM; + goto dyn_pll_io_error; + } + } + + gdsc_base_reg = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "gdsc_base"); + if (!gdsc_base_reg) { + pr_err("Unable to get the gdsc base resource\n"); + rc = -ENOMEM; + goto gdsc_io_error; + } + pll_res->gdsc_base = ioremap(gdsc_base_reg->start, + resource_size(gdsc_base_reg)); + if (!pll_res->gdsc_base) { + pr_err("Unable to remap gdsc base resources\n"); + rc = -ENOMEM; + goto gdsc_io_error; + } + + rc = mdss_pll_resource_init(pdev, pll_res); + if (rc) { + pr_err("Pll ndx=%d resource init failed rc=%d\n", + pll_res->index, rc); + goto res_init_error; + } + + rc = mdss_pll_clock_register(pdev, pll_res); + if (rc) { + pr_err("Pll ndx=%d clock register failed rc=%d\n", + pll_res->index, rc); + goto clock_register_error; + } + + return rc; + +clock_register_error: + mdss_pll_resource_deinit(pdev, pll_res); +res_init_error: + if (pll_res->gdsc_base) + iounmap(pll_res->gdsc_base); +gdsc_io_error: + if (pll_res->dyn_pll_base) + iounmap(pll_res->dyn_pll_base); +dyn_pll_io_error: + if (pll_res->phy_base) + iounmap(pll_res->phy_base); +phy_io_error: + mdss_pll_resource_release(pdev, pll_res); +res_parse_error: + iounmap(pll_res->pll_base); +io_error: + devm_kfree(&pdev->dev, pll_res); +error: + return rc; +} + +static int mdss_pll_remove(struct platform_device *pdev) +{ + struct mdss_pll_resources *pll_res; + + pll_res = platform_get_drvdata(pdev); + if (!pll_res) { + pr_err("Invalid PLL resource data"); + return 0; + } + + mdss_pll_resource_deinit(pdev, pll_res); + if (pll_res->phy_base) + iounmap(pll_res->phy_base); + if (pll_res->gdsc_base) + iounmap(pll_res->gdsc_base); + mdss_pll_resource_release(pdev, pll_res); + iounmap(pll_res->pll_base); + devm_kfree(&pdev->dev, pll_res); + return 0; +} + +static const struct of_device_id mdss_pll_dt_match[] = { + {.compatible = "qcom,mdss_dsi_pll_8996"}, + {.compatible = "qcom,mdss_dsi_pll_8996_v2"}, + {.compatible = "qcom,mdss_dsi_pll_cobalt"}, + {.compatible = "qcom,mdss_hdmi_pll_8996"}, + {.compatible = "qcom,mdss_hdmi_pll_8996_v2"}, + {.compatible = "qcom,mdss_hdmi_pll_8996_v3"}, + {.compatible = "qcom,mdss_hdmi_pll_8996_v3_1p8"}, + {.compatible = "qcom,mdss_dp_pll_cobalt"}, + {.compatible = "qcom,mdss_hdmi_pll_cobalt"}, + {} +}; + +MODULE_DEVICE_TABLE(of, mdss_clock_dt_match); + +static struct platform_driver mdss_pll_driver = { + .probe = mdss_pll_probe, + .remove = mdss_pll_remove, + .driver = { + .name = "mdss_pll", + .of_match_table = mdss_pll_dt_match, + }, +}; + +static int __init mdss_pll_driver_init(void) +{ + int rc; + + rc = platform_driver_register(&mdss_pll_driver); + if (rc) + pr_err("mdss_register_pll_driver() failed!\n"); + + return rc; +} +subsys_initcall(mdss_pll_driver_init); + +static void __exit mdss_pll_driver_deinit(void) +{ + platform_driver_unregister(&mdss_pll_driver); +} +module_exit(mdss_pll_driver_deinit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("mdss pll driver"); diff --git a/drivers/clk/qcom/mdss/mdss-pll.h b/drivers/clk/qcom/mdss/mdss-pll.h new file mode 100644 index 000000000000..a2eb03e09146 --- /dev/null +++ b/drivers/clk/qcom/mdss/mdss-pll.h @@ -0,0 +1,233 @@ +/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MDSS_PLL_H +#define __MDSS_PLL_H + +#include <linux/mdss_io_util.h> +#include <linux/clk/msm-clock-generic.h> +#include <linux/io.h> + +#define MDSS_PLL_REG_W(base, offset, data) \ + writel_relaxed((data), (base) + (offset)) +#define MDSS_PLL_REG_R(base, offset) readl_relaxed((base) + (offset)) + +#define PLL_CALC_DATA(addr0, addr1, data0, data1) \ + (((data1) << 24) | ((((addr1) / 4) & 0xFF) << 16) | \ + ((data0) << 8) | (((addr0) / 4) & 0xFF)) + +#define MDSS_DYN_PLL_REG_W(base, offset, addr0, addr1, data0, data1) \ + writel_relaxed(PLL_CALC_DATA(addr0, addr1, data0, data1), \ + (base) + (offset)) + +enum { + MDSS_DSI_PLL_8996, + MDSS_DSI_PLL_COBALT, + MDSS_DP_PLL_COBALT, + MDSS_HDMI_PLL_8996, + MDSS_HDMI_PLL_8996_V2, + MDSS_HDMI_PLL_8996_V3, + MDSS_HDMI_PLL_8996_V3_1_8, + MDSS_HDMI_PLL_COBALT, + MDSS_UNKNOWN_PLL, +}; + +enum { + MDSS_PLL_TARGET_8996, +}; + +#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; +}; + +struct mdss_pll_resources { + + /* Pll specific resources like GPIO, power supply, clocks, etc*/ + struct dss_module_power mp; + + /* + * dsi/edp/hmdi plls' base register, phy, gdsc and dynamic refresh + * register mapping + */ + void __iomem *pll_base; + void __iomem *phy_base; + void __iomem *gdsc_base; + void __iomem *dyn_pll_base; + + bool is_init_locked; + s64 vco_current_rate; + s64 vco_locking_rate; + s64 vco_ref_clk_rate; + + /* + * Certain pll's needs to update the same vco rate after resume in + * suspend/resume scenario. Cached the vco rate for such plls. + */ + unsigned long vco_cached_rate; + + /* dsi/edp/hmdi pll interface type */ + u32 pll_interface_type; + + /* + * Target ID. Used in pll_register API for valid target check before + * registering the PLL clocks. + */ + u32 target_id; + + /* HW recommended delay during configuration of vco clock rate */ + u32 vco_delay; + + /* Ref-count of the PLL resources */ + u32 resource_ref_cnt; + + /* + * Keep track to resource status to avoid updating same status for the + * pll from different paths + */ + bool resource_enable; + + /* + * Certain plls' do not allow vco rate update if it is on. Keep track of + * status for them to turn on/off after set rate success. + */ + bool pll_on; + + /* + * handoff_status is true of pll is already enabled by bootloader with + * continuous splash enable case. Clock API will call the handoff API + * to enable the status. It is disabled if continuous splash + * feature is disabled. + */ + bool handoff_resources; + + /* + * caching the pll trim codes in the case of dynamic refresh + */ + int cache_pll_trim_codes[2]; + + /* + * for maintaining the status of saving trim codes + */ + bool reg_upd; + + /* + * Notifier callback for MDSS gdsc regulator events + */ + struct notifier_block gdsc_cb; + + /* + * Worker function to call PLL off event + */ + struct work_struct pll_off; + + /* + * PLL index if multiple index are available. Eg. in case of + * DSI we have 2 plls. + */ + uint32_t index; + + bool ssc_en; /* share pll with master */ + bool ssc_center; /* default is down spread */ + u32 ssc_freq; + u32 ssc_ppm; + + struct mdss_pll_resources *slave; + + /* + * target pll revision information + */ + int revision; + + void *priv; + + /* + * dynamic refresh pll codes stored in this structure + */ + struct dfps_info *dfps; + +}; + +struct mdss_pll_vco_calc { + s32 div_frac_start1; + s32 div_frac_start2; + s32 div_frac_start3; + s64 dec_start1; + s64 dec_start2; + s64 pll_plllock_cmp1; + s64 pll_plllock_cmp2; + s64 pll_plllock_cmp3; +}; + +static inline bool is_gdsc_disabled(struct mdss_pll_resources *pll_res) +{ + if (!pll_res->gdsc_base) { + WARN(1, "gdsc_base register is not defined\n"); + return true; + } + + return ((readl_relaxed(pll_res->gdsc_base + 0x4) & BIT(31)) && + (!(readl_relaxed(pll_res->gdsc_base) & BIT(0)))) ? false : true; +} + +static inline int mdss_pll_div_prepare(struct clk *c) +{ + struct div_clk *div = to_div_clk(c); + /* Restore the divider's value */ + return div->ops->set_div(div, div->data.div); +} + +static inline int mdss_set_mux_sel(struct mux_clk *clk, int sel) +{ + return 0; +} + +static inline int mdss_get_mux_sel(struct mux_clk *clk) +{ + return 0; +} + +int mdss_pll_resource_enable(struct mdss_pll_resources *pll_res, bool enable); +int mdss_pll_util_resource_init(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +void mdss_pll_util_resource_deinit(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +void mdss_pll_util_resource_release(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +int mdss_pll_util_resource_enable(struct mdss_pll_resources *pll_res, + bool enable); +int mdss_pll_util_resource_parse(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +struct dss_vreg *mdss_pll_get_mp_by_reg_name(struct mdss_pll_resources *pll_res + , char *name); +#endif diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index abb47608713b..fe728f8dcbe4 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -718,6 +718,7 @@ static const char *const rk3188_critical_clocks[] __initconst = { "hclk_peri", "pclk_cpu", "pclk_peri", + "hclk_cpubus" }; static void __init rk3188_common_clk_init(struct device_node *np) diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c index 7e6b783e6eee..1b148694b633 100644 --- a/drivers/clk/rockchip/clk-rk3368.c +++ b/drivers/clk/rockchip/clk-rk3368.c @@ -165,7 +165,7 @@ static const struct rockchip_cpuclk_reg_data rk3368_cpuclkb_data = { .core_reg = RK3368_CLKSEL_CON(0), .div_core_shift = 0, .div_core_mask = 0x1f, - .mux_core_shift = 15, + .mux_core_shift = 7, }; static const struct rockchip_cpuclk_reg_data rk3368_cpuclkl_data = { @@ -218,29 +218,29 @@ static const struct rockchip_cpuclk_reg_data rk3368_cpuclkl_data = { } static struct rockchip_cpuclk_rate_table rk3368_cpuclkb_rates[] __initdata = { - RK3368_CPUCLKB_RATE(1512000000, 2, 6, 6), - RK3368_CPUCLKB_RATE(1488000000, 2, 5, 5), - RK3368_CPUCLKB_RATE(1416000000, 2, 5, 5), - RK3368_CPUCLKB_RATE(1200000000, 2, 4, 4), - RK3368_CPUCLKB_RATE(1008000000, 2, 4, 4), - RK3368_CPUCLKB_RATE( 816000000, 2, 3, 3), - RK3368_CPUCLKB_RATE( 696000000, 2, 3, 3), - RK3368_CPUCLKB_RATE( 600000000, 2, 2, 2), - RK3368_CPUCLKB_RATE( 408000000, 2, 2, 2), - RK3368_CPUCLKB_RATE( 312000000, 2, 2, 2), + RK3368_CPUCLKB_RATE(1512000000, 1, 5, 5), + RK3368_CPUCLKB_RATE(1488000000, 1, 4, 4), + RK3368_CPUCLKB_RATE(1416000000, 1, 4, 4), + RK3368_CPUCLKB_RATE(1200000000, 1, 3, 3), + RK3368_CPUCLKB_RATE(1008000000, 1, 3, 3), + RK3368_CPUCLKB_RATE( 816000000, 1, 2, 2), + RK3368_CPUCLKB_RATE( 696000000, 1, 2, 2), + RK3368_CPUCLKB_RATE( 600000000, 1, 1, 1), + RK3368_CPUCLKB_RATE( 408000000, 1, 1, 1), + RK3368_CPUCLKB_RATE( 312000000, 1, 1, 1), }; static struct rockchip_cpuclk_rate_table rk3368_cpuclkl_rates[] __initdata = { - RK3368_CPUCLKL_RATE(1512000000, 2, 7, 7), - RK3368_CPUCLKL_RATE(1488000000, 2, 6, 6), - RK3368_CPUCLKL_RATE(1416000000, 2, 6, 6), - RK3368_CPUCLKL_RATE(1200000000, 2, 5, 5), - RK3368_CPUCLKL_RATE(1008000000, 2, 5, 5), - RK3368_CPUCLKL_RATE( 816000000, 2, 4, 4), - RK3368_CPUCLKL_RATE( 696000000, 2, 3, 3), - RK3368_CPUCLKL_RATE( 600000000, 2, 3, 3), - RK3368_CPUCLKL_RATE( 408000000, 2, 2, 2), - RK3368_CPUCLKL_RATE( 312000000, 2, 2, 2), + RK3368_CPUCLKL_RATE(1512000000, 1, 6, 6), + RK3368_CPUCLKL_RATE(1488000000, 1, 5, 5), + RK3368_CPUCLKL_RATE(1416000000, 1, 5, 5), + RK3368_CPUCLKL_RATE(1200000000, 1, 4, 4), + RK3368_CPUCLKL_RATE(1008000000, 1, 4, 4), + RK3368_CPUCLKL_RATE( 816000000, 1, 3, 3), + RK3368_CPUCLKL_RATE( 696000000, 1, 2, 2), + RK3368_CPUCLKL_RATE( 600000000, 1, 2, 2), + RK3368_CPUCLKL_RATE( 408000000, 1, 1, 1), + RK3368_CPUCLKL_RATE( 312000000, 1, 1, 1), }; static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = { @@ -384,10 +384,10 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = { * Clock-Architecture Diagram 3 */ - COMPOSITE(0, "aclk_vepu", mux_pll_src_cpll_gpll_usb_p, 0, + COMPOSITE(0, "aclk_vepu", mux_pll_src_cpll_gpll_npll_usb_p, 0, RK3368_CLKSEL_CON(15), 6, 2, MFLAGS, 0, 5, DFLAGS, RK3368_CLKGATE_CON(4), 6, GFLAGS), - COMPOSITE(0, "aclk_vdpu", mux_pll_src_cpll_gpll_usb_p, 0, + COMPOSITE(0, "aclk_vdpu", mux_pll_src_cpll_gpll_npll_usb_p, 0, RK3368_CLKSEL_CON(15), 14, 2, MFLAGS, 8, 5, DFLAGS, RK3368_CLKGATE_CON(4), 7, GFLAGS), @@ -442,7 +442,7 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = { GATE(SCLK_HDMI_HDCP, "sclk_hdmi_hdcp", "xin24m", 0, RK3368_CLKGATE_CON(4), 13, GFLAGS), GATE(SCLK_HDMI_CEC, "sclk_hdmi_cec", "xin32k", 0, - RK3368_CLKGATE_CON(5), 12, GFLAGS), + RK3368_CLKGATE_CON(4), 12, GFLAGS), COMPOSITE_NODIV(0, "vip_src", mux_pll_src_cpll_gpll_p, 0, RK3368_CLKSEL_CON(21), 15, 1, MFLAGS, diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index 90d64081ddb3..f951f911786e 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -31,9 +31,8 @@ struct private_data { struct device *cpu_dev; - struct regulator *cpu_reg; struct thermal_cooling_device *cdev; - unsigned int voltage_tolerance; /* in percentage */ + const char *reg_name; }; static struct freq_attr *cpufreq_dt_attr[] = { @@ -44,175 +43,128 @@ static struct freq_attr *cpufreq_dt_attr[] = { static int set_target(struct cpufreq_policy *policy, unsigned int index) { - struct dev_pm_opp *opp; - struct cpufreq_frequency_table *freq_table = policy->freq_table; - struct clk *cpu_clk = policy->clk; struct private_data *priv = policy->driver_data; - struct device *cpu_dev = priv->cpu_dev; - struct regulator *cpu_reg = priv->cpu_reg; - unsigned long volt = 0, volt_old = 0, tol = 0; - unsigned int old_freq, new_freq; - long freq_Hz, freq_exact; - int ret; - - freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); - if (freq_Hz <= 0) - freq_Hz = freq_table[index].frequency * 1000; - freq_exact = freq_Hz; - new_freq = freq_Hz / 1000; - old_freq = clk_get_rate(cpu_clk) / 1000; + return dev_pm_opp_set_rate(priv->cpu_dev, + policy->freq_table[index].frequency * 1000); +} - if (!IS_ERR(cpu_reg)) { - unsigned long opp_freq; +/* + * An earlier version of opp-v1 bindings used to name the regulator + * "cpu0-supply", we still need to handle that for backwards compatibility. + */ +static const char *find_supply_name(struct device *dev) +{ + struct device_node *np; + struct property *pp; + int cpu = dev->id; + const char *name = NULL; - rcu_read_lock(); - opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); - if (IS_ERR(opp)) { - rcu_read_unlock(); - dev_err(cpu_dev, "failed to find OPP for %ld\n", - freq_Hz); - return PTR_ERR(opp); - } - volt = dev_pm_opp_get_voltage(opp); - opp_freq = dev_pm_opp_get_freq(opp); - rcu_read_unlock(); - tol = volt * priv->voltage_tolerance / 100; - volt_old = regulator_get_voltage(cpu_reg); - dev_dbg(cpu_dev, "Found OPP: %ld kHz, %ld uV\n", - opp_freq / 1000, volt); - } + np = of_node_get(dev->of_node); - dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n", - old_freq / 1000, (volt_old > 0) ? volt_old / 1000 : -1, - new_freq / 1000, volt ? volt / 1000 : -1); + /* This must be valid for sure */ + if (WARN_ON(!np)) + return NULL; - /* scaling up? scale voltage before frequency */ - if (!IS_ERR(cpu_reg) && new_freq > old_freq) { - ret = regulator_set_voltage_tol(cpu_reg, volt, tol); - if (ret) { - dev_err(cpu_dev, "failed to scale voltage up: %d\n", - ret); - return ret; + /* Try "cpu0" for older DTs */ + if (!cpu) { + pp = of_find_property(np, "cpu0-supply", NULL); + if (pp) { + name = "cpu0"; + goto node_put; } } - ret = clk_set_rate(cpu_clk, freq_exact); - if (ret) { - dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); - if (!IS_ERR(cpu_reg) && volt_old > 0) - regulator_set_voltage_tol(cpu_reg, volt_old, tol); - return ret; + pp = of_find_property(np, "cpu-supply", NULL); + if (pp) { + name = "cpu"; + goto node_put; } - /* scaling down? scale voltage after frequency */ - if (!IS_ERR(cpu_reg) && new_freq < old_freq) { - ret = regulator_set_voltage_tol(cpu_reg, volt, tol); - if (ret) { - dev_err(cpu_dev, "failed to scale voltage down: %d\n", - ret); - clk_set_rate(cpu_clk, old_freq * 1000); - } - } - - return ret; + dev_dbg(dev, "no regulator for cpu%d\n", cpu); +node_put: + of_node_put(np); + return name; } -static int allocate_resources(int cpu, struct device **cdev, - struct regulator **creg, struct clk **cclk) +static int resources_available(void) { struct device *cpu_dev; struct regulator *cpu_reg; struct clk *cpu_clk; int ret = 0; - char *reg_cpu0 = "cpu0", *reg_cpu = "cpu", *reg; + const char *name; - cpu_dev = get_cpu_device(cpu); + cpu_dev = get_cpu_device(0); if (!cpu_dev) { - pr_err("failed to get cpu%d device\n", cpu); + pr_err("failed to get cpu0 device\n"); return -ENODEV; } - /* Try "cpu0" for older DTs */ - if (!cpu) - reg = reg_cpu0; - else - reg = reg_cpu; - -try_again: - cpu_reg = regulator_get_optional(cpu_dev, reg); - if (IS_ERR(cpu_reg)) { + cpu_clk = clk_get(cpu_dev, NULL); + ret = PTR_ERR_OR_ZERO(cpu_clk); + if (ret) { /* - * If cpu's regulator supply node is present, but regulator is - * not yet registered, we should try defering probe. + * If cpu's clk node is present, but clock is not yet + * registered, we should try defering probe. */ - if (PTR_ERR(cpu_reg) == -EPROBE_DEFER) { - dev_dbg(cpu_dev, "cpu%d regulator not ready, retry\n", - cpu); - return -EPROBE_DEFER; - } - - /* Try with "cpu-supply" */ - if (reg == reg_cpu0) { - reg = reg_cpu; - goto try_again; - } + if (ret == -EPROBE_DEFER) + dev_dbg(cpu_dev, "clock not ready, retry\n"); + else + dev_err(cpu_dev, "failed to get clock: %d\n", ret); - dev_dbg(cpu_dev, "no regulator for cpu%d: %ld\n", - cpu, PTR_ERR(cpu_reg)); + return ret; } - cpu_clk = clk_get(cpu_dev, NULL); - if (IS_ERR(cpu_clk)) { - /* put regulator */ - if (!IS_ERR(cpu_reg)) - regulator_put(cpu_reg); + clk_put(cpu_clk); - ret = PTR_ERR(cpu_clk); + name = find_supply_name(cpu_dev); + /* Platform doesn't require regulator */ + if (!name) + return 0; + cpu_reg = regulator_get_optional(cpu_dev, name); + ret = PTR_ERR_OR_ZERO(cpu_reg); + if (ret) { /* - * If cpu's clk node is present, but clock is not yet - * registered, we should try defering probe. + * If cpu's regulator supply node is present, but regulator is + * not yet registered, we should try defering probe. */ if (ret == -EPROBE_DEFER) - dev_dbg(cpu_dev, "cpu%d clock not ready, retry\n", cpu); + dev_dbg(cpu_dev, "cpu0 regulator not ready, retry\n"); else - dev_err(cpu_dev, "failed to get cpu%d clock: %d\n", cpu, - ret); - } else { - *cdev = cpu_dev; - *creg = cpu_reg; - *cclk = cpu_clk; + dev_dbg(cpu_dev, "no regulator for cpu0: %d\n", ret); + + return ret; } - return ret; + regulator_put(cpu_reg); + return 0; } static int cpufreq_init(struct cpufreq_policy *policy) { struct cpufreq_frequency_table *freq_table; - struct device_node *np; struct private_data *priv; struct device *cpu_dev; - struct regulator *cpu_reg; struct clk *cpu_clk; struct dev_pm_opp *suspend_opp; - unsigned long min_uV = ~0, max_uV = 0; unsigned int transition_latency; - bool need_update = false; + bool opp_v1 = false; + const char *name; int ret; - ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk); - if (ret) { - pr_err("%s: Failed to allocate resources: %d\n", __func__, ret); - return ret; + cpu_dev = get_cpu_device(policy->cpu); + if (!cpu_dev) { + pr_err("failed to get cpu%d device\n", policy->cpu); + return -ENODEV; } - np = of_node_get(cpu_dev->of_node); - if (!np) { - dev_err(cpu_dev, "failed to find cpu%d node\n", policy->cpu); - ret = -ENOENT; - goto out_put_reg_clk; + cpu_clk = clk_get(cpu_dev, NULL); + if (IS_ERR(cpu_clk)) { + ret = PTR_ERR(cpu_clk); + dev_err(cpu_dev, "%s: failed to get clk: %d\n", __func__, ret); + return ret; } /* Get OPP-sharing information from "operating-points-v2" bindings */ @@ -223,9 +175,23 @@ static int cpufreq_init(struct cpufreq_policy *policy) * finding shared-OPPs for backward compatibility. */ if (ret == -ENOENT) - need_update = true; + opp_v1 = true; else - goto out_node_put; + goto out_put_clk; + } + + /* + * OPP layer will be taking care of regulators now, but it needs to know + * the name of the regulator first. + */ + name = find_supply_name(cpu_dev); + if (name) { + ret = dev_pm_opp_set_regulator(cpu_dev, name); + if (ret) { + dev_err(cpu_dev, "Failed to set regulator for cpu%d: %d\n", + policy->cpu, ret); + goto out_put_clk; + } } /* @@ -246,12 +212,12 @@ static int cpufreq_init(struct cpufreq_policy *policy) */ ret = dev_pm_opp_get_opp_count(cpu_dev); if (ret <= 0) { - pr_debug("OPP table is not ready, deferring probe\n"); + dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n"); ret = -EPROBE_DEFER; goto out_free_opp; } - if (need_update) { + if (opp_v1) { struct cpufreq_dt_platform_data *pd = cpufreq_get_driver_data(); if (!pd || !pd->independent_clocks) @@ -265,10 +231,6 @@ static int cpufreq_init(struct cpufreq_policy *policy) if (ret) dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", __func__, ret); - - of_property_read_u32(np, "clock-latency", &transition_latency); - } else { - transition_latency = dev_pm_opp_get_max_clock_latency(cpu_dev); } priv = kzalloc(sizeof(*priv), GFP_KERNEL); @@ -277,62 +239,16 @@ static int cpufreq_init(struct cpufreq_policy *policy) goto out_free_opp; } - of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance); - - if (!transition_latency) - transition_latency = CPUFREQ_ETERNAL; - - if (!IS_ERR(cpu_reg)) { - unsigned long opp_freq = 0; - - /* - * Disable any OPPs where the connected regulator isn't able to - * provide the specified voltage and record minimum and maximum - * voltage levels. - */ - while (1) { - struct dev_pm_opp *opp; - unsigned long opp_uV, tol_uV; - - rcu_read_lock(); - opp = dev_pm_opp_find_freq_ceil(cpu_dev, &opp_freq); - if (IS_ERR(opp)) { - rcu_read_unlock(); - break; - } - opp_uV = dev_pm_opp_get_voltage(opp); - rcu_read_unlock(); - - tol_uV = opp_uV * priv->voltage_tolerance / 100; - if (regulator_is_supported_voltage(cpu_reg, - opp_uV - tol_uV, - opp_uV + tol_uV)) { - if (opp_uV < min_uV) - min_uV = opp_uV; - if (opp_uV > max_uV) - max_uV = opp_uV; - } else { - dev_pm_opp_disable(cpu_dev, opp_freq); - } - - opp_freq++; - } - - ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV); - if (ret > 0) - transition_latency += ret * 1000; - } + priv->reg_name = name; ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); if (ret) { - pr_err("failed to init cpufreq table: %d\n", ret); + dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); goto out_free_priv; } priv->cpu_dev = cpu_dev; - priv->cpu_reg = cpu_reg; policy->driver_data = priv; - policy->clk = cpu_clk; rcu_read_lock(); @@ -357,9 +273,11 @@ static int cpufreq_init(struct cpufreq_policy *policy) cpufreq_dt_attr[1] = &cpufreq_freq_attr_scaling_boost_freqs; } - policy->cpuinfo.transition_latency = transition_latency; + transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev); + if (!transition_latency) + transition_latency = CPUFREQ_ETERNAL; - of_node_put(np); + policy->cpuinfo.transition_latency = transition_latency; return 0; @@ -369,12 +287,10 @@ out_free_priv: kfree(priv); out_free_opp: dev_pm_opp_of_cpumask_remove_table(policy->cpus); -out_node_put: - of_node_put(np); -out_put_reg_clk: + if (name) + dev_pm_opp_put_regulator(cpu_dev); +out_put_clk: clk_put(cpu_clk); - if (!IS_ERR(cpu_reg)) - regulator_put(cpu_reg); return ret; } @@ -386,9 +302,10 @@ static int cpufreq_exit(struct cpufreq_policy *policy) cpufreq_cooling_unregister(priv->cdev); dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); + if (priv->reg_name) + dev_pm_opp_put_regulator(priv->cpu_dev); + clk_put(policy->clk); - if (!IS_ERR(priv->cpu_reg)) - regulator_put(priv->cpu_reg); kfree(priv); return 0; @@ -407,8 +324,13 @@ static void cpufreq_ready(struct cpufreq_policy *policy) * thermal DT code takes care of matching them. */ if (of_find_property(np, "#cooling-cells", NULL)) { - priv->cdev = of_cpufreq_cooling_register(np, - policy->related_cpus); + u32 power_coefficient = 0; + + of_property_read_u32(np, "dynamic-power-coefficient", + &power_coefficient); + + priv->cdev = of_cpufreq_power_cooling_register(np, + policy->related_cpus, power_coefficient, NULL); if (IS_ERR(priv->cdev)) { dev_err(priv->cpu_dev, "running cpufreq without cooling device: %ld\n", @@ -436,9 +358,6 @@ static struct cpufreq_driver dt_cpufreq_driver = { static int dt_cpufreq_probe(struct platform_device *pdev) { - struct device *cpu_dev; - struct regulator *cpu_reg; - struct clk *cpu_clk; int ret; /* @@ -448,19 +367,15 @@ static int dt_cpufreq_probe(struct platform_device *pdev) * * FIXME: Is checking this only for CPU0 sufficient ? */ - ret = allocate_resources(0, &cpu_dev, &cpu_reg, &cpu_clk); + ret = resources_available(); if (ret) return ret; - clk_put(cpu_clk); - if (!IS_ERR(cpu_reg)) - regulator_put(cpu_reg); - dt_cpufreq_driver.driver_data = dev_get_platdata(&pdev->dev); ret = cpufreq_register_driver(&dt_cpufreq_driver); if (ret) - dev_err(cpu_dev, "failed register driver: %d\n", ret); + dev_err(&pdev->dev, "failed register driver: %d\n", ret); return ret; } diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 680d0b596970..386c85fc714b 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -372,18 +372,18 @@ config CRYPTO_DEV_QCRYPTO config CRYPTO_DEV_QCOM_MSM_QCE tristate "Qualcomm Crypto Engine (QCE) module" - select CRYPTO_DEV_QCE50 if ARCH_APQ8084 || ARCH_MSM8916 || ARCH_MSM8994 || ARCH_MSM8996 || ARCH_MSM8992 || ARCH_MSMTITANIUM || ARCH_MSM8909 || ARCH_MSMCOBALT + select CRYPTO_DEV_QCE50 if ARCH_APQ8084 || ARCH_MSM8916 || ARCH_MSM8994 || ARCH_MSM8996 || ARCH_MSM8992 || ARCH_MSMTITANIUM || ARCH_MSM8909 || ARCH_MSMCOBALT || ARCH_MSMFALCON default n help This driver supports Qualcomm Crypto Engine in MSM7x30, MSM8660 MSM8x55, MSM8960, MSM9615, MSM8916, MSM8994, MSM8996, FSM9900, - MSMTITANINUM, APQ8084 and MSMCOBALT. + MSMTITANINUM, APQ8084, MSMCOBALT and MSMFALCON. To compile this driver as a module, choose M here: the For MSM7x30 MSM8660 and MSM8x55 the module is called qce For MSM8960, APQ8064 and MSM9615 the module is called qce40 For MSM8974, MSM8916, MSM8994, MSM8996, MSM8992, MSMTITANIUM, - APQ8084 and MSMCOBALT the module is called qce50. + APQ8084, MSMCOBALT and MSMFALCON the module is called qce50. config CRYPTO_DEV_QCEDEV tristate "QCEDEV Interface to CE module" @@ -391,7 +391,7 @@ config CRYPTO_DEV_QCEDEV help This driver supports Qualcomm QCEDEV Crypto in MSM7x30, MSM8660, MSM8960, MSM9615, APQ8064, MSM8974, MSM8916, MSM8994, MSM8996, - APQ8084, MSMCOBALT. This exposes the interface to the QCE hardware + APQ8084, MSMCOBALT, MSMFALCON. This exposes the interface to the QCE hardware accelerator via IOCTLs. To compile this driver as a module, choose M here: the diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c index fb16d812c8f5..1dffb13e5c2f 100644 --- a/drivers/crypto/atmel-aes.c +++ b/drivers/crypto/atmel-aes.c @@ -1396,9 +1396,9 @@ static int atmel_aes_probe(struct platform_device *pdev) } aes_dd->io_base = devm_ioremap_resource(&pdev->dev, aes_res); - if (!aes_dd->io_base) { + if (IS_ERR(aes_dd->io_base)) { dev_err(dev, "can't ioremap\n"); - err = -ENOMEM; + err = PTR_ERR(aes_dd->io_base); goto res_err; } diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c index 3178f84d2757..0dadb6332f0e 100644 --- a/drivers/crypto/atmel-sha.c +++ b/drivers/crypto/atmel-sha.c @@ -1405,9 +1405,9 @@ static int atmel_sha_probe(struct platform_device *pdev) } sha_dd->io_base = devm_ioremap_resource(&pdev->dev, sha_res); - if (!sha_dd->io_base) { + if (IS_ERR(sha_dd->io_base)) { dev_err(dev, "can't ioremap\n"); - err = -ENOMEM; + err = PTR_ERR(sha_dd->io_base); goto res_err; } diff --git a/drivers/crypto/atmel-tdes.c b/drivers/crypto/atmel-tdes.c index 2c7a628d0375..bf467d7be35c 100644 --- a/drivers/crypto/atmel-tdes.c +++ b/drivers/crypto/atmel-tdes.c @@ -1417,9 +1417,9 @@ static int atmel_tdes_probe(struct platform_device *pdev) } tdes_dd->io_base = devm_ioremap_resource(&pdev->dev, tdes_res); - if (!tdes_dd->io_base) { + if (IS_ERR(tdes_dd->io_base)) { dev_err(dev, "can't ioremap\n"); - err = -ENOMEM; + err = PTR_ERR(tdes_dd->io_base); goto res_err; } diff --git a/drivers/crypto/ccp/ccp-crypto-aes-cmac.c b/drivers/crypto/ccp/ccp-crypto-aes-cmac.c index d89f20c04266..3d9acc53d247 100644 --- a/drivers/crypto/ccp/ccp-crypto-aes-cmac.c +++ b/drivers/crypto/ccp/ccp-crypto-aes-cmac.c @@ -220,6 +220,39 @@ static int ccp_aes_cmac_digest(struct ahash_request *req) return ccp_aes_cmac_finup(req); } +static int ccp_aes_cmac_export(struct ahash_request *req, void *out) +{ + struct ccp_aes_cmac_req_ctx *rctx = ahash_request_ctx(req); + struct ccp_aes_cmac_exp_ctx state; + + state.null_msg = rctx->null_msg; + memcpy(state.iv, rctx->iv, sizeof(state.iv)); + state.buf_count = rctx->buf_count; + memcpy(state.buf, rctx->buf, sizeof(state.buf)); + + /* 'out' may not be aligned so memcpy from local variable */ + memcpy(out, &state, sizeof(state)); + + return 0; +} + +static int ccp_aes_cmac_import(struct ahash_request *req, const void *in) +{ + struct ccp_aes_cmac_req_ctx *rctx = ahash_request_ctx(req); + struct ccp_aes_cmac_exp_ctx state; + + /* 'in' may not be aligned so memcpy to local variable */ + memcpy(&state, in, sizeof(state)); + + memset(rctx, 0, sizeof(*rctx)); + rctx->null_msg = state.null_msg; + memcpy(rctx->iv, state.iv, sizeof(rctx->iv)); + rctx->buf_count = state.buf_count; + memcpy(rctx->buf, state.buf, sizeof(rctx->buf)); + + return 0; +} + static int ccp_aes_cmac_setkey(struct crypto_ahash *tfm, const u8 *key, unsigned int key_len) { @@ -352,10 +385,13 @@ int ccp_register_aes_cmac_algs(struct list_head *head) alg->final = ccp_aes_cmac_final; alg->finup = ccp_aes_cmac_finup; alg->digest = ccp_aes_cmac_digest; + alg->export = ccp_aes_cmac_export; + alg->import = ccp_aes_cmac_import; alg->setkey = ccp_aes_cmac_setkey; halg = &alg->halg; halg->digestsize = AES_BLOCK_SIZE; + halg->statesize = sizeof(struct ccp_aes_cmac_exp_ctx); base = &halg->base; snprintf(base->cra_name, CRYPTO_MAX_ALG_NAME, "cmac(aes)"); diff --git a/drivers/crypto/ccp/ccp-crypto-sha.c b/drivers/crypto/ccp/ccp-crypto-sha.c index d14b3f28e010..8ef06fad8b14 100644 --- a/drivers/crypto/ccp/ccp-crypto-sha.c +++ b/drivers/crypto/ccp/ccp-crypto-sha.c @@ -207,6 +207,43 @@ static int ccp_sha_digest(struct ahash_request *req) return ccp_sha_finup(req); } +static int ccp_sha_export(struct ahash_request *req, void *out) +{ + struct ccp_sha_req_ctx *rctx = ahash_request_ctx(req); + struct ccp_sha_exp_ctx state; + + state.type = rctx->type; + state.msg_bits = rctx->msg_bits; + state.first = rctx->first; + memcpy(state.ctx, rctx->ctx, sizeof(state.ctx)); + state.buf_count = rctx->buf_count; + memcpy(state.buf, rctx->buf, sizeof(state.buf)); + + /* 'out' may not be aligned so memcpy from local variable */ + memcpy(out, &state, sizeof(state)); + + return 0; +} + +static int ccp_sha_import(struct ahash_request *req, const void *in) +{ + struct ccp_sha_req_ctx *rctx = ahash_request_ctx(req); + struct ccp_sha_exp_ctx state; + + /* 'in' may not be aligned so memcpy to local variable */ + memcpy(&state, in, sizeof(state)); + + memset(rctx, 0, sizeof(*rctx)); + rctx->type = state.type; + rctx->msg_bits = state.msg_bits; + rctx->first = state.first; + memcpy(rctx->ctx, state.ctx, sizeof(rctx->ctx)); + rctx->buf_count = state.buf_count; + memcpy(rctx->buf, state.buf, sizeof(rctx->buf)); + + return 0; +} + static int ccp_sha_setkey(struct crypto_ahash *tfm, const u8 *key, unsigned int key_len) { @@ -403,9 +440,12 @@ static int ccp_register_sha_alg(struct list_head *head, alg->final = ccp_sha_final; alg->finup = ccp_sha_finup; alg->digest = ccp_sha_digest; + alg->export = ccp_sha_export; + alg->import = ccp_sha_import; halg = &alg->halg; halg->digestsize = def->digest_size; + halg->statesize = sizeof(struct ccp_sha_exp_ctx); base = &halg->base; snprintf(base->cra_name, CRYPTO_MAX_ALG_NAME, "%s", def->name); diff --git a/drivers/crypto/ccp/ccp-crypto.h b/drivers/crypto/ccp/ccp-crypto.h index 76a96f0f44c6..a326ec20bfa8 100644 --- a/drivers/crypto/ccp/ccp-crypto.h +++ b/drivers/crypto/ccp/ccp-crypto.h @@ -129,6 +129,15 @@ struct ccp_aes_cmac_req_ctx { struct ccp_cmd cmd; }; +struct ccp_aes_cmac_exp_ctx { + unsigned int null_msg; + + u8 iv[AES_BLOCK_SIZE]; + + unsigned int buf_count; + u8 buf[AES_BLOCK_SIZE]; +}; + /***** SHA related defines *****/ #define MAX_SHA_CONTEXT_SIZE SHA256_DIGEST_SIZE #define MAX_SHA_BLOCK_SIZE SHA256_BLOCK_SIZE @@ -171,6 +180,19 @@ struct ccp_sha_req_ctx { struct ccp_cmd cmd; }; +struct ccp_sha_exp_ctx { + enum ccp_sha_type type; + + u64 msg_bits; + + unsigned int first; + + u8 ctx[MAX_SHA_CONTEXT_SIZE]; + + unsigned int buf_count; + u8 buf[MAX_SHA_BLOCK_SIZE]; +}; + /***** Common Context Structure *****/ struct ccp_ctx { int (*complete)(struct crypto_async_request *req, int ret); diff --git a/drivers/crypto/marvell/cesa.c b/drivers/crypto/marvell/cesa.c index c0656e7f37b5..80239ae69527 100644 --- a/drivers/crypto/marvell/cesa.c +++ b/drivers/crypto/marvell/cesa.c @@ -420,7 +420,7 @@ static int mv_cesa_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); cesa->regs = devm_ioremap_resource(dev, res); if (IS_ERR(cesa->regs)) - return -ENOMEM; + return PTR_ERR(cesa->regs); ret = mv_cesa_dev_dma_init(cesa); if (ret) diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c index 6bcc08e94657..61f99370863d 100644 --- a/drivers/crypto/msm/qce50.c +++ b/drivers/crypto/msm/qce50.c @@ -1,6 +1,6 @@ /* Qualcomm Crypto Engine driver. * - * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-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 @@ -69,7 +69,7 @@ static LIST_HEAD(qce50_bam_list); /* Max number of request supported */ #define MAX_QCE_BAM_REQ 8 /* Interrupt flag will be set for every SET_INTR_AT_REQ request */ -#define SET_INTR_AT_REQ (MAX_QCE_BAM_REQ - 2) +#define SET_INTR_AT_REQ (MAX_QCE_BAM_REQ / 2) /* To create extra request space to hold dummy request */ #define MAX_QCE_BAM_REQ_WITH_DUMMY_REQ (MAX_QCE_BAM_REQ + 1) /* Allocate the memory for MAX_QCE_BAM_REQ + 1 (for dummy request) */ @@ -84,6 +84,12 @@ static LIST_HEAD(qce50_bam_list); /* Index to point the dummy request */ #define DUMMY_REQ_INDEX MAX_QCE_BAM_REQ +enum qce_owner { + QCE_OWNER_NONE = 0, + QCE_OWNER_CLIENT = 1, + QCE_OWNER_TIMEOUT = 2 +}; + struct dummy_request { struct qce_sha_req sreq; uint8_t *in_buf; @@ -133,9 +139,8 @@ struct qce_device { struct ce_bam_info ce_bam_info; struct ce_request_info ce_request_info[MAX_QCE_ALLOC_BAM_REQ]; unsigned int ce_request_index; - spinlock_t lock; - spinlock_t sps_lock; - unsigned int no_of_queued_req; + enum qce_owner owner; + atomic_t no_of_queued_req; struct timer_list timer; struct dummy_request dummyreq; unsigned int mode; @@ -144,6 +149,7 @@ struct qce_device { struct qce_driver_stats qce_stats; atomic_t bunch_cmd_seq; atomic_t last_intr_seq; + bool cadence_flag; }; static void print_notify_debug(struct sps_event_notify *notify); @@ -2539,7 +2545,6 @@ static int _qce_sps_add_cmd(struct qce_device *pce_dev, uint32_t flag, static int _qce_sps_transfer(struct qce_device *pce_dev, int req_info) { int rc = 0; - unsigned long flags; struct ce_sps_data *pce_sps_data; pce_sps_data = &pce_dev->ce_request_info[req_info].ce_sps; @@ -2551,7 +2556,6 @@ static int _qce_sps_transfer(struct qce_device *pce_dev, int req_info) (unsigned int) req_info)); _qce_dump_descr_fifos_dbg(pce_dev, req_info); - spin_lock_irqsave(&pce_dev->sps_lock, flags); if (pce_sps_data->in_transfer.iovec_count) { rc = sps_transfer(pce_dev->ce_bam_info.consumer.pipe, &pce_sps_data->in_transfer); @@ -2570,7 +2574,6 @@ static int _qce_sps_transfer(struct qce_device *pce_dev, int req_info) ret: if (rc) _qce_dump_descr_fifos(pce_dev, req_info); - spin_unlock_irqrestore(&pce_dev->sps_lock, flags); return rc; } @@ -2955,23 +2958,20 @@ static inline int qce_alloc_req_info(struct qce_device *pce_dev) } } pr_warn("pcedev %d no reqs available no_of_queued_req %d\n", - pce_dev->dev_no, pce_dev->no_of_queued_req); + pce_dev->dev_no, atomic_read( + &pce_dev->no_of_queued_req)); return -EBUSY; } static inline void qce_free_req_info(struct qce_device *pce_dev, int req_info, bool is_complete) { - unsigned long flags; - - spin_lock_irqsave(&pce_dev->lock, flags); pce_dev->ce_request_info[req_info].xfer_type = QCE_XFER_TYPE_LAST; if (xchg(&pce_dev->ce_request_info[req_info].in_use, false) == true) { if (req_info < MAX_QCE_BAM_REQ && is_complete) - pce_dev->no_of_queued_req--; + atomic_dec(&pce_dev->no_of_queued_req); } else pr_warn("request info %d free already\n", req_info); - spin_unlock_irqrestore(&pce_dev->lock, flags); } static void print_notify_debug(struct sps_event_notify *notify) @@ -3018,7 +3018,6 @@ static void qce_multireq_timeout(unsigned long data) { struct qce_device *pce_dev = (struct qce_device *)data; int ret = 0; - unsigned long flags; int last_seq; last_seq = atomic_read(&pce_dev->bunch_cmd_seq); @@ -3029,27 +3028,29 @@ static void qce_multireq_timeout(unsigned long data) return; } /* last bunch mode command time out */ - spin_lock_irqsave(&pce_dev->lock, flags); + if (cmpxchg(&pce_dev->owner, QCE_OWNER_NONE, QCE_OWNER_TIMEOUT) + != QCE_OWNER_NONE) { + mod_timer(&(pce_dev->timer), (jiffies + DELAY_IN_JIFFIES)); + return; + } del_timer(&(pce_dev->timer)); pce_dev->mode = IN_INTERRUPT_MODE; pce_dev->qce_stats.no_of_timeouts++; pr_debug("pcedev %d mode switch to INTR\n", pce_dev->dev_no); - spin_unlock_irqrestore(&pce_dev->lock, flags); ret = qce_dummy_req(pce_dev); if (ret) pr_warn("pcedev %d: Failed to insert dummy req\n", pce_dev->dev_no); + cmpxchg(&pce_dev->owner, QCE_OWNER_TIMEOUT, QCE_OWNER_NONE); } void qce_get_driver_stats(void *handle) { - unsigned long flags; struct qce_device *pce_dev = (struct qce_device *) handle; if (!_qce50_disp_stats) return; - spin_lock_irqsave(&pce_dev->lock, flags); pr_info("Engine %d timeout occuured %d\n", pce_dev->dev_no, pce_dev->qce_stats.no_of_timeouts); pr_info("Engine %d dummy request inserted %d\n", pce_dev->dev_no, @@ -3059,20 +3060,16 @@ void qce_get_driver_stats(void *handle) else pr_info("Engine %d is in INTERRUPT MODE\n", pce_dev->dev_no); pr_info("Engine %d outstanding request %d\n", pce_dev->dev_no, - pce_dev->no_of_queued_req); - spin_unlock_irqrestore(&pce_dev->lock, flags); + atomic_read(&pce_dev->no_of_queued_req)); } EXPORT_SYMBOL(qce_get_driver_stats); void qce_clear_driver_stats(void *handle) { - unsigned long flags; struct qce_device *pce_dev = (struct qce_device *) handle; - spin_lock_irqsave(&pce_dev->lock, flags); pce_dev->qce_stats.no_of_timeouts = 0; pce_dev->qce_stats.no_of_dummy_reqs = 0; - spin_unlock_irqrestore(&pce_dev->lock, flags); } EXPORT_SYMBOL(qce_clear_driver_stats); @@ -3084,7 +3081,6 @@ static void _sps_producer_callback(struct sps_event_notify *notify) unsigned int req_info; struct ce_sps_data *pce_sps_data; struct ce_request_info *preq_info; - unsigned long flags; print_notify_debug(notify); @@ -3113,10 +3109,8 @@ static void _sps_producer_callback(struct sps_event_notify *notify) &pce_sps_data->out_transfer); _qce_set_flag(&pce_sps_data->out_transfer, SPS_IOVEC_FLAG_INT); - spin_lock_irqsave(&pce_dev->sps_lock, flags); rc = sps_transfer(pce_dev->ce_bam_info.producer.pipe, &pce_sps_data->out_transfer); - spin_unlock_irqrestore(&pce_dev->sps_lock, flags); if (rc) { pr_err("sps_xfr() fail (producer pipe=0x%lx) rc = %d\n", (uintptr_t)pce_dev->ce_bam_info.producer.pipe, @@ -4590,18 +4584,27 @@ static int qce_dummy_req(struct qce_device *pce_dev) static int select_mode(struct qce_device *pce_dev, struct ce_request_info *preq_info) { - unsigned long flags; struct ce_sps_data *pce_sps_data = &preq_info->ce_sps; + unsigned int no_of_queued_req; + unsigned int cadence; if (!pce_dev->no_get_around) { _qce_set_flag(&pce_sps_data->out_transfer, SPS_IOVEC_FLAG_INT); return 0; } - spin_lock_irqsave(&pce_dev->lock, flags); - pce_dev->no_of_queued_req++; + /* + * claim ownership of device + */ +again: + if (cmpxchg(&pce_dev->owner, QCE_OWNER_NONE, QCE_OWNER_CLIENT) + != QCE_OWNER_NONE) { + ndelay(40); + goto again; + } + no_of_queued_req = atomic_inc_return(&pce_dev->no_of_queued_req); if (pce_dev->mode == IN_INTERRUPT_MODE) { - if (pce_dev->no_of_queued_req >= MAX_BUNCH_MODE_REQ) { + if (no_of_queued_req >= MAX_BUNCH_MODE_REQ) { pce_dev->mode = IN_BUNCH_MODE; pr_debug("pcedev %d mode switch to BUNCH\n", pce_dev->dev_no); @@ -4618,17 +4621,21 @@ static int select_mode(struct qce_device *pce_dev, } } else { pce_dev->intr_cadence++; - if (pce_dev->intr_cadence >= SET_INTR_AT_REQ) { + cadence = (preq_info->req_len >> 7) + 1; + if (cadence > SET_INTR_AT_REQ) + cadence = SET_INTR_AT_REQ; + if (pce_dev->intr_cadence < cadence || ((pce_dev->intr_cadence + == cadence) && pce_dev->cadence_flag)) + atomic_inc(&pce_dev->bunch_cmd_seq); + else { _qce_set_flag(&pce_sps_data->out_transfer, SPS_IOVEC_FLAG_INT); pce_dev->intr_cadence = 0; atomic_set(&pce_dev->bunch_cmd_seq, 0); atomic_set(&pce_dev->last_intr_seq, 0); - } else { - atomic_inc(&pce_dev->bunch_cmd_seq); + pce_dev->cadence_flag = ~pce_dev->cadence_flag; } } - spin_unlock_irqrestore(&pce_dev->lock, flags); return 0; } @@ -4746,6 +4753,7 @@ static int _qce_aead_ccm_req(void *handle, struct qce_req *q_req) /* setup xfer type for producer callback handling */ preq_info->xfer_type = QCE_XFER_AEAD; + preq_info->req_len = totallen_in; _qce_sps_iovec_count_init(pce_dev, req_info); @@ -4804,8 +4812,9 @@ static int _qce_aead_ccm_req(void *handle, struct qce_req *q_req) _qce_ccm_get_around_output(pce_dev, preq_info, q_req->dir); select_mode(pce_dev, preq_info); + rc = _qce_sps_transfer(pce_dev, req_info); + cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE); } - rc = _qce_sps_transfer(pce_dev, req_info); if (rc) goto bad; return 0; @@ -4973,6 +4982,7 @@ int qce_aead_req(void *handle, struct qce_req *q_req) /* setup xfer type for producer callback handling */ preq_info->xfer_type = QCE_XFER_AEAD; + preq_info->req_len = totallen; _qce_sps_iovec_count_init(pce_dev, req_info); @@ -5013,6 +5023,7 @@ int qce_aead_req(void *handle, struct qce_req *q_req) SPS_IOVEC_FLAG_INT); pce_sps_data->producer_state = QCE_PIPE_STATE_COMP; } + rc = _qce_sps_transfer(pce_dev, req_info); } else { if (_qce_sps_add_sg_data(pce_dev, areq->src, totallen, &pce_sps_data->in_transfer)) @@ -5040,8 +5051,9 @@ int qce_aead_req(void *handle, struct qce_req *q_req) pce_sps_data->producer_state = QCE_PIPE_STATE_IDLE; } select_mode(pce_dev, preq_info); + rc = _qce_sps_transfer(pce_dev, req_info); + cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE); } - rc = _qce_sps_transfer(pce_dev, req_info); if (rc) goto bad; return 0; @@ -5129,6 +5141,7 @@ int qce_ablk_cipher_req(void *handle, struct qce_req *c_req) /* setup xfer type for producer callback handling */ preq_info->xfer_type = QCE_XFER_CIPHERING; + preq_info->req_len = areq->nbytes; _qce_sps_iovec_count_init(pce_dev, req_info); if (pce_dev->support_cmd_dscr) @@ -5160,8 +5173,8 @@ int qce_ablk_cipher_req(void *handle, struct qce_req *c_req) } select_mode(pce_dev, preq_info); - rc = _qce_sps_transfer(pce_dev, req_info); + cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE); if (rc) goto bad; @@ -5233,6 +5246,7 @@ int qce_process_sha_req(void *handle, struct qce_sha_req *sreq) /* setup xfer type for producer callback handling */ preq_info->xfer_type = QCE_XFER_HASHING; + preq_info->req_len = sreq->size; _qce_sps_iovec_count_init(pce_dev, req_info); @@ -5261,11 +5275,14 @@ int qce_process_sha_req(void *handle, struct qce_sha_req *sreq) &pce_sps_data->out_transfer)) goto bad; - if (is_dummy) + if (is_dummy) { _qce_set_flag(&pce_sps_data->out_transfer, SPS_IOVEC_FLAG_INT); - else + rc = _qce_sps_transfer(pce_dev, req_info); + } else { select_mode(pce_dev, preq_info); - rc = _qce_sps_transfer(pce_dev, req_info); + rc = _qce_sps_transfer(pce_dev, req_info); + cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE); + } if (rc) goto bad; return 0; @@ -5353,6 +5370,7 @@ int qce_f8_req(void *handle, struct qce_f8_req *req, /* setup xfer type for producer callback handling */ preq_info->xfer_type = QCE_XFER_F8; + preq_info->req_len = req->data_len; _qce_sps_iovec_count_init(pce_dev, req_info); @@ -5378,8 +5396,8 @@ int qce_f8_req(void *handle, struct qce_f8_req *req, &pce_sps_data->out_transfer); select_mode(pce_dev, preq_info); - rc = _qce_sps_transfer(pce_dev, req_info); + cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE); if (rc) goto bad; return 0; @@ -5468,6 +5486,7 @@ int qce_f8_multi_pkt_req(void *handle, struct qce_f8_multi_pkt_req *mreq, /* setup xfer type for producer callback handling */ preq_info->xfer_type = QCE_XFER_F8; + preq_info->req_len = total; _qce_sps_iovec_count_init(pce_dev, req_info); @@ -5492,8 +5511,8 @@ int qce_f8_multi_pkt_req(void *handle, struct qce_f8_multi_pkt_req *mreq, &pce_sps_data->out_transfer); select_mode(pce_dev, preq_info); - rc = _qce_sps_transfer(pce_dev, req_info); + cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE); if (rc == 0) return 0; @@ -5554,6 +5573,7 @@ int qce_f9_req(void *handle, struct qce_f9_req *req, void *cookie, /* setup xfer type for producer callback handling */ preq_info->xfer_type = QCE_XFER_F9; + preq_info->req_len = req->msize; _qce_sps_iovec_count_init(pce_dev, req_info); if (pce_dev->support_cmd_dscr) @@ -5573,8 +5593,8 @@ int qce_f9_req(void *handle, struct qce_f9_req *req, void *cookie, &pce_sps_data->out_transfer); select_mode(pce_dev, preq_info); - rc = _qce_sps_transfer(pce_dev, req_info); + cmpxchg(&pce_dev->owner, QCE_OWNER_CLIENT, QCE_OWNER_NONE); if (rc) goto bad; return 0; @@ -5939,9 +5959,7 @@ void *qce_open(struct platform_device *pdev, int *rc) qce_setup_ce_sps_data(pce_dev); qce_disable_clk(pce_dev); setup_dummy_req(pce_dev); - spin_lock_init(&pce_dev->lock); - spin_lock_init(&pce_dev->sps_lock); - pce_dev->no_of_queued_req = 0; + atomic_set(&pce_dev->no_of_queued_req, 0); pce_dev->mode = IN_INTERRUPT_MODE; init_timer(&(pce_dev->timer)); pce_dev->timer.function = qce_multireq_timeout; @@ -5950,6 +5968,7 @@ void *qce_open(struct platform_device *pdev, int *rc) pce_dev->intr_cadence = 0; pce_dev->dev_no = pcedev_no; pcedev_no++; + pce_dev->owner = QCE_OWNER_NONE; mutex_unlock(&qce_iomap_mutex); return pce_dev; err: diff --git a/drivers/crypto/msm/qce50.h b/drivers/crypto/msm/qce50.h index cef466382ee4..6dba3664ff08 100644 --- a/drivers/crypto/msm/qce50.h +++ b/drivers/crypto/msm/qce50.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -232,6 +232,7 @@ struct ce_request_info { dma_addr_t phy_ota_src; dma_addr_t phy_ota_dst; unsigned int ota_size; + unsigned int req_len; }; struct qce_driver_stats { diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c index ab026d24e978..faeff0b55202 100644 --- a/drivers/crypto/msm/qcrypto.c +++ b/drivers/crypto/msm/qcrypto.c @@ -32,6 +32,7 @@ #include <linux/cache.h> #include <linux/platform_data/qcom_crypto_device.h> #include <linux/msm-bus.h> +#include <linux/hardirq.h> #include <linux/qcrypto.h> #include <crypto/ctr.h> @@ -51,7 +52,7 @@ #include "qce.h" #define DEBUG_MAX_FNAME 16 -#define DEBUG_MAX_RW_BUF 2048 +#define DEBUG_MAX_RW_BUF 4096 #define QCRYPTO_BIG_NUMBER 9999999 /* a big number */ /* @@ -131,6 +132,7 @@ struct qcrypto_req_control { struct crypto_engine *pce; struct crypto_async_request *req; struct qcrypto_resp_ctx *arsp; + int res; /* execution result */ }; struct crypto_engine { @@ -167,8 +169,14 @@ struct crypto_engine { unsigned int max_req; struct qcrypto_req_control *preq_pool; atomic_t req_count; + bool issue_req; /* an request is being issued to qce */ + bool first_engine; /* this engine is the first engine or not */ + unsigned int irq_cpu; /* the cpu running the irq of this engine */ + unsigned int max_req_used; /* debug stats */ }; +#define MAX_SMP_CPU 8 + struct crypto_priv { /* CE features supported by target device*/ struct msm_ce_hw_support platform_support; @@ -208,21 +216,37 @@ struct crypto_priv { enum resp_workq_sts sched_resp_workq_status; enum req_processing_sts ce_req_proc_sts; int cpu_getting_irqs_frm_first_ce; + struct crypto_engine *first_engine; + struct crypto_engine *scheduled_eng; /* last engine scheduled */ + + /* debug stats */ + unsigned no_avail; + unsigned resp_stop; + unsigned resp_start; + unsigned max_qlen; + unsigned int queue_work_eng3; + unsigned int queue_work_not_eng3; + unsigned int queue_work_not_eng3_nz; + unsigned int max_resp_qlen; + unsigned int max_reorder_cnt; + unsigned int cpu_req[MAX_SMP_CPU+1]; }; static struct crypto_priv qcrypto_dev; static struct crypto_engine *_qcrypto_static_assign_engine( struct crypto_priv *cp); static struct crypto_engine *_avail_eng(struct crypto_priv *cp); - static struct qcrypto_req_control *qcrypto_alloc_req_control( struct crypto_engine *pce) { int i; struct qcrypto_req_control *pqcrypto_req_control = pce->preq_pool; + unsigned int req_count; for (i = 0; i < pce->max_req; i++) { if (xchg(&pqcrypto_req_control->in_use, true) == false) { - atomic_inc(&pce->req_count); + req_count = atomic_inc_return(&pce->req_count); + if (req_count > pce->max_req_used) + pce->max_req_used = req_count; return pqcrypto_req_control; } pqcrypto_req_control++; @@ -233,11 +257,13 @@ static struct qcrypto_req_control *qcrypto_alloc_req_control( static void qcrypto_free_req_control(struct crypto_engine *pce, struct qcrypto_req_control *preq) { + /* do this before free req */ + preq->req = NULL; + preq->arsp = NULL; + /* free req */ if (xchg(&preq->in_use, false) == false) { pr_warn("request info %p free already\n", preq); } else { - preq->req = NULL; - preq->arsp = NULL; atomic_dec(&pce->req_count); } } @@ -441,7 +467,9 @@ struct qcrypto_cipher_req_ctx { #define SHA_MAX_DIGEST_SIZE SHA256_DIGEST_SIZE #define MSM_QCRYPTO_REQ_QUEUE_LENGTH 768 -#define COMPLETION_CB_BACKLOG_LENGTH 768 +#define COMPLETION_CB_BACKLOG_LENGTH_STOP 400 +#define COMPLETION_CB_BACKLOG_LENGTH_START \ + (COMPLETION_CB_BACKLOG_LENGTH_STOP / 2) static uint8_t _std_init_vector_sha1_uint8[] = { 0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, 0x89, @@ -1066,6 +1094,7 @@ static int _disp_stats(int id) unsigned long flags; struct crypto_priv *cp = &qcrypto_dev; struct crypto_engine *pe; + int i; pstat = &_qcrypto_stat; len = scnprintf(_debug_read_buf, DEBUG_MAX_RW_BUF - 1, @@ -1188,14 +1217,27 @@ static int _disp_stats(int id) " AHASH operation fail : %llu\n", pstat->ahash_op_fail); len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1, + " resp start, resp stop, max rsp queue reorder-cnt : %u %u %u %u\n", + cp->resp_start, cp->resp_stop, + cp->max_resp_qlen, cp->max_reorder_cnt); + len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1, + " max queue legnth, no avail : %u %u\n", + cp->max_qlen, cp->no_avail); + len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1, + " work queue : %u %u %u\n", + cp->queue_work_eng3, + cp->queue_work_not_eng3, + cp->queue_work_not_eng3_nz); + len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1, "\n"); spin_lock_irqsave(&cp->lock, flags); list_for_each_entry(pe, &cp->engine_list, elist) { len += scnprintf( _debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1, - " Engine %4d Req : %llu\n", + " Engine %4d Req max %d : %llu\n", pe->unit, + pe->max_req_used, pe->total_req ); len += scnprintf( @@ -1208,6 +1250,14 @@ static int _disp_stats(int id) qce_get_driver_stats(pe->qce); } spin_unlock_irqrestore(&cp->lock, flags); + + for (i = 0; i < MAX_SMP_CPU+1; i++) + if (cp->cpu_req[i]) + len += scnprintf( + _debug_read_buf + len, + DEBUG_MAX_RW_BUF - len - 1, + "CPU %d Issue Req : %d\n", + i, cp->cpu_req[i]); return len; } @@ -1217,13 +1267,25 @@ static void _qcrypto_remove_engine(struct crypto_engine *pengine) struct qcrypto_alg *q_alg; struct qcrypto_alg *n; unsigned long flags; + struct crypto_engine *pe; cp = pengine->pcp; spin_lock_irqsave(&cp->lock, flags); list_del(&pengine->elist); + if (pengine->first_engine) { + cp->first_engine = NULL; + pe = list_first_entry(&cp->engine_list, struct crypto_engine, + elist); + if (pe) { + pe->first_engine = true; + cp->first_engine = pe; + } + } if (cp->next_engine == pengine) cp->next_engine = NULL; + if (cp->scheduled_eng == pengine) + cp->scheduled_eng = NULL; spin_unlock_irqrestore(&cp->lock, flags); cp->total_units--; @@ -1432,41 +1494,15 @@ static int _qcrypto_setkey_3des(struct crypto_ablkcipher *cipher, const u8 *key, return 0; }; -static struct crypto_engine *eng_sel_avoid_first(struct crypto_priv *cp) -{ - /* - * This function need not be spinlock protected when called from - * the seq_response workq as it will not have any contentions when all - * request processing is stopped. - */ - struct crypto_engine *p; - struct crypto_engine *q = NULL; - int max_user = QCRYPTO_BIG_NUMBER; - int use_cnt; - - if (unlikely(list_empty(&cp->engine_list))) { - pr_err("%s: no valid ce to schedule\n", __func__); - return NULL; - } - - p = list_first_entry(&cp->engine_list, struct crypto_engine, - elist); - list_for_each_entry_continue(p, &cp->engine_list, elist) { - use_cnt = atomic_read(&p->req_count); - if ((use_cnt < p->max_req) && (use_cnt < max_user)) { - q = p; - max_user = use_cnt; - } - } - return q; -} - static void seq_response(struct work_struct *work) { struct crypto_priv *cp = container_of(work, struct crypto_priv, resp_work); struct llist_node *list; struct llist_node *rev = NULL; + struct crypto_engine *pengine; + unsigned long flags; + int total_unit; again: list = llist_del_all(&cp->ordered_resp_list); @@ -1485,7 +1521,6 @@ again: while (rev) { struct qcrypto_resp_ctx *arsp; struct crypto_async_request *areq; - struct crypto_engine *pengine; arsp = container_of(rev, struct qcrypto_resp_ctx, llist); rev = llist_next(rev); @@ -1495,12 +1530,20 @@ again: areq->complete(areq, arsp->res); local_bh_enable(); atomic_dec(&cp->resp_cnt); - if (ACCESS_ONCE(cp->ce_req_proc_sts) == STOPPED && - atomic_read(&cp->resp_cnt) <= - (COMPLETION_CB_BACKLOG_LENGTH / 2)) { - pengine = eng_sel_avoid_first(cp); + } + + if (atomic_read(&cp->resp_cnt) < COMPLETION_CB_BACKLOG_LENGTH_START && + (cmpxchg(&cp->ce_req_proc_sts, STOPPED, IN_PROGRESS) + == STOPPED)) { + cp->resp_start++; + for (total_unit = cp->total_units; total_unit-- > 0;) { + spin_lock_irqsave(&cp->lock, flags); + pengine = _avail_eng(cp); + spin_unlock_irqrestore(&cp->lock, flags); if (pengine) _start_qcrypto_process(cp, pengine); + else + break; } } end: @@ -1512,12 +1555,19 @@ end: goto end; } -static void _qcrypto_tfm_complete(struct crypto_priv *cp, u32 type, - void *tfm_ctx) +#define SCHEUDLE_RSP_QLEN_THRESHOLD 64 + +static void _qcrypto_tfm_complete(struct crypto_engine *pengine, u32 type, + void *tfm_ctx, + struct qcrypto_resp_ctx *cur_arsp, + int res) { + struct crypto_priv *cp = pengine->pcp; unsigned long flags; struct qcrypto_resp_ctx *arsp; struct list_head *plist; + unsigned int resp_qlen; + unsigned int cnt = 0; switch (type) { case CRYPTO_ALG_TYPE_AHASH: @@ -1531,6 +1581,8 @@ static void _qcrypto_tfm_complete(struct crypto_priv *cp, u32 type, } spin_lock_irqsave(&cp->lock, flags); + + cur_arsp->res = res; while (!list_empty(plist)) { arsp = list_first_entry(plist, struct qcrypto_resp_ctx, list); @@ -1539,16 +1591,51 @@ static void _qcrypto_tfm_complete(struct crypto_priv *cp, u32 type, else { list_del(&arsp->list); llist_add(&arsp->llist, &cp->ordered_resp_list); + atomic_inc(&cp->resp_cnt); + cnt++; } } + resp_qlen = atomic_read(&cp->resp_cnt); + if (resp_qlen > cp->max_resp_qlen) + cp->max_resp_qlen = resp_qlen; + if (cnt > cp->max_reorder_cnt) + cp->max_reorder_cnt = cnt; + if ((resp_qlen >= COMPLETION_CB_BACKLOG_LENGTH_STOP) && + cmpxchg(&cp->ce_req_proc_sts, IN_PROGRESS, + STOPPED) == IN_PROGRESS) { + cp->resp_stop++; + } + spin_unlock_irqrestore(&cp->lock, flags); retry: if (!llist_empty(&cp->ordered_resp_list)) { + unsigned int cpu; + + if (pengine->first_engine) { + cpu = WORK_CPU_UNBOUND; + cp->queue_work_eng3++; + } else { + cp->queue_work_not_eng3++; + cpu = cp->cpu_getting_irqs_frm_first_ce; + /* + * If source not the first engine, and there + * are outstanding requests going on first engine, + * skip scheduling of work queue to anticipate + * more may be coming. If the response queue + * length exceeds threshold, to avoid further + * delay, schedule work queue immediately. + */ + if (cp->first_engine && atomic_read( + &cp->first_engine->req_count)) { + if (resp_qlen < SCHEUDLE_RSP_QLEN_THRESHOLD) + return; + cp->queue_work_not_eng3_nz++; + } + } if (cmpxchg(&cp->sched_resp_workq_status, NOT_SCHEDULED, IS_SCHEDULED) == NOT_SCHEDULED) - queue_work_on(cp->cpu_getting_irqs_frm_first_ce, - cp->resp_wq, &cp->resp_work); + queue_work_on(cpu, cp->resp_wq, &cp->resp_work); else if (cmpxchg(&cp->sched_resp_workq_status, IS_SCHEDULED, SCHEDULE_AGAIN) == NOT_SCHEDULED) goto retry; @@ -1559,36 +1646,34 @@ static void req_done(struct qcrypto_req_control *pqcrypto_req_control) { struct crypto_engine *pengine; struct crypto_async_request *areq; - struct crypto_engine *pe; struct crypto_priv *cp; - unsigned long flags; struct qcrypto_resp_ctx *arsp; u32 type = 0; void *tfm_ctx = NULL; + unsigned int cpu; + int res; pengine = pqcrypto_req_control->pce; cp = pengine->pcp; - spin_lock_irqsave(&cp->lock, flags); areq = pqcrypto_req_control->req; arsp = pqcrypto_req_control->arsp; + res = pqcrypto_req_control->res; qcrypto_free_req_control(pengine, pqcrypto_req_control); if (areq) { type = crypto_tfm_alg_type(areq->tfm); tfm_ctx = crypto_tfm_ctx(areq->tfm); } - pe = list_first_entry(&cp->engine_list, struct crypto_engine, elist); - if (pe == pengine) - if (cp->cpu_getting_irqs_frm_first_ce != smp_processor_id()) - cp->cpu_getting_irqs_frm_first_ce = smp_processor_id(); - spin_unlock_irqrestore(&cp->lock, flags); - if (atomic_read(&cp->resp_cnt) <= COMPLETION_CB_BACKLOG_LENGTH) { - cmpxchg(&cp->ce_req_proc_sts, STOPPED, IN_PROGRESS); - _start_qcrypto_process(cp, pengine); - } else - cmpxchg(&cp->ce_req_proc_sts, IN_PROGRESS, STOPPED); + cpu = smp_processor_id(); + pengine->irq_cpu = cpu; + if (pengine->first_engine) { + if (cpu != cp->cpu_getting_irqs_frm_first_ce) + cp->cpu_getting_irqs_frm_first_ce = cpu; + } if (areq) - _qcrypto_tfm_complete(cp, type, tfm_ctx); + _qcrypto_tfm_complete(pengine, type, tfm_ctx, arsp, res); + if (ACCESS_ONCE(cp->ce_req_proc_sts) == IN_PROGRESS) + _start_qcrypto_process(cp, pengine); } static void _qce_ahash_complete(void *cookie, unsigned char *digest, @@ -1639,10 +1724,10 @@ static void _qce_ahash_complete(void *cookie, unsigned char *digest, rctx->first_blk = 0; if (ret) { - pqcrypto_req_control->arsp->res = -ENXIO; + pqcrypto_req_control->res = -ENXIO; pstat->ahash_op_fail++; } else { - pqcrypto_req_control->arsp->res = 0; + pqcrypto_req_control->res = 0; pstat->ahash_op_success++; } if (cp->ce_support.aligned_only) { @@ -1684,10 +1769,10 @@ static void _qce_ablk_cipher_complete(void *cookie, unsigned char *icb, memcpy(ctx->iv, iv, crypto_ablkcipher_ivsize(ablk)); if (ret) { - pqcrypto_req_control->arsp->res = -ENXIO; + pqcrypto_req_control->res = -ENXIO; pstat->ablk_cipher_op_fail++; } else { - pqcrypto_req_control->arsp->res = 0; + pqcrypto_req_control->res = 0; pstat->ablk_cipher_op_success++; } @@ -1773,7 +1858,7 @@ static void _qce_aead_complete(void *cookie, unsigned char *icv, else pstat->aead_op_success++; - pqcrypto_req_control->arsp->res = ret; + pqcrypto_req_control->res = ret; req_done(pqcrypto_req_control); } @@ -2100,12 +2185,24 @@ static int _start_qcrypto_process(struct crypto_priv *cp, struct aead_request *aead_req; struct qcrypto_resp_ctx *arsp; struct qcrypto_req_control *pqcrypto_req_control; + unsigned int cpu = MAX_SMP_CPU; + + if (ACCESS_ONCE(cp->ce_req_proc_sts) == STOPPED) + return 0; + + if (in_interrupt()) { + cpu = smp_processor_id(); + if (cpu >= MAX_SMP_CPU) + cpu = MAX_SMP_CPU - 1; + } else + cpu = MAX_SMP_CPU; pstat = &_qcrypto_stat; again: spin_lock_irqsave(&cp->lock, flags); - if (atomic_read(&pengine->req_count) >= (pengine->max_req)) { + if (pengine->issue_req || + atomic_read(&pengine->req_count) >= (pengine->max_req)) { spin_unlock_irqrestore(&cp->lock, flags); return 0; } @@ -2176,7 +2273,6 @@ again: break; } - atomic_inc(&cp->resp_cnt); arsp->res = -EINPROGRESS; arsp->async_req = async_req; pqcrypto_req_control->pce = pengine; @@ -2185,6 +2281,10 @@ again: pengine->active_seq++; pengine->check_flag = true; + pengine->issue_req = true; + cp->cpu_req[cpu]++; + smp_mb(); /* make it visible */ + spin_unlock_irqrestore(&cp->lock, flags); if (backlog_eng) backlog_eng->complete(backlog_eng, -EINPROGRESS); @@ -2204,9 +2304,12 @@ again: default: ret = -EINVAL; }; + + pengine->issue_req = false; + smp_mb(); /* make it visible */ + pengine->total_req++; if (ret) { - arsp->res = ret; pengine->err_req++; qcrypto_free_req_control(pengine, pqcrypto_req_control); @@ -2218,32 +2321,48 @@ again: else pstat->aead_op_fail++; - _qcrypto_tfm_complete(cp, type, tfm_ctx); + _qcrypto_tfm_complete(pengine, type, tfm_ctx, arsp, ret); goto again; }; return ret; } +static inline struct crypto_engine *_next_eng(struct crypto_priv *cp, + struct crypto_engine *p) +{ + + if (p == NULL || list_is_last(&p->elist, &cp->engine_list)) + p = list_first_entry(&cp->engine_list, struct crypto_engine, + elist); + else + p = list_entry(p->elist.next, struct crypto_engine, elist); + return p; +} static struct crypto_engine *_avail_eng(struct crypto_priv *cp) { /* call this function with spinlock set */ - struct crypto_engine *p; struct crypto_engine *q = NULL; - int max_user = QCRYPTO_BIG_NUMBER; - int use_cnt; + struct crypto_engine *p = cp->scheduled_eng; + struct crypto_engine *q1; + int eng_cnt = cp->total_units; if (unlikely(list_empty(&cp->engine_list))) { pr_err("%s: no valid ce to schedule\n", __func__); return NULL; } - list_for_each_entry(p, &cp->engine_list, elist) { - use_cnt = atomic_read(&p->req_count); - if ((use_cnt < p->max_req) && (use_cnt < max_user)) { + p = _next_eng(cp, p); + q1 = p; + while (eng_cnt-- > 0) { + if (!p->issue_req && atomic_read(&p->req_count) < p->max_req) { q = p; - max_user = use_cnt; + break; } + p = _next_eng(cp, p); + if (q1 == p) + break; } + cp->scheduled_eng = q; return q; } @@ -2261,6 +2380,8 @@ static int _qcrypto_queue_req(struct crypto_priv *cp, } else { ret = crypto_enqueue_request(&cp->req_queue, req); pengine = _avail_eng(cp); + if (cp->req_queue.qlen > cp->max_qlen) + cp->max_qlen = cp->req_queue.qlen; } if (pengine) { switch (pengine->bw_state) { @@ -2286,16 +2407,12 @@ static int _qcrypto_queue_req(struct crypto_priv *cp, pengine = NULL; break; } + } else { + cp->no_avail++; } spin_unlock_irqrestore(&cp->lock, flags); - if (pengine) { - if (atomic_read(&cp->resp_cnt) <= - COMPLETION_CB_BACKLOG_LENGTH) { - cmpxchg(&cp->ce_req_proc_sts, STOPPED, IN_PROGRESS); - _start_qcrypto_process(cp, pengine); - } else - cmpxchg(&cp->ce_req_proc_sts, IN_PROGRESS, STOPPED); - } + if (pengine && (ACCESS_ONCE(cp->ce_req_proc_sts) == IN_PROGRESS)) + _start_qcrypto_process(cp, pengine); return ret; } @@ -4762,6 +4879,8 @@ static int _qcrypto_probe(struct platform_device *pdev) pengine->active_seq = 0; pengine->last_active_seq = 0; pengine->check_flag = false; + pengine->max_req_used = 0; + pengine->issue_req = false; crypto_init_queue(&pengine->req_queue, MSM_QCRYPTO_REQ_QUEUE_LENGTH); @@ -4770,6 +4889,9 @@ static int _qcrypto_probe(struct platform_device *pdev) pengine->unit = cp->total_units; spin_lock_irqsave(&cp->lock, flags); + pengine->first_engine = list_empty(&cp->engine_list); + if (pengine->first_engine) + cp->first_engine = pengine; list_add_tail(&pengine->elist, &cp->engine_list); cp->next_engine = pengine; spin_unlock_irqrestore(&cp->lock, flags); @@ -5292,6 +5414,7 @@ static ssize_t _debug_stats_write(struct file *file, const char __user *buf, unsigned long flags; struct crypto_priv *cp = &qcrypto_dev; struct crypto_engine *pe; + int i; memset((char *)&_qcrypto_stat, 0, sizeof(struct crypto_stat)); spin_lock_irqsave(&cp->lock, flags); @@ -5299,7 +5422,19 @@ static ssize_t _debug_stats_write(struct file *file, const char __user *buf, pe->total_req = 0; pe->err_req = 0; qce_clear_driver_stats(pe->qce); + pe->max_req_used = 0; } + cp->max_qlen = 0; + cp->resp_start = 0; + cp->resp_stop = 0; + cp->no_avail = 0; + cp->max_resp_qlen = 0; + cp->queue_work_eng3 = 0; + cp->queue_work_not_eng3 = 0; + cp->queue_work_not_eng3_nz = 0; + cp->max_reorder_cnt = 0; + for (i = 0; i < MAX_SMP_CPU + 1; i++) + cp->cpu_req[i] = 0; spin_unlock_irqrestore(&cp->lock, flags); return count; } @@ -5362,6 +5497,8 @@ static int __init _qcrypto_init(void) pcp->total_units = 0; pcp->platform_support.bus_scale_table = NULL; pcp->next_engine = NULL; + pcp->scheduled_eng = NULL; + pcp->ce_req_proc_sts = IN_PROGRESS; crypto_init_queue(&pcp->req_queue, MSM_QCRYPTO_REQ_QUEUE_LENGTH); return platform_driver_register(&_qualcomm_crypto); } diff --git a/drivers/crypto/ux500/cryp/cryp_core.c b/drivers/crypto/ux500/cryp/cryp_core.c index 4c243c1ffc7f..790f7cadc1ed 100644 --- a/drivers/crypto/ux500/cryp/cryp_core.c +++ b/drivers/crypto/ux500/cryp/cryp_core.c @@ -1440,9 +1440,9 @@ static int ux500_cryp_probe(struct platform_device *pdev) device_data->phybase = res->start; device_data->base = devm_ioremap_resource(dev, res); - if (!device_data->base) { + if (IS_ERR(device_data->base)) { dev_err(dev, "[%s]: ioremap failed!", __func__); - ret = -ENOMEM; + ret = PTR_ERR(device_data->base); goto out; } diff --git a/drivers/crypto/ux500/hash/hash_core.c b/drivers/crypto/ux500/hash/hash_core.c index f47d112041b2..66b1c3313e2e 100644 --- a/drivers/crypto/ux500/hash/hash_core.c +++ b/drivers/crypto/ux500/hash/hash_core.c @@ -1675,9 +1675,9 @@ static int ux500_hash_probe(struct platform_device *pdev) device_data->phybase = res->start; device_data->base = devm_ioremap_resource(dev, res); - if (!device_data->base) { + if (IS_ERR(device_data->base)) { dev_err(dev, "%s: ioremap() failed!\n", __func__); - ret = -ENOMEM; + ret = PTR_ERR(device_data->base); goto out; } spin_lock_init(&device_data->ctx_lock); diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 9eee13ef83a5..d87a47547ba5 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -1452,7 +1452,7 @@ static u64 f1x_get_norm_dct_addr(struct amd64_pvt *pvt, u8 range, u64 chan_off; u64 dram_base = get_dram_base(pvt, range); u64 hole_off = f10_dhar_offset(pvt); - u64 dct_sel_base_off = (pvt->dct_sel_hi & 0xFFFFFC00) << 16; + u64 dct_sel_base_off = (u64)(pvt->dct_sel_hi & 0xFFFFFC00) << 16; if (hi_rng) { /* diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index 429309c62699..cbee3179ec08 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c @@ -1117,8 +1117,8 @@ static void get_memory_layout(const struct mem_ctl_info *mci) edac_dbg(0, "TAD#%d: up to %u.%03u GB (0x%016Lx), socket interleave %d, memory interleave %d, TGT: %d, %d, %d, %d, reg=0x%08x\n", n_tads, gb, (mb*1000)/1024, ((u64)tmp_mb) << 20L, - (u32)TAD_SOCK(reg), - (u32)TAD_CH(reg), + (u32)(1 << TAD_SOCK(reg)), + (u32)TAD_CH(reg) + 1, (u32)TAD_TGT0(reg), (u32)TAD_TGT1(reg), (u32)TAD_TGT2(reg), @@ -1396,7 +1396,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci, } ch_way = TAD_CH(reg) + 1; - sck_way = TAD_SOCK(reg) + 1; + sck_way = 1 << TAD_SOCK(reg); if (ch_way == 3) idx = addr >> 6; @@ -1453,7 +1453,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci, n_tads, addr, limit, - (u32)TAD_SOCK(reg), + sck_way, ch_way, offset, idx, @@ -1468,18 +1468,12 @@ static int get_memory_error_data(struct mem_ctl_info *mci, offset, addr); return -EINVAL; } - addr -= offset; - /* Store the low bits [0:6] of the addr */ - ch_addr = addr & 0x7f; - /* Remove socket wayness and remove 6 bits */ - addr >>= 6; - addr = div_u64(addr, sck_xch); -#if 0 - /* Divide by channel way */ - addr = addr / ch_way; -#endif - /* Recover the last 6 bits */ - ch_addr |= addr << 6; + + ch_addr = addr - offset; + ch_addr >>= (6 + shiftup); + ch_addr /= ch_way * sck_way; + ch_addr <<= (6 + shiftup); + ch_addr |= addr & ((1 << (6 + shiftup)) - 1); /* * Step 3) Decode rank diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c index 5a8fbadbd27b..8ac49812a716 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c @@ -63,6 +63,10 @@ bool amdgpu_has_atpx(void) { return amdgpu_atpx_priv.atpx_detected; } +bool amdgpu_has_atpx_dgpu_power_cntl(void) { + return amdgpu_atpx_priv.atpx.functions.power_cntl; +} + /** * amdgpu_atpx_call - call an ATPX method * @@ -142,10 +146,6 @@ static void amdgpu_atpx_parse_functions(struct amdgpu_atpx_functions *f, u32 mas */ static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx) { - /* make sure required functions are enabled */ - /* dGPU power control is required */ - atpx->functions.power_cntl = true; - if (atpx->functions.px_params) { union acpi_object *info; struct atpx_px_params output; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index c961fe093e12..9d88023df836 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -61,6 +61,12 @@ static const char *amdgpu_asic_name[] = { "LAST", }; +#if defined(CONFIG_VGA_SWITCHEROO) +bool amdgpu_has_atpx_dgpu_power_cntl(void); +#else +static inline bool amdgpu_has_atpx_dgpu_power_cntl(void) { return false; } +#endif + bool amdgpu_device_is_px(struct drm_device *dev) { struct amdgpu_device *adev = dev->dev_private; @@ -1469,7 +1475,7 @@ int amdgpu_device_init(struct amdgpu_device *adev, if (amdgpu_runtime_pm == 1) runtime = true; - if (amdgpu_device_is_px(ddev)) + if (amdgpu_device_is_px(ddev) && amdgpu_has_atpx_dgpu_power_cntl()) runtime = true; vga_switcheroo_register_client(adev->pdev, &amdgpu_switcheroo_ops, runtime); if (runtime) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c index 272110cc18c2..ea87033bfaf6 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c @@ -898,14 +898,6 @@ static int gmc_v7_0_early_init(void *handle) gmc_v7_0_set_gart_funcs(adev); gmc_v7_0_set_irq_funcs(adev); - if (adev->flags & AMD_IS_APU) { - adev->mc.vram_type = AMDGPU_VRAM_TYPE_UNKNOWN; - } else { - u32 tmp = RREG32(mmMC_SEQ_MISC0); - tmp &= MC_SEQ_MISC0__MT__MASK; - adev->mc.vram_type = gmc_v7_0_convert_vram_type(tmp); - } - return 0; } @@ -926,6 +918,14 @@ static int gmc_v7_0_sw_init(void *handle) if (r) return r; + if (adev->flags & AMD_IS_APU) { + adev->mc.vram_type = AMDGPU_VRAM_TYPE_UNKNOWN; + } else { + u32 tmp = RREG32(mmMC_SEQ_MISC0); + tmp &= MC_SEQ_MISC0__MT__MASK; + adev->mc.vram_type = gmc_v7_0_convert_vram_type(tmp); + } + r = amdgpu_irq_add_id(adev, 146, &adev->mc.vm_fault); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c index ba4ad00ba8b4..08423089fb84 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c @@ -852,14 +852,6 @@ static int gmc_v8_0_early_init(void *handle) gmc_v8_0_set_gart_funcs(adev); gmc_v8_0_set_irq_funcs(adev); - if (adev->flags & AMD_IS_APU) { - adev->mc.vram_type = AMDGPU_VRAM_TYPE_UNKNOWN; - } else { - u32 tmp = RREG32(mmMC_SEQ_MISC0); - tmp &= MC_SEQ_MISC0__MT__MASK; - adev->mc.vram_type = gmc_v8_0_convert_vram_type(tmp); - } - return 0; } @@ -870,6 +862,8 @@ static int gmc_v8_0_late_init(void *handle) return amdgpu_irq_get(adev, &adev->mc.vm_fault, 0); } +#define mmMC_SEQ_MISC0_FIJI 0xA71 + static int gmc_v8_0_sw_init(void *handle) { int r; @@ -880,6 +874,19 @@ static int gmc_v8_0_sw_init(void *handle) if (r) return r; + if (adev->flags & AMD_IS_APU) { + adev->mc.vram_type = AMDGPU_VRAM_TYPE_UNKNOWN; + } else { + u32 tmp; + + if (adev->asic_type == CHIP_FIJI) + tmp = RREG32(mmMC_SEQ_MISC0_FIJI); + else + tmp = RREG32(mmMC_SEQ_MISC0); + tmp &= MC_SEQ_MISC0__MT__MASK; + adev->mc.vram_type = gmc_v8_0_convert_vram_type(tmp); + } + r = amdgpu_irq_add_id(adev, 146, &adev->mc.vm_fault); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c index 2cf50180cc51..b1c7a9b3631b 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c @@ -32,8 +32,8 @@ #include "oss/oss_2_4_d.h" #include "oss/oss_2_4_sh_mask.h" -#include "gmc/gmc_8_1_d.h" -#include "gmc/gmc_8_1_sh_mask.h" +#include "gmc/gmc_7_1_d.h" +#include "gmc/gmc_7_1_sh_mask.h" #include "gca/gfx_8_0_d.h" #include "gca/gfx_8_0_enum.h" diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 9535c5b60387..7e5a97204051 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -178,7 +178,7 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, { struct drm_dp_aux_msg msg; unsigned int retry; - int err; + int err = 0; memset(&msg, 0, sizeof(msg)); msg.address = offset; @@ -186,6 +186,8 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, msg.buffer = buffer; msg.size = size; + mutex_lock(&aux->hw_mutex); + /* * The specification doesn't give any recommendation on how often to * retry native transactions. We used to retry 7 times like for @@ -194,25 +196,24 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, */ for (retry = 0; retry < 32; retry++) { - mutex_lock(&aux->hw_mutex); err = aux->transfer(aux, &msg); - mutex_unlock(&aux->hw_mutex); if (err < 0) { if (err == -EBUSY) continue; - return err; + goto unlock; } switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) { case DP_AUX_NATIVE_REPLY_ACK: if (err < size) - return -EPROTO; - return err; + err = -EPROTO; + goto unlock; case DP_AUX_NATIVE_REPLY_NACK: - return -EIO; + err = -EIO; + goto unlock; case DP_AUX_NATIVE_REPLY_DEFER: usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100); @@ -221,7 +222,11 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, } DRM_DEBUG_KMS("too many retries, giving up\n"); - return -EIO; + err = -EIO; + +unlock: + mutex_unlock(&aux->hw_mutex); + return err; } /** @@ -543,9 +548,7 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) int max_retries = max(7, drm_dp_i2c_retry_count(msg, dp_aux_i2c_speed_khz)); for (retry = 0, defer_i2c = 0; retry < (max_retries + defer_i2c); retry++) { - mutex_lock(&aux->hw_mutex); ret = aux->transfer(aux, msg); - mutex_unlock(&aux->hw_mutex); if (ret < 0) { if (ret == -EBUSY) continue; @@ -684,6 +687,8 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, memset(&msg, 0, sizeof(msg)); + mutex_lock(&aux->hw_mutex); + for (i = 0; i < num; i++) { msg.address = msgs[i].addr; drm_dp_i2c_msg_set_request(&msg, &msgs[i]); @@ -738,6 +743,8 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, msg.size = 0; (void)drm_dp_i2c_do_msg(aux, &msg); + mutex_unlock(&aux->hw_mutex); + return err; } diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index bb292143997e..adf74f4366bb 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -892,8 +892,6 @@ atombios_dig_encoder_setup2(struct drm_encoder *encoder, int action, int panel_m else args.v1.ucLaneNum = 4; - if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode) && (dp_clock == 270000)) - args.v1.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ; switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER1; @@ -910,6 +908,10 @@ atombios_dig_encoder_setup2(struct drm_encoder *encoder, int action, int panel_m args.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKB; else args.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKA; + + if (ENCODER_MODE_IS_DP(args.v1.ucEncoderMode) && (dp_clock == 270000)) + args.v1.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ; + break; case 2: case 3: diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c index c4b4f298a283..9bc408c9f9f6 100644 --- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c +++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c @@ -62,6 +62,10 @@ bool radeon_has_atpx(void) { return radeon_atpx_priv.atpx_detected; } +bool radeon_has_atpx_dgpu_power_cntl(void) { + return radeon_atpx_priv.atpx.functions.power_cntl; +} + /** * radeon_atpx_call - call an ATPX method * @@ -141,10 +145,6 @@ static void radeon_atpx_parse_functions(struct radeon_atpx_functions *f, u32 mas */ static int radeon_atpx_validate(struct radeon_atpx *atpx) { - /* make sure required functions are enabled */ - /* dGPU power control is required */ - atpx->functions.power_cntl = true; - if (atpx->functions.px_params) { union acpi_object *info; struct atpx_px_params output; diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index c566993a2ec3..f78f111e68de 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -103,6 +103,12 @@ static const char radeon_family_name[][16] = { "LAST", }; +#if defined(CONFIG_VGA_SWITCHEROO) +bool radeon_has_atpx_dgpu_power_cntl(void); +#else +static inline bool radeon_has_atpx_dgpu_power_cntl(void) { return false; } +#endif + #define RADEON_PX_QUIRK_DISABLE_PX (1 << 0) #define RADEON_PX_QUIRK_LONG_WAKEUP (1 << 1) @@ -1433,7 +1439,7 @@ int radeon_device_init(struct radeon_device *rdev, * ignore it */ vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode); - if (rdev->flags & RADEON_IS_PX) + if ((rdev->flags & RADEON_IS_PX) && radeon_has_atpx_dgpu_power_cntl()) runtime = true; vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops, runtime); if (runtime) diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index a82b891ae1fe..7285adb27099 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -2926,9 +2926,11 @@ static struct si_dpm_quirk si_dpm_quirk_list[] = { /* PITCAIRN - https://bugs.freedesktop.org/show_bug.cgi?id=76490 */ { PCI_VENDOR_ID_ATI, 0x6810, 0x1462, 0x3036, 0, 120000 }, { PCI_VENDOR_ID_ATI, 0x6811, 0x174b, 0xe271, 0, 120000 }, + { PCI_VENDOR_ID_ATI, 0x6811, 0x174b, 0x2015, 0, 120000 }, { PCI_VENDOR_ID_ATI, 0x6810, 0x174b, 0xe271, 85000, 90000 }, { PCI_VENDOR_ID_ATI, 0x6811, 0x1462, 0x2015, 0, 120000 }, { PCI_VENDOR_ID_ATI, 0x6811, 0x1043, 0x2015, 0, 120000 }, + { PCI_VENDOR_ID_ATI, 0x6811, 0x148c, 0x2015, 0, 120000 }, { 0, 0, 0, 0 }, }; @@ -3008,6 +3010,10 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, } ++p; } + /* limit mclk on all R7 370 parts for stability */ + if (rdev->pdev->device == 0x6811 && + rdev->pdev->revision == 0x81) + max_mclk = 120000; if (rps->vce_active) { rps->evclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].evclk; diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index 62c7b1dafaa4..73e41a8613da 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -539,7 +539,7 @@ static int udlfb_create(struct drm_fb_helper *helper, out_destroy_fbi: drm_fb_helper_release_fbi(helper); out_gfree: - drm_gem_object_unreference(&ufbdev->ufb.obj->base); + drm_gem_object_unreference_unlocked(&ufbdev->ufb.obj->base); out: return ret; } diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c index 2a0a784ab6ee..d7528e0d8442 100644 --- a/drivers/gpu/drm/udl/udl_gem.c +++ b/drivers/gpu/drm/udl/udl_gem.c @@ -52,7 +52,7 @@ udl_gem_create(struct drm_file *file, return ret; } - drm_gem_object_unreference(&obj->base); + drm_gem_object_unreference_unlocked(&obj->base); *handle_p = handle; return 0; } diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile index db5a9ca28408..90aee3cad5ad 100644 --- a/drivers/gpu/msm/Makefile +++ b/drivers/gpu/msm/Makefile @@ -33,6 +33,8 @@ msm_adreno-y += \ adreno_a3xx_snapshot.o \ adreno_a4xx_snapshot.o \ adreno_a5xx_snapshot.o \ + adreno_a4xx_preempt.o \ + adreno_a5xx_preempt.o \ adreno_sysfs.o \ adreno.o \ adreno_cp_parser.o \ diff --git a/drivers/gpu/msm/a5xx_reg.h b/drivers/gpu/msm/a5xx_reg.h index 913cedb885ad..207588844931 100644 --- a/drivers/gpu/msm/a5xx_reg.h +++ b/drivers/gpu/msm/a5xx_reg.h @@ -60,6 +60,8 @@ #define A5XX_CP_RB_BASE 0x800 #define A5XX_CP_RB_BASE_HI 0x801 #define A5XX_CP_RB_CNTL 0x802 +#define A5XX_CP_RB_RPTR_ADDR_LO 0x804 +#define A5XX_CP_RB_RPTR_ADDR_HI 0x805 #define A5XX_CP_RB_RPTR 0x806 #define A5XX_CP_RB_WPTR 0x807 #define A5XX_CP_PFP_STAT_ADDR 0x808 diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 26e341a876e8..a802671acba0 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -171,6 +171,30 @@ void adreno_writereg64(struct adreno_device *adreno_dev, } /** + * adreno_get_rptr() - Get the current ringbuffer read pointer + * @rb: Pointer the ringbuffer to query + * + * Get the latest rptr + */ +unsigned int adreno_get_rptr(struct adreno_ringbuffer *rb) +{ + struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); + unsigned int rptr = 0; + + if (adreno_is_a3xx(adreno_dev)) + adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_RPTR, + &rptr); + else { + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + + kgsl_sharedmem_readl(&device->scratch, &rptr, + SCRATCH_RPTR_OFFSET(rb->id)); + } + + return rptr; +} + +/** * adreno_of_read_property() - Adreno read property * @node: Device node * @@ -1290,6 +1314,28 @@ static void _update_threshold_count(struct adreno_device *adreno_dev, adreno_dev->lm_threshold_cross = adj; } +static void _set_secvid(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + + /* Program GPU contect protection init values */ + if (device->mmu.secured) { + if (adreno_is_a4xx(adreno_dev)) + adreno_writereg(adreno_dev, + ADRENO_REG_RBBM_SECVID_TRUST_CONFIG, 0x2); + adreno_writereg(adreno_dev, + ADRENO_REG_RBBM_SECVID_TSB_CONTROL, 0x0); + + adreno_writereg64(adreno_dev, + ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_BASE, + ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_BASE_HI, + KGSL_IOMMU_SECURE_BASE); + adreno_writereg(adreno_dev, + ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_SIZE, + KGSL_IOMMU_SECURE_SIZE); + } +} + /** * _adreno_start - Power up the GPU and prepare to accept commands * @adreno_dev: Pointer to an adreno_device structure @@ -1332,26 +1378,13 @@ static int _adreno_start(struct adreno_device *adreno_dev) if (regulator_left_on) _soft_reset(adreno_dev); + adreno_ringbuffer_set_global(adreno_dev, 0); + status = kgsl_mmu_start(device); if (status) goto error_pwr_off; - /* Program GPU contect protection init values */ - if (device->mmu.secured) { - if (adreno_is_a4xx(adreno_dev)) - adreno_writereg(adreno_dev, - ADRENO_REG_RBBM_SECVID_TRUST_CONFIG, 0x2); - adreno_writereg(adreno_dev, - ADRENO_REG_RBBM_SECVID_TSB_CONTROL, 0x0); - - adreno_writereg64(adreno_dev, - ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_BASE, - ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_BASE_HI, - KGSL_IOMMU_SECURE_BASE); - adreno_writereg(adreno_dev, - ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_SIZE, - KGSL_IOMMU_SECURE_SIZE); - } + _set_secvid(device); status = adreno_ocmem_malloc(adreno_dev); if (status) { @@ -1533,6 +1566,22 @@ static int adreno_vbif_clear_pending_transactions(struct kgsl_device *device) return ret; } +static void adreno_set_active_ctxs_null(struct adreno_device *adreno_dev) +{ + int i; + struct adreno_ringbuffer *rb; + + FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { + if (rb->drawctxt_active) + kgsl_context_put(&(rb->drawctxt_active->base)); + rb->drawctxt_active = NULL; + + kgsl_sharedmem_writel(KGSL_DEVICE(adreno_dev), + &rb->pagetable_desc, PT_INFO_OFFSET(current_rb_ptname), + 0); + } +} + static int adreno_stop(struct kgsl_device *device) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); @@ -1645,13 +1694,6 @@ int adreno_reset(struct kgsl_device *device, int fault) else kgsl_pwrctrl_change_state(device, KGSL_STATE_NAP); - /* Set the page table back to the default page table */ - kgsl_mmu_set_pt(&device->mmu, device->mmu.defaultpagetable); - kgsl_sharedmem_writel(device, - &adreno_dev->ringbuffers[0].pagetable_desc, - offsetof(struct adreno_ringbuffer_pagetable_info, - current_global_ptname), 0); - return ret; } @@ -1718,6 +1760,30 @@ static int adreno_getproperty(struct kgsl_device *device, status = 0; } break; + case KGSL_PROP_DEVICE_QDSS_STM: + { + struct kgsl_qdss_stm_prop qdssprop = {0}; + struct kgsl_memdesc *qdss_desc = + kgsl_mmu_get_qdss_global_entry(device); + + if (sizebytes != sizeof(qdssprop)) { + status = -EINVAL; + break; + } + + if (qdss_desc) { + qdssprop.gpuaddr = qdss_desc->gpuaddr; + qdssprop.size = qdss_desc->size; + } + + if (copy_to_user(value, &qdssprop, + sizeof(qdssprop))) { + status = -EFAULT; + break; + } + status = 0; + } + break; case KGSL_PROP_MMU_ENABLE: { /* Report MMU only if we can handle paged memory */ @@ -2051,6 +2117,14 @@ bool adreno_hw_isidle(struct adreno_device *adreno_dev) const struct adreno_gpu_core *gpucore = adreno_dev->gpucore; unsigned int reg_rbbm_status; + if (adreno_is_a540(adreno_dev)) + /** + * Due to CRC idle throttling GPU + * idle hysteresys can take up to + * 3usec for expire - account for it + */ + udelay(5); + adreno_readreg(adreno_dev, ADRENO_REG_RBBM_STATUS, ®_rbbm_status); @@ -2094,9 +2168,15 @@ static int adreno_soft_reset(struct kgsl_device *device) /* Reset the GPU */ _soft_reset(adreno_dev); + /* Set the page table back to the default page table */ + adreno_ringbuffer_set_global(adreno_dev, 0); + kgsl_mmu_set_pt(&device->mmu, device->mmu.defaultpagetable); + /* start of new CFF after reset */ kgsl_cffdump_open(device); + _set_secvid(device); + /* Enable 64 bit gpu addr if feature is set */ if (gpudev->enable_64bit && adreno_support_64bit(adreno_dev)) @@ -2149,8 +2229,6 @@ bool adreno_isidle(struct kgsl_device *device) if (!kgsl_state_is_awake(device)) return true; - adreno_get_rptr(ADRENO_CURRENT_RINGBUFFER(adreno_dev)); - /* * wptr is updated when we add commands to ringbuffer, add a barrier * to make sure updated wptr is compared to rptr @@ -2161,15 +2239,13 @@ bool adreno_isidle(struct kgsl_device *device) * ringbuffer is truly idle when all ringbuffers read and write * pointers are equal */ + FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { - if (rb->rptr != rb->wptr) - break; + if (!adreno_rb_empty(rb)) + return false; } - if (i == adreno_dev->num_ringbuffers) - return adreno_hw_isidle(adreno_dev); - - return false; + return adreno_hw_isidle(adreno_dev); } /** @@ -2267,25 +2343,11 @@ static int adreno_drain(struct kgsl_device *device) /* Caller must hold the device mutex. */ static int adreno_suspend_context(struct kgsl_device *device) { - int status = 0; - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - /* process any profiling results that are available */ - adreno_profile_process_results(adreno_dev); + adreno_profile_process_results(ADRENO_DEVICE(device)); - status = adreno_idle(device); - if (status) - return status; - /* set the device to default pagetable */ - kgsl_mmu_set_pt(&device->mmu, device->mmu.defaultpagetable); - kgsl_sharedmem_writel(device, - &adreno_dev->ringbuffers[0].pagetable_desc, - offsetof(struct adreno_ringbuffer_pagetable_info, - current_global_ptname), 0); - /* set ringbuffers to NULL ctxt */ - adreno_set_active_ctxs_null(adreno_dev); - - return status; + /* Wait for the device to go idle */ + return adreno_idle(device); } /** diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index 7ac91f203a70..9f462bca26ce 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -193,6 +193,47 @@ enum adreno_gpurev { struct adreno_gpudev; +/* Time to allow preemption to complete (in ms) */ +#define ADRENO_PREEMPT_TIMEOUT 10000 + +/** + * enum adreno_preempt_states + * ADRENO_PREEMPT_NONE: No preemption is scheduled + * ADRENO_PREEMPT_START: The S/W has started + * ADRENO_PREEMPT_TRIGGERED: A preeempt has been triggered in the HW + * ADRENO_PREEMPT_FAULTED: The preempt timer has fired + * ADRENO_PREEMPT_PENDING: The H/W has signaled preemption complete + * ADRENO_PREEMPT_COMPLETE: Preemption could not be finished in the IRQ handler, + * worker has been scheduled + */ +enum adreno_preempt_states { + ADRENO_PREEMPT_NONE = 0, + ADRENO_PREEMPT_START, + ADRENO_PREEMPT_TRIGGERED, + ADRENO_PREEMPT_FAULTED, + ADRENO_PREEMPT_PENDING, + ADRENO_PREEMPT_COMPLETE, +}; + +/** + * struct adreno_preemption + * @state: The current state of preemption + * @counters: Memory descriptor for the memory where the GPU writes the + * preemption counters on switch + * @timer: A timer to make sure preemption doesn't stall + * @work: A work struct for the preemption worker (for 5XX) + * @token_submit: Indicates if a preempt token has been submitted in + * current ringbuffer (for 4XX) + */ +struct adreno_preemption { + atomic_t state; + struct kgsl_memdesc counters; + struct timer_list timer; + struct work_struct work; + bool token_submit; +}; + + struct adreno_busy_data { unsigned int gpu_busy; unsigned int vbif_ram_cycles; @@ -368,7 +409,7 @@ struct adreno_device { const struct firmware *lm_fw; uint32_t *lm_sequence; uint32_t lm_size; - struct kgsl_memdesc preemption_counters; + struct adreno_preemption preempt; struct work_struct gpmu_work; uint32_t lm_leakage; uint32_t lm_limit; @@ -458,6 +499,8 @@ enum adreno_regs { ADRENO_REG_CP_WFI_PEND_CTR, ADRENO_REG_CP_RB_BASE, ADRENO_REG_CP_RB_BASE_HI, + ADRENO_REG_CP_RB_RPTR_ADDR_LO, + ADRENO_REG_CP_RB_RPTR_ADDR_HI, ADRENO_REG_CP_RB_RPTR, ADRENO_REG_CP_RB_WPTR, ADRENO_REG_CP_CNTL, @@ -709,17 +752,12 @@ struct adreno_gpudev { void (*pwrlevel_change_settings)(struct adreno_device *, unsigned int prelevel, unsigned int postlevel, bool post); - int (*preemption_pre_ibsubmit)(struct adreno_device *, - struct adreno_ringbuffer *, unsigned int *, - struct kgsl_context *, uint64_t cond_addr, - struct kgsl_memobj_node *); + unsigned int (*preemption_pre_ibsubmit)(struct adreno_device *, + struct adreno_ringbuffer *rb, + unsigned int *, struct kgsl_context *); int (*preemption_yield_enable)(unsigned int *); - int (*preemption_post_ibsubmit)(struct adreno_device *, - struct adreno_ringbuffer *, unsigned int *, - struct kgsl_context *); - int (*preemption_token)(struct adreno_device *, - struct adreno_ringbuffer *, unsigned int *, - uint64_t gpuaddr); + unsigned int (*preemption_post_ibsubmit)(struct adreno_device *, + unsigned int *); int (*preemption_init)(struct adreno_device *); void (*preemption_schedule)(struct adreno_device *); void (*enable_64bit)(struct adreno_device *); @@ -1260,34 +1298,32 @@ static inline int adreno_bootstrap_ucode(struct adreno_device *adreno_dev) } /** - * adreno_preempt_state() - Check if preemption state is equal to given state + * adreno_in_preempt_state() - Check if preemption state is equal to given state * @adreno_dev: Device whose preemption state is checked * @state: State to compare against */ -static inline unsigned int adreno_preempt_state( - struct adreno_device *adreno_dev, - enum adreno_dispatcher_preempt_states state) +static inline bool adreno_in_preempt_state(struct adreno_device *adreno_dev, + enum adreno_preempt_states state) { - return atomic_read(&adreno_dev->dispatcher.preemption_state) == - state; + return atomic_read(&adreno_dev->preempt.state) == state; } - /** - * adreno_get_rptr() - Get the current ringbuffer read pointer - * @rb: Pointer the ringbuffer to query - * - * Get the current read pointer from the GPU register. + * adreno_set_preempt_state() - Set the specified preemption state + * @adreno_dev: Device to change preemption state + * @state: State to set */ -static inline unsigned int -adreno_get_rptr(struct adreno_ringbuffer *rb) +static inline void adreno_set_preempt_state(struct adreno_device *adreno_dev, + enum adreno_preempt_states state) { - struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); - if (adreno_dev->cur_rb == rb && - adreno_preempt_state(adreno_dev, - ADRENO_DISPATCHER_PREEMPT_CLEAR)) - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_RPTR, &(rb->rptr)); + /* + * atomic_set doesn't use barriers, so we need to do it ourselves. One + * before... + */ + smp_wmb(); + atomic_set(&adreno_dev->preempt.state, state); - return rb->rptr; + /* ... and one after */ + smp_wmb(); } static inline bool adreno_is_preemption_enabled( @@ -1295,7 +1331,6 @@ static inline bool adreno_is_preemption_enabled( { return test_bit(ADRENO_DEVICE_PREEMPTION, &adreno_dev->priv); } - /** * adreno_ctx_get_rb() - Return the ringbuffer that a context should * use based on priority @@ -1332,25 +1367,6 @@ static inline struct adreno_ringbuffer *adreno_ctx_get_rb( return &(adreno_dev->ringbuffers[ adreno_dev->num_ringbuffers - 1]); } -/* - * adreno_set_active_ctxs_null() - Put back reference to any active context - * and set the active context to NULL - * @adreno_dev: The adreno device - */ -static inline void adreno_set_active_ctxs_null(struct adreno_device *adreno_dev) -{ - int i; - struct adreno_ringbuffer *rb; - FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { - if (rb->drawctxt_active) - kgsl_context_put(&(rb->drawctxt_active->base)); - rb->drawctxt_active = NULL; - kgsl_sharedmem_writel(KGSL_DEVICE(adreno_dev), - &rb->pagetable_desc, - offsetof(struct adreno_ringbuffer_pagetable_info, - current_rb_ptname), 0); - } -} /* * adreno_compare_prio_level() - Compares 2 priority levels based on enum values @@ -1371,6 +1387,13 @@ void adreno_readreg64(struct adreno_device *adreno_dev, void adreno_writereg64(struct adreno_device *adreno_dev, enum adreno_regs lo, enum adreno_regs hi, uint64_t val); +unsigned int adreno_get_rptr(struct adreno_ringbuffer *rb); + +static inline bool adreno_rb_empty(struct adreno_ringbuffer *rb) +{ + return (adreno_get_rptr(rb) == rb->wptr); +} + static inline bool adreno_soft_fault_detect(struct adreno_device *adreno_dev) { return adreno_dev->fast_hang_detect && @@ -1400,4 +1423,36 @@ static inline bool adreno_support_64bit(struct adreno_device *adreno_dev) } #endif /*BITS_PER_LONG*/ +static inline void adreno_ringbuffer_set_global( + struct adreno_device *adreno_dev, int name) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + + kgsl_sharedmem_writel(device, + &adreno_dev->ringbuffers[0].pagetable_desc, + PT_INFO_OFFSET(current_global_ptname), name); +} + +static inline void adreno_ringbuffer_set_pagetable(struct adreno_ringbuffer *rb, + struct kgsl_pagetable *pt) +{ + struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned long flags; + + spin_lock_irqsave(&rb->preempt_lock, flags); + + kgsl_sharedmem_writel(device, &rb->pagetable_desc, + PT_INFO_OFFSET(current_rb_ptname), pt->name); + + kgsl_sharedmem_writeq(device, &rb->pagetable_desc, + PT_INFO_OFFSET(ttbr0), kgsl_mmu_pagetable_get_ttbr0(pt)); + + kgsl_sharedmem_writel(device, &rb->pagetable_desc, + PT_INFO_OFFSET(contextidr), + kgsl_mmu_pagetable_get_contextidr(pt)); + + spin_unlock_irqrestore(&rb->preempt_lock, flags); +} + #endif /*__ADRENO_H */ diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c index ea8b75f4c83b..2accbe5c5764 100644 --- a/drivers/gpu/msm/adreno_a3xx.c +++ b/drivers/gpu/msm/adreno_a3xx.c @@ -1756,9 +1756,9 @@ static int _ringbuffer_bootstrap_ucode(struct adreno_device *adreno_dev, *cmds++ = cp_type3_packet(CP_INTERRUPT, 1); *cmds++ = 0; - rb->wptr = rb->wptr - 2; + rb->_wptr = rb->_wptr - 2; adreno_ringbuffer_submit(rb, NULL); - rb->wptr = rb->wptr + 2; + rb->_wptr = rb->_wptr + 2; } else { for (i = pfp_idx; i < adreno_dev->pfp_fw_size; i++) *cmds++ = adreno_dev->pfp_fw[i]; diff --git a/drivers/gpu/msm/adreno_a4xx.c b/drivers/gpu/msm/adreno_a4xx.c index b1196da0cee1..b15d23cfbe0a 100644 --- a/drivers/gpu/msm/adreno_a4xx.c +++ b/drivers/gpu/msm/adreno_a4xx.c @@ -178,111 +178,6 @@ static const struct adreno_vbif_platform a4xx_vbif_platforms[] = { { adreno_is_a418, a430_vbif }, }; -/* a4xx_preemption_start() - Setup state to start preemption */ -static void a4xx_preemption_start(struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - uint32_t val; - - /* - * Setup scratch registers from which the GPU will program the - * registers required to start execution of new ringbuffer - * set ringbuffer address - */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG8, - rb->buffer_desc.gpuaddr); - kgsl_regread(device, A4XX_CP_RB_CNTL, &val); - /* scratch REG9 corresponds to CP_RB_CNTL register */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG9, val); - /* scratch REG10 corresponds to rptr address */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG10, 0); - /* scratch REG11 corresponds to rptr */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG11, rb->rptr); - /* scratch REG12 corresponds to wptr */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG12, rb->wptr); - /* - * scratch REG13 corresponds to IB1_BASE, - * 0 since we do not do switches in between IB's - */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG13, 0); - /* scratch REG14 corresponds to IB1_BUFSZ */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG14, 0); - /* scratch REG15 corresponds to IB2_BASE */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG15, 0); - /* scratch REG16 corresponds to IB2_BUFSZ */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG16, 0); - /* scratch REG17 corresponds to GPR11 */ - kgsl_regwrite(device, A4XX_CP_SCRATCH_REG17, rb->gpr11); -} - -/* a4xx_preemption_save() - Save the state after preemption is done */ -static void a4xx_preemption_save(struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - - kgsl_regread(device, A4XX_CP_SCRATCH_REG18, &rb->rptr); - kgsl_regread(device, A4XX_CP_SCRATCH_REG23, &rb->gpr11); -} - -static int a4xx_preemption_token(struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb, unsigned int *cmds, - uint64_t gpuaddr) -{ - unsigned int *cmds_orig = cmds; - - /* Turn on preemption flag */ - /* preemption token - fill when pt switch command size is known */ - *cmds++ = cp_type3_packet(CP_PREEMPT_TOKEN, 3); - *cmds++ = (uint)gpuaddr; - *cmds++ = 1; - /* generate interrupt on preemption completion */ - *cmds++ = 1 << CP_PREEMPT_ORDINAL_INTERRUPT; - - return cmds - cmds_orig; - -} - -static int a4xx_preemption_pre_ibsubmit( - struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb, unsigned int *cmds, - struct kgsl_context *context, uint64_t cond_addr, - struct kgsl_memobj_node *ib) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - unsigned int *cmds_orig = cmds; - int exec_ib = 0; - - cmds += a4xx_preemption_token(adreno_dev, rb, cmds, - device->memstore.gpuaddr + - KGSL_MEMSTORE_OFFSET(context->id, preempted)); - - if (ib) - exec_ib = 1; - - *cmds++ = cp_type3_packet(CP_COND_EXEC, 4); - *cmds++ = cond_addr; - *cmds++ = cond_addr; - *cmds++ = 1; - *cmds++ = 7 + exec_ib * 3; - if (exec_ib) { - *cmds++ = cp_type3_packet(CP_INDIRECT_BUFFER_PFE, 2); - *cmds++ = ib->gpuaddr; - *cmds++ = (unsigned int) ib->size >> 2; - } - /* clear preemption flag */ - *cmds++ = cp_type3_packet(CP_MEM_WRITE, 2); - *cmds++ = cond_addr; - *cmds++ = 0; - *cmds++ = cp_type3_packet(CP_WAIT_MEM_WRITES, 1); - *cmds++ = 0; - *cmds++ = cp_type3_packet(CP_WAIT_FOR_ME, 1); - *cmds++ = 0; - - return cmds - cmds_orig; -} - /* * a4xx_is_sptp_idle() - A430 SP/TP should be off to be considered idle * @adreno_dev: The adreno device pointer @@ -723,6 +618,8 @@ static void a4xx_start(struct adreno_device *adreno_dev) gpudev->vbif_xin_halt_ctrl0_mask = A405_VBIF_XIN_HALT_CTRL0_MASK; + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); + a4xx_protect_init(adreno_dev); } @@ -839,6 +736,7 @@ static unsigned int a4xx_register_offsets[ADRENO_REG_REGISTER_MAX] = { ADRENO_REG_DEFINE(ADRENO_REG_CP_WFI_PEND_CTR, A4XX_CP_WFI_PEND_CTR), ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_BASE, A4XX_CP_RB_BASE), ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_BASE_HI, ADRENO_REG_SKIP), + ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_RPTR_ADDR_LO, A4XX_CP_RB_RPTR_ADDR), ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_RPTR, A4XX_CP_RB_RPTR), ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_WPTR, A4XX_CP_RB_WPTR), ADRENO_REG_DEFINE(ADRENO_REG_CP_CNTL, A4XX_CP_CNTL), @@ -1634,8 +1532,15 @@ static int a4xx_rb_start(struct adreno_device *adreno_dev, unsigned int start_type) { struct adreno_ringbuffer *rb = ADRENO_CURRENT_RINGBUFFER(adreno_dev); + struct kgsl_device *device = &adreno_dev->dev; + uint64_t addr; int ret; + addr = SCRATCH_RPTR_GPU_ADDR(device, rb->id); + + adreno_writereg64(adreno_dev, ADRENO_REG_CP_RB_RPTR_ADDR_LO, + ADRENO_REG_CP_RB_RPTR_ADDR_HI, addr); + /* * The size of the ringbuffer in the hardware is the log2 * representation of the size in quadwords (sizedwords / 2). @@ -1644,8 +1549,8 @@ static int a4xx_rb_start(struct adreno_device *adreno_dev, */ adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_CNTL, - (ilog2(KGSL_RB_DWORDS >> 1) & 0x3F) | - (1 << 27)); + ((ilog2(4) << 8) & 0x1F00) | + (ilog2(KGSL_RB_DWORDS >> 1) & 0x3F)); adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_BASE, rb->buffer_desc.gpuaddr); @@ -1755,6 +1660,19 @@ static struct adreno_coresight a4xx_coresight = { .groups = a4xx_coresight_groups, }; +static void a4xx_preempt_callback(struct adreno_device *adreno_dev, int bit) +{ + if (atomic_read(&adreno_dev->preempt.state) != ADRENO_PREEMPT_TRIGGERED) + return; + + trace_adreno_hw_preempt_trig_to_comp_int(adreno_dev->cur_rb, + adreno_dev->next_rb, + adreno_get_rptr(adreno_dev->cur_rb), + adreno_get_rptr(adreno_dev->next_rb)); + + adreno_dispatcher_schedule(KGSL_DEVICE(adreno_dev)); +} + #define A4XX_INT_MASK \ ((1 << A4XX_INT_RBBM_AHB_ERROR) | \ (1 << A4XX_INT_RBBM_REG_TIMEOUT) | \ @@ -1792,7 +1710,7 @@ static struct adreno_irq_funcs a4xx_irq_funcs[32] = { /* 6 - RBBM_ATB_ASYNC_OVERFLOW */ ADRENO_IRQ_CALLBACK(a4xx_err_callback), ADRENO_IRQ_CALLBACK(NULL), /* 7 - RBBM_GPC_ERR */ - ADRENO_IRQ_CALLBACK(adreno_dispatcher_preempt_callback), /* 8 - CP_SW */ + ADRENO_IRQ_CALLBACK(a4xx_preempt_callback), /* 8 - CP_SW */ ADRENO_IRQ_CALLBACK(a4xx_err_callback), /* 9 - CP_OPCODE_ERROR */ /* 10 - CP_RESERVED_BIT_ERROR */ ADRENO_IRQ_CALLBACK(a4xx_err_callback), @@ -1833,433 +1751,6 @@ static struct adreno_snapshot_data a4xx_snapshot_data = { .sect_sizes = &a4xx_snap_sizes, }; -#define ADRENO_RB_PREEMPT_TOKEN_DWORDS 125 - -static int a4xx_submit_preempt_token(struct adreno_ringbuffer *rb, - struct adreno_ringbuffer *incoming_rb) -{ - struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - unsigned int *ringcmds, *start; - struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); - int ptname; - struct kgsl_pagetable *pt; - int pt_switch_sizedwords = 0, total_sizedwords = 20; - unsigned link[ADRENO_RB_PREEMPT_TOKEN_DWORDS]; - uint i; - - if (incoming_rb->preempted_midway) { - - kgsl_sharedmem_readl(&incoming_rb->pagetable_desc, - &ptname, offsetof( - struct adreno_ringbuffer_pagetable_info, - current_rb_ptname)); - pt = kgsl_mmu_get_pt_from_ptname(&(device->mmu), - ptname); - /* - * always expect a valid pt, else pt refcounting is - * messed up or current pt tracking has a bug which - * could lead to eventual disaster - */ - BUG_ON(!pt); - /* set the ringbuffer for incoming RB */ - pt_switch_sizedwords = - adreno_iommu_set_pt_generate_cmds(incoming_rb, - &link[0], pt); - total_sizedwords += pt_switch_sizedwords; - } - - /* - * Allocate total_sizedwords space in RB, this is the max space - * required. - */ - ringcmds = adreno_ringbuffer_allocspace(rb, total_sizedwords); - - if (IS_ERR(ringcmds)) - return PTR_ERR(ringcmds); - - start = ringcmds; - - *ringcmds++ = cp_packet(adreno_dev, CP_SET_PROTECTED_MODE, 1); - *ringcmds++ = 0; - - if (incoming_rb->preempted_midway) { - for (i = 0; i < pt_switch_sizedwords; i++) - *ringcmds++ = link[i]; - } - - *ringcmds++ = cp_register(adreno_dev, adreno_getreg(adreno_dev, - ADRENO_REG_CP_PREEMPT_DISABLE), 1); - *ringcmds++ = 0; - - *ringcmds++ = cp_packet(adreno_dev, CP_SET_PROTECTED_MODE, 1); - *ringcmds++ = 1; - - ringcmds += gpudev->preemption_token(adreno_dev, rb, ringcmds, - device->memstore.gpuaddr + - KGSL_MEMSTORE_RB_OFFSET(rb, preempted)); - - if ((uint)(ringcmds - start) > total_sizedwords) { - KGSL_DRV_ERR(device, "Insufficient rb size allocated\n"); - BUG(); - } - - /* - * If we have commands less than the space reserved in RB - * adjust the wptr accordingly - */ - rb->wptr = rb->wptr - (total_sizedwords - (uint)(ringcmds - start)); - - /* submit just the preempt token */ - mb(); - kgsl_pwrscale_busy(device); - adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, rb->wptr); - return 0; -} - -/** - * a4xx_preempt_trig_state() - Schedule preemption in TRIGGERRED - * state - * @adreno_dev: Device which is in TRIGGERRED state - */ -static void a4xx_preempt_trig_state( - struct adreno_device *adreno_dev) -{ - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - unsigned int rbbase, val; - - /* - * Hardware not yet idle means that preemption interrupt - * may still occur, nothing to do here until interrupt signals - * completion of preemption, just return here - */ - if (!adreno_hw_isidle(adreno_dev)) - return; - - /* - * We just changed states, reschedule dispatcher to change - * preemption states - */ - if (ADRENO_DISPATCHER_PREEMPT_TRIGGERED != - atomic_read(&dispatcher->preemption_state)) { - adreno_dispatcher_schedule(device); - return; - } - - /* - * H/W is idle and we did not get a preemption interrupt, may - * be device went idle w/o encountering any preempt token or - * we already preempted w/o interrupt - */ - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_BASE, &rbbase); - /* Did preemption occur, if so then change states and return */ - if (rbbase != adreno_dev->cur_rb->buffer_desc.gpuaddr) { - adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT_DEBUG, &val); - if (val && rbbase == adreno_dev->next_rb->buffer_desc.gpuaddr) { - KGSL_DRV_INFO(device, - "Preemption completed without interrupt\n"); - trace_adreno_hw_preempt_trig_to_comp(adreno_dev->cur_rb, - adreno_dev->next_rb); - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_COMPLETE); - adreno_dispatcher_schedule(device); - return; - } - adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); - /* reschedule dispatcher to take care of the fault */ - adreno_dispatcher_schedule(device); - return; - } - /* - * Check if preempt token was submitted after preemption trigger, if so - * then preemption should have occurred, since device is already idle it - * means something went wrong - trigger FT - */ - if (dispatcher->preempt_token_submit) { - adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); - /* reschedule dispatcher to take care of the fault */ - adreno_dispatcher_schedule(device); - return; - } - /* - * Preempt token was not submitted after preemption trigger so device - * may have gone idle before preemption could occur, if there are - * commands that got submitted to current RB after triggering preemption - * then submit them as those commands may have a preempt token in them - */ - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_RPTR, - &adreno_dev->cur_rb->rptr); - if (adreno_dev->cur_rb->rptr != adreno_dev->cur_rb->wptr) { - /* - * Memory barrier before informing the - * hardware of new commands - */ - mb(); - kgsl_pwrscale_busy(device); - adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, - adreno_dev->cur_rb->wptr); - return; - } - - /* Submit preempt token to make preemption happen */ - if (adreno_drawctxt_switch(adreno_dev, adreno_dev->cur_rb, NULL, 0)) - BUG(); - if (a4xx_submit_preempt_token(adreno_dev->cur_rb, - adreno_dev->next_rb)) - BUG(); - dispatcher->preempt_token_submit = 1; - adreno_dev->cur_rb->wptr_preempt_end = adreno_dev->cur_rb->wptr; - trace_adreno_hw_preempt_token_submit(adreno_dev->cur_rb, - adreno_dev->next_rb); -} - -/** - * a4xx_preempt_clear_state() - Schedule preemption in - * CLEAR state. Preemption can be issued in this state. - * @adreno_dev: Device which is in CLEAR state - */ -static void a4xx_preempt_clear_state( - struct adreno_device *adreno_dev) - -{ - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct adreno_dispatcher_cmdqueue *dispatch_tempq; - struct kgsl_cmdbatch *cmdbatch; - struct adreno_ringbuffer *highest_busy_rb; - int switch_low_to_high; - int ret; - - /* Device not awake means there is nothing to do */ - if (!kgsl_state_is_awake(device)) - return; - - /* keep updating the current rptr when preemption is clear */ - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_RPTR, - &(adreno_dev->cur_rb->rptr)); - - highest_busy_rb = adreno_dispatcher_get_highest_busy_rb(adreno_dev); - if (!highest_busy_rb) - return; - - switch_low_to_high = adreno_compare_prio_level( - highest_busy_rb->id, - adreno_dev->cur_rb->id); - - /* already current then return */ - if (!switch_low_to_high) - return; - - if (switch_low_to_high < 0) { - /* - * if switching to lower priority make sure that the rptr and - * wptr are equal, when the lower rb is not starved - */ - if (adreno_dev->cur_rb->rptr != adreno_dev->cur_rb->wptr) - return; - /* - * switch to default context because when we switch back - * to higher context then its not known which pt will - * be current, so by making it default here the next - * commands submitted will set the right pt - */ - ret = adreno_drawctxt_switch(adreno_dev, - adreno_dev->cur_rb, - NULL, 0); - /* - * lower priority RB has to wait until space opens up in - * higher RB - */ - if (ret) - return; - - adreno_writereg(adreno_dev, - ADRENO_REG_CP_PREEMPT_DISABLE, 1); - } - - /* - * setup registers to do the switch to highest priority RB - * which is not empty or may be starving away(poor thing) - */ - a4xx_preemption_start(adreno_dev, highest_busy_rb); - - /* turn on IOMMU as the preemption may trigger pt switch */ - kgsl_mmu_enable_clk(&device->mmu); - - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_TRIGGERED); - - adreno_dev->next_rb = highest_busy_rb; - mod_timer(&dispatcher->preempt_timer, jiffies + - msecs_to_jiffies(ADRENO_DISPATCH_PREEMPT_TIMEOUT)); - - trace_adreno_hw_preempt_clear_to_trig(adreno_dev->cur_rb, - adreno_dev->next_rb); - /* issue PREEMPT trigger */ - adreno_writereg(adreno_dev, ADRENO_REG_CP_PREEMPT, 1); - /* - * IOMMU clock can be safely switched off after the timestamp - * of the first command in the new rb - */ - dispatch_tempq = &adreno_dev->next_rb->dispatch_q; - if (dispatch_tempq->head != dispatch_tempq->tail) - cmdbatch = dispatch_tempq->cmd_q[dispatch_tempq->head]; - else - cmdbatch = NULL; - if (cmdbatch) - adreno_ringbuffer_mmu_disable_clk_on_ts(device, - adreno_dev->next_rb, - cmdbatch->global_ts); - else - adreno_ringbuffer_mmu_disable_clk_on_ts(device, - adreno_dev->next_rb, adreno_dev->next_rb->timestamp); - /* submit preempt token packet to ensure preemption */ - if (switch_low_to_high < 0) { - ret = a4xx_submit_preempt_token( - adreno_dev->cur_rb, adreno_dev->next_rb); - /* - * unexpected since we are submitting this when rptr = wptr, - * this was checked above already - */ - BUG_ON(ret); - dispatcher->preempt_token_submit = 1; - adreno_dev->cur_rb->wptr_preempt_end = adreno_dev->cur_rb->wptr; - } else { - dispatcher->preempt_token_submit = 0; - adreno_dispatcher_schedule(device); - adreno_dev->cur_rb->wptr_preempt_end = 0xFFFFFFFF; - } -} - -/** - * a4xx_preempt_complete_state() - Schedule preemption in - * COMPLETE state - * @adreno_dev: Device which is in COMPLETE state - */ -static void a4xx_preempt_complete_state( - struct adreno_device *adreno_dev) - -{ - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct adreno_dispatcher_cmdqueue *dispatch_q; - unsigned int wptr, rbbase; - unsigned int val, val1; - - del_timer_sync(&dispatcher->preempt_timer); - - adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT, &val); - adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT_DEBUG, &val1); - - if (val || !val1) { - KGSL_DRV_ERR(device, - "Invalid state after preemption CP_PREEMPT: %08x, CP_PREEMPT_DEBUG: %08x\n", - val, val1); - adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); - adreno_dispatcher_schedule(device); - return; - } - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_BASE, &rbbase); - if (rbbase != adreno_dev->next_rb->buffer_desc.gpuaddr) { - KGSL_DRV_ERR(device, - "RBBASE incorrect after preemption, expected %x got %016llx\b", - rbbase, - adreno_dev->next_rb->buffer_desc.gpuaddr); - adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); - adreno_dispatcher_schedule(device); - return; - } - - a4xx_preemption_save(adreno_dev, adreno_dev->cur_rb); - - dispatch_q = &(adreno_dev->cur_rb->dispatch_q); - /* new RB is the current RB */ - trace_adreno_hw_preempt_comp_to_clear(adreno_dev->next_rb, - adreno_dev->cur_rb); - adreno_dev->prev_rb = adreno_dev->cur_rb; - adreno_dev->cur_rb = adreno_dev->next_rb; - adreno_dev->cur_rb->preempted_midway = 0; - adreno_dev->cur_rb->wptr_preempt_end = 0xFFFFFFFF; - adreno_dev->next_rb = NULL; - if (adreno_disp_preempt_fair_sched) { - /* starved rb is now scheduled so unhalt dispatcher */ - if (ADRENO_DISPATCHER_RB_STARVE_TIMER_ELAPSED == - adreno_dev->cur_rb->starve_timer_state) - adreno_put_gpu_halt(adreno_dev); - adreno_dev->cur_rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_SCHEDULED; - adreno_dev->cur_rb->sched_timer = jiffies; - /* - * If the outgoing RB is has commands then set the - * busy time for it - */ - if (adreno_dev->prev_rb->rptr != adreno_dev->prev_rb->wptr) { - adreno_dev->prev_rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_INIT; - adreno_dev->prev_rb->sched_timer = jiffies; - } else { - adreno_dev->prev_rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_UNINIT; - } - } - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_CLEAR); - if (adreno_compare_prio_level(adreno_dev->prev_rb->id, - adreno_dev->cur_rb->id) < 0) { - if (adreno_dev->prev_rb->wptr_preempt_end != - adreno_dev->prev_rb->rptr) - adreno_dev->prev_rb->preempted_midway = 1; - } else if (adreno_dev->prev_rb->wptr_preempt_end != - adreno_dev->prev_rb->rptr) { - BUG(); - } - /* submit wptr if required for new rb */ - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_WPTR, &wptr); - if (adreno_dev->cur_rb->wptr != wptr) { - kgsl_pwrscale_busy(device); - adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, - adreno_dev->cur_rb->wptr); - } - /* clear preemption register */ - adreno_writereg(adreno_dev, ADRENO_REG_CP_PREEMPT_DEBUG, 0); - adreno_preempt_process_dispatch_queue(adreno_dev, dispatch_q); -} - -static void a4xx_preemption_schedule( - struct adreno_device *adreno_dev) -{ - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - - if (!adreno_is_preemption_enabled(adreno_dev)) - return; - - mutex_lock(&device->mutex); - - switch (atomic_read(&dispatcher->preemption_state)) { - case ADRENO_DISPATCHER_PREEMPT_CLEAR: - a4xx_preempt_clear_state(adreno_dev); - break; - case ADRENO_DISPATCHER_PREEMPT_TRIGGERED: - a4xx_preempt_trig_state(adreno_dev); - /* - * if we transitioned to next state then fall-through - * processing to next state - */ - if (!adreno_preempt_state(adreno_dev, - ADRENO_DISPATCHER_PREEMPT_COMPLETE)) - break; - case ADRENO_DISPATCHER_PREEMPT_COMPLETE: - a4xx_preempt_complete_state(adreno_dev); - break; - default: - BUG(); - } - - mutex_unlock(&device->mutex); -} - struct adreno_gpudev adreno_a4xx_gpudev = { .reg_offsets = &a4xx_reg_offsets, .ft_perf_counters = a4xx_ft_perf_counters, @@ -2284,6 +1775,6 @@ struct adreno_gpudev adreno_a4xx_gpudev = { .regulator_enable = a4xx_regulator_enable, .regulator_disable = a4xx_regulator_disable, .preemption_pre_ibsubmit = a4xx_preemption_pre_ibsubmit, - .preemption_token = a4xx_preemption_token, .preemption_schedule = a4xx_preemption_schedule, + .preemption_init = a4xx_preemption_init, }; diff --git a/drivers/gpu/msm/adreno_a4xx.h b/drivers/gpu/msm/adreno_a4xx.h index e425dc8e9f7b..5dabc26fd34f 100644 --- a/drivers/gpu/msm/adreno_a4xx.h +++ b/drivers/gpu/msm/adreno_a4xx.h @@ -47,6 +47,15 @@ "RBBM_DPM_THERMAL_YELLOW_ERR" }, \ { BIT(A4XX_INT_RBBM_DPM_THERMAL_RED_ERR), "RBBM_DPM_THERMAL_RED_ERR" } +unsigned int a4xx_preemption_pre_ibsubmit(struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb, + unsigned int *cmds, + struct kgsl_context *context); + +void a4xx_preemption_schedule(struct adreno_device *adreno_dev); + +int a4xx_preemption_init(struct adreno_device *adreno_dev); + void a4xx_snapshot(struct adreno_device *adreno_dev, struct kgsl_snapshot *snapshot); diff --git a/drivers/gpu/msm/adreno_a4xx_preempt.c b/drivers/gpu/msm/adreno_a4xx_preempt.c new file mode 100644 index 000000000000..4087ac60c89e --- /dev/null +++ b/drivers/gpu/msm/adreno_a4xx_preempt.c @@ -0,0 +1,571 @@ +/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "adreno.h" +#include "adreno_a4xx.h" +#include "adreno_trace.h" +#include "adreno_pm4types.h" + +#define ADRENO_RB_PREEMPT_TOKEN_DWORDS 125 + +static void a4xx_preemption_timer(unsigned long data) +{ + struct adreno_device *adreno_dev = (struct adreno_device *) data; + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int cur_rptr = adreno_get_rptr(adreno_dev->cur_rb); + unsigned int next_rptr = adreno_get_rptr(adreno_dev->next_rb); + + KGSL_DRV_ERR(device, + "Preemption timed out. cur_rb rptr/wptr %x/%x id %d, next_rb rptr/wptr %x/%x id %d, disp_state: %d\n", + cur_rptr, adreno_dev->cur_rb->wptr, adreno_dev->cur_rb->id, + next_rptr, adreno_dev->next_rb->wptr, adreno_dev->next_rb->id, + atomic_read(&adreno_dev->preempt.state)); + + adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); + adreno_dispatcher_schedule(device); +} + +static unsigned int a4xx_preemption_token(struct adreno_device *adreno_dev, + unsigned int *cmds, uint64_t gpuaddr) +{ + unsigned int *cmds_orig = cmds; + + /* Turn on preemption flag */ + /* preemption token - fill when pt switch command size is known */ + *cmds++ = cp_type3_packet(CP_PREEMPT_TOKEN, 3); + *cmds++ = (uint)gpuaddr; + *cmds++ = 1; + /* generate interrupt on preemption completion */ + *cmds++ = 1 << CP_PREEMPT_ORDINAL_INTERRUPT; + + return (unsigned int) (cmds - cmds_orig); +} + +unsigned int a4xx_preemption_pre_ibsubmit(struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb, unsigned int *cmds, + struct kgsl_context *context) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int *cmds_orig = cmds; + unsigned int cond_addr = device->memstore.gpuaddr + + MEMSTORE_ID_GPU_ADDR(device, context->id, preempted); + + cmds += a4xx_preemption_token(adreno_dev, cmds, cond_addr); + + *cmds++ = cp_type3_packet(CP_COND_EXEC, 4); + *cmds++ = cond_addr; + *cmds++ = cond_addr; + *cmds++ = 1; + *cmds++ = 7; + + /* clear preemption flag */ + *cmds++ = cp_type3_packet(CP_MEM_WRITE, 2); + *cmds++ = cond_addr; + *cmds++ = 0; + *cmds++ = cp_type3_packet(CP_WAIT_MEM_WRITES, 1); + *cmds++ = 0; + *cmds++ = cp_type3_packet(CP_WAIT_FOR_ME, 1); + *cmds++ = 0; + + return (unsigned int) (cmds - cmds_orig); +} + + +static void a4xx_preemption_start(struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + uint32_t val; + + /* + * Setup scratch registers from which the GPU will program the + * registers required to start execution of new ringbuffer + * set ringbuffer address + */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG8, + rb->buffer_desc.gpuaddr); + kgsl_regread(device, A4XX_CP_RB_CNTL, &val); + /* scratch REG9 corresponds to CP_RB_CNTL register */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG9, val); + /* scratch REG10 corresponds to rptr address */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG10, + SCRATCH_RPTR_GPU_ADDR(device, rb->id)); + /* scratch REG11 corresponds to rptr */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG11, adreno_get_rptr(rb)); + /* scratch REG12 corresponds to wptr */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG12, rb->wptr); + /* + * scratch REG13 corresponds to IB1_BASE, + * 0 since we do not do switches in between IB's + */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG13, 0); + /* scratch REG14 corresponds to IB1_BUFSZ */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG14, 0); + /* scratch REG15 corresponds to IB2_BASE */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG15, 0); + /* scratch REG16 corresponds to IB2_BUFSZ */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG16, 0); + /* scratch REG17 corresponds to GPR11 */ + kgsl_regwrite(device, A4XX_CP_SCRATCH_REG17, rb->gpr11); +} + +static void a4xx_preemption_save(struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + + kgsl_regread(device, A4XX_CP_SCRATCH_REG23, &rb->gpr11); +} + + +static int a4xx_submit_preempt_token(struct adreno_ringbuffer *rb, + struct adreno_ringbuffer *incoming_rb) +{ + struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int *ringcmds, *start; + int ptname; + struct kgsl_pagetable *pt; + int pt_switch_sizedwords = 0, total_sizedwords = 20; + unsigned link[ADRENO_RB_PREEMPT_TOKEN_DWORDS]; + uint i; + + if (incoming_rb->preempted_midway) { + + kgsl_sharedmem_readl(&incoming_rb->pagetable_desc, + &ptname, PT_INFO_OFFSET(current_rb_ptname)); + pt = kgsl_mmu_get_pt_from_ptname(&(device->mmu), + ptname); + /* set the ringbuffer for incoming RB */ + pt_switch_sizedwords = + adreno_iommu_set_pt_generate_cmds(incoming_rb, + &link[0], pt); + total_sizedwords += pt_switch_sizedwords; + } + + /* + * Allocate total_sizedwords space in RB, this is the max space + * required. + */ + ringcmds = adreno_ringbuffer_allocspace(rb, total_sizedwords); + + if (IS_ERR(ringcmds)) + return PTR_ERR(ringcmds); + + start = ringcmds; + + *ringcmds++ = cp_packet(adreno_dev, CP_SET_PROTECTED_MODE, 1); + *ringcmds++ = 0; + + if (incoming_rb->preempted_midway) { + for (i = 0; i < pt_switch_sizedwords; i++) + *ringcmds++ = link[i]; + } + + *ringcmds++ = cp_register(adreno_dev, adreno_getreg(adreno_dev, + ADRENO_REG_CP_PREEMPT_DISABLE), 1); + *ringcmds++ = 0; + + *ringcmds++ = cp_packet(adreno_dev, CP_SET_PROTECTED_MODE, 1); + *ringcmds++ = 1; + + ringcmds += a4xx_preemption_token(adreno_dev, ringcmds, + device->memstore.gpuaddr + + MEMSTORE_RB_OFFSET(rb, preempted)); + + if ((uint)(ringcmds - start) > total_sizedwords) + KGSL_DRV_ERR(device, "Insufficient rb size allocated\n"); + + /* + * If we have commands less than the space reserved in RB + * adjust the wptr accordingly + */ + rb->wptr = rb->wptr - (total_sizedwords - (uint)(ringcmds - start)); + + /* submit just the preempt token */ + mb(); + kgsl_pwrscale_busy(device); + adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, rb->wptr); + return 0; +} + +static void a4xx_preempt_trig_state(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int rbbase, val; + int ret; + + /* + * Hardware not yet idle means that preemption interrupt + * may still occur, nothing to do here until interrupt signals + * completion of preemption, just return here + */ + if (!adreno_hw_isidle(adreno_dev)) + return; + + /* + * We just changed states, reschedule dispatcher to change + * preemption states + */ + + if (atomic_read(&adreno_dev->preempt.state) != + ADRENO_PREEMPT_TRIGGERED) { + adreno_dispatcher_schedule(device); + return; + } + + /* + * H/W is idle and we did not get a preemption interrupt, may + * be device went idle w/o encountering any preempt token or + * we already preempted w/o interrupt + */ + adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_BASE, &rbbase); + /* Did preemption occur, if so then change states and return */ + if (rbbase != adreno_dev->cur_rb->buffer_desc.gpuaddr) { + adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT_DEBUG, &val); + if (val && rbbase == adreno_dev->next_rb->buffer_desc.gpuaddr) { + KGSL_DRV_INFO(device, + "Preemption completed without interrupt\n"); + trace_adreno_hw_preempt_trig_to_comp(adreno_dev->cur_rb, + adreno_dev->next_rb, + adreno_get_rptr(adreno_dev->cur_rb), + adreno_get_rptr(adreno_dev->next_rb)); + adreno_set_preempt_state(adreno_dev, + ADRENO_PREEMPT_COMPLETE); + adreno_dispatcher_schedule(device); + return; + } + adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); + /* reschedule dispatcher to take care of the fault */ + adreno_dispatcher_schedule(device); + return; + } + /* + * Check if preempt token was submitted after preemption trigger, if so + * then preemption should have occurred, since device is already idle it + * means something went wrong - trigger FT + */ + if (adreno_dev->preempt.token_submit) { + adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); + /* reschedule dispatcher to take care of the fault */ + adreno_dispatcher_schedule(device); + return; + } + /* + * Preempt token was not submitted after preemption trigger so device + * may have gone idle before preemption could occur, if there are + * commands that got submitted to current RB after triggering preemption + * then submit them as those commands may have a preempt token in them + */ + if (!adreno_rb_empty(adreno_dev->cur_rb)) { + /* + * Memory barrier before informing the + * hardware of new commands + */ + mb(); + kgsl_pwrscale_busy(device); + adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, + adreno_dev->cur_rb->wptr); + return; + } + + /* Submit preempt token to make preemption happen */ + ret = adreno_drawctxt_switch(adreno_dev, adreno_dev->cur_rb, + NULL, 0); + if (ret) + KGSL_DRV_ERR(device, + "Unable to switch context to NULL: %d\n", ret); + + ret = a4xx_submit_preempt_token(adreno_dev->cur_rb, + adreno_dev->next_rb); + if (ret) + KGSL_DRV_ERR(device, + "Unable to submit preempt token: %d\n", ret); + + adreno_dev->preempt.token_submit = true; + adreno_dev->cur_rb->wptr_preempt_end = adreno_dev->cur_rb->wptr; + trace_adreno_hw_preempt_token_submit(adreno_dev->cur_rb, + adreno_dev->next_rb, + adreno_get_rptr(adreno_dev->cur_rb), + adreno_get_rptr(adreno_dev->next_rb)); +} + +static struct adreno_ringbuffer *a4xx_next_ringbuffer( + struct adreno_device *adreno_dev) +{ + struct adreno_ringbuffer *rb, *next = NULL; + int i; + + FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { + if (!adreno_rb_empty(rb) && next == NULL) { + next = rb; + continue; + } + + if (!adreno_disp_preempt_fair_sched) + continue; + + switch (rb->starve_timer_state) { + case ADRENO_DISPATCHER_RB_STARVE_TIMER_UNINIT: + if (!adreno_rb_empty(rb) && + adreno_dev->cur_rb != rb) { + rb->starve_timer_state = + ADRENO_DISPATCHER_RB_STARVE_TIMER_INIT; + rb->sched_timer = jiffies; + } + break; + case ADRENO_DISPATCHER_RB_STARVE_TIMER_INIT: + if (time_after(jiffies, rb->sched_timer + + msecs_to_jiffies( + adreno_dispatch_starvation_time))) { + rb->starve_timer_state = + ADRENO_DISPATCHER_RB_STARVE_TIMER_ELAPSED; + /* halt dispatcher to remove starvation */ + adreno_get_gpu_halt(adreno_dev); + } + break; + case ADRENO_DISPATCHER_RB_STARVE_TIMER_SCHEDULED: + /* + * If the RB has not been running for the minimum + * time slice then allow it to run + */ + if (!adreno_rb_empty(rb) && time_before(jiffies, + adreno_dev->cur_rb->sched_timer + + msecs_to_jiffies(adreno_dispatch_time_slice))) + next = rb; + else + rb->starve_timer_state = + ADRENO_DISPATCHER_RB_STARVE_TIMER_UNINIT; + break; + case ADRENO_DISPATCHER_RB_STARVE_TIMER_ELAPSED: + default: + break; + } + } + + return next; +} + +static void a4xx_preempt_clear_state(struct adreno_device *adreno_dev) + +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct adreno_ringbuffer *highest_busy_rb; + int switch_low_to_high; + int ret; + + /* Device not awake means there is nothing to do */ + if (!kgsl_state_is_awake(device)) + return; + + highest_busy_rb = a4xx_next_ringbuffer(adreno_dev); + if (!highest_busy_rb || highest_busy_rb == adreno_dev->cur_rb) + return; + + switch_low_to_high = adreno_compare_prio_level( + highest_busy_rb->id, + adreno_dev->cur_rb->id); + + if (switch_low_to_high < 0) { + /* + * if switching to lower priority make sure that the rptr and + * wptr are equal, when the lower rb is not starved + */ + if (!adreno_rb_empty(adreno_dev->cur_rb)) + return; + /* + * switch to default context because when we switch back + * to higher context then its not known which pt will + * be current, so by making it default here the next + * commands submitted will set the right pt + */ + ret = adreno_drawctxt_switch(adreno_dev, + adreno_dev->cur_rb, + NULL, 0); + /* + * lower priority RB has to wait until space opens up in + * higher RB + */ + if (ret) { + KGSL_DRV_ERR(device, + "Unable to switch context to NULL: %d", + ret); + + return; + } + + adreno_writereg(adreno_dev, + ADRENO_REG_CP_PREEMPT_DISABLE, 1); + } + + /* + * setup registers to do the switch to highest priority RB + * which is not empty or may be starving away(poor thing) + */ + a4xx_preemption_start(adreno_dev, highest_busy_rb); + + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_TRIGGERED); + + adreno_dev->next_rb = highest_busy_rb; + mod_timer(&adreno_dev->preempt.timer, jiffies + + msecs_to_jiffies(ADRENO_PREEMPT_TIMEOUT)); + + trace_adreno_hw_preempt_clear_to_trig(adreno_dev->cur_rb, + adreno_dev->next_rb, + adreno_get_rptr(adreno_dev->cur_rb), + adreno_get_rptr(adreno_dev->next_rb)); + /* issue PREEMPT trigger */ + adreno_writereg(adreno_dev, ADRENO_REG_CP_PREEMPT, 1); + + /* submit preempt token packet to ensure preemption */ + if (switch_low_to_high < 0) { + ret = a4xx_submit_preempt_token( + adreno_dev->cur_rb, adreno_dev->next_rb); + KGSL_DRV_ERR(device, + "Unable to submit preempt token: %d\n", ret); + adreno_dev->preempt.token_submit = true; + adreno_dev->cur_rb->wptr_preempt_end = adreno_dev->cur_rb->wptr; + } else { + adreno_dev->preempt.token_submit = false; + adreno_dispatcher_schedule(device); + adreno_dev->cur_rb->wptr_preempt_end = 0xFFFFFFFF; + } +} + +static void a4xx_preempt_complete_state(struct adreno_device *adreno_dev) + +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int wptr, rbbase; + unsigned int val, val1; + unsigned int prevrptr; + + del_timer_sync(&adreno_dev->preempt.timer); + + adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT, &val); + adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT_DEBUG, &val1); + + if (val || !val1) { + KGSL_DRV_ERR(device, + "Invalid state after preemption CP_PREEMPT: %08x, CP_PREEMPT_DEBUG: %08x\n", + val, val1); + adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); + adreno_dispatcher_schedule(device); + return; + } + adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_BASE, &rbbase); + if (rbbase != adreno_dev->next_rb->buffer_desc.gpuaddr) { + KGSL_DRV_ERR(device, + "RBBASE incorrect after preemption, expected %x got %016llx\b", + rbbase, + adreno_dev->next_rb->buffer_desc.gpuaddr); + adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); + adreno_dispatcher_schedule(device); + return; + } + + a4xx_preemption_save(adreno_dev, adreno_dev->cur_rb); + + /* new RB is the current RB */ + trace_adreno_hw_preempt_comp_to_clear(adreno_dev->next_rb, + adreno_dev->cur_rb, + adreno_get_rptr(adreno_dev->next_rb), + adreno_get_rptr(adreno_dev->cur_rb)); + + adreno_dev->prev_rb = adreno_dev->cur_rb; + adreno_dev->cur_rb = adreno_dev->next_rb; + adreno_dev->cur_rb->preempted_midway = 0; + adreno_dev->cur_rb->wptr_preempt_end = 0xFFFFFFFF; + adreno_dev->next_rb = NULL; + + if (adreno_disp_preempt_fair_sched) { + /* starved rb is now scheduled so unhalt dispatcher */ + if (ADRENO_DISPATCHER_RB_STARVE_TIMER_ELAPSED == + adreno_dev->cur_rb->starve_timer_state) + adreno_put_gpu_halt(adreno_dev); + adreno_dev->cur_rb->starve_timer_state = + ADRENO_DISPATCHER_RB_STARVE_TIMER_SCHEDULED; + adreno_dev->cur_rb->sched_timer = jiffies; + /* + * If the outgoing RB is has commands then set the + * busy time for it + */ + if (!adreno_rb_empty(adreno_dev->prev_rb)) { + adreno_dev->prev_rb->starve_timer_state = + ADRENO_DISPATCHER_RB_STARVE_TIMER_INIT; + adreno_dev->prev_rb->sched_timer = jiffies; + } else { + adreno_dev->prev_rb->starve_timer_state = + ADRENO_DISPATCHER_RB_STARVE_TIMER_UNINIT; + } + } + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); + + prevrptr = adreno_get_rptr(adreno_dev->prev_rb); + + if (adreno_compare_prio_level(adreno_dev->prev_rb->id, + adreno_dev->cur_rb->id) < 0) { + if (adreno_dev->prev_rb->wptr_preempt_end != prevrptr) + adreno_dev->prev_rb->preempted_midway = 1; + } + + /* submit wptr if required for new rb */ + adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_WPTR, &wptr); + if (adreno_dev->cur_rb->wptr != wptr) { + kgsl_pwrscale_busy(device); + adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, + adreno_dev->cur_rb->wptr); + } + /* clear preemption register */ + adreno_writereg(adreno_dev, ADRENO_REG_CP_PREEMPT_DEBUG, 0); +} + +void a4xx_preemption_schedule(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + + if (!adreno_is_preemption_enabled(adreno_dev)) + return; + + mutex_lock(&device->mutex); + + switch (atomic_read(&adreno_dev->preempt.state)) { + case ADRENO_PREEMPT_NONE: + a4xx_preempt_clear_state(adreno_dev); + break; + case ADRENO_PREEMPT_TRIGGERED: + a4xx_preempt_trig_state(adreno_dev); + /* + * if we transitioned to next state then fall-through + * processing to next state + */ + if (!adreno_in_preempt_state(adreno_dev, + ADRENO_PREEMPT_COMPLETE)) + break; + case ADRENO_PREEMPT_COMPLETE: + a4xx_preempt_complete_state(adreno_dev); + break; + default: + break; + } + + mutex_unlock(&device->mutex); +} + +int a4xx_preemption_init(struct adreno_device *adreno_dev) +{ + setup_timer(&adreno_dev->preempt.timer, a4xx_preemption_timer, + (unsigned long) adreno_dev); + + return 0; +} diff --git a/drivers/gpu/msm/adreno_a4xx_snapshot.c b/drivers/gpu/msm/adreno_a4xx_snapshot.c index b07e970aae32..6921af5c0ab5 100644 --- a/drivers/gpu/msm/adreno_a4xx_snapshot.c +++ b/drivers/gpu/msm/adreno_a4xx_snapshot.c @@ -534,9 +534,6 @@ void a4xx_snapshot(struct adreno_device *adreno_dev, kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL, 0); kgsl_regwrite(device, A4XX_RBBM_CLOCK_CTL2, 0); - /* Turn on MMU clocks since we read MMU registers */ - kgsl_mmu_enable_clk(&device->mmu); - /* Master set of (non debug) registers */ SNAPSHOT_REGISTERS(device, snapshot, a4xx_registers); @@ -554,8 +551,6 @@ void a4xx_snapshot(struct adreno_device *adreno_dev, a4xx_vbif_snapshot_registers, ARRAY_SIZE(a4xx_vbif_snapshot_registers)); - kgsl_mmu_disable_clk(&device->mmu); - kgsl_snapshot_indexed_registers(device, snapshot, A4XX_CP_STATE_DEBUG_INDEX, A4XX_CP_STATE_DEBUG_DATA, 0, snap_data->sect_sizes->cp_pfp); diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index 512dcd483f45..96f72c59e4cd 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.c @@ -60,19 +60,12 @@ static const struct adreno_vbif_platform a5xx_vbif_platforms[] = { { adreno_is_a506, a530_vbif }, }; -#define PREEMPT_RECORD(_field) \ - offsetof(struct a5xx_cp_preemption_record, _field) - -#define PREEMPT_SMMU_RECORD(_field) \ - offsetof(struct a5xx_cp_smmu_info, _field) - static void a5xx_irq_storm_worker(struct work_struct *work); static int _read_fw2_block_header(uint32_t *header, uint32_t id, uint32_t major, uint32_t minor); static void a5xx_gpmu_reset(struct work_struct *work); static int a5xx_gpmu_init(struct adreno_device *adreno_dev); - /** * Number of times to check if the regulator enabled before * giving up and returning failure. @@ -108,8 +101,9 @@ static void spin_idle_debug(struct kgsl_device *device, kgsl_regread(device, A5XX_CP_HW_FAULT, &hwfault); dev_err(device->dev, - " rb=%X/%X rbbm_status=%8.8X/%8.8X int_0_status=%8.8X\n", - rptr, wptr, status, status3, intstatus); + "rb=%d pos=%X/%X rbbm_status=%8.8X/%8.8X int_0_status=%8.8X\n", + adreno_dev->cur_rb->id, rptr, wptr, status, status3, intstatus); + dev_err(device->dev, " hwfault=%8.8X\n", hwfault); kgsl_device_snapshot(device, NULL); @@ -179,277 +173,6 @@ static void a5xx_check_features(struct adreno_device *adreno_dev) adreno_efuse_unmap(adreno_dev); } -/* - * a5xx_preemption_start() - Setup state to start preemption - */ -static void a5xx_preemption_start(struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct kgsl_iommu *iommu = KGSL_IOMMU_PRIV(device); - uint64_t ttbr0; - uint32_t contextidr; - struct kgsl_pagetable *pt; - bool switch_default_pt = true; - - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(wptr), rb->wptr); - kgsl_regwrite(device, A5XX_CP_CONTEXT_SWITCH_RESTORE_ADDR_LO, - lower_32_bits(rb->preemption_desc.gpuaddr)); - kgsl_regwrite(device, A5XX_CP_CONTEXT_SWITCH_RESTORE_ADDR_HI, - upper_32_bits(rb->preemption_desc.gpuaddr)); - kgsl_sharedmem_readq(&rb->pagetable_desc, &ttbr0, - offsetof(struct adreno_ringbuffer_pagetable_info, ttbr0)); - kgsl_sharedmem_readl(&rb->pagetable_desc, &contextidr, - offsetof(struct adreno_ringbuffer_pagetable_info, contextidr)); - - spin_lock(&kgsl_driver.ptlock); - list_for_each_entry(pt, &kgsl_driver.pagetable_list, list) { - if (kgsl_mmu_pagetable_get_ttbr0(pt) == ttbr0) { - switch_default_pt = false; - break; - } - } - spin_unlock(&kgsl_driver.ptlock); - - if (switch_default_pt) { - ttbr0 = kgsl_mmu_pagetable_get_ttbr0( - device->mmu.defaultpagetable); - contextidr = kgsl_mmu_pagetable_get_contextidr( - device->mmu.defaultpagetable); - } - - kgsl_sharedmem_writeq(device, &iommu->smmu_info, - offsetof(struct a5xx_cp_smmu_info, ttbr0), ttbr0); - kgsl_sharedmem_writel(device, &iommu->smmu_info, - offsetof(struct a5xx_cp_smmu_info, context_idr), contextidr); -} - -/* - * a5xx_preemption_save() - Save the state after preemption is done - */ -static void a5xx_preemption_save(struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb) -{ - /* save the rptr from ctxrecord here */ - kgsl_sharedmem_readl(&rb->preemption_desc, &rb->rptr, - PREEMPT_RECORD(rptr)); -} - -#ifdef CONFIG_QCOM_KGSL_IOMMU -static int a5xx_preemption_iommu_init(struct adreno_device *adreno_dev) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct kgsl_iommu *iommu = KGSL_IOMMU_PRIV(device); - - /* Allocate mem for storing preemption smmu record */ - return kgsl_allocate_global(device, &iommu->smmu_info, PAGE_SIZE, - KGSL_MEMFLAGS_GPUREADONLY, KGSL_MEMDESC_PRIVILEGED); -} -#else -static int a5xx_preemption_iommu_init(struct adreno_device *adreno_dev) -{ - return -ENODEV; -} -#endif - -static int a5xx_preemption_init(struct adreno_device *adreno_dev) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct adreno_ringbuffer *rb; - int ret; - unsigned int i; - uint64_t addr; - - /* We are dependent on IOMMU to make preemption go on the CP side */ - if (kgsl_mmu_get_mmutype(device) != KGSL_MMU_TYPE_IOMMU) - return -ENODEV; - - /* Allocate mem for storing preemption counters */ - ret = kgsl_allocate_global(device, &adreno_dev->preemption_counters, - adreno_dev->num_ringbuffers * - A5XX_CP_CTXRECORD_PREEMPTION_COUNTER_SIZE, 0, 0); - if (ret) - return ret; - - addr = adreno_dev->preemption_counters.gpuaddr; - - /* Allocate mem for storing preemption switch record */ - FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { - ret = kgsl_allocate_global(device, - &rb->preemption_desc, A5XX_CP_CTXRECORD_SIZE_IN_BYTES, - 0, KGSL_MEMDESC_PRIVILEGED); - if (ret) - return ret; - - /* Initialize the context switch record here */ - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(magic), A5XX_CP_CTXRECORD_MAGIC_REF); - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(info), 0); - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(data), 0); - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(cntl), 0x0800000C); - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(rptr), 0); - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(wptr), 0); - kgsl_sharedmem_writeq(device, &rb->preemption_desc, - PREEMPT_RECORD(rbase), - adreno_dev->ringbuffers[i].buffer_desc.gpuaddr); - kgsl_sharedmem_writeq(device, &rb->preemption_desc, - PREEMPT_RECORD(counter), addr); - - addr += A5XX_CP_CTXRECORD_PREEMPTION_COUNTER_SIZE; - } - - return a5xx_preemption_iommu_init(adreno_dev); -} - -/* - * a5xx_preemption_token() - Preempt token on a5xx - * PM4 commands for preempt token on a5xx. These commands are - * submitted to ringbuffer to trigger preemption. - */ -static int a5xx_preemption_token(struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb, unsigned int *cmds, - uint64_t gpuaddr) -{ - unsigned int *cmds_orig = cmds; - - *cmds++ = cp_type7_packet(CP_CONTEXT_SWITCH_YIELD, 4); - cmds += cp_gpuaddr(adreno_dev, cmds, gpuaddr); - *cmds++ = 1; - /* generate interrupt on preemption completion */ - *cmds++ = 1; - - return cmds - cmds_orig; - -} - -/* - * a5xx_preemption_pre_ibsubmit() - Below PM4 commands are - * added at the beginning of every cmdbatch submission. - */ -static int a5xx_preemption_pre_ibsubmit( - struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb, unsigned int *cmds, - struct kgsl_context *context, uint64_t cond_addr, - struct kgsl_memobj_node *ib) -{ - unsigned int *cmds_orig = cmds; - uint64_t gpuaddr = rb->preemption_desc.gpuaddr; - unsigned int preempt_style = 0; - - if (context) { - /* - * Preemption from secure to unsecure needs Zap shader to be - * run to clear all secure content. CP does not know during - * preemption if it is switching between secure and unsecure - * contexts so restrict Secure contexts to be preempted at - * ringbuffer level. - */ - if (context->flags & KGSL_CONTEXT_SECURE) - preempt_style = KGSL_CONTEXT_PREEMPT_STYLE_RINGBUFFER; - else - preempt_style = ADRENO_PREEMPT_STYLE(context->flags); - } - - /* - * CP_PREEMPT_ENABLE_GLOBAL(global preemption) can only be set by KMD - * in ringbuffer. - * 1) set global preemption to 0x0 to disable global preemption. - * Only RB level preemption is allowed in this mode - * 2) Set global preemption to defer(0x2) for finegrain preemption. - * when global preemption is set to defer(0x2), - * CP_PREEMPT_ENABLE_LOCAL(local preemption) determines the - * preemption point. Local preemption - * can be enabled by both UMD(within IB) and KMD. - */ - *cmds++ = cp_type7_packet(CP_PREEMPT_ENABLE_GLOBAL, 1); - *cmds++ = ((preempt_style == KGSL_CONTEXT_PREEMPT_STYLE_FINEGRAIN) - ? 2 : 0); - - /* Turn CP protection OFF */ - *cmds++ = cp_type7_packet(CP_SET_PROTECTED_MODE, 1); - *cmds++ = 0; - - /* - * CP during context switch will save context switch info to - * a5xx_cp_preemption_record pointed by CONTEXT_SWITCH_SAVE_ADDR - */ - *cmds++ = cp_type4_packet(A5XX_CP_CONTEXT_SWITCH_SAVE_ADDR_LO, 1); - *cmds++ = lower_32_bits(gpuaddr); - *cmds++ = cp_type4_packet(A5XX_CP_CONTEXT_SWITCH_SAVE_ADDR_HI, 1); - *cmds++ = upper_32_bits(gpuaddr); - - /* Turn CP protection ON */ - *cmds++ = cp_type7_packet(CP_SET_PROTECTED_MODE, 1); - *cmds++ = 1; - - /* - * Enable local preemption for finegrain preemption in case of - * a misbehaving IB - */ - if (preempt_style == KGSL_CONTEXT_PREEMPT_STYLE_FINEGRAIN) { - *cmds++ = cp_type7_packet(CP_PREEMPT_ENABLE_LOCAL, 1); - *cmds++ = 1; - } else { - *cmds++ = cp_type7_packet(CP_PREEMPT_ENABLE_LOCAL, 1); - *cmds++ = 0; - } - - /* Enable CP_CONTEXT_SWITCH_YIELD packets in the IB2s */ - *cmds++ = cp_type7_packet(CP_YIELD_ENABLE, 1); - *cmds++ = 2; - - return cmds - cmds_orig; -} - -/* - * a5xx_preemption_yield_enable() - Below PM4 commands are - * added after every cmdbatch submission. - */ -static int a5xx_preemption_yield_enable(unsigned int *cmds) -{ - /* - * SRM -- set render mode (ex binning, direct render etc) - * SRM is set by UMD usually at start of IB to tell CP the type of - * preemption. - * KMD needs to set SRM to NULL to indicate CP that rendering is - * done by IB. - */ - *cmds++ = cp_type7_packet(CP_SET_RENDER_MODE, 5); - *cmds++ = 0; - *cmds++ = 0; - *cmds++ = 0; - *cmds++ = 0; - *cmds++ = 0; - - *cmds++ = cp_type7_packet(CP_YIELD_ENABLE, 1); - *cmds++ = 1; - - return 8; -} - -/* - * a5xx_preemption_post_ibsubmit() - Below PM4 commands are - * added after every cmdbatch submission. - */ -static int a5xx_preemption_post_ibsubmit(struct adreno_device *adreno_dev, - struct adreno_ringbuffer *rb, unsigned int *cmds, - struct kgsl_context *context) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - unsigned int ctx_id = context ? context->id : 0; - - return a5xx_preemption_token(adreno_dev, rb, cmds, - device->memstore.gpuaddr + - KGSL_MEMSTORE_OFFSET(ctx_id, preempted)); - -} - static void a5xx_platform_setup(struct adreno_device *adreno_dev) { uint64_t addr; @@ -1972,12 +1695,8 @@ out: static void a5xx_start(struct adreno_device *adreno_dev) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct kgsl_iommu *iommu = KGSL_IOMMU_PRIV(device); struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); - unsigned int i, bit; - struct adreno_ringbuffer *rb; - uint64_t def_ttbr0; - uint32_t contextidr; + unsigned int bit; adreno_vbif_start(adreno_dev, a5xx_vbif_platforms, ARRAY_SIZE(a5xx_vbif_platforms)); @@ -2178,58 +1897,21 @@ static void a5xx_start(struct adreno_device *adreno_dev) } - if (adreno_is_preemption_enabled(adreno_dev)) { - struct kgsl_pagetable *pt = device->mmu.defaultpagetable; - - def_ttbr0 = kgsl_mmu_pagetable_get_ttbr0(pt); - contextidr = kgsl_mmu_pagetable_get_contextidr(pt); - - /* Initialize the context switch record here */ - kgsl_sharedmem_writel(device, &iommu->smmu_info, - PREEMPT_SMMU_RECORD(magic), - A5XX_CP_SMMU_INFO_MAGIC_REF); - kgsl_sharedmem_writeq(device, &iommu->smmu_info, - PREEMPT_SMMU_RECORD(ttbr0), def_ttbr0); - /* - * The CP doesn't actually use the asid field, so - * put a bad value into it until it is removed from - * the preemption record. - */ - kgsl_sharedmem_writeq(device, &iommu->smmu_info, - PREEMPT_SMMU_RECORD(asid), - 0xdecafbad); - kgsl_sharedmem_writeq(device, &iommu->smmu_info, - PREEMPT_SMMU_RECORD(context_idr), - contextidr); - adreno_writereg64(adreno_dev, - ADRENO_REG_CP_CONTEXT_SWITCH_SMMU_INFO_LO, - ADRENO_REG_CP_CONTEXT_SWITCH_SMMU_INFO_HI, - iommu->smmu_info.gpuaddr); - - FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(rptr), 0); - kgsl_sharedmem_writel(device, &rb->preemption_desc, - PREEMPT_RECORD(wptr), 0); - kgsl_sharedmem_writeq(device, &rb->pagetable_desc, - offsetof(struct adreno_ringbuffer_pagetable_info, - ttbr0), def_ttbr0); - } - } - + a5xx_preemption_start(adreno_dev); a5xx_protect_init(adreno_dev); } +/* + * Follow the ME_INIT sequence with a preemption yield to allow the GPU to move + * to a different ringbuffer, if desired + */ static int _preemption_init( struct adreno_device *adreno_dev, struct adreno_ringbuffer *rb, unsigned int *cmds, struct kgsl_context *context) { - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); unsigned int *cmds_orig = cmds; uint64_t gpuaddr = rb->preemption_desc.gpuaddr; - uint64_t gpuaddr_token = device->memstore.gpuaddr + - KGSL_MEMSTORE_OFFSET(0, preempted); /* Turn CP protection OFF */ *cmds++ = cp_type7_packet(CP_SET_PROTECTED_MODE, 1); @@ -2258,8 +1940,8 @@ static int _preemption_init( *cmds++ = 1; *cmds++ = cp_type7_packet(CP_CONTEXT_SWITCH_YIELD, 4); - cmds += cp_gpuaddr(adreno_dev, cmds, gpuaddr_token); - *cmds++ = 1; + cmds += cp_gpuaddr(adreno_dev, cmds, 0x0); + *cmds++ = 0; /* generate interrupt on preemption completion */ *cmds++ = 1; @@ -2297,7 +1979,7 @@ static int a5xx_post_start(struct adreno_device *adreno_dev) if (adreno_is_preemption_enabled(adreno_dev)) cmds += _preemption_init(adreno_dev, rb, cmds, NULL); - rb->wptr = rb->wptr - (42 - (cmds - start)); + rb->_wptr = rb->_wptr - (42 - (cmds - start)); ret = adreno_ringbuffer_submit_spin(rb, NULL, 2000); if (ret) @@ -2595,8 +2277,15 @@ static int a5xx_rb_start(struct adreno_device *adreno_dev, unsigned int start_type) { struct adreno_ringbuffer *rb = ADRENO_CURRENT_RINGBUFFER(adreno_dev); + struct kgsl_device *device = &adreno_dev->dev; + uint64_t addr; int ret; + addr = SCRATCH_RPTR_GPU_ADDR(device, rb->id); + + adreno_writereg64(adreno_dev, ADRENO_REG_CP_RB_RPTR_ADDR_LO, + ADRENO_REG_CP_RB_RPTR_ADDR_HI, addr); + /* * The size of the ringbuffer in the hardware is the log2 * representation of the size in quadwords (sizedwords / 2). @@ -2605,8 +2294,7 @@ static int a5xx_rb_start(struct adreno_device *adreno_dev, */ adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_CNTL, - (ilog2(KGSL_RB_DWORDS >> 1) & 0x3F) | - (1 << 27)); + A5XX_CP_RB_CNTL_DEFAULT); adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_BASE, rb->buffer_desc.gpuaddr); @@ -3147,6 +2835,10 @@ static unsigned int a5xx_register_offsets[ADRENO_REG_REGISTER_MAX] = { ADRENO_REG_DEFINE(ADRENO_REG_CP_WFI_PEND_CTR, A5XX_CP_WFI_PEND_CTR), ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_BASE, A5XX_CP_RB_BASE), ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_BASE_HI, A5XX_CP_RB_BASE_HI), + ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_RPTR_ADDR_LO, + A5XX_CP_RB_RPTR_ADDR_LO), + ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_RPTR_ADDR_HI, + A5XX_CP_RB_RPTR_ADDR_HI), ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_RPTR, A5XX_CP_RB_RPTR), ADRENO_REG_DEFINE(ADRENO_REG_CP_RB_WPTR, A5XX_CP_RB_WPTR), ADRENO_REG_DEFINE(ADRENO_REG_CP_CNTL, A5XX_CP_CNTL), @@ -3416,6 +3108,8 @@ static void a5xx_cp_callback(struct adreno_device *adreno_dev, int bit) prev = cur; } + a5xx_preemption_trigger(adreno_dev); + kgsl_schedule_work(&device->event_work); adreno_dispatcher_schedule(device); } @@ -3500,9 +3194,6 @@ void a5x_gpc_err_int_callback(struct adreno_device *adreno_dev, int bit) (1 << A5XX_INT_RBBM_ATB_ASYNC_OVERFLOW) | \ (1 << A5XX_INT_RBBM_GPC_ERROR) | \ (1 << A5XX_INT_CP_HW_ERROR) | \ - (1 << A5XX_INT_CP_IB1) | \ - (1 << A5XX_INT_CP_IB2) | \ - (1 << A5XX_INT_CP_RB) | \ (1 << A5XX_INT_CP_CACHE_FLUSH_TS) | \ (1 << A5XX_INT_RBBM_ATB_BUS_OVERFLOW) | \ (1 << A5XX_INT_UCHE_OOB_ACCESS) | \ @@ -3525,7 +3216,7 @@ static struct adreno_irq_funcs a5xx_irq_funcs[32] = { /* 6 - RBBM_ATB_ASYNC_OVERFLOW */ ADRENO_IRQ_CALLBACK(a5xx_err_callback), ADRENO_IRQ_CALLBACK(a5x_gpc_err_int_callback), /* 7 - GPC_ERR */ - ADRENO_IRQ_CALLBACK(adreno_dispatcher_preempt_callback),/* 8 - CP_SW */ + ADRENO_IRQ_CALLBACK(a5xx_preempt_callback),/* 8 - CP_SW */ ADRENO_IRQ_CALLBACK(a5xx_cp_hw_err_callback), /* 9 - CP_HW_ERROR */ /* 10 - CP_CCU_FLUSH_DEPTH_TS */ ADRENO_IRQ_CALLBACK(NULL), @@ -3533,9 +3224,9 @@ static struct adreno_irq_funcs a5xx_irq_funcs[32] = { ADRENO_IRQ_CALLBACK(NULL), /* 12 - CP_CCU_RESOLVE_TS */ ADRENO_IRQ_CALLBACK(NULL), - ADRENO_IRQ_CALLBACK(adreno_cp_callback), /* 13 - CP_IB2_INT */ - ADRENO_IRQ_CALLBACK(adreno_cp_callback), /* 14 - CP_IB1_INT */ - ADRENO_IRQ_CALLBACK(adreno_cp_callback), /* 15 - CP_RB_INT */ + ADRENO_IRQ_CALLBACK(NULL), /* 13 - CP_IB2_INT */ + ADRENO_IRQ_CALLBACK(NULL), /* 14 - CP_IB1_INT */ + ADRENO_IRQ_CALLBACK(NULL), /* 15 - CP_RB_INT */ /* 16 - CCP_UNUSED_1 */ ADRENO_IRQ_CALLBACK(NULL), ADRENO_IRQ_CALLBACK(NULL), /* 17 - CP_RB_DONE_TS */ @@ -3772,323 +3463,6 @@ static struct adreno_coresight a5xx_coresight = { .groups = a5xx_coresight_groups, }; -/** - * a5xx_preempt_trig_state() - Schedule preemption in TRIGGERRED - * state - * @adreno_dev: Device which is in TRIGGERRED state - */ -static void a5xx_preempt_trig_state( - struct adreno_device *adreno_dev) -{ - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - unsigned int preempt_busy; - uint64_t rbbase; - - /* - * triggered preemption, check for busy bits, if not set go to complete - * bit 0: When high indicates CP is not done with preemption. - * bit 4: When high indicates that the CP is actively switching between - * application contexts. - * Check both the bits to make sure CP is done with preemption. - */ - adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT, &preempt_busy); - if (!(preempt_busy & 0x11)) { - - adreno_readreg64(adreno_dev, ADRENO_REG_CP_RB_BASE, - ADRENO_REG_CP_RB_BASE_HI, &rbbase); - /* Did preemption occur, if so then change states and return */ - if (rbbase != adreno_dev->cur_rb->buffer_desc.gpuaddr) { - if (rbbase == - adreno_dev->next_rb->buffer_desc.gpuaddr) { - KGSL_DRV_INFO(device, - "Preemption completed without interrupt\n"); - trace_adreno_hw_preempt_trig_to_comp( - adreno_dev->cur_rb, - adreno_dev->next_rb); - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_COMPLETE); - } else { - /* - * Something wrong with preemption. - * Set fault and reschedule dispatcher to take - * care of fault. - */ - adreno_set_gpu_fault(adreno_dev, - ADRENO_PREEMPT_FAULT); - } - adreno_dispatcher_schedule(device); - return; - } - } - - /* - * Preemption is still happening. - * Hardware not yet idle means that preemption interrupt - * may still occur, nothing to do here until interrupt signals - * completion of preemption, just return here - */ - if (!adreno_hw_isidle(adreno_dev)) - return; - - /* - * We just changed states, reschedule dispatcher to change - * preemption states - */ - if (ADRENO_DISPATCHER_PREEMPT_TRIGGERED != - atomic_read(&dispatcher->preemption_state)) { - adreno_dispatcher_schedule(device); - return; - } - - - adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); - - /* reschedule dispatcher to take care of the fault */ - adreno_dispatcher_schedule(device); -} - -/** - * a5xx_preempt_clear_state() - Schedule preemption in CLEAR - * state. Preemption can be issued in this state. - * @adreno_dev: Device which is in CLEAR state - */ -static void a5xx_preempt_clear_state( - struct adreno_device *adreno_dev) - -{ - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct adreno_ringbuffer *highest_busy_rb; - int switch_low_to_high; - int ret; - - /* Device not awake means there is nothing to do */ - if (!kgsl_state_is_awake(device)) - return; - - /* keep updating the current rptr when preemption is clear */ - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_RPTR, - &(adreno_dev->cur_rb->rptr)); - - highest_busy_rb = adreno_dispatcher_get_highest_busy_rb(adreno_dev); - if (!highest_busy_rb) - return; - - switch_low_to_high = adreno_compare_prio_level( - highest_busy_rb->id, adreno_dev->cur_rb->id); - - /* already current then return */ - if (!switch_low_to_high) - return; - - if (switch_low_to_high < 0) { - - if (!adreno_hw_isidle(adreno_dev)) { - adreno_dispatcher_schedule(device); - return; - } - - /* - * if switching to lower priority make sure that the rptr and - * wptr are equal, when the lower rb is not starved - */ - if (adreno_dev->cur_rb->rptr != adreno_dev->cur_rb->wptr) - return; - /* - * switch to default context because when we switch back - * to higher context then its not known which pt will - * be current, so by making it default here the next - * commands submitted will set the right pt - */ - ret = adreno_drawctxt_switch(adreno_dev, - adreno_dev->cur_rb, - NULL, 0); - /* - * lower priority RB has to wait until space opens up in - * higher RB - */ - if (ret) - return; - } - - /* rptr could be updated in drawctxt switch above, update it here */ - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_RPTR, - &(adreno_dev->cur_rb->rptr)); - - /* turn on IOMMU as the preemption may trigger pt switch */ - kgsl_mmu_enable_clk(&device->mmu); - - /* - * setup memory to do the switch to highest priority RB - * which is not empty or may be starving away(poor thing) - */ - a5xx_preemption_start(adreno_dev, highest_busy_rb); - - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_TRIGGERED); - - adreno_dev->next_rb = highest_busy_rb; - mod_timer(&dispatcher->preempt_timer, jiffies + - msecs_to_jiffies(ADRENO_DISPATCH_PREEMPT_TIMEOUT)); - - trace_adreno_hw_preempt_clear_to_trig(adreno_dev->cur_rb, - adreno_dev->next_rb); - /* issue PREEMPT trigger */ - adreno_writereg(adreno_dev, ADRENO_REG_CP_PREEMPT, 1); - - adreno_dispatcher_schedule(device); -} - -/** - * a5xx_preempt_complete_state() - Schedule preemption in - * COMPLETE state - * @adreno_dev: Device which is in COMPLETE state - */ -static void a5xx_preempt_complete_state( - struct adreno_device *adreno_dev) - -{ - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct adreno_dispatcher_cmdqueue *dispatch_q; - uint64_t rbbase; - unsigned int wptr; - unsigned int val; - static unsigned long wait_for_preemption_complete; - - del_timer_sync(&dispatcher->preempt_timer); - - adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT, &val); - - if (val) { - /* - * Wait for 50ms for preemption state to be updated by CP - * before triggering hang - */ - if (wait_for_preemption_complete == 0) - wait_for_preemption_complete = jiffies + - msecs_to_jiffies(50); - if (time_after(jiffies, wait_for_preemption_complete)) { - wait_for_preemption_complete = 0; - KGSL_DRV_ERR(device, - "Invalid state after preemption CP_PREEMPT:%08x STOP:%1x BUSY:%1x\n", - val, (val & 0x1), (val & 0x10)>>4); - adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); - } - adreno_dispatcher_schedule(device); - return; - } - - wait_for_preemption_complete = 0; - adreno_readreg64(adreno_dev, ADRENO_REG_CP_RB_BASE, - ADRENO_REG_CP_RB_BASE_HI, &rbbase); - if (rbbase != adreno_dev->next_rb->buffer_desc.gpuaddr) { - KGSL_DRV_ERR(device, - "RBBASE incorrect after preemption, expected %016llx got %016llx\b", - rbbase, - adreno_dev->next_rb->buffer_desc.gpuaddr); - adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); - adreno_dispatcher_schedule(device); - return; - } - - a5xx_preemption_save(adreno_dev, adreno_dev->cur_rb); - - dispatch_q = &(adreno_dev->cur_rb->dispatch_q); - /* new RB is the current RB */ - trace_adreno_hw_preempt_comp_to_clear(adreno_dev->next_rb, - adreno_dev->cur_rb); - adreno_dev->prev_rb = adreno_dev->cur_rb; - adreno_dev->cur_rb = adreno_dev->next_rb; - adreno_dev->cur_rb->preempted_midway = 0; - adreno_dev->cur_rb->wptr_preempt_end = 0xFFFFFFFF; - adreno_dev->next_rb = NULL; - - if (adreno_disp_preempt_fair_sched) { - /* starved rb is now scheduled so unhalt dispatcher */ - if (ADRENO_DISPATCHER_RB_STARVE_TIMER_ELAPSED == - adreno_dev->cur_rb->starve_timer_state) - adreno_put_gpu_halt(adreno_dev); - adreno_dev->cur_rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_SCHEDULED; - adreno_dev->cur_rb->sched_timer = jiffies; - /* - * If the outgoing RB is has commands then set the - * busy time for it - */ - if (adreno_dev->prev_rb->rptr != adreno_dev->prev_rb->wptr) { - adreno_dev->prev_rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_INIT; - adreno_dev->prev_rb->sched_timer = jiffies; - } else { - adreno_dev->prev_rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_UNINIT; - } - } - adreno_ringbuffer_mmu_disable_clk_on_ts(device, adreno_dev->cur_rb, - adreno_dev->cur_rb->timestamp); - - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_CLEAR); - - /* submit wptr if required for new rb */ - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_WPTR, &wptr); - if (adreno_dev->cur_rb->wptr != wptr) { - kgsl_pwrscale_busy(device); - adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, - adreno_dev->cur_rb->wptr); - } - - adreno_preempt_process_dispatch_queue(adreno_dev, dispatch_q); -} - -static void a5xx_preemption_schedule( - struct adreno_device *adreno_dev) -{ - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct adreno_ringbuffer *rb; - int i = 0; - - if (!adreno_is_preemption_enabled(adreno_dev)) - return; - - mutex_lock(&device->mutex); - - /* - * This barrier is needed for most updated preemption_state - * to be read. - */ - smp_mb(); - - if (KGSL_STATE_ACTIVE == device->state) - FOR_EACH_RINGBUFFER(adreno_dev, rb, i) - rb->rptr = adreno_get_rptr(rb); - - switch (atomic_read(&dispatcher->preemption_state)) { - case ADRENO_DISPATCHER_PREEMPT_CLEAR: - a5xx_preempt_clear_state(adreno_dev); - break; - case ADRENO_DISPATCHER_PREEMPT_TRIGGERED: - a5xx_preempt_trig_state(adreno_dev); - /* - * if we transitioned to next state then fall-through - * processing to next state - */ - if (!adreno_preempt_state(adreno_dev, - ADRENO_DISPATCHER_PREEMPT_COMPLETE)) - break; - case ADRENO_DISPATCHER_PREEMPT_COMPLETE: - a5xx_preempt_complete_state(adreno_dev); - break; - default: - BUG(); - } - - mutex_unlock(&device->mutex); -} - struct adreno_gpudev adreno_a5xx_gpudev = { .reg_offsets = &a5xx_reg_offsets, .ft_perf_counters = a5xx_ft_perf_counters, @@ -4116,7 +3490,6 @@ struct adreno_gpudev adreno_a5xx_gpudev = { a5xx_preemption_yield_enable, .preemption_post_ibsubmit = a5xx_preemption_post_ibsubmit, - .preemption_token = a5xx_preemption_token, .preemption_init = a5xx_preemption_init, .preemption_schedule = a5xx_preemption_schedule, .enable_64bit = a5xx_enable_64bit, diff --git a/drivers/gpu/msm/adreno_a5xx.h b/drivers/gpu/msm/adreno_a5xx.h index 6ce95ff7bdbf..7965bb7b5440 100644 --- a/drivers/gpu/msm/adreno_a5xx.h +++ b/drivers/gpu/msm/adreno_a5xx.h @@ -112,6 +112,8 @@ void a5xx_crashdump_init(struct adreno_device *adreno_dev); void a5xx_hwcg_set(struct adreno_device *adreno_dev, bool on); +#define A5XX_CP_RB_CNTL_DEFAULT (((ilog2(4) << 8) & 0x1F00) | \ + (ilog2(KGSL_RB_DWORDS >> 1) & 0x3F)) /* GPMU interrupt multiplexor */ #define FW_INTR_INFO (0) #define LLM_ACK_ERR_INTR (1) @@ -232,4 +234,22 @@ static inline bool lm_on(struct adreno_device *adreno_dev) return ADRENO_FEATURE(adreno_dev, ADRENO_LM) && test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag); } + +/* Preemption functions */ +void a5xx_preemption_trigger(struct adreno_device *adreno_dev); +void a5xx_preemption_schedule(struct adreno_device *adreno_dev); +void a5xx_preemption_start(struct adreno_device *adreno_dev); +int a5xx_preemption_init(struct adreno_device *adreno_dev); +int a5xx_preemption_yield_enable(unsigned int *cmds); + +unsigned int a5xx_preemption_post_ibsubmit(struct adreno_device *adreno_dev, + unsigned int *cmds); +unsigned int a5xx_preemption_pre_ibsubmit( + struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb, + unsigned int *cmds, struct kgsl_context *context); + + +void a5xx_preempt_callback(struct adreno_device *adreno_dev, int bit); + #endif diff --git a/drivers/gpu/msm/adreno_a5xx_preempt.c b/drivers/gpu/msm/adreno_a5xx_preempt.c new file mode 100644 index 000000000000..c1463b824c67 --- /dev/null +++ b/drivers/gpu/msm/adreno_a5xx_preempt.c @@ -0,0 +1,574 @@ +/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "adreno.h" +#include "adreno_a5xx.h" +#include "a5xx_reg.h" +#include "adreno_trace.h" +#include "adreno_pm4types.h" + +#define PREEMPT_RECORD(_field) \ + offsetof(struct a5xx_cp_preemption_record, _field) + +#define PREEMPT_SMMU_RECORD(_field) \ + offsetof(struct a5xx_cp_smmu_info, _field) + +static void _update_wptr(struct adreno_device *adreno_dev) +{ + struct adreno_ringbuffer *rb = adreno_dev->cur_rb; + unsigned int wptr; + unsigned long flags; + + spin_lock_irqsave(&rb->preempt_lock, flags); + + adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_WPTR, &wptr); + + if (wptr != rb->wptr) { + adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, + rb->wptr); + + rb->dispatch_q.expires = jiffies + + msecs_to_jiffies(adreno_cmdbatch_timeout); + } + + spin_unlock_irqrestore(&rb->preempt_lock, flags); +} + +static inline bool adreno_move_preempt_state(struct adreno_device *adreno_dev, + enum adreno_preempt_states old, enum adreno_preempt_states new) +{ + return (atomic_cmpxchg(&adreno_dev->preempt.state, old, new) == old); +} + +static void _a5xx_preemption_done(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int status; + + /* + * In the very unlikely case that the power is off, do nothing - the + * state will be reset on power up and everybody will be happy + */ + + if (!kgsl_state_is_awake(device)) + return; + + adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT, &status); + + if (status != 0) { + KGSL_DRV_ERR(device, + "Preemption not complete: status=%X cur=%d R/W=%X/%X next=%d R/W=%X/%X\n", + status, adreno_dev->cur_rb->id, + adreno_get_rptr(adreno_dev->cur_rb), + adreno_dev->cur_rb->wptr, adreno_dev->next_rb->id, + adreno_get_rptr(adreno_dev->next_rb), + adreno_dev->next_rb->wptr); + + /* Set a fault and restart */ + adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); + adreno_dispatcher_schedule(device); + + return; + } + + del_timer_sync(&adreno_dev->preempt.timer); + + trace_adreno_preempt_done(adreno_dev->cur_rb, adreno_dev->next_rb); + + /* Clean up all the bits */ + adreno_dev->prev_rb = adreno_dev->cur_rb; + adreno_dev->cur_rb = adreno_dev->next_rb; + adreno_dev->next_rb = NULL; + + /* Update the wptr for the new command queue */ + _update_wptr(adreno_dev); + + /* Update the dispatcher timer for the new command queue */ + mod_timer(&adreno_dev->dispatcher.timer, + adreno_dev->cur_rb->dispatch_q.expires); + + /* Clear the preempt state */ + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); +} + +static void _a5xx_preemption_fault(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int status; + + /* + * If the power is on check the preemption status one more time - if it + * was successful then just transition to the complete state + */ + if (kgsl_state_is_awake(device)) { + adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT, &status); + + if (status == 0) { + adreno_set_preempt_state(adreno_dev, + ADRENO_PREEMPT_COMPLETE); + + adreno_dispatcher_schedule(device); + return; + } + } + + KGSL_DRV_ERR(device, + "Preemption timed out: cur=%d R/W=%X/%X, next=%d R/W=%X/%X\n", + adreno_dev->cur_rb->id, + adreno_get_rptr(adreno_dev->cur_rb), adreno_dev->cur_rb->wptr, + adreno_dev->next_rb->id, + adreno_get_rptr(adreno_dev->next_rb), + adreno_dev->next_rb->wptr); + + adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); + adreno_dispatcher_schedule(device); +} + +static void _a5xx_preemption_worker(struct work_struct *work) +{ + struct adreno_preemption *preempt = container_of(work, + struct adreno_preemption, work); + struct adreno_device *adreno_dev = container_of(preempt, + struct adreno_device, preempt); + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + + /* Need to take the mutex to make sure that the power stays on */ + mutex_lock(&device->mutex); + + if (adreno_in_preempt_state(adreno_dev, ADRENO_PREEMPT_FAULTED)) + _a5xx_preemption_fault(adreno_dev); + + mutex_unlock(&device->mutex); +} + +static void _a5xx_preemption_timer(unsigned long data) +{ + struct adreno_device *adreno_dev = (struct adreno_device *) data; + + /* We should only be here from a triggered state */ + if (!adreno_move_preempt_state(adreno_dev, + ADRENO_PREEMPT_TRIGGERED, ADRENO_PREEMPT_FAULTED)) + return; + + /* Schedule the worker to take care of the details */ + queue_work(system_unbound_wq, &adreno_dev->preempt.work); +} + +/* Find the highest priority active ringbuffer */ +static struct adreno_ringbuffer *a5xx_next_ringbuffer( + struct adreno_device *adreno_dev) +{ + struct adreno_ringbuffer *rb; + unsigned long flags; + unsigned int i; + + FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { + bool empty; + + spin_lock_irqsave(&rb->preempt_lock, flags); + empty = adreno_rb_empty(rb); + spin_unlock_irqrestore(&rb->preempt_lock, flags); + + if (empty == false) + return rb; + } + + return NULL; +} + +void a5xx_preemption_trigger(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct kgsl_iommu *iommu = KGSL_IOMMU_PRIV(device); + struct adreno_ringbuffer *next; + uint64_t ttbr0; + unsigned int contextidr; + unsigned long flags; + + /* Put ourselves into a possible trigger state */ + if (!adreno_move_preempt_state(adreno_dev, + ADRENO_PREEMPT_NONE, ADRENO_PREEMPT_START)) + return; + + /* Get the next ringbuffer to preempt in */ + next = a5xx_next_ringbuffer(adreno_dev); + + /* + * Nothing to do if every ringbuffer is empty or if the current + * ringbuffer is the only active one + */ + if (next == NULL || next == adreno_dev->cur_rb) { + /* + * Update any critical things that might have been skipped while + * we were looking for a new ringbuffer + */ + + if (next != NULL) { + _update_wptr(adreno_dev); + + mod_timer(&adreno_dev->dispatcher.timer, + adreno_dev->cur_rb->dispatch_q.expires); + } + + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); + return; + } + + /* Turn off the dispatcher timer */ + del_timer(&adreno_dev->dispatcher.timer); + + /* + * This is the most critical section - we need to take care not to race + * until we have programmed the CP for the switch + */ + + spin_lock_irqsave(&next->preempt_lock, flags); + + /* Get the pagetable from the pagetable info */ + kgsl_sharedmem_readq(&next->pagetable_desc, &ttbr0, + PT_INFO_OFFSET(ttbr0)); + kgsl_sharedmem_readl(&next->pagetable_desc, &contextidr, + PT_INFO_OFFSET(contextidr)); + + kgsl_sharedmem_writel(device, &next->preemption_desc, + PREEMPT_RECORD(wptr), next->wptr); + + spin_unlock_irqrestore(&next->preempt_lock, flags); + + /* And write it to the smmu info */ + kgsl_sharedmem_writeq(device, &iommu->smmu_info, + PREEMPT_SMMU_RECORD(ttbr0), ttbr0); + kgsl_sharedmem_writel(device, &iommu->smmu_info, + PREEMPT_SMMU_RECORD(context_idr), contextidr); + + kgsl_regwrite(device, A5XX_CP_CONTEXT_SWITCH_RESTORE_ADDR_LO, + lower_32_bits(next->preemption_desc.gpuaddr)); + kgsl_regwrite(device, A5XX_CP_CONTEXT_SWITCH_RESTORE_ADDR_HI, + upper_32_bits(next->preemption_desc.gpuaddr)); + + adreno_dev->next_rb = next; + + /* Start the timer to detect a stuck preemption */ + mod_timer(&adreno_dev->preempt.timer, + jiffies + msecs_to_jiffies(ADRENO_PREEMPT_TIMEOUT)); + + trace_adreno_preempt_trigger(adreno_dev->cur_rb, adreno_dev->next_rb); + + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_TRIGGERED); + + /* Trigger the preemption */ + adreno_writereg(adreno_dev, ADRENO_REG_CP_PREEMPT, 1); +} + +void a5xx_preempt_callback(struct adreno_device *adreno_dev, int bit) +{ + unsigned int status; + + if (!adreno_move_preempt_state(adreno_dev, + ADRENO_PREEMPT_TRIGGERED, ADRENO_PREEMPT_PENDING)) + return; + + adreno_readreg(adreno_dev, ADRENO_REG_CP_PREEMPT, &status); + + if (status != 0) { + KGSL_DRV_ERR(KGSL_DEVICE(adreno_dev), + "preempt interrupt with non-zero status: %X\n", status); + + /* + * Under the assumption that this is a race between the + * interrupt and the register, schedule the worker to clean up. + * If the status still hasn't resolved itself by the time we get + * there then we have to assume something bad happened + */ + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_COMPLETE); + adreno_dispatcher_schedule(KGSL_DEVICE(adreno_dev)); + return; + } + + del_timer(&adreno_dev->preempt.timer); + + trace_adreno_preempt_done(adreno_dev->cur_rb, + adreno_dev->next_rb); + + adreno_dev->prev_rb = adreno_dev->cur_rb; + adreno_dev->cur_rb = adreno_dev->next_rb; + adreno_dev->next_rb = NULL; + + /* Update the wptr if it changed while preemption was ongoing */ + _update_wptr(adreno_dev); + + /* Update the dispatcher timer for the new command queue */ + mod_timer(&adreno_dev->dispatcher.timer, + adreno_dev->cur_rb->dispatch_q.expires); + + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); +} + +void a5xx_preemption_schedule(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + + if (!adreno_is_preemption_enabled(adreno_dev)) + return; + + mutex_lock(&device->mutex); + + if (adreno_in_preempt_state(adreno_dev, ADRENO_PREEMPT_COMPLETE)) + _a5xx_preemption_done(adreno_dev); + + a5xx_preemption_trigger(adreno_dev); + + mutex_unlock(&device->mutex); +} + +unsigned int a5xx_preemption_pre_ibsubmit( + struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb, + unsigned int *cmds, struct kgsl_context *context) +{ + unsigned int *cmds_orig = cmds; + uint64_t gpuaddr = rb->preemption_desc.gpuaddr; + unsigned int preempt_style = 0; + + if (context) { + /* + * Preemption from secure to unsecure needs Zap shader to be + * run to clear all secure content. CP does not know during + * preemption if it is switching between secure and unsecure + * contexts so restrict Secure contexts to be preempted at + * ringbuffer level. + */ + if (context->flags & KGSL_CONTEXT_SECURE) + preempt_style = KGSL_CONTEXT_PREEMPT_STYLE_RINGBUFFER; + else + preempt_style = ADRENO_PREEMPT_STYLE(context->flags); + } + + /* + * CP_PREEMPT_ENABLE_GLOBAL(global preemption) can only be set by KMD + * in ringbuffer. + * 1) set global preemption to 0x0 to disable global preemption. + * Only RB level preemption is allowed in this mode + * 2) Set global preemption to defer(0x2) for finegrain preemption. + * when global preemption is set to defer(0x2), + * CP_PREEMPT_ENABLE_LOCAL(local preemption) determines the + * preemption point. Local preemption + * can be enabled by both UMD(within IB) and KMD. + */ + *cmds++ = cp_type7_packet(CP_PREEMPT_ENABLE_GLOBAL, 1); + *cmds++ = ((preempt_style == KGSL_CONTEXT_PREEMPT_STYLE_FINEGRAIN) + ? 2 : 0); + + /* Turn CP protection OFF */ + *cmds++ = cp_type7_packet(CP_SET_PROTECTED_MODE, 1); + *cmds++ = 0; + + /* + * CP during context switch will save context switch info to + * a5xx_cp_preemption_record pointed by CONTEXT_SWITCH_SAVE_ADDR + */ + *cmds++ = cp_type4_packet(A5XX_CP_CONTEXT_SWITCH_SAVE_ADDR_LO, 1); + *cmds++ = lower_32_bits(gpuaddr); + *cmds++ = cp_type4_packet(A5XX_CP_CONTEXT_SWITCH_SAVE_ADDR_HI, 1); + *cmds++ = upper_32_bits(gpuaddr); + + /* Turn CP protection ON */ + *cmds++ = cp_type7_packet(CP_SET_PROTECTED_MODE, 1); + *cmds++ = 1; + + /* + * Enable local preemption for finegrain preemption in case of + * a misbehaving IB + */ + if (preempt_style == KGSL_CONTEXT_PREEMPT_STYLE_FINEGRAIN) { + *cmds++ = cp_type7_packet(CP_PREEMPT_ENABLE_LOCAL, 1); + *cmds++ = 1; + } else { + *cmds++ = cp_type7_packet(CP_PREEMPT_ENABLE_LOCAL, 1); + *cmds++ = 0; + } + + /* Enable CP_CONTEXT_SWITCH_YIELD packets in the IB2s */ + *cmds++ = cp_type7_packet(CP_YIELD_ENABLE, 1); + *cmds++ = 2; + + return (unsigned int) (cmds - cmds_orig); +} + +int a5xx_preemption_yield_enable(unsigned int *cmds) +{ + /* + * SRM -- set render mode (ex binning, direct render etc) + * SRM is set by UMD usually at start of IB to tell CP the type of + * preemption. + * KMD needs to set SRM to NULL to indicate CP that rendering is + * done by IB. + */ + *cmds++ = cp_type7_packet(CP_SET_RENDER_MODE, 5); + *cmds++ = 0; + *cmds++ = 0; + *cmds++ = 0; + *cmds++ = 0; + *cmds++ = 0; + + *cmds++ = cp_type7_packet(CP_YIELD_ENABLE, 1); + *cmds++ = 1; + + return 8; +} + +unsigned int a5xx_preemption_post_ibsubmit(struct adreno_device *adreno_dev, + unsigned int *cmds) +{ + int dwords = 0; + + cmds[dwords++] = cp_type7_packet(CP_CONTEXT_SWITCH_YIELD, 4); + /* Write NULL to the address to skip the data write */ + dwords += cp_gpuaddr(adreno_dev, &cmds[dwords], 0x0); + cmds[dwords++] = 1; + /* generate interrupt on preemption completion */ + cmds[dwords++] = 1; + + return dwords; +} + +void a5xx_preemption_start(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct kgsl_iommu *iommu = KGSL_IOMMU_PRIV(device); + struct adreno_ringbuffer *rb; + unsigned int i; + + if (!adreno_is_preemption_enabled(adreno_dev)) + return; + + /* Force the state to be clear */ + adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); + + kgsl_sharedmem_writel(device, &iommu->smmu_info, + PREEMPT_SMMU_RECORD(magic), A5XX_CP_SMMU_INFO_MAGIC_REF); + kgsl_sharedmem_writeq(device, &iommu->smmu_info, + PREEMPT_SMMU_RECORD(ttbr0), MMU_DEFAULT_TTBR0(device)); + + /* The CP doesn't use the asid record, so poison it */ + kgsl_sharedmem_writel(device, &iommu->smmu_info, + PREEMPT_SMMU_RECORD(asid), 0xDECAFBAD); + kgsl_sharedmem_writel(device, &iommu->smmu_info, + PREEMPT_SMMU_RECORD(context_idr), + MMU_DEFAULT_CONTEXTIDR(device)); + + adreno_writereg64(adreno_dev, + ADRENO_REG_CP_CONTEXT_SWITCH_SMMU_INFO_LO, + ADRENO_REG_CP_CONTEXT_SWITCH_SMMU_INFO_HI, + iommu->smmu_info.gpuaddr); + + FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { + kgsl_sharedmem_writel(device, &rb->preemption_desc, + PREEMPT_RECORD(rptr), 0); + kgsl_sharedmem_writel(device, &rb->preemption_desc, + PREEMPT_RECORD(wptr), 0); + + adreno_ringbuffer_set_pagetable(rb, + device->mmu.defaultpagetable); + } + +} + +static int a5xx_preemption_ringbuffer_init(struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb, uint64_t counteraddr) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + int ret; + + ret = kgsl_allocate_global(device, &rb->preemption_desc, + A5XX_CP_CTXRECORD_SIZE_IN_BYTES, 0, KGSL_MEMDESC_PRIVILEGED); + if (ret) + return ret; + + kgsl_sharedmem_writel(device, &rb->preemption_desc, + PREEMPT_RECORD(magic), A5XX_CP_CTXRECORD_MAGIC_REF); + kgsl_sharedmem_writel(device, &rb->preemption_desc, + PREEMPT_RECORD(info), 0); + kgsl_sharedmem_writel(device, &rb->preemption_desc, + PREEMPT_RECORD(data), 0); + kgsl_sharedmem_writel(device, &rb->preemption_desc, + PREEMPT_RECORD(cntl), A5XX_CP_RB_CNTL_DEFAULT); + kgsl_sharedmem_writel(device, &rb->preemption_desc, + PREEMPT_RECORD(rptr), 0); + kgsl_sharedmem_writel(device, &rb->preemption_desc, + PREEMPT_RECORD(wptr), 0); + kgsl_sharedmem_writeq(device, &rb->preemption_desc, + PREEMPT_RECORD(rptr_addr), SCRATCH_RPTR_GPU_ADDR(device, + rb->id)); + kgsl_sharedmem_writeq(device, &rb->preemption_desc, + PREEMPT_RECORD(rbase), rb->buffer_desc.gpuaddr); + kgsl_sharedmem_writeq(device, &rb->preemption_desc, + PREEMPT_RECORD(counter), counteraddr); + + return 0; +} + +#ifdef CONFIG_QCOM_KGSL_IOMMU +static int a5xx_preemption_iommu_init(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct kgsl_iommu *iommu = KGSL_IOMMU_PRIV(device); + + /* Allocate mem for storing preemption smmu record */ + return kgsl_allocate_global(device, &iommu->smmu_info, PAGE_SIZE, + KGSL_MEMFLAGS_GPUREADONLY, KGSL_MEMDESC_PRIVILEGED); +} +#else +static int a5xx_preemption_iommu_init(struct adreno_device *adreno_dev) +{ + return -ENODEV; +} +#endif + +int a5xx_preemption_init(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct adreno_preemption *preempt = &adreno_dev->preempt; + struct adreno_ringbuffer *rb; + int ret; + unsigned int i; + uint64_t addr; + + /* We are dependent on IOMMU to make preemption go on the CP side */ + if (kgsl_mmu_get_mmutype(device) != KGSL_MMU_TYPE_IOMMU) + return -ENODEV; + + INIT_WORK(&preempt->work, _a5xx_preemption_worker); + + setup_timer(&preempt->timer, _a5xx_preemption_timer, + (unsigned long) adreno_dev); + + /* Allocate mem for storing preemption counters */ + ret = kgsl_allocate_global(device, &preempt->counters, + adreno_dev->num_ringbuffers * + A5XX_CP_CTXRECORD_PREEMPTION_COUNTER_SIZE, 0, 0); + if (ret) + return ret; + + addr = preempt->counters.gpuaddr; + + /* Allocate mem for storing preemption switch record */ + FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { + ret = a5xx_preemption_ringbuffer_init(adreno_dev, rb, addr); + if (ret) + return ret; + + addr += A5XX_CP_CTXRECORD_PREEMPTION_COUNTER_SIZE; + } + + return a5xx_preemption_iommu_init(adreno_dev); +} diff --git a/drivers/gpu/msm/adreno_compat.c b/drivers/gpu/msm/adreno_compat.c index 582cbfb61e78..d86a0c60f0b4 100644 --- a/drivers/gpu/msm/adreno_compat.c +++ b/drivers/gpu/msm/adreno_compat.c @@ -89,6 +89,30 @@ int adreno_getproperty_compat(struct kgsl_device *device, status = 0; } break; + case KGSL_PROP_DEVICE_QDSS_STM: + { + struct kgsl_qdss_stm_prop qdssprop = {0}; + struct kgsl_memdesc *qdss_desc = + kgsl_mmu_get_qdss_global_entry(device); + + if (sizebytes != sizeof(qdssprop)) { + status = -EINVAL; + break; + } + + if (qdss_desc) { + qdssprop.gpuaddr = qdss_desc->gpuaddr; + qdssprop.size = qdss_desc->size; + } + + if (copy_to_user(value, &qdssprop, + sizeof(qdssprop))) { + status = -EFAULT; + break; + } + status = 0; + } + break; default: /* * Call the adreno_getproperty to check if the property type diff --git a/drivers/gpu/msm/adreno_debugfs.c b/drivers/gpu/msm/adreno_debugfs.c index 1a1db3ab3dc9..9cbcd06d7658 100644 --- a/drivers/gpu/msm/adreno_debugfs.c +++ b/drivers/gpu/msm/adreno_debugfs.c @@ -226,8 +226,7 @@ static void cmdbatch_print(struct seq_file *s, struct kgsl_cmdbatch *cmdbatch) if (cmdbatch->flags & KGSL_CONTEXT_SYNC) return; - seq_printf(s, "\t%d: ib: expires: %lu", - cmdbatch->timestamp, cmdbatch->expires); + seq_printf(s, "\t%d: ", cmdbatch->timestamp); seq_puts(s, " flags: "); print_flags(s, cmdbatch_flags, ARRAY_SIZE(cmdbatch_flags), diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c index 3f36a93ea110..ac3805800691 100644 --- a/drivers/gpu/msm/adreno_dispatch.c +++ b/drivers/gpu/msm/adreno_dispatch.c @@ -28,10 +28,10 @@ #define CMDQUEUE_NEXT(_i, _s) (((_i) + 1) % (_s)) /* Time in ms after which the dispatcher tries to schedule an unscheduled RB */ -static unsigned int _dispatch_starvation_time = 2000; +unsigned int adreno_dispatch_starvation_time = 2000; /* Amount of time in ms that a starved RB is permitted to execute for */ -static unsigned int _dispatch_time_slice = 25; +unsigned int adreno_dispatch_time_slice = 25; /* * If set then dispatcher tries to schedule lower priority RB's after if they @@ -78,6 +78,24 @@ unsigned int adreno_cmdbatch_timeout = 2000; /* Interval for reading and comparing fault detection registers */ static unsigned int _fault_timer_interval = 200; +#define CMDQUEUE_RB(_cmdqueue) \ + ((struct adreno_ringbuffer *) \ + container_of((_cmdqueue), struct adreno_ringbuffer, dispatch_q)) + +#define CMDQUEUE(_ringbuffer) (&(_ringbuffer)->dispatch_q) + +static int adreno_dispatch_retire_cmdqueue(struct adreno_device *adreno_dev, + struct adreno_dispatcher_cmdqueue *cmdqueue); + +static inline bool cmdqueue_is_current( + struct adreno_dispatcher_cmdqueue *cmdqueue) +{ + struct adreno_ringbuffer *rb = CMDQUEUE_RB(cmdqueue); + struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); + + return (adreno_dev->cur_rb == rb); +} + static void _add_context(struct adreno_device *adreno_dev, struct adreno_context *drawctxt) { @@ -283,7 +301,8 @@ static void _retire_marker(struct kgsl_cmdbatch *cmdbatch) /* Retire pending GPU events for the object */ kgsl_process_event_group(device, &context->events); - trace_adreno_cmdbatch_retired(cmdbatch, -1, 0, 0, drawctxt->rb); + trace_adreno_cmdbatch_retired(cmdbatch, -1, 0, 0, drawctxt->rb, + adreno_get_rptr(drawctxt->rb)); kgsl_cmdbatch_destroy(cmdbatch); } @@ -576,8 +595,15 @@ static int sendcmd(struct adreno_device *adreno_dev, if (dispatcher->inflight == 1) { if (ret == 0) { + + /* Stop fault timer before reading fault registers */ + del_timer_sync(&dispatcher->fault_timer); + fault_detect_read(adreno_dev); + /* Start the fault timer on first submission */ + start_fault_timer(adreno_dev); + if (!test_and_set_bit(ADRENO_DISPATCHER_ACTIVE, &dispatcher->priv)) reinit_completion(&dispatcher->idle_gate); @@ -594,11 +620,15 @@ static int sendcmd(struct adreno_device *adreno_dev, dispatch_q->inflight--; /* + * Don't log a message in case of: * -ENOENT means that the context was detached before the - * command was submitted - don't log a message in that case + * command was submitted + * -ENOSPC means that there temporarily isn't any room in the + * ringbuffer + * -PROTO means that a fault is currently being worked */ - if (ret != -ENOENT) + if (ret != -ENOENT && ret != -ENOSPC && ret != -EPROTO) KGSL_DRV_ERR(device, "Unable to submit command to the ringbuffer %d\n", ret); @@ -609,7 +639,8 @@ static int sendcmd(struct adreno_device *adreno_dev, nsecs = do_div(secs, 1000000000); trace_adreno_cmdbatch_submitted(cmdbatch, (int) dispatcher->inflight, - time.ticks, (unsigned long) secs, nsecs / 1000, drawctxt->rb); + time.ticks, (unsigned long) secs, nsecs / 1000, drawctxt->rb, + adreno_get_rptr(drawctxt->rb)); cmdbatch->submit_ticks = time.ticks; @@ -618,28 +649,26 @@ static int sendcmd(struct adreno_device *adreno_dev, ADRENO_DISPATCH_CMDQUEUE_SIZE; /* - * If this is the first command in the pipe then the GPU will - * immediately start executing it so we can start the expiry timeout on - * the command batch here. Subsequent command batches will have their - * timer started when the previous command batch is retired. - * Set the timer if the cmdbatch was submitted to current - * active RB else this timer will need to be set when the - * RB becomes active, also if dispatcher is not is CLEAR - * state then the cmdbatch it is currently executing is - * unclear so do not set timer in that case either. + * For the first submission in any given command queue update the + * expected expire time - this won't actually be used / updated until + * the command queue in question goes current, but universally setting + * it here avoids the possibilty of some race conditions with preempt */ - if (1 == dispatch_q->inflight && - (&(adreno_dev->cur_rb->dispatch_q)) == dispatch_q && - adreno_preempt_state(adreno_dev, - ADRENO_DISPATCHER_PREEMPT_CLEAR)) { - cmdbatch->expires = jiffies + + + if (dispatch_q->inflight == 1) + dispatch_q->expires = jiffies + msecs_to_jiffies(adreno_cmdbatch_timeout); - mod_timer(&dispatcher->timer, cmdbatch->expires); + + /* + * If we believe ourselves to be current and preemption isn't a thing, + * then set up the timer. If this misses, then preemption is indeed a + * thing and the timer will be set up in due time + */ + if (!adreno_in_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE)) { + if (cmdqueue_is_current(dispatch_q)) + mod_timer(&dispatcher->timer, dispatch_q->expires); } - /* Start the fault detection timer on the first submission */ - if (dispatcher->inflight == 1) - start_fault_timer(adreno_dev); /* * we just submitted something, readjust ringbuffer @@ -924,87 +953,6 @@ static int get_timestamp(struct adreno_context *drawctxt, } /** - * adreno_dispatcher_preempt_timer() - Timer that triggers when preemption has - * not completed - * @data: Pointer to adreno device that did not preempt in timely manner - */ -static void adreno_dispatcher_preempt_timer(unsigned long data) -{ - struct adreno_device *adreno_dev = (struct adreno_device *) data; - struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - - KGSL_DRV_ERR(KGSL_DEVICE(adreno_dev), - "Preemption timed out. cur_rb rptr/wptr %x/%x id %d, next_rb rptr/wptr %x/%x id %d, disp_state: %d\n", - adreno_dev->cur_rb->rptr, adreno_dev->cur_rb->wptr, - adreno_dev->cur_rb->id, adreno_dev->next_rb->rptr, - adreno_dev->next_rb->wptr, adreno_dev->next_rb->id, - atomic_read(&dispatcher->preemption_state)); - adreno_set_gpu_fault(adreno_dev, ADRENO_PREEMPT_FAULT); - adreno_dispatcher_schedule(KGSL_DEVICE(adreno_dev)); -} - -/** - * adreno_dispatcher_get_highest_busy_rb() - Returns the highest priority RB - * which is busy - * @adreno_dev: Device whose RB is returned - */ -struct adreno_ringbuffer *adreno_dispatcher_get_highest_busy_rb( - struct adreno_device *adreno_dev) -{ - struct adreno_ringbuffer *rb, *highest_busy_rb = NULL; - int i; - - FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { - if (rb->rptr != rb->wptr && !highest_busy_rb) { - highest_busy_rb = rb; - goto done; - } - - if (!adreno_disp_preempt_fair_sched) - continue; - - switch (rb->starve_timer_state) { - case ADRENO_DISPATCHER_RB_STARVE_TIMER_UNINIT: - if (rb->rptr != rb->wptr && - adreno_dev->cur_rb != rb) { - rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_INIT; - rb->sched_timer = jiffies; - } - break; - case ADRENO_DISPATCHER_RB_STARVE_TIMER_INIT: - if (time_after(jiffies, rb->sched_timer + - msecs_to_jiffies(_dispatch_starvation_time))) { - rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_ELAPSED; - /* halt dispatcher to remove starvation */ - adreno_get_gpu_halt(adreno_dev); - } - break; - case ADRENO_DISPATCHER_RB_STARVE_TIMER_SCHEDULED: - BUG_ON(adreno_dev->cur_rb != rb); - /* - * If the RB has not been running for the minimum - * time slice then allow it to run - */ - if ((rb->rptr != rb->wptr) && time_before(jiffies, - adreno_dev->cur_rb->sched_timer + - msecs_to_jiffies(_dispatch_time_slice))) - highest_busy_rb = rb; - else - rb->starve_timer_state = - ADRENO_DISPATCHER_RB_STARVE_TIMER_UNINIT; - break; - case ADRENO_DISPATCHER_RB_STARVE_TIMER_ELAPSED: - default: - break; - } - } -done: - return highest_busy_rb; -} - -/** * adreno_dispactcher_queue_cmd() - Queue a new command in the context * @adreno_dev: Pointer to the adreno device struct * @drawctxt: Pointer to the adreno draw context @@ -1433,7 +1381,7 @@ static void adreno_fault_header(struct kgsl_device *device, if (rb != NULL) pr_fault(device, cmdbatch, "gpu fault rb %d rb sw r/w %4.4x/%4.4x\n", - rb->id, rb->rptr, rb->wptr); + rb->id, rptr, rb->wptr); } else { int id = (rb != NULL) ? rb->id : -1; @@ -1444,7 +1392,7 @@ static void adreno_fault_header(struct kgsl_device *device, if (rb != NULL) dev_err(device->dev, "RB[%d] gpu fault rb sw r/w %4.4x/%4.4x\n", - rb->id, rb->rptr, rb->wptr); + rb->id, rptr, rb->wptr); } } @@ -1751,6 +1699,27 @@ replay: kfree(replay); } +static void do_header_and_snapshot(struct kgsl_device *device, + struct adreno_ringbuffer *rb, struct kgsl_cmdbatch *cmdbatch) +{ + /* Always dump the snapshot on a non-cmdbatch failure */ + if (cmdbatch == NULL) { + adreno_fault_header(device, rb, NULL); + kgsl_device_snapshot(device, NULL); + return; + } + + /* Skip everything if the PMDUMP flag is set */ + if (test_bit(KGSL_FT_SKIP_PMDUMP, &cmdbatch->fault_policy)) + return; + + /* Print the fault header */ + adreno_fault_header(device, rb, cmdbatch); + + if (!(cmdbatch->context->flags & KGSL_CONTEXT_NO_SNAPSHOT)) + kgsl_device_snapshot(device, cmdbatch->context); +} + static int dispatcher_do_fault(struct adreno_device *adreno_dev) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); @@ -1787,7 +1756,7 @@ static int dispatcher_do_fault(struct adreno_device *adreno_dev) /* Turn off all the timers */ del_timer_sync(&dispatcher->timer); del_timer_sync(&dispatcher->fault_timer); - del_timer_sync(&dispatcher->preempt_timer); + del_timer_sync(&adreno_dev->preempt.timer); mutex_lock(&device->mutex); @@ -1813,14 +1782,12 @@ static int dispatcher_do_fault(struct adreno_device *adreno_dev) * retire cmdbatches from all the dispatch_q's before starting recovery */ FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { - adreno_dispatch_process_cmdqueue(adreno_dev, - &(rb->dispatch_q), 0); + adreno_dispatch_retire_cmdqueue(adreno_dev, + &(rb->dispatch_q)); /* Select the active dispatch_q */ if (base == rb->buffer_desc.gpuaddr) { dispatch_q = &(rb->dispatch_q); hung_rb = rb; - adreno_readreg(adreno_dev, ADRENO_REG_CP_RB_RPTR, - &hung_rb->rptr); if (adreno_dev->cur_rb != hung_rb) { adreno_dev->prev_rb = adreno_dev->cur_rb; adreno_dev->cur_rb = hung_rb; @@ -1834,7 +1801,7 @@ static int dispatcher_do_fault(struct adreno_device *adreno_dev) } } - if (dispatch_q && (dispatch_q->tail != dispatch_q->head)) { + if (!adreno_cmdqueue_is_empty(dispatch_q)) { cmdbatch = dispatch_q->cmd_q[dispatch_q->head]; trace_adreno_cmdbatch_fault(cmdbatch, fault); } @@ -1842,17 +1809,7 @@ static int dispatcher_do_fault(struct adreno_device *adreno_dev) adreno_readreg64(adreno_dev, ADRENO_REG_CP_IB1_BASE, ADRENO_REG_CP_IB1_BASE_HI, &base); - /* - * Dump the snapshot information if this is the first - * detected fault for the oldest active command batch - */ - - if (cmdbatch == NULL || - !test_bit(KGSL_FT_SKIP_PMDUMP, &cmdbatch->fault_policy)) { - adreno_fault_header(device, hung_rb, cmdbatch); - kgsl_device_snapshot(device, - cmdbatch ? cmdbatch->context : NULL); - } + do_header_and_snapshot(device, hung_rb, cmdbatch); /* Terminate the stalled transaction and resume the IOMMU */ if (fault & ADRENO_IOMMU_PAGE_FAULT) @@ -1860,8 +1817,6 @@ static int dispatcher_do_fault(struct adreno_device *adreno_dev) /* Reset the dispatcher queue */ dispatcher->inflight = 0; - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_CLEAR); /* Reset the GPU and make sure halt is not set during recovery */ halt = adreno_gpu_halt(adreno_dev); @@ -1875,12 +1830,12 @@ static int dispatcher_do_fault(struct adreno_device *adreno_dev) if (hung_rb != NULL) { kgsl_sharedmem_writel(device, &device->memstore, - KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_MAX + hung_rb->id, - soptimestamp), hung_rb->timestamp); + MEMSTORE_RB_OFFSET(hung_rb, soptimestamp), + hung_rb->timestamp); kgsl_sharedmem_writel(device, &device->memstore, - KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_MAX + hung_rb->id, - eoptimestamp), hung_rb->timestamp); + MEMSTORE_RB_OFFSET(hung_rb, eoptimestamp), + hung_rb->timestamp); /* Schedule any pending events to be run */ kgsl_process_event_group(device, &hung_rb->events); @@ -1953,139 +1908,170 @@ static void cmdbatch_profile_ticks(struct adreno_device *adreno_dev, *retire = entry->retired; } -int adreno_dispatch_process_cmdqueue(struct adreno_device *adreno_dev, - struct adreno_dispatcher_cmdqueue *dispatch_q, - int long_ib_detect) +static void retire_cmdbatch(struct adreno_device *adreno_dev, + struct kgsl_cmdbatch *cmdbatch) { - struct adreno_dispatcher *dispatcher = &(adreno_dev->dispatcher); - uint64_t start_ticks = 0, retire_ticks = 0; + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + struct adreno_context *drawctxt = ADRENO_CONTEXT(cmdbatch->context); + uint64_t start = 0, end = 0; - struct adreno_dispatcher_cmdqueue *active_q = - &(adreno_dev->cur_rb->dispatch_q); + if (cmdbatch->fault_recovery != 0) { + set_bit(ADRENO_CONTEXT_FAULT, &cmdbatch->context->priv); + _print_recovery(KGSL_DEVICE(adreno_dev), cmdbatch); + } + + if (test_bit(CMDBATCH_FLAG_PROFILE, &cmdbatch->priv)) + cmdbatch_profile_ticks(adreno_dev, cmdbatch, &start, &end); + + trace_adreno_cmdbatch_retired(cmdbatch, (int) dispatcher->inflight, + start, end, ADRENO_CMDBATCH_RB(cmdbatch), + adreno_get_rptr(drawctxt->rb)); + + drawctxt->submit_retire_ticks[drawctxt->ticks_index] = + end - cmdbatch->submit_ticks; + + drawctxt->ticks_index = (drawctxt->ticks_index + 1) % + SUBMIT_RETIRE_TICKS_SIZE; + + kgsl_cmdbatch_destroy(cmdbatch); +} + +static int adreno_dispatch_retire_cmdqueue(struct adreno_device *adreno_dev, + struct adreno_dispatcher_cmdqueue *cmdqueue) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; int count = 0; - while (dispatch_q->head != dispatch_q->tail) { + while (!adreno_cmdqueue_is_empty(cmdqueue)) { struct kgsl_cmdbatch *cmdbatch = - dispatch_q->cmd_q[dispatch_q->head]; - struct adreno_context *drawctxt; - BUG_ON(cmdbatch == NULL); + cmdqueue->cmd_q[cmdqueue->head]; - drawctxt = ADRENO_CONTEXT(cmdbatch->context); + if (!kgsl_check_timestamp(device, cmdbatch->context, + cmdbatch->timestamp)) + break; - /* - * First try to expire the timestamp. This happens if the - * context is valid and the timestamp expired normally or if the - * context was destroyed before the command batch was finished - * in the GPU. Either way retire the command batch advance the - * pointers and continue processing the queue - */ + retire_cmdbatch(adreno_dev, cmdbatch); - if (kgsl_check_timestamp(KGSL_DEVICE(adreno_dev), - cmdbatch->context, cmdbatch->timestamp)) { + dispatcher->inflight--; + cmdqueue->inflight--; - /* - * If the cmdbatch in question had faulted announce its - * successful completion to the world - */ + cmdqueue->cmd_q[cmdqueue->head] = NULL; - if (cmdbatch->fault_recovery != 0) { - /* Mark the context as faulted and recovered */ - set_bit(ADRENO_CONTEXT_FAULT, - &cmdbatch->context->priv); + cmdqueue->head = CMDQUEUE_NEXT(cmdqueue->head, + ADRENO_DISPATCH_CMDQUEUE_SIZE); - _print_recovery(KGSL_DEVICE(adreno_dev), - cmdbatch); - } + count++; + } - /* Reduce the number of inflight command batches */ - dispatcher->inflight--; - dispatch_q->inflight--; + return count; +} - /* - * If kernel profiling is enabled get the submit and - * retired ticks from the buffer - */ +static void _adreno_dispatch_check_timeout(struct adreno_device *adreno_dev, + struct adreno_dispatcher_cmdqueue *cmdqueue) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct kgsl_cmdbatch *cmdbatch = cmdqueue->cmd_q[cmdqueue->head]; - if (test_bit(CMDBATCH_FLAG_PROFILE, &cmdbatch->priv)) - cmdbatch_profile_ticks(adreno_dev, cmdbatch, - &start_ticks, &retire_ticks); + /* Don't timeout if the timer hasn't expired yet (duh) */ + if (time_is_after_jiffies(cmdqueue->expires)) + return; - trace_adreno_cmdbatch_retired(cmdbatch, - (int) dispatcher->inflight, start_ticks, - retire_ticks, ADRENO_CMDBATCH_RB(cmdbatch)); + /* Don't timeout if the IB timeout is disabled globally */ + if (!adreno_long_ib_detect(adreno_dev)) + return; - /* Record the delta between submit and retire ticks */ - drawctxt->submit_retire_ticks[drawctxt->ticks_index] = - retire_ticks - cmdbatch->submit_ticks; + /* Don't time out if the context has disabled it */ + if (cmdbatch->context->flags & KGSL_CONTEXT_NO_FAULT_TOLERANCE) + return; - drawctxt->ticks_index = (drawctxt->ticks_index + 1) - % SUBMIT_RETIRE_TICKS_SIZE; + pr_context(device, cmdbatch->context, "gpu timeout ctx %d ts %d\n", + cmdbatch->context->id, cmdbatch->timestamp); - /* Zero the old entry*/ - dispatch_q->cmd_q[dispatch_q->head] = NULL; + adreno_set_gpu_fault(adreno_dev, ADRENO_TIMEOUT_FAULT); +} - /* Advance the buffer head */ - dispatch_q->head = CMDQUEUE_NEXT(dispatch_q->head, - ADRENO_DISPATCH_CMDQUEUE_SIZE); +static int adreno_dispatch_process_cmdqueue(struct adreno_device *adreno_dev, + struct adreno_dispatcher_cmdqueue *cmdqueue) +{ + int count = adreno_dispatch_retire_cmdqueue(adreno_dev, cmdqueue); - /* Destroy the retired command batch */ - kgsl_cmdbatch_destroy(cmdbatch); + /* Nothing to do if there are no pending commands */ + if (adreno_cmdqueue_is_empty(cmdqueue)) + return count; - /* Update the expire time for the next command batch */ + /* Don't update the cmdqueue timeout if we are about to preempt out */ + if (!adreno_in_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE)) + return count; - if (dispatch_q->inflight > 0 && - dispatch_q == active_q) { - cmdbatch = - dispatch_q->cmd_q[dispatch_q->head]; - cmdbatch->expires = jiffies + - msecs_to_jiffies( - adreno_cmdbatch_timeout); - } + /* Don't update the cmdqueue timeout if it isn't active */ + if (!cmdqueue_is_current(cmdqueue)) + return count; - count++; - continue; - } - /* - * Break here if fault detection is disabled for the context or - * if the long running IB detection is disaled device wide or - * if the dispatch q is not active - * Long running command buffers will be allowed to run to - * completion - but badly behaving command buffers (infinite - * shaders etc) can end up running forever. - */ + /* + * If the current ringbuffer retired any commands then universally + * reset the timeout + */ - if (!long_ib_detect || - drawctxt->base.flags & KGSL_CONTEXT_NO_FAULT_TOLERANCE - || dispatch_q != active_q) - break; + if (count) { + cmdqueue->expires = jiffies + + msecs_to_jiffies(adreno_cmdbatch_timeout); + return count; + } - /* - * The last line of defense is to check if the command batch has - * timed out. If we get this far but the timeout hasn't expired - * yet then the GPU is still ticking away - */ + /* + * If we get here then 1) the ringbuffer is current and 2) we haven't + * retired anything. Check to see if the timeout if valid for the + * current cmdbatch and fault if it has expired + */ + _adreno_dispatch_check_timeout(adreno_dev, cmdqueue); + return 0; +} - if (time_is_after_jiffies(cmdbatch->expires)) - break; +/* Update the dispatcher timers */ +static void _dispatcher_update_timers(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - /* Boom goes the dynamite */ + /* Kick the idle timer */ + mutex_lock(&device->mutex); + kgsl_pwrscale_update(device); + mod_timer(&device->idle_timer, + jiffies + device->pwrctrl.interval_timeout); + mutex_unlock(&device->mutex); - pr_context(KGSL_DEVICE(adreno_dev), cmdbatch->context, - "gpu timeout ctx %d ts %d\n", - cmdbatch->context->id, cmdbatch->timestamp); + /* Check to see if we need to update the command timer */ + if (adreno_in_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE)) { + struct adreno_dispatcher_cmdqueue *cmdqueue = + CMDQUEUE(adreno_dev->cur_rb); - adreno_set_gpu_fault(adreno_dev, ADRENO_TIMEOUT_FAULT); - break; + if (!adreno_cmdqueue_is_empty(cmdqueue)) + mod_timer(&dispatcher->timer, cmdqueue->expires); } - return count; } -/** - * adreno_dispatcher_work() - Master work handler for the dispatcher - * @work: Pointer to the work struct for the current work queue - * - * Process expired commands and send new ones. - */ +/* Take down the dispatcher and release any power states */ +static void _dispatcher_power_down(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + + mutex_lock(&device->mutex); + + if (test_and_clear_bit(ADRENO_DISPATCHER_ACTIVE, &dispatcher->priv)) + complete_all(&dispatcher->idle_gate); + + del_timer_sync(&dispatcher->fault_timer); + + if (test_bit(ADRENO_DISPATCHER_POWER, &dispatcher->priv)) { + kgsl_active_count_put(device); + clear_bit(ADRENO_DISPATCHER_POWER, &dispatcher->priv); + } + + mutex_unlock(&device->mutex); +} + static void adreno_dispatcher_work(struct work_struct *work) { struct adreno_dispatcher *dispatcher = @@ -2095,95 +2081,50 @@ static void adreno_dispatcher_work(struct work_struct *work) struct kgsl_device *device = KGSL_DEVICE(adreno_dev); struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); int count = 0; - int cur_rb_id = adreno_dev->cur_rb->id; + unsigned int i = 0; mutex_lock(&dispatcher->mutex); - if (ADRENO_DISPATCHER_PREEMPT_CLEAR == - atomic_read(&dispatcher->preemption_state)) - /* process the active q*/ - count = adreno_dispatch_process_cmdqueue(adreno_dev, - &(adreno_dev->cur_rb->dispatch_q), - adreno_long_ib_detect(adreno_dev)); - - else if (ADRENO_DISPATCHER_PREEMPT_TRIGGERED == - atomic_read(&dispatcher->preemption_state)) - count = adreno_dispatch_process_cmdqueue(adreno_dev, - &(adreno_dev->cur_rb->dispatch_q), 0); - - /* Check if gpu fault occurred */ - if (dispatcher_do_fault(adreno_dev)) - goto done; - - if (gpudev->preemption_schedule) - gpudev->preemption_schedule(adreno_dev); - - if (cur_rb_id != adreno_dev->cur_rb->id) { - struct adreno_dispatcher_cmdqueue *dispatch_q = - &(adreno_dev->cur_rb->dispatch_q); - /* active level switched, clear new level cmdbatches */ - count = adreno_dispatch_process_cmdqueue(adreno_dev, - dispatch_q, - adreno_long_ib_detect(adreno_dev)); - /* - * If GPU has already completed all the commands in new incoming - * RB then we may not get another interrupt due to which - * dispatcher may not run again. Schedule dispatcher here so - * we can come back and process the other RB's if required - */ - if (dispatch_q->head == dispatch_q->tail) - adreno_dispatcher_schedule(device); - } /* - * If inflight went to 0, queue back up the event processor to catch - * stragglers + * As long as there are inflight commands, process retired comamnds from + * all cmdqueues */ - if (dispatcher->inflight == 0 && count) - kgsl_schedule_work(&device->event_work); - - /* Try to dispatch new commands */ - _adreno_dispatcher_issuecmds(adreno_dev); - -done: - /* Either update the timer for the next command batch or disable it */ - if (dispatcher->inflight) { - struct kgsl_cmdbatch *cmdbatch = - adreno_dev->cur_rb->dispatch_q.cmd_q[ - adreno_dev->cur_rb->dispatch_q.head]; - if (cmdbatch && adreno_preempt_state(adreno_dev, - ADRENO_DISPATCHER_PREEMPT_CLEAR)) - /* Update the timeout timer for the next cmdbatch */ - mod_timer(&dispatcher->timer, cmdbatch->expires); - - /* There are still things in flight - update the idle counts */ - mutex_lock(&device->mutex); - kgsl_pwrscale_update(device); - mod_timer(&device->idle_timer, jiffies + - device->pwrctrl.interval_timeout); - mutex_unlock(&device->mutex); - } else { - /* There is nothing left in the pipeline. Shut 'er down boys */ - mutex_lock(&device->mutex); + for (i = 0; i < adreno_dev->num_ringbuffers; i++) { + struct adreno_dispatcher_cmdqueue *cmdqueue = + CMDQUEUE(&adreno_dev->ringbuffers[i]); - if (test_and_clear_bit(ADRENO_DISPATCHER_ACTIVE, - &dispatcher->priv)) - complete_all(&dispatcher->idle_gate); + count += adreno_dispatch_process_cmdqueue(adreno_dev, + cmdqueue); + if (dispatcher->inflight == 0) + break; + } - /* - * Stop the fault timer before decrementing the active count to - * avoid reading the hardware registers while we are trying to - * turn clocks off - */ - del_timer_sync(&dispatcher->fault_timer); + /* + * dispatcher_do_fault() returns 0 if no faults occurred. If that is the + * case, then clean up preemption and try to schedule more work + */ + if (dispatcher_do_fault(adreno_dev) == 0) { + /* Clean up after preemption */ + if (gpudev->preemption_schedule) + gpudev->preemption_schedule(adreno_dev); - if (test_bit(ADRENO_DISPATCHER_POWER, &dispatcher->priv)) { - kgsl_active_count_put(device); - clear_bit(ADRENO_DISPATCHER_POWER, &dispatcher->priv); - } + /* Re-kick the event engine to catch stragglers */ + if (dispatcher->inflight == 0 && count != 0) + kgsl_schedule_work(&device->event_work); - mutex_unlock(&device->mutex); + /* Run the scheduler for to dispatch new commands */ + _adreno_dispatcher_issuecmds(adreno_dev); } + /* + * If there are commands pending, update the timers, otherwise release + * the power state to prepare for power down + */ + if (dispatcher->inflight > 0) + _dispatcher_update_timers(adreno_dev); + else + _dispatcher_power_down(adreno_dev); + mutex_unlock(&dispatcher->mutex); } @@ -2305,7 +2246,7 @@ void adreno_dispatcher_close(struct adreno_device *adreno_dev) FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { struct adreno_dispatcher_cmdqueue *dispatch_q = &(rb->dispatch_q); - while (dispatch_q->head != dispatch_q->tail) { + while (!adreno_cmdqueue_is_empty(dispatch_q)) { kgsl_cmdbatch_destroy( dispatch_q->cmd_q[dispatch_q->head]); dispatch_q->head = (dispatch_q->head + 1) @@ -2395,9 +2336,9 @@ static DISPATCHER_UINT_ATTR(fault_throttle_burst, 0644, 0, static DISPATCHER_UINT_ATTR(disp_preempt_fair_sched, 0644, 0, adreno_disp_preempt_fair_sched); static DISPATCHER_UINT_ATTR(dispatch_time_slice, 0644, 0, - _dispatch_time_slice); + adreno_dispatch_time_slice); static DISPATCHER_UINT_ATTR(dispatch_starvation_time, 0644, 0, - _dispatch_starvation_time); + adreno_dispatch_starvation_time); static struct attribute *dispatcher_attrs[] = { &dispatcher_attr_inflight.attr, @@ -2474,9 +2415,6 @@ int adreno_dispatcher_init(struct adreno_device *adreno_dev) setup_timer(&dispatcher->fault_timer, adreno_dispatcher_fault_timer, (unsigned long) adreno_dev); - setup_timer(&dispatcher->preempt_timer, adreno_dispatcher_preempt_timer, - (unsigned long) adreno_dev); - INIT_WORK(&dispatcher->work, adreno_dispatcher_work); init_completion(&dispatcher->idle_gate); @@ -2485,9 +2423,6 @@ int adreno_dispatcher_init(struct adreno_device *adreno_dev) plist_head_init(&dispatcher->pending); spin_lock_init(&dispatcher->plist_lock); - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_CLEAR); - ret = kobject_init_and_add(&dispatcher->kobj, &ktype_dispatcher, &device->dev->kobj, "dispatch"); @@ -2544,49 +2479,3 @@ int adreno_dispatcher_idle(struct adreno_device *adreno_dev) adreno_dispatcher_schedule(device); return ret; } - -void adreno_preempt_process_dispatch_queue(struct adreno_device *adreno_dev, - struct adreno_dispatcher_cmdqueue *dispatch_q) -{ - struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - struct kgsl_cmdbatch *cmdbatch; - - if (dispatch_q->head != dispatch_q->tail) { - /* - * retire cmdbacthes from previous q, and don't check for - * timeout since the cmdbatch may have been preempted - */ - adreno_dispatch_process_cmdqueue(adreno_dev, - dispatch_q, 0); - } - - /* set the timer for the first cmdbatch of active dispatch_q */ - dispatch_q = &(adreno_dev->cur_rb->dispatch_q); - if (dispatch_q->head != dispatch_q->tail) { - cmdbatch = dispatch_q->cmd_q[dispatch_q->head]; - cmdbatch->expires = jiffies + - msecs_to_jiffies(adreno_cmdbatch_timeout); - } - kgsl_schedule_work(&device->event_work); -} - -/** - * adreno_dispatcher_preempt_callback() - Callback funcion for CP_SW interrupt - * @adreno_dev: The device on which the interrupt occurred - * @bit: Interrupt bit in the interrupt status register - */ -void adreno_dispatcher_preempt_callback(struct adreno_device *adreno_dev, - int bit) -{ - struct adreno_dispatcher *dispatcher = &(adreno_dev->dispatcher); - - if (ADRENO_DISPATCHER_PREEMPT_TRIGGERED != - atomic_read(&dispatcher->preemption_state)) - return; - - trace_adreno_hw_preempt_trig_to_comp_int(adreno_dev->cur_rb, - adreno_dev->next_rb); - atomic_set(&dispatcher->preemption_state, - ADRENO_DISPATCHER_PREEMPT_COMPLETE); - adreno_dispatcher_schedule(KGSL_DEVICE(adreno_dev)); -} diff --git a/drivers/gpu/msm/adreno_dispatch.h b/drivers/gpu/msm/adreno_dispatch.h index 308d5b936819..699c3e4adb27 100644 --- a/drivers/gpu/msm/adreno_dispatch.h +++ b/drivers/gpu/msm/adreno_dispatch.h @@ -11,29 +11,13 @@ * */ - #ifndef ____ADRENO_DISPATCHER_H #define ____ADRENO_DISPATCHER_H -/* Time to allow preemption to complete (in ms) */ -#define ADRENO_DISPATCH_PREEMPT_TIMEOUT 10000 - extern unsigned int adreno_disp_preempt_fair_sched; extern unsigned int adreno_cmdbatch_timeout; - -/** - * enum adreno_dispatcher_preempt_states - States of dispatcher for ringbuffer - * preemption - * @ADRENO_DISPATCHER_PREEMPT_CLEAR: No preemption is underway, - * only 1 preemption can be underway at any point - * @ADRENO_DISPATCHER_PREEMPT_TRIGGERED: A preemption is underway - * @ADRENO_DISPATCHER_PREEMPT_COMPLETE: A preemption has just completed - */ -enum adreno_dispatcher_preempt_states { - ADRENO_DISPATCHER_PREEMPT_CLEAR = 0, - ADRENO_DISPATCHER_PREEMPT_TRIGGERED, - ADRENO_DISPATCHER_PREEMPT_COMPLETE, -}; +extern unsigned int adreno_dispatch_starvation_time; +extern unsigned int adreno_dispatch_time_slice; /** * enum adreno_dispatcher_starve_timer_states - Starvation control states of @@ -71,6 +55,7 @@ enum adreno_dispatcher_starve_timer_states { * @head: Head pointer to the q * @tail: Queues tail pointer * @active_context_count: Number of active contexts seen in this rb cmdqueue + * @expires: The jiffies value at which this cmdqueue has run too long */ struct adreno_dispatcher_cmdqueue { struct kgsl_cmdbatch *cmd_q[ADRENO_DISPATCH_CMDQUEUE_SIZE]; @@ -78,6 +63,7 @@ struct adreno_dispatcher_cmdqueue { unsigned int head; unsigned int tail; int active_context_count; + unsigned long expires; }; /** @@ -92,11 +78,6 @@ struct adreno_dispatcher_cmdqueue { * @work: work_struct to put the dispatcher in a work queue * @kobj: kobject for the dispatcher directory in the device sysfs node * @idle_gate: Gate to wait on for dispatcher to idle - * @preemption_state: Indicated what state the dispatcher is in, states are - * defined by enum adreno_dispatcher_preempt_states - * @preempt_token_submit: Indicates if a preempt token has been subnitted in - * current ringbuffer. - * @preempt_timer: Timer to track if preemption occured within specified time * @disp_preempt_fair_sched: If set then dispatcher will try to be fair to * starving RB's by scheduling them in and enforcing a minimum time slice * for every RB that is scheduled to run on the device @@ -113,9 +94,6 @@ struct adreno_dispatcher { struct work_struct work; struct kobject kobj; struct completion idle_gate; - atomic_t preemption_state; - int preempt_token_submit; - struct timer_list preempt_timer; unsigned int disp_preempt_fair_sched; }; @@ -141,12 +119,12 @@ void adreno_dispatcher_queue_context(struct kgsl_device *device, struct adreno_context *drawctxt); void adreno_dispatcher_preempt_callback(struct adreno_device *adreno_dev, int bit); -struct adreno_ringbuffer *adreno_dispatcher_get_highest_busy_rb( - struct adreno_device *adreno_dev); -int adreno_dispatch_process_cmdqueue(struct adreno_device *adreno_dev, - struct adreno_dispatcher_cmdqueue *dispatch_q, - int long_ib_detect); void adreno_preempt_process_dispatch_queue(struct adreno_device *adreno_dev, struct adreno_dispatcher_cmdqueue *dispatch_q); +static inline bool adreno_cmdqueue_is_empty( + struct adreno_dispatcher_cmdqueue *cmdqueue) +{ + return (cmdqueue != NULL && cmdqueue->head == cmdqueue->tail); +} #endif /* __ADRENO_DISPATCHER_H */ diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c index d8498d938b6a..fb95f6108fb8 100644 --- a/drivers/gpu/msm/adreno_drawctxt.c +++ b/drivers/gpu/msm/adreno_drawctxt.c @@ -346,7 +346,8 @@ adreno_drawctxt_create(struct kgsl_device_private *dev_priv, KGSL_CONTEXT_PWR_CONSTRAINT | KGSL_CONTEXT_IFH_NOP | KGSL_CONTEXT_SECURE | - KGSL_CONTEXT_PREEMPT_STYLE_MASK); + KGSL_CONTEXT_PREEMPT_STYLE_MASK | + KGSL_CONTEXT_NO_SNAPSHOT); /* Check for errors before trying to initialize */ @@ -466,20 +467,6 @@ void adreno_drawctxt_detach(struct kgsl_context *context) list_del_init(&drawctxt->active_node); spin_unlock(&adreno_dev->active_list_lock); - /* deactivate context */ - mutex_lock(&device->mutex); - if (rb->drawctxt_active == drawctxt) { - if (adreno_dev->cur_rb == rb) { - if (!kgsl_active_count_get(device)) { - adreno_drawctxt_switch(adreno_dev, rb, NULL, 0); - kgsl_active_count_put(device); - } else - BUG(); - } else - adreno_drawctxt_switch(adreno_dev, rb, NULL, 0); - } - mutex_unlock(&device->mutex); - spin_lock(&drawctxt->lock); count = drawctxt_detach_cmdbatches(drawctxt, list); spin_unlock(&drawctxt->lock); @@ -548,12 +535,21 @@ void adreno_drawctxt_destroy(struct kgsl_context *context) kfree(drawctxt); } +static void _drawctxt_switch_wait_callback(struct kgsl_device *device, + struct kgsl_event_group *group, + void *priv, int result) +{ + struct adreno_context *drawctxt = (struct adreno_context *) priv; + + kgsl_context_put(&drawctxt->base); +} + /** * adreno_drawctxt_switch - switch the current draw context in a given RB * @adreno_dev - The 3D device that owns the context * @rb: The ringubffer pointer on which the current context is being changed * @drawctxt - the 3D context to switch to - * @flags - Flags to accompany the switch (from user space) + * @flags: Control flags for the switch * * Switch the current draw context in given RB */ @@ -583,8 +579,7 @@ int adreno_drawctxt_switch(struct adreno_device *adreno_dev, if (drawctxt != NULL && kgsl_context_detached(&drawctxt->base)) return -ENOENT; - trace_adreno_drawctxt_switch(rb, - drawctxt, flags); + trace_adreno_drawctxt_switch(rb, drawctxt); /* Get a refcount to the new instance */ if (drawctxt) { @@ -596,16 +591,18 @@ int adreno_drawctxt_switch(struct adreno_device *adreno_dev, /* No context - set the default pagetable and thats it. */ new_pt = device->mmu.defaultpagetable; } - ret = adreno_ringbuffer_set_pt_ctx(rb, new_pt, drawctxt); - if (ret) { - KGSL_DRV_ERR(device, - "Failed to set pagetable on rb %d\n", rb->id); + ret = adreno_ringbuffer_set_pt_ctx(rb, new_pt, drawctxt, flags); + if (ret) return ret; - } - /* Put the old instance of the active drawctxt */ - if (rb->drawctxt_active) - kgsl_context_put(&rb->drawctxt_active->base); + if (rb->drawctxt_active) { + /* Wait for the timestamp to expire */ + if (kgsl_add_event(device, &rb->events, rb->timestamp, + _drawctxt_switch_wait_callback, + rb->drawctxt_active)) { + kgsl_context_put(&rb->drawctxt_active->base); + } + } rb->drawctxt_active = drawctxt; return 0; diff --git a/drivers/gpu/msm/adreno_drawctxt.h b/drivers/gpu/msm/adreno_drawctxt.h index 7e80247e9322..5ea911954991 100644 --- a/drivers/gpu/msm/adreno_drawctxt.h +++ b/drivers/gpu/msm/adreno_drawctxt.h @@ -104,6 +104,9 @@ enum adreno_context_priv { ADRENO_CONTEXT_SKIP_CMD, }; +/* Flags for adreno_drawctxt_switch() */ +#define ADRENO_CONTEXT_SWITCH_FORCE_GPU BIT(0) + struct kgsl_context *adreno_drawctxt_create(struct kgsl_device_private *, uint32_t *flags); diff --git a/drivers/gpu/msm/adreno_ioctl.c b/drivers/gpu/msm/adreno_ioctl.c index 519087a77b83..0d5e3e094c36 100644 --- a/drivers/gpu/msm/adreno_ioctl.c +++ b/drivers/gpu/msm/adreno_ioctl.c @@ -103,7 +103,7 @@ static long adreno_ioctl_preemption_counters_query( levels_to_copy = gpudev->num_prio_levels; if (copy_to_user((void __user *) (uintptr_t) read->counters, - adreno_dev->preemption_counters.hostptr, + adreno_dev->preempt.counters.hostptr, levels_to_copy * size_level)) return -EFAULT; diff --git a/drivers/gpu/msm/adreno_iommu.c b/drivers/gpu/msm/adreno_iommu.c index 2eeda01b3c4d..aa00dcb84185 100644 --- a/drivers/gpu/msm/adreno_iommu.c +++ b/drivers/gpu/msm/adreno_iommu.c @@ -275,6 +275,7 @@ static bool _ctx_switch_use_cpu_path( struct adreno_ringbuffer *rb) { struct kgsl_mmu *mmu = KGSL_MMU(adreno_dev); + /* * If rb is current, we can use cpu path when GPU is * idle and we are switching to default pt. @@ -284,7 +285,7 @@ static bool _ctx_switch_use_cpu_path( if (adreno_dev->cur_rb == rb) return adreno_isidle(KGSL_DEVICE(adreno_dev)) && (new_pt == mmu->defaultpagetable); - else if ((rb->wptr == rb->rptr) && + else if (adreno_rb_empty(rb) && (new_pt == mmu->defaultpagetable)) return true; @@ -360,8 +361,7 @@ static unsigned int _adreno_mmu_set_pt_update_condition( */ *cmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); cmds += cp_gpuaddr(adreno_dev, cmds, (rb->pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, - switch_pt_enable))); + PT_INFO_OFFSET(switch_pt_enable))); *cmds++ = 1; *cmds++ = cp_packet(adreno_dev, CP_WAIT_MEM_WRITES, 1); *cmds++ = 0; @@ -375,14 +375,11 @@ static unsigned int _adreno_mmu_set_pt_update_condition( *cmds++ = (1 << 8) | (1 << 4) | 3; cmds += cp_gpuaddr(adreno_dev, cmds, (adreno_dev->ringbuffers[0].pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, - current_global_ptname))); + PT_INFO_OFFSET(current_global_ptname))); *cmds++ = ptname; *cmds++ = 0xFFFFFFFF; - cmds += cp_gpuaddr(adreno_dev, cmds, - (rb->pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, - switch_pt_enable))); + cmds += cp_gpuaddr(adreno_dev, cmds, (rb->pagetable_desc.gpuaddr + + PT_INFO_OFFSET(switch_pt_enable))); *cmds++ = 0; *cmds++ = cp_packet(adreno_dev, CP_WAIT_MEM_WRITES, 1); *cmds++ = 0; @@ -406,23 +403,18 @@ static unsigned int _adreno_iommu_pt_update_pid_to_mem( unsigned int *cmds_orig = cmds; *cmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); - cmds += cp_gpuaddr(adreno_dev, cmds, - (rb->pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, - current_rb_ptname))); + cmds += cp_gpuaddr(adreno_dev, cmds, (rb->pagetable_desc.gpuaddr + + PT_INFO_OFFSET(current_rb_ptname))); *cmds++ = ptname; *cmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); cmds += cp_gpuaddr(adreno_dev, cmds, - (adreno_dev->ringbuffers[0].pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, - current_global_ptname))); + (adreno_dev->ringbuffers[0].pagetable_desc.gpuaddr + + PT_INFO_OFFSET(current_global_ptname))); *cmds++ = ptname; /* pagetable switch done, Housekeeping: set the switch_pt_enable to 0 */ *cmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); - cmds += cp_gpuaddr(adreno_dev, cmds, - (rb->pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, - switch_pt_enable))); + cmds += cp_gpuaddr(adreno_dev, cmds, (rb->pagetable_desc.gpuaddr + + PT_INFO_OFFSET(switch_pt_enable))); *cmds++ = 0; *cmds++ = cp_packet(adreno_dev, CP_WAIT_MEM_WRITES, 1); *cmds++ = 0; @@ -444,14 +436,10 @@ static unsigned int _adreno_iommu_set_pt_v1(struct adreno_ringbuffer *rb, /* set flag that indicates whether pt switch is required*/ cmds += _adreno_mmu_set_pt_update_condition(rb, cmds, ptname); *cmds++ = cp_mem_packet(adreno_dev, CP_COND_EXEC, 4, 2); - cmds += cp_gpuaddr(adreno_dev, cmds, - (rb->pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, - switch_pt_enable))); - cmds += cp_gpuaddr(adreno_dev, cmds, - (rb->pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, - switch_pt_enable))); + cmds += cp_gpuaddr(adreno_dev, cmds, (rb->pagetable_desc.gpuaddr + + PT_INFO_OFFSET(switch_pt_enable))); + cmds += cp_gpuaddr(adreno_dev, cmds, (rb->pagetable_desc.gpuaddr + + PT_INFO_OFFSET(switch_pt_enable))); *cmds++ = 1; /* Exec count to be filled later */ cond_exec_ptr = cmds; @@ -566,7 +554,7 @@ static unsigned int _adreno_iommu_set_pt_v2_a5xx(struct kgsl_device *device, *cmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 4, 1); cmds += cp_gpuaddr(adreno_dev, cmds, (rb->pagetable_desc.gpuaddr + - offsetof(struct adreno_ringbuffer_pagetable_info, ttbr0))); + PT_INFO_OFFSET(ttbr0))); *cmds++ = lower_32_bits(ttbr0); *cmds++ = upper_32_bits(ttbr0); *cmds++ = contextidr; @@ -651,14 +639,14 @@ static unsigned int __add_curr_ctxt_cmds(struct adreno_ringbuffer *rb, *cmds++ = KGSL_CONTEXT_TO_MEM_IDENTIFIER; *cmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); - cmds += cp_gpuaddr(adreno_dev, cmds, device->memstore.gpuaddr + - KGSL_MEMSTORE_RB_OFFSET(rb, current_context)); + cmds += cp_gpuaddr(adreno_dev, cmds, + MEMSTORE_RB_GPU_ADDR(device, rb, current_context)); *cmds++ = (drawctxt ? drawctxt->base.id : 0); *cmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); - cmds += cp_gpuaddr(adreno_dev, cmds, device->memstore.gpuaddr + - KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, - current_context)); + cmds += cp_gpuaddr(adreno_dev, cmds, + MEMSTORE_ID_GPU_ADDR(device, + KGSL_MEMSTORE_GLOBAL, current_context)); *cmds++ = (drawctxt ? drawctxt->base.id : 0); /* Invalidate UCHE for new context */ @@ -706,7 +694,7 @@ static void _set_ctxt_cpu(struct adreno_ringbuffer *rb, } /* Update rb memstore with current context */ kgsl_sharedmem_writel(device, &device->memstore, - KGSL_MEMSTORE_RB_OFFSET(rb, current_context), + MEMSTORE_RB_OFFSET(rb, current_context), drawctxt ? drawctxt->base.id : 0); } @@ -746,26 +734,11 @@ static int _set_pagetable_cpu(struct adreno_ringbuffer *rb, if (result) return result; /* write the new pt set to memory var */ - kgsl_sharedmem_writel(device, - &adreno_dev->ringbuffers[0].pagetable_desc, - offsetof( - struct adreno_ringbuffer_pagetable_info, - current_global_ptname), new_pt->name); + adreno_ringbuffer_set_global(adreno_dev, new_pt->name); } /* Update the RB pagetable info here */ - kgsl_sharedmem_writel(device, &rb->pagetable_desc, - offsetof( - struct adreno_ringbuffer_pagetable_info, - current_rb_ptname), new_pt->name); - kgsl_sharedmem_writeq(device, &rb->pagetable_desc, - offsetof( - struct adreno_ringbuffer_pagetable_info, - ttbr0), kgsl_mmu_pagetable_get_ttbr0(new_pt)); - kgsl_sharedmem_writel(device, &rb->pagetable_desc, - offsetof( - struct adreno_ringbuffer_pagetable_info, - contextidr), kgsl_mmu_pagetable_get_contextidr(new_pt)); + adreno_ringbuffer_set_pagetable(rb, new_pt); return 0; } @@ -795,8 +768,6 @@ static int _set_pagetable_gpu(struct adreno_ringbuffer *rb, return 0; } - kgsl_mmu_enable_clk(KGSL_MMU(adreno_dev)); - cmds += adreno_iommu_set_pt_generate_cmds(rb, cmds, new_pt); if ((unsigned int) (cmds - link) > (PAGE_SIZE / sizeof(unsigned int))) { @@ -812,16 +783,6 @@ static int _set_pagetable_gpu(struct adreno_ringbuffer *rb, KGSL_CMD_FLAGS_PMODE, link, (unsigned int)(cmds - link)); - /* - * On error disable the IOMMU clock right away otherwise turn it off - * after the command has been retired - */ - if (result) - kgsl_mmu_disable_clk(KGSL_MMU(adreno_dev)); - else - adreno_ringbuffer_mmu_disable_clk_on_ts(KGSL_DEVICE(adreno_dev), - rb, rb->timestamp); - kfree(link); return result; } @@ -886,7 +847,8 @@ int adreno_iommu_init(struct adreno_device *adreno_dev) */ int adreno_iommu_set_pt_ctx(struct adreno_ringbuffer *rb, struct kgsl_pagetable *new_pt, - struct adreno_context *drawctxt) + struct adreno_context *drawctxt, + unsigned long flags) { struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); struct kgsl_device *device = KGSL_DEVICE(adreno_dev); @@ -897,7 +859,8 @@ int adreno_iommu_set_pt_ctx(struct adreno_ringbuffer *rb, if (rb->drawctxt_active) cur_pt = rb->drawctxt_active->base.proc_priv->pagetable; - cpu_path = _ctx_switch_use_cpu_path(adreno_dev, new_pt, rb); + cpu_path = !(flags & ADRENO_CONTEXT_SWITCH_FORCE_GPU) && + _ctx_switch_use_cpu_path(adreno_dev, new_pt, rb); /* Pagetable switch */ if (new_pt != cur_pt) { @@ -907,10 +870,8 @@ int adreno_iommu_set_pt_ctx(struct adreno_ringbuffer *rb, result = _set_pagetable_gpu(rb, new_pt); } - if (result) { - KGSL_DRV_ERR(device, "Error switching pagetable %d\n", result); + if (result) return result; - } /* Context switch */ if (cpu_path) @@ -918,8 +879,5 @@ int adreno_iommu_set_pt_ctx(struct adreno_ringbuffer *rb, else result = _set_ctxt_gpu(rb, drawctxt); - if (result) - KGSL_DRV_ERR(device, "Error switching context %d\n", result); - return result; } diff --git a/drivers/gpu/msm/adreno_iommu.h b/drivers/gpu/msm/adreno_iommu.h index c557c65bb4c9..5a6c2c549370 100644 --- a/drivers/gpu/msm/adreno_iommu.h +++ b/drivers/gpu/msm/adreno_iommu.h @@ -17,7 +17,8 @@ #ifdef CONFIG_QCOM_KGSL_IOMMU int adreno_iommu_set_pt_ctx(struct adreno_ringbuffer *rb, struct kgsl_pagetable *new_pt, - struct adreno_context *drawctxt); + struct adreno_context *drawctxt, + unsigned long flags); int adreno_iommu_init(struct adreno_device *adreno_dev); @@ -33,7 +34,8 @@ static inline int adreno_iommu_init(struct adreno_device *adreno_dev) static inline int adreno_iommu_set_pt_ctx(struct adreno_ringbuffer *rb, struct kgsl_pagetable *new_pt, - struct adreno_context *drawctxt) + struct adreno_context *drawctxt, + unsigned long flags) { return 0; } diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index dceb8fb93461..0160939e97f9 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -30,8 +30,6 @@ #include "a3xx_reg.h" #include "adreno_a5xx.h" -#define GSL_RB_NOP_SIZEDWORDS 2 - #define RB_HOSTPTR(_rb, _pos) \ ((unsigned int *) ((_rb)->buffer_desc.hostptr + \ ((_pos) * sizeof(unsigned int)))) @@ -50,86 +48,89 @@ static void _cff_write_ringbuffer(struct adreno_ringbuffer *rb) if (device->cff_dump_enable == 0) return; - /* - * This code is predicated on the fact that we write a full block of - * stuff without wrapping - */ - BUG_ON(rb->wptr < rb->last_wptr); - - size = (rb->wptr - rb->last_wptr) * sizeof(unsigned int); + size = (rb->_wptr - rb->last_wptr) * sizeof(unsigned int); hostptr = RB_HOSTPTR(rb, rb->last_wptr); gpuaddr = RB_GPUADDR(rb, rb->last_wptr); kgsl_cffdump_memcpy(device, gpuaddr, hostptr, size); + rb->last_wptr = rb->_wptr; } -void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb, +static void adreno_get_submit_time(struct adreno_device *adreno_dev, struct adreno_submit_time *time) { - struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); - BUG_ON(rb->wptr == 0); - - /* Write the changes to CFF if so enabled */ - _cff_write_ringbuffer(rb); - + unsigned long flags; /* - * Read the current GPU ticks and wallclock for most accurate - * profiling + * Here we are attempting to create a mapping between the + * GPU time domain (alwayson counter) and the CPU time domain + * (local_clock) by sampling both values as close together as + * possible. This is useful for many types of debugging and + * profiling. In order to make this mapping as accurate as + * possible, we must turn off interrupts to avoid running + * interrupt handlers between the two samples. */ - if (time != NULL) { - /* - * Here we are attempting to create a mapping between the - * GPU time domain (alwayson counter) and the CPU time domain - * (local_clock) by sampling both values as close together as - * possible. This is useful for many types of debugging and - * profiling. In order to make this mapping as accurate as - * possible, we must turn off interrupts to avoid running - * interrupt handlers between the two samples. - */ - unsigned long flags; - local_irq_save(flags); + local_irq_save(flags); - /* Read always on registers */ - if (!adreno_is_a3xx(adreno_dev)) { - adreno_readreg64(adreno_dev, - ADRENO_REG_RBBM_ALWAYSON_COUNTER_LO, - ADRENO_REG_RBBM_ALWAYSON_COUNTER_HI, - &time->ticks); + /* Read always on registers */ + if (!adreno_is_a3xx(adreno_dev)) { + adreno_readreg64(adreno_dev, + ADRENO_REG_RBBM_ALWAYSON_COUNTER_LO, + ADRENO_REG_RBBM_ALWAYSON_COUNTER_HI, + &time->ticks); - /* - * Mask hi bits as they may be incorrect on - * a4x and some a5x - */ - if (ADRENO_GPUREV(adreno_dev) >= 400 && + /* Mask hi bits as they may be incorrect on some targets */ + if (ADRENO_GPUREV(adreno_dev) >= 400 && ADRENO_GPUREV(adreno_dev) <= ADRENO_REV_A530) - time->ticks &= 0xFFFFFFFF; - } - else - time->ticks = 0; + time->ticks &= 0xFFFFFFFF; + } else + time->ticks = 0; - /* Get the kernel clock for time since boot */ - time->ktime = local_clock(); + /* Get the kernel clock for time since boot */ + time->ktime = local_clock(); - /* Get the timeofday for the wall time (for the user) */ - getnstimeofday(&time->utime); + /* Get the timeofday for the wall time (for the user) */ + getnstimeofday(&time->utime); - local_irq_restore(flags); - } + local_irq_restore(flags); +} + +void adreno_ringbuffer_wptr(struct adreno_device *adreno_dev, + struct adreno_ringbuffer *rb) +{ + unsigned long flags; - /* Memory barrier before informing the hardware of new commands */ - mb(); + spin_lock_irqsave(&rb->preempt_lock, flags); + if (adreno_in_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE)) { - if (adreno_preempt_state(adreno_dev, ADRENO_DISPATCHER_PREEMPT_CLEAR) && - (adreno_dev->cur_rb == rb)) { - /* - * Let the pwrscale policy know that new commands have - * been submitted. - */ - kgsl_pwrscale_busy(KGSL_DEVICE(adreno_dev)); - adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, rb->wptr); + if (adreno_dev->cur_rb == rb) { + /* + * Let the pwrscale policy know that new commands have + * been submitted. + */ + kgsl_pwrscale_busy(KGSL_DEVICE(adreno_dev)); + adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, + rb->_wptr); + } } + + rb->wptr = rb->_wptr; + spin_unlock_irqrestore(&rb->preempt_lock, flags); +} + +void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb, + struct adreno_submit_time *time) +{ + struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); + + /* Write the changes to CFF if so enabled */ + _cff_write_ringbuffer(rb); + + if (time != NULL) + adreno_get_submit_time(adreno_dev, time); + + adreno_ringbuffer_wptr(adreno_dev, rb); } int adreno_ringbuffer_submit_spin(struct adreno_ringbuffer *rb, @@ -141,125 +142,36 @@ int adreno_ringbuffer_submit_spin(struct adreno_ringbuffer *rb, return adreno_spin_idle(adreno_dev, timeout); } -static int -adreno_ringbuffer_waitspace(struct adreno_ringbuffer *rb, - unsigned int numcmds, int wptr_ahead) +unsigned int *adreno_ringbuffer_allocspace(struct adreno_ringbuffer *rb, + unsigned int dwords) { - int nopcount = 0; - unsigned int freecmds; - unsigned int wptr = rb->wptr; - unsigned int *cmds = NULL; - uint64_t gpuaddr; - unsigned long wait_time; - unsigned long wait_timeout = msecs_to_jiffies(ADRENO_IDLE_TIMEOUT); - unsigned int rptr; struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb); + unsigned int rptr = adreno_get_rptr(rb); + unsigned int ret; - /* if wptr ahead, fill the remaining with NOPs */ - if (wptr_ahead) { - /* -1 for header */ - nopcount = KGSL_RB_DWORDS - rb->wptr - 1; - - cmds = RB_HOSTPTR(rb, rb->wptr); - gpuaddr = RB_GPUADDR(rb, rb->wptr); - - rptr = adreno_get_rptr(rb); - /* For non current rb we don't expect the rptr to move */ - if ((adreno_dev->cur_rb != rb || - !adreno_preempt_state(adreno_dev, - ADRENO_DISPATCHER_PREEMPT_CLEAR)) && - !rptr) - return -ENOSPC; - - /* Make sure that rptr is not 0 before submitting - * commands at the end of ringbuffer. We do not - * want the rptr and wptr to become equal when - * the ringbuffer is not empty */ - wait_time = jiffies + wait_timeout; - while (!rptr) { - rptr = adreno_get_rptr(rb); - if (time_after(jiffies, wait_time)) - return -ETIMEDOUT; - } - - rb->wptr = 0; - } - - rptr = adreno_get_rptr(rb); - freecmds = rptr - rb->wptr; - if (freecmds == 0 || freecmds > numcmds) - goto done; + if (rptr <= rb->_wptr) { + unsigned int *cmds; - /* non current rptr will not advance anyway or if preemption underway */ - if (adreno_dev->cur_rb != rb || - !adreno_preempt_state(adreno_dev, - ADRENO_DISPATCHER_PREEMPT_CLEAR)) { - rb->wptr = wptr; - return -ENOSPC; - } - - wait_time = jiffies + wait_timeout; - /* wait for space in ringbuffer */ - while (1) { - rptr = adreno_get_rptr(rb); - - freecmds = rptr - rb->wptr; - - if (freecmds == 0 || freecmds > numcmds) - break; - - if (time_after(jiffies, wait_time)) { - KGSL_DRV_ERR(KGSL_DEVICE(adreno_dev), - "Timed out waiting for freespace in RB rptr: 0x%x, wptr: 0x%x, rb id %d\n", - rptr, wptr, rb->id); - return -ETIMEDOUT; + if (rb->_wptr + dwords <= (KGSL_RB_DWORDS - 2)) { + ret = rb->_wptr; + rb->_wptr = (rb->_wptr + dwords) % KGSL_RB_DWORDS; + return RB_HOSTPTR(rb, ret); } - } -done: - if (wptr_ahead) { - *cmds = cp_packet(adreno_dev, CP_NOP, nopcount); - kgsl_cffdump_write(KGSL_DEVICE(adreno_dev), gpuaddr, *cmds); - } - return 0; -} + cmds = RB_HOSTPTR(rb, rb->_wptr); + *cmds = cp_packet(adreno_dev, CP_NOP, + KGSL_RB_DWORDS - rb->_wptr - 1); -unsigned int *adreno_ringbuffer_allocspace(struct adreno_ringbuffer *rb, - unsigned int numcmds) -{ - unsigned int *ptr = NULL; - int ret = 0; - unsigned int rptr; - BUG_ON(numcmds >= KGSL_RB_DWORDS); - - rptr = adreno_get_rptr(rb); - /* check for available space */ - if (rb->wptr >= rptr) { - /* wptr ahead or equal to rptr */ - /* reserve dwords for nop packet */ - if ((rb->wptr + numcmds) > (KGSL_RB_DWORDS - - GSL_RB_NOP_SIZEDWORDS)) - ret = adreno_ringbuffer_waitspace(rb, numcmds, 1); - } else { - /* wptr behind rptr */ - if ((rb->wptr + numcmds) >= rptr) - ret = adreno_ringbuffer_waitspace(rb, numcmds, 0); - /* check for remaining space */ - /* reserve dwords for nop packet */ - if (!ret && (rb->wptr + numcmds) > (KGSL_RB_DWORDS - - GSL_RB_NOP_SIZEDWORDS)) - ret = adreno_ringbuffer_waitspace(rb, numcmds, 1); + rb->_wptr = 0; } - if (!ret) { - rb->last_wptr = rb->wptr; - - ptr = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr; - rb->wptr += numcmds; - } else - ptr = ERR_PTR(ret); + if (rb->_wptr + dwords < rptr) { + ret = rb->_wptr; + rb->_wptr = (rb->_wptr + dwords) % KGSL_RB_DWORDS; + return RB_HOSTPTR(rb, ret); + } - return ptr; + return ERR_PTR(-ENOSPC); } /** @@ -279,8 +191,10 @@ int adreno_ringbuffer_start(struct adreno_device *adreno_dev, FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { kgsl_sharedmem_set(device, &(rb->buffer_desc), 0, 0xAA, KGSL_RB_SIZE); + kgsl_sharedmem_writel(device, &device->scratch, + SCRATCH_RPTR_OFFSET(rb->id), 0); rb->wptr = 0; - rb->rptr = 0; + rb->_wptr = 0; rb->wptr_preempt_end = 0xFFFFFFFF; rb->starve_timer_state = ADRENO_DISPATCHER_RB_STARVE_TIMER_UNINIT; @@ -322,6 +236,8 @@ static int _adreno_ringbuffer_probe(struct adreno_device *adreno_dev, rb->timestamp = 0; init_waitqueue_head(&rb->ts_expire_waitq); + spin_lock_init(&rb->preempt_lock); + /* * Allocate mem for storing RB pagetables and commands to * switch pagetable @@ -433,6 +349,18 @@ int cp_secure_mode(struct adreno_device *adreno_dev, uint *cmds, return cmds - start; } +static inline int cp_mem_write(struct adreno_device *adreno_dev, + unsigned int *cmds, uint64_t gpuaddr, unsigned int value) +{ + int dwords = 0; + + cmds[dwords++] = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); + dwords += cp_gpuaddr(adreno_dev, &cmds[dwords], gpuaddr); + cmds[dwords++] = value; + + return dwords; +} + static int adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, unsigned int flags, unsigned int *cmds, @@ -446,18 +374,20 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, unsigned int total_sizedwords = sizedwords; unsigned int i; unsigned int context_id = 0; - uint64_t gpuaddr = device->memstore.gpuaddr; bool profile_ready; struct adreno_context *drawctxt = rb->drawctxt_active; struct kgsl_context *context = NULL; bool secured_ctxt = false; - uint64_t cond_addr; static unsigned int _seq_cnt; if (drawctxt != NULL && kgsl_context_detached(&drawctxt->base) && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) return -ENOENT; + /* On fault return error so that we don't keep submitting */ + if (adreno_gpu_fault(adreno_dev) != 0) + return -EPROTO; + rb->timestamp++; /* If this is a internal IB, use the global timestamp for it */ @@ -529,7 +459,7 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, * required in ringbuffer and adjust the write pointer depending on * gpucore at the end of this function. */ - total_sizedwords += 4; /* sop timestamp */ + total_sizedwords += 8; /* sop timestamp */ total_sizedwords += 5; /* eop timestamp */ if (drawctxt && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) { @@ -564,14 +494,9 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, *ringcmds++ = KGSL_CMD_IDENTIFIER; if (adreno_is_preemption_enabled(adreno_dev) && - gpudev->preemption_pre_ibsubmit) { - cond_addr = device->memstore.gpuaddr + - KGSL_MEMSTORE_OFFSET(context_id, - preempted); + gpudev->preemption_pre_ibsubmit) ringcmds += gpudev->preemption_pre_ibsubmit( - adreno_dev, rb, ringcmds, context, - cond_addr, NULL); - } + adreno_dev, rb, ringcmds, context); if (flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE) { *ringcmds++ = cp_packet(adreno_dev, CP_NOP, 1); @@ -601,16 +526,15 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, adreno_profile_preib_processing(adreno_dev, drawctxt, &flags, &ringcmds); - /* start-of-pipeline timestamp */ - *ringcmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); + /* start-of-pipeline timestamp for the context */ if (drawctxt && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) - ringcmds += cp_gpuaddr(adreno_dev, ringcmds, - gpuaddr + KGSL_MEMSTORE_OFFSET(context_id, - soptimestamp)); - else - ringcmds += cp_gpuaddr(adreno_dev, ringcmds, - gpuaddr + KGSL_MEMSTORE_RB_OFFSET(rb, soptimestamp)); - *ringcmds++ = timestamp; + ringcmds += cp_mem_write(adreno_dev, ringcmds, + MEMSTORE_ID_GPU_ADDR(device, context_id, soptimestamp), + timestamp); + + /* start-of-pipeline timestamp for the ringbuffer */ + ringcmds += cp_mem_write(adreno_dev, ringcmds, + MEMSTORE_RB_GPU_ADDR(device, rb, soptimestamp), rb->timestamp); if (secured_ctxt) ringcmds += cp_secure_mode(adreno_dev, ringcmds, 1); @@ -659,11 +583,9 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, * early detection of timestamp interrupt storms to stave * off system collapse. */ - *ringcmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); - ringcmds += cp_gpuaddr(adreno_dev, ringcmds, gpuaddr + - KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, - ref_wait_ts)); - *ringcmds++ = ++_seq_cnt; + ringcmds += cp_mem_write(adreno_dev, ringcmds, + MEMSTORE_ID_GPU_ADDR(device, KGSL_MEMSTORE_GLOBAL, + ref_wait_ts), ++_seq_cnt); /* * end-of-pipeline timestamp. If per context timestamps is not @@ -677,16 +599,17 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, *ringcmds++ = CACHE_FLUSH_TS; if (drawctxt && !(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE)) { - ringcmds += cp_gpuaddr(adreno_dev, ringcmds, gpuaddr + - KGSL_MEMSTORE_OFFSET(context_id, eoptimestamp)); + ringcmds += cp_gpuaddr(adreno_dev, ringcmds, + MEMSTORE_ID_GPU_ADDR(device, context_id, eoptimestamp)); *ringcmds++ = timestamp; - *ringcmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 1); - ringcmds += cp_gpuaddr(adreno_dev, ringcmds, gpuaddr + - KGSL_MEMSTORE_RB_OFFSET(rb, eoptimestamp)); - *ringcmds++ = rb->timestamp; + + /* Write the end of pipeline timestamp to the ringbuffer too */ + ringcmds += cp_mem_write(adreno_dev, ringcmds, + MEMSTORE_RB_GPU_ADDR(device, rb, eoptimestamp), + rb->timestamp); } else { - ringcmds += cp_gpuaddr(adreno_dev, ringcmds, gpuaddr + - KGSL_MEMSTORE_RB_OFFSET(rb, eoptimestamp)); + ringcmds += cp_gpuaddr(adreno_dev, ringcmds, + MEMSTORE_RB_GPU_ADDR(device, rb, eoptimestamp)); *ringcmds++ = timestamp; } @@ -707,8 +630,8 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, if (gpudev->preemption_post_ibsubmit && adreno_is_preemption_enabled(adreno_dev)) - ringcmds += gpudev->preemption_post_ibsubmit(adreno_dev, rb, - ringcmds, &drawctxt->base); + ringcmds += gpudev->preemption_post_ibsubmit(adreno_dev, + ringcmds); /* * If we have more ringbuffer commands than space reserved @@ -722,7 +645,7 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, * required. If we have commands less than the space reserved in RB * adjust the wptr accordingly. */ - rb->wptr = rb->wptr - (total_sizedwords - (ringcmds - start)); + rb->_wptr = rb->_wptr - (total_sizedwords - (ringcmds - start)); adreno_ringbuffer_submit(rb, time); @@ -1063,14 +986,24 @@ int adreno_ringbuffer_submitcmd(struct adreno_device *adreno_dev, *cmds++ = cp_packet(adreno_dev, CP_NOP, 1); *cmds++ = KGSL_END_OF_IB_IDENTIFIER; - ret = adreno_drawctxt_switch(adreno_dev, rb, drawctxt, cmdbatch->flags); + /* Context switches commands should *always* be on the GPU */ + ret = adreno_drawctxt_switch(adreno_dev, rb, drawctxt, + ADRENO_CONTEXT_SWITCH_FORCE_GPU); /* * In the unlikely event of an error in the drawctxt switch, * treat it like a hang */ - if (ret) + if (ret) { + /* + * It is "normal" to get a -ENOSPC or a -ENOENT. Don't log it, + * the upper layers know how to handle it + */ + if (ret != -ENOSPC && ret != -ENOENT) + KGSL_DRV_ERR(device, + "Unable to switch draw context: %d\n", ret); goto done; + } if (test_bit(CMDBATCH_FLAG_WFI, &cmdbatch->priv)) flags = KGSL_CMD_FLAGS_WFI; @@ -1138,44 +1071,6 @@ done: } /** - * adreno_ringbuffer_mmu_clk_disable_event() - Callback function that - * disables the MMU clocks. - * @device: Device pointer - * @context: The ringbuffer context pointer - * @data: Pointer containing the adreno_mmu_disable_clk_param structure - * @type: The event call type (RETIRED or CANCELLED) - */ -static void adreno_ringbuffer_mmu_clk_disable_event(struct kgsl_device *device, - struct kgsl_event_group *group, void *data, int type) -{ - kgsl_mmu_disable_clk(&device->mmu); -} - -/* - * adreno_ringbuffer_mmu_disable_clk_on_ts() - Sets up event to disable MMU - * clocks - * @device - The kgsl device pointer - * @rb: The ringbuffer in whose event list the event is added - * @timestamp: The timestamp on which the event should trigger - * - * Creates an event to disable the MMU clocks on timestamp and if event - * already exists then updates the timestamp of disabling the MMU clocks - * with the passed in ts if it is greater than the current value at which - * the clocks will be disabled - * Return - void - */ -void -adreno_ringbuffer_mmu_disable_clk_on_ts(struct kgsl_device *device, - struct adreno_ringbuffer *rb, unsigned int timestamp) -{ - if (kgsl_add_event(device, &(rb->events), timestamp, - adreno_ringbuffer_mmu_clk_disable_event, NULL)) { - KGSL_DRV_ERR(device, - "Failed to add IOMMU disable clk event\n"); - } -} - -/** * adreno_ringbuffer_wait_callback() - Callback function for event registered * on a ringbuffer timestamp * @device: Device for which the the callback is valid diff --git a/drivers/gpu/msm/adreno_ringbuffer.h b/drivers/gpu/msm/adreno_ringbuffer.h index f1980fd92961..b126f710b5e6 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.h +++ b/drivers/gpu/msm/adreno_ringbuffer.h @@ -73,13 +73,16 @@ struct adreno_ringbuffer_pagetable_info { unsigned int contextidr; }; +#define PT_INFO_OFFSET(_field) \ + offsetof(struct adreno_ringbuffer_pagetable_info, _field) + /** * struct adreno_ringbuffer - Definition for an adreno ringbuffer object * @flags: Internal control flags for the ringbuffer - * @buffer_desc: Pointer to the ringbuffer memory descriptor - * @wptr: Local copy of the wptr offset - * @rptr: Read pointer offset in dwords from baseaddr - * @last_wptr: offset of the last H/W committed wptr + * @buffer_desc: Pointer to the ringbuffer memory descripto + * @_wptr: The next value of wptr to be written to the hardware on submit + * @wptr: Local copy of the wptr offset last written to hardware + * @last_wptr: offset of the last wptr that was written to CFF * @rb_ctx: The context that represents a ringbuffer * @id: Priority level of the ringbuffer, also used as an ID * @fault_detect_ts: The last retired global timestamp read during fault detect @@ -101,12 +104,13 @@ struct adreno_ringbuffer_pagetable_info { * @sched_timer: Timer that tracks how long RB has been waiting to be scheduled * or how long it has been scheduled for after preempting in * @starve_timer_state: Indicates the state of the wait. + * @preempt_lock: Lock to protect the wptr pointer while it is being updated */ struct adreno_ringbuffer { uint32_t flags; struct kgsl_memdesc buffer_desc; + unsigned int _wptr; unsigned int wptr; - unsigned int rptr; unsigned int last_wptr; int id; unsigned int fault_detect_ts; @@ -122,14 +126,12 @@ struct adreno_ringbuffer { int preempted_midway; unsigned long sched_timer; enum adreno_dispatcher_starve_timer_states starve_timer_state; + spinlock_t preempt_lock; }; /* Returns the current ringbuffer */ #define ADRENO_CURRENT_RINGBUFFER(a) ((a)->cur_rb) -#define KGSL_MEMSTORE_RB_OFFSET(rb, field) \ - KGSL_MEMSTORE_OFFSET((rb->id + KGSL_MEMSTORE_MAX), field) - int cp_secure_mode(struct adreno_device *adreno_dev, uint *cmds, int set); int adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv, @@ -170,9 +172,6 @@ void adreno_ringbuffer_read_pfp_ucode(struct kgsl_device *device); void adreno_ringbuffer_read_pm4_ucode(struct kgsl_device *device); -void adreno_ringbuffer_mmu_disable_clk_on_ts(struct kgsl_device *device, - struct adreno_ringbuffer *rb, unsigned int ts); - int adreno_ringbuffer_waittimestamp(struct adreno_ringbuffer *rb, unsigned int timestamp, unsigned int msecs); @@ -204,9 +203,10 @@ static inline unsigned int adreno_ringbuffer_dec_wrapped(unsigned int val, } static inline int adreno_ringbuffer_set_pt_ctx(struct adreno_ringbuffer *rb, - struct kgsl_pagetable *pt, struct adreno_context *context) + struct kgsl_pagetable *pt, struct adreno_context *context, + unsigned long flags) { - return adreno_iommu_set_pt_ctx(rb, pt, context); + return adreno_iommu_set_pt_ctx(rb, pt, context, flags); } #endif /* __ADRENO_RINGBUFFER_H */ diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c index ca61d36a1384..b069b16c75ef 100644 --- a/drivers/gpu/msm/adreno_snapshot.c +++ b/drivers/gpu/msm/adreno_snapshot.c @@ -467,7 +467,7 @@ static size_t snapshot_rb(struct kgsl_device *device, u8 *buf, header->start = 0; header->end = KGSL_RB_DWORDS; header->wptr = rb->wptr; - header->rptr = rb->rptr; + header->rptr = adreno_get_rptr(rb); header->rbsize = KGSL_RB_DWORDS; header->count = KGSL_RB_DWORDS; adreno_rb_readtimestamp(adreno_dev, rb, KGSL_TIMESTAMP_QUEUED, @@ -741,8 +741,7 @@ static size_t snapshot_global(struct kgsl_device *device, u8 *buf, header->size = memdesc->size >> 2; header->gpuaddr = memdesc->gpuaddr; - header->ptbase = - kgsl_mmu_pagetable_get_ttbr0(device->mmu.defaultpagetable); + header->ptbase = MMU_DEFAULT_TTBR0(device); header->type = SNAPSHOT_GPU_OBJECT_GLOBAL; memcpy(ptr, memdesc->hostptr, memdesc->size); diff --git a/drivers/gpu/msm/adreno_trace.h b/drivers/gpu/msm/adreno_trace.h index 5f1bbb9a83b3..f52ddfa894d5 100644 --- a/drivers/gpu/msm/adreno_trace.h +++ b/drivers/gpu/msm/adreno_trace.h @@ -55,8 +55,8 @@ TRACE_EVENT(adreno_cmdbatch_queued, TRACE_EVENT(adreno_cmdbatch_submitted, TP_PROTO(struct kgsl_cmdbatch *cmdbatch, int inflight, uint64_t ticks, unsigned long secs, unsigned long usecs, - struct adreno_ringbuffer *rb), - TP_ARGS(cmdbatch, inflight, ticks, secs, usecs, rb), + struct adreno_ringbuffer *rb, unsigned int rptr), + TP_ARGS(cmdbatch, inflight, ticks, secs, usecs, rb, rptr), TP_STRUCT__entry( __field(unsigned int, id) __field(unsigned int, timestamp) @@ -81,7 +81,7 @@ TRACE_EVENT(adreno_cmdbatch_submitted, __entry->usecs = usecs; __entry->prio = cmdbatch->context->priority; __entry->rb_id = rb->id; - __entry->rptr = rb->rptr; + __entry->rptr = rptr; __entry->wptr = rb->wptr; __entry->q_inflight = rb->dispatch_q.inflight; ), @@ -100,8 +100,8 @@ TRACE_EVENT(adreno_cmdbatch_submitted, TRACE_EVENT(adreno_cmdbatch_retired, TP_PROTO(struct kgsl_cmdbatch *cmdbatch, int inflight, uint64_t start, uint64_t retire, - struct adreno_ringbuffer *rb), - TP_ARGS(cmdbatch, inflight, start, retire, rb), + struct adreno_ringbuffer *rb, unsigned int rptr), + TP_ARGS(cmdbatch, inflight, start, retire, rb, rptr), TP_STRUCT__entry( __field(unsigned int, id) __field(unsigned int, timestamp) @@ -126,7 +126,7 @@ TRACE_EVENT(adreno_cmdbatch_retired, __entry->retire = retire; __entry->prio = cmdbatch->context->priority; __entry->rb_id = rb->id; - __entry->rptr = rb->rptr; + __entry->rptr = rptr; __entry->wptr = rb->wptr; __entry->q_inflight = rb->dispatch_q.inflight; ), @@ -267,9 +267,8 @@ TRACE_EVENT(adreno_drawctxt_wait_done, TRACE_EVENT(adreno_drawctxt_switch, TP_PROTO(struct adreno_ringbuffer *rb, - struct adreno_context *newctx, - unsigned int flags), - TP_ARGS(rb, newctx, flags), + struct adreno_context *newctx), + TP_ARGS(rb, newctx), TP_STRUCT__entry( __field(int, rb_level) __field(unsigned int, oldctx) @@ -283,8 +282,8 @@ TRACE_EVENT(adreno_drawctxt_switch, __entry->newctx = newctx ? newctx->base.id : 0; ), TP_printk( - "rb level=%d oldctx=%u newctx=%u flags=%X", - __entry->rb_level, __entry->oldctx, __entry->newctx, flags + "rb level=%d oldctx=%u newctx=%u", + __entry->rb_level, __entry->oldctx, __entry->newctx ) ); @@ -427,8 +426,9 @@ TRACE_EVENT(kgsl_a5xx_irq_status, DECLARE_EVENT_CLASS(adreno_hw_preempt_template, TP_PROTO(struct adreno_ringbuffer *cur_rb, - struct adreno_ringbuffer *new_rb), - TP_ARGS(cur_rb, new_rb), + struct adreno_ringbuffer *new_rb, + unsigned int cur_rptr, unsigned int new_rptr), + TP_ARGS(cur_rb, new_rb, cur_rptr, new_rptr), TP_STRUCT__entry(__field(int, cur_level) __field(int, new_level) __field(unsigned int, cur_rptr) @@ -440,8 +440,8 @@ DECLARE_EVENT_CLASS(adreno_hw_preempt_template, ), TP_fast_assign(__entry->cur_level = cur_rb->id; __entry->new_level = new_rb->id; - __entry->cur_rptr = cur_rb->rptr; - __entry->new_rptr = new_rb->rptr; + __entry->cur_rptr = cur_rptr; + __entry->new_rptr = new_rptr; __entry->cur_wptr = cur_rb->wptr; __entry->new_wptr = new_rb->wptr; __entry->cur_rbbase = cur_rb->buffer_desc.gpuaddr; @@ -458,26 +458,30 @@ DECLARE_EVENT_CLASS(adreno_hw_preempt_template, DEFINE_EVENT(adreno_hw_preempt_template, adreno_hw_preempt_clear_to_trig, TP_PROTO(struct adreno_ringbuffer *cur_rb, - struct adreno_ringbuffer *new_rb), - TP_ARGS(cur_rb, new_rb) + struct adreno_ringbuffer *new_rb, + unsigned int cur_rptr, unsigned int new_rptr), + TP_ARGS(cur_rb, new_rb, cur_rptr, new_rptr) ); DEFINE_EVENT(adreno_hw_preempt_template, adreno_hw_preempt_trig_to_comp, TP_PROTO(struct adreno_ringbuffer *cur_rb, - struct adreno_ringbuffer *new_rb), - TP_ARGS(cur_rb, new_rb) + struct adreno_ringbuffer *new_rb, + unsigned int cur_rptr, unsigned int new_rptr), + TP_ARGS(cur_rb, new_rb, cur_rptr, new_rptr) ); DEFINE_EVENT(adreno_hw_preempt_template, adreno_hw_preempt_trig_to_comp_int, TP_PROTO(struct adreno_ringbuffer *cur_rb, - struct adreno_ringbuffer *new_rb), - TP_ARGS(cur_rb, new_rb) + struct adreno_ringbuffer *new_rb, + unsigned int cur_rptr, unsigned int new_rptr), + TP_ARGS(cur_rb, new_rb, cur_rptr, new_rptr) ); TRACE_EVENT(adreno_hw_preempt_comp_to_clear, TP_PROTO(struct adreno_ringbuffer *cur_rb, - struct adreno_ringbuffer *new_rb), - TP_ARGS(cur_rb, new_rb), + struct adreno_ringbuffer *new_rb, + unsigned int cur_rptr, unsigned int new_rptr), + TP_ARGS(cur_rb, new_rb, cur_rptr, new_rptr), TP_STRUCT__entry(__field(int, cur_level) __field(int, new_level) __field(unsigned int, cur_rptr) @@ -490,8 +494,8 @@ TRACE_EVENT(adreno_hw_preempt_comp_to_clear, ), TP_fast_assign(__entry->cur_level = cur_rb->id; __entry->new_level = new_rb->id; - __entry->cur_rptr = cur_rb->rptr; - __entry->new_rptr = new_rb->rptr; + __entry->cur_rptr = cur_rptr; + __entry->new_rptr = new_rptr; __entry->cur_wptr = cur_rb->wptr; __entry->new_wptr_end = new_rb->wptr_preempt_end; __entry->new_wptr = new_rb->wptr; @@ -509,8 +513,9 @@ TRACE_EVENT(adreno_hw_preempt_comp_to_clear, TRACE_EVENT(adreno_hw_preempt_token_submit, TP_PROTO(struct adreno_ringbuffer *cur_rb, - struct adreno_ringbuffer *new_rb), - TP_ARGS(cur_rb, new_rb), + struct adreno_ringbuffer *new_rb, + unsigned int cur_rptr, unsigned int new_rptr), + TP_ARGS(cur_rb, new_rb, cur_rptr, new_rptr), TP_STRUCT__entry(__field(int, cur_level) __field(int, new_level) __field(unsigned int, cur_rptr) @@ -523,8 +528,8 @@ TRACE_EVENT(adreno_hw_preempt_token_submit, ), TP_fast_assign(__entry->cur_level = cur_rb->id; __entry->new_level = new_rb->id; - __entry->cur_rptr = cur_rb->rptr; - __entry->new_rptr = new_rb->rptr; + __entry->cur_rptr = cur_rptr; + __entry->new_rptr = new_rptr; __entry->cur_wptr = cur_rb->wptr; __entry->cur_wptr_end = cur_rb->wptr_preempt_end; __entry->new_wptr = new_rb->wptr; @@ -541,23 +546,37 @@ TRACE_EVENT(adreno_hw_preempt_token_submit, ) ); -TRACE_EVENT(adreno_rb_starve, - TP_PROTO(struct adreno_ringbuffer *rb), - TP_ARGS(rb), - TP_STRUCT__entry(__field(int, id) - __field(unsigned int, rptr) - __field(unsigned int, wptr) +TRACE_EVENT(adreno_preempt_trigger, + TP_PROTO(struct adreno_ringbuffer *cur, struct adreno_ringbuffer *next), + TP_ARGS(cur, next), + TP_STRUCT__entry( + __field(struct adreno_ringbuffer *, cur) + __field(struct adreno_ringbuffer *, next) ), - TP_fast_assign(__entry->id = rb->id; - __entry->rptr = rb->rptr; - __entry->wptr = rb->wptr; + TP_fast_assign( + __entry->cur = cur; + __entry->next = next; ), - TP_printk( - "rb %d r/w %x/%x starved", __entry->id, __entry->rptr, - __entry->wptr + TP_printk("trigger from id=%d to id=%d", + __entry->cur->id, __entry->next->id ) ); +TRACE_EVENT(adreno_preempt_done, + TP_PROTO(struct adreno_ringbuffer *cur, struct adreno_ringbuffer *next), + TP_ARGS(cur, next), + TP_STRUCT__entry( + __field(struct adreno_ringbuffer *, cur) + __field(struct adreno_ringbuffer *, next) + ), + TP_fast_assign( + __entry->cur = cur; + __entry->next = next; + ), + TP_printk("done switch to id=%d from id=%d", + __entry->next->id, __entry->cur->id + ) +); #endif /* _ADRENO_TRACE_H */ /* This part must be outside protection */ diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 2563591f376e..f77dbb7f20af 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -233,6 +233,8 @@ int kgsl_readtimestamp(struct kgsl_device *device, void *priv, } EXPORT_SYMBOL(kgsl_readtimestamp); +static long gpumem_free_entry(struct kgsl_mem_entry *entry); + /* Scheduled by kgsl_mem_entry_put_deferred() */ static void _deferred_put(struct work_struct *work) { @@ -247,10 +249,8 @@ kgsl_mem_entry_create(void) { struct kgsl_mem_entry *entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (entry != NULL) { + if (entry != NULL) kref_init(&entry->refcount); - INIT_WORK(&entry->work, _deferred_put); - } return entry; } @@ -1150,6 +1150,8 @@ static int kgsl_open_device(struct kgsl_device *device) atomic_inc(&device->active_cnt); kgsl_sharedmem_set(device, &device->memstore, 0, 0, device->memstore.size); + kgsl_sharedmem_set(device, &device->scratch, 0, 0, + device->scratch.size); result = device->ftbl->init(device); if (result) @@ -1855,7 +1857,10 @@ static long gpuobj_free_on_timestamp(struct kgsl_device_private *dev_priv, static void gpuobj_free_fence_func(void *priv) { - kgsl_mem_entry_put_deferred((struct kgsl_mem_entry *) priv); + struct kgsl_mem_entry *entry = priv; + + INIT_WORK(&entry->work, _deferred_put); + queue_work(kgsl_driver.mem_workqueue, &entry->work); } static long gpuobj_free_on_fence(struct kgsl_device_private *dev_priv, @@ -3910,11 +3915,13 @@ int kgsl_device_platform_probe(struct kgsl_device *device) status = kgsl_allocate_global(device, &device->memstore, KGSL_MEMSTORE_SIZE, 0, 0); - if (status != 0) { - KGSL_DRV_ERR(device, "kgsl_allocate_global failed %d\n", - status); + if (status != 0) goto error_close_mmu; - } + + status = kgsl_allocate_global(device, &device->scratch, + PAGE_SIZE, 0, 0); + if (status != 0) + goto error_free_memstore; /* * The default request type PM_QOS_REQ_ALL_CORES is @@ -3964,6 +3971,8 @@ int kgsl_device_platform_probe(struct kgsl_device *device) return 0; +error_free_memstore: + kgsl_free_global(device, &device->memstore); error_close_mmu: kgsl_mmu_close(device); error_pwrctrl_close: @@ -3990,6 +3999,8 @@ void kgsl_device_platform_remove(struct kgsl_device *device) idr_destroy(&device->context_idr); + kgsl_free_global(device, &device->scratch); + kgsl_free_global(device, &device->memstore); kgsl_mmu_close(device); @@ -4091,8 +4102,9 @@ static int __init kgsl_core_init(void) INIT_LIST_HEAD(&kgsl_driver.pagetable_list); kgsl_driver.workqueue = create_singlethread_workqueue("kgsl-workqueue"); - kgsl_driver.mem_workqueue = - create_singlethread_workqueue("kgsl-mementry"); + + kgsl_driver.mem_workqueue = alloc_workqueue("kgsl-mementry", + WQ_UNBOUND | WQ_MEM_RECLAIM, 0); kgsl_events_init(); diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index dfe83be799b3..c172021c8944 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -37,6 +37,32 @@ #define KGSL_MEMSTORE_MAX (KGSL_MEMSTORE_SIZE / \ sizeof(struct kgsl_devmemstore) - 1 - KGSL_PRIORITY_MAX_RB_LEVELS) +#define MEMSTORE_RB_OFFSET(rb, field) \ + KGSL_MEMSTORE_OFFSET(((rb)->id + KGSL_MEMSTORE_MAX), field) + +#define MEMSTORE_ID_GPU_ADDR(dev, iter, field) \ + ((dev)->memstore.gpuaddr + KGSL_MEMSTORE_OFFSET(iter, field)) + +#define MEMSTORE_RB_GPU_ADDR(dev, rb, field) \ + ((dev)->memstore.gpuaddr + \ + KGSL_MEMSTORE_OFFSET(((rb)->id + KGSL_MEMSTORE_MAX), field)) + +/* + * SCRATCH MEMORY: The scratch memory is one page worth of data that + * is mapped into the GPU. This allows for some 'shared' data between + * the GPU and CPU. For example, it will be used by the GPU to write + * each updated RPTR for each RB. + * + * Used Data: + * Offset: Length(bytes): What + * 0x0: 4 * KGSL_PRIORITY_MAX_RB_LEVELS: RB0 RPTR + */ + +/* Shadow global helpers */ +#define SCRATCH_RPTR_OFFSET(id) ((id) * sizeof(unsigned int)) +#define SCRATCH_RPTR_GPU_ADDR(dev, id) \ + ((dev)->scratch.gpuaddr + SCRATCH_RPTR_OFFSET(id)) + /* Timestamp window used to detect rollovers (half of integer range) */ #define KGSL_TIMESTAMP_WINDOW 0x80000000 @@ -447,21 +473,6 @@ kgsl_mem_entry_put(struct kgsl_mem_entry *entry) kref_put(&entry->refcount, kgsl_mem_entry_destroy); } -/** - * kgsl_mem_entry_put_deferred() - Schedule a task to put the memory entry - * @entry: Mem entry to put - * - * This function is for atomic contexts where a normal kgsl_mem_entry_put() - * would result in the memory entry getting destroyed and possibly taking - * mutexes along the way. Schedule the work to happen outside of the atomic - * context. - */ -static inline void kgsl_mem_entry_put_deferred(struct kgsl_mem_entry *entry) -{ - if (entry != NULL) - queue_work(kgsl_driver.mem_workqueue, &entry->work); -} - /* * kgsl_addr_range_overlap() - Checks if 2 ranges overlap * @gpuaddr1: Start of first address range diff --git a/drivers/gpu/msm/kgsl_cmdbatch.h b/drivers/gpu/msm/kgsl_cmdbatch.h index 1547ac02fdbf..d5cbf375b5d3 100644 --- a/drivers/gpu/msm/kgsl_cmdbatch.h +++ b/drivers/gpu/msm/kgsl_cmdbatch.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-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 @@ -31,7 +31,6 @@ * @fault_policy: Internal policy describing how to handle this command in case * of a fault * @fault_recovery: recovery actions actually tried for this batch - * @expires: Point in time when the cmdbatch is considered to be hung * @refcount: kref structure to maintain the reference count * @cmdlist: List of IBs to issue * @memlist: List of all memory used in this command batch @@ -61,7 +60,6 @@ struct kgsl_cmdbatch { unsigned long priv; unsigned long fault_policy; unsigned long fault_recovery; - unsigned long expires; struct kref refcount; struct list_head cmdlist; struct list_head memlist; diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index c3fb2b81fcbd..4159a5fe375f 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -227,6 +227,7 @@ struct kgsl_device { /* GPU shader memory size */ unsigned int shader_mem_len; struct kgsl_memdesc memstore; + struct kgsl_memdesc scratch; const char *iomemname; const char *shadermemname; diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c index e1f9ad17d0ff..6f70b9ddd376 100644 --- a/drivers/gpu/msm/kgsl_events.c +++ b/drivers/gpu/msm/kgsl_events.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-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 @@ -56,6 +56,23 @@ static void _kgsl_event_worker(struct work_struct *work) kmem_cache_free(events_cache, event); } +/* return true if the group needs to be processed */ +static bool _do_process_group(unsigned int processed, unsigned int cur) +{ + if (processed == cur) + return false; + + /* + * This ensures that the timestamp didn't slip back accidently, maybe + * due to a memory barrier issue. This is highly unlikely but we've + * been burned here in the past. + */ + if ((cur < processed) && ((processed - cur) < KGSL_TIMESTAMP_WINDOW)) + return false; + + return true; +} + static void _process_event_group(struct kgsl_device *device, struct kgsl_event_group *group, bool flush) { @@ -80,11 +97,7 @@ static void _process_event_group(struct kgsl_device *device, group->readtimestamp(device, group->priv, KGSL_TIMESTAMP_RETIRED, ×tamp); - /* - * If no timestamps have been retired since the last time we were here - * then we can avoid going through this loop - */ - if (!flush && timestamp_cmp(timestamp, group->processed) <= 0) + if (!flush && _do_process_group(group->processed, timestamp) == false) goto out; list_for_each_entry_safe(event, tmp, &group->events, node) { diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index a338559ac0bb..865cd9d8f498 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -96,6 +96,7 @@ static struct kgsl_memdesc *global_pt_entries[GLOBAL_PT_ENTRIES]; static struct kgsl_memdesc *kgsl_global_secure_pt_entry; static int global_pt_count; uint64_t global_pt_alloc; +static struct kgsl_memdesc gpu_qdss_desc; static void kgsl_iommu_unmap_globals(struct kgsl_pagetable *pagetable) { @@ -183,6 +184,51 @@ void kgsl_add_global_secure_entry(struct kgsl_device *device, kgsl_global_secure_pt_entry = memdesc; } +struct kgsl_memdesc *kgsl_iommu_get_qdss_global_entry(void) +{ + return &gpu_qdss_desc; +} + +static void kgsl_setup_qdss_desc(struct kgsl_device *device) +{ + int result = 0; + uint32_t gpu_qdss_entry[2]; + + if (!of_find_property(device->pdev->dev.of_node, + "qcom,gpu-qdss-stm", NULL)) + return; + + if (of_property_read_u32_array(device->pdev->dev.of_node, + "qcom,gpu-qdss-stm", gpu_qdss_entry, 2)) { + KGSL_CORE_ERR("Failed to read gpu qdss dts entry\n"); + return; + } + + gpu_qdss_desc.flags = 0; + gpu_qdss_desc.priv = 0; + gpu_qdss_desc.physaddr = gpu_qdss_entry[0]; + gpu_qdss_desc.size = gpu_qdss_entry[1]; + gpu_qdss_desc.pagetable = NULL; + gpu_qdss_desc.ops = NULL; + gpu_qdss_desc.dev = device->dev->parent; + gpu_qdss_desc.hostptr = NULL; + + result = memdesc_sg_dma(&gpu_qdss_desc, gpu_qdss_desc.physaddr, + gpu_qdss_desc.size); + if (result) { + KGSL_CORE_ERR("memdesc_sg_dma failed: %d\n", result); + return; + } + + kgsl_mmu_add_global(device, &gpu_qdss_desc); +} + +static inline void kgsl_cleanup_qdss_desc(struct kgsl_mmu *mmu) +{ + kgsl_iommu_remove_global(mmu, &gpu_qdss_desc); + kgsl_sharedmem_free(&gpu_qdss_desc); +} + static inline void _iommu_sync_mmu_pc(bool lock) { @@ -220,9 +266,6 @@ static int _attach_pt(struct kgsl_iommu_pt *iommu_pt, if (ret == 0) iommu_pt->attached = true; - else - KGSL_CORE_ERR("iommu_attach_device(%s) failed: %d\n", - ctx->name, ret); return ret; } @@ -1268,6 +1311,7 @@ static void kgsl_iommu_close(struct kgsl_mmu *mmu) kgsl_iommu_remove_global(mmu, &iommu->setstate); kgsl_sharedmem_free(&iommu->setstate); + kgsl_cleanup_qdss_desc(mmu); } static int _setstate_alloc(struct kgsl_device *device, @@ -1339,6 +1383,7 @@ static int kgsl_iommu_init(struct kgsl_mmu *mmu) } kgsl_iommu_add_global(mmu, &iommu->setstate); + kgsl_setup_qdss_desc(device); done: if (status) @@ -1452,25 +1497,25 @@ done: return ret; } +static int kgsl_iommu_set_pt(struct kgsl_mmu *mmu, struct kgsl_pagetable *pt); + static int kgsl_iommu_start(struct kgsl_mmu *mmu) { int status; struct kgsl_iommu *iommu = _IOMMU_PRIV(mmu); - struct kgsl_iommu_context *ctx = &iommu->ctx[KGSL_IOMMU_CONTEXT_USER]; status = _setup_user_context(mmu); if (status) return status; status = _setup_secure_context(mmu); - if (status) + if (status) { _detach_context(&iommu->ctx[KGSL_IOMMU_CONTEXT_USER]); - else { - kgsl_iommu_enable_clk(mmu); - KGSL_IOMMU_SET_CTX_REG(ctx, TLBIALL, 1); - kgsl_iommu_disable_clk(mmu); + return status; } - return status; + + /* Make sure the hardware is programmed to the default pagetable */ + return kgsl_iommu_set_pt(mmu, mmu->defaultpagetable); } static int @@ -1707,23 +1752,15 @@ kgsl_iommu_get_current_ttbr0(struct kgsl_mmu *mmu) * * Return - void */ -static int kgsl_iommu_set_pt(struct kgsl_mmu *mmu, - struct kgsl_pagetable *pt) +static int kgsl_iommu_set_pt(struct kgsl_mmu *mmu, struct kgsl_pagetable *pt) { - struct kgsl_device *device = KGSL_MMU_DEVICE(mmu); struct kgsl_iommu *iommu = _IOMMU_PRIV(mmu); struct kgsl_iommu_context *ctx = &iommu->ctx[KGSL_IOMMU_CONTEXT_USER]; - int ret = 0; uint64_t ttbr0, temp; unsigned int contextidr; unsigned long wait_for_flush; - /* - * If using a global pagetable, we can skip all this - * because the pagetable will be set up by the iommu - * driver and never changed at runtime. - */ - if (!kgsl_mmu_is_perprocess(mmu)) + if ((pt != mmu->defaultpagetable) && !kgsl_mmu_is_perprocess(mmu)) return 0; kgsl_iommu_enable_clk(mmu); @@ -1731,14 +1768,6 @@ static int kgsl_iommu_set_pt(struct kgsl_mmu *mmu, ttbr0 = kgsl_mmu_pagetable_get_ttbr0(pt); contextidr = kgsl_mmu_pagetable_get_contextidr(pt); - /* - * Taking the liberty to spin idle since this codepath - * is invoked when we can spin safely for it to be idle - */ - ret = adreno_spin_idle(ADRENO_DEVICE(device), ADRENO_IDLE_TIMEOUT); - if (ret) - return ret; - KGSL_IOMMU_SET_CTX_REG_Q(ctx, TTBR0, ttbr0); KGSL_IOMMU_SET_CTX_REG(ctx, CONTEXTIDR, contextidr); @@ -1767,10 +1796,8 @@ static int kgsl_iommu_set_pt(struct kgsl_mmu *mmu, cpu_relax(); } - /* Disable smmu clock */ kgsl_iommu_disable_clk(mmu); - - return ret; + return 0; } /* @@ -1788,8 +1815,6 @@ static int kgsl_iommu_set_pf_policy(struct kgsl_mmu *mmu, struct kgsl_iommu_context *ctx = &iommu->ctx[KGSL_IOMMU_CONTEXT_USER]; struct kgsl_device *device = KGSL_MMU_DEVICE(mmu); struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - int ret = 0; - unsigned int sctlr_val; if ((adreno_dev->ft_pf_policy & BIT(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE)) == @@ -1798,10 +1823,7 @@ static int kgsl_iommu_set_pf_policy(struct kgsl_mmu *mmu, /* If not attached, policy will be updated during the next attach */ if (ctx->default_pt != NULL) { - /* Need to idle device before changing options */ - ret = device->ftbl->idle(device); - if (ret) - return ret; + unsigned int sctlr_val; kgsl_iommu_enable_clk(mmu); @@ -1820,7 +1842,7 @@ static int kgsl_iommu_set_pf_policy(struct kgsl_mmu *mmu, kgsl_iommu_disable_clk(mmu); } - return ret; + return 0; } static struct kgsl_protected_registers * @@ -2367,6 +2389,7 @@ struct kgsl_mmu_ops kgsl_iommu_ops = { .mmu_add_global = kgsl_iommu_add_global, .mmu_remove_global = kgsl_iommu_remove_global, .mmu_getpagetable = kgsl_iommu_getpagetable, + .mmu_get_qdss_global_entry = kgsl_iommu_get_qdss_global_entry, .probe = kgsl_iommu_probe, }; diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c index f8315090ff06..8b0d93fda32c 100644 --- a/drivers/gpu/msm/kgsl_mmu.c +++ b/drivers/gpu/msm/kgsl_mmu.c @@ -546,6 +546,17 @@ bool kgsl_mmu_gpuaddr_in_range(struct kgsl_pagetable *pagetable, } EXPORT_SYMBOL(kgsl_mmu_gpuaddr_in_range); +struct kgsl_memdesc *kgsl_mmu_get_qdss_global_entry(struct kgsl_device *device) +{ + struct kgsl_mmu *mmu = &device->mmu; + + if (MMU_OP_VALID(mmu, mmu_get_qdss_global_entry)) + return mmu->mmu_ops->mmu_get_qdss_global_entry(); + + return NULL; +} +EXPORT_SYMBOL(kgsl_mmu_get_qdss_global_entry); + /* * NOMMU defintions - NOMMU really just means that the MMU is kept in pass * through and the GPU directly accesses physical memory. Used in debug mode and diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h index 3652aa2e6ec4..588777af353f 100644 --- a/drivers/gpu/msm/kgsl_mmu.h +++ b/drivers/gpu/msm/kgsl_mmu.h @@ -21,6 +21,12 @@ #define KGSL_MMU_GLOBAL_PT 0 #define KGSL_MMU_SECURE_PT 1 +#define MMU_DEFAULT_TTBR0(_d) \ + (kgsl_mmu_pagetable_get_ttbr0((_d)->mmu.defaultpagetable)) + +#define MMU_DEFAULT_CONTEXTIDR(_d) \ + (kgsl_mmu_pagetable_get_contextidr((_d)->mmu.defaultpagetable)) + struct kgsl_device; enum kgsl_mmutype { @@ -74,6 +80,7 @@ struct kgsl_mmu_ops { struct kgsl_memdesc *memdesc); struct kgsl_pagetable * (*mmu_getpagetable)(struct kgsl_mmu *mmu, unsigned long name); + struct kgsl_memdesc* (*mmu_get_qdss_global_entry)(void); }; struct kgsl_mmu_pt_ops { @@ -221,6 +228,8 @@ int kgsl_mmu_unmap_offset(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc, uint64_t addr, uint64_t offset, uint64_t size); +struct kgsl_memdesc *kgsl_mmu_get_qdss_global_entry(struct kgsl_device *device); + /* * Static inline functions of MMU that simply call the SMMU specific * function using a function pointer. These functions can be thought diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index da8c8585d31e..2b9eef8b6351 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -1381,6 +1381,9 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state, _isense_clk_set_rate(pwr, pwr->num_pwrlevels - 1); } + + /* Turn off the IOMMU clocks */ + kgsl_mmu_disable_clk(&device->mmu); } else if (requested_state == KGSL_STATE_SLEEP) { /* High latency clock maintenance. */ for (i = KGSL_MAX_CLKS - 1; i > 0; i--) @@ -1428,7 +1431,11 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state, pwr->gpu_bimc_interface_enabled = 1; } } + + /* Turn on the IOMMU clocks */ + kgsl_mmu_enable_clk(&device->mmu); } + } } diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index c6f7a694f67a..ec791e169f8f 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1897,6 +1897,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) }, @@ -2615,9 +2616,10 @@ int hid_add_device(struct hid_device *hdev) /* * Scan generic devices for group information */ - if (hid_ignore_special_drivers || - (!hdev->group && - !hid_match_id(hdev, hid_have_special_driver))) { + if (hid_ignore_special_drivers) { + hdev->group = HID_GROUP_GENERIC; + } else if (!hdev->group && + !hid_match_id(hdev, hid_have_special_driver)) { ret = hid_scan_report(hdev); if (ret) hid_warn(hdev, "bad device descriptor (%d)\n", ret); diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 591d4ad7708f..7ecd96bdf834 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -396,6 +396,11 @@ static void mt_feature_mapping(struct hid_device *hdev, td->is_buttonpad = true; break; + case 0xff0000c5: + /* Retrieve the Win8 blob once to enable some devices */ + if (usage->usage_index == 0) + mt_get_feature(hdev, field->report); + break; } } diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 10bd8e6e4c9c..0b80633bae91 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -282,17 +282,21 @@ static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType, u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister); u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister); u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength); + u16 size; + int args_len; + int index = 0; + + i2c_hid_dbg(ihid, "%s\n", __func__); + + if (data_len > ihid->bufsize) + return -EINVAL; - /* hid_hw_* already checked that data_len < HID_MAX_BUFFER_SIZE */ - u16 size = 2 /* size */ + + size = 2 /* size */ + (reportID ? 1 : 0) /* reportID */ + data_len /* buf */; - int args_len = (reportID >= 0x0F ? 1 : 0) /* optional third byte */ + + args_len = (reportID >= 0x0F ? 1 : 0) /* optional third byte */ + 2 /* dataRegister */ + size /* args */; - int index = 0; - - i2c_hid_dbg(ihid, "%s\n", __func__); if (!use_data && maxOutputLength == 0) return -ENOSYS; diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 5dd426fee8cc..0df32fe0e345 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -951,14 +951,6 @@ static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count) return ret; } -static void usbhid_restart_queues(struct usbhid_device *usbhid) -{ - if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl)) - usbhid_restart_out_queue(usbhid); - if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl)) - usbhid_restart_ctrl_queue(usbhid); -} - static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; @@ -1404,6 +1396,37 @@ static void hid_cease_io(struct usbhid_device *usbhid) usb_kill_urb(usbhid->urbout); } +static void hid_restart_io(struct hid_device *hid) +{ + struct usbhid_device *usbhid = hid->driver_data; + int clear_halt = test_bit(HID_CLEAR_HALT, &usbhid->iofl); + int reset_pending = test_bit(HID_RESET_PENDING, &usbhid->iofl); + + spin_lock_irq(&usbhid->lock); + clear_bit(HID_SUSPENDED, &usbhid->iofl); + usbhid_mark_busy(usbhid); + + if (clear_halt || reset_pending) + schedule_work(&usbhid->reset_work); + usbhid->retry_delay = 0; + spin_unlock_irq(&usbhid->lock); + + if (reset_pending || !test_bit(HID_STARTED, &usbhid->iofl)) + return; + + if (!clear_halt) { + if (hid_start_in(hid) < 0) + hid_io_error(hid); + } + + spin_lock_irq(&usbhid->lock); + if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl)) + usbhid_restart_out_queue(usbhid); + if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl)) + usbhid_restart_ctrl_queue(usbhid); + spin_unlock_irq(&usbhid->lock); +} + /* Treat USB reset pretty much the same as suspend/resume */ static int hid_pre_reset(struct usb_interface *intf) { @@ -1453,14 +1476,14 @@ static int hid_post_reset(struct usb_interface *intf) return 1; } + /* No need to do another reset or clear a halted endpoint */ spin_lock_irq(&usbhid->lock); clear_bit(HID_RESET_PENDING, &usbhid->iofl); + clear_bit(HID_CLEAR_HALT, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0); - status = hid_start_in(hid); - if (status < 0) - hid_io_error(hid); - usbhid_restart_queues(usbhid); + + hid_restart_io(hid); return 0; } @@ -1483,25 +1506,9 @@ void usbhid_put_power(struct hid_device *hid) #ifdef CONFIG_PM static int hid_resume_common(struct hid_device *hid, bool driver_suspended) { - struct usbhid_device *usbhid = hid->driver_data; - int status; - - spin_lock_irq(&usbhid->lock); - clear_bit(HID_SUSPENDED, &usbhid->iofl); - usbhid_mark_busy(usbhid); - - if (test_bit(HID_CLEAR_HALT, &usbhid->iofl) || - test_bit(HID_RESET_PENDING, &usbhid->iofl)) - schedule_work(&usbhid->reset_work); - usbhid->retry_delay = 0; - - usbhid_restart_queues(usbhid); - spin_unlock_irq(&usbhid->lock); - - status = hid_start_in(hid); - if (status < 0) - hid_io_error(hid); + int status = 0; + hid_restart_io(hid); if (driver_suspended && hid->driver && hid->driver->resume) status = hid->driver->resume(hid); return status; @@ -1570,12 +1577,8 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) static int hid_resume(struct usb_interface *intf) { struct hid_device *hid = usb_get_intfdata (intf); - struct usbhid_device *usbhid = hid->driver_data; int status; - if (!test_bit(HID_STARTED, &usbhid->iofl)) - return 0; - status = hid_resume_common(hid, true); dev_dbg(&intf->dev, "resume status %d\n", status); return 0; @@ -1584,10 +1587,8 @@ static int hid_resume(struct usb_interface *intf) static int hid_reset_resume(struct usb_interface *intf) { struct hid_device *hid = usb_get_intfdata(intf); - struct usbhid_device *usbhid = hid->driver_data; int status; - clear_bit(HID_SUSPENDED, &usbhid->iofl); status = hid_post_reset(intf); if (status >= 0 && hid->driver && hid->driver->reset_resume) { int ret = hid->driver->reset_resume(hid); diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 01a4f05c1642..3c0f47ac8e53 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2493,6 +2493,17 @@ void wacom_setup_device_quirks(struct wacom *wacom) } /* + * Hack for the Bamboo One: + * the device presents a PAD/Touch interface as most Bamboos and even + * sends ghosts PAD data on it. However, later, we must disable this + * ghost interface, and we can not detect it unless we set it here + * to WACOM_DEVICETYPE_PAD or WACOM_DEVICETYPE_TOUCH. + */ + if (features->type == BAMBOO_PEN && + features->pktlen == WACOM_PKGLEN_BBTOUCH3) + features->device_type |= WACOM_DEVICETYPE_PAD; + + /* * Raw Wacom-mode pen and touch events both come from interface * 0, whose HID descriptor has an application usage of 0xFF0D * (i.e., WACOM_VENDORDEFINED_PEN). We route pen packets back diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c index 36544c4f653c..303d0c9df907 100644 --- a/drivers/hwmon/max1111.c +++ b/drivers/hwmon/max1111.c @@ -85,6 +85,9 @@ static struct max1111_data *the_max1111; int max1111_read_channel(int channel) { + if (!the_max1111 || !the_max1111->spi) + return -ENODEV; + return max1111_read(&the_max1111->spi->dev, channel); } EXPORT_SYMBOL(max1111_read_channel); @@ -258,6 +261,9 @@ static int max1111_remove(struct spi_device *spi) { struct max1111_data *data = spi_get_drvdata(spi); +#ifdef CONFIG_SHARPSL_PM + the_max1111 = NULL; +#endif hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&spi->dev.kobj, &max1110_attr_group); sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group); diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c index ba54e7bde795..3776e748647e 100644 --- a/drivers/hwmon/qpnp-adc-common.c +++ b/drivers/hwmon/qpnp-adc-common.c @@ -38,6 +38,7 @@ #define PMI_CHG_SCALE_1 -138890 #define PMI_CHG_SCALE_2 391750000000 #define QPNP_VADC_HC_VREF_CODE 0x4000 +#define QPNP_VADC_HC_VDD_REFERENCE_MV 1875 /* Units for temperature below (on x axis) is in 0.1DegC as required by the battery driver. Note the resolution used @@ -789,41 +790,51 @@ int32_t qpnp_adc_scale_millidegc_pmic_voltage_thr(struct qpnp_vadc_chip *chip, int64_t low_output = 0, high_output = 0; int rc = 0, sign = 0; - rc = qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_ABSOLUTE); - if (rc < 0) { - pr_err("Could not acquire gain and offset\n"); - return rc; - } - /* Convert to Kelvin and account for voltage to be written as 2mV/K */ low_output = (param->low_temp + KELVINMIL_DEGMIL) * 2; - /* Convert to voltage threshold */ - low_output = (low_output - QPNP_ADC_625_UV) * btm_param.dy; - if (low_output < 0) { - sign = 1; - low_output = -low_output; - } - do_div(low_output, QPNP_ADC_625_UV); - if (sign) - low_output = -low_output; - low_output += btm_param.adc_gnd; - - sign = 0; /* Convert to Kelvin and account for voltage to be written as 2mV/K */ high_output = (param->high_temp + KELVINMIL_DEGMIL) * 2; - /* Convert to voltage threshold */ - high_output = (high_output - QPNP_ADC_625_UV) * btm_param.dy; - if (high_output < 0) { - sign = 1; - high_output = -high_output; + + if (param->adc_tm_hc) { + low_output *= QPNP_VADC_HC_VREF_CODE; + do_div(low_output, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000)); + high_output *= QPNP_VADC_HC_VREF_CODE; + do_div(high_output, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000)); + } else { + rc = qpnp_get_vadc_gain_and_offset(chip, &btm_param, + CALIB_ABSOLUTE); + if (rc < 0) { + pr_err("Could not acquire gain and offset\n"); + return rc; + } + + /* Convert to voltage threshold */ + low_output = (low_output - QPNP_ADC_625_UV) * btm_param.dy; + if (low_output < 0) { + sign = 1; + low_output = -low_output; + } + do_div(low_output, QPNP_ADC_625_UV); + if (sign) + low_output = -low_output; + low_output += btm_param.adc_gnd; + + sign = 0; + /* Convert to voltage threshold */ + high_output = (high_output - QPNP_ADC_625_UV) * btm_param.dy; + if (high_output < 0) { + sign = 1; + high_output = -high_output; + } + do_div(high_output, QPNP_ADC_625_UV); + if (sign) + high_output = -high_output; + high_output += btm_param.adc_gnd; } - do_div(high_output, QPNP_ADC_625_UV); - if (sign) - high_output = -high_output; - high_output += btm_param.adc_gnd; *low_threshold = (uint32_t) low_output; *high_threshold = (uint32_t) high_output; + pr_debug("high_temp:%d, low_temp:%d\n", param->high_temp, param->low_temp); pr_debug("adc_code_high:%x, adc_code_low:%x\n", *high_threshold, @@ -1079,29 +1090,34 @@ int32_t qpnp_adc_tm_scale_voltage_therm_pu2(struct qpnp_vadc_chip *chip, { int64_t adc_voltage = 0; struct qpnp_vadc_linear_graph param1; - int negative_offset; - - qpnp_get_vadc_gain_and_offset(chip, ¶m1, CALIB_RATIOMETRIC); + int negative_offset = 0; - adc_voltage = (reg - param1.adc_gnd) * param1.adc_vref; - if (adc_voltage < 0) { - negative_offset = 1; - adc_voltage = -adc_voltage; - } - - do_div(adc_voltage, param1.dy); - - if (adc_properties->adc_hc) + if (adc_properties->adc_hc) { + /* (ADC code * vref_vadc (1.875V)) / 0x4000 */ + adc_voltage = (int64_t) reg; + adc_voltage *= QPNP_VADC_HC_VDD_REFERENCE_MV; + adc_voltage = div64_s64(adc_voltage, + QPNP_VADC_HC_VREF_CODE); qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref, ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref), adc_voltage, result); - else + } else { + qpnp_get_vadc_gain_and_offset(chip, ¶m1, CALIB_RATIOMETRIC); + + adc_voltage = (reg - param1.adc_gnd) * param1.adc_vref; + if (adc_voltage < 0) { + negative_offset = 1; + adc_voltage = -adc_voltage; + } + + do_div(adc_voltage, param1.dy); + qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb, ARRAY_SIZE(adcmap_100k_104ef_104fb), adc_voltage, result); - - if (negative_offset) - adc_voltage = -adc_voltage; + if (negative_offset) + adc_voltage = -adc_voltage; + } return 0; } @@ -1114,8 +1130,6 @@ int32_t qpnp_adc_tm_scale_therm_voltage_pu2(struct qpnp_vadc_chip *chip, struct qpnp_vadc_linear_graph param1; int rc; - qpnp_get_vadc_gain_and_offset(chip, ¶m1, CALIB_RATIOMETRIC); - if (adc_properties->adc_hc) { rc = qpnp_adc_map_temp_voltage( adcmap_100k_104ef_104fb_1875_vref, @@ -1123,27 +1137,40 @@ int32_t qpnp_adc_tm_scale_therm_voltage_pu2(struct qpnp_vadc_chip *chip, param->low_thr_temp, ¶m->low_thr_voltage); if (rc) return rc; + param->low_thr_voltage *= QPNP_VADC_HC_VREF_CODE; + do_div(param->low_thr_voltage, QPNP_VADC_HC_VDD_REFERENCE_MV); + + rc = qpnp_adc_map_temp_voltage( + adcmap_100k_104ef_104fb_1875_vref, + ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref), + param->high_thr_temp, ¶m->high_thr_voltage); + if (rc) + return rc; + param->high_thr_voltage *= QPNP_VADC_HC_VREF_CODE; + do_div(param->high_thr_voltage, QPNP_VADC_HC_VDD_REFERENCE_MV); } else { + qpnp_get_vadc_gain_and_offset(chip, ¶m1, CALIB_RATIOMETRIC); + rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb, ARRAY_SIZE(adcmap_100k_104ef_104fb), param->low_thr_temp, ¶m->low_thr_voltage); if (rc) return rc; - } - param->low_thr_voltage *= param1.dy; - do_div(param->low_thr_voltage, param1.adc_vref); - param->low_thr_voltage += param1.adc_gnd; + param->low_thr_voltage *= param1.dy; + do_div(param->low_thr_voltage, param1.adc_vref); + param->low_thr_voltage += param1.adc_gnd; - rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb, - ARRAY_SIZE(adcmap_100k_104ef_104fb), - param->high_thr_temp, ¶m->high_thr_voltage); - if (rc) - return rc; + rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb, + ARRAY_SIZE(adcmap_100k_104ef_104fb), + param->high_thr_temp, ¶m->high_thr_voltage); + if (rc) + return rc; - param->high_thr_voltage *= param1.dy; - do_div(param->high_thr_voltage, param1.adc_vref); - param->high_thr_voltage += param1.adc_gnd; + param->high_thr_voltage *= param1.dy; + do_div(param->high_thr_voltage, param1.adc_vref); + param->high_thr_voltage += param1.adc_gnd; + } return 0; } @@ -1251,7 +1278,7 @@ int32_t qpnp_adc_usb_scaler(struct qpnp_vadc_chip *chip, } EXPORT_SYMBOL(qpnp_adc_usb_scaler); -int32_t qpnp_adc_vbatt_rscaler(struct qpnp_vadc_chip *chip, +int32_t qpnp_adc_absolute_rthr(struct qpnp_vadc_chip *chip, struct qpnp_adc_tm_btm_param *param, uint32_t *low_threshold, uint32_t *high_threshold) { @@ -1259,32 +1286,49 @@ int32_t qpnp_adc_vbatt_rscaler(struct qpnp_vadc_chip *chip, int rc = 0, sign = 0; int64_t low_thr = 0, high_thr = 0; - rc = qpnp_get_vadc_gain_and_offset(chip, &vbatt_param, CALIB_ABSOLUTE); - if (rc < 0) - return rc; - - low_thr = (((param->low_thr/param->gain_den) - QPNP_ADC_625_UV) * - vbatt_param.dy); - if (low_thr < 0) { - sign = 1; - low_thr = -low_thr; - } - do_div(low_thr, QPNP_ADC_625_UV); - if (sign) - low_thr = -low_thr; - *low_threshold = low_thr + vbatt_param.adc_gnd; + if (param->adc_tm_hc) { + low_thr = (param->low_thr/param->gain_den); + low_thr *= param->gain_num; + low_thr *= QPNP_VADC_HC_VREF_CODE; + do_div(low_thr, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000)); + *low_threshold = low_thr; + + high_thr = (param->high_thr/param->gain_den); + high_thr *= param->gain_num; + high_thr *= QPNP_VADC_HC_VREF_CODE; + do_div(high_thr, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000)); + *high_threshold = high_thr; + } else { + rc = qpnp_get_vadc_gain_and_offset(chip, &vbatt_param, + CALIB_ABSOLUTE); + if (rc < 0) + return rc; - sign = 0; - high_thr = (((param->high_thr/param->gain_den) - QPNP_ADC_625_UV) * - vbatt_param.dy); - if (high_thr < 0) { - sign = 1; - high_thr = -high_thr; + low_thr = (((param->low_thr/param->gain_den) - + QPNP_ADC_625_UV) * vbatt_param.dy); + if (low_thr < 0) { + sign = 1; + low_thr = -low_thr; + } + low_thr = low_thr * param->gain_num; + do_div(low_thr, QPNP_ADC_625_UV); + if (sign) + low_thr = -low_thr; + *low_threshold = low_thr + vbatt_param.adc_gnd; + + sign = 0; + high_thr = (((param->high_thr/param->gain_den) - + QPNP_ADC_625_UV) * vbatt_param.dy); + if (high_thr < 0) { + sign = 1; + high_thr = -high_thr; + } + high_thr = high_thr * param->gain_num; + do_div(high_thr, QPNP_ADC_625_UV); + if (sign) + high_thr = -high_thr; + *high_threshold = high_thr + vbatt_param.adc_gnd; } - do_div(high_thr, QPNP_ADC_625_UV); - if (sign) - high_thr = -high_thr; - *high_threshold = high_thr + vbatt_param.adc_gnd; pr_debug("high_volt:%d, low_volt:%d\n", param->high_thr, param->low_thr); @@ -1292,48 +1336,16 @@ int32_t qpnp_adc_vbatt_rscaler(struct qpnp_vadc_chip *chip, *low_threshold); return 0; } -EXPORT_SYMBOL(qpnp_adc_vbatt_rscaler); +EXPORT_SYMBOL(qpnp_adc_absolute_rthr); -int32_t qpnp_adc_absolute_rthr(struct qpnp_vadc_chip *chip, +int32_t qpnp_adc_vbatt_rscaler(struct qpnp_vadc_chip *chip, struct qpnp_adc_tm_btm_param *param, uint32_t *low_threshold, uint32_t *high_threshold) { - struct qpnp_vadc_linear_graph vbatt_param; - int rc = 0, sign = 0; - int64_t low_thr = 0, high_thr = 0; - - rc = qpnp_get_vadc_gain_and_offset(chip, &vbatt_param, CALIB_ABSOLUTE); - if (rc < 0) - return rc; - - low_thr = (((param->low_thr) - QPNP_ADC_625_UV) * vbatt_param.dy); - if (low_thr < 0) { - sign = 1; - low_thr = -low_thr; - } - do_div(low_thr, QPNP_ADC_625_UV); - if (sign) - low_thr = -low_thr; - *low_threshold = low_thr + vbatt_param.adc_gnd; - - sign = 0; - high_thr = (((param->high_thr) - QPNP_ADC_625_UV) * vbatt_param.dy); - if (high_thr < 0) { - sign = 1; - high_thr = -high_thr; - } - do_div(high_thr, QPNP_ADC_625_UV); - if (sign) - high_thr = -high_thr; - *high_threshold = high_thr + vbatt_param.adc_gnd; - - pr_debug("high_volt:%d, low_volt:%d\n", param->high_thr, - param->low_thr); - pr_debug("adc_code_high:%x, adc_code_low:%x\n", *high_threshold, - *low_threshold); - return 0; + return qpnp_adc_absolute_rthr(chip, param, low_threshold, + high_threshold); } -EXPORT_SYMBOL(qpnp_adc_absolute_rthr); +EXPORT_SYMBOL(qpnp_adc_vbatt_rscaler); int32_t qpnp_vadc_absolute_rthr(struct qpnp_vadc_chip *chip, const struct qpnp_vadc_chan_properties *chan_prop, @@ -1393,6 +1405,11 @@ int32_t qpnp_adc_btm_scaler(struct qpnp_vadc_chip *chip, int64_t low_output = 0, high_output = 0; int rc = 0; + if (param->adc_tm_hc) { + pr_err("Update scaling for VADC_TM_HC\n"); + return -EINVAL; + } + qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_RATIOMETRIC); pr_debug("warm_temp:%d and cool_temp:%d\n", param->high_temp, @@ -1446,6 +1463,11 @@ int32_t qpnp_adc_qrd_skuh_btm_scaler(struct qpnp_vadc_chip *chip, int64_t low_output = 0, high_output = 0; int rc = 0; + if (param->adc_tm_hc) { + pr_err("Update scaling for VADC_TM_HC\n"); + return -EINVAL; + } + qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_RATIOMETRIC); pr_debug("warm_temp:%d and cool_temp:%d\n", param->high_temp, @@ -1499,6 +1521,11 @@ int32_t qpnp_adc_qrd_skut1_btm_scaler(struct qpnp_vadc_chip *chip, int64_t low_output = 0, high_output = 0; int rc = 0; + if (param->adc_tm_hc) { + pr_err("Update scaling for VADC_TM_HC\n"); + return -EINVAL; + } + qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_RATIOMETRIC); pr_debug("warm_temp:%d and cool_temp:%d\n", param->high_temp, @@ -1552,6 +1579,11 @@ int32_t qpnp_adc_smb_btm_rscaler(struct qpnp_vadc_chip *chip, int64_t low_output = 0, high_output = 0; int rc = 0; + if (param->adc_tm_hc) { + pr_err("Update scaling for VADC_TM_HC\n"); + return -EINVAL; + } + qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_RATIOMETRIC); pr_debug("warm_temp:%d and cool_temp:%d\n", param->high_temp, diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c index f36bb933a03e..6ed947e5603b 100644 --- a/drivers/hwmon/qpnp-adc-voltage.c +++ b/drivers/hwmon/qpnp-adc-voltage.c @@ -1642,8 +1642,12 @@ static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc) vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dy = (calib_read_1 - calib_read_2); - vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx + if (calib_type == CALIB_ABSOLUTE) + vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx = QPNP_ADC_625_UV; + else if (calib_type == ADC_HC_ABS_CAL) + vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx + = QPNP_ADC_1P25_UV; vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_vref = calib_read_1; vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_gnd = diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 7e93f7654347..617c766f032e 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -68,24 +68,8 @@ static bool etm4_arch_supported(u8 arch) static int etm4_trace_id(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - unsigned long flags; - int trace_id = -1; - if (!drvdata->enable) - return drvdata->trcid; - - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); - - CS_UNLOCK(drvdata->base); - trace_id = readl_relaxed(drvdata->base + TRCTRACEIDR); - trace_id &= ETM_TRACEID_MASK; - CS_LOCK(drvdata->base); - - spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); - - return trace_id; + return drvdata->trcid; } static void etm4_enable_hw(void *info) diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index e0a50e814d44..10e50df1e6d5 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -1837,7 +1837,11 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) drvdata->size = SZ_1M; drvdata->mem_size = drvdata->size; - drvdata->memtype = TMC_ETR_MEM_TYPE_CONTIG; + + if (of_property_read_bool(np, "arm,sg-enable")) + drvdata->memtype = TMC_ETR_MEM_TYPE_SG; + else + drvdata->memtype = TMC_ETR_MEM_TYPE_CONTIG; drvdata->mem_type = drvdata->memtype; } else { drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4; diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c index a80f07b88b42..3a11b061e5b0 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.c +++ b/drivers/hwtracing/coresight/coresight-tpdm.c @@ -2647,11 +2647,11 @@ static ssize_t tpdm_store_dsb_edge_ctrl_mask(struct device *dev, size_t size) { struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); - unsigned start, end, val; + unsigned long start, end, val; uint32_t set; int i, bit, reg; - if (sscanf(buf, "%ui %ui %ui", &start, &end, &val) != 3) + if (sscanf(buf, "%lx %lx %lx", &start, &end, &val) != 3) return -EINVAL; if (!test_bit(TPDM_DS_DSB, drvdata->datasets) || (start >= TPDM_DSB_MAX_LINES) || (end >= TPDM_DSB_MAX_LINES)) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index cd4510a63375..146eed70bdf4 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -65,7 +65,7 @@ #include <asm/mwait.h> #include <asm/msr.h> -#define INTEL_IDLE_VERSION "0.4" +#define INTEL_IDLE_VERSION "0.4.1" #define PREFIX "intel_idle: " static struct cpuidle_driver intel_idle_driver = { @@ -994,36 +994,92 @@ static void intel_idle_cpuidle_devices_uninit(void) } /* - * intel_idle_state_table_update() - * - * Update the default state_table for this CPU-id + * ivt_idle_state_table_update(void) * - * Currently used to access tuned IVT multi-socket targets + * Tune IVT multi-socket targets * Assumption: num_sockets == (max_package_num + 1) */ -void intel_idle_state_table_update(void) +static void ivt_idle_state_table_update(void) { /* IVT uses a different table for 1-2, 3-4, and > 4 sockets */ - if (boot_cpu_data.x86_model == 0x3e) { /* IVT */ - int cpu, package_num, num_sockets = 1; - - for_each_online_cpu(cpu) { - package_num = topology_physical_package_id(cpu); - if (package_num + 1 > num_sockets) { - num_sockets = package_num + 1; - - if (num_sockets > 4) { - cpuidle_state_table = ivt_cstates_8s; - return; - } + int cpu, package_num, num_sockets = 1; + + for_each_online_cpu(cpu) { + package_num = topology_physical_package_id(cpu); + if (package_num + 1 > num_sockets) { + num_sockets = package_num + 1; + + if (num_sockets > 4) { + cpuidle_state_table = ivt_cstates_8s; + return; } } + } + + if (num_sockets > 2) + cpuidle_state_table = ivt_cstates_4s; + + /* else, 1 and 2 socket systems use default ivt_cstates */ +} +/* + * sklh_idle_state_table_update(void) + * + * On SKL-H (model 0x5e) disable C8 and C9 if: + * C10 is enabled and SGX disabled + */ +static void sklh_idle_state_table_update(void) +{ + unsigned long long msr; + unsigned int eax, ebx, ecx, edx; + + + /* if PC10 disabled via cmdline intel_idle.max_cstate=7 or shallower */ + if (max_cstate <= 7) + return; + + /* if PC10 not present in CPUID.MWAIT.EDX */ + if ((mwait_substates & (0xF << 28)) == 0) + return; + + rdmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr); + + /* PC10 is not enabled in PKG C-state limit */ + if ((msr & 0xF) != 8) + return; + + ecx = 0; + cpuid(7, &eax, &ebx, &ecx, &edx); + + /* if SGX is present */ + if (ebx & (1 << 2)) { - if (num_sockets > 2) - cpuidle_state_table = ivt_cstates_4s; - /* else, 1 and 2 socket systems use default ivt_cstates */ + rdmsrl(MSR_IA32_FEATURE_CONTROL, msr); + + /* if SGX is enabled */ + if (msr & (1 << 18)) + return; + } + + skl_cstates[5].disabled = 1; /* C8-SKL */ + skl_cstates[6].disabled = 1; /* C9-SKL */ +} +/* + * intel_idle_state_table_update() + * + * Update the default state_table for this CPU-id + */ + +static void intel_idle_state_table_update(void) +{ + switch (boot_cpu_data.x86_model) { + + case 0x3e: /* IVT */ + ivt_idle_state_table_update(); + break; + case 0x5e: /* SKL-H */ + sklh_idle_state_table_update(); + break; } - return; } /* @@ -1063,6 +1119,14 @@ static int __init intel_idle_cpuidle_driver_init(void) if (num_substates == 0) continue; + /* if state marked as disabled, skip it */ + if (cpuidle_state_table[cstate].disabled != 0) { + pr_debug(PREFIX "state %s is disabled", + cpuidle_state_table[cstate].name); + continue; + } + + if (((mwait_cstate + 1) > 2) && !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) mark_tsc_unstable("TSC halts in idle" diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 2d33f1e821db..291c61a41c9a 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -547,7 +547,7 @@ static int bmc150_accel_get_axis(struct bmc150_accel_data *data, { int ret; int axis = chan->scan_index; - unsigned int raw_val; + __le16 raw_val; mutex_lock(&data->mutex); ret = bmc150_accel_set_power_state(data, true); @@ -557,14 +557,14 @@ static int bmc150_accel_get_axis(struct bmc150_accel_data *data, } ret = regmap_bulk_read(data->regmap, BMC150_ACCEL_AXIS_TO_REG(axis), - &raw_val, 2); + &raw_val, sizeof(raw_val)); if (ret < 0) { dev_err(data->dev, "Error reading axis %d\n", axis); bmc150_accel_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; } - *val = sign_extend32(raw_val >> chan->scan_type.shift, + *val = sign_extend32(le16_to_cpu(raw_val) >> chan->scan_type.shift, chan->scan_type.realbits - 1); ret = bmc150_accel_set_power_state(data, false); mutex_unlock(&data->mutex); @@ -988,6 +988,7 @@ static const struct iio_event_spec bmc150_accel_event = { .realbits = (bits), \ .storagebits = 16, \ .shift = 16 - (bits), \ + .endianness = IIO_LE, \ }, \ .event_spec = &bmc150_accel_event, \ .num_event_specs = 1 \ diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c index dea0448c365c..ae2df4f7ff0d 100644 --- a/drivers/iio/adc/qcom-rradc.c +++ b/drivers/iio/adc/qcom-rradc.c @@ -149,12 +149,12 @@ #define FG_ADC_RR_TEMP_FS_VOLTAGE_NUM 5000000 #define FG_ADC_RR_TEMP_FS_VOLTAGE_DEN 3 #define FG_ADC_RR_DIE_TEMP_OFFSET 600000 -#define FG_ADC_RR_DIE_TEMP_SLOPE 2000 -#define FG_ADC_RR_DIE_TEMP_OFFSET_DEGC 25 +#define FG_ADC_RR_DIE_TEMP_SLOPE 2 +#define FG_ADC_RR_DIE_TEMP_OFFSET_MILLI_DEGC 25000 #define FG_ADC_RR_CHG_TEMP_OFFSET 1288000 -#define FG_ADC_RR_CHG_TEMP_SLOPE 4000 -#define FG_ADC_RR_CHG_TEMP_OFFSET_DEGC 27 +#define FG_ADC_RR_CHG_TEMP_SLOPE 4 +#define FG_ADC_RR_CHG_TEMP_OFFSET_MILLI_DEGC 27000 #define FG_ADC_RR_VOLT_INPUT_FACTOR 8 #define FG_ADC_RR_CURR_INPUT_FACTOR 2 @@ -162,6 +162,9 @@ #define FG_ADC_KELVINMIL_CELSIUSMIL 273150 #define FG_ADC_RR_GPIO_FS_RANGE 5000 +#define FG_RR_ADC_COHERENT_CHECK_RETRY 5 +#define FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN 16 +#define FG_RR_ADC_STS_CHANNEL_READING_MASK 0x3 /* * The channel number is not a physical index in hardware, @@ -171,21 +174,20 @@ * the RR ADC before RR_ADC_MAX. */ enum rradc_channel_id { - RR_ADC_BATT_ID_5 = 0, - RR_ADC_BATT_ID_15, - RR_ADC_BATT_ID_150, - RR_ADC_BATT_ID, + RR_ADC_BATT_ID = 0, RR_ADC_BATT_THERM, RR_ADC_SKIN_TEMP, - RR_ADC_USBIN_V, RR_ADC_USBIN_I, - RR_ADC_DCIN_V, + RR_ADC_USBIN_V, RR_ADC_DCIN_I, + RR_ADC_DCIN_V, RR_ADC_DIE_TEMP, RR_ADC_CHG_TEMP, RR_ADC_GPIO, - RR_ADC_ATEST, - RR_ADC_TM_ADC, + RR_ADC_CHG_HOT_TEMP, + RR_ADC_CHG_TOO_HOT_TEMP, + RR_ADC_SKIN_HOT_TEMP, + RR_ADC_SKIN_TOO_HOT_TEMP, RR_ADC_MAX }; @@ -205,51 +207,75 @@ struct rradc_channels { long info_mask; u8 lsb; u8 msb; + u8 sts; int (*scale)(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 adc_code, int *result); }; struct rradc_chan_prop { enum rradc_channel_id channel; + uint32_t channel_data; int (*scale)(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 adc_code, int *result); }; static int rradc_read(struct rradc_chip *rr_adc, u16 offset, u8 *data, int len) { - int rc = 0; + int rc = 0, retry_cnt = 0, i = 0; + u8 data_check[FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN]; + bool coherent_err = false; + + if (len > FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN) { + pr_err("Increase the buffer length\n"); + return -EINVAL; + } + + while (retry_cnt < FG_RR_ADC_COHERENT_CHECK_RETRY) { + rc = regmap_bulk_read(rr_adc->regmap, rr_adc->base + offset, + data, len); + if (rc < 0) { + pr_err("rr_adc reg 0x%x failed :%d\n", offset, rc); + return rc; + } + + rc = regmap_bulk_read(rr_adc->regmap, rr_adc->base + offset, + data_check, len); + if (rc < 0) { + pr_err("rr_adc reg 0x%x failed :%d\n", offset, rc); + return rc; + } + + for (i = 0; i < len; i++) { + if (data[i] != data_check[i]) + coherent_err = true; + } + + if (coherent_err) { + retry_cnt++; + coherent_err = false; + pr_debug("retry_cnt:%d\n", retry_cnt); + } else { + break; + } + } - rc = regmap_bulk_read(rr_adc->regmap, rr_adc->base + offset, data, len); - if (rc < 0) - pr_err("rr adc read reg %d failed with %d\n", offset, rc); + if (retry_cnt == FG_RR_ADC_COHERENT_CHECK_RETRY) + pr_err("Retry exceeded for coherrency check\n"); return rc; } static int rradc_post_process_batt_id(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 adc_code, - int *result_mohms) + int *result_ohms) { uint32_t current_value; int64_t r_id; - switch (prop->channel) { - case RR_ADC_BATT_ID_5: - current_value = FG_ADC_RR_BATT_ID_5_MA; - break; - case RR_ADC_BATT_ID_15: - current_value = FG_ADC_RR_BATT_ID_15_MA; - break; - case RR_ADC_BATT_ID_150: - current_value = FG_ADC_RR_BATT_ID_150_MA; - break; - default: - return -EINVAL; - } - + current_value = prop->channel_data; r_id = ((int64_t)adc_code * FG_ADC_RR_FS_VOLTAGE_MV); r_id = div64_s64(r_id, (FG_MAX_ADC_READINGS * current_value)); - *result_mohms = (r_id * FG_ADC_SCALE_MILLI_FACTOR); + *result_ohms = (r_id * FG_ADC_SCALE_MILLI_FACTOR); return 0; } @@ -270,30 +296,30 @@ static int rradc_post_process_therm(struct rradc_chip *chip, static int rradc_post_process_volt(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 adc_code, - int *result_mv) + int *result_uv) { - int64_t mv = 0; + int64_t uv = 0; /* 8x input attenuation; 2.5V ADC full scale */ - mv = ((int64_t)adc_code * FG_ADC_RR_VOLT_INPUT_FACTOR); - mv *= FG_ADC_RR_FS_VOLTAGE_MV; - mv = div64_s64(mv, FG_MAX_ADC_READINGS); - *result_mv = mv; + uv = ((int64_t)adc_code * FG_ADC_RR_VOLT_INPUT_FACTOR); + uv *= (FG_ADC_RR_FS_VOLTAGE_MV * FG_ADC_SCALE_MILLI_FACTOR); + uv = div64_s64(uv, FG_MAX_ADC_READINGS); + *result_uv = uv; return 0; } static int rradc_post_process_curr(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 adc_code, - int *result_ma) + int *result_ua) { - int64_t ma = 0; + int64_t ua = 0; /* 0.5 V/A; 2.5V ADC full scale */ - ma = ((int64_t)adc_code * FG_ADC_RR_CURR_INPUT_FACTOR); - ma *= FG_ADC_RR_FS_VOLTAGE_MV; - ma = div64_s64(ma, FG_MAX_ADC_READINGS); - *result_ma = ma; + ua = ((int64_t)adc_code * FG_ADC_RR_CURR_INPUT_FACTOR); + ua *= (FG_ADC_RR_FS_VOLTAGE_MV * FG_ADC_SCALE_MILLI_FACTOR); + ua = div64_s64(ua, FG_MAX_ADC_READINGS); + *result_ua = ua; return 0; } @@ -309,8 +335,41 @@ static int rradc_post_process_die_temp(struct rradc_chip *chip, FG_MAX_ADC_READINGS)); temp -= FG_ADC_RR_DIE_TEMP_OFFSET; temp = div64_s64(temp, FG_ADC_RR_DIE_TEMP_SLOPE); - temp += FG_ADC_RR_DIE_TEMP_OFFSET_DEGC; - *result_millidegc = (temp * FG_ADC_SCALE_MILLI_FACTOR); + temp += FG_ADC_RR_DIE_TEMP_OFFSET_MILLI_DEGC; + *result_millidegc = temp; + + return 0; +} + +static int rradc_post_process_chg_temp_hot(struct rradc_chip *chip, + struct rradc_chan_prop *prop, u16 adc_code, + int *result_millidegc) +{ + int64_t temp = 0; + + temp = (int64_t) adc_code * 4; + temp = temp * FG_ADC_RR_TEMP_FS_VOLTAGE_NUM; + temp = div64_s64(temp, (FG_ADC_RR_TEMP_FS_VOLTAGE_DEN * + FG_MAX_ADC_READINGS)); + temp = FG_ADC_RR_CHG_TEMP_OFFSET - temp; + temp = div64_s64(temp, FG_ADC_RR_CHG_TEMP_SLOPE); + temp = temp + FG_ADC_RR_CHG_TEMP_OFFSET_MILLI_DEGC; + *result_millidegc = temp; + + return 0; +} + +static int rradc_post_process_skin_temp_hot(struct rradc_chip *chip, + struct rradc_chan_prop *prop, u16 adc_code, + int *result_millidegc) +{ + int64_t temp = 0; + + temp = (int64_t) adc_code; + temp = div64_s64(temp, 2); + temp = temp - 30; + temp *= FG_ADC_SCALE_MILLI_FACTOR; + *result_millidegc = temp; return 0; } @@ -326,8 +385,8 @@ static int rradc_post_process_chg_temp(struct rradc_chip *chip, FG_MAX_ADC_READINGS)); temp = FG_ADC_RR_CHG_TEMP_OFFSET - temp; temp = div64_s64(temp, FG_ADC_RR_CHG_TEMP_SLOPE); - temp = temp + FG_ADC_RR_CHG_TEMP_OFFSET_DEGC; - *result_millidegc = (temp * FG_ADC_SCALE_MILLI_FACTOR); + temp = temp + FG_ADC_RR_CHG_TEMP_OFFSET_MILLI_DEGC; + *result_millidegc = temp; return 0; } @@ -346,63 +405,80 @@ static int rradc_post_process_gpio(struct rradc_chip *chip, return 0; } -#define RR_ADC_CHAN(_dname, _type, _mask, _scale, _lsb, _msb) \ +#define RR_ADC_CHAN(_dname, _type, _mask, _scale, _lsb, _msb, _sts) \ { \ - .datasheet_name = __stringify(_dname), \ + .datasheet_name = (_dname), \ .type = _type, \ .info_mask = _mask, \ .scale = _scale, \ .lsb = _lsb, \ .msb = _msb, \ + .sts = _sts, \ }, \ -#define RR_ADC_CHAN_TEMP(_dname, _scale, _lsb, _msb) \ +#define RR_ADC_CHAN_TEMP(_dname, _scale, _lsb, _msb, _sts) \ RR_ADC_CHAN(_dname, IIO_TEMP, \ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \ - _scale, _lsb, _msb) \ + _scale, _lsb, _msb, _sts) \ -#define RR_ADC_CHAN_VOLT(_dname, _scale, _lsb, _msb) \ +#define RR_ADC_CHAN_VOLT(_dname, _scale, _lsb, _msb, _sts) \ RR_ADC_CHAN(_dname, IIO_VOLTAGE, \ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\ - _scale, _lsb, _msb) \ + _scale, _lsb, _msb, _sts) \ -#define RR_ADC_CHAN_CURRENT(_dname, _scale, _lsb, _msb) \ +#define RR_ADC_CHAN_CURRENT(_dname, _scale, _lsb, _msb, _sts) \ RR_ADC_CHAN(_dname, IIO_CURRENT, \ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\ - _scale, _lsb, _msb) \ + _scale, _lsb, _msb, _sts) \ -#define RR_ADC_CHAN_RESISTANCE(_dname, _scale, _lsb, _msb) \ +#define RR_ADC_CHAN_RESISTANCE(_dname, _scale, _lsb, _msb, _sts) \ RR_ADC_CHAN(_dname, IIO_RESISTANCE, \ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\ - _scale, _lsb, _msb) \ + _scale, _lsb, _msb, _sts) \ static const struct rradc_channels rradc_chans[] = { - RR_ADC_CHAN_RESISTANCE("batt_id_5", rradc_post_process_batt_id, - FG_ADC_RR_BATT_ID_5_LSB, FG_ADC_RR_BATT_ID_5_MSB) - RR_ADC_CHAN_RESISTANCE("batt_id_15", rradc_post_process_batt_id, - FG_ADC_RR_BATT_ID_15_LSB, FG_ADC_RR_BATT_ID_15_MSB) - RR_ADC_CHAN_RESISTANCE("batt_id_150", rradc_post_process_batt_id, - FG_ADC_RR_BATT_ID_150_LSB, FG_ADC_RR_BATT_ID_150_MSB) RR_ADC_CHAN_RESISTANCE("batt_id", rradc_post_process_batt_id, - FG_ADC_RR_BATT_ID_5_LSB, FG_ADC_RR_BATT_ID_5_MSB) + FG_ADC_RR_BATT_ID_5_LSB, FG_ADC_RR_BATT_ID_5_MSB, + FG_ADC_RR_BATT_ID_STS) RR_ADC_CHAN_TEMP("batt_therm", &rradc_post_process_therm, - FG_ADC_RR_BATT_THERM_LSB, FG_ADC_RR_BATT_THERM_MSB) + FG_ADC_RR_BATT_THERM_LSB, FG_ADC_RR_BATT_THERM_MSB, + FG_ADC_RR_BATT_THERM_STS) RR_ADC_CHAN_TEMP("skin_temp", &rradc_post_process_therm, - FG_ADC_RR_SKIN_TEMP_LSB, FG_ADC_RR_SKIN_TEMP_MSB) + FG_ADC_RR_SKIN_TEMP_LSB, FG_ADC_RR_SKIN_TEMP_MSB, + FG_ADC_RR_AUX_THERM_STS) RR_ADC_CHAN_CURRENT("usbin_i", &rradc_post_process_curr, - FG_ADC_RR_USB_IN_V_LSB, FG_ADC_RR_USB_IN_V_MSB) + FG_ADC_RR_USB_IN_I_LSB, FG_ADC_RR_USB_IN_I_MSB, + FG_ADC_RR_USB_IN_I_STS) RR_ADC_CHAN_VOLT("usbin_v", &rradc_post_process_volt, - FG_ADC_RR_USB_IN_I_LSB, FG_ADC_RR_USB_IN_I_MSB) + FG_ADC_RR_USB_IN_V_LSB, FG_ADC_RR_USB_IN_V_MSB, + FG_ADC_RR_USB_IN_V_STS) RR_ADC_CHAN_CURRENT("dcin_i", &rradc_post_process_curr, - FG_ADC_RR_DC_IN_V_LSB, FG_ADC_RR_DC_IN_V_MSB) + FG_ADC_RR_DC_IN_I_LSB, FG_ADC_RR_DC_IN_I_MSB, + FG_ADC_RR_DC_IN_I_STS) RR_ADC_CHAN_VOLT("dcin_v", &rradc_post_process_volt, - FG_ADC_RR_DC_IN_I_LSB, FG_ADC_RR_DC_IN_I_MSB) + FG_ADC_RR_DC_IN_V_LSB, FG_ADC_RR_DC_IN_V_MSB, + FG_ADC_RR_DC_IN_V_STS) RR_ADC_CHAN_TEMP("die_temp", &rradc_post_process_die_temp, - FG_ADC_RR_PMI_DIE_TEMP_LSB, FG_ADC_RR_PMI_DIE_TEMP_MSB) + FG_ADC_RR_PMI_DIE_TEMP_LSB, FG_ADC_RR_PMI_DIE_TEMP_MSB, + FG_ADC_RR_PMI_DIE_TEMP_STS) RR_ADC_CHAN_TEMP("chg_temp", &rradc_post_process_chg_temp, - FG_ADC_RR_CHARGER_TEMP_LSB, FG_ADC_RR_CHARGER_TEMP_MSB) + FG_ADC_RR_CHARGER_TEMP_LSB, FG_ADC_RR_CHARGER_TEMP_MSB, + FG_ADC_RR_CHARGER_TEMP_STS) RR_ADC_CHAN_VOLT("gpio", &rradc_post_process_gpio, - FG_ADC_RR_GPIO_LSB, FG_ADC_RR_GPIO_MSB) + FG_ADC_RR_GPIO_LSB, FG_ADC_RR_GPIO_MSB, + FG_ADC_RR_GPIO_STS) + RR_ADC_CHAN_TEMP("chg_temp_hot", &rradc_post_process_chg_temp_hot, + FG_ADC_RR_CHARGER_HOT, FG_ADC_RR_CHARGER_HOT, + FG_ADC_RR_CHARGER_TEMP_STS) + RR_ADC_CHAN_TEMP("chg_temp_too_hot", &rradc_post_process_chg_temp_hot, + FG_ADC_RR_CHARGER_TOO_HOT, FG_ADC_RR_CHARGER_TOO_HOT, + FG_ADC_RR_CHARGER_TEMP_STS) + RR_ADC_CHAN_TEMP("skin_temp_hot", &rradc_post_process_skin_temp_hot, + FG_ADC_RR_SKIN_HOT, FG_ADC_RR_SKIN_HOT, + FG_ADC_RR_AUX_THERM_STS) + RR_ADC_CHAN_TEMP("skin_temp_too_hot", &rradc_post_process_skin_temp_hot, + FG_ADC_RR_SKIN_TOO_HOT, FG_ADC_RR_SKIN_TOO_HOT, + FG_ADC_RR_AUX_THERM_STS) }; static int rradc_do_conversion(struct rradc_chip *chip, @@ -411,15 +487,44 @@ static int rradc_do_conversion(struct rradc_chip *chip, int rc = 0, bytes_to_read = 0; u8 buf[6]; u16 offset = 0, batt_id_5 = 0, batt_id_15 = 0, batt_id_150 = 0; + u16 status = 0; mutex_lock(&chip->lock); + if ((prop->channel != RR_ADC_BATT_ID) && + (prop->channel != RR_ADC_CHG_HOT_TEMP) && + (prop->channel != RR_ADC_CHG_TOO_HOT_TEMP) && + (prop->channel != RR_ADC_SKIN_HOT_TEMP) && + (prop->channel != RR_ADC_SKIN_TOO_HOT_TEMP)) { + /* BATT_ID STS bit does not get set initially */ + status = rradc_chans[prop->channel].sts; + rc = rradc_read(chip, status, buf, 1); + if (rc < 0) { + pr_err("status read failed:%d\n", rc); + goto fail; + } + + buf[0] &= FG_RR_ADC_STS_CHANNEL_READING_MASK; + if (buf[0] != FG_RR_ADC_STS_CHANNEL_READING_MASK) { + pr_warn("%s is not ready; nothing to read\n", + rradc_chans[prop->channel].datasheet_name); + rc = -ENODATA; + goto fail; + } + } + offset = rradc_chans[prop->channel].lsb; if (prop->channel == RR_ADC_BATT_ID) bytes_to_read = 6; + else if ((prop->channel == RR_ADC_CHG_HOT_TEMP) || + (prop->channel == RR_ADC_CHG_TOO_HOT_TEMP) || + (prop->channel == RR_ADC_SKIN_HOT_TEMP) || + (prop->channel == RR_ADC_SKIN_TOO_HOT_TEMP)) + bytes_to_read = 1; else bytes_to_read = 2; + buf[0] = 0; rc = rradc_read(chip, offset, buf, bytes_to_read); if (rc) { pr_err("read data failed\n"); @@ -427,19 +532,33 @@ static int rradc_do_conversion(struct rradc_chip *chip, } if (prop->channel == RR_ADC_BATT_ID) { - batt_id_150 = (buf[4] << 8) | buf[5]; - batt_id_15 = (buf[2] << 8) | buf[3]; - batt_id_5 = (buf[0] << 8) | buf[1]; + batt_id_150 = (buf[5] << 8) | buf[4]; + batt_id_15 = (buf[3] << 8) | buf[2]; + batt_id_5 = (buf[1] << 8) | buf[0]; + if ((!batt_id_150) && (!batt_id_15) && (!batt_id_5)) { + pr_err("Invalid batt_id values with all zeros\n"); + rc = -EINVAL; + goto fail; + } + if (batt_id_150 <= FG_ADC_RR_BATT_ID_RANGE) { pr_debug("Batt_id_150 is chosen\n"); *data = batt_id_150; + prop->channel_data = FG_ADC_RR_BATT_ID_150_MA; } else if (batt_id_15 <= FG_ADC_RR_BATT_ID_RANGE) { pr_debug("Batt_id_15 is chosen\n"); *data = batt_id_15; + prop->channel_data = FG_ADC_RR_BATT_ID_15_MA; } else { pr_debug("Batt_id_5 is chosen\n"); *data = batt_id_5; + prop->channel_data = FG_ADC_RR_BATT_ID_5_MA; } + } else if ((prop->channel == RR_ADC_CHG_HOT_TEMP) || + (prop->channel == RR_ADC_CHG_TOO_HOT_TEMP) || + (prop->channel == RR_ADC_SKIN_HOT_TEMP) || + (prop->channel == RR_ADC_SKIN_TOO_HOT_TEMP)) { + *data = buf[0]; } else { *data = (buf[1] << 8) | buf[0]; } @@ -458,6 +577,11 @@ static int rradc_read_raw(struct iio_dev *indio_dev, u16 adc_code; int rc = 0; + if (chan->address >= RR_ADC_MAX) { + pr_err("Invalid channel index:%ld\n", chan->address); + return -EINVAL; + } + switch (mask) { case IIO_CHAN_INFO_PROCESSED: prop = &chip->chan_props[chan->address]; @@ -477,10 +601,6 @@ static int rradc_read_raw(struct iio_dev *indio_dev, *val = (int) adc_code; return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - *val = 0; - *val2 = 1000; - return IIO_VAL_INT_PLUS_MICRO; default: rc = -EINVAL; break; @@ -498,15 +618,11 @@ static int rradc_get_dt_data(struct rradc_chip *chip, struct device_node *node) { const struct rradc_channels *rradc_chan; struct iio_chan_spec *iio_chan; - struct device_node *child; - unsigned int index = 0, chan, base; + unsigned int i = 0, base; int rc = 0; struct rradc_chan_prop prop; - chip->nchannels = of_get_available_child_count(node); - if (!chip->nchannels || (chip->nchannels >= RR_ADC_MAX)) - return -EINVAL; - + chip->nchannels = RR_ADC_MAX; chip->iio_chans = devm_kcalloc(chip->dev, chip->nchannels, sizeof(*chip->iio_chans), GFP_KERNEL); if (!chip->iio_chans) @@ -529,30 +645,21 @@ static int rradc_get_dt_data(struct rradc_chip *chip, struct device_node *node) chip->base = base; iio_chan = chip->iio_chans; - for_each_available_child_of_node(node, child) { - rc = of_property_read_u32(child, "channel", &chan); - if (rc) { - dev_err(chip->dev, "invalid channel number %d\n", chan); - return rc; - } - - if (chan > RR_ADC_MAX || chan < RR_ADC_BATT_ID_5) { - dev_err(chip->dev, "invalid channel number %d\n", chan); - return -EINVAL; - } - - prop.channel = chan; - prop.scale = rradc_chans[chan].scale; - chip->chan_props[index] = prop; + for (i = 0; i < RR_ADC_MAX; i++) { + prop.channel = i; + prop.scale = rradc_chans[i].scale; + /* Private channel data used for selecting batt_id */ + prop.channel_data = 0; + chip->chan_props[i] = prop; - rradc_chan = &rradc_chans[chan]; + rradc_chan = &rradc_chans[i]; iio_chan->channel = prop.channel; iio_chan->datasheet_name = rradc_chan->datasheet_name; + iio_chan->extend_name = rradc_chan->datasheet_name; iio_chan->info_mask_separate = rradc_chan->info_mask; iio_chan->type = rradc_chan->type; - iio_chan->indexed = 1; - iio_chan->address = index++; + iio_chan->address = i; iio_chan++; } diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index 02ff789852a0..acb3b303d800 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -452,7 +452,7 @@ static int bmg160_get_temp(struct bmg160_data *data, int *val) static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val) { int ret; - unsigned int raw_val; + __le16 raw_val; mutex_lock(&data->mutex); ret = bmg160_set_power_state(data, true); @@ -462,7 +462,7 @@ static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val) } ret = regmap_bulk_read(data->regmap, BMG160_AXIS_TO_REG(axis), &raw_val, - 2); + sizeof(raw_val)); if (ret < 0) { dev_err(data->dev, "Error reading axis %d\n", axis); bmg160_set_power_state(data, false); @@ -470,7 +470,7 @@ static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val) return ret; } - *val = sign_extend32(raw_val, 15); + *val = sign_extend32(le16_to_cpu(raw_val), 15); ret = bmg160_set_power_state(data, false); mutex_unlock(&data->mutex); if (ret < 0) @@ -733,6 +733,7 @@ static const struct iio_event_spec bmg160_event = { .sign = 's', \ .realbits = 16, \ .storagebits = 16, \ + .endianness = IIO_LE, \ }, \ .event_spec = &bmg160_event, \ .num_event_specs = 1 \ @@ -780,7 +781,7 @@ static irqreturn_t bmg160_trigger_handler(int irq, void *p) mutex_unlock(&data->mutex); goto err; } - data->buffer[i++] = ret; + data->buffer[i++] = val; } mutex_unlock(&data->mutex); diff --git a/drivers/iio/magnetometer/st_magn.h b/drivers/iio/magnetometer/st_magn.h index 06a4d9c35581..9daca4681922 100644 --- a/drivers/iio/magnetometer/st_magn.h +++ b/drivers/iio/magnetometer/st_magn.h @@ -44,6 +44,7 @@ static inline int st_magn_allocate_ring(struct iio_dev *indio_dev) static inline void st_magn_deallocate_ring(struct iio_dev *indio_dev) { } +#define ST_MAGN_TRIGGER_SET_STATE NULL #endif /* CONFIG_IIO_BUFFER */ #endif /* ST_MAGN_H */ diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index f357ca67a41c..87799de90a1d 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -456,7 +456,10 @@ out_locked: return status; } -static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast) +/* + * Caller must hold 'priv->lock' + */ +static int ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ib_sa_multicast *multicast; @@ -466,6 +469,10 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast) ib_sa_comp_mask comp_mask; int ret = 0; + if (!priv->broadcast || + !test_bit(IPOIB_FLAG_OPER_UP, &priv->flags)) + return -EINVAL; + ipoib_dbg_mcast(priv, "joining MGID %pI6\n", mcast->mcmember.mgid.raw); rec.mgid = mcast->mcmember.mgid; @@ -525,20 +532,23 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast) rec.join_state = 4; #endif } + spin_unlock_irq(&priv->lock); multicast = ib_sa_join_multicast(&ipoib_sa_client, priv->ca, priv->port, &rec, comp_mask, GFP_KERNEL, ipoib_mcast_join_complete, mcast); + spin_lock_irq(&priv->lock); if (IS_ERR(multicast)) { ret = PTR_ERR(multicast); ipoib_warn(priv, "ib_sa_join_multicast failed, status %d\n", ret); - spin_lock_irq(&priv->lock); /* Requeue this join task with a backoff delay */ __ipoib_mcast_schedule_join_thread(priv, mcast, 1); clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags); spin_unlock_irq(&priv->lock); complete(&mcast->done); + spin_lock_irq(&priv->lock); } + return 0; } void ipoib_mcast_join_task(struct work_struct *work) @@ -620,9 +630,10 @@ void ipoib_mcast_join_task(struct work_struct *work) /* Found the next unjoined group */ init_completion(&mcast->done); set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags); - spin_unlock_irq(&priv->lock); - ipoib_mcast_join(dev, mcast); - spin_lock_irq(&priv->lock); + if (ipoib_mcast_join(dev, mcast)) { + spin_unlock_irq(&priv->lock); + return; + } } else if (!delay_until || time_before(mcast->delay_until, delay_until)) delay_until = mcast->delay_until; @@ -641,10 +652,9 @@ out: if (mcast) { init_completion(&mcast->done); set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags); + ipoib_mcast_join(dev, mcast); } spin_unlock_irq(&priv->lock); - if (mcast) - ipoib_mcast_join(dev, mcast); } int ipoib_mcast_start_thread(struct net_device *dev) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 8a51c3b5d657..b0edb66a291b 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -66,6 +66,7 @@ isert_rdma_accept(struct isert_conn *isert_conn); struct rdma_cm_id *isert_setup_id(struct isert_np *isert_np); static void isert_release_work(struct work_struct *work); +static void isert_wait4flush(struct isert_conn *isert_conn); static inline bool isert_prot_cmd(struct isert_conn *conn, struct se_cmd *cmd) @@ -815,12 +816,31 @@ isert_put_conn(struct isert_conn *isert_conn) kref_put(&isert_conn->kref, isert_release_kref); } +static void +isert_handle_unbound_conn(struct isert_conn *isert_conn) +{ + struct isert_np *isert_np = isert_conn->cm_id->context; + + mutex_lock(&isert_np->mutex); + if (!list_empty(&isert_conn->node)) { + /* + * This means iscsi doesn't know this connection + * so schedule a cleanup ourselves + */ + list_del_init(&isert_conn->node); + isert_put_conn(isert_conn); + complete(&isert_conn->wait); + queue_work(isert_release_wq, &isert_conn->release_work); + } + mutex_unlock(&isert_np->mutex); +} + /** * isert_conn_terminate() - Initiate connection termination * @isert_conn: isert connection struct * * Notes: - * In case the connection state is FULL_FEATURE, move state + * In case the connection state is BOUND, move state * to TEMINATING and start teardown sequence (rdma_disconnect). * In case the connection state is UP, complete flush as well. * @@ -832,23 +852,19 @@ isert_conn_terminate(struct isert_conn *isert_conn) { int err; - switch (isert_conn->state) { - case ISER_CONN_TERMINATING: - break; - case ISER_CONN_UP: - case ISER_CONN_FULL_FEATURE: /* FALLTHRU */ - isert_info("Terminating conn %p state %d\n", - isert_conn, isert_conn->state); - isert_conn->state = ISER_CONN_TERMINATING; - err = rdma_disconnect(isert_conn->cm_id); - if (err) - isert_warn("Failed rdma_disconnect isert_conn %p\n", - isert_conn); - break; - default: - isert_warn("conn %p teminating in state %d\n", - isert_conn, isert_conn->state); - } + if (isert_conn->state >= ISER_CONN_TERMINATING) + return; + + isert_info("Terminating conn %p state %d\n", + isert_conn, isert_conn->state); + isert_conn->state = ISER_CONN_TERMINATING; + err = rdma_disconnect(isert_conn->cm_id); + if (err) + isert_warn("Failed rdma_disconnect isert_conn %p\n", + isert_conn); + + isert_info("conn %p completing wait\n", isert_conn); + complete(&isert_conn->wait); } static int @@ -882,35 +898,27 @@ static int isert_disconnected_handler(struct rdma_cm_id *cma_id, enum rdma_cm_event_type event) { - struct isert_np *isert_np = cma_id->context; - struct isert_conn *isert_conn; - bool terminating = false; - - if (isert_np->cm_id == cma_id) - return isert_np_cma_handler(cma_id->context, event); - - isert_conn = cma_id->qp->qp_context; + struct isert_conn *isert_conn = cma_id->qp->qp_context; mutex_lock(&isert_conn->mutex); - terminating = (isert_conn->state == ISER_CONN_TERMINATING); - isert_conn_terminate(isert_conn); - mutex_unlock(&isert_conn->mutex); - - isert_info("conn %p completing wait\n", isert_conn); - complete(&isert_conn->wait); - - if (terminating) - goto out; - - mutex_lock(&isert_np->mutex); - if (!list_empty(&isert_conn->node)) { - list_del_init(&isert_conn->node); - isert_put_conn(isert_conn); - queue_work(isert_release_wq, &isert_conn->release_work); + switch (isert_conn->state) { + case ISER_CONN_TERMINATING: + break; + case ISER_CONN_UP: + isert_conn_terminate(isert_conn); + isert_wait4flush(isert_conn); + isert_handle_unbound_conn(isert_conn); + break; + case ISER_CONN_BOUND: + case ISER_CONN_FULL_FEATURE: /* FALLTHRU */ + iscsit_cause_connection_reinstatement(isert_conn->conn, 0); + break; + default: + isert_warn("conn %p teminating in state %d\n", + isert_conn, isert_conn->state); } - mutex_unlock(&isert_np->mutex); + mutex_unlock(&isert_conn->mutex); -out: return 0; } @@ -929,12 +937,16 @@ isert_connect_error(struct rdma_cm_id *cma_id) static int isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) { + struct isert_np *isert_np = cma_id->context; int ret = 0; isert_info("%s (%d): status %d id %p np %p\n", rdma_event_msg(event->event), event->event, event->status, cma_id, cma_id->context); + if (isert_np->cm_id == cma_id) + return isert_np_cma_handler(cma_id->context, event->event); + switch (event->event) { case RDMA_CM_EVENT_CONNECT_REQUEST: ret = isert_connect_request(cma_id, event); @@ -980,13 +992,10 @@ isert_post_recvm(struct isert_conn *isert_conn, u32 count) rx_wr--; rx_wr->next = NULL; /* mark end of work requests list */ - isert_conn->post_recv_buf_count += count; ret = ib_post_recv(isert_conn->qp, isert_conn->rx_wr, &rx_wr_failed); - if (ret) { + if (ret) isert_err("ib_post_recv() failed with ret: %d\n", ret); - isert_conn->post_recv_buf_count -= count; - } return ret; } @@ -1002,12 +1011,9 @@ isert_post_recv(struct isert_conn *isert_conn, struct iser_rx_desc *rx_desc) rx_wr.num_sge = 1; rx_wr.next = NULL; - isert_conn->post_recv_buf_count++; ret = ib_post_recv(isert_conn->qp, &rx_wr, &rx_wr_failed); - if (ret) { + if (ret) isert_err("ib_post_recv() failed with ret: %d\n", ret); - isert_conn->post_recv_buf_count--; - } return ret; } @@ -1120,12 +1126,9 @@ isert_rdma_post_recvl(struct isert_conn *isert_conn) rx_wr.sg_list = &sge; rx_wr.num_sge = 1; - isert_conn->post_recv_buf_count++; ret = ib_post_recv(isert_conn->qp, &rx_wr, &rx_wr_fail); - if (ret) { + if (ret) isert_err("ib_post_recv() failed: %d\n", ret); - isert_conn->post_recv_buf_count--; - } return ret; } @@ -1620,7 +1623,6 @@ isert_rcv_completion(struct iser_rx_desc *desc, ib_dma_sync_single_for_device(ib_dev, rx_dma, rx_buflen, DMA_FROM_DEVICE); - isert_conn->post_recv_buf_count--; } static int @@ -2035,7 +2037,8 @@ is_isert_tx_desc(struct isert_conn *isert_conn, void *wr_id) void *start = isert_conn->rx_descs; int len = ISERT_QP_MAX_RECV_DTOS * sizeof(*isert_conn->rx_descs); - if (wr_id >= start && wr_id < start + len) + if ((wr_id >= start && wr_id < start + len) || + (wr_id == isert_conn->login_req_buf)) return false; return true; @@ -2059,10 +2062,6 @@ isert_cq_comp_err(struct isert_conn *isert_conn, struct ib_wc *wc) isert_unmap_tx_desc(desc, ib_dev); else isert_completion_put(desc, isert_cmd, ib_dev, true); - } else { - isert_conn->post_recv_buf_count--; - if (!isert_conn->post_recv_buf_count) - iscsit_cause_connection_reinstatement(isert_conn->conn, 0); } } @@ -3193,6 +3192,7 @@ accept_wait: conn->context = isert_conn; isert_conn->conn = conn; + isert_conn->state = ISER_CONN_BOUND; isert_set_conn_info(np, conn, isert_conn); diff --git a/drivers/infiniband/ulp/isert/ib_isert.h b/drivers/infiniband/ulp/isert/ib_isert.h index 3d7fbc47c343..1874d21daee0 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.h +++ b/drivers/infiniband/ulp/isert/ib_isert.h @@ -50,6 +50,7 @@ enum iser_ib_op_code { enum iser_conn_state { ISER_CONN_INIT, ISER_CONN_UP, + ISER_CONN_BOUND, ISER_CONN_FULL_FEATURE, ISER_CONN_TERMINATING, ISER_CONN_DOWN, @@ -144,7 +145,6 @@ struct isert_device; struct isert_conn { enum iser_conn_state state; - int post_recv_buf_count; u32 responder_resources; u32 initiator_depth; bool pi_support; diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 2e2fe818ca9f..eaabf3125846 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -1737,47 +1737,6 @@ send_sense: return -1; } -/** - * srpt_rx_mgmt_fn_tag() - Process a task management function by tag. - * @ch: RDMA channel of the task management request. - * @fn: Task management function to perform. - * @req_tag: Tag of the SRP task management request. - * @mgmt_ioctx: I/O context of the task management request. - * - * Returns zero if the target core will process the task management - * request asynchronously. - * - * Note: It is assumed that the initiator serializes tag-based task management - * requests. - */ -static int srpt_rx_mgmt_fn_tag(struct srpt_send_ioctx *ioctx, u64 tag) -{ - struct srpt_device *sdev; - struct srpt_rdma_ch *ch; - struct srpt_send_ioctx *target; - int ret, i; - - ret = -EINVAL; - ch = ioctx->ch; - BUG_ON(!ch); - BUG_ON(!ch->sport); - sdev = ch->sport->sdev; - BUG_ON(!sdev); - spin_lock_irq(&sdev->spinlock); - for (i = 0; i < ch->rq_size; ++i) { - target = ch->ioctx_ring[i]; - if (target->cmd.se_lun == ioctx->cmd.se_lun && - target->cmd.tag == tag && - srpt_get_cmd_state(target) != SRPT_STATE_DONE) { - ret = 0; - /* now let the target core abort &target->cmd; */ - break; - } - } - spin_unlock_irq(&sdev->spinlock); - return ret; -} - static int srp_tmr_to_tcm(int fn) { switch (fn) { @@ -1812,7 +1771,6 @@ static void srpt_handle_tsk_mgmt(struct srpt_rdma_ch *ch, struct se_cmd *cmd; struct se_session *sess = ch->sess; uint64_t unpacked_lun; - uint32_t tag = 0; int tcm_tmr; int rc; @@ -1828,25 +1786,10 @@ static void srpt_handle_tsk_mgmt(struct srpt_rdma_ch *ch, srpt_set_cmd_state(send_ioctx, SRPT_STATE_MGMT); send_ioctx->cmd.tag = srp_tsk->tag; tcm_tmr = srp_tmr_to_tcm(srp_tsk->tsk_mgmt_func); - if (tcm_tmr < 0) { - send_ioctx->cmd.se_tmr_req->response = - TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED; - goto fail; - } unpacked_lun = srpt_unpack_lun((uint8_t *)&srp_tsk->lun, sizeof(srp_tsk->lun)); - - if (srp_tsk->tsk_mgmt_func == SRP_TSK_ABORT_TASK) { - rc = srpt_rx_mgmt_fn_tag(send_ioctx, srp_tsk->task_tag); - if (rc < 0) { - send_ioctx->cmd.se_tmr_req->response = - TMR_TASK_DOES_NOT_EXIST; - goto fail; - } - tag = srp_tsk->task_tag; - } rc = target_submit_tmr(&send_ioctx->cmd, sess, NULL, unpacked_lun, - srp_tsk, tcm_tmr, GFP_KERNEL, tag, + srp_tsk, tcm_tmr, GFP_KERNEL, srp_tsk->task_tag, TARGET_SCF_ACK_KREF); if (rc != 0) { send_ioctx->cmd.se_tmr_req->response = TMR_FUNCTION_REJECTED; diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c index cfd58e87da26..1c5914cae853 100644 --- a/drivers/input/misc/ati_remote2.c +++ b/drivers/input/misc/ati_remote2.c @@ -817,26 +817,49 @@ static int ati_remote2_probe(struct usb_interface *interface, const struct usb_d ar2->udev = udev; + /* Sanity check, first interface must have an endpoint */ + if (alt->desc.bNumEndpoints < 1 || !alt->endpoint) { + dev_err(&interface->dev, + "%s(): interface 0 must have an endpoint\n", __func__); + r = -ENODEV; + goto fail1; + } ar2->intf[0] = interface; ar2->ep[0] = &alt->endpoint[0].desc; + /* Sanity check, the device must have two interfaces */ ar2->intf[1] = usb_ifnum_to_if(udev, 1); + if ((udev->actconfig->desc.bNumInterfaces < 2) || !ar2->intf[1]) { + dev_err(&interface->dev, "%s(): need 2 interfaces, found %d\n", + __func__, udev->actconfig->desc.bNumInterfaces); + r = -ENODEV; + goto fail1; + } + r = usb_driver_claim_interface(&ati_remote2_driver, ar2->intf[1], ar2); if (r) goto fail1; + + /* Sanity check, second interface must have an endpoint */ alt = ar2->intf[1]->cur_altsetting; + if (alt->desc.bNumEndpoints < 1 || !alt->endpoint) { + dev_err(&interface->dev, + "%s(): interface 1 must have an endpoint\n", __func__); + r = -ENODEV; + goto fail2; + } ar2->ep[1] = &alt->endpoint[0].desc; r = ati_remote2_urb_init(ar2); if (r) - goto fail2; + goto fail3; ar2->channel_mask = channel_mask; ar2->mode_mask = mode_mask; r = ati_remote2_setup(ar2, ar2->channel_mask); if (r) - goto fail2; + goto fail3; usb_make_path(udev, ar2->phys, sizeof(ar2->phys)); strlcat(ar2->phys, "/input0", sizeof(ar2->phys)); @@ -845,11 +868,11 @@ static int ati_remote2_probe(struct usb_interface *interface, const struct usb_d r = sysfs_create_group(&udev->dev.kobj, &ati_remote2_attr_group); if (r) - goto fail2; + goto fail3; r = ati_remote2_input_init(ar2); if (r) - goto fail3; + goto fail4; usb_set_intfdata(interface, ar2); @@ -857,10 +880,11 @@ static int ati_remote2_probe(struct usb_interface *interface, const struct usb_d return 0; - fail3: + fail4: sysfs_remove_group(&udev->dev.kobj, &ati_remote2_attr_group); - fail2: + fail3: ati_remote2_urb_cleanup(ar2); + fail2: usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]); fail1: kfree(ar2); diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index ac1fa5f44580..9c0ea36913b4 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1663,6 +1663,8 @@ static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pc pcu->ctrl_intf = usb_ifnum_to_if(pcu->udev, union_desc->bMasterInterface0); + if (!pcu->ctrl_intf) + return -EINVAL; alt = pcu->ctrl_intf->cur_altsetting; pcu->ep_ctrl = &alt->endpoint[0].desc; @@ -1670,6 +1672,8 @@ static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pc pcu->data_intf = usb_ifnum_to_if(pcu->udev, union_desc->bSlaveInterface0); + if (!pcu->data_intf) + return -EINVAL; alt = pcu->data_intf->cur_altsetting; if (alt->desc.bNumEndpoints != 2) { diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c index 63b539d3daba..84909a12ff36 100644 --- a/drivers/input/misc/powermate.c +++ b/drivers/input/misc/powermate.c @@ -307,6 +307,9 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i int error = -ENOMEM; interface = intf->cur_altsetting; + if (interface->desc.bNumEndpoints < 1) + return -EINVAL; + endpoint = &interface->endpoint[0].desc; if (!usb_endpoint_is_int_in(endpoint)) return -EIO; diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 6025eb430c0a..a41d8328c064 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -862,8 +862,9 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse, if (!SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)) return; - /* Bug in FW 8.1, buttons are reported only when ExtBit is 1 */ - if (SYN_ID_FULL(priv->identity) == 0x801 && + /* Bug in FW 8.1 & 8.2, buttons are reported only when ExtBit is 1 */ + if ((SYN_ID_FULL(priv->identity) == 0x801 || + SYN_ID_FULL(priv->identity) == 0x802) && !((psmouse->packet[0] ^ psmouse->packet[3]) & 0x02)) return; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 93ad9df1f294..529edb16565a 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1153,4 +1153,17 @@ config TOUCHSCREEN_GEN_VKEYS To compile this driver as a module, choose M here: the module will be called gen_vkeys. +config TOUCHSCREEN_FT5X06 + tristate "FocalTech touchscreens" + depends on I2C + help + Say Y here if you have a ft5X06 touchscreen. + Ft5x06 controllers are multi touch controllers which can + report 5 touches at a time. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ft5x06_ts. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index aaf7f587ed19..e04e787cea6e 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o +obj-$(CONFIG_TOUCHSCREEN_FT5X06) += ft5x06_ts.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o obj-$(CONFIG_TOUCHSCREEN_GEN_VKEYS) += gen_vkeys.o diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c new file mode 100644 index 000000000000..d619c1d06e9e --- /dev/null +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -0,0 +1,654 @@ +/* + * + * FocalTech ft5x06 TouchScreen driver. + * + * Copyright (c) 2010 Focal tech Ltd. + * Copyright (c) 2012, Code Aurora Forum. 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. + * + */ + +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/regulator/consumer.h> +#include <linux/input/ft5x06_ts.h> + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include <linux/earlysuspend.h> +/* Early-suspend level */ +#define FT5X06_SUSPEND_LEVEL 1 +#endif + +#define CFG_MAX_TOUCH_POINTS 5 + +#define FT_STARTUP_DLY 150 +#define FT_RESET_DLY 20 + +#define FT_PRESS 0x7F +#define FT_MAX_ID 0x0F +#define FT_TOUCH_STEP 6 +#define FT_TOUCH_X_H_POS 3 +#define FT_TOUCH_X_L_POS 4 +#define FT_TOUCH_Y_H_POS 5 +#define FT_TOUCH_Y_L_POS 6 +#define FT_TOUCH_EVENT_POS 3 +#define FT_TOUCH_ID_POS 5 + +#define POINT_READ_BUF (3 + FT_TOUCH_STEP * CFG_MAX_TOUCH_POINTS) + +/*register address*/ +#define FT5X06_REG_PMODE 0xA5 +#define FT5X06_REG_FW_VER 0xA6 +#define FT5X06_REG_POINT_RATE 0x88 +#define FT5X06_REG_THGROUP 0x80 + +/* power register bits*/ +#define FT5X06_PMODE_ACTIVE 0x00 +#define FT5X06_PMODE_MONITOR 0x01 +#define FT5X06_PMODE_STANDBY 0x02 +#define FT5X06_PMODE_HIBERNATE 0x03 + +#define FT5X06_VTG_MIN_UV 2600000 +#define FT5X06_VTG_MAX_UV 3300000 +#define FT5X06_I2C_VTG_MIN_UV 1800000 +#define FT5X06_I2C_VTG_MAX_UV 1800000 + +struct ts_event { + u16 x[CFG_MAX_TOUCH_POINTS]; /*x coordinate */ + u16 y[CFG_MAX_TOUCH_POINTS]; /*y coordinate */ + /* touch event: 0 -- down; 1-- contact; 2 -- contact */ + u8 touch_event[CFG_MAX_TOUCH_POINTS]; + u8 finger_id[CFG_MAX_TOUCH_POINTS]; /*touch ID */ + u16 pressure; + u8 touch_point; +}; + +struct ft5x06_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct ts_event event; + const struct ft5x06_ts_platform_data *pdata; + struct regulator *vdd; + struct regulator *vcc_i2c; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif +}; + +static int ft5x06_i2c_read(struct i2c_client *client, char *writebuf, + int writelen, char *readbuf, int readlen) +{ + int ret; + + if (writelen > 0) { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) + dev_err(&client->dev, "%s: i2c read error.\n", + __func__); + } else { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s:i2c read error.\n", __func__); + } + return ret; +} + +static int ft5x06_i2c_write(struct i2c_client *client, char *writebuf, + int writelen) +{ + int ret; + + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s: i2c write error.\n", __func__); + + return ret; +} + +static void ft5x06_report_value(struct ft5x06_ts_data *data) +{ + struct ts_event *event = &data->event; + int i; + int fingerdown = 0; + + for (i = 0; i < event->touch_point; i++) { + if (event->touch_event[i] == 0 || event->touch_event[i] == 2) { + event->pressure = FT_PRESS; + fingerdown++; + } else { + event->pressure = 0; + } + + input_report_abs(data->input_dev, ABS_MT_POSITION_X, + event->x[i]); + input_report_abs(data->input_dev, ABS_MT_POSITION_Y, + event->y[i]); + input_report_abs(data->input_dev, ABS_MT_PRESSURE, + event->pressure); + input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, + event->finger_id[i]); + input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, + event->pressure); + input_mt_sync(data->input_dev); + } + + input_report_key(data->input_dev, BTN_TOUCH, !!fingerdown); + input_sync(data->input_dev); +} + +static int ft5x06_handle_touchdata(struct ft5x06_ts_data *data) +{ + struct ts_event *event = &data->event; + int ret, i; + u8 buf[POINT_READ_BUF] = { 0 }; + u8 pointid = FT_MAX_ID; + + ret = ft5x06_i2c_read(data->client, buf, 1, buf, POINT_READ_BUF); + if (ret < 0) { + dev_err(&data->client->dev, "%s read touchdata failed.\n", + __func__); + return ret; + } + memset(event, 0, sizeof(struct ts_event)); + + event->touch_point = 0; + for (i = 0; i < CFG_MAX_TOUCH_POINTS; i++) { + pointid = (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4; + if (pointid >= FT_MAX_ID) + break; + + event->touch_point++; + + event->x[i] = + (s16) (buf[FT_TOUCH_X_H_POS + FT_TOUCH_STEP * i] & 0x0F) << + 8 | (s16) buf[FT_TOUCH_X_L_POS + FT_TOUCH_STEP * i]; + event->y[i] = + (s16) (buf[FT_TOUCH_Y_H_POS + FT_TOUCH_STEP * i] & 0x0F) << + 8 | (s16) buf[FT_TOUCH_Y_L_POS + FT_TOUCH_STEP * i]; + event->touch_event[i] = + buf[FT_TOUCH_EVENT_POS + FT_TOUCH_STEP * i] >> 6; + event->finger_id[i] = + (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4; + } + + ft5x06_report_value(data); + + return 0; +} + +static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id) +{ + struct ft5x06_ts_data *data = dev_id; + int rc; + + rc = ft5x06_handle_touchdata(data); + if (rc) + pr_err("%s: handling touchdata failed\n", __func__); + + return IRQ_HANDLED; +} + +static int ft5x06_power_on(struct ft5x06_ts_data *data, bool on) +{ + int rc; + + if (!on) + goto power_off; + + rc = regulator_enable(data->vdd); + if (rc) { + dev_err(&data->client->dev, + "Regulator vdd enable failed rc=%d\n", rc); + return rc; + } + + rc = regulator_enable(data->vcc_i2c); + if (rc) { + dev_err(&data->client->dev, + "Regulator vcc_i2c enable failed rc=%d\n", rc); + regulator_disable(data->vdd); + } + + return rc; + +power_off: + rc = regulator_disable(data->vdd); + if (rc) { + dev_err(&data->client->dev, + "Regulator vdd disable failed rc=%d\n", rc); + return rc; + } + + rc = regulator_disable(data->vcc_i2c); + if (rc) { + dev_err(&data->client->dev, + "Regulator vcc_i2c disable failed rc=%d\n", rc); + regulator_enable(data->vdd); + } + + return rc; +} + +static int ft5x06_power_init(struct ft5x06_ts_data *data, bool on) +{ + int rc; + + if (!on) + goto pwr_deinit; + + data->vdd = regulator_get(&data->client->dev, "vdd"); + if (IS_ERR(data->vdd)) { + rc = PTR_ERR(data->vdd); + dev_err(&data->client->dev, + "Regulator get failed vdd rc=%d\n", rc); + return rc; + } + + if (regulator_count_voltages(data->vdd) > 0) { + rc = regulator_set_voltage(data->vdd, FT5X06_VTG_MIN_UV, + FT5X06_VTG_MAX_UV); + if (rc) { + dev_err(&data->client->dev, + "Regulator set_vtg failed vdd rc=%d\n", rc); + goto reg_vdd_put; + } + } + + data->vcc_i2c = regulator_get(&data->client->dev, "vcc_i2c"); + if (IS_ERR(data->vcc_i2c)) { + rc = PTR_ERR(data->vcc_i2c); + dev_err(&data->client->dev, + "Regulator get failed vcc_i2c rc=%d\n", rc); + goto reg_vdd_set_vtg; + } + + if (regulator_count_voltages(data->vcc_i2c) > 0) { + rc = regulator_set_voltage(data->vcc_i2c, FT5X06_I2C_VTG_MIN_UV, + FT5X06_I2C_VTG_MAX_UV); + if (rc) { + dev_err(&data->client->dev, + "Regulator set_vtg failed vcc_i2c rc=%d\n", rc); + goto reg_vcc_i2c_put; + } + } + + return 0; + +reg_vcc_i2c_put: + regulator_put(data->vcc_i2c); +reg_vdd_set_vtg: + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, 0, FT5X06_VTG_MAX_UV); +reg_vdd_put: + regulator_put(data->vdd); + return rc; + +pwr_deinit: + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, 0, FT5X06_VTG_MAX_UV); + + regulator_put(data->vdd); + + if (regulator_count_voltages(data->vcc_i2c) > 0) + regulator_set_voltage(data->vcc_i2c, 0, FT5X06_I2C_VTG_MAX_UV); + + regulator_put(data->vcc_i2c); + return 0; +} + +#ifdef CONFIG_PM +static int ft5x06_ts_suspend(struct device *dev) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + char txbuf[2]; + + disable_irq(data->client->irq); + + if (gpio_is_valid(data->pdata->reset_gpio)) { + txbuf[0] = FT5X06_REG_PMODE; + txbuf[1] = FT5X06_PMODE_HIBERNATE; + ft5x06_i2c_write(data->client, txbuf, sizeof(txbuf)); + } + + return 0; +} + +static int ft5x06_ts_resume(struct device *dev) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + + if (gpio_is_valid(data->pdata->reset_gpio)) { + gpio_set_value_cansleep(data->pdata->reset_gpio, 0); + msleep(FT_RESET_DLY); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } + enable_irq(data->client->irq); + + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void ft5x06_ts_early_suspend(struct early_suspend *handler) +{ + struct ft5x06_ts_data *data = container_of(handler, + struct ft5x06_ts_data, + early_suspend); + + ft5x06_ts_suspend(&data->client->dev); +} + +static void ft5x06_ts_late_resume(struct early_suspend *handler) +{ + struct ft5x06_ts_data *data = container_of(handler, + struct ft5x06_ts_data, + early_suspend); + + ft5x06_ts_resume(&data->client->dev); +} +#endif + +static const struct dev_pm_ops ft5x06_ts_pm_ops = { +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = ft5x06_ts_suspend, + .resume = ft5x06_ts_resume, +#endif +}; +#endif + +static int ft5x06_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct ft5x06_ts_platform_data *pdata = client->dev.platform_data; + struct ft5x06_ts_data *data; + struct input_dev *input_dev; + u8 reg_value; + u8 reg_addr; + int err; + + if (!pdata) { + dev_err(&client->dev, "Invalid pdata\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "I2C not supported\n"); + return -ENODEV; + } + + data = kzalloc(sizeof(struct ft5x06_ts_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + input_dev = input_allocate_device(); + if (!input_dev) { + err = -ENOMEM; + dev_err(&client->dev, "failed to allocate input device\n"); + goto free_mem; + } + + data->input_dev = input_dev; + data->client = client; + data->pdata = pdata; + + input_dev->name = "ft5x06_ts"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + input_set_drvdata(input_dev, data); + i2c_set_clientdata(client, data); + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, + pdata->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, + pdata->y_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, + CFG_MAX_TOUCH_POINTS, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, FT_PRESS, 0, 0); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, FT_PRESS, 0, 0); + + err = input_register_device(input_dev); + if (err) { + dev_err(&client->dev, "Input device registration failed\n"); + goto free_inputdev; + } + + if (pdata->power_init) { + err = pdata->power_init(true); + if (err) { + dev_err(&client->dev, "power init failed"); + goto unreg_inputdev; + } + } else { + err = ft5x06_power_init(data, true); + if (err) { + dev_err(&client->dev, "power init failed"); + goto unreg_inputdev; + } + } + + if (pdata->power_on) { + err = pdata->power_on(true); + if (err) { + dev_err(&client->dev, "power on failed"); + goto pwr_deinit; + } + } else { + err = ft5x06_power_on(data, true); + if (err) { + dev_err(&client->dev, "power on failed"); + goto pwr_deinit; + } + } + + if (gpio_is_valid(pdata->irq_gpio)) { + err = gpio_request(pdata->irq_gpio, "ft5x06_irq_gpio"); + if (err) { + dev_err(&client->dev, "irq gpio request failed"); + goto pwr_off; + } + err = gpio_direction_input(pdata->irq_gpio); + if (err) { + dev_err(&client->dev, + "set_direction for irq gpio failed\n"); + goto free_irq_gpio; + } + } + + if (gpio_is_valid(pdata->reset_gpio)) { + err = gpio_request(pdata->reset_gpio, "ft5x06_reset_gpio"); + if (err) { + dev_err(&client->dev, "reset gpio request failed"); + goto free_irq_gpio; + } + + err = gpio_direction_output(pdata->reset_gpio, 0); + if (err) { + dev_err(&client->dev, + "set_direction for reset gpio failed\n"); + goto free_reset_gpio; + } + msleep(FT_RESET_DLY); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } + + /* make sure CTP already finish startup process */ + msleep(FT_STARTUP_DLY); + + /*get some register information */ + reg_addr = FT5X06_REG_FW_VER; + err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err) + dev_err(&client->dev, "version read failed"); + + dev_info(&client->dev, "[FTS] Firmware version = 0x%x\n", reg_value); + + reg_addr = FT5X06_REG_POINT_RATE; + ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err) + dev_err(&client->dev, "report rate read failed"); + dev_info(&client->dev, "[FTS] report rate is %dHz.\n", reg_value * 10); + + reg_addr = FT5X06_REG_THGROUP; + err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err) + dev_err(&client->dev, "threshold read failed"); + dev_dbg(&client->dev, "[FTS] touch threshold is %d.\n", reg_value * 4); + + err = request_threaded_irq(client->irq, NULL, + ft5x06_ts_interrupt, pdata->irqflags, + client->dev.driver->name, data); + if (err) { + dev_err(&client->dev, "request irq failed\n"); + goto free_reset_gpio; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + + FT5X06_SUSPEND_LEVEL; + data->early_suspend.suspend = ft5x06_ts_early_suspend; + data->early_suspend.resume = ft5x06_ts_late_resume; + register_early_suspend(&data->early_suspend); +#endif + + return 0; + +free_reset_gpio: + if (gpio_is_valid(pdata->reset_gpio)) + gpio_free(pdata->reset_gpio); +free_irq_gpio: + if (gpio_is_valid(pdata->irq_gpio)) + gpio_free(pdata->reset_gpio); +pwr_off: + if (pdata->power_on) + pdata->power_on(false); + else + ft5x06_power_on(data, false); +pwr_deinit: + if (pdata->power_init) + pdata->power_init(false); + else + ft5x06_power_init(data, false); +unreg_inputdev: + input_unregister_device(input_dev); + input_dev = NULL; +free_inputdev: + input_free_device(input_dev); +free_mem: + kfree(data); + return err; +} + +static int ft5x06_ts_remove(struct i2c_client *client) +{ + struct ft5x06_ts_data *data = i2c_get_clientdata(client); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&data->early_suspend); +#endif + free_irq(client->irq, data); + + if (gpio_is_valid(data->pdata->reset_gpio)) + gpio_free(data->pdata->reset_gpio); + + if (gpio_is_valid(data->pdata->irq_gpio)) + gpio_free(data->pdata->reset_gpio); + + if (data->pdata->power_on) + data->pdata->power_on(false); + else + ft5x06_power_on(data, false); + + if (data->pdata->power_init) + data->pdata->power_init(false); + else + ft5x06_power_init(data, false); + + input_unregister_device(data->input_dev); + kfree(data); + + return 0; +} + +static const struct i2c_device_id ft5x06_ts_id[] = { + {"ft5x06_ts", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ft5x06_ts_id); + +static struct i2c_driver ft5x06_ts_driver = { + .probe = ft5x06_ts_probe, + .remove = __devexit_p(ft5x06_ts_remove), + .driver = { + .name = "ft5x06_ts", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &ft5x06_ts_pm_ops, +#endif + }, + .id_table = ft5x06_ts_id, +}; + +static int __init ft5x06_ts_init(void) +{ + return i2c_add_driver(&ft5x06_ts_driver); +} +module_init(ft5x06_ts_init); + +static void __exit ft5x06_ts_exit(void) +{ + i2c_del_driver(&ft5x06_ts_driver); +} +module_exit(ft5x06_ts_exit); + +MODULE_DESCRIPTION("FocalTech ft5x06 TouchScreen driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/gt9xx/goodix_tool.c b/drivers/input/touchscreen/gt9xx/goodix_tool.c new file mode 100644 index 000000000000..3dfe4e1d334e --- /dev/null +++ b/drivers/input/touchscreen/gt9xx/goodix_tool.c @@ -0,0 +1,615 @@ +/* drivers/input/touchscreen/goodix_tool.c
+ *
+ * 2010 - 2012 Goodix Technology.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be a reference
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
+ * 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.
+ *
+ * Version:1.6
+ * V1.0:2012/05/01,create file.
+ * V1.2:2012/06/08,modify some warning.
+ * V1.4:2012/08/28,modified to support GT9XX
+ * V1.6:new proc name
+ */
+
+#include "gt9xx.h"
+
+#define DATA_LENGTH_UINT 512
+#define CMD_HEAD_LENGTH (sizeof(st_cmd_head) - sizeof(u8*))
+static char procname[20] = {0};
+
+#define UPDATE_FUNCTIONS
+
+#ifdef UPDATE_FUNCTIONS
+extern s32 gup_enter_update_mode(struct i2c_client *client);
+extern void gup_leave_update_mode(void);
+extern s32 gup_update_proc(void *dir);
+#endif
+
+extern void gtp_irq_disable(struct goodix_ts_data *);
+extern void gtp_irq_enable(struct goodix_ts_data *);
+
+#pragma pack(1)
+typedef struct{
+ u8 wr; //write read flag£¬0:R 1:W 2:PID 3:
+ u8 flag; //0:no need flag/int 1: need flag 2:need int
+ u8 flag_addr[2]; //flag address
+ u8 flag_val; //flag val
+ u8 flag_relation; //flag_val:flag 0:not equal 1:equal 2:> 3:<
+ u16 circle; //polling cycle
+ u8 times; //plling times
+ u8 retry; //I2C retry times
+ u16 delay; //delay befor read or after write
+ u16 data_len; //data length
+ u8 addr_len; //address length
+ u8 addr[2]; //address
+ u8 res[3]; //reserved
+ u8* data; //data pointer
+}st_cmd_head;
+#pragma pack()
+st_cmd_head cmd_head;
+
+static struct i2c_client *gt_client = NULL;
+
+static struct proc_dir_entry *goodix_proc_entry;
+
+static s32 goodix_tool_write(struct file *filp, const char __user *buff, unsigned long len, void *data);
+static s32 goodix_tool_read( char *page, char **start, off_t off, int count, int *eof, void *data );
+static s32 (*tool_i2c_read)(u8 *, u16);
+static s32 (*tool_i2c_write)(u8 *, u16);
+
+#if GTP_ESD_PROTECT
+extern void gtp_esd_switch(struct i2c_client *, s32);
+#endif
+s32 DATA_LENGTH = 0;
+s8 IC_TYPE[16] = {0};
+
+static void tool_set_proc_name(char * procname)
+{
+ char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May",
+ "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+ char date[20] = {0};
+ char month[4] = {0};
+ int i = 0, n_month = 1, n_day = 0, n_year = 0;
+
+ sprintf(date, "%s", __DATE__);
+
+ //GTP_DEBUG("compile date: %s", date);
+
+ sscanf(date, "%s %d %d", month, &n_day, &n_year);
+
+ for (i = 0; i < 12; ++i)
+ {
+ if (!memcmp(months[i], month, 3))
+ {
+ n_month = i+1;
+ break;
+ }
+ }
+
+ sprintf(procname, "gmnode%04d%02d%02d", n_year, n_month, n_day);
+
+ //GTP_DEBUG("procname = %s", procname);
+}
+
+
+static s32 tool_i2c_read_no_extra(u8* buf, u16 len)
+{
+ s32 ret = -1;
+ s32 i = 0;
+ struct i2c_msg msgs[2];
+
+ msgs[0].flags = !I2C_M_RD;
+ msgs[0].addr = gt_client->addr;
+ msgs[0].len = cmd_head.addr_len;
+ msgs[0].buf = &buf[0];
+
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].addr = gt_client->addr;
+ msgs[1].len = len;
+ msgs[1].buf = &buf[GTP_ADDR_LENGTH];
+
+ for (i = 0; i < cmd_head.retry; i++)
+ {
+ ret=i2c_transfer(gt_client->adapter, msgs, 2);
+ if (ret > 0)
+ {
+ break;
+ }
+ }
+ return ret;
+}
+
+static s32 tool_i2c_write_no_extra(u8* buf, u16 len)
+{
+ s32 ret = -1;
+ s32 i = 0;
+ struct i2c_msg msg;
+
+ msg.flags = !I2C_M_RD;
+ msg.addr = gt_client->addr;
+ msg.len = len;
+ msg.buf = buf;
+
+ for (i = 0; i < cmd_head.retry; i++)
+ {
+ ret=i2c_transfer(gt_client->adapter, &msg, 1);
+ if (ret > 0)
+ {
+ break;
+ }
+ }
+ return ret;
+}
+
+static s32 tool_i2c_read_with_extra(u8* buf, u16 len)
+{
+ s32 ret = -1;
+ u8 pre[2] = {0x0f, 0xff};
+ u8 end[2] = {0x80, 0x00};
+
+ tool_i2c_write_no_extra(pre, 2);
+ ret = tool_i2c_read_no_extra(buf, len);
+ tool_i2c_write_no_extra(end, 2);
+
+ return ret;
+}
+
+static s32 tool_i2c_write_with_extra(u8* buf, u16 len)
+{
+ s32 ret = -1;
+ u8 pre[2] = {0x0f, 0xff};
+ u8 end[2] = {0x80, 0x00};
+
+ tool_i2c_write_no_extra(pre, 2);
+ ret = tool_i2c_write_no_extra(buf, len);
+ tool_i2c_write_no_extra(end, 2);
+
+ return ret;
+}
+
+static void register_i2c_func(void)
+{
+// if (!strncmp(IC_TYPE, "GT818", 5) || !strncmp(IC_TYPE, "GT816", 5)
+// || !strncmp(IC_TYPE, "GT811", 5) || !strncmp(IC_TYPE, "GT818F", 6)
+// || !strncmp(IC_TYPE, "GT827", 5) || !strncmp(IC_TYPE,"GT828", 5)
+// || !strncmp(IC_TYPE, "GT813", 5))
+ if (strncmp(IC_TYPE, "GT8110", 6) && strncmp(IC_TYPE, "GT8105", 6)
+ && strncmp(IC_TYPE, "GT801", 5) && strncmp(IC_TYPE, "GT800", 5)
+ && strncmp(IC_TYPE, "GT801PLUS", 9) && strncmp(IC_TYPE, "GT811", 5)
+ && strncmp(IC_TYPE, "GTxxx", 5))
+ {
+ tool_i2c_read = tool_i2c_read_with_extra;
+ tool_i2c_write = tool_i2c_write_with_extra;
+ GTP_DEBUG("I2C function: with pre and end cmd!");
+ }
+ else
+ {
+ tool_i2c_read = tool_i2c_read_no_extra;
+ tool_i2c_write = tool_i2c_write_no_extra;
+ GTP_INFO("I2C function: without pre and end cmd!");
+ }
+}
+
+static void unregister_i2c_func(void)
+{
+ tool_i2c_read = NULL;
+ tool_i2c_write = NULL;
+ GTP_INFO("I2C function: unregister i2c transfer function!");
+}
+
+
+s32 init_wr_node(struct i2c_client *client)
+{
+ s32 i;
+
+ gt_client = client;
+ memset(&cmd_head, 0, sizeof(cmd_head));
+ cmd_head.data = NULL;
+
+ i = 5;
+ while ((!cmd_head.data) && i)
+ {
+ cmd_head.data = kzalloc(i * DATA_LENGTH_UINT, GFP_KERNEL);
+ if (NULL != cmd_head.data)
+ {
+ break;
+ }
+ i--;
+ }
+ if (i)
+ {
+ DATA_LENGTH = i * DATA_LENGTH_UINT + GTP_ADDR_LENGTH;
+ GTP_INFO("Applied memory size:%d.", DATA_LENGTH);
+ }
+ else
+ {
+ GTP_ERROR("Apply for memory failed.");
+ return FAIL;
+ }
+
+ cmd_head.addr_len = 2;
+ cmd_head.retry = 5;
+
+ register_i2c_func();
+
+ tool_set_proc_name(procname);
+ goodix_proc_entry = create_proc_entry(procname, 0666, NULL);
+ if (goodix_proc_entry == NULL)
+ {
+ GTP_ERROR("Couldn't create proc entry!");
+ return FAIL;
+ }
+ else
+ {
+ GTP_INFO("Create proc entry success!");
+ goodix_proc_entry->write_proc = goodix_tool_write;
+ goodix_proc_entry->read_proc = goodix_tool_read;
+ }
+
+ return SUCCESS;
+}
+
+void uninit_wr_node(void)
+{
+ kfree(cmd_head.data);
+ cmd_head.data = NULL;
+ unregister_i2c_func();
+ remove_proc_entry(procname, NULL);
+}
+
+static u8 relation(u8 src, u8 dst, u8 rlt)
+{
+ u8 ret = 0;
+
+ switch (rlt)
+ {
+ case 0:
+ ret = (src != dst) ? true : false;
+ break;
+
+ case 1:
+ ret = (src == dst) ? true : false;
+ GTP_DEBUG("equal:src:0x%02x dst:0x%02x ret:%d.", src, dst, (s32)ret);
+ break;
+
+ case 2:
+ ret = (src > dst) ? true : false;
+ break;
+
+ case 3:
+ ret = (src < dst) ? true : false;
+ break;
+
+ case 4:
+ ret = (src & dst) ? true : false;
+ break;
+
+ case 5:
+ ret = (!(src | dst)) ? true : false;
+ break;
+
+ default:
+ ret = false;
+ break;
+ }
+
+ return ret;
+}
+
+/*******************************************************
+Function:
+ Comfirm function.
+Input:
+ None.
+Output:
+ Return write length.
+********************************************************/
+static u8 comfirm(void)
+{
+ s32 i = 0;
+ u8 buf[32];
+
+// memcpy(&buf[GTP_ADDR_LENGTH - cmd_head.addr_len], &cmd_head.flag_addr, cmd_head.addr_len);
+// memcpy(buf, &cmd_head.flag_addr, cmd_head.addr_len);//Modified by Scott, 2012-02-17
+ memcpy(buf, cmd_head.flag_addr, cmd_head.addr_len);
+
+ for (i = 0; i < cmd_head.times; i++)
+ {
+ if (tool_i2c_read(buf, 1) <= 0)
+ {
+ GTP_ERROR("Read flag data failed!");
+ return FAIL;
+ }
+ if (true == relation(buf[GTP_ADDR_LENGTH], cmd_head.flag_val, cmd_head.flag_relation))
+ {
+ GTP_DEBUG("value at flag addr:0x%02x.", buf[GTP_ADDR_LENGTH]);
+ GTP_DEBUG("flag value:0x%02x.", cmd_head.flag_val);
+ break;
+ }
+
+ msleep(cmd_head.circle);
+ }
+
+ if (i >= cmd_head.times)
+ {
+ GTP_ERROR("Didn't get the flag to continue!");
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+/*******************************************************
+Function:
+ Goodix tool write function.
+Input:
+ standard proc write function param.
+Output:
+ Return write length.
+********************************************************/
+static s32 goodix_tool_write(struct file *filp, const char __user *buff, unsigned long len, void *data)
+{
+ s32 ret = 0;
+ GTP_DEBUG_FUNC();
+ GTP_DEBUG_ARRAY((u8*)buff, len);
+
+ ret = copy_from_user(&cmd_head, buff, CMD_HEAD_LENGTH);
+ if(ret)
+ {
+ GTP_ERROR("copy_from_user failed.");
+ }
+
+ GTP_DEBUG("wr :0x%02x.", cmd_head.wr);
+ GTP_DEBUG("flag:0x%02x.", cmd_head.flag);
+ GTP_DEBUG("flag addr:0x%02x%02x.", cmd_head.flag_addr[0], cmd_head.flag_addr[1]);
+ GTP_DEBUG("flag val:0x%02x.", cmd_head.flag_val);
+ GTP_DEBUG("flag rel:0x%02x.", cmd_head.flag_relation);
+ GTP_DEBUG("circle :%d.", (s32)cmd_head.circle);
+ GTP_DEBUG("times :%d.", (s32)cmd_head.times);
+ GTP_DEBUG("retry :%d.", (s32)cmd_head.retry);
+ GTP_DEBUG("delay :%d.", (s32)cmd_head.delay);
+ GTP_DEBUG("data len:%d.", (s32)cmd_head.data_len);
+ GTP_DEBUG("addr len:%d.", (s32)cmd_head.addr_len);
+ GTP_DEBUG("addr:0x%02x%02x.", cmd_head.addr[0], cmd_head.addr[1]);
+ GTP_DEBUG("len:%d.", (s32)len);
+ GTP_DEBUG("buf[20]:0x%02x.", buff[CMD_HEAD_LENGTH]);
+
+ if (1 == cmd_head.wr)
+ {
+ // copy_from_user(&cmd_head.data[cmd_head.addr_len], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ if(ret)
+ {
+ GTP_ERROR("copy_from_user failed.");
+ }
+ memcpy(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len], cmd_head.addr, cmd_head.addr_len);
+
+ GTP_DEBUG_ARRAY(cmd_head.data, cmd_head.data_len + cmd_head.addr_len);
+ GTP_DEBUG_ARRAY((u8*)&buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+
+ if (1 == cmd_head.flag)
+ {
+ if (FAIL == comfirm())
+ {
+ GTP_ERROR("[WRITE]Comfirm fail!");
+ return FAIL;
+ }
+ }
+ else if (2 == cmd_head.flag)
+ {
+ //Need interrupt!
+ }
+ if (tool_i2c_write(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len],
+ cmd_head.data_len + cmd_head.addr_len) <= 0)
+ {
+ GTP_ERROR("[WRITE]Write data failed!");
+ return FAIL;
+ }
+
+ GTP_DEBUG_ARRAY(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len],cmd_head.data_len + cmd_head.addr_len);
+ if (cmd_head.delay)
+ {
+ msleep(cmd_head.delay);
+ }
+
+ return cmd_head.data_len + CMD_HEAD_LENGTH;
+ }
+ else if (3 == cmd_head.wr) //Write ic type
+ {
+ ret = copy_from_user(&cmd_head.data[0], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ if(ret)
+ {
+ GTP_ERROR("copy_from_user failed.");
+ }
+ memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len);
+
+ register_i2c_func();
+
+ return cmd_head.data_len + CMD_HEAD_LENGTH;
+ }
+ else if (5 == cmd_head.wr)
+ {
+ //memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len);
+
+ return cmd_head.data_len + CMD_HEAD_LENGTH;
+ }
+ else if (7 == cmd_head.wr)//disable irq!
+ {
+ gtp_irq_disable(i2c_get_clientdata(gt_client));
+
+ #if GTP_ESD_PROTECT
+ gtp_esd_switch(gt_client, SWITCH_OFF);
+ #endif
+ return CMD_HEAD_LENGTH;
+ }
+ else if (9 == cmd_head.wr) //enable irq!
+ {
+ gtp_irq_enable(i2c_get_clientdata(gt_client));
+
+ #if GTP_ESD_PROTECT
+ gtp_esd_switch(gt_client, SWITCH_ON);
+ #endif
+ return CMD_HEAD_LENGTH;
+ }
+ else if(17 == cmd_head.wr)
+ {
+ struct goodix_ts_data *ts = i2c_get_clientdata(gt_client);
+ ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH], &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+ if(ret)
+ {
+ GTP_DEBUG("copy_from_user failed.");
+ }
+ if(cmd_head.data[GTP_ADDR_LENGTH])
+ {
+ GTP_DEBUG("gtp enter rawdiff.");
+ ts->gtp_rawdiff_mode = true;
+ }
+ else
+ {
+ ts->gtp_rawdiff_mode = false;
+ GTP_DEBUG("gtp leave rawdiff.");
+ }
+ return CMD_HEAD_LENGTH;
+ }
+#ifdef UPDATE_FUNCTIONS
+ else if (11 == cmd_head.wr)//Enter update mode!
+ {
+ if (FAIL == gup_enter_update_mode(gt_client))
+ {
+ return FAIL;
+ }
+ }
+ else if (13 == cmd_head.wr)//Leave update mode!
+ {
+ gup_leave_update_mode();
+ }
+ else if (15 == cmd_head.wr) //Update firmware!
+ {
+ show_len = 0;
+ total_len = 0;
+ memset(cmd_head.data, 0, cmd_head.data_len + 1);
+ memcpy(cmd_head.data, &buff[CMD_HEAD_LENGTH], cmd_head.data_len);
+
+ if (FAIL == gup_update_proc((void*)cmd_head.data))
+ {
+ return FAIL;
+ }
+ }
+#endif
+
+ return CMD_HEAD_LENGTH;
+}
+
+/*******************************************************
+Function:
+ Goodix tool read function.
+Input:
+ standard proc read function param.
+Output:
+ Return read length.
+********************************************************/
+static s32 goodix_tool_read( char *page, char **start, off_t off, int count, int *eof, void *data )
+{
+ GTP_DEBUG_FUNC();
+
+ if (cmd_head.wr % 2)
+ {
+ return FAIL;
+ }
+ else if (!cmd_head.wr)
+ {
+ u16 len = 0;
+ s16 data_len = 0;
+ u16 loc = 0;
+
+ if (1 == cmd_head.flag)
+ {
+ if (FAIL == comfirm())
+ {
+ GTP_ERROR("[READ]Comfirm fail!");
+ return FAIL;
+ }
+ }
+ else if (2 == cmd_head.flag)
+ {
+ //Need interrupt!
+ }
+
+ memcpy(cmd_head.data, cmd_head.addr, cmd_head.addr_len);
+
+ GTP_DEBUG("[CMD HEAD DATA] ADDR:0x%02x%02x.", cmd_head.data[0], cmd_head.data[1]);
+ GTP_DEBUG("[CMD HEAD ADDR] ADDR:0x%02x%02x.", cmd_head.addr[0], cmd_head.addr[1]);
+
+ if (cmd_head.delay)
+ {
+ msleep(cmd_head.delay);
+ }
+
+ data_len = cmd_head.data_len;
+ while(data_len > 0)
+ {
+ if (data_len > DATA_LENGTH)
+ {
+ len = DATA_LENGTH;
+ }
+ else
+ {
+ len = data_len;
+ }
+ data_len -= DATA_LENGTH;
+
+ if (tool_i2c_read(cmd_head.data, len) <= 0)
+ {
+ GTP_ERROR("[READ]Read data failed!");
+ return FAIL;
+ }
+ memcpy(&page[loc], &cmd_head.data[GTP_ADDR_LENGTH], len);
+ loc += len;
+
+ GTP_DEBUG_ARRAY(&cmd_head.data[GTP_ADDR_LENGTH], len);
+ GTP_DEBUG_ARRAY(page, len);
+ }
+ }
+ else if (2 == cmd_head.wr)
+ {
+ // memcpy(page, "gt8", cmd_head.data_len);
+ // memcpy(page, "GT818", 5);
+ // page[5] = 0;
+
+ GTP_DEBUG("Return ic type:%s len:%d.", page, (s32)cmd_head.data_len);
+ return cmd_head.data_len;
+ //return sizeof(IC_TYPE_NAME);
+ }
+ else if (4 == cmd_head.wr)
+ {
+ page[0] = show_len >> 8;
+ page[1] = show_len & 0xff;
+ page[2] = total_len >> 8;
+ page[3] = total_len & 0xff;
+
+ return cmd_head.data_len;
+ }
+ else if (6 == cmd_head.wr)
+ {
+ //Read error code!
+ }
+ else if (8 == cmd_head.wr) //Read driver version
+ {
+ // memcpy(page, GTP_DRIVER_VERSION, strlen(GTP_DRIVER_VERSION));
+ s32 tmp_len;
+ tmp_len = strlen(GTP_DRIVER_VERSION);
+ memcpy(page, GTP_DRIVER_VERSION, tmp_len);
+ page[tmp_len] = 0;
+ }
+
+ return cmd_head.data_len;
+}
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.c b/drivers/input/touchscreen/gt9xx/gt9xx.c new file mode 100644 index 000000000000..b1dc08bdd54f --- /dev/null +++ b/drivers/input/touchscreen/gt9xx/gt9xx.c @@ -0,0 +1,1810 @@ +/* drivers/input/touchscreen/gt9xx.c + * + * 2010 - 2013 Goodix Technology. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * 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. + * + * Version: 1.8 + * Authors: andrew@goodix.com, meta@goodix.com + * Release Date: 2013/04/25 + * Revision record: + * V1.0: + * first Release. By Andrew, 2012/08/31 + * V1.2: + * modify gtp_reset_guitar,slot report,tracking_id & 0x0F. By Andrew, 2012/10/15 + * V1.4: + * modify gt9xx_update.c. By Andrew, 2012/12/12 + * V1.6: + * 1. new heartbeat/esd_protect mechanism(add external watchdog) + * 2. doze mode, sliding wakeup + * 3. 3 more cfg_group(GT9 Sensor_ID: 0~5) + * 3. config length verification + * 4. names & comments + * By Meta, 2013/03/11 + * V1.8: + * 1. pen/stylus identification + * 2. read double check & fixed config support + * 2. new esd & slide wakeup optimization + * By Meta, 2013/06/08 + */ + +#include <linux/irq.h> +#include "gt9xx.h" + +#if GTP_ICS_SLOT_REPORT + #include <linux/input/mt.h> +#endif + +static const char *goodix_ts_name = "Goodix Capacitive TouchScreen"; +static struct workqueue_struct *goodix_wq; +struct i2c_client * i2c_connect_client = NULL; +u8 config[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH] + = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff}; + +#if GTP_HAVE_TOUCH_KEY + static const u16 touch_key_array[] = GTP_KEY_TAB; + #define GTP_MAX_KEY_NUM (sizeof(touch_key_array)/sizeof(touch_key_array[0])) + +#if GTP_DEBUG_ON + static const int key_codes[] = {KEY_HOME, KEY_BACK, KEY_MENU, KEY_SEARCH}; + static const char *key_names[] = {"Key_Home", "Key_Back", "Key_Menu", "Key_Search"}; +#endif + +#endif + +static s8 gtp_i2c_test(struct i2c_client *client); +void gtp_reset_guitar(struct i2c_client *client, s32 ms); +void gtp_int_sync(s32 ms); + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void goodix_ts_early_suspend(struct early_suspend *h); +static void goodix_ts_late_resume(struct early_suspend *h); +#endif + +#if GTP_CREATE_WR_NODE +extern s32 init_wr_node(struct i2c_client*); +extern void uninit_wr_node(void); +#endif + +#if GTP_AUTO_UPDATE +extern u8 gup_init_update_proc(struct goodix_ts_data *); +#endif + +#if GTP_ESD_PROTECT +static struct delayed_work gtp_esd_check_work; +static struct workqueue_struct * gtp_esd_check_workqueue = NULL; +static void gtp_esd_check_func(struct work_struct *); +static s32 gtp_init_ext_watchdog(struct i2c_client *client); +void gtp_esd_switch(struct i2c_client *, s32); +#endif + + +#if GTP_SLIDE_WAKEUP +typedef enum +{ + DOZE_DISABLED = 0, + DOZE_ENABLED = 1, + DOZE_WAKEUP = 2, +}DOZE_T; +static DOZE_T doze_status = DOZE_DISABLED; +static s8 gtp_enter_doze(struct goodix_ts_data *ts); +#endif + +static u8 chip_gt9xxs = 0; // true if ic is gt9xxs, like gt915s +u8 grp_cfg_version = 0; + +/******************************************************* +Function: + Read data from the i2c slave device. +Input: + client: i2c device. + buf[0~1]: read start address. + buf[2~len-1]: read data buffer. + len: GTP_ADDR_LENGTH + read bytes count +Output: + numbers of i2c_msgs to transfer: + 2: succeed, otherwise: failed +*********************************************************/ +s32 gtp_i2c_read(struct i2c_client *client, u8 *buf, s32 len) +{ + struct i2c_msg msgs[2]; + s32 ret=-1; + s32 retries = 0; + + GTP_DEBUG_FUNC(); + + msgs[0].flags = !I2C_M_RD; + msgs[0].addr = client->addr; + msgs[0].len = GTP_ADDR_LENGTH; + msgs[0].buf = &buf[0]; + //msgs[0].scl_rate = 300 * 1000; // for Rockchip + + msgs[1].flags = I2C_M_RD; + msgs[1].addr = client->addr; + msgs[1].len = len - GTP_ADDR_LENGTH; + msgs[1].buf = &buf[GTP_ADDR_LENGTH]; + //msgs[1].scl_rate = 300 * 1000; + + while(retries < 5) + { + ret = i2c_transfer(client->adapter, msgs, 2); + if(ret == 2)break; + retries++; + } + if((retries >= 5)) + { + #if GTP_SLIDE_WAKEUP + // reset chip would quit doze mode + if (DOZE_ENABLED == doze_status) + { + return ret; + } + #endif + GTP_DEBUG("I2C communication timeout, resetting chip..."); + gtp_reset_guitar(client, 10); + } + return ret; +} + +/******************************************************* +Function: + Write data to the i2c slave device. +Input: + client: i2c device. + buf[0~1]: write start address. + buf[2~len-1]: data buffer + len: GTP_ADDR_LENGTH + write bytes count +Output: + numbers of i2c_msgs to transfer: + 1: succeed, otherwise: failed +*********************************************************/ +s32 gtp_i2c_write(struct i2c_client *client,u8 *buf,s32 len) +{ + struct i2c_msg msg; + s32 ret = -1; + s32 retries = 0; + + GTP_DEBUG_FUNC(); + + msg.flags = !I2C_M_RD; + msg.addr = client->addr; + msg.len = len; + msg.buf = buf; + //msg.scl_rate = 300 * 1000; // for Rockchip + + while(retries < 5) + { + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret == 1)break; + retries++; + } + if((retries >= 5)) + { + #if GTP_SLIDE_WAKEUP + if (DOZE_ENABLED == doze_status) + { + return ret; + } + #endif + GTP_DEBUG("I2C communication timeout, resetting chip..."); + gtp_reset_guitar(client, 10); + } + return ret; +} +/******************************************************* +Function: + i2c read twice, compare the results +Input: + client: i2c device + addr: operate address + rxbuf: read data to store, if compare successful + len: bytes to read +Output: + FAIL: read failed + SUCCESS: read successful +*********************************************************/ +s32 gtp_i2c_read_dbl_check(struct i2c_client *client, u16 addr, u8 *rxbuf, int len) +{ + u8 buf[16] = {0}; + u8 confirm_buf[16] = {0}; + u8 retry = 0; + + while (retry++ < 3) + { + memset(buf, 0xAA, 16); + buf[0] = (u8)(addr >> 8); + buf[1] = (u8)(addr & 0xFF); + gtp_i2c_read(client, buf, len + 2); + + memset(confirm_buf, 0xAB, 16); + confirm_buf[0] = (u8)(addr >> 8); + confirm_buf[1] = (u8)(addr & 0xFF); + gtp_i2c_read(client, confirm_buf, len + 2); + + if (!memcmp(buf, confirm_buf, len+2)) + { + break; + } + } + if (retry < 3) + { + memcpy(rxbuf, confirm_buf+2, len); + return SUCCESS; + } + else + { + GTP_ERROR("i2c read 0x%04X, %d bytes, double check failed!", addr, len); + return FAIL; + } +} + +/******************************************************* +Function: + Send config. +Input: + client: i2c device. +Output: + result of i2c write operation. + 1: succeed, otherwise: failed +*********************************************************/ +s32 gtp_send_cfg(struct i2c_client *client) +{ + s32 ret = 2; + +#if GTP_DRIVER_SEND_CFG + s32 retry = 0; + struct goodix_ts_data *ts = i2c_get_clientdata(client); + + if (ts->fixed_cfg) + { + GTP_INFO("Ic fixed config, no config sent!"); + return 2; + } + GTP_INFO("driver send config"); + for (retry = 0; retry < 5; retry++) + { + ret = gtp_i2c_write(client, config , GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH); + if (ret > 0) + { + break; + } + } +#endif + + return ret; +} + +/******************************************************* +Function: + Disable irq function +Input: + ts: goodix i2c_client private data +Output: + None. +*********************************************************/ +void gtp_irq_disable(struct goodix_ts_data *ts) +{ + unsigned long irqflags; + + GTP_DEBUG_FUNC(); + + spin_lock_irqsave(&ts->irq_lock, irqflags); + if (!ts->irq_is_disable) + { + ts->irq_is_disable = 1; + disable_irq_nosync(ts->client->irq); + } + spin_unlock_irqrestore(&ts->irq_lock, irqflags); +} + +/******************************************************* +Function: + Enable irq function +Input: + ts: goodix i2c_client private data +Output: + None. +*********************************************************/ +void gtp_irq_enable(struct goodix_ts_data *ts) +{ + unsigned long irqflags = 0; + + GTP_DEBUG_FUNC(); + + spin_lock_irqsave(&ts->irq_lock, irqflags); + if (ts->irq_is_disable) + { + enable_irq(ts->client->irq); + ts->irq_is_disable = 0; + } + spin_unlock_irqrestore(&ts->irq_lock, irqflags); +} + + +/******************************************************* +Function: + Report touch point event +Input: + ts: goodix i2c_client private data + id: trackId + x: input x coordinate + y: input y coordinate + w: input pressure +Output: + None. +*********************************************************/ +static void gtp_touch_down(struct goodix_ts_data* ts,s32 id,s32 x,s32 y,s32 w) +{ +#if GTP_CHANGE_X2Y + GTP_SWAP(x, y); +#endif + +#if GTP_ICS_SLOT_REPORT + input_mt_slot(ts->input_dev, id); + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); +#else + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id); + input_mt_sync(ts->input_dev); +#endif + + GTP_DEBUG("ID:%d, X:%d, Y:%d, W:%d", id, x, y, w); +} + +/******************************************************* +Function: + Report touch release event +Input: + ts: goodix i2c_client private data +Output: + None. +*********************************************************/ +static void gtp_touch_up(struct goodix_ts_data* ts, s32 id) +{ +#if GTP_ICS_SLOT_REPORT + input_mt_slot(ts->input_dev, id); + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1); + GTP_DEBUG("Touch id[%2d] release!", id); +#else + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); + input_mt_sync(ts->input_dev); +#endif +} + + +/******************************************************* +Function: + Goodix touchscreen work function +Input: + work: work struct of goodix_workqueue +Output: + None. +*********************************************************/ +static void goodix_ts_work_func(struct work_struct *work) +{ + u8 end_cmd[3] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF, 0}; + u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1]={GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF}; + u8 touch_num = 0; + u8 finger = 0; + static u16 pre_touch = 0; + static u8 pre_key = 0; +#if GTP_WITH_PEN + static u8 pre_pen = 0; +#endif + u8 key_value = 0; + u8* coor_data = NULL; + s32 input_x = 0; + s32 input_y = 0; + s32 input_w = 0; + s32 id = 0; + s32 i = 0; + s32 ret = -1; + struct goodix_ts_data *ts = NULL; + +#if GTP_SLIDE_WAKEUP + u8 doze_buf[3] = {0x81, 0x4B}; +#endif + + GTP_DEBUG_FUNC(); + ts = container_of(work, struct goodix_ts_data, work); + if (ts->enter_update) + { + return; + } +#if GTP_SLIDE_WAKEUP + if (DOZE_ENABLED == doze_status) + { + ret = gtp_i2c_read(i2c_connect_client, doze_buf, 3); + GTP_DEBUG("0x814B = 0x%02X", doze_buf[2]); + if (ret > 0) + { + if (doze_buf[2] == 0xAA) + { + GTP_INFO("Slide(0xAA) To Light up the screen!"); + doze_status = DOZE_WAKEUP; + input_report_key(ts->input_dev, KEY_POWER, 1); + input_sync(ts->input_dev); + input_report_key(ts->input_dev, KEY_POWER, 0); + input_sync(ts->input_dev); + // clear 0x814B + doze_buf[2] = 0x00; + gtp_i2c_write(i2c_connect_client, doze_buf, 3); + } + else if (doze_buf[2] == 0xBB) + { + GTP_INFO("Slide(0xBB) To Light up the screen!"); + doze_status = DOZE_WAKEUP; + input_report_key(ts->input_dev, KEY_POWER, 1); + input_sync(ts->input_dev); + input_report_key(ts->input_dev, KEY_POWER, 0); + input_sync(ts->input_dev); + // clear 0x814B + doze_buf[2] = 0x00; + gtp_i2c_write(i2c_connect_client, doze_buf, 3); + } + else if (0xC0 == (doze_buf[2] & 0xC0)) + { + GTP_INFO("double click to light up the screen!"); + doze_status = DOZE_WAKEUP; + input_report_key(ts->input_dev, KEY_POWER, 1); + input_sync(ts->input_dev); + input_report_key(ts->input_dev, KEY_POWER, 0); + input_sync(ts->input_dev); + // clear 0x814B + doze_buf[2] = 0x00; + gtp_i2c_write(i2c_connect_client, doze_buf, 3); + } + else + { + gtp_enter_doze(ts); + } + } + if (ts->use_irq) + { + gtp_irq_enable(ts); + } + return; + } +#endif + + ret = gtp_i2c_read(ts->client, point_data, 12); + if (ret < 0) + { + GTP_ERROR("I2C transfer error. errno:%d\n ", ret); + goto exit_work_func; + } + + finger = point_data[GTP_ADDR_LENGTH]; + if((finger & 0x80) == 0) + { + goto exit_work_func; + } + + touch_num = finger & 0x0f; + if (touch_num > GTP_MAX_TOUCH) + { + goto exit_work_func; + } + + if (touch_num > 1) + { + u8 buf[8 * GTP_MAX_TOUCH] = {(GTP_READ_COOR_ADDR + 10) >> 8, (GTP_READ_COOR_ADDR + 10) & 0xff}; + + ret = gtp_i2c_read(ts->client, buf, 2 + 8 * (touch_num - 1)); + memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1)); + } + +#if GTP_HAVE_TOUCH_KEY + key_value = point_data[3 + 8 * touch_num]; + + if(key_value || pre_key) + { + for (i = 0; i < GTP_MAX_KEY_NUM; i++) + { + #if GTP_DEBUG_ON + for (ret = 0; ret < 4; ++ret) + { + if (key_codes[ret] == touch_key_array[i]) + { + GTP_DEBUG("Key: %s %s", key_names[ret], (key_value & (0x01 << i)) ? "Down" : "Up"); + break; + } + } + #endif + input_report_key(ts->input_dev, touch_key_array[i], key_value & (0x01<<i)); + } + touch_num = 0; + pre_touch = 0; + } +#endif + pre_key = key_value; + + GTP_DEBUG("pre_touch:%02x, finger:%02x.", pre_touch, finger); + +#if GTP_ICS_SLOT_REPORT + +#if GTP_WITH_PEN + if (pre_pen && (touch_num == 0)) + { + GTP_DEBUG("Pen touch UP(Slot)!"); + input_report_key(ts->input_dev, BTN_TOOL_PEN, 0); + input_mt_slot(ts->input_dev, 5); + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1); + pre_pen = 0; + } +#endif + if (pre_touch || touch_num) + { + s32 pos = 0; + u16 touch_index = 0; + + coor_data = &point_data[3]; + + if(touch_num) + { + id = coor_data[pos] & 0x0F; + + #if GTP_WITH_PEN + id = coor_data[pos]; + if ((id == 128)) + { + GTP_DEBUG("Pen touch DOWN(Slot)!"); + input_x = coor_data[pos + 1] | (coor_data[pos + 2] << 8); + input_y = coor_data[pos + 3] | (coor_data[pos + 4] << 8); + input_w = coor_data[pos + 5] | (coor_data[pos + 6] << 8); + + input_report_key(ts->input_dev, BTN_TOOL_PEN, 1); + input_mt_slot(ts->input_dev, 5); + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 5); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w); + GTP_DEBUG("Pen/Stylus: (%d, %d)[%d]", input_x, input_y, input_w); + pre_pen = 1; + pre_touch = 0; + } + #endif + + touch_index |= (0x01<<id); + } + + GTP_DEBUG("id = %d,touch_index = 0x%x, pre_touch = 0x%x\n",id, touch_index,pre_touch); + for (i = 0; i < GTP_MAX_TOUCH; i++) + { + #if GTP_WITH_PEN + if (pre_pen == 1) + { + break; + } + #endif + + if (touch_index & (0x01<<i)) + { + input_x = coor_data[pos + 1] | (coor_data[pos + 2] << 8); + input_y = coor_data[pos + 3] | (coor_data[pos + 4] << 8); + input_w = coor_data[pos + 5] | (coor_data[pos + 6] << 8); + + gtp_touch_down(ts, id, input_x, input_y, input_w); + pre_touch |= 0x01 << i; + + pos += 8; + id = coor_data[pos] & 0x0F; + touch_index |= (0x01<<id); + } + else + { + gtp_touch_up(ts, i); + pre_touch &= ~(0x01 << i); + } + } + } +#else + input_report_key(ts->input_dev, BTN_TOUCH, (touch_num || key_value)); + if (touch_num) + { + for (i = 0; i < touch_num; i++) + { + coor_data = &point_data[i * 8 + 3]; + + id = coor_data[0]; // & 0x0F; + input_x = coor_data[1] | (coor_data[2] << 8); + input_y = coor_data[3] | (coor_data[4] << 8); + input_w = coor_data[5] | (coor_data[6] << 8); + + #if GTP_WITH_PEN + if (id == 128) + { + GTP_DEBUG("Pen touch DOWN!"); + input_report_key(ts->input_dev, BTN_TOOL_PEN, 1); + pre_pen = 1; + id = 0; + } + #endif + + gtp_touch_down(ts, id, input_x, input_y, input_w); + } + } + else if (pre_touch) + { + + #if GTP_WITH_PEN + if (pre_pen == 1) + { + GTP_DEBUG("Pen touch UP!"); + input_report_key(ts->input_dev, BTN_TOOL_PEN, 0); + pre_pen = 0; + } + #endif + + GTP_DEBUG("Touch Release!"); + gtp_touch_up(ts, 0); + } + + pre_touch = touch_num; +#endif + + input_sync(ts->input_dev); + +exit_work_func: + if(!ts->gtp_rawdiff_mode) + { + ret = gtp_i2c_write(ts->client, end_cmd, 3); + if (ret < 0) + { + GTP_INFO("I2C write end_cmd error!"); + } + } + if (ts->use_irq) + { + gtp_irq_enable(ts); + } +} + +/******************************************************* +Function: + Timer interrupt service routine for polling mode. +Input: + timer: timer struct pointer +Output: + Timer work mode. + HRTIMER_NORESTART: no restart mode +*********************************************************/ +static enum hrtimer_restart goodix_ts_timer_handler(struct hrtimer *timer) +{ + struct goodix_ts_data *ts = container_of(timer, struct goodix_ts_data, timer); + + GTP_DEBUG_FUNC(); + + queue_work(goodix_wq, &ts->work); + hrtimer_start(&ts->timer, ktime_set(0, (GTP_POLL_TIME+6)*1000000), HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +/******************************************************* +Function: + External interrupt service routine for interrupt mode. +Input: + irq: interrupt number. + dev_id: private data pointer +Output: + Handle Result. + IRQ_HANDLED: interrupt handled successfully +*********************************************************/ +static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) +{ + struct goodix_ts_data *ts = dev_id; + + GTP_DEBUG_FUNC(); + + gtp_irq_disable(ts); + + queue_work(goodix_wq, &ts->work); + + return IRQ_HANDLED; +} +/******************************************************* +Function: + Synchronization. +Input: + ms: synchronization time in millisecond. +Output: + None. +*******************************************************/ +void gtp_int_sync(s32 ms) +{ + GTP_GPIO_OUTPUT(GTP_INT_PORT, 0); + msleep(ms); + GTP_GPIO_AS_INT(GTP_INT_PORT); +} + +/******************************************************* +Function: + Reset chip. +Input: + ms: reset time in millisecond +Output: + None. +*******************************************************/ +void gtp_reset_guitar(struct i2c_client *client, s32 ms) +{ + GTP_DEBUG_FUNC(); + + GTP_GPIO_OUTPUT(GTP_RST_PORT, 0); // begin select I2C slave addr + msleep(ms); // T2: > 10ms + // HIGH: 0x28/0x29, LOW: 0xBA/0xBB + GTP_GPIO_OUTPUT(GTP_INT_PORT, client->addr == 0x14); + + msleep(2); // T3: > 100us + GTP_GPIO_OUTPUT(GTP_RST_PORT, 1); + + msleep(6); // T4: > 5ms + + GTP_GPIO_AS_INPUT(GTP_RST_PORT); // end select I2C slave addr + + gtp_int_sync(50); + +#if GTP_ESD_PROTECT + gtp_init_ext_watchdog(client); +#endif +} + +#if GTP_SLIDE_WAKEUP +/******************************************************* +Function: + Enter doze mode for sliding wakeup. +Input: + ts: goodix tp private data +Output: + 1: succeed, otherwise failed +*******************************************************/ +static s8 gtp_enter_doze(struct goodix_ts_data *ts) +{ + s8 ret = -1; + s8 retry = 0; + u8 i2c_control_buf[3] = {(u8)(GTP_REG_SLEEP >> 8), (u8)GTP_REG_SLEEP, 8}; + + GTP_DEBUG_FUNC(); + +#if GTP_DBL_CLK_WAKEUP + i2c_control_buf[2] = 0x09; +#endif + + gtp_irq_disable(ts); + + GTP_DEBUG("entering doze mode..."); + while(retry++ < 5) + { + i2c_control_buf[0] = 0x80; + i2c_control_buf[1] = 0x46; + ret = gtp_i2c_write(ts->client, i2c_control_buf, 3); + if (ret < 0) + { + GTP_DEBUG("failed to set doze flag into 0x8046, %d", retry); + continue; + } + i2c_control_buf[0] = 0x80; + i2c_control_buf[1] = 0x40; + ret = gtp_i2c_write(ts->client, i2c_control_buf, 3); + if (ret > 0) + { + doze_status = DOZE_ENABLED; + GTP_INFO("GTP has been working in doze mode!"); + gtp_irq_enable(ts); + return ret; + } + msleep(10); + } + GTP_ERROR("GTP send doze cmd failed."); + gtp_irq_enable(ts); + return ret; +} +#else +/******************************************************* +Function: + Enter sleep mode. +Input: + ts: private data. +Output: + Executive outcomes. + 1: succeed, otherwise failed. +*******************************************************/ +static s8 gtp_enter_sleep(struct goodix_ts_data * ts) +{ + s8 ret = -1; + s8 retry = 0; + u8 i2c_control_buf[3] = {(u8)(GTP_REG_SLEEP >> 8), (u8)GTP_REG_SLEEP, 5}; + + GTP_DEBUG_FUNC(); + + GTP_GPIO_OUTPUT(GTP_INT_PORT, 0); + msleep(5); + + while(retry++ < 5) + { + ret = gtp_i2c_write(ts->client, i2c_control_buf, 3); + if (ret > 0) + { + GTP_INFO("GTP enter sleep!"); + + return ret; + } + msleep(10); + } + GTP_ERROR("GTP send sleep cmd failed."); + return ret; +} +#endif +/******************************************************* +Function: + Wakeup from sleep. +Input: + ts: private data. +Output: + Executive outcomes. + >0: succeed, otherwise: failed. +*******************************************************/ +static s8 gtp_wakeup_sleep(struct goodix_ts_data * ts) +{ + u8 retry = 0; + s8 ret = -1; + + GTP_DEBUG_FUNC(); + +#if GTP_POWER_CTRL_SLEEP + while(retry++ < 5) + { + gtp_reset_guitar(ts->client, 20); + + ret = gtp_send_cfg(ts->client); + if (ret < 0) + { + GTP_INFO("Wakeup sleep send config failed!"); + continue; + } + GTP_INFO("GTP wakeup sleep"); + return 1; + } +#else + while(retry++ < 10) + { + #if GTP_SLIDE_WAKEUP + if (DOZE_WAKEUP != doze_status) // wakeup not by slide + { + gtp_reset_guitar(ts->client, 10); + } + else // wakeup by slide + { + doze_status = DOZE_DISABLED; + } + #else + if (chip_gt9xxs == 1) + { + gtp_reset_guitar(ts->client, 10); + } + else + { + GTP_GPIO_OUTPUT(GTP_INT_PORT, 1); + msleep(5); + } + #endif + ret = gtp_i2c_test(ts->client); + if (ret > 0) + { + GTP_INFO("GTP wakeup sleep."); + + #if (!GTP_SLIDE_WAKEUP) + if (chip_gt9xxs == 0) + { + gtp_int_sync(25); + msleep(20); + #if GTP_ESD_PROTECT + gtp_init_ext_watchdog(ts->client); + #endif + } + #endif + return ret; + } + gtp_reset_guitar(ts->client, 20); + } +#endif + + GTP_ERROR("GTP wakeup sleep failed."); + return ret; +} + +/******************************************************* +Function: + Initialize gtp. +Input: + ts: goodix private data +Output: + Executive outcomes. + 0: succeed, otherwise: failed +*******************************************************/ +static s32 gtp_init_panel(struct goodix_ts_data *ts) +{ + s32 ret = -1; + +#if GTP_DRIVER_SEND_CFG + s32 i; + u8 check_sum = 0; + u8 opr_buf[16]; + u8 sensor_id = 0; + + u8 cfg_info_group1[] = CTP_CFG_GROUP1; + u8 cfg_info_group2[] = CTP_CFG_GROUP2; + u8 cfg_info_group3[] = CTP_CFG_GROUP3; + u8 cfg_info_group4[] = CTP_CFG_GROUP4; + u8 cfg_info_group5[] = CTP_CFG_GROUP5; + u8 cfg_info_group6[] = CTP_CFG_GROUP6; + u8 *send_cfg_buf[] = {cfg_info_group1, cfg_info_group2, cfg_info_group3, + cfg_info_group4, cfg_info_group5, cfg_info_group6}; + u8 cfg_info_len[] = { CFG_GROUP_LEN(cfg_info_group1), + CFG_GROUP_LEN(cfg_info_group2), + CFG_GROUP_LEN(cfg_info_group3), + CFG_GROUP_LEN(cfg_info_group4), + CFG_GROUP_LEN(cfg_info_group5), + CFG_GROUP_LEN(cfg_info_group6)}; + + GTP_DEBUG("Config Groups\' Lengths: %d, %d, %d, %d, %d, %d", + cfg_info_len[0], cfg_info_len[1], cfg_info_len[2], cfg_info_len[3], + cfg_info_len[4], cfg_info_len[5]); + + ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1); + if (SUCCESS == ret) + { + if (opr_buf[0] != 0xBE) + { + ts->fw_error = 1; + GTP_ERROR("Firmware error, no config sent!"); + return -1; + } + } + + if ((!cfg_info_len[1]) && (!cfg_info_len[2]) && + (!cfg_info_len[3]) && (!cfg_info_len[4]) && + (!cfg_info_len[5])) + { + sensor_id = 0; + } + else + { + ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID, &sensor_id, 1); + if (SUCCESS == ret) + { + if (sensor_id >= 0x06) + { + GTP_ERROR("Invalid sensor_id(0x%02X), No Config Sent!", sensor_id); + return -1; + } + } + else + { + GTP_ERROR("Failed to get sensor_id, No config sent!"); + return -1; + } + } + GTP_DEBUG("Sensor_ID: %d", sensor_id); + + ts->gtp_cfg_len = cfg_info_len[sensor_id]; + + if (ts->gtp_cfg_len < GTP_CONFIG_MIN_LENGTH) + { + GTP_ERROR("Sensor_ID(%d) matches with NULL or INVALID CONFIG GROUP! NO Config Sent! You need to check you header file CFG_GROUP section!", sensor_id); + return -1; + } + + ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA, &opr_buf[0], 1); + + if (ret == SUCCESS) + { + GTP_DEBUG("CFG_GROUP%d Config Version: %d, 0x%02X; IC Config Version: %d, 0x%02X", sensor_id+1, + send_cfg_buf[sensor_id][0], send_cfg_buf[sensor_id][0], opr_buf[0], opr_buf[0]); + + if (opr_buf[0] < 90) + { + grp_cfg_version = send_cfg_buf[sensor_id][0]; // backup group config version + send_cfg_buf[sensor_id][0] = 0x00; + ts->fixed_cfg = 0; + } + else // treated as fixed config, not send config + { + GTP_INFO("Ic fixed config with config version(%d, 0x%02X)", opr_buf[0], opr_buf[0]); + ts->fixed_cfg = 1; + } + } + else + { + GTP_ERROR("Failed to get ic config version!No config sent!"); + return -1; + } + + memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH); + memcpy(&config[GTP_ADDR_LENGTH], send_cfg_buf[sensor_id], ts->gtp_cfg_len); + +#if GTP_CUSTOM_CFG + config[RESOLUTION_LOC] = (u8)GTP_MAX_WIDTH; + config[RESOLUTION_LOC + 1] = (u8)(GTP_MAX_WIDTH>>8); + config[RESOLUTION_LOC + 2] = (u8)GTP_MAX_HEIGHT; + config[RESOLUTION_LOC + 3] = (u8)(GTP_MAX_HEIGHT>>8); + + if (GTP_INT_TRIGGER == 0) //RISING + { + config[TRIGGER_LOC] &= 0xfe; + } + else if (GTP_INT_TRIGGER == 1) //FALLING + { + config[TRIGGER_LOC] |= 0x01; + } +#endif // GTP_CUSTOM_CFG + + check_sum = 0; + for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++) + { + check_sum += config[i]; + } + config[ts->gtp_cfg_len] = (~check_sum) + 1; + +#else // DRIVER NOT SEND CONFIG + ts->gtp_cfg_len = GTP_CONFIG_MAX_LENGTH; + ret = gtp_i2c_read(ts->client, config, ts->gtp_cfg_len + GTP_ADDR_LENGTH); + if (ret < 0) + { + GTP_ERROR("Read Config Failed, Using Default Resolution & INT Trigger!"); + ts->abs_x_max = GTP_MAX_WIDTH; + ts->abs_y_max = GTP_MAX_HEIGHT; + ts->int_trigger_type = GTP_INT_TRIGGER; + } +#endif // GTP_DRIVER_SEND_CFG + + GTP_DEBUG_FUNC(); + if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0)) + { + ts->abs_x_max = (config[RESOLUTION_LOC + 1] << 8) + config[RESOLUTION_LOC]; + ts->abs_y_max = (config[RESOLUTION_LOC + 3] << 8) + config[RESOLUTION_LOC + 2]; + ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03; + } + ret = gtp_send_cfg(ts->client); + if (ret < 0) + { + GTP_ERROR("Send config error."); + } + GTP_DEBUG("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x", + ts->abs_x_max,ts->abs_y_max,ts->int_trigger_type); + + msleep(10); + return 0; +} + +/******************************************************* +Function: + Read chip version. +Input: + client: i2c device + version: buffer to keep ic firmware version +Output: + read operation return. + 2: succeed, otherwise: failed +*******************************************************/ +s32 gtp_read_version(struct i2c_client *client, u16* version) +{ + s32 ret = -1; + u8 buf[8] = {GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff}; + + GTP_DEBUG_FUNC(); + + ret = gtp_i2c_read(client, buf, sizeof(buf)); + if (ret < 0) + { + GTP_ERROR("GTP read version failed"); + return ret; + } + + if (version) + { + *version = (buf[7] << 8) | buf[6]; + } + + if (buf[5] == 0x00) + { + GTP_INFO("IC Version: %c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[7], buf[6]); + } + else + { + if (buf[5] == 'S' || buf[5] == 's') + { + chip_gt9xxs = 1; + } + GTP_INFO("IC Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]); + } + return ret; +} + +/******************************************************* +Function: + I2c test Function. +Input: + client:i2c client. +Output: + Executive outcomes. + 2: succeed, otherwise failed. +*******************************************************/ +static s8 gtp_i2c_test(struct i2c_client *client) +{ + u8 test[3] = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff}; + u8 retry = 0; + s8 ret = -1; + + GTP_DEBUG_FUNC(); + + while(retry++ < 5) + { + ret = gtp_i2c_read(client, test, 3); + if (ret > 0) + { + return ret; + } + GTP_ERROR("GTP i2c test failed time %d.",retry); + msleep(10); + } + return ret; +} + +/******************************************************* +Function: + Request gpio(INT & RST) ports. +Input: + ts: private data. +Output: + Executive outcomes. + >= 0: succeed, < 0: failed +*******************************************************/ +static s8 gtp_request_io_port(struct goodix_ts_data *ts) +{ + s32 ret = 0; + + ret = GTP_GPIO_REQUEST(GTP_INT_PORT, "GTP_INT_IRQ"); + if (ret < 0) + { + GTP_ERROR("Failed to request GPIO:%d, ERRNO:%d", (s32)GTP_INT_PORT, ret); + ret = -ENODEV; + } + else + { + GTP_GPIO_AS_INT(GTP_INT_PORT); + ts->client->irq = GTP_INT_IRQ; + } + + ret = GTP_GPIO_REQUEST(GTP_RST_PORT, "GTP_RST_PORT"); + if (ret < 0) + { + GTP_ERROR("Failed to request GPIO:%d, ERRNO:%d",(s32)GTP_RST_PORT,ret); + ret = -ENODEV; + } + + GTP_GPIO_AS_INPUT(GTP_RST_PORT); + gtp_reset_guitar(ts->client, 20); + + + if(ret < 0) + { + GTP_GPIO_FREE(GTP_RST_PORT); + GTP_GPIO_FREE(GTP_INT_PORT); + } + + return ret; +} + +/******************************************************* +Function: + Request interrupt. +Input: + ts: private data. +Output: + Executive outcomes. + 0: succeed, -1: failed. +*******************************************************/ +static s8 gtp_request_irq(struct goodix_ts_data *ts) +{ + s32 ret = -1; + const u8 irq_table[] = GTP_IRQ_TAB; + + GTP_DEBUG("INT trigger type:%x", ts->int_trigger_type); + + ret = request_irq(ts->client->irq, + goodix_ts_irq_handler, + irq_table[ts->int_trigger_type], + ts->client->name, + ts); + if (ret) + { + GTP_ERROR("Request IRQ failed!ERRNO:%d.", ret); + GTP_GPIO_AS_INPUT(GTP_INT_PORT); + GTP_GPIO_FREE(GTP_INT_PORT); + + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = goodix_ts_timer_handler; + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + return -1; + } + else + { + gtp_irq_disable(ts); + ts->use_irq = 1; + return 0; + } +} + +/******************************************************* +Function: + Request input device Function. +Input: + ts:private data. +Output: + Executive outcomes. + 0: succeed, otherwise: failed. +*******************************************************/ +static s8 gtp_request_input_dev(struct goodix_ts_data *ts) +{ + s8 ret = -1; + s8 phys[32]; +#if GTP_HAVE_TOUCH_KEY + u8 index = 0; +#endif + + GTP_DEBUG_FUNC(); + + ts->input_dev = input_allocate_device(); + if (ts->input_dev == NULL) + { + GTP_ERROR("Failed to allocate input device."); + return -ENOMEM; + } + + ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ; +#if GTP_ICS_SLOT_REPORT + __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); + input_mt_init_slots(ts->input_dev, 10); // in case of "out of memory" +#else + ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); +#endif + +#if GTP_HAVE_TOUCH_KEY + for (index = 0; index < GTP_MAX_KEY_NUM; index++) + { + input_set_capability(ts->input_dev, EV_KEY, touch_key_array[index]); + } +#endif + +#if GTP_SLIDE_WAKEUP + input_set_capability(ts->input_dev, EV_KEY, KEY_POWER); +#endif + +#if GTP_WITH_PEN + // pen support + __set_bit(BTN_TOOL_PEN, ts->input_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); + __set_bit(INPUT_PROP_POINTER, ts->input_dev->propbit); +#endif + +#if GTP_CHANGE_X2Y + GTP_SWAP(ts->abs_x_max, ts->abs_y_max); +#endif + + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, 255, 0, 0); + + sprintf(phys, "input/ts"); + ts->input_dev->name = goodix_ts_name; + ts->input_dev->phys = phys; + ts->input_dev->id.bustype = BUS_I2C; + ts->input_dev->id.vendor = 0xDEAD; + ts->input_dev->id.product = 0xBEEF; + ts->input_dev->id.version = 10427; + + ret = input_register_device(ts->input_dev); + if (ret) + { + GTP_ERROR("Register %s input device failed", ts->input_dev->name); + return -ENODEV; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend = goodix_ts_early_suspend; + ts->early_suspend.resume = goodix_ts_late_resume; + register_early_suspend(&ts->early_suspend); +#endif + + return 0; +} + +/******************************************************* +Function: + I2c probe. +Input: + client: i2c device struct. + id: device id. +Output: + Executive outcomes. + 0: succeed. +*******************************************************/ +static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + s32 ret = -1; + struct goodix_ts_data *ts; + u16 version_info; + + GTP_DEBUG_FUNC(); + + //do NOT remove these logs + GTP_INFO("GTP Driver Version: %s", GTP_DRIVER_VERSION); + GTP_INFO("GTP Driver Built@%s, %s", __TIME__, __DATE__); + GTP_INFO("GTP I2C Address: 0x%02x", client->addr); + + i2c_connect_client = client; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + { + GTP_ERROR("I2C check functionality failed."); + return -ENODEV; + } + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (ts == NULL) + { + GTP_ERROR("Alloc GFP_KERNEL memory failed."); + return -ENOMEM; + } + + memset(ts, 0, sizeof(*ts)); + INIT_WORK(&ts->work, goodix_ts_work_func); + ts->client = client; + spin_lock_init(&ts->irq_lock); // 2.6.39 later + // ts->irq_lock = SPIN_LOCK_UNLOCKED; // 2.6.39 & before + i2c_set_clientdata(client, ts); + + ts->gtp_rawdiff_mode = 0; + + ret = gtp_request_io_port(ts); + if (ret < 0) + { + GTP_ERROR("GTP request IO port failed."); + kfree(ts); + return ret; + } + + ret = gtp_i2c_test(client); + if (ret < 0) + { + GTP_ERROR("I2C communication ERROR!"); + } + +#if GTP_AUTO_UPDATE + ret = gup_init_update_proc(ts); + if (ret < 0) + { + GTP_ERROR("Create update thread error."); + } +#endif + + ret = gtp_init_panel(ts); + if (ret < 0) + { + GTP_ERROR("GTP init panel failed."); + ts->abs_x_max = GTP_MAX_WIDTH; + ts->abs_y_max = GTP_MAX_HEIGHT; + ts->int_trigger_type = GTP_INT_TRIGGER; + } + + ret = gtp_request_input_dev(ts); + if (ret < 0) + { + GTP_ERROR("GTP request input dev failed"); + } + + ret = gtp_request_irq(ts); + if (ret < 0) + { + GTP_INFO("GTP works in polling mode."); + } + else + { + GTP_INFO("GTP works in interrupt mode."); + } + + ret = gtp_read_version(client, &version_info); + if (ret < 0) + { + GTP_ERROR("Read version failed."); + } + if (ts->use_irq) + { + gtp_irq_enable(ts); + } + +#if GTP_CREATE_WR_NODE + init_wr_node(client); +#endif + +#if GTP_ESD_PROTECT + gtp_esd_switch(client, SWITCH_ON); +#endif + return 0; +} + + +/******************************************************* +Function: + Goodix touchscreen driver release function. +Input: + client: i2c device struct. +Output: + Executive outcomes. 0---succeed. +*******************************************************/ +static int goodix_ts_remove(struct i2c_client *client) +{ + struct goodix_ts_data *ts = i2c_get_clientdata(client); + + GTP_DEBUG_FUNC(); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ts->early_suspend); +#endif + +#if GTP_CREATE_WR_NODE + uninit_wr_node(); +#endif + +#if GTP_ESD_PROTECT + destroy_workqueue(gtp_esd_check_workqueue); +#endif + + if (ts) + { + if (ts->use_irq) + { + GTP_GPIO_AS_INPUT(GTP_INT_PORT); + GTP_GPIO_FREE(GTP_INT_PORT); + free_irq(client->irq, ts); + } + else + { + hrtimer_cancel(&ts->timer); + } + } + + GTP_INFO("GTP driver removing..."); + i2c_set_clientdata(client, NULL); + input_unregister_device(ts->input_dev); + kfree(ts); + + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +/******************************************************* +Function: + Early suspend function. +Input: + h: early_suspend struct. +Output: + None. +*******************************************************/ +static void goodix_ts_early_suspend(struct early_suspend *h) +{ + struct goodix_ts_data *ts; + s8 ret = -1; + ts = container_of(h, struct goodix_ts_data, early_suspend); + + GTP_DEBUG_FUNC(); + +#if GTP_ESD_PROTECT + ts->gtp_is_suspend = 1; + gtp_esd_switch(ts->client, SWITCH_OFF); +#endif + +#if GTP_SLIDE_WAKEUP + ret = gtp_enter_doze(ts); +#else + if (ts->use_irq) + { + gtp_irq_disable(ts); + } + else + { + hrtimer_cancel(&ts->timer); + } + ret = gtp_enter_sleep(ts); +#endif + if (ret < 0) + { + GTP_ERROR("GTP early suspend failed."); + } + // to avoid waking up while not sleeping + // delay 48 + 10ms to ensure reliability + msleep(58); +} + +/******************************************************* +Function: + Late resume function. +Input: + h: early_suspend struct. +Output: + None. +*******************************************************/ +static void goodix_ts_late_resume(struct early_suspend *h) +{ + struct goodix_ts_data *ts; + s8 ret = -1; + ts = container_of(h, struct goodix_ts_data, early_suspend); + + GTP_DEBUG_FUNC(); + + ret = gtp_wakeup_sleep(ts); + +#if GTP_SLIDE_WAKEUP + doze_status = DOZE_DISABLED; +#endif + + if (ret < 0) + { + GTP_ERROR("GTP later resume failed."); + } + + if (ts->use_irq) + { + gtp_irq_enable(ts); + } + else + { + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + } + +#if GTP_ESD_PROTECT + ts->gtp_is_suspend = 0; + gtp_esd_switch(ts->client, SWITCH_ON); +#endif +} +#endif + +#if GTP_ESD_PROTECT +/******************************************************* +Function: + switch on & off esd delayed work +Input: + client: i2c device + on: SWITCH_ON / SWITCH_OFF +Output: + void +*********************************************************/ +void gtp_esd_switch(struct i2c_client *client, s32 on) +{ + struct goodix_ts_data *ts; + + ts = i2c_get_clientdata(client); + if (SWITCH_ON == on) // switch on esd + { + if (!ts->esd_running) + { + ts->esd_running = 1; + GTP_INFO("Esd started"); + queue_delayed_work(gtp_esd_check_workqueue, >p_esd_check_work, GTP_ESD_CHECK_CIRCLE); + } + } + else // switch off esd + { + if (ts->esd_running) + { + ts->esd_running = 0; + GTP_INFO("Esd cancelled"); + cancel_delayed_work_sync(>p_esd_check_work); + } + } +} + +/******************************************************* +Function: + Initialize external watchdog for esd protect +Input: + client: i2c device. +Output: + result of i2c write operation. + 1: succeed, otherwise: failed +*********************************************************/ +static s32 gtp_init_ext_watchdog(struct i2c_client *client) +{ + u8 opr_buffer[4] = {0x80, 0x40, 0xAA, 0xAA}; + + struct i2c_msg msg; // in case of recursively reset by calling gtp_i2c_write + s32 ret = -1; + s32 retries = 0; + + GTP_DEBUG("Init external watchdog..."); + GTP_DEBUG_FUNC(); + + msg.flags = !I2C_M_RD; + msg.addr = client->addr; + msg.len = 4; + msg.buf = opr_buffer; + + while(retries < 5) + { + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret == 1) + { + return 1; + } + retries++; + } + if (retries >= 5) + { + GTP_ERROR("init external watchdog failed!"); + } + return 0; +} + +/******************************************************* +Function: + Esd protect function. + Added external watchdog by meta, 2013/03/07 +Input: + work: delayed work +Output: + None. +*******************************************************/ +static void gtp_esd_check_func(struct work_struct *work) +{ + s32 i; + s32 ret = -1; + struct goodix_ts_data *ts = NULL; + u8 test[4] = {0x80, 0x40}; + + GTP_DEBUG_FUNC(); + + ts = i2c_get_clientdata(i2c_connect_client); + + if (ts->gtp_is_suspend) + { + ts->esd_running = 0; + GTP_INFO("Esd terminated!"); + return; + } + + for (i = 0; i < 3; i++) + { + ret = gtp_i2c_read(ts->client, test, 4); + + GTP_DEBUG("0x8040 = 0x%02X, 0x8041 = 0x%02X", test[2], test[3]); + if ((ret < 0)) + { + // IIC communication problem + continue; + } + else + { + if ((test[2] == 0xAA) || (test[3] != 0xAA)) + { + // IC works abnormally.. + i = 3; + break; + } + else + { + // IC works normally, Write 0x8040 0xAA, feed the dog + test[2] = 0xAA; + gtp_i2c_write(ts->client, test, 3); + break; + } + } + } + if (i >= 3) + { + GTP_ERROR("IC Working ABNORMALLY, Resetting Guitar..."); + gtp_reset_guitar(ts->client, 50); + } + + if(!ts->gtp_is_suspend) + { + queue_delayed_work(gtp_esd_check_workqueue, >p_esd_check_work, GTP_ESD_CHECK_CIRCLE); + } + else + { + GTP_INFO("Esd terminated!"); + ts->esd_running = 0; + } + return; +} +#endif + +static const struct i2c_device_id goodix_ts_id[] = { + { GTP_I2C_NAME, 0 }, + { } +}; + +static struct i2c_driver goodix_ts_driver = { + .probe = goodix_ts_probe, + .remove = goodix_ts_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = goodix_ts_early_suspend, + .resume = goodix_ts_late_resume, +#endif + .id_table = goodix_ts_id, + .driver = { + .name = GTP_I2C_NAME, + .owner = THIS_MODULE, + }, +}; + +/******************************************************* +Function: + Driver Install function. +Input: + None. +Output: + Executive Outcomes. 0---succeed. +********************************************************/ +static int __devinit goodix_ts_init(void) +{ + s32 ret; + + GTP_DEBUG_FUNC(); + GTP_INFO("GTP driver installing..."); + goodix_wq = create_singlethread_workqueue("goodix_wq"); + if (!goodix_wq) + { + GTP_ERROR("Creat workqueue failed."); + return -ENOMEM; + } +#if GTP_ESD_PROTECT + INIT_DELAYED_WORK(>p_esd_check_work, gtp_esd_check_func); + gtp_esd_check_workqueue = create_workqueue("gtp_esd_check"); +#endif + ret = i2c_add_driver(&goodix_ts_driver); + return ret; +} + +/******************************************************* +Function: + Driver uninstall function. +Input: + None. +Output: + Executive Outcomes. 0---succeed. +********************************************************/ +static void __exit goodix_ts_exit(void) +{ + GTP_DEBUG_FUNC(); + GTP_INFO("GTP driver exited."); + i2c_del_driver(&goodix_ts_driver); + if (goodix_wq) + { + destroy_workqueue(goodix_wq); + } +} + +late_initcall(goodix_ts_init); +module_exit(goodix_ts_exit); + +MODULE_DESCRIPTION("GTP Series Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.h b/drivers/input/touchscreen/gt9xx/gt9xx.h new file mode 100644 index 000000000000..e375af530d57 --- /dev/null +++ b/drivers/input/touchscreen/gt9xx/gt9xx.h @@ -0,0 +1,241 @@ +/* drivers/input/touchscreen/gt9xx.h + * + * 2010 - 2013 Goodix Technology. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * 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 _GOODIX_GT9XX_H_ +#define _GOODIX_GT9XX_H_ + +#include <linux/kernel.h> +#include <linux/hrtimer.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/proc_fs.h> +#include <linux/string.h> +#include <asm/uaccess.h> +#include <linux/vmalloc.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <mach/gpio.h> +#include <linux/earlysuspend.h> + +struct goodix_ts_data { + spinlock_t irq_lock; + struct i2c_client *client; + struct input_dev *input_dev; + struct hrtimer timer; + struct work_struct work; + struct early_suspend early_suspend; + s32 irq_is_disable; + s32 use_irq; + u16 abs_x_max; + u16 abs_y_max; + u8 max_touch_num; + u8 int_trigger_type; + u8 green_wake_mode; + u8 chip_type; + u8 enter_update; + u8 gtp_is_suspend; + u8 gtp_rawdiff_mode; + u8 gtp_cfg_len; + u8 fixed_cfg; + u8 esd_running; + u8 fw_error; +}; + +extern u16 show_len; +extern u16 total_len; + +//***************************PART1:ON/OFF define******************************* +#define GTP_CUSTOM_CFG 0 +#define GTP_CHANGE_X2Y 0 +#define GTP_DRIVER_SEND_CFG 1 +#define GTP_HAVE_TOUCH_KEY 0 +#define GTP_POWER_CTRL_SLEEP 0 +#define GTP_ICS_SLOT_REPORT 0 + +#define GTP_AUTO_UPDATE 1 // auto updated by .bin file as default +#define GTP_HEADER_FW_UPDATE 0 // auto updated by head_fw_array in gt9xx_firmware.h, function together with GTP_AUTO_UPDATE + +#define GTP_CREATE_WR_NODE 1 +#define GTP_ESD_PROTECT 0 +#define GTP_WITH_PEN 0 + +#define GTP_SLIDE_WAKEUP 0 +#define GTP_DBL_CLK_WAKEUP 0 // double-click wakeup, function together with GTP_SLIDE_WAKEUP + +#define GTP_DEBUG_ON 1 +#define GTP_DEBUG_ARRAY_ON 0 +#define GTP_DEBUG_FUNC_ON 0 + +//*************************** PART2:TODO define ********************************** +// STEP_1(REQUIRED): Define Configuration Information Group(s) +// Sensor_ID Map: +/* sensor_opt1 sensor_opt2 Sensor_ID + GND GND 0 + VDDIO GND 1 + NC GND 2 + GND NC/300K 3 + VDDIO NC/300K 4 + NC NC/300K 5 +*/ +// TODO: define your own default or for Sensor_ID == 0 config here. +// The predefined one is just a sample config, which is not suitable for your tp in most cases. +#define CTP_CFG_GROUP1 {\ + 0x41,0x1C,0x02,0xC0,0x03,0x0A,0x05,0x01,0x01,0x0F,\ + 0x23,0x0F,0x5F,0x41,0x03,0x05,0x00,0x00,0x00,0x00,\ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x91,0x00,0x0A,\ + 0x28,0x00,0xB8,0x0B,0x00,0x00,0x00,0x9A,0x03,0x25,\ + 0x00,0x00,0x00,0x00,0x00,0x03,0x64,0x32,0x00,0x00,\ + 0x00,0x32,0x8C,0x94,0x05,0x01,0x05,0x00,0x00,0x96,\ + 0x0C,0x22,0xD8,0x0E,0x23,0x56,0x11,0x25,0xFF,0x13,\ + 0x28,0xA7,0x15,0x2E,0x00,0x00,0x10,0x30,0x48,0x00,\ + 0x56,0x4A,0x3A,0xFF,0xFF,0x16,0x00,0x00,0x00,0x00,\ + 0x00,0x01,0x1B,0x14,0x0D,0x19,0x00,0x00,0x01,0x00,\ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ + 0x00,0x00,0x1A,0x18,0x16,0x14,0x12,0x10,0x0E,0x0C,\ + 0x0A,0x08,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,\ + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,\ + 0xFF,0xFF,0x1D,0x1E,0x1F,0x20,0x22,0x24,0x28,0x29,\ + 0x0C,0x0A,0x08,0x00,0x02,0x04,0x05,0x06,0x0E,0xFF,\ + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,\ + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,\ + 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,\ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ + 0x00,0x00,0x00,0x00,0x00,0x00,0x91,0x01\ + } + +// TODO: define your config for Sensor_ID == 1 here, if needed +#define CTP_CFG_GROUP2 {\ + } +// TODO: define your config for Sensor_ID == 2 here, if needed +#define CTP_CFG_GROUP3 {\ + } + +// TODO: define your config for Sensor_ID == 3 here, if needed +#define CTP_CFG_GROUP4 {\ + } + +// TODO: define your config for Sensor_ID == 4 here, if needed +#define CTP_CFG_GROUP5 {\ + } + +// TODO: define your config for Sensor_ID == 5 here, if needed +#define CTP_CFG_GROUP6 {\ + } + +// STEP_2(REQUIRED): Customize your I/O ports & I/O operations +#define GTP_RST_PORT S5PV210_GPJ3(6) +#define GTP_INT_PORT S5PV210_GPH1(3) +#define GTP_INT_IRQ gpio_to_irq(GTP_INT_PORT) +#define GTP_INT_CFG S3C_GPIO_SFN(0xF) + +#define GTP_GPIO_AS_INPUT(pin) do{\ + gpio_direction_input(pin);\ + s3c_gpio_setpull(pin, S3C_GPIO_PULL_NONE);\ + }while(0) +#define GTP_GPIO_AS_INT(pin) do{\ + GTP_GPIO_AS_INPUT(pin);\ + s3c_gpio_cfgpin(pin, GTP_INT_CFG);\ + }while(0) +#define GTP_GPIO_GET_VALUE(pin) gpio_get_value(pin) +#define GTP_GPIO_OUTPUT(pin,level) gpio_direction_output(pin,level) +#define GTP_GPIO_REQUEST(pin, label) gpio_request(pin, label) +#define GTP_GPIO_FREE(pin) gpio_free(pin) +#define GTP_IRQ_TAB {IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_LOW, IRQ_TYPE_LEVEL_HIGH} + +// STEP_3(optional): Specify your special config info if needed +#if GTP_CUSTOM_CFG + #define GTP_MAX_HEIGHT 800 + #define GTP_MAX_WIDTH 480 + #define GTP_INT_TRIGGER 0 // 0: Rising 1: Falling +#else + #define GTP_MAX_HEIGHT 4096 + #define GTP_MAX_WIDTH 4096 + #define GTP_INT_TRIGGER 1 +#endif +#define GTP_MAX_TOUCH 5 +#define GTP_ESD_CHECK_CIRCLE 2000 // jiffy: ms + +// STEP_4(optional): If keys are available and reported as keys, config your key info here +#if GTP_HAVE_TOUCH_KEY + #define GTP_KEY_TAB {KEY_MENU, KEY_HOME, KEY_BACK} +#endif + +//***************************PART3:OTHER define********************************* +#define GTP_DRIVER_VERSION "V1.8<2013/06/08>" +#define GTP_I2C_NAME "Goodix-TS" +#define GTP_POLL_TIME 10 // jiffy: ms +#define GTP_ADDR_LENGTH 2 +#define GTP_CONFIG_MIN_LENGTH 186 +#define GTP_CONFIG_MAX_LENGTH 240 +#define FAIL 0 +#define SUCCESS 1 +#define SWITCH_OFF 0 +#define SWITCH_ON 1 + +// Registers define +#define GTP_READ_COOR_ADDR 0x814E +#define GTP_REG_SLEEP 0x8040 +#define GTP_REG_SENSOR_ID 0x814A +#define GTP_REG_CONFIG_DATA 0x8047 +#define GTP_REG_VERSION 0x8140 + +#define RESOLUTION_LOC 3 +#define TRIGGER_LOC 8 + +#define CFG_GROUP_LEN(p_cfg_grp) (sizeof(p_cfg_grp) / sizeof(p_cfg_grp[0])) +// Log define +#define GTP_INFO(fmt,arg...) printk("<<-GTP-INFO->> "fmt"\n",##arg) +#define GTP_ERROR(fmt,arg...) printk("<<-GTP-ERROR->> "fmt"\n",##arg) +#define GTP_DEBUG(fmt,arg...) do{\ + if(GTP_DEBUG_ON)\ + printk("<<-GTP-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\ + }while(0) +#define GTP_DEBUG_ARRAY(array, num) do{\ + s32 i;\ + u8* a = array;\ + if(GTP_DEBUG_ARRAY_ON)\ + {\ + printk("<<-GTP-DEBUG-ARRAY->>\n");\ + for (i = 0; i < (num); i++)\ + {\ + printk("%02x ", (a)[i]);\ + if ((i + 1 ) %10 == 0)\ + {\ + printk("\n");\ + }\ + }\ + printk("\n");\ + }\ + }while(0) +#define GTP_DEBUG_FUNC() do{\ + if(GTP_DEBUG_FUNC_ON)\ + printk("<<-GTP-FUNC->> Func:%s@Line:%d\n",__func__,__LINE__);\ + }while(0) +#define GTP_SWAP(x, y) do{\ + typeof(x) z = x;\ + x = y;\ + y = z;\ + }while (0) + +//*****************************End of Part III******************************** + +#endif /* _GOODIX_GT9XX_H_ */ diff --git a/drivers/input/touchscreen/gt9xx/gt9xx_firmware.h b/drivers/input/touchscreen/gt9xx/gt9xx_firmware.h new file mode 100644 index 000000000000..3998bf0023f8 --- /dev/null +++ b/drivers/input/touchscreen/gt9xx/gt9xx_firmware.h @@ -0,0 +1,6 @@ +// make sense only when GTP_HEADER_FW_UPDATE & GTP_AUTO_UPDATE are enabled
+// define your own firmware array here
+const unsigned char header_fw_array[] =
+{
+
+};
\ No newline at end of file diff --git a/drivers/input/touchscreen/gt9xx/gt9xx_update.c b/drivers/input/touchscreen/gt9xx/gt9xx_update.c new file mode 100644 index 000000000000..f564a6b3aaed --- /dev/null +++ b/drivers/input/touchscreen/gt9xx/gt9xx_update.c @@ -0,0 +1,1930 @@ +/* drivers/input/touchscreen/gt9xx_update.c + * + * 2010 - 2012 Goodix Technology. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * 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. + * + * Latest Version:1.6 + * Author: andrew@goodix.com + * Revision Record: + * V1.0: + * first release. By Andrew, 2012/08/31 + * V1.2: + * add force update,GT9110P pid map. By Andrew, 2012/10/15 + * V1.4: + * 1. add config auto update function; + * 2. modify enter_update_mode; + * 3. add update file cal checksum. + * By Andrew, 2012/12/12 + * V1.6: + * 1. replace guitar_client with i2c_connect_client; + * 2. support firmware header array update. + * By Meta, 2013/03/11 + */ +#include <linux/kthread.h> +#include "gt9xx.h" + +#if GTP_HEADER_FW_UPDATE +#include <linux/namei.h> +#include <linux/mount.h> +#include "gt9xx_firmware.h" +#endif + +#define GUP_REG_HW_INFO 0x4220 +#define GUP_REG_FW_MSG 0x41E4 +#define GUP_REG_PID_VID 0x8140 + +#define GUP_SEARCH_FILE_TIMES 50 +#define UPDATE_FILE_PATH_2 "/data/_goodix_update_.bin" +#define UPDATE_FILE_PATH_1 "/sdcard/_goodix_update_.bin" + +#define CONFIG_FILE_PATH_1 "/data/_goodix_config_.cfg" +#define CONFIG_FILE_PATH_2 "/sdcard/_goodix_config_.cfg" + +#define FW_HEAD_LENGTH 14 +#define FW_SECTION_LENGTH 0x2000 +#define FW_DSP_ISP_LENGTH 0x1000 +#define FW_DSP_LENGTH 0x1000 +#define FW_BOOT_LENGTH 0x800 + +#define PACK_SIZE 256 +#define MAX_FRAME_CHECK_TIME 5 + +#define _bRW_MISCTL__SRAM_BANK 0x4048 +#define _bRW_MISCTL__MEM_CD_EN 0x4049 +#define _bRW_MISCTL__CACHE_EN 0x404B +#define _bRW_MISCTL__TMR0_EN 0x40B0 +#define _rRW_MISCTL__SWRST_B0_ 0x4180 +#define _bWO_MISCTL__CPU_SWRST_PULSE 0x4184 +#define _rRW_MISCTL__BOOTCTL_B0_ 0x4190 +#define _rRW_MISCTL__BOOT_OPT_B0_ 0x4218 +#define _rRW_MISCTL__BOOT_CTL_ 0x5094 + +#define FAIL 0 +#define SUCCESS 1 + +#pragma pack(1) +typedef struct +{ + u8 hw_info[4]; //hardware info// + u8 pid[8]; //product id // + u16 vid; //version id // +}st_fw_head; +#pragma pack() + +typedef struct +{ + u8 force_update; + u8 fw_flag; + struct file *file; + struct file *cfg_file; + st_fw_head ic_fw_msg; + mm_segment_t old_fs; +}st_update_msg; + +st_update_msg update_msg; +u16 show_len; +u16 total_len; +u8 got_file_flag = 0; +u8 searching_file = 0; +extern u8 config[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH]; +extern void gtp_reset_guitar(struct i2c_client *client, s32 ms); +extern s32 gtp_send_cfg(struct i2c_client *client); +extern struct i2c_client * i2c_connect_client; +extern void gtp_irq_enable(struct goodix_ts_data *ts); +extern void gtp_irq_disable(struct goodix_ts_data *ts); +extern s32 gtp_i2c_read_dbl_check(struct i2c_client *, u16, u8 *, int); +#if GTP_ESD_PROTECT +extern void gtp_esd_switch(struct i2c_client *, s32); +#endif +/******************************************************* +Function: + Read data from the i2c slave device. +Input: + client: i2c device. + buf[0~1]: read start address. + buf[2~len-1]: read data buffer. + len: GTP_ADDR_LENGTH + read bytes count +Output: + numbers of i2c_msgs to transfer: + 2: succeed, otherwise: failed +*********************************************************/ +s32 gup_i2c_read(struct i2c_client *client, u8 *buf, s32 len) +{ + struct i2c_msg msgs[2]; + s32 ret=-1; + s32 retries = 0; + + GTP_DEBUG_FUNC(); + + msgs[0].flags = !I2C_M_RD; + msgs[0].addr = client->addr; + msgs[0].len = GTP_ADDR_LENGTH; + msgs[0].buf = &buf[0]; + //msgs[0].scl_rate = 300 * 1000; // for Rockchip + + msgs[1].flags = I2C_M_RD; + msgs[1].addr = client->addr; + msgs[1].len = len - GTP_ADDR_LENGTH; + msgs[1].buf = &buf[GTP_ADDR_LENGTH]; + //msgs[1].scl_rate = 300 * 1000; + + while(retries < 5) + { + ret = i2c_transfer(client->adapter, msgs, 2); + if(ret == 2)break; + retries++; + } + + return ret; +} + +/******************************************************* +Function: + Write data to the i2c slave device. +Input: + client: i2c device. + buf[0~1]: write start address. + buf[2~len-1]: data buffer + len: GTP_ADDR_LENGTH + write bytes count +Output: + numbers of i2c_msgs to transfer: + 1: succeed, otherwise: failed +*********************************************************/ +s32 gup_i2c_write(struct i2c_client *client,u8 *buf,s32 len) +{ + struct i2c_msg msg; + s32 ret=-1; + s32 retries = 0; + + GTP_DEBUG_FUNC(); + + msg.flags = !I2C_M_RD; + msg.addr = client->addr; + msg.len = len; + msg.buf = buf; + //msg.scl_rate = 300 * 1000; // for Rockchip + + while(retries < 5) + { + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret == 1)break; + retries++; + } + + return ret; +} + +static s32 gup_init_panel(struct goodix_ts_data *ts) +{ + s32 ret = 0; + s32 i = 0; + u8 check_sum = 0; + u8 opr_buf[16]; + u8 sensor_id = 0; + + u8 cfg_info_group1[] = CTP_CFG_GROUP1; + u8 cfg_info_group2[] = CTP_CFG_GROUP2; + u8 cfg_info_group3[] = CTP_CFG_GROUP3; + u8 cfg_info_group4[] = CTP_CFG_GROUP4; + u8 cfg_info_group5[] = CTP_CFG_GROUP5; + u8 cfg_info_group6[] = CTP_CFG_GROUP6; + u8 *send_cfg_buf[] = {cfg_info_group1, cfg_info_group2, cfg_info_group3, + cfg_info_group4, cfg_info_group5, cfg_info_group6}; + u8 cfg_info_len[] = { CFG_GROUP_LEN(cfg_info_group1), + CFG_GROUP_LEN(cfg_info_group2), + CFG_GROUP_LEN(cfg_info_group3), + CFG_GROUP_LEN(cfg_info_group4), + CFG_GROUP_LEN(cfg_info_group5), + CFG_GROUP_LEN(cfg_info_group6)}; + + if ((!cfg_info_len[1]) && (!cfg_info_len[2]) && + (!cfg_info_len[3]) && (!cfg_info_len[4]) && + (!cfg_info_len[5])) + { + sensor_id = 0; + } + else + { + ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID, &sensor_id, 1); + if (SUCCESS == ret) + { + if (sensor_id >= 0x06) + { + GTP_ERROR("Invalid sensor_id(0x%02X), No Config Sent!", sensor_id); + return -1; + } + } + else + { + GTP_ERROR("Failed to get sensor_id, No config sent!"); + return -1; + } + } + + GTP_DEBUG("Sensor_ID: %d", sensor_id); + + ts->gtp_cfg_len = cfg_info_len[sensor_id]; + + if (ts->gtp_cfg_len < GTP_CONFIG_MIN_LENGTH) + { + GTP_ERROR("Sensor_ID(%d) matches with NULL or INVALID CONFIG GROUP! NO Config Sent! You need to check you header file CFG_GROUP section!", sensor_id); + return -1; + } + + ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA, &opr_buf[0], 1); + + if (ret == SUCCESS) + { + GTP_DEBUG("CFG_GROUP%d Config Version: %d, IC Config Version: %d", sensor_id+1, + send_cfg_buf[sensor_id][0], opr_buf[0]); + + send_cfg_buf[sensor_id][0] = opr_buf[0]; + ts->fixed_cfg = 0; + /* + if (opr_buf[0] < 90) + { + grp_cfg_version = send_cfg_buf[sensor_id][0]; // backup group config version + send_cfg_buf[sensor_id][0] = 0x00; + ts->fixed_cfg = 0; + } + else // treated as fixed config, not send config + { + GTP_INFO("Ic fixed config with config version(%d)", opr_buf[0]); + ts->fixed_cfg = 1; + }*/ + } + else + { + GTP_ERROR("Failed to get ic config version!No config sent!"); + return -1; + } + + memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH); + memcpy(&config[GTP_ADDR_LENGTH], send_cfg_buf[sensor_id], ts->gtp_cfg_len); + + GTP_DEBUG("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x", + ts->abs_x_max, ts->abs_y_max, ts->int_trigger_type); + + config[RESOLUTION_LOC] = (u8)GTP_MAX_WIDTH; + config[RESOLUTION_LOC + 1] = (u8)(GTP_MAX_WIDTH>>8); + config[RESOLUTION_LOC + 2] = (u8)GTP_MAX_HEIGHT; + config[RESOLUTION_LOC + 3] = (u8)(GTP_MAX_HEIGHT>>8); + + if (GTP_INT_TRIGGER == 0) //RISING + { + config[TRIGGER_LOC] &= 0xfe; + } + else if (GTP_INT_TRIGGER == 1) //FALLING + { + config[TRIGGER_LOC] |= 0x01; + } + + check_sum = 0; + for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++) + { + check_sum += config[i]; + } + config[ts->gtp_cfg_len] = (~check_sum) + 1; + + GTP_DEBUG_FUNC(); + ret = gtp_send_cfg(ts->client); + if (ret < 0) + { + GTP_ERROR("Send config error."); + } + + msleep(10); + return 0; +} + + +static u8 gup_get_ic_msg(struct i2c_client *client, u16 addr, u8* msg, s32 len) +{ + s32 i = 0; + + msg[0] = (addr >> 8) & 0xff; + msg[1] = addr & 0xff; + + for (i = 0; i < 5; i++) + { + if (gup_i2c_read(client, msg, GTP_ADDR_LENGTH + len) > 0) + { + break; + } + } + + if (i >= 5) + { + GTP_ERROR("Read data from 0x%02x%02x failed!", msg[0], msg[1]); + return FAIL; + } + + return SUCCESS; +} + +static u8 gup_set_ic_msg(struct i2c_client *client, u16 addr, u8 val) +{ + s32 i = 0; + u8 msg[3]; + + msg[0] = (addr >> 8) & 0xff; + msg[1] = addr & 0xff; + msg[2] = val; + + for (i = 0; i < 5; i++) + { + if (gup_i2c_write(client, msg, GTP_ADDR_LENGTH + 1) > 0) + { + break; + } + } + + if (i >= 5) + { + GTP_ERROR("Set data to 0x%02x%02x failed!", msg[0], msg[1]); + return FAIL; + } + + return SUCCESS; +} + +static u8 gup_get_ic_fw_msg(struct i2c_client *client) +{ + s32 ret = -1; + u8 retry = 0; + u8 buf[16]; + u8 i; + + // step1:get hardware info + ret = gtp_i2c_read_dbl_check(client, GUP_REG_HW_INFO, &buf[GTP_ADDR_LENGTH], 4); + if (FAIL == ret) + { + GTP_ERROR("[get_ic_fw_msg]get hw_info failed,exit"); + return FAIL; + } + + // buf[2~5]: 00 06 90 00 + // hw_info: 00 90 06 00 + for(i=0; i<4; i++) + { + update_msg.ic_fw_msg.hw_info[i] = buf[GTP_ADDR_LENGTH + 3 - i]; + } + GTP_DEBUG("IC Hardware info:%02x%02x%02x%02x", update_msg.ic_fw_msg.hw_info[0], update_msg.ic_fw_msg.hw_info[1], + update_msg.ic_fw_msg.hw_info[2], update_msg.ic_fw_msg.hw_info[3]); + // step2:get firmware message + for(retry=0; retry<2; retry++) + { + ret = gup_get_ic_msg(client, GUP_REG_FW_MSG, buf, 1); + if(FAIL == ret) + { + GTP_ERROR("Read firmware message fail."); + return ret; + } + + update_msg.force_update = buf[GTP_ADDR_LENGTH]; + if((0xBE != update_msg.force_update)&&(!retry)) + { + GTP_INFO("The check sum in ic is error."); + GTP_INFO("The IC will be updated by force."); + continue; + } + break; + } + GTP_DEBUG("IC force update flag:0x%x", update_msg.force_update); + + // step3:get pid & vid + ret = gtp_i2c_read_dbl_check(client, GUP_REG_PID_VID, &buf[GTP_ADDR_LENGTH], 6); + if (FAIL == ret) + { + GTP_ERROR("[get_ic_fw_msg]get pid & vid failed,exit"); + return FAIL; + } + + memset(update_msg.ic_fw_msg.pid, 0, sizeof(update_msg.ic_fw_msg.pid)); + memcpy(update_msg.ic_fw_msg.pid, &buf[GTP_ADDR_LENGTH], 4); + GTP_DEBUG("IC Product id:%s", update_msg.ic_fw_msg.pid); + + //GT9XX PID MAPPING + /*|-----FLASH-----RAM-----| + |------918------918-----| + |------968------968-----| + |------913------913-----| + |------913P-----913P----| + |------927------927-----| + |------927P-----927P----| + |------9110-----9110----| + |------9110P----9111----|*/ + if(update_msg.ic_fw_msg.pid[0] != 0) + { + if(!memcmp(update_msg.ic_fw_msg.pid, "9111", 4)) + { + GTP_DEBUG("IC Mapping Product id:%s", update_msg.ic_fw_msg.pid); + memcpy(update_msg.ic_fw_msg.pid, "9110P", 5); + } + } + + update_msg.ic_fw_msg.vid = buf[GTP_ADDR_LENGTH+4] + (buf[GTP_ADDR_LENGTH+5]<<8); + GTP_DEBUG("IC version id:%04x", update_msg.ic_fw_msg.vid); + + return SUCCESS; +} + +s32 gup_enter_update_mode(struct i2c_client *client) +{ + s32 ret = -1; + s32 retry = 0; + u8 rd_buf[3]; + + //step1:RST output low last at least 2ms + GTP_GPIO_OUTPUT(GTP_RST_PORT, 0); + msleep(2); + + //step2:select I2C slave addr,INT:0--0xBA;1--0x28. + GTP_GPIO_OUTPUT(GTP_INT_PORT, (client->addr == 0x14)); + msleep(2); + + //step3:RST output high reset guitar + GTP_GPIO_OUTPUT(GTP_RST_PORT, 1); + + //20121211 modify start + msleep(5); + while(retry++ < 200) + { + //step4:Hold ss51 & dsp + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); + if(ret <= 0) + { + GTP_DEBUG("Hold ss51 & dsp I2C error,retry:%d", retry); + continue; + } + + //step5:Confirm hold + ret = gup_get_ic_msg(client, _rRW_MISCTL__SWRST_B0_, rd_buf, 1); + if(ret <= 0) + { + GTP_DEBUG("Hold ss51 & dsp I2C error,retry:%d", retry); + continue; + } + if(0x0C == rd_buf[GTP_ADDR_LENGTH]) + { + GTP_DEBUG("Hold ss51 & dsp confirm SUCCESS"); + break; + } + GTP_DEBUG("Hold ss51 & dsp confirm 0x4180 failed,value:%d", rd_buf[GTP_ADDR_LENGTH]); + } + if(retry >= 200) + { + GTP_ERROR("Enter update Hold ss51 failed."); + return FAIL; + } + + //step6:DSP_CK and DSP_ALU_CK PowerOn + ret = gup_set_ic_msg(client, 0x4010, 0x00); + + //20121211 modify end + return ret; +} + +void gup_leave_update_mode(void) +{ + GTP_GPIO_AS_INT(GTP_INT_PORT); + + GTP_DEBUG("[leave_update_mode]reset chip."); + gtp_reset_guitar(i2c_connect_client, 20); +} + +// Get the correct nvram data +// The correct conditions: +// 1. the hardware info is the same +// 2. the product id is the same +// 3. the firmware version in update file is greater than the firmware version in ic +// or the check sum in ic is wrong +/* Update Conditions: + 1. Same hardware info + 2. Same PID + 3. File PID > IC PID + Force Update Conditions: + 1. Wrong ic firmware checksum + 2. INVALID IC PID or VID + 3. IC PID == 91XX || File PID == 91XX +*/ + +static u8 gup_enter_update_judge(st_fw_head *fw_head) +{ + u16 u16_tmp; + s32 i = 0; + + u16_tmp = fw_head->vid; + fw_head->vid = (u16)(u16_tmp>>8) + (u16)(u16_tmp<<8); + + GTP_DEBUG("FILE HARDWARE INFO:%02x%02x%02x%02x", fw_head->hw_info[0], fw_head->hw_info[1], fw_head->hw_info[2], fw_head->hw_info[3]); + GTP_DEBUG("FILE PID:%s", fw_head->pid); + GTP_DEBUG("FILE VID:%04x", fw_head->vid); + + GTP_DEBUG("IC HARDWARE INFO:%02x%02x%02x%02x", update_msg.ic_fw_msg.hw_info[0], update_msg.ic_fw_msg.hw_info[1], + update_msg.ic_fw_msg.hw_info[2], update_msg.ic_fw_msg.hw_info[3]); + GTP_DEBUG("IC PID:%s", update_msg.ic_fw_msg.pid); + GTP_DEBUG("IC VID:%04x", update_msg.ic_fw_msg.vid); + + //First two conditions + if ( !memcmp(fw_head->hw_info, update_msg.ic_fw_msg.hw_info, sizeof(update_msg.ic_fw_msg.hw_info))) + { + GTP_DEBUG("Get the same hardware info."); + if( update_msg.force_update != 0xBE ) + { + GTP_INFO("FW chksum error,need enter update."); + return SUCCESS; + } + + // 20130523 start + if (strlen(update_msg.ic_fw_msg.pid) < 3) + { + GTP_INFO("Illegal IC pid, need enter update"); + return SUCCESS; + } + else + { + for (i = 0; i < 3; i++) + { + if ((update_msg.ic_fw_msg.pid[i] < 0x30) || (update_msg.ic_fw_msg.pid[i] > 0x39)) + { + GTP_INFO("Illegal IC pid, out of bound, need enter update"); + return SUCCESS; + } + } + } + // 20130523 end + + + if (( !memcmp(fw_head->pid, update_msg.ic_fw_msg.pid, (strlen(fw_head->pid)<3?3:strlen(fw_head->pid))))|| + (!memcmp(update_msg.ic_fw_msg.pid, "91XX", 4))|| + (!memcmp(fw_head->pid, "91XX", 4))) + { + if(!memcmp(fw_head->pid, "91XX", 4)) + { + GTP_DEBUG("Force none same pid update mode."); + } + else + { + GTP_DEBUG("Get the same pid."); + } + //The third condition + if (fw_head->vid > update_msg.ic_fw_msg.vid) + { + + GTP_INFO("Need enter update."); + return SUCCESS; + } + GTP_ERROR("Don't meet the third condition."); + GTP_ERROR("File VID <= Ic VID, update aborted!"); + } + else + { + GTP_ERROR("File PID != Ic PID, update aborted!"); + } + } + else + { + GTP_ERROR("Different Hardware, update aborted!"); + } + return FAIL; +} + +static u8 ascii2hex(u8 a) +{ + s8 value = 0; + + if(a >= '0' && a <= '9') + { + value = a - '0'; + } + else if(a >= 'A' && a <= 'F') + { + value = a - 'A' + 0x0A; + } + else if(a >= 'a' && a <= 'f') + { + value = a - 'a' + 0x0A; + } + else + { + value = 0xff; + } + + return value; +} + +static s8 gup_update_config(struct i2c_client *client) +{ + s32 file_len = 0; + s32 ret = 0; + s32 i = 0; + s32 file_cfg_len = 0; + s32 chip_cfg_len = 0; + s32 count = 0; + u8 *buf; + u8 *pre_buf; + u8 *file_config; + //u8 checksum = 0; + u8 pid[8]; + + if(NULL == update_msg.cfg_file) + { + GTP_ERROR("[update_cfg]No need to upgrade config!"); + return FAIL; + } + file_len = update_msg.cfg_file->f_op->llseek(update_msg.cfg_file, 0, SEEK_END); + + ret = gup_get_ic_msg(client, GUP_REG_PID_VID, pid, 6); + if(FAIL == ret) + { + GTP_ERROR("[update_cfg]Read product id & version id fail."); + return FAIL; + } + pid[5] = '\0'; + GTP_DEBUG("update cfg get pid:%s", &pid[GTP_ADDR_LENGTH]); + + chip_cfg_len = 186; + if(!memcmp(&pid[GTP_ADDR_LENGTH], "968", 3) || + !memcmp(&pid[GTP_ADDR_LENGTH], "910", 3) || + !memcmp(&pid[GTP_ADDR_LENGTH], "960", 3)) + { + chip_cfg_len = 228; + } + GTP_DEBUG("[update_cfg]config file len:%d", file_len); + GTP_DEBUG("[update_cfg]need config len:%d",chip_cfg_len); + if((file_len+5) < chip_cfg_len*5) + { + GTP_ERROR("Config length error"); + return -1; + } + + buf = (u8*)kzalloc(file_len, GFP_KERNEL); + pre_buf = (u8*)kzalloc(file_len, GFP_KERNEL); + file_config = (u8*)kzalloc(chip_cfg_len + GTP_ADDR_LENGTH, GFP_KERNEL); + update_msg.cfg_file->f_op->llseek(update_msg.cfg_file, 0, SEEK_SET); + + GTP_DEBUG("[update_cfg]Read config from file."); + ret = update_msg.cfg_file->f_op->read(update_msg.cfg_file, (char*)pre_buf, file_len, &update_msg.cfg_file->f_pos); + if(ret<0) + { + GTP_ERROR("[update_cfg]Read config file failed."); + goto update_cfg_file_failed; + } + + GTP_DEBUG("[update_cfg]Delete illgal charactor."); + for(i=0,count=0; i<file_len; i++) + { + if (pre_buf[i] == ' ' || pre_buf[i] == '\r' || pre_buf[i] == '\n') + { + continue; + } + buf[count++] = pre_buf[i]; + } + + GTP_DEBUG("[update_cfg]Ascii to hex."); + file_config[0] = GTP_REG_CONFIG_DATA >> 8; + file_config[1] = GTP_REG_CONFIG_DATA & 0xff; + for(i=0,file_cfg_len=GTP_ADDR_LENGTH; i<count; i+=5) + { + if((buf[i]=='0') && ((buf[i+1]=='x') || (buf[i+1]=='X'))) + { + u8 high,low; + high = ascii2hex(buf[i+2]); + low = ascii2hex(buf[i+3]); + + if((high == 0xFF) || (low == 0xFF)) + { + ret = 0; + GTP_ERROR("[update_cfg]Illegal config file."); + goto update_cfg_file_failed; + } + file_config[file_cfg_len++] = (high<<4) + low; + } + else + { + ret = 0; + GTP_ERROR("[update_cfg]Illegal config file."); + goto update_cfg_file_failed; + } + } + +// //cal checksum +// for(i=GTP_ADDR_LENGTH; i<chip_cfg_len; i++) +// { +// checksum += file_config[i]; +// } +// file_config[chip_cfg_len] = (~checksum) + 1; +// file_config[chip_cfg_len+1] = 0x01; + + GTP_DEBUG("config:"); + GTP_DEBUG_ARRAY(file_config+2, file_cfg_len); + + i = 0; + while(i++ < 5) + { + ret = gup_i2c_write(client, file_config, file_cfg_len); + if(ret > 0) + { + GTP_INFO("[update_cfg]Send config SUCCESS."); + break; + } + GTP_ERROR("[update_cfg]Send config i2c error."); + } + +update_cfg_file_failed: + kfree(pre_buf); + kfree(buf); + kfree(file_config); + return ret; +} + +#if GTP_HEADER_FW_UPDATE +static u8 gup_check_fs_mounted(char *path_name) +{ + struct path root_path; + struct path path; + int err; + err = kern_path("/", LOOKUP_FOLLOW, &root_path); + + if (err) + { + GTP_DEBUG("\"/\" NOT Mounted: %d", err); + return FAIL; + } + err = kern_path(path_name, LOOKUP_FOLLOW, &path); + + if (err) + { + GTP_DEBUG("/data/ NOT Mounted: %d", err); + return FAIL; + } + + return SUCCESS; + + /* + if (path.mnt->mnt_sb == root_path.mnt->mnt_sb) + { + //-- not mounted + return FAIL; + } + else + { + return SUCCESS; + }*/ + +} +#endif +static u8 gup_check_update_file(struct i2c_client *client, st_fw_head* fw_head, u8* path) +{ + s32 ret = 0; + s32 i = 0; + s32 fw_checksum = 0; + u8 buf[FW_HEAD_LENGTH]; + + if (path) + { + GTP_DEBUG("Update File path:%s, %d", path, strlen(path)); + update_msg.file = filp_open(path, O_RDONLY, 0); + + if (IS_ERR(update_msg.file)) + { + GTP_ERROR("Open update file(%s) error!", path); + return FAIL; + } + } + else + { +#if GTP_HEADER_FW_UPDATE + for (i = 0; i < (GUP_SEARCH_FILE_TIMES); i++) + { + GTP_DEBUG("Waiting for /data mounted [%d]", i); + + if (gup_check_fs_mounted("/data") == SUCCESS) + { + GTP_DEBUG("/data Mounted!"); + break; + } + msleep(3000); + } + if (i >= (GUP_SEARCH_FILE_TIMES)) + { + GTP_ERROR("Wait for /data mounted timeout!"); + return FAIL; + } + + // update config + update_msg.cfg_file = filp_open(CONFIG_FILE_PATH_1, O_RDONLY, 0); + if (IS_ERR(update_msg.cfg_file)) + { + GTP_DEBUG("%s is unavailable", CONFIG_FILE_PATH_1); + } + else + { + GTP_INFO("Update Config File: %s", CONFIG_FILE_PATH_1); + ret = gup_update_config(client); + if(ret <= 0) + { + GTP_ERROR("Update config failed."); + } + filp_close(update_msg.cfg_file, NULL); + } + + if (sizeof(header_fw_array) < (FW_HEAD_LENGTH+FW_SECTION_LENGTH*4+FW_DSP_ISP_LENGTH+FW_DSP_LENGTH+FW_BOOT_LENGTH)) + { + GTP_ERROR("INVALID header_fw_array, check your gt9xx_firmware.h file!"); + return FAIL; + } + update_msg.file = filp_open(UPDATE_FILE_PATH_2, O_CREAT | O_RDWR, 0666); + if ((IS_ERR(update_msg.file))) + { + GTP_ERROR("Failed to Create file: %s for fw_header!", UPDATE_FILE_PATH_2); + return FAIL; + } + update_msg.file->f_op->llseek(update_msg.file, 0, SEEK_SET); + update_msg.file->f_op->write(update_msg.file, (char *)header_fw_array, sizeof(header_fw_array), &update_msg.file->f_pos); + filp_close(update_msg.file, NULL); + update_msg.file = filp_open(UPDATE_FILE_PATH_2, O_RDONLY, 0); +#else + u8 fp_len = max(sizeof(UPDATE_FILE_PATH_1), sizeof(UPDATE_FILE_PATH_2)); + u8 cfp_len = max(sizeof(CONFIG_FILE_PATH_1), sizeof(CONFIG_FILE_PATH_2)); + u8 *search_update_path = (u8*)kzalloc(fp_len, GFP_KERNEL); + u8 *search_cfg_path = (u8*)kzalloc(cfp_len, GFP_KERNEL); + //Begin to search update file,the config file & firmware file must be in the same path,single or double. + searching_file = 1; + for (i = 0; i < GUP_SEARCH_FILE_TIMES; i++) + { + if (searching_file == 0) + { + kfree(search_update_path); + kfree(search_cfg_path); + GTP_INFO(".bin/.cfg update file search forcely terminated!"); + return FAIL; + } + if(i%2) + { + memcpy(search_update_path, UPDATE_FILE_PATH_1, sizeof(UPDATE_FILE_PATH_1)); + memcpy(search_cfg_path, CONFIG_FILE_PATH_1, sizeof(CONFIG_FILE_PATH_1)); + } + else + { + memcpy(search_update_path, UPDATE_FILE_PATH_2, sizeof(UPDATE_FILE_PATH_2)); + memcpy(search_cfg_path, CONFIG_FILE_PATH_2, sizeof(CONFIG_FILE_PATH_2)); + } + + if(!(got_file_flag&0x0F)) + { + update_msg.file = filp_open(search_update_path, O_RDONLY, 0); + if(!IS_ERR(update_msg.file)) + { + GTP_DEBUG("Find the bin file"); + got_file_flag |= 0x0F; + } + } + if(!(got_file_flag&0xF0)) + { + update_msg.cfg_file = filp_open(search_cfg_path, O_RDONLY, 0); + if(!IS_ERR(update_msg.cfg_file)) + { + GTP_DEBUG("Find the cfg file"); + got_file_flag |= 0xF0; + } + } + + if(got_file_flag) + { + if(got_file_flag == 0xFF) + { + break; + } + else + { + i += 4; + } + } + GTP_DEBUG("%3d:Searching %s %s file...", i, (got_file_flag&0x0F)?"":"bin", (got_file_flag&0xF0)?"":"cfg"); + msleep(3000); + } + searching_file = 0; + kfree(search_update_path); + kfree(search_cfg_path); + + if(!got_file_flag) + { + GTP_ERROR("Can't find update file."); + goto load_failed; + } + + if(got_file_flag&0xF0) + { + GTP_DEBUG("Got the update config file."); + ret = gup_update_config(client); + if(ret <= 0) + { + GTP_ERROR("Update config failed."); + } + filp_close(update_msg.cfg_file, NULL); + msleep(500); //waiting config to be stored in FLASH. + } + if(got_file_flag&0x0F) + { + GTP_DEBUG("Got the update firmware file."); + } + else + { + GTP_ERROR("No need to upgrade firmware."); + goto load_failed; + } +#endif + } + + update_msg.old_fs = get_fs(); + set_fs(KERNEL_DS); + + update_msg.file->f_op->llseek(update_msg.file, 0, SEEK_SET); + //update_msg.file->f_pos = 0; + + ret = update_msg.file->f_op->read(update_msg.file, (char*)buf, FW_HEAD_LENGTH, &update_msg.file->f_pos); + if (ret < 0) + { + GTP_ERROR("Read firmware head in update file error."); + goto load_failed; + } + memcpy(fw_head, buf, FW_HEAD_LENGTH); + + //check firmware legality + fw_checksum = 0; + for(i=0; i<FW_SECTION_LENGTH*4+FW_DSP_ISP_LENGTH+FW_DSP_LENGTH+FW_BOOT_LENGTH; i+=2) + { + u16 temp; + ret = update_msg.file->f_op->read(update_msg.file, (char*)buf, 2, &update_msg.file->f_pos); + if (ret < 0) + { + GTP_ERROR("Read firmware file error."); + goto load_failed; + } + //GTP_DEBUG("BUF[0]:%x", buf[0]); + temp = (buf[0]<<8) + buf[1]; + fw_checksum += temp; + } + + GTP_DEBUG("firmware checksum:%x", fw_checksum&0xFFFF); + if(fw_checksum&0xFFFF) + { + GTP_ERROR("Illegal firmware file."); + goto load_failed; + } + + return SUCCESS; + +load_failed: + set_fs(update_msg.old_fs); + return FAIL; +} + +#if 0 +static u8 gup_check_update_header(struct i2c_client *client, st_fw_head* fw_head) +{ + const u8* pos; + int i = 0; + u8 mask_num = 0; + s32 ret = 0; + + pos = HEADER_UPDATE_DATA; + + memcpy(fw_head, pos, FW_HEAD_LENGTH); + pos += FW_HEAD_LENGTH; + + ret = gup_enter_update_judge(fw_head); + if(SUCCESS == ret) + { + return SUCCESS; + } + return FAIL; +} +#endif + +static u8 gup_burn_proc(struct i2c_client *client, u8 *burn_buf, u16 start_addr, u16 total_length) +{ + s32 ret = 0; + u16 burn_addr = start_addr; + u16 frame_length = 0; + u16 burn_length = 0; + u8 wr_buf[PACK_SIZE + GTP_ADDR_LENGTH]; + u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH]; + u8 retry = 0; + + GTP_DEBUG("Begin burn %dk data to addr 0x%x", (total_length/1024), start_addr); + while(burn_length < total_length) + { + GTP_DEBUG("B/T:%04d/%04d", burn_length, total_length); + frame_length = ((total_length - burn_length) > PACK_SIZE) ? PACK_SIZE : (total_length - burn_length); + wr_buf[0] = (u8)(burn_addr>>8); + rd_buf[0] = wr_buf[0]; + wr_buf[1] = (u8)burn_addr; + rd_buf[1] = wr_buf[1]; + memcpy(&wr_buf[GTP_ADDR_LENGTH], &burn_buf[burn_length], frame_length); + + for(retry = 0; retry < MAX_FRAME_CHECK_TIME; retry++) + { + ret = gup_i2c_write(client, wr_buf, GTP_ADDR_LENGTH + frame_length); + if(ret <= 0) + { + GTP_ERROR("Write frame data i2c error."); + continue; + } + ret = gup_i2c_read(client, rd_buf, GTP_ADDR_LENGTH + frame_length); + if(ret <= 0) + { + GTP_ERROR("Read back frame data i2c error."); + continue; + } + + if(memcmp(&wr_buf[GTP_ADDR_LENGTH], &rd_buf[GTP_ADDR_LENGTH], frame_length)) + { + GTP_ERROR("Check frame data fail,not equal."); + GTP_DEBUG("write array:"); + GTP_DEBUG_ARRAY(&wr_buf[GTP_ADDR_LENGTH], frame_length); + GTP_DEBUG("read array:"); + GTP_DEBUG_ARRAY(&rd_buf[GTP_ADDR_LENGTH], frame_length); + continue; + } + else + { + //GTP_DEBUG("Check frame data success."); + break; + } + } + if(retry >= MAX_FRAME_CHECK_TIME) + { + GTP_ERROR("Burn frame data time out,exit."); + return FAIL; + } + burn_length += frame_length; + burn_addr += frame_length; + } + return SUCCESS; +} + +static u8 gup_load_section_file(u8* buf, u16 offset, u16 length) +{ + s32 ret = 0; + + if(update_msg.file == NULL) + { + GTP_ERROR("cannot find update file,load section file fail."); + return FAIL; + } + update_msg.file->f_pos = FW_HEAD_LENGTH + offset; + + ret = update_msg.file->f_op->read(update_msg.file, (char*)buf, length, &update_msg.file->f_pos); + if(ret < 0) + { + GTP_ERROR("Read update file fail."); + return FAIL; + } + + return SUCCESS; +} + +static u8 gup_recall_check(struct i2c_client *client, u8* chk_src, u16 start_rd_addr, u16 chk_length) +{ + u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH]; + s32 ret = 0; + u16 recall_addr = start_rd_addr; + u16 recall_length = 0; + u16 frame_length = 0; + + while(recall_length < chk_length) + { + frame_length = ((chk_length - recall_length) > PACK_SIZE) ? PACK_SIZE : (chk_length - recall_length); + ret = gup_get_ic_msg(client, recall_addr, rd_buf, frame_length); + if(ret <= 0) + { + GTP_ERROR("recall i2c error,exit"); + return FAIL; + } + + if(memcmp(&rd_buf[GTP_ADDR_LENGTH], &chk_src[recall_length], frame_length)) + { + GTP_ERROR("Recall frame data fail,not equal."); + GTP_DEBUG("chk_src array:"); + GTP_DEBUG_ARRAY(&chk_src[recall_length], frame_length); + GTP_DEBUG("recall array:"); + GTP_DEBUG_ARRAY(&rd_buf[GTP_ADDR_LENGTH], frame_length); + return FAIL; + } + + recall_length += frame_length; + recall_addr += frame_length; + } + GTP_DEBUG("Recall check %dk firmware success.", (chk_length/1024)); + + return SUCCESS; +} + +static u8 gup_burn_fw_section(struct i2c_client *client, u8 *fw_section, u16 start_addr, u8 bank_cmd ) +{ + s32 ret = 0; + u8 rd_buf[5]; + + //step1:hold ss51 & dsp + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_section]hold ss51 & dsp fail."); + return FAIL; + } + + //step2:set scramble + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_section]set scramble fail."); + return FAIL; + } + + //step3:select bank + ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, (bank_cmd >> 4)&0x0F); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_section]select bank %d fail.", (bank_cmd >> 4)&0x0F); + return FAIL; + } + + //step4:enable accessing code + ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_section]enable accessing code fail."); + return FAIL; + } + + //step5:burn 8k fw section + ret = gup_burn_proc(client, fw_section, start_addr, FW_SECTION_LENGTH); + if(FAIL == ret) + { + GTP_ERROR("[burn_fw_section]burn fw_section fail."); + return FAIL; + } + + //step6:hold ss51 & release dsp + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_section]hold ss51 & release dsp fail."); + return FAIL; + } + //must delay + msleep(1); + + //step7:send burn cmd to move data to flash from sram + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, bank_cmd&0x0f); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_section]send burn cmd fail."); + return FAIL; + } + GTP_DEBUG("[burn_fw_section]Wait for the burn is complete......"); + do{ + ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_section]Get burn state fail"); + return FAIL; + } + msleep(10); + //GTP_DEBUG("[burn_fw_section]Get burn state:%d.", rd_buf[GTP_ADDR_LENGTH]); + }while(rd_buf[GTP_ADDR_LENGTH]); + + //step8:select bank + ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, (bank_cmd >> 4)&0x0F); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_section]select bank %d fail.", (bank_cmd >> 4)&0x0F); + return FAIL; + } + + //step9:enable accessing code + ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_section]enable accessing code fail."); + return FAIL; + } + + //step10:recall 8k fw section + ret = gup_recall_check(client, fw_section, start_addr, FW_SECTION_LENGTH); + if(FAIL == ret) + { + GTP_ERROR("[burn_fw_section]recall check 8k firmware fail."); + return FAIL; + } + + //step11:disable accessing code + ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x00); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_section]disable accessing code fail."); + return FAIL; + } + + return SUCCESS; +} + +static u8 gup_burn_dsp_isp(struct i2c_client *client) +{ + s32 ret = 0; + u8* fw_dsp_isp = NULL; + u8 retry = 0; + + GTP_DEBUG("[burn_dsp_isp]Begin burn dsp isp---->>"); + + //step1:alloc memory + GTP_DEBUG("[burn_dsp_isp]step1:alloc memory"); + while(retry++ < 5) + { + fw_dsp_isp = (u8*)kzalloc(FW_DSP_ISP_LENGTH, GFP_KERNEL); + if(fw_dsp_isp == NULL) + { + continue; + } + else + { + GTP_INFO("[burn_dsp_isp]Alloc %dk byte memory success.", (FW_DSP_ISP_LENGTH/1024)); + break; + } + } + if(retry >= 5) + { + GTP_ERROR("[burn_dsp_isp]Alloc memory fail,exit."); + return FAIL; + } + + //step2:load dsp isp file data + GTP_DEBUG("[burn_dsp_isp]step2:load dsp isp file data"); + ret = gup_load_section_file(fw_dsp_isp, (4*FW_SECTION_LENGTH+FW_DSP_LENGTH+FW_BOOT_LENGTH), FW_DSP_ISP_LENGTH); + if(FAIL == ret) + { + GTP_ERROR("[burn_dsp_isp]load firmware dsp_isp fail."); + goto exit_burn_dsp_isp; + } + + //step3:disable wdt,clear cache enable + GTP_DEBUG("[burn_dsp_isp]step3:disable wdt,clear cache enable"); + ret = gup_set_ic_msg(client, _bRW_MISCTL__TMR0_EN, 0x00); + if(ret <= 0) + { + GTP_ERROR("[burn_dsp_isp]disable wdt fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + ret = gup_set_ic_msg(client, _bRW_MISCTL__CACHE_EN, 0x00); + if(ret <= 0) + { + GTP_ERROR("[burn_dsp_isp]clear cache enable fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + + //step4:hold ss51 & dsp + GTP_DEBUG("[burn_dsp_isp]step4:hold ss51 & dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); + if(ret <= 0) + { + GTP_ERROR("[burn_dsp_isp]hold ss51 & dsp fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + + //step5:set boot from sram + GTP_DEBUG("[burn_dsp_isp]step5:set boot from sram"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOTCTL_B0_, 0x02); + if(ret <= 0) + { + GTP_ERROR("[burn_dsp_isp]set boot from sram fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + + //step6:software reboot + GTP_DEBUG("[burn_dsp_isp]step6:software reboot"); + ret = gup_set_ic_msg(client, _bWO_MISCTL__CPU_SWRST_PULSE, 0x01); + if(ret <= 0) + { + GTP_ERROR("[burn_dsp_isp]software reboot fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + + //step7:select bank2 + GTP_DEBUG("[burn_dsp_isp]step7:select bank2"); + ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x02); + if(ret <= 0) + { + GTP_ERROR("[burn_dsp_isp]select bank2 fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + + //step8:enable accessing code + GTP_DEBUG("[burn_dsp_isp]step8:enable accessing code"); + ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01); + if(ret <= 0) + { + GTP_ERROR("[burn_dsp_isp]enable accessing code fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + + //step9:burn 4k dsp_isp + GTP_DEBUG("[burn_dsp_isp]step9:burn 4k dsp_isp"); + ret = gup_burn_proc(client, fw_dsp_isp, 0xC000, FW_DSP_ISP_LENGTH); + if(FAIL == ret) + { + GTP_ERROR("[burn_dsp_isp]burn dsp_isp fail."); + goto exit_burn_dsp_isp; + } + + //step10:set scramble + GTP_DEBUG("[burn_dsp_isp]step10:set scramble"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); + if(ret <= 0) + { + GTP_ERROR("[burn_dsp_isp]set scramble fail."); + ret = FAIL; + goto exit_burn_dsp_isp; + } + ret = SUCCESS; + +exit_burn_dsp_isp: + kfree(fw_dsp_isp); + return ret; +} + +static u8 gup_burn_fw_ss51(struct i2c_client *client) +{ + u8* fw_ss51 = NULL; + u8 retry = 0; + s32 ret = 0; + + GTP_DEBUG("[burn_fw_ss51]Begin burn ss51 firmware---->>"); + + //step1:alloc memory + GTP_DEBUG("[burn_fw_ss51]step1:alloc memory"); + while(retry++ < 5) + { + fw_ss51 = (u8*)kzalloc(FW_SECTION_LENGTH, GFP_KERNEL); + if(fw_ss51 == NULL) + { + continue; + } + else + { + GTP_INFO("[burn_fw_ss51]Alloc %dk byte memory success.", (FW_SECTION_LENGTH/1024)); + break; + } + } + if(retry >= 5) + { + GTP_ERROR("[burn_fw_ss51]Alloc memory fail,exit."); + return FAIL; + } + + //step2:load ss51 firmware section 1 file data + GTP_DEBUG("[burn_fw_ss51]step2:load ss51 firmware section 1 file data"); + ret = gup_load_section_file(fw_ss51, 0, FW_SECTION_LENGTH); + if(FAIL == ret) + { + GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 1 fail."); + goto exit_burn_fw_ss51; + } + + //step3:clear control flag + GTP_DEBUG("[burn_fw_ss51]step3:clear control flag"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x00); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_ss51]clear control flag fail."); + ret = FAIL; + goto exit_burn_fw_ss51; + } + + //step4:burn ss51 firmware section 1 + GTP_DEBUG("[burn_fw_ss51]step4:burn ss51 firmware section 1"); + ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x01); + if(FAIL == ret) + { + GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 1 fail."); + goto exit_burn_fw_ss51; + } + + //step5:load ss51 firmware section 2 file data + GTP_DEBUG("[burn_fw_ss51]step5:load ss51 firmware section 2 file data"); + ret = gup_load_section_file(fw_ss51, FW_SECTION_LENGTH, FW_SECTION_LENGTH); + if(FAIL == ret) + { + GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 2 fail."); + goto exit_burn_fw_ss51; + } + + //step6:burn ss51 firmware section 2 + GTP_DEBUG("[burn_fw_ss51]step6:burn ss51 firmware section 2"); + ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x02); + if(FAIL == ret) + { + GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 2 fail."); + goto exit_burn_fw_ss51; + } + + //step7:load ss51 firmware section 3 file data + GTP_DEBUG("[burn_fw_ss51]step7:load ss51 firmware section 3 file data"); + ret = gup_load_section_file(fw_ss51, 2*FW_SECTION_LENGTH, FW_SECTION_LENGTH); + if(FAIL == ret) + { + GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 3 fail."); + goto exit_burn_fw_ss51; + } + + //step8:burn ss51 firmware section 3 + GTP_DEBUG("[burn_fw_ss51]step8:burn ss51 firmware section 3"); + ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x13); + if(FAIL == ret) + { + GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 3 fail."); + goto exit_burn_fw_ss51; + } + + //step9:load ss51 firmware section 4 file data + GTP_DEBUG("[burn_fw_ss51]step9:load ss51 firmware section 4 file data"); + ret = gup_load_section_file(fw_ss51, 3*FW_SECTION_LENGTH, FW_SECTION_LENGTH); + if(FAIL == ret) + { + GTP_ERROR("[burn_fw_ss51]load ss51 firmware section 4 fail."); + goto exit_burn_fw_ss51; + } + + //step10:burn ss51 firmware section 4 + GTP_DEBUG("[burn_fw_ss51]step10:burn ss51 firmware section 4"); + ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x14); + if(FAIL == ret) + { + GTP_ERROR("[burn_fw_ss51]burn ss51 firmware section 4 fail."); + goto exit_burn_fw_ss51; + } + + ret = SUCCESS; + +exit_burn_fw_ss51: + kfree(fw_ss51); + return ret; +} + +static u8 gup_burn_fw_dsp(struct i2c_client *client) +{ + s32 ret = 0; + u8* fw_dsp = NULL; + u8 retry = 0; + u8 rd_buf[5]; + + GTP_DEBUG("[burn_fw_dsp]Begin burn dsp firmware---->>"); + //step1:alloc memory + GTP_DEBUG("[burn_fw_dsp]step1:alloc memory"); + while(retry++ < 5) + { + fw_dsp = (u8*)kzalloc(FW_DSP_LENGTH, GFP_KERNEL); + if(fw_dsp == NULL) + { + continue; + } + else + { + GTP_INFO("[burn_fw_dsp]Alloc %dk byte memory success.", (FW_SECTION_LENGTH/1024)); + break; + } + } + if(retry >= 5) + { + GTP_ERROR("[burn_fw_dsp]Alloc memory fail,exit."); + return FAIL; + } + + //step2:load firmware dsp + GTP_DEBUG("[burn_fw_dsp]step2:load firmware dsp"); + ret = gup_load_section_file(fw_dsp, 4*FW_SECTION_LENGTH, FW_DSP_LENGTH); + if(FAIL == ret) + { + GTP_ERROR("[burn_fw_dsp]load firmware dsp fail."); + goto exit_burn_fw_dsp; + } + + //step3:select bank3 + GTP_DEBUG("[burn_fw_dsp]step3:select bank3"); + ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_dsp]select bank3 fail."); + ret = FAIL; + goto exit_burn_fw_dsp; + } + + //step4:hold ss51 & dsp + GTP_DEBUG("[burn_fw_dsp]step4:hold ss51 & dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_dsp]hold ss51 & dsp fail."); + ret = FAIL; + goto exit_burn_fw_dsp; + } + + //step5:set scramble + GTP_DEBUG("[burn_fw_dsp]step5:set scramble"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_dsp]set scramble fail."); + ret = FAIL; + goto exit_burn_fw_dsp; + } + + //step6:release ss51 & dsp + GTP_DEBUG("[burn_fw_dsp]step6:release ss51 & dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); //20121211 + if(ret <= 0) + { + GTP_ERROR("[burn_fw_dsp]release ss51 & dsp fail."); + ret = FAIL; + goto exit_burn_fw_dsp; + } + //must delay + msleep(1); + + //step7:burn 4k dsp firmware + GTP_DEBUG("[burn_fw_dsp]step7:burn 4k dsp firmware"); + ret = gup_burn_proc(client, fw_dsp, 0x9000, FW_DSP_LENGTH); + if(FAIL == ret) + { + GTP_ERROR("[burn_fw_dsp]burn fw_section fail."); + goto exit_burn_fw_dsp; + } + + //step8:send burn cmd to move data to flash from sram + GTP_DEBUG("[burn_fw_dsp]step8:send burn cmd to move data to flash from sram"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x05); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_dsp]send burn cmd fail."); + goto exit_burn_fw_dsp; + } + GTP_DEBUG("[burn_fw_dsp]Wait for the burn is complete......"); + do{ + ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_dsp]Get burn state fail"); + goto exit_burn_fw_dsp; + } + msleep(10); + //GTP_DEBUG("[burn_fw_dsp]Get burn state:%d.", rd_buf[GTP_ADDR_LENGTH]); + }while(rd_buf[GTP_ADDR_LENGTH]); + + //step9:recall check 4k dsp firmware + GTP_DEBUG("[burn_fw_dsp]step9:recall check 4k dsp firmware"); + ret = gup_recall_check(client, fw_dsp, 0x9000, FW_DSP_LENGTH); + if(FAIL == ret) + { + GTP_ERROR("[burn_fw_dsp]recall check 4k dsp firmware fail."); + goto exit_burn_fw_dsp; + } + + ret = SUCCESS; + +exit_burn_fw_dsp: + kfree(fw_dsp); + return ret; +} + +static u8 gup_burn_fw_boot(struct i2c_client *client) +{ + s32 ret = 0; + u8* fw_boot = NULL; + u8 retry = 0; + u8 rd_buf[5]; + + GTP_DEBUG("[burn_fw_boot]Begin burn bootloader firmware---->>"); + + //step1:Alloc memory + GTP_DEBUG("[burn_fw_boot]step1:Alloc memory"); + while(retry++ < 5) + { + fw_boot = (u8*)kzalloc(FW_BOOT_LENGTH, GFP_KERNEL); + if(fw_boot == NULL) + { + continue; + } + else + { + GTP_INFO("[burn_fw_boot]Alloc %dk byte memory success.", (FW_BOOT_LENGTH/1024)); + break; + } + } + if(retry >= 5) + { + GTP_ERROR("[burn_fw_boot]Alloc memory fail,exit."); + return FAIL; + } + + //step2:load firmware bootloader + GTP_DEBUG("[burn_fw_boot]step2:load firmware bootloader"); + ret = gup_load_section_file(fw_boot, (4*FW_SECTION_LENGTH+FW_DSP_LENGTH), FW_BOOT_LENGTH); + if(FAIL == ret) + { + GTP_ERROR("[burn_fw_boot]load firmware dsp fail."); + goto exit_burn_fw_boot; + } + + //step3:hold ss51 & dsp + GTP_DEBUG("[burn_fw_boot]step3:hold ss51 & dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_boot]hold ss51 & dsp fail."); + ret = FAIL; + goto exit_burn_fw_boot; + } + + //step4:set scramble + GTP_DEBUG("[burn_fw_boot]step4:set scramble"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_boot]set scramble fail."); + ret = FAIL; + goto exit_burn_fw_boot; + } + + //step5:release ss51 & dsp + GTP_DEBUG("[burn_fw_boot]step5:release ss51 & dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04); //20121211 + if(ret <= 0) + { + GTP_ERROR("[burn_fw_boot]release ss51 & dsp fail."); + ret = FAIL; + goto exit_burn_fw_boot; + } + //must delay + msleep(1); + + //step6:select bank3 + GTP_DEBUG("[burn_fw_boot]step6:select bank3"); + ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_boot]select bank3 fail."); + ret = FAIL; + goto exit_burn_fw_boot; + } + + //step7:burn 2k bootloader firmware + GTP_DEBUG("[burn_fw_boot]step7:burn 2k bootloader firmware"); + ret = gup_burn_proc(client, fw_boot, 0x9000, FW_BOOT_LENGTH); + if(FAIL == ret) + { + GTP_ERROR("[burn_fw_boot]burn fw_section fail."); + goto exit_burn_fw_boot; + } + + //step7:send burn cmd to move data to flash from sram + GTP_DEBUG("[burn_fw_boot]step7:send burn cmd to move data to flash from sram"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x06); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_boot]send burn cmd fail."); + goto exit_burn_fw_boot; + } + GTP_DEBUG("[burn_fw_boot]Wait for the burn is complete......"); + do{ + ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_boot]Get burn state fail"); + goto exit_burn_fw_boot; + } + msleep(10); + //GTP_DEBUG("[burn_fw_boot]Get burn state:%d.", rd_buf[GTP_ADDR_LENGTH]); + }while(rd_buf[GTP_ADDR_LENGTH]); + + //step8:recall check 2k bootloader firmware + GTP_DEBUG("[burn_fw_boot]step8:recall check 2k bootloader firmware"); + ret = gup_recall_check(client, fw_boot, 0x9000, FW_BOOT_LENGTH); + if(FAIL == ret) + { + GTP_ERROR("[burn_fw_boot]recall check 4k dsp firmware fail."); + goto exit_burn_fw_boot; + } + + //step9:enable download DSP code + GTP_DEBUG("[burn_fw_boot]step9:enable download DSP code "); + ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x99); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_boot]enable download DSP code fail."); + ret = FAIL; + goto exit_burn_fw_boot; + } + + //step10:release ss51 & hold dsp + GTP_DEBUG("[burn_fw_boot]step10:release ss51 & hold dsp"); + ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x08); + if(ret <= 0) + { + GTP_ERROR("[burn_fw_boot]release ss51 & hold dsp fail."); + ret = FAIL; + goto exit_burn_fw_boot; + } + + ret = SUCCESS; + +exit_burn_fw_boot: + kfree(fw_boot); + return ret; +} + +s32 gup_update_proc(void *dir) +{ + s32 ret = 0; + u8 retry = 0; + st_fw_head fw_head; + struct goodix_ts_data *ts = NULL; + + GTP_DEBUG("[update_proc]Begin update ......"); + + show_len = 1; + total_len = 100; + if(dir == NULL) + { + msleep(3000); //wait main thread to be completed + } + + ts = i2c_get_clientdata(i2c_connect_client); + + if (searching_file) + { + searching_file = 0; // exit .bin update file searching + GTP_INFO("Exiting searching .bin update file..."); + while ((show_len != 200) && (show_len != 100)) // wait for auto update quitted completely + { + msleep(100); + } + } + + update_msg.file = NULL; + ret = gup_check_update_file(i2c_connect_client, &fw_head, (u8*)dir); //20121211 + if(FAIL == ret) + { + GTP_ERROR("[update_proc]check update file fail."); + goto file_fail; + } + + //gtp_reset_guitar(i2c_connect_client, 20); + ret = gup_get_ic_fw_msg(i2c_connect_client); + if(FAIL == ret) + { + GTP_ERROR("[update_proc]get ic message fail."); + goto file_fail; + } + + ret = gup_enter_update_judge(&fw_head); + if(FAIL == ret) + { + GTP_ERROR("[update_proc]Check *.bin file fail."); + goto file_fail; + } + + ts->enter_update = 1; + gtp_irq_disable(ts); +#if GTP_ESD_PROTECT + gtp_esd_switch(ts->client, SWITCH_OFF); +#endif + ret = gup_enter_update_mode(i2c_connect_client); + if(FAIL == ret) + { + GTP_ERROR("[update_proc]enter update mode fail."); + goto update_fail; + } + + while(retry++ < 5) + { + show_len = 10; + total_len = 100; + ret = gup_burn_dsp_isp(i2c_connect_client); + if(FAIL == ret) + { + GTP_ERROR("[update_proc]burn dsp isp fail."); + continue; + } + + show_len += 10; + ret = gup_burn_fw_ss51(i2c_connect_client); + if(FAIL == ret) + { + GTP_ERROR("[update_proc]burn ss51 firmware fail."); + continue; + } + + show_len += 40; + ret = gup_burn_fw_dsp(i2c_connect_client); + if(FAIL == ret) + { + GTP_ERROR("[update_proc]burn dsp firmware fail."); + continue; + } + + show_len += 20; + ret = gup_burn_fw_boot(i2c_connect_client); + if(FAIL == ret) + { + GTP_ERROR("[update_proc]burn bootloader firmware fail."); + continue; + } + show_len += 10; + GTP_INFO("[update_proc]UPDATE SUCCESS."); + break; + } + if(retry >= 5) + { + GTP_ERROR("[update_proc]retry timeout,UPDATE FAIL."); + goto update_fail; + } + + GTP_DEBUG("[update_proc]leave update mode."); + gup_leave_update_mode(); + + msleep(100); +// GTP_DEBUG("[update_proc]send config."); +// ret = gtp_send_cfg(i2c_connect_client); +// if(ret < 0) +// { +// GTP_ERROR("[update_proc]send config fail."); +// } + if (ts->fw_error) + { + GTP_INFO("firmware error auto update, resent config!"); + gup_init_panel(ts); + } + show_len = 100; + total_len = 100; + ts->enter_update = 0; + gtp_irq_enable(ts); + +#if GTP_ESD_PROTECT + gtp_esd_switch(ts->client, SWITCH_ON); +#endif + filp_close(update_msg.file, NULL); + return SUCCESS; + +update_fail: + ts->enter_update = 0; + gtp_irq_enable(ts); + +#if GTP_ESD_PROTECT + gtp_esd_switch(ts->client, SWITCH_ON); +#endif + +file_fail: + if(update_msg.file && !IS_ERR(update_msg.file)) + { + filp_close(update_msg.file, NULL); + } + show_len = 200; + total_len = 100; + return FAIL; +} + +#if GTP_AUTO_UPDATE +u8 gup_init_update_proc(struct goodix_ts_data *ts) +{ + struct task_struct *thread = NULL; + + GTP_INFO("Ready to run update thread."); + thread = kthread_run(gup_update_proc, (void*)NULL, "guitar_update"); + if (IS_ERR(thread)) + { + GTP_ERROR("Failed to create update thread.\n"); + return -1; + } + + return 0; +} +#endif
\ No newline at end of file diff --git a/drivers/input/touchscreen/it7258_ts_i2c.c b/drivers/input/touchscreen/it7258_ts_i2c.c new file mode 100644 index 000000000000..1fa28f1f0d9d --- /dev/null +++ b/drivers/input/touchscreen/it7258_ts_i2c.c @@ -0,0 +1,1979 @@ +/* drivers/input/touchscreen/it7258_ts_i2c.c + * + * Copyright (C) 2014 ITE Tech. Inc. + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +#include <linux/fb.h> +#include <linux/debugfs.h> +#include <linux/input/mt.h> +#include <linux/string.h> + +#define MAX_BUFFER_SIZE 144 +#define DEVICE_NAME "IT7260" +#define SCREEN_X_RESOLUTION 320 +#define SCREEN_Y_RESOLUTION 320 +#define DEBUGFS_DIR_NAME "ts_debug" +#define FW_NAME "it7260_fw.bin" +#define CFG_NAME "it7260_cfg.bin" +#define VER_BUFFER_SIZE 4 +#define IT_FW_CHECK(x, y) \ + (((x)[0] < (y)->data[8]) || ((x)[1] < (y)->data[9]) || \ + ((x)[2] < (y)->data[10]) || ((x)[3] < (y)->data[11])) +#define IT_CFG_CHECK(x, y) \ + (((x)[0] < (y)->data[(y)->size - 8]) || \ + ((x)[1] < (y)->data[(y)->size - 7]) || \ + ((x)[2] < (y)->data[(y)->size - 6]) || \ + ((x)[3] < (y)->data[(y)->size - 5])) +#define IT7260_COORDS_ARR_SIZE 4 + +/* all commands writes go to this idx */ +#define BUF_COMMAND 0x20 +#define BUF_SYS_COMMAND 0x40 +/* + * "device ready?" and "wake up please" and "read touch data" reads + * go to this idx + */ +#define BUF_QUERY 0x80 +/* most command response reads go to this idx */ +#define BUF_RESPONSE 0xA0 +#define BUF_SYS_RESPONSE 0xC0 +/* reads of "point" go through here and produce 14 bytes of data */ +#define BUF_POINT_INFO 0xE0 + +/* + * commands and their subcommands. when no subcommands exist, a zero + * is send as the second byte + */ +#define CMD_IDENT_CHIP 0x00 +/* VERSION_LENGTH bytes of data in response */ +#define CMD_READ_VERSIONS 0x01 +#define SUB_CMD_READ_FIRMWARE_VERSION 0x00 +#define SUB_CMD_READ_CONFIG_VERSION 0x06 +#define VERSION_LENGTH 10 +/* subcommand is zero, next byte is power mode */ +#define CMD_PWR_CTL 0x04 +/* active mode */ +#define PWR_CTL_ACTIVE_MODE 0x00 +/* idle mode */ +#define PWR_CTL_LOW_POWER_MODE 0x01 +/* sleep mode */ +#define PWR_CTL_SLEEP_MODE 0x02 +#define WAIT_CHANGE_MODE 20 +/* command is not documented in the datasheet v1.0.0.7 */ +#define CMD_UNKNOWN_7 0x07 +#define CMD_FIRMWARE_REINIT_C 0x0C +/* needs to be followed by 4 bytes of zeroes */ +#define CMD_CALIBRATE 0x13 +#define CMD_FIRMWARE_UPGRADE 0x60 +#define SUB_CMD_ENTER_FW_UPGRADE_MODE 0x00 +#define SUB_CMD_EXIT_FW_UPGRADE_MODE 0x80 +/* address for FW read/write */ +#define CMD_SET_START_OFFSET 0x61 +/* subcommand is number of bytes to write */ +#define CMD_FW_WRITE 0x62 +/* subcommand is number of bytes to read */ +#define CMD_FW_READ 0x63 +#define CMD_FIRMWARE_REINIT_6F 0x6F + +#define FW_WRITE_CHUNK_SIZE 128 +#define FW_WRITE_RETRY_COUNT 4 +#define CHIP_FLASH_SIZE 0x8000 +#define DEVICE_READY_MAX_WAIT 10 + +/* result of reading with BUF_QUERY bits */ +#define CMD_STATUS_BITS 0x07 +#define CMD_STATUS_DONE 0x00 +#define CMD_STATUS_BUSY 0x01 +#define CMD_STATUS_ERROR 0x02 +#define PT_INFO_BITS 0xF8 +#define BT_INFO_NONE 0x00 +#define PT_INFO_YES 0x80 +/* no new data but finder(s) still down */ +#define BT_INFO_NONE_BUT_DOWN 0x08 + +#define PD_FLAGS_DATA_TYPE_BITS 0xF0 +/* other types (like chip-detected gestures) exist but we do not care */ +#define PD_FLAGS_DATA_TYPE_TOUCH 0x00 +/* a bit for each finger data that is valid (from lsb to msb) */ +#define PD_FLAGS_HAVE_FINGERS 0x07 +#define PD_PALM_FLAG_BIT 0x01 +#define FD_PRESSURE_BITS 0x0F +#define FD_PRESSURE_NONE 0x00 +#define FD_PRESSURE_LIGHT 0x02 + +#define IT_VTG_MIN_UV 1800000 +#define IT_VTG_MAX_UV 1800000 +#define IT_ACTIVE_LOAD_UA 15000 +#define IT_I2C_VTG_MIN_UV 2600000 +#define IT_I2C_VTG_MAX_UV 3300000 +#define IT_I2C_ACTIVE_LOAD_UA 10000 + +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "pmx_ts_release" +#define IT_I2C_WAIT 1000 + +struct FingerData { + uint8_t xLo; + uint8_t hi; + uint8_t yLo; + uint8_t pressure; +} __packed; + +struct PointData { + uint8_t flags; + uint8_t palm; + struct FingerData fd[3]; +} __packed; + +struct IT7260_ts_platform_data { + u32 irq_gpio; + u32 irq_gpio_flags; + u32 reset_gpio; + u32 reset_gpio_flags; + bool wakeup; + bool palm_detect_en; + u16 palm_detect_keycode; + const char *fw_name; + const char *cfg_name; + unsigned int panel_minx; + unsigned int panel_miny; + unsigned int panel_maxx; + unsigned int panel_maxy; + unsigned int disp_minx; + unsigned int disp_miny; + unsigned int disp_maxx; + unsigned int disp_maxy; + unsigned num_of_fingers; + unsigned int reset_delay; + bool low_reset; +}; + +struct IT7260_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + const struct IT7260_ts_platform_data *pdata; + struct regulator *vdd; + struct regulator *avdd; + bool device_needs_wakeup; + bool suspended; + bool fw_upgrade_result; + bool cfg_upgrade_result; + bool fw_cfg_uploading; + struct work_struct work_pm_relax; + bool calibration_success; + bool had_finger_down; + char fw_name[MAX_BUFFER_SIZE]; + char cfg_name[MAX_BUFFER_SIZE]; + struct mutex fw_cfg_mutex; + u8 fw_ver[VER_BUFFER_SIZE]; + u8 cfg_ver[VER_BUFFER_SIZE]; +#ifdef CONFIG_FB + struct notifier_block fb_notif; +#endif + struct dentry *dir; + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; +}; + +/* Function declarations */ +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); +static int IT7260_ts_resume(struct device *dev); +static int IT7260_ts_suspend(struct device *dev); + +static struct IT7260_ts_data *gl_ts; + +static int IT7260_debug_suspend_set(void *_data, u64 val) +{ + if (val) + IT7260_ts_suspend(&gl_ts->client->dev); + else + IT7260_ts_resume(&gl_ts->client->dev); + + return 0; +} + +static int IT7260_debug_suspend_get(void *_data, u64 *val) +{ + *val = gl_ts->suspended; + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, IT7260_debug_suspend_get, + IT7260_debug_suspend_set, "%lld\n"); + +/* internal use func - does not make sure chip is ready before read */ +static bool IT7260_i2cReadNoReadyCheck(uint8_t buf_index, uint8_t *buffer, + uint16_t buf_len) +{ + struct i2c_msg msgs[2] = { + { + .addr = gl_ts->client->addr, + .flags = I2C_M_NOSTART, + .len = 1, + .buf = &buf_index + }, + { + .addr = gl_ts->client->addr, + .flags = I2C_M_RD, + .len = buf_len, + .buf = buffer + } + }; + + memset(buffer, 0xFF, buf_len); + + return i2c_transfer(gl_ts->client->adapter, msgs, 2); +} + +static bool IT7260_i2cWriteNoReadyCheck(uint8_t buf_index, + const uint8_t *buffer, uint16_t buf_len) +{ + uint8_t txbuf[257]; + struct i2c_msg msg = { + .addr = gl_ts->client->addr, + .flags = 0, + .len = buf_len + 1, + .buf = txbuf + }; + + /* just to be careful */ + if (buf_len > sizeof(txbuf) - 1) { + dev_err(&gl_ts->client->dev, "buf length is out of limit\n"); + return false; + } + + txbuf[0] = buf_index; + memcpy(txbuf + 1, buffer, buf_len); + + return i2c_transfer(gl_ts->client->adapter, &msg, 1); +} + +/* + * Device is apparently always ready for i2c but not for actual + * register reads/writes. This function ascertains it is ready + * for that too. the results of this call often were ignored. + */ +static bool IT7260_waitDeviceReady(bool forever, bool slowly) +{ + uint8_t query; + uint32_t count = DEVICE_READY_MAX_WAIT; + + do { + if (!IT7260_i2cReadNoReadyCheck(BUF_QUERY, &query, + sizeof(query))) + query = CMD_STATUS_BUSY; + + if (slowly) + msleep(IT_I2C_WAIT); + if (!forever) + count--; + + } while ((query & CMD_STATUS_BUSY) && count); + + return !query; +} + +static bool IT7260_i2cRead(uint8_t buf_index, uint8_t *buffer, + uint16_t buf_len) +{ + IT7260_waitDeviceReady(false, false); + return IT7260_i2cReadNoReadyCheck(buf_index, buffer, buf_len); +} + +static bool IT7260_i2cWrite(uint8_t buf_index, const uint8_t *buffer, + uint16_t buf_len) +{ + IT7260_waitDeviceReady(false, false); + return IT7260_i2cWriteNoReadyCheck(buf_index, buffer, buf_len); +} + +static bool IT7260_firmware_reinitialize(u8 command) +{ + uint8_t cmd[] = {command}; + uint8_t rsp[2]; + + if (!IT7260_i2cWrite(BUF_COMMAND, cmd, sizeof(cmd))) + return false; + + if (!IT7260_i2cRead(BUF_RESPONSE, rsp, sizeof(rsp))) + return false; + + /* a reply of two zero bytes signifies success */ + return !rsp[0] && !rsp[1]; +} + +static bool IT7260_enter_exit_fw_ugrade_mode(bool enter) +{ + uint8_t cmd[] = {CMD_FIRMWARE_UPGRADE, 0, 'I', 'T', '7', '2', + '6', '0', 0x55, 0xAA}; + uint8_t resp[2]; + + cmd[1] = enter ? SUB_CMD_ENTER_FW_UPGRADE_MODE : + SUB_CMD_EXIT_FW_UPGRADE_MODE; + if (!IT7260_i2cWrite(BUF_COMMAND, cmd, sizeof(cmd))) + return false; + + if (!IT7260_i2cRead(BUF_RESPONSE, resp, sizeof(resp))) + return false; + + /* a reply of two zero bytes signifies success */ + return !resp[0] && !resp[1]; +} + +static bool IT7260_chipSetStartOffset(uint16_t offset) +{ + uint8_t cmd[] = {CMD_SET_START_OFFSET, 0, ((uint8_t)(offset)), + ((uint8_t)((offset) >> 8))}; + uint8_t resp[2]; + + if (!IT7260_i2cWrite(BUF_COMMAND, cmd, 4)) + return false; + + + if (!IT7260_i2cRead(BUF_RESPONSE, resp, sizeof(resp))) + return false; + + + /* a reply of two zero bytes signifies success */ + return !resp[0] && !resp[1]; +} + + +/* write fw_length bytes from fw_data at chip offset wr_start_offset */ +static bool IT7260_fw_flash_write_verify(unsigned int fw_length, + const uint8_t *fw_data, uint16_t wr_start_offset) +{ + uint32_t cur_data_off; + + for (cur_data_off = 0; cur_data_off < fw_length; + cur_data_off += FW_WRITE_CHUNK_SIZE) { + + uint8_t cmd_write[2 + FW_WRITE_CHUNK_SIZE] = {CMD_FW_WRITE}; + uint8_t buf_read[FW_WRITE_CHUNK_SIZE]; + uint8_t cmd_read[2] = {CMD_FW_READ}; + unsigned i, retries; + uint32_t cur_wr_size; + + /* figure out how much to write */ + cur_wr_size = fw_length - cur_data_off; + if (cur_wr_size > FW_WRITE_CHUNK_SIZE) + cur_wr_size = FW_WRITE_CHUNK_SIZE; + + /* prepare the write command */ + cmd_write[1] = cur_wr_size; + for (i = 0; i < cur_wr_size; i++) + cmd_write[i + 2] = fw_data[cur_data_off + i]; + + /* prepare the read command */ + cmd_read[1] = cur_wr_size; + + for (retries = 0; retries < FW_WRITE_RETRY_COUNT; + retries++) { + + /* set write offset and write the data */ + IT7260_chipSetStartOffset( + wr_start_offset + cur_data_off); + IT7260_i2cWrite(BUF_COMMAND, cmd_write, + cur_wr_size + 2); + + /* set offset and read the data back */ + IT7260_chipSetStartOffset( + wr_start_offset + cur_data_off); + IT7260_i2cWrite(BUF_COMMAND, cmd_read, + sizeof(cmd_read)); + IT7260_i2cRead(BUF_RESPONSE, buf_read, cur_wr_size); + + /* verify. If success break out of retry loop */ + i = 0; + while (i < cur_wr_size && + buf_read[i] == cmd_write[i + 2]) + i++; + if (i == cur_wr_size) + break; + } + /* if we've failed after all the retries, tell the caller */ + if (retries == FW_WRITE_RETRY_COUNT) { + dev_err(&gl_ts->client->dev, + "write of data offset %u failed on try %u at byte %u/%u\n", + cur_data_off, retries, i, cur_wr_size); + return false; + } + } + + return true; +} + +/* + * this code to get versions from the chip via i2c transactions, and save + * them in driver data structure. + */ +static void IT7260_get_chip_versions(struct device *dev) +{ + static const u8 cmd_read_fw_ver[] = {CMD_READ_VERSIONS, + SUB_CMD_READ_FIRMWARE_VERSION}; + static const u8 cmd_read_cfg_ver[] = {CMD_READ_VERSIONS, + SUB_CMD_READ_CONFIG_VERSION}; + u8 ver_fw[VERSION_LENGTH], ver_cfg[VERSION_LENGTH]; + bool ret = true; + + ret = IT7260_i2cWrite(BUF_COMMAND, cmd_read_fw_ver, + sizeof(cmd_read_fw_ver)); + if (ret) { + ret = IT7260_i2cRead(BUF_RESPONSE, ver_fw, VERSION_LENGTH); + if (ret) + memcpy(gl_ts->fw_ver, ver_fw + (5 * sizeof(u8)), + VER_BUFFER_SIZE * sizeof(u8)); + } + if (!ret) + dev_err(dev, "failed to read fw version from chip\n"); + + ret = IT7260_i2cWrite(BUF_COMMAND, cmd_read_cfg_ver, + sizeof(cmd_read_cfg_ver)); + if (ret) { + ret = IT7260_i2cRead(BUF_RESPONSE, ver_cfg, VERSION_LENGTH) + && ret; + if (ret) + memcpy(gl_ts->cfg_ver, ver_cfg + (1 * sizeof(u8)), + VER_BUFFER_SIZE * sizeof(u8)); + } + if (!ret) + dev_err(dev, "failed to read cfg version from chip\n"); + + dev_info(dev, "Current fw{%X.%X.%X.%X} cfg{%X.%X.%X.%X}\n", + gl_ts->fw_ver[0], gl_ts->fw_ver[1], gl_ts->fw_ver[2], + gl_ts->fw_ver[3], gl_ts->cfg_ver[0], gl_ts->cfg_ver[1], + gl_ts->cfg_ver[2], gl_ts->cfg_ver[3]); +} + +static int IT7260_cfg_upload(struct device *dev, bool force) +{ + const struct firmware *cfg = NULL; + int ret; + bool success, cfg_upgrade = false; + + ret = request_firmware(&cfg, gl_ts->cfg_name, dev); + if (ret) { + dev_err(dev, "failed to get config data %s for it7260 %d\n", + gl_ts->cfg_name, ret); + return ret; + } + + /* + * This compares the cfg version number from chip and the cfg + * data file. IT flashes only when version of cfg data file is + * greater than that of chip or if it is set for force cfg upgrade. + */ + if (force) + cfg_upgrade = true; + else if (IT_CFG_CHECK(gl_ts->cfg_ver, cfg)) + cfg_upgrade = true; + + if (!cfg_upgrade) { + dev_err(dev, "CFG upgrade no required ...\n"); + ret = -EFAULT; + goto out; + } else { + dev_info(dev, "Config upgrading...\n"); + + disable_irq(gl_ts->client->irq); + /* enter cfg upload mode */ + success = IT7260_enter_exit_fw_ugrade_mode(true); + if (!success) { + dev_err(dev, "Can't enter cfg upgrade mode\n"); + ret = -EIO; + goto out; + } + /* flash config data if requested */ + success = IT7260_fw_flash_write_verify(cfg->size, cfg->data, + CHIP_FLASH_SIZE - cfg->size); + if (!success) { + dev_err(dev, "failed to upgrade touch cfg data\n"); + IT7260_enter_exit_fw_ugrade_mode(false); + IT7260_firmware_reinitialize(CMD_FIRMWARE_REINIT_6F); + ret = -EIO; + goto out; + } else { + memcpy(gl_ts->cfg_ver, cfg->data + + (cfg->size - 8 * sizeof(u8)), + VER_BUFFER_SIZE * sizeof(u8)); + dev_info(dev, "CFG upgrade is success. New cfg ver: %X.%X.%X.%X\n", + gl_ts->cfg_ver[0], gl_ts->cfg_ver[1], + gl_ts->cfg_ver[2], gl_ts->cfg_ver[3]); + + } + enable_irq(gl_ts->client->irq); + } + +out: + release_firmware(cfg); + + return ret; +} + +static int IT7260_fw_upload(struct device *dev, bool force) +{ + const struct firmware *fw = NULL; + int ret; + bool success, fw_upgrade = false; + + ret = request_firmware(&fw, gl_ts->fw_name, dev); + if (ret) { + dev_err(dev, "failed to get firmware %s for it7260 %d\n", + gl_ts->fw_name, ret); + return ret; + } + + /* + * This compares the fw version number from chip and the fw data + * file. It flashes only when version of fw data file is greater + * than that of chip or it it is set for force fw upgrade. + */ + if (force) + fw_upgrade = true; + else if (IT_FW_CHECK(gl_ts->fw_ver, fw)) + fw_upgrade = true; + + if (!fw_upgrade) { + dev_err(dev, "FW upgrade not required ...\n"); + ret = -EFAULT; + goto out; + } else { + dev_info(dev, "Firmware upgrading...\n"); + + disable_irq(gl_ts->client->irq); + /* enter fw upload mode */ + success = IT7260_enter_exit_fw_ugrade_mode(true); + if (!success) { + dev_err(dev, "Can't enter fw upgrade mode\n"); + ret = -EIO; + goto out; + } + /* flash the firmware if requested */ + success = IT7260_fw_flash_write_verify(fw->size, fw->data, 0); + if (!success) { + dev_err(dev, "failed to upgrade touch firmware\n"); + IT7260_enter_exit_fw_ugrade_mode(false); + IT7260_firmware_reinitialize(CMD_FIRMWARE_REINIT_6F); + ret = -EIO; + goto out; + } else { + memcpy(gl_ts->fw_ver, fw->data + (8 * sizeof(u8)), + VER_BUFFER_SIZE * sizeof(u8)); + dev_info(dev, "FW upgrade is success. New fw ver: %X.%X.%X.%X\n", + gl_ts->fw_ver[0], gl_ts->fw_ver[1], + gl_ts->fw_ver[2], gl_ts->fw_ver[3]); + } + enable_irq(gl_ts->client->irq); + } + +out: + release_firmware(fw); + + return ret; +} + +static int IT7260_ts_chipLowPowerMode(const u8 sleep_type) +{ + const uint8_t cmd_sleep[] = {CMD_PWR_CTL, 0x00, sleep_type}; + uint8_t dummy; + + if (sleep_type) + IT7260_i2cWriteNoReadyCheck(BUF_COMMAND, cmd_sleep, + sizeof(cmd_sleep)); + else + IT7260_i2cReadNoReadyCheck(BUF_QUERY, &dummy, sizeof(dummy)); + + msleep(WAIT_CHANGE_MODE); + return 0; +} + +static ssize_t sysfs_fw_upgrade_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int mode = 0, ret; + + if (gl_ts->suspended) { + dev_err(dev, "Device is suspended, can't flash fw!!!\n"); + return -EBUSY; + } + + ret = kstrtoint(buf, 10, &mode); + if (!ret) { + dev_err(dev, "failed to read input for sysfs\n"); + return -EINVAL; + } + + mutex_lock(&gl_ts->fw_cfg_mutex); + if (mode == 1) { + gl_ts->fw_cfg_uploading = true; + ret = IT7260_fw_upload(dev, false); + if (ret) { + dev_err(dev, "Failed to flash fw: %d", ret); + gl_ts->fw_upgrade_result = false; + } else { + gl_ts->fw_upgrade_result = true; + } + gl_ts->fw_cfg_uploading = false; + } + mutex_unlock(&gl_ts->fw_cfg_mutex); + + return count; +} + +static ssize_t sysfs_cfg_upgrade_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int mode = 0, ret; + + if (gl_ts->suspended) { + dev_err(dev, "Device is suspended, can't flash cfg!!!\n"); + return -EBUSY; + } + + ret = kstrtoint(buf, 10, &mode); + if (!ret) { + dev_err(dev, "failed to read input for sysfs\n"); + return -EINVAL; + } + + mutex_lock(&gl_ts->fw_cfg_mutex); + if (mode == 1) { + gl_ts->fw_cfg_uploading = true; + ret = IT7260_cfg_upload(dev, false); + if (ret) { + dev_err(dev, "Failed to flash cfg: %d", ret); + gl_ts->cfg_upgrade_result = false; + } else { + gl_ts->cfg_upgrade_result = true; + } + gl_ts->fw_cfg_uploading = false; + } + mutex_unlock(&gl_ts->fw_cfg_mutex); + + return count; +} + +static ssize_t sysfs_fw_upgrade_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, MAX_BUFFER_SIZE, "%d\n", + gl_ts->fw_upgrade_result); +} + +static ssize_t sysfs_cfg_upgrade_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, MAX_BUFFER_SIZE, "%d\n", + gl_ts->cfg_upgrade_result); +} + +static ssize_t sysfs_force_fw_upgrade_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int mode = 0, ret; + + if (gl_ts->suspended) { + dev_err(dev, "Device is suspended, can't flash fw!!!\n"); + return -EBUSY; + } + + ret = kstrtoint(buf, 10, &mode); + if (!ret) { + dev_err(dev, "failed to read input for sysfs\n"); + return -EINVAL; + } + + mutex_lock(&gl_ts->fw_cfg_mutex); + if (mode == 1) { + gl_ts->fw_cfg_uploading = true; + ret = IT7260_fw_upload(dev, true); + if (ret) { + dev_err(dev, "Failed to force flash fw: %d", ret); + gl_ts->fw_upgrade_result = false; + } else { + gl_ts->fw_upgrade_result = true; + } + gl_ts->fw_cfg_uploading = false; + } + mutex_unlock(&gl_ts->fw_cfg_mutex); + + return count; +} + +static ssize_t sysfs_force_cfg_upgrade_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int mode = 0, ret; + + if (gl_ts->suspended) { + dev_err(dev, "Device is suspended, can't flash cfg!!!\n"); + return -EBUSY; + } + + ret = kstrtoint(buf, 10, &mode); + if (!ret) { + dev_err(dev, "failed to read input for sysfs\n"); + return -EINVAL; + } + + mutex_lock(&gl_ts->fw_cfg_mutex); + if (mode == 1) { + gl_ts->fw_cfg_uploading = true; + ret = IT7260_cfg_upload(dev, true); + if (ret) { + dev_err(dev, "Failed to force flash cfg: %d", ret); + gl_ts->cfg_upgrade_result = false; + } else { + gl_ts->cfg_upgrade_result = true; + } + gl_ts->fw_cfg_uploading = false; + } + mutex_unlock(&gl_ts->fw_cfg_mutex); + + return count; +} + +static ssize_t sysfs_force_fw_upgrade_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, MAX_BUFFER_SIZE, "%d", gl_ts->fw_upgrade_result); +} + +static ssize_t sysfs_force_cfg_upgrade_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, MAX_BUFFER_SIZE, "%d", gl_ts->cfg_upgrade_result); +} + +static ssize_t sysfs_calibration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, MAX_BUFFER_SIZE, "%d\n", + gl_ts->calibration_success); +} + +static bool IT7260_chipSendCalibrationCmd(bool auto_tune_on) +{ + uint8_t cmd_calibrate[] = {CMD_CALIBRATE, 0, + auto_tune_on ? 1 : 0, 0, 0}; + return IT7260_i2cWrite(BUF_COMMAND, cmd_calibrate, + sizeof(cmd_calibrate)); +} + +static ssize_t sysfs_calibration_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + uint8_t resp; + + if (!IT7260_chipSendCalibrationCmd(false)) { + dev_err(dev, "failed to send calibration command\n"); + } else { + gl_ts->calibration_success = + IT7260_i2cRead(BUF_RESPONSE, &resp, sizeof(resp)); + + /* + * previous logic that was here never called + * IT7260_firmware_reinitialize() due to checking a + * guaranteed-not-null value against null. We now + * call it. Hopefully this is OK + */ + if (!resp) + dev_dbg(dev, "IT7260_firmware_reinitialize-> %s\n", + IT7260_firmware_reinitialize(CMD_FIRMWARE_REINIT_6F) + ? "success" : "fail"); + } + + return count; +} + +static ssize_t sysfs_point_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint8_t point_data[sizeof(struct PointData)]; + bool readSuccess; + ssize_t ret; + + readSuccess = IT7260_i2cReadNoReadyCheck(BUF_POINT_INFO, point_data, + sizeof(point_data)); + + if (readSuccess) { + ret = scnprintf(buf, MAX_BUFFER_SIZE, + "point_show read ret[%d]--point[%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x]\n", + readSuccess, point_data[0], point_data[1], + point_data[2], point_data[3], point_data[4], + point_data[5], point_data[6], point_data[7], + point_data[8], point_data[9], point_data[10], + point_data[11], point_data[12], point_data[13]); + } else { + ret = scnprintf(buf, MAX_BUFFER_SIZE, + "failed to read point data\n"); + } + dev_dbg(dev, "%s", buf); + + return ret; +} + +static ssize_t sysfs_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, MAX_BUFFER_SIZE, + "fw{%X.%X.%X.%X} cfg{%X.%X.%X.%X}\n", + gl_ts->fw_ver[0], gl_ts->fw_ver[1], gl_ts->fw_ver[2], + gl_ts->fw_ver[3], gl_ts->cfg_ver[0], gl_ts->cfg_ver[1], + gl_ts->cfg_ver[2], gl_ts->cfg_ver[3]); +} + +static ssize_t sysfs_sleep_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + /* + * The usefulness of this was questionable at best - we were at least + * leaking a byte of kernel data (by claiming to return a byte but not + * writing to buf. To fix this now we actually return the sleep status + */ + *buf = gl_ts->suspended ? '1' : '0'; + return 1; +} + +static ssize_t sysfs_sleep_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int go_to_sleep, ret; + + ret = kstrtoint(buf, 10, &go_to_sleep); + + /* (gl_ts->suspended == true && goToSleepVal > 0) means + * device is already suspended and you want it to be in sleep, + * (gl_ts->suspended == false && goToSleepVal == 0) means + * device is already active and you also want it to be active. + */ + if ((gl_ts->suspended && go_to_sleep > 0) || + (!gl_ts->suspended && go_to_sleep == 0)) + dev_err(dev, "duplicate request to %s chip\n", + go_to_sleep ? "sleep" : "wake"); + else if (go_to_sleep) { + disable_irq(gl_ts->client->irq); + IT7260_ts_chipLowPowerMode(PWR_CTL_SLEEP_MODE); + dev_dbg(dev, "touch is going to sleep...\n"); + } else { + IT7260_ts_chipLowPowerMode(PWR_CTL_ACTIVE_MODE); + enable_irq(gl_ts->client->irq); + dev_dbg(dev, "touch is going to wake!\n"); + } + gl_ts->suspended = go_to_sleep; + + return count; +} + +static ssize_t sysfs_cfg_name_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *strptr; + + if (count >= MAX_BUFFER_SIZE) { + dev_err(dev, "Input over %d chars long\n", MAX_BUFFER_SIZE); + return -EINVAL; + } + + strptr = strnstr(buf, ".bin", count); + if (!strptr) { + dev_err(dev, "Input is invalid cfg file\n"); + return -EINVAL; + } + + strlcpy(gl_ts->cfg_name, buf, count); + + return count; +} + +static ssize_t sysfs_cfg_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (strnlen(gl_ts->cfg_name, MAX_BUFFER_SIZE) > 0) + return scnprintf(buf, MAX_BUFFER_SIZE, "%s\n", + gl_ts->cfg_name); + else + return scnprintf(buf, MAX_BUFFER_SIZE, + "No config file name given\n"); +} + +static ssize_t sysfs_fw_name_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *strptr; + + if (count >= MAX_BUFFER_SIZE) { + dev_err(dev, "Input over %d chars long\n", MAX_BUFFER_SIZE); + return -EINVAL; + } + + strptr = strnstr(buf, ".bin", count); + if (!strptr) { + dev_err(dev, "Input is invalid fw file\n"); + return -EINVAL; + } + + strlcpy(gl_ts->fw_name, buf, count); + return count; +} + +static ssize_t sysfs_fw_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (strnlen(gl_ts->fw_name, MAX_BUFFER_SIZE) > 0) + return scnprintf(buf, MAX_BUFFER_SIZE, "%s\n", + gl_ts->fw_name); + else + return scnprintf(buf, MAX_BUFFER_SIZE, + "No firmware file name given\n"); +} + +static DEVICE_ATTR(version, S_IRUGO | S_IWUSR, + sysfs_version_show, NULL); +static DEVICE_ATTR(sleep, S_IRUGO | S_IWUSR, + sysfs_sleep_show, sysfs_sleep_store); +static DEVICE_ATTR(calibration, S_IRUGO | S_IWUSR, + sysfs_calibration_show, sysfs_calibration_store); +static DEVICE_ATTR(fw_update, S_IRUGO | S_IWUSR, + sysfs_fw_upgrade_show, sysfs_fw_upgrade_store); +static DEVICE_ATTR(cfg_update, S_IRUGO | S_IWUSR, + sysfs_cfg_upgrade_show, sysfs_cfg_upgrade_store); +static DEVICE_ATTR(point, S_IRUGO | S_IWUSR, + sysfs_point_show, NULL); +static DEVICE_ATTR(fw_name, S_IRUGO | S_IWUSR, + sysfs_fw_name_show, sysfs_fw_name_store); +static DEVICE_ATTR(cfg_name, S_IRUGO | S_IWUSR, + sysfs_cfg_name_show, sysfs_cfg_name_store); +static DEVICE_ATTR(force_fw_update, S_IRUGO | S_IWUSR, + sysfs_force_fw_upgrade_show, + sysfs_force_fw_upgrade_store); +static DEVICE_ATTR(force_cfg_update, S_IRUGO | S_IWUSR, + sysfs_force_cfg_upgrade_show, + sysfs_force_cfg_upgrade_store); + +static struct attribute *it7260_attributes[] = { + &dev_attr_version.attr, + &dev_attr_sleep.attr, + &dev_attr_calibration.attr, + &dev_attr_fw_update.attr, + &dev_attr_cfg_update.attr, + &dev_attr_point.attr, + &dev_attr_fw_name.attr, + &dev_attr_cfg_name.attr, + &dev_attr_force_fw_update.attr, + &dev_attr_force_cfg_update.attr, + NULL +}; + +static const struct attribute_group it7260_attr_group = { + .attrs = it7260_attributes, +}; + +static void IT7260_chipExternalCalibration(bool autoTuneEnabled) +{ + uint8_t resp[2]; + + dev_dbg(&gl_ts->client->dev, "sent calibration command -> %d\n", + IT7260_chipSendCalibrationCmd(autoTuneEnabled)); + IT7260_waitDeviceReady(true, true); + IT7260_i2cReadNoReadyCheck(BUF_RESPONSE, resp, sizeof(resp)); + IT7260_firmware_reinitialize(CMD_FIRMWARE_REINIT_C); +} + +void IT7260_sendCalibrationCmd(void) +{ + IT7260_chipExternalCalibration(false); +} +EXPORT_SYMBOL(IT7260_sendCalibrationCmd); + +static void IT7260_ts_release_all(void) +{ + int finger; + + for (finger = 0; finger < gl_ts->pdata->num_of_fingers; finger++) { + input_mt_slot(gl_ts->input_dev, finger); + input_mt_report_slot_state(gl_ts->input_dev, + MT_TOOL_FINGER, 0); + } + + input_report_key(gl_ts->input_dev, BTN_TOUCH, 0); + input_sync(gl_ts->input_dev); +} + +static irqreturn_t IT7260_ts_threaded_handler(int irq, void *devid) +{ + struct PointData point_data; + struct input_dev *input_dev = gl_ts->input_dev; + u8 dev_status, finger, touch_count = 0, finger_status; + u8 pressure = FD_PRESSURE_NONE; + u16 x, y; + bool palm_detected; + + /* verify there is point data to read & it is readable and valid */ + IT7260_i2cReadNoReadyCheck(BUF_QUERY, &dev_status, sizeof(dev_status)); + if (!((dev_status & PT_INFO_BITS) & PT_INFO_YES)) + return IRQ_HANDLED; + if (!IT7260_i2cReadNoReadyCheck(BUF_POINT_INFO, (void *)&point_data, + sizeof(point_data))) { + dev_err(&gl_ts->client->dev, + "failed to read point data buffer\n"); + return IRQ_HANDLED; + } + + /* Check if controller moves from idle to active state */ + if ((point_data.flags & PD_FLAGS_DATA_TYPE_BITS) != + PD_FLAGS_DATA_TYPE_TOUCH) { + /* + * This code adds the touch-to-wake functionality to the ITE + * tech driver. When user puts a finger on touch controller in + * idle state, the controller moves to active state and driver + * sends the KEY_WAKEUP event to wake the device. The + * pm_stay_awake() call tells the pm core to stay awake until + * the CPU cores are up already. The schedule_work() call + * schedule a work that tells the pm core to relax once the CPU + * cores are up. + */ + if (gl_ts->device_needs_wakeup) { + pm_stay_awake(&gl_ts->client->dev); + input_report_key(input_dev, KEY_WAKEUP, 1); + input_sync(input_dev); + input_report_key(input_dev, KEY_WAKEUP, 0); + input_sync(input_dev); + schedule_work(&gl_ts->work_pm_relax); + return IRQ_HANDLED; + } + } + + palm_detected = point_data.palm & PD_PALM_FLAG_BIT; + if (palm_detected && gl_ts->pdata->palm_detect_en) { + input_report_key(input_dev, + gl_ts->pdata->palm_detect_keycode, 1); + input_sync(input_dev); + input_report_key(input_dev, + gl_ts->pdata->palm_detect_keycode, 0); + input_sync(input_dev); + } + + for (finger = 0; finger < gl_ts->pdata->num_of_fingers; finger++) { + finger_status = point_data.flags & (0x01 << finger); + + input_mt_slot(input_dev, finger); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, + finger_status != 0); + + x = point_data.fd[finger].xLo + + (((u16)(point_data.fd[finger].hi & 0x0F)) << 8); + y = point_data.fd[finger].yLo + + (((u16)(point_data.fd[finger].hi & 0xF0)) << 4); + + pressure = point_data.fd[finger].pressure & FD_PRESSURE_BITS; + + if (finger_status) { + if (pressure >= FD_PRESSURE_LIGHT) { + input_report_key(input_dev, BTN_TOUCH, 1); + input_report_abs(input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(input_dev, + ABS_MT_POSITION_Y, y); + touch_count++; + } + } + } + + input_report_key(input_dev, BTN_TOUCH, touch_count > 0); + input_sync(input_dev); + + return IRQ_HANDLED; +} + +static void IT7260_ts_work_func(struct work_struct *work) +{ + pm_relax(&gl_ts->client->dev); +} + +static int IT7260_chipIdentify(void) +{ + static const uint8_t cmd_ident[] = {CMD_IDENT_CHIP}; + static const uint8_t expected_id[] = {0x0A, 'I', 'T', 'E', '7', + '2', '6', '0'}; + uint8_t chip_id[10] = {0,}; + + IT7260_waitDeviceReady(false, false); + + if (!IT7260_i2cWriteNoReadyCheck(BUF_COMMAND, cmd_ident, + sizeof(cmd_ident))) { + dev_err(&gl_ts->client->dev, "failed to write CMD_IDENT_CHIP\n"); + return -ENODEV; + } + + IT7260_waitDeviceReady(false, false); + + if (!IT7260_i2cReadNoReadyCheck(BUF_RESPONSE, chip_id, + sizeof(chip_id))) { + dev_err(&gl_ts->client->dev, "failed to read chip-id\n"); + return -ENODEV; + } + dev_info(&gl_ts->client->dev, + "IT7260_chipIdentify read id: %02X %c%c%c%c%c%c%c %c%c\n", + chip_id[0], chip_id[1], chip_id[2], chip_id[3], chip_id[4], + chip_id[5], chip_id[6], chip_id[7], chip_id[8], chip_id[9]); + + if (memcmp(chip_id, expected_id, sizeof(expected_id))) + return -EINVAL; + + if (chip_id[8] == '5' && chip_id[9] == '6') + dev_info(&gl_ts->client->dev, "rev BX3 found\n"); + else if (chip_id[8] == '6' && chip_id[9] == '6') + dev_info(&gl_ts->client->dev, "rev BX4 found\n"); + else + dev_info(&gl_ts->client->dev, "unknown revision (0x%02X 0x%02X) found\n", + chip_id[8], chip_id[9]); + + return 0; +} + +static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA) +{ + return (regulator_count_voltages(reg) > 0) ? + regulator_set_optimum_mode(reg, load_uA) : 0; +} + +static int IT7260_regulator_configure(bool on) +{ + int retval; + + if (on == false) + goto hw_shutdown; + + gl_ts->vdd = devm_regulator_get(&gl_ts->client->dev, "vdd"); + if (IS_ERR(gl_ts->vdd)) { + dev_err(&gl_ts->client->dev, + "%s: Failed to get vdd regulator\n", __func__); + return PTR_ERR(gl_ts->vdd); + } + + if (regulator_count_voltages(gl_ts->vdd) > 0) { + retval = regulator_set_voltage(gl_ts->vdd, + IT_VTG_MIN_UV, IT_VTG_MAX_UV); + if (retval) { + dev_err(&gl_ts->client->dev, + "regulator set_vtg failed retval =%d\n", + retval); + goto err_set_vtg_vdd; + } + } + + gl_ts->avdd = devm_regulator_get(&gl_ts->client->dev, "avdd"); + if (IS_ERR(gl_ts->avdd)) { + dev_err(&gl_ts->client->dev, + "%s: Failed to get i2c regulator\n", __func__); + retval = PTR_ERR(gl_ts->avdd); + goto err_get_vtg_i2c; + } + + if (regulator_count_voltages(gl_ts->avdd) > 0) { + retval = regulator_set_voltage(gl_ts->avdd, + IT_I2C_VTG_MIN_UV, IT_I2C_VTG_MAX_UV); + if (retval) { + dev_err(&gl_ts->client->dev, + "reg set i2c vtg failed retval =%d\n", + retval); + goto err_set_vtg_i2c; + } + } + + return 0; + +err_set_vtg_i2c: +err_get_vtg_i2c: + if (regulator_count_voltages(gl_ts->vdd) > 0) + regulator_set_voltage(gl_ts->vdd, 0, IT_VTG_MAX_UV); +err_set_vtg_vdd: + return retval; + +hw_shutdown: + if (regulator_count_voltages(gl_ts->vdd) > 0) + regulator_set_voltage(gl_ts->vdd, 0, IT_VTG_MAX_UV); + if (regulator_count_voltages(gl_ts->avdd) > 0) + regulator_set_voltage(gl_ts->avdd, 0, IT_I2C_VTG_MAX_UV); + return 0; +}; + +static int IT7260_power_on(bool on) +{ + int retval; + + if (on == false) + goto power_off; + + retval = reg_set_optimum_mode_check(gl_ts->vdd, + IT_ACTIVE_LOAD_UA); + if (retval < 0) { + dev_err(&gl_ts->client->dev, + "Regulator vdd set_opt failed rc=%d\n", + retval); + return retval; + } + + retval = regulator_enable(gl_ts->vdd); + if (retval) { + dev_err(&gl_ts->client->dev, + "Regulator vdd enable failed rc=%d\n", + retval); + goto error_reg_en_vdd; + } + + retval = reg_set_optimum_mode_check(gl_ts->avdd, + IT_I2C_ACTIVE_LOAD_UA); + if (retval < 0) { + dev_err(&gl_ts->client->dev, + "Regulator avdd set_opt failed rc=%d\n", + retval); + goto error_reg_opt_i2c; + } + + retval = regulator_enable(gl_ts->avdd); + if (retval) { + dev_err(&gl_ts->client->dev, + "Regulator avdd enable failed rc=%d\n", + retval); + goto error_reg_en_avdd; + } + + return 0; + +error_reg_en_avdd: + reg_set_optimum_mode_check(gl_ts->avdd, 0); +error_reg_opt_i2c: + regulator_disable(gl_ts->vdd); +error_reg_en_vdd: + reg_set_optimum_mode_check(gl_ts->vdd, 0); + return retval; + +power_off: + reg_set_optimum_mode_check(gl_ts->vdd, 0); + regulator_disable(gl_ts->vdd); + reg_set_optimum_mode_check(gl_ts->avdd, 0); + regulator_disable(gl_ts->avdd); + + return 0; +} + +static int IT7260_gpio_configure(bool on) +{ + int retval = 0; + + if (on) { + if (gpio_is_valid(gl_ts->pdata->irq_gpio)) { + /* configure touchscreen irq gpio */ + retval = gpio_request(gl_ts->pdata->irq_gpio, + "ite_irq_gpio"); + if (retval) { + dev_err(&gl_ts->client->dev, + "unable to request irq gpio [%d]\n", + retval); + goto err_irq_gpio_req; + } + + retval = gpio_direction_input(gl_ts->pdata->irq_gpio); + if (retval) { + dev_err(&gl_ts->client->dev, + "unable to set direction for irq gpio [%d]\n", + retval); + goto err_irq_gpio_dir; + } + } else { + dev_err(&gl_ts->client->dev, + "irq gpio not provided\n"); + goto err_irq_gpio_req; + } + + if (gpio_is_valid(gl_ts->pdata->reset_gpio)) { + /* configure touchscreen reset out gpio */ + retval = gpio_request(gl_ts->pdata->reset_gpio, + "ite_reset_gpio"); + if (retval) { + dev_err(&gl_ts->client->dev, + "unable to request reset gpio [%d]\n", + retval); + goto err_reset_gpio_req; + } + + retval = gpio_direction_output( + gl_ts->pdata->reset_gpio, 1); + if (retval) { + dev_err(&gl_ts->client->dev, + "unable to set direction for reset gpio [%d]\n", + retval); + goto err_reset_gpio_dir; + } + + if (gl_ts->pdata->low_reset) + gpio_set_value(gl_ts->pdata->reset_gpio, 0); + else + gpio_set_value(gl_ts->pdata->reset_gpio, 1); + + msleep(gl_ts->pdata->reset_delay); + } else { + dev_err(&gl_ts->client->dev, + "reset gpio not provided\n"); + goto err_reset_gpio_req; + } + } else { + if (gpio_is_valid(gl_ts->pdata->irq_gpio)) + gpio_free(gl_ts->pdata->irq_gpio); + if (gpio_is_valid(gl_ts->pdata->reset_gpio)) { + /* + * This is intended to save leakage current + * only. Even if the call(gpio_direction_input) + * fails, only leakage current will be more but + * functionality will not be affected. + */ + retval = gpio_direction_input(gl_ts->pdata->reset_gpio); + if (retval) { + dev_err(&gl_ts->client->dev, + "unable to set direction for gpio reset [%d]\n", + retval); + } + gpio_free(gl_ts->pdata->reset_gpio); + } + } + + return 0; + +err_reset_gpio_dir: + if (gpio_is_valid(gl_ts->pdata->reset_gpio)) + gpio_free(gl_ts->pdata->reset_gpio); +err_reset_gpio_req: +err_irq_gpio_dir: + if (gpio_is_valid(gl_ts->pdata->irq_gpio)) + gpio_free(gl_ts->pdata->irq_gpio); +err_irq_gpio_req: + return retval; +} + +#if CONFIG_OF +static int IT7260_get_dt_coords(struct device *dev, char *name, + struct IT7260_ts_platform_data *pdata) +{ + u32 coords[IT7260_COORDS_ARR_SIZE]; + struct property *prop; + struct device_node *np = dev->of_node; + int coords_size, rc; + + prop = of_find_property(np, name, NULL); + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + + coords_size = prop->length / sizeof(u32); + if (coords_size != IT7260_COORDS_ARR_SIZE) { + dev_err(dev, "invalid %s\n", name); + return -EINVAL; + } + + rc = of_property_read_u32_array(np, name, coords, coords_size); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read %s\n", name); + return rc; + } + + if (strcmp(name, "ite,panel-coords") == 0) { + pdata->panel_minx = coords[0]; + pdata->panel_miny = coords[1]; + pdata->panel_maxx = coords[2]; + pdata->panel_maxy = coords[3]; + + if (pdata->panel_maxx == 0 || pdata->panel_minx > 0) + rc = -EINVAL; + else if (pdata->panel_maxy == 0 || pdata->panel_miny > 0) + rc = -EINVAL; + + if (rc) { + dev_err(dev, "Invalid panel resolution %d\n", rc); + return rc; + } + } else if (strcmp(name, "ite,display-coords") == 0) { + pdata->disp_minx = coords[0]; + pdata->disp_miny = coords[1]; + pdata->disp_maxx = coords[2]; + pdata->disp_maxy = coords[3]; + } else { + dev_err(dev, "unsupported property %s\n", name); + return -EINVAL; + } + + return 0; +} + +static int IT7260_parse_dt(struct device *dev, + struct IT7260_ts_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + u32 temp_val; + int rc; + + /* reset, irq gpio info */ + pdata->reset_gpio = of_get_named_gpio_flags(np, + "ite,reset-gpio", 0, &pdata->reset_gpio_flags); + pdata->irq_gpio = of_get_named_gpio_flags(np, + "ite,irq-gpio", 0, &pdata->irq_gpio_flags); + + rc = of_property_read_u32(np, "ite,num-fingers", &temp_val); + if (!rc) + pdata->num_of_fingers = temp_val; + else if (rc != -EINVAL) { + dev_err(dev, "Unable to read reset delay\n"); + return rc; + } + + pdata->wakeup = of_property_read_bool(np, "ite,wakeup"); + pdata->palm_detect_en = of_property_read_bool(np, "ite,palm-detect-en"); + if (pdata->palm_detect_en) { + rc = of_property_read_u32(np, "ite,palm-detect-keycode", + &temp_val); + if (!rc) { + pdata->palm_detect_keycode = temp_val; + } else { + dev_err(dev, "Unable to read palm-detect-keycode\n"); + return rc; + } + } + + rc = of_property_read_string(np, "ite,fw-name", &pdata->fw_name); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw image name %d\n", rc); + return rc; + } + + rc = of_property_read_string(np, "ite,cfg-name", &pdata->cfg_name); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read cfg image name %d\n", rc); + return rc; + } + + snprintf(gl_ts->fw_name, MAX_BUFFER_SIZE, "%s", + (pdata->fw_name != NULL) ? pdata->fw_name : FW_NAME); + snprintf(gl_ts->cfg_name, MAX_BUFFER_SIZE, "%s", + (pdata->cfg_name != NULL) ? pdata->cfg_name : CFG_NAME); + + rc = of_property_read_u32(np, "ite,reset-delay", &temp_val); + if (!rc) + pdata->reset_delay = temp_val; + else if (rc != -EINVAL) { + dev_err(dev, "Unable to read reset delay\n"); + return rc; + } + + pdata->low_reset = of_property_read_bool(np, "ite,low-reset"); + + rc = IT7260_get_dt_coords(dev, "ite,display-coords", pdata); + if (rc && (rc != -EINVAL)) + return rc; + + rc = IT7260_get_dt_coords(dev, "ite,panel-coords", pdata); + if (rc && (rc != -EINVAL)) + return rc; + + return 0; +} +#else +static inline int IT7260_ts_parse_dt(struct device *dev, + struct IT7260_ts_platform_data *pdata) +{ + return 0; +} +#endif + +static int IT7260_ts_pinctrl_init(struct IT7260_ts_data *ts_data) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + ts_data->ts_pinctrl = devm_pinctrl_get(&(ts_data->client->dev)); + if (IS_ERR_OR_NULL(ts_data->ts_pinctrl)) { + retval = PTR_ERR(ts_data->ts_pinctrl); + dev_dbg(&ts_data->client->dev, + "Target does not use pinctrl %d\n", retval); + goto err_pinctrl_get; + } + + ts_data->pinctrl_state_active + = pinctrl_lookup_state(ts_data->ts_pinctrl, + PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(ts_data->pinctrl_state_active)) { + retval = PTR_ERR(ts_data->pinctrl_state_active); + dev_err(&ts_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + ts_data->pinctrl_state_suspend + = pinctrl_lookup_state(ts_data->ts_pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(ts_data->pinctrl_state_suspend)) { + retval = PTR_ERR(ts_data->pinctrl_state_suspend); + dev_err(&ts_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + ts_data->pinctrl_state_release + = pinctrl_lookup_state(ts_data->ts_pinctrl, + PINCTRL_STATE_RELEASE); + if (IS_ERR_OR_NULL(ts_data->pinctrl_state_release)) { + retval = PTR_ERR(ts_data->pinctrl_state_release); + dev_dbg(&ts_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, retval); + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(ts_data->ts_pinctrl); +err_pinctrl_get: + ts_data->ts_pinctrl = NULL; + return retval; +} + +static int IT7260_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + static const uint8_t cmd_start[] = {CMD_UNKNOWN_7}; + struct IT7260_ts_platform_data *pdata; + uint8_t rsp[2]; + int ret = -1, err; + struct dentry *temp; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "need I2C_FUNC_I2C\n"); + return -ENODEV; + } + + gl_ts = devm_kzalloc(&client->dev, sizeof(*gl_ts), GFP_KERNEL); + if (!gl_ts) + return -ENOMEM; + + gl_ts->client = client; + i2c_set_clientdata(client, gl_ts); + + if (client->dev.platform_data == NULL) + return -ENODEV; + + if (client->dev.of_node) { + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + ret = IT7260_parse_dt(&client->dev, pdata); + if (ret) + return ret; + } else { + pdata = client->dev.platform_data; + } + + if (!pdata) + return -ENOMEM; + + gl_ts->pdata = pdata; + + ret = IT7260_regulator_configure(true); + if (ret < 0) { + dev_err(&client->dev, "Failed to configure regulators\n"); + goto err_reg_configure; + } + + ret = IT7260_power_on(true); + if (ret < 0) { + dev_err(&client->dev, "Failed to power on\n"); + goto err_power_device; + } + + ret = IT7260_ts_pinctrl_init(gl_ts); + if (!ret && gl_ts->ts_pinctrl) { + /* + * Pinctrl handle is optional. If pinctrl handle is found + * let pins to be configured in active state. If not + * found continue further without error. + */ + ret = pinctrl_select_state(gl_ts->ts_pinctrl, + gl_ts->pinctrl_state_active); + if (ret < 0) { + dev_err(&gl_ts->client->dev, + "failed to select pin to active state %d", + ret); + } + } else { + ret = IT7260_gpio_configure(true); + if (ret < 0) { + dev_err(&client->dev, "Failed to configure gpios\n"); + goto err_gpio_config; + } + } + + ret = IT7260_chipIdentify(); + if (ret) { + dev_err(&client->dev, "Failed to identify chip %d!!!", ret); + goto err_identification_fail; + } + + IT7260_get_chip_versions(&client->dev); + + gl_ts->input_dev = input_allocate_device(); + if (!gl_ts->input_dev) { + dev_err(&client->dev, "failed to allocate input device\n"); + ret = -ENOMEM; + goto err_input_alloc; + } + + /* Initialize mutex for fw and cfg upgrade */ + mutex_init(&gl_ts->fw_cfg_mutex); + + gl_ts->input_dev->name = DEVICE_NAME; + gl_ts->input_dev->phys = "I2C"; + gl_ts->input_dev->id.bustype = BUS_I2C; + gl_ts->input_dev->id.vendor = 0x0001; + gl_ts->input_dev->id.product = 0x7260; + set_bit(EV_SYN, gl_ts->input_dev->evbit); + set_bit(EV_KEY, gl_ts->input_dev->evbit); + set_bit(EV_ABS, gl_ts->input_dev->evbit); + set_bit(INPUT_PROP_DIRECT, gl_ts->input_dev->propbit); + set_bit(BTN_TOUCH, gl_ts->input_dev->keybit); + input_set_abs_params(gl_ts->input_dev, ABS_MT_POSITION_X, + gl_ts->pdata->disp_minx, gl_ts->pdata->disp_maxx, 0, 0); + input_set_abs_params(gl_ts->input_dev, ABS_MT_POSITION_Y, + gl_ts->pdata->disp_miny, gl_ts->pdata->disp_maxy, 0, 0); + input_mt_init_slots(gl_ts->input_dev, gl_ts->pdata->num_of_fingers, 0); + + input_set_drvdata(gl_ts->input_dev, gl_ts); + + if (pdata->wakeup) { + set_bit(KEY_WAKEUP, gl_ts->input_dev->keybit); + INIT_WORK(&gl_ts->work_pm_relax, IT7260_ts_work_func); + device_init_wakeup(&client->dev, pdata->wakeup); + } + + if (pdata->palm_detect_en) + set_bit(gl_ts->pdata->palm_detect_keycode, + gl_ts->input_dev->keybit); + + if (input_register_device(gl_ts->input_dev)) { + dev_err(&client->dev, "failed to register input device\n"); + goto err_input_register; + } + + if (request_threaded_irq(client->irq, NULL, IT7260_ts_threaded_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, gl_ts)) { + dev_err(&client->dev, "request_irq failed\n"); + goto err_irq_reg; + } + + if (sysfs_create_group(&(client->dev.kobj), &it7260_attr_group)) { + dev_err(&client->dev, "failed to register sysfs #2\n"); + goto err_sysfs_grp_create; + } + +#if defined(CONFIG_FB) + gl_ts->fb_notif.notifier_call = fb_notifier_callback; + + ret = fb_register_client(&gl_ts->fb_notif); + if (ret) + dev_err(&client->dev, "Unable to register fb_notifier %d\n", + ret); +#endif + + IT7260_i2cWriteNoReadyCheck(BUF_COMMAND, cmd_start, sizeof(cmd_start)); + msleep(pdata->reset_delay); + IT7260_i2cReadNoReadyCheck(BUF_RESPONSE, rsp, sizeof(rsp)); + msleep(pdata->reset_delay); + + gl_ts->dir = debugfs_create_dir(DEBUGFS_DIR_NAME, NULL); + if (gl_ts->dir == NULL || IS_ERR(gl_ts->dir)) { + dev_err(&client->dev, + "%s: Failed to create debugfs directory, ret = %ld\n", + __func__, PTR_ERR(gl_ts->dir)); + ret = PTR_ERR(gl_ts->dir); + goto err_create_debugfs_dir; + } + + temp = debugfs_create_file("suspend", S_IRUSR | S_IWUSR, gl_ts->dir, + gl_ts, &debug_suspend_fops); + if (temp == NULL || IS_ERR(temp)) { + dev_err(&client->dev, + "%s: Failed to create suspend debugfs file, ret = %ld\n", + __func__, PTR_ERR(temp)); + ret = PTR_ERR(temp); + goto err_create_debugfs_file; + } + + return 0; + +err_create_debugfs_file: + debugfs_remove_recursive(gl_ts->dir); +err_create_debugfs_dir: +#if defined(CONFIG_FB) + if (fb_unregister_client(&gl_ts->fb_notif)) + dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); +#endif + sysfs_remove_group(&(client->dev.kobj), &it7260_attr_group); + +err_sysfs_grp_create: + free_irq(client->irq, gl_ts); + +err_irq_reg: + input_unregister_device(gl_ts->input_dev); + +err_input_register: + if (pdata->wakeup) { + cancel_work_sync(&gl_ts->work_pm_relax); + device_init_wakeup(&client->dev, false); + } + if (gl_ts->input_dev) + input_free_device(gl_ts->input_dev); + gl_ts->input_dev = NULL; + +err_input_alloc: +err_identification_fail: + if (gl_ts->ts_pinctrl) { + if (IS_ERR_OR_NULL(gl_ts->pinctrl_state_release)) { + devm_pinctrl_put(gl_ts->ts_pinctrl); + gl_ts->ts_pinctrl = NULL; + } else { + err = pinctrl_select_state(gl_ts->ts_pinctrl, + gl_ts->pinctrl_state_release); + if (err) + dev_err(&gl_ts->client->dev, + "failed to select relase pinctrl state %d\n", + err); + } + } else { + if (gpio_is_valid(pdata->reset_gpio)) + gpio_free(pdata->reset_gpio); + if (gpio_is_valid(pdata->irq_gpio)) + gpio_free(pdata->irq_gpio); + } + +err_gpio_config: + IT7260_power_on(false); + +err_power_device: + IT7260_regulator_configure(false); + +err_reg_configure: + return ret; +} + +static int IT7260_ts_remove(struct i2c_client *client) +{ + int ret; + + debugfs_remove_recursive(gl_ts->dir); +#if defined(CONFIG_FB) + if (fb_unregister_client(&gl_ts->fb_notif)) + dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); +#endif + sysfs_remove_group(&(client->dev.kobj), &it7260_attr_group); + free_irq(client->irq, gl_ts); + input_unregister_device(gl_ts->input_dev); + if (gl_ts->input_dev) + input_free_device(gl_ts->input_dev); + gl_ts->input_dev = NULL; + if (gl_ts->pdata->wakeup) { + cancel_work_sync(&gl_ts->work_pm_relax); + device_init_wakeup(&client->dev, false); + } + if (gl_ts->ts_pinctrl) { + if (IS_ERR_OR_NULL(gl_ts->pinctrl_state_release)) { + devm_pinctrl_put(gl_ts->ts_pinctrl); + gl_ts->ts_pinctrl = NULL; + } else { + ret = pinctrl_select_state(gl_ts->ts_pinctrl, + gl_ts->pinctrl_state_release); + if (ret) + dev_err(&gl_ts->client->dev, + "failed to select relase pinctrl state %d\n", + ret); + } + } else { + if (gpio_is_valid(gl_ts->pdata->reset_gpio)) + gpio_free(gl_ts->pdata->reset_gpio); + if (gpio_is_valid(gl_ts->pdata->irq_gpio)) + gpio_free(gl_ts->pdata->irq_gpio); + } + IT7260_power_on(false); + IT7260_regulator_configure(false); + + return 0; +} + +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + + if (evdata && evdata->data && gl_ts && gl_ts->client) { + if (event == FB_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) + IT7260_ts_resume(&(gl_ts->client->dev)); + else if (*blank == FB_BLANK_POWERDOWN || + *blank == FB_BLANK_VSYNC_SUSPEND) + IT7260_ts_suspend(&(gl_ts->client->dev)); + } + } + + return 0; +} +#endif + +#ifdef CONFIG_PM +static int IT7260_ts_resume(struct device *dev) +{ + int retval; + + if (device_may_wakeup(dev)) { + if (gl_ts->device_needs_wakeup) { + gl_ts->device_needs_wakeup = false; + disable_irq_wake(gl_ts->client->irq); + } + return 0; + } + + if (gl_ts->ts_pinctrl) { + retval = pinctrl_select_state(gl_ts->ts_pinctrl, + gl_ts->pinctrl_state_active); + if (retval < 0) { + dev_err(dev, "Cannot get default pinctrl state %d\n", + retval); + goto err_pinctrl_select_suspend; + } + } + + enable_irq(gl_ts->client->irq); + gl_ts->suspended = false; + return 0; + +err_pinctrl_select_suspend: + return retval; +} + +static int IT7260_ts_suspend(struct device *dev) +{ + int retval; + + if (gl_ts->fw_cfg_uploading) { + dev_dbg(dev, "Fw/cfg uploading. Can't go to suspend.\n"); + return -EBUSY; + } + + if (device_may_wakeup(dev)) { + if (!gl_ts->device_needs_wakeup) { + /* put the device in low power idle mode */ + IT7260_ts_chipLowPowerMode(PWR_CTL_LOW_POWER_MODE); + + gl_ts->device_needs_wakeup = true; + enable_irq_wake(gl_ts->client->irq); + } + return 0; + } + + disable_irq(gl_ts->client->irq); + + IT7260_ts_release_all(); + + if (gl_ts->ts_pinctrl) { + retval = pinctrl_select_state(gl_ts->ts_pinctrl, + gl_ts->pinctrl_state_suspend); + if (retval < 0) { + dev_err(dev, "Cannot get idle pinctrl state %d\n", + retval); + goto err_pinctrl_select_suspend; + } + } + + gl_ts->suspended = true; + + return 0; + +err_pinctrl_select_suspend: + return retval; +} + +static const struct dev_pm_ops IT7260_ts_dev_pm_ops = { + .suspend = IT7260_ts_suspend, + .resume = IT7260_ts_resume, +}; +#else +static int IT7260_ts_resume(struct device *dev) +{ + return 0; +} + +static int IT7260_ts_suspend(struct device *dev) +{ + return 0; +} +#endif + +static const struct i2c_device_id IT7260_ts_id[] = { + { DEVICE_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, IT7260_ts_id); + +static const struct of_device_id IT7260_match_table[] = { + { .compatible = "ite,it7260_ts",}, + {}, +}; + +static struct i2c_driver IT7260_ts_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DEVICE_NAME, + .of_match_table = IT7260_match_table, +#ifdef CONFIG_PM + .pm = &IT7260_ts_dev_pm_ops, +#endif + }, + .probe = IT7260_ts_probe, + .remove = IT7260_ts_remove, + .id_table = IT7260_ts_id, +}; + +module_i2c_driver(IT7260_ts_driver); + +MODULE_DESCRIPTION("IT7260 Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/msg21xx_ts.c b/drivers/input/touchscreen/msg21xx_ts.c new file mode 100644 index 000000000000..4eb7fd4b1cc9 --- /dev/null +++ b/drivers/input/touchscreen/msg21xx_ts.c @@ -0,0 +1,1757 @@ +/* + * MStar MSG21XX touchscreen driver + * + * Copyright (c) 2006-2012 MStar Semiconductor, Inc. + * + * Copyright (C) 2012 Bruce Ding <bruce.ding@mstarsemi.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/timer.h> +#include <linux/gpio.h> + +#include <linux/sysfs.h> +#include <linux/init.h> +#include <linux/mutex.h> +#include <mach/gpio.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include <linux/syscalls.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/fcntl.h> +#include <linux/string.h> +#include <asm/unistd.h> +#include <linux/cdev.h> +#include <asm/uaccess.h> +#if defined(CONFIG_HAS_EARLYSUSPEND) +#include <linux/earlysuspend.h> +#endif +#include <linux/input.h> +#if defined(CONFIG_FB) +#include <linux/notifier.h> +#include <linux/fb.h> +#endif +#ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR +#include <linux/input/vir_ps.h> +#endif + +/*=============================================================*/ +// Macro Definition +/*=============================================================*/ + +#define TOUCH_DRIVER_DEBUG 0 +#if (TOUCH_DRIVER_DEBUG == 1) +#define DBG(fmt, arg...) pr_err(fmt, ##arg) //pr_info(fmt, ##arg) +#else +#define DBG(fmt, arg...) +#endif + +/*=============================================================*/ +// Constant Value & Variable Definition +/*=============================================================*/ + +#define U8 unsigned char +#define U16 unsigned short +#define U32 unsigned int +#define S8 signed char +#define S16 signed short +#define S32 signed int + +#define TOUCH_SCREEN_X_MIN (0) +#define TOUCH_SCREEN_Y_MIN (0) +/* + * Note. + * Please change the below touch screen resolution according to the touch panel that you are using. + */ +#define TOUCH_SCREEN_X_MAX (480) +#define TOUCH_SCREEN_Y_MAX (800) +/* + * Note. + * Please do not change the below setting. + */ +#define TPD_WIDTH (2048) +#define TPD_HEIGHT (2048) + +/* + * Note. + * Please change the below GPIO pin setting to follow the platform that you are using + */ +static int int_gpio = 1; +static int reset_gpio = 0; +#define MS_TS_MSG21XX_GPIO_RST reset_gpio +#define MS_TS_MSG21XX_GPIO_INT int_gpio +//---------------------------------------------------------------------// + +//#define SYSFS_AUTHORITY_CHANGE_FOR_CTS_TEST + +#ifdef SYSFS_AUTHORITY_CHANGE_FOR_CTS_TEST +#define SYSFS_AUTHORITY (0644) +#else +#define SYSFS_AUTHORITY (0777) +#endif + +#define FIRMWARE_AUTOUPDATE +#ifdef FIRMWARE_AUTOUPDATE +typedef enum { + SWID_START = 1, + SWID_TRULY = SWID_START, + SWID_NULL, +} SWID_ENUM; + +unsigned char MSG_FIRMWARE[1][33*1024] = +{ + { + #include "msg21xx_truly_update_bin.h" + } +}; +#endif + +#define CONFIG_TP_HAVE_KEY + +/* + * Note. + * If the below virtual key value definition are not consistent with those that defined in key layout file of platform, + * please change the below virtual key value to follow the platform that you are using. + */ +#ifdef CONFIG_TP_HAVE_KEY +#define TOUCH_KEY_MENU (139) //229 +#define TOUCH_KEY_HOME (172) //102 +#define TOUCH_KEY_BACK (158) +#define TOUCH_KEY_SEARCH (217) + +const U16 tp_key_array[] = {TOUCH_KEY_MENU, TOUCH_KEY_HOME, TOUCH_KEY_BACK, TOUCH_KEY_SEARCH}; +#define MAX_KEY_NUM (sizeof(tp_key_array)/sizeof(tp_key_array[0])) +#endif + +#define SLAVE_I2C_ID_DBBUS (0xC4>>1) +#define SLAVE_I2C_ID_DWI2C (0x4C>>1) + +#define DEMO_MODE_PACKET_LENGTH (8) +#define MAX_TOUCH_NUM (2) //5 + +#define TP_PRINT +#ifdef TP_PRINT +static int tp_print_proc_read(void); +static void tp_print_create_entry(void); +#endif + +static char *fw_version = NULL; // customer firmware version +static U16 fw_version_major = 0; +static U16 fw_version_minor = 0; +static U8 temp[94][1024]; +static U32 crc32_table[256]; +static int FwDataCnt = 0; +static U8 bFwUpdating = 0; +static struct class *firmware_class = NULL; +static struct device *firmware_cmd_dev = NULL; + +static struct i2c_client *i2c_client = NULL; + +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +static struct notifier_block msg21xx_fb_notif; +#elif defined (CONFIG_HAS_EARLYSUSPEND) +static struct early_suspend mstar_ts_early_suspend; +#endif + +#ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR +static U8 bEnableTpProximity = 0; +static U8 bFaceClosingTp = 0; +#endif +static U8 bTpInSuspend = 0; + +static int irq_msg21xx = -1; +static struct work_struct msg21xx_wk; +static struct mutex msg21xx_mutex; +static struct input_dev *input_dev = NULL; + +/*=============================================================*/ +// Data Type Definition +/*=============================================================*/ + +typedef struct +{ + U16 x; + U16 y; +} touchPoint_t; + +/// max 80+1+1 = 82 bytes +typedef struct +{ + touchPoint_t point[MAX_TOUCH_NUM]; + U8 count; + U8 keycode; +} touchInfo_t; + +enum i2c_speed +{ + I2C_SLOW = 0, + I2C_NORMAL = 1, /* Enable erasing/writing for 10 msec. */ + I2C_FAST = 2, /* Disable EWENB before 10 msec timeout. */ +}; + +typedef enum +{ + EMEM_ALL = 0, + EMEM_MAIN, + EMEM_INFO, +} EMEM_TYPE_t; + +/*=============================================================*/ +// Function Definition +/*=============================================================*/ + +/// CRC +static U32 _CRC_doReflect(U32 ref, S8 ch) +{ + U32 value = 0; + U32 i = 0; + + for (i = 1; i < (ch + 1); i ++) + { + if (ref & 1) + { + value |= 1 << (ch - i); + } + ref >>= 1; + } + + return value; +} + +U32 _CRC_getValue(U32 text, U32 prevCRC) +{ + U32 ulCRC = prevCRC; + + ulCRC = (ulCRC >> 8) ^ crc32_table[(ulCRC & 0xFF) ^ text]; + + return ulCRC; +} + +static void _CRC_initTable(void) +{ + U32 magic_number = 0x04c11db7; + U32 i, j; + + for (i = 0; i <= 0xFF; i ++) + { + crc32_table[i] = _CRC_doReflect (i, 8) << 24; + for (j = 0; j < 8; j ++) + { + crc32_table[i] = (crc32_table[i] << 1) ^ (crc32_table[i] & (0x80000000L) ? magic_number : 0); + } + crc32_table[i] = _CRC_doReflect(crc32_table[i], 32); + } +} + +static void reset_hw(void) +{ + DBG("reset_hw()\n"); + + gpio_direction_output(MS_TS_MSG21XX_GPIO_RST, 1); + gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 0); + mdelay(100); /* Note that the RST must be in LOW 10ms at least */ + gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 1); + mdelay(100); /* Enable the interrupt service thread/routine for INT after 50ms */ +} + +static int read_i2c_seq(U8 addr, U8* buf, U16 size) +{ + int rc = 0; + struct i2c_msg msgs[] = + { + { + .addr = addr, + .flags = I2C_M_RD, // read flag + .len = size, + .buf = buf, + }, + }; + + /* If everything went ok (i.e. 1 msg transmitted), return #bytes + transmitted, else error code. */ + if (i2c_client != NULL) + { + rc = i2c_transfer(i2c_client->adapter, msgs, 1); + if (rc < 0) + { + DBG("read_i2c_seq() error %d\n", rc); + } + } + else + { + DBG("i2c_client is NULL\n"); + } + + return rc; +} + +static int write_i2c_seq(U8 addr, U8* buf, U16 size) +{ + int rc = 0; + struct i2c_msg msgs[] = + { + { + .addr = addr, + .flags = 0, // if read flag is undefined, then it means write flag. + .len = size, + .buf = buf, + }, + }; + + /* If everything went ok (i.e. 1 msg transmitted), return #bytes + transmitted, else error code. */ + if (i2c_client != NULL) + { + rc = i2c_transfer(i2c_client->adapter, msgs, 1); + if ( rc < 0 ) + { + DBG("write_i2c_seq() error %d\n", rc); + } + } + else + { + DBG("i2c_client is NULL\n"); + } + + return rc; +} + +static U16 read_reg(U8 bank, U8 addr) +{ + U8 tx_data[3] = {0x10, bank, addr}; + U8 rx_data[2] = {0}; + + write_i2c_seq(SLAVE_I2C_ID_DBBUS, &tx_data[0], 3); + read_i2c_seq(SLAVE_I2C_ID_DBBUS, &rx_data[0], 2); + + return (rx_data[1] << 8 | rx_data[0]); +} + +static void write_reg(U8 bank, U8 addr, U16 data) +{ + U8 tx_data[5] = {0x10, bank, addr, data & 0xFF, data >> 8}; + write_i2c_seq(SLAVE_I2C_ID_DBBUS, &tx_data[0], 5); +} + +static void write_reg_8bit(U8 bank, U8 addr, U8 data) +{ + U8 tx_data[4] = {0x10, bank, addr, data}; + write_i2c_seq(SLAVE_I2C_ID_DBBUS, &tx_data[0], 4); +} + +void dbbusDWIICEnterSerialDebugMode(void) +{ + U8 data[5]; + + // Enter the Serial Debug Mode + data[0] = 0x53; + data[1] = 0x45; + data[2] = 0x52; + data[3] = 0x44; + data[4] = 0x42; + + write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 5); +} + +void dbbusDWIICStopMCU(void) +{ + U8 data[1]; + + // Stop the MCU + data[0] = 0x37; + + write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 1); +} + +void dbbusDWIICIICUseBus(void) +{ + U8 data[1]; + + // IIC Use Bus + data[0] = 0x35; + + write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 1); +} + +void dbbusDWIICIICReshape(void) +{ + U8 data[1]; + + // IIC Re-shape + data[0] = 0x71; + + write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 1); +} + +void dbbusDWIICIICNotUseBus(void) +{ + U8 data[1]; + + // IIC Not Use Bus + data[0] = 0x34; + + write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 1); +} + +void dbbusDWIICNotStopMCU(void) +{ + U8 data[1]; + + // Not Stop the MCU + data[0] = 0x36; + + write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 1); +} + +void dbbusDWIICExitSerialDebugMode(void) +{ + U8 data[1]; + + // Exit the Serial Debug Mode + data[0] = 0x45; + + write_i2c_seq(SLAVE_I2C_ID_DBBUS, data, 1); + + // Delay some interval to guard the next transaction + //udelay ( 200 ); // delay about 0.2ms +} + +//---------------------------------------------------------------------// + +static U8 get_ic_type(void) +{ + U8 ic_type = 0; + + reset_hw(); + dbbusDWIICEnterSerialDebugMode(); + dbbusDWIICStopMCU(); + dbbusDWIICIICUseBus(); + dbbusDWIICIICReshape(); + mdelay ( 300 ); + + // stop mcu + write_reg_8bit ( 0x0F, 0xE6, 0x01 ); + // disable watch dog + write_reg ( 0x3C, 0x60, 0xAA55 ); + // get ic type + ic_type = (0xff)&(read_reg(0x1E, 0xCC)); + + if (ic_type != 1 //msg2133 + && ic_type != 2 //msg21xxA + && ic_type != 3) //msg26xxM + { + ic_type = 0; + } + + reset_hw(); + + return ic_type; +} + +static int get_customer_firmware_version(void) +{ + U8 dbbus_tx_data[3] = {0}; + U8 dbbus_rx_data[4] = {0}; + int ret = 0; + + DBG("get_customer_firmware_version()\n"); + + dbbus_tx_data[0] = 0x53; + dbbus_tx_data[1] = 0x00; + dbbus_tx_data[2] = 0x2A; + mutex_lock(&msg21xx_mutex); + write_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_tx_data[0], 3); + read_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_rx_data[0], 4); + mutex_unlock(&msg21xx_mutex); + fw_version_major = (dbbus_rx_data[1]<<8) + dbbus_rx_data[0]; + fw_version_minor = (dbbus_rx_data[3]<<8) + dbbus_rx_data[2]; + + DBG("*** major = %d ***\n", fw_version_major); + DBG("*** minor = %d ***\n", fw_version_minor); + + if (fw_version == NULL) + { + fw_version = kzalloc(sizeof(char), GFP_KERNEL); + } + + sprintf(fw_version, "%03d%03d", fw_version_major, fw_version_minor); + + return ret; +} + +static int firmware_erase_c33 ( EMEM_TYPE_t emem_type ) +{ + // stop mcu + write_reg ( 0x0F, 0xE6, 0x0001 ); + + //disable watch dog + write_reg_8bit ( 0x3C, 0x60, 0x55 ); + write_reg_8bit ( 0x3C, 0x61, 0xAA ); + + // set PROGRAM password + write_reg_8bit ( 0x16, 0x1A, 0xBA ); + write_reg_8bit ( 0x16, 0x1B, 0xAB ); + + write_reg_8bit ( 0x16, 0x18, 0x80 ); + + if ( emem_type == EMEM_ALL ) + { + write_reg_8bit ( 0x16, 0x08, 0x10 ); //mark + } + + write_reg_8bit ( 0x16, 0x18, 0x40 ); + mdelay ( 10 ); + + // clear pce + write_reg_8bit ( 0x16, 0x18, 0x80 ); + + // erase trigger + if ( emem_type == EMEM_MAIN ) + { + write_reg_8bit ( 0x16, 0x0E, 0x04 ); //erase main + } + else + { + write_reg_8bit ( 0x16, 0x0E, 0x08 ); //erase all block + } + + return ( 1 ); +} + +static ssize_t firmware_update_c33 ( struct device *dev, struct device_attribute *attr, + const char *buf, size_t size, EMEM_TYPE_t emem_type ) +{ + U32 i, j; + U32 crc_main, crc_main_tp; + U32 crc_info, crc_info_tp; + U16 reg_data = 0; + int update_pass = 1; + + crc_main = 0xffffffff; + crc_info = 0xffffffff; + + reset_hw(); + dbbusDWIICEnterSerialDebugMode(); + dbbusDWIICStopMCU(); + dbbusDWIICIICUseBus(); + dbbusDWIICIICReshape(); + mdelay ( 300 ); + + //erase main + firmware_erase_c33 ( EMEM_MAIN ); + mdelay ( 1000 ); + + reset_hw(); + dbbusDWIICEnterSerialDebugMode(); + dbbusDWIICStopMCU(); + dbbusDWIICIICUseBus(); + dbbusDWIICIICReshape(); + mdelay ( 300 ); + + ///////////////////////// + // Program + ///////////////////////// + + //polling 0x3CE4 is 0x1C70 + if ( ( emem_type == EMEM_ALL ) || ( emem_type == EMEM_MAIN ) ) + { + do + { + reg_data = read_reg ( 0x3C, 0xE4 ); + } + while ( reg_data != 0x1C70 ); + } + + switch ( emem_type ) + { + case EMEM_ALL: + write_reg ( 0x3C, 0xE4, 0xE38F ); // for all-blocks + break; + case EMEM_MAIN: + write_reg ( 0x3C, 0xE4, 0x7731 ); // for main block + break; + case EMEM_INFO: + write_reg ( 0x3C, 0xE4, 0x7731 ); // for info block + + write_reg_8bit ( 0x0F, 0xE6, 0x01 ); + + write_reg_8bit ( 0x3C, 0xE4, 0xC5 ); + write_reg_8bit ( 0x3C, 0xE5, 0x78 ); + + write_reg_8bit ( 0x1E, 0x04, 0x9F ); + write_reg_8bit ( 0x1E, 0x05, 0x82 ); + + write_reg_8bit ( 0x0F, 0xE6, 0x00 ); + mdelay ( 100 ); + break; + } + + // polling 0x3CE4 is 0x2F43 + do + { + reg_data = read_reg ( 0x3C, 0xE4 ); + } + while ( reg_data != 0x2F43 ); + + // calculate CRC 32 + _CRC_initTable (); + + for ( i = 0; i < 32; i++ ) // total 32 KB : 2 byte per R/W + { + if ( i == 31 ) + { + temp[i][1014] = 0x5A; + temp[i][1015] = 0xA5; + + for ( j = 0; j < 1016; j++ ) + { + crc_main = _CRC_getValue ( temp[i][j], crc_main); + } + } + else + { + for ( j = 0; j < 1024; j++ ) + { + crc_main = _CRC_getValue ( temp[i][j], crc_main); + } + } + + //write_i2c_seq(SLAVE_I2C_ID_DWI2C, temp[i], 1024); + for (j = 0; j < 8; j++) + { + write_i2c_seq(SLAVE_I2C_ID_DWI2C, &temp[i][j*128], 128 ); + } + msleep (100); + + // polling 0x3CE4 is 0xD0BC + do + { + reg_data = read_reg ( 0x3C, 0xE4 ); + } + while ( reg_data != 0xD0BC ); + + write_reg ( 0x3C, 0xE4, 0x2F43 ); + } + + if ( ( emem_type == EMEM_ALL ) || ( emem_type == EMEM_MAIN ) ) + { + // write file done and check crc + write_reg ( 0x3C, 0xE4, 0x1380 ); + } + mdelay ( 10 ); + + if ( ( emem_type == EMEM_ALL ) || ( emem_type == EMEM_MAIN ) ) + { + // polling 0x3CE4 is 0x9432 + do + { + reg_data = read_reg ( 0x3C, 0xE4 ); + }while ( reg_data != 0x9432 ); + } + + crc_main = crc_main ^ 0xffffffff; + crc_info = crc_info ^ 0xffffffff; + + if ( ( emem_type == EMEM_ALL ) || ( emem_type == EMEM_MAIN ) ) + { + // CRC Main from TP + crc_main_tp = read_reg ( 0x3C, 0x80 ); + crc_main_tp = ( crc_main_tp << 16 ) | read_reg ( 0x3C, 0x82 ); + + // CRC Info from TP + crc_info_tp = read_reg ( 0x3C, 0xA0 ); + crc_info_tp = ( crc_info_tp << 16 ) | read_reg ( 0x3C, 0xA2 ); + } + + update_pass = 1; + if ( ( emem_type == EMEM_ALL ) || ( emem_type == EMEM_MAIN ) ) + { + if ( crc_main_tp != crc_main ) + update_pass = 0; + + /* + if ( crc_info_tp != crc_info ) + update_pass = 0; + */ + } + + if ( !update_pass ) + { + DBG( "update_C33 failed\n" ); + reset_hw(); + FwDataCnt = 0; + return 0; + } + + DBG( "update_C33 OK\n" ); + reset_hw(); + FwDataCnt = 0; + return size; +} + +#ifdef FIRMWARE_AUTOUPDATE +unsigned short main_sw_id = 0x7FF, info_sw_id = 0x7FF; +U32 bin_conf_crc32 = 0; + +static U32 _CalMainCRC32(void) +{ + U32 ret=0; + U16 reg_data=0; + + reset_hw(); + + dbbusDWIICEnterSerialDebugMode(); + dbbusDWIICStopMCU(); + dbbusDWIICIICUseBus(); + dbbusDWIICIICReshape(); + msleep ( 100 ); + + //Stop MCU + write_reg ( 0x0F, 0xE6, 0x0001 ); + + // Stop Watchdog + write_reg_8bit ( 0x3C, 0x60, 0x55 ); + write_reg_8bit ( 0x3C, 0x61, 0xAA ); + + //cmd + write_reg ( 0x3C, 0xE4, 0xDF4C ); + write_reg ( 0x1E, 0x04, 0x7d60 ); + // TP SW reset + write_reg ( 0x1E, 0x04, 0x829F ); + + //MCU run + write_reg ( 0x0F, 0xE6, 0x0000 ); + + //polling 0x3CE4 + do + { + reg_data = read_reg ( 0x3C, 0xE4 ); + }while ( reg_data != 0x9432 ); + + // Cal CRC Main from TP + ret = read_reg ( 0x3C, 0x80 ); + ret = ( ret << 16 ) | read_reg ( 0x3C, 0x82 ); + + DBG("[21xxA]:Current main crc32=0x%x\n",ret); + return (ret); +} + +static void _ReadBinConfig ( void ) +{ + U8 dbbus_tx_data[5]={0}; + U8 dbbus_rx_data[4]={0}; + U16 reg_data=0; + + reset_hw(); + + dbbusDWIICEnterSerialDebugMode(); + dbbusDWIICStopMCU(); + dbbusDWIICIICUseBus(); + dbbusDWIICIICReshape(); + msleep ( 100 ); + + //Stop MCU + write_reg ( 0x0F, 0xE6, 0x0001 ); + + // Stop Watchdog + write_reg_8bit ( 0x3C, 0x60, 0x55 ); + write_reg_8bit ( 0x3C, 0x61, 0xAA ); + + //cmd + write_reg ( 0x3C, 0xE4, 0xA4AB ); + write_reg ( 0x1E, 0x04, 0x7d60 ); + + // TP SW reset + write_reg ( 0x1E, 0x04, 0x829F ); + + //MCU run + write_reg ( 0x0F, 0xE6, 0x0000 ); + + //polling 0x3CE4 + do + { + reg_data = read_reg ( 0x3C, 0xE4 ); + } + while ( reg_data != 0x5B58 ); + + dbbus_tx_data[0] = 0x72; + dbbus_tx_data[1] = 0x7F; + dbbus_tx_data[2] = 0x55; + dbbus_tx_data[3] = 0x00; + dbbus_tx_data[4] = 0x04; + write_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_tx_data[0], 5 ); + read_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_rx_data[0], 4 ); + if ((dbbus_rx_data[0]>=0x30 && dbbus_rx_data[0]<=0x39) + &&(dbbus_rx_data[1]>=0x30 && dbbus_rx_data[1]<=0x39) + &&(dbbus_rx_data[2]>=0x31 && dbbus_rx_data[2]<=0x39)) + { + main_sw_id = (dbbus_rx_data[0]-0x30)*100+(dbbus_rx_data[1]-0x30)*10+(dbbus_rx_data[2]-0x30); + } + + dbbus_tx_data[0] = 0x72; + dbbus_tx_data[1] = 0x7F; + dbbus_tx_data[2] = 0xFC; + dbbus_tx_data[3] = 0x00; + dbbus_tx_data[4] = 0x04; + write_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_tx_data[0], 5 ); + read_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_rx_data[0], 4 ); + bin_conf_crc32 = dbbus_rx_data[0]; + bin_conf_crc32 = (bin_conf_crc32<<8)|dbbus_rx_data[1]; + bin_conf_crc32 = (bin_conf_crc32<<8)|dbbus_rx_data[2]; + bin_conf_crc32 = (bin_conf_crc32<<8)|dbbus_rx_data[3]; + + dbbus_tx_data[0] = 0x72; + dbbus_tx_data[1] = 0x83; + dbbus_tx_data[2] = 0x00; + dbbus_tx_data[3] = 0x00; + dbbus_tx_data[4] = 0x04; + write_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_tx_data[0], 5 ); + read_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_rx_data[0], 4 ); + if ((dbbus_rx_data[0]>=0x30 && dbbus_rx_data[0]<=0x39) + &&(dbbus_rx_data[1]>=0x30 && dbbus_rx_data[1]<=0x39) + &&(dbbus_rx_data[2]>=0x31 && dbbus_rx_data[2]<=0x39)) + { + info_sw_id = (dbbus_rx_data[0]-0x30)*100+(dbbus_rx_data[1]-0x30)*10+(dbbus_rx_data[2]-0x30); + } + + DBG("[21xxA]:main_sw_id = %d, info_sw_id = %d, bin_conf_crc32=0x%x\n", main_sw_id, info_sw_id, bin_conf_crc32); +} + +static int fwAutoUpdate(void *unused) +{ + int time = 0; + ssize_t ret = 0; + + for (time = 0; time < 5; time++) + { + DBG("fwAutoUpdate time = %d\n",time); + ret = firmware_update_c33(NULL, NULL, NULL, 1, EMEM_MAIN); + if (ret == 1) + { + DBG("AUTO_UPDATE OK!!!"); + break; + } + } + if (time == 5) + { + DBG("AUTO_UPDATE failed!!!"); + } + enable_irq(irq_msg21xx); + return 0; +} +#endif + +//------------------------------------------------------------------------------// +static ssize_t firmware_update_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + DBG("*** firmware_update_show() fw_version = %s ***\n", fw_version); + + return sprintf(buf, "%s\n", fw_version); +} + +static ssize_t firmware_update_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + bFwUpdating = 1; + disable_irq(irq_msg21xx); + + DBG("*** update fw size = %d ***\n", FwDataCnt); + size = firmware_update_c33 (dev, attr, buf, size, EMEM_MAIN); + + enable_irq(irq_msg21xx); + bFwUpdating = 0; + + return size; +} + +static DEVICE_ATTR(update, SYSFS_AUTHORITY, firmware_update_show, firmware_update_store); + +static ssize_t firmware_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + DBG("*** firmware_version_show() fw_version = %s ***\n", fw_version); + + return sprintf(buf, "%s\n", fw_version); +} + +static ssize_t firmware_version_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + get_customer_firmware_version(); + + DBG("*** firmware_version_store() fw_version = %s ***\n", fw_version); + + return size; +} + +static DEVICE_ATTR(version, SYSFS_AUTHORITY, firmware_version_show, firmware_version_store); + +static ssize_t firmware_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + DBG("*** firmware_data_show() FwDataCnt = %d ***\n", FwDataCnt); + + return FwDataCnt; +} + +static ssize_t firmware_data_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int count = size / 1024; + int i; + + for (i = 0; i < count; i ++) + { + memcpy(temp[FwDataCnt], buf+(i*1024), 1024); + + FwDataCnt ++; + } + + DBG("***FwDataCnt = %d ***\n", FwDataCnt); + + if (buf != NULL) + { + DBG("*** buf[0] = %c ***\n", buf[0]); + } + + return size; +} + +static DEVICE_ATTR(data, SYSFS_AUTHORITY, firmware_data_show, firmware_data_store); + +#ifdef TP_PRINT +static ssize_t tp_print_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + tp_print_proc_read(); + + return sprintf(buf, "%d\n", bTpInSuspend); +} + +static ssize_t tp_print_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + DBG("*** tp_print_store() ***\n"); + + return size; +} + +static DEVICE_ATTR(tpp, SYSFS_AUTHORITY, tp_print_show, tp_print_store); +#endif + +//------------------------------------------------------------------------------// +#ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR +static void _msg_enable_proximity(void) +{ + U8 tx_data[4] = {0}; + + DBG("_msg_enable_proximity!"); + tx_data[0] = 0x52; + tx_data[1] = 0x00; + tx_data[2] = 0x47; + tx_data[3] = 0xa0; + mutex_lock(&msg21xx_mutex); + write_i2c_seq(SLAVE_I2C_ID_DWI2C, &tx_data[0], 4); + mutex_unlock(&msg21xx_mutex); + + bEnableTpProximity = 1; +} + +static void _msg_disable_proximity(void) +{ + U8 tx_data[4] = {0}; + + DBG("_msg_disable_proximity!"); + tx_data[0] = 0x52; + tx_data[1] = 0x00; + tx_data[2] = 0x47; + tx_data[3] = 0xa1; + mutex_lock(&msg21xx_mutex); + write_i2c_seq(SLAVE_I2C_ID_DWI2C, &tx_data[0], 4); + mutex_unlock(&msg21xx_mutex); + + bEnableTpProximity = 0; + bFaceClosingTp = 0; +} + +void tsps_msg21xx_enable(int en) +{ + if (en) + { + _msg_enable_proximity(); + } + else + { + _msg_disable_proximity(); + } +} + +int tsps_msg21xx_data(void) +{ + return bFaceClosingTp; +} +#endif + +static U8 calculate_checksum(U8 *msg, S32 length) +{ + S32 Checksum = 0; + S32 i; + + for (i = 0; i < length; i ++) + { + Checksum += msg[i]; + } + + return (U8)((-Checksum) & 0xFF); +} + +static S32 parse_info(touchInfo_t *info) +{ + U8 data[DEMO_MODE_PACKET_LENGTH] = {0}; + U8 checksum = 0; + U32 x = 0, y = 0; + U32 x2 = 0, y2 = 0; + U32 delta_x = 0, delta_y = 0; + + mutex_lock(&msg21xx_mutex); + read_i2c_seq(SLAVE_I2C_ID_DWI2C, &data[0], DEMO_MODE_PACKET_LENGTH); + mutex_unlock(&msg21xx_mutex); + checksum = calculate_checksum(&data[0], (DEMO_MODE_PACKET_LENGTH-1)); + DBG("check sum: [%x] == [%x]? \n", data[DEMO_MODE_PACKET_LENGTH-1], checksum); + + if(data[DEMO_MODE_PACKET_LENGTH-1] != checksum) + { + DBG("WRONG CHECKSUM\n"); + return -1; + } + + if(data[0] != 0x52) + { + DBG("WRONG HEADER\n"); + return -1; + } + + info->keycode = 0xFF; + if ((data[1] == 0xFF) && (data[2] == 0xFF) && (data[3] == 0xFF) && (data[4] == 0xFF) && (data[6] == 0xFF)) + { + if ((data[5] == 0xFF) || (data[5] == 0)) + { + info->keycode = 0xFF; + } + else if ((data[5] == 1) || (data[5] == 2) || (data[5] == 4) || (data[5] == 8)) + { + if (data[5] == 1) + { + info->keycode = 0; + } + else if (data[5] == 2) + { + info->keycode = 1; + } + else if (data[5] == 4) + { + info->keycode = 2; + } + else if (data[5] == 8) + { + info->keycode = 3; + } + } + #ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR + else if (bEnableTpProximity &&((data[5] == 0x80) || (data[5] == 0x40))) + { + if (data[5] == 0x80) + { + bFaceClosingTp = 1; + } + else if (data[5] == 0x40) + { + bFaceClosingTp = 0; + } + DBG("bEnableTpProximity=%d; bFaceClosingTp=%d; data[5]=%x;\n", bEnableTpProximity, bFaceClosingTp, data[5]); + return -1; + } + #endif + else + { + DBG("WRONG KEY\n"); + return -1; + } + } + else + { + x = (((data[1] & 0xF0 ) << 4) | data[2]); + y = ((( data[1] & 0x0F) << 8) | data[3]); + delta_x = (((data[4] & 0xF0) << 4 ) | data[5]); + delta_y = (((data[4] & 0x0F) << 8 ) | data[6]); + + if ((delta_x == 0) && (delta_y == 0)) + { + info->point[0].x = x * TOUCH_SCREEN_X_MAX / TPD_WIDTH; + info->point[0].y = y * TOUCH_SCREEN_Y_MAX/ TPD_HEIGHT; + info->count = 1; + } + else + { + if (delta_x > 2048) + { + delta_x -= 4096; + } + if (delta_y > 2048) + { + delta_y -= 4096; + } + x2 = (U32)((S16)x + (S16)delta_x); + y2 = (U32)((S16)y + (S16)delta_y); + info->point[0].x = x * TOUCH_SCREEN_X_MAX / TPD_WIDTH; + info->point[0].y = y * TOUCH_SCREEN_Y_MAX/ TPD_HEIGHT; + info->point[1].x = x2 * TOUCH_SCREEN_X_MAX / TPD_WIDTH; + info->point[1].y = y2 * TOUCH_SCREEN_Y_MAX/ TPD_HEIGHT; + info->count = 2; + } + } + + return 0; +} + +static void touch_driver_touch_pressed(int x, int y) +{ + DBG("point touch pressed"); + + input_report_key(input_dev, BTN_TOUCH, 1); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_mt_sync(input_dev); +} + +static void touch_driver_touch_released(void) +{ + DBG("point touch released"); + + input_report_key(input_dev, BTN_TOUCH, 0); + input_mt_sync(input_dev); +} + +/* read data through I2C then report data to input sub-system when interrupt occurred */ +void touch_driver_do_work(struct work_struct *work) +{ + touchInfo_t info; + int i = 0; + static int last_keycode = 0xFF; + static int last_count = 0; + + DBG("touch_driver_do_work()\n"); + + memset(&info, 0x0, sizeof(info)); + if (0 == parse_info(&info)) + { + #ifdef CONFIG_TP_HAVE_KEY + if (info.keycode != 0xFF) //key touch pressed + { + DBG("touch_driver_do_work() info.keycode=%x, last_keycode=%x, tp_key_array[%d]=%d\n", info.keycode, last_keycode, info.keycode, tp_key_array[info.keycode]); + if (info.keycode < MAX_KEY_NUM) + { + if (info.keycode != last_keycode) + { + DBG("key touch pressed"); + + input_report_key(input_dev, BTN_TOUCH, 1); + input_report_key(input_dev, tp_key_array[info.keycode], 1); + + last_keycode = info.keycode; + } + else + { + /// pass duplicate key-pressing + DBG("REPEATED KEY\n"); + } + } + else + { + DBG("WRONG KEY\n"); + } + } + else //key touch released + { + if (last_keycode != 0xFF) + { + DBG("key touch released"); + + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_key(input_dev, tp_key_array[last_keycode], 0); + + last_keycode = 0xFF; + } + } + #endif //CONFIG_TP_HAVE_KEY + + if (info.count > 0) //point touch pressed + { + for (i = 0; i < info.count; i ++) + { + touch_driver_touch_pressed(info.point[i].x, info.point[i].y); + } + last_count = info.count; + } + else if (last_count > 0) //point touch released + { + touch_driver_touch_released(); + last_count = 0; + } + + input_sync(input_dev); + } + + enable_irq(irq_msg21xx); +} + +/* The interrupt service routine will be triggered when interrupt occurred */ +irqreturn_t touch_driver_isr(int irq, void *dev_id) +{ + DBG("touch_driver_isr()\n"); + + disable_irq_nosync(irq_msg21xx); + schedule_work(&msg21xx_wk); + + return IRQ_HANDLED; +} + +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + + if (evdata && evdata->data && event == FB_EVENT_BLANK ) + { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) + { + if (bTpInSuspend) + { + gpio_direction_output(MS_TS_MSG21XX_GPIO_RST, 1); + mdelay(10); + gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 0); + mdelay(10); + gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 1); + mdelay(200); + + touch_driver_touch_released(); + input_sync(input_dev); + + enable_irq(irq_msg21xx); + } + bTpInSuspend = 0; + } + else if (*blank == FB_BLANK_POWERDOWN) + { + if (bFwUpdating) + { + DBG("suspend bFwUpdating=%d\n", bFwUpdating); + return 0; + } + + #ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR + if (bEnableTpProximity) + { + DBG("suspend bEnableTpProximity=%d\n", bEnableTpProximity); + return 0; + } + #endif + + if (bTpInSuspend == 0) + { + disable_irq(irq_msg21xx); + gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 0); + } + bTpInSuspend = 1; + } + } + + return 0; +} +#endif + +#ifdef CONFIG_HAS_EARLYSUSPEND +void touch_driver_early_suspend(struct early_suspend *p) +{ + DBG("touch_driver_early_suspend()\n"); + + if (bFwUpdating) + { + DBG("suspend bFwUpdating=%d\n", bFwUpdating); + return; + } + +#ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR + if (bEnableTpProximity) + { + DBG("suspend bEnableTpProximity=%d\n", bEnableTpProximity); + return; + } +#endif + + if (bTpInSuspend == 0) + { + disable_irq(irq_msg21xx); + gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 0); + } + bTpInSuspend = 1; +} + +void touch_driver_early_resume(struct early_suspend *p) +{ + DBG("touch_driver_early_resume() bTpInSuspend=%d\n", bTpInSuspend); + + if (bTpInSuspend) + { + gpio_direction_output(MS_TS_MSG21XX_GPIO_RST, 1); + mdelay(10); + gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 0); + mdelay(10); + gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 1); + mdelay(200); + + touch_driver_touch_released(); + input_sync(input_dev); + + enable_irq(irq_msg21xx); + } + bTpInSuspend = 0; +} +#endif + +/* probe function is used for matching and initializing input device */ +static int touch_driver_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ +#ifdef FIRMWARE_AUTOUPDATE + unsigned short update_bin_major = 0, update_bin_minor = 0; + int i, update_flag = 0; +#endif + int ret = 0; + + if (input_dev != NULL) + { + DBG("input device has found\n"); + return -1; + } + + DBG("*** %s ***\n", __FUNCTION__); + + i2c_client = client; + + ret = gpio_request(MS_TS_MSG21XX_GPIO_RST, "reset"); + if (ret < 0) + { + pr_err("*** Failed to request GPIO %d, error %d ***\n", MS_TS_MSG21XX_GPIO_RST, ret); + goto err0; + } + + // power on TP + gpio_direction_output(MS_TS_MSG21XX_GPIO_RST, 1); + mdelay(100); + gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 0); + mdelay(10); + gpio_set_value(MS_TS_MSG21XX_GPIO_RST, 1); + mdelay(200); + if (0 == get_ic_type()) + { + pr_err("the currnet ic is not Mstar\n"); + ret = -1; + goto err0; + } + + mutex_init(&msg21xx_mutex); + + /* allocate an input device */ + input_dev = input_allocate_device(); + if (!input_dev) + { + ret = -ENOMEM; + pr_err("*** input device allocation failed ***\n"); + goto err1; + } + + input_dev->name = client->name; + input_dev->phys = "I2C"; + input_dev->dev.parent = &client->dev; + input_dev->id.bustype = BUS_I2C; + + /* set the supported event type for input device */ + set_bit(EV_ABS, input_dev->evbit); + set_bit(EV_SYN, input_dev->evbit); + set_bit(EV_KEY, input_dev->evbit); + set_bit(BTN_TOUCH, input_dev->keybit); + set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + +#ifdef CONFIG_TP_HAVE_KEY + { + int i; + for (i = 0; i < MAX_KEY_NUM; i ++) + { + input_set_capability(input_dev, EV_KEY, tp_key_array[i]); + } + } +#endif + + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 2, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, TOUCH_SCREEN_X_MIN, TOUCH_SCREEN_X_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, TOUCH_SCREEN_Y_MIN, TOUCH_SCREEN_Y_MAX, 0, 0); + + /* register the input device to input sub-system */ + ret = input_register_device(input_dev); + if (ret < 0) + { + pr_err("*** Unable to register ms-touchscreen input device ***\n"); + goto err1; + } + + /* set sysfs for firmware */ + firmware_class = class_create(THIS_MODULE, "ms-touchscreen-msg20xx"); //client->name + if (IS_ERR(firmware_class)) + pr_err("Failed to create class(firmware)!\n"); + + firmware_cmd_dev = device_create(firmware_class, NULL, 0, NULL, "device"); + if (IS_ERR(firmware_cmd_dev)) + pr_err("Failed to create device(firmware_cmd_dev)!\n"); + + // version + if (device_create_file(firmware_cmd_dev, &dev_attr_version) < 0) + pr_err("Failed to create device file(%s)!\n", dev_attr_version.attr.name); + // update + if (device_create_file(firmware_cmd_dev, &dev_attr_update) < 0) + pr_err("Failed to create device file(%s)!\n", dev_attr_update.attr.name); + // data + if (device_create_file(firmware_cmd_dev, &dev_attr_data) < 0) + pr_err("Failed to create device file(%s)!\n", dev_attr_data.attr.name); + +#ifdef TP_PRINT + tp_print_create_entry(); +#endif + + dev_set_drvdata(firmware_cmd_dev, NULL); + + /* initialize the work queue */ + INIT_WORK(&msg21xx_wk, touch_driver_do_work); + + ret = gpio_request(MS_TS_MSG21XX_GPIO_INT, "interrupt"); + if (ret < 0) + { + pr_err("*** Failed to request GPIO %d, error %d ***\n", MS_TS_MSG21XX_GPIO_INT, ret); + goto err2; + } + gpio_direction_input(MS_TS_MSG21XX_GPIO_INT); + gpio_set_value(MS_TS_MSG21XX_GPIO_INT, 1); + + irq_msg21xx = gpio_to_irq(MS_TS_MSG21XX_GPIO_INT); + + /* request an irq and register the isr */ + ret = request_irq(irq_msg21xx, touch_driver_isr, IRQF_TRIGGER_RISING, "msg21xx", NULL); + if (ret != 0) + { + pr_err("*** Unable to claim irq %d; error %d ***\n", MS_TS_MSG21XX_GPIO_INT, ret); + goto err3; + } + + disable_irq(irq_msg21xx); + +#if defined(CONFIG_FB) + msg21xx_fb_notif.notifier_call = fb_notifier_callback; + ret = fb_register_client(&msg21xx_fb_notif); +#elif defined (CONFIG_HAS_EARLYSUSPEND) + mstar_ts_early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN; + mstar_ts_early_suspend.suspend = touch_driver_early_suspend; + mstar_ts_early_suspend.resume = touch_driver_early_resume; + register_early_suspend(&mstar_ts_early_suspend); +#endif + +#ifdef CONFIG_TOUCHSCREEN_PROXIMITY_SENSOR + tsps_assist_register_callback("msg21xx", &tsps_msg21xx_enable, &tsps_msg21xx_data); +#endif + +#ifdef FIRMWARE_AUTOUPDATE + get_customer_firmware_version(); + _ReadBinConfig(); + + if (main_sw_id == info_sw_id) + { + if (_CalMainCRC32() == bin_conf_crc32) + { + if ((main_sw_id >= SWID_START) && (main_sw_id < SWID_NULL)) + { + update_bin_major= (MSG_FIRMWARE[main_sw_id-SWID_START][0x7f4f] << 8) + MSG_FIRMWARE[main_sw_id-SWID_START][0x7f4e]; + update_bin_minor= (MSG_FIRMWARE[main_sw_id-SWID_START][0x7f51] << 8) + MSG_FIRMWARE[main_sw_id-SWID_START][0x7f50]; + + //check upgrading + if ((update_bin_major == fw_version_major) && (update_bin_minor > fw_version_minor)) + { + update_flag = 1; + } + } + DBG("MAIN sw_id=%d,update_flag=%d,update_bin_major=%d,update_bin_minor=%d\n",main_sw_id,update_flag,update_bin_major,update_bin_minor); + } + else + { + if ((info_sw_id >= SWID_START) && (info_sw_id < SWID_NULL)) + { + update_bin_major= (MSG_FIRMWARE[info_sw_id-SWID_START][0x7f4f] << 8) + MSG_FIRMWARE[info_sw_id-SWID_START][0x7f4e]; + update_bin_minor= (MSG_FIRMWARE[info_sw_id-SWID_START][0x7f51] << 8) + MSG_FIRMWARE[info_sw_id-SWID_START][0x7f50]; + update_flag = 1; + } + DBG("INFO1 sw_id=%d,update_flag=%d,update_bin_major=%d,update_bin_minor=%d\n",info_sw_id,update_flag,update_bin_major,update_bin_minor); + } + } + else + { + if ((info_sw_id >= SWID_START) && (info_sw_id < SWID_NULL)) + { + update_bin_major= (MSG_FIRMWARE[info_sw_id-SWID_START][0x7f4f] << 8) + MSG_FIRMWARE[info_sw_id-SWID_START][0x7f4e]; + update_bin_minor= (MSG_FIRMWARE[info_sw_id-SWID_START][0x7f51] << 8) + MSG_FIRMWARE[info_sw_id-SWID_START][0x7f50]; + update_flag = 1; + } + DBG("INFO2 sw_id=%d,update_flag=%d,update_bin_major=%d,update_bin_minor=%d\n",info_sw_id,update_flag,update_bin_major,update_bin_minor); + } + + if (update_flag == 1) + { + DBG("MSG21XX_fw_auto_update begin....\n"); + //transfer data + for (i = 0; i < 33; i++) + { + firmware_data_store(NULL, NULL, &(MSG_FIRMWARE[info_sw_id-SWID_START][i*1024]), 1024); + } + + kthread_run(fwAutoUpdate, 0, "MSG21XX_fw_auto_update"); + DBG("*** mstar touch screen registered ***\n"); + return 0; + } + + reset_hw(); +#endif + + DBG("*** mstar touch screen registered ***\n"); + enable_irq(irq_msg21xx); + return 0; + +err3: + free_irq(irq_msg21xx, input_dev); + +err2: + gpio_free(MS_TS_MSG21XX_GPIO_INT); + +err1: + mutex_destroy(&msg21xx_mutex); + input_unregister_device(input_dev); + input_free_device(input_dev); + input_dev = NULL; + +err0: + gpio_free(MS_TS_MSG21XX_GPIO_RST); + + return ret; +} + +/* remove function is triggered when the input device is removed from input sub-system */ +static int touch_driver_remove(struct i2c_client *client) +{ + DBG("touch_driver_remove()\n"); + + free_irq(irq_msg21xx, input_dev); + gpio_free(MS_TS_MSG21XX_GPIO_INT); + gpio_free(MS_TS_MSG21XX_GPIO_RST); + input_unregister_device(input_dev); + mutex_destroy(&msg21xx_mutex); + + return 0; +} + +/* The I2C device list is used for matching I2C device and I2C device driver. */ +static const struct i2c_device_id touch_device_id[] = +{ + {"msg21xx", 0}, + {}, /* should not omitted */ +}; + +MODULE_DEVICE_TABLE(i2c, touch_device_id); + +static struct i2c_driver touch_device_driver = +{ + .driver = { + .name = "msg21xx", + .owner = THIS_MODULE, + }, + .probe = touch_driver_probe, + .remove = touch_driver_remove, + .id_table = touch_device_id, +}; + +static int __init touch_driver_init(void) +{ + int ret; + + /* register driver */ + ret = i2c_add_driver(&touch_device_driver); + if (ret < 0) + { + DBG("add touch_device_driver i2c driver failed.\n"); + return -ENODEV; + } + DBG("add touch_device_driver i2c driver.\n"); + + return ret; +} + +static void __exit touch_driver_exit(void) +{ + DBG("remove touch_device_driver i2c driver.\n"); + + i2c_del_driver(&touch_device_driver); +} + +#ifdef TP_PRINT +#include <linux/proc_fs.h> + +static U16 InfoAddr = 0x0F, PoolAddr = 0x10, TransLen = 256; +static U8 row, units, cnt; + +static int tp_print_proc_read(void) +{ + U16 i, j; + U16 left, offset = 0; + U8 dbbus_tx_data[3] = {0}; + U8 u8Data; + S16 s16Data; + S32 s32Data; + char *buf = NULL; + + left = cnt*row*units; + if ((bTpInSuspend == 0) && (InfoAddr != 0x0F) && (PoolAddr != 0x10) && (left > 0)) + { + buf = kmalloc(left, GFP_KERNEL); + if (buf != NULL) + { + printk("tpp: \n"); + + while (left > 0) + { + dbbus_tx_data[0] = 0x53; + dbbus_tx_data[1] = ((PoolAddr + offset) >> 8) & 0xFF; + dbbus_tx_data[2] = (PoolAddr + offset) & 0xFF; + mutex_lock(&msg21xx_mutex); + write_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_tx_data[0], 3); + read_i2c_seq(SLAVE_I2C_ID_DWI2C, &buf[offset], left > TransLen ? TransLen : left); + mutex_unlock(&msg21xx_mutex); + + if (left > TransLen) + { + left -= TransLen; + offset += TransLen; + } + else + { + left = 0; + } + } + + for (i = 0; i < cnt; i++) + { + printk("tpp: "); + for (j = 0; j < row; j++) + { + if (units == 1) + { + u8Data = buf[i*row*units + j*units]; + printk("%d\t", u8Data); + } + else if (units == 2) + { + s16Data = buf[i*row*units + j*units] + (buf[i*row*units + j*units + 1] << 8); + printk("%d\t", s16Data); + } + else if (units == 4) + { + s32Data = buf[i*row*units + j*units] + (buf[i*row*units + j*units + 1] << 8) + (buf[i*row*units + j*units + 2] << 16) + (buf[i*row*units + j*units + 3] << 24); + printk("%d\t", s32Data); + } + } + printk("\n"); + } + + kfree(buf); + } + } + + return 0; +} + +static void tp_print_create_entry(void) +{ + U8 dbbus_tx_data[3] = {0}; + U8 dbbus_rx_data[8] = {0}; + + dbbus_tx_data[0] = 0x53; + dbbus_tx_data[1] = 0x00; + dbbus_tx_data[2] = 0x58; + mutex_lock(&msg21xx_mutex); + write_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_tx_data[0], 3); + read_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_rx_data[0], 4); + mutex_unlock(&msg21xx_mutex); + InfoAddr = (dbbus_rx_data[1]<<8) + dbbus_rx_data[0]; + PoolAddr = (dbbus_rx_data[3]<<8) + dbbus_rx_data[2]; + printk("InfoAddr=0x%X\n", InfoAddr); + printk("PoolAddr=0x%X\n", PoolAddr); + + if ((InfoAddr != 0x0F) && (PoolAddr != 0x10)) + { + msleep(10); + dbbus_tx_data[0] = 0x53; + dbbus_tx_data[1] = (InfoAddr >> 8) & 0xFF; + dbbus_tx_data[2] = InfoAddr & 0xFF; + mutex_lock(&msg21xx_mutex); + write_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_tx_data[0], 3); + read_i2c_seq(SLAVE_I2C_ID_DWI2C, &dbbus_rx_data[0], 8); + mutex_unlock(&msg21xx_mutex); + + units = dbbus_rx_data[0]; + row = dbbus_rx_data[1]; + cnt = dbbus_rx_data[2]; + TransLen = (dbbus_rx_data[7]<<8) + dbbus_rx_data[6]; + printk("tpp: row=%d, units=%d\n", row, units); + printk("tpp: cnt=%d, TransLen=%d\n", cnt, TransLen); + + // tpp + if (device_create_file(firmware_cmd_dev, &dev_attr_tpp) < 0) + { + pr_err("Failed to create device file(%s)!\n", dev_attr_tpp.attr.name); + } + } +} +#endif + +module_init(touch_driver_init); +module_exit(touch_driver_exit); +MODULE_AUTHOR("MStar Semiconductor, Inc."); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index ce3f8713df3e..55eff5ae04e4 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -568,6 +568,28 @@ static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu, return NULL; } +static struct arm_smmu_master *find_smmu_master_by_sid( + struct arm_smmu_device *smmu, u32 sid) +{ + struct rb_node *next; + struct arm_smmu_master *master; + struct arm_smmu_master_cfg *cfg; + int i; + + next = rb_first(&smmu->masters); + for (; next; next = rb_next(next)) { + master = container_of(next, struct arm_smmu_master, node); + cfg = &master->cfg; + + for (i = 0; i < cfg->num_streamids; i++) { + if (cfg->streamids[i] == sid) + return master; + } + } + + return NULL; +} + static struct arm_smmu_master_cfg * find_smmu_master_cfg(struct device *dev) { @@ -1175,8 +1197,9 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev) bool fatal_asf; void __iomem *gr1_base; phys_addr_t phys_soft; - u32 frsynra; + u32 sid; bool non_fatal_fault = smmu_domain->non_fatal_faults; + struct arm_smmu_master *master; static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL, @@ -1231,7 +1254,9 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev) iova = far; phys_soft = arm_smmu_iova_to_phys(domain, iova); - frsynra = readl_relaxed(gr1_base + ARM_SMMU_GR1_CBFRSYNRA(cfg->cbndx)); + sid = readl_relaxed(gr1_base + ARM_SMMU_GR1_CBFRSYNRA(cfg->cbndx)); + sid &= 0xffff; + master = find_smmu_master_by_sid(smmu, sid); tmp = report_iommu_fault(domain, smmu->dev, iova, flags); if (!tmp || (tmp == -EBUSY)) { dev_dbg(smmu->dev, @@ -1246,6 +1271,9 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev) fsr); if (__ratelimit(&_rs)) { + dev_err(smmu->dev, "Context Fault for %s\n", + master ? master->of_node->name : "Unknown SID"); + dev_err(smmu->dev, "Unhandled context fault: iova=0x%08lx, fsr=0x%x, fsynr=0x%x, cb=%d\n", iova, fsr, fsynr, cfg->cbndx); @@ -1271,7 +1299,7 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev) dev_name(smmu->dev)); dev_err(smmu->dev, "hard iova-to-phys (ATOS)=%pa\n", &phys_atos); - dev_err(smmu->dev, "SID=0x%x\n", frsynra & 0xffff); + dev_err(smmu->dev, "SID=0x%x\n", sid); } ret = IRQ_NONE; resume = RESUME_TERMINATE; @@ -2820,10 +2848,16 @@ static struct iommu_group *arm_smmu_device_group(struct device *dev) struct iommu_group *group; int ret; - if (dev_is_pci(dev)) - group = pci_device_group(dev); - else - group = generic_device_group(dev); + /* + * We used to call pci_device_group here for dev_is_pci(dev) + * devices. However, that causes the root complex device to be + * placed in the same group as endpoint devices (and probably puts + * all endpoint devices in the same group as well), which makes + * things tricky in the DMA layer since we don't actually want to + * attach *everybody* in the group when one client calls attach. + * Instead, we'll just allocate a new group for everybody here. + */ + group = generic_device_group(dev); if (IS_ERR(group)) return group; diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c index c78178c0e1a1..a0227fd05939 100644 --- a/drivers/iommu/iommu-debug.c +++ b/drivers/iommu/iommu-debug.c @@ -29,6 +29,8 @@ #include <asm/cacheflush.h> #include <asm/dma-iommu.h> +#if defined(CONFIG_IOMMU_DEBUG_TRACKING) || defined(CONFIG_IOMMU_TESTS) + static const char *iommu_debug_attr_to_string(enum iommu_attr attr) { switch (attr) { @@ -74,6 +76,7 @@ static const char *iommu_debug_attr_to_string(enum iommu_attr attr) return "Unknown attr!"; } } +#endif #ifdef CONFIG_IOMMU_DEBUG_TRACKING @@ -457,6 +460,20 @@ static inline void iommu_debug_destroy_tracking(void) { } #ifdef CONFIG_IOMMU_TESTS +#ifdef CONFIG_64BIT + +#define kstrtoux kstrtou64 +#define kstrtox_from_user kstrtoll_from_user +#define kstrtosize_t kstrtoul + +#else + +#define kstrtoux kstrtou32 +#define kstrtox_from_user kstrtoint_from_user +#define kstrtosize_t kstrtouint + +#endif + static LIST_HEAD(iommu_debug_devices); static struct dentry *debugfs_tests_dir; static u32 iters_per_op = 1; @@ -549,10 +566,10 @@ DEFINE_SIMPLE_ATTRIBUTE(iommu_debug_nr_iters_ops, static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev, enum iommu_attr attrs[], void *attr_values[], int nattrs, - const unsigned long sizes[]) + const size_t sizes[]) { int i; - const unsigned long *sz; + const size_t *sz; struct iommu_domain *domain; struct bus_type *bus; unsigned long iova = 0x10000; @@ -593,7 +610,7 @@ static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev, seq_printf(s, "(average over %d iterations)\n", iters_per_op); seq_printf(s, "%8s %19s %16s\n", "size", "iommu_map", "iommu_unmap"); for (sz = sizes; *sz; ++sz) { - unsigned long size = *sz; + size_t size = *sz; size_t unmapped; u64 map_elapsed_ns = 0, unmap_elapsed_ns = 0; u64 map_elapsed_us = 0, unmap_elapsed_us = 0; @@ -625,8 +642,10 @@ static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev, unmap_elapsed_ns += timespec_to_ns(&diff); } - map_elapsed_ns /= iters_per_op; - unmap_elapsed_ns /= iters_per_op; + map_elapsed_ns = div_u64_rem(map_elapsed_ns, iters_per_op, + &map_elapsed_rem); + unmap_elapsed_ns = div_u64_rem(unmap_elapsed_ns, iters_per_op, + &unmap_elapsed_rem); map_elapsed_us = div_u64_rem(map_elapsed_ns, 1000, &map_elapsed_rem); @@ -642,7 +661,7 @@ static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev, seq_putc(s, '\n'); seq_printf(s, "%8s %19s %16s\n", "size", "iommu_map_sg", "iommu_unmap"); for (sz = sizes; *sz; ++sz) { - unsigned long size = *sz; + size_t size = *sz; size_t unmapped; u64 map_elapsed_ns = 0, unmap_elapsed_ns = 0; u64 map_elapsed_us = 0, unmap_elapsed_us = 0; @@ -683,8 +702,10 @@ static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev, unmap_elapsed_ns += timespec_to_ns(&diff); } - map_elapsed_ns /= iters_per_op; - unmap_elapsed_ns /= iters_per_op; + map_elapsed_ns = div_u64_rem(map_elapsed_ns, iters_per_op, + &map_elapsed_rem); + unmap_elapsed_ns = div_u64_rem(unmap_elapsed_ns, iters_per_op, + &unmap_elapsed_rem); map_elapsed_us = div_u64_rem(map_elapsed_ns, 1000, &map_elapsed_rem); @@ -709,7 +730,7 @@ out_domain_free: static int iommu_debug_profiling_show(struct seq_file *s, void *ignored) { struct iommu_debug_device *ddev = s->private; - const unsigned long sizes[] = { SZ_4K, SZ_64K, SZ_2M, SZ_1M * 12, + const size_t sizes[] = { SZ_4K, SZ_64K, SZ_2M, SZ_1M * 12, SZ_1M * 20, 0 }; enum iommu_attr attrs[] = { DOMAIN_ATTR_COHERENT_HTW_DISABLE, @@ -739,7 +760,7 @@ static const struct file_operations iommu_debug_profiling_fops = { static int iommu_debug_secure_profiling_show(struct seq_file *s, void *ignored) { struct iommu_debug_device *ddev = s->private; - const unsigned long sizes[] = { SZ_4K, SZ_64K, SZ_2M, SZ_1M * 12, + const size_t sizes[] = { SZ_4K, SZ_64K, SZ_2M, SZ_1M * 12, SZ_1M * 20, 0 }; enum iommu_attr attrs[] = { @@ -830,7 +851,7 @@ static int iommu_debug_profiling_fast_dma_api_show(struct seq_file *s, if (!virt) goto out; - mapping = arm_iommu_create_mapping(&platform_bus_type, 0, SZ_1G * 4ULL); + mapping = arm_iommu_create_mapping(&platform_bus_type, 0, SZ_1G * 4UL); if (!mapping) { seq_puts(s, "fast_smmu_create_mapping failed\n"); goto out_kfree; @@ -851,7 +872,7 @@ static int iommu_debug_profiling_fast_dma_api_show(struct seq_file *s, goto out_detach; } for (experiment = 0; experiment < 2; ++experiment) { - u64 map_avg = 0, unmap_avg = 0; + size_t map_avg = 0, unmap_avg = 0; for (i = 0; i < 10; ++i) { struct timespec tbefore, tafter, diff; @@ -888,7 +909,7 @@ static int iommu_debug_profiling_fast_dma_api_show(struct seq_file *s, i < 9 ? ", " : ""); } map_avg /= 10; - seq_printf(s, "] (avg: %llu)\n", map_avg); + seq_printf(s, "] (avg: %zu)\n", map_avg); seq_printf(s, "%13s %24s (ns): [", extra_labels[experiment], "dma_unmap_single_attrs"); @@ -898,7 +919,7 @@ static int iommu_debug_profiling_fast_dma_api_show(struct seq_file *s, i < 9 ? ", " : ""); } unmap_avg /= 10; - seq_printf(s, "] (avg: %llu)\n", unmap_avg); + seq_printf(s, "] (avg: %zu)\n", unmap_avg); } out_disable_config_clocks: @@ -1382,7 +1403,7 @@ static int __apply_to_new_mapping(struct seq_file *s, int ret = -EINVAL, fast = 1; phys_addr_t pt_phys; - mapping = arm_iommu_create_mapping(&platform_bus_type, 0, SZ_1G * 4ULL); + mapping = arm_iommu_create_mapping(&platform_bus_type, 0, SZ_1G * 4UL); if (!mapping) goto out; @@ -1631,7 +1652,7 @@ static ssize_t iommu_debug_atos_write(struct file *file, struct iommu_debug_device *ddev = file->private_data; dma_addr_t iova; - if (kstrtoll_from_user(ubuf, count, 0, &iova)) { + if (kstrtox_from_user(ubuf, count, 0, &iova)) { pr_err("Invalid format for iova\n"); ddev->iova = 0; return -EINVAL; @@ -1730,13 +1751,13 @@ static ssize_t iommu_debug_map_write(struct file *file, const char __user *ubuf, /* split up the words */ *comma1 = *comma2 = *comma3 = '\0'; - if (kstrtou64(buf, 0, &iova)) + if (kstrtoux(buf, 0, &iova)) goto invalid_format; - if (kstrtou64(comma1 + 1, 0, &phys)) + if (kstrtoux(comma1 + 1, 0, &phys)) goto invalid_format; - if (kstrtoul(comma2 + 1, 0, &size)) + if (kstrtosize_t(comma2 + 1, 0, &size)) goto invalid_format; if (kstrtoint(comma3 + 1, 0, &prot)) @@ -1802,10 +1823,10 @@ static ssize_t iommu_debug_unmap_write(struct file *file, /* split up the words */ *comma1 = '\0'; - if (kstrtou64(buf, 0, &iova)) + if (kstrtoux(buf, 0, &iova)) goto invalid_format; - if (kstrtoul(comma1 + 1, 0, &size)) + if (kstrtosize_t(comma1 + 1, 0, &size)) goto invalid_format; unmapped = iommu_unmap(ddev->domain, iova, size); diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 5118c2f32a4c..8ab502d80270 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -851,7 +851,8 @@ struct iommu_group *iommu_group_get_for_dev(struct device *dev) if (!group->default_domain) { group->default_domain = __iommu_domain_alloc(dev->bus, IOMMU_DOMAIN_DMA); - group->domain = group->default_domain; + if (!group->domain) + group->domain = group->default_domain; } ret = iommu_group_add_device(group, dev); diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 022473473971..190d294197a7 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -588,9 +588,6 @@ static int gic_populate_rdist(void) u64 offset = ptr - gic_data.redist_regions[i].redist_base; gic_data_rdist_rd_base() = ptr; gic_data_rdist()->phys_base = gic_data.redist_regions[i].phys_base + offset; - pr_info("CPU%d: found redistributor %lx region %d:%pa\n", - smp_processor_id(), mpidr, i, - &gic_data_rdist()->phys_base); return 0; } diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c index 786ffa822851..b39aa8084250 100644 --- a/drivers/leds/leds-qpnp-flash-v2.c +++ b/drivers/leds/leds-qpnp-flash-v2.c @@ -16,13 +16,20 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/of.h> +#include <linux/of_irq.h> #include <linux/of_gpio.h> #include <linux/gpio.h> #include <linux/regmap.h> #include <linux/platform_device.h> +#include <linux/interrupt.h> #include <linux/regulator/consumer.h> +#include <linux/leds-qpnp-flash.h> #include <linux/leds-qpnp-flash-v2.h> +#include "leds.h" +#define FLASH_LED_REG_LED_STATUS1(base) (base + 0x08) +#define FLASH_LED_REG_LED_STATUS2(base) (base + 0x09) +#define FLASH_LED_REG_INT_RT_STS(base) (base + 0x10) #define FLASH_LED_REG_SAFETY_TMR(base) (base + 0x40) #define FLASH_LED_REG_TGR_CURRENT(base) (base + 0x43) #define FLASH_LED_REG_MOD_CTRL(base) (base + 0x46) @@ -32,20 +39,40 @@ #define FLASH_LED_EN_LED_CTRL(base) (base + 0x4C) #define FLASH_LED_REG_HDRM_PRGM(base) (base + 0x4D) #define FLASH_LED_REG_HDRM_AUTO_MODE_CTRL(base) (base + 0x50) +#define FLASH_LED_REG_WARMUP_DELAY(base) (base + 0x51) #define FLASH_LED_REG_ISC_DELAY(base) (base + 0x52) +#define FLASH_LED_REG_VPH_DROOP_THRESHOLD(base) (base + 0x61) +#define FLASH_LED_REG_VPH_DROOP_DEBOUNCE(base) (base + 0x62) +#define FLASH_LED_REG_CURRENT_DERATE_EN(base) (base + 0x76) #define FLASH_LED_HDRM_MODE_PRGM_MASK GENMASK(7, 0) #define FLASH_LED_HDRM_VOL_MASK GENMASK(7, 4) #define FLASH_LED_CURRENT_MASK GENMASK(6, 0) #define FLASH_LED_ENABLE_MASK GENMASK(2, 0) #define FLASH_LED_SAFETY_TMR_MASK GENMASK(7, 0) -#define FLASH_LED_ISC_DELAY_MASK GENMASK(1, 0) +#define FLASH_LED_INT_RT_STS_MASK GENMASK(7, 0) +#define FLASH_LED_ISC_WARMUP_DELAY_MASK GENMASK(1, 0) +#define FLASH_LED_CURRENT_DERATE_EN_MASK GENMASK(2, 0) +#define FLASH_LED_VPH_DROOP_DEBOUNCE_MASK GENMASK(1, 0) +#define FLASH_LED_VPH_DROOP_HYSTERESIS_MASK GENMASK(5, 4) +#define FLASH_LED_VPH_DROOP_THRESHOLD_MASK GENMASK(2, 0) #define FLASH_LED_MOD_CTRL_MASK BIT(7) #define FLASH_LED_HW_SW_STROBE_SEL_MASK BIT(2) - -#define FLASH_LED_HEADROOM_AUTO_MODE_ENABLED true -#define FLASH_LED_ISC_DELAY_SHIFT 6 -#define FLASH_LED_ISC_DELAY_DEFAULT_US 3 +#define FLASH_LED_VPH_DROOP_FAULT_MASK BIT(4) + +#define VPH_DROOP_DEBOUNCE_US_TO_VAL(val_us) (val_us / 8) +#define VPH_DROOP_HYST_MV_TO_VAL(val_mv) (val_mv / 25) +#define VPH_DROOP_THRESH_MV_TO_VAL(val_mv) ((val_mv / 100) - 25) + +#define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6 +#define FLASH_LED_WARMUP_DELAY_DEFAULT 2 +#define FLASH_LED_ISC_DELAY_DEFAULT 3 +#define FLASH_LED_VPH_DROOP_DEBOUNCE_DEFAULT 2 +#define FLASH_LED_VPH_DROOP_HYST_DEFAULT 2 +#define FLASH_LED_VPH_DROOP_THRESH_DEFAULT 5 +#define FLASH_LED_VPH_DROOP_DEBOUNCE_MAX 3 +#define FLASH_LED_VPH_DROOP_HYST_MAX 3 +#define FLASH_LED_VPH_DROOP_THRESH_MAX 7 #define FLASH_LED_SAFETY_TMR_VAL_OFFSET 1 #define FLASH_LED_SAFETY_TMR_VAL_DIVISOR 10 #define FLASH_LED_SAFETY_TMR_ENABLE BIT(7) @@ -69,6 +96,9 @@ #define FLASH_LED_SAFETY_TMR_DISABLED 0x13 #define FLASH_LED_MIN_CURRENT_MA 25 +/* notifier call chain for flash-led irqs */ +static ATOMIC_NOTIFIER_HEAD(irq_notifier_list); + enum flash_led_type { FLASH_LED_TYPE_FLASH, FLASH_LED_TYPE_TORCH, @@ -125,9 +155,17 @@ struct flash_switch_data { * Flash LED configuration read from device tree */ struct flash_led_platform_data { - u8 isc_delay_us; - u8 hw_strobe_option; - bool hdrm_auto_mode_en; + int all_ramp_up_done_irq; + int all_ramp_down_done_irq; + int led_fault_irq; + u8 isc_delay; + u8 warmup_delay; + u8 current_derate_en_cfg; + u8 vph_droop_threshold; + u8 vph_droop_hysteresis; + u8 vph_droop_debounce; + u8 hw_strobe_option; + bool hdrm_auto_mode_en; }; /* @@ -147,19 +185,36 @@ struct qpnp_flash_led { }; static int +qpnp_flash_led_read(struct qpnp_flash_led *led, u16 addr, u8 *data) +{ + int rc; + uint val; + + rc = regmap_read(led->regmap, addr, &val); + if (rc < 0) + dev_err(&led->pdev->dev, "Unable to read from 0x%04X rc = %d\n", + addr, rc); + else + dev_dbg(&led->pdev->dev, "Read 0x%02X from addr 0x%04X\n", + val, addr); + + *data = (u8)val; + return rc; +} + +static int qpnp_flash_led_masked_write(struct qpnp_flash_led *led, u16 addr, u8 mask, - u8 val) + u8 val) { int rc; rc = regmap_update_bits(led->regmap, addr, mask, val); if (rc < 0) - dev_err(&led->pdev->dev, - "Unable to update bits from 0x%02X, rc = %d\n", - addr, rc); + dev_err(&led->pdev->dev, "Unable to update bits from 0x%04X, rc = %d\n", + addr, rc); else - dev_dbg(&led->pdev->dev, "Wrote 0x%02X to addr 0x%02X\n", - val, addr); + dev_dbg(&led->pdev->dev, "Wrote 0x%02X to addr 0x%04X\n", + val, addr); return rc; } @@ -195,7 +250,43 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_ISC_DELAY(led->base), - FLASH_LED_ISC_DELAY_MASK, led->pdata->isc_delay_us); + FLASH_LED_ISC_WARMUP_DELAY_MASK, + led->pdata->isc_delay); + if (rc < 0) + return rc; + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_WARMUP_DELAY(led->base), + FLASH_LED_ISC_WARMUP_DELAY_MASK, + led->pdata->warmup_delay); + if (rc < 0) + return rc; + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_CURRENT_DERATE_EN(led->base), + FLASH_LED_CURRENT_DERATE_EN_MASK, + led->pdata->current_derate_en_cfg); + if (rc < 0) + return rc; + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_VPH_DROOP_DEBOUNCE(led->base), + FLASH_LED_VPH_DROOP_DEBOUNCE_MASK, + led->pdata->vph_droop_debounce); + if (rc < 0) + return rc; + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_VPH_DROOP_THRESHOLD(led->base), + FLASH_LED_VPH_DROOP_THRESHOLD_MASK, + led->pdata->vph_droop_threshold); + if (rc < 0) + return rc; + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_VPH_DROOP_THRESHOLD(led->base), + FLASH_LED_VPH_DROOP_HYSTERESIS_MASK, + led->pdata->vph_droop_hysteresis); if (rc < 0) return rc; @@ -459,13 +550,21 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) return 0; } -int qpnp_flash_led_prepare(struct led_classdev *led_cdev, int options) +int qpnp_flash_led_prepare(struct led_trigger *trig, int options) { - struct flash_switch_data *snode = - container_of(led_cdev, struct flash_switch_data, cdev); - struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev); + struct led_classdev *led_cdev = trigger_to_lcdev(trig); + struct flash_switch_data *snode; + struct qpnp_flash_led *led; int rc, val = 0; + if (!led_cdev) { + pr_err("Invalid led_trigger provided\n"); + return -EINVAL; + } + + snode = container_of(led_cdev, struct flash_switch_data, cdev); + led = dev_get_drvdata(&snode->pdev->dev); + if (!(options & (ENABLE_REGULATOR | QUERY_MAX_CURRENT))) { dev_err(&led->pdev->dev, "Invalid options %d\n", options); return -EINVAL; @@ -521,6 +620,77 @@ static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev, spin_unlock(&led->lock); } +/* irq handler */ +static irqreturn_t qpnp_flash_led_irq_handler(int irq, void *_led) +{ + struct qpnp_flash_led *led = _led; + enum flash_led_irq_type irq_type = INVALID_IRQ; + int rc; + u8 irq_status, led_status1, led_status2; + + dev_dbg(&led->pdev->dev, "irq received, irq=%d\n", irq); + + rc = qpnp_flash_led_read(led, + FLASH_LED_REG_INT_RT_STS(led->base), &irq_status); + if (rc < 0) { + dev_err(&led->pdev->dev, "Failed to read interrupt status reg, rc=%d\n", + rc); + goto exit; + } + + if (irq == led->pdata->all_ramp_up_done_irq) + irq_type = ALL_RAMP_UP_DONE_IRQ; + else if (irq == led->pdata->all_ramp_down_done_irq) + irq_type = ALL_RAMP_DOWN_DONE_IRQ; + else if (irq == led->pdata->led_fault_irq) + irq_type = LED_FAULT_IRQ; + + if (irq_type == ALL_RAMP_UP_DONE_IRQ) + atomic_notifier_call_chain(&irq_notifier_list, + irq_type, NULL); + + if (irq_type == LED_FAULT_IRQ) { + rc = qpnp_flash_led_read(led, + FLASH_LED_REG_LED_STATUS1(led->base), &led_status1); + if (rc < 0) { + dev_err(&led->pdev->dev, "Failed to read led_status1 reg, rc=%d\n", + rc); + goto exit; + } + + rc = qpnp_flash_led_read(led, + FLASH_LED_REG_LED_STATUS2(led->base), &led_status2); + if (rc < 0) { + dev_err(&led->pdev->dev, "Failed to read led_status2 reg, rc=%d\n", + rc); + goto exit; + } + + if (led_status1) + dev_emerg(&led->pdev->dev, "led short/open fault detected! led_status1=%x\n", + led_status1); + + if (led_status2 & FLASH_LED_VPH_DROOP_FAULT_MASK) + dev_emerg(&led->pdev->dev, "led vph_droop fault detected!\n"); + } + + dev_dbg(&led->pdev->dev, "irq handled, irq_type=%x, irq_status=%x\n", + irq_type, irq_status); + +exit: + return IRQ_HANDLED; +} + +int qpnp_flash_led_register_irq_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&irq_notifier_list, nb); +} + +int qpnp_flash_led_unregister_irq_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&irq_notifier_list, nb); +} + static int qpnp_flash_led_regulator_setup(struct qpnp_flash_led *led, struct flash_switch_data *snode, bool on) { @@ -901,28 +1071,116 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, { int rc; u32 val; + bool short_circuit_det, open_circuit_det, vph_droop_det; - led->pdata->hdrm_auto_mode_en = FLASH_LED_HEADROOM_AUTO_MODE_ENABLED; led->pdata->hdrm_auto_mode_en = of_property_read_bool(node, "qcom,hdrm-auto-mode"); - led->pdata->isc_delay_us = FLASH_LED_ISC_DELAY_DEFAULT_US; - rc = of_property_read_u32(node, "qcom,isc-delay", &val); + led->pdata->isc_delay = FLASH_LED_ISC_DELAY_DEFAULT; + rc = of_property_read_u32(node, "qcom,isc-delay-us", &val); + if (!rc) { + led->pdata->isc_delay = + val >> FLASH_LED_ISC_WARMUP_DELAY_SHIFT; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, + "Unable to read ISC delay, rc=%d\n", rc); + return rc; + } + + led->pdata->warmup_delay = FLASH_LED_WARMUP_DELAY_DEFAULT; + rc = of_property_read_u32(node, "qcom,warmup-delay-us", &val); + if (!rc) { + led->pdata->warmup_delay = + val >> FLASH_LED_ISC_WARMUP_DELAY_SHIFT; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, + "Unable to read WARMUP delay, rc=%d\n", rc); + return rc; + } + + short_circuit_det = + of_property_read_bool(node, "qcom,short-circuit-det"); + open_circuit_det = of_property_read_bool(node, "qcom,open-circuit-det"); + vph_droop_det = of_property_read_bool(node, "qcom,vph-droop-det"); + led->pdata->current_derate_en_cfg = (vph_droop_det << 2) | + (open_circuit_det << 1) | short_circuit_det; + + led->pdata->vph_droop_debounce = FLASH_LED_VPH_DROOP_DEBOUNCE_DEFAULT; + rc = of_property_read_u32(node, "qcom,vph-droop-debounce-us", &val); + if (!rc) { + led->pdata->vph_droop_debounce = + VPH_DROOP_DEBOUNCE_US_TO_VAL(val); + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, + "Unable to read VPH droop debounce, rc=%d\n", rc); + return rc; + } + + if (led->pdata->vph_droop_debounce > FLASH_LED_VPH_DROOP_DEBOUNCE_MAX) { + dev_err(&led->pdev->dev, + "Invalid VPH droop debounce specified"); + return -EINVAL; + } + + led->pdata->vph_droop_threshold = FLASH_LED_VPH_DROOP_THRESH_DEFAULT; + rc = of_property_read_u32(node, "qcom,vph-droop-threshold-mv", &val); + if (!rc) { + led->pdata->vph_droop_threshold = + VPH_DROOP_THRESH_MV_TO_VAL(val); + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, + "Unable to read VPH droop threshold, rc=%d\n", rc); + return rc; + } + + if (led->pdata->vph_droop_threshold > FLASH_LED_VPH_DROOP_THRESH_MAX) { + dev_err(&led->pdev->dev, + "Invalid VPH droop threshold specified"); + return -EINVAL; + } + + led->pdata->vph_droop_hysteresis = + FLASH_LED_VPH_DROOP_HYST_DEFAULT; + rc = of_property_read_u32(node, "qcom,vph-droop-hysteresis-mv", &val); if (!rc) { - led->pdata->isc_delay_us = val >> FLASH_LED_ISC_DELAY_SHIFT; + led->pdata->vph_droop_hysteresis = + VPH_DROOP_HYST_MV_TO_VAL(val); } else if (rc != -EINVAL) { - dev_err(&led->pdev->dev, "Unable to read ISC delay\n"); + dev_err(&led->pdev->dev, + "Unable to read VPH droop hysteresis, rc=%d\n", rc); return rc; } + if (led->pdata->vph_droop_hysteresis > FLASH_LED_VPH_DROOP_HYST_MAX) { + dev_err(&led->pdev->dev, + "Invalid VPH droop hysteresis specified"); + return -EINVAL; + } + rc = of_property_read_u32(node, "qcom,hw-strobe-option", &val); if (!rc) { led->pdata->hw_strobe_option = (u8)val; } else if (rc != -EINVAL) { - dev_err(&led->pdev->dev, "Unable to parse hw strobe option\n"); + dev_err(&led->pdev->dev, + "Unable to parse hw strobe option, rc=%d\n", rc); return rc; } + led->pdata->all_ramp_up_done_irq = + of_irq_get_byname(node, "all-ramp-up-done-irq"); + if (led->pdata->all_ramp_up_done_irq < 0) + dev_dbg(&led->pdev->dev, "all-ramp-up-done-irq not used\n"); + + led->pdata->all_ramp_down_done_irq = + of_irq_get_byname(node, "all-ramp-down-done-irq"); + if (led->pdata->all_ramp_down_done_irq < 0) + dev_dbg(&led->pdev->dev, "all-ramp-down-done-irq not used\n"); + + led->pdata->led_fault_irq = + of_irq_get_byname(node, "led-fault-irq"); + if (led->pdata->led_fault_irq < 0) + dev_dbg(&led->pdev->dev, "led-fault-irq not used\n"); + return 0; } @@ -1033,6 +1291,49 @@ static int qpnp_flash_led_probe(struct platform_device *pdev) } } + /* setup irqs */ + if (led->pdata->all_ramp_up_done_irq >= 0) { + rc = devm_request_threaded_irq(&led->pdev->dev, + led->pdata->all_ramp_up_done_irq, + NULL, qpnp_flash_led_irq_handler, + IRQF_ONESHOT, + "qpnp_flash_led_all_ramp_up_done_irq", led); + if (rc < 0) { + dev_err(&pdev->dev, + "Unable to request all_ramp_up_done(%d) IRQ(err:%d)\n", + led->pdata->all_ramp_up_done_irq, rc); + return rc; + } + } + + if (led->pdata->all_ramp_down_done_irq >= 0) { + rc = devm_request_threaded_irq(&led->pdev->dev, + led->pdata->all_ramp_down_done_irq, + NULL, qpnp_flash_led_irq_handler, + IRQF_ONESHOT, + "qpnp_flash_led_all_ramp_down_done_irq", led); + if (rc < 0) { + dev_err(&pdev->dev, + "Unable to request all_ramp_down_done(%d) IRQ(err:%d)\n", + led->pdata->all_ramp_down_done_irq, rc); + return rc; + } + } + + if (led->pdata->led_fault_irq >= 0) { + rc = devm_request_threaded_irq(&led->pdev->dev, + led->pdata->led_fault_irq, + NULL, qpnp_flash_led_irq_handler, + IRQF_ONESHOT, + "qpnp_flash_led_fault_irq", led); + if (rc < 0) { + dev_err(&pdev->dev, + "Unable to request led_fault(%d) IRQ(err:%d)\n", + led->pdata->led_fault_irq, rc); + return rc; + } + } + rc = qpnp_flash_led_init_settings(led); if (rc < 0) { dev_err(&pdev->dev, diff --git a/drivers/leds/leds-qpnp-flash.c b/drivers/leds/leds-qpnp-flash.c index c45b217ee754..3e19cf6796a3 100644 --- a/drivers/leds/leds-qpnp-flash.c +++ b/drivers/leds/leds-qpnp-flash.c @@ -26,11 +26,12 @@ #include <linux/regulator/consumer.h> #include <linux/workqueue.h> #include <linux/power_supply.h> +#include <linux/leds-qpnp-flash.h> #include <linux/qpnp/qpnp-adc.h> #include <linux/qpnp/qpnp-revid.h> -#include "leds.h" #include <linux/debugfs.h> #include <linux/uaccess.h> +#include "leds.h" #define FLASH_LED_PERIPHERAL_SUBTYPE(base) (base + 0x05) #define FLASH_SAFETY_TIMER(base) (base + 0x40) @@ -1154,6 +1155,47 @@ error_regulator_enable: return rc; } +int qpnp_flash_led_prepare(struct led_trigger *trig, int options) +{ + struct led_classdev *led_cdev = trigger_to_lcdev(trig); + struct flash_node_data *flash_node; + struct qpnp_flash_led *led; + int rc, val = 0; + + if (!led_cdev) { + pr_err("Invalid led_trigger provided\n"); + return -EINVAL; + } + + flash_node = container_of(led_cdev, struct flash_node_data, cdev); + led = dev_get_drvdata(&flash_node->pdev->dev); + + if (!(options & (ENABLE_REGULATOR | QUERY_MAX_CURRENT))) { + dev_err(&led->pdev->dev, "Invalid options %d\n", options); + return -EINVAL; + } + + if (options & ENABLE_REGULATOR) { + rc = flash_regulator_enable(led, flash_node, true); + if (rc < 0) { + dev_err(&led->pdev->dev, + "enable regulator failed, rc=%d\n", rc); + return rc; + } + } + + if (options & QUERY_MAX_CURRENT) { + val = qpnp_flash_led_get_max_avail_current(flash_node, led); + if (val < 0) { + dev_err(&led->pdev->dev, + "query max current failed, rc=%d\n", val); + return val; + } + } + + return val; +} + static void qpnp_flash_led_work(struct work_struct *work) { struct flash_node_data *flash_node = container_of(work, diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h index 4238fbc31d35..61de87e2ad80 100644 --- a/drivers/leds/leds.h +++ b/drivers/leds/leds.h @@ -44,6 +44,22 @@ static inline int led_get_brightness(struct led_classdev *led_cdev) return led_cdev->brightness; } +static inline struct led_classdev *trigger_to_lcdev(struct led_trigger *trig) +{ + struct led_classdev *led_cdev; + + read_lock(&trig->leddev_list_lock); + list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) { + if (!strcmp(led_cdev->default_trigger, trig->name)) { + read_unlock(&trig->leddev_list_lock); + return led_cdev; + } + } + + read_unlock(&trig->leddev_list_lock); + return NULL; +} + void led_init_core(struct led_classdev *led_cdev); void led_stop_software_blink(struct led_classdev *led_cdev); diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index 8d0ead98eb6e..a296425a7270 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -1015,8 +1015,12 @@ int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c) */ atomic_set(&dc->count, 1); - if (bch_cached_dev_writeback_start(dc)) + /* Block writeback thread, but spawn it */ + down_write(&dc->writeback_lock); + if (bch_cached_dev_writeback_start(dc)) { + up_write(&dc->writeback_lock); return -ENOMEM; + } if (BDEV_STATE(&dc->sb) == BDEV_STATE_DIRTY) { bch_sectors_dirty_init(dc); @@ -1028,6 +1032,9 @@ int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c) bch_cached_dev_run(dc); bcache_device_link(&dc->disk, c, "bdev"); + /* Allow the writeback thread to proceed */ + up_write(&dc->writeback_lock); + pr_info("Caching %s as %s on set %pU", bdevname(dc->bdev, buf), dc->disk.disk->disk_name, dc->disk.c->sb.set_uuid); @@ -1366,6 +1373,9 @@ static void cache_set_flush(struct closure *cl) struct btree *b; unsigned i; + if (!c) + closure_return(cl); + bch_cache_accounting_destroy(&c->accounting); kobject_put(&c->internal); @@ -1828,11 +1838,12 @@ static int cache_alloc(struct cache_sb *sb, struct cache *ca) return 0; } -static void register_cache(struct cache_sb *sb, struct page *sb_page, +static int register_cache(struct cache_sb *sb, struct page *sb_page, struct block_device *bdev, struct cache *ca) { char name[BDEVNAME_SIZE]; - const char *err = "cannot allocate memory"; + const char *err = NULL; + int ret = 0; memcpy(&ca->sb, sb, sizeof(struct cache_sb)); ca->bdev = bdev; @@ -1847,27 +1858,35 @@ static void register_cache(struct cache_sb *sb, struct page *sb_page, if (blk_queue_discard(bdev_get_queue(ca->bdev))) ca->discard = CACHE_DISCARD(&ca->sb); - if (cache_alloc(sb, ca) != 0) + ret = cache_alloc(sb, ca); + if (ret != 0) goto err; - err = "error creating kobject"; - if (kobject_add(&ca->kobj, &part_to_dev(bdev->bd_part)->kobj, "bcache")) - goto err; + if (kobject_add(&ca->kobj, &part_to_dev(bdev->bd_part)->kobj, "bcache")) { + err = "error calling kobject_add"; + ret = -ENOMEM; + goto out; + } mutex_lock(&bch_register_lock); err = register_cache_set(ca); mutex_unlock(&bch_register_lock); - if (err) - goto err; + if (err) { + ret = -ENODEV; + goto out; + } pr_info("registered cache device %s", bdevname(bdev, name)); + out: kobject_put(&ca->kobj); - return; + err: - pr_notice("error opening %s: %s", bdevname(bdev, name), err); - goto out; + if (err) + pr_notice("error opening %s: %s", bdevname(bdev, name), err); + + return ret; } /* Global interfaces/init */ @@ -1965,7 +1984,8 @@ static ssize_t register_bcache(struct kobject *k, struct kobj_attribute *attr, if (!ca) goto err_close; - register_cache(sb, sb_page, bdev, ca); + if (register_cache(sb, sb_page, bdev, ca) != 0) + goto err_close; } out: if (sb_page) diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c index f6543f3a970f..27f2ef300f8b 100644 --- a/drivers/md/dm-cache-metadata.c +++ b/drivers/md/dm-cache-metadata.c @@ -867,19 +867,40 @@ static int blocks_are_unmapped_or_clean(struct dm_cache_metadata *cmd, return 0; } -#define WRITE_LOCK(cmd) \ - if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) \ +#define WRITE_LOCK(cmd) \ + down_write(&cmd->root_lock); \ + if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \ + up_write(&cmd->root_lock); \ return -EINVAL; \ - down_write(&cmd->root_lock) + } #define WRITE_LOCK_VOID(cmd) \ - if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) \ + down_write(&cmd->root_lock); \ + if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \ + up_write(&cmd->root_lock); \ return; \ - down_write(&cmd->root_lock) + } #define WRITE_UNLOCK(cmd) \ up_write(&cmd->root_lock) +#define READ_LOCK(cmd) \ + down_read(&cmd->root_lock); \ + if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \ + up_read(&cmd->root_lock); \ + return -EINVAL; \ + } + +#define READ_LOCK_VOID(cmd) \ + down_read(&cmd->root_lock); \ + if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \ + up_read(&cmd->root_lock); \ + return; \ + } + +#define READ_UNLOCK(cmd) \ + up_read(&cmd->root_lock) + int dm_cache_resize(struct dm_cache_metadata *cmd, dm_cblock_t new_cache_size) { int r; @@ -1015,22 +1036,20 @@ int dm_cache_load_discards(struct dm_cache_metadata *cmd, { int r; - down_read(&cmd->root_lock); + READ_LOCK(cmd); r = __load_discards(cmd, fn, context); - up_read(&cmd->root_lock); + READ_UNLOCK(cmd); return r; } -dm_cblock_t dm_cache_size(struct dm_cache_metadata *cmd) +int dm_cache_size(struct dm_cache_metadata *cmd, dm_cblock_t *result) { - dm_cblock_t r; + READ_LOCK(cmd); + *result = cmd->cache_blocks; + READ_UNLOCK(cmd); - down_read(&cmd->root_lock); - r = cmd->cache_blocks; - up_read(&cmd->root_lock); - - return r; + return 0; } static int __remove(struct dm_cache_metadata *cmd, dm_cblock_t cblock) @@ -1188,9 +1207,9 @@ int dm_cache_load_mappings(struct dm_cache_metadata *cmd, { int r; - down_read(&cmd->root_lock); + READ_LOCK(cmd); r = __load_mappings(cmd, policy, fn, context); - up_read(&cmd->root_lock); + READ_UNLOCK(cmd); return r; } @@ -1215,18 +1234,18 @@ static int __dump_mappings(struct dm_cache_metadata *cmd) void dm_cache_dump(struct dm_cache_metadata *cmd) { - down_read(&cmd->root_lock); + READ_LOCK_VOID(cmd); __dump_mappings(cmd); - up_read(&cmd->root_lock); + READ_UNLOCK(cmd); } int dm_cache_changed_this_transaction(struct dm_cache_metadata *cmd) { int r; - down_read(&cmd->root_lock); + READ_LOCK(cmd); r = cmd->changed; - up_read(&cmd->root_lock); + READ_UNLOCK(cmd); return r; } @@ -1276,9 +1295,9 @@ int dm_cache_set_dirty(struct dm_cache_metadata *cmd, void dm_cache_metadata_get_stats(struct dm_cache_metadata *cmd, struct dm_cache_statistics *stats) { - down_read(&cmd->root_lock); + READ_LOCK_VOID(cmd); *stats = cmd->stats; - up_read(&cmd->root_lock); + READ_UNLOCK(cmd); } void dm_cache_metadata_set_stats(struct dm_cache_metadata *cmd, @@ -1312,9 +1331,9 @@ int dm_cache_get_free_metadata_block_count(struct dm_cache_metadata *cmd, { int r = -EINVAL; - down_read(&cmd->root_lock); + READ_LOCK(cmd); r = dm_sm_get_nr_free(cmd->metadata_sm, result); - up_read(&cmd->root_lock); + READ_UNLOCK(cmd); return r; } @@ -1324,9 +1343,9 @@ int dm_cache_get_metadata_dev_size(struct dm_cache_metadata *cmd, { int r = -EINVAL; - down_read(&cmd->root_lock); + READ_LOCK(cmd); r = dm_sm_get_nr_blocks(cmd->metadata_sm, result); - up_read(&cmd->root_lock); + READ_UNLOCK(cmd); return r; } @@ -1417,7 +1436,13 @@ int dm_cache_write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy * int dm_cache_metadata_all_clean(struct dm_cache_metadata *cmd, bool *result) { - return blocks_are_unmapped_or_clean(cmd, 0, cmd->cache_blocks, result); + int r; + + READ_LOCK(cmd); + r = blocks_are_unmapped_or_clean(cmd, 0, cmd->cache_blocks, result); + READ_UNLOCK(cmd); + + return r; } void dm_cache_metadata_set_read_only(struct dm_cache_metadata *cmd) @@ -1440,10 +1465,7 @@ int dm_cache_metadata_set_needs_check(struct dm_cache_metadata *cmd) struct dm_block *sblock; struct cache_disk_superblock *disk_super; - /* - * We ignore fail_io for this function. - */ - down_write(&cmd->root_lock); + WRITE_LOCK(cmd); set_bit(NEEDS_CHECK, &cmd->flags); r = superblock_lock(cmd, &sblock); @@ -1458,19 +1480,17 @@ int dm_cache_metadata_set_needs_check(struct dm_cache_metadata *cmd) dm_bm_unlock(sblock); out: - up_write(&cmd->root_lock); + WRITE_UNLOCK(cmd); return r; } -bool dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd) +int dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd, bool *result) { - bool needs_check; + READ_LOCK(cmd); + *result = !!test_bit(NEEDS_CHECK, &cmd->flags); + READ_UNLOCK(cmd); - down_read(&cmd->root_lock); - needs_check = !!test_bit(NEEDS_CHECK, &cmd->flags); - up_read(&cmd->root_lock); - - return needs_check; + return 0; } int dm_cache_metadata_abort(struct dm_cache_metadata *cmd) diff --git a/drivers/md/dm-cache-metadata.h b/drivers/md/dm-cache-metadata.h index 2ffee21f318d..8528744195e5 100644 --- a/drivers/md/dm-cache-metadata.h +++ b/drivers/md/dm-cache-metadata.h @@ -66,7 +66,7 @@ void dm_cache_metadata_close(struct dm_cache_metadata *cmd); * origin blocks to map to. */ int dm_cache_resize(struct dm_cache_metadata *cmd, dm_cblock_t new_cache_size); -dm_cblock_t dm_cache_size(struct dm_cache_metadata *cmd); +int dm_cache_size(struct dm_cache_metadata *cmd, dm_cblock_t *result); int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd, sector_t discard_block_size, @@ -137,7 +137,7 @@ int dm_cache_write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy * */ int dm_cache_metadata_all_clean(struct dm_cache_metadata *cmd, bool *result); -bool dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd); +int dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd, bool *result); int dm_cache_metadata_set_needs_check(struct dm_cache_metadata *cmd); void dm_cache_metadata_set_read_only(struct dm_cache_metadata *cmd); void dm_cache_metadata_set_read_write(struct dm_cache_metadata *cmd); diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index 2fd4c8296144..515f83e7d9ab 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c @@ -987,9 +987,14 @@ static void notify_mode_switch(struct cache *cache, enum cache_metadata_mode mod static void set_cache_mode(struct cache *cache, enum cache_metadata_mode new_mode) { - bool needs_check = dm_cache_metadata_needs_check(cache->cmd); + bool needs_check; enum cache_metadata_mode old_mode = get_cache_mode(cache); + if (dm_cache_metadata_needs_check(cache->cmd, &needs_check)) { + DMERR("unable to read needs_check flag, setting failure mode"); + new_mode = CM_FAIL; + } + if (new_mode == CM_WRITE && needs_check) { DMERR("%s: unable to switch cache to write mode until repaired.", cache_device_name(cache)); @@ -3513,6 +3518,7 @@ static void cache_status(struct dm_target *ti, status_type_t type, char buf[BDEVNAME_SIZE]; struct cache *cache = ti->private; dm_cblock_t residency; + bool needs_check; switch (type) { case STATUSTYPE_INFO: @@ -3586,7 +3592,9 @@ static void cache_status(struct dm_target *ti, status_type_t type, else DMEMIT("rw "); - if (dm_cache_metadata_needs_check(cache->cmd)) + r = dm_cache_metadata_needs_check(cache->cmd, &needs_check); + + if (r || needs_check) DMEMIT("needs_check "); else DMEMIT("- "); diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 61f184ad081c..e108deebbaaa 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -1106,6 +1106,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) int i; int r = -EINVAL; char *origin_path, *cow_path; + dev_t origin_dev, cow_dev; unsigned args_used, num_flush_bios = 1; fmode_t origin_mode = FMODE_READ; @@ -1136,11 +1137,19 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) ti->error = "Cannot get origin device"; goto bad_origin; } + origin_dev = s->origin->bdev->bd_dev; cow_path = argv[0]; argv++; argc--; + cow_dev = dm_get_dev_t(cow_path); + if (cow_dev && cow_dev == origin_dev) { + ti->error = "COW device cannot be the same as origin device"; + r = -EINVAL; + goto bad_cow; + } + r = dm_get_device(ti, cow_path, dm_table_get_mode(ti->table), &s->cow); if (r) { ti->error = "Cannot get COW device"; diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 061152a43730..cb5d0daf53bb 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -365,6 +365,26 @@ static int upgrade_mode(struct dm_dev_internal *dd, fmode_t new_mode, } /* + * Convert the path to a device + */ +dev_t dm_get_dev_t(const char *path) +{ + dev_t uninitialized_var(dev); + struct block_device *bdev; + + bdev = lookup_bdev(path); + if (IS_ERR(bdev)) + dev = name_to_dev_t(path); + else { + dev = bdev->bd_dev; + bdput(bdev); + } + + return dev; +} +EXPORT_SYMBOL_GPL(dm_get_dev_t); + +/* * Add a device to the list, or just increment the usage count if * it's already present. */ @@ -372,23 +392,15 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode, struct dm_dev **result) { int r; - dev_t uninitialized_var(dev); + dev_t dev; struct dm_dev_internal *dd; struct dm_table *t = ti->table; - struct block_device *bdev; BUG_ON(!t); - /* convert the path to a device */ - bdev = lookup_bdev(path); - if (IS_ERR(bdev)) { - dev = name_to_dev_t(path); - if (!dev) - return -ENODEV; - } else { - dev = bdev->bd_dev; - bdput(bdev); - } + dev = dm_get_dev_t(path); + if (!dev) + return -ENODEV; dd = find_device(&t->devices, dev); if (!dd) { diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index c219a053c7f6..911ada643364 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -1943,5 +1943,8 @@ bool dm_pool_metadata_needs_check(struct dm_pool_metadata *pmd) void dm_pool_issue_prefetches(struct dm_pool_metadata *pmd) { - dm_tm_issue_prefetches(pmd->tm); + down_read(&pmd->root_lock); + if (!pmd->fail_io) + dm_tm_issue_prefetches(pmd->tm); + up_read(&pmd->root_lock); } diff --git a/drivers/md/dm.c b/drivers/md/dm.c index c46c9dc9b667..479fdbb3dcb2 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1109,12 +1109,8 @@ static void rq_completed(struct mapped_device *md, int rw, bool run_queue) * back into ->request_fn() could deadlock attempting to grab the * queue lock again. */ - if (run_queue) { - if (md->queue->mq_ops) - blk_mq_run_hw_queues(md->queue, true); - else - blk_run_queue_async(md->queue); - } + if (!md->queue->mq_ops && run_queue) + blk_run_queue_async(md->queue); /* * dm_put() must be at the end of this function. See the comment above @@ -1214,9 +1210,9 @@ static void dm_requeue_original_request(struct mapped_device *md, { int rw = rq_data_dir(rq); + rq_end_stats(md, rq); dm_unprep_request(rq); - rq_end_stats(md, rq); if (!rq->q->mq_ops) old_requeue_request(rq); else { @@ -1336,7 +1332,10 @@ static void dm_complete_request(struct request *rq, int error) struct dm_rq_target_io *tio = tio_from_request(rq); tio->error = error; - blk_complete_request(rq); + if (!rq->q->mq_ops) + blk_complete_request(rq); + else + blk_mq_complete_request(rq, error); } /* diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index 0a72ab6e6c20..dd483bb2e111 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -129,7 +129,9 @@ static void multipath_make_request(struct mddev *mddev, struct bio * bio) } multipath = conf->multipaths + mp_bh->path; - mp_bh->bio = *bio; + bio_init(&mp_bh->bio); + __bio_clone_fast(&mp_bh->bio, bio); + mp_bh->bio.bi_iter.bi_sector += multipath->rdev->data_offset; mp_bh->bio.bi_bdev = multipath->rdev->bdev; mp_bh->bio.bi_rw |= REQ_FAILFAST_TRANSPORT; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index c4b913409226..515554c7365b 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -2274,6 +2274,7 @@ static void handle_write_finished(struct r1conf *conf, struct r1bio *r1_bio) if (fail) { spin_lock_irq(&conf->device_lock); list_add(&r1_bio->retry_list, &conf->bio_end_io_list); + conf->nr_queued++; spin_unlock_irq(&conf->device_lock); md_wakeup_thread(conf->mddev->thread); } else { @@ -2391,8 +2392,10 @@ static void raid1d(struct md_thread *thread) LIST_HEAD(tmp); spin_lock_irqsave(&conf->device_lock, flags); if (!test_bit(MD_CHANGE_PENDING, &mddev->flags)) { - list_add(&tmp, &conf->bio_end_io_list); - list_del_init(&conf->bio_end_io_list); + while (!list_empty(&conf->bio_end_io_list)) { + list_move(conf->bio_end_io_list.prev, &tmp); + conf->nr_queued--; + } } spin_unlock_irqrestore(&conf->device_lock, flags); while (!list_empty(&tmp)) { diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index ce959b4ae4df..ebb0dd612ebd 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -2664,6 +2664,7 @@ static void handle_write_completed(struct r10conf *conf, struct r10bio *r10_bio) if (fail) { spin_lock_irq(&conf->device_lock); list_add(&r10_bio->retry_list, &conf->bio_end_io_list); + conf->nr_queued++; spin_unlock_irq(&conf->device_lock); md_wakeup_thread(conf->mddev->thread); } else { @@ -2691,8 +2692,10 @@ static void raid10d(struct md_thread *thread) LIST_HEAD(tmp); spin_lock_irqsave(&conf->device_lock, flags); if (!test_bit(MD_CHANGE_PENDING, &mddev->flags)) { - list_add(&tmp, &conf->bio_end_io_list); - list_del_init(&conf->bio_end_io_list); + while (!list_empty(&conf->bio_end_io_list)) { + list_move(conf->bio_end_io_list.prev, &tmp); + conf->nr_queued--; + } } spin_unlock_irqrestore(&conf->device_lock, flags); while (!list_empty(&tmp)) { diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 704ef7fcfbf8..10ce885445f6 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -340,8 +340,7 @@ static void release_inactive_stripe_list(struct r5conf *conf, int hash) { int size; - unsigned long do_wakeup = 0; - int i = 0; + bool do_wakeup = false; unsigned long flags; if (hash == NR_STRIPE_HASH_LOCKS) { @@ -362,19 +361,15 @@ static void release_inactive_stripe_list(struct r5conf *conf, !list_empty(list)) atomic_dec(&conf->empty_inactive_list_nr); list_splice_tail_init(list, conf->inactive_list + hash); - do_wakeup |= 1 << hash; + do_wakeup = true; spin_unlock_irqrestore(conf->hash_locks + hash, flags); } size--; hash--; } - for (i = 0; i < NR_STRIPE_HASH_LOCKS; i++) { - if (do_wakeup & (1 << i)) - wake_up(&conf->wait_for_stripe[i]); - } - if (do_wakeup) { + wake_up(&conf->wait_for_stripe); if (atomic_read(&conf->active_stripes) == 0) wake_up(&conf->wait_for_quiescent); if (conf->retry_read_aligned) @@ -687,15 +682,14 @@ raid5_get_active_stripe(struct r5conf *conf, sector_t sector, if (!sh) { set_bit(R5_INACTIVE_BLOCKED, &conf->cache_state); - wait_event_exclusive_cmd( - conf->wait_for_stripe[hash], + wait_event_lock_irq( + conf->wait_for_stripe, !list_empty(conf->inactive_list + hash) && (atomic_read(&conf->active_stripes) < (conf->max_nr_stripes * 3 / 4) || !test_bit(R5_INACTIVE_BLOCKED, &conf->cache_state)), - spin_unlock_irq(conf->hash_locks + hash), - spin_lock_irq(conf->hash_locks + hash)); + *(conf->hash_locks + hash)); clear_bit(R5_INACTIVE_BLOCKED, &conf->cache_state); } else { @@ -720,9 +714,6 @@ raid5_get_active_stripe(struct r5conf *conf, sector_t sector, } } while (sh == NULL); - if (!list_empty(conf->inactive_list + hash)) - wake_up(&conf->wait_for_stripe[hash]); - spin_unlock_irq(conf->hash_locks + hash); return sh; } @@ -2091,6 +2082,14 @@ static int resize_chunks(struct r5conf *conf, int new_disks, int new_sectors) unsigned long cpu; int err = 0; + /* + * Never shrink. And mddev_suspend() could deadlock if this is called + * from raid5d. In that case, scribble_disks and scribble_sectors + * should equal to new_disks and new_sectors + */ + if (conf->scribble_disks >= new_disks && + conf->scribble_sectors >= new_sectors) + return 0; mddev_suspend(conf->mddev); get_online_cpus(); for_each_present_cpu(cpu) { @@ -2112,6 +2111,10 @@ static int resize_chunks(struct r5conf *conf, int new_disks, int new_sectors) } put_online_cpus(); mddev_resume(conf->mddev); + if (!err) { + conf->scribble_disks = new_disks; + conf->scribble_sectors = new_sectors; + } return err; } @@ -2192,7 +2195,7 @@ static int resize_stripes(struct r5conf *conf, int newsize) cnt = 0; list_for_each_entry(nsh, &newstripes, lru) { lock_device_hash_lock(conf, hash); - wait_event_exclusive_cmd(conf->wait_for_stripe[hash], + wait_event_cmd(conf->wait_for_stripe, !list_empty(conf->inactive_list + hash), unlock_device_hash_lock(conf, hash), lock_device_hash_lock(conf, hash)); @@ -4238,7 +4241,6 @@ static void break_stripe_batch_list(struct stripe_head *head_sh, WARN_ON_ONCE(sh->state & ((1 << STRIPE_ACTIVE) | (1 << STRIPE_SYNCING) | (1 << STRIPE_REPLACED) | - (1 << STRIPE_PREREAD_ACTIVE) | (1 << STRIPE_DELAYED) | (1 << STRIPE_BIT_DELAY) | (1 << STRIPE_FULL_WRITE) | @@ -4253,6 +4255,7 @@ static void break_stripe_batch_list(struct stripe_head *head_sh, (1 << STRIPE_REPLACED))); set_mask_bits(&sh->state, ~(STRIPE_EXPAND_SYNC_FLAGS | + (1 << STRIPE_PREREAD_ACTIVE) | (1 << STRIPE_DEGRADED)), head_sh->state & (1 << STRIPE_INSYNC)); @@ -6414,6 +6417,12 @@ static int raid5_alloc_percpu(struct r5conf *conf) } put_online_cpus(); + if (!err) { + conf->scribble_disks = max(conf->raid_disks, + conf->previous_raid_disks); + conf->scribble_sectors = max(conf->chunk_sectors, + conf->prev_chunk_sectors); + } return err; } @@ -6504,9 +6513,7 @@ static struct r5conf *setup_conf(struct mddev *mddev) seqcount_init(&conf->gen_lock); mutex_init(&conf->cache_size_mutex); init_waitqueue_head(&conf->wait_for_quiescent); - for (i = 0; i < NR_STRIPE_HASH_LOCKS; i++) { - init_waitqueue_head(&conf->wait_for_stripe[i]); - } + init_waitqueue_head(&conf->wait_for_stripe); init_waitqueue_head(&conf->wait_for_overlap); INIT_LIST_HEAD(&conf->handle_list); INIT_LIST_HEAD(&conf->hold_list); @@ -7015,8 +7022,8 @@ static int run(struct mddev *mddev) } if (discard_supported && - mddev->queue->limits.max_discard_sectors >= stripe && - mddev->queue->limits.discard_granularity >= stripe) + mddev->queue->limits.max_discard_sectors >= (stripe >> 9) && + mddev->queue->limits.discard_granularity >= stripe) queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mddev->queue); else diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h index a415e1cd39b8..517d4b68a1be 100644 --- a/drivers/md/raid5.h +++ b/drivers/md/raid5.h @@ -510,6 +510,8 @@ struct r5conf { * conversions */ } __percpu *percpu; + int scribble_disks; + int scribble_sectors; #ifdef CONFIG_HOTPLUG_CPU struct notifier_block cpu_notify; #endif @@ -522,7 +524,7 @@ struct r5conf { atomic_t empty_inactive_list_nr; struct llist_head released_stripes; wait_queue_head_t wait_for_quiescent; - wait_queue_head_t wait_for_stripe[NR_STRIPE_HASH_LOCKS]; + wait_queue_head_t wait_for_stripe; wait_queue_head_t wait_for_overlap; unsigned long cache_state; #define R5_INACTIVE_BLOCKED 1 /* release of inactive stripes blocked, diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index e4900df1140b..c24839cfcc35 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -1161,12 +1161,23 @@ static void adv7511_dbg_dump_edid(int lvl, int debug, struct v4l2_subdev *sd, in } } +static void adv7511_notify_no_edid(struct v4l2_subdev *sd) +{ + struct adv7511_state *state = get_adv7511_state(sd); + struct adv7511_edid_detect ed; + + /* We failed to read the EDID, so send an event for this. */ + ed.present = false; + ed.segment = adv7511_rd(sd, 0xc4); + v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); + v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, 0x0); +} + static void adv7511_edid_handler(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); struct adv7511_state *state = container_of(dwork, struct adv7511_state, edid_handler); struct v4l2_subdev *sd = &state->sd; - struct adv7511_edid_detect ed; v4l2_dbg(1, debug, sd, "%s:\n", __func__); @@ -1191,9 +1202,7 @@ static void adv7511_edid_handler(struct work_struct *work) } /* We failed to read the EDID, so send an event for this. */ - ed.present = false; - ed.segment = adv7511_rd(sd, 0xc4); - v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); + adv7511_notify_no_edid(sd); v4l2_dbg(1, debug, sd, "%s: no edid found\n", __func__); } @@ -1264,7 +1273,6 @@ static void adv7511_check_monitor_present_status(struct v4l2_subdev *sd) /* update read only ctrls */ v4l2_ctrl_s_ctrl(state->hotplug_ctrl, adv7511_have_hotplug(sd) ? 0x1 : 0x0); v4l2_ctrl_s_ctrl(state->rx_sense_ctrl, adv7511_have_rx_sense(sd) ? 0x1 : 0x0); - v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0); if ((status & MASK_ADV7511_HPD_DETECT) && ((status & MASK_ADV7511_MSEN_DETECT) || state->edid.segments)) { v4l2_dbg(1, debug, sd, "%s: hotplug and (rx-sense or edid)\n", __func__); @@ -1294,6 +1302,7 @@ static void adv7511_check_monitor_present_status(struct v4l2_subdev *sd) } adv7511_s_power(sd, false); memset(&state->edid, 0, sizeof(struct adv7511_state_edid)); + adv7511_notify_no_edid(sd); } } @@ -1370,6 +1379,7 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd) } /* one more segment read ok */ state->edid.segments = segment + 1; + v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, 0x1); if (((state->edid.data[0x7e] >> 1) + 1) > state->edid.segments) { /* Request next EDID segment */ v4l2_dbg(1, debug, sd, "%s: request segment %d\n", __func__, state->edid.segments); @@ -1389,7 +1399,6 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd) ed.present = true; ed.segment = 0; state->edid_detect_counter++; - v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0); v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); return ed.present; } diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 15a4ebc2844d..51dbef2f9a48 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -2334,6 +2334,19 @@ static int bttv_g_fmt_vid_overlay(struct file *file, void *priv, return 0; } +static void bttv_get_width_mask_vid_cap(const struct bttv_format *fmt, + unsigned int *width_mask, + unsigned int *width_bias) +{ + if (fmt->flags & FORMAT_FLAGS_PLANAR) { + *width_mask = ~15; /* width must be a multiple of 16 pixels */ + *width_bias = 8; /* nearest */ + } else { + *width_mask = ~3; /* width must be a multiple of 4 pixels */ + *width_bias = 2; /* nearest */ + } +} + static int bttv_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { @@ -2343,6 +2356,7 @@ static int bttv_try_fmt_vid_cap(struct file *file, void *priv, enum v4l2_field field; __s32 width, height; __s32 height2; + unsigned int width_mask, width_bias; int rc; fmt = format_by_fourcc(f->fmt.pix.pixelformat); @@ -2375,9 +2389,9 @@ static int bttv_try_fmt_vid_cap(struct file *file, void *priv, width = f->fmt.pix.width; height = f->fmt.pix.height; + bttv_get_width_mask_vid_cap(fmt, &width_mask, &width_bias); rc = limit_scaled_size_lock(fh, &width, &height, field, - /* width_mask: 4 pixels */ ~3, - /* width_bias: nearest */ 2, + width_mask, width_bias, /* adjust_size */ 1, /* adjust_crop */ 0); if (0 != rc) @@ -2410,6 +2424,7 @@ static int bttv_s_fmt_vid_cap(struct file *file, void *priv, struct bttv_fh *fh = priv; struct bttv *btv = fh->btv; __s32 width, height; + unsigned int width_mask, width_bias; enum v4l2_field field; retval = bttv_switch_type(fh, f->type); @@ -2424,9 +2439,10 @@ static int bttv_s_fmt_vid_cap(struct file *file, void *priv, height = f->fmt.pix.height; field = f->fmt.pix.field; + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + bttv_get_width_mask_vid_cap(fmt, &width_mask, &width_bias); retval = limit_scaled_size_lock(fh, &width, &height, f->fmt.pix.field, - /* width_mask: 4 pixels */ ~3, - /* width_bias: nearest */ 2, + width_mask, width_bias, /* adjust_size */ 1, /* adjust_crop */ 1); if (0 != retval) @@ -2434,8 +2450,6 @@ static int bttv_s_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.field = field; - fmt = format_by_fourcc(f->fmt.pix.pixelformat); - /* update our state informations */ fh->fmt = fmt; fh->cap.field = f->fmt.pix.field; diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 518086c7aed5..15e56c07b217 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1219,10 +1219,13 @@ static int saa7134_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.height = dev->height; f->fmt.pix.field = dev->field; f->fmt.pix.pixelformat = dev->fmt->fourcc; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * dev->fmt->depth) >> 3; + if (dev->fmt->planar) + f->fmt.pix.bytesperline = f->fmt.pix.width; + else + f->fmt.pix.bytesperline = + (f->fmt.pix.width * dev->fmt->depth) / 8; f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; + (f->fmt.pix.height * f->fmt.pix.width * dev->fmt->depth) / 8; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } @@ -1298,10 +1301,13 @@ static int saa7134_try_fmt_vid_cap(struct file *file, void *priv, if (f->fmt.pix.height > maxh) f->fmt.pix.height = maxh; f->fmt.pix.width &= ~0x03; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fmt->depth) >> 3; + if (fmt->planar) + f->fmt.pix.bytesperline = f->fmt.pix.width; + else + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) / 8; f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; + (f->fmt.pix.height * f->fmt.pix.width * fmt->depth) / 8; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 654e964f84a2..d76511c1c1e3 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1342,7 +1342,7 @@ static void coda_finish_encode(struct coda_ctx *ctx) /* Calculate bytesused field */ if (dst_buf->sequence == 0) { - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr + ctx->vpu_header_size[0] + ctx->vpu_header_size[1] + ctx->vpu_header_size[2]); diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 15516a6e3a39..323aad3c89de 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -2119,14 +2119,12 @@ static int coda_probe(struct platform_device *pdev) pdev_id = of_id ? of_id->data : platform_get_device_id(pdev); - if (of_id) { + if (of_id) dev->devtype = of_id->data; - } else if (pdev_id) { + else if (pdev_id) dev->devtype = &coda_devdata[pdev_id->driver_data]; - } else { - ret = -EINVAL; - goto err_v4l2_register; - } + else + return -EINVAL; spin_lock_init(&dev->irqlock); INIT_LIST_HEAD(&dev->instances); diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c index c1aeb8c43e81..3985df780216 100644 --- a/drivers/media/platform/msm/camera_v2/camera/camera.c +++ b/drivers/media/platform/msm/camera_v2/camera/camera.c @@ -538,7 +538,7 @@ static int camera_v4l2_fh_open(struct file *filep) { struct msm_video_device *pvdev = video_drvdata(filep); struct camera_v4l2_private *sp; - unsigned int stream_id; + unsigned long stream_id; sp = kzalloc(sizeof(*sp), GFP_KERNEL); if (!sp) { @@ -617,7 +617,7 @@ static int camera_v4l2_open(struct file *filep) int rc = 0; struct v4l2_event event; struct msm_video_device *pvdev = video_drvdata(filep); - unsigned int opn_idx, idx; + unsigned long opn_idx, idx; BUG_ON(!pvdev); rc = camera_v4l2_fh_open(filep); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c index a76ccc06c9e1..d42ada769380 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c @@ -101,29 +101,21 @@ static void msm_vfe40_config_irq(struct vfe_device *vfe_dev, uint32_t irq0_mask, uint32_t irq1_mask, enum msm_isp_irq_operation oper) { - uint32_t val; - switch (oper) { case MSM_ISP_IRQ_ENABLE: - val = msm_camera_io_r(vfe_dev->vfe_base + 0x28); - val |= irq0_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x28); - val = msm_camera_io_r(vfe_dev->vfe_base + 0x2C); - val |= irq1_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x2C); + vfe_dev->irq0_mask |= irq0_mask; + vfe_dev->irq1_mask |= irq1_mask; break; case MSM_ISP_IRQ_DISABLE: - val = msm_camera_io_r(vfe_dev->vfe_base + 0x28); - val &= ~irq0_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x28); - val = msm_camera_io_r(vfe_dev->vfe_base + 0x2C); - val &= ~irq1_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x2C); + vfe_dev->irq0_mask &= ~irq0_mask; + vfe_dev->irq1_mask &= ~irq1_mask; break; case MSM_ISP_IRQ_SET: - msm_camera_io_w_mb(irq0_mask, vfe_dev->vfe_base + 0x28); - msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x2C); + vfe_dev->irq0_mask = irq0_mask; + vfe_dev->irq1_mask = irq1_mask; } + msm_camera_io_w_mb(vfe_dev->irq0_mask, vfe_dev->vfe_base + 0x28); + msm_camera_io_w_mb(vfe_dev->irq1_mask, vfe_dev->vfe_base + 0x2C); } static int32_t msm_vfe40_init_qos_parms(struct vfe_device *vfe_dev, @@ -335,10 +327,8 @@ static void msm_vfe40_init_hardware_reg(struct vfe_device *vfe_dev) msm_vfe40_init_vbif_parms(vfe_dev, &vbif_parms); /* BUS_CFG */ msm_camera_io_w(0x10000001, vfe_dev->vfe_base + 0x50); - vfe_dev->irq0_mask = 0xE00000F1; - vfe_dev->irq1_mask = 0xFEFFFFFF; - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, 0x800000E0, 0xFEFFFF7E, + MSM_ISP_IRQ_ENABLE); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30); msm_camera_io_w_mb(0xFEFFFFFF, vfe_dev->vfe_base + 0x34); msm_camera_io_w(1, vfe_dev->vfe_base + 0x24); @@ -346,15 +336,13 @@ static void msm_vfe40_init_hardware_reg(struct vfe_device *vfe_dev) msm_camera_io_w(0, vfe_dev->vfe_base + 0x30); msm_camera_io_w_mb(0, vfe_dev->vfe_base + 0x34); msm_camera_io_w(1, vfe_dev->vfe_base + 0x24); - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); } static void msm_vfe40_clear_status_reg(struct vfe_device *vfe_dev) { vfe_dev->irq0_mask = (1 << 31); vfe_dev->irq1_mask = 0; - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, + msm_vfe40_config_irq(vfe_dev, (1 << 31), 0, MSM_ISP_IRQ_SET); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x34); @@ -589,7 +577,6 @@ static void msm_vfe40_read_irq_status(struct vfe_device *vfe_dev, if (*irq_status1 & (1 << 0)) { vfe_dev->error_info.camif_status = msm_camera_io_r(vfe_dev->vfe_base + 0x31C); - vfe_dev->irq1_mask &= ~(1 << 0); msm_vfe40_config_irq(vfe_dev, 0, (1 << 0), MSM_ISP_IRQ_DISABLE); } @@ -812,11 +799,9 @@ static void msm_vfe40_axi_cfg_comp_mask(struct vfe_device *vfe_dev, comp_mask |= (axi_data->composite_info[comp_mask_index]. stream_composite_mask << (comp_mask_index * 8)); - vfe_dev->irq0_mask |= 1 << (comp_mask_index + 25); - msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40); - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, 1 << (comp_mask_index + 25), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe40_axi_clear_comp_mask(struct vfe_device *vfe_dev, @@ -828,27 +813,24 @@ static void msm_vfe40_axi_clear_comp_mask(struct vfe_device *vfe_dev, comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x40); comp_mask &= ~(0x7F << (comp_mask_index * 8)); - vfe_dev->irq0_mask &= ~(1 << (comp_mask_index + 25)); - msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40); - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, (1 << (comp_mask_index + 25)), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe40_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { - vfe_dev->irq0_mask |= 1 << (stream_info->wm[0] + 8); - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, 1 << (stream_info->wm[0] + 8), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe40_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { vfe_dev->irq0_mask &= ~(1 << (stream_info->wm[0] + 8)); - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, (1 << (stream_info->wm[0] + 8)), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe40_cfg_framedrop(void __iomem *vfe_base, @@ -1088,10 +1070,8 @@ static void msm_vfe40_cfg_fetch_engine(struct vfe_device *vfe_dev, temp |= (1 << 1); msm_camera_io_w(temp, vfe_dev->vfe_base + 0x50); - vfe_dev->irq0_mask &= 0xFEFFFFFF; - vfe_dev->irq0_mask |= (1 << 24); - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, (1 << 24), 0, + MSM_ISP_IRQ_ENABLE); msm_camera_io_w((fe_cfg->fetch_height - 1), vfe_dev->vfe_base + 0x238); @@ -1382,13 +1362,11 @@ static void msm_vfe40_update_camif_state(struct vfe_device *vfe_dev, return; if (update_state == ENABLE_CAMIF) { - msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30); - msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x34); + msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x30); + msm_camera_io_w_mb(0x81, vfe_dev->vfe_base + 0x34); msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x24); - vfe_dev->irq0_mask |= 0xF7; - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, - vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, 0xF7, 0x81, + MSM_ISP_IRQ_ENABLE); msm_camera_io_w_mb(0x140000, vfe_dev->vfe_base + 0x318); bus_en = @@ -1413,8 +1391,8 @@ static void msm_vfe40_update_camif_state(struct vfe_device *vfe_dev, if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN) update_state = DISABLE_CAMIF; - msm_vfe40_config_irq(vfe_dev, 0, 0, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, 0, 0x81, + MSM_ISP_IRQ_DISABLE); val = msm_camera_io_r(vfe_dev->vfe_base + 0x464); /* disable danger signal */ msm_camera_io_w_mb(val & ~(1 << 8), vfe_dev->vfe_base + 0x464); @@ -1897,6 +1875,9 @@ static void msm_vfe40_stats_cfg_comp_mask(struct vfe_device *vfe_dev, comp_mask_reg |= mask_bf_scale << (16 + request_comp_index * 8); atomic_set(stats_comp_mask, stats_mask | atomic_read(stats_comp_mask)); + msm_vfe40_config_irq(vfe_dev, + 1 << (request_comp_index + 29), 0, + MSM_ISP_IRQ_ENABLE); } else { if (!(atomic_read(stats_comp_mask) & stats_mask)) return; @@ -1904,6 +1885,9 @@ static void msm_vfe40_stats_cfg_comp_mask(struct vfe_device *vfe_dev, ~stats_mask & atomic_read(stats_comp_mask)); comp_mask_reg &= ~(mask_bf_scale << (16 + request_comp_index * 8)); + msm_vfe40_config_irq(vfe_dev, + 1 << (request_comp_index + 29), 0, + MSM_ISP_IRQ_DISABLE); } msm_camera_io_w(comp_mask_reg, vfe_dev->vfe_base + 0x44); @@ -1919,20 +1903,18 @@ static void msm_vfe40_stats_cfg_wm_irq_mask( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { - vfe_dev->irq0_mask |= - 1 << (STATS_IDX(stream_info->stream_handle) + 16); - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, + 1 << (STATS_IDX(stream_info->stream_handle) + 16), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe40_stats_clear_wm_irq_mask( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { - vfe_dev->irq0_mask &= - ~(1 << (STATS_IDX(stream_info->stream_handle) + 16)); - msm_vfe40_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe40_config_irq(vfe_dev, + (1 << (STATS_IDX(stream_info->stream_handle) + 16)), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe40_stats_cfg_wm_reg( diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c index 08b20395813c..388656b9ca30 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c @@ -70,30 +70,22 @@ static void msm_vfe44_config_irq(struct vfe_device *vfe_dev, uint32_t irq0_mask, uint32_t irq1_mask, enum msm_isp_irq_operation oper) { - uint32_t val; - switch (oper) { case MSM_ISP_IRQ_ENABLE: - val = msm_camera_io_r(vfe_dev->vfe_base + 0x28); - val |= irq0_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x28); - val = msm_camera_io_r(vfe_dev->vfe_base + 0x2C); - val |= irq1_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x2C); + vfe_dev->irq0_mask |= irq0_mask; + vfe_dev->irq1_mask |= irq1_mask; break; case MSM_ISP_IRQ_DISABLE: - val = msm_camera_io_r(vfe_dev->vfe_base + 0x28); - val &= ~irq0_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x28); - val = msm_camera_io_r(vfe_dev->vfe_base + 0x2C); - val &= ~irq1_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x2C); + vfe_dev->irq0_mask &= ~irq0_mask; + vfe_dev->irq1_mask &= ~irq1_mask; break; case MSM_ISP_IRQ_SET: - msm_camera_io_w_mb(irq0_mask, vfe_dev->vfe_base + 0x28); - msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x2C); + vfe_dev->irq0_mask = irq0_mask; + vfe_dev->irq1_mask = irq1_mask; break; } + msm_camera_io_w_mb(irq0_mask, vfe_dev->vfe_base + 0x28); + msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x2C); } static int32_t msm_vfe44_init_dt_parms(struct vfe_device *vfe_dev, @@ -181,10 +173,8 @@ static void msm_vfe44_init_hardware_reg(struct vfe_device *vfe_dev) /* BUS_CFG */ msm_camera_io_w(0x10000001, vfe_dev->vfe_base + 0x50); - vfe_dev->irq0_mask = 0xE00000F1; - vfe_dev->irq1_mask = 0xFFFFFFFF; - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, 0x800000E0, 0xFFFFFF7E, + MSM_ISP_IRQ_ENABLE); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x34); msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x24); @@ -193,9 +183,7 @@ static void msm_vfe44_init_hardware_reg(struct vfe_device *vfe_dev) static void msm_vfe44_clear_status_reg(struct vfe_device *vfe_dev) { - vfe_dev->irq0_mask = 0x80000000; - vfe_dev->irq1_mask = 0; - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, + msm_vfe44_config_irq(vfe_dev, 0x80000000, 0, MSM_ISP_IRQ_SET); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x34); @@ -419,7 +407,6 @@ static void msm_vfe44_read_irq_status(struct vfe_device *vfe_dev, if (*irq_status1 & (1 << 0)) { vfe_dev->error_info.camif_status = msm_camera_io_r(vfe_dev->vfe_base + 0x31C); - vfe_dev->irq1_mask &= ~(1 << 0); msm_vfe44_config_irq(vfe_dev, 0, (1 << 0), MSM_ISP_IRQ_DISABLE); } @@ -650,9 +637,8 @@ static void msm_vfe44_axi_cfg_comp_mask(struct vfe_device *vfe_dev, stream_composite_mask << (comp_mask_index * 8)); msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40); - vfe_dev->irq0_mask |= 1 << (comp_mask_index + 25); - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, 1 << (comp_mask_index + 25), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe44_axi_clear_comp_mask(struct vfe_device *vfe_dev, @@ -664,25 +650,22 @@ static void msm_vfe44_axi_clear_comp_mask(struct vfe_device *vfe_dev, comp_mask &= ~(0x7F << (comp_mask_index * 8)); msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40); - vfe_dev->irq0_mask &= ~(1 << (comp_mask_index + 25)); - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, (1 << (comp_mask_index + 25)), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe44_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { - vfe_dev->irq0_mask |= 1 << (stream_info->wm[0] + 8); - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, 1 << (stream_info->wm[0] + 8), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe44_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { - vfe_dev->irq0_mask &= ~(1 << (stream_info->wm[0] + 8)); - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, (1 << (stream_info->wm[0] + 8)), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe44_cfg_framedrop(void __iomem *vfe_base, @@ -918,10 +901,8 @@ static void msm_vfe44_cfg_fetch_engine(struct vfe_device *vfe_dev, temp |= (1 << 1); msm_camera_io_w(temp, vfe_dev->vfe_base + 0x50); - vfe_dev->irq0_mask &= 0xFEFFFFFF; - vfe_dev->irq0_mask |= (1 << 24); - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, - vfe_dev->irq1_mask, MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, (1 << 24), 0, + MSM_ISP_IRQ_SET); msm_camera_io_w((fe_cfg->fetch_height - 1) & 0xFFF, vfe_dev->vfe_base + 0x238); @@ -1045,13 +1026,12 @@ static void msm_vfe44_update_camif_state(struct vfe_device *vfe_dev, return; if (update_state == ENABLE_CAMIF) { - msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30); - msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x34); + msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x30); + msm_camera_io_w_mb(0x81, vfe_dev->vfe_base + 0x34); msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x24); - vfe_dev->irq0_mask |= 0xF7; - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, - vfe_dev->irq1_mask, MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, 0xF7, 0x81, + MSM_ISP_IRQ_ENABLE); msm_camera_io_w_mb(0x140000, vfe_dev->vfe_base + 0x318); bus_en = @@ -1075,7 +1055,7 @@ static void msm_vfe44_update_camif_state(struct vfe_device *vfe_dev, if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN) update_state = DISABLE_CAMIF; msm_vfe44_config_irq(vfe_dev, 0, - 0, MSM_ISP_IRQ_SET); + 0x81, MSM_ISP_IRQ_DISABLE); val = msm_camera_io_r(vfe_dev->vfe_base + 0xC18); /* disable danger signal */ msm_camera_io_w_mb(val & ~(1 << 8), vfe_dev->vfe_base + 0xC18); @@ -1526,6 +1506,9 @@ static void msm_vfe44_stats_cfg_comp_mask( comp_mask_reg |= mask_bf_scale << (16 + request_comp_index * 8); atomic_set(stats_comp_mask, stats_mask | atomic_read(stats_comp_mask)); + msm_vfe44_config_irq(vfe_dev, + 1 << (request_comp_index + 29), 0, + MSM_ISP_IRQ_ENABLE); } else { if (!(atomic_read(stats_comp_mask) & stats_mask)) return; @@ -1540,6 +1523,9 @@ static void msm_vfe44_stats_cfg_comp_mask( ~stats_mask & atomic_read(stats_comp_mask)); comp_mask_reg &= ~(mask_bf_scale << (16 + request_comp_index * 8)); + msm_vfe44_config_irq(vfe_dev, + 1 << (request_comp_index + 29), 0, + MSM_ISP_IRQ_DISABLE); } msm_camera_io_w(comp_mask_reg, vfe_dev->vfe_base + 0x44); @@ -1555,20 +1541,18 @@ static void msm_vfe44_stats_cfg_wm_irq_mask( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { - vfe_dev->irq0_mask |= - 1 << (STATS_IDX(stream_info->stream_handle) + 15); - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, + 1 << (STATS_IDX(stream_info->stream_handle) + 15), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe44_stats_clear_wm_irq_mask( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { - vfe_dev->irq0_mask &= - ~(1 << (STATS_IDX(stream_info->stream_handle) + 15)); - msm_vfe44_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe44_config_irq(vfe_dev, + (1 << (STATS_IDX(stream_info->stream_handle) + 15)), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe44_stats_cfg_wm_reg( diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c index 9f815e65edc8..40bb044fde47 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c @@ -92,30 +92,24 @@ static void msm_vfe46_config_irq(struct vfe_device *vfe_dev, uint32_t irq0_mask, uint32_t irq1_mask, enum msm_isp_irq_operation oper) { - uint32_t val; - switch (oper) { case MSM_ISP_IRQ_ENABLE: - val = msm_camera_io_r(vfe_dev->vfe_base + 0x5C); - val |= irq0_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x5C); - val = msm_camera_io_r(vfe_dev->vfe_base + 0x60); - val |= irq1_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x60); + vfe_dev->irq0_mask |= irq0_mask; + vfe_dev->irq1_mask |= irq1_mask; break; case MSM_ISP_IRQ_DISABLE: - val = msm_camera_io_r(vfe_dev->vfe_base + 0x5C); - val &= ~irq0_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x5C); - val = msm_camera_io_r(vfe_dev->vfe_base + 0x60); - val &= ~irq1_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x60); + vfe_dev->irq0_mask &= ~irq0_mask; + vfe_dev->irq1_mask &= ~irq1_mask; break; case MSM_ISP_IRQ_SET: - msm_camera_io_w_mb(irq0_mask, vfe_dev->vfe_base + 0x5C); - msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x60); + vfe_dev->irq0_mask = irq0_mask; + vfe_dev->irq1_mask = irq1_mask; break; } + msm_camera_io_w_mb(vfe_dev->irq0_mask, + vfe_dev->vfe_base + 0x5C); + msm_camera_io_w_mb(vfe_dev->irq1_mask, + vfe_dev->vfe_base + 0x60); } static int32_t msm_vfe46_init_dt_parms(struct vfe_device *vfe_dev, @@ -208,20 +202,16 @@ static void msm_vfe46_init_hardware_reg(struct vfe_device *vfe_dev) /* BUS_CFG */ msm_camera_io_w(0x00000001, vfe_dev->vfe_base + 0x84); /* IRQ_MASK/CLEAR */ - vfe_dev->irq0_mask = 0xE00000F1; - vfe_dev->irq1_mask = 0xE1FFFFFF; - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, 0x810000E0, 0xFFFFFF7E, + MSM_ISP_IRQ_ENABLE); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68); + msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58); } static void msm_vfe46_clear_status_reg(struct vfe_device *vfe_dev) { - vfe_dev->irq0_mask = 0x80000000; - vfe_dev->irq1_mask = 0x0; - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, 0x80000000, 0, MSM_ISP_IRQ_SET); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68); msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58); @@ -355,7 +345,6 @@ static void msm_vfe46_read_irq_status(struct vfe_device *vfe_dev, if (*irq_status1 & (1 << 0)) { vfe_dev->error_info.camif_status = msm_camera_io_r(vfe_dev->vfe_base + 0x3D0); - vfe_dev->irq1_mask &= ~(1 << 0); msm_vfe46_config_irq(vfe_dev, 0, (1 << 0), MSM_ISP_IRQ_DISABLE); } @@ -587,9 +576,8 @@ static void msm_vfe46_axi_cfg_comp_mask(struct vfe_device *vfe_dev, stream_composite_mask << (comp_mask_index * 8)); msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74); - vfe_dev->irq0_mask |= 1 << (comp_mask_index + 25); - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, 1 << (comp_mask_index + 25), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe46_axi_clear_comp_mask(struct vfe_device *vfe_dev, @@ -601,25 +589,22 @@ static void msm_vfe46_axi_clear_comp_mask(struct vfe_device *vfe_dev, comp_mask &= ~(0x7F << (comp_mask_index * 8)); msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74); - vfe_dev->irq0_mask |= 1 << (comp_mask_index + 25); - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, 1 << (comp_mask_index + 25), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe46_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { - vfe_dev->irq0_mask |= 1 << (stream_info->wm[0] + 8); - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, 1 << (stream_info->wm[0] + 8), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe46_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { - vfe_dev->irq0_mask &= ~(1 << (stream_info->wm[0] + 8)); - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, (1 << (stream_info->wm[0] + 8)), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe46_cfg_framedrop(void __iomem *vfe_base, @@ -857,10 +842,8 @@ static void msm_vfe46_cfg_fetch_engine(struct vfe_device *vfe_dev, temp |= (1 << 1); msm_camera_io_w(temp, vfe_dev->vfe_base + 0x84); - vfe_dev->irq0_mask &= 0xFEFFFFFF; - vfe_dev->irq0_mask |= (1 << 24); - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, - vfe_dev->irq1_mask, MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, 1 << 24, 0, + MSM_ISP_IRQ_ENABLE); temp = fe_cfg->fetch_height - 1; msm_camera_io_w(temp & 0x3FFF, vfe_dev->vfe_base + 0x278); @@ -1120,9 +1103,11 @@ static void msm_vfe46_update_camif_state(struct vfe_device *vfe_dev, return; if (update_state == ENABLE_CAMIF) { - vfe_dev->irq0_mask |= 0xF5; - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, - vfe_dev->irq1_mask, MSM_ISP_IRQ_SET); + msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x64); + msm_camera_io_w(0x81, vfe_dev->vfe_base + 0x68); + msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x58); + msm_vfe46_config_irq(vfe_dev, 0x15, 0x81, + MSM_ISP_IRQ_ENABLE); bus_en = ((vfe_dev->axi_data. @@ -1148,7 +1133,8 @@ static void msm_vfe46_update_camif_state(struct vfe_device *vfe_dev, if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN) update_state = DISABLE_CAMIF; - msm_vfe46_config_irq(vfe_dev, 0, 0, MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, 0, 0x81, + MSM_ISP_IRQ_DISABLE); /* disable danger signal */ val = msm_camera_io_r(vfe_dev->vfe_base + 0xC18); val &= ~(1 << 8); @@ -1611,6 +1597,9 @@ static void msm_vfe46_stats_cfg_comp_mask( comp_mask_reg |= mask_bf_scale << (16 + request_comp_index * 8); atomic_set(stats_comp_mask, stats_mask | atomic_read(stats_comp_mask)); + msm_vfe46_config_irq(vfe_dev, + 1 << (request_comp_index + 29), 0, + MSM_ISP_IRQ_ENABLE); } else { if (!(atomic_read(stats_comp_mask) & stats_mask)) return; @@ -1625,6 +1614,9 @@ static void msm_vfe46_stats_cfg_comp_mask( ~stats_mask & atomic_read(stats_comp_mask)); comp_mask_reg &= ~(mask_bf_scale << (16 + request_comp_index * 8)); + msm_vfe46_config_irq(vfe_dev, + 1 << (request_comp_index + 29), 0, + MSM_ISP_IRQ_DISABLE); } msm_camera_io_w(comp_mask_reg, vfe_dev->vfe_base + 0x78); @@ -1640,19 +1632,18 @@ static void msm_vfe46_stats_cfg_wm_irq_mask( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { - vfe_dev->irq0_mask |= 1 << (STATS_IDX(stream_info->stream_handle) + 15); - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, + 1 << (STATS_IDX(stream_info->stream_handle) + 15), 0, + MSM_ISP_IRQ_ENABLE); } static void msm_vfe46_stats_clear_wm_irq_mask( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { - vfe_dev->irq0_mask &= - ~(1 << (STATS_IDX(stream_info->stream_handle) + 15)); - msm_vfe46_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe46_config_irq(vfe_dev, + 1 << (STATS_IDX(stream_info->stream_handle) + 15), 0, + MSM_ISP_IRQ_DISABLE); } static void msm_vfe46_stats_cfg_wm_reg( diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c index 20aa69f322db..290f100ffeba 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c @@ -149,30 +149,24 @@ void msm_vfe47_config_irq(struct vfe_device *vfe_dev, uint32_t irq0_mask, uint32_t irq1_mask, enum msm_isp_irq_operation oper) { - uint32_t val; - switch (oper) { case MSM_ISP_IRQ_ENABLE: - val = msm_camera_io_r(vfe_dev->vfe_base + 0x5C); - val |= irq0_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x5C); - val = msm_camera_io_r(vfe_dev->vfe_base + 0x60); - val |= irq1_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x60); + vfe_dev->irq0_mask |= irq0_mask; + vfe_dev->irq1_mask |= irq1_mask; break; case MSM_ISP_IRQ_DISABLE: - val = msm_camera_io_r(vfe_dev->vfe_base + 0x5C); - val &= ~irq0_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x5C); - val = msm_camera_io_r(vfe_dev->vfe_base + 0x60); - val &= ~irq1_mask; - msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x60); + vfe_dev->irq0_mask &= ~irq0_mask; + vfe_dev->irq1_mask &= ~irq1_mask; break; case MSM_ISP_IRQ_SET: - msm_camera_io_w_mb(irq0_mask, vfe_dev->vfe_base + 0x5C); - msm_camera_io_w_mb(irq1_mask, vfe_dev->vfe_base + 0x60); + vfe_dev->irq0_mask = irq0_mask; + vfe_dev->irq1_mask = irq1_mask; break; } + msm_camera_io_w_mb(vfe_dev->irq0_mask, + vfe_dev->vfe_base + 0x5C); + msm_camera_io_w_mb(vfe_dev->irq1_mask, + vfe_dev->vfe_base + 0x60); } static int32_t msm_vfe47_init_dt_parms(struct vfe_device *vfe_dev, @@ -285,13 +279,6 @@ int msm_vfe47_init_hardware(struct vfe_device *vfe_dev) else id = CAM_AHB_CLIENT_VFE1; - rc = cam_config_ahb_clk(NULL, 0, id, CAM_AHB_SVS_VOTE); - if (rc < 0) { - pr_err("%s: failed to vote for AHB\n", __func__); - goto ahb_vote_fail; - } - vfe_dev->ahb_vote = CAM_AHB_SVS_VOTE; - rc = vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators( vfe_dev, 1); if (rc) @@ -302,6 +289,13 @@ int msm_vfe47_init_hardware(struct vfe_device *vfe_dev) if (rc) goto clk_enable_failed; + rc = cam_config_ahb_clk(NULL, 0, id, CAM_AHB_SVS_VOTE); + if (rc < 0) { + pr_err("%s: failed to vote for AHB\n", __func__); + goto ahb_vote_fail; + } + vfe_dev->ahb_vote = CAM_AHB_SVS_VOTE; + vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] = vfe_dev->vfe_base; @@ -312,14 +306,14 @@ int msm_vfe47_init_hardware(struct vfe_device *vfe_dev) return rc; irq_enable_fail: vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] = NULL; - vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks(vfe_dev, 0); -clk_enable_failed: - vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0); -enable_regulators_failed: if (cam_config_ahb_clk(NULL, 0, id, CAM_AHB_SUSPEND_VOTE) < 0) pr_err("%s: failed to remove vote for AHB\n", __func__); vfe_dev->ahb_vote = CAM_AHB_SUSPEND_VOTE; ahb_vote_fail: + vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks(vfe_dev, 0); +clk_enable_failed: + vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0); +enable_regulators_failed: return rc; } @@ -338,9 +332,6 @@ void msm_vfe47_release_hardware(struct vfe_device *vfe_dev) msm_isp_flush_tasklet(vfe_dev); vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] = NULL; - vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks( - vfe_dev, 0); - vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0); msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id, 0, 0); @@ -351,7 +342,12 @@ void msm_vfe47_release_hardware(struct vfe_device *vfe_dev) if (cam_config_ahb_clk(NULL, 0, id, CAM_AHB_SUSPEND_VOTE) < 0) pr_err("%s: failed to vote for AHB\n", __func__); - vfe_dev->ahb_vote = CAM_AHB_SUSPEND_VOTE; + + vfe_dev->ahb_vote = CAM_AHB_SUSPEND_VOTE; + + vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks( + vfe_dev, 0); + vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0); } void msm_vfe47_init_hardware_reg(struct vfe_device *vfe_dev) @@ -382,19 +378,16 @@ void msm_vfe47_init_hardware_reg(struct vfe_device *vfe_dev) /* BUS_CFG */ msm_camera_io_w(0x00000101, vfe_dev->vfe_base + 0x84); /* IRQ_MASK/CLEAR */ - vfe_dev->irq0_mask = 0xE00000F3; - vfe_dev->irq1_mask = 0xFFFFFFFF; - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe47_config_irq(vfe_dev, 0x810000E0, 0xFFFFFF7E, + MSM_ISP_IRQ_ENABLE); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68); + msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x58); } void msm_vfe47_clear_status_reg(struct vfe_device *vfe_dev) { - vfe_dev->irq0_mask = 0x80000000; - vfe_dev->irq1_mask = 0x0; - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, + msm_vfe47_config_irq(vfe_dev, 0x80000000, 0x0, MSM_ISP_IRQ_SET); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x64); msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x68); @@ -543,7 +536,6 @@ void msm_vfe47_read_irq_status(struct vfe_device *vfe_dev, vfe_dev->error_info.camif_status = msm_camera_io_r(vfe_dev->vfe_base + 0x4A4); /* mask off camif error after first occurrance */ - vfe_dev->irq1_mask &= ~(1 << 0); msm_vfe47_config_irq(vfe_dev, 0, (1 << 0), MSM_ISP_IRQ_DISABLE); } @@ -785,9 +777,8 @@ void msm_vfe47_axi_cfg_comp_mask(struct vfe_device *vfe_dev, stream_composite_mask << (comp_mask_index * 8)); msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74); - vfe_dev->irq0_mask |= 1 << (comp_mask_index + 25); - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe47_config_irq(vfe_dev, 1 << (comp_mask_index + 25), 0, + MSM_ISP_IRQ_ENABLE); } void msm_vfe47_axi_clear_comp_mask(struct vfe_device *vfe_dev, @@ -799,25 +790,22 @@ void msm_vfe47_axi_clear_comp_mask(struct vfe_device *vfe_dev, comp_mask &= ~(0x7F << (comp_mask_index * 8)); msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x74); - vfe_dev->irq0_mask &= ~(1 << (comp_mask_index + 25)); - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe47_config_irq(vfe_dev, (1 << (comp_mask_index + 25)), 0, + MSM_ISP_IRQ_DISABLE); } void msm_vfe47_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { - vfe_dev->irq0_mask |= 1 << (stream_info->wm[0] + 8); - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe47_config_irq(vfe_dev, 1 << (stream_info->wm[0] + 8), 0, + MSM_ISP_IRQ_ENABLE); } void msm_vfe47_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { - vfe_dev->irq0_mask &= ~(1 << (stream_info->wm[0] + 8)); - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, vfe_dev->irq1_mask, - MSM_ISP_IRQ_SET); + msm_vfe47_config_irq(vfe_dev, (1 << (stream_info->wm[0] + 8)), 0, + MSM_ISP_IRQ_DISABLE); } void msm_vfe47_cfg_framedrop(void __iomem *vfe_base, @@ -1065,10 +1053,8 @@ void msm_vfe47_cfg_fetch_engine(struct vfe_device *vfe_dev, temp |= (1 << 1); msm_camera_io_w(temp, vfe_dev->vfe_base + 0x84); - vfe_dev->irq0_mask &= 0xFEFFFFFF; - vfe_dev->irq0_mask |= (1 << 24); - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, - vfe_dev->irq1_mask, MSM_ISP_IRQ_SET); + msm_vfe47_config_irq(vfe_dev, (1 << 24), 0, + MSM_ISP_IRQ_ENABLE); temp = fe_cfg->fetch_height - 1; msm_camera_io_w(temp & 0x3FFF, vfe_dev->vfe_base + 0x308); @@ -1394,9 +1380,11 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev, val = msm_camera_io_r(vfe_dev->vfe_base + 0x47C); if (update_state == ENABLE_CAMIF) { - vfe_dev->irq0_mask |= 0xF5; - msm_vfe47_config_irq(vfe_dev, vfe_dev->irq0_mask, - vfe_dev->irq1_mask, MSM_ISP_IRQ_SET); + msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x64); + msm_camera_io_w(0x81, vfe_dev->vfe_base + 0x68); + msm_camera_io_w(0x1, vfe_dev->vfe_base + 0x58); + msm_vfe47_config_irq(vfe_dev, 0x15, 0x81, + MSM_ISP_IRQ_ENABLE); if ((vfe_dev->hvx_cmd > HVX_DISABLE) && (vfe_dev->hvx_cmd <= HVX_ROUND_TRIP)) @@ -1427,8 +1415,9 @@ void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev, /* For testgen always halt on camif boundary */ if (vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux == TESTGEN) update_state = DISABLE_CAMIF; - /* turn off all irq before camif disable */ - msm_vfe47_config_irq(vfe_dev, 0, 0, MSM_ISP_IRQ_SET); + /* turn off camif violation and error irqs */ + msm_vfe47_config_irq(vfe_dev, 0, 0x81, + MSM_ISP_IRQ_DISABLE); val = msm_camera_io_r(vfe_dev->vfe_base + 0x464); /* disable danger signal */ msm_camera_io_w_mb(val & ~(1 << 8), vfe_dev->vfe_base + 0x464); @@ -1896,6 +1885,8 @@ void msm_vfe47_stats_cfg_comp_mask( comp_mask_reg |= stats_mask << (request_comp_index * 16); atomic_set(stats_comp_mask, stats_mask | atomic_read(stats_comp_mask)); + msm_vfe47_config_irq(vfe_dev, 1 << (29 + request_comp_index), + 0, MSM_ISP_IRQ_ENABLE); } else { if (!(atomic_read(stats_comp_mask) & stats_mask)) return; @@ -1903,6 +1894,8 @@ void msm_vfe47_stats_cfg_comp_mask( atomic_set(stats_comp_mask, ~stats_mask & atomic_read(stats_comp_mask)); comp_mask_reg &= ~(stats_mask << (request_comp_index * 16)); + msm_vfe47_config_irq(vfe_dev, 1 << (29 + request_comp_index), + 0, MSM_ISP_IRQ_DISABLE); } msm_camera_io_w(comp_mask_reg, vfe_dev->vfe_base + 0x78); @@ -1919,49 +1912,39 @@ void msm_vfe47_stats_cfg_wm_irq_mask( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { - uint32_t irq_mask; - uint32_t irq_mask_1; - - irq_mask = vfe_dev->irq0_mask; - irq_mask_1 = vfe_dev->irq1_mask; - switch (STATS_IDX(stream_info->stream_handle)) { case STATS_COMP_IDX_AEC_BG: - irq_mask |= 1 << 15; + msm_vfe47_config_irq(vfe_dev, 1 << 15, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_HDR_BE: - irq_mask |= 1 << 16; + msm_vfe47_config_irq(vfe_dev, 1 << 16, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_BG: - irq_mask |= 1 << 17; + msm_vfe47_config_irq(vfe_dev, 1 << 17, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_BF: - irq_mask |= 1 << 18; - irq_mask_1 |= 1 << 26; + msm_vfe47_config_irq(vfe_dev, 1 << 18, 1 << 26, + MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_HDR_BHIST: - irq_mask |= 1 << 19; + msm_vfe47_config_irq(vfe_dev, 1 << 19, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_RS: - irq_mask |= 1 << 20; + msm_vfe47_config_irq(vfe_dev, 1 << 20, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_CS: - irq_mask |= 1 << 21; + msm_vfe47_config_irq(vfe_dev, 1 << 21, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_IHIST: - irq_mask |= 1 << 22; + msm_vfe47_config_irq(vfe_dev, 1 << 22, 0, MSM_ISP_IRQ_ENABLE); break; case STATS_COMP_IDX_BHIST: - irq_mask |= 1 << 23; + msm_vfe47_config_irq(vfe_dev, 1 << 23, 0, MSM_ISP_IRQ_ENABLE); break; default: pr_err("%s: Invalid stats idx %d\n", __func__, STATS_IDX(stream_info->stream_handle)); } - - msm_vfe47_config_irq(vfe_dev, irq_mask, irq_mask_1, MSM_ISP_IRQ_SET); - vfe_dev->irq0_mask = irq_mask; - vfe_dev->irq1_mask = irq_mask_1; } void msm_vfe47_stats_clear_wm_irq_mask( @@ -1975,41 +1958,37 @@ void msm_vfe47_stats_clear_wm_irq_mask( switch (STATS_IDX(stream_info->stream_handle)) { case STATS_COMP_IDX_AEC_BG: - irq_mask &= ~(1 << 15); + msm_vfe47_config_irq(vfe_dev, 1 << 15, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_HDR_BE: - irq_mask &= ~(1 << 16); + msm_vfe47_config_irq(vfe_dev, 1 << 16, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_BG: - irq_mask &= ~(1 << 17); + msm_vfe47_config_irq(vfe_dev, 1 << 17, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_BF: - irq_mask &= ~(1 << 18); - irq_mask_1 &= ~(1 << 26); + msm_vfe47_config_irq(vfe_dev, 1 << 18, 1 << 26, + MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_HDR_BHIST: - irq_mask &= ~(1 << 19); + msm_vfe47_config_irq(vfe_dev, 1 << 19, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_RS: - irq_mask &= ~(1 << 20); + msm_vfe47_config_irq(vfe_dev, 1 << 20, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_CS: - irq_mask &= ~(1 << 21); + msm_vfe47_config_irq(vfe_dev, 1 << 21, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_IHIST: - irq_mask &= ~(1 << 22); + msm_vfe47_config_irq(vfe_dev, 1 << 22, 0, MSM_ISP_IRQ_DISABLE); break; case STATS_COMP_IDX_BHIST: - irq_mask &= ~(1 << 23); + msm_vfe47_config_irq(vfe_dev, 1 << 23, 0, MSM_ISP_IRQ_DISABLE); break; default: pr_err("%s: Invalid stats idx %d\n", __func__, STATS_IDX(stream_info->stream_handle)); } - - msm_vfe47_config_irq(vfe_dev, irq_mask, irq_mask_1, MSM_ISP_IRQ_SET); - vfe_dev->irq0_mask = irq_mask; - vfe_dev->irq1_mask = irq_mask_1; } void msm_vfe47_stats_cfg_wm_reg( diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 3dd55e02826d..8721fc18eaa8 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -1188,15 +1188,9 @@ int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg) vfe_dev->vt_enable = stream_cfg_cmd->vt_enable; msm_isp_start_avtimer(); } - if (stream_info->num_planes > 1) { + if (stream_info->num_planes > 1) msm_isp_axi_reserve_comp_mask( &vfe_dev->axi_data, stream_info); - vfe_dev->hw_info->vfe_ops.axi_ops. - cfg_comp_mask(vfe_dev, stream_info); - } else { - vfe_dev->hw_info->vfe_ops.axi_ops. - cfg_wm_irq_mask(vfe_dev, stream_info); - } for (i = 0; i < stream_info->num_planes; i++) { vfe_dev->hw_info->vfe_ops.axi_ops. @@ -1252,14 +1246,8 @@ int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg) clear_wm_xbar_reg(vfe_dev, stream_info, i); } - if (stream_info->num_planes > 1) { - vfe_dev->hw_info->vfe_ops.axi_ops. - clear_comp_mask(vfe_dev, stream_info); + if (stream_info->num_planes > 1) msm_isp_axi_free_comp_mask(&vfe_dev->axi_data, stream_info); - } else { - vfe_dev->hw_info->vfe_ops.axi_ops. - clear_wm_irq_mask(vfe_dev, stream_info); - } vfe_dev->hw_info->vfe_ops.axi_ops.clear_framedrop(vfe_dev, stream_info); msm_isp_axi_free_wm(axi_data, stream_info); @@ -2617,6 +2605,13 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev, return rc; } spin_unlock_irqrestore(&stream_info->lock, flags); + if (stream_info->num_planes > 1) { + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_comp_mask(vfe_dev, stream_info); + } else { + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_wm_irq_mask(vfe_dev, stream_info); + } stream_info->state = START_PENDING; @@ -2733,6 +2728,13 @@ static int msm_isp_stop_axi_stream(struct vfe_device *vfe_dev, spin_unlock_irqrestore(&stream_info->lock, flags); wait_for_complete_for_this_stream = 0; + if (stream_info->num_planes > 1) + vfe_dev->hw_info->vfe_ops.axi_ops. + clear_comp_mask(vfe_dev, stream_info); + else + vfe_dev->hw_info->vfe_ops.axi_ops. + clear_wm_irq_mask(vfe_dev, stream_info); + stream_info->state = STOP_PENDING; if (!halt && !ext_read && diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c index e98c99fcb62d..4aef6b5c7f38 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c @@ -444,10 +444,6 @@ int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg) stream_info->framedrop_pattern = 0x1; stream_info->framedrop_period = framedrop_period - 1; - if (!stream_info->composite_flag) - vfe_dev->hw_info->vfe_ops.stats_ops. - cfg_wm_irq_mask(vfe_dev, stream_info); - if (stream_info->init_stats_frame_drop == 0) vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_reg(vfe_dev, stream_info); @@ -485,10 +481,6 @@ int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg) rc = msm_isp_cfg_stats_stream(vfe_dev, &stream_cfg_cmd); } - if (!stream_info->composite_flag) - vfe_dev->hw_info->vfe_ops.stats_ops. - clear_wm_irq_mask(vfe_dev, stream_info); - vfe_dev->hw_info->vfe_ops.stats_ops.clear_wm_reg(vfe_dev, stream_info); memset(stream_info, 0, sizeof(struct msm_vfe_stats_stream)); return 0; @@ -711,6 +703,9 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev, pr_err("%s: No buffer for stream%d\n", __func__, idx); return rc; } + if (!stream_info->composite_flag) + vfe_dev->hw_info->vfe_ops.stats_ops. + cfg_wm_irq_mask(vfe_dev, stream_info); if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) stream_info->state = STATS_START_PENDING; @@ -784,6 +779,10 @@ static int msm_isp_stop_stats_stream(struct vfe_device *vfe_dev, return -EINVAL; } + if (!stream_info->composite_flag) + vfe_dev->hw_info->vfe_ops.stats_ops. + clear_wm_irq_mask(vfe_dev, stream_info); + if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) stream_info->state = STATS_STOP_PENDING; else diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c index 57e389b4c497..b8840115b674 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c +++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c @@ -1009,15 +1009,9 @@ static int msm_jpegdma_s_crop(struct file *file, void *fh, if (crop->c.width % formats[ctx->format_idx].h_align) return -EINVAL; - if (crop->c.left % formats[ctx->format_idx].h_align) - return -EINVAL; - if (crop->c.height % formats[ctx->format_idx].v_align) return -EINVAL; - if (crop->c.top % formats[ctx->format_idx].v_align) - return -EINVAL; - ctx->crop = crop->c; if (atomic_read(&ctx->active)) ret = msm_jpegdma_update_hw_config(ctx); diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.h b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.h index 0a9cab6e4322..6a1205daf1d2 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.h +++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.h @@ -36,9 +36,9 @@ /* Dma input output size limitations */ #define MSM_JPEGDMA_MAX_WIDTH 65536 -#define MSM_JPEGDMA_MIN_WIDTH 32 +#define MSM_JPEGDMA_MIN_WIDTH 8 #define MSM_JPEGDMA_MAX_HEIGHT 65536 -#define MSM_JPEGDMA_MIN_HEIGHT 32 +#define MSM_JPEGDMA_MIN_HEIGHT 8 #define MSM_JPEGDMA_STRIDE_ALIGN 8 /* diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/Makefile b/drivers/media/platform/msm/camera_v2/pproc/cpp/Makefile index 56880f4d5676..2198352143f7 100644 --- a/drivers/media/platform/msm/camera_v2/pproc/cpp/Makefile +++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/Makefile @@ -2,4 +2,5 @@ ccflags-y += -Idrivers/media/platform/msm/camera_v2 ccflags-y += -Idrivers/media/platform/msm/camera_v2/isp/ ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io ccflags-y += -Idrivers/media/platform/msm/camera_v2/common +ccflags-y += -Idrivers/media/platform/msm/camera_v2/msm_buf_mgr/ obj-$(CONFIG_MSM_CPP) += msm_cpp_soc.o msm_cpp.o diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c index 32eb883ed6c0..5a1e99adc7f3 100644 --- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c +++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c @@ -83,8 +83,27 @@ if (IS_BATCH_BUFFER_ON_PREVIEW(new_frame)) \ iden = swap_iden; \ } + +#define SWAP_BUF_INDEX_FOR_BATCH_ON_PREVIEW(new_frame, buff_mgr_info, \ + cur_index, swap_index) { \ + if (IS_BATCH_BUFFER_ON_PREVIEW(new_frame)) \ + buff_mgr_info.index = swap_index; \ + else \ + buff_mgr_info.index = cur_index; \ +} + +/* + * Default value for get buf to be used - 0xFFFFFFFF + * 0 is a valid index + * no valid index from userspace, use last buffer from queue. + */ +#define DEFAULT_OUTPUT_BUF_INDEX 0xFFFFFFFF +#define IS_DEFAULT_OUTPUT_BUF_INDEX(index) \ + ((index == DEFAULT_OUTPUT_BUF_INDEX) ? 1 : 0) + static int msm_cpp_buffer_ops(struct cpp_device *cpp_dev, - uint32_t buff_mgr_ops, struct msm_buf_mngr_info *buff_mgr_info); + uint32_t buff_mgr_ops, uint32_t ids, void *arg); + static int msm_cpp_send_frame_to_hardware(struct cpp_device *cpp_dev, struct msm_queue_cmd *frame_qcmd); static int msm_cpp_send_command_to_hardware(struct cpp_device *cpp_dev, @@ -92,6 +111,9 @@ static int msm_cpp_send_command_to_hardware(struct cpp_device *cpp_dev, static int msm_cpp_update_gdscr_status(struct cpp_device *cpp_dev, bool status); +static int msm_cpp_buffer_private_ops(struct cpp_device *cpp_dev, + uint32_t buff_mgr_ops, uint32_t id, void *arg); + #if CONFIG_MSM_CPP_DBG #define CPP_DBG(fmt, args...) pr_err(fmt, ##args) #else @@ -802,12 +824,9 @@ static int cpp_init_hardware(struct cpp_device *cpp_dev) pr_err("%s: irq request fail\n", __func__); goto req_irq_fail; } - cpp_dev->buf_mgr_subdev = msm_buf_mngr_get_subdev(); - - rc = msm_cpp_buffer_ops(cpp_dev, - VIDIOC_MSM_BUF_MNGR_INIT, NULL); + rc = msm_cam_buf_mgr_register_ops(&cpp_dev->buf_mgr_ops); if (rc < 0) { - pr_err("buf mngr init failed\n"); + pr_err("buf mngr req ops failed\n"); msm_camera_unregister_irq(cpp_dev->pdev, cpp_dev->irq, cpp_dev); goto req_irq_fail; @@ -878,12 +897,6 @@ static void cpp_release_hardware(struct cpp_device *cpp_dev) { int32_t rc; if (cpp_dev->state != CPP_STATE_BOOT) { - rc = msm_cpp_buffer_ops(cpp_dev, - VIDIOC_MSM_BUF_MNGR_DEINIT, NULL); - if (rc < 0) { - pr_err("error in buf mngr deinit\n"); - rc = -EINVAL; - } msm_camera_unregister_irq(cpp_dev->pdev, cpp_dev->irq, cpp_dev); tasklet_kill(&cpp_dev->cpp_tasklet); atomic_set(&cpp_dev->irq_cnt, 0); @@ -1193,12 +1206,28 @@ static const struct v4l2_subdev_internal_ops msm_cpp_internal_ops = { }; static int msm_cpp_buffer_ops(struct cpp_device *cpp_dev, - uint32_t buff_mgr_ops, struct msm_buf_mngr_info *buff_mgr_info) + uint32_t buff_mgr_ops, uint32_t ids, + void *arg) { int rc = -EINVAL; - rc = v4l2_subdev_call(cpp_dev->buf_mgr_subdev, core, ioctl, - buff_mgr_ops, buff_mgr_info); + switch (buff_mgr_ops) { + case VIDIOC_MSM_BUF_MNGR_IOCTL_CMD: { + rc = msm_cpp_buffer_private_ops(cpp_dev, buff_mgr_ops, + ids, arg); + break; + } + case VIDIOC_MSM_BUF_MNGR_PUT_BUF: + case VIDIOC_MSM_BUF_MNGR_BUF_DONE: + case VIDIOC_MSM_BUF_MNGR_GET_BUF: + default: { + struct msm_buf_mngr_info *buff_mgr_info = + (struct msm_buf_mngr_info *)arg; + rc = cpp_dev->buf_mgr_ops.msm_cam_buf_mgr_ops(buff_mgr_ops, + buff_mgr_info); + break; + } + } if (rc < 0) pr_debug("%s: line %d rc = %d\n", __func__, __LINE__, rc); return rc; @@ -1242,9 +1271,9 @@ static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev, SWAP_IDENTITY_FOR_BATCH_ON_PREVIEW(processed_frame, iden, processed_frame->duplicate_identity); - memset(&buff_mgr_info, 0 , sizeof(struct msm_buf_mngr_info)); + buff_mgr_info.session_id = ((iden >> 16) & 0xFFFF); buff_mgr_info.stream_id = (iden & 0xFFFF); buff_mgr_info.frame_id = processed_frame->frame_id; @@ -1262,7 +1291,7 @@ static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev, if (put_buf) { rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_PUT_BUF, - &buff_mgr_info); + 0x0, &buff_mgr_info); if (rc < 0) { pr_err("error putting buffer\n"); rc = -EINVAL; @@ -1270,7 +1299,7 @@ static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev, } else { rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_BUF_DONE, - &buff_mgr_info); + 0x0, &buff_mgr_info); if (rc < 0) { pr_err("error putting buffer\n"); rc = -EINVAL; @@ -1289,6 +1318,7 @@ static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev, memset(&buff_mgr_info, 0 , sizeof(struct msm_buf_mngr_info)); + buff_mgr_info.session_id = ((iden >> 16) & 0xFFFF); buff_mgr_info.stream_id = (iden & 0xFFFF); buff_mgr_info.frame_id = processed_frame->frame_id; @@ -1298,7 +1328,7 @@ static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev, if (put_buf) { rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_PUT_BUF, - &buff_mgr_info); + 0x0, &buff_mgr_info); if (rc < 0) { pr_err("error putting buffer\n"); rc = -EINVAL; @@ -1306,7 +1336,7 @@ static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev, } else { rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_BUF_DONE, - &buff_mgr_info); + 0x0, &buff_mgr_info); if (rc < 0) { pr_err("error putting buffer\n"); rc = -EINVAL; @@ -2158,6 +2188,8 @@ static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev, uint8_t tnr_enabled; enum msm_camera_buf_mngr_buf_type buf_type = MSM_CAMERA_BUF_MNGR_BUF_PLANAR; + uint32_t ioctl_cmd, idx; + uint32_t op_index, dup_index; stripe_base = cpp_dev->payload_params.stripe_base; stripe_size = cpp_dev->payload_params.stripe_size; @@ -2212,8 +2244,12 @@ static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev, goto frame_msg_err; } + op_index = new_frame->output_buffer_info[0].index; + dup_index = new_frame->duplicate_buffer_info.index; + if (new_frame->we_disable == 0) { int32_t iden = new_frame->identity; + if ((new_frame->output_buffer_info[0].native_buff == 0) && (new_frame->first_payload)) { memset(&buff_mgr_info, 0, @@ -2226,16 +2262,32 @@ static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev, SWAP_IDENTITY_FOR_BATCH_ON_PREVIEW(new_frame, iden, new_frame->duplicate_identity); + /* + * Swap the input buffer index for batch mode with + * buffer on preview + */ + SWAP_BUF_INDEX_FOR_BATCH_ON_PREVIEW(new_frame, + buff_mgr_info, op_index, dup_index); + buff_mgr_info.session_id = ((iden >> 16) & 0xFFFF); buff_mgr_info.stream_id = (iden & 0xFFFF); buff_mgr_info.type = buf_type; + + if (IS_DEFAULT_OUTPUT_BUF_INDEX(buff_mgr_info.index)) { + ioctl_cmd = VIDIOC_MSM_BUF_MNGR_GET_BUF; + idx = 0x0; + } else { + ioctl_cmd = VIDIOC_MSM_BUF_MNGR_IOCTL_CMD; + idx = + MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX; + } rc = msm_cpp_buffer_ops(cpp_dev, - VIDIOC_MSM_BUF_MNGR_GET_BUF, - &buff_mgr_info); + ioctl_cmd, idx, &buff_mgr_info); if (rc < 0) { rc = -EAGAIN; - pr_debug("%s: error getting buffer rc:%d\n", - __func__, rc); + pr_debug("%s:get_buf err rc:%d, index %d\n", + __func__, rc, + new_frame->output_buffer_info[0].index); goto frame_msg_err; } num_output_bufs = @@ -2273,16 +2325,32 @@ static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev, iden, new_frame->identity); memset(&dup_buff_mgr_info, 0, sizeof(struct msm_buf_mngr_info)); + + /* + * Swap the input buffer index for batch mode with + * buffer on preview + */ + SWAP_BUF_INDEX_FOR_BATCH_ON_PREVIEW(new_frame, + dup_buff_mgr_info, dup_index, op_index); + dup_buff_mgr_info.session_id = ((iden >> 16) & 0xFFFF); dup_buff_mgr_info.stream_id = (iden & 0xFFFF); dup_buff_mgr_info.type = MSM_CAMERA_BUF_MNGR_BUF_PLANAR; - rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_GET_BUF, + if (IS_DEFAULT_OUTPUT_BUF_INDEX(dup_buff_mgr_info.index)) { + ioctl_cmd = VIDIOC_MSM_BUF_MNGR_GET_BUF; + idx = 0x0; + } else { + ioctl_cmd = VIDIOC_MSM_BUF_MNGR_IOCTL_CMD; + idx = MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX; + } + rc = msm_cpp_buffer_ops(cpp_dev, ioctl_cmd, idx, &dup_buff_mgr_info); if (rc < 0) { rc = -EAGAIN; - pr_debug("%s: error getting buffer rc:%d\n", - __func__, rc); + pr_debug("%s: get_buf err rc:%d, index %d\n", + __func__, rc, + new_frame->duplicate_buffer_info.index); goto phyaddr_err; } new_frame->duplicate_buffer_info.index = @@ -2296,7 +2364,7 @@ static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev, pr_err("error gettting output physical address\n"); rc = -EINVAL; msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_PUT_BUF, - &dup_buff_mgr_info); + 0x0, &dup_buff_mgr_info); goto phyaddr_err; } /* set duplicate enable bit */ @@ -2374,7 +2442,7 @@ qcmd_err: phyaddr_err: if (new_frame->output_buffer_info[0].native_buff == 0) msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_PUT_BUF, - &buff_mgr_info); + 0x0, &buff_mgr_info); frame_msg_err: kfree(cpp_frame_msg); kfree(new_frame); @@ -2985,11 +3053,11 @@ STREAM_BUFF_END: if (queue_buf_info.is_buf_dirty) { rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_PUT_BUF, - &queue_buf_info.buff_mgr_info); + 0x0, &queue_buf_info.buff_mgr_info); } else { rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_BUF_DONE, - &queue_buf_info.buff_mgr_info); + 0x0, &queue_buf_info.buff_mgr_info); } if (rc < 0) { pr_err("error in buf done\n"); @@ -3001,6 +3069,7 @@ STREAM_BUFF_END: case VIDIOC_MSM_CPP_POP_STREAM_BUFFER: { struct msm_buf_mngr_info buff_mgr_info; struct msm_cpp_frame_info_t frame_info; + uint32_t ioctl_cmd, idx; if (ioctl_ptr->ioctl_ptr == NULL || (ioctl_ptr->len != sizeof(struct msm_cpp_frame_info_t))) { @@ -3020,16 +3089,25 @@ STREAM_BUFF_END: buff_mgr_info.stream_id = (frame_info.identity & 0xFFFF); buff_mgr_info.type = MSM_CAMERA_BUF_MNGR_BUF_PLANAR; - rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_GET_BUF, + if (IS_DEFAULT_OUTPUT_BUF_INDEX( + frame_info.output_buffer_info[0].index)) { + ioctl_cmd = VIDIOC_MSM_BUF_MNGR_GET_BUF; + idx = 0x0; + } else { + ioctl_cmd = VIDIOC_MSM_BUF_MNGR_IOCTL_CMD; + idx = MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX; + } + rc = msm_cpp_buffer_ops(cpp_dev, ioctl_cmd, idx, &buff_mgr_info); if (rc < 0) { rc = -EAGAIN; - pr_err_ratelimited("error getting buffer rc:%d\n", rc); + pr_err_ratelimited("POP: get_buf err rc:%d, index %d\n", + rc, frame_info.output_buffer_info[0].index); break; } buff_mgr_info.frame_id = frame_info.frame_id; rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_BUF_DONE, - &buff_mgr_info); + 0x0, &buff_mgr_info); if (rc < 0) { pr_err("error in buf done\n"); rc = -EAGAIN; @@ -3813,6 +3891,43 @@ static void msm_cpp_set_vbif_reg_values(struct cpp_device *cpp_dev) } } +static int msm_cpp_buffer_private_ops(struct cpp_device *cpp_dev, + uint32_t buff_mgr_ops, uint32_t id, void *arg) { + + int32_t rc = 0; + + switch (id) { + case MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX: { + struct msm_camera_private_ioctl_arg ioctl_arg; + struct msm_buf_mngr_info *buff_mgr_info = + (struct msm_buf_mngr_info *)arg; + + ioctl_arg.id = MSM_CAMERA_BUF_MNGR_IOCTL_ID_GET_BUF_BY_IDX; + ioctl_arg.size = sizeof(struct msm_buf_mngr_info); + ioctl_arg.result = 0; + ioctl_arg.reserved = 0x0; + ioctl_arg.ioctl_ptr = 0x0; + MSM_CAM_GET_IOCTL_ARG_PTR(&ioctl_arg.ioctl_ptr, &buff_mgr_info, + sizeof(void *)); + rc = cpp_dev->buf_mgr_ops.msm_cam_buf_mgr_ops(buff_mgr_ops, + &ioctl_arg); + /* Use VIDIOC_MSM_BUF_MNGR_GET_BUF if getbuf with indx fails */ + if (rc < 0) { + pr_err_ratelimited("get_buf_by_idx for %d err %d,use get_buf\n", + buff_mgr_info->index, rc); + rc = cpp_dev->buf_mgr_ops.msm_cam_buf_mgr_ops( + VIDIOC_MSM_BUF_MNGR_GET_BUF, buff_mgr_info); + } + break; + } + default: { + pr_err("unsupported buffer manager ioctl\n"); + break; + } + } + return rc; +} + static int cpp_probe(struct platform_device *pdev) { struct cpp_device *cpp_dev; diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h index 753c6ffd810a..1784e27b1e37 100644 --- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h +++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h @@ -19,6 +19,7 @@ #include <linux/platform_device.h> #include <linux/interrupt.h> #include <media/v4l2-subdev.h> +#include "msm_generic_buf_mgr.h" #include "msm_sd.h" #include "cam_soc_api.h" #include "cam_hw_ops.h" @@ -254,7 +255,7 @@ struct cpp_device { struct msm_cpp_buff_queue_info_t *buff_queue; uint32_t num_buffq; - struct v4l2_subdev *buf_mgr_subdev; + struct msm_cam_buf_mgr_req_ops buf_mgr_ops; uint32_t bus_client; uint32_t bus_idx; diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c index 84bd3fe3fb85..c12e95d3310a 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c +++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c @@ -756,9 +756,14 @@ static int32_t msm_flash_get_pmic_source_info( "qcom,current", &fctrl->flash_op_current[i]); if (rc < 0) { - pr_err("current: read failed\n"); - of_node_put(flash_src_node); - continue; + rc = of_property_read_u32(flash_src_node, + "qcom,current-ma", + &fctrl->flash_op_current[i]); + if (rc < 0) { + pr_err("current: read failed\n"); + of_node_put(flash_src_node); + continue; + } } /* Read max-current */ @@ -776,8 +781,13 @@ static int32_t msm_flash_get_pmic_source_info( "qcom,duration", &fctrl->flash_max_duration[i]); if (rc < 0) { - pr_err("duration: read failed\n"); - of_node_put(flash_src_node); + rc = of_property_read_u32(flash_src_node, + "qcom,duration-ms", + &fctrl->flash_max_duration[i]); + if (rc < 0) { + pr_err("duration: read failed\n"); + of_node_put(flash_src_node); + } /* Non-fatal; this property is optional */ } diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c index 9bf5738d838c..02b83c969958 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c @@ -283,6 +283,49 @@ static int32_t msm_sensor_fill_actuator_subdevid_by_name( return rc; } +static int32_t msm_sensor_fill_flash_subdevid_by_name( + struct msm_sensor_ctrl_t *s_ctrl) +{ + int32_t rc = 0; + struct device_node *src_node = NULL; + uint32_t val = 0, flash_name_len; + int32_t *flash_subdev_id; + struct msm_sensor_info_t *sensor_info; + struct device_node *of_node = s_ctrl->of_node; + + if (!of_node || !s_ctrl->sensordata->flash_name) + return -EINVAL; + + sensor_info = s_ctrl->sensordata->sensor_info; + flash_subdev_id = &sensor_info->subdev_id[SUB_MODULE_LED_FLASH]; + + *flash_subdev_id = -1; + + flash_name_len = strlen(s_ctrl->sensordata->flash_name); + if (flash_name_len >= MAX_SENSOR_NAME) + return -EINVAL; + + if (flash_name_len == 0) + return 0; + + src_node = of_parse_phandle(of_node, "qcom,led-flash-src", 0); + if (!src_node) { + CDBG("%s:%d src_node NULL\n", __func__, __LINE__); + } else { + rc = of_property_read_u32(src_node, "cell-index", &val); + CDBG("%s qcom,flash cell index %d, rc %d\n", __func__, + val, rc); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + return -EINVAL; + } + *flash_subdev_id = val; + of_node_put(src_node); + src_node = NULL; + } + return rc; +} + static int32_t msm_sensor_fill_ois_subdevid_by_name( struct msm_sensor_ctrl_t *s_ctrl) { @@ -872,6 +915,7 @@ CSID_TG: s_ctrl->sensordata->eeprom_name = slave_info->eeprom_name; s_ctrl->sensordata->actuator_name = slave_info->actuator_name; s_ctrl->sensordata->ois_name = slave_info->ois_name; + s_ctrl->sensordata->flash_name = slave_info->flash_name; /* * Update eeporm subdevice Id by input eeprom name */ @@ -895,6 +939,12 @@ CSID_TG: goto free_camera_info; } + rc = msm_sensor_fill_flash_subdevid_by_name(s_ctrl); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto free_camera_info; + } + /* Power up and probe sensor */ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl); if (rc < 0) { @@ -911,14 +961,6 @@ CSID_TG: s_ctrl->is_probe_succeed = 1; /* - * Update the subdevice id of flash-src based on availability in kernel. - */ - if (strlen(slave_info->flash_name) == 0) { - s_ctrl->sensordata->sensor_info-> - subdev_id[SUB_MODULE_LED_FLASH] = -1; - } - - /* * Create /dev/videoX node, comment for now until dummy /dev/videoX * node is created and used by HAL */ diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c index d58684109395..4a26ab920016 100644 --- a/drivers/media/platform/msm/vidc/hfi_packetization.c +++ b/drivers/media/platform/msm/vidc/hfi_packetization.c @@ -2116,6 +2116,14 @@ int create_pkt_cmd_session_set_property( pkt->size += sizeof(u32) + sizeof(struct hfi_enable); break; } + case HAL_PARAM_VENC_H264_TRANSFORM_8x8: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_H264_8X8_TRANSFORM, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } /* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */ case HAL_CONFIG_BUFFER_REQUIREMENTS: case HAL_CONFIG_PRIORITY: diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c index 0e668b93598f..5c7408740e95 100644 --- a/drivers/media/platform/msm/vidc/msm_venc.c +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -869,7 +869,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { .name = "Ltr Mode", .type = V4L2_CTRL_TYPE_INTEGER, .minimum = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_DISABLE, - .maximum = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_PERIODIC, + .maximum = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_MANUAL, .default_value = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_DISABLE, .step = 1, .qmenu = NULL, @@ -1232,6 +1232,15 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { .default_value = 0, .step = 1, }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8, + .name = "Transform 8x8", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_DISABLE, + .maximum = V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE, + .default_value = V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE, + .step = 1, + }, }; #define NUM_CTRLS ARRAY_SIZE(msm_venc_ctrls) @@ -3100,6 +3109,24 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) pdata = &enable; break; } + case V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8: + property_id = HAL_PARAM_VENC_H264_TRANSFORM_8x8; + switch (ctrl->val) { + case V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE: + enable.enable = 1; + break; + case V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_DISABLE: + enable.enable = 0; + break; + default: + dprintk(VIDC_ERR, + "Invalid H264 8x8 transform control value %d\n", + ctrl->val); + rc = -ENOTSUPP; + break; + } + pdata = &enable; + break; default: dprintk(VIDC_ERR, "Unsupported index: %x\n", ctrl->id); rc = -ENOTSUPP; diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 05bfabce2bb2..40be56e874c3 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -86,6 +86,7 @@ struct getprop_buf { static void msm_comm_generate_session_error(struct msm_vidc_inst *inst); static void msm_comm_generate_sys_error(struct msm_vidc_inst *inst); static void handle_session_error(enum hal_command_response cmd, void *data); +static void msm_vidc_print_running_insts(struct msm_vidc_core *core); bool msm_comm_turbo_session(struct msm_vidc_inst *inst) { @@ -879,11 +880,13 @@ static int wait_for_sess_signal_receipt(struct msm_vidc_inst *inst, enum hal_command_response cmd) { int rc = 0; + struct hfi_device *hdev; if (!IS_HAL_SESSION_CMD(cmd)) { dprintk(VIDC_ERR, "Invalid inst cmd response: %d\n", cmd); return -EINVAL; } + hdev = (struct hfi_device *)(inst->core->device); rc = wait_for_completion_timeout( &inst->completions[SESSION_MSG_INDEX(cmd)], msecs_to_jiffies(msm_vidc_hw_rsp_timeout)); @@ -891,7 +894,11 @@ static int wait_for_sess_signal_receipt(struct msm_vidc_inst *inst, dprintk(VIDC_ERR, "Wait interrupted or timed out: %d\n", SESSION_MSG_INDEX(cmd)); msm_comm_kill_session(inst); - BUG_ON(msm_vidc_debug_timeout); + call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data); + dprintk(VIDC_ERR, + "sess resp timeout can potentially crash the system\n"); + + BUG_ON(inst->core->resources.debug_timeout); rc = -EIO; } else { rc = 0; @@ -1623,6 +1630,13 @@ static void handle_sys_error(enum hal_command_response cmd, void *data) core->state = VIDC_CORE_UNINIT; } mutex_unlock(&core->lock); + + msm_vidc_print_running_insts(core); + call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data); + dprintk(VIDC_ERR, + "SYS_ERROR can potentially crash the system\n"); + + BUG_ON(core->resources.debug_timeout); } void msm_comm_session_clean(struct msm_vidc_inst *inst) @@ -2393,7 +2407,11 @@ static int msm_comm_session_abort(struct msm_vidc_inst *inst) dprintk(VIDC_ERR, "%s: Wait interrupted or timed out [%p]: %d\n", __func__, inst, abort_completion); - BUG_ON(msm_vidc_debug_timeout); + call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data); + dprintk(VIDC_ERR, + "ABORT timeout can potentially crash the system\n"); + + BUG_ON(inst->core->resources.debug_timeout); rc = -EBUSY; } else { rc = 0; @@ -2463,6 +2481,7 @@ void msm_comm_handle_thermal_event() int msm_comm_check_core_init(struct msm_vidc_core *core) { int rc = 0; + struct hfi_device *hdev; mutex_lock(&core->lock); if (core->state >= VIDC_CORE_INIT_DONE) { @@ -2471,13 +2490,18 @@ int msm_comm_check_core_init(struct msm_vidc_core *core) goto exit; } dprintk(VIDC_DBG, "Waiting for SYS_INIT_DONE\n"); + hdev = (struct hfi_device *)core->device; rc = wait_for_completion_timeout( &core->completions[SYS_MSG_INDEX(HAL_SYS_INIT_DONE)], msecs_to_jiffies(msm_vidc_hw_rsp_timeout)); if (!rc) { dprintk(VIDC_ERR, "%s: Wait interrupted or timed out: %d\n", __func__, SYS_MSG_INDEX(HAL_SYS_INIT_DONE)); - BUG_ON(msm_vidc_debug_timeout); + call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data); + dprintk(VIDC_ERR, + "SYS_INIT timeout can potentially crash the system\n"); + + BUG_ON(core->resources.debug_timeout); rc = -EIO; goto exit; } else { @@ -3944,7 +3968,11 @@ int msm_comm_try_get_prop(struct msm_vidc_inst *inst, enum hal_property ptype, SESSION_MSG_INDEX(HAL_SESSION_PROPERTY_INFO)); inst->state = MSM_VIDC_CORE_INVALID; msm_comm_kill_session(inst); - BUG_ON(msm_vidc_debug_timeout); + call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data); + dprintk(VIDC_ERR, + "SESS_PROP timeout can potentially crash the system\n"); + + BUG_ON(inst->core->resources.debug_timeout); rc = -ETIMEDOUT; goto exit; } else { diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c index 4503e46b044d..2bb91ccc6c26 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c @@ -1103,6 +1103,11 @@ int read_platform_resources_from_dt( res->never_unload_fw = of_property_read_bool(pdev->dev.of_node, "qcom,never-unload-fw"); + res->debug_timeout = of_property_read_bool(pdev->dev.of_node, + "qcom,debug-timeout"); + + res->debug_timeout |= msm_vidc_debug_timeout; + of_property_read_u32(pdev->dev.of_node, "qcom,pm-qos-latency-us", &res->pm_qos_latency_us); diff --git a/drivers/media/platform/msm/vidc/msm_vidc_resources.h b/drivers/media/platform/msm/vidc/msm_vidc_resources.h index 1fc1888e81c6..c61605c7e405 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_resources.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_resources.h @@ -185,6 +185,7 @@ struct msm_vidc_platform_resources { const char *fw_name; const char *hfi_version; bool never_unload_fw; + bool debug_timeout; uint32_t pm_qos_latency_us; uint32_t max_inst_count; uint32_t max_secure_inst_count; diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c index ac53b3bcb4ed..20e217cc0445 100644 --- a/drivers/media/platform/msm/vidc/venus_hfi.c +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -1311,6 +1311,29 @@ static int venus_hfi_suspend(void *dev) return rc; } +static int venus_hfi_flush_debug_queue(void *dev) +{ + int rc = 0; + struct venus_hfi_device *device = (struct venus_hfi_device *) dev; + + if (!device) { + dprintk(VIDC_ERR, "%s invalid device\n", __func__); + return -EINVAL; + } + + mutex_lock(&device->lock); + + if (device->power_enabled) { + dprintk(VIDC_DBG, "Venus is busy\n"); + rc = -EBUSY; + goto exit; + } + __flush_debug_queue(device, NULL); +exit: + mutex_unlock(&device->lock); + return rc; +} + static enum hal_default_properties venus_hfi_get_default_properties(void *dev) { enum hal_default_properties prop = 0; @@ -3322,6 +3345,7 @@ static void __process_sys_error(struct venus_hfi_device *device) static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet) { bool local_packet = false; + enum vidc_msg_prio log_level = VIDC_FW; if (!device) { dprintk(VIDC_ERR, "%s: Invalid params\n", __func__); @@ -3337,6 +3361,13 @@ static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet) } local_packet = true; + + /* + * Local packek is used when something FATAL occurred. + * It is good to print these logs by default. + */ + + log_level = VIDC_ERR; } while (!__iface_dbgq_read(device, packet)) { @@ -3353,7 +3384,7 @@ static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet) } else { struct hfi_msg_sys_debug_packet *pkt = (struct hfi_msg_sys_debug_packet *) packet; - dprintk(VIDC_FW, "%s", pkt->rg_msg_data); + dprintk(log_level, "%s", pkt->rg_msg_data); } } @@ -4629,6 +4660,7 @@ static void venus_init_hfi_callbacks(struct hfi_device *hdev) hdev->get_fw_info = venus_hfi_get_fw_info; hdev->get_core_capabilities = venus_hfi_get_core_capabilities; hdev->suspend = venus_hfi_suspend; + hdev->flush_debug_queue = venus_hfi_flush_debug_queue; hdev->get_core_clock_rate = venus_hfi_get_core_clock_rate; hdev->get_default_properties = venus_hfi_get_default_properties; } diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h index 624fd53debe8..aba1d04726e4 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h @@ -237,6 +237,7 @@ enum hal_property { HAL_PARAM_VENC_CONSTRAINED_INTRA_PRED, HAL_CONFIG_VENC_BLUR_RESOLUTION, HAL_PARAM_VENC_SESSION_QP_RANGE_PACKED, + HAL_PARAM_VENC_H264_TRANSFORM_8x8, }; enum hal_domain { @@ -1498,6 +1499,7 @@ struct hfi_device { int (*session_clean)(void *sess); int (*get_core_capabilities)(void *dev); int (*suspend)(void *dev); + int (*flush_debug_queue)(void *dev); unsigned long (*get_core_clock_rate)(void *dev, bool actual_rate); enum hal_default_properties (*get_default_properties)(void *dev); }; diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h index a2d95927e400..ff043e9a819b 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h @@ -364,6 +364,8 @@ struct hfi_buffer_info { (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x022) #define HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY \ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x023) +#define HFI_PROPERTY_PARAM_VENC_H264_8X8_TRANSFORM \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x025) #define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER \ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x026) #define HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP \ diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 6310acab60e7..d41ae950d1a1 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -154,6 +154,7 @@ static int sru_s_stream(struct v4l2_subdev *subdev, int enable) mutex_lock(sru->ctrls.lock); ctrl0 |= vsp1_sru_read(sru, VI6_SRU_CTRL0) & (VI6_SRU_CTRL0_PARAM0_MASK | VI6_SRU_CTRL0_PARAM1_MASK); + vsp1_sru_write(sru, VI6_SRU_CTRL0, ctrl0); mutex_unlock(sru->ctrls.lock); vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5); diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index 0934024fb89d..d91ded795c93 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -159,7 +159,7 @@ static void au0828_usb_disconnect(struct usb_interface *interface) Set the status so poll routines can check and avoid access after disconnect. */ - dev->dev_state = DEV_DISCONNECTED; + set_bit(DEV_DISCONNECTED, &dev->dev_state); au0828_rc_unregister(dev); /* Digital TV */ diff --git a/drivers/media/usb/au0828/au0828-input.c b/drivers/media/usb/au0828/au0828-input.c index b0f067971979..3d6687f0407d 100644 --- a/drivers/media/usb/au0828/au0828-input.c +++ b/drivers/media/usb/au0828/au0828-input.c @@ -130,7 +130,7 @@ static int au0828_get_key_au8522(struct au0828_rc *ir) bool first = true; /* do nothing if device is disconnected */ - if (ir->dev->dev_state == DEV_DISCONNECTED) + if (test_bit(DEV_DISCONNECTED, &ir->dev->dev_state)) return 0; /* Check IR int */ @@ -260,7 +260,7 @@ static void au0828_rc_stop(struct rc_dev *rc) cancel_delayed_work_sync(&ir->work); /* do nothing if device is disconnected */ - if (ir->dev->dev_state != DEV_DISCONNECTED) { + if (!test_bit(DEV_DISCONNECTED, &ir->dev->dev_state)) { /* Disable IR */ au8522_rc_clear(ir, 0xe0, 1 << 4); } diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 45c622e234f7..7b2fe1b56039 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -104,14 +104,13 @@ static inline void print_err_status(struct au0828_dev *dev, static int check_dev(struct au0828_dev *dev) { - if (dev->dev_state & DEV_DISCONNECTED) { + if (test_bit(DEV_DISCONNECTED, &dev->dev_state)) { pr_info("v4l2 ioctl: device not present\n"); return -ENODEV; } - if (dev->dev_state & DEV_MISCONFIGURED) { - pr_info("v4l2 ioctl: device is misconfigured; " - "close and open it again\n"); + if (test_bit(DEV_MISCONFIGURED, &dev->dev_state)) { + pr_info("v4l2 ioctl: device is misconfigured; close and open it again\n"); return -EIO; } return 0; @@ -519,8 +518,8 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) if (!dev) return 0; - if ((dev->dev_state & DEV_DISCONNECTED) || - (dev->dev_state & DEV_MISCONFIGURED)) + if (test_bit(DEV_DISCONNECTED, &dev->dev_state) || + test_bit(DEV_MISCONFIGURED, &dev->dev_state)) return 0; if (urb->status < 0) { @@ -766,10 +765,10 @@ static int au0828_stream_interrupt(struct au0828_dev *dev) int ret = 0; dev->stream_state = STREAM_INTERRUPT; - if (dev->dev_state == DEV_DISCONNECTED) + if (test_bit(DEV_DISCONNECTED, &dev->dev_state)) return -ENODEV; else if (ret) { - dev->dev_state = DEV_MISCONFIGURED; + set_bit(DEV_MISCONFIGURED, &dev->dev_state); dprintk(1, "%s device is misconfigured!\n", __func__); return ret; } @@ -958,7 +957,7 @@ static int au0828_v4l2_open(struct file *filp) int ret; dprintk(1, - "%s called std_set %d dev_state %d stream users %d users %d\n", + "%s called std_set %d dev_state %ld stream users %d users %d\n", __func__, dev->std_set_in_tuner_core, dev->dev_state, dev->streaming_users, dev->users); @@ -977,7 +976,7 @@ static int au0828_v4l2_open(struct file *filp) au0828_analog_stream_enable(dev); au0828_analog_stream_reset(dev); dev->stream_state = STREAM_OFF; - dev->dev_state |= DEV_INITIALIZED; + set_bit(DEV_INITIALIZED, &dev->dev_state); } dev->users++; mutex_unlock(&dev->lock); @@ -991,7 +990,7 @@ static int au0828_v4l2_close(struct file *filp) struct video_device *vdev = video_devdata(filp); dprintk(1, - "%s called std_set %d dev_state %d stream users %d users %d\n", + "%s called std_set %d dev_state %ld stream users %d users %d\n", __func__, dev->std_set_in_tuner_core, dev->dev_state, dev->streaming_users, dev->users); @@ -1007,7 +1006,7 @@ static int au0828_v4l2_close(struct file *filp) del_timer_sync(&dev->vbi_timeout); } - if (dev->dev_state == DEV_DISCONNECTED) + if (test_bit(DEV_DISCONNECTED, &dev->dev_state)) goto end; if (dev->users == 1) { @@ -1036,7 +1035,7 @@ static void au0828_init_tuner(struct au0828_dev *dev) .type = V4L2_TUNER_ANALOG_TV, }; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); if (dev->std_set_in_tuner_core) @@ -1108,7 +1107,7 @@ static int vidioc_querycap(struct file *file, void *priv, struct video_device *vdev = video_devdata(file); struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); strlcpy(cap->driver, "au0828", sizeof(cap->driver)); @@ -1151,7 +1150,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); f->fmt.pix.width = dev->width; @@ -1170,7 +1169,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); return au0828_set_format(dev, VIDIOC_TRY_FMT, f); @@ -1182,7 +1181,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct au0828_dev *dev = video_drvdata(file); int rc; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); rc = check_dev(dev); @@ -1204,7 +1203,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); if (norm == dev->std) @@ -1236,7 +1235,7 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); *norm = dev->std; @@ -1259,7 +1258,7 @@ static int vidioc_enum_input(struct file *file, void *priv, [AU0828_VMUX_DEBUG] = "tv debug" }; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); tmp = input->index; @@ -1289,7 +1288,7 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); *i = dev->ctrl_input; @@ -1300,7 +1299,7 @@ static void au0828_s_input(struct au0828_dev *dev, int index) { int i; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); switch (AUVI_INPUT(index).type) { @@ -1385,7 +1384,7 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); a->index = dev->ctrl_ainput; @@ -1405,7 +1404,7 @@ static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio if (a->index != dev->ctrl_ainput) return -EINVAL; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); return 0; } @@ -1417,7 +1416,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) if (t->index != 0) return -EINVAL; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); strcpy(t->name, "Auvitek tuner"); @@ -1437,7 +1436,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, if (t->index != 0) return -EINVAL; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); au0828_init_tuner(dev); @@ -1459,7 +1458,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, if (freq->tuner != 0) return -EINVAL; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); freq->frequency = dev->ctrl_freq; return 0; @@ -1474,7 +1473,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (freq->tuner != 0) return -EINVAL; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); au0828_init_tuner(dev); @@ -1500,7 +1499,7 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); format->fmt.vbi.samples_per_line = dev->vbi_width; @@ -1526,7 +1525,7 @@ static int vidioc_cropcap(struct file *file, void *priv, if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); cc->bounds.left = 0; @@ -1548,7 +1547,7 @@ static int vidioc_g_register(struct file *file, void *priv, { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); reg->val = au0828_read(dev, reg->reg); @@ -1561,7 +1560,7 @@ static int vidioc_s_register(struct file *file, void *priv, { struct au0828_dev *dev = video_drvdata(file); - dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); return au0828_writereg(dev, reg->reg, reg->val); diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index 60b59391ea2a..d1b6405a05a4 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -21,6 +21,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/bitops.h> #include <linux/usb.h> #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> @@ -122,9 +123,9 @@ enum au0828_stream_state { /* device state */ enum au0828_dev_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, - DEV_MISCONFIGURED = 0x04 + DEV_INITIALIZED = 0, + DEV_DISCONNECTED = 1, + DEV_MISCONFIGURED = 2 }; struct au0828_dev; @@ -248,7 +249,7 @@ struct au0828_dev { int input_type; int std_set_in_tuner_core; unsigned int ctrl_input; - enum au0828_dev_state dev_state; + long unsigned int dev_state; /* defined at enum au0828_dev_state */; enum au0828_stream_state stream_state; wait_queue_head_t open; diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index b79c36fd8cd2..58f23bcfe94e 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -91,6 +91,7 @@ static const struct usb_device_id pwc_device_table [] = { { USB_DEVICE(0x0471, 0x0312) }, { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */ { USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC PC Camera */ + { USB_DEVICE(0x0471, 0x032C) }, /* Philips SPC 880NC PC Camera */ { USB_DEVICE(0x069A, 0x0001) }, /* Askey */ { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */ { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */ @@ -811,6 +812,11 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id name = "Philips SPC 900NC webcam"; type_id = 740; break; + case 0x032C: + PWC_INFO("Philips SPC 880NC USB webcam detected.\n"); + name = "Philips SPC 880NC webcam"; + type_id = 740; + break; default: return -ENODEV; break; diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index b693206f66dd..d1dc1a198e3e 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -1463,9 +1463,23 @@ static int usbvision_probe(struct usb_interface *intf, if (usbvision_device_data[model].interface >= 0) interface = &dev->actconfig->interface[usbvision_device_data[model].interface]->altsetting[0]; - else + else if (ifnum < dev->actconfig->desc.bNumInterfaces) interface = &dev->actconfig->interface[ifnum]->altsetting[0]; + else { + dev_err(&intf->dev, "interface %d is invalid, max is %d\n", + ifnum, dev->actconfig->desc.bNumInterfaces - 1); + ret = -ENODEV; + goto err_usb; + } + + if (interface->desc.bNumEndpoints < 2) { + dev_err(&intf->dev, "interface %d has %d endpoints, but must" + " have minimum 2\n", ifnum, interface->desc.bNumEndpoints); + ret = -ENODEV; + goto err_usb; + } endpoint = &interface->endpoint[1].desc; + if (!usb_endpoint_xfer_isoc(endpoint)) { dev_err(&intf->dev, "%s: interface %d. has non-ISO endpoint!\n", __func__, ifnum); diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index fc9e4395c21d..2da7fd7deacd 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -425,7 +425,8 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user get_user(kp->index, &up->index) || get_user(kp->type, &up->type) || get_user(kp->flags, &up->flags) || - get_user(kp->memory, &up->memory)) + get_user(kp->memory, &up->memory) || + get_user(kp->length, &up->length)) return -EFAULT; if (V4L2_TYPE_IS_OUTPUT(kp->type)) @@ -437,9 +438,6 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user return -EFAULT; if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) { - if (get_user(kp->length, &up->length)) - return -EFAULT; - num_planes = kp->length; if (num_planes == 0) { kp->m.planes = NULL; @@ -472,16 +470,14 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user } else { switch (kp->memory) { case V4L2_MEMORY_MMAP: - if (get_user(kp->length, &up->length) || - get_user(kp->m.offset, &up->m.offset)) + if (get_user(kp->m.offset, &up->m.offset)) return -EFAULT; break; case V4L2_MEMORY_USERPTR: { compat_long_t tmp; - if (get_user(kp->length, &up->length) || - get_user(tmp, &up->m.userptr)) + if (get_user(tmp, &up->m.userptr)) return -EFAULT; kp->m.userptr = (unsigned long)compat_ptr(tmp); @@ -523,7 +519,8 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) || put_user(kp->sequence, &up->sequence) || put_user(kp->reserved2, &up->reserved2) || - put_user(kp->reserved, &up->reserved)) + put_user(kp->reserved, &up->reserved) || + put_user(kp->length, &up->length)) return -EFAULT; if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) { @@ -546,13 +543,11 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user } else { switch (kp->memory) { case V4L2_MEMORY_MMAP: - if (put_user(kp->length, &up->length) || - put_user(kp->m.offset, &up->m.offset)) + if (put_user(kp->m.offset, &up->m.offset)) return -EFAULT; break; case V4L2_MEMORY_USERPTR: - if (put_user(kp->length, &up->length) || - put_user(kp->m.userptr, &up->m.userptr)) + if (put_user(kp->m.userptr, &up->m.userptr)) return -EFAULT; break; case V4L2_MEMORY_OVERLAY: diff --git a/drivers/mfd/wcd934x-regmap.c b/drivers/mfd/wcd934x-regmap.c index 7f16f1f3f417..398f0086537a 100644 --- a/drivers/mfd/wcd934x-regmap.c +++ b/drivers/mfd/wcd934x-regmap.c @@ -1815,6 +1815,8 @@ static bool wcd934x_is_readable_register(struct device *dev, unsigned int reg) pg_num = reg >> 0x8; if (pg_num == 0x80) pg_num = WCD934X_PAGE_0X80; + else if (pg_num == 0x50) + pg_num = WCD934X_PAGE_0x50; else if (pg_num > 0xF) return false; @@ -1835,6 +1837,8 @@ static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg) pg_num = reg >> 0x8; if (pg_num == 0x80) pg_num = WCD934X_PAGE_0X80; + else if (pg_num == 0x50) + pg_num = WCD934X_PAGE_0x50; else if (pg_num > 0xF) return false; @@ -1844,6 +1848,15 @@ static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg) if (reg_tbl && reg_tbl[reg_offset] == WCD934X_READ) return true; + /* + * Need to mark volatile for registers that are writable but + * only few bits are read-only + */ + switch (reg) { + case WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL: + return true; + } + return false; } diff --git a/drivers/mfd/wcd934x-tables.c b/drivers/mfd/wcd934x-tables.c index ab5d18cd8493..db963d08b66e 100644 --- a/drivers/mfd/wcd934x-tables.c +++ b/drivers/mfd/wcd934x-tables.c @@ -1983,6 +1983,40 @@ const u8 wcd934x_page15_reg_access[WCD934X_PAGE_SIZE] = { WCD934X_READ_WRITE, }; +const u8 wcd934x_page_0x50_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE80_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_NOM_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_NOM_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_MAX_SVS2_STEP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_START)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_CPR_STATUS)] = WCD934X_READ_WRITE, +}; + const u8 wcd934x_page_0x80_reg_access[WCD934X_PAGE_SIZE] = { [WCD934X_REG(WCD934X_PAGE80_PAGE_REGISTER)] = WCD934X_READ_WRITE, [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_0)] = WCD934X_READ_WRITE, @@ -2116,5 +2150,6 @@ const u8 * const wcd934x_reg[WCD934X_NUM_PAGES] = { [WCD934X_PAGE_13] = wcd934x_page13_reg_access, [WCD934X_PAGE_14] = wcd934x_page14_reg_access, [WCD934X_PAGE_15] = wcd934x_page15_reg_access, + [WCD934X_PAGE_0x50] = wcd934x_page_0x50_reg_access, [WCD934X_PAGE_0X80] = wcd934x_page_0x80_reg_access, }; diff --git a/drivers/mfd/wcd9xxx-utils.c b/drivers/mfd/wcd9xxx-utils.c index 2160dfd063b1..22d61d96a11d 100644 --- a/drivers/mfd/wcd9xxx-utils.c +++ b/drivers/mfd/wcd9xxx-utils.c @@ -39,6 +39,10 @@ static enum wcd9xxx_intf_status wcd9xxx_intf = -1; static struct mfd_cell tavil_devs[] = { { + .name = "qcom-wcd-pinctrl", + .of_compatible = "qcom,wcd-pinctrl", + }, + { .name = "tavil_codec", }, }; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 055b19432e7c..8a08ca61062a 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -412,10 +412,6 @@ config TI_DAC7512 This driver can also be built as a module. If so, the module will be called ti_dac7512. -config UID_STAT - bool "UID based statistics tracking exported to /proc/uid_stat" - default n - config VMWARE_BALLOON tristate "VMware Balloon Driver" depends on VMWARE_VMCI && X86 && HYPERVISOR_GUEST diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index fa36baace850..6acb70964fb8 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -35,7 +35,6 @@ obj-$(CONFIG_ISL29020) += isl29020.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o obj-$(CONFIG_DS1682) += ds1682.o obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o -obj-$(CONFIG_UID_STAT) += uid_stat.o obj-$(CONFIG_C2PORT) += c2port/ obj-$(CONFIG_HMC6352) += hmc6352.o obj-y += eeprom/ diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c index 7d1b9e9121a9..0c6f1de2465b 100644 --- a/drivers/misc/hdcp.c +++ b/drivers/misc/hdcp.c @@ -456,6 +456,7 @@ struct hdcp_lib_handle { atomic_t hdcp_off; uint32_t session_id; bool legacy_app; + enum hdcp_device_type device_type; struct task_struct *thread; struct completion topo_wait; @@ -901,7 +902,7 @@ static int hdcp_lib_session_init(struct hdcp_lib_handle *handle) req_buf = (struct hdcp_lib_session_init_req *)handle->qseecom_handle->sbuf; req_buf->commandid = HDCP_SESSION_INIT; - req_buf->deviceid = HDCP_TXMTR_HDMI; + req_buf->deviceid = handle->device_type; rsp_buf = (struct hdcp_lib_session_init_rsp *) (handle->qseecom_handle->sbuf + QSEECOM_ALIGN(sizeof(struct hdcp_lib_session_init_req))); @@ -2060,6 +2061,7 @@ int hdcp_library_register(struct hdcp_register_data *data) handle->tethered = data->tethered; handle->hdcp_app_init = NULL; handle->hdcp_txmtr_init = NULL; + handle->device_type = data->device_type; pr_debug("tethered %d\n", handle->tethered); diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 0b05aa938799..1a173d0af694 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -53,6 +53,11 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, bus = cl->dev; mutex_lock(&bus->device_lock); + if (bus->dev_state != MEI_DEV_ENABLED) { + rets = -ENODEV; + goto out; + } + if (!mei_cl_is_connected(cl)) { rets = -ENODEV; goto out; @@ -109,6 +114,10 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) bus = cl->dev; mutex_lock(&bus->device_lock); + if (bus->dev_state != MEI_DEV_ENABLED) { + rets = -ENODEV; + goto out; + } cb = mei_cl_read_cb(cl, NULL); if (cb) diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_v2.c b/drivers/misc/qcom/qdsp6v2/q6audio_v2.c index 7eb6629b1e57..51ba23da1270 100644 --- a/drivers/misc/qcom/qdsp6v2/q6audio_v2.c +++ b/drivers/misc/qcom/qdsp6v2/q6audio_v2.c @@ -85,6 +85,13 @@ void audio_in_get_dsp_frames(void *priv, pr_debug("%s:session id %d: enc_framesotal_size=0x%8x\n", __func__, audio->ac->session, payload[4]); + /* Ensure the index is within max array size: FRAME_NUM */ + if (index >= FRAME_NUM) { + pr_err("%s: Invalid index %d\n", + __func__, index); + return; + } + audio->out_frame_info[index][0] = payload[9]; audio->out_frame_info[index][1] = payload[5]; diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 273728482227..c994f7e00a16 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -4152,41 +4152,80 @@ static int qseecom_reentrancy_send_resp(struct qseecom_dev_handle *data) return 0; } -static int __qseecom_send_modfd_resp(struct qseecom_dev_handle *data, - void __user *argp, bool is_64bit_addr) +static int __validate_send_modfd_resp_inputs(struct qseecom_dev_handle *data, + struct qseecom_send_modfd_listener_resp *resp, + struct qseecom_registered_listener_list *this_lstnr) { - struct qseecom_send_modfd_listener_resp resp; int i; - struct qseecom_registered_listener_list *this_lstnr = NULL; - if (copy_from_user(&resp, argp, sizeof(resp))) { - pr_err("copy_from_user failed"); + if (!data || !resp || !this_lstnr) { + pr_err("listener handle or resp msg is null\n"); return -EINVAL; } - this_lstnr = __qseecom_find_svc(data->listener.id); - if (this_lstnr == NULL) + + if (resp->resp_buf_ptr == NULL) { + pr_err("resp buffer is null\n"); + return -EINVAL; + } + /* validate resp buf length */ + if ((resp->resp_len == 0) || + (resp->resp_len > this_lstnr->sb_length)) { + pr_err("resp buf length %d not valid\n", resp->resp_len); return -EINVAL; + } - if (resp.resp_buf_ptr == NULL) { - pr_err("Invalid resp_buf_ptr\n"); + if ((uintptr_t)resp->resp_buf_ptr > (ULONG_MAX - resp->resp_len)) { + pr_err("Integer overflow in resp_len & resp_buf\n"); + return -EINVAL; + } + if ((uintptr_t)this_lstnr->user_virt_sb_base > + (ULONG_MAX - this_lstnr->sb_length)) { + pr_err("Integer overflow in user_virt_sb_base & sb_length\n"); + return -EINVAL; + } + /* validate resp buf */ + if (((uintptr_t)resp->resp_buf_ptr < + (uintptr_t)this_lstnr->user_virt_sb_base) || + ((uintptr_t)resp->resp_buf_ptr >= + ((uintptr_t)this_lstnr->user_virt_sb_base + + this_lstnr->sb_length)) || + (((uintptr_t)resp->resp_buf_ptr + resp->resp_len) > + ((uintptr_t)this_lstnr->user_virt_sb_base + + this_lstnr->sb_length))) { + pr_err("resp buf is out of shared buffer region\n"); return -EINVAL; } + /* validate offsets */ for (i = 0; i < MAX_ION_FD; i++) { - if (resp.ifd_data[i].cmd_buf_offset >= resp.resp_len) { + if (resp->ifd_data[i].cmd_buf_offset >= resp->resp_len) { pr_err("Invalid offset %d = 0x%x\n", - i, resp.ifd_data[i].cmd_buf_offset); + i, resp->ifd_data[i].cmd_buf_offset); return -EINVAL; } } - if ((resp.resp_buf_ptr < this_lstnr->user_virt_sb_base) || - ((uintptr_t)resp.resp_buf_ptr >= - ((uintptr_t)this_lstnr->user_virt_sb_base + - this_lstnr->sb_length))) { - pr_err("resp_buf_ptr address not within shared buffer\n"); + return 0; +} + +static int __qseecom_send_modfd_resp(struct qseecom_dev_handle *data, + void __user *argp, bool is_64bit_addr) +{ + struct qseecom_send_modfd_listener_resp resp; + struct qseecom_registered_listener_list *this_lstnr = NULL; + + if (copy_from_user(&resp, argp, sizeof(resp))) { + pr_err("copy_from_user failed"); return -EINVAL; } + + this_lstnr = __qseecom_find_svc(data->listener.id); + if (this_lstnr == NULL) + return -EINVAL; + + if (__validate_send_modfd_resp_inputs(data, &resp, this_lstnr)) + return -EINVAL; + resp.resp_buf_ptr = this_lstnr->sb_virt + (uintptr_t)(resp.resp_buf_ptr - this_lstnr->user_virt_sb_base); diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c deleted file mode 100644 index 8b8c9a22360b..000000000000 --- a/drivers/misc/uid_stat.c +++ /dev/null @@ -1,153 +0,0 @@ -/* drivers/misc/uid_stat.c - * - * Copyright (C) 2008 - 2009 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <asm/atomic.h> - -#include <linux/err.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/stat.h> -#include <linux/uid_stat.h> -#include <net/activity_stats.h> - -static DEFINE_SPINLOCK(uid_lock); -static LIST_HEAD(uid_list); -static struct proc_dir_entry *parent; - -struct uid_stat { - struct list_head link; - uid_t uid; - atomic_t tcp_rcv; - atomic_t tcp_snd; -}; - -static struct uid_stat *find_uid_stat(uid_t uid) { - struct uid_stat *entry; - - list_for_each_entry(entry, &uid_list, link) { - if (entry->uid == uid) { - return entry; - } - } - return NULL; -} - -static int uid_stat_atomic_int_show(struct seq_file *m, void *v) -{ - unsigned int bytes; - atomic_t *counter = m->private; - - bytes = (unsigned int) (atomic_read(counter) + INT_MIN); - seq_printf(m, "%u\n", bytes); - return seq_has_overflowed(m) ? -ENOSPC : 0; -} - -static int uid_stat_read_atomic_int_open(struct inode *inode, struct file *file) -{ - return single_open(file, uid_stat_atomic_int_show, PDE_DATA(inode)); -} - -static const struct file_operations uid_stat_read_atomic_int_fops = { - .open = uid_stat_read_atomic_int_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -/* Create a new entry for tracking the specified uid. */ -static struct uid_stat *create_stat(uid_t uid) { - struct uid_stat *new_uid; - /* Create the uid stat struct and append it to the list. */ - new_uid = kmalloc(sizeof(struct uid_stat), GFP_ATOMIC); - if (!new_uid) - return NULL; - - new_uid->uid = uid; - /* Counters start at INT_MIN, so we can track 4GB of network traffic. */ - atomic_set(&new_uid->tcp_rcv, INT_MIN); - atomic_set(&new_uid->tcp_snd, INT_MIN); - - list_add_tail(&new_uid->link, &uid_list); - return new_uid; -} - -static void create_stat_proc(struct uid_stat *new_uid) -{ - char uid_s[32]; - struct proc_dir_entry *entry; - sprintf(uid_s, "%d", new_uid->uid); - entry = proc_mkdir(uid_s, parent); - - /* Keep reference to uid_stat so we know what uid to read stats from. */ - proc_create_data("tcp_snd", S_IRUGO, entry, - &uid_stat_read_atomic_int_fops, &new_uid->tcp_snd); - - proc_create_data("tcp_rcv", S_IRUGO, entry, - &uid_stat_read_atomic_int_fops, &new_uid->tcp_rcv); -} - -static struct uid_stat *find_or_create_uid_stat(uid_t uid) -{ - struct uid_stat *entry; - unsigned long flags; - spin_lock_irqsave(&uid_lock, flags); - entry = find_uid_stat(uid); - if (entry) { - spin_unlock_irqrestore(&uid_lock, flags); - return entry; - } - entry = create_stat(uid); - spin_unlock_irqrestore(&uid_lock, flags); - if (entry) - create_stat_proc(entry); - return entry; -} - -int uid_stat_tcp_snd(uid_t uid, int size) { - struct uid_stat *entry; - activity_stats_update(); - entry = find_or_create_uid_stat(uid); - if (!entry) - return -1; - atomic_add(size, &entry->tcp_snd); - return 0; -} - -int uid_stat_tcp_rcv(uid_t uid, int size) { - struct uid_stat *entry; - activity_stats_update(); - entry = find_or_create_uid_stat(uid); - if (!entry) - return -1; - atomic_add(size, &entry->tcp_rcv); - return 0; -} - -static int __init uid_stat_init(void) -{ - parent = proc_mkdir("uid_stat", NULL); - if (!parent) { - pr_err("uid_stat: failed to create proc entry\n"); - return -1; - } - return 0; -} - -__initcall(uid_stat_init); diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index dafdbd99ce71..b116122c5767 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -935,6 +935,14 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, struct mmc_card *card; int err = 0, ioc_err = 0; + /* + * The caller must have CAP_SYS_RAWIO, and must be calling this on the + * whole block device, not on a partition. This prevents overspray + * between sibling partitions. + */ + if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains)) + return -EPERM; + idata = mmc_blk_ioctl_copy_from_user(ic_ptr); if (IS_ERR_OR_NULL(idata)) return PTR_ERR(idata); @@ -993,6 +1001,14 @@ static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev, int i, err = 0, ioc_err = 0; __u64 num_of_cmds; + /* + * The caller must have CAP_SYS_RAWIO, and must be calling this on the + * whole block device, not on a partition. This prevents overspray + * between sibling partitions. + */ + if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains)) + return -EPERM; + if (copy_from_user(&num_of_cmds, &user->num_of_cmds, sizeof(num_of_cmds))) return -EFAULT; @@ -1048,14 +1064,6 @@ cmd_err: static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { - /* - * The caller must have CAP_SYS_RAWIO, and must be calling this on the - * whole block device, not on a partition. This prevents overspray - * between sibling partitions. - */ - if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains)) - return -EPERM; - switch (cmd) { case MMC_IOC_CMD: return mmc_blk_ioctl_cmd(bdev, diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 1c1b45ef3faf..aad3243a48fc 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1436,6 +1436,12 @@ static int mmc_spi_probe(struct spi_device *spi) host->pdata->cd_debounce); if (status != 0) goto fail_add_host; + + /* The platform has a CD GPIO signal that may support + * interrupts, so let mmc_gpiod_request_cd_irq() decide + * if polling is needed or not. + */ + mmc->caps &= ~MMC_CAP_NEEDS_POLL; mmc_gpiod_request_cd_irq(mmc); } diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 45ee07d3a761..610154836d79 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -390,6 +390,7 @@ static int byt_sd_probe_slot(struct sdhci_pci_slot *slot) slot->cd_idx = 0; slot->cd_override_level = true; if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BXT_SD || + slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BXTM_SD || slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_APL_SD) slot->host->mmc_host_ops.get_cd = bxt_get_cd; @@ -1173,6 +1174,30 @@ static const struct pci_device_id pci_ids[] = { { .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BXTM_EMMC, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BXTM_SDIO, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BXTM_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sd, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, .device = PCI_DEVICE_ID_INTEL_APL_EMMC, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index d1a0b4db60db..89e7151684a1 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -28,6 +28,9 @@ #define PCI_DEVICE_ID_INTEL_BXT_SD 0x0aca #define PCI_DEVICE_ID_INTEL_BXT_EMMC 0x0acc #define PCI_DEVICE_ID_INTEL_BXT_SDIO 0x0ad0 +#define PCI_DEVICE_ID_INTEL_BXTM_SD 0x1aca +#define PCI_DEVICE_ID_INTEL_BXTM_EMMC 0x1acc +#define PCI_DEVICE_ID_INTEL_BXTM_SDIO 0x1ad0 #define PCI_DEVICE_ID_INTEL_APL_SD 0x5aca #define PCI_DEVICE_ID_INTEL_APL_EMMC 0x5acc #define PCI_DEVICE_ID_INTEL_APL_SDIO 0x5ad0 diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 36b75f048e76..5f7eac922c54 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -754,9 +754,20 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) if (!data) target_timeout = cmd->busy_timeout * 1000; else { - target_timeout = data->timeout_ns / 1000; - if (host->clock) - target_timeout += data->timeout_clks / host->clock; + target_timeout = DIV_ROUND_UP(data->timeout_ns, 1000); + if (host->clock && data->timeout_clks) { + unsigned long long val; + + /* + * data->timeout_clks is in units of clock cycles. + * host->clock is in Hz. target_timeout is in us. + * Hence, us = 1000000 * cycles / Hz. Round up. + */ + val = 1000000 * data->timeout_clks; + if (do_div(val, host->clock)) + target_timeout++; + target_timeout += val; + } } /* @@ -3921,14 +3932,14 @@ int sdhci_add_host(struct sdhci_host *host) if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT) host->timeout_clk *= 1000; + if (override_timeout_clk) + host->timeout_clk = override_timeout_clk; + mmc->max_busy_timeout = host->ops->get_max_timeout_count ? host->ops->get_max_timeout_count(host) : 1 << 27; mmc->max_busy_timeout /= host->timeout_clk; } - if (override_timeout_clk) - host->timeout_clk = override_timeout_clk; - mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index ad9ffea7d659..6234eab38ff3 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -397,38 +397,26 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host) } static struct dma_chan * -sh_mmcif_request_dma_one(struct sh_mmcif_host *host, - struct sh_mmcif_plat_data *pdata, - enum dma_transfer_direction direction) +sh_mmcif_request_dma_pdata(struct sh_mmcif_host *host, uintptr_t slave_id) { - struct dma_slave_config cfg = { 0, }; - struct dma_chan *chan; - void *slave_data = NULL; - struct resource *res; - struct device *dev = sh_mmcif_host_to_dev(host); dma_cap_mask_t mask; - int ret; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); + if (slave_id <= 0) + return NULL; - if (pdata) - slave_data = direction == DMA_MEM_TO_DEV ? - (void *)pdata->slave_id_tx : - (void *)pdata->slave_id_rx; - - chan = dma_request_slave_channel_compat(mask, shdma_chan_filter, - slave_data, dev, - direction == DMA_MEM_TO_DEV ? "tx" : "rx"); - - dev_dbg(dev, "%s: %s: got channel %p\n", __func__, - direction == DMA_MEM_TO_DEV ? "TX" : "RX", chan); + return dma_request_channel(mask, shdma_chan_filter, (void *)slave_id); +} - if (!chan) - return NULL; +static int sh_mmcif_dma_slave_config(struct sh_mmcif_host *host, + struct dma_chan *chan, + enum dma_transfer_direction direction) +{ + struct resource *res; + struct dma_slave_config cfg = { 0, }; res = platform_get_resource(host->pd, IORESOURCE_MEM, 0); - cfg.direction = direction; if (direction == DMA_DEV_TO_MEM) { @@ -439,38 +427,42 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host, cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; } - ret = dmaengine_slave_config(chan, &cfg); - if (ret < 0) { - dma_release_channel(chan); - return NULL; - } - - return chan; + return dmaengine_slave_config(chan, &cfg); } -static void sh_mmcif_request_dma(struct sh_mmcif_host *host, - struct sh_mmcif_plat_data *pdata) +static void sh_mmcif_request_dma(struct sh_mmcif_host *host) { struct device *dev = sh_mmcif_host_to_dev(host); host->dma_active = false; - if (pdata) { - if (pdata->slave_id_tx <= 0 || pdata->slave_id_rx <= 0) - return; - } else if (!dev->of_node) { - return; + /* We can only either use DMA for both Tx and Rx or not use it at all */ + if (IS_ENABLED(CONFIG_SUPERH) && dev->platform_data) { + struct sh_mmcif_plat_data *pdata = dev->platform_data; + + host->chan_tx = sh_mmcif_request_dma_pdata(host, + pdata->slave_id_tx); + host->chan_rx = sh_mmcif_request_dma_pdata(host, + pdata->slave_id_rx); + } else { + host->chan_tx = dma_request_slave_channel(dev, "tx"); + host->chan_rx = dma_request_slave_channel(dev, "rx"); } + dev_dbg(dev, "%s: got channel TX %p RX %p\n", __func__, host->chan_tx, + host->chan_rx); - /* We can only either use DMA for both Tx and Rx or not use it at all */ - host->chan_tx = sh_mmcif_request_dma_one(host, pdata, DMA_MEM_TO_DEV); - if (!host->chan_tx) - return; + if (!host->chan_tx || !host->chan_rx || + sh_mmcif_dma_slave_config(host, host->chan_tx, DMA_MEM_TO_DEV) || + sh_mmcif_dma_slave_config(host, host->chan_rx, DMA_DEV_TO_MEM)) + goto error; - host->chan_rx = sh_mmcif_request_dma_one(host, pdata, DMA_DEV_TO_MEM); - if (!host->chan_rx) { + return; + +error: + if (host->chan_tx) dma_release_channel(host->chan_tx); - host->chan_tx = NULL; - } + if (host->chan_rx) + dma_release_channel(host->chan_rx); + host->chan_tx = host->chan_rx = NULL; } static void sh_mmcif_release_dma(struct sh_mmcif_host *host) @@ -1102,7 +1094,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (ios->power_mode == MMC_POWER_UP) { if (!host->card_present) { /* See if we also get DMA */ - sh_mmcif_request_dma(host, dev->platform_data); + sh_mmcif_request_dma(host); host->card_present = true; } sh_mmcif_set_power(host, ios); diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 43b3392ffee7..652d01832873 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -2599,6 +2599,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) */ static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) { + struct onenand_chip *this = mtd->priv; int ret; ret = onenand_block_isbad(mtd, ofs); @@ -2610,7 +2611,7 @@ static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) } onenand_get_device(mtd, FL_WRITING); - ret = mtd_block_markbad(mtd, ofs); + ret = this->block_markbad(mtd, ofs); onenand_release_device(mtd); return ret; } diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 28bbca0af238..b3d70a7a5262 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3260,6 +3260,30 @@ static int bond_close(struct net_device *bond_dev) return 0; } +/* fold stats, assuming all rtnl_link_stats64 fields are u64, but + * that some drivers can provide 32bit values only. + */ +static void bond_fold_stats(struct rtnl_link_stats64 *_res, + const struct rtnl_link_stats64 *_new, + const struct rtnl_link_stats64 *_old) +{ + const u64 *new = (const u64 *)_new; + const u64 *old = (const u64 *)_old; + u64 *res = (u64 *)_res; + int i; + + for (i = 0; i < sizeof(*_res) / sizeof(u64); i++) { + u64 nv = new[i]; + u64 ov = old[i]; + + /* detects if this particular field is 32bit only */ + if (((nv | ov) >> 32) == 0) + res[i] += (u32)nv - (u32)ov; + else + res[i] += nv - ov; + } +} + static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev, struct rtnl_link_stats64 *stats) { @@ -3268,43 +3292,23 @@ static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev, struct list_head *iter; struct slave *slave; + spin_lock(&bond->stats_lock); memcpy(stats, &bond->bond_stats, sizeof(*stats)); - bond_for_each_slave(bond, slave, iter) { - const struct rtnl_link_stats64 *sstats = + rcu_read_lock(); + bond_for_each_slave_rcu(bond, slave, iter) { + const struct rtnl_link_stats64 *new = dev_get_stats(slave->dev, &temp); - struct rtnl_link_stats64 *pstats = &slave->slave_stats; - - stats->rx_packets += sstats->rx_packets - pstats->rx_packets; - stats->rx_bytes += sstats->rx_bytes - pstats->rx_bytes; - stats->rx_errors += sstats->rx_errors - pstats->rx_errors; - stats->rx_dropped += sstats->rx_dropped - pstats->rx_dropped; - - stats->tx_packets += sstats->tx_packets - pstats->tx_packets;; - stats->tx_bytes += sstats->tx_bytes - pstats->tx_bytes; - stats->tx_errors += sstats->tx_errors - pstats->tx_errors; - stats->tx_dropped += sstats->tx_dropped - pstats->tx_dropped; - - stats->multicast += sstats->multicast - pstats->multicast; - stats->collisions += sstats->collisions - pstats->collisions; - - stats->rx_length_errors += sstats->rx_length_errors - pstats->rx_length_errors; - stats->rx_over_errors += sstats->rx_over_errors - pstats->rx_over_errors; - stats->rx_crc_errors += sstats->rx_crc_errors - pstats->rx_crc_errors; - stats->rx_frame_errors += sstats->rx_frame_errors - pstats->rx_frame_errors; - stats->rx_fifo_errors += sstats->rx_fifo_errors - pstats->rx_fifo_errors; - stats->rx_missed_errors += sstats->rx_missed_errors - pstats->rx_missed_errors; - - stats->tx_aborted_errors += sstats->tx_aborted_errors - pstats->tx_aborted_errors; - stats->tx_carrier_errors += sstats->tx_carrier_errors - pstats->tx_carrier_errors; - stats->tx_fifo_errors += sstats->tx_fifo_errors - pstats->tx_fifo_errors; - stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors - pstats->tx_heartbeat_errors; - stats->tx_window_errors += sstats->tx_window_errors - pstats->tx_window_errors; + + bond_fold_stats(stats, new, &slave->slave_stats); /* save off the slave stats for the next run */ - memcpy(pstats, sstats, sizeof(*sstats)); + memcpy(&slave->slave_stats, new, sizeof(*new)); } + rcu_read_unlock(); + memcpy(&bond->bond_stats, stats, sizeof(*stats)); + spin_unlock(&bond->stats_lock); return stats; } @@ -4118,6 +4122,7 @@ void bond_setup(struct net_device *bond_dev) struct bonding *bond = netdev_priv(bond_dev); spin_lock_init(&bond->mode_lock); + spin_lock_init(&bond->stats_lock); bond->params = bonding_defaults; /* Initialize pointers */ diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 17f017ab4dac..0fb3f8de88e9 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1197,7 +1197,7 @@ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev, dev->stats.tx_bytes += tx_cb_ptr->skb->len; dma_unmap_single(&dev->dev, dma_unmap_addr(tx_cb_ptr, dma_addr), - tx_cb_ptr->skb->len, + dma_unmap_len(tx_cb_ptr, dma_len), DMA_TO_DEVICE); bcmgenet_free_cb(tx_cb_ptr); } else if (dma_unmap_addr(tx_cb_ptr, dma_addr)) { @@ -1308,7 +1308,7 @@ static int bcmgenet_xmit_single(struct net_device *dev, } dma_unmap_addr_set(tx_cb_ptr, dma_addr, mapping); - dma_unmap_len_set(tx_cb_ptr, dma_len, skb->len); + dma_unmap_len_set(tx_cb_ptr, dma_len, skb_len); length_status = (skb_len << DMA_BUFLENGTH_SHIFT) | dma_desc_flags | (priv->hw_params->qtag_mask << DMA_TX_QTAG_SHIFT) | DMA_TX_APPEND_CRC; diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c index 060dd3922974..973dade2d07f 100644 --- a/drivers/net/ethernet/jme.c +++ b/drivers/net/ethernet/jme.c @@ -3312,13 +3312,14 @@ jme_resume(struct device *dev) jme_reset_phy_processor(jme); jme_phy_calibration(jme); jme_phy_setEA(jme); - jme_start_irq(jme); netif_device_attach(netdev); atomic_inc(&jme->link_changing); jme_reset_link(jme); + jme_start_irq(jme); + return 0; } diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index ed622fa29dfa..a4ac6fedac75 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3404,7 +3404,7 @@ static int mvneta_probe(struct platform_device *pdev) dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; dev->hw_features |= dev->features; dev->vlan_features |= dev->features; - dev->priv_flags |= IFF_UNICAST_FLT; + dev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE; dev->gso_max_segs = MVNETA_MAX_TSO_SEGS; err = register_netdev(dev); diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index cad6c44df91c..d314d96dcb1c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -3132,7 +3132,7 @@ static int verify_qp_parameters(struct mlx4_dev *dev, case QP_TRANS_RTS2RTS: case QP_TRANS_SQD2SQD: case QP_TRANS_SQD2RTS: - if (slave != mlx4_master_func_num(dev)) + if (slave != mlx4_master_func_num(dev)) { if (optpar & MLX4_QP_OPTPAR_PRIMARY_ADDR_PATH) { port = (qp_ctx->pri_path.sched_queue >> 6 & 1) + 1; if (dev->caps.port_mask[port] != MLX4_PORT_TYPE_IB) @@ -3151,6 +3151,7 @@ static int verify_qp_parameters(struct mlx4_dev *dev, if (qp_ctx->alt_path.mgid_index >= num_gids) return -EINVAL; } + } break; default: break; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 4365c8bccc6d..605f6410f867 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -61,6 +61,8 @@ struct mlxsw_sp { #define MLXSW_SP_DEFAULT_LEARNING_INTERVAL 100 unsigned int interval; /* ms */ } fdb_notify; +#define MLXSW_SP_MIN_AGEING_TIME 10 +#define MLXSW_SP_MAX_AGEING_TIME 1000000 #define MLXSW_SP_DEFAULT_AGEING_TIME 300 u32 ageing_time; struct { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 7dbeafa65934..d4c4c2b5156c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -232,8 +232,13 @@ static int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port, unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t); u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000; - if (switchdev_trans_ph_prepare(trans)) - return 0; + if (switchdev_trans_ph_prepare(trans)) { + if (ageing_time < MLXSW_SP_MIN_AGEING_TIME || + ageing_time > MLXSW_SP_MAX_AGEING_TIME) + return -ERANGE; + else + return 0; + } return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time); } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 46bbea8e023c..55007f1e6bbc 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -566,6 +566,7 @@ struct qlcnic_adapter_stats { u64 tx_dma_map_error; u64 spurious_intr; u64 mac_filter_limit_overrun; + u64 mbx_spurious_intr; }; /* @@ -1099,7 +1100,7 @@ struct qlcnic_mailbox { unsigned long status; spinlock_t queue_lock; /* Mailbox queue lock */ spinlock_t aen_lock; /* Mailbox response/AEN lock */ - atomic_t rsp_status; + u32 rsp_status; u32 num_cmds; }; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index 37a731be7d39..f9640d5ce6ba 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -491,7 +491,7 @@ irqreturn_t qlcnic_83xx_clear_legacy_intr(struct qlcnic_adapter *adapter) static inline void qlcnic_83xx_notify_mbx_response(struct qlcnic_mailbox *mbx) { - atomic_set(&mbx->rsp_status, QLC_83XX_MBX_RESPONSE_ARRIVED); + mbx->rsp_status = QLC_83XX_MBX_RESPONSE_ARRIVED; complete(&mbx->completion); } @@ -510,7 +510,7 @@ static void qlcnic_83xx_poll_process_aen(struct qlcnic_adapter *adapter) if (event & QLCNIC_MBX_ASYNC_EVENT) { __qlcnic_83xx_process_aen(adapter); } else { - if (atomic_read(&mbx->rsp_status) != rsp_status) + if (mbx->rsp_status != rsp_status) qlcnic_83xx_notify_mbx_response(mbx); } out: @@ -1023,7 +1023,7 @@ static void qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter) if (event & QLCNIC_MBX_ASYNC_EVENT) { __qlcnic_83xx_process_aen(adapter); } else { - if (atomic_read(&mbx->rsp_status) != rsp_status) + if (mbx->rsp_status != rsp_status) qlcnic_83xx_notify_mbx_response(mbx); } } @@ -2338,9 +2338,9 @@ static void qlcnic_83xx_handle_link_aen(struct qlcnic_adapter *adapter, static irqreturn_t qlcnic_83xx_handle_aen(int irq, void *data) { + u32 mask, resp, event, rsp_status = QLC_83XX_MBX_RESPONSE_ARRIVED; struct qlcnic_adapter *adapter = data; struct qlcnic_mailbox *mbx; - u32 mask, resp, event; unsigned long flags; mbx = adapter->ahw->mailbox; @@ -2350,10 +2350,14 @@ static irqreturn_t qlcnic_83xx_handle_aen(int irq, void *data) goto out; event = readl(QLCNIC_MBX_FW(adapter->ahw, 0)); - if (event & QLCNIC_MBX_ASYNC_EVENT) + if (event & QLCNIC_MBX_ASYNC_EVENT) { __qlcnic_83xx_process_aen(adapter); - else - qlcnic_83xx_notify_mbx_response(mbx); + } else { + if (mbx->rsp_status != rsp_status) + qlcnic_83xx_notify_mbx_response(mbx); + else + adapter->stats.mbx_spurious_intr++; + } out: mask = QLCRDX(adapter->ahw, QLCNIC_DEF_INT_MASK); @@ -4050,10 +4054,10 @@ static void qlcnic_83xx_mailbox_worker(struct work_struct *work) struct qlcnic_adapter *adapter = mbx->adapter; const struct qlcnic_mbx_ops *mbx_ops = mbx->ops; struct device *dev = &adapter->pdev->dev; - atomic_t *rsp_status = &mbx->rsp_status; struct list_head *head = &mbx->cmd_q; struct qlcnic_hardware_context *ahw; struct qlcnic_cmd_args *cmd = NULL; + unsigned long flags; ahw = adapter->ahw; @@ -4063,7 +4067,9 @@ static void qlcnic_83xx_mailbox_worker(struct work_struct *work) return; } - atomic_set(rsp_status, QLC_83XX_MBX_RESPONSE_WAIT); + spin_lock_irqsave(&mbx->aen_lock, flags); + mbx->rsp_status = QLC_83XX_MBX_RESPONSE_WAIT; + spin_unlock_irqrestore(&mbx->aen_lock, flags); spin_lock(&mbx->queue_lock); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c index 494e8105adee..0a2318cad34d 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c @@ -59,7 +59,8 @@ static const struct qlcnic_stats qlcnic_gstrings_stats[] = { QLC_OFF(stats.mac_filter_limit_overrun)}, {"spurious intr", QLC_SIZEOF(stats.spurious_intr), QLC_OFF(stats.spurious_intr)}, - + {"mbx spurious intr", QLC_SIZEOF(stats.mbx_spurious_intr), + QLC_OFF(stats.mbx_spurious_intr)}, }; static const char qlcnic_device_gstrings_stats[][ETH_GSTRING_LEN] = { diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c index 997976426799..b28e73ea2c25 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c @@ -1648,7 +1648,18 @@ static void ql_process_mac_rx_skb(struct ql_adapter *qdev, return; } skb_reserve(new_skb, NET_IP_ALIGN); + + pci_dma_sync_single_for_cpu(qdev->pdev, + dma_unmap_addr(sbq_desc, mapaddr), + dma_unmap_len(sbq_desc, maplen), + PCI_DMA_FROMDEVICE); + memcpy(skb_put(new_skb, length), skb->data, length); + + pci_dma_sync_single_for_device(qdev->pdev, + dma_unmap_addr(sbq_desc, mapaddr), + dma_unmap_len(sbq_desc, maplen), + PCI_DMA_FROMDEVICE); skb = new_skb; /* Frame error, so drop the packet. */ diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c index 689a4a5c8dcf..1ef03939d25f 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.c +++ b/drivers/net/ethernet/qualcomm/qca_spi.c @@ -811,7 +811,7 @@ qcaspi_netdev_setup(struct net_device *dev) dev->netdev_ops = &qcaspi_netdev_ops; qcaspi_set_ethtool_ops(dev); dev->watchdog_timeo = QCASPI_TX_TIMEOUT; - dev->flags = IFF_MULTICAST; + dev->priv_flags &= ~IFF_TX_SKB_SHARING; dev->tx_queue_len = 100; qca = netdev_priv(dev); diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 6a8fc0f341ff..36fc9427418f 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -1185,11 +1185,8 @@ static void sh_eth_ring_format(struct net_device *ndev) break; sh_eth_set_receive_align(skb); - /* RX descriptor */ - rxdesc = &mdp->rx_ring[i]; /* The size of the buffer is a multiple of 32 bytes. */ buf_len = ALIGN(mdp->rx_buf_sz, 32); - rxdesc->len = cpu_to_edmac(mdp, buf_len << 16); dma_addr = dma_map_single(&ndev->dev, skb->data, buf_len, DMA_FROM_DEVICE); if (dma_mapping_error(&ndev->dev, dma_addr)) { @@ -1197,6 +1194,10 @@ static void sh_eth_ring_format(struct net_device *ndev) break; } mdp->rx_skbuff[i] = skb; + + /* RX descriptor */ + rxdesc = &mdp->rx_ring[i]; + rxdesc->len = cpu_to_edmac(mdp, buf_len << 16); rxdesc->addr = cpu_to_edmac(mdp, dma_addr); rxdesc->status = cpu_to_edmac(mdp, RD_RACT | RD_RFP); @@ -1212,7 +1213,8 @@ static void sh_eth_ring_format(struct net_device *ndev) mdp->dirty_rx = (u32) (i - mdp->num_rx_ring); /* Mark the last entry as wrapping the ring. */ - rxdesc->status |= cpu_to_edmac(mdp, RD_RDLE); + if (rxdesc) + rxdesc->status |= cpu_to_edmac(mdp, RD_RDLE); memset(mdp->tx_ring, 0, tx_ringsize); diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c index 52ec3d6e056a..2b34622a4bfe 100644 --- a/drivers/net/ethernet/rocker/rocker.c +++ b/drivers/net/ethernet/rocker/rocker.c @@ -239,6 +239,7 @@ struct rocker { struct { u64 id; } hw; + unsigned long ageing_time; spinlock_t cmd_ring_lock; /* for cmd ring accesses */ struct rocker_dma_ring_info cmd_ring; struct rocker_dma_ring_info event_ring; @@ -3704,7 +3705,7 @@ static void rocker_fdb_cleanup(unsigned long data) struct rocker_port *rocker_port; struct rocker_fdb_tbl_entry *entry; struct hlist_node *tmp; - unsigned long next_timer = jiffies + BR_MIN_AGEING_TIME; + unsigned long next_timer = jiffies + rocker->ageing_time; unsigned long expires; unsigned long lock_flags; int flags = ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_REMOVE | @@ -4367,8 +4368,12 @@ static int rocker_port_bridge_ageing_time(struct rocker_port *rocker_port, struct switchdev_trans *trans, u32 ageing_time) { + struct rocker *rocker = rocker_port->rocker; + if (!switchdev_trans_ph_prepare(trans)) { rocker_port->ageing_time = clock_t_to_jiffies(ageing_time); + if (rocker_port->ageing_time < rocker->ageing_time) + rocker->ageing_time = rocker_port->ageing_time; mod_timer(&rocker_port->rocker->fdb_cleanup_timer, jiffies); } @@ -5206,10 +5211,13 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_init_tbls; } + rocker->ageing_time = BR_DEFAULT_AGEING_TIME; setup_timer(&rocker->fdb_cleanup_timer, rocker_fdb_cleanup, (unsigned long) rocker); mod_timer(&rocker->fdb_cleanup_timer, jiffies); + rocker->ageing_time = BR_DEFAULT_AGEING_TIME; + err = rocker_probe_ports(rocker); if (err) { dev_err(&pdev->dev, "failed to probe ports\n"); diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c index 696852eb23c3..7a3f990c1935 100644 --- a/drivers/net/irda/irtty-sir.c +++ b/drivers/net/irda/irtty-sir.c @@ -430,16 +430,6 @@ static int irtty_open(struct tty_struct *tty) /* Module stuff handled via irda_ldisc.owner - Jean II */ - /* First make sure we're not already connected. */ - if (tty->disc_data != NULL) { - priv = tty->disc_data; - if (priv && priv->magic == IRTTY_MAGIC) { - ret = -EEXIST; - goto out; - } - tty->disc_data = NULL; /* ### */ - } - /* stop the underlying driver */ irtty_stop_receiver(tty, TRUE); if (tty->ops->stop) diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 0fc521941c71..159a68782bec 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -760,6 +760,8 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, macvtap16_to_cpu(q, vnet_hdr.hdr_len) : GOODCOPY_LEN; if (copylen > good_linear) copylen = good_linear; + else if (copylen < ETH_HLEN) + copylen = ETH_HLEN; linear = copylen; i = *from; iov_iter_advance(&i, copylen); @@ -769,10 +771,11 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, if (!zerocopy) { copylen = len; - if (macvtap16_to_cpu(q, vnet_hdr.hdr_len) > good_linear) + linear = macvtap16_to_cpu(q, vnet_hdr.hdr_len); + if (linear > good_linear) linear = good_linear; - else - linear = macvtap16_to_cpu(q, vnet_hdr.hdr_len); + else if (linear < ETH_HLEN) + linear = ETH_HLEN; } skb = macvtap_alloc_skb(&q->sk, MACVTAP_RESERVE, copylen, diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 9a863c6a6a33..174e06ec7c2f 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -567,7 +567,7 @@ static int get_filter(void __user *arg, struct sock_filter **p) static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct ppp_file *pf = file->private_data; + struct ppp_file *pf; struct ppp *ppp; int err = -EFAULT, val, val2, i; struct ppp_idle idle; @@ -577,9 +577,14 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) void __user *argp = (void __user *)arg; int __user *p = argp; - if (!pf) - return ppp_unattached_ioctl(current->nsproxy->net_ns, - pf, file, cmd, arg); + mutex_lock(&ppp_mutex); + + pf = file->private_data; + if (!pf) { + err = ppp_unattached_ioctl(current->nsproxy->net_ns, + pf, file, cmd, arg); + goto out; + } if (cmd == PPPIOCDETACH) { /* @@ -594,7 +599,6 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) * this fd and reopening /dev/ppp. */ err = -EINVAL; - mutex_lock(&ppp_mutex); if (pf->kind == INTERFACE) { ppp = PF_TO_PPP(pf); rtnl_lock(); @@ -608,15 +612,13 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } else pr_warn("PPPIOCDETACH file->f_count=%ld\n", atomic_long_read(&file->f_count)); - mutex_unlock(&ppp_mutex); - return err; + goto out; } if (pf->kind == CHANNEL) { struct channel *pch; struct ppp_channel *chan; - mutex_lock(&ppp_mutex); pch = PF_TO_CHANNEL(pf); switch (cmd) { @@ -638,17 +640,16 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) err = chan->ops->ioctl(chan, cmd, arg); up_read(&pch->chan_sem); } - mutex_unlock(&ppp_mutex); - return err; + goto out; } if (pf->kind != INTERFACE) { /* can't happen */ pr_err("PPP: not interface or channel??\n"); - return -EINVAL; + err = -EINVAL; + goto out; } - mutex_lock(&ppp_mutex); ppp = PF_TO_PPP(pf); switch (cmd) { case PPPIOCSMRU: @@ -823,7 +824,10 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) default: err = -ENOTTY; } + +out: mutex_unlock(&ppp_mutex); + return err; } @@ -836,7 +840,6 @@ static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf, struct ppp_net *pn; int __user *p = (int __user *)arg; - mutex_lock(&ppp_mutex); switch (cmd) { case PPPIOCNEWUNIT: /* Create a new ppp unit */ @@ -886,7 +889,7 @@ static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf, default: err = -ENOTTY; } - mutex_unlock(&ppp_mutex); + return err; } @@ -2290,7 +2293,7 @@ int ppp_register_net_channel(struct net *net, struct ppp_channel *chan) pch->ppp = NULL; pch->chan = chan; - pch->chan_net = net; + pch->chan_net = get_net(net); chan->ppp = pch; init_ppp_file(&pch->file, CHANNEL); pch->file.hdrlen = chan->hdrlen; @@ -2387,6 +2390,8 @@ ppp_unregister_channel(struct ppp_channel *chan) spin_lock_bh(&pn->all_channels_lock); list_del(&pch->list); spin_unlock_bh(&pn->all_channels_lock); + put_net(pch->chan_net); + pch->chan_net = NULL; pch->file.dead = 1; wake_up_interruptible(&pch->file.rwait); @@ -2803,6 +2808,7 @@ static struct ppp *ppp_create_interface(struct net *net, int unit, out2: mutex_unlock(&pn->all_ppp_mutex); + rtnl_unlock(); free_netdev(dev); out1: *retp = ret; diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c index 01f08a7751f7..e7034c55e796 100644 --- a/drivers/net/rionet.c +++ b/drivers/net/rionet.c @@ -280,7 +280,7 @@ static void rionet_outb_msg_event(struct rio_mport *mport, void *dev_id, int mbo struct net_device *ndev = dev_id; struct rionet_private *rnet = netdev_priv(ndev); - spin_lock(&rnet->lock); + spin_lock(&rnet->tx_lock); if (netif_msg_intr(rnet)) printk(KERN_INFO @@ -299,7 +299,7 @@ static void rionet_outb_msg_event(struct rio_mport *mport, void *dev_id, int mbo if (rnet->tx_cnt < RIONET_TX_RING_SIZE) netif_wake_queue(ndev); - spin_unlock(&rnet->lock); + spin_unlock(&rnet->tx_lock); } static int rionet_open(struct net_device *ndev) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 6d398f13e1e6..4b15d9ee5a54 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -621,7 +621,8 @@ static int tun_attach(struct tun_struct *tun, struct file *file, bool skip_filte /* Re-attach the filter to persist device */ if (!skip_filter && (tun->filter_attached == true)) { - err = sk_attach_filter(&tun->fprog, tfile->socket.sk); + err = __sk_attach_filter(&tun->fprog, tfile->socket.sk, + lockdep_rtnl_is_held()); if (!err) goto out; } @@ -1000,7 +1001,6 @@ static void tun_net_init(struct net_device *dev) /* Zero header length */ dev->type = ARPHRD_NONE; dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; - dev->tx_queue_len = TUN_READQ_SIZE; /* We prefer our own queue length */ break; case IFF_TAP: @@ -1012,7 +1012,6 @@ static void tun_net_init(struct net_device *dev) eth_hw_addr_random(dev); - dev->tx_queue_len = TUN_READQ_SIZE; /* We prefer our own queue length */ break; } } @@ -1467,6 +1466,8 @@ static void tun_setup(struct net_device *dev) dev->ethtool_ops = &tun_ethtool_ops; dev->destructor = tun_free_netdev; + /* We prefer our own queue length */ + dev->tx_queue_len = TUN_READQ_SIZE; } /* Trivial set of netlink ops to allow deleting tun or tap @@ -1808,7 +1809,7 @@ static void tun_detach_filter(struct tun_struct *tun, int n) for (i = 0; i < n; i++) { tfile = rtnl_dereference(tun->tfiles[i]); - sk_detach_filter(tfile->socket.sk); + __sk_detach_filter(tfile->socket.sk, lockdep_rtnl_is_held()); } tun->filter_attached = false; @@ -1821,7 +1822,8 @@ static int tun_attach_filter(struct tun_struct *tun) for (i = 0; i < tun->numqueues; i++) { tfile = rtnl_dereference(tun->tfiles[i]); - ret = sk_attach_filter(&tun->fprog, tfile->socket.sk); + ret = __sk_attach_filter(&tun->fprog, tfile->socket.sk, + lockdep_rtnl_is_held()); if (ret) { tun_detach_filter(tun, i); return ret; diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 3da70bf9936a..7cba2c3759df 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -160,6 +160,12 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) info->u = header.usb_cdc_union_desc; info->header = header.usb_cdc_header_desc; info->ether = header.usb_cdc_ether_desc; + if (!info->u) { + if (rndis) + goto skip; + else /* in that case a quirk is mandatory */ + goto bad_desc; + } /* we need a master/control interface (what we're * probed with) and a slave/data interface; union * descriptors sort this all out. @@ -256,7 +262,7 @@ skip: goto bad_desc; } - } else if (!info->header || !info->u || (!rndis && !info->ether)) { + } else if (!info->header || (!rndis && !info->ether)) { dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n", info->header ? "" : "header ", info->u ? "" : "union ", diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index e8a1144c5a8b..8c2bb77db049 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -794,7 +794,11 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_ iface_no = ctx->data->cur_altsetting->desc.bInterfaceNumber; - /* reset data interface */ + /* Reset data interface. Some devices will not reset properly + * unless they are configured first. Toggle the altsetting to + * force a reset + */ + usb_set_interface(dev->udev, iface_no, data_altsetting); temp = usb_set_interface(dev->udev, iface_no, 0); if (temp) { dev_dbg(&intf->dev, "set interface failed\n"); diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 982e0acd1a36..a34f491224c1 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -699,6 +699,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x19d2, 0x1426, 2)}, /* ZTE MF91 */ {QMI_FIXED_INTF(0x19d2, 0x1428, 2)}, /* Telewell TW-LTE 4G v2 */ {QMI_FIXED_INTF(0x19d2, 0x2002, 4)}, /* ZTE (Vodafone) K3765-Z */ + {QMI_FIXED_INTF(0x2001, 0x7e19, 4)}, /* D-Link DWM-221 B1 */ {QMI_FIXED_INTF(0x0f3d, 0x68a2, 8)}, /* Sierra Wireless MC7700 */ {QMI_FIXED_INTF(0x114f, 0x68a2, 8)}, /* Sierra Wireless MC7750 */ {QMI_FIXED_INTF(0x1199, 0x68a2, 8)}, /* Sierra Wireless MC7710 in QMI mode */ @@ -718,8 +719,10 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x1199, 0x9061, 8)}, /* Sierra Wireless Modem */ {QMI_FIXED_INTF(0x1199, 0x9070, 8)}, /* Sierra Wireless MC74xx/EM74xx */ {QMI_FIXED_INTF(0x1199, 0x9070, 10)}, /* Sierra Wireless MC74xx/EM74xx */ - {QMI_FIXED_INTF(0x1199, 0x9071, 8)}, /* Sierra Wireless MC74xx/EM74xx */ - {QMI_FIXED_INTF(0x1199, 0x9071, 10)}, /* Sierra Wireless MC74xx/EM74xx */ + {QMI_FIXED_INTF(0x1199, 0x9071, 8)}, /* Sierra Wireless MC74xx */ + {QMI_FIXED_INTF(0x1199, 0x9071, 10)}, /* Sierra Wireless MC74xx */ + {QMI_FIXED_INTF(0x1199, 0x9079, 8)}, /* Sierra Wireless EM74xx */ + {QMI_FIXED_INTF(0x1199, 0x9079, 10)}, /* Sierra Wireless EM74xx */ {QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */ {QMI_FIXED_INTF(0x1bbb, 0x0203, 2)}, /* Alcatel L800MA */ {QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */ diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 0744bf2ef2d6..c2ea4e5666fb 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1766,6 +1766,13 @@ out3: if (info->unbind) info->unbind (dev, udev); out1: + /* subdrivers must undo all they did in bind() if they + * fail it, but we may fail later and a deferred kevent + * may trigger an error resubmitting itself and, worse, + * schedule a timer. So we kill it all just in case. + */ + cancel_work_sync(&dev->kevent); + del_timer_sync(&dev->delay); free_netdev(net); out: return status; diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 0a242b200df4..903bda437839 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -114,20 +114,23 @@ static struct dst_ops vrf_dst_ops = { #if IS_ENABLED(CONFIG_IPV6) static bool check_ipv6_frame(const struct sk_buff *skb) { - const struct ipv6hdr *ipv6h = (struct ipv6hdr *)skb->data; - size_t hlen = sizeof(*ipv6h); + const struct ipv6hdr *ipv6h; + struct ipv6hdr _ipv6h; bool rc = true; - if (skb->len < hlen) + ipv6h = skb_header_pointer(skb, 0, sizeof(_ipv6h), &_ipv6h); + if (!ipv6h) goto out; if (ipv6h->nexthdr == NEXTHDR_ICMP) { const struct icmp6hdr *icmph; + struct icmp6hdr _icmph; - if (skb->len < hlen + sizeof(*icmph)) + icmph = skb_header_pointer(skb, sizeof(_ipv6h), + sizeof(_icmph), &_icmph); + if (!icmph) goto out; - icmph = (struct icmp6hdr *)(skb->data + sizeof(*ipv6h)); switch (icmph->icmp6_type) { case NDISC_ROUTER_SOLICITATION: case NDISC_ROUTER_ADVERTISEMENT: diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index e0fcda4ddd55..3c0df70e2f53 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1306,8 +1306,10 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) gbp = (struct vxlanhdr_gbp *)vxh; md->gbp = ntohs(gbp->policy_id); - if (tun_dst) + if (tun_dst) { tun_dst->u.tun_info.key.tun_flags |= TUNNEL_VXLAN_OPT; + tun_dst->u.tun_info.options_len = sizeof(*md); + } if (gbp->dont_learn) md->gbp |= VXLAN_GBP_DONT_LEARN; diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index 44541dbc5c28..69b994f3b8c5 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -2516,7 +2516,7 @@ fst_add_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->mem_start = card->phys_mem + BUF_OFFSET ( txBuffer[i][0][0]); dev->mem_end = card->phys_mem - + BUF_OFFSET ( txBuffer[i][NUM_TX_BUFFER][0]); + + BUF_OFFSET ( txBuffer[i][NUM_TX_BUFFER - 1][LEN_RX_BUFFER - 1]); dev->base_addr = card->pci_conf; dev->irq = card->irq; diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index cc81482c934d..113a43fca9cf 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -403,10 +403,9 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah, if (match) { if (AR_SREV_9287(ah)) { - /* FIXME: array overrun? */ for (i = 0; i < numXpdGains; i++) { minPwrT4[i] = data_9287[idxL].pwrPdg[i][0]; - maxPwrT4[i] = data_9287[idxL].pwrPdg[i][4]; + maxPwrT4[i] = data_9287[idxL].pwrPdg[i][intercepts - 1]; ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], data_9287[idxL].pwrPdg[i], data_9287[idxL].vpdPdg[i], @@ -416,7 +415,7 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah, } else if (eeprom_4k) { for (i = 0; i < numXpdGains; i++) { minPwrT4[i] = data_4k[idxL].pwrPdg[i][0]; - maxPwrT4[i] = data_4k[idxL].pwrPdg[i][4]; + maxPwrT4[i] = data_4k[idxL].pwrPdg[i][intercepts - 1]; ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], data_4k[idxL].pwrPdg[i], data_4k[idxL].vpdPdg[i], @@ -426,7 +425,7 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah, } else { for (i = 0; i < numXpdGains; i++) { minPwrT4[i] = data_def[idxL].pwrPdg[i][0]; - maxPwrT4[i] = data_def[idxL].pwrPdg[i][4]; + maxPwrT4[i] = data_def[idxL].pwrPdg[i][intercepts - 1]; ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], data_def[idxL].pwrPdg[i], data_def[idxL].vpdPdg[i], diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index d906fa13ba97..610c442c7ab2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -106,7 +106,7 @@ static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant) sizeof(tx_ant_cmd), &tx_ant_cmd); } -static void iwl_free_fw_paging(struct iwl_mvm *mvm) +void iwl_free_fw_paging(struct iwl_mvm *mvm) { int i; @@ -126,6 +126,8 @@ static void iwl_free_fw_paging(struct iwl_mvm *mvm) get_order(mvm->fw_paging_db[i].fw_paging_size)); } kfree(mvm->trans->paging_download_buf); + mvm->trans->paging_download_buf = NULL; + memset(mvm->fw_paging_db, 0, sizeof(mvm->fw_paging_db)); } diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 4bde2d027dcd..244e26c26821 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -1190,6 +1190,9 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); +/* Paging */ +void iwl_free_fw_paging(struct iwl_mvm *mvm); + /* MVM debugfs */ #ifdef CONFIG_IWLWIFI_DEBUGFS int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir); diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 13c97f665ba8..c3adf2bcdc85 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -645,6 +645,8 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++) kfree(mvm->nvm_sections[i].data); + iwl_free_fw_paging(mvm); + iwl_mvm_tof_clean(mvm); ieee80211_free_hw(mvm->hw); diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index 7e2c43f701bc..5f47356d6942 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -335,7 +335,7 @@ static const struct nd_cmd_desc __nd_cmd_dimm_descs[] = { [ND_CMD_IMPLEMENTED] = { }, [ND_CMD_SMART] = { .out_num = 2, - .out_sizes = { 4, 8, }, + .out_sizes = { 4, 128, }, }, [ND_CMD_SMART_THRESHOLD] = { .out_num = 2, @@ -513,10 +513,10 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, /* fail write commands (when read-only) */ if (read_only) - switch (ioctl_cmd) { - case ND_IOCTL_VENDOR: - case ND_IOCTL_SET_CONFIG_DATA: - case ND_IOCTL_ARS_START: + switch (cmd) { + case ND_CMD_VENDOR: + case ND_CMD_SET_CONFIG_DATA: + case ND_CMD_ARS_START: dev_dbg(&nvdimm_bus->dev, "'%s' command while read-only.\n", nvdimm ? nvdimm_cmd_name(cmd) : nvdimm_bus_cmd_name(cmd)); diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c index 71805a1aa0f3..9d3974591cd6 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c @@ -275,7 +275,7 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn) } else { /* from init we validate */ if (memcmp(nd_pfn->uuid, pfn_sb->uuid, 16) != 0) - return -EINVAL; + return -ENODEV; } /* diff --git a/drivers/of/of_batterydata.c b/drivers/of/of_batterydata.c index 5f140cd0c2a6..4410f270f557 100644 --- a/drivers/of/of_batterydata.c +++ b/drivers/of/of_batterydata.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -312,32 +312,15 @@ static int64_t of_batterydata_convert_battery_id_kohm(int batt_id_uv, struct device_node *of_batterydata_get_best_profile( const struct device_node *batterydata_container_node, - const char *psy_name, const char *batt_type) + int batt_id_kohm, const char *batt_type) { struct batt_ids batt_ids; struct device_node *node, *best_node = NULL; - struct power_supply *psy; const char *battery_type = NULL; - union power_supply_propval ret = {0, }; int delta = 0, best_delta = 0, best_id_kohm = 0, id_range_pct, - batt_id_kohm = 0, i = 0, rc = 0, limit = 0; + i = 0, rc = 0, limit = 0; bool in_range = false; - psy = power_supply_get_by_name(psy_name); - if (!psy) { - pr_err("%s supply not found. defer\n", psy_name); - return ERR_PTR(-EPROBE_DEFER); - } - - rc = power_supply_get_property(psy, POWER_SUPPLY_PROP_RESISTANCE_ID, - &ret); - if (rc) { - pr_err("failed to retrieve resistance value rc=%d\n", rc); - return ERR_PTR(-ENOSYS); - } - - batt_id_kohm = ret.intval / 1000; - /* read battery id range percentage for best profile */ rc = of_property_read_u32(batterydata_container_node, "qcom,batt-id-range-pct", &id_range_pct); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index edb1984201e9..7aafb5fb9336 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -179,6 +179,9 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, u16 orig_cmd; struct pci_bus_region region, inverted_region; + if (dev->non_compliant_bars) + return 0; + mask = type ? PCI_ROM_ADDRESS_MASK : ~0; /* No printks while decoding is disabled! */ @@ -1174,6 +1177,7 @@ void pci_msi_setup_pci_dev(struct pci_dev *dev) int pci_setup_device(struct pci_dev *dev) { u32 class; + u16 cmd; u8 hdr_type; int pos = 0; struct pci_bus_region region; @@ -1219,6 +1223,16 @@ int pci_setup_device(struct pci_dev *dev) /* device class may be changed after fixup */ class = dev->class >> 8; + if (dev->non_compliant_bars) { + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) { + dev_info(&dev->dev, "device has non-compliant BARs; disabling IO/MEM decoding\n"); + cmd &= ~PCI_COMMAND_IO; + cmd &= ~PCI_COMMAND_MEMORY; + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + } + switch (dev->hdr_type) { /* header type */ case PCI_HEADER_TYPE_NORMAL: /* standard header */ if (class == PCI_CLASS_BRIDGE_PCI) diff --git a/drivers/pcmcia/db1xxx_ss.c b/drivers/pcmcia/db1xxx_ss.c index 4c2fa05b4589..944674ee3464 100644 --- a/drivers/pcmcia/db1xxx_ss.c +++ b/drivers/pcmcia/db1xxx_ss.c @@ -56,6 +56,7 @@ struct db1x_pcmcia_sock { int stschg_irq; /* card-status-change irq */ int card_irq; /* card irq */ int eject_irq; /* db1200/pb1200 have these */ + int insert_gpio; /* db1000 carddetect gpio */ #define BOARD_TYPE_DEFAULT 0 /* most boards */ #define BOARD_TYPE_DB1200 1 /* IRQs aren't gpios */ @@ -83,7 +84,7 @@ static int db1200_card_inserted(struct db1x_pcmcia_sock *sock) /* carddetect gpio: low-active */ static int db1000_card_inserted(struct db1x_pcmcia_sock *sock) { - return !gpio_get_value(irq_to_gpio(sock->insert_irq)); + return !gpio_get_value(sock->insert_gpio); } static int db1x_card_inserted(struct db1x_pcmcia_sock *sock) @@ -457,9 +458,15 @@ static int db1x_pcmcia_socket_probe(struct platform_device *pdev) r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "card"); sock->card_irq = r ? r->start : 0; - /* insert: irq which triggers on card insertion/ejection */ + /* insert: irq which triggers on card insertion/ejection + * BIG FAT NOTE: on DB1000/1100/1500/1550 we pass a GPIO here! + */ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "insert"); sock->insert_irq = r ? r->start : -1; + if (sock->board_type == BOARD_TYPE_DEFAULT) { + sock->insert_gpio = r ? r->start : -1; + sock->insert_irq = r ? gpio_to_irq(r->start) : -1; + } /* stschg: irq which trigger on card status change (optional) */ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "stschg"); diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c index 2e6ca69635aa..17dd8fe12b54 100644 --- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c +++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c @@ -779,7 +779,7 @@ static int bcm2835_pctl_dt_node_to_map(struct pinctrl_dev *pctldev, } if (num_pulls) { err = of_property_read_u32_index(np, "brcm,pull", - (num_funcs > 1) ? i : 0, &pull); + (num_pulls > 1) ? i : 0, &pull); if (err) goto out; err = bcm2835_pctl_dt_node_to_map_pull(pc, np, pin, diff --git a/drivers/pinctrl/freescale/pinctrl-imx.c b/drivers/pinctrl/freescale/pinctrl-imx.c index a5bb93987378..1029aa7889b5 100644 --- a/drivers/pinctrl/freescale/pinctrl-imx.c +++ b/drivers/pinctrl/freescale/pinctrl-imx.c @@ -726,19 +726,18 @@ int imx_pinctrl_probe(struct platform_device *pdev, if (of_property_read_bool(dev_np, "fsl,input-sel")) { np = of_parse_phandle(dev_np, "fsl,input-sel", 0); - if (np) { - ipctl->input_sel_base = of_iomap(np, 0); - if (IS_ERR(ipctl->input_sel_base)) { - of_node_put(np); - dev_err(&pdev->dev, - "iomuxc input select base address not found\n"); - return PTR_ERR(ipctl->input_sel_base); - } - } else { + if (!np) { dev_err(&pdev->dev, "iomuxc fsl,input-sel property not found\n"); return -EINVAL; } + + ipctl->input_sel_base = of_iomap(np, 0); of_node_put(np); + if (!ipctl->input_sel_base) { + dev_err(&pdev->dev, + "iomuxc input select base address not found\n"); + return -ENOMEM; + } } imx_pinctrl_desc.name = dev_name(&pdev->dev); diff --git a/drivers/pinctrl/nomadik/pinctrl-nomadik.c b/drivers/pinctrl/nomadik/pinctrl-nomadik.c index eebfae0c9b7c..f844b4ae7f79 100644 --- a/drivers/pinctrl/nomadik/pinctrl-nomadik.c +++ b/drivers/pinctrl/nomadik/pinctrl-nomadik.c @@ -995,7 +995,7 @@ static void nmk_gpio_dbg_show_one(struct seq_file *s, int val; if (pull) - pullidx = data_out ? 1 : 2; + pullidx = data_out ? 2 : 1; seq_printf(s, " gpio-%-3d (%-20.20s) in %s %s", gpio, diff --git a/drivers/pinctrl/pinctrl-pistachio.c b/drivers/pinctrl/pinctrl-pistachio.c index 85c9046c690e..6b1a47f8c096 100644 --- a/drivers/pinctrl/pinctrl-pistachio.c +++ b/drivers/pinctrl/pinctrl-pistachio.c @@ -469,27 +469,27 @@ static const char * const pistachio_mips_pll_lock_groups[] = { "mfio83", }; -static const char * const pistachio_sys_pll_lock_groups[] = { +static const char * const pistachio_audio_pll_lock_groups[] = { "mfio84", }; -static const char * const pistachio_wifi_pll_lock_groups[] = { +static const char * const pistachio_rpu_v_pll_lock_groups[] = { "mfio85", }; -static const char * const pistachio_bt_pll_lock_groups[] = { +static const char * const pistachio_rpu_l_pll_lock_groups[] = { "mfio86", }; -static const char * const pistachio_rpu_v_pll_lock_groups[] = { +static const char * const pistachio_sys_pll_lock_groups[] = { "mfio87", }; -static const char * const pistachio_rpu_l_pll_lock_groups[] = { +static const char * const pistachio_wifi_pll_lock_groups[] = { "mfio88", }; -static const char * const pistachio_audio_pll_lock_groups[] = { +static const char * const pistachio_bt_pll_lock_groups[] = { "mfio89", }; @@ -559,12 +559,12 @@ enum pistachio_mux_option { PISTACHIO_FUNCTION_DREQ4, PISTACHIO_FUNCTION_DREQ5, PISTACHIO_FUNCTION_MIPS_PLL_LOCK, + PISTACHIO_FUNCTION_AUDIO_PLL_LOCK, + PISTACHIO_FUNCTION_RPU_V_PLL_LOCK, + PISTACHIO_FUNCTION_RPU_L_PLL_LOCK, PISTACHIO_FUNCTION_SYS_PLL_LOCK, PISTACHIO_FUNCTION_WIFI_PLL_LOCK, PISTACHIO_FUNCTION_BT_PLL_LOCK, - PISTACHIO_FUNCTION_RPU_V_PLL_LOCK, - PISTACHIO_FUNCTION_RPU_L_PLL_LOCK, - PISTACHIO_FUNCTION_AUDIO_PLL_LOCK, PISTACHIO_FUNCTION_DEBUG_RAW_CCA_IND, PISTACHIO_FUNCTION_DEBUG_ED_SEC20_CCA_IND, PISTACHIO_FUNCTION_DEBUG_ED_SEC40_CCA_IND, @@ -620,12 +620,12 @@ static const struct pistachio_function pistachio_functions[] = { FUNCTION(dreq4), FUNCTION(dreq5), FUNCTION(mips_pll_lock), + FUNCTION(audio_pll_lock), + FUNCTION(rpu_v_pll_lock), + FUNCTION(rpu_l_pll_lock), FUNCTION(sys_pll_lock), FUNCTION(wifi_pll_lock), FUNCTION(bt_pll_lock), - FUNCTION(rpu_v_pll_lock), - FUNCTION(rpu_l_pll_lock), - FUNCTION(audio_pll_lock), FUNCTION(debug_raw_cca_ind), FUNCTION(debug_ed_sec20_cca_ind), FUNCTION(debug_ed_sec40_cca_ind), diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c index 181ea98a63b7..2b0d70217bbd 100644 --- a/drivers/pinctrl/sh-pfc/core.c +++ b/drivers/pinctrl/sh-pfc/core.c @@ -545,7 +545,9 @@ static int sh_pfc_probe(struct platform_device *pdev) return ret; } - pinctrl_provide_dummies(); + /* Enable dummy states for those platforms without pinctrl support */ + if (!of_have_populated_dt()) + pinctrl_provide_dummies(); ret = sh_pfc_init_ranges(pfc); if (ret < 0) diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c index 00265f0435a7..8b381d69df86 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c @@ -485,6 +485,7 @@ static const struct sunxi_pinctrl_desc sun8i_a33_pinctrl_data = { .pins = sun8i_a33_pins, .npins = ARRAY_SIZE(sun8i_a33_pins), .irq_banks = 2, + .irq_bank_base = 1, }; static int sun8i_a33_pinctrl_probe(struct platform_device *pdev) diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index dead97daca35..a4a5b504c532 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -578,7 +578,7 @@ static void sunxi_pinctrl_irq_release_resources(struct irq_data *d) static int sunxi_pinctrl_irq_set_type(struct irq_data *d, unsigned int type) { struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); - u32 reg = sunxi_irq_cfg_reg(d->hwirq); + u32 reg = sunxi_irq_cfg_reg(d->hwirq, pctl->desc->irq_bank_base); u8 index = sunxi_irq_cfg_offset(d->hwirq); unsigned long flags; u32 regval; @@ -625,7 +625,8 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d, unsigned int type) static void sunxi_pinctrl_irq_ack(struct irq_data *d) { struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); - u32 status_reg = sunxi_irq_status_reg(d->hwirq); + u32 status_reg = sunxi_irq_status_reg(d->hwirq, + pctl->desc->irq_bank_base); u8 status_idx = sunxi_irq_status_offset(d->hwirq); /* Clear the IRQ */ @@ -635,7 +636,7 @@ static void sunxi_pinctrl_irq_ack(struct irq_data *d) static void sunxi_pinctrl_irq_mask(struct irq_data *d) { struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); - u32 reg = sunxi_irq_ctrl_reg(d->hwirq); + u32 reg = sunxi_irq_ctrl_reg(d->hwirq, pctl->desc->irq_bank_base); u8 idx = sunxi_irq_ctrl_offset(d->hwirq); unsigned long flags; u32 val; @@ -652,7 +653,7 @@ static void sunxi_pinctrl_irq_mask(struct irq_data *d) static void sunxi_pinctrl_irq_unmask(struct irq_data *d) { struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); - u32 reg = sunxi_irq_ctrl_reg(d->hwirq); + u32 reg = sunxi_irq_ctrl_reg(d->hwirq, pctl->desc->irq_bank_base); u8 idx = sunxi_irq_ctrl_offset(d->hwirq); unsigned long flags; u32 val; @@ -744,7 +745,7 @@ static void sunxi_pinctrl_irq_handler(struct irq_desc *desc) if (bank == pctl->desc->irq_banks) return; - reg = sunxi_irq_status_reg_from_bank(bank); + reg = sunxi_irq_status_reg_from_bank(bank, pctl->desc->irq_bank_base); val = readl(pctl->membase + reg); if (val) { @@ -1023,9 +1024,11 @@ int sunxi_pinctrl_init(struct platform_device *pdev, for (i = 0; i < pctl->desc->irq_banks; i++) { /* Mask and clear all IRQs before registering a handler */ - writel(0, pctl->membase + sunxi_irq_ctrl_reg_from_bank(i)); + writel(0, pctl->membase + sunxi_irq_ctrl_reg_from_bank(i, + pctl->desc->irq_bank_base)); writel(0xffffffff, - pctl->membase + sunxi_irq_status_reg_from_bank(i)); + pctl->membase + sunxi_irq_status_reg_from_bank(i, + pctl->desc->irq_bank_base)); irq_set_chained_handler_and_data(pctl->irq[i], sunxi_pinctrl_irq_handler, diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h index e248e81a0f9e..0afce1ab12d0 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h @@ -97,6 +97,7 @@ struct sunxi_pinctrl_desc { int npins; unsigned pin_base; unsigned irq_banks; + unsigned irq_bank_base; bool irq_read_needs_mux; }; @@ -233,12 +234,12 @@ static inline u32 sunxi_pull_offset(u16 pin) return pin_num * PULL_PINS_BITS; } -static inline u32 sunxi_irq_cfg_reg(u16 irq) +static inline u32 sunxi_irq_cfg_reg(u16 irq, unsigned bank_base) { u8 bank = irq / IRQ_PER_BANK; u8 reg = (irq % IRQ_PER_BANK) / IRQ_CFG_IRQ_PER_REG * 0x04; - return IRQ_CFG_REG + bank * IRQ_MEM_SIZE + reg; + return IRQ_CFG_REG + (bank_base + bank) * IRQ_MEM_SIZE + reg; } static inline u32 sunxi_irq_cfg_offset(u16 irq) @@ -247,16 +248,16 @@ static inline u32 sunxi_irq_cfg_offset(u16 irq) return irq_num * IRQ_CFG_IRQ_BITS; } -static inline u32 sunxi_irq_ctrl_reg_from_bank(u8 bank) +static inline u32 sunxi_irq_ctrl_reg_from_bank(u8 bank, unsigned bank_base) { - return IRQ_CTRL_REG + bank * IRQ_MEM_SIZE; + return IRQ_CTRL_REG + (bank_base + bank) * IRQ_MEM_SIZE; } -static inline u32 sunxi_irq_ctrl_reg(u16 irq) +static inline u32 sunxi_irq_ctrl_reg(u16 irq, unsigned bank_base) { u8 bank = irq / IRQ_PER_BANK; - return sunxi_irq_ctrl_reg_from_bank(bank); + return sunxi_irq_ctrl_reg_from_bank(bank, bank_base); } static inline u32 sunxi_irq_ctrl_offset(u16 irq) @@ -265,16 +266,16 @@ static inline u32 sunxi_irq_ctrl_offset(u16 irq) return irq_num * IRQ_CTRL_IRQ_BITS; } -static inline u32 sunxi_irq_status_reg_from_bank(u8 bank) +static inline u32 sunxi_irq_status_reg_from_bank(u8 bank, unsigned bank_base) { - return IRQ_STATUS_REG + bank * IRQ_MEM_SIZE; + return IRQ_STATUS_REG + (bank_base + bank) * IRQ_MEM_SIZE; } -static inline u32 sunxi_irq_status_reg(u16 irq) +static inline u32 sunxi_irq_status_reg(u16 irq, unsigned bank_base) { u8 bank = irq / IRQ_PER_BANK; - return sunxi_irq_status_reg_from_bank(bank); + return sunxi_irq_status_reg_from_bank(bank, bank_base); } static inline u32 sunxi_irq_status_offset(u16 irq) diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c index 208a4ce1e40e..7a9307294a6d 100644 --- a/drivers/platform/msm/ipa/ipa_api.c +++ b/drivers/platform/msm/ipa/ipa_api.c @@ -369,6 +369,24 @@ int ipa_reset_endpoint(u32 clnt_hdl) } EXPORT_SYMBOL(ipa_reset_endpoint); +/** +* ipa_disable_endpoint() - Disable an endpoint from IPA perspective +* @clnt_hdl: [in] IPA client handle +* +* Returns: 0 on success, negative on failure +* +* Note: Should not be called from atomic context +*/ +int ipa_disable_endpoint(u32 clnt_hdl) +{ + int ret; + + IPA_API_DISPATCH_RETURN(ipa_disable_endpoint, clnt_hdl); + + return ret; +} +EXPORT_SYMBOL(ipa_disable_endpoint); + /** * ipa_cfg_ep - IPA end-point configuration diff --git a/drivers/platform/msm/ipa/ipa_api.h b/drivers/platform/msm/ipa/ipa_api.h index 862bdc475025..3c2471dd11dd 100644 --- a/drivers/platform/msm/ipa/ipa_api.h +++ b/drivers/platform/msm/ipa/ipa_api.h @@ -26,6 +26,8 @@ struct ipa_api_controller { int (*ipa_clear_endpoint_delay)(u32 clnt_hdl); + int (*ipa_disable_endpoint)(u32 clnt_hdl); + int (*ipa_cfg_ep)(u32 clnt_hdl, const struct ipa_ep_cfg *ipa_ep_cfg); int (*ipa_cfg_ep_nat)(u32 clnt_hdl, diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c index d7a987335dda..8e583203abda 100644 --- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c +++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c @@ -162,6 +162,12 @@ struct ipa3_usb_transport_type_ctx { void *user_data; enum ipa3_usb_state state; struct finish_suspend_work_context finish_suspend_work; + struct ipa_usb_xdci_chan_params ch_params; +}; + +struct ipa3_usb_smmu_reg_map { + int cnt; + phys_addr_t addr; }; struct ipa3_usb_context { @@ -179,6 +185,7 @@ struct ipa3_usb_context { ttype_ctx[IPA_USB_TRANSPORT_MAX]; struct dentry *dfile_state_info; struct dentry *dent; + struct ipa3_usb_smmu_reg_map smmu_reg_map; }; enum ipa3_usb_op { @@ -1061,8 +1068,8 @@ static bool ipa3_usb_check_chan_params(struct ipa_usb_xdci_chan_params *params) IPA_USB_DBG_LOW("xfer_ring_len = %d\n", params->xfer_ring_len); IPA_USB_DBG_LOW("xfer_ring_base_addr = %llx\n", params->xfer_ring_base_addr); - IPA_USB_DBG_LOW("last_trb_addr = %x\n", - params->xfer_scratch.last_trb_addr); + IPA_USB_DBG_LOW("last_trb_addr_iova = %x\n", + params->xfer_scratch.last_trb_addr_iova); IPA_USB_DBG_LOW("const_buffer_size = %d\n", params->xfer_scratch.const_buffer_size); IPA_USB_DBG_LOW("depcmd_low_addr = %x\n", @@ -1112,6 +1119,74 @@ static bool ipa3_usb_check_chan_params(struct ipa_usb_xdci_chan_params *params) return true; } +static int ipa3_usb_smmu_map_xdci_channel( + struct ipa_usb_xdci_chan_params *params, bool map) +{ + int result; + u32 gevntcount_r = rounddown(params->gevntcount_low_addr, PAGE_SIZE); + u32 xfer_scratch_r = + rounddown(params->xfer_scratch.depcmd_low_addr, PAGE_SIZE); + + if (gevntcount_r != xfer_scratch_r) { + IPA_USB_ERR("No support more than 1 page map for USB regs\n"); + WARN_ON(1); + return -EINVAL; + } + + if (map) { + if (ipa3_usb_ctx->smmu_reg_map.cnt == 0) { + ipa3_usb_ctx->smmu_reg_map.addr = gevntcount_r; + result = ipa3_smmu_map_peer_reg( + ipa3_usb_ctx->smmu_reg_map.addr, true); + if (result) { + IPA_USB_ERR("failed to map USB regs %d\n", + result); + return result; + } + } else { + if (gevntcount_r != ipa3_usb_ctx->smmu_reg_map.addr) { + IPA_USB_ERR( + "No support for map different reg\n"); + return -EINVAL; + } + } + ipa3_usb_ctx->smmu_reg_map.cnt++; + } else { + if (gevntcount_r != ipa3_usb_ctx->smmu_reg_map.addr) { + IPA_USB_ERR( + "No support for map different reg\n"); + return -EINVAL; + } + + if (ipa3_usb_ctx->smmu_reg_map.cnt == 1) { + result = ipa3_smmu_map_peer_reg( + ipa3_usb_ctx->smmu_reg_map.addr, false); + if (result) { + IPA_USB_ERR("failed to unmap USB regs %d\n", + result); + return result; + } + } + ipa3_usb_ctx->smmu_reg_map.cnt--; + } + + result = ipa3_smmu_map_peer_buff(params->xfer_ring_base_addr_iova, + params->xfer_ring_base_addr, params->xfer_ring_len, map); + if (result) { + IPA_USB_ERR("failed to map Xfer ring %d\n", result); + return result; + } + + result = ipa3_smmu_map_peer_buff(params->data_buff_base_addr_iova, + params->data_buff_base_addr, params->data_buff_base_len, map); + if (result) { + IPA_USB_ERR("failed to map TRBs buff %d\n", result); + return result; + } + + return 0; +} + static int ipa3_usb_request_xdci_channel( struct ipa_usb_xdci_chan_params *params, struct ipa_req_chan_out_params *out_params) @@ -1186,6 +1261,16 @@ static int ipa3_usb_request_xdci_channel( default: break; } + + result = ipa3_usb_smmu_map_xdci_channel(params, true); + if (result) { + IPA_USB_ERR("failed to smmu map %d\n", result); + return result; + } + + /* store channel params for SMMU unmap */ + ipa3_usb_ctx->ttype_ctx[ttype].ch_params = *params; + chan_params.keep_ipa_awake = params->keep_ipa_awake; chan_params.evt_ring_params.intf = GSI_EVT_CHTYPE_XDCI_EV; chan_params.evt_ring_params.intr = GSI_INTR_IRQ; @@ -1227,7 +1312,7 @@ static int ipa3_usb_request_xdci_channel( chan_params.chan_params.err_cb = ipa3_usb_gsi_chan_err_cb; chan_params.chan_params.chan_user_data = NULL; chan_params.chan_scratch.xdci.last_trb_addr = - params->xfer_scratch.last_trb_addr; + params->xfer_scratch.last_trb_addr_iova; /* xferrscidx will be updated later */ chan_params.chan_scratch.xdci.xferrscidx = 0; chan_params.chan_scratch.xdci.const_buffer_size = @@ -1243,6 +1328,7 @@ static int ipa3_usb_request_xdci_channel( result = ipa3_request_gsi_channel(&chan_params, out_params); if (result) { IPA_USB_ERR("failed to allocate GSI channel\n"); + ipa3_usb_smmu_map_xdci_channel(params, false); return result; } @@ -1273,6 +1359,9 @@ static int ipa3_usb_release_xdci_channel(u32 clnt_hdl, return result; } + result = ipa3_usb_smmu_map_xdci_channel( + &ipa3_usb_ctx->ttype_ctx[ttype].ch_params, false); + /* Change ipa_usb state to INITIALIZED */ if (!ipa3_usb_set_state(IPA_USB_INITIALIZED, false, ttype)) IPA_USB_ERR("failed to change state to initialized\n"); diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c index 1f9fd7a38a37..e94b144457ce 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c @@ -65,6 +65,10 @@ #define IPA2_ACTIVE_CLIENT_LOG_TYPE_RESOURCE 2 #define IPA2_ACTIVE_CLIENT_LOG_TYPE_SPECIAL 3 +#define MAX_POLLING_ITERATION 40 +#define MIN_POLLING_ITERATION 1 +#define ONE_MSEC 1 + #define IPA_AGGR_STR_IN_BYTES(str) \ (strnlen((str), IPA_AGGR_MAX_STR_LENGTH - 1) + 1) @@ -1524,7 +1528,7 @@ static void ipa_free_buffer(void *user1, int user2) kfree(user1); } -static int ipa_q6_pipe_delay(bool zip_pipes) +int ipa_q6_pipe_delay(bool zip_pipes) { u32 reg_val = 0; int client_idx; @@ -1911,14 +1915,14 @@ int ipa_q6_pre_shutdown_cleanup(void) BUG(); IPA_ACTIVE_CLIENTS_INC_SPECIAL("Q6"); + /* - * pipe delay and holb discard for ZIP pipes are handled - * in post shutdown callback. + * Do not delay Q6 pipes here. This may result in IPA reading a + * DMA_TASK with lock bit set and then Q6 pipe delay is set. In this + * situation IPA will be remain locked as the DMA_TASK with unlock + * bit will not be read by IPA as pipe delay is enabled. IPA uC will + * wait for pipe to be empty before issuing a BAM pipe reset. */ - if (ipa_q6_pipe_delay(false)) { - IPAERR("Failed to delay Q6 pipes\n"); - BUG(); - } if (ipa_q6_monitor_holb_mitigation(false)) { IPAERR("Failed to disable HOLB monitroing on Q6 pipes\n"); @@ -1958,13 +1962,13 @@ int ipa_q6_post_shutdown_cleanup(void) int res; /* - * pipe delay and holb discard for ZIP pipes are handled in - * post shutdown. + * Do not delay Q6 pipes here. This may result in IPA reading a + * DMA_TASK with lock bit set and then Q6 pipe delay is set. In this + * situation IPA will be remain locked as the DMA_TASK with unlock + * bit will not be read by IPA as pipe delay is enabled. IPA uC will + * wait for pipe to be empty before issuing a BAM pipe reset. */ - if (ipa_q6_pipe_delay(true)) { - IPAERR("Failed to delay Q6 ZIP pipes\n"); - BUG(); - } + if (ipa_q6_avoid_holb(true)) { IPAERR("Failed to set HOLB on Q6 ZIP pipes\n"); BUG(); @@ -3613,6 +3617,19 @@ static int ipa_init(const struct ipa_plat_drv_res *resource_p, ipa_ctx->use_dma_zone = resource_p->use_dma_zone; ipa_ctx->tethered_flow_control = resource_p->tethered_flow_control; + /* Setting up IPA RX Polling Timeout Seconds */ + ipa_rx_timeout_min_max_calc(&ipa_ctx->ipa_rx_min_timeout_usec, + &ipa_ctx->ipa_rx_max_timeout_usec, + resource_p->ipa_rx_polling_sleep_msec); + + /* Setting up ipa polling iteration */ + if ((resource_p->ipa_polling_iteration >= MIN_POLLING_ITERATION) + && (resource_p->ipa_polling_iteration <= MAX_POLLING_ITERATION)) + ipa_ctx->ipa_polling_iteration = + resource_p->ipa_polling_iteration; + else + ipa_ctx->ipa_polling_iteration = MAX_POLLING_ITERATION; + /* default aggregation parameters */ ipa_ctx->aggregation_type = IPA_MBIM_16; ipa_ctx->aggregation_byte_limit = 1; @@ -4268,6 +4285,31 @@ static int get_ipa_dts_configuration(struct platform_device *pdev, if (result) ipa_drv_res->ee = 0; + /* Get IPA RX Polling Timeout Seconds */ + result = of_property_read_u32(pdev->dev.of_node, + "qcom,rx-polling-sleep-ms", + &ipa_drv_res->ipa_rx_polling_sleep_msec); + + if (result) { + ipa_drv_res->ipa_rx_polling_sleep_msec = ONE_MSEC; + IPADBG("using default polling timeout of 1MSec\n"); + } else { + IPADBG(": found ipa_drv_res->ipa_rx_polling_sleep_sec = %d", + ipa_drv_res->ipa_rx_polling_sleep_msec); + } + + /* Get IPA Polling Iteration */ + result = of_property_read_u32(pdev->dev.of_node, + "qcom,ipa-polling-iteration", + &ipa_drv_res->ipa_polling_iteration); + if (result) { + ipa_drv_res->ipa_polling_iteration = MAX_POLLING_ITERATION; + IPADBG("using default polling iteration\n"); + } else { + IPADBG(": found ipa_drv_res->ipa_polling_iteration = %d", + ipa_drv_res->ipa_polling_iteration); + } + return 0; } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_client.c b/drivers/platform/msm/ipa/ipa_v2/ipa_client.c index 64246ac4eec0..66e329a03df7 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_client.c @@ -560,22 +560,30 @@ int ipa2_disconnect(u32 clnt_hdl) if (!ep->keep_ipa_awake) IPA_ACTIVE_CLIENTS_INC_EP(client_type); - /* Set Disconnect in Progress flag. */ - spin_lock(&ipa_ctx->disconnect_lock); - ep->disconnect_in_progress = true; - spin_unlock(&ipa_ctx->disconnect_lock); - - /* Notify uc to stop monitoring holb on USB BAM Producer pipe. */ - if (IPA_CLIENT_IS_USB_CONS(ep->client)) { - ipa_uc_monitor_holb(ep->client, false); - IPADBG("Disabling holb monitor for client: %d\n", ep->client); - } + /* For USB 2.0 controller, first the ep will be disabled. + * so this sequence is not needed again when disconnecting the pipe. + */ + if (!ep->ep_disabled) { + /* Set Disconnect in Progress flag. */ + spin_lock(&ipa_ctx->disconnect_lock); + ep->disconnect_in_progress = true; + spin_unlock(&ipa_ctx->disconnect_lock); + + /* Notify uc to stop monitoring holb on USB BAM + * Producer pipe. + */ + if (IPA_CLIENT_IS_USB_CONS(ep->client)) { + ipa_uc_monitor_holb(ep->client, false); + IPADBG("Disabling holb monitor for client: %d\n", + ep->client); + } - result = ipa_disable_data_path(clnt_hdl); - if (result) { - IPAERR("disable data path failed res=%d clnt=%d.\n", result, - clnt_hdl); - return -EPERM; + result = ipa_disable_data_path(clnt_hdl); + if (result) { + IPAERR("disable data path failed res=%d clnt=%d.\n", + result, clnt_hdl); + return -EPERM; + } } result = sps_disconnect(ep->ep_hdl); @@ -784,6 +792,82 @@ int ipa2_clear_endpoint_delay(u32 clnt_hdl) } /** + * ipa2_disable_endpoint() - low-level IPA client disable endpoint + * @clnt_hdl: [in] opaque client handle assigned by IPA to client + * + * Should be called by the driver of the peripheral that wants to + * disable the pipe from IPA in BAM-BAM mode. + * + * Returns: 0 on success, negative on failure + * + * Note: Should not be called from atomic context + */ +int ipa2_disable_endpoint(u32 clnt_hdl) +{ + int result; + struct ipa_ep_context *ep; + enum ipa_client_type client_type; + unsigned long bam; + + if (unlikely(!ipa_ctx)) { + IPAERR("IPA driver was not initialized\n"); + return -EINVAL; + } + + if (clnt_hdl >= ipa_ctx->ipa_num_pipes || + ipa_ctx->ep[clnt_hdl].valid == 0) { + IPAERR("bad parm.\n"); + return -EINVAL; + } + + ep = &ipa_ctx->ep[clnt_hdl]; + client_type = ipa2_get_client_mapping(clnt_hdl); + IPA_ACTIVE_CLIENTS_INC_EP(client_type); + + /* Set Disconnect in Progress flag. */ + spin_lock(&ipa_ctx->disconnect_lock); + ep->disconnect_in_progress = true; + spin_unlock(&ipa_ctx->disconnect_lock); + + /* Notify uc to stop monitoring holb on USB BAM Producer pipe. */ + if (IPA_CLIENT_IS_USB_CONS(ep->client)) { + ipa_uc_monitor_holb(ep->client, false); + IPADBG("Disabling holb monitor for client: %d\n", ep->client); + } + + result = ipa_disable_data_path(clnt_hdl); + if (result) { + IPAERR("disable data path failed res=%d clnt=%d.\n", result, + clnt_hdl); + goto fail; + } + + if (IPA_CLIENT_IS_CONS(ep->client)) + bam = ep->connect.source; + else + bam = ep->connect.destination; + + result = sps_pipe_reset(bam, clnt_hdl); + if (result) { + IPAERR("SPS pipe reset failed.\n"); + goto fail; + } + + ep->ep_disabled = true; + + IPA_ACTIVE_CLIENTS_DEC_EP(client_type); + + IPADBG("client (ep: %d) disabled\n", clnt_hdl); + + return 0; + +fail: + IPA_ACTIVE_CLIENTS_DEC_EP(client_type); + return -EPERM; +} + + +/** * ipa_sps_connect_safe() - connect endpoint from BAM prespective * @h: [in] sps pipe handle * @connect: [in] sps connect parameters diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c index 566cb4d03c51..ca3c6d0a1c1a 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c @@ -25,6 +25,12 @@ * IPA2_ACTIVE_CLIENTS_LOG_BUFFER_SIZE_LINES) \ + IPA_MAX_MSG_LEN) +#define RX_MIN_POLL_CNT "Rx Min Poll Count" +#define RX_MAX_POLL_CNT "Rx Max Poll Count" +#define MAX_COUNT_LENGTH 6 +#define MAX_POLLING_ITERATION 40 +#define MIN_POLLING_ITERATION 1 + #define IPA_DUMP_STATUS_FIELD(f) \ pr_err(#f "=0x%x\n", status->f) @@ -110,6 +116,9 @@ static struct dentry *dfile_ip4_nat; static struct dentry *dfile_rm_stats; static struct dentry *dfile_status_stats; static struct dentry *dfile_active_clients; +static struct dentry *dfile_ipa_rx_poll_timeout; +static struct dentry *dfile_ipa_poll_iteration; + static char dbg_buff[IPA_MAX_MSG_LEN]; static char *active_clients_buf; static s8 ep_reg_idx; @@ -1597,6 +1606,97 @@ static ssize_t ipa2_clear_active_clients_log(struct file *file, return count; } +static ssize_t ipa_read_rx_polling_timeout(struct file *file, + char __user *ubuf, size_t count, loff_t *ppos) +{ + int min_cnt; + int max_cnt; + + if (active_clients_buf == NULL) { + IPAERR("Active Clients buffer is not allocated"); + return 0; + } + memset(active_clients_buf, 0, IPA_DBG_ACTIVE_CLIENTS_BUF_SIZE); + min_cnt = scnprintf(active_clients_buf, + IPA_DBG_ACTIVE_CLIENTS_BUF_SIZE, + "Rx Min Poll count = %u\n", + ipa_ctx->ipa_rx_min_timeout_usec); + + max_cnt = scnprintf(active_clients_buf + min_cnt, + IPA_DBG_ACTIVE_CLIENTS_BUF_SIZE, + "Rx Max Poll count = %u\n", + ipa_ctx->ipa_rx_max_timeout_usec); + + return simple_read_from_buffer(ubuf, count, ppos, active_clients_buf, + min_cnt + max_cnt); +} + +static ssize_t ipa_write_rx_polling_timeout(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + s8 polltime = 0; + + if (sizeof(dbg_buff) < count + 1) + return -EFAULT; + + if (copy_from_user(dbg_buff, ubuf, count)) + return -EFAULT; + + dbg_buff[count] = '\0'; + + if (kstrtos8(dbg_buff, 0, &polltime)) + return -EFAULT; + + ipa_rx_timeout_min_max_calc(&ipa_ctx->ipa_rx_min_timeout_usec, + &ipa_ctx->ipa_rx_max_timeout_usec, polltime); + return count; +} + +static ssize_t ipa_read_polling_iteration(struct file *file, + char __user *ubuf, size_t count, loff_t *ppos) +{ + int cnt; + + if (active_clients_buf == NULL) { + IPAERR("Active Clients buffer is not allocated"); + return 0; + } + + memset(active_clients_buf, 0, IPA_DBG_ACTIVE_CLIENTS_BUF_SIZE); + + cnt = scnprintf(active_clients_buf, IPA_DBG_ACTIVE_CLIENTS_BUF_SIZE, + "Polling Iteration count = %u\n", + ipa_ctx->ipa_polling_iteration); + + return simple_read_from_buffer(ubuf, count, ppos, active_clients_buf, + cnt); +} + +static ssize_t ipa_write_polling_iteration(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + s8 iteration_cnt = 0; + + if (sizeof(dbg_buff) < count + 1) + return -EFAULT; + + if (copy_from_user(dbg_buff, ubuf, count)) + return -EFAULT; + + dbg_buff[count] = '\0'; + + if (kstrtos8(dbg_buff, 0, &iteration_cnt)) + return -EFAULT; + + if ((iteration_cnt >= MIN_POLLING_ITERATION) && + (iteration_cnt <= MAX_POLLING_ITERATION)) + ipa_ctx->ipa_polling_iteration = iteration_cnt; + else + ipa_ctx->ipa_polling_iteration = MAX_POLLING_ITERATION; + + return count; +} + const struct file_operations ipa_gen_reg_ops = { .read = ipa_read_gen_reg, }; @@ -1671,6 +1771,16 @@ const struct file_operations ipa2_active_clients = { .write = ipa2_clear_active_clients_log, }; +const struct file_operations ipa_rx_poll_time_ops = { + .read = ipa_read_rx_polling_timeout, + .write = ipa_write_rx_polling_timeout, +}; + +const struct file_operations ipa_poll_iteration_ops = { + .read = ipa_read_polling_iteration, + .write = ipa_write_polling_iteration, +}; + void ipa_debugfs_init(void) { const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH; @@ -1832,6 +1942,20 @@ void ipa_debugfs_init(void) goto fail; } + dfile_ipa_rx_poll_timeout = debugfs_create_file("ipa_rx_poll_time", + read_write_mode, dent, 0, &ipa_rx_poll_time_ops); + if (!dfile_ipa_rx_poll_timeout || IS_ERR(dfile_ipa_rx_poll_timeout)) { + IPAERR("fail to create file for debug_fs rx poll timeout\n"); + goto fail; + } + + dfile_ipa_poll_iteration = debugfs_create_file("ipa_poll_iteration", + read_write_mode, dent, 0, &ipa_poll_iteration_ops); + if (!dfile_ipa_poll_iteration || IS_ERR(dfile_ipa_poll_iteration)) { + IPAERR("fail to create file for debug_fs poll iteration\n"); + goto fail; + } + file = debugfs_create_u32("enable_clock_scaling", read_write_mode, dent, &ipa_ctx->enable_clock_scaling); if (!file) { diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c index 1c93ac16d419..0bb863037772 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c @@ -20,8 +20,6 @@ #define IPA_LAST_DESC_CNT 0xFFFF #define POLLING_INACTIVITY_RX 40 -#define POLLING_MIN_SLEEP_RX 1010 -#define POLLING_MAX_SLEEP_RX 1050 #define POLLING_INACTIVITY_TX 40 #define POLLING_MIN_SLEEP_TX 400 #define POLLING_MAX_SLEEP_TX 500 @@ -1045,8 +1043,8 @@ static void ipa_handle_rx(struct ipa_sys_context *sys) if (cnt == 0) { inactive_cycles++; trace_idle_sleep_enter(sys->ep->client); - usleep_range(POLLING_MIN_SLEEP_RX, - POLLING_MAX_SLEEP_RX); + usleep_range(ipa_ctx->ipa_rx_min_timeout_usec, + ipa_ctx->ipa_rx_max_timeout_usec); trace_idle_sleep_exit(sys->ep->client); } else { inactive_cycles = 0; @@ -1059,7 +1057,7 @@ static void ipa_handle_rx(struct ipa_sys_context *sys) if (sys->len == 0) break; - } while (inactive_cycles <= POLLING_INACTIVITY_RX); + } while (inactive_cycles <= ipa_ctx->ipa_polling_iteration); trace_poll_to_intr(sys->ep->client); ipa_rx_switch_to_intr_mode(sys); @@ -3171,7 +3169,7 @@ static int ipa_assign_policy_v2(struct ipa_sys_connect_params *in, IPA_GENERIC_RX_BUFF_SZ( ipa_adjust_ra_buff_base_sz( in->ipa_ep_cfg.aggr. - aggr_byte_limit)); + aggr_byte_limit - IPA_HEADROOM)); in->ipa_ep_cfg.aggr. aggr_byte_limit = sys->rx_buff_sz < in-> diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c index 790a0b41147e..62e026262663 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c @@ -581,7 +581,8 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, return 0; bad_len: - hdr_entry->ref_cnt--; + if (add_ref_hdr) + hdr_entry->ref_cnt--; entry->cookie = 0; kmem_cache_free(ipa_ctx->hdr_proc_ctx_cache, entry); return -EPERM; @@ -761,7 +762,7 @@ static int __ipa_del_hdr_proc_ctx(u32 proc_ctx_hdl, bool release_hdr) } if (release_hdr) - __ipa_release_hdr(entry->hdr->id); + __ipa_del_hdr(entry->hdr->id); /* move the offset entry to appropriate free list */ list_move(&entry->offset_entry->link, @@ -1089,12 +1090,19 @@ int ipa2_reset_hdr(void) &ipa_ctx->hdr_tbl.head_hdr_entry_list, link) { /* do not remove the default header */ - if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME)) + if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME)) { + if (entry->is_hdr_proc_ctx) { + mutex_unlock(&ipa_ctx->lock); + WARN_ON(1); + IPAERR("default header is proc ctx\n"); + return -EFAULT; + } continue; + } if (ipa_id_find(entry->id) == NULL) { - WARN_ON(1); mutex_unlock(&ipa_ctx->lock); + WARN_ON(1); return -EFAULT; } if (entry->is_hdr_proc_ctx) { @@ -1147,8 +1155,8 @@ int ipa2_reset_hdr(void) link) { if (ipa_id_find(ctx_entry->id) == NULL) { - WARN_ON(1); mutex_unlock(&ipa_ctx->lock); + WARN_ON(1); return -EFAULT; } list_del(&ctx_entry->link); @@ -1311,8 +1319,8 @@ int ipa2_put_hdr(u32 hdr_hdl) goto bail; } - if (entry == NULL || entry->cookie != IPA_COOKIE) { - IPAERR("bad params\n"); + if (entry->cookie != IPA_COOKIE) { + IPAERR("invalid header entry\n"); result = -EINVAL; goto bail; } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h index f94418efc927..6a5b779b24f8 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h @@ -242,7 +242,7 @@ struct ipa_rt_tbl { * @is_partial: flag indicating if header table entry is partial * @is_hdr_proc_ctx: false - hdr entry resides in hdr table, * true - hdr entry resides in DDR and pointed to by proc ctx - * @phys_base: physical address of entry in SRAM when is_hdr_proc_ctx is true, + * @phys_base: physical address of entry in DDR when is_hdr_proc_ctx is true, * else 0 * @proc_ctx: processing context header * @offset_entry: entry's offset @@ -553,6 +553,7 @@ struct ipa_ep_context { bool switch_to_intr; int inactive_cycles; u32 eot_in_poll_err; + bool ep_disabled; /* sys MUST be the last element of this struct */ struct ipa_sys_context *sys; @@ -1244,6 +1245,9 @@ struct ipa_context { /* M-release support to know client pipes */ struct ipacm_client_info ipacm_client[IPA_MAX_NUM_PIPES]; bool tethered_flow_control; + u32 ipa_rx_min_timeout_usec; + u32 ipa_rx_max_timeout_usec; + u32 ipa_polling_iteration; }; /** @@ -1295,6 +1299,8 @@ struct ipa_plat_drv_res { bool skip_uc_pipe_reset; bool use_dma_zone; bool tethered_flow_control; + u32 ipa_rx_polling_sleep_msec; + u32 ipa_polling_iteration; }; struct ipa_mem_partition { @@ -1426,6 +1432,11 @@ int ipa2_reset_endpoint(u32 clnt_hdl); int ipa2_clear_endpoint_delay(u32 clnt_hdl); /* + * Disable ep + */ +int ipa2_disable_endpoint(u32 clnt_hdl); + +/* * Configuration */ int ipa2_cfg_ep(u32 clnt_hdl, const struct ipa_ep_cfg *ipa_ep_cfg); @@ -1730,6 +1741,9 @@ void ipa_debugfs_init(void); void ipa_debugfs_remove(void); void ipa_dump_buff_internal(void *base, dma_addr_t phy_base, u32 size); + +void ipa_rx_timeout_min_max_calc(u32 *min, u32 *max, s8 time); + #ifdef IPA_DEBUG #define IPA_DUMP_BUFF(base, phy_base, size) \ ipa_dump_buff_internal(base, phy_base, size) diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c index 7c10c4cee150..e8f25c9c23d3 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c @@ -276,8 +276,6 @@ fail_ep_exists: */ int ipa2_disconnect_mhi_pipe(u32 clnt_hdl) { - struct ipa_ep_context *ep; - IPA_MHI_FUNC_ENTRY(); if (clnt_hdl >= ipa_ctx->ipa_num_pipes) { @@ -290,7 +288,8 @@ int ipa2_disconnect_mhi_pipe(u32 clnt_hdl) return -EINVAL; } - ep->valid = 0; + ipa_ctx->ep[clnt_hdl].valid = 0; + ipa_delete_dflt_flt_rules(clnt_hdl); IPA_MHI_DBG("client (ep: %d) disconnected\n", clnt_hdl); @@ -302,14 +301,13 @@ int ipa2_mhi_resume_channels_internal(enum ipa_client_type client, bool LPTransitionRejected, bool brstmode_enabled, union __packed gsi_channel_scratch ch_scratch, u8 index) { - int i; int res; IPA_MHI_FUNC_ENTRY(); res = ipa_uc_mhi_resume_channel(index, LPTransitionRejected); if (res) { - IPA_MHI_ERR("failed to suspend channel %d error %d\n", - i, res); + IPA_MHI_ERR("failed to suspend channel %u error %d\n", + index, res); return res; } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c index 9d4704ded0c3..15476f38cf44 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c @@ -1008,6 +1008,10 @@ static int __ipa_add_rt_rule(enum ipa_ip_type ip, const char *name, return 0; ipa_insert_failed: + if (entry->hdr) + entry->hdr->ref_cnt--; + else if (entry->proc_ctx) + entry->proc_ctx->ref_cnt--; list_del(&entry->link); kmem_cache_free(ipa_ctx->rt_rule_cache, entry); error: diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c index 0dd10743a01e..1d88082352c6 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c @@ -43,6 +43,11 @@ #define IPA_AGGR_GRAN_MAX (32) #define IPA_EOT_COAL_GRAN_MIN (1) #define IPA_EOT_COAL_GRAN_MAX (16) +#define MSEC 1000 +#define MIN_RX_POLL_TIME 1 +#define MAX_RX_POLL_TIME 5 +#define UPPER_CUTOFF 50 +#define LOWER_CUTOFF 10 #define IPA_DEFAULT_SYS_YELLOW_WM 32 @@ -3623,6 +3628,30 @@ void ipa_dump_buff_internal(void *base, dma_addr_t phy_base, u32 size) } /** + * void ipa_rx_timeout_min_max_calc() - calc min max timeout time of rx polling + * @time: time fom dtsi entry or from debugfs file system + * @min: rx polling min timeout + * @max: rx polling max timeout + * Maximum time could be of 10Msec allowed. + */ +void ipa_rx_timeout_min_max_calc(u32 *min, u32 *max, s8 time) +{ + if ((time >= MIN_RX_POLL_TIME) && + (time <= MAX_RX_POLL_TIME)) { + *min = (time * MSEC) + LOWER_CUTOFF; + *max = (time * MSEC) + UPPER_CUTOFF; + } else { + /* Setting up the default min max time */ + IPADBG("Setting up default rx polling timeout\n"); + *min = (MIN_RX_POLL_TIME * MSEC) + + LOWER_CUTOFF; + *max = (MIN_RX_POLL_TIME * MSEC) + + UPPER_CUTOFF; + } + IPADBG("Rx polling timeout Min = %u len = %u\n", *min, *max); +} + +/** * ipa_pipe_mem_init() - initialize the pipe memory * @start_ofst: start offset * @size: size @@ -4920,6 +4949,7 @@ int ipa2_bind_api_controller(enum ipa_hw_type ipa_hw_type, api_ctrl->ipa_disconnect = ipa2_disconnect; api_ctrl->ipa_reset_endpoint = ipa2_reset_endpoint; api_ctrl->ipa_clear_endpoint_delay = ipa2_clear_endpoint_delay; + api_ctrl->ipa_disable_endpoint = ipa2_disable_endpoint; api_ctrl->ipa_cfg_ep = ipa2_cfg_ep; api_ctrl->ipa_cfg_ep_nat = ipa2_cfg_ep_nat; api_ctrl->ipa_cfg_ep_hdr = ipa2_cfg_ep_hdr; diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index 50e820992f29..2420dd78b4c0 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -1144,14 +1144,16 @@ static void apps_ipa_tx_complete_notify(void *priv, struct net_device *dev = (struct net_device *)priv; struct wwan_private *wwan_ptr; - if (evt != IPA_WRITE_DONE) { - IPAWANDBG("unsupported event on Tx callback\n"); + if (dev != ipa_netdevs[0]) { + IPAWANDBG("Received pre-SSR packet completion\n"); + dev_kfree_skb_any(skb); return; } - if (dev != ipa_netdevs[0]) { - IPAWANDBG("Received pre-SSR packet completion\n"); + if (evt != IPA_WRITE_DONE) { + IPAWANERR("unsupported evt on Tx callback, Drop the packet\n"); dev_kfree_skb_any(skb); + dev->stats.tx_dropped++; return; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index c553be1ad717..33066e8b9c19 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -37,6 +37,7 @@ #include <linux/hashtable.h> #include <linux/hash.h> #include <soc/qcom/subsystem_restart.h> +#include <soc/qcom/smem.h> #define IPA_SUBSYSTEM_NAME "ipa_fws" #include "ipa_i.h" #include "../ipa_rm_i.h" @@ -75,6 +76,17 @@ #define IPA3_ACTIVE_CLIENT_LOG_TYPE_RESOURCE 2 #define IPA3_ACTIVE_CLIENT_LOG_TYPE_SPECIAL 3 +#define IPA_SMEM_SIZE (8 * 1024) + +/* round addresses for closes page per SMMU requirements */ +#define IPA_SMMU_ROUND_TO_PAGE(iova, pa, size, iova_p, pa_p, size_p) \ + do { \ + (iova_p) = rounddown((iova), PAGE_SIZE); \ + (pa_p) = rounddown((pa), PAGE_SIZE); \ + (size_p) = roundup((size) + (pa) - (pa_p), PAGE_SIZE); \ + } while (0) + + /* The relative location in /lib/firmware where the FWs will reside */ #define IPA_FWS_PATH "ipa/ipa_fws.elf" @@ -1887,44 +1899,43 @@ static int ipa3_q6_clean_q6_tables(void) if (ipa3_q6_clean_q6_flt_tbls(IPA_IP_v4, IPA_RULE_HASHABLE)) { IPAERR("failed to clean q6 flt tbls (v4/hashable)\n"); - goto bail_desc; + return -EFAULT; } if (ipa3_q6_clean_q6_flt_tbls(IPA_IP_v6, IPA_RULE_HASHABLE)) { IPAERR("failed to clean q6 flt tbls (v6/hashable)\n"); - goto bail_desc; + return -EFAULT; } if (ipa3_q6_clean_q6_flt_tbls(IPA_IP_v4, IPA_RULE_NON_HASHABLE)) { IPAERR("failed to clean q6 flt tbls (v4/non-hashable)\n"); - goto bail_desc; + return -EFAULT; } if (ipa3_q6_clean_q6_flt_tbls(IPA_IP_v6, IPA_RULE_NON_HASHABLE)) { IPAERR("failed to clean q6 flt tbls (v6/non-hashable)\n"); - goto bail_desc; + return -EFAULT; } if (ipa3_q6_clean_q6_rt_tbls(IPA_IP_v4, IPA_RULE_HASHABLE)) { IPAERR("failed to clean q6 rt tbls (v4/hashable)\n"); - goto bail_desc; + return -EFAULT; } if (ipa3_q6_clean_q6_rt_tbls(IPA_IP_v6, IPA_RULE_HASHABLE)) { IPAERR("failed to clean q6 rt tbls (v6/hashable)\n"); - goto bail_desc; + return -EFAULT; } if (ipa3_q6_clean_q6_rt_tbls(IPA_IP_v4, IPA_RULE_NON_HASHABLE)) { IPAERR("failed to clean q6 rt tbls (v4/non-hashable)\n"); - goto bail_desc; + return -EFAULT; } if (ipa3_q6_clean_q6_rt_tbls(IPA_IP_v6, IPA_RULE_NON_HASHABLE)) { IPAERR("failed to clean q6 rt tbls (v6/non-hashable)\n"); - goto bail_desc; + return -EFAULT; } /* Flush rules cache */ desc = kzalloc(sizeof(struct ipa3_desc), GFP_KERNEL); if (!desc) { IPAERR("failed to allocate memory\n"); - retval = -ENOMEM; - goto bail_dma; + return -ENOMEM; } flush.v4_flt = true; @@ -1941,6 +1952,7 @@ static int ipa3_q6_clean_q6_tables(void) ®_write_cmd, false); if (!cmd_pyld) { IPAERR("fail construct register_write imm cmd\n"); + retval = -EFAULT; goto bail_desc; } desc->opcode = @@ -1957,9 +1969,9 @@ static int ipa3_q6_clean_q6_tables(void) } ipahal_destroy_imm_cmd(cmd_pyld); + bail_desc: kfree(desc); -bail_dma: IPADBG("Done - retval = %d\n", retval); return retval; } @@ -4813,6 +4825,10 @@ static int ipa_smmu_ap_cb_probe(struct device *dev) int fast = 1; int bypass = 1; u32 iova_ap_mapping[2]; + u32 add_map_size; + const u32 *add_map; + void *smem_addr; + int i; IPADBG("AP CB probe: sub pdev=%p\n", dev); @@ -4902,6 +4918,55 @@ static int ipa_smmu_ap_cb_probe(struct device *dev) return result; } + add_map = of_get_property(dev->of_node, + "qcom,additional-mapping", &add_map_size); + if (add_map) { + /* mapping size is an array of 3-tuple of u32 */ + if (add_map_size % (3 * sizeof(u32))) { + IPAERR("wrong additional mapping format\n"); + cb->valid = false; + return -EFAULT; + } + + /* iterate of each entry of the additional mapping array */ + for (i = 0; i < add_map_size / sizeof(u32); i += 3) { + u32 iova = be32_to_cpu(add_map[i]); + u32 pa = be32_to_cpu(add_map[i + 1]); + u32 size = be32_to_cpu(add_map[i + 2]); + unsigned long iova_p; + phys_addr_t pa_p; + u32 size_p; + + IPA_SMMU_ROUND_TO_PAGE(iova, pa, size, + iova_p, pa_p, size_p); + IPADBG("mapping 0x%lx to 0x%pa size %d\n", + iova_p, &pa_p, size_p); + ipa3_iommu_map(cb->mapping->domain, + iova_p, pa_p, size_p, + IOMMU_READ | IOMMU_WRITE | IOMMU_DEVICE); + } + } + + /* map SMEM memory for IPA table accesses */ + smem_addr = smem_alloc(SMEM_IPA_FILTER_TABLE, IPA_SMEM_SIZE, + SMEM_MODEM, 0); + if (smem_addr) { + phys_addr_t iova = smem_virt_to_phys(smem_addr); + phys_addr_t pa = iova; + unsigned long iova_p; + phys_addr_t pa_p; + u32 size_p; + + IPA_SMMU_ROUND_TO_PAGE(iova, pa, IPA_SMEM_SIZE, + iova_p, pa_p, size_p); + IPADBG("mapping 0x%lx to 0x%pa size %d\n", + iova_p, &pa_p, size_p); + ipa3_iommu_map(cb->mapping->domain, + iova_p, pa_p, size_p, + IOMMU_READ | IOMMU_WRITE | IOMMU_DEVICE); + } + + smmu_info.present = true; if (!ipa3_bus_scale_table) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c index 7b0376ecba7e..8326c3fdd9d1 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c @@ -1055,6 +1055,83 @@ static bool ipa3_is_legal_params(struct ipa_request_gsi_channel_params *params) return true; } +int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map) +{ + struct iommu_domain *smmu_domain; + int res; + + if (ipa3_ctx->smmu_s1_bypass) + return 0; + + smmu_domain = ipa3_get_smmu_domain(); + if (!smmu_domain) { + IPAERR("invalid smmu domain\n"); + return -EINVAL; + } + + if (map) { + res = ipa3_iommu_map(smmu_domain, phys_addr, phys_addr, + PAGE_SIZE, IOMMU_READ | IOMMU_WRITE | IOMMU_DEVICE); + } else { + res = iommu_unmap(smmu_domain, phys_addr, PAGE_SIZE); + res = (res != PAGE_SIZE); + } + if (res) { + IPAERR("Fail to %s reg 0x%pa\n", map ? "map" : "unmap", + &phys_addr); + return -EINVAL; + } + + IPADBG("Peer reg 0x%pa %s\n", &phys_addr, map ? "map" : "unmap"); + + return 0; +} + +int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr, u32 size, bool map) +{ + struct iommu_domain *smmu_domain; + int res; + + if (ipa3_ctx->smmu_s1_bypass) + return 0; + + smmu_domain = ipa3_get_smmu_domain(); + if (!smmu_domain) { + IPAERR("invalid smmu domain\n"); + return -EINVAL; + } + + if (map) { + res = ipa3_iommu_map(smmu_domain, + rounddown(iova, PAGE_SIZE), + rounddown(phys_addr, PAGE_SIZE), + roundup(size + iova - rounddown(iova, PAGE_SIZE), + PAGE_SIZE), + IOMMU_READ | IOMMU_WRITE); + if (res) { + IPAERR("Fail to map 0x%llx->0x%pa\n", iova, &phys_addr); + return -EINVAL; + } + } else { + res = iommu_unmap(smmu_domain, + rounddown(iova, PAGE_SIZE), + roundup(size + iova - rounddown(iova, PAGE_SIZE), + PAGE_SIZE)); + if (res != roundup(size + iova - rounddown(iova, PAGE_SIZE), + PAGE_SIZE)) { + IPAERR("Fail to unmap 0x%llx->0x%pa\n", + iova, &phys_addr); + return -EINVAL; + } + } + + IPADBG("Peer buff %s 0x%llx->0x%pa\n", map ? "map" : "unmap", + iova, &phys_addr); + + return 0; +} + + int ipa3_request_gsi_channel(struct ipa_request_gsi_channel_params *params, struct ipa_req_chan_out_params *out_params) { diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index 82df3768ba26..4c600c6131e9 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -1914,6 +1914,7 @@ static void ipa3_replenish_wlan_rx_cache(struct ipa3_sys_context *sys) gsi_xfer_elem_one.addr = rx_pkt->data.dma_addr; gsi_xfer_elem_one.len = IPA_WLAN_RX_BUFF_SZ; gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOT; + gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOB; gsi_xfer_elem_one.type = GSI_XFER_ELEM_DATA; gsi_xfer_elem_one.xfer_user_data = rx_pkt; @@ -2101,6 +2102,7 @@ static void ipa3_replenish_rx_cache(struct ipa3_sys_context *sys) gsi_xfer_elem_one.addr = rx_pkt->data.dma_addr; gsi_xfer_elem_one.len = sys->rx_buff_sz; gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOT; + gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOB; gsi_xfer_elem_one.type = GSI_XFER_ELEM_DATA; gsi_xfer_elem_one.xfer_user_data = rx_pkt; @@ -2207,6 +2209,7 @@ static void ipa3_replenish_rx_cache_recycle(struct ipa3_sys_context *sys) gsi_xfer_elem_one.addr = rx_pkt->data.dma_addr; gsi_xfer_elem_one.len = sys->rx_buff_sz; gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOT; + gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOB; gsi_xfer_elem_one.type = GSI_XFER_ELEM_DATA; gsi_xfer_elem_one.xfer_user_data = rx_pkt; @@ -2272,6 +2275,7 @@ static void ipa3_fast_replenish_rx_cache(struct ipa3_sys_context *sys) gsi_xfer_elem_one.addr = rx_pkt->data.dma_addr; gsi_xfer_elem_one.len = sys->rx_buff_sz; gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOT; + gsi_xfer_elem_one.flags |= GSI_XFER_FLAG_EOB; gsi_xfer_elem_one.type = GSI_XFER_ELEM_DATA; gsi_xfer_elem_one.xfer_user_data = rx_pkt; @@ -2407,7 +2411,6 @@ static int ipa3_lan_rx_pyld_hdlr(struct sk_buff *skb, if (skb->len == 0) { IPAERR("ZLT\n"); - sys->free_skb(skb); return rc; } @@ -2467,7 +2470,6 @@ static int ipa3_lan_rx_pyld_hdlr(struct sk_buff *skb, sys->prev_skb = skb2; } sys->len_rem -= skb->len; - sys->free_skb(skb); return rc; } } @@ -2481,7 +2483,7 @@ begin: if (skb->len < pkt_status_sz) { WARN_ON(sys->prev_skb != NULL); IPADBG_LOW("status straddles buffer\n"); - sys->prev_skb = skb; + sys->prev_skb = skb_copy(skb, GFP_KERNEL); sys->len_partial = skb->len; return rc; } @@ -2573,7 +2575,7 @@ begin: IPAHAL_PKT_STATUS_EXCEPTION_NONE) { WARN_ON(sys->prev_skb != NULL); IPADBG_LOW("Ins header in next buffer\n"); - sys->prev_skb = skb; + sys->prev_skb = skb_copy(skb, GFP_KERNEL); sys->len_partial = skb->len; return rc; } @@ -2594,7 +2596,7 @@ begin: } skb2 = ipa3_skb_copy_for_client(skb, - status.pkt_len + pkt_status_sz); + min(status.pkt_len + pkt_status_sz, skb->len)); if (likely(skb2)) { if (skb->len < len + pkt_status_sz) { IPADBG_LOW("SPL skb len %d len %d\n", @@ -3764,6 +3766,7 @@ static void ipa_gsi_irq_rx_notify_cb(struct gsi_chan_xfer_notify *notify) switch (notify->evt_id) { case GSI_CHAN_EVT_EOT: + case GSI_CHAN_EVT_EOB: atomic_set(&ipa3_ctx->transport_pm.eot_activity, 1); if (!atomic_read(&sys->curr_polling_state)) { /* put the gsi channel into polling mode */ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c index 029647213531..11da023c9d6a 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c @@ -418,7 +418,8 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, return 0; bad_len: - hdr_entry->ref_cnt--; + if (add_ref_hdr) + hdr_entry->ref_cnt--; entry->cookie = 0; kmem_cache_free(ipa3_ctx->hdr_proc_ctx_cache, entry); return -EPERM; @@ -589,7 +590,7 @@ static int __ipa3_del_hdr_proc_ctx(u32 proc_ctx_hdl, bool release_hdr) } if (release_hdr) - __ipa3_release_hdr(entry->hdr->id); + __ipa3_del_hdr(entry->hdr->id); /* move the offset entry to appropriate free list */ list_move(&entry->offset_entry->link, @@ -893,12 +894,19 @@ int ipa3_reset_hdr(void) &ipa3_ctx->hdr_tbl.head_hdr_entry_list, link) { /* do not remove the default header */ - if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME)) + if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME)) { + if (entry->is_hdr_proc_ctx) { + IPAERR("default header is proc ctx\n"); + mutex_unlock(&ipa3_ctx->lock); + WARN_ON(1); + return -EFAULT; + } continue; + } if (ipa3_id_find(entry->id) == NULL) { - WARN_ON(1); mutex_unlock(&ipa3_ctx->lock); + WARN_ON(1); return -EFAULT; } if (entry->is_hdr_proc_ctx) { @@ -951,8 +959,8 @@ int ipa3_reset_hdr(void) link) { if (ipa3_id_find(ctx_entry->id) == NULL) { - WARN_ON(1); mutex_unlock(&ipa3_ctx->lock); + WARN_ON(1); return -EFAULT; } list_del(&ctx_entry->link); @@ -1115,8 +1123,8 @@ int ipa3_put_hdr(u32 hdr_hdl) goto bail; } - if (entry == NULL || entry->cookie != IPA_COOKIE) { - IPAERR("bad params\n"); + if (entry->cookie != IPA_COOKIE) { + IPAERR("invalid header entry\n"); result = -EINVAL; goto bail; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index d2c605d7627c..97a3117d44e9 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -271,7 +271,7 @@ struct ipa3_rt_tbl { * @is_partial: flag indicating if header table entry is partial * @is_hdr_proc_ctx: false - hdr entry resides in hdr table, * true - hdr entry resides in DDR and pointed to by proc ctx - * @phys_base: physical address of entry in SRAM when is_hdr_proc_ctx is true, + * @phys_base: physical address of entry in DDR when is_hdr_proc_ctx is true, * else 0 * @proc_ctx: processing context header * @offset_entry: entry's offset @@ -2194,4 +2194,7 @@ const char *ipa_hw_error_str(enum ipa3_hw_errors err_type); int ipa_gsi_ch20_wa(void); int ipa3_rx_poll(u32 clnt_hdl, int budget); void ipa3_recycle_wan_skb(struct sk_buff *skb); +int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map); +int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr, + u32 size, bool map); #endif /* _IPA3_I_H_ */ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c index 14e2f1f4c510..e83c249ad425 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c @@ -549,11 +549,6 @@ int ipa3_mhi_resume_channels_internal(enum ipa_client_type client, return res; } } - if (res) { - IPA_MHI_ERR("failed to resume channel error %d\n", - res); - return res; - } res = gsi_start_channel(ep->gsi_chan_hdl); if (res) { diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c index 138db3dbde84..b06e33a8258a 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c @@ -957,6 +957,10 @@ static int __ipa_finish_rt_rule_add(struct ipa3_rt_entry *entry, u32 *rule_hdl, return 0; ipa_insert_failed: + if (entry->hdr) + entry->hdr->ref_cnt--; + else if (entry->proc_ctx) + entry->proc_ctx->ref_cnt--; idr_remove(&tbl->rule_ids, entry->rule_id); list_del(&entry->link); kmem_cache_free(ipa3_ctx->rt_rule_cache, entry); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index 21b8cac11d2b..5499eba92b1c 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -40,7 +40,9 @@ #define IPA_TAG_SLEEP_MIN_USEC (1000) #define IPA_TAG_SLEEP_MAX_USEC (2000) #define IPA_FORCE_CLOSE_TAG_PROCESS_TIMEOUT (10 * HZ) -#define IPA_BCR_REG_VAL (0x00000001) +#define IPA_BCR_REG_VAL_v3_0 (0x00000001) +#define IPA_BCR_REG_VAL_v3_1 (0x00000003) +#define IPA_BCR_REG_VAL_v3_5_1 (0x0000003B) #define IPA_AGGR_GRAN_MIN (1) #define IPA_AGGR_GRAN_MAX (32) #define IPA_EOT_COAL_GRAN_MIN (1) @@ -848,14 +850,29 @@ void ipa3_cfg_qsb(void) int ipa3_init_hw(void) { u32 ipa_version = 0; + u32 val; /* Read IPA version and make sure we have access to the registers */ ipa_version = ipahal_read_reg(IPA_VERSION); if (ipa_version == 0) return -EFAULT; - /* using old BCR configuration(IPAv2.6)*/ - ipahal_write_reg(IPA_BCR, IPA_BCR_REG_VAL); + switch (ipa3_ctx->ipa_hw_type) { + case IPA_HW_v3_0: + val = IPA_BCR_REG_VAL_v3_0; + break; + case IPA_HW_v3_1: + val = IPA_BCR_REG_VAL_v3_1; + break; + case IPA_HW_v3_5_1: + val = IPA_BCR_REG_VAL_v3_5_1; + break; + default: + IPAERR("unknown HW type in dts\n"); + return -EFAULT; + } + + ipahal_write_reg(IPA_BCR, val); ipa3_cfg_qsb(); @@ -3029,6 +3046,7 @@ int ipa3_bind_api_controller(enum ipa_hw_type ipa_hw_type, api_ctrl->ipa_disconnect = ipa3_disconnect; api_ctrl->ipa_reset_endpoint = ipa3_reset_endpoint; api_ctrl->ipa_clear_endpoint_delay = ipa3_clear_endpoint_delay; + api_ctrl->ipa_disable_endpoint = NULL; api_ctrl->ipa_cfg_ep = ipa3_cfg_ep; api_ctrl->ipa_cfg_ep_nat = ipa3_cfg_ep_nat; api_ctrl->ipa_cfg_ep_hdr = ipa3_cfg_ep_hdr; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c index 4f6097c6da35..6c4d14b093c3 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c @@ -1215,7 +1215,10 @@ int ipahal_cp_proc_ctx_to_hw_buff(enum ipa_hdr_proc_type type, (!phys_base && !hdr_base_addr) || !hdr_base_addr || ((is_hdr_proc_ctx == false) && !offset_entry)) { - IPAHAL_ERR("failed on validating params"); + IPAHAL_ERR( + "invalid input: hdr_len:%u phys_base:%pad hdr_base_addr:%u is_hdr_proc_ctx:%d offset_entry:%pK\n" + , hdr_len, &phys_base, hdr_base_addr + , is_hdr_proc_ctx, offset_entry); return -EINVAL; } diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index a4eab02cb571..aebdaab3ac77 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -1157,14 +1157,16 @@ static void apps_ipa_tx_complete_notify(void *priv, struct net_device *dev = (struct net_device *)priv; struct ipa3_wwan_private *wwan_ptr; - if (evt != IPA_WRITE_DONE) { - IPAWANDBG("unsupported event on Tx callback\n"); + if (dev != IPA_NETDEV()) { + IPAWANDBG("Received pre-SSR packet completion\n"); + dev_kfree_skb_any(skb); return; } - if (dev != IPA_NETDEV()) { - IPAWANDBG("Received pre-SSR packet completion\n"); + if (evt != IPA_WRITE_DONE) { + IPAWANERR("unsupported evt on Tx callback, Drop the packet\n"); dev_kfree_skb_any(skb); + dev->stats.tx_dropped++; return; } diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index d78ee151c9e4..be3bc2f4edd4 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -865,6 +865,20 @@ static const struct dmi_system_id no_hw_rfkill_list[] = { }, }, { + .ident = "Lenovo ideapad Y700-15ISK", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-15ISK"), + }, + }, + { + .ident = "Lenovo ideapad Y700 Touch-15ISK", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700 Touch-15ISK"), + }, + }, + { .ident = "Lenovo ideapad Y700-17ISK", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 61f611296ad6..a45a5d103040 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -265,6 +265,8 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(typec_power_role), POWER_SUPPLY_ATTR(pd_allowed), POWER_SUPPLY_ATTR(pd_active), + POWER_SUPPLY_ATTR(charger_temp), + POWER_SUPPLY_ATTR(charger_temp_max), /* Local extensions of type int64_t */ POWER_SUPPLY_ATTR(charge_counter_ext), /* Properties of type `const char *' */ diff --git a/drivers/power/qcom-charger/Kconfig b/drivers/power/qcom-charger/Kconfig index b37853b4f70c..7a0b1464ad86 100644 --- a/drivers/power/qcom-charger/Kconfig +++ b/drivers/power/qcom-charger/Kconfig @@ -20,6 +20,16 @@ config QPNP_FG fuel gauge. The state of charge is reported through a BMS power supply property and also sends uevents when the capacity is updated. +config QPNP_FG_GEN3 + tristate "QPNP GEN3 fuel gauge driver" + depends on SPMI + select REGMAP_SPMI + help + Say Y here to enable the GEN3 Fuel Gauge driver. This adds support + for battery fuel gauging and state of charge of battery connected to + the fuel gauge. The state of charge is reported through a BMS power + supply property and also sends uevents when the capacity is updated. + config SMB135X_CHARGER tristate "SMB135X Battery Charger" depends on I2C diff --git a/drivers/power/qcom-charger/Makefile b/drivers/power/qcom-charger/Makefile index df7b78d4fc52..aae6084c3c10 100644 --- a/drivers/power/qcom-charger/Makefile +++ b/drivers/power/qcom-charger/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_QPNP_SMBCHARGER) += qpnp-smbcharger.o batterydata-lib.o pmic-voter.o obj-$(CONFIG_QPNP_FG) += qpnp-fg.o +obj-$(CONFIG_QPNP_FG_GEN3) += qpnp-fg-gen3.o fg-memif.o fg-util.o obj-$(CONFIG_SMB135X_CHARGER) += smb135x-charger.o pmic-voter.o obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o obj-$(CONFIG_MSM_BCL_CTL) += msm_bcl.o diff --git a/drivers/power/qcom-charger/fg-core.h b/drivers/power/qcom-charger/fg-core.h new file mode 100644 index 000000000000..cf7869ea1515 --- /dev/null +++ b/drivers/power/qcom-charger/fg-core.h @@ -0,0 +1,247 @@ +/* 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. + */ + +#ifndef __FG_CORE_H__ +#define __FG_CORE_H__ + +#include <linux/atomic.h> +#include <linux/bitops.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/string_helpers.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include "pmic-voter.h" + +#define fg_dbg(chip, reason, fmt, ...) \ + do { \ + if (*chip->debug_mask & (reason)) \ + pr_info(fmt, ##__VA_ARGS__); \ + else \ + pr_debug(fmt, ##__VA_ARGS__); \ + } while (0) + +#define SRAM_READ "fg_sram_read" +#define SRAM_WRITE "fg_sram_write" +#define SRAM_UPDATE "fg_sram_update" +#define PROFILE_LOAD "fg_profile_load" +#define DELTA_SOC "fg_delta_soc" + +#define DEBUG_PRINT_BUFFER_SIZE 64 +/* 3 byte address + 1 space character */ +#define ADDR_LEN 4 +/* Format is 'XX ' */ +#define CHARS_PER_ITEM 3 +/* 4 data items per line */ +#define ITEMS_PER_LINE 4 +#define MAX_LINE_LENGTH (ADDR_LEN + (ITEMS_PER_LINE * \ + CHARS_PER_ITEM) + 1) \ + +#define FG_SRAM_ADDRESS_MAX 255 + +/* Debug flag definitions */ +enum fg_debug_flag { + FG_IRQ = BIT(0), /* Show interrupts */ + FG_STATUS = BIT(1), /* Show FG status changes */ + FG_POWER_SUPPLY = BIT(2), /* Show POWER_SUPPLY */ + FG_SRAM_WRITE = BIT(3), /* Show SRAM writes */ + FG_SRAM_READ = BIT(4), /* Show SRAM reads */ + FG_BUS_WRITE = BIT(5), /* Show REGMAP writes */ + FG_BUS_READ = BIT(6), /* Show REGMAP reads */ +}; + +/* SRAM access */ +enum sram_access_flags { + FG_IMA_DEFAULT = 0, + FG_IMA_ATOMIC, +}; + +/* JEITA */ +enum { + JEITA_COLD = 0, + JEITA_COOL, + JEITA_WARM, + JEITA_HOT, + NUM_JEITA_LEVELS, +}; + +/* FG irqs */ +enum fg_irq_index { + MSOC_FULL_IRQ = 0, + MSOC_HIGH_IRQ, + MSOC_EMPTY_IRQ, + MSOC_LOW_IRQ, + MSOC_DELTA_IRQ, + BSOC_DELTA_IRQ, + SOC_READY_IRQ, + SOC_UPDATE_IRQ, + BATT_TEMP_DELTA_IRQ, + BATT_MISSING_IRQ, + ESR_DELTA_IRQ, + VBATT_LOW_IRQ, + VBATT_PRED_DELTA_IRQ, + DMA_GRANT_IRQ, + MEM_XCP_IRQ, + IMA_RDY_IRQ, + FG_IRQ_MAX, +}; + +/* WA flags */ +enum { + DELTA_SOC_IRQ_WA = BIT(0), +}; + +/* SRAM parameters */ +enum fg_sram_param_id { + FG_SRAM_BATT_SOC = 0, + FG_SRAM_VOLTAGE_PRED, + FG_SRAM_OCV, + FG_SRAM_RSLOW, + /* Entries below here are configurable during initialization */ + FG_SRAM_CUTOFF_VOLT, + FG_SRAM_EMPTY_VOLT, + FG_SRAM_VBATT_LOW, + FG_SRAM_SYS_TERM_CURR, + FG_SRAM_CHG_TERM_CURR, + FG_SRAM_DELTA_SOC_THR, + FG_SRAM_RECHARGE_SOC_THR, + FG_SRAM_MAX, +}; + +struct fg_sram_param { + u16 address; + int offset; + u8 len; + int value; + int numrtr; + int denmtr; + void (*encode)(struct fg_sram_param *sp, enum fg_sram_param_id id, + int val, u8 *buf); + int (*decode)(struct fg_sram_param *sp, enum fg_sram_param_id id, + int val); +}; + +/* DT parameters for FG device */ +struct fg_dt_props { + int cutoff_volt_mv; + int empty_volt_mv; + int vbatt_low_thr_mv; + int chg_term_curr_ma; + int sys_term_curr_ma; + int delta_soc_thr; + int recharge_soc_thr; + int rsense_sel; + int jeita_thresholds[NUM_JEITA_LEVELS]; +}; + +/* parameters from battery profile */ +struct fg_batt_props { + const char *batt_type_str; + char *batt_profile; + int float_volt_uv; + int fastchg_curr_ma; + int batt_id_kohm; +}; + +struct fg_irq_info { + const char *name; + const irq_handler_t handler; + int irq; + bool wakeable; +}; + +struct fg_chip { + struct device *dev; + struct pmic_revid_data *pmic_rev_id; + struct regmap *regmap; + struct dentry *dentry; + struct power_supply *fg_psy; + struct power_supply *batt_psy; + struct iio_channel *batt_id_chan; + struct fg_memif *sram; + struct fg_irq_info *irqs; + struct votable *awake_votable; + struct fg_sram_param *sp; + int *debug_mask; + char *batt_profile; + struct fg_dt_props dt; + struct fg_batt_props bp; + struct notifier_block nb; + struct mutex bus_lock; + struct mutex sram_rw_lock; + u32 batt_soc_base; + u32 batt_info_base; + u32 mem_if_base; + int nom_cap_uah; + bool batt_id_avail; + bool profile_loaded; + bool battery_missing; + struct completion soc_update; + struct completion soc_ready; + struct delayed_work profile_load_work; + struct work_struct status_change_work; +}; + +/* Debugfs data structures are below */ + +/* Log buffer */ +struct fg_log_buffer { + size_t rpos; + size_t wpos; + size_t len; + char data[0]; +}; + +/* transaction parameters */ +struct fg_trans { + struct fg_chip *chip; + struct fg_log_buffer *log; + u32 cnt; + u16 addr; + u32 offset; + u8 *data; +}; + +struct fg_dbgfs { + struct debugfs_blob_wrapper help_msg; + struct fg_chip *chip; + struct dentry *root; + u32 cnt; + u32 addr; +}; + +extern int fg_sram_write(struct fg_chip *chip, u16 address, u8 offset, + u8 *val, int len, int flags); +extern int fg_sram_read(struct fg_chip *chip, u16 address, u8 offset, + u8 *val, int len, int flags); +extern int fg_sram_masked_write(struct fg_chip *chip, u16 address, u8 offset, + u8 mask, u8 val, int flags); +extern int fg_interleaved_mem_read(struct fg_chip *chip, u16 address, + u8 offset, u8 *val, int len); +extern int fg_interleaved_mem_write(struct fg_chip *chip, u16 address, + u8 offset, u8 *val, int len, bool atomic_access); +extern int fg_read(struct fg_chip *chip, int addr, u8 *val, int len); +extern int fg_write(struct fg_chip *chip, int addr, u8 *val, int len); +extern int fg_masked_write(struct fg_chip *chip, int addr, u8 mask, u8 val); +extern int fg_ima_init(struct fg_chip *chip); +extern int fg_sram_debugfs_create(struct fg_chip *chip); +extern void fill_string(char *str, size_t str_len, u8 *buf, int buf_len); +extern int64_t twos_compliment_extend(int64_t val, int s_bit_pos); +#endif diff --git a/drivers/power/qcom-charger/fg-memif.c b/drivers/power/qcom-charger/fg-memif.c new file mode 100644 index 000000000000..087223d708da --- /dev/null +++ b/drivers/power/qcom-charger/fg-memif.c @@ -0,0 +1,583 @@ +/* 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. + */ + +#define pr_fmt(fmt) "FG: %s: " fmt, __func__ + +#include "fg-core.h" +#include "fg-reg.h" + +/* Generic definitions */ +#define RETRY_COUNT 3 +#define BYTES_PER_SRAM_WORD 4 + +enum { + FG_READ = 0, + FG_WRITE, +}; + +static int fg_set_address(struct fg_chip *chip, u16 address) +{ + u8 buffer[2]; + int rc; + + buffer[0] = address & 0xFF; + /* MSB has to be written zero */ + buffer[1] = 0; + + rc = fg_write(chip, MEM_IF_ADDR_LSB(chip), buffer, 2); + if (rc < 0) { + pr_err("failed to write to 0x%04X, rc=%d\n", + MEM_IF_ADDR_LSB(chip), rc); + return rc; + } + + return rc; +} + +static int fg_config_access_mode(struct fg_chip *chip, bool access, bool burst) +{ + int rc; + u8 intf_ctl = 0; + + intf_ctl = ((access == FG_WRITE) ? IMA_WR_EN_BIT : 0) | + (burst ? MEM_ACS_BURST_BIT : 0); + + rc = fg_masked_write(chip, MEM_IF_IMA_CTL(chip), IMA_CTL_MASK, + intf_ctl); + if (rc < 0) { + pr_err("failed to write to 0x%04x, rc=%d\n", + MEM_IF_IMA_CTL(chip), rc); + return -EIO; + } + + return rc; +} + +static int fg_run_iacs_clear_sequence(struct fg_chip *chip) +{ + u8 tmp; + int rc; + + /* + * Values to write for running IACS clear sequence comes from + * hardware documentation. + */ + rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), IACS_CLR_BIT, + IACS_CLR_BIT); + if (rc < 0) { + pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_IMA_CFG(chip), + rc); + return rc; + } + + tmp = 0x4; + rc = fg_write(chip, MEM_IF_ADDR_MSB(chip), &tmp, 1); + if (rc < 0) { + pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_ADDR_LSB(chip), + rc); + return rc; + } + + tmp = 0x0; + rc = fg_write(chip, MEM_IF_WR_DATA3(chip), &tmp, 1); + if (rc < 0) { + pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_WR_DATA3(chip), + rc); + return rc; + } + + rc = fg_read(chip, MEM_IF_RD_DATA3(chip), &tmp, 1); + if (rc < 0) { + pr_err("failed to read 0x%04x, rc=%d\n", MEM_IF_RD_DATA3(chip), + rc); + return rc; + } + + rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), IACS_CLR_BIT, 0); + if (rc < 0) { + pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_IMA_CFG(chip), + rc); + return rc; + } + + fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "IACS clear sequence complete\n"); + return rc; +} + +static int fg_check_for_ima_errors(struct fg_chip *chip) +{ + int rc = 0; + u8 err_sts, exp_sts = 0, hw_sts = 0; + + rc = fg_read(chip, MEM_IF_IMA_ERR_STS(chip), &err_sts, 1); + if (rc < 0) { + pr_err("failed to read ima_err_sts rc=%d\n", rc); + return rc; + } + + if (err_sts & (ADDR_STBL_ERR_BIT | WR_ACS_ERR_BIT | RD_ACS_ERR_BIT)) { + rc = fg_read(chip, MEM_IF_IMA_EXP_STS(chip), &exp_sts, 1); + if (rc < 0) { + pr_err("failed to read ima_exp_sts rc=%d\n", rc); + return rc; + } + + rc = fg_read(chip, MEM_IF_IMA_HW_STS(chip), &hw_sts, 1); + if (rc < 0) { + pr_err("failed to read ima_hw_sts rc=%d\n", rc); + return rc; + } + + pr_err("ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n", + err_sts, exp_sts, hw_sts); + + /* clear the error */ + rc = fg_run_iacs_clear_sequence(chip); + if (rc < 0) { + pr_err("failed to run iacs clear sequence rc=%d\n", rc); + return rc; + } + + /* Retry again as there was an error in the transaction */ + return -EAGAIN; + } + + return rc; +} + +static int fg_check_iacs_ready(struct fg_chip *chip) +{ + int rc = 0, timeout = 250; + u8 ima_opr_sts = 0; + + /* + * Additional delay to make sure IACS ready bit is set after + * Read/Write operation. + */ + + usleep_range(30, 35); + while (1) { + rc = fg_read(chip, MEM_IF_IMA_OPR_STS(chip), &ima_opr_sts, 1); + if (rc < 0) { + pr_err("failed to read 0x%04x, rc=%d\n", + MEM_IF_IMA_OPR_STS(chip), rc); + return rc; + } + + if (ima_opr_sts & IACS_RDY_BIT) + break; + + if (!(--timeout)) + break; + + /* delay for iacs_ready to be asserted */ + usleep_range(5000, 7000); + } + + if (!timeout) { + pr_err("IACS_RDY not set\n"); + + rc = fg_check_for_ima_errors(chip); + if (rc < 0) { + pr_err("Failed to check for ima errors rc=%d\n", rc); + return rc; + } + + return -EBUSY; + } + + return 0; +} + +static int __fg_interleaved_mem_write(struct fg_chip *chip, u16 address, + int offset, u8 *val, int len) +{ + int rc = 0, i; + u8 *ptr = val, byte_enable = 0, num_bytes = 0; + + fg_dbg(chip, FG_SRAM_WRITE, "length %d addr=%02X offset=%d\n", len, + address, offset); + + while (len > 0) { + num_bytes = (offset + len) > BYTES_PER_SRAM_WORD ? + (BYTES_PER_SRAM_WORD - offset) : len; + + /* write to byte_enable */ + for (i = offset; i < (offset + num_bytes); i++) + byte_enable |= BIT(i); + + rc = fg_write(chip, MEM_IF_IMA_BYTE_EN(chip), &byte_enable, 1); + if (rc < 0) { + pr_err("Unable to write to byte_en_reg rc=%d\n", + rc); + return rc; + } + + /* write data */ + rc = fg_write(chip, MEM_IF_WR_DATA0(chip) + offset, ptr, + num_bytes); + if (rc < 0) { + pr_err("failed to write to 0x%04x, rc=%d\n", + MEM_IF_WR_DATA0(chip) + offset, rc); + return rc; + } + + /* + * The last-byte WR_DATA3 starts the write transaction. + * Write a dummy value to WR_DATA3 if it does not have + * valid data. This dummy data is not written to the + * SRAM as byte_en for WR_DATA3 is not set. + */ + if (!(byte_enable & BIT(3))) { + u8 dummy_byte = 0x0; + + rc = fg_write(chip, MEM_IF_WR_DATA3(chip), &dummy_byte, + 1); + if (rc < 0) { + pr_err("failed to write dummy-data to WR_DATA3 rc=%d\n", + rc); + return rc; + } + } + + /* check for error condition */ + rc = fg_check_for_ima_errors(chip); + if (rc < 0) { + pr_err("Failed to check for ima errors rc=%d\n", rc); + return rc; + } + + ptr += num_bytes; + len -= num_bytes; + offset = byte_enable = 0; + + rc = fg_check_iacs_ready(chip); + if (rc < 0) { + pr_debug("IACS_RDY failed rc=%d\n", rc); + return rc; + } + } + + return rc; +} + +static int __fg_interleaved_mem_read(struct fg_chip *chip, u16 address, + int offset, u8 *val, int len) +{ + int rc = 0, total_len; + u8 *rd_data = val, num_bytes; + char str[DEBUG_PRINT_BUFFER_SIZE]; + + fg_dbg(chip, FG_SRAM_READ, "length %d addr=%02X\n", len, address); + + total_len = len; + while (len > 0) { + num_bytes = (offset + len) > BYTES_PER_SRAM_WORD ? + (BYTES_PER_SRAM_WORD - offset) : len; + rc = fg_read(chip, MEM_IF_RD_DATA0(chip) + offset, rd_data, + num_bytes); + if (rc < 0) { + pr_err("failed to read 0x%04x, rc=%d\n", + MEM_IF_RD_DATA0(chip) + offset, rc); + return rc; + } + + rd_data += num_bytes; + len -= num_bytes; + offset = 0; + + /* check for error condition */ + rc = fg_check_for_ima_errors(chip); + if (rc < 0) { + pr_err("Failed to check for ima errors rc=%d\n", rc); + return rc; + } + + if (len && len < BYTES_PER_SRAM_WORD) { + /* + * Move to single mode. Changing address is not + * required here as it must be in burst mode. Address + * will get incremented internally by FG HW once the MSB + * of RD_DATA is read. + */ + rc = fg_config_access_mode(chip, FG_READ, 0); + if (rc < 0) { + pr_err("failed to move to single mode rc=%d\n", + rc); + return -EIO; + } + } + + rc = fg_check_iacs_ready(chip); + if (rc < 0) { + pr_debug("IACS_RDY failed rc=%d\n", rc); + return rc; + } + } + + if (*chip->debug_mask & FG_SRAM_READ) { + fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len); + pr_info("data read: %s\n", str); + } + + return rc; +} + +static int fg_get_mem_access_status(struct fg_chip *chip, bool *status) +{ + int rc; + u8 mem_if_sts; + + rc = fg_read(chip, MEM_IF_MEM_INTF_CFG(chip), &mem_if_sts, 1); + if (rc < 0) { + pr_err("failed to read rif_mem status rc=%d\n", rc); + return rc; + } + + *status = mem_if_sts & MEM_ACCESS_REQ_BIT; + return 0; +} + +static bool is_mem_access_available(struct fg_chip *chip, int access) +{ + bool rif_mem_sts = true; + int rc, time_count = 0; + + while (1) { + rc = fg_get_mem_access_status(chip, &rif_mem_sts); + if (rc < 0) + return rc; + + /* This is an inverting logic */ + if (!rif_mem_sts) + break; + + fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "MEM_ACCESS_REQ is not clear yet for IMA_%s\n", + access ? "write" : "read"); + + /* + * Try this no more than 4 times. If MEM_ACCESS_REQ is not + * clear, then return an error instead of waiting for it again. + */ + if (time_count > 4) { + pr_err("Tried 4 times(~16ms) polling MEM_ACCESS_REQ\n"); + return false; + } + + /* Wait for 4ms before reading MEM_ACCESS_REQ again */ + usleep_range(4000, 4100); + time_count++; + } + return true; +} + +static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val, + u16 address, int offset, int len, bool access) +{ + int rc = 0; + + if (!is_mem_access_available(chip, access)) + return -EBUSY; + + /* configure for IMA access */ + rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip), + MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT, + MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT); + if (rc < 0) { + pr_err("failed to set ima_req_access bit rc=%d\n", rc); + return rc; + } + + /* configure for the read/write, single/burst mode */ + rc = fg_config_access_mode(chip, access, (offset + len) > 4); + if (rc < 0) { + pr_err("failed to set memory access rc = %d\n", rc); + return rc; + } + + rc = fg_check_iacs_ready(chip); + if (rc < 0) { + pr_err_ratelimited("IACS_RDY failed rc=%d\n", rc); + return rc; + } + + rc = fg_set_address(chip, address); + if (rc < 0) { + pr_err("failed to set address rc = %d\n", rc); + return rc; + } + + if (access == FG_READ) { + rc = fg_check_iacs_ready(chip); + if (rc < 0) { + pr_debug("IACS_RDY failed rc=%d\n", rc); + return rc; + } + } + + return rc; +} + +static int fg_get_beat_count(struct fg_chip *chip, u8 *count) +{ + int rc; + + rc = fg_read(chip, MEM_IF_FG_BEAT_COUNT(chip), count, 1); + *count &= BEAT_COUNT_MASK; + return rc; +} + +int fg_interleaved_mem_read(struct fg_chip *chip, u16 address, u8 offset, + u8 *val, int len) +{ + int rc = 0; + u8 start_beat_count, end_beat_count, count = 0; + bool retry_once = false; + + if (offset > 3) { + pr_err("offset too large %d\n", offset); + return -EINVAL; + } + +retry: + rc = fg_interleaved_mem_config(chip, val, address, offset, len, + FG_READ); + if (rc < 0) { + pr_err("failed to configure SRAM for IMA rc = %d\n", rc); + goto out; + } + + /* read the start beat count */ + rc = fg_get_beat_count(chip, &start_beat_count); + if (rc < 0) { + pr_err("failed to read beat count rc=%d\n", rc); + goto out; + } + + /* read data */ + rc = __fg_interleaved_mem_read(chip, address, offset, val, len); + if (rc < 0) { + if ((rc == -EAGAIN) && (count < RETRY_COUNT)) { + count++; + pr_err("IMA access failed retry_count = %d\n", count); + goto retry; + } + pr_err("failed to read SRAM address rc = %d\n", rc); + goto out; + } + + /* read the end beat count */ + rc = fg_get_beat_count(chip, &end_beat_count); + if (rc < 0) { + pr_err("failed to read beat count rc=%d\n", rc); + goto out; + } + + fg_dbg(chip, FG_SRAM_READ, "Start beat_count = %x End beat_count = %x\n", + start_beat_count, end_beat_count); + + if (start_beat_count != end_beat_count && !retry_once) { + fg_dbg(chip, FG_SRAM_READ, "Beat count(%d/%d) do not match - retry transaction\n", + start_beat_count, end_beat_count); + retry_once = true; + } +out: + /* Release IMA access */ + rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip), + MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT, 0); + if (rc < 0) { + pr_err("failed to reset IMA access bit rc = %d\n", rc); + return rc; + } + + if (retry_once) + goto retry; + + return rc; +} + +int fg_interleaved_mem_write(struct fg_chip *chip, u16 address, u8 offset, + u8 *val, int len, bool atomic_access) +{ + int rc = 0; + u8 start_beat_count, end_beat_count, count = 0; + + if (offset > 3) { + pr_err("offset too large %d\n", offset); + return -EINVAL; + } + +retry: + rc = fg_interleaved_mem_config(chip, val, address, offset, len, + FG_WRITE); + if (rc < 0) { + pr_err("failed to configure SRAM for IMA rc = %d\n", rc); + goto out; + } + + /* read the start beat count */ + rc = fg_get_beat_count(chip, &start_beat_count); + if (rc < 0) { + pr_err("failed to read beat count rc=%d\n", rc); + goto out; + } + + /* write data */ + rc = __fg_interleaved_mem_write(chip, address, offset, val, len); + if (rc < 0) { + if ((rc == -EAGAIN) && (count < RETRY_COUNT)) { + count++; + pr_err("IMA access failed retry_count = %d\n", count); + goto retry; + } + pr_err("failed to write SRAM address rc = %d\n", rc); + goto out; + } + + /* read the end beat count */ + rc = fg_get_beat_count(chip, &end_beat_count); + if (rc < 0) { + pr_err("failed to read beat count rc=%d\n", rc); + goto out; + } + + if (atomic_access && start_beat_count != end_beat_count) + pr_err("Start beat_count = %x End beat_count = %x\n", + start_beat_count, end_beat_count); +out: + /* Release IMA access */ + rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip), + MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT, 0); + if (rc < 0) + pr_err("failed to reset IMA access bit rc = %d\n", rc); + + return rc; +} + +int fg_ima_init(struct fg_chip *chip) +{ + int rc; + + /* + * Change the FG_MEM_INT interrupt to track IACS_READY + * condition instead of end-of-transaction. This makes sure + * that the next transaction starts only after the hw is ready. + */ + rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), IACS_INTR_SRC_SLCT_BIT, + IACS_INTR_SRC_SLCT_BIT); + if (rc < 0) { + pr_err("failed to configure interrupt source %d\n", rc); + return rc; + } + + return 0; +} diff --git a/drivers/power/qcom-charger/fg-reg.h b/drivers/power/qcom-charger/fg-reg.h new file mode 100644 index 000000000000..9d5874340a8e --- /dev/null +++ b/drivers/power/qcom-charger/fg-reg.h @@ -0,0 +1,299 @@ +/* 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. + */ + +#ifndef __FG_REG_H__ +#define __FG_REG_H__ + +/* FG_BATT_SOC register definitions */ +#define BATT_SOC_FG_ALG_STS(chip) (chip->batt_soc_base + 0x06) +#define BATT_SOC_FG_ALG_AUX_STS0(chip) (chip->batt_soc_base + 0x07) +#define BATT_SOC_SLEEP_SHUTDOWN_STS(chip) (chip->batt_soc_base + 0x08) +#define BATT_SOC_FG_MONOTONIC_SOC(chip) (chip->batt_soc_base + 0x09) +#define BATT_SOC_FG_MONOTONIC_SOC_CP(chip) (chip->batt_soc_base + 0x0A) +#define BATT_SOC_INT_RT_STS(chip) (chip->batt_soc_base + 0x10) +#define BATT_SOC_EN_CTL(chip) (chip->batt_soc_base + 0x46) +#define BATT_SOC_RESTART(chip) (chip->batt_soc_base + 0x48) +#define BATT_SOC_STS_CLR(chip) (chip->batt_soc_base + 0x4A) +#define BATT_SOC_LOW_PWR_CFG(chip) (chip->batt_soc_base + 0x52) +#define BATT_SOC_LOW_PWR_STS(chip) (chip->batt_soc_base + 0x56) + +/* BATT_SOC_EN_CTL */ +#define FG_ALGORITHM_EN_BIT BIT(7) + +/* BATT_SOC_RESTART */ +#define RESTART_GO_BIT BIT(0) + +/* FG_BATT_INFO register definitions */ +#define BATT_INFO_BATT_TEMP_STS(chip) (chip->batt_info_base + 0x06) +#define BATT_INFO_SYS_BATT(chip) (chip->batt_info_base + 0x07) +#define BATT_INFO_FG_STS(chip) (chip->batt_info_base + 0x09) +#define BATT_INFO_INT_RT_STS(chip) (chip->batt_info_base + 0x10) +#define BATT_INFO_BATT_REM_LATCH(chip) (chip->batt_info_base + 0x4F) +#define BATT_INFO_BATT_TEMP_LSB(chip) (chip->batt_info_base + 0x50) +#define BATT_INFO_BATT_TEMP_MSB(chip) (chip->batt_info_base + 0x51) +#define BATT_INFO_BATT_TEMP_CFG(chip) (chip->batt_info_base + 0x56) +#define BATT_INFO_BATT_TMPR_INTR(chip) (chip->batt_info_base + 0x59) +#define BATT_INFO_THERM_C1(chip) (chip->batt_info_base + 0x5C) +#define BATT_INFO_THERM_C2(chip) (chip->batt_info_base + 0x5D) +#define BATT_INFO_THERM_C3(chip) (chip->batt_info_base + 0x5E) +#define BATT_INFO_THERM_HALF_RANGE(chip) (chip->batt_info_base + 0x5F) +#define BATT_INFO_JEITA_CTLS(chip) (chip->batt_info_base + 0x61) +#define BATT_INFO_JEITA_TOO_COLD(chip) (chip->batt_info_base + 0x62) +#define BATT_INFO_JEITA_COLD(chip) (chip->batt_info_base + 0x63) +#define BATT_INFO_JEITA_HOT(chip) (chip->batt_info_base + 0x64) +#define BATT_INFO_JEITA_TOO_HOT(chip) (chip->batt_info_base + 0x65) + +/* only for v1.1 */ +#define BATT_INFO_ESR_CFG(chip) (chip->batt_info_base + 0x69) +/* starting from v2.0 */ +#define BATT_INFO_ESR_GENERAL_CFG(chip) (chip->batt_info_base + 0x68) +#define BATT_INFO_ESR_PULL_DN_CFG(chip) (chip->batt_info_base + 0x69) +#define BATT_INFO_ESR_FAST_CRG_CFG(chip) (chip->batt_info_base + 0x6A) + +#define BATT_INFO_BATT_MISS_CFG(chip) (chip->batt_info_base + 0x6B) +#define BATT_INFO_WATCHDOG_COUNT(chip) (chip->batt_info_base + 0x70) +#define BATT_INFO_WATCHDOG_CFG(chip) (chip->batt_info_base + 0x71) +#define BATT_INFO_IBATT_SENSING_CFG(chip) (chip->batt_info_base + 0x73) +#define BATT_INFO_QNOVO_CFG(chip) (chip->batt_info_base + 0x74) +#define BATT_INFO_QNOVO_SCALER(chip) (chip->batt_info_base + 0x75) + +/* starting from v2.0 */ +#define BATT_INFO_CRG_SERVICES(chip) (chip->batt_info_base + 0x90) + +/* Following LSB/MSB address are for v2.0 and above; v1.1 have them swapped */ +#define BATT_INFO_VBATT_LSB(chip) (chip->batt_info_base + 0xA0) +#define BATT_INFO_VBATT_MSB(chip) (chip->batt_info_base + 0xA1) +#define BATT_INFO_IBATT_LSB(chip) (chip->batt_info_base + 0xA2) +#define BATT_INFO_IBATT_MSB(chip) (chip->batt_info_base + 0xA3) +#define BATT_INFO_ESR_LSB(chip) (chip->batt_info_base + 0xA4) +#define BATT_INFO_ESR_MSB(chip) (chip->batt_info_base + 0xA5) +#define BATT_INFO_VBATT_LSB_CP(chip) (chip->batt_info_base + 0xA6) +#define BATT_INFO_VBATT_MSB_CP(chip) (chip->batt_info_base + 0xA7) +#define BATT_INFO_IBATT_LSB_CP(chip) (chip->batt_info_base + 0xA8) +#define BATT_INFO_IBATT_MSB_CP(chip) (chip->batt_info_base + 0xA9) +#define BATT_INFO_ESR_LSB_CP(chip) (chip->batt_info_base + 0xAA) +#define BATT_INFO_ESR_MSB_CP(chip) (chip->batt_info_base + 0xAB) +#define BATT_INFO_VADC_LSB(chip) (chip->batt_info_base + 0xAC) +#define BATT_INFO_VADC_MSB(chip) (chip->batt_info_base + 0xAD) +#define BATT_INFO_IADC_LSB(chip) (chip->batt_info_base + 0xAE) +#define BATT_INFO_IADC_MSB(chip) (chip->batt_info_base + 0xAF) +#define BATT_INFO_TM_MISC(chip) (chip->batt_info_base + 0xE5) +#define BATT_INFO_TM_MISC1(chip) (chip->batt_info_base + 0xE6) + +/* BATT_INFO_BATT_TEMP_STS */ +#define JEITA_TOO_HOT_STS_BIT BIT(7) +#define JEITA_HOT_STS_BIT BIT(6) +#define JEITA_COLD_STS_BIT BIT(5) +#define JEITA_TOO_COLD_STS_BIT BIT(4) +#define BATT_TEMP_DELTA_BIT BIT(1) +#define BATT_TEMP_AVAIL_BIT BIT(0) + +/* BATT_INFO_SYS_BATT */ +#define BATT_REM_LATCH_STS_BIT BIT(4) +#define BATT_MISSING_HW_BIT BIT(2) +#define BATT_MISSING_ALG_BIT BIT(1) +#define BATT_MISSING_CMP_BIT BIT(0) + +/* BATT_INFO_FG_STS */ +#define FG_WD_RESET_BIT BIT(7) +/* This bit is not present in v1.1 */ +#define FG_CRG_TRM_BIT BIT(0) + +/* BATT_INFO_INT_RT_STS */ +#define BT_TMPR_DELTA_BIT BIT(6) +#define WDOG_EXP_BIT BIT(5) +#define BT_ATTN_BIT BIT(4) +#define BT_MISS_BIT BIT(3) +#define ESR_DELTA_BIT BIT(2) +#define VBT_LOW_BIT BIT(1) +#define VBT_PRD_DELTA_BIT BIT(0) + +/* BATT_INFO_INT_RT_STS */ +#define BATT_REM_LATCH_CLR_BIT BIT(7) + +/* BATT_INFO_BATT_TEMP_LSB/MSB */ +#define BATT_TEMP_LSB_MASK GENMASK(7, 0) +#define BATT_TEMP_MSB_MASK GENMASK(2, 0) + +/* BATT_INFO_BATT_TEMP_CFG */ +#define JEITA_TEMP_HYST_MASK GENMASK(5, 4) +#define JEITA_TEMP_NO_HYST 0x0 +#define JEITA_TEMP_HYST_1C 0x1 +#define JEITA_TEMP_HYST_2C 0x2 +#define JEITA_TEMP_HYST_3C 0x3 + +/* BATT_INFO_BATT_TMPR_INTR */ +#define CHANGE_THOLD_MASK GENMASK(1, 0) +#define BTEMP_DELTA_2K 0x0 +#define BTEMP_DELTA_4K 0x1 +#define BTEMP_DELTA_6K 0x2 +#define BTEMP_DELTA_10K 0x3 + +/* BATT_INFO_THERM_C1/C2/C3 */ +#define BATT_INFO_THERM_COEFF_MASK GENMASK(7, 0) + +/* BATT_INFO_THERM_HALF_RANGE */ +#define BATT_INFO_THERM_TEMP_MASK GENMASK(7, 0) + +/* BATT_INFO_JEITA_CTLS */ +#define JEITA_STS_CLEAR_BIT BIT(0) + +/* BATT_INFO_JEITA_TOO_COLD/COLD/HOT/TOO_HOT */ +#define JEITA_THOLD_MASK GENMASK(7, 0) + +/* BATT_INFO_ESR_CFG */ +#define CFG_ACTIVE_PD_MASK GENMASK(2, 1) +#define CFG_FCC_DEC_MASK GENMASK(4, 3) + +/* BATT_INFO_ESR_GENERAL_CFG */ +#define ESR_DEEP_TAPER_EN_BIT BIT(0) + +/* BATT_INFO_ESR_PULL_DN_CFG */ +#define ESR_PULL_DOWN_IVAL_MASK GENMASK(3, 2) +#define ESR_MEAS_CUR_60MA 0x0 +#define ESR_MEAS_CUR_120MA 0x1 +#define ESR_MEAS_CUR_180MA 0x2 +#define ESR_MEAS_CUR_240MA 0x3 +#define ESR_PULL_DOWN_MODE_MASK GENMASK(1, 0) +#define ESR_NO_PULL_DOWN 0x0 +#define ESR_STATIC_PULL_DOWN 0x1 +#define ESR_CRG_DSC_PULL_DOWN 0x2 +#define ESR_DSC_PULL_DOWN 0x3 + +/* BATT_INFO_ESR_FAST_CRG_CFG */ +#define ESR_FAST_CRG_IVAL_MASK GENMASK(3, 1) +#define ESR_FCC_300MA 0x0 +#define ESR_FCC_600MA 0x1 +#define ESR_FCC_1A 0x2 +#define ESR_FCC_2A 0x3 +#define ESR_FCC_3A 0x4 +#define ESR_FCC_4A 0x5 +#define ESR_FCC_5A 0x6 +#define ESR_FCC_6A 0x7 +#define ESR_FAST_CRG_CTL_EN_BIT BIT(0) + +/* BATT_INFO_BATT_MISS_CFG */ +#define BM_THERM_TH_MASK GENMASK(5, 4) +#define RES_TH_0P75_MOHM 0x0 +#define RES_TH_1P00_MOHM 0x1 +#define RES_TH_1P50_MOHM 0x2 +#define RES_TH_3P00_MOHM 0x3 +#define BM_BATT_ID_TH_MASK GENMASK(3, 2) +#define BM_FROM_THERM_BIT BIT(1) +#define BM_FROM_BATT_ID_BIT BIT(0) + +/* BATT_INFO_WATCHDOG_COUNT */ +#define WATCHDOG_COUNTER GENMASK(7, 0) + +/* BATT_INFO_WATCHDOG_CFG */ +#define RESET_CAPABLE_BIT BIT(2) +#define PET_CTRL_BIT BIT(1) +#define ENABLE_CTRL_BIT BIT(0) + +/* BATT_INFO_IBATT_SENSING_CFG */ +#define ADC_BITSTREAM_INV_BIT BIT(4) +#define SOURCE_SELECT_MASK GENMASK(1, 0) +#define SRC_SEL_BATFET 0x0 +#define SRC_SEL_RSENSE 0x1 +#define SRC_SEL_BATFET_SMB 0x2 +#define SRC_SEL_RESERVED 0x3 + +/* BATT_INFO_QNOVO_CFG */ +#define LD_REG_FORCE_CTL_BIT BIT(2) +#define LD_REG_CTRL_FORCE_HIGH LD_REG_FORCE_CTL_BIT +#define LD_REG_CTRL_FORCE_LOW 0 +#define LD_REG_CTRL_BIT BIT(1) +#define LD_REG_CTRL_REGISTER LD_REG_CTRL_BIT +#define LD_REG_CTRL_LOGIC 0 +#define BIT_STREAM_CFG_BIT BIT(0) + +/* BATT_INFO_QNOVO_SCALER */ +#define QNOVO_SCALER_MASK GENMASK(7, 0) + +/* BATT_INFO_CRG_SERVICES */ +#define FG_CRC_TRM_EN_BIT BIT(0) + +/* BATT_INFO_VBATT_LSB/MSB */ +#define VBATT_MASK GENMASK(7, 0) + +/* BATT_INFO_IBATT_LSB/MSB */ +#define IBATT_MASK GENMASK(7, 0) + +/* BATT_INFO_ESR_LSB/MSB */ +#define ESR_LSB_MASK GENMASK(7, 0) +#define ESR_MSB_MASK GENMASK(5, 0) + +/* BATT_INFO_VADC_LSB/MSB */ +#define VADC_LSB_MASK GENMASK(7, 0) +#define VADC_MSB_MASK GENMASK(6, 0) + +/* BATT_INFO_IADC_LSB/MSB */ +#define IADC_LSB_MASK GENMASK(7, 0) +#define IADC_MSB_MASK GENMASK(6, 0) + +/* BATT_INFO_TM_MISC */ +#define FORCE_SEQ_RESP_TOGGLE_BIT BIT(6) +#define ALG_DIRECT_VALID_DATA_BIT BIT(5) +#define ALG_DIRECT_MODE_EN_BIT BIT(4) +#define BATT_VADC_CONV_BIT BIT(3) +#define BATT_IADC_CONV_BIT BIT(2) +#define ADC_ENABLE_REG_CTRL_BIT BIT(1) +#define WDOG_FORCE_EXP_BIT BIT(0) +/* only for v1.1 */ +#define ESR_PULSE_FORCE_CTRL_BIT BIT(7) + +/* BATT_INFO_TM_MISC1 */ +/* for v2.0 and above */ +#define ESR_REQ_CTL_BIT BIT(1) +#define ESR_REQ_CTL_EN_BIT BIT(0) + +/* FG_MEM_IF register and bit definitions */ +#define MEM_IF_MEM_INTF_CFG(chip) ((chip->mem_if_base) + 0x50) +#define MEM_IF_IMA_CTL(chip) ((chip->mem_if_base) + 0x51) +#define MEM_IF_IMA_CFG(chip) ((chip->mem_if_base) + 0x52) +#define MEM_IF_IMA_OPR_STS(chip) ((chip->mem_if_base) + 0x54) +#define MEM_IF_IMA_EXP_STS(chip) ((chip->mem_if_base) + 0x55) +#define MEM_IF_IMA_HW_STS(chip) ((chip->mem_if_base) + 0x56) +#define MEM_IF_FG_BEAT_COUNT(chip) ((chip->mem_if_base) + 0x57) +#define MEM_IF_IMA_ERR_STS(chip) ((chip->mem_if_base) + 0x5F) +#define MEM_IF_IMA_BYTE_EN(chip) ((chip->mem_if_base) + 0x60) +#define MEM_IF_ADDR_LSB(chip) ((chip->mem_if_base) + 0x61) +#define MEM_IF_ADDR_MSB(chip) ((chip->mem_if_base) + 0x62) +#define MEM_IF_WR_DATA0(chip) ((chip->mem_if_base) + 0x63) +#define MEM_IF_WR_DATA3(chip) ((chip->mem_if_base) + 0x66) +#define MEM_IF_RD_DATA0(chip) ((chip->mem_if_base) + 0x67) +#define MEM_IF_RD_DATA3(chip) ((chip->mem_if_base) + 0x6A) + +/* MEM_IF_MEM_INTF_CFG */ +#define MEM_ACCESS_REQ_BIT BIT(7) +#define IACS_SLCT_BIT BIT(5) + +/* MEM_IF_IMA_CTL */ +#define MEM_ACS_BURST_BIT BIT(7) +#define IMA_WR_EN_BIT BIT(6) +#define IMA_CTL_MASK GENMASK(7, 6) + +/* MEM_IF_IMA_CFG */ +#define IACS_CLR_BIT BIT(2) +#define IACS_INTR_SRC_SLCT_BIT BIT(3) + +/* MEM_IF_IMA_OPR_STS */ +#define IACS_RDY_BIT BIT(1) + +/* MEM_IF_IMA_ERR_STS */ +#define ADDR_STBL_ERR_BIT BIT(7) +#define WR_ACS_ERR_BIT BIT(6) +#define RD_ACS_ERR_BIT BIT(5) + +/* MEM_IF_FG_BEAT_COUNT */ +#define BEAT_COUNT_MASK GENMASK(3, 0) +#endif diff --git a/drivers/power/qcom-charger/fg-util.c b/drivers/power/qcom-charger/fg-util.c new file mode 100644 index 000000000000..9f2d9973e04b --- /dev/null +++ b/drivers/power/qcom-charger/fg-util.c @@ -0,0 +1,644 @@ +/* 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. + */ + +#define pr_fmt(fmt) "FG: %s: " fmt, __func__ + +#include "fg-core.h" + +static struct fg_dbgfs dbgfs_data = { + .help_msg = { + .data = + "FG Debug-FS support\n" + "\n" + "Hierarchy schema:\n" + "/sys/kernel/debug/fg_sram\n" + " /help -- Static help text\n" + " /address -- Starting register address for reads or writes\n" + " /count -- Number of registers to read (only used for reads)\n" + " /data -- Initiates the SRAM read (formatted output)\n" + "\n", + }, +}; + +void fill_string(char *str, size_t str_len, u8 *buf, int buf_len) +{ + int pos = 0; + int i; + + for (i = 0; i < buf_len; i++) { + pos += scnprintf(str + pos, str_len - pos, "%02x", buf[i]); + if (i < buf_len - 1) + pos += scnprintf(str + pos, str_len - pos, " "); + } +} + +static inline bool fg_sram_address_valid(u16 address, int len) +{ + if (address > FG_SRAM_ADDRESS_MAX) + return false; + + if ((address + DIV_ROUND_UP(len, 4)) > FG_SRAM_ADDRESS_MAX + 1) + return false; + + return true; +} + +#define SOC_UPDATE_WAIT_MS 1500 +int fg_sram_write(struct fg_chip *chip, u16 address, u8 offset, + u8 *val, int len, int flags) +{ + int rc = 0; + bool tried_again = false; + bool atomic_access = false; + + if (!chip) + return -ENXIO; + + if (!fg_sram_address_valid(address, len)) + return -EFAULT; + + vote(chip->awake_votable, SRAM_WRITE, true, 0); + mutex_lock(&chip->sram_rw_lock); + + if ((flags & FG_IMA_ATOMIC) && chip->irqs[SOC_UPDATE_IRQ].irq) { + /* + * This interrupt need to be enabled only when it is + * required. It will be kept disabled other times. + */ + enable_irq(chip->irqs[SOC_UPDATE_IRQ].irq); + atomic_access = true; + } else { + flags = FG_IMA_DEFAULT; + } +wait: + /* + * Atomic access mean waiting upon SOC_UPDATE interrupt from + * FG_ALG and do the transaction after that. This is to make + * sure that there will be no SOC update happening when an + * IMA write is happening. SOC_UPDATE interrupt fires every + * FG cycle (~1.47 seconds). + */ + if (atomic_access) { + /* Wait for SOC_UPDATE completion */ + rc = wait_for_completion_interruptible_timeout( + &chip->soc_update, + msecs_to_jiffies(SOC_UPDATE_WAIT_MS)); + + /* If we were interrupted wait again one more time. */ + if (rc == -ERESTARTSYS && !tried_again) { + tried_again = true; + goto wait; + } else if (rc <= 0) { + pr_err("wait for soc_update timed out rc=%d\n", rc); + goto out; + } + } + + rc = fg_interleaved_mem_write(chip, address, offset, val, len, + atomic_access); + if (rc < 0) + pr_err("Error in writing SRAM address 0x%x[%d], rc=%d\n", + address, offset, rc); +out: + if (atomic_access) + disable_irq_nosync(chip->irqs[SOC_UPDATE_IRQ].irq); + + mutex_unlock(&chip->sram_rw_lock); + vote(chip->awake_votable, SRAM_WRITE, false, 0); + return rc; +} + +int fg_sram_read(struct fg_chip *chip, u16 address, u8 offset, + u8 *val, int len, int flags) +{ + int rc = 0; + + if (!chip) + return -ENXIO; + + if (!fg_sram_address_valid(address, len)) + return -EFAULT; + + vote(chip->awake_votable, SRAM_READ, true, 0); + mutex_lock(&chip->sram_rw_lock); + + rc = fg_interleaved_mem_read(chip, address, offset, val, len); + if (rc < 0) + pr_err("Error in reading SRAM address 0x%x[%d], rc=%d\n", + address, offset, rc); + + mutex_unlock(&chip->sram_rw_lock); + vote(chip->awake_votable, SRAM_READ, false, 0); + return rc; +} + +int fg_sram_masked_write(struct fg_chip *chip, u16 address, u8 offset, + u8 mask, u8 val, int flags) +{ + int rc = 0; + u8 buf[4]; + + rc = fg_sram_read(chip, address, 0, buf, 4, flags); + if (rc < 0) { + pr_err("sram read failed: address=%03X, rc=%d\n", address, rc); + return rc; + } + + buf[offset] &= ~mask; + buf[offset] |= val & mask; + + rc = fg_sram_write(chip, address, 0, buf, 4, flags); + if (rc < 0) { + pr_err("sram write failed: address=%03X, rc=%d\n", address, rc); + return rc; + } + + return rc; +} + +int fg_read(struct fg_chip *chip, int addr, u8 *val, int len) +{ + int rc, i; + + if (!chip || !chip->regmap) + return -ENXIO; + + rc = regmap_bulk_read(chip->regmap, addr, val, len); + + if (rc < 0) { + dev_err(chip->dev, "regmap_read failed for address %04x rc=%d\n", + addr, rc); + return rc; + } + + if (*chip->debug_mask & FG_BUS_READ) { + pr_info("length %d addr=%04x\n", len, addr); + for (i = 0; i < len; i++) + pr_info("val[%d]: %02x\n", i, val[i]); + } + + return 0; +} + +int fg_write(struct fg_chip *chip, int addr, u8 *val, int len) +{ + int rc, i; + bool sec_access = false; + + if (!chip || !chip->regmap) + return -ENXIO; + + mutex_lock(&chip->bus_lock); + sec_access = (addr & 0xFF00) > 0xD0; + if (sec_access) { + rc = regmap_write(chip->regmap, (addr & 0xFF00) | 0xD0, 0xA5); + if (rc < 0) { + dev_err(chip->dev, "regmap_write failed for address %x rc=%d\n", + addr, rc); + goto out; + } + } + + if (len > 1) + rc = regmap_bulk_write(chip->regmap, addr, val, len); + else + rc = regmap_write(chip->regmap, addr, *val); + + if (rc < 0) { + dev_err(chip->dev, "regmap_write failed for address %04x rc=%d\n", + addr, rc); + goto out; + } + + if (*chip->debug_mask & FG_BUS_WRITE) { + pr_info("length %d addr=%04x\n", len, addr); + for (i = 0; i < len; i++) + pr_info("val[%d]: %02x\n", i, val[i]); + } +out: + mutex_unlock(&chip->bus_lock); + return rc; +} + +int fg_masked_write(struct fg_chip *chip, int addr, u8 mask, u8 val) +{ + int rc; + bool sec_access = false; + + if (!chip || !chip->regmap) + return -ENXIO; + + mutex_lock(&chip->bus_lock); + sec_access = (addr & 0xFF00) > 0xD0; + if (sec_access) { + rc = regmap_write(chip->regmap, (addr & 0xFF00) | 0xD0, 0xA5); + if (rc < 0) { + dev_err(chip->dev, "regmap_write failed for address %x rc=%d\n", + addr, rc); + goto out; + } + } + + rc = regmap_update_bits(chip->regmap, addr, mask, val); + if (rc < 0) { + dev_err(chip->dev, "regmap_update_bits failed for address %04x rc=%d\n", + addr, rc); + goto out; + } + + fg_dbg(chip, FG_BUS_WRITE, "addr=%04x mask: %02x val: %02x\n", addr, + mask, val); +out: + mutex_unlock(&chip->bus_lock); + return rc; +} + +int64_t twos_compliment_extend(int64_t val, int sign_bit_pos) +{ + int i, nbytes = DIV_ROUND_UP(sign_bit_pos, 8); + int64_t mask, val_out; + + val_out = val; + mask = 1 << sign_bit_pos; + if (val & mask) { + for (i = 8; i > nbytes; i--) { + mask = 0xFFLL << ((i - 1) * 8); + val_out |= mask; + } + + if ((nbytes * 8) - 1 > sign_bit_pos) { + mask = 1 << sign_bit_pos; + for (i = 1; i <= (nbytes * 8) - sign_bit_pos; i++) + val_out |= mask << i; + } + } + + pr_debug("nbytes: %d val: %llx val_out: %llx\n", nbytes, val, val_out); + return val_out; +} + +/* All the debugfs related functions are defined below */ +static int fg_sram_dfs_open(struct inode *inode, struct file *file) +{ + struct fg_log_buffer *log; + struct fg_trans *trans; + u8 *data_buf; + + size_t logbufsize = SZ_4K; + size_t databufsize = SZ_4K; + + if (!dbgfs_data.chip) { + pr_err("Not initialized data\n"); + return -EINVAL; + } + + /* Per file "transaction" data */ + trans = devm_kzalloc(dbgfs_data.chip->dev, sizeof(*trans), GFP_KERNEL); + if (!trans) + return -ENOMEM; + + /* Allocate log buffer */ + log = devm_kzalloc(dbgfs_data.chip->dev, logbufsize, GFP_KERNEL); + if (!log) + return -ENOMEM; + + log->rpos = 0; + log->wpos = 0; + log->len = logbufsize - sizeof(*log); + + /* Allocate data buffer */ + data_buf = devm_kzalloc(dbgfs_data.chip->dev, databufsize, GFP_KERNEL); + if (!data_buf) + return -ENOMEM; + + trans->log = log; + trans->data = data_buf; + trans->cnt = dbgfs_data.cnt; + trans->addr = dbgfs_data.addr; + trans->chip = dbgfs_data.chip; + trans->offset = trans->addr; + + file->private_data = trans; + return 0; +} + +static int fg_sram_dfs_close(struct inode *inode, struct file *file) +{ + struct fg_trans *trans = file->private_data; + + if (trans && trans->log && trans->data) { + file->private_data = NULL; + devm_kfree(trans->chip->dev, trans->log); + devm_kfree(trans->chip->dev, trans->data); + devm_kfree(trans->chip->dev, trans); + } + + return 0; +} + +/** + * print_to_log: format a string and place into the log buffer + * @log: The log buffer to place the result into. + * @fmt: The format string to use. + * @...: The arguments for the format string. + * + * The return value is the number of characters written to @log buffer + * not including the trailing '\0'. + */ +static int print_to_log(struct fg_log_buffer *log, const char *fmt, ...) +{ + va_list args; + int cnt; + char *buf = &log->data[log->wpos]; + size_t size = log->len - log->wpos; + + va_start(args, fmt); + cnt = vscnprintf(buf, size, fmt, args); + va_end(args); + + log->wpos += cnt; + return cnt; +} + +/** + * write_next_line_to_log: Writes a single "line" of data into the log buffer + * @trans: Pointer to SRAM transaction data. + * @offset: SRAM address offset to start reading from. + * @pcnt: Pointer to 'cnt' variable. Indicates the number of bytes to read. + * + * The 'offset' is a 12-bit SRAM address. + * + * On a successful read, the pcnt is decremented by the number of data + * bytes read from the SRAM. When the cnt reaches 0, all requested bytes have + * been read. + */ +static int write_next_line_to_log(struct fg_trans *trans, int offset, + size_t *pcnt) +{ + int i; + u8 data[ITEMS_PER_LINE]; + u16 address; + struct fg_log_buffer *log = trans->log; + int cnt = 0; + int items_to_read = min(ARRAY_SIZE(data), *pcnt); + int items_to_log = min(ITEMS_PER_LINE, items_to_read); + + /* Buffer needs enough space for an entire line */ + if ((log->len - log->wpos) < MAX_LINE_LENGTH) + goto done; + + memcpy(data, trans->data + (offset - trans->addr), items_to_read); + *pcnt -= items_to_read; + + /* address is in word now and it increments by 1. */ + address = trans->addr + ((offset - trans->addr) / ITEMS_PER_LINE); + cnt = print_to_log(log, "%3.3d ", address & 0xfff); + if (cnt == 0) + goto done; + + /* Log the data items */ + for (i = 0; i < items_to_log; ++i) { + cnt = print_to_log(log, "%2.2X ", data[i]); + if (cnt == 0) + goto done; + } + + /* If the last character was a space, then replace it with a newline */ + if (log->wpos > 0 && log->data[log->wpos - 1] == ' ') + log->data[log->wpos - 1] = '\n'; + +done: + return cnt; +} + +/** + * get_log_data - reads data from SRAM and saves to the log buffer + * @trans: Pointer to SRAM transaction data. + * + * Returns the number of "items" read or SPMI error code for read failures. + */ +static int get_log_data(struct fg_trans *trans) +{ + int cnt, rc; + int last_cnt; + int items_read; + int total_items_read = 0; + u32 offset = trans->offset; + size_t item_cnt = trans->cnt; + struct fg_log_buffer *log = trans->log; + + if (item_cnt == 0) + return 0; + + if (item_cnt > SZ_4K) { + pr_err("Reading too many bytes\n"); + return -EINVAL; + } + + pr_debug("addr: %d offset: %d count: %d\n", trans->addr, trans->offset, + trans->cnt); + rc = fg_sram_read(trans->chip, trans->addr, 0, + trans->data, trans->cnt, 0); + if (rc < 0) { + pr_err("SRAM read failed: rc = %d\n", rc); + return rc; + } + /* Reset the log buffer 'pointers' */ + log->wpos = log->rpos = 0; + + /* Keep reading data until the log is full */ + do { + last_cnt = item_cnt; + cnt = write_next_line_to_log(trans, offset, &item_cnt); + items_read = last_cnt - item_cnt; + offset += items_read; + total_items_read += items_read; + } while (cnt && item_cnt > 0); + + /* Adjust the transaction offset and count */ + trans->cnt = item_cnt; + trans->offset += total_items_read; + + return total_items_read; +} + +/** + * fg_sram_dfs_reg_read: reads value(s) from SRAM and fills user's buffer a + * byte array (coded as string) + * @file: file pointer + * @buf: where to put the result + * @count: maximum space available in @buf + * @ppos: starting position + * @return number of user bytes read, or negative error value + */ +static ssize_t fg_sram_dfs_reg_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct fg_trans *trans = file->private_data; + struct fg_log_buffer *log = trans->log; + size_t ret; + size_t len; + + /* Is the the log buffer empty */ + if (log->rpos >= log->wpos) { + if (get_log_data(trans) <= 0) + return 0; + } + + len = min(count, log->wpos - log->rpos); + + ret = copy_to_user(buf, &log->data[log->rpos], len); + if (ret == len) { + pr_err("error copy sram register values to user\n"); + return -EFAULT; + } + + /* 'ret' is the number of bytes not copied */ + len -= ret; + + *ppos += len; + log->rpos += len; + return len; +} + +/** + * fg_sram_dfs_reg_write: write user's byte array (coded as string) to SRAM. + * @file: file pointer + * @buf: user data to be written. + * @count: maximum space available in @buf + * @ppos: starting position + * @return number of user byte written, or negative error value + */ +static ssize_t fg_sram_dfs_reg_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int bytes_read; + int data; + int pos = 0; + int cnt = 0; + u8 *values; + char *kbuf; + size_t ret = 0; + struct fg_trans *trans = file->private_data; + u32 address = trans->addr; + + /* Make a copy of the user data */ + kbuf = kmalloc(count + 1, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + ret = copy_from_user(kbuf, buf, count); + if (ret == count) { + pr_err("failed to copy data from user\n"); + ret = -EFAULT; + goto free_buf; + } + + count -= ret; + *ppos += count; + kbuf[count] = '\0'; + + /* Override the text buffer with the raw data */ + values = kbuf; + + /* Parse the data in the buffer. It should be a string of numbers */ + while ((pos < count) && + sscanf(kbuf + pos, "%i%n", &data, &bytes_read) == 1) { + pos += bytes_read; + values[cnt++] = data & 0xff; + } + + if (!cnt) + goto free_buf; + + pr_debug("address %d, count %d\n", address, cnt); + /* Perform the write(s) */ + + ret = fg_sram_write(trans->chip, address, 0, values, cnt, 0); + if (ret) { + pr_err("SRAM write failed, err = %zu\n", ret); + } else { + ret = count; + trans->offset += cnt > 4 ? 4 : cnt; + } + +free_buf: + kfree(kbuf); + return ret; +} + +static const struct file_operations fg_sram_dfs_reg_fops = { + .open = fg_sram_dfs_open, + .release = fg_sram_dfs_close, + .read = fg_sram_dfs_reg_read, + .write = fg_sram_dfs_reg_write, +}; + +/* + * fg_debugfs_create: adds new fg_sram debugfs entry + * @return zero on success + */ +int fg_sram_debugfs_create(struct fg_chip *chip) +{ + struct dentry *root; + struct dentry *file; + mode_t dfs_mode = S_IRUSR | S_IWUSR; + + pr_debug("Creating FG_SRAM debugfs file-system\n"); + root = debugfs_create_dir("fg_sram", NULL); + if (IS_ERR_OR_NULL(root)) { + pr_err("Error creating top level directory err:%ld", + (long)root); + if (PTR_ERR(root) == -ENODEV) + pr_err("debugfs is not enabled in the kernel"); + return -ENODEV; + } + + if (!root) + return -ENOENT; + + dbgfs_data.help_msg.size = strlen(dbgfs_data.help_msg.data); + file = debugfs_create_blob("help", S_IRUGO, root, &dbgfs_data.help_msg); + if (!file) { + pr_err("error creating help entry\n"); + goto err_remove_fs; + } + + dbgfs_data.chip = chip; + + file = debugfs_create_u32("count", dfs_mode, root, &(dbgfs_data.cnt)); + if (!file) { + pr_err("error creating 'count' entry\n"); + goto err_remove_fs; + } + + file = debugfs_create_x32("address", dfs_mode, + root, &(dbgfs_data.addr)); + if (!file) { + pr_err("error creating 'address' entry\n"); + goto err_remove_fs; + } + + file = debugfs_create_file("data", dfs_mode, root, &dbgfs_data, + &fg_sram_dfs_reg_fops); + if (!file) { + pr_err("error creating 'data' entry\n"); + goto err_remove_fs; + } + + chip->dentry = root; + return 0; + +err_remove_fs: + debugfs_remove_recursive(root); + return -ENOMEM; +} diff --git a/drivers/power/qcom-charger/qpnp-fg-gen3.c b/drivers/power/qcom-charger/qpnp-fg-gen3.c new file mode 100644 index 000000000000..2adc07ddc5a0 --- /dev/null +++ b/drivers/power/qcom-charger/qpnp-fg-gen3.c @@ -0,0 +1,1495 @@ +/* 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. + */ + +#define pr_fmt(fmt) "FG: %s: " fmt, __func__ + +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/of_batterydata.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/iio/consumer.h> +#include <linux/qpnp/qpnp-revid.h> +#include "fg-core.h" +#include "fg-reg.h" + +#define FG_GEN3_DEV_NAME "qcom,fg-gen3" + +#define PERPH_SUBTYPE_REG 0x05 +#define FG_BATT_SOC_PMICOBALT 0x10 +#define FG_BATT_INFO_PMICOBALT 0x11 +#define FG_MEM_INFO_PMICOBALT 0x0D + +/* SRAM address and offset in ascending order */ +#define CUTOFF_VOLT_WORD 5 +#define CUTOFF_VOLT_OFFSET 0 +#define SYS_TERM_CURR_WORD 6 +#define SYS_TERM_CURR_OFFSET 0 +#define DELTA_SOC_THR_WORD 12 +#define DELTA_SOC_THR_OFFSET 3 +#define RECHARGE_SOC_THR_WORD 14 +#define RECHARGE_SOC_THR_OFFSET 0 +#define CHG_TERM_CURR_WORD 14 +#define CHG_TERM_CURR_OFFSET 1 +#define EMPTY_VOLT_WORD 15 +#define EMPTY_VOLT_OFFSET 0 +#define VBATT_LOW_WORD 15 +#define VBATT_LOW_OFFSET 1 +#define PROFILE_LOAD_WORD 24 +#define PROFILE_LOAD_OFFSET 0 +#define NOM_CAP_WORD 58 +#define NOM_CAP_OFFSET 0 +#define PROFILE_INTEGRITY_WORD 79 +#define PROFILE_INTEGRITY_OFFSET 3 +#define BATT_SOC_WORD 91 +#define BATT_SOC_OFFSET 0 +#define MONOTONIC_SOC_WORD 94 +#define MONOTONIC_SOC_OFFSET 2 +#define VOLTAGE_PRED_WORD 97 +#define VOLTAGE_PRED_OFFSET 0 +#define OCV_WORD 97 +#define OCV_OFFSET 2 +#define RSLOW_WORD 101 +#define RSLOW_OFFSET 0 +#define LAST_BATT_SOC_WORD 119 +#define LAST_BATT_SOC_OFFSET 0 +#define LAST_MONOTONIC_SOC_WORD 119 +#define LAST_MONOTONIC_SOC_OFFSET 2 + +static int fg_decode_value_16b(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val); +static int fg_decode_default(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val); +static void fg_encode_voltage_16b(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val, u8 *buf); +static void fg_encode_voltage_8b(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val, u8 *buf); +static void fg_encode_current(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val, u8 *buf); +static void fg_encode_default(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val, u8 *buf); + +#define PARAM(_id, _addr, _offset, _len, _num, _den, _enc, _dec) \ + [FG_SRAM_##_id] = { \ + .address = _addr, \ + .offset = _offset, \ + .len = _len, \ + .numrtr = _num, \ + .denmtr = _den, \ + .encode = _enc, \ + .decode = _dec, \ + } \ + +static struct fg_sram_param pmicobalt_v1_sram_params[] = { + PARAM(BATT_SOC, BATT_SOC_WORD, BATT_SOC_OFFSET, 4, 1, 1, NULL, + fg_decode_default), + PARAM(VOLTAGE_PRED, VOLTAGE_PRED_WORD, VOLTAGE_PRED_OFFSET, 2, 244141, + 1000, NULL, fg_decode_value_16b), + PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 244141, 1000, NULL, + fg_decode_value_16b), + PARAM(RSLOW, RSLOW_WORD, RSLOW_OFFSET, 2, 244141, 1000, NULL, + fg_decode_value_16b), + /* Entries below here are configurable during initialization */ + PARAM(CUTOFF_VOLT, CUTOFF_VOLT_WORD, CUTOFF_VOLT_OFFSET, 2, 1000000, + 244141, fg_encode_voltage_16b, NULL), + PARAM(EMPTY_VOLT, EMPTY_VOLT_WORD, EMPTY_VOLT_OFFSET, 1, 100000, 390625, + fg_encode_voltage_8b, NULL), + PARAM(VBATT_LOW, VBATT_LOW_WORD, VBATT_LOW_OFFSET, 1, 100000, 390625, + fg_encode_voltage_8b, NULL), + PARAM(SYS_TERM_CURR, SYS_TERM_CURR_WORD, SYS_TERM_CURR_OFFSET, 3, + 1000000, 122070, fg_encode_current, NULL), + PARAM(CHG_TERM_CURR, CHG_TERM_CURR_WORD, CHG_TERM_CURR_OFFSET, 1, + 100000, 390625, fg_encode_current, NULL), + PARAM(DELTA_SOC_THR, DELTA_SOC_THR_WORD, DELTA_SOC_THR_OFFSET, 1, 256, + 100, fg_encode_default, NULL), + PARAM(RECHARGE_SOC_THR, RECHARGE_SOC_THR_WORD, RECHARGE_SOC_THR_OFFSET, + 1, 256, 100, fg_encode_default, NULL), +}; + +static int fg_gen3_debug_mask; +module_param_named( + debug_mask, fg_gen3_debug_mask, int, S_IRUSR | S_IWUSR +); + +static int fg_sram_update_period_ms = 30000; +module_param_named( + sram_update_period_ms, fg_sram_update_period_ms, int, S_IRUSR | S_IWUSR +); + +/* Other functions HERE */ + +static int fg_awake_cb(struct votable *votable, void *data, int awake, + const char *client) +{ + struct fg_chip *chip = data; + + if (awake) + pm_stay_awake(chip->dev); + else + pm_relax(chip->dev); + + pr_debug("client: %s awake: %d\n", client, awake); + return 0; +} + +static bool is_charger_available(struct fg_chip *chip) +{ + if (!chip->batt_psy) + chip->batt_psy = power_supply_get_by_name("battery"); + + if (!chip->batt_psy) + return false; + + return true; +} + +static void status_change_work(struct work_struct *work) +{ + struct fg_chip *chip = container_of(work, + struct fg_chip, status_change_work); + union power_supply_propval prop = {0, }; + + if (!is_charger_available(chip)) { + fg_dbg(chip, FG_STATUS, "Charger not available?!\n"); + return; + } + + power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS, + &prop); + switch (prop.intval) { + case POWER_SUPPLY_STATUS_CHARGING: + fg_dbg(chip, FG_POWER_SUPPLY, "Charging\n"); + break; + case POWER_SUPPLY_STATUS_DISCHARGING: + fg_dbg(chip, FG_POWER_SUPPLY, "Discharging\n"); + break; + case POWER_SUPPLY_STATUS_FULL: + fg_dbg(chip, FG_POWER_SUPPLY, "Full\n"); + break; + default: + break; + } +} + +#define PROFILE_LEN 224 +#define PROFILE_COMP_LEN 32 +#define SOC_READY_WAIT_MS 2000 +static void profile_load_work(struct work_struct *work) +{ + struct fg_chip *chip = container_of(work, + struct fg_chip, + profile_load_work.work); + int rc; + u8 buf[PROFILE_COMP_LEN], val; + bool tried_again = false, profiles_same = false; + + if (!chip->batt_id_avail) { + pr_err("batt_id not available\n"); + return; + } + + rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD, + PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("failed to read profile integrity rc=%d\n", rc); + return; + } + + vote(chip->awake_votable, PROFILE_LOAD, true, 0); + if (val == 0x01) { + fg_dbg(chip, FG_STATUS, "Battery profile integrity bit is set\n"); + rc = fg_sram_read(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET, + buf, PROFILE_COMP_LEN, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in reading battery profile, rc:%d\n", rc); + goto out; + } + profiles_same = memcmp(chip->batt_profile, buf, + PROFILE_COMP_LEN) == 0; + if (profiles_same) { + fg_dbg(chip, FG_STATUS, "Battery profile is same\n"); + goto done; + } + fg_dbg(chip, FG_STATUS, "profiles are different?\n"); + } + + fg_dbg(chip, FG_STATUS, "profile loading started\n"); + rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0); + if (rc < 0) { + pr_err("Error in writing to %04x, rc=%d\n", + BATT_SOC_RESTART(chip), rc); + goto out; + } + + /* load battery profile */ + rc = fg_sram_write(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET, + chip->batt_profile, PROFILE_LEN, FG_IMA_ATOMIC); + if (rc < 0) { + pr_err("Error in writing battery profile, rc:%d\n", rc); + goto out; + } + + rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, + RESTART_GO_BIT); + if (rc < 0) { + pr_err("Error in writing to %04x, rc=%d\n", + BATT_SOC_RESTART(chip), rc); + goto out; + } + +wait: + rc = wait_for_completion_interruptible_timeout(&chip->soc_ready, + msecs_to_jiffies(SOC_READY_WAIT_MS)); + + /* If we were interrupted wait again one more time. */ + if (rc == -ERESTARTSYS && !tried_again) { + tried_again = true; + goto wait; + } else if (rc <= 0) { + pr_err("wait for soc_ready timed out rc=%d\n", rc); + goto out; + } + + fg_dbg(chip, FG_STATUS, "SOC is ready\n"); + + /* Set the profile integrity bit */ + val = 0x1; + rc = fg_sram_write(chip, PROFILE_INTEGRITY_WORD, + PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("failed to write profile integrity rc=%d\n", rc); + goto out; + } + + fg_dbg(chip, FG_STATUS, "profile loaded successfully"); +done: + rc = fg_sram_read(chip, NOM_CAP_WORD, NOM_CAP_OFFSET, buf, 2, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in reading %04x[%d] rc=%d\n", NOM_CAP_WORD, + NOM_CAP_OFFSET, rc); + goto out; + } + + chip->nom_cap_uah = (int)(buf[0] | buf[1] << 8) * 1000; + chip->profile_loaded = true; +out: + vote(chip->awake_votable, PROFILE_LOAD, false, 0); + rc = fg_masked_write(chip, BATT_SOC_RESTART(chip), RESTART_GO_BIT, 0); + if (rc < 0) + pr_err("Error in writing to %04x, rc=%d\n", + BATT_SOC_RESTART(chip), rc); +} + +/* All getters HERE */ + +static int fg_decode_value_16b(struct fg_sram_param *sp, + enum fg_sram_param_id id, int value) +{ + sp[id].value = div_u64((u64)(u16)value * sp[id].numrtr, sp[id].denmtr); + pr_debug("id: %d raw value: %x decoded value: %x\n", id, value, + sp[id].value); + return sp[id].value; +} + +static int fg_decode_default(struct fg_sram_param *sp, + enum fg_sram_param_id id, int value) +{ + return value; +} + +static int fg_decode(struct fg_sram_param *sp, enum fg_sram_param_id id, + int value) +{ + if (!sp[id].decode) { + pr_err("No decoding function for parameter %d\n", id); + return -EINVAL; + } + + return sp[id].decode(sp, id, value); +} + +static void fg_encode_voltage_16b(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val, u8 *buf) +{ + int i, mask = 0xff; + int64_t temp; + + temp = (int64_t)div_u64((u64)val * sp[id].numrtr, sp[id].denmtr); + pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val); + for (i = 0; i < sp[id].len; i++) { + buf[i] = temp & mask; + temp >>= 8; + pr_debug("%x ", buf[i]); + } + pr_debug("]\n"); +} + +static void fg_encode_voltage_8b(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val, u8 *buf) +{ + int i, mask = 0xff; + int64_t temp; + + /* Offset is 2.5V */ + val -= 2500; + temp = (int64_t)div_u64((u64)val * sp[id].numrtr, sp[id].denmtr); + pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val); + for (i = 0; i < sp[id].len; i++) { + buf[i] = temp & mask; + temp >>= 8; + pr_debug("%x ", buf[i]); + } + pr_debug("]\n"); +} + +static void fg_encode_current(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val, u8 *buf) +{ + int i, mask = 0xff; + int64_t temp; + s64 current_ma; + + current_ma = -val; + temp = (int64_t)div_s64(current_ma * sp[id].numrtr, sp[id].denmtr); + pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val); + for (i = 0; i < sp[id].len; i++) { + buf[i] = temp & mask; + temp >>= 8; + pr_debug("%x ", buf[i]); + } + pr_debug("]\n"); +} + +static void fg_encode_default(struct fg_sram_param *sp, + enum fg_sram_param_id id, int val, u8 *buf) +{ + int i, mask = 0xff; + int64_t temp; + + temp = DIV_ROUND_CLOSEST(val * sp[id].numrtr, sp[id].denmtr); + pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val); + for (i = 0; i < sp[id].len; i++) { + buf[i] = temp & mask; + temp >>= 8; + pr_debug("%x ", buf[i]); + } + pr_debug("]\n"); +} + +static void fg_encode(struct fg_sram_param *sp, enum fg_sram_param_id id, + int val, u8 *buf) +{ + if (!sp[id].encode) { + pr_err("No encoding function for parameter %d\n", id); + return; + } + + sp[id].encode(sp, id, val, buf); +} + +/* + * Please make sure *_sram_params table has the entry for the parameter + * obtained through this function. In addition to address, offset, + * length from where this SRAM parameter is read, a decode function + * need to be specified. + */ +static int fg_get_sram_prop(struct fg_chip *chip, enum fg_sram_param_id id, + int *val) +{ + int temp, rc, i; + u8 buf[4]; + + if (id < 0 || id > FG_SRAM_MAX || chip->sp[id].len > sizeof(buf)) + return -EINVAL; + + vote(chip->awake_votable, SRAM_UPDATE, true, 0); + rc = fg_sram_read(chip, chip->sp[id].address, chip->sp[id].offset, + buf, chip->sp[id].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error reading address 0x%04x[%d] rc=%d\n", + chip->sp[id].address, chip->sp[id].offset, rc); + goto out; + } + + for (i = 0, temp = 0; i < chip->sp[id].len; i++) + temp |= buf[i] << (8 * i); + + *val = fg_decode(chip->sp, id, temp); + return 0; +out: + vote(chip->awake_votable, SRAM_UPDATE, false, 0); + return rc; +} + +#define BATT_TEMP_NUMR 1 +#define BATT_TEMP_DENR 1 +static int fg_get_battery_temp(struct fg_chip *chip, int *val) +{ + int rc = 0; + u16 temp = 0; + u8 buf[2]; + + rc = fg_read(chip, BATT_INFO_BATT_TEMP_LSB(chip), buf, 2); + if (rc < 0) { + pr_err("failed to read addr=0x%04x, rc=%d\n", + BATT_INFO_BATT_TEMP_LSB(chip), rc); + return rc; + } + + temp = ((buf[1] & BATT_TEMP_MSB_MASK) << 8) | + (buf[0] & BATT_TEMP_LSB_MASK); + temp = DIV_ROUND_CLOSEST(temp, 4); + + /* Value is in Kelvin; Convert it to deciDegC */ + temp = (temp - 273) * 10; + *val = temp; + return 0; +} + +#define BATT_ESR_NUMR 244141 +#define BATT_ESR_DENR 1000 +static int fg_get_battery_esr(struct fg_chip *chip, int *val) +{ + int rc = 0; + u16 temp = 0; + u8 buf[2]; + + rc = fg_read(chip, BATT_INFO_ESR_LSB(chip), buf, 2); + if (rc < 0) { + pr_err("failed to read addr=0x%04x, rc=%d\n", + BATT_INFO_ESR_LSB(chip), rc); + return rc; + } + + if (chip->pmic_rev_id->rev4 < PMICOBALT_V2P0_REV4) + temp = ((buf[0] & ESR_MSB_MASK) << 8) | + (buf[1] & ESR_LSB_MASK); + else + temp = ((buf[1] & ESR_MSB_MASK) << 8) | + (buf[0] & ESR_LSB_MASK); + + pr_debug("buf: %x %x temp: %x\n", buf[0], buf[1], temp); + *val = div_u64((u64)temp * BATT_ESR_NUMR, BATT_ESR_DENR); + return 0; +} + +static int fg_get_battery_resistance(struct fg_chip *chip, int *val) +{ + int rc, esr, rslow; + + rc = fg_get_battery_esr(chip, &esr); + if (rc < 0) { + pr_err("failed to get ESR, rc=%d\n", rc); + return rc; + } + + rc = fg_get_sram_prop(chip, FG_SRAM_RSLOW, &rslow); + if (rc < 0) { + pr_err("failed to get Rslow, rc=%d\n", rc); + return rc; + } + + *val = esr + rslow; + return 0; +} + +#define BATT_CURRENT_NUMR 488281 +#define BATT_CURRENT_DENR 1000 +static int fg_get_battery_current(struct fg_chip *chip, int *val) +{ + int rc = 0; + int64_t temp = 0; + u8 buf[2]; + + rc = fg_read(chip, BATT_INFO_IBATT_LSB(chip), buf, 2); + if (rc < 0) { + pr_err("failed to read addr=0x%04x, rc=%d\n", + BATT_INFO_IBATT_LSB(chip), rc); + return rc; + } + + if (chip->pmic_rev_id->rev4 < PMICOBALT_V2P0_REV4) + temp = buf[0] << 8 | buf[1]; + else + temp = buf[1] << 8 | buf[0]; + + pr_debug("buf: %x %x temp: %llx\n", buf[0], buf[1], temp); + /* Sign bit is bit 15 */ + temp = twos_compliment_extend(temp, 15); + *val = div_s64((s64)temp * BATT_CURRENT_NUMR, BATT_CURRENT_DENR); + return 0; +} + +#define BATT_VOLTAGE_NUMR 122070 +#define BATT_VOLTAGE_DENR 1000 +static int fg_get_battery_voltage(struct fg_chip *chip, int *val) +{ + int rc = 0; + u16 temp = 0; + u8 buf[2]; + + rc = fg_read(chip, BATT_INFO_VBATT_LSB(chip), buf, 2); + if (rc < 0) { + pr_err("failed to read addr=0x%04x, rc=%d\n", + BATT_INFO_VBATT_LSB(chip), rc); + return rc; + } + + if (chip->pmic_rev_id->rev4 < PMICOBALT_V2P0_REV4) + temp = buf[0] << 8 | buf[1]; + else + temp = buf[1] << 8 | buf[0]; + + pr_debug("buf: %x %x temp: %x\n", buf[0], buf[1], temp); + *val = div_u64((u64)temp * BATT_VOLTAGE_NUMR, BATT_VOLTAGE_DENR); + return 0; +} + +#define MAX_TRIES_SOC 5 +static int fg_get_msoc_raw(struct fg_chip *chip, int *val) +{ + u8 cap[2]; + int rc, tries = 0; + + while (tries < MAX_TRIES_SOC) { + rc = fg_read(chip, BATT_SOC_FG_MONOTONIC_SOC(chip), cap, 2); + if (rc < 0) { + pr_err("failed to read addr=0x%04x, rc=%d\n", + BATT_SOC_FG_MONOTONIC_SOC(chip), rc); + return rc; + } + + if (cap[0] == cap[1]) + break; + + tries++; + } + + if (tries == MAX_TRIES_SOC) { + pr_err("shadow registers do not match\n"); + return -EINVAL; + } + + fg_dbg(chip, FG_POWER_SUPPLY, "raw: 0x%02x\n", cap[0]); + + *val = cap[0]; + return 0; +} + +#define FULL_CAPACITY 100 +#define FULL_SOC_RAW 255 +static int fg_get_prop_capacity(struct fg_chip *chip, int *val) +{ + int rc, msoc; + + rc = fg_get_msoc_raw(chip, &msoc); + if (rc < 0) + return rc; + + *val = DIV_ROUND_CLOSEST(msoc * FULL_CAPACITY, FULL_SOC_RAW); + return 0; +} + +#define DEFAULT_BATT_TYPE "Unknown Battery" +#define MISSING_BATT_TYPE "Missing Battery" +#define LOADING_BATT_TYPE "Loading Battery" +static const char *fg_get_battery_type(struct fg_chip *chip) +{ + if (chip->battery_missing) + return MISSING_BATT_TYPE; + + if (chip->bp.batt_type_str) { + if (chip->profile_loaded) + return chip->bp.batt_type_str; + else + return LOADING_BATT_TYPE; + } + + return DEFAULT_BATT_TYPE; +} + +static int fg_get_batt_id(struct fg_chip *chip, int *val) +{ + int rc, batt_id = -EINVAL; + + if (!chip->batt_id_chan) + return -EINVAL; + + rc = iio_read_channel_processed(chip->batt_id_chan, &batt_id); + if (rc < 0) { + pr_err("Error in reading batt_id channel, rc:%d\n", rc); + return rc; + } + + chip->batt_id_avail = true; + fg_dbg(chip, FG_STATUS, "batt_id: %d\n", batt_id); + + *val = batt_id; + return 0; +} + +static int fg_get_batt_profile(struct fg_chip *chip) +{ + struct device_node *node = chip->dev->of_node; + struct device_node *batt_node, *profile_node; + const char *data; + int rc, len, batt_id; + + rc = fg_get_batt_id(chip, &batt_id); + if (rc < 0) { + pr_err("Error in getting batt_id rc:%d\n", rc); + return rc; + } + + batt_node = of_find_node_by_name(node, "qcom,battery-data"); + if (!batt_node) { + pr_err("Batterydata not available\n"); + return -ENXIO; + } + + batt_id /= 1000; + profile_node = of_batterydata_get_best_profile(batt_node, batt_id, + NULL); + if (IS_ERR(profile_node)) + return PTR_ERR(profile_node); + + if (!profile_node) { + pr_err("couldn't find profile handle\n"); + return -ENODATA; + } + + rc = of_property_read_string(profile_node, "qcom,battery-type", + &chip->bp.batt_type_str); + if (rc < 0) { + pr_err("battery type unavailable, rc:%d\n", rc); + return rc; + } + + rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv", + &chip->bp.float_volt_uv); + if (rc < 0) { + pr_err("battery float voltage unavailable, rc:%d\n", rc); + chip->bp.float_volt_uv = -EINVAL; + } + + rc = of_property_read_u32(profile_node, "qcom,nom-batt-capacity-mah", + &chip->bp.fastchg_curr_ma); + if (rc < 0) { + pr_err("battery nominal capacity unavailable, rc:%d\n", rc); + chip->bp.fastchg_curr_ma = -EINVAL; + } + + data = of_get_property(profile_node, "qcom,fg-profile-data", &len); + if (!data) { + pr_err("No profile data available\n"); + return -ENODATA; + } + + if (len != PROFILE_LEN) { + pr_err("battery profile incorrect size: %d\n", len); + return -EINVAL; + } + + memcpy(chip->batt_profile, data, len); + return 0; +} + +static inline void get_temp_setpoint(int threshold, u8 *val) +{ + /* Resolution is 0.5C. Base is -30C. */ + *val = DIV_ROUND_CLOSEST((threshold + 30) * 10, 5); +} + +/* PSY CALLBACKS STAY HERE */ + +static int fg_psy_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *pval) +{ + struct fg_chip *chip = power_supply_get_drvdata(psy); + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_CAPACITY: + rc = fg_get_prop_capacity(chip, &pval->intval); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + rc = fg_get_battery_voltage(chip, &pval->intval); + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + rc = fg_get_battery_current(chip, &pval->intval); + break; + case POWER_SUPPLY_PROP_TEMP: + rc = fg_get_battery_temp(chip, &pval->intval); + break; + case POWER_SUPPLY_PROP_RESISTANCE: + rc = fg_get_battery_resistance(chip, &pval->intval); + break; + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + rc = fg_get_sram_prop(chip, FG_SRAM_OCV, &pval->intval); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + pval->intval = chip->nom_cap_uah; + break; + case POWER_SUPPLY_PROP_RESISTANCE_ID: + rc = fg_get_batt_id(chip, &pval->intval); + break; + case POWER_SUPPLY_PROP_BATTERY_TYPE: + pval->strval = fg_get_battery_type(chip); + break; + default: + break; + } + + return rc; +} + +static int fg_psy_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *pval) +{ + switch (psp) { + default: + break; + } + + return 0; +} + +static int fg_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + default: + break; + } + + return 0; +} + +static void fg_external_power_changed(struct power_supply *psy) +{ + pr_debug("power supply changed\n"); +} + +static int fg_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct power_supply *psy = data; + struct fg_chip *chip = container_of(nb, struct fg_chip, nb); + + if (event != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + if ((strcmp(psy->desc->name, "battery") == 0) + || (strcmp(psy->desc->name, "usb") == 0)) + schedule_work(&chip->status_change_work); + + return NOTIFY_OK; +} + +static enum power_supply_property fg_psy_props[] = { + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_OCV, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_RESISTANCE_ID, + POWER_SUPPLY_PROP_RESISTANCE, + POWER_SUPPLY_PROP_BATTERY_TYPE, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, +}; + +static const struct power_supply_desc fg_psy_desc = { + .name = "bms", + .type = POWER_SUPPLY_TYPE_BMS, + .properties = fg_psy_props, + .num_properties = ARRAY_SIZE(fg_psy_props), + .get_property = fg_psy_get_property, + .set_property = fg_psy_set_property, + .external_power_changed = fg_external_power_changed, + .property_is_writeable = fg_property_is_writeable, +}; + +/* INIT FUNCTIONS STAY HERE */ + +static int fg_hw_init(struct fg_chip *chip) +{ + int rc; + u8 buf[4], val; + + fg_encode(chip->sp, FG_SRAM_CUTOFF_VOLT, chip->dt.cutoff_volt_mv, buf); + rc = fg_sram_write(chip, chip->sp[FG_SRAM_CUTOFF_VOLT].address, + chip->sp[FG_SRAM_CUTOFF_VOLT].offset, buf, + chip->sp[FG_SRAM_CUTOFF_VOLT].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing cutoff_volt, rc=%d\n", rc); + return rc; + } + + fg_encode(chip->sp, FG_SRAM_EMPTY_VOLT, chip->dt.empty_volt_mv, buf); + rc = fg_sram_write(chip, chip->sp[FG_SRAM_EMPTY_VOLT].address, + chip->sp[FG_SRAM_EMPTY_VOLT].offset, buf, + chip->sp[FG_SRAM_EMPTY_VOLT].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing empty_volt, rc=%d\n", rc); + return rc; + } + + fg_encode(chip->sp, FG_SRAM_CHG_TERM_CURR, chip->dt.chg_term_curr_ma, + buf); + rc = fg_sram_write(chip, chip->sp[FG_SRAM_CHG_TERM_CURR].address, + chip->sp[FG_SRAM_CHG_TERM_CURR].offset, buf, + chip->sp[FG_SRAM_CHG_TERM_CURR].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing chg_term_curr, rc=%d\n", rc); + return rc; + } + + fg_encode(chip->sp, FG_SRAM_SYS_TERM_CURR, chip->dt.sys_term_curr_ma, + buf); + rc = fg_sram_write(chip, chip->sp[FG_SRAM_SYS_TERM_CURR].address, + chip->sp[FG_SRAM_SYS_TERM_CURR].offset, buf, + chip->sp[FG_SRAM_SYS_TERM_CURR].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing sys_term_curr, rc=%d\n", rc); + return rc; + } + + if (chip->dt.vbatt_low_thr_mv > 0) { + fg_encode(chip->sp, FG_SRAM_VBATT_LOW, + chip->dt.vbatt_low_thr_mv, buf); + rc = fg_sram_write(chip, chip->sp[FG_SRAM_VBATT_LOW].address, + chip->sp[FG_SRAM_VBATT_LOW].offset, buf, + chip->sp[FG_SRAM_VBATT_LOW].len, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing vbatt_low_thr, rc=%d\n", rc); + return rc; + } + } + + if (chip->dt.delta_soc_thr > 0 && chip->dt.delta_soc_thr < 100) { + fg_encode(chip->sp, FG_SRAM_DELTA_SOC_THR, + chip->dt.delta_soc_thr, buf); + rc = fg_sram_write(chip, + chip->sp[FG_SRAM_DELTA_SOC_THR].address, + chip->sp[FG_SRAM_DELTA_SOC_THR].offset, + buf, chip->sp[FG_SRAM_DELTA_SOC_THR].len, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing delta_soc_thr, rc=%d\n", rc); + return rc; + } + } + + if (chip->dt.recharge_soc_thr > 0 && chip->dt.recharge_soc_thr < 100) { + fg_encode(chip->sp, FG_SRAM_RECHARGE_SOC_THR, + chip->dt.recharge_soc_thr, buf); + rc = fg_sram_write(chip, + chip->sp[FG_SRAM_RECHARGE_SOC_THR].address, + chip->sp[FG_SRAM_RECHARGE_SOC_THR].offset, + buf, chip->sp[FG_SRAM_RECHARGE_SOC_THR].len, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing recharge_soc_thr, rc=%d\n", + rc); + return rc; + } + } + + if (chip->dt.rsense_sel >= SRC_SEL_BATFET && + chip->dt.rsense_sel < SRC_SEL_RESERVED) { + rc = fg_masked_write(chip, BATT_INFO_IBATT_SENSING_CFG(chip), + SOURCE_SELECT_MASK, chip->dt.rsense_sel); + if (rc < 0) { + pr_err("Error in writing rsense_sel, rc=%d\n", rc); + return rc; + } + } + + get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_COLD], &val); + rc = fg_write(chip, BATT_INFO_JEITA_TOO_COLD(chip), &val, 1); + if (rc < 0) { + pr_err("Error in writing jeita_cold, rc=%d\n", rc); + return rc; + } + + get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_COOL], &val); + rc = fg_write(chip, BATT_INFO_JEITA_COLD(chip), &val, 1); + if (rc < 0) { + pr_err("Error in writing jeita_cool, rc=%d\n", rc); + return rc; + } + + get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_WARM], &val); + rc = fg_write(chip, BATT_INFO_JEITA_HOT(chip), &val, 1); + if (rc < 0) { + pr_err("Error in writing jeita_warm, rc=%d\n", rc); + return rc; + } + + get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_HOT], &val); + rc = fg_write(chip, BATT_INFO_JEITA_TOO_HOT(chip), &val, 1); + if (rc < 0) { + pr_err("Error in writing jeita_hot, rc=%d\n", rc); + return rc; + } + + return 0; +} + +static int fg_memif_init(struct fg_chip *chip) +{ + return fg_ima_init(chip); +} + +static int fg_batt_profile_init(struct fg_chip *chip) +{ + int rc; + + if (!chip->batt_profile) { + chip->batt_profile = devm_kcalloc(chip->dev, PROFILE_LEN, + sizeof(*chip->batt_profile), + GFP_KERNEL); + if (!chip->batt_profile) + return -ENOMEM; + } + + rc = fg_get_batt_profile(chip); + if (rc < 0) { + pr_err("Error in getting battery profile, rc:%d\n", rc); + return rc; + } + + schedule_delayed_work(&chip->profile_load_work, msecs_to_jiffies(0)); + return 0; +} + +/* INTERRUPT HANDLERS STAY HERE */ + +static irqreturn_t fg_vbatt_low_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + return IRQ_HANDLED; +} + +static irqreturn_t fg_batt_missing_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + u8 status; + int rc; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + rc = fg_read(chip, BATT_INFO_INT_RT_STS(chip), &status, 1); + if (rc < 0) { + pr_err("failed to read addr=0x%04x, rc=%d\n", + BATT_INFO_INT_RT_STS(chip), rc); + return IRQ_HANDLED; + } + + chip->battery_missing = (status & BT_MISS_BIT); + + if (chip->battery_missing) { + chip->batt_id_avail = false; + chip->profile_loaded = false; + } else { + rc = fg_batt_profile_init(chip); + if (rc < 0) { + pr_err("Error in initializing battery profile, rc=%d\n", + rc); + return IRQ_HANDLED; + } + } + + return IRQ_HANDLED; +} + +static irqreturn_t fg_delta_batt_temp_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + return IRQ_HANDLED; +} + +static irqreturn_t fg_first_est_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + complete_all(&chip->soc_ready); + return IRQ_HANDLED; +} + +static irqreturn_t fg_soc_update_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + complete_all(&chip->soc_update); + return IRQ_HANDLED; +} + +static irqreturn_t fg_delta_soc_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + return IRQ_HANDLED; +} + +static irqreturn_t fg_empty_soc_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + return IRQ_HANDLED; +} + +static irqreturn_t fg_soc_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + return IRQ_HANDLED; +} + +static irqreturn_t fg_dummy_irq_handler(int irq, void *data) +{ + struct fg_chip *chip = data; + + fg_dbg(chip, FG_IRQ, "irq %d triggered\n", irq); + return IRQ_HANDLED; +} + +static struct fg_irq_info fg_irqs[FG_IRQ_MAX] = { + /* BATT_SOC irqs */ + [MSOC_FULL_IRQ] = { + "msoc-full", fg_soc_irq_handler, true }, + [MSOC_HIGH_IRQ] = { + "msoc-high", fg_soc_irq_handler, true }, + [MSOC_EMPTY_IRQ] = { + "msoc-empty", fg_empty_soc_irq_handler, true }, + [MSOC_LOW_IRQ] = { + "msoc-low", fg_soc_irq_handler }, + [MSOC_DELTA_IRQ] = { + "msoc-delta", fg_delta_soc_irq_handler, true }, + [BSOC_DELTA_IRQ] = { + "bsoc-delta", fg_delta_soc_irq_handler, true }, + [SOC_READY_IRQ] = { + "soc-ready", fg_first_est_irq_handler, true }, + [SOC_UPDATE_IRQ] = { + "soc-update", fg_soc_update_irq_handler }, + /* BATT_INFO irqs */ + [BATT_TEMP_DELTA_IRQ] = { + "batt-temp-delta", fg_delta_batt_temp_irq_handler }, + [BATT_MISSING_IRQ] = { + "batt-missing", fg_batt_missing_irq_handler, true }, + [ESR_DELTA_IRQ] = { + "esr-delta", fg_dummy_irq_handler }, + [VBATT_LOW_IRQ] = { + "vbatt-low", fg_vbatt_low_irq_handler, true }, + [VBATT_PRED_DELTA_IRQ] = { + "vbatt-pred-delta", fg_dummy_irq_handler }, + /* MEM_IF irqs */ + [DMA_GRANT_IRQ] = { + "dma-grant", fg_dummy_irq_handler }, + [MEM_XCP_IRQ] = { + "mem-xcp", fg_dummy_irq_handler }, + [IMA_RDY_IRQ] = { + "ima-rdy", fg_dummy_irq_handler }, +}; + +static int fg_get_irq_index_byname(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fg_irqs); i++) { + if (strcmp(fg_irqs[i].name, name) == 0) + return i; + } + + pr_err("%s is not in irq list\n", name); + return -ENOENT; +} + +static int fg_register_interrupts(struct fg_chip *chip) +{ + struct device_node *child, *node = chip->dev->of_node; + struct property *prop; + const char *name; + int rc, irq, irq_index; + + for_each_available_child_of_node(node, child) { + of_property_for_each_string(child, "interrupt-names", prop, + name) { + irq = of_irq_get_byname(child, name); + if (irq < 0) { + dev_err(chip->dev, "failed to get irq %s irq:%d\n", + name, irq); + return irq; + } + + irq_index = fg_get_irq_index_byname(name); + if (irq_index < 0) + return irq_index; + + rc = devm_request_threaded_irq(chip->dev, irq, NULL, + fg_irqs[irq_index].handler, + IRQF_ONESHOT, name, chip); + if (rc < 0) { + dev_err(chip->dev, "failed to register irq handler for %s rc:%d\n", + name, rc); + return rc; + } + + fg_irqs[irq_index].irq = irq; + if (fg_irqs[irq_index].wakeable) + enable_irq_wake(fg_irqs[irq_index].irq); + } + } + + return 0; +} + +#define DEFAULT_CUTOFF_VOLT_MV 3200 +#define DEFAULT_EMPTY_VOLT_MV 3100 +#define DEFAULT_CHG_TERM_CURR_MA 100 +#define DEFAULT_SYS_TERM_CURR_MA 125 +#define DEFAULT_DELTA_SOC_THR 1 +#define DEFAULT_RECHARGE_SOC_THR 95 +#define DEFAULT_BATT_TEMP_COLD 0 +#define DEFAULT_BATT_TEMP_COOL 5 +#define DEFAULT_BATT_TEMP_WARM 45 +#define DEFAULT_BATT_TEMP_HOT 50 +static int fg_parse_dt(struct fg_chip *chip) +{ + struct device_node *child, *revid_node, *node = chip->dev->of_node; + u32 base, temp; + u8 subtype; + int rc, len; + + if (!node) { + dev_err(chip->dev, "device tree node missing\n"); + return -ENXIO; + } + + revid_node = of_parse_phandle(node, "qcom,pmic-revid", 0); + if (!revid_node) { + pr_err("Missing qcom,pmic-revid property - driver failed\n"); + return -EINVAL; + } + + chip->pmic_rev_id = get_revid_data(revid_node); + if (IS_ERR_OR_NULL(chip->pmic_rev_id)) { + pr_err("Unable to get pmic_revid rc=%ld\n", + PTR_ERR(chip->pmic_rev_id)); + /* + * the revid peripheral must be registered, any failure + * here only indicates that the rev-id module has not + * probed yet. + */ + return -EPROBE_DEFER; + } + + pr_debug("PMIC subtype %d Digital major %d\n", + chip->pmic_rev_id->pmic_subtype, chip->pmic_rev_id->rev4); + + switch (chip->pmic_rev_id->pmic_subtype) { + case PMICOBALT_SUBTYPE: + if (chip->pmic_rev_id->rev4 < PMICOBALT_V2P0_REV4) + chip->sp = pmicobalt_v1_sram_params; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + chip->batt_id_chan = iio_channel_get(chip->dev, "rradc_batt_id"); + if (IS_ERR(chip->batt_id_chan)) { + if (PTR_ERR(chip->batt_id_chan) != -EPROBE_DEFER) + pr_err("batt_id_chan unavailable %ld\n", + PTR_ERR(chip->batt_id_chan)); + rc = PTR_ERR(chip->batt_id_chan); + chip->batt_id_chan = NULL; + return rc; + } + + if (of_get_available_child_count(node) == 0) { + dev_err(chip->dev, "No child nodes specified!\n"); + return -ENXIO; + } + + for_each_available_child_of_node(node, child) { + rc = of_property_read_u32(child, "reg", &base); + if (rc < 0) { + dev_err(chip->dev, "reg not specified in node %s, rc=%d\n", + child->full_name, rc); + return rc; + } + + rc = fg_read(chip, base + PERPH_SUBTYPE_REG, &subtype, 1); + if (rc < 0) { + dev_err(chip->dev, "Couldn't read subtype for base %d, rc=%d\n", + base, rc); + return rc; + } + + switch (subtype) { + case FG_BATT_SOC_PMICOBALT: + chip->batt_soc_base = base; + break; + case FG_BATT_INFO_PMICOBALT: + chip->batt_info_base = base; + break; + case FG_MEM_INFO_PMICOBALT: + chip->mem_if_base = base; + break; + default: + dev_err(chip->dev, "Invalid peripheral subtype 0x%x\n", + subtype); + return -ENXIO; + } + } + + /* Read all the optional properties below */ + rc = of_property_read_u32(node, "qcom,fg-cutoff-voltage", &temp); + if (rc < 0) + chip->dt.cutoff_volt_mv = DEFAULT_CUTOFF_VOLT_MV; + else + chip->dt.cutoff_volt_mv = temp; + + rc = of_property_read_u32(node, "qcom,fg-empty-voltage", &temp); + if (rc < 0) + chip->dt.empty_volt_mv = DEFAULT_EMPTY_VOLT_MV; + else + chip->dt.empty_volt_mv = temp; + + rc = of_property_read_u32(node, "qcom,fg-vbatt-low-thr", &temp); + if (rc < 0) + chip->dt.vbatt_low_thr_mv = -EINVAL; + else + chip->dt.vbatt_low_thr_mv = temp; + + rc = of_property_read_u32(node, "qcom,fg-chg-term-current", &temp); + if (rc < 0) + chip->dt.chg_term_curr_ma = DEFAULT_CHG_TERM_CURR_MA; + else + chip->dt.chg_term_curr_ma = temp; + + rc = of_property_read_u32(node, "qcom,fg-sys-term-current", &temp); + if (rc < 0) + chip->dt.sys_term_curr_ma = DEFAULT_SYS_TERM_CURR_MA; + else + chip->dt.sys_term_curr_ma = temp; + + rc = of_property_read_u32(node, "qcom,fg-delta-soc-thr", &temp); + if (rc < 0) + chip->dt.delta_soc_thr = DEFAULT_DELTA_SOC_THR; + else + chip->dt.delta_soc_thr = temp; + + rc = of_property_read_u32(node, "qcom,fg-recharge-soc-thr", &temp); + if (rc < 0) + chip->dt.recharge_soc_thr = DEFAULT_RECHARGE_SOC_THR; + else + chip->dt.recharge_soc_thr = temp; + + rc = of_property_read_u32(node, "qcom,fg-rsense-sel", &temp); + if (rc < 0) + chip->dt.rsense_sel = SRC_SEL_BATFET_SMB; + else + chip->dt.rsense_sel = (u8)temp & SOURCE_SELECT_MASK; + + chip->dt.jeita_thresholds[JEITA_COLD] = DEFAULT_BATT_TEMP_COLD; + chip->dt.jeita_thresholds[JEITA_COOL] = DEFAULT_BATT_TEMP_COOL; + chip->dt.jeita_thresholds[JEITA_WARM] = DEFAULT_BATT_TEMP_WARM; + chip->dt.jeita_thresholds[JEITA_HOT] = DEFAULT_BATT_TEMP_HOT; + if (of_find_property(node, "qcom,fg-jeita-thresholds", &len)) { + if (len == NUM_JEITA_LEVELS) { + rc = of_property_read_u32_array(node, + "qcom,fg-jeita-thresholds", + chip->dt.jeita_thresholds, len); + if (rc < 0) + pr_warn("Error reading Jeita thresholds, default values will be used rc:%d\n", + rc); + } + } + + return 0; +} + +static void fg_cleanup(struct fg_chip *chip) +{ + power_supply_unreg_notifier(&chip->nb); + debugfs_remove_recursive(chip->dentry); + if (chip->awake_votable) + destroy_votable(chip->awake_votable); + + if (chip->batt_id_chan) + iio_channel_release(chip->batt_id_chan); + + dev_set_drvdata(chip->dev, NULL); +} + +static int fg_gen3_probe(struct platform_device *pdev) +{ + struct fg_chip *chip; + struct power_supply_config fg_psy_cfg; + int rc; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &pdev->dev; + chip->debug_mask = &fg_gen3_debug_mask; + chip->irqs = fg_irqs; + chip->regmap = dev_get_regmap(chip->dev->parent, NULL); + if (!chip->regmap) { + dev_err(chip->dev, "Parent regmap is unavailable\n"); + return -ENXIO; + } + + rc = fg_parse_dt(chip); + if (rc < 0) { + dev_err(chip->dev, "Error in reading DT parameters, rc:%d\n", + rc); + return rc; + } + + chip->awake_votable = create_votable("FG_WS", VOTE_SET_ANY, fg_awake_cb, + chip); + if (IS_ERR(chip->awake_votable)) { + rc = PTR_ERR(chip->awake_votable); + return rc; + } + + mutex_init(&chip->bus_lock); + mutex_init(&chip->sram_rw_lock); + init_completion(&chip->soc_update); + init_completion(&chip->soc_ready); + INIT_DELAYED_WORK(&chip->profile_load_work, profile_load_work); + INIT_WORK(&chip->status_change_work, status_change_work); + + rc = fg_memif_init(chip); + if (rc < 0) { + dev_err(chip->dev, "Error in initializing FG_MEMIF, rc:%d\n", + rc); + goto exit; + } + + rc = fg_hw_init(chip); + if (rc < 0) { + dev_err(chip->dev, "Error in initializing FG hardware, rc:%d\n", + rc); + goto exit; + } + + platform_set_drvdata(pdev, chip); + + /* Register the power supply */ + fg_psy_cfg.drv_data = chip; + fg_psy_cfg.of_node = NULL; + fg_psy_cfg.supplied_to = NULL; + fg_psy_cfg.num_supplicants = 0; + chip->fg_psy = devm_power_supply_register(chip->dev, &fg_psy_desc, + &fg_psy_cfg); + if (IS_ERR(chip->fg_psy)) { + pr_err("failed to register fg_psy rc = %ld\n", + PTR_ERR(chip->fg_psy)); + goto exit; + } + + chip->nb.notifier_call = fg_notifier_cb; + rc = power_supply_reg_notifier(&chip->nb); + if (rc < 0) { + pr_err("Couldn't register psy notifier rc = %d\n", rc); + goto exit; + } + + rc = fg_register_interrupts(chip); + if (rc < 0) { + dev_err(chip->dev, "Error in registering interrupts, rc:%d\n", + rc); + goto exit; + } + + /* Keep SOC_UPDATE irq disabled until we require it */ + if (fg_irqs[SOC_UPDATE_IRQ].irq) + disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq); + + rc = fg_sram_debugfs_create(chip); + if (rc < 0) { + dev_err(chip->dev, "Error in creating debugfs entries, rc:%d\n", + rc); + goto exit; + } + + rc = fg_batt_profile_init(chip); + if (rc < 0) + dev_warn(chip->dev, "Error in initializing battery profile, rc:%d\n", + rc); + + device_init_wakeup(chip->dev, true); + pr_debug("FG GEN3 driver successfully probed\n"); + return 0; +exit: + fg_cleanup(chip); + return rc; +} + +static int fg_gen3_remove(struct platform_device *pdev) +{ + struct fg_chip *chip = dev_get_drvdata(&pdev->dev); + + fg_cleanup(chip); + return 0; +} + +static const struct of_device_id fg_gen3_match_table[] = { + {.compatible = FG_GEN3_DEV_NAME}, + {}, +}; + +static struct platform_driver fg_gen3_driver = { + .driver = { + .name = FG_GEN3_DEV_NAME, + .owner = THIS_MODULE, + .of_match_table = fg_gen3_match_table, + }, + .probe = fg_gen3_probe, + .remove = fg_gen3_remove, +}; + +static int __init fg_gen3_init(void) +{ + return platform_driver_register(&fg_gen3_driver); +} + +static void __exit fg_gen3_exit(void) +{ + return platform_driver_unregister(&fg_gen3_driver); +} + +module_init(fg_gen3_init); +module_exit(fg_gen3_exit); + +MODULE_DESCRIPTION("QPNP Fuel gauge GEN3 driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" FG_GEN3_DEV_NAME); diff --git a/drivers/power/qcom-charger/qpnp-fg.c b/drivers/power/qcom-charger/qpnp-fg.c index 8660c1f8c3f5..0658f0d3b1eb 100644 --- a/drivers/power/qcom-charger/qpnp-fg.c +++ b/drivers/power/qcom-charger/qpnp-fg.c @@ -4772,8 +4772,7 @@ fail: #define BATTERY_PSY_WAIT_MS 2000 static int fg_batt_profile_init(struct fg_chip *chip) { - int rc = 0, ret; - int len; + int rc = 0, ret, len, batt_id; struct device_node *node = chip->pdev->dev.of_node; struct device_node *batt_node, *profile_node; const char *data, *batt_type_str; @@ -4802,14 +4801,16 @@ wait: goto no_profile; } + batt_id = get_sram_prop_now(chip, FG_DATA_BATT_ID); + batt_id /= 1000; if (fg_debug_mask & FG_STATUS) - pr_info("battery id = %d\n", - get_sram_prop_now(chip, FG_DATA_BATT_ID)); - profile_node = of_batterydata_get_best_profile(batt_node, "bms", + pr_info("battery id = %dKOhms\n", batt_id); + + profile_node = of_batterydata_get_best_profile(batt_node, batt_id, fg_batt_type); - if (!profile_node) { - pr_err("couldn't find profile handle\n"); - rc = -ENODATA; + if (IS_ERR_OR_NULL(profile_node)) { + rc = PTR_ERR(profile_node); + pr_err("couldn't find profile handle %d\n", rc); goto no_profile; } diff --git a/drivers/power/qcom-charger/qpnp-qnovo.c b/drivers/power/qcom-charger/qpnp-qnovo.c index d50188a5efbf..2418b112d670 100644 --- a/drivers/power/qcom-charger/qpnp-qnovo.c +++ b/drivers/power/qcom-charger/qpnp-qnovo.c @@ -153,7 +153,7 @@ struct qnovo { struct work_struct status_change_work; int fv_uV_request; int fcc_uA_request; - struct votable *fcc_votable; + struct votable *fcc_max_votable; struct votable *fv_votable; }; @@ -243,8 +243,9 @@ static int qnovo_disable_cb(struct votable *votable, void *data, int disable, vote(chip->fv_votable, QNOVO_VOTER, false, 0); } if (chip->fcc_uA_request != -EINVAL) { - if (chip->fcc_votable) - vote(chip->fcc_votable, QNOVO_VOTER, false, 0); + if (chip->fcc_max_votable) + vote(chip->fcc_max_votable, QNOVO_VOTER, + false, 0); } } @@ -265,10 +266,10 @@ static int qnovo_disable_cb(struct votable *votable, void *data, int disable, true, chip->fv_uV_request); } if (chip->fcc_uA_request != -EINVAL) { - if (!chip->fcc_votable) - chip->fcc_votable = find_votable("FCC"); - if (chip->fcc_votable) - vote(chip->fcc_votable, QNOVO_VOTER, + if (!chip->fcc_max_votable) + chip->fcc_max_votable = find_votable("FCC_MAX"); + if (chip->fcc_max_votable) + vote(chip->fcc_max_votable, QNOVO_VOTER, true, chip->fcc_uA_request); } } diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c index 7810ecb9b15b..08e64973d588 100644 --- a/drivers/power/qcom-charger/qpnp-smb2.c +++ b/drivers/power/qcom-charger/qpnp-smb2.c @@ -25,54 +25,105 @@ #include "smb-lib.h" #include "pmic-voter.h" -#define SMB2_DEFAULT_FCC_UA 3000000 -#define SMB2_DEFAULT_FV_UV 4350000 -#define SMB2_DEFAULT_ICL_UA 3000000 +#define SMB2_DEFAULT_FCC_UA 3000000 +#define SMB2_DEFAULT_FV_UV 4350000 +#define SMB2_DEFAULT_ICL_UA 3000000 +#define SMB2_DEFAULT_WPWR_UW 8000000 static struct smb_params v1_params = { - .fcc = { + .fcc = { .name = "fast charge current", .reg = FAST_CHARGE_CURRENT_CFG_REG, .min_u = 0, .max_u = 4500000, .step_u = 25000, }, - .fv = { + .fv = { .name = "float voltage", .reg = FLOAT_VOLTAGE_CFG_REG, - .min_u = 3487500, - .max_u = 4920000, - .step_u = 7500, + .min_u = 2500000, + .max_u = 5000000, + .step_u = 10000, }, - .usb_icl = { + .usb_icl = { .name = "usb input current limit", .reg = USBIN_CURRENT_LIMIT_CFG_REG, .min_u = 0, - .max_u = 4800000, + .max_u = 6000000, .step_u = 25000, }, - .icl_stat = { + .icl_stat = { .name = "input current limit status", .reg = ICL_STATUS_REG, .min_u = 0, .max_u = 4800000, .step_u = 25000, }, - .dc_icl = { + .dc_icl = { .name = "dc input current limit", .reg = DCIN_CURRENT_LIMIT_CFG_REG, .min_u = 0, + .max_u = 6000000, + .step_u = 25000, + }, + .dc_icl_pt_lv = { + .name = "dc icl PT <8V", + .reg = ZIN_ICL_PT_REG, + .min_u = 0, + .max_u = 3000000, + .step_u = 25000, + }, + .dc_icl_pt_hv = { + .name = "dc icl PT >8V", + .reg = ZIN_ICL_PT_HV_REG, + .min_u = 0, + .max_u = 3000000, + .step_u = 25000, + }, + .dc_icl_div2_lv = { + .name = "dc icl div2 <5.5V", + .reg = ZIN_ICL_LV_REG, + .min_u = 0, + .max_u = 3000000, + .step_u = 25000, + }, + .dc_icl_div2_mid_lv = { + .name = "dc icl div2 5.5-6.5V", + .reg = ZIN_ICL_MID_LV_REG, + .min_u = 0, + .max_u = 3000000, + .step_u = 25000, + }, + .dc_icl_div2_mid_hv = { + .name = "dc icl div2 6.5-8.0V", + .reg = ZIN_ICL_MID_HV_REG, + .min_u = 0, + .max_u = 3000000, + .step_u = 25000, + }, + .dc_icl_div2_hv = { + .name = "dc icl div2 >8.0V", + .reg = ZIN_ICL_HV_REG, + .min_u = 0, .max_u = 3000000, .step_u = 25000, }, + .jeita_cc_comp = { + .name = "jeita fcc reduction", + .reg = JEITA_CCCOMP_CFG_REG, + .min_u = 0, + .max_u = 1575000, + .step_u = 25000, + }, }; struct smb_dt_props { - bool suspend_input; - int fcc_ua; - int usb_icl_ua; - int dc_icl_ua; - int fv_uv; + bool suspend_input; + int fcc_ua; + int usb_icl_ua; + int dc_icl_ua; + int fv_uv; + int wipower_max_uw; }; struct smb2 { @@ -95,7 +146,7 @@ static int smb2_parse_dt(struct smb2 *chip) { struct smb_charger *chg = &chip->chg; struct device_node *node = chg->dev->of_node; - int rc; + int rc, byte_len; if (!node) { pr_err("device tree node missing\n"); @@ -125,6 +176,30 @@ static int smb2_parse_dt(struct smb2 *chip) if (rc < 0) chip->dt.dc_icl_ua = SMB2_DEFAULT_ICL_UA; + rc = of_property_read_u32(node, + "qcom,wipower-max-uw", &chip->dt.wipower_max_uw); + if (rc < 0) + chip->dt.wipower_max_uw = SMB2_DEFAULT_WPWR_UW; + + if (of_find_property(node, "qcom,thermal-mitigation", &byte_len)) { + chg->thermal_mitigation = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation", + chg->thermal_mitigation, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + return 0; } @@ -294,6 +369,105 @@ static int smb2_init_usb_psy(struct smb2 *chip) } /************************* + * DC PSY REGISTRATION * + *************************/ + +static enum power_supply_property smb2_dc_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CURRENT_MAX, +}; + +static int smb2_dc_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct smb2 *chip = power_supply_get_drvdata(psy); + struct smb_charger *chg = &chip->chg; + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + rc = smblib_get_prop_dc_present(chg, val); + break; + case POWER_SUPPLY_PROP_ONLINE: + rc = smblib_get_prop_dc_online(chg, val); + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + rc = smblib_get_prop_dc_current_max(chg, val); + break; + default: + return -EINVAL; + } + + return rc; +} + +static int smb2_dc_set_prop(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct smb2 *chip = power_supply_get_drvdata(psy); + struct smb_charger *chg = &chip->chg; + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_CURRENT_MAX: + rc = smblib_set_prop_dc_current_max(chg, val); + break; + default: + return -EINVAL; + } + + return rc; +} + +static int smb2_dc_prop_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + int rc; + + switch (psp) { + case POWER_SUPPLY_PROP_CURRENT_MAX: + rc = 1; + break; + default: + rc = 0; + break; + } + + return rc; +} + +static const struct power_supply_desc dc_psy_desc = { + .name = "dc", + .type = POWER_SUPPLY_TYPE_WIPOWER, + .properties = smb2_dc_props, + .num_properties = ARRAY_SIZE(smb2_dc_props), + .get_property = smb2_dc_get_prop, + .set_property = smb2_dc_set_prop, + .property_is_writeable = smb2_dc_prop_is_writeable, +}; + +static int smb2_init_dc_psy(struct smb2 *chip) +{ + struct power_supply_config dc_cfg = {}; + struct smb_charger *chg = &chip->chg; + + dc_cfg.drv_data = chip; + dc_cfg.of_node = chg->dev->of_node; + chg->dc_psy = devm_power_supply_register(chg->dev, + &dc_psy_desc, + &dc_cfg); + if (IS_ERR(chg->dc_psy)) { + pr_err("Couldn't register USB power supply\n"); + return PTR_ERR(chg->dc_psy); + } + + return 0; +} + +/************************* * BATT PSY REGISTRATION * *************************/ @@ -304,6 +478,7 @@ static enum power_supply_property smb2_batt_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, }; static int smb2_batt_get_prop(struct power_supply *psy, @@ -331,9 +506,11 @@ static int smb2_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CAPACITY: smblib_get_prop_batt_capacity(chg, val); break; + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + smblib_get_prop_system_temp_level(chg, val); + break; default: - pr_err("batt power supply prop %d not supported\n", - psp); + pr_err("batt power supply prop %d not supported\n", psp); return -EINVAL; } @@ -344,17 +521,21 @@ static int smb2_batt_set_prop(struct power_supply *psy, enum power_supply_property prop, const union power_supply_propval *val) { + int rc = 0; struct smb_charger *chg = power_supply_get_drvdata(psy); switch (prop) { case POWER_SUPPLY_PROP_INPUT_SUSPEND: - smblib_set_prop_input_suspend(chg, val); + rc = smblib_set_prop_input_suspend(chg, val); + break; + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + rc = smblib_set_prop_system_temp_level(chg, val); break; default: - return -EINVAL; + rc = -EINVAL; } - return 0; + return rc; } static int smb2_batt_prop_is_writeable(struct power_supply *psy, @@ -362,6 +543,7 @@ static int smb2_batt_prop_is_writeable(struct power_supply *psy, { switch (psp) { case POWER_SUPPLY_PROP_INPUT_SUSPEND: + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: return 1; default: break; @@ -486,6 +668,57 @@ static int smb2_init_vconn_regulator(struct smb2 *chip) /*************************** * HARDWARE INITIALIZATION * ***************************/ +static int smb2_config_wipower_input_power(struct smb2 *chip, int uw) +{ + int rc; + int ua; + struct smb_charger *chg = &chip->chg; + s64 nw = (s64)uw * 1000; + + ua = div_s64(nw, ZIN_ICL_PT_MAX_MV); + rc = smblib_set_charge_param(chg, &chg->param.dc_icl_pt_lv, ua); + if (rc < 0) { + pr_err("Couldn't configure dc_icl_pt_lv rc = %d\n", rc); + return rc; + } + + ua = div_s64(nw, ZIN_ICL_PT_HV_MAX_MV); + rc = smblib_set_charge_param(chg, &chg->param.dc_icl_pt_hv, ua); + if (rc < 0) { + pr_err("Couldn't configure dc_icl_pt_hv rc = %d\n", rc); + return rc; + } + + ua = div_s64(nw, ZIN_ICL_LV_MAX_MV); + rc = smblib_set_charge_param(chg, &chg->param.dc_icl_div2_lv, ua); + if (rc < 0) { + pr_err("Couldn't configure dc_icl_div2_lv rc = %d\n", rc); + return rc; + } + + ua = div_s64(nw, ZIN_ICL_MID_LV_MAX_MV); + rc = smblib_set_charge_param(chg, &chg->param.dc_icl_div2_mid_lv, ua); + if (rc < 0) { + pr_err("Couldn't configure dc_icl_div2_mid_lv rc = %d\n", rc); + return rc; + } + + ua = div_s64(nw, ZIN_ICL_MID_HV_MAX_MV); + rc = smblib_set_charge_param(chg, &chg->param.dc_icl_div2_mid_hv, ua); + if (rc < 0) { + pr_err("Couldn't configure dc_icl_div2_mid_hv rc = %d\n", rc); + return rc; + } + + ua = div_s64(nw, ZIN_ICL_HV_MAX_MV); + rc = smblib_set_charge_param(chg, &chg->param.dc_icl_div2_hv, ua); + if (rc < 0) { + pr_err("Couldn't configure dc_icl_div2_hv rc = %d\n", rc); + return rc; + } + + return 0; +} static int smb2_init_hw(struct smb2 *chip) { @@ -501,7 +734,7 @@ static int smb2_init_hw(struct smb2 *chip) DEFAULT_VOTER, chip->dt.suspend_input, 0); vote(chg->dc_suspend_votable, DEFAULT_VOTER, chip->dt.suspend_input, 0); - vote(chg->fcc_votable, + vote(chg->fcc_max_votable, DEFAULT_VOTER, true, chip->dt.fcc_ua); vote(chg->fv_votable, DEFAULT_VOTER, true, chip->dt.fv_uv); @@ -510,17 +743,21 @@ static int smb2_init_hw(struct smb2 *chip) vote(chg->dc_icl_votable, DEFAULT_VOTER, true, chip->dt.dc_icl_ua); - /* configure charge enable for software control; active high */ + /* + * Configure charge enable for software control; active high, and end + * the charge cycle while the battery is OV. + */ rc = smblib_masked_write(chg, CHGR_CFG2_REG, - CHG_EN_POLARITY_BIT | CHG_EN_SRC_BIT, 0); + CHG_EN_POLARITY_BIT | + CHG_EN_SRC_BIT | + BAT_OV_ECC_BIT, BAT_OV_ECC_BIT); if (rc < 0) { - dev_err(chg->dev, - "Couldn't configure charge enable source rc=%d\n", rc); + dev_err(chg->dev, "Couldn't configure charger rc=%d\n", rc); return rc; } /* enable the charging path */ - rc = smblib_enable_charging(chg, true); + rc = vote(chg->chg_disable_votable, DEFAULT_VOTER, false, 0); if (rc < 0) { dev_err(chg->dev, "Couldn't enable charging rc=%d\n", rc); return rc; @@ -528,11 +765,10 @@ static int smb2_init_hw(struct smb2 *chip) /* * trigger the usb-typec-change interrupt only when the CC state - * changes, or there was a VBUS error + * changes */ rc = smblib_write(chg, TYPE_C_INTRPT_ENB_REG, - TYPEC_CCSTATE_CHANGE_INT_EN_BIT - | TYPEC_VBUS_ERROR_INT_EN_BIT); + TYPEC_CCSTATE_CHANGE_INT_EN_BIT); if (rc < 0) { dev_err(chg->dev, "Couldn't configure Type-C interrupts rc=%d\n", rc); @@ -582,6 +818,13 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } + /* configure wipower watts */ + rc = smb2_config_wipower_input_power(chip, chip->dt.wipower_max_uw); + if (rc < 0) { + dev_err(chg->dev, "Couldn't configure wipower rc=%d\n", rc); + return rc; + } + return rc; } @@ -615,49 +858,49 @@ struct smb2_irq_info { static struct smb2_irq_info smb2_irqs[] = { /* CHARGER IRQs */ - { "chg-error", smblib_handle_debug }, - { "chg-state-change", smblib_handle_chg_state_change, true }, - { "step-chg-state-change", smblib_handle_debug }, - { "step-chg-soc-update-fail", smblib_handle_debug }, + { "chg-error", smblib_handle_debug }, + { "chg-state-change", smblib_handle_chg_state_change, true }, + { "step-chg-state-change", smblib_handle_debug }, + { "step-chg-soc-update-fail", smblib_handle_debug }, { "step-chg-soc-update-request", smblib_handle_debug }, /* OTG IRQs */ - { "otg-fail", smblib_handle_debug }, - { "otg-overcurrent", smblib_handle_debug }, - { "otg-oc-dis-sw-sts", smblib_handle_debug }, - { "testmode-change-detect", smblib_handle_debug }, + { "otg-fail", smblib_handle_debug }, + { "otg-overcurrent", smblib_handle_debug }, + { "otg-oc-dis-sw-sts", smblib_handle_debug }, + { "testmode-change-detect", smblib_handle_debug }, /* BATTERY IRQs */ - { "bat-temp", smblib_handle_batt_psy_changed }, - { "bat-ocp", smblib_handle_batt_psy_changed }, - { "bat-ov", smblib_handle_batt_psy_changed }, - { "bat-low", smblib_handle_batt_psy_changed }, - { "bat-therm-or-id-missing", smblib_handle_batt_psy_changed }, - { "bat-terminal-missing", smblib_handle_batt_psy_changed }, + { "bat-temp", smblib_handle_batt_temp_changed }, + { "bat-ocp", smblib_handle_batt_psy_changed }, + { "bat-ov", smblib_handle_batt_psy_changed }, + { "bat-low", smblib_handle_batt_psy_changed }, + { "bat-therm-or-id-missing", smblib_handle_batt_psy_changed }, + { "bat-terminal-missing", smblib_handle_batt_psy_changed }, /* USB INPUT IRQs */ - { "usbin-collapse", smblib_handle_debug }, - { "usbin-lt-3p6v", smblib_handle_debug }, - { "usbin-uv", smblib_handle_debug }, - { "usbin-ov", smblib_handle_debug }, - { "usbin-plugin", smblib_handle_usb_plugin, true }, - { "usbin-src-change", smblib_handle_usb_source_change, true }, - { "usbin-icl-change", smblib_handle_icl_change, true }, + { "usbin-collapse", smblib_handle_debug }, + { "usbin-lt-3p6v", smblib_handle_debug }, + { "usbin-uv", smblib_handle_debug }, + { "usbin-ov", smblib_handle_debug }, + { "usbin-plugin", smblib_handle_usb_plugin, true }, + { "usbin-src-change", smblib_handle_usb_source_change, true }, + { "usbin-icl-change", smblib_handle_icl_change, true }, { "type-c-change", smblib_handle_usb_typec_change, true }, /* DC INPUT IRQs */ - { "dcin-collapse", smblib_handle_debug }, - { "dcin-lt-3p6v", smblib_handle_debug }, - { "dcin-uv", smblib_handle_debug }, - { "dcin-ov", smblib_handle_debug }, - { "dcin-plugin", smblib_handle_debug }, - { "div2-en-dg", smblib_handle_debug }, - { "dcin-icl-change", smblib_handle_debug }, + { "dcin-collapse", smblib_handle_debug }, + { "dcin-lt-3p6v", smblib_handle_debug }, + { "dcin-uv", smblib_handle_debug }, + { "dcin-ov", smblib_handle_debug }, + { "dcin-plugin", smblib_handle_debug }, + { "div2-en-dg", smblib_handle_debug }, + { "dcin-icl-change", smblib_handle_debug }, /* MISCELLANEOUS IRQs */ - { "wdog-snarl", NULL }, - { "wdog-bark", NULL }, - { "aicl-fail", smblib_handle_debug }, - { "aicl-done", smblib_handle_debug }, - { "high-duty-cycle", smblib_handle_debug }, - { "input-current-limiting", smblib_handle_debug }, - { "temperature-change", smblib_handle_debug }, - { "switcher-power-ok", smblib_handle_debug }, + { "wdog-snarl", NULL }, + { "wdog-bark", NULL }, + { "aicl-fail", smblib_handle_debug }, + { "aicl-done", smblib_handle_debug }, + { "high-duty-cycle", smblib_handle_debug }, + { "input-current-limiting", smblib_handle_debug }, + { "temperature-change", smblib_handle_debug }, + { "switcher-power-ok", smblib_handle_debug }, }; static int smb2_get_irq_index_byname(const char *irq_name) @@ -790,6 +1033,12 @@ static int smb2_probe(struct platform_device *pdev) goto cleanup; } + rc = smb2_init_dc_psy(chip); + if (rc < 0) { + pr_err("Couldn't initialize dc psy rc=%d\n", rc); + goto cleanup; + } + rc = smb2_init_usb_psy(chip); if (rc < 0) { pr_err("Couldn't initialize usb psy rc=%d\n", rc); diff --git a/drivers/power/qcom-charger/qpnp-smbcharger.c b/drivers/power/qcom-charger/qpnp-smbcharger.c index 2536f4ec5c15..6c1e58d046e8 100644 --- a/drivers/power/qcom-charger/qpnp-smbcharger.c +++ b/drivers/power/qcom-charger/qpnp-smbcharger.c @@ -3507,19 +3507,27 @@ static int smbchg_config_chg_battery_type(struct smbchg_chip *chip) if (chip->battery_type && !strcmp(prop.strval, chip->battery_type)) return 0; + chip->battery_type = prop.strval; batt_node = of_parse_phandle(node, "qcom,battery-data", 0); if (!batt_node) { pr_smb(PR_MISC, "No batterydata available\n"); return 0; } + rc = power_supply_get_property(chip->bms_psy, + POWER_SUPPLY_PROP_RESISTANCE_ID, &prop); + if (rc < 0) { + pr_smb(PR_STATUS, "Unable to read battery-id rc=%d\n", rc); + return 0; + } + profile_node = of_batterydata_get_best_profile(batt_node, - "bms", NULL); - if (!profile_node) { - pr_err("couldn't find profile handle\n"); - return -EINVAL; + prop.intval / 1000, NULL); + if (IS_ERR_OR_NULL(profile_node)) { + rc = PTR_ERR(profile_node); + pr_err("couldn't find profile handle %d\n", rc); + return rc; } - chip->battery_type = prop.strval; /* change vfloat */ rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv", diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c index 55bcc9ec443e..8fe882e078f0 100644 --- a/drivers/power/qcom-charger/smb-lib.c +++ b/drivers/power/qcom-charger/smb-lib.c @@ -30,7 +30,7 @@ static bool is_secure(struct smb_charger *chg, int addr) { /* assume everything above 0xC0 is secure */ - return (bool)(addr >= 0xC0); + return (bool)((addr & 0xFF) >= 0xC0); } int smblib_read(struct smb_charger *chg, u16 addr, u8 *val) @@ -85,10 +85,36 @@ unlock: static void smblib_fcc_split_ua(struct smb_charger *chg, int total_fcc, int *master_ua, int *slave_ua) { + int rc, cc_reduction_ua = 0; int master_percent = min(max(*chg->pl.master_percent, 0), 100); + union power_supply_propval pval = {0, }; + + /* + * if master_percent is 0, s/w will configure master's fcc to zero and + * slave's fcc to the max. However since master's fcc is zero it + * disables its own charging and as a result the slave's charging is + * disabled via the fault line. + */ + + rc = smblib_get_prop_batt_health(chg, &pval); + if (rc == 0) { + if (pval.intval == POWER_SUPPLY_HEALTH_WARM + || pval.intval == POWER_SUPPLY_HEALTH_COOL) { + rc = smblib_get_charge_param(chg, + &chg->param.jeita_cc_comp, + &cc_reduction_ua); + if (rc < 0) { + dev_err(chg->dev, "Could not get jeita comp, rc=%d\n", + rc); + cc_reduction_ua = 0; + } + } + } + total_fcc = max(0, total_fcc - cc_reduction_ua); *master_ua = (total_fcc * master_percent) / 100; *slave_ua = (total_fcc - *master_ua) * chg->pl.taper_percent / 100; + *master_ua += cc_reduction_ua; } /******************** @@ -184,22 +210,6 @@ static const struct apsd_result *smblib_get_apsd_result(struct smb_charger *chg) * REGISTER SETTERS * ********************/ -int smblib_enable_charging(struct smb_charger *chg, bool enable) -{ - int rc = 0; - - rc = smblib_masked_write(chg, CHARGING_ENABLE_CMD_REG, - CHARGING_ENABLE_CMD_BIT, - enable ? CHARGING_ENABLE_CMD_BIT : 0); - if (rc < 0) { - dev_err(chg->dev, "Couldn't %s charging rc=%d\n", - enable ? "enable" : "disable", rc); - return rc; - } - - return rc; -} - int smblib_set_charge_param(struct smb_charger *chg, struct smb_chg_param *param, int val_u) { @@ -390,6 +400,14 @@ static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data, return smblib_set_dc_suspend(chg, suspend); } +static int smblib_fcc_max_vote_callback(struct votable *votable, void *data, + int fcc_ua, const char *client) +{ + struct smb_charger *chg = data; + + return vote(chg->fcc_votable, FCC_MAX_RESULT, true, fcc_ua); +} + static int smblib_fcc_vote_callback(struct votable *votable, void *data, int fcc_ua, const char *client) { @@ -580,6 +598,23 @@ static int smblib_pl_disable_vote_callback(struct votable *votable, void *data, return 0; } +static int smblib_chg_disable_vote_callback(struct votable *votable, void *data, + int chg_disable, const char *client) +{ + struct smb_charger *chg = data; + int rc; + + rc = smblib_masked_write(chg, CHARGING_ENABLE_CMD_REG, + CHARGING_ENABLE_CMD_BIT, + chg_disable ? 0 : CHARGING_ENABLE_CMD_BIT); + if (rc < 0) { + dev_err(chg->dev, "Couldn't %s charging rc=%d\n", + chg_disable ? "disable" : "enable", rc); + return rc; + } + + return 0; +} /***************** * OTG REGULATOR * *****************/ @@ -823,6 +858,13 @@ done: return rc; } +int smblib_get_prop_system_temp_level(struct smb_charger *chg, + union power_supply_propval *val) +{ + val->intval = chg->system_temp_level; + return 0; +} + /*********************** * BATTERY PSY SETTERS * ***********************/ @@ -846,6 +888,102 @@ int smblib_set_prop_input_suspend(struct smb_charger *chg, return rc; } + power_supply_changed(chg->batt_psy); + return rc; +} + +int smblib_set_prop_system_temp_level(struct smb_charger *chg, + const union power_supply_propval *val) +{ + if (val->intval < 0) + return -EINVAL; + + if (chg->thermal_levels <= 0) + return -EINVAL; + + if (val->intval > chg->thermal_levels) + return -EINVAL; + + chg->system_temp_level = val->intval; + if (chg->system_temp_level == chg->thermal_levels) + return vote(chg->chg_disable_votable, THERMAL_DAEMON, true, 0); + + vote(chg->chg_disable_votable, THERMAL_DAEMON, false, 0); + if (chg->system_temp_level == 0) + return vote(chg->fcc_votable, THERMAL_DAEMON, false, 0); + + vote(chg->fcc_votable, THERMAL_DAEMON, true, + chg->thermal_mitigation[chg->system_temp_level]); + return 0; +} + +/******************* + * DC PSY GETTERS * + *******************/ + +int smblib_get_prop_dc_present(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc = 0; + u8 stat; + + rc = smblib_read(chg, DC_INT_RT_STS_REG, &stat); + if (rc < 0) { + dev_err(chg->dev, "Couldn't read DC_INT_RT_STS_REG rc=%d\n", + rc); + return rc; + } + smblib_dbg(chg, PR_REGISTER, "DC_INT_RT_STS_REG = 0x%02x\n", + stat); + + val->intval = (bool)(stat & DCIN_PLUGIN_RT_STS_BIT); + + return rc; +} + +int smblib_get_prop_dc_online(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc = 0; + u8 stat; + + if (get_client_vote(chg->dc_suspend_votable, USER_VOTER)) { + val->intval = false; + return rc; + } + + rc = smblib_read(chg, POWER_PATH_STATUS_REG, &stat); + if (rc < 0) { + dev_err(chg->dev, "Couldn't read POWER_PATH_STATUS rc=%d\n", + rc); + return rc; + } + smblib_dbg(chg, PR_REGISTER, "POWER_PATH_STATUS = 0x%02x\n", + stat); + + val->intval = (stat & USE_DCIN_BIT) && + (stat & VALID_INPUT_POWER_SOURCE_BIT); + + return rc; +} + +int smblib_get_prop_dc_current_max(struct smb_charger *chg, + union power_supply_propval *val) +{ + val->intval = get_effective_result_locked(chg->dc_icl_votable); + return 0; +} + +/******************* + * USB PSY SETTERS * + * *****************/ + +int smblib_set_prop_dc_current_max(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc; + + rc = vote(chg->dc_icl_votable, USER_VOTER, true, val->intval); return rc; } @@ -1253,6 +1391,16 @@ irqreturn_t smblib_handle_chg_state_change(int irq, void *data) return IRQ_HANDLED; } +irqreturn_t smblib_handle_batt_temp_changed(int irq, void *data) +{ + struct smb_irq_data *irq_data = data; + struct smb_charger *chg = irq_data->parent_data; + + rerun_election(chg->fcc_votable); + power_supply_changed(chg->batt_psy); + return IRQ_HANDLED; +} + irqreturn_t smblib_handle_batt_psy_changed(int irq, void *data) { struct smb_irq_data *irq_data = data; @@ -1280,15 +1428,6 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) int rc; u8 stat; - rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); - if (rc < 0) { - dev_err(chg->dev, "Couldn't read USB_INT_RT_STS rc=%d\n", - rc); - return IRQ_HANDLED; - } - - chg->vbus_present = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); - /* fetch the DPDM regulator */ if (!chg->dpdm_reg && of_get_property(chg->dev->of_node, "dpdm-supply", NULL)) { @@ -1303,18 +1442,30 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) if (!chg->dpdm_reg) goto skip_dpdm_float; - if (chg->vbus_present && !regulator_is_enabled(chg->dpdm_reg)) { - smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n"); - rc = regulator_enable(chg->dpdm_reg); - if (rc < 0) - dev_err(chg->dev, "Couldn't enable dpdm regulator rc=%d\n", - rc); - } else if (regulator_is_enabled(chg->dpdm_reg)) { - smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n"); - rc = regulator_disable(chg->dpdm_reg); - if (rc < 0) - dev_err(chg->dev, "Couldn't disable dpdm regulator rc=%d\n", - rc); + rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); + if (rc < 0) { + dev_err(chg->dev, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); + return IRQ_HANDLED; + } + + chg->vbus_present = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); + + if (chg->vbus_present) { + if (!regulator_is_enabled(chg->dpdm_reg)) { + smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n"); + rc = regulator_enable(chg->dpdm_reg); + if (rc < 0) + dev_err(chg->dev, "Couldn't enable dpdm regulator rc=%d\n", + rc); + } + } else { + if (regulator_is_enabled(chg->dpdm_reg)) { + smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n"); + rc = regulator_disable(chg->dpdm_reg); + if (rc < 0) + dev_err(chg->dev, "Couldn't disable dpdm regulator rc=%d\n", + rc); + } } skip_dpdm_float: @@ -1654,7 +1805,15 @@ int smblib_create_votables(struct smb_charger *chg) return rc; } - chg->fcc_votable = create_votable("FCC", VOTE_MAX, + chg->fcc_max_votable = create_votable("FCC_MAX", VOTE_MAX, + smblib_fcc_max_vote_callback, + chg); + if (IS_ERR(chg->fcc_max_votable)) { + rc = PTR_ERR(chg->fcc_max_votable); + return rc; + } + + chg->fcc_votable = create_votable("FCC", VOTE_MIN, smblib_fcc_vote_callback, chg); if (IS_ERR(chg->fcc_votable)) { @@ -1709,6 +1868,14 @@ int smblib_create_votables(struct smb_charger *chg) return rc; } + chg->chg_disable_votable = create_votable("CHG_DISABLE", VOTE_SET_ANY, + smblib_chg_disable_vote_callback, + chg); + if (IS_ERR(chg->chg_disable_votable)) { + rc = PTR_ERR(chg->chg_disable_votable); + return rc; + } + return rc; } @@ -1756,6 +1923,7 @@ int smblib_deinit(struct smb_charger *chg) { destroy_votable(chg->usb_suspend_votable); destroy_votable(chg->dc_suspend_votable); + destroy_votable(chg->fcc_max_votable); destroy_votable(chg->fcc_votable); destroy_votable(chg->fv_votable); destroy_votable(chg->usb_icl_votable); diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h index 8b3d00b6a5c1..1521fdb3fccf 100644 --- a/drivers/power/qcom-charger/smb-lib.h +++ b/drivers/power/qcom-charger/smb-lib.h @@ -31,6 +31,8 @@ enum print_reason { #define CHG_STATE_VOTER "CHG_STATE_VOTER" #define TYPEC_SRC_VOTER "TYPEC_SRC_VOTER" #define TAPER_END_VOTER "TAPER_END_VOTER" +#define FCC_MAX_RESULT "FCC_MAX_RESULT" +#define THERMAL_DAEMON "THERMAL_DAEMON" enum smb_mode { PARALLEL_MASTER = 0, @@ -62,6 +64,13 @@ struct smb_params { struct smb_chg_param usb_icl; struct smb_chg_param icl_stat; struct smb_chg_param dc_icl; + struct smb_chg_param dc_icl_pt_lv; + struct smb_chg_param dc_icl_pt_hv; + struct smb_chg_param dc_icl_div2_lv; + struct smb_chg_param dc_icl_div2_mid_lv; + struct smb_chg_param dc_icl_div2_mid_hv; + struct smb_chg_param dc_icl_div2_hv; + struct smb_chg_param jeita_cc_comp; }; struct parallel_params { @@ -86,6 +95,7 @@ struct smb_charger { /* power supplies */ struct power_supply *batt_psy; struct power_supply *usb_psy; + struct power_supply *dc_psy; struct power_supply_desc usb_psy_desc; /* parallel charging */ @@ -99,6 +109,7 @@ struct smb_charger { /* votables */ struct votable *usb_suspend_votable; struct votable *dc_suspend_votable; + struct votable *fcc_max_votable; struct votable *fcc_votable; struct votable *fv_votable; struct votable *usb_icl_votable; @@ -106,6 +117,7 @@ struct smb_charger { struct votable *pd_allowed_votable; struct votable *awake_votable; struct votable *pl_disable_votable; + struct votable *chg_disable_votable; /* work */ struct work_struct pl_detect_work; @@ -118,6 +130,10 @@ struct smb_charger { int voltage_max_uv; bool pd_active; bool vbus_present; + + int system_temp_level; + int thermal_levels; + int *thermal_mitigation; }; int smblib_read(struct smb_charger *chg, u16 addr, u8 *val); @@ -144,6 +160,7 @@ int smblib_vconn_regulator_is_enabled(struct regulator_dev *rdev); irqreturn_t smblib_handle_debug(int irq, void *data); irqreturn_t smblib_handle_chg_state_change(int irq, void *data); +irqreturn_t smblib_handle_batt_temp_changed(int irq, void *data); irqreturn_t smblib_handle_batt_psy_changed(int irq, void *data); irqreturn_t smblib_handle_usb_psy_changed(int irq, void *data); irqreturn_t smblib_handle_usb_plugin(int irq, void *data); @@ -163,8 +180,22 @@ int smblib_get_prop_batt_charge_type(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_batt_health(struct smb_charger *chg, union power_supply_propval *val); +int smblib_get_prop_system_temp_level(struct smb_charger *chg, + union power_supply_propval *val); + int smblib_set_prop_input_suspend(struct smb_charger *chg, const union power_supply_propval *val); +int smblib_set_prop_system_temp_level(struct smb_charger *chg, + const union power_supply_propval *val); + +int smblib_get_prop_dc_present(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_get_prop_dc_online(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_get_prop_dc_current_max(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_set_prop_dc_current_max(struct smb_charger *chg, + const union power_supply_propval *val); int smblib_get_prop_usb_present(struct smb_charger *chg, union power_supply_propval *val); diff --git a/drivers/power/qcom-charger/smb-reg.h b/drivers/power/qcom-charger/smb-reg.h index f63e983c595c..b03e8a7e0403 100644 --- a/drivers/power/qcom-charger/smb-reg.h +++ b/drivers/power/qcom-charger/smb-reg.h @@ -641,6 +641,9 @@ enum { #define WIPWR_RANGE_STATUS_REG (DCIN_BASE + 0x08) #define WIPWR_RANGE_STATUS_MASK GENMASK(4, 0) +#define DC_INT_RT_STS_REG (DCIN_BASE + 0x10) +#define DCIN_PLUGIN_RT_STS_BIT BIT(4) + /* DCIN Interrupt Bits */ #define WIPWR_VOLTAGE_RANGE_RT_STS_BIT BIT(7) #define DCIN_ICL_CHANGE_RT_STS_BIT BIT(6) @@ -719,6 +722,15 @@ enum { #define ZIN_ICL_MID_HV_REG (DCIN_BASE + 0x98) #define ZIN_ICL_MID_HV_MASK GENMASK(7, 0) +enum { + ZIN_ICL_PT_MAX_MV = 8000, + ZIN_ICL_PT_HV_MAX_MV = 9000, + ZIN_ICL_LV_MAX_MV = 5500, + ZIN_ICL_MID_LV_MAX_MV = 6500, + ZIN_ICL_MID_HV_MAX_MV = 8000, + ZIN_ICL_HV_MAX_MV = 11000, +}; + /* MISC Peripheral Registers */ #define REVISION1_REG (MISC_BASE + 0x00) #define DIG_MINOR_MASK GENMASK(7, 0) diff --git a/drivers/power/qcom-charger/smb138x-charger.c b/drivers/power/qcom-charger/smb138x-charger.c index 9a6baff27dac..11d936762e3c 100644 --- a/drivers/power/qcom-charger/smb138x-charger.c +++ b/drivers/power/qcom-charger/smb138x-charger.c @@ -564,7 +564,7 @@ static int smb138x_init_hw(struct smb138x *chip) } /* enable the charging path */ - rc = smblib_enable_charging(chg, true); + rc = vote(chg->chg_disable_votable, DEFAULT_VOTER, false, 0); if (rc < 0) { dev_err(chg->dev, "Couldn't enable charging rc=%d\n", rc); return rc; @@ -857,7 +857,9 @@ static int smb138x_slave_probe(struct smb138x *chip) } /* enable the charging path */ - rc = smblib_enable_charging(chg, true); + rc = smblib_masked_write(chg, CHARGING_ENABLE_CMD_REG, + CHARGING_ENABLE_CMD_BIT, + CHARGING_ENABLE_CMD_BIT); if (rc < 0) { dev_err(chg->dev, "Couldn't enable charging rc=%d\n", rc); return rc; diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index eb515721dfde..d49d8606da15 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -134,24 +134,24 @@ static bool have_full_constraints(void) return has_full_constraints || of_have_populated_dt(); } +static inline struct regulator_dev *rdev_get_supply(struct regulator_dev *rdev) +{ + if (rdev && rdev->supply) + return rdev->supply->rdev; + + return NULL; +} + /** * regulator_lock_supply - lock a regulator and its supplies * @rdev: regulator source */ static void regulator_lock_supply(struct regulator_dev *rdev) { - struct regulator *supply; - int i = 0; - - while (1) { - mutex_lock_nested(&rdev->mutex, i++); - supply = rdev->supply; - - if (!rdev->supply) - return; + int i; - rdev = supply->rdev; - } + for (i = 0; rdev->supply; rdev = rdev_get_supply(rdev), i++) + mutex_lock_nested(&rdev->mutex, i); } /** diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c index c13e811a5d71..78c5c47e4e8b 100644 --- a/drivers/regulator/cpr3-regulator.c +++ b/drivers/regulator/cpr3-regulator.c @@ -1109,10 +1109,18 @@ static int cpr3_regulator_init_cprh_corners(struct cpr3_regulator *vreg) } if (ro_sel == INT_MAX) { - cpr3_err(vreg, "corner=%d has invalid RO select value\n", - i); - rc = -EINVAL; - goto free_base_quots; + if (!corner->proc_freq) { + /* + * Corner is not used as active DCVS set point + * select RO 0 arbitrarily. + */ + ro_sel = 0; + } else { + cpr3_err(vreg, "corner=%d has invalid RO select value\n", + i); + rc = -EINVAL; + goto free_base_quots; + } } open_loop_volt_steps = DIV_ROUND_UP(corner->open_loop_volt - @@ -1121,9 +1129,11 @@ static int cpr3_regulator_init_cprh_corners(struct cpr3_regulator *vreg) floor_volt_steps = DIV_ROUND_UP(corner->floor_volt - ctrl->base_volt, ctrl->step_volt); - delta_quot_steps = DIV_ROUND_UP(corner->target_quot[ro_sel] - - base_quots[ro_sel], - CPRH_DELTA_QUOT_STEP_FACTOR); + delta_quot_steps = corner->proc_freq ? + DIV_ROUND_UP(corner->target_quot[ro_sel] - + base_quots[ro_sel], + CPRH_DELTA_QUOT_STEP_FACTOR) : + 0; if (open_loop_volt_steps > CPRH_CORNER_INIT_VOLTAGE_MAX_VALUE || floor_volt_steps > CPRH_CORNER_FLOOR_VOLTAGE_MAX_VALUE || diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h index d750b70519d1..3ddc1dc3c982 100644 --- a/drivers/regulator/cpr3-regulator.h +++ b/drivers/regulator/cpr3-regulator.h @@ -572,7 +572,13 @@ struct cpr3_panic_regs_info { * when hardware closed-loop attempts to exceed the ceiling * voltage * @apm: Handle to the array power mux (APM) - * @apm_threshold_volt: APM threshold voltage in microvolts + * @apm_threshold_volt: Voltage in microvolts which defines the threshold + * voltage to determine the APM supply selection for + * each corner + * @apm_crossover_volt: Voltage in microvolts corresponding to the voltage that + * the VDD supply must be set to while an APM switch is in + * progress. This element must be initialized for CPRh + * controllers when an APM threshold voltage is defined * @apm_adj_volt: Minimum difference between APM threshold voltage and * open-loop voltage which allows the APM threshold voltage * to be used as a ceiling @@ -736,6 +742,7 @@ struct cpr3_controller { int ceiling_irq; struct msm_apm_ctrl_dev *apm; int apm_threshold_volt; + int apm_crossover_volt; int apm_adj_volt; enum msm_apm_supply apm_high_supply; enum msm_apm_supply apm_low_supply; diff --git a/drivers/regulator/cprh-kbss-regulator.c b/drivers/regulator/cprh-kbss-regulator.c index ffd3db1a6dff..dfdd6921fed5 100644 --- a/drivers/regulator/cprh-kbss-regulator.c +++ b/drivers/regulator/cprh-kbss-regulator.c @@ -697,61 +697,38 @@ free_temp: } /** - * cprh_kbss_apm_threshold_as_corner() - introduce a corner whose floor, open-loop, - * and ceiling voltages correspond to the APM threshold voltage. + * cprh_kbss_apm_crossover_as_corner() - introduce a corner whose floor, + * open-loop, and ceiling voltages correspond to the APM + * crossover voltage. * @vreg: Pointer to the CPR3 regulator * * The APM corner is utilized as a crossover corner by OSM and CPRh - * hardware to determine the correct APM supply selection for the - * rest of the corners. This function must be called after all other - * functions which load per-corner values. + * hardware to set the VDD supply voltage during the APM switch + * routine. * * Return: 0 on success, errno on failure */ -static int cprh_kbss_apm_threshold_as_corner(struct cpr3_regulator *vreg) +static int cprh_kbss_apm_crossover_as_corner(struct cpr3_regulator *vreg) { struct cpr3_controller *ctrl = vreg->thread->ctrl; struct cpr3_corner *corner; - struct cprh_corner_band *corner_band; - int i, threshold, apm_corner = 0; - if (!ctrl->apm_threshold_volt) { - /* APM voltage threshold corner not required. */ + if (!ctrl->apm_crossover_volt) { + /* APM voltage crossover corner not required. */ return 0; } - threshold = ctrl->apm_threshold_volt; - vreg->corner_count++; - - for (i = vreg->corner_count - 1; i >= 1; i--) { - corner = &vreg->corner[i]; - - if (threshold >= vreg->corner[i - 1].open_loop_volt) { - apm_corner = i; - break; - } - - memcpy(corner, &vreg->corner[i - 1], sizeof(*corner)); - } - - corner = &vreg->corner[apm_corner]; - corner->proc_freq = 0; - corner->floor_volt = threshold; - corner->ceiling_volt = threshold; - corner->open_loop_volt = threshold; - corner->use_open_loop = true; - cpr3_info(vreg, "APM threshold corner=%d, open-loop=%d\n", - apm_corner, threshold); - + corner = &vreg->corner[vreg->corner_count]; /* - * Update corner band mappings to account for the inserted - * APM crossover corner. + * 0 MHz indicates this corner is not to be + * used as active DCVS set point. */ - for (i = 0; i < vreg->corner_band_count; i++) { - corner_band = &vreg->corner_band[i]; - if (corner_band->corner >= apm_corner) - corner_band->corner++; - } + corner->proc_freq = 0; + corner->floor_volt = ctrl->apm_crossover_volt; + corner->ceiling_volt = ctrl->apm_crossover_volt; + corner->open_loop_volt = ctrl->apm_crossover_volt; + corner->use_open_loop = true; + vreg->corner_count++; return 0; } @@ -1203,9 +1180,9 @@ static int cprh_kbss_init_regulator(struct cpr3_regulator *vreg) return -EINVAL; } - rc = cprh_kbss_apm_threshold_as_corner(vreg); + rc = cprh_kbss_apm_crossover_as_corner(vreg); if (rc) { - cpr3_err(vreg, "unable to introduce APM voltage threshold corner\n, rc=%d\n", + cpr3_err(vreg, "unable to introduce APM voltage crossover corner, rc=%d\n", rc); return rc; } @@ -1288,8 +1265,18 @@ static int cprh_kbss_init_controller(struct cpr3_controller *ctrl) rc = of_property_read_u32(ctrl->dev->of_node, "qcom,apm-threshold-voltage", &ctrl->apm_threshold_volt); - if (rc) + if (rc) { cpr3_debug(ctrl, "qcom,apm-threshold-voltage not specified\n"); + } else { + rc = of_property_read_u32(ctrl->dev->of_node, + "qcom,apm-crossover-voltage", + &ctrl->apm_crossover_volt); + if (rc) { + cpr3_err(ctrl, "error reading property qcom,apm-crossover-voltage, rc=%d\n", + rc); + return rc; + } + } of_property_read_u32(ctrl->dev->of_node, "qcom,apm-hysteresis-voltage", &ctrl->apm_adj_volt); diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index 074878b55a0b..d044f3f273be 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -944,6 +944,7 @@ struct fib { */ struct list_head fiblink; void *data; + u32 vector_no; struct hw_fib *hw_fib_va; /* Actual shared object */ dma_addr_t hw_fib_pa; /* physical address of hw_fib*/ }; @@ -2113,6 +2114,7 @@ static inline unsigned int cap_to_cyls(sector_t capacity, unsigned divisor) int aac_acquire_irq(struct aac_dev *dev); void aac_free_irq(struct aac_dev *dev); const char *aac_driverinfo(struct Scsi_Host *); +void aac_fib_vector_assign(struct aac_dev *dev); struct fib *aac_fib_alloc(struct aac_dev *dev); int aac_fib_setup(struct aac_dev *dev); void aac_fib_map_free(struct aac_dev *dev); diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index a1f90fe849c9..4cbf54928640 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -83,13 +83,38 @@ static int fib_map_alloc(struct aac_dev *dev) void aac_fib_map_free(struct aac_dev *dev) { - pci_free_consistent(dev->pdev, - dev->max_fib_size * (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB), - dev->hw_fib_va, dev->hw_fib_pa); + if (dev->hw_fib_va && dev->max_fib_size) { + pci_free_consistent(dev->pdev, + (dev->max_fib_size * + (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB)), + dev->hw_fib_va, dev->hw_fib_pa); + } dev->hw_fib_va = NULL; dev->hw_fib_pa = 0; } +void aac_fib_vector_assign(struct aac_dev *dev) +{ + u32 i = 0; + u32 vector = 1; + struct fib *fibptr = NULL; + + for (i = 0, fibptr = &dev->fibs[i]; + i < (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB); + i++, fibptr++) { + if ((dev->max_msix == 1) || + (i > ((dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB - 1) + - dev->vector_cap))) { + fibptr->vector_no = 0; + } else { + fibptr->vector_no = vector; + vector++; + if (vector == dev->max_msix) + vector = 1; + } + } +} + /** * aac_fib_setup - setup the fibs * @dev: Adapter to set up @@ -151,6 +176,12 @@ int aac_fib_setup(struct aac_dev * dev) hw_fib_pa = hw_fib_pa + dev->max_fib_size + sizeof(struct aac_fib_xporthdr); } + + /* + *Assign vector numbers to fibs + */ + aac_fib_vector_assign(dev); + /* * Add the fib chain to the free list */ diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index 3b6e5c67e853..aa6eccb8940b 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -1404,8 +1404,18 @@ static int aac_acquire_resources(struct aac_dev *dev) aac_adapter_enable_int(dev); - if (!dev->sync_mode) + /*max msix may change after EEH + * Re-assign vectors to fibs + */ + aac_fib_vector_assign(dev); + + if (!dev->sync_mode) { + /* After EEH recovery or suspend resume, max_msix count + * may change, therfore updating in init as well. + */ aac_adapter_start(dev); + dev->init->Sa_MSIXVectors = cpu_to_le32(dev->max_msix); + } return 0; error_iounmap: diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c index 2aa34ea8ceb1..bc0203f3d243 100644 --- a/drivers/scsi/aacraid/src.c +++ b/drivers/scsi/aacraid/src.c @@ -156,8 +156,8 @@ static irqreturn_t aac_src_intr_message(int irq, void *dev_id) break; if (dev->msi_enabled && dev->max_msix > 1) atomic_dec(&dev->rrq_outstanding[vector_no]); - aac_intr_normal(dev, handle-1, 0, isFastResponse, NULL); dev->host_rrq[index++] = 0; + aac_intr_normal(dev, handle-1, 0, isFastResponse, NULL); if (index == (vector_no + 1) * dev->vector_cap) index = vector_no * dev->vector_cap; dev->host_rrq_idx[vector_no] = index; @@ -452,36 +452,20 @@ static int aac_src_deliver_message(struct fib *fib) #endif u16 hdr_size = le16_to_cpu(fib->hw_fib_va->header.Size); + u16 vector_no; atomic_inc(&q->numpending); if (dev->msi_enabled && fib->hw_fib_va->header.Command != AifRequest && dev->max_msix > 1) { - u_int16_t vector_no, first_choice = 0xffff; - - vector_no = dev->fibs_pushed_no % dev->max_msix; - do { - vector_no += 1; - if (vector_no == dev->max_msix) - vector_no = 1; - if (atomic_read(&dev->rrq_outstanding[vector_no]) < - dev->vector_cap) - break; - if (0xffff == first_choice) - first_choice = vector_no; - else if (vector_no == first_choice) - break; - } while (1); - if (vector_no == first_choice) - vector_no = 0; - atomic_inc(&dev->rrq_outstanding[vector_no]); - if (dev->fibs_pushed_no == 0xffffffff) - dev->fibs_pushed_no = 0; - else - dev->fibs_pushed_no++; + vector_no = fib->vector_no; fib->hw_fib_va->header.Handle += (vector_no << 16); + } else { + vector_no = 0; } + atomic_inc(&dev->rrq_outstanding[vector_no]); + if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE2) { /* Calculate the amount to the fibsize bits */ fibsize = (hdr_size + 127) / 128 - 1; diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c index b846a4683562..fc6a83188c1e 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c @@ -1336,6 +1336,7 @@ ahc_platform_set_tags(struct ahc_softc *ahc, struct scsi_device *sdev, case AHC_DEV_Q_TAGGED: scsi_change_queue_depth(sdev, dev->openings + dev->active); + break; default: /* * We allow the OS to queue 2 untagged transactions to diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index fe0c5143f8e6..758f76e88704 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -4470,6 +4470,7 @@ put_shost: scsi_host_put(phba->shost); free_kset: iscsi_boot_destroy_kset(phba->boot_kset); + phba->boot_kset = NULL; return -ENOMEM; } diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 536cd5a80422..43ac62623bf2 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -4003,13 +4003,17 @@ static ssize_t ipr_store_update_fw(struct device *dev, struct ipr_sglist *sglist; char fname[100]; char *src; - int len, result, dnld_size; + char *endline; + int result, dnld_size; if (!capable(CAP_SYS_ADMIN)) return -EACCES; - len = snprintf(fname, 99, "%s", buf); - fname[len-1] = '\0'; + snprintf(fname, sizeof(fname), "%s", buf); + + endline = strchr(fname, '\n'); + if (endline) + *endline = '\0'; if (request_firmware(&fw_entry, fname, &ioa_cfg->pdev->dev)) { dev_err(&ioa_cfg->pdev->dev, "Firmware file %s not found\n", fname); diff --git a/drivers/scsi/scsi_common.c b/drivers/scsi/scsi_common.c index c126966130ab..ce79de822e46 100644 --- a/drivers/scsi/scsi_common.c +++ b/drivers/scsi/scsi_common.c @@ -278,8 +278,16 @@ int scsi_set_sense_information(u8 *buf, int buf_len, u64 info) ucp[3] = 0; put_unaligned_be64(info, &ucp[4]); } else if ((buf[0] & 0x7f) == 0x70) { - buf[0] |= 0x80; - put_unaligned_be64(info, &buf[3]); + /* + * Only set the 'VALID' bit if we can represent the value + * correctly; otherwise just fill out the lower bytes and + * clear the 'VALID' flag. + */ + if (info <= 0xffffffffUL) + buf[0] |= 0x80; + else + buf[0] &= 0x7f; + put_unaligned_be32((u32)info, &buf[3]); } return 0; diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 6d6606e1568a..5d81bcc1dc75 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -673,7 +673,7 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) */ if (sdkp->lbprz) { q->limits.discard_alignment = 0; - q->limits.discard_granularity = 1; + q->limits.discard_granularity = logical_block_size; } else { q->limits.discard_alignment = sdkp->unmap_alignment * logical_block_size; @@ -1300,18 +1300,19 @@ static int sd_getgeo(struct block_device *bdev, struct hd_geometry *geo) struct scsi_disk *sdkp = scsi_disk(bdev->bd_disk); struct scsi_device *sdp = sdkp->device; struct Scsi_Host *host = sdp->host; + sector_t capacity = logical_to_sectors(sdp, sdkp->capacity); int diskinfo[4]; /* default to most commonly used values */ - diskinfo[0] = 0x40; /* 1 << 6 */ - diskinfo[1] = 0x20; /* 1 << 5 */ - diskinfo[2] = sdkp->capacity >> 11; - + diskinfo[0] = 0x40; /* 1 << 6 */ + diskinfo[1] = 0x20; /* 1 << 5 */ + diskinfo[2] = capacity >> 11; + /* override with calculated, extended default, or driver values */ if (host->hostt->bios_param) - host->hostt->bios_param(sdp, bdev, sdkp->capacity, diskinfo); + host->hostt->bios_param(sdp, bdev, capacity, diskinfo); else - scsicam_bios_param(bdev, sdkp->capacity, diskinfo); + scsicam_bios_param(bdev, capacity, diskinfo); geo->heads = diskinfo[0]; geo->sectors = diskinfo[1]; @@ -2281,14 +2282,6 @@ got_data: if (sdkp->capacity > 0xffffffff) sdp->use_16_for_rw = 1; - /* Rescale capacity to 512-byte units */ - if (sector_size == 4096) - sdkp->capacity <<= 3; - else if (sector_size == 2048) - sdkp->capacity <<= 2; - else if (sector_size == 1024) - sdkp->capacity <<= 1; - blk_queue_physical_block_size(sdp->request_queue, sdkp->physical_block_size); sdkp->device->sector_size = sector_size; @@ -2739,11 +2732,6 @@ static int sd_try_extended_inquiry(struct scsi_device *sdp) return 0; } -static inline u32 logical_to_sectors(struct scsi_device *sdev, u32 blocks) -{ - return blocks << (ilog2(sdev->sector_size) - 9); -} - /** * sd_revalidate_disk - called the first time a new disk is seen, * performs disk spin up, read_capacity, etc. @@ -2827,7 +2815,7 @@ static int sd_revalidate_disk(struct gendisk *disk) /* Combine with controller limits */ q->limits.max_sectors = min(rw_max, queue_max_hw_sectors(q)); - set_capacity(disk, sdkp->capacity); + set_capacity(disk, logical_to_sectors(sdp, sdkp->capacity)); sd_config_write_same(sdkp); kfree(buffer); diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 5f2a84aff29f..654630bb7d0e 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -65,7 +65,7 @@ struct scsi_disk { struct device dev; struct gendisk *disk; atomic_t openers; - sector_t capacity; /* size in 512-byte sectors */ + sector_t capacity; /* size in logical blocks */ u32 max_xfer_blocks; u32 opt_xfer_blocks; u32 max_ws_blocks; @@ -146,6 +146,11 @@ static inline int scsi_medium_access_command(struct scsi_cmnd *scmd) return 0; } +static inline sector_t logical_to_sectors(struct scsi_device *sdev, sector_t blocks) +{ + return blocks << (ilog2(sdev->sector_size) - 9); +} + /* * A DIF-capable target device can be formatted with different * protection schemes. Currently 0 through 3 are defined: diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index ab184cd4d773..77b2da269d6e 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -652,7 +652,8 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) else hp->dxfer_direction = (mxsize > 0) ? SG_DXFER_FROM_DEV : SG_DXFER_NONE; hp->dxfer_len = mxsize; - if (hp->dxfer_direction == SG_DXFER_TO_DEV) + if ((hp->dxfer_direction == SG_DXFER_TO_DEV) || + (hp->dxfer_direction == SG_DXFER_TO_FROM_DEV)) hp->dxferp = (char __user *)buf + cmd_size; else hp->dxferp = NULL; diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 3fba42ad9fb8..0f636cc4c809 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -889,8 +889,9 @@ static void storvsc_handle_error(struct vmscsi_request *vm_srb, do_work = true; process_err_fn = storvsc_remove_lun; break; - case (SRB_STATUS_ABORTED | SRB_STATUS_AUTOSENSE_VALID): - if ((asc == 0x2a) && (ascq == 0x9)) { + case SRB_STATUS_ABORTED: + if (vm_srb->srb_status & SRB_STATUS_AUTOSENSE_VALID && + (asc == 0x2a) && (ascq == 0x9)) { do_work = true; process_err_fn = storvsc_device_scan; /* diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 9135415a5a51..4d406c51d884 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4067,8 +4067,11 @@ int ufshcd_uic_hibern8_enter(struct ufs_hba *hba) for (retries = UIC_HIBERN8_ENTER_RETRIES; retries > 0; retries--) { ret = __ufshcd_uic_hibern8_enter(hba); - if (!ret || ret == -ENOLINK) + if (!ret) goto out; + /* Unable to recover the link, so no point proceeding */ + if (ret == -ENOLINK) + BUG(); } out: return ret; @@ -4090,6 +4093,9 @@ int ufshcd_uic_hibern8_exit(struct ufs_hba *hba) dev_err(hba->dev, "%s: hibern8 exit failed. ret = %d", __func__, ret); ret = ufshcd_link_recovery(hba); + /* Unable to recover the link, so no point proceeding */ + if (ret) + BUG(); } else { dev_dbg(hba->dev, "%s: Hibern8 Exit at %lld us", __func__, ktime_to_us(ktime_get())); @@ -6316,6 +6322,12 @@ static int ufshcd_reset_and_restore(struct ufs_hba *hba) } while (err && --retries); /* + * There is no point proceeding even after failing + * to recover after multiple retries. + */ + if (err) + BUG(); + /* * After reset the door-bell might be cleared, complete * outstanding requests in s/w here. */ diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c index 2769d08b3056..2e6672326a77 100644 --- a/drivers/slimbus/slim-msm-ngd.c +++ b/drivers/slimbus/slim-msm-ngd.c @@ -299,6 +299,26 @@ static void slim_reinit_tx_msgq(struct msm_slim_ctrl *dev) } } +static int ngd_check_hw_status(struct msm_slim_ctrl *dev) +{ + void __iomem *ngd = dev->base + NGD_BASE(dev->ctrl.nr, dev->ver); + u32 laddr = readl_relaxed(ngd + NGD_STATUS); + int ret = 0; + + /* Lost logical addr due to noise */ + if (!(laddr & NGD_LADDR)) { + SLIM_WARN(dev, "NGD lost LADDR: status:0x%x\n", laddr); + ret = ngd_slim_power_up(dev, false); + + if (ret) { + SLIM_WARN(dev, "slim resume ret:%d, state:%d\n", + ret, dev->state); + ret = -EREMOTEIO; + } + } + return ret; +} + static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn) { DECLARE_COMPLETION_ONSTACK(done); @@ -351,7 +371,6 @@ static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn) /* If txn is tried when controller is down, wait for ADSP to boot */ if (!report_sat) { - if (dev->state == MSM_CTRL_DOWN) { u8 mc = (u8)txn->mc; int timeout; @@ -418,6 +437,12 @@ static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn) msm_slim_put_ctrl(dev); return -EREMOTEIO; } + ret = ngd_check_hw_status(dev); + if (ret) { + mutex_unlock(&dev->tx_lock); + msm_slim_put_ctrl(dev); + return ret; + } } if (txn->mt == SLIM_MSG_MT_CORE && @@ -711,7 +736,6 @@ static int ngd_bulk_wr(struct slim_controller *ctrl, u8 la, u8 mt, u8 mc, struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl); int i, ret; struct msm_slim_endp *endpoint = &dev->tx_msgq; - struct sps_pipe *pipe = endpoint->sps; u32 *header; DECLARE_COMPLETION_ONSTACK(done); @@ -738,6 +762,14 @@ static int ngd_bulk_wr(struct slim_controller *ctrl, u8 la, u8 mt, u8 mc, } mutex_lock(&dev->tx_lock); } + + ret = ngd_check_hw_status(dev); + if (ret) { + mutex_unlock(&dev->tx_lock); + msm_slim_put_ctrl(dev); + return ret; + } + if (dev->use_tx_msgqs != MSM_MSGQ_ENABLED) { SLIM_WARN(dev, "bulk wr not supported"); ret = -EPROTONOSUPPORT; @@ -808,8 +840,8 @@ static int ngd_bulk_wr(struct slim_controller *ctrl, u8 la, u8 mt, u8 mc, goto retpath; } - ret = sps_transfer_one(pipe, dev->bulk.wr_dma, dev->bulk.size, NULL, - SPS_IOVEC_FLAG_EOT); + ret = sps_transfer_one(endpoint->sps, dev->bulk.wr_dma, dev->bulk.size, + NULL, SPS_IOVEC_FLAG_EOT); if (ret) { SLIM_WARN(dev, "sps transfer one returned error:%d", ret); goto retpath; @@ -1081,6 +1113,7 @@ static void ngd_slim_setup(struct msm_slim_ctrl *dev) } else { if (dev->use_rx_msgqs == MSM_MSGQ_DISABLED) goto setup_tx_msg_path; + if ((dev->use_rx_msgqs == MSM_MSGQ_ENABLED) && (cfg & NGD_CFG_RX_MSGQ_EN)) goto setup_tx_msg_path; @@ -1186,7 +1219,7 @@ static void ngd_slim_rx(struct msm_slim_ctrl *dev, u8 *buf) static int ngd_slim_power_up(struct msm_slim_ctrl *dev, bool mdm_restart) { void __iomem *ngd; - int timeout, ret = 0; + int timeout, retries = 0, ret = 0; enum msm_ctrl_state cur_state = dev->state; u32 laddr; u32 rx_msgq; @@ -1204,16 +1237,24 @@ static int ngd_slim_power_up(struct msm_slim_ctrl *dev, bool mdm_restart) } } +hw_init_retry: /* No need to vote if contorller is not in low power mode */ if (!mdm_restart && (cur_state == MSM_CTRL_DOWN || cur_state == MSM_CTRL_ASLEEP)) { ret = msm_slim_qmi_power_request(dev, true); if (ret) { - SLIM_ERR(dev, "SLIM QMI power request failed:%d\n", - ret); + SLIM_WARN(dev, "SLIM power req failed:%d, retry:%d\n", + ret, retries); + msm_slim_qmi_power_request(dev, false); + if (retries < INIT_MX_RETRIES) { + retries++; + goto hw_init_retry; + } return ret; } } + retries = 0; + if (!dev->ver) { dev->ver = readl_relaxed(dev->base); /* Version info in 16 MSbits */ @@ -1277,6 +1318,7 @@ static int ngd_slim_power_up(struct msm_slim_ctrl *dev, bool mdm_restart) dev->state = MSM_CTRL_DOWN; } +capability_retry: /* * ADSP power collapse case (OR SSR), where HW was reset * BAM programming will happen when capability message is received @@ -1297,7 +1339,16 @@ static int ngd_slim_power_up(struct msm_slim_ctrl *dev, bool mdm_restart) timeout = wait_for_completion_timeout(&dev->reconf, HZ); if (!timeout) { - SLIM_WARN(dev, "capability exchange timed-out\n"); + u32 cfg = readl_relaxed(dev->base + + NGD_BASE(dev->ctrl.nr, dev->ver)); + laddr = readl_relaxed(ngd + NGD_STATUS); + SLIM_WARN(dev, + "slim capability time-out:%d, stat:0x%x,cfg:0x%x\n", + retries, laddr, cfg); + if (retries < INIT_MX_RETRIES) { + retries++; + goto capability_retry; + } return -ETIMEDOUT; } /* mutliple transactions waiting on slimbus to power up? */ @@ -1389,12 +1440,11 @@ capability_retry: SLIM_INFO(dev, "SLIM SAT: capability exchange successful\n"); - if (prev_state >= MSM_CTRL_ASLEEP) - complete(&dev->reconf); - else + if (prev_state < MSM_CTRL_ASLEEP) SLIM_WARN(dev, - "SLIM: unexpected capability, state:%d\n", + "capability due to noise, state:%d\n", prev_state); + complete(&dev->reconf); /* ADSP SSR, send device_up notifications */ if (prev_state == MSM_CTRL_DOWN) complete(&dev->qmi.slave_notify); @@ -1471,11 +1521,13 @@ static void ngd_adsp_down(struct msm_slim_ctrl *dev) struct slim_controller *ctrl = &dev->ctrl; struct slim_device *sbdev; + mutex_lock(&dev->ssr_lock); ngd_slim_enable(dev, false); /* device up should be called again after SSR */ list_for_each_entry(sbdev, &ctrl->devs, dev_list) slim_report_absent(sbdev); SLIM_INFO(dev, "SLIM ADSP SSR (DOWN) done\n"); + mutex_unlock(&dev->ssr_lock); } static void ngd_adsp_up(struct work_struct *work) @@ -1484,7 +1536,9 @@ static void ngd_adsp_up(struct work_struct *work) container_of(work, struct msm_slim_qmi, ssr_up); struct msm_slim_ctrl *dev = container_of(qmi, struct msm_slim_ctrl, qmi); + mutex_lock(&dev->ssr_lock); ngd_slim_enable(dev, true); + mutex_unlock(&dev->ssr_lock); } static ssize_t show_mask(struct device *device, struct device_attribute *attr, @@ -1648,6 +1702,7 @@ static int ngd_slim_probe(struct platform_device *pdev) init_completion(&dev->reconf); init_completion(&dev->ctrl_up); mutex_init(&dev->tx_lock); + mutex_init(&dev->ssr_lock); spin_lock_init(&dev->tx_buf_lock); spin_lock_init(&dev->rx_lock); dev->ee = 1; diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h index 86d2606182fa..fc0a8d23f573 100644 --- a/drivers/slimbus/slim-msm.h +++ b/drivers/slimbus/slim-msm.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-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 @@ -67,7 +67,7 @@ #define SLIM_MSG_ASM_FIRST_WORD(l, mt, mc, dt, ad) \ ((l) | ((mt) << 5) | ((mc) << 8) | ((dt) << 15) | ((ad) << 16)) -#define INIT_MX_RETRIES 10 +#define INIT_MX_RETRIES 3 #define DEF_RETRY_MS 10 #define MSM_CONCUR_MSG 8 #define SAT_CONCUR_MSG 8 @@ -284,6 +284,7 @@ struct msm_slim_ctrl { struct clk *rclk; struct clk *hclk; struct mutex tx_lock; + struct mutex ssr_lock; spinlock_t tx_buf_lock; u8 pgdla; enum msm_slim_msgq use_rx_msgqs; diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 45dc329a776e..3f8aa534c220 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -374,6 +374,15 @@ config QCOM_WATCHDOG_V2 deadlocks. It does not run during the bootup process, so it will not catch any early lockups. +config QCOM_IRQ_HELPER + bool "QCOM Irq Helper" + help + This enables the irq helper module. It exposes two APIs + int irq_blacklist_on(void) and int irq_blacklist_off(void) + to other kernel module. + These two apis will be used to control the black list used + by the irq balancer. + config QCOM_MEMORY_DUMP bool "Qualcomm Memory Dump Support" help diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index bb3f092f6de9..f8450a4868ad 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -13,7 +13,7 @@ obj-$(CONFIG_MSM_GLINK_SMD_XPRT) += glink_smd_xprt.o obj-$(CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT)+= glink_smem_native_xprt.o obj-$(CONFIG_MSM_SMEM_LOGGING) += smem_log.o obj-$(CONFIG_MSM_SYSMON_GLINK_COMM) += sysmon-glink.o sysmon-qmi.o -obj-$(CONFIG_ARCH_QCOM) += kryo-l2-accessors.o +obj-$(CONFIG_ARCH_MSM8996) += kryo-l2-accessors.o obj-$(CONFIG_MSM_SMP2P) += smp2p.o smp2p_debug.o smp2p_sleepstate.o obj-$(CONFIG_MSM_SMP2P_TEST) += smp2p_loopback.o smp2p_test.o smp2p_spinlock_test.o obj-$(CONFIG_MSM_QMI_INTERFACE) += qmi_interface.o @@ -67,6 +67,7 @@ obj-$(CONFIG_QCOM_MEMORY_DUMP_V2) += memory_dump_v2.o obj-$(CONFIG_QCOM_DCC) += dcc.o obj-$(CONFIG_QCOM_WATCHDOG_V2) += watchdog_v2.o obj-$(CONFIG_QCOM_COMMON_LOG) += common_log.o +obj-$(CONFIG_QCOM_IRQ_HELPER) += irq-helper.o obj-$(CONFIG_TRACER_PKT) += tracer_pkt.o obj-$(CONFIG_ICNSS) += icnss.o wlan_firmware_service_v01.o obj-$(CONFIG_SOC_BUS) += socinfo.o @@ -83,7 +84,9 @@ obj-$(CONFIG_MSM_GLADIATOR_HANG_DETECT) += gladiator_hang_detect.o obj-$(CONFIG_MSM_RUN_QUEUE_STATS) += msm_rq_stats.o obj-$(CONFIG_MSM_BOOT_STATS) += boot_stats.o obj-$(CONFIG_MSM_AVTIMER) += avtimer.o +ifdef CONFIG_ARCH_MSM8996 obj-$(CONFIG_HW_PERF_EVENTS) += perf_event_kryo.o +endif obj-$(CONFIG_MSM_JTAGV8) += jtag-fuse.o jtagv8.o jtagv8-etm.o obj-$(CONFIG_MSM_KERNEL_PROTECT) += kernel_protect.o obj-$(CONFIG_MSM_RTB) += msm_rtb-hotplug.o diff --git a/drivers/soc/qcom/glink_private.h b/drivers/soc/qcom/glink_private.h index 2f064e546f48..cdd6988418f7 100644 --- a/drivers/soc/qcom/glink_private.h +++ b/drivers/soc/qcom/glink_private.h @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/kref.h> #include <linux/ratelimit.h> +#include <linux/sched.h> #include <linux/seq_file.h> #include <linux/spinlock.h> #include <linux/types.h> diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c index d7d08dc588e5..84f346385f18 100644 --- a/drivers/soc/qcom/glink_smem_native_xprt.c +++ b/drivers/soc/qcom/glink_smem_native_xprt.c @@ -2191,6 +2191,8 @@ static int subsys_name_to_id(const char *name) return SMEM_WCNSS; if (!strcmp(name, "spss")) return SMEM_SPSS; + if (!strcmp(name, "cdsp")) + return SMEM_CDSP; return -ENODEV; } diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 883f23d8234d..ea25ed5d0611 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -2387,7 +2387,7 @@ static int icnss_probe(struct platform_device *pdev) spin_lock_init(&penv->event_lock); spin_lock_init(&penv->on_off_lock); - penv->event_wq = alloc_workqueue("icnss_driver_event", 0, 0); + penv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1); if (!penv->event_wq) { icnss_pr_err("Workqueue creation failed\n"); ret = -EFAULT; @@ -2487,11 +2487,11 @@ static int icnss_suspend(struct platform_device *pdev, icnss_pr_dbg("Driver suspending, state: 0x%lx\n", penv->state); - if (!penv->ops) + if (!penv->ops || !penv->ops->suspend || + !test_bit(ICNSS_DRIVER_PROBED, &penv->state)) goto out; - if (penv->ops->suspend) - ret = penv->ops->suspend(&pdev->dev, state); + ret = penv->ops->suspend(&pdev->dev, state); out: if (ret == 0) @@ -2511,11 +2511,11 @@ static int icnss_resume(struct platform_device *pdev) icnss_pr_dbg("Driver resuming, state: 0x%lx\n", penv->state); - if (!penv->ops) + if (!penv->ops || !penv->ops->resume || + !test_bit(ICNSS_DRIVER_PROBED, &penv->state)) goto out; - if (penv->ops->resume) - ret = penv->ops->resume(&pdev->dev); + ret = penv->ops->resume(&pdev->dev); out: if (ret == 0) diff --git a/drivers/soc/qcom/irq-helper.c b/drivers/soc/qcom/irq-helper.c new file mode 100644 index 000000000000..270a1ba9ba19 --- /dev/null +++ b/drivers/soc/qcom/irq-helper.c @@ -0,0 +1,179 @@ +/* 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. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/cpu.h> +#include <linux/sysfs.h> +#include <linux/kobject.h> +#include <linux/spinlock.h> +#include <linux/slab.h> + +struct irq_helper { + bool enable; + bool deploy; + uint32_t count; + struct kobject kobj; + /* spinlock to protect reference count variable 'count' */ + spinlock_t lock; +}; + +struct irq_helper_attr { + struct attribute attr; + ssize_t (*show)(struct kobject *kobj, struct attribute *attr, + char *buf); + size_t (*store)(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count); +}; + +#define IRQ_HELPER_ATTR(_name, _mode, _show, _store) \ + struct irq_helper_attr irq_helper_##_name = \ + __ATTR(_name, _mode, _show, _store) + +#define to_irq_helper(kobj) \ + container_of(kobj, struct irq_helper, kobj) + +#define to_irq_helper_attr(_attr) \ + container_of(_attr, struct irq_helper_attr, attr) + +static ssize_t attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct irq_helper_attr *irq_attr = to_irq_helper_attr(attr); + ssize_t ret = -EIO; + + if (irq_attr->show) + ret = irq_attr->show(kobj, attr, buf); + + return ret; +} + +static const struct sysfs_ops irq_helper_sysfs_ops = { + .show = attr_show, +}; + +static struct kobj_type irq_helper_ktype = { + .sysfs_ops = &irq_helper_sysfs_ops, +}; + +static ssize_t show_deploy(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct irq_helper *irq = to_irq_helper(kobj); + + return snprintf(buf, sizeof(irq->deploy), "%u\n", irq->deploy); +} +IRQ_HELPER_ATTR(irq_blacklist_on, 0444, show_deploy, NULL); + +static struct irq_helper *irq_h; + +int irq_blacklist_on(void) +{ + bool flag = false; + + if (!irq_h) { + pr_err("%s: init function is not called", __func__); + return -EPERM; + } + if (!irq_h->enable) { + pr_err("%s: enable bit is not set up", __func__); + return -EPERM; + } + spin_lock(&irq_h->lock); + irq_h->count++; + if (!irq_h->deploy) { + irq_h->deploy = true; + flag = true; + } + spin_unlock(&irq_h->lock); + if (flag) + sysfs_notify(&irq_h->kobj, NULL, "irq_blacklist_on"); + return 0; +} +EXPORT_SYMBOL(irq_blacklist_on); + +int irq_blacklist_off(void) +{ + bool flag = false; + + if (!irq_h) { + pr_err("%s: init function is not called", __func__); + return -EPERM; + } + if (!irq_h->enable) { + pr_err("%s: enable bit is not set up", __func__); + return -EPERM; + } + spin_lock(&irq_h->lock); + if (irq_h->count == 0) { + pr_err("%s: ref-count is 0, cannot call irq blacklist off.", + __func__); + spin_unlock(&irq_h->lock); + return -EPERM; + } + irq_h->count--; + if (irq_h->count == 0) { + irq_h->deploy = false; + flag = true; + } + spin_unlock(&irq_h->lock); + + if (flag) + sysfs_notify(&irq_h->kobj, NULL, "irq_blacklist_on"); + return 0; +} +EXPORT_SYMBOL(irq_blacklist_off); + +static int __init irq_helper_init(void) +{ + int ret; + + irq_h = kzalloc(sizeof(struct irq_helper), GFP_KERNEL); + if (!irq_h) + return -ENOMEM; + + ret = kobject_init_and_add(&irq_h->kobj, &irq_helper_ktype, + kernel_kobj, "%s", "irq_helper"); + if (ret) { + pr_err("%s:Error in creation kobject_add\n", __func__); + goto out_free_irq; + } + + ret = sysfs_create_file(&irq_h->kobj, + &irq_helper_irq_blacklist_on.attr); + if (ret) { + pr_err("%s:Error in sysfs_create_file\n", __func__); + goto out_put_kobj; + } + + spin_lock_init(&irq_h->lock); + irq_h->count = 0; + irq_h->enable = true; + return 0; +out_put_kobj: + koject_put(&irq_h->kobj); +out_free_irq: + kfree(irq_h); + return ret; +} +module_init(irq_helper_init); + +static void __exit irq_helper_exit(void) +{ + sysfs_remove_file(&irq_h->kobj, &irq_helper_irq_blacklist_on.attr); + kobject_del(&irq_h->kobj); + kobject_put(&irq_h->kobj); + kfree(irq_h); +} +module_exit(irq_helper_exit); +MODULE_DESCRIPTION("IRQ Helper APIs"); diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c index e4d235957981..2bc425a437b2 100644 --- a/drivers/soc/qcom/service-locator.c +++ b/drivers/soc/qcom/service-locator.c @@ -48,6 +48,7 @@ module_param_named(enable, locator_status, uint, S_IRUGO | S_IWUSR); static void service_locator_svc_arrive(struct work_struct *work); static void service_locator_svc_exit(struct work_struct *work); static void service_locator_recv_msg(struct work_struct *work); +static void pd_locator_work(struct work_struct *work); struct workqueue_struct *servloc_wq; @@ -61,6 +62,11 @@ struct pd_qmi_data { struct qmi_handle *clnt_handle; }; +struct pd_qmi_work { + struct work_struct pd_loc_work; + struct pd_qmi_client_data *pdc; + struct notifier_block *notifier; +}; DEFINE_MUTEX(service_init_mutex); struct pd_qmi_data service_locator; @@ -288,7 +294,6 @@ out: static int init_service_locator(void) { - static bool service_inited; int rc = 0; mutex_lock(&service_init_mutex); @@ -324,50 +329,86 @@ static int init_service_locator(void) goto inited; } - rc = wait_for_completion_timeout(&service_locator.service_available, - msecs_to_jiffies(QMI_SERVREG_LOC_SERVER_INITIAL_TIMEOUT)); - if (!rc) { - rc = -ENODEV; - mutex_unlock(&service_init_mutex); - pr_err("Process domain service locator response timeout!\n"); - goto error; - } + wait_for_completion(&service_locator.service_available); service_inited = true; mutex_unlock(&service_init_mutex); pr_info("Service locator initialized\n"); return 0; -error: - qmi_svc_event_notifier_unregister(SERVREG_LOC_SERVICE_ID_V01, - SERVREG_LOC_SERVICE_VERS_V01, SERVREG_LOC_SERVICE_INSTANCE_ID, - &service_locator.notifier); - destroy_workqueue(servloc_wq); + inited: mutex_unlock(&service_init_mutex); return rc; } -int get_service_location(struct pd_qmi_client_data *data) +int get_service_location(char *client_name, char *service_name, + struct notifier_block *locator_nb) { + struct pd_qmi_client_data *pqcd; + struct pd_qmi_work *pqw; int rc = 0; - if (!data || !data->client_name || !data->service_name) { + if (!locator_nb || !client_name || !service_name) { rc = -EINVAL; pr_err("Invalid input!\n"); goto err; } + + pqcd = kmalloc(sizeof(struct pd_qmi_client_data), GFP_KERNEL); + if (!pqcd) { + rc = -ENOMEM; + pr_err("Allocation failed\n"); + goto err; + } + strlcpy(pqcd->client_name, client_name, ARRAY_SIZE(pqcd->client_name)); + strlcpy(pqcd->service_name, service_name, + ARRAY_SIZE(pqcd->service_name)); + + pqw = kmalloc(sizeof(struct pd_qmi_work), GFP_KERNEL); + if (!pqw) { + rc = -ENOMEM; + pr_err("Allocation failed\n"); + goto err; + } + pqw->notifier = locator_nb; + pqw->pdc = pqcd; + + INIT_WORK(&pqw->pd_loc_work, pd_locator_work); + schedule_work(&pqw->pd_loc_work); + +err: + return rc; +} +EXPORT_SYMBOL(get_service_location); + +static void pd_locator_work(struct work_struct *work) +{ + int rc = 0; + struct pd_qmi_client_data *data; + struct pd_qmi_work *pdqw = container_of(work, struct pd_qmi_work, + pd_loc_work); + + data = pdqw->pdc; rc = init_service_locator(); if (rc) { pr_err("Unable to connect to service locator!, rc = %d\n", rc); + pdqw->notifier->notifier_call(pdqw->notifier, + LOCATOR_DOWN, NULL); goto err; } rc = service_locator_send_msg(data); - if (rc) + if (rc) { pr_err("Failed to get process domains for %s for client %s\n", data->service_name, data->client_name); + pdqw->notifier->notifier_call(pdqw->notifier, + LOCATOR_DOWN, NULL); + goto err; + } + pdqw->notifier->notifier_call(pdqw->notifier, LOCATOR_UP, data); + err: - return rc; + kfree(data); + kfree(pdqw); } -EXPORT_SYMBOL(get_service_location); int find_subsys(const char *pd_path, char *subsys) { @@ -391,75 +432,137 @@ EXPORT_SYMBOL(find_subsys); static struct pd_qmi_client_data test_data; -static ssize_t show_servloc(struct seq_file *f, void *unused) +static int servloc_test_pdr_cb(struct notifier_block *this, + unsigned long opcode, void *ptr) { - int rc = 0, i = 0; + int i, rc = 0; char subsys[QMI_SERVREG_LOC_NAME_LENGTH_V01]; + struct pd_qmi_client_data *return_data; - rc = get_service_location(&test_data); - if (rc) { - seq_printf(f, "Failed to get process domain!, rc = %d\n", rc); + return_data = (struct pd_qmi_client_data *)ptr; + + if (opcode) { + pr_err("%s: Failed to get process domain!, opcode = %lu\n", + __func__, opcode); return -EIO; } - seq_printf(f, "Service Name: %s\tTotal Domains: %d\n", - test_data.service_name, test_data.total_domains); - for (i = 0; i < test_data.total_domains; i++) { - seq_printf(f, "Instance ID: %d\t ", - test_data.domain_list[i].instance_id); - seq_printf(f, "Domain Name: %s\n", - test_data.domain_list[i].name); - rc = find_subsys(test_data.domain_list[i].name, subsys); + pr_err("Service Name: %s\tTotal Domains: %d\n", + return_data->service_name, return_data->total_domains); + + for (i = 0; i < return_data->total_domains; i++) { + pr_err("Instance ID: %d\t ", + return_data->domain_list[i].instance_id); + pr_err("Domain Name: %s\n", + return_data->domain_list[i].name); + rc = find_subsys(return_data->domain_list[i].name, + subsys); if (rc < 0) - seq_printf(f, "No valid subsys found for %s!\n", - test_data.domain_list[i].name); + pr_err("No valid subsys found for %s!\n", + return_data->domain_list[i].name); else - seq_printf(f, "Subsys: %s\n", subsys); - } + pr_err("Subsys: %s\n", subsys); + } return 0; } -static ssize_t store_servloc(struct file *fp, const char __user *buf, - size_t count, loff_t *unused) +static struct notifier_block pdr_service_nb = { + .notifier_call = servloc_test_pdr_cb, +}; + +static ssize_t servloc_read(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) { + int rc = 0; + char *node_name = filp->private_data; + + if (!strcmp(node_name, "test_servloc_get")) + rc = get_service_location(test_data.client_name, + test_data.service_name, &pdr_service_nb); + + return rc; +} + +static ssize_t servloc_write(struct file *fp, const char __user *buf, + size_t count, loff_t *unused) +{ + char *node_name = fp->private_data; + if (!buf) return -EIO; - snprintf(test_data.service_name, sizeof(test_data.service_name), + if (!strcmp(node_name, "service_name")) { + snprintf(test_data.service_name, sizeof(test_data.service_name), "%.*s", (int) min((size_t)count - 1, (sizeof(test_data.service_name) - 1)), buf); + } else { + snprintf(test_data.client_name, sizeof(test_data.client_name), + "%.*s", (int) min((size_t)count - 1, + (sizeof(test_data.client_name) - 1)), buf); + } return count; } -static int servloc_open(struct inode *inode, struct file *file) -{ - return single_open(file, (void *)show_servloc, inode->i_private); -} - static const struct file_operations servloc_fops = { - .open = servloc_open, - .read = seq_read, - .write = store_servloc, - .llseek = seq_lseek, - .release = seq_release, + .open = simple_open, + .read = servloc_read, + .write = servloc_write, }; +static struct dentry *servloc_base_dir; static struct dentry *test_servloc_file; +static int __init servloc_debugfs_init(void) +{ + servloc_base_dir = debugfs_create_dir("test_servloc", NULL); + return !servloc_base_dir ? -ENOMEM : 0; +} + +static void servloc_debugfs_exit(void) +{ + debugfs_remove_recursive(servloc_base_dir); +} + +static int servloc_debugfs_add(void) +{ + int rc; + + if (!servloc_base_dir) + return -ENOMEM; + + test_servloc_file = debugfs_create_file("client_name", + S_IRUGO | S_IWUSR, servloc_base_dir, + "client_name", &servloc_fops); + rc = !test_servloc_file ? -ENOMEM : 0; + + if (rc == 0) { + test_servloc_file = debugfs_create_file("service_name", + S_IRUGO | S_IWUSR, servloc_base_dir, + "service_name", &servloc_fops); + rc = !test_servloc_file ? -ENOMEM : 0; + } + + if (rc == 0) { + test_servloc_file = debugfs_create_file("test_servloc_get", + S_IRUGO | S_IWUSR, servloc_base_dir, + "test_servloc_get", &servloc_fops); + rc = !test_servloc_file ? -ENOMEM : 0; + } + return rc; +} + static int __init service_locator_init(void) { pr_debug("service_locator_status = %d\n", locator_status); - test_servloc_file = debugfs_create_file("test_servloc", - S_IRUGO | S_IWUSR, NULL, NULL, - &servloc_fops); - if (!test_servloc_file) - pr_err("Could not create test_servloc debugfs entry!"); + if (servloc_debugfs_init()) + pr_err("Could not create test_servloc base directory!"); + if (servloc_debugfs_add()) + pr_err("Could not create test_servloc node entries!"); return 0; } static void __exit service_locator_exit(void) { - debugfs_remove(test_servloc_file); + servloc_debugfs_exit(); } - module_init(service_locator_init); module_exit(service_locator_exit); diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 4f29923e054c..b8cef11f4067 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -528,10 +528,11 @@ static struct msm_soc_info cpu_of_id[] = { [270] = {MSM_CPU_8929, "MSM8229"}, [271] = {MSM_CPU_8929, "APQ8029"}, - /* Cobalt ID */ + /* Cobalt IDs */ [292] = {MSM_CPU_COBALT, "MSMCOBALT"}, + [319] = {MSM_CPU_COBALT, "APQCOBALT"}, - /* Cobalt ID */ + /* Hamster ID */ [306] = {MSM_CPU_HAMSTER, "MSMHAMSTER"}, /* falcon ID */ @@ -1205,6 +1206,10 @@ static void * __init setup_dummy_socinfo(void) dummy_socinfo.id = 317; strlcpy(dummy_socinfo.build_id, "msmfalcon - ", sizeof(dummy_socinfo.build_id)); + } else if (early_machine_is_apqcobalt()) { + dummy_socinfo.id = 319; + strlcpy(dummy_socinfo.build_id, "apqcobalt - ", + sizeof(dummy_socinfo.build_id)); } strlcat(dummy_socinfo.build_id, "Dummy socinfo", diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 5e1fd988b22c..b02e48185355 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -97,6 +97,17 @@ enum pmic_arb_cmd_op_code { /* interrupt enable bit */ #define SPMI_PIC_ACC_ENABLE_BIT BIT(0) +#define HWIRQ(slave_id, periph_id, irq_id, apid) \ + ((((slave_id) & 0xF) << 28) | \ + (((periph_id) & 0xFF) << 20) | \ + (((irq_id) & 0x7) << 16) | \ + (((apid) & 0x1FF) << 0)) + +#define HWIRQ_SID(hwirq) (((hwirq) >> 28) & 0xF) +#define HWIRQ_PER(hwirq) (((hwirq) >> 20) & 0xFF) +#define HWIRQ_IRQ(hwirq) (((hwirq) >> 16) & 0x7) +#define HWIRQ_APID(hwirq) (((hwirq) >> 0) & 0x1FF) + struct pmic_arb_ver_ops; struct apid_data { @@ -172,7 +183,7 @@ struct spmi_pmic_arb { struct pmic_arb_ver_ops { const char *ver_str; int (*ppid_to_apid)(struct spmi_pmic_arb *pa, u8 sid, u16 addr, - u8 *apid); + u16 *apid); int (*mode)(struct spmi_pmic_arb *dev, u8 sid, u16 addr, mode_t *mode); /* spmi commands (read_cmd, write_cmd, cmd) functionality */ @@ -181,10 +192,10 @@ struct pmic_arb_ver_ops { u32 (*fmt_cmd)(u8 opc, u8 sid, u16 addr, u8 bc); int (*non_data_cmd)(struct spmi_controller *ctrl, u8 opc, u8 sid); /* Interrupts controller functionality (offset of PIC registers) */ - u32 (*owner_acc_status)(u8 m, u8 n); - u32 (*acc_enable)(u8 n); - u32 (*irq_status)(u8 n); - u32 (*irq_clear)(u8 n); + u32 (*owner_acc_status)(u8 m, u16 n); + u32 (*acc_enable)(u16 n); + u32 (*irq_status)(u16 n); + u32 (*irq_clear)(u16 n); }; static inline void pmic_arb_base_write(struct spmi_pmic_arb *pa, @@ -466,8 +477,8 @@ static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf, size_t len) { struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); - u8 sid = d->hwirq >> 24; - u8 per = d->hwirq >> 16; + u8 sid = HWIRQ_SID(d->hwirq); + u8 per = HWIRQ_PER(d->hwirq); if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid, (per << 8) + reg, buf, len)) @@ -479,8 +490,8 @@ static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf, static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len) { struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); - u8 sid = d->hwirq >> 24; - u8 per = d->hwirq >> 16; + u8 sid = HWIRQ_SID(d->hwirq); + u8 per = HWIRQ_PER(d->hwirq); if (pmic_arb_read_cmd(pa->spmic, SPMI_CMD_EXT_READL, sid, (per << 8) + reg, buf, len)) @@ -489,7 +500,7 @@ static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len) d->irq); } -static void cleanup_irq(struct spmi_pmic_arb *pa, u8 apid, int id) +static void cleanup_irq(struct spmi_pmic_arb *pa, u16 apid, int id) { u16 ppid = pa->apid_data[apid].ppid; u8 sid = ppid >> 8; @@ -514,20 +525,19 @@ static void cleanup_irq(struct spmi_pmic_arb *pa, u8 apid, int id) irq_mask, ppid); } -static void periph_interrupt(struct spmi_pmic_arb *pa, u8 apid) +static void periph_interrupt(struct spmi_pmic_arb *pa, u16 apid) { unsigned int irq; u32 status; int id; + u8 sid = (pa->apid_data[apid].ppid >> 8) & 0xF; + u8 per = pa->apid_data[apid].ppid & 0xFF; status = readl_relaxed(pa->intr + pa->ver_ops->irq_status(apid)); while (status) { id = ffs(status) - 1; status &= ~BIT(id); - irq = irq_find_mapping(pa->domain, - pa->apid_data[apid].ppid << 16 - | id << 8 - | apid); + irq = irq_find_mapping(pa->domain, HWIRQ(sid, per, id, apid)); if (irq == 0) { cleanup_irq(pa, apid, id); continue; @@ -568,8 +578,8 @@ static void pmic_arb_chained_irq(struct irq_desc *desc) static void qpnpint_irq_ack(struct irq_data *d) { struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); - u8 irq = d->hwirq >> 8; - u8 apid = d->hwirq; + u8 irq = HWIRQ_IRQ(d->hwirq); + u16 apid = HWIRQ_APID(d->hwirq); u8 data; writel_relaxed(BIT(irq), pa->intr + pa->ver_ops->irq_clear(apid)); @@ -580,7 +590,7 @@ static void qpnpint_irq_ack(struct irq_data *d) static void qpnpint_irq_mask(struct irq_data *d) { - u8 irq = d->hwirq >> 8; + u8 irq = HWIRQ_IRQ(d->hwirq); u8 data = BIT(irq); qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1); @@ -589,8 +599,8 @@ static void qpnpint_irq_mask(struct irq_data *d) static void qpnpint_irq_unmask(struct irq_data *d) { struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); - u8 irq = d->hwirq >> 8; - u8 apid = d->hwirq; + u8 irq = HWIRQ_IRQ(d->hwirq); + u16 apid = HWIRQ_APID(d->hwirq); u8 buf[2]; writel_relaxed(SPMI_PIC_ACC_ENABLE_BIT, @@ -612,7 +622,7 @@ static void qpnpint_irq_unmask(struct irq_data *d) static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type) { struct spmi_pmic_arb_qpnpint_type type; - u8 irq = d->hwirq >> 8; + u8 irq = HWIRQ_IRQ(d->hwirq); u8 bit_mask_irq = BIT(irq); qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); @@ -649,7 +659,7 @@ static int qpnpint_get_irqchip_state(struct irq_data *d, enum irqchip_irq_state which, bool *state) { - u8 irq = d->hwirq >> 8; + u8 irq = HWIRQ_IRQ(d->hwirq); u8 status = 0; if (which != IRQCHIP_STATE_LINE_LEVEL) @@ -681,7 +691,7 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, { struct spmi_pmic_arb *pa = d->host_data; int rc; - u8 apid; + u16 apid; dev_dbg(&pa->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n", @@ -709,10 +719,7 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, if (apid < pa->min_apid) pa->min_apid = apid; - *out_hwirq = (intspec[0] & 0xF) << 24 - | (intspec[1] & 0xFF) << 16 - | (intspec[2] & 0x7) << 8 - | apid; + *out_hwirq = HWIRQ(intspec[0], intspec[1], intspec[2], apid); *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK; dev_dbg(&pa->spmic->dev, "out_hwirq = %lu\n", *out_hwirq); @@ -735,7 +742,7 @@ static int qpnpint_irq_domain_map(struct irq_domain *d, } static int -pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u8 *apid) +pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u16 *apid) { u16 ppid = sid << 8 | ((addr >> 8) & 0xFF); u32 *mapping_table = pa->mapping_table; @@ -834,7 +841,7 @@ static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pa, u16 ppid) } static int -pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u8 *apid) +pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u16 *apid) { u16 ppid = (sid << 8) | (addr >> 8); u16 apid_valid; @@ -852,7 +859,7 @@ pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u8 *apid) static int pmic_arb_mode_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, mode_t *mode) { - u8 apid; + u16 apid; int rc; rc = pmic_arb_ppid_to_apid_v2(pa, sid, addr, &apid); @@ -871,7 +878,7 @@ pmic_arb_mode_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, mode_t *mode) static int pmic_arb_offset_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u32 *offset) { - u8 apid; + u16 apid; int rc; rc = pmic_arb_ppid_to_apid_v2(pa, sid, addr, &apid); @@ -892,47 +899,47 @@ static u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 sid, u16 addr, u8 bc) return (opc << 27) | ((addr & 0xff) << 4) | (bc & 0x7); } -static u32 pmic_arb_owner_acc_status_v1(u8 m, u8 n) +static u32 pmic_arb_owner_acc_status_v1(u8 m, u16 n) { return 0x20 * m + 0x4 * n; } -static u32 pmic_arb_owner_acc_status_v2(u8 m, u8 n) +static u32 pmic_arb_owner_acc_status_v2(u8 m, u16 n) { return 0x100000 + 0x1000 * m + 0x4 * n; } -static u32 pmic_arb_owner_acc_status_v3(u8 m, u8 n) +static u32 pmic_arb_owner_acc_status_v3(u8 m, u16 n) { return 0x200000 + 0x1000 * m + 0x4 * n; } -static u32 pmic_arb_acc_enable_v1(u8 n) +static u32 pmic_arb_acc_enable_v1(u16 n) { return 0x200 + 0x4 * n; } -static u32 pmic_arb_acc_enable_v2(u8 n) +static u32 pmic_arb_acc_enable_v2(u16 n) { return 0x1000 * n; } -static u32 pmic_arb_irq_status_v1(u8 n) +static u32 pmic_arb_irq_status_v1(u16 n) { return 0x600 + 0x4 * n; } -static u32 pmic_arb_irq_status_v2(u8 n) +static u32 pmic_arb_irq_status_v2(u16 n) { return 0x4 + 0x1000 * n; } -static u32 pmic_arb_irq_clear_v1(u8 n) +static u32 pmic_arb_irq_clear_v1(u16 n) { return 0xA00 + 0x4 * n; } -static u32 pmic_arb_irq_clear_v2(u8 n) +static u32 pmic_arb_irq_clear_v2(u16 n) { return 0x8 + 0x1000 * n; } diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index e9237202e79f..a8c8e120c348 100755 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -257,9 +257,10 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, * memory coming from the heaps is ready for dma, ie if it has a * cached mapping that mapping has been invalidated */ - for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) + for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) { sg_dma_address(sg) = sg_phys(sg); - + sg_dma_len(sg) = sg->length; + } mutex_lock(&dev->buffer_lock); ion_buffer_add(dev, buffer); mutex_unlock(&dev->buffer_lock); diff --git a/drivers/staging/android/ion/ion_test.c b/drivers/staging/android/ion/ion_test.c index b8dcf5a26cc4..58d46893e5ff 100644 --- a/drivers/staging/android/ion/ion_test.c +++ b/drivers/staging/android/ion/ion_test.c @@ -285,8 +285,8 @@ static int __init ion_test_init(void) { ion_test_pdev = platform_device_register_simple("ion-test", -1, NULL, 0); - if (!ion_test_pdev) - return -ENODEV; + if (IS_ERR(ion_test_pdev)) + return PTR_ERR(ion_test_pdev); return platform_driver_probe(&ion_test_platform_driver, ion_test_probe); } diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c index 6cc304a4c59b..27fbf1a81097 100644 --- a/drivers/staging/comedi/drivers/ni_mio_common.c +++ b/drivers/staging/comedi/drivers/ni_mio_common.c @@ -246,24 +246,24 @@ static void ni_writel(struct comedi_device *dev, uint32_t data, int reg) { if (dev->mmio) writel(data, dev->mmio + reg); - - outl(data, dev->iobase + reg); + else + outl(data, dev->iobase + reg); } static void ni_writew(struct comedi_device *dev, uint16_t data, int reg) { if (dev->mmio) writew(data, dev->mmio + reg); - - outw(data, dev->iobase + reg); + else + outw(data, dev->iobase + reg); } static void ni_writeb(struct comedi_device *dev, uint8_t data, int reg) { if (dev->mmio) writeb(data, dev->mmio + reg); - - outb(data, dev->iobase + reg); + else + outb(data, dev->iobase + reg); } static uint32_t ni_readl(struct comedi_device *dev, int reg) diff --git a/drivers/staging/comedi/drivers/ni_tiocmd.c b/drivers/staging/comedi/drivers/ni_tiocmd.c index 437f723bb34d..823e47910004 100644 --- a/drivers/staging/comedi/drivers/ni_tiocmd.c +++ b/drivers/staging/comedi/drivers/ni_tiocmd.c @@ -92,7 +92,7 @@ static int ni_tio_input_inttrig(struct comedi_device *dev, unsigned long flags; int ret = 0; - if (trig_num != cmd->start_src) + if (trig_num != cmd->start_arg) return -EINVAL; spin_lock_irqsave(&counter->lock, flags); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 94f4ffac723f..d151bc3d6971 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -2618,8 +2618,6 @@ void target_wait_for_sess_cmds(struct se_session *se_sess) list_for_each_entry_safe(se_cmd, tmp_cmd, &se_sess->sess_wait_list, se_cmd_list) { - list_del_init(&se_cmd->se_cmd_list); - pr_debug("Waiting for se_cmd: %p t_state: %d, fabric state:" " %d\n", se_cmd, se_cmd->t_state, se_cmd->se_tfo->get_cmd_state(se_cmd)); diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c index 9e2ba25ce1ac..97ab02dfc753 100644 --- a/drivers/thermal/msm-tsens.c +++ b/drivers/thermal/msm-tsens.c @@ -759,9 +759,9 @@ enum tsens_trip_type { }; enum tsens_tm_trip_type { - TSENS_TM_TRIP_CRITICAL = 0, - TSENS_TM_TRIP_WARM, + TSENS_TM_TRIP_WARM = 0, TSENS_TM_TRIP_COOL, + TSENS_TM_TRIP_CRITICAL, TSENS_TM_TRIP_NUM, }; @@ -831,6 +831,7 @@ struct tsens_tm_device { bool prev_reading_avail; bool calibration_less_mode; bool tsens_local_init; + bool gain_offset_programmed; int tsens_factor; uint32_t tsens_num_sensor; int tsens_irq; @@ -1575,9 +1576,6 @@ static int tsens_tm_get_trip_type(struct thermal_zone_device *thermal, case TSENS_TM_TRIP_COOL: *type = THERMAL_TRIP_CONFIGURABLE_LOW; break; - case TSENS_TM_TRIP_CRITICAL: - *type = THERMAL_TRIP_CRITICAL; - break; default: return -EINVAL; } @@ -5344,17 +5342,25 @@ static int get_device_tree_data(struct platform_device *pdev, return -ENODEV; } - tsens_slope_data = devm_kzalloc(&pdev->dev, + /* TSENS calibration region */ + tmdev->res_calib_mem = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "tsens_eeprom_physical"); + if (!tmdev->res_calib_mem) { + pr_debug("Using controller programmed gain and offset\n"); + tmdev->gain_offset_programmed = true; + } else { + tsens_slope_data = devm_kzalloc(&pdev->dev, tsens_num_sensors * sizeof(u32), GFP_KERNEL); - if (!tsens_slope_data) - return -ENOMEM; + if (!tsens_slope_data) + return -ENOMEM; - rc = of_property_read_u32_array(of_node, - "qcom,slope", tsens_slope_data, tsens_num_sensors); - if (rc) { - dev_err(&pdev->dev, "invalid or missing property: tsens-slope\n"); - return rc; - }; + rc = of_property_read_u32_array(of_node, + "qcom,slope", tsens_slope_data, tsens_num_sensors); + if (rc) { + dev_err(&pdev->dev, "missing property: tsens-slope\n"); + return rc; + }; + } if (!of_match_node(tsens_match, of_node)) { pr_err("Need to read SoC specific fuse map\n"); @@ -5367,9 +5373,13 @@ static int get_device_tree_data(struct platform_device *pdev, return -ENODEV; } - for (i = 0; i < tsens_num_sensors; i++) - tmdev->sensor[i].slope_mul_tsens_factor = tsens_slope_data[i]; - tmdev->tsens_factor = TSENS_SLOPE_FACTOR; + if (!tmdev->gain_offset_programmed) { + for (i = 0; i < tsens_num_sensors; i++) + tmdev->sensor[i].slope_mul_tsens_factor = + tsens_slope_data[i]; + tmdev->tsens_factor = TSENS_SLOPE_FACTOR; + } + tmdev->tsens_num_sensor = tsens_num_sensors; tmdev->calibration_less_mode = of_property_read_bool(of_node, "qcom,calibration-less-mode"); @@ -5539,24 +5549,17 @@ static int get_device_tree_data(struct platform_device *pdev, goto fail_unmap_tsens_region; } - /* TSENS calibration region */ - tmdev->res_calib_mem = platform_get_resource_byname(pdev, - IORESOURCE_MEM, "tsens_eeprom_physical"); - if (!tmdev->res_calib_mem) { - pr_err("Could not get qfprom physical address resource\n"); - rc = -EINVAL; - goto fail_unmap_tsens; - } - - tmdev->calib_len = tmdev->res_calib_mem->end - + if (!tmdev->gain_offset_programmed) { + tmdev->calib_len = tmdev->res_calib_mem->end - tmdev->res_calib_mem->start + 1; - tmdev->tsens_calib_addr = ioremap(tmdev->res_calib_mem->start, + tmdev->tsens_calib_addr = ioremap(tmdev->res_calib_mem->start, tmdev->calib_len); - if (!tmdev->tsens_calib_addr) { - pr_err("Failed to IO map EEPROM registers.\n"); - rc = -EINVAL; - goto fail_unmap_tsens; + if (!tmdev->tsens_calib_addr) { + pr_err("Failed to IO map EEPROM registers.\n"); + rc = -EINVAL; + goto fail_unmap_tsens; + } } return 0; @@ -5717,8 +5720,8 @@ static int tsens_thermal_zone_register(struct tsens_tm_device *tmdev) tmdev->sensor[i].tm = tmdev; if (tmdev->tsens_type == TSENS_TYPE3) { tmdev->sensor[i].tz_dev = thermal_zone_device_register( - name, TSENS_TM_TRIP_NUM, - TSENS_TM_WRITABLE_TRIPS_MASK, + name, TSENS_TRIP_NUM, + TSENS_WRITABLE_TRIPS_MASK, &tmdev->sensor[i], &tsens_tm_thermal_zone_ops, NULL, 0, 0); if (IS_ERR(tmdev->sensor[i].tz_dev)) { diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c index bbc6a8e96d41..ff40d6fad922 100644 --- a/drivers/thermal/msm_thermal.c +++ b/drivers/thermal/msm_thermal.c @@ -3615,7 +3615,7 @@ static int hotplug_init_cpu_offlined(void) int temp = 0; uint32_t cpu = 0; - if (!hotplug_enabled) + if (!hotplug_enabled || !hotplug_task) return 0; mutex_lock(&core_control_mutex); @@ -3632,8 +3632,7 @@ static int hotplug_init_cpu_offlined(void) if (temp >= msm_thermal_info.hotplug_temp_degC) cpus[cpu].offline = 1; - else if (temp <= (msm_thermal_info.hotplug_temp_degC - - msm_thermal_info.hotplug_temp_hysteresis_degC)) + else cpus[cpu].offline = 0; } mutex_unlock(&core_control_mutex); diff --git a/drivers/thermal/qpnp-adc-tm.c b/drivers/thermal/qpnp-adc-tm.c index 500d47d4ffdf..84ab45fde4ae 100644 --- a/drivers/thermal/qpnp-adc-tm.c +++ b/drivers/thermal/qpnp-adc-tm.c @@ -250,6 +250,8 @@ struct qpnp_adc_tm_sensor { bool thermal_node; uint32_t scale_type; struct list_head thr_list; + bool high_thr_triggered; + bool low_thr_triggered; }; struct qpnp_adc_tm_chip { @@ -501,8 +503,8 @@ static int32_t qpnp_adc_tm_rc_check_channel_en(struct qpnp_adc_tm_chip *chip) } adc_tm_ctl &= QPNP_BTM_Mn_MEAS_EN; - status_low &= QPNP_BTM_Mn_LOW_THR_INT_EN; - status_high &= QPNP_BTM_Mn_HIGH_THR_INT_EN; + status_low = adc_tm_ctl & QPNP_BTM_Mn_LOW_THR_INT_EN; + status_high = adc_tm_ctl & QPNP_BTM_Mn_HIGH_THR_INT_EN; /* Enable only if there are pending measurement requests */ if ((adc_tm_ctl && status_high) || @@ -1497,11 +1499,13 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal, if (qpnp_adc_tm_check_revision(chip, adc_tm->btm_channel_num)) return -EINVAL; + mutex_lock(&chip->adc->adc_lock); + btm_chan = adc_tm->btm_channel_num; rc = qpnp_adc_tm_get_btm_idx(chip, btm_chan, &btm_chan_idx); if (rc < 0) { pr_err("Invalid btm channel idx\n"); - return rc; + goto fail; } if (mode == THERMAL_DEVICE_ENABLED) { @@ -1527,14 +1531,14 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal, rc = qpnp_adc_tm_configure(chip, chip->adc->amux_prop); if (rc) { pr_err("adc-tm configure failed with %d\n", rc); - return -EINVAL; + goto fail; } } else { rc = qpnp_adc_tm_hc_configure(chip, chip->adc->amux_prop); if (rc) { pr_err("hc configure failed with %d\n", rc); - return -EINVAL; + goto fail; } } } else if (mode == THERMAL_DEVICE_DISABLED) { @@ -1545,7 +1549,7 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal, rc = qpnp_adc_tm_mode_select(chip, mode_ctl); if (rc < 0) { pr_err("adc-tm single mode select failed\n"); - return rc; + goto fail; } } @@ -1553,7 +1557,7 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal, rc = qpnp_adc_tm_disable(chip); if (rc < 0) { pr_err("adc-tm disable failed\n"); - return rc; + goto fail; } if (!chip->adc_tm_hc) { @@ -1561,14 +1565,14 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal, rc = qpnp_adc_tm_req_sts_check(chip); if (rc < 0) { pr_err("adc-tm req_sts check failed\n"); - return rc; + goto fail; } rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_TM_MULTI_MEAS_EN, sensor_mask, false); if (rc < 0) { pr_err("multi measurement update failed\n"); - return rc; + goto fail; } } else { rc = qpnp_adc_tm_reg_update(chip, @@ -1576,19 +1580,22 @@ static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal, QPNP_BTM_Mn_MEAS_EN, false); if (rc < 0) { pr_err("multi measurement disable failed\n"); - return rc; + goto fail; } } rc = qpnp_adc_tm_enable_if_channel_meas(chip); if (rc < 0) { pr_err("re-enabling measurement failed\n"); - return rc; + goto fail; } } adc_tm->mode = mode; +fail: + mutex_unlock(&chip->adc->adc_lock); + return 0; } @@ -2096,17 +2103,207 @@ fail: return rc; } -static int qpnp_adc_tm_read_status(struct qpnp_adc_tm_chip *chip) +static int qpnp_adc_tm_disable_rearm_high_thresholds( + struct qpnp_adc_tm_chip *chip, int sensor_num) { - u8 sensor_mask = 0, notify_check = 0; - int rc = 0, sensor_notify_num = 0, i = 0, sensor_num = 0; + + struct qpnp_adc_thr_client_info *client_info = NULL; + struct list_head *thr_list; uint32_t btm_chan_num = 0; + u8 sensor_mask = 0, notify_check = 0; + int rc = 0; + + btm_chan_num = chip->sensor[sensor_num].btm_channel_num; + pr_debug("high:sen:%d, hs:0x%x, ls:0x%x, meas_en:0x%x\n", + sensor_num, chip->th_info.adc_tm_high_enable, + chip->th_info.adc_tm_low_enable, + chip->th_info.qpnp_adc_tm_meas_en); + if (!chip->sensor[sensor_num].thermal_node) { + /* + * For non thermal registered clients such as usb_id, + * vbatt, pmic_therm + */ + sensor_mask = 1 << sensor_num; + pr_debug("non thermal node - mask:%x\n", sensor_mask); + rc = qpnp_adc_tm_recalib_request_check(chip, + sensor_num, true, ¬ify_check); + if (rc < 0 || !notify_check) { + pr_debug("Calib recheck re-armed rc=%d\n", rc); + chip->th_info.adc_tm_high_enable = 0; + return rc; + } + } else { + /* + * Uses the thermal sysfs registered device to disable + * the corresponding high voltage threshold which + * is triggered by low temp + */ + sensor_mask = 1 << sensor_num; + pr_debug("thermal node with mask:%x\n", sensor_mask); + rc = qpnp_adc_tm_activate_trip_type( + chip->sensor[sensor_num].tz_dev, + ADC_TM_TRIP_LOW_COOL, + THERMAL_TRIP_ACTIVATION_DISABLED); + if (rc < 0) { + pr_err("notify error:%d\n", sensor_num); + return rc; + } + } + list_for_each(thr_list, &chip->sensor[sensor_num].thr_list) { + client_info = list_entry(thr_list, + struct qpnp_adc_thr_client_info, list); + if (client_info->high_thr_set) { + client_info->high_thr_set = false; + client_info->notify_high_thr = true; + if (client_info->state_req_copy == + ADC_TM_HIGH_LOW_THR_ENABLE) + client_info->state_req_copy = + ADC_TM_LOW_THR_ENABLE; + else + client_info->state_req_copy = + ADC_TM_HIGH_THR_DISABLE; + } + } + qpnp_adc_tm_manage_thresholds(chip, sensor_num, btm_chan_num); + + if (!chip->adc_tm_hc) { + rc = qpnp_adc_tm_reg_update(chip, + QPNP_ADC_TM_MULTI_MEAS_EN, + sensor_mask, false); + if (rc < 0) { + pr_err("multi meas disable failed\n"); + return rc; + } + } else { + rc = qpnp_adc_tm_reg_update(chip, + QPNP_BTM_Mn_EN(sensor_num), + QPNP_BTM_Mn_MEAS_EN, false); + if (rc < 0) { + pr_err("multi meas disable failed\n"); + return rc; + } + } + + rc = qpnp_adc_tm_enable_if_channel_meas(chip); + if (rc < 0) { + pr_err("re-enabling measurement failed\n"); + return rc; + } + + queue_work(chip->sensor[sensor_num].req_wq, + &chip->sensor[sensor_num].work); + + return rc; +} + +static int qpnp_adc_tm_disable_rearm_low_thresholds( + struct qpnp_adc_tm_chip *chip, int sensor_num) +{ struct qpnp_adc_thr_client_info *client_info = NULL; struct list_head *thr_list; + uint32_t btm_chan_num = 0; + u8 sensor_mask = 0, notify_check = 0; + int rc = 0; + + btm_chan_num = chip->sensor[sensor_num].btm_channel_num; + pr_debug("low:sen:%d, hs:0x%x, ls:0x%x, meas_en:0x%x\n", + sensor_num, chip->th_info.adc_tm_high_enable, + chip->th_info.adc_tm_low_enable, + chip->th_info.qpnp_adc_tm_meas_en); + if (!chip->sensor[sensor_num].thermal_node) { + /* + * For non thermal registered clients such as usb_id, + * vbatt, pmic_therm + */ + pr_debug("non thermal node - mask:%x\n", sensor_mask); + rc = qpnp_adc_tm_recalib_request_check(chip, + sensor_num, false, ¬ify_check); + if (rc < 0 || !notify_check) { + pr_debug("Calib recheck re-armed rc=%d\n", rc); + chip->th_info.adc_tm_low_enable = 0; + return rc; + } + sensor_mask = 1 << sensor_num; + rc = qpnp_adc_tm_reg_update(chip, + QPNP_ADC_TM_LOW_THR_INT_EN, + sensor_mask, false); + if (rc < 0) { + pr_err("low threshold int read failed\n"); + return rc; + } + } else { + /* + * Uses the thermal sysfs registered device to disable + * the corresponding high voltage threshold which + * is triggered by low temp + */ + sensor_mask = 1 << sensor_num; + pr_debug("thermal node with mask:%x\n", sensor_mask); + rc = qpnp_adc_tm_activate_trip_type( + chip->sensor[sensor_num].tz_dev, + ADC_TM_TRIP_HIGH_WARM, + THERMAL_TRIP_ACTIVATION_DISABLED); + if (rc < 0) { + pr_err("notify error:%d\n", sensor_num); + return rc; + } + } + list_for_each(thr_list, &chip->sensor[sensor_num].thr_list) { + client_info = list_entry(thr_list, + struct qpnp_adc_thr_client_info, list); + if (client_info->low_thr_set) { + client_info->low_thr_set = false; + client_info->notify_low_thr = true; + if (client_info->state_req_copy == + ADC_TM_HIGH_LOW_THR_ENABLE) + client_info->state_req_copy = + ADC_TM_HIGH_THR_ENABLE; + else + client_info->state_req_copy = + ADC_TM_LOW_THR_DISABLE; + } + } + qpnp_adc_tm_manage_thresholds(chip, sensor_num, btm_chan_num); + + if (!chip->adc_tm_hc) { + rc = qpnp_adc_tm_reg_update(chip, + QPNP_ADC_TM_MULTI_MEAS_EN, + sensor_mask, false); + if (rc < 0) { + pr_err("multi meas disable failed\n"); + return rc; + } + } else { + rc = qpnp_adc_tm_reg_update(chip, + QPNP_BTM_Mn_EN(sensor_num), + QPNP_BTM_Mn_MEAS_EN, false); + if (rc < 0) { + pr_err("multi meas disable failed\n"); + return rc; + } + } + + rc = qpnp_adc_tm_enable_if_channel_meas(chip); + if (rc < 0) { + pr_err("re-enabling measurement failed\n"); + return rc; + } + + queue_work(chip->sensor[sensor_num].req_wq, + &chip->sensor[sensor_num].work); + + return rc; +} + +static int qpnp_adc_tm_read_status(struct qpnp_adc_tm_chip *chip) +{ + int rc = 0, sensor_num = 0; if (qpnp_adc_tm_is_valid(chip)) return -ENODEV; + pr_debug("%s\n", __func__); + mutex_lock(&chip->adc->adc_lock); if (!chip->adc_tm_hc) { @@ -2117,159 +2314,36 @@ static int qpnp_adc_tm_read_status(struct qpnp_adc_tm_chip *chip) } } - if (chip->th_info.adc_tm_high_enable) { - sensor_notify_num = chip->th_info.adc_tm_high_enable; - while (i < chip->max_channels_available) { - if ((sensor_notify_num & 0x1) == 1) - sensor_num = i; - sensor_notify_num >>= 1; - i++; - } - - btm_chan_num = chip->sensor[sensor_num].btm_channel_num; - pr_debug("high:sen:%d, hs:0x%x, ls:0x%x, meas_en:0x%x\n", - sensor_num, chip->th_info.adc_tm_high_enable, - chip->th_info.adc_tm_low_enable, - chip->th_info.qpnp_adc_tm_meas_en); - if (!chip->sensor[sensor_num].thermal_node) { - /* For non thermal registered clients - such as usb_id, vbatt, pmic_therm */ - sensor_mask = 1 << sensor_num; - pr_debug("non thermal node - mask:%x\n", sensor_mask); - rc = qpnp_adc_tm_recalib_request_check(chip, - sensor_num, true, ¬ify_check); - if (rc < 0 || !notify_check) { - pr_debug("Calib recheck re-armed rc=%d\n", rc); - chip->th_info.adc_tm_high_enable = 0; + while (sensor_num < chip->max_channels_available) { + if (chip->sensor[sensor_num].high_thr_triggered) { + rc = qpnp_adc_tm_disable_rearm_high_thresholds( + chip, sensor_num); + if (rc) { + pr_err("rearm threshold failed\n"); goto fail; } - } else { - /* Uses the thermal sysfs registered device to disable - the corresponding high voltage threshold which - is triggered by low temp */ - pr_debug("thermal node with mask:%x\n", sensor_mask); - } - list_for_each(thr_list, &chip->sensor[sensor_num].thr_list) { - client_info = list_entry(thr_list, - struct qpnp_adc_thr_client_info, list); - if (client_info->high_thr_set) { - client_info->high_thr_set = false; - client_info->notify_high_thr = true; - if (client_info->state_req_copy == - ADC_TM_HIGH_LOW_THR_ENABLE) - client_info->state_req_copy = - ADC_TM_LOW_THR_ENABLE; - else - client_info->state_req_copy = - ADC_TM_HIGH_THR_DISABLE; - } + chip->sensor[sensor_num].high_thr_triggered = false; } + sensor_num++; } - if (chip->th_info.adc_tm_low_enable) { - sensor_notify_num = chip->th_info.adc_tm_low_enable; - i = 0; - while (i < chip->max_channels_available) { - if ((sensor_notify_num & 0x1) == 1) - sensor_num = i; - sensor_notify_num >>= 1; - i++; - } - - btm_chan_num = chip->sensor[sensor_num].btm_channel_num; - pr_debug("low:sen:%d, hs:0x%x, ls:0x%x, meas_en:0x%x\n", - sensor_num, chip->th_info.adc_tm_high_enable, - chip->th_info.adc_tm_low_enable, - chip->th_info.qpnp_adc_tm_meas_en); - if (!chip->sensor[sensor_num].thermal_node) { - /* For non thermal registered clients - such as usb_id, vbatt, pmic_therm */ - pr_debug("non thermal node - mask:%x\n", sensor_mask); - rc = qpnp_adc_tm_recalib_request_check(chip, - sensor_num, false, ¬ify_check); - if (rc < 0 || !notify_check) { - pr_debug("Calib recheck re-armed rc=%d\n", rc); - chip->th_info.adc_tm_low_enable = 0; - goto fail; - } - sensor_mask = 1 << sensor_num; - rc = qpnp_adc_tm_reg_update(chip, - QPNP_ADC_TM_LOW_THR_INT_EN, - sensor_mask, false); - if (rc < 0) { - pr_err("low threshold int read failed\n"); - goto fail; - } - } else { - /* Uses the thermal sysfs registered device to disable - the corresponding low voltage threshold which - is triggered by high temp */ - pr_debug("thermal node with mask:%x\n", sensor_mask); - rc = qpnp_adc_tm_activate_trip_type( - chip->sensor[sensor_num].tz_dev, - ADC_TM_TRIP_HIGH_WARM, - THERMAL_TRIP_ACTIVATION_DISABLED); - if (rc < 0) { - pr_err("notify error:%d\n", sensor_num); + sensor_num = 0; + while (sensor_num < chip->max_channels_available) { + if (chip->sensor[sensor_num].low_thr_triggered) { + rc = qpnp_adc_tm_disable_rearm_low_thresholds( + chip, sensor_num); + if (rc) { + pr_err("rearm threshold failed\n"); goto fail; } + chip->sensor[sensor_num].low_thr_triggered = false; } - list_for_each(thr_list, &chip->sensor[sensor_num].thr_list) { - client_info = list_entry(thr_list, - struct qpnp_adc_thr_client_info, list); - if (client_info->low_thr_set) { - /* mark the corresponding clients threshold - as not set */ - client_info->low_thr_set = false; - client_info->notify_low_thr = true; - if (client_info->state_req_copy == - ADC_TM_HIGH_LOW_THR_ENABLE) - client_info->state_req_copy = - ADC_TM_HIGH_THR_ENABLE; - else - client_info->state_req_copy = - ADC_TM_LOW_THR_DISABLE; - } - } + sensor_num++; } - qpnp_adc_tm_manage_thresholds(chip, sensor_num, btm_chan_num); - - if (chip->th_info.adc_tm_high_enable || - chip->th_info.adc_tm_low_enable) { - if (!chip->adc_tm_hc) { - rc = qpnp_adc_tm_reg_update(chip, - QPNP_ADC_TM_MULTI_MEAS_EN, - sensor_mask, false); - if (rc < 0) { - pr_err("multi meas disable failed\n"); - goto fail; - } - } else { - rc = qpnp_adc_tm_reg_update(chip, - QPNP_BTM_Mn_EN(sensor_mask), - QPNP_BTM_Mn_MEAS_EN, false); - if (rc < 0) { - pr_err("multi meas disable failed\n"); - goto fail; - } - } - - rc = qpnp_adc_tm_enable_if_channel_meas(chip); - if (rc < 0) { - pr_err("re-enabling measurement failed\n"); - return rc; - } - } else - pr_debug("No threshold status enable %d for high/low??\n", - sensor_mask); - fail: mutex_unlock(&chip->adc->adc_lock); - if (chip->th_info.adc_tm_high_enable || chip->th_info.adc_tm_low_enable) - queue_work(chip->sensor[sensor_num].req_wq, - &chip->sensor[sensor_num].work); if (rc < 0 || (!chip->th_info.adc_tm_high_enable && !chip->th_info.adc_tm_low_enable)) atomic_dec(&chip->wq_cnt); @@ -2290,6 +2364,8 @@ static void qpnp_adc_tm_high_thr_work(struct work_struct *work) chip->adc_vote_enable = false; } + pr_debug("thr:0x%x\n", chip->th_info.adc_tm_high_enable); + rc = qpnp_adc_tm_read_status(chip); if (rc < 0) pr_err("adc-tm high thr work failed\n"); @@ -2393,6 +2469,8 @@ static void qpnp_adc_tm_low_thr_work(struct work_struct *work) chip->adc_vote_enable = false; } + pr_debug("thr:0x%x\n", chip->th_info.adc_tm_low_enable); + rc = qpnp_adc_tm_read_status(chip); if (rc < 0) pr_err("adc-tm low thr work failed\n"); @@ -2483,7 +2561,7 @@ static irqreturn_t qpnp_adc_tm_low_thr_isr(int irq, void *data) static int qpnp_adc_tm_rc_check_sensor_trip(struct qpnp_adc_tm_chip *chip, u8 status_low, u8 status_high, int i, - int sensor_low_notify_num, int sensor_high_notify_num) + int *sensor_low_notify_num, int *sensor_high_notify_num) { int rc = 0; u8 ctl = 0, sensor_mask = 0; @@ -2523,7 +2601,8 @@ static int qpnp_adc_tm_rc_check_sensor_trip(struct qpnp_adc_tm_chip *chip, return IRQ_HANDLED; } } - sensor_low_notify_num |= (status_low & 0x1); + *sensor_low_notify_num |= (status_low & 0x1); + chip->sensor[i].low_thr_triggered = true; } if ((status_high & 0x1) && (ctl & QPNP_BTM_Mn_MEAS_EN) && @@ -2553,7 +2632,8 @@ static int qpnp_adc_tm_rc_check_sensor_trip(struct qpnp_adc_tm_chip *chip, return IRQ_HANDLED; } } - sensor_high_notify_num |= (status_high & 0x1); + *sensor_high_notify_num |= (status_high & 0x1); + chip->sensor[i].high_thr_triggered = true; } } @@ -2590,7 +2670,8 @@ static irqreturn_t qpnp_adc_tm_rc_thr_isr(int irq, void *data) while (i < chip->max_channels_available) { rc = qpnp_adc_tm_rc_check_sensor_trip(chip, status_low, status_high, i, - sensor_low_notify_num, sensor_high_notify_num); + &sensor_low_notify_num, + &sensor_high_notify_num); if (rc) { pr_err("Sensor trip read failed\n"); return IRQ_HANDLED; @@ -2600,14 +2681,15 @@ static irqreturn_t qpnp_adc_tm_rc_thr_isr(int irq, void *data) i++; } - if (sensor_low_notify_num || sensor_high_notify_num) + if (sensor_low_notify_num) { atomic_inc(&chip->wq_cnt); - - if (sensor_low_notify_num) queue_work(chip->low_thr_wq, &chip->trigger_low_thr_work); + } - if (sensor_high_notify_num) + if (sensor_high_notify_num) { + atomic_inc(&chip->wq_cnt); queue_work(chip->high_thr_wq, &chip->trigger_high_thr_work); + } return IRQ_HANDLED; } @@ -2710,6 +2792,7 @@ int32_t qpnp_adc_tm_channel_measure(struct qpnp_adc_tm_chip *chip, channel, scale_type, dt_index); param->gain_num = qpnp_vadc_amux_scaling_ratio[amux_prescaling].num; param->gain_den = qpnp_vadc_amux_scaling_ratio[amux_prescaling].den; + param->adc_tm_hc = chip->adc_tm_hc; chip->adc->amux_prop->amux_channel = channel; chip->adc->amux_prop->decimation = chip->adc->adc_channels[dt_index].adc_decimation; diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index df8c82aa2dd9..e03d3b41c25b 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -39,7 +39,6 @@ #include <linux/kthread.h> #include <net/netlink.h> #include <net/genetlink.h> -#include <linux/suspend.h> #define CREATE_TRACE_POINTS #include <trace/events/thermal.h> @@ -64,8 +63,6 @@ static LIST_HEAD(thermal_governor_list); static DEFINE_MUTEX(thermal_list_lock); static DEFINE_MUTEX(thermal_governor_lock); -static atomic_t in_suspend; - static struct thermal_governor *def_governor; static struct thermal_governor *__find_governor(const char *name) @@ -851,6 +848,10 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip) { enum thermal_trip_type type; + /* Ignore disabled trip points */ + if (test_bit(trip, &tz->trips_disabled)) + return; + tz->ops->get_trip_type(tz, trip, &type); if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT || @@ -957,9 +958,6 @@ void thermal_zone_device_update(struct thermal_zone_device *tz) { int count; - if (atomic_read(&in_suspend)) - return; - if (!tz->ops->get_temp) return; @@ -2247,6 +2245,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, { struct thermal_zone_device *tz; enum thermal_trip_type trip_type; + int trip_temp; int result; int count; int passive = 0; @@ -2318,9 +2317,15 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, goto unregister; for (count = 0; count < trips; count++) { - tz->ops->get_trip_type(tz, count, &trip_type); + if (tz->ops->get_trip_type(tz, count, &trip_type)) + set_bit(count, &tz->trips_disabled); if (trip_type == THERMAL_TRIP_PASSIVE) passive = 1; + if (tz->ops->get_trip_temp(tz, count, &trip_temp)) + set_bit(count, &tz->trips_disabled); + /* Check for bogus trip points */ + if (trip_temp == 0) + set_bit(count, &tz->trips_disabled); } if (!passive) { @@ -2630,36 +2635,6 @@ static void thermal_unregister_governors(void) thermal_gov_power_allocator_unregister(); } -static int thermal_pm_notify(struct notifier_block *nb, - unsigned long mode, void *_unused) -{ - struct thermal_zone_device *tz; - - switch (mode) { - case PM_HIBERNATION_PREPARE: - case PM_RESTORE_PREPARE: - case PM_SUSPEND_PREPARE: - atomic_set(&in_suspend, 1); - break; - case PM_POST_HIBERNATION: - case PM_POST_RESTORE: - case PM_POST_SUSPEND: - atomic_set(&in_suspend, 0); - list_for_each_entry(tz, &thermal_tz_list, node) { - thermal_zone_device_reset(tz); - thermal_zone_device_update(tz); - } - break; - default: - break; - } - return 0; -} - -static struct notifier_block thermal_pm_nb = { - .notifier_call = thermal_pm_notify, -}; - static int __init thermal_init(void) { int result; @@ -2680,11 +2655,6 @@ static int __init thermal_init(void) if (result) goto exit_netlink; - result = register_pm_notifier(&thermal_pm_nb); - if (result) - pr_warn("Thermal: Can not register suspend notifier, return %d\n", - result); - return 0; exit_netlink: @@ -2704,7 +2674,6 @@ error: static void __exit thermal_exit(void) { - unregister_pm_notifier(&thermal_pm_nb); of_thermal_destroy_zones(); genetlink_exit(); class_unregister(&thermal_class); diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 52d82d2ac726..56ccbcefdd85 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -713,22 +713,16 @@ static int size_fifo(struct uart_8250_port *up) */ static unsigned int autoconfig_read_divisor_id(struct uart_8250_port *p) { - unsigned char old_dll, old_dlm, old_lcr; - unsigned int id; + unsigned char old_lcr; + unsigned int id, old_dl; old_lcr = serial_in(p, UART_LCR); serial_out(p, UART_LCR, UART_LCR_CONF_MODE_A); + old_dl = serial_dl_read(p); + serial_dl_write(p, 0); + id = serial_dl_read(p); + serial_dl_write(p, old_dl); - old_dll = serial_in(p, UART_DLL); - old_dlm = serial_in(p, UART_DLM); - - serial_out(p, UART_DLL, 0); - serial_out(p, UART_DLM, 0); - - id = serial_in(p, UART_DLL) | serial_in(p, UART_DLM) << 8; - - serial_out(p, UART_DLL, old_dll); - serial_out(p, UART_DLM, old_dlm); serial_out(p, UART_LCR, old_lcr); return id; diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 6843711774b2..d4ece0e56954 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -70,9 +70,11 @@ #define UART_SPS_CONS_PERIPHERAL 0 #define UART_SPS_PROD_PERIPHERAL 1 -#define IPC_MSM_HS_LOG_PAGES 5 +#define IPC_MSM_HS_LOG_STATE_PAGES 2 +#define IPC_MSM_HS_LOG_USER_PAGES 2 +#define IPC_MSM_HS_LOG_DATA_PAGES 3 #define UART_DMA_DESC_NR 8 -#define BUF_DUMP_SIZE 20 +#define BUF_DUMP_SIZE 32 /* If the debug_mask gets set to FATAL_LEV, * a fatal error has happened and further IPC logging @@ -121,6 +123,11 @@ enum { } \ } while (0) +#define LOG_USR_MSG(ctx, x...) do { \ + if (ctx) \ + ipc_log_string(ctx, x); \ +} while (0) + /* * There are 3 different kind of UART Core available on MSM. * High Speed UART (i.e. Legacy HSUART), GSBI based HSUART @@ -164,6 +171,7 @@ struct msm_hs_tx { struct task_struct *task; struct msm_hs_sps_ep_conn_data cons; struct timer_list tx_timeout_timer; + void *ipc_tx_ctxt; }; struct msm_hs_rx { @@ -181,6 +189,7 @@ struct msm_hs_rx { unsigned long pending_flag; int rx_inx; struct sps_iovec iovec[UART_DMA_DESC_NR]; /* track descriptors */ + void *ipc_rx_ctxt; }; enum buffer_states { NONE_PENDING = 0x0, @@ -214,7 +223,7 @@ struct msm_hs_port { struct clk *pclk; struct msm_hs_tx tx; struct msm_hs_rx rx; - atomic_t clk_count; + atomic_t resource_count; struct msm_hs_wakeup wakeup; struct dentry *loopback_dir; @@ -248,6 +257,7 @@ struct msm_hs_port { bool obs; /* out of band sleep flag */ atomic_t client_req_state; void *ipc_msm_hs_log_ctxt; + void *ipc_msm_hs_pwr_ctxt; int ipc_debug_mask; }; @@ -315,7 +325,7 @@ static int msm_hs_ioctl(struct uart_port *uport, unsigned int cmd, break; } default: { - MSM_HS_DBG("%s():Unknown cmd specified: cmd=%d\n", __func__, + MSM_HS_INFO("%s():Unknown cmd specified: cmd=%d\n", __func__, cmd); ret = -ENOIOCTLCMD; break; @@ -380,7 +390,7 @@ static void msm_hs_clk_bus_unvote(struct msm_hs_port *msm_uport) static void msm_hs_resource_unvote(struct msm_hs_port *msm_uport) { struct uart_port *uport = &(msm_uport->uport); - int rc = atomic_read(&msm_uport->clk_count); + int rc = atomic_read(&msm_uport->resource_count); MSM_HS_DBG("%s(): power usage count %d", __func__, rc); if (rc <= 0) { @@ -388,7 +398,7 @@ static void msm_hs_resource_unvote(struct msm_hs_port *msm_uport) WARN_ON(1); return; } - atomic_dec(&msm_uport->clk_count); + atomic_dec(&msm_uport->resource_count); pm_runtime_mark_last_busy(uport->dev); pm_runtime_put_autosuspend(uport->dev); } @@ -400,12 +410,12 @@ static void msm_hs_resource_vote(struct msm_hs_port *msm_uport) struct uart_port *uport = &(msm_uport->uport); ret = pm_runtime_get_sync(uport->dev); if (ret < 0 || msm_uport->pm_state != MSM_HS_PM_ACTIVE) { - MSM_HS_WARN("%s(): %p runtime PM callback not invoked(%d)", - __func__, uport->dev, ret); + MSM_HS_WARN("%s:%s runtime callback not invoked ret:%d st:%d", + __func__, dev_name(uport->dev), ret, + msm_uport->pm_state); msm_hs_pm_resume(uport->dev); } - - atomic_inc(&msm_uport->clk_count); + atomic_inc(&msm_uport->resource_count); } /* Check if the uport line number matches with user id stored in pdata. @@ -567,23 +577,21 @@ static int sps_rx_disconnect(struct sps_pipe *sps_pipe_handler) return sps_disconnect(sps_pipe_handler); } -static void hex_dump_ipc(struct msm_hs_port *msm_uport, - char *prefix, char *string, int size) +static void hex_dump_ipc(struct msm_hs_port *msm_uport, void *ipc_ctx, + char *prefix, char *string, u64 addr, int size) + { - unsigned char linebuf[512]; - unsigned char firstbuf[40], lastbuf[40]; + char buf[(BUF_DUMP_SIZE * 3) + 2]; + int len = 0; - if ((msm_uport->ipc_debug_mask != DBG_LEV) && (size > BUF_DUMP_SIZE)) { - hex_dump_to_buffer(string, 10, 16, 1, - firstbuf, sizeof(firstbuf), 1); - hex_dump_to_buffer(string + (size - 10), 10, 16, 1, - lastbuf, sizeof(lastbuf), 1); - MSM_HS_INFO("%s : %s...%s", prefix, firstbuf, lastbuf); - } else { - hex_dump_to_buffer(string, size, 16, 1, - linebuf, sizeof(linebuf), 1); - MSM_HS_INFO("%s : %s", prefix, linebuf); - } + len = min(size, BUF_DUMP_SIZE); + /* + * Print upto 32 data bytes, 32 bytes per line, 1 byte at a time and + * don't include the ASCII text at the end of the buffer. + */ + hex_dump_to_buffer(string, len, 32, 1, buf, sizeof(buf), false); + ipc_log_string(ipc_ctx, "%s[0x%.10x:%d] : %s", prefix, + (unsigned int)addr, size, buf); } /* @@ -594,8 +602,8 @@ static void dump_uart_hs_registers(struct msm_hs_port *msm_uport) struct uart_port *uport = &(msm_uport->uport); if (msm_uport->pm_state != MSM_HS_PM_ACTIVE) { - MSM_HS_INFO("%s:Failed clocks are off, clk_count %d", - __func__, atomic_read(&msm_uport->clk_count)); + MSM_HS_INFO("%s:Failed clocks are off, resource_count %d", + __func__, atomic_read(&msm_uport->resource_count)); return; } @@ -757,8 +765,10 @@ static int msm_hs_spsconnect_tx(struct msm_hs_port *msm_uport) unsigned long flags; unsigned int data; - if (tx->flush != FLUSH_SHUTDOWN) + if (tx->flush != FLUSH_SHUTDOWN) { + MSM_HS_ERR("%s:Invalid flush state:%d\n", __func__, tx->flush); return 0; + } /* Establish connection between peripheral and memory endpoint */ ret = sps_connect(sps_pipe_handle, sps_config); @@ -1100,7 +1110,6 @@ static void msm_hs_set_termios(struct uart_port *uport, mutex_lock(&msm_uport->mtx); msm_hs_write(uport, UART_DM_IMR, 0); - MSM_HS_DBG("Entering %s\n", __func__); msm_hs_disable_flow_control(uport, true); /* @@ -1214,10 +1223,10 @@ static void msm_hs_set_termios(struct uart_port *uport, msm_uport->flow_control = true; } msm_hs_write(uport, UART_DM_MR1, data); + MSM_HS_INFO("%s: Cflags 0x%x Baud %u\n", __func__, c_cflag, bps); mutex_unlock(&msm_uport->mtx); - MSM_HS_DBG("Exit %s\n", __func__); msm_hs_resource_unvote(msm_uport); } @@ -1400,9 +1409,6 @@ static void msm_hs_submit_tx_locked(struct uart_port *uport) if (tx_count > left) tx_count = left; - MSM_HS_INFO("%s(): [UART_TX]<%d>\n", __func__, tx_count); - hex_dump_ipc(msm_uport, "HSUART write: ", - &tx_buf->buf[tx_buf->tail], tx_count); src_addr = tx->dma_base + tx_buf->tail; /* Mask the src_addr to align on a cache @@ -1415,6 +1421,8 @@ static void msm_hs_submit_tx_locked(struct uart_port *uport) tx->tx_count = tx_count; + hex_dump_ipc(msm_uport, tx->ipc_tx_ctxt, "Tx", + &tx_buf->buf[tx_buf->tail], (u64)src_addr, tx_count); sps_pipe_handle = tx->cons.pipe_handle; /* Queue transfer request to SPS */ ret = sps_transfer_one(sps_pipe_handle, src_addr, tx_count, @@ -1717,12 +1725,11 @@ static void msm_serial_hs_rx_work(struct kthread_work *work) goto out; rx_count = msm_uport->rx.iovec[msm_uport->rx.rx_inx].size; - - MSM_HS_INFO("%s():[UART_RX]<%d>\n", __func__, rx_count); - hex_dump_ipc(msm_uport, "HSUART Read: ", - (msm_uport->rx.buffer + - (msm_uport->rx.rx_inx * UARTDM_RX_BUF_SIZE)), - rx_count); + hex_dump_ipc(msm_uport, rx->ipc_rx_ctxt, "Rx", + (msm_uport->rx.buffer + + (msm_uport->rx.rx_inx * UARTDM_RX_BUF_SIZE)), + msm_uport->rx.iovec[msm_uport->rx.rx_inx].addr, + rx_count); /* * We are in a spin locked context, spin lock taken at @@ -1733,7 +1740,7 @@ static void msm_serial_hs_rx_work(struct kthread_work *work) &msm_uport->rx.pending_flag) && !test_bit(msm_uport->rx.rx_inx, &msm_uport->rx.queued_flag)) - MSM_HS_ERR("RX INX not set"); + MSM_HS_ERR("%s: RX INX not set", __func__); else if (test_bit(msm_uport->rx.rx_inx, &msm_uport->rx.pending_flag) && !test_bit(msm_uport->rx.rx_inx, @@ -1748,14 +1755,14 @@ static void msm_serial_hs_rx_work(struct kthread_work *work) rx_count); if (retval != rx_count) { - MSM_HS_DBG("%s(): ret %d rx_count %d", + MSM_HS_INFO("%s(): ret %d rx_count %d", __func__, retval, rx_count); msm_uport->rx.buffer_pending |= CHARS_NORMAL | retval << 5 | (rx_count - retval) << 16; } } else - MSM_HS_ERR("Error in inx %d", + MSM_HS_ERR("%s: Error in inx %d", __func__, msm_uport->rx.rx_inx); } @@ -1778,7 +1785,7 @@ static void msm_serial_hs_rx_work(struct kthread_work *work) } out: if (msm_uport->rx.buffer_pending) { - MSM_HS_WARN("tty buffer exhausted. Stalling\n"); + MSM_HS_WARN("%s: tty buffer exhausted. Stalling\n", __func__); schedule_delayed_work(&msm_uport->rx.flip_insert_work , msecs_to_jiffies(RETRY_TIMEOUT)); } @@ -1796,7 +1803,7 @@ static void msm_hs_start_tx_locked(struct uart_port *uport) /* Bail if transfer in progress */ if (tx->flush < FLUSH_STOP || tx->dma_in_flight) { - MSM_HS_DBG("%s(): retry, flush %d, dma_in_flight %d\n", + MSM_HS_INFO("%s(): retry, flush %d, dma_in_flight %d\n", __func__, tx->flush, tx->dma_in_flight); return; } @@ -1826,11 +1833,9 @@ static void msm_hs_sps_tx_callback(struct sps_event_notify *notify) notify->data.transfer.iovec.addr); msm_uport->notify = *notify; - MSM_HS_DBG("%s: ev_id=%d, addr=0x%pa, size=0x%x, flags=0x%x, line=%d\n", - __func__, notify->event_id, &addr, - notify->data.transfer.iovec.size, - notify->data.transfer.iovec.flags, - msm_uport->uport.line); + MSM_HS_INFO("tx_cb: addr=0x%pa, size=0x%x, flags=0x%x\n", + &addr, notify->data.transfer.iovec.size, + notify->data.transfer.iovec.flags); del_timer(&msm_uport->tx.tx_timeout_timer); MSM_HS_DBG("%s(): Queue kthread work", __func__); @@ -1931,9 +1936,8 @@ static void msm_hs_sps_rx_callback(struct sps_event_notify *notify) uport = &(msm_uport->uport); msm_uport->notify = *notify; - MSM_HS_DBG("\n%s: sps ev_id=%d, addr=0x%pa, size=0x%x, flags=0x%x\n", - __func__, notify->event_id, &addr, - notify->data.transfer.iovec.size, + MSM_HS_INFO("rx_cb: addr=0x%pa, size=0x%x, flags=0x%x\n", + &addr, notify->data.transfer.iovec.size, notify->data.transfer.iovec.flags); spin_lock_irqsave(&uport->lock, flags); @@ -1985,13 +1989,13 @@ void msm_hs_set_mctrl_locked(struct uart_port *uport, unsigned int set_rts; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - MSM_HS_DBG("%s()", __func__); if (msm_uport->pm_state != MSM_HS_PM_ACTIVE) { MSM_HS_WARN("%s(): Clocks are off\n", __func__); return; } /* RTS is active low */ set_rts = TIOCM_RTS & mctrl ? 0 : 1; + MSM_HS_INFO("%s: set_rts %d\n", __func__, set_rts); if (set_rts) msm_hs_disable_flow_control(uport, false); @@ -2186,7 +2190,7 @@ static struct msm_hs_port *msm_hs_get_hs_port(int port_index) return NULL; } -void toggle_wakeup_interrupt(struct msm_hs_port *msm_uport) +void enable_wakeup_interrupt(struct msm_hs_port *msm_uport) { unsigned long flags; struct uart_port *uport = &(msm_uport->uport); @@ -2197,7 +2201,6 @@ void toggle_wakeup_interrupt(struct msm_hs_port *msm_uport) return; if (!(msm_uport->wakeup.enabled)) { - MSM_HS_DBG("%s(): Enable Wakeup IRQ", __func__); enable_irq(msm_uport->wakeup.irq); disable_irq(uport->irq); spin_lock_irqsave(&uport->lock, flags); @@ -2205,12 +2208,28 @@ void toggle_wakeup_interrupt(struct msm_hs_port *msm_uport) msm_uport->wakeup.enabled = true; spin_unlock_irqrestore(&uport->lock, flags); } else { + MSM_HS_WARN("%s:Wake up IRQ already enabled", __func__); + } +} + +void disable_wakeup_interrupt(struct msm_hs_port *msm_uport) +{ + unsigned long flags; + struct uart_port *uport = &(msm_uport->uport); + + if (!is_use_low_power_wakeup(msm_uport)) + return; + if (msm_uport->wakeup.freed) + return; + + if (msm_uport->wakeup.enabled) { disable_irq_nosync(msm_uport->wakeup.irq); enable_irq(uport->irq); spin_lock_irqsave(&uport->lock, flags); msm_uport->wakeup.enabled = false; spin_unlock_irqrestore(&uport->lock, flags); - MSM_HS_DBG("%s(): Disable Wakeup IRQ", __func__); + } else { + MSM_HS_WARN("%s:Wake up IRQ already disabled", __func__); } } @@ -2267,6 +2286,7 @@ int msm_hs_request_clock_off(struct uart_port *uport) { struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); int ret = 0; + int client_count = 0; mutex_lock(&msm_uport->mtx); /* @@ -2293,8 +2313,10 @@ int msm_hs_request_clock_off(struct uart_port *uport) atomic_set(&msm_uport->client_req_state, 1); msm_hs_resource_unvote(msm_uport); atomic_dec(&msm_uport->client_count); - MSM_HS_INFO("%s():DISABLE UART CLOCK: ioc %d\n", - __func__, atomic_read(&msm_uport->client_count)); + client_count = atomic_read(&msm_uport->client_count); + LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, + "%s: Client_Count %d\n", __func__, + client_count); exit_request_clock_off: return ret; } @@ -2323,8 +2345,9 @@ int msm_hs_request_clock_on(struct uart_port *uport) msm_hs_resource_vote(UARTDM_TO_MSM(uport)); atomic_inc(&msm_uport->client_count); client_count = atomic_read(&msm_uport->client_count); - MSM_HS_INFO("%s():ENABLE UART CLOCK: ioc %d\n", - __func__, client_count); + LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, + "%s: Client_Count %d\n", __func__, + client_count); /* Clear the flag */ if (msm_uport->obs) @@ -2342,11 +2365,8 @@ static irqreturn_t msm_hs_wakeup_isr(int irq, void *dev) struct uart_port *uport = &msm_uport->uport; struct tty_struct *tty = NULL; - msm_hs_resource_vote(msm_uport); spin_lock_irqsave(&uport->lock, flags); - MSM_HS_DBG("%s(): ignore %d\n", __func__, - msm_uport->wakeup.ignore); if (msm_uport->wakeup.ignore) msm_uport->wakeup.ignore = 0; else @@ -2362,13 +2382,15 @@ static irqreturn_t msm_hs_wakeup_isr(int irq, void *dev) tty_insert_flip_char(tty->port, msm_uport->wakeup.rx_to_inject, TTY_NORMAL); - MSM_HS_DBG("%s(): Inject 0x%x", __func__, - msm_uport->wakeup.rx_to_inject); + hex_dump_ipc(msm_uport, msm_uport->rx.ipc_rx_ctxt, + "Rx Inject", + &msm_uport->wakeup.rx_to_inject, 0, 1); + MSM_HS_INFO("Wakeup ISR.Ignore%d\n", + msm_uport->wakeup.ignore); } } spin_unlock_irqrestore(&uport->lock, flags); - msm_hs_resource_unvote(msm_uport); if (wakeup && msm_uport->wakeup.inject_rx) tty_flip_buffer_push(tty->port); @@ -2396,7 +2418,7 @@ static void msm_hs_unconfig_uart_gpios(struct uart_port *uport) ret = pinctrl_select_state(msm_uport->pinctrl, msm_uport->gpio_state_suspend); if (ret) - MSM_HS_ERR("%s(): Failed to pinctrl set_state", + MSM_HS_ERR("%s():Failed to pinctrl set_state", __func__); } else if (pdata) { if (gpio_is_valid(pdata->uart_tx_gpio)) @@ -2674,6 +2696,8 @@ static int msm_hs_startup(struct uart_port *uport) spin_lock_irqsave(&uport->lock, flags); atomic_set(&msm_uport->client_count, 0); atomic_set(&msm_uport->client_req_state, 0); + LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, + "%s: Client_Count 0\n", __func__); msm_hs_start_rx_locked(uport); spin_unlock_irqrestore(&uport->lock, flags); @@ -3092,17 +3116,19 @@ static void msm_hs_pm_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct msm_hs_port *msm_uport = get_matching_hs_port(pdev); int ret; + int client_count = 0; if (!msm_uport) goto err_suspend; mutex_lock(&msm_uport->mtx); + client_count = atomic_read(&msm_uport->client_count); /* For OBS, don't use wakeup interrupt, set gpio to suspended state */ if (msm_uport->obs) { ret = pinctrl_select_state(msm_uport->pinctrl, msm_uport->gpio_state_suspend); if (ret) - MSM_HS_ERR("%s(): Error selecting suspend state", + MSM_HS_ERR("%s():Error selecting pinctrl suspend state", __func__); } @@ -3111,8 +3137,10 @@ static void msm_hs_pm_suspend(struct device *dev) obs_manage_irq(msm_uport, false); msm_hs_clk_bus_unvote(msm_uport); if (!atomic_read(&msm_uport->client_req_state)) - toggle_wakeup_interrupt(msm_uport); - MSM_HS_DBG("%s(): return suspend\n", __func__); + enable_wakeup_interrupt(msm_uport); + LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, + "%s: PM State Suspended client_count %d\n", __func__, + client_count); mutex_unlock(&msm_uport->mtx); return; err_suspend: @@ -3124,17 +3152,26 @@ static int msm_hs_pm_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct msm_hs_port *msm_uport = get_matching_hs_port(pdev); - int ret; + int ret = 0; + int client_count = 0; - if (!msm_uport) - goto err_resume; + if (!msm_uport) { + dev_err(dev, "%s:Invalid uport\n", __func__); + return -ENODEV; + } mutex_lock(&msm_uport->mtx); + client_count = atomic_read(&msm_uport->client_count); if (msm_uport->pm_state == MSM_HS_PM_ACTIVE) goto exit_pm_resume; if (!atomic_read(&msm_uport->client_req_state)) - toggle_wakeup_interrupt(msm_uport); - msm_hs_clk_bus_vote(msm_uport); + disable_wakeup_interrupt(msm_uport); + ret = msm_hs_clk_bus_vote(msm_uport); + if (ret) { + MSM_HS_ERR("%s:Failed clock vote %d\n", __func__, ret); + dev_err(dev, "%s:Failed clock vote %d\n", __func__, ret); + goto exit_pm_resume; + } obs_manage_irq(msm_uport, true); msm_uport->pm_state = MSM_HS_PM_ACTIVE; msm_hs_resource_on(msm_uport); @@ -3144,17 +3181,15 @@ static int msm_hs_pm_resume(struct device *dev) ret = pinctrl_select_state(msm_uport->pinctrl, msm_uport->gpio_state_active); if (ret) - MSM_HS_ERR("%s(): Error selecting active state", + MSM_HS_ERR("%s():Error selecting active state", __func__); } - MSM_HS_DBG("%s(): return resume\n", __func__); + LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, + "%s:PM State:Active client_count %d\n", __func__, client_count); exit_pm_resume: mutex_unlock(&msm_uport->mtx); - return 0; -err_resume: - pr_err("%s(): invalid uport", __func__); - return 0; + return ret; } #ifdef CONFIG_PM @@ -3174,20 +3209,20 @@ static int msm_hs_pm_sys_suspend_noirq(struct device *dev) * If there is an active clk request or an impending userspace request * fail the suspend callback. */ - clk_cnt = atomic_read(&msm_uport->clk_count); + clk_cnt = atomic_read(&msm_uport->resource_count); client_count = atomic_read(&msm_uport->client_count); - if (clk_cnt || (pm_runtime_enabled(dev) && - !pm_runtime_suspended(dev))) { - MSM_HS_WARN("%s:Fail Suspend.clk_cnt:%d,clnt_count:%d,RPM:%d\n", - __func__, clk_cnt, client_count, - dev->power.runtime_status); + if (msm_uport->pm_state == MSM_HS_PM_ACTIVE) { + MSM_HS_WARN("%s:Fail Suspend.clk_cnt:%d,clnt_count:%d\n", + __func__, clk_cnt, client_count); ret = -EBUSY; goto exit_suspend_noirq; } prev_pwr_state = msm_uport->pm_state; msm_uport->pm_state = MSM_HS_PM_SYS_SUSPENDED; - MSM_HS_DBG("%s(): suspending", __func__); + LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, + "%s:PM State:Sys-Suspended client_count %d\n", __func__, + client_count); exit_suspend_noirq: mutex_unlock(&msm_uport->mtx); return ret; @@ -3207,9 +3242,10 @@ static int msm_hs_pm_sys_resume_noirq(struct device *dev) */ mutex_lock(&msm_uport->mtx); - MSM_HS_DBG("%s(): system resume", __func__); if (msm_uport->pm_state == MSM_HS_PM_SYS_SUSPENDED) msm_uport->pm_state = MSM_HS_PM_SUSPENDED; + LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, + "%s:PM State: Suspended\n", __func__); mutex_unlock(&msm_uport->mtx); return 0; } @@ -3257,6 +3293,7 @@ static int msm_hs_probe(struct platform_device *pdev) int core_irqres, bam_irqres, wakeup_irqres; struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data; unsigned long data; + char name[30]; if (pdev->dev.of_node) { dev_dbg(&pdev->dev, "device tree enabled\n"); @@ -3350,11 +3387,13 @@ static int msm_hs_probe(struct platform_device *pdev) iounmap(uport->membase); return -ENOMEM; } + + memset(name, 0, sizeof(name)); + scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev), + "_state"); msm_uport->ipc_msm_hs_log_ctxt = - ipc_log_context_create(IPC_MSM_HS_LOG_PAGES, - dev_name(msm_uport->uport.dev), 0); - pr_debug("%s: Device name is %s\n", __func__, - dev_name(msm_uport->uport.dev)); + ipc_log_context_create(IPC_MSM_HS_LOG_STATE_PAGES, + name, 0); if (!msm_uport->ipc_msm_hs_log_ctxt) { dev_err(&pdev->dev, "%s: error creating logging context", __func__); @@ -3439,6 +3478,36 @@ static int msm_hs_probe(struct platform_device *pdev) msm_uport->tx.flush = FLUSH_SHUTDOWN; msm_uport->rx.flush = FLUSH_SHUTDOWN; + memset(name, 0, sizeof(name)); + scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev), + "_tx"); + msm_uport->tx.ipc_tx_ctxt = + ipc_log_context_create(IPC_MSM_HS_LOG_DATA_PAGES, name, 0); + if (!msm_uport->tx.ipc_tx_ctxt) + dev_err(&pdev->dev, "%s: error creating tx logging context", + __func__); + + memset(name, 0, sizeof(name)); + scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev), + "_rx"); + msm_uport->rx.ipc_rx_ctxt = ipc_log_context_create( + IPC_MSM_HS_LOG_DATA_PAGES, name, 0); + if (!msm_uport->rx.ipc_rx_ctxt) + dev_err(&pdev->dev, "%s: error creating rx logging context", + __func__); + + memset(name, 0, sizeof(name)); + scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev), + "_pwr"); + msm_uport->ipc_msm_hs_pwr_ctxt = ipc_log_context_create( + IPC_MSM_HS_LOG_USER_PAGES, name, 0); + if (!msm_uport->ipc_msm_hs_pwr_ctxt) + dev_err(&pdev->dev, "%s: error creating usr logging context", + __func__); + + uport->irq = core_irqres; + msm_uport->bam_irq = bam_irqres; + clk_set_rate(msm_uport->clk, msm_uport->uport.uartclk); msm_hs_clk_bus_vote(msm_uport); ret = uartdm_init_port(uport); @@ -3622,9 +3691,9 @@ static void msm_hs_shutdown(struct uart_port *uport) UART_XMIT_SIZE, DMA_TO_DEVICE); msm_hs_resource_unvote(msm_uport); - rc = atomic_read(&msm_uport->clk_count); + rc = atomic_read(&msm_uport->resource_count); if (rc) { - atomic_set(&msm_uport->clk_count, 1); + atomic_set(&msm_uport->resource_count, 1); MSM_HS_WARN("%s(): removing extra vote\n", __func__); msm_hs_resource_unvote(msm_uport); } @@ -3635,6 +3704,8 @@ static void msm_hs_shutdown(struct uart_port *uport) if (atomic_read(&msm_uport->client_count)) { MSM_HS_WARN("%s: Client vote on, forcing to 0\n", __func__); atomic_set(&msm_uport->client_count, 0); + LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, + "%s: Client_Count 0\n", __func__); } msm_hs_unconfig_uart_gpios(uport); MSM_HS_INFO("%s:UART port closed successfully\n", __func__); diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index fa4e23930614..d37fdcc3143c 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1114,6 +1114,9 @@ static int acm_probe(struct usb_interface *intf, if (quirks == NO_UNION_NORMAL) { data_interface = usb_ifnum_to_if(usb_dev, 1); control_interface = usb_ifnum_to_if(usb_dev, 0); + /* we would crash */ + if (!data_interface || !control_interface) + return -ENODEV; goto skip_normal_probe; } diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 56593a9a8726..2057d91d8336 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -502,11 +502,15 @@ static int usb_unbind_interface(struct device *dev) int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void *priv) { - struct device *dev = &iface->dev; + struct device *dev; struct usb_device *udev; int retval = 0; int lpm_disable_error; + if (!iface) + return -ENODEV; + + dev = &iface->dev; if (dev->driver) return -EBUSY; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 1560f3f3e756..84df093639ac 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4277,7 +4277,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, { struct usb_device *hdev = hub->hdev; struct usb_hcd *hcd = bus_to_hcd(hdev->bus); - int i, j, retval; + int retries, operations, retval, i; unsigned delay = HUB_SHORT_RESET_TIME; enum usb_device_speed oldspeed = udev->speed; const char *speed; @@ -4379,7 +4379,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, * first 8 bytes of the device descriptor to get the ep0 maxpacket * value. */ - for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) { + for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) { bool did_new_scheme = false; if (use_new_scheme(udev, retry_counter)) { @@ -4406,7 +4406,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, * 255 is for WUSB devices, we actually need to use * 512 (WUSB1.0[4.8.1]). */ - for (j = 0; j < 3; ++j) { + for (operations = 0; operations < 3; ++operations) { buf->bMaxPacketSize0 = 0; r = usb_control_msg(udev, usb_rcvaddr0pipe(), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, @@ -4426,7 +4426,13 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, r = -EPROTO; break; } - if (r == 0) + /* + * Some devices time out if they are powered on + * when already connected. They need a second + * reset. But only on the first attempt, + * lest we get into a time out/reset loop + */ + if (r == 0 || (r == -ETIMEDOUT && retries == 0)) break; } udev->descriptor.bMaxPacketSize0 = @@ -4458,7 +4464,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, * authorization will assign the final address. */ if (udev->wusb == 0) { - for (j = 0; j < SET_ADDRESS_TRIES; ++j) { + for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) { retval = hub_set_address(udev, devnum); if (retval >= 0) break; @@ -5386,6 +5392,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) } bos = udev->bos; + udev->bos = NULL; for (i = 0; i < SET_CONFIG_TRIES; ++i) { @@ -5478,11 +5485,8 @@ done: usb_set_usb2_hardware_lpm(udev, 1); usb_unlocked_enable_lpm(udev); usb_enable_ltm(udev); - /* release the new BOS descriptor allocated by hub_port_init() */ - if (udev->bos != bos) { - usb_release_bos_descriptor(udev); - udev->bos = bos; - } + usb_release_bos_descriptor(udev); + udev->bos = bos; return 0; re_enumerate: diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 49bed21b1284..4d35de1c14c5 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -92,6 +92,13 @@ MODULE_PARM_DESC(cpu_to_affin, "affin usb irq to this cpu"); #define PIPE3_PHYSTATUS_SW BIT(3) #define PIPE_UTMI_CLK_DIS BIT(8) +#define HS_PHY_CTRL_REG (QSCRATCH_REG_OFFSET + 0x10) +#define UTMI_OTG_VBUS_VALID BIT(20) +#define SW_SESSVLD_SEL BIT(28) + +#define SS_PHY_CTRL_REG (QSCRATCH_REG_OFFSET + 0x30) +#define LANE0_PWR_PRESENT BIT(24) + /* GSI related registers */ #define GSI_TRB_ADDR_BIT_53_MASK (1 << 21) #define GSI_TRB_ADDR_BIT_55_MASK (1 << 23) @@ -1796,6 +1803,36 @@ static void dwc3_msm_bus_vote_w(struct work_struct *w) dev_err(mdwc->dev, "Failed to reset bus bw vote %d\n", ret); } +static void dwc3_set_phy_speed_flags(struct dwc3_msm *mdwc) +{ + struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3); + int i, num_ports; + u32 reg; + + mdwc->hs_phy->flags &= ~(PHY_HSFS_MODE | PHY_LS_MODE); + if (mdwc->in_host_mode) { + reg = dwc3_msm_read_reg(mdwc->base, USB3_HCSPARAMS1); + num_ports = HCS_MAX_PORTS(reg); + for (i = 0; i < num_ports; i++) { + reg = dwc3_msm_read_reg(mdwc->base, + USB3_PORTSC + i*0x10); + if (reg & PORT_PE) { + if (DEV_HIGHSPEED(reg) || DEV_FULLSPEED(reg)) + mdwc->hs_phy->flags |= PHY_HSFS_MODE; + else if (DEV_LOWSPEED(reg)) + mdwc->hs_phy->flags |= PHY_LS_MODE; + } + } + } else { + if (dwc->gadget.speed == USB_SPEED_HIGH || + dwc->gadget.speed == USB_SPEED_FULL) + mdwc->hs_phy->flags |= PHY_HSFS_MODE; + else if (dwc->gadget.speed == USB_SPEED_LOW) + mdwc->hs_phy->flags |= PHY_LS_MODE; + } +} + + static int dwc3_msm_suspend(struct dwc3_msm *mdwc) { int ret, i; @@ -1869,6 +1906,7 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc) /* disable power event irq, hs and ss phy irq is used as wake up src */ disable_irq(mdwc->pwr_event_irq); + dwc3_set_phy_speed_flags(mdwc); /* Suspend HS PHY */ usb_phy_set_suspend(mdwc->hs_phy, 1); @@ -2018,6 +2056,7 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc) mdwc->lpm_flags &= ~MDWC3_SS_PHY_SUSPEND; } + mdwc->hs_phy->flags &= ~(PHY_HSFS_MODE | PHY_LS_MODE); /* Resume HS PHY */ usb_phy_set_suspend(mdwc->hs_phy, 0); @@ -2334,11 +2373,18 @@ static int dwc3_msm_get_clk_gdsc(struct dwc3_msm *mdwc) if (IS_ERR(mdwc->bus_aggr_clk)) mdwc->bus_aggr_clk = NULL; - mdwc->cfg_ahb_clk = devm_clk_get(mdwc->dev, "cfg_ahb_clk"); - if (IS_ERR(mdwc->cfg_ahb_clk)) { - dev_err(mdwc->dev, "failed to get cfg_ahb_clk\n"); - ret = PTR_ERR(mdwc->cfg_ahb_clk); - return ret; + if (of_property_match_string(mdwc->dev->of_node, + "clock-names", "cfg_ahb_clk") >= 0) { + mdwc->cfg_ahb_clk = devm_clk_get(mdwc->dev, "cfg_ahb_clk"); + if (IS_ERR(mdwc->cfg_ahb_clk)) { + ret = PTR_ERR(mdwc->cfg_ahb_clk); + mdwc->cfg_ahb_clk = NULL; + if (ret != -EPROBE_DEFER) + dev_err(mdwc->dev, + "failed to get cfg_ahb_clk ret %d\n", + ret); + return ret; + } } return 0; @@ -3051,6 +3097,25 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on) return 0; } +static void dwc3_override_vbus_status(struct dwc3_msm *mdwc, bool vbus_present) +{ + struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3); + + /* Update OTG VBUS Valid from HSPHY to controller */ + dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, + vbus_present ? UTMI_OTG_VBUS_VALID | SW_SESSVLD_SEL : + UTMI_OTG_VBUS_VALID, + vbus_present ? UTMI_OTG_VBUS_VALID | SW_SESSVLD_SEL : 0); + + /* Update only if Super Speed is supported */ + if (dwc->maximum_speed == USB_SPEED_SUPER) { + /* Update VBUS Valid from SSPHY to controller */ + dwc3_msm_write_readback(mdwc->base, SS_PHY_CTRL_REG, + LANE0_PWR_PRESENT, + vbus_present ? LANE0_PWR_PRESENT : 0); + } +} + /** * dwc3_otg_start_peripheral - bind/unbind the peripheral controller. * @@ -3071,6 +3136,7 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on) dev_dbg(mdwc->dev, "%s: turn on gadget %s\n", __func__, dwc->gadget.name); + dwc3_override_vbus_status(mdwc, true); usb_phy_notify_connect(mdwc->hs_phy, USB_SPEED_HIGH); usb_phy_notify_connect(mdwc->ss_phy, USB_SPEED_SUPER); @@ -3086,6 +3152,7 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on) usb_gadget_vbus_disconnect(&dwc->gadget); usb_phy_notify_disconnect(mdwc->hs_phy, USB_SPEED_HIGH); usb_phy_notify_disconnect(mdwc->ss_phy, USB_SPEED_SUPER); + dwc3_override_vbus_status(mdwc, false); dwc3_usb3_phy_suspend(dwc, false); } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 7087b5744eef..7d97aeb21340 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -723,7 +723,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) * due to stale trbs with HWO bit set from previous composition when update * transfer cmd is issued. */ - if (dep->number > 1) { + if (dep->number > 1 && dep->trb_pool) { memset(&dep->trb_pool[0], 0, sizeof(struct dwc3_trb) * dep->num_trbs); dbg_event(dep->number, "Clr_TRB", 0); @@ -1787,6 +1787,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (is_on) { + dbg_event(0xFF, "Pullup_enable", is_on); if (dwc->revision <= DWC3_REVISION_187A) { reg &= ~DWC3_DCTL_TRGTULST_MASK; reg |= DWC3_DCTL_TRGTULST_RX_DET; @@ -1824,6 +1825,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) dwc->pullups_connected = true; } else { + dbg_event(0xFF, "Pullup_disable", is_on); dwc3_gadget_disable_irq(dwc); __dwc3_gadget_ep_disable(dwc->eps[0]); __dwc3_gadget_ep_disable(dwc->eps[1]); @@ -1849,8 +1851,15 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) break; } timeout--; - if (!timeout) + if (!timeout) { + dev_err(dwc->dev, "failed to %s controller\n", + is_on ? "start" : "stop"); + if (is_on) + dbg_event(0xFF, "STARTTOUT", reg); + else + dbg_event(0xFF, "STOPTOUT", reg); return -ETIMEDOUT; + } udelay(1); } while (1); diff --git a/drivers/usb/gadget/function/f_cdev.c b/drivers/usb/gadget/function/f_cdev.c index 0a9a3afd72dd..b22ea656367e 100644 --- a/drivers/usb/gadget/function/f_cdev.c +++ b/drivers/usb/gadget/function/f_cdev.c @@ -529,6 +529,14 @@ static int usb_cser_notify(struct f_cdev *port, u8 type, u16 value, const unsigned len = sizeof(*notify) + length; void *buf; int status; + unsigned long flags; + + spin_lock_irqsave(&port->port_lock, flags); + if (!port->is_connected) { + spin_unlock_irqrestore(&port->port_lock, flags); + pr_debug("%s: port disconnected\n", __func__); + return -ENODEV; + } req = port->port_usb.notify_req; port->port_usb.notify_req = NULL; @@ -544,7 +552,9 @@ static int usb_cser_notify(struct f_cdev *port, u8 type, u16 value, notify->wValue = cpu_to_le16(value); notify->wIndex = cpu_to_le16(port->port_usb.data_id); notify->wLength = cpu_to_le16(length); + /* 2 byte data copy */ memcpy(buf, data, length); + spin_unlock_irqrestore(&port->port_lock, flags); status = usb_ep_queue(ep, req, GFP_ATOMIC); if (status < 0) { diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index 468a7bcd8dbd..5612645d7237 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -256,8 +256,14 @@ static int ipa_connect_channels(struct gsi_data_port *d_port) in_params->dir = GSI_CHAN_DIR_FROM_GSI; in_params->xfer_ring_len = gsi_channel_info.xfer_ring_len; in_params->xfer_ring_base_addr = gsi_channel_info.xfer_ring_base_addr; - in_params->xfer_scratch.last_trb_addr = - d_port->in_last_trb_addr = gsi_channel_info.last_trb_addr; + in_params->xfer_scratch.last_trb_addr_iova = + gsi_channel_info.last_trb_addr; + in_params->xfer_ring_base_addr = in_params->xfer_ring_base_addr_iova = + gsi_channel_info.xfer_ring_base_addr; + in_params->data_buff_base_len = d_port->in_request.buf_len * + d_port->in_request.num_bufs; + in_params->data_buff_base_addr = in_params->data_buff_base_addr_iova = + d_port->in_request.dma; in_params->xfer_scratch.const_buffer_size = gsi_channel_info.const_buffer_size; in_params->xfer_scratch.depcmd_low_addr = @@ -289,8 +295,14 @@ static int ipa_connect_channels(struct gsi_data_port *d_port) out_params->xfer_ring_len = gsi_channel_info.xfer_ring_len; out_params->xfer_ring_base_addr = + out_params->xfer_ring_base_addr_iova = gsi_channel_info.xfer_ring_base_addr; - out_params->xfer_scratch.last_trb_addr = + out_params->data_buff_base_len = d_port->out_request.buf_len * + d_port->out_request.num_bufs; + out_params->data_buff_base_addr = + out_params->data_buff_base_addr_iova = + d_port->out_request.dma; + out_params->xfer_scratch.last_trb_addr_iova = gsi_channel_info.last_trb_addr; out_params->xfer_scratch.const_buffer_size = gsi_channel_info.const_buffer_size; @@ -2787,7 +2799,7 @@ static ssize_t gsi_info_show(struct config_item *item, char *page) ipa_chnl_params->xfer_scratch.depcmd_low_addr); len += scnprintf(buf + len, PAGE_SIZE - len, "%25s %10x\n", "IN LastTRB Addr Off: ", - ipa_chnl_params->xfer_scratch.last_trb_addr); + ipa_chnl_params->xfer_scratch.last_trb_addr_iova); len += scnprintf(buf + len, PAGE_SIZE - len, "%25s %10u\n", "IN Buffer Size: ", ipa_chnl_params->xfer_scratch.const_buffer_size); @@ -2821,7 +2833,7 @@ static ssize_t gsi_info_show(struct config_item *item, char *page) ipa_chnl_params->xfer_scratch.depcmd_low_addr); len += scnprintf(buf + len, PAGE_SIZE - len, "%25s %10x\n", "OUT LastTRB Addr Off: ", - ipa_chnl_params->xfer_scratch.last_trb_addr); + ipa_chnl_params->xfer_scratch.last_trb_addr_iova); len += scnprintf(buf + len, PAGE_SIZE - len, "%25s %10u\n", "OUT Buffer Size: ", ipa_chnl_params->xfer_scratch.const_buffer_size); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index c2d65206ec6c..6c47c26b5df7 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -299,6 +299,7 @@ static void xhci_pci_remove(struct pci_dev *dev) struct xhci_hcd *xhci; xhci = hcd_to_xhci(pci_get_drvdata(dev)); + xhci->xhc_state |= XHCI_STATE_REMOVING; if (xhci->shared_hcd) { usb_remove_hcd(xhci->shared_hcd); usb_put_hcd(xhci->shared_hcd); diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 8125a8f96311..3e49861a09a2 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -284,6 +284,7 @@ static int xhci_plat_remove(struct platform_device *dev) pm_runtime_disable(&dev->dev); device_remove_file(&dev->dev, &dev_attr_config_imod); + xhci->xhc_state |= XHCI_STATE_REMOVING; usb_remove_hcd(xhci->shared_hcd); usb_phy_shutdown(hcd->usb_phy); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index db0f0831b94f..2b63969c2bbf 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -4008,7 +4008,8 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, int reserved_trbs = xhci->cmd_ring_reserved_trbs; int ret; - if (xhci->xhc_state) { + if ((xhci->xhc_state & XHCI_STATE_DYING) || + (xhci->xhc_state & XHCI_STATE_HALTED)) { xhci_dbg(xhci, "xHCI dying or halted, can't queue_command\n"); return -ESHUTDOWN; } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 42f74d55e3bd..dd7669331d00 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -155,7 +155,8 @@ static int xhci_start(struct xhci_hcd *xhci) "waited %u microseconds.\n", XHCI_MAX_HALT_USEC); if (!ret) - xhci->xhc_state &= ~(XHCI_STATE_HALTED | XHCI_STATE_DYING); + /* clear state flags. Including dying, halted or removing */ + xhci->xhc_state = 0; return ret; } @@ -2762,7 +2763,8 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) if (ret <= 0) return ret; xhci = hcd_to_xhci(hcd); - if (xhci->xhc_state & XHCI_STATE_DYING) + if ((xhci->xhc_state & XHCI_STATE_DYING) || + (xhci->xhc_state & XHCI_STATE_REMOVING)) return -ENODEV; xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev); @@ -3809,7 +3811,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, mutex_lock(&xhci->mutex); - if (xhci->xhc_state) /* dying or halted */ + if (xhci->xhc_state) /* dying, removing or halted */ goto out; if (!udev->slot_id) { diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index a6e94e029a10..dc03aac4f88a 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1604,6 +1604,7 @@ struct xhci_hcd { */ #define XHCI_STATE_DYING (1 << 0) #define XHCI_STATE_HALTED (1 << 1) +#define XHCI_STATE_REMOVING (1 << 2) /* Statistics */ int error_bitmask; unsigned int quirks; diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index c6bfd13f6c92..1950e87b4219 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -787,6 +787,12 @@ static int iowarrior_probe(struct usb_interface *interface, iface_desc = interface->cur_altsetting; dev->product_id = le16_to_cpu(udev->descriptor.idProduct); + if (iface_desc->desc.bNumEndpoints < 1) { + dev_err(&interface->dev, "Invalid number of endpoints\n"); + retval = -EINVAL; + goto error; + } + /* set up the endpoint information */ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index b7b978b8d306..f29c275c490b 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -16,12 +16,13 @@ #include <linux/list.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/platform_device.h> +#include <linux/of_platform.h> #include <linux/power_supply.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/workqueue.h> #include <linux/extcon.h> +#include <linux/usb/usbpd.h> #include "usbpd.h" enum usbpd_state { @@ -119,10 +120,13 @@ enum usbpd_data_msg_type { MSG_VDM = 0xF, }; -enum plug_orientation { - ORIENTATION_NONE, - ORIENTATION_CC1, - ORIENTATION_CC2, +enum vdm_state { + VDM_NONE, + DISCOVERED_ID, + DISCOVERED_SVIDS, + DISCOVERED_MODES, + MODE_ENTERED, + MODE_EXITED, }; static void *usbpd_ipc_log; @@ -162,6 +166,7 @@ static void *usbpd_ipc_log; #define PS_HARD_RESET_TIME 35 #define PS_SOURCE_ON 400 #define PS_SOURCE_OFF 900 +#define VDM_BUSY_TIME 50 #define PD_CAPS_COUNT 50 @@ -205,6 +210,33 @@ static void *usbpd_ipc_log; #define PD_SRC_PDO_VAR_BATT_MIN_VOLT(pdo) (((pdo) >> 10) & 0x3FF) #define PD_SRC_PDO_VAR_BATT_MAX(pdo) ((pdo) & 0x3FF) +/* Vendor Defined Messages */ +#define MAX_CRC_RECEIVE_TIME 9 /* ~(2 * tReceive_max(1.1ms) * # retry 4) */ +#define MAX_VDM_RESPONSE_TIME 60 /* 2 * tVDMSenderResponse_max(30ms) */ +#define MAX_VDM_BUSY_TIME 100 /* 2 * tVDMBusy (50ms) */ + +/* VDM header is the first 32-bit object following the 16-bit PD header */ +#define VDM_HDR_SVID(hdr) ((hdr) >> 16) +#define VDM_HDR_TYPE(hdr) ((hdr) & 0x8000) +#define VDM_HDR_CMD_TYPE(hdr) (((hdr) >> 6) & 0x3) +#define VDM_HDR_CMD(hdr) ((hdr) & 0x1f) + +#define SVDM_HDR(svid, ver, obj, cmd_type, cmd) \ + (((svid) << 16) | (1 << 15) | ((ver) << 13) \ + | ((obj) << 8) | ((cmd_type) << 6) | (cmd)) + +/* discover id response vdo bit fields */ +#define ID_HDR_USB_HOST BIT(31) +#define ID_HDR_USB_DEVICE BIT(30) +#define ID_HDR_MODAL_OPR BIT(26) +#define ID_HDR_PRODUCT_TYPE(n) ((n) >> 27) +#define ID_HDR_PRODUCT_PER_MASK (2 << 27) +#define ID_HDR_PRODUCT_HUB 1 +#define ID_HDR_PRODUCT_PER 2 +#define ID_HDR_PRODUCT_AMA 5 +#define ID_HDR_VID 0x05c6 /* qcom */ +#define PROD_VDO_PID 0x0a00 /* TBD */ + static int min_sink_current = 900; module_param(min_sink_current, int, S_IRUSR | S_IWUSR); @@ -217,6 +249,12 @@ static const u32 default_snk_caps[] = { 0x2601905A, /* 5V @ 900mA */ 0x0002D096, /* 9V @ 1.5A */ 0x0003C064 }; /* 12V @ 1A */ +struct vdm_tx { + u32 data[7]; + int size; + struct list_head entry; +}; + struct usbpd { struct device dev; struct workqueue_struct *wq; @@ -265,6 +303,12 @@ struct usbpd { int caps_count; int hard_reset_count; + enum vdm_state vdm_state; + u16 *discovered_svids; + struct vdm_tx *vdm_tx_retry; + struct list_head vdm_tx_queue; + struct list_head svid_handlers; + struct list_head instance; }; @@ -280,7 +324,7 @@ static const unsigned int usbpd_extcon_cable[] = { /* EXTCON_USB and EXTCON_USB_HOST are mutually exclusive */ static const u32 usbpd_extcon_exclusive[] = {0x3, 0}; -static enum plug_orientation usbpd_get_plug_orientation(struct usbpd *pd) +enum plug_orientation usbpd_get_plug_orientation(struct usbpd *pd) { int ret; union power_supply_propval val; @@ -292,6 +336,7 @@ static enum plug_orientation usbpd_get_plug_orientation(struct usbpd *pd) return val.intval; } +EXPORT_SYMBOL(usbpd_get_plug_orientation); static bool is_cable_flipped(struct usbpd *pd) { @@ -329,6 +374,17 @@ static int set_power_role(struct usbpd *pd, enum power_role pr) POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val); } +static struct usbpd_svid_handler *find_svid_handler(struct usbpd *pd, u16 svid) +{ + struct usbpd_svid_handler *handler; + + list_for_each_entry(handler, &pd->svid_handlers, entry) + if (svid == handler->svid) + return handler; + + return NULL; +} + static int pd_send_msg(struct usbpd *pd, u8 hdr_type, const u32 *data, size_t num_data, enum pd_msg_type type) { @@ -604,9 +660,17 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) break; case PE_SRC_READY: - if (pd->current_dr == DR_DFP) - extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 1); pd->in_explicit_contract = true; + if (pd->current_dr == DR_DFP) { + if (pd->vdm_state == VDM_NONE) + usbpd_send_svdm(pd, USBPD_SID, + USBPD_SVDM_DISCOVER_IDENTITY, + SVDM_CMD_TYPE_INITIATOR, 0, + NULL, 0); + + extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 1); + } + kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); break; @@ -669,12 +733,13 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->current_dr = DR_UFP; if (pd->psy_type == POWER_SUPPLY_TYPE_USB || - pd->psy_type == POWER_SUPPLY_TYPE_USB_CDP) + pd->psy_type == POWER_SUPPLY_TYPE_USB_CDP) { extcon_set_cable_state_(pd->extcon, EXTCON_USB_CC, is_cable_flipped(pd)); extcon_set_cable_state_(pd->extcon, EXTCON_USB, 1); + } } pd->rx_msg_len = 0; @@ -799,6 +864,315 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) } } +int usbpd_register_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr) +{ + if (find_svid_handler(pd, hdlr->svid)) { + usbpd_err(&pd->dev, "SVID 0x%04x already registered\n", + hdlr->svid); + return -EINVAL; + } + + usbpd_dbg(&pd->dev, "registered handler for SVID 0x%04x\n", hdlr->svid); + + list_add_tail(&hdlr->entry, &pd->svid_handlers); + + /* already connected with this SVID discovered? */ + if (pd->vdm_state >= DISCOVERED_SVIDS) { + u16 *psvid; + + for (psvid = pd->discovered_svids; *psvid; psvid++) { + if (*psvid == hdlr->svid) { + if (hdlr->connect) + hdlr->connect(hdlr); + break; + } + } + } + + return 0; +} +EXPORT_SYMBOL(usbpd_register_svid); + +void usbpd_unregister_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr) +{ + list_del_init(&hdlr->entry); +} +EXPORT_SYMBOL(usbpd_unregister_svid); + +int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, int num_vdos) +{ + struct vdm_tx *vdm_tx; + + if (!pd->in_explicit_contract) + return -EBUSY; + + vdm_tx = kzalloc(sizeof(*vdm_tx), GFP_KERNEL); + if (!vdm_tx) + return -ENOMEM; + + vdm_tx->data[0] = vdm_hdr; + memcpy(&vdm_tx->data[1], vdos, num_vdos * sizeof(u32)); + vdm_tx->size = num_vdos + 1; /* include the header */ + + /* VDM will get sent in PE_SRC/SNK_READY state handling */ + list_add_tail(&vdm_tx->entry, &pd->vdm_tx_queue); + queue_delayed_work(pd->wq, &pd->sm_work, 0); + + return 0; +} +EXPORT_SYMBOL(usbpd_send_vdm); + +int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd, + enum usbpd_svdm_cmd_type cmd_type, int obj_pos, + const u32 *vdos, int num_vdos) +{ + u32 svdm_hdr = SVDM_HDR(svid, 0, obj_pos, cmd_type, cmd); + + usbpd_dbg(&pd->dev, "VDM tx: svid:%x cmd:%x cmd_type:%x svdm_hdr:%x\n", + svid, cmd, cmd_type, svdm_hdr); + + return usbpd_send_vdm(pd, svdm_hdr, vdos, num_vdos); +} +EXPORT_SYMBOL(usbpd_send_svdm); + +static void handle_vdm_rx(struct usbpd *pd) +{ + u32 vdm_hdr = pd->rx_payload[0]; + u32 *vdos = &pd->rx_payload[1]; + u16 svid = VDM_HDR_SVID(vdm_hdr); + u16 *psvid; + u8 i, num_vdos = pd->rx_msg_len - 1; /* num objects minus header */ + u8 cmd = VDM_HDR_CMD(vdm_hdr); + u8 cmd_type = VDM_HDR_CMD_TYPE(vdm_hdr); + struct usbpd_svid_handler *handler; + + usbpd_dbg(&pd->dev, "VDM rx: svid:%x cmd:%x cmd_type:%x vdm_hdr:%x\n", + svid, cmd, cmd_type, vdm_hdr); + + /* if it's a supported SVID, pass the message to the handler */ + handler = find_svid_handler(pd, svid); + + /* Unstructured VDM */ + if (!VDM_HDR_TYPE(vdm_hdr)) { + if (handler && handler->vdm_received) + handler->vdm_received(handler, vdm_hdr, vdos, num_vdos); + return; + } + + if (handler && handler->svdm_received) + handler->svdm_received(handler, cmd, cmd_type, vdos, num_vdos); + + switch (cmd_type) { + case SVDM_CMD_TYPE_INITIATOR: + if (cmd == USBPD_SVDM_DISCOVER_IDENTITY) { + u32 tx_vdos[3] = { + ID_HDR_USB_HOST | ID_HDR_USB_DEVICE | + ID_HDR_PRODUCT_PER_MASK | ID_HDR_VID, + 0x0, /* TBD: Cert Stat VDO */ + (PROD_VDO_PID << 16), + /* TBD: Get these from gadget */ + }; + + usbpd_send_svdm(pd, USBPD_SID, cmd, + SVDM_CMD_TYPE_RESP_ACK, 0, tx_vdos, 3); + } else { + usbpd_send_svdm(pd, USBPD_SID, cmd, + SVDM_CMD_TYPE_RESP_NAK, 0, NULL, 0); + } + break; + + case SVDM_CMD_TYPE_RESP_ACK: + switch (cmd) { + case USBPD_SVDM_DISCOVER_IDENTITY: + if (svid != USBPD_SID) { + usbpd_err(&pd->dev, "invalid VID:0x%x\n", svid); + break; + } + + kfree(pd->vdm_tx_retry); + pd->vdm_tx_retry = NULL; + + pd->vdm_state = DISCOVERED_ID; + usbpd_send_svdm(pd, USBPD_SID, + USBPD_SVDM_DISCOVER_SVIDS, + SVDM_CMD_TYPE_INITIATOR, 0, NULL, 0); + break; + + case USBPD_SVDM_DISCOVER_SVIDS: + if (svid != USBPD_SID) { + usbpd_err(&pd->dev, "invalid VID:0x%x\n", svid); + break; + } + + pd->vdm_state = DISCOVERED_SVIDS; + + kfree(pd->vdm_tx_retry); + pd->vdm_tx_retry = NULL; + + kfree(pd->discovered_svids); + + /* TODO: handle > 12 SVIDs */ + pd->discovered_svids = kzalloc((2 * num_vdos + 1) * + sizeof(u16), + GFP_KERNEL); + if (!pd->discovered_svids) { + usbpd_err(&pd->dev, "unable to allocate SVIDs\n"); + break; + } + + /* convert 32-bit VDOs to list of 16-bit SVIDs */ + psvid = pd->discovered_svids; + for (i = 0; i < num_vdos * 2; i++) { + /* + * Within each 32-bit VDO, + * SVID[i]: upper 16-bits + * SVID[i+1]: lower 16-bits + * where i is even. + */ + if (!(i & 1)) + svid = vdos[i >> 1] >> 16; + else + svid = vdos[i >> 1] & 0xFFFF; + + /* + * There are some devices that incorrectly + * swap the order of SVIDs within a VDO. So in + * case of an odd-number of SVIDs it could end + * up with SVID[i] as 0 while SVID[i+1] is + * non-zero. Just skip over the zero ones. + */ + if (svid) { + usbpd_dbg(&pd->dev, "Discovered SVID: 0x%04x\n", + svid); + *psvid++ = svid; + + /* if SVID supported notify handler */ + handler = find_svid_handler(pd, svid); + if (handler && handler->connect) + handler->connect(handler); + } + } + + break; + + case USBPD_SVDM_DISCOVER_MODES: + usbpd_info(&pd->dev, "SVID:0x%04x VDM Modes discovered\n", + svid); + pd->vdm_state = DISCOVERED_MODES; + break; + + case USBPD_SVDM_ENTER_MODE: + usbpd_info(&pd->dev, "SVID:0x%04x VDM Mode entered\n", + svid); + pd->vdm_state = MODE_ENTERED; + kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); + break; + + case USBPD_SVDM_EXIT_MODE: + usbpd_info(&pd->dev, "SVID:0x%04x VDM Mode exited\n", + svid); + pd->vdm_state = MODE_EXITED; + kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); + break; + + default: + break; + } + break; + + case SVDM_CMD_TYPE_RESP_NAK: + usbpd_info(&pd->dev, "VDM NAK received for SVID:0x%04x command:%d\n", + svid, cmd); + break; + + case SVDM_CMD_TYPE_RESP_BUSY: + switch (cmd) { + case USBPD_SVDM_DISCOVER_IDENTITY: + case USBPD_SVDM_DISCOVER_SVIDS: + if (!pd->vdm_tx_retry) { + usbpd_err(&pd->dev, "Discover command %d VDM was unexpectedly freed\n", + cmd); + break; + } + + /* wait tVDMBusy, then retry */ + list_move(&pd->vdm_tx_retry->entry, &pd->vdm_tx_queue); + pd->vdm_tx_retry = NULL; + queue_delayed_work(pd->wq, &pd->sm_work, + msecs_to_jiffies(VDM_BUSY_TIME)); + break; + default: + break; + } + break; + } +} + +static void handle_vdm_tx(struct usbpd *pd) +{ + int ret; + + /* only send one VDM at a time */ + if (!list_empty(&pd->vdm_tx_queue)) { + struct vdm_tx *vdm_tx = list_first_entry(&pd->vdm_tx_queue, + struct vdm_tx, entry); + u32 vdm_hdr = vdm_tx->data[0]; + + ret = pd_send_msg(pd, MSG_VDM, vdm_tx->data, vdm_tx->size, + SOP_MSG); + if (ret) { + usbpd_err(&pd->dev, "Error sending VDM command %d\n", + VDM_HDR_CMD(vdm_tx->data[0])); + usbpd_set_state(pd, pd->current_pr == PR_SRC ? + PE_SRC_SEND_SOFT_RESET : + PE_SNK_SEND_SOFT_RESET); + + /* retry when hitting PE_SRC/SNK_Ready again */ + return; + } + + list_del(&vdm_tx->entry); + + /* + * special case: keep initiated Discover ID/SVIDs + * around in case we need to re-try when receiving BUSY + */ + if (VDM_HDR_TYPE(vdm_hdr) && + VDM_HDR_CMD_TYPE(vdm_hdr) == SVDM_CMD_TYPE_INITIATOR && + VDM_HDR_CMD(vdm_hdr) <= USBPD_SVDM_DISCOVER_SVIDS) { + if (pd->vdm_tx_retry) { + usbpd_err(&pd->dev, "Previous Discover VDM command %d not ACKed/NAKed\n", + VDM_HDR_CMD(pd->vdm_tx_retry->data[0])); + kfree(pd->vdm_tx_retry); + } + pd->vdm_tx_retry = vdm_tx; + } else { + kfree(vdm_tx); + } + } +} + +static void reset_vdm_state(struct usbpd *pd) +{ + struct usbpd_svid_handler *handler; + + pd->vdm_state = VDM_NONE; + list_for_each_entry(handler, &pd->svid_handlers, entry) + if (handler->disconnect) + handler->disconnect(handler); + kfree(pd->vdm_tx_retry); + pd->vdm_tx_retry = NULL; + kfree(pd->discovered_svids); + pd->discovered_svids = NULL; + while (!list_empty(&pd->vdm_tx_queue)) { + struct vdm_tx *vdm_tx = + list_first_entry(&pd->vdm_tx_queue, + struct vdm_tx, entry); + list_del(&vdm_tx->entry); + kfree(vdm_tx); + } +} + static void dr_swap(struct usbpd *pd) { if (pd->current_dr == DR_DFP) { @@ -813,6 +1187,12 @@ static void dr_swap(struct usbpd *pd) is_cable_flipped(pd)); extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 1); pd->current_dr = DR_DFP; + + if (pd->vdm_state == VDM_NONE) + usbpd_send_svdm(pd, USBPD_SID, + USBPD_SVDM_DISCOVER_IDENTITY, + SVDM_CMD_TYPE_INITIATOR, 0, + NULL, 0); } pd_phy_update_roles(pd->current_dr, pd->current_pr); @@ -873,6 +1253,8 @@ static void usbpd_sm(struct work_struct *w) pd->current_pr = PR_NONE; pd->current_dr = DR_NONE; + reset_vdm_state(pd); + /* Set CC back to DRP toggle */ val.intval = POWER_SUPPLY_TYPEC_PR_DUAL; power_supply_set_property(pd->usb_psy, @@ -884,6 +1266,8 @@ static void usbpd_sm(struct work_struct *w) /* Hard reset? */ if (pd->hard_reset) { + reset_vdm_state(pd); + if (pd->current_pr == PR_SINK) usbpd_set_state(pd, PE_SNK_TRANSITION_TO_DEFAULT); else @@ -1001,6 +1385,11 @@ static void usbpd_sm(struct work_struct *w) pd->rdo = pd->rx_payload[0]; usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY); } else if (ctrl_recvd == MSG_DR_SWAP) { + if (pd->vdm_state == MODE_ENTERED) { + usbpd_set_state(pd, PE_SRC_HARD_RESET); + break; + } + ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Accept\n"); @@ -1022,12 +1411,18 @@ static void usbpd_sm(struct work_struct *w) pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF; queue_delayed_work(pd->wq, &pd->sm_work, 0); break; + } else { + if (data_recvd == MSG_VDM) + handle_vdm_rx(pd); + else + handle_vdm_tx(pd); } break; case PE_SRC_HARD_RESET: pd_send_hard_reset(pd); pd->in_explicit_contract = false; + reset_vdm_state(pd); msleep(PS_HARD_RESET_TIME); usbpd_set_state(pd, PE_SRC_TRANSITION_TO_DEFAULT); @@ -1133,6 +1528,11 @@ static void usbpd_sm(struct work_struct *w) usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); } } else if (ctrl_recvd == MSG_DR_SWAP) { + if (pd->vdm_state == MODE_ENTERED) { + usbpd_set_state(pd, PE_SNK_HARD_RESET); + break; + } + ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Accept\n"); @@ -1166,6 +1566,11 @@ static void usbpd_sm(struct work_struct *w) queue_delayed_work(pd->wq, &pd->sm_work, msecs_to_jiffies(PS_SOURCE_OFF)); break; + } else { + if (data_recvd == MSG_VDM) + handle_vdm_rx(pd); + else + handle_vdm_tx(pd); } break; @@ -1218,6 +1623,7 @@ static void usbpd_sm(struct work_struct *w) pd_send_hard_reset(pd); pd->in_explicit_contract = false; + reset_vdm_state(pd); usbpd_set_state(pd, PE_SNK_TRANSITION_TO_DEFAULT); break; @@ -1480,6 +1886,7 @@ static int usbpd_uevent(struct device *dev, struct kobj_uevent_env *env) add_uevent_var(env, "RDO=%08x", pd->rdo); add_uevent_var(env, "CONTRACT=%s", pd->in_explicit_contract ? "explicit" : "implicit"); + add_uevent_var(env, "ALT_MODE=%d", pd->vdm_state == MODE_ENTERED); return 0; } @@ -1782,6 +2189,61 @@ static struct class usbpd_class = { .dev_groups = usbpd_groups, }; +static int match_usbpd_device(struct device *dev, const void *data) +{ + return dev->parent == data; +} + +static void devm_usbpd_put(struct device *dev, void *res) +{ + struct usbpd **ppd = res; + + put_device(&(*ppd)->dev); +} + +struct usbpd *devm_usbpd_get_by_phandle(struct device *dev, const char *phandle) +{ + struct usbpd **ptr, *pd = NULL; + struct device_node *pd_np; + struct platform_device *pdev; + struct device *pd_dev; + + if (!dev->of_node) + return ERR_PTR(-ENODEV); + + pd_np = of_parse_phandle(dev->of_node, phandle, 0); + if (!pd_np) + return ERR_PTR(-ENODEV); + + pdev = of_find_device_by_node(pd_np); + if (!pdev) + return ERR_PTR(-ENODEV); + + pd_dev = class_find_device(&usbpd_class, NULL, &pdev->dev, + match_usbpd_device); + if (!pd_dev) { + platform_device_put(pdev); + return ERR_PTR(-ENODEV); + } + + ptr = devres_alloc(devm_usbpd_put, sizeof(*ptr), GFP_KERNEL); + if (!ptr) { + put_device(pd_dev); + platform_device_put(pdev); + return ERR_PTR(-ENOMEM); + } + + pd = dev_get_drvdata(pd_dev); + if (!pd) + return ERR_PTR(-ENODEV); + + *ptr = pd; + devres_add(dev, ptr); + + return pd; +} +EXPORT_SYMBOL(devm_usbpd_get_by_phandle); + static int num_pd_instances; /** @@ -1869,6 +2331,9 @@ struct usbpd *usbpd_create(struct device *parent) pd->current_dr = DR_NONE; list_add_tail(&pd->instance, &_usbpd); + INIT_LIST_HEAD(&pd->vdm_tx_queue); + INIT_LIST_HEAD(&pd->svid_handlers); + /* force read initial power_supply values */ psy_changed(&pd->psy_nb, PSY_EVENT_PROP_CHANGED, pd->usb_psy); diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c index 1bc6b6ba4517..dd8149ef097d 100644 --- a/drivers/usb/phy/phy-msm-qusb-v2.c +++ b/drivers/usb/phy/phy-msm-qusb-v2.c @@ -54,10 +54,6 @@ #define QUSB2PHY_PORT_TUNE2 0x240 -#define HS_PHY_CTRL_REG 0x10 -#define UTMI_OTG_VBUS_VALID BIT(20) -#define SW_SESSVLD_SEL BIT(28) - #define QUSB2PHY_1P8_VOL_MIN 1800000 /* uV */ #define QUSB2PHY_1P8_VOL_MAX 1800000 /* uV */ #define QUSB2PHY_1P8_HPM_LOAD 30000 /* uA */ @@ -66,6 +62,9 @@ #define QUSB2PHY_3P3_VOL_MAX 3200000 /* uV */ #define QUSB2PHY_3P3_HPM_LOAD 30000 /* uA */ +#define LINESTATE_DP BIT(0) +#define LINESTATE_DM BIT(1) + unsigned int phy_tune2; module_param(phy_tune2, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(phy_tune2, "QUSB PHY v2 TUNE2"); @@ -73,7 +72,6 @@ MODULE_PARM_DESC(phy_tune2, "QUSB PHY v2 TUNE2"); struct qusb_phy { struct usb_phy phy; void __iomem *base; - void __iomem *qscratch_base; void __iomem *tune2_efuse_reg; struct clk *ref_clk_src; @@ -87,6 +85,8 @@ struct qusb_phy { int vdd_levels[3]; /* none, low, high */ int init_seq_len; int *qusb_phy_init_seq; + int host_init_seq_len; + int *qusb_phy_host_init_seq; u32 tune2_val; int tune2_efuse_bit_pos; @@ -374,6 +374,35 @@ static void qusb_phy_write_seq(void __iomem *base, u32 *seq, int cnt, } } +static void qusb_phy_host_init(struct usb_phy *phy) +{ + u8 reg; + struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); + + dev_dbg(phy->dev, "%s\n", __func__); + + /* Perform phy reset */ + clk_reset(qphy->phy_reset, CLK_RESET_ASSERT); + usleep_range(100, 150); + clk_reset(qphy->phy_reset, CLK_RESET_DEASSERT); + + qusb_phy_write_seq(qphy->base, qphy->qusb_phy_host_init_seq, + qphy->host_init_seq_len, 0); + + /* Ensure above write is completed before turning ON ref clk */ + wmb(); + + /* Require to get phy pll lock successfully */ + usleep_range(150, 160); + + reg = readb_relaxed(qphy->base + QUSB2PHY_PLL_COMMON_STATUS_ONE); + dev_dbg(phy->dev, "QUSB2PHY_PLL_COMMON_STATUS_ONE:%x\n", reg); + if (!(reg & CORE_READY_STATUS)) { + dev_err(phy->dev, "QUSB PHY PLL LOCK fails:%x\n", reg); + WARN_ON(1); + } +} + static int qusb_phy_init(struct usb_phy *phy) { struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy); @@ -489,6 +518,20 @@ static void qusb_phy_shutdown(struct usb_phy *phy) qusb_phy_enable_clocks(qphy, false); } + +static u32 qusb_phy_get_linestate(struct qusb_phy *qphy) +{ + u32 linestate = 0; + + if (qphy->cable_connected) { + if (qphy->phy.flags & PHY_HSFS_MODE) + linestate |= LINESTATE_DP; + else if (qphy->phy.flags & PHY_LS_MODE) + linestate |= LINESTATE_DM; + } + return linestate; +} + /** * Performs QUSB2 PHY suspend/resume functionality. * @@ -515,8 +558,7 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) writel_relaxed(0x00, qphy->base + QUSB2PHY_INTR_CTRL); - linestate = readl_relaxed(qphy->base + - QUSB2PHY_INTR_STAT); + linestate = qusb_phy_get_linestate(qphy); /* * D+/D- interrupts are level-triggered, but we are * only interested if the line state changes, so enable @@ -527,14 +569,17 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) * configure the mask to trigger on D+ low OR D- high */ intr_mask = DMSE_INTERRUPT | DPSE_INTERRUPT; - if (!(linestate & DPSE_INTR_EN)) /* D+ low */ + if (!(linestate & LINESTATE_DP)) /* D+ low */ intr_mask |= DPSE_INTR_HIGH_SEL; - if (!(linestate & DMSE_INTR_EN)) /* D- low */ + if (!(linestate & LINESTATE_DM)) /* D- low */ intr_mask |= DMSE_INTR_HIGH_SEL; writel_relaxed(intr_mask, qphy->base + QUSB2PHY_INTR_CTRL); + dev_dbg(phy->dev, "%s: intr_mask = %x\n", + __func__, intr_mask); + /* Makes sure that above write goes through */ wmb(); qusb_phy_enable_clocks(qphy, false); @@ -575,25 +620,6 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) return 0; } -static void qusb_write_readback(void *base, u32 offset, - const u32 mask, u32 val) -{ - u32 write_val, tmp = readl_relaxed(base + offset); - - tmp &= ~mask; /* retain other bits */ - write_val = tmp | val; - - writel_relaxed(write_val, base + offset); - - /* Read back to see if val was written */ - tmp = readl_relaxed(base + offset); - tmp &= mask; /* clear other bits */ - - if (tmp != val) - pr_err("%s: write: %x to QSCRATCH: %x FAILED\n", - __func__, val, offset); -} - static int qusb_phy_notify_connect(struct usb_phy *phy, enum usb_device_speed speed) { @@ -601,18 +627,11 @@ static int qusb_phy_notify_connect(struct usb_phy *phy, qphy->cable_connected = true; - dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected); + if (qphy->qusb_phy_host_init_seq && qphy->phy.flags & PHY_HOST_MODE) + qusb_phy_host_init(phy); - /* Set OTG VBUS Valid from HSPHY to controller */ - qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, - UTMI_OTG_VBUS_VALID, - UTMI_OTG_VBUS_VALID); - - /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */ - qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, - SW_SESSVLD_SEL, SW_SESSVLD_SEL); - - dev_dbg(phy->dev, "QUSB2 phy connect notification\n"); + dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n", + qphy->cable_connected); return 0; } @@ -623,17 +642,8 @@ static int qusb_phy_notify_disconnect(struct usb_phy *phy, qphy->cable_connected = false; - dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected); - - /* Set OTG VBUS Valid from HSPHY to controller */ - qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, - UTMI_OTG_VBUS_VALID, 0); - - /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */ - qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, - SW_SESSVLD_SEL, 0); - - dev_dbg(phy->dev, "QUSB2 phy disconnect notification\n"); + dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n", + qphy->cable_connected); return 0; } @@ -715,16 +725,6 @@ static int qusb_phy_probe(struct platform_device *pdev) return PTR_ERR(qphy->base); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "qscratch_base"); - if (res) { - qphy->qscratch_base = devm_ioremap_resource(dev, res); - if (IS_ERR(qphy->qscratch_base)) { - dev_dbg(dev, "couldn't ioremap qscratch_base\n"); - qphy->qscratch_base = NULL; - } - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "emu_phy_base"); if (res) { qphy->emu_phy_base = devm_ioremap_resource(dev, res); @@ -767,9 +767,17 @@ static int qusb_phy_probe(struct platform_device *pdev) else clk_set_rate(qphy->ref_clk, 19200000); - qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk"); - if (IS_ERR(qphy->cfg_ahb_clk)) - return PTR_ERR(qphy->cfg_ahb_clk); + if (of_property_match_string(pdev->dev.of_node, + "clock-names", "cfg_ahb_clk") >= 0) { + qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk"); + if (IS_ERR(qphy->cfg_ahb_clk)) { + ret = PTR_ERR(qphy->cfg_ahb_clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, + "clk get failed for cfg_ahb_clk ret %d\n", ret); + return ret; + } + } qphy->phy_reset = devm_clk_get(dev, "phy_reset"); if (IS_ERR(qphy->phy_reset)) @@ -866,6 +874,23 @@ static int qusb_phy_probe(struct platform_device *pdev) } } + qphy->host_init_seq_len = of_property_count_elems_of_size(dev->of_node, + "qcom,qusb-phy-host-init-seq", + sizeof(*qphy->qusb_phy_host_init_seq)); + if (qphy->host_init_seq_len > 0) { + qphy->qusb_phy_host_init_seq = devm_kcalloc(dev, + qphy->host_init_seq_len, + sizeof(*qphy->qusb_phy_host_init_seq), + GFP_KERNEL); + if (qphy->qusb_phy_host_init_seq) + of_property_read_u32_array(dev->of_node, + "qcom,qusb-phy-host-init-seq", + qphy->qusb_phy_host_init_seq, + qphy->host_init_seq_len); + else + return -ENOMEM; + } + ret = of_property_read_u32_array(dev->of_node, "qcom,vdd-voltage-level", (u32 *) qphy->vdd_levels, ARRAY_SIZE(qphy->vdd_levels)); @@ -899,11 +924,8 @@ static int qusb_phy_probe(struct platform_device *pdev) qphy->phy.set_suspend = qusb_phy_set_suspend; qphy->phy.shutdown = qusb_phy_shutdown; qphy->phy.type = USB_PHY_TYPE_USB2; - - if (qphy->qscratch_base) { - qphy->phy.notify_connect = qusb_phy_notify_connect; - qphy->phy.notify_disconnect = qusb_phy_notify_disconnect; - } + qphy->phy.notify_connect = qusb_phy_notify_connect; + qphy->phy.notify_disconnect = qusb_phy_notify_disconnect; ret = usb_add_phy_dev(&qphy->phy); if (ret) diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c index 1eb0c8d6b62f..325f5fcf161b 100644 --- a/drivers/usb/phy/phy-msm-qusb.c +++ b/drivers/usb/phy/phy-msm-qusb.c @@ -88,9 +88,6 @@ #define LINESTATE_DP BIT(0) #define LINESTATE_DM BIT(1) -#define HS_PHY_CTRL_REG 0x10 -#define UTMI_OTG_VBUS_VALID BIT(20) -#define SW_SESSVLD_SEL BIT(28) #define QUSB2PHY_1P8_VOL_MIN 1800000 /* uV */ #define QUSB2PHY_1P8_VOL_MAX 1800000 /* uV */ @@ -109,7 +106,6 @@ MODULE_PARM_DESC(tune2, "QUSB PHY TUNE2"); struct qusb_phy { struct usb_phy phy; void __iomem *base; - void __iomem *qscratch_base; void __iomem *tune2_efuse_reg; void __iomem *ref_clk_base; @@ -686,24 +682,6 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend) return 0; } -static void qusb_write_readback(void *base, u32 offset, - const u32 mask, u32 val) -{ - u32 write_val, tmp = readl_relaxed(base + offset); - tmp &= ~mask; /* retain other bits */ - write_val = tmp | val; - - writel_relaxed(write_val, base + offset); - - /* Read back to see if val was written */ - tmp = readl_relaxed(base + offset); - tmp &= mask; /* clear other bits */ - - if (tmp != val) - pr_err("%s: write: %x to QSCRATCH: %x FAILED\n", - __func__, val, offset); -} - static int qusb_phy_notify_connect(struct usb_phy *phy, enum usb_device_speed speed) { @@ -711,18 +689,8 @@ static int qusb_phy_notify_connect(struct usb_phy *phy, qphy->cable_connected = true; - dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected); - - /* Set OTG VBUS Valid from HSPHY to controller */ - qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, - UTMI_OTG_VBUS_VALID, - UTMI_OTG_VBUS_VALID); - - /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */ - qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, - SW_SESSVLD_SEL, SW_SESSVLD_SEL); - - dev_dbg(phy->dev, "QUSB2 phy connect notification\n"); + dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n", + qphy->cable_connected); return 0; } @@ -733,17 +701,8 @@ static int qusb_phy_notify_disconnect(struct usb_phy *phy, qphy->cable_connected = false; - dev_dbg(phy->dev, " cable_connected=%d\n", qphy->cable_connected); - - /* Set OTG VBUS Valid from HSPHY to controller */ - qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, - UTMI_OTG_VBUS_VALID, 0); - - /* Indicate value is driven by UTMI_OTG_VBUS_VALID bit */ - qusb_write_readback(qphy->qscratch_base, HS_PHY_CTRL_REG, - SW_SESSVLD_SEL, 0); - - dev_dbg(phy->dev, "QUSB2 phy disconnect notification\n"); + dev_dbg(phy->dev, "QUSB PHY: connect notification cable_connected=%d\n", + qphy->cable_connected); return 0; } @@ -827,16 +786,6 @@ static int qusb_phy_probe(struct platform_device *pdev) return PTR_ERR(qphy->base); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "qscratch_base"); - if (res) { - qphy->qscratch_base = devm_ioremap_resource(dev, res); - if (IS_ERR(qphy->qscratch_base)) { - dev_dbg(dev, "couldn't ioremap qscratch_base\n"); - qphy->qscratch_base = NULL; - } - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "emu_phy_base"); if (res) { qphy->emu_phy_base = devm_ioremap_resource(dev, res); @@ -1051,11 +1000,8 @@ static int qusb_phy_probe(struct platform_device *pdev) qphy->phy.set_suspend = qusb_phy_set_suspend; qphy->phy.shutdown = qusb_phy_shutdown; qphy->phy.type = USB_PHY_TYPE_USB2; - - if (qphy->qscratch_base) { - qphy->phy.notify_connect = qusb_phy_notify_connect; - qphy->phy.notify_disconnect = qusb_phy_notify_disconnect; - } + qphy->phy.notify_connect = qusb_phy_notify_connect; + qphy->phy.notify_disconnect = qusb_phy_notify_disconnect; /* * On some platforms multiple QUSB PHYs are available. If QUSB PHY is diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c index 50b63f912638..794bc2e33695 100644 --- a/drivers/usb/phy/phy-msm-ssusb-qmp.c +++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c @@ -547,13 +547,16 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev) clk_set_rate(phy->aux_clk, clk_round_rate(phy->aux_clk, ULONG_MAX)); - phy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk"); - if (IS_ERR(phy->cfg_ahb_clk)) { - ret = PTR_ERR(phy->cfg_ahb_clk); - phy->cfg_ahb_clk = NULL; - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get cfg_ahb_clk\n"); - goto err; + if (of_property_match_string(pdev->dev.of_node, + "clock-names", "cfg_ahb_clk") >= 0) { + phy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk"); + if (IS_ERR(phy->cfg_ahb_clk)) { + ret = PTR_ERR(phy->cfg_ahb_clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, + "failed to get cfg_ahb_clk ret %d\n", ret); + goto err; + } } phy->pipe_clk = devm_clk_get(dev, "pipe_clk"); diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index c0f5c652d272..f1893e08e51a 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -190,7 +190,8 @@ static int usbhsf_pkt_handler(struct usbhs_pipe *pipe, int type) goto __usbhs_pkt_handler_end; } - ret = func(pkt, &is_done); + if (likely(func)) + ret = func(pkt, &is_done); if (is_done) __usbhsf_pkt_del(pkt); @@ -889,6 +890,7 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done) pkt->trans = len; + usbhsf_tx_irq_ctrl(pipe, 0); INIT_WORK(&pkt->work, xfer_work); schedule_work(&pkt->work); diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 8f7a78e70975..fa14198daf77 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -158,10 +158,14 @@ static void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt) struct usbhs_pipe *pipe = pkt->pipe; struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe); struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt); + unsigned long flags; ureq->req.actual = pkt->actual; - usbhsg_queue_pop(uep, ureq, 0); + usbhs_lock(priv, flags); + if (uep) + __usbhsg_queue_pop(uep, ureq, 0); + usbhs_unlock(priv, flags); } static void usbhsg_queue_push(struct usbhsg_uep *uep, diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 7a76fe4c2f9e..bdc0f2f24f19 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -164,6 +164,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x18EF, 0xE025) }, /* ELV Marble Sound Board 1 */ { USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */ { USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */ + { USB_DEVICE(0x1901, 0x0194) }, /* GE Healthcare Remote Alarm Box */ { USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */ { USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */ { USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */ diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 01bf53392819..244acb1299a9 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -447,6 +447,11 @@ static int cypress_generic_port_probe(struct usb_serial_port *port) struct usb_serial *serial = port->serial; struct cypress_private *priv; + if (!port->interrupt_out_urb || !port->interrupt_in_urb) { + dev_err(&port->dev, "required endpoint is missing\n"); + return -ENODEV; + } + priv = kzalloc(sizeof(struct cypress_private), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -606,12 +611,6 @@ static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port) cypress_set_termios(tty, port, &priv->tmp_termios); /* setup the port and start reading from the device */ - if (!port->interrupt_in_urb) { - dev_err(&port->dev, "%s - interrupt_in_urb is empty!\n", - __func__); - return -1; - } - usb_fill_int_urb(port->interrupt_in_urb, serial->dev, usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress), port->interrupt_in_urb->transfer_buffer, diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 12b0e67473ba..3df7b7ec178e 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1251,8 +1251,27 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num) static int digi_startup(struct usb_serial *serial) { + struct device *dev = &serial->interface->dev; struct digi_serial *serial_priv; int ret; + int i; + + /* check whether the device has the expected number of endpoints */ + if (serial->num_port_pointers < serial->type->num_ports + 1) { + dev_err(dev, "OOB endpoints missing\n"); + return -ENODEV; + } + + for (i = 0; i < serial->type->num_ports + 1 ; i++) { + if (!serial->port[i]->read_urb) { + dev_err(dev, "bulk-in endpoint missing\n"); + return -ENODEV; + } + if (!serial->port[i]->write_urb) { + dev_err(dev, "bulk-out endpoint missing\n"); + return -ENODEV; + } + } serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL); if (!serial_priv) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 8c660ae401d8..b61f12160d37 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1004,6 +1004,10 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_DISPLAY_PID) }, { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_LITE_PID) }, { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_ANALOG_PID) }, + /* ICP DAS I-756xU devices */ + { USB_DEVICE(ICPDAS_VID, ICPDAS_I7560U_PID) }, + { USB_DEVICE(ICPDAS_VID, ICPDAS_I7561U_PID) }, + { USB_DEVICE(ICPDAS_VID, ICPDAS_I7563U_PID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index a84df2513994..c5d6c1e73e8e 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -872,6 +872,14 @@ #define NOVITUS_BONO_E_PID 0x6010 /* + * ICPDAS I-756*U devices + */ +#define ICPDAS_VID 0x1b5c +#define ICPDAS_I7560U_PID 0x0103 +#define ICPDAS_I7561U_PID 0x0104 +#define ICPDAS_I7563U_PID 0x0105 + +/* * RT Systems programming cables for various ham radios */ #define RTSYSTEMS_VID 0x2100 /* Vendor ID */ diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index fd707d6a10e2..89726f702202 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -376,14 +376,21 @@ static void mct_u232_msr_to_state(struct usb_serial_port *port, static int mct_u232_port_probe(struct usb_serial_port *port) { + struct usb_serial *serial = port->serial; struct mct_u232_private *priv; + /* check first to simplify error handling */ + if (!serial->port[1] || !serial->port[1]->interrupt_in_urb) { + dev_err(&port->dev, "expected endpoint missing\n"); + return -ENODEV; + } + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; /* Use second interrupt-in endpoint for reading. */ - priv->read_urb = port->serial->port[1]->interrupt_in_urb; + priv->read_urb = serial->port[1]->interrupt_in_urb; priv->read_urb->context = port; spin_lock_init(&priv->lock); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 348e19834b83..c6f497f16526 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1818,6 +1818,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d02, 0xff, 0x00, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x02, 0x01) }, { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x00, 0x00) }, + { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */ + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */ { USB_DEVICE_INTERFACE_CLASS(0x2020, 0x4000, 0xff) }, /* OLICARD300 - MT6225 */ diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 5c66d3f7a6d0..9baf081174ce 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -2,7 +2,7 @@ * USB Attached SCSI * Note that this is not the same as the USB Mass Storage driver * - * Copyright Hans de Goede <hdegoede@redhat.com> for Red Hat, Inc. 2013 - 2014 + * Copyright Hans de Goede <hdegoede@redhat.com> for Red Hat, Inc. 2013 - 2016 * Copyright Matthew Wilcox for Intel Corp, 2010 * Copyright Sarah Sharp for Intel Corp, 2010 * @@ -757,6 +757,17 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) return SUCCESS; } +static int uas_target_alloc(struct scsi_target *starget) +{ + struct uas_dev_info *devinfo = (struct uas_dev_info *) + dev_to_shost(starget->dev.parent)->hostdata; + + if (devinfo->flags & US_FL_NO_REPORT_LUNS) + starget->no_report_luns = 1; + + return 0; +} + static int uas_slave_alloc(struct scsi_device *sdev) { struct uas_dev_info *devinfo = @@ -800,7 +811,6 @@ static int uas_slave_configure(struct scsi_device *sdev) if (devinfo->flags & US_FL_BROKEN_FUA) sdev->broken_fua = 1; - scsi_change_queue_depth(sdev, devinfo->qdepth - 2); return 0; } @@ -808,11 +818,12 @@ static struct scsi_host_template uas_host_template = { .module = THIS_MODULE, .name = "uas", .queuecommand = uas_queuecommand, + .target_alloc = uas_target_alloc, .slave_alloc = uas_slave_alloc, .slave_configure = uas_slave_configure, .eh_abort_handler = uas_eh_abort_handler, .eh_bus_reset_handler = uas_eh_bus_reset_handler, - .can_queue = 65536, /* Is there a limit on the _host_ ? */ + .can_queue = MAX_CMNDS, .this_id = -1, .sg_tablesize = SG_NONE, .skip_settle_delay = 1, @@ -932,6 +943,12 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) if (result) goto set_alt0; + /* + * 1 tag is reserved for untagged commands + + * 1 tag to avoid off by one errors in some bridge firmwares + */ + shost->can_queue = devinfo->qdepth - 2; + usb_set_intfdata(intf, shost); result = scsi_add_host(shost, &intf->dev); if (result) diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index ccc113e83d88..53341a77d89f 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -64,6 +64,13 @@ UNUSUAL_DEV(0x0bc2, 0x3312, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_ATA_1X), +/* Reported-by: David Webb <djw@noc.ac.uk> */ +UNUSUAL_DEV(0x0bc2, 0x331a, 0x0000, 0x9999, + "Seagate", + "Expansion Desk", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_LUNS), + /* Reported-by: Hans de Goede <hdegoede@redhat.com> */ UNUSUAL_DEV(0x0bc2, 0x3320, 0x0000, 0x9999, "Seagate", diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 43576ed31ccd..9de988a0f856 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -482,7 +482,7 @@ void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags) US_FL_NO_READ_DISC_INFO | US_FL_NO_READ_CAPACITY_16 | US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE | US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES | - US_FL_MAX_SECTORS_240); + US_FL_MAX_SECTORS_240 | US_FL_NO_REPORT_LUNS); p = quirks; while (*p) { @@ -532,6 +532,9 @@ void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags) case 'i': f |= US_FL_IGNORE_DEVICE; break; + case 'j': + f |= US_FL_NO_REPORT_LUNS; + break; case 'l': f |= US_FL_NOT_LOCKABLE; break; diff --git a/drivers/video/fbdev/msm/Kconfig b/drivers/video/fbdev/msm/Kconfig index c49ce06430be..ef5c96214c19 100644 --- a/drivers/video/fbdev/msm/Kconfig +++ b/drivers/video/fbdev/msm/Kconfig @@ -96,13 +96,13 @@ config FB_MSM_MDSS_DSI_CTRL_STATUS fails to acknowledge the BTA command, it sends PANEL_ALIVE=0 status to HAL layer to reset the controller. -config FB_MSM_MDSS_EDP_PANEL +config FB_MSM_MDSS_DP_PANEL depends on FB_MSM_MDSS - bool "MDSS eDP Panel" + bool "MDSS DP Panel" ---help--- - The MDSS eDP Panel provides support for eDP host controller driver + The MDSS DP Panel provides support for DP host controller driver which runs in Video mode only and is responsible for transmitting - frame buffer from host SOC to eDP display panel. + frame buffer from host SOC to DP display panel. config FB_MSM_MDSS_MDP3 depends on FB_MSM_MDSS diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile index 9d25b08c753a..dccd7bcc7219 100644 --- a/drivers/video/fbdev/msm/Makefile +++ b/drivers/video/fbdev/msm/Makefile @@ -43,8 +43,8 @@ obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdmi_util.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdmi_edid.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_cec_core.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_dba_utils.o -obj-$(CONFIG_FB_MSM_MDSS_EDP_PANEL) += mdss_edp.o -obj-$(CONFIG_FB_MSM_MDSS_EDP_PANEL) += mdss_edp_aux.o +obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp.o mdss_dp_util.o +obj-$(CONFIG_FB_MSM_MDSS_DP_PANEL) += mdss_dp_aux.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_io_util.o obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_tx.o diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c new file mode 100644 index 000000000000..a99ae97cdb80 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -0,0 +1,1698 @@ +/* Copyright (c) 2012-2016 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/time.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/gpio.h> +#include <linux/err.h> +#include <linux/regulator/consumer.h> +#include <linux/qpnp/pwm.h> +#include <linux/clk.h> +#include <linux/spinlock_types.h> +#include <linux/kthread.h> + +#include "mdss.h" +#include "mdss_dp.h" +#include "mdss_dp_util.h" +#include "mdss_debug.h" + +#define RGB_COMPONENTS 3 +#define VDDA_MIN_UV 1800000 /* uV units */ +#define VDDA_MAX_UV 1800000 /* uV units */ +#define VDDA_UA_ON_LOAD 100000 /* uA units */ +#define VDDA_UA_OFF_LOAD 100 /* uA units */ + + + +static void mdss_dp_put_dt_clk_data(struct device *dev, + struct dss_module_power *module_power) +{ + if (!module_power) { + DEV_ERR("%s: invalid input\n", __func__); + return; + } + + if (module_power->clk_config) { + devm_kfree(dev, module_power->clk_config); + module_power->clk_config = NULL; + } + module_power->num_clk = 0; +} /* mdss_dp_put_dt_clk_data */ + +static int mdss_dp_is_clk_prefix(const char *clk_prefix, const char *clk_name) +{ + return !strncmp(clk_name, clk_prefix, strlen(clk_prefix)); +} + +static int mdss_dp_init_clk_power_data(struct device *dev, + struct mdss_dp_drv_pdata *pdata) +{ + int num_clk = 0, i = 0, rc = 0; + int core_clk_count = 0, ctrl_clk_count = 0; + const char *core_clk = "core"; + const char *ctrl_clk = "ctrl"; + struct dss_module_power *core_power_data = NULL; + struct dss_module_power *ctrl_power_data = NULL; + const char *clk_name; + + num_clk = of_property_count_strings(dev->of_node, + "clock-names"); + if (num_clk <= 0) { + pr_err("no clocks are defined\n"); + rc = -EINVAL; + goto exit; + } + + core_power_data = &pdata->power_data[DP_CORE_PM]; + ctrl_power_data = &pdata->power_data[DP_CTRL_PM]; + + for (i = 0; i < num_clk; i++) { + of_property_read_string_index(dev->of_node, "clock-names", + i, &clk_name); + + if (mdss_dp_is_clk_prefix(core_clk, clk_name)) + core_clk_count++; + if (mdss_dp_is_clk_prefix(ctrl_clk, clk_name)) + ctrl_clk_count++; + } + + /* Initialize the CORE power module */ + if (core_clk_count <= 0) { + pr_err("no core clocks are defined\n"); + rc = -EINVAL; + goto exit; + } + + core_power_data->num_clk = core_clk_count; + core_power_data->clk_config = devm_kzalloc(dev, sizeof(struct dss_clk) * + core_power_data->num_clk, GFP_KERNEL); + if (!core_power_data->clk_config) { + rc = -EINVAL; + goto exit; + } + + /* Initialize the CTRL power module */ + if (ctrl_clk_count <= 0) { + pr_err("no ctrl clocks are defined\n"); + rc = -EINVAL; + goto ctrl_clock_error; + } + + ctrl_power_data->num_clk = ctrl_clk_count; + ctrl_power_data->clk_config = devm_kzalloc(dev, sizeof(struct dss_clk) * + ctrl_power_data->num_clk, GFP_KERNEL); + if (!ctrl_power_data->clk_config) { + ctrl_power_data->num_clk = 0; + rc = -EINVAL; + goto ctrl_clock_error; + } + + return rc; + +ctrl_clock_error: + mdss_dp_put_dt_clk_data(dev, core_power_data); +exit: + return rc; +} + +static int mdss_dp_get_dt_clk_data(struct device *dev, + struct mdss_dp_drv_pdata *pdata) +{ + int rc = 0, i = 0; + const char *clk_name; + int num_clk = 0; + int core_clk_index = 0, ctrl_clk_index = 0; + int core_clk_count = 0, ctrl_clk_count = 0; + const char *core_clk = "core"; + const char *ctrl_clk = "ctrl"; + struct dss_module_power *core_power_data = NULL; + struct dss_module_power *ctrl_power_data = NULL; + + if (!dev || !pdata) { + pr_err("invalid input\n"); + rc = -EINVAL; + goto exit; + } + + rc = mdss_dp_init_clk_power_data(dev, pdata); + if (rc) { + pr_err("failed to initialize power data\n"); + rc = -EINVAL; + goto exit; + } + + core_power_data = &pdata->power_data[DP_CORE_PM]; + core_clk_count = core_power_data->num_clk; + ctrl_power_data = &pdata->power_data[DP_CTRL_PM]; + ctrl_clk_count = ctrl_power_data->num_clk; + + num_clk = core_clk_count + ctrl_clk_count; + + for (i = 0; i < num_clk; i++) { + of_property_read_string_index(dev->of_node, "clock-names", + i, &clk_name); + + if (mdss_dp_is_clk_prefix(core_clk, clk_name) + && core_clk_index < core_clk_count) { + struct dss_clk *clk = + &core_power_data->clk_config[core_clk_index]; + strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name)); + clk->type = DSS_CLK_AHB; + core_clk_index++; + } else if (mdss_dp_is_clk_prefix(ctrl_clk, clk_name) + && ctrl_clk_index < ctrl_clk_count) { + struct dss_clk *clk = + &ctrl_power_data->clk_config[ctrl_clk_index]; + strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name)); + ctrl_clk_index++; + if (!strcmp(clk_name, "ctrl_link_clk")) + clk->type = DSS_CLK_PCLK; + else if (!strcmp(clk_name, "ctrl_pixel_clk")) + clk->type = DSS_CLK_PCLK; + else + clk->type = DSS_CLK_AHB; + } + } + + pr_debug("Display-port clock parsing successful\n"); + +exit: + return rc; +} /* mdss_dp_get_dt_clk_data */ + +static int mdss_dp_clk_init(struct mdss_dp_drv_pdata *dp_drv, + struct device *dev, bool initialize) +{ + struct dss_module_power *core_power_data = NULL; + struct dss_module_power *ctrl_power_data = NULL; + int rc = 0; + + if (!dp_drv || !dev) { + pr_err("invalid input\n"); + rc = -EINVAL; + goto exit; + } + + core_power_data = &dp_drv->power_data[DP_CORE_PM]; + ctrl_power_data = &dp_drv->power_data[DP_CTRL_PM]; + + if (!core_power_data || !ctrl_power_data) { + pr_err("invalid power_data\n"); + rc = -EINVAL; + goto exit; + } + + if (initialize) { + rc = msm_dss_get_clk(dev, core_power_data->clk_config, + core_power_data->num_clk); + if (rc) { + DEV_ERR("Failed to get %s clk. Err=%d\n", + __mdss_dp_pm_name(DP_CORE_PM), rc); + goto exit; + } + + rc = msm_dss_get_clk(dev, ctrl_power_data->clk_config, + ctrl_power_data->num_clk); + if (rc) { + DEV_ERR("Failed to get %s clk. Err=%d\n", + __mdss_dp_pm_name(DP_CTRL_PM), rc); + goto ctrl_get_error; + } + + } else { + msm_dss_put_clk(ctrl_power_data->clk_config, + ctrl_power_data->num_clk); + msm_dss_put_clk(core_power_data->clk_config, + core_power_data->num_clk); + } + + return rc; + +ctrl_get_error: + msm_dss_put_clk(core_power_data->clk_config, + core_power_data->num_clk); + +exit: + return rc; +} + +static int mdss_dp_clk_set_rate_enable( + struct dss_module_power *power_data, + bool enable) +{ + int ret = 0; + + if (enable) { + ret = msm_dss_clk_set_rate( + power_data->clk_config, + power_data->num_clk); + if (ret) { + pr_err("failed to set clks rate.\n"); + goto exit; + } + + ret = msm_dss_enable_clk( + power_data->clk_config, + power_data->num_clk, 1); + if (ret) { + pr_err("failed to enable clks\n"); + goto exit; + } + } else { + ret = msm_dss_enable_clk( + power_data->clk_config, + power_data->num_clk, 0); + if (ret) { + pr_err("failed to disable clks\n"); + goto exit; + } + } +exit: + return ret; +} + +/* + * This clock control function supports enabling/disabling + * of core and ctrl power module clocks + */ +static int mdss_dp_clk_ctrl(struct mdss_dp_drv_pdata *dp_drv, + int pm_type, bool enable) +{ + int ret = 0; + + if ((pm_type != DP_CORE_PM) + && (pm_type != DP_CTRL_PM)) { + pr_err("unsupported power module: %s\n", + __mdss_dp_pm_name(pm_type)); + return -EINVAL; + } + + if (enable) { + if ((pm_type == DP_CORE_PM) + && (dp_drv->core_clks_on)) { + pr_debug("core clks already enabled\n"); + return 0; + } + + if ((pm_type == DP_CTRL_PM) + && (dp_drv->link_clks_on)) { + pr_debug("links clks already enabled\n"); + return 0; + } + + if ((pm_type == DP_CTRL_PM) + && (!dp_drv->core_clks_on)) { + pr_debug("Need to enable core clks before link clks\n"); + + ret = mdss_dp_clk_set_rate_enable( + &dp_drv->power_data[DP_CORE_PM], + enable); + if (ret) { + pr_err("failed to enable clks: %s. err=%d\n", + __mdss_dp_pm_name(DP_CORE_PM), ret); + goto error; + } else { + dp_drv->core_clks_on = true; + } + } + } + + ret = mdss_dp_clk_set_rate_enable( + &dp_drv->power_data[pm_type], + enable); + if (ret) { + pr_err("failed to '%s' clks for: %s. err=%d\n", + enable ? "enable" : "disable", + __mdss_dp_pm_name(pm_type), ret); + goto error; + } + + if (pm_type == DP_CORE_PM) + dp_drv->core_clks_on = enable; + else + dp_drv->link_clks_on = enable; + +error: + return ret; +} + +static int mdss_dp_regulator_ctrl(struct mdss_dp_drv_pdata *dp_drv, + bool enable) +{ + int ret = 0, i = 0, j = 0; + + if (dp_drv->core_power == enable) { + pr_debug("regulators already %s\n", + enable ? "enabled" : "disabled"); + return 0; + } + + for (i = DP_CORE_PM; i < DP_MAX_PM; i++) { + ret = msm_dss_enable_vreg( + dp_drv->power_data[i].vreg_config, + dp_drv->power_data[i].num_vreg, enable); + if (ret) { + pr_err("failed to '%s' vregs for %s\n", + enable ? "enable" : "disable", + __mdss_dp_pm_name(i)); + if (enable) { + /* Disabling the enabled vregs */ + for (j = i-1; j >= DP_CORE_PM; j--) { + msm_dss_enable_vreg( + dp_drv->power_data[j].vreg_config, + dp_drv->power_data[j].num_vreg, 0); + } + } + goto error; + } + } + + dp_drv->core_power = enable; + +error: + return ret; +} + +static void mdss_dp_put_dt_vreg_data(struct device *dev, + struct dss_module_power *module_power) +{ + if (!module_power) { + DEV_ERR("invalid input\n"); + return; + } + + if (module_power->vreg_config) { + devm_kfree(dev, module_power->vreg_config); + module_power->vreg_config = NULL; + } + module_power->num_vreg = 0; +} /* mdss_dp_put_dt_vreg_data */ + +static int mdss_dp_get_dt_vreg_data(struct device *dev, + struct device_node *of_node, struct dss_module_power *mp, + enum dp_pm_type module) +{ + int i = 0, rc = 0; + u32 tmp = 0; + struct device_node *supply_node = NULL; + const char *pm_supply_name = NULL; + struct device_node *supply_root_node = NULL; + + if (!dev || !mp) { + pr_err("invalid input\n"); + rc = -EINVAL; + return rc; + } + + mp->num_vreg = 0; + pm_supply_name = __mdss_dp_pm_supply_node_name(module); + supply_root_node = of_get_child_by_name(of_node, pm_supply_name); + if (!supply_root_node) { + pr_err("no supply entry present: %s\n", pm_supply_name); + goto novreg; + } + + mp->num_vreg = + of_get_available_child_count(supply_root_node); + + if (mp->num_vreg == 0) { + pr_debug("no vreg\n"); + goto novreg; + } else { + pr_debug("vreg found. count=%d\n", mp->num_vreg); + } + + mp->vreg_config = devm_kzalloc(dev, sizeof(struct dss_vreg) * + mp->num_vreg, GFP_KERNEL); + if (!mp->vreg_config) { + rc = -ENOMEM; + goto error; + } + + for_each_child_of_node(supply_root_node, supply_node) { + const char *st = NULL; + /* vreg-name */ + rc = of_property_read_string(supply_node, + "qcom,supply-name", &st); + if (rc) { + pr_err("error reading name. rc=%d\n", + rc); + goto error; + } + snprintf(mp->vreg_config[i].vreg_name, + ARRAY_SIZE((mp->vreg_config[i].vreg_name)), "%s", st); + /* vreg-min-voltage */ + rc = of_property_read_u32(supply_node, + "qcom,supply-min-voltage", &tmp); + if (rc) { + pr_err("error reading min volt. rc=%d\n", + rc); + goto error; + } + mp->vreg_config[i].min_voltage = tmp; + + /* vreg-max-voltage */ + rc = of_property_read_u32(supply_node, + "qcom,supply-max-voltage", &tmp); + if (rc) { + pr_err("error reading max volt. rc=%d\n", + rc); + goto error; + } + mp->vreg_config[i].max_voltage = tmp; + + /* enable-load */ + rc = of_property_read_u32(supply_node, + "qcom,supply-enable-load", &tmp); + if (rc) { + pr_err("error reading enable load. rc=%d\n", + rc); + goto error; + } + mp->vreg_config[i].enable_load = tmp; + + /* disable-load */ + rc = of_property_read_u32(supply_node, + "qcom,supply-disable-load", &tmp); + if (rc) { + pr_err("error reading disable load. rc=%d\n", + rc); + goto error; + } + mp->vreg_config[i].disable_load = tmp; + + pr_debug("%s min=%d, max=%d, enable=%d, disable=%d\n", + mp->vreg_config[i].vreg_name, + mp->vreg_config[i].min_voltage, + mp->vreg_config[i].max_voltage, + mp->vreg_config[i].enable_load, + mp->vreg_config[i].disable_load + ); + ++i; + } + + return rc; + +error: + if (mp->vreg_config) { + devm_kfree(dev, mp->vreg_config); + mp->vreg_config = NULL; + } +novreg: + mp->num_vreg = 0; + + return rc; +} /* mdss_dp_get_dt_vreg_data */ + +static int mdss_dp_regulator_init(struct platform_device *pdev, + struct mdss_dp_drv_pdata *dp_drv) +{ + int rc = 0, i = 0, j = 0; + + if (!pdev || !dp_drv) { + pr_err("invalid input\n"); + return -EINVAL; + } + + for (i = DP_CORE_PM; !rc && (i < DP_MAX_PM); i++) { + rc = msm_dss_config_vreg(&pdev->dev, + dp_drv->power_data[i].vreg_config, + dp_drv->power_data[i].num_vreg, 1); + if (rc) { + pr_err("failed to init vregs for %s\n", + __mdss_dp_pm_name(i)); + for (j = i-1; j >= DP_CORE_PM; j--) { + msm_dss_config_vreg(&pdev->dev, + dp_drv->power_data[j].vreg_config, + dp_drv->power_data[j].num_vreg, 0); + } + } + } + + return rc; +} + +static int mdss_dp_pinctrl_set_state( + struct mdss_dp_drv_pdata *dp, + bool active) +{ + struct pinctrl_state *pin_state; + int rc = -EFAULT; + + if (IS_ERR_OR_NULL(dp->pin_res.pinctrl)) + return PTR_ERR(dp->pin_res.pinctrl); + + pin_state = active ? dp->pin_res.state_active + : dp->pin_res.state_suspend; + if (!IS_ERR_OR_NULL(pin_state)) { + rc = pinctrl_select_state(dp->pin_res.pinctrl, + pin_state); + if (rc) + pr_err("can not set %s pins\n", + active ? "mdss_dp_active" + : "mdss_dp_sleep"); + } else { + pr_err("invalid '%s' pinstate\n", + active ? "mdss_dp_active" + : "mdss_dp_sleep"); + } + return rc; +} + +static int mdss_dp_pinctrl_init(struct platform_device *pdev, + struct mdss_dp_drv_pdata *dp) +{ + dp->pin_res.pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(dp->pin_res.pinctrl)) { + pr_err("failed to get pinctrl\n"); + return PTR_ERR(dp->pin_res.pinctrl); + } + + dp->pin_res.state_active + = pinctrl_lookup_state(dp->pin_res.pinctrl, + "mdss_dp_active"); + if (IS_ERR_OR_NULL(dp->pin_res.state_active)) { + pr_err("can not get dp active pinstate\n"); + return PTR_ERR(dp->pin_res.state_active); + } + + dp->pin_res.state_suspend + = pinctrl_lookup_state(dp->pin_res.pinctrl, + "mdss_dp_sleep"); + if (IS_ERR_OR_NULL(dp->pin_res.state_suspend)) { + pr_err("can not get dp sleep pinstate\n"); + return PTR_ERR(dp->pin_res.state_suspend); + } + + return 0; +} + +static int mdss_dp_request_gpios(struct mdss_dp_drv_pdata *dp) +{ + int rc = 0; + struct device *dev = NULL; + + if (!dp) { + pr_err("invalid input\n"); + return -EINVAL; + } + + dev = &dp->pdev->dev; + if (gpio_is_valid(dp->aux_en_gpio)) { + rc = devm_gpio_request(dev, dp->aux_en_gpio, + "aux_enable"); + if (rc) { + pr_err("request aux_en gpio failed, rc=%d\n", + rc); + goto aux_en_gpio_err; + } + } + if (gpio_is_valid(dp->aux_sel_gpio)) { + rc = devm_gpio_request(dev, dp->aux_sel_gpio, "aux_sel"); + if (rc) { + pr_err("request aux_sel gpio failed, rc=%d\n", + rc); + goto aux_sel_gpio_err; + } + } + if (gpio_is_valid(dp->usbplug_cc_gpio)) { + rc = devm_gpio_request(dev, dp->usbplug_cc_gpio, + "usbplug_cc"); + if (rc) { + pr_err("request usbplug_cc gpio failed, rc=%d\n", + rc); + goto usbplug_cc_gpio_err; + } + } + if (gpio_is_valid(dp->hpd_gpio)) { + rc = devm_gpio_request(dev, dp->hpd_gpio, "hpd"); + if (rc) { + pr_err("request hpd gpio failed, rc=%d\n", + rc); + goto hpd_gpio_err; + } + } + return rc; + +hpd_gpio_err: + if (gpio_is_valid(dp->usbplug_cc_gpio)) + gpio_free(dp->usbplug_cc_gpio); +usbplug_cc_gpio_err: + if (gpio_is_valid(dp->aux_sel_gpio)) + gpio_free(dp->aux_sel_gpio); +aux_sel_gpio_err: + if (gpio_is_valid(dp->aux_en_gpio)) + gpio_free(dp->aux_en_gpio); +aux_en_gpio_err: + return rc; +} + +static int mdss_dp_config_gpios(struct mdss_dp_drv_pdata *dp, bool enable) +{ + int rc = 0; + + if (enable == true) { + rc = mdss_dp_request_gpios(dp); + if (rc) { + pr_err("gpio request failed\n"); + return rc; + } + + if (gpio_is_valid(dp->aux_en_gpio)) { + rc = gpio_direction_output( + dp->aux_en_gpio, 0); + if (rc) + pr_err("unable to set dir for aux_en gpio\n"); + } + if (gpio_is_valid(dp->aux_sel_gpio)) { + rc = gpio_direction_output( + dp->aux_sel_gpio, 0); + if (rc) + pr_err("unable to set dir for aux_sel gpio\n"); + } + if (gpio_is_valid(dp->usbplug_cc_gpio)) { + gpio_set_value( + dp->usbplug_cc_gpio, 0); + } + if (gpio_is_valid(dp->hpd_gpio)) { + gpio_set_value( + dp->hpd_gpio, 1); + } + } else { + if (gpio_is_valid(dp->aux_en_gpio)) { + gpio_set_value((dp->aux_en_gpio), 0); + gpio_free(dp->aux_en_gpio); + } + if (gpio_is_valid(dp->aux_sel_gpio)) { + gpio_set_value((dp->aux_sel_gpio), 0); + gpio_free(dp->aux_sel_gpio); + } + if (gpio_is_valid(dp->usbplug_cc_gpio)) { + gpio_set_value((dp->usbplug_cc_gpio), 0); + gpio_free(dp->usbplug_cc_gpio); + } + if (gpio_is_valid(dp->hpd_gpio)) { + gpio_set_value((dp->hpd_gpio), 0); + gpio_free(dp->hpd_gpio); + } + } + return 0; +} + +static int mdss_dp_parse_gpio_params(struct platform_device *pdev, + struct mdss_dp_drv_pdata *dp) +{ + dp->aux_en_gpio = of_get_named_gpio( + pdev->dev.of_node, + "qcom,aux-en-gpio", 0); + + if (!gpio_is_valid(dp->aux_en_gpio)) { + pr_err("%d, Aux_en gpio not specified\n", + __LINE__); + return -EINVAL; + } + + dp->aux_sel_gpio = of_get_named_gpio( + pdev->dev.of_node, + "qcom,aux-sel-gpio", 0); + + if (!gpio_is_valid(dp->aux_sel_gpio)) { + pr_err("%d, Aux_sel gpio not specified\n", + __LINE__); + return -EINVAL; + } + + dp->usbplug_cc_gpio = of_get_named_gpio( + pdev->dev.of_node, + "qcom,usbplug-cc-gpio", 0); + + if (!gpio_is_valid(dp->usbplug_cc_gpio)) { + pr_err("%d,usbplug_cc gpio not specified\n", + __LINE__); + return -EINVAL; + } + + dp->hpd_gpio = of_get_named_gpio( + pdev->dev.of_node, + "qcom,hpd-gpio", 0); + + if (!gpio_is_valid(dp->hpd_gpio)) { + pr_info("%d,hpd gpio not specified\n", + __LINE__); + } + + return 0; +} + +void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp) +{ + /* + * To siwtch the usb3_phy to operate in DP mode, the phy and PLL + * should have the reset lines asserted + */ + mdss_dp_assert_phy_reset(&dp->ctrl_io, true); + /* Delay to make sure the assert is propagated */ + udelay(2000); + mdss_dp_switch_usb3_phy_to_dp_mode(&dp->tcsr_reg_io); + wmb(); /* ensure that the register write is successful */ + mdss_dp_assert_phy_reset(&dp->ctrl_io, false); +} + +void mdss_dp_config_ctrl(struct mdss_dp_drv_pdata *dp) +{ + struct dpcd_cap *cap; + struct display_timing_desc *timing; + u32 data = 0; + + timing = &dp->edid.timing[0]; + + cap = &dp->dpcd; + + data = dp->lane_cnt - 1; + data <<= 4; + + if (cap->enhanced_frame) + data |= 0x40; + + if (dp->edid.color_depth == 8) { + /* 0 == 6 bits, 1 == 8 bits */ + data |= 0x100; /* bit 8 */ + } + + if (!timing->interlaced) /* progressive */ + data |= 0x04; + + data |= 0x03; /* sycn clock & static Mvid */ + + mdss_dp_configuration_ctrl(&dp->ctrl_io, data); +} + +int mdss_dp_wait4train(struct mdss_dp_drv_pdata *dp_drv) +{ + int ret = 0; + + if (dp_drv->cont_splash) + return ret; + + ret = wait_for_completion_timeout(&dp_drv->video_comp, 30); + if (ret <= 0) { + pr_err("Link Train timedout\n"); + ret = -EINVAL; + } else { + ret = 0; + } + + pr_debug("End--\n"); + + return ret; +} + +#define DEFAULT_VIDEO_RESOLUTION HDMI_VFRMT_640x480p60_4_3 + +static int dp_init_panel_info(struct mdss_dp_drv_pdata *dp_drv) +{ + struct mdss_panel_info *pinfo; + struct msm_hdmi_mode_timing_info timing = {0}; + u32 ret; + + if (!dp_drv) { + DEV_ERR("invalid input\n"); + return -EINVAL; + } + + dp_drv->ds_data.ds_registered = false; + ret = hdmi_get_supported_mode(&timing, &dp_drv->ds_data, + DEFAULT_VIDEO_RESOLUTION); + pinfo = &dp_drv->panel_data.panel_info; + + if (ret || !timing.supported || !pinfo) { + DEV_ERR("%s: invalid timing data\n", __func__); + return -EINVAL; + } + + pinfo->xres = timing.active_h; + pinfo->yres = timing.active_v; + pinfo->clk_rate = timing.pixel_freq * 1000; + + pinfo->lcdc.h_back_porch = timing.back_porch_h; + pinfo->lcdc.h_front_porch = timing.front_porch_h; + pinfo->lcdc.h_pulse_width = timing.pulse_width_h; + pinfo->lcdc.v_back_porch = timing.back_porch_v; + pinfo->lcdc.v_front_porch = timing.front_porch_v; + pinfo->lcdc.v_pulse_width = timing.pulse_width_v; + + pinfo->type = EDP_PANEL; + pinfo->pdest = DISPLAY_4; + pinfo->wait_cycle = 0; + pinfo->bpp = 24; + pinfo->fb_num = 1; + + pinfo->lcdc.border_clr = 0; /* blk */ + pinfo->lcdc.underflow_clr = 0xff; /* blue */ + pinfo->lcdc.hsync_skew = 0; + + return 0; +} /* dp_init_panel_info */ + + +int mdss_dp_on(struct mdss_panel_data *pdata) +{ + struct mdss_dp_drv_pdata *dp_drv = NULL; + int ret = 0; + enum plug_orientation orientation = ORIENTATION_NONE; + struct lane_mapping ln_map; + + if (!pdata) { + pr_err("Invalid input data\n"); + return -EINVAL; + } + + dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, + panel_data); + + /* wait until link training is completed */ + mutex_lock(&dp_drv->host_mutex); + + pr_debug("Enter++ cont_splash=%d\n", dp_drv->cont_splash); + /* Default lane mapping */ + ln_map.lane0 = 2; + ln_map.lane1 = 3; + ln_map.lane2 = 1; + ln_map.lane3 = 0; + + if (!dp_drv->cont_splash) { /* vote for clocks */ + ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true); + if (ret) { + pr_err("Unabled to start core clocks\n"); + return ret; + } + mdss_dp_phy_reset(&dp_drv->ctrl_io); + mdss_dp_aux_reset(&dp_drv->ctrl_io); + mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true); + mdss_dp_hpd_configure(&dp_drv->ctrl_io, true); + + orientation = usbpd_get_plug_orientation(dp_drv->pd); + pr_debug("plug Orientation = %d\n", orientation); + + if (orientation == ORIENTATION_CC2) { + /* update lane mapping */ + ln_map.lane0 = 1; + ln_map.lane1 = 0; + ln_map.lane2 = 2; + ln_map.lane3 = 3; + + if (gpio_is_valid(dp_drv->usbplug_cc_gpio)) { + gpio_set_value( + dp_drv->usbplug_cc_gpio, 1); + pr_debug("Configured cc gpio for new Orientation\n"); + } + + } + + mdss_dp_phy_aux_setup(&dp_drv->phy_io); + + mdss_dp_irq_enable(dp_drv); + pr_debug("irq enabled\n"); + mdss_dp_dpcd_cap_read(dp_drv); + dp_drv->link_rate = + mdss_dp_gen_link_clk(&dp_drv->panel_data.panel_info, + dp_drv->dpcd.max_lane_count); + + pr_debug("link_rate=0x%x, Max rate supported by sink=0x%x\n", + dp_drv->link_rate, dp_drv->dpcd.max_link_rate); + if (!dp_drv->link_rate) { + pr_err("Unable to configure required link rate\n"); + return -EINVAL; + } + + pr_debug("link_rate = 0x%x\n", dp_drv->link_rate); + + dp_drv->power_data[DP_CTRL_PM].clk_config[0].rate = + dp_drv->link_rate * DP_LINK_RATE_MULTIPLIER; + + dp_drv->pixel_rate = dp_drv->panel_data.panel_info.clk_rate; + dp_drv->power_data[DP_CTRL_PM].clk_config[3].rate = + dp_drv->pixel_rate; + + ret = mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, true); + if (ret) { + mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, false); + pr_err("Unabled to start link clocks\n"); + return ret; + } + + mdss_dp_mainlink_reset(&dp_drv->ctrl_io); + + mdss_dp_ctrl_lane_mapping(&dp_drv->ctrl_io, ln_map); + reinit_completion(&dp_drv->idle_comp); + mdss_dp_fill_link_cfg(dp_drv); + mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, true); + mdss_dp_config_ctrl(dp_drv); + mdss_dp_sw_mvid_nvid(&dp_drv->ctrl_io); + mdss_dp_timing_cfg(&dp_drv->ctrl_io, + &dp_drv->panel_data.panel_info); + } else { + mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true); + } + + pr_debug("call link_training\n"); + mdss_dp_link_train(dp_drv); + + mdss_dp_wait4train(dp_drv); + + dp_drv->cont_splash = 0; + + if (mdss_dp_mainlink_ready(dp_drv, BIT(0))) + pr_debug("mainlink ready\n"); + + mutex_unlock(&dp_drv->host_mutex); + pr_debug("End-\n"); + + return ret; +} + +int mdss_dp_off(struct mdss_panel_data *pdata) +{ + struct mdss_dp_drv_pdata *dp_drv = NULL; + int ret = 0; + + dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, + panel_data); + if (!dp_drv) { + pr_err("Invalid input data\n"); + return -EINVAL; + } + pr_debug("Entered++, cont_splash=%d\n", dp_drv->cont_splash); + + /* wait until link training is completed */ + mutex_lock(&dp_drv->train_mutex); + + reinit_completion(&dp_drv->idle_comp); + mdss_dp_state_ctrl(&dp_drv->ctrl_io, ST_PUSH_IDLE); + + ret = wait_for_completion_timeout(&dp_drv->idle_comp, + msecs_to_jiffies(100)); + if (ret == 0) + pr_err("idle pattern timedout\n"); + + mdss_dp_state_ctrl(&dp_drv->ctrl_io, 0); + + mdss_dp_irq_disable(dp_drv); + + mdss_dp_mainlink_reset(&dp_drv->ctrl_io); + mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, false); + + mdss_dp_config_gpios(dp_drv, false); + mdss_dp_pinctrl_set_state(dp_drv, false); + + mdss_dp_aux_ctrl(&dp_drv->ctrl_io, false); + mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, false); + mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, false); + + mdss_dp_regulator_ctrl(dp_drv, false); + + pr_debug("End--: state_ctrl=%x\n", + dp_read(dp_drv->base + DP_STATE_CTRL)); + + mutex_unlock(&dp_drv->train_mutex); + return 0; +} + +static int mdss_dp_host_init(struct mdss_panel_data *pdata) +{ + struct mdss_dp_drv_pdata *dp_drv = NULL; + int ret = 0; + + if (!pdata) { + pr_err("Invalid input data\n"); + return -EINVAL; + } + + dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, + panel_data); + + ret = mdss_dp_regulator_ctrl(dp_drv, true); + if (ret) { + pr_err("failed to enable regulators\n"); + goto vreg_error; + } + + mdss_dp_pinctrl_set_state(dp_drv, true); + mdss_dp_config_gpios(dp_drv, true); + + ret = mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, true); + if (ret) { + pr_err("Unabled to start core clocks\n"); + goto clk_error; + } + + mdss_dp_aux_init(dp_drv); + + mdss_dp_phy_reset(&dp_drv->ctrl_io); + mdss_dp_aux_reset(&dp_drv->ctrl_io); + mdss_dp_phy_initialize(dp_drv); + mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true); + + pr_debug("Ctrl_hw_rev =0x%x, phy hw_rev =0x%x\n", + mdss_dp_get_ctrl_hw_version(&dp_drv->ctrl_io), + mdss_dp_get_phy_hw_version(&dp_drv->phy_io)); + + return ret; + +clk_error: + mdss_dp_regulator_ctrl(dp_drv, false); +vreg_error: + return ret; +} + +static int mdss_dp_event_handler(struct mdss_panel_data *pdata, + int event, void *arg) +{ + int rc = 0; + + pr_debug("event=%d\n", event); + switch (event) { + case MDSS_EVENT_UNBLANK: + rc = mdss_dp_on(pdata); + break; + case MDSS_EVENT_PANEL_OFF: + rc = mdss_dp_off(pdata); + break; + } + return rc; +} + +static int mdss_dp_remove(struct platform_device *pdev) +{ + struct mdss_dp_drv_pdata *dp_drv = NULL; + + dp_drv = platform_get_drvdata(pdev); + + iounmap(dp_drv->ctrl_io.base); + dp_drv->ctrl_io.base = NULL; + iounmap(dp_drv->phy_io.base); + dp_drv->phy_io.base = NULL; + + return 0; +} + +static int mdss_dp_device_register(struct mdss_dp_drv_pdata *dp_drv) +{ + int ret; + + ret = dp_init_panel_info(dp_drv); + if (ret) { + DEV_ERR("%s: dp_init_panel_info failed\n", __func__); + return ret; + } + + dp_drv->panel_data.event_handler = mdss_dp_event_handler; + + dp_drv->panel_data.panel_info.cont_splash_enabled = + dp_drv->cont_splash; + + ret = mdss_register_panel(dp_drv->pdev, &dp_drv->panel_data); + if (ret) { + dev_err(&(dp_drv->pdev->dev), "unable to register dp\n"); + return ret; + } + + pr_info("dp initialized\n"); + + return 0; +} + +/* + * Retrieve dp Resources + */ +static int mdss_retrieve_dp_ctrl_resources(struct platform_device *pdev, + struct mdss_dp_drv_pdata *dp_drv) +{ + int rc = 0; + u32 index; + + rc = of_property_read_u32(pdev->dev.of_node, "cell-index", &index); + if (rc) { + dev_err(&pdev->dev, + "Cell-index not specified, rc=%d\n", + rc); + return rc; + } + + rc = msm_dss_ioremap_byname(pdev, &dp_drv->ctrl_io, "dp_ctrl"); + if (rc) { + pr_err("%d unable to remap dp ctrl resources\n", + __LINE__); + return rc; + } + dp_drv->base = dp_drv->ctrl_io.base; + dp_drv->base_size = dp_drv->ctrl_io.len; + + rc = msm_dss_ioremap_byname(pdev, &dp_drv->phy_io, "dp_phy"); + if (rc) { + pr_err("%d unable to remap dp PHY resources\n", + __LINE__); + return rc; + } + + rc = msm_dss_ioremap_byname(pdev, &dp_drv->tcsr_reg_io, + "tcsr_regs"); + if (rc) { + pr_err("%d unable to remap dp tcsr_reg resources\n", + __LINE__); + return rc; + } + + pr_debug("DP Driver base=%p size=%x\n", + dp_drv->base, dp_drv->base_size); + + mdss_debug_register_base("dp", + dp_drv->base, dp_drv->base_size, NULL); + + return 0; +} + +static void mdss_dp_video_ready(struct mdss_dp_drv_pdata *dp) +{ + pr_debug("dp_video_ready\n"); + complete(&dp->video_comp); +} + +static void mdss_dp_idle_patterns_sent(struct mdss_dp_drv_pdata *dp) +{ + pr_debug("idle_patterns_sent\n"); + complete(&dp->idle_comp); +} + +static void mdss_dp_do_link_train(struct mdss_dp_drv_pdata *dp) +{ + if (dp->cont_splash) + return; + + mdss_dp_link_train(dp); +} + +static void mdss_dp_event_work(struct work_struct *work) +{ + struct mdss_dp_drv_pdata *dp = NULL; + struct delayed_work *dw = to_delayed_work(work); + unsigned long flag; + u32 todo = 0, dp_config_pkt[2]; + + if (!dw) { + pr_err("invalid work structure\n"); + return; + } + + dp = container_of(dw, struct mdss_dp_drv_pdata, dwork); + + spin_lock_irqsave(&dp->event_lock, flag); + todo = dp->current_event; + dp->current_event = 0; + spin_unlock_irqrestore(&dp->event_lock, flag); + + pr_debug("todo=%x\n", todo); + + switch (todo) { + case EV_EDID_READ: + mdss_dp_edid_read(dp, 0); + break; + case EV_DPCD_CAP_READ: + mdss_dp_dpcd_cap_read(dp); + break; + case EV_DPCD_STATUS_READ: + mdss_dp_dpcd_status_read(dp); + break; + case EV_LINK_TRAIN: + mdss_dp_do_link_train(dp); + break; + case EV_VIDEO_READY: + mdss_dp_video_ready(dp); + break; + case EV_IDLE_PATTERNS_SENT: + mdss_dp_idle_patterns_sent(dp); + break; + case EV_USBPD_DISCOVER_MODES: + usbpd_send_svdm(dp->pd, USB_C_DP_SID, USBPD_SVDM_DISCOVER_MODES, + SVDM_CMD_TYPE_INITIATOR, 0x0, 0x0, 0x0); + break; + case EV_USBPD_ENTER_MODE: + usbpd_send_svdm(dp->pd, USB_C_DP_SID, USBPD_SVDM_ENTER_MODE, + SVDM_CMD_TYPE_INITIATOR, 0x1, 0x0, 0x0); + break; + case EV_USBPD_EXIT_MODE: + usbpd_send_svdm(dp->pd, USB_C_DP_SID, USBPD_SVDM_EXIT_MODE, + SVDM_CMD_TYPE_INITIATOR, 0x1, 0x0, 0x0); + break; + case EV_USBPD_DP_STATUS: + usbpd_send_svdm(dp->pd, USB_C_DP_SID, DP_VDM_STATUS, + SVDM_CMD_TYPE_INITIATOR, 0x1, 0x0, 0x0); + break; + case EV_USBPD_DP_CONFIGURE: + dp_config_pkt[0] = SVDM_HDR(USB_C_DP_SID, VDM_VERSION, 0x1, + SVDM_CMD_TYPE_INITIATOR, DP_VDM_CONFIGURE); + dp_config_pkt[1] = mdss_dp_usbpd_gen_config_pkt(dp); + usbpd_send_svdm(dp->pd, USB_C_DP_SID, DP_VDM_CONFIGURE, + SVDM_CMD_TYPE_INITIATOR, 0x1, dp_config_pkt, 0x2); + break; + default: + pr_err("Unknown event:%d\n", todo); + } +} + +static void dp_send_events(struct mdss_dp_drv_pdata *dp, u32 events) +{ + spin_lock(&dp->event_lock); + dp->current_event = events; + queue_delayed_work(dp->workq, + &dp->dwork, HZ); + spin_unlock(&dp->event_lock); +} + +irqreturn_t dp_isr(int irq, void *ptr) +{ + struct mdss_dp_drv_pdata *dp = (struct mdss_dp_drv_pdata *)ptr; + unsigned char *base = dp->base; + u32 isr1, isr2, mask1, mask2; + u32 ack; + + spin_lock(&dp->lock); + isr1 = dp_read(base + DP_INTR_STATUS); + isr2 = dp_read(base + DP_INTR_STATUS2); + + mask1 = isr1 & dp->mask1; + mask2 = isr2 & dp->mask2; + + isr1 &= ~mask1; /* remove masks bit */ + isr2 &= ~mask2; + + pr_debug("isr=%x mask=%x isr2=%x mask2=%x\n", + isr1, mask1, isr2, mask2); + + ack = isr1 & EDP_INTR_STATUS1; + ack <<= 1; /* ack bits */ + ack |= mask1; + dp_write(base + DP_INTR_STATUS, ack); + + ack = isr2 & EDP_INTR_STATUS2; + ack <<= 1; /* ack bits */ + ack |= mask2; + dp_write(base + DP_INTR_STATUS2, ack); + spin_unlock(&dp->lock); + + if (isr1 & EDP_INTR_HPD) { + isr1 &= ~EDP_INTR_HPD; /* clear */ + mdss_dp_host_init(&dp->panel_data); + dp_send_events(dp, EV_LINK_TRAIN); + } + + if (isr2 & EDP_INTR_READY_FOR_VIDEO) + dp_send_events(dp, EV_VIDEO_READY); + + if (isr2 & EDP_INTR_IDLE_PATTERNs_SENT) + dp_send_events(dp, EV_IDLE_PATTERNS_SENT); + + if (isr1 && dp->aux_cmd_busy) { + /* clear DP_AUX_TRANS_CTRL */ + dp_write(base + DP_AUX_TRANS_CTRL, 0); + /* read DP_INTERRUPT_TRANS_NUM */ + dp->aux_trans_num = + dp_read(base + DP_INTERRUPT_TRANS_NUM); + + if (dp->aux_cmd_i2c) + dp_aux_i2c_handler(dp, isr1); + else + dp_aux_native_handler(dp, isr1); + } + + return IRQ_HANDLED; +} + +static int mdss_dp_event_setup(struct mdss_dp_drv_pdata *dp) +{ + + spin_lock_init(&dp->event_lock); + dp->workq = create_workqueue("mdss_dp_hpd"); + if (!dp->workq) { + pr_err("%s: Error creating workqueue\n", __func__); + return -EPERM; + } + + INIT_DELAYED_WORK(&dp->dwork, mdss_dp_event_work); + return 0; +} + +static void usbpd_connect_callback(struct usbpd_svid_handler *hdlr) +{ + struct mdss_dp_drv_pdata *dp_drv; + + dp_drv = container_of(hdlr, struct mdss_dp_drv_pdata, svid_handler); + if (!dp_drv->pd) { + pr_err("get_usbpd phandle failed\n"); + return; + } + + mutex_lock(&dp_drv->pd_msg_mutex); + dp_drv->cable_connected = true; + dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES); + mutex_unlock(&dp_drv->pd_msg_mutex); + pr_debug("discover_mode event sent\n"); +} + +static void usbpd_disconnect_callback(struct usbpd_svid_handler *hdlr) +{ + struct mdss_dp_drv_pdata *dp_drv; + + dp_drv = container_of(hdlr, struct mdss_dp_drv_pdata, svid_handler); + if (!dp_drv->pd) { + pr_err("get_usbpd phandle failed\n"); + return; + } + + pr_debug("cable disconnected\n"); + mutex_lock(&dp_drv->pd_msg_mutex); + dp_drv->cable_connected = false; + mutex_unlock(&dp_drv->pd_msg_mutex); +} + +static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, + enum usbpd_svdm_cmd_type cmd_type, + const u32 *vdos, int num_vdos) +{ + struct mdss_dp_drv_pdata *dp_drv; + + dp_drv = container_of(hdlr, struct mdss_dp_drv_pdata, svid_handler); + if (!dp_drv->pd) { + pr_err("get_usbpd phandle failed\n"); + return; + } + + pr_debug("callback -> cmd: 0x%x, *vdos = 0x%x, num_vdos = %d\n", + cmd, *vdos, num_vdos); + + switch (cmd) { + case USBPD_SVDM_DISCOVER_MODES: + if (cmd_type == SVDM_CMD_TYPE_RESP_ACK) { + dp_drv->alt_mode.dp_cap.response = *vdos; + mdss_dp_usbpd_ext_capabilities + (&dp_drv->alt_mode.dp_cap); + dp_drv->alt_mode.current_state = DISCOVER_MODES_DONE; + dp_send_events(dp_drv, EV_USBPD_ENTER_MODE); + } else { + pr_err("unknown response: %d for Discover_modes\n", + cmd_type); + } + break; + case USBPD_SVDM_ENTER_MODE: + if (cmd_type == SVDM_CMD_TYPE_RESP_ACK) { + dp_drv->alt_mode.current_state = ENTER_MODE_DONE; + dp_send_events(dp_drv, EV_USBPD_DP_STATUS); + } else { + pr_err("unknown response: %d for Enter_mode\n", + cmd_type); + } + break; + case USBPD_SVDM_ATTENTION: + if (cmd_type == SVDM_CMD_TYPE_INITIATOR) { + pr_debug("Attention. cmd_type=%d\n", + cmd_type); + if (!dp_drv->alt_mode.current_state + == ENTER_MODE_DONE) { + pr_debug("sending discover_mode\n"); + dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES); + break; + } + if (num_vdos == 1) { + dp_drv->alt_mode.dp_status.response = *vdos; + mdss_dp_usbpd_ext_dp_status + (&dp_drv->alt_mode.dp_status); + if (dp_drv->alt_mode.dp_status.hpd_high) { + pr_debug("HPD high\n"); + dp_drv->alt_mode.current_state = + DP_STATUS_DONE; + dp_send_events + (dp_drv, EV_USBPD_DP_CONFIGURE); + } + } + } else { + pr_debug("unknown response: %d for Attention\n", + cmd_type); + } + break; + case DP_VDM_STATUS: + if (cmd_type == SVDM_CMD_TYPE_RESP_ACK) { + dp_drv->alt_mode.dp_status.response = *vdos; + mdss_dp_usbpd_ext_dp_status + (&dp_drv->alt_mode.dp_status); + if (dp_drv->alt_mode.dp_status.hpd_high) { + pr_debug("HDP high\n"); + dp_drv->alt_mode.current_state = + DP_STATUS_DONE; + dp_send_events(dp_drv, EV_USBPD_DP_CONFIGURE); + } + } else { + pr_err("unknown response: %d for DP_Status\n", + cmd_type); + } + break; + case DP_VDM_CONFIGURE: + if ((dp_drv->cable_connected == true) + || (cmd_type == SVDM_CMD_TYPE_RESP_ACK)) { + dp_drv->alt_mode.current_state = DP_CONFIGURE_DONE; + pr_debug("config USBPD to DP done\n"); + mdss_dp_host_init(&dp_drv->panel_data); + } else { + pr_err("unknown response: %d for DP_Configure\n", + cmd_type); + } + break; + default: + pr_err("unknown cmd: %d\n", cmd); + break; + } +} + +static int mdss_dp_usbpd_setup(struct mdss_dp_drv_pdata *dp_drv) +{ + int ret = 0; + const char *pd_phandle = "qcom,dp-usbpd-detection"; + + dp_drv->pd = devm_usbpd_get_by_phandle(&dp_drv->pdev->dev, + pd_phandle); + + if (IS_ERR(dp_drv->pd)) { + pr_err("get_usbpd phandle failed (%ld)\n", + PTR_ERR(dp_drv->pd)); + return PTR_ERR(dp_drv->pd); + } + + dp_drv->svid_handler.svid = USB_C_DP_SID; + dp_drv->svid_handler.vdm_received = NULL; + dp_drv->svid_handler.connect = &usbpd_connect_callback; + dp_drv->svid_handler.svdm_received = &usbpd_response_callback; + dp_drv->svid_handler.disconnect = &usbpd_disconnect_callback; + + ret = usbpd_register_svid(dp_drv->pd, &dp_drv->svid_handler); + if (ret) { + pr_err("usbpd registration failed\n"); + return -ENODEV; + } + + return ret; +} + +static int mdss_dp_probe(struct platform_device *pdev) +{ + int ret, i; + struct mdss_dp_drv_pdata *dp_drv; + struct mdss_panel_cfg *pan_cfg = NULL; + struct mdss_util_intf *util; + + util = mdss_get_util_intf(); + if (!util) { + pr_err("Failed to get mdss utility functions\n"); + return -ENODEV; + } + + if (!util->mdp_probe_done) { + pr_err("MDP not probed yet!\n"); + return -EPROBE_DEFER; + } + + if (!pdev || !pdev->dev.of_node) { + pr_err("pdev not found for DP controller\n"); + return -ENODEV; + } + + pan_cfg = mdss_panel_intf_type(MDSS_PANEL_INTF_EDP); + if (IS_ERR(pan_cfg)) { + return PTR_ERR(pan_cfg); + } else if (pan_cfg) { + pr_debug("DP as prim not supported\n"); + return -ENODEV; + } + + dp_drv = devm_kzalloc(&pdev->dev, sizeof(*dp_drv), GFP_KERNEL); + if (dp_drv == NULL) + return -ENOMEM; + + dp_drv->pdev = pdev; + dp_drv->pdev->id = 1; + dp_drv->mdss_util = util; + dp_drv->clk_on = 0; + dp_drv->aux_rate = 19200000; + dp_drv->mask1 = EDP_INTR_MASK1; + dp_drv->mask2 = EDP_INTR_MASK2; + mutex_init(&dp_drv->emutex); + mutex_init(&dp_drv->host_mutex); + mutex_init(&dp_drv->pd_msg_mutex); + spin_lock_init(&dp_drv->lock); + + if (mdss_dp_usbpd_setup(dp_drv)) { + pr_err("Error usbpd setup!\n"); + devm_kfree(&pdev->dev, dp_drv); + dp_drv = NULL; + return -EPROBE_DEFER; + } + + ret = mdss_retrieve_dp_ctrl_resources(pdev, dp_drv); + if (ret) + goto probe_err; + + /* Parse the regulator information */ + for (i = DP_CORE_PM; i < DP_MAX_PM; i++) { + ret = mdss_dp_get_dt_vreg_data(&pdev->dev, + pdev->dev.of_node, &dp_drv->power_data[i], i); + if (ret) { + pr_err("get_dt_vreg_data failed for %s. rc=%d\n", + __mdss_dp_pm_name(i), ret); + i--; + for (; i >= DP_CORE_PM; i--) + mdss_dp_put_dt_vreg_data(&pdev->dev, + &dp_drv->power_data[i]); + goto probe_err; + } + } + + ret = mdss_dp_get_dt_clk_data(&pdev->dev, dp_drv); + if (ret) { + DEV_ERR("get_dt_clk_data failed.ret=%d\n", + ret); + goto probe_err; + } + + ret = mdss_dp_regulator_init(pdev, dp_drv); + if (ret) + goto probe_err; + + ret = mdss_dp_clk_init(dp_drv, + &pdev->dev, true); + if (ret) { + DEV_ERR("clk_init failed.ret=%d\n", + ret); + goto probe_err; + } + + ret = mdss_dp_irq_setup(dp_drv); + if (ret) + goto probe_err; + + ret = mdss_dp_event_setup(dp_drv); + if (ret) + goto probe_err; + + dp_drv->cont_splash = dp_drv->mdss_util->panel_intf_status(DISPLAY_1, + MDSS_PANEL_INTF_EDP) ? true : false; + + platform_set_drvdata(pdev, dp_drv); + + ret = mdss_dp_pinctrl_init(pdev, dp_drv); + if (ret) { + pr_err("pinctrl init failed, ret=%d\n", + ret); + goto probe_err; + } + + ret = mdss_dp_parse_gpio_params(pdev, dp_drv); + if (ret) { + pr_err("failed to parse gpio params, ret=%d\n", + ret); + goto probe_err; + } + + mdss_dp_device_register(dp_drv); + + dp_drv->inited = true; + + pr_debug("done\n"); + + dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES); + + return 0; + +probe_err: + iounmap(dp_drv->ctrl_io.base); + iounmap(dp_drv->phy_io.base); + if (dp_drv) + devm_kfree(&pdev->dev, dp_drv); + return ret; + +} + +static const struct of_device_id msm_mdss_dp_dt_match[] = { + {.compatible = "qcom,mdss-dp"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_mdss_dp_dt_match); + +static struct platform_driver mdss_dp_driver = { + .probe = mdss_dp_probe, + .remove = mdss_dp_remove, + .shutdown = NULL, + .driver = { + .name = "mdss_dp", + .of_match_table = msm_mdss_dp_dt_match, + }, +}; + +static int __init mdss_dp_init(void) +{ + int ret; + + ret = platform_driver_register(&mdss_dp_driver); + if (ret) { + pr_err("driver register failed"); + return ret; + } + + return ret; +} +module_init(mdss_dp_init); + +static void __exit mdss_dp_driver_cleanup(void) +{ + platform_driver_unregister(&mdss_dp_driver); +} +module_exit(mdss_dp_driver_cleanup); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DP controller driver"); diff --git a/drivers/video/fbdev/msm/mdss_edp.h b/drivers/video/fbdev/msm/mdss_dp.h index cd83c382a227..008e7d687dbd 100644 --- a/drivers/video/fbdev/msm/mdss_edp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2014, 2016 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,13 +11,24 @@ * */ -#ifndef MDSS_EDP_H -#define MDSS_EDP_H +#ifndef MDSS_DP_H +#define MDSS_DP_H +#include <linux/list.h> +#include <linux/mdss_io_util.h> +#include <linux/irqreturn.h> +#include <linux/pinctrl/consumer.h> +#include <linux/gpio.h> #include <linux/of_gpio.h> +#include <linux/usb/usbpd.h> -#define edp_read(offset) readl_relaxed((offset)) -#define edp_write(offset, data) writel_relaxed((data), (offset)) +#include "mdss_hdmi_util.h" +#include "video/msm_hdmi_modes.h" +#include "mdss.h" +#include "mdss_panel.h" + +#define dp_read(offset) readl_relaxed((offset)) +#define dp_write(offset, data) writel_relaxed((data), (offset)) #define AUX_CMD_FIFO_LEN 144 #define AUX_CMD_MAX 16 @@ -73,7 +84,7 @@ #define EDP_INTR_STATUS1 \ - (EDP_INTR_HPD | EDP_INTR_AUX_I2C_DONE| \ + (EDP_INTR_AUX_I2C_DONE| \ EDP_INTR_WRONG_ADDR | EDP_INTR_TIMEOUT | \ EDP_INTR_NACK_DEFER | EDP_INTR_WRONG_DATA_CNT | \ EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER | \ @@ -93,21 +104,6 @@ #define EDP_INTR_MASK2 (EDP_INTR_STATUS2 << 2) - -#define EDP_MAINLINK_CTRL 0x004 -#define EDP_STATE_CTRL 0x008 -#define EDP_MAINLINK_READY 0x084 - -#define EDP_AUX_CTRL 0x300 -#define EDP_INTERRUPT_STATUS 0x308 -#define EDP_INTERRUPT_STATUS_2 0x30c -#define EDP_AUX_DATA 0x314 -#define EDP_AUX_TRANS_CTRL 0x318 -#define EDP_AUX_STATUS 0x324 - -#define EDP_PHY_EDPPHY_GLB_VM_CFG0 0x510 -#define EDP_PHY_EDPPHY_GLB_VM_CFG1 0x514 - struct edp_cmd { char read; /* 1 == read, 0 == write */ char i2c; /* 1 == i2c cmd, 0 == native cmd */ @@ -127,6 +123,75 @@ struct edp_buf { char i2c; /* 1 == i2c cmd, 0 == native cmd */ }; +/* USBPD-TypeC specific Macros */ +#define VDM_VERSION 0x0 +#define USB_C_DP_SID 0xFF01 + +enum dp_pm_type { + DP_CORE_PM, + DP_CTRL_PM, + DP_PHY_PM, + DP_MAX_PM +}; + +#define PIN_ASSIGN_A BIT(0) +#define PIN_ASSIGN_B BIT(1) +#define PIN_ASSIGN_C BIT(2) +#define PIN_ASSIGN_D BIT(3) +#define PIN_ASSIGN_E BIT(4) +#define PIN_ASSIGN_F BIT(5) + +#define SVDM_HDR(svid, ver, mode, cmd_type, cmd) \ + (((svid) << 16) | (1 << 15) | ((ver) << 13) \ + | ((mode) << 8) | ((cmd_type) << 6) | (cmd)) + +/* DP specific VDM commands */ +#define DP_VDM_STATUS 0x10 +#define DP_VDM_CONFIGURE 0x11 + +enum dp_port_cap { + PORT_NONE = 0, + PORT_UFP_D, + PORT_DFP_D, + PORT_D_UFP_D, +}; + +struct usbpd_dp_capabilities { + u32 response; + enum dp_port_cap s_port; + bool receptacle_state; + u8 ulink_pin_config; + u8 dlink_pin_config; +}; + +struct usbpd_dp_status { + u32 response; + enum dp_port_cap c_port; + bool low_pow_st; + bool adaptor_dp_en; + bool multi_func; + bool switch_to_usb_config; + bool exit_dp_mode; + bool hpd_high; + bool hpd_irq; +}; + +enum dp_alt_mode_state { + ALT_MODE_INIT_STATE = 0, + DISCOVER_MODES_DONE, + ENTER_MODE_DONE, + DP_STATUS_DONE, + DP_CONFIGURE_DONE, + UNKNOWN_STATE, +}; + +struct dp_alt_mode { + struct usbpd_dp_capabilities dp_cap; + struct usbpd_dp_status dp_status; + u32 usbpd_dp_config; + enum dp_alt_mode_state current_state; +}; + #define DPCD_ENHANCED_FRAME BIT(0) #define DPCD_TPS3 BIT(1) #define DPCD_MAX_DOWNSPREAD_0_5 BIT(2) @@ -139,26 +204,37 @@ struct edp_buf { #define EV_DPCD_CAP_READ BIT(2) #define EV_DPCD_STATUS_READ BIT(3) #define EV_LINK_TRAIN BIT(4) -#define EV_IDLE_PATTERNS_SENT BIT(30) -#define EV_VIDEO_READY BIT(31) +#define EV_IDLE_PATTERNS_SENT BIT(5) +#define EV_VIDEO_READY BIT(6) + +#define EV_USBPD_DISCOVER_MODES BIT(7) +#define EV_USBPD_ENTER_MODE BIT(8) +#define EV_USBPD_DP_STATUS BIT(9) +#define EV_USBPD_DP_CONFIGURE BIT(10) +#define EV_USBPD_CC_PIN_POLARITY BIT(11) +#define EV_USBPD_EXIT_MODE BIT(12) -/* edp state ctrl */ +/* dp state ctrl */ #define ST_TRAIN_PATTERN_1 BIT(0) #define ST_TRAIN_PATTERN_2 BIT(1) #define ST_TRAIN_PATTERN_3 BIT(2) -#define ST_SYMBOL_ERR_RATE_MEASUREMENT BIT(3) -#define ST_PRBS7 BIT(4) -#define ST_CUSTOM_80_BIT_PATTERN BIT(5) -#define ST_SEND_VIDEO BIT(6) -#define ST_PUSH_IDLE BIT(7) +#define ST_TRAIN_PATTERN_4 BIT(3) +#define ST_SYMBOL_ERR_RATE_MEASUREMENT BIT(4) +#define ST_PRBS7 BIT(5) +#define ST_CUSTOM_80_BIT_PATTERN BIT(6) +#define ST_SEND_VIDEO BIT(7) +#define ST_PUSH_IDLE BIT(8) /* sink power state */ #define SINK_POWER_ON 1 #define SINK_POWER_OFF 2 -#define EDP_LINK_RATE_162 6 /* 1.62G = 270M * 6 */ -#define EDP_LINK_RATE_270 10 /* 2.70G = 270M * 10 */ -#define EDP_LINK_RATE_MAX EDP_LINK_RATE_270 +#define DP_LINK_RATE_162 6 /* 1.62G = 270M * 6 */ +#define DP_LINK_RATE_270 10 /* 2.70G = 270M * 10 */ +#define DP_LINK_RATE_540 20 /* 5.40G = 270M * 20 */ +#define DP_LINK_RATE_MAX DP_LINK_RATE_540 + +#define DP_LINK_RATE_MULTIPLIER 27000000 struct dpcd_cap { char major; @@ -215,7 +291,7 @@ struct edp_edid { short id_product; char version; char revision; - char video_intf; /* edp == 0x5 */ + char video_intf; /* dp == 0x5 */ char color_depth; /* 6, 8, 10, 12 and 14 bits */ char color_format; /* RGB 4:4:4, YCrCb 4:4:4, Ycrcb 4:2:2 */ char dpm; /* display power management */ @@ -227,7 +303,7 @@ struct edp_edid { struct display_timing_desc timing[4]; }; -struct edp_statistic { +struct dp_statistic { u32 intr_hpd; u32 intr_aux_i2c_done; u32 intr_wrong_addr; @@ -247,26 +323,41 @@ struct edp_statistic { u32 aux_native_rx; }; - #define DPCD_LINK_VOLTAGE_MAX 4 #define DPCD_LINK_PRE_EMPHASIS_MAX 4 -#define HPD_EVENT_MAX 8 +struct dp_pinctrl_res { + struct pinctrl *pinctrl; + struct pinctrl_state *state_active; + struct pinctrl_state *state_hpd_active; + struct pinctrl_state *state_suspend; +}; + +irqreturn_t dp_isr(int irq, void *ptr); -struct mdss_edp_drv_pdata { +struct mdss_dp_drv_pdata { /* device driver */ int (*on) (struct mdss_panel_data *pdata); int (*off) (struct mdss_panel_data *pdata); struct platform_device *pdev; + struct usbpd *pd; + struct usbpd_svid_handler svid_handler; + struct dp_alt_mode alt_mode; + struct mutex emutex; int clk_cnt; int cont_splash; bool inited; - int delay_link_train; + bool core_power; + bool core_clks_on; + bool link_clks_on; - /* edp specific */ + /* dp specific */ unsigned char *base; + struct dss_io_data ctrl_io; + struct dss_io_data phy_io; + struct dss_io_data tcsr_reg_io; int base_size; unsigned char *mmss_cc_base; u32 mask1; @@ -275,8 +366,8 @@ struct mdss_edp_drv_pdata { struct mdss_panel_data panel_data; struct mdss_util_intf *mdss_util; - int edp_on_cnt; - int edp_off_cnt; + int dp_on_cnt; + int dp_off_cnt; u32 pixel_rate; u32 aux_rate; @@ -289,26 +380,14 @@ struct mdss_edp_drv_pdata { struct dpcd_cap dpcd; /* regulators */ - struct regulator *vdda_vreg; - - /* clocks */ - struct clk *aux_clk; - struct clk *pixel_clk; - struct clk *ahb_clk; - struct clk *link_clk; - struct clk *mdp_core_clk; + struct dss_module_power power_data[DP_MAX_PM]; + struct dp_pinctrl_res pin_res; + int aux_sel_gpio; + int aux_en_gpio; + int usbplug_cc_gpio; + int hpd_gpio; int clk_on; - /* gpios */ - int gpio_panel_en; - int gpio_lvl_en; - - /* backlight */ - struct pwm_device *bl_pwm; - bool is_pwm_enabled; - int lpg_channel; - int pwm_period; - /* hpd */ int gpio_panel_hpd; enum of_gpio_flags hpd_flags; @@ -321,6 +400,9 @@ struct mdss_edp_drv_pdata { struct completion video_comp; struct mutex aux_mutex; struct mutex train_mutex; + struct mutex host_mutex; + struct mutex pd_msg_mutex; + bool cable_connected; u32 aux_cmd_busy; u32 aux_cmd_i2c; int aux_trans_num; @@ -338,43 +420,52 @@ struct mdss_edp_drv_pdata { char valid_boundary; char delay_start; u32 bpp; - struct edp_statistic edp_stat; + struct dp_statistic dp_stat; /* event */ - wait_queue_head_t event_q; - u32 event_pndx; - u32 event_gndx; - u32 event_todo_list[HPD_EVENT_MAX]; + struct workqueue_struct *workq; + struct delayed_work dwork; + u32 current_event; spinlock_t event_lock; spinlock_t lock; + struct hdmi_util_ds_data ds_data; }; -int mdss_edp_aux_clk_enable(struct mdss_edp_drv_pdata *edp_drv); -void mdss_edp_aux_clk_disable(struct mdss_edp_drv_pdata *edp_drv); -int mdss_edp_clk_enable(struct mdss_edp_drv_pdata *edp_drv); -void mdss_edp_clk_disable(struct mdss_edp_drv_pdata *edp_drv); -int mdss_edp_clk_init(struct mdss_edp_drv_pdata *edp_drv); -void mdss_edp_clk_deinit(struct mdss_edp_drv_pdata *edp_drv); -int mdss_edp_prepare_aux_clocks(struct mdss_edp_drv_pdata *edp_drv); -void mdss_edp_unprepare_aux_clocks(struct mdss_edp_drv_pdata *edp_drv); -int mdss_edp_prepare_clocks(struct mdss_edp_drv_pdata *edp_drv); -void mdss_edp_unprepare_clocks(struct mdss_edp_drv_pdata *edp_drv); - -void mdss_edp_dpcd_cap_read(struct mdss_edp_drv_pdata *edp); -int mdss_edp_dpcd_status_read(struct mdss_edp_drv_pdata *edp); -void mdss_edp_edid_read(struct mdss_edp_drv_pdata *edp, int block); -int mdss_edp_link_train(struct mdss_edp_drv_pdata *edp); -void edp_aux_i2c_handler(struct mdss_edp_drv_pdata *edp, u32 isr); -void edp_aux_native_handler(struct mdss_edp_drv_pdata *edp, u32 isr); -void mdss_edp_aux_init(struct mdss_edp_drv_pdata *ep); - -void mdss_edp_fill_link_cfg(struct mdss_edp_drv_pdata *ep); -void mdss_edp_sink_power_down(struct mdss_edp_drv_pdata *ep); -void mdss_edp_state_ctrl(struct mdss_edp_drv_pdata *ep, u32 state); -int mdss_edp_sink_power_state(struct mdss_edp_drv_pdata *ep, char state); -void mdss_edp_lane_power_ctrl(struct mdss_edp_drv_pdata *ep, int up); -void mdss_edp_config_ctrl(struct mdss_edp_drv_pdata *ep); - -void mdss_edp_clk_debug(unsigned char *edp_base, unsigned char *mmss_cc_base); - -#endif /* MDSS_EDP_H */ +static inline const char *__mdss_dp_pm_name(enum dp_pm_type module) +{ + switch (module) { + case DP_CORE_PM: return "DP_CORE_PM"; + case DP_CTRL_PM: return "DP_CTRL_PM"; + case DP_PHY_PM: return "DP_PHY_PM"; + default: return "???"; + } +} + +static inline const char *__mdss_dp_pm_supply_node_name( + enum dp_pm_type module) +{ + switch (module) { + case DP_CORE_PM: return "qcom,core-supply-entries"; + case DP_CTRL_PM: return "qcom,ctrl-supply-entries"; + case DP_PHY_PM: return "qcom,phy-supply-entries"; + default: return "???"; + } +} + +void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp); + +void mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *dp); +int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *dp); +void mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp, int block); +int mdss_dp_link_train(struct mdss_dp_drv_pdata *dp); +void dp_aux_i2c_handler(struct mdss_dp_drv_pdata *dp, u32 isr); +void dp_aux_native_handler(struct mdss_dp_drv_pdata *dp, u32 isr); +void mdss_dp_aux_init(struct mdss_dp_drv_pdata *ep); + +void mdss_dp_fill_link_cfg(struct mdss_dp_drv_pdata *ep); +void mdss_dp_sink_power_down(struct mdss_dp_drv_pdata *ep); +void mdss_dp_lane_power_ctrl(struct mdss_dp_drv_pdata *ep, int up); +void mdss_dp_config_ctrl(struct mdss_dp_drv_pdata *ep); +char mdss_dp_gen_link_clk(struct mdss_panel_info *pinfo, char lane_cnt); + +#endif /* MDSS_DP_H */ diff --git a/drivers/video/fbdev/msm/mdss_edp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index d3a060d6f288..7b14a7efb9dc 100644 --- a/drivers/video/fbdev/msm/mdss_edp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013, 2014, 2016 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,6 +11,8 @@ * */ +#define pr_fmt(fmt) "%s: " fmt, __func__ + #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -28,17 +30,14 @@ #include <linux/of_gpio.h> #include <linux/clk/msm-clk.h> -#include <mach/hardware.h> -#include <mach/gpio.h> -#include <mach/dma.h> - #include "mdss_panel.h" -#include "mdss_edp.h" +#include "mdss_dp.h" +#include "mdss_dp_util.h" /* * edp buffer operation */ -static char *edp_buf_init(struct edp_buf *eb, char *buf, int size) +static char *dp_buf_init(struct edp_buf *eb, char *buf, int size) { eb->start = buf; eb->size = size; @@ -50,7 +49,7 @@ static char *edp_buf_init(struct edp_buf *eb, char *buf, int size) return eb->data; } -static char *edp_buf_reset(struct edp_buf *eb) +static char *dp_buf_reset(struct edp_buf *eb) { eb->data = eb->start; eb->len = 0; @@ -59,23 +58,23 @@ static char *edp_buf_reset(struct edp_buf *eb) return eb->data; } -static char *edp_buf_push(struct edp_buf *eb, int len) +static char *dp_buf_push(struct edp_buf *eb, int len) { eb->data += len; eb->len += len; return eb->data; } -static int edp_buf_trailing(struct edp_buf *eb) +static int dp_buf_trailing(struct edp_buf *eb) { return (int)(eb->end - eb->data); } /* - * edp aux edp_buf_add_cmd: + * edp aux dp_buf_add_cmd: * NO native and i2c command mix allowed */ -static int edp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd) +static int dp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd) { char data; char *bp, *cp; @@ -86,7 +85,7 @@ static int edp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd) else len = cmd->len + 4; - if (edp_buf_trailing(eb) < len) + if (dp_buf_trailing(eb) < len) return 0; /* @@ -111,7 +110,7 @@ static int edp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd) for (i = 0; i < cmd->len; i++) *bp++ = *cp++; } - edp_buf_push(eb, len); + dp_buf_push(eb, len); if (cmd->i2c) eb->i2c++; @@ -121,7 +120,7 @@ static int edp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd) return cmd->len - 1; } -static int edp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base) +static int dp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base) { u32 data; char *dp; @@ -140,8 +139,8 @@ static int edp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base) data &= 0x00ff00; /* index = 0, write */ if (cnt == 0) data |= BIT(31); /* INDEX_WRITE */ - pr_debug("%s: data=%x\n", __func__, data); - edp_write(base + EDP_AUX_DATA, data); + pr_debug("data=%x\n", data); + dp_write(base + DP_AUX_DATA, data); cnt++; dp++; } @@ -151,13 +150,13 @@ static int edp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base) data |= BIT(8); /* I2C */ data |= BIT(9); /* GO */ - pr_debug("%s: data=%x\n", __func__, data); - edp_write(base + EDP_AUX_TRANS_CTRL, data); + pr_debug("data=%x\n", data); + dp_write(base + DP_AUX_TRANS_CTRL, data); return tp->len; } -static int edp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base) +static int dp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base) { u32 data; char *dp; @@ -166,15 +165,15 @@ static int edp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base) data = 0; /* index = 0 */ data |= BIT(31); /* INDEX_WRITE */ data |= BIT(0); /* read */ - edp_write(base + EDP_AUX_DATA, data); + dp_write(base + DP_AUX_DATA, data); dp = rp->data; /* discard first byte */ - data = edp_read(base + EDP_AUX_DATA); + data = dp_read(base + DP_AUX_DATA); for (i = 0; i < len; i++) { - data = edp_read(base + EDP_AUX_DATA); - pr_debug("%s: data=%x\n", __func__, data); + data = dp_read(base + DP_AUX_DATA); + pr_debug("data=%x\n", data); *dp++ = (char)((data >> 8) & 0xff); } @@ -182,7 +181,7 @@ static int edp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base) return len; } -static int edp_aux_write_cmds(struct mdss_edp_drv_pdata *ep, +static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep, struct edp_cmd *cmd) { struct edp_cmd *cm; @@ -193,14 +192,14 @@ static int edp_aux_write_cmds(struct mdss_edp_drv_pdata *ep, ep->aux_cmd_busy = 1; tp = &ep->txp; - edp_buf_reset(tp); + dp_buf_reset(tp); cm = cmd; while (cm) { - pr_debug("%s: i2c=%d read=%d addr=%x len=%d next=%d\n", - __func__, cm->i2c, cm->read, cm->addr, cm->len, + pr_debug("i2c=%d read=%d addr=%x len=%d next=%d\n", + cm->i2c, cm->read, cm->addr, cm->len, cm->next); - ret = edp_buf_add_cmd(tp, cm); + ret = dp_buf_add_cmd(tp, cm); if (ret <= 0) break; if (cm->next == 0) @@ -215,7 +214,7 @@ static int edp_aux_write_cmds(struct mdss_edp_drv_pdata *ep, reinit_completion(&ep->aux_comp); - len = edp_cmd_fifo_tx(&ep->txp, ep->base); + len = dp_cmd_fifo_tx(&ep->txp, ep->base); wait_for_completion(&ep->aux_comp); @@ -229,7 +228,7 @@ static int edp_aux_write_cmds(struct mdss_edp_drv_pdata *ep, return ret; } -static int edp_aux_read_cmds(struct mdss_edp_drv_pdata *ep, +static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, struct edp_cmd *cmds) { struct edp_cmd *cm; @@ -242,16 +241,16 @@ static int edp_aux_read_cmds(struct mdss_edp_drv_pdata *ep, tp = &ep->txp; rp = &ep->rxp; - edp_buf_reset(tp); - edp_buf_reset(rp); + dp_buf_reset(tp); + dp_buf_reset(rp); cm = cmds; len = 0; while (cm) { - pr_debug("%s: i2c=%d read=%d addr=%x len=%d next=%d\n", - __func__, cm->i2c, cm->read, cm->addr, cm->len, + pr_debug("i2c=%d read=%d addr=%x len=%d next=%d\n", + cm->i2c, cm->read, cm->addr, cm->len, cm->next); - ret = edp_buf_add_cmd(tp, cm); + ret = dp_buf_add_cmd(tp, cm); len += cm->len; if (ret <= 0) break; @@ -267,12 +266,12 @@ static int edp_aux_read_cmds(struct mdss_edp_drv_pdata *ep, reinit_completion(&ep->aux_comp); - edp_cmd_fifo_tx(tp, ep->base); + dp_cmd_fifo_tx(tp, ep->base); wait_for_completion(&ep->aux_comp); if (ep->aux_error_num == EDP_AUX_ERR_NONE) - ret = edp_cmd_fifo_rx(rp, len, ep->base); + ret = dp_cmd_fifo_rx(rp, len, ep->base); else ret = ep->aux_error_num; @@ -282,10 +281,10 @@ static int edp_aux_read_cmds(struct mdss_edp_drv_pdata *ep, return ret; } -void edp_aux_native_handler(struct mdss_edp_drv_pdata *ep, u32 isr) +void dp_aux_native_handler(struct mdss_dp_drv_pdata *ep, u32 isr) { - pr_debug("%s: isr=%x\n", __func__, isr); + pr_debug("isr=%x\n", isr); if (isr & EDP_INTR_AUX_I2C_DONE) ep->aux_error_num = EDP_AUX_ERR_NONE; @@ -299,10 +298,10 @@ void edp_aux_native_handler(struct mdss_edp_drv_pdata *ep, u32 isr) complete(&ep->aux_comp); } -void edp_aux_i2c_handler(struct mdss_edp_drv_pdata *ep, u32 isr) +void dp_aux_i2c_handler(struct mdss_dp_drv_pdata *ep, u32 isr) { - pr_debug("%s: isr=%x\n", __func__, isr); + pr_debug("isr=%x\n", isr); if (isr & EDP_INTR_AUX_I2C_DONE) { if (isr & (EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER)) @@ -325,7 +324,7 @@ void edp_aux_i2c_handler(struct mdss_edp_drv_pdata *ep, u32 isr) complete(&ep->aux_comp); } -static int edp_aux_write_buf(struct mdss_edp_drv_pdata *ep, u32 addr, +static int dp_aux_write_buf(struct mdss_dp_drv_pdata *ep, u32 addr, char *buf, int len, int i2c) { struct edp_cmd cmd; @@ -337,10 +336,10 @@ static int edp_aux_write_buf(struct mdss_edp_drv_pdata *ep, u32 addr, cmd.len = len & 0x0ff; cmd.next = 0; - return edp_aux_write_cmds(ep, &cmd); + return dp_aux_write_cmds(ep, &cmd); } -static int edp_aux_read_buf(struct mdss_edp_drv_pdata *ep, u32 addr, +static int dp_aux_read_buf(struct mdss_dp_drv_pdata *ep, u32 addr, int len, int i2c) { struct edp_cmd cmd; @@ -352,7 +351,7 @@ static int edp_aux_read_buf(struct mdss_edp_drv_pdata *ep, u32 addr, cmd.len = len & 0x0ff; cmd.next = 0; - return edp_aux_read_cmds(ep, &cmd); + return dp_aux_read_cmds(ep, &cmd); } /* @@ -360,7 +359,7 @@ static int edp_aux_read_buf(struct mdss_edp_drv_pdata *ep, u32 addr, */ static char edid_hdr[8] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}; -int edp_edid_buf_error(char *buf, int len) +int dp_edid_buf_error(char *buf, int len) { char *bp; int i; @@ -368,7 +367,7 @@ int edp_edid_buf_error(char *buf, int len) bp = buf; if (len < 128) { - pr_err("%s: Error: len=%x\n", __func__, len); + pr_err("Error: len=%x\n", len); return -EINVAL; } @@ -376,12 +375,12 @@ int edp_edid_buf_error(char *buf, int len) csum += *bp++; if (csum != 0) { - pr_err("%s: Error: csum=%x\n", __func__, csum); + pr_err("Error: csum=%x\n", csum); return -EINVAL; } if (strncmp(buf, edid_hdr, strlen(edid_hdr))) { - pr_err("%s: Error: header\n", __func__); + pr_err("Error: header\n"); return -EINVAL; } @@ -389,7 +388,7 @@ int edp_edid_buf_error(char *buf, int len) } -void edp_extract_edid_manufacturer(struct edp_edid *edid, char *buf) +void dp_extract_edid_manufacturer(struct edp_edid *edid, char *buf) { char *bp; char data; @@ -407,10 +406,10 @@ void edp_extract_edid_manufacturer(struct edp_edid *edid, char *buf) edid->id_name[2] = 'A' + data - 1; edid->id_name[3] = 0; - pr_debug("%s: edid manufacturer = %s\n", __func__, edid->id_name); + pr_debug("edid manufacturer = %s\n", edid->id_name); } -void edp_extract_edid_product(struct edp_edid *edid, char *buf) +void dp_extract_edid_product(struct edp_edid *edid, char *buf) { char *bp; u32 data; @@ -423,25 +422,25 @@ void edp_extract_edid_product(struct edp_edid *edid, char *buf) data <<= 8; edid->id_product |= data; - pr_debug("%s: edid product = 0x%x\n", __func__, edid->id_product); + pr_debug("edid product = 0x%x\n", edid->id_product); }; -void edp_extract_edid_version(struct edp_edid *edid, char *buf) +void dp_extract_edid_version(struct edp_edid *edid, char *buf) { edid->version = buf[0x12]; edid->revision = buf[0x13]; - pr_debug("%s: edid version = %d.%d\n", __func__, edid->version, + pr_debug("edid version = %d.%d\n", edid->version, edid->revision); }; -void edp_extract_edid_ext_block_cnt(struct edp_edid *edid, char *buf) +void dp_extract_edid_ext_block_cnt(struct edp_edid *edid, char *buf) { edid->ext_block_cnt = buf[0x7e]; - pr_debug("%s: edid extension = %d\n", __func__, + pr_debug("edid extension = %d\n", edid->ext_block_cnt); }; -void edp_extract_edid_video_support(struct edp_edid *edid, char *buf) +void dp_extract_edid_video_support(struct edp_edid *edid, char *buf) { char *bp; @@ -454,14 +453,14 @@ void edp_extract_edid_video_support(struct edp_edid *edid, char *buf) edid->color_depth *= 2; edid->color_depth += 4; } - pr_debug("%s: Digital Video intf=%d color_depth=%d\n", - __func__, edid->video_intf, edid->color_depth); + pr_debug("Digital Video intf=%d color_depth=%d\n", + edid->video_intf, edid->color_depth); } else { - pr_err("%s: Error, Analog video interface\n", __func__); + pr_err("Error, Analog video interface\n"); } }; -void edp_extract_edid_feature(struct edp_edid *edid, char *buf) +void dp_extract_edid_feature(struct edp_edid *edid, char *buf) { char *bp; char data; @@ -481,11 +480,42 @@ void edp_extract_edid_feature(struct edp_edid *edid, char *buf) } } - pr_debug("%s: edid dpm=%d color_format=%d\n", __func__, + pr_debug("edid dpm=%d color_format=%d\n", edid->dpm, edid->color_format); }; -void edp_extract_edid_detailed_timing_description(struct edp_edid *edid, +char mdss_dp_gen_link_clk(struct mdss_panel_info *pinfo, char lane_cnt) +{ + const u32 encoding_factx10 = 8; + const u32 ln_to_link_ratio = 10; + u32 min_link_rate; + char calc_link_rate = 0; + + pr_debug("clk_rate=%llu, bpp= %d, lane_cnt=%d\n", + pinfo->clk_rate, pinfo->bpp, lane_cnt); + min_link_rate = (pinfo->clk_rate * 10) / + (lane_cnt * encoding_factx10); + min_link_rate = (min_link_rate * pinfo->bpp) + / (DP_LINK_RATE_MULTIPLIER); + min_link_rate /= ln_to_link_ratio; + + pr_debug("min_link_rate = %d\n", min_link_rate); + + if (min_link_rate <= DP_LINK_RATE_162) + calc_link_rate = DP_LINK_RATE_162; + else if (min_link_rate <= DP_LINK_RATE_270) + calc_link_rate = DP_LINK_RATE_270; + else if (min_link_rate <= DP_LINK_RATE_540) + calc_link_rate = DP_LINK_RATE_540; + else { + pr_err("link_rate = %d is unsupported\n", min_link_rate); + calc_link_rate = 0; + } + + return calc_link_rate; +} + +void dp_extract_edid_detailed_timing_description(struct edp_edid *edid, char *buf) { char *bp; @@ -589,22 +619,22 @@ void edp_extract_edid_detailed_timing_description(struct edp_edid *edid, } } - pr_debug("%s: pixel_clock = %d\n", __func__, dp->pclk); + pr_debug("pixel_clock = %d\n", dp->pclk); - pr_debug("%s: horizontal=%d, blank=%d, porch=%d, sync=%d\n" - , __func__, dp->h_addressable, dp->h_blank, + pr_debug("horizontal=%d, blank=%d, porch=%d, sync=%d\n", + dp->h_addressable, dp->h_blank, dp->h_fporch, dp->h_sync_pulse); - pr_debug("%s: vertical=%d, blank=%d, porch=%d, vsync=%d\n" - , __func__, dp->v_addressable, dp->v_blank, + pr_debug("vertical=%d, blank=%d, porch=%d, vsync=%d\n", + dp->v_addressable, dp->v_blank, dp->v_fporch, dp->v_sync_pulse); - pr_debug("%s: panel size in mm, width=%d height=%d\n", __func__, + pr_debug("panel size in mm, width=%d height=%d\n", dp->width_mm, dp->height_mm); - pr_debug("%s: panel border horizontal=%d vertical=%d\n", __func__, + pr_debug("panel border horizontal=%d vertical=%d\n", dp->h_border, dp->v_border); - pr_debug("%s: flags: interlaced=%d stereo=%d sync_type=%d sync_sep=%d\n" - , __func__, dp->interlaced, dp->stereo, + pr_debug("flags: interlaced=%d stereo=%d sync_type=%d sync_sep=%d\n", + dp->interlaced, dp->stereo, dp->sync_type, dp->sync_separate); - pr_debug("%s: polarity vsync=%d, hsync=%d", __func__, + pr_debug("polarity vsync=%d, hsync=%d", dp->vsync_pol, dp->hsync_pol); } @@ -629,67 +659,67 @@ void edp_extract_edid_detailed_timing_description(struct edp_edid *edid, * 0, 75 }; */ -static int edp_aux_chan_ready(struct mdss_edp_drv_pdata *ep) +static int dp_aux_chan_ready(struct mdss_dp_drv_pdata *ep) { int cnt, ret; char data = 0; for (cnt = 5; cnt; cnt--) { - ret = edp_aux_write_buf(ep, 0x50, &data, 1, 1); - pr_debug("%s: ret=%d\n", __func__, ret); + ret = dp_aux_write_buf(ep, 0x50, &data, 1, 1); + pr_debug("ret=%d\n", ret); if (ret >= 0) break; msleep(100); } if (cnt <= 0) { - pr_err("%s: aux chan NOT ready\n", __func__); - return 0; + pr_err("aux chan NOT ready\n"); + return -EIO; } - return 1; + return 0; } -static int edp_sink_edid_read(struct mdss_edp_drv_pdata *ep, int block) +static int dp_sink_edid_read(struct mdss_dp_drv_pdata *ep, int block) { struct edp_buf *rp; int cnt, rlen; int ret = 0; - ret = edp_aux_chan_ready(ep); - if (ret == 0) { - pr_err("%s: aux chan NOT ready\n", __func__); + ret = dp_aux_chan_ready(ep); + if (ret) { + pr_err("aux chan NOT ready\n"); return ret; } for (cnt = 5; cnt; cnt--) { - rlen = edp_aux_read_buf(ep, 0x50, 128, 1); + rlen = dp_aux_read_buf(ep, 0x50, 128, 1); if (rlen > 0) { - pr_debug("%s: rlen=%d\n", __func__, rlen); + pr_debug("rlen=%d\n", rlen); rp = &ep->rxp; - if (!edp_edid_buf_error(rp->data, rp->len)) + if (!dp_edid_buf_error(rp->data, rp->len)) break; } } if (cnt <= 0) { - pr_err("%s: Failed\n", __func__); + pr_err("Failed\n"); return -EINVAL; } - edp_extract_edid_manufacturer(&ep->edid, rp->data); - edp_extract_edid_product(&ep->edid, rp->data); - edp_extract_edid_version(&ep->edid, rp->data); - edp_extract_edid_ext_block_cnt(&ep->edid, rp->data); - edp_extract_edid_video_support(&ep->edid, rp->data); - edp_extract_edid_feature(&ep->edid, rp->data); - edp_extract_edid_detailed_timing_description(&ep->edid, rp->data); + dp_extract_edid_manufacturer(&ep->edid, rp->data); + dp_extract_edid_product(&ep->edid, rp->data); + dp_extract_edid_version(&ep->edid, rp->data); + dp_extract_edid_ext_block_cnt(&ep->edid, rp->data); + dp_extract_edid_video_support(&ep->edid, rp->data); + dp_extract_edid_feature(&ep->edid, rp->data); + dp_extract_edid_detailed_timing_description(&ep->edid, rp->data); return 128; } -static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep, +static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep, int len) { char *bp; @@ -698,9 +728,9 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep, struct edp_buf *rp; int rlen; - rlen = edp_aux_read_buf(ep, 0, len, 0); + rlen = dp_aux_read_buf(ep, 0, len, 0); if (rlen <= 0) { - pr_err("%s: edp aux read failed\n", __func__); + pr_err("edp aux read failed\n"); return; } rp = &ep->rxp; @@ -712,14 +742,14 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep, cap->minor = data & 0x0f; if (--rlen <= 0) return; - pr_debug("%s: version: %d.%d\n", __func__, cap->major, cap->minor); + pr_debug("version: %d.%d\n", cap->major, cap->minor); data = *bp++; /* byte 1 */ /* 162, 270 and 540 MB, symbol rate, NOT bit rate */ cap->max_link_rate = data; if (--rlen <= 0) return; - pr_debug("%s: link_rate=%d\n", __func__, cap->max_link_rate); + pr_debug("link_rate=%d\n", cap->max_link_rate); data = *bp++; /* byte 2 */ if (data & BIT(7)) @@ -731,24 +761,24 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep, cap->max_lane_count = data; if (--rlen <= 0) return; - pr_debug("%s: lane_count=%d\n", __func__, cap->max_lane_count); + pr_debug("lane_count=%d\n", cap->max_lane_count); data = *bp++; /* byte 3 */ if (data & BIT(0)) { cap->flags |= DPCD_MAX_DOWNSPREAD_0_5; - pr_debug("%s: max_downspread\n", __func__); + pr_debug("max_downspread\n"); } if (data & BIT(6)) { cap->flags |= DPCD_NO_AUX_HANDSHAKE; - pr_debug("%s: NO Link Training\n", __func__); + pr_debug("NO Link Training\n"); } if (--rlen <= 0) return; data = *bp++; /* byte 4 */ cap->num_rx_port = (data & BIT(0)) + 1; - pr_debug("%s: rx_ports=%d", __func__, cap->num_rx_port); + pr_debug("rx_ports=%d", cap->num_rx_port); if (--rlen <= 0) return; @@ -760,14 +790,14 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep, data = *bp++; /* byte 8 */ if (data & BIT(1)) { cap->flags |= DPCD_PORT_0_EDID_PRESENTED; - pr_debug("%s: edid presented\n", __func__); + pr_debug("edid presented\n"); } if (--rlen <= 0) return; data = *bp++; /* byte 9 */ cap->rx_port0_buf_size = (data + 1) * 32; - pr_debug("%s: lane_buf_size=%d", __func__, cap->rx_port0_buf_size); + pr_debug("lane_buf_size=%d", cap->rx_port0_buf_size); if (--rlen <= 0) return; @@ -779,19 +809,19 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep, data = *bp++; /* byte 12 */ cap->i2c_speed_ctrl = data; if (cap->i2c_speed_ctrl > 0) - pr_debug("%s: i2c_rate=%d", __func__, cap->i2c_speed_ctrl); + pr_debug("i2c_rate=%d", cap->i2c_speed_ctrl); if (--rlen <= 0) return; data = *bp++; /* byte 13 */ cap->scrambler_reset = data & BIT(0); - pr_debug("%s: scrambler_reset=%d\n", __func__, + pr_debug("scrambler_reset=%d\n", cap->scrambler_reset); if (data & BIT(1)) cap->enhanced_frame++; - pr_debug("%s: enhanced_framing=%d\n", __func__, + pr_debug("enhanced_framing=%d\n", cap->enhanced_frame); if (--rlen <= 0) return; @@ -801,11 +831,11 @@ static void edp_sink_capability_read(struct mdss_edp_drv_pdata *ep, cap->training_read_interval = 4000; /* us */ else cap->training_read_interval = 4000 * data; /* us */ - pr_debug("%s: training_interval=%d\n", __func__, + pr_debug("training_interval=%d\n", cap->training_read_interval); } -static int edp_link_status_read(struct mdss_edp_drv_pdata *ep, int len) +static int dp_link_status_read(struct mdss_dp_drv_pdata *ep, int len) { char *bp; char data; @@ -813,11 +843,11 @@ static int edp_link_status_read(struct mdss_edp_drv_pdata *ep, int len) struct edp_buf *rp; int rlen; - pr_debug("%s: len=%d", __func__, len); + pr_debug("len=%d", len); /* skip byte 0x200 and 0x201 */ - rlen = edp_aux_read_buf(ep, 0x202, len, 0); + rlen = dp_aux_read_buf(ep, 0x202, len, 0); if (rlen < len) { - pr_err("%s: edp aux read failed\n", __func__); + pr_err("edp aux read failed\n"); return 0; } rp = &ep->rxp; @@ -860,7 +890,7 @@ static int edp_link_status_read(struct mdss_edp_drv_pdata *ep, int len) return len; } -static int edp_cap_lane_rate_set(struct mdss_edp_drv_pdata *ep) +static int dp_cap_lane_rate_set(struct mdss_dp_drv_pdata *ep) { char buf[4]; int len = 0; @@ -868,17 +898,17 @@ static int edp_cap_lane_rate_set(struct mdss_edp_drv_pdata *ep) cap = &ep->dpcd; - pr_debug("%s: bw=%x lane=%d\n", __func__, ep->link_rate, ep->lane_cnt); + pr_debug("bw=%x lane=%d\n", ep->link_rate, ep->lane_cnt); buf[0] = ep->link_rate; buf[1] = ep->lane_cnt; if (cap->enhanced_frame) buf[1] |= 0x80; - len = edp_aux_write_buf(ep, 0x100, buf, 2, 0); + len = dp_aux_write_buf(ep, 0x100, buf, 2, 0); return len; } -static int edp_lane_set_write(struct mdss_edp_drv_pdata *ep, int voltage_level, +static int dp_lane_set_write(struct mdss_dp_drv_pdata *ep, int voltage_level, int pre_emphasis_level) { int i; @@ -895,21 +925,21 @@ static int edp_lane_set_write(struct mdss_edp_drv_pdata *ep, int voltage_level, for (i = 0; i < 4; i++) buf[i] = voltage_level | pre_emphasis_level; - pr_debug("%s: p|v=0x%x", __func__, voltage_level | pre_emphasis_level); - return edp_aux_write_buf(ep, 0x103, buf, 4, 0); + pr_debug("p|v=0x%x", voltage_level | pre_emphasis_level); + return dp_aux_write_buf(ep, 0x103, buf, 4, 0); } -static int edp_train_pattern_set_write(struct mdss_edp_drv_pdata *ep, +static int dp_train_pattern_set_write(struct mdss_dp_drv_pdata *ep, int pattern) { char buf[4]; - pr_debug("%s: pattern=%x\n", __func__, pattern); + pr_debug("pattern=%x\n", pattern); buf[0] = pattern; - return edp_aux_write_buf(ep, 0x102, buf, 1, 0); + return dp_aux_write_buf(ep, 0x102, buf, 1, 0); } -static int edp_sink_clock_recovery_done(struct mdss_edp_drv_pdata *ep) +static int dp_sink_clock_recovery_done(struct mdss_dp_drv_pdata *ep) { u32 mask; u32 data; @@ -927,7 +957,7 @@ static int edp_sink_clock_recovery_done(struct mdss_edp_drv_pdata *ep) data |= ep->link_status.lane_01_status; } - pr_debug("%s: data=%x mask=%x\n", __func__, data, mask); + pr_debug("data=%x mask=%x\n", data, mask); data &= mask; if (data == mask) /* all done */ return 1; @@ -935,15 +965,15 @@ static int edp_sink_clock_recovery_done(struct mdss_edp_drv_pdata *ep) return 0; } -static int edp_sink_channel_eq_done(struct mdss_edp_drv_pdata *ep) +static int dp_sink_channel_eq_done(struct mdss_dp_drv_pdata *ep) { u32 mask; u32 data; - pr_debug("%s:\n", __func__); + pr_debug("Entered++\n"); if (!ep->link_status.interlane_align_done) { /* not align */ - pr_err("%s: interlane align failed\n", __func__); + pr_err("interlane align failed\n"); return 0; } @@ -960,7 +990,7 @@ static int edp_sink_channel_eq_done(struct mdss_edp_drv_pdata *ep) data |= ep->link_status.lane_01_status; } - pr_debug("%s: data=%x mask=%x\n", __func__, data, mask); + pr_debug("data=%x mask=%x\n", data, mask); data &= mask; if (data == mask)/* all done */ @@ -969,7 +999,7 @@ static int edp_sink_channel_eq_done(struct mdss_edp_drv_pdata *ep) return 0; } -void edp_sink_train_set_adjust(struct mdss_edp_drv_pdata *ep) +void dp_sink_train_set_adjust(struct mdss_dp_drv_pdata *ep) { int i; int max = 0; @@ -977,8 +1007,8 @@ void edp_sink_train_set_adjust(struct mdss_edp_drv_pdata *ep) /* use the max level across lanes */ for (i = 0; i < ep->lane_cnt; i++) { - pr_debug("%s: lane=%d req_voltage_swing=%d", - __func__, i, ep->link_status.req_voltage_swing[i]); + pr_debug("lane=%d req_voltage_swing=%d", + i, ep->link_status.req_voltage_swing[i]); if (max < ep->link_status.req_voltage_swing[i]) max = ep->link_status.req_voltage_swing[i]; } @@ -988,18 +1018,18 @@ void edp_sink_train_set_adjust(struct mdss_edp_drv_pdata *ep) /* use the max level across lanes */ max = 0; for (i = 0; i < ep->lane_cnt; i++) { - pr_debug(" %s: lane=%d req_pre_emphasis=%d", - __func__, i, ep->link_status.req_pre_emphasis[i]); + pr_debug("lane=%d req_pre_emphasis=%d", + i, ep->link_status.req_pre_emphasis[i]); if (max < ep->link_status.req_pre_emphasis[i]) max = ep->link_status.req_pre_emphasis[i]; } ep->p_level = max; - pr_debug("%s: v_level=%d, p_level=%d", __func__, + pr_debug("v_level=%d, p_level=%d", ep->v_level, ep->p_level); } -static void edp_host_train_set(struct mdss_edp_drv_pdata *ep, int train) +static void dp_host_train_set(struct mdss_dp_drv_pdata *ep, int train) { int bit, cnt; u32 data; @@ -1007,20 +1037,20 @@ static void edp_host_train_set(struct mdss_edp_drv_pdata *ep, int train) bit = 1; bit <<= (train - 1); - pr_debug("%s: bit=%d train=%d\n", __func__, bit, train); - edp_write(ep->base + EDP_STATE_CTRL, bit); + pr_debug("bit=%d train=%d\n", bit, train); + dp_write(ep->base + DP_STATE_CTRL, bit); bit = 8; bit <<= (train - 1); cnt = 10; while (cnt--) { - data = edp_read(ep->base + EDP_MAINLINK_READY); + data = dp_read(ep->base + DP_MAINLINK_READY); if (data & bit) break; } if (cnt == 0) - pr_err("%s: set link_train=%d failed\n", __func__, train); + pr_err("set link_train=%d failed\n", train); } char vm_pre_emphasis[4][4] = { @@ -1038,38 +1068,52 @@ char vm_voltage_swing[4][4] = { {0x1E, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */ }; -static void edp_voltage_pre_emphasise_set(struct mdss_edp_drv_pdata *ep) +static void dp_voltage_pre_emphasise_set(struct mdss_dp_drv_pdata *dp) { u32 value0 = 0; u32 value1 = 0; - pr_debug("%s: v=%d p=%d\n", __func__, ep->v_level, ep->p_level); + pr_debug("v=%d p=%d\n", dp->v_level, dp->p_level); - value0 = vm_pre_emphasis[(int)(ep->v_level)][(int)(ep->p_level)]; - value1 = vm_voltage_swing[(int)(ep->v_level)][(int)(ep->p_level)]; + value0 = vm_voltage_swing[(int)(dp->v_level)][(int)(dp->p_level)]; + value1 = vm_pre_emphasis[(int)(dp->v_level)][(int)(dp->p_level)]; + /* Enable MUX to use Cursor values from these registers */ + value0 |= BIT(5); + value1 |= BIT(5); /* Configure host and panel only if both values are allowed */ if (value0 != 0xFF && value1 != 0xFF) { - edp_write(ep->base + EDP_PHY_EDPPHY_GLB_VM_CFG0, value0); - edp_write(ep->base + EDP_PHY_EDPPHY_GLB_VM_CFG1, value1); - pr_debug("%s: value0=0x%x value1=0x%x", __func__, + dp_write(dp->phy_io.base + + QSERDES_TX0_OFFSET + TXn_TX_DRV_LVL, + value0); + dp_write(dp->phy_io.base + + QSERDES_TX1_OFFSET + TXn_TX_DRV_LVL, + value0); + dp_write(dp->phy_io.base + + QSERDES_TX0_OFFSET + TXn_TX_EMP_POST1_LVL, + value1); + dp_write(dp->phy_io.base + + QSERDES_TX1_OFFSET + TXn_TX_EMP_POST1_LVL, + value1); + + pr_debug("value0=0x%x value1=0x%x", value0, value1); - edp_lane_set_write(ep, ep->v_level, ep->p_level); + dp_lane_set_write(dp, dp->v_level, dp->p_level); } } -static int edp_start_link_train_1(struct mdss_edp_drv_pdata *ep) +static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep) { int tries, old_v_level; int ret = 0; int usleep_time; - pr_debug("%s:", __func__); + pr_debug("Entered++"); - edp_host_train_set(ep, 0x01); /* train_1 */ - edp_voltage_pre_emphasise_set(ep); - edp_train_pattern_set_write(ep, 0x21); /* train_1 */ + dp_host_train_set(ep, 0x01); /* train_1 */ + dp_voltage_pre_emphasise_set(ep); + dp_train_pattern_set_write(ep, 0x21); /* train_1 */ tries = 0; old_v_level = ep->v_level; @@ -1077,8 +1121,8 @@ static int edp_start_link_train_1(struct mdss_edp_drv_pdata *ep) usleep_time = ep->dpcd.training_read_interval; usleep_range(usleep_time, usleep_time); - edp_link_status_read(ep, 6); - if (edp_sink_clock_recovery_done(ep)) { + dp_link_status_read(ep, 6); + if (dp_sink_clock_recovery_done(ep)) { ret = 0; break; } @@ -1099,39 +1143,39 @@ static int edp_start_link_train_1(struct mdss_edp_drv_pdata *ep) old_v_level = ep->v_level; } - edp_sink_train_set_adjust(ep); - edp_voltage_pre_emphasise_set(ep); + dp_sink_train_set_adjust(ep); + dp_voltage_pre_emphasise_set(ep); } return ret; } -static int edp_start_link_train_2(struct mdss_edp_drv_pdata *ep) +static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep) { int tries; int ret = 0; int usleep_time; char pattern; - pr_debug("%s:", __func__); + pr_debug("Entered++"); if (ep->dpcd.flags & DPCD_TPS3) pattern = 0x03; else pattern = 0x02; - edp_host_train_set(ep, pattern); /* train_2 */ - edp_voltage_pre_emphasise_set(ep); - edp_train_pattern_set_write(ep, pattern | 0x20);/* train_2 */ + dp_host_train_set(ep, pattern); /* train_2 */ + dp_voltage_pre_emphasise_set(ep); + dp_train_pattern_set_write(ep, pattern | 0x20);/* train_2 */ tries = 0; while (1) { usleep_time = ep->dpcd.training_read_interval; usleep_range(usleep_time, usleep_time); - edp_link_status_read(ep, 6); + dp_link_status_read(ep, 6); - if (edp_sink_channel_eq_done(ep)) { + if (dp_sink_channel_eq_done(ep)) { ret = 0; break; } @@ -1142,14 +1186,14 @@ static int edp_start_link_train_2(struct mdss_edp_drv_pdata *ep) break; } - edp_sink_train_set_adjust(ep); - edp_voltage_pre_emphasise_set(ep); + dp_sink_train_set_adjust(ep); + dp_voltage_pre_emphasise_set(ep); } return ret; } -static int edp_link_rate_down_shift(struct mdss_edp_drv_pdata *ep) +static int dp_link_rate_down_shift(struct mdss_dp_drv_pdata *ep) { u32 prate, lrate; int rate, lane, max_lane; @@ -1164,7 +1208,7 @@ static int edp_link_rate_down_shift(struct mdss_edp_drv_pdata *ep) prate *= ep->bpp; prate /= 8; /* byte */ - if (rate > EDP_LINK_RATE_162 && rate <= EDP_LINK_RATE_MAX) { + if (rate > DP_LINK_RATE_162 && rate <= DP_LINK_RATE_MAX) { rate -= 4; /* reduce rate */ changed++; } @@ -1179,13 +1223,13 @@ static int edp_link_rate_down_shift(struct mdss_edp_drv_pdata *ep) lrate /= 10; /* byte, 10 bits --> 8 bits */ lrate *= lane; - pr_debug("%s: new lrate=%u prate=%u rate=%d lane=%d p=%d b=%d\n", - __func__, lrate, prate, rate, lane, ep->pixel_rate, ep->bpp); + pr_debug("new lrate=%u prate=%u rate=%d lane=%d p=%d b=%d\n", + lrate, prate, rate, lane, ep->pixel_rate, ep->bpp); if (lrate > prate) { ep->link_rate = rate; ep->lane_cnt = lane; - pr_debug("%s: new rate=%d %d\n", __func__, rate, lane); + pr_debug("new rate=%d %d\n", rate, lane); return 0; } } @@ -1194,90 +1238,104 @@ static int edp_link_rate_down_shift(struct mdss_edp_drv_pdata *ep) return -EINVAL; } -static void edp_clear_training_pattern(struct mdss_edp_drv_pdata *ep) +static int mdss_dp_sink_power_state(struct mdss_dp_drv_pdata *ep, char state) +{ + int ret; + + ret = dp_aux_write_buf(ep, 0x600, &state, 1, 0); + pr_debug("state=%d ret=%d\n", state, ret); + return ret; +} + +static void dp_clear_training_pattern(struct mdss_dp_drv_pdata *ep) { int usleep_time; - pr_debug("%s:\n", __func__); - edp_train_pattern_set_write(ep, 0); + + pr_debug("Entered++\n"); + dp_train_pattern_set_write(ep, 0); usleep_time = ep->dpcd.training_read_interval; usleep_range(usleep_time, usleep_time); } -static int edp_aux_link_train(struct mdss_edp_drv_pdata *ep) +static int dp_aux_link_train(struct mdss_dp_drv_pdata *dp) { int ret = 0; int usleep_time; - ret = edp_aux_chan_ready(ep); - if (ret == 0) { - pr_err("%s: LINK Train failed: aux chan NOT ready\n", __func__); - complete(&ep->train_comp); + ret = dp_aux_chan_ready(dp); + if (ret) { + pr_err("LINK Train failed: aux chan NOT ready\n"); + complete(&dp->train_comp); return ret; } - edp_write(ep->base + EDP_MAINLINK_CTRL, 0x1); + dp_write(dp->base + DP_MAINLINK_CTRL, 0x1); - mdss_edp_sink_power_state(ep, SINK_POWER_ON); + mdss_dp_sink_power_state(dp, SINK_POWER_ON); train_start: - ep->v_level = 0; /* start from default level */ - ep->p_level = 0; - edp_cap_lane_rate_set(ep); - mdss_edp_config_ctrl(ep); - mdss_edp_lane_power_ctrl(ep, 1); - - mdss_edp_state_ctrl(ep, 0); - edp_clear_training_pattern(ep); - usleep_time = ep->dpcd.training_read_interval; + dp->v_level = 0; /* start from default level */ + dp->p_level = 0; + dp_cap_lane_rate_set(dp); + mdss_dp_config_ctrl(dp); + + mdss_dp_state_ctrl(&dp->ctrl_io, 0); + dp_clear_training_pattern(dp); + usleep_time = dp->dpcd.training_read_interval; usleep_range(usleep_time, usleep_time); - ret = edp_start_link_train_1(ep); + ret = dp_start_link_train_1(dp); if (ret < 0) { - if (edp_link_rate_down_shift(ep) == 0) { + if (dp_link_rate_down_shift(dp) == 0) { goto train_start; } else { - pr_err("%s: Training 1 failed\n", __func__); + pr_err("Training 1 failed\n"); ret = -1; goto clear; } } - pr_debug("%s: Training 1 completed successfully\n", __func__); + pr_debug("Training 1 completed successfully\n"); - mdss_edp_state_ctrl(ep, 0); - edp_clear_training_pattern(ep); - ret = edp_start_link_train_2(ep); + mdss_dp_state_ctrl(&dp->ctrl_io, 0); + dp_clear_training_pattern(dp); + ret = dp_start_link_train_2(dp); if (ret < 0) { - if (edp_link_rate_down_shift(ep) == 0) { + if (dp_link_rate_down_shift(dp) == 0) { goto train_start; } else { - pr_err("%s: Training 2 failed\n", __func__); + pr_err("Training 2 failed\n"); ret = -1; goto clear; } } - pr_debug("%s: Training 2 completed successfully\n", __func__); + pr_debug("Training 2 completed successfully\n"); + - mdss_edp_state_ctrl(ep, ST_SEND_VIDEO); clear: - edp_clear_training_pattern(ep); + dp_clear_training_pattern(dp); + if (ret != -1) { + mdss_dp_setup_tr_unit(&dp->ctrl_io); + mdss_dp_state_ctrl(&dp->ctrl_io, ST_SEND_VIDEO); + pr_debug("State_ctrl set to SEND_VIDEO\n"); + } - complete(&ep->train_comp); + complete(&dp->train_comp); return ret; } -void mdss_edp_dpcd_cap_read(struct mdss_edp_drv_pdata *ep) +void mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) { - edp_sink_capability_read(ep, 16); + dp_sink_capability_read(ep, 16); } -int mdss_edp_dpcd_status_read(struct mdss_edp_drv_pdata *ep) +int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *ep) { struct dpcd_link_status *sp; int ret = 0; /* not sync */ - ret = edp_link_status_read(ep, 6); + ret = dp_link_status_read(ep, 6); if (ret) { sp = &ep->link_status; @@ -1287,45 +1345,34 @@ int mdss_edp_dpcd_status_read(struct mdss_edp_drv_pdata *ep) return ret; } -void mdss_edp_fill_link_cfg(struct mdss_edp_drv_pdata *ep) +void mdss_dp_fill_link_cfg(struct mdss_dp_drv_pdata *ep) { struct display_timing_desc *dp; dp = &ep->edid.timing[0]; - ep->pixel_rate = dp->pclk; ep->lane_cnt = ep->dpcd.max_lane_count; - ep->link_rate = ep->dpcd.max_link_rate; - pr_debug("%s: pclk=%d rate=%d lane=%d\n", __func__, + pr_debug("pclk=%d rate=%d lane=%d\n", ep->pixel_rate, ep->link_rate, ep->lane_cnt); } -void mdss_edp_edid_read(struct mdss_edp_drv_pdata *ep, int block) +void mdss_dp_edid_read(struct mdss_dp_drv_pdata *ep, int block) { - edp_sink_edid_read(ep, block); -} - -int mdss_edp_sink_power_state(struct mdss_edp_drv_pdata *ep, char state) -{ - int ret; - - ret = edp_aux_write_buf(ep, 0x600, &state, 1, 0); - pr_debug("%s: state=%d ret=%d\n", __func__, state, ret); - return ret; + dp_sink_edid_read(ep, block); } -int mdss_edp_link_train(struct mdss_edp_drv_pdata *ep) +int mdss_dp_link_train(struct mdss_dp_drv_pdata *ep) { int ret; mutex_lock(&ep->train_mutex); - ret = edp_aux_link_train(ep); + ret = dp_aux_link_train(ep); mutex_unlock(&ep->train_mutex); return ret; } -void mdss_edp_aux_init(struct mdss_edp_drv_pdata *ep) +void mdss_dp_aux_init(struct mdss_dp_drv_pdata *ep) { mutex_init(&ep->aux_mutex); mutex_init(&ep->train_mutex); @@ -1336,6 +1383,6 @@ void mdss_edp_aux_init(struct mdss_edp_drv_pdata *ep) complete(&ep->train_comp); /* make non block at first time */ complete(&ep->video_comp); /* make non block at first time */ - edp_buf_init(&ep->txp, ep->txbuf, sizeof(ep->txbuf)); - edp_buf_init(&ep->rxp, ep->rxbuf, sizeof(ep->rxbuf)); + dp_buf_init(&ep->txp, ep->txbuf, sizeof(ep->txbuf)); + dp_buf_init(&ep->rxp, ep->rxbuf, sizeof(ep->rxbuf)); } diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c new file mode 100644 index 000000000000..f7b27d1e56a1 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dp_util.c @@ -0,0 +1,372 @@ +/* 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. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/io.h> +#include <linux/delay.h> + +#include "mdss_dp_util.h" + +struct mdss_hw mdss_dp_hw = { + .hw_ndx = MDSS_HW_EDP, + .ptr = NULL, + .irq_handler = dp_isr, +}; + +/* DP retrieve ctrl HW version */ +u32 mdss_dp_get_ctrl_hw_version(struct dss_io_data *ctrl_io) +{ + return readl_relaxed(ctrl_io->base + DP_HW_VERSION); +} + +/* DP retrieve phy HW version */ +u32 mdss_dp_get_phy_hw_version(struct dss_io_data *phy_io) +{ + return readl_relaxed(phy_io->base + DP_PHY_REVISION_ID3); +} +/* DP PHY SW reset */ +void mdss_dp_phy_reset(struct dss_io_data *ctrl_io) +{ + writel_relaxed(0x04, ctrl_io->base + DP_PHY_CTRL); /* bit 2 */ + udelay(1000); + writel_relaxed(0x00, ctrl_io->base + DP_PHY_CTRL); +} + +void mdss_dp_switch_usb3_phy_to_dp_mode(struct dss_io_data *tcsr_reg_io) +{ + writel_relaxed(0x01, tcsr_reg_io->base + TCSR_USB3_DP_PHYMODE); +} + +/* DP PHY assert reset for PHY and PLL */ +void mdss_dp_assert_phy_reset(struct dss_io_data *ctrl_io, bool assert) +{ + if (assert) { + /* assert reset line for PHY and PLL */ + writel_relaxed(0x5, + ctrl_io->base + DP_PHY_CTRL); /* bit 0 & 2 */ + } else { + /* remove assert for PLL and PHY reset line */ + writel_relaxed(0x00, ctrl_io->base + DP_PHY_CTRL); + } +} + +/* reset AUX */ +void mdss_dp_aux_reset(struct dss_io_data *ctrl_io) +{ + u32 aux_ctrl = readl_relaxed(ctrl_io->base + DP_AUX_CTRL); + + aux_ctrl |= BIT(1); + writel_relaxed(aux_ctrl, ctrl_io->base + DP_AUX_CTRL); + udelay(1000); + aux_ctrl &= ~BIT(1); + writel_relaxed(aux_ctrl, ctrl_io->base + DP_AUX_CTRL); +} + +/* reset DP Mainlink */ +void mdss_dp_mainlink_reset(struct dss_io_data *ctrl_io) +{ + u32 mainlink_ctrl = readl_relaxed(ctrl_io->base + DP_MAINLINK_CTRL); + + mainlink_ctrl |= BIT(1); + writel_relaxed(mainlink_ctrl, ctrl_io->base + DP_MAINLINK_CTRL); + udelay(1000); + mainlink_ctrl &= ~BIT(1); + writel_relaxed(mainlink_ctrl, ctrl_io->base + DP_MAINLINK_CTRL); +} + +/* Configure HPD */ +void mdss_dp_hpd_configure(struct dss_io_data *ctrl_io, bool enable) +{ + if (enable) { + u32 reftimer = + readl_relaxed(ctrl_io->base + DP_DP_HPD_REFTIMER); + + writel_relaxed(0xf, ctrl_io->base + DP_DP_HPD_INT_ACK); + writel_relaxed(0xf, ctrl_io->base + DP_DP_HPD_INT_MASK); + + /* Enabling REFTIMER */ + reftimer |= BIT(16); + writel_relaxed(0xf, ctrl_io->base + DP_DP_HPD_REFTIMER); + /* Enable HPD */ + writel_relaxed(0x1, ctrl_io->base + DP_DP_HPD_CTRL); + } else { + /*Disable HPD */ + writel_relaxed(0x0, ctrl_io->base + DP_DP_HPD_CTRL); + } +} + +/* Enable/Disable AUX controller */ +void mdss_dp_aux_ctrl(struct dss_io_data *ctrl_io, bool enable) +{ + u32 aux_ctrl = readl_relaxed(ctrl_io->base + DP_AUX_CTRL); + + if (enable) + aux_ctrl |= BIT(0); + else + aux_ctrl &= ~BIT(0); + + writel_relaxed(aux_ctrl, ctrl_io->base + DP_AUX_CTRL); +} + +/* DP Mainlink controller*/ +void mdss_dp_mainlink_ctrl(struct dss_io_data *ctrl_io, bool enable) +{ + u32 mainlink_ctrl = readl_relaxed(ctrl_io->base + DP_MAINLINK_CTRL); + + if (enable) + mainlink_ctrl |= BIT(0); + else + mainlink_ctrl &= ~BIT(0); + + writel_relaxed(mainlink_ctrl, ctrl_io->base + DP_MAINLINK_CTRL); +} + +int mdss_dp_mainlink_ready(struct mdss_dp_drv_pdata *dp, u32 which) +{ + u32 data; + int cnt = 10; + + while (--cnt) { + /* DP_MAINLINK_READY */ + data = readl_relaxed(dp->base + DP_MAINLINK_READY); + if (data & which) { + pr_debug("which=%x ready\n", which); + return 1; + } + udelay(1000); + } + pr_err("which=%x NOT ready\n", which); + + return 0; +} + +/* DP Configuration controller*/ +void mdss_dp_configuration_ctrl(struct dss_io_data *ctrl_io, u32 data) +{ + writel_relaxed(data, ctrl_io->base + DP_CONFIGURATION_CTRL); +} + +/* DP state controller*/ +void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data) +{ + writel_relaxed(data, ctrl_io->base + DP_STATE_CTRL); +} + +void mdss_dp_timing_cfg(struct dss_io_data *ctrl_io, + struct mdss_panel_info *pinfo) +{ + u32 total_ver, total_hor; + u32 data; + + pr_debug("width=%d hporch= %d %d %d\n", + pinfo->xres, pinfo->lcdc.h_back_porch, + pinfo->lcdc.h_front_porch, pinfo->lcdc.h_pulse_width); + + pr_debug("height=%d vporch= %d %d %d\n", + pinfo->yres, pinfo->lcdc.v_back_porch, + pinfo->lcdc.v_front_porch, pinfo->lcdc.v_pulse_width); + + total_hor = pinfo->xres + pinfo->lcdc.h_back_porch + + pinfo->lcdc.h_front_porch + pinfo->lcdc.h_pulse_width; + + total_ver = pinfo->yres + pinfo->lcdc.v_back_porch + + pinfo->lcdc.v_front_porch + pinfo->lcdc.v_pulse_width; + + data = total_ver; + data <<= 16; + data |= total_hor; + /* DP_TOTAL_HOR_VER */ + writel_relaxed(data, ctrl_io->base + DP_TOTAL_HOR_VER); + + data = (pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width); + data <<= 16; + data |= (pinfo->lcdc.h_back_porch + pinfo->lcdc.h_pulse_width); + /* DP_START_HOR_VER_FROM_SYNC */ + writel_relaxed(data, ctrl_io->base + DP_START_HOR_VER_FROM_SYNC); + + data = pinfo->lcdc.v_pulse_width; + data <<= 16; + data |= pinfo->lcdc.h_pulse_width; + /* DP_HSYNC_VSYNC_WIDTH_POLARITY */ + writel_relaxed(data, ctrl_io->base + DP_HSYNC_VSYNC_WIDTH_POLARITY); + + data = pinfo->yres; + data <<= 16; + data |= pinfo->xres; + /* DP_ACTIVE_HOR_VER */ + writel_relaxed(data, ctrl_io->base + DP_ACTIVE_HOR_VER); +} + +void mdss_dp_sw_mvid_nvid(struct dss_io_data *ctrl_io) +{ + writel_relaxed(0x37, ctrl_io->base + DP_SOFTWARE_MVID); + writel_relaxed(0x3c, ctrl_io->base + DP_SOFTWARE_NVID); +} + +void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io) +{ + /* Current Tr unit configuration supports only 1080p */ + writel_relaxed(0x21, ctrl_io->base + DP_MISC1_MISC0); + writel_relaxed(0x0f0016, ctrl_io->base + DP_VALID_BOUNDARY); + writel_relaxed(0x1f, ctrl_io->base + DP_TU); + writel_relaxed(0x0, ctrl_io->base + DP_VALID_BOUNDARY_2); +} + +void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, + struct lane_mapping l_map) +{ + u8 bits_per_lane = 2; + u32 lane_map = ((l_map.lane0 << (bits_per_lane * 0)) + | (l_map.lane1 << (bits_per_lane * 1)) + | (l_map.lane2 << (bits_per_lane * 2)) + | (l_map.lane3 << (bits_per_lane * 3))); + pr_debug("%s: lane mapping reg = 0x%x\n", __func__, lane_map); + writel_relaxed(lane_map, + ctrl_io->base + DP_LOGICAL2PHYSCIAL_LANE_MAPPING); +} + +void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io) +{ + writel_relaxed(0x3d, phy_io->base + DP_PHY_PD_CTL); + writel_relaxed(0x03, phy_io->base + DP_PHY_AUX_CFG1); + writel_relaxed(0x00, phy_io->base + DP_PHY_AUX_CFG3); + writel_relaxed(0x0a, phy_io->base + DP_PHY_AUX_CFG4); + writel_relaxed(0x26, phy_io->base + DP_PHY_AUX_CFG5); + writel_relaxed(0x0a, phy_io->base + DP_PHY_AUX_CFG6); + writel_relaxed(0x03, phy_io->base + DP_PHY_AUX_CFG7); + writel_relaxed(0xbb, phy_io->base + DP_PHY_AUX_CFG8); + writel_relaxed(0x03, phy_io->base + DP_PHY_AUX_CFG9); + writel_relaxed(0x1f, phy_io->base + DP_PHY_AUX_INTERRUPT_MASK); +} + +int mdss_dp_irq_setup(struct mdss_dp_drv_pdata *dp_drv) +{ + int ret = 0; + + mdss_dp_hw.irq_info = mdss_intr_line(); + if (mdss_dp_hw.irq_info == NULL) { + pr_err("Failed to get mdss irq information\n"); + return -ENODEV; + } + + mdss_dp_hw.ptr = (void *)(dp_drv); + + ret = dp_drv->mdss_util->register_irq(&mdss_dp_hw); + if (ret) + pr_err("mdss_register_irq failed.\n"); + + return ret; +} + +void mdss_dp_irq_enable(struct mdss_dp_drv_pdata *dp_drv) +{ + unsigned long flags; + + spin_lock_irqsave(&dp_drv->lock, flags); + writel_relaxed(dp_drv->mask1, dp_drv->base + DP_INTR_STATUS); + writel_relaxed(dp_drv->mask2, dp_drv->base + DP_INTR_STATUS2); + spin_unlock_irqrestore(&dp_drv->lock, flags); + + dp_drv->mdss_util->enable_irq(&mdss_dp_hw); +} + +void mdss_dp_irq_disable(struct mdss_dp_drv_pdata *dp_drv) +{ + unsigned long flags; + + spin_lock_irqsave(&dp_drv->lock, flags); + writel_relaxed(0x00, dp_drv->base + DP_INTR_STATUS); + writel_relaxed(0x00, dp_drv->base + DP_INTR_STATUS2); + spin_unlock_irqrestore(&dp_drv->lock, flags); + + dp_drv->mdss_util->disable_irq(&mdss_dp_hw); +} + +static void mdss_dp_initialize_s_port(enum dp_port_cap *s_port, int port) +{ + switch (port) { + case 0: + *s_port = PORT_NONE; + break; + case 1: + *s_port = PORT_UFP_D; + break; + case 2: + *s_port = PORT_DFP_D; + break; + case 3: + *s_port = PORT_D_UFP_D; + break; + default: + *s_port = PORT_NONE; + } +} + +void mdss_dp_usbpd_ext_capabilities(struct usbpd_dp_capabilities *dp_cap) +{ + u32 buf = dp_cap->response; + int port = buf & 0x3; + + dp_cap->receptacle_state = + (buf & BIT(6)) ? true : false; + + dp_cap->dlink_pin_config = + (buf >> 8) & 0xff; + + dp_cap->ulink_pin_config = + (buf >> 16) & 0xff; + + mdss_dp_initialize_s_port(&dp_cap->s_port, port); +} + +void mdss_dp_usbpd_ext_dp_status(struct usbpd_dp_status *dp_status) +{ + u32 buf = dp_status->response; + int port = buf & 0x3; + + dp_status->low_pow_st = + (buf & BIT(2)) ? true : false; + + dp_status->adaptor_dp_en = + (buf & BIT(3)) ? true : false; + + dp_status->multi_func = + (buf & BIT(4)) ? true : false; + + dp_status->switch_to_usb_config = + (buf & BIT(5)) ? true : false; + + dp_status->exit_dp_mode = + (buf & BIT(6)) ? true : false; + + dp_status->hpd_high = + (buf & BIT(7)) ? true : false; + + dp_status->hpd_irq = + (buf & BIT(8)) ? true : false; + + mdss_dp_initialize_s_port(&dp_status->c_port, port); +} + +u32 mdss_dp_usbpd_gen_config_pkt(struct mdss_dp_drv_pdata *dp) +{ + u32 config = 0; + + config |= (dp->alt_mode.dp_cap.dlink_pin_config << 8); + config |= (0x1 << 2); /* configure for DPv1.3 */ + config |= 0x2; /* Configuring for UFP_D */ + + pr_debug("DP config = 0x%x\n", config); + return config; +} diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h new file mode 100644 index 000000000000..8ef00dd7248e --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dp_util.h @@ -0,0 +1,127 @@ +/* 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. + */ + +#ifndef __DP_UTIL_H__ +#define __DP_UTIL_H__ + +#include "mdss_dp.h" + +/* DP_TX Registers */ +#define DP_HW_VERSION (0x00000000) +#define DP_SW_RESET (0x00000010) +#define DP_PHY_CTRL (0x00000014) +#define DP_CLK_CTRL (0x00000018) +#define DP_CLK_ACTIVE (0x0000001C) +#define DP_INTR_STATUS (0x00000020) +#define DP_INTR_STATUS2 (0x00000024) +#define DP_INTR_STATUS3 (0x00000028) + +#define DP_DP_HPD_CTRL (0x00000200) +#define DP_DP_HPD_INT_STATUS (0x00000204) +#define DP_DP_HPD_INT_ACK (0x00000208) +#define DP_DP_HPD_INT_MASK (0x0000020C) +#define DP_DP_HPD_REFTIMER (0x00000218) +#define DP_DP_HPD_EVENT_TIME_0 (0x0000021C) +#define DP_DP_HPD_EVENT_TIME_1 (0x00000220) +#define DP_AUX_CTRL (0x00000230) +#define DP_AUX_DATA (0x00000234) +#define DP_AUX_TRANS_CTRL (0x00000238) +#define DP_AUX_STATUS (0x00000244) + +#define DP_INTERRUPT_TRANS_NUM (0x000002A0) + +#define DP_MAINLINK_CTRL (0x00000400) +#define DP_STATE_CTRL (0x00000404) +#define DP_CONFIGURATION_CTRL (0x00000408) +#define DP_SOFTWARE_MVID (0x00000410) +#define DP_SOFTWARE_NVID (0x00000418) +#define DP_TOTAL_HOR_VER (0x0000041C) +#define DP_START_HOR_VER_FROM_SYNC (0x00000420) +#define DP_HSYNC_VSYNC_WIDTH_POLARITY (0x00000424) +#define DP_ACTIVE_HOR_VER (0x00000428) +#define DP_MISC1_MISC0 (0x0000042C) +#define DP_VALID_BOUNDARY (0x00000430) +#define DP_VALID_BOUNDARY_2 (0x00000434) +#define DP_LOGICAL2PHYSCIAL_LANE_MAPPING (0x00000438) + +#define DP_MAINLINK_READY (0x00000440) +#define DP_TU (0x0000044C) + +/*DP PHY Register offsets */ +#define DP_PHY_REVISION_ID0 (0x00000000) +#define DP_PHY_REVISION_ID1 (0x00000004) +#define DP_PHY_REVISION_ID2 (0x00000008) +#define DP_PHY_REVISION_ID3 (0x0000000C) + +#define DP_PHY_CFG (0x00000010) +#define DP_PHY_PD_CTL (0x00000014) +#define DP_PHY_MODE (0x00000018) + +#define DP_PHY_AUX_CFG0 (0x0000001C) +#define DP_PHY_AUX_CFG1 (0x00000020) +#define DP_PHY_AUX_CFG2 (0x00000024) +#define DP_PHY_AUX_CFG3 (0x00000028) +#define DP_PHY_AUX_CFG4 (0x0000002C) +#define DP_PHY_AUX_CFG5 (0x00000030) +#define DP_PHY_AUX_CFG6 (0x00000034) +#define DP_PHY_AUX_CFG7 (0x00000038) +#define DP_PHY_AUX_CFG8 (0x0000003C) +#define DP_PHY_AUX_CFG9 (0x00000040) +#define DP_PHY_AUX_INTERRUPT_MASK (0x00000044) +#define DP_PHY_AUX_INTERRUPT_CLEAR (0x00000048) + +#define QSERDES_TX0_OFFSET 0x0400 +#define QSERDES_TX1_OFFSET 0x0800 + +#define TXn_TX_EMP_POST1_LVL 0x000C +#define TXn_TX_DRV_LVL 0x001C + +#define TCSR_USB3_DP_PHYMODE 0x48 + +struct lane_mapping { + char lane0; + char lane1; + char lane2; + char lane3; +}; + +void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data); +u32 mdss_dp_get_ctrl_hw_version(struct dss_io_data *ctrl_io); +u32 mdss_dp_get_phy_hw_version(struct dss_io_data *phy_io); +void mdss_dp_aux_reset(struct dss_io_data *ctrl_io); +void mdss_dp_mainlink_reset(struct dss_io_data *ctrl_io); +void mdss_dp_phy_reset(struct dss_io_data *ctrl_io); +void mdss_dp_switch_usb3_phy_to_dp_mode(struct dss_io_data *tcsr_reg_io); +void mdss_dp_assert_phy_reset(struct dss_io_data *ctrl_io, bool assert); +void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io); +void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io); +void mdss_dp_hpd_configure(struct dss_io_data *ctrl_io, bool enable); +void mdss_dp_aux_ctrl(struct dss_io_data *ctrl_io, bool enable); +void mdss_dp_mainlink_ctrl(struct dss_io_data *ctrl_io, bool enable); +void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, + struct lane_mapping l_map); +int mdss_dp_mainlink_ready(struct mdss_dp_drv_pdata *dp, u32 which); +void mdss_dp_timing_cfg(struct dss_io_data *ctrl_io, + struct mdss_panel_info *pinfo); +void mdss_dp_configuration_ctrl(struct dss_io_data *ctrl_io, u32 data); +void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data); +int mdss_dp_irq_setup(struct mdss_dp_drv_pdata *dp_drv); +void mdss_dp_irq_enable(struct mdss_dp_drv_pdata *dp_drv); +void mdss_dp_irq_disable(struct mdss_dp_drv_pdata *dp_drv); +void mdss_dp_sw_mvid_nvid(struct dss_io_data *ctrl_io); +void mdss_dp_usbpd_ext_capabilities(struct usbpd_dp_capabilities *dp_cap); +void mdss_dp_usbpd_ext_dp_status(struct usbpd_dp_status *dp_status); +u32 mdss_dp_usbpd_gen_config_pkt(struct mdss_dp_drv_pdata *dp); +void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, + struct lane_mapping l_map); + +#endif /* __DP_UTIL_H__ */ diff --git a/drivers/video/fbdev/msm/mdss_edp.c b/drivers/video/fbdev/msm/mdss_edp.c deleted file mode 100644 index add757c34e50..000000000000 --- a/drivers/video/fbdev/msm/mdss_edp.c +++ /dev/null @@ -1,1273 +0,0 @@ -/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/time.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include <linux/of_platform.h> -#include <linux/of_gpio.h> -#include <linux/gpio.h> -#include <linux/err.h> -#include <linux/regulator/consumer.h> -#include <linux/qpnp/pwm.h> -#include <linux/clk.h> -#include <linux/spinlock_types.h> -#include <linux/kthread.h> -#include <mach/hardware.h> -#include <mach/dma.h> - -#include "mdss.h" -#include "mdss_edp.h" -#include "mdss_debug.h" - -#define RGB_COMPONENTS 3 -#define VDDA_MIN_UV 1800000 /* uV units */ -#define VDDA_MAX_UV 1800000 /* uV units */ -#define VDDA_UA_ON_LOAD 100000 /* uA units */ -#define VDDA_UA_OFF_LOAD 100 /* uA units */ - -static int mdss_edp_regulator_on(struct mdss_edp_drv_pdata *edp_drv); -/* - * Init regulator needed for edp, 8974_l12 - */ -static int mdss_edp_regulator_init(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret; - - edp_drv->vdda_vreg = devm_regulator_get(&(edp_drv->pdev->dev), "vdda"); - if (IS_ERR(edp_drv->vdda_vreg)) { - pr_err("%s: Could not get 8941_l12, ret = %ld\n", __func__, - PTR_ERR(edp_drv->vdda_vreg)); - return -ENODEV; - } - - ret = regulator_set_voltage(edp_drv->vdda_vreg, - VDDA_MIN_UV, VDDA_MAX_UV); - if (ret) { - pr_err("%s: vdda_vreg set_voltage failed, ret=%d\n", __func__, - ret); - return -EINVAL; - } - - ret = mdss_edp_regulator_on(edp_drv); - if (ret) - return ret; - - return 0; -} - -/* - * Set uA and enable vdda - */ -static int mdss_edp_regulator_on(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret; - - ret = regulator_set_optimum_mode(edp_drv->vdda_vreg, VDDA_UA_ON_LOAD); - if (ret < 0) { - pr_err("%s: vdda_vreg set regulator mode failed.\n", __func__); - return ret; - } - - ret = regulator_enable(edp_drv->vdda_vreg); - if (ret) { - pr_err("%s: Failed to enable vdda_vreg regulator.\n", __func__); - return ret; - } - - return 0; -} - -/* - * Disable vdda and set uA - */ -static int mdss_edp_regulator_off(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret; - - ret = regulator_disable(edp_drv->vdda_vreg); - if (ret) { - pr_err("%s: Failed to disable vdda_vreg regulator.\n", - __func__); - return ret; - } - - ret = regulator_set_optimum_mode(edp_drv->vdda_vreg, VDDA_UA_OFF_LOAD); - if (ret < 0) { - pr_err("%s: vdda_vreg set regulator mode failed.\n", - __func__); - return ret; - } - - return 0; -} - -/* - * Enables the gpio that supply power to the panel and enable the backlight - */ -static int mdss_edp_gpio_panel_en(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret = 0; - - edp_drv->gpio_panel_en = of_get_named_gpio(edp_drv->pdev->dev.of_node, - "gpio-panel-en", 0); - if (!gpio_is_valid(edp_drv->gpio_panel_en)) { - pr_err("%s: gpio_panel_en=%d not specified\n", __func__, - edp_drv->gpio_panel_en); - goto gpio_err; - } - - ret = gpio_request(edp_drv->gpio_panel_en, "disp_enable"); - if (ret) { - pr_err("%s: Request reset gpio_panel_en failed, ret=%d\n", - __func__, ret); - return ret; - } - - ret = gpio_direction_output(edp_drv->gpio_panel_en, 1); - if (ret) { - pr_err("%s: Set direction for gpio_panel_en failed, ret=%d\n", - __func__, ret); - goto gpio_free; - } - - return 0; - -gpio_free: - gpio_free(edp_drv->gpio_panel_en); -gpio_err: - return -ENODEV; -} - -static int mdss_edp_gpio_lvl_en(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret = 0; - - edp_drv->gpio_lvl_en = of_get_named_gpio(edp_drv->pdev->dev.of_node, - "gpio-lvl-en", 0); - if (!gpio_is_valid(edp_drv->gpio_lvl_en)) { - pr_err("%s: gpio_lvl_en=%d not specified\n", __func__, - edp_drv->gpio_lvl_en); - ret = -ENODEV; - goto gpio_err; - } - - ret = gpio_request(edp_drv->gpio_lvl_en, "lvl_enable"); - if (ret) { - pr_err("%s: Request reset gpio_lvl_en failed, ret=%d\n", - __func__, ret); - return ret; - } - - ret = gpio_direction_output(edp_drv->gpio_lvl_en, 1); - if (ret) { - pr_err("%s: Set direction for gpio_lvl_en failed, ret=%d\n", - __func__, ret); - goto gpio_free; - } - - return ret; - -gpio_free: - gpio_free(edp_drv->gpio_lvl_en); -gpio_err: - return ret; -} - -static int mdss_edp_pwm_config(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret = 0; - - ret = of_property_read_u32(edp_drv->pdev->dev.of_node, - "qcom,panel-pwm-period", &edp_drv->pwm_period); - if (ret) { - pr_warn("%s: panel pwm period is not specified, %d", __func__, - edp_drv->pwm_period); - edp_drv->pwm_period = -EINVAL; - } - - ret = of_property_read_u32(edp_drv->pdev->dev.of_node, - "qcom,panel-lpg-channel", &edp_drv->lpg_channel); - if (ret) { - pr_warn("%s: panel lpg channel is not specified, %d", __func__, - edp_drv->lpg_channel); - edp_drv->lpg_channel = -EINVAL; - } - - if (edp_drv->pwm_period != -EINVAL && - edp_drv->lpg_channel != -EINVAL) { - edp_drv->bl_pwm = pwm_request(edp_drv->lpg_channel, - "lcd-backlight"); - if (edp_drv->bl_pwm == NULL || IS_ERR(edp_drv->bl_pwm)) { - pr_err("%s: pwm request failed", __func__); - edp_drv->bl_pwm = NULL; - return -EIO; - } - } else { - edp_drv->bl_pwm = NULL; - } - - return 0; -} - -void mdss_edp_set_backlight(struct mdss_panel_data *pdata, u32 bl_level) -{ - int ret = 0; - struct mdss_edp_drv_pdata *edp_drv = NULL; - int bl_max; - int period_ns; - - edp_drv = container_of(pdata, struct mdss_edp_drv_pdata, panel_data); - if (!edp_drv) { - pr_err("%s: Invalid input data\n", __func__); - return; - } - - if (edp_drv->bl_pwm != NULL) { - bl_max = edp_drv->panel_data.panel_info.bl_max; - if (bl_level > bl_max) - bl_level = bl_max; - - /* In order to avoid overflow, use the microsecond version - * of pwm_config if the pwm_period is greater than or equal - * to 1 second. - */ - if (edp_drv->pwm_period >= USEC_PER_SEC) { - ret = pwm_config_us(edp_drv->bl_pwm, - bl_level * edp_drv->pwm_period / bl_max, - edp_drv->pwm_period); - if (ret) { - pr_err("%s: pwm_config_us() failed err=%d.\n", - __func__, ret); - return; - } - } else { - period_ns = edp_drv->pwm_period * NSEC_PER_USEC; - ret = pwm_config(edp_drv->bl_pwm, - bl_level * period_ns / bl_max, - period_ns); - if (ret) { - pr_err("%s: pwm_config() failed err=%d.\n", - __func__, ret); - return; - } - } - - if (edp_drv->is_pwm_enabled) { - pwm_disable(edp_drv->bl_pwm); - edp_drv->is_pwm_enabled = 0; - } - - ret = pwm_enable(edp_drv->bl_pwm); - if (ret) { - pr_err("%s: pwm_enable() failed err=%d\n", __func__, - ret); - return; - } - edp_drv->is_pwm_enabled = 1; - } -} - -int mdss_edp_mainlink_ready(struct mdss_edp_drv_pdata *ep, u32 which) -{ - u32 data; - int cnt = 10; - - while (--cnt) { - data = edp_read(ep->base + 0x84); /* EDP_MAINLINK_READY */ - if (data & which) { - pr_debug("%s: which=%x ready\n", __func__, which); - return 1; - } - usleep_range(1000, 1000); - } - pr_err("%s: which=%x NOT ready\n", __func__, which); - - return 0; -} - -void mdss_edp_mainlink_reset(struct mdss_edp_drv_pdata *ep) -{ - edp_write(ep->base + 0x04, 0x02); /* EDP_MAINLINK_CTRL */ - usleep_range(1000, 1000); - edp_write(ep->base + 0x04, 0); /* EDP_MAINLINK_CTRL */ -} - -void mdss_edp_mainlink_ctrl(struct mdss_edp_drv_pdata *ep, int enable) -{ - u32 data; - - data = edp_read(ep->base + 0x04); - data &= ~BIT(0); - - if (enable) - data |= 0x1; - - edp_write(ep->base + 0x04, data); -} - -void mdss_edp_state_ctrl(struct mdss_edp_drv_pdata *ep, u32 state) -{ - edp_write(ep->base + EDP_STATE_CTRL, state); -} - -void mdss_edp_aux_reset(struct mdss_edp_drv_pdata *ep) -{ - /* reset AUX */ - edp_write(ep->base + 0x300, BIT(1)); /* EDP_AUX_CTRL */ - usleep_range(1000, 1000); - edp_write(ep->base + 0x300, 0); /* EDP_AUX_CTRL */ -} - -void mdss_edp_aux_ctrl(struct mdss_edp_drv_pdata *ep, int enable) -{ - u32 data; - - data = edp_read(ep->base + 0x300); - if (enable) - data |= 0x01; - else - data |= ~0x01; - edp_write(ep->base + 0x300, data); /* EDP_AUX_CTRL */ -} - -void mdss_edp_phy_pll_reset(struct mdss_edp_drv_pdata *ep) -{ - /* EDP_PHY_CTRL */ - edp_write(ep->base + 0x74, 0x005); /* bit 0, 2 */ - usleep_range(1000, 1000); - edp_write(ep->base + 0x74, 0x000); /* EDP_PHY_CTRL */ -} - -int mdss_edp_phy_pll_ready(struct mdss_edp_drv_pdata *ep) -{ - int cnt; - u32 status = 0; - - cnt = 100; - while (--cnt) { - status = edp_read(ep->base + 0x6c0); - if (status & 0x01) - break; - usleep_range(100, 100); - } - - pr_debug("%s: PLL cnt=%d status=%x\n", __func__, cnt, (int)status); - - if (cnt <= 0) { - pr_err("%s: PLL NOT ready\n", __func__); - return 0; - } else - return 1; -} - -int mdss_edp_phy_ready(struct mdss_edp_drv_pdata *ep) -{ - u32 status; - - status = edp_read(ep->base + 0x598); - status &= 0x01; - - return status; -} - -void mdss_edp_phy_power_ctrl(struct mdss_edp_drv_pdata *ep, int enable) -{ - if (enable) { - /* EDP_PHY_EDPPHY_GLB_PD_CTL */ - edp_write(ep->base + 0x52c, 0x3f); - /* EDP_PHY_EDPPHY_GLB_CFG */ - edp_write(ep->base + 0x528, 0x1); - /* EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG */ - edp_write(ep->base + 0x620, 0xf); - } else { - /* EDP_PHY_EDPPHY_GLB_PD_CTL */ - edp_write(ep->base + 0x52c, 0xc0); - } -} - -void mdss_edp_lane_power_ctrl(struct mdss_edp_drv_pdata *ep, int up) -{ - int i, off, max_lane; - u32 data; - - max_lane = ep->lane_cnt; - - if (up) - data = 0; /* power up */ - else - data = 0x7; /* power down */ - - /* EDP_PHY_EDPPHY_LNn_PD_CTL */ - for (i = 0; i < max_lane; i++) { - off = 0x40 * i; - edp_write(ep->base + 0x404 + off , data); - } - - /* power down un used lane */ - data = 0x7; /* power down */ - for (i = max_lane; i < EDP_MAX_LANE; i++) { - off = 0x40 * i; - edp_write(ep->base + 0x404 + off , data); - } -} - -void mdss_edp_clock_synchrous(struct mdss_edp_drv_pdata *ep, int sync) -{ - u32 data; - u32 color; - - /* EDP_MISC1_MISC0 */ - data = edp_read(ep->base + 0x02c); - - if (sync) - data |= 0x01; - else - data &= ~0x01; - - /* only legacy rgb mode supported */ - color = 0; /* 6 bits */ - if (ep->edid.color_depth == 8) - color = 0x01; - else if (ep->edid.color_depth == 10) - color = 0x02; - else if (ep->edid.color_depth == 12) - color = 0x03; - else if (ep->edid.color_depth == 16) - color = 0x04; - - color <<= 5; /* bit 5 to bit 7 */ - - data |= color; - /* EDP_MISC1_MISC0 */ - edp_write(ep->base + 0x2c, data); -} - -/* voltage mode and pre emphasis cfg */ -void mdss_edp_phy_vm_pe_init(struct mdss_edp_drv_pdata *ep) -{ - /* EDP_PHY_EDPPHY_GLB_VM_CFG0 */ - edp_write(ep->base + 0x510, 0x3); /* vm only */ - /* EDP_PHY_EDPPHY_GLB_VM_CFG1 */ - edp_write(ep->base + 0x514, 0x64); - /* EDP_PHY_EDPPHY_GLB_MISC9 */ - edp_write(ep->base + 0x518, 0x6c); -} - -void mdss_edp_config_ctrl(struct mdss_edp_drv_pdata *ep) -{ - struct dpcd_cap *cap; - struct display_timing_desc *dp; - u32 data = 0; - - dp = &ep->edid.timing[0]; - - cap = &ep->dpcd; - - data = ep->lane_cnt - 1; - data <<= 4; - - if (cap->enhanced_frame) - data |= 0x40; - - if (ep->edid.color_depth == 8) { - /* 0 == 6 bits, 1 == 8 bits */ - data |= 0x100; /* bit 8 */ - } - - if (!dp->interlaced) /* progressive */ - data |= 0x04; - - data |= 0x03; /* sycn clock & static Mvid */ - - edp_write(ep->base + 0xc, data); /* EDP_CONFIGURATION_CTRL */ -} - -static void mdss_edp_sw_mvid_nvid(struct mdss_edp_drv_pdata *ep) -{ - edp_write(ep->base + 0x14, 0x13b); /* EDP_SOFTWARE_MVID */ - edp_write(ep->base + 0x18, 0x266); /* EDP_SOFTWARE_NVID */ -} - -static void mdss_edp_timing_cfg(struct mdss_edp_drv_pdata *ep) -{ - struct mdss_panel_info *pinfo; - u32 total_ver, total_hor; - u32 data; - - pinfo = &ep->panel_data.panel_info; - - pr_debug("%s: width=%d hporch= %d %d %d\n", __func__, - pinfo->xres, pinfo->lcdc.h_back_porch, - pinfo->lcdc.h_front_porch, pinfo->lcdc.h_pulse_width); - - pr_debug("%s: height=%d vporch= %d %d %d\n", __func__, - pinfo->yres, pinfo->lcdc.v_back_porch, - pinfo->lcdc.v_front_porch, pinfo->lcdc.v_pulse_width); - - total_hor = pinfo->xres + pinfo->lcdc.h_back_porch + - pinfo->lcdc.h_front_porch + pinfo->lcdc.h_pulse_width; - - total_ver = pinfo->yres + pinfo->lcdc.v_back_porch + - pinfo->lcdc.v_front_porch + pinfo->lcdc.v_pulse_width; - - data = total_ver; - data <<= 16; - data |= total_hor; - edp_write(ep->base + 0x1c, data); /* EDP_TOTAL_HOR_VER */ - - data = (pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width); - data <<= 16; - data |= (pinfo->lcdc.h_back_porch + pinfo->lcdc.h_pulse_width); - edp_write(ep->base + 0x20, data); /* EDP_START_HOR_VER_FROM_SYNC */ - - data = pinfo->lcdc.v_pulse_width; - data <<= 16; - data |= pinfo->lcdc.h_pulse_width; - edp_write(ep->base + 0x24, data); /* EDP_HSYNC_VSYNC_WIDTH_POLARITY */ - - data = pinfo->yres; - data <<= 16; - data |= pinfo->xres; - edp_write(ep->base + 0x28, data); /* EDP_ACTIVE_HOR_VER */ -} - -int mdss_edp_wait4train(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret = 0; - - if (edp_drv->cont_splash) - return ret; - - ret = wait_for_completion_timeout(&edp_drv->video_comp, 30); - if (ret <= 0) { - pr_err("%s: Link Train timedout\n", __func__); - ret = -EINVAL; - } else { - ret = 0; - } - - pr_debug("%s:\n", __func__); - - return ret; -} - -static void mdss_edp_irq_enable(struct mdss_edp_drv_pdata *edp_drv); -static void mdss_edp_irq_disable(struct mdss_edp_drv_pdata *edp_drv); - -int mdss_edp_on(struct mdss_panel_data *pdata) -{ - struct mdss_edp_drv_pdata *edp_drv = NULL; - int ret = 0; - - if (!pdata) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - edp_drv = container_of(pdata, struct mdss_edp_drv_pdata, - panel_data); - - pr_debug("%s:+, cont_splash=%d\n", __func__, edp_drv->cont_splash); - - if (!edp_drv->cont_splash) { /* vote for clocks */ - mdss_edp_phy_pll_reset(edp_drv); - mdss_edp_aux_reset(edp_drv); - mdss_edp_mainlink_reset(edp_drv); - mdss_edp_aux_ctrl(edp_drv, 1); - - ret = mdss_edp_prepare_clocks(edp_drv); - if (ret) - return ret; - - mdss_edp_phy_power_ctrl(edp_drv, 1); - - ret = mdss_edp_clk_enable(edp_drv); - if (ret) { - mdss_edp_unprepare_clocks(edp_drv); - return ret; - } - - mdss_edp_phy_pll_ready(edp_drv); - - mdss_edp_lane_power_ctrl(edp_drv, 1); - - mdss_edp_clock_synchrous(edp_drv, 1); - mdss_edp_phy_vm_pe_init(edp_drv); - mdss_edp_config_ctrl(edp_drv); - mdss_edp_sw_mvid_nvid(edp_drv); - mdss_edp_timing_cfg(edp_drv); - - gpio_set_value(edp_drv->gpio_panel_en, 1); - if (gpio_is_valid(edp_drv->gpio_lvl_en)) - gpio_set_value(edp_drv->gpio_lvl_en, 1); - - reinit_completion(&edp_drv->idle_comp); - mdss_edp_mainlink_ctrl(edp_drv, 1); - } else { - mdss_edp_aux_ctrl(edp_drv, 1); - } - - mdss_edp_irq_enable(edp_drv); - - if (edp_drv->delay_link_train) { - mdss_edp_link_train(edp_drv); - edp_drv->delay_link_train = 0; - } - - mdss_edp_wait4train(edp_drv); - - edp_drv->cont_splash = 0; - - pr_debug("%s:-\n", __func__); - return ret; -} - -int mdss_edp_off(struct mdss_panel_data *pdata) -{ - struct mdss_edp_drv_pdata *edp_drv = NULL; - int ret = 0; - - edp_drv = container_of(pdata, struct mdss_edp_drv_pdata, - panel_data); - if (!edp_drv) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - pr_debug("%s:+, cont_splash=%d\n", __func__, edp_drv->cont_splash); - - /* wait until link training is completed */ - mutex_lock(&edp_drv->train_mutex); - - reinit_completion(&edp_drv->idle_comp); - mdss_edp_state_ctrl(edp_drv, ST_PUSH_IDLE); - - ret = wait_for_completion_timeout(&edp_drv->idle_comp, - msecs_to_jiffies(100)); - if (ret == 0) - pr_err("%s: idle pattern timedout\n", __func__); - - mdss_edp_state_ctrl(edp_drv, 0); - - mdss_edp_sink_power_state(edp_drv, SINK_POWER_OFF); - - mdss_edp_irq_disable(edp_drv); - - gpio_set_value(edp_drv->gpio_panel_en, 0); - if (gpio_is_valid(edp_drv->gpio_lvl_en)) - gpio_set_value(edp_drv->gpio_lvl_en, 0); - if (edp_drv->bl_pwm != NULL) - pwm_disable(edp_drv->bl_pwm); - edp_drv->is_pwm_enabled = 0; - - mdss_edp_mainlink_reset(edp_drv); - mdss_edp_mainlink_ctrl(edp_drv, 0); - - mdss_edp_lane_power_ctrl(edp_drv, 0); - mdss_edp_phy_power_ctrl(edp_drv, 0); - - mdss_edp_clk_disable(edp_drv); - mdss_edp_unprepare_clocks(edp_drv); - - mdss_edp_aux_ctrl(edp_drv, 0); - - pr_debug("%s-: state_ctrl=%x\n", __func__, - edp_read(edp_drv->base + 0x8)); - - mutex_unlock(&edp_drv->train_mutex); - return 0; -} - -static int mdss_edp_event_handler(struct mdss_panel_data *pdata, - int event, void *arg) -{ - int rc = 0; - - pr_debug("%s: event=%d\n", __func__, event); - switch (event) { - case MDSS_EVENT_UNBLANK: - rc = mdss_edp_on(pdata); - break; - case MDSS_EVENT_PANEL_OFF: - rc = mdss_edp_off(pdata); - break; - } - return rc; -} - -/* - * Converts from EDID struct to mdss_panel_info - */ -static void mdss_edp_edid2pinfo(struct mdss_edp_drv_pdata *edp_drv) -{ - struct display_timing_desc *dp; - struct mdss_panel_info *pinfo; - - dp = &edp_drv->edid.timing[0]; - pinfo = &edp_drv->panel_data.panel_info; - - pinfo->clk_rate = dp->pclk; - pr_debug("%s: pclk=%d\n", __func__, pinfo->clk_rate); - - pinfo->xres = dp->h_addressable + dp->h_border * 2; - pinfo->yres = dp->v_addressable + dp->v_border * 2; - - pr_debug("%s: x=%d y=%d\n", __func__, pinfo->xres, pinfo->yres); - - pinfo->lcdc.h_back_porch = dp->h_blank - dp->h_fporch \ - - dp->h_sync_pulse; - pinfo->lcdc.h_front_porch = dp->h_fporch; - pinfo->lcdc.h_pulse_width = dp->h_sync_pulse; - - pr_debug("%s: hporch= %d %d %d\n", __func__, - pinfo->lcdc.h_back_porch, pinfo->lcdc.h_front_porch, - pinfo->lcdc.h_pulse_width); - - pinfo->lcdc.v_back_porch = dp->v_blank - dp->v_fporch \ - - dp->v_sync_pulse; - pinfo->lcdc.v_front_porch = dp->v_fporch; - pinfo->lcdc.v_pulse_width = dp->v_sync_pulse; - - pr_debug("%s: vporch= %d %d %d\n", __func__, - pinfo->lcdc.v_back_porch, pinfo->lcdc.v_front_porch, - pinfo->lcdc.v_pulse_width); - - pinfo->type = EDP_PANEL; - pinfo->pdest = DISPLAY_1; - pinfo->wait_cycle = 0; - pinfo->bpp = edp_drv->edid.color_depth * RGB_COMPONENTS; - pinfo->fb_num = 2; - - pinfo->lcdc.border_clr = 0; /* black */ - pinfo->lcdc.underflow_clr = 0xff; /* blue */ - pinfo->lcdc.hsync_skew = 0; -} - -static int mdss_edp_remove(struct platform_device *pdev) -{ - struct mdss_edp_drv_pdata *edp_drv = NULL; - - edp_drv = platform_get_drvdata(pdev); - - gpio_free(edp_drv->gpio_panel_en); - if (gpio_is_valid(edp_drv->gpio_lvl_en)) - gpio_free(edp_drv->gpio_lvl_en); - mdss_edp_regulator_off(edp_drv); - iounmap(edp_drv->base); - iounmap(edp_drv->mmss_cc_base); - edp_drv->base = NULL; - - return 0; -} - -static int mdss_edp_device_register(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret; - u32 tmp; - - mdss_edp_edid2pinfo(edp_drv); - edp_drv->panel_data.panel_info.bl_min = 1; - edp_drv->panel_data.panel_info.bl_max = 255; - ret = of_property_read_u32(edp_drv->pdev->dev.of_node, - "qcom,mdss-brightness-max-level", &tmp); - edp_drv->panel_data.panel_info.brightness_max = - (!ret ? tmp : MDSS_MAX_BL_BRIGHTNESS); - - edp_drv->panel_data.panel_info.edp.frame_rate = - DEFAULT_FRAME_RATE;/* 60 fps */ - - edp_drv->panel_data.event_handler = mdss_edp_event_handler; - edp_drv->panel_data.set_backlight = mdss_edp_set_backlight; - - edp_drv->panel_data.panel_info.cont_splash_enabled = - edp_drv->cont_splash; - - ret = mdss_register_panel(edp_drv->pdev, &edp_drv->panel_data); - if (ret) { - dev_err(&(edp_drv->pdev->dev), "unable to register eDP\n"); - return ret; - } - - pr_info("%s: eDP initialized\n", __func__); - - return 0; -} - -/* - * Retrieve edp base address - */ -static int mdss_edp_get_base_address(struct mdss_edp_drv_pdata *edp_drv) -{ - struct resource *res; - - res = platform_get_resource_byname(edp_drv->pdev, IORESOURCE_MEM, - "edp_base"); - if (!res) { - pr_err("%s: Unable to get the MDSS EDP resources", __func__); - return -ENOMEM; - } - - edp_drv->base_size = resource_size(res); - edp_drv->base = ioremap(res->start, resource_size(res)); - if (!edp_drv->base) { - pr_err("%s: Unable to remap EDP resources", __func__); - return -ENOMEM; - } - - pr_debug("%s: drv=%x base=%x size=%x\n", __func__, - (int)edp_drv, (int)edp_drv->base, edp_drv->base_size); - - mdss_debug_register_base("edp", - edp_drv->base, edp_drv->base_size, NULL); - - return 0; -} - -static int mdss_edp_get_mmss_cc_base_address(struct mdss_edp_drv_pdata - *edp_drv) -{ - struct resource *res; - - res = platform_get_resource_byname(edp_drv->pdev, IORESOURCE_MEM, - "mmss_cc_base"); - if (!res) { - pr_err("%s: Unable to get the MMSS_CC resources", __func__); - return -ENOMEM; - } - - edp_drv->mmss_cc_base = ioremap(res->start, resource_size(res)); - if (!edp_drv->mmss_cc_base) { - pr_err("%s: Unable to remap MMSS_CC resources", __func__); - return -ENOMEM; - } - - return 0; -} - -static void mdss_edp_video_ready(struct mdss_edp_drv_pdata *ep) -{ - pr_debug("%s: edp_video_ready\n", __func__); - complete(&ep->video_comp); -} - -static void mdss_edp_idle_patterns_sent(struct mdss_edp_drv_pdata *ep) -{ - pr_debug("%s: idle_patterns_sent\n", __func__); - complete(&ep->idle_comp); -} - -static void mdss_edp_do_link_train(struct mdss_edp_drv_pdata *ep) -{ - if (ep->cont_splash) - return; - - if (!ep->inited) { - ep->delay_link_train++; - return; - } - - mdss_edp_link_train(ep); -} - -static int edp_event_thread(void *data) -{ - struct mdss_edp_drv_pdata *ep; - unsigned long flag; - u32 todo = 0; - - ep = (struct mdss_edp_drv_pdata *)data; - - while (1) { - wait_event(ep->event_q, (ep->event_pndx != ep->event_gndx)); - spin_lock_irqsave(&ep->event_lock, flag); - if (ep->event_pndx == ep->event_gndx) { - spin_unlock_irqrestore(&ep->event_lock, flag); - break; - } - todo = ep->event_todo_list[ep->event_gndx]; - ep->event_todo_list[ep->event_gndx++] = 0; - ep->event_gndx %= HPD_EVENT_MAX; - spin_unlock_irqrestore(&ep->event_lock, flag); - - pr_debug("%s: todo=%x\n", __func__, todo); - - if (todo == 0) - continue; - - if (todo & EV_EDID_READ) - mdss_edp_edid_read(ep, 0); - - if (todo & EV_DPCD_CAP_READ) - mdss_edp_dpcd_cap_read(ep); - - if (todo & EV_DPCD_STATUS_READ) - mdss_edp_dpcd_status_read(ep); - - if (todo & EV_LINK_TRAIN) - mdss_edp_do_link_train(ep); - - if (todo & EV_VIDEO_READY) - mdss_edp_video_ready(ep); - - if (todo & EV_IDLE_PATTERNS_SENT) - mdss_edp_idle_patterns_sent(ep); - } - - return 0; -} - -static void edp_send_events(struct mdss_edp_drv_pdata *ep, u32 events) -{ - spin_lock(&ep->event_lock); - ep->event_todo_list[ep->event_pndx++] = events; - ep->event_pndx %= HPD_EVENT_MAX; - wake_up(&ep->event_q); - spin_unlock(&ep->event_lock); -} - -irqreturn_t edp_isr(int irq, void *ptr) -{ - struct mdss_edp_drv_pdata *ep = (struct mdss_edp_drv_pdata *)ptr; - unsigned char *base = ep->base; - u32 isr1, isr2, mask1, mask2; - u32 ack; - - spin_lock(&ep->lock); - isr1 = edp_read(base + 0x308); - isr2 = edp_read(base + 0x30c); - - mask1 = isr1 & ep->mask1; - mask2 = isr2 & ep->mask2; - - isr1 &= ~mask1; /* remove masks bit */ - isr2 &= ~mask2; - - pr_debug("%s: isr=%x mask=%x isr2=%x mask2=%x\n", - __func__, isr1, mask1, isr2, mask2); - - ack = isr1 & EDP_INTR_STATUS1; - ack <<= 1; /* ack bits */ - ack |= mask1; - edp_write(base + 0x308, ack); - - ack = isr2 & EDP_INTR_STATUS2; - ack <<= 1; /* ack bits */ - ack |= mask2; - edp_write(base + 0x30c, ack); - spin_unlock(&ep->lock); - - if (isr1 & EDP_INTR_HPD) { - isr1 &= ~EDP_INTR_HPD; /* clear */ - edp_send_events(ep, EV_LINK_TRAIN); - } - - if (isr2 & EDP_INTR_READY_FOR_VIDEO) - edp_send_events(ep, EV_VIDEO_READY); - - if (isr2 & EDP_INTR_IDLE_PATTERNs_SENT) - edp_send_events(ep, EV_IDLE_PATTERNS_SENT); - - if (isr1 && ep->aux_cmd_busy) { - /* clear EDP_AUX_TRANS_CTRL */ - edp_write(base + 0x318, 0); - /* read EDP_INTERRUPT_TRANS_NUM */ - ep->aux_trans_num = edp_read(base + 0x310); - - if (ep->aux_cmd_i2c) - edp_aux_i2c_handler(ep, isr1); - else - edp_aux_native_handler(ep, isr1); - } - - return IRQ_HANDLED; -} - -struct mdss_hw mdss_edp_hw = { - .hw_ndx = MDSS_HW_EDP, - .ptr = NULL, - .irq_handler = edp_isr, -}; - -static void mdss_edp_irq_enable(struct mdss_edp_drv_pdata *edp_drv) -{ - unsigned long flags; - - spin_lock_irqsave(&edp_drv->lock, flags); - edp_write(edp_drv->base + 0x308, edp_drv->mask1); - edp_write(edp_drv->base + 0x30c, edp_drv->mask2); - spin_unlock_irqrestore(&edp_drv->lock, flags); - - edp_drv->mdss_util->enable_irq(&mdss_edp_hw); -} - -static void mdss_edp_irq_disable(struct mdss_edp_drv_pdata *edp_drv) -{ - unsigned long flags; - - spin_lock_irqsave(&edp_drv->lock, flags); - edp_write(edp_drv->base + 0x308, 0x0); - edp_write(edp_drv->base + 0x30c, 0x0); - spin_unlock_irqrestore(&edp_drv->lock, flags); - - edp_drv->mdss_util->disable_irq(&mdss_edp_hw); -} - -static int mdss_edp_irq_setup(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret = 0; - - edp_drv->gpio_panel_hpd = of_get_named_gpio_flags( - edp_drv->pdev->dev.of_node, "gpio-panel-hpd", 0, - &edp_drv->hpd_flags); - - if (!gpio_is_valid(edp_drv->gpio_panel_hpd)) { - pr_err("%s gpio_panel_hpd %d is not valid ", __func__, - edp_drv->gpio_panel_hpd); - return -ENODEV; - } - - ret = gpio_request(edp_drv->gpio_panel_hpd, "edp_hpd_irq_gpio"); - if (ret) { - pr_err("%s unable to request gpio_panel_hpd %d", __func__, - edp_drv->gpio_panel_hpd); - return -ENODEV; - } - - ret = gpio_tlmm_config(GPIO_CFG( - edp_drv->gpio_panel_hpd, - 1, - GPIO_CFG_INPUT, - GPIO_CFG_NO_PULL, - GPIO_CFG_2MA), - GPIO_CFG_ENABLE); - if (ret) { - pr_err("%s: unable to config tlmm = %d\n", __func__, - edp_drv->gpio_panel_hpd); - gpio_free(edp_drv->gpio_panel_hpd); - return -ENODEV; - } - - ret = gpio_direction_input(edp_drv->gpio_panel_hpd); - if (ret) { - pr_err("%s unable to set direction for gpio_panel_hpd %d", - __func__, edp_drv->gpio_panel_hpd); - return -ENODEV; - } - - mdss_edp_hw.ptr = (void *)(edp_drv); - - if (edp_drv->mdss_util->register_irq(&mdss_edp_hw)) - pr_err("%s: mdss_register_irq failed.\n", __func__); - - - return 0; -} - - -static void mdss_edp_event_setup(struct mdss_edp_drv_pdata *ep) -{ - init_waitqueue_head(&ep->event_q); - spin_lock_init(&ep->event_lock); - - kthread_run(edp_event_thread, (void *)ep, "mdss_edp_hpd"); -} - -static int mdss_edp_probe(struct platform_device *pdev) -{ - int ret; - struct mdss_edp_drv_pdata *edp_drv; - struct mdss_panel_cfg *pan_cfg = NULL; - - if (!mdss_is_ready()) { - pr_err("%s: MDP not probed yet!\n", __func__); - return -EPROBE_DEFER; - } - - pan_cfg = mdss_panel_intf_type(MDSS_PANEL_INTF_EDP); - if (IS_ERR(pan_cfg)) { - return PTR_ERR(pan_cfg); - } else if (!pan_cfg) { - pr_debug("%s: not configured as prim\n", __func__); - return -ENODEV; - } - - if (!pdev->dev.of_node) { - pr_err("%s: Failed\n", __func__); - return -EPERM; - } - - edp_drv = devm_kzalloc(&pdev->dev, sizeof(*edp_drv), GFP_KERNEL); - if (edp_drv == NULL) { - pr_err("%s: Failed, could not allocate edp_drv", __func__); - return -ENOMEM; - } - - edp_drv->mdss_util = mdss_get_util_intf(); - if (edp_drv->mdss_util == NULL) { - pr_err("Failed to get mdss utility functions\n"); - return -ENODEV; - } - edp_drv->panel_data.panel_info.is_prim_panel = true; - - mdss_edp_hw.irq_info = mdss_intr_line(); - if (mdss_edp_hw.irq_info == NULL) { - pr_err("Failed to get mdss irq information\n"); - return -ENODEV; - } - - edp_drv->pdev = pdev; - edp_drv->pdev->id = 1; - edp_drv->clk_on = 0; - edp_drv->aux_rate = 19200000; - edp_drv->mask1 = EDP_INTR_MASK1; - edp_drv->mask2 = EDP_INTR_MASK2; - mutex_init(&edp_drv->emutex); - spin_lock_init(&edp_drv->lock); - - ret = mdss_edp_get_base_address(edp_drv); - if (ret) - goto probe_err; - - ret = mdss_edp_get_mmss_cc_base_address(edp_drv); - if (ret) - goto edp_base_unmap; - - ret = mdss_edp_regulator_init(edp_drv); - if (ret) - goto mmss_cc_base_unmap; - - ret = mdss_edp_clk_init(edp_drv); - if (ret) - goto edp_clk_deinit; - - ret = mdss_edp_gpio_panel_en(edp_drv); - if (ret) - goto edp_clk_deinit; - - ret = mdss_edp_gpio_lvl_en(edp_drv); - if (ret) - pr_err("%s: No gpio_lvl_en detected\n", __func__); - - ret = mdss_edp_pwm_config(edp_drv); - if (ret) - goto edp_free_gpio_panel_en; - - mdss_edp_irq_setup(edp_drv); - - mdss_edp_aux_init(edp_drv); - - mdss_edp_event_setup(edp_drv); - - edp_drv->cont_splash = edp_drv->mdss_util->panel_intf_status(DISPLAY_1, - MDSS_PANEL_INTF_EDP) ? true : false; - - /* only need aux and ahb clock for aux channel */ - mdss_edp_prepare_aux_clocks(edp_drv); - mdss_edp_aux_clk_enable(edp_drv); - - if (!edp_drv->cont_splash) { - mdss_edp_phy_pll_reset(edp_drv); - mdss_edp_aux_reset(edp_drv); - mdss_edp_mainlink_reset(edp_drv); - mdss_edp_phy_power_ctrl(edp_drv, 1); - mdss_edp_aux_ctrl(edp_drv, 1); - } - - mdss_edp_irq_enable(edp_drv); - - mdss_edp_edid_read(edp_drv, 0); - mdss_edp_dpcd_cap_read(edp_drv); - mdss_edp_fill_link_cfg(edp_drv); - - mdss_edp_irq_disable(edp_drv); - - if (!edp_drv->cont_splash) { - mdss_edp_aux_ctrl(edp_drv, 0); - mdss_edp_phy_power_ctrl(edp_drv, 0); - } - - mdss_edp_aux_clk_disable(edp_drv); - mdss_edp_unprepare_aux_clocks(edp_drv); - - if (edp_drv->cont_splash) { /* vote for clocks */ - mdss_edp_prepare_clocks(edp_drv); - mdss_edp_clk_enable(edp_drv); - } - - mdss_edp_device_register(edp_drv); - - edp_drv->inited = true; - - pr_debug("%s: done\n", __func__); - - return 0; - - -edp_free_gpio_panel_en: - gpio_free(edp_drv->gpio_panel_en); - if (gpio_is_valid(edp_drv->gpio_lvl_en)) - gpio_free(edp_drv->gpio_lvl_en); -edp_clk_deinit: - mdss_edp_clk_deinit(edp_drv); - mdss_edp_regulator_off(edp_drv); -mmss_cc_base_unmap: - iounmap(edp_drv->mmss_cc_base); -edp_base_unmap: - iounmap(edp_drv->base); -probe_err: - return ret; - -} - -static const struct of_device_id msm_mdss_edp_dt_match[] = { - {.compatible = "qcom,mdss-edp"}, - {} -}; -MODULE_DEVICE_TABLE(of, msm_mdss_edp_dt_match); - -static struct platform_driver mdss_edp_driver = { - .probe = mdss_edp_probe, - .remove = mdss_edp_remove, - .shutdown = NULL, - .driver = { - .name = "mdss_edp", - .of_match_table = msm_mdss_edp_dt_match, - }, -}; - -static int __init mdss_edp_init(void) -{ - int ret; - - ret = platform_driver_register(&mdss_edp_driver); - if (ret) { - pr_err("%s driver register failed", __func__); - return ret; - } - - return ret; -} -module_init(mdss_edp_init); - -static void __exit mdss_edp_driver_cleanup(void) -{ - platform_driver_unregister(&mdss_edp_driver); -} -module_exit(mdss_edp_driver_cleanup); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("eDP controller driver"); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c index fb59d0b03afe..9ce47ccb5e09 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c @@ -1041,6 +1041,7 @@ void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data) register_data.hdcp_ctx = &ctrl->lib_ctx; register_data.client_ops = &client_ops; register_data.txmtr_ops = &txmtr_ops; + register_data.device_type = HDCP_TXMTR_HDMI; register_data.client_ctx = ctrl; register_data.tethered = ctrl->tethered; diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index 164bf0273597..5a355f226179 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -1638,9 +1638,16 @@ static int mdss_mdp_gdsc_notifier_call(struct notifier_block *self, if (event & REGULATOR_EVENT_ENABLE) { __mdss_restore_sec_cfg(mdata); } else if (event & REGULATOR_EVENT_PRE_DISABLE) { - pr_debug("mdss gdsc is getting disabled\n"); - /* halt the vbif transactions */ - mdss_mdp_vbif_axi_halt(mdata); + int active_cnt = atomic_read(&mdata->active_intf_cnt); + + pr_debug("mdss gdsc is getting disabled, active_cnt=%d\n", + active_cnt); + /* + * halt the vbif transactions only if we have any active + * overlay session + */ + if (active_cnt) + mdss_mdp_vbif_axi_halt(mdata); } return NOTIFY_OK; diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index d6cd6825e262..da5e7bb8a343 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -678,6 +678,8 @@ struct mdss_ad_info { bool last_calib_valid; u32 ipc_frame_count; u32 bl_data; + u32 bl_min_delta; + u32 bl_low_limit; u32 calc_itr; uint32_t bl_lin[AD_BL_LIN_LEN]; uint32_t bl_lin_inv[AD_BL_LIN_LEN]; @@ -1733,6 +1735,8 @@ void mdss_mdp_hist_intr_done(u32 isr); int mdss_mdp_ad_config(struct msm_fb_data_type *mfd, struct mdss_ad_init_cfg *init_cfg); +int mdss_mdp_ad_bl_config(struct msm_fb_data_type *mfd, + struct mdss_ad_bl_cfg *ad_bl_cfg); int mdss_mdp_ad_input(struct msm_fb_data_type *mfd, struct mdss_ad_input *input, int wait); int mdss_mdp_ad_addr_setup(struct mdss_data_type *mdata, u32 *ad_offsets); diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index f97a9f9a9adc..3fc6d94393d5 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -45,7 +45,7 @@ static struct { [MDSS_MDP_SSPP_VIG0] = { 0, { 0, 3, 0 }, { 0, 1, 3 } }, [MDSS_MDP_SSPP_VIG1] = { 1, { 3, 3, 0 }, { 2, 1, 3 } }, [MDSS_MDP_SSPP_VIG2] = { 2, { 6, 3, 0 }, { 4, 1, 3 } }, - [MDSS_MDP_SSPP_VIG3] = { 18, { 26, 3, 0 }, { 4, 1, 3 } }, + [MDSS_MDP_SSPP_VIG3] = { 18, { 26, 3, 0 }, { 6, 1, 3 } }, [MDSS_MDP_SSPP_RGB0] = { 3, { 9, 3, 0 }, { 8, 1, 3 } }, [MDSS_MDP_SSPP_RGB1] = { 4, { 12, 3, 0 }, { 10, 1, 3 } }, [MDSS_MDP_SSPP_RGB2] = { 5, { 15, 3, 0 }, { 12, 1, 3 } }, @@ -561,7 +561,8 @@ static u32 __calc_qseed3_mdp_clk_rate(struct mdss_mdp_pipe *pipe, u64 active_line; u64 backfill_line; - ver_dwnscale = ((u64)src_h << PHASE_STEP_SHIFT) / dst.h; + ver_dwnscale = (u64)src_h << PHASE_STEP_SHIFT; + do_div(ver_dwnscale, dst.h); if (ver_dwnscale > (MDSS_MDP_QSEED3_VER_DOWNSCALE_LIM << PHASE_STEP_SHIFT)) { diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c index 17cbdd44755a..0ae420724d61 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_layer.c +++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c @@ -114,7 +114,7 @@ static int __dest_scaler_data_setup(struct mdp_destination_scaler_data *ds_data, } static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl, - struct mdp_destination_scaler_data *ds_data) + struct mdp_destination_scaler_data *ds_data, int ds_count) { struct mdss_data_type *mdata; struct mdss_panel_info *pinfo; @@ -127,7 +127,7 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl, * when we switch between scaling factor or disabling scaling. */ if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map)) { - if (ctl->mixer_left) { + if (ctl->mixer_left && ctl->mixer_left->ds) { /* * Any scale update from usermode, we will update the * mixer width and height with the given LM width and @@ -149,10 +149,25 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl, ctl->mixer_left->height = ds_data->lm_height; pr_debug("Update mixer-left width/height: %dx%d\n", ds_data->lm_width, ds_data->lm_width); - } - if (ctl->mixer_right) { + if (ctl->mixer_right && ctl->mixer_right->ds) { + /* + * Advanced to next ds_data structure from commit if + * there is more than 1 for split display usecase. + */ + if (ds_count > 1) + ds_data++; + + pinfo = &ctl->panel_data->panel_info; + if ((ds_data->lm_width > get_panel_xres(pinfo)) || + (ds_data->lm_height > get_panel_yres(pinfo)) || + (ds_data->lm_width == 0) || + (ds_data->lm_height == 0)) { + pr_err("Invalid LM width / height setting\n"); + return -EINVAL; + } + /* * Split display both left and right should have the * same width and height @@ -162,6 +177,15 @@ static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl, pr_info("Update mixer-right width/height: %dx%d\n", ds_data->lm_width, ds_data->lm_height); + if (ctl->mixer_left && + ((ctl->mixer_right->width != + ctl->mixer_left->width) || + (ctl->mixer_right->height != + ctl->mixer_left->height))) { + pr_err("Mismatch width/heigth in LM for split display\n"); + return -EINVAL; + } + /* * For split display, CTL width should be equal to * whole panel size @@ -2098,17 +2122,17 @@ static int __validate_layers(struct msm_fb_data_type *mfd, if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map) && commit->dest_scaler && commit->dest_scaler_cnt) { + struct mdp_destination_scaler_data *ds_data = + commit->dest_scaler; + /* - * Find out which DS block to use based on LM assignment + * Find out which DS block to use based on DS commit info */ - if ((left_cnt > 0) && (right_cnt > 0) && - (commit->dest_scaler_cnt == 2)) + if (commit->dest_scaler_cnt == 2) ds_mode = DS_DUAL_MODE; - else if ((left_cnt > 0) && (right_cnt == 0) && - (commit->dest_scaler_cnt == 1)) + else if (ds_data->dest_scaler_ndx == 0) ds_mode = DS_LEFT; - else if ((left_cnt == 0) && (right_cnt > 0) && - (commit->dest_scaler_cnt == 1)) + else if (ds_data->dest_scaler_ndx == 1) ds_mode = DS_RIGHT; else { pr_err("Commit destination scaler count not matching with LM assignment, DS-cnt:%d\n", @@ -2396,7 +2420,8 @@ int mdss_mdp_layer_atomic_validate(struct msm_fb_data_type *mfd, if (commit->dest_scaler && commit->dest_scaler_cnt) { rc = mdss_mdp_destination_scaler_pre_validate(mdp5_data->ctl, - commit->dest_scaler); + commit->dest_scaler, + commit->dest_scaler_cnt); if (IS_ERR_VALUE(rc)) { pr_err("Destination scaler pre-validate failed\n"); return -EINVAL; diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 2baa1b9cd8b5..e5cdc750193e 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -4109,6 +4109,9 @@ static int mdss_mdp_pp_ioctl(struct msm_fb_data_type *mfd, case mdp_op_ad_cfg: ret = mdss_mdp_ad_config(mfd, &mdp_pp.data.ad_init_cfg); break; + case mdp_op_ad_bl_cfg: + ret = mdss_mdp_ad_bl_config(mfd, &mdp_pp.data.ad_bl_cfg); + break; case mdp_op_ad_input: ret = mdss_mdp_ad_input(mfd, &mdp_pp.data.ad_input, 1); if (ret > 0) { diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c index c4cbc220baf8..a760711e7501 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c @@ -3073,22 +3073,22 @@ int mdss_mdp_pp_default_overlay_config(struct msm_fb_data_type *mfd, return ret; } -static bool pp_ad_bl_threshold_check(int al_thresh, int base, int prev_bl, +static bool pp_ad_bl_threshold_check(int bl_min_delta, int base, int prev_bl, int curr_bl) { - int bl_thresh = 0, diff = 0; + int bl_delta_factor = 0, diff = 0; bool ret = false; - pr_debug("al_thresh = %d, base = %d\n", al_thresh, base); + pr_debug("bl_min_delta = %d, base = %d\n", bl_min_delta, base); if (base <= 0) { pr_debug("Invalid base for threshold calculation %d\n", base); return ret; } - bl_thresh = (curr_bl * al_thresh) / (base * 4); + bl_delta_factor = (curr_bl * bl_min_delta) / base; diff = (curr_bl > prev_bl) ? (curr_bl - prev_bl) : (prev_bl - curr_bl); - ret = (diff > bl_thresh) ? true : false; - pr_debug("prev_bl =%d, curr_bl = %d, bl_thresh = %d, diff = %d, ret = %d\n", - prev_bl, curr_bl, bl_thresh, diff, ret); + ret = (diff > bl_delta_factor) ? true : false; + pr_debug("prev_bl =%d, curr_bl = %d, bl_delta_factor = %d, diff = %d, ret = %d\n", + prev_bl, curr_bl, bl_delta_factor, diff, ret); return ret; } @@ -3163,7 +3163,10 @@ static int pp_ad_calc_bl(struct msm_fb_data_type *mfd, int bl_in, int *bl_out, ad_bl_out = temp; } - if (pp_ad_bl_threshold_check(ad->init.al_thresh, ad->init.alpha_base, + /* update AD backlight based on AD BL low limit */ + ad_bl_out = (ad_bl_out < ad->bl_low_limit ? + ad->bl_low_limit : ad_bl_out); + if (pp_ad_bl_threshold_check(ad->bl_min_delta, ad->init.alpha_base, ad->last_bl, ad_bl_out)) { mfd->ad_bl_level = ad_bl_out; pr_debug("backlight send to AD block: %d\n", mfd->ad_bl_level); @@ -5747,6 +5750,29 @@ ad_config_exit: return ret; } +int mdss_mdp_ad_bl_config(struct msm_fb_data_type *mfd, + struct mdss_ad_bl_cfg *ad_bl_cfg) +{ + int ret = 0; + struct mdss_ad_info *ad; + + ret = mdss_mdp_get_ad(mfd, &ad); + if (ret == -ENODEV || ret == -EPERM) { + pr_debug("AD not supported on device, disp num %d\n", + mfd->index); + return ret; + } else if (ret || !ad) { + pr_err("Failed to get ad info: ret = %d\n", ret); + return ret; + } + + mutex_lock(&ad->lock); + ad->bl_min_delta = ad_bl_cfg->bl_min_delta; + ad->bl_low_limit = ad_bl_cfg->bl_low_limit; + mutex_unlock(&ad->lock); + return 0; +} + int mdss_mdp_ad_input(struct msm_fb_data_type *mfd, struct mdss_ad_input *input, int wait) { int ret = 0; @@ -6293,6 +6319,8 @@ static int mdss_mdp_ad_setup(struct msm_fb_data_type *mfd) ad->last_calib_valid = false; ad->last_ad_data_valid = false; ad->ipc_frame_count = 0; + ad->bl_min_delta = 0; + ad->bl_low_limit = 0; ad->calc_itr = 0; ad->calc_hw_num = PP_AD_BAD_HW_NUM; memset(&ad->last_calib, 0, sizeof(ad->last_calib)); @@ -6556,6 +6584,8 @@ int mdss_mdp_ad_addr_setup(struct mdss_data_type *mdata, u32 *ad_offsets) mdata->ad_cfgs[i].last_str = 0xFFFFFFFF; mdata->ad_cfgs[i].last_bl = 0; mdata->ad_cfgs[i].last_ad_data = 0; + mdata->ad_cfgs[i].bl_low_limit = 0; + mdata->ad_cfgs[i].bl_min_delta = 0; memset(mdata->ad_cfgs[i].last_calib, 0, sizeof(mdata->ad_cfgs[i].last_calib)); mdata->ad_cfgs[i].last_calib_valid = false; diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c index fafc74d79ac1..1f62232e196b 100644 --- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c +++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c @@ -18,7 +18,7 @@ #include <linux/clk/msm-clk.h> #include "mdss_dsi.h" -#include "mdss_edp.h" +#include "mdss_dp.h" #include "mdss_dsi_phy.h" #define MDSS_DSI_DSIPHY_REGULATOR_CTRL_0 0x00 @@ -2423,314 +2423,3 @@ int mdss_dsi_pre_clkon_cb(void *priv, return rc; } - -void mdss_edp_clk_deinit(struct mdss_edp_drv_pdata *edp_drv) -{ - if (edp_drv->aux_clk) - clk_put(edp_drv->aux_clk); - if (edp_drv->pixel_clk) - clk_put(edp_drv->pixel_clk); - if (edp_drv->ahb_clk) - clk_put(edp_drv->ahb_clk); - if (edp_drv->link_clk) - clk_put(edp_drv->link_clk); - if (edp_drv->mdp_core_clk) - clk_put(edp_drv->mdp_core_clk); -} - -int mdss_edp_clk_init(struct mdss_edp_drv_pdata *edp_drv) -{ - struct device *dev = &(edp_drv->pdev->dev); - - edp_drv->aux_clk = clk_get(dev, "core_clk"); - if (IS_ERR(edp_drv->aux_clk)) { - pr_err("%s: Can't find aux_clk", __func__); - edp_drv->aux_clk = NULL; - goto mdss_edp_clk_err; - } - - edp_drv->pixel_clk = clk_get(dev, "pixel_clk"); - if (IS_ERR(edp_drv->pixel_clk)) { - pr_err("%s: Can't find pixel_clk", __func__); - edp_drv->pixel_clk = NULL; - goto mdss_edp_clk_err; - } - - edp_drv->ahb_clk = clk_get(dev, "iface_clk"); - if (IS_ERR(edp_drv->ahb_clk)) { - pr_err("%s: Can't find ahb_clk", __func__); - edp_drv->ahb_clk = NULL; - goto mdss_edp_clk_err; - } - - edp_drv->link_clk = clk_get(dev, "link_clk"); - if (IS_ERR(edp_drv->link_clk)) { - pr_err("%s: Can't find link_clk", __func__); - edp_drv->link_clk = NULL; - goto mdss_edp_clk_err; - } - - /* need mdss clock to receive irq */ - edp_drv->mdp_core_clk = clk_get(dev, "mdp_core_clk"); - if (IS_ERR(edp_drv->mdp_core_clk)) { - pr_err("%s: Can't find mdp_core_clk", __func__); - edp_drv->mdp_core_clk = NULL; - goto mdss_edp_clk_err; - } - - return 0; - -mdss_edp_clk_err: - mdss_edp_clk_deinit(edp_drv); - return -EPERM; -} - -int mdss_edp_aux_clk_enable(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret; - - if (clk_set_rate(edp_drv->aux_clk, 19200000) < 0) - pr_err("%s: aux_clk - clk_set_rate failed\n", - __func__); - - ret = clk_enable(edp_drv->aux_clk); - if (ret) { - pr_err("%s: Failed to enable aux clk\n", __func__); - goto c2; - } - - ret = clk_enable(edp_drv->ahb_clk); - if (ret) { - pr_err("%s: Failed to enable ahb clk\n", __func__); - goto c1; - } - - /* need mdss clock to receive irq */ - ret = clk_enable(edp_drv->mdp_core_clk); - if (ret) { - pr_err("%s: Failed to enable mdp_core_clk\n", __func__); - goto c0; - } - - return 0; -c0: - clk_disable(edp_drv->ahb_clk); -c1: - clk_disable(edp_drv->aux_clk); -c2: - return ret; - -} - -void mdss_edp_aux_clk_disable(struct mdss_edp_drv_pdata *edp_drv) -{ - clk_disable(edp_drv->aux_clk); - clk_disable(edp_drv->ahb_clk); - clk_disable(edp_drv->mdp_core_clk); -} - -static void mdss_edp_clk_set_rate(struct mdss_edp_drv_pdata *edp_drv) -{ - if (clk_set_rate(edp_drv->link_clk, edp_drv->link_rate * 27000000) < 0) - pr_err("%s: link_clk - clk_set_rate failed\n", - __func__); - - if (clk_set_rate(edp_drv->pixel_clk, edp_drv->pixel_rate) < 0) - pr_err("%s: pixel_clk - clk_set_rate failed\n", - __func__); -} - -int mdss_edp_clk_enable(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret; - - if (edp_drv->clk_on) { - pr_info("%s: edp clks are already ON\n", __func__); - return 0; - } - - if (clk_set_rate(edp_drv->link_clk, edp_drv->link_rate * 27000000) < 0) - pr_err("%s: link_clk - clk_set_rate failed\n", - __func__); - - if (clk_set_rate(edp_drv->aux_clk, edp_drv->aux_rate) < 0) - pr_err("%s: aux_clk - clk_set_rate failed\n", - __func__); - - if (clk_set_rate(edp_drv->pixel_clk, edp_drv->pixel_rate) < 0) - pr_err("%s: pixel_clk - clk_set_rate failed\n", - __func__); - - ret = clk_enable(edp_drv->aux_clk); - if (ret) { - pr_err("%s: Failed to enable aux clk\n", __func__); - goto c4; - } - ret = clk_enable(edp_drv->pixel_clk); - if (ret) { - pr_err("%s: Failed to enable pixel clk\n", __func__); - goto c3; - } - ret = clk_enable(edp_drv->ahb_clk); - if (ret) { - pr_err("%s: Failed to enable ahb clk\n", __func__); - goto c2; - } - ret = clk_enable(edp_drv->link_clk); - if (ret) { - pr_err("%s: Failed to enable link clk\n", __func__); - goto c1; - } - ret = clk_enable(edp_drv->mdp_core_clk); - if (ret) { - pr_err("%s: Failed to enable mdp_core_clk\n", __func__); - goto c0; - } - - edp_drv->clk_on = 1; - - return 0; - -c0: - clk_disable(edp_drv->link_clk); -c1: - clk_disable(edp_drv->ahb_clk); -c2: - clk_disable(edp_drv->pixel_clk); -c3: - clk_disable(edp_drv->aux_clk); -c4: - return ret; -} - -void mdss_edp_clk_disable(struct mdss_edp_drv_pdata *edp_drv) -{ - if (edp_drv->clk_on == 0) { - pr_info("%s: edp clks are already OFF\n", __func__); - return; - } - - clk_disable(edp_drv->aux_clk); - clk_disable(edp_drv->pixel_clk); - clk_disable(edp_drv->ahb_clk); - clk_disable(edp_drv->link_clk); - clk_disable(edp_drv->mdp_core_clk); - - edp_drv->clk_on = 0; -} - -int mdss_edp_prepare_aux_clocks(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret; - - /* ahb clock should be prepared first */ - ret = clk_prepare(edp_drv->ahb_clk); - if (ret) { - pr_err("%s: Failed to prepare ahb clk\n", __func__); - goto c3; - } - ret = clk_prepare(edp_drv->aux_clk); - if (ret) { - pr_err("%s: Failed to prepare aux clk\n", __func__); - goto c2; - } - - /* need mdss clock to receive irq */ - ret = clk_prepare(edp_drv->mdp_core_clk); - if (ret) { - pr_err("%s: Failed to prepare mdp_core clk\n", __func__); - goto c1; - } - - return 0; -c1: - clk_unprepare(edp_drv->aux_clk); -c2: - clk_unprepare(edp_drv->ahb_clk); -c3: - return ret; - -} - -void mdss_edp_unprepare_aux_clocks(struct mdss_edp_drv_pdata *edp_drv) -{ - clk_unprepare(edp_drv->mdp_core_clk); - clk_unprepare(edp_drv->aux_clk); - clk_unprepare(edp_drv->ahb_clk); -} - -int mdss_edp_prepare_clocks(struct mdss_edp_drv_pdata *edp_drv) -{ - int ret; - - mdss_edp_clk_set_rate(edp_drv); - - /* ahb clock should be prepared first */ - ret = clk_prepare(edp_drv->ahb_clk); - if (ret) { - pr_err("%s: Failed to prepare ahb clk\n", __func__); - goto c4; - } - ret = clk_prepare(edp_drv->aux_clk); - if (ret) { - pr_err("%s: Failed to prepare aux clk\n", __func__); - goto c3; - } - ret = clk_prepare(edp_drv->pixel_clk); - if (ret) { - pr_err("%s: Failed to prepare pixel clk\n", __func__); - goto c2; - } - ret = clk_prepare(edp_drv->link_clk); - if (ret) { - pr_err("%s: Failed to prepare link clk\n", __func__); - goto c1; - } - ret = clk_prepare(edp_drv->mdp_core_clk); - if (ret) { - pr_err("%s: Failed to prepare mdp_core clk\n", __func__); - goto c0; - } - - return 0; -c0: - clk_unprepare(edp_drv->link_clk); -c1: - clk_unprepare(edp_drv->pixel_clk); -c2: - clk_unprepare(edp_drv->aux_clk); -c3: - clk_unprepare(edp_drv->ahb_clk); -c4: - return ret; -} - -void mdss_edp_unprepare_clocks(struct mdss_edp_drv_pdata *edp_drv) -{ - clk_unprepare(edp_drv->mdp_core_clk); - clk_unprepare(edp_drv->aux_clk); - clk_unprepare(edp_drv->pixel_clk); - clk_unprepare(edp_drv->link_clk); - /* ahb clock should be last one to disable */ - clk_unprepare(edp_drv->ahb_clk); -} - -void mdss_edp_clk_debug(unsigned char *edp_base, unsigned char *mmss_cc_base) -{ - u32 da4, da0, d32c; - u32 dc4, dc0, d330; - - /* pixel clk */ - da0 = edp_read(mmss_cc_base + 0x0a0); - da4 = edp_read(mmss_cc_base + 0x0a4); - d32c = edp_read(mmss_cc_base + 0x32c); - - /* main link clk */ - dc0 = edp_read(mmss_cc_base + 0x0c0); - dc4 = edp_read(mmss_cc_base + 0x0c4); - d330 = edp_read(mmss_cc_base + 0x330); - - pr_err("%s: da0=%x da4=%x d32c=%x dc0=%x dc4=%x d330=%x\n", __func__, - (int)da0, (int)da4, (int)d32c, (int)dc0, (int)dc4, (int)d330); - -} diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c index 8e5cf194cc0b..4469202eaa8e 100644 --- a/drivers/virtio/virtio_pci_modern.c +++ b/drivers/virtio/virtio_pci_modern.c @@ -17,6 +17,7 @@ * */ +#include <linux/delay.h> #define VIRTIO_PCI_NO_LEGACY #include "virtio_pci_common.h" @@ -271,9 +272,13 @@ static void vp_reset(struct virtio_device *vdev) struct virtio_pci_device *vp_dev = to_vp_device(vdev); /* 0 status means a reset. */ vp_iowrite8(0, &vp_dev->common->device_status); - /* Flush out the status write, and flush in device writes, - * including MSI-X interrupts, if any. */ - vp_ioread8(&vp_dev->common->device_status); + /* After writing 0 to device_status, the driver MUST wait for a read of + * device_status to return 0 before reinitializing the device. + * This will flush out the status write, and flush in device writes, + * including MSI-X interrupts, if any. + */ + while (vp_ioread8(&vp_dev->common->device_status)) + msleep(1); /* Flush pending VQ/configuration callbacks. */ vp_synchronize_vectors(vdev); } diff --git a/drivers/watchdog/rc32434_wdt.c b/drivers/watchdog/rc32434_wdt.c index 71e78ef4b736..3a75f3b53452 100644 --- a/drivers/watchdog/rc32434_wdt.c +++ b/drivers/watchdog/rc32434_wdt.c @@ -237,7 +237,7 @@ static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd, return -EINVAL; /* Fall through */ case WDIOC_GETTIMEOUT: - return copy_to_user(argp, &timeout, sizeof(int)); + return copy_to_user(argp, &timeout, sizeof(int)) ? -EFAULT : 0; default: return -ENOTTY; } diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c index 524c22146429..44367783f07a 100644 --- a/drivers/xen/events/events_base.c +++ b/drivers/xen/events/events_base.c @@ -484,9 +484,19 @@ static void eoi_pirq(struct irq_data *data) struct physdev_eoi eoi = { .irq = pirq_from_irq(data->irq) }; int rc = 0; - irq_move_irq(data); + if (!VALID_EVTCHN(evtchn)) + return; - if (VALID_EVTCHN(evtchn)) + if (unlikely(irqd_is_setaffinity_pending(data))) { + int masked = test_and_set_mask(evtchn); + + clear_evtchn(evtchn); + + irq_move_masked_irq(data); + + if (!masked) + unmask_evtchn(evtchn); + } else clear_evtchn(evtchn); if (pirq_needs_eoi(data->irq)) { @@ -1357,9 +1367,19 @@ static void ack_dynirq(struct irq_data *data) { int evtchn = evtchn_from_irq(data->irq); - irq_move_irq(data); + if (!VALID_EVTCHN(evtchn)) + return; - if (VALID_EVTCHN(evtchn)) + if (unlikely(irqd_is_setaffinity_pending(data))) { + int masked = test_and_set_mask(evtchn); + + clear_evtchn(evtchn); + + irq_move_masked_irq(data); + + if (!masked) + unmask_evtchn(evtchn); + } else clear_evtchn(evtchn); } diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 0f09526aa7d9..5e5db3687e34 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1885,7 +1885,7 @@ static int start_ordered_ops(struct inode *inode, loff_t start, loff_t end) */ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) { - struct dentry *dentry = file->f_path.dentry; + struct dentry *dentry = file_dentry(file); struct inode *inode = d_inode(dentry); struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_trans_handle *trans; diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 323e12cc9d2f..0e044d7ee721 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -4406,6 +4406,127 @@ static int btrfs_log_trailing_hole(struct btrfs_trans_handle *trans, return ret; } +/* + * When we are logging a new inode X, check if it doesn't have a reference that + * matches the reference from some other inode Y created in a past transaction + * and that was renamed in the current transaction. If we don't do this, then at + * log replay time we can lose inode Y (and all its files if it's a directory): + * + * mkdir /mnt/x + * echo "hello world" > /mnt/x/foobar + * sync + * mv /mnt/x /mnt/y + * mkdir /mnt/x # or touch /mnt/x + * xfs_io -c fsync /mnt/x + * <power fail> + * mount fs, trigger log replay + * + * After the log replay procedure, we would lose the first directory and all its + * files (file foobar). + * For the case where inode Y is not a directory we simply end up losing it: + * + * echo "123" > /mnt/foo + * sync + * mv /mnt/foo /mnt/bar + * echo "abc" > /mnt/foo + * xfs_io -c fsync /mnt/foo + * <power fail> + * + * We also need this for cases where a snapshot entry is replaced by some other + * entry (file or directory) otherwise we end up with an unreplayable log due to + * attempts to delete the snapshot entry (entry of type BTRFS_ROOT_ITEM_KEY) as + * if it were a regular entry: + * + * mkdir /mnt/x + * btrfs subvolume snapshot /mnt /mnt/x/snap + * btrfs subvolume delete /mnt/x/snap + * rmdir /mnt/x + * mkdir /mnt/x + * fsync /mnt/x or fsync some new file inside it + * <power fail> + * + * The snapshot delete, rmdir of x, mkdir of a new x and the fsync all happen in + * the same transaction. + */ +static int btrfs_check_ref_name_override(struct extent_buffer *eb, + const int slot, + const struct btrfs_key *key, + struct inode *inode) +{ + int ret; + struct btrfs_path *search_path; + char *name = NULL; + u32 name_len = 0; + u32 item_size = btrfs_item_size_nr(eb, slot); + u32 cur_offset = 0; + unsigned long ptr = btrfs_item_ptr_offset(eb, slot); + + search_path = btrfs_alloc_path(); + if (!search_path) + return -ENOMEM; + search_path->search_commit_root = 1; + search_path->skip_locking = 1; + + while (cur_offset < item_size) { + u64 parent; + u32 this_name_len; + u32 this_len; + unsigned long name_ptr; + struct btrfs_dir_item *di; + + if (key->type == BTRFS_INODE_REF_KEY) { + struct btrfs_inode_ref *iref; + + iref = (struct btrfs_inode_ref *)(ptr + cur_offset); + parent = key->offset; + this_name_len = btrfs_inode_ref_name_len(eb, iref); + name_ptr = (unsigned long)(iref + 1); + this_len = sizeof(*iref) + this_name_len; + } else { + struct btrfs_inode_extref *extref; + + extref = (struct btrfs_inode_extref *)(ptr + + cur_offset); + parent = btrfs_inode_extref_parent(eb, extref); + this_name_len = btrfs_inode_extref_name_len(eb, extref); + name_ptr = (unsigned long)&extref->name; + this_len = sizeof(*extref) + this_name_len; + } + + if (this_name_len > name_len) { + char *new_name; + + new_name = krealloc(name, this_name_len, GFP_NOFS); + if (!new_name) { + ret = -ENOMEM; + goto out; + } + name_len = this_name_len; + name = new_name; + } + + read_extent_buffer(eb, name, name_ptr, this_name_len); + di = btrfs_lookup_dir_item(NULL, BTRFS_I(inode)->root, + search_path, parent, + name, this_name_len, 0); + if (di && !IS_ERR(di)) { + ret = 1; + goto out; + } else if (IS_ERR(di)) { + ret = PTR_ERR(di); + goto out; + } + btrfs_release_path(search_path); + + cur_offset += this_len; + } + ret = 0; +out: + btrfs_free_path(search_path); + kfree(name); + return ret; +} + /* log a single inode in the tree log. * At least one parent directory for this inode must exist in the tree * or be logged already. @@ -4578,6 +4699,22 @@ again: if (min_key.type == BTRFS_INODE_ITEM_KEY) need_log_inode_item = false; + if ((min_key.type == BTRFS_INODE_REF_KEY || + min_key.type == BTRFS_INODE_EXTREF_KEY) && + BTRFS_I(inode)->generation == trans->transid) { + ret = btrfs_check_ref_name_override(path->nodes[0], + path->slots[0], + &min_key, inode); + if (ret < 0) { + err = ret; + goto out_unlock; + } else if (ret > 0) { + err = 1; + btrfs_set_log_full_commit(root->fs_info, trans); + goto out_unlock; + } + } + /* Skip xattrs, we log them later with btrfs_log_all_xattrs() */ if (min_key.type == BTRFS_XATTR_ITEM_KEY) { if (ins_nr == 0) diff --git a/fs/coredump.c b/fs/coredump.c index 1777331eee76..dfc87c5f5a54 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -32,6 +32,9 @@ #include <linux/pipe_fs_i.h> #include <linux/oom.h> #include <linux/compat.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/path.h> #include <asm/uaccess.h> #include <asm/mmu_context.h> @@ -627,6 +630,8 @@ void do_coredump(const siginfo_t *siginfo) } } else { struct inode *inode; + int open_flags = O_CREAT | O_RDWR | O_NOFOLLOW | + O_LARGEFILE | O_EXCL; if (cprm.limit < binfmt->min_coredump) goto fail_unlock; @@ -665,10 +670,27 @@ void do_coredump(const siginfo_t *siginfo) * what matters is that at least one of the two processes * writes its coredump successfully, not which one. */ - cprm.file = filp_open(cn.corename, - O_CREAT | 2 | O_NOFOLLOW | - O_LARGEFILE | O_EXCL, - 0600); + if (need_suid_safe) { + /* + * Using user namespaces, normal user tasks can change + * their current->fs->root to point to arbitrary + * directories. Since the intention of the "only dump + * with a fully qualified path" rule is to control where + * coredumps may be placed using root privileges, + * current->fs->root must not be used. Instead, use the + * root directory of init_task. + */ + struct path root; + + task_lock(&init_task); + get_fs_root(init_task.fs, &root); + task_unlock(&init_task); + cprm.file = file_open_root(root.dentry, root.mnt, + cn.corename, open_flags, 0600); + path_put(&root); + } else { + cprm.file = filp_open(cn.corename, open_flags, 0600); + } if (IS_ERR(cprm.file)) goto fail_unlock; diff --git a/fs/dcache.c b/fs/dcache.c index 877bcbbd03ff..18effa378f97 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1666,7 +1666,8 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op) DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE | DCACHE_OP_DELETE | - DCACHE_OP_SELECT_INODE)); + DCACHE_OP_SELECT_INODE | + DCACHE_OP_REAL)); dentry->d_op = op; if (!op) return; @@ -1684,6 +1685,8 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op) dentry->d_flags |= DCACHE_OP_PRUNE; if (op->d_select_inode) dentry->d_flags |= DCACHE_OP_SELECT_INODE; + if (op->d_real) + dentry->d_flags |= DCACHE_OP_REAL; } EXPORT_SYMBOL(d_set_d_op); diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 5cf6d8be48dd..786cb51cab56 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -850,6 +850,29 @@ do { \ #include "extents_status.h" /* + * Lock subclasses for i_data_sem in the ext4_inode_info structure. + * + * These are needed to avoid lockdep false positives when we need to + * allocate blocks to the quota inode during ext4_map_blocks(), while + * holding i_data_sem for a normal (non-quota) inode. Since we don't + * do quota tracking for the quota inode, this avoids deadlock (as + * well as infinite recursion, since it isn't turtles all the way + * down...) + * + * I_DATA_SEM_NORMAL - Used for most inodes + * I_DATA_SEM_OTHER - Used by move_inode.c for the second normal inode + * where the second inode has larger inode number + * than the first + * I_DATA_SEM_QUOTA - Used for quota inodes only + */ +enum { + I_DATA_SEM_NORMAL = 0, + I_DATA_SEM_OTHER, + I_DATA_SEM_QUOTA, +}; + + +/* * fourth extended file system inode data in memory */ struct ext4_inode_info { diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index e032a0423e35..9bdbf98240a0 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c @@ -60,10 +60,10 @@ ext4_double_down_write_data_sem(struct inode *first, struct inode *second) { if (first < second) { down_write(&EXT4_I(first)->i_data_sem); - down_write_nested(&EXT4_I(second)->i_data_sem, SINGLE_DEPTH_NESTING); + down_write_nested(&EXT4_I(second)->i_data_sem, I_DATA_SEM_OTHER); } else { down_write(&EXT4_I(second)->i_data_sem); - down_write_nested(&EXT4_I(first)->i_data_sem, SINGLE_DEPTH_NESTING); + down_write_nested(&EXT4_I(first)->i_data_sem, I_DATA_SEM_OTHER); } } @@ -483,6 +483,13 @@ mext_check_arguments(struct inode *orig_inode, return -EBUSY; } + if (IS_NOQUOTA(orig_inode) || IS_NOQUOTA(donor_inode)) { + ext4_debug("ext4 move extent: The argument files should " + "not be quota files [ino:orig %lu, donor %lu]\n", + orig_inode->i_ino, donor_inode->i_ino); + return -EBUSY; + } + /* Ext4 move extent supports only extent based file */ if (!(ext4_test_inode_flag(orig_inode, EXT4_INODE_EXTENTS))) { ext4_debug("ext4 move extent: orig file is not extents " diff --git a/fs/ext4/super.c b/fs/ext4/super.c index c9ab67da6e5a..ba1cf0bf2f81 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1292,9 +1292,9 @@ static int set_qf_name(struct super_block *sb, int qtype, substring_t *args) return -1; } if (ext4_has_feature_quota(sb)) { - ext4_msg(sb, KERN_ERR, "Cannot set journaled quota options " - "when QUOTA feature is enabled"); - return -1; + ext4_msg(sb, KERN_INFO, "Journaled quota options " + "ignored when QUOTA feature is enabled"); + return 1; } qname = match_strdup(args); if (!qname) { @@ -1657,10 +1657,10 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token, return -1; } if (ext4_has_feature_quota(sb)) { - ext4_msg(sb, KERN_ERR, - "Cannot set journaled quota options " + ext4_msg(sb, KERN_INFO, + "Quota format mount options ignored " "when QUOTA feature is enabled"); - return -1; + return 1; } sbi->s_jquota_fmt = m->mount_opt; #endif @@ -1721,11 +1721,11 @@ static int parse_options(char *options, struct super_block *sb, #ifdef CONFIG_QUOTA if (ext4_has_feature_quota(sb) && (test_opt(sb, USRQUOTA) || test_opt(sb, GRPQUOTA))) { - ext4_msg(sb, KERN_ERR, "Cannot set quota options when QUOTA " - "feature is enabled"); - return 0; - } - if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) { + ext4_msg(sb, KERN_INFO, "Quota feature enabled, usrquota and grpquota " + "mount options ignored."); + clear_opt(sb, USRQUOTA); + clear_opt(sb, GRPQUOTA); + } else if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) { if (test_opt(sb, USRQUOTA) && sbi->s_qf_names[USRQUOTA]) clear_opt(sb, USRQUOTA); @@ -4936,6 +4936,20 @@ static int ext4_quota_on_mount(struct super_block *sb, int type) EXT4_SB(sb)->s_jquota_fmt, type); } +static void lockdep_set_quota_inode(struct inode *inode, int subclass) +{ + struct ext4_inode_info *ei = EXT4_I(inode); + + /* The first argument of lockdep_set_subclass has to be + * *exactly* the same as the argument to init_rwsem() --- in + * this case, in init_once() --- or lockdep gets unhappy + * because the name of the lock is set using the + * stringification of the argument to init_rwsem(). + */ + (void) ei; /* shut up clang warning if !CONFIG_LOCKDEP */ + lockdep_set_subclass(&ei->i_data_sem, subclass); +} + /* * Standard function to be called on quota_on */ @@ -4975,8 +4989,12 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id, if (err) return err; } - - return dquot_quota_on(sb, type, format_id, path); + lockdep_set_quota_inode(path->dentry->d_inode, I_DATA_SEM_QUOTA); + err = dquot_quota_on(sb, type, format_id, path); + if (err) + lockdep_set_quota_inode(path->dentry->d_inode, + I_DATA_SEM_NORMAL); + return err; } static int ext4_quota_enable(struct super_block *sb, int type, int format_id, @@ -5002,8 +5020,11 @@ static int ext4_quota_enable(struct super_block *sb, int type, int format_id, /* Don't account quota for quota files to avoid recursion */ qf_inode->i_flags |= S_NOQUOTA; + lockdep_set_quota_inode(qf_inode, I_DATA_SEM_QUOTA); err = dquot_enable(qf_inode, type, format_id, flags); iput(qf_inode); + if (err) + lockdep_set_quota_inode(qf_inode, I_DATA_SEM_NORMAL); return err; } diff --git a/fs/fhandle.c b/fs/fhandle.c index d59712dfa3e7..ca3c3dd01789 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c @@ -228,7 +228,7 @@ long do_handle_open(int mountdirfd, path_put(&path); return fd; } - file = file_open_root(path.dentry, path.mnt, "", open_flag); + file = file_open_root(path.dentry, path.mnt, "", open_flag, 0); if (IS_ERR(file)) { put_unused_fd(fd); retval = PTR_ERR(file); diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index bf8c78920bee..de11206dda63 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -281,13 +281,15 @@ locked_inode_to_wb_and_lock_list(struct inode *inode) wb_get(wb); spin_unlock(&inode->i_lock); spin_lock(&wb->list_lock); - wb_put(wb); /* not gonna deref it anymore */ /* i_wb may have changed inbetween, can't use inode_to_wb() */ - if (likely(wb == inode->i_wb)) - return wb; /* @inode already has ref */ + if (likely(wb == inode->i_wb)) { + wb_put(wb); /* @inode already has ref */ + return wb; + } spin_unlock(&wb->list_lock); + wb_put(wb); cpu_relax(); spin_lock(&inode->i_lock); } @@ -1339,10 +1341,10 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) * we go e.g. from filesystem. Flusher thread uses __writeback_single_inode() * and does more profound writeback list handling in writeback_sb_inodes(). */ -static int -writeback_single_inode(struct inode *inode, struct bdi_writeback *wb, - struct writeback_control *wbc) +static int writeback_single_inode(struct inode *inode, + struct writeback_control *wbc) { + struct bdi_writeback *wb; int ret = 0; spin_lock(&inode->i_lock); @@ -1380,7 +1382,8 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb, ret = __writeback_single_inode(inode, wbc); wbc_detach_inode(wbc); - spin_lock(&wb->list_lock); + + wb = inode_to_wb_and_lock_list(inode); spin_lock(&inode->i_lock); /* * If inode is clean, remove it from writeback lists. Otherwise don't @@ -1455,6 +1458,7 @@ static long writeback_sb_inodes(struct super_block *sb, while (!list_empty(&wb->b_io)) { struct inode *inode = wb_inode(wb->b_io.prev); + struct bdi_writeback *tmp_wb; if (inode->i_sb != sb) { if (work->sb) { @@ -1545,15 +1549,23 @@ static long writeback_sb_inodes(struct super_block *sb, cond_resched(); } - - spin_lock(&wb->list_lock); + /* + * Requeue @inode if still dirty. Be careful as @inode may + * have been switched to another wb in the meantime. + */ + tmp_wb = inode_to_wb_and_lock_list(inode); spin_lock(&inode->i_lock); if (!(inode->i_state & I_DIRTY_ALL)) wrote++; - requeue_inode(inode, wb, &wbc); + requeue_inode(inode, tmp_wb, &wbc); inode_sync_complete(inode); spin_unlock(&inode->i_lock); + if (unlikely(tmp_wb != wb)) { + spin_unlock(&tmp_wb->list_lock); + spin_lock(&wb->list_lock); + } + /* * bail out to wb_writeback() often enough to check * background threshold and other termination conditions. @@ -2340,7 +2352,6 @@ EXPORT_SYMBOL(sync_inodes_sb); */ int write_inode_now(struct inode *inode, int sync) { - struct bdi_writeback *wb = &inode_to_bdi(inode)->wb; struct writeback_control wbc = { .nr_to_write = LONG_MAX, .sync_mode = sync ? WB_SYNC_ALL : WB_SYNC_NONE, @@ -2352,7 +2363,7 @@ int write_inode_now(struct inode *inode, int sync) wbc.nr_to_write = 0; might_sleep(); - return writeback_single_inode(inode, wb, &wbc); + return writeback_single_inode(inode, &wbc); } EXPORT_SYMBOL(write_inode_now); @@ -2369,7 +2380,7 @@ EXPORT_SYMBOL(write_inode_now); */ int sync_inode(struct inode *inode, struct writeback_control *wbc) { - return writeback_single_inode(inode, &inode_to_bdi(inode)->wb, wbc); + return writeback_single_inode(inode, wbc); } EXPORT_SYMBOL(sync_inode); diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index 8e3ee1936c7e..c5b6b7165489 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -90,7 +90,7 @@ static struct list_head *cuse_conntbl_head(dev_t devt) static ssize_t cuse_read_iter(struct kiocb *kiocb, struct iov_iter *to) { - struct fuse_io_priv io = { .async = 0, .file = kiocb->ki_filp }; + struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(kiocb->ki_filp); loff_t pos = 0; return fuse_direct_io(&io, to, &pos, FUSE_DIO_CUSE); @@ -98,7 +98,7 @@ static ssize_t cuse_read_iter(struct kiocb *kiocb, struct iov_iter *to) static ssize_t cuse_write_iter(struct kiocb *kiocb, struct iov_iter *from) { - struct fuse_io_priv io = { .async = 0, .file = kiocb->ki_filp }; + struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(kiocb->ki_filp); loff_t pos = 0; /* * No locking or generic_write_checks(), the server is diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 8ff09059a91d..461dcf5e4526 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -546,6 +546,11 @@ static void fuse_release_user_pages(struct fuse_req *req, int write) } } +static void fuse_io_release(struct kref *kref) +{ + kfree(container_of(kref, struct fuse_io_priv, refcnt)); +} + static ssize_t fuse_get_res_by_io(struct fuse_io_priv *io) { if (io->err) @@ -603,8 +608,9 @@ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos) } io->iocb->ki_complete(io->iocb, res, 0); - kfree(io); } + + kref_put(&io->refcnt, fuse_io_release); } static void fuse_aio_complete_req(struct fuse_conn *fc, struct fuse_req *req) @@ -631,6 +637,7 @@ static size_t fuse_async_req_send(struct fuse_conn *fc, struct fuse_req *req, size_t num_bytes, struct fuse_io_priv *io) { spin_lock(&io->lock); + kref_get(&io->refcnt); io->size += num_bytes; io->reqs++; spin_unlock(&io->lock); @@ -709,7 +716,7 @@ static void fuse_short_read(struct fuse_req *req, struct inode *inode, static int fuse_do_readpage(struct file *file, struct page *page) { - struct fuse_io_priv io = { .async = 0, .file = file }; + struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(file); struct inode *inode = page->mapping->host; struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req; @@ -1009,7 +1016,7 @@ static size_t fuse_send_write_pages(struct fuse_req *req, struct file *file, size_t res; unsigned offset; unsigned i; - struct fuse_io_priv io = { .async = 0, .file = file }; + struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(file); for (i = 0; i < req->num_pages; i++) fuse_wait_on_page_writeback(inode, req->pages[i]->index); @@ -1430,7 +1437,7 @@ static ssize_t __fuse_direct_read(struct fuse_io_priv *io, static ssize_t fuse_direct_read_iter(struct kiocb *iocb, struct iov_iter *to) { - struct fuse_io_priv io = { .async = 0, .file = iocb->ki_filp }; + struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb->ki_filp); return __fuse_direct_read(&io, to, &iocb->ki_pos); } @@ -1438,7 +1445,7 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); - struct fuse_io_priv io = { .async = 0, .file = file }; + struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(file); ssize_t res; if (is_bad_inode(inode)) @@ -2824,6 +2831,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t offset) loff_t i_size; size_t count = iov_iter_count(iter); struct fuse_io_priv *io; + bool is_sync = is_sync_kiocb(iocb); pos = offset; inode = file->f_mapping->host; @@ -2844,6 +2852,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t offset) if (!io) return -ENOMEM; spin_lock_init(&io->lock); + kref_init(&io->refcnt); io->reqs = 1; io->bytes = -1; io->size = 0; @@ -2863,12 +2872,18 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t offset) * to wait on real async I/O requests, so we must submit this request * synchronously. */ - if (!is_sync_kiocb(iocb) && (offset + count > i_size) && + if (!is_sync && (offset + count > i_size) && iov_iter_rw(iter) == WRITE) io->async = false; - if (io->async && is_sync_kiocb(iocb)) + if (io->async && is_sync) { + /* + * Additional reference to keep io around after + * calling fuse_aio_complete() + */ + kref_get(&io->refcnt); io->done = &wait; + } if (iov_iter_rw(iter) == WRITE) { ret = fuse_direct_io(io, iter, &pos, FUSE_DIO_WRITE); @@ -2881,14 +2896,14 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t offset) fuse_aio_complete(io, ret < 0 ? ret : 0, -1); /* we have a non-extending, async request, so return */ - if (!is_sync_kiocb(iocb)) + if (!is_sync) return -EIOCBQUEUED; wait_for_completion(&wait); ret = fuse_get_res_by_io(io); } - kfree(io); + kref_put(&io->refcnt, fuse_io_release); if (iov_iter_rw(iter) == WRITE) { if (ret > 0) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 8db84062741d..0cbeea6ee831 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -22,6 +22,7 @@ #include <linux/rbtree.h> #include <linux/poll.h> #include <linux/workqueue.h> +#include <linux/kref.h> /** Max number of pages that can be used in a single read request */ #define FUSE_MAX_PAGES_PER_REQ 32 @@ -248,6 +249,7 @@ struct fuse_args { /** The request IO state (for asynchronous processing) */ struct fuse_io_priv { + struct kref refcnt; int async; spinlock_t lock; unsigned reqs; @@ -261,6 +263,13 @@ struct fuse_io_priv { struct completion *done; }; +#define FUSE_IO_PRIV_SYNC(f) \ +{ \ + .refcnt = { ATOMIC_INIT(1) }, \ + .async = 0, \ + .file = f, \ +} + /** * Request flags * diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 81e622681c82..624a57a9c4aa 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -1408,11 +1408,12 @@ out: /** * jbd2_mark_journal_empty() - Mark on disk journal as empty. * @journal: The journal to update. + * @write_op: With which operation should we write the journal sb * * Update a journal's dynamic superblock fields to show that journal is empty. * Write updated superblock to disk waiting for IO to complete. */ -static void jbd2_mark_journal_empty(journal_t *journal) +static void jbd2_mark_journal_empty(journal_t *journal, int write_op) { journal_superblock_t *sb = journal->j_superblock; @@ -1430,7 +1431,7 @@ static void jbd2_mark_journal_empty(journal_t *journal) sb->s_start = cpu_to_be32(0); read_unlock(&journal->j_state_lock); - jbd2_write_superblock(journal, WRITE_FUA); + jbd2_write_superblock(journal, write_op); /* Log is no longer empty */ write_lock(&journal->j_state_lock); @@ -1716,7 +1717,13 @@ int jbd2_journal_destroy(journal_t *journal) if (journal->j_sb_buffer) { if (!is_journal_aborted(journal)) { mutex_lock(&journal->j_checkpoint_mutex); - jbd2_mark_journal_empty(journal); + + write_lock(&journal->j_state_lock); + journal->j_tail_sequence = + ++journal->j_transaction_sequence; + write_unlock(&journal->j_state_lock); + + jbd2_mark_journal_empty(journal, WRITE_FLUSH_FUA); mutex_unlock(&journal->j_checkpoint_mutex); } else err = -EIO; @@ -1975,7 +1982,7 @@ int jbd2_journal_flush(journal_t *journal) * the magic code for a fully-recovered superblock. Any future * commits of data to the journal will restore the current * s_start value. */ - jbd2_mark_journal_empty(journal); + jbd2_mark_journal_empty(journal, WRITE_FUA); mutex_unlock(&journal->j_checkpoint_mutex); write_lock(&journal->j_state_lock); J_ASSERT(!journal->j_running_transaction); @@ -2021,7 +2028,7 @@ int jbd2_journal_wipe(journal_t *journal, int write) if (write) { /* Lock to make assertions happy... */ mutex_lock(&journal->j_checkpoint_mutex); - jbd2_mark_journal_empty(journal); + jbd2_mark_journal_empty(journal, WRITE_FUA); mutex_unlock(&journal->j_checkpoint_mutex); } diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index ce5a21861074..5fc2162afb67 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -377,7 +377,7 @@ int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc, again: timestamp = jiffies; gencount = nfs_inc_attr_generation_counter(); - error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, entry->cookie, pages, + error = NFS_PROTO(inode)->readdir(file_dentry(file), cred, entry->cookie, pages, NFS_SERVER(inode)->dtsize, desc->plus); if (error < 0) { /* We requested READDIRPLUS, but the server doesn't grok it */ @@ -560,7 +560,7 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en count++; if (desc->plus != 0) - nfs_prime_dcache(desc->file->f_path.dentry, entry); + nfs_prime_dcache(file_dentry(desc->file), entry); status = nfs_readdir_add_to_array(entry, page); if (status != 0) @@ -864,7 +864,7 @@ static bool nfs_dir_mapping_need_revalidate(struct inode *dir) */ static int nfs_readdir(struct file *file, struct dir_context *ctx) { - struct dentry *dentry = file->f_path.dentry; + struct dentry *dentry = file_dentry(file); struct inode *inode = d_inode(dentry); nfs_readdir_descriptor_t my_desc, *desc = &my_desc; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 3e2071a177fd..f714b98cfd74 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -927,7 +927,7 @@ int nfs_open(struct inode *inode, struct file *filp) { struct nfs_open_context *ctx; - ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode); + ctx = alloc_nfs_open_context(file_dentry(filp), filp->f_mode); if (IS_ERR(ctx)) return PTR_ERR(ctx); nfs_file_set_open_context(filp, ctx); diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c index db9b5fea5b3e..679e003818b1 100644 --- a/fs/nfs/nfs4file.c +++ b/fs/nfs/nfs4file.c @@ -26,7 +26,7 @@ static int nfs4_file_open(struct inode *inode, struct file *filp) { struct nfs_open_context *ctx; - struct dentry *dentry = filp->f_path.dentry; + struct dentry *dentry = file_dentry(filp); struct dentry *parent = NULL; struct inode *dir; unsigned openflags = filp->f_flags; @@ -57,7 +57,7 @@ nfs4_file_open(struct inode *inode, struct file *filp) parent = dget_parent(dentry); dir = d_inode(parent); - ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode); + ctx = alloc_nfs_open_context(file_dentry(filp), filp->f_mode); err = PTR_ERR(ctx); if (IS_ERR(ctx)) goto out; diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index a9f096c7e99f..7d5351cd67fb 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -877,6 +877,7 @@ nfsd4_secinfo(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, &exp, &dentry); if (err) return err; + fh_unlock(&cstate->current_fh); if (d_really_is_negative(dentry)) { exp_put(exp); err = nfserr_noent; diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 51c9e9ca39a4..12935209deca 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1072,8 +1072,9 @@ nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename READ_BUF(4); rename->rn_snamelen = be32_to_cpup(p++); - READ_BUF(rename->rn_snamelen + 4); + READ_BUF(rename->rn_snamelen); SAVEMEM(rename->rn_sname, rename->rn_snamelen); + READ_BUF(4); rename->rn_tnamelen = be32_to_cpup(p++); READ_BUF(rename->rn_tnamelen); SAVEMEM(rename->rn_tname, rename->rn_tnamelen); @@ -1155,13 +1156,14 @@ nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclient READ_BUF(8); setclientid->se_callback_prog = be32_to_cpup(p++); setclientid->se_callback_netid_len = be32_to_cpup(p++); - - READ_BUF(setclientid->se_callback_netid_len + 4); + READ_BUF(setclientid->se_callback_netid_len); SAVEMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len); + READ_BUF(4); setclientid->se_callback_addr_len = be32_to_cpup(p++); - READ_BUF(setclientid->se_callback_addr_len + 4); + READ_BUF(setclientid->se_callback_addr_len); SAVEMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len); + READ_BUF(4); setclientid->se_callback_ident = be32_to_cpup(p++); DECODE_TAIL; @@ -1815,8 +1817,9 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) READ_BUF(4); argp->taglen = be32_to_cpup(p++); - READ_BUF(argp->taglen + 8); + READ_BUF(argp->taglen); SAVEMEM(argp->tag, argp->taglen); + READ_BUF(8); argp->minorversion = be32_to_cpup(p++); argp->opcnt = be32_to_cpup(p++); max_reply += 4 + (XDR_QUADLEN(argp->taglen) << 2); diff --git a/fs/ocfs2/dlm/dlmconvert.c b/fs/ocfs2/dlm/dlmconvert.c index e36d63ff1783..f90931335c6b 100644 --- a/fs/ocfs2/dlm/dlmconvert.c +++ b/fs/ocfs2/dlm/dlmconvert.c @@ -262,6 +262,7 @@ enum dlm_status dlmconvert_remote(struct dlm_ctxt *dlm, struct dlm_lock *lock, int flags, int type) { enum dlm_status status; + u8 old_owner = res->owner; mlog(0, "type=%d, convert_type=%d, busy=%d\n", lock->ml.type, lock->ml.convert_type, res->state & DLM_LOCK_RES_IN_PROGRESS); @@ -287,6 +288,19 @@ enum dlm_status dlmconvert_remote(struct dlm_ctxt *dlm, status = DLM_DENIED; goto bail; } + + if (lock->ml.type == type && lock->ml.convert_type == LKM_IVMODE) { + mlog(0, "last convert request returned DLM_RECOVERING, but " + "owner has already queued and sent ast to me. res %.*s, " + "(cookie=%u:%llu, type=%d, conv=%d)\n", + res->lockname.len, res->lockname.name, + dlm_get_lock_cookie_node(be64_to_cpu(lock->ml.cookie)), + dlm_get_lock_cookie_seq(be64_to_cpu(lock->ml.cookie)), + lock->ml.type, lock->ml.convert_type); + status = DLM_NORMAL; + goto bail; + } + res->state |= DLM_LOCK_RES_IN_PROGRESS; /* move lock to local convert queue */ /* do not alter lock refcount. switching lists. */ @@ -316,11 +330,19 @@ enum dlm_status dlmconvert_remote(struct dlm_ctxt *dlm, spin_lock(&res->spinlock); res->state &= ~DLM_LOCK_RES_IN_PROGRESS; lock->convert_pending = 0; - /* if it failed, move it back to granted queue */ + /* if it failed, move it back to granted queue. + * if master returns DLM_NORMAL and then down before sending ast, + * it may have already been moved to granted queue, reset to + * DLM_RECOVERING and retry convert */ if (status != DLM_NORMAL) { if (status != DLM_NOTQUEUED) dlm_error(status); dlm_revert_pending_convert(res, lock); + } else if ((res->state & DLM_LOCK_RES_RECOVERING) || + (old_owner != res->owner)) { + mlog(0, "res %.*s is in recovering or has been recovered.\n", + res->lockname.len, res->lockname.name); + status = DLM_RECOVERING; } bail: spin_unlock(&res->spinlock); diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c index 42f0cae93a0a..4a338803e7e9 100644 --- a/fs/ocfs2/dlm/dlmrecovery.c +++ b/fs/ocfs2/dlm/dlmrecovery.c @@ -2064,7 +2064,6 @@ void dlm_move_lockres_to_recovery_list(struct dlm_ctxt *dlm, dlm_lock_get(lock); if (lock->convert_pending) { /* move converting lock back to granted */ - BUG_ON(i != DLM_CONVERTING_LIST); mlog(0, "node died with convert pending " "on %.*s. move back to granted list.\n", res->lockname.len, res->lockname.name); diff --git a/fs/open.c b/fs/open.c index b6f1e96a7c0b..6a24f988d253 100644 --- a/fs/open.c +++ b/fs/open.c @@ -995,14 +995,12 @@ struct file *filp_open(const char *filename, int flags, umode_t mode) EXPORT_SYMBOL(filp_open); struct file *file_open_root(struct dentry *dentry, struct vfsmount *mnt, - const char *filename, int flags) + const char *filename, int flags, umode_t mode) { struct open_flags op; - int err = build_open_flags(flags, 0, &op); + int err = build_open_flags(flags, mode, &op); if (err) return ERR_PTR(err); - if (flags & O_CREAT) - return ERR_PTR(-EINVAL); return do_file_open_root(dentry, mnt, filename, &op); } EXPORT_SYMBOL(file_open_root); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 000b2ed05c29..a1acc6004a91 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -276,6 +276,37 @@ static void ovl_dentry_release(struct dentry *dentry) } } +static struct dentry *ovl_d_real(struct dentry *dentry, struct inode *inode) +{ + struct dentry *real; + + if (d_is_dir(dentry)) { + if (!inode || inode == d_inode(dentry)) + return dentry; + goto bug; + } + + real = ovl_dentry_upper(dentry); + if (real && (!inode || inode == d_inode(real))) + return real; + + real = ovl_dentry_lower(dentry); + if (!real) + goto bug; + + if (!inode || inode == d_inode(real)) + return real; + + /* Handle recursion */ + if (real->d_flags & DCACHE_OP_REAL) + return real->d_op->d_real(real, inode); + +bug: + WARN(1, "ovl_d_real(%pd4, %s:%lu\n): real dentry not found\n", dentry, + inode ? inode->i_sb->s_id : "NULL", inode ? inode->i_ino : 0); + return dentry; +} + static int ovl_dentry_revalidate(struct dentry *dentry, unsigned int flags) { struct ovl_entry *oe = dentry->d_fsdata; @@ -320,11 +351,13 @@ static int ovl_dentry_weak_revalidate(struct dentry *dentry, unsigned int flags) static const struct dentry_operations ovl_dentry_operations = { .d_release = ovl_dentry_release, .d_select_inode = ovl_d_select_inode, + .d_real = ovl_d_real, }; static const struct dentry_operations ovl_reval_dentry_operations = { .d_release = ovl_dentry_release, .d_select_inode = ovl_d_select_inode, + .d_real = ovl_d_real, .d_revalidate = ovl_dentry_revalidate, .d_weak_revalidate = ovl_dentry_weak_revalidate, }; diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index 8ebd9a334085..87645955990d 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c @@ -197,6 +197,8 @@ static int show_vfsstat(struct seq_file *m, struct vfsmount *mnt) if (sb->s_op->show_devname) { seq_puts(m, "device "); err = sb->s_op->show_devname(m, mnt_path.dentry); + if (err) + goto out; } else { if (r->mnt_devname) { seq_puts(m, "device "); diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index ef0d64b2a6d9..353ff31dcee1 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -1398,7 +1398,7 @@ static int dquot_active(const struct inode *inode) static int __dquot_initialize(struct inode *inode, int type) { int cnt, init_needed = 0; - struct dquot **dquots, *got[MAXQUOTAS]; + struct dquot **dquots, *got[MAXQUOTAS] = {}; struct super_block *sb = inode->i_sb; qsize_t rsv; int ret = 0; @@ -1415,7 +1415,6 @@ static int __dquot_initialize(struct inode *inode, int type) int rc; struct dquot *dquot; - got[cnt] = NULL; if (type != -1 && cnt != type) continue; /* diff --git a/fs/splice.c b/fs/splice.c index 4cf700d50b40..0f77e9682857 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -185,6 +185,9 @@ ssize_t splice_to_pipe(struct pipe_inode_info *pipe, unsigned int spd_pages = spd->nr_pages; int ret, do_wakeup, page_nr; + if (!spd_pages) + return 0; + ret = 0; do_wakeup = 0; page_nr = 0; diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c index 0ef7c2ed3f8a..4fa14820e2e2 100644 --- a/fs/xfs/xfs_attr_list.c +++ b/fs/xfs/xfs_attr_list.c @@ -202,8 +202,10 @@ xfs_attr_shortform_list(xfs_attr_list_context_t *context) sbp->namelen, sbp->valuelen, &sbp->name[sbp->namelen]); - if (error) + if (error) { + kmem_free(sbuf); return error; + } if (context->seen_enough) break; cursor->offset++; @@ -454,14 +456,13 @@ xfs_attr3_leaf_list_int( args.rmtblkcnt = xfs_attr3_rmt_blocks( args.dp->i_mount, valuelen); retval = xfs_attr_rmtval_get(&args); - if (retval) - return retval; - retval = context->put_listent(context, - entry->flags, - name_rmt->name, - (int)name_rmt->namelen, - valuelen, - args.value); + if (!retval) + retval = context->put_listent(context, + entry->flags, + name_rmt->name, + (int)name_rmt->namelen, + valuelen, + args.value); kmem_free(args.value); } else { retval = context->put_listent(context, diff --git a/include/asm-generic/bitops/lock.h b/include/asm-generic/bitops/lock.h index c30266e94806..8ef0ccbf8167 100644 --- a/include/asm-generic/bitops/lock.h +++ b/include/asm-generic/bitops/lock.h @@ -29,16 +29,16 @@ do { \ * @nr: the bit to set * @addr: the address to start counting from * - * This operation is like clear_bit_unlock, however it is not atomic. - * It does provide release barrier semantics so it can be used to unlock - * a bit lock, however it would only be used if no other CPU can modify - * any bits in the memory until the lock is released (a good example is - * if the bit lock itself protects access to the other bits in the word). + * A weaker form of clear_bit_unlock() as used by __bit_lock_unlock(). If all + * the bits in the word are protected by this lock some archs can use weaker + * ops to safely unlock. + * + * See for example x86's implementation. */ #define __clear_bit_unlock(nr, addr) \ do { \ - smp_mb(); \ - __clear_bit(nr, addr); \ + smp_mb__before_atomic(); \ + clear_bit(nr, addr); \ } while (0) #endif /* _ASM_GENERIC_BITOPS_LOCK_H_ */ diff --git a/include/asm-generic/preempt.h b/include/asm-generic/preempt.h index 5d8ffa3e6f8c..c1cde3577551 100644 --- a/include/asm-generic/preempt.h +++ b/include/asm-generic/preempt.h @@ -7,10 +7,10 @@ static __always_inline int preempt_count(void) { - return current_thread_info()->preempt_count; + return READ_ONCE(current_thread_info()->preempt_count); } -static __always_inline int *preempt_count_ptr(void) +static __always_inline volatile int *preempt_count_ptr(void) { return ¤t_thread_info()->preempt_count; } diff --git a/include/dt-bindings/clock/msm-clocks-cobalt.h b/include/dt-bindings/clock/msm-clocks-cobalt.h index 47cd73b08a83..28efd55ea8f6 100644 --- a/include/dt-bindings/clock/msm-clocks-cobalt.h +++ b/include/dt-bindings/clock/msm-clocks-cobalt.h @@ -241,7 +241,6 @@ #define clk_gcc_usb30_sleep_clk 0xd0b65c92 #define clk_gcc_usb3_phy_aux_clk 0x0d9a36e0 #define clk_gcc_usb3_phy_pipe_clk 0xf279aff2 -#define clk_gcc_usb_phy_cfg_ahb2phy_clk 0xd1231a0e #define clk_gcc_wcss_ahb_s0_clk 0x639a01c4 #define clk_gcc_wcss_axi_m_clk 0xabc48ebd #define clk_gcc_wcss_ecahb_clk 0xf1815ce9 diff --git a/include/dt-bindings/clock/msm-clocks-hwio-cobalt.h b/include/dt-bindings/clock/msm-clocks-hwio-cobalt.h index 757344602f4a..7ef57256d8f0 100644 --- a/include/dt-bindings/clock/msm-clocks-hwio-cobalt.h +++ b/include/dt-bindings/clock/msm-clocks-hwio-cobalt.h @@ -213,7 +213,6 @@ #define GCC_USB3_PHY_AUX_CBCR 0x50000 #define GCC_USB3_PHY_PIPE_CBCR 0x50004 #define GCC_USB3PHY_PHY_BCR 0x50024 -#define GCC_USB_PHY_CFG_AHB2PHY_CBCR 0x6A004 #define GCC_WCSS_AHB_S0_CBCR 0x11004 #define GCC_WCSS_AXI_M_CBCR 0x11008 #define GCC_WCSS_ECAHB_CBCR 0x1100C diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index a702c042b716..e63d3a513e67 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -216,6 +216,9 @@ struct css_set { /* all css_task_iters currently walking this cset */ struct list_head task_iters; + /* dead and being drained, ignore for migration */ + bool dead; + /* For RCU-protected deletion */ struct rcu_head rcu_head; }; diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h index 22ab246feed3..eeae401a2412 100644 --- a/include/linux/compiler-gcc.h +++ b/include/linux/compiler-gcc.h @@ -199,7 +199,7 @@ #define unreachable() __builtin_unreachable() /* Mark a function definition as prohibited from being cloned. */ -#define __noclone __attribute__((__noclone__)) +#define __noclone __attribute__((__noclone__, __optimize__("no-tracer"))) #endif /* GCC_VERSION >= 40500 */ diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 8a2e009c8a5a..f513dd855cb2 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -161,6 +161,7 @@ struct dentry_operations { struct vfsmount *(*d_automount)(struct path *); int (*d_manage)(struct dentry *, bool); struct inode *(*d_select_inode)(struct dentry *, unsigned); + struct dentry *(*d_real)(struct dentry *, struct inode *); } ____cacheline_aligned; /* @@ -227,6 +228,7 @@ struct dentry_operations { #define DCACHE_MAY_FREE 0x00800000 #define DCACHE_FALLTHRU 0x01000000 /* Fall through to lower layer */ #define DCACHE_OP_SELECT_INODE 0x02000000 /* Unioned entry: dcache op selects inode */ +#define DCACHE_OP_REAL 0x08000000 extern seqlock_t rename_lock; @@ -582,4 +584,12 @@ static inline struct dentry *d_backing_dentry(struct dentry *upper) return upper; } +static inline struct dentry *d_real(struct dentry *dentry) +{ + if (unlikely(dentry->d_flags & DCACHE_OP_REAL)) + return dentry->d_op->d_real(dentry, NULL); + else + return dentry; +} + #endif /* __LINUX_DCACHE_H */ diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 2151ebfe0902..010b5ad2ac5d 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -124,6 +124,8 @@ struct dm_dev { char name[16]; }; +dev_t dm_get_dev_t(const char *path); + /* * Constructors should call these functions to ensure destination devices * are opened/closed correctly. diff --git a/include/linux/filter.h b/include/linux/filter.h index 5972ffe5719a..5110d4211866 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -446,8 +446,12 @@ int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog, void bpf_prog_destroy(struct bpf_prog *fp); int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); +int __sk_attach_filter(struct sock_fprog *fprog, struct sock *sk, + bool locked); int sk_attach_bpf(u32 ufd, struct sock *sk); int sk_detach_filter(struct sock *sk); +int __sk_detach_filter(struct sock *sk, bool locked); + int sk_get_filter(struct sock *sk, struct sock_filter __user *filter, unsigned int len); diff --git a/include/linux/fs.h b/include/linux/fs.h index 522b582e61c7..cc2796b2486f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1214,6 +1214,16 @@ static inline struct inode *file_inode(const struct file *f) return f->f_inode; } +static inline struct dentry *file_dentry(const struct file *file) +{ + struct dentry *dentry = file->f_path.dentry; + + if (unlikely(dentry->d_flags & DCACHE_OP_REAL)) + return dentry->d_op->d_real(dentry, file_inode(file)); + else + return dentry; +} + static inline int locks_lock_file_wait(struct file *filp, struct file_lock *fl) { return locks_lock_inode_wait(file_inode(filp), fl); @@ -2224,7 +2234,7 @@ extern long do_sys_open(int dfd, const char __user *filename, int flags, extern struct file *file_open_name(struct filename *, int, umode_t); extern struct file *filp_open(const char *, int, umode_t); extern struct file *file_open_root(struct dentry *, struct vfsmount *, - const char *, int); + const char *, int, umode_t); extern struct file * dentry_open(const struct path *, int, const struct cred *); extern int filp_close(struct file *, fl_owner_t id); diff --git a/include/linux/hdcp_qseecom.h b/include/linux/hdcp_qseecom.h index d9835193961e..26e97700fc73 100644 --- a/include/linux/hdcp_qseecom.h +++ b/include/linux/hdcp_qseecom.h @@ -108,9 +108,15 @@ struct hdcp_client_ops { int (*wakeup)(struct hdmi_hdcp_wakeup_data *data); }; +enum hdcp_device_type { + HDCP_TXMTR_HDMI = 0x8001, + HDCP_TXMTR_DP = 0x8002 +}; + struct hdcp_register_data { struct hdcp_client_ops *client_ops; struct hdcp_txmtr_ops *txmtr_ops; + enum hdcp_device_type device_type; void *client_ctx; void **hdcp_ctx; bool tethered; diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index a338a688ee4a..dcb89e3515db 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -46,10 +46,6 @@ struct br_ip_list { #define BR_LEARNING_SYNC BIT(9) #define BR_PROXYARP_WIFI BIT(10) -/* values as per ieee8021QBridgeFdbAgingTime */ -#define BR_MIN_AGEING_TIME (10 * HZ) -#define BR_MAX_AGEING_TIME (1000000 * HZ) - #define BR_DEFAULT_AGEING_TIME (300 * HZ) extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __user *)); diff --git a/include/net/activity_stats.h b/include/linux/input/ft5x06_ts.h index 10e4c1506eeb..a9577b62cb07 100644 --- a/include/net/activity_stats.h +++ b/include/linux/input/ft5x06_ts.h @@ -1,5 +1,9 @@ /* - * Copyright (C) 2010 Google, Inc. + * + * FocalTech ft5x06 TouchScreen driver header file. + * + * Copyright (c) 2010 Focal tech Ltd. + * Copyright (c) 2012, Code Aurora Forum. 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 @@ -10,16 +14,18 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * Author: Mike Chan (mike@android.com) */ +#ifndef __LINUX_FT5X06_TS_H__ +#define __LINUX_FT5X06_TS_H__ -#ifndef __activity_stats_h -#define __activity_stats_h +struct ft5x06_ts_platform_data { + unsigned long irqflags; + u32 x_max; + u32 y_max; + u32 irq_gpio; + u32 reset_gpio; + int (*power_init)(bool); + int (*power_on)(bool); +}; -#ifdef CONFIG_NET_ACTIVITY_STATS -void activity_stats_update(void); -#else -#define activity_stats_update(void) {} #endif - -#endif /* _NET_ACTIVITY_STATS_H */ diff --git a/include/linux/ipa.h b/include/linux/ipa.h index d152057af385..5f85508353c9 100644 --- a/include/linux/ipa.h +++ b/include/linux/ipa.h @@ -1096,6 +1096,11 @@ int ipa_reset_endpoint(u32 clnt_hdl); int ipa_clear_endpoint_delay(u32 clnt_hdl); /* + * Disable ep + */ +int ipa_disable_endpoint(u32 clnt_hdl); + +/* * Configuration */ int ipa_cfg_ep(u32 clnt_hdl, const struct ipa_ep_cfg *ipa_ep_cfg); @@ -1462,6 +1467,14 @@ static inline int ipa_clear_endpoint_delay(u32 clnt_hdl) } /* + * Disable ep + */ +static inline int ipa_disable_endpoint(u32 clnt_hdl) +{ + return -EPERM; +} + +/* * Configuration */ static inline int ipa_cfg_ep(u32 clnt_hdl, diff --git a/include/linux/ipa_usb.h b/include/linux/ipa_usb.h index c3885c72e5ea..0fe0e36c551f 100644 --- a/include/linux/ipa_usb.h +++ b/include/linux/ipa_usb.h @@ -94,7 +94,7 @@ struct ipa_usb_xdci_connect_params { * ipa_usb_xdci_chan_scratch - xDCI protocol SW config area of * channel scratch * - * @last_trb_addr: Address (LSB - based on alignment restrictions) of + * @last_trb_addr_iova: Address (iova LSB - based on alignment restrictions) of * last TRB in queue. Used to identify roll over case * @const_buffer_size: TRB buffer size in KB (similar to IPA aggregation * configuration). Must be aligned to max USB Packet Size. @@ -103,7 +103,7 @@ struct ipa_usb_xdci_connect_params { * @depcmd_hi_addr: Used to generate "Update Transfer" command. */ struct ipa_usb_xdci_chan_scratch { - u16 last_trb_addr; + u16 last_trb_addr_iova; u8 const_buffer_size; u32 depcmd_low_addr; u8 depcmd_hi_addr; @@ -124,6 +124,11 @@ struct ipa_usb_xdci_chan_scratch { * @xfer_ring_base_addr: physical base address of transfer ring. Address must be * aligned to xfer_ring_len rounded to power of two * @xfer_scratch: parameters for xDCI channel scratch + * @xfer_ring_base_addr_iova: IO virtual address mapped to xfer_ring_base_addr + * @data_buff_base_len: length of data buffer allocated by USB driver + * @data_buff_base_addr: physical base address for the data buffer (where TRBs + * points) + * @data_buff_base_addr_iova: IO virtual address mapped to data_buff_base_addr * */ struct ipa_usb_xdci_chan_params { @@ -140,6 +145,10 @@ struct ipa_usb_xdci_chan_params { u16 xfer_ring_len; u64 xfer_ring_base_addr; struct ipa_usb_xdci_chan_scratch xfer_scratch; + u64 xfer_ring_base_addr_iova; + u32 data_buff_base_len; + u64 data_buff_base_addr; + u64 data_buff_base_addr_iova; }; /** diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 1eaf3d81f5c1..2955e672391d 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -607,7 +607,7 @@ do { \ #define do_trace_printk(fmt, args...) \ do { \ - static const char *trace_printk_fmt \ + static const char *trace_printk_fmt __used \ __attribute__((section("__trace_printk_fmt"))) = \ __builtin_constant_p(fmt) ? fmt : NULL; \ \ @@ -651,7 +651,7 @@ int __trace_printk(unsigned long ip, const char *fmt, ...); */ #define trace_puts(str) ({ \ - static const char *trace_printk_fmt \ + static const char *trace_printk_fmt __used \ __attribute__((section("__trace_printk_fmt"))) = \ __builtin_constant_p(str) ? str : NULL; \ \ @@ -673,7 +673,7 @@ extern void trace_dump_stack(int skip); #define ftrace_vprintk(fmt, vargs) \ do { \ if (__builtin_constant_p(fmt)) { \ - static const char *trace_printk_fmt \ + static const char *trace_printk_fmt __used \ __attribute__((section("__trace_printk_fmt"))) = \ __builtin_constant_p(fmt) ? fmt : NULL; \ \ diff --git a/include/linux/leds-qpnp-flash-v2.h b/include/linux/leds-qpnp-flash-v2.h index 47fd0699a9c1..1ae77e2e277b 100644 --- a/include/linux/leds-qpnp-flash-v2.h +++ b/include/linux/leds-qpnp-flash-v2.h @@ -14,11 +14,21 @@ #define __LEDS_QPNP_FLASH_V2_H #include <linux/leds.h> -#include "leds.h" +#include <linux/notifier.h> -#define ENABLE_REGULATOR BIT(0) -#define QUERY_MAX_CURRENT BIT(1) +enum flash_led_irq_type { + LED_FAULT_IRQ = BIT(0), + MITIGATION_IRQ = BIT(1), + FLASH_TIMER_EXP_IRQ = BIT(2), + ALL_RAMP_DOWN_DONE_IRQ = BIT(3), + ALL_RAMP_UP_DONE_IRQ = BIT(4), + LED3_RAMP_UP_DONE_IRQ = BIT(5), + LED2_RAMP_UP_DONE_IRQ = BIT(6), + LED1_RAMP_UP_DONE_IRQ = BIT(7), + INVALID_IRQ = BIT(8), +}; -int qpnp_flash_led_prepare(struct led_classdev *led_cdev, int options); +int qpnp_flash_led_register_irq_notifier(struct notifier_block *nb); +int qpnp_flash_led_unregister_irq_notifier(struct notifier_block *nb); #endif diff --git a/include/linux/leds-qpnp-flash.h b/include/linux/leds-qpnp-flash.h new file mode 100644 index 000000000000..55867e78bba6 --- /dev/null +++ b/include/linux/leds-qpnp-flash.h @@ -0,0 +1,23 @@ +/* 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. + */ + +#ifndef __LEDS_QPNP_FLASH_H +#define __LEDS_QPNP_FLASH_H + +#include <linux/leds.h> + +#define ENABLE_REGULATOR BIT(0) +#define QUERY_MAX_CURRENT BIT(1) + +int qpnp_flash_led_prepare(struct led_trigger *trig, int options); + +#endif diff --git a/include/linux/mfd/wcd934x/registers.h b/include/linux/mfd/wcd934x/registers.h index 871bf6a778b1..085e16d66bc4 100644 --- a/include/linux/mfd/wcd934x/registers.h +++ b/include/linux/mfd/wcd934x/registers.h @@ -33,6 +33,7 @@ enum { WCD934X_PAGE_13, WCD934X_PAGE_14, WCD934X_PAGE_15, + WCD934X_PAGE_0x50, WCD934X_PAGE_0X80, }; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b222d86b501f..acf302ef2f01 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -265,6 +265,7 @@ struct header_ops { void (*cache_update)(struct hh_cache *hh, const struct net_device *dev, const unsigned char *haddr); + bool (*validate)(const char *ll_header, unsigned int len); }; /* These flag bits are private to the generic network queueing @@ -1398,8 +1399,7 @@ enum netdev_priv_flags { * @dma: DMA channel * @mtu: Interface MTU value * @type: Interface hardware type - * @hard_header_len: Hardware header length, which means that this is the - * minimum size of a packet. + * @hard_header_len: Maximum hardware header length. * * @needed_headroom: Extra headroom the hardware may need, but not in all * cases can this be guaranteed @@ -2493,6 +2493,24 @@ static inline int dev_parse_header(const struct sk_buff *skb, return dev->header_ops->parse(skb, haddr); } +/* ll_header must have at least hard_header_len allocated */ +static inline bool dev_validate_header(const struct net_device *dev, + char *ll_header, int len) +{ + if (likely(len >= dev->hard_header_len)) + return true; + + if (capable(CAP_SYS_RAWIO)) { + memset(ll_header + len, 0, dev->hard_header_len - len); + return true; + } + + if (dev->header_ops && dev->header_ops->validate) + return dev->header_ops->validate(ll_header, len); + + return false; +} + typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr, int len); int register_gifconf(unsigned int family, gifconf_func_t *gifconf); static inline int unregister_gifconf(unsigned int family) diff --git a/include/linux/of_batterydata.h b/include/linux/of_batterydata.h index fe2c996de264..5505371488d0 100644 --- a/include/linux/of_batterydata.h +++ b/include/linux/of_batterydata.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014, 2016 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -39,10 +39,7 @@ int of_batterydata_read_data(struct device_node *container_node, * of_batterydata_get_best_profile() - Find matching battery data device node * @batterydata_container_node: pointer to the battery-data container device * node containing the profile nodes. - * @psy_name: Name of the power supply which holds the - * POWER_SUPPLY_RESISTANCE_ID value to be used to match - * against the id resistances specified in the corresponding - * battery data profiles. + * @batt_id_kohm: Battery ID in KOhms for which we want to find the profile. * @batt_type: Battery type which we want to force load the profile. * * This routine returns a device_node pointer to the closest match battery data @@ -50,7 +47,7 @@ int of_batterydata_read_data(struct device_node *container_node, */ struct device_node *of_batterydata_get_best_profile( struct device_node *batterydata_container_node, - const char *psy_name, const char *batt_type); + int batt_id_kohm, const char *batt_type); #else static inline int of_batterydata_read_data(struct device_node *container_node, struct bms_battery_data *batt_data, @@ -60,7 +57,7 @@ static inline int of_batterydata_read_data(struct device_node *container_node, } static inline struct device_node *of_batterydata_get_best_profile( struct device_node *batterydata_container_node, - struct device_node *best_node, const char *psy_name) + int batt_id_kohm, const char *batt_type) { return -ENXIO; } diff --git a/include/linux/pci.h b/include/linux/pci.h index 6ae25aae88fd..e89c7ee7e803 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -359,6 +359,7 @@ struct pci_dev { unsigned int io_window_1k:1; /* Intel P2P bridge 1K I/O windows */ unsigned int irq_managed:1; unsigned int has_secondary_link:1; + unsigned int non_compliant_bars:1; /* broken BARs; ignore them */ pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ @@ -988,23 +989,6 @@ static inline int pci_is_managed(struct pci_dev *pdev) return pdev->is_managed; } -static inline void pci_set_managed_irq(struct pci_dev *pdev, unsigned int irq) -{ - pdev->irq = irq; - pdev->irq_managed = 1; -} - -static inline void pci_reset_managed_irq(struct pci_dev *pdev) -{ - pdev->irq = 0; - pdev->irq_managed = 0; -} - -static inline bool pci_has_managed_irq(struct pci_dev *pdev) -{ - return pdev->irq_managed && pdev->irq > 0; -} - void pci_disable_device(struct pci_dev *dev); extern unsigned int pcibios_max_latency; diff --git a/include/linux/platform_data/asoc-s3c.h b/include/linux/platform_data/asoc-s3c.h index 5e0bc779e6c5..33f88b4479e4 100644 --- a/include/linux/platform_data/asoc-s3c.h +++ b/include/linux/platform_data/asoc-s3c.h @@ -39,6 +39,10 @@ struct samsung_i2s { */ struct s3c_audio_pdata { int (*cfg_gpio)(struct platform_device *); + void *dma_playback; + void *dma_capture; + void *dma_play_sec; + void *dma_capture_mic; union { struct samsung_i2s i2s; } type; diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 9a2e50337af9..cccaf4a29e9f 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -34,6 +34,8 @@ bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp); int dev_pm_opp_get_opp_count(struct device *dev); unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev); +unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev); +unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev); struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev); struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, @@ -55,6 +57,14 @@ int dev_pm_opp_enable(struct device *dev, unsigned long freq); int dev_pm_opp_disable(struct device *dev, unsigned long freq); struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev); +int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, + unsigned int count); +void dev_pm_opp_put_supported_hw(struct device *dev); +int dev_pm_opp_set_prop_name(struct device *dev, const char *name); +void dev_pm_opp_put_prop_name(struct device *dev); +int dev_pm_opp_set_regulator(struct device *dev, const char *name); +void dev_pm_opp_put_regulator(struct device *dev); +int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); #else static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) { @@ -81,6 +91,16 @@ static inline unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev) return 0; } +static inline unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) +{ + return 0; +} + +static inline unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev) +{ + return 0; +} + static inline struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev) { return NULL; @@ -129,6 +149,35 @@ static inline struct srcu_notifier_head *dev_pm_opp_get_notifier( { return ERR_PTR(-EINVAL); } + +static inline int dev_pm_opp_set_supported_hw(struct device *dev, + const u32 *versions, + unsigned int count) +{ + return -EINVAL; +} + +static inline void dev_pm_opp_put_supported_hw(struct device *dev) {} + +static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name) +{ + return -EINVAL; +} + +static inline void dev_pm_opp_put_prop_name(struct device *dev) {} + +static inline int dev_pm_opp_set_regulator(struct device *dev, const char *name) +{ + return -EINVAL; +} + +static inline void dev_pm_opp_put_regulator(struct device *dev) {} + +static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) +{ + return -EINVAL; +} + #endif /* CONFIG_PM_OPP */ #if defined(CONFIG_PM_OPP) && defined(CONFIG_OF) diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index b081a56e250f..7d1e374e176c 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -214,6 +214,8 @@ enum power_supply_property { POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, POWER_SUPPLY_PROP_PD_ALLOWED, POWER_SUPPLY_PROP_PD_ACTIVE, + POWER_SUPPLY_PROP_CHARGER_TEMP, + POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, /* Local extensions of type int64_t */ POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT, /* Properties of type `const char *' */ diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h index 8d51ddcd4246..af25f0c01369 100644 --- a/include/linux/qpnp/qpnp-adc.h +++ b/include/linux/qpnp/qpnp-adc.h @@ -242,6 +242,7 @@ enum qpnp_iadc_channels { #define QPNP_ADC_HWMON_NAME_LENGTH 64 #define QPNP_MAX_PROP_NAME_LEN 32 #define QPNP_THERMALNODE_NAME_LENGTH 25 +#define QPNP_ADC_1P25_UV 1250000 /* Structure device for qpnp vadc */ struct qpnp_vadc_chip; @@ -950,6 +951,7 @@ enum qpnp_state_request { * @low_temp: Low temperature threshold for which notification is requested. * @high_thr_voltage: High voltage for which notification is requested. * @low_thr_voltage: Low voltage for which notification is requested. + * @adc_tm_hc: Represents the refreshed BTM register design. * @state_request: Enable/disable the corresponding high and low temperature * thresholds. * @timer_interval1: Select polling rate from qpnp_adc_meas_timer_1 type. @@ -972,6 +974,7 @@ struct qpnp_adc_tm_btm_param { int32_t low_thr; int32_t gain_num; int32_t gain_den; + bool adc_tm_hc; enum qpnp_vadc_channels channel; enum qpnp_state_request state_request; enum qpnp_adc_meas_timer_1 timer_interval; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 75f136a22a5e..4fde61804191 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1908,6 +1908,30 @@ static inline void skb_reserve(struct sk_buff *skb, int len) skb->tail += len; } +/** + * skb_tailroom_reserve - adjust reserved_tailroom + * @skb: buffer to alter + * @mtu: maximum amount of headlen permitted + * @needed_tailroom: minimum amount of reserved_tailroom + * + * Set reserved_tailroom so that headlen can be as large as possible but + * not larger than mtu and tailroom cannot be smaller than + * needed_tailroom. + * The required headroom should already have been reserved before using + * this function. + */ +static inline void skb_tailroom_reserve(struct sk_buff *skb, unsigned int mtu, + unsigned int needed_tailroom) +{ + SKB_LINEAR_ASSERT(skb); + if (mtu < skb_tailroom(skb) - needed_tailroom) + /* use at most mtu */ + skb->reserved_tailroom = skb_tailroom(skb) - mtu; + else + /* use up to all available space */ + skb->reserved_tailroom = needed_tailroom; +} + #define ENCAP_TYPE_ETHER 0 #define ENCAP_TYPE_IPPROTO 1 @@ -2724,6 +2748,23 @@ static inline void skb_postpull_rcsum(struct sk_buff *skb, unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len); +static inline void skb_postpush_rcsum(struct sk_buff *skb, + const void *start, unsigned int len) +{ + /* For performing the reverse operation to skb_postpull_rcsum(), + * we can instead of ... + * + * skb->csum = csum_add(skb->csum, csum_partial(start, len, 0)); + * + * ... just use this equivalent version here to save a few + * instructions. Feeding csum of 0 in csum_partial() and later + * on adding skb->csum is equivalent to feed skb->csum in the + * first place. + */ + if (skb->ip_summed == CHECKSUM_COMPLETE) + skb->csum = csum_partial(start, len, skb->csum); +} + /** * pskb_trim_rcsum - trim received skb and update checksum * @skb: buffer to trim diff --git a/include/linux/thermal.h b/include/linux/thermal.h index a81516c611c4..b90f8f5c663d 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -190,6 +190,7 @@ struct sensor_info { * @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis * @devdata: private pointer for device private data * @trips: number of trip points the thermal zone supports + * @trips_disabled; bitmap for disabled trips * @passive_delay: number of milliseconds to wait between polls when * performing passive cooling. * @polling_delay: number of milliseconds to wait between polls when @@ -225,6 +226,7 @@ struct thermal_zone_device { struct thermal_attr *trip_hyst_attrs; void *devdata; int trips; + unsigned long trips_disabled; /* bitmap for disabled trips */ int passive_delay; int polling_delay; int temperature; diff --git a/include/linux/tty.h b/include/linux/tty.h index 6b6e811f4575..3bf03b6b52e9 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -594,7 +594,7 @@ static inline int tty_ldisc_receive_buf(struct tty_ldisc *ld, unsigned char *p, count = ld->ops->receive_buf2(ld->tty, p, f, count); else { count = min_t(int, count, ld->tty->receive_room); - if (count) + if (count && ld->ops->receive_buf) ld->ops->receive_buf(ld->tty, p, f, count); } return count; diff --git a/include/linux/uid_stat.h b/include/linux/uid_stat.h deleted file mode 100644 index 6bd6c4e52d17..000000000000 --- a/include/linux/uid_stat.h +++ /dev/null @@ -1,29 +0,0 @@ -/* include/linux/uid_stat.h - * - * Copyright (C) 2008-2009 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#ifndef __uid_stat_h -#define __uid_stat_h - -/* Contains definitions for resource tracking per uid. */ - -#ifdef CONFIG_UID_STAT -int uid_stat_tcp_snd(uid_t uid, int size); -int uid_stat_tcp_rcv(uid_t uid, int size); -#else -#define uid_stat_tcp_snd(uid, size) do {} while (0); -#define uid_stat_tcp_rcv(uid, size) do {} while (0); -#endif - -#endif /* _LINUX_UID_STAT_H */ diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 89effb61d153..44b6222db9a3 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -176,6 +176,8 @@ struct msm_usb_cable { #define DEVICE_IN_SS_MODE BIT(5) #define PHY_LANE_A BIT(6) #define PHY_LANE_B BIT(7) +#define PHY_HSFS_MODE BIT(8) +#define PHY_LS_MODE BIT(9) #define USB_NUM_BUS_CLOCKS 3 diff --git a/include/linux/usb/usbpd.h b/include/linux/usb/usbpd.h new file mode 100644 index 000000000000..c2c1025feb8e --- /dev/null +++ b/include/linux/usb/usbpd.h @@ -0,0 +1,156 @@ +/* Copyright (c) 2016, Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LINUX_USB_USBPD_H +#define __LINUX_USB_USBPD_H + +#include <linux/list.h> + +struct usbpd; + +/* Standard IDs */ +#define USBPD_SID 0xff00 + +/* Structured VDM Command Type */ +enum usbpd_svdm_cmd_type { + SVDM_CMD_TYPE_INITIATOR, + SVDM_CMD_TYPE_RESP_ACK, + SVDM_CMD_TYPE_RESP_NAK, + SVDM_CMD_TYPE_RESP_BUSY, +}; + +/* Structured VDM Commands */ +#define USBPD_SVDM_DISCOVER_IDENTITY 0x1 +#define USBPD_SVDM_DISCOVER_SVIDS 0x2 +#define USBPD_SVDM_DISCOVER_MODES 0x3 +#define USBPD_SVDM_ENTER_MODE 0x4 +#define USBPD_SVDM_EXIT_MODE 0x5 +#define USBPD_SVDM_ATTENTION 0x6 + +/* + * Implemented by client + */ +struct usbpd_svid_handler { + u16 svid; + + void (*connect)(struct usbpd_svid_handler *hdlr); + void (*disconnect)(struct usbpd_svid_handler *hdlr); + + /* Unstructured VDM */ + void (*vdm_received)(struct usbpd_svid_handler *hdlr, u32 vdm_hdr, + const u32 *vdos, int num_vdos); + + /* Structured VDM */ + void (*svdm_received)(struct usbpd_svid_handler *hdlr, u8 cmd, + enum usbpd_svdm_cmd_type cmd_type, const u32 *vdos, + int num_vdos); + + struct list_head entry; +}; + +enum plug_orientation { + ORIENTATION_NONE, + ORIENTATION_CC1, + ORIENTATION_CC2, +}; + +#if IS_ENABLED(CONFIG_USB_PD_POLICY) +/* + * Obtains an instance of usbpd from a DT phandle + */ +struct usbpd *devm_usbpd_get_by_phandle(struct device *dev, + const char *phandle); + +/* + * Called by client to handle specific SVID messages. + * Specify callback functions in the usbpd_svid_handler argument + */ +int usbpd_register_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr); + +void usbpd_unregister_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr); + +/* + * Transmit a VDM message. + */ +int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, + int num_vdos); + +/* + * Transmit a Structured VDM message. + */ +int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd, + enum usbpd_svdm_cmd_type cmd_type, int obj_pos, + const u32 *vdos, int num_vdos); + +/* + * Get current status of CC pin orientation. + * + * Return: ORIENTATION_CC1 or ORIENTATION_CC2 if attached, + * otherwise ORIENTATION_NONE if not attached + */ +enum plug_orientation usbpd_get_plug_orientation(struct usbpd *pd); +#else +static inline struct usbpd *devm_usbpd_get_by_phandle(struct device *dev, + const char *phandle) +{ + return ERR_PTR(-ENODEV); +} + +static inline int usbpd_register_svid(struct usbpd *pd, + struct usbpd_svid_handler *hdlr) +{ + return -EINVAL; +} + +static inline void usbpd_unregister_svid(struct usbpd *pd, + struct usbpd_svid_handler *hdlr) +{ +} + +static inline int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, + int num_vdos) +{ + return -EINVAL; +} + +static inline int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd, + enum usbpd_svdm_cmd_type cmd_type, int obj_pos, + const u32 *vdos, int num_vdos) +{ + return -EINVAL; +} + +static inline enum plug_orientation usbpd_get_plug_orientation(struct usbpd *pd) +{ + return ORIENTATION_NONE; +} +#endif /* IS_ENABLED(CONFIG_USB_PD_POLICY) */ + +/* + * Additional helpers for Enter/Exit Mode commands + */ + +static inline int usbpd_enter_mode(struct usbpd *pd, u16 svid, int mode, + const u32 *vdo) +{ + return usbpd_send_svdm(pd, svid, USBPD_SVDM_ENTER_MODE, + SVDM_CMD_TYPE_INITIATOR, mode, vdo, vdo ? 1 : 0); +} + +static inline int usbpd_exit_mode(struct usbpd *pd, u16 svid, int mode, + const u32 *vdo) +{ + return usbpd_send_svdm(pd, svid, USBPD_SVDM_EXIT_MODE, + SVDM_CMD_TYPE_INITIATOR, mode, vdo, vdo ? 1 : 0); +} + +#endif /* __LINUX_USB_USBPD_H */ diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h index 7f5f78bd15ad..245f57dbbb61 100644 --- a/include/linux/usb_usual.h +++ b/include/linux/usb_usual.h @@ -79,6 +79,8 @@ /* Cannot handle MI_REPORT_SUPPORTED_OPERATION_CODES */ \ US_FLAG(MAX_SECTORS_240, 0x08000000) \ /* Sets max_sectors to 240 */ \ + US_FLAG(NO_REPORT_LUNS, 0x10000000) \ + /* Cannot handle REPORT_LUNS */ \ #define US_FLAG(name, value) US_FL_##name = value , enum { US_DO_ALL_FLAGS }; diff --git a/include/net/bonding.h b/include/net/bonding.h index c1740a2794a3..93abe5f6188d 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -214,6 +214,7 @@ struct bonding { * ALB mode (6) - to sync the use and modifications of its hash table */ spinlock_t mode_lock; + spinlock_t stats_lock; u8 send_peer_notif; u8 igmp_retrans; #ifdef CONFIG_PROC_FS diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 63568caf0de8..e0b0d2b12b88 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3042,6 +3042,24 @@ struct wiphy_vendor_command { }; /** + * struct wiphy_iftype_ext_capab - extended capabilities per interface type + * @iftype: interface type + * @extended_capabilities: extended capabilities supported by the driver, + * additional capabilities might be supported by userspace; these are the + * 802.11 extended capabilities ("Extended Capabilities element") and are + * in the same format as in the information element. See IEEE Std + * 802.11-2012 8.4.2.29 for the defined fields. + * @extended_capabilities_mask: mask of the valid values + * @extended_capabilities_len: length of the extended capabilities + */ +struct wiphy_iftype_ext_capab { + enum nl80211_iftype iftype; + const u8 *extended_capabilities; + const u8 *extended_capabilities_mask; + u8 extended_capabilities_len; +}; + +/** * struct wiphy - wireless hardware description * @reg_notifier: the driver's regulatory notification callback, * note that if your driver uses wiphy_apply_custom_regulatory() @@ -3158,9 +3176,14 @@ struct wiphy_vendor_command { * additional capabilities might be supported by userspace; these are * the 802.11 extended capabilities ("Extended Capabilities element") * and are in the same format as in the information element. See - * 802.11-2012 8.4.2.29 for the defined fields. + * 802.11-2012 8.4.2.29 for the defined fields. These are the default + * extended capabilities to be used if the capabilities are not specified + * for a specific interface type in iftype_ext_capab. * @extended_capabilities_mask: mask of the valid values * @extended_capabilities_len: length of the extended capabilities + * @iftype_ext_capab: array of extended capabilities per interface type + * @num_iftype_ext_capab: number of interface types for which extended + * capabilities are specified separately. * @coalesce: packet coalescing support information * * @vendor_commands: array of vendor commands supported by the hardware @@ -3257,6 +3280,9 @@ struct wiphy { const u8 *extended_capabilities, *extended_capabilities_mask; u8 extended_capabilities_len; + const struct wiphy_iftype_ext_capab *iftype_ext_capab; + unsigned int num_iftype_ext_capab; + /* If multiple wiphys are registered and you're handed e.g. * a regular netdev with assigned ieee80211_ptr, you won't * know whether it points to a wiphy your driver has registered diff --git a/include/net/xfrm.h b/include/net/xfrm.h index d6f6e5006ee9..d18cbafc3455 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -285,11 +285,10 @@ struct xfrm_policy_afinfo { unsigned short family; struct dst_ops *dst_ops; void (*garbage_collect)(struct net *net); - struct dst_entry *(*dst_lookup)(struct net *net, - int tos, int oif, + struct dst_entry *(*dst_lookup)(struct net *net, int tos, const xfrm_address_t *saddr, const xfrm_address_t *daddr); - int (*get_saddr)(struct net *net, int oif, + int (*get_saddr)(struct net *net, xfrm_address_t *saddr, xfrm_address_t *daddr); void (*decode_session)(struct sk_buff *skb, diff --git a/include/soc/qcom/camera2.h b/include/soc/qcom/camera2.h index 6e1aac3ff05a..bf9db17e6981 100644 --- a/include/soc/qcom/camera2.h +++ b/include/soc/qcom/camera2.h @@ -147,6 +147,7 @@ struct msm_camera_sensor_board_info { const char *eeprom_name; const char *actuator_name; const char *ois_name; + const char *flash_name; struct msm_camera_slave_info *slave_info; struct msm_camera_csi_lane_params *csi_lane_params; struct msm_camera_sensor_strobe_flash_data *strobe_flash_data; diff --git a/include/soc/qcom/service-locator.h b/include/soc/qcom/service-locator.h index be1a2b431dd9..6bf8ac0be15f 100644 --- a/include/soc/qcom/service-locator.h +++ b/include/soc/qcom/service-locator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * 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 @@ -51,16 +51,22 @@ struct pd_qmi_client_data { struct servreg_loc_entry_v01 *domain_list; }; +enum service_locator_state { + LOCATOR_DOWN = 0x0F, + LOCATOR_UP = 0x1F, +}; + #if defined(CONFIG_MSM_SERVICE_LOCATOR) /* - * Use this api to request information regarding the process domains on which - * a particular service runs. The client name and the service name inside the - * pd_qmi_client_data structure need to be filled in by the client calling the - * api. The total domains, db revision and the domain list will be filled in + * Use this api to request information regarding the process domains on + * which a particular service runs. The client name, the service name + * and notifier block pointer need to be provided by client calling the api. + * The total domains, db revision and the domain list will be filled in * by the service locator. * Returns 0 on success; otherwise a value < 0 if no valid subsystem is found. */ -int get_service_location(struct pd_qmi_client_data *data); +int get_service_location(char *client_name, char *service_name, + struct notifier_block *locator_nb); /* * Use this api to request information regarding the subsystem the process @@ -73,9 +79,10 @@ int find_subsys(const char *pd_path, char *subsys); #else -static inline int get_service_location(struct pd_qmi_client_data *data) +static inline int get_service_location(char *client_name, + char *service_name, struct notifier_block *locator_nb); { - return 0; + return -ENODEV; } static inline int find_subsys(const char *pd_path, const char *subsys) diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h index 9295532dec8a..79bcc1b31cf8 100644 --- a/include/soc/qcom/smem.h +++ b/include/soc/qcom/smem.h @@ -26,6 +26,7 @@ enum { SMEM_TZ, SMEM_SPSS, SMEM_HYP, + SMEM_CDSP, NUM_SMEM_SUBSYSTEMS, }; diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h index 39527ed83fa7..478243712d07 100644 --- a/include/soc/qcom/socinfo.h +++ b/include/soc/qcom/socinfo.h @@ -90,6 +90,8 @@ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8929") #define early_machine_is_msmcobalt() \ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmcobalt") +#define early_machine_is_apqcobalt() \ + of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,apqcobalt") #define early_machine_is_msmhamster() \ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmhamster") #define early_machine_is_msmfalcon() \ @@ -128,6 +130,7 @@ #define early_machine_is_msm8976() 0 #define early_machine_is_msm8929() 0 #define early_machine_is_msmcobalt() 0 +#define early_machine_is_apqcobalt() 0 #define early_machine_is_msmhamster() 0 #define early_machine_is_msmfalcon() 0 #endif diff --git a/include/sound/cpe_cmi.h b/include/sound/cpe_cmi.h index 5ce52ae912d4..cbf83e7a7e91 100644 --- a/include/sound/cpe_cmi.h +++ b/include/sound/cpe_cmi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, Linux Foundation. All rights reserved. + * Copyright (c) 2014-2016, Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -17,6 +17,7 @@ #include <linux/types.h> #define CPE_AFE_PORT_1_TX 1 +#define CPE_AFE_PORT_3_TX 3 #define CPE_AFE_PORT_ID_2_OUT 0x02 #define CMI_INBAND_MESSAGE_SIZE 127 @@ -80,6 +81,7 @@ #define CPE_LSM_PARAM_ID_LAB_CONFIG 0x00012C0A #define CPE_LSM_PARAM_ID_REGISTER_SOUND_MODEL (0x00012C14) #define CPE_LSM_PARAM_ID_DEREGISTER_SOUND_MODEL (0x00012C15) +#define CPE_LSM_PARAM_ID_MEDIA_FMT (0x00012C1E) /* AFE Service command opcodes */ #define CPE_AFE_PORT_CMD_START (0x1001) @@ -125,7 +127,7 @@ enum { CMI_CPE_SERVICE_ID_MAX, }; -#define CPE_LSM_SESSION_ID_MAX 1 +#define CPE_LSM_SESSION_ID_MAX 2 #define IS_VALID_SESSION_ID(s_id) \ (s_id <= CPE_LSM_SESSION_ID_MAX) @@ -418,6 +420,15 @@ struct cpe_lsm_lab_latency_config { struct cpe_lsm_lab_config latency_cfg; } __packed; +struct cpe_lsm_media_fmt_param { + struct cmi_hdr hdr; + struct cpe_param_data param; + u32 minor_version; + u32 sample_rate; + u16 num_channels; + u16 bit_width; +} __packed; + #define CPE_PARAM_LSM_LAB_LATENCY_SIZE (\ sizeof(struct cpe_lsm_lab_latency_config) - \ @@ -474,4 +485,8 @@ struct cpe_lsm_lab_latency_config { sizeof(struct cmi_hdr)) #define CPE_GAIN_PARAM_SIZE ((CPE_CMD_GAIN_PLD_SIZE) - \ sizeof(struct cpe_param_data)) +#define CPE_MEDIA_FMT_PLD_SIZE (sizeof(struct cpe_lsm_media_fmt_param) - \ + sizeof(struct cmi_hdr)) +#define CPE_MEDIA_FMT_PARAM_SIZE ((CPE_MEDIA_FMT_PLD_SIZE) - \ + sizeof(struct cpe_param_data)) #endif /* __CPE_CMI_H__ */ diff --git a/include/sound/cpe_core.h b/include/sound/cpe_core.h index 289ed1df34f5..323a63fd6238 100644 --- a/include/sound/cpe_core.h +++ b/include/sound/cpe_core.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. + * Copyright (c) 2013-2016, Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -46,6 +46,12 @@ struct lsm_out_fmt_cfg { u8 transfer_mode; }; +struct lsm_hw_params { + u32 sample_rate; + u16 num_chs; + u16 bit_width; +}; + struct cpe_lsm_session { /* sound model related */ void *snd_model_data; @@ -160,6 +166,11 @@ struct wcd_cpe_lsm_ops { void (*lsm_get_snd_model_offset) (void *core_handle, struct cpe_lsm_session *, size_t *offset); + int (*lsm_set_media_fmt_params)(void *core_handle, + struct cpe_lsm_session *session, + struct lsm_hw_params *param); + int (*lsm_set_port)(void *core_handle, + struct cpe_lsm_session *session, void *data); }; int wcd_cpe_get_lsm_ops(struct wcd_cpe_lsm_ops *); diff --git a/include/sound/wcd-dsp-mgr.h b/include/sound/wcd-dsp-mgr.h new file mode 100644 index 000000000000..5adcbcf660ba --- /dev/null +++ b/include/sound/wcd-dsp-mgr.h @@ -0,0 +1,118 @@ +/* + * 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. + */ + +#ifndef __WCD_DSP_MGR_H__ +#define __WCD_DSP_MGR_H__ + +#include <linux/types.h> + +/* + * These enums correspond to the component types + * that wcd-dsp-manager driver will use. The order + * of the enums specifies the order in which the + * manager driver will perform the sequencing. + * Changing this will cause the sequencing order + * to be changed as well. + */ +enum wdsp_cmpnt_type { + /* Component to control the DSP */ + WDSP_CMPNT_CONTROL = 0, + /* Component to perform data transfer to/from DSP */ + WDSP_CMPNT_TRANSPORT, + /* Component that performs high level IPC */ + WDSP_CMPNT_IPC, + + WDSP_CMPNT_TYPE_MAX, +}; + +enum wdsp_event_type { + /* Image download related */ + WDSP_EVENT_PRE_DLOAD_CODE, + WDSP_EVENT_DLOAD_SECTION, + WDSP_EVENT_POST_DLOAD_CODE, + WDSP_EVENT_PRE_DLOAD_DATA, + WDSP_EVENT_POST_DLOAD_DATA, + WDSP_EVENT_DLOAD_FAILED, + + /* DSP boot related */ + WDSP_EVENT_PRE_BOOTUP, + WDSP_EVENT_DO_BOOT, + WDSP_EVENT_POST_BOOTUP, + WDSP_EVENT_PRE_SHUTDOWN, + WDSP_EVENT_DO_SHUTDOWN, + WDSP_EVENT_POST_SHUTDOWN, + + /* IRQ handling related */ + WDSP_EVENT_IPC1_INTR, + + /* Suspend/Resume related */ + WDSP_EVENT_SUSPEND, + WDSP_EVENT_RESUME, +}; + +enum wdsp_intr { + WDSP_IPC1_INTR, +}; + +/* + * wdsp_cmpnt_ops: ops/function callbacks for components + * @init: called by manager driver, component is expected + * to initialize itself in this callback + * @deinit: called by manager driver, component should + * de-initialize itself in this callback + * @event_handler: Event handler for each component, called + * by the manager as per sequence + */ +struct wdsp_cmpnt_ops { + int (*init)(struct device *, void *priv_data); + int (*deinit)(struct device *, void *priv_data); + int (*event_handler)(struct device *, void *priv_data, + enum wdsp_event_type, void *data); +}; + +struct wdsp_img_section { + u32 addr; + size_t size; + u8 *data; +}; + +/* + * wdsp_ops: ops/function callbacks for manager driver + * @register_cmpnt_ops: components will use this to register + * their own ops to manager driver + * @get_dev_for_cmpnt: components can use this to get handle + * to struct device * of any other component + * @intr_handler: callback to notify manager driver that interrupt + * has occurred. + * @vote_for_dsp: notifies manager that dsp should be booted up + * @suspend: notifies manager that one component wants to suspend. + * Manager will make sure to suspend all components in order + * @resume: notifies manager that one component wants to resume. + * Manager will make sure to resume all components in order + */ + +struct wdsp_mgr_ops { + int (*register_cmpnt_ops)(struct device *wdsp_dev, + struct device *cdev, + void *priv_data, + struct wdsp_cmpnt_ops *ops); + struct device *(*get_dev_for_cmpnt)(struct device *wdsp_dev, + enum wdsp_cmpnt_type type); + int (*intr_handler)(struct device *wdsp_dev, + enum wdsp_intr intr); + int (*vote_for_dsp)(struct device *wdsp_dev, bool vote); + int (*suspend)(struct device *wdsp_dev); + int (*resume)(struct device *wdsp_dev); +}; + +#endif /* end of __WCD_DSP_MGR_H__ */ diff --git a/include/sound/wcd-spi.h b/include/sound/wcd-spi.h new file mode 100644 index 000000000000..1fff58d727a1 --- /dev/null +++ b/include/sound/wcd-spi.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef __WCD_SPI_H__ +#define __WCD_SPI_H__ + +struct wcd_spi_msg { + /* + * Caller's buffer pointer that holds data to + * be transmitted in case of data_write and + * data to be copied to in case of data_read. + */ + void *data; + + /* Length of data to write/read */ + size_t len; + + /* + * Address in remote memory to write to + * or read from. + */ + u32 remote_addr; + + /* Bitmask of flags, currently unused */ + u32 flags; +}; + +#ifdef CONFIG_SND_SOC_WCD_SPI + +int wcd_spi_data_write(struct spi_device *spi, struct wcd_spi_msg *msg); +int wcd_spi_data_read(struct spi_device *spi, struct wcd_spi_msg *msg); + +#else + +int wcd_spi_data_write(struct spi_device *spi, struct wcd_spi_msg *msg) +{ + return -ENODEV; +} + +int wcd_spi_data_read(struct spi_device *spi, struct wcd_spi_msg *msg) +{ + return -ENODEV; +} + +#endif /* End of CONFIG_SND_SOC_WCD_SPI */ + +#endif /* End of __WCD_SPI_H__ */ diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h index 54a02107a06e..60ebda8be9cb 100644 --- a/include/uapi/linux/msm_ipa.h +++ b/include/uapi/linux/msm_ipa.h @@ -434,6 +434,9 @@ enum ipa_rm_resource_name { * @IPA_HW_v2_6: IPA hardware version 2.6 * @IPA_HW_v2_6L: IPA hardware version 2.6L * @IPA_HW_v3_0: IPA hardware version 3.0 + * @IPA_HW_v3_1: IPA hardware version 3.1 + * @IPA_HW_v3_5: IPA hardware version 3.5 + * @IPA_HW_v3_5_1: IPA hardware version 3.5.1 */ enum ipa_hw_type { IPA_HW_None = 0, @@ -446,6 +449,8 @@ enum ipa_hw_type { IPA_HW_v2_6L = 6, IPA_HW_v3_0 = 10, IPA_HW_v3_1 = 11, + IPA_HW_v3_5 = 12, + IPA_HW_v3_5_1 = 13, IPA_HW_MAX }; diff --git a/include/uapi/linux/msm_kgsl.h b/include/uapi/linux/msm_kgsl.h index 34503420c882..a80278954a77 100644 --- a/include/uapi/linux/msm_kgsl.h +++ b/include/uapi/linux/msm_kgsl.h @@ -43,13 +43,13 @@ /* This is a cmdbatch exclusive flag - use the CMDBATCH equivalent instead */ #define KGSL_CONTEXT_SYNC 0x00000400 #define KGSL_CONTEXT_PWR_CONSTRAINT 0x00000800 - #define KGSL_CONTEXT_PRIORITY_MASK 0x0000F000 #define KGSL_CONTEXT_PRIORITY_SHIFT 12 #define KGSL_CONTEXT_PRIORITY_UNDEF 0 #define KGSL_CONTEXT_IFH_NOP 0x00010000 #define KGSL_CONTEXT_SECURE 0x00020000 +#define KGSL_CONTEXT_NO_SNAPSHOT 0x00040000 #define KGSL_CONTEXT_PREEMPT_STYLE_MASK 0x0E000000 #define KGSL_CONTEXT_PREEMPT_STYLE_SHIFT 25 @@ -308,6 +308,7 @@ enum kgsl_timestamp_type { #define KGSL_PROP_GPMU_VERSION 0x16 #define KGSL_PROP_HIGHEST_BANK_BIT 0x17 #define KGSL_PROP_DEVICE_BITNESS 0x18 +#define KGSL_PROP_DEVICE_QDSS_STM 0x19 struct kgsl_shadowprop { unsigned long gpuaddr; @@ -315,6 +316,11 @@ struct kgsl_shadowprop { unsigned int flags; /* contains KGSL_FLAGS_ values */ }; +struct kgsl_qdss_stm_prop { + uint64_t gpuaddr; + uint64_t size; +}; + struct kgsl_version { unsigned int drv_major; unsigned int drv_minor; diff --git a/include/uapi/linux/msm_mdp.h b/include/uapi/linux/msm_mdp.h index a1237ed996a5..1df65c7f90b3 100644 --- a/include/uapi/linux/msm_mdp.h +++ b/include/uapi/linux/msm_mdp.h @@ -1176,6 +1176,11 @@ struct mdss_ad_cfg { uint32_t bl_ctrl_mode; }; +struct mdss_ad_bl_cfg { + uint32_t bl_min_delta; + uint32_t bl_low_limit; +}; + /* ops uses standard MDP_PP_* flags */ struct mdss_ad_init_cfg { uint32_t ops; @@ -1220,11 +1225,14 @@ enum { mdp_op_calib_dcm_state, mdp_op_max, mdp_op_pa_dither_cfg, + mdp_op_ad_bl_cfg, mdp_op_pp_max = 255, }; #define mdp_op_pa_dither_cfg mdp_op_pa_dither_cfg #define mdp_op_pp_max mdp_op_pp_max +#define mdp_op_ad_bl_cfg mdp_op_ad_bl_cfg + enum { WB_FORMAT_NV12, WB_FORMAT_RGB_565, @@ -1254,6 +1262,7 @@ struct msmfb_mdp_pp { struct mdss_ad_input ad_input; struct mdp_calib_config_buffer calib_buffer; struct mdp_calib_dcm_state calib_dcm; + struct mdss_ad_bl_cfg ad_bl_cfg; } data; }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 25627f584405..b5323800eeb5 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1764,8 +1764,9 @@ enum nl80211_commands { * over all channels. * * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a - * scheduled scan (or a WoWLAN net-detect scan) is started, u32 - * in seconds. + * scheduled scan is started. Or the delay before a WoWLAN + * net-detect scan is started, counting from the moment the + * system is suspended. This value is a u32, in seconds. * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device * is operating in an indoor environment. @@ -1782,11 +1783,26 @@ enum nl80211_commands { * thus it must not specify the number of iterations, only the interval * between scans. The scan plans are executed sequentially. * Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan. - * * @NL80211_ATTR_PBSS: flag attribute. If set it means operate * in a PBSS. Specified in %NL80211_CMD_CONNECT to request * connecting to a PCP, and in %NL80211_CMD_START_AP to start * a PCP instead of AP. Relevant for DMG networks only. + * @NL80211_ATTR_BSS_SELECT: nested attribute for driver supporting the + * BSS selection feature. When used with %NL80211_CMD_GET_WIPHY it contains + * attributes according &enum nl80211_bss_select_attr to indicate what + * BSS selection behaviours are supported. When used with %NL80211_CMD_CONNECT + * it contains the behaviour-specific attribute containing the parameters for + * BSS selection to be done by driver and/or firmware. + * + * @NL80211_ATTR_STA_SUPPORT_P2P_PS: whether P2P PS mechanism supported + * or not. u8, one of the values of &enum nl80211_sta_p2p_ps_status + * + * @NL80211_ATTR_PAD: attribute used for padding for 64-bit alignment + * + * @NL80211_ATTR_IFTYPE_EXT_CAPA: Nested attribute of the following attributes: + * %NL80211_ATTR_IFTYPE, %NL80211_ATTR_EXT_CAPA, + * %NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities per + * interface type. * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined @@ -2164,6 +2180,14 @@ enum nl80211_attrs { NL80211_ATTR_PBSS, + NL80211_ATTR_BSS_SELECT, + + NL80211_ATTR_STA_SUPPORT_P2P_PS, + + NL80211_ATTR_PAD, + + NL80211_ATTR_IFTYPE_EXT_CAPA, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 27fe13a534b4..fe19c7596f8c 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -1166,6 +1166,13 @@ enum v4l2_mpeg_vidc_video_lowlatency_mode { #define V4L2_CID_MPEG_VIDEO_MAX_QP_PACKED \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 92) +#define V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8 \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 93) +enum v4l2_mpeg_vidc_video_h264_transform_8x8 { + V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_DISABLE = 0, + V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE = 1, +}; + /* Camera class control IDs */ #define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900) diff --git a/include/uapi/media/msmb_isp.h b/include/uapi/media/msmb_isp.h index e658f4b56df9..93613855228e 100644 --- a/include/uapi/media/msmb_isp.h +++ b/include/uapi/media/msmb_isp.h @@ -6,6 +6,7 @@ #define MAX_PLANES_PER_STREAM 3 #define MAX_NUM_STREAM 7 +#define ISP_VERSION_48 48 #define ISP_VERSION_47 47 #define ISP_VERSION_46 46 #define ISP_VERSION_44 44 diff --git a/kernel/Documentation/firmware_updater/request_firmware.txt b/kernel/Documentation/firmware_updater/request_firmware.txt new file mode 100644 index 000000000000..317f04ac5684 --- /dev/null +++ b/kernel/Documentation/firmware_updater/request_firmware.txt @@ -0,0 +1,22 @@ +Firmware Update Function +======================== + +Call export function "synaptics_fw_updater" in rmi_fw_update.c to start +firmware updating process in the driver. + +The RMI4 driver uses the kernel's request_firmware() feature to obtain +firmware for the touch sensor. The firmware is expected to live in +the file firmware/<firmware_name>.img.ihex. + +To prepare Synaptics provided .img file for reflashing, convert it to .ihex +format using the following command: + + objcopy -I binary -O ihex <firmware_name>.img firmware/<firmware_name>.img.ihex + +Then make sure to add the image file name to the +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_FW_UPDATE entry in firmware/Makefile. +If you don't do this, the image file won't be included, and +the firmware loader class will delay for 60 seconds waiting for a non-existent +userspace response to the firmware load request. + +Firmware updates for multichip solutions (aka LTS) are not supported. diff --git a/kernel/Documentation/firmware_updater/synaptics_fw_updater b/kernel/Documentation/firmware_updater/synaptics_fw_updater Binary files differnew file mode 100644 index 000000000000..b0c1b4d9e770 --- /dev/null +++ b/kernel/Documentation/firmware_updater/synaptics_fw_updater diff --git a/kernel/Documentation/firmware_updater/synaptics_fw_updater.c b/kernel/Documentation/firmware_updater/synaptics_fw_updater.c new file mode 100644 index 000000000000..7409dd424109 --- /dev/null +++ b/kernel/Documentation/firmware_updater/synaptics_fw_updater.c @@ -0,0 +1,753 @@ +/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + * Copyright © 2011, 2012 Synaptics Incorporated. All rights reserved. + * + * The information in this file is confidential under the terms + * of a non-disclosure agreement with Synaptics and is provided + * AS IS without warranties or guarantees of any kind. + * + * The information in this file shall remain the exclusive property + * of Synaptics and may be the subject of Synaptics patents, in + * whole or part. Synaptics intellectual property rights in the + * information in this file are not expressly or implicitly licensed + * or otherwise transferred to you as a result of such information + * being made available to you. + * + * File: synaptics_fw_updater.c + * + * Description: command line reflash implimentation using command + * line args. This file should not be OS dependant and should build and + * run under any Linux based OS that utilizes the Synaptice rmi driver + * built into the kernel (kernel/drivers/input/rmi4). + * + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> +#include <sys/time.h> + +#define DEFAULT_SENSOR "/sys/class/input/input1" + +#define MAX_STRING_LEN 256 +#define MAX_INT_LEN 33 + +#define DATA_FILENAME "data" +#define IMAGESIZE_FILENAME "imagesize" +#define DOREFLASH_FILENAME "doreflash" +#define CONFIGAREA_FILENAME "configarea" +#define READCONFIG_FILENAME "readconfig" +#define WRITECONFIG_FILENAME "writeconfig" +#define BLOCKSIZE_FILENAME "blocksize" +#define IMAGEBLOCKCOUNT_FILENAME "fwblockcount" +#define CONFIGBLOCKCOUNT_FILENAME "configblockcount" +#define PMCONFIGBLOCKCOUNT_FILENAME "permconfigblockcount" +#define BUILDID_FILENAME "buildid" +#define FLASHPROG_FILENAME "flashprog" + +#define UI_CONFIG_AREA 0 +#define PERM_CONFIG_AREA 1 +#define BL_CONFIG_AREA 2 +#define DISP_CONFIG_AREA 3 + +#define IMAGE_FILE_CHECKSUM_SIZE 4 + +unsigned char *firmware = NULL; +int fileSize; +int firmwareBlockSize; +int firmwareBlockCount; +int firmwareImgSize; +int configBlockSize; +int configBlockCount; +int configImgSize; +int totalBlockCount; +int readConfig = 0; +int writeConfig = 0; +int uiConfig = 0; +int pmConfig = 0; +int blConfig = 0; +int dpConfig = 0; +int force = 0; +int verbose = 0; + +char mySensor[MAX_STRING_LEN]; +char imageFileName[MAX_STRING_LEN]; + +static void usage(char *name) +{ + printf("Usage: %s [-b {image_file}] [-d {sysfs_entry}] [-r] [-ui] [-pm] [-bl] [-dp] [-f] [-v]\n", name); + printf("\t[-b {image_file}] - Name of image file\n"); + printf("\t[-d {sysfs_entry}] - Path to sysfs entry of sensor\n"); + printf("\t[-r] - Read config area\n"); + printf("\t[-ui] - UI config area\n"); + printf("\t[-pm] - Permanent config area\n"); + printf("\t[-bl] - BL config area\n"); + printf("\t[-dp] - Display config area\n"); + printf("\t[-f] - Force reflash\n"); + printf("\t[-v] - Verbose output\n"); + + return; +} + +static void TimeSubtract(struct timeval *result, struct timeval *x, struct timeval *y) +{ + if (x->tv_usec < y->tv_usec) { + result->tv_sec = x->tv_sec - y->tv_sec - 1; + result->tv_usec = y->tv_usec - x->tv_usec; + } else { + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_usec = x->tv_usec - y->tv_usec; + } + + return; +} + +static int CheckSysfsEntry(char *sensorName) +{ + int retval; + struct stat st; + + retval = stat(sensorName, &st); + if (retval) + printf("ERROR: sensor sysfs entry %s not found\n", sensorName); + + return retval; +} + +static void WriteBinData(char *fname, unsigned char *buf, int len) +{ + int numBytesWritten; + FILE *fp; + + fp = fopen(fname, "wb"); + if (!fp) { + printf("ERROR: failed to open %s for writing data\n", fname); + exit(EIO); + } + + numBytesWritten = fwrite(buf, 1, len, fp); + + if (numBytesWritten != len) { + printf("ERROR: failed to write all data to bin file\n"); + fclose(fp); + exit(EIO); + } + + fclose(fp); + + return; +} + +static void ReadBinData(char *fname, unsigned char *buf, int len) +{ + int numBytesRead; + FILE *fp; + + fp = fopen(fname, "rb"); + if (!fp) { + printf("ERROR: failed to open %s for reading data\n", fname); + exit(EIO); + } + + numBytesRead = fread(buf, 1, len, fp); + + if (numBytesRead != len) { + printf("ERROR: failed to read all data from bin file\n"); + fclose(fp); + exit(EIO); + } + + fclose(fp); + + return; +} + +static void WriteValueToFp(FILE *fp, unsigned int value) +{ + int numBytesWritten; + char buf[MAX_INT_LEN]; + + snprintf(buf, MAX_INT_LEN, "%u", value); + + fseek(fp, 0, 0); + + numBytesWritten = fwrite(buf, 1, strlen(buf) + 1, fp); + if (numBytesWritten != ((int)(strlen(buf) + 1))) { + printf("ERROR: failed to write value to file pointer\n"); + fclose(fp); + exit(EIO); + } + + return; +} + +static void WriteValueToSysfsFile(char *fname, unsigned int value) +{ + FILE *fp; + + fp = fopen(fname, "w"); + if (!fp) { + printf("ERROR: failed to open %s for writing value\n", fname); + exit(EIO); + } + + WriteValueToFp(fp, value); + + fclose(fp); + + return; +} + +static void ReadValueFromFp(FILE *fp, unsigned int *value) +{ + int retVal; + char buf[MAX_INT_LEN]; + + fseek(fp, 0, 0); + + retVal = fread(buf, 1, sizeof(buf), fp); + if (retVal == -1) { + printf("ERROR: failed to read value from file pointer\n"); + exit(EIO); + } + + *value = strtoul(buf, NULL, 0); + + return; +} + +static void ReadValueFromSysfsFile(char *fname, unsigned int *value) +{ + FILE *fp; + + fp = fopen(fname, "r"); + if (!fp) { + printf("ERROR: failed to open %s for reading value\n", fname); + exit(EIO); + } + + ReadValueFromFp(fp, value); + + fclose(fp); + + return; +} + +static void WriteBlockData(char *buf, int len) +{ + char tmpfname[MAX_STRING_LEN]; + + snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, DATA_FILENAME); + + WriteBinData(tmpfname, (unsigned char *)buf, len); + + return; +} + +static void ReadBlockData(char *buf, int len) +{ + char tmpfname[MAX_STRING_LEN]; + + snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, DATA_FILENAME); + + ReadBinData(tmpfname, (unsigned char *)buf, len); + + return; +} + +static void SetImageSize(int value) +{ + char tmpfname[MAX_STRING_LEN]; + + snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, IMAGESIZE_FILENAME); + + WriteValueToSysfsFile(tmpfname, value); + + return; +} + +static void StartReflash(int value) +{ + char tmpfname[MAX_STRING_LEN]; + + snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, DOREFLASH_FILENAME); + + WriteValueToSysfsFile(tmpfname, value); + + return; +} + +static void SetConfigArea(int value) +{ + char tmpfname[MAX_STRING_LEN]; + + snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, CONFIGAREA_FILENAME); + + WriteValueToSysfsFile(tmpfname, value); + + return; +} + +static void StartWriteConfig(int value) +{ + char tmpfname[MAX_STRING_LEN]; + + snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, WRITECONFIG_FILENAME); + + WriteValueToSysfsFile(tmpfname, value); + + return; +} + +static void StartReadConfig(int value) +{ + char tmpfname[MAX_STRING_LEN]; + + snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, READCONFIG_FILENAME); + + WriteValueToSysfsFile(tmpfname, value); + + return; +} + +static int ReadBlockSize(void) +{ + unsigned int blockSize; + char tmpfname[MAX_STRING_LEN]; + + snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, BLOCKSIZE_FILENAME); + + ReadValueFromSysfsFile(tmpfname, &blockSize); + + return blockSize; +} + +static int ReadFirmwareBlockCount(void) +{ + unsigned int imageBlockCount; + char tmpfname[MAX_STRING_LEN]; + + snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, IMAGEBLOCKCOUNT_FILENAME); + + ReadValueFromSysfsFile(tmpfname, &imageBlockCount); + + return imageBlockCount; +} + +static int ReadConfigBlockCount(void) +{ + unsigned int configBlockCount; + char tmpfname[MAX_STRING_LEN]; + + snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, CONFIGBLOCKCOUNT_FILENAME); + + ReadValueFromSysfsFile(tmpfname, &configBlockCount); + + return configBlockCount; +} + +static int ReadPmConfigBlockCount(void) +{ + unsigned int configBlockCount; + char tmpfname[MAX_STRING_LEN]; + + snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, PMCONFIGBLOCKCOUNT_FILENAME); + + ReadValueFromSysfsFile(tmpfname, &configBlockCount); + + return configBlockCount; +} + +static int ReadBuildID(void) +{ + unsigned int buildID; + char tmpfname[MAX_STRING_LEN]; + + snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, BUILDID_FILENAME); + + ReadValueFromSysfsFile(tmpfname, &buildID); + + return buildID; +} + +static int ReadFlashProg(void) +{ + unsigned int flashProg; + char tmpfname[MAX_STRING_LEN]; + + snprintf(tmpfname, MAX_STRING_LEN, "%s/%s", mySensor, FLASHPROG_FILENAME); + + ReadValueFromSysfsFile(tmpfname, &flashProg); + + return flashProg; +} + +static void ReadFirmwareInfo(void) +{ + firmwareBlockSize = ReadBlockSize(); + firmwareBlockCount = ReadFirmwareBlockCount(); + firmwareImgSize = firmwareBlockCount * firmwareBlockSize; + + return; +} + +static void ReadConfigInfo(void) +{ + configBlockSize = ReadBlockSize(); + configBlockCount = ReadConfigBlockCount(); + configImgSize = configBlockSize * configBlockCount; + + return; +} + +static void CalculateChecksum(unsigned short *data, unsigned short len, unsigned long *result) +{ + unsigned long temp; + unsigned long sum1 = 0xffff; + unsigned long sum2 = 0xffff; + + *result = 0xffffffff; + + while (len--) { + temp = *data; + sum1 += temp; + sum2 += sum1; + sum1 = (sum1 & 0xffff) + (sum1 >> 16); + sum2 = (sum2 & 0xffff) + (sum2 >> 16); + data++; + } + + *result = sum2 << 16 | sum1; + + return; +} + +static int CompareChecksum(void) +{ + unsigned long headerChecksum; + unsigned long computedChecksum; + + headerChecksum = (unsigned long)firmware[0] + + (unsigned long)firmware[1] * 0x100 + + (unsigned long)firmware[2] * 0x10000 + + (unsigned long)firmware[3] * 0x1000000; + + CalculateChecksum((unsigned short *)&firmware[IMAGE_FILE_CHECKSUM_SIZE], + ((fileSize - IMAGE_FILE_CHECKSUM_SIZE) / 2), &computedChecksum); + + if (verbose) { + printf("Checksum in image file header = 0x%08x\n", (unsigned int)headerChecksum); + printf("Checksum computed from image file = 0x%08x\n", (unsigned int)computedChecksum); + } + + if (headerChecksum == computedChecksum) + return 1; + else + return 0; +} + +static int ProceedWithReflash(void) +{ + int index = 0; + int deviceBuildID; + int imageBuildID; + char imagePR[MAX_STRING_LEN]; + char *strptr; + + if (force) { + printf("Force reflash...\n"); + return 1; + } + + if (ReadFlashProg()) { + printf("Force reflash (device in flash prog mode)...\n"); + return 1; + } + + strptr = strstr(imageFileName, "PR"); + if (!strptr) { + printf("No valid PR number (PRxxxxxxx) found in image file name...\n"); + return 0; + } + + strptr += 2; + while (strptr[index] >= '0' && strptr[index] <= '9') { + imagePR[index] = strptr[index]; + index++; + } + imagePR[index] = 0; + + imageBuildID = strtoul(imagePR, NULL, 0); + deviceBuildID = ReadBuildID(); + printf("Image file PR = %d\n", imageBuildID); + printf("Device PR = %d\n", deviceBuildID); + + if (imageBuildID > deviceBuildID) { + printf("Proceed with reflash...\n"); + return 1; + } else { + printf("No need to do reflash...\n"); + return 0; + } +} + +static void DoReadConfig(void) +{ + int ii; + int jj; + int index = 0; + int configSize; + int blockCount; + unsigned char *buffer; + + if (uiConfig) { + SetConfigArea(UI_CONFIG_AREA); + StartReadConfig(1); + blockCount = configBlockCount; + configSize = configImgSize; + buffer = malloc(configSize); + if (!buffer) + exit(ENOMEM); + ReadBlockData((char *)&buffer[0], configSize); + } else if (pmConfig) { + SetConfigArea(PERM_CONFIG_AREA); + StartReadConfig(1); + blockCount = ReadPmConfigBlockCount(); + configSize = configBlockSize * blockCount; + buffer = malloc(configSize); + if (!buffer) + exit(ENOMEM); + ReadBlockData((char *)&buffer[0], configSize); + } else { + return; + } + + for (ii = 0; ii < blockCount; ii++) { + for (jj = 0; jj < configBlockSize; jj++) { + printf("0x%02x ", buffer[index]); + index++; + } + printf("\n"); + } + + free(buffer); + + return; +} + +static void DoWriteConfig(void) +{ + printf("Starting config programming...\n"); + + if (uiConfig) + SetConfigArea(UI_CONFIG_AREA); + else if (pmConfig) + SetConfigArea(PERM_CONFIG_AREA); + else if (blConfig) + SetConfigArea(BL_CONFIG_AREA); + else if (dpConfig) + SetConfigArea(DISP_CONFIG_AREA); + else + return; + + SetImageSize(fileSize); + WriteBlockData((char *)&firmware[0], fileSize); + StartWriteConfig(1); + + printf("Config programming completed...\n"); + + return; +} + +static void DoReflash(void) +{ + if (verbose) + printf("Blocks: %d (firmware: %d, config: %d)\n", totalBlockCount, firmwareBlockCount, configBlockCount); + + if (!ProceedWithReflash()) + return; + + printf("Starting reflash...\n"); + + SetImageSize(fileSize); + WriteBlockData((char *)&firmware[0], fileSize); + StartReflash(1); + + printf("Reflash completed...\n"); + + return; +} + +static int InitFirmwareImage(void) +{ + int numBytesRead; + FILE *fp; + + if (!readConfig) { + fp = fopen(imageFileName, "rb"); + + if (!fp) { + printf("ERROR: image file %s not found\n", imageFileName); + exit(ENODEV); + } + + fseek(fp, 0L, SEEK_END); + fileSize = ftell(fp); + if (fileSize == -1) { + printf("ERROR: failed to determine size of %s\n", imageFileName); + exit(EIO); + } + + fseek(fp, 0L, SEEK_SET); + + firmware = malloc(fileSize + 1); + if (!firmware) { + exit(ENOMEM); + } else { + numBytesRead = fread(firmware, 1, fileSize, fp); + if (numBytesRead != fileSize) { + printf("ERROR: failed to read entire content of image file\n"); + exit(EIO); + } + } + + fclose(fp); + + if (!(pmConfig || blConfig || dpConfig)) { + if (!CompareChecksum()) { + printf("ERROR: failed to validate checksum of image file\n"); + exit(EINVAL); + } + } + } + + return 0; +} + +int main(int argc, char* argv[]) +{ + int retVal; + int this_arg = 1; + struct stat st; + struct timeval start_time; + struct timeval end_time; + struct timeval elapsed_time; + + if (argc == 1) { + usage(argv[0]); + exit(EINVAL); + } + + while (this_arg < argc) { + if (!strcmp((const char *)argv[this_arg], "-b")) { + /* Image file */ + FILE *file; + + this_arg++; + if (this_arg >= argc) { + printf("ERROR: image file missing\n"); + exit(EINVAL); + } + + /* check for presence of image file */ + file = fopen(argv[this_arg], "rb"); + if (file == 0) { + printf("ERROR: image file %s not found\n", argv[this_arg]); + exit(EINVAL); + } + fclose(file); + + strncpy(imageFileName, argv[this_arg], MAX_STRING_LEN); + } else if (!strcmp((const char *)argv[this_arg], "-d")) { + /* path to sensor sysfs entry */ + this_arg++; + + if (stat(argv[this_arg], &st) == 0) { + strncpy(mySensor, argv[this_arg], MAX_STRING_LEN); + } else { + printf("ERROR: sensor sysfs entry %s not found\n", argv[this_arg]); + exit(EINVAL); + } + } else if (!strcmp((const char *)argv[this_arg], "-r")) { + readConfig = 1; + } else if (!strcmp((const char *)argv[this_arg], "-ui")) { + uiConfig = 1; + } else if (!strcmp((const char *)argv[this_arg], "-pm")) { + pmConfig = 1; + } else if (!strcmp((const char *)argv[this_arg], "-bl")) { + blConfig = 1; + } else if (!strcmp((const char *)argv[this_arg], "-dp")) { + dpConfig = 1; + } else if (!strcmp((const char *)argv[this_arg], "-f")) { + force = 1; + } else if (!strcmp((const char *)argv[this_arg], "-v")) { + verbose = 1; + } else { + usage(argv[0]); + printf("ERROR: invalid parameter %s supplied\n", argv[this_arg]); + exit(EINVAL); + } + this_arg++; + } + + if ((uiConfig + pmConfig + blConfig + dpConfig) > 1) { + printf("ERROR: too many parameters\n"); + exit(EINVAL); + } + + if (uiConfig || pmConfig || blConfig || dpConfig) + writeConfig = 1; + + if (!readConfig && !strlen(imageFileName)) { + printf("ERROR: no image file specified\n"); + exit(EINVAL); + } + + if (!strlen(mySensor)) + strncpy(mySensor, DEFAULT_SENSOR, MAX_STRING_LEN); + + if (CheckSysfsEntry(mySensor)) + exit(ENODEV); + + InitFirmwareImage(); + + ReadFirmwareInfo(); + ReadConfigInfo(); + totalBlockCount = configBlockCount + firmwareBlockCount; + + retVal = gettimeofday(&start_time, NULL); + if (retVal) + printf("WARNING: failed to get start time\n"); + + if (verbose) { + if (!readConfig) + printf("Image file: %s\n", imageFileName); + printf("Sensor sysfs entry: %s\n", mySensor); + } + + if (readConfig) + DoReadConfig(); + else if (writeConfig) + DoWriteConfig(); + else + DoReflash(); + + retVal = gettimeofday(&end_time, NULL); + if (retVal) + printf("WARNING: failed to get end time\n"); + + TimeSubtract(&elapsed_time, &end_time, &start_time); + + if (verbose) { + printf("Elapsed time = %ld.%06ld seconds\n", + (long)elapsed_time.tv_sec, + (long)elapsed_time.tv_usec); + } + + return 0; +} diff --git a/kernel/Documentation/firmware_updater/synaptics_fw_updater_readme.txt b/kernel/Documentation/firmware_updater/synaptics_fw_updater_readme.txt new file mode 100644 index 000000000000..66f71922995a --- /dev/null +++ b/kernel/Documentation/firmware_updater/synaptics_fw_updater_readme.txt @@ -0,0 +1,41 @@ +Use ADB (Android Debug Bridge) to do command-line reflash +- Power on device. +- Connect device to host via USB. +- Open command prompt on host and go to directory where adb, synaptics_fw_updater, and FW image (e.g. PR1234567.img) reside. +- Run "adb devices" to ensure connection with device. +- Run "adb root" to have root privileges. +- Run "adb push synaptics_fw_updater /data" to copy synaptics_fw_updater to /data directory on device. +- Run "adb push firmware.img /data" to copy firmware.img to /data directory on device. +- Run "adb shell chmod 777 /data/synaptics_fw_updater" to make synaptics_fw_updater executable. +- Run "adb shell /data/synaptics_fw_updater -b /data/PR1234567.img -f -v" to start reflash process. + +Parameters +[-b {image_file}] - Name of image file +[-d {sysfs_entry}] - Path to sysfs entry of sensor +[-r] - Read config area +[-ui] - UI config area +[-pm] - Permanent config area +[-bl] - BL config area +[-dp] - Display config area +[-f] - Force reflash +[-v] - Verbose output + +Procedures for checking whether to proceed with reflash +- If [-f] flag is set, proceed with reflash +- If device is in flash prog (bootloader) mode, proceed with reflash +- If PR number contained in name of new FW image is greater than PR number of FW on device, proceed with reflash. +- Otherwise, no reflash is performed + +Usage examples +- Perform reflash using PR1234567.img regardless of PR number of FW on device + synaptics_fw_updater -b PR1234567.img -f +- Perform reflash using PR1234567.img only if 1234567 is greater than PR number of FW on device. + synaptics_fw_updater -b PR1234567.img +- Write UI config area from PR1234567.img (parsing UI config area from firmware image file) + synaptics_fw_updater -b PR1234567.img -ui +- Write permanent config area from pmconfig.img (binary file containing permanent config data) + synaptics_fw_updater -b pmconfig.img -pm +- Read UI config area + synaptics_fw_updater -r -ui +- Read permanent config area + synaptics_fw_updater -r -pm
\ No newline at end of file diff --git a/kernel/arch/arm/configs/omap3_beagle_android_defconfig b/kernel/arch/arm/configs/omap3_beagle_android_defconfig new file mode 100644 index 000000000000..4fc62c4fa440 --- /dev/null +++ b/kernel/arch/arm/configs/omap3_beagle_android_defconfig @@ -0,0 +1,2419 @@ +# +# Automatically generated make config: don't edit +# Linux/arm 2.6.37 Kernel Configuration +# Mon Apr 16 13:58:06 2012 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y +CONFIG_HAVE_IRQ_WORK=y +CONFIG_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +# CONFIG_HAVE_GENERIC_HARDIRQS is not set +# CONFIG_SPARSE_IRQ is not set + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=16 +# CONFIG_CGROUPS is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_EXTRA_PASS=y +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_ASHMEM=y +CONFIG_AIO=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +CONFIG_PERF_EVENTS=y +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +CONFIG_PROFILING=y +CONFIG_TRACEPOINTS=y +CONFIG_OPROFILE=y +CONFIG_HAVE_OPROFILE=y +CONFIG_KPROBES=y +CONFIG_KRETPROBES=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_HW_BREAKPOINT=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +CONFIG_INLINE_SPIN_UNLOCK=y +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_S5PV310 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_TCC_926 is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +CONFIG_ARCH_OMAP=y +# CONFIG_PLAT_SPEAR is not set + +# +# TI OMAP Common Features +# +CONFIG_ARCH_OMAP_OTG=y +# CONFIG_ARCH_OMAP1 is not set +CONFIG_ARCH_OMAP2PLUS=y + +# +# OMAP Feature Selections +# +CONFIG_OMAP_SMARTREFLEX=y +CONFIG_OMAP_SMARTREFLEX_CLASS3=y +CONFIG_OMAP_RESET_CLOCKS=y +CONFIG_OMAP_MUX=y +CONFIG_OMAP_MUX_DEBUG=y +CONFIG_OMAP_MUX_WARNINGS=y +CONFIG_OMAP_MCBSP=y +# CONFIG_OMAP_MBOX_FWK is not set +CONFIG_OMAP_IOMMU=y +# CONFIG_OMAP_IOMMU_DEBUG is not set +# CONFIG_OMAP_MPU_TIMER is not set +CONFIG_OMAP_32K_TIMER=y +# CONFIG_OMAP3_L2_AUX_SECURE_SAVE_RESTORE is not set +CONFIG_OMAP_32K_TIMER_HZ=128 +CONFIG_OMAP_DM_TIMER=y +# CONFIG_OMAP_PM_NONE is not set +CONFIG_OMAP_PM_NOOP=y + +# +# TI OMAP2/3/4 Specific Features +# +CONFIG_ARCH_OMAP2PLUS_TYPICAL=y +# CONFIG_ARCH_OMAP2 is not set +CONFIG_ARCH_OMAP3=y +# CONFIG_ARCH_OMAP4 is not set +# CONFIG_ARCH_TI81XX is not set +CONFIG_ARCH_OMAP3430=y +CONFIG_OMAP_PACKAGE_CBB=y + +# +# OMAP Board Type +# +CONFIG_MACH_OMAP3_BEAGLE=y +# CONFIG_MACH_DEVKIT8000 is not set +# CONFIG_MACH_OMAP_LDP is not set +# CONFIG_MACH_OMAP3530_LV_SOM is not set +# CONFIG_MACH_OMAP3_TORPEDO is not set +# CONFIG_MACH_OVERO is not set +# CONFIG_MACH_OMAP3EVM is not set +# CONFIG_MACH_FLASHBOARD is not set +# CONFIG_MACH_OMAP3517EVM is not set +# CONFIG_MACH_CRANEBOARD is not set +# CONFIG_MACH_OMAP3_PANDORA is not set +# CONFIG_MACH_OMAP3_TOUCHBOOK is not set +# CONFIG_MACH_OMAP_3430SDP is not set +# CONFIG_MACH_NOKIA_RM680 is not set +# CONFIG_MACH_NOKIA_RX51 is not set +# CONFIG_MACH_OMAP_ZOOM2 is not set +# CONFIG_MACH_OMAP_ZOOM3 is not set +# CONFIG_MACH_CM_T35 is not set +# CONFIG_MACH_CM_T3517 is not set +# CONFIG_MACH_IGEP0020 is not set +# CONFIG_MACH_IGEP0030 is not set +# CONFIG_MACH_SBC3530 is not set +# CONFIG_MACH_OMAP_3630SDP is not set +# CONFIG_OMAP3_EMU is not set +CONFIG_OMAP3_PM_DISABLE_VT_SWITCH=y +# CONFIG_OMAP3_SDRC_AC_TIMING is not set + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_743622 is not set +CONFIG_COMMON_CLKDEV=y +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=128 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +CONFIG_ARCH_HAS_HOLES_MEMORYMODEL=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +CONFIG_FORCE_MAX_ZONEORDER=11 +# CONFIG_LEDS is not set +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="root=/dev/mmcblk0p2 rootwait console=ttyO2,115200" +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +CONFIG_KEXEC=y +CONFIG_ATAGS_PROC=y +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +# CONFIG_CPU_FREQ_DEBUG is not set +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_GOV_INTERACTIVE is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_MISC=y + +# +# Power management options +# +CONFIG_PM=y +CONFIG_PM_DEBUG=y +# CONFIG_PM_ADVANCED_DEBUG is not set +# CONFIG_PM_VERBOSE is not set +CONFIG_CAN_PM_TRACE=y +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND_NVS=y +CONFIG_SUSPEND=y +# CONFIG_PM_TEST_SUSPEND is not set +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +# CONFIG_CONSOLE_EARLYSUSPEND is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_APM_EMULATION is not set +CONFIG_PM_RUNTIME=y +CONFIG_PM_OPS=y +CONFIG_ARCH_HAS_OPP=y +CONFIG_PM_OPP=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +CONFIG_XFRM_USER=y +# CONFIG_XFRM_SUB_POLICY is not set +CONFIG_XFRM_MIGRATE=y +# CONFIG_XFRM_STATISTICS is not set +CONFIG_NET_KEY=y +CONFIG_NET_KEY_MIGRATE=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +# CONFIG_IPV6_PRIVACY is not set +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_INET6_AH is not set +# CONFIG_INET6_ESP is not set +# CONFIG_INET6_IPCOMP is not set +# CONFIG_IPV6_MIP6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_NETLABEL is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +CONFIG_DNS_RESOLVER=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NET_TCPPROBE is not set +# CONFIG_NET_DROP_MONITOR is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +# CONFIG_CFG80211 is not set +CONFIG_WIRELESS_EXT_SYSFS=y +# CONFIG_LIB80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# + +# +# Some wireless drivers require a rate control algorithm +# +# CONFIG_WIMAX is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +CONFIG_MTD_OOPS=y + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_ARM_INTEGRATOR is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND_IDS=y +CONFIG_MTD_NAND_ECC=y +# CONFIG_MTD_NAND_ECC_SMC is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_SM_COMMON is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_GPIO is not set +CONFIG_MTD_NAND_OMAP2=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ALAUDA is not set +CONFIG_MTD_ONENAND=y +CONFIG_MTD_ONENAND_VERIFY_WRITE=y +# CONFIG_MTD_ONENAND_GENERIC is not set +CONFIG_MTD_ONENAND_OMAP2=y +# CONFIG_MTD_ONENAND_OTP is not set +# CONFIG_MTD_ONENAND_2X_PROGRAM is not set +# CONFIG_MTD_ONENAND_SIM is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=16384 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set +# CONFIG_MISC_DEVICES is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_ISCSI_BOOT_SYSFS is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=y +# CONFIG_DM_DEBUG is not set +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +CONFIG_MII=y +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +CONFIG_SMSC_PHY=y +# CONFIG_BROADCOM_PHY is not set +# CONFIG_BCM63XX_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +CONFIG_NET_ETHERNET=y +# CONFIG_AX88796 is not set +# CONFIG_SMC91X is not set +# CONFIG_DM9000 is not set +# CONFIG_ENC28J60 is not set +# CONFIG_ETHOC is not set +CONFIG_SMC911X=y +CONFIG_SMSC911X=y +# CONFIG_SMSC911X_ARCH_HOOKS is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +CONFIG_NETDEV_1000=y +CONFIG_TI_DAVINCI_EMAC=y +CONFIG_TI_DAVINCI_MDIO=y +CONFIG_TI_DAVINCI_CPDMA=y +# CONFIG_STMMAC_ETH is not set +CONFIG_NETDEV_10000=y +CONFIG_WLAN=y +# CONFIG_USB_ZD1201 is not set +# CONFIG_BCM4329 is not set +# CONFIG_HOSTAP is not set +CONFIG_WL12XX_PLATFORM_DATA=y + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +CONFIG_USB_USBNET=y +CONFIG_USB_NET_AX8817X=y +CONFIG_USB_NET_CDCETHER=y +# CONFIG_USB_NET_CDC_EEM is not set +# CONFIG_USB_NET_DM9601 is not set +# CONFIG_USB_NET_SMSC75XX is not set +CONFIG_USB_NET_SMSC95XX=y +# CONFIG_USB_NET_GL620A is not set +CONFIG_USB_NET_NET1080=y +# CONFIG_USB_NET_PLUSB is not set +# CONFIG_USB_NET_MCS7830 is not set +# CONFIG_USB_NET_RNDIS_HOST is not set +CONFIG_USB_NET_CDC_SUBSET=y +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +CONFIG_USB_BELKIN=y +CONFIG_USB_ARMLINUX=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_KC2190=y +CONFIG_USB_NET_ZAURUS=y +# CONFIG_USB_NET_CX82310_ETH is not set +# CONFIG_USB_NET_INT51X1 is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_USB_SIERRA_NET is not set +# CONFIG_WAN is not set + +# +# CAIF transport drivers +# +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +CONFIG_INPUT_JOYDEV=y +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +CONFIG_KEYBOARD_GPIO=y +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +CONFIG_KEYBOARD_TWL4030=y +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +CONFIG_MOUSE_PS2_ALPS=y +CONFIG_MOUSE_PS2_LOGIPS2PP=y +CONFIG_MOUSE_PS2_SYNAPTICS=y +CONFIG_MOUSE_PS2_TRACKPOINT=y +# CONFIG_MOUSE_PS2_ELANTECH is not set +# CONFIG_MOUSE_PS2_SENTELIC is not set +# CONFIG_MOUSE_PS2_TOUCHKIT is not set +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_BCM5974 is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_MOUSE_GPIO is not set +# CONFIG_MOUSE_SYNAPTICS_I2C is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_QT602240 is not set +CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE=y +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_TSC2004 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYCHORD is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_TWL4030_PWRBUTTON=y +# CONFIG_INPUT_TWL4030_VIBRA is not set +# CONFIG_INPUT_UINPUT is not set +# CONFIG_INPUT_GPIO is not set +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_SERIO_PS2MULT is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_DEVMEM=y +CONFIG_DEVKMEM=y +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=32 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_DETECT_IRQ=y +CONFIG_SERIAL_8250_RSA=y + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_OMAP=y +CONFIG_SERIAL_OMAP_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_TI81XX_HDMI is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +CONFIG_I2C_OMAP=y +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +CONFIG_SPI_OMAP24XX=y +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set + +# +# PPS support +# +# CONFIG_PPS is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_BASIC_MMIO is not set +# CONFIG_GPIO_IT8761E is not set +# CONFIG_GPIO_VX855 is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +CONFIG_GPIO_TWL4030=y +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_BQ20Z75 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_CHARGER_ISP1704 is not set +# CONFIG_CHARGER_TWL4030 is not set +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_GPIO_FAN is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_JC42 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LTC4261 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_SMM665 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_EMC1403 is not set +# CONFIG_SENSORS_EMC2103 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_ADS7871 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP102 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83795 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +CONFIG_OMAP_WATCHDOG=y +CONFIG_TWL4030_WATCHDOG=y +# CONFIG_MAX63XX_WATCHDOG is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_MFD_SUPPORT=y +CONFIG_MFD_CORE=y +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +CONFIG_TWL4030_CORE=y +CONFIG_TWL4030_POWER=y +CONFIG_TWL4030_SCRIPT=y +CONFIG_TWL4030_CODEC=y +# CONFIG_TWL6030_PWM is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC35892 is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_TPS6586X is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +CONFIG_REGULATOR_DUMMY=y +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +CONFIG_REGULATOR_TWL4030=y +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_AD5398 is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +CONFIG_VIDEO_ALLOW_V4L1=y +CONFIG_VIDEO_V4L1_COMPAT=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_IR_CORE is not set +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5761=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEO_V4L1=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders/decoders and other helper chips +# + +# +# Audio decoders +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TDA9875 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_VIDEO_TLV320AIC23B is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# RDS decoders +# +# CONFIG_VIDEO_SAA6588 is not set + +# +# Video decoders +# +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_MT9T001 is not set +CONFIG_VIDEO_MT9V011=y +# CONFIG_VIDEO_MT9V032 is not set +CONFIG_VIDEO_MT9V113=y +# CONFIG_VIDEO_MT9T111 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_AK881X is not set + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set +# CONFIG_VIDEO_VPSS_SYSTEM is not set +# CONFIG_VIDEO_VPFE_CAPTURE is not set +CONFIG_VIDEO_OMAP2_VOUT=y +# CONFIG_VIDEO_CPIA2 is not set +# CONFIG_VIDEO_SR030PC30 is not set +CONFIG_VIDEO_OMAP3=y +CONFIG_VIDEO_OMAP3_DEBUG=y +# CONFIG_SOC_CAMERA is not set +CONFIG_V4L_USB_DRIVERS=y +CONFIG_USB_VIDEO_CLASS=y +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +# CONFIG_USB_GSPCA is not set +# CONFIG_VIDEO_PVRUSB2 is not set +# CONFIG_VIDEO_HDPVR is not set +# CONFIG_VIDEO_USBVISION is not set +# CONFIG_USB_VICAM is not set +# CONFIG_USB_IBMCAM is not set +# CONFIG_USB_KONICAWC is not set +# CONFIG_USB_ET61X251 is not set +# CONFIG_USB_SE401 is not set +# CONFIG_USB_SN9C102 is not set +# CONFIG_USB_PWC is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_USB_STKWEBCAM is not set +# CONFIG_USB_S2255 is not set +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_RADIO_ADAPTERS is not set +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +CONFIG_FIRMWARE_EDID=y +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_TILEBLITTING=y + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_TMIO is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_OMAP_BOOTLOADER_INIT is not set +CONFIG_OMAP2_VRAM=y +CONFIG_OMAP2_VRFB=y +CONFIG_OMAP2_DSS=y +CONFIG_OMAP2_VRAM_SIZE=4 +CONFIG_OMAP2_DSS_DEBUG_SUPPORT=y +# CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS is not set +CONFIG_OMAP2_DSS_DPI=y +# CONFIG_OMAP2_DSS_RFBI is not set +CONFIG_OMAP2_DSS_VENC=y +CONFIG_OMAP2_VENC_OUT_TYPE_SVIDEO=y +# CONFIG_OMAP2_VENC_OUT_TYPE_COMPOSITE is not set +# CONFIG_OMAP2_DSS_SDI is not set +CONFIG_OMAP2_DSS_DSI=y +CONFIG_OMAP2_DSS_USE_DSI_PLL=y +# CONFIG_OMAP2_DSS_FAKE_VSYNC is not set +CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK=1 +CONFIG_FB_OMAP2=y +CONFIG_FB_OMAP2_DEBUG_SUPPORT=y +CONFIG_FB_OMAP2_NUM_FBS=1 + +# +# OMAP2/3 Display Device Drivers +# +CONFIG_PANEL_GENERIC=y +# CONFIG_PANEL_LGPHILIPS_LB035Q02 is not set +# CONFIG_PANEL_SAMSUNG_LTE430WQ_F0C is not set +CONFIG_PANEL_SHARP_LS037V7DW01=y +# CONFIG_PANEL_SHARP_LQ043T1DG01 is not set +# CONFIG_PANEL_SAMSUNG_LMS700KF23 is not set +# CONFIG_PANEL_TAAL is not set +# CONFIG_PANEL_TOPPOLY_TDO35S is not set +# CONFIG_PANEL_TPO_TD043MTEA1 is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +# CONFIG_LCD_L4F00242T03 is not set +# CONFIG_LCD_LMS283GF05 is not set +# CONFIG_LCD_LTV350QV is not set +# CONFIG_LCD_TDO24M is not set +# CONFIG_LCD_VGG2432A4 is not set +CONFIG_LCD_PLATFORM=y +# CONFIG_LCD_S6E63M0 is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=m +# CONFIG_BACKLIGHT_ADP8860 is not set + +# +# Display device support +# +CONFIG_DISPLAY_SUPPORT=y + +# +# Display hardware drivers +# + +# +# Console display driver support +# +CONFIG_DUMMY_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE is not set +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_SOUND=y +# CONFIG_SOUND_OSS_CORE is not set +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_HWDEP=y +CONFIG_SND_RAWMIDI=y +CONFIG_SND_JACK=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_ALOOP is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +CONFIG_SND_ARM=y +CONFIG_SND_SPI=y +CONFIG_SND_USB=y +CONFIG_SND_USB_AUDIO=y +# CONFIG_SND_USB_UA101 is not set +# CONFIG_SND_USB_CAIAQ is not set +CONFIG_SND_SOC=y +CONFIG_SND_OMAP_SOC=y +CONFIG_SND_OMAP_SOC_MCBSP=y +CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE=y +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +CONFIG_SND_SOC_TWL4030=y +# CONFIG_SND_SOC_WL1271BT is not set +# CONFIG_SOUND_PRIME is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# Special HID drivers +# +# CONFIG_HID_3M_PCT is not set +# CONFIG_HID_A4TECH is not set +# CONFIG_HID_ACRUX_FF is not set +# CONFIG_HID_APPLE is not set +# CONFIG_HID_BELKIN is not set +# CONFIG_HID_CANDO is not set +# CONFIG_HID_CHERRY is not set +# CONFIG_HID_CHICONY is not set +# CONFIG_HID_PRODIKEYS is not set +# CONFIG_HID_CYPRESS is not set +# CONFIG_HID_DRAGONRISE is not set +# CONFIG_HID_EGALAX is not set +# CONFIG_HID_EZKEY is not set +# CONFIG_HID_KYE is not set +# CONFIG_HID_UCLOGIC is not set +# CONFIG_HID_WALTOP is not set +# CONFIG_HID_GYRATION is not set +# CONFIG_HID_TWINHAN is not set +# CONFIG_HID_KENSINGTON is not set +# CONFIG_HID_LOGITECH is not set +# CONFIG_HID_MICROSOFT is not set +# CONFIG_HID_MOSART is not set +# CONFIG_HID_MONTEREY is not set +# CONFIG_HID_NTRIG is not set +# CONFIG_HID_ORTEK is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_PICOLCD is not set +# CONFIG_HID_QUANTA is not set +# CONFIG_HID_ROCCAT is not set +# CONFIG_HID_ROCCAT_KONE is not set +# CONFIG_HID_ROCCAT_PYRA is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SONY is not set +# CONFIG_HID_STANTUM is not set +# CONFIG_HID_SUNPLUS is not set +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_ZEROPLUS is not set +# CONFIG_HID_ZYDACRON is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_SUSPEND=y +CONFIG_USB_OTG=y +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +CONFIG_USB_MON=y +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_TT_NEWSCHED=y +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_OHCI_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HWA_HCD is not set +CONFIG_USB_MUSB_HDRC=y + +# +# Platform Glue Layer +# +# CONFIG_USB_MUSB_TUSB6010_GLUE is not set +CONFIG_USB_MUSB_OMAP2PLUS_GLUE=y +# CONFIG_USB_MUSB_AM35X_GLUE is not set +# CONFIG_USB_MUSB_DAVINCI is not set +# CONFIG_USB_MUSB_DA8XX is not set +# CONFIG_USB_MUSB_TUSB6010 is not set +CONFIG_USB_MUSB_OMAP2PLUS=y +# CONFIG_USB_MUSB_AM35X is not set +# CONFIG_USB_MUSB_TI81XX is not set +# CONFIG_USB_MUSB_BLACKFIN is not set +# CONFIG_USB_MUSB_UX500 is not set +# CONFIG_USB_MUSB_HOST is not set +# CONFIG_USB_MUSB_PERIPHERAL is not set +CONFIG_USB_MUSB_OTG=y +CONFIG_USB_GADGET_MUSB_HDRC=y +CONFIG_USB_MUSB_HDRC_HCD=y +# CONFIG_MUSB_PIO_ONLY is not set +CONFIG_USB_INVENTRA_DMA_HW=y +# CONFIG_USB_TI_CPPI_DMA_HW is not set +# CONFIG_USB_TI_CPPI41_DMA_HW is not set +CONFIG_USB_INVENTRA_DMA=y +CONFIG_MUSB_USE_SYSTEM_DMA_WORKAROUND=y +# CONFIG_USB_TI_CPPI_DMA is not set +# CONFIG_USB_TI_CPPI41_DMA is not set +# CONFIG_USB_TUSB_OMAP_DMA is not set +# CONFIG_USB_MUSB_DEBUG is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_UAS is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=2 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_OMAP is not set +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_ANDROID=y +# CONFIG_USB_ANDROID_ACM is not set +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +# CONFIG_USB_ANDROID_MTP is not set +# CONFIG_USB_ANDROID_RNDIS is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set + +# +# OTG and related infrastructure +# +CONFIG_USB_OTG_UTILS=y +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_ISP1301_OMAP is not set +# CONFIG_USB_ULPI is not set +CONFIG_TWL4030_USB=y +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_EMBEDDED_SDIO is not set +# CONFIG_MMC_PARANOID_SD_INIT is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +CONFIG_SDIO_UART=y +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_OMAP=y +CONFIG_MMC_OMAP_HS=y +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_USHC is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +CONFIG_SWITCH=y +# CONFIG_SWITCH_GPIO is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +CONFIG_RTC_DRV_TWL4030=y +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_VIDEO_CPIA is not set +# CONFIG_USB_IP_COMMON is not set +# CONFIG_ECHO is not set +# CONFIG_BRCM80211 is not set +# CONFIG_RT2870 is not set +# CONFIG_COMEDI is not set +# CONFIG_ASUS_OLED is not set +# CONFIG_R8712U is not set +# CONFIG_TRANZPORT is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE=128 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE=16 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE=8 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_POHMELFS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_LINE6_USB is not set +# CONFIG_VT6656 is not set +# CONFIG_FB_UDL is not set +# CONFIG_IIO is not set +# CONFIG_ZRAM is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_FB_SM7XX is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_ADIS16255 is not set +# CONFIG_SMB_FS is not set +# CONFIG_EASYCAP is not set +# CONFIG_TIDSPBRIDGE is not set +# CONFIG_WESTBRIDGE is not set +CONFIG_WESTBRIDGE_HAL_SELECTED=y +CONFIG_MACH_OMAP3_WESTBRIDGE_AST_PNAND_HAL=y +# CONFIG_MACH_NO_WESTBRIDGE is not set +# CONFIG_ATH6K_LEGACY is not set +# CONFIG_USB_ENESTORAGE is not set +# CONFIG_BCM_WIMAX is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_SPEAKUP is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_DEFAULTS_TO_ORDERED=y +# CONFIG_EXT3_FS_XATTR is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +CONFIG_FS_POSIX_ACL=y +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +CONFIG_QUOTA=y +# CONFIG_QUOTA_NETLINK_INTERFACE is not set +CONFIG_PRINT_QUOTA_WARNING=y +# CONFIG_QUOTA_DEBUG is not set +CONFIG_QUOTA_TREE=y +# CONFIG_QFMT_V1 is not set +CONFIG_QFMT_V2=y +CONFIG_QUOTACTL=y +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_ECRYPT_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_YAFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +CONFIG_JFFS2_SUMMARY=y +CONFIG_JFFS2_FS_XATTR=y +CONFIG_JFFS2_FS_POSIX_ACL=y +CONFIG_JFFS2_FS_SECURITY=y +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_LZO=y +CONFIG_JFFS2_RTIME=y +CONFIG_JFFS2_RUBIN=y +# CONFIG_JFFS2_CMODE_NONE is not set +CONFIG_JFFS2_CMODE_PRIORITY=y +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_LOGFS is not set +CONFIG_CRAMFS=y +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +# CONFIG_NFS_V4_1 is not set +CONFIG_ROOT_NFS=y +CONFIG_NFS_USE_LEGACY_DNS=y +# CONFIG_NFS_USE_NEW_IDMAPPER is not set +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_ACL_SUPPORT=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_CEPH_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +CONFIG_BKL=y +# CONFIG_SPARSE_RCU_POINTER is not set +CONFIG_STACKTRACE=y +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_MEMORY_INIT is not set +CONFIG_FRAME_POINTER=y +# CONFIG_LKDTM is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +CONFIG_NOP_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_RING_BUFFER_ALLOW_SWAP=y +CONFIG_TRACING=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_ENABLE_DEFAULT_TRACERS is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +CONFIG_KPROBE_EVENT=y +# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_ARM_UNWIND is not set +# CONFIG_DEBUG_USER is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +CONFIG_KEYS=y +# CONFIG_KEYS_DEBUG_PROC_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +CONFIG_SECURITY=y +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_NETWORK is not set +# CONFIG_SECURITY_PATH is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_APPARMOR is not set +# CONFIG_IMA is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_HW=y +# CONFIG_CRYPTO_DEV_OMAP_SHAM is not set +# CONFIG_CRYPTO_DEV_OMAP_AES is not set +CONFIG_BINARY_PRINTF=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +# CONFIG_CRC16 is not set +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/kernel/arch/arm/configs/panda_defconfig b/kernel/arch/arm/configs/panda_defconfig new file mode 100644 index 000000000000..4c5e56c56cf6 --- /dev/null +++ b/kernel/arch/arm/configs/panda_defconfig @@ -0,0 +1,331 @@ +CONFIG_EXPERIMENTAL=y +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_KALLSYMS_ALL=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_ASHMEM=y +# CONFIG_AIO is not set +CONFIG_EMBEDDED=y +# CONFIG_SLUB_DEBUG is not set +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_ARCH_OMAP=y +CONFIG_OMAP_RESET_CLOCKS=y +# CONFIG_ARCH_OMAP2 is not set +# CONFIG_ARCH_OMAP3 is not set +# CONFIG_MACH_OMAP_4430SDP is not set +CONFIG_ARM_THUMBEE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_SMP=y +# CONFIG_SMP_ON_UP is not set +CONFIG_NR_CPUS=2 +CONFIG_PREEMPT=y +CONFIG_CMDLINE="console=ttyO2,115200n8 mem=1G androidboot.console=ttyO2" +CONFIG_CMDLINE_EXTEND=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_IDLE=y +CONFIG_OMAP_SMARTREFLEX=y +CONFIG_OMAP_SMARTREFLEX_CLASS1P5=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_BINFMT_MISC=y +CONFIG_WAKELOCK=y +CONFIG_PM_DEBUG=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_INET_ESP=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_REJECT_SKERR=y +CONFIG_IP_NF_TARGET_LOG=y +CONFIG_NF_NAT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_TARGET_REJECT_SKERR=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_PHONET=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_BT=y +CONFIG_BT_BNEP=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_WILINK=y +CONFIG_RFKILL=y +CONFIG_RFKILL_INPUT=y +CONFIG_MTD=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_NAND_IDS=y +CONFIG_MTD_ONENAND=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_MISC_DEVICES=y +# CONFIG_ANDROID_PMEM is not set +CONFIG_KERNEL_DEBUGGER_CORE=y +CONFIG_UID_STAT=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_IFB=y +CONFIG_USB_USBNET=y +CONFIG_USB_NET_SMSC95XX=y +CONFIG_PPP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_HW_RANDOM=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_GPIO=y +CONFIG_SPI=y +CONFIG_SPI_GPIO=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_TWL4030=y +CONFIG_POWER_SUPPLY=y +# CONFIG_HWMON is not set +CONFIG_TWL6030_PWM=y +CONFIG_REGULATOR_TWL4030=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_PVR_SGX=y +CONFIG_PVR_NEED_PVR_DPF=y +CONFIG_PVR_NEED_PVR_ASSERT=y +CONFIG_PVR_USSE_EDM_STATUS_DEBUG=y +CONFIG_FB=y +CONFIG_OMAP2_DSS=y +# CONFIG_OMAP2_DSS_VENC is not set +CONFIG_FB_OMAP2=y +CONFIG_FB_OMAP2_NUM_FBS=2 +CONFIG_OMAP2_VRAM_SIZE=16 +CONFIG_PANEL_GENERIC_DPI=y +CONFIG_DISPLAY_SUPPORT=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_DEVICEFS=y +CONFIG_USB_SUSPEND=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_OMAP2PLUS=y +CONFIG_USB_MUSB_PERIPHERAL=y +CONFIG_USB_GADGET_MUSB_HDRC=y +CONFIG_USB_ACM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_KEYSPAN=y +CONFIG_USB_SERIAL_KEYSPAN_MPR=y +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_G_ANDROID=y +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_OMAP=y +CONFIG_MMC_OMAP_HS=y +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +CONFIG_RTC_CLASS=y +CONFIG_STAGING=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_EXT2_FS=y +CONFIG_EXT4_FS=y +# CONFIG_EXT4_FS_XATTR is not set +# CONFIG_DNOTIFY is not set +CONFIG_FUSE_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_PARTITION_ADVANCED=y +CONFIG_EFI_PARTITION=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_KERNEL=y +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_DEBUG_RT_MUTEXES=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_SPINLOCK_SLEEP=y +CONFIG_DEBUG_INFO=y +CONFIG_SYSCTL_SYSCALL_CHECK=y +# CONFIG_ARM_UNWIND is not set +CONFIG_DEBUG_USER=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRC_CCITT=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_OMAP_SOC=y +CONFIG_SND_OMAP_SOC_SDP4430=y +CONFIG_SND_OMAP_SOC_OMAP4_HDMI=y +CONFIG_OMAP_HSI=y +CONFIG_OMAP_HSI_DEVICE=y +CONFIG_CFG80211=y +CONFIG_NL80211_TESTMODE=y +CONFIG_LIB80211=y +CONFIG_MAC80211=y +CONFIG_MAC80211_LEDS=y +CONFIG_MAC80211_DEBUGFS=y +CONFIG_USB_ZD1201=y +CONFIG_WL12XX_MENU=y +CONFIG_WL12XX=y +CONFIG_WL12XX_SDIO=y +CONFIG_CRYPTO_PCBC=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MICHAEL_MIC=y +CONFIG_CRYPTO_SHA256=y +CONFIG_OMAP_TEMP_SENSOR=y +CONFIG_OMAP_DIE_TEMP_SENSOR=y +CONFIG_TI_ST=y +CONFIG_KEYBOARD_GPIO=y diff --git a/kernel/arch/arm/mach-omap2/board-omap3beagle.c b/kernel/arch/arm/mach-omap2/board-omap3beagle.c new file mode 100644 index 000000000000..b3d1b81b2a2e --- /dev/null +++ b/kernel/arch/arm/mach-omap2/board-omap3beagle.c @@ -0,0 +1,1038 @@ +/* + * linux/arch/arm/mach-omap2/board-omap3beagle.c + * + * Copyright (C) 2008 Texas Instruments + * + * Modified from mach-omap2/board-3430sdp.c + * + * Initial code: Syed Mohammed Khasim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/leds.h> +#include <linux/gpio.h> +#include <linux/input.h> +#include <linux/gpio_keys.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/nand.h> +#include <linux/mmc/host.h> + +#include <linux/usb/android_composite.h> + +#include <linux/regulator/machine.h> +#include <linux/i2c/twl.h> + +#include <mach/hardware.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/flash.h> + +#include <plat/board.h> +#include <plat/common.h> +#include <plat/display.h> +#include <plat/gpmc.h> +#include <plat/nand.h> +#include <plat/usb.h> + +#include "mux.h" +#include "hsmmc.h" +#include "timer-gp.h" +#include "board-flash.h" + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 +#include <linux/input/synaptics_dsx.h> + +#define TM_SAMPLE1 (1) // 2D only +#define TM_SAMPLE2 (2) // 2D + 0D x 2 +#define TM_SAMPLE3 (3) // 2D + 0D x 4 +#define SYNAPTICS_MODULE TM_SAMPLE1 +#endif + +#define NAND_BLOCK_SIZE SZ_128K + +#ifdef CONFIG_USB_ANDROID +#define GOOGLE_VENDOR_ID 0x18d1 +#define GOOGLE_PRODUCT_ID 0x9018 +#define GOOGLE_ADB_PRODUCT_ID 0x9015 +#endif + +/* Synaptics Thin Driver */ +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 +static int synaptics_gpio_setup(unsigned gpio, bool configure) +{ + int retval=0; + if (configure) + { + retval = gpio_request(gpio, "rmi4_attn"); + if (retval) { + pr_err("%s: Failed to get attn gpio %d. Code: %d.", + __func__, gpio, retval); + return retval; + } + omap_mux_init_signal("sdmmc2_clk.gpio_130", OMAP_PIN_INPUT_PULLUP); + + retval = gpio_direction_input(gpio); + if (retval) { + pr_err("%s: Failed to setup attn gpio %d. Code: %d.", + __func__, gpio, retval); + gpio_free(gpio); + } + } else { + pr_warn("%s: No way to deconfigure gpio %d.", + __func__, gpio); + } + + return retval; +} + + #if (SYNAPTICS_MODULE == TM_SAMPLE1) +#define TM_SAMPLE1_ADDR 0x20 +#define TM_SAMPLE1_ATTN 130 + +static unsigned char TM_SAMPLE1_f1a_button_codes[] = {}; + +static struct synaptics_rmi4_capacitance_button_map TM_SAMPLE1_capacitance_button_map = { + .nbuttons = ARRAY_SIZE(TM_SAMPLE1_f1a_button_codes), + .map = TM_SAMPLE1_f1a_button_codes, +}; + +static struct synaptics_rmi4_platform_data rmi4_platformdata = { + .irq_flags = IRQF_TRIGGER_FALLING, + .irq_gpio = TM_SAMPLE1_ATTN, + .gpio_config = synaptics_gpio_setup, + .capacitance_button_map = &TM_SAMPLE1_capacitance_button_map, +}; + +static struct i2c_board_info bus2_i2c_devices[] = { + { + I2C_BOARD_INFO("synaptics_rmi4_i2c", TM_SAMPLE1_ADDR), + .platform_data = &rmi4_platformdata, + }, +}; + +#elif (SYNAPTICS_MODULE == TM_SAMPLE2) +#define TM_SAMPLE2_ADDR 0x20 +#define TM_SAMPLE2_ATTN 130 + +static unsigned char TM_SAMPLE2_f1a_button_codes[] = {KEY_MENU, KEY_BACK}; + +static struct synaptics_rmi4_capacitance_button_map TM_SAMPLE2_capacitance_button_map = { + .nbuttons = ARRAY_SIZE(TM_SAMPLE2_f1a_button_codes), + .map = TM_SAMPLE2_f1a_button_codes, +}; + +static struct synaptics_rmi4_platform_data rmi4_platformdata = { + .irq_flags = IRQF_TRIGGER_FALLING, + .irq_gpio = TM_SAMPLE2_ATTN, + .gpio_config = synaptics_gpio_setup, + .capacitance_button_map = &TM_SAMPLE2_capacitance_button_map, +}; + +static struct i2c_board_info bus2_i2c_devices[] = { + { + I2C_BOARD_INFO("synaptics_rmi4_i2c", TM_SAMPLE2_ADDR), + .platform_data = &rmi4_platformdata, + }, +}; + +#elif (SYNAPTICS_MODULE == TM_SAMPLE3) +#define TM_SAMPLE3_ADDR 0x20 +#define TM_SAMPLE3_ATTN 130 + +static unsigned char TM_SAMPLE3_f1a_button_codes[] = {KEY_MENU, KEY_HOME,KEY_BACK,KEY_SEARCH}; + +static struct synaptics_rmi4_capacitance_button_map TM_SAMPLE3_capacitance_button_map = { + .nbuttons = ARRAY_SIZE(TM_SAMPLE3_f1a_button_codes), + .map = TM_SAMPLE3_f1a_button_codes, +}; + +static struct synaptics_rmi4_platform_data rmi4_platformdata = { + .irq_flags = IRQF_TRIGGER_FALLING, + .irq_gpio = TM_SAMPLE3_ATTN, + .gpio_config = synaptics_gpio_setup, + .capacitance_button_map = &TM_SAMPLE3_capacitance_button_map, +}; + +static struct i2c_board_info bus2_i2c_devices[] = { + { + I2C_BOARD_INFO("synaptics_rmi4_i2c", TM_SAMPLE3_ADDR), + .platform_data = &rmi4_platformdata, + }, +}; +#endif + +void __init i2c_device_setup(void) +{ + pr_info(">>>>I2C device setup."); + if (ARRAY_SIZE(bus2_i2c_devices)) { + i2c_register_board_info(2, bus2_i2c_devices, + ARRAY_SIZE(bus2_i2c_devices)); + } +} + +/* End of Synaptics change for beagle board */ + +static char *usb_functions_adb[] = { + "adb", +}; + +static char *usb_functions_mass_storage[] = { + "usb_mass_storage", +}; +static char *usb_functions_ums_adb[] = { + "usb_mass_storage", + "adb", +}; + +static char *usb_functions_all[] = { + "adb", "usb_mass_storage", +}; + +static struct android_usb_product usb_products[] = { + { + .product_id = GOOGLE_PRODUCT_ID, + .num_functions = ARRAY_SIZE(usb_functions_adb), + .functions = usb_functions_adb, + }, + { + .product_id = GOOGLE_PRODUCT_ID, + .num_functions = ARRAY_SIZE(usb_functions_mass_storage), + .functions = usb_functions_mass_storage, + }, + { + .product_id = GOOGLE_PRODUCT_ID, + .num_functions = ARRAY_SIZE(usb_functions_ums_adb), + .functions = usb_functions_ums_adb, + }, +}; + +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .vendor = "rowboat", + .product = "rowboat gadget", + .release = 0x100, +}; + +static struct platform_device usb_mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &mass_storage_pdata, + }, +}; + +static struct android_usb_platform_data android_usb_pdata = { + .vendor_id = GOOGLE_VENDOR_ID, + .product_id = GOOGLE_PRODUCT_ID, + .functions = usb_functions_all, + .products = usb_products, + .num_products = ARRAY_SIZE(usb_products), + .version = 0x0100, + .product_name = "rowboat gadget", + .manufacturer_name = "rowboat", + .serial_number = "20100720", + .num_functions = ARRAY_SIZE(usb_functions_all), +}; + +static struct platform_device androidusb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; + +static void omap3beagle_android_gadget_init(void) +{ + platform_device_register(&androidusb_device); +} +#endif +/* + * OMAP3 Beagle revision + * Run time detection of Beagle revision is done by reading GPIO. + * GPIO ID - + * AXBX = GPIO173, GPIO172, GPIO171: 1 1 1 + * C1_3 = GPIO173, GPIO172, GPIO171: 1 1 0 + * C4 = GPIO173, GPIO172, GPIO171: 1 0 1 + * XM = GPIO173, GPIO172, GPIO171: 0 0 0 + */ +enum { + OMAP3BEAGLE_BOARD_UNKN = 0, + OMAP3BEAGLE_BOARD_AXBX, + OMAP3BEAGLE_BOARD_C1_3, + OMAP3BEAGLE_BOARD_C4, + OMAP3BEAGLE_BOARD_XM, + OMAP3BEAGLE_BOARD_XMC, +}; + +extern void omap_pm_sys_offmode_select(int); +extern void omap_pm_sys_offmode_pol(int); +extern void omap_pm_sys_clkreq_pol(int); +extern void omap_pm_auto_off(int); +extern void omap_pm_auto_ret(int); + +static u8 omap3_beagle_version; + +static u8 omap3_beagle_get_rev(void) +{ + return omap3_beagle_version; +} + +/** + * Board specific initialization of PM components + */ +static void __init omap3_beagle_pm_init(void) +{ + /* Use sys_offmode signal */ + omap_pm_sys_offmode_select(1); + + /* sys_clkreq - active high */ + omap_pm_sys_clkreq_pol(1); + + /* sys_offmode - active low */ + omap_pm_sys_offmode_pol(0); + + /* Automatically send OFF command */ + omap_pm_auto_off(1); + + /* Automatically send RET command */ + omap_pm_auto_ret(1); +} + +static void __init omap3_beagle_init_rev(void) +{ + int ret; + u16 beagle_rev = 0; + + omap_mux_init_gpio(171, OMAP_PIN_INPUT_PULLUP); + omap_mux_init_gpio(172, OMAP_PIN_INPUT_PULLUP); + omap_mux_init_gpio(173, OMAP_PIN_INPUT_PULLUP); + + ret = gpio_request(171, "rev_id_0"); + if (ret < 0) + goto fail0; + + ret = gpio_request(172, "rev_id_1"); + if (ret < 0) + goto fail1; + + ret = gpio_request(173, "rev_id_2"); + if (ret < 0) + goto fail2; + + gpio_direction_input(171); + gpio_direction_input(172); + gpio_direction_input(173); + + beagle_rev = gpio_get_value(171) | (gpio_get_value(172) << 1) + | (gpio_get_value(173) << 2); + + switch (beagle_rev) { + case 7: + printk(KERN_INFO "OMAP3 Beagle Rev: Ax/Bx\n"); + omap3_beagle_version = OMAP3BEAGLE_BOARD_AXBX; + break; + case 6: + printk(KERN_INFO "OMAP3 Beagle Rev: C1/C2/C3\n"); + omap3_beagle_version = OMAP3BEAGLE_BOARD_C1_3; + break; + case 5: + printk(KERN_INFO "OMAP3 Beagle Rev: C4\n"); + omap3_beagle_version = OMAP3BEAGLE_BOARD_C4; + break; + case 2: + printk(KERN_INFO "OMAP3 Beagle Rev: xM C\n"); + omap3_beagle_version = OMAP3BEAGLE_BOARD_XMC; + break; + case 0: + printk(KERN_INFO "OMAP3 Beagle Rev: xM\n"); + omap3_beagle_version = OMAP3BEAGLE_BOARD_XM; + break; + default: + printk(KERN_INFO "OMAP3 Beagle Rev: unknown %hd\n", beagle_rev); + omap3_beagle_version = OMAP3BEAGLE_BOARD_UNKN; + } + + return; + +fail2: + gpio_free(172); +fail1: + gpio_free(171); +fail0: + printk(KERN_ERR "Unable to get revision detection GPIO pins\n"); + omap3_beagle_version = OMAP3BEAGLE_BOARD_UNKN; + + return; +} + +static struct mtd_partition omap3beagle_nand_partitions[] = { + /* All the partition sizes are listed in terms of NAND block size */ + { + .name = "X-Loader", + .offset = 0, + .size = 4 * NAND_BLOCK_SIZE, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "U-Boot", + .offset = MTDPART_OFS_APPEND, /* Offset = 0x80000 */ + .size = 15 * NAND_BLOCK_SIZE, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "U-Boot Env", + .offset = MTDPART_OFS_APPEND, /* Offset = 0x260000 */ + .size = 1 * NAND_BLOCK_SIZE, + }, + { + .name = "Kernel", + .offset = MTDPART_OFS_APPEND, /* Offset = 0x280000 */ + .size = 32 * NAND_BLOCK_SIZE, + }, + { + .name = "File System", + .offset = MTDPART_OFS_APPEND, /* Offset = 0x680000 */ + .size = MTDPART_SIZ_FULL, + }, +}; + +/* DSS */ + +static int beagle_enable_dvi(struct omap_dss_device *dssdev) +{ + if (gpio_is_valid(dssdev->reset_gpio)) + gpio_set_value(dssdev->reset_gpio, 1); + + return 0; +} + +static void beagle_disable_dvi(struct omap_dss_device *dssdev) +{ + if (gpio_is_valid(dssdev->reset_gpio)) + gpio_set_value(dssdev->reset_gpio, 0); +} + +static struct omap_dss_device beagle_dvi_device = { + .type = OMAP_DISPLAY_TYPE_DPI, + .name = "dvi", + .driver_name = "generic_panel", + .phy.dpi.data_lines = 24, + .reset_gpio = -EINVAL, + .platform_enable = beagle_enable_dvi, + .platform_disable = beagle_disable_dvi, +}; + +static struct omap_dss_device beagle_tv_device = { + .name = "tv", + .driver_name = "venc", + .type = OMAP_DISPLAY_TYPE_VENC, + .phy.venc.type = OMAP_DSS_VENC_TYPE_SVIDEO, +}; + +static struct omap_dss_device *beagle_dss_devices[] = { + &beagle_dvi_device, + &beagle_tv_device, +}; + +static struct omap_dss_board_info beagle_dss_data = { + .num_devices = ARRAY_SIZE(beagle_dss_devices), + .devices = beagle_dss_devices, + .default_device = &beagle_dvi_device, +}; + +static struct platform_device beagle_dss_device = { + .name = "omapdss", + .id = -1, + .dev = { + .platform_data = &beagle_dss_data, + }, +}; + +static struct regulator_consumer_supply beagle_vdac_supply = + REGULATOR_SUPPLY("vdda_dac", "omapdss"); + +static struct regulator_consumer_supply beagle_vdvi_supply = + REGULATOR_SUPPLY("vdds_dsi", "omapdss"); + +static void __init beagle_display_init(void) +{ + int r; + + r = gpio_request(beagle_dvi_device.reset_gpio, "DVI reset"); + if (r < 0) { + printk(KERN_ERR "Unable to get DVI reset GPIO\n"); + return; + } + + gpio_direction_output(beagle_dvi_device.reset_gpio, 0); +} + +#include "sdram-micron-mt46h32m32lf-6.h" + +static struct omap2_hsmmc_info mmc[] = { + { + .mmc = 1, + .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA, + .gpio_wp = 29, + }, + {} /* Terminator */ +}; + +static struct regulator_consumer_supply beagle_vmmc1_supply = { + .supply = "vmmc", +}; + +static struct regulator_consumer_supply beagle_vsim_supply = { + .supply = "vmmc_aux", +}; + +static struct regulator_consumer_supply beagle_vaux3_supply = { + .supply = "cam_1v8", +}; + +static struct regulator_consumer_supply beagle_vaux4_supply = { + .supply = "cam_2v8", +}; + +static struct gpio_led gpio_leds[]; + +static int beagle_twl_gpio_setup(struct device *dev, + unsigned gpio, unsigned ngpio) +{ + if (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XM || omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XMC) { + mmc[0].gpio_wp = -EINVAL; + } else if ((omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_C1_3) || + (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_C4)) { + omap_mux_init_gpio(23, OMAP_PIN_INPUT); + mmc[0].gpio_wp = 23; + } else { + omap_mux_init_gpio(29, OMAP_PIN_INPUT); + } + /* gpio + 0 is "mmc0_cd" (input/IRQ) */ + mmc[0].gpio_cd = gpio + 0; + omap2_hsmmc_init(mmc); + + /* link regulators to MMC adapters */ + beagle_vmmc1_supply.dev = mmc[0].dev; + beagle_vsim_supply.dev = mmc[0].dev; + + /* REVISIT: need ehci-omap hooks for external VBUS + * power switch and overcurrent detect + */ + if (omap3_beagle_get_rev() != OMAP3BEAGLE_BOARD_XM || omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XMC) { + gpio_request(gpio + 1, "EHCI_nOC"); + gpio_direction_input(gpio + 1); + } + + /* + * TWL4030_GPIO_MAX + 0 == ledA, EHCI nEN_USB_PWR (out, XM active + * high / others active low) + */ + gpio_request(gpio + TWL4030_GPIO_MAX, "nEN_USB_PWR"); + gpio_direction_output(gpio + TWL4030_GPIO_MAX, 0); + if (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XM) + gpio_direction_output(gpio + TWL4030_GPIO_MAX, 1); + else + gpio_direction_output(gpio + TWL4030_GPIO_MAX, 0); + + /* DVI reset GPIO is different between beagle revisions */ + if (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XM || omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XMC) + beagle_dvi_device.reset_gpio = 129; + else + beagle_dvi_device.reset_gpio = 170; + + if (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XM) { + /* Power on camera interface */ + gpio_request(gpio + 2, "CAM_EN"); + gpio_direction_output(gpio + 2, 1); + + /* TWL4030_GPIO_MAX + 0 == ledA, EHCI nEN_USB_PWR (out, active low) */ + gpio_request(gpio + TWL4030_GPIO_MAX, "nEN_USB_PWR"); + gpio_direction_output(gpio + TWL4030_GPIO_MAX, 1); + } else { + gpio_request(gpio + 1, "EHCI_nOC"); + gpio_direction_input(gpio + 1); + + /* TWL4030_GPIO_MAX + 0 == ledA, EHCI nEN_USB_PWR (out, active low) */ + gpio_request(gpio + TWL4030_GPIO_MAX, "nEN_USB_PWR"); + gpio_direction_output(gpio + TWL4030_GPIO_MAX, 0); + } + /* TWL4030_GPIO_MAX + 1 == ledB, PMU_STAT (out, active low LED) */ + gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1; + + /* + * gpio + 1 on Xm controls the TFP410's enable line (active low) + * gpio + 2 control varies depending on the board rev as follows: + * P7/P8 revisions(prototype): Camera EN + * A2+ revisions (production): LDO (supplies DVI, serial, led blocks) + */ + if (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XM || omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XMC) { + gpio_request(gpio + 1, "nDVI_PWR_EN"); + gpio_direction_output(gpio + 1, 0); + gpio_request(gpio + 2, "DVI_LDO_EN"); + gpio_direction_output(gpio + 2, 1); + } + + return 0; +} + +static struct twl4030_gpio_platform_data beagle_gpio_data = { + .gpio_base = OMAP_MAX_GPIO_LINES, + .irq_base = TWL4030_GPIO_IRQ_BASE, + .irq_end = TWL4030_GPIO_IRQ_END, + .use_leds = true, + .pullups = BIT(1), + .pulldowns = BIT(2) | BIT(6) | BIT(7) | BIT(8) | BIT(13) + | BIT(15) | BIT(16) | BIT(17), + .setup = beagle_twl_gpio_setup, +}; + +/* VMMC1 for MMC1 pins CMD, CLK, DAT0..DAT3 (20 mA, plus card == max 220 mA) */ +static struct regulator_init_data beagle_vmmc1 = { + .constraints = { + .min_uV = 1850000, + .max_uV = 3150000, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE + | REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &beagle_vmmc1_supply, +}; + +/* VSIM for MMC1 pins DAT4..DAT7 (2 mA, plus card == max 50 mA) */ +static struct regulator_init_data beagle_vsim = { + .constraints = { + .min_uV = 1800000, + .max_uV = 3000000, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE + | REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &beagle_vsim_supply, +}; + +/* VDAC for DSS driving S-Video (8 mA unloaded, max 65 mA) */ +static struct regulator_init_data beagle_vdac = { + .constraints = { + .min_uV = 1800000, + .max_uV = 1800000, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &beagle_vdac_supply, +}; + +/* VPLL2 for digital video outputs */ +static struct regulator_init_data beagle_vpll2 = { + .constraints = { + .name = "VDVI", + .min_uV = 1800000, + .max_uV = 1800000, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &beagle_vdvi_supply, +}; + +/* VAUX3 for CAM_1V8 */ +static struct regulator_init_data beagle_vaux3 = { + .constraints = { + .min_uV = 1800000, + .max_uV = 1800000, + .apply_uV = true, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &beagle_vaux3_supply, +}; + + /* VAUX4 for CAM_2V8 */ +static struct regulator_init_data beagle_vaux4 = { + .constraints = { + .min_uV = 1800000, + .max_uV = 1800000, + .apply_uV = true, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &beagle_vaux4_supply, +}; + +static struct twl4030_usb_data beagle_usb_data = { + .usb_mode = T2_USB_MODE_ULPI, +}; + +/** + * Macro to configure resources + */ +#define TWL4030_RESCONFIG(res,grp,typ1,typ2,state) \ + { \ + .resource = res, \ + .devgroup = grp, \ + .type = typ1, \ + .type2 = typ2, \ + .remap_sleep = state \ + } + +static struct twl4030_resconfig __initdata board_twl4030_rconfig[] = { + TWL4030_RESCONFIG(RES_VPLL1, DEV_GRP_P1, 3, 1, RES_STATE_OFF), /* ? */ + TWL4030_RESCONFIG(RES_VINTANA1, DEV_GRP_ALL, 1, 2, RES_STATE_SLEEP), + TWL4030_RESCONFIG(RES_VINTANA2, DEV_GRP_ALL, 0, 2, RES_STATE_SLEEP), + TWL4030_RESCONFIG(RES_VINTDIG, DEV_GRP_ALL, 1, 2, RES_STATE_SLEEP), + TWL4030_RESCONFIG(RES_VIO, DEV_GRP_ALL, 2, 2, RES_STATE_SLEEP), + TWL4030_RESCONFIG(RES_VDD1, DEV_GRP_P1, 4, 1, RES_STATE_OFF), /* ? */ + TWL4030_RESCONFIG(RES_VDD2, DEV_GRP_P1, 3, 1, RES_STATE_OFF), /* ? */ + TWL4030_RESCONFIG(RES_REGEN, DEV_GRP_ALL, 2, 1, RES_STATE_SLEEP), + TWL4030_RESCONFIG(RES_NRES_PWRON, DEV_GRP_ALL, 0, 1, RES_STATE_SLEEP), + TWL4030_RESCONFIG(RES_CLKEN, DEV_GRP_ALL, 3, 2, RES_STATE_SLEEP), + TWL4030_RESCONFIG(RES_SYSEN, DEV_GRP_ALL, 6, 1, RES_STATE_SLEEP), + TWL4030_RESCONFIG(RES_HFCLKOUT, DEV_GRP_P3, 0, 2, RES_STATE_SLEEP), /* ? */ + TWL4030_RESCONFIG(0, 0, 0, 0, 0), +}; + +/** + * Optimized 'Active to Sleep' sequence + */ +static struct twl4030_ins omap3beagle_sleep_seq[] __initdata = { + { MSG_SINGULAR(DEV_GRP_NULL, RES_HFCLKOUT, RES_STATE_SLEEP), 20}, + { MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R1, RES_STATE_SLEEP), 2 }, + { MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R2, RES_STATE_SLEEP), 2 }, +}; + +static struct twl4030_script omap3beagle_sleep_script __initdata = { + .script = omap3beagle_sleep_seq, + .size = ARRAY_SIZE(omap3beagle_sleep_seq), + .flags = TWL4030_SLEEP_SCRIPT, +}; + +/** + * Optimized 'Sleep to Active (P12)' sequence + */ +static struct twl4030_ins omap3beagle_wake_p12_seq[] __initdata = { + { MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R1, RES_STATE_ACTIVE), 2 } +}; + +static struct twl4030_script omap3beagle_wake_p12_script __initdata = { + .script = omap3beagle_wake_p12_seq, + .size = ARRAY_SIZE(omap3beagle_wake_p12_seq), + .flags = TWL4030_WAKEUP12_SCRIPT, +}; + +/** + * Optimized 'Sleep to Active' (P3) sequence + */ +static struct twl4030_ins omap3beagle_wake_p3_seq[] __initdata = { + { MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R2, RES_STATE_ACTIVE), 2 } +}; + +static struct twl4030_script omap3beagle_wake_p3_script __initdata = { + .script = omap3beagle_wake_p3_seq, + .size = ARRAY_SIZE(omap3beagle_wake_p3_seq), + .flags = TWL4030_WAKEUP3_SCRIPT, +}; + +/** + * Optimized warm reset sequence (for less power surge) + */ +static struct twl4030_ins omap3beagle_wrst_seq[] __initdata = { + { MSG_SINGULAR(DEV_GRP_NULL, RES_RESET, RES_STATE_OFF), 0x2 }, + { MSG_SINGULAR(DEV_GRP_NULL, RES_MAIN_REF, RES_STATE_WRST), 2 }, + { MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R2, RES_STATE_WRST), 0x2}, + { MSG_SINGULAR(DEV_GRP_NULL, RES_VUSB_3V1, RES_STATE_WRST), 0x2 }, + { MSG_SINGULAR(DEV_GRP_NULL, RES_VPLL1, RES_STATE_WRST), 0x2 }, + { MSG_SINGULAR(DEV_GRP_NULL, RES_VDD2, RES_STATE_WRST), 0x7 }, + { MSG_SINGULAR(DEV_GRP_NULL, RES_VDD1, RES_STATE_WRST), 0x25 }, + { MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_RC, RES_TYPE_ALL, RES_TYPE2_R0, RES_STATE_WRST), 0x2 }, + { MSG_SINGULAR(DEV_GRP_NULL, RES_RESET, RES_STATE_ACTIVE), 0x2 }, + +}; + +static struct twl4030_script omap3beagle_wrst_script __initdata = { + .script = omap3beagle_wrst_seq, + .size = ARRAY_SIZE(omap3beagle_wrst_seq), + .flags = TWL4030_WRST_SCRIPT, +}; + +static struct twl4030_script __initdata *board_twl4030_scripts[] = { + &omap3beagle_wake_p12_script, + &omap3beagle_wake_p3_script, + &omap3beagle_sleep_script, + &omap3beagle_wrst_script +}; + +static struct twl4030_power_data __initdata omap3beagle_script_data = { + .scripts = board_twl4030_scripts, + .num = ARRAY_SIZE(board_twl4030_scripts), + .resource_config = board_twl4030_rconfig, +}; + +static struct twl4030_codec_audio_data beagle_audio_data = { + .audio_mclk = 26000000, + .digimic_delay = 1, + .ramp_delay_value = 1, + .offset_cncl_path = 1, + .check_defaults = false, + .reset_registers = false, + .reset_registers = false, +}; + +static struct twl4030_codec_data beagle_codec_data = { + .audio_mclk = 26000000, + .audio = &beagle_audio_data, +}; + +static struct twl4030_platform_data beagle_twldata = { + .irq_base = TWL4030_IRQ_BASE, + .irq_end = TWL4030_IRQ_END, + + /* platform_data for children goes here */ + .usb = &beagle_usb_data, + .gpio = &beagle_gpio_data, + .codec = &beagle_codec_data, + .vmmc1 = &beagle_vmmc1, + .vsim = &beagle_vsim, + .vdac = &beagle_vdac, + .vpll2 = &beagle_vpll2, + .vaux3 = &beagle_vaux3, + .vaux4 = &beagle_vaux4, + .power = &omap3beagle_script_data, +}; + +static struct i2c_board_info __initdata beagle_i2c_boardinfo[] = { + { + I2C_BOARD_INFO("twl4030", 0x48), + .flags = I2C_CLIENT_WAKE, + .irq = INT_34XX_SYS_NIRQ, + .platform_data = &beagle_twldata, + }, +}; + +static struct i2c_board_info __initdata beagle_i2c_eeprom[] = { + { + I2C_BOARD_INFO("eeprom", 0x50), + }, +}; + +static int __init omap3_beagle_i2c_init(void) +{ + omap_register_i2c_bus(1, 2600, beagle_i2c_boardinfo, + ARRAY_SIZE(beagle_i2c_boardinfo)); + + /* Bus 2 is used for Camera/Sensor interface */ + if (ARRAY_SIZE(bus2_i2c_devices)) + omap_register_i2c_bus(2, 400, bus2_i2c_devices, + ARRAY_SIZE(bus2_i2c_devices)); + else + omap_register_i2c_bus(2, 400, NULL, 0); + + /* Bus 3 is attached to the DVI port where devices like the pico DLP + * projector don't work reliably with 400kHz */ + omap_register_i2c_bus(3, 100, beagle_i2c_eeprom, ARRAY_SIZE(beagle_i2c_eeprom)); + + return 0; +} + +static struct gpio_led gpio_leds[] = { + { + .name = "beagleboard::usr0", + .default_trigger = "heartbeat", + .gpio = 150, + }, + { + .name = "beagleboard::usr1", + .default_trigger = "mmc0", + .gpio = 149, + }, + { + .name = "beagleboard::pmu_stat", + .gpio = -EINVAL, /* gets replaced */ + .active_low = true, + }, +}; + +static struct gpio_led_platform_data gpio_led_info = { + .leds = gpio_leds, + .num_leds = ARRAY_SIZE(gpio_leds), +}; + +static struct platform_device leds_gpio = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &gpio_led_info, + }, +}; + +static struct gpio_keys_button gpio_buttons[] = { + { + .code = KEY_POWER, + .gpio = 4, + .desc = "user", + .wakeup = 1, + }, +}; + +static struct gpio_keys_platform_data gpio_key_info = { + .buttons = gpio_buttons, + .nbuttons = ARRAY_SIZE(gpio_buttons), +}; + +static struct platform_device keys_gpio = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &gpio_key_info, + }, +}; + +static void __init omap3_beagle_init_irq(void) +{ + omap2_init_common_infrastructure(); + omap2_init_common_devices(mt46h32m32lf6_sdrc_params, + mt46h32m32lf6_sdrc_params); + omap_init_irq(); + gpmc_init(); +#ifdef CONFIG_OMAP_32K_TIMER + if (omap3_beagle_version == OMAP3BEAGLE_BOARD_AXBX) + omap2_gp_clockevent_set_gptimer(12); + else + omap2_gp_clockevent_set_gptimer(1); +#endif +} + +static struct platform_device *omap3_beagle_devices[] __initdata = { + &leds_gpio, + &keys_gpio, + &beagle_dss_device, + &usb_mass_storage_device, +}; + +static void __init omap3beagle_flash_init(void) +{ + u8 cs = 0; + u8 nandcs = GPMC_CS_NUM + 1; + + /* find out the chip-select on which NAND exists */ + while (cs < GPMC_CS_NUM) { + u32 ret = 0; + ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); + + if ((ret & 0xC00) == 0x800) { + printk(KERN_INFO "Found NAND on CS%d\n", cs); + if (nandcs > GPMC_CS_NUM) + nandcs = cs; + } + cs++; + } + + if (nandcs > GPMC_CS_NUM) { + printk(KERN_INFO "NAND: Unable to find configuration " + "in GPMC\n "); + return; + } + + if (nandcs < GPMC_CS_NUM) { + printk(KERN_INFO "Registering NAND on CS%d\n", nandcs); + board_nand_init(omap3beagle_nand_partitions, + ARRAY_SIZE(omap3beagle_nand_partitions), + nandcs, NAND_BUSWIDTH_16); + } +} + +static const struct ehci_hcd_omap_platform_data ehci_pdata __initconst = { + + .port_mode[0] = EHCI_HCD_OMAP_MODE_PHY, + .port_mode[1] = EHCI_HCD_OMAP_MODE_PHY, + .port_mode[2] = EHCI_HCD_OMAP_MODE_UNKNOWN, + + .phy_reset = true, + .reset_gpio_port[0] = -EINVAL, + .reset_gpio_port[1] = 147, + .reset_gpio_port[2] = -EINVAL +}; + +#ifdef CONFIG_OMAP_MUX +static struct omap_board_mux board_mux[] __initdata = { + OMAP3_MUX(SYS_NIRQ, OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP | + OMAP_PIN_OFF_INPUT_PULLUP | OMAP_PIN_OFF_OUTPUT_LOW | + OMAP_PIN_OFF_WAKEUPENABLE), + { .reg_offset = OMAP_MUX_TERMINATOR }, +}; +#endif + +static struct omap_musb_board_data musb_board_data = { + .interface_type = MUSB_INTERFACE_ULPI, + .mode = MUSB_OTG, + .power = 100, +}; + +static void __init omap3_beagle_init(void) +{ + omap3_mux_init(board_mux, OMAP_PACKAGE_CBB); + omap3_beagle_init_rev(); + omap3_beagle_i2c_init(); + platform_add_devices(omap3_beagle_devices, + ARRAY_SIZE(omap3_beagle_devices)); + omap_serial_init(); + + omap_mux_init_gpio(170, OMAP_PIN_INPUT); + gpio_request(170, "DVI_nPD"); + /* REVISIT leave DVI powered down until it's needed ... */ + gpio_direction_output(170, true); + + usb_musb_init(&musb_board_data); + usb_ehci_init(&ehci_pdata); + omap3beagle_flash_init(); + + /* Ensure SDRC pins are mux'd for self-refresh */ + omap_mux_init_signal("sdrc_cke0", OMAP_PIN_OUTPUT); + omap_mux_init_signal("sdrc_cke1", OMAP_PIN_OUTPUT); + + beagle_display_init(); +#ifdef CONFIG_USB_ANDROID + omap3beagle_android_gadget_init(); +#endif + omap3_beagle_pm_init(); +} + +MACHINE_START(OMAP3_BEAGLE, "OMAP3 Beagle Board") + /* Maintainer: Syed Mohammed Khasim - http://beagleboard.org */ + .boot_params = 0x80000100, + .map_io = omap3_map_io, + .reserve = omap_reserve, + .init_irq = omap3_beagle_init_irq, + .init_machine = omap3_beagle_init, + .timer = &omap_timer, +MACHINE_END diff --git a/kernel/arch/arm/mach-omap2/board-omap4panda.c b/kernel/arch/arm/mach-omap2/board-omap4panda.c new file mode 100644 index 000000000000..4f8c79ddd650 --- /dev/null +++ b/kernel/arch/arm/mach-omap2/board-omap4panda.c @@ -0,0 +1,1053 @@ +/* + * Board support file for OMAP4430 based PandaBoard. + * + * Copyright (C) 2010 Texas Instruments + * + * Author: David Anders <x0132446@ti.com> + * + * Based on mach-omap2/board-4430sdp.c + * + * Author: Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * Based on mach-omap2/board-3430sdp.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/input.h> +#include <linux/io.h> +#include <linux/leds.h> +#include <linux/gpio.h> +#include <linux/gpio_keys.h> +#include <linux/omapfb.h> +#include <linux/reboot.h> +#include <linux/usb/otg.h> +#include <linux/i2c/twl.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/fixed.h> +#include <linux/wl12xx.h> +#include <linux/memblock.h> +#include <linux/skbuff.h> +#include <linux/ti_wilink_st.h> +#include <linux/platform_data/ram_console.h> + +#include <mach/hardware.h> +#include <mach/omap4-common.h> +#include <mach/emif.h> +#include <mach/lpddr2-elpida.h> +#include <mach/dmm.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <video/omapdss.h> + +#include <plat/board.h> +#include <plat/common.h> +#include <plat/usb.h> +#include <plat/mmc.h> +#include <plat/remoteproc.h> +#include <plat/vram.h> +#include <video/omap-panel-generic-dpi.h> +#include "timer-gp.h" + +#include "hsmmc.h" +#include "control.h" +#include "mux.h" +#include "common-board-devices.h" +#include "prm-regbits-44xx.h" +#include "prm44xx.h" +#include "pm.h" +#include "resetreason.h" + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 +#include <linux/input/synaptics_dsx.h> +#define TM_SAMPLE1 (1) // 2D only +#define TM_SAMPLE2 (2) // 2D + 0D x 2 +#define TM_SAMPLE3 (3) // 2D + 0D x 4 +#define SYNAPTICS_MODULE TM_SAMPLE1 +#endif + +#define PANDA_RAMCONSOLE_START (PLAT_PHYS_OFFSET + SZ_512M) +#define PANDA_RAMCONSOLE_SIZE SZ_2M + +#define GPIO_HUB_POWER 1 +#define GPIO_HUB_NRESET 62 +#define GPIO_WIFI_PMENA 43 +#define GPIO_WIFI_IRQ 53 +#define HDMI_GPIO_CT_CP_HPD 60 +#define HDMI_GPIO_HPD 63 /* Hot plug pin for HDMI */ +#define HDMI_GPIO_LS_OE 41 /* Level shifter for HDMI */ +#define TPS62361_GPIO 7 /* VCORE1 power control */ +#define PANDA_BT_GPIO 46 + + +#define PHYS_ADDR_SMC_SIZE (SZ_1M * 3) +#define PHYS_ADDR_SMC_MEM (0x80000000 + SZ_1G - PHYS_ADDR_SMC_SIZE) +#define OMAP_ION_HEAP_SECURE_INPUT_SIZE (SZ_1M * 90) +#define PHYS_ADDR_DUCATI_SIZE (SZ_1M * 105) +#define PHYS_ADDR_DUCATI_MEM (PHYS_ADDR_SMC_MEM - PHYS_ADDR_DUCATI_SIZE - \ + OMAP_ION_HEAP_SECURE_INPUT_SIZE) + +#define WILINK_UART_DEV_NAME "/dev/ttyO1" + + +/* Synaptics changes for PandaBoard */ +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 +static int synaptics_gpio_setup(unsigned gpio, bool configure) +{ + int retval = 0; + + if (configure) { + retval = gpio_request(gpio, "rmi4_attn"); + if (retval) { + pr_err("%s: Failed to get attn gpio %d (code: %d)", + __func__, gpio, retval); + return retval; + } + omap_mux_init_signal("gpmc_ad15.gpio_39", OMAP_PIN_INPUT_PULLUP); + + retval = gpio_direction_input(gpio); + if (retval) { + pr_err("%s: Failed to setup attn gpio %d (code: %d)", + __func__, gpio, retval); + gpio_free(gpio); + } + } else { + pr_warn("%s: No way to deconfigure gpio %d", + __func__, gpio); + } + + return retval; +} + + #if (SYNAPTICS_MODULE == TM_SAMPLE1) +#define TM_SAMPLE1_ADDR 0x20 +#define TM_SAMPLE1_ATTN 130 + +static unsigned char TM_SAMPLE1_f1a_button_codes[] = {}; + +static struct synaptics_rmi4_capacitance_button_map TM_SAMPLE1_capacitance_button_map = { + .nbuttons = ARRAY_SIZE(TM_SAMPLE1_f1a_button_codes), + .map = TM_SAMPLE1_f1a_button_codes, +}; + +static struct synaptics_rmi4_platform_data rmi4_platformdata = { + .irq_flags = IRQF_TRIGGER_FALLING, + .irq_gpio = TM_SAMPLE1_ATTN, + .gpio_config = synaptics_gpio_setup, + .capacitance_button_map = &TM_SAMPLE1_capacitance_button_map, +}; + +static struct i2c_board_info bus4_i2c_devices[] = { + { + I2C_BOARD_INFO("synaptics_rmi4_i2c", TM_SAMPLE1_ADDR), + .platform_data = &rmi4_platformdata, + }, +}; + +#elif (SYNAPTICS_MODULE == TM_SAMPLE2) +#define TM_SAMPLE2_ADDR 0x20 +#define TM_SAMPLE2_ATTN 130 + +static unsigned char TM_SAMPLE2_f1a_button_codes[] = {KEY_MENU, KEY_BACK}; + +static struct synaptics_rmi4_capacitance_button_map TM_SAMPLE2_capacitance_button_map = { + .nbuttons = ARRAY_SIZE(TM_SAMPLE2_f1a_button_codes), + .map = TM_SAMPLE2_f1a_button_codes, +}; + +static struct synaptics_rmi4_platform_data rmi4_platformdata = { + .irq_flags = IRQF_TRIGGER_FALLING, + .irq_gpio = TM_SAMPLE2_ATTN, + .gpio_config = synaptics_gpio_setup, + .capacitance_button_map = &TM_SAMPLE2_capacitance_button_map, +}; + +static struct i2c_board_info bus4_i2c_devices[] = { + { + I2C_BOARD_INFO("synaptics_rmi4_i2c", TM_SAMPLE2_ADDR), + .platform_data = &rmi4_platformdata, + }, +}; +}; + +#elif (SYNAPTICS_MODULE == TM_SAMPLE3) +#define TM_SAMPLE3_ADDR 0x20 +#define TM_SAMPLE3_ATTN 130 + +static unsigned char TM_SAMPLE3_f1a_button_codes[] = {KEY_MENU, KEY_HOME,KEY_BACK,KEY_SEARCH}; + +static struct synaptics_rmi4_capacitance_button_map TM_SAMPLE3_capacitance_button_map = { + .nbuttons = ARRAY_SIZE(TM_SAMPLE3_f1a_button_codes), + .map = TM_SAMPLE3_f1a_button_codes, +}; + +static struct synaptics_rmi4_platform_data rmi4_platformdata = { + .irq_flags = IRQF_TRIGGER_FALLING, + .irq_gpio = TM_SAMPLE3_ATTN, + .gpio_config = synaptics_gpio_setup, + .capacitance_button_map = &TM_SAMPLE3_capacitance_button_map, +}; + +static struct i2c_board_info bus4_i2c_devices[] = { + { + I2C_BOARD_INFO("synaptics_rmi4_i2c", TM_SAMPLE3_ADDR), + .platform_data = &rmi4_platformdata, + }, +}; +#endif + +void __init i2c_device_setup(void) +{ + pr_info(">>>>I2C device setup"); + if (ARRAY_SIZE(bus4_i2c_devices)) { + i2c_register_board_info(4, bus4_i2c_devices, + ARRAY_SIZE(bus4_i2c_devices)); + } +} +#endif +/* End of Synaptics changes for PandaBoard */ + +static struct gpio_led gpio_leds[] = { + { + .name = "pandaboard::status1", + .default_trigger = "heartbeat", + .gpio = 7, + }, + { + .name = "pandaboard::status2", + .default_trigger = "mmc0", + .gpio = 8, + }, +}; + +static struct gpio_led_platform_data gpio_led_info = { + .leds = gpio_leds, + .num_leds = ARRAY_SIZE(gpio_leds), +}; + +static struct platform_device leds_gpio = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &gpio_led_info, + }, +}; + +/* GPIO_KEY for the panda */ +static struct gpio_keys_button panda_gpio_keys_buttons[] = { + [0] = { + .code = KEY_HOME, + .gpio = 113, + .desc = "user_button", + .active_low = 1, + .debounce_interval = 5, + }, +}; + +static struct gpio_keys_platform_data panda_gpio_keys = { + .buttons = panda_gpio_keys_buttons, + .nbuttons = ARRAY_SIZE(panda_gpio_keys_buttons), + .rep = 0, +}; + +static struct platform_device panda_gpio_keys_device = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &panda_gpio_keys, + }, +}; + +/* TODO: handle suspend/resume here. + * Upon every suspend, make sure the wilink chip is + * capable enough to wake-up the OMAP host. + */ +static int plat_wlink_kim_suspend(struct platform_device *pdev, pm_message_t + state) +{ + return 0; +} + +static int plat_wlink_kim_resume(struct platform_device *pdev) +{ + return 0; +} + +/* wl128x BT, FM, GPS connectivity chip */ +static struct ti_st_plat_data wilink_pdata = { + .nshutdown_gpio = PANDA_BT_GPIO, + .dev_name = WILINK_UART_DEV_NAME, + .flow_cntrl = 1, + .baud_rate = 3686400, + .suspend = plat_wlink_kim_suspend, + .resume = plat_wlink_kim_resume, +}; + +static struct platform_device btwilink_device = { + .name = "btwilink", + .id = -1, +}; + +/* wl127x BT, FM, GPS connectivity chip */ +static struct platform_device wl1271_device = { + .name = "kim", + .id = -1, + .dev.platform_data = &wilink_pdata, +}; + + +static struct platform_device *panda_devices[] __initdata = { + &leds_gpio, + &wl1271_device, + &btwilink_device, + &panda_gpio_keys_device, +}; + +static void __init omap4_panda_init_early(void) +{ + omap2_init_common_infrastructure(); + omap2_init_common_devices(NULL, NULL); +} + +static const struct usbhs_omap_board_data usbhs_bdata __initconst = { + .port_mode[0] = OMAP_EHCI_PORT_MODE_PHY, + .port_mode[1] = OMAP_USBHS_PORT_MODE_UNUSED, + .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED, + .phy_reset = false, + .reset_gpio_port[0] = -EINVAL, + .reset_gpio_port[1] = -EINVAL, + .reset_gpio_port[2] = -EINVAL +}; + +static struct gpio panda_ehci_gpios[] __initdata = { + { GPIO_HUB_POWER, GPIOF_OUT_INIT_LOW, "hub_power" }, + { GPIO_HUB_NRESET, GPIOF_OUT_INIT_LOW, "hub_nreset" }, +}; + +static void __init omap4_ehci_init(void) +{ + int ret; + struct clk *phy_ref_clk; + + /* FREF_CLK3 provides the 19.2 MHz reference clock to the PHY */ + phy_ref_clk = clk_get(NULL, "auxclk3_ck"); + if (IS_ERR(phy_ref_clk)) { + pr_err("Cannot request auxclk3\n"); + return; + } + clk_set_rate(phy_ref_clk, 19200000); + clk_enable(phy_ref_clk); + + /* disable the power to the usb hub prior to init and reset phy+hub */ + ret = gpio_request_array(panda_ehci_gpios, + ARRAY_SIZE(panda_ehci_gpios)); + if (ret) { + pr_err("Unable to initialize EHCI power/reset\n"); + return; + } + + gpio_export(GPIO_HUB_POWER, 0); + gpio_export(GPIO_HUB_NRESET, 0); + gpio_set_value(GPIO_HUB_NRESET, 1); + + usbhs_init(&usbhs_bdata); + + /* enable power to hub */ + gpio_set_value(GPIO_HUB_POWER, 1); +} + +static struct omap_musb_board_data musb_board_data = { + .interface_type = MUSB_INTERFACE_UTMI, +#ifdef CONFIG_USB_GADGET_MUSB_HDRC + .mode = MUSB_PERIPHERAL, +#else + .mode = MUSB_OTG, +#endif + .power = 100, +}; + +static struct twl4030_usb_data omap4_usbphy_data = { + .phy_init = omap4430_phy_init, + .phy_exit = omap4430_phy_exit, + .phy_power = omap4430_phy_power, + .phy_set_clock = omap4430_phy_set_clk, + .phy_suspend = omap4430_phy_suspend, +}; + +static struct omap2_hsmmc_info mmc[] = { + { + .mmc = 1, + .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA, + .gpio_wp = -EINVAL, + .gpio_cd = -EINVAL, + }, + { + .name = "wl1271", + .mmc = 5, + .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_POWER_OFF_CARD, + .gpio_wp = -EINVAL, + .gpio_cd = -EINVAL, + .ocr_mask = MMC_VDD_165_195, + .nonremovable = true, + }, + {} /* Terminator */ +}; + +static struct regulator_consumer_supply omap4_panda_vmmc_supply[] = { + { + .supply = "vmmc", + .dev_name = "omap_hsmmc.0", + }, +}; + +static struct regulator_consumer_supply omap4_panda_vmmc5_supply = { + .supply = "vmmc", + .dev_name = "omap_hsmmc.4", +}; + +static struct regulator_init_data panda_vmmc5 = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &omap4_panda_vmmc5_supply, +}; + +static struct fixed_voltage_config panda_vwlan = { + .supply_name = "vwl1271", + .microvolts = 1800000, /* 1.8V */ + .gpio = GPIO_WIFI_PMENA, + .startup_delay = 70000, /* 70msec */ + .enable_high = 1, + .enabled_at_boot = 0, + .init_data = &panda_vmmc5, +}; + +static struct platform_device omap_vwlan_device = { + .name = "reg-fixed-voltage", + .id = 1, + .dev = { + .platform_data = &panda_vwlan, + }, +}; + +struct wl12xx_platform_data omap_panda_wlan_data __initdata = { + .irq = OMAP_GPIO_IRQ(GPIO_WIFI_IRQ), + /* PANDA ref clock is 38.4 MHz */ + .board_ref_clock = 2, +}; + +static int omap4_twl6030_hsmmc_late_init(struct device *dev) +{ + int ret = 0; + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + struct omap_mmc_platform_data *pdata = dev->platform_data; + + if (!pdata) { + dev_err(dev, "%s: NULL platform data\n", __func__); + return -EINVAL; + } + /* Setting MMC1 Card detect Irq */ + if (pdev->id == 0) { + ret = twl6030_mmc_card_detect_config(); + if (ret) + dev_err(dev, "%s: Error card detect config(%d)\n", + __func__, ret); + else + pdata->slots[0].card_detect = twl6030_mmc_card_detect; + } + return ret; +} + +static __init void omap4_twl6030_hsmmc_set_late_init(struct device *dev) +{ + struct omap_mmc_platform_data *pdata; + + /* dev can be null if CONFIG_MMC_OMAP_HS is not set */ + if (!dev) { + pr_err("Failed omap4_twl6030_hsmmc_set_late_init\n"); + return; + } + pdata = dev->platform_data; + + pdata->init = omap4_twl6030_hsmmc_late_init; +} + +static int __init omap4_twl6030_hsmmc_init(struct omap2_hsmmc_info *controllers) +{ + struct omap2_hsmmc_info *c; + + omap2_hsmmc_init(controllers); + for (c = controllers; c->mmc; c++) + omap4_twl6030_hsmmc_set_late_init(c->dev); + + return 0; +} + +static struct regulator_init_data omap4_panda_vaux2 = { + .constraints = { + .min_uV = 1200000, + .max_uV = 2800000, + .apply_uV = true, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE + | REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, +}; + +static struct regulator_init_data omap4_panda_vaux3 = { + .constraints = { + .min_uV = 1000000, + .max_uV = 3000000, + .apply_uV = true, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE + | REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, +}; + +/* VMMC1 for MMC1 card */ +static struct regulator_init_data omap4_panda_vmmc = { + .constraints = { + .min_uV = 1200000, + .max_uV = 3000000, + .apply_uV = true, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE + | REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = omap4_panda_vmmc_supply, +}; + +static struct regulator_init_data omap4_panda_vpp = { + .constraints = { + .min_uV = 1800000, + .max_uV = 2500000, + .apply_uV = true, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE + | REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, +}; + +static struct regulator_init_data omap4_panda_vana = { + .constraints = { + .min_uV = 2100000, + .max_uV = 2100000, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, +}; + +static struct regulator_init_data omap4_panda_vcxio = { + .constraints = { + .min_uV = 1800000, + .max_uV = 1800000, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, +}; + +static struct regulator_consumer_supply panda_vdac_supply[] = { + { + .supply = "hdmi_vref", + }, +}; + +static struct regulator_init_data omap4_panda_vdac = { + .constraints = { + .min_uV = 1800000, + .max_uV = 1800000, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(panda_vdac_supply), + .consumer_supplies = panda_vdac_supply, +}; + +static struct regulator_init_data omap4_panda_vusb = { + .constraints = { + .min_uV = 3300000, + .max_uV = 3300000, + .apply_uV = true, + .valid_modes_mask = REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .valid_ops_mask = REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }, +}; + +static struct regulator_init_data omap4_panda_clk32kg = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .always_on = true, + }, +}; + +static void omap4_audio_conf(void) +{ + /* twl6040 naudint */ + omap_mux_init_signal("sys_nirq2.sys_nirq2", \ + OMAP_PIN_INPUT_PULLUP); +} + +static struct twl4030_codec_audio_data twl6040_audio = { + /* single-step ramp for headset and handsfree */ + .hs_left_step = 0x0f, + .hs_right_step = 0x0f, + .hf_left_step = 0x1d, + .hf_right_step = 0x1d, + .hs_switch_dev = 0x1, + .hs_forced_hs_state = 0x1 +}; + +static struct twl4030_codec_data twl6040_codec = { + .audio = &twl6040_audio, + .audpwron_gpio = 127, + .naudint_irq = OMAP44XX_IRQ_SYS_2N, + .irq_base = TWL6040_CODEC_IRQ_BASE, +}; + +static struct twl4030_platform_data omap4_panda_twldata = { + .irq_base = TWL6030_IRQ_BASE, + .irq_end = TWL6030_IRQ_END, + + /* Regulators */ + .vmmc = &omap4_panda_vmmc, + .vpp = &omap4_panda_vpp, + .vana = &omap4_panda_vana, + .vcxio = &omap4_panda_vcxio, + .vdac = &omap4_panda_vdac, + .vusb = &omap4_panda_vusb, + .vaux2 = &omap4_panda_vaux2, + .vaux3 = &omap4_panda_vaux3, + .clk32kg = &omap4_panda_clk32kg, + .usb = &omap4_usbphy_data, + + /* children */ + .codec = &twl6040_codec, +}; + +/* + * Display monitor features are burnt in their EEPROM as EDID data. The EEPROM + * is connected as I2C slave device, and can be accessed at address 0x50 + */ +static struct i2c_board_info __initdata panda_i2c_eeprom[] = { + { + I2C_BOARD_INFO("eeprom", 0x50), + }, +}; + +static int __init omap4_panda_i2c_init(void) +{ + omap4_pmic_init("twl6030", &omap4_panda_twldata); + omap_register_i2c_bus(2, 400, NULL, 0); + /* + * Bus 3 is attached to the DVI port where devices like the pico DLP + * projector don't work reliably with 400kHz + */ + omap_register_i2c_bus(3, 100, panda_i2c_eeprom, + ARRAY_SIZE(panda_i2c_eeprom)); + if(ARRAY_SIZE(bus4_i2c_devices)) + omap_register_i2c_bus(4, 400, bus4_i2c_devices, ARRAY_SIZE(bus4_i2c_devices)); + else + omap_register_i2c_bus(4, 400, NULL, 0); + return 0; +} + +#ifdef CONFIG_OMAP_MUX +static struct omap_board_mux board_mux[] __initdata = { + /* WLAN IRQ - GPIO 53 */ + OMAP4_MUX(GPMC_NCS3, OMAP_MUX_MODE3 | OMAP_PIN_INPUT), + /* WLAN POWER ENABLE - GPIO 43 */ + OMAP4_MUX(GPMC_A19, OMAP_MUX_MODE3 | OMAP_PIN_OUTPUT), + /* WLAN SDIO: MMC5 CMD */ + OMAP4_MUX(SDMMC5_CMD, OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP), + /* WLAN SDIO: MMC5 CLK */ + OMAP4_MUX(SDMMC5_CLK, OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP), + /* WLAN SDIO: MMC5 DAT[0-3] */ + OMAP4_MUX(SDMMC5_DAT0, OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP), + OMAP4_MUX(SDMMC5_DAT1, OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP), + OMAP4_MUX(SDMMC5_DAT2, OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP), + OMAP4_MUX(SDMMC5_DAT3, OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP), + /* gpio 0 - TFP410 PD */ + OMAP4_MUX(KPD_COL1, OMAP_PIN_OUTPUT | OMAP_MUX_MODE3), + /* dispc2_data23 */ + OMAP4_MUX(USBB2_ULPITLL_STP, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data22 */ + OMAP4_MUX(USBB2_ULPITLL_DIR, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data21 */ + OMAP4_MUX(USBB2_ULPITLL_NXT, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data20 */ + OMAP4_MUX(USBB2_ULPITLL_DAT0, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data19 */ + OMAP4_MUX(USBB2_ULPITLL_DAT1, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data18 */ + OMAP4_MUX(USBB2_ULPITLL_DAT2, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data15 */ + OMAP4_MUX(USBB2_ULPITLL_DAT3, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data14 */ + OMAP4_MUX(USBB2_ULPITLL_DAT4, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data13 */ + OMAP4_MUX(USBB2_ULPITLL_DAT5, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data12 */ + OMAP4_MUX(USBB2_ULPITLL_DAT6, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data11 */ + OMAP4_MUX(USBB2_ULPITLL_DAT7, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data10 */ + OMAP4_MUX(DPM_EMU3, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data9 */ + OMAP4_MUX(DPM_EMU4, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data16 */ + OMAP4_MUX(DPM_EMU5, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data17 */ + OMAP4_MUX(DPM_EMU6, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_hsync */ + OMAP4_MUX(DPM_EMU7, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_pclk */ + OMAP4_MUX(DPM_EMU8, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_vsync */ + OMAP4_MUX(DPM_EMU9, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_de */ + OMAP4_MUX(DPM_EMU10, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data8 */ + OMAP4_MUX(DPM_EMU11, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data7 */ + OMAP4_MUX(DPM_EMU12, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data6 */ + OMAP4_MUX(DPM_EMU13, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data5 */ + OMAP4_MUX(DPM_EMU14, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data4 */ + OMAP4_MUX(DPM_EMU15, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data3 */ + OMAP4_MUX(DPM_EMU16, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data2 */ + OMAP4_MUX(DPM_EMU17, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data1 */ + OMAP4_MUX(DPM_EMU18, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + /* dispc2_data0 */ + OMAP4_MUX(DPM_EMU19, OMAP_PIN_OUTPUT | OMAP_MUX_MODE5), + { .reg_offset = OMAP_MUX_TERMINATOR }, +}; + +static inline void __init board_serial_init(void) +{ + omap_serial_init(); +} +#else +#define board_mux NULL + +static inline void __init board_serial_init(void) +{ + omap_serial_init(); +} +#endif + +/* Display DVI */ +#define PANDA_DVI_TFP410_POWER_DOWN_GPIO 0 + +static int omap4_panda_enable_dvi(struct omap_dss_device *dssdev) +{ + gpio_set_value(dssdev->reset_gpio, 1); + return 0; +} + +static void omap4_panda_disable_dvi(struct omap_dss_device *dssdev) +{ + gpio_set_value(dssdev->reset_gpio, 0); +} + +/* Using generic display panel */ +static struct panel_generic_dpi_data omap4_dvi_panel = { + .name = "generic_720p", + .platform_enable = omap4_panda_enable_dvi, + .platform_disable = omap4_panda_disable_dvi, +}; + +struct omap_dss_device omap4_panda_dvi_device = { + .type = OMAP_DISPLAY_TYPE_DPI, + .name = "dvi", + .driver_name = "generic_dpi_panel", + .data = &omap4_dvi_panel, + .phy.dpi.data_lines = 24, + .reset_gpio = PANDA_DVI_TFP410_POWER_DOWN_GPIO, + .channel = OMAP_DSS_CHANNEL_LCD2, +}; + +int __init omap4_panda_dvi_init(void) +{ + int r; + + /* Requesting TFP410 DVI GPIO and disabling it, at bootup */ + r = gpio_request_one(omap4_panda_dvi_device.reset_gpio, + GPIOF_OUT_INIT_LOW, "DVI PD"); + if (r) + pr_err("Failed to get DVI powerdown GPIO\n"); + + return r; +} + +static struct gpio panda_hdmi_gpios[] = { + { HDMI_GPIO_CT_CP_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_hpd" }, + { HDMI_GPIO_LS_OE, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ls_oe" }, +}; + +static void omap4_panda_hdmi_mux_init(void) +{ + u32 r; + int status; + /* PAD0_HDMI_HPD_PAD1_HDMI_CEC */ + omap_mux_init_signal("hdmi_hpd.hdmi_hpd", + OMAP_PIN_INPUT_PULLUP); + omap_mux_init_signal("gpmc_wait2.gpio_100", + OMAP_PIN_INPUT_PULLDOWN); + omap_mux_init_signal("hdmi_cec.hdmi_cec", + OMAP_PIN_INPUT_PULLUP); + /* PAD0_HDMI_DDC_SCL_PAD1_HDMI_DDC_SDA */ + omap_mux_init_signal("hdmi_ddc_scl.hdmi_ddc_scl", + OMAP_PIN_INPUT_PULLUP); + omap_mux_init_signal("hdmi_ddc_sda.hdmi_ddc_sda", + OMAP_PIN_INPUT_PULLUP); + + /* strong pullup on DDC lines using unpublished register */ + r = ((1 << 24) | (1 << 28)) ; + omap4_ctrl_pad_writel(r, OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_I2C_1); + + gpio_request(HDMI_GPIO_HPD, NULL); + omap_mux_init_gpio(HDMI_GPIO_HPD, OMAP_PIN_INPUT | OMAP_PULL_ENA); + gpio_direction_input(HDMI_GPIO_HPD); + + status = gpio_request_array(panda_hdmi_gpios, + ARRAY_SIZE(panda_hdmi_gpios)); + if (status) + pr_err("%s: Cannot request HDMI GPIOs %x \n", __func__, status); +} + +static struct omap_dss_device omap4_panda_hdmi_device = { + .name = "hdmi", + .driver_name = "hdmi_panel", + .type = OMAP_DISPLAY_TYPE_HDMI, + .clocks = { + .dispc = { + .dispc_fclk_src = OMAP_DSS_CLK_SRC_FCK, + }, + .hdmi = { + .regn = 15, + .regm2 = 1, + }, + }, + .hpd_gpio = HDMI_GPIO_HPD, + .channel = OMAP_DSS_CHANNEL_DIGIT, +}; + +static struct omap_dss_device *omap4_panda_dss_devices[] = { + &omap4_panda_dvi_device, + &omap4_panda_hdmi_device, +}; + +static struct omap_dss_board_info omap4_panda_dss_data = { + .num_devices = ARRAY_SIZE(omap4_panda_dss_devices), + .devices = omap4_panda_dss_devices, + .default_device = &omap4_panda_dvi_device, +}; + +/* + * LPDDR2 Configeration Data: + * The memory organisation is as below : + * EMIF1 - CS0 - 2 Gb + * CS1 - 2 Gb + * EMIF2 - CS0 - 2 Gb + * CS1 - 2 Gb + * -------------------- + * TOTAL - 8 Gb + * + * Same devices installed on EMIF1 and EMIF2 + */ +static __initdata struct emif_device_details emif_devices = { + .cs0_device = &lpddr2_elpida_2G_S4_dev, + .cs1_device = &lpddr2_elpida_2G_S4_dev +}; + +void omap4_panda_display_init(void) +{ + int r; + + r = omap4_panda_dvi_init(); + if (r) + pr_err("error initializing panda DVI\n"); + + omap4_panda_hdmi_mux_init(); + omap_display_init(&omap4_panda_dss_data); +} + +static int panda_notifier_call(struct notifier_block *this, + unsigned long code, void *cmd) +{ + void __iomem *sar_base; + u32 v = OMAP4430_RST_GLOBAL_COLD_SW_MASK; + + sar_base = omap4_get_sar_ram_base(); + + if (!sar_base) + return notifier_from_errno(-ENOMEM); + + if ((code == SYS_RESTART) && (cmd != NULL)) { + /* cmd != null; case: warm boot */ + if (!strcmp(cmd, "bootloader")) { + /* Save reboot mode in scratch memory */ + strcpy(sar_base + 0xA0C, cmd); + v |= OMAP4430_RST_GLOBAL_WARM_SW_MASK; + } else if (!strcmp(cmd, "recovery")) { + /* Save reboot mode in scratch memory */ + strcpy(sar_base + 0xA0C, cmd); + v |= OMAP4430_RST_GLOBAL_WARM_SW_MASK; + } else { + v |= OMAP4430_RST_GLOBAL_COLD_SW_MASK; + } + } + + omap4_prm_write_inst_reg(0xfff, OMAP4430_PRM_DEVICE_INST, + OMAP4_RM_RSTST); + omap4_prm_write_inst_reg(v, OMAP4430_PRM_DEVICE_INST, OMAP4_RM_RSTCTRL); + v = omap4_prm_read_inst_reg(WKUP_MOD, OMAP4_RM_RSTCTRL); + + return NOTIFY_DONE; +} + +static struct notifier_block panda_reboot_notifier = { + .notifier_call = panda_notifier_call, +}; + +#define PANDA_FB_RAM_SIZE SZ_16M /* 1920?1080*4 * 2 */ +static struct omapfb_platform_data panda_fb_pdata = { + .mem_desc = { + .region_cnt = 1, + .region = { + [0] = { + .size = PANDA_FB_RAM_SIZE, + }, + }, + }, +}; + +static struct resource ramconsole_resources[] = { + { + .flags = IORESOURCE_MEM, + .start = PANDA_RAMCONSOLE_START, + .end = PANDA_RAMCONSOLE_START + PANDA_RAMCONSOLE_SIZE - 1, + }, +}; + +static struct ram_console_platform_data ramconsole_pdata; + +static struct platform_device ramconsole_device = { + .name = "ram_console", + .id = -1, + .num_resources = ARRAY_SIZE(ramconsole_resources), + .resource = ramconsole_resources, + .dev = { + .platform_data = &ramconsole_pdata, + }, +}; + +extern void __init omap4_panda_android_init(void); + +static void __init omap4_panda_init(void) +{ + int package = OMAP_PACKAGE_CBS; + int status; + + omap_emif_setup_device_details(&emif_devices, &emif_devices); + + if (omap_rev() == OMAP4430_REV_ES1_0) + package = OMAP_PACKAGE_CBL; + omap4_mux_init(board_mux, NULL, package); + + if (wl12xx_set_platform_data(&omap_panda_wlan_data)) + pr_err("error setting wl12xx data\n"); + + register_reboot_notifier(&panda_reboot_notifier); + ramconsole_pdata.bootinfo = omap4_get_resetreason(); + platform_device_register(&ramconsole_device); + omap4_panda_i2c_init(); + omap4_audio_conf(); + + if (cpu_is_omap4430()) + panda_gpio_keys_buttons[0].gpio = 121; + + platform_add_devices(panda_devices, ARRAY_SIZE(panda_devices)); + platform_device_register(&omap_vwlan_device); + board_serial_init(); + omap4_twl6030_hsmmc_init(mmc); + omap4_ehci_init(); + usb_musb_init(&musb_board_data); + + omap_dmm_init(); + omap_vram_set_sdram_vram(PANDA_FB_RAM_SIZE, 0); + omapfb_set_platform_data(&panda_fb_pdata); + omap4_panda_display_init(); + + if (cpu_is_omap446x()) { + /* Vsel0 = gpio, vsel1 = gnd */ + status = omap_tps6236x_board_setup(true, TPS62361_GPIO, -1, + OMAP_PIN_OFF_OUTPUT_HIGH, -1); + if (status) + pr_err("TPS62361 initialization failed: %d\n", status); + } + omap_enable_smartreflex_on_init(); +} + +static void __init omap4_panda_map_io(void) +{ + omap2_set_globals_443x(); + omap44xx_map_common_io(); +} + +static void __init omap4_panda_reserve(void) +{ + /* do the static reservations first */ + memblock_remove(PANDA_RAMCONSOLE_START, PANDA_RAMCONSOLE_SIZE); + memblock_remove(PHYS_ADDR_SMC_MEM, PHYS_ADDR_SMC_SIZE); + memblock_remove(PHYS_ADDR_DUCATI_MEM, PHYS_ADDR_DUCATI_SIZE); + /* ipu needs to recognize secure input buffer area as well */ + omap_ipu_set_static_mempool(PHYS_ADDR_DUCATI_MEM, PHYS_ADDR_DUCATI_SIZE + + OMAP_ION_HEAP_SECURE_INPUT_SIZE); + + omap_reserve(); +} + +MACHINE_START(OMAP4_PANDA, "OMAP4 Panda board") + /* Maintainer: David Anders - Texas Instruments Inc */ + .boot_params = 0x80000100, + .reserve = omap4_panda_reserve, + .map_io = omap4_panda_map_io, + .init_early = omap4_panda_init_early, + .init_irq = gic_init_irq, + .init_machine = omap4_panda_init, + .timer = &omap_timer, +MACHINE_END diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 4504ca66118d..50da680c479f 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -166,7 +166,7 @@ static u64 bpf_get_current_comm(u64 r1, u64 size, u64 r3, u64 r4, u64 r5) if (!task) return -EINVAL; - memcpy(buf, task->comm, min_t(size_t, size, sizeof(task->comm))); + strlcpy(buf, task->comm, min_t(size_t, size, sizeof(task->comm))); return 0; } diff --git a/kernel/cgroup.c b/kernel/cgroup.c index d5512e1e7138..e8d71110ed2a 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2498,6 +2498,14 @@ static void cgroup_migrate_add_src(struct css_set *src_cset, lockdep_assert_held(&cgroup_mutex); lockdep_assert_held(&css_set_lock); + /* + * If ->dead, @src_set is associated with one or more dead cgroups + * and doesn't contain any migratable tasks. Ignore it early so + * that the rest of migration path doesn't get confused by it. + */ + if (src_cset->dead) + return; + src_cgrp = cset_cgroup_from_root(src_cset, dst_cgrp->root); if (!list_empty(&src_cset->mg_preload_node)) @@ -5186,6 +5194,7 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) __releases(&cgroup_mutex) __acquires(&cgroup_mutex) { struct cgroup_subsys_state *css; + struct cgrp_cset_link *link; int ssid; lockdep_assert_held(&cgroup_mutex); @@ -5206,11 +5215,18 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) return -EBUSY; /* - * Mark @cgrp dead. This prevents further task migration and child - * creation by disabling cgroup_lock_live_group(). + * Mark @cgrp and the associated csets dead. The former prevents + * further task migration and child creation by disabling + * cgroup_lock_live_group(). The latter makes the csets ignored by + * the migration path. */ cgrp->self.flags &= ~CSS_ONLINE; + spin_lock_bh(&css_set_lock); + list_for_each_entry(link, &cgrp->cset_links, cset_link) + link->cset->dead = true; + spin_unlock_bh(&css_set_lock); + /* initiate massacre of all css's */ for_each_css(css, ssid, cgrp) kill_css(css); diff --git a/kernel/drivers/input/touchscreen/Kconfig b/kernel/drivers/input/touchscreen/Kconfig new file mode 100644 index 000000000000..18655c0b3997 --- /dev/null +++ b/kernel/drivers/input/touchscreen/Kconfig @@ -0,0 +1,721 @@ +# +# Touchscreen driver configuration +# +menuconfig INPUT_TOUCHSCREEN + bool "Touchscreens" + help + Say Y here, and a list of supported touchscreens will be displayed. + This option doesn't affect the kernel. + + If unsure, say Y. + +if INPUT_TOUCHSCREEN + +config TOUCHSCREEN_88PM860X + tristate "Marvell 88PM860x touchscreen" + depends on MFD_88PM860X + help + Say Y here if you have a 88PM860x PMIC and want to enable + support for the built-in touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called 88pm860x-ts. + +config TOUCHSCREEN_ADS7846 + tristate "ADS7846/TSC2046/AD7873 and AD(S)7843 based touchscreens" + depends on SPI_MASTER + depends on HWMON = n || HWMON + help + Say Y here if you have a touchscreen interface using the + ADS7846/TSC2046/AD7873 or ADS7843/AD7843 controller, + and your board-specific setup code includes that in its + table of SPI devices. + + If HWMON is selected, and the driver is told the reference voltage + on your board, you will also get hwmon interfaces for the voltage + (and on ads7846/tsc2046/ad7873, temperature) sensors of this chip. + + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called ads7846. + +config TOUCHSCREEN_AD7877 + tristate "AD7877 based touchscreens" + depends on SPI_MASTER + help + Say Y here if you have a touchscreen interface using the + AD7877 controller, and your board-specific initialization + code includes that in its table of SPI devices. + + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called ad7877. + +config TOUCHSCREEN_AD7879 + tristate "Analog Devices AD7879-1/AD7889-1 touchscreen interface" + help + Say Y here if you want to support a touchscreen interface using + the AD7879-1/AD7889-1 controller. + + You should select a bus connection too. + + To compile this driver as a module, choose M here: the + module will be called ad7879. + +config TOUCHSCREEN_AD7879_I2C + tristate "support I2C bus connection" + depends on TOUCHSCREEN_AD7879 && I2C + help + Say Y here if you have AD7879-1/AD7889-1 hooked to an I2C bus. + + To compile this driver as a module, choose M here: the + module will be called ad7879-i2c. + +config TOUCHSCREEN_AD7879_SPI + tristate "support SPI bus connection" + depends on TOUCHSCREEN_AD7879 && SPI_MASTER + help + Say Y here if you have AD7879-1/AD7889-1 hooked to a SPI bus. + + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called ad7879-spi. + +config TOUCHSCREEN_BITSY + tristate "Compaq iPAQ H3600 (Bitsy) touchscreen" + depends on SA1100_BITSY + select SERIO + help + Say Y here if you have the h3600 (Bitsy) touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called h3600_ts_input. + +config TOUCHSCREEN_BU21013 + tristate "BU21013 based touch panel controllers" + depends on I2C + help + Say Y here if you have a bu21013 touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called bu21013_ts. + +config TOUCHSCREEN_CY8CTMG110 + tristate "cy8ctmg110 touchscreen" + depends on I2C + depends on GPIOLIB + + help + Say Y here if you have a cy8ctmg110 capacitive touchscreen on + an AAVA device. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called cy8ctmg110_ts. + +config TOUCHSCREEN_DA9034 + tristate "Touchscreen support for Dialog Semiconductor DA9034" + depends on PMIC_DA903X + default y + help + Say Y here to enable the support for the touchscreen found + on Dialog Semiconductor DA9034 PMIC. + +config TOUCHSCREEN_DYNAPRO + tristate "Dynapro serial touchscreen" + select SERIO + help + Say Y here if you have a Dynapro serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called dynapro. + +config TOUCHSCREEN_HAMPSHIRE + tristate "Hampshire serial touchscreen" + select SERIO + help + Say Y here if you have a Hampshire serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called hampshire. + +config TOUCHSCREEN_EETI + tristate "EETI touchscreen panel support" + depends on I2C + help + Say Y here to enable support for I2C connected EETI touch panels. + + To compile this driver as a module, choose M here: the + module will be called eeti_ts. + +config TOUCHSCREEN_FUJITSU + tristate "Fujitsu serial touchscreen" + select SERIO + help + Say Y here if you have the Fujitsu touchscreen (such as one + installed in Lifebook P series laptop) connected to your + system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called fujitsu-ts. + +config TOUCHSCREEN_S3C2410 + tristate "Samsung S3C2410/generic touchscreen input driver" + depends on ARCH_S3C2410 || SAMSUNG_DEV_TS + select S3C_ADC + help + Say Y here if you have the s3c2410 touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called s3c2410_ts. + +config TOUCHSCREEN_GUNZE + tristate "Gunze AHL-51S touchscreen" + select SERIO + help + Say Y here if you have the Gunze AHL-51 touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called gunze. + +config TOUCHSCREEN_ELO + tristate "Elo serial touchscreens" + select SERIO + help + Say Y here if you have an Elo serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called elo. + +config TOUCHSCREEN_WACOM_W8001 + tristate "Wacom W8001 penabled serial touchscreen" + select SERIO + help + Say Y here if you have an Wacom W8001 penabled serial touchscreen + connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called wacom_w8001. + +config TOUCHSCREEN_LPC32XX + tristate "LPC32XX touchscreen controller" + depends on ARCH_LPC32XX + help + Say Y here if you have a LPC32XX device and want + to support the built-in touchscreen. + + To compile this driver as a module, choose M here: the + module will be called lpc32xx_ts. + +config TOUCHSCREEN_MCS5000 + tristate "MELFAS MCS-5000 touchscreen" + depends on I2C + help + Say Y here if you have the MELFAS MCS-5000 touchscreen controller + chip in your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mcs5000_ts. + +config TOUCHSCREEN_MTOUCH + tristate "MicroTouch serial touchscreens" + select SERIO + help + Say Y here if you have a MicroTouch (3M) serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mtouch. + +config TOUCHSCREEN_INEXIO + tristate "iNexio serial touchscreens" + select SERIO + help + Say Y here if you have an iNexio serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called inexio. + +config TOUCHSCREEN_INTEL_MID + tristate "Intel MID platform resistive touchscreen" + depends on INTEL_SCU_IPC + help + Say Y here if you have a Intel MID based touchscreen in + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called intel_mid_touch. + +config TOUCHSCREEN_MK712 + tristate "ICS MicroClock MK712 touchscreen" + help + Say Y here if you have the ICS MicroClock MK712 touchscreen + controller chip in your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mk712. + +config TOUCHSCREEN_HP600 + tristate "HP Jornada 6xx touchscreen" + depends on SH_HP6XX && SH_ADC + help + Say Y here if you have a HP Jornada 620/660/680/690 and want to + support the built-in touchscreen. + + To compile this driver as a module, choose M here: the + module will be called hp680_ts_input. + +config TOUCHSCREEN_HP7XX + tristate "HP Jornada 7xx touchscreen" + depends on SA1100_JORNADA720_SSP + help + Say Y here if you have a HP Jornada 710/720/728 and want + to support the built-in touchscreen. + + To compile this driver as a module, choose M here: the + module will be called jornada720_ts. + +config TOUCHSCREEN_HTCPEN + tristate "HTC Shift X9500 touchscreen" + depends on ISA + help + Say Y here if you have an HTC Shift UMPC also known as HTC X9500 + Clio / Shangrila and want to support the built-in touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called htcpen. + +config TOUCHSCREEN_PENMOUNT + tristate "Penmount serial touchscreen" + select SERIO + help + Say Y here if you have a Penmount serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called penmount. + +config TOUCHSCREEN_QT602240 + tristate "QT602240 I2C Touchscreen" + depends on I2C + help + Say Y here if you have the AT42QT602240/ATMXT224 I2C touchscreen + connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called qt602240_ts. + +config TOUCHSCREEN_MIGOR + tristate "Renesas MIGO-R touchscreen" + depends on SH_MIGOR && I2C + help + Say Y here to enable MIGO-R touchscreen support. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called migor_ts. + +config TOUCHSCREEN_TNETV107X + tristate "TI TNETV107X touchscreen support" + depends on ARCH_DAVINCI_TNETV107X + help + Say Y here if you want to use the TNETV107X touchscreen. + + To compile this driver as a module, choose M here: the + module will be called tnetv107x-ts. + +config TOUCHSCREEN_SYNAPTICS_I2C_RMI4 + tristate "Synaptics DSX I2C touchscreen" + depends on I2C + help + Say Y here if you have a Synaptics DSX I2C touchscreen + connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called synaptics_i2c_rmi4. + +config TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV + tristate "Synaptics I2C touchscreen rmi device" + depends on TOUCHSCREEN_SYNAPTICS_I2C_RMI4 + help + This enables support for character device channel for Synaptics RMI + touchscreens. + +config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE + tristate "Synaptics I2C touchscreen firmware update" + depends on TOUCHSCREEN_SYNAPTICS_I2C_RMI4 + help + This enables support for firmware update for Synaptics RMI + touchscreens. + +config TOUCHSCREEN_TOUCHRIGHT + tristate "Touchright serial touchscreen" + select SERIO + help + Say Y here if you have a Touchright serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called touchright. + +config TOUCHSCREEN_TOUCHWIN + tristate "Touchwin serial touchscreen" + select SERIO + help + Say Y here if you have a Touchwin serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called touchwin. + +config TOUCHSCREEN_ATMEL_TSADCC + tristate "Atmel Touchscreen Interface" + depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 + help + Say Y here if you have a 4-wire touchscreen connected to the + ADC Controller on your Atmel SoC (such as the AT91SAM9RL). + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called atmel_tsadcc. + +config TOUCHSCREEN_UCB1400 + tristate "Philips UCB1400 touchscreen" + depends on AC97_BUS + depends on UCB1400_CORE + help + This enables support for the Philips UCB1400 touchscreen interface. + The UCB1400 is an AC97 audio codec. The touchscreen interface + will be initialized only after the ALSA subsystem has been + brought up and the UCB1400 detected. You therefore have to + configure ALSA support as well (either built-in or modular, + independently of whether this driver is itself built-in or + modular) for this driver to work. + + To compile this driver as a module, choose M here: the + module will be called ucb1400_ts. + +config TOUCHSCREEN_WM97XX + tristate "Support for WM97xx AC97 touchscreen controllers" + depends on AC97_BUS + help + Say Y here if you have a Wolfson Microelectronics WM97xx + touchscreen connected to your system. Note that this option + only enables core driver, you will also need to select + support for appropriate chip below. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called wm97xx-ts. + +config TOUCHSCREEN_WM9705 + bool "WM9705 Touchscreen interface support" + depends on TOUCHSCREEN_WM97XX + default y + help + Say Y here to enable support for the Wolfson Microelectronics + WM9705 touchscreen controller. + +config TOUCHSCREEN_WM9712 + bool "WM9712 Touchscreen interface support" + depends on TOUCHSCREEN_WM97XX + default y + help + Say Y here to enable support for the Wolfson Microelectronics + WM9712 touchscreen controller. + +config TOUCHSCREEN_WM9713 + bool "WM9713 Touchscreen interface support" + depends on TOUCHSCREEN_WM97XX + default y + help + Say Y here to enable support for the Wolfson Microelectronics + WM9713 touchscreen controller. + +config TOUCHSCREEN_WM97XX_ATMEL + tristate "WM97xx Atmel accelerated touch" + depends on TOUCHSCREEN_WM97XX && (AVR32 || ARCH_AT91) + help + Say Y here for support for streaming mode with WM97xx touchscreens + on Atmel AT91 or AVR32 systems with an AC97C module. + + Be aware that this will use channel B in the controller for + streaming data, this must not conflict with other AC97C drivers. + + If unsure, say N. + + To compile this driver as a module, choose M here: the module will + be called atmel-wm97xx. + +config TOUCHSCREEN_WM97XX_MAINSTONE + tristate "WM97xx Mainstone/Palm accelerated touch" + depends on TOUCHSCREEN_WM97XX && ARCH_PXA + help + Say Y here for support for streaming mode with WM97xx touchscreens + on Mainstone, Palm Tungsten T5, TX and LifeDrive systems. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mainstone-wm97xx. + +config TOUCHSCREEN_WM97XX_ZYLONITE + tristate "Zylonite accelerated touch" + depends on TOUCHSCREEN_WM97XX && MACH_ZYLONITE + select TOUCHSCREEN_WM9713 + help + Say Y here for support for streaming mode with the touchscreen + on Zylonite systems. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called zylonite-wm97xx. + +config TOUCHSCREEN_USB_COMPOSITE + tristate "USB Touchscreen Driver" + depends on USB_ARCH_HAS_HCD + select USB + help + USB Touchscreen driver for: + - eGalax Touchkit USB (also includes eTurboTouch CT-410/510/700) + - PanJit TouchSet USB + - 3M MicroTouch USB (EX II series) + - ITM + - some other eTurboTouch + - Gunze AHL61 + - DMC TSC-10/25 + - IRTOUCHSYSTEMS/UNITOP + - IdealTEK URTC1000 + - GoTop Super_Q2/GogoPen/PenPower tablets + - JASTEC USB Touch Controller/DigiTech DTR-02U + - Zytronic controllers + + Have a look at <http://linux.chapter7.ch/touchkit/> for + a usage description and the required user-space stuff. + + To compile this driver as a module, choose M here: the + module will be called usbtouchscreen. + +config TOUCHSCREEN_MC13783 + tristate "Freescale MC13783 touchscreen input driver" + depends on MFD_MC13783 + help + Say Y here if you have an Freescale MC13783 PMIC on your + board and want to use its touchscreen + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mc13783_ts. + +config TOUCHSCREEN_USB_EGALAX + default y + bool "eGalax, eTurboTouch CT-410/510/700 device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_PANJIT + default y + bool "PanJit device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_3M + default y + bool "3M/Microtouch EX II series device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_ITM + default y + bool "ITM device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_ETURBO + default y + bool "eTurboTouch (non-eGalax compatible) device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_GUNZE + default y + bool "Gunze AHL61 device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_DMC_TSC10 + default y + bool "DMC TSC-10/25 device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_IRTOUCH + default y + bool "IRTOUCHSYSTEMS/UNITOP device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_IDEALTEK + default y + bool "IdealTEK URTC1000 device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_GENERAL_TOUCH + default y + bool "GeneralTouch Touchscreen device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_GOTOP + default y + bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_JASTEC + default y + bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_E2I + default y + bool "e2i Touchscreen controller (e.g. from Mimo 740)" + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_ZYTRONIC + default y + bool "Zytronic controller" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_ETT_TC45USB + default y + bool "ET&T USB series TC4UM/TC5UH touchscreen controler support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_NEXIO + default y + bool "NEXIO/iNexio device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_TOUCHIT213 + tristate "Sahara TouchIT-213 touchscreen" + select SERIO + help + Say Y here if you have a Sahara TouchIT-213 Tablet PC. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called touchit213. + +config TOUCHSCREEN_TSC2007 + tristate "TSC2007 based touchscreens" + depends on I2C + help + Say Y here if you have a TSC2007 based touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tsc2007. + +config TOUCHSCREEN_TSC2004 + tristate "TSC2004 based touchscreens" + depends on I2C + help + Say Y here if you have a TSC2004 based touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tsc2004. + +config TOUCHSCREEN_W90X900 + tristate "W90P910 touchscreen driver" + depends on HAVE_CLK + help + Say Y here if you have a W90P910 based touchscreen. + + To compile this driver as a module, choose M here: the + module will be called w90p910_ts. + +config TOUCHSCREEN_PCAP + tristate "Motorola PCAP touchscreen" + depends on EZX_PCAP + help + Say Y here if you have a Motorola EZX telephone and + want to enable support for the built-in touchscreen. + + To compile this driver as a module, choose M here: the + module will be called pcap_ts. + +config TOUCHSCREEN_TPS6507X + tristate "TPS6507x based touchscreens" + depends on I2C + help + Say Y here if you have a TPS6507x based touchscreen + controller. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tps6507x_ts. + +config TOUCHSCREEN_STMPE + tristate "STMicroelectronics STMPE touchscreens" + depends on MFD_STMPE + help + Say Y here if you want support for STMicroelectronics + STMPE touchscreen controllers. + + To compile this driver as a module, choose M here: the + module will be called stmpe-ts. + +endif diff --git a/kernel/drivers/input/touchscreen/Makefile b/kernel/drivers/input/touchscreen/Makefile new file mode 100644 index 000000000000..a6c7d9f388a6 --- /dev/null +++ b/kernel/drivers/input/touchscreen/Makefile @@ -0,0 +1,68 @@ +# +# Makefile for the touchscreen drivers. +# + +# Each configuration option enables a list of files. + +wm97xx-ts-y := wm97xx-core.o + +obj-$(CONFIG_TOUCHSCREEN_88PM860X) += 88pm860x-ts.o +obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o +obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o +obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o +obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o +obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o +obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o +obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o +obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o +obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o +obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o +obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o +obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o +obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o +obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o +obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o +obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o +obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o +obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o +obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o +obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o +obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o +obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o +obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o +obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o +obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o +obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o +obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o +obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o +obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o +obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o +obj-$(CONFIG_TOUCHSCREEN_QT602240) += qt602240_ts.o +obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o +obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o +obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o +obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += synaptics_i2c_rmi4.o +obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV) += synaptics_rmi_dev.o +obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE) += synaptics_fw_update.o +obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o +obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o +obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o +obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o +obj-$(CONFIG_TOUCHSCREEN_TSC2004) += tsc2004.o +obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o +obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o +obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o +wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o +wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o +wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o +obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o +obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o +obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o +obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o +obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o + +all: +make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + +clean: +make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean diff --git a/kernel/drivers/input/touchscreen/synaptics_fw_update.c b/kernel/drivers/input/touchscreen/synaptics_fw_update.c new file mode 100644 index 000000000000..8b6d7c7e368d --- /dev/null +++ b/kernel/drivers/input/touchscreen/synaptics_fw_update.c @@ -0,0 +1,1698 @@ +/* + * Synaptics RMI4 touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com> + * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/firmware.h> +#include <linux/input/synaptics_dsx.h> +#include "synaptics_i2c_rmi4.h" + +#define DEBUG_FW_UPDATE +#define SHOW_PROGRESS +#define FW_IMAGE_NAME "PR1063486-s7301_00000000.img" +#define MAX_FIRMWARE_ID_LEN 10 +#define FORCE_UPDATE false +#define INSIDE_FIRMWARE_UPDATE + +#define CHECKSUM_OFFSET 0x00 +#define BOOTLOADER_VERSION_OFFSET 0x07 +#define IMAGE_SIZE_OFFSET 0x08 +#define CONFIG_SIZE_OFFSET 0x0C +#define PRODUCT_ID_OFFSET 0x10 +#define PRODUCT_INFO_OFFSET 0x1E +#define FW_IMAGE_OFFSET 0x100 +#define PRODUCT_ID_SIZE 10 + +#define BOOTLOADER_ID_OFFSET 0 +#define FLASH_PROPERTIES_OFFSET 2 +#define BLOCK_SIZE_OFFSET 3 +#define FW_BLOCK_COUNT_OFFSET 5 + +#define REG_MAP (1 << 0) +#define UNLOCKED (1 << 1) +#define HAS_CONFIG_ID (1 << 2) +#define HAS_PERM_CONFIG (1 << 3) +#define HAS_BL_CONFIG (1 << 4) +#define HAS_DISP_CONFIG (1 << 5) +#define HAS_CTRL1 (1 << 6) + +#define BLOCK_NUMBER_OFFSET 0 +#define BLOCK_DATA_OFFSET 2 + +#define UI_CONFIG_AREA 0x00 +#define PERM_CONFIG_AREA 0x01 +#define BL_CONFIG_AREA 0x02 +#define DISP_CONFIG_AREA 0x03 + +enum flash_command { + CMD_WRITE_FW_BLOCK = 0x2, + CMD_ERASE_ALL = 0x3, + CMD_READ_CONFIG_BLOCK = 0x5, + CMD_WRITE_CONFIG_BLOCK = 0x6, + CMD_ERASE_CONFIG = 0x7, + CMD_ERASE_BL_CONFIG = 0x9, + CMD_ERASE_DISP_CONFIG = 0xA, + CMD_ENABLE_FLASH_PROG = 0xF, +}; + +enum flash_area { + NONE, + UI_FIRMWARE, + CONFIG_AREA +}; + +#define SLEEP_MODE_NORMAL (0x00) +#define SLEEP_MODE_SENSOR_SLEEP (0x01) +#define SLEEP_MODE_RESERVED0 (0x02) +#define SLEEP_MODE_RESERVED1 (0x03) + +#define ENABLE_WAIT_MS (1 * 1000) +#define WRITE_WAIT_MS (3 * 1000) +#define ERASE_WAIT_MS (5 * 1000) +#define RESET_WAIT_MS (500) + +#define SLEEP_TIME_US 50 + +static ssize_t fwu_sysfs_show_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t fwu_sysfs_store_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t fwu_sysfs_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_write_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_read_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_config_area_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_image_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_block_size_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static int fwu_wait_for_idle(int timeout_ms); + +struct image_header { + unsigned int checksum; + unsigned int image_size; + unsigned int config_size; + unsigned char options; + unsigned char bootloader_version; + unsigned char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1]; + unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE]; +}; + +struct pdt_properties { + union { + struct { + unsigned char reserved_1:6; + unsigned char has_bsr:1; + unsigned char reserved_2:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f01_device_status { + union { + struct { + unsigned char status_code:4; + unsigned char reserved:2; + unsigned char flash_prog:1; + unsigned char unconfigured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f01_device_control { + union { + struct { + unsigned char sleep_mode:2; + unsigned char nosleep:1; + unsigned char reserved:2; + unsigned char charger_connected:1; + unsigned char report_rate:1; + unsigned char configured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_flash_control { + union { + struct { + unsigned char command:4; + unsigned char status:3; + unsigned char program_enabled:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_flash_properties { + union { + struct { + unsigned char regmap:1; + unsigned char unlocked:1; + unsigned char has_configid:1; + unsigned char has_perm_config:1; + unsigned char has_bl_config:1; + unsigned char has_display_config:1; + unsigned char has_blob_config:1; + unsigned char reserved:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_fwu_handle { + bool initialized; + bool force_update; + char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1]; + unsigned int image_size; + unsigned int data_pos; + unsigned char intr_mask; + unsigned char bootloader_id[2]; + unsigned char productinfo1; + unsigned char productinfo2; + unsigned char *ext_data_source; + unsigned char *read_config_buf; + const unsigned char *firmware_data; + const unsigned char *config_data; + unsigned short block_size; + unsigned short fw_block_count; + unsigned short config_block_count; + unsigned short perm_config_block_count; + unsigned short bl_config_block_count; + unsigned short disp_config_block_count; + unsigned short config_size; + unsigned short config_area; + unsigned short addr_f34_flash_control; + unsigned short addr_f01_interrupt_register; + struct synaptics_rmi4_fn_desc f01_fd; + struct synaptics_rmi4_fn_desc f34_fd; + struct synaptics_rmi4_exp_fn_ptr *fn_ptr; + struct synaptics_rmi4_data *rmi4_data; + struct f34_flash_control flash_control; + struct f34_flash_properties flash_properties; + struct workqueue_struct *fwu_workqueue; + struct delayed_work fwu_work; +}; + +static struct bin_attribute dev_attr_data = { + .attr = { + .name = "data", + .mode = (S_IRUGO | S_IWUGO), + }, + .size = 0, + .read = fwu_sysfs_show_image, + .write = fwu_sysfs_store_image, +}; + +static struct device_attribute attrs[] = { + __ATTR(doreflash, S_IWUGO, + synaptics_rmi4_show_error, + fwu_sysfs_do_reflash_store), + __ATTR(writeconfig, S_IWUGO, + synaptics_rmi4_show_error, + fwu_sysfs_write_config_store), + __ATTR(readconfig, S_IWUGO, + synaptics_rmi4_show_error, + fwu_sysfs_read_config_store), + __ATTR(configarea, S_IWUGO, + synaptics_rmi4_show_error, + fwu_sysfs_config_area_store), + __ATTR(imagesize, S_IWUGO, + synaptics_rmi4_show_error, + fwu_sysfs_image_size_store), + __ATTR(blocksize, S_IRUGO, + fwu_sysfs_block_size_show, + synaptics_rmi4_store_error), + __ATTR(fwblockcount, S_IRUGO, + fwu_sysfs_firmware_block_count_show, + synaptics_rmi4_store_error), + __ATTR(configblockcount, S_IRUGO, + fwu_sysfs_configuration_block_count_show, + synaptics_rmi4_store_error), + __ATTR(permconfigblockcount, S_IRUGO, + fwu_sysfs_perm_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(blconfigblockcount, S_IRUGO, + fwu_sysfs_bl_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(dispconfigblockcount, S_IRUGO, + fwu_sysfs_disp_config_block_count_show, + synaptics_rmi4_store_error), +}; + +static struct synaptics_rmi4_fwu_handle *fwu; + +static struct completion remove_complete; + +static unsigned int extract_uint(const unsigned char *ptr) +{ + return (unsigned int)ptr[0] + + (unsigned int)ptr[1] * 0x100 + + (unsigned int)ptr[2] * 0x10000 + + (unsigned int)ptr[3] * 0x1000000; +} + +static void parse_header(struct image_header *header, + const unsigned char *fw_image) +{ + header->checksum = extract_uint(&fw_image[CHECKSUM_OFFSET]); + header->bootloader_version = fw_image[BOOTLOADER_VERSION_OFFSET]; + header->image_size = extract_uint(&fw_image[IMAGE_SIZE_OFFSET]); + header->config_size = extract_uint(&fw_image[CONFIG_SIZE_OFFSET]); + memcpy(header->product_id, &fw_image[PRODUCT_ID_OFFSET], + SYNAPTICS_RMI4_PRODUCT_ID_SIZE); + header->product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE] = 0; + memcpy(header->product_info, &fw_image[PRODUCT_INFO_OFFSET], + SYNAPTICS_RMI4_PRODUCT_INFO_SIZE); + +#ifdef DEBUG_FW_UPDATE + dev_info(&fwu->rmi4_data->i2c_client->dev, + "Firwmare size %d, config size %d\n", + header->image_size, + header->config_size); +#endif + return; +} + +static int fwu_read_f01_device_status(struct f01_device_status *status) +{ + int retval; + + retval = fwu->fn_ptr->read(fwu->rmi4_data, + fwu->f01_fd.data_base_addr, + status->data, + sizeof(status->data)); + if (retval < 0) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Failed to read F01 device status\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_read_f34_queries(void) +{ + int retval; + unsigned char count = 4; + unsigned char buf[10]; + struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client; + + retval = fwu->fn_ptr->read(fwu->rmi4_data, + fwu->f34_fd.query_base_addr + BOOTLOADER_ID_OFFSET, + fwu->bootloader_id, + sizeof(fwu->bootloader_id)); + if (retval < 0) { + dev_err(&i2c_client->dev, + "%s: Failed to read bootloader ID\n", + __func__); + return retval; + } + + retval = fwu->fn_ptr->read(fwu->rmi4_data, + fwu->f34_fd.query_base_addr + FLASH_PROPERTIES_OFFSET, + fwu->flash_properties.data, + sizeof(fwu->flash_properties.data)); + if (retval < 0) { + dev_err(&i2c_client->dev, + "%s: Failed to read flash properties\n", + __func__); + return retval; + } + + dev_info(&i2c_client->dev, "%s perm:%d, bl:%d, display:%d\n", + __func__, + fwu->flash_properties.has_perm_config, + fwu->flash_properties.has_bl_config, + fwu->flash_properties.has_display_config); + + if (fwu->flash_properties.has_perm_config) + count += 2; + + if (fwu->flash_properties.has_bl_config) + count += 2; + + if (fwu->flash_properties.has_display_config) + count += 2; + + retval = fwu->fn_ptr->read(fwu->rmi4_data, + fwu->f34_fd.query_base_addr + BLOCK_SIZE_OFFSET, + buf, + 2); + if (retval < 0) { + dev_err(&i2c_client->dev, + "%s: Failed to read block size info\n", + __func__); + return retval; + } + + batohs(&fwu->block_size, &(buf[0])); + + retval = fwu->fn_ptr->read(fwu->rmi4_data, + fwu->f34_fd.query_base_addr + FW_BLOCK_COUNT_OFFSET, + buf, + count); + if (retval < 0) { + dev_err(&i2c_client->dev, + "%s: Failed to read block count info\n", + __func__); + return retval; + } + + batohs(&fwu->fw_block_count, &(buf[0])); + batohs(&fwu->config_block_count, &(buf[2])); + + count = 4; + + if (fwu->flash_properties.has_perm_config) { + batohs(&fwu->perm_config_block_count, &(buf[count])); + count += 2; + } + + if (fwu->flash_properties.has_bl_config) { + batohs(&fwu->bl_config_block_count, &(buf[count])); + count += 2; + } + + if (fwu->flash_properties.has_display_config) + batohs(&fwu->disp_config_block_count, &(buf[count])); + + fwu->addr_f34_flash_control = fwu->f34_fd.data_base_addr + + BLOCK_DATA_OFFSET + + fwu->block_size; + return 0; +} + +static int fwu_read_interrupt_status(void) +{ + int retval; + unsigned char interrupt_status; + retval = fwu->fn_ptr->read(fwu->rmi4_data, + fwu->addr_f01_interrupt_register, + &interrupt_status, + sizeof(interrupt_status)); + if (retval < 0) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Failed to read flash status\n", + __func__); + return retval; + } + return interrupt_status; +} + +static int fwu_read_f34_flash_status(void) +{ + int retval; + retval = fwu->fn_ptr->read(fwu->rmi4_data, + fwu->addr_f34_flash_control, + fwu->flash_control.data, + sizeof(fwu->flash_control.data)); + if (retval < 0) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Failed to read flash status\n", + __func__); + return retval; + } + return 0; +} + +static int fwu_reset_device(void) +{ + int retval; + +#ifdef DEBUG_FW_UPDATE + dev_info(&fwu->rmi4_data->i2c_client->dev, + "%s: Reset device\n", + __func__); +#endif + + retval = fwu->rmi4_data->reset_device(fwu->rmi4_data); + if (retval < 0) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Failed to reset core driver after reflash\n", + __func__); + return retval; + } + return 0; +} + +static int fwu_write_f34_command(unsigned char cmd) +{ + int retval; + + fwu->flash_control.data[0] = cmd; + retval = fwu->fn_ptr->write(fwu->rmi4_data, + fwu->addr_f34_flash_control, + fwu->flash_control.data, + sizeof(fwu->flash_control.data)); + if (retval < 0) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Failed to write command 0x%02x\n", + __func__, fwu->flash_control.data[0]); + return retval; + } + return 0; +} + +static int fwu_wait_for_idle(int timeout_ms) +{ + int count = 0; + int timeout_count = ((timeout_ms * 1000) / SLEEP_TIME_US) + 1; + do { + if (fwu->flash_control.command == 0x00) + return 0; + + usleep_range(SLEEP_TIME_US, SLEEP_TIME_US + 100); + } while (count++ < timeout_count); + + fwu_read_f34_flash_status(); + if (fwu->flash_control.command == 0x00) + return 0; + + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Timed out waiting for idle status\n", + __func__); + + return -ETIMEDOUT; +} + +static enum flash_area fwu_go_nogo(void) +{ + int retval = 0; + int index = 0; + int deviceFirmwareID; + int imageConfigID; + int deviceConfigID; + unsigned long imageFirmwareID; + unsigned char firmware_id[4]; + unsigned char config_id[4]; + char *strptr; + char *imagePR = kzalloc(sizeof(MAX_FIRMWARE_ID_LEN), GFP_KERNEL); + enum flash_area flash_area = NONE; + struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client; + struct f01_device_status f01_device_status; + + if (fwu->force_update) { + flash_area = UI_FIRMWARE; + goto exit; + } + + retval = fwu_read_f01_device_status(&f01_device_status); + if (retval < 0) { + flash_area = NONE; + goto exit; + } + + imagePR = kzalloc(sizeof(MAX_FIRMWARE_ID_LEN), GFP_KERNEL); + + /* Force update firmware when device is in bootloader mode */ + if (f01_device_status.flash_prog) { + dev_info(&i2c_client->dev, + "%s: In flash prog mode\n", + __func__); + flash_area = UI_FIRMWARE; + goto exit; + } + + + /* device firmware id */ + retval = fwu->fn_ptr->read(fwu->rmi4_data, + fwu->f01_fd.query_base_addr + 18, + firmware_id, + sizeof(firmware_id)); + if (retval < 0) { + dev_err(&i2c_client->dev, + "Failed to read firmware ID (code %d).\n", retval); + goto exit; + } + firmware_id[3] = 0; + deviceFirmwareID = extract_uint(firmware_id); + + /* .img firmware id */ + strptr = strstr(FW_IMAGE_NAME, "PR"); + if (!strptr) { + dev_err(&i2c_client->dev, + "No valid PR number (PRxxxxxxx)" \ + "found in image file name...\n"); + goto exit; + } + + strptr += 2; + while (strptr[index] >= '0' && strptr[index] <= '9') { + imagePR[index] = strptr[index]; + index++; + } + imagePR[index] = 0; + + retval = sstrtoul(imagePR, 10, &imageFirmwareID); + if (retval == -EINVAL) { + dev_err(&i2c_client->dev, + "invalid image firmware id...\n"); + goto exit; + } + + dev_info(&i2c_client->dev, + "Device firmware id %d, .img firmware id %d\n", + deviceFirmwareID, + (unsigned int)imageFirmwareID); + if (imageFirmwareID > deviceFirmwareID) { + flash_area = UI_FIRMWARE; + goto exit; + } + + /* device config id */ + retval = fwu->fn_ptr->read(fwu->rmi4_data, + fwu->f34_fd.ctrl_base_addr, + config_id, + sizeof(config_id)); + if (retval < 0) { + dev_err(&i2c_client->dev, + "Failed to read config ID (code %d).\n", retval); + flash_area = NONE; + goto exit; + } + deviceConfigID = extract_uint(config_id); + + dev_info(&i2c_client->dev, + "Device config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", + config_id[0], config_id[1], config_id[2], config_id[3]); + + /* .img config id */ + dev_info(&i2c_client->dev, + ".img config ID 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", + fwu->config_data[0], + fwu->config_data[1], + fwu->config_data[2], + fwu->config_data[3]); + imageConfigID = extract_uint(fwu->config_data); + + if (imageConfigID > deviceConfigID) { + flash_area = CONFIG_AREA; + goto exit; + } + +exit: + kfree(imagePR); + if (flash_area == NONE) + dev_info(&i2c_client->dev, + "Nothing needs to be updated\n"); + else + dev_info(&i2c_client->dev, + "Update %s block\n", + flash_area == UI_FIRMWARE ? "UI FW" : "CONFIG"); + return flash_area; +} + +static int fwu_scan_pdt(void) +{ + int retval; + unsigned char ii; + unsigned char intr_count = 0; + unsigned char intr_off; + unsigned char intr_src; + unsigned short addr; + bool f01found = false; + bool f34found = false; + struct synaptics_rmi4_fn_desc rmi_fd; + +#ifdef DEBUG_FW_UPDATE + dev_info(&fwu->rmi4_data->i2c_client->dev, "Scan PDT\n"); +#endif + + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + retval = fwu->fn_ptr->read(fwu->rmi4_data, + addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + if (rmi_fd.fn_number) { + dev_dbg(&fwu->rmi4_data->i2c_client->dev, + "%s: Found F%02x\n", + __func__, rmi_fd.fn_number); + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F01: + f01found = true; + fwu->f01_fd = rmi_fd; + fwu->addr_f01_interrupt_register = + fwu->f01_fd.data_base_addr + 1; + break; + case SYNAPTICS_RMI4_F34: + f34found = true; + fwu->f34_fd = rmi_fd; + fwu->intr_mask = 0; + intr_src = rmi_fd.intr_src_count; + intr_off = intr_count % 8; + for (ii = intr_off; + ii < ((intr_src & MASK_3BIT) + + intr_off); + ii++) + fwu->intr_mask |= 1 << ii; + break; + } + } else + break; + + intr_count += (rmi_fd.intr_src_count & MASK_3BIT); + } + + if (!f01found || !f34found) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Failed to find both F01 and F34\n", + __func__); + return -EINVAL; + } + + fwu_read_interrupt_status(); + return 0; +} + +static int fwu_write_blocks(unsigned char *block_ptr, unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char block_offset[] = {0, 0}; + unsigned short block_num; + struct i2c_client *i2c_client = fwu->rmi4_data->i2c_client; +#ifdef SHOW_PROGRESS + unsigned int progress = (command == CMD_WRITE_CONFIG_BLOCK) ? + 10 : 100; +#endif + +#ifdef DEBUG_FW_UPDATE + dev_info(&i2c_client->dev, + "%s: Start to update %s blocks\n", + __func__, + command == CMD_WRITE_CONFIG_BLOCK ? + "config" : "firmware"); +#endif + retval = fwu->fn_ptr->write(fwu->rmi4_data, + fwu->f34_fd.data_base_addr + BLOCK_NUMBER_OFFSET, + block_offset, + sizeof(block_offset)); + if (retval < 0) { + dev_err(&i2c_client->dev, + "%s: Failed to write to block number registers\n", + __func__); + return retval; + } + + for (block_num = 0; block_num < block_cnt; block_num++) { +#ifdef SHOW_PROGRESS + if (block_num % progress == 0) + dev_info(&i2c_client->dev, + "%s: update %s %3d / %3d\n", + __func__, + command == CMD_WRITE_CONFIG_BLOCK ? + "config" : "firmware", + block_num, block_cnt); +#endif + retval = fwu->fn_ptr->write(fwu->rmi4_data, + fwu->f34_fd.data_base_addr + BLOCK_DATA_OFFSET, + block_ptr, + fwu->block_size); + if (retval < 0) { + dev_err(&i2c_client->dev, + "%s: Failed to write block data (block %d)\n", + __func__, block_num); + return retval; + } + + retval = fwu_write_f34_command(command); + if (retval < 0) { + dev_err(&i2c_client->dev, + "%s: Failed to write command for block %d\n", + __func__, block_num); + return retval; + } + + retval = fwu_wait_for_idle(WRITE_WAIT_MS); + if (retval < 0) { + dev_err(&i2c_client->dev, + "%s: Failed to wait for idle status (block %d)\n", + __func__, block_num); + return retval; + } + + if (fwu->flash_control.status != 0x00) { + dev_err(&i2c_client->dev, + "%s: Flash block %d failed, status 0x%02X\n", + __func__, block_num, retval); + return -1; + } + + block_ptr += fwu->block_size; + } +#ifdef SHOW_PROGRESS + dev_info(&i2c_client->dev, + "%s: update %s %3d / %3d\n", + __func__, + command == CMD_WRITE_CONFIG_BLOCK ? + "config" : "firmware", + block_cnt, block_cnt); +#endif + return 0; +} + +static int fwu_write_firmware(void) +{ + return fwu_write_blocks((unsigned char *)fwu->firmware_data, + fwu->fw_block_count, CMD_WRITE_FW_BLOCK); +} + +static int fwu_write_configuration(void) +{ + return fwu_write_blocks((unsigned char *)fwu->config_data, + fwu->config_block_count, CMD_WRITE_CONFIG_BLOCK); +} + +static int fwu_write_bootloader_id(void) +{ + int retval; + +#ifdef DEBUG_FW_UPDATE + dev_info(&fwu->rmi4_data->i2c_client->dev, + "Write bootloader ID 0x%02X 0x%02X\n", + fwu->bootloader_id[0], + fwu->bootloader_id[1]); +#endif + retval = fwu->fn_ptr->write(fwu->rmi4_data, + fwu->f34_fd.data_base_addr + BLOCK_DATA_OFFSET, + fwu->bootloader_id, + sizeof(fwu->bootloader_id)); + if (retval < 0) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Failed to write bootloader ID\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_enter_flash_prog(void) +{ + int retval; + struct f01_device_status f01_device_status; + struct f01_device_control f01_device_control; + +#ifdef DEBUG_FW_UPDATE + dev_info(&fwu->rmi4_data->i2c_client->dev, "Enter bootloader mode\n"); +#endif + retval = fwu_read_f01_device_status(&f01_device_status); + if (retval < 0) + return retval; + + if (f01_device_status.flash_prog) { + dev_info(&fwu->rmi4_data->i2c_client->dev, + "%s: Already in flash prog mode\n", + __func__); + return 0; + } + + retval = fwu_write_bootloader_id(); + if (retval < 0) + return retval; + + retval = fwu_write_f34_command(CMD_ENABLE_FLASH_PROG); + if (retval < 0) + return retval; + + retval = fwu_wait_for_idle(ENABLE_WAIT_MS); + if (retval < 0) + return retval; + + retval = fwu_scan_pdt(); + if (retval < 0) + return retval; + + retval = fwu_read_f01_device_status(&f01_device_status); + if (retval < 0) + return retval; + + if (!f01_device_status.flash_prog) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Not in flash prog mode\n", + __func__); + return -EINVAL; + } + + retval = fwu_read_f34_queries(); + if (retval < 0) + return retval; + + retval = fwu->fn_ptr->read(fwu->rmi4_data, + fwu->f01_fd.ctrl_base_addr, + f01_device_control.data, + sizeof(f01_device_control.data)); + if (retval < 0) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Failed to read F01 device control\n", + __func__); + return retval; + } + + f01_device_control.nosleep = true; + f01_device_control.sleep_mode = SLEEP_MODE_NORMAL; + + retval = fwu->fn_ptr->write(fwu->rmi4_data, + fwu->f01_fd.ctrl_base_addr, + f01_device_control.data, + sizeof(f01_device_control.data)); + if (retval < 0) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Failed to write F01 device control\n", + __func__); + return retval; + } + + return retval; +} + +static int fwu_do_reflash(void) +{ + int retval; + + retval = fwu_enter_flash_prog(); + if (retval < 0) + return retval; + + dev_dbg(&fwu->rmi4_data->i2c_client->dev, + "%s: Entered flash prog mode\n", + __func__); + + retval = fwu_write_bootloader_id(); + if (retval < 0) + return retval; + + dev_dbg(&fwu->rmi4_data->i2c_client->dev, + "%s: Bootloader ID written\n", + __func__); + + retval = fwu_write_f34_command(CMD_ERASE_ALL); + if (retval < 0) + return retval; + + dev_dbg(&fwu->rmi4_data->i2c_client->dev, + "%s: Erase all command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS); + if (retval < 0) + return retval; + + if (fwu->flash_control.status != 0x00) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Erase all command failed, status 0x%02X\n", + __func__, retval); + return -1; + } + + if (fwu->firmware_data) { + retval = fwu_write_firmware(); + if (retval < 0) + return retval; + pr_notice("%s: Firmware programmed\n", __func__); + } + + if (fwu->config_data) { + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + pr_notice("%s: Configuration programmed\n", __func__); + } + + return retval; +} + +static int fwu_do_write_config(void) +{ + int retval; + + retval = fwu_enter_flash_prog(); + if (retval < 0) + return retval; + + dev_dbg(&fwu->rmi4_data->i2c_client->dev, + "%s: Entered flash prog mode\n", + __func__); + + if (fwu->config_area == PERM_CONFIG_AREA) { + fwu->config_block_count = fwu->perm_config_block_count; + goto write_config; + } + + retval = fwu_write_bootloader_id(); + if (retval < 0) + return retval; + + dev_dbg(&fwu->rmi4_data->i2c_client->dev, + "%s: Bootloader ID written\n", + __func__); + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_CONFIG); + break; + case BL_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_BL_CONFIG); + fwu->config_block_count = fwu->bl_config_block_count; + break; + case DISP_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_DISP_CONFIG); + fwu->config_block_count = fwu->disp_config_block_count; + break; + } + if (retval < 0) + return retval; + + dev_dbg(&fwu->rmi4_data->i2c_client->dev, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS); + if (retval < 0) + return retval; + + dev_dbg(&fwu->rmi4_data->i2c_client->dev, + "%s: Idle status detected\n", + __func__); + +write_config: + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + + pr_notice("%s: Config written\n", __func__); + + return retval; +} + +static int fwu_start_write_config(void) +{ + int retval; + struct image_header header; + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + break; + case PERM_CONFIG_AREA: + if (!fwu->flash_properties.has_perm_config) + return -EINVAL; + break; + case BL_CONFIG_AREA: + if (!fwu->flash_properties.has_bl_config) + return -EINVAL; + break; + case DISP_CONFIG_AREA: + if (!fwu->flash_properties.has_display_config) + return -EINVAL; + break; + default: + return -EINVAL; + } + + if (fwu->ext_data_source) + fwu->config_data = fwu->ext_data_source; + else + return -EINVAL; + + if (fwu->config_area == UI_CONFIG_AREA) { + parse_header(&header, fwu->ext_data_source); + + if (header.config_size) { + fwu->config_data = fwu->ext_data_source + + FW_IMAGE_OFFSET + + header.image_size; + } else { + return -EINVAL; + } + } + + pr_notice("%s: Start of write config process\n", __func__); + + retval = fwu_do_write_config(); + if (retval < 0) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Failed to write config\n", + __func__); + } + + fwu->rmi4_data->reset_device(fwu->rmi4_data); + + pr_notice("%s: End of write config process\n", __func__); + + return retval; +} + +static int fwu_do_read_config(void) +{ + int retval; + unsigned char block_offset[] = {0, 0}; + unsigned short block_num; + unsigned short block_count; + unsigned short index = 0; + + retval = fwu_enter_flash_prog(); + if (retval < 0) + goto exit; + + dev_dbg(&fwu->rmi4_data->i2c_client->dev, + "%s: Entered flash prog mode\n", + __func__); + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + block_count = fwu->config_block_count; + break; + case PERM_CONFIG_AREA: + if (!fwu->flash_properties.has_perm_config) { + retval = -EINVAL; + goto exit; + } + block_count = fwu->perm_config_block_count; + break; + case BL_CONFIG_AREA: + if (!fwu->flash_properties.has_bl_config) { + retval = -EINVAL; + goto exit; + } + block_count = fwu->bl_config_block_count; + break; + case DISP_CONFIG_AREA: + if (!fwu->flash_properties.has_display_config) { + retval = -EINVAL; + goto exit; + } + block_count = fwu->disp_config_block_count; + break; + default: + retval = -EINVAL; + goto exit; + } + + fwu->config_size = fwu->block_size * block_count; + + kfree(fwu->read_config_buf); + fwu->read_config_buf = kzalloc(fwu->config_size, GFP_KERNEL); + + block_offset[1] |= (fwu->config_area << 5); + + retval = fwu->fn_ptr->write(fwu->rmi4_data, + fwu->f34_fd.data_base_addr + BLOCK_NUMBER_OFFSET, + block_offset, + sizeof(block_offset)); + if (retval < 0) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Failed to write to block number registers\n", + __func__); + goto exit; + } + + for (block_num = 0; block_num < block_count; block_num++) { + retval = fwu_write_f34_command(CMD_READ_CONFIG_BLOCK); + if (retval < 0) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Failed to write read config command\n", + __func__); + goto exit; + } + + retval = fwu_wait_for_idle(WRITE_WAIT_MS); + if (retval < 0) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Failed to wait for idle status\n", + __func__); + goto exit; + } + + retval = fwu->fn_ptr->read(fwu->rmi4_data, + fwu->f34_fd.data_base_addr + BLOCK_DATA_OFFSET, + &fwu->read_config_buf[index], + fwu->block_size); + if (retval < 0) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Failed to read block data (block %d)\n", + __func__, block_num); + goto exit; + } + + index += fwu->block_size; + } + +exit: + fwu->rmi4_data->reset_device(fwu->rmi4_data); + + return retval; +} + +static int fwu_start_reflash(void) +{ + int retval; + struct image_header header; + const unsigned char *fw_image; + const struct firmware *fw_entry = NULL; + struct f01_device_status f01_device_status; + enum flash_area flash_area; + + pr_notice("%s: Start of reflash process\n", __func__); + + if (fwu->ext_data_source) + fw_image = fwu->ext_data_source; + else { + dev_dbg(&fwu->rmi4_data->i2c_client->dev, + "%s: Requesting firmware image %s\n", + __func__, FW_IMAGE_NAME); + + retval = request_firmware(&fw_entry, FW_IMAGE_NAME, + &fwu->rmi4_data->i2c_client->dev); + if (retval != 0) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Firmware image %s not available\n", + __func__, FW_IMAGE_NAME); + retval = -EINVAL; + goto exit; + } + + dev_dbg(&fwu->rmi4_data->i2c_client->dev, + "%s: Firmware image size = %d\n", + __func__, fw_entry->size); + + fw_image = fw_entry->data; + } + + parse_header(&header, fw_image); + + if (header.image_size) + fwu->firmware_data = fw_image + FW_IMAGE_OFFSET; + if (header.config_size) { + fwu->config_data = fw_image + FW_IMAGE_OFFSET + + header.image_size; + } + + if (fwu->ext_data_source) + flash_area = UI_FIRMWARE; + else + flash_area = fwu_go_nogo(); + + switch (flash_area) { + case NONE: + dev_info(&fwu->rmi4_data->i2c_client->dev, + "%s: No need to do reflash.\n", + __func__); + goto exit; + case UI_FIRMWARE: + retval = fwu_do_reflash(); + break; + case CONFIG_AREA: + retval = fwu_do_write_config(); + break; + default: + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Unknown flash area\n", + __func__); + goto exit; + } + + if (retval < 0) { + dev_err(&fwu->rmi4_data->i2c_client->dev, + "%s: Failed to do reflash\n", + __func__); + } + + /* reset device */ + fwu_reset_device(); + + /* check device status */ + retval = fwu_read_f01_device_status(&f01_device_status); + if (retval < 0) + goto exit; + + dev_info(&fwu->rmi4_data->i2c_client->dev, "Device is in %s mode\n", + f01_device_status.flash_prog == 1 ? "bootloader" : "UI"); + if (f01_device_status.flash_prog) + dev_info(&fwu->rmi4_data->i2c_client->dev, "Flash status %d\n", + f01_device_status.status_code); + + if (f01_device_status.flash_prog) { + dev_info(&fwu->rmi4_data->i2c_client->dev, + "%s: Device is in flash prog mode 0x%02X\n", + __func__, f01_device_status.status_code); + retval = 0; + goto exit; + } + + if (fw_entry) + release_firmware(fw_entry); + + pr_notice("%s: End of reflash process\n", __func__); +exit: + return retval; +} + +int synaptics_fw_updater(unsigned char *fw_data) +{ + int retval; + + if (!fwu) + return -ENODEV; + + if (!fwu->initialized) + return -ENODEV; + + fwu->ext_data_source = fw_data; + fwu->config_area = UI_CONFIG_AREA; + + retval = fwu_start_reflash(); + + return retval; +} +EXPORT_SYMBOL(synaptics_fw_updater); + +static ssize_t fwu_sysfs_show_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (count < fwu->config_size) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Not enough space (%d bytes) in buffer\n", + __func__, count); + return -EINVAL; + } + + memcpy(buf, fwu->read_config_buf, fwu->config_size); + + return fwu->config_size; +} + +static ssize_t fwu_sysfs_store_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + memcpy((void *)(&fwu->ext_data_source[fwu->data_pos]), + (const void *)buf, + count); + + fwu->data_pos += count; + + return count; +} + +static ssize_t fwu_sysfs_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (sscanf(buf, "%u", &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (input != 1) { + retval = -EINVAL; + goto exit; + } + + retval = synaptics_fw_updater(fwu->ext_data_source); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to do reflash\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + return retval; +} + +static ssize_t fwu_sysfs_write_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (sscanf(buf, "%u", &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (input != 1) { + retval = -EINVAL; + goto exit; + } + + retval = fwu_start_write_config(); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write config\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + return retval; +} + +static ssize_t fwu_sysfs_read_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + retval = fwu_do_read_config(); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read config\n", + __func__); + return retval; + } + + return count; +} + +static ssize_t fwu_sysfs_config_area_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long config_area; + + retval = sstrtoul(buf, 10, &config_area); + if (retval) + return retval; + + fwu->config_area = config_area; + + return count; +} + +static ssize_t fwu_sysfs_image_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long size; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = sstrtoul(buf, 10, &size); + if (retval) + return retval; + + fwu->image_size = size; + fwu->data_pos = 0; + + kfree(fwu->ext_data_source); + fwu->ext_data_source = kzalloc(fwu->image_size, GFP_KERNEL); + if (!fwu->ext_data_source) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for image data\n", + __func__); + return -ENOMEM; + } + + return count; +} + +static ssize_t fwu_sysfs_block_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->block_size); +} + +static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->fw_block_count); +} + +static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->config_block_count); +} + +static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->perm_config_block_count); +} + +static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->bl_config_block_count); +} + +static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->disp_config_block_count); +} + +static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + if (fwu->intr_mask & intr_mask) + fwu_read_f34_flash_status(); + + return; +} + +static void synaptics_rmi4_fwu_work(struct work_struct *work) +{ + fwu_start_reflash(); +} + +static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char attr_count; + struct pdt_properties pdt_props; + + fwu = kzalloc(sizeof(*fwu), GFP_KERNEL); + if (!fwu) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for fwu\n", + __func__); + goto exit; + } + + fwu->fn_ptr = kzalloc(sizeof(*(fwu->fn_ptr)), GFP_KERNEL); + if (!fwu->fn_ptr) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for fn_ptr\n", + __func__); + retval = -ENOMEM; + goto exit_free_fwu; + } + + fwu->rmi4_data = rmi4_data; + fwu->fn_ptr->read = rmi4_data->i2c_read; + fwu->fn_ptr->write = rmi4_data->i2c_write; + fwu->fn_ptr->enable = rmi4_data->irq_enable; + + retval = fwu->fn_ptr->read(rmi4_data, + PDT_PROPS, + pdt_props.data, + sizeof(pdt_props.data)); + if (retval < 0) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Failed to read PDT properties, assuming 0x00\n", + __func__); + } else if (pdt_props.has_bsr) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Reflash for LTS not currently supported\n", + __func__); + goto exit_free_mem; + } + + retval = fwu_scan_pdt(); + if (retval < 0) + goto exit_free_mem; + + fwu->productinfo1 = rmi4_data->rmi4_mod_info.product_info[0]; + fwu->productinfo2 = rmi4_data->rmi4_mod_info.product_info[1]; + + memcpy(fwu->product_id, rmi4_data->rmi4_mod_info.product_id_string, + SYNAPTICS_RMI4_PRODUCT_ID_SIZE); + fwu->product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE] = 0; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: F01 product info: 0x%04x 0x%04x\n", + __func__, fwu->productinfo1, fwu->productinfo2); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: F01 product ID: %s\n", + __func__, fwu->product_id); + + retval = fwu_read_f34_queries(); + if (retval < 0) + goto exit_free_mem; + + fwu->initialized = true; + fwu->force_update = FORCE_UPDATE; + + retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj, + &dev_attr_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs bin file\n", + __func__); + goto exit_free_mem; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto exit_remove_attrs; + } + } + +#ifdef INSIDE_FIRMWARE_UPDATE + fwu->fwu_workqueue = create_singlethread_workqueue("fwu_workqueue"); + INIT_DELAYED_WORK(&fwu->fwu_work, synaptics_rmi4_fwu_work); + queue_delayed_work(fwu->fwu_workqueue, + &fwu->fwu_work, + msecs_to_jiffies(1000)); +#endif + return 0; + +exit_remove_attrs: +for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); +} + +sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data); + +exit_free_mem: + kfree(fwu->fn_ptr); + +exit_free_fwu: + kfree(fwu); + +exit: + return 0; +} + +static void synaptics_rmi4_fwu_remove(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char attr_count; + + sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data); + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + + kfree(fwu->fn_ptr); + kfree(fwu); + + complete(&remove_complete); + + return; +} + +static int __init rmi4_fw_update_module_init(void) +{ + synaptics_rmi4_new_function(RMI_FW_UPDATER, true, + synaptics_rmi4_fwu_init, + synaptics_rmi4_fwu_remove, + synaptics_rmi4_fwu_attn); + return 0; +} + +static void __exit rmi4_fw_update_module_exit(void) +{ + init_completion(&remove_complete); + synaptics_rmi4_new_function(RMI_FW_UPDATER, false, + synaptics_rmi4_fwu_init, + synaptics_rmi4_fwu_remove, + synaptics_rmi4_fwu_attn); + wait_for_completion(&remove_complete); + return; +} + +module_init(rmi4_fw_update_module_init); +module_exit(rmi4_fw_update_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("RMI4 FW Update Module"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(SYNAPTICS_RMI4_DRIVER_VERSION); diff --git a/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c new file mode 100644 index 000000000000..76f9155bd49c --- /dev/null +++ b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.c @@ -0,0 +1,2162 @@ +/* + * Synaptics RMI4 touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com> + * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/gpio.h> +#include <linux/regulator/consumer.h> +#include <linux/input/synaptics_dsx.h> +#include "synaptics_i2c_rmi4.h" +#ifdef KERNEL_ABOVE_2_6_38 +#include <linux/input/mt.h> +#endif + +#define DRIVER_NAME "synaptics_rmi4_i2c" +#define INPUT_PHYS_NAME "synaptics_rmi4_i2c/input0" + +#ifdef KERNEL_ABOVE_2_6_38 +#define TYPE_B_PROTOCOL +#endif + +#define NO_0D_WHILE_2D +/* +#define REPORT_2D_Z +*/ +#define REPORT_2D_W + +#define RPT_TYPE (1 << 0) +#define RPT_X_LSB (1 << 1) +#define RPT_X_MSB (1 << 2) +#define RPT_Y_LSB (1 << 3) +#define RPT_Y_MSB (1 << 4) +#define RPT_Z (1 << 5) +#define RPT_WX (1 << 6) +#define RPT_WY (1 << 7) +#define RPT_DEFAULT (RPT_TYPE | RPT_X_LSB | RPT_X_MSB | RPT_Y_LSB | RPT_Y_MSB) + +#define EXP_FN_DET_INTERVAL 1000 /* ms */ +#define POLLING_PERIOD 1 /* ms */ +#define SYN_I2C_RETRY_TIMES 10 +#define MAX_ABS_MT_TOUCH_MAJOR 15 + +#define F01_STD_QUERY_LEN 21 +#define F01_BUID_ID_OFFSET 18 +#define F11_STD_QUERY_LEN 9 +#define F11_STD_CTRL_LEN 10 +#define F11_STD_DATA_LEN 12 + +#define NORMAL_OPERATION (0 << 0) +#define SENSOR_SLEEP (1 << 0) +#define NO_SLEEP_OFF (0 << 3) +#define NO_SLEEP_ON (1 << 3) + +static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, + unsigned short length); + +static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, + unsigned short length); + +static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data); + +#ifdef CONFIG_HAS_EARLYSUSPEND +static ssize_t synaptics_rmi4_full_pm_cycle_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_full_pm_cycle_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static void synaptics_rmi4_early_suspend(struct early_suspend *h); + +static void synaptics_rmi4_late_resume(struct early_suspend *h); + +static int synaptics_rmi4_suspend(struct device *dev); + +static int synaptics_rmi4_resume(struct device *dev); +#endif + +static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +struct synaptics_rmi4_f01_device_status { + union { + struct { + unsigned char status_code:4; + unsigned char reserved:2; + unsigned char flash_prog:1; + unsigned char unconfigured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f1a_query { + union { + struct { + unsigned char max_button_count:3; + unsigned char reserved:5; + unsigned char has_general_control:1; + unsigned char has_interrupt_enable:1; + unsigned char has_multibutton_select:1; + unsigned char has_tx_rx_map:1; + unsigned char has_perbutton_threshold:1; + unsigned char has_release_threshold:1; + unsigned char has_strongestbtn_hysteresis:1; + unsigned char has_filter_strength:1; + } __packed; + unsigned char data[2]; + }; +}; + +struct synaptics_rmi4_f1a_control_0 { + union { + struct { + unsigned char multibutton_report:2; + unsigned char filter_mode:2; + unsigned char reserved:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f1a_control_3_4 { + unsigned char transmitterbutton; + unsigned char receiverbutton; +}; + +struct synaptics_rmi4_f1a_control { + struct synaptics_rmi4_f1a_control_0 general_control; + unsigned char *button_int_enable; + unsigned char *multi_button; + struct synaptics_rmi4_f1a_control_3_4 *electrode_map; + unsigned char *button_threshold; + unsigned char button_release_threshold; + unsigned char strongest_button_hysteresis; + unsigned char filter_strength; +}; + +struct synaptics_rmi4_f1a_handle { + int button_bitmask_size; + unsigned char button_count; + unsigned char valid_button_count; + unsigned char *button_data_buffer; + unsigned char *button_map; + struct synaptics_rmi4_f1a_query button_query; + struct synaptics_rmi4_f1a_control button_control; +}; + +struct synaptics_rmi4_exp_fn { + enum exp_fn fn_type; + bool inserted; + int (*func_init)(struct synaptics_rmi4_data *rmi4_data); + void (*func_remove)(struct synaptics_rmi4_data *rmi4_data); + void (*func_attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask); + struct list_head link; +}; + +static struct device_attribute attrs[] = { +#ifdef CONFIG_HAS_EARLYSUSPEND + __ATTR(full_pm_cycle, (S_IRUGO | S_IWUGO), + synaptics_rmi4_full_pm_cycle_show, + synaptics_rmi4_full_pm_cycle_store), +#endif + __ATTR(reset, S_IWUGO, + synaptics_rmi4_show_error, + synaptics_rmi4_f01_reset_store), + __ATTR(productinfo, S_IRUGO, + synaptics_rmi4_f01_productinfo_show, + synaptics_rmi4_store_error), + __ATTR(buildid, S_IRUGO, + synaptics_rmi4_f01_buildid_show, + synaptics_rmi4_store_error), + __ATTR(flashprog, S_IRUGO, + synaptics_rmi4_f01_flashprog_show, + synaptics_rmi4_store_error), + __ATTR(0dbutton, (S_IRUGO | S_IWUGO), + synaptics_rmi4_0dbutton_show, + synaptics_rmi4_0dbutton_store), +}; + +static bool exp_fn_inited; +static struct mutex exp_fn_list_mutex; +static struct list_head exp_fn_list; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static ssize_t synaptics_rmi4_full_pm_cycle_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", + rmi4_data->full_pm_cycle); +} + +static ssize_t synaptics_rmi4_full_pm_cycle_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + rmi4_data->full_pm_cycle = input > 0 ? 1 : 0; + + return count; +} +#endif + +static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int reset; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + if (sscanf(buf, "%u", &reset) != 1) + return -EINVAL; + + if (reset != 1) + return -EINVAL; + + retval = synaptics_rmi4_reset_device(rmi4_data); + if (retval < 0) { + dev_err(dev, + "%s: Failed to issue reset command, error = %d\n", + __func__, retval); + return retval; + } + + return count; +} + +static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "0x%02x 0x%02x\n", + (rmi4_data->rmi4_mod_info.product_info[0]), + (rmi4_data->rmi4_mod_info.product_info[1])); +} + +static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int build_id; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + build_id = (unsigned int)rmi->build_id[0] + + (unsigned int)rmi->build_id[1] * 0x100 + + (unsigned int)rmi->build_id[2] * 0x10000; + + return snprintf(buf, PAGE_SIZE, "%u\n", + build_id); +} + +static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + struct synaptics_rmi4_f01_device_status device_status; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr, + device_status.data, + sizeof(device_status.data)); + if (retval < 0) { + dev_err(dev, + "%s: Failed to read device status, error = %d\n", + __func__, retval); + return retval; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", + device_status.flash_prog); +} + +static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", + rmi4_data->button_0d_enabled); +} + +static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + unsigned char ii; + unsigned char intr_enable; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + input = input > 0 ? 1 : 0; + + if (rmi4_data->button_0d_enabled == input) + return count; + + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) { + ii = fhandler->intr_reg_num; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr + + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; + + if (input == 1) + intr_enable |= fhandler->intr_mask; + else + intr_enable &= ~fhandler->intr_mask; + + retval = synaptics_rmi4_i2c_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr + + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; + } + } + } + + rmi4_data->button_0d_enabled = input; + + return count; +} + + /** + * synaptics_rmi4_set_page() + * + * Called by synaptics_rmi4_i2c_read() and synaptics_rmi4_i2c_write(). + * + * This function writes to the page select register to switch to the + * assigned page. + */ +static int synaptics_rmi4_set_page(struct synaptics_rmi4_data *rmi4_data, + unsigned int address) +{ + int retval = 0; + unsigned char retry; + unsigned char buf[PAGE_SELECT_LEN]; + unsigned char page; + struct i2c_client *i2c = rmi4_data->i2c_client; + + page = ((address >> 8) & MASK_8BIT); + if (page != rmi4_data->current_page) { + buf[0] = MASK_8BIT; + buf[1] = page; + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + retval = i2c_master_send(i2c, buf, PAGE_SELECT_LEN); + if (retval != PAGE_SELECT_LEN) { + dev_err(&i2c->dev, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + } else { + rmi4_data->current_page = page; + break; + } + } + } else + return PAGE_SELECT_LEN; + return (retval == PAGE_SELECT_LEN) ? retval : -EIO; +} + + /** + * synaptics_rmi4_i2c_read() + * + * Called by various functions in this driver, and also exported to + * other expansion Function modules such as rmi_dev. + * + * This function reads data of an arbitrary length from the sensor, + * starting from an assigned register address of the sensor, via I2C + * with a retry mechanism. + */ +static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned short length) +{ + int retval; + unsigned char retry; + unsigned char buf; + struct i2c_msg msg[] = { + { + .addr = rmi4_data->i2c_client->addr, + .flags = 0, + .len = 1, + .buf = &buf, + }, + { + .addr = rmi4_data->i2c_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + }, + }; + + buf = addr & MASK_8BIT; + + mutex_lock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + retval = synaptics_rmi4_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) + goto exit; + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(rmi4_data->i2c_client->adapter, msg, 2) == 2) { + retval = length; + break; + } + dev_err(&rmi4_data->i2c_client->dev, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + } + + if (retry == SYN_I2C_RETRY_TIMES) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: I2C read over retry limit\n", + __func__); + retval = -EIO; + } + +exit: + mutex_unlock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + return retval; +} + + /** + * synaptics_rmi4_i2c_write() + * + * Called by various functions in this driver, and also exported to + * other expansion Function modules such as rmi_dev. + * + * This function writes data of an arbitrary length to the sensor, + * starting from an assigned register address of the sensor, via I2C with + * a retry mechanism. + */ +static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned short length) +{ + int retval; + unsigned char retry; + unsigned char buf[length + 1]; + struct i2c_msg msg[] = { + { + .addr = rmi4_data->i2c_client->addr, + .flags = 0, + .len = length + 1, + .buf = buf, + } + }; + + mutex_lock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + retval = synaptics_rmi4_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) + goto exit; + + buf[0] = addr & MASK_8BIT; + memcpy(&buf[1], &data[0], length); + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(rmi4_data->i2c_client->adapter, msg, 1) == 1) { + retval = length; + break; + } + dev_err(&rmi4_data->i2c_client->dev, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + } + + if (retry == SYN_I2C_RETRY_TIMES) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: I2C write over retry limit\n", + __func__); + retval = -EIO; + } + +exit: + mutex_unlock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + return retval; +} + + /** + * synaptics_rmi4_f11_abs_report() + * + * Called by synaptics_rmi4_report_touch() when valid Function $11 + * finger data has been detected. + * + * This function reads the Function $11 data registers, determines the + * status of each finger supported by the Function, processes any + * necessary coordinate manipulation, reports the finger data to + * the input subsystem, and returns the number of fingers detected. + */ +static int synaptics_rmi4_f11_abs_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; /* number of touch points */ + unsigned char reg_index; + unsigned char finger; + unsigned char fingers_supported; + unsigned char num_of_finger_status_regs; + unsigned char finger_shift; + unsigned char finger_status; + unsigned char data_reg_blk_size; + unsigned char finger_status_reg[3]; + unsigned char data[F11_STD_DATA_LEN]; + unsigned short data_addr; + unsigned short data_offset; + int x; + int y; + int wx; + int wy; + + /* + * The number of finger status registers is determined by the + * maximum number of fingers supported - 2 bits per finger. So + * the number of finger status registers to read is: + * register_count = ceil(max_num_of_fingers / 4) + */ + fingers_supported = fhandler->num_of_data_points; + num_of_finger_status_regs = (fingers_supported + 3) / 4; + data_addr = fhandler->full_addr.data_base; + data_reg_blk_size = fhandler->size_of_data_register_block; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr, + finger_status_reg, + num_of_finger_status_regs); + if (retval < 0) + return 0; + + for (finger = 0; finger < fingers_supported; finger++) { + reg_index = finger / 4; + finger_shift = (finger % 4) * 2; + finger_status = (finger_status_reg[reg_index] >> finger_shift) + & MASK_2BIT; + + /* + * Each 2-bit finger status field represents the following: + * 00 = finger not present + * 01 = finger present and data accurate + * 10 = finger present but data may be inaccurate + * 11 = reserved + */ +#ifdef TYPE_B_PROTOCOL + input_mt_slot(rmi4_data->input_dev, finger); + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, finger_status != 0); +#endif + + if (finger_status) { + data_offset = data_addr + + num_of_finger_status_regs + + (finger * data_reg_blk_size); + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_offset, + data, + data_reg_blk_size); + if (retval < 0) + return 0; + + x = (data[0] << 4) | (data[2] & MASK_4BIT); + y = (data[1] << 4) | ((data[2] >> 4) & MASK_4BIT); + wx = (data[3] & MASK_4BIT); + wy = (data[3] >> 4) & MASK_4BIT; + + if (rmi4_data->board->x_flip) + x = rmi4_data->sensor_max_x - x; + if (rmi4_data->board->y_flip) + y = rmi4_data->sensor_max_y - y; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Finger %d:\n" + "status = 0x%02x\n" + "x = %d\n" + "y = %d\n" + "wx = %d\n" + "wy = %d\n", + __func__, finger, + finger_status, + x, y, wx, wy); + + input_report_key(rmi4_data->input_dev, + BTN_TOUCH, 1); + input_report_key(rmi4_data->input_dev, + BTN_TOOL_FINGER, 1); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_Y, y); + +#ifdef REPORT_2D_W + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, max(wx, wy)); + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, min(wx, wy)); +#endif +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + touch_count++; + } + } + +#ifndef TYPE_B_PROTOCOL + if (!touch_count) + input_mt_sync(rmi4_data->input_dev); +#else + /* sync after groups of events */ + #ifdef KERNEL_ABOVE_3_7 + input_mt_sync_frame(rmi4_data->input_dev); + #endif +#endif + + input_sync(rmi4_data->input_dev); + + return touch_count; +} + +static void synaptics_rmi4_f1a_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char button; + unsigned char index; + unsigned char shift; + unsigned char status; + unsigned char *data; + unsigned short data_addr = fhandler->full_addr.data_base; + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + static unsigned char do_once = 1; + static bool current_status[MAX_NUMBER_OF_BUTTONS]; +#ifdef NO_0D_WHILE_2D + static bool before_2d_status[MAX_NUMBER_OF_BUTTONS]; + static bool while_2d_status[MAX_NUMBER_OF_BUTTONS]; +#endif + + if (do_once) { + memset(current_status, 0, sizeof(current_status)); +#ifdef NO_0D_WHILE_2D + memset(before_2d_status, 0, sizeof(before_2d_status)); + memset(while_2d_status, 0, sizeof(while_2d_status)); +#endif + do_once = 0; + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr, + f1a->button_data_buffer, + f1a->button_bitmask_size); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read button data registers\n", + __func__); + return; + } + + data = f1a->button_data_buffer; + + for (button = 0; button < f1a->valid_button_count; button++) { + index = button / 8; + shift = button % 8; + status = ((data[index] >> shift) & MASK_1BIT); + + if (current_status[button] == status) + continue; + else + current_status[button] = status; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Button %d (code %d) ->%d\n", + __func__, button, + f1a->button_map[button], + status); +#ifdef NO_0D_WHILE_2D + if (rmi4_data->fingers_on_2d == false) { + if (status == 1) { + before_2d_status[button] = 1; + } else { + if (while_2d_status[button] == 1) { + while_2d_status[button] = 0; + continue; + } else { + before_2d_status[button] = 0; + } + } + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + } else { + if (before_2d_status[button] == 1) { + before_2d_status[button] = 0; + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + } else { + if (status == 1) + while_2d_status[button] = 1; + else + while_2d_status[button] = 0; + } + } +#else + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); +#endif + } + + input_sync(rmi4_data->input_dev); + + return; +} + + /** + * synaptics_rmi4_report_touch() + * + * Called by synaptics_rmi4_sensor_report(). + * + * This function calls the appropriate finger data reporting function + * based on the function handler it receives and returns the number of + * fingers detected. + */ +static void synaptics_rmi4_report_touch(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + unsigned char *touch_count) +{ + unsigned char touch_count_2d; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Function %02x reporting\n", + __func__, fhandler->fn_number); + + switch (fhandler->fn_number) { + case SYNAPTICS_RMI4_F11: + touch_count_2d = synaptics_rmi4_f11_abs_report(rmi4_data, + fhandler); + + *touch_count += touch_count_2d; + + if (touch_count_2d) + rmi4_data->fingers_on_2d = true; + else + rmi4_data->fingers_on_2d = false; + break; + + case SYNAPTICS_RMI4_F1A: + synaptics_rmi4_f1a_report(rmi4_data, fhandler); + break; + + default: + break; + } + + return; +} + + /** + * synaptics_rmi4_sensor_report() + * + * Called by synaptics_rmi4_irq(). + * + * This function determines the interrupt source(s) from the sensor + * and calls synaptics_rmi4_report_touch() with the appropriate + * function handler for each function with valid data inputs. + */ +static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char touch_count = 0; + unsigned char intr[MAX_INTR_REGISTERS]; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_exp_fn *exp_fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + /* + * Get interrupt status information from F01 Data1 register to + * determine the source(s) that are flagging the interrupt. + */ + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + intr, + rmi4_data->num_of_intr_regs); + if (retval < 0) + return retval; + + /* + * Traverse the function handler list and service the source(s) + * of the interrupt accordingly. + */ + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + if (fhandler->intr_mask & + intr[fhandler->intr_reg_num]) { + synaptics_rmi4_report_touch(rmi4_data, + fhandler, &touch_count); + } + } + } + } + + mutex_lock(&exp_fn_list_mutex); + if (!list_empty(&exp_fn_list)) { + list_for_each_entry(exp_fhandler, &exp_fn_list, link) { + if (exp_fhandler->inserted && + (exp_fhandler->func_attn != NULL)) + exp_fhandler->func_attn(rmi4_data, intr[0]); + } + } + mutex_unlock(&exp_fn_list_mutex); + + return touch_count; +} + + /** + * synaptics_rmi4_irq() + * + * Called by the kernel when an interrupt occurs (when the sensor + * asserts the attention irq). + * + * This function is the ISR thread and handles the acquisition + * and the reporting of finger data when the presence of fingers + * is detected. + */ +static irqreturn_t synaptics_rmi4_irq(int irq, void *data) +{ + struct synaptics_rmi4_data *rmi4_data = data; + + synaptics_rmi4_sensor_report(rmi4_data); + + return IRQ_HANDLED; +} + + /** + * synaptics_rmi4_irq_enable() + * + * Called by synaptics_rmi4_probe() and the power management functions + * in this driver and also exported to other expansion Function modules + * such as rmi_dev. + * + * This function handles the enabling and disabling of the attention + * irq including the setting up of the ISR thread. + */ +static int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval = 0; + unsigned char intr_status; + const struct synaptics_rmi4_platform_data *platform_data = + rmi4_data->i2c_client->dev.platform_data; + + if (enable) { + if (rmi4_data->irq_enabled) + return retval; + + /* Clear interrupts first */ + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + &intr_status, + rmi4_data->num_of_intr_regs); + if (retval < 0) + return retval; + + retval = request_threaded_irq(rmi4_data->irq, NULL, + synaptics_rmi4_irq, platform_data->irq_flags, + DRIVER_NAME, rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create irq thread\n", + __func__); + return retval; + } + + rmi4_data->irq_enabled = true; + } else { + if (rmi4_data->irq_enabled) { + disable_irq(rmi4_data->irq); + free_irq(rmi4_data->irq, rmi4_data); + rmi4_data->irq_enabled = false; + } + } + + return retval; +} + + /** + * synaptics_rmi4_f11_init() + * + * Called by synaptics_rmi4_query_device(). + * + * This funtion parses information from the Function 11 registers + * and determines the number of fingers supported, x and y data ranges, + * offset to the associated interrupt status register, interrupt bit + * mask, and gathers finger data acquisition capabilities from the query + * registers. + */ +static int synaptics_rmi4_f11_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned char intr_offset; + unsigned char abs_data_size; + unsigned char abs_data_blk_size; + unsigned char query[F11_STD_QUERY_LEN]; + unsigned char control[F11_STD_CTRL_LEN]; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = fd->intr_src_count; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.query_base, + query, + sizeof(query)); + if (retval < 0) + return retval; + + /* Maximum number of fingers supported */ + if ((query[1] & MASK_3BIT) <= 4) + fhandler->num_of_data_points = (query[1] & MASK_3BIT) + 1; + else if ((query[1] & MASK_3BIT) == 5) + fhandler->num_of_data_points = 10; + + rmi4_data->num_of_fingers = fhandler->num_of_data_points; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.ctrl_base, + control, + sizeof(control)); + if (retval < 0) + return retval; + + /* Maximum x and y */ + rmi4_data->sensor_max_x = ((control[6] & MASK_8BIT) << 0) | + ((control[7] & MASK_4BIT) << 8); + rmi4_data->sensor_max_y = ((control[8] & MASK_8BIT) << 0) | + ((control[9] & MASK_4BIT) << 8); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Function %02x max x = %d max y = %d\n", + __func__, fhandler->fn_number, + rmi4_data->sensor_max_x, + rmi4_data->sensor_max_y); + + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + + abs_data_size = query[5] & MASK_2BIT; + abs_data_blk_size = 3 + (2 * (abs_data_size == 0 ? 1 : 0)); + fhandler->size_of_data_register_block = abs_data_blk_size; + + return retval; +} + +static int synaptics_rmi4_f1a_alloc_mem(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + struct synaptics_rmi4_f1a_handle *f1a; + + f1a = kzalloc(sizeof(*f1a), GFP_KERNEL); + if (!f1a) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for function handle\n", + __func__); + return -ENOMEM; + } + + fhandler->data = (void *)f1a; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.query_base, + f1a->button_query.data, + sizeof(f1a->button_query.data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read query registers\n", + __func__); + return retval; + } + + f1a->button_count = f1a->button_query.max_button_count + 1; + f1a->button_bitmask_size = (f1a->button_count + 7) / 8; + + f1a->button_data_buffer = kcalloc(f1a->button_bitmask_size, + sizeof(*(f1a->button_data_buffer)), GFP_KERNEL); + if (!f1a->button_data_buffer) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for data buffer\n", + __func__); + return -ENOMEM; + } + + f1a->button_map = kcalloc(f1a->button_count, + sizeof(*(f1a->button_map)), GFP_KERNEL); + if (!f1a->button_map) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for button map\n", + __func__); + return -ENOMEM; + } + + return 0; +} + +static int synaptics_rmi4_capacitance_button_map( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + unsigned char ii; + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board; + + if (!pdata->capacitance_button_map) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: capacitance_button_map is" \ + "NULL in board file\n", + __func__); + return -ENODEV; + } else if (!pdata->capacitance_button_map->map) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Button map is missing in board file\n", + __func__); + return -ENODEV; + } else { + if (pdata->capacitance_button_map->nbuttons != + f1a->button_count) { + f1a->valid_button_count = min(f1a->button_count, + pdata->capacitance_button_map->nbuttons); + } else { + f1a->valid_button_count = f1a->button_count; + } + + for (ii = 0; ii < f1a->valid_button_count; ii++) + f1a->button_map[ii] = + pdata->capacitance_button_map->map[ii]; + } + + return 0; +} + +static void synaptics_rmi4_f1a_kfree(struct synaptics_rmi4_fn *fhandler) +{ + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + + if (f1a) { + kfree(f1a->button_data_buffer); + kfree(f1a->button_map); + kfree(f1a); + fhandler->data = NULL; + } + + return; +} + +static int synaptics_rmi4_f1a_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned short intr_offset; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = fd->intr_src_count; + + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + + retval = synaptics_rmi4_f1a_alloc_mem(rmi4_data, fhandler); + if (retval < 0) + goto error_exit; + + retval = synaptics_rmi4_capacitance_button_map(rmi4_data, fhandler); + if (retval < 0) + goto error_exit; + + rmi4_data->button_0d_enabled = 1; + + return 0; + +error_exit: + synaptics_rmi4_f1a_kfree(fhandler); + + return retval; +} + +static int synaptics_rmi4_alloc_fh(struct synaptics_rmi4_fn **fhandler, + struct synaptics_rmi4_fn_desc *rmi_fd, int page_number) +{ + *fhandler = kmalloc(sizeof(**fhandler), GFP_KERNEL); + if (!(*fhandler)) + return -ENOMEM; + + (*fhandler)->full_addr.data_base = + (rmi_fd->data_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.ctrl_base = + (rmi_fd->ctrl_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.cmd_base = + (rmi_fd->cmd_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.query_base = + (rmi_fd->query_base_addr | + (page_number << 8)); + + return 0; +} + + + /** + * synaptics_rmi4_query_device_info() + * + * Called by synaptics_rmi4_query_device(). + * + */ +static int synaptics_rmi4_query_device_info( + struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char f01_query[F01_STD_QUERY_LEN]; + struct synaptics_rmi4_device_info *rmi = &(rmi4_data->rmi4_mod_info); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr, + f01_query, + sizeof(f01_query)); + if (retval < 0) + return retval; + + /* RMI Version 4.0 currently supported */ + rmi->version_major = 4; + rmi->version_minor = 0; + + rmi->manufacturer_id = f01_query[0]; + rmi->product_props = f01_query[1]; + rmi->product_info[0] = f01_query[2] & MASK_7BIT; + rmi->product_info[1] = f01_query[3] & MASK_7BIT; + rmi->date_code[0] = f01_query[4] & MASK_5BIT; + rmi->date_code[1] = f01_query[5] & MASK_4BIT; + rmi->date_code[2] = f01_query[6] & MASK_5BIT; + rmi->tester_id = ((f01_query[7] & MASK_7BIT) << 8) | + (f01_query[8] & MASK_7BIT); + rmi->serial_number = ((f01_query[9] & MASK_7BIT) << 8) | + (f01_query[10] & MASK_7BIT); + memcpy(rmi->product_id_string, &f01_query[11], 10); + + if (rmi->manufacturer_id != 1) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Non-Synaptics device found, manufacturer ID = %d\n", + __func__, rmi->manufacturer_id); + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr + F01_BUID_ID_OFFSET, + rmi->build_id, + sizeof(rmi->build_id)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read firmware build id (code %d)\n", + __func__, retval); + return retval; + } + return retval; +} + + /** + * synaptics_rmi4_query_device() + * + * Called by synaptics_rmi4_probe(). + * + * This funtion scans the page description table, records the offsets + * to the register types of Function $01, sets up the function handlers + * for Function $11 and Function $12, determines the number of interrupt + * sources from the sensor, adds valid Functions with data inputs to the + * Function linked list, parses information from the query registers of + * Function $01, and enables the interrupt sources from the valid Functions + * with data inputs. + */ +static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char ii; + unsigned char page_number; + unsigned char intr_count = 0; + unsigned char data_sources = 0; + unsigned short pdt_entry_addr; + unsigned short intr_addr; + struct synaptics_rmi4_f01_device_status status; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + INIT_LIST_HEAD(&rmi->support_fn_list); + + /* Scan the page description tables of the pages to service */ + for (page_number = 0; page_number < PAGES_TO_SERVICE; page_number++) { + for (pdt_entry_addr = PDT_START; pdt_entry_addr > PDT_END; + pdt_entry_addr -= PDT_ENTRY_SIZE) { + pdt_entry_addr |= (page_number << 8); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + pdt_entry_addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + fhandler = NULL; + + if (rmi_fd.fn_number == 0) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Reached end of PDT\n", + __func__); + break; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: F%02x found (page %d)\n", + __func__, rmi_fd.fn_number, + page_number); + + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F01: + rmi4_data->f01_query_base_addr = + rmi_fd.query_base_addr; + rmi4_data->f01_ctrl_base_addr = + rmi_fd.ctrl_base_addr; + rmi4_data->f01_data_base_addr = + rmi_fd.data_base_addr; + rmi4_data->f01_cmd_base_addr = + rmi_fd.cmd_base_addr; + + retval = + synaptics_rmi4_query_device_info(rmi4_data); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status.data, + sizeof(status.data)); + if (retval < 0) + return retval; + + if (status.flash_prog == 1) { + pr_notice("%s: In flash prog mode, status = 0x%02x\n", + __func__, + status.status_code); + goto flash_prog_mode; + } + break; + + case SYNAPTICS_RMI4_F34: + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi_fd.ctrl_base_addr, + rmi->config_id, + sizeof(rmi->config_id)); + if (retval < 0) + return retval; + break; + + case SYNAPTICS_RMI4_F11: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%d\n", + __func__, + rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f11_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) + return retval; + break; + + case SYNAPTICS_RMI4_F1A: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%d\n", + __func__, + rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f1a_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) + return retval; + break; + } + + /* Accumulate the interrupt count */ + intr_count += (rmi_fd.intr_src_count & MASK_3BIT); + + if (fhandler && rmi_fd.intr_src_count) { + list_add_tail(&fhandler->link, + &rmi->support_fn_list); + } + } + } + +flash_prog_mode: + rmi4_data->num_of_intr_regs = (intr_count + 7) / 8; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Number of interrupt registers = %d\n", + __func__, rmi4_data->num_of_intr_regs); + + memset(rmi4_data->intr_mask, 0x00, sizeof(rmi4_data->intr_mask)); + + /* + * Map out the interrupt bit masks for the interrupt sources + * from the registered function handlers. + */ + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) + data_sources += fhandler->num_of_data_sources; + } + if (data_sources) { + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, + &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + rmi4_data->intr_mask[fhandler->intr_reg_num] |= + fhandler->intr_mask; + } + } + } + } + + /* Enable the interrupt sources */ + for (ii = 0; ii < rmi4_data->num_of_intr_regs; ii++) { + if (rmi4_data->intr_mask[ii] != 0x00) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Interrupt enable mask %d = 0x%02x\n", + __func__, ii, rmi4_data->intr_mask[ii]); + intr_addr = rmi4_data->f01_ctrl_base_addr + 1 + ii; + retval = synaptics_rmi4_i2c_write(rmi4_data, + intr_addr, + &(rmi4_data->intr_mask[ii]), + sizeof(rmi4_data->intr_mask[ii])); + if (retval < 0) + return retval; + } + } + + return 0; +} + +static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char command = 0x01; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + retval = synaptics_rmi4_i2c_write(rmi4_data, + rmi4_data->f01_cmd_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to issue reset command, error = %d\n", + __func__, retval); + return retval; + } + + msleep(100); + + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) + synaptics_rmi4_f1a_kfree(fhandler); + else + kfree(fhandler->data); + kfree(fhandler); + } + } + + retval = synaptics_rmi4_query_device(rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to query device\n", + __func__); + return retval; + } + + return 0; +} + +/** +* synaptics_rmi4_detection_work() +* +* Called by the kernel at the scheduled time. +* +* This function is a self-rearming work thread that checks for the +* insertion and removal of other expansion Function modules such as +* rmi_dev and calls their initialization and removal callback functions +* accordingly. +*/ +static void synaptics_rmi4_detection_work(struct work_struct *work) +{ + struct synaptics_rmi4_exp_fn *exp_fhandler, *next_list_entry; + struct synaptics_rmi4_data *rmi4_data = + container_of(work, struct synaptics_rmi4_data, + det_work.work); + + queue_delayed_work(rmi4_data->det_workqueue, + &rmi4_data->det_work, + msecs_to_jiffies(EXP_FN_DET_INTERVAL)); + + mutex_lock(&exp_fn_list_mutex); + if (!list_empty(&exp_fn_list)) { + list_for_each_entry_safe(exp_fhandler, + next_list_entry, + &exp_fn_list, + link) { + if ((exp_fhandler->func_init != NULL) && + (exp_fhandler->inserted == false)) { + exp_fhandler->func_init(rmi4_data); + exp_fhandler->inserted = true; + } else if ((exp_fhandler->func_init == NULL) && + (exp_fhandler->inserted == true)) { + exp_fhandler->func_remove(rmi4_data); + list_del(&exp_fhandler->link); + kfree(exp_fhandler); + } + } + } + mutex_unlock(&exp_fn_list_mutex); + + return; +} + +/** +* synaptics_rmi4_new_function() +* +* Called by other expansion Function modules in their module init and +* module exit functions. +* +* This function is used by other expansion Function modules such as +* rmi_dev to register themselves with the driver by providing their +* initialization and removal callback function pointers so that they +* can be inserted or removed dynamically at module init and exit times, +* respectively. +*/ +void synaptics_rmi4_new_function(enum exp_fn fn_type, bool insert, + int (*func_init)(struct synaptics_rmi4_data *rmi4_data), + void (*func_remove)(struct synaptics_rmi4_data *rmi4_data), + void (*func_attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask)) +{ + struct synaptics_rmi4_exp_fn *exp_fhandler; + + if (!exp_fn_inited) { + mutex_init(&exp_fn_list_mutex); + INIT_LIST_HEAD(&exp_fn_list); + exp_fn_inited = 1; + } + + mutex_lock(&exp_fn_list_mutex); + if (insert) { + exp_fhandler = kzalloc(sizeof(*exp_fhandler), GFP_KERNEL); + if (!exp_fhandler) { + pr_err("%s: Failed to alloc mem for expansion function\n", + __func__); + goto exit; + } + exp_fhandler->fn_type = fn_type; + exp_fhandler->func_init = func_init; + exp_fhandler->func_attn = func_attn; + exp_fhandler->func_remove = func_remove; + exp_fhandler->inserted = false; + list_add_tail(&exp_fhandler->link, &exp_fn_list); + } else { + if (!list_empty(&exp_fn_list)) { + list_for_each_entry(exp_fhandler, &exp_fn_list, link) { + if (exp_fhandler->func_init == func_init) { + exp_fhandler->inserted = false; + exp_fhandler->func_init = NULL; + exp_fhandler->func_attn = NULL; + goto exit; + } + } + } + } + +exit: + mutex_unlock(&exp_fn_list_mutex); + + return; +} +EXPORT_SYMBOL(synaptics_rmi4_new_function); + + /** + * synaptics_rmi4_probe() + * + * Called by the kernel when an association with an I2C device of the + * same name is made (after doing i2c_add_driver). + * + * This funtion allocates and initializes the resources for the driver + * as an input driver, turns on the power to the sensor, queries the + * sensor for its supported Functions and characteristics, registers + * the driver to the input subsystem, sets up the interrupt, handles + * the registration of the early_suspend and late_resume functions, + * and creates a work queue for detection of other expansion Function + * modules. + */ +static int __devinit synaptics_rmi4_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int retval; + unsigned char ii; + unsigned char attr_count; + struct synaptics_rmi4_f1a_handle *f1a; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_data *rmi4_data; + struct synaptics_rmi4_device_info *rmi; + const struct synaptics_rmi4_platform_data *platform_data = + client->dev.platform_data; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, + "%s: SMBus byte data not supported\n", + __func__); + return -EIO; + } + + if (!platform_data) { + dev_err(&client->dev, + "%s: No platform data found\n", + __func__); + return -EINVAL; + } + + rmi4_data = kzalloc(sizeof(*rmi4_data) * 2, GFP_KERNEL); + if (!rmi4_data) { + dev_err(&client->dev, + "%s: Failed to alloc mem for rmi4_data\n", + __func__); + return -ENOMEM; + } + + rmi = &(rmi4_data->rmi4_mod_info); + + rmi4_data->input_dev = input_allocate_device(); + if (rmi4_data->input_dev == NULL) { + dev_err(&client->dev, + "%s: Failed to allocate input device\n", + __func__); + retval = -ENOMEM; + goto err_input_device; + } + + if (platform_data->regulator_en) { + rmi4_data->regulator = regulator_get(&client->dev, "vdd"); + if (IS_ERR(rmi4_data->regulator)) { + dev_err(&client->dev, + "%s: Failed to get regulator\n", + __func__); + retval = PTR_ERR(rmi4_data->regulator); + goto err_regulator; + } + regulator_enable(rmi4_data->regulator); + } + + rmi4_data->i2c_client = client; + rmi4_data->current_page = MASK_8BIT; + rmi4_data->board = platform_data; + rmi4_data->touch_stopped = false; + rmi4_data->sensor_sleep = false; + rmi4_data->irq_enabled = false; + + rmi4_data->i2c_read = synaptics_rmi4_i2c_read; + rmi4_data->i2c_write = synaptics_rmi4_i2c_write; + rmi4_data->irq_enable = synaptics_rmi4_irq_enable; + rmi4_data->reset_device = synaptics_rmi4_reset_device; + + init_waitqueue_head(&rmi4_data->wait); + mutex_init(&(rmi4_data->rmi4_io_ctrl_mutex)); + + retval = synaptics_rmi4_query_device(rmi4_data); + if (retval < 0) { + dev_err(&client->dev, + "%s: Failed to query device\n", + __func__); + goto err_query_device; + } + + i2c_set_clientdata(client, rmi4_data); + + rmi4_data->input_dev->name = DRIVER_NAME; + rmi4_data->input_dev->phys = INPUT_PHYS_NAME; + rmi4_data->input_dev->id.bustype = BUS_I2C; + rmi4_data->input_dev->id.product = SYNAPTICS_RMI4_DRIVER_PRODUCT; + rmi4_data->input_dev->id.version = SYNAPTICS_RMI4_DRIVER_VERSION; + rmi4_data->input_dev->dev.parent = &client->dev; + input_set_drvdata(rmi4_data->input_dev, rmi4_data); + + set_bit(EV_SYN, rmi4_data->input_dev->evbit); + set_bit(EV_KEY, rmi4_data->input_dev->evbit); + set_bit(EV_ABS, rmi4_data->input_dev->evbit); + set_bit(BTN_TOUCH, rmi4_data->input_dev->keybit); + set_bit(BTN_TOOL_FINGER, rmi4_data->input_dev->keybit); + +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit); +#endif + + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_X, 0, + rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_Y, 0, + rmi4_data->sensor_max_y, 0, 0); +#ifdef REPORT_2D_W + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, 0, + MAX_ABS_MT_TOUCH_MAJOR, 0, 0); +#endif + +#ifdef TYPE_B_PROTOCOL + input_mt_init_slots(rmi4_data->input_dev, + rmi4_data->num_of_fingers); +#endif + + f1a = NULL; + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) + f1a = fhandler->data; + } + } + + if (f1a) { + for (ii = 0; ii < f1a->valid_button_count; ii++) { + set_bit(f1a->button_map[ii], + rmi4_data->input_dev->keybit); + input_set_capability(rmi4_data->input_dev, + EV_KEY, f1a->button_map[ii]); + } + } + + retval = input_register_device(rmi4_data->input_dev); + if (retval) { + dev_err(&client->dev, + "%s: Failed to register input device\n", + __func__); + goto err_register_input; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + rmi4_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + rmi4_data->early_suspend.suspend = synaptics_rmi4_early_suspend; + rmi4_data->early_suspend.resume = synaptics_rmi4_late_resume; + register_early_suspend(&rmi4_data->early_suspend); +#endif + + if (!exp_fn_inited) { + mutex_init(&exp_fn_list_mutex); + INIT_LIST_HEAD(&exp_fn_list); + exp_fn_inited = 1; + } + + rmi4_data->det_workqueue = + create_singlethread_workqueue("rmi_det_workqueue"); + INIT_DELAYED_WORK(&rmi4_data->det_work, + synaptics_rmi4_detection_work); + queue_delayed_work(rmi4_data->det_workqueue, + &rmi4_data->det_work, + msecs_to_jiffies(EXP_FN_DET_INTERVAL)); + + if (platform_data->gpio_config) { + retval = platform_data->gpio_config(platform_data->irq_gpio, + true); + if (retval < 0) { + dev_err(&client->dev, + "%s: Failed to configure GPIO\n", + __func__); + goto err_gpio; + } + } + + rmi4_data->irq = gpio_to_irq(platform_data->irq_gpio); + + retval = synaptics_rmi4_irq_enable(rmi4_data, true); + if (retval < 0) { + dev_err(&client->dev, + "%s: Failed to enable attention interrupt\n", + __func__); + goto err_enable_irq; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(&client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto err_sysfs; + } + } + + return retval; + +err_sysfs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + +err_enable_irq: +err_gpio: + input_unregister_device(rmi4_data->input_dev); + +err_register_input: +err_query_device: + if (platform_data->regulator_en) { + regulator_disable(rmi4_data->regulator); + regulator_put(rmi4_data->regulator); + } + + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) + synaptics_rmi4_f1a_kfree(fhandler); + else + kfree(fhandler->data); + kfree(fhandler); + } + } + +err_regulator: + input_free_device(rmi4_data->input_dev); + rmi4_data->input_dev = NULL; + +err_input_device: + kfree(rmi4_data); + + return retval; +} + + /** + * synaptics_rmi4_remove() + * + * Called by the kernel when the association with an I2C device of the + * same name is broken (when the driver is unloaded). + * + * This funtion terminates the work queue, stops sensor data acquisition, + * frees the interrupt, unregisters the driver from the input subsystem, + * turns off the power to the sensor, and frees other allocated resources. + */ +static int __devexit synaptics_rmi4_remove(struct i2c_client *client) +{ + unsigned char attr_count; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_data *rmi4_data = i2c_get_clientdata(client); + struct synaptics_rmi4_device_info *rmi; + const struct synaptics_rmi4_platform_data *platform_data = + rmi4_data->board; + + rmi = &(rmi4_data->rmi4_mod_info); + + cancel_delayed_work_sync(&rmi4_data->det_work); + flush_workqueue(rmi4_data->det_workqueue); + destroy_workqueue(rmi4_data->det_workqueue); + + rmi4_data->touch_stopped = true; + wake_up(&rmi4_data->wait); + + synaptics_rmi4_irq_enable(rmi4_data, false); + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + + input_unregister_device(rmi4_data->input_dev); + + if (platform_data->regulator_en) { + regulator_disable(rmi4_data->regulator); + regulator_put(rmi4_data->regulator); + } + + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) + synaptics_rmi4_f1a_kfree(fhandler); + else + kfree(fhandler->data); + kfree(fhandler); + } + } + input_free_device(rmi4_data->input_dev); + + kfree(rmi4_data); + + return 0; +} + +#ifdef CONFIG_PM + /** + * synaptics_rmi4_sensor_sleep() + * + * Called by synaptics_rmi4_early_suspend() and synaptics_rmi4_suspend(). + * + * This function stops finger data acquisition and puts the sensor to sleep. + */ +static void synaptics_rmi4_sensor_sleep(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char device_ctrl; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(&(rmi4_data->input_dev->dev), + "%s: Failed to enter sleep mode\n", + __func__); + rmi4_data->sensor_sleep = false; + return; + } + + device_ctrl = (device_ctrl & ~MASK_3BIT); + device_ctrl = (device_ctrl | NO_SLEEP_OFF | SENSOR_SLEEP); + + retval = synaptics_rmi4_i2c_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(&(rmi4_data->input_dev->dev), + "%s: Failed to enter sleep mode\n", + __func__); + rmi4_data->sensor_sleep = false; + return; + } else { + rmi4_data->sensor_sleep = true; + } + + return; +} + + /** + * synaptics_rmi4_sensor_wake() + * + * Called by synaptics_rmi4_resume() and synaptics_rmi4_late_resume(). + * + * This function wakes the sensor from sleep. + */ +static void synaptics_rmi4_sensor_wake(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char device_ctrl; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(&(rmi4_data->input_dev->dev), + "%s: Failed to wake from sleep mode\n", + __func__); + rmi4_data->sensor_sleep = true; + return; + } + + device_ctrl = (device_ctrl & ~MASK_3BIT); + device_ctrl = (device_ctrl | NO_SLEEP_OFF | NORMAL_OPERATION); + + retval = synaptics_rmi4_i2c_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(&(rmi4_data->input_dev->dev), + "%s: Failed to wake from sleep mode\n", + __func__); + rmi4_data->sensor_sleep = true; + return; + } else { + rmi4_data->sensor_sleep = false; + } + + return; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND + /** + * synaptics_rmi4_early_suspend() + * + * Called by the kernel during the early suspend phase when the system + * enters suspend. + * + * This function calls synaptics_rmi4_sensor_sleep() to stop finger + * data acquisition and put the sensor to sleep. + */ +static void synaptics_rmi4_early_suspend(struct early_suspend *h) +{ + struct synaptics_rmi4_data *rmi4_data = + container_of(h, struct synaptics_rmi4_data, + early_suspend); + + rmi4_data->touch_stopped = true; + wake_up(&rmi4_data->wait); + synaptics_rmi4_irq_enable(rmi4_data, false); + synaptics_rmi4_sensor_sleep(rmi4_data); + + if (rmi4_data->full_pm_cycle) + synaptics_rmi4_suspend(&(rmi4_data->input_dev->dev)); + + return; +} + + /** + * synaptics_rmi4_late_resume() + * + * Called by the kernel during the late resume phase when the system + * wakes up from suspend. + * + * This function goes through the sensor wake process if the system wakes + * up from early suspend (without going into suspend). + */ +static void synaptics_rmi4_late_resume(struct early_suspend *h) +{ + struct synaptics_rmi4_data *rmi4_data = + container_of(h, struct synaptics_rmi4_data, + early_suspend); + + if (rmi4_data->full_pm_cycle) + synaptics_rmi4_resume(&(rmi4_data->input_dev->dev)); + + if (rmi4_data->sensor_sleep == true) { + synaptics_rmi4_sensor_wake(rmi4_data); + rmi4_data->touch_stopped = false; + synaptics_rmi4_irq_enable(rmi4_data, true); + } + + return; +} +#endif + + /** + * synaptics_rmi4_suspend() + * + * Called by the kernel during the suspend phase when the system + * enters suspend. + * + * This function stops finger data acquisition and puts the sensor to + * sleep (if not already done so during the early suspend phase), + * disables the interrupt, and turns off the power to the sensor. + */ +static int synaptics_rmi4_suspend(struct device *dev) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + const struct synaptics_rmi4_platform_data *platform_data = + rmi4_data->board; + + if (!rmi4_data->sensor_sleep) { + rmi4_data->touch_stopped = true; + wake_up(&rmi4_data->wait); + synaptics_rmi4_irq_enable(rmi4_data, false); + synaptics_rmi4_sensor_sleep(rmi4_data); + } + + if (platform_data->regulator_en) + regulator_disable(rmi4_data->regulator); + + return 0; +} + + /** + * synaptics_rmi4_resume() + * + * Called by the kernel during the resume phase when the system + * wakes up from suspend. + * + * This function turns on the power to the sensor, wakes the sensor + * from sleep, enables the interrupt, and starts finger data + * acquisition. + */ +static int synaptics_rmi4_resume(struct device *dev) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + const struct synaptics_rmi4_platform_data *platform_data = + rmi4_data->board; + + if (platform_data->regulator_en) + regulator_enable(rmi4_data->regulator); + + synaptics_rmi4_sensor_wake(rmi4_data); + rmi4_data->touch_stopped = false; + synaptics_rmi4_irq_enable(rmi4_data, true); + + return 0; +} + +static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = { + .suspend = synaptics_rmi4_suspend, + .resume = synaptics_rmi4_resume, +}; +#endif + +static const struct i2c_device_id synaptics_rmi4_id_table[] = { + {DRIVER_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table); + +static struct i2c_driver synaptics_rmi4_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &synaptics_rmi4_dev_pm_ops, +#endif + }, + .probe = synaptics_rmi4_probe, + .remove = __devexit_p(synaptics_rmi4_remove), + .id_table = synaptics_rmi4_id_table, +}; + + /** + * synaptics_rmi4_init() + * + * Called by the kernel during do_initcalls (if built-in) + * or when the driver is loaded (if a module). + * + * This function registers the driver to the I2C subsystem. + * + */ +static int __init synaptics_rmi4_init(void) +{ + return i2c_add_driver(&synaptics_rmi4_driver); +} + + /** + * synaptics_rmi4_exit() + * + * Called by the kernel when the driver is unloaded. + * + * This funtion unregisters the driver from the I2C subsystem. + * + */ +static void __exit synaptics_rmi4_exit(void) +{ + i2c_del_driver(&synaptics_rmi4_driver); +} + +module_init(synaptics_rmi4_init); +module_exit(synaptics_rmi4_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics RMI4 I2C Touch Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(SYNAPTICS_RMI4_DRIVER_VERSION); diff --git a/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h new file mode 100644 index 000000000000..ecb9b9415e8a --- /dev/null +++ b/kernel/drivers/input/touchscreen/synaptics_i2c_rmi4.h @@ -0,0 +1,286 @@ +/* + * Synaptics RMI4 touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com> + * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _SYNAPTICS_DSX_RMI4_H_ +#define _SYNAPTICS_DSX_RMI4_H_ + +#define SYNAPTICS_RMI4_DS4 0x0001 +#define SYNAPTICS_RMI4_DS5 0x0002 +#define SYNAPTICS_RMI4_DRIVER_PRODUCT SYNAPTICS_RMI4_DS4 +#define SYNAPTICS_RMI4_DRIVER_VERSION 0x1001 + +#include <linux/version.h> +#ifdef CONFIG_HAS_EARLYSUSPEND +#include <linux/earlysuspend.h> +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)) +#define KERNEL_ABOVE_2_6_38 +#endif + +#ifdef KERNEL_ABOVE_2_6_38 +#define sstrtoul(...) kstrtoul(__VA_ARGS__) +#else +#define sstrtoul(...) strict_strtoul(__VA_ARGS__) +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0)) +#define KERNEL_ABOVE_3_7 +#endif + +#define PDT_PROPS (0x00EF) +#define PDT_START (0x00E9) +#define PDT_END (0x000A) +#define PDT_ENTRY_SIZE (0x0006) +#define PAGES_TO_SERVICE (10) +#define PAGE_SELECT_LEN (2) + +#define SYNAPTICS_RMI4_F01 (0x01) +#define SYNAPTICS_RMI4_F11 (0x11) +#define SYNAPTICS_RMI4_F1A (0x1a) +#define SYNAPTICS_RMI4_F34 (0x34) +#define SYNAPTICS_RMI4_F54 (0x54) +#define SYNAPTICS_RMI4_F55 (0x55) + +#define SYNAPTICS_RMI4_PRODUCT_INFO_SIZE 2 +#define SYNAPTICS_RMI4_DATE_CODE_SIZE 3 +#define SYNAPTICS_RMI4_PRODUCT_ID_SIZE 10 +#define SYNAPTICS_RMI4_BUILD_ID_SIZE 3 + +#define MAX_NUMBER_OF_FINGERS 10 +#define MAX_NUMBER_OF_BUTTONS 4 +#define MAX_INTR_REGISTERS 4 + +#define MASK_16BIT 0xFFFF +#define MASK_8BIT 0xFF +#define MASK_7BIT 0x7F +#define MASK_6BIT 0x3F +#define MASK_5BIT 0x1F +#define MASK_4BIT 0x0F +#define MASK_3BIT 0x07 +#define MASK_2BIT 0x03 +#define MASK_1BIT 0x01 + +/* + * struct synaptics_rmi4_fn_desc - function descriptor fields in PDT + * @query_base_addr: base address for query registers + * @cmd_base_addr: base address for command registers + * @ctrl_base_addr: base address for control registers + * @data_base_addr: base address for data registers + * @intr_src_count: number of interrupt sources + * @fn_number: function number + */ +struct synaptics_rmi4_fn_desc { + unsigned char query_base_addr; + unsigned char cmd_base_addr; + unsigned char ctrl_base_addr; + unsigned char data_base_addr; + unsigned char intr_src_count; + unsigned char fn_number; +}; + +/* + * synaptics_rmi4_fn_full_addr - full 16-bit base addresses + * @query_base: 16-bit base address for query registers + * @cmd_base: 16-bit base address for data registers + * @ctrl_base: 16-bit base address for command registers + * @data_base: 16-bit base address for control registers + */ +struct synaptics_rmi4_fn_full_addr { + unsigned short query_base; + unsigned short cmd_base; + unsigned short ctrl_base; + unsigned short data_base; +}; + +/* + * struct synaptics_rmi4_fn - function handler data structure + * @fn_number: function number + * @num_of_data_sources: number of data sources + * @num_of_data_points: maximum number of fingers supported + * @size_of_data_register_block: data register block size + * @data1_offset: offset to data1 register from data base address + * @intr_reg_num: index to associated interrupt register + * @intr_mask: interrupt mask + * @full_addr: full 16-bit base addresses of function registers + * @link: linked list for function handlers + * @data_size: size of private data + * @data: pointer to private data + */ +struct synaptics_rmi4_fn { + unsigned char fn_number; + unsigned char num_of_data_sources; + unsigned char num_of_data_points; + unsigned char size_of_data_register_block; + unsigned char data1_offset; + unsigned char intr_reg_num; + unsigned char intr_mask; + struct synaptics_rmi4_fn_full_addr full_addr; + struct list_head link; + int data_size; + void *data; +}; + +/* + * struct synaptics_rmi4_device_info - device information + * @version_major: rmi protocol major version number + * @version_minor: rmi protocol minor version number + * @manufacturer_id: manufacturer id + * @product_props: product properties information + * @product_info: product info array + * @date_code: device manufacture date + * @tester_id: tester id array + * @serial_number: device serial number + * @product_id_string: device product id + * @support_fn_list: linked list for function handlers + */ +struct synaptics_rmi4_device_info { + unsigned int version_major; + unsigned int version_minor; + unsigned char manufacturer_id; + unsigned char product_props; + unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE]; + unsigned char date_code[SYNAPTICS_RMI4_DATE_CODE_SIZE]; + unsigned short tester_id; + unsigned short serial_number; + unsigned char product_id_string[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1]; + unsigned char build_id[SYNAPTICS_RMI4_BUILD_ID_SIZE]; + unsigned char config_id[3]; + struct list_head support_fn_list; +}; + +/* + * struct synaptics_rmi4_data - rmi4 device instance data + * @i2c_client: pointer to associated i2c client + * @input_dev: pointer to associated input device + * @board: constant pointer to platform data + * @rmi4_mod_info: device information + * @regulator: pointer to associated regulator + * @rmi4_io_ctrl_mutex: mutex for i2c i/o control + * @det_work: work thread instance for expansion function detection + * @det_workqueue: pointer to work queue for work thread instance + * @early_suspend: instance to support early suspend power management + * @current_page: current page in sensor to acess + * @button_0d_enabled: flag for 0d button support + * @full_pm_cycle: flag for full power management cycle in early suspend stage + * @num_of_intr_regs: number of interrupt registers + * @f01_query_base_addr: query base address for f01 + * @f01_cmd_base_addr: command base address for f01 + * @f01_ctrl_base_addr: control base address for f01 + * @f01_data_base_addr: data base address for f01 + * @irq: attention interrupt + * @sensor_max_x: sensor maximum x value + * @sensor_max_y: sensor maximum y value + * @irq_enabled: flag for indicating interrupt enable status + * @touch_stopped: flag to stop interrupt thread processing + * @fingers_on_2d: flag to indicate presence of fingers in 2d area + * @sensor_sleep: flag to indicate sleep state of sensor + * @wait: wait queue for touch data polling in interrupt thread + * @i2c_read: pointer to i2c read function + * @i2c_write: pointer to i2c write function + * @irq_enable: pointer to irq enable function + */ +struct synaptics_rmi4_data { + struct i2c_client *i2c_client; + struct input_dev *input_dev; + const struct synaptics_rmi4_platform_data *board; + struct synaptics_rmi4_device_info rmi4_mod_info; + struct regulator *regulator; + struct mutex rmi4_io_ctrl_mutex; + struct delayed_work det_work; + struct workqueue_struct *det_workqueue; + struct early_suspend early_suspend; + unsigned char current_page; + unsigned char button_0d_enabled; + unsigned char full_pm_cycle; + unsigned char num_of_rx; + unsigned char num_of_tx; + unsigned char num_of_fingers; + unsigned char intr_mask[MAX_INTR_REGISTERS]; + unsigned short num_of_intr_regs; + unsigned short f01_query_base_addr; + unsigned short f01_cmd_base_addr; + unsigned short f01_ctrl_base_addr; + unsigned short f01_data_base_addr; + int irq; + int sensor_max_x; + int sensor_max_y; + bool irq_enabled; + bool touch_stopped; + bool fingers_on_2d; + bool sensor_sleep; + wait_queue_head_t wait; + int (*i2c_read)(struct synaptics_rmi4_data *pdata, unsigned short addr, + unsigned char *data, unsigned short length); + int (*i2c_write)(struct synaptics_rmi4_data *pdata, unsigned short addr, + unsigned char *data, unsigned short length); + int (*irq_enable)(struct synaptics_rmi4_data *rmi4_data, bool enable); + int (*reset_device)(struct synaptics_rmi4_data *rmi4_data); +}; + +enum exp_fn { + RMI_DEV = 0, + RMI_F34, + RMI_F54, + RMI_FW_UPDATER, + RMI_LAST, +}; + +struct synaptics_rmi4_exp_fn_ptr { + int (*read)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr, + unsigned char *data, unsigned short length); + int (*write)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr, + unsigned char *data, unsigned short length); + int (*enable)(struct synaptics_rmi4_data *rmi4_data, bool enable); +}; + +void synaptics_rmi4_new_function(enum exp_fn fn_type, bool insert, + int (*func_init)(struct synaptics_rmi4_data *rmi4_data), + void (*func_remove)(struct synaptics_rmi4_data *rmi4_data), + void (*func_attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask)); + +static inline ssize_t synaptics_rmi4_show_error(struct device *dev, + struct device_attribute *attr, char *buf) +{ + dev_warn(dev, "%s Attempted to read from write-only attribute %s\n", + __func__, attr->attr.name); + return -EPERM; +} + +static inline ssize_t synaptics_rmi4_store_error(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + dev_warn(dev, "%s Attempted to write to read-only attribute %s\n", + __func__, attr->attr.name); + return -EPERM; +} + +static inline void batohs(unsigned short *dest, unsigned char *src) +{ + *dest = src[1] * 0x100 + src[0]; +} + +static inline void hstoba(unsigned char *dest, unsigned short src) +{ + dest[0] = src % 0x100; + dest[1] = src / 0x100; +} + +#endif diff --git a/kernel/drivers/input/touchscreen/synaptics_rmi_dev.c b/kernel/drivers/input/touchscreen/synaptics_rmi_dev.c new file mode 100644 index 000000000000..75857802c97a --- /dev/null +++ b/kernel/drivers/input/touchscreen/synaptics_rmi_dev.c @@ -0,0 +1,710 @@ +/* + * Synaptics RMI4 touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com> + * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/gpio.h> +#include <linux/uaccess.h> +#include <linux/cdev.h> +#include <linux/input/synaptics_dsx.h> +#include "synaptics_i2c_rmi4.h" + +#define CHAR_DEVICE_NAME "rmi" +#define DEVICE_CLASS_NAME "rmidev" +#define DEV_NUMBER 1 +#define REG_ADDR_LIMIT 0xFFFF + +static ssize_t rmidev_sysfs_open_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_release_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_address_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_length_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_data_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmidev_sysfs_data_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +struct rmidev_handle { + dev_t dev_no; + unsigned short address; + unsigned int length; + struct device dev; + struct synaptics_rmi4_data *rmi4_data; + struct synaptics_rmi4_exp_fn_ptr *fn_ptr; + struct kobject *sysfs_dir; + void *data; +}; + +struct rmidev_data { + int ref_count; + struct cdev main_dev; + struct class *device_class; + struct mutex file_mutex; + struct rmidev_handle *rmi_dev; +}; + +static struct device_attribute attrs[] = { + __ATTR(open, S_IWUGO, + synaptics_rmi4_show_error, + rmidev_sysfs_open_store), + __ATTR(release, S_IWUGO, + synaptics_rmi4_show_error, + rmidev_sysfs_release_store), + __ATTR(address, S_IWUGO, + synaptics_rmi4_show_error, + rmidev_sysfs_address_store), + __ATTR(length, S_IWUGO, + synaptics_rmi4_show_error, + rmidev_sysfs_length_store), + __ATTR(data, (S_IRUGO | S_IWUGO), + rmidev_sysfs_data_show, + rmidev_sysfs_data_store), +}; + +static int rmidev_major_num; + +static struct class *rmidev_device_class; + +static struct rmidev_handle *rmidev; + +static struct completion remove_complete; + +static ssize_t rmidev_sysfs_open_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + rmidev->fn_ptr->enable(rmidev->rmi4_data, false); + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: Attention interrupt disabled\n", + __func__); + + return count; +} + +static ssize_t rmidev_sysfs_release_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + rmidev->fn_ptr->enable(rmidev->rmi4_data, true); + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: Attention interrupt enabled\n", + __func__); + + return count; +} + +static ssize_t rmidev_sysfs_address_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input > REG_ADDR_LIMIT) + return -EINVAL; + + rmidev->address = (unsigned short)input; + + return count; +} + +static ssize_t rmidev_sysfs_length_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input > REG_ADDR_LIMIT) + return -EINVAL; + + rmidev->length = input; + + return count; +} + +static ssize_t rmidev_sysfs_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned int data_length = rmidev->length; + + if (data_length > (REG_ADDR_LIMIT - rmidev->address)) + data_length = REG_ADDR_LIMIT - rmidev->address; + + if (data_length) { + retval = rmidev->fn_ptr->read(rmidev->rmi4_data, + rmidev->address, + (unsigned char *)buf, + data_length); + if (retval < 0) { + dev_err(&rmidev->rmi4_data->i2c_client->dev, + "%s: Failed to read data\n", + __func__); + return retval; + } + } else { + return -EINVAL; + } + + return data_length; +} + +static ssize_t rmidev_sysfs_data_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int data_length = rmidev->length; + + if (data_length > (REG_ADDR_LIMIT - rmidev->address)) + data_length = REG_ADDR_LIMIT - rmidev->address; + + if (data_length) { + retval = rmidev->fn_ptr->write(rmidev->rmi4_data, + rmidev->address, + (unsigned char *)buf, + data_length); + if (retval < 0) { + dev_err(&rmidev->rmi4_data->i2c_client->dev, + "%s: Failed to write data\n", + __func__); + return retval; + } + } else { + return -EINVAL; + } + + return data_length; +} + +/* + * rmidev_llseek - used to set up register address + * + * @filp: file structure for seek + * @off: offset + * if whence == SEEK_SET, + * high 16 bits: page address + * low 16 bits: register address + * if whence == SEEK_CUR, + * offset from current position + * if whence == SEEK_END, + * offset from end position (0xFFFF) + * @whence: SEEK_SET, SEEK_CUR, or SEEK_END + */ +static loff_t rmidev_llseek(struct file *filp, loff_t off, int whence) +{ + loff_t newpos; + struct rmidev_data *dev_data = filp->private_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + mutex_lock(&(dev_data->file_mutex)); + + switch (whence) { + case SEEK_SET: + newpos = off; + break; + case SEEK_CUR: + newpos = filp->f_pos + off; + break; + case SEEK_END: + newpos = REG_ADDR_LIMIT + off; + break; + default: + newpos = -EINVAL; + goto clean_up; + } + + if (newpos < 0 || newpos > REG_ADDR_LIMIT) { + dev_err(&rmidev->rmi4_data->i2c_client->dev, + "%s: New position 0x%04x is invalid\n", + __func__, (unsigned int)newpos); + newpos = -EINVAL; + goto clean_up; + } + + filp->f_pos = newpos; + +clean_up: + mutex_unlock(&(dev_data->file_mutex)); + + return newpos; +} + +/* + * rmidev_read: - use to read data from rmi device + * + * @filp: file structure for read + * @buf: user space buffer pointer + * @count: number of bytes to read + * @f_pos: offset (starting register address) + */ +static ssize_t rmidev_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + ssize_t retval; + unsigned char tmpbuf[count + 1]; + struct rmidev_data *dev_data = filp->private_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + if (count == 0) + return 0; + + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + + mutex_lock(&(dev_data->file_mutex)); + + retval = rmidev->fn_ptr->read(rmidev->rmi4_data, + *f_pos, + tmpbuf, + count); + if (retval < 0) + goto clean_up; + + if (copy_to_user(buf, tmpbuf, count)) + retval = -EFAULT; + else + *f_pos += retval; + +clean_up: + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +/* + * rmidev_write: - used to write data to rmi device + * + * @filep: file structure for write + * @buf: user space buffer pointer + * @count: number of bytes to write + * @f_pos: offset (starting register address) + */ +static ssize_t rmidev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + ssize_t retval; + unsigned char tmpbuf[count + 1]; + struct rmidev_data *dev_data = filp->private_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + if (count == 0) + return 0; + + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + + if (copy_from_user(tmpbuf, buf, count)) + return -EFAULT; + + mutex_lock(&(dev_data->file_mutex)); + + retval = rmidev->fn_ptr->write(rmidev->rmi4_data, + *f_pos, + tmpbuf, + count); + if (retval >= 0) + *f_pos += retval; + + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +/* + * rmidev_open: enable access to rmi device + * @inp: inode struture + * @filp: file structure + */ +static int rmidev_open(struct inode *inp, struct file *filp) +{ + int retval = 0; + struct rmidev_data *dev_data = + container_of(inp->i_cdev, struct rmidev_data, main_dev); + + if (!dev_data) + return -EACCES; + + filp->private_data = dev_data; + + mutex_lock(&(dev_data->file_mutex)); + + rmidev->fn_ptr->enable(rmidev->rmi4_data, false); + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: Attention interrupt disabled\n", + __func__); + + if (dev_data->ref_count < 1) + dev_data->ref_count++; + else + retval = -EACCES; + + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +/* + * rmidev_release: - release access to rmi device + * @inp: inode structure + * @filp: file structure + */ +static int rmidev_release(struct inode *inp, struct file *filp) +{ + struct rmidev_data *dev_data = + container_of(inp->i_cdev, struct rmidev_data, main_dev); + + if (!dev_data) + return -EACCES; + + mutex_lock(&(dev_data->file_mutex)); + + dev_data->ref_count--; + if (dev_data->ref_count < 0) + dev_data->ref_count = 0; + + rmidev->fn_ptr->enable(rmidev->rmi4_data, true); + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: Attention interrupt enabled\n", + __func__); + + mutex_unlock(&(dev_data->file_mutex)); + + return 0; +} + +static const struct file_operations rmidev_fops = { + .owner = THIS_MODULE, + .llseek = rmidev_llseek, + .read = rmidev_read, + .write = rmidev_write, + .open = rmidev_open, + .release = rmidev_release, +}; + +static void rmidev_device_cleanup(struct rmidev_data *dev_data) +{ + dev_t devno; + + if (dev_data) { + devno = dev_data->main_dev.dev; + + if (dev_data->device_class) + device_destroy(dev_data->device_class, devno); + + cdev_del(&dev_data->main_dev); + + unregister_chrdev_region(devno, 1); + + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: rmidev device removed\n", + __func__); + } + + return; +} + +static char *rmi_char_devnode(struct device *dev, mode_t *mode) +{ + if (!mode) + return NULL; + + *mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + + return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev)); +} + +static int rmidev_create_device_class(void) +{ + rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME); + + if (IS_ERR(rmidev_device_class)) { + pr_err("%s: Failed to create /dev/%s\n", + __func__, CHAR_DEVICE_NAME); + return -ENODEV; + } + + rmidev_device_class->devnode = rmi_char_devnode; + + return 0; +} + +static int rmidev_init_device(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + dev_t dev_no; + unsigned char attr_count; + struct rmidev_data *dev_data; + struct device *device_ptr; + + rmidev = kzalloc(sizeof(*rmidev), GFP_KERNEL); + if (!rmidev) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for rmidev\n", + __func__); + retval = -ENOMEM; + goto err_rmidev; + } + + rmidev->fn_ptr = kzalloc(sizeof(*(rmidev->fn_ptr)), GFP_KERNEL); + if (!rmidev) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for fn_ptr\n", + __func__); + retval = -ENOMEM; + goto err_fn_ptr; + } + + rmidev->fn_ptr->read = rmi4_data->i2c_read; + rmidev->fn_ptr->write = rmi4_data->i2c_write; + rmidev->fn_ptr->enable = rmi4_data->irq_enable; + rmidev->rmi4_data = rmi4_data; + + retval = rmidev_create_device_class(); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create device class\n", + __func__); + goto err_device_class; + } + + if (rmidev_major_num) { + dev_no = MKDEV(rmidev_major_num, DEV_NUMBER); + retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME); + } else { + retval = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to allocate char device region\n", + __func__); + goto err_device_region; + } + + rmidev_major_num = MAJOR(dev_no); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Major number of rmidev = %d\n", + __func__, rmidev_major_num); + } + + dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL); + if (!dev_data) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for dev_data\n", + __func__); + retval = -ENOMEM; + goto err_dev_data; + } + + mutex_init(&dev_data->file_mutex); + dev_data->rmi_dev = rmidev; + rmidev->data = dev_data; + + cdev_init(&dev_data->main_dev, &rmidev_fops); + + retval = cdev_add(&dev_data->main_dev, dev_no, 1); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to add rmi char device\n", + __func__); + goto err_char_device; + } + + dev_set_name(&rmidev->dev, "rmidev%d", MINOR(dev_no)); + dev_data->device_class = rmidev_device_class; + + device_ptr = device_create(dev_data->device_class, NULL, dev_no, + NULL, CHAR_DEVICE_NAME"%d", MINOR(dev_no)); + if (IS_ERR(device_ptr)) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create rmi char device\n", + __func__); + retval = -ENODEV; + goto err_char_device; + } + + retval = gpio_export(rmi4_data->board->irq_gpio, false); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to export attention gpio\n", + __func__); + } else { + retval = gpio_export_link(&(rmi4_data->input_dev->dev), + "attn", rmi4_data->board->irq_gpio); + if (retval < 0) { + dev_err(&rmi4_data->input_dev->dev, + "%s Failed to create gpio symlink\n", + __func__); + } else { + dev_dbg(&rmi4_data->input_dev->dev, + "%s: Exported attention gpio %d\n", + __func__, rmi4_data->board->irq_gpio); + } + } + + rmidev->sysfs_dir = kobject_create_and_add("rmidev", + &rmi4_data->input_dev->dev.kobj); + if (!rmidev->sysfs_dir) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs directory\n", + __func__); + goto err_sysfs_dir; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(rmidev->sysfs_dir, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(&rmi4_data->input_dev->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto err_sysfs_attrs; + } + } + + return 0; + +err_sysfs_attrs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + + kobject_put(rmidev->sysfs_dir); + +err_sysfs_dir: +err_char_device: + rmidev_device_cleanup(dev_data); + kfree(dev_data); + +err_dev_data: + unregister_chrdev_region(dev_no, 1); + +err_device_region: + class_destroy(rmidev_device_class); + +err_device_class: + kfree(rmidev->fn_ptr); + +err_fn_ptr: + kfree(rmidev); + +err_rmidev: + return retval; +} + +static void rmidev_remove_device(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char attr_count; + struct rmidev_data *dev_data; + + if (!rmidev) + return; + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) + sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr); + + kobject_put(rmidev->sysfs_dir); + + dev_data = rmidev->data; + if (dev_data) { + rmidev_device_cleanup(dev_data); + kfree(dev_data); + } + + unregister_chrdev_region(rmidev->dev_no, 1); + + class_destroy(rmidev_device_class); + + kfree(rmidev->fn_ptr); + kfree(rmidev); + + complete(&remove_complete); + + return; +} + +static int __init rmidev_module_init(void) +{ + synaptics_rmi4_new_function(RMI_DEV, true, + rmidev_init_device, + rmidev_remove_device, + NULL); + return 0; +} + +static void __exit rmidev_module_exit(void) +{ + init_completion(&remove_complete); + synaptics_rmi4_new_function(RMI_DEV, false, + rmidev_init_device, + rmidev_remove_device, + NULL); + wait_for_completion(&remove_complete); + return; +} + +module_init(rmidev_module_init); +module_exit(rmidev_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("RMI4 RMI_Dev Module"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(SYNAPTICS_RMI4_DRIVER_VERSION); diff --git a/kernel/events/core.c b/kernel/events/core.c index 1cb22d44ccbb..96100cc046c5 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1584,14 +1584,14 @@ event_sched_out(struct perf_event *event, perf_pmu_disable(event->pmu); + event->tstamp_stopped = tstamp; + event->pmu->del(event, 0); + event->oncpu = -1; event->state = PERF_EVENT_STATE_INACTIVE; if (event->pending_disable) { event->pending_disable = 0; event->state = PERF_EVENT_STATE_OFF; } - event->tstamp_stopped = tstamp; - event->pmu->del(event, 0); - event->oncpu = -1; if (!is_software_event(event)) cpuctx->active_oncpu--; @@ -8032,6 +8032,9 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, } } + /* symmetric to unaccount_event() in _free_event() */ + account_event(event); + return event; err_per_task: @@ -8398,8 +8401,6 @@ SYSCALL_DEFINE5(perf_event_open, } } - account_event(event); - /* * Special case software events and allow them to be part of * any hardware group. @@ -8638,7 +8639,12 @@ err_context: perf_unpin_context(ctx); put_ctx(ctx); err_alloc: - free_event(event); + /* + * If event_file is set, the fput() above will have called ->release() + * and that will take care of freeing the event. + */ + if (!event_file) + free_event(event); err_cpus: put_online_cpus(); err_task: @@ -8682,8 +8688,6 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, /* Mark owner so we could distinguish it from user events. */ event->owner = EVENT_OWNER_KERNEL; - account_event(event); - ctx = find_get_context(event->pmu, task, event); if (IS_ERR(ctx)) { err = PTR_ERR(ctx); diff --git a/kernel/include/linux/input/synaptics_dsx.h b/kernel/include/linux/input/synaptics_dsx.h new file mode 100644 index 000000000000..b779e42a9bac --- /dev/null +++ b/kernel/include/linux/input/synaptics_dsx.h @@ -0,0 +1,59 @@ +/* + * Synaptics RMI4 touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com> + * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _SYNAPTICS_DSX_H_ +#define _SYNAPTICS_DSX_H_ + +/* + * struct synaptics_rmi4_capacitance_button_map - 0d button map + * @nbuttons: number of buttons + * @map: button map + */ +struct synaptics_rmi4_capacitance_button_map { + unsigned char nbuttons; + unsigned char *map; +}; + +/* + * struct synaptics_rmi4_platform_data - rmi4 platform data + * @x_flip: x flip flag + * @y_flip: y flip flag + * @regulator_en: regulator enable flag + * @irq_gpio: attention interrupt gpio + * @irq_flags: flags used by the irq + * @reset_gpio: reset gpio + * @panel_x: panel maximum values on the x + * @panel_y: panel maximum values on the y + * @gpio_config: pointer to gpio configuration function + * @capacitance_button_map: pointer to 0d button map + */ +struct synaptics_rmi4_platform_data { + bool x_flip; + bool y_flip; + bool regulator_en; + unsigned irq_gpio; + unsigned long irq_flags; + unsigned reset_gpio; + unsigned panel_x; + unsigned panel_y; + int (*gpio_config)(unsigned gpio, bool configure); + struct synaptics_rmi4_capacitance_button_map *capacitance_button_map; +}; + +#endif diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index b7342a24f559..b7dd5718836e 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -339,6 +339,7 @@ int hibernation_snapshot(int platform_mode) pm_message_t msg; int error; + pm_suspend_clear_flags(); error = platform_begin(platform_mode); if (error) goto Close; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index f640c0e3c7ea..58303b3dc356 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -8986,6 +8986,7 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu) set_window_start(rq); raw_spin_unlock_irqrestore(&rq->lock, flags); rq->calc_load_update = calc_load_update; + account_reset_rq(rq); break; case CPU_ONLINE: diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index 647f184f8aec..f29b132a9f8b 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -270,21 +270,21 @@ static __always_inline bool steal_account_process_tick(void) #ifdef CONFIG_PARAVIRT if (static_key_false(¶virt_steal_enabled)) { u64 steal; - cputime_t steal_ct; + unsigned long steal_jiffies; steal = paravirt_steal_clock(smp_processor_id()); steal -= this_rq()->prev_steal_time; /* - * cputime_t may be less precise than nsecs (eg: if it's - * based on jiffies). Lets cast the result to cputime + * steal is in nsecs but our caller is expecting steal + * time in jiffies. Lets cast the result to jiffies * granularity and account the rest on the next rounds. */ - steal_ct = nsecs_to_cputime(steal); - this_rq()->prev_steal_time += cputime_to_nsecs(steal_ct); + steal_jiffies = nsecs_to_jiffies(steal); + this_rq()->prev_steal_time += jiffies_to_nsecs(steal_jiffies); - account_steal_time(steal_ct); - return steal_ct; + account_steal_time(jiffies_to_cputime(steal_jiffies)); + return steal_jiffies; } #endif return false; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 45b76cfff1ec..b9566cf3ad37 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -2363,3 +2363,16 @@ static inline u64 irq_time_read(int cpu) } #endif /* CONFIG_64BIT */ #endif /* CONFIG_IRQ_TIME_ACCOUNTING */ + +static inline void account_reset_rq(struct rq *rq) +{ +#ifdef CONFIG_IRQ_TIME_ACCOUNTING + rq->prev_irq_time = 0; +#endif +#ifdef CONFIG_PARAVIRT + rq->prev_steal_time = 0; +#endif +#ifdef CONFIG_PARAVIRT_TIME_ACCOUNTING + rq->prev_steal_time_rq = 0; +#endif +} diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c index b65c78c15ab3..4a816bab38a2 100644 --- a/kernel/sysctl_binary.c +++ b/kernel/sysctl_binary.c @@ -1324,7 +1324,7 @@ static ssize_t binary_sysctl(const int *name, int nlen, } mnt = task_active_pid_ns(current)->proc_mnt; - file = file_open_root(mnt->mnt_root, mnt, pathname, flags); + file = file_open_root(mnt->mnt_root, mnt, pathname, flags, 0); result = PTR_ERR(file); if (IS_ERR(file)) goto out_putname; diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 530bbc44082c..ae68222c5a74 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -5044,7 +5044,10 @@ static ssize_t tracing_splice_read_pipe(struct file *filp, spd.nr_pages = i; - ret = splice_to_pipe(pipe, &spd); + if (i) + ret = splice_to_pipe(pipe, &spd); + else + ret = 0; out: splice_shrink_spd(&spd); return ret; diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c index e4e56589ec1d..be3222b7d72e 100644 --- a/kernel/trace/trace_irqsoff.c +++ b/kernel/trace/trace_irqsoff.c @@ -109,8 +109,12 @@ static int func_prolog_dec(struct trace_array *tr, return 0; local_save_flags(*flags); - /* slight chance to get a false positive on tracing_cpu */ - if (!irqs_disabled_flags(*flags)) + /* + * Slight chance to get a false positive on tracing_cpu, + * although I'm starting to think there isn't a chance. + * Leave this for now just to be paranoid. + */ + if (!irqs_disabled_flags(*flags) && !preempt_count()) return 0; *data = per_cpu_ptr(tr->trace_buffer.data, cpu); diff --git a/kernel/trace/trace_printk.c b/kernel/trace/trace_printk.c index 060df67dbdd1..f96f0383f6c6 100644 --- a/kernel/trace/trace_printk.c +++ b/kernel/trace/trace_printk.c @@ -296,6 +296,9 @@ static int t_show(struct seq_file *m, void *v) const char *str = *fmt; int i; + if (!*fmt) + return 0; + seq_printf(m, "0x%lx : \"", *(unsigned long *)fmt); /* diff --git a/kernel/watchdog.c b/kernel/watchdog.c index b40f7f35413d..9472691c1eb0 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -1038,6 +1038,9 @@ static int proc_watchdog_common(int which, struct ctl_table *table, int write, * both lockup detectors are disabled if proc_watchdog_update() * returns an error. */ + if (old == new) + goto out; + err = proc_watchdog_update(); } out: @@ -1082,7 +1085,7 @@ int proc_soft_watchdog(struct ctl_table *table, int write, int proc_watchdog_thresh(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { - int err, old; + int err, old, new; get_online_cpus(); mutex_lock(&watchdog_proc_mutex); @@ -1102,6 +1105,10 @@ int proc_watchdog_thresh(struct ctl_table *table, int write, /* * Update the sample period. Restore on failure. */ + new = ACCESS_ONCE(watchdog_thresh); + if (old == new) + goto out; + set_sample_period(); err = proc_watchdog_update(); if (err) { diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 5c714f33f494..7535ef32a75b 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1332,7 +1332,7 @@ static unsigned long mem_cgroup_get_limit(struct mem_cgroup *memcg) return limit; } -static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, +static bool mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, int order) { struct oom_control oc = { @@ -1410,6 +1410,7 @@ static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, } unlock: mutex_unlock(&oom_lock); + return chosen; } #if MAX_NUMNODES > 1 @@ -5130,6 +5131,7 @@ static ssize_t memory_high_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of)); + unsigned long nr_pages; unsigned long high; int err; @@ -5140,6 +5142,11 @@ static ssize_t memory_high_write(struct kernfs_open_file *of, memcg->high = high; + nr_pages = page_counter_read(&memcg->memory); + if (nr_pages > high) + try_to_free_mem_cgroup_pages(memcg, nr_pages - high, + GFP_KERNEL, true); + memcg_wb_domain_size_changed(memcg); return nbytes; } @@ -5161,6 +5168,8 @@ static ssize_t memory_max_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of)); + unsigned int nr_reclaims = MEM_CGROUP_RECLAIM_RETRIES; + bool drained = false; unsigned long max; int err; @@ -5169,9 +5178,36 @@ static ssize_t memory_max_write(struct kernfs_open_file *of, if (err) return err; - err = mem_cgroup_resize_limit(memcg, max); - if (err) - return err; + xchg(&memcg->memory.limit, max); + + for (;;) { + unsigned long nr_pages = page_counter_read(&memcg->memory); + + if (nr_pages <= max) + break; + + if (signal_pending(current)) { + err = -EINTR; + break; + } + + if (!drained) { + drain_all_stock(memcg); + drained = true; + continue; + } + + if (nr_reclaims) { + if (!try_to_free_mem_cgroup_pages(memcg, nr_pages - max, + GFP_KERNEL, true)) + nr_reclaims--; + continue; + } + + mem_cgroup_events(memcg, MEMCG_OOM, 1); + if (!mem_cgroup_out_of_memory(memcg, GFP_KERNEL, 0)) + break; + } memcg_wb_domain_size_changed(memcg); return nbytes; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index c7d6f92f041b..ffcb2b56f6c1 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -734,7 +734,7 @@ continue_merging: if (migratetype != buddy_mt && (is_migrate_isolate(migratetype) || - is_migrate_isolate(buddy_mt))) + is_migrate_isolate(buddy_mt))) goto done_merging; } max_order++; @@ -742,7 +742,6 @@ continue_merging: } done_merging: - set_page_order(page, order); /* diff --git a/mm/page_isolation.c b/mm/page_isolation.c index 4568fd58f70a..00c96462cc36 100644 --- a/mm/page_isolation.c +++ b/mm/page_isolation.c @@ -283,11 +283,11 @@ struct page *alloc_migrate_target(struct page *page, unsigned long private, * now as a simple work-around, we use the next node for destination. */ if (PageHuge(page)) { - nodemask_t src = nodemask_of_node(page_to_nid(page)); - nodemask_t dst; - nodes_complement(dst, src); + int node = next_online_node(page_to_nid(page)); + if (node == MAX_NUMNODES) + node = first_online_node; return alloc_huge_page_node(page_hstate(compound_head(page)), - next_node(page_to_nid(page), dst)); + node); } if (PageHighMem(page)) diff --git a/net/Kconfig b/net/Kconfig index dd79708d4262..5cff5877d7d1 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -102,14 +102,6 @@ config ANDROID_PARANOID_NETWORK help none -config NET_ACTIVITY_STATS - bool "Network activity statistics tracking" - default y - help - Network activity statistics are useful for tracking wireless - modem activity on 2G, 3G, 4G wireless networks. Counts number of - transmissions and groups them in specified time buckets. - config NETWORK_SECMARK bool "Security Marking" help diff --git a/net/Makefile b/net/Makefile index e04baae76e3c..e700aa62b1af 100644 --- a/net/Makefile +++ b/net/Makefile @@ -77,6 +77,5 @@ endif ifneq ($(CONFIG_NET_L3_MASTER_DEV),) obj-y += l3mdev/ endif -obj-$(CONFIG_NET_ACTIVITY_STATS) += activity_stats.o obj-$(CONFIG_IPC_ROUTER) += ipc_router/ obj-$(CONFIG_RMNET_DATA) += rmnet_data/ diff --git a/net/activity_stats.c b/net/activity_stats.c deleted file mode 100644 index 3bf92d80b8b9..000000000000 --- a/net/activity_stats.c +++ /dev/null @@ -1,118 +0,0 @@ -/* net/activity_stats.c - * - * Copyright (C) 2010 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Author: Mike Chan (mike@android.com) - */ - -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#include <linux/suspend.h> -#include <net/net_namespace.h> - -/* - * Track transmission rates in buckets (power of 2). - * 1,2,4,8...512 seconds. - * - * Buckets represent the count of network transmissions at least - * N seconds apart, where N is 1 << bucket index. - */ -#define BUCKET_MAX 10 - -/* Track network activity frequency */ -static unsigned long activity_stats[BUCKET_MAX]; -static ktime_t last_transmit; -static ktime_t suspend_time; -static DEFINE_SPINLOCK(activity_lock); - -void activity_stats_update(void) -{ - int i; - unsigned long flags; - ktime_t now; - s64 delta; - - spin_lock_irqsave(&activity_lock, flags); - now = ktime_get(); - delta = ktime_to_ns(ktime_sub(now, last_transmit)); - - for (i = BUCKET_MAX - 1; i >= 0; i--) { - /* - * Check if the time delta between network activity is within the - * minimum bucket range. - */ - if (delta < (1000000000ULL << i)) - continue; - - activity_stats[i]++; - last_transmit = now; - break; - } - spin_unlock_irqrestore(&activity_lock, flags); -} - -static int activity_stats_show(struct seq_file *m, void *v) -{ - int i; - - seq_printf(m, "Min Bucket(sec) Count\n"); - - for (i = 0; i < BUCKET_MAX; i++) { - seq_printf(m, "%15d %lu\n", 1 << i, activity_stats[i]); - if (seq_has_overflowed(m)) - return -ENOSPC; - } - - return 0; -} - -static int activity_stats_notifier(struct notifier_block *nb, - unsigned long event, void *dummy) -{ - switch (event) { - case PM_SUSPEND_PREPARE: - suspend_time = ktime_get_real(); - break; - - case PM_POST_SUSPEND: - suspend_time = ktime_sub(ktime_get_real(), suspend_time); - last_transmit = ktime_sub(last_transmit, suspend_time); - } - - return 0; -} - -static int activity_stats_open(struct inode *inode, struct file *file) -{ - return single_open(file, activity_stats_show, PDE_DATA(inode)); -} - -static const struct file_operations activity_stats_fops = { - .open = activity_stats_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -static struct notifier_block activity_stats_notifier_block = { - .notifier_call = activity_stats_notifier, -}; - -static int __init activity_stats_init(void) -{ - proc_create("activity", S_IRUGO, - init_net.proc_net_stat, &activity_stats_fops); - return register_pm_notifier(&activity_stats_notifier_block); -} - -subsys_initcall(activity_stats_init); - diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c index b563a3f5f2a8..2fa3be965101 100644 --- a/net/ax25/ax25_ip.c +++ b/net/ax25/ax25_ip.c @@ -228,8 +228,23 @@ netdev_tx_t ax25_ip_xmit(struct sk_buff *skb) } #endif +static bool ax25_validate_header(const char *header, unsigned int len) +{ + ax25_digi digi; + + if (!len) + return false; + + if (header[0]) + return true; + + return ax25_addr_parse(header + 1, len - 1, NULL, NULL, &digi, NULL, + NULL); +} + const struct header_ops ax25_header_ops = { .create = ax25_hard_header, + .validate = ax25_validate_header, }; EXPORT_SYMBOL(ax25_header_ops); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f00e788e0184..db399c1662ab 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -7155,6 +7155,10 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, status); + if (data_len != sizeof(*cp) + cp->adv_data_len + cp->scan_rsp_len) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, + MGMT_STATUS_INVALID_PARAMS); + flags = __le32_to_cpu(cp->flags); timeout = __le16_to_cpu(cp->timeout); duration = __le16_to_cpu(cp->duration); diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 5f3f64553179..eff69cb270d2 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -567,6 +567,14 @@ int br_set_max_age(struct net_bridge *br, unsigned long val) } +/* Set time interval that dynamic forwarding entries live + * For pure software bridge, allow values outside the 802.1 + * standard specification for special cases: + * 0 - entry never ages (all permanant) + * 1 - entry disappears (no persistance) + * + * Offloaded switch entries maybe more restrictive + */ int br_set_ageing_time(struct net_bridge *br, u32 ageing_time) { struct switchdev_attr attr = { @@ -577,11 +585,8 @@ int br_set_ageing_time(struct net_bridge *br, u32 ageing_time) unsigned long t = clock_t_to_jiffies(ageing_time); int err; - if (t < BR_MIN_AGEING_TIME || t > BR_MAX_AGEING_TIME) - return -ERANGE; - err = switchdev_port_attr_set(br->dev, &attr); - if (err) + if (err && err != -EOPNOTSUPP) return err; br->ageing_time = t; diff --git a/net/core/filter.c b/net/core/filter.c index 37157c4c1a78..f393a22b9d50 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1139,7 +1139,8 @@ void bpf_prog_destroy(struct bpf_prog *fp) } EXPORT_SYMBOL_GPL(bpf_prog_destroy); -static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk) +static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk, + bool locked) { struct sk_filter *fp, *old_fp; @@ -1155,10 +1156,8 @@ static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk) return -ENOMEM; } - old_fp = rcu_dereference_protected(sk->sk_filter, - sock_owned_by_user(sk)); + old_fp = rcu_dereference_protected(sk->sk_filter, locked); rcu_assign_pointer(sk->sk_filter, fp); - if (old_fp) sk_filter_uncharge(sk, old_fp); @@ -1175,7 +1174,8 @@ static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk) * occurs or there is insufficient memory for the filter a negative * errno code is returned. On success the return is zero. */ -int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) +int __sk_attach_filter(struct sock_fprog *fprog, struct sock *sk, + bool locked) { unsigned int fsize = bpf_classic_proglen(fprog); unsigned int bpf_fsize = bpf_prog_size(fprog->len); @@ -1213,7 +1213,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) if (IS_ERR(prog)) return PTR_ERR(prog); - err = __sk_attach_prog(prog, sk); + err = __sk_attach_prog(prog, sk, locked); if (err < 0) { __bpf_prog_release(prog); return err; @@ -1221,7 +1221,12 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) return 0; } -EXPORT_SYMBOL_GPL(sk_attach_filter); +EXPORT_SYMBOL_GPL(__sk_attach_filter); + +int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) +{ + return __sk_attach_filter(fprog, sk, sock_owned_by_user(sk)); +} int sk_attach_bpf(u32 ufd, struct sock *sk) { @@ -1240,7 +1245,7 @@ int sk_attach_bpf(u32 ufd, struct sock *sk) return -EINVAL; } - err = __sk_attach_prog(prog, sk); + err = __sk_attach_prog(prog, sk, sock_owned_by_user(sk)); if (err < 0) { bpf_prog_put(prog); return err; @@ -1913,7 +1918,7 @@ static int __init register_sk_filter_ops(void) } late_initcall(register_sk_filter_ops); -int sk_detach_filter(struct sock *sk) +int __sk_detach_filter(struct sock *sk, bool locked) { int ret = -ENOENT; struct sk_filter *filter; @@ -1921,8 +1926,7 @@ int sk_detach_filter(struct sock *sk) if (sock_flag(sk, SOCK_FILTER_LOCKED)) return -EPERM; - filter = rcu_dereference_protected(sk->sk_filter, - sock_owned_by_user(sk)); + filter = rcu_dereference_protected(sk->sk_filter, locked); if (filter) { RCU_INIT_POINTER(sk->sk_filter, NULL); sk_filter_uncharge(sk, filter); @@ -1931,7 +1935,12 @@ int sk_detach_filter(struct sock *sk) return ret; } -EXPORT_SYMBOL_GPL(sk_detach_filter); +EXPORT_SYMBOL_GPL(__sk_detach_filter); + +int sk_detach_filter(struct sock *sk) +{ + return __sk_detach_filter(sk, sock_owned_by_user(sk)); +} int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf, unsigned int len) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 34ba7a08876d..ca966f7de351 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -905,6 +905,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, + rtnl_link_get_af_size(dev, ext_filter_mask) /* IFLA_AF_SPEC */ + nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_PORT_ID */ + nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_SWITCH_ID */ + + nla_total_size(IFNAMSIZ) /* IFLA_PHYS_PORT_NAME */ + nla_total_size(1); /* IFLA_PROTO_DOWN */ } diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 2d1dd16327a2..732be5afa6ce 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2965,6 +2965,24 @@ int skb_append_pagefrags(struct sk_buff *skb, struct page *page, EXPORT_SYMBOL_GPL(skb_append_pagefrags); /** + * skb_push_rcsum - push skb and update receive checksum + * @skb: buffer to update + * @len: length of data pulled + * + * This function performs an skb_push on the packet and updates + * the CHECKSUM_COMPLETE checksum. It should be used on + * receive path processing instead of skb_push unless you know + * that the checksum difference is zero (e.g., a valid IP header) + * or you are setting ip_summed to CHECKSUM_NONE. + */ +static unsigned char *skb_push_rcsum(struct sk_buff *skb, unsigned len) +{ + skb_push(skb, len); + skb_postpush_rcsum(skb, skb->data, len); + return skb->data; +} + +/** * skb_pull_rcsum - pull skb and update receive checksum * @skb: buffer to update * @len: length of data pulled @@ -4101,9 +4119,9 @@ struct sk_buff *skb_checksum_trimmed(struct sk_buff *skb, if (!pskb_may_pull(skb_chk, offset)) goto err; - __skb_pull(skb_chk, offset); + skb_pull_rcsum(skb_chk, offset); ret = skb_chkf(skb_chk); - __skb_push(skb_chk, offset); + skb_push_rcsum(skb_chk, offset); if (ret) goto err; diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 902d606324a0..8be8f27bfacc 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -204,8 +204,6 @@ void dccp_req_err(struct sock *sk, u64 seq) * ICMPs are not backlogged, hence we cannot get an established * socket here. */ - WARN_ON(req->sk); - if (!between48(seq, dccp_rsk(req)->dreq_iss, dccp_rsk(req)->dreq_gss)) { NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); } else { diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 011e73357ba5..926169c94a0b 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -335,6 +335,9 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, ASSERT_RTNL(); + if (in_dev->dead) + goto no_promotions; + /* 1. Deleting primary ifaddr forces deletion all secondaries * unless alias promotion is set **/ @@ -381,6 +384,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, fib_del_ifaddr(ifa, ifa1); } +no_promotions: /* 2. Unlink it */ *ifap = ifa1->ifa_next; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index e10edb5e78b0..f97ae9d93ee9 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -280,7 +280,6 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb) struct in_device *in_dev; struct fib_result res; struct rtable *rt; - struct flowi4 fl4; struct net *net; int scope; @@ -296,14 +295,13 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb) scope = RT_SCOPE_UNIVERSE; if (!ipv4_is_zeronet(ip_hdr(skb)->saddr)) { - fl4.flowi4_oif = 0; - fl4.flowi4_iif = LOOPBACK_IFINDEX; - fl4.daddr = ip_hdr(skb)->saddr; - fl4.saddr = 0; - fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos); - fl4.flowi4_scope = scope; - fl4.flowi4_mark = IN_DEV_SRC_VMARK(in_dev) ? skb->mark : 0; - fl4.flowi4_tun_key.tun_id = 0; + struct flowi4 fl4 = { + .flowi4_iif = LOOPBACK_IFINDEX, + .daddr = ip_hdr(skb)->saddr, + .flowi4_tos = RT_TOS(ip_hdr(skb)->tos), + .flowi4_scope = scope, + .flowi4_mark = IN_DEV_SRC_VMARK(in_dev) ? skb->mark : 0, + }; if (!fib_lookup(net, &fl4, &res, 0)) return FIB_RES_PREFSRC(net, res); } else { @@ -923,6 +921,9 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim) subnet = 1; } + if (in_dev->dead) + goto no_promotions; + /* Deletion is more complicated than add. * We should take care of not to delete too much :-) * @@ -998,6 +999,7 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim) } } +no_promotions: if (!(ok & BRD_OK)) fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim); if (subnet && ifa->ifa_prefixlen < 31) { diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 05e4cba14162..b3086cf27027 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -356,9 +356,8 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu) skb_dst_set(skb, &rt->dst); skb->dev = dev; - skb->reserved_tailroom = skb_end_offset(skb) - - min(mtu, skb_end_offset(skb)); skb_reserve(skb, hlen); + skb_tailroom_reserve(skb, mtu, tlen); skb_reset_network_header(skb); pip = ip_hdr(skb); diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index e73b4f8b2096..dbf7f7ee2958 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1237,13 +1237,16 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, if (!skb) return -EINVAL; - cork->length += size; if ((size + skb->len > mtu) && (sk->sk_protocol == IPPROTO_UDP) && (rt->dst.dev->features & NETIF_F_UFO)) { + if (skb->ip_summed != CHECKSUM_PARTIAL) + return -EOPNOTSUPP; + skb_shinfo(skb)->gso_size = mtu - fragheaderlen; skb_shinfo(skb)->gso_type = SKB_GSO_UDP; } + cork->length += size; while (size > 0) { if (skb_is_gso(skb)) { diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index cbb51f3fac06..ce30c8b72457 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -663,6 +663,8 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, inner_iph = (const struct iphdr *)skb_inner_network_header(skb); connected = (tunnel->parms.iph.daddr != 0); + memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); + dst = tnl_params->daddr; if (dst == 0) { /* NBMA tunnel */ @@ -760,7 +762,6 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, tunnel->err_time + IPTUNNEL_ERR_TIMEO)) { tunnel->err_count--; - memset(IPCB(skb), 0, sizeof(*IPCB(skb))); dst_link_failure(skb); } else tunnel->err_count = 0; diff --git a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c index c6eb42100e9a..ea91058b5f6f 100644 --- a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c +++ b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c @@ -108,10 +108,18 @@ static int masq_inet_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev; + struct in_device *idev = ((struct in_ifaddr *)ptr)->ifa_dev; struct netdev_notifier_info info; - netdev_notifier_info_init(&info, dev); + /* The masq_dev_notifier will catch the case of the device going + * down. So if the inetdev is dead and being destroyed we have + * no work to do. Otherwise this is an individual address removal + * and we have to perform the flush. + */ + if (idev->dead) + return NOTIFY_DONE; + + netdev_notifier_info_init(&info, idev->dev); return masq_device_event(this, event, &info); } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index e5cdafcc2140..2ee7b830a35e 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -269,7 +269,6 @@ #include <linux/crypto.h> #include <linux/time.h> #include <linux/slab.h> -#include <linux/uid_stat.h> #include <net/icmp.h> #include <net/inet_common.h> @@ -1294,10 +1293,6 @@ out: tcp_push(sk, flags, mss_now, tp->nonagle, size_goal); out_nopush: release_sock(sk); - - if (copied + copied_syn) - uid_stat_tcp_snd(from_kuid(&init_user_ns, current_uid()), - copied + copied_syn); return copied + copied_syn; do_fault: @@ -1575,8 +1570,6 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc, if (copied > 0) { tcp_recv_skb(sk, seq, &offset); tcp_cleanup_rbuf(sk, copied); - uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()), - copied); } return copied; } @@ -1910,10 +1903,6 @@ skip_copy: tcp_cleanup_rbuf(sk, copied); release_sock(sk); - - if (copied > 0) - uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()), - copied); return copied; out: @@ -1922,9 +1911,6 @@ out: recv_urg: err = tcp_recv_urg(sk, msg, len, flags); - if (err > 0) - uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()), - err); goto out; recv_sndq: diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index f5cfc5ff0712..07408b81bcd7 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -320,8 +320,6 @@ void tcp_req_err(struct sock *sk, u32 seq, bool abort) /* ICMPs are not backlogged, hence we cannot get * an established socket here. */ - WARN_ON(req->sk); - if (seq != tcp_rsk(req)->snt_isn) { NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); } else if (abort) { diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index c8cbc2b4b792..a726d7853ce5 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -550,7 +550,7 @@ reset: */ if (crtt > tp->srtt_us) { /* Set RTO like tcp_rtt_estimator(), but from cached RTT. */ - crtt /= 8 * USEC_PER_MSEC; + crtt /= 8 * USEC_PER_SEC / HZ; inet_csk(sk)->icsk_rto = crtt + max(2 * crtt, tcp_rto_min(sk)); } else if (tp->srtt_us == 0) { /* RFC6298: 5.7 We've failed to get a valid RTT sample from diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index ac6b1961ffeb..9475a2748a9a 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -458,7 +458,7 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, newtp->rcv_wup = newtp->copied_seq = newtp->rcv_nxt = treq->rcv_isn + 1; - newtp->segs_in = 0; + newtp->segs_in = 1; newtp->snd_sml = newtp->snd_una = newtp->snd_nxt = newtp->snd_up = treq->snt_isn + 1; @@ -818,6 +818,7 @@ int tcp_child_process(struct sock *parent, struct sock *child, int ret = 0; int state = child->sk_state; + tcp_sk(child)->segs_in += max_t(u16, 1, skb_shinfo(skb)->gso_segs); if (!sock_owned_by_user(child)) { ret = tcp_rcv_state_process(child, skb); /* Wakeup parent, send SIGIO */ diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 5411b65b7702..eeac33e94527 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1996,10 +1996,14 @@ void udp_v4_early_demux(struct sk_buff *skb) if (!in_dev) return; - ours = ip_check_mc_rcu(in_dev, iph->daddr, iph->saddr, - iph->protocol); - if (!ours) - return; + /* we are supposed to accept bcast packets */ + if (skb->pkt_type == PACKET_MULTICAST) { + ours = ip_check_mc_rcu(in_dev, iph->daddr, iph->saddr, + iph->protocol); + if (!ours) + return; + } + sk = __udp4_lib_mcast_demux_lookup(net, uh->dest, iph->daddr, uh->source, iph->saddr, dif); } else if (skb->pkt_type == PACKET_HOST) { diff --git a/net/ipv4/udp_tunnel.c b/net/ipv4/udp_tunnel.c index aba428626b52..280a9bdeddee 100644 --- a/net/ipv4/udp_tunnel.c +++ b/net/ipv4/udp_tunnel.c @@ -89,6 +89,8 @@ int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, uh->source = src_port; uh->len = htons(skb->len); + memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); + udp_set_csum(nocheck, skb, src, dst, skb->len); return iptunnel_xmit(sk, rt, skb, src, dst, IPPROTO_UDP, diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 7b0edb37a115..ab7ab839b057 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -20,7 +20,7 @@ static struct xfrm_policy_afinfo xfrm4_policy_afinfo; static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4, - int tos, int oif, + int tos, const xfrm_address_t *saddr, const xfrm_address_t *daddr) { @@ -29,7 +29,6 @@ static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4, memset(fl4, 0, sizeof(*fl4)); fl4->daddr = daddr->a4; fl4->flowi4_tos = tos; - fl4->flowi4_oif = oif; if (saddr) fl4->saddr = saddr->a4; @@ -42,22 +41,22 @@ static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4, return ERR_CAST(rt); } -static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, int oif, +static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, const xfrm_address_t *saddr, const xfrm_address_t *daddr) { struct flowi4 fl4; - return __xfrm4_dst_lookup(net, &fl4, tos, oif, saddr, daddr); + return __xfrm4_dst_lookup(net, &fl4, tos, saddr, daddr); } -static int xfrm4_get_saddr(struct net *net, int oif, +static int xfrm4_get_saddr(struct net *net, xfrm_address_t *saddr, xfrm_address_t *daddr) { struct dst_entry *dst; struct flowi4 fl4; - dst = __xfrm4_dst_lookup(net, &fl4, 0, oif, NULL, daddr); + dst = __xfrm4_dst_lookup(net, &fl4, 0, NULL, daddr); if (IS_ERR(dst)) return -EHOSTUNREACH; diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c index 835ec57c233b..840a4388f860 100644 --- a/net/ipv6/exthdrs_core.c +++ b/net/ipv6/exthdrs_core.c @@ -260,7 +260,11 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, return -EINVAL; } } - return -ENOENT; + if (!found) + return -ENOENT; + if (fragoff) + *fragoff = _frag_off; + break; } hdrlen = 8; } else if (nexthdr == NEXTHDR_AUTH) { diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index e5ea177d34c6..4650c6824783 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -778,6 +778,8 @@ static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev) __u32 mtu; int err; + memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); + if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) encap_limit = t->parms.encap_limit; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 31144c486c52..c87ca6987f9c 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -891,8 +891,7 @@ static struct dst_entry *ip6_sk_dst_check(struct sock *sk, #ifdef CONFIG_IPV6_SUBTREES ip6_rt_check(&rt->rt6i_src, &fl6->saddr, np->saddr_cache) || #endif - (!(fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) && - (fl6->flowi6_oif && fl6->flowi6_oif != dst->dev->ifindex))) { + (fl6->flowi6_oif && fl6->flowi6_oif != dst->dev->ifindex)) { dst_release(dst); dst = NULL; } @@ -1091,8 +1090,8 @@ static inline int ip6_ufo_append_data(struct sock *sk, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb), void *from, int length, int hh_len, int fragheaderlen, - int transhdrlen, int mtu, unsigned int flags, - const struct flowi6 *fl6) + int exthdrlen, int transhdrlen, int mtu, + unsigned int flags, const struct flowi6 *fl6) { struct sk_buff *skb; @@ -1117,7 +1116,7 @@ static inline int ip6_ufo_append_data(struct sock *sk, skb_put(skb, fragheaderlen + transhdrlen); /* initialize network header pointer */ - skb_reset_network_header(skb); + skb_set_network_header(skb, exthdrlen); /* initialize protocol header pointer */ skb->transport_header = skb->network_header + fragheaderlen; @@ -1359,7 +1358,7 @@ emsgsize: (rt->dst.dev->features & NETIF_F_UFO) && (sk->sk_type == SOCK_DGRAM) && !udp_get_no_check6_tx(sk)) { err = ip6_ufo_append_data(sk, queue, getfrag, from, length, - hh_len, fragheaderlen, + hh_len, fragheaderlen, exthdrlen, transhdrlen, mtu, flags, fl6); if (err) goto error; diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 137fca42aaa6..3991b21e24ad 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -343,12 +343,12 @@ static int ip6_tnl_create2(struct net_device *dev) t = netdev_priv(dev); + dev->rtnl_link_ops = &ip6_link_ops; err = register_netdevice(dev); if (err < 0) goto out; strcpy(t->parms.name, dev->name); - dev->rtnl_link_ops = &ip6_link_ops; dev_hold(dev); ip6_tnl_link(ip6n, t); @@ -1180,6 +1180,8 @@ ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) u8 tproto; int err; + memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); + tproto = ACCESS_ONCE(t->parms.proto); if (tproto != IPPROTO_IPIP && tproto != 0) return -1; diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 5ee56d0a8699..d64ee7e83664 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1574,9 +1574,8 @@ static struct sk_buff *mld_newpack(struct inet6_dev *idev, unsigned int mtu) return NULL; skb->priority = TC_PRIO_CONTROL; - skb->reserved_tailroom = skb_end_offset(skb) - - min(mtu, skb_end_offset(skb)); skb_reserve(skb, hlen); + skb_tailroom_reserve(skb, mtu, tlen); if (__ipv6_get_lladdr(idev, &addr_buf, IFA_F_TENTATIVE)) { /* <draft-ietf-magma-mld-source-05.txt>: diff --git a/net/ipv6/route.c b/net/ipv6/route.c index dd76806358fe..01d7ee57d937 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1049,9 +1049,6 @@ static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); saved_fn = fn; - if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) - oif = 0; - redo_rt6_select: rt = rt6_select(fn, oif, strict); if (rt->rt6i_nsiblings) diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index a1b6adc20e1e..9cb0ff304336 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -837,8 +837,8 @@ start_lookup: flush_stack(stack, count, skb, count - 1); } else { if (!inner_flushed) - UDP_INC_STATS_BH(net, UDP_MIB_IGNOREDMULTI, - proto == IPPROTO_UDPLITE); + UDP6_INC_STATS_BH(net, UDP_MIB_IGNOREDMULTI, + proto == IPPROTO_UDPLITE); consume_skb(skb); } return 0; @@ -916,11 +916,9 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, ret = udpv6_queue_rcv_skb(sk, skb); sock_put(sk); - /* a return value > 0 means to resubmit the input, but - * it wants the return to be -protocol, or 0 - */ + /* a return value > 0 means to resubmit the input */ if (ret > 0) - return -ret; + return ret; return 0; } diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index c074771a10f7..c23742462f02 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -27,7 +27,7 @@ static struct xfrm_policy_afinfo xfrm6_policy_afinfo; -static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif, +static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, const xfrm_address_t *saddr, const xfrm_address_t *daddr) { @@ -36,8 +36,6 @@ static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif, int err; memset(&fl6, 0, sizeof(fl6)); - fl6.flowi6_oif = oif; - fl6.flowi6_flags = FLOWI_FLAG_SKIP_NH_OIF; memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr)); if (saddr) memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr)); @@ -53,13 +51,13 @@ static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif, return dst; } -static int xfrm6_get_saddr(struct net *net, int oif, +static int xfrm6_get_saddr(struct net *net, xfrm_address_t *saddr, xfrm_address_t *daddr) { struct dst_entry *dst; struct net_device *dev; - dst = xfrm6_dst_lookup(net, 0, oif, NULL, daddr); + dst = xfrm6_dst_lookup(net, 0, NULL, daddr); if (IS_ERR(dst)) return -EHOSTUNREACH; diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index ec22078b0914..42de4ccd159f 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -123,12 +123,11 @@ static int l2tp_ip_recv(struct sk_buff *skb) struct l2tp_tunnel *tunnel = NULL; int length; - /* Point to L2TP header */ - optr = ptr = skb->data; - if (!pskb_may_pull(skb, 4)) goto discard; + /* Point to L2TP header */ + optr = ptr = skb->data; session_id = ntohl(*((__be32 *) ptr)); ptr += 4; @@ -156,6 +155,9 @@ static int l2tp_ip_recv(struct sk_buff *skb) if (!pskb_may_pull(skb, length)) goto discard; + /* Point to L2TP header */ + optr = ptr = skb->data; + ptr += 4; pr_debug("%s: ip recv\n", tunnel->name); print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, ptr, length); } diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index a2c8747d2936..9ee4ddb6b397 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -135,12 +135,11 @@ static int l2tp_ip6_recv(struct sk_buff *skb) struct l2tp_tunnel *tunnel = NULL; int length; - /* Point to L2TP header */ - optr = ptr = skb->data; - if (!pskb_may_pull(skb, 4)) goto discard; + /* Point to L2TP header */ + optr = ptr = skb->data; session_id = ntohl(*((__be32 *) ptr)); ptr += 4; @@ -168,6 +167,9 @@ static int l2tp_ip6_recv(struct sk_buff *skb) if (!pskb_may_pull(skb, length)) goto discard; + /* Point to L2TP header */ + optr = ptr = skb->data; + ptr += 4; pr_debug("%s: ip recv\n", tunnel->name); print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, ptr, length); } diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 6a12b0f5cac8..980e9e9b6684 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -7,6 +7,7 @@ * Copyright 2007, Michael Wu <flamingice@sourmilk.net> * Copyright 2009, Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * 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 @@ -1484,14 +1485,21 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) sdata_info(sdata, "Trigger new scan to find an IBSS to join\n"); - num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy, - &ifibss->chandef, - channels, - ARRAY_SIZE(channels)); scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef); - ieee80211_request_ibss_scan(sdata, ifibss->ssid, - ifibss->ssid_len, channels, num, - scan_width); + + if (ifibss->fixed_channel) { + num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy, + &ifibss->chandef, + channels, + ARRAY_SIZE(channels)); + ieee80211_request_ibss_scan(sdata, ifibss->ssid, + ifibss->ssid_len, channels, + num, scan_width); + } else { + ieee80211_request_ibss_scan(sdata, ifibss->ssid, + ifibss->ssid_len, NULL, + 0, scan_width); + } } else { int interval = IEEE80211_SCAN_INTERVAL; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index c9e325d2e120..7a2b7915093b 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -977,7 +977,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, if (sdata->vif.txq) { struct txq_info *txqi = to_txq_info(sdata->vif.txq); + spin_lock_bh(&txqi->queue.lock); ieee80211_purge_tx_queue(&local->hw, &txqi->queue); + spin_unlock_bh(&txqi->queue.lock); + atomic_set(&sdata->txqs_len[txqi->txq.ac], 0); } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 4cbf36cae806..a3bb8f7f5fc5 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2250,7 +2250,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) struct ieee80211_local *local = rx->local; struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; - u16 q, hdrlen; + u16 ac, q, hdrlen; hdr = (struct ieee80211_hdr *) skb->data; hdrlen = ieee80211_hdrlen(hdr->frame_control); @@ -2319,7 +2319,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) ether_addr_equal(sdata->vif.addr, hdr->addr3)) return RX_CONTINUE; - q = ieee80211_select_queue_80211(sdata, skb, hdr); + ac = ieee80211_select_queue_80211(sdata, skb, hdr); + q = sdata->vif.hw_queue[ac]; if (ieee80211_queue_stopped(&local->hw, q)) { IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion); return RX_DROP_MONITOR; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index f91d1873218c..67066d048e6f 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -256,11 +256,11 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) } /* Caller must hold local->sta_mtx */ -static void sta_info_hash_add(struct ieee80211_local *local, - struct sta_info *sta) +static int sta_info_hash_add(struct ieee80211_local *local, + struct sta_info *sta) { - rhashtable_insert_fast(&local->sta_hash, &sta->hash_node, - sta_rht_params); + return rhashtable_insert_fast(&local->sta_hash, &sta->hash_node, + sta_rht_params); } static void sta_deliver_ps_frames(struct work_struct *wk) @@ -484,11 +484,17 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) { struct ieee80211_local *local = sta->local; struct ieee80211_sub_if_data *sdata = sta->sdata; - struct station_info sinfo; + struct station_info *sinfo; int err = 0; lockdep_assert_held(&local->sta_mtx); + sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL); + if (!sinfo) { + err = -ENOMEM; + goto out_err; + } + /* check if STA exists already */ if (sta_info_get_bss(sdata, sta->sta.addr)) { err = -EEXIST; @@ -503,7 +509,9 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) set_sta_flag(sta, WLAN_STA_BLOCK_BA); /* make the station visible */ - sta_info_hash_add(local, sta); + err = sta_info_hash_add(local, sta); + if (err) + goto out_drop_sta; list_add_tail_rcu(&sta->list, &local->sta_list); @@ -520,10 +528,9 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) ieee80211_sta_debugfs_add(sta); rate_control_add_sta_debugfs(sta); - memset(&sinfo, 0, sizeof(sinfo)); - sinfo.filled = 0; - sinfo.generation = local->sta_generation; - cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL); + sinfo->generation = local->sta_generation; + cfg80211_new_sta(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL); + kfree(sinfo); sta_dbg(sdata, "Inserted STA %pM\n", sta->sta.addr); @@ -538,6 +545,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) out_remove: sta_info_hash_del(local, sta); list_del_rcu(&sta->list); + out_drop_sta: local->num_sta--; synchronize_net(); __cleanup_single_sta(sta); @@ -882,7 +890,7 @@ static void __sta_info_destroy_part2(struct sta_info *sta) { struct ieee80211_local *local = sta->local; struct ieee80211_sub_if_data *sdata = sta->sdata; - struct station_info sinfo = {}; + struct station_info *sinfo; int ret; /* @@ -920,8 +928,11 @@ static void __sta_info_destroy_part2(struct sta_info *sta) sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr); - sta_set_sinfo(sta, &sinfo); - cfg80211_del_sta_sinfo(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL); + sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL); + if (sinfo) + sta_set_sinfo(sta, sinfo); + cfg80211_del_sta_sinfo(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL); + kfree(sinfo); rate_control_remove_sta_debugfs(sta); ieee80211_sta_debugfs_remove(sta); diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index c32fc411a911..881bc2072809 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -518,6 +518,9 @@ static struct net_device *find_outdev(struct net *net, if (!dev) return ERR_PTR(-ENODEV); + if (IS_ERR(dev)) + return dev; + /* The caller is holding rtnl anyways, so release the dev reference */ dev_put(dev); diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index e1442bfb668d..e2e7d54f9bb1 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1606,7 +1606,7 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, * When in TCP_TIME_WAIT the sk is not a "struct sock" but * "struct inet_timewait_sock" which is missing fields. */ - if (sk->sk_state == TCP_TIME_WAIT) { + if (!sk_fullsock(sk) || sk->sk_state == TCP_TIME_WAIT) { sock_gen_put(sk); sk = NULL; } @@ -1689,7 +1689,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) /* default: Fall through and do UID releated work */ } - sk = skb->sk; + sk = skb_to_full_sk(skb); /* * When in TCP_TIME_WAIT the sk is not a "struct sock" but * "struct inet_timewait_sock" which is missing fields. diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 992396aa635c..da1ae0e13cb5 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1916,6 +1916,10 @@ retry: goto retry; } + if (!dev_validate_header(dev, skb->data, len)) { + err = -EINVAL; + goto out_unlock; + } if (len > (dev->mtu + dev->hard_header_len + extra_len) && !packet_extra_vlan_len_allowed(dev, skb)) { err = -EMSGSIZE; @@ -2326,18 +2330,6 @@ static void tpacket_destruct_skb(struct sk_buff *skb) sock_wfree(skb); } -static bool ll_header_truncated(const struct net_device *dev, int len) -{ - /* net device doesn't like empty head */ - if (unlikely(len < dev->hard_header_len)) { - net_warn_ratelimited("%s: packet size is too short (%d < %d)\n", - current->comm, len, dev->hard_header_len); - return true; - } - - return false; -} - static void tpacket_set_protocol(const struct net_device *dev, struct sk_buff *skb) { @@ -2420,19 +2412,19 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, if (unlikely(err < 0)) return -EINVAL; } else if (dev->hard_header_len) { - if (ll_header_truncated(dev, tp_len)) - return -EINVAL; + int hdrlen = min_t(int, dev->hard_header_len, tp_len); skb_push(skb, dev->hard_header_len); - err = skb_store_bits(skb, 0, data, - dev->hard_header_len); + err = skb_store_bits(skb, 0, data, hdrlen); if (unlikely(err)) return err; + if (!dev_validate_header(dev, skb->data, hdrlen)) + return -EINVAL; if (!skb->protocol) tpacket_set_protocol(dev, skb); - data += dev->hard_header_len; - to_write -= dev->hard_header_len; + data += hdrlen; + to_write -= hdrlen; } offset = offset_in_page(data); @@ -2763,9 +2755,6 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) offset = dev_hard_header(skb, dev, ntohs(proto), addr, NULL, len); if (unlikely(offset < 0)) goto out_free; - } else { - if (ll_header_truncated(dev, len)) - goto out_free; } /* Returns -EFAULT on error */ @@ -2773,6 +2762,12 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) if (err) goto out_free; + if (sock->type == SOCK_RAW && + !dev_validate_header(dev, skb->data, len)) { + err = -EINVAL; + goto out_free; + } + sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags); if (!gso_type && (len > dev->mtu + reserve + extra_len) && diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index ec529121f38a..ce46f1c7f133 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -526,6 +526,8 @@ static int sctp_v6_cmp_addr(const union sctp_addr *addr1, } return 0; } + if (addr1->v6.sin6_port != addr2->v6.sin6_port) + return 0; if (!ipv6_addr_equal(&addr1->v6.sin6_addr, &addr2->v6.sin6_addr)) return 0; /* If this is a linklocal address, compare the scope_id. */ diff --git a/net/socket.c b/net/socket.c index b3d29ea2dd75..5211c40daecc 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2280,31 +2280,31 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, break; } -out_put: - fput_light(sock->file, fput_needed); - if (err == 0) - return datagrams; + goto out_put; - if (datagrams != 0) { + if (datagrams == 0) { + datagrams = err; + goto out_put; + } + + /* + * We may return less entries than requested (vlen) if the + * sock is non block and there aren't enough datagrams... + */ + if (err != -EAGAIN) { /* - * We may return less entries than requested (vlen) if the - * sock is non block and there aren't enough datagrams... + * ... or if recvmsg returns an error after we + * received some datagrams, where we record the + * error to return on the next call or if the + * app asks about it using getsockopt(SO_ERROR). */ - if (err != -EAGAIN) { - /* - * ... or if recvmsg returns an error after we - * received some datagrams, where we record the - * error to return on the next call or if the - * app asks about it using getsockopt(SO_ERROR). - */ - sock->sk->sk_err = -err; - } - - return datagrams; + sock->sk->sk_err = -err; } +out_put: + fput_light(sock->file, fput_needed); - return err; + return datagrams; } SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr __user *, mmsg, diff --git a/net/tipc/socket.c b/net/tipc/socket.c index b53246fb0412..e53003cf7703 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -673,7 +673,7 @@ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq, struct tipc_sock *tsk = tipc_sk(sk); struct net *net = sock_net(sk); struct tipc_msg *mhdr = &tsk->phdr; - struct sk_buff_head *pktchain = &sk->sk_write_queue; + struct sk_buff_head pktchain; struct iov_iter save = msg->msg_iter; uint mtu; int rc; @@ -687,14 +687,16 @@ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq, msg_set_nameupper(mhdr, seq->upper); msg_set_hdr_sz(mhdr, MCAST_H_SIZE); + skb_queue_head_init(&pktchain); + new_mtu: mtu = tipc_bcast_get_mtu(net); - rc = tipc_msg_build(mhdr, msg, 0, dsz, mtu, pktchain); + rc = tipc_msg_build(mhdr, msg, 0, dsz, mtu, &pktchain); if (unlikely(rc < 0)) return rc; do { - rc = tipc_bcast_xmit(net, pktchain); + rc = tipc_bcast_xmit(net, &pktchain); if (likely(!rc)) return dsz; @@ -704,7 +706,7 @@ new_mtu: if (!rc) continue; } - __skb_queue_purge(pktchain); + __skb_queue_purge(&pktchain); if (rc == -EMSGSIZE) { msg->msg_iter = save; goto new_mtu; @@ -863,7 +865,7 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz) struct net *net = sock_net(sk); struct tipc_msg *mhdr = &tsk->phdr; u32 dnode, dport; - struct sk_buff_head *pktchain = &sk->sk_write_queue; + struct sk_buff_head pktchain; struct sk_buff *skb; struct tipc_name_seq *seq; struct iov_iter save; @@ -924,17 +926,18 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz) msg_set_hdr_sz(mhdr, BASIC_H_SIZE); } + skb_queue_head_init(&pktchain); save = m->msg_iter; new_mtu: mtu = tipc_node_get_mtu(net, dnode, tsk->portid); - rc = tipc_msg_build(mhdr, m, 0, dsz, mtu, pktchain); + rc = tipc_msg_build(mhdr, m, 0, dsz, mtu, &pktchain); if (rc < 0) return rc; do { - skb = skb_peek(pktchain); + skb = skb_peek(&pktchain); TIPC_SKB_CB(skb)->wakeup_pending = tsk->link_cong; - rc = tipc_node_xmit(net, pktchain, dnode, tsk->portid); + rc = tipc_node_xmit(net, &pktchain, dnode, tsk->portid); if (likely(!rc)) { if (sock->state != SS_READY) sock->state = SS_CONNECTING; @@ -946,7 +949,7 @@ new_mtu: if (!rc) continue; } - __skb_queue_purge(pktchain); + __skb_queue_purge(&pktchain); if (rc == -EMSGSIZE) { m->msg_iter = save; goto new_mtu; @@ -1016,7 +1019,7 @@ static int __tipc_send_stream(struct socket *sock, struct msghdr *m, size_t dsz) struct net *net = sock_net(sk); struct tipc_sock *tsk = tipc_sk(sk); struct tipc_msg *mhdr = &tsk->phdr; - struct sk_buff_head *pktchain = &sk->sk_write_queue; + struct sk_buff_head pktchain; DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name); u32 portid = tsk->portid; int rc = -EINVAL; @@ -1044,17 +1047,19 @@ static int __tipc_send_stream(struct socket *sock, struct msghdr *m, size_t dsz) timeo = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT); dnode = tsk_peer_node(tsk); + skb_queue_head_init(&pktchain); next: save = m->msg_iter; mtu = tsk->max_pkt; send = min_t(uint, dsz - sent, TIPC_MAX_USER_MSG_SIZE); - rc = tipc_msg_build(mhdr, m, sent, send, mtu, pktchain); + rc = tipc_msg_build(mhdr, m, sent, send, mtu, &pktchain); if (unlikely(rc < 0)) return rc; + do { if (likely(!tsk_conn_cong(tsk))) { - rc = tipc_node_xmit(net, pktchain, dnode, portid); + rc = tipc_node_xmit(net, &pktchain, dnode, portid); if (likely(!rc)) { tsk->sent_unacked++; sent += send; @@ -1063,7 +1068,7 @@ next: goto next; } if (rc == -EMSGSIZE) { - __skb_queue_purge(pktchain); + __skb_queue_purge(&pktchain); tsk->max_pkt = tipc_node_get_mtu(net, dnode, portid); m->msg_iter = save; @@ -1077,7 +1082,7 @@ next: rc = tipc_wait_for_sndpkt(sock, &timeo); } while (!rc); - __skb_queue_purge(pktchain); + __skb_queue_purge(&pktchain); return sent ? sent : rc; } diff --git a/net/wireless/core.c b/net/wireless/core.c index 7426ab13cede..6d3402434a63 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -730,6 +730,36 @@ int wiphy_register(struct wiphy *wiphy) nl80211_send_reg_change_event(&request); } + /* Check that nobody globally advertises any capabilities they do not + * advertise on all possible interface types. + */ + if (wiphy->extended_capabilities_len && + wiphy->num_iftype_ext_capab && + wiphy->iftype_ext_capab) { + u8 supported_on_all, j; + const struct wiphy_iftype_ext_capab *capab; + + capab = wiphy->iftype_ext_capab; + for (j = 0; j < wiphy->extended_capabilities_len; j++) { + if (capab[0].extended_capabilities_len > j) + supported_on_all = + capab[0].extended_capabilities[j]; + else + supported_on_all = 0x00; + for (i = 1; i < wiphy->num_iftype_ext_capab; i++) { + if (j >= capab[i].extended_capabilities_len) { + supported_on_all = 0x00; + break; + } + supported_on_all &= + capab[i].extended_capabilities[j]; + } + if (WARN_ON(wiphy->extended_capabilities[j] & + ~supported_on_all)) + break; + } + } + rdev->wiphy.registered = true; rtnl_unlock(); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ad4b729262fd..30f54d1fc841 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1253,7 +1253,7 @@ nl80211_send_mgmt_stypes(struct sk_buff *msg, struct nl80211_dump_wiphy_state { s64 filter_wiphy; long start; - long split_start, band_start, chan_start; + long split_start, band_start, chan_start, capa_start; bool split; }; @@ -1731,6 +1731,47 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, rdev->wiphy.ext_features)) goto nla_put_failure; + state->split_start++; + break; + case 13: + if (rdev->wiphy.num_iftype_ext_capab && + rdev->wiphy.iftype_ext_capab) { + struct nlattr *nested_ext_capab, *nested; + + nested = nla_nest_start(msg, + NL80211_ATTR_IFTYPE_EXT_CAPA); + if (!nested) + goto nla_put_failure; + + for (i = state->capa_start; + i < rdev->wiphy.num_iftype_ext_capab; i++) { + const struct wiphy_iftype_ext_capab *capab; + + capab = &rdev->wiphy.iftype_ext_capab[i]; + + nested_ext_capab = nla_nest_start(msg, i); + if (!nested_ext_capab || + nla_put_u32(msg, NL80211_ATTR_IFTYPE, + capab->iftype) || + nla_put(msg, NL80211_ATTR_EXT_CAPA, + capab->extended_capabilities_len, + capab->extended_capabilities) || + nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK, + capab->extended_capabilities_len, + capab->extended_capabilities_mask)) + goto nla_put_failure; + + nla_nest_end(msg, nested_ext_capab); + if (state->split) + break; + } + nla_nest_end(msg, nested); + if (i < rdev->wiphy.num_iftype_ext_capab) { + state->capa_start = i + 1; + break; + } + } + /* done */ state->split_start = 0; break; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index b5e665b3cfb0..cf0193b74ae3 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -115,8 +115,7 @@ static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo) rcu_read_unlock(); } -static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, - int tos, int oif, +static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, const xfrm_address_t *saddr, const xfrm_address_t *daddr, int family) @@ -128,15 +127,14 @@ static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, if (unlikely(afinfo == NULL)) return ERR_PTR(-EAFNOSUPPORT); - dst = afinfo->dst_lookup(net, tos, oif, saddr, daddr); + dst = afinfo->dst_lookup(net, tos, saddr, daddr); xfrm_policy_put_afinfo(afinfo); return dst; } -static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, - int tos, int oif, +static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos, xfrm_address_t *prev_saddr, xfrm_address_t *prev_daddr, int family) @@ -155,7 +153,7 @@ static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, daddr = x->coaddr; } - dst = __xfrm_dst_lookup(net, tos, oif, saddr, daddr, family); + dst = __xfrm_dst_lookup(net, tos, saddr, daddr, family); if (!IS_ERR(dst)) { if (prev_saddr != saddr) @@ -1395,15 +1393,15 @@ int __xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk) } static int -xfrm_get_saddr(struct net *net, int oif, xfrm_address_t *local, - xfrm_address_t *remote, unsigned short family) +xfrm_get_saddr(struct net *net, xfrm_address_t *local, xfrm_address_t *remote, + unsigned short family) { int err; struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); if (unlikely(afinfo == NULL)) return -EINVAL; - err = afinfo->get_saddr(net, oif, local, remote); + err = afinfo->get_saddr(net, local, remote); xfrm_policy_put_afinfo(afinfo); return err; } @@ -1432,8 +1430,7 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, const struct flowi *fl, remote = &tmpl->id.daddr; local = &tmpl->saddr; if (xfrm_addr_any(local, tmpl->encap_family)) { - error = xfrm_get_saddr(net, fl->flowi_oif, - &tmp, remote, + error = xfrm_get_saddr(net, &tmp, remote, tmpl->encap_family); if (error) goto fail; @@ -1712,8 +1709,8 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) { family = xfrm[i]->props.family; - dst = xfrm_dst_lookup(xfrm[i], tos, fl->flowi_oif, - &saddr, &daddr, family); + dst = xfrm_dst_lookup(xfrm[i], tos, &saddr, &daddr, + family); err = PTR_ERR(dst); if (IS_ERR(dst)) goto put_states; diff --git a/scripts/coccinelle/iterators/use_after_iter.cocci b/scripts/coccinelle/iterators/use_after_iter.cocci index f085f5968c52..ce8cc9c006e5 100644 --- a/scripts/coccinelle/iterators/use_after_iter.cocci +++ b/scripts/coccinelle/iterators/use_after_iter.cocci @@ -123,7 +123,7 @@ list_remove_head(x,c,...) | sizeof(<+...c...+>) | -&c->member + &c->member | c = E | diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile index d79cba4ce3eb..ebced77deb9c 100644 --- a/scripts/kconfig/Makefile +++ b/scripts/kconfig/Makefile @@ -96,13 +96,15 @@ savedefconfig: $(obj)/conf defconfig: $(obj)/conf ifeq ($(KBUILD_DEFCONFIG),) $< $(silent) --defconfig $(Kconfig) -else ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG)),) +else +ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG)),) @$(kecho) "*** Default configuration is based on '$(KBUILD_DEFCONFIG)'" $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG) $(Kconfig) else @$(kecho) "*** Default configuration is based on target '$(KBUILD_DEFCONFIG)'" $(Q)$(MAKE) -f $(srctree)/Makefile $(KBUILD_DEFCONFIG) endif +endif %_defconfig: $(obj)/conf $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) diff --git a/scripts/package/mkspec b/scripts/package/mkspec index 71004daefe31..fe44d68e9344 100755 --- a/scripts/package/mkspec +++ b/scripts/package/mkspec @@ -131,11 +131,11 @@ echo 'rm -rf $RPM_BUILD_ROOT' echo "" echo "%post" echo "if [ -x /sbin/installkernel -a -r /boot/vmlinuz-$KERNELRELEASE -a -r /boot/System.map-$KERNELRELEASE ]; then" -echo "cp /boot/vmlinuz-$KERNELRELEASE /boot/vmlinuz-$KERNELRELEASE-rpm" -echo "cp /boot/System.map-$KERNELRELEASE /boot/System.map-$KERNELRELEASE-rpm" +echo "cp /boot/vmlinuz-$KERNELRELEASE /boot/.vmlinuz-$KERNELRELEASE-rpm" +echo "cp /boot/System.map-$KERNELRELEASE /boot/.System.map-$KERNELRELEASE-rpm" echo "rm -f /boot/vmlinuz-$KERNELRELEASE /boot/System.map-$KERNELRELEASE" -echo "/sbin/installkernel $KERNELRELEASE /boot/vmlinuz-$KERNELRELEASE-rpm /boot/System.map-$KERNELRELEASE-rpm" -echo "rm -f /boot/vmlinuz-$KERNELRELEASE-rpm /boot/System.map-$KERNELRELEASE-rpm" +echo "/sbin/installkernel $KERNELRELEASE /boot/.vmlinuz-$KERNELRELEASE-rpm /boot/.System.map-$KERNELRELEASE-rpm" +echo "rm -f /boot/.vmlinuz-$KERNELRELEASE-rpm /boot/.System.map-$KERNELRELEASE-rpm" echo "fi" echo "" echo "%files" diff --git a/sound/core/control.c b/sound/core/control.c index a85d45595d02..b4fe9b002512 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -160,6 +160,8 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, if (snd_BUG_ON(!card || !id)) return; + if (card->shutdown) + return; read_lock(&card->ctl_files_rwlock); #if IS_ENABLED(CONFIG_SND_MIXER_OSS) card->mixer_oss_change_count++; diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 019751a83e25..9e4743e833be 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -849,6 +849,22 @@ int snd_pcm_new_internal(struct snd_card *card, const char *id, int device, } EXPORT_SYMBOL(snd_pcm_new_internal); +static void free_pcm_kctl(struct snd_pcm_str *pstr) +{ + if (pstr->chmap_kctl) { + snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl); + pstr->chmap_kctl = NULL; + } + if (pstr->vol_kctl) { + snd_ctl_remove(pstr->pcm->card, pstr->vol_kctl); + pstr->vol_kctl = NULL; + } + if (pstr->usr_kctl) { + snd_ctl_remove(pstr->pcm->card, pstr->usr_kctl); + pstr->usr_kctl = NULL; + } +} + static void snd_pcm_free_stream(struct snd_pcm_str * pstr) { struct snd_pcm_substream *substream, *substream_next; @@ -871,6 +887,7 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr) kfree(setup); } #endif + free_pcm_kctl(pstr); if (pstr->substream_count) put_device(&pstr->dev); } @@ -1135,18 +1152,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) for (cidx = 0; cidx < 2; cidx++) { if (!pcm->internal) snd_unregister_device(&pcm->streams[cidx].dev); - if (pcm->streams[cidx].chmap_kctl) { - snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl); - pcm->streams[cidx].chmap_kctl = NULL; - } - if (pcm->streams[cidx].vol_kctl) { - snd_ctl_remove(pcm->card, pcm->streams[cidx].vol_kctl); - pcm->streams[cidx].vol_kctl = NULL; - } - if (pcm->streams[cidx].usr_kctl) { - snd_ctl_remove(pcm->card, pcm->streams[cidx].usr_kctl); - pcm->streams[cidx].usr_kctl = NULL; - } + free_pcm_kctl(&pcm->streams[cidx]); } mutex_unlock(&pcm->open_mutex); mutex_unlock(®ister_mutex); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index a41e481e99a3..328b4a43f09a 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -325,7 +325,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, char name[16]; snd_pcm_debug_name(substream, name, sizeof(name)); pcm_err(substream->pcm, - "BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n", + "invalid position: %s, pos = %ld, buffer size = %ld, period size = %ld\n", name, pos, runtime->buffer_size, runtime->period_size); } diff --git a/sound/core/timer.c b/sound/core/timer.c index f24c9fccf008..b982d1b089bd 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1051,8 +1051,8 @@ static int snd_timer_s_start(struct snd_timer * timer) njiff += timer->sticks - priv->correction; priv->correction = 0; } - priv->last_expires = priv->tlist.expires = njiff; - add_timer(&priv->tlist); + priv->last_expires = njiff; + mod_timer(&priv->tlist, njiff); return 0; } diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index c1c855a6c0af..a47e8ae0eb30 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -174,8 +174,12 @@ static void cs_automute(struct hda_codec *codec) snd_hda_gen_update_outputs(codec); if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) { - spec->gpio_data = spec->gen.hp_jack_present ? - spec->gpio_eapd_hp : spec->gpio_eapd_speaker; + if (spec->gen.automute_speaker) + spec->gpio_data = spec->gen.hp_jack_present ? + spec->gpio_eapd_hp : spec->gpio_eapd_speaker; + else + spec->gpio_data = + spec->gpio_eapd_hp | spec->gpio_eapd_speaker; snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data); } diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index ef198903c0c3..600af5878e75 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -204,8 +204,13 @@ static void cx_auto_reboot_notify(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - if (codec->core.vendor_id != 0x14f150f2) + switch (codec->core.vendor_id) { + case 0x14f150f2: /* CX20722 */ + case 0x14f150f4: /* CX20724 */ + break; + default: return; + } /* Turn the CX20722 codec into D3 to avoid spurious noises from the internal speaker during (and after) reboot */ diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 70c945603379..f7bcd8dbac14 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2353,6 +2353,10 @@ static void intel_pin_eld_notify(void *audio_ptr, int port) struct hda_codec *codec = audio_ptr; int pin_nid = port + 0x04; + /* we assume only from port-B to port-D */ + if (port < 1 || port > 3) + return; + /* skip notification during system suspend (but not in runtime PM); * the state will be updated at resume */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c2430b36e1ce..1402ba954b3d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3801,6 +3801,10 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, static void alc_headset_mode_default(struct hda_codec *codec) { + static struct coef_fw coef0225[] = { + UPDATE_COEF(0x45, 0x3f<<10, 0x34<<10), + {} + }; static struct coef_fw coef0255[] = { WRITE_COEF(0x45, 0xc089), WRITE_COEF(0x45, 0xc489), @@ -3842,6 +3846,9 @@ static void alc_headset_mode_default(struct hda_codec *codec) }; switch (codec->core.vendor_id) { + case 0x10ec0225: + alc_process_coef_fw(codec, coef0225); + break; case 0x10ec0255: case 0x10ec0256: alc_process_coef_fw(codec, coef0255); @@ -4750,6 +4757,10 @@ enum { ALC293_FIXUP_LENOVO_SPK_NOISE, ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, ALC255_FIXUP_DELL_SPK_NOISE, + ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC280_FIXUP_HP_HEADSET_MIC, + ALC221_FIXUP_HP_FRONT_MIC, + ALC292_FIXUP_TPT460, }; static const struct hda_fixup alc269_fixups[] = { @@ -5375,6 +5386,36 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE }, + [ALC225_FIXUP_DELL1_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Disable pass-through path for FRONT 14h */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x36 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x57d7 }, + {} + }, + .chained = true, + .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE + }, + [ALC280_FIXUP_HP_HEADSET_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_aamix, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC, + }, + [ALC221_FIXUP_HP_FRONT_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x02a19020 }, /* Front Mic */ + { } + }, + }, + [ALC292_FIXUP_TPT460] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_tpt440_dock, + .chained = true, + .chain_id = ALC293_FIXUP_LENOVO_SPK_NOISE, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -5479,6 +5520,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x2335, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2336, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2337, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x221c, "HP EliteBook 755 G2", ALC280_FIXUP_HP_HEADSET_MIC), + SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -5527,8 +5570,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x2218, "Thinkpad X1 Carbon 2nd", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE), + SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC292_FIXUP_TPT460), SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), + SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP), @@ -5621,6 +5665,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC283_FIXUP_SENSE_COMBO_JACK, .name = "alc283-sense-combo"}, {.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"}, {.id = ALC292_FIXUP_TPT440, .name = "tpt440"}, + {.id = ALC292_FIXUP_TPT460, .name = "tpt460"}, {} }; #define ALC225_STANDARD_PINS \ @@ -5647,10 +5692,10 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {0x21, 0x03211020} static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { - SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, + SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, ALC225_STANDARD_PINS, {0x14, 0x901701a0}), - SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, + SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, ALC225_STANDARD_PINS, {0x14, 0x901701b0}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, @@ -6378,6 +6423,7 @@ enum { ALC668_FIXUP_AUTO_MUTE, ALC668_FIXUP_DELL_DISABLE_AAMIX, ALC668_FIXUP_DELL_XPS13, + ALC662_FIXUP_ASUS_Nx50, }; static const struct hda_fixup alc662_fixups[] = { @@ -6618,6 +6664,12 @@ static const struct hda_fixup alc662_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_bass_chmap, }, + [ALC662_FIXUP_ASUS_Nx50] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_auto_mute_via_amp, + .chained = true, + .chain_id = ALC662_FIXUP_BASS_1A + }, }; static const struct snd_pci_quirk alc662_fixup_tbl[] = { @@ -6640,8 +6692,9 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0698, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), - SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_BASS_1A), + SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_ASUS_Nx50), SND_PCI_QUIRK(0x1043, 0x13df, "Asus N550JX", ALC662_FIXUP_BASS_1A), + SND_PCI_QUIRK(0x1043, 0x129d, "Asus N750", ALC662_FIXUP_ASUS_Nx50), SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16), SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16), diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 42bcbac801a3..ccdab29a8b66 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -2879,6 +2879,7 @@ static void intel8x0_measure_ac97_clock(struct intel8x0 *chip) static struct snd_pci_quirk intel8x0_clock_list[] = { SND_PCI_QUIRK(0x0e11, 0x008a, "AD1885", 41000), + SND_PCI_QUIRK(0x1014, 0x0581, "AD1981B", 48000), SND_PCI_QUIRK(0x1028, 0x00be, "AD1885", 44100), SND_PCI_QUIRK(0x1028, 0x0177, "AD1980", 48000), SND_PCI_QUIRK(0x1028, 0x01ad, "AD1981B", 48000), diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 871c2980f4f7..6fc326421108 100755 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -762,6 +762,13 @@ config SND_SOC_WCD_MBHC tristate default y if (SND_SOC_MSM8909_WCD=y || SND_SOC_MSM8X16_WCD=y || SND_SOC_WCD9335=y) && SND_SOC_MDMCALIFORNIUM!=y +config SND_SOC_WCD_DSP_MGR + tristate + +config SND_SOC_WCD_SPI + depends on CONFIG_SPI + tristate + config SND_SOC_WL1273 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index ed2e49ddf432..4b79b277ce29 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -143,6 +143,9 @@ snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o snd-soc-msm8952-wcd-objs := msm8x16-wcd.o msm8x16-wcd-tables.o snd-soc-wsa881x-analog-objs := wsa881x-analog.o wsa881x-tables-analog.o snd-soc-wsa881x-analog-objs += wsa881x-regmap-analog.o wsa881x-irq.o +snd-soc-wcd-dsp-utils-objs := wcd-dsp-utils.o +snd-soc-wcd-dsp-mgr-objs := wcd-dsp-mgr.o +snd-soc-wcd-spi-objs := wcd-spi.o snd-soc-wl1273-objs := wl1273.o snd-soc-wm-adsp-objs := wm_adsp.o snd-soc-wm0010-objs := wm0010.o @@ -351,6 +354,8 @@ obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o obj-$(CONFIG_SND_SOC_WSA881X_ANALOG) += snd-soc-wsa881x-analog.o obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o +obj-$(CONFIG_SND_SOC_WCD_DSP_MGR) += snd-soc-wcd-dsp-mgr.o snd-soc-wcd-dsp-utils.o +obj-$(CONFIG_SND_SOC_WCD_SPI) += snd-soc-wcd-spi.o obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o diff --git a/sound/soc/codecs/audio-ext-clk.c b/sound/soc/codecs/audio-ext-clk.c index 7faabcfb1db1..c422267dbf2c 100755 --- a/sound/soc/codecs/audio-ext-clk.c +++ b/sound/soc/codecs/audio-ext-clk.c @@ -91,6 +91,10 @@ static int audio_ext_clk2_prepare(struct clk *clk) struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info; int ret; + + if (!pnctrl_info->pinctrl || !pnctrl_info->active) + return 0; + ret = pinctrl_select_state(pnctrl_info->pinctrl, pnctrl_info->active); if (ret) { @@ -115,6 +119,9 @@ static void audio_ext_clk2_unprepare(struct clk *clk) struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info; int ret; + if (!pnctrl_info->pinctrl || !pnctrl_info->sleep) + return; + ret = pinctrl_select_state(pnctrl_info->pinctrl, pnctrl_info->sleep); if (ret) diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/sound/soc/codecs/wcd-dsp-mgr.c new file mode 100644 index 000000000000..69246ac9cc87 --- /dev/null +++ b/sound/soc/codecs/wcd-dsp-mgr.c @@ -0,0 +1,803 @@ +/* + * 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. + */ +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/stringify.h> +#include <linux/of.h> +#include <linux/component.h> +#include <sound/wcd-dsp-mgr.h> +#include "wcd-dsp-utils.h" + +/* Forward declarations */ +static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type); + +/* Component related macros */ +#define WDSP_GET_COMPONENT(wdsp, x) (&(wdsp->cmpnts[x])) +#define WDSP_GET_CMPNT_TYPE_STR(x) wdsp_get_cmpnt_type_string(x) + +/* + * These #defines indicate the bit number in status field + * for each of the status. If bit is set, it indicates + * the status as done, else if bit is not set, it indicates + * the status is either failed or not done. + */ +#define WDSP_STATUS_INITIALIZED BIT(0) +#define WDSP_STATUS_CODE_DLOADED BIT(1) +#define WDSP_STATUS_DATA_DLOADED BIT(2) +#define WDSP_STATUS_BOOTED BIT(3) + +/* Helper macros for printing wdsp messages */ +#define WDSP_ERR(wdsp, fmt, ...) \ + dev_err(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__) +#define WDSP_DBG(wdsp, fmt, ...) \ + dev_dbg(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__) + +/* Helper macros for locking */ +#define WDSP_MGR_MUTEX_LOCK(wdsp, lock) \ +{ \ + WDSP_DBG(wdsp, "mutex_lock(%s)", \ + __stringify_1(lock)); \ + mutex_lock(&lock); \ +} + +#define WDSP_MGR_MUTEX_UNLOCK(wdsp, lock) \ +{ \ + WDSP_DBG(wdsp, "mutex_unlock(%s)", \ + __stringify_1(lock)); \ + mutex_unlock(&lock); \ +} + +/* Helper macros for using status mask */ +#define WDSP_SET_STATUS(wdsp, state) \ +{ \ + wdsp->status |= state; \ + WDSP_DBG(wdsp, "set 0x%lx, new_state = 0x%x", \ + state, wdsp->status); \ +} + +#define WDSP_CLEAR_STATUS(wdsp, state) \ +{ \ + wdsp->status &= (~state); \ + WDSP_DBG(wdsp, "clear 0x%lx, new_state = 0x%x", \ + state, wdsp->status); \ +} + +#define WDSP_STATUS_IS_SET(wdsp, state) (wdsp->status & state) + +struct wdsp_cmpnt { + + /* OF node of the phandle */ + struct device_node *np; + + /* + * Child component's dev_name, should be set in DT for the child's + * phandle if child's dev->of_node does not match the phandle->of_node + */ + const char *cdev_name; + + /* Child component's device node */ + struct device *cdev; + + /* Private data that component may want back on callbacks */ + void *priv_data; + + /* Child ops */ + struct wdsp_cmpnt_ops *ops; +}; + +struct wdsp_mgr_priv { + + /* Manager driver's struct device pointer */ + struct device *mdev; + + /* Match struct for component framework */ + struct component_match *match; + + /* Manager's ops/function callbacks */ + struct wdsp_mgr_ops *ops; + + /* Array to store information for all expected components */ + struct wdsp_cmpnt cmpnts[WDSP_CMPNT_TYPE_MAX]; + + /* The filename of image to be downloaded */ + const char *img_fname; + + /* Keeps track of current state of manager driver */ + u32 status; + + /* Work to load the firmware image after component binding */ + struct work_struct load_fw_work; + + /* List of segments in image to be downloaded */ + struct list_head *seg_list; + + /* Base address of the image in memory */ + u32 base_addr; + + /* Instances using dsp */ + int dsp_users; + + /* Lock for serializing ops called by components */ + struct mutex api_mutex; +}; + +static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type) +{ + switch (type) { + case WDSP_CMPNT_CONTROL: + return "control"; + case WDSP_CMPNT_IPC: + return "ipc"; + case WDSP_CMPNT_TRANSPORT: + return "transport"; + default: + pr_err("%s: Invalid component type %d\n", + __func__, type); + return "Invalid"; + } +} + +static void wdsp_broadcast_event_upseq(struct wdsp_mgr_priv *wdsp, + enum wdsp_event_type event, + void *data) +{ + struct wdsp_cmpnt *cmpnt; + int i; + + for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) + cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data, + event, data); + } +} + +static void wdsp_broadcast_event_downseq(struct wdsp_mgr_priv *wdsp, + enum wdsp_event_type event, + void *data) +{ + struct wdsp_cmpnt *cmpnt; + int i; + + for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) + cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data, + event, data); + } +} + +static int wdsp_unicast_event(struct wdsp_mgr_priv *wdsp, + enum wdsp_cmpnt_type type, + enum wdsp_event_type event, + void *data) +{ + struct wdsp_cmpnt *cmpnt; + int ret; + + cmpnt = WDSP_GET_COMPONENT(wdsp, type); + if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) { + ret = cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data, + event, data); + } else { + WDSP_ERR(wdsp, "not valid event_handler for %s", + WDSP_GET_CMPNT_TYPE_STR(type)); + ret = -EINVAL; + } + + return ret; +} + +static int wdsp_init_components(struct wdsp_mgr_priv *wdsp) +{ + struct wdsp_cmpnt *cmpnt; + int fail_idx = WDSP_CMPNT_TYPE_MAX; + int i, ret = 0; + + for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) { + + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + + /* Init is allowed to be NULL */ + if (!cmpnt->ops || !cmpnt->ops->init) + continue; + ret = cmpnt->ops->init(cmpnt->cdev, cmpnt->priv_data); + if (ret) { + WDSP_ERR(wdsp, "Init failed (%d) for component %s", + ret, WDSP_GET_CMPNT_TYPE_STR(i)); + fail_idx = i; + break; + } + } + + if (fail_idx < WDSP_CMPNT_TYPE_MAX) { + /* Undo init for already initialized components */ + for (i = fail_idx - 1; i >= 0; i--) { + struct wdsp_cmpnt *cmpnt = WDSP_GET_COMPONENT(wdsp, i); + + if (cmpnt->ops && cmpnt->ops->deinit) + cmpnt->ops->deinit(cmpnt->cdev, + cmpnt->priv_data); + } + } + + return ret; +} + +static int wdsp_load_each_segment(struct wdsp_mgr_priv *wdsp, + struct wdsp_img_segment *seg) +{ + struct wdsp_img_section img_section; + int ret; + + WDSP_DBG(wdsp, + "base_addr 0x%x, split_fname %s, load_addr 0x%x, size 0x%zx", + wdsp->base_addr, seg->split_fname, seg->load_addr, seg->size); + + if (seg->load_addr < wdsp->base_addr) { + WDSP_ERR(wdsp, "Invalid addr 0x%x, base_addr = 0x%x", + seg->load_addr, wdsp->base_addr); + return -EINVAL; + } + + img_section.addr = seg->load_addr - wdsp->base_addr; + img_section.size = seg->size; + img_section.data = seg->data; + + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT, + WDSP_EVENT_DLOAD_SECTION, + &img_section); + if (IS_ERR_VALUE(ret)) + WDSP_ERR(wdsp, + "Failed, err = %d for base_addr = 0x%x split_fname = %s, load_addr = 0x%x, size = 0x%zx", + ret, wdsp->base_addr, seg->split_fname, + seg->load_addr, seg->size); + return ret; +} + +static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp, + unsigned int type) +{ + struct wdsp_cmpnt *ctl; + struct wdsp_img_segment *seg = NULL; + enum wdsp_event_type pre, post; + int ret; + + ctl = WDSP_GET_COMPONENT(wdsp, WDSP_CMPNT_CONTROL); + + if (type == WDSP_ELF_FLAG_RE) { + pre = WDSP_EVENT_PRE_DLOAD_CODE; + post = WDSP_EVENT_POST_DLOAD_CODE; + } else if (type == WDSP_ELF_FLAG_WRITE) { + pre = WDSP_EVENT_PRE_DLOAD_DATA; + post = WDSP_EVENT_POST_DLOAD_DATA; + } else { + WDSP_ERR(wdsp, "Invalid type %u", type); + return -EINVAL; + } + + ret = wdsp_get_segment_list(ctl->cdev, wdsp->img_fname, + type, wdsp->seg_list, &wdsp->base_addr); + if (IS_ERR_VALUE(ret) || + list_empty(wdsp->seg_list)) { + WDSP_ERR(wdsp, "Error %d to get image segments for type %d", + ret, type); + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_DLOAD_FAILED, + NULL); + goto done; + } + + /* Notify all components that image is about to be downloaded */ + wdsp_broadcast_event_upseq(wdsp, pre, NULL); + + /* Go through the list of segments and download one by one */ + list_for_each_entry(seg, wdsp->seg_list, list) { + ret = wdsp_load_each_segment(wdsp, seg); + if (IS_ERR_VALUE(ret)) { + wdsp_broadcast_event_downseq(wdsp, + WDSP_EVENT_DLOAD_FAILED, + NULL); + goto dload_error; + } + } + + /* Notify all components that image is downloaded */ + wdsp_broadcast_event_downseq(wdsp, post, NULL); + +dload_error: + wdsp_flush_segment_list(wdsp->seg_list); +done: + return ret; +} + +static void wdsp_load_fw_image(struct work_struct *work) +{ + struct wdsp_mgr_priv *wdsp; + struct wdsp_cmpnt *cmpnt; + int ret, idx; + + wdsp = container_of(work, struct wdsp_mgr_priv, load_fw_work); + if (!wdsp) { + pr_err("%s: Invalid private_data\n", __func__); + goto done; + } + + /* Initialize the components first */ + ret = wdsp_init_components(wdsp); + if (IS_ERR_VALUE(ret)) + goto done; + + /* Set init done status */ + WDSP_SET_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + + /* Download the read-execute sections of image */ + ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_RE); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Error %d to download code sections", ret); + for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, idx); + if (cmpnt->ops && cmpnt->ops->deinit) + cmpnt->ops->deinit(cmpnt->cdev, + cmpnt->priv_data); + } + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + } + + WDSP_SET_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED); +done: + return; +} + +static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp) +{ + int ret; + + /* Make sure wdsp is in good state */ + if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_CODE_DLOADED)) { + WDSP_ERR(wdsp, "WDSP in invalid state 0x%x", wdsp->status); + ret = -EINVAL; + goto done; + } + + /* Download the read-write sections of image */ + ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_WRITE); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Data section download failed, err = %d", ret); + goto done; + } + + WDSP_SET_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); + + wdsp_broadcast_event_upseq(wdsp, WDSP_EVENT_PRE_BOOTUP, NULL); + + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL, + WDSP_EVENT_DO_BOOT, NULL); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Failed to boot dsp, err = %d", ret); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); + goto done; + } + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_BOOTUP, NULL); + WDSP_SET_STATUS(wdsp, WDSP_STATUS_BOOTED); +done: + return ret; +} + +static int wdsp_disable_dsp(struct wdsp_mgr_priv *wdsp) +{ + int ret; + + /* Make sure wdsp is in good state */ + if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) { + WDSP_ERR(wdsp, "wdsp in invalid state 0x%x", wdsp->status); + ret = -EINVAL; + goto done; + } + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN, NULL); + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL, + WDSP_EVENT_DO_SHUTDOWN, NULL); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Failed to shutdown dsp, err = %d", ret); + goto done; + } + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN, NULL); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED); + + /* Data sections are to be downloaded per boot */ + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); +done: + return ret; +} + +static int wdsp_register_cmpnt_ops(struct device *wdsp_dev, + struct device *cdev, + void *priv_data, + struct wdsp_cmpnt_ops *ops) +{ + struct wdsp_mgr_priv *wdsp; + struct wdsp_cmpnt *cmpnt; + int i, ret; + + if (!wdsp_dev || !cdev || !ops) + return -EINVAL; + + wdsp = dev_get_drvdata(wdsp_dev); + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); + + for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if ((cdev->of_node && cdev->of_node == cmpnt->np) || + (cmpnt->cdev_name && + !strcmp(dev_name(cdev), cmpnt->cdev_name))) { + break; + } + } + + if (i == WDSP_CMPNT_TYPE_MAX) { + WDSP_ERR(wdsp, "Failed to register component dev %s", + dev_name(cdev)); + ret = -EINVAL; + goto done; + } + + cmpnt->cdev = cdev; + cmpnt->ops = ops; + cmpnt->priv_data = priv_data; +done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); + return 0; +} + +static struct device *wdsp_get_dev_for_cmpnt(struct device *wdsp_dev, + enum wdsp_cmpnt_type type) +{ + struct wdsp_mgr_priv *wdsp; + struct wdsp_cmpnt *cmpnt; + + if (!wdsp_dev || type >= WDSP_CMPNT_TYPE_MAX) + return NULL; + + wdsp = dev_get_drvdata(wdsp_dev); + cmpnt = WDSP_GET_COMPONENT(wdsp, type); + + return cmpnt->cdev; +} + +static int wdsp_intr_handler(struct device *wdsp_dev, + enum wdsp_intr intr) +{ + struct wdsp_mgr_priv *wdsp; + int ret; + + if (!wdsp_dev) + return -EINVAL; + + wdsp = dev_get_drvdata(wdsp_dev); + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); + + switch (intr) { + case WDSP_IPC1_INTR: + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_IPC, + WDSP_EVENT_IPC1_INTR, NULL); + break; + default: + ret = -EINVAL; + break; + } + + if (IS_ERR_VALUE(ret)) + WDSP_ERR(wdsp, "handling intr %d failed with error %d", + intr, ret); + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); + + return ret; +} + +static int wdsp_vote_for_dsp(struct device *wdsp_dev, + bool vote) +{ + struct wdsp_mgr_priv *wdsp; + int ret = 0; + + if (!wdsp_dev) + return -EINVAL; + + wdsp = dev_get_drvdata(wdsp_dev); + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); + WDSP_DBG(wdsp, "request %s, current users = %d", + vote ? "enable" : "disable", wdsp->dsp_users); + + if (vote) { + wdsp->dsp_users++; + if (wdsp->dsp_users == 1) + ret = wdsp_enable_dsp(wdsp); + } else { + if (wdsp->dsp_users == 0) + goto done; + + wdsp->dsp_users--; + if (wdsp->dsp_users == 0) + ret = wdsp_disable_dsp(wdsp); + } + + if (IS_ERR_VALUE(ret)) + WDSP_DBG(wdsp, "wdsp %s failed, err = %d", + vote ? "enable" : "disable", ret); + +done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); + return ret; +} + +static int wdsp_suspend(struct device *wdsp_dev) +{ + return 0; +} + +static int wdsp_resume(struct device *wdsp_dev) +{ + return 0; +} + +static struct wdsp_mgr_ops wdsp_ops = { + .register_cmpnt_ops = wdsp_register_cmpnt_ops, + .get_dev_for_cmpnt = wdsp_get_dev_for_cmpnt, + .intr_handler = wdsp_intr_handler, + .vote_for_dsp = wdsp_vote_for_dsp, + .suspend = wdsp_suspend, + .resume = wdsp_resume, +}; + +static int wdsp_mgr_compare_of(struct device *dev, void *data) +{ + struct wdsp_cmpnt *cmpnt = data; + + /* + * First try to match based on of_node, if of_node is not + * present, try to match on the dev_name + */ + return ((dev->of_node && dev->of_node == cmpnt->np) || + (cmpnt->cdev_name && + !strcmp(dev_name(dev), cmpnt->cdev_name))); +} + +static int wdsp_mgr_bind(struct device *dev) +{ + struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev); + struct wdsp_cmpnt *cmpnt; + int ret, idx; + + wdsp->ops = &wdsp_ops; + + ret = component_bind_all(dev, wdsp->ops); + if (IS_ERR_VALUE(ret)) + WDSP_ERR(wdsp, "component_bind_all failed %d\n", ret); + + /* Make sure all components registered ops */ + for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, idx); + if (!cmpnt->cdev || !cmpnt->ops) { + WDSP_ERR(wdsp, "%s did not register ops\n", + WDSP_GET_CMPNT_TYPE_STR(idx)); + ret = -EINVAL; + component_unbind_all(dev, wdsp->ops); + break; + } + } + + /* Schedule the work to download image if binding was successful. */ + if (!ret) + schedule_work(&wdsp->load_fw_work); + + return ret; +} + +static void wdsp_mgr_unbind(struct device *dev) +{ + struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev); + struct wdsp_cmpnt *cmpnt; + int idx; + + component_unbind_all(dev, wdsp->ops); + + /* Clear all status bits */ + wdsp->status = 0x00; + + /* clean up the components */ + for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, idx); + cmpnt->cdev = NULL; + cmpnt->ops = NULL; + cmpnt->priv_data = NULL; + } +} + +static const struct component_master_ops wdsp_master_ops = { + .bind = wdsp_mgr_bind, + .unbind = wdsp_mgr_unbind, +}; + +static void *wdsp_mgr_parse_phandle(struct wdsp_mgr_priv *wdsp, + int index) +{ + struct device *mdev = wdsp->mdev; + struct device_node *np; + struct wdsp_cmpnt *cmpnt = NULL; + struct of_phandle_args pargs; + u32 value; + int ret; + + ret = of_parse_phandle_with_fixed_args(mdev->of_node, + "qcom,wdsp-components", 1, + index, &pargs); + if (ret) { + WDSP_ERR(wdsp, "parse_phandle at index %d failed %d", + index, ret); + return NULL; + } + + np = pargs.np; + value = pargs.args[0]; + + if (value >= WDSP_CMPNT_TYPE_MAX) { + WDSP_ERR(wdsp, "invalid phandle_arg to of_node %s", np->name); + goto done; + } + + cmpnt = WDSP_GET_COMPONENT(wdsp, value); + if (cmpnt->np || cmpnt->cdev_name) { + WDSP_ERR(wdsp, "cmpnt %d already added", value); + cmpnt = NULL; + goto done; + } + + cmpnt->np = np; + of_property_read_string(np, "qcom,wdsp-cmpnt-dev-name", + &cmpnt->cdev_name); +done: + of_node_put(np); + return cmpnt; +} + +static int wdsp_mgr_parse_dt_entries(struct wdsp_mgr_priv *wdsp) +{ + struct device *dev = wdsp->mdev; + void *match_data; + int ph_idx, ret; + + ret = of_property_read_string(dev->of_node, "qcom,img-filename", + &wdsp->img_fname); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Reading property %s failed, error = %d", + "qcom,img-filename", ret); + return ret; + } + + ret = of_count_phandle_with_args(dev->of_node, + "qcom,wdsp-components", + NULL); + if (ret == -ENOENT) { + WDSP_ERR(wdsp, "Property %s not defined in DT", + "qcom,wdsp-components"); + goto done; + } else if (ret != WDSP_CMPNT_TYPE_MAX * 2) { + WDSP_ERR(wdsp, "Invalid phandle + arg count %d, expected %d", + ret, WDSP_CMPNT_TYPE_MAX * 2); + ret = -EINVAL; + goto done; + } + + ret = 0; + + for (ph_idx = 0; ph_idx < WDSP_CMPNT_TYPE_MAX; ph_idx++) { + + match_data = wdsp_mgr_parse_phandle(wdsp, ph_idx); + if (!match_data) { + WDSP_ERR(wdsp, "component not found at idx %d", ph_idx); + ret = -EINVAL; + goto done; + } + + component_match_add(dev, &wdsp->match, + wdsp_mgr_compare_of, match_data); + } + +done: + return ret; +} + +static int wdsp_mgr_probe(struct platform_device *pdev) +{ + struct wdsp_mgr_priv *wdsp; + struct device *mdev = &pdev->dev; + int ret; + + wdsp = devm_kzalloc(mdev, sizeof(*wdsp), GFP_KERNEL); + if (!wdsp) + return -ENOMEM; + wdsp->mdev = mdev; + wdsp->seg_list = devm_kzalloc(mdev, sizeof(struct list_head), + GFP_KERNEL); + if (!wdsp->seg_list) { + devm_kfree(mdev, wdsp); + return -ENOMEM; + } + + ret = wdsp_mgr_parse_dt_entries(wdsp); + if (ret) + goto err_dt_parse; + + INIT_WORK(&wdsp->load_fw_work, wdsp_load_fw_image); + INIT_LIST_HEAD(wdsp->seg_list); + mutex_init(&wdsp->api_mutex); + dev_set_drvdata(mdev, wdsp); + + ret = component_master_add_with_match(mdev, &wdsp_master_ops, + wdsp->match); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Failed to add master, err = %d", ret); + goto err_master_add; + } + + return 0; + +err_master_add: + mutex_destroy(&wdsp->api_mutex); +err_dt_parse: + devm_kfree(mdev, wdsp->seg_list); + devm_kfree(mdev, wdsp); + dev_set_drvdata(mdev, NULL); + + return ret; +} + +static int wdsp_mgr_remove(struct platform_device *pdev) +{ + struct device *mdev = &pdev->dev; + struct wdsp_mgr_priv *wdsp = dev_get_drvdata(mdev); + + component_master_del(mdev, &wdsp_master_ops); + + mutex_destroy(&wdsp->api_mutex); + devm_kfree(mdev, wdsp->seg_list); + devm_kfree(mdev, wdsp); + dev_set_drvdata(mdev, NULL); + + return 0; +}; + +static const struct of_device_id wdsp_mgr_dt_match[] = { + {.compatible = "qcom,wcd-dsp-mgr" }, + { } +}; + +static struct platform_driver wdsp_mgr_driver = { + .driver = { + .name = "wcd-dsp-mgr", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(wdsp_mgr_dt_match), + }, + .probe = wdsp_mgr_probe, + .remove = wdsp_mgr_remove, +}; +module_platform_driver(wdsp_mgr_driver); + +MODULE_DESCRIPTION("WCD DSP manager driver"); +MODULE_DEVICE_TABLE(of, wdsp_mgr_dt_match); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd-dsp-utils.c b/sound/soc/codecs/wcd-dsp-utils.c new file mode 100644 index 000000000000..1f048917a6c8 --- /dev/null +++ b/sound/soc/codecs/wcd-dsp-utils.c @@ -0,0 +1,221 @@ +/* + * 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. + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/firmware.h> +#include <linux/elf.h> +#include <linux/slab.h> +#include <linux/list.h> +#include "wcd-dsp-utils.h" + +static bool wdsp_is_valid_elf_hdr(const struct elf32_hdr *ehdr, + size_t fw_size) +{ + if (fw_size < sizeof(*ehdr)) { + pr_err("%s: Firmware too small\n", __func__); + goto elf_check_fail; + } + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) { + pr_err("%s: Not an ELF file\n", __func__); + goto elf_check_fail; + } + + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) { + pr_err("%s: Not an executable image\n", __func__); + goto elf_check_fail; + } + + if (ehdr->e_phnum == 0) { + pr_err("%s: no segments to load\n", __func__); + goto elf_check_fail; + } + + if (sizeof(struct elf32_phdr) * ehdr->e_phnum + + sizeof(struct elf32_hdr) > fw_size) { + pr_err("%s: Too small MDT file\n", __func__); + goto elf_check_fail; + } + + return true; + +elf_check_fail: + return false; +} + +static int wdsp_add_segment_to_list(struct device *dev, + const char *img_fname, + const struct elf32_phdr *phdr, + int phdr_idx, + struct list_head *seg_list) +{ + struct wdsp_img_segment *seg; + int ret = 0; + + /* Do not load segments with zero size */ + if (phdr->p_filesz == 0 || phdr->p_memsz == 0) + goto done; + + seg = kzalloc(sizeof(*seg), GFP_KERNEL); + if (!seg) { + ret = -ENOMEM; + goto done; + } + + snprintf(seg->split_fname, sizeof(seg->split_fname), + "%s.b%02d", img_fname, phdr_idx); + ret = request_firmware(&seg->split_fw, seg->split_fname, dev); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "%s: firmware %s not found\n", + __func__, seg->split_fname); + goto bad_seg; + } + + seg->load_addr = phdr->p_paddr; + seg->size = phdr->p_filesz; + seg->data = (u8 *) seg->split_fw->data; + + list_add_tail(&seg->list, seg_list); +done: + return ret; +bad_seg: + kfree(seg); + return ret; +} + +/* + * wdsp_flush_segment_list: Flush the list of segments + * @seg_list: List of segments to be flushed + * This API will traverse through the list of segments provided in + * seg_list, release the firmware for each segment and delete the + * segment from the list. + */ +void wdsp_flush_segment_list(struct list_head *seg_list) +{ + struct wdsp_img_segment *seg, *next; + + list_for_each_entry_safe(seg, next, seg_list, list) { + release_firmware(seg->split_fw); + list_del(&seg->list); + kfree(seg); + } +} +EXPORT_SYMBOL(wdsp_flush_segment_list); + +/* + * wdsp_get_segment_list: Get the list of requested segments + * @dev: struct device pointer of caller + * @img_fname: Image name for the mdt and split firmware files + * @segment_type: Requested segment type, should be either + * WDSP_ELF_FLAG_RE or WDSP_ELF_FLAG_WRITE + * @seg_list: An initialized head for list of segmented to be returned + * @entry_point: Pointer to return the entry point of the image + * This API will parse the mdt file for img_fname and create + * an struct wdsp_img_segment for each segment that matches segment_type + * and add this structure to list pointed by seg_list + */ +int wdsp_get_segment_list(struct device *dev, + const char *img_fname, + unsigned int segment_type, + struct list_head *seg_list, + u32 *entry_point) +{ + const struct firmware *fw; + const struct elf32_hdr *ehdr; + const struct elf32_phdr *phdr; + const u8 *elf_ptr; + char mdt_name[WDSP_IMG_NAME_LEN_MAX]; + int ret, phdr_idx; + bool segment_match; + + if (!dev) { + ret = -EINVAL; + pr_err("%s: Invalid device handle\n", __func__); + goto done; + } + + if (!img_fname || !seg_list || !entry_point) { + ret = -EINVAL; + dev_err(dev, "%s: Invalid input params\n", + __func__); + goto done; + } + + if (segment_type != WDSP_ELF_FLAG_RE && + segment_type != WDSP_ELF_FLAG_WRITE) { + dev_err(dev, "%s: Invalid request for segment_type %d\n", + __func__, segment_type); + ret = -EINVAL; + goto done; + } + + snprintf(mdt_name, sizeof(mdt_name), "%s.mdt", img_fname); + ret = request_firmware(&fw, mdt_name, dev); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "%s: firmware %s not found\n", + __func__, mdt_name); + goto done; + } + + ehdr = (struct elf32_hdr *) fw->data; + *entry_point = ehdr->e_entry; + if (!wdsp_is_valid_elf_hdr(ehdr, fw->size)) { + dev_err(dev, "%s: fw mdt %s is invalid\n", + __func__, mdt_name); + ret = -EINVAL; + goto bad_elf; + } + + elf_ptr = fw->data + sizeof(*ehdr); + for (phdr_idx = 0; phdr_idx < ehdr->e_phnum; phdr_idx++) { + phdr = (struct elf32_phdr *) elf_ptr; + segment_match = false; + + switch (segment_type) { + case WDSP_ELF_FLAG_RE: + /* + * Flag can be READ or EXECUTE or both but + * WRITE flag should not be set. + */ + if ((phdr->p_flags & segment_type) && + !(phdr->p_flags & WDSP_ELF_FLAG_WRITE)) + segment_match = true; + break; + case WDSP_ELF_FLAG_WRITE: + /* + * If WRITE flag is set, other flags do not + * matter. + */ + if (phdr->p_flags & segment_type) + segment_match = true; + break; + } + + if (segment_match) { + ret = wdsp_add_segment_to_list(dev, img_fname, phdr, + phdr_idx, seg_list); + if (IS_ERR_VALUE(ret)) { + wdsp_flush_segment_list(seg_list); + goto bad_elf; + } + } + elf_ptr = elf_ptr + sizeof(*phdr); + } + +bad_elf: + release_firmware(fw); +done: + return ret; +} +EXPORT_SYMBOL(wdsp_get_segment_list); diff --git a/sound/soc/codecs/wcd-dsp-utils.h b/sound/soc/codecs/wcd-dsp-utils.h new file mode 100644 index 000000000000..81842f77260e --- /dev/null +++ b/sound/soc/codecs/wcd-dsp-utils.h @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#ifndef __WCD_DSP_UTILS_H__ +#define __WCD_DSP_UTILS_H__ + +#define WDSP_IMG_NAME_LEN_MAX 64 + +#define WDSP_ELF_FLAG_EXECUTE (1 << 0) +#define WDSP_ELF_FLAG_WRITE (1 << 1) +#define WDSP_ELF_FLAG_READ (1 << 2) + +#define WDSP_ELF_FLAG_RE (WDSP_ELF_FLAG_READ | WDSP_ELF_FLAG_EXECUTE) + +struct wdsp_img_segment { + + /* Firmware for the slit image */ + const struct firmware *split_fw; + + /* Name of the split firmware file */ + char split_fname[WDSP_IMG_NAME_LEN_MAX]; + + /* Address where the segment is to be loaded */ + u32 load_addr; + + /* Buffer to hold the data to be loaded */ + u8 *data; + + /* Size of the data to be loaded */ + size_t size; + + /* List node pointing to next segment */ + struct list_head list; +}; + +int wdsp_get_segment_list(struct device *, const char *, + unsigned int, struct list_head *, + u32 *); +void wdsp_flush_segment_list(struct list_head *); + +#endif /* __WCD_DSP_UTILS_H__ */ diff --git a/sound/soc/codecs/wcd-spi-registers.h b/sound/soc/codecs/wcd-spi-registers.h new file mode 100644 index 000000000000..4e579696cc49 --- /dev/null +++ b/sound/soc/codecs/wcd-spi-registers.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#ifndef __WCD_SPI_REGISTERS_H__ +#define __WCD_SPI_REGISTERS_H__ + +#include <linux/regmap.h> + +#define WCD_SPI_SLAVE_SANITY (0x00) +#define WCD_SPI_SLAVE_DEVICE_ID (0x04) +#define WCD_SPI_SLAVE_STATUS (0x08) +#define WCD_SPI_SLAVE_CONFIG (0x0c) +#define WCD_SPI_SLAVE_SW_RESET (0x10) +#define WCD_SPI_SLAVE_IRQ_STATUS (0x14) +#define WCD_SPI_SLAVE_IRQ_EN (0x18) +#define WCD_SPI_SLAVE_IRQ_CLR (0x1c) +#define WCD_SPI_SLAVE_IRQ_FORCE (0x20) +#define WCD_SPI_SLAVE_TX (0x24) +#define WCD_SPI_SLAVE_TEST_BUS_DATA (0x2c) +#define WCD_SPI_SLAVE_TEST_BUS_CTRL (0x30) +#define WCD_SPI_SLAVE_SW_RST_IRQ (0x34) +#define WCD_SPI_SLAVE_CHAR_CFG (0x38) +#define WCD_SPI_SLAVE_CHAR_DATA_MOSI (0x3c) +#define WCD_SPI_SLAVE_CHAR_DATA_CS_N (0x40) +#define WCD_SPI_SLAVE_CHAR_DATA_MISO (0x44) +#define WCD_SPI_SLAVE_TRNS_BYTE_CNT (0x4c) +#define WCD_SPI_SLAVE_TRNS_LEN (0x50) +#define WCD_SPI_SLAVE_FIFO_LEVEL (0x54) +#define WCD_SPI_SLAVE_GENERICS (0x58) +#define WCD_SPI_SLAVE_EXT_BASE_ADDR (0x5c) +#define WCD_SPI_MAX_REGISTER (0x5F) + +#endif /* End __WCD_SPI_REGISTERS_H__ */ diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c new file mode 100644 index 000000000000..3049d87c6c05 --- /dev/null +++ b/sound/soc/codecs/wcd-spi.c @@ -0,0 +1,1250 @@ +/* + * 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. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/bitops.h> +#include <linux/spi/spi.h> +#include <linux/regmap.h> +#include <linux/component.h> +#include <linux/ratelimit.h> +#include <sound/wcd-dsp-mgr.h> +#include <sound/wcd-spi.h> +#include "wcd-spi-registers.h" + +/* Byte manipulations */ +#define SHIFT_1_BYTES (8) +#define SHIFT_2_BYTES (16) +#define SHIFT_3_BYTES (24) + +/* Command opcodes */ +#define WCD_SPI_CMD_NOP (0x00) +#define WCD_SPI_CMD_WREN (0x06) +#define WCD_SPI_CMD_CLKREQ (0xDA) +#define WCD_SPI_CMD_RDSR (0x05) +#define WCD_SPI_CMD_IRR (0x81) +#define WCD_SPI_CMD_IRW (0x82) +#define WCD_SPI_CMD_MIOR (0x83) +#define WCD_SPI_CMD_FREAD (0x0B) +#define WCD_SPI_CMD_MIOW (0x02) +#define WCD_SPI_WRITE_FRAME_OPCODE \ + (WCD_SPI_CMD_MIOW << SHIFT_3_BYTES) +#define WCD_SPI_READ_FRAME_OPCODE \ + (WCD_SPI_CMD_MIOR << SHIFT_3_BYTES) +#define WCD_SPI_FREAD_FRAME_OPCODE \ + (WCD_SPI_CMD_FREAD << SHIFT_3_BYTES) + +/* Command lengths */ +#define WCD_SPI_OPCODE_LEN (0x01) +#define WCD_SPI_CMD_NOP_LEN (0x01) +#define WCD_SPI_CMD_WREN_LEN (0x01) +#define WCD_SPI_CMD_CLKREQ_LEN (0x04) +#define WCD_SPI_CMD_IRR_LEN (0x04) +#define WCD_SPI_CMD_IRW_LEN (0x06) +#define WCD_SPI_WRITE_SINGLE_LEN (0x08) +#define WCD_SPI_READ_SINGLE_LEN (0x13) +#define WCD_SPI_CMD_FREAD_LEN (0x13) + +/* Command delays */ +#define WCD_SPI_CLKREQ_DELAY_USECS (500) +#define WCD_SPI_CLK_OFF_TIMER_MS (3000) + +/* Command masks */ +#define WCD_CMD_ADDR_MASK \ + (0xFF | \ + (0xFF << SHIFT_1_BYTES) | \ + (0xFF << SHIFT_2_BYTES)) + +/* Clock ctrl request related */ +#define WCD_SPI_CLK_ENABLE true +#define WCD_SPI_CLK_DISABLE false +#define WCD_SPI_CLK_FLAG_DELAYED (1 << 0) +#define WCD_SPI_CLK_FLAG_IMMEDIATE (1 << 1) + +/* Internal addresses */ +#define WCD_SPI_ADDR_IPC_CTL_HOST (0x012014) + +/* Word sizes and min/max lengths */ +#define WCD_SPI_WORD_BYTE_CNT (4) +#define WCD_SPI_RW_MULTI_MIN_LEN (16) +#define WCD_SPI_RW_MULTI_MAX_LEN (64 * 1024) + +/* Alignment requirements */ +#define WCD_SPI_RW_MIN_ALIGN WCD_SPI_WORD_BYTE_CNT +#define WCD_SPI_RW_MULTI_ALIGN (16) + +/* Status mask bits */ +#define WCD_SPI_CLK_STATE_ENABLED BIT(0) + +/* Locking related */ +#define WCD_SPI_MUTEX_LOCK(spi, lock) \ +{ \ + dev_vdbg(&spi->dev, "%s: mutex_lock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_lock(&lock); \ +} + +#define WCD_SPI_MUTEX_UNLOCK(spi, lock) \ +{ \ + dev_vdbg(&spi->dev, "%s: mutex_unlock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_unlock(&lock); \ +} + +struct wcd_spi_priv { + struct spi_device *spi; + u32 mem_base_addr; + + struct regmap *regmap; + + /* Message for single transfer */ + struct spi_message msg1; + struct spi_transfer xfer1; + + /* Message for two transfers */ + struct spi_message msg2; + struct spi_transfer xfer2[2]; + + /* Register access related */ + u32 reg_bytes; + u32 val_bytes; + + /* Clock requests related */ + struct mutex clk_mutex; + int clk_users; + unsigned long status_mask; + struct delayed_work clk_dwork; + + /* Transaction related */ + struct mutex xfer_mutex; + + struct device *m_dev; + struct wdsp_mgr_ops *m_ops; +}; + +enum xfer_request { + WCD_SPI_XFER_WRITE, + WCD_SPI_XFER_READ, +}; + + +static char *wcd_spi_xfer_req_str(enum xfer_request req) +{ + if (req == WCD_SPI_XFER_WRITE) + return "xfer_write"; + else if (req == WCD_SPI_XFER_READ) + return "xfer_read"; + else + return "xfer_invalid"; +} + +static void wcd_spi_reinit_xfer(struct spi_transfer *xfer) +{ + xfer->tx_buf = NULL; + xfer->rx_buf = NULL; + xfer->delay_usecs = 0; + xfer->len = 0; +} + +static int wcd_spi_read_single(struct spi_device *spi, + u32 remote_addr, u32 *val) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0]; + struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1]; + u8 *tx_buf; + u32 frame = 0; + int ret; + + dev_dbg(&spi->dev, "%s: remote_addr = 0x%x\n", + __func__, remote_addr); + + tx_buf = kzalloc(WCD_SPI_READ_SINGLE_LEN, + GFP_KERNEL | GFP_DMA); + if (!tx_buf) + return -ENOMEM; + + frame |= WCD_SPI_READ_FRAME_OPCODE; + frame |= remote_addr & WCD_CMD_ADDR_MASK; + + wcd_spi_reinit_xfer(tx_xfer); + frame = cpu_to_be32(frame); + memcpy(tx_buf, &frame, sizeof(frame)); + tx_xfer->tx_buf = tx_buf; + tx_xfer->len = WCD_SPI_READ_SINGLE_LEN; + + wcd_spi_reinit_xfer(rx_xfer); + rx_xfer->rx_buf = val; + rx_xfer->len = sizeof(*val); + + ret = spi_sync(spi, &wcd_spi->msg2); + kfree(tx_buf); + + return ret; +} + +static int wcd_spi_read_multi(struct spi_device *spi, + u32 remote_addr, u8 *data, + size_t len) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u8 *tx_buf; + u8 *rx_buf; + u32 frame = 0; + int ret; + + dev_dbg(&spi->dev, "%s: addr 0x%x, len = %zd\n", + __func__, remote_addr, len); + + frame |= WCD_SPI_FREAD_FRAME_OPCODE; + frame |= remote_addr & WCD_CMD_ADDR_MASK; + + tx_buf = kzalloc(WCD_SPI_CMD_FREAD_LEN + len, + GFP_KERNEL | GFP_DMA); + if (!tx_buf) + return -ENOMEM; + + rx_buf = kzalloc(WCD_SPI_CMD_FREAD_LEN + len, + GFP_KERNEL | GFP_DMA); + if (!rx_buf) { + kfree(tx_buf); + return -ENOMEM; + } + + wcd_spi_reinit_xfer(xfer); + frame = cpu_to_be32(frame); + memcpy(tx_buf, &frame, sizeof(frame)); + xfer->tx_buf = tx_buf; + xfer->rx_buf = rx_buf; + xfer->len = WCD_SPI_CMD_FREAD_LEN + len; + + ret = spi_sync(spi, &wcd_spi->msg1); + if (ret) { + dev_err(&spi->dev, "%s: failed, err = %d\n", + __func__, ret); + goto done; + } + + memcpy(data, rx_buf + WCD_SPI_CMD_FREAD_LEN, len); +done: + kfree(tx_buf); + kfree(rx_buf); + return ret; +} + +static int wcd_spi_write_single(struct spi_device *spi, + u32 remote_addr, u32 val) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u8 buf[WCD_SPI_WRITE_SINGLE_LEN]; + u32 frame = 0; + + dev_dbg(&spi->dev, "%s: remote_addr = 0x%x, val = 0x%x\n", + __func__, remote_addr, val); + + memset(buf, 0, WCD_SPI_WRITE_SINGLE_LEN); + frame |= WCD_SPI_WRITE_FRAME_OPCODE; + frame |= (remote_addr & WCD_CMD_ADDR_MASK); + + frame = cpu_to_be32(frame); + memcpy(buf, &frame, sizeof(frame)); + memcpy(buf + sizeof(frame), &val, sizeof(val)); + + wcd_spi_reinit_xfer(xfer); + xfer->tx_buf = buf; + xfer->len = WCD_SPI_WRITE_SINGLE_LEN; + + return spi_sync(spi, &wcd_spi->msg1); +} + +static int wcd_spi_write_multi(struct spi_device *spi, + u32 remote_addr, u8 *data, + size_t len) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u32 frame = 0; + u8 *tx_buf; + int xfer_len, ret; + + dev_dbg(&spi->dev, "%s: addr = 0x%x len = %zd\n", + __func__, remote_addr, len); + + frame |= WCD_SPI_WRITE_FRAME_OPCODE; + frame |= (remote_addr & WCD_CMD_ADDR_MASK); + + frame = cpu_to_be32(frame); + xfer_len = len + sizeof(frame); + + tx_buf = kzalloc(xfer_len, GFP_KERNEL); + if (!tx_buf) + return -ENOMEM; + + memcpy(tx_buf, &frame, sizeof(frame)); + memcpy(tx_buf + sizeof(frame), data, len); + + wcd_spi_reinit_xfer(xfer); + xfer->tx_buf = tx_buf; + xfer->len = xfer_len; + + ret = spi_sync(spi, &wcd_spi->msg1); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, + "%s: Failed, addr = 0x%x, len = %zd\n", + __func__, remote_addr, len); + kfree(tx_buf); + + return ret; +} + +static int wcd_spi_transfer_split(struct spi_device *spi, + struct wcd_spi_msg *data_msg, + enum xfer_request xfer_req) +{ + u32 addr = data_msg->remote_addr; + u8 *data = data_msg->data; + int remain_size = data_msg->len; + int to_xfer, loop_cnt, ret; + + /* Perform single writes until multi word alignment is met */ + loop_cnt = 1; + while (remain_size && + !IS_ALIGNED(addr, WCD_SPI_RW_MULTI_ALIGN)) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_single(spi, addr, + (*(u32 *)data)); + else + ret = wcd_spi_read_single(spi, addr, + (u32 *)data); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, + "%s: %s fail iter(%d) start-word addr (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + loop_cnt, addr); + goto done; + } + + addr += WCD_SPI_WORD_BYTE_CNT; + data += WCD_SPI_WORD_BYTE_CNT; + remain_size -= WCD_SPI_WORD_BYTE_CNT; + loop_cnt++; + } + + /* Perform multi writes for max allowed multi writes */ + loop_cnt = 1; + while (remain_size >= WCD_SPI_RW_MULTI_MAX_LEN) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_multi(spi, addr, data, + WCD_SPI_RW_MULTI_MAX_LEN); + else + ret = wcd_spi_read_multi(spi, addr, data, + WCD_SPI_RW_MULTI_MAX_LEN); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, + "%s: %s fail iter(%d) max-write addr (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + loop_cnt, addr); + goto done; + } + + addr += WCD_SPI_RW_MULTI_MAX_LEN; + data += WCD_SPI_RW_MULTI_MAX_LEN; + remain_size -= WCD_SPI_RW_MULTI_MAX_LEN; + loop_cnt++; + } + + /* + * Perform write for max possible data that is multiple + * of the minimum size for multi-write commands. + */ + to_xfer = remain_size - (remain_size % WCD_SPI_RW_MULTI_MIN_LEN); + if (remain_size >= WCD_SPI_RW_MULTI_MIN_LEN && + to_xfer > 0) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_multi(spi, addr, data, to_xfer); + else + ret = wcd_spi_read_multi(spi, addr, data, to_xfer); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, + "%s: %s fail write addr (0x%x), size (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + addr, to_xfer); + goto done; + } + + addr += to_xfer; + data += to_xfer; + remain_size -= to_xfer; + } + + /* Perform single writes for the last remaining data */ + loop_cnt = 1; + while (remain_size > 0) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_single(spi, addr, (*((u32 *)data))); + else + ret = wcd_spi_read_single(spi, addr, (u32 *) data); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, + "%s: %s fail iter(%d) end-write addr (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + loop_cnt, addr); + goto done; + } + + addr += WCD_SPI_WORD_BYTE_CNT; + data += WCD_SPI_WORD_BYTE_CNT; + remain_size -= WCD_SPI_WORD_BYTE_CNT; + loop_cnt++; + } + +done: + return ret; +} + +static int wcd_spi_cmd_nop(struct spi_device *spi) +{ + u8 nop = WCD_SPI_CMD_NOP; + + return spi_write(spi, &nop, WCD_SPI_CMD_NOP_LEN); +} + +static int wcd_spi_cmd_clkreq(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u8 cmd[WCD_SPI_CMD_CLKREQ_LEN] = { + WCD_SPI_CMD_CLKREQ, + 0xBA, 0x80, 0x00}; + + wcd_spi_reinit_xfer(xfer); + xfer->tx_buf = cmd; + xfer->len = WCD_SPI_CMD_CLKREQ_LEN; + xfer->delay_usecs = WCD_SPI_CLKREQ_DELAY_USECS; + + return spi_sync(spi, &wcd_spi->msg1); +} + +static int wcd_spi_cmd_wr_en(struct spi_device *spi) +{ + u8 wr_en = WCD_SPI_CMD_WREN; + + return spi_write(spi, &wr_en, WCD_SPI_CMD_WREN_LEN); +} + +static int wcd_spi_cmd_rdsr(struct spi_device *spi, + u32 *rdsr_status) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0]; + struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1]; + u8 rdsr_cmd; + u32 status; + int ret; + + rdsr_cmd = WCD_SPI_CMD_RDSR; + wcd_spi_reinit_xfer(tx_xfer); + tx_xfer->tx_buf = &rdsr_cmd; + tx_xfer->len = sizeof(rdsr_cmd); + + + wcd_spi_reinit_xfer(rx_xfer); + rx_xfer->rx_buf = &status; + rx_xfer->len = sizeof(status); + + ret = spi_sync(spi, &wcd_spi->msg2); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: RDSR failed, err = %d\n", + __func__, ret); + goto done; + } + + *rdsr_status = be32_to_cpu(status); + + dev_dbg(&spi->dev, "%s: RDSR success, value = 0x%x\n", + __func__, *rdsr_status); +done: + return ret; +} + +static int wcd_spi_clk_enable(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + u32 rd_status; + + ret = wcd_spi_cmd_nop(spi); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: NOP1 failed, err = %d\n", + __func__, ret); + goto done; + } + + ret = wcd_spi_cmd_clkreq(spi); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: CLK_REQ failed, err = %d\n", + __func__, ret); + goto done; + } + + ret = wcd_spi_cmd_nop(spi); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: NOP2 failed, err = %d\n", + __func__, ret); + goto done; + } + wcd_spi_cmd_rdsr(spi, &rd_status); + /* + * Read status zero means reads are not + * happenning on the bus, possibly because + * clock request failed. + */ + if (rd_status) { + set_bit(WCD_SPI_CLK_STATE_ENABLED, + &wcd_spi->status_mask); + } else { + dev_err(&spi->dev, "%s: RDSR status is zero\n", + __func__); + ret = -EIO; + } +done: + return ret; +} + +static int wcd_spi_clk_disable(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + + ret = wcd_spi_write_single(spi, WCD_SPI_ADDR_IPC_CTL_HOST, 0x01); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, "%s: Failed, err = %d\n", + __func__, ret); + else + clear_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask); + + return ret; +} + +static int wcd_spi_clk_ctrl(struct spi_device *spi, + bool request, u32 flags) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret = 0; + const char *delay_str; + + delay_str = (flags == WCD_SPI_CLK_FLAG_DELAYED) ? + "delayed" : "immediate"; + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + + /* Reject any unbalanced disable request */ + if (wcd_spi->clk_users < 0 || + (!request && wcd_spi->clk_users == 0)) { + dev_err(&spi->dev, "%s: Unbalanced clk_users %d for %s\n", + __func__, wcd_spi->clk_users, + request ? "enable" : "disable"); + ret = -EINVAL; + + /* Reset the clk_users to 0 */ + wcd_spi->clk_users = 0; + + goto done; + } + + if (request == WCD_SPI_CLK_ENABLE) { + /* Cancel the disable clk work */ + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + cancel_delayed_work_sync(&wcd_spi->clk_dwork); + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + + wcd_spi->clk_users++; + + /* + * If clk state is already set, + * then clk wasnt really disabled + */ + if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask)) + goto done; + else if (wcd_spi->clk_users == 1) + ret = wcd_spi_clk_enable(spi); + + } else { + wcd_spi->clk_users--; + + /* Clock is still voted for */ + if (wcd_spi->clk_users > 0) + goto done; + + /* + * If we are here, clk_users must be 0 and needs + * to be disabled. Call the disable based on the + * flags. + */ + if (flags == WCD_SPI_CLK_FLAG_DELAYED) { + schedule_delayed_work(&wcd_spi->clk_dwork, + msecs_to_jiffies(WCD_SPI_CLK_OFF_TIMER_MS)); + } else { + ret = wcd_spi_clk_disable(spi); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, + "%s: Failed to disable clk err = %d\n", + __func__, ret); + } + } + +done: + dev_dbg(&spi->dev, "%s: updated clk_users = %d, request_%s %s\n", + __func__, wcd_spi->clk_users, request ? "enable" : "disable", + request ? "" : delay_str); + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + + return ret; +} + +static int wcd_spi_init(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (IS_ERR_VALUE(ret)) + goto done; + + ret = wcd_spi_cmd_wr_en(spi); + if (IS_ERR_VALUE(ret)) + goto err_wr_en; + + regmap_write(wcd_spi->regmap, WCD_SPI_SLAVE_CONFIG, + 0x0F3D0800); + + /* Write the MTU to 64K */ + regmap_update_bits(wcd_spi->regmap, + WCD_SPI_SLAVE_TRNS_LEN, + 0xFFFF0000, + (WCD_SPI_RW_MULTI_MAX_LEN / 4) << 16); +done: + return ret; + +err_wr_en: + wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + return ret; +} + +static void wcd_spi_clk_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wcd_spi_priv *wcd_spi; + struct spi_device *spi; + int ret; + + dwork = to_delayed_work(work); + wcd_spi = container_of(dwork, struct wcd_spi_priv, clk_dwork); + spi = wcd_spi->spi; + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + ret = wcd_spi_clk_disable(spi); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, + "%s: Failed to disable clk, err = %d\n", + __func__, ret); + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); +} + +static int __wcd_spi_data_xfer(struct spi_device *spi, + struct wcd_spi_msg *msg, + enum xfer_request xfer_req) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + + /* Check for minimum alignment requirements */ + if (!IS_ALIGNED(msg->remote_addr, WCD_SPI_RW_MIN_ALIGN)) { + dev_err(&spi->dev, + "%s addr 0x%x is not aligned to 0x%x\n", + __func__, msg->remote_addr, WCD_SPI_RW_MIN_ALIGN); + return -EINVAL; + } else if (msg->len % WCD_SPI_WORD_BYTE_CNT) { + dev_err(&spi->dev, + "%s len 0x%zx is not multiple of %d\n", + __func__, msg->len, WCD_SPI_WORD_BYTE_CNT); + return -EINVAL; + } + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->xfer_mutex); + if (msg->len == WCD_SPI_WORD_BYTE_CNT) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_single(spi, msg->remote_addr, + (*((u32 *)msg->data))); + else + ret = wcd_spi_read_single(spi, msg->remote_addr, + (u32 *) msg->data); + } else { + ret = wcd_spi_transfer_split(spi, msg, xfer_req); + } + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->xfer_mutex); + + return ret; +} + +static int wcd_spi_data_xfer(struct spi_device *spi, + struct wcd_spi_msg *msg, + enum xfer_request req) +{ + int ret, ret1; + + if (msg->len <= 0) { + dev_err(&spi->dev, "%s: Invalid size %zd\n", + __func__, msg->len); + return -EINVAL; + } + + /* Request for clock */ + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: clk enable failed %d\n", + __func__, ret); + goto done; + } + + /* Perform the transaction */ + ret = __wcd_spi_data_xfer(spi, msg, req); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, + "%s: Failed %s, addr = 0x%x, size = 0x%zx, err = %d\n", + __func__, wcd_spi_xfer_req_str(req), + msg->remote_addr, msg->len, ret); + + /* Release the clock even if xfer failed */ + ret1 = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, + WCD_SPI_CLK_FLAG_DELAYED); + if (IS_ERR_VALUE(ret1)) + dev_err(&spi->dev, "%s: clk disable failed %d\n", + __func__, ret1); +done: + return ret; +} + +/* + * wcd_spi_data_write: Write data to WCD SPI + * @spi: spi_device struct + * @msg: msg that needs to be written to WCD + * + * This API writes length of data to address specified. These details + * about the write are encapsulated in @msg. Write size should be multiple + * of 4 bytes and write address should be 4-byte aligned. + */ +int wcd_spi_data_write(struct spi_device *spi, + struct wcd_spi_msg *msg) +{ + if (!spi || !msg) { + pr_err("%s: Invalid %s\n", __func__, + (!spi) ? "spi device" : "msg"); + return -EINVAL; + } + + dev_dbg_ratelimited(&spi->dev, "%s: addr = 0x%x, len = %zu\n", + __func__, msg->remote_addr, msg->len); + return wcd_spi_data_xfer(spi, msg, WCD_SPI_XFER_WRITE); +} +EXPORT_SYMBOL(wcd_spi_data_write); + +/* + * wcd_spi_data_read: Read data from WCD SPI + * @spi: spi_device struct + * @msg: msg that needs to be read from WCD + * + * This API reads length of data from address specified. These details + * about the read are encapsulated in @msg. Read size should be multiple + * of 4 bytes and read address should be 4-byte aligned. + */ +int wcd_spi_data_read(struct spi_device *spi, + struct wcd_spi_msg *msg) +{ + if (!spi || !msg) { + pr_err("%s: Invalid %s\n", __func__, + (!spi) ? "spi device" : "msg"); + return -EINVAL; + } + + dev_dbg_ratelimited(&spi->dev, "%s: addr = 0x%x,len = %zu\n", + __func__, msg->remote_addr, msg->len); + return wcd_spi_data_xfer(spi, msg, WCD_SPI_XFER_READ); +} +EXPORT_SYMBOL(wcd_spi_data_read); + +static int wdsp_spi_dload_section(struct spi_device *spi, + void *data) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wdsp_img_section *sec = data; + struct wcd_spi_msg msg; + int ret; + + dev_dbg(&spi->dev, "%s: addr = 0x%x, size = 0x%zx\n", + __func__, sec->addr, sec->size); + + msg.remote_addr = sec->addr + wcd_spi->mem_base_addr; + msg.data = sec->data; + msg.len = sec->size; + + ret = __wcd_spi_data_xfer(spi, &msg, WCD_SPI_XFER_WRITE); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, "%s: fail addr (0x%x) size (0x%zx)\n", + __func__, msg.remote_addr, msg.len); + return ret; +} + +static int wdsp_spi_event_handler(struct device *dev, void *priv_data, + enum wdsp_event_type event, + void *data) +{ + struct spi_device *spi = to_spi_device(dev); + int ret; + + dev_dbg(&spi->dev, "%s: event type %d\n", + __func__, event); + + switch (event) { + case WDSP_EVENT_PRE_DLOAD_DATA: + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, "%s: clk_req failed %d\n", + __func__, ret); + break; + + case WDSP_EVENT_POST_DLOAD_CODE: + case WDSP_EVENT_POST_DLOAD_DATA: + case WDSP_EVENT_DLOAD_FAILED: + + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, "%s: clk unvote failed %d\n", + __func__, ret); + break; + + case WDSP_EVENT_DLOAD_SECTION: + ret = wdsp_spi_dload_section(spi, data); + break; + default: + dev_dbg(&spi->dev, "%s: Unhandled event %d\n", + __func__, event); + break; + } + + return ret; +} + +static int wcd_spi_bus_gwrite(void *context, const void *reg, + size_t reg_len, const void *val, + size_t val_len) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + u8 tx_buf[WCD_SPI_CMD_IRW_LEN]; + + if (!reg || !val || reg_len != wcd_spi->reg_bytes || + val_len != wcd_spi->val_bytes) { + dev_err(&spi->dev, + "%s: Invalid input, reg_len = %zd, val_len = %zd", + __func__, reg_len, val_len); + return -EINVAL; + } + + tx_buf[0] = WCD_SPI_CMD_IRW; + tx_buf[1] = *((u8 *)reg); + memcpy(&tx_buf[WCD_SPI_OPCODE_LEN + reg_len], + val, val_len); + + return spi_write(spi, tx_buf, WCD_SPI_CMD_IRW_LEN); +} + +static int wcd_spi_bus_write(void *context, const void *data, + size_t count) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + if (count < (wcd_spi->reg_bytes + wcd_spi->val_bytes)) { + dev_err(&spi->dev, "%s: Invalid size %zd\n", + __func__, count); + WARN_ON(1); + return -EINVAL; + } + + return wcd_spi_bus_gwrite(context, data, wcd_spi->reg_bytes, + data + wcd_spi->reg_bytes, + count - wcd_spi->reg_bytes); +} + +static int wcd_spi_bus_read(void *context, const void *reg, + size_t reg_len, void *val, + size_t val_len) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0]; + struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1]; + u8 tx_buf[WCD_SPI_CMD_IRR_LEN]; + + if (!reg || !val || reg_len != wcd_spi->reg_bytes || + val_len != wcd_spi->val_bytes) { + dev_err(&spi->dev, + "%s: Invalid input, reg_len = %zd, val_len = %zd", + __func__, reg_len, val_len); + return -EINVAL; + } + + memset(tx_buf, 0, WCD_SPI_OPCODE_LEN); + tx_buf[0] = WCD_SPI_CMD_IRR; + tx_buf[1] = *((u8 *)reg); + + wcd_spi_reinit_xfer(tx_xfer); + tx_xfer->tx_buf = tx_buf; + tx_xfer->rx_buf = NULL; + tx_xfer->len = WCD_SPI_CMD_IRR_LEN; + + wcd_spi_reinit_xfer(rx_xfer); + rx_xfer->tx_buf = NULL; + rx_xfer->rx_buf = val; + rx_xfer->len = val_len; + + return spi_sync(spi, &wcd_spi->msg2); +} + +static struct regmap_bus wcd_spi_regmap_bus = { + .write = wcd_spi_bus_write, + .gather_write = wcd_spi_bus_gwrite, + .read = wcd_spi_bus_read, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static int wcd_spi_state_show(struct seq_file *f, void *ptr) +{ + struct spi_device *spi = f->private; + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + const char *clk_state, *clk_mutex, *xfer_mutex; + + if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask)) + clk_state = "enabled"; + else + clk_state = "disabled"; + + clk_mutex = mutex_is_locked(&wcd_spi->clk_mutex) ? + "locked" : "unlocked"; + + xfer_mutex = mutex_is_locked(&wcd_spi->xfer_mutex) ? + "locked" : "unlocked"; + + seq_printf(f, "clk_state = %s\nclk_users = %d\n" + "clk_mutex = %s\nxfer_mutex = %s\n", + clk_state, wcd_spi->clk_users, clk_mutex, + xfer_mutex); + return 0; +} + +static int wcd_spi_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, wcd_spi_state_show, inode->i_private); +} + +static const struct file_operations state_fops = { + .open = wcd_spi_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int wcd_spi_debugfs_init(struct spi_device *spi) +{ + int rc = 0; + struct dentry *dir; + + dir = debugfs_create_dir("wcd_spi", NULL); + if (IS_ERR_OR_NULL(dir)) { + dir = NULL; + rc = -ENODEV; + goto done; + } + + debugfs_create_file("state", 0444, dir, spi, &state_fops); + +done: + return rc; +} + + +static const struct reg_default wcd_spi_defaults[] = { + {WCD_SPI_SLAVE_SANITY, 0xDEADBEEF}, + {WCD_SPI_SLAVE_DEVICE_ID, 0x00500000}, + {WCD_SPI_SLAVE_STATUS, 0x80100000}, + {WCD_SPI_SLAVE_CONFIG, 0x0F200808}, + {WCD_SPI_SLAVE_SW_RESET, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_STATUS, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_EN, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_CLR, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_FORCE, 0x00000000}, + {WCD_SPI_SLAVE_TX, 0x00000000}, + {WCD_SPI_SLAVE_TEST_BUS_DATA, 0x00000000}, + {WCD_SPI_SLAVE_TEST_BUS_CTRL, 0x00000000}, + {WCD_SPI_SLAVE_SW_RST_IRQ, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_CFG, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_DATA_MOSI, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_DATA_CS_N, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_DATA_MISO, 0x00000000}, + {WCD_SPI_SLAVE_TRNS_BYTE_CNT, 0x00000000}, + {WCD_SPI_SLAVE_TRNS_LEN, 0x00000000}, + {WCD_SPI_SLAVE_FIFO_LEVEL, 0x00000000}, + {WCD_SPI_SLAVE_GENERICS, 0x80000000}, + {WCD_SPI_SLAVE_EXT_BASE_ADDR, 0x00000000}, +}; + +static bool wcd_spi_is_volatile_reg(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case WCD_SPI_SLAVE_SANITY: + case WCD_SPI_SLAVE_STATUS: + case WCD_SPI_SLAVE_IRQ_STATUS: + case WCD_SPI_SLAVE_TX: + case WCD_SPI_SLAVE_SW_RST_IRQ: + case WCD_SPI_SLAVE_TRNS_BYTE_CNT: + case WCD_SPI_SLAVE_FIFO_LEVEL: + case WCD_SPI_SLAVE_GENERICS: + return true; + } + + return false; +} + +static bool wcd_spi_is_readable_reg(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case WCD_SPI_SLAVE_SW_RESET: + case WCD_SPI_SLAVE_IRQ_CLR: + case WCD_SPI_SLAVE_IRQ_FORCE: + return false; + } + + return true; +} + +static struct regmap_config wcd_spi_regmap_cfg = { + .reg_bits = 8, + .val_bits = 32, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wcd_spi_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd_spi_defaults), + .max_register = WCD_SPI_MAX_REGISTER, + .volatile_reg = wcd_spi_is_volatile_reg, + .readable_reg = wcd_spi_is_readable_reg, +}; + +static int wdsp_spi_init(struct device *dev, void *priv_data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + + wcd_spi->reg_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.reg_bits, 8); + wcd_spi->val_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.val_bits, 8); + + wcd_spi->regmap = devm_regmap_init(&spi->dev, &wcd_spi_regmap_bus, + &spi->dev, &wcd_spi_regmap_cfg); + if (IS_ERR(wcd_spi->regmap)) { + ret = PTR_ERR(wcd_spi->regmap); + dev_err(&spi->dev, "%s: Failed to allocate regmap, err = %d\n", + __func__, ret); + goto err_regmap; + } + + if (wcd_spi_debugfs_init(spi)) + dev_err(&spi->dev, "%s: Failed debugfs init\n", __func__); + + spi_message_init(&wcd_spi->msg1); + spi_message_add_tail(&wcd_spi->xfer1, &wcd_spi->msg1); + + spi_message_init(&wcd_spi->msg2); + spi_message_add_tail(&wcd_spi->xfer2[0], &wcd_spi->msg2); + spi_message_add_tail(&wcd_spi->xfer2[1], &wcd_spi->msg2); + + ret = wcd_spi_init(spi); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: Init failed, err = %d\n", + __func__, ret); + goto err_init; + } + + return 0; + +err_init: + spi_transfer_del(&wcd_spi->xfer1); + spi_transfer_del(&wcd_spi->xfer2[0]); + spi_transfer_del(&wcd_spi->xfer2[1]); + +err_regmap: + return ret; +} + +static int wdsp_spi_deinit(struct device *dev, void *priv_data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + spi_transfer_del(&wcd_spi->xfer1); + spi_transfer_del(&wcd_spi->xfer2[0]); + spi_transfer_del(&wcd_spi->xfer2[1]); + + return 0; +} + +static struct wdsp_cmpnt_ops wdsp_spi_ops = { + .init = wdsp_spi_init, + .deinit = wdsp_spi_deinit, + .event_handler = wdsp_spi_event_handler, +}; + +static int wcd_spi_component_bind(struct device *dev, + struct device *master, + void *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret = 0; + + wcd_spi->m_dev = master; + wcd_spi->m_ops = data; + + if (wcd_spi->m_ops && + wcd_spi->m_ops->register_cmpnt_ops) + ret = wcd_spi->m_ops->register_cmpnt_ops(master, dev, + wcd_spi, + &wdsp_spi_ops); + if (ret) + dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n", + __func__, ret); + return ret; +} + +static void wcd_spi_component_unbind(struct device *dev, + struct device *master, + void *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + wcd_spi->m_dev = NULL; + wcd_spi->m_ops = NULL; +} + +static const struct component_ops wcd_spi_component_ops = { + .bind = wcd_spi_component_bind, + .unbind = wcd_spi_component_unbind, +}; + +static int wcd_spi_probe(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi; + int ret = 0; + + wcd_spi = devm_kzalloc(&spi->dev, sizeof(*wcd_spi), + GFP_KERNEL); + if (!wcd_spi) + return -ENOMEM; + + ret = of_property_read_u32(spi->dev.of_node, + "qcom,mem-base-addr", + &wcd_spi->mem_base_addr); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: Missing %s DT entry", + __func__, "qcom,mem-base-addr"); + goto err_ret; + } + + dev_dbg(&spi->dev, + "%s: mem_base_addr 0x%x\n", __func__, wcd_spi->mem_base_addr); + + mutex_init(&wcd_spi->clk_mutex); + mutex_init(&wcd_spi->xfer_mutex); + INIT_DELAYED_WORK(&wcd_spi->clk_dwork, wcd_spi_clk_work); + + wcd_spi->spi = spi; + spi_set_drvdata(spi, wcd_spi); + + ret = component_add(&spi->dev, &wcd_spi_component_ops); + if (ret) { + dev_err(&spi->dev, "%s: component_add failed err = %d\n", + __func__, ret); + goto err_component_add; + } + + return ret; + +err_component_add: + mutex_destroy(&wcd_spi->clk_mutex); + mutex_destroy(&wcd_spi->xfer_mutex); +err_ret: + devm_kfree(&spi->dev, wcd_spi); + spi_set_drvdata(spi, NULL); + return ret; +} + +static int wcd_spi_remove(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + component_del(&spi->dev, &wcd_spi_component_ops); + + mutex_destroy(&wcd_spi->clk_mutex); + mutex_destroy(&wcd_spi->xfer_mutex); + + devm_kfree(&spi->dev, wcd_spi); + spi_set_drvdata(spi, NULL); + + return 0; +} + +static const struct of_device_id wcd_spi_of_match[] = { + { .compatible = "qcom,wcd-spi-v2", }, + { } +}; +MODULE_DEVICE_TABLE(of, wcd_spi_of_match); + +static struct spi_driver wcd_spi_driver = { + .driver = { + .name = "wcd-spi-v2", + .of_match_table = wcd_spi_of_match, + }, + .probe = wcd_spi_probe, + .remove = wcd_spi_remove, +}; + +module_spi_driver(wcd_spi_driver); + +MODULE_DESCRIPTION("WCD SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index f46057d027e0..f5a71b2a2d1a 100755..100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -369,6 +369,7 @@ enum { AIF_MIX1_PB, AIF4_MAD_TX, AIF4_VIFEED, + AIF5_CPE_TX, NUM_CODEC_DAIS, }; @@ -477,15 +478,18 @@ static const struct wcd9xxx_ch tasha_tx_chs[TASHA_TX_MAX] = { }; static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = { - 0, /* AIF1_PB */ - BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF1_CAP */ - 0, /* AIF2_PB */ - BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF2_CAP */ - 0, /* AIF3_PB */ - BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX), /* AIF3_CAP */ - 0, /* AIF4_PB */ - 0, /* AIF_MIX1_PB */ - BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP), /* AIF4_MAD_TX */ + /* Needs to define in the same order of DAI enum definitions */ + 0, + BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), }; static const u32 vport_i2s_check_table[NUM_CODEC_DAIS] = { @@ -2520,6 +2524,7 @@ static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol, } break; case AIF4_MAD_TX: + case AIF5_CPE_TX: break; default: pr_err("Unknown AIF %d\n", dai_id); @@ -3289,7 +3294,7 @@ static int __tasha_codec_enable_slimtx(struct snd_soc_codec *codec, ret = wcd9xxx_disconnect_port(core, &dai->wcd9xxx_ch_list, dai->grph); - pr_debug("%s: Disconnect RX port, ret = %d\n", + pr_debug("%s: Disconnect TX port, ret = %d\n", __func__, ret); } @@ -6199,6 +6204,9 @@ static const struct snd_soc_dapm_route audio_map[] = { {"AIF4 MAD", NULL, "AIF4_MAD Mixer"}, {"AIF4 MAD", NULL, "AIF4"}, + {"EC BUF MUX INP", "DEC1", "ADC MUX1"}, + {"AIF5 CPE", NULL, "EC BUF MUX INP"}, + /* SLIMBUS Connections */ {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, @@ -9960,6 +9968,45 @@ static const struct snd_kcontrol_new anc0_fb_mux = static const struct snd_kcontrol_new anc1_fb_mux = SOC_DAPM_ENUM("ANC1 FB MUX Mux", anc1_fb_mux_enum); +static int tasha_codec_ec_buf_mux_enable(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event = %d name = %s\n", + __func__, event, w->name); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x3B); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x68, 0x28); + snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, + 0x08, 0x08); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, + 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x68, 0x40); + snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x00); + break; + } + + return 0; +}; + +static const char * const ec_buf_mux_text[] = { + "ZERO", "RXMIXEC", "SB_RX0", "SB_RX1", "SB_RX2", "SB_RX3", + "I2S_RX_SD0_L", "I2S_RX_SD0_R", "I2S_RX_SD1_L", "I2S_RX_SD1_R", + "DEC1" +}; + +static SOC_ENUM_SINGLE_DECL(ec_buf_mux_enum, WCD9335_CPE_SS_US_EC_MUX_CFG, + 0, ec_buf_mux_text); + +static const struct snd_kcontrol_new ec_buf_mux = + SOC_DAPM_ENUM("EC BUF Mux", ec_buf_mux_enum); + static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("EAR"), SND_SOC_DAPM_OUTPUT("ANC EAR"), @@ -10449,6 +10496,14 @@ static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = { aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)), SND_SOC_DAPM_INPUT("VIINPUT"), + + SND_SOC_DAPM_AIF_OUT("AIF5 CPE", "AIF5 CPE TX", 0, SND_SOC_NOPM, + AIF5_CPE_TX, 0), + + SND_SOC_DAPM_MUX_E("EC BUF MUX INP", SND_SOC_NOPM, 0, 0, &ec_buf_mux, + tasha_codec_ec_buf_mux_enable, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + /* Digital Mic Inputs */ SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0, tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | @@ -11432,6 +11487,19 @@ static struct snd_soc_dai_driver tasha_dai[] = { }, .ops = &tasha_dai_ops, }, + { + .name = "tasha_cpe", + .id = AIF5_CPE_TX, + .capture = { + .stream_name = "AIF5 CPE TX", + .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000, + .formats = TASHA_FORMATS_S16_S24_S32_LE, + .rate_min = 16000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 1, + }, + }, }; static struct snd_soc_dai_driver tasha_i2s_dai[] = { @@ -13098,6 +13166,20 @@ static int tasha_codec_probe(struct snd_soc_codec *codec) control->tx_chs = ptr + sizeof(tasha_rx_chs); memcpy(control->tx_chs, tasha_tx_chs, sizeof(tasha_tx_chs)); + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture"); + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF Mix Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX"); + snd_soc_dapm_ignore_suspend(dapm, "VIfeed"); + } + snd_soc_dapm_sync(dapm); ret = tasha_setup_irqs(tasha); diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c index 6b2ef88c7163..75387b7c2069 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -108,6 +108,7 @@ static const struct snd_kcontrol_new name##_mux = \ #define WCD934X_DEC_PWR_LVL_LP 0x02 #define WCD934X_DEC_PWR_LVL_HP 0x04 #define WCD934X_DEC_PWR_LVL_DF 0x00 +#define WCD934X_STRING_LEN 100 #define WCD934X_MAX_MICBIAS 4 #define DAPM_MICBIAS1_STANDALONE "MIC BIAS1 Standalone" @@ -470,7 +471,7 @@ struct tavil_priv { struct clk *wcd_ext_clk; struct mutex codec_mutex; - struct work_struct wcd_add_child_devices_work; + struct work_struct tavil_add_child_devices_work; struct hpf_work tx_hpf_work[WCD934X_NUM_DECIMATORS]; struct tx_mute_work tx_mute_dwork[WCD934X_NUM_DECIMATORS]; }; @@ -5551,7 +5552,7 @@ static int tavil_swrm_handle_irq(void *handle, return ret; } -static void wcd_add_child_devices(struct work_struct *work) +static void tavil_add_child_devices(struct work_struct *work) { struct tavil_priv *tavil; struct platform_device *pdev; @@ -5560,9 +5561,10 @@ static void wcd_add_child_devices(struct work_struct *work) struct tavil_swr_ctrl_data *swr_ctrl_data = NULL, *temp; int ret, ctrl_num = 0; struct wcd_swr_ctrl_platform_data *platdata; + char plat_dev_name[WCD934X_STRING_LEN]; tavil = container_of(work, struct tavil_priv, - wcd_add_child_devices_work); + tavil_add_child_devices_work); if (!tavil) { pr_err("%s: Memory for WCD934X does not exist\n", __func__); @@ -5583,17 +5585,17 @@ static void wcd_add_child_devices(struct work_struct *work) platdata = &tavil->swr.plat_data; for_each_child_of_node(wcd9xxx->dev->of_node, node) { - temp = krealloc(swr_ctrl_data, - (ctrl_num + 1) * sizeof(struct tavil_swr_ctrl_data), - GFP_KERNEL); - if (!temp) { - dev_err(wcd9xxx->dev, "out of memory\n"); - ret = -ENOMEM; - goto err_mem; - } - swr_ctrl_data = temp; - swr_ctrl_data[ctrl_num].swr_pdev = NULL; - pdev = platform_device_alloc("tavil_swr_ctrl", -1); + if (!strcmp(node->name, "swr_master")) + strlcpy(plat_dev_name, "tavil_swr_ctrl", + (WCD934X_STRING_LEN - 1)); + else if (strnstr(node->name, "msm_cdc_pinctrl", + strlen("msm_cdc_pinctrl")) != NULL) + strlcpy(plat_dev_name, node->name, + (WCD934X_STRING_LEN - 1)); + else + continue; + + pdev = platform_device_alloc(plat_dev_name, -1); if (!pdev) { dev_err(wcd9xxx->dev, "%s: pdev memory alloc failed\n", __func__); @@ -5603,34 +5605,51 @@ static void wcd_add_child_devices(struct work_struct *work) pdev->dev.parent = tavil->dev; pdev->dev.of_node = node; - ret = platform_device_add_data(pdev, platdata, - sizeof(*platdata)); - if (ret) { - dev_err(&pdev->dev, "%s: cannot add plat data for ctrl:%d\n", - __func__, ctrl_num); - goto err_pdev_add; + if (strcmp(node->name, "swr_master") == 0) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto err_pdev_add; + } } ret = platform_device_add(pdev); if (ret) { - dev_err(&pdev->dev, "%s: Cannot add swr platform device\n", + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", __func__); goto err_pdev_add; } - swr_ctrl_data[ctrl_num].swr_pdev = pdev; - ctrl_num++; - dev_dbg(&pdev->dev, "%s: Added soundwire ctrl device(s)\n", - __func__); + if (strcmp(node->name, "swr_master") == 0) { + temp = krealloc(swr_ctrl_data, + (ctrl_num + 1) * sizeof( + struct tavil_swr_ctrl_data), + GFP_KERNEL); + if (!temp) { + dev_err(wcd9xxx->dev, "out of memory\n"); + ret = -ENOMEM; + goto err_pdev_add; + } + swr_ctrl_data = temp; + swr_ctrl_data[ctrl_num].swr_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + tavil->swr.ctrl_data = swr_ctrl_data; + } } - tavil->swr.ctrl_data = swr_ctrl_data; return; err_pdev_add: platform_device_put(pdev); err_mem: - kfree(swr_ctrl_data); + return; } static int __tavil_enable_efuse_sensing(struct tavil_priv *tavil) @@ -5676,7 +5695,8 @@ static int tavil_probe(struct platform_device *pdev) tavil->wcd9xxx = dev_get_drvdata(pdev->dev.parent); tavil->dev = &pdev->dev; - INIT_WORK(&tavil->wcd_add_child_devices_work, wcd_add_child_devices); + INIT_WORK(&tavil->tavil_add_child_devices_work, + tavil_add_child_devices); mutex_init(&tavil->swr.read_mutex); mutex_init(&tavil->swr.write_mutex); mutex_init(&tavil->swr.clk_mutex); @@ -5733,7 +5753,7 @@ static int tavil_probe(struct platform_device *pdev) __func__); goto err_cdc_reg; } - schedule_work(&tavil->wcd_add_child_devices_work); + schedule_work(&tavil->tavil_add_child_devices_work); return ret; diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c index b1e105b3153a..0b4bd3c15127 100644 --- a/sound/soc/codecs/wcd_cpe_core.c +++ b/sound/soc/codecs/wcd_cpe_core.c @@ -35,9 +35,10 @@ #include "wcd_cmi_api.h" #define CMI_CMD_TIMEOUT (10 * HZ) -#define WCD_CPE_LSM_MAX_SESSIONS 1 +#define WCD_CPE_LSM_MAX_SESSIONS 2 #define WCD_CPE_AFE_MAX_PORTS 4 #define AFE_SVC_EXPLICIT_PORT_START 1 +#define WCD_CPE_EC_PP_BUF_SIZE 480 /* 5 msec buffer */ #define ELF_FLAG_EXECUTE (1 << 0) #define ELF_FLAG_WRITE (1 << 1) @@ -1638,7 +1639,8 @@ static int wcd_cpe_vote(struct wcd_cpe_core *core, core->cpe_users); if (enable) { - if (core->cpe_users == 0) { + core->cpe_users++; + if (core->cpe_users == 1) { ret = wcd_cpe_enable(core, enable); if (ret) { dev_err(core->dev, @@ -1646,7 +1648,6 @@ static int wcd_cpe_vote(struct wcd_cpe_core *core, __func__, ret); goto done; } - core->cpe_users++; } else { dev_dbg(core->dev, "%s: cpe already enabled, users = %u\n", @@ -1654,7 +1655,8 @@ static int wcd_cpe_vote(struct wcd_cpe_core *core, goto done; } } else { - if (core->cpe_users == 1) { + core->cpe_users--; + if (core->cpe_users == 0) { ret = wcd_cpe_enable(core, enable); if (ret) { dev_err(core->dev, @@ -1662,7 +1664,6 @@ static int wcd_cpe_vote(struct wcd_cpe_core *core, __func__, ret); goto done; } - core->cpe_users--; } else { dev_dbg(core->dev, "%s: %u valid users on cpe\n", @@ -3335,7 +3336,6 @@ static int wcd_cpe_cmd_lsm_start(void *core_handle, { struct cmi_hdr cmd_lsm_start; struct wcd_cpe_core *core = core_handle; - struct cpe_lsm_ids ids; int ret = 0; ret = wcd_cpe_is_valid_lsm_session(core, session, @@ -3343,30 +3343,6 @@ static int wcd_cpe_cmd_lsm_start(void *core_handle, if (ret) return ret; - /* Send connect to port (input) */ - ids.module_id = CPE_LSM_MODULE_ID_VOICE_WAKEUP; - ids.param_id = CPE_LSM_PARAM_ID_CONNECT_TO_PORT; - ret = wcd_cpe_send_param_connectport(core, session, - NULL, &ids, CPE_AFE_PORT_1_TX); - if (ret) { - dev_err(core->dev, - "%s: Failed to set connectPort, err=%d\n", - __func__, ret); - return ret; - } - - /* Send connect to port (output) */ - ids.module_id = CPE_LSM_MODULE_FRAMEWORK; - ids.param_id = CPE_LSM_PARAM_ID_CONNECT_TO_PORT; - ret = wcd_cpe_send_param_connectport(core, session, - NULL, &ids, session->afe_out_port_id); - if (ret) { - dev_err(core->dev, - "%s: Failed to set connectPort, err=%d\n", - __func__, ret); - return ret; - } - WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); memset(&cmd_lsm_start, 0, sizeof(struct cmi_hdr)); @@ -3449,9 +3425,12 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( if (!wcd_cpe_lsm_session_active()) afe_register_service = true; - for (i = 1; i <= WCD_CPE_LSM_MAX_SESSIONS; i++) - if (!lsm_sessions[i]) + for (i = 1; i <= WCD_CPE_LSM_MAX_SESSIONS; i++) { + if (!lsm_sessions[i]) { session_id = i; + break; + } + } if (session_id < 0) { dev_err(core->dev, @@ -3875,6 +3854,83 @@ static void wcd_cpe_snd_model_offset(void *core_handle, *offset = sizeof(struct cpe_param_data); } +static int wcd_cpe_lsm_set_media_fmt_params(void *core_handle, + struct cpe_lsm_session *session, + struct lsm_hw_params *param) +{ + struct cpe_lsm_media_fmt_param media_fmt; + struct cmi_hdr *msg_hdr = &media_fmt.hdr; + struct wcd_cpe_core *core = core_handle; + struct cpe_param_data *param_d = &media_fmt.param; + struct cpe_lsm_ids ids; + int ret; + + memset(&media_fmt, 0, sizeof(media_fmt)); + if (fill_lsm_cmd_header_v0_inband(msg_hdr, + session->id, + CPE_MEDIA_FMT_PLD_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + ret = -EINVAL; + goto done; + } + + memset(&ids, 0, sizeof(ids)); + ids.module_id = CPE_LSM_MODULE_FRAMEWORK; + ids.param_id = CPE_LSM_PARAM_ID_MEDIA_FMT; + + wcd_cpe_set_param_data(param_d, &ids, CPE_MEDIA_FMT_PARAM_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + media_fmt.minor_version = 1; + media_fmt.sample_rate = param->sample_rate; + media_fmt.num_channels = param->num_chs; + media_fmt.bit_width = param->bit_width; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &media_fmt); + if (ret) + dev_err(core->dev, + "%s: Set_param(media_format) failed, err=%d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +done: + return ret; +} + +static int wcd_cpe_lsm_set_port(void *core_handle, + struct cpe_lsm_session *session, void *data) +{ + u32 port_id; + int ret; + struct cpe_lsm_ids ids; + struct wcd_cpe_core *core = core_handle; + + ret = wcd_cpe_is_valid_lsm_session(core, session, __func__); + if (ret) + goto done; + + if (!data) { + dev_err(core->dev, "%s: data is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + port_id = *(u32 *)data; + dev_dbg(core->dev, "%s: port_id: %d\n", __func__, port_id); + + memset(&ids, 0, sizeof(ids)); + ids.module_id = LSM_MODULE_ID_FRAMEWORK; + ids.param_id = LSM_PARAM_ID_CONNECT_TO_PORT; + + ret = wcd_cpe_send_param_connectport(core, session, NULL, + &ids, port_id); + if (ret) + dev_err(core->dev, + "%s: send_param_connectport failed, err %d\n", + __func__, ret); +done: + return ret; +} + /* * wcd_cpe_get_lsm_ops: register lsm driver to codec * @lsm_ops: structure with lsm callbacks @@ -3899,6 +3955,9 @@ int wcd_cpe_get_lsm_ops(struct wcd_cpe_lsm_ops *lsm_ops) lsm_ops->lsm_set_fmt_cfg = wcd_cpe_lsm_set_fmt_cfg; lsm_ops->lsm_set_one_param = wcd_cpe_set_one_param; lsm_ops->lsm_get_snd_model_offset = wcd_cpe_snd_model_offset; + lsm_ops->lsm_set_media_fmt_params = wcd_cpe_lsm_set_media_fmt_params; + lsm_ops->lsm_set_port = wcd_cpe_lsm_set_port; + return 0; } EXPORT_SYMBOL(wcd_cpe_get_lsm_ops); @@ -4129,10 +4188,10 @@ static int wcd_cpe_send_afe_cal(void *core_handle, goto rel_cal_mutex; } - rc = fill_cmi_header(hdr, port_d->port_id, - CMI_CPE_AFE_SERVICE_ID, - 0, 20, CPE_AFE_CMD_SET_PARAM, - true); + rc = fill_afe_cmd_header(hdr, port_d->port_id, + CPE_AFE_CMD_SET_PARAM, + CPE_AFE_PARAM_PAYLOAD_SIZE, + true); if (rc) { dev_err(core->dev, "%s: invalid params for header, err = %d\n", @@ -4163,10 +4222,10 @@ static int wcd_cpe_send_afe_cal(void *core_handle, hdr = (struct cmi_hdr *) inb_msg; - rc = fill_cmi_header(hdr, port_d->port_id, - CMI_CPE_AFE_SERVICE_ID, - 0, afe_cal->cal_data.size, - CPE_AFE_CMD_SET_PARAM, false); + rc = fill_afe_cmd_header(hdr, port_d->port_id, + CPE_AFE_CMD_SET_PARAM, + CPE_AFE_PARAM_PAYLOAD_SIZE, + false); if (rc) { dev_err(core->dev, "%s: invalid params for header, err = %d\n", @@ -4299,8 +4358,12 @@ static int wcd_cpe_afe_cmd_port_cfg(void *core_handle, port_cfg_cmd.bit_width = afe_cfg->bit_width; port_cfg_cmd.num_channels = afe_cfg->num_channels; port_cfg_cmd.sample_rate = afe_cfg->sample_rate; - port_cfg_cmd.buffer_size = AFE_OUT_BUF_SIZE(afe_cfg->bit_width, - afe_cfg->sample_rate); + + if (afe_port_d->port_id == CPE_AFE_PORT_3_TX) + port_cfg_cmd.buffer_size = WCD_CPE_EC_PP_BUF_SIZE; + else + port_cfg_cmd.buffer_size = AFE_OUT_BUF_SIZE(afe_cfg->bit_width, + afe_cfg->sample_rate); ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &port_cfg_cmd); if (ret) diff --git a/sound/soc/msm/msm-cpe-lsm.c b/sound/soc/msm/msm-cpe-lsm.c index a509107ea9f2..45eed236c5c9 100644 --- a/sound/soc/msm/msm-cpe-lsm.c +++ b/sound/soc/msm/msm-cpe-lsm.c @@ -28,7 +28,11 @@ #include <sound/pcm_params.h> #include <sound/msm-slim-dma.h> +#define SAMPLE_RATE_48KHZ 48000 +#define SAMPLE_RATE_16KHZ 16000 #define LSM_VOICE_WAKEUP_APP_V2 2 +#define AFE_PORT_ID_1 1 +#define AFE_PORT_ID_3 3 #define AFE_OUT_PORT_2 2 #define LISTEN_MIN_NUM_PERIODS 2 #define LISTEN_MAX_NUM_PERIODS 12 @@ -135,6 +139,7 @@ struct cpe_priv { struct wcd_cpe_lsm_ops lsm_ops; struct wcd_cpe_afe_ops afe_ops; bool afe_mad_ctl; + u32 input_port_id; }; struct cpe_lsm_data { @@ -1156,12 +1161,6 @@ static int msm_cpe_lsm_ioctl_shared(struct snd_pcm_substream *substream, __func__, rc); return rc; } - rc = lsm_ops->lsm_lab_control(cpe->core_handle, - session, false); - if (IS_ERR_VALUE(rc)) - dev_err(rtd->dev, - "%s: Lab Disable Failed rc %d\n", - __func__, rc); /* * Buffer has to be de-allocated even if * lab_control failed. @@ -1390,14 +1389,6 @@ static int msm_cpe_lsm_ioctl_shared(struct snd_pcm_substream *substream, dev_dbg(rtd->dev, "%s: %s\n", __func__, "SNDRV_LSM_START"); - rc = lsm_ops->lsm_get_afe_out_port_id(cpe->core_handle, - session); - if (rc != 0) { - dev_err(rtd->dev, - "%s: failed to get port id, err = %d\n", - __func__, rc); - return rc; - } rc = lsm_ops->lsm_start(cpe->core_handle, session); if (rc != 0) { dev_err(rtd->dev, @@ -2680,6 +2671,8 @@ static int msm_cpe_lsm_prepare(struct snd_pcm_substream *substream) struct cpe_lsm_session *lsm_session; struct cpe_lsm_lab *lab_d = &lsm_d->lab; struct snd_pcm_runtime *runtime = substream->runtime; + struct lsm_hw_params lsm_param; + struct wcd_cpe_lsm_ops *lsm_ops; if (!cpe || !cpe->core_handle) { dev_err(rtd->dev, @@ -2712,23 +2705,74 @@ static int msm_cpe_lsm_prepare(struct snd_pcm_substream *substream) return 0; } + lsm_ops = &cpe->lsm_ops; afe_ops = &cpe->afe_ops; afe_cfg = &(lsm_d->lsm_session->afe_port_cfg); - afe_cfg->port_id = 1; - afe_cfg->bit_width = 16; - afe_cfg->num_channels = 1; - afe_cfg->sample_rate = 16000; + switch (cpe->input_port_id) { + case AFE_PORT_ID_3: + afe_cfg->port_id = AFE_PORT_ID_3; + afe_cfg->bit_width = 16; + afe_cfg->num_channels = 1; + afe_cfg->sample_rate = SAMPLE_RATE_48KHZ; + rc = afe_ops->afe_port_cmd_cfg(cpe->core_handle, afe_cfg); + break; + case AFE_PORT_ID_1: + default: + afe_cfg->port_id = AFE_PORT_ID_1; + afe_cfg->bit_width = 16; + afe_cfg->num_channels = 1; + afe_cfg->sample_rate = SAMPLE_RATE_16KHZ; + rc = afe_ops->afe_set_params(cpe->core_handle, + afe_cfg, cpe->afe_mad_ctl); + break; + } - rc = afe_ops->afe_set_params(cpe->core_handle, - afe_cfg, cpe->afe_mad_ctl); if (rc != 0) { dev_err(rtd->dev, - "%s: cpe afe params failed, err = %d\n", - __func__, rc); + "%s: cpe afe params failed for port = %d, err = %d\n", + __func__, afe_cfg->port_id, rc); + return rc; + } + lsm_param.sample_rate = afe_cfg->sample_rate; + lsm_param.num_chs = afe_cfg->num_channels; + lsm_param.bit_width = afe_cfg->bit_width; + rc = lsm_ops->lsm_set_media_fmt_params(cpe->core_handle, lsm_session, + &lsm_param); + if (rc) + dev_dbg(rtd->dev, + "%s: failed to set lsm media fmt params, err = %d\n", + __func__, rc); + + /* Send connect to port (input) */ + rc = lsm_ops->lsm_set_port(cpe->core_handle, lsm_session, + &cpe->input_port_id); + if (rc) { + dev_err(rtd->dev, + "%s: Failed to set connect input port, err=%d\n", + __func__, rc); return rc; } + if (cpe->input_port_id != 3) { + rc = lsm_ops->lsm_get_afe_out_port_id(cpe->core_handle, + lsm_session); + if (rc != 0) { + dev_err(rtd->dev, + "%s: failed to get port id, err = %d\n", + __func__, rc); + return rc; + } + /* Send connect to port (output) */ + rc = lsm_ops->lsm_set_port(cpe->core_handle, lsm_session, + &lsm_session->afe_out_port_id); + if (rc) { + dev_err(rtd->dev, + "%s: Failed to set connect output port, err=%d\n", + __func__, rc); + return rc; + } + } rc = msm_cpe_afe_port_cntl(substream, cpe->core_handle, afe_ops, afe_cfg, @@ -2978,6 +3022,9 @@ static int msm_asoc_cpe_lsm_probe(struct snd_soc_platform *platform) struct cpe_priv *cpe_priv; const struct snd_kcontrol_new *kcontrol; bool found_runtime = false; + const char *cpe_dev_id = "qcom,msm-cpe-lsm-id"; + u32 port_id = 0; + int ret = 0; int i; if (!platform || !platform->component.card) { @@ -3007,6 +3054,14 @@ static int msm_asoc_cpe_lsm_probe(struct snd_soc_platform *platform) return -EINVAL; } + ret = of_property_read_u32(platform->dev->of_node, cpe_dev_id, + &port_id); + if (ret) { + dev_dbg(platform->dev, + "%s: missing 0x%x in dt node\n", __func__, port_id); + port_id = 1; + } + codec = rtd->codec; cpe_priv = kzalloc(sizeof(struct cpe_priv), @@ -3019,6 +3074,7 @@ static int msm_asoc_cpe_lsm_probe(struct snd_soc_platform *platform) } cpe_priv->codec = codec; + cpe_priv->input_port_id = port_id; wcd_cpe_get_lsm_ops(&cpe_priv->lsm_ops); wcd_cpe_get_afe_ops(&cpe_priv->afe_ops); diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c index e98e31c77e7a..85fa45ff400f 100644 --- a/sound/soc/msm/msm-dai-fe.c +++ b/sound/soc/msm/msm-dai-fe.c @@ -63,6 +63,7 @@ static int fe_dai_probe(struct snd_soc_dai *dai) dev_dbg(dai->dev, "%s src %s sink %s\n", __func__, intercon.source, intercon.sink); snd_soc_dapm_add_routes(dapm, &intercon, 1); + snd_soc_dapm_ignore_suspend(dapm, intercon.source); } if (dai->driver->capture.stream_name && dai->driver->capture.aif_name) { @@ -73,6 +74,7 @@ static int fe_dai_probe(struct snd_soc_dai *dai) dev_dbg(dai->dev, "%s src %s sink %s\n", __func__, intercon.source, intercon.sink); snd_soc_dapm_add_routes(dapm, &intercon, 1); + snd_soc_dapm_ignore_suspend(dapm, intercon.sink); } return 0; } diff --git a/sound/soc/msm/msmcobalt.c b/sound/soc/msm/msmcobalt.c index 706a9aec6a89..945250babf2d 100644 --- a/sound/soc/msm/msmcobalt.c +++ b/sound/soc/msm/msmcobalt.c @@ -170,7 +170,7 @@ static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight"}; static const char *const vi_feed_ch_text[] = {"One", "Two"}; -static char const *bit_format_text[] = {"S16_LE", "S24_LE"}; +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"}; static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", "KHZ_44P1", "KHZ_48", "KHZ_96", "KHZ_192"}; @@ -325,6 +325,9 @@ static int slim_get_bit_format_val(int bit_format) int val = 0; switch (bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + val = 2; + break; case SNDRV_PCM_FORMAT_S24_LE: val = 1; break; @@ -347,6 +350,9 @@ static int slim_get_bit_format(int val) case 1: bit_fmt = SNDRV_PCM_FORMAT_S24_LE; break; + case 2: + bit_fmt = SNDRV_PCM_FORMAT_S24_3LE; + break; default: bit_fmt = SNDRV_PCM_FORMAT_S16_LE; break; @@ -749,6 +755,9 @@ static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { switch (usb_rx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; case SNDRV_PCM_FORMAT_S24_LE: ucontrol->value.integer.value[0] = 1; break; @@ -770,6 +779,9 @@ static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol, int rc = 0; switch (ucontrol->value.integer.value[0]) { + case 2: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; case 1: usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; break; @@ -893,6 +905,9 @@ static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { switch (usb_tx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S24_3LE: + ucontrol->value.integer.value[0] = 2; + break; case SNDRV_PCM_FORMAT_S24_LE: ucontrol->value.integer.value[0] = 1; break; @@ -914,6 +929,9 @@ static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol, int rc = 0; switch (ucontrol->value.integer.value[0]) { + case 2: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; case 1: usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE; break; @@ -1058,7 +1076,7 @@ static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol, { pr_debug("%s: proxy_rx channels = %d\n", __func__, proxy_rx_cfg.channels); - ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 1; + ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 2; return 0; } @@ -1066,7 +1084,7 @@ static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol, static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 1; + proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 2; pr_debug("%s: proxy_rx channels = %d\n", __func__, proxy_rx_cfg.channels); @@ -1213,6 +1231,40 @@ static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit) } } +static int msm_slim_get_ch_from_beid(int32_t be_id) +{ + int ch_id = 0; + + switch (be_id) { + case MSM_BACKEND_DAI_SLIMBUS_0_RX: + ch_id = SLIM_RX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_1_RX: + ch_id = SLIM_RX_1; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_RX: + ch_id = SLIM_RX_3; + break; + case MSM_BACKEND_DAI_SLIMBUS_4_RX: + ch_id = SLIM_RX_4; + break; + case MSM_BACKEND_DAI_SLIMBUS_6_RX: + ch_id = SLIM_RX_6; + break; + case MSM_BACKEND_DAI_SLIMBUS_0_TX: + ch_id = SLIM_TX_0; + break; + case MSM_BACKEND_DAI_SLIMBUS_3_TX: + ch_id = SLIM_TX_3; + break; + default: + ch_id = SLIM_RX_0; + break; + } + + return ch_id; +} + static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -1224,6 +1276,7 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, int rc = 0; void *config = NULL; struct snd_soc_codec *codec = NULL; + int ch_num; pr_debug("%s: format = %d, rate = %d\n", __func__, params_format(params), params_rate(params)); @@ -1234,18 +1287,20 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, case MSM_BACKEND_DAI_SLIMBUS_3_RX: case MSM_BACKEND_DAI_SLIMBUS_4_RX: case MSM_BACKEND_DAI_SLIMBUS_6_RX: + ch_num = msm_slim_get_ch_from_beid(dai_link->be_id); param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, - slim_rx_cfg[0].bit_format); - rate->min = rate->max = slim_rx_cfg[0].sample_rate; - channels->min = channels->max = slim_rx_cfg[0].channels; + slim_rx_cfg[ch_num].bit_format); + rate->min = rate->max = slim_rx_cfg[ch_num].sample_rate; + channels->min = channels->max = slim_rx_cfg[ch_num].channels; break; case MSM_BACKEND_DAI_SLIMBUS_0_TX: case MSM_BACKEND_DAI_SLIMBUS_3_TX: + ch_num = msm_slim_get_ch_from_beid(dai_link->be_id); param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, - slim_tx_cfg[0].bit_format); - rate->min = rate->max = slim_tx_cfg[0].sample_rate; - channels->min = channels->max = slim_tx_cfg[0].channels; + slim_tx_cfg[ch_num].bit_format); + rate->min = rate->max = slim_tx_cfg[ch_num].sample_rate; + channels->min = channels->max = slim_tx_cfg[ch_num].channels; break; case MSM_BACKEND_DAI_SLIMBUS_1_TX: @@ -1320,15 +1375,11 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, case MSM_BACKEND_DAI_HDMI_RX: param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, hdmi_rx_cfg.bit_format); - if (channels->max < 2) - channels->min = channels->max = 2; rate->min = rate->max = hdmi_rx_cfg.sample_rate; channels->min = channels->max = hdmi_rx_cfg.channels; break; case MSM_BACKEND_DAI_AFE_PCM_RX: - if (channels->max < 2) - channels->min = channels->max = 2; channels->min = channels->max = proxy_rx_cfg.channels; rate->min = rate->max = SAMPLING_RATE_48KHZ; break; diff --git a/sound/soc/msm/qdsp6v2/audio_calibration.c b/sound/soc/msm/qdsp6v2/audio_calibration.c index c4ea4ed857ca..60d09dfaeb7f 100644 --- a/sound/soc/msm/qdsp6v2/audio_calibration.c +++ b/sound/soc/msm/qdsp6v2/audio_calibration.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014, 2016 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -490,7 +490,13 @@ static long audio_cal_shared_ioctl(struct file *file, unsigned int cmd, goto unlock; if (data == NULL) goto unlock; - if (copy_to_user((void *)arg, data, + if ((sizeof(data->hdr) + data->hdr.cal_type_size) > size) { + pr_err("%s: header size %zd plus cal type size %d are greater than data buffer size %d\n", + __func__, sizeof(data->hdr), + data->hdr.cal_type_size, size); + ret = -EFAULT; + goto unlock; + } else if (copy_to_user((void *)arg, data, sizeof(data->hdr) + data->hdr.cal_type_size)) { pr_err("%s: Could not copy cal type to user\n", __func__); diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c index 0af4b336acd7..08c2b89de646 100755 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -964,7 +964,8 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) return -EINVAL; } - if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE) + if ((prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE) || + (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_3LE)) bits_per_sample = 24; else if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S32_LE) bits_per_sample = 32; diff --git a/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c index 48180cf5e337..ad2f2e9865c3 100644 --- a/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c +++ b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c @@ -1523,8 +1523,9 @@ static int msm_ds2_dap_get_param(u32 cmd, void *arg) } /* Return if invalid length */ - if (dolby_data->length > - (DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM - DOLBY_PARAM_PAYLOAD_SIZE)) { + if ((dolby_data->length > + (DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM - DOLBY_PARAM_PAYLOAD_SIZE)) || + (dolby_data->length <= 0)) { pr_err("Invalid length %d", dolby_data->length); rc = -EINVAL; goto end; diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c index 3c75e30fb419..ff7cf5812c0c 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c @@ -50,7 +50,7 @@ struct msm_pcm_loopback { int capture_start; int session_id; struct audio_client *audio_client; - int volume; + uint32_t volume; }; struct fe_dai_session_map { @@ -65,6 +65,8 @@ static struct fe_dai_session_map session_map[LOOPBACK_SESSION_MAX] = { { {}, NULL}, }; +static u32 hfp_tx_mute; + static void stop_pcm(struct msm_pcm_loopback *pcm); static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd, struct msm_pcm_loopback **pcm); @@ -109,6 +111,13 @@ static void msm_pcm_loopback_event_handler(uint32_t opcode, uint32_t token, } } +static int msm_loopback_session_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = hfp_tx_mute; + return 0; +} + static int msm_loopback_session_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -123,7 +132,7 @@ static int msm_loopback_session_mute_put(struct snd_kcontrol *kcontrol, } pr_debug("%s: mute=%d\n", __func__, mute); - + hfp_tx_mute = mute; for (n = 0; n < LOOPBACK_SESSION_MAX; n++) { if (!strcmp(session_map[n].stream_name, "MultiMedia6")) pcm = session_map[n].loopback_priv; @@ -140,7 +149,8 @@ done: static struct snd_kcontrol_new msm_loopback_controls[] = { SOC_SINGLE_EXT("HFP TX Mute", SND_SOC_NOPM, 0, 1, 0, - NULL, msm_loopback_session_mute_put), + msm_loopback_session_mute_get, + msm_loopback_session_mute_put), }; static int msm_pcm_loopback_probe(struct snd_soc_platform *platform) @@ -150,7 +160,8 @@ static int msm_pcm_loopback_probe(struct snd_soc_platform *platform) return 0; } -static int pcm_loopback_set_volume(struct msm_pcm_loopback *prtd, int volume) +static int pcm_loopback_set_volume(struct msm_pcm_loopback *prtd, + uint32_t volume) { int rc = -EINVAL; @@ -459,10 +470,49 @@ static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol, int rc = 0; struct snd_pcm_volume *vol = kcontrol->private_data; struct snd_pcm_substream *substream = vol->pcm->streams[0].substream; - struct msm_pcm_loopback *prtd = substream->runtime->private_data; + struct msm_pcm_loopback *prtd; int volume = ucontrol->value.integer.value[0]; + pr_debug("%s: volume : 0x%x\n", __func__, volume); + if ((!substream) || (!substream->runtime)) { + pr_err("%s substream or runtime not found\n", __func__); + rc = -ENODEV; + goto exit; + } + prtd = substream->runtime->private_data; + if (!prtd) { + rc = -ENODEV; + goto exit; + } rc = pcm_loopback_set_volume(prtd, volume); + +exit: + return rc; +} + +static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol); + struct snd_pcm_substream *substream = + vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct msm_pcm_loopback *prtd; + + pr_debug("%s\n", __func__); + if ((!substream) || (!substream->runtime)) { + pr_err("%s substream or runtime not found\n", __func__); + rc = -ENODEV; + goto exit; + } + prtd = substream->runtime->private_data; + if (!prtd) { + rc = -ENODEV; + goto exit; + } + ucontrol->value.integer.value[0] = prtd->volume; + +exit: return rc; } @@ -482,6 +532,7 @@ static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd) return ret; kctl = volume_info->kctl; kctl->put = msm_pcm_volume_ctl_put; + kctl->get = msm_pcm_volume_ctl_get; kctl->tlv.p = loopback_rx_vol_gain; return 0; } diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c index e4145509d63c..9c5219392460 100644 --- a/sound/soc/samsung/ac97.c +++ b/sound/soc/samsung/ac97.c @@ -324,7 +324,7 @@ static const struct snd_soc_component_driver s3c_ac97_component = { static int s3c_ac97_probe(struct platform_device *pdev) { - struct resource *mem_res, *dmatx_res, *dmarx_res, *dmamic_res, *irq_res; + struct resource *mem_res, *irq_res; struct s3c_audio_pdata *ac97_pdata; int ret; @@ -335,24 +335,6 @@ static int s3c_ac97_probe(struct platform_device *pdev) } /* Check for availability of necessary resource */ - dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!dmatx_res) { - dev_err(&pdev->dev, "Unable to get AC97-TX dma resource\n"); - return -ENXIO; - } - - dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!dmarx_res) { - dev_err(&pdev->dev, "Unable to get AC97-RX dma resource\n"); - return -ENXIO; - } - - dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2); - if (!dmamic_res) { - dev_err(&pdev->dev, "Unable to get AC97-MIC dma resource\n"); - return -ENXIO; - } - irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!irq_res) { dev_err(&pdev->dev, "AC97 IRQ not provided!\n"); @@ -364,11 +346,11 @@ static int s3c_ac97_probe(struct platform_device *pdev) if (IS_ERR(s3c_ac97.regs)) return PTR_ERR(s3c_ac97.regs); - s3c_ac97_pcm_out.channel = dmatx_res->start; + s3c_ac97_pcm_out.slave = ac97_pdata->dma_playback; s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA; - s3c_ac97_pcm_in.channel = dmarx_res->start; + s3c_ac97_pcm_in.slave = ac97_pdata->dma_capture; s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA; - s3c_ac97_mic_in.channel = dmamic_res->start; + s3c_ac97_mic_in.slave = ac97_pdata->dma_capture_mic; s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA; init_completion(&s3c_ac97.done); diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h index 0e85dcfec023..085ef30f5ca2 100644 --- a/sound/soc/samsung/dma.h +++ b/sound/soc/samsung/dma.h @@ -15,7 +15,7 @@ #include <sound/dmaengine_pcm.h> struct s3c_dma_params { - int channel; /* Channel ID */ + void *slave; /* Channel ID */ dma_addr_t dma_addr; int dma_size; /* Size of the DMA transfer */ char *ch_name; diff --git a/sound/soc/samsung/dmaengine.c b/sound/soc/samsung/dmaengine.c index 506f5bf6d082..727008d57d14 100644 --- a/sound/soc/samsung/dmaengine.c +++ b/sound/soc/samsung/dmaengine.c @@ -50,14 +50,14 @@ void samsung_asoc_init_dma_data(struct snd_soc_dai *dai, if (playback) { playback_data = &playback->dma_data; - playback_data->filter_data = (void *)playback->channel; + playback_data->filter_data = playback->slave; playback_data->chan_name = playback->ch_name; playback_data->addr = playback->dma_addr; playback_data->addr_width = playback->dma_size; } if (capture) { capture_data = &capture->dma_data; - capture_data->filter_data = (void *)capture->channel; + capture_data->filter_data = capture->slave; capture_data->chan_name = capture->ch_name; capture_data->addr = capture->dma_addr; capture_data->addr_width = capture->dma_size; diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 7dbf899b2af2..e163b0148c4b 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1260,27 +1260,14 @@ static int samsung_i2s_probe(struct platform_device *pdev) pri_dai->lock = &pri_dai->spinlock; if (!np) { - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!res) { - dev_err(&pdev->dev, - "Unable to get I2S-TX dma resource\n"); - return -ENXIO; - } - pri_dai->dma_playback.channel = res->start; - - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!res) { - dev_err(&pdev->dev, - "Unable to get I2S-RX dma resource\n"); - return -ENXIO; - } - pri_dai->dma_capture.channel = res->start; - if (i2s_pdata == NULL) { dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n"); return -EINVAL; } + pri_dai->dma_playback.slave = i2s_pdata->dma_playback; + pri_dai->dma_capture.slave = i2s_pdata->dma_capture; + if (&i2s_pdata->type) i2s_cfg = &i2s_pdata->type.i2s; @@ -1341,11 +1328,8 @@ static int samsung_i2s_probe(struct platform_device *pdev) sec_dai->dma_playback.dma_addr = regs_base + I2STXDS; sec_dai->dma_playback.ch_name = "tx-sec"; - if (!np) { - res = platform_get_resource(pdev, IORESOURCE_DMA, 2); - if (res) - sec_dai->dma_playback.channel = res->start; - } + if (!np) + sec_dai->dma_playback.slave = i2s_pdata->dma_play_sec; sec_dai->dma_playback.dma_size = 4; sec_dai->addr = pri_dai->addr; diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c index b320a9d3fbf8..c77f324e0bb8 100644 --- a/sound/soc/samsung/pcm.c +++ b/sound/soc/samsung/pcm.c @@ -486,7 +486,7 @@ static const struct snd_soc_component_driver s3c_pcm_component = { static int s3c_pcm_dev_probe(struct platform_device *pdev) { struct s3c_pcm_info *pcm; - struct resource *mem_res, *dmatx_res, *dmarx_res; + struct resource *mem_res; struct s3c_audio_pdata *pcm_pdata; int ret; @@ -499,18 +499,6 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev) pcm_pdata = pdev->dev.platform_data; /* Check for availability of necessary resource */ - dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!dmatx_res) { - dev_err(&pdev->dev, "Unable to get PCM-TX dma resource\n"); - return -ENXIO; - } - - dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!dmarx_res) { - dev_err(&pdev->dev, "Unable to get PCM-RX dma resource\n"); - return -ENXIO; - } - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem_res) { dev_err(&pdev->dev, "Unable to get register resource\n"); @@ -568,8 +556,10 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev) s3c_pcm_stereo_out[pdev->id].dma_addr = mem_res->start + S3C_PCM_TXFIFO; - s3c_pcm_stereo_in[pdev->id].channel = dmarx_res->start; - s3c_pcm_stereo_out[pdev->id].channel = dmatx_res->start; + if (pcm_pdata) { + s3c_pcm_stereo_in[pdev->id].slave = pcm_pdata->dma_capture; + s3c_pcm_stereo_out[pdev->id].slave = pcm_pdata->dma_playback; + } pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id]; pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id]; diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c index 2b766d212ce0..77d27c85a32a 100644 --- a/sound/soc/samsung/s3c2412-i2s.c +++ b/sound/soc/samsung/s3c2412-i2s.c @@ -34,13 +34,13 @@ #include "s3c2412-i2s.h" static struct s3c_dma_params s3c2412_i2s_pcm_stereo_out = { - .channel = DMACH_I2S_OUT, + .slave = (void *)(uintptr_t)DMACH_I2S_OUT, .ch_name = "tx", .dma_size = 4, }; static struct s3c_dma_params s3c2412_i2s_pcm_stereo_in = { - .channel = DMACH_I2S_IN, + .slave = (void *)(uintptr_t)DMACH_I2S_IN, .ch_name = "rx", .dma_size = 4, }; diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c index 5bf723689692..9da3a77ea2c7 100644 --- a/sound/soc/samsung/s3c24xx-i2s.c +++ b/sound/soc/samsung/s3c24xx-i2s.c @@ -32,13 +32,13 @@ #include "s3c24xx-i2s.h" static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_out = { - .channel = DMACH_I2S_OUT, + .slave = (void *)(uintptr_t)DMACH_I2S_OUT, .ch_name = "tx", .dma_size = 2, }; static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_in = { - .channel = DMACH_I2S_IN, + .slave = (void *)(uintptr_t)DMACH_I2S_IN, .ch_name = "rx", .dma_size = 2, }; diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c index 36dbc0e96004..9dd7ee6d03ff 100644 --- a/sound/soc/samsung/spdif.c +++ b/sound/soc/samsung/spdif.c @@ -359,7 +359,7 @@ static const struct snd_soc_component_driver samsung_spdif_component = { static int spdif_probe(struct platform_device *pdev) { struct s3c_audio_pdata *spdif_pdata; - struct resource *mem_res, *dma_res; + struct resource *mem_res; struct samsung_spdif_info *spdif; int ret; @@ -367,12 +367,6 @@ static int spdif_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Entered %s\n", __func__); - dma_res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!dma_res) { - dev_err(&pdev->dev, "Unable to get dma resource.\n"); - return -ENXIO; - } - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem_res) { dev_err(&pdev->dev, "Unable to get register resource.\n"); @@ -432,7 +426,7 @@ static int spdif_probe(struct platform_device *pdev) spdif_stereo_out.dma_size = 2; spdif_stereo_out.dma_addr = mem_res->start + DATA_OUTBUF; - spdif_stereo_out.channel = dma_res->start; + spdif_stereo_out.slave = spdif_pdata ? spdif_pdata->dma_playback : NULL; spdif->dma_playback = &spdif_stereo_out; diff --git a/sound/usb/card.c b/sound/usb/card.c index e94f4d2f2620..524688e4c144 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -643,6 +643,7 @@ static int usb_audio_probe(struct usb_interface *intf, usb_chip[chip->index] = chip; chip->num_interfaces++; usb_set_intfdata(intf, chip); + usb_enable_autosuspend(chip->dev); atomic_dec(&chip->active); mutex_unlock(®ister_mutex); return 0; diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 2ed260b10f6d..7ccbcaf6a147 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -285,6 +285,8 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, unsigned char data[3]; int err, crate; + if (get_iface_desc(alts)->bNumEndpoints < 1) + return -EINVAL; ep = get_endpoint(alts, 0)->bEndpointAddress; /* if endpoint doesn't have sampling rate control, bail out */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 7b1cb365ffab..c07a7eda42a2 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -438,6 +438,9 @@ exit_clear: * * New endpoints will be added to chip->ep_list and must be freed by * calling snd_usb_endpoint_free(). + * + * For SND_USB_ENDPOINT_TYPE_SYNC, the caller needs to guarantee that + * bNumEndpoints > 1 beforehand. */ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, struct usb_host_interface *alts, diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index ddca6547399b..1f8fb0d904e0 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -349,6 +349,16 @@ static struct usbmix_name_map bose_companion5_map[] = { }; /* + * Dell usb dock with ALC4020 codec had a firmware problem where it got + * screwed up when zero volume is passed; just skip it as a workaround + */ +static const struct usbmix_name_map dell_alc4020_map[] = { + { 16, NULL }, + { 19, NULL }, + { 0 } +}; + +/* * Control map entries */ @@ -431,6 +441,10 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = { .map = aureon_51_2_map, }, { + .id = USB_ID(0x0bda, 0x4014), + .map = dell_alc4020_map, + }, + { .id = USB_ID(0x0dba, 0x1000), .map = mbox1_map, }, diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 279025650568..f6c3bf79af9a 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -1519,7 +1519,11 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, /* use known values for that card: interface#1 altsetting#1 */ iface = usb_ifnum_to_if(chip->dev, 1); + if (!iface || iface->num_altsetting < 2) + return -EINVAL; alts = &iface->altsetting[1]; + if (get_iface_desc(alts)->bNumEndpoints < 1) + return -EINVAL; ep = get_endpoint(alts, 0)->bEndpointAddress; err = snd_usb_ctl_msg(chip->dev, diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 9a9b789a56ef..3033bf7ce309 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -159,6 +159,8 @@ static int init_pitch_v1(struct snd_usb_audio *chip, int iface, unsigned char data[1]; int err; + if (get_iface_desc(alts)->bNumEndpoints < 1) + return -EINVAL; ep = get_endpoint(alts, 0)->bEndpointAddress; data[0] = 1; diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index c458d60d5030..001fb4dc0722 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -150,6 +150,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, usb_audio_err(chip, "cannot memdup\n"); return -ENOMEM; } + INIT_LIST_HEAD(&fp->list); if (fp->nr_rates > MAX_NR_RATES) { kfree(fp); return -EINVAL; @@ -167,19 +168,20 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, stream = (fp->endpoint & USB_DIR_IN) ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; err = snd_usb_add_audio_stream(chip, stream, fp); - if (err < 0) { - kfree(fp); - kfree(rate_table); - return err; - } + if (err < 0) + goto error; if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber || fp->altset_idx >= iface->num_altsetting) { - kfree(fp); - kfree(rate_table); - return -EINVAL; + err = -EINVAL; + goto error; } alts = &iface->altsetting[fp->altset_idx]; altsd = get_iface_desc(alts); + if (altsd->bNumEndpoints < 1) { + err = -EINVAL; + goto error; + } + fp->protocol = altsd->bInterfaceProtocol; if (fp->datainterval == 0) @@ -190,6 +192,12 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, snd_usb_init_pitch(chip, fp->iface, alts, fp); snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max); return 0; + + error: + list_del(&fp->list); /* unlink for avoiding double-free */ + kfree(fp); + kfree(rate_table); + return err; } static int create_auto_pcm_quirk(struct snd_usb_audio *chip, @@ -462,6 +470,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; fp->datainterval = 0; fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); + INIT_LIST_HEAD(&fp->list); switch (fp->maxpacksize) { case 0x120: @@ -485,6 +494,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; err = snd_usb_add_audio_stream(chip, stream, fp); if (err < 0) { + list_del(&fp->list); /* unlink for avoiding double-free */ kfree(fp); return err; } @@ -1121,12 +1131,15 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip) switch (chip->usb_id) { case USB_ID(0x045E, 0x075D): /* MS Lifecam Cinema */ case USB_ID(0x045E, 0x076D): /* MS Lifecam HD-5000 */ + case USB_ID(0x045E, 0x076E): /* MS Lifecam HD-5001 */ case USB_ID(0x045E, 0x076F): /* MS Lifecam HD-6000 */ case USB_ID(0x045E, 0x0772): /* MS Lifecam Studio */ case USB_ID(0x045E, 0x0779): /* MS Lifecam HD-3000 */ + case USB_ID(0x047F, 0x0415): /* Plantronics BT-300 */ case USB_ID(0x047F, 0xAA05): /* Plantronics DA45 */ case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */ case USB_ID(0x074D, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */ + case USB_ID(0x1de7, 0x0014): /* Phoenix Audio TMX320 */ case USB_ID(0x21B4, 0x0081): /* AudioQuest DragonFly */ return true; } diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 81172a395e55..98b2c28ae46b 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -321,7 +321,9 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits, /* * add this endpoint to the chip instance. * if a stream with the same endpoint already exists, append to it. - * if not, create a new pcm stream. + * if not, create a new pcm stream. note, fp is added to the substream + * fmt_list and will be freed on the chip instance release. do not free + * fp or do remove it from the substream fmt_list to avoid double-free. */ int snd_usb_add_audio_stream(struct snd_usb_audio *chip, int stream, @@ -682,6 +684,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) * (fp->maxpacksize & 0x7ff); fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no); fp->clock = clock; + INIT_LIST_HEAD(&fp->list); /* some quirks for attributes here */ @@ -730,6 +733,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint); err = snd_usb_add_audio_stream(chip, stream, fp); if (err < 0) { + list_del(&fp->list); /* unlink for avoiding double-free */ kfree(fp->rate_table); kfree(fp->chmap); kfree(fp); diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c index 9d2b75876bfe..94c051773fd9 100644 --- a/sound/usb/usb_audio_qmi_svc.c +++ b/sound/usb/usb_audio_qmi_svc.c @@ -405,12 +405,15 @@ static int prepare_qmi_response(struct snd_usb_substream *subs, subs->interface, subs->altset_idx); goto err; } - resp->bDelay = as->bDelay; + resp->data_path_delay = as->bDelay; + resp->data_path_delay_valid = 1; fmt_v1 = (struct uac_format_type_i_discrete_descriptor *)fmt; - resp->bSubslotSize = fmt_v1->bSubframeSize; + resp->usb_audio_subslot_size = fmt_v1->bSubframeSize; + resp->usb_audio_subslot_size_valid = 1; } else if (protocol == UAC_VERSION_2) { fmt_v2 = (struct uac_format_type_i_ext_descriptor *)fmt; - resp->bSubslotSize = fmt_v2->bSubslotSize; + resp->usb_audio_subslot_size = fmt_v2->bSubslotSize; + resp->usb_audio_subslot_size_valid = 1; } else { pr_err("%s: unknown protocol version %x\n", __func__, protocol); goto err; @@ -424,11 +427,14 @@ static int prepare_qmi_response(struct snd_usb_substream *subs, subs->interface, subs->altset_idx); goto err; } - resp->bcdADC = ac->bcdADC; + resp->usb_audio_spec_revision = ac->bcdADC; + resp->usb_audio_spec_revision_valid = 1; resp->slot_id = subs->dev->slot_id; + resp->slot_id_valid = 1; memcpy(&resp->std_as_opr_intf_desc, &alts->desc, sizeof(alts->desc)); + resp->std_as_opr_intf_desc_valid = 1; ep = usb_pipe_endpoint(subs->dev, subs->data_endpoint->pipe); if (!ep) { @@ -437,6 +443,7 @@ static int prepare_qmi_response(struct snd_usb_substream *subs, goto err; } memcpy(&resp->std_as_data_ep_desc, &ep->desc, sizeof(ep->desc)); + resp->std_as_data_ep_desc_valid = 1; xhci_pa = usb_get_xfer_ring_dma_addr(subs->dev, ep); if (!xhci_pa) { @@ -454,6 +461,8 @@ static int prepare_qmi_response(struct snd_usb_substream *subs, goto err; } memcpy(&resp->std_as_sync_ep_desc, &ep->desc, sizeof(ep->desc)); + resp->std_as_sync_ep_desc_valid = 1; + xhci_pa = usb_get_xfer_ring_dma_addr(subs->dev, ep); if (!xhci_pa) { pr_err("%s:failed to get sync ep ring dma address\n", @@ -464,6 +473,7 @@ static int prepare_qmi_response(struct snd_usb_substream *subs, } resp->interrupter_num = uaudio_qdev->intr_num; + resp->interrupter_num_valid = 1; /* map xhci data structures PA memory to iova */ @@ -570,6 +580,8 @@ skip_sync: resp->xhci_mem_info.xfer_buff.va = PREPEND_SID_TO_IOVA(va, uaudio_qdev->sid); + resp->xhci_mem_info_valid = 1; + if (!atomic_read(&uadev[card_num].in_use)) { kref_init(&uadev[card_num].kref); init_waitqueue_head(&uadev[card_num].disconnect_wq); @@ -734,7 +746,7 @@ static void uaudio_dev_release(struct kref *kref) static int handle_uaudio_stream_req(void *req_h, void *req) { struct qmi_uaudio_stream_req_msg_v01 *req_msg; - struct qmi_uaudio_stream_resp_msg_v01 resp = {0}; + struct qmi_uaudio_stream_resp_msg_v01 resp = {{0}, 0}; struct snd_usb_substream *subs; struct snd_usb_audio *chip = NULL; struct uaudio_qmi_svc *svc = uaudio_svc; @@ -744,6 +756,13 @@ static int handle_uaudio_stream_req(void *req_h, void *req) req_msg = (struct qmi_uaudio_stream_req_msg_v01 *)req; + if (!req_msg->audio_format_valid || !req_msg->bit_rate_valid || + !req_msg->number_of_ch_valid || !req_msg->xfer_buff_size_valid) { + pr_err("%s: invalid request msg\n", __func__); + ret = -EINVAL; + goto response; + } + direction = req_msg->usb_token & SND_PCM_STREAM_DIRECTION; pcm_dev_num = (req_msg->usb_token & SND_PCM_DEV_NUM_MASK) >> 8; pcm_card_num = (req_msg->usb_token & SND_PCM_CARD_NUM_MASK) >> 16; @@ -828,7 +847,12 @@ response: uaudio_dev_release); } - resp.status = ret; + resp.usb_token = req_msg->usb_token; + resp.usb_token_valid = 1; + resp.internal_status = ret; + resp.internal_status_valid = 1; + resp.status = ret ? USB_AUDIO_STREAM_REQ_FAILURE_V01 : ret; + resp.status_valid = 1; ret = qmi_send_resp_from_cb(svc->uaudio_svc_hdl, svc->curr_conn, req_h, &uaudio_stream_resp_desc, &resp, sizeof(resp)); diff --git a/sound/usb/usb_audio_qmi_v01.c b/sound/usb/usb_audio_qmi_v01.c index 31b1ba74d5c7..6f6f194e89fb 100644 --- a/sound/usb/usb_audio_qmi_v01.c +++ b/sound/usb/usb_audio_qmi_v01.c @@ -280,65 +280,92 @@ static struct elem_info usb_interface_descriptor_v01_ei[] = { struct elem_info qmi_uaudio_stream_req_msg_v01_ei[] = { { - .data_type = QMI_UNSIGNED_4_BYTE, + .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 1, - .elem_size = sizeof(uint32_t), + .elem_size = sizeof(uint8_t), .is_array = NO_ARRAY, .tlv_type = 0x01, .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, - priv_data), + enable), }, { - .data_type = QMI_UNSIGNED_1_BYTE, + .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, - .elem_size = sizeof(uint8_t), + .elem_size = sizeof(uint32_t), .is_array = NO_ARRAY, .tlv_type = 0x02, .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, - enable), + usb_token), }, { - .data_type = QMI_UNSIGNED_4_BYTE, + .data_type = QMI_OPT_FLAG, .elem_len = 1, - .elem_size = sizeof(uint32_t), + .elem_size = sizeof(uint8_t), .is_array = NO_ARRAY, - .tlv_type = 0x03, + .tlv_type = 0x10, .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, - usb_token), + audio_format_valid), }, { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, .elem_size = sizeof(uint32_t), .is_array = NO_ARRAY, - .tlv_type = 0x04, + .tlv_type = 0x10, .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, audio_format), }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, + number_of_ch_valid), + }, + { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, .elem_size = sizeof(uint32_t), .is_array = NO_ARRAY, - .tlv_type = 0x05, + .tlv_type = 0x11, .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, number_of_ch), }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, + bit_rate_valid), + }, + { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, .elem_size = sizeof(uint32_t), .is_array = NO_ARRAY, - .tlv_type = 0x06, + .tlv_type = 0x12, .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, bit_rate), }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, + xfer_buff_size_valid), + }, + { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, .elem_size = sizeof(uint32_t), .is_array = NO_ARRAY, - .tlv_type = 0x07, + .tlv_type = 0x13, .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, xfer_buff_size), }, @@ -351,115 +378,256 @@ struct elem_info qmi_uaudio_stream_req_msg_v01_ei[] = { struct elem_info qmi_uaudio_stream_resp_msg_v01_ei[] = { { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + status_valid), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum usb_audio_stream_status_enum_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + status), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + internal_status_valid), + }, + { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, .elem_size = sizeof(uint32_t), .is_array = NO_ARRAY, - .tlv_type = 0x01, + .tlv_type = 0x11, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + internal_status), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x12, .offset = offsetof( struct qmi_uaudio_stream_resp_msg_v01, - priv_data), + slot_id_valid), }, { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, .elem_size = sizeof(uint32_t), .is_array = NO_ARRAY, - .tlv_type = 0x02, + .tlv_type = 0x12, .offset = offsetof( struct qmi_uaudio_stream_resp_msg_v01, - status), + slot_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + usb_token_valid), }, { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, .elem_size = sizeof(uint32_t), .is_array = NO_ARRAY, - .tlv_type = 0x03, + .tlv_type = 0x13, .offset = offsetof( struct qmi_uaudio_stream_resp_msg_v01, - slot_id), + usb_token), }, { - .data_type = QMI_UNSIGNED_1_BYTE, + .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(uint8_t), .is_array = NO_ARRAY, - .tlv_type = 0x04, + .tlv_type = 0x14, .offset = offsetof( struct qmi_uaudio_stream_resp_msg_v01, - bSubslotSize), + std_as_opr_intf_desc_valid), }, { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct usb_interface_descriptor_v01), .is_array = NO_ARRAY, - .tlv_type = 0x05, + .tlv_type = 0x14, .offset = offsetof( struct qmi_uaudio_stream_resp_msg_v01, std_as_opr_intf_desc), .ei_array = usb_interface_descriptor_v01_ei, }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + std_as_data_ep_desc_valid), + }, + { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct usb_endpoint_descriptor_v01), .is_array = NO_ARRAY, - .tlv_type = 0x06, + .tlv_type = 0x15, .offset = offsetof( struct qmi_uaudio_stream_resp_msg_v01, std_as_data_ep_desc), .ei_array = usb_endpoint_descriptor_v01_ei, }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + std_as_sync_ep_desc_valid), + }, + { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct usb_endpoint_descriptor_v01), .is_array = NO_ARRAY, - .tlv_type = 0x07, + .tlv_type = 0x16, .offset = offsetof( struct qmi_uaudio_stream_resp_msg_v01, std_as_sync_ep_desc), .ei_array = usb_endpoint_descriptor_v01_ei, }, { - .data_type = QMI_UNSIGNED_1_BYTE, + .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(uint8_t), .is_array = NO_ARRAY, - .tlv_type = 0x08, + .tlv_type = 0x17, .offset = offsetof( struct qmi_uaudio_stream_resp_msg_v01, - bDelay), + usb_audio_spec_revision_valid), }, { .data_type = QMI_UNSIGNED_2_BYTE, .elem_len = 1, .elem_size = sizeof(uint16_t), .is_array = NO_ARRAY, - .tlv_type = 0x09, + .tlv_type = 0x17, .offset = offsetof( struct qmi_uaudio_stream_resp_msg_v01, - bcdADC), + usb_audio_spec_revision), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + data_path_delay_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + data_path_delay), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + usb_audio_subslot_size_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + usb_audio_subslot_size), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x1A, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + xhci_mem_info_valid), }, { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct apps_mem_info_v01), .is_array = NO_ARRAY, - .tlv_type = 0x0A, + .tlv_type = 0x1A, .offset = offsetof( struct qmi_uaudio_stream_resp_msg_v01, xhci_mem_info), .ei_array = apps_mem_info_v01_ei, }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x1B, + .offset = offsetof( + struct qmi_uaudio_stream_resp_msg_v01, + interrupter_num_valid), + }, + { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 1, .elem_size = sizeof(uint8_t), .is_array = NO_ARRAY, - .tlv_type = 0x0B, + .tlv_type = 0x1B, .offset = offsetof( struct qmi_uaudio_stream_resp_msg_v01, interrupter_num), @@ -473,13 +641,14 @@ struct elem_info qmi_uaudio_stream_resp_msg_v01_ei[] = { struct elem_info qmi_uaudio_stream_ind_msg_v01_ei[] = { { - .data_type = QMI_UNSIGNED_4_BYTE, + .data_type = QMI_SIGNED_4_BYTE_ENUM, .elem_len = 1, - .elem_size = sizeof(uint32_t), + .elem_size = sizeof( + enum usb_audio_device_indication_enum_v01), .is_array = NO_ARRAY, .tlv_type = 0x01, .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, - usb_token), + dev_event), }, { .data_type = QMI_UNSIGNED_4_BYTE, @@ -488,76 +657,175 @@ struct elem_info qmi_uaudio_stream_ind_msg_v01_ei[] = { .is_array = NO_ARRAY, .tlv_type = 0x02, .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, - priv_data), + slot_id), }, { - .data_type = QMI_UNSIGNED_4_BYTE, + .data_type = QMI_OPT_FLAG, .elem_len = 1, - .elem_size = sizeof(uint32_t), + .elem_size = sizeof(uint8_t), .is_array = NO_ARRAY, - .tlv_type = 0x03, + .tlv_type = 0x10, .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, - status), + usb_token_valid), }, { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, .elem_size = sizeof(uint32_t), .is_array = NO_ARRAY, - .tlv_type = 0x04, + .tlv_type = 0x10, .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, - slot_id), + usb_token), }, { - .data_type = QMI_UNSIGNED_1_BYTE, + .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(uint8_t), .is_array = NO_ARRAY, - .tlv_type = 0x05, + .tlv_type = 0x11, .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, - bSubslotSize), + std_as_opr_intf_desc_valid), }, { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct usb_interface_descriptor_v01), .is_array = NO_ARRAY, - .tlv_type = 0x06, + .tlv_type = 0x11, .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, std_as_opr_intf_desc), .ei_array = usb_interface_descriptor_v01_ei, }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + std_as_data_ep_desc_valid), + }, + { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct usb_endpoint_descriptor_v01), .is_array = NO_ARRAY, - .tlv_type = 0x07, + .tlv_type = 0x12, .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, std_as_data_ep_desc), .ei_array = usb_endpoint_descriptor_v01_ei, }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + std_as_sync_ep_desc_valid), + }, + { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct usb_endpoint_descriptor_v01), .is_array = NO_ARRAY, - .tlv_type = 0x08, + .tlv_type = 0x13, .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, std_as_sync_ep_desc), .ei_array = usb_endpoint_descriptor_v01_ei, }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + usb_audio_spec_revision_valid), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint16_t), + .is_array = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + usb_audio_spec_revision), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + data_path_delay_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + data_path_delay), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + usb_audio_subslot_size_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + usb_audio_subslot_size), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + xhci_mem_info_valid), + }, + { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct apps_mem_info_v01), .is_array = NO_ARRAY, - .tlv_type = 0x09, + .tlv_type = 0x17, .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, xhci_mem_info), .ei_array = apps_mem_info_v01_ei, }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + interrupter_num_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct qmi_uaudio_stream_ind_msg_v01, + interrupter_num), + }, + { .data_type = QMI_EOTI, .is_array = NO_ARRAY, .is_array = QMI_COMMON_TLV_TYPE, diff --git a/sound/usb/usb_audio_qmi_v01.h b/sound/usb/usb_audio_qmi_v01.h index 7ad1ab8a61a9..aa1018a22105 100644 --- a/sound/usb/usb_audio_qmi_v01.h +++ b/sound/usb/usb_audio_qmi_v01.h @@ -13,7 +13,7 @@ #ifndef USB_QMI_V01_H #define USB_QMI_V01_H -#define UAUDIO_STREAM_SERVICE_ID_V01 0x41C +#define UAUDIO_STREAM_SERVICE_ID_V01 0x41D #define UAUDIO_STREAM_SERVICE_VERS_V01 0x01 #define QMI_UAUDIO_STREAM_RESP_V01 0x0001 @@ -58,46 +58,93 @@ struct usb_interface_descriptor_v01 { uint8_t iInterface; }; +enum usb_audio_stream_status_enum_v01 { + USB_AUDIO_STREAM_STATUS_ENUM_MIN_VAL_V01 = INT_MIN, + USB_AUDIO_STREAM_REQ_SUCCESS_V01 = 0, + USB_AUDIO_STREAM_REQ_FAILURE_V01 = 1, + USB_AUDIO_STREAM_REQ_FAILURE_NOT_FOUND_V01 = 2, + USB_AUDIO_STREAM_REQ_FAILURE_INVALID_PARAM_V01 = 3, + USB_AUDIO_STREAM_REQ_FAILURE_MEMALLOC_V01 = 4, + USB_AUDIO_STREAM_STATUS_ENUM_MAX_VAL_V01 = INT_MAX, +}; + +enum usb_audio_device_indication_enum_v01 { + USB_AUDIO_DEVICE_INDICATION_ENUM_MIN_VAL_V01 = INT_MIN, + USB_AUDIO_DEV_CONNECT_V01 = 0, + USB_AUDIO_DEV_DISCONNECT_V01 = 1, + USB_AUDIO_DEV_SUSPEND_V01 = 2, + USB_AUDIO_DEV_RESUME_V01 = 3, + USB_AUDIO_DEVICE_INDICATION_ENUM_MAX_VAL_V01 = INT_MAX, +}; + struct qmi_uaudio_stream_req_msg_v01 { - uint32_t priv_data; uint8_t enable; uint32_t usb_token; + uint8_t audio_format_valid; uint32_t audio_format; + uint8_t number_of_ch_valid; uint32_t number_of_ch; + uint8_t bit_rate_valid; uint32_t bit_rate; + uint8_t xfer_buff_size_valid; uint32_t xfer_buff_size; }; -#define QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN 46 +#define QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN 39 extern struct elem_info qmi_uaudio_stream_req_msg_v01_ei[]; struct qmi_uaudio_stream_resp_msg_v01 { - uint32_t priv_data; - uint32_t status; + struct qmi_response_type_v01 resp; + uint8_t status_valid; + enum usb_audio_stream_status_enum_v01 status; + uint8_t internal_status_valid; + uint32_t internal_status; + uint8_t slot_id_valid; uint32_t slot_id; - uint8_t bSubslotSize; + uint8_t usb_token_valid; + uint32_t usb_token; + uint8_t std_as_opr_intf_desc_valid; struct usb_interface_descriptor_v01 std_as_opr_intf_desc; + uint8_t std_as_data_ep_desc_valid; struct usb_endpoint_descriptor_v01 std_as_data_ep_desc; + uint8_t std_as_sync_ep_desc_valid; struct usb_endpoint_descriptor_v01 std_as_sync_ep_desc; - uint8_t bDelay; - uint16_t bcdADC; + uint8_t usb_audio_spec_revision_valid; + uint16_t usb_audio_spec_revision; + uint8_t data_path_delay_valid; + uint8_t data_path_delay; + uint8_t usb_audio_subslot_size_valid; + uint8_t usb_audio_subslot_size; + uint8_t xhci_mem_info_valid; struct apps_mem_info_v01 xhci_mem_info; + uint8_t interrupter_num_valid; uint8_t interrupter_num; }; -#define QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN 177 +#define QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN 191 extern struct elem_info qmi_uaudio_stream_resp_msg_v01_ei[]; struct qmi_uaudio_stream_ind_msg_v01 { - uint32_t usb_token; - uint32_t priv_data; - uint32_t status; + enum usb_audio_device_indication_enum_v01 dev_event; uint32_t slot_id; - uint8_t bSubslotSize; + uint8_t usb_token_valid; + uint32_t usb_token; + uint8_t std_as_opr_intf_desc_valid; struct usb_interface_descriptor_v01 std_as_opr_intf_desc; + uint8_t std_as_data_ep_desc_valid; struct usb_endpoint_descriptor_v01 std_as_data_ep_desc; + uint8_t std_as_sync_ep_desc_valid; struct usb_endpoint_descriptor_v01 std_as_sync_ep_desc; + uint8_t usb_audio_spec_revision_valid; + uint16_t usb_audio_spec_revision; + uint8_t data_path_delay_valid; + uint8_t data_path_delay; + uint8_t usb_audio_subslot_size_valid; + uint8_t usb_audio_subslot_size; + uint8_t xhci_mem_info_valid; struct apps_mem_info_v01 xhci_mem_info; + uint8_t interrupter_num_valid; + uint8_t interrupter_num; }; -#define QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN 171 +#define QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN 177 extern struct elem_info qmi_uaudio_stream_ind_msg_v01_ei[]; #endif diff --git a/tools/hv/Makefile b/tools/hv/Makefile index a8ab79556926..a8c4644022a6 100644 --- a/tools/hv/Makefile +++ b/tools/hv/Makefile @@ -5,6 +5,8 @@ PTHREAD_LIBS = -lpthread WARNINGS = -Wall -Wextra CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS) $(shell getconf LFS_CFLAGS) +CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include + all: hv_kvp_daemon hv_vss_daemon hv_fcopy_daemon %: %.c $(CC) $(CFLAGS) -o $@ $^ diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index b48e87693aa5..a35db828bd0d 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -2101,11 +2101,11 @@ char *parse_events_formats_error_string(char *additional_terms) /* valid terms */ if (additional_terms) { - if (!asprintf(&str, "valid terms: %s,%s", - additional_terms, static_terms)) + if (asprintf(&str, "valid terms: %s,%s", + additional_terms, static_terms) < 0) goto fail; } else { - if (!asprintf(&str, "valid terms: %s", static_terms)) + if (asprintf(&str, "valid terms: %s", static_terms) < 0) goto fail; } return str; diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index e4b173dec4b9..6f2a0279476c 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -283,13 +283,12 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) { struct dirent *evt_ent; DIR *event_dir; - int ret = 0; event_dir = opendir(dir); if (!event_dir) return -EINVAL; - while (!ret && (evt_ent = readdir(event_dir))) { + while ((evt_ent = readdir(event_dir))) { char path[PATH_MAX]; char *name = evt_ent->d_name; FILE *file; @@ -305,17 +304,19 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) snprintf(path, PATH_MAX, "%s/%s", dir, name); - ret = -EINVAL; file = fopen(path, "r"); - if (!file) - break; + if (!file) { + pr_debug("Cannot open %s\n", path); + continue; + } - ret = perf_pmu__new_alias(head, dir, name, file); + if (perf_pmu__new_alias(head, dir, name, file) < 0) + pr_debug("Cannot set up %s\n", name); fclose(file); } closedir(event_dir); - return ret; + return 0; } /* diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 1833103768cb..c8680984d2d6 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py @@ -22,6 +22,7 @@ cflags = getenv('CFLAGS', '').split() # switch off several checks (need to be at the end of cflags list) cflags += ['-fno-strict-aliasing', '-Wno-write-strings', '-Wno-unused-parameter' ] +src_perf = getenv('srctree') + '/tools/perf' build_lib = getenv('PYTHON_EXTBUILD_LIB') build_tmp = getenv('PYTHON_EXTBUILD_TMP') libtraceevent = getenv('LIBTRACEEVENT') @@ -30,6 +31,9 @@ libapikfs = getenv('LIBAPI') ext_sources = [f.strip() for f in file('util/python-ext-sources') if len(f.strip()) > 0 and f[0] != '#'] +# use full paths with source files +ext_sources = map(lambda x: '%s/%s' % (src_perf, x) , ext_sources) + perf = Extension('perf', sources = ext_sources, include_dirs = ['util/include'], diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 7338e30421d8..fefbf2d148ef 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -547,6 +547,16 @@ static struct kvm *kvm_create_vm(unsigned long type) if (!kvm) return ERR_PTR(-ENOMEM); + spin_lock_init(&kvm->mmu_lock); + atomic_inc(¤t->mm->mm_count); + kvm->mm = current->mm; + kvm_eventfd_init(kvm); + mutex_init(&kvm->lock); + mutex_init(&kvm->irq_lock); + mutex_init(&kvm->slots_lock); + atomic_set(&kvm->users_count, 1); + INIT_LIST_HEAD(&kvm->devices); + r = kvm_arch_init_vm(kvm, type); if (r) goto out_err_no_disable; @@ -579,16 +589,6 @@ static struct kvm *kvm_create_vm(unsigned long type) goto out_err; } - spin_lock_init(&kvm->mmu_lock); - kvm->mm = current->mm; - atomic_inc(&kvm->mm->mm_count); - kvm_eventfd_init(kvm); - mutex_init(&kvm->lock); - mutex_init(&kvm->irq_lock); - mutex_init(&kvm->slots_lock); - atomic_set(&kvm->users_count, 1); - INIT_LIST_HEAD(&kvm->devices); - r = kvm_init_mmu_notifier(kvm); if (r) goto out_err; @@ -613,6 +613,7 @@ out_err_no_disable: for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++) kvm_free_memslots(kvm, kvm->memslots[i]); kvm_arch_free_vm(kvm); + mmdrop(current->mm); return ERR_PTR(r); } |
