diff options
75 files changed, 4809 insertions, 662 deletions
diff --git a/Documentation/devicetree/bindings/iio/adc/qcom-rradc.txt b/Documentation/devicetree/bindings/iio/adc/qcom-rradc.txt index 8a64ccbd66c0..721a4f72563e 100644 --- a/Documentation/devicetree/bindings/iio/adc/qcom-rradc.txt +++ b/Documentation/devicetree/bindings/iio/adc/qcom-rradc.txt @@ -36,13 +36,10 @@ Main node properties: Definition: Must be one. For details about IIO bindings see: Documentation/devicetree/bindings/iio/iio-bindings.txt -Channel subnode properties: - -- channel: - Usage: required - Value type: <u32> - Definition: ADC channel number. - See drivers/iio/adc/qcom-rradc.c for channels within rradc_channel_id +IIO client nodes need to specify the RRADC channel number while requesting ADC reads. +The channel list supported by the RRADC driver is available in the enum rradc_channel_id +located at at drivers/iio/adc/qcom-rradc.c. Clients can use this index from the enum +as the channel number while requesting ADC reads. Example: @@ -53,11 +50,6 @@ Example: #address-cells = <1>; #size-cells = <0>; #io-channel-cells = <1>; - - /* Channel node */ - batt_id { - channel = <0>; - }; }; /* IIO client node */ diff --git a/Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt b/Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt index b06aca1755b9..832ec34dbbda 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt @@ -17,6 +17,7 @@ Required properties: same as "interrupts" node. It will also contain active low or active high information. - ite,reset-gpio : reset gpio to control the reset of chip + - ite,reset-delay : reset delay for controller (ms), default 20 Optional properties: - avdd-supply : Analog power supply needed to power device @@ -31,6 +32,19 @@ Optional properties: - ite,display-coords : display min x, min y, max x and max y resolution - ite,num-fingers : number of fingers supported by the touch controller + - pinctrl-names : This should be defined if a target uses pinctrl framework. + See "pinctrl" in Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt. + It should specify the names of the configs that pinctrl can install in driver. + Following are the pinctrl configs that can be installed: + "pmx_ts_active" : Active configuration of pins, this should specify active + config defined in pin groups of interrupt and reset gpio. + "pmx_ts_suspend" : Disabled configuration of pins, this should specify sleep + config defined in pin groups of interrupt and reset gpio. + "pmx_ts_release" : Release configuration of pins, this should specify + release config defined in pin groups of interrupt and reset gpio. + - ite,low-reset : boolean, if the controller needs low-state of the reset gpio while + initializing, and reset gpio should be made as high-state to reset the + controller. It means the controller needs "active-high" reset gpio. Required properties palm-detect-en feature: - ite,palm-detect-keycode : The keycode that is required to be sent when @@ -42,6 +56,11 @@ Example: compatible = "ite,it7260_ts"; reg = <0x46>; interrupt-parent = <&msmgpio>; + /* pins used by touchscreen */ + pinctrl-names = "pmx_ts_active","pmx_ts_suspend","pmx_ts_release"; + pinctrl-0 = <&ts_int_active &ts_reset_active>; + pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>; + pinctrl-2 = <&ts_release> interrupts = <17 0x2>; avdd-supply = <&pm8226_l19>; vdd-supply = <&pm8226_lvs1>; @@ -55,5 +74,7 @@ Example: ite,panel-coords = <0 0 320 320>; ite,display-coords = <0 0 320 320>; ite,num-fingers = <2>; + ite,reset-delay = <20>; + ite,low-reset; }; }; diff --git a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt index 368f0b8c9525..5a415d04fbcf 100644 --- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt +++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt @@ -56,6 +56,15 @@ Charger specific properties: Definition: Specifies the DC input power limit in micro-watts. If the value is not present, 8W is used as default. +- qcom,thermal-mitigation + Usage: optional + Value type: Array of <u32> + Definition: Array of fast charge current limit values for + different system thermal mitigation levels. + This should be a flat array that denotes the + maximum charge current in mA for each thermal + level. + ============================================= Second Level Nodes - SMB2 Charger Peripherals ============================================= diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt index d25a7930289e..7fa51e394f5c 100755 --- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt +++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt @@ -2142,3 +2142,35 @@ Example: asoc-codec-names = "msm-stub-codec.1"; qcom,wsa-max-devs = <0>; }; + +* WCD DSP manager driver + +Required properties: +- compatible : "qcom,wcd-dsp-mgr" +- qcom,wdsp-components : This is phandle list containing the references to the + components of the manager driver. Manager driver will + register to component framework with these phandles. +- qcom,img-filename : String property to provide the dsp image file name that is + to be read from file system and downloaded to dsp memory +Optional properties: +- qcom,wdsp-cmpnt-dev-name : Property that manager driver will parse, but defined + in the child's DT entry that is given to manager driver + with phandle. This property will be used by the manager + driver in case the manager driver cannot match child's + of_node pointer to registered phandle. + +Example: + + qcom,wcd-dsp-mgr { + compatible = "qcom,wcd-dsp-mgr"; + qcom,wdsp-components = <&wcd934x_cdc 0>, + <&wcd_spi_0 1>, + <&glink_spi 2>; + qcom,img-filename = "cpe_9340"; + }; + +Example of child node that would have qcom,wdsp-cmpnt-dev-name property + + wcd934x_cdc: tavil_codec { + qcom,wdsp-cmpnt-dev-name = "tavil_codec"; + }; diff --git a/Documentation/devicetree/bindings/sound/wcd-spi.txt b/Documentation/devicetree/bindings/sound/wcd-spi.txt new file mode 100644 index 000000000000..6ea513b026c2 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wcd-spi.txt @@ -0,0 +1,27 @@ +WCD audio codec SPI driver support + +* wcd_spi + +The wcd_spi device is child device node to the master contoller's device node +and will have properties that the SPI framework or the master controller driver +expects. The properties listed here are specific to wcd-spi driver. + +Required properties: + +- compatible : "qcom,wcd-spi-v2" + +- qcom,mem-base-addr : Defines the memory base address from the SPI + memory map. This will be used as an offset to read + and write memory. + +Example: + +&spi_10 { + status = "ok"; + wcd_spi_0: wcd_spi@0 { + compatible = "qcom,wcd-spi-v2"; + reg = <0>; + spi-max-frequency = <24000000>; + qcom,mem-base-addr = <0x100000>; + }; +}; diff --git a/Documentation/devicetree/bindings/thermal/tsens.txt b/Documentation/devicetree/bindings/thermal/tsens.txt index 9aa52dde2dd4..684bea131405 100644 --- a/Documentation/devicetree/bindings/thermal/tsens.txt +++ b/Documentation/devicetree/bindings/thermal/tsens.txt @@ -34,16 +34,22 @@ Required properties: should be "qcom,msmfalcon-tsens" for falcon TSENS driver. The compatible property is used to identify the respective fusemap to use for the corresponding SoC. -- reg : offset and length of the TSENS registers. -- reg : offset and length of the QFPROM registers used for storing - the calibration data for the individual sensors. +- reg : offset and length of the TSENS registers with associated property in reg-names + as "tsens_physical" and QFPROM registers with associated property in reg-names + as "tsens_eeprom_physical". The efuse registers are used for storing + the calibration data for the individual sensors. If the gain and offset are + programmed by the TSENS control registers then adding the QFPROM register property + is optional. - reg-names : resource names used for the physical address of the TSENS registers, the QFPROM efuse primary calibration address region, Should be "tsens_physical" for physical address of the TSENS, "tsens_eeprom_physical" for physical address where primary calibration data is stored. This includes the backup calibration address region if TSENS calibration data is stored - in the region. + in the region. The reg-name "tsens_eeprom_physical" property is + optional if the gain and offset are programmed by the TSENS + control registers and the status registers directly reports the TSENS + temperature readings. - interrupts : TSENS interrupt to notify Upper/Lower temperature threshold. - interrupt-names: Should be "tsens-upper-lower" for temperature threshold. Add "tsens-critical" for Critical temperature threshold notification @@ -53,7 +59,8 @@ Required properties: - qcom,slope : One point calibration characterized slope data for each sensor used to compute the offset. Slope is represented as ADC code/DegC and the value is multipled by a factor - of 1000. + of 1000. If gain and offset are programmed by the TSENS control + registers then this property is optional. Optional properties: - qcom,calibration-less-mode : If present the pre-characterized data for offsets diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi index 75f3cd3e06ec..c724ce5a8ad9 100644 --- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi @@ -297,47 +297,6 @@ #address-cells = <1>; #size-cells = <0>; #io-channel-cells = <1>; - - /* Channel node */ - batt_id { - channel = <3>; - }; - - batt_therm { - channel = <4>; - }; - - skin_temp { - channel = <5>; - }; - - usbin_v { - channel = <6>; - }; - - usbin_i { - channel = <7>; - }; - - dcin_v { - channel = <8>; - }; - - dcin_i { - channel = <9>; - }; - - die_temp { - channel = <0xa>; - }; - - chg_temp { - channel = <0xb>; - }; - - gpio { - channel = <0xc>; - }; }; pmicobalt_fg: qpnp,fg { diff --git a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts index c8402ba5b69a..10edf71da2f3 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts +++ b/arch/arm/boot/dts/qcom/msmcobalt-cdp.dts @@ -17,7 +17,7 @@ #include "msmcobalt-cdp.dtsi" / { - model = "Qualcomm Technologies, Inc. MSM cobalt"; + model = "Qualcomm Technologies, Inc. MSM COBALT v1 CDP"; compatible = "qcom,msmcobalt-cdp", "qcom,msmcobalt", "qcom,cdp"; qcom,board-id = <1 0>; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts index 89ef72c104f5..ea4047df25f6 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts +++ b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dts @@ -17,7 +17,7 @@ #include "msmcobalt-mtp.dtsi" / { - model = "Qualcomm Technologies, Inc. MSM cobalt v1"; + model = "Qualcomm Technologies, Inc. MSM COBALT v1 MTP"; compatible = "qcom,msmcobalt-mtp", "qcom,msmcobalt", "qcom,mtp"; qcom,board-id = <8 0>; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi index 6db4ed1f45e1..4cbd17f80cb8 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-pinctrl.dtsi @@ -185,7 +185,7 @@ config { pins = "gpio87", "gpio88"; drive-strength = <2>; - bias-pull-up; + bias-disable; }; }; }; @@ -1245,7 +1245,7 @@ config { pins = "gpio45", "gpio46", "gpio47", "gpio48"; drive-strength = <2>; - bias-disable; + bias-pull-up; }; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi index 1720aca3b298..425a902568ae 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-qrd.dtsi @@ -11,3 +11,61 @@ */ #include "msmcobalt-mtp.dtsi" + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dsi { + hw-config = "split_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_dual_nt35597_truly_video>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 94 0>; + qcom,platform-te-gpio = <&tlmm 10 0>; + qcom,panel-mode-gpio = <&tlmm 91 0>; +}; + +&mdss_dsi1 { + qcom,dsi-pref-prim-pan = <&dsi_dual_nt35597_truly_video>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 94 0>; + qcom,platform-te-gpio = <&tlmm 10 0>; + qcom,panel-mode-gpio = <&tlmm 91 0>; +}; + +&dsi_sharp_4k_dsc_video { + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; +}; + +&dsi_sharp_4k_dsc_cmd { + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; +}; + +&dsi_dual_nt35597_truly_video { + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-mode-sel-gpio-state = "dual_port"; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; +}; + +&dsi_dual_nt35597_truly_cmd { + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <4095>; + qcom,mdss-dsi-mode-sel-gpio-state = "dual_port"; + qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; +}; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2-cdp.dts b/arch/arm/boot/dts/qcom/msmcobalt-v2-cdp.dts index d47213603bdf..7cb8f107abc4 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-v2-cdp.dts +++ b/arch/arm/boot/dts/qcom/msmcobalt-v2-cdp.dts @@ -17,7 +17,7 @@ #include "msmcobalt-cdp.dtsi" / { - model = "Qualcomm Technologies, Inc. MSM cobalt v2"; + model = "Qualcomm Technologies, Inc. MSM COBALT v2 CDP"; compatible = "qcom,msmcobalt-cdp", "qcom,msmcobalt", "qcom,cdp"; qcom,board-id = <1 0>; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2-mtp.dts b/arch/arm/boot/dts/qcom/msmcobalt-v2-mtp.dts index eb48e4444ef8..96e671f04df3 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-v2-mtp.dts +++ b/arch/arm/boot/dts/qcom/msmcobalt-v2-mtp.dts @@ -17,7 +17,7 @@ #include "msmcobalt-mtp.dtsi" / { - model = "Qualcomm Technologies, Inc. MSM cobalt v2"; + model = "Qualcomm Technologies, Inc. MSM COBALT v2 MTP"; compatible = "qcom,msmcobalt-mtp", "qcom,msmcobalt", "qcom,mtp"; qcom,board-id = <8 0>; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi index aa9390de6525..3bbc98788b9f 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi @@ -761,7 +761,7 @@ vdd_gpucc-supply = <&gfx_vreg>; vdd_mx-supply = <&pmcobalt_s9_level>; vdd_gpu_mx-supply = <&pmcobalt_s9_level>; - qcom,gfx3d_clk_src-opp-store-vcorner = <&msm_gpu>; + qcom,gfx3d_clk_src-opp-handle = <&msm_gpu>; qcom,gfxfreq-speedbin0 = < 0 0 0 >, < 171000000 1 RPM_SMD_REGULATOR_LEVEL_SVS >, @@ -815,9 +815,9 @@ < 1324800000 0x04040045 0x06370037 0x2 >, < 1401600000 0x04040049 0x073a003a 0x2 >, < 1478400000 0x0404004d 0x073e003e 0x2 >, - < 1574400000 0x04040052 0x08420042 0x3 >, - < 1651200000 0x04040056 0x08450045 0x3 >, - < 1728000000 0x0404005a 0x08480048 0x3 >, + < 1574400000 0x04040052 0x08420042 0x2 >, + < 1651200000 0x04040056 0x08450045 0x2 >, + < 1728000000 0x0404005a 0x08480048 0x2 >, < 1804800000 0x0404005e 0x094b004b 0x3 >, < 1881600000 0x04040062 0x094e004e 0x3 >; @@ -840,10 +840,10 @@ < 1401600000 0x04040049 0x073a003a 0x2 >, < 1478400000 0x0404004d 0x073e003e 0x2 >, < 1536000000 0x04040050 0x07400040 0x2 >, - < 1632000000 0x04040055 0x08440044 0x3 >, - < 1708800000 0x04040059 0x08470047 0x3 >, - < 1785600000 0x0404005d 0x094a004a 0x3 >, - < 1862400000 0x04040061 0x094e004e 0x3 >, + < 1632000000 0x04040055 0x08440044 0x2 >, + < 1708800000 0x04040059 0x08470047 0x2 >, + < 1785600000 0x0404005d 0x094a004a 0x2 >, + < 1862400000 0x04040061 0x094e004e 0x2 >, < 1939200000 0x04040065 0x09510051 0x3 >, < 2016000000 0x04040069 0x0a540054 0x3 >, < 2092800000 0x0404006d 0x0a570057 0x3 >; @@ -983,15 +983,15 @@ /* SVS */ <90 512 80000 640000>, <90 585 80000 640000>, - <1 676 80000 160000>, + <1 676 80000 80000>, /* NOMINAL */ <90 512 206000 960000>, <90 585 206000 960000>, - <1 676 206000 200000>, + <1 676 206000 160000>, /* TURBO */ <90 512 206000 3600000>, <90 585 206000 3600000>, - <1 676 206000 960000>; + <1 676 206000 300000>; qcom,bus-vector-names = "MIN", "SVS", "NOMINAL", "TURBO"; /* IPA RAM mmap */ @@ -2059,27 +2059,22 @@ tsens0: tsens@10aa000 { compatible = "qcom,msmcobalt-tsens"; - reg = <0x10aa000 0x2000>, - <0x74230 0x1000>; - reg-names = "tsens_physical", "tsens_eeprom_physical"; + reg = <0x10aa000 0x2000>; + reg-names = "tsens_physical"; interrupts = <0 458 0>, <0 445 0>; interrupt-names = "tsens-upper-lower", "tsens-critical"; qcom,sensors = <14>; - qcom,slope = <2901 2846 3200 3200 3200 3200 3200 3200 3200 - 3200 3200 3200 3200 3200>; }; tsens1: tsens@10ad000 { compatible = "qcom,msmcobalt-tsens"; - reg = <0x10ad000 0x2000>, - <0x75230 0x1000>; - reg-names = "tsens_physical", "tsens_eeprom_physical"; + reg = <0x10ad000 0x2000>; + reg-names = "tsens_physical"; interrupts = <0 184 0>, <0 430 0>; interrupt-names = "tsens-upper-lower", "tsens-critical"; qcom,client-id = <14 15 16 17 18 19 20 21>; qcom,sensor-id = <0 1 3 4 5 6 7 2>; qcom,sensors = <8>; - qcom,slope = <2901 2846 3200 3200 3200 3200 3200 3200>; }; qcom,sensor-information { diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 65f5afed59ca..de464a023e18 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -509,6 +509,7 @@ CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y CONFIG_MSM_MPM_OF=y CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_CORE_CTL_HELPER=y +CONFIG_QCOM_REMOTEQDSS=y CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y CONFIG_MSM_RPM_LOG=y CONFIG_MSM_RPM_STATS_LOG=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index 67793cd8c0a8..55f7eb39c70d 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -529,6 +529,7 @@ CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y CONFIG_MSM_MPM_OF=y CONFIG_MSM_EVENT_TIMER=y CONFIG_MSM_CORE_CTL_HELPER=y +CONFIG_QCOM_REMOTEQDSS=y CONFIG_MSM_SERVICE_NOTIFIER=y CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y CONFIG_MSM_RPM_LOG=y diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 4c67945ef36f..20d17906fc9b 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -379,6 +379,7 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) struct files_struct *files = proc->files; unsigned long rlim_cur; unsigned long irqs; + int ret; if (files == NULL) return -ESRCH; @@ -389,7 +390,11 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) rlim_cur = task_rlimit(proc->tsk, RLIMIT_NOFILE); unlock_task_sighand(proc->tsk, &irqs); - return __alloc_fd(files, 0, rlim_cur, flags); + preempt_enable_no_resched(); + ret = __alloc_fd(files, 0, rlim_cur, flags); + preempt_disable(); + + return ret; } /* @@ -398,8 +403,11 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) static void task_fd_install( struct binder_proc *proc, unsigned int fd, struct file *file) { - if (proc->files) + if (proc->files) { + preempt_enable_no_resched(); __fd_install(proc->files, fd, file); + preempt_disable(); + } } /* @@ -427,6 +435,7 @@ static inline void binder_lock(const char *tag) { trace_binder_lock(tag); mutex_lock(&binder_main_lock); + preempt_disable(); trace_binder_locked(tag); } @@ -434,8 +443,62 @@ static inline void binder_unlock(const char *tag) { trace_binder_unlock(tag); mutex_unlock(&binder_main_lock); + preempt_enable(); +} + +static inline void *kzalloc_preempt_disabled(size_t size) +{ + void *ptr; + + ptr = kzalloc(size, GFP_NOWAIT); + if (ptr) + return ptr; + + preempt_enable_no_resched(); + ptr = kzalloc(size, GFP_KERNEL); + preempt_disable(); + + return ptr; +} + +static inline long copy_to_user_preempt_disabled(void __user *to, const void *from, long n) +{ + long ret; + + preempt_enable_no_resched(); + ret = copy_to_user(to, from, n); + preempt_disable(); + return ret; +} + +static inline long copy_from_user_preempt_disabled(void *to, const void __user *from, long n) +{ + long ret; + + preempt_enable_no_resched(); + ret = copy_from_user(to, from, n); + preempt_disable(); + return ret; } +#define get_user_preempt_disabled(x, ptr) \ +({ \ + int __ret; \ + preempt_enable_no_resched(); \ + __ret = get_user(x, ptr); \ + preempt_disable(); \ + __ret; \ +}) + +#define put_user_preempt_disabled(x, ptr) \ +({ \ + int __ret; \ + preempt_enable_no_resched(); \ + __ret = put_user(x, ptr); \ + preempt_disable(); \ + __ret; \ +}) + static void binder_set_nice(long nice) { long min_nice; @@ -568,6 +631,8 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate, else mm = get_task_mm(proc->tsk); + preempt_enable_no_resched(); + if (mm) { down_write(&mm->mmap_sem); vma = proc->vma; @@ -622,6 +687,9 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate, up_write(&mm->mmap_sem); mmput(mm); } + + preempt_disable(); + return 0; free_range: @@ -644,6 +712,9 @@ err_no_vma: up_write(&mm->mmap_sem); mmput(mm); } + + preempt_disable(); + return -ENOMEM; } @@ -903,7 +974,7 @@ static struct binder_node *binder_new_node(struct binder_proc *proc, return NULL; } - node = kzalloc(sizeof(*node), GFP_KERNEL); + node = kzalloc_preempt_disabled(sizeof(*node)); if (node == NULL) return NULL; binder_stats_created(BINDER_STAT_NODE); @@ -1040,7 +1111,7 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, else return ref; } - new_ref = kzalloc(sizeof(*ref), GFP_KERNEL); + new_ref = kzalloc_preempt_disabled(sizeof(*ref)); if (new_ref == NULL) return NULL; binder_stats_created(BINDER_STAT_REF); @@ -1438,14 +1509,14 @@ static void binder_transaction(struct binder_proc *proc, e->to_proc = target_proc->pid; /* TODO: reuse incoming transaction for reply */ - t = kzalloc(sizeof(*t), GFP_KERNEL); + t = kzalloc_preempt_disabled(sizeof(*t)); if (t == NULL) { return_error = BR_FAILED_REPLY; goto err_alloc_t_failed; } binder_stats_created(BINDER_STAT_TRANSACTION); - tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL); + tcomplete = kzalloc_preempt_disabled(sizeof(*tcomplete)); if (tcomplete == NULL) { return_error = BR_FAILED_REPLY; goto err_alloc_tcomplete_failed; @@ -1502,14 +1573,14 @@ static void binder_transaction(struct binder_proc *proc, offp = (binder_size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *))); - if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t) + if (copy_from_user_preempt_disabled(t->buffer->data, (const void __user *)(uintptr_t) tr->data.ptr.buffer, tr->data_size)) { binder_user_error("%d:%d got transaction with invalid data ptr\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; goto err_copy_data_failed; } - if (copy_from_user(offp, (const void __user *)(uintptr_t) + if (copy_from_user_preempt_disabled(offp, (const void __user *)(uintptr_t) tr->data.ptr.offsets, tr->offsets_size)) { binder_user_error("%d:%d got transaction with invalid offsets ptr\n", proc->pid, thread->pid); @@ -1778,7 +1849,7 @@ static int binder_thread_write(struct binder_proc *proc, void __user *end = buffer + size; while (ptr < end && thread->return_error == BR_OK) { - if (get_user(cmd, (uint32_t __user *)ptr)) + if (get_user_preempt_disabled(cmd, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); trace_binder_command(cmd); @@ -1796,7 +1867,7 @@ static int binder_thread_write(struct binder_proc *proc, struct binder_ref *ref; const char *debug_string; - if (get_user(target, (uint32_t __user *)ptr)) + if (get_user_preempt_disabled(target, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); if (target == 0 && binder_context_mgr_node && @@ -1846,10 +1917,10 @@ static int binder_thread_write(struct binder_proc *proc, binder_uintptr_t cookie; struct binder_node *node; - if (get_user(node_ptr, (binder_uintptr_t __user *)ptr)) + if (get_user_preempt_disabled(node_ptr, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); - if (get_user(cookie, (binder_uintptr_t __user *)ptr)) + if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); node = binder_get_node(proc, node_ptr); @@ -1907,7 +1978,7 @@ static int binder_thread_write(struct binder_proc *proc, binder_uintptr_t data_ptr; struct binder_buffer *buffer; - if (get_user(data_ptr, (binder_uintptr_t __user *)ptr)) + if (get_user_preempt_disabled(data_ptr, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); @@ -1949,7 +2020,7 @@ static int binder_thread_write(struct binder_proc *proc, case BC_REPLY: { struct binder_transaction_data tr; - if (copy_from_user(&tr, ptr, sizeof(tr))) + if (copy_from_user_preempt_disabled(&tr, ptr, sizeof(tr))) return -EFAULT; ptr += sizeof(tr); binder_transaction(proc, thread, &tr, cmd == BC_REPLY); @@ -1999,10 +2070,10 @@ static int binder_thread_write(struct binder_proc *proc, struct binder_ref *ref; struct binder_ref_death *death; - if (get_user(target, (uint32_t __user *)ptr)) + if (get_user_preempt_disabled(target, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); - if (get_user(cookie, (binder_uintptr_t __user *)ptr)) + if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); ref = binder_get_ref(proc, target); @@ -2031,7 +2102,7 @@ static int binder_thread_write(struct binder_proc *proc, proc->pid, thread->pid); break; } - death = kzalloc(sizeof(*death), GFP_KERNEL); + death = kzalloc_preempt_disabled(sizeof(*death)); if (death == NULL) { thread->return_error = BR_ERROR; binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, @@ -2085,8 +2156,7 @@ static int binder_thread_write(struct binder_proc *proc, struct binder_work *w; binder_uintptr_t cookie; struct binder_ref_death *death = NULL; - - if (get_user(cookie, (binder_uintptr_t __user *)ptr)) + if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(cookie); @@ -2118,7 +2188,8 @@ static int binder_thread_write(struct binder_proc *proc, wake_up_interruptible(&proc->wait); } } - } break; + } + break; default: pr_err("%d:%d unknown command %d\n", @@ -2167,7 +2238,7 @@ static int binder_thread_read(struct binder_proc *proc, int wait_for_proc_work; if (*consumed == 0) { - if (put_user(BR_NOOP, (uint32_t __user *)ptr)) + if (put_user_preempt_disabled(BR_NOOP, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); } @@ -2178,7 +2249,7 @@ retry: if (thread->return_error != BR_OK && ptr < end) { if (thread->return_error2 != BR_OK) { - if (put_user(thread->return_error2, (uint32_t __user *)ptr)) + if (put_user_preempt_disabled(thread->return_error2, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); binder_stat_br(proc, thread, thread->return_error2); @@ -2186,7 +2257,7 @@ retry: goto done; thread->return_error2 = BR_OK; } - if (put_user(thread->return_error, (uint32_t __user *)ptr)) + if (put_user_preempt_disabled(thread->return_error, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); binder_stat_br(proc, thread, thread->return_error); @@ -2264,7 +2335,7 @@ retry: } break; case BINDER_WORK_TRANSACTION_COMPLETE: { cmd = BR_TRANSACTION_COMPLETE; - if (put_user(cmd, (uint32_t __user *)ptr)) + if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr)) return -EFAULT; ptr += sizeof(uint32_t); @@ -2306,14 +2377,14 @@ retry: node->has_weak_ref = 0; } if (cmd != BR_NOOP) { - if (put_user(cmd, (uint32_t __user *)ptr)) + if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr)) return -EFAULT; ptr += sizeof(uint32_t); - if (put_user(node->ptr, + if (put_user_preempt_disabled(node->ptr, (binder_uintptr_t __user *) (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); - if (put_user(node->cookie, + if (put_user_preempt_disabled(node->cookie, (binder_uintptr_t __user *) (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); @@ -2357,11 +2428,10 @@ retry: cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE; else cmd = BR_DEAD_BINDER; - if (put_user(cmd, (uint32_t __user *)ptr)) + if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr)) return -EFAULT; ptr += sizeof(uint32_t); - if (put_user(death->cookie, - (binder_uintptr_t __user *)ptr)) + if (put_user_preempt_disabled(death->cookie, (binder_uintptr_t __user *) ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); binder_stat_br(proc, thread, cmd); @@ -2428,10 +2498,10 @@ retry: ALIGN(t->buffer->data_size, sizeof(void *)); - if (put_user(cmd, (uint32_t __user *)ptr)) + if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr)) return -EFAULT; ptr += sizeof(uint32_t); - if (copy_to_user(ptr, &tr, sizeof(tr))) + if (copy_to_user_preempt_disabled(ptr, &tr, sizeof(tr))) return -EFAULT; ptr += sizeof(tr); @@ -2473,7 +2543,7 @@ done: binder_debug(BINDER_DEBUG_THREADS, "%d:%d BR_SPAWN_LOOPER\n", proc->pid, thread->pid); - if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer)) + if (put_user_preempt_disabled(BR_SPAWN_LOOPER, (uint32_t __user *) buffer)) return -EFAULT; binder_stat_br(proc, thread, BR_SPAWN_LOOPER); } @@ -2548,7 +2618,7 @@ static struct binder_thread *binder_get_thread(struct binder_proc *proc) break; } if (*p == NULL) { - thread = kzalloc(sizeof(*thread), GFP_KERNEL); + thread = kzalloc_preempt_disabled(sizeof(*thread)); if (thread == NULL) return NULL; binder_stats_created(BINDER_STAT_THREAD); @@ -2652,7 +2722,7 @@ static int binder_ioctl_write_read(struct file *filp, ret = -EINVAL; goto out; } - if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { + if (copy_from_user_preempt_disabled(&bwr, ubuf, sizeof(bwr))) { ret = -EFAULT; goto out; } @@ -2670,7 +2740,7 @@ static int binder_ioctl_write_read(struct file *filp, trace_binder_write_done(ret); if (ret < 0) { bwr.read_consumed = 0; - if (copy_to_user(ubuf, &bwr, sizeof(bwr))) + if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr))) ret = -EFAULT; goto out; } @@ -2684,7 +2754,7 @@ static int binder_ioctl_write_read(struct file *filp, if (!list_empty(&proc->todo)) wake_up_interruptible(&proc->wait); if (ret < 0) { - if (copy_to_user(ubuf, &bwr, sizeof(bwr))) + if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr))) ret = -EFAULT; goto out; } @@ -2694,7 +2764,7 @@ static int binder_ioctl_write_read(struct file *filp, proc->pid, thread->pid, (u64)bwr.write_consumed, (u64)bwr.write_size, (u64)bwr.read_consumed, (u64)bwr.read_size); - if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { + if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr))) { ret = -EFAULT; goto out; } @@ -2772,7 +2842,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) goto err; break; case BINDER_SET_MAX_THREADS: - if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) { + if (copy_from_user_preempt_disabled(&proc->max_threads, ubuf, sizeof(proc->max_threads))) { ret = -EINVAL; goto err; } @@ -2795,9 +2865,8 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ret = -EINVAL; goto err; } - if (put_user(BINDER_CURRENT_PROTOCOL_VERSION, - &ver->protocol_version)) { - ret = -EINVAL; + if (put_user_preempt_disabled(BINDER_CURRENT_PROTOCOL_VERSION, &ver->protocol_version)) { + ret = -EINVAL; goto err; } break; @@ -2858,6 +2927,7 @@ static const struct vm_operations_struct binder_vm_ops = { static int binder_mmap(struct file *filp, struct vm_area_struct *vma) { int ret; + struct vm_struct *area; struct binder_proc *proc = filp->private_data; const char *failure_string; @@ -2918,7 +2988,11 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) vma->vm_ops = &binder_vm_ops; vma->vm_private_data = proc; - if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) { + /* binder_update_page_range assumes preemption is disabled */ + preempt_disable(); + ret = binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma); + preempt_enable_no_resched(); + if (ret) { ret = -ENOMEM; failure_string = "alloc small buf"; goto err_alloc_small_buf_failed; @@ -3188,8 +3262,12 @@ static void binder_deferred_func(struct work_struct *work) int defer; do { - binder_lock(__func__); + trace_binder_lock(__func__); + mutex_lock(&binder_main_lock); + trace_binder_locked(__func__); + mutex_lock(&binder_deferred_lock); + preempt_disable(); if (!hlist_empty(&binder_deferred_list)) { proc = hlist_entry(binder_deferred_list.first, struct binder_proc, deferred_work_node); @@ -3215,7 +3293,9 @@ static void binder_deferred_func(struct work_struct *work) if (defer & BINDER_DEFERRED_RELEASE) binder_deferred_release(proc); /* frees proc */ - binder_unlock(__func__); + trace_binder_unlock(__func__); + mutex_unlock(&binder_main_lock); + preempt_enable_no_resched(); if (files) put_files_struct(files); } while (proc); diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c index dea0448c365c..ae2df4f7ff0d 100644 --- a/drivers/iio/adc/qcom-rradc.c +++ b/drivers/iio/adc/qcom-rradc.c @@ -149,12 +149,12 @@ #define FG_ADC_RR_TEMP_FS_VOLTAGE_NUM 5000000 #define FG_ADC_RR_TEMP_FS_VOLTAGE_DEN 3 #define FG_ADC_RR_DIE_TEMP_OFFSET 600000 -#define FG_ADC_RR_DIE_TEMP_SLOPE 2000 -#define FG_ADC_RR_DIE_TEMP_OFFSET_DEGC 25 +#define FG_ADC_RR_DIE_TEMP_SLOPE 2 +#define FG_ADC_RR_DIE_TEMP_OFFSET_MILLI_DEGC 25000 #define FG_ADC_RR_CHG_TEMP_OFFSET 1288000 -#define FG_ADC_RR_CHG_TEMP_SLOPE 4000 -#define FG_ADC_RR_CHG_TEMP_OFFSET_DEGC 27 +#define FG_ADC_RR_CHG_TEMP_SLOPE 4 +#define FG_ADC_RR_CHG_TEMP_OFFSET_MILLI_DEGC 27000 #define FG_ADC_RR_VOLT_INPUT_FACTOR 8 #define FG_ADC_RR_CURR_INPUT_FACTOR 2 @@ -162,6 +162,9 @@ #define FG_ADC_KELVINMIL_CELSIUSMIL 273150 #define FG_ADC_RR_GPIO_FS_RANGE 5000 +#define FG_RR_ADC_COHERENT_CHECK_RETRY 5 +#define FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN 16 +#define FG_RR_ADC_STS_CHANNEL_READING_MASK 0x3 /* * The channel number is not a physical index in hardware, @@ -171,21 +174,20 @@ * the RR ADC before RR_ADC_MAX. */ enum rradc_channel_id { - RR_ADC_BATT_ID_5 = 0, - RR_ADC_BATT_ID_15, - RR_ADC_BATT_ID_150, - RR_ADC_BATT_ID, + RR_ADC_BATT_ID = 0, RR_ADC_BATT_THERM, RR_ADC_SKIN_TEMP, - RR_ADC_USBIN_V, RR_ADC_USBIN_I, - RR_ADC_DCIN_V, + RR_ADC_USBIN_V, RR_ADC_DCIN_I, + RR_ADC_DCIN_V, RR_ADC_DIE_TEMP, RR_ADC_CHG_TEMP, RR_ADC_GPIO, - RR_ADC_ATEST, - RR_ADC_TM_ADC, + RR_ADC_CHG_HOT_TEMP, + RR_ADC_CHG_TOO_HOT_TEMP, + RR_ADC_SKIN_HOT_TEMP, + RR_ADC_SKIN_TOO_HOT_TEMP, RR_ADC_MAX }; @@ -205,51 +207,75 @@ struct rradc_channels { long info_mask; u8 lsb; u8 msb; + u8 sts; int (*scale)(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 adc_code, int *result); }; struct rradc_chan_prop { enum rradc_channel_id channel; + uint32_t channel_data; int (*scale)(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 adc_code, int *result); }; static int rradc_read(struct rradc_chip *rr_adc, u16 offset, u8 *data, int len) { - int rc = 0; + int rc = 0, retry_cnt = 0, i = 0; + u8 data_check[FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN]; + bool coherent_err = false; + + if (len > FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN) { + pr_err("Increase the buffer length\n"); + return -EINVAL; + } + + while (retry_cnt < FG_RR_ADC_COHERENT_CHECK_RETRY) { + rc = regmap_bulk_read(rr_adc->regmap, rr_adc->base + offset, + data, len); + if (rc < 0) { + pr_err("rr_adc reg 0x%x failed :%d\n", offset, rc); + return rc; + } + + rc = regmap_bulk_read(rr_adc->regmap, rr_adc->base + offset, + data_check, len); + if (rc < 0) { + pr_err("rr_adc reg 0x%x failed :%d\n", offset, rc); + return rc; + } + + for (i = 0; i < len; i++) { + if (data[i] != data_check[i]) + coherent_err = true; + } + + if (coherent_err) { + retry_cnt++; + coherent_err = false; + pr_debug("retry_cnt:%d\n", retry_cnt); + } else { + break; + } + } - rc = regmap_bulk_read(rr_adc->regmap, rr_adc->base + offset, data, len); - if (rc < 0) - pr_err("rr adc read reg %d failed with %d\n", offset, rc); + if (retry_cnt == FG_RR_ADC_COHERENT_CHECK_RETRY) + pr_err("Retry exceeded for coherrency check\n"); return rc; } static int rradc_post_process_batt_id(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 adc_code, - int *result_mohms) + int *result_ohms) { uint32_t current_value; int64_t r_id; - switch (prop->channel) { - case RR_ADC_BATT_ID_5: - current_value = FG_ADC_RR_BATT_ID_5_MA; - break; - case RR_ADC_BATT_ID_15: - current_value = FG_ADC_RR_BATT_ID_15_MA; - break; - case RR_ADC_BATT_ID_150: - current_value = FG_ADC_RR_BATT_ID_150_MA; - break; - default: - return -EINVAL; - } - + current_value = prop->channel_data; r_id = ((int64_t)adc_code * FG_ADC_RR_FS_VOLTAGE_MV); r_id = div64_s64(r_id, (FG_MAX_ADC_READINGS * current_value)); - *result_mohms = (r_id * FG_ADC_SCALE_MILLI_FACTOR); + *result_ohms = (r_id * FG_ADC_SCALE_MILLI_FACTOR); return 0; } @@ -270,30 +296,30 @@ static int rradc_post_process_therm(struct rradc_chip *chip, static int rradc_post_process_volt(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 adc_code, - int *result_mv) + int *result_uv) { - int64_t mv = 0; + int64_t uv = 0; /* 8x input attenuation; 2.5V ADC full scale */ - mv = ((int64_t)adc_code * FG_ADC_RR_VOLT_INPUT_FACTOR); - mv *= FG_ADC_RR_FS_VOLTAGE_MV; - mv = div64_s64(mv, FG_MAX_ADC_READINGS); - *result_mv = mv; + uv = ((int64_t)adc_code * FG_ADC_RR_VOLT_INPUT_FACTOR); + uv *= (FG_ADC_RR_FS_VOLTAGE_MV * FG_ADC_SCALE_MILLI_FACTOR); + uv = div64_s64(uv, FG_MAX_ADC_READINGS); + *result_uv = uv; return 0; } static int rradc_post_process_curr(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 adc_code, - int *result_ma) + int *result_ua) { - int64_t ma = 0; + int64_t ua = 0; /* 0.5 V/A; 2.5V ADC full scale */ - ma = ((int64_t)adc_code * FG_ADC_RR_CURR_INPUT_FACTOR); - ma *= FG_ADC_RR_FS_VOLTAGE_MV; - ma = div64_s64(ma, FG_MAX_ADC_READINGS); - *result_ma = ma; + ua = ((int64_t)adc_code * FG_ADC_RR_CURR_INPUT_FACTOR); + ua *= (FG_ADC_RR_FS_VOLTAGE_MV * FG_ADC_SCALE_MILLI_FACTOR); + ua = div64_s64(ua, FG_MAX_ADC_READINGS); + *result_ua = ua; return 0; } @@ -309,8 +335,41 @@ static int rradc_post_process_die_temp(struct rradc_chip *chip, FG_MAX_ADC_READINGS)); temp -= FG_ADC_RR_DIE_TEMP_OFFSET; temp = div64_s64(temp, FG_ADC_RR_DIE_TEMP_SLOPE); - temp += FG_ADC_RR_DIE_TEMP_OFFSET_DEGC; - *result_millidegc = (temp * FG_ADC_SCALE_MILLI_FACTOR); + temp += FG_ADC_RR_DIE_TEMP_OFFSET_MILLI_DEGC; + *result_millidegc = temp; + + return 0; +} + +static int rradc_post_process_chg_temp_hot(struct rradc_chip *chip, + struct rradc_chan_prop *prop, u16 adc_code, + int *result_millidegc) +{ + int64_t temp = 0; + + temp = (int64_t) adc_code * 4; + temp = temp * FG_ADC_RR_TEMP_FS_VOLTAGE_NUM; + temp = div64_s64(temp, (FG_ADC_RR_TEMP_FS_VOLTAGE_DEN * + FG_MAX_ADC_READINGS)); + temp = FG_ADC_RR_CHG_TEMP_OFFSET - temp; + temp = div64_s64(temp, FG_ADC_RR_CHG_TEMP_SLOPE); + temp = temp + FG_ADC_RR_CHG_TEMP_OFFSET_MILLI_DEGC; + *result_millidegc = temp; + + return 0; +} + +static int rradc_post_process_skin_temp_hot(struct rradc_chip *chip, + struct rradc_chan_prop *prop, u16 adc_code, + int *result_millidegc) +{ + int64_t temp = 0; + + temp = (int64_t) adc_code; + temp = div64_s64(temp, 2); + temp = temp - 30; + temp *= FG_ADC_SCALE_MILLI_FACTOR; + *result_millidegc = temp; return 0; } @@ -326,8 +385,8 @@ static int rradc_post_process_chg_temp(struct rradc_chip *chip, FG_MAX_ADC_READINGS)); temp = FG_ADC_RR_CHG_TEMP_OFFSET - temp; temp = div64_s64(temp, FG_ADC_RR_CHG_TEMP_SLOPE); - temp = temp + FG_ADC_RR_CHG_TEMP_OFFSET_DEGC; - *result_millidegc = (temp * FG_ADC_SCALE_MILLI_FACTOR); + temp = temp + FG_ADC_RR_CHG_TEMP_OFFSET_MILLI_DEGC; + *result_millidegc = temp; return 0; } @@ -346,63 +405,80 @@ static int rradc_post_process_gpio(struct rradc_chip *chip, return 0; } -#define RR_ADC_CHAN(_dname, _type, _mask, _scale, _lsb, _msb) \ +#define RR_ADC_CHAN(_dname, _type, _mask, _scale, _lsb, _msb, _sts) \ { \ - .datasheet_name = __stringify(_dname), \ + .datasheet_name = (_dname), \ .type = _type, \ .info_mask = _mask, \ .scale = _scale, \ .lsb = _lsb, \ .msb = _msb, \ + .sts = _sts, \ }, \ -#define RR_ADC_CHAN_TEMP(_dname, _scale, _lsb, _msb) \ +#define RR_ADC_CHAN_TEMP(_dname, _scale, _lsb, _msb, _sts) \ RR_ADC_CHAN(_dname, IIO_TEMP, \ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \ - _scale, _lsb, _msb) \ + _scale, _lsb, _msb, _sts) \ -#define RR_ADC_CHAN_VOLT(_dname, _scale, _lsb, _msb) \ +#define RR_ADC_CHAN_VOLT(_dname, _scale, _lsb, _msb, _sts) \ RR_ADC_CHAN(_dname, IIO_VOLTAGE, \ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\ - _scale, _lsb, _msb) \ + _scale, _lsb, _msb, _sts) \ -#define RR_ADC_CHAN_CURRENT(_dname, _scale, _lsb, _msb) \ +#define RR_ADC_CHAN_CURRENT(_dname, _scale, _lsb, _msb, _sts) \ RR_ADC_CHAN(_dname, IIO_CURRENT, \ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\ - _scale, _lsb, _msb) \ + _scale, _lsb, _msb, _sts) \ -#define RR_ADC_CHAN_RESISTANCE(_dname, _scale, _lsb, _msb) \ +#define RR_ADC_CHAN_RESISTANCE(_dname, _scale, _lsb, _msb, _sts) \ RR_ADC_CHAN(_dname, IIO_RESISTANCE, \ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\ - _scale, _lsb, _msb) \ + _scale, _lsb, _msb, _sts) \ static const struct rradc_channels rradc_chans[] = { - RR_ADC_CHAN_RESISTANCE("batt_id_5", rradc_post_process_batt_id, - FG_ADC_RR_BATT_ID_5_LSB, FG_ADC_RR_BATT_ID_5_MSB) - RR_ADC_CHAN_RESISTANCE("batt_id_15", rradc_post_process_batt_id, - FG_ADC_RR_BATT_ID_15_LSB, FG_ADC_RR_BATT_ID_15_MSB) - RR_ADC_CHAN_RESISTANCE("batt_id_150", rradc_post_process_batt_id, - FG_ADC_RR_BATT_ID_150_LSB, FG_ADC_RR_BATT_ID_150_MSB) RR_ADC_CHAN_RESISTANCE("batt_id", rradc_post_process_batt_id, - FG_ADC_RR_BATT_ID_5_LSB, FG_ADC_RR_BATT_ID_5_MSB) + FG_ADC_RR_BATT_ID_5_LSB, FG_ADC_RR_BATT_ID_5_MSB, + FG_ADC_RR_BATT_ID_STS) RR_ADC_CHAN_TEMP("batt_therm", &rradc_post_process_therm, - FG_ADC_RR_BATT_THERM_LSB, FG_ADC_RR_BATT_THERM_MSB) + FG_ADC_RR_BATT_THERM_LSB, FG_ADC_RR_BATT_THERM_MSB, + FG_ADC_RR_BATT_THERM_STS) RR_ADC_CHAN_TEMP("skin_temp", &rradc_post_process_therm, - FG_ADC_RR_SKIN_TEMP_LSB, FG_ADC_RR_SKIN_TEMP_MSB) + FG_ADC_RR_SKIN_TEMP_LSB, FG_ADC_RR_SKIN_TEMP_MSB, + FG_ADC_RR_AUX_THERM_STS) RR_ADC_CHAN_CURRENT("usbin_i", &rradc_post_process_curr, - FG_ADC_RR_USB_IN_V_LSB, FG_ADC_RR_USB_IN_V_MSB) + FG_ADC_RR_USB_IN_I_LSB, FG_ADC_RR_USB_IN_I_MSB, + FG_ADC_RR_USB_IN_I_STS) RR_ADC_CHAN_VOLT("usbin_v", &rradc_post_process_volt, - FG_ADC_RR_USB_IN_I_LSB, FG_ADC_RR_USB_IN_I_MSB) + FG_ADC_RR_USB_IN_V_LSB, FG_ADC_RR_USB_IN_V_MSB, + FG_ADC_RR_USB_IN_V_STS) RR_ADC_CHAN_CURRENT("dcin_i", &rradc_post_process_curr, - FG_ADC_RR_DC_IN_V_LSB, FG_ADC_RR_DC_IN_V_MSB) + FG_ADC_RR_DC_IN_I_LSB, FG_ADC_RR_DC_IN_I_MSB, + FG_ADC_RR_DC_IN_I_STS) RR_ADC_CHAN_VOLT("dcin_v", &rradc_post_process_volt, - FG_ADC_RR_DC_IN_I_LSB, FG_ADC_RR_DC_IN_I_MSB) + FG_ADC_RR_DC_IN_V_LSB, FG_ADC_RR_DC_IN_V_MSB, + FG_ADC_RR_DC_IN_V_STS) RR_ADC_CHAN_TEMP("die_temp", &rradc_post_process_die_temp, - FG_ADC_RR_PMI_DIE_TEMP_LSB, FG_ADC_RR_PMI_DIE_TEMP_MSB) + FG_ADC_RR_PMI_DIE_TEMP_LSB, FG_ADC_RR_PMI_DIE_TEMP_MSB, + FG_ADC_RR_PMI_DIE_TEMP_STS) RR_ADC_CHAN_TEMP("chg_temp", &rradc_post_process_chg_temp, - FG_ADC_RR_CHARGER_TEMP_LSB, FG_ADC_RR_CHARGER_TEMP_MSB) + FG_ADC_RR_CHARGER_TEMP_LSB, FG_ADC_RR_CHARGER_TEMP_MSB, + FG_ADC_RR_CHARGER_TEMP_STS) RR_ADC_CHAN_VOLT("gpio", &rradc_post_process_gpio, - FG_ADC_RR_GPIO_LSB, FG_ADC_RR_GPIO_MSB) + FG_ADC_RR_GPIO_LSB, FG_ADC_RR_GPIO_MSB, + FG_ADC_RR_GPIO_STS) + RR_ADC_CHAN_TEMP("chg_temp_hot", &rradc_post_process_chg_temp_hot, + FG_ADC_RR_CHARGER_HOT, FG_ADC_RR_CHARGER_HOT, + FG_ADC_RR_CHARGER_TEMP_STS) + RR_ADC_CHAN_TEMP("chg_temp_too_hot", &rradc_post_process_chg_temp_hot, + FG_ADC_RR_CHARGER_TOO_HOT, FG_ADC_RR_CHARGER_TOO_HOT, + FG_ADC_RR_CHARGER_TEMP_STS) + RR_ADC_CHAN_TEMP("skin_temp_hot", &rradc_post_process_skin_temp_hot, + FG_ADC_RR_SKIN_HOT, FG_ADC_RR_SKIN_HOT, + FG_ADC_RR_AUX_THERM_STS) + RR_ADC_CHAN_TEMP("skin_temp_too_hot", &rradc_post_process_skin_temp_hot, + FG_ADC_RR_SKIN_TOO_HOT, FG_ADC_RR_SKIN_TOO_HOT, + FG_ADC_RR_AUX_THERM_STS) }; static int rradc_do_conversion(struct rradc_chip *chip, @@ -411,15 +487,44 @@ static int rradc_do_conversion(struct rradc_chip *chip, int rc = 0, bytes_to_read = 0; u8 buf[6]; u16 offset = 0, batt_id_5 = 0, batt_id_15 = 0, batt_id_150 = 0; + u16 status = 0; mutex_lock(&chip->lock); + if ((prop->channel != RR_ADC_BATT_ID) && + (prop->channel != RR_ADC_CHG_HOT_TEMP) && + (prop->channel != RR_ADC_CHG_TOO_HOT_TEMP) && + (prop->channel != RR_ADC_SKIN_HOT_TEMP) && + (prop->channel != RR_ADC_SKIN_TOO_HOT_TEMP)) { + /* BATT_ID STS bit does not get set initially */ + status = rradc_chans[prop->channel].sts; + rc = rradc_read(chip, status, buf, 1); + if (rc < 0) { + pr_err("status read failed:%d\n", rc); + goto fail; + } + + buf[0] &= FG_RR_ADC_STS_CHANNEL_READING_MASK; + if (buf[0] != FG_RR_ADC_STS_CHANNEL_READING_MASK) { + pr_warn("%s is not ready; nothing to read\n", + rradc_chans[prop->channel].datasheet_name); + rc = -ENODATA; + goto fail; + } + } + offset = rradc_chans[prop->channel].lsb; if (prop->channel == RR_ADC_BATT_ID) bytes_to_read = 6; + else if ((prop->channel == RR_ADC_CHG_HOT_TEMP) || + (prop->channel == RR_ADC_CHG_TOO_HOT_TEMP) || + (prop->channel == RR_ADC_SKIN_HOT_TEMP) || + (prop->channel == RR_ADC_SKIN_TOO_HOT_TEMP)) + bytes_to_read = 1; else bytes_to_read = 2; + buf[0] = 0; rc = rradc_read(chip, offset, buf, bytes_to_read); if (rc) { pr_err("read data failed\n"); @@ -427,19 +532,33 @@ static int rradc_do_conversion(struct rradc_chip *chip, } if (prop->channel == RR_ADC_BATT_ID) { - batt_id_150 = (buf[4] << 8) | buf[5]; - batt_id_15 = (buf[2] << 8) | buf[3]; - batt_id_5 = (buf[0] << 8) | buf[1]; + batt_id_150 = (buf[5] << 8) | buf[4]; + batt_id_15 = (buf[3] << 8) | buf[2]; + batt_id_5 = (buf[1] << 8) | buf[0]; + if ((!batt_id_150) && (!batt_id_15) && (!batt_id_5)) { + pr_err("Invalid batt_id values with all zeros\n"); + rc = -EINVAL; + goto fail; + } + if (batt_id_150 <= FG_ADC_RR_BATT_ID_RANGE) { pr_debug("Batt_id_150 is chosen\n"); *data = batt_id_150; + prop->channel_data = FG_ADC_RR_BATT_ID_150_MA; } else if (batt_id_15 <= FG_ADC_RR_BATT_ID_RANGE) { pr_debug("Batt_id_15 is chosen\n"); *data = batt_id_15; + prop->channel_data = FG_ADC_RR_BATT_ID_15_MA; } else { pr_debug("Batt_id_5 is chosen\n"); *data = batt_id_5; + prop->channel_data = FG_ADC_RR_BATT_ID_5_MA; } + } else if ((prop->channel == RR_ADC_CHG_HOT_TEMP) || + (prop->channel == RR_ADC_CHG_TOO_HOT_TEMP) || + (prop->channel == RR_ADC_SKIN_HOT_TEMP) || + (prop->channel == RR_ADC_SKIN_TOO_HOT_TEMP)) { + *data = buf[0]; } else { *data = (buf[1] << 8) | buf[0]; } @@ -458,6 +577,11 @@ static int rradc_read_raw(struct iio_dev *indio_dev, u16 adc_code; int rc = 0; + if (chan->address >= RR_ADC_MAX) { + pr_err("Invalid channel index:%ld\n", chan->address); + return -EINVAL; + } + switch (mask) { case IIO_CHAN_INFO_PROCESSED: prop = &chip->chan_props[chan->address]; @@ -477,10 +601,6 @@ static int rradc_read_raw(struct iio_dev *indio_dev, *val = (int) adc_code; return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - *val = 0; - *val2 = 1000; - return IIO_VAL_INT_PLUS_MICRO; default: rc = -EINVAL; break; @@ -498,15 +618,11 @@ static int rradc_get_dt_data(struct rradc_chip *chip, struct device_node *node) { const struct rradc_channels *rradc_chan; struct iio_chan_spec *iio_chan; - struct device_node *child; - unsigned int index = 0, chan, base; + unsigned int i = 0, base; int rc = 0; struct rradc_chan_prop prop; - chip->nchannels = of_get_available_child_count(node); - if (!chip->nchannels || (chip->nchannels >= RR_ADC_MAX)) - return -EINVAL; - + chip->nchannels = RR_ADC_MAX; chip->iio_chans = devm_kcalloc(chip->dev, chip->nchannels, sizeof(*chip->iio_chans), GFP_KERNEL); if (!chip->iio_chans) @@ -529,30 +645,21 @@ static int rradc_get_dt_data(struct rradc_chip *chip, struct device_node *node) chip->base = base; iio_chan = chip->iio_chans; - for_each_available_child_of_node(node, child) { - rc = of_property_read_u32(child, "channel", &chan); - if (rc) { - dev_err(chip->dev, "invalid channel number %d\n", chan); - return rc; - } - - if (chan > RR_ADC_MAX || chan < RR_ADC_BATT_ID_5) { - dev_err(chip->dev, "invalid channel number %d\n", chan); - return -EINVAL; - } - - prop.channel = chan; - prop.scale = rradc_chans[chan].scale; - chip->chan_props[index] = prop; + for (i = 0; i < RR_ADC_MAX; i++) { + prop.channel = i; + prop.scale = rradc_chans[i].scale; + /* Private channel data used for selecting batt_id */ + prop.channel_data = 0; + chip->chan_props[i] = prop; - rradc_chan = &rradc_chans[chan]; + rradc_chan = &rradc_chans[i]; iio_chan->channel = prop.channel; iio_chan->datasheet_name = rradc_chan->datasheet_name; + iio_chan->extend_name = rradc_chan->datasheet_name; iio_chan->info_mask_separate = rradc_chan->info_mask; iio_chan->type = rradc_chan->type; - iio_chan->indexed = 1; - iio_chan->address = index++; + iio_chan->address = i; iio_chan++; } diff --git a/drivers/input/touchscreen/it7258_ts_i2c.c b/drivers/input/touchscreen/it7258_ts_i2c.c index eb0ff71f72d4..1fa28f1f0d9d 100644 --- a/drivers/input/touchscreen/it7258_ts_i2c.c +++ b/drivers/input/touchscreen/it7258_ts_i2c.c @@ -99,7 +99,7 @@ #define FW_WRITE_CHUNK_SIZE 128 #define FW_WRITE_RETRY_COUNT 4 #define CHIP_FLASH_SIZE 0x8000 -#define DEVICE_READY_MAX_WAIT 500 +#define DEVICE_READY_MAX_WAIT 10 /* result of reading with BUF_QUERY bits */ #define CMD_STATUS_BITS 0x07 @@ -124,8 +124,15 @@ #define IT_VTG_MIN_UV 1800000 #define IT_VTG_MAX_UV 1800000 +#define IT_ACTIVE_LOAD_UA 15000 #define IT_I2C_VTG_MIN_UV 2600000 #define IT_I2C_VTG_MAX_UV 3300000 +#define IT_I2C_ACTIVE_LOAD_UA 10000 + +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "pmx_ts_release" +#define IT_I2C_WAIT 1000 struct FingerData { uint8_t xLo; @@ -159,6 +166,8 @@ struct IT7260_ts_platform_data { unsigned int disp_maxx; unsigned int disp_maxy; unsigned num_of_fingers; + unsigned int reset_delay; + bool low_reset; }; struct IT7260_ts_data { @@ -184,6 +193,10 @@ struct IT7260_ts_data { struct notifier_block fb_notif; #endif struct dentry *dir; + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; }; /* Function declarations */ @@ -277,7 +290,7 @@ static bool IT7260_waitDeviceReady(bool forever, bool slowly) query = CMD_STATUS_BUSY; if (slowly) - mdelay(1000); + msleep(IT_I2C_WAIT); if (!forever) count--; @@ -1104,27 +1117,27 @@ static void IT7260_ts_work_func(struct work_struct *work) pm_relax(&gl_ts->client->dev); } -static bool IT7260_chipIdentify(void) +static int IT7260_chipIdentify(void) { static const uint8_t cmd_ident[] = {CMD_IDENT_CHIP}; static const uint8_t expected_id[] = {0x0A, 'I', 'T', 'E', '7', '2', '6', '0'}; uint8_t chip_id[10] = {0,}; - IT7260_waitDeviceReady(true, false); + IT7260_waitDeviceReady(false, false); if (!IT7260_i2cWriteNoReadyCheck(BUF_COMMAND, cmd_ident, sizeof(cmd_ident))) { dev_err(&gl_ts->client->dev, "failed to write CMD_IDENT_CHIP\n"); - return false; + return -ENODEV; } - IT7260_waitDeviceReady(true, false); + IT7260_waitDeviceReady(false, false); if (!IT7260_i2cReadNoReadyCheck(BUF_RESPONSE, chip_id, sizeof(chip_id))) { dev_err(&gl_ts->client->dev, "failed to read chip-id\n"); - return false; + return -ENODEV; } dev_info(&gl_ts->client->dev, "IT7260_chipIdentify read id: %02X %c%c%c%c%c%c%c %c%c\n", @@ -1132,7 +1145,7 @@ static bool IT7260_chipIdentify(void) chip_id[5], chip_id[6], chip_id[7], chip_id[8], chip_id[9]); if (memcmp(chip_id, expected_id, sizeof(expected_id))) - return false; + return -EINVAL; if (chip_id[8] == '5' && chip_id[9] == '6') dev_info(&gl_ts->client->dev, "rev BX3 found\n"); @@ -1142,7 +1155,227 @@ static bool IT7260_chipIdentify(void) dev_info(&gl_ts->client->dev, "unknown revision (0x%02X 0x%02X) found\n", chip_id[8], chip_id[9]); - return true; + return 0; +} + +static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA) +{ + return (regulator_count_voltages(reg) > 0) ? + regulator_set_optimum_mode(reg, load_uA) : 0; +} + +static int IT7260_regulator_configure(bool on) +{ + int retval; + + if (on == false) + goto hw_shutdown; + + gl_ts->vdd = devm_regulator_get(&gl_ts->client->dev, "vdd"); + if (IS_ERR(gl_ts->vdd)) { + dev_err(&gl_ts->client->dev, + "%s: Failed to get vdd regulator\n", __func__); + return PTR_ERR(gl_ts->vdd); + } + + if (regulator_count_voltages(gl_ts->vdd) > 0) { + retval = regulator_set_voltage(gl_ts->vdd, + IT_VTG_MIN_UV, IT_VTG_MAX_UV); + if (retval) { + dev_err(&gl_ts->client->dev, + "regulator set_vtg failed retval =%d\n", + retval); + goto err_set_vtg_vdd; + } + } + + gl_ts->avdd = devm_regulator_get(&gl_ts->client->dev, "avdd"); + if (IS_ERR(gl_ts->avdd)) { + dev_err(&gl_ts->client->dev, + "%s: Failed to get i2c regulator\n", __func__); + retval = PTR_ERR(gl_ts->avdd); + goto err_get_vtg_i2c; + } + + if (regulator_count_voltages(gl_ts->avdd) > 0) { + retval = regulator_set_voltage(gl_ts->avdd, + IT_I2C_VTG_MIN_UV, IT_I2C_VTG_MAX_UV); + if (retval) { + dev_err(&gl_ts->client->dev, + "reg set i2c vtg failed retval =%d\n", + retval); + goto err_set_vtg_i2c; + } + } + + return 0; + +err_set_vtg_i2c: +err_get_vtg_i2c: + if (regulator_count_voltages(gl_ts->vdd) > 0) + regulator_set_voltage(gl_ts->vdd, 0, IT_VTG_MAX_UV); +err_set_vtg_vdd: + return retval; + +hw_shutdown: + if (regulator_count_voltages(gl_ts->vdd) > 0) + regulator_set_voltage(gl_ts->vdd, 0, IT_VTG_MAX_UV); + if (regulator_count_voltages(gl_ts->avdd) > 0) + regulator_set_voltage(gl_ts->avdd, 0, IT_I2C_VTG_MAX_UV); + return 0; +}; + +static int IT7260_power_on(bool on) +{ + int retval; + + if (on == false) + goto power_off; + + retval = reg_set_optimum_mode_check(gl_ts->vdd, + IT_ACTIVE_LOAD_UA); + if (retval < 0) { + dev_err(&gl_ts->client->dev, + "Regulator vdd set_opt failed rc=%d\n", + retval); + return retval; + } + + retval = regulator_enable(gl_ts->vdd); + if (retval) { + dev_err(&gl_ts->client->dev, + "Regulator vdd enable failed rc=%d\n", + retval); + goto error_reg_en_vdd; + } + + retval = reg_set_optimum_mode_check(gl_ts->avdd, + IT_I2C_ACTIVE_LOAD_UA); + if (retval < 0) { + dev_err(&gl_ts->client->dev, + "Regulator avdd set_opt failed rc=%d\n", + retval); + goto error_reg_opt_i2c; + } + + retval = regulator_enable(gl_ts->avdd); + if (retval) { + dev_err(&gl_ts->client->dev, + "Regulator avdd enable failed rc=%d\n", + retval); + goto error_reg_en_avdd; + } + + return 0; + +error_reg_en_avdd: + reg_set_optimum_mode_check(gl_ts->avdd, 0); +error_reg_opt_i2c: + regulator_disable(gl_ts->vdd); +error_reg_en_vdd: + reg_set_optimum_mode_check(gl_ts->vdd, 0); + return retval; + +power_off: + reg_set_optimum_mode_check(gl_ts->vdd, 0); + regulator_disable(gl_ts->vdd); + reg_set_optimum_mode_check(gl_ts->avdd, 0); + regulator_disable(gl_ts->avdd); + + return 0; +} + +static int IT7260_gpio_configure(bool on) +{ + int retval = 0; + + if (on) { + if (gpio_is_valid(gl_ts->pdata->irq_gpio)) { + /* configure touchscreen irq gpio */ + retval = gpio_request(gl_ts->pdata->irq_gpio, + "ite_irq_gpio"); + if (retval) { + dev_err(&gl_ts->client->dev, + "unable to request irq gpio [%d]\n", + retval); + goto err_irq_gpio_req; + } + + retval = gpio_direction_input(gl_ts->pdata->irq_gpio); + if (retval) { + dev_err(&gl_ts->client->dev, + "unable to set direction for irq gpio [%d]\n", + retval); + goto err_irq_gpio_dir; + } + } else { + dev_err(&gl_ts->client->dev, + "irq gpio not provided\n"); + goto err_irq_gpio_req; + } + + if (gpio_is_valid(gl_ts->pdata->reset_gpio)) { + /* configure touchscreen reset out gpio */ + retval = gpio_request(gl_ts->pdata->reset_gpio, + "ite_reset_gpio"); + if (retval) { + dev_err(&gl_ts->client->dev, + "unable to request reset gpio [%d]\n", + retval); + goto err_reset_gpio_req; + } + + retval = gpio_direction_output( + gl_ts->pdata->reset_gpio, 1); + if (retval) { + dev_err(&gl_ts->client->dev, + "unable to set direction for reset gpio [%d]\n", + retval); + goto err_reset_gpio_dir; + } + + if (gl_ts->pdata->low_reset) + gpio_set_value(gl_ts->pdata->reset_gpio, 0); + else + gpio_set_value(gl_ts->pdata->reset_gpio, 1); + + msleep(gl_ts->pdata->reset_delay); + } else { + dev_err(&gl_ts->client->dev, + "reset gpio not provided\n"); + goto err_reset_gpio_req; + } + } else { + if (gpio_is_valid(gl_ts->pdata->irq_gpio)) + gpio_free(gl_ts->pdata->irq_gpio); + if (gpio_is_valid(gl_ts->pdata->reset_gpio)) { + /* + * This is intended to save leakage current + * only. Even if the call(gpio_direction_input) + * fails, only leakage current will be more but + * functionality will not be affected. + */ + retval = gpio_direction_input(gl_ts->pdata->reset_gpio); + if (retval) { + dev_err(&gl_ts->client->dev, + "unable to set direction for gpio reset [%d]\n", + retval); + } + gpio_free(gl_ts->pdata->reset_gpio); + } + } + + return 0; + +err_reset_gpio_dir: + if (gpio_is_valid(gl_ts->pdata->reset_gpio)) + gpio_free(gl_ts->pdata->reset_gpio); +err_reset_gpio_req: +err_irq_gpio_dir: + if (gpio_is_valid(gl_ts->pdata->irq_gpio)) + gpio_free(gl_ts->pdata->irq_gpio); +err_irq_gpio_req: + return retval; } #if CONFIG_OF @@ -1251,6 +1484,16 @@ static int IT7260_parse_dt(struct device *dev, snprintf(gl_ts->cfg_name, MAX_BUFFER_SIZE, "%s", (pdata->cfg_name != NULL) ? pdata->cfg_name : CFG_NAME); + rc = of_property_read_u32(np, "ite,reset-delay", &temp_val); + if (!rc) + pdata->reset_delay = temp_val; + else if (rc != -EINVAL) { + dev_err(dev, "Unable to read reset delay\n"); + return rc; + } + + pdata->low_reset = of_property_read_bool(np, "ite,low-reset"); + rc = IT7260_get_dt_coords(dev, "ite,display-coords", pdata); if (rc && (rc != -EINVAL)) return rc; @@ -1269,13 +1512,67 @@ static inline int IT7260_ts_parse_dt(struct device *dev, } #endif +static int IT7260_ts_pinctrl_init(struct IT7260_ts_data *ts_data) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + ts_data->ts_pinctrl = devm_pinctrl_get(&(ts_data->client->dev)); + if (IS_ERR_OR_NULL(ts_data->ts_pinctrl)) { + retval = PTR_ERR(ts_data->ts_pinctrl); + dev_dbg(&ts_data->client->dev, + "Target does not use pinctrl %d\n", retval); + goto err_pinctrl_get; + } + + ts_data->pinctrl_state_active + = pinctrl_lookup_state(ts_data->ts_pinctrl, + PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(ts_data->pinctrl_state_active)) { + retval = PTR_ERR(ts_data->pinctrl_state_active); + dev_err(&ts_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + ts_data->pinctrl_state_suspend + = pinctrl_lookup_state(ts_data->ts_pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(ts_data->pinctrl_state_suspend)) { + retval = PTR_ERR(ts_data->pinctrl_state_suspend); + dev_err(&ts_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + ts_data->pinctrl_state_release + = pinctrl_lookup_state(ts_data->ts_pinctrl, + PINCTRL_STATE_RELEASE); + if (IS_ERR_OR_NULL(ts_data->pinctrl_state_release)) { + retval = PTR_ERR(ts_data->pinctrl_state_release); + dev_dbg(&ts_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, retval); + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(ts_data->ts_pinctrl); +err_pinctrl_get: + ts_data->ts_pinctrl = NULL; + return retval; +} + static int IT7260_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { static const uint8_t cmd_start[] = {CMD_UNKNOWN_7}; struct IT7260_ts_platform_data *pdata; uint8_t rsp[2]; - int ret = -1; + int ret = -1, err; struct dentry *temp; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { @@ -1310,82 +1607,43 @@ static int IT7260_ts_probe(struct i2c_client *client, gl_ts->pdata = pdata; - gl_ts->vdd = devm_regulator_get(&gl_ts->client->dev, "vdd"); - if (IS_ERR(gl_ts->vdd)) { - dev_err(&client->dev, - "Regulator get failed vdd\n"); - gl_ts->vdd = NULL; - } else { - ret = regulator_set_voltage(gl_ts->vdd, - IT_VTG_MIN_UV, IT_VTG_MAX_UV); - if (ret) { - dev_err(&client->dev, - "Regulator set_vtg failed vdd %d\n", ret); - return ret; - } - } - - gl_ts->avdd = devm_regulator_get(&gl_ts->client->dev, "avdd"); - if (IS_ERR(gl_ts->avdd)) { - dev_err(&client->dev, - "Regulator get failed avdd\n"); - gl_ts->avdd = NULL; - } else { - ret = regulator_set_voltage(gl_ts->avdd, IT_I2C_VTG_MIN_UV, - IT_I2C_VTG_MAX_UV); - if (ret) { - dev_err(&client->dev, - "Regulator get failed avdd %d\n", ret); - return ret; - } + ret = IT7260_regulator_configure(true); + if (ret < 0) { + dev_err(&client->dev, "Failed to configure regulators\n"); + goto err_reg_configure; } - if (gl_ts->vdd) { - ret = regulator_enable(gl_ts->vdd); - if (ret) { - dev_err(&gl_ts->client->dev, - "Regulator vdd enable failed ret=%d\n", ret); - return ret; - } + ret = IT7260_power_on(true); + if (ret < 0) { + dev_err(&client->dev, "Failed to power on\n"); + goto err_power_device; } - if (gl_ts->avdd) { - ret = regulator_enable(gl_ts->avdd); - if (ret) { + ret = IT7260_ts_pinctrl_init(gl_ts); + if (!ret && gl_ts->ts_pinctrl) { + /* + * Pinctrl handle is optional. If pinctrl handle is found + * let pins to be configured in active state. If not + * found continue further without error. + */ + ret = pinctrl_select_state(gl_ts->ts_pinctrl, + gl_ts->pinctrl_state_active); + if (ret < 0) { dev_err(&gl_ts->client->dev, - "Regulator avdd enable failed ret=%d\n", ret); - return ret; - } - } - - /* reset gpio info */ - if (gpio_is_valid(pdata->reset_gpio)) { - if (gpio_request(pdata->reset_gpio, "ite_reset_gpio")) { - dev_err(&client->dev, - "gpio_request failed for reset GPIO\n"); - return -EINVAL; - } - if (gpio_direction_output(pdata->reset_gpio, 0)) { - dev_err(&client->dev, - "gpio_direction_output for reset GPIO\n"); - return -EINVAL; + "failed to select pin to active state %d", + ret); } - dev_dbg(&gl_ts->client->dev, "Reset GPIO %d\n", - pdata->reset_gpio); } else { - return pdata->reset_gpio; - } - - /* irq gpio info */ - if (gpio_is_valid(pdata->irq_gpio)) { - dev_dbg(&gl_ts->client->dev, "IRQ GPIO %d, IRQ # %d\n", - pdata->irq_gpio, gpio_to_irq(pdata->irq_gpio)); - } else { - return pdata->irq_gpio; + ret = IT7260_gpio_configure(true); + if (ret < 0) { + dev_err(&client->dev, "Failed to configure gpios\n"); + goto err_gpio_config; + } } - if (!IT7260_chipIdentify()) { - dev_err(&client->dev, "Failed to identify chip!!!"); + ret = IT7260_chipIdentify(); + if (ret) { + dev_err(&client->dev, "Failed to identify chip %d!!!", ret); goto err_identification_fail; } @@ -1455,9 +1713,9 @@ static int IT7260_ts_probe(struct i2c_client *client, #endif IT7260_i2cWriteNoReadyCheck(BUF_COMMAND, cmd_start, sizeof(cmd_start)); - mdelay(10); + msleep(pdata->reset_delay); IT7260_i2cReadNoReadyCheck(BUF_RESPONSE, rsp, sizeof(rsp)); - mdelay(10); + msleep(pdata->reset_delay); gl_ts->dir = debugfs_create_dir(DEBUGFS_DIR_NAME, NULL); if (gl_ts->dir == NULL || IS_ERR(gl_ts->dir)) { @@ -1506,21 +1764,39 @@ err_input_register: err_input_alloc: err_identification_fail: - if (gpio_is_valid(pdata->reset_gpio)) - gpio_free(pdata->reset_gpio); - if (gpio_is_valid(pdata->irq_gpio)) - gpio_free(pdata->irq_gpio); + if (gl_ts->ts_pinctrl) { + if (IS_ERR_OR_NULL(gl_ts->pinctrl_state_release)) { + devm_pinctrl_put(gl_ts->ts_pinctrl); + gl_ts->ts_pinctrl = NULL; + } else { + err = pinctrl_select_state(gl_ts->ts_pinctrl, + gl_ts->pinctrl_state_release); + if (err) + dev_err(&gl_ts->client->dev, + "failed to select relase pinctrl state %d\n", + err); + } + } else { + if (gpio_is_valid(pdata->reset_gpio)) + gpio_free(pdata->reset_gpio); + if (gpio_is_valid(pdata->irq_gpio)) + gpio_free(pdata->irq_gpio); + } - regulator_disable(gl_ts->vdd); - regulator_disable(gl_ts->avdd); - regulator_put(gl_ts->vdd); - regulator_put(gl_ts->avdd); +err_gpio_config: + IT7260_power_on(false); + +err_power_device: + IT7260_regulator_configure(false); +err_reg_configure: return ret; } static int IT7260_ts_remove(struct i2c_client *client) { + int ret; + debugfs_remove_recursive(gl_ts->dir); #if defined(CONFIG_FB) if (fb_unregister_client(&gl_ts->fb_notif)) @@ -1536,14 +1812,27 @@ static int IT7260_ts_remove(struct i2c_client *client) cancel_work_sync(&gl_ts->work_pm_relax); device_init_wakeup(&client->dev, false); } - if (gpio_is_valid(gl_ts->pdata->reset_gpio)) - gpio_free(gl_ts->pdata->reset_gpio); - if (gpio_is_valid(gl_ts->pdata->irq_gpio)) - gpio_free(gl_ts->pdata->irq_gpio); - regulator_disable(gl_ts->vdd); - regulator_disable(gl_ts->avdd); - regulator_put(gl_ts->vdd); - regulator_put(gl_ts->avdd); + if (gl_ts->ts_pinctrl) { + if (IS_ERR_OR_NULL(gl_ts->pinctrl_state_release)) { + devm_pinctrl_put(gl_ts->ts_pinctrl); + gl_ts->ts_pinctrl = NULL; + } else { + ret = pinctrl_select_state(gl_ts->ts_pinctrl, + gl_ts->pinctrl_state_release); + if (ret) + dev_err(&gl_ts->client->dev, + "failed to select relase pinctrl state %d\n", + ret); + } + } else { + if (gpio_is_valid(gl_ts->pdata->reset_gpio)) + gpio_free(gl_ts->pdata->reset_gpio); + if (gpio_is_valid(gl_ts->pdata->irq_gpio)) + gpio_free(gl_ts->pdata->irq_gpio); + } + IT7260_power_on(false); + IT7260_regulator_configure(false); + return 0; } @@ -1572,6 +1861,8 @@ static int fb_notifier_callback(struct notifier_block *self, #ifdef CONFIG_PM static int IT7260_ts_resume(struct device *dev) { + int retval; + if (device_may_wakeup(dev)) { if (gl_ts->device_needs_wakeup) { gl_ts->device_needs_wakeup = false; @@ -1580,23 +1871,38 @@ static int IT7260_ts_resume(struct device *dev) return 0; } + if (gl_ts->ts_pinctrl) { + retval = pinctrl_select_state(gl_ts->ts_pinctrl, + gl_ts->pinctrl_state_active); + if (retval < 0) { + dev_err(dev, "Cannot get default pinctrl state %d\n", + retval); + goto err_pinctrl_select_suspend; + } + } + enable_irq(gl_ts->client->irq); gl_ts->suspended = false; return 0; + +err_pinctrl_select_suspend: + return retval; } static int IT7260_ts_suspend(struct device *dev) { + int retval; + if (gl_ts->fw_cfg_uploading) { dev_dbg(dev, "Fw/cfg uploading. Can't go to suspend.\n"); return -EBUSY; } - /* put the device in low power idle mode */ - IT7260_ts_chipLowPowerMode(PWR_CTL_LOW_POWER_MODE); - if (device_may_wakeup(dev)) { if (!gl_ts->device_needs_wakeup) { + /* put the device in low power idle mode */ + IT7260_ts_chipLowPowerMode(PWR_CTL_LOW_POWER_MODE); + gl_ts->device_needs_wakeup = true; enable_irq_wake(gl_ts->client->irq); } @@ -1606,9 +1912,23 @@ static int IT7260_ts_suspend(struct device *dev) disable_irq(gl_ts->client->irq); IT7260_ts_release_all(); + + if (gl_ts->ts_pinctrl) { + retval = pinctrl_select_state(gl_ts->ts_pinctrl, + gl_ts->pinctrl_state_suspend); + if (retval < 0) { + dev_err(dev, "Cannot get idle pinctrl state %d\n", + retval); + goto err_pinctrl_select_suspend; + } + } + gl_ts->suspended = true; return 0; + +err_pinctrl_select_suspend: + return retval; } static const struct dev_pm_ops IT7260_ts_dev_pm_ops = { diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 022473473971..190d294197a7 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -588,9 +588,6 @@ static int gic_populate_rdist(void) u64 offset = ptr - gic_data.redist_regions[i].redist_base; gic_data_rdist_rd_base() = ptr; gic_data_rdist()->phys_base = gic_data.redist_regions[i].phys_base + offset; - pr_info("CPU%d: found redistributor %lx region %d:%pa\n", - smp_processor_id(), mpidr, i, - &gic_data_rdist()->phys_base); return 0; } diff --git a/drivers/mfd/wcd934x-regmap.c b/drivers/mfd/wcd934x-regmap.c index 9a09f87d8472..398f0086537a 100644 --- a/drivers/mfd/wcd934x-regmap.c +++ b/drivers/mfd/wcd934x-regmap.c @@ -1848,6 +1848,15 @@ static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg) if (reg_tbl && reg_tbl[reg_offset] == WCD934X_READ) return true; + /* + * Need to mark volatile for registers that are writable but + * only few bits are read-only + */ + switch (reg) { + case WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL: + return true; + } + return false; } diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c index 7d1b9e9121a9..0c6f1de2465b 100644 --- a/drivers/misc/hdcp.c +++ b/drivers/misc/hdcp.c @@ -456,6 +456,7 @@ struct hdcp_lib_handle { atomic_t hdcp_off; uint32_t session_id; bool legacy_app; + enum hdcp_device_type device_type; struct task_struct *thread; struct completion topo_wait; @@ -901,7 +902,7 @@ static int hdcp_lib_session_init(struct hdcp_lib_handle *handle) req_buf = (struct hdcp_lib_session_init_req *)handle->qseecom_handle->sbuf; req_buf->commandid = HDCP_SESSION_INIT; - req_buf->deviceid = HDCP_TXMTR_HDMI; + req_buf->deviceid = handle->device_type; rsp_buf = (struct hdcp_lib_session_init_rsp *) (handle->qseecom_handle->sbuf + QSEECOM_ALIGN(sizeof(struct hdcp_lib_session_init_req))); @@ -2060,6 +2061,7 @@ int hdcp_library_register(struct hdcp_register_data *data) handle->tethered = data->tethered; handle->hdcp_app_init = NULL; handle->hdcp_txmtr_init = NULL; + handle->device_type = data->device_type; pr_debug("tethered %d\n", handle->tethered); diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c index 208a4ce1e40e..7a9307294a6d 100644 --- a/drivers/platform/msm/ipa/ipa_api.c +++ b/drivers/platform/msm/ipa/ipa_api.c @@ -369,6 +369,24 @@ int ipa_reset_endpoint(u32 clnt_hdl) } EXPORT_SYMBOL(ipa_reset_endpoint); +/** +* ipa_disable_endpoint() - Disable an endpoint from IPA perspective +* @clnt_hdl: [in] IPA client handle +* +* Returns: 0 on success, negative on failure +* +* Note: Should not be called from atomic context +*/ +int ipa_disable_endpoint(u32 clnt_hdl) +{ + int ret; + + IPA_API_DISPATCH_RETURN(ipa_disable_endpoint, clnt_hdl); + + return ret; +} +EXPORT_SYMBOL(ipa_disable_endpoint); + /** * ipa_cfg_ep - IPA end-point configuration diff --git a/drivers/platform/msm/ipa/ipa_api.h b/drivers/platform/msm/ipa/ipa_api.h index 862bdc475025..3c2471dd11dd 100644 --- a/drivers/platform/msm/ipa/ipa_api.h +++ b/drivers/platform/msm/ipa/ipa_api.h @@ -26,6 +26,8 @@ struct ipa_api_controller { int (*ipa_clear_endpoint_delay)(u32 clnt_hdl); + int (*ipa_disable_endpoint)(u32 clnt_hdl); + int (*ipa_cfg_ep)(u32 clnt_hdl, const struct ipa_ep_cfg *ipa_ep_cfg); int (*ipa_cfg_ep_nat)(u32 clnt_hdl, diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_client.c b/drivers/platform/msm/ipa/ipa_v2/ipa_client.c index 64246ac4eec0..66e329a03df7 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_client.c @@ -560,22 +560,30 @@ int ipa2_disconnect(u32 clnt_hdl) if (!ep->keep_ipa_awake) IPA_ACTIVE_CLIENTS_INC_EP(client_type); - /* Set Disconnect in Progress flag. */ - spin_lock(&ipa_ctx->disconnect_lock); - ep->disconnect_in_progress = true; - spin_unlock(&ipa_ctx->disconnect_lock); - - /* Notify uc to stop monitoring holb on USB BAM Producer pipe. */ - if (IPA_CLIENT_IS_USB_CONS(ep->client)) { - ipa_uc_monitor_holb(ep->client, false); - IPADBG("Disabling holb monitor for client: %d\n", ep->client); - } + /* For USB 2.0 controller, first the ep will be disabled. + * so this sequence is not needed again when disconnecting the pipe. + */ + if (!ep->ep_disabled) { + /* Set Disconnect in Progress flag. */ + spin_lock(&ipa_ctx->disconnect_lock); + ep->disconnect_in_progress = true; + spin_unlock(&ipa_ctx->disconnect_lock); + + /* Notify uc to stop monitoring holb on USB BAM + * Producer pipe. + */ + if (IPA_CLIENT_IS_USB_CONS(ep->client)) { + ipa_uc_monitor_holb(ep->client, false); + IPADBG("Disabling holb monitor for client: %d\n", + ep->client); + } - result = ipa_disable_data_path(clnt_hdl); - if (result) { - IPAERR("disable data path failed res=%d clnt=%d.\n", result, - clnt_hdl); - return -EPERM; + result = ipa_disable_data_path(clnt_hdl); + if (result) { + IPAERR("disable data path failed res=%d clnt=%d.\n", + result, clnt_hdl); + return -EPERM; + } } result = sps_disconnect(ep->ep_hdl); @@ -784,6 +792,82 @@ int ipa2_clear_endpoint_delay(u32 clnt_hdl) } /** + * ipa2_disable_endpoint() - low-level IPA client disable endpoint + * @clnt_hdl: [in] opaque client handle assigned by IPA to client + * + * Should be called by the driver of the peripheral that wants to + * disable the pipe from IPA in BAM-BAM mode. + * + * Returns: 0 on success, negative on failure + * + * Note: Should not be called from atomic context + */ +int ipa2_disable_endpoint(u32 clnt_hdl) +{ + int result; + struct ipa_ep_context *ep; + enum ipa_client_type client_type; + unsigned long bam; + + if (unlikely(!ipa_ctx)) { + IPAERR("IPA driver was not initialized\n"); + return -EINVAL; + } + + if (clnt_hdl >= ipa_ctx->ipa_num_pipes || + ipa_ctx->ep[clnt_hdl].valid == 0) { + IPAERR("bad parm.\n"); + return -EINVAL; + } + + ep = &ipa_ctx->ep[clnt_hdl]; + client_type = ipa2_get_client_mapping(clnt_hdl); + IPA_ACTIVE_CLIENTS_INC_EP(client_type); + + /* Set Disconnect in Progress flag. */ + spin_lock(&ipa_ctx->disconnect_lock); + ep->disconnect_in_progress = true; + spin_unlock(&ipa_ctx->disconnect_lock); + + /* Notify uc to stop monitoring holb on USB BAM Producer pipe. */ + if (IPA_CLIENT_IS_USB_CONS(ep->client)) { + ipa_uc_monitor_holb(ep->client, false); + IPADBG("Disabling holb monitor for client: %d\n", ep->client); + } + + result = ipa_disable_data_path(clnt_hdl); + if (result) { + IPAERR("disable data path failed res=%d clnt=%d.\n", result, + clnt_hdl); + goto fail; + } + + if (IPA_CLIENT_IS_CONS(ep->client)) + bam = ep->connect.source; + else + bam = ep->connect.destination; + + result = sps_pipe_reset(bam, clnt_hdl); + if (result) { + IPAERR("SPS pipe reset failed.\n"); + goto fail; + } + + ep->ep_disabled = true; + + IPA_ACTIVE_CLIENTS_DEC_EP(client_type); + + IPADBG("client (ep: %d) disabled\n", clnt_hdl); + + return 0; + +fail: + IPA_ACTIVE_CLIENTS_DEC_EP(client_type); + return -EPERM; +} + + +/** * ipa_sps_connect_safe() - connect endpoint from BAM prespective * @h: [in] sps pipe handle * @connect: [in] sps connect parameters diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c index 50caeb9a19ee..0bb863037772 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c @@ -3169,7 +3169,7 @@ static int ipa_assign_policy_v2(struct ipa_sys_connect_params *in, IPA_GENERIC_RX_BUFF_SZ( ipa_adjust_ra_buff_base_sz( in->ipa_ep_cfg.aggr. - aggr_byte_limit)); + aggr_byte_limit - IPA_HEADROOM)); in->ipa_ep_cfg.aggr. aggr_byte_limit = sys->rx_buff_sz < in-> diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c index 790a0b41147e..62e026262663 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c @@ -581,7 +581,8 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, return 0; bad_len: - hdr_entry->ref_cnt--; + if (add_ref_hdr) + hdr_entry->ref_cnt--; entry->cookie = 0; kmem_cache_free(ipa_ctx->hdr_proc_ctx_cache, entry); return -EPERM; @@ -761,7 +762,7 @@ static int __ipa_del_hdr_proc_ctx(u32 proc_ctx_hdl, bool release_hdr) } if (release_hdr) - __ipa_release_hdr(entry->hdr->id); + __ipa_del_hdr(entry->hdr->id); /* move the offset entry to appropriate free list */ list_move(&entry->offset_entry->link, @@ -1089,12 +1090,19 @@ int ipa2_reset_hdr(void) &ipa_ctx->hdr_tbl.head_hdr_entry_list, link) { /* do not remove the default header */ - if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME)) + if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME)) { + if (entry->is_hdr_proc_ctx) { + mutex_unlock(&ipa_ctx->lock); + WARN_ON(1); + IPAERR("default header is proc ctx\n"); + return -EFAULT; + } continue; + } if (ipa_id_find(entry->id) == NULL) { - WARN_ON(1); mutex_unlock(&ipa_ctx->lock); + WARN_ON(1); return -EFAULT; } if (entry->is_hdr_proc_ctx) { @@ -1147,8 +1155,8 @@ int ipa2_reset_hdr(void) link) { if (ipa_id_find(ctx_entry->id) == NULL) { - WARN_ON(1); mutex_unlock(&ipa_ctx->lock); + WARN_ON(1); return -EFAULT; } list_del(&ctx_entry->link); @@ -1311,8 +1319,8 @@ int ipa2_put_hdr(u32 hdr_hdl) goto bail; } - if (entry == NULL || entry->cookie != IPA_COOKIE) { - IPAERR("bad params\n"); + if (entry->cookie != IPA_COOKIE) { + IPAERR("invalid header entry\n"); result = -EINVAL; goto bail; } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h index 0a0b23815ce3..6a5b779b24f8 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h @@ -242,7 +242,7 @@ struct ipa_rt_tbl { * @is_partial: flag indicating if header table entry is partial * @is_hdr_proc_ctx: false - hdr entry resides in hdr table, * true - hdr entry resides in DDR and pointed to by proc ctx - * @phys_base: physical address of entry in SRAM when is_hdr_proc_ctx is true, + * @phys_base: physical address of entry in DDR when is_hdr_proc_ctx is true, * else 0 * @proc_ctx: processing context header * @offset_entry: entry's offset @@ -553,6 +553,7 @@ struct ipa_ep_context { bool switch_to_intr; int inactive_cycles; u32 eot_in_poll_err; + bool ep_disabled; /* sys MUST be the last element of this struct */ struct ipa_sys_context *sys; @@ -1431,6 +1432,11 @@ int ipa2_reset_endpoint(u32 clnt_hdl); int ipa2_clear_endpoint_delay(u32 clnt_hdl); /* + * Disable ep + */ +int ipa2_disable_endpoint(u32 clnt_hdl); + +/* * Configuration */ int ipa2_cfg_ep(u32 clnt_hdl, const struct ipa_ep_cfg *ipa_ep_cfg); diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c index 7c10c4cee150..e8f25c9c23d3 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c @@ -276,8 +276,6 @@ fail_ep_exists: */ int ipa2_disconnect_mhi_pipe(u32 clnt_hdl) { - struct ipa_ep_context *ep; - IPA_MHI_FUNC_ENTRY(); if (clnt_hdl >= ipa_ctx->ipa_num_pipes) { @@ -290,7 +288,8 @@ int ipa2_disconnect_mhi_pipe(u32 clnt_hdl) return -EINVAL; } - ep->valid = 0; + ipa_ctx->ep[clnt_hdl].valid = 0; + ipa_delete_dflt_flt_rules(clnt_hdl); IPA_MHI_DBG("client (ep: %d) disconnected\n", clnt_hdl); @@ -302,14 +301,13 @@ int ipa2_mhi_resume_channels_internal(enum ipa_client_type client, bool LPTransitionRejected, bool brstmode_enabled, union __packed gsi_channel_scratch ch_scratch, u8 index) { - int i; int res; IPA_MHI_FUNC_ENTRY(); res = ipa_uc_mhi_resume_channel(index, LPTransitionRejected); if (res) { - IPA_MHI_ERR("failed to suspend channel %d error %d\n", - i, res); + IPA_MHI_ERR("failed to suspend channel %u error %d\n", + index, res); return res; } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c index 9d4704ded0c3..15476f38cf44 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c @@ -1008,6 +1008,10 @@ static int __ipa_add_rt_rule(enum ipa_ip_type ip, const char *name, return 0; ipa_insert_failed: + if (entry->hdr) + entry->hdr->ref_cnt--; + else if (entry->proc_ctx) + entry->proc_ctx->ref_cnt--; list_del(&entry->link); kmem_cache_free(ipa_ctx->rt_rule_cache, entry); error: diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c index 69052eb289bb..1d88082352c6 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c @@ -4949,6 +4949,7 @@ int ipa2_bind_api_controller(enum ipa_hw_type ipa_hw_type, api_ctrl->ipa_disconnect = ipa2_disconnect; api_ctrl->ipa_reset_endpoint = ipa2_reset_endpoint; api_ctrl->ipa_clear_endpoint_delay = ipa2_clear_endpoint_delay; + api_ctrl->ipa_disable_endpoint = ipa2_disable_endpoint; api_ctrl->ipa_cfg_ep = ipa2_cfg_ep; api_ctrl->ipa_cfg_ep_nat = ipa2_cfg_ep_nat; api_ctrl->ipa_cfg_ep_hdr = ipa2_cfg_ep_hdr; diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index 50e820992f29..2420dd78b4c0 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -1144,14 +1144,16 @@ static void apps_ipa_tx_complete_notify(void *priv, struct net_device *dev = (struct net_device *)priv; struct wwan_private *wwan_ptr; - if (evt != IPA_WRITE_DONE) { - IPAWANDBG("unsupported event on Tx callback\n"); + if (dev != ipa_netdevs[0]) { + IPAWANDBG("Received pre-SSR packet completion\n"); + dev_kfree_skb_any(skb); return; } - if (dev != ipa_netdevs[0]) { - IPAWANDBG("Received pre-SSR packet completion\n"); + if (evt != IPA_WRITE_DONE) { + IPAWANERR("unsupported evt on Tx callback, Drop the packet\n"); dev_kfree_skb_any(skb); + dev->stats.tx_dropped++; return; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 3dd9738f67c7..33066e8b9c19 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -1899,44 +1899,43 @@ static int ipa3_q6_clean_q6_tables(void) if (ipa3_q6_clean_q6_flt_tbls(IPA_IP_v4, IPA_RULE_HASHABLE)) { IPAERR("failed to clean q6 flt tbls (v4/hashable)\n"); - goto bail_desc; + return -EFAULT; } if (ipa3_q6_clean_q6_flt_tbls(IPA_IP_v6, IPA_RULE_HASHABLE)) { IPAERR("failed to clean q6 flt tbls (v6/hashable)\n"); - goto bail_desc; + return -EFAULT; } if (ipa3_q6_clean_q6_flt_tbls(IPA_IP_v4, IPA_RULE_NON_HASHABLE)) { IPAERR("failed to clean q6 flt tbls (v4/non-hashable)\n"); - goto bail_desc; + return -EFAULT; } if (ipa3_q6_clean_q6_flt_tbls(IPA_IP_v6, IPA_RULE_NON_HASHABLE)) { IPAERR("failed to clean q6 flt tbls (v6/non-hashable)\n"); - goto bail_desc; + return -EFAULT; } if (ipa3_q6_clean_q6_rt_tbls(IPA_IP_v4, IPA_RULE_HASHABLE)) { IPAERR("failed to clean q6 rt tbls (v4/hashable)\n"); - goto bail_desc; + return -EFAULT; } if (ipa3_q6_clean_q6_rt_tbls(IPA_IP_v6, IPA_RULE_HASHABLE)) { IPAERR("failed to clean q6 rt tbls (v6/hashable)\n"); - goto bail_desc; + return -EFAULT; } if (ipa3_q6_clean_q6_rt_tbls(IPA_IP_v4, IPA_RULE_NON_HASHABLE)) { IPAERR("failed to clean q6 rt tbls (v4/non-hashable)\n"); - goto bail_desc; + return -EFAULT; } if (ipa3_q6_clean_q6_rt_tbls(IPA_IP_v6, IPA_RULE_NON_HASHABLE)) { IPAERR("failed to clean q6 rt tbls (v6/non-hashable)\n"); - goto bail_desc; + return -EFAULT; } /* Flush rules cache */ desc = kzalloc(sizeof(struct ipa3_desc), GFP_KERNEL); if (!desc) { IPAERR("failed to allocate memory\n"); - retval = -ENOMEM; - goto bail_dma; + return -ENOMEM; } flush.v4_flt = true; @@ -1953,6 +1952,7 @@ static int ipa3_q6_clean_q6_tables(void) ®_write_cmd, false); if (!cmd_pyld) { IPAERR("fail construct register_write imm cmd\n"); + retval = -EFAULT; goto bail_desc; } desc->opcode = @@ -1969,9 +1969,9 @@ static int ipa3_q6_clean_q6_tables(void) } ipahal_destroy_imm_cmd(cmd_pyld); + bail_desc: kfree(desc); -bail_dma: IPADBG("Done - retval = %d\n", retval); return retval; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c index 029647213531..11da023c9d6a 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c @@ -418,7 +418,8 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, return 0; bad_len: - hdr_entry->ref_cnt--; + if (add_ref_hdr) + hdr_entry->ref_cnt--; entry->cookie = 0; kmem_cache_free(ipa3_ctx->hdr_proc_ctx_cache, entry); return -EPERM; @@ -589,7 +590,7 @@ static int __ipa3_del_hdr_proc_ctx(u32 proc_ctx_hdl, bool release_hdr) } if (release_hdr) - __ipa3_release_hdr(entry->hdr->id); + __ipa3_del_hdr(entry->hdr->id); /* move the offset entry to appropriate free list */ list_move(&entry->offset_entry->link, @@ -893,12 +894,19 @@ int ipa3_reset_hdr(void) &ipa3_ctx->hdr_tbl.head_hdr_entry_list, link) { /* do not remove the default header */ - if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME)) + if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME)) { + if (entry->is_hdr_proc_ctx) { + IPAERR("default header is proc ctx\n"); + mutex_unlock(&ipa3_ctx->lock); + WARN_ON(1); + return -EFAULT; + } continue; + } if (ipa3_id_find(entry->id) == NULL) { - WARN_ON(1); mutex_unlock(&ipa3_ctx->lock); + WARN_ON(1); return -EFAULT; } if (entry->is_hdr_proc_ctx) { @@ -951,8 +959,8 @@ int ipa3_reset_hdr(void) link) { if (ipa3_id_find(ctx_entry->id) == NULL) { - WARN_ON(1); mutex_unlock(&ipa3_ctx->lock); + WARN_ON(1); return -EFAULT; } list_del(&ctx_entry->link); @@ -1115,8 +1123,8 @@ int ipa3_put_hdr(u32 hdr_hdl) goto bail; } - if (entry == NULL || entry->cookie != IPA_COOKIE) { - IPAERR("bad params\n"); + if (entry->cookie != IPA_COOKIE) { + IPAERR("invalid header entry\n"); result = -EINVAL; goto bail; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index cce05cf31b3c..97a3117d44e9 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -271,7 +271,7 @@ struct ipa3_rt_tbl { * @is_partial: flag indicating if header table entry is partial * @is_hdr_proc_ctx: false - hdr entry resides in hdr table, * true - hdr entry resides in DDR and pointed to by proc ctx - * @phys_base: physical address of entry in SRAM when is_hdr_proc_ctx is true, + * @phys_base: physical address of entry in DDR when is_hdr_proc_ctx is true, * else 0 * @proc_ctx: processing context header * @offset_entry: entry's offset diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c index 14e2f1f4c510..e83c249ad425 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c @@ -549,11 +549,6 @@ int ipa3_mhi_resume_channels_internal(enum ipa_client_type client, return res; } } - if (res) { - IPA_MHI_ERR("failed to resume channel error %d\n", - res); - return res; - } res = gsi_start_channel(ep->gsi_chan_hdl); if (res) { diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c index 138db3dbde84..b06e33a8258a 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c @@ -957,6 +957,10 @@ static int __ipa_finish_rt_rule_add(struct ipa3_rt_entry *entry, u32 *rule_hdl, return 0; ipa_insert_failed: + if (entry->hdr) + entry->hdr->ref_cnt--; + else if (entry->proc_ctx) + entry->proc_ctx->ref_cnt--; idr_remove(&tbl->rule_ids, entry->rule_id); list_del(&entry->link); kmem_cache_free(ipa3_ctx->rt_rule_cache, entry); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index 2c2708c4e2f3..5499eba92b1c 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -3046,6 +3046,7 @@ int ipa3_bind_api_controller(enum ipa_hw_type ipa_hw_type, api_ctrl->ipa_disconnect = ipa3_disconnect; api_ctrl->ipa_reset_endpoint = ipa3_reset_endpoint; api_ctrl->ipa_clear_endpoint_delay = ipa3_clear_endpoint_delay; + api_ctrl->ipa_disable_endpoint = NULL; api_ctrl->ipa_cfg_ep = ipa3_cfg_ep; api_ctrl->ipa_cfg_ep_nat = ipa3_cfg_ep_nat; api_ctrl->ipa_cfg_ep_hdr = ipa3_cfg_ep_hdr; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c index 4f6097c6da35..6c4d14b093c3 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c @@ -1215,7 +1215,10 @@ int ipahal_cp_proc_ctx_to_hw_buff(enum ipa_hdr_proc_type type, (!phys_base && !hdr_base_addr) || !hdr_base_addr || ((is_hdr_proc_ctx == false) && !offset_entry)) { - IPAHAL_ERR("failed on validating params"); + IPAHAL_ERR( + "invalid input: hdr_len:%u phys_base:%pad hdr_base_addr:%u is_hdr_proc_ctx:%d offset_entry:%pK\n" + , hdr_len, &phys_base, hdr_base_addr + , is_hdr_proc_ctx, offset_entry); return -EINVAL; } diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index a4eab02cb571..aebdaab3ac77 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -1157,14 +1157,16 @@ static void apps_ipa_tx_complete_notify(void *priv, struct net_device *dev = (struct net_device *)priv; struct ipa3_wwan_private *wwan_ptr; - if (evt != IPA_WRITE_DONE) { - IPAWANDBG("unsupported event on Tx callback\n"); + if (dev != IPA_NETDEV()) { + IPAWANDBG("Received pre-SSR packet completion\n"); + dev_kfree_skb_any(skb); return; } - if (dev != IPA_NETDEV()) { - IPAWANDBG("Received pre-SSR packet completion\n"); + if (evt != IPA_WRITE_DONE) { + IPAWANERR("unsupported evt on Tx callback, Drop the packet\n"); dev_kfree_skb_any(skb); + dev->stats.tx_dropped++; return; } diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 61f611296ad6..a45a5d103040 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -265,6 +265,8 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(typec_power_role), POWER_SUPPLY_ATTR(pd_allowed), POWER_SUPPLY_ATTR(pd_active), + POWER_SUPPLY_ATTR(charger_temp), + POWER_SUPPLY_ATTR(charger_temp_max), /* Local extensions of type int64_t */ POWER_SUPPLY_ATTR(charge_counter_ext), /* Properties of type `const char *' */ diff --git a/drivers/power/qcom-charger/qpnp-qnovo.c b/drivers/power/qcom-charger/qpnp-qnovo.c index d50188a5efbf..2418b112d670 100644 --- a/drivers/power/qcom-charger/qpnp-qnovo.c +++ b/drivers/power/qcom-charger/qpnp-qnovo.c @@ -153,7 +153,7 @@ struct qnovo { struct work_struct status_change_work; int fv_uV_request; int fcc_uA_request; - struct votable *fcc_votable; + struct votable *fcc_max_votable; struct votable *fv_votable; }; @@ -243,8 +243,9 @@ static int qnovo_disable_cb(struct votable *votable, void *data, int disable, vote(chip->fv_votable, QNOVO_VOTER, false, 0); } if (chip->fcc_uA_request != -EINVAL) { - if (chip->fcc_votable) - vote(chip->fcc_votable, QNOVO_VOTER, false, 0); + if (chip->fcc_max_votable) + vote(chip->fcc_max_votable, QNOVO_VOTER, + false, 0); } } @@ -265,10 +266,10 @@ static int qnovo_disable_cb(struct votable *votable, void *data, int disable, true, chip->fv_uV_request); } if (chip->fcc_uA_request != -EINVAL) { - if (!chip->fcc_votable) - chip->fcc_votable = find_votable("FCC"); - if (chip->fcc_votable) - vote(chip->fcc_votable, QNOVO_VOTER, + if (!chip->fcc_max_votable) + chip->fcc_max_votable = find_votable("FCC_MAX"); + if (chip->fcc_max_votable) + vote(chip->fcc_max_votable, QNOVO_VOTER, true, chip->fcc_uA_request); } } diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c index a3a4591f05ed..08e64973d588 100644 --- a/drivers/power/qcom-charger/qpnp-smb2.c +++ b/drivers/power/qcom-charger/qpnp-smb2.c @@ -146,7 +146,7 @@ static int smb2_parse_dt(struct smb2 *chip) { struct smb_charger *chg = &chip->chg; struct device_node *node = chg->dev->of_node; - int rc; + int rc, byte_len; if (!node) { pr_err("device tree node missing\n"); @@ -181,6 +181,25 @@ static int smb2_parse_dt(struct smb2 *chip) if (rc < 0) chip->dt.wipower_max_uw = SMB2_DEFAULT_WPWR_UW; + if (of_find_property(node, "qcom,thermal-mitigation", &byte_len)) { + chg->thermal_mitigation = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation", + chg->thermal_mitigation, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + return 0; } @@ -350,6 +369,105 @@ static int smb2_init_usb_psy(struct smb2 *chip) } /************************* + * DC PSY REGISTRATION * + *************************/ + +static enum power_supply_property smb2_dc_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CURRENT_MAX, +}; + +static int smb2_dc_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct smb2 *chip = power_supply_get_drvdata(psy); + struct smb_charger *chg = &chip->chg; + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + rc = smblib_get_prop_dc_present(chg, val); + break; + case POWER_SUPPLY_PROP_ONLINE: + rc = smblib_get_prop_dc_online(chg, val); + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + rc = smblib_get_prop_dc_current_max(chg, val); + break; + default: + return -EINVAL; + } + + return rc; +} + +static int smb2_dc_set_prop(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct smb2 *chip = power_supply_get_drvdata(psy); + struct smb_charger *chg = &chip->chg; + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_CURRENT_MAX: + rc = smblib_set_prop_dc_current_max(chg, val); + break; + default: + return -EINVAL; + } + + return rc; +} + +static int smb2_dc_prop_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + int rc; + + switch (psp) { + case POWER_SUPPLY_PROP_CURRENT_MAX: + rc = 1; + break; + default: + rc = 0; + break; + } + + return rc; +} + +static const struct power_supply_desc dc_psy_desc = { + .name = "dc", + .type = POWER_SUPPLY_TYPE_WIPOWER, + .properties = smb2_dc_props, + .num_properties = ARRAY_SIZE(smb2_dc_props), + .get_property = smb2_dc_get_prop, + .set_property = smb2_dc_set_prop, + .property_is_writeable = smb2_dc_prop_is_writeable, +}; + +static int smb2_init_dc_psy(struct smb2 *chip) +{ + struct power_supply_config dc_cfg = {}; + struct smb_charger *chg = &chip->chg; + + dc_cfg.drv_data = chip; + dc_cfg.of_node = chg->dev->of_node; + chg->dc_psy = devm_power_supply_register(chg->dev, + &dc_psy_desc, + &dc_cfg); + if (IS_ERR(chg->dc_psy)) { + pr_err("Couldn't register USB power supply\n"); + return PTR_ERR(chg->dc_psy); + } + + return 0; +} + +/************************* * BATT PSY REGISTRATION * *************************/ @@ -360,6 +478,7 @@ static enum power_supply_property smb2_batt_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, }; static int smb2_batt_get_prop(struct power_supply *psy, @@ -387,9 +506,11 @@ static int smb2_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CAPACITY: smblib_get_prop_batt_capacity(chg, val); break; + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + smblib_get_prop_system_temp_level(chg, val); + break; default: - pr_err("batt power supply prop %d not supported\n", - psp); + pr_err("batt power supply prop %d not supported\n", psp); return -EINVAL; } @@ -400,17 +521,21 @@ static int smb2_batt_set_prop(struct power_supply *psy, enum power_supply_property prop, const union power_supply_propval *val) { + int rc = 0; struct smb_charger *chg = power_supply_get_drvdata(psy); switch (prop) { case POWER_SUPPLY_PROP_INPUT_SUSPEND: - smblib_set_prop_input_suspend(chg, val); + rc = smblib_set_prop_input_suspend(chg, val); + break; + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + rc = smblib_set_prop_system_temp_level(chg, val); break; default: - return -EINVAL; + rc = -EINVAL; } - return 0; + return rc; } static int smb2_batt_prop_is_writeable(struct power_supply *psy, @@ -418,6 +543,7 @@ static int smb2_batt_prop_is_writeable(struct power_supply *psy, { switch (psp) { case POWER_SUPPLY_PROP_INPUT_SUSPEND: + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: return 1; default: break; @@ -608,7 +734,7 @@ static int smb2_init_hw(struct smb2 *chip) DEFAULT_VOTER, chip->dt.suspend_input, 0); vote(chg->dc_suspend_votable, DEFAULT_VOTER, chip->dt.suspend_input, 0); - vote(chg->fcc_votable, + vote(chg->fcc_max_votable, DEFAULT_VOTER, true, chip->dt.fcc_ua); vote(chg->fv_votable, DEFAULT_VOTER, true, chip->dt.fv_uv); @@ -617,12 +743,16 @@ static int smb2_init_hw(struct smb2 *chip) vote(chg->dc_icl_votable, DEFAULT_VOTER, true, chip->dt.dc_icl_ua); - /* configure charge enable for software control; active high */ + /* + * Configure charge enable for software control; active high, and end + * the charge cycle while the battery is OV. + */ rc = smblib_masked_write(chg, CHGR_CFG2_REG, - CHG_EN_POLARITY_BIT | CHG_EN_SRC_BIT, 0); + CHG_EN_POLARITY_BIT | + CHG_EN_SRC_BIT | + BAT_OV_ECC_BIT, BAT_OV_ECC_BIT); if (rc < 0) { - dev_err(chg->dev, - "Couldn't configure charge enable source rc=%d\n", rc); + dev_err(chg->dev, "Couldn't configure charger rc=%d\n", rc); return rc; } @@ -903,6 +1033,12 @@ static int smb2_probe(struct platform_device *pdev) goto cleanup; } + rc = smb2_init_dc_psy(chip); + if (rc < 0) { + pr_err("Couldn't initialize dc psy rc=%d\n", rc); + goto cleanup; + } + rc = smb2_init_usb_psy(chip); if (rc < 0) { pr_err("Couldn't initialize usb psy rc=%d\n", rc); diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c index 43c360d98b69..8fe882e078f0 100644 --- a/drivers/power/qcom-charger/smb-lib.c +++ b/drivers/power/qcom-charger/smb-lib.c @@ -400,6 +400,14 @@ static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data, return smblib_set_dc_suspend(chg, suspend); } +static int smblib_fcc_max_vote_callback(struct votable *votable, void *data, + int fcc_ua, const char *client) +{ + struct smb_charger *chg = data; + + return vote(chg->fcc_votable, FCC_MAX_RESULT, true, fcc_ua); +} + static int smblib_fcc_vote_callback(struct votable *votable, void *data, int fcc_ua, const char *client) { @@ -850,6 +858,13 @@ done: return rc; } +int smblib_get_prop_system_temp_level(struct smb_charger *chg, + union power_supply_propval *val) +{ + val->intval = chg->system_temp_level; + return 0; +} + /*********************** * BATTERY PSY SETTERS * ***********************/ @@ -877,6 +892,101 @@ int smblib_set_prop_input_suspend(struct smb_charger *chg, return rc; } +int smblib_set_prop_system_temp_level(struct smb_charger *chg, + const union power_supply_propval *val) +{ + if (val->intval < 0) + return -EINVAL; + + if (chg->thermal_levels <= 0) + return -EINVAL; + + if (val->intval > chg->thermal_levels) + return -EINVAL; + + chg->system_temp_level = val->intval; + if (chg->system_temp_level == chg->thermal_levels) + return vote(chg->chg_disable_votable, THERMAL_DAEMON, true, 0); + + vote(chg->chg_disable_votable, THERMAL_DAEMON, false, 0); + if (chg->system_temp_level == 0) + return vote(chg->fcc_votable, THERMAL_DAEMON, false, 0); + + vote(chg->fcc_votable, THERMAL_DAEMON, true, + chg->thermal_mitigation[chg->system_temp_level]); + return 0; +} + +/******************* + * DC PSY GETTERS * + *******************/ + +int smblib_get_prop_dc_present(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc = 0; + u8 stat; + + rc = smblib_read(chg, DC_INT_RT_STS_REG, &stat); + if (rc < 0) { + dev_err(chg->dev, "Couldn't read DC_INT_RT_STS_REG rc=%d\n", + rc); + return rc; + } + smblib_dbg(chg, PR_REGISTER, "DC_INT_RT_STS_REG = 0x%02x\n", + stat); + + val->intval = (bool)(stat & DCIN_PLUGIN_RT_STS_BIT); + + return rc; +} + +int smblib_get_prop_dc_online(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc = 0; + u8 stat; + + if (get_client_vote(chg->dc_suspend_votable, USER_VOTER)) { + val->intval = false; + return rc; + } + + rc = smblib_read(chg, POWER_PATH_STATUS_REG, &stat); + if (rc < 0) { + dev_err(chg->dev, "Couldn't read POWER_PATH_STATUS rc=%d\n", + rc); + return rc; + } + smblib_dbg(chg, PR_REGISTER, "POWER_PATH_STATUS = 0x%02x\n", + stat); + + val->intval = (stat & USE_DCIN_BIT) && + (stat & VALID_INPUT_POWER_SOURCE_BIT); + + return rc; +} + +int smblib_get_prop_dc_current_max(struct smb_charger *chg, + union power_supply_propval *val) +{ + val->intval = get_effective_result_locked(chg->dc_icl_votable); + return 0; +} + +/******************* + * USB PSY SETTERS * + * *****************/ + +int smblib_set_prop_dc_current_max(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc; + + rc = vote(chg->dc_icl_votable, USER_VOTER, true, val->intval); + return rc; +} + /******************* * USB PSY GETTERS * *******************/ @@ -1695,7 +1805,15 @@ int smblib_create_votables(struct smb_charger *chg) return rc; } - chg->fcc_votable = create_votable("FCC", VOTE_MAX, + chg->fcc_max_votable = create_votable("FCC_MAX", VOTE_MAX, + smblib_fcc_max_vote_callback, + chg); + if (IS_ERR(chg->fcc_max_votable)) { + rc = PTR_ERR(chg->fcc_max_votable); + return rc; + } + + chg->fcc_votable = create_votable("FCC", VOTE_MIN, smblib_fcc_vote_callback, chg); if (IS_ERR(chg->fcc_votable)) { @@ -1805,6 +1923,7 @@ int smblib_deinit(struct smb_charger *chg) { destroy_votable(chg->usb_suspend_votable); destroy_votable(chg->dc_suspend_votable); + destroy_votable(chg->fcc_max_votable); destroy_votable(chg->fcc_votable); destroy_votable(chg->fv_votable); destroy_votable(chg->usb_icl_votable); diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h index 06a4428ffd13..1521fdb3fccf 100644 --- a/drivers/power/qcom-charger/smb-lib.h +++ b/drivers/power/qcom-charger/smb-lib.h @@ -31,6 +31,8 @@ enum print_reason { #define CHG_STATE_VOTER "CHG_STATE_VOTER" #define TYPEC_SRC_VOTER "TYPEC_SRC_VOTER" #define TAPER_END_VOTER "TAPER_END_VOTER" +#define FCC_MAX_RESULT "FCC_MAX_RESULT" +#define THERMAL_DAEMON "THERMAL_DAEMON" enum smb_mode { PARALLEL_MASTER = 0, @@ -93,6 +95,7 @@ struct smb_charger { /* power supplies */ struct power_supply *batt_psy; struct power_supply *usb_psy; + struct power_supply *dc_psy; struct power_supply_desc usb_psy_desc; /* parallel charging */ @@ -106,6 +109,7 @@ struct smb_charger { /* votables */ struct votable *usb_suspend_votable; struct votable *dc_suspend_votable; + struct votable *fcc_max_votable; struct votable *fcc_votable; struct votable *fv_votable; struct votable *usb_icl_votable; @@ -126,6 +130,10 @@ struct smb_charger { int voltage_max_uv; bool pd_active; bool vbus_present; + + int system_temp_level; + int thermal_levels; + int *thermal_mitigation; }; int smblib_read(struct smb_charger *chg, u16 addr, u8 *val); @@ -172,8 +180,22 @@ int smblib_get_prop_batt_charge_type(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_batt_health(struct smb_charger *chg, union power_supply_propval *val); +int smblib_get_prop_system_temp_level(struct smb_charger *chg, + union power_supply_propval *val); + int smblib_set_prop_input_suspend(struct smb_charger *chg, const union power_supply_propval *val); +int smblib_set_prop_system_temp_level(struct smb_charger *chg, + const union power_supply_propval *val); + +int smblib_get_prop_dc_present(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_get_prop_dc_online(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_get_prop_dc_current_max(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_set_prop_dc_current_max(struct smb_charger *chg, + const union power_supply_propval *val); int smblib_get_prop_usb_present(struct smb_charger *chg, union power_supply_propval *val); diff --git a/drivers/power/qcom-charger/smb-reg.h b/drivers/power/qcom-charger/smb-reg.h index 5af01c229f01..b03e8a7e0403 100644 --- a/drivers/power/qcom-charger/smb-reg.h +++ b/drivers/power/qcom-charger/smb-reg.h @@ -641,6 +641,9 @@ enum { #define WIPWR_RANGE_STATUS_REG (DCIN_BASE + 0x08) #define WIPWR_RANGE_STATUS_MASK GENMASK(4, 0) +#define DC_INT_RT_STS_REG (DCIN_BASE + 0x10) +#define DCIN_PLUGIN_RT_STS_BIT BIT(4) + /* DCIN Interrupt Bits */ #define WIPWR_VOLTAGE_RANGE_RT_STS_BIT BIT(7) #define DCIN_ICL_CHANGE_RT_STS_BIT BIT(6) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 45dc329a776e..3f8aa534c220 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -374,6 +374,15 @@ config QCOM_WATCHDOG_V2 deadlocks. It does not run during the bootup process, so it will not catch any early lockups. +config QCOM_IRQ_HELPER + bool "QCOM Irq Helper" + help + This enables the irq helper module. It exposes two APIs + int irq_blacklist_on(void) and int irq_blacklist_off(void) + to other kernel module. + These two apis will be used to control the black list used + by the irq balancer. + config QCOM_MEMORY_DUMP bool "Qualcomm Memory Dump Support" help diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index adbf2dc7a166..f8450a4868ad 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_QCOM_MEMORY_DUMP_V2) += memory_dump_v2.o obj-$(CONFIG_QCOM_DCC) += dcc.o obj-$(CONFIG_QCOM_WATCHDOG_V2) += watchdog_v2.o obj-$(CONFIG_QCOM_COMMON_LOG) += common_log.o +obj-$(CONFIG_QCOM_IRQ_HELPER) += irq-helper.o obj-$(CONFIG_TRACER_PKT) += tracer_pkt.o obj-$(CONFIG_ICNSS) += icnss.o wlan_firmware_service_v01.o obj-$(CONFIG_SOC_BUS) += socinfo.o diff --git a/drivers/soc/qcom/irq-helper.c b/drivers/soc/qcom/irq-helper.c new file mode 100644 index 000000000000..270a1ba9ba19 --- /dev/null +++ b/drivers/soc/qcom/irq-helper.c @@ -0,0 +1,179 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/cpu.h> +#include <linux/sysfs.h> +#include <linux/kobject.h> +#include <linux/spinlock.h> +#include <linux/slab.h> + +struct irq_helper { + bool enable; + bool deploy; + uint32_t count; + struct kobject kobj; + /* spinlock to protect reference count variable 'count' */ + spinlock_t lock; +}; + +struct irq_helper_attr { + struct attribute attr; + ssize_t (*show)(struct kobject *kobj, struct attribute *attr, + char *buf); + size_t (*store)(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count); +}; + +#define IRQ_HELPER_ATTR(_name, _mode, _show, _store) \ + struct irq_helper_attr irq_helper_##_name = \ + __ATTR(_name, _mode, _show, _store) + +#define to_irq_helper(kobj) \ + container_of(kobj, struct irq_helper, kobj) + +#define to_irq_helper_attr(_attr) \ + container_of(_attr, struct irq_helper_attr, attr) + +static ssize_t attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct irq_helper_attr *irq_attr = to_irq_helper_attr(attr); + ssize_t ret = -EIO; + + if (irq_attr->show) + ret = irq_attr->show(kobj, attr, buf); + + return ret; +} + +static const struct sysfs_ops irq_helper_sysfs_ops = { + .show = attr_show, +}; + +static struct kobj_type irq_helper_ktype = { + .sysfs_ops = &irq_helper_sysfs_ops, +}; + +static ssize_t show_deploy(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct irq_helper *irq = to_irq_helper(kobj); + + return snprintf(buf, sizeof(irq->deploy), "%u\n", irq->deploy); +} +IRQ_HELPER_ATTR(irq_blacklist_on, 0444, show_deploy, NULL); + +static struct irq_helper *irq_h; + +int irq_blacklist_on(void) +{ + bool flag = false; + + if (!irq_h) { + pr_err("%s: init function is not called", __func__); + return -EPERM; + } + if (!irq_h->enable) { + pr_err("%s: enable bit is not set up", __func__); + return -EPERM; + } + spin_lock(&irq_h->lock); + irq_h->count++; + if (!irq_h->deploy) { + irq_h->deploy = true; + flag = true; + } + spin_unlock(&irq_h->lock); + if (flag) + sysfs_notify(&irq_h->kobj, NULL, "irq_blacklist_on"); + return 0; +} +EXPORT_SYMBOL(irq_blacklist_on); + +int irq_blacklist_off(void) +{ + bool flag = false; + + if (!irq_h) { + pr_err("%s: init function is not called", __func__); + return -EPERM; + } + if (!irq_h->enable) { + pr_err("%s: enable bit is not set up", __func__); + return -EPERM; + } + spin_lock(&irq_h->lock); + if (irq_h->count == 0) { + pr_err("%s: ref-count is 0, cannot call irq blacklist off.", + __func__); + spin_unlock(&irq_h->lock); + return -EPERM; + } + irq_h->count--; + if (irq_h->count == 0) { + irq_h->deploy = false; + flag = true; + } + spin_unlock(&irq_h->lock); + + if (flag) + sysfs_notify(&irq_h->kobj, NULL, "irq_blacklist_on"); + return 0; +} +EXPORT_SYMBOL(irq_blacklist_off); + +static int __init irq_helper_init(void) +{ + int ret; + + irq_h = kzalloc(sizeof(struct irq_helper), GFP_KERNEL); + if (!irq_h) + return -ENOMEM; + + ret = kobject_init_and_add(&irq_h->kobj, &irq_helper_ktype, + kernel_kobj, "%s", "irq_helper"); + if (ret) { + pr_err("%s:Error in creation kobject_add\n", __func__); + goto out_free_irq; + } + + ret = sysfs_create_file(&irq_h->kobj, + &irq_helper_irq_blacklist_on.attr); + if (ret) { + pr_err("%s:Error in sysfs_create_file\n", __func__); + goto out_put_kobj; + } + + spin_lock_init(&irq_h->lock); + irq_h->count = 0; + irq_h->enable = true; + return 0; +out_put_kobj: + koject_put(&irq_h->kobj); +out_free_irq: + kfree(irq_h); + return ret; +} +module_init(irq_helper_init); + +static void __exit irq_helper_exit(void) +{ + sysfs_remove_file(&irq_h->kobj, &irq_helper_irq_blacklist_on.attr); + kobject_del(&irq_h->kobj); + kobject_put(&irq_h->kobj); + kfree(irq_h); +} +module_exit(irq_helper_exit); +MODULE_DESCRIPTION("IRQ Helper APIs"); diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 4f29923e054c..b8cef11f4067 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -528,10 +528,11 @@ static struct msm_soc_info cpu_of_id[] = { [270] = {MSM_CPU_8929, "MSM8229"}, [271] = {MSM_CPU_8929, "APQ8029"}, - /* Cobalt ID */ + /* Cobalt IDs */ [292] = {MSM_CPU_COBALT, "MSMCOBALT"}, + [319] = {MSM_CPU_COBALT, "APQCOBALT"}, - /* Cobalt ID */ + /* Hamster ID */ [306] = {MSM_CPU_HAMSTER, "MSMHAMSTER"}, /* falcon ID */ @@ -1205,6 +1206,10 @@ static void * __init setup_dummy_socinfo(void) dummy_socinfo.id = 317; strlcpy(dummy_socinfo.build_id, "msmfalcon - ", sizeof(dummy_socinfo.build_id)); + } else if (early_machine_is_apqcobalt()) { + dummy_socinfo.id = 319; + strlcpy(dummy_socinfo.build_id, "apqcobalt - ", + sizeof(dummy_socinfo.build_id)); } strlcat(dummy_socinfo.build_id, "Dummy socinfo", diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 5e1fd988b22c..b02e48185355 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -97,6 +97,17 @@ enum pmic_arb_cmd_op_code { /* interrupt enable bit */ #define SPMI_PIC_ACC_ENABLE_BIT BIT(0) +#define HWIRQ(slave_id, periph_id, irq_id, apid) \ + ((((slave_id) & 0xF) << 28) | \ + (((periph_id) & 0xFF) << 20) | \ + (((irq_id) & 0x7) << 16) | \ + (((apid) & 0x1FF) << 0)) + +#define HWIRQ_SID(hwirq) (((hwirq) >> 28) & 0xF) +#define HWIRQ_PER(hwirq) (((hwirq) >> 20) & 0xFF) +#define HWIRQ_IRQ(hwirq) (((hwirq) >> 16) & 0x7) +#define HWIRQ_APID(hwirq) (((hwirq) >> 0) & 0x1FF) + struct pmic_arb_ver_ops; struct apid_data { @@ -172,7 +183,7 @@ struct spmi_pmic_arb { struct pmic_arb_ver_ops { const char *ver_str; int (*ppid_to_apid)(struct spmi_pmic_arb *pa, u8 sid, u16 addr, - u8 *apid); + u16 *apid); int (*mode)(struct spmi_pmic_arb *dev, u8 sid, u16 addr, mode_t *mode); /* spmi commands (read_cmd, write_cmd, cmd) functionality */ @@ -181,10 +192,10 @@ struct pmic_arb_ver_ops { u32 (*fmt_cmd)(u8 opc, u8 sid, u16 addr, u8 bc); int (*non_data_cmd)(struct spmi_controller *ctrl, u8 opc, u8 sid); /* Interrupts controller functionality (offset of PIC registers) */ - u32 (*owner_acc_status)(u8 m, u8 n); - u32 (*acc_enable)(u8 n); - u32 (*irq_status)(u8 n); - u32 (*irq_clear)(u8 n); + u32 (*owner_acc_status)(u8 m, u16 n); + u32 (*acc_enable)(u16 n); + u32 (*irq_status)(u16 n); + u32 (*irq_clear)(u16 n); }; static inline void pmic_arb_base_write(struct spmi_pmic_arb *pa, @@ -466,8 +477,8 @@ static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf, size_t len) { struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); - u8 sid = d->hwirq >> 24; - u8 per = d->hwirq >> 16; + u8 sid = HWIRQ_SID(d->hwirq); + u8 per = HWIRQ_PER(d->hwirq); if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid, (per << 8) + reg, buf, len)) @@ -479,8 +490,8 @@ static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf, static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len) { struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); - u8 sid = d->hwirq >> 24; - u8 per = d->hwirq >> 16; + u8 sid = HWIRQ_SID(d->hwirq); + u8 per = HWIRQ_PER(d->hwirq); if (pmic_arb_read_cmd(pa->spmic, SPMI_CMD_EXT_READL, sid, (per << 8) + reg, buf, len)) @@ -489,7 +500,7 @@ static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len) d->irq); } -static void cleanup_irq(struct spmi_pmic_arb *pa, u8 apid, int id) +static void cleanup_irq(struct spmi_pmic_arb *pa, u16 apid, int id) { u16 ppid = pa->apid_data[apid].ppid; u8 sid = ppid >> 8; @@ -514,20 +525,19 @@ static void cleanup_irq(struct spmi_pmic_arb *pa, u8 apid, int id) irq_mask, ppid); } -static void periph_interrupt(struct spmi_pmic_arb *pa, u8 apid) +static void periph_interrupt(struct spmi_pmic_arb *pa, u16 apid) { unsigned int irq; u32 status; int id; + u8 sid = (pa->apid_data[apid].ppid >> 8) & 0xF; + u8 per = pa->apid_data[apid].ppid & 0xFF; status = readl_relaxed(pa->intr + pa->ver_ops->irq_status(apid)); while (status) { id = ffs(status) - 1; status &= ~BIT(id); - irq = irq_find_mapping(pa->domain, - pa->apid_data[apid].ppid << 16 - | id << 8 - | apid); + irq = irq_find_mapping(pa->domain, HWIRQ(sid, per, id, apid)); if (irq == 0) { cleanup_irq(pa, apid, id); continue; @@ -568,8 +578,8 @@ static void pmic_arb_chained_irq(struct irq_desc *desc) static void qpnpint_irq_ack(struct irq_data *d) { struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); - u8 irq = d->hwirq >> 8; - u8 apid = d->hwirq; + u8 irq = HWIRQ_IRQ(d->hwirq); + u16 apid = HWIRQ_APID(d->hwirq); u8 data; writel_relaxed(BIT(irq), pa->intr + pa->ver_ops->irq_clear(apid)); @@ -580,7 +590,7 @@ static void qpnpint_irq_ack(struct irq_data *d) static void qpnpint_irq_mask(struct irq_data *d) { - u8 irq = d->hwirq >> 8; + u8 irq = HWIRQ_IRQ(d->hwirq); u8 data = BIT(irq); qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1); @@ -589,8 +599,8 @@ static void qpnpint_irq_mask(struct irq_data *d) static void qpnpint_irq_unmask(struct irq_data *d) { struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); - u8 irq = d->hwirq >> 8; - u8 apid = d->hwirq; + u8 irq = HWIRQ_IRQ(d->hwirq); + u16 apid = HWIRQ_APID(d->hwirq); u8 buf[2]; writel_relaxed(SPMI_PIC_ACC_ENABLE_BIT, @@ -612,7 +622,7 @@ static void qpnpint_irq_unmask(struct irq_data *d) static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type) { struct spmi_pmic_arb_qpnpint_type type; - u8 irq = d->hwirq >> 8; + u8 irq = HWIRQ_IRQ(d->hwirq); u8 bit_mask_irq = BIT(irq); qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); @@ -649,7 +659,7 @@ static int qpnpint_get_irqchip_state(struct irq_data *d, enum irqchip_irq_state which, bool *state) { - u8 irq = d->hwirq >> 8; + u8 irq = HWIRQ_IRQ(d->hwirq); u8 status = 0; if (which != IRQCHIP_STATE_LINE_LEVEL) @@ -681,7 +691,7 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, { struct spmi_pmic_arb *pa = d->host_data; int rc; - u8 apid; + u16 apid; dev_dbg(&pa->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n", @@ -709,10 +719,7 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, if (apid < pa->min_apid) pa->min_apid = apid; - *out_hwirq = (intspec[0] & 0xF) << 24 - | (intspec[1] & 0xFF) << 16 - | (intspec[2] & 0x7) << 8 - | apid; + *out_hwirq = HWIRQ(intspec[0], intspec[1], intspec[2], apid); *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK; dev_dbg(&pa->spmic->dev, "out_hwirq = %lu\n", *out_hwirq); @@ -735,7 +742,7 @@ static int qpnpint_irq_domain_map(struct irq_domain *d, } static int -pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u8 *apid) +pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u16 *apid) { u16 ppid = sid << 8 | ((addr >> 8) & 0xFF); u32 *mapping_table = pa->mapping_table; @@ -834,7 +841,7 @@ static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pa, u16 ppid) } static int -pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u8 *apid) +pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u16 *apid) { u16 ppid = (sid << 8) | (addr >> 8); u16 apid_valid; @@ -852,7 +859,7 @@ pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u8 *apid) static int pmic_arb_mode_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, mode_t *mode) { - u8 apid; + u16 apid; int rc; rc = pmic_arb_ppid_to_apid_v2(pa, sid, addr, &apid); @@ -871,7 +878,7 @@ pmic_arb_mode_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, mode_t *mode) static int pmic_arb_offset_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u32 *offset) { - u8 apid; + u16 apid; int rc; rc = pmic_arb_ppid_to_apid_v2(pa, sid, addr, &apid); @@ -892,47 +899,47 @@ static u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 sid, u16 addr, u8 bc) return (opc << 27) | ((addr & 0xff) << 4) | (bc & 0x7); } -static u32 pmic_arb_owner_acc_status_v1(u8 m, u8 n) +static u32 pmic_arb_owner_acc_status_v1(u8 m, u16 n) { return 0x20 * m + 0x4 * n; } -static u32 pmic_arb_owner_acc_status_v2(u8 m, u8 n) +static u32 pmic_arb_owner_acc_status_v2(u8 m, u16 n) { return 0x100000 + 0x1000 * m + 0x4 * n; } -static u32 pmic_arb_owner_acc_status_v3(u8 m, u8 n) +static u32 pmic_arb_owner_acc_status_v3(u8 m, u16 n) { return 0x200000 + 0x1000 * m + 0x4 * n; } -static u32 pmic_arb_acc_enable_v1(u8 n) +static u32 pmic_arb_acc_enable_v1(u16 n) { return 0x200 + 0x4 * n; } -static u32 pmic_arb_acc_enable_v2(u8 n) +static u32 pmic_arb_acc_enable_v2(u16 n) { return 0x1000 * n; } -static u32 pmic_arb_irq_status_v1(u8 n) +static u32 pmic_arb_irq_status_v1(u16 n) { return 0x600 + 0x4 * n; } -static u32 pmic_arb_irq_status_v2(u8 n) +static u32 pmic_arb_irq_status_v2(u16 n) { return 0x4 + 0x1000 * n; } -static u32 pmic_arb_irq_clear_v1(u8 n) +static u32 pmic_arb_irq_clear_v1(u16 n) { return 0xA00 + 0x4 * n; } -static u32 pmic_arb_irq_clear_v2(u8 n) +static u32 pmic_arb_irq_clear_v2(u16 n) { return 0x8 + 0x1000 * n; } diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c index 73d7435d2eb8..97ab02dfc753 100644 --- a/drivers/thermal/msm-tsens.c +++ b/drivers/thermal/msm-tsens.c @@ -831,6 +831,7 @@ struct tsens_tm_device { bool prev_reading_avail; bool calibration_less_mode; bool tsens_local_init; + bool gain_offset_programmed; int tsens_factor; uint32_t tsens_num_sensor; int tsens_irq; @@ -5341,17 +5342,25 @@ static int get_device_tree_data(struct platform_device *pdev, return -ENODEV; } - tsens_slope_data = devm_kzalloc(&pdev->dev, + /* TSENS calibration region */ + tmdev->res_calib_mem = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "tsens_eeprom_physical"); + if (!tmdev->res_calib_mem) { + pr_debug("Using controller programmed gain and offset\n"); + tmdev->gain_offset_programmed = true; + } else { + tsens_slope_data = devm_kzalloc(&pdev->dev, tsens_num_sensors * sizeof(u32), GFP_KERNEL); - if (!tsens_slope_data) - return -ENOMEM; + if (!tsens_slope_data) + return -ENOMEM; - rc = of_property_read_u32_array(of_node, - "qcom,slope", tsens_slope_data, tsens_num_sensors); - if (rc) { - dev_err(&pdev->dev, "invalid or missing property: tsens-slope\n"); - return rc; - }; + rc = of_property_read_u32_array(of_node, + "qcom,slope", tsens_slope_data, tsens_num_sensors); + if (rc) { + dev_err(&pdev->dev, "missing property: tsens-slope\n"); + return rc; + }; + } if (!of_match_node(tsens_match, of_node)) { pr_err("Need to read SoC specific fuse map\n"); @@ -5364,9 +5373,13 @@ static int get_device_tree_data(struct platform_device *pdev, return -ENODEV; } - for (i = 0; i < tsens_num_sensors; i++) - tmdev->sensor[i].slope_mul_tsens_factor = tsens_slope_data[i]; - tmdev->tsens_factor = TSENS_SLOPE_FACTOR; + if (!tmdev->gain_offset_programmed) { + for (i = 0; i < tsens_num_sensors; i++) + tmdev->sensor[i].slope_mul_tsens_factor = + tsens_slope_data[i]; + tmdev->tsens_factor = TSENS_SLOPE_FACTOR; + } + tmdev->tsens_num_sensor = tsens_num_sensors; tmdev->calibration_less_mode = of_property_read_bool(of_node, "qcom,calibration-less-mode"); @@ -5536,24 +5549,17 @@ static int get_device_tree_data(struct platform_device *pdev, goto fail_unmap_tsens_region; } - /* TSENS calibration region */ - tmdev->res_calib_mem = platform_get_resource_byname(pdev, - IORESOURCE_MEM, "tsens_eeprom_physical"); - if (!tmdev->res_calib_mem) { - pr_err("Could not get qfprom physical address resource\n"); - rc = -EINVAL; - goto fail_unmap_tsens; - } - - tmdev->calib_len = tmdev->res_calib_mem->end - + if (!tmdev->gain_offset_programmed) { + tmdev->calib_len = tmdev->res_calib_mem->end - tmdev->res_calib_mem->start + 1; - tmdev->tsens_calib_addr = ioremap(tmdev->res_calib_mem->start, + tmdev->tsens_calib_addr = ioremap(tmdev->res_calib_mem->start, tmdev->calib_len); - if (!tmdev->tsens_calib_addr) { - pr_err("Failed to IO map EEPROM registers.\n"); - rc = -EINVAL; - goto fail_unmap_tsens; + if (!tmdev->tsens_calib_addr) { + pr_err("Failed to IO map EEPROM registers.\n"); + rc = -EINVAL; + goto fail_unmap_tsens; + } } return 0; diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index d7bedf8beee3..e03d3b41c25b 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -39,7 +39,6 @@ #include <linux/kthread.h> #include <net/netlink.h> #include <net/genetlink.h> -#include <linux/suspend.h> #define CREATE_TRACE_POINTS #include <trace/events/thermal.h> @@ -64,8 +63,6 @@ static LIST_HEAD(thermal_governor_list); static DEFINE_MUTEX(thermal_list_lock); static DEFINE_MUTEX(thermal_governor_lock); -static atomic_t in_suspend; - static struct thermal_governor *def_governor; static struct thermal_governor *__find_governor(const char *name) @@ -961,9 +958,6 @@ void thermal_zone_device_update(struct thermal_zone_device *tz) { int count; - if (atomic_read(&in_suspend)) - return; - if (!tz->ops->get_temp) return; @@ -2641,36 +2635,6 @@ static void thermal_unregister_governors(void) thermal_gov_power_allocator_unregister(); } -static int thermal_pm_notify(struct notifier_block *nb, - unsigned long mode, void *_unused) -{ - struct thermal_zone_device *tz; - - switch (mode) { - case PM_HIBERNATION_PREPARE: - case PM_RESTORE_PREPARE: - case PM_SUSPEND_PREPARE: - atomic_set(&in_suspend, 1); - break; - case PM_POST_HIBERNATION: - case PM_POST_RESTORE: - case PM_POST_SUSPEND: - atomic_set(&in_suspend, 0); - list_for_each_entry(tz, &thermal_tz_list, node) { - thermal_zone_device_reset(tz); - thermal_zone_device_update(tz); - } - break; - default: - break; - } - return 0; -} - -static struct notifier_block thermal_pm_nb = { - .notifier_call = thermal_pm_notify, -}; - static int __init thermal_init(void) { int result; @@ -2691,11 +2655,6 @@ static int __init thermal_init(void) if (result) goto exit_netlink; - result = register_pm_notifier(&thermal_pm_nb); - if (result) - pr_warn("Thermal: Can not register suspend notifier, return %d\n", - result); - return 0; exit_netlink: @@ -2715,7 +2674,6 @@ error: static void __exit thermal_exit(void) { - unregister_pm_notifier(&thermal_pm_nb); of_thermal_destroy_zones(); genetlink_exit(); class_unregister(&thermal_class); diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 6843711774b2..d4ece0e56954 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -70,9 +70,11 @@ #define UART_SPS_CONS_PERIPHERAL 0 #define UART_SPS_PROD_PERIPHERAL 1 -#define IPC_MSM_HS_LOG_PAGES 5 +#define IPC_MSM_HS_LOG_STATE_PAGES 2 +#define IPC_MSM_HS_LOG_USER_PAGES 2 +#define IPC_MSM_HS_LOG_DATA_PAGES 3 #define UART_DMA_DESC_NR 8 -#define BUF_DUMP_SIZE 20 +#define BUF_DUMP_SIZE 32 /* If the debug_mask gets set to FATAL_LEV, * a fatal error has happened and further IPC logging @@ -121,6 +123,11 @@ enum { } \ } while (0) +#define LOG_USR_MSG(ctx, x...) do { \ + if (ctx) \ + ipc_log_string(ctx, x); \ +} while (0) + /* * There are 3 different kind of UART Core available on MSM. * High Speed UART (i.e. Legacy HSUART), GSBI based HSUART @@ -164,6 +171,7 @@ struct msm_hs_tx { struct task_struct *task; struct msm_hs_sps_ep_conn_data cons; struct timer_list tx_timeout_timer; + void *ipc_tx_ctxt; }; struct msm_hs_rx { @@ -181,6 +189,7 @@ struct msm_hs_rx { unsigned long pending_flag; int rx_inx; struct sps_iovec iovec[UART_DMA_DESC_NR]; /* track descriptors */ + void *ipc_rx_ctxt; }; enum buffer_states { NONE_PENDING = 0x0, @@ -214,7 +223,7 @@ struct msm_hs_port { struct clk *pclk; struct msm_hs_tx tx; struct msm_hs_rx rx; - atomic_t clk_count; + atomic_t resource_count; struct msm_hs_wakeup wakeup; struct dentry *loopback_dir; @@ -248,6 +257,7 @@ struct msm_hs_port { bool obs; /* out of band sleep flag */ atomic_t client_req_state; void *ipc_msm_hs_log_ctxt; + void *ipc_msm_hs_pwr_ctxt; int ipc_debug_mask; }; @@ -315,7 +325,7 @@ static int msm_hs_ioctl(struct uart_port *uport, unsigned int cmd, break; } default: { - MSM_HS_DBG("%s():Unknown cmd specified: cmd=%d\n", __func__, + MSM_HS_INFO("%s():Unknown cmd specified: cmd=%d\n", __func__, cmd); ret = -ENOIOCTLCMD; break; @@ -380,7 +390,7 @@ static void msm_hs_clk_bus_unvote(struct msm_hs_port *msm_uport) static void msm_hs_resource_unvote(struct msm_hs_port *msm_uport) { struct uart_port *uport = &(msm_uport->uport); - int rc = atomic_read(&msm_uport->clk_count); + int rc = atomic_read(&msm_uport->resource_count); MSM_HS_DBG("%s(): power usage count %d", __func__, rc); if (rc <= 0) { @@ -388,7 +398,7 @@ static void msm_hs_resource_unvote(struct msm_hs_port *msm_uport) WARN_ON(1); return; } - atomic_dec(&msm_uport->clk_count); + atomic_dec(&msm_uport->resource_count); pm_runtime_mark_last_busy(uport->dev); pm_runtime_put_autosuspend(uport->dev); } @@ -400,12 +410,12 @@ static void msm_hs_resource_vote(struct msm_hs_port *msm_uport) struct uart_port *uport = &(msm_uport->uport); ret = pm_runtime_get_sync(uport->dev); if (ret < 0 || msm_uport->pm_state != MSM_HS_PM_ACTIVE) { - MSM_HS_WARN("%s(): %p runtime PM callback not invoked(%d)", - __func__, uport->dev, ret); + MSM_HS_WARN("%s:%s runtime callback not invoked ret:%d st:%d", + __func__, dev_name(uport->dev), ret, + msm_uport->pm_state); msm_hs_pm_resume(uport->dev); } - - atomic_inc(&msm_uport->clk_count); + atomic_inc(&msm_uport->resource_count); } /* Check if the uport line number matches with user id stored in pdata. @@ -567,23 +577,21 @@ static int sps_rx_disconnect(struct sps_pipe *sps_pipe_handler) return sps_disconnect(sps_pipe_handler); } -static void hex_dump_ipc(struct msm_hs_port *msm_uport, - char *prefix, char *string, int size) +static void hex_dump_ipc(struct msm_hs_port *msm_uport, void *ipc_ctx, + char *prefix, char *string, u64 addr, int size) + { - unsigned char linebuf[512]; - unsigned char firstbuf[40], lastbuf[40]; + char buf[(BUF_DUMP_SIZE * 3) + 2]; + int len = 0; - if ((msm_uport->ipc_debug_mask != DBG_LEV) && (size > BUF_DUMP_SIZE)) { - hex_dump_to_buffer(string, 10, 16, 1, - firstbuf, sizeof(firstbuf), 1); - hex_dump_to_buffer(string + (size - 10), 10, 16, 1, - lastbuf, sizeof(lastbuf), 1); - MSM_HS_INFO("%s : %s...%s", prefix, firstbuf, lastbuf); - } else { - hex_dump_to_buffer(string, size, 16, 1, - linebuf, sizeof(linebuf), 1); - MSM_HS_INFO("%s : %s", prefix, linebuf); - } + len = min(size, BUF_DUMP_SIZE); + /* + * Print upto 32 data bytes, 32 bytes per line, 1 byte at a time and + * don't include the ASCII text at the end of the buffer. + */ + hex_dump_to_buffer(string, len, 32, 1, buf, sizeof(buf), false); + ipc_log_string(ipc_ctx, "%s[0x%.10x:%d] : %s", prefix, + (unsigned int)addr, size, buf); } /* @@ -594,8 +602,8 @@ static void dump_uart_hs_registers(struct msm_hs_port *msm_uport) struct uart_port *uport = &(msm_uport->uport); if (msm_uport->pm_state != MSM_HS_PM_ACTIVE) { - MSM_HS_INFO("%s:Failed clocks are off, clk_count %d", - __func__, atomic_read(&msm_uport->clk_count)); + MSM_HS_INFO("%s:Failed clocks are off, resource_count %d", + __func__, atomic_read(&msm_uport->resource_count)); return; } @@ -757,8 +765,10 @@ static int msm_hs_spsconnect_tx(struct msm_hs_port *msm_uport) unsigned long flags; unsigned int data; - if (tx->flush != FLUSH_SHUTDOWN) + if (tx->flush != FLUSH_SHUTDOWN) { + MSM_HS_ERR("%s:Invalid flush state:%d\n", __func__, tx->flush); return 0; + } /* Establish connection between peripheral and memory endpoint */ ret = sps_connect(sps_pipe_handle, sps_config); @@ -1100,7 +1110,6 @@ static void msm_hs_set_termios(struct uart_port *uport, mutex_lock(&msm_uport->mtx); msm_hs_write(uport, UART_DM_IMR, 0); - MSM_HS_DBG("Entering %s\n", __func__); msm_hs_disable_flow_control(uport, true); /* @@ -1214,10 +1223,10 @@ static void msm_hs_set_termios(struct uart_port *uport, msm_uport->flow_control = true; } msm_hs_write(uport, UART_DM_MR1, data); + MSM_HS_INFO("%s: Cflags 0x%x Baud %u\n", __func__, c_cflag, bps); mutex_unlock(&msm_uport->mtx); - MSM_HS_DBG("Exit %s\n", __func__); msm_hs_resource_unvote(msm_uport); } @@ -1400,9 +1409,6 @@ static void msm_hs_submit_tx_locked(struct uart_port *uport) if (tx_count > left) tx_count = left; - MSM_HS_INFO("%s(): [UART_TX]<%d>\n", __func__, tx_count); - hex_dump_ipc(msm_uport, "HSUART write: ", - &tx_buf->buf[tx_buf->tail], tx_count); src_addr = tx->dma_base + tx_buf->tail; /* Mask the src_addr to align on a cache @@ -1415,6 +1421,8 @@ static void msm_hs_submit_tx_locked(struct uart_port *uport) tx->tx_count = tx_count; + hex_dump_ipc(msm_uport, tx->ipc_tx_ctxt, "Tx", + &tx_buf->buf[tx_buf->tail], (u64)src_addr, tx_count); sps_pipe_handle = tx->cons.pipe_handle; /* Queue transfer request to SPS */ ret = sps_transfer_one(sps_pipe_handle, src_addr, tx_count, @@ -1717,12 +1725,11 @@ static void msm_serial_hs_rx_work(struct kthread_work *work) goto out; rx_count = msm_uport->rx.iovec[msm_uport->rx.rx_inx].size; - - MSM_HS_INFO("%s():[UART_RX]<%d>\n", __func__, rx_count); - hex_dump_ipc(msm_uport, "HSUART Read: ", - (msm_uport->rx.buffer + - (msm_uport->rx.rx_inx * UARTDM_RX_BUF_SIZE)), - rx_count); + hex_dump_ipc(msm_uport, rx->ipc_rx_ctxt, "Rx", + (msm_uport->rx.buffer + + (msm_uport->rx.rx_inx * UARTDM_RX_BUF_SIZE)), + msm_uport->rx.iovec[msm_uport->rx.rx_inx].addr, + rx_count); /* * We are in a spin locked context, spin lock taken at @@ -1733,7 +1740,7 @@ static void msm_serial_hs_rx_work(struct kthread_work *work) &msm_uport->rx.pending_flag) && !test_bit(msm_uport->rx.rx_inx, &msm_uport->rx.queued_flag)) - MSM_HS_ERR("RX INX not set"); + MSM_HS_ERR("%s: RX INX not set", __func__); else if (test_bit(msm_uport->rx.rx_inx, &msm_uport->rx.pending_flag) && !test_bit(msm_uport->rx.rx_inx, @@ -1748,14 +1755,14 @@ static void msm_serial_hs_rx_work(struct kthread_work *work) rx_count); if (retval != rx_count) { - MSM_HS_DBG("%s(): ret %d rx_count %d", + MSM_HS_INFO("%s(): ret %d rx_count %d", __func__, retval, rx_count); msm_uport->rx.buffer_pending |= CHARS_NORMAL | retval << 5 | (rx_count - retval) << 16; } } else - MSM_HS_ERR("Error in inx %d", + MSM_HS_ERR("%s: Error in inx %d", __func__, msm_uport->rx.rx_inx); } @@ -1778,7 +1785,7 @@ static void msm_serial_hs_rx_work(struct kthread_work *work) } out: if (msm_uport->rx.buffer_pending) { - MSM_HS_WARN("tty buffer exhausted. Stalling\n"); + MSM_HS_WARN("%s: tty buffer exhausted. Stalling\n", __func__); schedule_delayed_work(&msm_uport->rx.flip_insert_work , msecs_to_jiffies(RETRY_TIMEOUT)); } @@ -1796,7 +1803,7 @@ static void msm_hs_start_tx_locked(struct uart_port *uport) /* Bail if transfer in progress */ if (tx->flush < FLUSH_STOP || tx->dma_in_flight) { - MSM_HS_DBG("%s(): retry, flush %d, dma_in_flight %d\n", + MSM_HS_INFO("%s(): retry, flush %d, dma_in_flight %d\n", __func__, tx->flush, tx->dma_in_flight); return; } @@ -1826,11 +1833,9 @@ static void msm_hs_sps_tx_callback(struct sps_event_notify *notify) notify->data.transfer.iovec.addr); msm_uport->notify = *notify; - MSM_HS_DBG("%s: ev_id=%d, addr=0x%pa, size=0x%x, flags=0x%x, line=%d\n", - __func__, notify->event_id, &addr, - notify->data.transfer.iovec.size, - notify->data.transfer.iovec.flags, - msm_uport->uport.line); + MSM_HS_INFO("tx_cb: addr=0x%pa, size=0x%x, flags=0x%x\n", + &addr, notify->data.transfer.iovec.size, + notify->data.transfer.iovec.flags); del_timer(&msm_uport->tx.tx_timeout_timer); MSM_HS_DBG("%s(): Queue kthread work", __func__); @@ -1931,9 +1936,8 @@ static void msm_hs_sps_rx_callback(struct sps_event_notify *notify) uport = &(msm_uport->uport); msm_uport->notify = *notify; - MSM_HS_DBG("\n%s: sps ev_id=%d, addr=0x%pa, size=0x%x, flags=0x%x\n", - __func__, notify->event_id, &addr, - notify->data.transfer.iovec.size, + MSM_HS_INFO("rx_cb: addr=0x%pa, size=0x%x, flags=0x%x\n", + &addr, notify->data.transfer.iovec.size, notify->data.transfer.iovec.flags); spin_lock_irqsave(&uport->lock, flags); @@ -1985,13 +1989,13 @@ void msm_hs_set_mctrl_locked(struct uart_port *uport, unsigned int set_rts; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - MSM_HS_DBG("%s()", __func__); if (msm_uport->pm_state != MSM_HS_PM_ACTIVE) { MSM_HS_WARN("%s(): Clocks are off\n", __func__); return; } /* RTS is active low */ set_rts = TIOCM_RTS & mctrl ? 0 : 1; + MSM_HS_INFO("%s: set_rts %d\n", __func__, set_rts); if (set_rts) msm_hs_disable_flow_control(uport, false); @@ -2186,7 +2190,7 @@ static struct msm_hs_port *msm_hs_get_hs_port(int port_index) return NULL; } -void toggle_wakeup_interrupt(struct msm_hs_port *msm_uport) +void enable_wakeup_interrupt(struct msm_hs_port *msm_uport) { unsigned long flags; struct uart_port *uport = &(msm_uport->uport); @@ -2197,7 +2201,6 @@ void toggle_wakeup_interrupt(struct msm_hs_port *msm_uport) return; if (!(msm_uport->wakeup.enabled)) { - MSM_HS_DBG("%s(): Enable Wakeup IRQ", __func__); enable_irq(msm_uport->wakeup.irq); disable_irq(uport->irq); spin_lock_irqsave(&uport->lock, flags); @@ -2205,12 +2208,28 @@ void toggle_wakeup_interrupt(struct msm_hs_port *msm_uport) msm_uport->wakeup.enabled = true; spin_unlock_irqrestore(&uport->lock, flags); } else { + MSM_HS_WARN("%s:Wake up IRQ already enabled", __func__); + } +} + +void disable_wakeup_interrupt(struct msm_hs_port *msm_uport) +{ + unsigned long flags; + struct uart_port *uport = &(msm_uport->uport); + + if (!is_use_low_power_wakeup(msm_uport)) + return; + if (msm_uport->wakeup.freed) + return; + + if (msm_uport->wakeup.enabled) { disable_irq_nosync(msm_uport->wakeup.irq); enable_irq(uport->irq); spin_lock_irqsave(&uport->lock, flags); msm_uport->wakeup.enabled = false; spin_unlock_irqrestore(&uport->lock, flags); - MSM_HS_DBG("%s(): Disable Wakeup IRQ", __func__); + } else { + MSM_HS_WARN("%s:Wake up IRQ already disabled", __func__); } } @@ -2267,6 +2286,7 @@ int msm_hs_request_clock_off(struct uart_port *uport) { struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); int ret = 0; + int client_count = 0; mutex_lock(&msm_uport->mtx); /* @@ -2293,8 +2313,10 @@ int msm_hs_request_clock_off(struct uart_port *uport) atomic_set(&msm_uport->client_req_state, 1); msm_hs_resource_unvote(msm_uport); atomic_dec(&msm_uport->client_count); - MSM_HS_INFO("%s():DISABLE UART CLOCK: ioc %d\n", - __func__, atomic_read(&msm_uport->client_count)); + client_count = atomic_read(&msm_uport->client_count); + LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, + "%s: Client_Count %d\n", __func__, + client_count); exit_request_clock_off: return ret; } @@ -2323,8 +2345,9 @@ int msm_hs_request_clock_on(struct uart_port *uport) msm_hs_resource_vote(UARTDM_TO_MSM(uport)); atomic_inc(&msm_uport->client_count); client_count = atomic_read(&msm_uport->client_count); - MSM_HS_INFO("%s():ENABLE UART CLOCK: ioc %d\n", - __func__, client_count); + LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, + "%s: Client_Count %d\n", __func__, + client_count); /* Clear the flag */ if (msm_uport->obs) @@ -2342,11 +2365,8 @@ static irqreturn_t msm_hs_wakeup_isr(int irq, void *dev) struct uart_port *uport = &msm_uport->uport; struct tty_struct *tty = NULL; - msm_hs_resource_vote(msm_uport); spin_lock_irqsave(&uport->lock, flags); - MSM_HS_DBG("%s(): ignore %d\n", __func__, - msm_uport->wakeup.ignore); if (msm_uport->wakeup.ignore) msm_uport->wakeup.ignore = 0; else @@ -2362,13 +2382,15 @@ static irqreturn_t msm_hs_wakeup_isr(int irq, void *dev) tty_insert_flip_char(tty->port, msm_uport->wakeup.rx_to_inject, TTY_NORMAL); - MSM_HS_DBG("%s(): Inject 0x%x", __func__, - msm_uport->wakeup.rx_to_inject); + hex_dump_ipc(msm_uport, msm_uport->rx.ipc_rx_ctxt, + "Rx Inject", + &msm_uport->wakeup.rx_to_inject, 0, 1); + MSM_HS_INFO("Wakeup ISR.Ignore%d\n", + msm_uport->wakeup.ignore); } } spin_unlock_irqrestore(&uport->lock, flags); - msm_hs_resource_unvote(msm_uport); if (wakeup && msm_uport->wakeup.inject_rx) tty_flip_buffer_push(tty->port); @@ -2396,7 +2418,7 @@ static void msm_hs_unconfig_uart_gpios(struct uart_port *uport) ret = pinctrl_select_state(msm_uport->pinctrl, msm_uport->gpio_state_suspend); if (ret) - MSM_HS_ERR("%s(): Failed to pinctrl set_state", + MSM_HS_ERR("%s():Failed to pinctrl set_state", __func__); } else if (pdata) { if (gpio_is_valid(pdata->uart_tx_gpio)) @@ -2674,6 +2696,8 @@ static int msm_hs_startup(struct uart_port *uport) spin_lock_irqsave(&uport->lock, flags); atomic_set(&msm_uport->client_count, 0); atomic_set(&msm_uport->client_req_state, 0); + LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, + "%s: Client_Count 0\n", __func__); msm_hs_start_rx_locked(uport); spin_unlock_irqrestore(&uport->lock, flags); @@ -3092,17 +3116,19 @@ static void msm_hs_pm_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct msm_hs_port *msm_uport = get_matching_hs_port(pdev); int ret; + int client_count = 0; if (!msm_uport) goto err_suspend; mutex_lock(&msm_uport->mtx); + client_count = atomic_read(&msm_uport->client_count); /* For OBS, don't use wakeup interrupt, set gpio to suspended state */ if (msm_uport->obs) { ret = pinctrl_select_state(msm_uport->pinctrl, msm_uport->gpio_state_suspend); if (ret) - MSM_HS_ERR("%s(): Error selecting suspend state", + MSM_HS_ERR("%s():Error selecting pinctrl suspend state", __func__); } @@ -3111,8 +3137,10 @@ static void msm_hs_pm_suspend(struct device *dev) obs_manage_irq(msm_uport, false); msm_hs_clk_bus_unvote(msm_uport); if (!atomic_read(&msm_uport->client_req_state)) - toggle_wakeup_interrupt(msm_uport); - MSM_HS_DBG("%s(): return suspend\n", __func__); + enable_wakeup_interrupt(msm_uport); + LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, + "%s: PM State Suspended client_count %d\n", __func__, + client_count); mutex_unlock(&msm_uport->mtx); return; err_suspend: @@ -3124,17 +3152,26 @@ static int msm_hs_pm_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct msm_hs_port *msm_uport = get_matching_hs_port(pdev); - int ret; + int ret = 0; + int client_count = 0; - if (!msm_uport) - goto err_resume; + if (!msm_uport) { + dev_err(dev, "%s:Invalid uport\n", __func__); + return -ENODEV; + } mutex_lock(&msm_uport->mtx); + client_count = atomic_read(&msm_uport->client_count); if (msm_uport->pm_state == MSM_HS_PM_ACTIVE) goto exit_pm_resume; if (!atomic_read(&msm_uport->client_req_state)) - toggle_wakeup_interrupt(msm_uport); - msm_hs_clk_bus_vote(msm_uport); + disable_wakeup_interrupt(msm_uport); + ret = msm_hs_clk_bus_vote(msm_uport); + if (ret) { + MSM_HS_ERR("%s:Failed clock vote %d\n", __func__, ret); + dev_err(dev, "%s:Failed clock vote %d\n", __func__, ret); + goto exit_pm_resume; + } obs_manage_irq(msm_uport, true); msm_uport->pm_state = MSM_HS_PM_ACTIVE; msm_hs_resource_on(msm_uport); @@ -3144,17 +3181,15 @@ static int msm_hs_pm_resume(struct device *dev) ret = pinctrl_select_state(msm_uport->pinctrl, msm_uport->gpio_state_active); if (ret) - MSM_HS_ERR("%s(): Error selecting active state", + MSM_HS_ERR("%s():Error selecting active state", __func__); } - MSM_HS_DBG("%s(): return resume\n", __func__); + LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, + "%s:PM State:Active client_count %d\n", __func__, client_count); exit_pm_resume: mutex_unlock(&msm_uport->mtx); - return 0; -err_resume: - pr_err("%s(): invalid uport", __func__); - return 0; + return ret; } #ifdef CONFIG_PM @@ -3174,20 +3209,20 @@ static int msm_hs_pm_sys_suspend_noirq(struct device *dev) * If there is an active clk request or an impending userspace request * fail the suspend callback. */ - clk_cnt = atomic_read(&msm_uport->clk_count); + clk_cnt = atomic_read(&msm_uport->resource_count); client_count = atomic_read(&msm_uport->client_count); - if (clk_cnt || (pm_runtime_enabled(dev) && - !pm_runtime_suspended(dev))) { - MSM_HS_WARN("%s:Fail Suspend.clk_cnt:%d,clnt_count:%d,RPM:%d\n", - __func__, clk_cnt, client_count, - dev->power.runtime_status); + if (msm_uport->pm_state == MSM_HS_PM_ACTIVE) { + MSM_HS_WARN("%s:Fail Suspend.clk_cnt:%d,clnt_count:%d\n", + __func__, clk_cnt, client_count); ret = -EBUSY; goto exit_suspend_noirq; } prev_pwr_state = msm_uport->pm_state; msm_uport->pm_state = MSM_HS_PM_SYS_SUSPENDED; - MSM_HS_DBG("%s(): suspending", __func__); + LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, + "%s:PM State:Sys-Suspended client_count %d\n", __func__, + client_count); exit_suspend_noirq: mutex_unlock(&msm_uport->mtx); return ret; @@ -3207,9 +3242,10 @@ static int msm_hs_pm_sys_resume_noirq(struct device *dev) */ mutex_lock(&msm_uport->mtx); - MSM_HS_DBG("%s(): system resume", __func__); if (msm_uport->pm_state == MSM_HS_PM_SYS_SUSPENDED) msm_uport->pm_state = MSM_HS_PM_SUSPENDED; + LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, + "%s:PM State: Suspended\n", __func__); mutex_unlock(&msm_uport->mtx); return 0; } @@ -3257,6 +3293,7 @@ static int msm_hs_probe(struct platform_device *pdev) int core_irqres, bam_irqres, wakeup_irqres; struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data; unsigned long data; + char name[30]; if (pdev->dev.of_node) { dev_dbg(&pdev->dev, "device tree enabled\n"); @@ -3350,11 +3387,13 @@ static int msm_hs_probe(struct platform_device *pdev) iounmap(uport->membase); return -ENOMEM; } + + memset(name, 0, sizeof(name)); + scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev), + "_state"); msm_uport->ipc_msm_hs_log_ctxt = - ipc_log_context_create(IPC_MSM_HS_LOG_PAGES, - dev_name(msm_uport->uport.dev), 0); - pr_debug("%s: Device name is %s\n", __func__, - dev_name(msm_uport->uport.dev)); + ipc_log_context_create(IPC_MSM_HS_LOG_STATE_PAGES, + name, 0); if (!msm_uport->ipc_msm_hs_log_ctxt) { dev_err(&pdev->dev, "%s: error creating logging context", __func__); @@ -3439,6 +3478,36 @@ static int msm_hs_probe(struct platform_device *pdev) msm_uport->tx.flush = FLUSH_SHUTDOWN; msm_uport->rx.flush = FLUSH_SHUTDOWN; + memset(name, 0, sizeof(name)); + scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev), + "_tx"); + msm_uport->tx.ipc_tx_ctxt = + ipc_log_context_create(IPC_MSM_HS_LOG_DATA_PAGES, name, 0); + if (!msm_uport->tx.ipc_tx_ctxt) + dev_err(&pdev->dev, "%s: error creating tx logging context", + __func__); + + memset(name, 0, sizeof(name)); + scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev), + "_rx"); + msm_uport->rx.ipc_rx_ctxt = ipc_log_context_create( + IPC_MSM_HS_LOG_DATA_PAGES, name, 0); + if (!msm_uport->rx.ipc_rx_ctxt) + dev_err(&pdev->dev, "%s: error creating rx logging context", + __func__); + + memset(name, 0, sizeof(name)); + scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev), + "_pwr"); + msm_uport->ipc_msm_hs_pwr_ctxt = ipc_log_context_create( + IPC_MSM_HS_LOG_USER_PAGES, name, 0); + if (!msm_uport->ipc_msm_hs_pwr_ctxt) + dev_err(&pdev->dev, "%s: error creating usr logging context", + __func__); + + uport->irq = core_irqres; + msm_uport->bam_irq = bam_irqres; + clk_set_rate(msm_uport->clk, msm_uport->uport.uartclk); msm_hs_clk_bus_vote(msm_uport); ret = uartdm_init_port(uport); @@ -3622,9 +3691,9 @@ static void msm_hs_shutdown(struct uart_port *uport) UART_XMIT_SIZE, DMA_TO_DEVICE); msm_hs_resource_unvote(msm_uport); - rc = atomic_read(&msm_uport->clk_count); + rc = atomic_read(&msm_uport->resource_count); if (rc) { - atomic_set(&msm_uport->clk_count, 1); + atomic_set(&msm_uport->resource_count, 1); MSM_HS_WARN("%s(): removing extra vote\n", __func__); msm_hs_resource_unvote(msm_uport); } @@ -3635,6 +3704,8 @@ static void msm_hs_shutdown(struct uart_port *uport) if (atomic_read(&msm_uport->client_count)) { MSM_HS_WARN("%s: Client vote on, forcing to 0\n", __func__); atomic_set(&msm_uport->client_count, 0); + LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt, + "%s: Client_Count 0\n", __func__); } msm_hs_unconfig_uart_gpios(uport); MSM_HS_INFO("%s:UART port closed successfully\n", __func__); diff --git a/drivers/usb/gadget/function/f_cdev.c b/drivers/usb/gadget/function/f_cdev.c index 0a9a3afd72dd..b22ea656367e 100644 --- a/drivers/usb/gadget/function/f_cdev.c +++ b/drivers/usb/gadget/function/f_cdev.c @@ -529,6 +529,14 @@ static int usb_cser_notify(struct f_cdev *port, u8 type, u16 value, const unsigned len = sizeof(*notify) + length; void *buf; int status; + unsigned long flags; + + spin_lock_irqsave(&port->port_lock, flags); + if (!port->is_connected) { + spin_unlock_irqrestore(&port->port_lock, flags); + pr_debug("%s: port disconnected\n", __func__); + return -ENODEV; + } req = port->port_usb.notify_req; port->port_usb.notify_req = NULL; @@ -544,7 +552,9 @@ static int usb_cser_notify(struct f_cdev *port, u8 type, u16 value, notify->wValue = cpu_to_le16(value); notify->wIndex = cpu_to_le16(port->port_usb.data_id); notify->wLength = cpu_to_le16(length); + /* 2 byte data copy */ memcpy(buf, data, length); + spin_unlock_irqrestore(&port->port_lock, flags); status = usb_ep_queue(ep, req, GFP_ATOMIC); if (status < 0) { diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c index 6a6cdc8b502c..4f1435d006b2 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c @@ -216,7 +216,7 @@ static int hdmi_edid_reset_parser(struct hdmi_edid_ctrl *edid_ctrl) /* reset HDR related data */ edid_ctrl->hdr_supported = false; edid_ctrl->hdr_data.eotf = 0; - edid_ctrl->hdr_data.descriptor = 0; + edid_ctrl->hdr_data.metadata_type_one = false; edid_ctrl->hdr_data.max_luminance = 0; edid_ctrl->hdr_data.avg_luminance = 0; edid_ctrl->hdr_data.min_luminance = 0; @@ -794,7 +794,7 @@ static ssize_t hdmi_edid_sysfs_rda_hdr_data(struct device *dev, ret = scnprintf(buf, PAGE_SIZE, "%d, %u, %u, %u, %u, %u\n", edid_ctrl->hdr_supported, edid_ctrl->hdr_data.eotf, - edid_ctrl->hdr_data.descriptor, + edid_ctrl->hdr_data.metadata_type_one, edid_ctrl->hdr_data.max_luminance, edid_ctrl->hdr_data.avg_luminance, edid_ctrl->hdr_data.min_luminance); @@ -964,8 +964,8 @@ static void hdmi_edid_parse_hdrdb(struct hdmi_edid_ctrl *edid_ctrl, /* Byte 3: Electro-Optical Transfer Functions */ edid_ctrl->hdr_data.eotf = data_block[2] & 0x3F; - /* Byte 4: Static Metadata Descriptors */ - edid_ctrl->hdr_data.descriptor = data_block[3] & 0x1; + /* Byte 4: Static Metadata Descriptor Type 1 */ + edid_ctrl->hdr_data.metadata_type_one = (data_block[3] & 0x1) & BIT(0); /* Byte 5: Desired Content Maximum Luminance */ if (hdmi_edid_is_luminance_value_present(len, MAXIMUM_LUMINANCE)) @@ -2458,16 +2458,16 @@ u8 hdmi_edid_get_deep_color(void *input) * Return: HDR data. */ void hdmi_edid_get_hdr_data(void *input, - struct hdmi_edid_hdr_data *hdr_data) + struct hdmi_edid_hdr_data **hdr_data) { struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; - if (!edid_ctrl || !hdr_data) { + if (!edid_ctrl) { DEV_ERR("%s: invalid input\n", __func__); return; } - hdr_data = &edid_ctrl->hdr_data; + *hdr_data = &edid_ctrl->hdr_data; } bool hdmi_edid_is_s3d_mode_supported(void *input, u32 video_mode, u32 s3d_mode) diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.h b/drivers/video/fbdev/msm/mdss_hdmi_edid.h index c818f3fc0d19..ce6cecbb2e03 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.h @@ -31,14 +31,14 @@ struct hdmi_edid_init_data { /* * struct hdmi_edid_hdr_data - HDR Static Metadata * @eotf: Electro-Optical Transfer Function - * @descriptor: Static Metadata Descriptor + * @metadata_type_one: Static Metadata Type 1 support * @max_luminance: Desired Content Maximum Luminance * @avg_luminance: Desired Content Frame-average Luminance * @min_luminance: Desired Content Minimum Luminance */ struct hdmi_edid_hdr_data { u32 eotf; - u32 descriptor; + bool metadata_type_one; u32 max_luminance; u32 avg_luminance; u32 min_luminance; @@ -61,6 +61,6 @@ bool hdmi_edid_is_s3d_mode_supported(void *input, u32 video_mode, u32 s3d_mode); u8 hdmi_edid_get_deep_color(void *edid_ctrl); void hdmi_edid_get_hdr_data(void *edid_ctrl, - struct hdmi_edid_hdr_data *hdr_data); + struct hdmi_edid_hdr_data **hdr_data); #endif /* __HDMI_EDID_H__ */ diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c index fb59d0b03afe..9ce47ccb5e09 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c @@ -1041,6 +1041,7 @@ void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data) register_data.hdcp_ctx = &ctrl->lib_ctx; register_data.client_ops = &client_ops; register_data.txmtr_ops = &txmtr_ops; + register_data.device_type = HDCP_TXMTR_HDMI; register_data.client_ctx = ctrl; register_data.tethered = ctrl->tethered; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index 3d773371713d..10e7a2d1a940 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -79,6 +79,8 @@ #define HDMI_TX_MAX_FPS 120000 #define HDMI_TX_VERSION_403 0x40000003 /* msmcobalt */ +#define HDMI_GET_MSB(x) (x >> 8) +#define HDMI_GET_LSB(x) (x & 0xff) /* Enable HDCP by default */ static bool hdcp_feature_on = true; @@ -111,6 +113,9 @@ static int hdmi_tx_enable_power(struct hdmi_tx_ctrl *hdmi_ctrl, enum hdmi_tx_power_module_type module, int enable); static int hdmi_tx_setup_tmds_clk_rate(struct hdmi_tx_ctrl *hdmi_ctrl); static void hdmi_tx_fps_work(struct work_struct *work); +static int hdmi_tx_pinctrl_set_state(struct hdmi_tx_ctrl *hdmi_ctrl, + enum hdmi_tx_power_module_type module, bool active); +static void hdmi_panel_set_hdr_infoframe(struct hdmi_tx_ctrl *hdmi_ctrl); static struct mdss_hw hdmi_tx_hw = { .hw_ndx = MDSS_HW_HDMI, @@ -285,6 +290,29 @@ static inline bool hdmi_tx_is_hdcp_enabled(struct hdmi_tx_ctrl *hdmi_ctrl) hdmi_ctrl->hdcp_ops; } +/* + * The sink must support at least one electro-optical transfer function for + * HDMI controller to sendi the dynamic range and mastering infoframe. + */ +static inline bool hdmi_tx_is_hdr_supported(struct hdmi_tx_ctrl *hdmi_ctrl) +{ + struct hdmi_edid_hdr_data *hdr_data; + + hdmi_edid_get_hdr_data(hdmi_tx_get_fd(HDMI_TX_FEAT_EDID), &hdr_data); + + return (hdr_data->eotf & BIT(0)) || (hdr_data->eotf & BIT(1)) || + (hdr_data->eotf & BIT(2)); +} + +static inline bool hdmi_tx_metadata_type_one(struct hdmi_tx_ctrl *hdmi_ctrl) +{ + struct hdmi_edid_hdr_data *hdr_data; + + hdmi_edid_get_hdr_data(hdmi_tx_get_fd(HDMI_TX_FEAT_EDID), &hdr_data); + + return hdr_data->metadata_type_one; +} + static inline bool hdmi_tx_dc_support(struct hdmi_tx_ctrl *hdmi_ctrl) { return hdmi_ctrl->dc_feature_on && hdmi_ctrl->dc_support && @@ -478,25 +506,30 @@ void *hdmi_get_featuredata_from_sysfs_dev(struct device *device, } /* hdmi_tx_get_featuredata_from_sysfs_dev */ EXPORT_SYMBOL(hdmi_get_featuredata_from_sysfs_dev); -static int hdmi_tx_config_5v(struct hdmi_tx_ctrl *hdmi_ctrl, bool enable) +static int hdmi_tx_config_5v(struct hdmi_tx_ctrl *ctrl, bool enable) { - struct dss_module_power *pd = NULL; int ret = 0; + struct dss_module_power *pd = NULL; - if (!hdmi_ctrl) { - DEV_ERR("%s: invalid input\n", __func__); + if (!ctrl) { + DEV_ERR("%s: Invalid HDMI ctrl\n", __func__); ret = -EINVAL; goto end; } - pd = &hdmi_ctrl->pdata.power_data[HDMI_TX_HPD_PM]; - if (!pd || !pd->gpio_config) { - DEV_ERR("%s: Error: invalid power data\n", __func__); - ret = -EINVAL; - goto end; + if (ctrl->hdmi_tx_version >= HDMI_TX_VERSION_403) + ret = hdmi_tx_pinctrl_set_state(ctrl, HDMI_TX_HPD_PM, enable); + else { + pd = &ctrl->pdata.power_data[HDMI_TX_HPD_PM]; + if (!pd || !pd->gpio_config) { + DEV_ERR("%s: Invalid power data\n", __func__); + ret = -EINVAL; + goto end; + } + + gpio_set_value(pd->gpio_config->gpio, enable); } - gpio_set_value(pd->gpio_config->gpio, enable); end: return ret; } @@ -1220,12 +1253,6 @@ static ssize_t hdmi_tx_sysfs_wta_5v(struct device *dev, } mutex_lock(&hdmi_ctrl->tx_lock); - pd = &hdmi_ctrl->pdata.power_data[HDMI_TX_HPD_PM]; - if (!pd || !pd->gpio_config) { - DEV_ERR("%s: Error: invalid power data\n", __func__); - ret = -EINVAL; - goto end; - } ret = kstrtoint(buf, 10, &read); if (ret) { @@ -1245,6 +1272,72 @@ end: return ret; } +static ssize_t hdmi_tx_sysfs_wta_hdr_stream(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + u32 const hdr_param_count = 13; + struct hdmi_tx_ctrl *ctrl = NULL; + + ctrl = hdmi_tx_get_drvdata_from_sysfs_dev(dev); + if (!ctrl) { + pr_err("%s: invalid input\n", __func__); + ret = -EINVAL; + goto end; + } + + if (!hdmi_tx_is_hdr_supported(ctrl)) { + pr_err("%s: Sink does not support HDR\n", __func__); + ret = -EINVAL; + goto end; + } + + if (sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u %u", + &ctrl->hdr_data.eotf, + &ctrl->hdr_data.display_primaries_x[0], + &ctrl->hdr_data.display_primaries_y[0], + &ctrl->hdr_data.display_primaries_x[1], + &ctrl->hdr_data.display_primaries_y[1], + &ctrl->hdr_data.display_primaries_x[2], + &ctrl->hdr_data.display_primaries_y[2], + &ctrl->hdr_data.white_point_x, + &ctrl->hdr_data.white_point_y, + &ctrl->hdr_data.max_luminance, + &ctrl->hdr_data.min_luminance, + &ctrl->hdr_data.max_content_light_level, + &ctrl->hdr_data.max_average_light_level) + != hdr_param_count) { + pr_err("%s: Invalid HDR stream data\n", __func__); + ret = -EINVAL; + goto end; + } + + pr_debug("%s: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n", + __func__, + ctrl->hdr_data.eotf, + ctrl->hdr_data.display_primaries_x[0], + ctrl->hdr_data.display_primaries_y[0], + ctrl->hdr_data.display_primaries_x[1], + ctrl->hdr_data.display_primaries_y[1], + ctrl->hdr_data.display_primaries_x[2], + ctrl->hdr_data.display_primaries_y[2]); + + pr_debug("%s: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n", + __func__, + ctrl->hdr_data.white_point_x, + ctrl->hdr_data.white_point_y, + ctrl->hdr_data.max_luminance, + ctrl->hdr_data.min_luminance, + ctrl->hdr_data.max_content_light_level, + ctrl->hdr_data.max_average_light_level); + + hdmi_panel_set_hdr_infoframe(ctrl); + + ret = strnlen(buf, PAGE_SIZE); +end: + return ret; +} + static DEVICE_ATTR(connected, S_IRUGO, hdmi_tx_sysfs_rda_connected, NULL); static DEVICE_ATTR(hdmi_audio_cb, S_IWUSR, NULL, hdmi_tx_sysfs_wta_audio_cb); static DEVICE_ATTR(hot_plug, S_IWUSR, NULL, hdmi_tx_sysfs_wta_hot_plug); @@ -1265,6 +1358,7 @@ static DEVICE_ATTR(avi_cn0_1, S_IWUSR, NULL, hdmi_tx_sysfs_wta_avi_cn_bits); static DEVICE_ATTR(s3d_mode, S_IRUGO | S_IWUSR, hdmi_tx_sysfs_rda_s3d_mode, hdmi_tx_sysfs_wta_s3d_mode); static DEVICE_ATTR(5v, S_IWUSR, NULL, hdmi_tx_sysfs_wta_5v); +static DEVICE_ATTR(hdr_stream, S_IWUSR, NULL, hdmi_tx_sysfs_wta_hdr_stream); static struct attribute *hdmi_tx_fs_attrs[] = { &dev_attr_connected.attr, @@ -1280,6 +1374,7 @@ static struct attribute *hdmi_tx_fs_attrs[] = { &dev_attr_avi_cn0_1.attr, &dev_attr_s3d_mode.attr, &dev_attr_5v.attr, + &dev_attr_hdr_stream.attr, NULL, }; static struct attribute_group hdmi_tx_fs_attrs_group = { @@ -2209,7 +2304,7 @@ static int hdmi_tx_check_capability(struct hdmi_tx_ctrl *hdmi_ctrl) DEV_DBG("%s: Features <HDMI:%s, HDCP:%s, Deep Color:%s>\n", __func__, hdmi_disabled ? "OFF" : "ON", hdcp_disabled ? "OFF" : "ON", - hdmi_ctrl->dc_feature_on ? "OFF" : "ON"); + !hdmi_ctrl->dc_feature_on ? "OFF" : "ON"); if (hdmi_disabled) { DEV_ERR("%s: HDMI disabled\n", __func__); @@ -2635,6 +2730,102 @@ static void hdmi_tx_phy_reset(struct hdmi_tx_ctrl *hdmi_ctrl) DSS_REG_W_ND(io, HDMI_PHY_CTRL, val | SW_RESET_PLL); } /* hdmi_tx_phy_reset */ +static void hdmi_panel_set_hdr_infoframe(struct hdmi_tx_ctrl *ctrl) +{ + u32 packet_payload = 0; + u32 packet_header = 0; + u32 packet_control = 0; + u32 const type_code = 0x87; + u32 const version = 0x01; + u32 const length = 0x1a; + u32 const descriptor_id = 0x00; + struct dss_io_data *io = NULL; + + if (!ctrl) { + pr_err("%s: invalid input\n", __func__); + return; + } + + if (!hdmi_tx_is_hdr_supported(ctrl)) { + pr_err("%s: Sink does not support HDR\n", __func__); + return; + } + + io = &ctrl->pdata.io[HDMI_TX_CORE_IO]; + if (!io->base) { + pr_err("%s: core io not inititalized\n", __func__); + return; + } + + /* Setup Packet header and payload */ + packet_header = type_code | (version << 8) | (length << 16); + DSS_REG_W(io, HDMI_GENERIC0_HDR, packet_header); + + packet_payload = (ctrl->hdr_data.eotf << 8); + if (hdmi_tx_metadata_type_one(ctrl)) { + packet_payload |= (descriptor_id << 16) + | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_x[0]) + << 24); + DSS_REG_W(io, HDMI_GENERIC0_0, packet_payload); + } else { + pr_debug("%s: Metadata Type 1 not supported\n", __func__); + DSS_REG_W(io, HDMI_GENERIC0_0, packet_payload); + goto enable_packet_control; + } + + packet_payload = + (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_x[0])) + | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_y[0]) << 8) + | (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_y[0]) << 16) + | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_x[1]) << 24); + DSS_REG_W(io, HDMI_GENERIC0_1, packet_payload); + + packet_payload = + (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_x[1])) + | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_y[1]) << 8) + | (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_y[1]) << 16) + | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_x[2]) << 24); + DSS_REG_W(io, HDMI_GENERIC0_2, packet_payload); + + packet_payload = + (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_x[2])) + | (HDMI_GET_LSB(ctrl->hdr_data.display_primaries_y[2]) << 8) + | (HDMI_GET_MSB(ctrl->hdr_data.display_primaries_y[2]) << 16) + | (HDMI_GET_LSB(ctrl->hdr_data.white_point_x) << 24); + DSS_REG_W(io, HDMI_GENERIC0_3, packet_payload); + + packet_payload = + (HDMI_GET_MSB(ctrl->hdr_data.white_point_x)) + | (HDMI_GET_LSB(ctrl->hdr_data.white_point_y) << 8) + | (HDMI_GET_MSB(ctrl->hdr_data.white_point_y) << 16) + | (HDMI_GET_LSB(ctrl->hdr_data.max_luminance) << 24); + DSS_REG_W(io, HDMI_GENERIC0_4, packet_payload); + + packet_payload = + (HDMI_GET_MSB(ctrl->hdr_data.max_luminance)) + | (HDMI_GET_LSB(ctrl->hdr_data.min_luminance) << 8) + | (HDMI_GET_MSB(ctrl->hdr_data.min_luminance) << 16) + | (HDMI_GET_LSB(ctrl->hdr_data.max_content_light_level) << 24); + DSS_REG_W(io, HDMI_GENERIC0_5, packet_payload); + + packet_payload = + (HDMI_GET_MSB(ctrl->hdr_data.max_content_light_level)) + | (HDMI_GET_LSB(ctrl->hdr_data.max_average_light_level) << 8) + | (HDMI_GET_MSB(ctrl->hdr_data.max_average_light_level) << 16); + DSS_REG_W(io, HDMI_GENERIC0_6, packet_payload); + +enable_packet_control: + /* + * GENERIC0_LINE | GENERIC0_CONT | GENERIC0_SEND + * Setup HDMI TX generic packet control + * Enable this packet to transmit every frame + * Enable HDMI TX engine to transmit Generic packet 1 + */ + packet_control = DSS_REG_R_ND(io, HDMI_GEN_PKT_CTRL); + packet_control |= BIT(0) | BIT(1) | BIT(2) | BIT(16); + DSS_REG_W(io, HDMI_GEN_PKT_CTRL, packet_control); +} + static int hdmi_tx_audio_info_setup(struct platform_device *pdev, struct msm_ext_disp_audio_setup_params *params) { diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.h b/drivers/video/fbdev/msm/mdss_hdmi_tx.h index 462edac31c09..1c306df70c7e 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.h @@ -20,6 +20,7 @@ #include "mdss_hdmi_audio.h" #define MAX_SWITCH_NAME_SIZE 5 +#define HDR_PRIMARIES_COUNT 3 enum hdmi_tx_io_type { HDMI_TX_CORE_IO, @@ -61,6 +62,30 @@ struct hdmi_tx_pinctrl { struct hdmi_tx_ctrl; typedef int (*hdmi_tx_evt_handler) (struct hdmi_tx_ctrl *); +/* + * struct hdmi_tx_hdr_stream - HDR video stream characteristics + * @eotf: Electro-Optical Transfer Function + * @display_primaries_x: display primaries data for x-coordinate + * @display_primaries_y: display primaries data for y-coordinate + * @white_point_x: white point data for x-coordinate + * @white_point_y: white point data for y-coordinate + * @max_luminance: content maximum luminance + * @min_luminance: content minimum luminance + * @max_content_light_level: content maximum light level + * @max_average_light_level: content average light level + */ +struct hdmi_tx_hdr_stream_data { + u32 eotf; + u32 display_primaries_x[HDR_PRIMARIES_COUNT]; + u32 display_primaries_y[HDR_PRIMARIES_COUNT]; + u32 white_point_x; + u32 white_point_y; + u32 max_luminance; + u32 min_luminance; + u32 max_content_light_level; + u32 max_average_light_level; +}; + struct hdmi_tx_ctrl { struct platform_device *pdev; struct hdmi_tx_platform_data pdata; @@ -88,6 +113,7 @@ struct hdmi_tx_ctrl { struct hdmi_panel_ops panel_ops; struct msm_ext_disp_audio_setup_params audio_params; struct work_struct fps_work; + struct hdmi_tx_hdr_stream_data hdr_data; spinlock_t hpd_state_lock; diff --git a/include/asm-generic/preempt.h b/include/asm-generic/preempt.h index 5d8ffa3e6f8c..c1cde3577551 100644 --- a/include/asm-generic/preempt.h +++ b/include/asm-generic/preempt.h @@ -7,10 +7,10 @@ static __always_inline int preempt_count(void) { - return current_thread_info()->preempt_count; + return READ_ONCE(current_thread_info()->preempt_count); } -static __always_inline int *preempt_count_ptr(void) +static __always_inline volatile int *preempt_count_ptr(void) { return ¤t_thread_info()->preempt_count; } diff --git a/include/linux/hdcp_qseecom.h b/include/linux/hdcp_qseecom.h index d9835193961e..26e97700fc73 100644 --- a/include/linux/hdcp_qseecom.h +++ b/include/linux/hdcp_qseecom.h @@ -108,9 +108,15 @@ struct hdcp_client_ops { int (*wakeup)(struct hdmi_hdcp_wakeup_data *data); }; +enum hdcp_device_type { + HDCP_TXMTR_HDMI = 0x8001, + HDCP_TXMTR_DP = 0x8002 +}; + struct hdcp_register_data { struct hdcp_client_ops *client_ops; struct hdcp_txmtr_ops *txmtr_ops; + enum hdcp_device_type device_type; void *client_ctx; void **hdcp_ctx; bool tethered; diff --git a/include/linux/ipa.h b/include/linux/ipa.h index d152057af385..5f85508353c9 100644 --- a/include/linux/ipa.h +++ b/include/linux/ipa.h @@ -1096,6 +1096,11 @@ int ipa_reset_endpoint(u32 clnt_hdl); int ipa_clear_endpoint_delay(u32 clnt_hdl); /* + * Disable ep + */ +int ipa_disable_endpoint(u32 clnt_hdl); + +/* * Configuration */ int ipa_cfg_ep(u32 clnt_hdl, const struct ipa_ep_cfg *ipa_ep_cfg); @@ -1462,6 +1467,14 @@ static inline int ipa_clear_endpoint_delay(u32 clnt_hdl) } /* + * Disable ep + */ +static inline int ipa_disable_endpoint(u32 clnt_hdl) +{ + return -EPERM; +} + +/* * Configuration */ static inline int ipa_cfg_ep(u32 clnt_hdl, diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index b081a56e250f..7d1e374e176c 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -214,6 +214,8 @@ enum power_supply_property { POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, POWER_SUPPLY_PROP_PD_ALLOWED, POWER_SUPPLY_PROP_PD_ACTIVE, + POWER_SUPPLY_PROP_CHARGER_TEMP, + POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, /* Local extensions of type int64_t */ POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT, /* Properties of type `const char *' */ diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h index 39527ed83fa7..478243712d07 100644 --- a/include/soc/qcom/socinfo.h +++ b/include/soc/qcom/socinfo.h @@ -90,6 +90,8 @@ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8929") #define early_machine_is_msmcobalt() \ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmcobalt") +#define early_machine_is_apqcobalt() \ + of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,apqcobalt") #define early_machine_is_msmhamster() \ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmhamster") #define early_machine_is_msmfalcon() \ @@ -128,6 +130,7 @@ #define early_machine_is_msm8976() 0 #define early_machine_is_msm8929() 0 #define early_machine_is_msmcobalt() 0 +#define early_machine_is_apqcobalt() 0 #define early_machine_is_msmhamster() 0 #define early_machine_is_msmfalcon() 0 #endif diff --git a/include/sound/wcd-dsp-mgr.h b/include/sound/wcd-dsp-mgr.h new file mode 100644 index 000000000000..5adcbcf660ba --- /dev/null +++ b/include/sound/wcd-dsp-mgr.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __WCD_DSP_MGR_H__ +#define __WCD_DSP_MGR_H__ + +#include <linux/types.h> + +/* + * These enums correspond to the component types + * that wcd-dsp-manager driver will use. The order + * of the enums specifies the order in which the + * manager driver will perform the sequencing. + * Changing this will cause the sequencing order + * to be changed as well. + */ +enum wdsp_cmpnt_type { + /* Component to control the DSP */ + WDSP_CMPNT_CONTROL = 0, + /* Component to perform data transfer to/from DSP */ + WDSP_CMPNT_TRANSPORT, + /* Component that performs high level IPC */ + WDSP_CMPNT_IPC, + + WDSP_CMPNT_TYPE_MAX, +}; + +enum wdsp_event_type { + /* Image download related */ + WDSP_EVENT_PRE_DLOAD_CODE, + WDSP_EVENT_DLOAD_SECTION, + WDSP_EVENT_POST_DLOAD_CODE, + WDSP_EVENT_PRE_DLOAD_DATA, + WDSP_EVENT_POST_DLOAD_DATA, + WDSP_EVENT_DLOAD_FAILED, + + /* DSP boot related */ + WDSP_EVENT_PRE_BOOTUP, + WDSP_EVENT_DO_BOOT, + WDSP_EVENT_POST_BOOTUP, + WDSP_EVENT_PRE_SHUTDOWN, + WDSP_EVENT_DO_SHUTDOWN, + WDSP_EVENT_POST_SHUTDOWN, + + /* IRQ handling related */ + WDSP_EVENT_IPC1_INTR, + + /* Suspend/Resume related */ + WDSP_EVENT_SUSPEND, + WDSP_EVENT_RESUME, +}; + +enum wdsp_intr { + WDSP_IPC1_INTR, +}; + +/* + * wdsp_cmpnt_ops: ops/function callbacks for components + * @init: called by manager driver, component is expected + * to initialize itself in this callback + * @deinit: called by manager driver, component should + * de-initialize itself in this callback + * @event_handler: Event handler for each component, called + * by the manager as per sequence + */ +struct wdsp_cmpnt_ops { + int (*init)(struct device *, void *priv_data); + int (*deinit)(struct device *, void *priv_data); + int (*event_handler)(struct device *, void *priv_data, + enum wdsp_event_type, void *data); +}; + +struct wdsp_img_section { + u32 addr; + size_t size; + u8 *data; +}; + +/* + * wdsp_ops: ops/function callbacks for manager driver + * @register_cmpnt_ops: components will use this to register + * their own ops to manager driver + * @get_dev_for_cmpnt: components can use this to get handle + * to struct device * of any other component + * @intr_handler: callback to notify manager driver that interrupt + * has occurred. + * @vote_for_dsp: notifies manager that dsp should be booted up + * @suspend: notifies manager that one component wants to suspend. + * Manager will make sure to suspend all components in order + * @resume: notifies manager that one component wants to resume. + * Manager will make sure to resume all components in order + */ + +struct wdsp_mgr_ops { + int (*register_cmpnt_ops)(struct device *wdsp_dev, + struct device *cdev, + void *priv_data, + struct wdsp_cmpnt_ops *ops); + struct device *(*get_dev_for_cmpnt)(struct device *wdsp_dev, + enum wdsp_cmpnt_type type); + int (*intr_handler)(struct device *wdsp_dev, + enum wdsp_intr intr); + int (*vote_for_dsp)(struct device *wdsp_dev, bool vote); + int (*suspend)(struct device *wdsp_dev); + int (*resume)(struct device *wdsp_dev); +}; + +#endif /* end of __WCD_DSP_MGR_H__ */ diff --git a/include/sound/wcd-spi.h b/include/sound/wcd-spi.h new file mode 100644 index 000000000000..1fff58d727a1 --- /dev/null +++ b/include/sound/wcd-spi.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __WCD_SPI_H__ +#define __WCD_SPI_H__ + +struct wcd_spi_msg { + /* + * Caller's buffer pointer that holds data to + * be transmitted in case of data_write and + * data to be copied to in case of data_read. + */ + void *data; + + /* Length of data to write/read */ + size_t len; + + /* + * Address in remote memory to write to + * or read from. + */ + u32 remote_addr; + + /* Bitmask of flags, currently unused */ + u32 flags; +}; + +#ifdef CONFIG_SND_SOC_WCD_SPI + +int wcd_spi_data_write(struct spi_device *spi, struct wcd_spi_msg *msg); +int wcd_spi_data_read(struct spi_device *spi, struct wcd_spi_msg *msg); + +#else + +int wcd_spi_data_write(struct spi_device *spi, struct wcd_spi_msg *msg) +{ + return -ENODEV; +} + +int wcd_spi_data_read(struct spi_device *spi, struct wcd_spi_msg *msg) +{ + return -ENODEV; +} + +#endif /* End of CONFIG_SND_SOC_WCD_SPI */ + +#endif /* End of __WCD_SPI_H__ */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 871c2980f4f7..6fc326421108 100755 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -762,6 +762,13 @@ config SND_SOC_WCD_MBHC tristate default y if (SND_SOC_MSM8909_WCD=y || SND_SOC_MSM8X16_WCD=y || SND_SOC_WCD9335=y) && SND_SOC_MDMCALIFORNIUM!=y +config SND_SOC_WCD_DSP_MGR + tristate + +config SND_SOC_WCD_SPI + depends on CONFIG_SPI + tristate + config SND_SOC_WL1273 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index ed2e49ddf432..4b79b277ce29 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -143,6 +143,9 @@ snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o snd-soc-msm8952-wcd-objs := msm8x16-wcd.o msm8x16-wcd-tables.o snd-soc-wsa881x-analog-objs := wsa881x-analog.o wsa881x-tables-analog.o snd-soc-wsa881x-analog-objs += wsa881x-regmap-analog.o wsa881x-irq.o +snd-soc-wcd-dsp-utils-objs := wcd-dsp-utils.o +snd-soc-wcd-dsp-mgr-objs := wcd-dsp-mgr.o +snd-soc-wcd-spi-objs := wcd-spi.o snd-soc-wl1273-objs := wl1273.o snd-soc-wm-adsp-objs := wm_adsp.o snd-soc-wm0010-objs := wm0010.o @@ -351,6 +354,8 @@ obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o obj-$(CONFIG_SND_SOC_WSA881X_ANALOG) += snd-soc-wsa881x-analog.o obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o +obj-$(CONFIG_SND_SOC_WCD_DSP_MGR) += snd-soc-wcd-dsp-mgr.o snd-soc-wcd-dsp-utils.o +obj-$(CONFIG_SND_SOC_WCD_SPI) += snd-soc-wcd-spi.o obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/sound/soc/codecs/wcd-dsp-mgr.c new file mode 100644 index 000000000000..69246ac9cc87 --- /dev/null +++ b/sound/soc/codecs/wcd-dsp-mgr.c @@ -0,0 +1,803 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/stringify.h> +#include <linux/of.h> +#include <linux/component.h> +#include <sound/wcd-dsp-mgr.h> +#include "wcd-dsp-utils.h" + +/* Forward declarations */ +static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type); + +/* Component related macros */ +#define WDSP_GET_COMPONENT(wdsp, x) (&(wdsp->cmpnts[x])) +#define WDSP_GET_CMPNT_TYPE_STR(x) wdsp_get_cmpnt_type_string(x) + +/* + * These #defines indicate the bit number in status field + * for each of the status. If bit is set, it indicates + * the status as done, else if bit is not set, it indicates + * the status is either failed or not done. + */ +#define WDSP_STATUS_INITIALIZED BIT(0) +#define WDSP_STATUS_CODE_DLOADED BIT(1) +#define WDSP_STATUS_DATA_DLOADED BIT(2) +#define WDSP_STATUS_BOOTED BIT(3) + +/* Helper macros for printing wdsp messages */ +#define WDSP_ERR(wdsp, fmt, ...) \ + dev_err(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__) +#define WDSP_DBG(wdsp, fmt, ...) \ + dev_dbg(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__) + +/* Helper macros for locking */ +#define WDSP_MGR_MUTEX_LOCK(wdsp, lock) \ +{ \ + WDSP_DBG(wdsp, "mutex_lock(%s)", \ + __stringify_1(lock)); \ + mutex_lock(&lock); \ +} + +#define WDSP_MGR_MUTEX_UNLOCK(wdsp, lock) \ +{ \ + WDSP_DBG(wdsp, "mutex_unlock(%s)", \ + __stringify_1(lock)); \ + mutex_unlock(&lock); \ +} + +/* Helper macros for using status mask */ +#define WDSP_SET_STATUS(wdsp, state) \ +{ \ + wdsp->status |= state; \ + WDSP_DBG(wdsp, "set 0x%lx, new_state = 0x%x", \ + state, wdsp->status); \ +} + +#define WDSP_CLEAR_STATUS(wdsp, state) \ +{ \ + wdsp->status &= (~state); \ + WDSP_DBG(wdsp, "clear 0x%lx, new_state = 0x%x", \ + state, wdsp->status); \ +} + +#define WDSP_STATUS_IS_SET(wdsp, state) (wdsp->status & state) + +struct wdsp_cmpnt { + + /* OF node of the phandle */ + struct device_node *np; + + /* + * Child component's dev_name, should be set in DT for the child's + * phandle if child's dev->of_node does not match the phandle->of_node + */ + const char *cdev_name; + + /* Child component's device node */ + struct device *cdev; + + /* Private data that component may want back on callbacks */ + void *priv_data; + + /* Child ops */ + struct wdsp_cmpnt_ops *ops; +}; + +struct wdsp_mgr_priv { + + /* Manager driver's struct device pointer */ + struct device *mdev; + + /* Match struct for component framework */ + struct component_match *match; + + /* Manager's ops/function callbacks */ + struct wdsp_mgr_ops *ops; + + /* Array to store information for all expected components */ + struct wdsp_cmpnt cmpnts[WDSP_CMPNT_TYPE_MAX]; + + /* The filename of image to be downloaded */ + const char *img_fname; + + /* Keeps track of current state of manager driver */ + u32 status; + + /* Work to load the firmware image after component binding */ + struct work_struct load_fw_work; + + /* List of segments in image to be downloaded */ + struct list_head *seg_list; + + /* Base address of the image in memory */ + u32 base_addr; + + /* Instances using dsp */ + int dsp_users; + + /* Lock for serializing ops called by components */ + struct mutex api_mutex; +}; + +static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type) +{ + switch (type) { + case WDSP_CMPNT_CONTROL: + return "control"; + case WDSP_CMPNT_IPC: + return "ipc"; + case WDSP_CMPNT_TRANSPORT: + return "transport"; + default: + pr_err("%s: Invalid component type %d\n", + __func__, type); + return "Invalid"; + } +} + +static void wdsp_broadcast_event_upseq(struct wdsp_mgr_priv *wdsp, + enum wdsp_event_type event, + void *data) +{ + struct wdsp_cmpnt *cmpnt; + int i; + + for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) + cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data, + event, data); + } +} + +static void wdsp_broadcast_event_downseq(struct wdsp_mgr_priv *wdsp, + enum wdsp_event_type event, + void *data) +{ + struct wdsp_cmpnt *cmpnt; + int i; + + for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) + cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data, + event, data); + } +} + +static int wdsp_unicast_event(struct wdsp_mgr_priv *wdsp, + enum wdsp_cmpnt_type type, + enum wdsp_event_type event, + void *data) +{ + struct wdsp_cmpnt *cmpnt; + int ret; + + cmpnt = WDSP_GET_COMPONENT(wdsp, type); + if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) { + ret = cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data, + event, data); + } else { + WDSP_ERR(wdsp, "not valid event_handler for %s", + WDSP_GET_CMPNT_TYPE_STR(type)); + ret = -EINVAL; + } + + return ret; +} + +static int wdsp_init_components(struct wdsp_mgr_priv *wdsp) +{ + struct wdsp_cmpnt *cmpnt; + int fail_idx = WDSP_CMPNT_TYPE_MAX; + int i, ret = 0; + + for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) { + + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + + /* Init is allowed to be NULL */ + if (!cmpnt->ops || !cmpnt->ops->init) + continue; + ret = cmpnt->ops->init(cmpnt->cdev, cmpnt->priv_data); + if (ret) { + WDSP_ERR(wdsp, "Init failed (%d) for component %s", + ret, WDSP_GET_CMPNT_TYPE_STR(i)); + fail_idx = i; + break; + } + } + + if (fail_idx < WDSP_CMPNT_TYPE_MAX) { + /* Undo init for already initialized components */ + for (i = fail_idx - 1; i >= 0; i--) { + struct wdsp_cmpnt *cmpnt = WDSP_GET_COMPONENT(wdsp, i); + + if (cmpnt->ops && cmpnt->ops->deinit) + cmpnt->ops->deinit(cmpnt->cdev, + cmpnt->priv_data); + } + } + + return ret; +} + +static int wdsp_load_each_segment(struct wdsp_mgr_priv *wdsp, + struct wdsp_img_segment *seg) +{ + struct wdsp_img_section img_section; + int ret; + + WDSP_DBG(wdsp, + "base_addr 0x%x, split_fname %s, load_addr 0x%x, size 0x%zx", + wdsp->base_addr, seg->split_fname, seg->load_addr, seg->size); + + if (seg->load_addr < wdsp->base_addr) { + WDSP_ERR(wdsp, "Invalid addr 0x%x, base_addr = 0x%x", + seg->load_addr, wdsp->base_addr); + return -EINVAL; + } + + img_section.addr = seg->load_addr - wdsp->base_addr; + img_section.size = seg->size; + img_section.data = seg->data; + + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT, + WDSP_EVENT_DLOAD_SECTION, + &img_section); + if (IS_ERR_VALUE(ret)) + WDSP_ERR(wdsp, + "Failed, err = %d for base_addr = 0x%x split_fname = %s, load_addr = 0x%x, size = 0x%zx", + ret, wdsp->base_addr, seg->split_fname, + seg->load_addr, seg->size); + return ret; +} + +static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp, + unsigned int type) +{ + struct wdsp_cmpnt *ctl; + struct wdsp_img_segment *seg = NULL; + enum wdsp_event_type pre, post; + int ret; + + ctl = WDSP_GET_COMPONENT(wdsp, WDSP_CMPNT_CONTROL); + + if (type == WDSP_ELF_FLAG_RE) { + pre = WDSP_EVENT_PRE_DLOAD_CODE; + post = WDSP_EVENT_POST_DLOAD_CODE; + } else if (type == WDSP_ELF_FLAG_WRITE) { + pre = WDSP_EVENT_PRE_DLOAD_DATA; + post = WDSP_EVENT_POST_DLOAD_DATA; + } else { + WDSP_ERR(wdsp, "Invalid type %u", type); + return -EINVAL; + } + + ret = wdsp_get_segment_list(ctl->cdev, wdsp->img_fname, + type, wdsp->seg_list, &wdsp->base_addr); + if (IS_ERR_VALUE(ret) || + list_empty(wdsp->seg_list)) { + WDSP_ERR(wdsp, "Error %d to get image segments for type %d", + ret, type); + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_DLOAD_FAILED, + NULL); + goto done; + } + + /* Notify all components that image is about to be downloaded */ + wdsp_broadcast_event_upseq(wdsp, pre, NULL); + + /* Go through the list of segments and download one by one */ + list_for_each_entry(seg, wdsp->seg_list, list) { + ret = wdsp_load_each_segment(wdsp, seg); + if (IS_ERR_VALUE(ret)) { + wdsp_broadcast_event_downseq(wdsp, + WDSP_EVENT_DLOAD_FAILED, + NULL); + goto dload_error; + } + } + + /* Notify all components that image is downloaded */ + wdsp_broadcast_event_downseq(wdsp, post, NULL); + +dload_error: + wdsp_flush_segment_list(wdsp->seg_list); +done: + return ret; +} + +static void wdsp_load_fw_image(struct work_struct *work) +{ + struct wdsp_mgr_priv *wdsp; + struct wdsp_cmpnt *cmpnt; + int ret, idx; + + wdsp = container_of(work, struct wdsp_mgr_priv, load_fw_work); + if (!wdsp) { + pr_err("%s: Invalid private_data\n", __func__); + goto done; + } + + /* Initialize the components first */ + ret = wdsp_init_components(wdsp); + if (IS_ERR_VALUE(ret)) + goto done; + + /* Set init done status */ + WDSP_SET_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + + /* Download the read-execute sections of image */ + ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_RE); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Error %d to download code sections", ret); + for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, idx); + if (cmpnt->ops && cmpnt->ops->deinit) + cmpnt->ops->deinit(cmpnt->cdev, + cmpnt->priv_data); + } + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + } + + WDSP_SET_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED); +done: + return; +} + +static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp) +{ + int ret; + + /* Make sure wdsp is in good state */ + if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_CODE_DLOADED)) { + WDSP_ERR(wdsp, "WDSP in invalid state 0x%x", wdsp->status); + ret = -EINVAL; + goto done; + } + + /* Download the read-write sections of image */ + ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_WRITE); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Data section download failed, err = %d", ret); + goto done; + } + + WDSP_SET_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); + + wdsp_broadcast_event_upseq(wdsp, WDSP_EVENT_PRE_BOOTUP, NULL); + + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL, + WDSP_EVENT_DO_BOOT, NULL); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Failed to boot dsp, err = %d", ret); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); + goto done; + } + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_BOOTUP, NULL); + WDSP_SET_STATUS(wdsp, WDSP_STATUS_BOOTED); +done: + return ret; +} + +static int wdsp_disable_dsp(struct wdsp_mgr_priv *wdsp) +{ + int ret; + + /* Make sure wdsp is in good state */ + if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) { + WDSP_ERR(wdsp, "wdsp in invalid state 0x%x", wdsp->status); + ret = -EINVAL; + goto done; + } + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN, NULL); + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL, + WDSP_EVENT_DO_SHUTDOWN, NULL); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Failed to shutdown dsp, err = %d", ret); + goto done; + } + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN, NULL); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED); + + /* Data sections are to be downloaded per boot */ + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); +done: + return ret; +} + +static int wdsp_register_cmpnt_ops(struct device *wdsp_dev, + struct device *cdev, + void *priv_data, + struct wdsp_cmpnt_ops *ops) +{ + struct wdsp_mgr_priv *wdsp; + struct wdsp_cmpnt *cmpnt; + int i, ret; + + if (!wdsp_dev || !cdev || !ops) + return -EINVAL; + + wdsp = dev_get_drvdata(wdsp_dev); + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); + + for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if ((cdev->of_node && cdev->of_node == cmpnt->np) || + (cmpnt->cdev_name && + !strcmp(dev_name(cdev), cmpnt->cdev_name))) { + break; + } + } + + if (i == WDSP_CMPNT_TYPE_MAX) { + WDSP_ERR(wdsp, "Failed to register component dev %s", + dev_name(cdev)); + ret = -EINVAL; + goto done; + } + + cmpnt->cdev = cdev; + cmpnt->ops = ops; + cmpnt->priv_data = priv_data; +done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); + return 0; +} + +static struct device *wdsp_get_dev_for_cmpnt(struct device *wdsp_dev, + enum wdsp_cmpnt_type type) +{ + struct wdsp_mgr_priv *wdsp; + struct wdsp_cmpnt *cmpnt; + + if (!wdsp_dev || type >= WDSP_CMPNT_TYPE_MAX) + return NULL; + + wdsp = dev_get_drvdata(wdsp_dev); + cmpnt = WDSP_GET_COMPONENT(wdsp, type); + + return cmpnt->cdev; +} + +static int wdsp_intr_handler(struct device *wdsp_dev, + enum wdsp_intr intr) +{ + struct wdsp_mgr_priv *wdsp; + int ret; + + if (!wdsp_dev) + return -EINVAL; + + wdsp = dev_get_drvdata(wdsp_dev); + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); + + switch (intr) { + case WDSP_IPC1_INTR: + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_IPC, + WDSP_EVENT_IPC1_INTR, NULL); + break; + default: + ret = -EINVAL; + break; + } + + if (IS_ERR_VALUE(ret)) + WDSP_ERR(wdsp, "handling intr %d failed with error %d", + intr, ret); + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); + + return ret; +} + +static int wdsp_vote_for_dsp(struct device *wdsp_dev, + bool vote) +{ + struct wdsp_mgr_priv *wdsp; + int ret = 0; + + if (!wdsp_dev) + return -EINVAL; + + wdsp = dev_get_drvdata(wdsp_dev); + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); + WDSP_DBG(wdsp, "request %s, current users = %d", + vote ? "enable" : "disable", wdsp->dsp_users); + + if (vote) { + wdsp->dsp_users++; + if (wdsp->dsp_users == 1) + ret = wdsp_enable_dsp(wdsp); + } else { + if (wdsp->dsp_users == 0) + goto done; + + wdsp->dsp_users--; + if (wdsp->dsp_users == 0) + ret = wdsp_disable_dsp(wdsp); + } + + if (IS_ERR_VALUE(ret)) + WDSP_DBG(wdsp, "wdsp %s failed, err = %d", + vote ? "enable" : "disable", ret); + +done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); + return ret; +} + +static int wdsp_suspend(struct device *wdsp_dev) +{ + return 0; +} + +static int wdsp_resume(struct device *wdsp_dev) +{ + return 0; +} + +static struct wdsp_mgr_ops wdsp_ops = { + .register_cmpnt_ops = wdsp_register_cmpnt_ops, + .get_dev_for_cmpnt = wdsp_get_dev_for_cmpnt, + .intr_handler = wdsp_intr_handler, + .vote_for_dsp = wdsp_vote_for_dsp, + .suspend = wdsp_suspend, + .resume = wdsp_resume, +}; + +static int wdsp_mgr_compare_of(struct device *dev, void *data) +{ + struct wdsp_cmpnt *cmpnt = data; + + /* + * First try to match based on of_node, if of_node is not + * present, try to match on the dev_name + */ + return ((dev->of_node && dev->of_node == cmpnt->np) || + (cmpnt->cdev_name && + !strcmp(dev_name(dev), cmpnt->cdev_name))); +} + +static int wdsp_mgr_bind(struct device *dev) +{ + struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev); + struct wdsp_cmpnt *cmpnt; + int ret, idx; + + wdsp->ops = &wdsp_ops; + + ret = component_bind_all(dev, wdsp->ops); + if (IS_ERR_VALUE(ret)) + WDSP_ERR(wdsp, "component_bind_all failed %d\n", ret); + + /* Make sure all components registered ops */ + for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, idx); + if (!cmpnt->cdev || !cmpnt->ops) { + WDSP_ERR(wdsp, "%s did not register ops\n", + WDSP_GET_CMPNT_TYPE_STR(idx)); + ret = -EINVAL; + component_unbind_all(dev, wdsp->ops); + break; + } + } + + /* Schedule the work to download image if binding was successful. */ + if (!ret) + schedule_work(&wdsp->load_fw_work); + + return ret; +} + +static void wdsp_mgr_unbind(struct device *dev) +{ + struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev); + struct wdsp_cmpnt *cmpnt; + int idx; + + component_unbind_all(dev, wdsp->ops); + + /* Clear all status bits */ + wdsp->status = 0x00; + + /* clean up the components */ + for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, idx); + cmpnt->cdev = NULL; + cmpnt->ops = NULL; + cmpnt->priv_data = NULL; + } +} + +static const struct component_master_ops wdsp_master_ops = { + .bind = wdsp_mgr_bind, + .unbind = wdsp_mgr_unbind, +}; + +static void *wdsp_mgr_parse_phandle(struct wdsp_mgr_priv *wdsp, + int index) +{ + struct device *mdev = wdsp->mdev; + struct device_node *np; + struct wdsp_cmpnt *cmpnt = NULL; + struct of_phandle_args pargs; + u32 value; + int ret; + + ret = of_parse_phandle_with_fixed_args(mdev->of_node, + "qcom,wdsp-components", 1, + index, &pargs); + if (ret) { + WDSP_ERR(wdsp, "parse_phandle at index %d failed %d", + index, ret); + return NULL; + } + + np = pargs.np; + value = pargs.args[0]; + + if (value >= WDSP_CMPNT_TYPE_MAX) { + WDSP_ERR(wdsp, "invalid phandle_arg to of_node %s", np->name); + goto done; + } + + cmpnt = WDSP_GET_COMPONENT(wdsp, value); + if (cmpnt->np || cmpnt->cdev_name) { + WDSP_ERR(wdsp, "cmpnt %d already added", value); + cmpnt = NULL; + goto done; + } + + cmpnt->np = np; + of_property_read_string(np, "qcom,wdsp-cmpnt-dev-name", + &cmpnt->cdev_name); +done: + of_node_put(np); + return cmpnt; +} + +static int wdsp_mgr_parse_dt_entries(struct wdsp_mgr_priv *wdsp) +{ + struct device *dev = wdsp->mdev; + void *match_data; + int ph_idx, ret; + + ret = of_property_read_string(dev->of_node, "qcom,img-filename", + &wdsp->img_fname); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Reading property %s failed, error = %d", + "qcom,img-filename", ret); + return ret; + } + + ret = of_count_phandle_with_args(dev->of_node, + "qcom,wdsp-components", + NULL); + if (ret == -ENOENT) { + WDSP_ERR(wdsp, "Property %s not defined in DT", + "qcom,wdsp-components"); + goto done; + } else if (ret != WDSP_CMPNT_TYPE_MAX * 2) { + WDSP_ERR(wdsp, "Invalid phandle + arg count %d, expected %d", + ret, WDSP_CMPNT_TYPE_MAX * 2); + ret = -EINVAL; + goto done; + } + + ret = 0; + + for (ph_idx = 0; ph_idx < WDSP_CMPNT_TYPE_MAX; ph_idx++) { + + match_data = wdsp_mgr_parse_phandle(wdsp, ph_idx); + if (!match_data) { + WDSP_ERR(wdsp, "component not found at idx %d", ph_idx); + ret = -EINVAL; + goto done; + } + + component_match_add(dev, &wdsp->match, + wdsp_mgr_compare_of, match_data); + } + +done: + return ret; +} + +static int wdsp_mgr_probe(struct platform_device *pdev) +{ + struct wdsp_mgr_priv *wdsp; + struct device *mdev = &pdev->dev; + int ret; + + wdsp = devm_kzalloc(mdev, sizeof(*wdsp), GFP_KERNEL); + if (!wdsp) + return -ENOMEM; + wdsp->mdev = mdev; + wdsp->seg_list = devm_kzalloc(mdev, sizeof(struct list_head), + GFP_KERNEL); + if (!wdsp->seg_list) { + devm_kfree(mdev, wdsp); + return -ENOMEM; + } + + ret = wdsp_mgr_parse_dt_entries(wdsp); + if (ret) + goto err_dt_parse; + + INIT_WORK(&wdsp->load_fw_work, wdsp_load_fw_image); + INIT_LIST_HEAD(wdsp->seg_list); + mutex_init(&wdsp->api_mutex); + dev_set_drvdata(mdev, wdsp); + + ret = component_master_add_with_match(mdev, &wdsp_master_ops, + wdsp->match); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Failed to add master, err = %d", ret); + goto err_master_add; + } + + return 0; + +err_master_add: + mutex_destroy(&wdsp->api_mutex); +err_dt_parse: + devm_kfree(mdev, wdsp->seg_list); + devm_kfree(mdev, wdsp); + dev_set_drvdata(mdev, NULL); + + return ret; +} + +static int wdsp_mgr_remove(struct platform_device *pdev) +{ + struct device *mdev = &pdev->dev; + struct wdsp_mgr_priv *wdsp = dev_get_drvdata(mdev); + + component_master_del(mdev, &wdsp_master_ops); + + mutex_destroy(&wdsp->api_mutex); + devm_kfree(mdev, wdsp->seg_list); + devm_kfree(mdev, wdsp); + dev_set_drvdata(mdev, NULL); + + return 0; +}; + +static const struct of_device_id wdsp_mgr_dt_match[] = { + {.compatible = "qcom,wcd-dsp-mgr" }, + { } +}; + +static struct platform_driver wdsp_mgr_driver = { + .driver = { + .name = "wcd-dsp-mgr", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(wdsp_mgr_dt_match), + }, + .probe = wdsp_mgr_probe, + .remove = wdsp_mgr_remove, +}; +module_platform_driver(wdsp_mgr_driver); + +MODULE_DESCRIPTION("WCD DSP manager driver"); +MODULE_DEVICE_TABLE(of, wdsp_mgr_dt_match); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd-dsp-utils.c b/sound/soc/codecs/wcd-dsp-utils.c new file mode 100644 index 000000000000..1f048917a6c8 --- /dev/null +++ b/sound/soc/codecs/wcd-dsp-utils.c @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/firmware.h> +#include <linux/elf.h> +#include <linux/slab.h> +#include <linux/list.h> +#include "wcd-dsp-utils.h" + +static bool wdsp_is_valid_elf_hdr(const struct elf32_hdr *ehdr, + size_t fw_size) +{ + if (fw_size < sizeof(*ehdr)) { + pr_err("%s: Firmware too small\n", __func__); + goto elf_check_fail; + } + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) { + pr_err("%s: Not an ELF file\n", __func__); + goto elf_check_fail; + } + + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) { + pr_err("%s: Not an executable image\n", __func__); + goto elf_check_fail; + } + + if (ehdr->e_phnum == 0) { + pr_err("%s: no segments to load\n", __func__); + goto elf_check_fail; + } + + if (sizeof(struct elf32_phdr) * ehdr->e_phnum + + sizeof(struct elf32_hdr) > fw_size) { + pr_err("%s: Too small MDT file\n", __func__); + goto elf_check_fail; + } + + return true; + +elf_check_fail: + return false; +} + +static int wdsp_add_segment_to_list(struct device *dev, + const char *img_fname, + const struct elf32_phdr *phdr, + int phdr_idx, + struct list_head *seg_list) +{ + struct wdsp_img_segment *seg; + int ret = 0; + + /* Do not load segments with zero size */ + if (phdr->p_filesz == 0 || phdr->p_memsz == 0) + goto done; + + seg = kzalloc(sizeof(*seg), GFP_KERNEL); + if (!seg) { + ret = -ENOMEM; + goto done; + } + + snprintf(seg->split_fname, sizeof(seg->split_fname), + "%s.b%02d", img_fname, phdr_idx); + ret = request_firmware(&seg->split_fw, seg->split_fname, dev); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "%s: firmware %s not found\n", + __func__, seg->split_fname); + goto bad_seg; + } + + seg->load_addr = phdr->p_paddr; + seg->size = phdr->p_filesz; + seg->data = (u8 *) seg->split_fw->data; + + list_add_tail(&seg->list, seg_list); +done: + return ret; +bad_seg: + kfree(seg); + return ret; +} + +/* + * wdsp_flush_segment_list: Flush the list of segments + * @seg_list: List of segments to be flushed + * This API will traverse through the list of segments provided in + * seg_list, release the firmware for each segment and delete the + * segment from the list. + */ +void wdsp_flush_segment_list(struct list_head *seg_list) +{ + struct wdsp_img_segment *seg, *next; + + list_for_each_entry_safe(seg, next, seg_list, list) { + release_firmware(seg->split_fw); + list_del(&seg->list); + kfree(seg); + } +} +EXPORT_SYMBOL(wdsp_flush_segment_list); + +/* + * wdsp_get_segment_list: Get the list of requested segments + * @dev: struct device pointer of caller + * @img_fname: Image name for the mdt and split firmware files + * @segment_type: Requested segment type, should be either + * WDSP_ELF_FLAG_RE or WDSP_ELF_FLAG_WRITE + * @seg_list: An initialized head for list of segmented to be returned + * @entry_point: Pointer to return the entry point of the image + * This API will parse the mdt file for img_fname and create + * an struct wdsp_img_segment for each segment that matches segment_type + * and add this structure to list pointed by seg_list + */ +int wdsp_get_segment_list(struct device *dev, + const char *img_fname, + unsigned int segment_type, + struct list_head *seg_list, + u32 *entry_point) +{ + const struct firmware *fw; + const struct elf32_hdr *ehdr; + const struct elf32_phdr *phdr; + const u8 *elf_ptr; + char mdt_name[WDSP_IMG_NAME_LEN_MAX]; + int ret, phdr_idx; + bool segment_match; + + if (!dev) { + ret = -EINVAL; + pr_err("%s: Invalid device handle\n", __func__); + goto done; + } + + if (!img_fname || !seg_list || !entry_point) { + ret = -EINVAL; + dev_err(dev, "%s: Invalid input params\n", + __func__); + goto done; + } + + if (segment_type != WDSP_ELF_FLAG_RE && + segment_type != WDSP_ELF_FLAG_WRITE) { + dev_err(dev, "%s: Invalid request for segment_type %d\n", + __func__, segment_type); + ret = -EINVAL; + goto done; + } + + snprintf(mdt_name, sizeof(mdt_name), "%s.mdt", img_fname); + ret = request_firmware(&fw, mdt_name, dev); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "%s: firmware %s not found\n", + __func__, mdt_name); + goto done; + } + + ehdr = (struct elf32_hdr *) fw->data; + *entry_point = ehdr->e_entry; + if (!wdsp_is_valid_elf_hdr(ehdr, fw->size)) { + dev_err(dev, "%s: fw mdt %s is invalid\n", + __func__, mdt_name); + ret = -EINVAL; + goto bad_elf; + } + + elf_ptr = fw->data + sizeof(*ehdr); + for (phdr_idx = 0; phdr_idx < ehdr->e_phnum; phdr_idx++) { + phdr = (struct elf32_phdr *) elf_ptr; + segment_match = false; + + switch (segment_type) { + case WDSP_ELF_FLAG_RE: + /* + * Flag can be READ or EXECUTE or both but + * WRITE flag should not be set. + */ + if ((phdr->p_flags & segment_type) && + !(phdr->p_flags & WDSP_ELF_FLAG_WRITE)) + segment_match = true; + break; + case WDSP_ELF_FLAG_WRITE: + /* + * If WRITE flag is set, other flags do not + * matter. + */ + if (phdr->p_flags & segment_type) + segment_match = true; + break; + } + + if (segment_match) { + ret = wdsp_add_segment_to_list(dev, img_fname, phdr, + phdr_idx, seg_list); + if (IS_ERR_VALUE(ret)) { + wdsp_flush_segment_list(seg_list); + goto bad_elf; + } + } + elf_ptr = elf_ptr + sizeof(*phdr); + } + +bad_elf: + release_firmware(fw); +done: + return ret; +} +EXPORT_SYMBOL(wdsp_get_segment_list); diff --git a/sound/soc/codecs/wcd-dsp-utils.h b/sound/soc/codecs/wcd-dsp-utils.h new file mode 100644 index 000000000000..81842f77260e --- /dev/null +++ b/sound/soc/codecs/wcd-dsp-utils.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __WCD_DSP_UTILS_H__ +#define __WCD_DSP_UTILS_H__ + +#define WDSP_IMG_NAME_LEN_MAX 64 + +#define WDSP_ELF_FLAG_EXECUTE (1 << 0) +#define WDSP_ELF_FLAG_WRITE (1 << 1) +#define WDSP_ELF_FLAG_READ (1 << 2) + +#define WDSP_ELF_FLAG_RE (WDSP_ELF_FLAG_READ | WDSP_ELF_FLAG_EXECUTE) + +struct wdsp_img_segment { + + /* Firmware for the slit image */ + const struct firmware *split_fw; + + /* Name of the split firmware file */ + char split_fname[WDSP_IMG_NAME_LEN_MAX]; + + /* Address where the segment is to be loaded */ + u32 load_addr; + + /* Buffer to hold the data to be loaded */ + u8 *data; + + /* Size of the data to be loaded */ + size_t size; + + /* List node pointing to next segment */ + struct list_head list; +}; + +int wdsp_get_segment_list(struct device *, const char *, + unsigned int, struct list_head *, + u32 *); +void wdsp_flush_segment_list(struct list_head *); + +#endif /* __WCD_DSP_UTILS_H__ */ diff --git a/sound/soc/codecs/wcd-spi-registers.h b/sound/soc/codecs/wcd-spi-registers.h new file mode 100644 index 000000000000..4e579696cc49 --- /dev/null +++ b/sound/soc/codecs/wcd-spi-registers.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __WCD_SPI_REGISTERS_H__ +#define __WCD_SPI_REGISTERS_H__ + +#include <linux/regmap.h> + +#define WCD_SPI_SLAVE_SANITY (0x00) +#define WCD_SPI_SLAVE_DEVICE_ID (0x04) +#define WCD_SPI_SLAVE_STATUS (0x08) +#define WCD_SPI_SLAVE_CONFIG (0x0c) +#define WCD_SPI_SLAVE_SW_RESET (0x10) +#define WCD_SPI_SLAVE_IRQ_STATUS (0x14) +#define WCD_SPI_SLAVE_IRQ_EN (0x18) +#define WCD_SPI_SLAVE_IRQ_CLR (0x1c) +#define WCD_SPI_SLAVE_IRQ_FORCE (0x20) +#define WCD_SPI_SLAVE_TX (0x24) +#define WCD_SPI_SLAVE_TEST_BUS_DATA (0x2c) +#define WCD_SPI_SLAVE_TEST_BUS_CTRL (0x30) +#define WCD_SPI_SLAVE_SW_RST_IRQ (0x34) +#define WCD_SPI_SLAVE_CHAR_CFG (0x38) +#define WCD_SPI_SLAVE_CHAR_DATA_MOSI (0x3c) +#define WCD_SPI_SLAVE_CHAR_DATA_CS_N (0x40) +#define WCD_SPI_SLAVE_CHAR_DATA_MISO (0x44) +#define WCD_SPI_SLAVE_TRNS_BYTE_CNT (0x4c) +#define WCD_SPI_SLAVE_TRNS_LEN (0x50) +#define WCD_SPI_SLAVE_FIFO_LEVEL (0x54) +#define WCD_SPI_SLAVE_GENERICS (0x58) +#define WCD_SPI_SLAVE_EXT_BASE_ADDR (0x5c) +#define WCD_SPI_MAX_REGISTER (0x5F) + +#endif /* End __WCD_SPI_REGISTERS_H__ */ diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c new file mode 100644 index 000000000000..3049d87c6c05 --- /dev/null +++ b/sound/soc/codecs/wcd-spi.c @@ -0,0 +1,1250 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/bitops.h> +#include <linux/spi/spi.h> +#include <linux/regmap.h> +#include <linux/component.h> +#include <linux/ratelimit.h> +#include <sound/wcd-dsp-mgr.h> +#include <sound/wcd-spi.h> +#include "wcd-spi-registers.h" + +/* Byte manipulations */ +#define SHIFT_1_BYTES (8) +#define SHIFT_2_BYTES (16) +#define SHIFT_3_BYTES (24) + +/* Command opcodes */ +#define WCD_SPI_CMD_NOP (0x00) +#define WCD_SPI_CMD_WREN (0x06) +#define WCD_SPI_CMD_CLKREQ (0xDA) +#define WCD_SPI_CMD_RDSR (0x05) +#define WCD_SPI_CMD_IRR (0x81) +#define WCD_SPI_CMD_IRW (0x82) +#define WCD_SPI_CMD_MIOR (0x83) +#define WCD_SPI_CMD_FREAD (0x0B) +#define WCD_SPI_CMD_MIOW (0x02) +#define WCD_SPI_WRITE_FRAME_OPCODE \ + (WCD_SPI_CMD_MIOW << SHIFT_3_BYTES) +#define WCD_SPI_READ_FRAME_OPCODE \ + (WCD_SPI_CMD_MIOR << SHIFT_3_BYTES) +#define WCD_SPI_FREAD_FRAME_OPCODE \ + (WCD_SPI_CMD_FREAD << SHIFT_3_BYTES) + +/* Command lengths */ +#define WCD_SPI_OPCODE_LEN (0x01) +#define WCD_SPI_CMD_NOP_LEN (0x01) +#define WCD_SPI_CMD_WREN_LEN (0x01) +#define WCD_SPI_CMD_CLKREQ_LEN (0x04) +#define WCD_SPI_CMD_IRR_LEN (0x04) +#define WCD_SPI_CMD_IRW_LEN (0x06) +#define WCD_SPI_WRITE_SINGLE_LEN (0x08) +#define WCD_SPI_READ_SINGLE_LEN (0x13) +#define WCD_SPI_CMD_FREAD_LEN (0x13) + +/* Command delays */ +#define WCD_SPI_CLKREQ_DELAY_USECS (500) +#define WCD_SPI_CLK_OFF_TIMER_MS (3000) + +/* Command masks */ +#define WCD_CMD_ADDR_MASK \ + (0xFF | \ + (0xFF << SHIFT_1_BYTES) | \ + (0xFF << SHIFT_2_BYTES)) + +/* Clock ctrl request related */ +#define WCD_SPI_CLK_ENABLE true +#define WCD_SPI_CLK_DISABLE false +#define WCD_SPI_CLK_FLAG_DELAYED (1 << 0) +#define WCD_SPI_CLK_FLAG_IMMEDIATE (1 << 1) + +/* Internal addresses */ +#define WCD_SPI_ADDR_IPC_CTL_HOST (0x012014) + +/* Word sizes and min/max lengths */ +#define WCD_SPI_WORD_BYTE_CNT (4) +#define WCD_SPI_RW_MULTI_MIN_LEN (16) +#define WCD_SPI_RW_MULTI_MAX_LEN (64 * 1024) + +/* Alignment requirements */ +#define WCD_SPI_RW_MIN_ALIGN WCD_SPI_WORD_BYTE_CNT +#define WCD_SPI_RW_MULTI_ALIGN (16) + +/* Status mask bits */ +#define WCD_SPI_CLK_STATE_ENABLED BIT(0) + +/* Locking related */ +#define WCD_SPI_MUTEX_LOCK(spi, lock) \ +{ \ + dev_vdbg(&spi->dev, "%s: mutex_lock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_lock(&lock); \ +} + +#define WCD_SPI_MUTEX_UNLOCK(spi, lock) \ +{ \ + dev_vdbg(&spi->dev, "%s: mutex_unlock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_unlock(&lock); \ +} + +struct wcd_spi_priv { + struct spi_device *spi; + u32 mem_base_addr; + + struct regmap *regmap; + + /* Message for single transfer */ + struct spi_message msg1; + struct spi_transfer xfer1; + + /* Message for two transfers */ + struct spi_message msg2; + struct spi_transfer xfer2[2]; + + /* Register access related */ + u32 reg_bytes; + u32 val_bytes; + + /* Clock requests related */ + struct mutex clk_mutex; + int clk_users; + unsigned long status_mask; + struct delayed_work clk_dwork; + + /* Transaction related */ + struct mutex xfer_mutex; + + struct device *m_dev; + struct wdsp_mgr_ops *m_ops; +}; + +enum xfer_request { + WCD_SPI_XFER_WRITE, + WCD_SPI_XFER_READ, +}; + + +static char *wcd_spi_xfer_req_str(enum xfer_request req) +{ + if (req == WCD_SPI_XFER_WRITE) + return "xfer_write"; + else if (req == WCD_SPI_XFER_READ) + return "xfer_read"; + else + return "xfer_invalid"; +} + +static void wcd_spi_reinit_xfer(struct spi_transfer *xfer) +{ + xfer->tx_buf = NULL; + xfer->rx_buf = NULL; + xfer->delay_usecs = 0; + xfer->len = 0; +} + +static int wcd_spi_read_single(struct spi_device *spi, + u32 remote_addr, u32 *val) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0]; + struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1]; + u8 *tx_buf; + u32 frame = 0; + int ret; + + dev_dbg(&spi->dev, "%s: remote_addr = 0x%x\n", + __func__, remote_addr); + + tx_buf = kzalloc(WCD_SPI_READ_SINGLE_LEN, + GFP_KERNEL | GFP_DMA); + if (!tx_buf) + return -ENOMEM; + + frame |= WCD_SPI_READ_FRAME_OPCODE; + frame |= remote_addr & WCD_CMD_ADDR_MASK; + + wcd_spi_reinit_xfer(tx_xfer); + frame = cpu_to_be32(frame); + memcpy(tx_buf, &frame, sizeof(frame)); + tx_xfer->tx_buf = tx_buf; + tx_xfer->len = WCD_SPI_READ_SINGLE_LEN; + + wcd_spi_reinit_xfer(rx_xfer); + rx_xfer->rx_buf = val; + rx_xfer->len = sizeof(*val); + + ret = spi_sync(spi, &wcd_spi->msg2); + kfree(tx_buf); + + return ret; +} + +static int wcd_spi_read_multi(struct spi_device *spi, + u32 remote_addr, u8 *data, + size_t len) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u8 *tx_buf; + u8 *rx_buf; + u32 frame = 0; + int ret; + + dev_dbg(&spi->dev, "%s: addr 0x%x, len = %zd\n", + __func__, remote_addr, len); + + frame |= WCD_SPI_FREAD_FRAME_OPCODE; + frame |= remote_addr & WCD_CMD_ADDR_MASK; + + tx_buf = kzalloc(WCD_SPI_CMD_FREAD_LEN + len, + GFP_KERNEL | GFP_DMA); + if (!tx_buf) + return -ENOMEM; + + rx_buf = kzalloc(WCD_SPI_CMD_FREAD_LEN + len, + GFP_KERNEL | GFP_DMA); + if (!rx_buf) { + kfree(tx_buf); + return -ENOMEM; + } + + wcd_spi_reinit_xfer(xfer); + frame = cpu_to_be32(frame); + memcpy(tx_buf, &frame, sizeof(frame)); + xfer->tx_buf = tx_buf; + xfer->rx_buf = rx_buf; + xfer->len = WCD_SPI_CMD_FREAD_LEN + len; + + ret = spi_sync(spi, &wcd_spi->msg1); + if (ret) { + dev_err(&spi->dev, "%s: failed, err = %d\n", + __func__, ret); + goto done; + } + + memcpy(data, rx_buf + WCD_SPI_CMD_FREAD_LEN, len); +done: + kfree(tx_buf); + kfree(rx_buf); + return ret; +} + +static int wcd_spi_write_single(struct spi_device *spi, + u32 remote_addr, u32 val) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u8 buf[WCD_SPI_WRITE_SINGLE_LEN]; + u32 frame = 0; + + dev_dbg(&spi->dev, "%s: remote_addr = 0x%x, val = 0x%x\n", + __func__, remote_addr, val); + + memset(buf, 0, WCD_SPI_WRITE_SINGLE_LEN); + frame |= WCD_SPI_WRITE_FRAME_OPCODE; + frame |= (remote_addr & WCD_CMD_ADDR_MASK); + + frame = cpu_to_be32(frame); + memcpy(buf, &frame, sizeof(frame)); + memcpy(buf + sizeof(frame), &val, sizeof(val)); + + wcd_spi_reinit_xfer(xfer); + xfer->tx_buf = buf; + xfer->len = WCD_SPI_WRITE_SINGLE_LEN; + + return spi_sync(spi, &wcd_spi->msg1); +} + +static int wcd_spi_write_multi(struct spi_device *spi, + u32 remote_addr, u8 *data, + size_t len) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u32 frame = 0; + u8 *tx_buf; + int xfer_len, ret; + + dev_dbg(&spi->dev, "%s: addr = 0x%x len = %zd\n", + __func__, remote_addr, len); + + frame |= WCD_SPI_WRITE_FRAME_OPCODE; + frame |= (remote_addr & WCD_CMD_ADDR_MASK); + + frame = cpu_to_be32(frame); + xfer_len = len + sizeof(frame); + + tx_buf = kzalloc(xfer_len, GFP_KERNEL); + if (!tx_buf) + return -ENOMEM; + + memcpy(tx_buf, &frame, sizeof(frame)); + memcpy(tx_buf + sizeof(frame), data, len); + + wcd_spi_reinit_xfer(xfer); + xfer->tx_buf = tx_buf; + xfer->len = xfer_len; + + ret = spi_sync(spi, &wcd_spi->msg1); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, + "%s: Failed, addr = 0x%x, len = %zd\n", + __func__, remote_addr, len); + kfree(tx_buf); + + return ret; +} + +static int wcd_spi_transfer_split(struct spi_device *spi, + struct wcd_spi_msg *data_msg, + enum xfer_request xfer_req) +{ + u32 addr = data_msg->remote_addr; + u8 *data = data_msg->data; + int remain_size = data_msg->len; + int to_xfer, loop_cnt, ret; + + /* Perform single writes until multi word alignment is met */ + loop_cnt = 1; + while (remain_size && + !IS_ALIGNED(addr, WCD_SPI_RW_MULTI_ALIGN)) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_single(spi, addr, + (*(u32 *)data)); + else + ret = wcd_spi_read_single(spi, addr, + (u32 *)data); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, + "%s: %s fail iter(%d) start-word addr (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + loop_cnt, addr); + goto done; + } + + addr += WCD_SPI_WORD_BYTE_CNT; + data += WCD_SPI_WORD_BYTE_CNT; + remain_size -= WCD_SPI_WORD_BYTE_CNT; + loop_cnt++; + } + + /* Perform multi writes for max allowed multi writes */ + loop_cnt = 1; + while (remain_size >= WCD_SPI_RW_MULTI_MAX_LEN) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_multi(spi, addr, data, + WCD_SPI_RW_MULTI_MAX_LEN); + else + ret = wcd_spi_read_multi(spi, addr, data, + WCD_SPI_RW_MULTI_MAX_LEN); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, + "%s: %s fail iter(%d) max-write addr (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + loop_cnt, addr); + goto done; + } + + addr += WCD_SPI_RW_MULTI_MAX_LEN; + data += WCD_SPI_RW_MULTI_MAX_LEN; + remain_size -= WCD_SPI_RW_MULTI_MAX_LEN; + loop_cnt++; + } + + /* + * Perform write for max possible data that is multiple + * of the minimum size for multi-write commands. + */ + to_xfer = remain_size - (remain_size % WCD_SPI_RW_MULTI_MIN_LEN); + if (remain_size >= WCD_SPI_RW_MULTI_MIN_LEN && + to_xfer > 0) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_multi(spi, addr, data, to_xfer); + else + ret = wcd_spi_read_multi(spi, addr, data, to_xfer); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, + "%s: %s fail write addr (0x%x), size (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + addr, to_xfer); + goto done; + } + + addr += to_xfer; + data += to_xfer; + remain_size -= to_xfer; + } + + /* Perform single writes for the last remaining data */ + loop_cnt = 1; + while (remain_size > 0) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_single(spi, addr, (*((u32 *)data))); + else + ret = wcd_spi_read_single(spi, addr, (u32 *) data); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, + "%s: %s fail iter(%d) end-write addr (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + loop_cnt, addr); + goto done; + } + + addr += WCD_SPI_WORD_BYTE_CNT; + data += WCD_SPI_WORD_BYTE_CNT; + remain_size -= WCD_SPI_WORD_BYTE_CNT; + loop_cnt++; + } + +done: + return ret; +} + +static int wcd_spi_cmd_nop(struct spi_device *spi) +{ + u8 nop = WCD_SPI_CMD_NOP; + + return spi_write(spi, &nop, WCD_SPI_CMD_NOP_LEN); +} + +static int wcd_spi_cmd_clkreq(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u8 cmd[WCD_SPI_CMD_CLKREQ_LEN] = { + WCD_SPI_CMD_CLKREQ, + 0xBA, 0x80, 0x00}; + + wcd_spi_reinit_xfer(xfer); + xfer->tx_buf = cmd; + xfer->len = WCD_SPI_CMD_CLKREQ_LEN; + xfer->delay_usecs = WCD_SPI_CLKREQ_DELAY_USECS; + + return spi_sync(spi, &wcd_spi->msg1); +} + +static int wcd_spi_cmd_wr_en(struct spi_device *spi) +{ + u8 wr_en = WCD_SPI_CMD_WREN; + + return spi_write(spi, &wr_en, WCD_SPI_CMD_WREN_LEN); +} + +static int wcd_spi_cmd_rdsr(struct spi_device *spi, + u32 *rdsr_status) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0]; + struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1]; + u8 rdsr_cmd; + u32 status; + int ret; + + rdsr_cmd = WCD_SPI_CMD_RDSR; + wcd_spi_reinit_xfer(tx_xfer); + tx_xfer->tx_buf = &rdsr_cmd; + tx_xfer->len = sizeof(rdsr_cmd); + + + wcd_spi_reinit_xfer(rx_xfer); + rx_xfer->rx_buf = &status; + rx_xfer->len = sizeof(status); + + ret = spi_sync(spi, &wcd_spi->msg2); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: RDSR failed, err = %d\n", + __func__, ret); + goto done; + } + + *rdsr_status = be32_to_cpu(status); + + dev_dbg(&spi->dev, "%s: RDSR success, value = 0x%x\n", + __func__, *rdsr_status); +done: + return ret; +} + +static int wcd_spi_clk_enable(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + u32 rd_status; + + ret = wcd_spi_cmd_nop(spi); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: NOP1 failed, err = %d\n", + __func__, ret); + goto done; + } + + ret = wcd_spi_cmd_clkreq(spi); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: CLK_REQ failed, err = %d\n", + __func__, ret); + goto done; + } + + ret = wcd_spi_cmd_nop(spi); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: NOP2 failed, err = %d\n", + __func__, ret); + goto done; + } + wcd_spi_cmd_rdsr(spi, &rd_status); + /* + * Read status zero means reads are not + * happenning on the bus, possibly because + * clock request failed. + */ + if (rd_status) { + set_bit(WCD_SPI_CLK_STATE_ENABLED, + &wcd_spi->status_mask); + } else { + dev_err(&spi->dev, "%s: RDSR status is zero\n", + __func__); + ret = -EIO; + } +done: + return ret; +} + +static int wcd_spi_clk_disable(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + + ret = wcd_spi_write_single(spi, WCD_SPI_ADDR_IPC_CTL_HOST, 0x01); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, "%s: Failed, err = %d\n", + __func__, ret); + else + clear_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask); + + return ret; +} + +static int wcd_spi_clk_ctrl(struct spi_device *spi, + bool request, u32 flags) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret = 0; + const char *delay_str; + + delay_str = (flags == WCD_SPI_CLK_FLAG_DELAYED) ? + "delayed" : "immediate"; + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + + /* Reject any unbalanced disable request */ + if (wcd_spi->clk_users < 0 || + (!request && wcd_spi->clk_users == 0)) { + dev_err(&spi->dev, "%s: Unbalanced clk_users %d for %s\n", + __func__, wcd_spi->clk_users, + request ? "enable" : "disable"); + ret = -EINVAL; + + /* Reset the clk_users to 0 */ + wcd_spi->clk_users = 0; + + goto done; + } + + if (request == WCD_SPI_CLK_ENABLE) { + /* Cancel the disable clk work */ + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + cancel_delayed_work_sync(&wcd_spi->clk_dwork); + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + + wcd_spi->clk_users++; + + /* + * If clk state is already set, + * then clk wasnt really disabled + */ + if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask)) + goto done; + else if (wcd_spi->clk_users == 1) + ret = wcd_spi_clk_enable(spi); + + } else { + wcd_spi->clk_users--; + + /* Clock is still voted for */ + if (wcd_spi->clk_users > 0) + goto done; + + /* + * If we are here, clk_users must be 0 and needs + * to be disabled. Call the disable based on the + * flags. + */ + if (flags == WCD_SPI_CLK_FLAG_DELAYED) { + schedule_delayed_work(&wcd_spi->clk_dwork, + msecs_to_jiffies(WCD_SPI_CLK_OFF_TIMER_MS)); + } else { + ret = wcd_spi_clk_disable(spi); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, + "%s: Failed to disable clk err = %d\n", + __func__, ret); + } + } + +done: + dev_dbg(&spi->dev, "%s: updated clk_users = %d, request_%s %s\n", + __func__, wcd_spi->clk_users, request ? "enable" : "disable", + request ? "" : delay_str); + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + + return ret; +} + +static int wcd_spi_init(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (IS_ERR_VALUE(ret)) + goto done; + + ret = wcd_spi_cmd_wr_en(spi); + if (IS_ERR_VALUE(ret)) + goto err_wr_en; + + regmap_write(wcd_spi->regmap, WCD_SPI_SLAVE_CONFIG, + 0x0F3D0800); + + /* Write the MTU to 64K */ + regmap_update_bits(wcd_spi->regmap, + WCD_SPI_SLAVE_TRNS_LEN, + 0xFFFF0000, + (WCD_SPI_RW_MULTI_MAX_LEN / 4) << 16); +done: + return ret; + +err_wr_en: + wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + return ret; +} + +static void wcd_spi_clk_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wcd_spi_priv *wcd_spi; + struct spi_device *spi; + int ret; + + dwork = to_delayed_work(work); + wcd_spi = container_of(dwork, struct wcd_spi_priv, clk_dwork); + spi = wcd_spi->spi; + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + ret = wcd_spi_clk_disable(spi); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, + "%s: Failed to disable clk, err = %d\n", + __func__, ret); + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); +} + +static int __wcd_spi_data_xfer(struct spi_device *spi, + struct wcd_spi_msg *msg, + enum xfer_request xfer_req) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + + /* Check for minimum alignment requirements */ + if (!IS_ALIGNED(msg->remote_addr, WCD_SPI_RW_MIN_ALIGN)) { + dev_err(&spi->dev, + "%s addr 0x%x is not aligned to 0x%x\n", + __func__, msg->remote_addr, WCD_SPI_RW_MIN_ALIGN); + return -EINVAL; + } else if (msg->len % WCD_SPI_WORD_BYTE_CNT) { + dev_err(&spi->dev, + "%s len 0x%zx is not multiple of %d\n", + __func__, msg->len, WCD_SPI_WORD_BYTE_CNT); + return -EINVAL; + } + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->xfer_mutex); + if (msg->len == WCD_SPI_WORD_BYTE_CNT) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_single(spi, msg->remote_addr, + (*((u32 *)msg->data))); + else + ret = wcd_spi_read_single(spi, msg->remote_addr, + (u32 *) msg->data); + } else { + ret = wcd_spi_transfer_split(spi, msg, xfer_req); + } + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->xfer_mutex); + + return ret; +} + +static int wcd_spi_data_xfer(struct spi_device *spi, + struct wcd_spi_msg *msg, + enum xfer_request req) +{ + int ret, ret1; + + if (msg->len <= 0) { + dev_err(&spi->dev, "%s: Invalid size %zd\n", + __func__, msg->len); + return -EINVAL; + } + + /* Request for clock */ + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: clk enable failed %d\n", + __func__, ret); + goto done; + } + + /* Perform the transaction */ + ret = __wcd_spi_data_xfer(spi, msg, req); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, + "%s: Failed %s, addr = 0x%x, size = 0x%zx, err = %d\n", + __func__, wcd_spi_xfer_req_str(req), + msg->remote_addr, msg->len, ret); + + /* Release the clock even if xfer failed */ + ret1 = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, + WCD_SPI_CLK_FLAG_DELAYED); + if (IS_ERR_VALUE(ret1)) + dev_err(&spi->dev, "%s: clk disable failed %d\n", + __func__, ret1); +done: + return ret; +} + +/* + * wcd_spi_data_write: Write data to WCD SPI + * @spi: spi_device struct + * @msg: msg that needs to be written to WCD + * + * This API writes length of data to address specified. These details + * about the write are encapsulated in @msg. Write size should be multiple + * of 4 bytes and write address should be 4-byte aligned. + */ +int wcd_spi_data_write(struct spi_device *spi, + struct wcd_spi_msg *msg) +{ + if (!spi || !msg) { + pr_err("%s: Invalid %s\n", __func__, + (!spi) ? "spi device" : "msg"); + return -EINVAL; + } + + dev_dbg_ratelimited(&spi->dev, "%s: addr = 0x%x, len = %zu\n", + __func__, msg->remote_addr, msg->len); + return wcd_spi_data_xfer(spi, msg, WCD_SPI_XFER_WRITE); +} +EXPORT_SYMBOL(wcd_spi_data_write); + +/* + * wcd_spi_data_read: Read data from WCD SPI + * @spi: spi_device struct + * @msg: msg that needs to be read from WCD + * + * This API reads length of data from address specified. These details + * about the read are encapsulated in @msg. Read size should be multiple + * of 4 bytes and read address should be 4-byte aligned. + */ +int wcd_spi_data_read(struct spi_device *spi, + struct wcd_spi_msg *msg) +{ + if (!spi || !msg) { + pr_err("%s: Invalid %s\n", __func__, + (!spi) ? "spi device" : "msg"); + return -EINVAL; + } + + dev_dbg_ratelimited(&spi->dev, "%s: addr = 0x%x,len = %zu\n", + __func__, msg->remote_addr, msg->len); + return wcd_spi_data_xfer(spi, msg, WCD_SPI_XFER_READ); +} +EXPORT_SYMBOL(wcd_spi_data_read); + +static int wdsp_spi_dload_section(struct spi_device *spi, + void *data) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wdsp_img_section *sec = data; + struct wcd_spi_msg msg; + int ret; + + dev_dbg(&spi->dev, "%s: addr = 0x%x, size = 0x%zx\n", + __func__, sec->addr, sec->size); + + msg.remote_addr = sec->addr + wcd_spi->mem_base_addr; + msg.data = sec->data; + msg.len = sec->size; + + ret = __wcd_spi_data_xfer(spi, &msg, WCD_SPI_XFER_WRITE); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, "%s: fail addr (0x%x) size (0x%zx)\n", + __func__, msg.remote_addr, msg.len); + return ret; +} + +static int wdsp_spi_event_handler(struct device *dev, void *priv_data, + enum wdsp_event_type event, + void *data) +{ + struct spi_device *spi = to_spi_device(dev); + int ret; + + dev_dbg(&spi->dev, "%s: event type %d\n", + __func__, event); + + switch (event) { + case WDSP_EVENT_PRE_DLOAD_DATA: + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, "%s: clk_req failed %d\n", + __func__, ret); + break; + + case WDSP_EVENT_POST_DLOAD_CODE: + case WDSP_EVENT_POST_DLOAD_DATA: + case WDSP_EVENT_DLOAD_FAILED: + + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, "%s: clk unvote failed %d\n", + __func__, ret); + break; + + case WDSP_EVENT_DLOAD_SECTION: + ret = wdsp_spi_dload_section(spi, data); + break; + default: + dev_dbg(&spi->dev, "%s: Unhandled event %d\n", + __func__, event); + break; + } + + return ret; +} + +static int wcd_spi_bus_gwrite(void *context, const void *reg, + size_t reg_len, const void *val, + size_t val_len) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + u8 tx_buf[WCD_SPI_CMD_IRW_LEN]; + + if (!reg || !val || reg_len != wcd_spi->reg_bytes || + val_len != wcd_spi->val_bytes) { + dev_err(&spi->dev, + "%s: Invalid input, reg_len = %zd, val_len = %zd", + __func__, reg_len, val_len); + return -EINVAL; + } + + tx_buf[0] = WCD_SPI_CMD_IRW; + tx_buf[1] = *((u8 *)reg); + memcpy(&tx_buf[WCD_SPI_OPCODE_LEN + reg_len], + val, val_len); + + return spi_write(spi, tx_buf, WCD_SPI_CMD_IRW_LEN); +} + +static int wcd_spi_bus_write(void *context, const void *data, + size_t count) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + if (count < (wcd_spi->reg_bytes + wcd_spi->val_bytes)) { + dev_err(&spi->dev, "%s: Invalid size %zd\n", + __func__, count); + WARN_ON(1); + return -EINVAL; + } + + return wcd_spi_bus_gwrite(context, data, wcd_spi->reg_bytes, + data + wcd_spi->reg_bytes, + count - wcd_spi->reg_bytes); +} + +static int wcd_spi_bus_read(void *context, const void *reg, + size_t reg_len, void *val, + size_t val_len) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0]; + struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1]; + u8 tx_buf[WCD_SPI_CMD_IRR_LEN]; + + if (!reg || !val || reg_len != wcd_spi->reg_bytes || + val_len != wcd_spi->val_bytes) { + dev_err(&spi->dev, + "%s: Invalid input, reg_len = %zd, val_len = %zd", + __func__, reg_len, val_len); + return -EINVAL; + } + + memset(tx_buf, 0, WCD_SPI_OPCODE_LEN); + tx_buf[0] = WCD_SPI_CMD_IRR; + tx_buf[1] = *((u8 *)reg); + + wcd_spi_reinit_xfer(tx_xfer); + tx_xfer->tx_buf = tx_buf; + tx_xfer->rx_buf = NULL; + tx_xfer->len = WCD_SPI_CMD_IRR_LEN; + + wcd_spi_reinit_xfer(rx_xfer); + rx_xfer->tx_buf = NULL; + rx_xfer->rx_buf = val; + rx_xfer->len = val_len; + + return spi_sync(spi, &wcd_spi->msg2); +} + +static struct regmap_bus wcd_spi_regmap_bus = { + .write = wcd_spi_bus_write, + .gather_write = wcd_spi_bus_gwrite, + .read = wcd_spi_bus_read, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static int wcd_spi_state_show(struct seq_file *f, void *ptr) +{ + struct spi_device *spi = f->private; + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + const char *clk_state, *clk_mutex, *xfer_mutex; + + if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask)) + clk_state = "enabled"; + else + clk_state = "disabled"; + + clk_mutex = mutex_is_locked(&wcd_spi->clk_mutex) ? + "locked" : "unlocked"; + + xfer_mutex = mutex_is_locked(&wcd_spi->xfer_mutex) ? + "locked" : "unlocked"; + + seq_printf(f, "clk_state = %s\nclk_users = %d\n" + "clk_mutex = %s\nxfer_mutex = %s\n", + clk_state, wcd_spi->clk_users, clk_mutex, + xfer_mutex); + return 0; +} + +static int wcd_spi_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, wcd_spi_state_show, inode->i_private); +} + +static const struct file_operations state_fops = { + .open = wcd_spi_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int wcd_spi_debugfs_init(struct spi_device *spi) +{ + int rc = 0; + struct dentry *dir; + + dir = debugfs_create_dir("wcd_spi", NULL); + if (IS_ERR_OR_NULL(dir)) { + dir = NULL; + rc = -ENODEV; + goto done; + } + + debugfs_create_file("state", 0444, dir, spi, &state_fops); + +done: + return rc; +} + + +static const struct reg_default wcd_spi_defaults[] = { + {WCD_SPI_SLAVE_SANITY, 0xDEADBEEF}, + {WCD_SPI_SLAVE_DEVICE_ID, 0x00500000}, + {WCD_SPI_SLAVE_STATUS, 0x80100000}, + {WCD_SPI_SLAVE_CONFIG, 0x0F200808}, + {WCD_SPI_SLAVE_SW_RESET, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_STATUS, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_EN, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_CLR, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_FORCE, 0x00000000}, + {WCD_SPI_SLAVE_TX, 0x00000000}, + {WCD_SPI_SLAVE_TEST_BUS_DATA, 0x00000000}, + {WCD_SPI_SLAVE_TEST_BUS_CTRL, 0x00000000}, + {WCD_SPI_SLAVE_SW_RST_IRQ, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_CFG, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_DATA_MOSI, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_DATA_CS_N, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_DATA_MISO, 0x00000000}, + {WCD_SPI_SLAVE_TRNS_BYTE_CNT, 0x00000000}, + {WCD_SPI_SLAVE_TRNS_LEN, 0x00000000}, + {WCD_SPI_SLAVE_FIFO_LEVEL, 0x00000000}, + {WCD_SPI_SLAVE_GENERICS, 0x80000000}, + {WCD_SPI_SLAVE_EXT_BASE_ADDR, 0x00000000}, +}; + +static bool wcd_spi_is_volatile_reg(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case WCD_SPI_SLAVE_SANITY: + case WCD_SPI_SLAVE_STATUS: + case WCD_SPI_SLAVE_IRQ_STATUS: + case WCD_SPI_SLAVE_TX: + case WCD_SPI_SLAVE_SW_RST_IRQ: + case WCD_SPI_SLAVE_TRNS_BYTE_CNT: + case WCD_SPI_SLAVE_FIFO_LEVEL: + case WCD_SPI_SLAVE_GENERICS: + return true; + } + + return false; +} + +static bool wcd_spi_is_readable_reg(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case WCD_SPI_SLAVE_SW_RESET: + case WCD_SPI_SLAVE_IRQ_CLR: + case WCD_SPI_SLAVE_IRQ_FORCE: + return false; + } + + return true; +} + +static struct regmap_config wcd_spi_regmap_cfg = { + .reg_bits = 8, + .val_bits = 32, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wcd_spi_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd_spi_defaults), + .max_register = WCD_SPI_MAX_REGISTER, + .volatile_reg = wcd_spi_is_volatile_reg, + .readable_reg = wcd_spi_is_readable_reg, +}; + +static int wdsp_spi_init(struct device *dev, void *priv_data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + + wcd_spi->reg_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.reg_bits, 8); + wcd_spi->val_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.val_bits, 8); + + wcd_spi->regmap = devm_regmap_init(&spi->dev, &wcd_spi_regmap_bus, + &spi->dev, &wcd_spi_regmap_cfg); + if (IS_ERR(wcd_spi->regmap)) { + ret = PTR_ERR(wcd_spi->regmap); + dev_err(&spi->dev, "%s: Failed to allocate regmap, err = %d\n", + __func__, ret); + goto err_regmap; + } + + if (wcd_spi_debugfs_init(spi)) + dev_err(&spi->dev, "%s: Failed debugfs init\n", __func__); + + spi_message_init(&wcd_spi->msg1); + spi_message_add_tail(&wcd_spi->xfer1, &wcd_spi->msg1); + + spi_message_init(&wcd_spi->msg2); + spi_message_add_tail(&wcd_spi->xfer2[0], &wcd_spi->msg2); + spi_message_add_tail(&wcd_spi->xfer2[1], &wcd_spi->msg2); + + ret = wcd_spi_init(spi); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: Init failed, err = %d\n", + __func__, ret); + goto err_init; + } + + return 0; + +err_init: + spi_transfer_del(&wcd_spi->xfer1); + spi_transfer_del(&wcd_spi->xfer2[0]); + spi_transfer_del(&wcd_spi->xfer2[1]); + +err_regmap: + return ret; +} + +static int wdsp_spi_deinit(struct device *dev, void *priv_data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + spi_transfer_del(&wcd_spi->xfer1); + spi_transfer_del(&wcd_spi->xfer2[0]); + spi_transfer_del(&wcd_spi->xfer2[1]); + + return 0; +} + +static struct wdsp_cmpnt_ops wdsp_spi_ops = { + .init = wdsp_spi_init, + .deinit = wdsp_spi_deinit, + .event_handler = wdsp_spi_event_handler, +}; + +static int wcd_spi_component_bind(struct device *dev, + struct device *master, + void *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret = 0; + + wcd_spi->m_dev = master; + wcd_spi->m_ops = data; + + if (wcd_spi->m_ops && + wcd_spi->m_ops->register_cmpnt_ops) + ret = wcd_spi->m_ops->register_cmpnt_ops(master, dev, + wcd_spi, + &wdsp_spi_ops); + if (ret) + dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n", + __func__, ret); + return ret; +} + +static void wcd_spi_component_unbind(struct device *dev, + struct device *master, + void *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + wcd_spi->m_dev = NULL; + wcd_spi->m_ops = NULL; +} + +static const struct component_ops wcd_spi_component_ops = { + .bind = wcd_spi_component_bind, + .unbind = wcd_spi_component_unbind, +}; + +static int wcd_spi_probe(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi; + int ret = 0; + + wcd_spi = devm_kzalloc(&spi->dev, sizeof(*wcd_spi), + GFP_KERNEL); + if (!wcd_spi) + return -ENOMEM; + + ret = of_property_read_u32(spi->dev.of_node, + "qcom,mem-base-addr", + &wcd_spi->mem_base_addr); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, "%s: Missing %s DT entry", + __func__, "qcom,mem-base-addr"); + goto err_ret; + } + + dev_dbg(&spi->dev, + "%s: mem_base_addr 0x%x\n", __func__, wcd_spi->mem_base_addr); + + mutex_init(&wcd_spi->clk_mutex); + mutex_init(&wcd_spi->xfer_mutex); + INIT_DELAYED_WORK(&wcd_spi->clk_dwork, wcd_spi_clk_work); + + wcd_spi->spi = spi; + spi_set_drvdata(spi, wcd_spi); + + ret = component_add(&spi->dev, &wcd_spi_component_ops); + if (ret) { + dev_err(&spi->dev, "%s: component_add failed err = %d\n", + __func__, ret); + goto err_component_add; + } + + return ret; + +err_component_add: + mutex_destroy(&wcd_spi->clk_mutex); + mutex_destroy(&wcd_spi->xfer_mutex); +err_ret: + devm_kfree(&spi->dev, wcd_spi); + spi_set_drvdata(spi, NULL); + return ret; +} + +static int wcd_spi_remove(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + component_del(&spi->dev, &wcd_spi_component_ops); + + mutex_destroy(&wcd_spi->clk_mutex); + mutex_destroy(&wcd_spi->xfer_mutex); + + devm_kfree(&spi->dev, wcd_spi); + spi_set_drvdata(spi, NULL); + + return 0; +} + +static const struct of_device_id wcd_spi_of_match[] = { + { .compatible = "qcom,wcd-spi-v2", }, + { } +}; +MODULE_DEVICE_TABLE(of, wcd_spi_of_match); + +static struct spi_driver wcd_spi_driver = { + .driver = { + .name = "wcd-spi-v2", + .of_match_table = wcd_spi_of_match, + }, + .probe = wcd_spi_probe, + .remove = wcd_spi_remove, +}; + +module_spi_driver(wcd_spi_driver); + +MODULE_DESCRIPTION("WCD SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 53049a2644be..f5a71b2a2d1a 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -369,6 +369,7 @@ enum { AIF_MIX1_PB, AIF4_MAD_TX, AIF4_VIFEED, + AIF5_CPE_TX, NUM_CODEC_DAIS, }; @@ -477,15 +478,18 @@ static const struct wcd9xxx_ch tasha_tx_chs[TASHA_TX_MAX] = { }; static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = { - 0, /* AIF1_PB */ - BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF1_CAP */ - 0, /* AIF2_PB */ - BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF2_CAP */ - 0, /* AIF3_PB */ - BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX), /* AIF3_CAP */ - 0, /* AIF4_PB */ - 0, /* AIF_MIX1_PB */ - BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP), /* AIF4_MAD_TX */ + /* Needs to define in the same order of DAI enum definitions */ + 0, + BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), }; static const u32 vport_i2s_check_table[NUM_CODEC_DAIS] = { @@ -2520,6 +2524,7 @@ static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol, } break; case AIF4_MAD_TX: + case AIF5_CPE_TX: break; default: pr_err("Unknown AIF %d\n", dai_id); @@ -6199,6 +6204,9 @@ static const struct snd_soc_dapm_route audio_map[] = { {"AIF4 MAD", NULL, "AIF4_MAD Mixer"}, {"AIF4 MAD", NULL, "AIF4"}, + {"EC BUF MUX INP", "DEC1", "ADC MUX1"}, + {"AIF5 CPE", NULL, "EC BUF MUX INP"}, + /* SLIMBUS Connections */ {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, @@ -9960,6 +9968,45 @@ static const struct snd_kcontrol_new anc0_fb_mux = static const struct snd_kcontrol_new anc1_fb_mux = SOC_DAPM_ENUM("ANC1 FB MUX Mux", anc1_fb_mux_enum); +static int tasha_codec_ec_buf_mux_enable(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event = %d name = %s\n", + __func__, event, w->name); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x3B); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x68, 0x28); + snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, + 0x08, 0x08); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, + 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x68, 0x40); + snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x00); + break; + } + + return 0; +}; + +static const char * const ec_buf_mux_text[] = { + "ZERO", "RXMIXEC", "SB_RX0", "SB_RX1", "SB_RX2", "SB_RX3", + "I2S_RX_SD0_L", "I2S_RX_SD0_R", "I2S_RX_SD1_L", "I2S_RX_SD1_R", + "DEC1" +}; + +static SOC_ENUM_SINGLE_DECL(ec_buf_mux_enum, WCD9335_CPE_SS_US_EC_MUX_CFG, + 0, ec_buf_mux_text); + +static const struct snd_kcontrol_new ec_buf_mux = + SOC_DAPM_ENUM("EC BUF Mux", ec_buf_mux_enum); + static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("EAR"), SND_SOC_DAPM_OUTPUT("ANC EAR"), @@ -10449,6 +10496,14 @@ static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = { aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)), SND_SOC_DAPM_INPUT("VIINPUT"), + + SND_SOC_DAPM_AIF_OUT("AIF5 CPE", "AIF5 CPE TX", 0, SND_SOC_NOPM, + AIF5_CPE_TX, 0), + + SND_SOC_DAPM_MUX_E("EC BUF MUX INP", SND_SOC_NOPM, 0, 0, &ec_buf_mux, + tasha_codec_ec_buf_mux_enable, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + /* Digital Mic Inputs */ SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0, tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | @@ -11432,6 +11487,19 @@ static struct snd_soc_dai_driver tasha_dai[] = { }, .ops = &tasha_dai_ops, }, + { + .name = "tasha_cpe", + .id = AIF5_CPE_TX, + .capture = { + .stream_name = "AIF5 CPE TX", + .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000, + .formats = TASHA_FORMATS_S16_S24_S32_LE, + .rate_min = 16000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 1, + }, + }, }; static struct snd_soc_dai_driver tasha_i2s_dai[] = { |
