summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/msm/bcl.txt220
-rw-r--r--Documentation/devicetree/bindings/cnss/icnss.txt6
-rw-r--r--Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt153
-rw-r--r--arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi15
-rw-r--r--arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi15
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-cdp.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-mdss.dtsi3
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-mtp.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-audio.dtsi10
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-bus.dtsi49
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-cdp.dtsi78
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-camera-sensor-mtp.dtsi78
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-cdp.dtsi6
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mdss.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi12
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt-vidc.dtsi77
-rw-r--r--arch/arm/boot/dts/qcom/msmcobalt.dtsi21
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig7
-rw-r--r--arch/arm64/configs/msmcortex_defconfig21
-rw-r--r--drivers/base/regmap/internal.h3
-rw-r--r--drivers/base/regmap/regmap-debugfs.c69
-rw-r--r--drivers/clk/msm/clock-gcc-cobalt.c2
-rw-r--r--drivers/clk/msm/clock-osm.c11
-rw-r--r--drivers/gpu/msm/adreno.c20
-rw-r--r--drivers/gpu/msm/adreno_a5xx.c1
-rw-r--r--drivers/gpu/msm/adreno_a5xx_snapshot.c435
-rw-r--r--drivers/gpu/msm/kgsl_cmdbatch.c39
-rw-r--r--drivers/gpu/msm/kgsl_pool.c144
-rw-r--r--drivers/gpu/msm/kgsl_pool.h4
-rw-r--r--drivers/gpu/msm/kgsl_sharedmem.c85
-rw-r--r--drivers/leds/Kconfig10
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-qpnp-flash-v2.c639
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c2
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_util.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-compat-ioctl32.c19
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_dp.c17
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c4
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_dp.c18
-rw-r--r--drivers/power/qcom-charger/Kconfig9
-rw-r--r--drivers/power/qcom-charger/Makefile1
-rw-r--r--drivers/power/qcom-charger/battery_current_limit.c1827
-rw-r--r--drivers/power/qcom-charger/bcl_peripheral.c414
-rw-r--r--drivers/regulator/cpr3-mmss-regulator.c108
-rw-r--r--drivers/regulator/cpr3-regulator.c3
-rw-r--r--drivers/slimbus/slim-msm-ngd.c54
-rw-r--r--drivers/slimbus/slimbus.c8
-rw-r--r--drivers/soc/qcom/icnss.c301
-rw-r--r--drivers/thermal/msm_thermal.c5
-rw-r--r--drivers/usb/dwc3/dwc3-msm.c4
-rw-r--r--drivers/usb/dwc3/gadget.c12
-rw-r--r--drivers/usb/gadget/composite.c14
-rw-r--r--drivers/usb/gadget/function/f_gsi.c68
-rw-r--r--drivers/usb/gadget/function/rndis.c11
-rw-r--r--drivers/video/fbdev/msm/mdss_debug.c8
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_panel.c16
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp.c14
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.c1
-rw-r--r--include/linux/leds-qpnp-flash-v2.h44
-rw-r--r--include/linux/slimbus/slimbus.h5
-rw-r--r--include/linux/usb/gadget.h1
-rw-r--r--include/uapi/linux/msm_kgsl.h1
-rw-r--r--sound/soc/soc-core.c4
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, &regmap_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, &regmap_data_fops);
+
debugfs_create_file("access", 0400, map->debugfs,
map, &regmap_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, &regs);
+}
- 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, &regs);
-}
+ 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), &registers,
- 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, &registers, 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");
}