diff options
66 files changed, 4605 insertions, 648 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/bcl.txt b/Documentation/devicetree/bindings/arm/msm/bcl.txt new file mode 100644 index 000000000000..42acaa49c2a5 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/bcl.txt @@ -0,0 +1,220 @@ +* Battery Current Limit + +This Battery Current Limit(BCL) device, provides an interface to detect and notify +interested applications when the SOC is drawing current in excess of the limits +specified. +The BCL driver has another operation mode, where it monitors the battery +current and voltage via ADC TM hardware called BTM. The newer devices support +a BTM hardware configuration, which can measure the battery current and voltage. +This ADC hardware is capable of sampling the sensor every 1 msec and interrupts +the BCL driver, which in turn mitigates the CPU frequency based on the +current load thresholds. The BCL drivers operation mode is decided based +on the parameters given in the device tree. In this BTM operation mode, BCL +driver provides sysfs entries to configure the thresholds, ADC polling +timer interval and other operational parameters. + +The device tree parameters for bcl are: + +Required parameters: +- compatible: Must be "qcom,bcl" + +Optional parameters: +- qcom,bcl-enable : If this property is defined, BCL functionality will + be enabled from boot. The mode of operation, will be based + on the properties defined in the device tree. +- qcom,ibat-vadc = <&vadc_phandle>: A phandle to the VADC device. The BTM mode + of operation requires this property to be defined if and only + if qcom,ibat-threshold-adc_tm and qcom,ibat-monitor are defined. + Error in any of these properties will disable BTM mode of operation + and will fall back to the available current monitor mode. +- qcom,ibat-threshold-adc_tm = <&vadc_tm_phandle>: A phandle to the ADC TM + device. BCL registers with the hardware monitor for this TM + device to be able to set thresholds and get threshold + notifications. The BTM mode of operation requires this property + to be defined if and only if qcom,ibat-vadc and qcom,ibat-monitor + are defined. Error in any of these properties will disable BTM + mode of operation and will fall back to the available current + monitor mode. +- qcom,bcl-framework-interface: If this property is defined, then the BCL uses + the BCL framework for monitoring battery voltage and current. + When this property is defined, the 'qcom,high-threshold-uamp', + 'qcom,low-threshold-uamp', 'qcom,mitigation-freq-khz', + 'qcom,vph-high-threshold-uv', 'qcom,vph-low-threshold-uv' and + 'qcom,thermal-handle' properties should be defined in the + 'qcom,ibat-monitor' node. +- qcom,bcl-hotplug-list = <hotplug-phandle-list>: List of phandles to the cores + that are to be hotplugged, when battery current limit condition + is reached. +- qcom,bcl-soc-hotplug-list: List of phandles to the cores that are to be hotplugged, + when battery SOC limit condition is reached. +- qcom,bcl-freq-control-list: List of phandles to the cores that are to be frequency + mitigated when BCL condition is reached. +- qcom,bcl-no-bms: This is an optional node for BCL IAVAIL monitor mode. + If this property is defined, BCL IAVAIL monitor gets rbat value + from power supply battery module instead of bms module. + +Optional nodes: +- qcom,ibat-monitor: This optional node defines all the parameters for the + battery current monitoring. The BTM mode of operation requires + all the below properties to be defined with valid values. Also, + this node should be defined if and only if qcom,ibat-vadc and + qcom,ibat-threshold-adc_tm are defined. Error in any of these + properties will disable BTM mode of operation and will fall + back to the available current monitor mode. + * qcom,high-threshold-uamp: The battery current, in microampere, after + which the BCL driver should cap the maximum frequency. + * qcom,low-threshold-uamp: The battery current, in microampere, below + which the BCL driver should clear the CPU frequency mitigation. + * qcom,mitigation-freq-khz: The maximum frequency value the BCL driver + should mitigate the CPUS's with. This frequency shouldn't be + less than the minimum frequency request that the kernel thermal + monitor driver places during vdd restriction. + * qcom,ibat-channel: The ADC hardware's Ibat channel number. + * qcom,uv-to-ua-numerator: The conversion parameter required for converting + the voltage measure from ADC hardware to current value. + * qcom,uv-to-ua-denominator: The conversion parameter required for + converting the voltage measure from ADC hardware to current. + The microvolt to microampere (or vice-versa) conversion uses + the below conversion formulae. + ua = (uv * uv-to-ua-numerator) / uv-to-ua-denominator + * qcom,adc-interval-usec: The polling interval, in microseconds, for the ADC + hardware. + * qcom,vph-channel: The ADC hardware's Vph channel number. + * qcom,vph-high-threshold-uv: The battery voltage threshold above which the + BCL driver clears the previously applied mitigation, disables + the battery current monitoring, and starts monitoring for low + battery voltage. + * qcom,vph-low-threshold-uv: The battery voltage threshold below which the + BCL driver starts monitoring the battery current thresholds and + mitigates the CPU on the event of high load. + * qcom,thermal-handle = <&phandle_to_vdd_apps>: phandle to the "qcom,msm_thermal" + vdd restriction property, "qcom,vdd-apps-rstr". This phandle is + used by BCL driver to get the minimum frequency request that the + thermal driver places during vdd restriction. This frequency + value will be the lowest max frequency value the BCL driver can + request. + * qcom,soc-low-threshold: The battery SOC percentage threshold below which + mitigation needs to be applied. + + +Example: + qcom,bcl { + compatible = "qcom,bcl"; + qcom,ibat-vadc = <&pma8084_vadc>; + qcom,ibat-threshold-adc_tm = <&pma8084_adc_tm>; + qcom,bcl-no-bms; + qcom,ibat-monitor { + qcom,high-threshold-uamp = <1500>; + qcom,low-threshold-uamp = <500>; + qcom,mitigation-freq-khz = <1958400>; + qcom,ibat-channel = <0x15>; + qcom,adc-interval-usec = <3900>; + qcom,uv-to-ua-numerator = <2>; + qcom,uv-to-ua-denominator = <1>; + qcom,vph-channel = <0x07>; + qcom,vph-high-threshold-uv = <3700000>; + qcom,vph-low-threshold-uv = <3500000>; + qcom,thermal-handle = <&msm_thermal_freq>; + }; + }; +For Using BCL peripheral interface: + qcom,bcl { + compatible = "qcom,bcl"; + qcom,bcl-framework-interface; + qcom,bcl-freq-mit-list = <&CPU4 &CPU5 &CPU6 &CPU7>; + qcom,bcl-hotplug-list = <&CPU5 &CPU6 &CPU7>; + qcom,bcl-soc-hotplug-list = <&CPU4 &CPU5 &CPU6 &CPU7>; + qcom,ibat-monitor { + qcom,high-threshold-uamp = <1500>; + qcom,low-threshold-uamp = <500>; + qcom,mitigation-freq-khz = <1958400>; + qcom,vph-high-threshold-uv = <3700000>; + qcom,vph-low-threshold-uv = <3500000>; + qcom,thermal-handle = <&msm_thermal_freq>; + }; + }; + +=============================================================================== +BCL PMIC Peripheral driver: +=============================================================================== +In newer targets from MSM8994, the PMIC has BCL monitoring capabilities +in the hardware. The PMIC exposes this BCL monitoring peripheral as a PMIC +peripheral. The BCL peripheral driver interacts with the PMIC peripheral using +the SPMI driver interfaces. The details and the configuration for the BCL +peripheral can be inputted using the device tree. + +The units of the Vbat and Ibat values returned and read depends on the scaling +factor that is given as input for BCL peripheral driver through device tree. The +scaling factors should be configured to handle Vbat in micro-volt and Ibat in +micro-amps. + +Required Parameters: +- compatible: must be either + 1. 'qcom,msm-bcl' for bcl peripheral without LMH DCVSh + interface + 2. 'qcom,msm-bcl-lmh' for bcl peripheral with LMH DCVSh interface. +- reg: <a b> where 'a' is the starting register address of the PMIC + peripheral and 'b' is the size of the peripheral address space. + If the BCL inhibit current derating feature is enabled, this must also + contain the PON spare registers as well. Example: <a b c d> where + c is the first PON spare register that will be written and d is the + size of the registers space needed to be written. Certain version + of PMIC, can send interrupt to LMH hardware driver directly. In that + case the shadow peripheral address space should be mentioned along + with the bcl peripherals address. +- reg-names: a list of names of the registers corresponding to the reg + property. The fuel gauge peripheral should be "fg_user_adc", the + PON spare should be "pon_spare", and the bcl-lmh shadow peripheral + should be "fg_lmh". +-interrupts: <a b c> Where 'a' is the SLAVE ID of the PMIC, 'b' is + the peripheral ID and 'c' is the interrupt number in PMIC. +- interrupt-names: user defined names for the interrupts. These + interrupt names will be used by the drivers to identify the + interrupts, instead of specifying the ID's. +- qcom,ibat-polling-delay-ms: Software polling interval for monitoring ibat + low threshold. +- qcom,vbat-polling-delay-ms: Software polling interval for monitoring vbat + high threshold. + +Optional parameters for peripheral with LMH DCVSh interface: +- qcom,vbat-scaling-factor: The scaling factor to be used for converting + the raw vbat ADC value to milli-volt. +- qcom,vbat-gain-numerator: The numerator of the vbat gain correction factor. +- qcom,vbat-gain-denominator: The denominator of the vbat gain correction + factor. +- qcom,ibat-scaling-factor: The scaling factor to be used for converting + the raw ibat ADC value to micro-amps. +- qcom, ibat-gain-numerator: The numerator of the ibat gain correction factor. +- qcom, ibat-gain-denominator: The denominator of the ibat gain correction + factor. +- qcom, ibat-offset-numerator: The numerator of the ibat offset correction + factor. +- qcom, ibat-offset-denominator: The denominator of the ibat offset + correction factor. + +Optional Parameters: +- qcom,inhibit-derating-ua: The amount that the bcl current high trip threshold + should be lowered by when the bcl peripheral is operating in a + dead time. + + bcl@4200 { + compatible = "qcom,msm-bcl"; + reg = <0x4200 0xFF 0x88e 0x2>; + reg-names = "fg_user_adc", "pon_spare"; + interrupts = <0x2 0x42 0x0>, + <0x2 0x42 0x1>; + interrupt-names = "bcl-high-ibat-int", + "bcl-low-vbat-int"; + qcom,vbat-scaling-factor = <39>; + qcom,vbat-gain-numerator = <1>; + qcom,vbat-gain-denominator = <32>; + qcom,vbat-polling-delay-ms = <50>; + qcom,ibat-scaling-factor = <39>; + qcom,ibat-gain-numerator = <1>; + qcom,ibat-gain-denominator = <32>; + qcom,ibat-offset-numerator = <12>; + qcom,ibat-offset-denominator = <10>; + qcom,ibat-polling-delay-ms = <50>; + }; + + diff --git a/Documentation/devicetree/bindings/cnss/icnss.txt b/Documentation/devicetree/bindings/cnss/icnss.txt index 7857eeb963a6..f088074fa3ef 100644 --- a/Documentation/devicetree/bindings/cnss/icnss.txt +++ b/Documentation/devicetree/bindings/cnss/icnss.txt @@ -17,6 +17,10 @@ Required properties: - iommus: SMMUs and corresponding Stream IDs needed by WLAN - qcom,wlan-smmu-iova-address: I/O virtual address range as <start length> format to be used for allocations associated between WLAN and SMMU + - <supply-name>-supply: phandle to the regulator device tree node + Required "supply-name" is "vdd-io". + - qcom,<supply>-voltage-level - specifies voltage levels for supply. Should be + specified in pairs (min, max), units uV. Optional properties: - qcom,skip-qmi: Boolean property to decide whether to use QMI or not @@ -47,4 +51,6 @@ Example: <0 141 0 /* CE11 */ >; qcom,wlan-msa-memory = <0x200000>; qcom,skip-qmi; + vdd-io-supply = <&pmcobalt_l5>; + qcom,vdd-io-voltage-level = <800000 800000>; }; diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt new file mode 100644 index 000000000000..827f96c341b6 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt @@ -0,0 +1,153 @@ +Qualcomm Technologies Inc. PNP v2 Flash LED + +QPNP (Qualcomm Technologies Inc. Plug N Play) Flash LED (Light +Emitting Diode) driver v2 is used to provide illumination to +camera sensor when background light is dim to capture good +picture. It can also be used for flashlight/torch application. +It is part of PMIC on Qualcomm Technologies Inc. reference platforms. + +Required properties: +- compatible : Should be "qcom,qpnp-flash-led-v2" +- reg : Base address and size for flash LED modules + +Optional properties: +- 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. + +Required properties inside child node. Child node contains settings for each individual LED. +Each LED channel needs a flash node and torch node for itself, and an individual switch node to +serve as an overall switch. +- label : Type of led that will be used, either "flash", "torch", or "switch. +- qcom,led-name : Name of the LED. +- qcom,default-led-trigger : Trigger for the camera flash and torch. Accepted values are + "flash0_trigger", "flash1_trigger", "flash2_trigger, "torch0_trigger", + "torch1_trigger", "torch2_trigger", and "switch_trigger". +- qcom,id : ID for each physical LED equipped. In order to handle situation when + only 1 or 2 LEDs are installed, flash and torch nodes on LED channel 0 + should be specified with ID 0; nodes on channel 1 be ID 1, etc. This is + not required for switch node. +- qcom,max-current : Maximum current allowed on this LED. Valid values should be + integer from 0 to 1500 inclusive. Flash 2 should have maximum current of + 750 per hardware requirement. Unit is mA. This is not required for switch + node. +- qcom,duration-ms : Required property for flash nodes but not needed for torch. Integer + type specifying flash duration. Values are from 10ms to 1280ms with + 10ms resolution. This is not required for switch node. + +Optional properties inside child node: +- qcom,ires-ua : Integer type to specify current resolution. Accepted values should be + 12500, 10000, 7500, and 5000. Unit is uA. +- qcom,hdrm-voltage-mv : Integer type specifying headroom voltage. Values are from 125mV to 500mV + with 25mV resolution. Default setting is 325mV +- qcom,hdrm-vol-hi-lo-win-mv : Integer type to specify headroom voltage swing range. Values are + from 0mV to 375mV with 25mV resolution. Default setting is 100mV. +- pinctrl-names : This should be defined if a target uses pinctrl framework and there is GPIO + requirement for flash LEDs. 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: + "led_enable" : Enablement configuration of pins. This should specify active + config defined in each pin or pin group. + "led_disable" : Disablement configuration of pins. This should specify inactive + config defined in each pin or pin groups. + +Example: + qcom,leds@d300 { + compatible = "qcom,qpnp-flash-led-v2"; + status = "okay"; + reg = <0xd300 0x100>; + label = "flash"; + + qcom,hdrm-auto-mode; + qcom,isc-delay = <192>; + + pmi8998_flash0: qcom,flash_0 { + label = "flash"; + qcom,led-name = "led:flash_0"; + qcom,max-current = <1500>; + qcom,default-led-trigger = + "flash0_trigger"; + qcom,id = <0>; + qcom,duration-ms = <1280>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pmi8998_flash1: qcom,flash_1 { + label = "flash"; + qcom,led-name = "led:flash_1"; + qcom,max-current = <1500>; + qcom,default-led-trigger = + "flash1_trigger"; + qcom,id = <1>; + qcom,duration-ms = <1280>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pmi8998_flash2: qcom,flash_2 { + label = "flash"; + qcom,led-name = "led:flash_2"; + qcom,max-current = <750>; + qcom,default-led-trigger = + "flash2_trigger"; + qcom,id = <2>; + qcom,duration-ms = <1280>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + pinctrl-names = "led_enable","led_disable"; + pinctrl-0 = <&led_enable>; + pinctrl-1 = <&led_disable>; + }; + + pmi8998_torch0: qcom,torch_0 { + label = "torch"; + qcom,led-name = "led:torch_0"; + qcom,max-current = <200>; + qcom,default-led-trigger = + "torch0_trigger"; + qcom,id = <0>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pmi8998_torch1: qcom,torch_1 { + label = "torch"; + qcom,led-name = "led:torch_1"; + qcom,max-current = <200>; + qcom,default-led-trigger = + "torch1_trigger"; + qcom,id = <1>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + }; + + pmi8998_torch2: qcom,torch_2 { + label = "torch"; + qcom,led-name = "led:torch_2"; + qcom,max-current = <200>; + qcom,default-led-trigger = + "torch2_trigger"; + qcom,id = <2>; + qcom,ires-ua = <12500>; + qcom,hdrm-voltage-mv = <325>; + qcom,hdrm-vol-hi-lo-win-mv = <100>; + pinctrl-names = "led_enable","led_disable"; + pinctrl-0 = <&led_enable>; + pinctrl-1 = <&led_disable>; + }; + + pmi8998_switch: qcom,led_switch { + label = "switch"; + qcom,led-name = "led:switch"; + qcom,default-led-trigger = + "switch_trigger"; + }; + }; + diff --git a/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi b/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi index 9d60068afbf0..ab6b614471e5 100644 --- a/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi +++ b/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi @@ -187,6 +187,11 @@ qcom,msm-dai-q6-dev-id = <16394>; }; + sb_6_rx: qcom,msm-dai-q6-sb-6-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <16396>; + }; + sb_7_rx: qcom,msm-dai-q6-sb-7-rx { compatible = "qcom,msm-dai-q6-dev"; qcom,msm-dai-q6-dev-id = <16398>; @@ -266,6 +271,16 @@ compatible = "qcom,msm-dai-q6-dev"; qcom,msm-dai-q6-dev-id = <32770>; }; + + usb_audio_rx: qcom,msm-dai-q6-usb-audio-rx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <28672>; + }; + + usb_audio_tx: qcom,msm-dai-q6-usb-audio-tx { + compatible = "qcom,msm-dai-q6-dev"; + qcom,msm-dai-q6-dev-id = <28673>; + }; }; hostless: qcom,msm-pcm-hostless { diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi index d0601719054d..ee214eeaaa00 100644 --- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi @@ -264,6 +264,21 @@ "msg-tx-discarded", "msg-rx-discarded"; }; + + bcl@4200 { + compatible = "qcom,msm-bcl-lmh"; + reg = <0x4200 0xff>, + <0x4300 0xff>; + reg-names = "fg_user_adc", + "fg_lmh"; + interrupts = <0x2 0x42 0x0 IRQ_TYPE_NONE>, + <0x2 0x42 0x2 IRQ_TYPE_NONE>; + interrupt-names = "bcl-high-ibat-int", + "bcl-low-vbat-int"; + qcom,vbat-polling-delay-ms = <100>; + qcom,ibat-polling-delay-ms = <100>; + }; + }; qcom,pmicobalt@3 { diff --git a/arch/arm/boot/dts/qcom/msm8996-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8996-cdp.dtsi index 50dd83ef20d0..51bb308d9832 100644 --- a/arch/arm/boot/dts/qcom/msm8996-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-cdp.dtsi @@ -601,6 +601,10 @@ interrupts = <125 0x2008>; vdd-supply = <&pm8994_l14>; avdd-supply = <&pm8994_l22>; + synaptics,vdd-voltage = <1800000 1800000>; + synaptics,avdd-voltage = <3300000 3300000>; + synaptics,vdd-current = <40000>; + synaptics,avdd-current = <20000>; pinctrl-names = "pmx_ts_active", "pmx_ts_suspend"; pinctrl-0 = <&ts_active>; pinctrl-1 = <&ts_suspend>; diff --git a/arch/arm/boot/dts/qcom/msm8996-mdss.dtsi b/arch/arm/boot/dts/qcom/msm8996-mdss.dtsi index 306a49ae2b69..5c01812d7d6e 100644 --- a/arch/arm/boot/dts/qcom/msm8996-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-mdss.dtsi @@ -100,7 +100,8 @@ qcom,mdss-pingpong-off = <0x00071000 0x00071800 0x00072000 0x00072800>; qcom,mdss-slave-pingpong-off = <0x00073000>; - qcom,mdss-ppb-off = <0x00000330 0x00000338>; + qcom,mdss-ppb-ctl-off = <0x00000330 0x00000338>; + qcom,mdss-ppb-cfg-off = <0x00000334 0x0000033C>; qcom,mdss-has-pingpong-split; qcom,mdss-ad-off = <0x0079000 0x00079800 0x0007a000>; diff --git a/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi index 84df45454605..36af9f431cad 100644 --- a/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8996-mtp.dtsi @@ -623,6 +623,10 @@ interrupts = <125 0x2008>; vdd-supply = <&pm8994_l14>; avdd-supply = <&pm8994_l22>; + synaptics,vdd-voltage = <1800000 1800000>; + synaptics,avdd-voltage = <3300000 3300000>; + synaptics,vdd-current = <40000>; + synaptics,avdd-current = <20000>; pinctrl-names = "pmx_ts_active", "pmx_ts_suspend"; pinctrl-0 = <&ts_active>; pinctrl-1 = <&ts_suspend>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi index 3e7c8189b793..62ebcd7acbc5 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi @@ -80,8 +80,9 @@ <&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>, <&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>, - <&incall_music_2_rx>, <&sb_5_rx>, - <&sb_7_rx>, <&sb_7_tx>, <&sb_8_tx>; + <&incall_music_2_rx>, <&sb_5_rx>, <&sb_6_rx>, + <&sb_7_rx>, <&sb_7_tx>, <&sb_8_tx>, + <&usb_audio_rx>, <&usb_audio_tx>; asoc-cpu-names = "msm-dai-q6-hdmi.8", "msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385", "msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387", @@ -93,8 +94,9 @@ "msm-dai-q6-dev.240", "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770", "msm-dai-q6-dev.16394", - "msm-dai-q6-dev.16398", "msm-dai-q6-dev.16399", - "msm-dai-q6-dev.16401"; + "msm-dai-q6-dev.16396", "msm-dai-q6-dev.16398", + "msm-dai-q6-dev.16399", "msm-dai-q6-dev.16401", + "msm-dai-q6-dev.28672", "msm-dai-q6-dev.28673"; asoc-codec = <&stub_codec>; asoc-codec-names = "msm-stub-codec.1"; qcom,wsa-max-devs = <2>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-bus.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-bus.dtsi index 27929113d2cb..45eee0caaac9 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-bus.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-bus.dtsi @@ -22,11 +22,12 @@ <0x1660000 0x60000>, <0x1700000 0x60000>, <0x17900000 0x10000>, + <0x1740000 0x10000>, <0x1740000 0x10000>; reg-names = "snoc-base", "bimc-base", "cnoc-base", "a1noc-base", "a2noc-base", "gnoc-base", - "mnoc-base"; + "mmnoc-ahb-base", "mnoc-base"; /*Buses*/ fab_a1noc: fab-a1noc { @@ -66,8 +67,8 @@ qcom,bypass-qos-prg; qcom,util-fact = <153>; clock-names = "bus_clk", "bus_a_clk"; - clocks = <&clock_gcc clk_bimc_clk>, - <&clock_gcc clk_bimc_a_clk>; + clocks = <&clock_gcc clk_bimc_msmbus_clk>, + <&clock_gcc clk_bimc_msmbus_a_clk>; }; fab_cnoc: fab-cnoc { @@ -98,8 +99,8 @@ qcom,base-name = "gnoc-base"; qcom,bypass-qos-prg; clock-names = "bus_clk", "bus_a_clk"; - clocks = <&clock_gcc clk_bimc_clk>, - <&clock_gcc clk_bimc_a_clk>; + clocks = <&clock_gcc clk_bimc_msmbus_clk>, + <&clock_gcc clk_bimc_msmbus_a_clk>; }; fab_mnoc: fab-mnoc { @@ -131,6 +132,20 @@ <&clock_gcc clk_snoc_a_clk>; }; + fab_mnoc_ahb: fab-mnoc-ahb { + cell-id = <MSM_BUS_FAB_MMSS_AHB>; + label = "fab-mnoc-ahb"; + qcom,fab-dev; + qcom,base-name = "mmnoc-ahb-base"; + qcom,bypass-qos-prg; + qcom,setrate-only-clk; + qcom,bus-type = <1>; + clock-names = "bus_clk", "bus_a_clk"; + clocks = <&clock_mmss clk_ahb_clk_src>, + <&clock_mmss clk_ahb_clk_src>; + }; + + /*Masters*/ mas_pcie_0: mas-pcie-0 { @@ -423,7 +438,7 @@ &slv_venus_throttle_cfg &slv_display_cfg &slv_mmss_clk_cfg &slv_vmem_cfg &slv_mmss_clk_xpu_cfg &slv_smmu_cfg>; - qcom,bus-dev = <&fab_mnoc>; + qcom,bus-dev = <&fab_mnoc_ahb>; qcom,mas-rpm-id = <ICBID_MASTER_CNOC_MNOC_MMSS_CFG>; }; @@ -1121,7 +1136,7 @@ qcom,buswidth = <8>; qcom,agg-ports = <1>; qcom,ap-owned; - qcom,bus-dev = <&fab_mnoc>; + qcom,bus-dev = <&fab_mnoc_ahb>; qcom,slv-rpm-id = <ICBID_SLAVE_CAMERA_CFG>; }; @@ -1131,7 +1146,7 @@ qcom,buswidth = <8>; qcom,agg-ports = <1>; qcom,ap-owned; - qcom,bus-dev = <&fab_mnoc>; + qcom,bus-dev = <&fab_mnoc_ahb>; qcom,slv-rpm-id = <ICBID_SLAVE_CAMERA_THROTTLE_CFG>; }; @@ -1141,7 +1156,7 @@ qcom,buswidth = <8>; qcom,agg-ports = <1>; qcom,ap-owned; - qcom,bus-dev = <&fab_mnoc>; + qcom,bus-dev = <&fab_mnoc_ahb>; qcom,slv-rpm-id = <ICBID_SLAVE_MISC_CFG>; }; @@ -1151,7 +1166,7 @@ qcom,buswidth = <8>; qcom,agg-ports = <1>; qcom,ap-owned; - qcom,bus-dev = <&fab_mnoc>; + qcom,bus-dev = <&fab_mnoc_ahb>; qcom,slv-rpm-id = <ICBID_SLAVE_VENUS_THROTTLE_CFG>; }; @@ -1161,7 +1176,7 @@ qcom,buswidth = <8>; qcom,agg-ports = <1>; qcom,ap-owned; - qcom,bus-dev = <&fab_mnoc>; + qcom,bus-dev = <&fab_mnoc_ahb>; qcom,slv-rpm-id = <ICBID_SLAVE_VENUS_CFG>; }; @@ -1171,7 +1186,7 @@ qcom,buswidth = <8>; qcom,agg-ports = <1>; qcom,ap-owned; - qcom,bus-dev = <&fab_mnoc>; + qcom,bus-dev = <&fab_mnoc_ahb>; qcom,slv-rpm-id = <ICBID_SLAVE_VMEM_CFG>; qcom,enable-only-clk; clock-names = "node_clk"; @@ -1184,7 +1199,7 @@ qcom,buswidth = <8>; qcom,agg-ports = <1>; qcom,ap-owned; - qcom,bus-dev = <&fab_mnoc>; + qcom,bus-dev = <&fab_mnoc_ahb>; qcom,slv-rpm-id = <ICBID_SLAVE_MMSS_CLK_XPU_CFG>; }; @@ -1194,7 +1209,7 @@ qcom,buswidth = <8>; qcom,agg-ports = <1>; qcom,ap-owned; - qcom,bus-dev = <&fab_mnoc>; + qcom,bus-dev = <&fab_mnoc_ahb>; qcom,slv-rpm-id = <ICBID_SLAVE_MMSS_CLK_CFG>; }; @@ -1204,7 +1219,7 @@ qcom,buswidth = <8>; qcom,agg-ports = <1>; qcom,ap-owned; - qcom,bus-dev = <&fab_mnoc>; + qcom,bus-dev = <&fab_mnoc_ahb>; qcom,slv-rpm-id = <ICBID_SLAVE_DISPLAY_CFG>; }; @@ -1214,7 +1229,7 @@ qcom,buswidth = <8>; qcom,agg-ports = <1>; qcom,ap-owned; - qcom,bus-dev = <&fab_mnoc>; + qcom,bus-dev = <&fab_mnoc_ahb>; qcom,slv-rpm-id = <ICBID_SLAVE_DISPLAY_THROTTLE_CFG>; }; @@ -1224,7 +1239,7 @@ qcom,buswidth = <8>; qcom,agg-ports = <1>; qcom,ap-owned; - qcom,bus-dev = <&fab_mnoc>; + qcom,bus-dev = <&fab_mnoc_ahb>; qcom,slv-rpm-id = <ICBID_SLAVE_MMSS_SMMU_CFG>; }; 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 f5d55972f92f..fe145634fb80 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi @@ -23,10 +23,8 @@ qcom,gpio-req-tbl-flags = <0>; qcom,gpio-req-tbl-label = "CAM_VAF"; pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active - &cam_actuator_vaf_suspend>; - pinctrl-1 = <&cam_actuator_vaf_active - &cam_actuator_vaf_suspend>; + pinctrl-0 = <&cam_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; }; actuator1: qcom,actuator@1 { @@ -40,10 +38,8 @@ qcom,gpio-req-tbl-flags = <0>; qcom,gpio-req-tbl-label = "CAM_VAF"; pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active - &cam_actuator_vaf_suspend>; - pinctrl-1 = <&cam_actuator_vaf_active - &cam_actuator_vaf_suspend>; + pinctrl-0 = <&cam_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; }; ois0: qcom,ois@0 { @@ -57,10 +53,8 @@ qcom,gpio-req-tbl-flags = <0>; qcom,gpio-req-tbl-label = "CAM_VAF"; pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active - &cam_actuator_vaf_suspend>; - pinctrl-1 = <&cam_actuator_vaf_active - &cam_actuator_vaf_suspend>; + pinctrl-0 = <&cam_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; status = "disabled"; }; @@ -108,6 +102,43 @@ cell-index = <1>; reg = <0x1>; compatible = "qcom,eeprom"; + cam_vdig-supply = <&pmcobalt_lvs1>; + cam_vio-supply = <&pmcobalt_lvs1>; + cam_vana-supply = <&pmicobalt_bob>; + qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; + qcom,cam-vreg-min-voltage = <0 0 3312000>; + qcom,cam-vreg-max-voltage = <0 0 3312000>; + qcom,cam-vreg-op-mode = <0 0 80000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_mclk2_active + &cam_sensor_rear2_active>; + pinctrl-1 = <&cam_sensor_mclk2_suspend + &cam_sensor_rear2_suspend>; + gpios = <&tlmm 15 0>, + <&tlmm 9 0>, + <&tlmm 8 0>; + qcom,gpio-reset = <1>; + qcom,gpio-vana = <2>; + qcom,gpio-req-tbl-num = <0 1 2>; + qcom,gpio-req-tbl-flags = <1 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK1", + "CAM_RESET1", + "CAM_VANA1"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <1>; + status = "ok"; + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + eeprom2: qcom,eeprom@2 { + cell-index = <2>; + reg = <0x2>; + compatible = "qcom,eeprom"; cam_vio-supply = <&pmcobalt_lvs1>; cam_vana-supply = <&pmcobalt_l22>; cam_vdig-supply = <&pmcobalt_s3>; @@ -125,16 +156,13 @@ &cam_sensor_front_suspend>; gpios = <&tlmm 14 0>, <&tlmm 28 0>, - <&tlmm 27 0>, <&pmcobalt_gpios 9 0>; qcom,gpio-reset = <1>; - qcom,gpio-standby = <2>; - qcom,gpio-vdig = <3>; - qcom,gpio-req-tbl-num = <0 1 2 3>; - qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-vdig = <2>; + qcom,gpio-req-tbl-num = <0 1 2>; + qcom,gpio-req-tbl-flags = <1 0 0>; qcom,gpio-req-tbl-label = "CAMIF_MCLK2", "CAM_RESET2", - "CAM_STANDBY2", "CAM_VDIG"; qcom,sensor-position = <1>; qcom,sensor-mode = <0>; @@ -199,6 +227,7 @@ qcom,csiphy-sd-index = <1>; qcom,csid-sd-index = <1>; qcom,mount-angle = <90>; + qcom,eeprom-src = <&eeprom1>; cam_vdig-supply = <&pmcobalt_lvs1>; cam_vio-supply = <&pmcobalt_lvs1>; cam_vana-supply = <&pmicobalt_bob>; @@ -224,7 +253,7 @@ "CAM_VANA1"; qcom,sensor-position = <0>; qcom,sensor-mode = <0>; - qcom,cci-master = <0>; + qcom,cci-master = <1>; status = "ok"; clocks = <&clock_mmss clk_mclk2_clk_src>, <&clock_mmss clk_mmss_camss_mclk2_clk>; @@ -239,7 +268,7 @@ qcom,csiphy-sd-index = <2>; qcom,csid-sd-index = <2>; qcom,mount-angle = <90>; - qcom,eeprom-src = <&eeprom1>; + qcom,eeprom-src = <&eeprom2>; qcom,actuator-src = <&actuator1>; cam_vio-supply = <&pmcobalt_lvs1>; cam_vana-supply = <&pmcobalt_l22>; @@ -258,16 +287,13 @@ &cam_sensor_front_suspend>; gpios = <&tlmm 14 0>, <&tlmm 28 0>, - <&tlmm 27 0>, <&pmcobalt_gpios 9 0>; qcom,gpio-reset = <1>; - qcom,gpio-standby = <2>; - qcom,gpio-vdig = <3>; - qcom,gpio-req-tbl-num = <0 1 2 3>; - qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-vdig = <2>; + qcom,gpio-req-tbl-num = <0 1 2>; + qcom,gpio-req-tbl-flags = <1 0 0>; qcom,gpio-req-tbl-label = "CAMIF_MCLK2", "CAM_RESET2", - "CAM_STANDBY2", "CAM_VDIG"; qcom,sensor-position = <1>; qcom,sensor-mode = <0>; 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 f5d55972f92f..fe145634fb80 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi @@ -23,10 +23,8 @@ qcom,gpio-req-tbl-flags = <0>; qcom,gpio-req-tbl-label = "CAM_VAF"; pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active - &cam_actuator_vaf_suspend>; - pinctrl-1 = <&cam_actuator_vaf_active - &cam_actuator_vaf_suspend>; + pinctrl-0 = <&cam_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; }; actuator1: qcom,actuator@1 { @@ -40,10 +38,8 @@ qcom,gpio-req-tbl-flags = <0>; qcom,gpio-req-tbl-label = "CAM_VAF"; pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active - &cam_actuator_vaf_suspend>; - pinctrl-1 = <&cam_actuator_vaf_active - &cam_actuator_vaf_suspend>; + pinctrl-0 = <&cam_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; }; ois0: qcom,ois@0 { @@ -57,10 +53,8 @@ qcom,gpio-req-tbl-flags = <0>; qcom,gpio-req-tbl-label = "CAM_VAF"; pinctrl-names = "cam_default", "cam_suspend"; - pinctrl-0 = <&cam_actuator_vaf_active - &cam_actuator_vaf_suspend>; - pinctrl-1 = <&cam_actuator_vaf_active - &cam_actuator_vaf_suspend>; + pinctrl-0 = <&cam_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; status = "disabled"; }; @@ -108,6 +102,43 @@ cell-index = <1>; reg = <0x1>; compatible = "qcom,eeprom"; + cam_vdig-supply = <&pmcobalt_lvs1>; + cam_vio-supply = <&pmcobalt_lvs1>; + cam_vana-supply = <&pmicobalt_bob>; + qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; + qcom,cam-vreg-min-voltage = <0 0 3312000>; + qcom,cam-vreg-max-voltage = <0 0 3312000>; + qcom,cam-vreg-op-mode = <0 0 80000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_mclk2_active + &cam_sensor_rear2_active>; + pinctrl-1 = <&cam_sensor_mclk2_suspend + &cam_sensor_rear2_suspend>; + gpios = <&tlmm 15 0>, + <&tlmm 9 0>, + <&tlmm 8 0>; + qcom,gpio-reset = <1>; + qcom,gpio-vana = <2>; + qcom,gpio-req-tbl-num = <0 1 2>; + qcom,gpio-req-tbl-flags = <1 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK1", + "CAM_RESET1", + "CAM_VANA1"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <1>; + status = "ok"; + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + eeprom2: qcom,eeprom@2 { + cell-index = <2>; + reg = <0x2>; + compatible = "qcom,eeprom"; cam_vio-supply = <&pmcobalt_lvs1>; cam_vana-supply = <&pmcobalt_l22>; cam_vdig-supply = <&pmcobalt_s3>; @@ -125,16 +156,13 @@ &cam_sensor_front_suspend>; gpios = <&tlmm 14 0>, <&tlmm 28 0>, - <&tlmm 27 0>, <&pmcobalt_gpios 9 0>; qcom,gpio-reset = <1>; - qcom,gpio-standby = <2>; - qcom,gpio-vdig = <3>; - qcom,gpio-req-tbl-num = <0 1 2 3>; - qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-vdig = <2>; + qcom,gpio-req-tbl-num = <0 1 2>; + qcom,gpio-req-tbl-flags = <1 0 0>; qcom,gpio-req-tbl-label = "CAMIF_MCLK2", "CAM_RESET2", - "CAM_STANDBY2", "CAM_VDIG"; qcom,sensor-position = <1>; qcom,sensor-mode = <0>; @@ -199,6 +227,7 @@ qcom,csiphy-sd-index = <1>; qcom,csid-sd-index = <1>; qcom,mount-angle = <90>; + qcom,eeprom-src = <&eeprom1>; cam_vdig-supply = <&pmcobalt_lvs1>; cam_vio-supply = <&pmcobalt_lvs1>; cam_vana-supply = <&pmicobalt_bob>; @@ -224,7 +253,7 @@ "CAM_VANA1"; qcom,sensor-position = <0>; qcom,sensor-mode = <0>; - qcom,cci-master = <0>; + qcom,cci-master = <1>; status = "ok"; clocks = <&clock_mmss clk_mclk2_clk_src>, <&clock_mmss clk_mmss_camss_mclk2_clk>; @@ -239,7 +268,7 @@ qcom,csiphy-sd-index = <2>; qcom,csid-sd-index = <2>; qcom,mount-angle = <90>; - qcom,eeprom-src = <&eeprom1>; + qcom,eeprom-src = <&eeprom2>; qcom,actuator-src = <&actuator1>; cam_vio-supply = <&pmcobalt_lvs1>; cam_vana-supply = <&pmcobalt_l22>; @@ -258,16 +287,13 @@ &cam_sensor_front_suspend>; gpios = <&tlmm 14 0>, <&tlmm 28 0>, - <&tlmm 27 0>, <&pmcobalt_gpios 9 0>; qcom,gpio-reset = <1>; - qcom,gpio-standby = <2>; - qcom,gpio-vdig = <3>; - qcom,gpio-req-tbl-num = <0 1 2 3>; - qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-vdig = <2>; + qcom,gpio-req-tbl-num = <0 1 2>; + qcom,gpio-req-tbl-flags = <1 0 0>; qcom,gpio-req-tbl-label = "CAMIF_MCLK2", "CAM_RESET2", - "CAM_STANDBY2", "CAM_VDIG"; qcom,sensor-position = <1>; qcom,sensor-mode = <0>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi index 7df393167380..d4d33e0d8dec 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi @@ -125,8 +125,8 @@ pinctrl-names = "pmx_ts_active", "pmx_ts_suspend"; pinctrl-0 = <&ts_active>; pinctrl-1 = <&ts_suspend>; - synaptics,display-coords = <0 0 1599 2559>; - synaptics,panel-coords = <0 0 1599 2703>; + synaptics,display-coords = <0 0 1439 2559>; + synaptics,panel-coords = <0 0 1439 2559>; synaptics,reset-gpio = <&tlmm 89 0x00>; synaptics,irq-gpio = <&tlmm 125 0x2008>; synaptics,disable-gpios; @@ -171,7 +171,7 @@ 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-panel-mode-gpio-state = "dual_port"; + 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-mdss.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi index 54b16934a89d..d0769168f756 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi @@ -93,7 +93,9 @@ qcom,mdss-pingpong-off = <0x00071000 0x00071800 0x00072000 0x00072800>; qcom,mdss-slave-pingpong-off = <0x00073000>; - qcom,mdss-ppb-off = <0x00000330 0x00000338>; + qcom,mdss-ppb-ctl-off = <0x00000330 0x00000338 0x00000370 + 0x00000374> ; + qcom,mdss-ppb-cfg-off = <0x00000334 0x0000033C>; qcom,mdss-has-pingpong-split; qcom,mdss-has-separate-rotator; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi index 3891237ccf17..68f8dbd871a4 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi @@ -125,8 +125,8 @@ pinctrl-names = "pmx_ts_active", "pmx_ts_suspend"; pinctrl-0 = <&ts_active>; pinctrl-1 = <&ts_suspend>; - synaptics,display-coords = <0 0 1599 2559>; - synaptics,panel-coords = <0 0 1599 2703>; + synaptics,display-coords = <0 0 1439 2559>; + synaptics,panel-coords = <0 0 1439 2703>; synaptics,reset-gpio = <&tlmm 89 0x00>; synaptics,irq-gpio = <&tlmm 125 0x2008>; synaptics,disable-gpios; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi index 9acbeae79a28..3c8919f2d217 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi @@ -1162,14 +1162,14 @@ }; cam_sensor_front_active: cam_sensor_front_active { - /* RESET, STANDBY */ + /* RESET */ mux { - pins = "gpio28","gpio27"; + pins = "gpio28"; function = "gpio"; }; config { - pins = "gpio28","gpio27"; + pins = "gpio28"; bias-disable; /* No PULL */ drive-strength = <2>; /* 2 MA */ }; @@ -1228,14 +1228,14 @@ }; cam_sensor_front_suspend: cam_sensor_front_suspend { - /* RESET, STANDBY */ + /* RESET */ mux { - pins = "gpio28","gpio27"; + pins = "gpio28"; function = "gpio"; }; config { - pins = "gpio28","gpio27"; + pins = "gpio28"; bias-disable; /* No PULL */ drive-strength = <2>; /* 2 MA */ }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi index 3538bcee684d..6bb7ee4311c6 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi @@ -37,16 +37,16 @@ qcom,max-hw-load = <2563200>; /* Full 4k @ 60 + 1080p @ 60 */ qcom,load-freq-tbl = /* Encoders */ - <972000 490000000 0x55555555>, /* 4k UHD @ 30 */ - <489600 320000000 0x55555555>, /* 1080p @ 60 */ - <244800 150000000 0x55555555>, /* 1080p @ 30 */ - <108000 75000000 0x55555555>, /* 720p @ 30 */ + <972000 465000000 0x55555555>, /* 4k UHD @ 30 */ + <489600 360000000 0x55555555>, /* 1080p @ 60 */ + <244800 186000000 0x55555555>, /* 1080p @ 30 */ + <108000 100000000 0x55555555>, /* 720p @ 30 */ /* Decoders */ - <1944000 490000000 0xffffffff>, /* 4k UHD @ 60 */ - < 972000 320000000 0xffffffff>, /* 4k UHD @ 30 */ - < 489600 150000000 0xffffffff>, /* 1080p @ 60 */ - < 244800 75000000 0xffffffff>; /* 1080p @ 30 */ + <1944000 465000000 0xffffffff>, /* 4k UHD @ 60 */ + < 972000 360000000 0xffffffff>, /* 4k UHD @ 30 */ + < 489600 186000000 0xffffffff>, /* 1080p @ 60 */ + < 244800 100000000 0xffffffff>; /* 1080p @ 30 */ qcom,dcvs-tbl = <972000 972000 19944000 0x3f00000c>, /* UHD 30 */ @@ -63,26 +63,29 @@ * corresponding video core frequency. */ qcom,imem-ab-tbl = - <75000000 1500000>, /* imem @ svs2 freq 75 Mhz */ - <150000000 1500000>, /* imem @ svs2 freq 75 Mhz */ - <320000000 2500000>, /* imem @ svs freq 171 Mhz */ - <490000000 6000000>; /* imem @ noimal freq 320 Mhz */ + <100000000 1500000>, /* imem @ svs2 freq 75 Mhz */ + <186000000 1500000>, /* imem @ svs2 freq 75 Mhz */ + <360000000 2500000>, /* imem @ svs freq 171 Mhz */ + <465000000 6000000>; /* imem @ noimal freq 320 Mhz */ /* Regulators */ + smmu-vdd-supply = <&gdsc_bimc_smmu>; venus-supply = <&gdsc_venus>; venus-core0-supply = <&gdsc_venus_core0>; venus-core1-supply = <&gdsc_venus_core1>; /* Clocks */ clock-names = "clk_gcc_mmss_sys_noc_axi_clk", + "noc_axi", "mnoc_ahb_clk", "smmu_ahb_clk", "smmu_axi_clk", - "mnoc_ahb_clk", "mmss_maxi_clk", + "mmss_maxi_clk", "core_clk", "iface_clk", "bus_clk", "maxi_clk", "core0_clk", "core1_clk"; clocks = <&clock_gcc clk_gcc_mmss_sys_noc_axi_clk>, + <&clock_gcc clk_mmssnoc_axi_clk>, + <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_ahb_clk>, <&clock_mmss clk_mmss_bimc_smmu_axi_clk>, - <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_mnoc_maxi_clk>, <&clock_mmss clk_mmss_video_core_clk>, <&clock_mmss clk_mmss_video_ahb_clk>, @@ -90,7 +93,7 @@ <&clock_mmss clk_mmss_video_maxi_clk>, <&clock_mmss clk_mmss_video_subcore0_clk>, <&clock_mmss clk_mmss_video_subcore1_clk>; - qcom,clock-configs = <0x0 0x0 0x0 0x0 0x0 + qcom,clock-configs = <0x0 0x0 0x0 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x1 0x1>; /* Buses */ @@ -166,7 +169,51 @@ iommus = <&mmss_smmu 0x580>, <&mmss_smmu 0x586>; }; + secure_bitstream_cb { + compatible = "qcom,msm-vidc,context-bank"; + label = "venus_sec_bitstream"; + iommus = <&mmss_smmu 0x500>, + <&mmss_smmu 0x502>, + <&mmss_smmu 0x509>, + <&mmss_smmu 0x50a>, + <&mmss_smmu 0x50b>, + <&mmss_smmu 0x50e>, + <&mmss_smmu 0x526>, + <&mmss_smmu 0x529>, + <&mmss_smmu 0x52b>; + buffer-types = <0x241>; + virtual-addr-pool = <0x4b000000 0x25800000>; + qcom,secure-context-bank; + }; + venus_secure_pixel_cb: secure_pixel_cb { + compatible = "qcom,msm-vidc,context-bank"; + label = "venus_sec_pixel"; + iommus = <&mmss_smmu 0x504>, + <&mmss_smmu 0x50c>, + <&mmss_smmu 0x510>, + <&mmss_smmu 0x52c>; + buffer-types = <0x106>; + virtual-addr-pool = <0x25800000 0x25800000>; + qcom,secure-context-bank; + }; + + venus_secure_non_pixel_cb: secure_non_pixel_cb { + compatible = "qcom,msm-vidc,context-bank"; + label = "venus_sec_non_pixel"; + iommus = <&mmss_smmu 0x505>, + <&mmss_smmu 0x507>, + <&mmss_smmu 0x508>, + <&mmss_smmu 0x50d>, + <&mmss_smmu 0x50f>, + <&mmss_smmu 0x525>, + <&mmss_smmu 0x528>, + <&mmss_smmu 0x52d>, + <&mmss_smmu 0x540>; + buffer-types = <0x480>; + virtual-addr-pool = <0x1000000 0x24800000>; + qcom,secure-context-bank; + }; }; qcom,vmem@c880000 { diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi index c346bf0411bd..03ae8880d531 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi @@ -402,6 +402,7 @@ reg-names = "slimbus_physical", "slimbus_bam_physical"; interrupts = <0 163 0>, <0 164 0>; interrupt-names = "slimbus_irq", "slimbus_bam_irq"; + qcom,apps-ch-pipes = <0x00001f80>; qcom,ea-pc = <0x210>; }; @@ -1965,6 +1966,24 @@ }; }; + qcom,bcl { + compatible = "qcom,bcl"; + qcom,bcl-enable; + qcom,bcl-framework-interface; + qcom,bcl-freq-control-list = <&CPU4 &CPU5 &CPU6 &CPU7>; + qcom,bcl-hotplug-list = <&CPU4 &CPU5 &CPU6 &CPU7>; + qcom,bcl-soc-hotplug-list = <&CPU4 &CPU5 &CPU6 &CPU7>; + qcom,ibat-monitor { + qcom,low-threshold-uamp = <3400000>; + qcom,high-threshold-uamp = <4200000>; + qcom,mitigation-freq-khz = <576000>; + qcom,vph-high-threshold-uv = <3500000>; + qcom,vph-low-threshold-uv = <3300000>; + qcom,soc-low-threshold = <10>; + qcom,thermal-handle = <&msm_thermal_freq>; + }; + }; + qcom,ssc@5c00000 { compatible = "qcom,pil-tz-generic"; reg = <0x5c00000 0x4000>; @@ -2245,6 +2264,8 @@ <0 424 0 /* CE10 */ >, <0 425 0 /* CE11 */ >; qcom,wlan-msa-memory = <0x100000>; + vdd-io-supply = <&pmcobalt_l5>; + qcom,vdd-io-voltage-level = <800000 800000>; }; }; diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index f4dfb0474bcb..1b862b4e51e0 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -257,6 +257,7 @@ CONFIG_NETDEVICES=y CONFIG_TUN=y CONFIG_VIRTIO_NET=y CONFIG_SKY2=y +CONFIG_RNDIS_IPA=y CONFIG_SMSC911X=y CONFIG_PPP=y CONFIG_PPP_BSDCOMP=y @@ -311,12 +312,16 @@ CONFIG_MSM_APM=y CONFIG_QPNP_SMBCHARGER=y CONFIG_SMB135X_CHARGER=y CONFIG_SMB1351_USB_CHARGER=y +CONFIG_MSM_BCL_CTL=y +CONFIG_MSM_BCL_PERIPHERAL_CTL=y +CONFIG_BATTERY_BCL=y CONFIG_QPNP_SMB2=y -# CONFIG_HWMON is not set +CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y CONFIG_LIMITS_MONITOR=y CONFIG_LIMITS_LITE_HW=y CONFIG_THERMAL_MONITOR=y CONFIG_THERMAL_TSENS8974=y +CONFIG_THERMAL_QPNP_ADC_TM=y CONFIG_QCOM_THERMAL_LIMITS_DCVS=y CONFIG_MFD_SPMI_PMIC=y CONFIG_WCD9335_CODEC=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index 720f7b642568..7dc8ef6a2ee5 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -258,6 +258,7 @@ CONFIG_NETDEVICES=y CONFIG_TUN=y CONFIG_VIRTIO_NET=y CONFIG_SKY2=y +CONFIG_RNDIS_IPA=y CONFIG_SMSC911X=y CONFIG_PPP=y CONFIG_PPP_BSDCOMP=y @@ -283,8 +284,6 @@ CONFIG_INPUT_GPIO=y CONFIG_SERIO_AMBAKMI=y # CONFIG_VT is not set # CONFIG_LEGACY_PTYS is not set -# CONFIG_DEVMEM is not set -# CONFIG_DEVKMEM is not set CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_8250_DW=y @@ -323,12 +322,16 @@ CONFIG_MSM_APM=y CONFIG_QPNP_SMBCHARGER=y CONFIG_SMB135X_CHARGER=y CONFIG_SMB1351_USB_CHARGER=y +CONFIG_MSM_BCL_CTL=y +CONFIG_MSM_BCL_PERIPHERAL_CTL=y +CONFIG_BATTERY_BCL=y CONFIG_QPNP_SMB2=y -# CONFIG_HWMON is not set +CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y CONFIG_LIMITS_MONITOR=y CONFIG_LIMITS_LITE_HW=y CONFIG_THERMAL_MONITOR=y CONFIG_THERMAL_TSENS8974=y +CONFIG_THERMAL_QPNP_ADC_TM=y CONFIG_QCOM_THERMAL_LIMITS_DCVS=y CONFIG_MFD_SPMI_PMIC=y CONFIG_WCD9335_CODEC=y @@ -582,6 +585,18 @@ CONFIG_PID_IN_CONTEXTIDR=y CONFIG_DEBUG_SET_MODULE_RONX=y CONFIG_DEBUG_RODATA=y CONFIG_FREE_PAGES_RDONLY=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_SECURITY=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SMACK=y diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 32ded9b6e22b..61cdbaa3c09a 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -85,6 +85,9 @@ struct regmap { struct list_head debugfs_off_cache; struct mutex cache_lock; + + unsigned int dump_address; + unsigned int dump_count; #endif unsigned int max_register; diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 243718679150..f16fa6cc23bc 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -310,6 +310,67 @@ static const struct file_operations regmap_map_fops = { .llseek = default_llseek, }; +static ssize_t regmap_data_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct regmap *map = file->private_data; + int new_count; + + regmap_calc_tot_len(map, NULL, 0); + new_count = map->dump_count * map->debugfs_tot_len; + if (new_count > count) + new_count = count; + + if (*ppos == 0) + *ppos = map->dump_address * map->debugfs_tot_len; + else if (*ppos >= map->dump_address * map->debugfs_tot_len + + map->dump_count * map->debugfs_tot_len) + return 0; + return regmap_read_debugfs(map, 0, map->max_register, user_buf, + new_count, ppos); +} + +#ifdef REGMAP_ALLOW_WRITE_DEBUGFS +static ssize_t regmap_data_write_file(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + char buf[32]; + size_t buf_size; + char *start = buf; + unsigned long value; + struct regmap *map = file->private_data; + int ret; + + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + while (*start == ' ') + start++; + if (kstrtoul(start, 16, &value)) + return -EINVAL; + + /* Userspace has been fiddling around behind the kernel's back */ + add_taint(TAINT_USER, LOCKDEP_STILL_OK); + + ret = regmap_write(map, map->dump_address, value); + if (ret < 0) + return ret; + return buf_size; +} +#else +#define regmap_data_write_file NULL +#endif + +static const struct file_operations regmap_data_fops = { + .open = simple_open, + .read = regmap_data_read_file, + .write = regmap_data_write_file, + .llseek = default_llseek, +}; + static ssize_t regmap_range_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -603,6 +664,14 @@ void regmap_debugfs_init(struct regmap *map, const char *name) debugfs_create_file("registers", registers_mode, map->debugfs, map, ®map_map_fops); + + debugfs_create_x32("address", 0600, map->debugfs, + &map->dump_address); + debugfs_create_u32("count", 0600, map->debugfs, + &map->dump_count); + debugfs_create_file("data", registers_mode, map->debugfs, + map, ®map_data_fops); + debugfs_create_file("access", 0400, map->debugfs, map, ®map_access_fops); } diff --git a/drivers/clk/msm/clock-gcc-cobalt.c b/drivers/clk/msm/clock-gcc-cobalt.c index 7c17ef357165..6f40e36ef5d9 100644 --- a/drivers/clk/msm/clock-gcc-cobalt.c +++ b/drivers/clk/msm/clock-gcc-cobalt.c @@ -1031,7 +1031,7 @@ static struct rcg_clk hmss_gpll0_clk_src = { .c = { .dbg_name = "hmss_gpll0_clk_src", .ops = &clk_ops_rcg, - VDD_DIG_FMAX_MAP2(LOWER, 150000000, NOMINAL, 600000000), + VDD_DIG_FMAX_MAP1(LOWER, 600000000), CLK_INIT(hmss_gpll0_clk_src.c), }, }; diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c index 142704fd8897..baea421badfd 100644 --- a/drivers/clk/msm/clock-osm.c +++ b/drivers/clk/msm/clock-osm.c @@ -108,6 +108,7 @@ enum clk_osm_trace_packet_id { #define PLL_L_VAL 0x4 #define PLL_USER_CTRL 0xC #define PLL_CONFIG_CTL_LO 0x10 +#define PLL_TEST_CTL_HI 0x1C #define PLL_STATUS 0x2C #define PLL_LOCK_DET_MASK BIT(16) #define PLL_WAIT_LOCK_TIME_US 5 @@ -1507,6 +1508,16 @@ static void clk_osm_setup_osm_was(struct clk_osm *c) val &= ~IGNORE_PLL_LOCK_MASK; scm_io_write(c->pbases[OSM_BASE] + SEQ_REG(48), val); } + + if (c->cluster_num == 0) { + val = readl_relaxed(c->vbases[PLL_BASE] + PLL_TEST_CTL_HI) + | BIT(13); + writel_relaxed(val, c->vbases[PLL_BASE] + + PLL_TEST_CTL_HI); + } + + /* Ensure writes complete before returning */ + mb(); } static void clk_osm_setup_fsms(struct clk_osm *c) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index fa981cf575a6..36c336a0477e 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1855,6 +1855,26 @@ static int adreno_getproperty(struct kgsl_device *device, } status = 0; break; + case KGSL_PROP_DEVICE_BITNESS: + { + unsigned int bitness = 32; + + if (sizebytes != sizeof(unsigned int)) { + status = -EINVAL; + break; + } + /* No of bits used by the GPU */ + if (adreno_support_64bit(adreno_dev)) + bitness = 48; + + if (copy_to_user(value, &bitness, + sizeof(unsigned int))) { + status = -EFAULT; + break; + } + status = 0; + } + break; default: status = -EINVAL; diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index e173dc594789..b34e8f6ae433 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.c @@ -161,6 +161,7 @@ static const struct { } a5xx_efuse_funcs[] = { { adreno_is_a530, a530_efuse_leakage }, { adreno_is_a530, a530_efuse_speed_bin }, + { adreno_is_a505, a530_efuse_speed_bin }, }; static void a5xx_check_features(struct adreno_device *adreno_dev) diff --git a/drivers/gpu/msm/adreno_a5xx_snapshot.c b/drivers/gpu/msm/adreno_a5xx_snapshot.c index 154f5af99ffe..dc5dee7ce0c9 100644 --- a/drivers/gpu/msm/adreno_a5xx_snapshot.c +++ b/drivers/gpu/msm/adreno_a5xx_snapshot.c @@ -414,48 +414,47 @@ static const unsigned int a5xx_registers[] = { 0xB000, 0xB97F, 0xB9A0, 0xB9BF, }; -struct a5xx_hlsq_sp_tp_regs { - unsigned int statetype; - unsigned int ahbaddr; - unsigned int size; -}; - -static const struct a5xx_hlsq_sp_tp_regs a5xx_hlsq_sp_tp_registers[] = { - /* HLSQ CTX 0 2D */ - { 0x31, 0x2080, 0x1 }, - /* HLSQ CTX 1 2D */ - { 0x33, 0x2480, 0x1 }, - /* HLSQ CTX 0 3D */ - { 0x32, 0xE780, 0x7f }, - /* HLSQ CTX 1 3D */ - { 0x34, 0xEF80, 0x7f }, - +/* + * The HLSQ registers can only be read via the crash dumper (not AHB) so they + * need to be in their own array because the array above does double duty for + * the fallback path too + */ +static const unsigned int a5xx_hlsq_registers[] = { /* SP non context */ - { 0x3f, 0x0EC0, 0x40 }, + 0x0EC0, 0xEC2, 0xED0, 0xEE0, 0xEF0, 0xEF2, 0xEFA, 0xEFF, /* SP CTX 0 2D */ - { 0x3d, 0x2040, 0x1 }, + 0x2040, 0x2040, /* SP CTX 1 2D */ - { 0x3b, 0x2440, 0x1 }, - /* SP CTX 0 3D */ - { 0x3e, 0xE580, 0x180 }, - /* SP CTX 1 3D */ - { 0x3c, 0xED80, 0x180 }, - + 0x2440, 0x2440, + /* SP CTXT 0 3D */ + 0xE580, 0xE580, 0xE584, 0xE58B, 0xE590, 0xE5B1, 0xE5C0, 0xE5DF, + 0xE5F0, 0xE5F9, 0xE600, 0xE608, 0xE610, 0xE631, 0xE640, 0xE661, + 0xE670, 0xE673, 0xE6F0, 0xE6F0, + /* SP CTXT 1 3D */ + 0xED80, 0xED80, 0xED84, 0xED8B, 0xED90, 0xEDB1, 0xEDC0, 0xEDDF, + 0xEDF0, 0xEDF9, 0xEE00, 0xEE08, 0xEE10, 0xEE31, 0xEE40, 0xEE61, + 0xEE70, 0xEE73, 0xEEF0, 0xEEF0, /* TP non context */ - { 0x3a, 0x0F00, 0x40 }, + 0xF00, 0xF03, 0xF08, 0xF08, 0xF10, 0xF1B, /* TP CTX 0 2D */ - { 0x38, 0x2000, 0x10 }, + 0x2000, 0x2009, /* TP CTX 1 2D */ - { 0x36, 0x2400, 0x10 }, + 0x2400, 0x2409, /* TP CTX 0 3D */ - { 0x39, 0xE700, 0x128 }, + 0xE700, 0xE707, 0xE70E, 0xE731, + 0xE750, 0xE751, 0xE75A, 0xE764, 0xE76C, 0xE77F, /* TP CTX 1 3D */ - { 0x37, 0xEF00, 0x128 }, -}; - -/* HLSQ non context registers - can't be read on A530v1 */ -static const struct a5xx_hlsq_sp_tp_regs a5xx_hlsq_non_ctx_registers = { - 0x35, 0xE00, 0x1C + 0xEF00, 0xEF07, 0xEF0E, 0xEF31, + 0xEF50, 0xEF51, 0xEF5A, 0xEF64, 0xEF6C, 0xEF7F, + /* HLSQ non context */ + 0xE00, 0xE01, 0xE04, 0xE06, 0xE08, 0xE09, 0xE10, 0xE17, + 0xE20, 0xE25, + /* HLSQ CTXT 0 3D */ + 0xE784, 0xE789, 0xE78B, 0xE796, 0xE7A0, 0xE7A2, 0xE7B0, 0xE7BB, + 0xE7C0, 0xE7DD, 0xE7E0, 0xE7E1, + /* HLSQ CTXT 1 3D */ + 0xEF84, 0xEF89, 0xEF8B, 0xEF96, 0xEFA0, 0xEFA2, 0xEFB0, 0xEFBB, + 0xEFC0, 0xEFDD, 0xEFE0, 0xEFE1, }; #define A5XX_NUM_SHADER_BANKS 4 @@ -520,14 +519,16 @@ enum a5xx_shader_obj { struct a5xx_shader_block { unsigned int statetype; unsigned int sz; + uint64_t offset; }; struct a5xx_shader_block_info { - const struct a5xx_shader_block *shader_block; - unsigned int shader_num; + struct a5xx_shader_block *block; + unsigned int bank; + uint64_t offset; }; -static const struct a5xx_shader_block a5xx_shader_blocks[] = { +static struct a5xx_shader_block a5xx_shader_blocks[] = { {A5XX_TP_W_MEMOBJ, 0x200}, {A5XX_TP_W_MIPMAP_BASE, 0x3C0}, {A5XX_TP_W_SAMPLER_TAG, 0x40}, @@ -582,181 +583,174 @@ static const struct a5xx_shader_block a5xx_shader_blocks[] = { {A5XX_TP_POWER_RESTORE_RAM, 0x40}, }; +static struct kgsl_memdesc capturescript; +static struct kgsl_memdesc registers; +static bool crash_dump_valid; + static size_t a5xx_snapshot_shader_memory(struct kgsl_device *device, u8 *buf, size_t remain, void *priv) { struct kgsl_snapshot_shader *header = - (struct kgsl_snapshot_shader *)buf; - unsigned int *data = (unsigned int *)(buf + sizeof(*header)); - unsigned int i; - struct a5xx_shader_block_info *shader_block_info = - (struct a5xx_shader_block_info *)priv; - unsigned int statetype = shader_block_info->shader_block->statetype; - unsigned int size = shader_block_info->shader_block->sz; - unsigned int shader_num = shader_block_info->shader_num; - + (struct kgsl_snapshot_shader *) buf; + struct a5xx_shader_block_info *info = + (struct a5xx_shader_block_info *) priv; + struct a5xx_shader_block *block = info->block; + unsigned int *data = (unsigned int *) (buf + sizeof(*header)); - if (remain < SHADER_SECTION_SZ(size)) { + if (remain < SHADER_SECTION_SZ(block->sz)) { SNAPSHOT_ERR_NOMEM(device, "SHADER MEMORY"); return 0; } - kgsl_regwrite(device, A5XX_HLSQ_DBG_READ_SEL, - ((statetype << A5XX_SHADER_STATETYPE_SHIFT) | shader_num)); + header->type = block->statetype; + header->index = info->bank; + header->size = block->sz; - header->type = statetype; - header->index = shader_num; - header->size = size; - - for (i = 0; i < size; i++) - kgsl_regread(device, A5XX_HLSQ_DBG_AHB_READ_APERTURE + i, - data++); + memcpy(data, registers.hostptr + info->offset, block->sz); - return SHADER_SECTION_SZ(size); + return SHADER_SECTION_SZ(block->sz); } static void a5xx_snapshot_shader(struct kgsl_device *device, struct kgsl_snapshot *snapshot) { unsigned int i, j; - struct a5xx_shader_block_info blk; + struct a5xx_shader_block_info info; + + /* Shader blocks can only be read by the crash dumper */ + if (crash_dump_valid == false) + return; for (i = 0; i < ARRAY_SIZE(a5xx_shader_blocks); i++) { for (j = 0; j < A5XX_NUM_SHADER_BANKS; j++) { - blk.shader_block = &a5xx_shader_blocks[i]; - blk.shader_num = j; + info.block = &a5xx_shader_blocks[i]; + info.bank = j; + info.offset = a5xx_shader_blocks[i].offset + + (j * a5xx_shader_blocks[i].sz); + /* Shader working/shadow memory */ kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_SHADER, - snapshot, a5xx_snapshot_shader_memory, &blk); + snapshot, a5xx_snapshot_shader_memory, &info); } } } -static int get_hlsq_registers(struct kgsl_device *device, - const struct a5xx_hlsq_sp_tp_regs *regs, unsigned int *data) +static size_t a5xx_legacy_snapshot_registers(struct kgsl_device *device, + u8 *buf, size_t remain) { - int j; - unsigned int val; + struct kgsl_snapshot_registers regs = { + .regs = a5xx_registers, + .count = ARRAY_SIZE(a5xx_registers) / 2, + }; - kgsl_regwrite(device, A5XX_HLSQ_DBG_READ_SEL, - (regs->statetype << A5XX_SHADER_STATETYPE_SHIFT)); + return kgsl_snapshot_dump_registers(device, buf, remain, ®s); +} - for (j = 0; j < regs->size; j++) { - kgsl_regread(device, A5XX_HLSQ_DBG_AHB_READ_APERTURE + j, &val); - *data++ = regs->ahbaddr + j; - *data++ = val; - } +static struct cdregs { + const unsigned int *regs; + unsigned int size; +} _a5xx_cd_registers[] = { + { a5xx_registers, ARRAY_SIZE(a5xx_registers) }, + { a5xx_hlsq_registers, ARRAY_SIZE(a5xx_hlsq_registers) }, +}; - return (regs->size * 2); -} +#define REG_PAIR_COUNT(_a, _i) \ + (((_a)[(2 * (_i)) + 1] - (_a)[2 * (_i)]) + 1) -static size_t a5xx_snapshot_dump_hlsq_sp_tp_regs(struct kgsl_device *device, - u8 *buf, size_t remain, void *priv) +static size_t a5xx_snapshot_registers(struct kgsl_device *device, u8 *buf, + size_t remain, void *priv) { - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); struct kgsl_snapshot_regs *header = (struct kgsl_snapshot_regs *)buf; unsigned int *data = (unsigned int *)(buf + sizeof(*header)); - int count = 0, i; - - /* Figure out how many registers we are going to dump */ - for (i = 0; i < ARRAY_SIZE(a5xx_hlsq_sp_tp_registers); i++) - count += a5xx_hlsq_sp_tp_registers[i].size; + unsigned int *src = (unsigned int *) registers.hostptr; + unsigned int i, j, k; + unsigned int count = 0; - /* the HLSQ non context registers cannot be dumped on A530v1 */ - if (!adreno_is_a530v1(adreno_dev)) - count += a5xx_hlsq_non_ctx_registers.size; + if (crash_dump_valid == false) + return a5xx_legacy_snapshot_registers(device, buf, remain); - if (remain < (count * 8) + sizeof(*header)) { + if (remain < sizeof(*header)) { SNAPSHOT_ERR_NOMEM(device, "REGISTERS"); return 0; } - for (i = 0; i < ARRAY_SIZE(a5xx_hlsq_sp_tp_registers); i++) - data += get_hlsq_registers(device, - &a5xx_hlsq_sp_tp_registers[i], data); + remain -= sizeof(*header); + + for (i = 0; i < ARRAY_SIZE(_a5xx_cd_registers); i++) { + struct cdregs *regs = &_a5xx_cd_registers[i]; + + for (j = 0; j < regs->size / 2; j++) { + unsigned int start = regs->regs[2 * j]; + unsigned int end = regs->regs[(2 * j) + 1]; + + if (remain < ((end - start) + 1) * 8) { + SNAPSHOT_ERR_NOMEM(device, "REGISTERS"); + goto out; + } + + remain -= ((end - start) + 1) * 8; - if (!adreno_is_a530v1(adreno_dev)) - data += get_hlsq_registers(device, - &a5xx_hlsq_non_ctx_registers, data); + for (k = start; k <= end; k++, count++) { + *data++ = k; + *data++ = *src++; + } + } + } +out: header->count = count; /* Return the size of the section */ return (count * 8) + sizeof(*header); } -static size_t a5xx_legacy_snapshot_registers(struct kgsl_device *device, - u8 *buf, size_t remain) +/* Snapshot a preemption record buffer */ +static size_t snapshot_preemption_record(struct kgsl_device *device, u8 *buf, + size_t remain, void *priv) { - struct kgsl_snapshot_registers regs = { - .regs = a5xx_registers, - .count = ARRAY_SIZE(a5xx_registers) / 2, - }; + struct kgsl_memdesc *memdesc = priv; - return kgsl_snapshot_dump_registers(device, buf, remain, ®s); -} + struct kgsl_snapshot_gpu_object_v2 *header = + (struct kgsl_snapshot_gpu_object_v2 *)buf; -static struct kgsl_memdesc capturescript; -static struct kgsl_memdesc registers; + u8 *ptr = buf + sizeof(*header); -#define REG_PAIR_COUNT(_a, _i) \ - (((_a)[(2 * (_i)) + 1] - (_a)[2 * (_i)]) + 1) + if (remain < (SZ_64K + sizeof(*header))) { + SNAPSHOT_ERR_NOMEM(device, "PREEMPTION RECORD"); + return 0; + } -static inline unsigned int count_registers(void) -{ - unsigned int i, count = 0; + header->size = SZ_64K >> 2; + header->gpuaddr = memdesc->gpuaddr; + header->ptbase = + kgsl_mmu_pagetable_get_ttbr0(device->mmu.defaultpagetable); + header->type = SNAPSHOT_GPU_OBJECT_GLOBAL; - for (i = 0; i < ARRAY_SIZE(a5xx_registers) / 2; i++) - count += REG_PAIR_COUNT(a5xx_registers, i); + memcpy(ptr, memdesc->hostptr, SZ_64K); - return count; + return SZ_64K + sizeof(*header); } -static unsigned int copy_registers(unsigned int *dst) -{ - unsigned int *src = (unsigned int *) registers.hostptr; - unsigned int i, count = 0; - for (i = 0; i < ARRAY_SIZE(a5xx_registers) / 2; i++) { - unsigned int j; - unsigned int start = a5xx_registers[2 * i]; - unsigned int end = a5xx_registers[(2 * i) + 1]; - - for (j = start; j <= end; j++, count++) { - *dst++ = j; - *dst++ = *src++; - } - } - - return count; -} - -static size_t a5xx_snapshot_registers(struct kgsl_device *device, u8 *buf, - size_t remain, void *priv) +static void _a5xx_do_crashdump(struct kgsl_device *device) { - struct kgsl_snapshot_regs *header = (struct kgsl_snapshot_regs *)buf; - unsigned int *data = (unsigned int *)(buf + sizeof(*header)); unsigned long wait_time; unsigned int reg = 0; unsigned int val; - /* Jump to legacy if the crash dump script was not initialized */ + crash_dump_valid = false; + if (capturescript.gpuaddr == 0 || registers.gpuaddr == 0) - return a5xx_legacy_snapshot_registers(device, buf, remain); + return; - /* - * If we got here because we are stalled on fault the crash dumper has - * won't work - */ + /* IF the SMMU is stalled we cannot do a crash dump */ kgsl_regread(device, A5XX_RBBM_STATUS3, &val); if (val & BIT(24)) - return a5xx_legacy_snapshot_registers(device, buf, remain); + return; - if (remain < (count_registers() * 8) + sizeof(*header)) { - SNAPSHOT_ERR_NOMEM(device, "REGISTERS"); - return 0; - } + /* Turn on APRIV so we can access the buffers */ + kgsl_regwrite(device, A5XX_CP_CNTL, 1); kgsl_regwrite(device, A5XX_CP_CRASH_SCRIPT_BASE_LO, lower_32_bits(capturescript.gpuaddr)); @@ -772,42 +766,14 @@ static size_t a5xx_snapshot_registers(struct kgsl_device *device, u8 *buf, cpu_relax(); } + kgsl_regwrite(device, A5XX_CP_CNTL, 0); + if (!(reg & 0x4)) { KGSL_CORE_ERR("Crash dump timed out: 0x%X\n", reg); - return a5xx_legacy_snapshot_registers(device, buf, remain); - } - - header->count = copy_registers(data); - - /* Return the size of the section */ - return (header->count * 8) + sizeof(*header); -} - -/* Snapshot a preemption record buffer */ -static size_t snapshot_preemption_record(struct kgsl_device *device, u8 *buf, - size_t remain, void *priv) -{ - struct kgsl_memdesc *memdesc = priv; - - struct kgsl_snapshot_gpu_object_v2 *header = - (struct kgsl_snapshot_gpu_object_v2 *)buf; - - u8 *ptr = buf + sizeof(*header); - - if (remain < (SZ_64K + sizeof(*header))) { - SNAPSHOT_ERR_NOMEM(device, "PREEMPTION RECORD"); - return 0; + return; } - header->size = SZ_64K >> 2; - header->gpuaddr = memdesc->gpuaddr; - header->ptbase = - kgsl_mmu_pagetable_get_ttbr0(device->mmu.defaultpagetable); - header->type = SNAPSHOT_GPU_OBJECT_GLOBAL; - - memcpy(ptr, memdesc->hostptr, SZ_64K); - - return SZ_64K + sizeof(*header); + crash_dump_valid = true; } /* @@ -830,6 +796,9 @@ void a5xx_snapshot(struct adreno_device *adreno_dev, /* Disable Clock gating temporarily for the debug bus to work */ a5xx_hwcg_set(adreno_dev, false); + /* Try to run the crash dumper */ + _a5xx_do_crashdump(device); + kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS, snapshot, a5xx_snapshot_registers, NULL); @@ -837,10 +806,6 @@ void a5xx_snapshot(struct adreno_device *adreno_dev, a5xx_vbif_snapshot_registers, ARRAY_SIZE(a5xx_vbif_snapshot_registers)); - /* Dump SP TP HLSQ registers */ - kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS, snapshot, - a5xx_snapshot_dump_hlsq_sp_tp_regs, NULL); - /* CP_PFP indexed registers */ kgsl_snapshot_indexed_registers(device, snapshot, A5XX_CP_PFP_STAT_ADDR, A5XX_CP_PFP_STAT_DATA, @@ -913,34 +878,93 @@ void a5xx_snapshot(struct adreno_device *adreno_dev, } +static int _a5xx_crashdump_init(struct a5xx_shader_block *block, uint64_t *ptr, + uint64_t *offset) +{ + int qwords = 0; + unsigned int j; + + /* Capture each bank in the block */ + for (j = 0; j < A5XX_NUM_SHADER_BANKS; j++) { + /* Program the aperture */ + ptr[qwords++] = + (block->statetype << A5XX_SHADER_STATETYPE_SHIFT) | j; + ptr[qwords++] = (((uint64_t) A5XX_HLSQ_DBG_READ_SEL << 44)) | + (1 << 21) | 1; + + /* Read all the data in one chunk */ + ptr[qwords++] = registers.gpuaddr + *offset; + ptr[qwords++] = + (((uint64_t) A5XX_HLSQ_DBG_AHB_READ_APERTURE << 44)) | + block->sz; + + /* Remember the offset of the first bank for easy access */ + if (j == 0) + block->offset = *offset; + + *offset += block->sz * sizeof(unsigned int); + } + + return qwords; +} + void a5xx_crashdump_init(struct adreno_device *adreno_dev) { - unsigned int i, count; + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + unsigned int script_size = 0; + unsigned int data_size = 0; + unsigned int i, j; uint64_t *ptr; - uint64_t gpuaddr; + uint64_t offset = 0; if (capturescript.gpuaddr != 0 && registers.gpuaddr != 0) return; /* - * For the capture script two blocks of memory are needed: A block of - * GPU readonly memory for the special capture script and a destination - * block for the register values. The size of the capture script needs - * is 128 bits (4 dwords) per register pair and 4 dwords at the end. - * The destination block needs to be big enough to hold all the - * registers that we will capture. + * We need to allocate two buffers: + * 1 - the buffer to hold the draw script + * 2 - the buffer to hold the data */ - if (kgsl_allocate_global(KGSL_DEVICE(adreno_dev), &capturescript, - ((ARRAY_SIZE(a5xx_registers) / 2) * 16) + 16, - KGSL_MEMFLAGS_GPUREADONLY, 0)) - return; + /* + * To save the registers, we need 16 bytes per register pair for the + * script and a dword for each register int the data + */ + for (i = 0; i < ARRAY_SIZE(_a5xx_cd_registers); i++) { + struct cdregs *regs = &_a5xx_cd_registers[i]; + + /* Each pair needs 16 bytes (2 qwords) */ + script_size += (regs->size / 2) * 16; - /* Count the total number of registers to capture */ - count = count_registers(); + /* Each register needs a dword in the data */ + for (j = 0; j < regs->size / 2; j++) + data_size += REG_PAIR_COUNT(regs->regs, j) * + sizeof(unsigned int); - if (kgsl_allocate_global(KGSL_DEVICE(adreno_dev), ®isters, - count * sizeof(unsigned int), 0, 0)) { + } + + /* + * To save the shader blocks for each block in each type we need 32 + * bytes for the script (16 bytes to program the aperture and 16 to + * read the data) and then a block specific number of bytes to hold + * the data + */ + for (i = 0; i < ARRAY_SIZE(a5xx_shader_blocks); i++) { + script_size += 32 * A5XX_NUM_SHADER_BANKS; + data_size += a5xx_shader_blocks[i].sz * sizeof(unsigned int) * + A5XX_NUM_SHADER_BANKS; + } + + /* Now allocate the script and data buffers */ + + /* The script buffers needs 2 extra qwords on the end */ + if (kgsl_allocate_global(device, &capturescript, + script_size + 16, KGSL_MEMFLAGS_GPUREADONLY, + KGSL_MEMDESC_PRIVILEGED)) + return; + + if (kgsl_allocate_global(device, ®isters, data_size, 0, + KGSL_MEMDESC_PRIVILEGED)) { kgsl_free_global(KGSL_DEVICE(adreno_dev), &capturescript); return; } @@ -948,14 +972,23 @@ void a5xx_crashdump_init(struct adreno_device *adreno_dev) /* Build the crash script */ ptr = (uint64_t *) capturescript.hostptr; - gpuaddr = registers.gpuaddr; - for (i = 0; i < ARRAY_SIZE(a5xx_registers) / 2; i++) { - unsigned int regs = REG_PAIR_COUNT(a5xx_registers, i); - *ptr++ = gpuaddr; - *ptr++ = (((uint64_t) a5xx_registers[2 * i]) << 44) | regs; + /* For the registers, program a read command for each pair */ + for (i = 0; i < ARRAY_SIZE(_a5xx_cd_registers); i++) { + struct cdregs *regs = &_a5xx_cd_registers[i]; - gpuaddr += regs * sizeof(unsigned int); + for (j = 0; j < regs->size / 2; j++) { + unsigned int r = REG_PAIR_COUNT(regs->regs, j); + *ptr++ = registers.gpuaddr + offset; + *ptr++ = (((uint64_t) regs->regs[2 * j]) << 44) | r; + offset += r * sizeof(unsigned int); + } + } + + /* Program each shader block */ + for (i = 0; i < ARRAY_SIZE(a5xx_shader_blocks); i++) { + ptr += _a5xx_crashdump_init(&a5xx_shader_blocks[i], ptr, + &offset); } *ptr++ = 0; diff --git a/drivers/gpu/msm/kgsl_cmdbatch.c b/drivers/gpu/msm/kgsl_cmdbatch.c index f0d674161036..ceca8b1e1522 100644 --- a/drivers/gpu/msm/kgsl_cmdbatch.c +++ b/drivers/gpu/msm/kgsl_cmdbatch.c @@ -539,42 +539,41 @@ static void add_profiling_buffer(struct kgsl_device *device, int kgsl_cmdbatch_add_ibdesc(struct kgsl_device *device, struct kgsl_cmdbatch *cmdbatch, struct kgsl_ibdesc *ibdesc) { + uint64_t gpuaddr = (uint64_t) ibdesc->gpuaddr; + uint64_t size = (uint64_t) ibdesc->sizedwords << 2; struct kgsl_memobj_node *mem; + /* sanitize the ibdesc ctrl flags */ + ibdesc->ctrl &= KGSL_IBDESC_MEMLIST | KGSL_IBDESC_PROFILING_BUFFER; + + if (cmdbatch->flags & KGSL_CMDBATCH_MEMLIST && + ibdesc->ctrl & KGSL_IBDESC_MEMLIST) { + if (ibdesc->ctrl & KGSL_IBDESC_PROFILING_BUFFER) { + add_profiling_buffer(device, cmdbatch, + gpuaddr, size, 0, 0); + return 0; + } + } + + if (cmdbatch->flags & (KGSL_CMDBATCH_SYNC | KGSL_CMDBATCH_MARKER)) + return 0; + mem = kmem_cache_alloc(memobjs_cache, GFP_KERNEL); if (mem == NULL) return -ENOMEM; - mem->gpuaddr = (uint64_t) ibdesc->gpuaddr; - mem->size = (uint64_t) ibdesc->sizedwords << 2; + mem->gpuaddr = gpuaddr; + mem->size = size; mem->priv = 0; mem->id = 0; mem->offset = 0; mem->flags = 0; - /* sanitize the ibdesc ctrl flags */ - ibdesc->ctrl &= KGSL_IBDESC_MEMLIST | KGSL_IBDESC_PROFILING_BUFFER; - if (cmdbatch->flags & KGSL_CMDBATCH_MEMLIST && ibdesc->ctrl & KGSL_IBDESC_MEMLIST) { - if (ibdesc->ctrl & KGSL_IBDESC_PROFILING_BUFFER) { - add_profiling_buffer(device, cmdbatch, mem->gpuaddr, - mem->size, 0, 0); - return 0; - } - /* add to the memlist */ list_add_tail(&mem->node, &cmdbatch->memlist); - - if (ibdesc->ctrl & KGSL_IBDESC_PROFILING_BUFFER) - add_profiling_buffer(device, cmdbatch, mem->gpuaddr, - mem->size, 0, 0); } else { - /* Ignore if SYNC or MARKER is specified */ - if (cmdbatch->flags & - (KGSL_CMDBATCH_SYNC | KGSL_CMDBATCH_MARKER)) - return 0; - /* set the preamble flag if directed to */ if (cmdbatch->context->flags & KGSL_CONTEXT_PREAMBLE && list_empty(&cmdbatch->cmdlist)) diff --git a/drivers/gpu/msm/kgsl_pool.c b/drivers/gpu/msm/kgsl_pool.c index 497cfdaae2ec..48b7b0872425 100644 --- a/drivers/gpu/msm/kgsl_pool.c +++ b/drivers/gpu/msm/kgsl_pool.c @@ -21,18 +21,21 @@ #include "kgsl_device.h" #include "kgsl_pool.h" -/* - * Maximum pool size in terms of pages - * = (Number of pools * Max size per pool) +/** + * struct kgsl_page_pool - Structure to hold information for the pool + * @pool_order: Page order describing the size of the page + * @page_count: Number of pages currently present in the pool + * @reserved_pages: Number of pages reserved at init for the pool + * @allocation_allowed: Tells if reserved pool gets exhausted, can we allocate + * from system memory + * @list_lock: Spinlock for page list in the pool + * @page_list: List of pages held/reserved in this pool */ -#define KGSL_POOL_MAX_PAGES (2 * 4096) - -/* Set the max pool size to 8192 pages */ -static unsigned int kgsl_pool_max_pages = KGSL_POOL_MAX_PAGES; - struct kgsl_page_pool { unsigned int pool_order; int page_count; + unsigned int reserved_pages; + bool allocation_allowed; spinlock_t list_lock; struct list_head page_list; }; @@ -40,15 +43,34 @@ struct kgsl_page_pool { static struct kgsl_page_pool kgsl_pools[] = { { .pool_order = 0, + .reserved_pages = 2048, + .allocation_allowed = true, .list_lock = __SPIN_LOCK_UNLOCKED(kgsl_pools[0].list_lock), .page_list = LIST_HEAD_INIT(kgsl_pools[0].page_list), }, #ifndef CONFIG_ALLOC_BUFFERS_IN_4K_CHUNKS { - .pool_order = 4, + .pool_order = 1, + .reserved_pages = 1024, + .allocation_allowed = true, .list_lock = __SPIN_LOCK_UNLOCKED(kgsl_pools[1].list_lock), .page_list = LIST_HEAD_INIT(kgsl_pools[1].page_list), }, + { + .pool_order = 4, + .reserved_pages = 256, + .allocation_allowed = false, + .list_lock = __SPIN_LOCK_UNLOCKED(kgsl_pools[2].list_lock), + .page_list = LIST_HEAD_INIT(kgsl_pools[2].page_list), + }, + { + .pool_order = 8, + .reserved_pages = 32, + .allocation_allowed = false, + .list_lock = __SPIN_LOCK_UNLOCKED(kgsl_pools[3].list_lock), + .page_list = LIST_HEAD_INIT(kgsl_pools[3].page_list), + }, + #endif }; @@ -68,10 +90,28 @@ _kgsl_get_pool_from_order(unsigned int order) return NULL; } +/* Map the page into kernel and zero it out */ +static void +_kgsl_pool_zero_page(struct page *p, unsigned int pool_order) +{ + int i; + + for (i = 0; i < (1 << pool_order); i++) { + struct page *page = nth_page(p, i); + void *addr = kmap_atomic(page); + + memset(addr, 0, PAGE_SIZE); + dmac_flush_range(addr, addr + PAGE_SIZE); + kunmap_atomic(addr); + } +} + /* Add a page to specified pool */ static void _kgsl_pool_add_page(struct kgsl_page_pool *pool, struct page *p) { + _kgsl_pool_zero_page(p, pool->pool_order); + spin_lock(&pool->list_lock); list_add_tail(&p->lru, &pool->page_list); pool->page_count++; @@ -156,14 +196,14 @@ _kgsl_pool_shrink(struct kgsl_page_pool *pool, int num_pages) * (current_pool_size - target_pages) pages from pool * starting from higher order pool. */ -static int +static unsigned long kgsl_pool_reduce(unsigned int target_pages) { int total_pages = 0; int i; int nr_removed; struct kgsl_page_pool *pool; - unsigned int pcount = 0; + unsigned long pcount = 0; total_pages = kgsl_pool_size_total(); @@ -223,6 +263,17 @@ void kgsl_pool_free_sgt(struct sg_table *sgt) } } +static int kgsl_pool_idx_lookup(unsigned int order) +{ + int i; + + for (i = 0; i < KGSL_NUM_POOLS; i++) + if (order == kgsl_pools[i].pool_order) + return i; + + return -ENOMEM; +} + /** * kgsl_pool_alloc_page() - Allocate a page of requested size * @page_size: Size of the page to be allocated @@ -232,35 +283,55 @@ void kgsl_pool_free_sgt(struct sg_table *sgt) * * Return total page count on success and negative value on failure */ -int kgsl_pool_alloc_page(int page_size, struct page **pages, - unsigned int pages_len) +int kgsl_pool_alloc_page(int *page_size, struct page **pages, + unsigned int pages_len, unsigned int *align) { int j; int pcount = 0; struct kgsl_page_pool *pool; struct page *page = NULL; struct page *p = NULL; + int order = get_order(*page_size); + int pool_idx; - if ((pages == NULL) || pages_len < (page_size >> PAGE_SHIFT)) + if ((pages == NULL) || pages_len < (*page_size >> PAGE_SHIFT)) return -EINVAL; - pool = _kgsl_get_pool_from_order(get_order(page_size)); + pool = _kgsl_get_pool_from_order(order); + pool_idx = kgsl_pool_idx_lookup(order); if (pool != NULL) page = _kgsl_pool_get_page(pool); /* Allocate a new page if not allocated from pool */ if (page == NULL) { - gfp_t gfp_mask = kgsl_gfp_mask(get_order(page_size)); + gfp_t gfp_mask = kgsl_gfp_mask(order); + + /* Only allocate non-reserved memory for certain pools */ + if (!pool->allocation_allowed) { + *page_size = PAGE_SIZE << + kgsl_pools[pool_idx-1].pool_order; + *align = ilog2(*page_size); + return -EAGAIN; + } - page = alloc_pages(gfp_mask, - get_order(page_size)); + page = alloc_pages(gfp_mask, order); + + if (!page) { + if (pool_idx > 0) { + /* Retry with lower order pages */ + *page_size = PAGE_SIZE << + kgsl_pools[pool_idx-1].pool_order; + *align = ilog2(*page_size); + return -EAGAIN; + } else + return -ENOMEM; + } - if (!page) - return -ENOMEM; + _kgsl_pool_zero_page(page, order); } - for (j = 0; j < (page_size >> PAGE_SHIFT); j++) { + for (j = 0; j < (*page_size >> PAGE_SHIFT); j++) { p = nth_page(page, j); pages[pcount] = p; pcount++; @@ -279,18 +350,34 @@ void kgsl_pool_free_page(struct page *page) page_order = compound_order(page); - if (kgsl_pool_size_total() < kgsl_pool_max_pages) { - pool = _kgsl_get_pool_from_order(page_order); - if (pool != NULL) { - _kgsl_pool_add_page(pool, page); - return; - } + pool = _kgsl_get_pool_from_order(page_order); + if (pool != NULL) { + _kgsl_pool_add_page(pool, page); + return; } /* Give back to system as not added to pool */ __free_pages(page, page_order); } +static void kgsl_pool_reserve_pages(void) +{ + int i, j; + + for (i = 0; i < KGSL_NUM_POOLS; i++) { + struct page *page; + + for (j = 0; j < kgsl_pools[i].reserved_pages; j++) { + int order = kgsl_pools[i].pool_order; + gfp_t gfp_mask = kgsl_gfp_mask(order); + + page = alloc_pages(gfp_mask, order); + if (page != NULL) + _kgsl_pool_add_page(&kgsl_pools[i], page); + } + } +} + /* Functions for the shrinker */ static unsigned long @@ -326,6 +413,9 @@ static struct shrinker kgsl_pool_shrinker = { void kgsl_init_page_pools(void) { + /* Reserve the appropriate number of pages for each pool */ + kgsl_pool_reserve_pages(); + /* Initialize shrinker */ register_shrinker(&kgsl_pool_shrinker); } diff --git a/drivers/gpu/msm/kgsl_pool.h b/drivers/gpu/msm/kgsl_pool.h index f76c8aadbf30..f2cdda19140b 100644 --- a/drivers/gpu/msm/kgsl_pool.h +++ b/drivers/gpu/msm/kgsl_pool.h @@ -36,8 +36,8 @@ kgsl_gfp_mask(unsigned int page_order) void kgsl_pool_free_sgt(struct sg_table *sgt); void kgsl_init_page_pools(void); void kgsl_exit_page_pools(void); -int kgsl_pool_alloc_page(int page_size, struct page **pages, - unsigned int pages_len); +int kgsl_pool_alloc_page(int *page_size, struct page **pages, + unsigned int pages_len, unsigned int *align); void kgsl_pool_free_page(struct page *p); #endif /* __KGSL_POOL_H */ diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index 4cb9bc5d1651..50dcd39fac58 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -656,8 +656,14 @@ EXPORT_SYMBOL(kgsl_cache_range_op); #ifndef CONFIG_ALLOC_BUFFERS_IN_4K_CHUNKS static inline int get_page_size(size_t size, unsigned int align) { - return (align >= ilog2(SZ_64K) && size >= SZ_64K) - ? SZ_64K : PAGE_SIZE; + if (align >= ilog2(SZ_1M) && size >= SZ_1M) + return SZ_1M; + else if (align >= ilog2(SZ_64K) && size >= SZ_64K) + return SZ_64K; + else if (align >= ilog2(SZ_8K) && size >= SZ_8K) + return SZ_8K; + else + return PAGE_SIZE; } #else static inline int get_page_size(size_t size, unsigned int align) @@ -666,56 +672,6 @@ static inline int get_page_size(size_t size, unsigned int align) } #endif -static void kgsl_zero_pages(struct page **pages, unsigned int pcount) -{ - unsigned int j; - unsigned int step = ((VMALLOC_END - VMALLOC_START)/8) >> PAGE_SHIFT; - pgprot_t page_prot = pgprot_writecombine(PAGE_KERNEL); - void *ptr; - - /* - * All memory that goes to the user has to be zeroed out before it gets - * exposed to userspace. This means that the memory has to be mapped in - * the kernel, zeroed (memset) and then unmapped. This also means that - * the dcache has to be flushed to ensure coherency between the kernel - * and user pages. We used to pass __GFP_ZERO to alloc_page which mapped - * zeroed and unmaped each individual page, and then we had to turn - * around and call flush_dcache_page() on that page to clear the caches. - * This was killing us for performance. Instead, we found it is much - * faster to allocate the pages without GFP_ZERO, map a chunk of the - * range ('step' pages), memset it, flush it and then unmap - * - this results in a factor of 4 improvement for speed for large - * buffers. There is a small decrease in speed for small buffers, - * but only on the order of a few microseconds at best. The 'step' - * size is based on a guess at the amount of free vmalloc space, but - * will scale down if there's not enough free space. - */ - for (j = 0; j < pcount; j += step) { - step = min(step, pcount - j); - - ptr = vmap(&pages[j], step, VM_IOREMAP, page_prot); - - if (ptr != NULL) { - memset(ptr, 0, step * PAGE_SIZE); - dmac_flush_range(ptr, ptr + step * PAGE_SIZE); - vunmap(ptr); - } else { - int k; - /* Very, very, very slow path */ - - for (k = j; k < j + step; k++) { - ptr = kmap_atomic(pages[k]); - memset(ptr, 0, PAGE_SIZE); - dmac_flush_range(ptr, ptr + PAGE_SIZE); - kunmap_atomic(ptr); - } - /* scale down the step size to avoid this path */ - if (step > 1) - step >>= 1; - } - } -} - static int kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc, struct kgsl_pagetable *pagetable, @@ -741,8 +697,10 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc, * larger however to accomodate hardware quirks */ - if (align < ilog2(page_size)) + if (align < ilog2(page_size)) { kgsl_memdesc_set_align(memdesc, ilog2(page_size)); + align = ilog2(page_size); + } /* * There needs to be enough room in the page array to be able to @@ -775,18 +733,13 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc, while (len > 0) { int page_count; - /* don't waste space at the end of the allocation*/ - if (len < page_size) - page_size = PAGE_SIZE; - - page_count = kgsl_pool_alloc_page(page_size, - pages + pcount, len_alloc - pcount); + page_count = kgsl_pool_alloc_page(&page_size, + pages + pcount, len_alloc - pcount, + &align); if (page_count <= 0) { - if (page_size != PAGE_SIZE) { - page_size = PAGE_SIZE; + if (page_count == -EAGAIN) continue; - } /* * Update sglen and memdesc size,as requested allocation @@ -807,6 +760,9 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc, pcount += page_count; len -= page_size; memdesc->size += page_size; + + /* Get the needed page size for the next iteration */ + page_size = get_page_size(len, align); } ret = sg_alloc_table_from_pages(memdesc->sgt, pages, pcount, 0, @@ -844,11 +800,6 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc, KGSL_STATS_ADD(memdesc->size, &kgsl_driver.stats.page_alloc, &kgsl_driver.stats.page_alloc_max); - /* - * Zero out the pages. - */ - kgsl_zero_pages(pages, pcount); - done: if (ret) { if (pages) { diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 608f0c12e3ab..0fc18922801e 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -602,6 +602,16 @@ config LEDS_QPNP_FLASH To compile this driver as a module, choose M here: the module will be called leds-qpnp-flash. +config LEDS_QPNP_FLASH_V2 + tristate "Support for QPNP V2 Flash LEDs" + depends on LEDS_CLASS && MFD_SPMI_PMIC + help + This driver supports the leds functionality of Qualcomm Technologies + PNP PMIC. It includes Flash Led. + + To compile this driver as a module, choose M here: the module will + be called leds-qpnp-flash-v2. + config LEDS_QPNP_WLED tristate "Support for QPNP WLED" depends on LEDS_CLASS && SPMI diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 04a0f62c4035..8d8ba9175810 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o obj-$(CONFIG_LEDS_QPNP) += leds-qpnp.o obj-$(CONFIG_LEDS_QPNP_FLASH) += leds-qpnp-flash.o +obj-$(CONFIG_LEDS_QPNP_FLASH_V2) += leds-qpnp-flash-v2.o obj-$(CONFIG_LEDS_QPNP_WLED) += leds-qpnp-wled.o obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c new file mode 100644 index 000000000000..89636557dec1 --- /dev/null +++ b/drivers/leds/leds-qpnp-flash-v2.c @@ -0,0 +1,639 @@ +/* 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/init.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/leds-qpnp-flash-v2.h> + +#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) +#define FLASH_LED_REG_IRES(base) (base + 0x47) +#define FLASH_LED_REG_STROBE_CTRL(base) (base + 0x49) +#define FLASH_LED_REG_CHANNEL_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_ISC_DELAY(base) (base + 0x52) + +#define FLASH_LED_HDRM_MODE_PRGM_MASK 0xFF +#define FLASH_LED_HDRM_VOL_MASK 0xF0 +#define FLASH_LED_CURRENT_MASK 0x3F +#define FLASH_LED_STROBE_CTRL_MASK 0x07 +#define FLASH_LED_SAFETY_TMR_MASK_MASK 0x7F +#define FLASH_LED_MOD_CTRL_MASK 0x80 +#define FLASH_LED_ISC_DELAY_MASK 0x03 + +#define FLASH_LED_TYPE_FLASH 0 +#define FLASH_LED_TYPE_TORCH 1 +#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_SAFETY_TMR_VAL_OFFSET 1 +#define FLASH_LED_SAFETY_TMR_VAL_DIVISOR 10 +#define FLASH_LED_SAFETY_TMR_ENABLED 0x08 +#define FLASH_LED_IRES_BASE 3 +#define FLASH_LED_IRES_DIVISOR 2500 +#define FLASH_LED_IRES_MIN_UA 5000 +#define FLASH_LED_IRES_DEFAULT_UA 12500 +#define FLASH_LED_IRES_DEFAULT_VAL 0x00 +#define FLASH_LED_HDRM_VOL_SHIFT 4 +#define FLASH_LED_HDRM_VOL_DEFAULT_MV 0x80 +#define FLASH_LED_HDRM_VOL_HI_LO_WIN_DEFAULT_MV 0x04 +#define FLASH_LED_HDRM_VOL_BASE_MV 125 +#define FLASH_LED_HDRM_VOL_STEP_MV 25 +#define FLASH_LED_STROBE_ENABLE 0x01 +#define FLASH_LED_MOD_ENABLE 0x80 +#define FLASH_LED_DISABLE 0x00 +#define FLASH_LED_SAFETY_TMR_DISABLED 0x13 + +/* + * Flash LED configuration read from device tree + */ +struct flash_led_platform_data { + u8 isc_delay_us; + bool hdrm_auto_mode_en; +}; + +/* + * Flash LED data structure containing flash LED attributes + */ +struct qpnp_flash_led { + struct flash_led_platform_data *pdata; + struct platform_device *pdev; + struct regmap *regmap; + struct flash_node_data *fnode; + struct flash_switch_data *snode; + spinlock_t lock; + int num_led_nodes; + int num_avail_leds; + u16 base; +}; + +static int +qpnp_flash_led_masked_write(struct qpnp_flash_led *led, u16 addr, u8 mask, + u8 val) +{ + int rc; + + rc = regmap_update_bits(led->regmap, addr, mask, val); + if (rc) + dev_err(&led->pdev->dev, + "Unable to update bits from 0x%02X, rc = %d\n", + addr, rc); + + dev_dbg(&led->pdev->dev, "Write 0x%02X to addr 0x%02X\n", val, addr); + + return rc; +} + +static enum +led_brightness qpnp_flash_led_brightness_get(struct led_classdev *led_cdev) +{ + return led_cdev->brightness; +} + +static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) +{ + int rc, i, addr_offset; + u8 val = 0; + + for (i = 0; i < led->num_avail_leds; i++) { + addr_offset = led->fnode[i].id; + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_HDRM_PRGM(led->base + addr_offset), + FLASH_LED_HDRM_MODE_PRGM_MASK, + led->fnode[i].hdrm_val); + if (rc) + return rc; + + val |= 0x1 << led->fnode[i].id; + } + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_HDRM_AUTO_MODE_CTRL(led->base), + FLASH_LED_HDRM_MODE_PRGM_MASK, val); + if (rc) + return rc; + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_ISC_DELAY(led->base), + FLASH_LED_ISC_DELAY_MASK, led->pdata->isc_delay_us); + if (rc) + return rc; + + return 0; +} + +static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value) +{ + int prgm_current_ma; + + prgm_current_ma = value < 0 ? 0 : value; + prgm_current_ma = value > fnode->cdev.max_brightness ? + fnode->cdev.max_brightness : value; + fnode->cdev.brightness = prgm_current_ma; + fnode->brightness = prgm_current_ma * 1000 / fnode->ires_ua + 1; + fnode->led_on = prgm_current_ma != 0; +} + +static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) +{ + struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev); + int rc, i, addr_offset; + u8 val; + + if (!on) + goto leds_turn_off; + + val = 0; + for (i = 0; i < led->num_led_nodes; i++) + val |= led->fnode[i].ires << (led->fnode[i].id * 2); + rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_IRES(led->base), + FLASH_LED_CURRENT_MASK, val); + if (rc) + return rc; + + val = 0; + for (i = 0; i < led->num_avail_leds; i++) { + if (!led->fnode[i].led_on) + continue; + + addr_offset = led->fnode[i].id; + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_STROBE_CTRL(led->base + addr_offset), + FLASH_LED_STROBE_CTRL_MASK, FLASH_LED_STROBE_ENABLE); + if (rc) + return rc; + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_TGR_CURRENT(led->base + addr_offset), + FLASH_LED_CURRENT_MASK, led->fnode[i].brightness); + if (rc) + return rc; + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_SAFETY_TMR(led->base + addr_offset), + FLASH_LED_SAFETY_TMR_MASK_MASK, led->fnode[i].duration); + if (rc) + return rc; + + val |= FLASH_LED_STROBE_ENABLE << led->fnode[i].id; + + if (led->fnode[i].pinctrl) { + rc = pinctrl_select_state(led->fnode[i].pinctrl, + led->fnode[i].gpio_state_active); + if (rc) { + dev_err(&led->pdev->dev, + "failed to enable GPIO\n"); + return rc; + } + } + } + + rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_MOD_CTRL(led->base), + FLASH_LED_MOD_CTRL_MASK, FLASH_LED_MOD_ENABLE); + if (rc) + return rc; + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_CHANNEL_CTRL(led->base), + FLASH_LED_STROBE_CTRL_MASK, val); + if (rc) + return rc; + + return 0; + +leds_turn_off: + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_CHANNEL_CTRL(led->base), + FLASH_LED_STROBE_CTRL_MASK, FLASH_LED_DISABLE); + if (rc) + return rc; + + rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_MOD_CTRL(led->base), + FLASH_LED_MOD_CTRL_MASK, FLASH_LED_DISABLE); + if (rc) + return rc; + + for (i = 0; i < led->num_led_nodes; i++) { + if (!led->fnode[i].led_on) + continue; + + addr_offset = led->fnode[i].id; + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_TGR_CURRENT(led->base + addr_offset), + FLASH_LED_CURRENT_MASK, 0); + if (rc) + return rc; + led->fnode[i].led_on = false; + + if (led->fnode[i].pinctrl) { + rc = pinctrl_select_state(led->fnode[i].pinctrl, + led->fnode[i].gpio_state_suspend); + if (rc) { + dev_err(&led->pdev->dev, + "failed to disable GPIO\n"); + return rc; + } + } + } + + return 0; +} + +static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct flash_node_data *fnode = NULL; + struct flash_switch_data *snode = NULL; + struct qpnp_flash_led *led = dev_get_drvdata(&fnode->pdev->dev); + int rc; + + if (!strcmp(led_cdev->name, "led:switch")) { + snode = container_of(led_cdev, struct flash_switch_data, cdev); + led = dev_get_drvdata(&snode->pdev->dev); + } else { + fnode = container_of(led_cdev, struct flash_node_data, cdev); + led = dev_get_drvdata(&fnode->pdev->dev); + } + + spin_lock(&led->lock); + if (!fnode) { + rc = qpnp_flash_led_switch_set(snode, value > 0); + if (rc) { + dev_err(&led->pdev->dev, + "Failed to set flash LED switch\n"); + goto exit; + } + } else { + qpnp_flash_led_node_set(fnode, value); + } + +exit: + spin_unlock(&led->lock); +} + +static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, + struct flash_node_data *fnode, struct device_node *node) +{ + const char *temp_string; + int rc; + u32 val; + + fnode->pdev = led->pdev; + fnode->cdev.brightness_set = qpnp_flash_led_brightness_set; + fnode->cdev.brightness_get = qpnp_flash_led_brightness_get; + + rc = of_property_read_string(node, "qcom,led-name", &fnode->cdev.name); + if (rc) { + dev_err(&led->pdev->dev, "Unable to read flash LED names\n"); + return rc; + } + + rc = of_property_read_u32(node, "qcom,max-current", &val); + if (!rc) { + fnode->cdev.max_brightness = val; + } else { + dev_err(&led->pdev->dev, "Unable to read max current\n"); + return rc; + } + + rc = of_property_read_string(node, "label", &temp_string); + if (!rc) { + if (!strcmp(temp_string, "flash")) + fnode->type = FLASH_LED_TYPE_FLASH; + else if (!strcmp(temp_string, "torch")) + fnode->type = FLASH_LED_TYPE_TORCH; + else { + dev_err(&led->pdev->dev, "Wrong flash LED type\n"); + return rc; + } + } else { + dev_err(&led->pdev->dev, "Unable to read flash LED label\n"); + return rc; + } + + rc = of_property_read_u32(node, "qcom,id", &val); + if (!rc) { + fnode->id = (u8)val; + } else { + dev_err(&led->pdev->dev, "Unable to read flash LED ID\n"); + return rc; + } + + rc = of_property_read_string(node, "qcom,default-led-trigger", + &fnode->cdev.default_trigger); + if (rc) { + dev_err(&led->pdev->dev, "Unable to read trigger name\n"); + return rc; + } + + fnode->ires_ua = FLASH_LED_IRES_DEFAULT_UA; + fnode->ires = FLASH_LED_IRES_DEFAULT_VAL; + rc = of_property_read_u32(node, "qcom,ires-ua", &val); + if (!rc) { + fnode->ires_ua = val; + fnode->ires = FLASH_LED_IRES_BASE - + (val - FLASH_LED_IRES_MIN_UA) / FLASH_LED_IRES_DIVISOR; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, "Unable to read current resolution\n"); + return rc; + } + + fnode->duration = FLASH_LED_SAFETY_TMR_DISABLED; + rc = of_property_read_u32(node, "qcom,duration-ms", &val); + if (!rc) { + fnode->duration = (u8)(((val - + FLASH_LED_SAFETY_TMR_VAL_OFFSET) / + FLASH_LED_SAFETY_TMR_VAL_DIVISOR) | + FLASH_LED_SAFETY_TMR_ENABLED); + } else if (rc == -EINVAL) { + if (fnode->type == FLASH_LED_TYPE_FLASH) { + dev_err(&led->pdev->dev, + "Timer duration is required for flash LED\n"); + return rc; + } + } else { + dev_err(&led->pdev->dev, + "Unable to read timer duration\n"); + return rc; + } + + fnode->hdrm_val = FLASH_LED_HDRM_VOL_DEFAULT_MV; + rc = of_property_read_u32(node, "qcom,hdrm-voltage-mv", &val); + if (!rc) { + val = (val - FLASH_LED_HDRM_VOL_BASE_MV) / + FLASH_LED_HDRM_VOL_STEP_MV; + fnode->hdrm_val = (val << FLASH_LED_HDRM_VOL_SHIFT) & + FLASH_LED_HDRM_VOL_MASK; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, "Unable to read headroom voltage\n"); + return rc; + } + + rc = of_property_read_u32(node, "qcom,hdrm-vol-hi-lo-win-mv", &val); + if (!rc) { + fnode->hdrm_val |= (val / FLASH_LED_HDRM_VOL_STEP_MV) & + ~FLASH_LED_HDRM_VOL_MASK; + } else if (rc == -EINVAL) { + fnode->hdrm_val |= FLASH_LED_HDRM_VOL_HI_LO_WIN_DEFAULT_MV; + } else { + dev_err(&led->pdev->dev, + "Unable to read hdrm hi-lo window voltage\n"); + return rc; + } + + rc = led_classdev_register(&led->pdev->dev, &fnode->cdev); + if (rc) { + dev_err(&led->pdev->dev, "Unable to register led node %d\n", + fnode->id); + return rc; + } + fnode->cdev.dev->of_node = node; + + fnode->pinctrl = devm_pinctrl_get(fnode->cdev.dev); + if (IS_ERR_OR_NULL(fnode->pinctrl)) { + dev_err(&led->pdev->dev, "No pinctrl defined\n"); + fnode->pinctrl = NULL; + } else { + fnode->gpio_state_active = + pinctrl_lookup_state(fnode->pinctrl, "led_enable"); + if (IS_ERR_OR_NULL(fnode->gpio_state_active)) { + dev_err(&led->pdev->dev, + "Cannot lookup LED active state\n"); + devm_pinctrl_put(fnode->pinctrl); + fnode->pinctrl = NULL; + return PTR_ERR(fnode->gpio_state_active); + } + + fnode->gpio_state_suspend = + pinctrl_lookup_state(fnode->pinctrl, "led_disable"); + if (IS_ERR_OR_NULL(fnode->gpio_state_suspend)) { + dev_err(&led->pdev->dev, + "Cannot lookup LED disable state\n"); + devm_pinctrl_put(fnode->pinctrl); + fnode->pinctrl = NULL; + return PTR_ERR(fnode->gpio_state_suspend); + } + } + + return 0; +} + +static int qpnp_flash_led_parse_and_register_switch(struct qpnp_flash_led *led, + struct device_node *node) +{ + int rc; + + rc = of_property_read_string(node, "qcom,led-name", + &led->snode->cdev.name); + if (rc) { + dev_err(&led->pdev->dev, "Failed to read switch node name\n"); + return rc; + } + + rc = of_property_read_string(node, "qcom,default-led-trigger", + &led->snode->cdev.default_trigger); + if (rc) { + dev_err(&led->pdev->dev, "Unable to read trigger name\n"); + return rc; + } + + led->snode->pdev = led->pdev; + led->snode->cdev.brightness_set = qpnp_flash_led_brightness_set; + led->snode->cdev.brightness_get = qpnp_flash_led_brightness_get; + rc = led_classdev_register(&led->pdev->dev, &led->snode->cdev); + if (rc) { + dev_err(&led->pdev->dev, + "Unable to register led switch node\n"); + return rc; + } + + return 0; +} + +static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, + struct device_node *node) +{ + int rc; + u32 val; + + 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); + if (!rc) { + led->pdata->isc_delay_us = val >> FLASH_LED_ISC_DELAY_SHIFT; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, "Unable to read ISC delay\n"); + return rc; + } + + return 0; +} + +static int qpnp_flash_led_probe(struct platform_device *pdev) +{ + struct qpnp_flash_led *led; + struct device_node *node, *temp; + unsigned int base; + int rc, i = 0; + + node = pdev->dev.of_node; + if (!node) { + dev_info(&pdev->dev, "No flash LED nodes defined\n"); + return -ENODEV; + } + + rc = of_property_read_u32(node, "reg", &base); + if (rc) { + dev_err(&pdev->dev, "Couldn't find reg in node %s, rc = %d\n", + node->full_name, rc); + return rc; + } + + led = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_flash_led), + GFP_KERNEL); + if (!led) + return -ENOMEM; + + led->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!led->regmap) { + dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); + return -EINVAL; + } + + led->base = base; + led->pdev = pdev; + led->pdata = devm_kzalloc(&pdev->dev, + sizeof(struct flash_led_platform_data), GFP_KERNEL); + if (!led->pdata) + return -ENOMEM; + + rc = qpnp_flash_led_parse_common_dt(led, node); + if (rc) { + dev_err(&pdev->dev, + "Failed to parse common flash LED device tree\n"); + return rc; + } + + for_each_child_of_node(node, temp) + led->num_led_nodes++; + if (!led->num_led_nodes) { + dev_err(&pdev->dev, "No LED nodes defined\n"); + return -ECHILD; + } + + led->fnode = devm_kzalloc(&pdev->dev, + sizeof(struct flash_node_data) * (--led->num_led_nodes), + GFP_KERNEL); + if (!led->fnode) + return -ENOMEM; + + temp = NULL; + for (i = 0; i < led->num_led_nodes; i++) { + temp = of_get_next_child(node, temp); + rc = qpnp_flash_led_parse_each_led_dt(led, + &led->fnode[i], temp); + if (rc) { + dev_err(&pdev->dev, + "Unable to parse flash node %d\n", i); + goto error_led_register; + } + } + led->num_avail_leds = i; + + led->snode = devm_kzalloc(&pdev->dev, + sizeof(struct flash_switch_data), GFP_KERNEL); + if (!led->snode) { + rc = -ENOMEM; + goto error_led_register; + } + + temp = of_get_next_child(node, temp); + rc = qpnp_flash_led_parse_and_register_switch(led, temp); + if (rc) { + dev_err(&pdev->dev, + "Unable to parse and register switch node\n"); + goto error_led_register; + } + + rc = qpnp_flash_led_init_settings(led); + if (rc) { + dev_err(&pdev->dev, "Failed to initialize flash LED\n"); + goto error_switch_register; + } + + spin_lock_init(&led->lock); + + dev_set_drvdata(&pdev->dev, led); + + return 0; + +error_switch_register: + led_classdev_unregister(&led->snode->cdev); +error_led_register: + while (i > 0) + led_classdev_unregister(&led->fnode[--i].cdev); + + return rc; +} + +static int qpnp_flash_led_remove(struct platform_device *pdev) +{ + struct qpnp_flash_led *led = dev_get_drvdata(&pdev->dev); + int i = led->num_led_nodes; + + led_classdev_unregister(&led->snode->cdev); + while (i > 0) + led_classdev_unregister(&led->fnode[--i].cdev); + + return 0; +} + +const struct of_device_id qpnp_flash_led_match_table[] = { + { .compatible = "qcom,qpnp-flash-led-v2",}, + { }, +}; + +static struct platform_driver qpnp_flash_led_driver = { + .driver = { + .name = "qcom,qpnp-flash-led-v2", + .of_match_table = qpnp_flash_led_match_table, + }, + .probe = qpnp_flash_led_probe, + .remove = qpnp_flash_led_remove, +}; + +static int __init qpnp_flash_led_init(void) +{ + return platform_driver_register(&qpnp_flash_led_driver); +} +late_initcall(qpnp_flash_led_init); + +static void __exit qpnp_flash_led_exit(void) +{ + platform_driver_unregister(&qpnp_flash_led_driver); +} +module_exit(qpnp_flash_led_exit); + +MODULE_DESCRIPTION("QPNP Flash LED driver v2"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("leds:leds-qpnp-flash-v2"); diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c index ed0b9742bddb..7e3666042fde 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c @@ -42,7 +42,7 @@ static const struct v4l2_subdev_internal_ops msm_sensor_init_internal_ops; static int msm_sensor_wait_for_probe_done(struct msm_sensor_init_t *s_init) { int rc; - int tm = 10000; + int tm = 20000; if (s_init->module_init_status == 1) { CDBG("msm_cam_get_module_init_status -2\n"); return 0; diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c index c8814815ab66..44f7af089ee9 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c @@ -192,7 +192,7 @@ static int sde_mdp_get_ubwc_plane_size(struct sde_mdp_format_params *fmt, 4096); /* CbCr bitstream stride and plane size */ - ps->ystride[1] = ALIGN(width, 64); + ps->ystride[1] = ALIGN(width, 128); ps->plane_size[1] = ALIGN(ps->ystride[1] * ALIGN(height / 2, 32), 4096); diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 327e83ac2469..fc9e4395c21d 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -359,6 +359,10 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __ if (copy_in_user(up, up32, 2 * sizeof(__u32)) || copy_in_user(&up->data_offset, &up32->data_offset, + sizeof(__u32)) || + copy_in_user(up->reserved, up32->reserved, + sizeof(up->reserved)) || + copy_in_user(&up->length, &up32->length, sizeof(__u32))) return -EFAULT; @@ -384,6 +388,8 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __ enum v4l2_memory memory) { if (copy_in_user(up32, up, 2 * sizeof(__u32)) || + copy_in_user(up32->reserved, up->reserved, + sizeof(up32->reserved)) || copy_in_user(&up32->data_offset, &up->data_offset, sizeof(__u32))) return -EFAULT; @@ -399,6 +405,10 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __ if (copy_in_user(&up32->m.fd, &up->m.fd, sizeof(int))) return -EFAULT; + if (memory == V4L2_MEMORY_USERPTR) + if (copy_in_user(&up32->m.userptr, &up->m.userptr, + sizeof(compat_long_t))) + return -EFAULT; return 0; } @@ -760,8 +770,13 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext struct v4l2_event32 { __u32 type; union { - compat_s64 value64; - __u8 data[64]; + struct v4l2_event_vsync vsync; + struct v4l2_event_ctrl ctrl; + struct v4l2_event_frame_sync frame_sync; + struct v4l2_event_src_change src_change; + struct v4l2_event_motion_det motion_det; + compat_s64 value64; + __u8 data[64]; } u; __u32 pending; __u32 sequence; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c index 2255a74331cb..e3dfccd01549 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c @@ -820,16 +820,23 @@ static void ipa_rx_switch_to_intr_mode(struct ipa_sys_context *sys) return; } - if (!atomic_read(&sys->curr_polling_state)) { - IPAERR("already in intr mode\n"); - goto fail; - } - ret = sps_get_config(sys->ep->ep_hdl, &sys->ep->connect); if (ret) { IPAERR("sps_get_config() failed %d\n", ret); goto fail; } + + if (!atomic_read(&sys->curr_polling_state) && + ((sys->ep->connect.options & SPS_O_EOT) == SPS_O_EOT)) { + IPADBG("already in intr mode\n"); + return; + } + + if (!atomic_read(&sys->curr_polling_state)) { + IPAERR("already in intr mode\n"); + goto fail; + } + sys->event.options = SPS_O_EOT; ret = sps_register_event(sys->ep->ep_hdl, &sys->event); if (ret) { diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index de703bf6b582..eeecc508e8db 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -2040,6 +2040,10 @@ static int ipa_wwan_probe(struct platform_device *pdev) goto set_perf_err; /* IPA_RM configuration ends */ + /* Enable SG support in netdevice. */ + if (ipa_rmnet_res.ipa_advertise_sg_support) + dev->hw_features |= NETIF_F_SG; + ret = register_netdev(dev); if (ret) { IPAWANERR("unable to register ipa_netdev %d rc=%d\n", diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index f5e95b679e4e..f2d60cb14212 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -882,12 +882,11 @@ static void ipa3_rx_switch_to_intr_mode(struct ipa3_sys_context *sys) { int ret; - if (!atomic_read(&sys->curr_polling_state)) { - IPAERR("already in intr mode\n"); - goto fail; - } - if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) { + if (!atomic_read(&sys->curr_polling_state)) { + IPAERR("already in intr mode\n"); + goto fail; + } atomic_set(&sys->curr_polling_state, 0); ipa3_dec_release_wakelock(); ret = gsi_config_channel_mode(sys->ep->gsi_chan_hdl, @@ -902,6 +901,15 @@ static void ipa3_rx_switch_to_intr_mode(struct ipa3_sys_context *sys) IPAERR("sps_get_config() failed %d\n", ret); goto fail; } + if (!atomic_read(&sys->curr_polling_state) && + ((sys->ep->connect.options & SPS_O_EOT) == SPS_O_EOT)) { + IPADBG("already in intr mode\n"); + return; + } + if (!atomic_read(&sys->curr_polling_state)) { + IPAERR("already in intr mode\n"); + goto fail; + } sys->event.options = SPS_O_EOT; ret = sps_register_event(sys->ep->ep_hdl, &sys->event); if (ret) { diff --git a/drivers/power/qcom-charger/Kconfig b/drivers/power/qcom-charger/Kconfig index a109597d194f..e36ecc66a04e 100644 --- a/drivers/power/qcom-charger/Kconfig +++ b/drivers/power/qcom-charger/Kconfig @@ -61,6 +61,15 @@ config MSM_BCL_PERIPHERAL_CTL provides routines to configure and monitor the BCL PMIC peripheral. +config BATTERY_BCL + tristate "Battery Current Limit driver" + depends on THERMAL_MONITOR + help + Say Y here to enable support for battery current limit + device. The BCL driver will poll BMS if + thermal daemon enables BCL. + It will notify thermal daemon if IBat crosses Imax threshold. + config QPNP_SMB2 tristate "SMB2 Battery Charger" depends on MFD_SPMI_PMIC diff --git a/drivers/power/qcom-charger/Makefile b/drivers/power/qcom-charger/Makefile index 350e291bed7d..21af050d96f1 100644 --- a/drivers/power/qcom-charger/Makefile +++ b/drivers/power/qcom-charger/Makefile @@ -4,4 +4,5 @@ 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 obj-$(CONFIG_MSM_BCL_PERIPHERAL_CTL) += bcl_peripheral.o +obj-$(CONFIG_BATTERY_BCL) += battery_current_limit.o obj-$(CONFIG_QPNP_SMB2) += qpnp-smb2.o smb-lib.o pmic-voter.o diff --git a/drivers/power/qcom-charger/battery_current_limit.c b/drivers/power/qcom-charger/battery_current_limit.c new file mode 100644 index 000000000000..2bda5ce1a8c4 --- /dev/null +++ b/drivers/power/qcom-charger/battery_current_limit.c @@ -0,0 +1,1827 @@ +/* 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/moduleparam.h> +#include <linux/platform_device.h> +#include <linux/errno.h> +#include <linux/device.h> +#include <linux/power_supply.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/cpufreq.h> +#include <linux/qpnp/qpnp-adc.h> +#include <linux/cpu.h> +#include <linux/msm_bcl.h> +#include <linux/power_supply.h> +#include <linux/cpumask.h> +#include <linux/msm_thermal.h> + +#define CREATE_TRACE_POINTS +#define _BCL_SW_TRACE +#include <trace/trace_thermal.h> + +#define BCL_DEV_NAME "battery_current_limit" +#define BCL_NAME_LENGTH 20 +/* + * Default BCL poll interval 1000 msec + */ +#define BCL_POLL_INTERVAL 1000 +/* + * Mininum BCL poll interval 10 msec + */ +#define MIN_BCL_POLL_INTERVAL 10 +#define BATTERY_VOLTAGE_MIN 3400 +#define BTM_8084_FREQ_MITIG_LIMIT 1958400 +#define MAX_CPU_NAME 10 + +#define BCL_FETCH_DT_U32(_dev, _key, _search_str, _ret, _out) do { \ + _key = _search_str; \ + _ret = of_property_read_u32(_dev, _key, &_out); \ + } while (0) + +/* + * Battery Current Limit Enable or Not + */ +enum bcl_device_mode { + BCL_DEVICE_DISABLED = 0, + BCL_DEVICE_ENABLED, +}; + +/* + * Battery Current Limit Iavail Threshold Mode set + */ +enum bcl_iavail_threshold_mode { + BCL_IAVAIL_THRESHOLD_DISABLED = 0, + BCL_IAVAIL_THRESHOLD_ENABLED, +}; + +/* + * Battery Current Limit Iavail Threshold Mode + */ +enum bcl_iavail_threshold_type { + BCL_LOW_THRESHOLD_TYPE = 0, + BCL_HIGH_THRESHOLD_TYPE, + BCL_THRESHOLD_TYPE_MAX, +}; + +enum bcl_monitor_type { + BCL_IAVAIL_MONITOR_TYPE, + BCL_IBAT_MONITOR_TYPE, + BCL_IBAT_PERIPH_MONITOR_TYPE, + BCL_MONITOR_TYPE_MAX, +}; + +enum bcl_adc_monitor_mode { + BCL_MONITOR_DISABLED, + BCL_VPH_MONITOR_MODE, + BCL_IBAT_MONITOR_MODE, + BCL_IBAT_HIGH_LOAD_MODE, + BCL_MONITOR_MODE_MAX, +}; + +static const char *bcl_type[BCL_MONITOR_TYPE_MAX] = {"bcl", "btm", + "bcl_peripheral"}; +int adc_timer_val_usec[] = { + [ADC_MEAS1_INTERVAL_0MS] = 0, + [ADC_MEAS1_INTERVAL_1P0MS] = 1000, + [ADC_MEAS1_INTERVAL_2P0MS] = 2000, + [ADC_MEAS1_INTERVAL_3P9MS] = 3900, + [ADC_MEAS1_INTERVAL_7P8MS] = 7800, + [ADC_MEAS1_INTERVAL_15P6MS] = 15600, + [ADC_MEAS1_INTERVAL_31P3MS] = 31300, + [ADC_MEAS1_INTERVAL_62P5MS] = 62500, + [ADC_MEAS1_INTERVAL_125MS] = 125000, + [ADC_MEAS1_INTERVAL_250MS] = 250000, + [ADC_MEAS1_INTERVAL_500MS] = 500000, + [ADC_MEAS1_INTERVAL_1S] = 1000000, + [ADC_MEAS1_INTERVAL_2S] = 2000000, + [ADC_MEAS1_INTERVAL_4S] = 4000000, + [ADC_MEAS1_INTERVAL_8S] = 8000000, + [ADC_MEAS1_INTERVAL_16S] = 16000000, +}; + +/** + * BCL control block + * + */ +struct bcl_context { + /* BCL device */ + struct device *dev; + + /* BCL related config parameter */ + /* BCL mode enable or not */ + enum bcl_device_mode bcl_mode; + /* BCL monitoring Iavail or Ibat */ + enum bcl_monitor_type bcl_monitor_type; + /* BCL Iavail Threshold Activate or Not */ + enum bcl_iavail_threshold_mode + bcl_threshold_mode[BCL_THRESHOLD_TYPE_MAX]; + /* BCL Iavail Threshold value in milli Amp */ + int bcl_threshold_value_ma[BCL_THRESHOLD_TYPE_MAX]; + /* BCL Type */ + char bcl_type[BCL_NAME_LENGTH]; + /* BCL poll in msec */ + int bcl_poll_interval_msec; + + /* BCL realtime value based on poll */ + /* BCL realtime vbat in mV*/ + int bcl_vbat_mv; + /* BCL realtime rbat in mOhms*/ + int bcl_rbat_mohm; + /*BCL realtime iavail in milli Amp*/ + int bcl_iavail; + /*BCL vbatt min in mV*/ + int bcl_vbat_min; + /* BCL period poll delay work structure */ + struct delayed_work bcl_iavail_work; + /* For non-bms target */ + bool bcl_no_bms; + /* The max CPU frequency the BTM restricts during high load */ + uint32_t btm_freq_max; + /* Indicates whether there is a high load */ + enum bcl_adc_monitor_mode btm_mode; + /* battery current high load clr threshold */ + int btm_low_threshold_uv; + /* battery current high load threshold */ + int btm_high_threshold_uv; + /* ADC battery current polling timer interval */ + enum qpnp_adc_meas_timer_1 btm_adc_interval; + /* Ibat ADC config parameters */ + struct qpnp_adc_tm_chip *btm_adc_tm_dev; + struct qpnp_vadc_chip *btm_vadc_dev; + int btm_ibat_chan; + struct qpnp_adc_tm_btm_param btm_ibat_adc_param; + uint32_t btm_uv_to_ua_numerator; + uint32_t btm_uv_to_ua_denominator; + /* Vph ADC config parameters */ + int btm_vph_chan; + uint32_t btm_vph_high_thresh; + uint32_t btm_vph_low_thresh; + struct qpnp_adc_tm_btm_param btm_vph_adc_param; + /* Low temp min freq limit requested by thermal */ + uint32_t thermal_freq_limit; + + /* BCL Peripheral monitor parameters */ + struct bcl_threshold ibat_high_thresh; + struct bcl_threshold ibat_low_thresh; + struct bcl_threshold vbat_high_thresh; + struct bcl_threshold vbat_low_thresh; + uint32_t bcl_p_freq_max; + struct workqueue_struct *bcl_hotplug_wq; + struct device_clnt_data *hotplug_handle; + struct device_clnt_data *cpufreq_handle[NR_CPUS]; +}; + +enum bcl_threshold_state { + BCL_LOW_THRESHOLD = 0, + BCL_HIGH_THRESHOLD, + BCL_THRESHOLD_DISABLED, +}; + +static struct bcl_context *gbcl; +static enum bcl_threshold_state bcl_vph_state = BCL_THRESHOLD_DISABLED, + bcl_ibat_state = BCL_THRESHOLD_DISABLED, + bcl_soc_state = BCL_THRESHOLD_DISABLED; +static DEFINE_MUTEX(bcl_notify_mutex); +static uint32_t bcl_hotplug_request, bcl_hotplug_mask, bcl_soc_hotplug_mask; +static uint32_t bcl_frequency_mask; +static struct work_struct bcl_hotplug_work; +static DEFINE_MUTEX(bcl_hotplug_mutex); +static bool bcl_hotplug_enabled; +static uint32_t battery_soc_val = 100; +static uint32_t soc_low_threshold; +static struct power_supply *bcl_psy; +static struct power_supply_desc bcl_psy_des; +static const char bcl_psy_name[] = "bcl"; + +static void bcl_handle_hotplug(struct work_struct *work) +{ + int ret = 0, cpu = 0; + union device_request curr_req; + + trace_bcl_sw_mitigation_event("start hotplug mitigation"); + mutex_lock(&bcl_hotplug_mutex); + + if (bcl_soc_state == BCL_LOW_THRESHOLD + || bcl_vph_state == BCL_LOW_THRESHOLD) + bcl_hotplug_request = bcl_soc_hotplug_mask; + else if (bcl_ibat_state == BCL_HIGH_THRESHOLD) + bcl_hotplug_request = bcl_hotplug_mask; + else + bcl_hotplug_request = 0; + + cpumask_clear(&curr_req.offline_mask); + for_each_possible_cpu(cpu) { + if (bcl_hotplug_request & BIT(cpu)) + cpumask_set_cpu(cpu, &curr_req.offline_mask); + } + trace_bcl_sw_mitigation("Start hotplug CPU", bcl_hotplug_request); + ret = devmgr_client_request_mitigation( + gbcl->hotplug_handle, + HOTPLUG_MITIGATION_REQ, + &curr_req); + if (ret) { + pr_err("hotplug request failed. err:%d\n", ret); + goto handle_hotplug_exit; + } + +handle_hotplug_exit: + mutex_unlock(&bcl_hotplug_mutex); + trace_bcl_sw_mitigation_event("stop hotplug mitigation"); +} + +static void update_cpu_freq(void) +{ + int cpu, ret = 0; + union device_request cpufreq_req; + + trace_bcl_sw_mitigation_event("Start Frequency Mitigate"); + cpufreq_req.freq.max_freq = UINT_MAX; + cpufreq_req.freq.min_freq = CPUFREQ_MIN_NO_MITIGATION; + + if (bcl_vph_state == BCL_LOW_THRESHOLD + || bcl_ibat_state == BCL_HIGH_THRESHOLD + || bcl_soc_state == BCL_LOW_THRESHOLD) { + cpufreq_req.freq.max_freq = (gbcl->bcl_monitor_type + == BCL_IBAT_MONITOR_TYPE) ? gbcl->btm_freq_max + : gbcl->bcl_p_freq_max; + } + + for_each_possible_cpu(cpu) { + if (!(bcl_frequency_mask & BIT(cpu))) + continue; + pr_debug("Requesting Max freq:%u for CPU%d\n", + cpufreq_req.freq.max_freq, cpu); + trace_bcl_sw_mitigation("Frequency Mitigate CPU", cpu); + ret = devmgr_client_request_mitigation( + gbcl->cpufreq_handle[cpu], + CPUFREQ_MITIGATION_REQ, &cpufreq_req); + if (ret) + pr_err("Error updating freq for CPU%d. ret:%d\n", + cpu, ret); + } + trace_bcl_sw_mitigation_event("End Frequency Mitigation"); +} + +static void power_supply_callback(struct power_supply *psy) +{ + static struct power_supply *bms_psy; + union power_supply_propval ret = {0,}; + int battery_percentage; + enum bcl_threshold_state prev_soc_state; + + if (gbcl->bcl_mode != BCL_DEVICE_ENABLED) { + pr_debug("BCL is not enabled\n"); + return; + } + + if (!bms_psy) + bms_psy = power_supply_get_by_name("bms"); + if (bms_psy) { + battery_percentage = power_supply_get_property(bms_psy, + POWER_SUPPLY_PROP_CAPACITY, &ret); + battery_percentage = ret.intval; + battery_soc_val = battery_percentage; + pr_debug("Battery SOC reported:%d", battery_soc_val); + trace_bcl_sw_mitigation("SoC reported", battery_soc_val); + prev_soc_state = bcl_soc_state; + bcl_soc_state = (battery_soc_val <= soc_low_threshold) ? + BCL_LOW_THRESHOLD : BCL_HIGH_THRESHOLD; + if (bcl_soc_state == prev_soc_state) + return; + trace_bcl_sw_mitigation_event( + (bcl_soc_state == BCL_LOW_THRESHOLD) + ? "trigger SoC mitigation" + : "clear SoC mitigation"); + if (bcl_hotplug_enabled) + queue_work(gbcl->bcl_hotplug_wq, &bcl_hotplug_work); + update_cpu_freq(); + } +} + +static int bcl_get_battery_voltage(int *vbatt_mv) +{ + static struct power_supply *psy; + union power_supply_propval ret = {0,}; + + if (psy == NULL) { + psy = power_supply_get_by_name("battery"); + if (psy == NULL) { + pr_err("failed to get ps battery\n"); + return -EINVAL; + } + } + + if (power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &ret)) + return -EINVAL; + + if (ret.intval <= 0) + return -EINVAL; + + *vbatt_mv = ret.intval / 1000; + return 0; +} + + +static int bcl_get_resistance(int *rbatt_mohm) +{ + static struct power_supply *psy; + union power_supply_propval ret = {0,}; + + if (psy == NULL) { + psy = + power_supply_get_by_name(gbcl->bcl_no_bms ? "battery" : "bms"); + if (psy == NULL) { + pr_err("failed to get ps %s\n", + gbcl->bcl_no_bms ? "battery" : "bms"); + return -EINVAL; + } + } + if (power_supply_get_property(psy, POWER_SUPPLY_PROP_RESISTANCE, &ret)) + return -EINVAL; + + if (ret.intval < 1000) + return -EINVAL; + + *rbatt_mohm = ret.intval / 1000; + + return 0; +} + +/* + * BCL iavail calculation and trigger notification to user space + * if iavail cross threshold + */ +static void bcl_calculate_iavail_trigger(void) +{ + int iavail_ma = 0; + int vbatt_mv; + int rbatt_mohm; + bool threshold_cross = false; + + if (!gbcl) { + pr_err("called before initialization\n"); + return; + } + + if (bcl_get_battery_voltage(&vbatt_mv)) + return; + + if (bcl_get_resistance(&rbatt_mohm)) + return; + + iavail_ma = (vbatt_mv - gbcl->bcl_vbat_min) * 1000 / rbatt_mohm; + + gbcl->bcl_rbat_mohm = rbatt_mohm; + gbcl->bcl_vbat_mv = vbatt_mv; + gbcl->bcl_iavail = iavail_ma; + + pr_debug("iavail %d, vbatt %d rbatt %d\n", iavail_ma, vbatt_mv, + rbatt_mohm); + + if ((gbcl->bcl_threshold_mode[BCL_HIGH_THRESHOLD_TYPE] == + BCL_IAVAIL_THRESHOLD_ENABLED) + && (iavail_ma >= + gbcl->bcl_threshold_value_ma[BCL_HIGH_THRESHOLD_TYPE])) + threshold_cross = true; + else if ((gbcl->bcl_threshold_mode[BCL_LOW_THRESHOLD_TYPE] + == BCL_IAVAIL_THRESHOLD_ENABLED) + && (iavail_ma <= + gbcl->bcl_threshold_value_ma[BCL_LOW_THRESHOLD_TYPE])) + threshold_cross = true; + + if (threshold_cross) + sysfs_notify(&gbcl->dev->kobj, NULL, "type"); +} + +/* + * BCL iavail work + */ +static void bcl_iavail_work(struct work_struct *work) +{ + struct bcl_context *bcl = container_of(work, + struct bcl_context, bcl_iavail_work.work); + + if (gbcl->bcl_mode == BCL_DEVICE_ENABLED) { + bcl_calculate_iavail_trigger(); + /* restart the delay work for caculating imax */ + schedule_delayed_work(&bcl->bcl_iavail_work, + msecs_to_jiffies(bcl->bcl_poll_interval_msec)); + } +} + +static void bcl_ibat_notify(enum bcl_threshold_state thresh_type) +{ + bcl_ibat_state = thresh_type; + if (bcl_hotplug_enabled) + queue_work(gbcl->bcl_hotplug_wq, &bcl_hotplug_work); + update_cpu_freq(); +} + +static void bcl_vph_notify(enum bcl_threshold_state thresh_type) +{ + bcl_vph_state = thresh_type; + if (bcl_hotplug_enabled) + queue_work(gbcl->bcl_hotplug_wq, &bcl_hotplug_work); + update_cpu_freq(); +} + +int bcl_voltage_notify(bool is_high_thresh) +{ + int ret = 0; + + if (!gbcl) { + pr_err("BCL Driver not configured\n"); + return -EINVAL; + } + if (gbcl->bcl_mode == BCL_DEVICE_ENABLED) { + pr_err("BCL Driver is enabled\n"); + return -EINVAL; + } + + trace_bcl_sw_mitigation_event((is_high_thresh) + ? "vbat High trip notify" + : "vbat Low trip notify"); + bcl_vph_notify((is_high_thresh) ? BCL_HIGH_THRESHOLD + : BCL_LOW_THRESHOLD); + return ret; +} +EXPORT_SYMBOL(bcl_voltage_notify); + +int bcl_current_notify(bool is_high_thresh) +{ + int ret = 0; + + if (!gbcl) { + pr_err("BCL Driver not configured\n"); + return -EINVAL; + } + if (gbcl->bcl_mode == BCL_DEVICE_ENABLED) { + pr_err("BCL Driver is enabled\n"); + return -EINVAL; + } + + trace_bcl_sw_mitigation_event((is_high_thresh) + ? "ibat High trip notify" + : "ibat Low trip notify"); + bcl_ibat_notify((is_high_thresh) ? BCL_HIGH_THRESHOLD + : BCL_LOW_THRESHOLD); + return ret; +} +EXPORT_SYMBOL(bcl_current_notify); + +static void bcl_ibat_notification(enum qpnp_tm_state state, void *ctx); +static void bcl_vph_notification(enum qpnp_tm_state state, void *ctx); +static int bcl_config_ibat_adc(struct bcl_context *bcl, + enum bcl_iavail_threshold_type thresh_type); +static int bcl_config_vph_adc(struct bcl_context *bcl, + enum bcl_iavail_threshold_type thresh_type) +{ + int ret = 0; + + if (bcl->bcl_mode == BCL_DEVICE_DISABLED + || bcl->bcl_monitor_type != BCL_IBAT_MONITOR_TYPE) + return -EINVAL; + + switch (thresh_type) { + case BCL_HIGH_THRESHOLD_TYPE: + bcl->btm_vph_adc_param.state_request = ADC_TM_HIGH_THR_ENABLE; + break; + case BCL_LOW_THRESHOLD_TYPE: + bcl->btm_vph_adc_param.state_request = ADC_TM_LOW_THR_ENABLE; + break; + default: + pr_err("Invalid threshold type:%d\n", thresh_type); + return -EINVAL; + } + bcl->btm_vph_adc_param.low_thr = bcl->btm_vph_low_thresh; + bcl->btm_vph_adc_param.high_thr = bcl->btm_vph_high_thresh; + bcl->btm_vph_adc_param.timer_interval = + adc_timer_val_usec[ADC_MEAS1_INTERVAL_1S]; + bcl->btm_vph_adc_param.btm_ctx = bcl; + bcl->btm_vph_adc_param.threshold_notification = bcl_vph_notification; + bcl->btm_vph_adc_param.channel = bcl->btm_vph_chan; + + ret = qpnp_adc_tm_channel_measure(bcl->btm_adc_tm_dev, + &bcl->btm_vph_adc_param); + if (ret < 0) + pr_err("Error configuring BTM for Vph. ret:%d\n", ret); + else + pr_debug("Vph config. poll:%d high_uv:%d(%s) low_uv:%d(%s)\n", + bcl->btm_vph_adc_param.timer_interval, + bcl->btm_vph_adc_param.high_thr, + (bcl->btm_vph_adc_param.state_request == + ADC_TM_HIGH_THR_ENABLE) ? "enabled" : "disabled", + bcl->btm_vph_adc_param.low_thr, + (bcl->btm_vph_adc_param.state_request == + ADC_TM_LOW_THR_ENABLE) ? "enabled" : "disabled"); + + return ret; +} + +static int current_to_voltage(struct bcl_context *bcl, int ua) +{ + return DIV_ROUND_CLOSEST(ua * bcl->btm_uv_to_ua_denominator, + bcl->btm_uv_to_ua_numerator); +} + +static int voltage_to_current(struct bcl_context *bcl, int uv) +{ + return DIV_ROUND_CLOSEST(uv * bcl->btm_uv_to_ua_numerator, + bcl->btm_uv_to_ua_denominator); +} + +static int adc_time_to_uSec(struct bcl_context *bcl, + enum qpnp_adc_meas_timer_1 t) +{ + return adc_timer_val_usec[t]; +} + +static int uSec_to_adc_time(struct bcl_context *bcl, int us) +{ + int i; + + for (i = ARRAY_SIZE(adc_timer_val_usec) - 1; + i >= 0 && adc_timer_val_usec[i] > us; i--) + ; + + /* disallow continuous mode */ + if (i <= 0) + return -EINVAL; + + return i; +} + +static int vph_disable(void) +{ + int ret = 0; + + ret = qpnp_adc_tm_disable_chan_meas(gbcl->btm_adc_tm_dev, + &gbcl->btm_vph_adc_param); + if (ret) { + pr_err("Error disabling ADC. err:%d\n", ret); + gbcl->bcl_mode = BCL_DEVICE_ENABLED; + gbcl->btm_mode = BCL_VPH_MONITOR_MODE; + goto vph_disable_exit; + } + bcl_vph_notify(BCL_THRESHOLD_DISABLED); + gbcl->btm_mode = BCL_MONITOR_DISABLED; + +vph_disable_exit: + return ret; +} + +static int ibat_disable(void) +{ + int ret = 0; + + ret = qpnp_adc_tm_disable_chan_meas(gbcl->btm_adc_tm_dev, + &gbcl->btm_ibat_adc_param); + if (ret) { + pr_err("Error disabling ADC. err:%d\n", ret); + gbcl->bcl_mode = BCL_DEVICE_ENABLED; + gbcl->btm_mode = BCL_IBAT_MONITOR_MODE; + goto ibat_disable_exit; + } + bcl_ibat_notify(BCL_THRESHOLD_DISABLED); + +ibat_disable_exit: + return ret; +} + +static void bcl_periph_ibat_notify(enum bcl_trip_type type, int trip_temp, + void *data) +{ + if (type == BCL_HIGH_TRIP) + bcl_ibat_notify(BCL_HIGH_THRESHOLD); + else + bcl_ibat_notify(BCL_LOW_THRESHOLD); +} + +static void bcl_periph_vbat_notify(enum bcl_trip_type type, int trip_temp, + void *data) +{ + if (type == BCL_HIGH_TRIP) + bcl_vph_notify(BCL_HIGH_THRESHOLD); + else + bcl_vph_notify(BCL_LOW_THRESHOLD); +} + +static void bcl_periph_mode_set(enum bcl_device_mode mode) +{ + int ret = 0; + struct power_supply_config bcl_psy_cfg = {}; + + if (mode == BCL_DEVICE_ENABLED) { + /* + * Power supply monitor wont send a callback till the + * power state changes. Make sure we read the current SoC + * and mitigate. + */ + power_supply_callback(bcl_psy); + bcl_psy_cfg.num_supplicants = 0; + bcl_psy_cfg.drv_data = gbcl; + + bcl_psy = power_supply_register(gbcl->dev, &bcl_psy_des, + &bcl_psy_cfg); + if (IS_ERR(bcl_psy)) { + pr_err("Unable to register bcl_psy rc = %ld\n", + PTR_ERR(bcl_psy)); + return; + } + ret = msm_bcl_set_threshold(BCL_PARAM_CURRENT, BCL_HIGH_TRIP, + &gbcl->ibat_high_thresh); + if (ret) { + pr_err("Error setting Ibat high threshold. err:%d\n", + ret); + return; + } + ret = msm_bcl_set_threshold(BCL_PARAM_CURRENT, BCL_LOW_TRIP, + &gbcl->ibat_low_thresh); + if (ret) { + pr_err("Error setting Ibat low threshold. err:%d\n", + ret); + return; + } + ret = msm_bcl_set_threshold(BCL_PARAM_VOLTAGE, BCL_LOW_TRIP, + &gbcl->vbat_low_thresh); + if (ret) { + pr_err("Error setting Vbat low threshold. err:%d\n", + ret); + return; + } + ret = msm_bcl_set_threshold(BCL_PARAM_VOLTAGE, BCL_HIGH_TRIP, + &gbcl->vbat_high_thresh); + if (ret) { + pr_err("Error setting Vbat high threshold. err:%d\n", + ret); + return; + } + ret = msm_bcl_enable(); + if (ret) { + pr_err("Error enabling BCL\n"); + return; + } + gbcl->btm_mode = BCL_VPH_MONITOR_MODE; + } else { + power_supply_unregister(bcl_psy); + ret = msm_bcl_disable(); + if (ret) { + pr_err("Error disabling BCL\n"); + return; + } + gbcl->btm_mode = BCL_MONITOR_DISABLED; + bcl_soc_state = BCL_THRESHOLD_DISABLED; + bcl_vph_notify(BCL_HIGH_THRESHOLD); + bcl_ibat_notify(BCL_LOW_THRESHOLD); + bcl_handle_hotplug(NULL); + } +} + +static void ibat_mode_set(enum bcl_device_mode mode) +{ + int ret = 0; + + if (mode == BCL_DEVICE_ENABLED) { + gbcl->btm_mode = BCL_VPH_MONITOR_MODE; + ret = bcl_config_vph_adc(gbcl, BCL_LOW_THRESHOLD_TYPE); + if (ret) { + pr_err("Vph config error. ret:%d\n", ret); + gbcl->bcl_mode = BCL_DEVICE_DISABLED; + gbcl->btm_mode = BCL_MONITOR_DISABLED; + return; + } + } else { + switch (gbcl->btm_mode) { + case BCL_IBAT_MONITOR_MODE: + case BCL_IBAT_HIGH_LOAD_MODE: + ret = ibat_disable(); + if (ret) + return; + ret = vph_disable(); + if (ret) + return; + break; + case BCL_VPH_MONITOR_MODE: + ret = vph_disable(); + if (ret) + return; + break; + case BCL_MONITOR_DISABLED: + default: + break; + } + gbcl->btm_mode = BCL_MONITOR_DISABLED; + } +} + +static void bcl_vph_notification(enum qpnp_tm_state state, void *ctx) +{ + struct bcl_context *bcl = ctx; + int ret = 0; + + mutex_lock(&bcl_notify_mutex); + if (bcl->btm_mode == BCL_MONITOR_DISABLED) + goto unlock_and_exit; + + switch (state) { + case ADC_TM_LOW_STATE: + if (bcl->btm_mode != BCL_VPH_MONITOR_MODE) { + pr_err("Low thresh received with invalid btm mode:%d\n", + bcl->btm_mode); + ibat_mode_set(BCL_DEVICE_DISABLED); + goto unlock_and_exit; + } + pr_debug("Initiating Ibat current monitoring\n"); + bcl_vph_notify(BCL_LOW_THRESHOLD); + bcl_config_ibat_adc(gbcl, BCL_HIGH_THRESHOLD_TYPE); + bcl_config_vph_adc(gbcl, BCL_HIGH_THRESHOLD_TYPE); + bcl->btm_mode = BCL_IBAT_MONITOR_MODE; + break; + case ADC_TM_HIGH_STATE: + if (bcl->btm_mode != BCL_IBAT_MONITOR_MODE + && bcl->btm_mode != BCL_IBAT_HIGH_LOAD_MODE) { + pr_err("High thresh received with invalid btm mode:%d\n" + , bcl->btm_mode); + ibat_mode_set(BCL_DEVICE_DISABLED); + goto unlock_and_exit; + } + pr_debug("Exiting Ibat current monitoring\n"); + bcl->btm_mode = BCL_VPH_MONITOR_MODE; + ret = ibat_disable(); + if (ret) { + pr_err("Error disabling ibat ADC. err:%d\n", ret); + goto unlock_and_exit; + } + bcl_vph_notify(BCL_HIGH_THRESHOLD); + bcl_config_vph_adc(gbcl, BCL_LOW_THRESHOLD_TYPE); + break; + default: + goto set_thresh; + } +unlock_and_exit: + mutex_unlock(&bcl_notify_mutex); + return; + +set_thresh: + mutex_unlock(&bcl_notify_mutex); + bcl_config_vph_adc(gbcl, BCL_HIGH_THRESHOLD_TYPE); +} + +/* + * Set BCL mode + */ +static void bcl_mode_set(enum bcl_device_mode mode) +{ + if (!gbcl) + return; + if (gbcl->bcl_mode == mode) + return; + + gbcl->bcl_mode = mode; + switch (gbcl->bcl_monitor_type) { + case BCL_IAVAIL_MONITOR_TYPE: + if (mode == BCL_DEVICE_ENABLED) + schedule_delayed_work(&gbcl->bcl_iavail_work, 0); + else + cancel_delayed_work_sync(&(gbcl->bcl_iavail_work)); + break; + case BCL_IBAT_MONITOR_TYPE: + ibat_mode_set(mode); + break; + case BCL_IBAT_PERIPH_MONITOR_TYPE: + bcl_periph_mode_set(mode); + break; + default: + pr_err("Invalid monitor type:%d\n", gbcl->bcl_monitor_type); + break; + } +} + +#define show_bcl(name, variable, format) \ +static ssize_t \ +name##_show(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + if (gbcl) \ + return snprintf(buf, PAGE_SIZE, format, variable); \ + else \ + return -EPERM; \ +} + +show_bcl(type, gbcl->bcl_type, "%s\n") +show_bcl(vbat, gbcl->bcl_vbat_mv, "%d\n") +show_bcl(rbat, gbcl->bcl_rbat_mohm, "%d\n") +show_bcl(iavail, gbcl->bcl_iavail, "%d\n") +show_bcl(vbat_min, gbcl->bcl_vbat_min, "%d\n") +show_bcl(poll_interval, gbcl->bcl_poll_interval_msec, "%d\n") +show_bcl(high_ua, (gbcl->bcl_monitor_type == BCL_IBAT_MONITOR_TYPE) ? + voltage_to_current(gbcl, gbcl->btm_high_threshold_uv) + : gbcl->ibat_high_thresh.trip_value, "%d\n") +show_bcl(low_ua, (gbcl->bcl_monitor_type == BCL_IBAT_MONITOR_TYPE) ? + voltage_to_current(gbcl, gbcl->btm_low_threshold_uv) + : gbcl->ibat_low_thresh.trip_value, "%d\n") +show_bcl(adc_interval_us, (gbcl->bcl_monitor_type == BCL_IBAT_MONITOR_TYPE) ? + adc_time_to_uSec(gbcl, gbcl->btm_adc_interval) : 0, "%d\n") +show_bcl(freq_max, (gbcl->bcl_monitor_type == BCL_IBAT_MONITOR_TYPE) ? + gbcl->btm_freq_max : gbcl->bcl_p_freq_max, "%u\n") +show_bcl(vph_high, (gbcl->bcl_monitor_type == BCL_IBAT_MONITOR_TYPE) ? + gbcl->btm_vph_high_thresh : gbcl->vbat_high_thresh.trip_value, "%d\n") +show_bcl(vph_low, (gbcl->bcl_monitor_type == BCL_IBAT_MONITOR_TYPE) ? + gbcl->btm_vph_low_thresh : gbcl->vbat_low_thresh.trip_value, "%d\n") +show_bcl(freq_limit, gbcl->thermal_freq_limit, "%u\n") +show_bcl(vph_state, bcl_vph_state, "%d\n") +show_bcl(ibat_state, bcl_ibat_state, "%d\n") +show_bcl(hotplug_mask, bcl_hotplug_mask, "%d\n") +show_bcl(hotplug_soc_mask, bcl_soc_hotplug_mask, "%d\n") +show_bcl(hotplug_status, bcl_hotplug_request, "%d\n") +show_bcl(soc_low_thresh, soc_low_threshold, "%d\n") + +static ssize_t +mode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + if (!gbcl) + return -EPERM; + + return snprintf(buf, PAGE_SIZE, "%s\n", + gbcl->bcl_mode == BCL_DEVICE_ENABLED ? "enabled" + : "disabled"); +} + +static ssize_t +mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + if (!gbcl) + return -EPERM; + + if (!strcmp(buf, "enable")) { + bcl_mode_set(BCL_DEVICE_ENABLED); + pr_info("bcl enabled\n"); + } else if (!strcmp(buf, "disable")) { + bcl_mode_set(BCL_DEVICE_DISABLED); + pr_info("bcl disabled\n"); + } else { + return -EINVAL; + } + + return count; +} + +static ssize_t +poll_interval_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int value = 0, ret = 0; + + if (!gbcl) + return -EPERM; + + ret = kstrtoint(buf, 10, &value); + if (!ret) + return ret; + + if (value < MIN_BCL_POLL_INTERVAL) + return -EINVAL; + + gbcl->bcl_poll_interval_msec = value; + + return count; +} + +static ssize_t vbat_min_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int value = 0; + int ret = 0; + + if (!gbcl) + return -EPERM; + + ret = kstrtoint(buf, 10, &value); + + if (ret || (value < 0)) { + pr_err("Incorrect vbatt min value\n"); + return -EINVAL; + } + + gbcl->bcl_vbat_min = value; + return count; +} + +static ssize_t iavail_low_threshold_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (!gbcl) + return -EPERM; + + return snprintf(buf, PAGE_SIZE, "%s\n", + gbcl->bcl_threshold_mode[BCL_LOW_THRESHOLD_TYPE] + == BCL_IAVAIL_THRESHOLD_ENABLED ? "enabled" : "disabled"); +} + +static ssize_t iavail_low_threshold_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + if (!gbcl) + return -EPERM; + + if (!strcmp(buf, "enable")) + gbcl->bcl_threshold_mode[BCL_LOW_THRESHOLD_TYPE] + = BCL_IAVAIL_THRESHOLD_ENABLED; + else if (!strcmp(buf, "disable")) + gbcl->bcl_threshold_mode[BCL_LOW_THRESHOLD_TYPE] + = BCL_IAVAIL_THRESHOLD_DISABLED; + else + return -EINVAL; + + return count; +} +static ssize_t iavail_high_threshold_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (!gbcl) + return -EPERM; + + return snprintf(buf, PAGE_SIZE, "%s\n", + gbcl->bcl_threshold_mode[BCL_HIGH_THRESHOLD_TYPE] + == BCL_IAVAIL_THRESHOLD_ENABLED ? "enabled" : "disabled"); +} + +static ssize_t iavail_high_threshold_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + if (!gbcl) + return -EPERM; + + if (!strcmp(buf, "enable")) + gbcl->bcl_threshold_mode[BCL_HIGH_THRESHOLD_TYPE] + = BCL_IAVAIL_THRESHOLD_ENABLED; + else if (!strcmp(buf, "disable")) + gbcl->bcl_threshold_mode[BCL_HIGH_THRESHOLD_TYPE] + = BCL_IAVAIL_THRESHOLD_DISABLED; + else + return -EINVAL; + + return count; +} + +static ssize_t iavail_low_threshold_value_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (!gbcl) + return -EPERM; + + return snprintf(buf, PAGE_SIZE, "%d\n", + gbcl->bcl_threshold_value_ma[BCL_LOW_THRESHOLD_TYPE]); +} + + +static ssize_t iavail_low_threshold_value_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val = 0; + int ret = 0; + + if (!gbcl) + return -EPERM; + + ret = kstrtoint(buf, 10, &val); + + if (ret || (val < 0)) { + pr_err("Incorrect available current threshold value\n"); + return -EINVAL; + } + + gbcl->bcl_threshold_value_ma[BCL_LOW_THRESHOLD_TYPE] = val; + + return count; +} +static ssize_t iavail_high_threshold_value_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (!gbcl) + return -EPERM; + + return snprintf(buf, PAGE_SIZE, "%d\n", + gbcl->bcl_threshold_value_ma[BCL_HIGH_THRESHOLD_TYPE]); +} + +static ssize_t iavail_high_threshold_value_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val = 0; + int ret = 0; + + if (!gbcl) + return -EPERM; + ret = kstrtoint(buf, 10, &val); + + if (ret || (val < 0)) { + pr_err("Incorrect available current threshold value\n"); + return -EINVAL; + } + + gbcl->bcl_threshold_value_ma[BCL_HIGH_THRESHOLD_TYPE] = val; + + return count; +} + +static int convert_to_int(const char *buf, int *val) +{ + int ret = 0; + + if (!gbcl) + return -EPERM; + if (gbcl->bcl_mode != BCL_DEVICE_DISABLED) { + pr_err("BCL is not disabled\n"); + return -EINVAL; + } + + ret = kstrtoint(buf, 10, val); + if (ret || (*val < 0)) { + pr_err("Invalid high threshold %s val:%d ret:%d\n", buf, *val, + ret); + return -EINVAL; + } + + return ret; +} + +static ssize_t high_ua_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val = 0; + int ret = 0; + + ret = convert_to_int(buf, &val); + if (ret) + return ret; + + if (gbcl->bcl_monitor_type == BCL_IBAT_MONITOR_TYPE) + gbcl->btm_high_threshold_uv = current_to_voltage(gbcl, val); + else + gbcl->ibat_high_thresh.trip_value = val; + + return count; +} + +static ssize_t low_ua_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val = 0; + int ret = 0; + + ret = convert_to_int(buf, &val); + if (ret) + return ret; + + if (gbcl->bcl_monitor_type == BCL_IBAT_MONITOR_TYPE) + gbcl->btm_low_threshold_uv = current_to_voltage(gbcl, val); + else + gbcl->ibat_low_thresh.trip_value = val; + + return count; +} + +static ssize_t freq_max_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val = 0; + int ret = 0; + uint32_t *freq_lim = NULL; + + ret = convert_to_int(buf, &val); + if (ret) + return ret; + freq_lim = (gbcl->bcl_monitor_type == BCL_IBAT_MONITOR_TYPE) ? + &gbcl->btm_freq_max : &gbcl->bcl_p_freq_max; + *freq_lim = max_t(uint32_t, val, gbcl->thermal_freq_limit); + + return count; +} + +static ssize_t vph_low_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val = 0; + int ret = 0; + int *thresh = NULL; + + ret = convert_to_int(buf, &val); + if (ret) + return ret; + + thresh = (gbcl->bcl_monitor_type == BCL_IBAT_MONITOR_TYPE) + ? (int *)&gbcl->btm_vph_low_thresh + : &gbcl->vbat_low_thresh.trip_value; + *thresh = val; + + return count; +} + +static ssize_t vph_high_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val = 0; + int ret = 0; + int *thresh = NULL; + + ret = convert_to_int(buf, &val); + if (ret) + return ret; + + thresh = (gbcl->bcl_monitor_type == BCL_IBAT_MONITOR_TYPE) + ? (int *)&gbcl->btm_vph_high_thresh + : &gbcl->vbat_high_thresh.trip_value; + *thresh = val; + + return count; +} + +static ssize_t hotplug_mask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0, val = 0; + + ret = convert_to_int(buf, &val); + if (ret) + return ret; + + bcl_hotplug_mask = val; + pr_info("bcl hotplug mask updated to %d\n", bcl_hotplug_mask); + + if (!bcl_hotplug_mask && !bcl_soc_hotplug_mask) + bcl_hotplug_enabled = false; + else + bcl_hotplug_enabled = true; + + return count; +} + +static ssize_t hotplug_soc_mask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0, val = 0; + + ret = convert_to_int(buf, &val); + if (ret) + return ret; + + bcl_soc_hotplug_mask = val; + pr_info("bcl soc hotplug mask updated to %d\n", bcl_soc_hotplug_mask); + + if (!bcl_hotplug_mask && !bcl_soc_hotplug_mask) + bcl_hotplug_enabled = false; + else + bcl_hotplug_enabled = true; + + return count; +} + +static ssize_t soc_low_thresh_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int val = 0; + int ret = 0; + + ret = convert_to_int(buf, &val); + if (ret) + return ret; + + soc_low_threshold = val; + pr_info("bcl soc low threshold updated to %d\n", soc_low_threshold); + + return count; +} + +/* + * BCL device attributes + */ +static struct device_attribute bcl_dev_attr[] = { + __ATTR(type, 0444, type_show, NULL), + __ATTR(iavail, 0444, iavail_show, NULL), + __ATTR(vbat_min, 0644, vbat_min_show, vbat_min_store), + __ATTR(vbat, 0444, vbat_show, NULL), + __ATTR(rbat, 0444, rbat_show, NULL), + __ATTR(mode, 0644, mode_show, mode_store), + __ATTR(poll_interval, 0644, + poll_interval_show, poll_interval_store), + __ATTR(iavail_low_threshold_mode, 0644, + iavail_low_threshold_mode_show, + iavail_low_threshold_mode_store), + __ATTR(iavail_high_threshold_mode, 0644, + iavail_high_threshold_mode_show, + iavail_high_threshold_mode_store), + __ATTR(iavail_low_threshold_value, 0644, + iavail_low_threshold_value_show, + iavail_low_threshold_value_store), + __ATTR(iavail_high_threshold_value, 0644, + iavail_high_threshold_value_show, + iavail_high_threshold_value_store), +}; + +static struct device_attribute btm_dev_attr[] = { + __ATTR(type, 0444, type_show, NULL), + __ATTR(mode, 0644, mode_show, mode_store), + __ATTR(vph_state, 0444, vph_state_show, NULL), + __ATTR(ibat_state, 0444, ibat_state_show, NULL), + __ATTR(high_threshold_ua, 0644, high_ua_show, high_ua_store), + __ATTR(low_threshold_ua, 0644, low_ua_show, low_ua_store), + __ATTR(adc_interval_us, 0444, adc_interval_us_show, NULL), + __ATTR(freq_max, 0644, freq_max_show, freq_max_store), + __ATTR(vph_high_thresh_uv, 0644, vph_high_show, vph_high_store), + __ATTR(vph_low_thresh_uv, 0644, vph_low_show, vph_low_store), + __ATTR(thermal_freq_limit, 0444, freq_limit_show, NULL), + __ATTR(hotplug_status, 0444, hotplug_status_show, NULL), + __ATTR(hotplug_mask, 0644, hotplug_mask_show, hotplug_mask_store), + __ATTR(hotplug_soc_mask, 0644, hotplug_soc_mask_show, + hotplug_soc_mask_store), + __ATTR(soc_low_thresh, 0644, soc_low_thresh_show, soc_low_thresh_store), +}; + +static int create_bcl_sysfs(struct bcl_context *bcl) +{ + int result = 0, num_attr = 0, i; + struct device_attribute *attr_ptr = NULL; + + switch (bcl->bcl_monitor_type) { + case BCL_IAVAIL_MONITOR_TYPE: + num_attr = sizeof(bcl_dev_attr)/sizeof(struct device_attribute); + attr_ptr = bcl_dev_attr; + break; + case BCL_IBAT_MONITOR_TYPE: + case BCL_IBAT_PERIPH_MONITOR_TYPE: + num_attr = sizeof(btm_dev_attr)/sizeof(struct device_attribute); + attr_ptr = btm_dev_attr; + break; + default: + pr_err("Invalid monitor type:%d\n", bcl->bcl_monitor_type); + return -EINVAL; + } + + for (i = 0; i < num_attr; i++) { + result = device_create_file(bcl->dev, &attr_ptr[i]); + if (result < 0) + return result; + } + + return result; +} + +static void remove_bcl_sysfs(struct bcl_context *bcl) +{ + int num_attr = 0, i; + struct device_attribute *attr_ptr = NULL; + + switch (bcl->bcl_monitor_type) { + case BCL_IAVAIL_MONITOR_TYPE: + num_attr = sizeof(bcl_dev_attr)/sizeof(struct device_attribute); + attr_ptr = bcl_dev_attr; + break; + case BCL_IBAT_MONITOR_TYPE: + num_attr = sizeof(btm_dev_attr)/sizeof(struct device_attribute); + attr_ptr = btm_dev_attr; + break; + default: + pr_err("Invalid monitor type:%d\n", bcl->bcl_monitor_type); + return; + } + + for (i = 0; i < num_attr; i++) + device_remove_file(bcl->dev, &attr_ptr[i]); +} + +static int bcl_config_ibat_adc(struct bcl_context *bcl, + enum bcl_iavail_threshold_type thresh_type) +{ + int ret = 0; + + if (bcl->bcl_mode == BCL_DEVICE_DISABLED + || bcl->bcl_monitor_type != BCL_IBAT_MONITOR_TYPE) + return -EINVAL; + + switch (thresh_type) { + case BCL_HIGH_THRESHOLD_TYPE: + bcl->btm_ibat_adc_param.state_request = ADC_TM_HIGH_THR_ENABLE; + break; + case BCL_LOW_THRESHOLD_TYPE: + bcl->btm_ibat_adc_param.state_request = ADC_TM_LOW_THR_ENABLE; + break; + default: + pr_err("Invalid threshold type:%d\n", thresh_type); + return -EINVAL; + } + + bcl->btm_ibat_adc_param.low_thr = bcl->btm_low_threshold_uv; + bcl->btm_ibat_adc_param.high_thr = bcl->btm_high_threshold_uv; + bcl->btm_ibat_adc_param.timer_interval = bcl->btm_adc_interval; + bcl->btm_ibat_adc_param.btm_ctx = bcl; + bcl->btm_ibat_adc_param.threshold_notification = bcl_ibat_notification; + bcl->btm_ibat_adc_param.channel = bcl->btm_ibat_chan; + + ret = qpnp_adc_tm_channel_measure(bcl->btm_adc_tm_dev, + &bcl->btm_ibat_adc_param); + if (ret < 0) + pr_err("Error configuring BTM. ret:%d\n", ret); + else + pr_debug("BTM config. poll:%d high_uv:%d(%s) low_uv:%d(%s)\n", + bcl->btm_adc_interval, + bcl->btm_ibat_adc_param.high_thr, + (bcl->btm_ibat_adc_param.state_request == + ADC_TM_HIGH_THR_ENABLE) ? "enabled" : "disabled", + bcl->btm_ibat_adc_param.low_thr, + (bcl->btm_ibat_adc_param.state_request == + ADC_TM_LOW_THR_ENABLE) ? "enabled" : "disabled"); + return ret; +} + +static void bcl_ibat_notification(enum qpnp_tm_state state, void *ctx) +{ + struct bcl_context *bcl = ctx; + int ret = 0; + + mutex_lock(&bcl_notify_mutex); + if (bcl->btm_mode == BCL_MONITOR_DISABLED || + bcl->btm_mode == BCL_VPH_MONITOR_MODE) + goto unlock_and_return; + + switch (state) { + case ADC_TM_LOW_STATE: + if (bcl->btm_mode != BCL_IBAT_HIGH_LOAD_MODE) + goto set_ibat_threshold; + pr_debug("ibat low load enter\n"); + bcl->btm_mode = BCL_IBAT_MONITOR_MODE; + bcl_ibat_notify(BCL_LOW_THRESHOLD); + break; + case ADC_TM_HIGH_STATE: + if (bcl->btm_mode != BCL_IBAT_MONITOR_MODE) + goto set_ibat_threshold; + pr_debug("ibat high load enter\n"); + bcl->btm_mode = BCL_IBAT_HIGH_LOAD_MODE; + bcl_ibat_notify(BCL_HIGH_THRESHOLD); + break; + default: + pr_err("Invalid threshold state:%d\n", state); + bcl_config_ibat_adc(bcl, BCL_HIGH_THRESHOLD_TYPE); + goto unlock_and_return; + } + +set_ibat_threshold: + ret = bcl_config_ibat_adc(bcl, (state == ADC_TM_LOW_STATE) ? + BCL_HIGH_THRESHOLD_TYPE : BCL_LOW_THRESHOLD_TYPE); + if (ret < 0) + pr_err("Error configuring %s thresh. err:%d\n", + (state == ADC_TM_LOW_STATE) ? "high" : "low", ret); +unlock_and_return: + mutex_unlock(&bcl_notify_mutex); +} + +static int bcl_suspend(struct device *dev) +{ + int ret = 0; + struct bcl_context *bcl = dev_get_drvdata(dev); + + if (bcl->bcl_monitor_type == BCL_IBAT_MONITOR_TYPE && + bcl->bcl_mode == BCL_DEVICE_ENABLED) { + switch (bcl->btm_mode) { + case BCL_IBAT_MONITOR_MODE: + case BCL_IBAT_HIGH_LOAD_MODE: + ret = ibat_disable(); + if (!ret) + vph_disable(); + break; + case BCL_VPH_MONITOR_MODE: + vph_disable(); + break; + case BCL_MONITOR_DISABLED: + default: + break; + } + } + return 0; +} + +static int bcl_resume(struct device *dev) +{ + struct bcl_context *bcl = dev_get_drvdata(dev); + + if (bcl->bcl_monitor_type == BCL_IBAT_MONITOR_TYPE && + bcl->bcl_mode == BCL_DEVICE_ENABLED) { + bcl->btm_mode = BCL_VPH_MONITOR_MODE; + bcl_config_vph_adc(bcl, BCL_LOW_THRESHOLD_TYPE); + } + return 0; +} + +static void get_vdd_rstr_freq(struct bcl_context *bcl, + struct device_node *ibat_node) +{ + int ret = 0; + struct device_node *phandle = NULL; + char *key = NULL; + + key = "qcom,thermal-handle"; + phandle = of_parse_phandle(ibat_node, key, 0); + if (!phandle) { + pr_err("Thermal handle not present\n"); + ret = -ENODEV; + goto vdd_rstr_exit; + } + key = "qcom,levels"; + ret = of_property_read_u32_index(phandle, key, 0, + &bcl->thermal_freq_limit); + if (ret) { + pr_err("Error reading property %s. ret:%d\n", key, ret); + goto vdd_rstr_exit; + } + +vdd_rstr_exit: + if (ret) + bcl->thermal_freq_limit = BTM_8084_FREQ_MITIG_LIMIT; +} + +static int probe_bcl_periph_prop(struct bcl_context *bcl) +{ + int ret = 0; + struct device_node *ibat_node = NULL, *dev_node = bcl->dev->of_node; + char *key = NULL; + + key = "qcom,ibat-monitor"; + ibat_node = of_find_node_by_name(dev_node, key); + if (!ibat_node) { + ret = -ENODEV; + goto ibat_probe_exit; + } + + BCL_FETCH_DT_U32(ibat_node, key, "qcom,low-threshold-uamp", ret, + bcl->ibat_low_thresh.trip_value); + if (ret) + goto ibat_probe_exit; + BCL_FETCH_DT_U32(ibat_node, key, "qcom,high-threshold-uamp", ret, + bcl->ibat_high_thresh.trip_value); + if (ret) + goto ibat_probe_exit; + BCL_FETCH_DT_U32(ibat_node, key, "qcom,mitigation-freq-khz", ret, + bcl->bcl_p_freq_max); + if (ret) + goto ibat_probe_exit; + BCL_FETCH_DT_U32(ibat_node, key, "qcom,vph-high-threshold-uv", ret, + bcl->vbat_high_thresh.trip_value); + if (ret) + goto ibat_probe_exit; + BCL_FETCH_DT_U32(ibat_node, key, "qcom,vph-low-threshold-uv", ret, + bcl->vbat_low_thresh.trip_value); + if (ret) + goto ibat_probe_exit; + BCL_FETCH_DT_U32(ibat_node, key, "qcom,soc-low-threshold", ret, + soc_low_threshold); + if (ret) + goto ibat_probe_exit; + + bcl->vbat_high_thresh.trip_notify + = bcl->vbat_low_thresh.trip_notify = bcl_periph_vbat_notify; + bcl->vbat_high_thresh.trip_data + = bcl->vbat_low_thresh.trip_data = (void *) bcl; + bcl->ibat_high_thresh.trip_notify + = bcl->ibat_low_thresh.trip_notify = bcl_periph_ibat_notify; + bcl->ibat_high_thresh.trip_data + = bcl->ibat_low_thresh.trip_data = (void *) bcl; + get_vdd_rstr_freq(bcl, ibat_node); + bcl->bcl_p_freq_max = max(bcl->bcl_p_freq_max, bcl->thermal_freq_limit); + + bcl->btm_mode = BCL_MONITOR_DISABLED; + bcl->bcl_monitor_type = BCL_IBAT_PERIPH_MONITOR_TYPE; + snprintf(bcl->bcl_type, BCL_NAME_LENGTH, "%s", + bcl_type[BCL_IBAT_PERIPH_MONITOR_TYPE]); + +ibat_probe_exit: + if (ret && ret != -EPROBE_DEFER) + dev_info(bcl->dev, "%s:%s Error reading key:%s. ret = %d\n", + KBUILD_MODNAME, __func__, key, ret); + + return ret; +} + +static int probe_btm_properties(struct bcl_context *bcl) +{ + int ret = 0, curr_ua = 0; + int adc_interval_us; + struct device_node *ibat_node = NULL, *dev_node = bcl->dev->of_node; + char *key = NULL; + + key = "qcom,ibat-monitor"; + ibat_node = of_find_node_by_name(dev_node, key); + if (!ibat_node) { + ret = -ENODEV; + goto btm_probe_exit; + } + + key = "qcom,uv-to-ua-numerator"; + ret = of_property_read_u32(ibat_node, key, + &bcl->btm_uv_to_ua_numerator); + if (ret < 0) + goto btm_probe_exit; + + key = "qcom,uv-to-ua-denominator"; + ret = of_property_read_u32(ibat_node, key, + &bcl->btm_uv_to_ua_denominator); + if (ret < 0) + goto btm_probe_exit; + + key = "qcom,low-threshold-uamp"; + ret = of_property_read_u32(ibat_node, key, &curr_ua); + if (ret < 0) + goto btm_probe_exit; + bcl->btm_low_threshold_uv = current_to_voltage(bcl, curr_ua); + + key = "qcom,high-threshold-uamp"; + ret = of_property_read_u32(ibat_node, key, &curr_ua); + if (ret < 0) + goto btm_probe_exit; + bcl->btm_high_threshold_uv = current_to_voltage(bcl, curr_ua); + + key = "qcom,mitigation-freq-khz"; + ret = of_property_read_u32(ibat_node, key, &bcl->btm_freq_max); + if (ret < 0) + goto btm_probe_exit; + + key = "qcom,ibat-channel"; + ret = of_property_read_u32(ibat_node, key, &bcl->btm_ibat_chan); + if (ret < 0) + goto btm_probe_exit; + + key = "qcom,adc-interval-usec"; + ret = of_property_read_u32(ibat_node, key, &adc_interval_us); + if (ret < 0) + goto btm_probe_exit; + bcl->btm_adc_interval = uSec_to_adc_time(bcl, adc_interval_us); + + key = "qcom,vph-channel"; + ret = of_property_read_u32(ibat_node, key, &bcl->btm_vph_chan); + if (ret < 0) + goto btm_probe_exit; + + key = "qcom,vph-high-threshold-uv"; + ret = of_property_read_u32(ibat_node, key, &bcl->btm_vph_high_thresh); + if (ret < 0) + goto btm_probe_exit; + + key = "qcom,vph-low-threshold-uv"; + ret = of_property_read_u32(ibat_node, key, &bcl->btm_vph_low_thresh); + if (ret < 0) + goto btm_probe_exit; + + key = "ibat-threshold"; + bcl->btm_adc_tm_dev = qpnp_get_adc_tm(bcl->dev, key); + if (IS_ERR(bcl->btm_adc_tm_dev)) { + ret = PTR_ERR(bcl->btm_adc_tm_dev); + goto btm_probe_exit; + } + + key = "ibat"; + bcl->btm_vadc_dev = qpnp_get_vadc(bcl->dev, key); + if (IS_ERR(bcl->btm_vadc_dev)) { + ret = PTR_ERR(bcl->btm_vadc_dev); + goto btm_probe_exit; + } + get_vdd_rstr_freq(bcl, ibat_node); + bcl->btm_freq_max = max(bcl->btm_freq_max, bcl->thermal_freq_limit); + + bcl->btm_mode = BCL_MONITOR_DISABLED; + bcl->bcl_monitor_type = BCL_IBAT_MONITOR_TYPE; + snprintf(bcl->bcl_type, BCL_NAME_LENGTH, "%s", + bcl_type[BCL_IBAT_MONITOR_TYPE]); + +btm_probe_exit: + if (ret && ret != -EPROBE_DEFER) + dev_info(bcl->dev, "%s:%s Error reading key:%s. ret = %d\n", + KBUILD_MODNAME, __func__, key, ret); + + return ret; +} + +static int bcl_battery_get_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) +{ + return 0; +} +static int bcl_battery_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + return 0; +} + +static uint32_t get_mask_from_core_handle(struct platform_device *pdev, + const char *key) +{ + struct device_node *core_phandle = NULL; + int i = 0, cpu = 0; + uint32_t mask = 0; + + core_phandle = of_parse_phandle(pdev->dev.of_node, + key, i++); + while (core_phandle) { + for_each_possible_cpu(cpu) { + if (of_get_cpu_node(cpu, NULL) == core_phandle) { + mask |= BIT(cpu); + break; + } + } + core_phandle = of_parse_phandle(pdev->dev.of_node, + key, i++); + } + + return mask; +} + +static int bcl_probe(struct platform_device *pdev) +{ + struct bcl_context *bcl = NULL; + int ret = 0; + enum bcl_device_mode bcl_mode = BCL_DEVICE_DISABLED; + char cpu_str[MAX_CPU_NAME]; + int cpu; + + bcl = devm_kzalloc(&pdev->dev, sizeof(struct bcl_context), GFP_KERNEL); + if (!bcl) + return -ENOMEM; + + /* For BCL */ + /* Init default BCL params */ + if (of_property_read_bool(pdev->dev.of_node, "qcom,bcl-enable")) + bcl_mode = BCL_DEVICE_ENABLED; + else + bcl_mode = BCL_DEVICE_DISABLED; + bcl->bcl_mode = BCL_DEVICE_DISABLED; + bcl->dev = &pdev->dev; + bcl->bcl_monitor_type = BCL_IAVAIL_MONITOR_TYPE; + bcl->bcl_threshold_mode[BCL_LOW_THRESHOLD_TYPE] = + BCL_IAVAIL_THRESHOLD_DISABLED; + bcl->bcl_threshold_mode[BCL_HIGH_THRESHOLD_TYPE] = + BCL_IAVAIL_THRESHOLD_DISABLED; + bcl->bcl_threshold_value_ma[BCL_LOW_THRESHOLD_TYPE] = 0; + bcl->bcl_threshold_value_ma[BCL_HIGH_THRESHOLD_TYPE] = 0; + bcl->bcl_vbat_min = BATTERY_VOLTAGE_MIN; + snprintf(bcl->bcl_type, BCL_NAME_LENGTH, "%s", + bcl_type[BCL_IAVAIL_MONITOR_TYPE]); + bcl->bcl_poll_interval_msec = BCL_POLL_INTERVAL; + + if (of_property_read_bool(pdev->dev.of_node, "qcom,bcl-no-bms")) + bcl->bcl_no_bms = true; + else + bcl->bcl_no_bms = false; + + bcl_frequency_mask = get_mask_from_core_handle(pdev, + "qcom,bcl-freq-control-list"); + bcl_hotplug_mask = get_mask_from_core_handle(pdev, + "qcom,bcl-hotplug-list"); + bcl_soc_hotplug_mask = get_mask_from_core_handle(pdev, + "qcom,bcl-soc-hotplug-list"); + + if (!bcl_hotplug_mask && !bcl_soc_hotplug_mask) + bcl_hotplug_enabled = false; + else + bcl_hotplug_enabled = true; + + if (of_property_read_bool(pdev->dev.of_node, + "qcom,bcl-framework-interface")) + ret = probe_bcl_periph_prop(bcl); + else + ret = probe_btm_properties(bcl); + + if (ret == -EPROBE_DEFER) + return ret; + ret = create_bcl_sysfs(bcl); + if (ret < 0) { + pr_err("Cannot create bcl sysfs\n"); + return ret; + } + bcl_psy_des.name = bcl_psy_name; + bcl_psy_des.type = POWER_SUPPLY_TYPE_BMS; + bcl_psy_des.get_property = bcl_battery_get_property; + bcl_psy_des.set_property = bcl_battery_set_property; + bcl_psy_des.num_properties = 0; + bcl_psy_des.external_power_changed = power_supply_callback; + bcl->bcl_hotplug_wq = alloc_workqueue("bcl_hotplug_wq", WQ_HIGHPRI, 0); + if (!bcl->bcl_hotplug_wq) { + pr_err("Workqueue alloc failed\n"); + return -ENOMEM; + } + + /* Initialize mitigation KTM interface */ + if (num_possible_cpus() > 1) { + bcl->hotplug_handle = devmgr_register_mitigation_client( + &pdev->dev, HOTPLUG_DEVICE, NULL); + if (IS_ERR(bcl->hotplug_handle)) { + ret = PTR_ERR(bcl->hotplug_handle); + pr_err("Error registering for hotplug. ret:%d\n", ret); + return ret; + } + } + for_each_possible_cpu(cpu) { + snprintf(cpu_str, MAX_CPU_NAME, "cpu%d", cpu); + bcl->cpufreq_handle[cpu] = devmgr_register_mitigation_client( + &pdev->dev, cpu_str, NULL); + if (IS_ERR(bcl->cpufreq_handle[cpu])) { + ret = PTR_ERR(bcl->cpufreq_handle[cpu]); + pr_err("Error registering for cpufreq. ret:%d\n", ret); + return ret; + } + } + + gbcl = bcl; + platform_set_drvdata(pdev, bcl); + INIT_DEFERRABLE_WORK(&bcl->bcl_iavail_work, bcl_iavail_work); + INIT_WORK(&bcl_hotplug_work, bcl_handle_hotplug); + if (bcl_mode == BCL_DEVICE_ENABLED) + bcl_mode_set(bcl_mode); + + return 0; +} + +static int bcl_remove(struct platform_device *pdev) +{ + int cpu; + + /* De-register KTM handle */ + if (gbcl->hotplug_handle) + devmgr_unregister_mitigation_client(&pdev->dev, + gbcl->hotplug_handle); + for_each_possible_cpu(cpu) { + if (gbcl->cpufreq_handle[cpu]) + devmgr_unregister_mitigation_client(&pdev->dev, + gbcl->cpufreq_handle[cpu]); + } + remove_bcl_sysfs(gbcl); + if (gbcl->bcl_hotplug_wq) + destroy_workqueue(gbcl->bcl_hotplug_wq); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static const struct of_device_id bcl_match_table[] = { + {.compatible = "qcom,bcl"}, + {}, +}; + +static const struct dev_pm_ops bcl_pm_ops = { + .resume = bcl_resume, + .suspend = bcl_suspend, +}; + +static struct platform_driver bcl_driver = { + .probe = bcl_probe, + .remove = bcl_remove, + .driver = { + .name = BCL_DEV_NAME, + .owner = THIS_MODULE, + .of_match_table = bcl_match_table, + .pm = &bcl_pm_ops, + }, +}; + +static int __init bcl_init(void) +{ + return platform_driver_register(&bcl_driver); +} + +static void __exit bcl_exit(void) +{ + platform_driver_unregister(&bcl_driver); +} + +late_initcall(bcl_init); +module_exit(bcl_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("battery current limit driver"); +MODULE_ALIAS("platform:" BCL_DEV_NAME); diff --git a/drivers/power/qcom-charger/bcl_peripheral.c b/drivers/power/qcom-charger/bcl_peripheral.c index 85d018a1a8b8..fc958b160f86 100644 --- a/drivers/power/qcom-charger/bcl_peripheral.c +++ b/drivers/power/qcom-charger/bcl_peripheral.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, 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 @@ -14,7 +14,6 @@ #define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__ #include <linux/module.h> -#include <linux/platform_device.h> #include <linux/interrupt.h> #include <linux/workqueue.h> #include <linux/kernel.h> @@ -28,6 +27,9 @@ #include <linux/mutex.h> #include <linux/msm_bcl.h> #include <linux/power_supply.h> +#include <soc/qcom/scm.h> +#include <linux/slab.h> +#include <asm/cacheflush.h> #define CREATE_TRACE_POINTS #define _BCL_HW_TRACE @@ -38,16 +40,11 @@ #define BCL_IBAT_INT_NAME "bcl-high-ibat-int" #define BCL_PARAM_MAX_ATTR 3 -#define BCL_INT_EN 0x15 #define BCL_MONITOR_EN 0x46 #define BCL_VBAT_VALUE 0x54 #define BCL_IBAT_VALUE 0x55 -#define BCL_VBAT_CP_VALUE 0x56 -#define BCL_IBAT_CP_VALUE 0x57 #define BCL_VBAT_MIN 0x58 #define BCL_IBAT_MAX 0x59 -#define BCL_VBAT_MIN_CP 0x5A -#define BCL_IBAT_MAX_CP 0x5B #define BCL_V_GAIN_BAT 0x60 #define BCL_I_GAIN_RSENSE 0x61 #define BCL_I_OFFSET_RSENSE 0x62 @@ -59,6 +56,32 @@ #define BCL_VBAT_TRIP 0x68 #define BCL_IBAT_TRIP 0x69 +#define BCL_COBALT_VBAT_VALUE 0x58 +#define BCL_COBALT_IBAT_VALUE 0x59 +#define BCL_COBALT_VBAT_MIN 0x5C +#define BCL_COBALT_IBAT_MAX 0x5D +#define BCL_COBALT_MAX_MIN_CLR 0x48 +#define BCL_COBALT_IBAT_MAX_CLR 3 +#define BCL_COBALT_VBAT_MIN_CLR 2 +#define BCL_COBALT_VBAT_ADC_LOW 0x72 +#define BCL_COBALT_VBAT_COMP_LOW 0x75 +#define BCL_COBALT_VBAT_COMP_TLOW 0x76 +#define BCL_COBALT_IBAT_HIGH 0x78 +#define BCL_COBALT_IBAT_TOO_HIGH 0x79 +#define BCL_COBALT_LMH_CFG 0xA3 +#define BCL_COBALT_BCL_CFG 0x6A +#define LMH_COBALT_INT_POL_HIGH 0x12 +#define LMH_COBALT_INT_EN 0x15 + +#define BCL_COBALT_VBAT_SCALING 39000 +#define BCL_COBALT_IBAT_SCALING 80000 +#define BCL_VBAT_LOW_THRESHOLD 0x7 /* 3.1V */ +#define BCL_VBAT_TLOW_THRESHOLD 0x5 /* 2.9v */ +#define BCL_IBAT_HIGH_THRESH_UA 4300000 +#define BCL_LMH_CFG_VAL 0x3 +#define BCL_CFG_VAL 0x81 +#define LMH_INT_VAL 0x7 + #define BCL_CONSTANT_NUM 32 #define BCL_READ_RETRY_LIMIT 3 #define VAL_CP_REG_BUF_LEN 3 @@ -67,6 +90,13 @@ #define PON_SPARE_FULL_CURRENT 0x0 #define PON_SPARE_DERATED_CURRENT 0x1 +#define LMH_DCVSH 0x10 +#define LMH_NODE_DCVS 0x44435653 /* DCVS */ +#define LMH_SUB_FN_BCL 0x42434C00 /* BCL */ +#define LMH_CLUSTER_0 0x6370302D /* cpAG */ +#define LMH_CLUSTER_1 0x6370312D /* cpAU */ +#define LMH_ALGO_ENABLE 0x454E424C /* ENBL */ + #define READ_CONV_FACTOR(_node, _key, _val, _ret, _dest) do { \ _ret = of_property_read_u32(_node, _key, &_val); \ if (_ret) { \ @@ -92,6 +122,12 @@ enum bcl_monitor_state { BCL_PARAM_POLLING, }; +enum bcl_hw_type { + BCL_PMI8994, + BCL_PMICOBALT, + BCL_VERSION_MAX, +}; + struct bcl_peripheral_data { struct bcl_param_data *param_data; struct bcl_driver_ops ops; @@ -122,6 +158,7 @@ struct bcl_device { struct regmap *regmap; uint16_t base_addr; uint16_t pon_spare_addr; + uint16_t fg_lmh_addr; int i_src; struct bcl_peripheral_data param[BCL_PARAM_MAX]; }; @@ -133,6 +170,7 @@ static const char bcl_psy_name[] = "fg_adc"; static bool calibration_done; static DEFINE_MUTEX(bcl_access_mutex); static DEFINE_MUTEX(bcl_enable_mutex); +static enum bcl_hw_type bcl_perph_version; static int bcl_read_multi_register(int16_t reg_offset, uint8_t *data, int len) { @@ -194,14 +232,24 @@ static void convert_vbat_to_adc_val(int *val) { struct bcl_peripheral_data *perph_data = NULL; - if (!bcl_perph) - return; - perph_data = &bcl_perph->param[BCL_PARAM_VOLTAGE]; - *val = (*val * 100 - / (100 + (perph_data->gain_factor_num * perph_data->gain) - * BCL_CONSTANT_NUM - / perph_data->gain_factor_den)) - / perph_data->scaling_factor; + switch (bcl_perph_version) { + case BCL_PMI8994: + if (!bcl_perph) + return; + perph_data = &bcl_perph->param[BCL_PARAM_VOLTAGE]; + *val = (*val * 100 + / (100 + (perph_data->gain_factor_num + * perph_data->gain) * BCL_CONSTANT_NUM + / perph_data->gain_factor_den)) + / perph_data->scaling_factor; + break; + case BCL_PMICOBALT: + *val = *val / BCL_COBALT_VBAT_SCALING; + break; + default: + break; + } + return; } @@ -209,13 +257,24 @@ static void convert_adc_to_vbat_val(int *val) { struct bcl_peripheral_data *perph_data = NULL; - if (!bcl_perph) - return; - perph_data = &bcl_perph->param[BCL_PARAM_VOLTAGE]; - *val = ((*val + 2) * perph_data->scaling_factor) - * (100 + (perph_data->gain_factor_num * perph_data->gain) - * BCL_CONSTANT_NUM / perph_data->gain_factor_den) - / 100; + switch (bcl_perph_version) { + case BCL_PMI8994: + if (!bcl_perph) + return; + perph_data = &bcl_perph->param[BCL_PARAM_VOLTAGE]; + *val = ((*val + 2) * perph_data->scaling_factor) + * (100 + (perph_data->gain_factor_num + * perph_data->gain) + * BCL_CONSTANT_NUM / perph_data->gain_factor_den) + / 100; + break; + case BCL_PMICOBALT: + *val = *val * BCL_COBALT_VBAT_SCALING; + break; + default: + break; + } + return; } @@ -223,15 +282,26 @@ static void convert_ibat_to_adc_val(int *val) { struct bcl_peripheral_data *perph_data = NULL; - if (!bcl_perph) - return; - perph_data = &bcl_perph->param[BCL_PARAM_CURRENT]; - *val = (*val * 100 - / (100 + (perph_data->gain_factor_num * perph_data->gain) - * BCL_CONSTANT_NUM / perph_data->gain_factor_den) - - (perph_data->offset_factor_num * perph_data->offset) - / perph_data->offset_factor_den) - / perph_data->scaling_factor; + switch (bcl_perph_version) { + case BCL_PMI8994: + if (!bcl_perph) + return; + perph_data = &bcl_perph->param[BCL_PARAM_CURRENT]; + *val = (*val * 100 + / (100 + (perph_data->gain_factor_num + * perph_data->gain) + * BCL_CONSTANT_NUM / perph_data->gain_factor_den) + - (perph_data->offset_factor_num * perph_data->offset) + / perph_data->offset_factor_den) + / perph_data->scaling_factor; + break; + case BCL_PMICOBALT: + *val = *val / BCL_COBALT_IBAT_SCALING; + break; + default: + break; + } + return; } @@ -239,14 +309,25 @@ static void convert_adc_to_ibat_val(int *val) { struct bcl_peripheral_data *perph_data = NULL; - if (!bcl_perph) - return; - perph_data = &bcl_perph->param[BCL_PARAM_CURRENT]; - *val = (*val * perph_data->scaling_factor - + (perph_data->offset_factor_num * perph_data->offset) - / perph_data->offset_factor_den) - * (100 + (perph_data->gain_factor_num * perph_data->gain) - * BCL_CONSTANT_NUM / perph_data->gain_factor_den) / 100; + switch (bcl_perph_version) { + case BCL_PMI8994: + if (!bcl_perph) + return; + perph_data = &bcl_perph->param[BCL_PARAM_CURRENT]; + *val = (*val * perph_data->scaling_factor + + (perph_data->offset_factor_num * perph_data->offset) + / perph_data->offset_factor_den) + * (100 + (perph_data->gain_factor_num + * perph_data->gain) * BCL_CONSTANT_NUM / + perph_data->gain_factor_den) / 100; + break; + case BCL_PMICOBALT: + *val = *val * BCL_COBALT_IBAT_SCALING; + break; + default: + break; + } + return; } @@ -266,18 +347,31 @@ static int bcl_set_high_ibat(int thresh_value) { int ret = 0, ibat_ua; int8_t val = 0; + uint32_t too_high_thresh = BCL_IBAT_HIGH_THRESH_UA; ibat_ua = thresh_value; convert_ibat_to_adc_val(&thresh_value); pr_debug("Setting Ibat high trip:%d. ADC_val:%d\n", ibat_ua, thresh_value); val = (int8_t)thresh_value; - ret = bcl_write_register(BCL_IBAT_TRIP, val); + ret = bcl_write_register((bcl_perph_version == BCL_PMI8994) ? + BCL_IBAT_TRIP : BCL_COBALT_IBAT_HIGH, val); if (ret) { pr_err("Error accessing BCL peripheral. err:%d\n", ret); return ret; } bcl_perph->param[BCL_PARAM_CURRENT].high_trip = thresh_value; + if (bcl_perph_version == BCL_PMICOBALT) { + convert_ibat_to_adc_val(&too_high_thresh); + pr_debug("Setting Ibat too high trip:%d. ADC_val:%d\n", + BCL_IBAT_HIGH_THRESH_UA, too_high_thresh); + val = (int8_t)too_high_thresh; + ret = bcl_write_register(BCL_COBALT_IBAT_TOO_HIGH, val); + if (ret) { + pr_err("Error accessing BCL peripheral. err:%d\n", ret); + return ret; + } + } if (bcl_perph->param[BCL_PARAM_CURRENT].inhibit_derating_ua == 0 || bcl_perph->pon_spare_addr == 0) @@ -313,25 +407,84 @@ static int bcl_set_low_vbat(int thresh_value) pr_debug("Setting Vbat low trip:%d. ADC_val:%d\n", vbat_uv, thresh_value); val = (int8_t)thresh_value; - ret = bcl_write_register(BCL_VBAT_TRIP, val); + ret = bcl_write_register((bcl_perph_version == BCL_PMI8994) + ? BCL_VBAT_TRIP : BCL_COBALT_VBAT_ADC_LOW, val); if (ret) { pr_err("Error accessing BCL peripheral. err:%d\n", ret); return ret; } + if (bcl_perph_version == BCL_PMICOBALT) { + ret = bcl_write_register(BCL_COBALT_VBAT_COMP_LOW, + BCL_VBAT_LOW_THRESHOLD); + if (ret) { + pr_err("Error accessing BCL peripheral. err:%d\n", ret); + return ret; + } + pr_debug("Setting Vbat low comparator threshold:0x%x.\n", + BCL_VBAT_LOW_THRESHOLD); + ret = bcl_write_register(BCL_COBALT_VBAT_COMP_TLOW, + BCL_VBAT_TLOW_THRESHOLD); + if (ret) { + pr_err("Error accessing BCL peripheral. err:%d\n", ret); + return ret; + } + pr_debug("Setting Vbat too low comparator threshold:0x%x.\n", + BCL_VBAT_TLOW_THRESHOLD); + } bcl_perph->param[BCL_PARAM_VOLTAGE].low_trip = thresh_value; return ret; } +static void bcl_lmh_dcvs_enable(void) +{ + struct scm_desc desc_arg; + uint32_t *payload = NULL; + + payload = kzalloc(sizeof(uint32_t) * 5, GFP_KERNEL); + if (!payload) + return; + + payload[0] = LMH_SUB_FN_BCL; + payload[1] = 0; /* unused sub-algorithm */ + payload[2] = LMH_ALGO_ENABLE; + payload[3] = 1; /* number of values */ + payload[4] = 1; + + desc_arg.args[0] = SCM_BUFFER_PHYS(payload); + desc_arg.args[1] = sizeof(uint32_t) * 5; + desc_arg.args[2] = LMH_NODE_DCVS; + desc_arg.args[3] = LMH_CLUSTER_0; + desc_arg.args[4] = 0; /* version */ + desc_arg.arginfo = SCM_ARGS(5, SCM_RO, SCM_VAL, SCM_VAL, + SCM_VAL, SCM_VAL); + + dmac_flush_range(payload, payload + 5 * (sizeof(uint32_t))); + if (scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, LMH_DCVSH), + &desc_arg)) + pr_err("Error enabling LMH BCL monitoringfor cluster0\n"); + + desc_arg.args[3] = LMH_CLUSTER_1; + if (scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, LMH_DCVSH), + &desc_arg)) + pr_err("Error enabling LMH BCL monitoringfor cluster1\n"); + + kfree(payload); +} + static int bcl_access_monitor_enable(bool enable) { int ret = 0, i = 0; struct bcl_peripheral_data *perph_data = NULL; + static bool hw_enabled; mutex_lock(&bcl_enable_mutex); if (enable == bcl_perph->enabled) goto access_exit; + if ((bcl_perph_version == BCL_PMICOBALT) && !hw_enabled && enable) + bcl_lmh_dcvs_enable(); + for (; i < BCL_PARAM_MAX; i++) { perph_data = &bcl_perph->param[i]; mutex_lock(&perph_data->state_trans_lock); @@ -398,7 +551,8 @@ static int bcl_read_ibat_high_trip(int *thresh_value) int8_t val = 0; *thresh_value = (int)val; - ret = bcl_read_register(BCL_IBAT_TRIP, &val); + ret = bcl_read_register((bcl_perph_version == BCL_PMI8994) ? + BCL_IBAT_TRIP : BCL_COBALT_IBAT_HIGH, &val); if (ret) { pr_err("BCL register read error. err:%d\n", ret); ret = 0; @@ -426,7 +580,9 @@ static int bcl_read_vbat_low_trip(int *thresh_value) int8_t val = 0; *thresh_value = (int)val; - ret = bcl_read_register(BCL_VBAT_TRIP, &val); + ret = bcl_read_register((bcl_perph_version == BCL_PMI8994) + ? BCL_VBAT_TRIP : BCL_COBALT_VBAT_ADC_LOW, + &val); if (ret) { pr_err("BCL register read error. err:%d\n", ret); ret = 0; @@ -451,7 +607,11 @@ static int bcl_clear_vbat_min(void) { int ret = 0; - ret = bcl_write_register(BCL_VBAT_MIN_CLR, BIT(7)); + if (bcl_perph_version == BCL_PMI8994) + ret = bcl_write_register(BCL_VBAT_MIN_CLR, BIT(7)); + else + ret = bcl_write_register(BCL_COBALT_MAX_MIN_CLR, + BIT(BCL_COBALT_VBAT_MIN_CLR)); if (ret) pr_err("Error in clearing vbat min reg. err:%d", ret); @@ -462,7 +622,11 @@ static int bcl_clear_ibat_max(void) { int ret = 0; - ret = bcl_write_register(BCL_IBAT_MAX_CLR, BIT(7)); + if (bcl_perph_version == BCL_PMI8994) + ret = bcl_write_register(BCL_IBAT_MAX_CLR, BIT(7)); + else + ret = bcl_write_register(BCL_COBALT_MAX_MIN_CLR, + BIT(BCL_COBALT_IBAT_MAX_CLR)); if (ret) pr_err("Error in clearing ibat max reg. err:%d", ret); @@ -476,7 +640,9 @@ static int bcl_read_ibat_max(int *adc_value) *adc_value = (int)val[VAL_REG_BUF_OFFSET]; do { - ret = bcl_read_multi_register(BCL_IBAT_MAX, val, + ret = bcl_read_multi_register( + (bcl_perph_version == BCL_PMI8994) ? BCL_IBAT_MAX + : BCL_COBALT_IBAT_MAX, val, VAL_CP_REG_BUF_LEN); if (ret) { pr_err("BCL register read error. err:%d\n", ret); @@ -505,7 +671,9 @@ static int bcl_read_vbat_min(int *adc_value) *adc_value = (int)val[VAL_REG_BUF_OFFSET]; do { - ret = bcl_read_multi_register(BCL_VBAT_MIN, val, + ret = bcl_read_multi_register( + (bcl_perph_version == BCL_PMI8994) ? BCL_VBAT_MIN + : BCL_COBALT_VBAT_MIN, val, VAL_CP_REG_BUF_LEN); if (ret) { pr_err("BCL register read error. err:%d\n", ret); @@ -534,7 +702,9 @@ static int bcl_read_ibat(int *adc_value) *adc_value = (int)val[VAL_REG_BUF_OFFSET]; do { - ret = bcl_read_multi_register(BCL_IBAT_VALUE, val, + ret = bcl_read_multi_register( + (bcl_perph_version == BCL_PMI8994) ? BCL_IBAT_VALUE + : BCL_COBALT_IBAT_VALUE, val, VAL_CP_REG_BUF_LEN); if (ret) { pr_err("BCL register read error. err:%d\n", ret); @@ -563,7 +733,9 @@ static int bcl_read_vbat(int *adc_value) *adc_value = (int)val[VAL_REG_BUF_OFFSET]; do { - ret = bcl_read_multi_register(BCL_VBAT_VALUE, val, + ret = bcl_read_multi_register( + (bcl_perph_version == BCL_PMI8994) ? BCL_VBAT_VALUE : + BCL_COBALT_VBAT_VALUE, val, VAL_CP_REG_BUF_LEN); if (ret) { pr_err("BCL register read error. err:%d\n", ret); @@ -812,40 +984,52 @@ static int bcl_get_devicetree_data(struct platform_device *pdev) } bcl_perph->param[BCL_PARAM_CURRENT].irq_num = irq_num; - /* Get VADC and IADC scaling factor */ - key = "qcom,vbat-scaling-factor"; - READ_CONV_FACTOR(dev_node, key, temp_val, ret, - bcl_perph->param[BCL_PARAM_VOLTAGE].scaling_factor); - key = "qcom,vbat-gain-numerator"; - READ_CONV_FACTOR(dev_node, key, temp_val, ret, - bcl_perph->param[BCL_PARAM_VOLTAGE].gain_factor_num); - key = "qcom,vbat-gain-denominator"; - READ_CONV_FACTOR(dev_node, key, temp_val, ret, - bcl_perph->param[BCL_PARAM_VOLTAGE].gain_factor_den); - key = "qcom,ibat-scaling-factor"; - READ_CONV_FACTOR(dev_node, key, temp_val, ret, - bcl_perph->param[BCL_PARAM_CURRENT].scaling_factor); - key = "qcom,ibat-offset-numerator"; - READ_CONV_FACTOR(dev_node, key, temp_val, ret, - bcl_perph->param[BCL_PARAM_CURRENT].offset_factor_num); - key = "qcom,ibat-offset-denominator"; - READ_CONV_FACTOR(dev_node, key, temp_val, ret, - bcl_perph->param[BCL_PARAM_CURRENT].offset_factor_den); - key = "qcom,ibat-gain-numerator"; - READ_CONV_FACTOR(dev_node, key, temp_val, ret, - bcl_perph->param[BCL_PARAM_CURRENT].gain_factor_num); - key = "qcom,ibat-gain-denominator"; - READ_CONV_FACTOR(dev_node, key, temp_val, ret, - bcl_perph->param[BCL_PARAM_CURRENT].gain_factor_den); + if (bcl_perph_version == BCL_PMI8994) { + /* Get VADC and IADC scaling factor */ + key = "qcom,vbat-scaling-factor"; + READ_CONV_FACTOR(dev_node, key, temp_val, ret, + bcl_perph->param[BCL_PARAM_VOLTAGE].scaling_factor); + key = "qcom,vbat-gain-numerator"; + READ_CONV_FACTOR(dev_node, key, temp_val, ret, + bcl_perph->param[BCL_PARAM_VOLTAGE].gain_factor_num); + key = "qcom,vbat-gain-denominator"; + READ_CONV_FACTOR(dev_node, key, temp_val, ret, + bcl_perph->param[BCL_PARAM_VOLTAGE].gain_factor_den); + key = "qcom,ibat-scaling-factor"; + READ_CONV_FACTOR(dev_node, key, temp_val, ret, + bcl_perph->param[BCL_PARAM_CURRENT].scaling_factor); + key = "qcom,ibat-offset-numerator"; + READ_CONV_FACTOR(dev_node, key, temp_val, ret, + bcl_perph->param[BCL_PARAM_CURRENT].offset_factor_num); + key = "qcom,ibat-offset-denominator"; + READ_CONV_FACTOR(dev_node, key, temp_val, ret, + bcl_perph->param[BCL_PARAM_CURRENT].offset_factor_den); + key = "qcom,ibat-gain-numerator"; + READ_CONV_FACTOR(dev_node, key, temp_val, ret, + bcl_perph->param[BCL_PARAM_CURRENT].gain_factor_num); + key = "qcom,ibat-gain-denominator"; + READ_CONV_FACTOR(dev_node, key, temp_val, ret, + bcl_perph->param[BCL_PARAM_CURRENT].gain_factor_den); + key = "qcom,inhibit-derating-ua"; + READ_OPTIONAL_PROP(dev_node, key, temp_val, ret, + bcl_perph->param[BCL_PARAM_CURRENT]. + inhibit_derating_ua); + } else { + prop = of_get_address_by_name(dev_node, + "fg_lmh", 0, 0); + if (prop) { + bcl_perph->fg_lmh_addr = be32_to_cpu(*prop); + pr_debug("fg_lmh@%04x\n", bcl_perph->fg_lmh_addr); + } else { + return -ENODEV; + } + } key = "qcom,vbat-polling-delay-ms"; READ_CONV_FACTOR(dev_node, key, temp_val, ret, bcl_perph->param[BCL_PARAM_VOLTAGE].polling_delay_ms); key = "qcom,ibat-polling-delay-ms"; READ_CONV_FACTOR(dev_node, key, temp_val, ret, bcl_perph->param[BCL_PARAM_CURRENT].polling_delay_ms); - key = "qcom,inhibit-derating-ua"; - READ_OPTIONAL_PROP(dev_node, key, temp_val, ret, - bcl_perph->param[BCL_PARAM_CURRENT].inhibit_derating_ua); bcl_dev_exit: return ret; @@ -1022,27 +1206,37 @@ static int bcl_probe(struct platform_device *pdev) pr_err("Device tree data fetch error. err:%d", ret); goto bcl_probe_exit; } - ret = bcl_calibrate(); - if (ret) { - pr_debug("Could not read calibration values. err:%d", ret); - goto bcl_probe_exit; - } - bcl_psy_d.name = bcl_psy_name; - bcl_psy_d.type = POWER_SUPPLY_TYPE_BMS; - bcl_psy_d.get_property = bcl_psy_get_property; - bcl_psy_d.set_property = bcl_psy_set_property; - bcl_psy_d.num_properties = 0; - bcl_psy_d.external_power_changed = power_supply_callback; - - bcl_psy_cfg.num_supplicants = 0; - bcl_psy_cfg.drv_data = bcl_perph; - - bcl_psy = devm_power_supply_register(&pdev->dev, &bcl_psy_d, - &bcl_psy_cfg); - if (IS_ERR(bcl_psy)) { - pr_err("Unable to register bcl_psy rc = %ld\n", - PTR_ERR(bcl_psy)); - return ret; + if (bcl_perph_version == BCL_PMI8994) { + ret = bcl_calibrate(); + if (ret) { + pr_debug("Could not read calibration values. err:%d", + ret); + goto bcl_probe_exit; + } + bcl_psy_d.name = bcl_psy_name; + bcl_psy_d.type = POWER_SUPPLY_TYPE_BMS; + bcl_psy_d.get_property = bcl_psy_get_property; + bcl_psy_d.set_property = bcl_psy_set_property; + bcl_psy_d.num_properties = 0; + bcl_psy_d.external_power_changed = power_supply_callback; + + bcl_psy_cfg.num_supplicants = 0; + bcl_psy_cfg.drv_data = bcl_perph; + + bcl_psy = devm_power_supply_register(&pdev->dev, &bcl_psy_d, + &bcl_psy_cfg); + if (IS_ERR(bcl_psy)) { + pr_err("Unable to register bcl_psy rc = %ld\n", + PTR_ERR(bcl_psy)); + return ret; + } + } else { + bcl_write_register(BCL_COBALT_LMH_CFG, BCL_LMH_CFG_VAL); + bcl_write_register(BCL_COBALT_BCL_CFG, BCL_CFG_VAL); + bcl_write_general_register(LMH_COBALT_INT_POL_HIGH, + bcl_perph->fg_lmh_addr, LMH_INT_VAL); + bcl_write_general_register(LMH_COBALT_INT_EN, + bcl_perph->fg_lmh_addr, LMH_INT_VAL); } ret = bcl_update_data(); @@ -1122,7 +1316,11 @@ static int bcl_remove(struct platform_device *pdev) } static struct of_device_id bcl_match[] = { - { .compatible = "qcom,msm-bcl", + { .compatible = "qcom,msm-bcl", + .data = (void *) BCL_PMI8994, + }, + { .compatible = "qcom,msm-bcl-lmh", + .data = (void *) BCL_PMICOBALT, }, {}, }; @@ -1139,7 +1337,22 @@ static struct platform_driver bcl_driver = { static int __init bcl_perph_init(void) { - pr_info("BCL Initialized\n"); + struct device_node *comp_node; + + comp_node = of_find_matching_node(NULL, bcl_match); + bcl_perph_version = BCL_PMI8994; + if (comp_node) { + const struct of_device_id *match = of_match_node(bcl_match, + comp_node); + if (!match) { + pr_err("Couldnt find a match\n"); + goto plt_register; + } + bcl_perph_version = (enum bcl_hw_type)match->data; + of_node_put(comp_node); + } + +plt_register: return platform_driver_register(&bcl_driver); } @@ -1150,4 +1363,3 @@ static void __exit bcl_perph_exit(void) fs_initcall(bcl_perph_init); module_exit(bcl_perph_exit); MODULE_ALIAS("platform:" BCL_DRIVER_NAME); - diff --git a/drivers/regulator/cpr3-mmss-regulator.c b/drivers/regulator/cpr3-mmss-regulator.c index c80b6b703e26..e5055708a871 100644 --- a/drivers/regulator/cpr3-mmss-regulator.c +++ b/drivers/regulator/cpr3-mmss-regulator.c @@ -208,6 +208,7 @@ msmcobalt_rev0_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = { #define MSM8996_MMSS_FUSE_STEP_VOLT 10000 #define MSM8996_MMSS_OFFSET_FUSE_STEP_VOLT 10000 #define MSM8996_MMSS_VOLTAGE_FUSE_SIZE 5 +#define MSM8996_MMSS_MIN_VOLTAGE_FUSE_VAL 0x1F #define MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SCALE 2 #define MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SIZE 6 @@ -230,6 +231,18 @@ msmcobalt_rev0_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = { #define MSMCOBALT_MMSS_TEMP_SENSOR_ID_START 12 #define MSMCOBALT_MMSS_TEMP_SENSOR_ID_END 13 +/* + * Some initial msmcobalt parts cannot be operated at low voltages. The + * open-loop voltage fuses are reused to identify these parts so that software + * can properly handle the limitation. 0xF means that the next higher fuse + * corner should be used. 0xE means that the next higher fuse corner which + * does not have a voltage limitation should be used. + */ +enum msmcobalt_cpr_partial_binning { + MSMCOBALT_CPR_PARTIAL_BINNING_NEXT_CORNER = 0xF, + MSMCOBALT_CPR_PARTIAL_BINNING_SAFE_CORNER = 0xE, +}; + /** * cpr3_msm8996_mmss_read_fuse_data() - load MMSS specific fuse parameter values * @vreg: Pointer to the CPR3 regulator @@ -685,9 +698,11 @@ static int cpr3_msm8996_mmss_calculate_open_loop_voltages( { struct device_node *node = vreg->of_node; struct cpr3_msm8996_mmss_fuses *fuse = vreg->platform_fuses; + bool is_msmcobalt + = (vreg->thread->ctrl->soc_revision == MSMCOBALT_SOC_ID); int rc = 0; bool allow_interpolation; - u64 freq_low, volt_low, freq_high, volt_high; + u64 freq_low, volt_low, freq_high, volt_high, volt_init; int i, j; const int *ref_volt; int *fuse_volt; @@ -713,8 +728,21 @@ static int cpr3_msm8996_mmss_calculate_open_loop_voltages( ref_volt = msm8996_mmss_fuse_ref_volt; for (i = 0; i < vreg->fuse_corner_count; i++) { + volt_init = fuse->init_voltage[i]; + /* + * Handle partial binning on MSMCOBALT where the initial voltage + * fuse is reused as a flag for partial binning needs. Set the + * open-loop voltage to the minimum possible value so that it + * does not result in higher fuse corners getting forced to + * higher open-loop voltages after monotonicity enforcement. + */ + if (is_msmcobalt && + (volt_init == MSMCOBALT_CPR_PARTIAL_BINNING_NEXT_CORNER || + volt_init == MSMCOBALT_CPR_PARTIAL_BINNING_SAFE_CORNER)) + volt_init = MSM8996_MMSS_MIN_VOLTAGE_FUSE_VAL; + fuse_volt[i] = cpr3_convert_open_loop_voltage_fuse(ref_volt[i], - MSM8996_MMSS_FUSE_STEP_VOLT, fuse->init_voltage[i], + MSM8996_MMSS_FUSE_STEP_VOLT, volt_init, MSM8996_MMSS_VOLTAGE_FUSE_SIZE); cpr3_info(vreg, "fuse_corner[%d] open-loop=%7d uV\n", i, fuse_volt[i]); @@ -803,6 +831,75 @@ done: } /** + * cpr3_msmcobalt_partial_binning_override() - override the voltage and quotient + * settings for low corners based upon the special partial binning + * open-loop voltage fuse values + * @vreg: Pointer to the CPR3 regulator + * + * Some parts are not able to operate at low voltages. The partial binning + * open-loop voltage fuse values specify if a given part has such limitations. + * + * Return: 0 on success, errno on failure + */ +static int cpr3_msmcobalt_partial_binning_override(struct cpr3_regulator *vreg) +{ + struct cpr3_msm8996_mmss_fuses *fuse = vreg->platform_fuses; + u64 next = MSMCOBALT_CPR_PARTIAL_BINNING_NEXT_CORNER; + u64 safe = MSMCOBALT_CPR_PARTIAL_BINNING_SAFE_CORNER; + u32 proc_freq; + struct cpr3_corner *corner; + struct cpr3_corner *safe_corner; + int i, j, low, high, safe_fuse_corner; + + if (vreg->thread->ctrl->soc_revision != MSMCOBALT_SOC_ID) + return 0; + + /* Loop over all fuse corners except for the highest one. */ + for (i = 0; i < vreg->fuse_corner_count - 1; i++) { + /* Determine which higher corners to override with (if any). */ + if (fuse->init_voltage[i] != next + && fuse->init_voltage[i] != safe) + continue; + + for (j = i + 1; j < vreg->fuse_corner_count - 1; j++) + if (fuse->init_voltage[j] != next + && fuse->init_voltage[j] != safe) + break; + safe_fuse_corner = j; + + j = fuse->init_voltage[i] == next ? i + 1 : safe_fuse_corner; + + low = i > 0 ? vreg->fuse_corner_map[i] : 0; + high = vreg->fuse_corner_map[i + 1] - 1; + + cpr3_info(vreg, "overriding CPR parameters for corners %d to %d with quotients of corner %d and voltages of corner %d\n", + low, high, vreg->fuse_corner_map[j], + vreg->fuse_corner_map[safe_fuse_corner]); + + corner = &vreg->corner[vreg->fuse_corner_map[j]]; + safe_corner + = &vreg->corner[vreg->fuse_corner_map[safe_fuse_corner]]; + + for (j = low; j <= high; j++) { + proc_freq = vreg->corner[j].proc_freq; + vreg->corner[j] = *corner; + vreg->corner[j].proc_freq = proc_freq; + + vreg->corner[j].floor_volt + = safe_corner->floor_volt; + vreg->corner[j].ceiling_volt + = safe_corner->ceiling_volt; + vreg->corner[j].open_loop_volt + = safe_corner->open_loop_volt; + vreg->corner[j].abs_ceiling_volt + = safe_corner->abs_ceiling_volt; + } + } + + return 0; +} + +/** * cpr3_mmss_print_settings() - print out MMSS CPR configuration settings into * the kernel log for debugging purposes * @vreg: Pointer to the CPR3 regulator @@ -973,6 +1070,13 @@ static int cpr3_mmss_init_thread(struct cpr3_thread *thread) } } + rc = cpr3_msmcobalt_partial_binning_override(vreg); + if (rc) { + cpr3_err(vreg, "unable to override CPR parameters based on partial binning fuse values, rc=%d\n", + rc); + return rc; + } + cpr3_mmss_print_settings(vreg); return 0; diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c index c93b71c4bc37..931227b476ac 100644 --- a/drivers/regulator/cpr3-regulator.c +++ b/drivers/regulator/cpr3-regulator.c @@ -6040,6 +6040,9 @@ int cpr3_regulator_unregister(struct cpr3_controller *ctrl) cpr3_regulator_debugfs_ctrl_remove(ctrl); mutex_unlock(&cpr3_controller_list_mutex); + if (ctrl->irq && !cpumask_empty(&ctrl->irq_affinity_mask)) + unregister_hotcpu_notifier(&ctrl->cpu_hotplug_notifier); + if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) rc = cpr3_ctrl_clear_cpr4_config(ctrl); if (rc) diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c index 53c137ac723f..2769d08b3056 100644 --- a/drivers/slimbus/slim-msm-ngd.c +++ b/drivers/slimbus/slim-msm-ngd.c @@ -1062,6 +1062,7 @@ static int ngd_get_laddr(struct slim_controller *ctrl, const u8 *ea, static void ngd_slim_setup(struct msm_slim_ctrl *dev) { + u32 new_cfg = NGD_CFG_ENABLE; u32 cfg = readl_relaxed(dev->base + NGD_BASE(dev->ctrl.nr, dev->ver)); if (dev->state == MSM_CTRL_DOWN) { @@ -1080,11 +1081,9 @@ 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 (cfg & NGD_CFG_RX_MSGQ_EN) { - SLIM_WARN(dev, "RX msgq status HW:0x%x, SW:%d:", cfg, - dev->use_rx_msgqs); + if ((dev->use_rx_msgqs == MSM_MSGQ_ENABLED) && + (cfg & NGD_CFG_RX_MSGQ_EN)) goto setup_tx_msg_path; - } if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED) msm_slim_disconnect_endp(dev, &dev->rx_msgq, @@ -1094,11 +1093,9 @@ static void ngd_slim_setup(struct msm_slim_ctrl *dev) setup_tx_msg_path: if (dev->use_tx_msgqs == MSM_MSGQ_DISABLED) goto ngd_enable; - if (cfg & NGD_CFG_TX_MSGQ_EN) { - SLIM_WARN(dev, "TX msgq status HW:0x%x, SW:%d:", cfg, - dev->use_tx_msgqs); + if (dev->use_tx_msgqs == MSM_MSGQ_ENABLED && + cfg & NGD_CFG_TX_MSGQ_EN) goto ngd_enable; - } if (dev->use_tx_msgqs == MSM_MSGQ_ENABLED) msm_slim_disconnect_endp(dev, &dev->tx_msgq, @@ -1106,19 +1103,19 @@ setup_tx_msg_path: msm_slim_connect_endp(dev, &dev->tx_msgq); } ngd_enable: + if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED) - cfg |= NGD_CFG_RX_MSGQ_EN; + new_cfg |= NGD_CFG_RX_MSGQ_EN; if (dev->use_tx_msgqs == MSM_MSGQ_ENABLED) - cfg |= NGD_CFG_TX_MSGQ_EN; + new_cfg |= NGD_CFG_TX_MSGQ_EN; - /* Enable NGD if it's not already enabled*/ - if (!(cfg & NGD_CFG_ENABLE)) - cfg |= NGD_CFG_ENABLE; + /* Enable NGD, and program MSGQs if not already */ + if (cfg == new_cfg) + return; - writel_relaxed(cfg, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver)); + writel_relaxed(new_cfg, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver)); /* make sure NGD MSG-Q config goes through */ mb(); - } static void ngd_slim_rx(struct msm_slim_ctrl *dev, u8 *buf) @@ -1196,7 +1193,7 @@ static int ngd_slim_power_up(struct msm_slim_ctrl *dev, bool mdm_restart) u32 ngd_int = (NGD_INT_TX_NACKED_2 | NGD_INT_MSG_BUF_CONTE | NGD_INT_MSG_TX_INVAL | NGD_INT_IE_VE_CHG | NGD_INT_DEV_ERR | - NGD_INT_TX_MSG_SENT | NGD_INT_RX_MSG_RCVD); + NGD_INT_TX_MSG_SENT); if (!mdm_restart && cur_state == MSM_CTRL_DOWN) { int timeout = wait_for_completion_timeout(&dev->qmi.qmi_comp, @@ -1225,6 +1222,8 @@ static int ngd_slim_power_up(struct msm_slim_ctrl *dev, bool mdm_restart) ngd = dev->base + NGD_BASE(dev->ctrl.nr, dev->ver); laddr = readl_relaxed(ngd + NGD_STATUS); if (laddr & NGD_LADDR) { + u32 int_en = readl_relaxed(ngd + NGD_INT_EN); + /* * external MDM restart case where ADSP itself was active framer * For example, modem restarted when playback was active @@ -1236,6 +1235,29 @@ static int ngd_slim_power_up(struct msm_slim_ctrl *dev, bool mdm_restart) /* * ADSP power collapse case, where HW wasn't reset. */ + if (int_en != 0) + return 0; + + /* Retention */ + if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED) + msm_slim_disconnect_endp(dev, &dev->rx_msgq, + &dev->use_rx_msgqs); + if (dev->use_tx_msgqs == MSM_MSGQ_ENABLED) + msm_slim_disconnect_endp(dev, &dev->tx_msgq, + &dev->use_tx_msgqs); + + writel_relaxed(ngd_int, (dev->base + NGD_INT_EN + + NGD_BASE(dev->ctrl.nr, dev->ver))); + + rx_msgq = readl_relaxed(ngd + NGD_RX_MSGQ_CFG); + /** + * Program with minimum value so that signal get + * triggered immediately after receiving the message + */ + writel_relaxed((rx_msgq | SLIM_RX_MSGQ_TIMEOUT_VAL), + (ngd + NGD_RX_MSGQ_CFG)); + /* reconnect BAM pipes if needed and enable NGD */ + ngd_slim_setup(dev); return 0; } diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c index a01a226be8b5..0819395a5e65 100644 --- a/drivers/slimbus/slimbus.c +++ b/drivers/slimbus/slimbus.c @@ -62,6 +62,14 @@ static const struct slim_device_id *slim_match(const struct slim_device_id *id, return NULL; } +const struct slim_device_id *slim_get_device_id(const struct slim_device *sdev) +{ + const struct slim_driver *sdrv = to_slim_driver(sdev->dev.driver); + + return slim_match(sdrv->id_table, sdev); +} +EXPORT_SYMBOL(slim_get_device_id); + static int slim_device_match(struct device *dev, struct device_driver *driver) { struct slim_device *slim_dev; diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 75a5327b28a5..68cc30b5b4df 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -21,6 +21,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/platform_device.h> +#include <linux/regulator/consumer.h> #include <linux/interrupt.h> #include <linux/sched.h> #include <linux/dma-mapping.h> @@ -49,11 +50,14 @@ struct icnss_qmi_event { #define ICNSS_WLFW_QMI_CONNECTED BIT(0) #define ICNSS_FW_READY BIT(1) #define SMMU_CLOCK_NAME "smmu_aggre2_noc_clk" +#define MAX_PROP_SIZE 32 +#define MAX_VOLTAGE_LEVEL 2 +#define VREG_ON 1 +#define VREG_OFF 0 #define ICNSS_IS_WLFW_QMI_CONNECTED(_state) \ ((_state) & ICNSS_WLFW_QMI_CONNECTED) #define ICNSS_IS_FW_READY(_state) ((_state) & ICNSS_FW_READY) - #ifdef ICNSS_PANIC #define ICNSS_ASSERT(_condition) do { \ if (!(_condition)) { \ @@ -79,10 +83,19 @@ struct ce_irq_list { irqreturn_t (*handler)(int, void *); }; -static struct { +struct icnss_vreg_info { + struct regulator *reg; + const char *name; + u32 nominal_min; + u32 max_voltage; + bool state; +}; + +static struct icnss_data { struct platform_device *pdev; struct icnss_driver_ops *ops; struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS]; + struct icnss_vreg_info vreg_info; u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS]; phys_addr_t mem_base_pa; void __iomem *mem_base_va; @@ -174,11 +187,99 @@ out: return ret; } +static int icnss_vreg_on(struct icnss_vreg_info *vreg_info) +{ + int ret = 0; + + if (!vreg_info->reg) { + pr_err("%s: regulator is not initialized\n", __func__); + return -ENOENT; + } + + if (!vreg_info->max_voltage || !vreg_info->nominal_min) { + pr_err("%s: %s invalid constraints specified\n", + __func__, vreg_info->name); + return -EINVAL; + } + + ret = regulator_set_voltage(vreg_info->reg, + vreg_info->nominal_min, vreg_info->max_voltage); + if (ret < 0) { + pr_err("%s: regulator_set_voltage failed for (%s). min_uV=%d,max_uV=%d,ret=%d\n", + __func__, vreg_info->name, + vreg_info->nominal_min, + vreg_info->max_voltage, ret); + return ret; + } + + ret = regulator_enable(vreg_info->reg); + if (ret < 0) { + pr_err("%s: Fail to enable regulator (%s) ret=%d\n", + __func__, vreg_info->name, ret); + } + return ret; +} + +static int icnss_vreg_off(struct icnss_vreg_info *vreg_info) +{ + int ret = 0; + int min_uV = 0; + + if (!vreg_info->reg) { + pr_err("%s: regulator is not initialized\n", __func__); + return -ENOENT; + } + + ret = regulator_disable(vreg_info->reg); + if (ret < 0) { + pr_err("%s: Fail to disable regulator (%s) ret=%d\n", + __func__, vreg_info->name, ret); + return ret; + } + + ret = regulator_set_voltage(vreg_info->reg, + min_uV, vreg_info->max_voltage); + if (ret < 0) { + pr_err("%s: regulator_set_voltage failed for (%s). min_uV=%d,max_uV=%d,ret=%d\n", + __func__, vreg_info->name, min_uV, + vreg_info->max_voltage, ret); + } + return ret; +} + +static int icnss_vreg_set(bool state) +{ + int ret = 0; + struct icnss_vreg_info *vreg_info = &penv->vreg_info; + + if (vreg_info->state == state) { + pr_debug("Already %s state is %s\n", vreg_info->name, + state ? "enabled" : "disabled"); + return ret; + } + + if (state) + ret = icnss_vreg_on(vreg_info); + else + ret = icnss_vreg_off(vreg_info); + + if (ret < 0) + goto out; + + pr_debug("%s: %s is now %s\n", __func__, vreg_info->name, + state ? "enabled" : "disabled"); -static void icnss_adrastea_reset(void) + vreg_info->state = state; +out: + return ret; +} + +static void icnss_hw_release_reset(struct icnss_data *pdata) { uint32_t rdata = 0; + pr_debug("%s\n", __func__); + if (penv->mpm_config_va) { writel_relaxed(0x1, penv->mpm_config_va + @@ -189,19 +290,52 @@ static void icnss_adrastea_reset(void) } } -static int icnss_adrastea_power_on(void) +static void icnss_hw_reset(struct icnss_data *pdata) +{ + uint32_t rdata = 0; + + pr_debug("%s\n", __func__); + + if (penv->mpm_config_va) { + writel_relaxed(0x0, + penv->mpm_config_va + + MPM2_MPM_WCSSAON_CONFIG_OFFSET); + while (rdata != 0x0) + rdata = readl_relaxed(penv->mpm_config_va + + MPM2_MPM_WCSSAON_CONFIG_OFFSET); + } +} + +static int icnss_hw_power_on(struct icnss_data *pdata) { int ret = 0; - /* Top level adrastea reset */ - icnss_adrastea_reset(); + ret = icnss_vreg_set(VREG_ON); + if (ret < 0) { + pr_err("%s: Failed to turn on voltagre regulator: %d\n", + __func__, ret); + goto out; + } + icnss_hw_release_reset(pdata); +out: return ret; } -void icnss_adrastea_power_off(void) +static int icnss_hw_power_off(struct icnss_data *pdata) { - /* TZ API of power off adrastea */ + int ret = 0; + + icnss_hw_reset(pdata); + + ret = icnss_vreg_set(VREG_OFF); + if (ret < 0) { + pr_err("%s: Failed to turn off voltagre regulator: %d\n", + __func__, ret); + goto out; + } +out: + return ret; } static int wlfw_msa_mem_info_send_sync_msg(void) @@ -646,18 +780,15 @@ static int icnss_qmi_event_server_arrive(void *data) pr_info("%s: QMI Server Connected\n", __func__); - ret = icnss_adrastea_power_on(); - if (ret < 0) { - pr_err("%s: Failed to power on hardware: %d\n", - __func__, ret); + ret = icnss_hw_power_on(penv); + if (ret < 0) goto fail; - } ret = wlfw_ind_register_send_sync_msg(); if (ret < 0) { pr_err("%s: Failed to send indication message: %d\n", __func__, ret); - goto fail; + goto err_power_on; } if (penv->msa_va) { @@ -665,27 +796,30 @@ static int icnss_qmi_event_server_arrive(void *data) if (ret < 0) { pr_err("%s: Failed to send MSA info: %d\n", __func__, ret); - goto fail; + goto err_power_on; } ret = wlfw_msa_ready_send_sync_msg(); if (ret < 0) { pr_err("%s: Failed to send MSA ready : %d\n", __func__, ret); - goto fail; + goto err_power_on; } } else { pr_err("%s: Invalid MSA address\n", __func__); ret = -EINVAL; - goto fail; + goto err_power_on; } ret = wlfw_cap_send_sync_msg(); if (ret < 0) { pr_err("%s: Failed to get capability: %d\n", __func__, ret); - goto fail; + goto err_power_on; } return ret; + +err_power_on: + ret = icnss_hw_power_off(penv); fail: qmi_handle_destroy(penv->wlfw_clnt); penv->wlfw_clnt = NULL; @@ -726,7 +860,9 @@ static int icnss_qmi_event_fw_ready_ind(void *data) goto out; } - icnss_adrastea_power_off(); + ret = icnss_hw_power_off(penv); + if (ret < 0) + goto out; if (!penv->ops || !penv->ops->probe) goto out; @@ -830,12 +966,17 @@ int icnss_register_driver(struct icnss_driver_ops *ops) /* check for all conditions before invoking probe */ if (ICNSS_IS_FW_READY(penv->state) && penv->ops->probe) { + ret = icnss_hw_power_on(penv); + if (ret < 0) { + pr_err("%s: Failed to turn on voltagre regulator: %d\n", + __func__, ret); + goto out; + } ret = penv->ops->probe(&pdev->dev); } else { pr_err("icnss: FW is not ready\n"); ret = -ENOENT; } - out: return ret; } @@ -865,6 +1006,8 @@ int icnss_unregister_driver(struct icnss_driver_ops *ops) penv->ops->remove(&pdev->dev); penv->ops = NULL; + + ret = icnss_hw_power_off(penv); out: return ret; } @@ -1209,7 +1352,6 @@ static int icnss_clock_enable(struct clk *c) if (ret < 0) pr_err("%s: couldn't enable clock!\n", __func__); - return ret; } @@ -1287,6 +1429,79 @@ static void icnss_smmu_remove(struct device *dev) penv->smmu_mapping = NULL; } +static int icnss_dt_parse_vreg_info(struct device *dev, + struct icnss_vreg_info *vreg_info, + const char *vreg_name) +{ + int ret = 0; + u32 voltage_levels[MAX_VOLTAGE_LEVEL]; + char prop_name[MAX_PROP_SIZE]; + struct device_node *np = dev->of_node; + + snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", vreg_name); + if (!of_parse_phandle(np, prop_name, 0)) { + pr_err("%s: No vreg data found for %s\n", __func__, vreg_name); + ret = -EINVAL; + return ret; + } + + vreg_info->name = vreg_name; + + snprintf(prop_name, MAX_PROP_SIZE, + "qcom,%s-voltage-level", vreg_name); + ret = of_property_read_u32_array(np, prop_name, voltage_levels, + ARRAY_SIZE(voltage_levels)); + if (ret) { + pr_err("%s: error reading %s property\n", __func__, prop_name); + return ret; + } + + vreg_info->nominal_min = voltage_levels[0]; + vreg_info->max_voltage = voltage_levels[1]; + + return ret; +} + +static int icnss_get_resources(struct device *dev) +{ + int ret = 0; + struct icnss_vreg_info *vreg_info; + + vreg_info = &penv->vreg_info; + if (vreg_info->reg) { + pr_err("%s: %s regulator is already initialized\n", __func__, + vreg_info->name); + return ret; + } + + vreg_info->reg = devm_regulator_get(dev, vreg_info->name); + if (IS_ERR(vreg_info->reg)) { + ret = PTR_ERR(vreg_info->reg); + if (ret == -EPROBE_DEFER) { + pr_err("%s: %s probe deferred!\n", __func__, + vreg_info->name); + } else { + pr_err("%s: Get %s failed!\n", __func__, + vreg_info->name); + } + } + return ret; +} + +static int icnss_release_resources(void) +{ + int ret = 0; + struct icnss_vreg_info *vreg_info = &penv->vreg_info; + + if (!vreg_info->reg) { + pr_err("%s: regulator is not initialized\n", __func__); + return -ENOENT; + } + + devm_regulator_put(vreg_info->reg); + return ret; +} + static int icnss_probe(struct platform_device *pdev) { int ret = 0; @@ -1295,8 +1510,10 @@ static int icnss_probe(struct platform_device *pdev) int i; struct device *dev = &pdev->dev; - if (penv) + if (penv) { + pr_err("%s: penv is already initialized\n", __func__); return -EEXIST; + } penv = devm_kzalloc(&pdev->dev, sizeof(*penv), GFP_KERNEL); if (!penv) @@ -1304,18 +1521,30 @@ static int icnss_probe(struct platform_device *pdev) penv->pdev = pdev; + ret = icnss_dt_parse_vreg_info(dev, &penv->vreg_info, "vdd-io"); + if (ret < 0) { + pr_err("%s: failed parsing vdd io data\n", __func__); + goto out; + } + + ret = icnss_get_resources(dev); + if (ret < 0) { + pr_err("%s: Regulator setup failed (%d)\n", __func__, ret); + goto out; + } + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase"); if (!res) { - pr_err("icnss: Memory base not found\n"); + pr_err("%s: Memory base not found\n", __func__); ret = -EINVAL; - goto out; + goto release_regulator; } penv->mem_base_pa = res->start; penv->mem_base_va = ioremap(penv->mem_base_pa, resource_size(res)); if (!penv->mem_base_va) { pr_err("%s: mem_base ioremap failed\n", __func__); ret = -EINVAL; - goto out; + goto release_regulator; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, @@ -1336,7 +1565,7 @@ static int icnss_probe(struct platform_device *pdev) for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) { res = platform_get_resource(pdev, IORESOURCE_IRQ, i); if (!res) { - pr_err("icnss: Fail to get IRQ-%d\n", i); + pr_err("%s: Fail to get IRQ-%d\n", __func__, i); ret = -ENODEV; goto unmap_mpm_config; } else { @@ -1446,12 +1675,21 @@ unmap_mpm_config: unmap_mem_base: if (penv->mem_base_va) iounmap(penv->mem_base_va); +release_regulator: + ret = icnss_release_resources(); + if (ret < 0) + pr_err("%s: fail to release the platform resource\n", + __func__); out: + devm_kfree(&pdev->dev, penv); + penv = NULL; return ret; } static int icnss_remove(struct platform_device *pdev) { + int ret = 0; + qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01, WLFW_SERVICE_VERS_V01, WLFW_SERVICE_INS_ID_V01, @@ -1474,7 +1712,16 @@ static int icnss_remove(struct platform_device *pdev) if (penv->mem_base_va) iounmap(penv->mem_base_va); - return 0; + ret = icnss_hw_power_off(penv); + if (ret < 0) + pr_err("%s: Failed to turn off voltagre regulator: %d\n", + __func__, ret); + + ret = icnss_release_resources(); + if (ret < 0) + pr_err("%s: fail to release the platform resource\n", + __func__); + return ret; } diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c index 0a37c97b79d5..3f435f00e04d 100644 --- a/drivers/thermal/msm_thermal.c +++ b/drivers/thermal/msm_thermal.c @@ -973,7 +973,8 @@ static int msm_thermal_cpufreq_callback(struct notifier_block *nfb, switch (event) { case CPUFREQ_ADJUST: - max_freq_req = cpus[policy->cpu].parent_ptr->limited_max_freq; + max_freq_req = (lmh_dcvs_available) ? UINT_MAX : + cpus[policy->cpu].parent_ptr->limited_max_freq; min_freq_req = cpus[policy->cpu].parent_ptr->limited_min_freq; pr_debug("mitigating CPU%d to freq max: %u min: %u\n", policy->cpu, max_freq_req, min_freq_req); @@ -1094,7 +1095,7 @@ static void update_cpu_freq(int cpu, enum freq_limits changed) */ if (lmh_dcvs_available) { msm_lmh_dcvs_update(cpu); - if (changed | FREQ_LIMIT_MIN) + if (changed & FREQ_LIMIT_MIN) cpufreq_update_policy(cpu); } else { cpufreq_update_policy(cpu); diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 5dbe537a41ea..2b0299c293e2 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -1291,6 +1291,10 @@ static int dwc3_msm_gsi_ep_op(struct usb_ep *ep, f_suspend = *((bool *)op_data); ret = gsi_check_ready_to_suspend(ep, f_suspend); break; + case GSI_EP_OP_DISABLE: + dev_dbg(mdwc->dev, "EP_OP_DISABLE\n"); + ret = ep->ops->disable(ep); + break; default: dev_err(mdwc->dev, "%s: Invalid opcode GSI EP\n", __func__); } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 98df702b291d..f5554d7a8e00 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -664,9 +664,12 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) dep->type = 0; dep->flags = 0; - snprintf(dep->name, sizeof(dep->name), "ep%d%s", + /* Keep GSI ep names with "-gsi" suffix */ + if (!strnstr(dep->name, "gsi", 10)) { + snprintf(dep->name, sizeof(dep->name), "ep%d%s", dep->number >> 1, (dep->number & 1) ? "in" : "out"); + } /* * Clean up ep ring of non-control endpoint to avoid getting xferInProgress @@ -754,13 +757,6 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep) return 0; } - /* Keep GSI ep names with "-gsi" suffix */ - if (!strnstr(dep->name, "gsi", 10)) { - snprintf(dep->name, sizeof(dep->name), "ep%d%s", - dep->number >> 1, - (dep->number & 1) ? "in" : "out"); - } - spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_ep_disable(dep); dbg_event(dep->number, "DISABLE", ret); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 0bcb73bc8cb1..e2049464af11 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1912,6 +1912,16 @@ unknown: } break; } + + if (value < 0) { + DBG(cdev, "%s: unhandled os desc request\n", + __func__); + DBG(cdev, "req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + return value; + } + req->length = value; req->context = cdev; req->zero = value < w_length; @@ -2211,14 +2221,18 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev) usb_ep_dequeue(cdev->gadget->ep0, cdev->os_desc_req); kfree(cdev->os_desc_req->buf); + cdev->os_desc_req->buf = NULL; usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req); + cdev->os_desc_req = NULL; } if (cdev->req) { if (cdev->setup_pending) usb_ep_dequeue(cdev->gadget->ep0, cdev->req); kfree(cdev->req->buf); + cdev->req->buf = NULL; usb_ep_free_request(cdev->gadget->ep0, cdev->req); + cdev->req = NULL; } cdev->next_string_id = 0; device_remove_file(&cdev->gadget->dev, &dev_attr_suspended); diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index f0240525bca3..1cb5d945ba7e 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -432,10 +432,10 @@ static void ipa_disconnect_handler(struct gsi_data_port *d_port) usb_gsi_ep_op(d_port->in_ep, (void *)&block_db, GSI_EP_OP_SET_CLR_BLOCK_DBL); - usb_ep_disable(gsi->d_port.in_ep); + usb_gsi_ep_op(gsi->d_port.in_ep, NULL, GSI_EP_OP_DISABLE); if (gsi->d_port.out_ep) - usb_ep_disable(gsi->d_port.out_ep); + usb_gsi_ep_op(gsi->d_port.out_ep, NULL, GSI_EP_OP_DISABLE); gsi->d_port.net_ready_trigger = false; } @@ -2252,6 +2252,16 @@ fail: return -ENOMEM; } +static void ipa_ready_callback(void *user_data) +{ + struct f_gsi *gsi = user_data; + + log_event_info("%s: ipa is ready\n", __func__); + + gsi->d_port.ipa_ready = true; + wake_up_interruptible(&gsi->d_port.wait_for_ipa_ready); +} + static int gsi_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; @@ -2489,6 +2499,29 @@ static int gsi_bind(struct usb_configuration *c, struct usb_function *f) if (status) goto dereg_rndis; + status = ipa_register_ipa_ready_cb(ipa_ready_callback, gsi); + if (!status) { + log_event_info("%s: ipa is not ready", __func__); + status = wait_event_interruptible_timeout( + gsi->d_port.wait_for_ipa_ready, gsi->d_port.ipa_ready, + msecs_to_jiffies(GSI_IPA_READY_TIMEOUT)); + if (!status) { + log_event_err("%s: ipa ready timeout", __func__); + status = -ETIMEDOUT; + goto dereg_rndis; + } + } + + gsi->d_port.ipa_usb_notify_cb = ipa_usb_notify_cb; + status = ipa_usb_init_teth_prot(gsi->prot_id, + &gsi->d_port.ipa_init_params, gsi->d_port.ipa_usb_notify_cb, + gsi); + if (status) { + log_event_err("%s: failed to init teth prot %d", + __func__, gsi->prot_id); + goto dereg_rndis; + } + post_event(&gsi->d_port, EVT_INITIALIZED); queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w); @@ -2552,15 +2585,6 @@ static void gsi_unbind(struct usb_configuration *c, struct usb_function *f) gsi->d_port.in_request.dma); } -static void ipa_ready_callback(void *user_data) -{ - struct f_gsi *gsi = user_data; - - log_event_info("%s: ipa is ready\n", __func__); - - gsi->d_port.ipa_ready = true; - wake_up_interruptible(&gsi->d_port.wait_for_ipa_ready); -} static void gsi_free_func(struct usb_function *f) { @@ -2614,28 +2638,6 @@ int gsi_bind_config(struct f_gsi *gsi) INIT_WORK(&gsi->d_port.usb_ipa_w, ipa_work_handler); - status = ipa_register_ipa_ready_cb(ipa_ready_callback, gsi); - if (!status) { - log_event_info("%s: ipa is not ready", __func__); - status = wait_event_interruptible_timeout( - gsi->d_port.wait_for_ipa_ready, gsi->d_port.ipa_ready, - msecs_to_jiffies(GSI_IPA_READY_TIMEOUT)); - if (!status) { - log_event_err("%s: ipa ready timeout", __func__); - return -ETIMEDOUT; - } - } - - gsi->d_port.ipa_usb_notify_cb = ipa_usb_notify_cb; - status = ipa_usb_init_teth_prot(prot_id, - &gsi->d_port.ipa_init_params, gsi->d_port.ipa_usb_notify_cb, - gsi); - if (status) { - log_event_err("%s: failed to init teth prot %d", - __func__, prot_id); - return status; - } - return status; } diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index f0a2d2126f22..b0e7b65b84bd 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -538,14 +538,11 @@ static int gen_ndis_set_resp(struct rndis_params *params, u32 OID, */ retval = 0; if (*params->filter) { - params->state = RNDIS_DATA_INITIALIZED; - netif_carrier_on(params->dev); - if (netif_running(params->dev)) - netif_wake_queue(params->dev); + pr_debug("%s(): disable flow control\n", __func__); + rndis_flow_control(params, false); } else { - params->state = RNDIS_INITIALIZED; - netif_carrier_off(params->dev); - netif_stop_queue(params->dev); + pr_err("%s(): enable flow control\n", __func__); + rndis_flow_control(params, true); } break; diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c index 08d57e9de244..d1a516637506 100644 --- a/drivers/video/fbdev/msm/mdss_debug.c +++ b/drivers/video/fbdev/msm/mdss_debug.c @@ -1266,7 +1266,7 @@ static inline struct mdss_mdp_misr_map *mdss_misr_get_map(u32 block_id, char *ctrl_reg = NULL, *value_reg = NULL; char *intf_base = NULL; - if (block_id > DISPLAY_MISR_MDP) { + if (block_id > DISPLAY_MISR_HDMI && block_id != DISPLAY_MISR_MDP) { pr_err("MISR Block id (%d) out of range\n", block_id); return NULL; } @@ -1408,12 +1408,16 @@ int mdss_misr_set(struct mdss_data_type *mdata, bool is_valid_wb_mixer = true; bool use_mdp_up_misr = false; + if (!mdata || !req || !ctl) { + pr_err("Invalid input params: mdata = %p req = %p ctl = %p", + mdata, req, ctl); + return -EINVAL; + } pr_debug("req[block:%d frame:%d op_mode:%d]\n", req->block_id, req->frame_count, req->crc_op_mode); map = mdss_misr_get_map(req->block_id, ctl, mdata, ctl->is_video_mode); - if (!map) { pr_err("Invalid MISR Block=%d\n", req->block_id); return -EINVAL; diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index de65567f2844..de4c4c7c2762 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -649,8 +649,10 @@ static ssize_t mdss_dsi_cmd_state_write(struct file *file, return -ENOMEM; } - if (copy_from_user(input, p, count)) + if (copy_from_user(input, p, count)) { + kfree(input); return -EFAULT; + } input[count-1] = '\0'; if (strnstr(input, "dsi_hs_mode", strlen("dsi_hs_mode"))) diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index a64cdc161e55..707b1cbbd07b 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -354,14 +354,22 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) } if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { + bool out; + if ((pinfo->mode_sel_state == MODE_SEL_SINGLE_PORT) || (pinfo->mode_sel_state == MODE_GPIO_HIGH)) - gpio_set_value( - ctrl_pdata->lcd_mode_sel_gpio, 1); + out = true; else if ((pinfo->mode_sel_state == MODE_SEL_DUAL_PORT) || (pinfo->mode_sel_state == MODE_GPIO_LOW)) - gpio_set_value( - ctrl_pdata->lcd_mode_sel_gpio, 0); + out = false; + + rc = gpio_direction_output( + ctrl_pdata->lcd_mode_sel_gpio, out); + if (rc) { + pr_err("%s: unable to set dir for mode gpio\n", + __func__); + goto exit; + } } if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) { diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index 15d85b40388f..d66a578dd1ed 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -1948,7 +1948,9 @@ void mdss_fb_free_fb_ion_memory(struct msm_fb_data_type *mfd) ion_unmap_kernel(mfd->fb_ion_client, mfd->fb_ion_handle); - if (mfd->mdp.fb_mem_get_iommu_domain) { + if (mfd->mdp.fb_mem_get_iommu_domain && !(!mfd->fb_attachment || + !mfd->fb_attachment->dmabuf || + !mfd->fb_attachment->dmabuf->ops)) { dma_buf_unmap_attachment(mfd->fb_attachment, mfd->fb_table, DMA_BIDIRECTIONAL); dma_buf_detach(mfd->fbmem_buf, mfd->fb_attachment); diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c index 6b37100a78bf..68ab530f3583 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c @@ -1597,7 +1597,7 @@ int mdss_mdp_qseed3_setup(struct mdss_mdp_pipe *pipe, struct mdss_data_type *mdata; char __iomem *offset, *lut_offset; struct mdss_mdp_format_params *fmt; - uint32_t op_mode; + uint32_t op_mode = 0; uint32_t phase_init, preload, src_y_rgb, src_uv, dst; mdata = mdss_mdp_get_mdata(); @@ -1632,8 +1632,6 @@ int mdss_mdp_qseed3_setup(struct mdss_mdp_pipe *pipe, } pr_debug("scaler->enable=%d", scaler->enable); - op_mode = readl_relaxed(MDSS_MDP_REG_SCALER_OP_MODE + - offset); if (scaler->enable) { op_mode |= SCALER_EN; @@ -1661,7 +1659,7 @@ int mdss_mdp_qseed3_setup(struct mdss_mdp_pipe *pipe, SCALER_BLEND_CFG; op_mode |= (scaler->enable & ENABLE_DIRECTION_DETECTION) ? - (1 << SCALER_DIR_EN) : 0; + SCALER_DIR_EN : 0; phase_init = ((scaler->init_phase_x[0] & PHASE_BITS) << Y_PHASE_INIT_H) | @@ -7170,6 +7168,8 @@ int mdss_mdp_copy_layer_pp_info(struct mdp_input_layer *layer) pr_err("Failed to copy IGC payload, ret = %d\n", ret); goto exit_pp_info; } + } else { + pp_info->igc_cfg.cfg_payload = NULL; } if (ops & MDP_OVERLAY_PP_HIST_LUT_CFG) { ret = pp_copy_layer_hist_lut_payload(pp_info); @@ -7178,6 +7178,8 @@ int mdss_mdp_copy_layer_pp_info(struct mdp_input_layer *layer) ret); goto exit_igc; } + } else { + pp_info->hist_lut_cfg.cfg_payload = NULL; } if (ops & MDP_OVERLAY_PP_PA_V2_CFG) { ret = pp_copy_layer_pa_payload(pp_info); @@ -7185,6 +7187,8 @@ int mdss_mdp_copy_layer_pp_info(struct mdp_input_layer *layer) pr_err("Failed to copy PA payload, ret = %d\n", ret); goto exit_hist_lut; } + } else { + pp_info->pa_v2_cfg_data.cfg_payload = NULL; } if (ops & MDP_OVERLAY_PP_PCC_CFG) { ret = pp_copy_layer_pcc_payload(pp_info); @@ -7192,6 +7196,8 @@ int mdss_mdp_copy_layer_pp_info(struct mdp_input_layer *layer) pr_err("Failed to copy PCC payload, ret = %d\n", ret); goto exit_pa; } + } else { + pp_info->pcc_cfg_data.cfg_payload = NULL; } layer->pp_info = pp_info; diff --git a/drivers/video/fbdev/msm/mdss_panel.c b/drivers/video/fbdev/msm/mdss_panel.c index d8fed98a9e46..aab8c9c63451 100644 --- a/drivers/video/fbdev/msm/mdss_panel.c +++ b/drivers/video/fbdev/msm/mdss_panel.c @@ -228,6 +228,7 @@ struct dentry *panel_debugfs_create_array(const char *name, umode_t mode, (size != sizeof(u16)) && (size != sizeof(u32))) { pr_warn("Value size %zu bytes is not supported\n", size); + kfree(data); return NULL; } diff --git a/include/linux/leds-qpnp-flash-v2.h b/include/linux/leds-qpnp-flash-v2.h new file mode 100644 index 000000000000..353466f6c108 --- /dev/null +++ b/include/linux/leds-qpnp-flash-v2.h @@ -0,0 +1,44 @@ +/* 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. 1 + */ + +#ifndef __LEDS_QPNP_FLASH_V2_H +#define __LEDS_QPNP_FLASH_V2_H + +#include <linux/leds.h> +#include "leds.h" + +/* + * Configurations for each individual LED + */ +struct flash_node_data { + struct platform_device *pdev; + struct led_classdev cdev; + struct pinctrl *pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; + int ires_ua; + u16 prgm_current; + u8 duration; + u8 id; + u8 type; + u8 ires; + u8 hdrm_val; + u8 brightness; + bool led_on; +}; + +struct flash_switch_data { + struct platform_device *pdev; + struct led_classdev cdev; +}; + +#endif diff --git a/include/linux/slimbus/slimbus.h b/include/linux/slimbus/slimbus.h index a2092f062833..05fb62e45b52 100644 --- a/include/linux/slimbus/slimbus.h +++ b/include/linux/slimbus/slimbus.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 @@ -1173,6 +1173,9 @@ extern struct slim_controller *slim_busnum_to_ctrl(u32 busnum); */ extern void slim_ctrl_add_boarddevs(struct slim_controller *ctrl); +extern const +struct slim_device_id *slim_get_device_id(const struct slim_device *sdev); + /* * slim_register_board_info: Board-initialization routine. * @info: List of all devices on all controllers present on the board. diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index ab06d2988505..2c64620254eb 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -48,6 +48,7 @@ enum gsi_ep_op { GSI_EP_OP_FREE_TRBS, GSI_EP_OP_SET_CLR_BLOCK_DBL, GSI_EP_OP_CHECK_FOR_SUSPEND, + GSI_EP_OP_DISABLE, }; /* diff --git a/include/uapi/linux/msm_kgsl.h b/include/uapi/linux/msm_kgsl.h index 7def4b8f1dce..34503420c882 100644 --- a/include/uapi/linux/msm_kgsl.h +++ b/include/uapi/linux/msm_kgsl.h @@ -307,6 +307,7 @@ enum kgsl_timestamp_type { #define KGSL_PROP_UCODE_VERSION 0x15 #define KGSL_PROP_GPMU_VERSION 0x16 #define KGSL_PROP_HIGHEST_BANK_BIT 0x17 +#define KGSL_PROP_DEVICE_BITNESS 0x18 struct kgsl_shadowprop { unsigned long gpuaddr; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 19defa060105..c9e54acb06ec 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -303,7 +303,7 @@ static void soc_init_component_debugfs(struct snd_soc_component *component) } if (!component->debugfs_root) { - dev_warn(component->dev, + dev_dbg(component->dev, "ASoC: Failed to create component debugfs directory\n"); return; } @@ -328,7 +328,7 @@ static void soc_init_codec_debugfs(struct snd_soc_component *component) codec->component.debugfs_root, codec, &codec_reg_fops); if (!codec->debugfs_reg) - dev_warn(codec->dev, + dev_dbg(codec->dev, "ASoC: Failed to create codec register debugfs file\n"); } |
